From 6a174ae6129b3bac9fc875a3f3f51d3c9deb97f0 Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Sat, 12 Aug 2017 16:50:49 -0400 Subject: [PATCH] 2017-08-12 Fred Gleason * Implemented a driver for WheatNet SLIO devices. --- ChangeLog | 2 + docs/SWITCHERS.txt | 15 ++ lib/rdmatrix.cpp | 13 +- lib/rdmatrix.h | 2 +- ripcd/Makefile.am | 6 +- ripcd/loaddrivers.cpp | 7 +- ripcd/wheatnet_slio.cpp | 370 ++++++++++++++++++++++++++++++++++++++++ ripcd/wheatnet_slio.h | 81 +++++++++ 8 files changed, 489 insertions(+), 7 deletions(-) create mode 100644 ripcd/wheatnet_slio.cpp create mode 100644 ripcd/wheatnet_slio.h diff --git a/ChangeLog b/ChangeLog index c72fd4bf..a65ebaf6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15929,3 +15929,5 @@ 'rlm_filewrite' RLM from 256 to 8192 characters. 2017-08-07 Fred Gleason * Incremented the package version to 2.16.0int06_rfa02. +2017-08-12 Fred Gleason + * Implemented a driver for WheatNet SLIO devices. diff --git a/docs/SWITCHERS.txt b/docs/SWITCHERS.txt index db7935e5..286a83a9 100644 --- a/docs/SWITCHERS.txt +++ b/docs/SWITCHERS.txt @@ -37,6 +37,7 @@ Sine Systems ACU-1 (Prophet version) Software Authority Protocol StarGuide III Satellite Receiver Wegener Unity4000 DVB Satellite Receiver +WheatNet Blade SLIO See the sections below for notes on each specific model. @@ -851,4 +852,18 @@ output at a time. Commanding a stream to an output will cause that stream to be silently deselected from a previously selected output. +---------------------------------------------------------------------------- +WHEATNET BLADE SLIO + +Driver Name: WheatNet SLIO + +Supported RML Commands: + GPO Set ('GO') + +GENERAL NOTES: +Control is done by means of a TCP/IP connection to port 55776 on a blade. +The only required configuration parameters are IP Address and IP Port. +The number of pins is auto-detected at driver startup. + + ---------------------------------------------------------------------------- diff --git a/lib/rdmatrix.cpp b/lib/rdmatrix.cpp index e9782744..f80a037b 100644 --- a/lib/rdmatrix.cpp +++ b/lib/rdmatrix.cpp @@ -68,7 +68,8 @@ bool __mx_primary_controls[RDMatrix::LastType][RDMatrix::LastControl]= {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0}, // BT ADMS 44.22 {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0}, // BT SS 4.1 MLR {0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0}, // Modbus - {0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0} // Kernel GPIO + {0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0}, // Kernel GPIO + {0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0} // WheatNet SLIO }; bool __mx_backup_controls[RDMatrix::LastType][RDMatrix::LastControl]= { @@ -113,7 +114,8 @@ bool __mx_backup_controls[RDMatrix::LastType][RDMatrix::LastControl]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // BT ADMS 44.22 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // BT SS 4.1 MLR {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // Modbus - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} // Kernel GPIO + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // Kernel GPIO + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} // WheatNet SLIO }; int __mx_default_values[RDMatrix::LastType][RDMatrix::LastControl]= @@ -159,7 +161,8 @@ int __mx_default_values[RDMatrix::LastType][RDMatrix::LastControl]= {0,0,0,0,0,0,0,0,0,0,0,8,2,16,13,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // BT ADMS 44.22 {0,0,0,0,0,0,0,0,0,0,0,4,1,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // BT SS 4.1 MLR {1,0,0,502,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // Modbus - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0} // Kernel GPIO + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // Kernel GPIO + {1,0,0,55776,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0} // WheatNet SLIO }; RDMatrix::RDMatrix(const QString &station,int matrix) @@ -731,6 +734,10 @@ QString RDMatrix::typeString(RDMatrix::Type type) return QString("Kernel GPIO"); break; + case RDMatrix::WheatnetSlio: + return QString("WheatNet SLIO"); + break; + default: return QString("Unknown Type"); break; diff --git a/lib/rdmatrix.h b/lib/rdmatrix.h index e7319ee3..4774dca0 100644 --- a/lib/rdmatrix.h +++ b/lib/rdmatrix.h @@ -38,7 +38,7 @@ class RDMatrix BtSrc16=24,Harlond=25,Acu1p=26,LiveWireMcastGpio=27,Am16=28, LiveWireLwrpGpio=29,BtSentinel4Web=30,BtGpi16=31,ModemLines=32, SoftwareAuthority=33,Sas16000=34,RossNkScp=35,BtAdms4422=36, - BtSs41Mlr=37,Modbus=38,KernelGpio=39,LastType=40}; + BtSs41Mlr=37,Modbus=38,KernelGpio=39,WheatnetSlio=40,LastType=41}; enum Endpoint {Input=0,Output=1}; enum Mode {Stereo=0,Left=1,Right=2}; enum VguestAttribute {VguestEngine=0,VguestDevice=1,VguestSurface=2, diff --git a/ripcd/Makefile.am b/ripcd/Makefile.am index df5610a8..479a59ed 100644 --- a/ripcd/Makefile.am +++ b/ripcd/Makefile.am @@ -76,7 +76,8 @@ dist_ripcd_SOURCES = acu1p.cpp acu1p.h\ switcher.cpp switcher.h\ unity4000.cpp unity4000.h\ unity_feed.cpp unity_feed.h\ - vguest.cpp vguest.h + vguest.cpp vguest.h\ + wheatnet_slio.cpp wheatnet_slio.h nodist_ripcd_SOURCES = moc_am16.cpp\ moc_acu1p.cpp\ @@ -118,7 +119,8 @@ nodist_ripcd_SOURCES = moc_am16.cpp\ moc_swauthority.cpp\ moc_switcher.cpp\ moc_unity4000.cpp\ - moc_vguest.cpp + moc_vguest.cpp\ + moc_wheatnet_slio.cpp ripcd_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ diff --git a/ripcd/loaddrivers.cpp b/ripcd/loaddrivers.cpp index 3a48e96d..64aa9c63 100644 --- a/ripcd/loaddrivers.cpp +++ b/ripcd/loaddrivers.cpp @@ -2,7 +2,7 @@ // // Load Switcher drivers for ripcd(8) // -// (C) Copyright 2002-2016 Fred Gleason +// (C) Copyright 2002-2017 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 @@ -61,6 +61,7 @@ #include #include #include +#include bool MainObject::LoadSwitchDriver(int matrix_num) { @@ -219,6 +220,10 @@ bool MainObject::LoadSwitchDriver(int matrix_num) ripcd_switcher[matrix_num]=new Unity4000(matrix,this); break; + case RDMatrix::WheatnetSlio: + ripcd_switcher[matrix_num]=new WheatnetSlio(matrix,this); + break; + default: ripcd_switcher[matrix_num]=NULL; delete matrix; diff --git a/ripcd/wheatnet_slio.cpp b/ripcd/wheatnet_slio.cpp new file mode 100644 index 00000000..5ab9eb35 --- /dev/null +++ b/ripcd/wheatnet_slio.cpp @@ -0,0 +1,370 @@ +// wheatnet_slio.cpp +// +// A Rivendell switcher driver for Modbus TCP +// +// (C) Copyright 2017 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_slio.h" + +WheatnetSlio::WheatnetSlio(RDMatrix *matrix,QObject *parent) + : Switcher(matrix,parent) +{ + slio_watchdog_active=false; + + slio_gpios=0; + slio_ip_address=matrix->ipAddress(RDMatrix::Primary); + slio_ip_port=matrix->ipPort(RDMatrix::Primary); + + slio_socket=new QSocket(this); + connect(slio_socket,SIGNAL(connected()),this,SLOT(connectedData())); + connect(slio_socket,SIGNAL(readyRead()),this,SLOT(readyReadData())); + connect(slio_socket,SIGNAL(error(int)),this,SLOT(errorData(int))); + slio_socket->connectToHost(slio_ip_address.toString(),slio_ip_port); + + slio_poll_timer=new QTimer(this); + connect(slio_poll_timer,SIGNAL(timeout()),this,SLOT(pollInputs())); + + slio_reset_mapper=new QSignalMapper(this); + connect(slio_reset_mapper,SIGNAL(mapped(int)), + this,SLOT(resetStateData(int))); + for(int i=0;isetMapping(slio_reset_timers.back(),i); + slio_reset_states.push_back(false); + } + slio_watchdog_timer=new QTimer(this); + connect(slio_watchdog_timer,SIGNAL(timeout()),this,SLOT(watchdogData())); +} + + +WheatnetSlio::~WheatnetSlio() +{ + delete slio_watchdog_timer; + delete slio_poll_timer; + for(unsigned i=0;icommand()) { + case RDMacro::GO: + if((cmd->argQuantity()!=5)|| + ((cmd->arg(1).toString().lower()!="i")&& + (cmd->arg(1).toString().lower()!="o"))|| + (cmd->arg(2).toInt()<1)||(cmd->arg(3).toInt()>slio_gpios)|| + (cmd->arg(2).toInt()>slio_gpios)|| + ((cmd->arg(3).toInt()!=1)&&(cmd->arg(3).toInt()!=0)&& + (cmd->arg(1).toString().lower()!="i"))|| + ((cmd->arg(3).toInt()!=1)&&(cmd->arg(3).toInt()!=0)&& + (cmd->arg(3).toInt()!=-1)&&(cmd->arg(1).toString().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).toString().lower()=="o") { + SendCommand(QString().sprintf("",cmd->arg(2).toInt())); + 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).toString().lower()=="o") { + SendCommand(QString().sprintf("",cmd->arg(2).toInt())); + emit gpoChanged(matrixNumber(),cmd->arg(2).toInt()-1,true); + } + } + else { // Pulse + if(cmd->arg(1).toString().lower()=="o") { + SendCommand(QString().sprintf("", + cmd->arg(2).toInt(), + cmd->arg(3).toInt()!=0)); + slio_reset_states[cmd->arg(2).toInt()-1]=cmd->arg(3).toInt()==0; + slio_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 WheatnetSlio::connectedData() +{ + syslog(LOG_INFO, + "connection to WheatNet SLIO device at %s:%u established", + (const char *)slio_ip_address.toString(),0xffff&slio_ip_port); + slio_watchdog_active=false; + SendCommand(""); +} + + +void WheatnetSlio::readyReadData() +{ + char data[1501]; + int n=0; + + while((n=slio_socket->readBlock(data,1500))>0) { + data[n]=0; + for(int i=0;i",i+1)); + } +} + + +void WheatnetSlio::resetStateData(int line) +{ + SendCommand(QString().sprintf("",line+1, + (int)slio_reset_states[line])); + emit gpoChanged(matrixNumber(),line,slio_reset_states[line]); +} + + +void WheatnetSlio::watchdogData() +{ + if(!slio_watchdog_active) { + syslog(LOG_WARNING, + "connection to Wheatnet SLIO device at %s:%u lost, attempting reconnect", + (const char *)slio_ip_address.toString(),0xffff&slio_ip_port); + slio_watchdog_active=true; + } + slio_socket->close(); + slio_socket->connectToHost(slio_ip_address.toString(),slio_ip_port); +} + + +void WheatnetSlio::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 WheatnetSlio::ProcessSys(const QString &cmd) +{ + // printf("SYS: %s\n",(const char *)cmd); + QString sql; + RDSqlQuery *q; + bool ok=false; + + QStringList f0=f0.split(":",cmd); + if((f0[0]=="SLIO")&&(f0.size()==2)) { + int slio=f0[1].toUInt(&ok); + if(ok) { + slio_gpios=slio; + for(unsigned i=0;isetMapping(slio_reset_timers.back(),i); + slio_reset_states.push_back(false); + slio_gpi_states.push_back(false); + CheckLineEntry(i+1); + } + sql=QString("update MATRICES set ")+ + QString().sprintf("GPIS=%d,GPOS=%d where ",slio_gpios,slio_gpios)+ + "(STATION_NAME=\""+RDEscapeString(stationName())+"\")&&"+ + QString().sprintf("(MATRIX=%d)",matrixNumber()); + q=new RDSqlQuery(sql); + delete q; + pollInputs(); + } + } +} + + +void WheatnetSlio::ProcessSlio(int chan,QString &cmd) +{ + // printf("ProcessSlip(%d,%s)\n",chan,(const char *)cmd); + QStringList f0=f0.split(":",cmd); + if((f0[0]=="LVL")&&(f0.size()==2)) { + if(chan<=(int)slio_gpi_states.size()) { + bool state=f0[1]=="1"; + if(state!=slio_gpi_states[chan-1]) { + printf("change chan: %d state: %d\n",chan,state); + slio_gpi_states[chan-1]=state; + emit gpiChanged(matrixNumber(),chan-1,state); + } + } + else { + syslog(LOG_WARNING, + "WheatNet device at %s:%d sent invalid SLIO LVL update [%s]", + (const char *)slio_ip_address.toString(), + slio_ip_port,(const char *)cmd); + } + if(chan==slio_gpios) { + slio_poll_timer->start(50,true); + slio_watchdog_timer->stop(); + slio_watchdog_timer->start(1000,true); + } + } +} + + +void WheatnetSlio::ProcessCommand(const QString &cmd) +{ + // printf("ProcessCommand(%s)\n",(const char *)cmd); + bool ok=false; + + if((cmd.left(1)=="<")&&(cmd.right(1)==">")) { + QStringList f0=f0.split("|",cmd.mid(1,cmd.length()-2)); + if(f0.size()==2) { + QStringList f1=f1.split(":",f0[0]); + if(f1[0]=="SYS") { + ProcessSys(f0[1]); + } + if((f1[0]=="SLIO")&&(f1.size()==2)) { + int chan=f1[1].toUInt(&ok); + if(ok) { + ProcessSlio(chan,f0[1]); + } + } + } + } +} + + +void WheatnetSlio::SendCommand(const QString &cmd) +{ + slio_socket->writeBlock(cmd+"\r\n",cmd.length()+2); +} diff --git a/ripcd/wheatnet_slio.h b/ripcd/wheatnet_slio.h new file mode 100644 index 00000000..096e1706 --- /dev/null +++ b/ripcd/wheatnet_slio.h @@ -0,0 +1,81 @@ +// wheatnet_slio.h +// +// A Rivendell switcher driver for Wheatnet SLIO devices +// +// (C) Copyright 2017 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. +// + +#ifndef WHEATNET_SLIO_H +#define WHEATNET_SLIO_H + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#define WHEATNET_SLIO_POLL_INTERVAL 100 +#define WHEATNET_SLIO_WATCHDOG_INTERVAL 1000 + +class WheatnetSlio : public Switcher +{ + Q_OBJECT + public: + WheatnetSlio(RDMatrix *matrix,QObject *parent=0); + ~WheatnetSlio(); + RDMatrix::Type type(); + unsigned gpiQuantity(); + unsigned gpoQuantity(); + bool primaryTtyActive(); + bool secondaryTtyActive(); + void processCommand(RDMacro *cmd); + + private slots: + void connectedData(); + void readyReadData(); + void errorData(int err); + void pollInputs(); + void resetStateData(int line); + void watchdogData(); + + private: + void CheckLineEntry(int line); + void ProcessSys(const QString &cmd); + void ProcessSlio(int chan,QString &cmd); + void ProcessCommand(const QString &cmd); + void SendCommand(const QString &cmd); + QSocket *slio_socket; + QTimer *slio_poll_timer; + QTimer *slio_watchdog_timer; + bool slio_watchdog_active; + QHostAddress slio_ip_address; + uint16_t slio_ip_port; + int slio_gpios; + QString slio_accum; + std::vector slio_gpi_states; + QSignalMapper *slio_reset_mapper; + std::vector slio_reset_timers; + std::vector slio_reset_states; +}; + + +#endif // WHEATNET_SLIO_H