add support for network announcement of unit registration, deregistration and group affiliation so the FNE can collate the data more accurately; implement REST API on dvmfne to support retreiving the list of known affiliations; correct a bug in enumeration of RID and TGID lists that could cause weirdness; remote unnecessary "control" RTP payload type and just use a singluar payload type;

pull/48/head
Bryan Biedenkapp 2 years ago
parent 3e5fe8b349
commit 5b5efcbe9d

@ -120,6 +120,16 @@ bool AffiliationLookup::isUnitReg(uint32_t srcId) const
}
}
/// <summary>
/// Helper to release unit registrations.
/// </summary>
void AffiliationLookup::clearUnitReg()
{
std::vector<uint32_t> srcToRel = std::vector<uint32_t>();
LogWarning(LOG_HOST, "%s, releasing all unit registrations", m_name);
m_unitRegTable.clear();
}
/// <summary>
/// Helper to group affiliate a source ID.
/// </summary>
@ -220,6 +230,10 @@ std::vector<uint32_t> AffiliationLookup::clearGroupAff(uint32_t dstId, bool rele
}
}
for (auto srcId : srcToRel) {
m_grpAffTable.erase(srcId);
}
return srcToRel;
}

@ -114,6 +114,8 @@ namespace lookups
virtual bool unitDereg(uint32_t srcId);
/// <summary>Helper to determine if the source ID has unit registered.</summary>
virtual bool isUnitReg(uint32_t srcId) const;
/// <summary>Helper to release unit registrations.</summary>
virtual void clearUnitReg();
/// <summary>Gets the count of affiliations.</summary>
uint8_t grpAffSize() const { return m_grpAffTable.size(); }

@ -172,6 +172,81 @@ bool BaseNetwork::writeDiagLog(const char* message)
0U, 0U);
}
/// <summary>
/// Writes a group affiliation to the network.
/// </summary>
/// <param name="srcId"></param>
/// <param name="dstId"></param>
bool BaseNetwork::announceGroupAffiliation(uint32_t srcId, uint32_t dstId)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false;
uint8_t buffer[DATA_PACKET_LENGTH];
__SET_UINT16(srcId, buffer, 0U);
__SET_UINT16(dstId, buffer, 3U);
return writeMaster({ NET_FUNC_ANNOUNCE, NET_ANNC_SUBFUNC_GRP_AFFIL }, buffer, MSG_ANNC_GRP_AFFIL, 0U, 0U);
}
/// <summary>
/// Writes a unit registration to the network.
/// </summary>
/// <param name="srcId"></param>
bool BaseNetwork::announceUnitRegistration(uint32_t srcId)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false;
uint8_t buffer[DATA_PACKET_LENGTH];
__SET_UINT16(srcId, buffer, 0U);
return writeMaster({ NET_FUNC_ANNOUNCE, NET_ANNC_SUBFUNC_UNIT_REG }, buffer, MSG_ANNC_UNIT_REG, 0U, 0U);
}
/// <summary>
/// Writes a unit deregistration to the network.
/// </summary>
/// <param name="srcId"></param>
bool BaseNetwork::announceUnitDeregistration(uint32_t srcId)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false;
uint8_t buffer[DATA_PACKET_LENGTH];
__SET_UINT16(srcId, buffer, 0U);
return writeMaster({ NET_FUNC_ANNOUNCE, NET_ANNC_SUBFUNC_UNIT_DEREG }, buffer, MSG_ANNC_UNIT_REG, 0U, 0U);
}
/// <summary>
/// Writes a complete update of the peer affiliation list to the network.
/// </summary>
/// <param name="affs"></param>
bool BaseNetwork::announceAffiliationUpdate(const std::unordered_map<uint32_t, uint32_t> affs)
{
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false;
uint8_t buffer[4U + (affs.size() * 8U)];
::memset(buffer, 0x00U, 4U + (affs.size() * 8U));
__SET_UINT32(affs.size(), buffer, 0U);
// write talkgroup IDs to active TGID payload
uint32_t offs = 4U;
for (auto it : affs) {
__SET_UINT16(it.first, buffer, offs);
__SET_UINT16(it.second, buffer, offs + 4U);
offs += 8U;
}
return writeMaster({ NET_FUNC_ANNOUNCE, NET_ANNC_SUBFUNC_AFFILS }, buffer, 4U + (affs.size() * 8U), 0U, 0U);
}
/// <summary>
/// Resets the DMR ring buffer for the given slot.
/// </summary>

