From 590aeb0beaa7a02e8e09b41ac6cee0a83f004836 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 26 Mar 2023 17:16:33 -0400 Subject: [PATCH] refactor common affiliations class to have a release grant callback; implement proper release grant callbacks for P25 and NXDN to call the permit TG API with a TGID 0 to ensure permits are released at the end of a call; update DMR release grant callback in the same way; allow permit TG API call to accept 0 as a valid TGID; --- src/dmr/Slot.cpp | 22 +++++++++++++++++++++- src/dmr/lookups/DMRAffiliationLookup.cpp | 3 +-- src/dmr/lookups/DMRAffiliationLookup.h | 6 ------ src/lookups/AffiliationLookup.cpp | 5 +++++ src/lookups/AffiliationLookup.h | 7 +++++++ src/network/RESTAPI.cpp | 5 ----- src/nxdn/Control.cpp | 23 +++++++++++++++++++++++ src/p25/Control.cpp | 23 +++++++++++++++++++++++ 8 files changed, 80 insertions(+), 14 deletions(-) diff --git a/src/dmr/Slot.cpp b/src/dmr/Slot.cpp index 57a73555..7ad75329 100644 --- a/src/dmr/Slot.cpp +++ b/src/dmr/Slot.cpp @@ -671,6 +671,8 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s m_ridLookup = ridLookup; m_tidLookup = tidLookup; m_affiliations = new dmr::lookups::DMRAffiliationLookup(verbose); + + // set the grant release callback m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { Slot* tscc = m_dmr->getTSCCSlot(); if (tscc != nullptr) { @@ -690,7 +692,25 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, tscc->m_debug); } else { - ::LogError(LOG_DMR, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to clear payload channel, chNo = %u", tscc->m_slotNo, chNo); + ::LogError(LOG_DMR, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to clear payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); + } + + // callback REST API to clear TG permit for the granted TG on the specified voice channel + if (m_authoritative && m_dmr->m_supervisor) { + if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { + json::object req = json::object(); + int state = modem::DVM_STATE::STATE_DMR; + req["state"].set(state); + dstId = 0U; // clear TG value + req["dstId"].set(dstId); + req["slot"].set(slot); + + RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), + HTTP_PUT, PUT_PERMIT_TG, req, m_dmr->m_debug); + } + else { + ::LogError(LOG_DMR, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to clear TG permit, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); + } } } }); diff --git a/src/dmr/lookups/DMRAffiliationLookup.cpp b/src/dmr/lookups/DMRAffiliationLookup.cpp index 21e8b19b..da906e1b 100644 --- a/src/dmr/lookups/DMRAffiliationLookup.cpp +++ b/src/dmr/lookups/DMRAffiliationLookup.cpp @@ -44,8 +44,7 @@ using namespace dmr::lookups; DMRAffiliationLookup::DMRAffiliationLookup(bool verbose) : ::lookups::AffiliationLookup("DMR Affiliation", verbose), m_grantChSlotTable(), m_tsccChNo(0U), - m_tsccSlot(0U), - m_releaseGrant(nullptr) + m_tsccSlot(0U) { /* stub */ } diff --git a/src/dmr/lookups/DMRAffiliationLookup.h b/src/dmr/lookups/DMRAffiliationLookup.h index ab784dc2..7229577b 100644 --- a/src/dmr/lookups/DMRAffiliationLookup.h +++ b/src/dmr/lookups/DMRAffiliationLookup.h @@ -30,7 +30,6 @@ #include "lookups/AffiliationLookup.h" #include -#include namespace dmr { @@ -66,16 +65,11 @@ namespace dmr /// Helper to determine the first available slot for given the channel number. uint8_t getAvailableSlotForChannel(uint32_t chNo) const; - /// Helper to set the release grant callback. - void setReleaseGrantCallback(std::function&& callback) { m_releaseGrant = callback; } - protected: std::unordered_map> m_grantChSlotTable; uint32_t m_tsccChNo; uint8_t m_tsccSlot; - - std::function m_releaseGrant; }; } // namespace lookups } // namespace dmr diff --git a/src/lookups/AffiliationLookup.cpp b/src/lookups/AffiliationLookup.cpp index a6e29012..9c99e7ad 100644 --- a/src/lookups/AffiliationLookup.cpp +++ b/src/lookups/AffiliationLookup.cpp @@ -51,6 +51,7 @@ AffiliationLookup::AffiliationLookup(const char* name, bool verbose) : m_grantChTable(), m_grantSrcIdTable(), m_grantTimers(), + m_releaseGrant(nullptr), m_name(name), m_verbose(verbose) { @@ -325,6 +326,10 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) m_name, chNo, dstId); } + if (m_releaseGrant != nullptr) { + m_releaseGrant(chNo, dstId, 0U); + } + m_grantChTable.erase(dstId); m_grantSrcIdTable.erase(dstId); m_rfChTable.push_back(chNo); diff --git a/src/lookups/AffiliationLookup.h b/src/lookups/AffiliationLookup.h index 43f8dbdb..b64d689e 100644 --- a/src/lookups/AffiliationLookup.h +++ b/src/lookups/AffiliationLookup.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace lookups { @@ -170,6 +171,9 @@ namespace lookups /// Updates the processor by the passed number of milliseconds. void clock(uint32_t ms); + /// Helper to set the release grant callback. + void setReleaseGrantCallback(std::function&& callback) { m_releaseGrant = callback; } + protected: std::vector m_rfChTable; std::unordered_map m_rfChDataTable; @@ -182,6 +186,9 @@ namespace lookups std::unordered_map m_grantSrcIdTable; std::unordered_map m_grantTimers; + // chNo dstId slot + std::function m_releaseGrant; + const char *m_name; bool m_verbose; diff --git a/src/network/RESTAPI.cpp b/src/network/RESTAPI.cpp index ea783531..0a173175 100644 --- a/src/network/RESTAPI.cpp +++ b/src/network/RESTAPI.cpp @@ -900,11 +900,6 @@ void RESTAPI::restAPI_PutPermitTG(const HTTPPayload& request, HTTPPayload& reply uint32_t dstId = req["dstId"].get(); - if (dstId == 0U) { - errorPayload(reply, "destination ID is an illegal TGID"); - return; - } - switch (state) { case STATE_DMR: #if defined(ENABLE_DMR) diff --git a/src/nxdn/Control.cpp b/src/nxdn/Control.cpp index afc049e0..92645a35 100644 --- a/src/nxdn/Control.cpp +++ b/src/nxdn/Control.cpp @@ -39,6 +39,7 @@ #include "nxdn/Sync.h" #include "nxdn/NXDNUtils.h" #include "edac/AMBEFEC.h" +#include "remote/RESTClient.h" #include "HostMain.h" #include "Log.h" #include "Utils.h" @@ -275,6 +276,28 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw std::unordered_map chData = std::unordered_map(voiceChData); m_affiliations.setRFChData(chData); + // set the grant release callback + m_affiliations.setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { + // callback REST API to clear TG permit for the granted TG on the specified voice channel + if (m_authoritative && m_supervisor) { + ::lookups::VoiceChData voiceChData = m_affiliations.getRFChData(chNo); + if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && + chNo != m_siteData.channelNo()) { + json::object req = json::object(); + int state = modem::DVM_STATE::STATE_NXDN; + req["state"].set(state); + dstId = 0U; // clear TG value + req["dstId"].set(dstId); + + RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), + HTTP_PUT, PUT_PERMIT_TG, req, m_debug); + } + else { + ::LogError(LOG_NXDN, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL_RESP ", failed to clear TG permit, chNo = %u", chNo); + } + } + }); + lc::RCCH::setSiteData(m_siteData); lc::RCCH::setCallsign(cwCallsign); diff --git a/src/p25/Control.cpp b/src/p25/Control.cpp index 4134357a..62371e4d 100644 --- a/src/p25/Control.cpp +++ b/src/p25/Control.cpp @@ -37,6 +37,7 @@ #include "p25/P25Utils.h" #include "p25/Sync.h" #include "edac/CRC.h" +#include "remote/RESTClient.h" #include "HostMain.h" #include "Log.h" #include "Utils.h" @@ -319,6 +320,28 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw std::unordered_map chData = std::unordered_map(voiceChData); m_affiliations.setRFChData(chData); + // set the grant release callback + m_affiliations.setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { + // callback REST API to clear TG permit for the granted TG on the specified voice channel + if (m_authoritative && m_supervisor) { + ::lookups::VoiceChData voiceChData = m_affiliations.getRFChData(chNo); + if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && + chNo != m_siteData.channelNo()) { + json::object req = json::object(); + int state = modem::DVM_STATE::STATE_P25; + req["state"].set(state); + dstId = 0U; // clear TG value + req["dstId"].set(dstId); + + RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), + HTTP_PUT, PUT_PERMIT_TG, req, m_debug); + } + else { + ::LogError(LOG_P25, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Grant), failed to clear TG permit, chNo = %u", chNo); + } + } + }); + uint32_t ccBcstInterval = p25Protocol["control"]["interval"].as(300U); m_trunk->m_adjSiteUpdateInterval += ccBcstInterval;