2022-10-28 Fred Gleason <fredg@paravelsystems.com>

* Added an 'RDCatchEvent' class.
	* Reimplemented the 'Deck Event Processed' command using
	'RDCatchEvent'.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2022-10-28 09:26:37 -04:00
parent 751600dd5d
commit d38349cf39
21 changed files with 863 additions and 153 deletions

View File

@ -23565,3 +23565,7 @@
* Incremented the package version to 4.0.0rc0int1.
2022-10-25 Fred Gleason <fredg@paravelsystems.com>
* Updated the 'Rivendell Notification Protocol' API document.
2022-10-28 Fred Gleason <fredg@paravelsystems.com>
* Added an 'RDCatchEvent' class.
* Reimplemented the 'Deck Event Processed' command using
'RDCatchEvent'.

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<article xmlns="http://docbook.org/ns/docbook" version="5.0">
<article xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Rivendell Notification Protocol</title>
<author>
@ -14,26 +14,65 @@
<sect1 xml:id="overview">
<title>Overview</title>
<para>
This defines the IP protocol used to notify Rivendell components of
changes to the state of database objects. Messages are send by means
of multicast UDP packets to port 20539.
This document defines the IP protocol used by Rivendell for real-time
communication between different modules and/or hosts. Messages are sent
by means of multicast UDP packets to port 20539. The IPv4 multicast group
address used is defined the in the SYSTEM.NOTIFICATION_ADDRESS database
field.
</para>
<para>
Update messages are textual, formatted as follows:
Update messages are textual, with individual fields delimited by
the ASCII SPACE character. They are formatted as follows:
</para>
<para>
NOTIFY <replaceable choice='req'>keyword</replaceable>
<replaceable choice='req'>keyword</replaceable>
<replaceable choice='req'>arg1</replaceable>
<replaceable choice='req'>arg2</replaceable>
<replaceable choice='opt'>[...]</replaceable>
</para>
<para>
where <replaceable>keyword</replaceable> defines the class of message.
The following classes are defined:
</para>
<variablelist>
<varlistentry>
<term>Notifications</term>
<listitem>
<para>
Signals a change to a Rivendell database object.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Catch Events</term>
<listitem>
<para>
RDCatch state changes.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 xml:id="sect.notification_messages">
<title>Notification Messages</title>
<para>
Notification messages use the following format:
</para>
<para>
NOTIFY <replaceable choice='req'>type</replaceable>
<replaceable choice='req'>action</replaceable>
<replaceable choice='req'>id</replaceable>
</para>
<variablelist>
<varlistentry>
<term>
<replaceable>keyword</replaceable>
<replaceable>type</replaceable>
</term>
<listitem>
<para>
The object type to which the message pertains.
The database object type to which the message pertains.
</para>
</listitem>
</varlistentry>
@ -90,16 +129,13 @@
</listitem>
</varlistentry>
</variablelist>
</sect1>
<sect1 xml:id="sect.object_types">
<title>Object Types</title>
<para>
The following object types are defined:
The following database object types are defined:
</para>
<sect2 xml:id="sect.object_types.cart">
<sect2 xml:id="sect.notifications.object_types.cart">
<title>Carts</title>
<table xml:id="table.object_types.carts" frame="all" pgwide="0">
<table xml:id="table.notifications.types.carts" frame="all" pgwide="0">
<title>Cart Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
@ -109,14 +145,14 @@
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Keyword</entry>
<entry>CART</entry>
</row>
<row>
<entry>Database Field</entry>
<entry>CART.NUMBER</entry>
</row>
<row>
<entry>Type</entry>
<entry>CART</entry>
</row>
<row>
<entry>Id Data Type</entry>
<entry>Unsigned Integer</entry>
@ -130,9 +166,9 @@
</table>
</sect2>
<sect2 xml:id="sect.object_types.log">
<sect2 xml:id="sect.notifications.types.log">
<title>Logs</title>
<table xml:id="table.object_types.logs" frame="all" pgwide="0">
<table xml:id="table.notifications.types.logs" frame="all" pgwide="0">
<title>Log Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
@ -142,14 +178,14 @@
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Keyword</entry>
<entry>LOG</entry>
</row>
<row>
<entry>Database Field</entry>
<entry>LOGS.NAME</entry>
</row>
<row>
<entry>Type</entry>
<entry>LOG</entry>
</row>
<row>
<entry>Id Data Type</entry>
<entry>String</entry>
@ -163,9 +199,9 @@
</table>
</sect2>
<sect2 xml:id="sect.object_types.pypad">
<sect2 xml:id="sect.notifications.types.pypad">
<title>Pypad Instances</title>
<table xml:id="table.object_types.pypad" frame="all" pgwide="0">
<table xml:id="table.notifications.types.pypad" frame="all" pgwide="0">
<title>Pypad Instance Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
@ -175,14 +211,14 @@
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Keyword</entry>
<entry>PYPAD</entry>
</row>
<row>
<entry>Database Field</entry>
<entry>PYPAD_INSTANCES.ID</entry>
</row>
<row>
<entry>Type</entry>
<entry>PYPAD</entry>
</row>
<row>
<entry>Id Data Type</entry>
<entry>Integer</entry>
@ -196,9 +232,9 @@
</table>
</sect2>
<sect2 xml:id="sect.object_types.dropbox">
<sect2 xml:id="sect.notifications.types.dropbox">
<title>Dropbox Instances</title>
<table xml:id="table.object_types.dropbox" frame="all" pgwide="0">
<table xml:id="table.notifications.types.dropbox" frame="all" pgwide="0">
<title>Dropbox Instance Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
@ -208,14 +244,14 @@
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Keyword</entry>
<entry>DROPBOX</entry>
</row>
<row>
<entry>Database Field</entry>
<entry>DROPBOXES.ID</entry>
</row>
<row>
<entry>Type</entry>
<entry>DROPBOX</entry>
</row>
<row>
<entry>Id Data Type</entry>
<entry>Integer</entry>
@ -229,9 +265,9 @@
</table>
</sect2>
<sect2 xml:id="sect.object_types.catch_event">
<sect2 xml:id="sect.notifications.types.catch_event">
<title>RDCatch Event Instances</title>
<table xml:id="table.object_types.catch_event" frame="all" pgwide="0">
<table xml:id="table.notifications.types.catch_event" frame="all" pgwide="0">
<title>RDCatch Event Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
@ -241,14 +277,14 @@
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Keyword</entry>
<entry>CATCH_EVENT</entry>
</row>
<row>
<entry>Database Field</entry>
<entry>RECORDINGS.ID</entry>
</row>
<row>
<entry>Type</entry>
<entry>CATCH_EVENT</entry>
</row>
<row>
<entry>Id Data Type</entry>
<entry>Integer</entry>
@ -262,9 +298,9 @@
</table>
</sect2>
<sect2 xml:id="sect.object_types.feed_item">
<sect2 xml:id="sect.notifications.types.feed_item">
<title>RSS Feed Items</title>
<table xml:id="table.object_types.feed_item" frame="all" pgwide="0">
<table xml:id="table.notifications.types.feed_item" frame="all" pgwide="0">
<title>RSS Feed Item Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
@ -274,14 +310,14 @@
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Keyword</entry>
<entry>FEED_ITEM</entry>
</row>
<row>
<entry>Database Field</entry>
<entry>PODCASTS.ID</entry>
</row>
<row>
<entry>Type</entry>
<entry>FEED_ITEM</entry>
</row>
<row>
<entry>Id Data Type</entry>
<entry>Unsigned Integer</entry>
@ -295,9 +331,9 @@
</table>
</sect2>
<sect2 xml:id="sect.object_types.feed">
<sect2 xml:id="sect.notifications.types.feed">
<title>RSS Feeds</title>
<table xml:id="table.object_types.feed" frame="all" pgwide="0">
<table xml:id="table.notifications.types.feed" frame="all" pgwide="0">
<title>RSS Feed Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
@ -307,14 +343,14 @@
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Keyword</entry>
<entry>FEED</entry>
</row>
<row>
<entry>Database Field</entry>
<entry>FEEDS.KEY_NAME</entry>
</row>
<row>
<entry>Type</entry>
<entry>FEED</entry>
</row>
<row>
<entry>Id Data Type</entry>
<entry>String</entry>
@ -327,7 +363,166 @@
</tgroup>
</table>
</sect2>
</sect1>
<sect1 xml:id="sect.rdcatch_messages">
<title>RDCatch Messages</title>
<para>
RDCatch messages use the following format:
</para>
<para>
CATCH <replaceable choice='req'>hostname</replaceable>
<replaceable choice='req'>operation</replaceable>
<replaceable choice='req'>arg1</replaceable>
<replaceable choice='opt'>[...]</replaceable>
</para>
<variablelist>
<varlistentry>
<term>
<replaceable>hostname</replaceable>
</term>
<listitem>
<para>
The name of the host originating the message. From STATIONS.NAME.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<replaceable>operation</replaceable>
</term>
<listitem>
<para>
The RDCatch operation. See the list below.
</para>
</listitem>
</varlistentry>
</variablelist>
<sect2 xml:id="sect.rdcatch_event_processed">
<title>Deck Event Processed Operation</title>
<table xml:id="table.rdcatch_event_processed" frame="all" pgwide="0">
<title>RDCatch Event Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
<colspec colname="Value" colwidth="2.0*"/>
<tbody>
<row>
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Hostname</entry>
<entry>String, from STATIONS.NAME</entry>
</row>
<row>
<entry>Operation</entry>
<entry>&quot;DE&quot;</entry>
</row>
<row>
<entry>Deck Channel</entry>
<entry>
Integer. Record decks have values in the range 1-127,
while play decks have values in the range 128-254.
</entry>
</row>
<row>
<entry>Event Number</entry>
<entry>
Integer. The value of CUT_EVENTS.NUMBER.
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2 xml:id="sect.rdcatch_deck_status">
<title>Deck Status Operation</title>
<table xml:id="table.rdcatch_deck_status" frame="all" pgwide="0">
<title>RDCatch Event Fields</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="Field" colwidth="2.0*"/>
<colspec colname="Value" colwidth="2.0*"/>
<tbody>
<row>
<entry>Field</entry>
<entry>Value</entry>
</row>
<row>
<entry>Hostname</entry>
<entry>String, from STATIONS.NAME</entry>
</row>
<row>
<entry>Operation</entry>
<entry>&quot;ME&quot;</entry>
</row>
<row>
<entry>Deck Channel</entry>
<entry>
Integer. Record decks have values in the range 1-127,
while play decks have values in the range 128-254.
</entry>
</row>
<row>
<entry>Status</entry>
<entry>
Integer. Current status of the specified deck. See
<xref linkend="sect.rdcatch_deck_status.deck_status_codes" />
</entry>
</row>
</tbody>
</tgroup>
</table>
<sect3 xml:id="sect.rdcatch_deck_status.deck_status_codes">
<title>Deck Status Codes</title>
<table frame="all">
<title>Deck Status Codes</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="Code" colwidth="1.0*" />
<colspec colname="Meaning" colwidth="5.0*" />
<colspec colname="RDDeck::Status" colwidth="5.0*" />
<thead>
<row>
<entry>Code</entry>
<entry>Meaning</entry>
<entry>RDDeck::Status Value</entry>
</row>
</thead>
<tbody>
<row>
<entry>0</entry>
<entry>Offline</entry>
<entry>RDDeck::Offline</entry>
</row>
<row>
<entry>1</entry>
<entry>Idle</entry>
<entry>RDDeck::Idle</entry>
</row>
<row>
<entry>2</entry>
<entry>Ready</entry>
<entry>RDDeck::Ready</entry>
</row>
<row>
<entry>3</entry>
<entry>Active (playing or recording)</entry>
<entry>RDDeck::Recording</entry>
</row>
<row>
<entry>4</entry>
<entry>Waiting (for a GPI)</entry>
<entry>RDDeck::Waiting</entry>
</row>
</tbody>
</tgroup>
</table>
</sect3>
</sect2>
</sect1>
</article>

