Rivendellaudio/ripcd/vguest.cpp
Fred Gleason 15cd9954a0 2020-02-17 Fred Gleason <fredg@paravelsystems.com>
* Removed Q3Socket dependency from the vGuest switcher driver.
2020-02-17 10:21:01 -05:00

835 lines
23 KiB
C++

// vguest.cpp
//
// A Rivendell switcher driver for the Logitek vGuest Protocol
//
// (C) Copyright 2002-2019 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <stdlib.h>
#include <syslog.h>
#include <qsignalmapper.h>
#include <rdapplication.h>
#include <rdescape_string.h>
#include "globals.h"
#include "vguest.h"
VGuest::VGuest(RDMatrix *matrix,QObject *parent)
: Switcher(matrix,parent)
{
RDTty *tty;
QString sql;
RDSqlQuery *q;
int n;
for(int i=0;i<2;i++) {
vguest_device[i]=NULL;
vguest_socket[i]=NULL;
vguest_error_notified[i]=false;
}
//
// Get Matrix Parameters
//
vguest_matrix=matrix->matrix();
vguest_device[0]=NULL;
vguest_device[1]=NULL;
vguest_socket[0]=NULL;
vguest_socket[1]=NULL;
vguest_porttype[0]=matrix->portType(RDMatrix::Primary);
vguest_porttype[1]=matrix->portType(RDMatrix::Backup);
vguest_ipaddress[0]=matrix->ipAddress(RDMatrix::Primary);
vguest_ipaddress[1]=matrix->ipAddress(RDMatrix::Backup);
vguest_username[0]=PadString(matrix->username(RDMatrix::Primary),16);
vguest_username[1]=PadString(matrix->username(RDMatrix::Backup),16);
vguest_password[0]=PadString(matrix->password(RDMatrix::Primary),16);
vguest_password[1]=PadString(matrix->password(RDMatrix::Backup),16);
vguest_start_cart[0]=matrix->startCart(RDMatrix::Primary);
vguest_start_cart[1]=matrix->startCart(RDMatrix::Backup);
vguest_stop_cart[0]=matrix->stopCart(RDMatrix::Primary);
vguest_stop_cart[1]=matrix->stopCart(RDMatrix::Backup);
vguest_ipport[0]=matrix->ipPort(RDMatrix::Primary);
vguest_ipport[1]=matrix->ipPort(RDMatrix::Backup);
vguest_inputs=matrix->inputs();
vguest_outputs=matrix->outputs();
vguest_gpis=matrix->gpis();
vguest_gpos=matrix->gpos();
//
// Load Engine Data - Inputs
//
sql=QString("select ")+
"NUMBER,"+ // 00
"ENGINE_NUM,"+ // 01
"DEVICE_NUM "+ // 02
"from INPUTS where "+
"(STATION_NAME=\""+RDEscapeString(matrix->station())+"\")&&"+
QString().sprintf("(MATRIX=%d) ",matrix->matrix())+
"order by NUMBER";
q=new RDSqlQuery(sql);
n=1;
while(q->next()) {
while(q->value(0).toInt()>n) {
vguest_input_engine_nums.push_back(-1);
vguest_input_device_nums.push_back(-1);
n++;
}
vguest_input_engine_nums.push_back(q->value(1).toInt());
vguest_input_device_nums.push_back(q->value(2).toInt());
n++;
}
delete q;
//
// Load Engine Data - Outputs
//
sql=
QString("select ")+
"NUMBER,"+ // 00
"ENGINE_NUM,"+ // 01
"DEVICE_NUM "+ // 02
"from OUTPUTS where "+
"(STATION_NAME=\""+RDEscapeString(matrix->station())+"\")&&"+
QString().sprintf("(MATRIX=%d) ",matrix->matrix())+
"order by NUMBER";
q=new RDSqlQuery(sql);
n=1;
while(q->next()) {
while(q->value(0).toInt()>n) {
vguest_output_engine_nums.push_back(-1);
vguest_output_device_nums.push_back(-1);
n++;
}
vguest_output_engine_nums.push_back(q->value(1).toInt());
vguest_output_device_nums.push_back(q->value(2).toInt());
n++;
}
delete q;
//
// Load Engine Data - Relays
//
sql=
QString("select ")+
"NUMBER,"+ // 00
"ENGINE_NUM,"+ // 01
"DEVICE_NUM,"+ // 02
"SURFACE_NUM,"+ // 03
"RELAY_NUM "+ // 04
"from VGUEST_RESOURCES where "+
"(STATION_NAME=\""+RDEscapeString(matrix->station())+"\")&&"+
QString().sprintf("(MATRIX_NUM=%d)&&",matrix->matrix())+
QString().sprintf("(VGUEST_TYPE=%d) ",RDMatrix::VguestTypeRelay)+
"order by NUMBER";
q=new RDSqlQuery(sql);
n=1;
while(q->next()) {
while(q->value(0).toInt()>n) {
vguest_relays_engine_nums.push_back(-1);
vguest_relays_device_nums.push_back(-1);
vguest_relays_surface_nums.push_back(-1);
vguest_relays_relay_nums.push_back(-1);
n++;
}
vguest_relays_engine_nums.push_back(q->value(1).toInt());
vguest_relays_device_nums.push_back(q->value(2).toInt());
vguest_relays_surface_nums.push_back(q->value(3).toInt());
vguest_relays_relay_nums.push_back(q->value(4).toInt());
n++;
}
delete q;
//
// Load Engine Data - Displays
//
sql=QString("select ")+
"NUMBER,"+ // 00
"ENGINE_NUM,"+ // 01
"DEVICE_NUM,"+ // 02
"SURFACE_NUM "+ // 03
"from VGUEST_RESOURCES where "+
"(STATION_NAME=\""+RDEscapeString(matrix->station())+"\")&&"+
QString().sprintf("(MATRIX_NUM=%d)&&",matrix->matrix())+
QString().sprintf("(VGUEST_TYPE=%d) ",RDMatrix::VguestTypeDisplay)+
"order by NUMBER";
q=new RDSqlQuery(sql);
n=1;
while(q->next()) {
while(q->value(0).toInt()>n) {
vguest_displays_engine_nums.push_back(-1);
vguest_displays_device_nums.push_back(-1);
vguest_displays_surface_nums.push_back(-1);
n++;
}
vguest_displays_engine_nums.push_back(q->value(1).toInt());
vguest_displays_device_nums.push_back(q->value(2).toInt());
vguest_displays_surface_nums.push_back(q->value(3).toInt());
n++;
}
delete q;
//
// Ping Timers
//
vguest_ping_mapper=new QSignalMapper(this);
connect(vguest_ping_mapper,SIGNAL(mapped(int)),this,SLOT(pingData(int)));
vguest_ping_response_mapper=new QSignalMapper(this);
connect(vguest_ping_response_mapper,SIGNAL(mapped(int)),
this,SLOT(pingResponseData(int)));
for(int i=0;i<2;i++) {
vguest_ping_timer[i]=new QTimer(this);
vguest_ping_mapper->setMapping(vguest_ping_timer[i],i);
connect(vguest_ping_timer[i],SIGNAL(timeout()),
vguest_ping_mapper,SLOT(map()));
vguest_ping_response_timer[i]=new QTimer(this);
vguest_ping_response_mapper->setMapping(vguest_ping_timer[i],i);
connect(vguest_ping_response_timer[i],SIGNAL(timeout()),
vguest_ping_response_mapper,SLOT(map()));
}
//
// Reconnection Timer
//
QSignalMapper *reconnect_mapper=new QSignalMapper(this);
connect(reconnect_mapper,SIGNAL(mapped(int)),
this,SLOT(ipConnect(int)));
for(int i=0;i<2;i++) {
vguest_reconnect_timer[i]=new QTimer(this);
reconnect_mapper->setMapping(vguest_reconnect_timer[i],i);
connect(vguest_reconnect_timer[i],SIGNAL(timeout()),
reconnect_mapper,SLOT(map()));
}
//
// Interval OneShots
//
vguest_gpio_oneshot=new RDOneShot(this);
connect(vguest_gpio_oneshot,SIGNAL(timeout(int)),
this,SLOT(gpioOneshotData(int)));
//
// Initialize the connection
//
for(int i=0;i<2;i++) {
if(vguest_porttype[i]==RDMatrix::TtyPort) {
tty=new RDTty(rda->station()->name(),matrix->port((RDMatrix::Role)i));
vguest_device[i]=new RDTTYDevice();
if(tty->active()) {
vguest_device[i]->setName(tty->port());
vguest_device[i]->setSpeed(tty->baudRate());
vguest_device[i]->setWordLength(tty->dataBits());
vguest_device[i]->setParity(tty->parity());
vguest_device[i]->open(QIODevice::Unbuffered|QIODevice::WriteOnly);
}
delete tty;
}
else {
if(vguest_porttype[i]==RDMatrix::TcpPort) {
vguest_socket[i]=new RDSocket(i,this);
connect(vguest_socket[i],SIGNAL(connectedID(int)),
this,SLOT(connectedData(int)));
connect(vguest_socket[i],SIGNAL(connectionClosedID(int)),
this,SLOT(connectionClosedData(int)));
connect(vguest_socket[i],SIGNAL(readyReadID(int)),
this,SLOT(readyReadData(int)));
connect(vguest_socket[i],
SIGNAL(errorID(QAbstractSocket::SocketError,int)),
this,SLOT(errorData(QAbstractSocket::SocketError,int)));
ipConnect(i);
}
}
}
}
VGuest::~VGuest()
{
delete vguest_gpio_oneshot;
for(int i=0;i<2;i++) {
delete vguest_reconnect_timer[i];
delete vguest_ping_response_timer[i];
delete vguest_ping_timer[i];
if(vguest_device[i]!=NULL) {
delete vguest_device[i];
}
if(vguest_socket[i]!=NULL) {
delete vguest_socket[i];
}
}
}
RDMatrix::Type VGuest::type()
{
return RDMatrix::LogitekVguest;
}
unsigned VGuest::gpiQuantity()
{
return vguest_gpis;
}
unsigned VGuest::gpoQuantity()
{
return vguest_gpos;
}
bool VGuest::primaryTtyActive()
{
return vguest_porttype[0]==RDMatrix::TtyPort;
}
bool VGuest::secondaryTtyActive()
{
return vguest_porttype[1]==RDMatrix::TtyPort;
}
void VGuest::processCommand(RDMacro *cmd)
{
char buffer[VGUEST_MAX_COMMAND_LENGTH];
char cmd_byte=0;
QString label;
switch(cmd->command()) {
case RDMacro::SD:
if((cmd->argQuantity()<5)||
(cmd->arg(1).toUInt()>vguest_displays_engine_nums.size())) {
cmd->acknowledge(false);
emit rmlEcho(cmd);
rda->syslog(LOG_WARNING,"*** not enough vGuest arguments ***");
return;
}
if((vguest_displays_engine_nums[cmd->arg(1).toInt()-1]<0)||
(vguest_displays_device_nums[cmd->arg(1).toInt()-1]<0)||
(vguest_displays_surface_nums[cmd->arg(1).toInt()-1]<0)) {
cmd->acknowledge(false);
emit rmlEcho(cmd);
rda->syslog(LOG_WARNING,"*** invalid vGuest hex parameters ***");
return;
}
label=cmd->rollupArgs(5).left(VGUEST_MAX_TEXT_LENGTH);
sprintf(buffer,"\x02%c\x5C%c%c%c%c%c%c%c%s",8+label.length(),
(char)vguest_displays_engine_nums[cmd->arg(1).toInt()-1],
(char)(vguest_displays_device_nums[cmd->arg(1).toInt()-1]>>8),
(char)(vguest_displays_device_nums[cmd->arg(1).toInt()-1]&0xFF),
(char)vguest_displays_surface_nums[cmd->arg(1).toInt()-1],
(char)(0xFF&cmd->arg(2).toInt()),
(char)(0xFF&cmd->arg(3).toInt()),
(char)(0xFF&cmd->arg(4).toInt()),
(const char *)label);
SendCommand(buffer,10+label.length());
break;
case RDMacro::ST:
if((cmd->arg(1).toInt()<1)||(cmd->arg(1).toInt()>vguest_inputs)||
(cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>vguest_outputs)) {
cmd->acknowledge(false);
emit rmlEcho(cmd);
return;
}
if((vguest_input_engine_nums[cmd->arg(1).toInt()-1]<0)||
(vguest_input_device_nums[cmd->arg(1).toInt()-1]<0)||
(vguest_output_engine_nums[cmd->arg(2).toInt()-1]<0)||
(vguest_output_device_nums[cmd->arg(2).toInt()-1]<0)) {
cmd->acknowledge(false);
emit rmlEcho(cmd);
return;
}
sprintf(buffer,"\x02\x08\x54%c%c%c%c%c%c%c",
(char)vguest_output_engine_nums[cmd->arg(2).toInt()-1],
(char)(vguest_output_device_nums[cmd->arg(2).toInt()-1]>>8),
(char)(vguest_output_device_nums[cmd->arg(2).toInt()-1]&0xFF),
VGUEST_DEFAULT_SURFACE_NUMBER,
(char)vguest_input_engine_nums[cmd->arg(1).toInt()-1],
(char)(vguest_input_device_nums[cmd->arg(1).toInt()-1]>>8),
(char)(vguest_input_device_nums[cmd->arg(1).toInt()-1]&0xFF));
SendCommand(buffer,10);
cmd->acknowledge(true);
emit rmlEcho(cmd);
break;
case RDMacro::GO:
if(((cmd->arg(1).lower()!="i")&&
(cmd->arg(1).lower()!="o"))||
(cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>vguest_gpos)||
(cmd->arg(3).toInt()<0)||(cmd->arg(3).toInt()>1)||
(cmd->arg(4).toInt()<0)) {
cmd->acknowledge(false);
emit rmlEcho(cmd);
return;
}
if(cmd->arg(4).toInt()>0) {
cmd_byte=0x51;
}
else {
switch(cmd->arg(3).toInt()) {
case 0:
cmd_byte=0x53;
break;
case 1:
cmd_byte=0x52;
break;
}
}
if((vguest_relays_engine_nums[cmd->arg(2).toInt()-1]<0)||
(vguest_relays_device_nums[cmd->arg(2).toInt()-1]<0)||
(vguest_relays_surface_nums[cmd->arg(2).toInt()-1]<0)||
(vguest_relays_relay_nums[cmd->arg(2).toInt()-1]<0)) {
cmd->acknowledge(false);
emit rmlEcho(cmd);
return;
}
switch(0xFF&cmd_byte) {
case 0x51:
sprintf(buffer,"\x02\x07\x51%c%c%c%c%c%c",
vguest_relays_engine_nums[cmd->arg(2).toInt()-1],
vguest_relays_device_nums[cmd->arg(2).toInt()-1]>>8,
vguest_relays_device_nums[cmd->arg(2).toInt()-1]&0xFF,
vguest_relays_surface_nums[cmd->arg(2).toInt()-1],
vguest_relays_relay_nums[cmd->arg(2).toInt()-1],
cmd->arg(4).toInt()/50);
SendCommand(buffer,9);
emit gpiChanged(vguest_matrix,cmd->arg(2).toInt()-1,true);
emit gpoChanged(vguest_matrix,cmd->arg(2).toInt()-1,true);
vguest_gpio_oneshot->start(cmd->arg(2).toInt()-1,2000);
break;
case 0x52:
case 0x53:
sprintf(buffer,"\x02\x06%c%c%c%c%c%c",
cmd_byte,
vguest_relays_engine_nums[cmd->arg(2).toInt()-1],
vguest_relays_device_nums[cmd->arg(2).toInt()-1]>>8,
vguest_relays_device_nums[cmd->arg(2).toInt()-1]&0xFF,
vguest_relays_surface_nums[cmd->arg(2).toInt()-1],
vguest_relays_relay_nums[cmd->arg(2).toInt()-1]);
SendCommand(buffer,8);
emit gpiChanged(vguest_matrix,cmd->arg(2).toInt()-1,
cmd->arg(3).toInt());
emit gpoChanged(vguest_matrix,cmd->arg(2).toInt()-1,
cmd->arg(3).toInt());
}
cmd->acknowledge(true);
emit rmlEcho(cmd);
break;
default:
cmd->acknowledge(false);
emit rmlEcho(cmd);
break;
}
}
void VGuest::ipConnect(int id)
{
if(!vguest_ipaddress[id].isNull()) {
vguest_socket[id]->
connectToHost(vguest_ipaddress[id].toString(),vguest_ipport[id]);
}
}
void VGuest::connectedData(int id)
{
vguest_istate[id]=0;
}
void VGuest::connectionClosedData(int id)
{
int interval=GetHoldoff();
if(!vguest_error_notified[id]) {
rda->syslog(LOG_WARNING,
"connection to vGuest device at %s:%d closed, attempting reconnect",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_error_notified[id]=true;
}
if(vguest_stop_cart[id]>0) {
ExecuteMacroCart(vguest_stop_cart[id]);
}
vguest_ping_timer[id]->stop();
vguest_ping_response_timer[id]->stop();
vguest_reconnect_timer[id]->start(interval,true);
}
void VGuest::readyReadData(int id)
{
char buffer[255];
int n=0;
while((n=vguest_socket[id]->readBlock(buffer,255))>0) {
for(int i=0;i<n;i++) {
switch(vguest_istate[id]) {
case 0: // STX Command Start
switch(buffer[i]) {
case 0x02:
vguest_istate[id]=1;
break;
case 0x04:
vguest_istate[id]=11;
break;
}
break;
case 1: // LP2 Command Length
vguest_cmd_length[id]=buffer[i];
vguest_cmd_buffer[id][0]=2;
vguest_cmd_buffer[id][1]=buffer[i];
vguest_cmd_ptr[id]=2;
vguest_istate[id]=2;
break;
case 2: // LP2 Command Body
vguest_cmd_buffer[id][vguest_cmd_ptr[id]++]=buffer[i];
if(vguest_cmd_ptr[id]==(vguest_cmd_length[id]+2)) {
DispatchCommand(vguest_cmd_buffer[id],vguest_cmd_length[id]+2,id);
vguest_istate[id]=0;
}
break;
case 11: // Metadata Command Length
vguest_cmd_length[id]=buffer[i];
vguest_cmd_buffer[id][0]=2;
vguest_cmd_buffer[id][1]=buffer[i];
vguest_cmd_ptr[id]=2;
vguest_istate[id]=12;
break;
case 12: // LP2 Command Body
vguest_cmd_buffer[id][vguest_cmd_ptr[id]++]=buffer[i];
if(vguest_cmd_ptr[id]==(vguest_cmd_length[id]+2)) {
MetadataCommand(vguest_cmd_buffer[id],vguest_cmd_length[id]+2,id);
vguest_istate[id]=0;
}
break;
}
}
}
}
void VGuest::errorData(QAbstractSocket::SocketError err,int id)
{
int interval=VGUEST_RECONNECT_MIN_INTERVAL;
switch(err) {
case QAbstractSocket::ConnectionRefusedError:
interval=GetHoldoff();
if(!vguest_error_notified[id]) {
rda->syslog(LOG_WARNING,
"connection to vGuest device at %s:%d refused, attempting reconnect",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_error_notified[id]=true;
}
vguest_reconnect_timer[id]->start(interval,true);
break;
case QAbstractSocket::HostNotFoundError:
if(!vguest_error_notified[id]) {
rda->syslog(LOG_WARNING,
"error on connection to vGuest device at %s:%d: Host Not Found",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_error_notified[id]=true;
}
break;
default:
if(!vguest_error_notified[id]) {
rda->syslog(LOG_WARNING,
"error %d on connection to vGuest device at %s:%d: Socket Read Error",
err,
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_error_notified[id]=true;
}
break;
}
}
void VGuest::gpioOneshotData(int value)
{
emit gpiChanged(vguest_matrix,value,false);
emit gpoChanged(vguest_matrix,value,false);
}
void VGuest::pingData(int id)
{
char buffer[VGUEST_MAX_COMMAND_LENGTH];
buffer[0]=0x04;
buffer[1]=0x01;
buffer[2]=0x03; // LPCore Connection Ping
vguest_socket[id]->writeBlock(buffer,3);
vguest_ping_response_timer[id]->start(VGUEST_PING_INTERVAL,true);
}
void VGuest::pingResponseData(int id)
{
vguest_socket[id]->close();
rda->syslog(LOG_WARNING,"vGuest connection to "+
vguest_ipaddress[id].toString()+
" timed out, restarting connection");
}
void VGuest::SendCommand(char *str,int len)
{
// LogLine(QString().sprintf("SENT: %s",(const char *)RenderCommand(str,len)));
for(int i=0;i<2;i++) {
switch(vguest_porttype[i]) {
case RDMatrix::TtyPort:
if(vguest_device[i]!=NULL) {
vguest_device[i]->write(str,len);
}
break;
case RDMatrix::TcpPort:
if(vguest_socket[i]!=NULL) {
vguest_socket[i]->writeBlock(str,len);
}
break;
case RDMatrix::NoPort:
break;
}
}
}
void VGuest::DispatchCommand(char *cmd,int len,int id)
{
char buffer[VGUEST_MAX_COMMAND_LENGTH];
QString str;
int linenum;
// LogLine(RDConfig::LogNotice,
// QString().sprintf("RCVD: %s",(const char *)RenderCommand(cmd,len)));
switch(0xFF&cmd[2]) {
case 0xF9: // Username/Password Query
buffer[0]=0x02;
buffer[1]=0x22;
buffer[2]=0xF9;
buffer[3]=VGUEST_ID_BYTE;
sprintf(buffer+4,"%s%s",
(const char *)vguest_username[id],
(const char *)vguest_password[id]);
SendCommand(buffer,36);
break;
case 0xF0: // Connect Status
switch(0xFF&cmd[3]) {
case 0x0A: // Valid connection
case 0x14:
rda->syslog(LOG_INFO,
"connection to vGuest device at %s:%d established",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_error_notified[id]=false;
if(vguest_start_cart[id]>0) {
ExecuteMacroCart(vguest_start_cart[id]);
}
if(vguest_socket[id]!=NULL) {
buffer[0]=0x04;
buffer[1]=0x01;
buffer[2]=0x03; // LPCore Connection Ping
vguest_socket[id]->writeBlock(buffer,3);
}
break;
case 0x0B: // Invalid Username
case 0x15:
rda->syslog(LOG_WARNING,
"connection to vGuest device at %s:%d refused: username invalid",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_socket[id]->close();
connectionClosedData(id);
break;
case 0x0C: // Invalid Password
case 0x16:
rda->syslog(LOG_WARNING,
"connection to vGuest device at %s:%d refused: password invalid",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_socket[id]->close();
connectionClosedData(id);
break;
case 0x0D: // No vGuest Permission
case 0x17:
rda->syslog(LOG_WARNING,
"connection to vGuest device at %s:%d refused: no vGuest permission",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_socket[id]->close();
connectionClosedData(id);
break;
case 0x0E: // No Profile
case 0x18:
rda->syslog(LOG_WARNING,
"connection to vGuest device at %s:%d refused: no profile assigned",
(const char *)vguest_ipaddress[id].toString().toUtf8(),
vguest_ipport[id]);
vguest_socket[id]->close();
connectionClosedData(id);
break;
}
break;
case 0x52: // Turn On
if((linenum=GetRelay(0xFF&cmd[3],256*(0xFF&cmd[4])+(0xFF&cmd[5]),
0xFF&cmd[6],0xFF&cmd[7]))>=0) {
emit gpiChanged(vguest_matrix,linenum,true);
emit gpoChanged(vguest_matrix,linenum,true);
}
else {
rda->syslog(LOG_DEBUG,
"unhandled vGuest command received: %s",
(const char *)RenderCommand(cmd,len).toUtf8());
}
break;
case 0x53: // Turn Off
if((linenum=GetRelay(0xFF&cmd[3],256*(0xFF&cmd[4])+(0xFF&cmd[5]),
0xFF&cmd[6],0xFF&cmd[7]))>=0) {
emit gpiChanged(vguest_matrix,linenum,false);
emit gpoChanged(vguest_matrix,linenum,false);
}
else {
rda->syslog(LOG_DEBUG,
"unhandled vGuest command received: %s",
(const char *)RenderCommand(cmd,len).toUtf8());
}
break;
case 0x54: // Input Assign
break;
case 0x55: // Input Mode
break;
case 0x56: // Fader Level
break;
default:
rda->syslog(LOG_DEBUG,"unrecognized vGuest command received: %s",
(const char *)RenderCommand(cmd,len).toUtf8());
break;
}
}
void VGuest::MetadataCommand(char *cmd,int len,int id)
{
switch(0xFF&cmd[2]) {
case 0x03: // Connection Ping
if(vguest_ping_response_timer[id]->isActive()) {
vguest_ping_response_timer[id]->stop();
}
else {
rda->syslog(LOG_INFO,
"vGuest system at %s understands ping, activating timeout monitoring",
(const char *)vguest_ipaddress[id].toString().toUtf8());
}
vguest_ping_timer[id]->start(VGUEST_PING_INTERVAL,true);
break;
}
}
QString VGuest::PadString(QString str,int len)
{
QString out;
out=str.left(len);
while(out.length()<len) {
out+=" ";
}
return out;
}
QString VGuest::RenderCommand(char *cmd,int len)
{
QString str;
for(int i=0;i<len;i++) {
str+=QString().sprintf("%02X",0xFF&cmd[i]);
}
return str;
}
int VGuest::GetRelay(int enginenum,int devicenum,int surfacenum,int relaynum)
{
for(unsigned i=0;i<vguest_relays_engine_nums.size();i++) {
/*
LogLine(QString().sprintf("Checking Engine: %d | %d",vguest_relays_engine_nums[i],enginenum));
LogLine(QString().sprintf("Checking Device: 0x%04X | 0x%04X",vguest_relays_device_nums[i],devicenum));
LogLine(QString().sprintf("Checking Surface: 0x%04X | 0x%04X",vguest_relays_surface_nums[i],surfacenum));
LogLine(QString().sprintf("Checking Relay: 0x%04X | 0x%04X",vguest_relays_relay_nums[i],relaynum));
*/
if((vguest_relays_engine_nums[i]==enginenum)&&
(vguest_relays_device_nums[i]==devicenum)&&
(vguest_relays_surface_nums[i]==surfacenum)&&
(vguest_relays_relay_nums[i]==relaynum)) {
// LogLine(" MATCH!");
return (int)i;
}
}
return -1;
}
int VGuest::GetHoldoff()
{
return (int)(VGUEST_RECONNECT_MIN_INTERVAL+
(VGUEST_RECONNECT_MAX_INTERVAL-VGUEST_RECONNECT_MIN_INTERVAL)*
(double)random()/(double)RAND_MAX);
}
void VGuest::ExecuteMacroCart(unsigned cartnum)
{
RDMacro rml;
rml.setRole(RDMacro::Cmd);
rml.setCommand(RDMacro::EX);
rml.setAddress(rda->station()->address());
rml.setEchoRequested(false);
rml.addArg(cartnum);
emit rmlEcho(&rml);
}