implement using a global affiliations table to globally track grants and unit registrations at the FNE; implement REST APIs to report granted talkgroups and registered units;

pull/121/head
Bryan Biedenkapp 2 months ago
parent 25386ec8f6
commit aa2fc994c6

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "common/Log.h" #include "common/Log.h"
@ -79,9 +79,9 @@ bool AffiliationLookup::unitDereg(uint32_t srcId, bool automatic)
uint32_t AffiliationLookup::getSSRCByUnitReg(uint32_t srcId) uint32_t AffiliationLookup::getSSRCByUnitReg(uint32_t srcId)
{ {
// lookup dynamic channel grant table entry // lookup dynamic unit registration table entry
m_unitRegPeerTable.lock(false); m_unitRegPeerTable.lock(false);
for (auto entry : m_grantChTable) { for (auto entry : m_unitRegPeerTable) {
if (entry.first == srcId) { if (entry.first == srcId) {
m_unitRegPeerTable.unlock(); m_unitRegPeerTable.unlock();
return entry.second; return entry.second;
@ -99,3 +99,126 @@ void AffiliationLookup::clearUnitReg()
::lookups::AffiliationLookup::clearUnitReg(); ::lookups::AffiliationLookup::clearUnitReg();
m_unitRegPeerTable.clear(); m_unitRegPeerTable.clear();
} }
/* Helper to grant a channel. */
bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t ssrc, uint32_t grantTimeout, bool grp)
{
if (dstId == 0U) {
return false;
}
__lock();
m_grantChTable[dstId] = 1U; // the FNE doesn't contain channels so internally all grants will be tracked with a
// channel number of 1, but this allows us to track the grant by the destination ID
m_grantSrcIdTable[dstId] = srcId;
m_grantSSRCTable[dstId] = ssrc;
m_rfGrantChCnt++;
m_uuGrantedTable[dstId] = !grp;
m_grantTimers[dstId] = Timer(1000U, grantTimeout);
m_grantTimers[dstId].start();
if (m_verbose) {
LogInfoEx(LOG_MASTER, "%s, granting talkgroup call, dstId = %u, srcId = %u, ssrc = %u, group = %u",
m_name.c_str(), dstId, srcId, ssrc, grp);
}
__unlock();
return true;
}
/* Helper to get the originating network peer ID by the registered source ID. */
uint32_t AffiliationLookup::getSSRCByGrant(uint32_t srcId)
{
// lookup dynamic channel grant table entry
m_grantSSRCTable.lock(false);
for (auto entry : m_grantSSRCTable) {
if (entry.first == srcId) {
m_grantSSRCTable.unlock();
return entry.second;
}
}
m_grantSSRCTable.unlock();
return 0U;
}
/* Helper to release the channel grant for the destination ID. */
bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll)
{
if (dstId == 0U && !releaseAll) {
return false;
}
// are we trying to release all grants?
if (dstId == 0U && releaseAll) {
LogWarning(LOG_MASTER, "%s, force releasing all talkgroup call grants", m_name.c_str());
m_grantSSRCTable.lock(false);
std::vector<uint32_t> gntsToRel = std::vector<uint32_t>();
for (auto entry : m_grantSSRCTable) {
uint32_t dstId = entry.first;
gntsToRel.push_back(dstId);
}
m_grantSSRCTable.unlock();
// release grants
for (uint32_t dstId : gntsToRel) {
releaseGrant(dstId, false);
}
return true;
}
if (isGranted(dstId)) {
uint32_t ssrc = 0U;
m_grantSSRCTable.lock(false);
for (auto entry : m_grantSSRCTable) {
if (entry.first == dstId) {
ssrc = entry.second;
break;
}
}
m_grantSSRCTable.unlock();
uint32_t srcId = getGrantedSrcId(dstId);
if (m_verbose) {
LogInfoEx(LOG_MASTER, "%s, releasing talkgroup call grant, ssrc = %u, dstId = %u",
m_name.c_str(), ssrc, dstId);
}
if (m_releaseGrant != nullptr) {
m_releaseGrant(0U, srcId, dstId, 0U);
}
__lock();
m_grantChTable.erase(dstId);
m_grantSSRCTable.erase(dstId);
m_grantSrcIdTable.erase(dstId);
m_uuGrantedTable.erase(dstId);
m_netGrantedTable.erase(dstId);
if (m_rfGrantChCnt > 0U) {
m_rfGrantChCnt--;
}
else {
m_rfGrantChCnt = 0U;
}
m_grantTimers[dstId].stop();
__unlock();
return true;
}
return false;
}

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
/** /**
@ -26,6 +26,12 @@
namespace fne_lookups namespace fne_lookups
{ {
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint32_t GRANT_TIMER_TIMEOUT = 15U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -75,8 +81,45 @@ namespace fne_lookups
void clearUnitReg() override; void clearUnitReg() override;
/** @} */ /** @} */
/** @name Channel Grants */
/**
* @brief Gets the grant table.
* @returns std::unordered_map<uint32_t, uint32_t> Channel Grant Table.
*/
std::unordered_map<uint32_t, uint32_t> grantSrcIdTable() const { return m_grantSrcIdTable.get(); }
/**
* @brief Gets the grant SSRC table.
* @returns std::unordered_map<uint32_t, uint32_t> Source Peer Grant Table.
*/
std::unordered_map<uint32_t, uint32_t> grantSSRCTable() const { return m_grantSSRCTable.get(); }
/**
* @brief Helper to grant a channel.
* @param dstId Destination Address.
* @param srcId Source Radio ID.
* @param ssrc Originating Peer ID.
* @param grantTimeout Time before the grant times out from inactivity.
* @param grp Flag indicating the grant is for a talkgroup.
* @returns bool True, if the destination address has been granted to the source ID, otherwise false.
*/
bool grantCh(uint32_t dstId, uint32_t srcId, uint32_t ssrc, uint32_t grantTimeout, bool grp);
/**
* @brief Helper to get the originating network peer ID by the granted destination ID.
* @param dstId Destination Address.
* @returns uint32_t Originating Peer ID.
*/
uint32_t getSSRCByGrant(uint32_t dstId);
/**
* @brief Helper to release the channel grant for the destination ID.
* @param dstId Destination Address.
* @param releaseAll Flag indicating all channel grants should be released.
* @returns bool True, if channel grant was released, otherwise false.
*/
bool releaseGrant(uint32_t dstId, bool releaseAll = false) override;
/** @} */
protected: protected:
concurrent::unordered_map<uint32_t, uint32_t> m_unitRegPeerTable; concurrent::unordered_map<uint32_t, uint32_t> m_unitRegPeerTable;
concurrent::unordered_map<uint32_t, uint32_t> m_grantSSRCTable;
}; };
} // namespace fne_lookups } // namespace fne_lookups