View File

@ -83,6 +83,7 @@ dist_librd_la_SOURCES = dbversion.h\
rdcastsearch.cpp rdcastsearch.h\
rdcatch_conf.cpp rdcatch_conf.h\
rdcatch_connect.cpp rdcatch_connect.h\
rdcatchevent.cpp rdcatchevent.h\
rdcutlistmodel.cpp rdcutlistmodel.h\
rdcddblookup.cpp rdcddblookup.h\
rdcdplayer.cpp rdcdplayer.h\
@ -315,6 +316,7 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\
moc_rdcartfilter.cpp\
moc_rdcartslot.cpp\
moc_rdcatch_connect.cpp\
moc_rdcatchevent.cpp\
moc_rdcddblookup.cpp\
moc_rdcdplayer.cpp\
moc_rdcdripper.cpp\

View File

@ -57,6 +57,7 @@ SOURCES += rdcart_search_text.cpp
SOURCES += rdcartdrag.cpp
SOURCES += rdcartfilter.cpp
SOURCES += rdcatch_connect.cpp
SOURCES += rdcatchevent.cpp
SOURCES += rdcddblookup.cpp
SOURCES += rdcdplayer.cpp
SOURCES += rdcutlistmodel.cpp
@ -246,13 +247,10 @@ HEADERS += rdcart_search_text.h
HEADERS += rdcartdrag.h
HEADERS += rdcartfilter.h
HEADERS += rdcatch_connect.h
HEADERS += rdcatchevent.h
HEADERS += rdcddblookup.h
HEADERS += rdcdplayer.h
HEADERS += rdcutlistmodel.h
HEADERS += rddatapacer.h
HEADERS += rddiscrecord.h
HEADERS += rddisclookup.h
HEADERS += rddateedit.h
HEADERS += rdcheck_version.h
HEADERS += rdclock.h
HEADERS += rdclockmodel.h
@ -268,6 +266,8 @@ HEADERS += rdcueeditdialog.h
HEADERS += rdcut_dialog.h
HEADERS += rdcut_path.h
HEADERS += rdcut.h
HEADERS += rddatapacer.h
HEADERS += rddateedit.h
HEADERS += rddatedecode.h
HEADERS += rddatedialog.h
HEADERS += rddatepicker.h
@ -281,6 +281,8 @@ HEADERS += rddialog.h
HEADERS += rddisclookup.h
HEADERS += rddisclookup_factory.h
HEADERS += rddiscmodel.h
HEADERS += rddiscrecord.h
HEADERS += rddisclookup.h
HEADERS += rddropbox.h
HEADERS += rddropboxlistmodel.h
HEADERS += rddummylookup.h