@ -30,6 +30,7 @@
#include <string>
#include <cstdint>
#include <random>
#include <unordered_map>
// ---------------------------------------------------------------------------
// Constants
@ -52,6 +53,8 @@
#define TAG_TRANSFER_ACT_LOG "TRNSLOG"
#define TAG_TRANSFER_DIAG_LOG "TRNSDIAG"
#define TAG_ANNOUNCE "ANNC"
namespace network
{
// ---------------------------------------------------------------------------
@ -61,6 +64,8 @@ namespace network
const uint32_t PACKET_PAD = 8U;
const uint32_t MSG_HDR_SIZE = 24U;
const uint32_t MSG_ANNC_GRP_AFFIL = 6U;
const uint32_t MSG_ANNC_UNIT_REG = 3U;
const uint32_t DMR_PACKET_LENGTH = 55U; // 20 byte header + DMR_FRAME_LENGTH_BYTES + 2 byte trailer
const uint32_t P25_LDU1_PACKET_LENGTH = 193U; // 24 byte header + DFSI data + 1 byte frame type + 12 byte enc sync
const uint32_t P25_LDU2_PACKET_LENGTH = 181U; // 24 byte header + DFSI data + 1 byte frame type
@ -98,6 +103,12 @@ namespace network
const uint8_t NET_TRANSFER_SUBFUNC_ACTIVITY = 0x01U; // Activity Log Transfer
const uint8_t NET_TRANSFER_SUBFUNC_DIAG = 0x02U; // Diagnostic Log Transfer
const uint8_t NET_FUNC_ANNOUNCE = 0x91U; // Network Announce Function
const uint8_t NET_ANNC_SUBFUNC_GRP_AFFIL = 0x00U; // Announce Group Affiliation
const uint8_t NET_ANNC_SUBFUNC_UNIT_REG = 0x01U; // Announce Unit Registration
const uint8_t NET_ANNC_SUBFUNC_UNIT_DEREG = 0x02U; // Announce Unit Deregistration
const uint8_t NET_ANNC_SUBFUNC_AFFILS = 0x90U; // Update All Affiliations
// ---------------------------------------------------------------------------
// Network Peer Connection Status
// ---------------------------------------------------------------------------
@ -144,6 +155,15 @@ namespace network
/// <summary>Writes the local diagnostic logs to the network.</summary>
virtual bool writeDiagLog(const char* message);
/// <summary>Writes a group affiliation to the network.</summary>
virtual bool announceGroupAffiliation(uint32_t srcId, uint32_t dstId);
/// <summary>Writes a unit registration to the network.</summary>
virtual bool announceUnitRegistration(uint32_t srcId);
/// <summary>Writes a unit deregistration to the network.</summary>
virtual bool announceUnitDeregistration(uint32_t srcId);
/// <summary>Writes a complete update of the peer affiliation list to the network.</summary>
virtual bool announceAffiliationUpdate(const std::unordered_map<uint32_t, uint32_t> affs);
/// <summary>Updates the timer by the passed number of milliseconds.</summary>
virtual void clock(uint32_t ms) = 0;

@ -91,8 +91,7 @@ UInt8Array FrameQueue::read(int& messageLength, sockaddr_storage& address, uint3
}
// ensure payload type is correct
if ((_rtpHeader.getPayloadType() != DVM_RTP_PAYLOAD_TYPE) &&
(_rtpHeader.getPayloadType() != DVM_CTRL_RTP_PAYLOAD_TYPE)) {
if (_rtpHeader.getPayloadType() != DVM_RTP_PAYLOAD_TYPE) {
LogError(LOG_NET, "FrameQueue::read(), invalid RTP payload type received from network");
return nullptr;
}
@ -194,12 +193,6 @@ void FrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, uint32_
header.setSequence(rtpSeq);
header.setSSRC(ssrc);
// properly flag control opcodes
if ((opcode.first == NET_FUNC_TRANSFER) || (opcode.first == NET_FUNC_GRANT_REQ)) {
header.setPayloadType(DVM_CTRL_RTP_PAYLOAD_TYPE);
header.setSequence(0U);
}
header.encode(buffer);
if (streamId != 0U && timestamp == INVALID_TS && rtpSeq != RTP_END_OF_CALL_SEQ) {

@ -27,7 +27,6 @@ namespace network
// ---------------------------------------------------------------------------
const uint8_t DVM_RTP_PAYLOAD_TYPE = 0x56U;
const uint8_t DVM_CTRL_RTP_PAYLOAD_TYPE = 0x57U; // these are still RTP, but do not carry stream IDs or sequence data
// ---------------------------------------------------------------------------
// Class Declaration

@ -7,7 +7,7 @@
* @package DVM / Converged FNE Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
*
*/
#include "fne/Defines.h"
@ -71,6 +71,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port,
m_tidLookup(nullptr),
m_status(NET_STAT_INVALID),
m_peers(),
m_peerAffiliations(),
m_maintainenceTimer(1000U, pingTime),
m_updateLookupTimer(1000U, (updateLookupTime * 60U)),
m_forceListUpdate(false),
@ -153,6 +154,8 @@ void FNENetwork::clock(uint32_t ms)
if (connection != nullptr) {
delete connection;
}
erasePeerAffiliations(peerId);
}
// roll the RTP timestamp if no call is in progress
@ -319,9 +322,7 @@ void FNENetwork::clock(uint32_t ms)
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
if (connection->connectionState() != NET_STAT_RUNNING) {
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
if (erasePeer(peerId)) {
delete connection;
}
}
@ -380,10 +381,7 @@ void FNENetwork::clock(uint32_t ms)
else {
LogWarning(LOG_NET, "PEER %u has failed the login exchange", peerId);
writePeerNAK(peerId, TAG_REPEATER_AUTH);
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
}
erasePeer(peerId);
}
m_peers[peerId] = connection;
@ -391,10 +389,7 @@ void FNENetwork::clock(uint32_t ms)
else {
LogWarning(LOG_NET, "PEER %u tried login exchange while in an incorrect state?", peerId);
writePeerNAK(peerId, TAG_REPEATER_AUTH);
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
}
erasePeer(peerId);
}
}
}
@ -422,20 +417,14 @@ void FNENetwork::clock(uint32_t ms)
if (!err.empty()) {
LogWarning(LOG_NET, "PEER %u has supplied invalid configuration data", peerId);
writePeerNAK(peerId, TAG_REPEATER_AUTH);
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
}
erasePeer(peerId);
}
else {
// ensure parsed JSON is an object
if (!v.is<json::object>()) {
LogWarning(LOG_NET, "PEER %u has supplied invalid configuration data", peerId);
writePeerNAK(peerId, TAG_REPEATER_AUTH);
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
}
erasePeer(peerId);
}
else {
connection->config(v.get<json::object>());
@ -462,16 +451,18 @@ void FNENetwork::clock(uint32_t ms)
std::string software = peerConfig["software"].get<std::string>();
LogInfoEx(LOG_NET, "PEER %u reports software %s", peerId, software.c_str());
}
// setup the affiliations list for this peer
std::stringstream peerName;
peerName << "PEER " << peerId;
m_peerAffiliations[peerId] = new lookups::AffiliationLookup(peerName.str().c_str(), m_verbose);
}
}
}
else {
LogWarning(LOG_NET, "PEER %u tried login exchange while in an incorrect state?", peerId);
writePeerNAK(peerId, TAG_REPEATER_CONFIG);
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
}
erasePeer(peerId);
}
}
}
@ -491,10 +482,8 @@ void FNENetwork::clock(uint32_t ms)
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
LogInfoEx(LOG_NET, "PEER %u is closing down", peerId);
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
if (erasePeer(peerId)) {
erasePeerAffiliations(peerId);
delete connection;
}
}
@ -609,6 +598,99 @@ void FNENetwork::clock(uint32_t ms)
}
break;
case NET_FUNC_ANNOUNCE:
{
if (fneHeader.getSubFunction() == NET_ANNC_SUBFUNC_GRP_AFFIL) { // Announce Group Affiliation
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
uint32_t srcId = __GET_UINT16(buffer.get(), 0U);
uint32_t dstId = __GET_UINT16(buffer.get(), 3U);
aff->groupUnaff(srcId);
aff->groupAff(srcId, dstId);
}
else {
writePeerNAK(peerId, TAG_ANNOUNCE);
}
}
}
}
else if (fneHeader.getSubFunction() == NET_ANNC_SUBFUNC_UNIT_REG) { // Announce Unit Registration
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
uint32_t srcId = __GET_UINT16(buffer.get(), 0U);
aff->unitReg(srcId);
}
else {
writePeerNAK(peerId, TAG_ANNOUNCE);
}
}
}
}
else if (fneHeader.getSubFunction() == NET_ANNC_SUBFUNC_UNIT_DEREG) { // Announce Unit Deregistration
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
uint32_t srcId = __GET_UINT16(buffer.get(), 0U);
aff->unitDereg(srcId);
}
else {
writePeerNAK(peerId, TAG_ANNOUNCE);
}
}
}
}
else if (fneHeader.getSubFunction() == NET_ANNC_SUBFUNC_AFFILS) { // Announce Update All Affiliations
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
aff->clearGroupAff(0U, true);
// update TGID lists
uint32_t len = __GET_UINT32(buffer.get(), 0U);
uint32_t offs = 4U;
for (uint32_t i = 0; i < len; i++) {
uint32_t srcId = __GET_UINT16(buffer.get(), offs);
uint32_t dstId = __GET_UINT16(buffer.get(), offs + 4U);
aff->groupAff(srcId, dstId);
offs += 8U;
}
LogMessage(LOG_NET, "PEER %u announced %u affiliations", peerId, len);
}
else {
writePeerNAK(peerId, TAG_ANNOUNCE);
}
}
}
}
else {
Utils::dump("Unknown transfer opcode from the peer", buffer.get(), length);
}
}
break;
default:
Utils::dump("Unknown opcode from the peer", buffer.get(), length);
break;
@ -691,6 +773,41 @@ void FNENetwork::close()
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Helper to erase the peer from the peers affiliations list.
/// </summary>
/// <param name="peerId"></param>
/// <returns></returns>
bool FNENetwork::erasePeerAffiliations(uint32_t peerId)
{
auto it = std::find_if(m_peerAffiliations.begin(), m_peerAffiliations.end(), [&](PeerAffiliationMapPair x) { return x.first == peerId; });
if (it != m_peerAffiliations.end()) {
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
m_peerAffiliations.erase(peerId);
delete aff;
return true;
}
return false;
}
/// <summary>
/// Helper to erase the peer from the peers list.
/// </summary>
/// <param name="peerId"></param>
/// <returns></returns>
bool FNENetwork::erasePeer(uint32_t peerId)
{
auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; });
if (it != m_peers.end()) {
m_peers.erase(peerId);
return true;
}
return false;
}
/// <summary>
/// Helper to send the list of whitelisted RIDs to the specified peer.
/// </summary>

