State event for TX status

Information about TX status, e.g. if transmitting or not, is now
published as a state event and is also sent to the reflector server.
This commit is contained in:
Tobias Blomberg 2019-10-19 13:39:52 +02:00
parent a52e87f124
commit 378fd3d8d5
13 changed files with 261 additions and 9 deletions

View File

@ -704,6 +704,25 @@ void Reflector::httpRequestReceived(Async::HttpServerConnection *con,
}
}
}
if (qth.isMember("tx") && qth["tx"].isObject())
{
//std::cout << "### Found tx" << std::endl;
Json::Value::Members txs(qth["tx"].getMemberNames());
for (Json::Value::Members::const_iterator it=txs.begin(); it!=txs.end(); ++it)
{
//std::cout << "### member=" << *it << std::endl;
const std::string& tx_id_str(*it);
if (tx_id_str.size() == 1)
{
char tx_id(tx_id_str[0]);
Json::Value& tx(qth["tx"][tx_id_str]);
if (client->txExist(tx_id))
{
tx["transmit"] = client->txTransmit(tx_id);
}
}
}
}
}
}
status["nodes"][client->callsign()] = node;

View File

@ -318,6 +318,9 @@ void ReflectorClient::onFrameReceived(FramedTcpConnection *con,
case MsgSignalStrengthValues::TYPE:
handleMsgSignalStrengthValues(ss);
break;
case MsgTxStatus::TYPE:
handleMsgTxStatus(ss);
break;
#if 0
case MsgNodeInfo::TYPE:
handleNodeInfo(ss);
@ -555,6 +558,28 @@ void ReflectorClient::handleMsgSignalStrengthValues(std::istream& is)
} /* ReflectorClient::handleMsgSignalStrengthValues */
void ReflectorClient::handleMsgTxStatus(std::istream& is)
{
MsgTxStatus msg;
if (!msg.unpack(is))
{
cerr << "*** WARNING[" << callsign()
<< "]: Could not unpack incoming MsgTxStatus message" << endl;
return;
}
typedef MsgTxStatus::Txs::const_iterator TxsIter;
for (TxsIter it = msg.txs().begin(); it != msg.txs().end(); ++it)
{
const MsgTxStatus::Tx& tx = *it;
std::cout << "### MsgTxStatus:"
<< " id=" << tx.id()
<< " transmit=" << tx.transmit()
<< std::endl;
setTxTransmit(tx.id(), tx.transmit());
}
} /* ReflectorClient::handleMsgTxStatus */
void ReflectorClient::handleRequestQsy(std::istream& is)
{
MsgRequestQsy msg;

View File

@ -127,6 +127,13 @@ class ReflectorClient
};
typedef std::map<char, Rx> RxMap;
struct Tx
{
std::string name;
bool transmit;
};
typedef std::map<char, Tx> TxMap;
class Filter
{
public:
@ -394,6 +401,14 @@ class ReflectorClient
bool rxActive(char id) { return m_rx_map[id].active; }
//RxMap& rxMap(void) { return m_rx_map; }
//const RxMap& rxMap(void) const { return m_rx_map; }
bool txExist(char tx_id) const
{
return m_tx_map.find(tx_id) != m_tx_map.end();
}
void setTxTransmit(char id, bool transmit) { m_tx_map[id].transmit = transmit; }
bool txTransmit(char id) { return m_tx_map[id].transmit; }
const Json::Value& nodeInfo(void) const { return m_node_info; }
private:
@ -429,6 +444,7 @@ class ReflectorClient
uint32_t m_current_tg;
std::set<uint32_t> m_monitored_tgs;
RxMap m_rx_map;
TxMap m_tx_map;
Json::Value m_node_info;
ReflectorClient(const ReflectorClient&);
@ -441,6 +457,7 @@ class ReflectorClient
void handleTgMonitor(std::istream& is);
void handleNodeInfo(std::istream& is);
void handleMsgSignalStrengthValues(std::istream& is);
void handleMsgTxStatus(std::istream& is);
void handleRequestQsy(std::istream& is);
void handleStateEvent(std::istream& is);
void handleMsgError(std::istream& is);

View File

@ -1021,6 +1021,57 @@ struct MsgSignalStrengthValues
}; /* MsgSignalStrengthValues */
/**
@brief Tx status message
@author Tobias Blomberg / SM0SVX
@date 2019-10-19
This message is used by a client to send tx status to the reflector server.
*/
class MsgTxStatus : public ReflectorMsgBase<113>
{
public:
class Tx : public Async::Msg
{
public:
Tx(void) : m_id('?'), m_flags(0) {}
Tx(char id) : m_id(id), m_flags(0) {}
void setTransmit(bool transmit) { setBit(BIT_TRANSMIT, transmit); }
bool transmit(void) const { return getBit(BIT_TRANSMIT); }
char id(void) const { return m_id; }
ASYNC_MSG_MEMBERS(m_id, m_flags)
private:
typedef enum {BIT_TRANSMIT=0} Bit;
char m_id;
uint8_t m_flags;
bool getBit(Bit bitno) const
{
uint8_t bit = 1 << static_cast<size_t>(bitno);
return (m_flags & bit) != 0;
}
void setBit(Bit bitno, bool is_enabled)
{
uint8_t bit = 1 << static_cast<size_t>(bitno);
m_flags = (m_flags & ~bit) | (is_enabled ? bit : 0);
}
};
typedef std::vector<Tx> Txs;
MsgTxStatus(void) {}
MsgTxStatus(const Txs& txs) : m_txs(txs) {}
Txs& txs(void) { return m_txs; }
void pushBack(const Tx& tx) { m_txs.push_back(tx); }
ASYNC_MSG_MEMBERS(m_txs)
private:
Txs m_txs;
}; /* class MsgTxStatus */
/***************************** UDP Messages *****************************/