247
lib/rdcatchevent.cpp Normal file
View File

@ -0,0 +1,247 @@
// rdcatchevent.cpp
//
// A container class for a Rivendell Catch Event message.
//
// (C) Copyright 2022 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 <QStringList>
#include "rdcatchevent.h"
#include "rdapplication.h"
RDCatchEvent::RDCatchEvent(RDDeck::Status status)
{
clear();
d_operation=RDCatchEvent::DeckEventProcessedOp;
d_deck_status=status;
}
RDCatchEvent::RDCatchEvent()
{
clear();
}
RDCatchEvent::Operation RDCatchEvent::operation() const
{
return d_operation;
}
void RDCatchEvent::setOperation(RDCatchEvent::Operation op)
{
d_operation=op;
}
int RDCatchEvent::eventNumber() const
{
return d_event_number;
}
void RDCatchEvent::setEventNumber(int num)
{
d_event_number=num;
}
QString RDCatchEvent::hostName() const
{
return d_host_name;
}
void RDCatchEvent::setHostName(const QString &str)
{
d_host_name=str;
}
unsigned RDCatchEvent::deckChannel() const
{
return d_deck_channel;
}
void RDCatchEvent::setDeckChannel(unsigned chan)
{
d_deck_channel=chan;
}
RDDeck::Status RDCatchEvent::deckStatus() const
{
return d_deck_status;
}
void RDCatchEvent::setDeckStatus(RDDeck::Status status)
{
d_deck_status=status;
}
bool RDCatchEvent::isValid() const
{
return true;
}
bool RDCatchEvent::read(const QString &str)
{
QStringList f0=str.split(" ");
bool ok=false;
clear();
//
// Common Fields
//
if((f0.size()<3)||(f0.at(0)!="CATCH")) {
return false;
}
//
// Operation-specific Fields
//
if(f0.at(2)==
RDCatchEvent::operationString(RDCatchEvent::DeckEventProcessedOp)) {
if(f0.size()!=5) {
return false;
}
unsigned chan=f0.at(3).toUInt(&ok);
if(!ok) {
return false;
}
unsigned num=f0.at(4).toUInt(&ok);
if(ok) {
d_operation=RDCatchEvent::DeckEventProcessedOp;
d_host_name=f0.at(1);
d_deck_channel=chan;
d_event_number=num;
return true;
}
}
/*
if(f0.at(2)==
RDCatchEvent::operationString(RDCatchEvent::DeckEventProcessedOp)) {
if(f0.size()!=5) {
return false;
}
unsigned chan=f0.at(3).toUInt(&ok);
if(!ok) {
return false;
}
unsigned val=f0.at(4).toUInt(&ok);
if(ok&&(val<RDDeck::LastStatus)) {
d_operation=RDCatchEvent::DeckEventProcessedOp;
d_host_name=f0.at(1);
d_deck_channel=chan;
d_deck_status=(RDDeck::Status)val;
return true;
}
}
*/
return false;
}
QString RDCatchEvent::write() const
{
QString ret;
//
// Common Fields
//
ret+="CATCH ";
ret+=d_host_name+" ";
//
// Operation-specific Fields
//
ret+=RDCatchEvent::operationString(d_operation);
switch(d_operation) {
case RDCatchEvent::DeckEventProcessedOp:
ret+=QString::asprintf(" %u",d_deck_channel);
ret+=QString::asprintf(" %u",d_event_number);
break;
case RDCatchEvent::NullOp:
case RDCatchEvent::LastOp:
break;
}
return ret;
}
QString RDCatchEvent::dump() const
{
QString ret;
//
// Common Fields
//
ret+="hostName: "+d_host_name+"\n";
//
// Operation-specific Fields
//
ret+="operation: "+RDCatchEvent::operationString(d_operation)+"\n";
switch(d_operation) {
case RDCatchEvent::DeckEventProcessedOp:
ret+=QString::asprintf("deck channel: %u\n",d_deck_channel);
ret+=QString::asprintf("event number: %u\n",d_event_number);
break;
case RDCatchEvent::NullOp:
case RDCatchEvent::LastOp:
break;
}
return ret;
}
void RDCatchEvent::clear()
{
d_operation=RDCatchEvent::NullOp;
d_host_name=rda->station()->name();
d_deck_channel=0;
d_event_number=0;
d_deck_status=RDDeck::Offline;
}
QString RDCatchEvent::operationString(Operation op)
{
QString ret="UNKNOWN";
switch(op) {
case RDCatchEvent::DeckEventProcessedOp:
ret="DE";
break;
case RDCatchEvent::NullOp:
case RDCatchEvent::LastOp:
break;
}
return ret;
}