@ -7,7 +7,7 @@
* @package DVM / Converged FNE Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__FNE_NETWORK_H__)
@ -16,6 +16,7 @@
#include "fne/Defines.h"
#include "common/network/BaseNetwork.h"
#include "common/network/json/json.h"
#include "common/lookups/AffiliationLookup.h"
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "host/network/Network.h"
@ -199,6 +200,8 @@ namespace network
typedef std::pair<const uint32_t, network::FNEPeerConnection*> PeerMapPair;
std::unordered_map<uint32_t, FNEPeerConnection*> m_peers;
typedef std::pair<const uint32_t, lookups::AffiliationLookup*> PeerAffiliationMapPair;
std::unordered_map<uint32_t, lookups::AffiliationLookup*> m_peerAffiliations;
Timer m_maintainenceTimer;
Timer m_updateLookupTimer;
@ -208,6 +211,11 @@ namespace network
bool m_verbose;
/// <summary>Helper to erase the peer from the peers affiliations list.</summary>
bool erasePeerAffiliations(uint32_t peerId);
/// <summary>Helper to erase the peer from the peers list.</summary>
bool erasePeer(uint32_t peerId);
/// <summary>Helper to send the list of whitelisted RIDs to the specified peer.</summary>
void writeWhitelistRIDs(uint32_t peerId, bool queueOnly = false);
/// <summary>Helper to send the list of whitelisted RIDs to connected peers.</summary>