View File

@ -557,6 +557,7 @@ bool Logic::initialize(void)
}
tx().transmitterStateChange.connect(
mem_fun(*this, &Logic::transmitterStateChange));
tx().publishStateEvent.connect(mem_fun(*this, &Logic::onPublishStateEvent));
prev_tx_src->registerSink(m_tx);
prev_tx_src = 0;

View File

@ -512,6 +512,54 @@ void ReflectorLogic::remoteReceivedPublishStateEvent(
//sendUdpMsg(msg);
sendMsg(msg);
}
else if (event_name == "Tx:state")
{
MsgTxStatus msg;
std::istringstream is(data);
Json::Value tx_data;
is >> tx_data;
std::string name = tx_data.get("name", "").asString();
std::string id_str = tx_data.get("id", "?").asString();
if (id_str.size() != 1)
{
return;
}
char id = id_str[0];
if (id != '\0')
{
bool transmit = tx_data.get("transmit", false).asBool();
MsgTxStatus::Tx tx(id);
tx.setTransmit(transmit);
msg.pushBack(tx);
sendMsg(msg);
}
}
else if (event_name == "MultiTx:state")
{
MsgTxStatus msg;
std::istringstream is(data);
Json::Value tx_arr;
is >> tx_arr;
for (Json::Value::ArrayIndex i = 0; i != tx_arr.size(); i++)
{
Json::Value& tx_data = tx_arr[i];
std::string name = tx_data.get("name", "").asString();
std::string id_str = tx_data.get("id", "").asString();
if (id_str.size() != 1)
{
return;
}
char id = id_str[0];
if (id != '\0')
{
bool transmit = tx_data.get("transmit", false).asBool();
MsgTxStatus::Tx tx(id);
tx.setTransmit(transmit);
msg.pushBack(tx);
}
}
sendMsg(msg);
}
} /* ReflectorLogic::remoteReceivedPublishStateEvent */

View File

@ -247,6 +247,7 @@ DTMF_SERIAL=/dev/ttyS0
[Tx1]
TYPE=Local
TX_ID=T
AUDIO_DEV=alsa:plughw:0
AUDIO_CHANNEL=0
#AUDIO_DEV_KEEP_OPEN=0

View File