61
lib/rdcatchevent.h Normal file
View File

@ -0,0 +1,61 @@
// rdcatchevent.h
//
// A container class for a Rivendell Catch Event message.
//
// (C) Copyright 2022 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.
//
#ifndef RDCATCHEVENT_H
#define RDCATCHEVENT_H
#include <QString>
#include <QVariant>
#include <rddeck.h>
class RDCatchEvent
{
public:
enum Operation {NullOp=0,DeckEventProcessedOp=1,LastOp=2};
RDCatchEvent(RDDeck::Status status);
RDCatchEvent();
Operation operation() const;
void setOperation(Operation op);
QString hostName() const;
void setHostName(const QString &str);
unsigned deckChannel() const;
void setDeckChannel(unsigned chan);
int eventNumber() const;
void setEventNumber(int num);
RDDeck::Status deckStatus() const;
void setDeckStatus(RDDeck::Status status);
bool isValid() const;
bool read(const QString &str);
QString write() const;
QString dump() const;
void clear();
static QString operationString(Operation op);
private:
Operation d_operation;
QString d_host_name;
unsigned d_deck_channel;
int d_event_number;
RDDeck::Status d_deck_status;
};
#endif // RDCATCHEVENT_H

View File

@ -30,7 +30,7 @@
class RDDeck
{
public:
enum Status {Offline=0,Idle=1,Ready=2,Recording=3,Waiting=4};
enum Status {Offline=0,Idle=1,Ready=2,Recording=3,Waiting=4,LastStatus=5};
RDDeck(QString station,unsigned channel,bool create=false);
bool isActive() const;
QString station() const;

View File

@ -2,7 +2,7 @@
//
// Connection to the Rivendell Interprocess Communication Daemon
//
// (C) Copyright 2002-2021 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2022 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
@ -147,6 +147,13 @@ void RDRipc::sendNotification(const RDNotification &notify)
}
void RDRipc::sendCatchEvent(RDCatchEvent *evt)
{
SendCommand("ON "+evt->write()+"!");
rda->syslog(LOG_NOTICE,"sent catch event: %s\n",evt->write().toUtf8().constData());
}
void RDRipc::sendOnairFlag()
{
SendCommand(QString::asprintf("TA %d!",ripc_onair_flag));
@ -406,12 +413,24 @@ void RDRipc::DispatchCommand()
msg+=QString(cmds[i])+" ";
}
msg=msg.left(msg.length()-1);
RDNotification *notify=new RDNotification();
if(!notify->read(msg)) {
QStringList f0=msg.split(" ",QString::SkipEmptyParts);
if(f0.at(0)=="NOTIFY") {
RDNotification *notify=new RDNotification();
if(!notify->read(msg)) {
delete notify;
return;
}
emit notificationReceived(notify);
delete notify;
return;
}
emit notificationReceived(notify);
delete notify;
if(f0.at(0)=="CATCH") {
RDCatchEvent *evt=new RDCatchEvent();
if(!evt->read(msg)) {
delete evt;
return;
}
emit catchEventReceived(evt);
delete evt;
}
}
}

