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());