From f69443c2eb133e7d1f9d1faab24d626666b741c9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 26 Mar 2023 16:57:35 -0400 Subject: [PATCH] relabel RCON configuration parameters to REST (see configs/config.example.yml); change "controlPermitTG" to "supervisor", this is more descriptive for what this function does as it is literally a supervisory flag enabling/disabling the CC from commanding subordinate DVMs; implement a REST API that can alter the supervisory state at runtime; --- configs/config.example.yml | 43 ++++++------- src/dmr/Control.cpp | 34 ++++++++-- src/dmr/Control.h | 6 +- src/dmr/Slot.cpp | 1 + src/dmr/Slot.h | 6 +- src/dmr/packet/ControlSignaling.cpp | 8 +-- src/host/Host.cpp | 35 ++++++----- src/host/Host.h | 2 +- src/network/RESTAPI.cpp | 96 +++++++++++++++++++++++++++++ src/network/RESTAPI.h | 2 + src/network/RESTDefines.h | 1 + src/nxdn/Control.cpp | 10 +-- src/nxdn/Control.h | 6 +- src/nxdn/packet/Trunk.cpp | 4 +- src/p25/Control.cpp | 10 +-- src/p25/Control.h | 6 +- src/p25/packet/Trunk.cpp | 8 +-- 17 files changed, 203 insertions(+), 75 deletions(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index 0aab755a..284b8023 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -57,16 +57,16 @@ network: allowDiagnosticTransfer: true # Flag indicating whether or not verbose debug logging is enabled. debug: false - # Flag indicating whether or not RCON (remote control) is enabled. - rconEnable: false - # IP address of the network interface to listen for RCON commands on (or 0.0.0.0 for all). - rconAddress: 127.0.0.1 - # Port number for RCON to listen on. - rconPort: 9990 - # RCON access password. - rconPassword: "PASSWORD" - # Flag indicating whether or not verbose RCON debug logging is enabled. - rconDebug: false + # Flag indicating whether or not REST API is enabled. + restEnable: false + # IP address of the network interface to listen for REST API on (or 0.0.0.0 for all). + restAddress: 127.0.0.1 + # Port number for REST API to listen on. + restPort: 9990 + # REST API authentication password. + restPassword: "PASSWORD" + # Flag indicating whether or not verbose REST API debug logging is enabled. + restDebug: false # # Digital Protocol Configuration @@ -303,14 +303,11 @@ system: # Site Configuration # config: - # Flag indicating whether or not this host is authoritative for TGID operations. - # (By default this should be true, unless you are operating in a mode that will dynamically permit - # TGIDs via the permit-tg RCON command.) + # Flag indicating whether or not this host is authoritative (i.e. will automatically handle channel grant) for TGID operations. authoritative: true - # Flag indicating whether or not this host sends permit-tg commands to voice channels for TGID operations. - # (By default this should be false, unless you are operating in a mode that will dynamically permit - # TGIDs via the permit-tg RCON command.) - controlPermitTG: false + # Flag indicating whether or not this host acts as a supervisor (i.e. will automatically handle permission) for voice channel TGID permission. + supervisor: false + # Channel Identity (corresponds to the appropriate entry in the iden_table file). channelId: 2 # Channel Number (used to calculate actual host frequency based on the identity table). @@ -322,12 +319,12 @@ system: voiceChNo: # Channel Number (used to calculate actual host frequency based on the identity table). - channelNo: 1 - # RCON IP Address for voice channel. - rconAddress: 127.0.0.1 - # RCON Port number for voice channel. - rconPort: 9990 - # RCON access password for voice channel. - rconPassword: "PASSWORD" + # REST API IP Address for voice channel. + restAddress: 127.0.0.1 + # REST API Port number for voice channel. + restPort: 9990 + # REST API access password for voice channel. + restPassword: "PASSWORD" # DMR Color Code. colorCode: 1 diff --git a/src/dmr/Control.cpp b/src/dmr/Control.cpp index baa0d231..d44adff6 100644 --- a/src/dmr/Control.cpp +++ b/src/dmr/Control.cpp @@ -69,6 +69,7 @@ Control::Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupIdLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) : m_authoritative(authoritative), + m_supervisor(false), m_colorCode(colorCode), m_modem(modem), m_network(network), @@ -116,7 +117,7 @@ Control::~Control() /// Helper to set DMR configuration options. /// /// Instance of the ConfigINI class. -/// +/// Flag indicating whether the DMR has supervisory functions. /// Voice Channel Number list. /// Voice Channel data map. /// DMR Network ID. @@ -124,13 +125,13 @@ Control::~Control() /// Channel ID. /// Channel Number. /// -void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::vector voiceChNo, const std::unordered_map voiceChData, +void Control::setOptions(yaml::Node& conf, bool supervisor, const std::vector voiceChNo, const std::unordered_map voiceChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions) { yaml::Node systemConf = conf["system"]; yaml::Node dmrProtocol = conf["protocols"]["dmr"]; - m_controlPermitTG = controlPermitTG; + m_supervisor = supervisor; Slot::m_verifyReg = dmrProtocol["verifyReg"].as(false); @@ -159,11 +160,11 @@ void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::vect switch (m_tsccSlotNo) { case 1U: m_slot1->setTSCC(enableTSCC, dedicatedTSCC); - m_slot1->setControlPermitTG(m_controlPermitTG); + m_slot1->setSupervisor(m_supervisor); break; case 2U: m_slot2->setTSCC(enableTSCC, dedicatedTSCC); - m_slot2->setControlPermitTG(m_controlPermitTG); + m_slot2->setSupervisor(m_supervisor); break; default: LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo); @@ -369,6 +370,29 @@ void Control::clock(uint32_t ms) m_slot2->clock(); } +/// +/// Sets a flag indicating whether DMR has supervisory functions and can send permit TG to voice channels. +/// +/// +void Control::setSupervisor(bool supervisor) +{ + if (!m_enableTSCC) { + return; + } + + switch (m_tsccSlotNo) { + case 1U: + m_slot1->setSupervisor(supervisor); + break; + case 2U: + m_slot2->setSupervisor(supervisor); + break; + default: + LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", m_tsccSlotNo); + break; + } +} + /// /// Permits a TGID on a non-authoritative host. /// diff --git a/src/dmr/Control.h b/src/dmr/Control.h index 6f07a88f..c61b153c 100644 --- a/src/dmr/Control.h +++ b/src/dmr/Control.h @@ -68,7 +68,7 @@ namespace dmr ~Control(); /// Helper to set DMR configuration options. - void setOptions(yaml::Node& conf, bool controlPermitTG, const std::vector voiceChNo, const std::unordered_map voiceChData, + void setOptions(yaml::Node& conf, bool supervisor, const std::vector voiceChNo, const std::unordered_map voiceChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions); /// Gets a flag indicating whether the DMR control channel is running. @@ -91,6 +91,8 @@ namespace dmr /// Updates the processor. void clock(uint32_t ms); + /// Sets a flag indicating whether DMR has supervisory functions and can send permit TG to voice channels. + void setSupervisor(bool supervisor); /// Permits a TGID on a non-authoritative host. void permittedTG(uint32_t dstId, uint8_t slot); @@ -127,7 +129,7 @@ namespace dmr friend class Slot; bool m_authoritative; - bool m_controlPermitTG; + bool m_supervisor; uint32_t m_colorCode; diff --git a/src/dmr/Slot.cpp b/src/dmr/Slot.cpp index 140c40c3..57a73555 100644 --- a/src/dmr/Slot.cpp +++ b/src/dmr/Slot.cpp @@ -160,6 +160,7 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_tsccPayloadGroup(false), m_tsccPayloadVoice(true), m_lastLateEntry(0U), + m_supervisor(false), m_verbose(verbose), m_debug(debug) { diff --git a/src/dmr/Slot.h b/src/dmr/Slot.h index a3ae083c..afed14e8 100644 --- a/src/dmr/Slot.h +++ b/src/dmr/Slot.h @@ -107,8 +107,8 @@ namespace dmr void setTSCC(bool enable, bool dedicated); /// Helper to activate a TSCC payload slot. void setTSCCActivated(uint32_t dstId, uint32_t srcId, bool group, bool voice); - /// Sets a flag indicating whether the DMR control channel can send permit-tg to voice channels. - void setControlPermitTG(bool controlPermitTG) { m_controlPermitTG = controlPermitTG; } + /// Sets a flag indicating whether the slot has supervisory functions and can send permit TG to voice channels. + void setSupervisor(bool supervisor) { m_supervisor = supervisor; } /// Helper to set the voice error silence threshold. void setSilenceThreshold(uint32_t threshold); @@ -198,7 +198,7 @@ namespace dmr uint32_t m_lastLateEntry; - bool m_controlPermitTG; + bool m_supervisor; bool m_verbose; bool m_debug; diff --git a/src/dmr/packet/ControlSignaling.cpp b/src/dmr/packet/ControlSignaling.cpp index 56cfcbe8..6de71005 100644 --- a/src/dmr/packet/ControlSignaling.cpp +++ b/src/dmr/packet/ControlSignaling.cpp @@ -872,8 +872,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); } - // callback RCON to permit-tg on the specified voice channel - if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) { + // callback REST API to permit the granted TG on the specified voice channel + if (m_tscc->m_authoritative && m_tscc->m_supervisor) { ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); @@ -939,8 +939,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); } - // callback RCON to permit-tg on the specified voice channel - if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) { + // callback REST API to permit the granted TG on the specified voice channel + if (m_tscc->m_authoritative && m_tscc->m_supervisor) { ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); diff --git a/src/host/Host.cpp b/src/host/Host.cpp index d0754d3d..e5d52f46 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -140,7 +140,7 @@ Host::Host(const std::string& confFile) : m_p25QueueSizeBytes(2592U), // 12 frames m_nxdnQueueSizeBytes(1488U), // 31 frames m_authoritative(true), - m_controlPermitTG(false), + m_supervisor(false), m_activeTickDelay(5U), m_idleTickDelay(5U), m_RESTAPI(nullptr) @@ -434,7 +434,7 @@ int Host::run() dmr = std::unique_ptr(new dmr::Control(m_authoritative, m_dmrColorCode, callHang, m_dmrQueueSizeBytes, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, dmrDumpCsbkData, dmrDebug, dmrVerbose)); - dmr->setOptions(m_conf, m_controlPermitTG, m_voiceChNo, m_voiceChData, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true); + dmr->setOptions(m_conf, m_supervisor, m_voiceChNo, m_voiceChData, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true); if (dmrCtrlChannel) { dmr->setCCRunning(true); @@ -507,7 +507,7 @@ int Host::run() p25 = std::unique_ptr(new p25::Control(m_authoritative, m_p25NAC, callHang, m_p25QueueSizeBytes, m_modem, m_network, m_timeout, m_rfTalkgroupHang, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket, p25DumpTsbkData, p25Debug, p25Verbose)); - p25->setOptions(m_conf, m_controlPermitTG, m_cwCallsign, m_voiceChNo, m_voiceChData, m_p25PatchSuperGroup, m_p25NetId, m_sysId, m_p25RfssId, + p25->setOptions(m_conf, m_supervisor, m_cwCallsign, m_voiceChNo, m_voiceChData, m_p25PatchSuperGroup, m_p25NetId, m_sysId, m_p25RfssId, m_siteId, m_channelId, m_channelNo, true); if (p25CtrlChannel) { @@ -572,7 +572,7 @@ int Host::run() nxdn = std::unique_ptr(new nxdn::Control(m_authoritative, m_nxdnRAN, callHang, m_nxdnQueueSizeBytes, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, nxdnDumpRcchData, nxdnDebug, nxdnVerbose)); - nxdn->setOptions(m_conf, m_controlPermitTG, m_cwCallsign, m_voiceChNo, m_voiceChData, m_siteId, m_sysId, m_channelId, m_channelNo, true); + nxdn->setOptions(m_conf, m_supervisor, m_cwCallsign, m_voiceChNo, m_voiceChData, m_siteId, m_sysId, m_channelId, m_channelNo, true); if (nxdnCtrlChannel) { nxdn->setCCRunning(true); @@ -1840,9 +1840,9 @@ bool Host::readParams() chNo = 4095U; } - std::string restApiAddress = channel["rconAddress"].as("127.0.0.1"); - uint16_t restApiPort = (uint16_t)channel["rconPort"].as(REST_API_DEFAULT_PORT); - std::string restApiPassword = channel["rconPassword"].as(); + std::string restApiAddress = channel["restAddress"].as("127.0.0.1"); + uint16_t restApiPort = (uint16_t)channel["restPort"].as(REST_API_DEFAULT_PORT); + std::string restApiPassword = channel["restPassword"].as(); ::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Adddress %s:%u", m_channelId, chNo, restApiAddress.c_str(), restApiPort); @@ -1896,6 +1896,10 @@ bool Host::readParams() LogInfo("System Config Parameters"); LogInfo(" Authoritative: %s", m_authoritative ? "yes" : "no"); + if (m_authoritative) { + m_supervisor = rfssConfig["supervisor"].as(false); + LogInfo(" Supervisor: %s", m_supervisor ? "yes" : "no"); + } LogInfo(" RX Frequency: %uHz", m_rxFrequency); LogInfo(" TX Frequency: %uHz", m_txFrequency); LogInfo(" Base Frequency: %uHz", entry.baseFrequency()); @@ -1921,11 +1925,8 @@ bool Host::readParams() LogInfo(" NXDN RAN: %u", m_nxdnRAN); if (!m_authoritative) { - m_controlPermitTG = false; - LogWarning(LOG_HOST, "Host is non-authoritative, this requires RCON to \"permit-tg\" for VCs and \"grant-tg\" for CCs!"); - } else { - m_controlPermitTG = rfssConfig["controlPermitTG"].as(false); - LogInfo(" Control Permit TG: %s", m_controlPermitTG ? "yes" : "no"); + m_supervisor = false; + LogWarning(LOG_HOST, "Host is non-authoritative! This requires REST API to handle permit TG for VCs and grant TG for CCs!"); } } else { @@ -2252,7 +2253,7 @@ bool Host::createNetwork() { yaml::Node networkConf = m_conf["network"]; bool netEnable = networkConf["enable"].as(false); - bool restApiEnable = networkConf["rconEnable"].as(false); + bool restApiEnable = networkConf["restEnable"].as(false); // dump out if both networking and REST API are disabled if (!netEnable && !restApiEnable) { @@ -2262,10 +2263,10 @@ bool Host::createNetwork() std::string address = networkConf["address"].as(); uint16_t port = (uint16_t)networkConf["port"].as(TRAFFIC_DEFAULT_PORT); uint16_t local = (uint16_t)networkConf["local"].as(0U); - std::string restApiAddress = networkConf["rconAddress"].as("127.0.0.1"); - uint16_t restApiPort = (uint16_t)networkConf["rconPort"].as(REST_API_DEFAULT_PORT); - std::string restApiPassword = networkConf["rconPassword"].as(); - bool restApiDebug = networkConf["rconDebug"].as(false); + std::string restApiAddress = networkConf["restAddress"].as("127.0.0.1"); + uint16_t restApiPort = (uint16_t)networkConf["restPort"].as(REST_API_DEFAULT_PORT); + std::string restApiPassword = networkConf["restPassword"].as(); + bool restApiDebug = networkConf["restDebug"].as(false); uint32_t id = networkConf["id"].as(0U); uint32_t jitter = networkConf["talkgroupHang"].as(360U); std::string password = networkConf["password"].as(); diff --git a/src/host/Host.h b/src/host/Host.h index b3b1bc29..79193515 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -147,7 +147,7 @@ private: uint32_t m_nxdnQueueSizeBytes; bool m_authoritative; - bool m_controlPermitTG; + bool m_supervisor; uint8_t m_activeTickDelay; uint8_t m_idleTickDelay; diff --git a/src/network/RESTAPI.cpp b/src/network/RESTAPI.cpp index e36362af..ea783531 100644 --- a/src/network/RESTAPI.cpp +++ b/src/network/RESTAPI.cpp @@ -273,6 +273,7 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(PUT_MDM_MODE).put(REST_API_BIND(RESTAPI::restAPI_PutModemMode, this)); m_dispatcher.match(PUT_MDM_KILL).put(REST_API_BIND(RESTAPI::restAPI_PutModemKill, this)); + m_dispatcher.match(PUT_SET_SUPERVISOR).put(REST_API_BIND(RESTAPI::restAPI_PutSetSupervisor, this)); m_dispatcher.match(PUT_PERMIT_TG).put(REST_API_BIND(RESTAPI::restAPI_PutPermitTG, this)); m_dispatcher.match(PUT_GRANT_TG).put(REST_API_BIND(RESTAPI::restAPI_PutGrantTG, this)); m_dispatcher.match(GET_RELEASE_GRNTS).get(REST_API_BIND(RESTAPI::restAPI_GetReleaseGrants, this)); @@ -764,6 +765,101 @@ void RESTAPI::restAPI_PutModemKill(const HTTPPayload& request, HTTPPayload& repl } } +/// +/// +/// +/// +/// +/// +void RESTAPI::restAPI_PutSetSupervisor(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object req = json::object(); + if (!parseRequestBody(request, reply, req)) { + return; + } + + errorPayload(reply, "OK", HTTPPayload::OK); + + if (!m_host->m_authoritative) { + errorPayload(reply, "Host is not authoritative, cannot set supervisory state"); + return; + } + + // validate state is a string within the JSON blob + if (!req["state"].is()) { + errorPayload(reply, "state was not a valid integer"); + return; + } + + DVM_STATE state = (DVM_STATE)req["state"].get(); + + // validate destination ID is a integer within the JSON blob + if (!req["enable"].is()) { + errorPayload(reply, "enable was not a boolean"); + return; + } + + bool enable = req["enable"].get(); + + switch (state) { + case STATE_DMR: +#if defined(ENABLE_DMR) + { + if (m_dmr != nullptr) { + m_dmr->setSupervisor(enable); + } + else { + errorPayload(reply, "DMR mode is not enabled", HTTPPayload::SERVICE_UNAVAILABLE); + } + } +#else + { + errorPayload(reply, "DMR operations are unavailable", HTTPPayload::SERVICE_UNAVAILABLE); + } +#endif // defined(ENABLE_DMR) + break; + case STATE_P25: +#if defined(ENABLE_P25) + { + if (m_p25 != nullptr) { + m_p25->setSupervisor(enable); + } + else { + errorPayload(reply, "P25 mode is not enabled", HTTPPayload::SERVICE_UNAVAILABLE); + } + } +#else + { + errorPayload(reply, "P25 operations are unavailable", HTTPPayload::SERVICE_UNAVAILABLE); + } +#endif // defined(ENABLE_P25) + break; + case STATE_NXDN: +#if defined(ENABLE_NXDN) + { + if (m_nxdn != nullptr) { + m_nxdn->setSupervisor(enable); + } + else { + errorPayload(reply, "NXDN mode is not enabled", HTTPPayload::SERVICE_UNAVAILABLE); + } + } +#else + { + errorPayload(reply, "NXDN operations are unavailable", HTTPPayload::SERVICE_UNAVAILABLE); + } +#endif // defined(ENABLE_NXDN) + break; + default: + errorPayload(reply, "invalid mode"); + break; + } +} + /// /// /// diff --git a/src/network/RESTAPI.h b/src/network/RESTAPI.h index 08c32767..012e7e83 100644 --- a/src/network/RESTAPI.h +++ b/src/network/RESTAPI.h @@ -122,6 +122,8 @@ private: /// void restAPI_PutModemKill(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + void restAPI_PutSetSupervisor(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /// void restAPI_PutPermitTG(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /// diff --git a/src/network/RESTDefines.h b/src/network/RESTDefines.h index 826e56fc..e6a05510 100644 --- a/src/network/RESTDefines.h +++ b/src/network/RESTDefines.h @@ -58,6 +58,7 @@ #define PUT_MDM_KILL "/mdm/kill" +#define PUT_SET_SUPERVISOR "/set-supervisor" #define PUT_PERMIT_TG "/permit-tg" #define PUT_GRANT_TG "/grant-tg" #define GET_RELEASE_GRNTS "/release-grants" diff --git a/src/nxdn/Control.cpp b/src/nxdn/Control.cpp index be89e2d3..afc049e0 100644 --- a/src/nxdn/Control.cpp +++ b/src/nxdn/Control.cpp @@ -94,7 +94,7 @@ Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t q m_voice(nullptr), m_data(nullptr), m_authoritative(authoritative), - m_controlPermitTG(false), + m_supervisor(false), m_ran(ran), m_timeout(timeout), m_modem(modem), @@ -204,8 +204,8 @@ void Control::reset() /// Helper to set NXDN configuration options. /// /// Instance of the yaml::Node class. -/// -/// +/// Flag indicating whether the DMR has supervisory functions. +/// CW callsign of this host. /// Voice Channel Number list. /// Voice Channel data map. /// NXDN Site Code. @@ -213,14 +213,14 @@ void Control::reset() /// Channel ID. /// Channel Number. /// -void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::string cwCallsign, const std::vector voiceChNo, +void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector voiceChNo, const std::unordered_map voiceChData, uint16_t siteId, uint32_t sysId, uint8_t channelId, uint32_t channelNo, bool printOptions) { yaml::Node systemConf = conf["system"]; yaml::Node nxdnProtocol = conf["protocols"]["nxdn"]; - m_controlPermitTG = controlPermitTG; + m_supervisor = supervisor; m_trunk->m_verifyAff = nxdnProtocol["verifyAff"].as(false); m_trunk->m_verifyReg = nxdnProtocol["verifyReg"].as(false); diff --git a/src/nxdn/Control.h b/src/nxdn/Control.h index 53bf62a8..9803faa1 100644 --- a/src/nxdn/Control.h +++ b/src/nxdn/Control.h @@ -82,7 +82,7 @@ namespace nxdn void reset(); /// Helper to set NXDN configuration options. - void setOptions(yaml::Node& conf, bool controlPermitTG, const std::string cwCallsign, const std::vector voiceChNo, + void setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector voiceChNo, const std::unordered_map voiceChData, uint16_t siteId, uint32_t sysId, uint8_t channelId, uint32_t channelNo, bool printOptions); @@ -103,6 +103,8 @@ namespace nxdn /// Updates the processor by the passed number of milliseconds. void clock(uint32_t ms); + /// Sets a flag indicating whether NXDN has supervisory functions and can send permit TG to voice channels. + void setSupervisor(bool supervisor) { m_supervisor = supervisor; } /// Permits a TGID on a non-authoritative host. void permittedTG(uint32_t dstId); @@ -132,7 +134,7 @@ namespace nxdn packet::Trunk* m_trunk; bool m_authoritative; - bool m_controlPermitTG; + bool m_supervisor; uint32_t m_ran; uint32_t m_timeout; diff --git a/src/nxdn/packet/Trunk.cpp b/src/nxdn/packet/Trunk.cpp index bce765b3..50d725d9 100644 --- a/src/nxdn/packet/Trunk.cpp +++ b/src/nxdn/packet/Trunk.cpp @@ -550,8 +550,8 @@ bool Trunk::writeRF_Message_Grant(uint32_t srcId, uint32_t dstId, uint8_t servic } } - // callback RCON to permit-tg on the specified voice channel - if (m_nxdn->m_authoritative && m_nxdn->m_controlPermitTG) { + // callback REST API to permit the granted TG on the specified voice channel + if (m_nxdn->m_authoritative && m_nxdn->m_supervisor) { ::lookups::VoiceChData voiceChData = m_nxdn->m_affiliations.getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && chNo != m_nxdn->m_siteData.channelNo()) { diff --git a/src/p25/Control.cpp b/src/p25/Control.cpp index 0ea072fa..4134357a 100644 --- a/src/p25/Control.cpp +++ b/src/p25/Control.cpp @@ -90,7 +90,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q m_data(nullptr), m_trunk(nullptr), m_authoritative(authoritative), - m_controlPermitTG(false), + m_supervisor(false), m_nac(nac), m_txNAC(nac), m_timeout(timeout), @@ -208,8 +208,8 @@ void Control::reset() /// Helper to set P25 configuration options. /// /// Instance of the yaml::Node class. -/// -/// +/// Flag indicating whether the DMR has supervisory functions. +/// CW callsign of this host. /// Voice Channel Number list. /// Voice Channel data map. /// @@ -220,14 +220,14 @@ void Control::reset() /// Channel ID. /// Channel Number. /// -void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::string cwCallsign, const std::vector voiceChNo, +void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector voiceChNo, const std::unordered_map voiceChData, uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions) { yaml::Node systemConf = conf["system"]; yaml::Node p25Protocol = conf["protocols"]["p25"]; - m_controlPermitTG = controlPermitTG; + m_supervisor = supervisor; m_tduPreambleCount = p25Protocol["tduPreambleCount"].as(8U); diff --git a/src/p25/Control.h b/src/p25/Control.h index ca1b0851..f8bcbfd6 100644 --- a/src/p25/Control.h +++ b/src/p25/Control.h @@ -84,7 +84,7 @@ namespace p25 void reset(); /// Helper to set P25 configuration options. - void setOptions(yaml::Node& conf, bool controlPermitTG, const std::string cwCallsign, const std::vector voiceChNo, + void setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector voiceChNo, const std::unordered_map voiceChData, uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions); @@ -113,6 +113,8 @@ namespace p25 /// Updates the processor by the passed number of milliseconds. void clock(uint32_t ms); + /// Sets a flag indicating whether P25 has supervisory functions and can send permit TG to voice channels. + void setSupervisor(bool supervisor) { m_supervisor = supervisor; } /// Permits a TGID on a non-authoritative host. void permittedTG(uint32_t dstId); @@ -145,7 +147,7 @@ namespace p25 friend class lookups::P25AffiliationLookup; bool m_authoritative; - bool m_controlPermitTG; + bool m_supervisor; uint32_t m_nac; uint32_t m_txNAC; diff --git a/src/p25/packet/Trunk.cpp b/src/p25/packet/Trunk.cpp index a62890e0..cd52a865 100644 --- a/src/p25/packet/Trunk.cpp +++ b/src/p25/packet/Trunk.cpp @@ -2232,8 +2232,8 @@ bool Trunk::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOp ::ActivityLog("P25", true, "group grant request from %u to TG %u", srcId, dstId); } - // callback RCON to permit-tg on the specified voice channel - if (m_p25->m_authoritative && m_p25->m_controlPermitTG) { + // callback REST API to permit the granted TG on the specified voice channel + if (m_p25->m_authoritative && m_p25->m_supervisor) { ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && chNo != m_p25->m_siteData.channelNo()) { @@ -2272,8 +2272,8 @@ bool Trunk::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOp ::ActivityLog("P25", true, "unit-to-unit grant request from %u to %u", srcId, dstId); } - // callback RCON to permit-tg on the specified voice channel - if (m_p25->m_authoritative && m_p25->m_controlPermitTG) { + // callback REST API to permit the granted TG on the specified voice channel + if (m_p25->m_authoritative && m_p25->m_supervisor) { ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && chNo != m_p25->m_siteData.channelNo()) {