View File

@ -2,7 +2,7 @@
//
// Connection to the Rivendell Interprocess Communication Daemon
//
// (C) Copyright 2002-2004,2016-2018 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2022 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
@ -18,21 +18,19 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <qsqldatabase.h>
#include <qstring.h>
#include <qobject.h>
#include <qlabel.h>
#include <qtimer.h>
#include <qtcpsocket.h>
#ifndef RDRIPC_H
#define RDRIPC_H
#include <QLabel>
#include <QTimer>
#include <QTcpSocket>
#include <rdcatchevent.h>
#include <rdconfig.h>
#include <rdmacro.h>
#include <rdnotification.h>
#include <rdstation.h>
#ifndef RDRIPC_H
#define RDRIPC_H
#define RIPC_MAX_ARGS 100
#define RIPC_MAX_LENGTH 256
#define RIPC_START_DELAY 2000
@ -58,6 +56,7 @@ class RDRipc : public QObject
void sendNotification(RDNotification::Type type,
RDNotification::Action action,const QVariant &id);
void sendNotification(const RDNotification &notify);
void sendCatchEvent(RDCatchEvent *evt);
void sendOnairFlag();
void sendRml(RDMacro *macro);
void reloadHeartbeat();
@ -72,6 +71,7 @@ class RDRipc : public QObject
void gpiCartChanged(int matrix,int line,int off_cartnum,int on_cartnum);
void gpoCartChanged(int matrix,int line,int off_cartnum,int on_cartnum);
void notificationReceived(RDNotification *notify);
void catchEventReceived(RDCatchEvent *evt);
void onairFlagChanged(bool state);
void rmlReceived(RDMacro *rml);

View File

@ -52,6 +52,7 @@ dist_rdcatch_SOURCES = add_recording.cpp add_recording.h\
edit_recording.cpp edit_recording.h\
edit_switchevent.cpp edit_switchevent.h\
edit_upload.cpp edit_upload.h\
eventlight.cpp eventlight.h\
eventwidget.cpp eventwidget.h\
globals.h\
list_reports.cpp list_reports.h\
@ -69,6 +70,7 @@ nodist_rdcatch_SOURCES = moc_add_recording.cpp\
moc_edit_recording.cpp\
moc_edit_switchevent.cpp\
moc_edit_upload.cpp\
moc_eventlight.cpp\
moc_eventwidget.cpp\
moc_list_reports.cpp\
moc_rdcatch.cpp\

View File