@ -242,6 +242,8 @@ void RESTAPI::initializeEndpoints()
m_dispatcher.match(FNE_GET_TGID_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetTGIDList, this));
m_dispatcher.match(FNE_GET_FORCE_UPDATE).get(REST_API_BIND(RESTAPI::restAPI_GetForceUpdate, this));
m_dispatcher.match(FNE_GET_AFF_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetAffList, this));
}
/// <summary>
@ -586,3 +588,56 @@ void RESTAPI::restAPI_GetForceUpdate(const HTTPPayload& request, HTTPPayload& re
reply.payload(response);
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="reply"></param>
/// <param name="match"></param>
void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object response = json::object();
setResponseDefaultStatus(response);
json::array affs = json::array();
if (m_network != nullptr) {
if (m_network->m_peers.size() > 0) {
for (auto entry : m_network->m_peers) {
uint32_t peerId = entry.first;
network::FNEPeerConnection* peer = entry.second;
if (peer != nullptr) {
lookups::AffiliationLookup* affLookup = m_network->m_peerAffiliations[peerId];
std::unordered_map<uint32_t, uint32_t> affTable = affLookup->grpAffTable();
json::object peerObj = json::object();
peerObj["peerId"].set<uint32_t>(peerId);
json::array peerAffs = json::array();
if (affLookup->grpAffSize() > 0U) {
for (auto entry : affTable) {
uint32_t srcId = entry.first;
uint32_t dstId = entry.second;
json::object affObj = json::object();
affObj["srcId"].set<uint32_t>(srcId);
affObj["dstId"].set<uint32_t>(dstId);
peerAffs.push_back(json::value(affObj));
}
}
peerObj["affiliations"].set<json::array>(peerAffs);
affs.push_back(json::value(peerObj));
}
}
}
}
response["affiliations"].set<json::array>(affs);
reply.payload(response);
}

@ -102,6 +102,9 @@ private:
/// <summary></summary>
void restAPI_GetForceUpdate(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
};
#endif // __REST_API_H__

@ -25,4 +25,6 @@
#define FNE_GET_FORCE_UPDATE "/force-update"
#define FNE_GET_AFF_LIST "/report-affiliations"
#endif // __FNE_REST_DEFINES_H__

@ -125,6 +125,7 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_netTimeoutTimer(1000U, timeout),
m_netTGHang(1000U, 2U),
m_packetTimer(1000U, 0U, 50U),
m_adjSiteUpdate(1000U, 75U),
m_ccPacketInterval(1000U, 0U, DMR_SLOT_TIME),
m_interval(),
m_elapsed(),
@ -473,6 +474,19 @@ void Slot::clock()
}
}
// do we need to network announce ourselves?
if (!m_adjSiteUpdate.isRunning()) {
m_adjSiteUpdate.start();
}
m_adjSiteUpdate.clock(ms);
if (m_adjSiteUpdate.isRunning() && m_adjSiteUpdate.hasExpired()) {
if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) {
m_network->announceAffiliationUpdate(m_affiliations->grpAffTable());
m_adjSiteUpdate.start();
}
}
if (m_ccPrevRunning && !m_ccRunning) {
m_txQueue.clear(); // clear the frame buffer
m_ccPrevRunning = m_ccRunning;

@ -9,7 +9,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__DMR_SLOT_H__)
@ -164,6 +164,8 @@ namespace dmr
Timer m_netTGHang;
Timer m_packetTimer;
Timer m_adjSiteUpdate;
Timer m_ccPacketInterval;
StopWatch m_interval;

@ -1275,6 +1275,8 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt
// remove dynamic unit registration table entry
m_slot->m_affiliations->unitDereg(srcId);
m_slot->m_network->announceUnitDeregistration(srcId);
csbk->setReason(TS_ACK_RSN_REG);
}
else
@ -1299,6 +1301,8 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt
if (!m_slot->m_affiliations->isUnitReg(srcId)) {
m_slot->m_affiliations->unitReg(srcId);
}
m_slot->m_network->announceUnitRegistration(srcId);
}
}

