mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-04-07 01:13:50 +02:00
* Fixed a regression in the implmenetation of the 'Set Serial Trap' ['SI'] RML that could cause deadlocks and intermittent operation.
857 lines
21 KiB
C++
857 lines
21 KiB
C++
// ripcd.cpp
|
|
//
|
|
// Rivendell Interprocess Communication Daemon
|
|
//
|
|
// (C) Copyright 2002-2020 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 <syslog.h>
|
|
|
|
#include <qapplication.h>
|
|
#include <qobject.h>
|
|
#include <qtimer.h>
|
|
#include <qdir.h>
|
|
#include <qsessionmanager.h>
|
|
#include <qsignalmapper.h>
|
|
#include <qstringlist.h>
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <netdb.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <rdapplication.h>
|
|
#include <rdconf.h>
|
|
#include <rdescape_string.h>
|
|
#include <rdnotification.h>
|
|
|
|
#include "globals.h"
|
|
#include "ripcd.h"
|
|
|
|
bool global_exiting=false;
|
|
|
|
void SigHandler(int signo)
|
|
{
|
|
pid_t pLocalPid;
|
|
|
|
switch(signo) {
|
|
case SIGCHLD:
|
|
pLocalPid=waitpid(-1,NULL,WNOHANG);
|
|
while(pLocalPid>0) {
|
|
pLocalPid=waitpid(-1,NULL,WNOHANG);
|
|
}
|
|
::signal(SIGCHLD,SigHandler);
|
|
::signal(SIGTERM,SigHandler);
|
|
::signal(SIGINT,SigHandler);
|
|
return;
|
|
|
|
case SIGTERM:
|
|
case SIGINT:
|
|
global_exiting=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
MainObject::MainObject(QObject *parent)
|
|
:QObject(parent)
|
|
{
|
|
QString err_msg;
|
|
RDApplication::ErrorType err_type=RDApplication::ErrorOk;
|
|
|
|
rda=new RDApplication("ripcd","ripcd",RIPCD_USAGE,this);
|
|
if(!rda->open(&err_msg,&err_type,false)) {
|
|
fprintf(stderr,"ripcd: %s\n",(const char *)err_msg.utf8());
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// Initialize Data Structures
|
|
//
|
|
debug=false;
|
|
for(int i=0;i<MAX_MATRICES;i++) {
|
|
ripcd_switcher[i]=NULL;
|
|
for(int j=0;j<MAX_GPIO_PINS;j++) {
|
|
ripcd_gpi_state[i][j]=false;
|
|
ripcd_gpo_state[i][j]=false;
|
|
}
|
|
}
|
|
ripc_onair_flag=false;
|
|
|
|
//
|
|
// Client Connections
|
|
//
|
|
ripcd_ready_mapper=new QSignalMapper(this);
|
|
connect(ripcd_ready_mapper,SIGNAL(mapped(int)),this,SLOT(readyReadData(int)));
|
|
|
|
ripcd_kill_mapper=new QSignalMapper(this);
|
|
connect(ripcd_kill_mapper,SIGNAL(mapped(int)),this,SLOT(killData(int)));
|
|
server=new QTcpServer(this);
|
|
if(!server->listen(QHostAddress::Any,RIPCD_TCP_PORT)) {
|
|
rda->syslog(LOG_ERR,"unable to bind ripc port");
|
|
exit(1);
|
|
}
|
|
connect(server,SIGNAL(newConnection()),this,SLOT(newConnectionData()));
|
|
|
|
//
|
|
// Macro Timers
|
|
//
|
|
QSignalMapper *mapper=new QSignalMapper(this);
|
|
connect(mapper,SIGNAL(mapped(int)),this,SLOT(macroTimerData(int)));
|
|
for(int i=0;i<RD_MAX_MACRO_TIMERS;i++) {
|
|
ripc_macro_cart[i]=0;
|
|
ripc_macro_timer[i]=new QTimer(this);
|
|
mapper->setMapping(ripc_macro_timer[i],i);
|
|
connect(ripc_macro_timer[i],SIGNAL(timeout()),mapper,SLOT(map()));
|
|
}
|
|
|
|
//
|
|
// TTY Ready Read Mapper
|
|
//
|
|
ripcd_tty_ready_read_mapper=new QSignalMapper(this);
|
|
connect(ripcd_tty_ready_read_mapper,SIGNAL(mapped(int)),
|
|
this,SLOT(ttyReadyReadData(int)));
|
|
|
|
ripcd_host_addr=rda->station()->address();
|
|
|
|
//
|
|
// CAE Connection
|
|
//
|
|
rda->cae()->connectHost();
|
|
|
|
if(qApp->argc()!=1) {
|
|
debug=true;
|
|
}
|
|
::signal(SIGCHLD,SigHandler);
|
|
::signal(SIGTERM,SigHandler);
|
|
::signal(SIGINT,SigHandler);
|
|
|
|
//
|
|
// The RML Sockets
|
|
//
|
|
ripcd_rml_send=new QUdpSocket(this);
|
|
|
|
ripcd_rml_echo=new QUdpSocket(this);
|
|
ripcd_rml_echo->bind(QHostAddress::Any,RD_RML_ECHO_PORT);
|
|
connect(ripcd_rml_echo,SIGNAL(readyRead()),this,SLOT(rmlEchoData()));
|
|
|
|
ripcd_rml_noecho=new QUdpSocket(this);
|
|
ripcd_rml_noecho->bind(QHostAddress::Any,RD_RML_NOECHO_PORT);
|
|
connect(ripcd_rml_noecho,SIGNAL(readyRead()),this,SLOT(rmlNoechoData()));
|
|
|
|
ripcd_rml_reply=new QUdpSocket(this);
|
|
ripcd_rml_reply->bind(QHostAddress::Any,RD_RML_REPLY_PORT);
|
|
connect(ripcd_rml_reply,SIGNAL(readyRead()),this,SLOT(rmlReplyData()));
|
|
|
|
LoadGpiTable();
|
|
|
|
//
|
|
// Initialize local RMLs
|
|
//
|
|
LoadLocalMacros();
|
|
|
|
//
|
|
// Initialize Notifications
|
|
//
|
|
ripcd_notification_mcaster=new RDMulticaster(this);
|
|
ripcd_notification_mcaster->enableLoopback(false);
|
|
connect(ripcd_notification_mcaster,
|
|
SIGNAL(received(const QString &,const QHostAddress &)),
|
|
this,
|
|
SLOT(notificationReceivedData(const QString &,const QHostAddress &)));
|
|
ripcd_notification_mcaster->bind(RD_NOTIFICATION_PORT);
|
|
ripcd_notification_mcaster->subscribe(rda->system()->notificationAddress());
|
|
|
|
//
|
|
// Exit Timer
|
|
//
|
|
QTimer *timer=new QTimer(this);
|
|
connect(timer,SIGNAL(timeout()),this,SLOT(exitTimerData()));
|
|
timer->start(200);
|
|
|
|
//
|
|
// Garbage Timer
|
|
//
|
|
ripcd_garbage_timer=new QTimer(this);
|
|
connect(ripcd_garbage_timer,SIGNAL(timeout()),this,SLOT(garbageData()));
|
|
|
|
//
|
|
// JACK
|
|
//
|
|
#ifdef JACK
|
|
ripcd_start_jack_timer=new QTimer(this);
|
|
ripcd_start_jack_timer->setSingleShot(true);
|
|
connect(ripcd_start_jack_timer, SIGNAL(timeout()),this,SLOT(startJackData()));
|
|
ripcd_start_jack_timer->start(5000);
|
|
#endif // JACK
|
|
|
|
|
|
rda->syslog(LOG_INFO,"started");
|
|
}
|
|
|
|
|
|
MainObject::~MainObject()
|
|
{
|
|
delete server;
|
|
delete ripcd_db;
|
|
}
|
|
|
|
|
|
void MainObject::newConnectionData()
|
|
{
|
|
unsigned i=0;
|
|
|
|
QTcpSocket *sock=server->nextPendingConnection();
|
|
while((i<ripcd_conns.size())&&(ripcd_conns[i]!=NULL)) {
|
|
i++;
|
|
}
|
|
if(i==ripcd_conns.size()) { // Table full, create a new slot
|
|
ripcd_conns.push_back(new RipcdConnection(i,sock));
|
|
}
|
|
else {
|
|
ripcd_conns[i]=new RipcdConnection(i,sock);
|
|
}
|
|
ripcd_ready_mapper->setMapping(ripcd_conns[i]->socket(),i);
|
|
connect(ripcd_conns[i]->socket(),SIGNAL(readyRead()),
|
|
ripcd_ready_mapper,SLOT(map()));
|
|
ripcd_kill_mapper->setMapping(ripcd_conns[i]->socket(),i);
|
|
connect(ripcd_conns[i]->socket(),SIGNAL(connectionClosed()),
|
|
ripcd_kill_mapper,SLOT(map()));
|
|
rda->syslog(LOG_DEBUG,"added new connection %d",i);
|
|
}
|
|
|
|
|
|
void MainObject::notificationReceivedData(const QString &msg,
|
|
const QHostAddress &addr)
|
|
{
|
|
RDNotification *notify=new RDNotification();
|
|
|
|
if(!notify->read(msg)) {
|
|
rda->syslog(LOG_INFO,"invalid notification received from %s",
|
|
(const char *)addr.toString().toUtf8());
|
|
delete notify;
|
|
return;
|
|
}
|
|
RunLocalNotifications(notify);
|
|
BroadcastCommand("ON "+msg+"!");
|
|
|
|
delete notify;
|
|
}
|
|
|
|
|
|
void MainObject::sendRml(RDMacro *rml)
|
|
{
|
|
QString str;
|
|
|
|
if(rml->isNull()) {
|
|
return;
|
|
}
|
|
str=rml->toString();
|
|
switch(rml->role()) {
|
|
case RDMacro::Cmd:
|
|
ripcd_rml_send->writeDatagram(str.utf8(),str.utf8().length(),
|
|
rml->address(),rml->port());
|
|
break;
|
|
|
|
case RDMacro::Reply:
|
|
if(!(ripcd_host_addr==rml->address())) {
|
|
ripcd_rml_send->writeDatagram(str.utf8(),str.utf8().length(),
|
|
rml->address(),RD_RML_REPLY_PORT);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::rmlEchoData()
|
|
{
|
|
ReadRmlSocket(ripcd_rml_echo,RDMacro::Cmd,true);
|
|
}
|
|
|
|
|
|
void MainObject::rmlNoechoData()
|
|
{
|
|
ReadRmlSocket(ripcd_rml_noecho,RDMacro::Cmd,false);
|
|
}
|
|
|
|
|
|
void MainObject::rmlReplyData()
|
|
{
|
|
ReadRmlSocket(ripcd_rml_reply,RDMacro::Reply,false);
|
|
}
|
|
|
|
|
|
void MainObject::readyReadData(int conn_id)
|
|
{
|
|
char data[1501];
|
|
int n;
|
|
RipcdConnection *conn=ripcd_conns[conn_id];
|
|
QChar c;
|
|
|
|
while((n=conn->socket()->readBlock(data,1500))>0) {
|
|
data[n]=0;
|
|
QString line=QString::fromUtf8(data);
|
|
for(int i=0;i<line.length();i++) {
|
|
QChar c=line.at(i);
|
|
if(c.toAscii()=='!') {
|
|
if(!DispatchCommand(conn)) {
|
|
return;
|
|
}
|
|
conn->accum="";
|
|
}
|
|
else {
|
|
if((c.toAscii()!='\r')&&(c.toAscii()!='\n')) {
|
|
conn->accum+=c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::killData(int conn_id)
|
|
{
|
|
ripcd_conns[conn_id]->close();
|
|
ripcd_garbage_timer->start(1,true);
|
|
rda->syslog(LOG_DEBUG,"closed connection %d",conn_id);
|
|
}
|
|
|
|
|
|
void MainObject::macroTimerData(int num)
|
|
{
|
|
ExecCart(ripc_macro_cart[num]);
|
|
ripc_macro_cart[num]=0;
|
|
}
|
|
|
|
|
|
void MainObject::exitTimerData()
|
|
{
|
|
if(global_exiting) {
|
|
for(int i=0;i<MAX_MATRICES;i++) {
|
|
if(ripcd_switcher[i]!=NULL) {
|
|
delete ripcd_switcher[i];
|
|
}
|
|
}
|
|
rda->syslog(LOG_INFO,"exiting normally");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::garbageData()
|
|
{
|
|
for(unsigned i=0;i<ripcd_conns.size();i++) {
|
|
if(ripcd_conns[i]!=NULL) {
|
|
if(ripcd_conns[i]->isClosing()) {
|
|
delete ripcd_conns[i];
|
|
ripcd_conns[i]=NULL;
|
|
rda->syslog(LOG_DEBUG,"cleaned up connection %d",i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::startJackData()
|
|
{
|
|
#ifdef JACK
|
|
jack_options_t jackopts=JackNullOption;
|
|
jack_status_t jackstat=JackFailure;
|
|
|
|
//
|
|
// Attempt to Connect to Jack Server
|
|
//
|
|
jackopts=JackNoStartServer;
|
|
if(rda->station()->jackServerName().isEmpty()) {
|
|
ripcd_jack_client=jack_client_open("rivendell-ripcd",jackopts,&jackstat);
|
|
}
|
|
else {
|
|
ripcd_jack_client=
|
|
jack_client_open("rivendell-ripcd",jackopts,&jackstat,
|
|
(const char *)rda->station()->jackServerName());
|
|
}
|
|
if(ripcd_jack_client==NULL) {
|
|
if((jackstat&JackInvalidOption)!=0) {
|
|
fprintf (stderr, "invalid or unsupported JACK option\n");
|
|
rda->syslog(LOG_WARNING,"invalid or unsupported JACK option");
|
|
}
|
|
|
|
if((jackstat&JackServerError)!=0) {
|
|
fprintf (stderr, "communication error with the JACK server\n");
|
|
rda->syslog(LOG_WARNING,"communication error with the JACK server");
|
|
}
|
|
|
|
if((jackstat&JackNoSuchClient)!=0) {
|
|
fprintf (stderr, "requested JACK client does not exist\n");
|
|
rda->syslog(LOG_WARNING,"requested JACK client does not exist");
|
|
}
|
|
|
|
if((jackstat&JackLoadFailure)!=0) {
|
|
fprintf (stderr, "unable to load internal JACK client\n");
|
|
rda->syslog(LOG_WARNING,"unable to load internal JACK client");
|
|
}
|
|
|
|
if((jackstat&JackInitFailure)!=0) {
|
|
fprintf (stderr, "unable to initialize JACK client\n");
|
|
rda->syslog(LOG_WARNING,"unable to initialize JACK client");
|
|
}
|
|
|
|
if((jackstat&JackShmFailure)!=0) {
|
|
fprintf (stderr, "unable to access JACK shared memory\n");
|
|
rda->syslog(LOG_WARNING,"unable to access JACK shared memory");
|
|
}
|
|
|
|
if((jackstat&JackVersionError)!=0) {
|
|
fprintf (stderr, "JACK protocol version mismatch\n");
|
|
rda->syslog(LOG_WARNING,"JACK protocol version mismatch");
|
|
}
|
|
|
|
if((jackstat&JackServerStarted)!=0) {
|
|
fprintf (stderr, "JACK server started\n");
|
|
rda->syslog(LOG_WARNING,"JACK server started");
|
|
}
|
|
|
|
if((jackstat&JackServerFailed)!=0) {
|
|
fprintf (stderr, "unable to communication with JACK server\n");
|
|
rda->syslog(LOG_WARNING,"unable to communicate with JACK server");
|
|
}
|
|
|
|
if((jackstat&JackNameNotUnique)!=0) {
|
|
fprintf (stderr, "JACK client name not unique\n");
|
|
rda->syslog(LOG_WARNING,"JACK client name not unique");
|
|
}
|
|
|
|
if((jackstat&JackFailure)!=0) {
|
|
fprintf (stderr, "JACK general failure\n");
|
|
rda->syslog(LOG_WARNING,"JACK general failure");
|
|
}
|
|
fprintf (stderr, "no connection to JACK server\n");
|
|
rda->syslog(LOG_WARNING,"no connection to JACK server");
|
|
return;
|
|
}
|
|
// jack_connected=true;
|
|
// jack_set_process_callback(jack_client,JackProcess,0);
|
|
// jack_set_sample_rate_callback(jack_client,JackSampleRate,0);
|
|
//jack_set_port_connect_callback(jack_client,JackPortConnectCB,this);
|
|
#ifdef HAVE_JACK_INFO_SHUTDOWN
|
|
// jack_on_info_shutdown(jack_client,JackInfoShutdown,0);
|
|
#else
|
|
// jack_on_shutdown(jack_client,JackShutdown,0);
|
|
#endif // HAVE_JACK_INFO_SHUTDOWN
|
|
rda->syslog(LOG_INFO,"connected to JACK server");
|
|
|
|
|
|
#endif // JACK
|
|
}
|
|
|
|
|
|
void MainObject::SetUser(QString username)
|
|
{
|
|
rda->station()->setUserName(username);
|
|
BroadcastCommand(QString("RU ")+username+"!");
|
|
}
|
|
|
|
|
|
bool MainObject::DispatchCommand(RipcdConnection *conn)
|
|
{
|
|
QString str;
|
|
RDMacro macro;
|
|
int echo=0;
|
|
QHostAddress addr;
|
|
|
|
//printf("DispatchCommand(%s)\n",(const char *)conn->accum.toUtf8());
|
|
QStringList cmds=conn->accum.split(" ",QString::SkipEmptyParts);
|
|
|
|
//
|
|
// Common Commands
|
|
// Authentication not required to execute these!
|
|
//
|
|
if(cmds[0]=="DC") { // Drop Connection
|
|
killData(conn->id());
|
|
return false;
|
|
}
|
|
|
|
if((cmds[0]=="PW")&&(cmds.size()==2)) { // Password Authenticate
|
|
if(cmds[1]==rda->config()->password()) {
|
|
conn->setAuthenticated(true);
|
|
EchoCommand(conn->id(),"PW +!");
|
|
return true;
|
|
}
|
|
else {
|
|
conn->setAuthenticated(false);
|
|
EchoCommand(conn->id(),"PW -!");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Priviledged Commands
|
|
// Authentication required to execute these!
|
|
//
|
|
if(!conn->isAuthenticated()) {
|
|
EchoCommand(conn->id(),cmds.join(" ")+"-!");
|
|
return true;
|
|
}
|
|
if(cmds[0]=="RU") { // Request User
|
|
EchoCommand(conn->id(),(const char *)QString("RU ")+rda->station()->userName()+"!");
|
|
return true;
|
|
}
|
|
|
|
if((cmds[0]=="SU")&&(cmds.size()==2)) { // Set User
|
|
SetUser(cmds[1]);
|
|
}
|
|
|
|
if(cmds[0]=="MS") { // Send RML Command
|
|
if(cmds.size()<4) {
|
|
return true;
|
|
}
|
|
str=cmds[3];
|
|
for(int i=4;i<cmds.size();i++) {
|
|
str+=" "+cmds[i];
|
|
}
|
|
str+="!";
|
|
}
|
|
macro=RDMacro::fromString(str);
|
|
if(!macro.isNull()) {
|
|
addr.setAddress(cmds[1]);
|
|
macro.setAddress(addr);
|
|
macro.setPort(cmds[2].toInt());
|
|
macro.setRole(RDMacro::Cmd);
|
|
|
|
if(!macro.address().isNull()) {
|
|
if(macro.address()==rda->station()->address()&&
|
|
((macro.port()==RD_RML_ECHO_PORT)||
|
|
(macro.port()==RD_RML_NOECHO_PORT))) { // Local Loopback
|
|
if(macro.echoRequested()) {
|
|
echo=1;
|
|
}
|
|
RunLocalMacros(¯o);
|
|
BroadcastCommand(QString("MS ")+macro.address().toString()+
|
|
QString().sprintf(" %d ",echo)+macro.toString());
|
|
}
|
|
else {
|
|
sendRml(¯o);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(cmds[0]=="ME") { // Send RML Reply
|
|
if(cmds.size()<4) {
|
|
return true;
|
|
}
|
|
str=cmds[3];
|
|
for(int i=4;i<cmds.size();i++) {
|
|
str+=" "+cmds[i];
|
|
}
|
|
str+="!";
|
|
}
|
|
macro=RDMacro::fromString(str,RDMacro::Reply);
|
|
if(!macro.isNull()) {
|
|
QHostAddress addr;
|
|
addr.setAddress(cmds[1]);
|
|
macro.setAddress(addr);
|
|
if(macro.address()==rda->station()->address()) { // Local Loopback
|
|
BroadcastCommand(QString("ME ")+macro.address().toString()+" 0 "+
|
|
macro.toString());
|
|
}
|
|
else {
|
|
sendRml(¯o);
|
|
}
|
|
}
|
|
|
|
if(cmds[0]=="RG") { // Reload the GPI Table
|
|
LoadGpiTable();
|
|
}
|
|
|
|
if((cmds[0]=="GI")&&(cmds.size()==2)) { // Send Complete GPI Status
|
|
SendGpi(conn->id(),cmds[1].toInt());
|
|
}
|
|
|
|
if((cmds[0]=="GO")&&(cmds.size()==2)) { // Send Complete GPO Status
|
|
SendGpo(conn->id(),cmds[1].toInt());
|
|
}
|
|
|
|
if((cmds[0]=="GM")&&(cmds.size()==2)) { // Send Complete GPI Mask States
|
|
SendGpiMask(conn->id(),cmds[1].toInt());
|
|
}
|
|
|
|
if((cmds[0]=="GN")&&(cmds.size()==2)) { // Send Complete GPO Mask States
|
|
SendGpoMask(conn->id(),cmds[1].toInt());
|
|
}
|
|
|
|
if((cmds[0]=="GC")&&(cmds.size()==2)) { // Send Complete GPI Cart Assignments
|
|
SendGpiCart(conn->id(),cmds[1].toInt());
|
|
}
|
|
|
|
if((cmds[0]=="GD")&&(cmds.size()==2)) { // Send Complete GPO Cart Assignments
|
|
SendGpoCart(conn->id(),cmds[1].toInt());
|
|
}
|
|
|
|
if(cmds[0]=="ON") { // Send Notification
|
|
QString msg;
|
|
for(int i=1;i<cmds.size();i++) {
|
|
msg+=QString(cmds[i])+" ";
|
|
}
|
|
msg=msg.left(msg.length()-1);
|
|
RDNotification *notify=new RDNotification();
|
|
if(!notify->read(msg)) {
|
|
rda->syslog(LOG_INFO,"invalid notification processed");
|
|
delete notify;
|
|
return true;
|
|
}
|
|
RunLocalNotifications(notify);
|
|
BroadcastCommand("ON "+msg+"!",conn->id());
|
|
ripcd_notification_mcaster->
|
|
send(msg,rda->system()->notificationAddress(),RD_NOTIFICATION_PORT);
|
|
rda->syslog(LOG_DEBUG,"sent notification: \"%s\" to %s:%d",
|
|
(const char *)msg.toUtf8(),
|
|
(const char *)rda->system()->notificationAddress().
|
|
toString().toUtf8(),
|
|
RD_NOTIFICATION_PORT);
|
|
delete notify;
|
|
}
|
|
|
|
if(cmds[0]=="TA") { // Send Onair Flag State
|
|
EchoCommand(conn->id(),QString().sprintf("TA %d!",ripc_onair_flag));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void MainObject::EchoCommand(int ch,const QString &cmd)
|
|
{
|
|
// printf("EchoCommand(%d,%s)\n",ch,(const char *)cmd.utf8());
|
|
if(ripcd_conns[ch]->socket()->state()==QAbstractSocket::ConnectedState) {
|
|
ripcd_conns[ch]->socket()->writeBlock(cmd.utf8(),cmd.utf8().length());
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::BroadcastCommand(const QString &cmd,int except_ch)
|
|
{
|
|
for(unsigned i=0;i<ripcd_conns.size();i++) {
|
|
if((int)i!=except_ch) {
|
|
if(ripcd_conns[i]!=NULL) {
|
|
EchoCommand(i,cmd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::ReadRmlSocket(QUdpSocket *sock,RDMacro::Role role,
|
|
bool echo)
|
|
{
|
|
char buffer[1501];
|
|
QString output;
|
|
int n;
|
|
QHostAddress peer_addr;
|
|
RDMacro macro;
|
|
|
|
while((n=sock->readDatagram(buffer,1501,&peer_addr))>0) {
|
|
buffer[n]=0;
|
|
macro=RDMacro::fromString(QString::fromUtf8(buffer));
|
|
if(!macro.isNull()) {
|
|
if(macro.command()==RDMacro::AG) {
|
|
if(ripc_onair_flag) {
|
|
QStringList f0=
|
|
QString::fromUtf8(buffer).split(" ",QString::SkipEmptyParts);
|
|
f0.pop_front();
|
|
QString rmlstr=f0.join(" ");
|
|
macro=RDMacro::fromString(rmlstr);
|
|
if(macro.isNull()) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
rda->syslog(LOG_INFO,
|
|
"rejected rml: \"%s\": on-air flag not active",buffer);
|
|
break;
|
|
}
|
|
}
|
|
macro.setRole(role);
|
|
macro.setAddress(peer_addr);
|
|
macro.setEchoRequested(echo);
|
|
switch(role) {
|
|
case RDMacro::Cmd:
|
|
RunLocalMacros(¯o);
|
|
BroadcastCommand(QString("MS ")+macro.address().toString()+
|
|
QString().sprintf(" %d ",echo)+
|
|
macro.toString());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
rda->syslog(LOG_INFO,"received malformed rml: \"%s\" from %s:%u",
|
|
buffer,
|
|
(const char *)sock->peerAddress().toString().toUtf8(),
|
|
sock->peerPort());
|
|
if(echo) {
|
|
macro.setRole(RDMacro::Reply);
|
|
macro.setCommand(RDMacro::NN);
|
|
macro.addArg("-");
|
|
macro.setAddress(peer_addr);
|
|
sendRml(¯o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::LoadGpiTable()
|
|
{
|
|
for(int i=0;i<MAX_MATRICES;i++) {
|
|
for(int j=0;j<MAX_GPIO_PINS;j++) {
|
|
ripcd_gpi_mask[i][j]=true;
|
|
ripcd_gpo_mask[i][j]=true;
|
|
for(int k=0;k<2;k++) {
|
|
ripcd_gpi_macro[i][j][k]=0;
|
|
ripcd_gpo_macro[i][j][k]=0;
|
|
}
|
|
}
|
|
}
|
|
QString sql=QString("select ")+
|
|
"MATRIX,"+ // 00
|
|
"NUMBER,"+ // 01
|
|
"OFF_MACRO_CART,"+ // 02
|
|
"MACRO_CART "+ // 03
|
|
"from GPIS where "+
|
|
"STATION_NAME=\""+RDEscapeString(rda->config()->stationName())+"\"";
|
|
RDSqlQuery *q=new RDSqlQuery(sql);
|
|
while(q->next()) {
|
|
ripcd_gpi_macro[q->value(0).toInt()][q->value(1).toInt()-1][0]=
|
|
q->value(2).toInt();
|
|
ripcd_gpi_macro[q->value(0).toInt()][q->value(1).toInt()-1][1]=
|
|
q->value(3).toInt();
|
|
}
|
|
delete q;
|
|
|
|
sql=QString("select ")+
|
|
"MATRIX,"+ // 00
|
|
"NUMBER,"+ // 01
|
|
"OFF_MACRO_CART,"+ // 02
|
|
"MACRO_CART "+ // 03
|
|
"from GPOS where "+
|
|
"STATION_NAME=\""+RDEscapeString(rda->config()->stationName())+"\"";
|
|
q=new RDSqlQuery(sql);
|
|
while(q->next()) {
|
|
ripcd_gpo_macro[q->value(0).toInt()][q->value(1).toInt()-1][0]=
|
|
q->value(2).toInt();
|
|
ripcd_gpo_macro[q->value(0).toInt()][q->value(1).toInt()-1][1]=
|
|
q->value(3).toInt();
|
|
}
|
|
delete q;
|
|
rda->syslog(LOG_DEBUG,"GPIO table settings reloaded");
|
|
}
|
|
|
|
|
|
void MainObject::SendGpi(int ch,int matrix)
|
|
{
|
|
if(ripcd_switcher[matrix]==NULL) {
|
|
return;
|
|
}
|
|
for(unsigned i=0;i<ripcd_switcher[matrix]->gpiQuantity();i++) {
|
|
EchoCommand(ch,QString().sprintf("GI %d %d %d %d!",
|
|
matrix,i,ripcd_gpi_state[matrix][i],
|
|
ripcd_gpi_mask[matrix][i]));
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::SendGpo(int ch,int matrix)
|
|
{
|
|
if(ripcd_switcher[matrix]==NULL) {
|
|
return;
|
|
}
|
|
for(unsigned i=0;i<ripcd_switcher[matrix]->gpoQuantity();i++) {
|
|
EchoCommand(ch,QString().sprintf("GO %d %d %d %d!",
|
|
matrix,i,ripcd_gpo_state[matrix][i],
|
|
ripcd_gpo_mask[matrix][i]));
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::SendGpiMask(int ch,int matrix)
|
|
{
|
|
if(ripcd_switcher[matrix]==NULL) {
|
|
return;
|
|
}
|
|
for(unsigned i=0;i<ripcd_switcher[matrix]->gpiQuantity();i++) {
|
|
EchoCommand(ch,QString().sprintf("GM %d %d %d!",
|
|
matrix,i,ripcd_gpi_mask[matrix][i]));
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::SendGpoMask(int ch,int matrix)
|
|
{
|
|
if(ripcd_switcher[matrix]==NULL) {
|
|
return;
|
|
}
|
|
for(unsigned i=0;i<ripcd_switcher[matrix]->gpoQuantity();i++) {
|
|
EchoCommand(ch,QString().sprintf("GN %d %d %d!",
|
|
matrix,i,ripcd_gpo_mask[matrix][i]));
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::SendGpiCart(int ch,int matrix)
|
|
{
|
|
if(ripcd_switcher[matrix]==NULL) {
|
|
return;
|
|
}
|
|
for(unsigned i=0;i<ripcd_switcher[matrix]->gpiQuantity();i++) {
|
|
EchoCommand(ch,QString().sprintf("GC %d %d %d %d!",
|
|
matrix,i,ripcd_gpi_macro[matrix][i][0],
|
|
ripcd_gpi_macro[matrix][i][1]));
|
|
}
|
|
}
|
|
|
|
|
|
void MainObject::SendGpoCart(int ch,int matrix)
|
|
{
|
|
if(ripcd_switcher[matrix]==NULL) {
|
|
return;
|
|
}
|
|
for(unsigned i=0;i<ripcd_switcher[matrix]->gpoQuantity();i++) {
|
|
EchoCommand(ch,QString().sprintf("GD %d %d %d %d!",
|
|
matrix,i,ripcd_gpo_macro[matrix][i][0],
|
|
ripcd_gpo_macro[matrix][i][1]));
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc,char *argv[])
|
|
{
|
|
QApplication a(argc,argv,false);
|
|
new MainObject();
|
|
return a.exec();
|
|
}
|