@ -2,7 +2,7 @@
//
// Monitor a Rivendell RDCatch Deck
//
// (C) Copyright 2002-2021 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2022 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
@ -40,10 +40,8 @@ DeckMon::DeckMon(QString station,unsigned channel,QWidget *parent)
mon_red_palette=palette();
mon_red_palette.setColor(QPalette::Background,Qt::darkRed);
mon_red_palette.setColor(QPalette::Foreground,Qt::white);
mon_red_stylesheet="color: white;background-color: darkRed;";
mon_dark_palette=palette();
mon_dark_palette.
setColor(QPalette::Background,palette().color(QPalette::Active,QPalette::Mid));
mon_dark_palette.setColor(QPalette::Foreground,Qt::white);
//
// Station/Channel
@ -91,15 +89,8 @@ DeckMon::DeckMon(QString station,unsigned channel,QWidget *parent)
//
// Event Indicator
//
mon_event_label=new QLabel(this);
mon_event_label->setFont(labelFont());
mon_event_label->setAlignment(Qt::AlignCenter);
mon_event_label->setFrameStyle(QFrame::Panel|QFrame::Sunken);
mon_event_label->setPalette(mon_dark_palette);
mon_event_label->setText("--");
mon_event_timer=new QTimer(this);
mon_event_timer->setSingleShot(true);
connect(mon_event_timer,SIGNAL(timeout()),this,SLOT(eventResetData()));
mon_event_light=new EventLight(this);
mon_event_light->setFont(labelFont());
//
// Status
@ -167,6 +158,7 @@ void DeckMon::setStatus(RDDeck::Status status,int id,const QString &cutname)
mon_left_meter->setPeakBar(-10000);
mon_right_meter->setPeakBar(-10000);
mon_abort_button->setDisabled(true);
mon_event_light->setDisabled(true);
return;
}
switch(status) {
@ -176,6 +168,7 @@ void DeckMon::setStatus(RDDeck::Status status,int id,const QString &cutname)
mon_left_meter->setPeakBar(-10000);
mon_right_meter->setPeakBar(-10000);
mon_abort_button->setDisabled(true);
mon_event_light->setDisabled(true);
break;
case RDDeck::Idle:
@ -184,6 +177,7 @@ void DeckMon::setStatus(RDDeck::Status status,int id,const QString &cutname)
mon_left_meter->setPeakBar(-10000);
mon_right_meter->setPeakBar(-10000);
mon_abort_button->setDisabled(true);
mon_event_light->setDisabled(true);
break;
case RDDeck::Ready:
@ -192,6 +186,7 @@ void DeckMon::setStatus(RDDeck::Status status,int id,const QString &cutname)
mon_left_meter->setPeakBar(-10000);
mon_right_meter->setPeakBar(-10000);
mon_abort_button->setEnabled(true);
mon_event_light->setDisabled(true);
break;
case RDDeck::Waiting:
@ -200,6 +195,7 @@ void DeckMon::setStatus(RDDeck::Status status,int id,const QString &cutname)
mon_left_meter->setPeakBar(-10000);
mon_right_meter->setPeakBar(-10000);
mon_abort_button->setEnabled(true);
mon_event_light->setDisabled(true);
break;
case RDDeck::Recording:
@ -211,17 +207,12 @@ void DeckMon::setStatus(RDDeck::Status status,int id,const QString &cutname)
}
SetCutInfo(id,cutname);
mon_abort_button->setEnabled(true);
mon_event_light->setEnabled(true);
break;
case RDDeck::LastStatus:
break;
}
mon_event_label->setText("--");
}
void DeckMon::setEvent(int number)
{
mon_event_label->setText(QString::asprintf("%d",number));
mon_event_label->setPalette(mon_red_palette);
mon_event_timer->start(1000);
}
@ -237,6 +228,16 @@ void DeckMon::setRightMeter(int level)
}
void DeckMon::processCatchEvent(RDCatchEvent *evt)
{
if((evt->hostName()==mon_station)&&(evt->deckChannel()==mon_channel)) {
if(evt->operation()==RDCatchEvent::DeckEventProcessedOp) {
mon_event_light->trigger(evt->eventNumber());
}
}
}
void DeckMon::monitorButtonData()
{
emit monitorClicked();
@ -249,19 +250,13 @@ void DeckMon::abortButtonData()
}
void DeckMon::eventResetData()
{
mon_event_label->setPalette(mon_dark_palette);
}
void DeckMon::resizeEvent(QResizeEvent *e)
{
mon_station_label->setGeometry(10,6,140,18);
mon_monitor_button->setGeometry(155,5,40,20);
mon_abort_button->setGeometry(200,5,40,20);
mon_cut_label->setGeometry(245,6,e->size().width()-595,18);
mon_event_label->setGeometry(e->size().width()-345,6,20,18);
mon_event_light->setGeometry(e->size().width()-345,6,20,18);
mon_status_label->setGeometry(e->size().width()-320,6,80,18);
mon_left_meter->setGeometry(e->size().width()-235,6,225,10);
mon_right_meter->setGeometry(e->size().width()-235,16,225,10);

View File

@ -29,6 +29,8 @@
#include <rdplaymeter.h>
#include <rdrecording.h>
#include "eventlight.h"
class DeckMon : public RDFrame
{
Q_OBJECT
@ -42,10 +44,10 @@ class DeckMon : public RDFrame
public slots:
void setMonitor(bool state);
void setStatus(RDDeck::Status status,int id,const QString &cutname);
void setEvent(int number);
void setLeftMeter(int level);
void setRightMeter(int level);
void processCatchEvent(RDCatchEvent *evt);
signals:
void monitorClicked();
void abortClicked();
@ -56,13 +58,12 @@ class DeckMon : public RDFrame
private slots:
void monitorButtonData();
void abortButtonData();
void eventResetData();
private:
void SetCutInfo(int id,const QString &cutname);
QLabel *mon_station_label;
QLabel *mon_cut_label;
QLabel *mon_event_label;
EventLight *mon_event_light;
QLabel *mon_status_label;
QPushButton *mon_abort_button;
QPushButton *mon_monitor_button;
@ -73,7 +74,9 @@ class DeckMon : public RDFrame
unsigned mon_channel;
QTimer *mon_event_timer;
QPalette mon_red_palette;
QString mon_red_stylesheet;
QPalette mon_dark_palette;
QString mon_dark_stylesheet;
};

74
rdcatch/eventlight.cpp Normal file
View File

@ -0,0 +1,74 @@
// eventlight.h
//
// Indicator light for cut events in rdcatch(1);
//
// (C) Copyright 2022 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 "eventlight.h"
EventLight::EventLight(QWidget *parent)
: QLabel("--",parent)
{
setAlignment(Qt::AlignCenter);
setFrameStyle(QFrame::Panel|QFrame::Sunken);
d_timer=new QTimer(this);
d_timer->setSingleShot(true);
connect(d_timer,SIGNAL(timeout()),this,SLOT(reset()));
}
QSize EventLight::sizeHint() const
{
return QSize(20,18);
}
QSizePolicy EventLight::sizePolicy() const
{
return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
}
void EventLight::trigger(int num)
{
setText(QString::asprintf("%d",num));
setStyleSheet("color: "+palette().color(QPalette::HighlightedText).name()+";"+
"background-color: "+
palette().color(QPalette::Highlight).name()+";");
d_timer->start(3000);
}
void EventLight::setEnabled(bool state)
{
QLabel::setEnabled(state);
}
void EventLight::setDisabled(bool state)
{
setText("--");
setStyleSheet("");
QLabel::setDisabled(state);
}
void EventLight::reset()
{
setStyleSheet("");
}

48
rdcatch/eventlight.h Normal file
View File

@ -0,0 +1,48 @@
// eventlight.h
//
// Indicator light for cut events in rdcatch(1);
//
// (C) Copyright 2022 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.
//
#ifndef EVENTLIGHT_H
#define EVENTLIGHT_H
#include <QLabel>
#include <QTimer>
class EventLight : public QLabel
{
Q_OBJECT
public:
EventLight(QWidget *parent=0);
QSize sizeHint() const;
QSizePolicy sizePolicy() const;
public slots:
void trigger(int num);
void setEnabled(bool state);
void setDisabled(bool state);
private slots:
void reset();
private:
QTimer *d_timer;
};
#endif // EVENTLIGHT_H

View File

@ -208,9 +208,11 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent)
connect(catch_connect.back()->connector(),
SIGNAL(eventPurged(int)),
this,SLOT(eventPurgedData(int)));
/*
connect(catch_connect.back()->connector(),
SIGNAL(deckEventSent(int,int,int)),
this,SLOT(deckEventSentData(int,int,int)));
*/
connect(catch_connect.back()->connector(),
SIGNAL(heartbeatFailed(int)),
this,SLOT(heartbeatFailedData(int)));
@ -230,11 +232,12 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent)
catch_connect.back()->chan.push_back(q1->value(0).toUInt());
catch_connect.back()->mon_id.push_back(catch_monitor.size());
DeckMon *mon=new DeckMon(q->value(0).toString(),q1->value(0).toUInt(),
catch_monitor_vbox);
connect(rda->ripc(),SIGNAL(catchEventReceived(RDCatchEvent *)),
mon,SLOT(processCatchEvent(RDCatchEvent *)));
catch_monitor.push_back(new CatchMonitor());
catch_monitor.back()->setDeckMon(new DeckMon(q->value(0).toString(),
q1->value(0).toUInt(),
catch_monitor_vbox));
catch_monitor.back()->setDeckMon(mon);
catch_monitor.back()->setSerialNumber(catch_connect.size()-1);
catch_monitor.back()->setChannelNumber(q1->value(0).toUInt());
catch_monitor_vbox->addWidget(catch_monitor.back()->deckMon());
@ -771,7 +774,7 @@ void MainWidget::monitorChangedData(int serial,unsigned chan,bool state)
}
}
/*
void MainWidget::deckEventSentData(int serial,int chan,int number)
{
int mon=GetMonitor(serial,chan);
@ -779,6 +782,13 @@ void MainWidget::deckEventSentData(int serial,int chan,int number)
catch_monitor[mon]->deckMon()->setEvent(number);
}
}
*/
void MainWidget::catchEventReceivedData(RDCatchEvent *evt)
{
printf("catchEventReceivedData()\n");
printf("%s\n",evt->dump().toUtf8().constData());
}
void MainWidget::scrollButtonData()