@ -286,7 +286,13 @@ LocalTx::~LocalTx(void)
bool LocalTx::initialize(void)
{
string value;
char tx_id = '\0';
if (cfg.getValue(name(), "TX_ID", tx_id))
{
setId(tx_id);
}
string audio_dev;
if (!cfg.getValue(name(), "AUDIO_DEV", audio_dev))
{

View File

@ -43,6 +43,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
****************************************************************************/
#include <AsyncAudioSplitter.h>
#include <json/json.h>
@ -115,9 +116,11 @@ using namespace Async;
****************************************************************************/
MultiTx::MultiTx(Config& cfg, const string& name)
: Tx(name), cfg(cfg), splitter(0)
: Tx(name), cfg(cfg), splitter(0),
m_tx_state_delay_timer(100, Async::Timer::TYPE_ONESHOT, false)
{
m_tx_state_delay_timer.expired.connect(
sigc::hide(sigc::mem_fun(*this, &MultiTx::txStateDelayExpired)));
} /* MultiTx::MultiTx */
@ -139,6 +142,12 @@ MultiTx::~MultiTx(void)
bool MultiTx::initialize(void)
{
char tx_id = '\0';
if (cfg.getValue(name(), "TX_ID", tx_id))
{
setId(tx_id);
}
string transmitters;
if (!cfg.getValue(name(), "TRANSMITTERS", transmitters))
{
@ -270,19 +279,50 @@ void MultiTx::sendData(const std::vector<uint8_t> &msg)
void MultiTx::onTransmitterStateChange(void)
{
bool is_transmitting = false;
list<Tx *>::const_iterator it;
for (it=txs.begin(); it!=txs.end(); ++it)
{
if ((*it)->isTransmitting())
{
setIsTransmitting(true);
return;
is_transmitting = true;
break;
}
}
setIsTransmitting(false);
setIsTransmitting(is_transmitting);
m_tx_state_delay_timer.setEnable(true);
m_tx_state_delay_timer.reset();
} /* MultiTx::onTransmitterStateChange */
void MultiTx::txStateDelayExpired(void)
{
Json::Value event(Json::arrayValue);
list<Tx *>::const_iterator it;
for (it=txs.begin(); it!=txs.end(); ++it)
{
Tx *tx = *it;
char tx_id = tx->id();
if (tx_id != '\0')
{
Json::Value tx_state(Json::objectValue);
tx_state["name"] = tx->name();
tx_state["id"] = std::string(&tx_id, &tx_id+1);
tx_state["transmit"] = tx->isTransmitting();
event.append(tx_state);
}
}
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = ""; //The JSON document is written on a single line
Json::StreamWriter* writer = builder.newStreamWriter();
std::stringstream os;
writer->write(event, &os);
delete writer;
//std::cout << "### " << os.str() << std::endl;
publishStateEvent("MultiTx:state", os.str());
} /* MultiTx::txStateDelayExpired */
/*
* This file has not been truncated

View File

@ -44,6 +44,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
****************************************************************************/
#include <AsyncTimer.h>
/****************************************************************************
@ -190,10 +191,12 @@ class MultiTx : public Tx
Async::Config &cfg;
std::list<Tx *> txs;
Async::AudioSplitter *splitter;
Async::Timer m_tx_state_delay_timer;
MultiTx(const MultiTx&);
MultiTx& operator=(const MultiTx&);
void onTransmitterStateChange(void);
void txStateDelayExpired(void);
}; /* class MultiTx */

View File

@ -33,6 +33,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
****************************************************************************/
#include <iostream>
#include <sstream>
#include <json/json.h>
@ -57,7 +59,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "DummyRxTx.h"
/****************************************************************************
*
* Namespaces to use
@ -242,6 +243,23 @@ void Tx::setIsTransmitting(bool is_transmitting)
}
m_is_transmitting = is_transmitting;
transmitterStateChange(is_transmitting);
char tx_id = id();
if (tx_id != '\0')
{
Json::Value tx(Json::objectValue);
tx["name"] = name();
tx["id"] = std::string(&tx_id, &tx_id+1);
tx["transmit"] = is_transmitting;
Json::StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = ""; //The JSON document is written on a single line
Json::StreamWriter* writer = builder.newStreamWriter();
std::stringstream os;
writer->write(tx, &os);
delete writer;
publishStateEvent("Tx:state", os.str());
}
}
} /* Tx::setIsTransmitting */

View File

@ -136,7 +136,8 @@ class Tx : public sigc::trackable, public Async::AudioSink
* @param tx_name The name of the transmitter
*/
Tx(std::string tx_name)
: m_name(tx_name), m_verbose(true), m_is_transmitting(false)
: m_name(tx_name), m_tx_id('\0'), m_verbose(true),
m_is_transmitting(false)
{
}
@ -157,6 +158,12 @@ class Tx : public sigc::trackable, public Async::AudioSink
*/
const std::string& name(void) const { return m_name; }
/**
* @brief Return the ID of the transmitter
* @return The transmitter ID is returned
*/
char id(void) const { return m_tx_id; }
/*
* @brief Set the verbosity level of the transmitter
* @param verbose Set to \em false to keep the rx from printing things
@ -246,11 +253,27 @@ class Tx : public sigc::trackable, public Async::AudioSink
*/
sigc::signal<void, bool> transmitterStateChange;
/**
* @brief A signal that is emitted to publish a state update event
* @param event_name The name of the event
* @param msg The state update message
*
* This signal is emitted when a receiver wish to publish a state update
* message. A state update message is a free text message that can be used
* by subscribers to act on certain state changes within SvxLink. The
* event name must be unique within SvxLink. The recommended format is
* <context>:<name>, e.g. Tx:tx_state.
*/
sigc::signal<void, const std::string&,
const std::string&> publishStateEvent;
protected:
void setId(char id) { m_tx_id = id; }
void setIsTransmitting(bool is_transmitting);
private:
std::string m_name;
char m_tx_id;
bool m_verbose;
bool m_is_transmitting;

View File

@ -11,7 +11,7 @@ LIBECHOLIB=1.3.3
LIBASYNC=1.6.0.99.3-reflector_tg
# SvxLink versions
SVXLINK=1.7.99.9-reflector_tg
SVXLINK=1.7.99.10-reflector_tg
MODULE_HELP=1.0.0
MODULE_PARROT=1.1.1
MODULE_ECHO_LINK=1.5.0