// wheatnet_lio.cpp // // A Rivendell switcher driver for WheatNet LIO // // (C) Copyright 2017-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 "wheatnet_lio.h" WheatnetLio::WheatnetLio(RDMatrix *matrix,QObject *parent) : Switcher(matrix,parent) { lio_watchdog_active=false; lio_gpios=0; lio_ip_address=matrix->ipAddress(RDMatrix::Primary); lio_ip_port=matrix->ipPort(RDMatrix::Primary); lio_card=matrix->card(); lio_socket=new QTcpSocket(this); connect(lio_socket,SIGNAL(connected()),this,SLOT(connectedData())); connect(lio_socket,SIGNAL(readyRead()),this,SLOT(readyReadData())); connect(lio_socket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(errorData(QAbstractSocket::SocketError))); lio_socket->connectToHost(lio_ip_address.toString(),lio_ip_port); lio_poll_timer=new QTimer(this); connect(lio_poll_timer,SIGNAL(timeout()),this,SLOT(pollData())); lio_reset_mapper=new QSignalMapper(this); connect(lio_reset_mapper,SIGNAL(mapped(int)), this,SLOT(resetStateData(int))); for(int i=0;isetMapping(lio_reset_timers.back(),i); lio_reset_states.push_back(false); } lio_watchdog_timer=new QTimer(this); connect(lio_watchdog_timer,SIGNAL(timeout()),this,SLOT(watchdogData())); } WheatnetLio::~WheatnetLio() { delete lio_watchdog_timer; delete lio_poll_timer; for(unsigned i=0;icommand()) { case RDMacro::GO: if((cmd->argQuantity()!=5)|| ((cmd->arg(1).lower()!="i")&& (cmd->arg(1).lower()!="o"))|| (cmd->arg(2).toInt()<1)||(cmd->arg(3).toInt()>lio_gpios)|| (cmd->arg(2).toInt()>lio_gpios)|| ((cmd->arg(3).toInt()!=1)&&(cmd->arg(3).toInt()!=0)&& (cmd->arg(1).lower()!="i"))|| ((cmd->arg(3).toInt()!=1)&&(cmd->arg(3).toInt()!=0)&& (cmd->arg(3).toInt()!=-1)&&(cmd->arg(1).lower()=="i"))|| (cmd->arg(4).toInt()<0)) { cmd->acknowledge(false); emit rmlEcho(cmd); return; } if(cmd->arg(3).toInt()==0) { // Turn OFF if(cmd->arg(4).toInt()==0) { if(cmd->arg(1).lower()=="o") { SendCommand(QString().sprintf("", cmd->arg(2).toInt()-1)); emit gpoChanged(matrixNumber(),cmd->arg(2).toInt()-1,false); } } else { if(cmd->echoRequested()) { cmd->acknowledge(false); emit rmlEcho(cmd); } return; } } else { if(cmd->arg(4).toInt()==0) { // Turn ON if(cmd->arg(1).lower()=="o") { SendCommand(QString().sprintf("", cmd->arg(2).toInt()-1)); emit gpoChanged(matrixNumber(),cmd->arg(2).toInt()-1,true); } } else { // Pulse if(cmd->arg(1).lower()=="o") { SendCommand(QString().sprintf("", cmd->arg(2).toInt()-1, cmd->arg(3).toInt()!=0)); lio_reset_states[cmd->arg(2).toInt()-1]=cmd->arg(3).toInt()==0; lio_reset_timers[cmd->arg(2).toInt()-1]-> start(cmd->arg(4).toInt(),true); emit gpoChanged(matrixNumber(),cmd->arg(2).toInt()-1, cmd->arg(3).toInt()!=0); } } } if(cmd->echoRequested()) { cmd->acknowledge(true); emit rmlEcho(cmd); } break; default: break; } } void WheatnetLio::connectedData() { rda->syslog(LOG_INFO, "connection to WheatNet LIO device at %s:%u established", (const char *)lio_ip_address.toString(),0xffff&lio_ip_port); lio_watchdog_active=false; SendCommand(""); } void WheatnetLio::readyReadData() { char data[1501]; int n=0; while((n=lio_socket->readBlock(data,1500))>0) { data[n]=0; for(int i=0;i",line, (int)lio_reset_states[line])); emit gpoChanged(matrixNumber(),line,lio_reset_states[line]); } void WheatnetLio::pollData() { SendCommand(""); } void WheatnetLio::watchdogData() { if(!lio_watchdog_active) { rda->syslog(LOG_WARNING, "connection to Wheatnet LIO device at %s:%u lost, attempting reconnect", (const char *)lio_ip_address.toString(),0xffff&lio_ip_port); lio_watchdog_active=true; } lio_socket->close(); lio_socket->connectToHost(lio_ip_address.toString(),lio_ip_port); } void WheatnetLio::CheckLineEntry(int line) { QString sql; RDSqlQuery *q; sql=QString("select ID from GPIS where ")+ "(STATION_NAME=\""+RDEscapeString(stationName())+"\")&&"+ QString().sprintf("(MATRIX=%d)&&",matrixNumber())+ QString().sprintf("(NUMBER=%d)",line); q=new RDSqlQuery(sql); if(!q->first()) { delete q; sql=QString("insert into GPIS set ")+ "STATION_NAME=\""+RDEscapeString(stationName())+"\","+ QString().sprintf("MATRIX=%d,",matrixNumber())+ QString().sprintf("NUMBER=%d",line); q=new RDSqlQuery(sql); } delete q; sql=QString("select ID from GPOS where ")+ "(STATION_NAME=\""+RDEscapeString(stationName())+"\")&&"+ QString().sprintf("(MATRIX=%d)&&",matrixNumber())+ QString().sprintf("(NUMBER=%d)",line); q=new RDSqlQuery(sql); if(!q->first()) { delete q; sql=QString("insert into GPOS set ")+ "STATION_NAME=\""+RDEscapeString(stationName())+"\","+ QString().sprintf("MATRIX=%d,",matrixNumber())+ QString().sprintf("NUMBER=%d",line); q=new RDSqlQuery(sql); } delete q; } void WheatnetLio::ProcessSys(const QString &cmd) { // printf("SYS: %s\n",(const char *)cmd); QString sql; RDSqlQuery *q; bool ok=false; QStringList f0=cmd.split(":"); if((f0[0]=="LIO")&&(f0.size()==2)) { int lio=f0[1].toUInt(&ok); if(ok) { lio_gpios=lio; for(unsigned i=0;isetMapping(lio_reset_timers.back(),i); lio_reset_states.push_back(false); lio_gpi_states.push_back(false); CheckLineEntry(i+1); SendCommand(QString().sprintf("",i)); } sql=QString("update MATRICES set ")+ QString().sprintf("GPIS=%d,GPOS=%d where ",lio_gpios,lio_gpios)+ "(STATION_NAME=\""+RDEscapeString(stationName())+"\")&&"+ QString().sprintf("(MATRIX=%d)",matrixNumber()); q=new RDSqlQuery(sql); delete q; lio_watchdog_timer->start(WHEATNET_LIO_WATCHDOG_INTERVAL,true); lio_poll_timer->start(WHEATNET_LIO_POLL_INTERVAL,true); } } if((f0[0]=="BLID")&&(f0.size()==2)) { lio_watchdog_timer->stop(); lio_watchdog_timer->start(WHEATNET_LIO_WATCHDOG_INTERVAL,true); lio_poll_timer->start(WHEATNET_LIO_POLL_INTERVAL,true); } } void WheatnetLio::ProcessLioevent(int chan,QString &cmd) { // printf("ProcessLioevent(%d,%s)\n",chan,(const char *)cmd); QStringList f0=cmd.split(":"); if((f0[0]=="LVL")&&(f0.size()==2)) { if(chan<(int)lio_gpi_states.size()) { bool state=f0[1]=="1"; if(state!=lio_gpi_states[chan]) { lio_gpi_states[chan]=state; emit gpiChanged(matrixNumber(),chan,state); } } else { rda->syslog(LOG_WARNING, "WheatNet device at %s:%d sent invalid LIOEVENT LVL update [%s]", (const char *)lio_ip_address.toString(), lio_ip_port,(const char *)cmd); } if((chan+1)==lio_gpios) { lio_watchdog_timer->stop(); lio_watchdog_timer->start(1000,true); } } } void WheatnetLio::ProcessCommand(const QString &cmd) { // printf("ProcessCommand(%s)\n",(const char *)cmd); bool ok=false; if((cmd.left(1)=="<")&&(cmd.right(1)==">")) { QStringList f0=cmd.mid(1,cmd.length()-2).split("|"); if(f0.size()==2) { QStringList f1=f0[0].split(":"); if(f1[0]=="SYS") { ProcessSys(f0[1]); } if((f1[0]=="LIOEVENT")&&(f1.size()==2)) { QStringList f2=f1[1].split("."); if((f2[0]=="0")&&(f2.size()==2)) { int chan=f2[1].toUInt(&ok); if(ok) { ProcessLioevent(chan,f0[1]); } } } } } } void WheatnetLio::SendCommand(const QString &cmd) { lio_socket->writeBlock(cmd+"\r\n",cmd.length()+2); }