View File

@ -82,7 +82,8 @@ class MainWidget : public RDMainWindow
void statusChangedData(int,unsigned,RDDeck::Status,int,
const QString &cutname);
void monitorChangedData(int serial,unsigned chan,bool state);
void deckEventSentData(int serial,int chan,int number);
// void deckEventSentData(int serial,int chan,int number);
void catchEventReceivedData(RDCatchEvent *evt);
void scrollButtonData();
void reportsButtonData();
void headButtonData();

View File

@ -2,7 +2,7 @@
#
# The QMake project file for RDCatch.
#
# (C) Copyright 2003-2021 Fred Gleason <fredg@paravelsystems.com>
# (C) Copyright 2003-2022 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
@ -30,6 +30,7 @@ x11 {
SOURCES += edit_switchevent.cpp
SOURCES += edit_download.cpp
SOURCES += edit_upload.cpp
SOURCES += eventlight.cpp
SOURCES += eventwidget.cpp
SOURCES += rdcatch.cpp
SOURCES += list_reports.cpp
@ -47,6 +48,7 @@ x11 {
HEADERS += edit_switchevent.h
HEADERS += edit_download.h
HEADERS += edit_upload.h
HEADERS += eventlight.h
HEADERS += eventwidget.h
HEADERS += rdcatch.h
HEADERS += list_reports.h

View File

@ -1595,6 +1595,13 @@ void MainObject::SendMeterLevel(int deck,short levels[2])
void MainObject::SendDeckEvent(int deck,int number)
{
BroadcastCommand(QString::asprintf("DE %d %d!",deck,number));
RDCatchEvent *evt=new RDCatchEvent();
evt->setOperation(RDCatchEvent::DeckEventProcessedOp);
evt->setDeckChannel(deck);
evt->setEventNumber(number);
rda->ripc()->sendCatchEvent(evt);
delete evt;
}

View File

@ -2,7 +2,7 @@
//
// Process local notifications for ripcd(8)
//
// (C) Copyright 2002-2019 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2022 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
@ -36,3 +36,7 @@ void MainObject::RunLocalNotifications(RDNotification *notify)
}
}
void MainObject::RunLocalNotifications(RDCatchEvent *evt)
{
}

