// sasusi.cpp // // A Rivendell switcher driver for the SAS USI Protocol // // (C) Copyright 2002-2020 Fred Gleason // // 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 #include #include #include #include #include "globals.h" #include "sasusi.h" SasUsi::SasUsi(RDMatrix *matrix,QObject *parent) : Switcher(matrix,parent) { QString sql; RDSqlQuery *q; RDTty *tty; sas_matrix=matrix->matrix(); sas_ptr=0; // // Get Matrix Parameters // sas_porttype=matrix->portType(RDMatrix::Primary); sas_ipaddress=matrix->ipAddress(RDMatrix::Primary); sas_ipport=matrix->ipPort(RDMatrix::Primary); sas_inputs=matrix->inputs(); sas_outputs=matrix->outputs(); sas_gpis=matrix->gpis(); sas_gpos=matrix->gpos(); sas_start_cart=matrix->startCart(RDMatrix::Primary); sas_stop_cart=matrix->stopCart(RDMatrix::Primary); // // Load Switch Table // sql=QString("select ")+ "ENGINE_NUM,"+ // 00 "DEVICE_NUM,"+ // 01 "RELAY_NUM "+ // 02 "from VGUEST_RESOURCES where "+ "(STATION_NAME=\""+RDEscapeString(rda->config()->stationName())+"\")&&"+ QString().sprintf("(MATRIX_NUM=%d) ",matrix->matrix())+ "order by NUMBER"; q=new RDSqlQuery(sql); while(q->next()) { sas_console_numbers.push_back(q->value(0).toInt()); sas_source_numbers.push_back(q->value(1).toInt()); sas_relay_numbers.push_back(q->value(2).toInt()); } delete q; // // Reconnection Timer // sas_reconnect_timer=new QTimer(this); connect(sas_reconnect_timer,SIGNAL(timeout()),this,SLOT(ipConnect())); // // Initialize the connection // switch(sas_porttype) { case RDMatrix::TtyPort: tty=new RDTty(rda->station()->name(),matrix->port(RDMatrix::Primary)); sas_device=new RDTTYDevice(); if(tty->active()) { sas_device->setName(tty->port()); sas_device->setSpeed(tty->baudRate()); sas_device->setWordLength(tty->dataBits()); sas_device->setParity(tty->parity()); sas_device->open(QIODevice::Unbuffered|QIODevice::WriteOnly); } delete tty; case RDMatrix::TcpPort: sas_socket=new QTcpSocket(this); connect(sas_socket,SIGNAL(connected()),this,SLOT(connectedData())); connect(sas_socket,SIGNAL(disconnected()), this,SLOT(connectionClosedData())); connect(sas_socket,SIGNAL(readyRead()), this,SLOT(readyReadData())); connect(sas_socket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(errorData(Q))); ipConnect(); break; case RDMatrix::NoPort: break; } } RDMatrix::Type SasUsi::type() { return RDMatrix::SasUsi; } unsigned SasUsi::gpiQuantity() { return sas_gpis; } unsigned SasUsi::gpoQuantity() { return sas_gpos; } bool SasUsi::primaryTtyActive() { return sas_porttype==RDMatrix::TtyPort; } bool SasUsi::secondaryTtyActive() { return false; } void SasUsi::processCommand(RDMacro *cmd) { char str[256]; char cmd_byte; QString label; switch(cmd->command()) { case RDMacro::CL: if((cmd->arg(1).toInt()<1)|| ((cmd->arg(1).toInt()>256)&&(cmd->arg(1).toInt()!=999))|| (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>sas_inputs)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } for(int i=3;i<(cmd->argQuantity()-1);i++) { label+=(cmd->arg(i)+" "); } label+=cmd->arg(cmd->argQuantity()-1); if(label.length()>8) { label=label.left(8); } for(int i=label.length();i<8;i++) { label+=" "; } snprintf(str,256,"%c21%03d%04d%s\x0D\x0A",26, cmd->arg(1).toInt(),cmd->arg(2).toInt(),(const char *)label); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::FS: if((cmd->arg(1).toInt()<1)||(cmd->arg(1).toInt()>256)|| (sas_porttype!=RDMatrix::TcpPort)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } snprintf(str,256,"%c1%03d\x0D\x0A",0x13,cmd->arg(1).toInt()); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::SG: if((cmd->arg(1).toInt()<1)||(cmd->arg(1).toInt()>sas_inputs)|| (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>sas_outputs)|| (cmd->arg(3).toInt()<-1024)||(cmd->arg(3).toInt()>1024)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } snprintf(str,256,"%c00%04d%04d%04d00548\x0D\x0A",26, cmd->arg(1).toInt(),cmd->arg(2).toInt(), cmd->arg(3).toInt()+1024); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::SX: if((cmd->arg(1).toInt()<1)||(cmd->arg(1).toInt()>sas_inputs)|| (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>sas_outputs)|| (cmd->arg(3).toInt()<-1024)||(cmd->arg(3).toInt()>1024)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } snprintf(str,256,"%c10%04d%04d%04d0010\x0D\x0A",26, cmd->arg(1).toInt(),cmd->arg(2).toInt(), cmd->arg(3).toInt()+1024); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::SL: if((cmd->arg(1).toInt()<1)||(cmd->arg(1).toInt()>sas_inputs)|| (cmd->arg(2).toInt()<-1024)||(cmd->arg(2).toInt()>1024)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } snprintf(str,256,"%c10%04d0000%04d0001\x0D\x0A",26, cmd->arg(1).toInt(),cmd->arg(2).toInt()+1024); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::SA: if((cmd->arg(1).toInt()<1)||(cmd->arg(1).toInt()>sas_inputs)|| (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>sas_outputs)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } snprintf(str,256,"%c00%04d%04d102400036\x0D\x0A",26, cmd->arg(1).toInt(),cmd->arg(2).toInt()); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::SR: if((cmd->arg(1).toInt()<1)||(cmd->arg(1).toInt()>sas_inputs)|| (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>sas_outputs)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } snprintf(str,256,"%c00%04d%04d102400032\x0D\x0A",26, cmd->arg(1).toInt(),cmd->arg(2).toInt()); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::ST: if((cmd->arg(1).toInt()<0)||(cmd->arg(1).toInt()>sas_inputs)|| (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>sas_outputs)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } snprintf(str,256,"%cT%04d%04d\x0D\x0A",5, cmd->arg(1).toInt(),cmd->arg(2).toInt()); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); break; case RDMacro::GO: if((cmd->arg(1).lower()!="o")|| (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>sas_gpos)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } if(cmd->arg(4).toInt()==0) { // Latch if(cmd->arg(3).toInt()==0) { // Off cmd_byte=0x03; } else { cmd_byte=0x02; } } else { if(cmd->arg(3).toInt()==0) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } cmd_byte=0x01; } if(cmd->arg(2).toUInt()<=sas_relay_numbers.size()) { if(sas_relay_numbers[cmd->arg(2).toUInt()-1]>=0) { snprintf(str,256,"\x05R%d%04d\x0D\x0A",cmd_byte, sas_relay_numbers[cmd->arg(2).toUInt()-1]); rda->syslog(LOG_DEBUG,"USI: %s", (const char *)PrettifyCommand(str)); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); } else { if((sas_console_numbers[cmd->arg(2).toUInt()-1]>=0)&& (sas_source_numbers[cmd->arg(2).toUInt()-1]>=0)) { if(cmd->arg(3).toInt()==0) { // Off cmd_byte=0; } else { cmd_byte=1; } snprintf(str,256,"\x1A%s%d%03d%04d\x0D\x0A","20",cmd_byte, sas_console_numbers[cmd->arg(2).toUInt()-1], sas_source_numbers[cmd->arg(2).toUInt()-1]); SendCommand(str); cmd->acknowledge(true); emit rmlEcho(cmd); } } } break; default: cmd->acknowledge(false); emit rmlEcho(cmd); break; } } void SasUsi::ipConnect() { sas_socket->connectToHost(sas_ipaddress.toString(),sas_ipport); } void SasUsi::connectedData() { rda->syslog(LOG_INFO, "connection to SasUsi device at %s:%d established", (const char *)sas_ipaddress.toString().toUtf8(), sas_ipport); if(sas_start_cart>0) { ExecuteMacroCart(sas_start_cart); } } void SasUsi::connectionClosedData() { rda->syslog(LOG_WARNING, "connection to SasUsi device at %s:%d closed unexpectedly, attempting reconnect", (const char *)sas_ipaddress.toString().toUtf8(), sas_ipport); if(sas_stop_cart>0) { ExecuteMacroCart(sas_stop_cart); } sas_reconnect_timer->start(SASUSI_RECONNECT_INTERVAL,true); } void SasUsi::readyReadData() { char buffer[256]; unsigned n; while((n=sas_socket->readBlock(buffer,255))>0) { buffer[n]=0; for(unsigned i=0;isyslog(LOG_WARNING, "connection to SasUsi device at %s:%d refused, attempting reconnect", (const char *)sas_ipaddress.toString().toUtf8(), sas_ipport); sas_reconnect_timer->start(SASUSI_RECONNECT_INTERVAL,true); break; case QAbstractSocket::HostNotFoundError: rda->syslog(LOG_WARNING, "error on connection to SasUsi device at %s:%d: Host Not Found", (const char *)sas_ipaddress.toString().toUtf8(), sas_ipport); break; default: rda->syslog(LOG_WARNING, "received network error %d on connection to SasUsi device at %s:%d", (const char *)sas_ipaddress.toString().toUtf8(), sas_ipport); break; } } void SasUsi::SendCommand(char *str) { rda->syslog(LOG_INFO,"sending USI cmd: %s", (const char *)PrettifyCommand(str)); switch(sas_porttype) { case RDMatrix::TtyPort: sas_device->write(str,strlen(str)); break; case RDMatrix::TcpPort: sas_socket->writeBlock(str,strlen(str)); break; case RDMatrix::NoPort: break; } } void SasUsi::DispatchCommand() { char buffer[SASUSI_MAX_LENGTH]; unsigned input; unsigned output; int line; unsigned action; int console; int source; bool state; bool ok=false; QString cmd; QString label; QString sql; RDSqlQuery *q; //LogLine(RDConfig::LogNotice,QString().sprintf("DISPATCHED: %s",(const char *)sas_buffer)); // // Startup Sequence. Get the input and output lists. The response // to the ^EI command lets us know when the lists are done. // if(QString("login sucessful")==(QString(sas_buffer).lower())) { sprintf(buffer,"%cX9999\x0D\x0A",5); // Request Input List SendCommand(buffer); sprintf(buffer,"%cY9999\x0D\x0A",5); // Request Output List SendCommand(buffer); sprintf(buffer,"%cI0001\x0D\x0A",5); // Start Finished SendCommand(buffer); return; } // // Work around the idiotic 'SAS READY' prompt // if(sas_buffer[0]==27) { for(unsigned i=17;isyslog(LOG_INFO,"received USI cmd: %s", (const char *)PrettifyCommand(sas_buffer)); // // Process Commands // switch(sas_buffer[0]) { case 21: // Input Name [^U] if(strlen(sas_buffer)<13) { return; } label=sas_buffer+5; sas_buffer[5]=0; if(sscanf(sas_buffer+1,"%u",&input)!=1) { return; } sql=QString("select NUMBER from INPUTS where ")+ "(STATION_NAME=\""+RDEscapeString(rda->station()->name())+"\")&&"+ QString().sprintf("(MATRIX=%d)&&",sas_matrix)+ QString().sprintf("(NUMBER=%d)",input); q=new RDSqlQuery(sql); if(q->first()) { sql=QString("update INPUTS set ")+ "NAME=\""+RDEscapeString(label)+"\" where "+ "(STATION_NAME=\""+RDEscapeString(rda->station()->name())+"\")&&"+ QString().sprintf("(MATRIX=%d)&&",sas_matrix)+ QString().sprintf("(NUMBER=%d)",input); } else { sql=QString("insert into INPUTS set ")+ "NAME=\""+RDEscapeString(label)+"\","+ "STATION_NAME=\""+RDEscapeString(rda->station()->name())+"\","+ QString().sprintf("MATRIX=%d,",sas_matrix)+ QString().sprintf("NUMBER=%d",input); } delete q; q=new RDSqlQuery(sql); delete q; break; case 22: // Output Name [^V] if(strlen(sas_buffer)<13) { return; } label=sas_buffer+5; sas_buffer[5]=0; if(sscanf(sas_buffer+1,"%u",&output)!=1) { return; } sql=QString("select NUMBER from OUTPUTS where ")+ "(STATION_NAME=\""+RDEscapeString(rda->station()->name())+"\")&&"+ QString().sprintf("(MATRIX=%d)&&",sas_matrix)+ QString().sprintf("(NUMBER=%d)",output); q=new RDSqlQuery(sql); if(q->first()) { sql=QString("update OUTPUTS set ")+ "NAME=\""+RDEscapeString(label)+"\" where "+ "(STATION_NAME=\""+RDEscapeString(rda->station()->name())+"\")&&"+ QString().sprintf("(MATRIX=%d)&&",sas_matrix)+ QString().sprintf("(NUMBER=%d)",output); } else { sql=QString("insert into OUTPUTS set ")+ "NAME=\""+RDEscapeString(label)+"\","+ "STATION_NAME=\""+RDEscapeString(rda->station()->name())+"\","+ QString().sprintf("MATRIX=%d,",sas_matrix)+ QString().sprintf("NUMBER=%d",output); } delete q; q=new RDSqlQuery(sql); delete q; break; case 'M': // Console Module Action if(strlen(sas_buffer)<9) { return; } cmd=QString(sas_buffer); console=cmd.mid(2,3).toInt(&ok); if(!ok) { return; } source=cmd.mid(5,4).toInt(&ok); if(!ok) { return; } for(unsigned i=0;istation()->address()); rml.setEchoRequested(false); rml.addArg(cartnum); emit rmlEcho(&rml); } QString SasUsi::PrettifyCommand(const char *cmd) const { QString ret; if(cmd[0]<26) { ret=QString().sprintf("^%c%s",'@'+cmd[0],cmd+1); } else { ret=cmd; } return ret; }