diff --git a/src/common/lookups/AffiliationLookup.cpp b/src/common/lookups/AffiliationLookup.cpp index a9495f0a..c8e50140 100644 --- a/src/common/lookups/AffiliationLookup.cpp +++ b/src/common/lookups/AffiliationLookup.cpp @@ -120,6 +120,16 @@ bool AffiliationLookup::isUnitReg(uint32_t srcId) const } } +/// +/// Helper to release unit registrations. +/// +void AffiliationLookup::clearUnitReg() +{ + std::vector srcToRel = std::vector(); + LogWarning(LOG_HOST, "%s, releasing all unit registrations", m_name); + m_unitRegTable.clear(); +} + /// /// Helper to group affiliate a source ID. /// @@ -220,6 +230,10 @@ std::vector AffiliationLookup::clearGroupAff(uint32_t dstId, bool rele } } + for (auto srcId : srcToRel) { + m_grpAffTable.erase(srcId); + } + return srcToRel; } diff --git a/src/common/lookups/AffiliationLookup.h b/src/common/lookups/AffiliationLookup.h index f16f9ff4..38494acd 100644 --- a/src/common/lookups/AffiliationLookup.h +++ b/src/common/lookups/AffiliationLookup.h @@ -114,6 +114,8 @@ namespace lookups virtual bool unitDereg(uint32_t srcId); /// Helper to determine if the source ID has unit registered. virtual bool isUnitReg(uint32_t srcId) const; + /// Helper to release unit registrations. + virtual void clearUnitReg(); /// Gets the count of affiliations. uint8_t grpAffSize() const { return m_grpAffTable.size(); } diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index f703e9a7..0e3d3224 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -172,6 +172,81 @@ bool BaseNetwork::writeDiagLog(const char* message) 0U, 0U); } +/// +/// Writes a group affiliation to the network. +/// +/// +/// +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); +} + +/// +/// Writes a unit registration to the network. +/// +/// +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); +} + +/// +/// Writes a unit deregistration to the network. +/// +/// +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); +} + +/// +/// Writes a complete update of the peer affiliation list to the network. +/// +/// +bool BaseNetwork::announceAffiliationUpdate(const std::unordered_map 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); +} + /// /// Resets the DMR ring buffer for the given slot. /// diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index d9b76640..1f4f3500 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -30,6 +30,7 @@ #include #include #include +#include // --------------------------------------------------------------------------- // 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 /// Writes the local diagnostic logs to the network. virtual bool writeDiagLog(const char* message); + /// Writes a group affiliation to the network. + virtual bool announceGroupAffiliation(uint32_t srcId, uint32_t dstId); + /// Writes a unit registration to the network. + virtual bool announceUnitRegistration(uint32_t srcId); + /// Writes a unit deregistration to the network. + virtual bool announceUnitDeregistration(uint32_t srcId); + /// Writes a complete update of the peer affiliation list to the network. + virtual bool announceAffiliationUpdate(const std::unordered_map affs); + /// Updates the timer by the passed number of milliseconds. virtual void clock(uint32_t ms) = 0; diff --git a/src/common/network/FrameQueue.cpp b/src/common/network/FrameQueue.cpp index 940a4986..60f03755 100644 --- a/src/common/network/FrameQueue.cpp +++ b/src/common/network/FrameQueue.cpp @@ -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) { diff --git a/src/common/network/FrameQueue.h b/src/common/network/FrameQueue.h index be64a39a..e2f5ceb8 100644 --- a/src/common/network/FrameQueue.h +++ b/src/common/network/FrameQueue.h @@ -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 diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 3fa1ba65..971d9043 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -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()) { 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()); @@ -462,16 +451,18 @@ void FNENetwork::clock(uint32_t ms) std::string software = peerConfig["software"].get(); 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 // --------------------------------------------------------------------------- +/// +/// Helper to erase the peer from the peers affiliations list. +/// +/// +/// +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; +} + +/// +/// Helper to erase the peer from the peers list. +/// +/// +/// +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; +} + /// /// Helper to send the list of whitelisted RIDs to the specified peer. /// diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index d3d8276b..93dd312b 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -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 PeerMapPair; std::unordered_map m_peers; + typedef std::pair PeerAffiliationMapPair; + std::unordered_map m_peerAffiliations; Timer m_maintainenceTimer; Timer m_updateLookupTimer; @@ -208,6 +211,11 @@ namespace network bool m_verbose; + /// Helper to erase the peer from the peers affiliations list. + bool erasePeerAffiliations(uint32_t peerId); + /// Helper to erase the peer from the peers list. + bool erasePeer(uint32_t peerId); + /// Helper to send the list of whitelisted RIDs to the specified peer. void writeWhitelistRIDs(uint32_t peerId, bool queueOnly = false); /// Helper to send the list of whitelisted RIDs to connected peers. diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index 29537066..7ffb7026 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -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)); } /// @@ -586,3 +588,56 @@ void RESTAPI::restAPI_GetForceUpdate(const HTTPPayload& request, HTTPPayload& re reply.payload(response); } + + +/// +/// +/// +/// +/// +/// +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 affTable = affLookup->grpAffTable(); + + json::object peerObj = json::object(); + peerObj["peerId"].set(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(srcId); + affObj["dstId"].set(dstId); + peerAffs.push_back(json::value(affObj)); + } + } + + peerObj["affiliations"].set(peerAffs); + affs.push_back(json::value(peerObj)); + } + } + } + } + + response["affiliations"].set(affs); + reply.payload(response); +} diff --git a/src/fne/network/RESTAPI.h b/src/fne/network/RESTAPI.h index d0b99674..b09cd821 100644 --- a/src/fne/network/RESTAPI.h +++ b/src/fne/network/RESTAPI.h @@ -102,6 +102,9 @@ private: /// void restAPI_GetForceUpdate(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + + /// + void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); }; #endif // __REST_API_H__ diff --git a/src/fne/network/RESTDefines.h b/src/fne/network/RESTDefines.h index 9f0228e1..c9c9d393 100644 --- a/src/fne/network/RESTDefines.h +++ b/src/fne/network/RESTDefines.h @@ -25,4 +25,6 @@ #define FNE_GET_FORCE_UPDATE "/force-update" +#define FNE_GET_AFF_LIST "/report-affiliations" + #endif // __FNE_REST_DEFINES_H__ diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 34deb870..e20ac161 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -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; diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 44b6ef1d..1dfa7428 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -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; diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index 9aa8a797..b826f05e 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -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); } } diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index e7b3b6f3..8060af4e 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -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()); } diff --git a/src/host/network/RESTAPI.cpp b/src/host/network/RESTAPI.cpp index cc661fe0..42558f33 100644 --- a/src/host/network/RESTAPI.cpp +++ b/src/host/network/RESTAPI.cpp @@ -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)); } /// @@ -1350,83 +1351,6 @@ void RESTAPI::restAPI_GetRIDBlacklist(const HTTPPayload& request, HTTPPayload& r } } -/// -/// -/// -/// -/// -/// -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 globalAffTable = std::unordered_map(); - - if (m_dmr != nullptr) { - std::unordered_map 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 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 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(srcId); - aff["grpId"].set(grpId); - - affs.push_back(json::value(aff)); - } - } - - response["affiliations"].set(affs); - reply.payload(response); -} - /* ** Digital Mobile Radio */ @@ -1756,6 +1680,40 @@ void RESTAPI::restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPay } } +/// +/// +/// +/// +/// +/// +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 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(srcId); + aff["grpId"].set(grpId); + + affs.push_back(json::value(aff)); + } + } + + response["affiliations"].set(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); } +/// +/// +/// +/// +/// +/// +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 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(srcId); + aff["grpId"].set(grpId); + + affs.push_back(json::value(aff)); + } + } + + response["affiliations"].set(affs); + reply.payload(response); +} + /* ** Next Generation Digital Narrowband */ @@ -2254,3 +2246,37 @@ void RESTAPI::restAPI_GetNXDNCCEnable(const HTTPPayload& request, HTTPPayload& r return; } } + +/// +/// +/// +/// +/// +/// +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 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(srcId); + aff["grpId"].set(grpId); + + affs.push_back(json::value(aff)); + } + } + + response["affiliations"].set(affs); + reply.payload(response); +} diff --git a/src/host/network/RESTAPI.h b/src/host/network/RESTAPI.h index 432b07ff..120b62e4 100644 --- a/src/host/network/RESTAPI.h +++ b/src/host/network/RESTAPI.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_API_H__) @@ -130,9 +130,6 @@ private: /// void restAPI_GetRIDBlacklist(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); - /// - 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); /// void restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + 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); /// void restAPI_PutP25RawTSBK(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + 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); /// void restAPI_GetNXDNCCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + void restAPI_GetNXDNAffList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); }; #endif // __REST_API_H__ diff --git a/src/host/network/RESTDefines.h b/src/host/network/RESTDefines.h index d002a81b..76300ca7 100644 --- a/src/host/network/RESTDefines.h +++ b/src/host/network/RESTDefines.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__ diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 378e7bce..4c448936 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -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(); diff --git a/src/host/nxdn/Control.h b/src/host/nxdn/Control.h index 4dfada81..0887c5ef 100644 --- a/src/host/nxdn/Control.h +++ b/src/host/nxdn/Control.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 * */ #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; diff --git a/src/host/nxdn/packet/ControlSignaling.cpp b/src/host/nxdn/packet/ControlSignaling.cpp index e91b478e..af6bc030 100644 --- a/src/host/nxdn/packet/ControlSignaling.cpp +++ b/src/host/nxdn/packet/ControlSignaling.cpp @@ -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); diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index a8ac60b3..c1e96e39 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -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(); } } diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index 9a6829bb..48e12e74 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -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); } } diff --git a/src/remote/RESTClientMain.cpp b/src/remote/RESTClientMain.cpp index 62fb40fc..bb087ad7 100644 --- a/src/remote/RESTClientMain.cpp +++ b/src/remote/RESTClientMain.cpp @@ -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 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 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 Sets the P25 MFId for the next sent P25 command\r\n"; reply += " p25-rid-page 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());