View File

@ -238,18 +238,31 @@ void MainObject::newConnectionData()
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());
QStringList f0=msg.split(msg,QString::SkipEmptyParts);
if(msg.at(0)=="NOTIFY") {
RDNotification *notify=new RDNotification();
if(!notify->read(msg)) {
rda->syslog(LOG_INFO,"invalid notification received from %s",
addr.toString().toUtf8().constData());
delete notify;
return;
}
RunLocalNotifications(notify);
BroadcastCommand("ON "+msg+"!");
delete notify;
return;
}
RunLocalNotifications(notify);
BroadcastCommand("ON "+msg+"!");
delete notify;
if(msg.at(0)=="CATCH") {
RDCatchEvent *evt=new RDCatchEvent();
if(!evt->read(msg)) {
rda->syslog(LOG_INFO,"invalid catch event received from %s",
addr.toString().toUtf8().constData());
delete evt;
return;
}
RunLocalNotifications(evt);
BroadcastCommand("ON "+msg+"!");
delete evt;
}
}
@ -611,22 +624,43 @@ bool MainObject::DispatchCommand(RipcdConnection *conn)
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");
QStringList f0=msg.split(" ",QString::SkipEmptyParts);
if(f0.at(0)=="NOTIFY") {
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",
msg.toUtf8().constData(),
rda->system()->notificationAddress().
toString().toUtf8().constData(),
RD_NOTIFICATION_PORT);
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(f0.at(0)=="CATCH") {
RDCatchEvent *evt=new RDCatchEvent();
if(!evt->read(msg)) {
rda->syslog(LOG_INFO,"invalid catch event processed");
delete evt;
return true;
}
RunLocalNotifications(evt);
BroadcastCommand("ON "+msg+"!",conn->id());
ripcd_notification_mcaster->
send(msg,rda->system()->notificationAddress(),RD_NOTIFICATION_PORT);
rda->syslog(LOG_DEBUG,"sent catch event: \"%s\" to %s:%d",
msg.toUtf8().constData(),
rda->system()->notificationAddress().
toString().toUtf8().constData(),
RD_NOTIFICATION_PORT);
delete evt;
}
}
if(cmds[0]=="TA") { // Send Onair Flag State

View File

@ -2,7 +2,7 @@
//
// Rivendell Interprocess Communication Daemon
//
// (C) Copyright 2002-2020 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2022 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
@ -25,26 +25,25 @@
#include <vector>
#include <qobject.h>
#include <qstring.h>
#include <qsignalmapper.h>
#include <qtcpserver.h>
#include <qtimer.h>
#include <qudpsocket.h>
#include <QSignalMapper>
#include <QTcpServer>
#include <QTimer>
#include <QUdpSocket>
#ifdef JACK
#include <jack/jack.h>
#endif // JACK
#include <rdcatchevent.h>
#include <rdcodetrap.h>
#include <rdmacro.h>
#include <rdmatrix.h>
#include <rdmulticaster.h>
#include <rdnotification.h>
#include <rdsocket.h>
#include <rdttydevice.h>
#include <rdcodetrap.h>
#include <rdstation.h>
#include <rdmatrix.h>
#include <rdmacro.h>
#include <rdmulticaster.h>
#include <rdtty.h>
#include <rdttydevice.h>
#include <ripcd_connection.h>
#include <globals.h>
@ -95,6 +94,7 @@ class MainObject : public QObject
QString StripPoint(QString);
void LoadLocalMacros();
void RunLocalNotifications(RDNotification *notify);
void RunLocalNotifications(RDCatchEvent *evt);
void RunLocalMacros(RDMacro *rml);
void LoadGpiTable();
void SendGpi(int ch,int matrix);