@ -389,12 +389,12 @@ void Network::clock(uint32_t ms)
if (m_ridLookup != nullptr) {
// update RID lists
uint32_t len = __GET_UINT16(buffer, 7U);
uint32_t j = 0U;
for (uint8_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, 11U + j);
uint32_t len = __GET_UINT32(buffer, 6U);
uint32_t offs = 11U;
for (uint32_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, offs);
m_ridLookup->toggleEntry(id, true);
j += 4U;
offs += 4U;
}
}
}
@ -406,12 +406,12 @@ void Network::clock(uint32_t ms)
if (m_ridLookup != nullptr) {
// update RID lists
uint32_t len = __GET_UINT16(buffer, 7U);
uint32_t j = 0U;
for (uint8_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, 11U + j);
uint32_t len = __GET_UINT32(buffer, 6U);
uint32_t offs = 11U;
for (uint32_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, offs);
m_ridLookup->toggleEntry(id, false);
j += 4U;
offs += 4U;
}
}
}
@ -423,11 +423,11 @@ void Network::clock(uint32_t ms)
if (m_tidLookup != nullptr) {
// update TGID lists
uint32_t len = __GET_UINT16(buffer, 7U);
uint32_t j = 0U;
for (uint8_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, 11U + j);
uint8_t slot = (buffer[14U + j]);
uint32_t len = __GET_UINT32(buffer, 6U);
uint32_t offs = 11U;
for (uint32_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, offs);
uint8_t slot = (buffer[offs + 3U]);
lookups::TalkgroupRuleGroupVoice tid = m_tidLookup->find(id, slot);
if (tid.isInvalid()) {
@ -439,7 +439,7 @@ void Network::clock(uint32_t ms)
m_tidLookup->addEntry(id, slot, true);
}
j += 5U;
offs += 5U;
}
LogMessage(LOG_NET, "Activated %u TGs; loaded %u entries into lookup table", len, m_tidLookup->groupVoice().size());
}
@ -452,11 +452,11 @@ void Network::clock(uint32_t ms)
if (m_tidLookup != nullptr) {
// update TGID lists
uint32_t len = __GET_UINT16(buffer, 7U);
uint32_t j = 0U;
for (uint8_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, 11U + j);
uint8_t slot = (buffer[14U + j]);
uint32_t len = __GET_UINT32(buffer, 6U);
uint32_t offs = 11U;
for (uint32_t i = 0; i < len; i++) {
uint32_t id = __GET_UINT16(buffer, offs);
uint8_t slot = (buffer[offs + 3U]);
lookups::TalkgroupRuleGroupVoice tid = m_tidLookup->find(id, slot);
if (!tid.isInvalid()) {
@ -464,7 +464,7 @@ void Network::clock(uint32_t ms)
m_tidLookup->eraseEntry(id, slot);
}
j += 5U;
offs += 5U;
}
LogMessage(LOG_NET, "Deactivated %u TGs; loaded %u entries into lookup table", len, m_tidLookup->groupVoice().size());
}

@ -7,7 +7,7 @@
* @package DVM / Modem Host Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -268,8 +268,6 @@ void RESTAPI::initializeEndpoints()
m_dispatcher.match(GET_RID_WHITELIST, true).get(REST_API_BIND(RESTAPI::restAPI_GetRIDWhitelist, this));
m_dispatcher.match(GET_RID_BLACKLIST, true).get(REST_API_BIND(RESTAPI::restAPI_GetRIDBlacklist, this));
m_dispatcher.match(GET_AFF_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetAffList, this));
/*
** Digital Mobile Radio
*/
@ -281,6 +279,7 @@ void RESTAPI::initializeEndpoints()
m_dispatcher.match(GET_DMR_CC_DEDICATED).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCEnable, this));
m_dispatcher.match(GET_DMR_CC_BCAST).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCBroadcast, this));
m_dispatcher.match(PUT_DMR_TSCC_PAYLOAD_ACT).put(REST_API_BIND(RESTAPI::restAPI_PutTSCCPayloadActivate, this));
m_dispatcher.match(GET_DMR_AFFILIATIONS).get(REST_API_BIND(RESTAPI::restAPI_GetDMRAffList, this));
/*
** Project 25
@ -293,6 +292,7 @@ void RESTAPI::initializeEndpoints()
m_dispatcher.match(GET_P25_CC_DEDICATED).get(REST_API_BIND(RESTAPI::restAPI_GetP25CCEnable, this));
m_dispatcher.match(GET_P25_CC_BCAST).get(REST_API_BIND(RESTAPI::restAPI_GetP25CCBroadcast, this));
m_dispatcher.match(PUT_P25_RAW_TSBK).put(REST_API_BIND(RESTAPI::restAPI_PutP25RawTSBK, this));
m_dispatcher.match(GET_P25_AFFILIATIONS).get(REST_API_BIND(RESTAPI::restAPI_GetP25AffList, this));
/*
** Next Generation Digital Narrowband
@ -302,6 +302,7 @@ void RESTAPI::initializeEndpoints()
m_dispatcher.match(GET_NXDN_DEBUG).get(REST_API_BIND(RESTAPI::restAPI_GetNXDNDebug, this));
m_dispatcher.match(GET_NXDN_DUMP_RCCH).get(REST_API_BIND(RESTAPI::restAPI_GetNXDNDumpRCCH, this));
m_dispatcher.match(GET_NXDN_CC_DEDICATED).get(REST_API_BIND(RESTAPI::restAPI_GetNXDNCCEnable, this));
m_dispatcher.match(GET_NXDN_AFFILIATIONS).get(REST_API_BIND(RESTAPI::restAPI_GetNXDNAffList, this));
}
/// <summary>
@ -1350,83 +1351,6 @@ void RESTAPI::restAPI_GetRIDBlacklist(const HTTPPayload& request, HTTPPayload& r
}
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="reply"></param>
/// <param name="match"></param>
void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object response = json::object();
setResponseDefaultStatus(response);
std::unordered_map<uint32_t, uint32_t> globalAffTable = std::unordered_map<uint32_t, uint32_t>();
if (m_dmr != nullptr) {
std::unordered_map<uint32_t, uint32_t> affTable = m_dmr->affiliations().grpAffTable();
for (auto entry : affTable) {
uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
// did we already catalog this affiliation?
auto globEntry = globalAffTable.find(srcId);
if (globEntry == globalAffTable.end()) {
globalAffTable[srcId] = grpId;
}
}
}
if (m_p25 != nullptr) {
std::unordered_map<uint32_t, uint32_t> affTable = m_p25->affiliations().grpAffTable();
for (auto entry : affTable) {
uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
// did we already catalog this affiliation?
auto globEntry = globalAffTable.find(srcId);
if (globEntry == globalAffTable.end()) {
globalAffTable[srcId] = grpId;
}
}
}
if (m_nxdn != nullptr) {
std::unordered_map<uint32_t, uint32_t> affTable = m_nxdn->affiliations().grpAffTable();
for (auto entry : affTable) {
uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
// did we already catalog this affiliation?
auto globEntry = globalAffTable.find(srcId);
if (globEntry == globalAffTable.end()) {
globalAffTable[srcId] = grpId;
}
}
}
json::array affs = json::array();
if (globalAffTable.size() > 0) {
for (auto entry : globalAffTable) {
uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
json::object aff = json::object();
aff["srcId"].set<uint32_t>(srcId);
aff["grpId"].set<uint32_t>(grpId);
affs.push_back(json::value(aff));
}
}
response["affiliations"].set<json::array>(affs);
reply.payload(response);
}
/*
** Digital Mobile Radio
*/
@ -1756,6 +1680,40 @@ void RESTAPI::restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPay
}
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="reply"></param>
/// <param name="match"></param>
void RESTAPI::restAPI_GetDMRAffList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object response = json::object();
setResponseDefaultStatus(response);
json::array affs = json::array();
std::unordered_map<uint32_t, uint32_t> affTable = m_dmr->affiliations().grpAffTable();
if (affTable.size() > 0) {
for (auto entry : affTable) {
uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
json::object aff = json::object();
aff["srcId"].set<uint32_t>(srcId);
aff["grpId"].set<uint32_t>(grpId);
affs.push_back(json::value(aff));
}
}
response["affiliations"].set<json::array>(affs);
reply.payload(response);
}
/*
** Project 25
*/
@ -2102,6 +2060,40 @@ void RESTAPI::restAPI_PutP25RawTSBK(const HTTPPayload& request, HTTPPayload& rep
m_p25->control()->writeRF_TSDU_Raw(tsbk);
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="reply"></param>
/// <param name="match"></param>
void RESTAPI::restAPI_GetP25AffList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object response = json::object();
setResponseDefaultStatus(response);
json::array affs = json::array();
std::unordered_map<uint32_t, uint32_t> affTable = m_p25->affiliations().grpAffTable();
if (affTable.size() > 0) {
for (auto entry : affTable) {
uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
json::object aff = json::object();
aff["srcId"].set<uint32_t>(srcId);
aff["grpId"].set<uint32_t>(grpId);
affs.push_back(json::value(aff));
}
}
response["affiliations"].set<json::array>(affs);
reply.payload(response);
}
/*
** Next Generation Digital Narrowband
*/
@ -2254,3 +2246,37 @@ void RESTAPI::restAPI_GetNXDNCCEnable(const HTTPPayload& request, HTTPPayload& r
return;
}
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="reply"></param>
/// <param name="match"></param>
void RESTAPI::restAPI_GetNXDNAffList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object response = json::object();
setResponseDefaultStatus(response);
json::array affs = json::array();
std::unordered_map<uint32_t, uint32_t> affTable = m_nxdn->affiliations().grpAffTable();
if (affTable.size() > 0) {
for (auto entry : affTable) {
uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
json::object aff = json::object();
aff["srcId"].set<uint32_t>(srcId);
aff["grpId"].set<uint32_t>(grpId);
affs.push_back(json::value(aff));
}
}
response["affiliations"].set<json::array>(affs);
reply.payload(response);
}

