diff --git a/dmr/Control.cpp b/dmr/Control.cpp
index 155e0a39..928aed7a 100644
--- a/dmr/Control.cpp
+++ b/dmr/Control.cpp
@@ -110,12 +110,15 @@ Control::~Control()
/// Helper to set DMR configuration options.
///
/// Instance of the ConfigINI class.
-///
-///
-///
-///
+/// Voice Channel Number list.
+/// Voice Channel data map.
+/// DMR Network ID.
+/// DMR Site ID.
+/// Channel ID.
+/// Channel Number.
///
-void Control::setOptions(yaml::Node& conf, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions)
+void Control::setOptions(yaml::Node& conf, 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"];
@@ -139,7 +142,7 @@ void Control::setOptions(yaml::Node& conf, uint32_t netId, uint8_t siteId, uint8
dedicatedTSCC = false;
}
- Slot::setSiteData(netId, siteId, channelId, channelNo, dedicatedTSCC);
+ Slot::setSiteData(voiceChNo, voiceChData, netId, siteId, channelId, channelNo, dedicatedTSCC);
Slot::setAlohaConfig(nRandWait, backOff);
m_tsccSlotNo = (uint8_t)control["slot"].as(0U);
@@ -328,9 +331,20 @@ void Control::clock()
/// Permits a TGID on a non-authoritative host.
///
///
-void Control::permittedTG(uint32_t dstId)
+///
+void Control::permittedTG(uint32_t dstId, uint8_t slot)
{
- // TODO TODO
+ switch (slot) {
+ case 1U:
+ m_slot1->permittedTG(dstId);
+ break;
+ case 2U:
+ m_slot2->permittedTG(dstId);
+ break;
+ default:
+ LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slot);
+ break;
+ }
}
///
diff --git a/dmr/Control.h b/dmr/Control.h
index 369434fe..4a662c18 100644
--- a/dmr/Control.h
+++ b/dmr/Control.h
@@ -41,6 +41,7 @@
#include "lookups/IdenTableLookup.h"
#include "lookups/RadioIdLookup.h"
#include "lookups/TalkgroupIdLookup.h"
+#include "lookups/AffiliationLookup.h"
#include "yaml/Yaml.h"
namespace dmr
@@ -68,7 +69,8 @@ namespace dmr
~Control();
/// Helper to set DMR configuration options.
- void setOptions(yaml::Node& conf, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions);
+ void setOptions(yaml::Node& conf, 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.
bool getCCRunning() { return m_ccRunning; }
@@ -91,7 +93,7 @@ namespace dmr
void clock();
/// Permits a TGID on a non-authoritative host.
- void permittedTG(uint32_t dstId);
+ void permittedTG(uint32_t dstId, uint8_t slot);
/// Helper to return the slot carrying the TSCC.
Slot* getTSCCSlot() const;
diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp
index 5af66d1e..ddcbce84 100644
--- a/dmr/Slot.cpp
+++ b/dmr/Slot.cpp
@@ -125,6 +125,7 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_rfLastDstId(0U),
m_netState(RS_NET_IDLE),
m_netLastDstId(0U),
+ m_permittedDstId(0U),
m_rfLC(nullptr),
m_rfPrivacyLC(nullptr),
m_rfDataHeader(nullptr),
@@ -538,6 +539,19 @@ void Slot::clock()
}
}
+///
+/// Permits a TGID on a non-authoritative host.
+///
+///
+void Slot::permittedTG(uint32_t dstId)
+{
+ if (!m_authoritative) {
+ return;
+ }
+
+ m_permittedDstId = dstId;
+}
+
///
/// Helper to change the debug and verbose state.
///
@@ -645,12 +659,15 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s
///
/// Sets local configured site data.
///
+/// Voice Channel Number list.
+/// Voice Channel data map.
/// DMR Network ID.
/// DMR Site ID.
/// Channel ID.
/// Channel Number.
///
-void Slot::setSiteData(uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReg)
+void Slot::setSiteData(const std::vector voiceChNo, const std::unordered_map voiceChData,
+ uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReg)
{
m_siteData = SiteData(SITE_MODEL_SMALL, netId, siteId, 3U, requireReg);
m_channelNo = channelNo;
@@ -664,6 +681,13 @@ void Slot::setSiteData(uint32_t netId, uint8_t siteId, uint8_t channelId, uint32
}
}
+ std::vector availCh = voiceChNo;
+ for (auto it = availCh.begin(); it != availCh.end(); ++it) {
+ m_affiliations->addRFCh(*it);
+ }
+
+ m_affiliations->setRFChData(voiceChData);
+
lc::CSBK::setSiteData(m_siteData);
}
diff --git a/dmr/Slot.h b/dmr/Slot.h
index 7cd6a696..8354a1c6 100644
--- a/dmr/Slot.h
+++ b/dmr/Slot.h
@@ -94,6 +94,9 @@ namespace dmr
/// Updates the slot processor.
void clock();
+ /// Permits a TGID on a non-authoritative host.
+ void permittedTG(uint32_t dstId);
+
/// Gets instance of the ControlSignaling class.
packet::ControlSignaling* control() { return m_control; }
@@ -110,7 +113,8 @@ namespace dmr
network::BaseNetwork* network, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup,
lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, uint32_t jitter, bool verbose);
/// Sets local configured site data.
- static void setSiteData(uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReq);
+ static void setSiteData(const std::vector voiceChNo, const std::unordered_map voiceChData,
+ uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReq);
/// Sets TSCC Aloha configuration.
static void setAlohaConfig(uint8_t nRandWait, uint8_t backOff);
@@ -132,6 +136,8 @@ namespace dmr
RPT_NET_STATE m_netState;
uint32_t m_netLastDstId;
+ uint32_t m_permittedDstId;
+
std::unique_ptr m_rfLC;
std::unique_ptr m_rfPrivacyLC;
std::unique_ptr m_rfDataHeader;
diff --git a/dmr/packet/ControlSignaling.cpp b/dmr/packet/ControlSignaling.cpp
index 32ecee4f..851db1bc 100644
--- a/dmr/packet/ControlSignaling.cpp
+++ b/dmr/packet/ControlSignaling.cpp
@@ -215,7 +215,11 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len)
// verify the source RID is registered
VERIFY_SRCID_REG("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, srcId);
- writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), false);
+ if (m_slot->m_authoritative) {
+ writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), false);
+ } else {
+ m_slot->m_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, true);
+ }
break;
case SVC_KIND_GRP_VOICE_CALL:
// make sure control data is supported
@@ -227,7 +231,11 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len)
// validate the talkgroup ID
VALID_TGID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call)", SVC_KIND_GRP_VOICE_CALL, srcId, dstId);
- writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), true);
+ if (m_slot->m_authoritative) {
+ writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), true);
+ } else {
+ m_slot->m_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, false);
+ }
break;
case SVC_KIND_IND_DATA_CALL:
case SVC_KIND_IND_UDT_DATA_CALL:
diff --git a/host/Host.cpp b/host/Host.cpp
index 737fc6d0..ed054ea8 100644
--- a/host/Host.cpp
+++ b/host/Host.cpp
@@ -113,6 +113,8 @@ Host::Host(const std::string& confFile) :
m_txFrequency(0U),
m_channelId(0U),
m_channelNo(0U),
+ m_voiceChNo(),
+ m_voiceChData(),
m_idenTable(nullptr),
m_ridLookup(nullptr),
m_tidLookup(nullptr),
@@ -431,7 +433,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_dmrNetId, m_siteId, m_channelId, m_channelNo, true);
+ dmr->setOptions(m_conf, m_voiceChNo, m_voiceChData, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true);
if (dmrCtrlChannel) {
dmr->setCCRunning(true);
@@ -504,7 +506,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_cwCallsign, m_voiceChNo, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId,
+ p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_voiceChData, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId,
m_siteId, m_channelId, m_channelNo, true);
if (p25CtrlChannel) {
@@ -1847,8 +1849,8 @@ bool Host::readParams()
::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X RCON Adddress %s:%u", m_channelId, chNo, rconAddress.c_str(), rconPort);
- // TODO handle storing RCON data for voice channels
-
+ VoiceChData data = VoiceChData(chNo, rconAddress, rconPort, rconPassword);
+ m_voiceChData[chNo] = data;
m_voiceChNo.push_back(chNo);
}
@@ -1921,6 +1923,10 @@ bool Host::readParams()
LogInfo(" P25 Network Id: $%05X", m_p25NetId);
LogInfo(" P25 System Id: $%03X", m_p25SysId);
LogInfo(" P25 RFSS Id: $%02X", m_p25RfssId);
+
+ if (!m_authoritative) {
+ LogWarning(LOG_HOST, "Host is non-authoritative, this requires RCON to \"permit-tg\" for VCs and \"grant-tg\" for CCs!");
+ }
}
else {
LogInfo(" Modem Remote Control: yes");
diff --git a/host/Host.h b/host/Host.h
index fd511f0a..6d925b09 100644
--- a/host/Host.h
+++ b/host/Host.h
@@ -36,12 +36,14 @@
#include "network/RemoteControl.h"
#include "modem/Modem.h"
#include "Timer.h"
+#include "lookups/AffiliationLookup.h"
#include "lookups/IdenTableLookup.h"
#include "lookups/RadioIdLookup.h"
#include "lookups/TalkgroupIdLookup.h"
#include "yaml/Yaml.h"
#include
+#include
// ---------------------------------------------------------------------------
// Class Prototypes
@@ -64,6 +66,11 @@ public:
/// Executes the main modem host processing loop.
int run();
+ /// Gets the voice channel number list.
+ std::vector getVoiceChNo() const { return m_voiceChNo; }
+ /// Gets the voice channel data.
+ std::unordered_map getVoiceChData() const { return m_voiceChData; }
+
private:
const std::string& m_confFile;
yaml::Node m_conf;
@@ -107,7 +114,9 @@ private:
uint32_t m_txFrequency;
uint8_t m_channelId;
uint32_t m_channelNo;
+
std::vector m_voiceChNo;
+ std::unordered_map m_voiceChData;
lookups::IdenTableLookup* m_idenTable;
lookups::RadioIdLookup* m_ridLookup;
diff --git a/lookups/AffiliationLookup.cpp b/lookups/AffiliationLookup.cpp
index d619a893..ae2dd09e 100644
--- a/lookups/AffiliationLookup.cpp
+++ b/lookups/AffiliationLookup.cpp
@@ -44,6 +44,7 @@ using namespace lookups;
/// Flag indicating whether verbose logging is enabled.
AffiliationLookup::AffiliationLookup(const char* name, bool verbose) :
m_rfChTable(),
+ m_rfChDataTable(),
m_rfGrantChCnt(0U),
m_unitRegTable(),
m_grpAffTable(),
@@ -401,6 +402,27 @@ uint32_t AffiliationLookup::getGrantedCh(uint32_t dstId)
return 0U;
}
+///
+/// Helper to get RF channel data.
+///
+///
+///
+VoiceChData AffiliationLookup::getRFChData(uint32_t chNo) const
+{
+ if (chNo == 0U) {
+ return VoiceChData();
+ }
+
+ VoiceChData data;
+ try {
+ data = m_rfChDataTable.at(chNo);
+ } catch (...) {
+ data = VoiceChData();
+ }
+
+ return VoiceChData();
+}
+
///
/// Updates the processor by the passed number of milliseconds.
///
diff --git a/lookups/AffiliationLookup.h b/lookups/AffiliationLookup.h
index e1151de6..ecf0abbd 100644
--- a/lookups/AffiliationLookup.h
+++ b/lookups/AffiliationLookup.h
@@ -36,6 +36,65 @@
namespace lookups
{
+ // ---------------------------------------------------------------------------
+ // Class Declaration
+ // Represents voice channel data.
+ // ---------------------------------------------------------------------------
+
+ class HOST_SW_API VoiceChData {
+ public:
+ /// Initializes a new instance of the VoiceChData class.
+ VoiceChData() :
+ m_chNo(0U),
+ m_address(),
+ m_port(),
+ m_password()
+ {
+ /* stub */
+ }
+ /// Initializes a new instance of the VoiceChData class.
+ /// Voice Channel Number.
+ /// RCON Address.
+ /// RCON Port.
+ /// RCON Password.
+ VoiceChData(uint32_t chNo, std::string address, uint16_t port, std::string password) :
+ m_chNo(chNo),
+ m_address(address),
+ m_port(port),
+ m_password(password)
+ {
+ /* stub */
+ }
+
+ /// Equals operator.
+ ///
+ ///
+ VoiceChData & operator=(const VoiceChData & data)
+ {
+ if (this != &data) {
+ m_chNo = data.m_chNo;
+ m_address = data.m_address;
+ m_port = data.m_port;
+ m_password = data.m_password;
+ }
+
+ return *this;
+ }
+
+ /// Helper to determine if the channel is valid.
+ bool isValidCh() { return m_chNo != 0U; }
+
+ public:
+ /// Voice Channel Number.
+ __READONLY_PROPERTY_PLAIN(uint32_t, chNo, chNo);
+ /// RCON Address.
+ __READONLY_PROPERTY_PLAIN(std::string, address, address);
+ /// RCON Port.
+ __READONLY_PROPERTY_PLAIN(uint16_t, port, port);
+ /// RCON Password.
+ __READONLY_PROPERTY_PLAIN(std::string, password, password);
+ };
+
// ---------------------------------------------------------------------------
// Class Declaration
// Implements a lookup table class that contains subscriber registration
@@ -90,6 +149,11 @@ namespace lookups
/// Helper to get the channel granted for the given destination ID.
virtual uint32_t getGrantedCh(uint32_t dstId);
+ /// Helper to set RF channel data.
+ void setRFChData(const std::unordered_map chData) { m_rfChDataTable = chData; }
+ /// Helper to get RF channel data.
+ VoiceChData getRFChData(uint32_t chNo) const;
+
/// Helper to add a RF channel.
void addRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); }
/// Helper to remove a RF channel.
@@ -106,6 +170,7 @@ namespace lookups
protected:
std::vector m_rfChTable;
+ std::unordered_map m_rfChDataTable;
uint8_t m_rfGrantChCnt;
std::vector m_unitRegTable;
diff --git a/network/BaseNetwork.cpp b/network/BaseNetwork.cpp
index e373e40f..50f4268c 100644
--- a/network/BaseNetwork.cpp
+++ b/network/BaseNetwork.cpp
@@ -466,6 +466,38 @@ bool BaseNetwork::writeNXDN(const nxdn::lc::RTCH& lc, const uint8_t* data, const
return writeNXDN(m_id, m_nxdnStreamId, lc, data, len);
}
+///
+/// Writes grant request to the network.
+///
+///
+///
+///
+///
+///
+///
+bool BaseNetwork::writeGrantReq(const uint8_t mode, const uint32_t srcId, const uint32_t dstId, const uint8_t slot, const bool unitToUnit)
+{
+ if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
+ return false;
+
+ uint8_t buffer[DATA_PACKET_LENGTH];
+ ::memset(buffer, 0x00U, DATA_PACKET_LENGTH);
+
+ ::memcpy(buffer + 0U, TAG_REPEATER_GRANT, 7U);
+ __SET_UINT32(m_id, buffer, 7U);
+
+ __SET_UINT32(srcId, buffer, 11U); // Source Address
+ __SET_UINT32(dstId, buffer, 15U); // Destination Address
+ buffer[19U] = slot; // Slot Number
+
+ if (unitToUnit)
+ buffer[19U] |= 0x80U;
+
+ buffer[20U] = mode; // DVM Mode State
+
+ return write((uint8_t*)buffer, 21U);
+}
+
///
/// Writes the local activity log to the network.
///
diff --git a/network/BaseNetwork.h b/network/BaseNetwork.h
index a2905969..4c79e800 100644
--- a/network/BaseNetwork.h
+++ b/network/BaseNetwork.h
@@ -73,8 +73,6 @@
#define TAG_MASTER_CLOSING "MSTCL"
#define TAG_MASTER_PONG "MSTPONG"
-#define TAG_MASTER_GRANT "MSTGRNT"
-
#define TAG_REPEATER_LOGIN "RPTL"
#define TAG_REPEATER_AUTH "RPTK"
#define TAG_REPEATER_CONFIG "RPTC"
@@ -83,6 +81,8 @@
#define TAG_REPEATER_CLOSING "RPTCL"
#define TAG_REPEATER_PING "RPTPING"
+#define TAG_REPEATER_GRANT "RPTGRNT"
+
#define TAG_TRANSFER_ACT_LOG "TRNSLOG"
#define TAG_TRANSFER_DIAG_LOG "TRNSDIAG"
@@ -156,6 +156,9 @@ namespace network
/// Writes NXDN frame data to the network.
virtual bool writeNXDN(const nxdn::lc::RTCH& lc, const uint8_t* data, const uint32_t len);
+ /// Writes a grant request to the network.
+ virtual bool writeGrantReq(const uint8_t mode, const uint32_t srcId, const uint32_t dstId, const uint8_t slot, const bool unitToUnit);
+
/// Writes the local activity log to the network.
virtual bool writeActLog(const char* message);
diff --git a/network/RemoteControl.cpp b/network/RemoteControl.cpp
index 48875ad0..214b5a47 100644
--- a/network/RemoteControl.cpp
+++ b/network/RemoteControl.cpp
@@ -56,14 +56,18 @@ using namespace modem;
// Constants
// ---------------------------------------------------------------------------
-#define BAD_CMD_STR "Bad or invalid remote command."
-#define INVALID_AUTH_STR "Invalid authentication"
-#define INVALID_OPT_STR "Invalid command arguments: "
-#define CMD_FAILED_STR "Remote command failed: "
+#define RCON_CMD_OK "ACK"
+#define RCON_CMD_NACK "NACK: "
+
+#define BAD_CMD_STR "NACK: Bad or invalid remote command"
+#define NO_DATA_CMD_STR "NACK: No data"
+#define INVALID_AUTH_STR "NACK: Invalid authentication"
+#define INVALID_OPT_STR "NACK: Invalid command arguments, "
#define RCD_GET_VERSION "version"
#define RCD_GET_HELP "help"
#define RCD_GET_STATUS "status"
+#define RCD_GET_VOICE_CH "voice-ch"
#define RCD_MODE "mdm-mode"
#define RCD_MODE_OPT_IDLE "idle"
@@ -76,6 +80,7 @@ using namespace modem;
#define RCD_FORCE_KILL "mdm-force-kill"
#define RCD_PERMIT_TG "permit-tg"
+#define RCD_GRANT_TG "grant-tg"
#define RCD_RID_WLIST "rid-whitelist"
#define RCD_RID_BLIST "rid-blacklist"
@@ -211,7 +216,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
args.clear();
uint8_t buffer[RC_BUFFER_LENGTH];
- std::string reply = "OK";
+ std::string reply = RCON_CMD_OK;
sockaddr_storage address;
uint32_t addrLen;
@@ -245,7 +250,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
uint8_t hash[32U];
::memset(hash, 0x00U, 32U);
if (::memcmp(m_passwordHash, buffer + 2U, 32U) != 0) {
- LogError(LOG_RCON, CMD_FAILED_STR INVALID_AUTH_STR " from %s", UDPSocket::address(address).c_str());
+ LogError(LOG_RCON, INVALID_AUTH_STR " from %s", UDPSocket::address(address).c_str());
reply = INVALID_AUTH_STR;
writeResponse(reply, address, addrLen);
return;
@@ -294,6 +299,9 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
else if (rcom == RCD_GET_STATUS) {
reply = rcdGetStatus(host, dmr, p25, nxdn);
}
+ else if (rcom == RCD_GET_VOICE_CH) {
+ reply = rcdGetVoiceCh(host, dmr, p25, nxdn);
+ }
else if (rcom == RCD_MODE && argCnt >= 1U) {
reply = rcdMode(args, host, dmr, p25, nxdn);
}
@@ -307,6 +315,9 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
else if (rcom == RCD_PERMIT_TG && argCnt >= 1U) {
reply = rcdPermitTG(args, host, dmr, p25, nxdn);
}
+ else if (rcom == RCD_GRANT_TG && argCnt >= 1U) {
+ reply = rcdPermitTG(args, host, dmr, p25, nxdn);
+ }
else if (rcom == RCD_RID_WLIST && argCnt >= 1U) {
uint32_t srcId = getArgUInt32(args, 0U);
if (srcId != 0U) {
@@ -334,12 +345,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
g_fireDMRBeacon = true;
}
else {
- reply = CMD_FAILED_STR "DMR beacons is not enabled!";
+ reply = RCON_CMD_NACK "DMR beacons is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -351,12 +362,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
g_fireP25Control = true;
}
else {
- reply = CMD_FAILED_STR "P25 control data is not enabled!";
+ reply = RCON_CMD_NACK "P25 control data is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -367,12 +378,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
p25->trunk()->setConvFallback((fallback == 1U) ? true : false);
}
else {
- reply = CMD_FAILED_STR "P25 control data is not enabled!";
+ reply = RCON_CMD_NACK "P25 control data is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -397,7 +408,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -420,7 +431,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -443,7 +454,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -466,7 +477,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -487,7 +498,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
p25->trunk()->setLastMFId(m_p25MFId);
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -505,7 +516,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -523,7 +534,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -541,7 +552,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -559,7 +570,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -577,7 +588,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -595,7 +606,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -604,7 +615,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
p25->affiliations().releaseGrant(0, true);
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -620,7 +631,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -630,7 +641,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
if (dmr != nullptr) {
if (host->m_dmrTSCCData) {
if (p25 != nullptr) {
- reply = CMD_FAILED_STR "Can't enable DMR control channel while P25 is enabled!";
+ reply = RCON_CMD_NACK "Can't enable DMR control channel while P25 is enabled!";
LogError(LOG_RCON, reply.c_str());
}
else {
@@ -640,12 +651,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "DMR control data is not enabled!";
+ reply = RCON_CMD_NACK "DMR control data is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -656,7 +667,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
LogInfoEx(LOG_RCON, reply.c_str());
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -666,7 +677,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
if (p25 != nullptr) {
if (host->m_p25CCData) {
if (dmr != nullptr) {
- reply = CMD_FAILED_STR "Can't enable P25 control channel while DMR is enabled!";
+ reply = RCON_CMD_NACK "Can't enable P25 control channel while DMR is enabled!";
LogError(LOG_RCON, reply.c_str());
}
else {
@@ -680,12 +691,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
}
}
else {
- reply = CMD_FAILED_STR "P25 control data is not enabled!";
+ reply = RCON_CMD_NACK "P25 control data is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -707,12 +718,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
LogInfoEx(LOG_RCON, reply.c_str());
}
else {
- reply = CMD_FAILED_STR "P25 control data is not enabled!";
+ reply = RCON_CMD_NACK "P25 control data is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -726,7 +737,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
reply = string_format("dmr->debug = %u, dmr->verbose = %u", debug, verbose);
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -737,7 +748,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
dmr->setDebugVerbose((debug == 1U) ? true : false, (verbose == 1U) ? true : false);
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -749,7 +760,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
reply = string_format("dmr->dumpCsbkData = %u", csbkDump);
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -759,7 +770,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
dmr->setCSBKVerbose((verbose == 1U) ? true : false);
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -774,7 +785,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
reply = string_format("p25->debug = %u, p25->verbose = %u", debug, verbose);
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -785,7 +796,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
p25->setDebugVerbose((debug == 1U) ? true : false, (verbose == 1U) ? true : false);
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -797,7 +808,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
reply = string_format("p25->dumpTsbkData = %u", tsbkDump);
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -807,7 +818,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
p25->trunk()->setTSBKVerbose((verbose == 1U) ? true : false);
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -822,7 +833,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
reply = string_format("nxdn->debug = %u, nxdn->verbose = %u", debug, verbose);
}
else {
- reply = CMD_FAILED_STR "NXDN mode is not enabled!";
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -833,7 +844,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
nxdn->setDebugVerbose((debug == 1U) ? true : false, (verbose == 1U) ? true : false);
}
else {
- reply = CMD_FAILED_STR "NXDN mode is not enabled!";
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -845,7 +856,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
reply = string_format("nxdn->dumpRcchData = %u", rcchDump);
}
else {
- reply = CMD_FAILED_STR "NXDN mode is not enabled!";
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -855,7 +866,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx
nxdn->setRCCHVerbose((verbose == 1U) ? true : false);
}
else {
- reply = CMD_FAILED_STR "NXDN mode is not enabled!";
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -997,12 +1008,14 @@ std::string RemoteControl::displayHelp()
reply += " version Display current version of host\r\n";
reply += " help Displays RCON help\r\n";
reply += " status Display current settings and operation mode\r\n";
+ reply += " voice-ch Retrieves the list of configured voice channels\r\n";
reply += "\r\n";
reply += " mdm-mode Set current mode of host (idle, lockout, dmr, p25, nxdn)\r\n";
reply += " mdm-kill Causes the host to quit\r\n";
reply += " mdm-force-kill Causes the host to quit immediately\r\n";
reply += "\r\n";
reply += " permit-tg Causes the host to permit the specified destination ID if non-authoritative\r\n";
+ reply += " grant-tg Causes the host to grant the specified destination ID if non-authoritative\r\n";
reply += "\r\n";
reply += " rid-whitelist Whitelists the specified RID in the host ACL tables\r\n";
reply += " rid-blacklist Blacklists the specified RID in the host ACL tables\r\n";
@@ -1078,6 +1091,7 @@ std::string RemoteControl::rcdGetStatus(Host* host, dmr::Control* dmr, p25::Cont
reply += string_format("Host State: %u, DMR: %u, P25: %u, NXDN: %u, Port Type: %s, Modem Port: %s, Port Speed: %u, Proto Ver: %u", host->m_state,
dmr != nullptr, p25 != nullptr, nxdn != nullptr, type.c_str(), modemPort.c_str(), portSpeed, host->m_modem->getVersion());
+ reply += string_format("\r\nDMR CC: %u, P25 CC: %u, NXDN CC: %u", host->m_dmrCtrlChannel, host->m_p25CtrlChannel, host->m_nxdnCtrlChannel);
}
{
@@ -1135,6 +1149,33 @@ std::string RemoteControl::rcdGetStatus(Host* host, dmr::Control* dmr, p25::Cont
return reply;
}
+///
+///
+///
+/// Instance of the Host class.
+/// Instance of the DMR Control class.
+/// Instance of the P25 Control class.
+/// Instance of the NXDN Control class.
+///
+std::string RemoteControl::rcdGetVoiceCh(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn)
+{
+ std::string reply = "";
+
+ if (host->m_voiceChData.size() > 0) {
+ for (auto it = host->m_voiceChData.begin(); it != host->m_voiceChData.end(); ++it) {
+ uint32_t chNo = it->first;
+ lookups::VoiceChData data = it->second;
+
+ reply += string_format("\r\n%u, %s:%u", chNo, data.address().c_str(), data.port());
+ }
+ }
+ else {
+ reply = NO_DATA_CMD_STR;
+ }
+
+ return reply;
+}
+
///
///
///
@@ -1170,7 +1211,7 @@ std::string RemoteControl::rcdMode(std::vector args, Host* host, dm
LogInfoEx(LOG_RCON, reply.c_str());
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -1184,7 +1225,7 @@ std::string RemoteControl::rcdMode(std::vector args, Host* host, dm
LogInfoEx(LOG_RCON, reply.c_str());
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -1198,7 +1239,7 @@ std::string RemoteControl::rcdMode(std::vector args, Host* host, dm
LogInfoEx(LOG_RCON, reply.c_str());
}
else {
- reply = CMD_FAILED_STR "NXDN mode is not enabled!";
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
}
@@ -1224,78 +1265,192 @@ std::string RemoteControl::rcdPermitTG(std::vector args, Host* host
{
std::string reply = "";
if (!host->m_authoritative) {
- DVM_STATE state = (DVM_STATE)getArgInt32(args, 0U);
- uint32_t dstId = getArgInt32(args, 1U);
- if (dstId == 0U) {
- reply = string_format(INVALID_OPT_STR "illegal TGID permitted (%u)", dstId);
+ reply = RCON_CMD_NACK "Host is authoritative, cannot permit TG!";
+ LogError(LOG_RCON, reply.c_str());
+ return reply;
+ }
+
+ DVM_STATE state = (DVM_STATE)getArgInt32(args, 0U);
+ uint32_t dstId = getArgInt32(args, 1U);
+ if (dstId == 0U) {
+ reply = string_format(INVALID_OPT_STR "illegal TGID permitted (%u)", dstId);
+ LogError(LOG_RCON, reply.c_str());
+ return reply;
+ }
+
+ switch (state) {
+ case STATE_DMR:
+#if defined(ENABLE_DMR)
+ {
+ uint8_t slot = getArgInt8(args, 2U);
+ if (slot == 0U) {
+ reply = INVALID_OPT_STR "illegal slot";
LogError(LOG_RCON, reply.c_str());
+ return reply;
+ }
+
+ if (dmr != nullptr) {
+ dmr->permittedTG(dstId, slot);
}
else {
- switch (state) {
- case STATE_DMR:
-#if defined(ENABLE_DMR)
- {
- if (dmr != nullptr) {
- dmr->permittedTG(dstId);
- }
- else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
- LogError(LOG_RCON, reply.c_str());
- }
- }
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+ }
#else
- {
- reply = INVALID_OPT_STR "invalid mode!";
- LogError(LOG_RCON, reply.c_str());
- }
+ {
+ reply = INVALID_OPT_STR "invalid mode!";
+ LogError(LOG_RCON, reply.c_str());
+ }
#endif // defined(ENABLE_DMR)
- break;
- case STATE_P25:
+ break;
+ case STATE_P25:
#if defined(ENABLE_P25)
- {
- if (p25 != nullptr) {
- p25->permittedTG(dstId);
- }
- else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
- LogError(LOG_RCON, reply.c_str());
- }
- }
+ {
+ if (p25 != nullptr) {
+ p25->permittedTG(dstId);
+ }
+ else {
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+ }
#else
- {
- reply = INVALID_OPT_STR "invalid mode!";
- LogError(LOG_RCON, reply.c_str());
- }
+ {
+ reply = INVALID_OPT_STR "invalid mode!";
+ LogError(LOG_RCON, reply.c_str());
+ }
#endif // defined(ENABLE_P25)
- break;
- case STATE_NXDN:
+ break;
+ case STATE_NXDN:
#if defined(ENABLE_NXDN)
- {
- if (nxdn != nullptr) {
- nxdn->permittedTG(dstId);
- }
- else {
- reply = CMD_FAILED_STR "NXDN mode is not enabled!";
- LogError(LOG_RCON, reply.c_str());
- }
- }
+ {
+ if (nxdn != nullptr) {
+ nxdn->permittedTG(dstId);
+ }
+ else {
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+ }
#else
- {
- reply = INVALID_OPT_STR "invalid mode!";
- LogError(LOG_RCON, reply.c_str());
- }
+ {
+ reply = INVALID_OPT_STR "invalid mode!";
+ LogError(LOG_RCON, reply.c_str());
+ }
#endif // defined(ENABLE_NXDN)
- break;
- default:
- reply = INVALID_OPT_STR "invalid mode!";
- LogError(LOG_RCON, reply.c_str());
- break;
- }
+ break;
+ default:
+ reply = INVALID_OPT_STR "invalid mode!";
+ LogError(LOG_RCON, reply.c_str());
+ break;
+ }
+
+ return reply;
+}
+
+///
+///
+///
+///
+/// Instance of the Host class.
+/// Instance of the DMR Control class.
+/// Instance of the P25 Control class.
+/// Instance of the NXDN Control class.
+///
+std::string RemoteControl::rcdGrantTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn)
+{
+ std::string reply = "";
+ if (!host->m_authoritative && (host->m_dmrCtrlChannel || host->m_p25CtrlChannel || host->m_nxdnCtrlChannel)) {
+ reply = RCON_CMD_NACK "Host is authoritative, cannot grant TG!";
+ LogError(LOG_RCON, reply.c_str());
+ return reply;
+ }
+
+ DVM_STATE state = (DVM_STATE)getArgInt32(args, 0U);
+ uint32_t dstId = getArgInt32(args, 1U);
+ uint8_t unitToUnit = getArgInt32(args, 2U);
+ if (unitToUnit > 1U) {
+ reply = string_format(INVALID_OPT_STR "illegal TGID granted (%u)", dstId);
+ LogError(LOG_RCON, reply.c_str());
+ return reply;
+ }
+
+ if (dstId == 0U) {
+ reply = string_format(INVALID_OPT_STR "illegal TGID granted (%u)", dstId);
+ LogError(LOG_RCON, reply.c_str());
+ return reply;
+ }
+
+ switch (state) {
+ case STATE_DMR:
+#if defined(ENABLE_DMR)
+ {
+ uint8_t slot = getArgInt8(args, 2U);
+ if (slot == 0U) {
+ reply = INVALID_OPT_STR "illegal slot";
+ LogError(LOG_RCON, reply.c_str());
+ return reply;
+ }
+
+ if (dmr != nullptr) {
+ // TODO TODO
+ //dmr->grantTG(dstId, slot, unitToUnit == 1U);
+ }
+ else {
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
+ LogError(LOG_RCON, reply.c_str());
}
}
- else {
- reply = CMD_FAILED_STR "Host is authoritative, cannot permit TG!";
+#else
+ {
+ reply = INVALID_OPT_STR "invalid mode!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+#endif // defined(ENABLE_DMR)
+ break;
+ case STATE_P25:
+#if defined(ENABLE_P25)
+ {
+ if (p25 != nullptr) {
+ // TODO TODO
+ //p25->grantTG(dstId, unitToUnit == 1U);
+ }
+ else {
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+ }
+#else
+ {
+ reply = INVALID_OPT_STR "invalid mode!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+#endif // defined(ENABLE_P25)
+ break;
+ case STATE_NXDN:
+#if defined(ENABLE_NXDN)
+ {
+ if (nxdn != nullptr) {
+ // TODO TODO
+ //nxdn->grantTG(dstId, unitToUnit == 1U);
+ }
+ else {
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+ }
+#else
+ {
+ reply = INVALID_OPT_STR "invalid mode!";
+ LogError(LOG_RCON, reply.c_str());
+ }
+#endif // defined(ENABLE_NXDN)
+ break;
+ default:
+ reply = INVALID_OPT_STR "invalid mode!";
LogError(LOG_RCON, reply.c_str());
+ break;
}
return reply;
@@ -1355,17 +1510,17 @@ std::string RemoteControl::rcdDMRModemInj(std::vector args, Host* h
host->m_modem->injectDMRData2(buffer, fileSize);
}
else {
- reply = CMD_FAILED_STR "invalid DMR slot!";
+ reply = RCON_CMD_NACK "invalid DMR slot!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "DMR data has too many errors!";
+ reply = RCON_CMD_NACK "DMR data has too many errors!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "DMR failed to open DMR data!";
+ reply = RCON_CMD_NACK "DMR failed to open DMR data!";
LogError(LOG_RCON, reply.c_str());
}
@@ -1377,7 +1532,7 @@ std::string RemoteControl::rcdDMRModemInj(std::vector args, Host* h
}
}
else {
- reply = CMD_FAILED_STR "DMR mode is not enabled!";
+ reply = RCON_CMD_NACK "DMR mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
@@ -1427,17 +1582,17 @@ std::string RemoteControl::rcdP25ModemInj(std::vector args, Host* h
host->m_modem->injectP25Data(buffer, fileSize);
}
else {
- reply = CMD_FAILED_STR "P25 data did not contain a valid NID!";
+ reply = RCON_CMD_NACK "P25 data did not contain a valid NID!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "P25 data has too many errors!";
+ reply = RCON_CMD_NACK "P25 data has too many errors!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "P25 failed to open P25 data!";
+ reply = RCON_CMD_NACK "P25 failed to open P25 data!";
LogError(LOG_RCON, reply.c_str());
}
@@ -1449,7 +1604,7 @@ std::string RemoteControl::rcdP25ModemInj(std::vector args, Host* h
}
}
else {
- reply = CMD_FAILED_STR "P25 mode is not enabled!";
+ reply = RCON_CMD_NACK "P25 mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
@@ -1497,12 +1652,12 @@ std::string RemoteControl::rcdNXDNModemInj(std::vector args, Host*
host->m_modem->injectNXDNData(buffer, fileSize);
}
else {
- reply = CMD_FAILED_STR "NXDN data has too many errors!";
+ reply = RCON_CMD_NACK "NXDN data has too many errors!";
LogError(LOG_RCON, reply.c_str());
}
}
else {
- reply = CMD_FAILED_STR "NXDN failed to open NXDN data!";
+ reply = RCON_CMD_NACK "NXDN failed to open NXDN data!";
LogError(LOG_RCON, reply.c_str());
}
@@ -1514,7 +1669,7 @@ std::string RemoteControl::rcdNXDNModemInj(std::vector args, Host*
}
}
else {
- reply = CMD_FAILED_STR "NXDN mode is not enabled!";
+ reply = RCON_CMD_NACK "NXDN mode is not enabled!";
LogError(LOG_RCON, reply.c_str());
}
diff --git a/network/RemoteControl.h b/network/RemoteControl.h
index 524bc1d6..2f7f885a 100644
--- a/network/RemoteControl.h
+++ b/network/RemoteControl.h
@@ -92,9 +92,14 @@ private:
///
std::string rcdGetStatus(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
///
+ std::string rcdGetVoiceCh(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
+ ///
std::string rcdMode(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
+
///
std::string rcdPermitTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
+ ///
+ std::string rcdGrantTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
///
std::string rcdDMRModemInj(std::vector args, Host* host, dmr::Control* dmr);
diff --git a/nxdn/Control.cpp b/nxdn/Control.cpp
index 3b6d7058..34ea38e8 100644
--- a/nxdn/Control.cpp
+++ b/nxdn/Control.cpp
@@ -105,6 +105,7 @@ Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t q
m_rfLastLICH(),
m_rfLC(),
m_netLC(),
+ m_permittedDstId(0U),
m_rfMask(0U),
m_netMask(0U),
m_idenTable(idenTable),
@@ -203,13 +204,15 @@ void Control::reset()
///
/// Instance of the yaml::Node class.
///
-///
-///
-///
-///
+/// Voice Channel Number list.
+/// Voice Channel data map.
+/// NXDN Location ID.
+/// Channel ID.
+/// Channel Number.
///
-void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo,
- uint16_t locId, uint8_t channelId, uint32_t channelNo, bool printOptions)
+void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo,
+ const std::unordered_map voiceChData, uint16_t locId,
+ uint8_t channelId, uint32_t channelNo, bool printOptions)
{
yaml::Node systemConf = conf["system"];
yaml::Node nxdnProtocol = conf["protocols"]["nxdn"];
@@ -249,6 +252,13 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
m_siteData = SiteData(locId, channelId, channelNo, serviceClass, false);
m_siteData.setCallsign(cwCallsign);
+ std::vector availCh = voiceChNo;
+ for (auto it = availCh.begin(); it != availCh.end(); ++it) {
+ m_affiliations.addRFCh(*it);
+ }
+
+ m_affiliations.setRFChData(voiceChData);
+
lc::RCCH::setSiteData(m_siteData);
lc::RCCH::setCallsign(cwCallsign);
@@ -261,11 +271,6 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
}
}
- std::vector availCh = voiceChNo;
- for (auto it = availCh.begin(); it != availCh.end(); ++it) {
- m_affiliations.addRFCh(*it);
- }
-
if (printOptions) {
LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F);
@@ -591,7 +596,11 @@ void Control::clock(uint32_t ms)
///
void Control::permittedTG(uint32_t dstId)
{
- // TODO TODO
+ if (!m_authoritative) {
+ return;
+ }
+
+ m_permittedDstId = dstId;
}
///
diff --git a/nxdn/Control.h b/nxdn/Control.h
index 214cb3aa..b3ded7c3 100644
--- a/nxdn/Control.h
+++ b/nxdn/Control.h
@@ -83,8 +83,9 @@ namespace nxdn
void reset();
/// Helper to set NXDN configuration options.
- void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo,
- uint16_t locId, uint8_t channelId, uint32_t channelNo, bool printOptions);
+ void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo,
+ const std::unordered_map voiceChData, uint16_t locId,
+ uint8_t channelId, uint32_t channelNo, bool printOptions);
/// Gets a flag indicating whether the NXDN control channel is running.
bool getCCRunning() { return m_ccRunning; }
@@ -145,6 +146,8 @@ namespace nxdn
lc::RTCH m_rfLC;
lc::RTCH m_netLC;
+ uint32_t m_permittedDstId;
+
uint8_t m_rfMask;
uint8_t m_netMask;
diff --git a/nxdn/packet/Trunk.cpp b/nxdn/packet/Trunk.cpp
index 7c82bd16..51118aac 100644
--- a/nxdn/packet/Trunk.cpp
+++ b/nxdn/packet/Trunk.cpp
@@ -181,7 +181,11 @@ bool Trunk::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len)
(rcch->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
(rcch->getPriority() & 0x07U); // Priority
- writeRF_Message_Grant(srcId, dstId, serviceOptions, true);
+ if (m_nxdn->m_authoritative) {
+ writeRF_Message_Grant(srcId, dstId, serviceOptions, true);
+ } else {
+ m_network->writeGrantReq(modem::DVM_STATE::STATE_NXDN, srcId, dstId, 0U, false);
+ }
}
break;
case RCCH_MESSAGE_TYPE_REG:
diff --git a/p25/Control.cpp b/p25/Control.cpp
index 85506d68..c9d7807b 100644
--- a/p25/Control.cpp
+++ b/p25/Control.cpp
@@ -115,6 +115,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q
m_rfLastDstId(0U),
m_netState(RS_NET_IDLE),
m_netLastDstId(0U),
+ m_permittedDstId(0U),
m_tailOnIdle(false),
m_ccRunning(false),
m_ccPrevRunning(false),
@@ -208,18 +209,19 @@ void Control::reset()
///
/// Instance of the yaml::Node class.
///
-///
+/// Voice Channel Number list.
+/// Voice Channel data map.
///
-///
-///
-///
-///
-///
-///
+/// P25 Network ID.
+/// P25 System ID.
+/// P25 RFSS ID.
+/// P25 Site ID.
+/// Channel ID.
+/// Channel Number.
///
-void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo,
- uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId,
- uint32_t channelNo, bool printOptions)
+void Control::setOptions(yaml::Node& conf, 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"];
@@ -309,6 +311,8 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
m_affiliations.addRFCh(*it);
}
+ m_affiliations.setRFChData(voiceChData);
+
uint32_t ccBcstInterval = p25Protocol["control"]["interval"].as(300U);
m_trunk->m_adjSiteUpdateInterval += ccBcstInterval;
@@ -760,7 +764,11 @@ void Control::clock(uint32_t ms)
///
void Control::permittedTG(uint32_t dstId)
{
- // TODO TODO
+ if (!m_authoritative) {
+ return;
+ }
+
+ m_permittedDstId = dstId;
}
///
diff --git a/p25/Control.h b/p25/Control.h
index b19b2da2..944cef30 100644
--- a/p25/Control.h
+++ b/p25/Control.h
@@ -50,6 +50,8 @@
#include "yaml/Yaml.h"
#include
+#include
+#include
namespace p25
{
@@ -83,9 +85,9 @@ namespace p25
void reset();
/// Helper to set P25 configuration options.
- void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo,
- uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId,
- uint8_t channelId, uint32_t channelNo, bool printOptions);
+ void setOptions(yaml::Node& conf, 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);
/// Gets a flag indicating whether the P25 control channel is running.
bool getCCRunning() { return m_ccRunning; }
@@ -178,6 +180,8 @@ namespace p25
RPT_NET_STATE m_netState;
uint32_t m_netLastDstId;
+ uint32_t m_permittedDstId;
+
bool m_tailOnIdle;
bool m_ccRunning;
bool m_ccPrevRunning;
diff --git a/p25/packet/Trunk.cpp b/p25/packet/Trunk.cpp
index 6187dad1..88069c4b 100644
--- a/p25/packet/Trunk.cpp
+++ b/p25/packet/Trunk.cpp
@@ -219,11 +219,15 @@ bool Trunk::process(uint8_t* data, uint32_t len, lc::TSBK* preDecodedTSBK)
LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Request), srcId = %u, dstId = %u", srcId, dstId);
}
- uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
- (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
- (tsbk->getPriority() & 0x07U); // Priority
+ if (m_p25->m_authoritative) {
+ uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
+ (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
+ (tsbk->getPriority() & 0x07U); // Priority
- writeRF_TSDU_Grant(srcId, dstId, serviceOptions, true);
+ writeRF_TSDU_Grant(srcId, dstId, serviceOptions, true);
+ } else {
+ m_network->writeGrantReq(modem::DVM_STATE::STATE_P25, srcId, dstId, 0U, false);
+ }
}
break;
case TSBK_IOSP_UU_VCH:
@@ -248,11 +252,15 @@ bool Trunk::process(uint8_t* data, uint32_t len, lc::TSBK* preDecodedTSBK)
writeRF_TSDU_UU_Ans_Req(srcId, dstId);
}
else {
- uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
- (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
- (tsbk->getPriority() & 0x07U); // Priority
+ if (m_p25->m_authoritative) {
+ uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
+ (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
+ (tsbk->getPriority() & 0x07U); // Priority
- writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false);
+ writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false);
+ } else {
+ m_network->writeGrantReq(modem::DVM_STATE::STATE_P25, srcId, dstId, 0U, true);
+ }
}
}
break;
@@ -278,11 +286,15 @@ bool Trunk::process(uint8_t* data, uint32_t len, lc::TSBK* preDecodedTSBK)
writeRF_TSDU_ACK_FNE(dstId, TSBK_IOSP_UU_ANS, false, true);
}
- uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
- (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
- (tsbk->getPriority() & 0x07U); // Priority
+ if (m_p25->m_authoritative) {
+ uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
+ (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
+ (tsbk->getPriority() & 0x07U); // Priority
- writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false);
+ writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false);
+ } else {
+ m_network->writeGrantReq(modem::DVM_STATE::STATE_P25, srcId, dstId, 0U, true);
+ }
}
else if (iosp->getResponse() == P25_ANS_RSP_DENY) {
writeRF_TSDU_Deny(P25_WUID_FNE, srcId, P25_DENY_RSN_TGT_UNIT_REFUSED, TSBK_IOSP_UU_ANS);