@ -99,6 +99,7 @@ TrafficNetwork::TrafficNetwork(HostFNE* host, const std::string& address, uint16
m_peerAffiliations(), m_peerAffiliations(),
m_ccPeerMap(), m_ccPeerMap(),
m_peerReplicaKeyQueue(), m_peerReplicaKeyQueue(),
m_globalAff(nullptr),
m_treeRoot(nullptr), m_treeRoot(nullptr),
m_treeLock(), m_treeLock(),
m_peerReplicaHAParams(), m_peerReplicaHAParams(),
@ -169,6 +170,8 @@ TrafficNetwork::TrafficNetwork(HostFNE* host, const std::string& address, uint16
m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose); m_p25OTARService = new P25OTARService(this, m_tagP25->packetData(), kmfDebug, verbose);
m_globalAff = new fne_lookups::AffiliationLookup("GlobalAffiliations", nullptr, false);
SpanningTree::s_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings; SpanningTree::s_maxUpdatesBeforeReparent = (uint8_t)host->m_maxMissedPings;
m_treeRoot = new SpanningTree(peerId, peerId, nullptr); m_treeRoot = new SpanningTree(peerId, peerId, nullptr);
m_treeRoot->identity(identity); m_treeRoot->identity(identity);
@ -674,6 +677,8 @@ void TrafficNetwork::clock(uint32_t ms)
m_updateLookupTimer.start(); m_updateLookupTimer.start();
} }
m_globalAff->clock(ms);
// if HA is enabled perform HA parameter updates // if HA is enabled perform HA parameter updates
if (m_haEnabled) { if (m_haEnabled) {
m_haUpdateTimer.clock(ms); m_haUpdateTimer.clock(ms);
@ -1928,6 +1933,7 @@ void TrafficNetwork::taskNetworkRx(NetPacketRequest* req)
if (connection->connected() && connection->address() == ip && aff != nullptr) { if (connection->connected() && connection->address() == ip && aff != nullptr) {
uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address
aff->unitReg(srcId, ssrc); aff->unitReg(srcId, ssrc);
network->m_globalAff->unitReg(srcId, ssrc);
// attempt to repeat traffic to replica masters // attempt to repeat traffic to replica masters
if (network->m_host->m_peerNetworks.size() > 0) { if (network->m_host->m_peerNetworks.size() > 0) {
@ -1964,6 +1970,7 @@ void TrafficNetwork::taskNetworkRx(NetPacketRequest* req)
if (connection->connected() && connection->address() == ip && aff != nullptr) { if (connection->connected() && connection->address() == ip && aff != nullptr) {
uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address
aff->unitDereg(srcId); aff->unitDereg(srcId);
network->m_globalAff->unitDereg(srcId);
// attempt to repeat traffic to replica masters // attempt to repeat traffic to replica masters
if (network->m_host->m_peerNetworks.size() > 0) { if (network->m_host->m_peerNetworks.size() > 0) {
@ -2001,6 +2008,7 @@ void TrafficNetwork::taskNetworkRx(NetPacketRequest* req)
if (connection->connected() && connection->address() == ip && aff != nullptr) { if (connection->connected() && connection->address() == ip && aff != nullptr) {
uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address uint32_t srcId = GET_UINT24(req->buffer, 0U); // Source Address
aff->groupUnaff(srcId); aff->groupUnaff(srcId);
network->m_globalAff->groupUnaff(srcId);
// attempt to repeat traffic to replica masters // attempt to repeat traffic to replica masters
if (network->m_host->m_peerNetworks.size() > 0) { if (network->m_host->m_peerNetworks.size() > 0) {
@ -2048,6 +2056,7 @@ void TrafficNetwork::taskNetworkRx(NetPacketRequest* req)
uint32_t dstId = GET_UINT24(req->buffer, offs + 4U); uint32_t dstId = GET_UINT24(req->buffer, offs + 4U);
aff->groupAff(srcId, dstId); aff->groupAff(srcId, dstId);
network->m_globalAff->groupAff(srcId, dstId);
offs += 8U; offs += 8U;
} }
LogInfoEx(LOG_MASTER, "PEER %u (%s) announced %u affiliations", peerId, connection->identWithQualifier().c_str(), len); LogInfoEx(LOG_MASTER, "PEER %u (%s) announced %u affiliations", peerId, connection->identWithQualifier().c_str(), len);

@ -345,6 +345,8 @@ namespace network
static std::timed_mutex s_keyQueueMutex; static std::timed_mutex s_keyQueueMutex;
std::unordered_map<uint32_t, uint16_t> m_peerReplicaKeyQueue; std::unordered_map<uint32_t, uint16_t> m_peerReplicaKeyQueue;
fne_lookups::AffiliationLookup* m_globalAff;
SpanningTree* m_treeRoot; SpanningTree* m_treeRoot;
std::mutex m_treeLock; std::mutex m_treeLock;

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "fne/Defines.h" #include "fne/Defines.h"
@ -13,6 +13,7 @@
#include "common/Clock.h" #include "common/Clock.h"
#include "common/Log.h" #include "common/Log.h"
#include "common/Utils.h" #include "common/Utils.h"
#include "lookups/AffiliationLookup.h"
#include "network/TrafficNetwork.h" #include "network/TrafficNetwork.h"
#include "network/callhandler/TagAnalogData.h" #include "network/callhandler/TagAnalogData.h"
#include "HostFNE.h" #include "HostFNE.h"
@ -121,6 +122,8 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee
} }
} }
m_network->m_globalAff->releaseGrant(dstId);
#define CALL_END_LOG "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream #define CALL_END_LOG "Analog, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_END_LOG); LogInfoEx(LOG_PEER, CALL_END_LOG);
@ -279,6 +282,9 @@ bool TagAnalogData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee
m_network->m_totalActiveCalls++; m_network->m_totalActiveCalls++;
} }
if (!m_network->m_globalAff->isGranted(dstId))
m_network->m_globalAff->grantCh(dstId, srcId, ssrc, fne_lookups::GRANT_TIMER_TIMEOUT, true);
#define CALL_START_LOG "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream #define CALL_START_LOG "Analog, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_START_LOG); LogInfoEx(LOG_PEER, CALL_START_LOG);

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2023-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "fne/Defines.h" #include "fne/Defines.h"
@ -16,6 +16,7 @@
#include "common/Clock.h" #include "common/Clock.h"
#include "common/Log.h" #include "common/Log.h"
#include "common/Utils.h" #include "common/Utils.h"
#include "lookups/AffiliationLookup.h"
#include "network/TrafficNetwork.h" #include "network/TrafficNetwork.h"
#include "network/callhandler/TagDMRData.h" #include "network/callhandler/TagDMRData.h"
#include "HostFNE.h" #include "HostFNE.h"
@ -193,6 +194,9 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}); });
if (it != m_statusPVCall.end()) { if (it != m_statusPVCall.end()) {
m_statusPVCall[dstId].reset(); m_statusPVCall[dstId].reset();
m_network->m_globalAff->releaseGrant(dstId);
#define PRV_CALL_END_LOG "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream #define PRV_CALL_END_LOG "DMR, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, PRV_CALL_END_LOG); LogInfoEx(LOG_PEER, PRV_CALL_END_LOG);
@ -200,6 +204,8 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG); LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG);
} }
else { else {
m_network->m_globalAff->releaseGrant(dstId);
#define CALL_END_LOG "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream #define CALL_END_LOG "DMR, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, slot = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, slotNo, duration / 1000, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_END_LOG); LogInfoEx(LOG_PEER, CALL_END_LOG);
@ -407,6 +413,9 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall[dstId].dstPeerId = regSSRC;
m_statusPVCall.unlock(); m_statusPVCall.unlock();
if (!m_network->m_globalAff->isGranted(dstId))
m_network->m_globalAff->grantCh(dstId, srcId, ssrc, fne_lookups::GRANT_TIMER_TIMEOUT, false);
#define PRV_CALL_START_LOG "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream #define PRV_CALL_START_LOG "DMR, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, PRV_CALL_START_LOG); LogInfoEx(LOG_PEER, PRV_CALL_START_LOG);
@ -414,6 +423,9 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG); LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG);
} }
else { else {
if (!m_network->m_globalAff->isGranted(dstId))
m_network->m_globalAff->grantCh(dstId, srcId, ssrc, fne_lookups::GRANT_TIMER_TIMEOUT, true);
#define CALL_START_LOG "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream #define CALL_START_LOG "DMR, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_START_LOG); LogInfoEx(LOG_PEER, CALL_START_LOG);
@ -673,7 +685,13 @@ bool TagDMRData::processGrantReq(uint32_t srcId, uint32_t dstId, uint8_t slot, b
} }
} }
return true; if (!m_network->m_globalAff->isGranted(dstId)) {
if (m_network->m_globalAff->grantCh(dstId, srcId, peerId, fne_lookups::GRANT_TIMER_TIMEOUT, !unitToUnit)) {
return true;
}
}
return false;
} }
/* Helper to trigger a call takeover from a In-Call control event. */ /* Helper to trigger a call takeover from a In-Call control event. */

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2023-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "fne/Defines.h" #include "fne/Defines.h"
@ -20,6 +20,7 @@
#include "common/Clock.h" #include "common/Clock.h"
#include "common/Log.h" #include "common/Log.h"
#include "common/Utils.h" #include "common/Utils.h"
#include "lookups/AffiliationLookup.h"
#include "network/TrafficNetwork.h" #include "network/TrafficNetwork.h"
#include "network/callhandler/TagNXDNData.h" #include "network/callhandler/TagNXDNData.h"
#include "HostFNE.h" #include "HostFNE.h"
@ -232,6 +233,9 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
}); });
if (it != m_statusPVCall.end()) { if (it != m_statusPVCall.end()) {
m_statusPVCall[dstId].reset(); m_statusPVCall[dstId].reset();
m_network->m_globalAff->releaseGrant(dstId);
#define PRV_CALL_END_LOG "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream #define PRV_CALL_END_LOG "NXDN, Private Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, PRV_CALL_END_LOG); LogInfoEx(LOG_PEER, PRV_CALL_END_LOG);
@ -239,6 +243,8 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG); LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG);
} }
else { else {
m_network->m_globalAff->releaseGrant(dstId);
#define CALL_END_LOG "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream #define CALL_END_LOG "NXDN, Call End, peer = %u, ssrc = %u, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, duration / 1000, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_END_LOG); LogInfoEx(LOG_PEER, CALL_END_LOG);
@ -432,6 +438,9 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall[dstId].dstPeerId = regSSRC;
m_statusPVCall.unlock(); m_statusPVCall.unlock();
if (!m_network->m_globalAff->isGranted(dstId))
m_network->m_globalAff->grantCh(dstId, srcId, ssrc, fne_lookups::GRANT_TIMER_TIMEOUT, false);
#define PRV_CALL_START_LOG "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream #define PRV_CALL_START_LOG "NXDN, Private Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, PRV_CALL_START_LOG); LogInfoEx(LOG_PEER, PRV_CALL_START_LOG);
@ -439,6 +448,9 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG); LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG);
} }
else { else {
if (!m_network->m_globalAff->isGranted(dstId))
m_network->m_globalAff->grantCh(dstId, srcId, ssrc, fne_lookups::GRANT_TIMER_TIMEOUT, true);
#define CALL_START_LOG "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream #define CALL_START_LOG "NXDN, Call Start, peer = %u, ssrc = %u, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, srcId, dstId, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_START_LOG); LogInfoEx(LOG_PEER, CALL_START_LOG);
@ -692,7 +704,13 @@ bool TagNXDNData::processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUni
} }
} }
return true; if (!m_network->m_globalAff->isGranted(dstId)) {
if (m_network->m_globalAff->grantCh(dstId, srcId, peerId, fne_lookups::GRANT_TIMER_TIMEOUT, !unitToUnit)) {
return true;
}
}
return false;
} }
/* Helper to trigger a call takeover from a In-Call control event. */ /* Helper to trigger a call takeover from a In-Call control event. */