@ -7,7 +7,7 @@
* @package DVM / Modem Host Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__REST_API_H__)
@ -130,9 +130,6 @@ private:
/// <summary></summary>
void restAPI_GetRIDBlacklist(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/*
** Digital Mobile Radio
*/
@ -151,6 +148,8 @@ private:
void restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetDMRAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/*
** Project 25
@ -170,6 +169,8 @@ private:
void restAPI_GetP25CCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutP25RawTSBK(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetP25AffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/*
** Next Generation Digital Narrowband
@ -183,6 +184,8 @@ private:
void restAPI_GetNXDNDumpRCCH(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetNXDNCCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetNXDNAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
};
#endif // __REST_API_H__

@ -7,7 +7,7 @@
* @package DVM / Modem Host Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__REST_DEFINES_H__)
@ -59,8 +59,6 @@
#define GET_RID_BLACKLIST_BASE "/rid-blacklist/"
#define GET_RID_BLACKLIST GET_RID_BLACKLIST_BASE"(\\d+)"
#define GET_AFF_LIST "/affs"
#define GET_DMR_BEACON "/dmr/beacon"
#define GET_DMR_DEBUG_BASE "/dmr/debug/"
#define GET_DMR_DEBUG GET_DMR_DEBUG_BASE"(\\d+)/(\\d+)"
@ -70,6 +68,7 @@
#define GET_DMR_CC_DEDICATED "/dmr/cc-enable"
#define GET_DMR_CC_BCAST "/dmr/cc-broadcast"
#define PUT_DMR_TSCC_PAYLOAD_ACT "/dmr/payload-activate"
#define GET_DMR_AFFILIATIONS "/dmr/report-affiliations"
#define GET_P25_CC "/p25/cc"
#define GET_P25_CC_FALLBACK_BASE "/p25/cc-fallback/"
@ -82,6 +81,7 @@
#define GET_P25_CC_DEDICATED "/p25/cc-enable"
#define GET_P25_CC_BCAST "/p25/cc-broadcast"
#define PUT_P25_RAW_TSBK "/p25/raw-tsbk"
#define GET_P25_AFFILIATIONS "/p25/report-affiliations"
#define GET_NXDN_CC "/nxdn/cc"
#define GET_NXDN_DEBUG_BASE "/nxdn/debug/"
@ -89,5 +89,6 @@
#define GET_NXDN_DUMP_RCCH_BASE "/nxdn/dump-rcch/"
#define GET_NXDN_DUMP_RCCH GET_NXDN_DUMP_RCCH_BASE"(\\d+)"
#define GET_NXDN_CC_DEDICATED "/nxdn/cc-enable"
#define GET_NXDN_AFFILIATIONS "/nxdn/report-affiliations"
#endif // __REST_DEFINES_H__

@ -9,7 +9,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2015-2020 Jonathan Naylor, G4KLX
* Copyright (C) 2022-2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2022-2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
@ -559,6 +559,19 @@ void Control::clock(uint32_t ms)
}
}
// do we need to network announce ourselves?
if (!m_adjSiteUpdate.isRunning()) {
m_adjSiteUpdate.start();
}
m_adjSiteUpdate.clock(ms);
if (m_adjSiteUpdate.isRunning() && m_adjSiteUpdate.hasExpired()) {
if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) {
m_network->announceAffiliationUpdate(m_affiliations.grpAffTable());
m_adjSiteUpdate.start();
}
}
if (m_ccPrevRunning && !m_ccRunning) {
m_txQueue.clear();
m_ccPacketInterval.stop();

@ -9,7 +9,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2015-2020 Jonathan Naylor, G4KLX
* Copyright (C) 2022-2023 Bryan Biedenkapp, N2PLL
* Copyright (C) 2022-2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__NXDN_CONTROL_H__)
@ -179,6 +179,8 @@ namespace nxdn
Timer m_netTGHang;
Timer m_networkWatchdog;
Timer m_adjSiteUpdate;
Timer m_ccPacketInterval;
uint8_t m_frameLossCnt;

@ -728,6 +728,8 @@ bool ControlSignaling::writeRF_Message_Grp_Reg_Rsp(uint32_t srcId, uint32_t dstI
// update dynamic affiliation table
m_nxdn->m_affiliations.groupAff(srcId, dstId);
m_nxdn->m_network->announceGroupAffiliation(srcId, dstId);
}
writeRF_Message_Imm(rcch.get(), false);
@ -769,6 +771,8 @@ void ControlSignaling::writeRF_Message_U_Reg_Rsp(uint32_t srcId, uint32_t locId)
if (!m_nxdn->m_affiliations.isUnitReg(srcId)) {
m_nxdn->m_affiliations.unitReg(srcId);
}
m_nxdn->m_network->announceUnitRegistration(srcId);
}
rcch->setSrcId(srcId);

@ -819,6 +819,7 @@ void Control::clock(uint32_t ms)
if (m_adjSiteUpdate.isRunning() && m_adjSiteUpdate.hasExpired()) {
if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) {
m_control->writeAdjSSNetwork();
m_network->announceAffiliationUpdate(m_affiliations.grpAffTable());
m_adjSiteUpdate.start();
}
}

@ -2682,6 +2682,8 @@ bool ControlSignaling::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId)
// update dynamic affiliation table
m_p25->m_affiliations.groupAff(srcId, dstId);
m_p25->m_network->announceGroupAffiliation(srcId, dstId);
}
writeRF_TSDU_SBF_Imm(iosp.get(), noNet);
@ -2726,6 +2728,8 @@ void ControlSignaling::writeRF_TSDU_U_Reg_Rsp(uint32_t srcId, uint32_t sysId)
if (!m_p25->m_affiliations.isUnitReg(srcId)) {
m_p25->m_affiliations.unitReg(srcId);
}
m_p25->m_network->announceUnitRegistration(srcId);
}
writeRF_TSDU_SBF_Imm(iosp.get(), true);
@ -2760,6 +2764,8 @@ void ControlSignaling::writeRF_TSDU_U_Dereg_Ack(uint32_t srcId)
::ActivityLog("P25", true, "unit deregistration request from %u", srcId);
writeRF_TSDU_SBF_Imm(osp.get(), false);
m_p25->m_network->announceUnitDeregistration(srcId);
}
}

@ -43,6 +43,7 @@
#define RCD_FNE_GET_PEERLIST "fne-peerlist"
#define RCD_FNE_GET_TGIDLIST "fne-tgidlist"
#define RCD_FNE_GET_FORCEUPDATE "fne-force-update"
#define RCD_FNE_GET_AFFLIST "fne-affs"
#define RCD_MODE "mdm-mode"
#define RCD_MODE_OPT_IDLE "idle"
@ -64,8 +65,6 @@
#define RCD_RELEASE_AFFS "rel-affs"
#define RCD_RELEASE_AFF "rel-aff"
#define RCD_GET_AFFS "affs"
#define RCD_DMR_BEACON "dmr-beacon"
#define RCD_P25_CC "p25-cc"
#define RCD_P25_CC_FALLBACK "p25-cc-fallback"
@ -92,6 +91,10 @@
#define RCD_NXDN_CC_DEDICATED "nxdn-cc-dedicated"
#define RCD_DMR_GET_AFFLIST "dmr-affs"
#define RCD_P25_GET_AFFLIST "p25-affs"
#define RCD_NXDN_GET_AFFLIST "nxdn-affs"
#define RCD_DMR_DEBUG "dmr-debug"
#define RCD_DMR_DUMP_CSBK "dmr-dump-csbk"
#define RCD_P25_DEBUG "p25-debug"
@ -181,6 +184,7 @@ void usage(const char* message, const char* arg)
reply += " fne-peerlist Retrieves the list of connected peers (Converged FNE only)\r\n";
reply += " fne-tgidlist Retrieves the list of configured TGIDs (Converged FNE only)\r\n";
reply += " fne-force-update Forces the FNE to send list update (Converged FNE only)\r\n";
reply += " fne-affs Retrieves the list of currently affiliated SUs (Converged FNE only)\r\n";
reply += "\r\n";
reply += " mdm-mode <mode> Set current mode of host (idle, lockout, dmr, p25, nxdn)\r\n";
reply += " mdm-kill Causes the host to quit\r\n";
@ -195,7 +199,6 @@ void usage(const char* message, const char* arg)
reply += " rel-grnts Forcibly releases all channel grants\r\n";
reply += " rel-affs Forcibly releases all group affiliations\r\n";
reply += " rel-aff <state> <dstid> Forcibly releases specified group affiliations\r\n";
reply += " affs Retrieves the list of currently affiliated SUs\r\n";
reply += "\r\n";
reply += " dmr-beacon Transmits a DMR beacon burst\r\n";
reply += " p25-cc Transmits a non-continous P25 CC burst\r\n";
@ -216,6 +219,8 @@ void usage(const char* message, const char* arg)
reply += "\r\n";
reply += " dmr-cc-dedicated Enables or disables dedicated control channel\r\n";
reply += " dmr-cc-bcast Enables or disables broadcast of the control channel\r\n";
reply += "\r\n";
reply += " dmr-affs Retrieves the list of currently affiliated DMR SUs\r\n";
reply += "\r\nP25 Commands:\r\n";
reply += " p25-set-mfid <mfid> Sets the P25 MFId for the next sent P25 command\r\n";
reply += " p25-rid-page <rid> Pages/Calls the specified RID\r\n";
@ -227,8 +232,12 @@ void usage(const char* message, const char* arg)
reply += "\r\n";
reply += " p25-cc-dedicated Enables or disables dedicated control channel\r\n";
reply += " p25-cc-bcast Enables or disables broadcast of the control channel\r\n";
reply += "\r\n";
reply += " p25-affs Retrieves the list of currently affiliated P25 SUs\r\n";
reply += "\r\nNXDN Commands:\r\n";
reply += " nxdn-cc-dedicated Enables or disables dedicated control channel\r\n";
reply += "\r\n";
reply += " nxdn-affs Retrieves the list of currently affiliated NXDN SUs\r\n";
::fprintf(stdout, "\n%s\n", reply.c_str());
exit(EXIT_FAILURE);
@ -423,15 +432,6 @@ int main(int argc, char** argv)
else if (rcom == RCD_GET_VOICE_CH) {
retCode = client->send(HTTP_GET, GET_VOICE_CH, json::object(), response);
}
else if (rcom == RCD_FNE_GET_PEERLIST) {
retCode = client->send(HTTP_GET, FNE_GET_PEERLIST, json::object(), response);
}
else if (rcom == RCD_FNE_GET_TGIDLIST) {
retCode = client->send(HTTP_GET, FNE_GET_TGID_LIST, json::object(), response);
}
else if (rcom == RCD_FNE_GET_FORCEUPDATE) {
retCode = client->send(HTTP_GET, FNE_GET_FORCE_UPDATE, json::object(), response);
}
else if (rcom == RCD_MODE && argCnt >= 1U) {
std::string mode = getArgString(args, 0U);
@ -522,9 +522,6 @@ int main(int argc, char** argv)
retCode = client->send(HTTP_PUT, PUT_RELEASE_TG, req, response);
}
else if (rcom == RCD_GET_AFFS) {
retCode = client->send(HTTP_GET, GET_AFF_LIST, json::object(), response);
}
/*
** Digital Mobile Radio
@ -598,6 +595,9 @@ int main(int argc, char** argv)
else if (rcom == RCD_DMR_CC_BCAST) {
retCode = client->send(HTTP_GET, GET_DMR_CC_BCAST, json::object(), response);
}
else if (rcom == RCD_DMR_GET_AFFLIST) {
retCode = client->send(HTTP_GET, GET_DMR_AFFILIATIONS, json::object(), response);
}
/*
** Project 25
@ -687,6 +687,9 @@ int main(int argc, char** argv)
else if (rcom == RCD_P25_CC_BCAST) {
retCode = client->send(HTTP_GET, GET_P25_CC_BCAST, json::object(), response);
}
else if (rcom == RCD_P25_GET_AFFLIST) {
retCode = client->send(HTTP_GET, GET_P25_AFFILIATIONS, json::object(), response);
}
/*
** Next Generation Digital Narrowband
@ -717,6 +720,25 @@ int main(int argc, char** argv)
else if (rcom == RCD_NXDN_CC_DEDICATED) {
retCode = client->send(HTTP_GET, GET_NXDN_CC_DEDICATED, json::object(), response);
}
else if (rcom == RCD_NXDN_GET_AFFLIST) {
retCode = client->send(HTTP_GET, GET_NXDN_AFFILIATIONS, json::object(), response);
}
/*
** Fixed Network Equipment
*/
else if (rcom == RCD_FNE_GET_PEERLIST) {
retCode = client->send(HTTP_GET, FNE_GET_PEERLIST, json::object(), response);
}
else if (rcom == RCD_FNE_GET_TGIDLIST) {
retCode = client->send(HTTP_GET, FNE_GET_TGID_LIST, json::object(), response);
}
else if (rcom == RCD_FNE_GET_FORCEUPDATE) {
retCode = client->send(HTTP_GET, FNE_GET_FORCE_UPDATE, json::object(), response);
}
else if (rcom == RCD_FNE_GET_AFFLIST) {
retCode = client->send(HTTP_GET, FNE_GET_AFF_LIST, json::object(), response);
}
else {
args.clear();
LogError(LOG_REST, BAD_CMD_STR " (\"%s\")", rcom.c_str());

Loading…
Cancel
Save

Powered by TurnKey Linux.