@ -15,6 +15,7 @@
#include "common/Log.h" #include "common/Log.h"
#include "common/Thread.h" #include "common/Thread.h"
#include "common/Utils.h" #include "common/Utils.h"
#include "lookups/AffiliationLookup.h"
#include "network/TrafficNetwork.h" #include "network/TrafficNetwork.h"
#include "network/callhandler/TagP25Data.h" #include "network/callhandler/TagP25Data.h"
#include "HostFNE.h" #include "HostFNE.h"
@ -30,12 +31,6 @@ using namespace p25::defines;
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint32_t GRANT_TIMER_TIMEOUT = 15U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -282,6 +277,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
}); });
if (it != m_statusPVCall.end()) { if (it != m_statusPVCall.end()) {
m_statusPVCall[dstId].reset(); m_statusPVCall[dstId].reset();
m_network->m_globalAff->releaseGrant(dstId);
#define PRV_CALL_END_LOG "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream #define PRV_CALL_END_LOG "P25, Private Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, PRV_CALL_END_LOG); LogInfoEx(LOG_PEER, PRV_CALL_END_LOG);
@ -289,6 +287,8 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG); LogInfoEx(LOG_MASTER, PRV_CALL_END_LOG);
} }
else { else {
m_network->m_globalAff->releaseGrant(dstId);
#define CALL_END_LOG "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream #define CALL_END_LOG "P25, Call End, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_END_LOG); LogInfoEx(LOG_PEER, CALL_END_LOG);
@ -493,6 +493,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
m_statusPVCall[dstId].dstPeerId = regSSRC; m_statusPVCall[dstId].dstPeerId = regSSRC;
m_statusPVCall.unlock(); m_statusPVCall.unlock();
if (!m_network->m_globalAff->isGranted(dstId))
m_network->m_globalAff->grantCh(dstId, srcId, ssrc, fne_lookups::GRANT_TIMER_TIMEOUT, false);
#define PRV_CALL_START_LOG "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream #define PRV_CALL_START_LOG "P25, Private Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, PRV_CALL_START_LOG); LogInfoEx(LOG_PEER, PRV_CALL_START_LOG);
@ -500,6 +503,9 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG); LogInfoEx(LOG_MASTER, PRV_CALL_START_LOG);
} }
else { else {
if (!m_network->m_globalAff->isGranted(dstId))
m_network->m_globalAff->grantCh(dstId, srcId, ssrc, fne_lookups::GRANT_TIMER_TIMEOUT, true);
#define CALL_START_LOG "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream #define CALL_START_LOG "P25, Call Start, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, streamId, fromUpstream
if (m_network->m_logUpstreamCallStartEnd && fromUpstream) if (m_network->m_logUpstreamCallStartEnd && fromUpstream)
LogInfoEx(LOG_PEER, CALL_START_LOG); LogInfoEx(LOG_PEER, CALL_START_LOG);
@ -782,7 +788,13 @@ bool TagP25Data::processGrantReq(uint32_t srcId, uint32_t dstId, bool unitToUnit
} }
m_network->m_peers.shared_unlock(); m_network->m_peers.shared_unlock();
return true; if (!m_network->m_globalAff->isGranted(dstId)) {
if (m_network->m_globalAff->grantCh(dstId, srcId, peerId, fne_lookups::GRANT_TIMER_TIMEOUT, !unitToUnit)) {
return true;
}
}
return false;
} }
/* Helper to trigger a call takeover from a In-Call control event. */ /* Helper to trigger a call takeover from a In-Call control event. */

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2024-2026 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024 Patrick McDonnell, W3AXL * Copyright (C) 2024 Patrick McDonnell, W3AXL
* *
*/ */
@ -676,7 +676,9 @@ void RESTAPI::initializeEndpoints()
m_dispatcher.match(FNE_GET_RESET_TOTAL_CALLS).get(REST_API_BIND(RESTAPI::restAPI_GetResetTotalCalls, this)); m_dispatcher.match(FNE_GET_RESET_TOTAL_CALLS).get(REST_API_BIND(RESTAPI::restAPI_GetResetTotalCalls, this));
m_dispatcher.match(FNE_GET_RESET_ACTIVE_CALLS).get(REST_API_BIND(RESTAPI::restAPI_GetResetActiveCalls, this)); m_dispatcher.match(FNE_GET_RESET_ACTIVE_CALLS).get(REST_API_BIND(RESTAPI::restAPI_GetResetActiveCalls, this));
m_dispatcher.match(FNE_GET_RESET_CALL_COLLISIONS).get(REST_API_BIND(RESTAPI::restAPI_GetResetCallCollisions, this)); m_dispatcher.match(FNE_GET_RESET_CALL_COLLISIONS).get(REST_API_BIND(RESTAPI::restAPI_GetResetCallCollisions, this));
m_dispatcher.match(FNE_GET_UNIT_REG_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetUnitRegList, this));
m_dispatcher.match(FNE_GET_AFF_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetAffList, this)); m_dispatcher.match(FNE_GET_AFF_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetAffList, this));
m_dispatcher.match(FNE_GET_GRANT_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetGrantList, this));
m_dispatcher.match(FNE_GET_SPANNING_TREE).get(REST_API_BIND(RESTAPI::restAPI_GetSpanningTree, this)); m_dispatcher.match(FNE_GET_SPANNING_TREE).get(REST_API_BIND(RESTAPI::restAPI_GetSpanningTree, this));
@ -2033,6 +2035,34 @@ void RESTAPI::restAPI_GetResetCallCollisions(const HTTPPayload& request, HTTPPay
reply.payload(response); reply.payload(response);
} }
/* REST API endpoint; implements get unit registration list request. */
void RESTAPI::restAPI_GetUnitRegList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object response = json::object();
setResponseDefaultStatus(response);
json::array units = json::array();
if (m_network != nullptr) {
std::vector<uint32_t> unitRegTable = m_network->m_globalAff->unitRegTable();
for (auto entry : unitRegTable) {
uint32_t srcId = entry;
units.push_back(json::value((double)srcId));
}
uint32_t totalUnits = unitRegTable.size();
response["totalUnits"].set<uint32_t>(totalUnits);
}
response["units"].set<json::array>(units);
reply.payload(response);
}
/* REST API endpoint; implements get affiliation list request. */ /* REST API endpoint; implements get affiliation list request. */
void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
@ -2046,6 +2076,7 @@ void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply,
json::array affs = json::array(); json::array affs = json::array();
if (m_network != nullptr) { if (m_network != nullptr) {
uint32_t totalAffiliations = 0U;
if (m_network->m_peers.size() > 0) { if (m_network->m_peers.size() > 0) {
for (auto entry : m_network->m_peers) { for (auto entry : m_network->m_peers) {
uint32_t peerId = entry.first; uint32_t peerId = entry.first;
@ -2073,16 +2104,58 @@ void RESTAPI::restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply,
peerObj["affiliations"].set<json::array>(peerAffs); peerObj["affiliations"].set<json::array>(peerAffs);
affs.push_back(json::value(peerObj)); affs.push_back(json::value(peerObj));
++totalAffiliations;
} }
} }
} }
} }
response["totalAffiliations"].set<uint32_t>(totalAffiliations);
} }
response["affiliations"].set<json::array>(affs); response["affiliations"].set<json::array>(affs);
reply.payload(response); reply.payload(response);
} }
/* REST API endpoint; implements get talkgroup grant list request. */
void RESTAPI::restAPI_GetGrantList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object response = json::object();
setResponseDefaultStatus(response);
json::array grants = json::array();
if (m_network != nullptr) {
std::unordered_map<uint32_t, uint32_t> grantSrcIdTable = m_network->m_globalAff->grantSrcIdTable();
std::unordered_map<uint32_t, uint32_t> grantSSRCTable = m_network->m_globalAff->grantSSRCTable();
for (auto entry : grantSrcIdTable) {
uint32_t dstId = entry.first;
uint32_t srcId = entry.second;
json::object grantObj = json::object();
grantObj["srcId"].set<uint32_t>(srcId);
grantObj["dstId"].set<uint32_t>(dstId);
uint32_t ssrc = 0U;
grantSSRCTable.find(dstId) != grantSSRCTable.end() ? ssrc = grantSSRCTable[dstId] : ssrc = 0U;
grantObj["ssrc"].set<uint32_t>(ssrc);
grants.push_back(json::value(grantObj));
}
uint32_t totalGrants = grantSrcIdTable.size();
response["totalGrants"].set<uint32_t>(totalGrants);
}
response["grants"].set<json::array>(grants);
reply.payload(response);
}
/* REST API endpoint; implements get spanning tree list request. */ /* REST API endpoint; implements get spanning tree list request. */
void RESTAPI::restAPI_GetSpanningTree(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) void RESTAPI::restAPI_GetSpanningTree(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2024-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
/** /**
@ -400,6 +400,14 @@ private:
*/ */
void restAPI_GetResetCallCollisions(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); void restAPI_GetResetCallCollisions(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match);
/**
* @brief REST API endpoint; implements get unit registration list request.
* @param request HTTP request.
* @param reply HTTP reply.
* @param match HTTP request matcher.
*/
void restAPI_GetUnitRegList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match);
/** /**
* @brief REST API endpoint; implements get affiliation list request. * @brief REST API endpoint; implements get affiliation list request.
* @param request HTTP request. * @param request HTTP request.
@ -408,6 +416,14 @@ private:
*/ */
void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); void restAPI_GetAffList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match);
/**
* @brief REST API endpoint; implements get talkgroup grant list request.
* @param request HTTP request.
* @param reply HTTP reply.
* @param match HTTP request matcher.
*/
void restAPI_GetGrantList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match);
/** /**
* @brief REST API endpoint; implements get spanning tree list request. * @brief REST API endpoint; implements get spanning tree list request.
* @param request HTTP request. * @param request HTTP request.

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2024-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
/** /**
@ -63,7 +63,9 @@
#define FNE_GET_RESET_TOTAL_CALLS "/stat-reset-total-calls" #define FNE_GET_RESET_TOTAL_CALLS "/stat-reset-total-calls"
#define FNE_GET_RESET_ACTIVE_CALLS "/stat-reset-active-calls" #define FNE_GET_RESET_ACTIVE_CALLS "/stat-reset-active-calls"
#define FNE_GET_RESET_CALL_COLLISIONS "/stat-reset-call-collisions" #define FNE_GET_RESET_CALL_COLLISIONS "/stat-reset-call-collisions"
#define FNE_GET_UNIT_REG_LIST "/report-unit-regs"
#define FNE_GET_AFF_LIST "/report-affiliations" #define FNE_GET_AFF_LIST "/report-affiliations"
#define FNE_GET_GRANT_LIST "/report-grants"
#define FNE_GET_SPANNING_TREE "/spanning-tree" #define FNE_GET_SPANNING_TREE "/spanning-tree"

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2023,2024,2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2023-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "remote/RESTClient.h" #include "remote/RESTClient.h"
@ -41,7 +41,9 @@
#define RCD_FNE_GET_PEERCOUNT "fne-peercount" #define RCD_FNE_GET_PEERCOUNT "fne-peercount"
#define RCD_FNE_GET_TGIDLIST "fne-tgidlist" #define RCD_FNE_GET_TGIDLIST "fne-tgidlist"
#define RCD_FNE_GET_FORCEUPDATE "fne-force-update" #define RCD_FNE_GET_FORCEUPDATE "fne-force-update"
#define RCD_FNE_GET_UNITREGS "fne-unitregs"
#define RCD_FNE_GET_AFFLIST "fne-affs" #define RCD_FNE_GET_AFFLIST "fne-affs"
#define RCD_FNE_GET_GRANTLIST "fne-grants"
#define RCD_FNE_GET_RELOADTGS "fne-reload-tgs" #define RCD_FNE_GET_RELOADTGS "fne-reload-tgs"
#define RCD_FNE_GET_RELOADRIDS "fne-reload-rids" #define RCD_FNE_GET_RELOADRIDS "fne-reload-rids"
#define RCD_FNE_GET_RELOADPEERLIST "fne-reload-peerlist" #define RCD_FNE_GET_RELOADPEERLIST "fne-reload-peerlist"
@ -220,7 +222,9 @@ void usage(const char* message, const char* arg)
reply += " fne-peercount Retrieves the count of connected peers (Converged FNE only)\r\n"; reply += " fne-peercount Retrieves the count of connected peers (Converged FNE only)\r\n";
reply += " fne-tgidlist Retrieves the list of configured TGIDs (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-force-update Forces the FNE to send list update (Converged FNE only)\r\n";
reply += " fne-unitregs Retrieves the list of currently registered unit IDs (Converged FNE only)\r\n";
reply += " fne-affs Retrieves the list of currently affiliated SUs (Converged FNE only)\r\n"; reply += " fne-affs Retrieves the list of currently affiliated SUs (Converged FNE only)\r\n";
reply += " fne-grants Retrieves the list of currently granted talkgroups (Converged FNE only)\r\n";
reply += " fne-reload-tgs Forces the FNE to reload its TGID list from disk (Converged FNE only)\r\n"; reply += " fne-reload-tgs Forces the FNE to reload its TGID list from disk (Converged FNE only)\r\n";
reply += " fne-reload-rids Forces the FNE to reload its RID list from disk (Converged FNE only)\r\n"; reply += " fne-reload-rids Forces the FNE to reload its RID list from disk (Converged FNE only)\r\n";
reply += " fne-reload-peerlist Forces the FNE to reload its peer list from disk (Converged FNE only)\r\n"; reply += " fne-reload-peerlist Forces the FNE to reload its peer list from disk (Converged FNE only)\r\n";
@ -901,9 +905,15 @@ int main(int argc, char** argv)
else if (rcom == RCD_FNE_GET_FORCEUPDATE) { else if (rcom == RCD_FNE_GET_FORCEUPDATE) {
retCode = client->send(HTTP_GET, FNE_GET_FORCE_UPDATE, json::object(), response); retCode = client->send(HTTP_GET, FNE_GET_FORCE_UPDATE, json::object(), response);
} }
else if (rcom == RCD_FNE_GET_UNITREGS) {
retCode = client->send(HTTP_GET, FNE_GET_UNIT_REG_LIST, json::object(), response);
}
else if (rcom == RCD_FNE_GET_AFFLIST) { else if (rcom == RCD_FNE_GET_AFFLIST) {
retCode = client->send(HTTP_GET, FNE_GET_AFF_LIST, json::object(), response); retCode = client->send(HTTP_GET, FNE_GET_AFF_LIST, json::object(), response);
} }
else if (rcom == RCD_FNE_GET_GRANTLIST) {
retCode = client->send(HTTP_GET, FNE_GET_GRANT_LIST, json::object(), response);
}
else if (rcom == RCD_FNE_GET_RELOADTGS) { else if (rcom == RCD_FNE_GET_RELOADTGS) {
retCode = client->send(HTTP_GET, FNE_GET_RELOAD_TGS, json::object(), response); retCode = client->send(HTTP_GET, FNE_GET_RELOAD_TGS, json::object(), response);
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.