From 9b828fe7a4b5f7b1d6519e83977099a70c636c0d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 2 Jun 2024 12:11:15 -0400 Subject: [PATCH] ensure the lookup table thread is named; don't display the not transmitting CC messages unless the test timer is not paused; add aggressive watchdog logging for when timers exceed 1s intervals; split adj. site and affiliation updating into a separate thread (huge adj. site updates could be the cause of main loop processing delays); ensure threads are properly shutdown; increase P25 adj site update timer from 30 seconds to 60 seconds (5 minutes of no updates will now FAIL an adj. site); --- src/common/lookups/LookupTable.h | 1 + src/host/Host.DMR.cpp | 4 +- src/host/Host.NXDN.cpp | 2 +- src/host/Host.P25.cpp | 2 +- src/host/Host.cpp | 215 ++++++++++++++++++++-- src/host/Host.h | 14 ++ src/host/dmr/Control.cpp | 25 ++- src/host/dmr/Control.h | 4 +- src/host/dmr/Slot.cpp | 113 +++++++----- src/host/dmr/Slot.h | 4 +- src/host/nxdn/Control.cpp | 24 ++- src/host/nxdn/Control.h | 9 +- src/host/nxdn/packet/ControlSignaling.cpp | 12 -- src/host/nxdn/packet/ControlSignaling.h | 3 - src/host/p25/Control.cpp | 110 ++++++++--- src/host/p25/Control.h | 9 +- src/host/p25/packet/ControlSignaling.cpp | 55 +----- src/host/p25/packet/ControlSignaling.h | 3 - 18 files changed, 424 insertions(+), 185 deletions(-) diff --git a/src/common/lookups/LookupTable.h b/src/common/lookups/LookupTable.h index 2eef168f..59903283 100644 --- a/src/common/lookups/LookupTable.h +++ b/src/common/lookups/LookupTable.h @@ -91,6 +91,7 @@ namespace lookups if (m_reloadTime > 0U) run(); + setName("host:lookup-tbl"); return ret; } diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp index e48bca21..0db552e9 100644 --- a/src/host/Host.DMR.cpp +++ b/src/host/Host.DMR.cpp @@ -141,7 +141,7 @@ void Host::writeFramesDMR1(dmr::Control* control, std::function&& afterW if (ret) { uint32_t nextLen = control->peekFrameLength(1U); if (m_dmrCtrlChannel) { - if (m_dmrDedicatedTxTestTimer.hasExpired()) { + if (m_dmrDedicatedTxTestTimer.hasExpired() && !m_dmrDedicatedTxTestTimer.isPaused()) { m_dmrDedicatedTxTestTimer.pause(); if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_DMR && (control->getTSCCSlotNo() == 1U) && control->getCCRunning()) { LogError(LOG_HOST, "DMR dedicated control not transmitting, running = %u, halted = %u, frameLength1 = %u", control->getCCRunning(), control->getCCHalted(), nextLen); @@ -273,7 +273,7 @@ void Host::writeFramesDMR2(dmr::Control* control, std::function&& afterW if (ret) { uint32_t nextLen = control->peekFrameLength(1U); if (m_dmrCtrlChannel) { - if (m_dmrDedicatedTxTestTimer.hasExpired()) { + if (m_dmrDedicatedTxTestTimer.hasExpired() && !m_dmrDedicatedTxTestTimer.isPaused()) { m_dmrDedicatedTxTestTimer.pause(); if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_DMR && (control->getTSCCSlotNo() == 2U) && control->getCCRunning()) { LogError(LOG_HOST, "DMR dedicated control not transmitting, running = %u, halted = %u, frameLength2 = %u", control->getCCRunning(), control->getCCHalted(), nextLen); diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp index d3e68ce7..b99c5bb9 100644 --- a/src/host/Host.NXDN.cpp +++ b/src/host/Host.NXDN.cpp @@ -90,7 +90,7 @@ void Host::writeFramesNXDN(nxdn::Control* control, std::function&& after if (ret) { uint32_t nextLen = control->peekFrameLength(); if (m_nxdnCtrlChannel) { - if (m_nxdnDedicatedTxTestTimer.hasExpired()) { + if (m_nxdnDedicatedTxTestTimer.hasExpired() && !m_nxdnDedicatedTxTestTimer.isPaused()) { m_nxdnDedicatedTxTestTimer.pause(); if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_NXDN && control->getCCRunning()) { LogError(LOG_HOST, "NXDN dedicated control stopped transmitting, running = %u, halted = %u, frameLength = %u", control->getCCRunning(), control->getCCHalted(), nextLen); diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp index e770f431..35ed82e5 100644 --- a/src/host/Host.P25.cpp +++ b/src/host/Host.P25.cpp @@ -132,7 +132,7 @@ void Host::writeFramesP25(p25::Control* control, std::function&& afterWr if (control != nullptr) { uint8_t nextLen = control->peekFrameLength(); if (m_p25CtrlChannel) { - if (m_p25DedicatedTxTestTimer.hasExpired()) { + if (m_p25DedicatedTxTestTimer.hasExpired() && !m_p25DedicatedTxTestTimer.isPaused()) { m_p25DedicatedTxTestTimer.pause(); if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_P25 && control->getCCRunning()) { LogError(LOG_HOST, "P25 dedicated control not transmitting, running = %u, halted = %u, frameLength = %u", control->getCCRunning(), control->getCCHalted(), nextLen); diff --git a/src/host/Host.cpp b/src/host/Host.cpp index f4a53908..64edd71f 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -122,6 +122,19 @@ Host::Host(const std::string& confFile) : m_p25DedicatedTxTestTimer(1000U, 0U, 125U), m_nxdnBcastDurationTimer(1000U), m_nxdnDedicatedTxTestTimer(1000U, 0U, 125U), + m_dmrTx1WatchdogTimer(1000U, 1U), + m_dmrTx1LoopMS(0U), + m_dmrTx2WatchdogTimer(1000U, 1U), + m_dmrTx2LoopMS(0U), + m_p25TxWatchdogTimer(1000U, 1U), + m_p25TxLoopMS(0U), + m_nxdnTxWatchdogTimer(1000U, 1U), + m_nxdnTxLoopMS(0U), + m_mainLoopStage(0U), + m_mainLoopMS(0U), + m_mainLoopWatchdogTimer(1000U, 1U), + m_adjSiteLoopMS(0U), + m_adjSiteLoopWatchdogTimer(1000U, 1U), m_activeTickDelay(5U), m_idleTickDelay(5U), m_restAddress("0.0.0.0"), @@ -733,15 +746,102 @@ int Host::run() } \ } - // setup protocol processor threads - /** Digital Mobile Radio */ + /** Watchdog */ + ThreadFunc watchdogThread([&, this]() { + if (g_killed) + return; + + StopWatch stopWatch; + stopWatch.start(); + + LogDebug(LOG_HOST, "started watchdog"); + while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + + // scope is intentional + { + /** Digital Mobile Radio */ + if (dmr != nullptr) { + if (m_dmrTx1WatchdogTimer.isRunning()) + m_dmrTx1WatchdogTimer.clock(ms); + if (m_dmrTx1WatchdogTimer.isRunning() && m_dmrTx1WatchdogTimer.hasExpired() && !m_dmrTx1WatchdogTimer.isPaused()) { + m_dmrTx1WatchdogTimer.pause(); + LogError(LOG_HOST, "DMR, slot 1 frame processor hung >%us, ms = %u", m_dmrTx1WatchdogTimer.getTimeout(), m_dmrTx1LoopMS); + } + + if (m_dmrTx2WatchdogTimer.isRunning()) + m_dmrTx2WatchdogTimer.clock(ms); + if (m_dmrTx2WatchdogTimer.isRunning() && m_dmrTx2WatchdogTimer.hasExpired() && !m_dmrTx2WatchdogTimer.isPaused()) { + m_dmrTx2WatchdogTimer.pause(); + LogError(LOG_HOST, "DMR, slot 2 frame processor hung >%us, ms = %u", m_dmrTx2WatchdogTimer.getTimeout(), m_dmrTx2LoopMS); + } + } + + /** Project 25 */ + if (p25 != nullptr) { + if (m_p25TxWatchdogTimer.isRunning()) + m_p25TxWatchdogTimer.clock(ms); + if (m_p25TxWatchdogTimer.isRunning() && m_p25TxWatchdogTimer.hasExpired() && !m_p25TxWatchdogTimer.isPaused()) { + m_p25TxWatchdogTimer.pause(); + LogError(LOG_HOST, "P25, frame processor hung >%us, ms = %u", m_p25TxWatchdogTimer.getTimeout(), m_p25TxLoopMS); + } + } + + /** Next Generation Digital Narrowband */ + if (nxdn != nullptr) { + if (m_nxdnTxWatchdogTimer.isRunning()) + m_nxdnTxWatchdogTimer.clock(ms); + if (m_nxdnTxWatchdogTimer.isRunning() && m_nxdnTxWatchdogTimer.hasExpired() && !m_nxdnTxWatchdogTimer.isPaused()) { + m_nxdnTxWatchdogTimer.pause(); + LogError(LOG_HOST, "NXDN, frame processor hung >%us, ms = %u", m_nxdnTxWatchdogTimer.getTimeout(), m_nxdnTxLoopMS); + } + } + } + + // scope is intentional + { + if (m_mainLoopWatchdogTimer.isRunning()) + m_mainLoopWatchdogTimer.clock(ms); + if (m_mainLoopWatchdogTimer.isRunning() && m_mainLoopWatchdogTimer.hasExpired() && !m_mainLoopWatchdogTimer.isPaused()) { + m_mainLoopWatchdogTimer.pause(); + LogError(LOG_HOST, "main processor hung >%us, stage = %u, ms = %u", m_mainLoopWatchdogTimer.getTimeout(), m_mainLoopStage, m_mainLoopMS); + } + + if (m_adjSiteLoopWatchdogTimer.isRunning()) + m_adjSiteLoopWatchdogTimer.clock(ms); + if (m_adjSiteLoopWatchdogTimer.isRunning() && m_adjSiteLoopWatchdogTimer.hasExpired() && !m_adjSiteLoopWatchdogTimer.isPaused()) { + m_adjSiteLoopWatchdogTimer.pause(); + LogError(LOG_HOST, "adj. site update hung >%us, ms = %u", m_adjSiteLoopWatchdogTimer.getTimeout(), m_adjSiteLoopMS); + } + } + + if (m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); + } + }); + watchdogThread.run(); + watchdogThread.setName("host:watchdog"); + + /** Digital Mobile Radio Frame Processor */ ThreadFunc dmrFrame1WriteThread([&, this]() { if (g_killed) return; + StopWatch stopWatch; + stopWatch.start(); + if (dmr != nullptr) { LogDebug(LOG_HOST, "DMR, started slot 1 frame processor (modem write)"); while (!g_killed) { + m_dmrTx1WatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + m_dmrTx1LoopMS = ms; + // scope is intentional { std::lock_guard lock(clockingMutex); @@ -782,9 +882,18 @@ int Host::run() if (g_killed) return; + StopWatch stopWatch; + stopWatch.start(); + if (dmr != nullptr) { LogDebug(LOG_HOST, "DMR, started slot 2 frame processor (modem write)"); while (!g_killed) { + m_dmrTx2WatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + m_dmrTx2LoopMS = ms; + // scope is intentional { std::lock_guard lock(clockingMutex); @@ -821,14 +930,23 @@ int Host::run() dmrFrame2WriteThread.run(); dmrFrame2WriteThread.setName("dmr:frame2-w"); - /** Project 25 */ + /** Project 25 Frame Processor */ ThreadFunc p25FrameWriteThread([&, this]() { if (g_killed) return; + StopWatch stopWatch; + stopWatch.start(); + if (p25 != nullptr) { LogDebug(LOG_HOST, "P25, started frame processor (modem write)"); while (!g_killed) { + m_p25TxWatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + m_p25TxLoopMS = ms; + // scope is intentional { std::lock_guard lock(clockingMutex); @@ -862,14 +980,23 @@ int Host::run() p25FrameWriteThread.run(); p25FrameWriteThread.setName("p25:frame-w"); - /** Next Generation Digital Narrowband */ + /** Next Generation Digital Narrowband Frame Processor */ ThreadFunc nxdnFrameWriteThread([&, this]() { if (g_killed) return; + StopWatch stopWatch; + stopWatch.start(); + if (nxdn != nullptr) { LogDebug(LOG_HOST, "NXDN, started frame processor (modem write)"); while (!g_killed) { + m_nxdnTxWatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + m_nxdnTxLoopMS = ms; + // scope is intentional { std::lock_guard lock(clockingMutex); @@ -903,6 +1030,38 @@ int Host::run() nxdnFrameWriteThread.run(); nxdnFrameWriteThread.setName("nxdn:frame-w"); + /** Adjacent Site and Affiliation Update */ + ThreadFunc siteDataUpdateThread([&, this]() { + if (g_killed) + return; + + StopWatch stopWatch; + stopWatch.start(); + + LogDebug(LOG_HOST, "started adj. site and affiliation processor"); + while (!g_killed) { + m_nxdnTxWatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + m_adjSiteLoopMS = ms; + + if (dmr != nullptr) + dmr->clockSiteData(ms); + if (p25 != nullptr) + p25->clockSiteData(ms); + if (nxdn != nullptr) + nxdn->clockSiteData(ms); + + if (m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); + } + }); + siteDataUpdateThread.run(); + siteDataUpdateThread.setName("host:site-data"); + /** Network Presence Notification */ ThreadFunc presenceThread([&, this]() { if (g_killed) @@ -915,6 +1074,7 @@ int Host::run() StopWatch stopWatch; stopWatch.start(); + LogDebug(LOG_HOST, "started presence notifier"); while (!g_killed) { // scope is intentional { @@ -1026,6 +1186,9 @@ int Host::run() } } + m_mainLoopWatchdogTimer.start(); + m_mainLoopStage = 0U; // intentional magic number + // scope is intentional { std::lock_guard lock(clockingMutex); @@ -1038,14 +1201,19 @@ int Host::run() stopWatch.start(); m_modem->clock(ms); + m_mainLoopStage = 1U; // intentional magic number } + m_mainLoopMS = ms; + // ------------------------------------------------------ // -- Read from Modem Processing -- // ------------------------------------------------------ /** Digital Mobile Radio */ if (dmr != nullptr) { + m_mainLoopStage = 2U; // intentional magic number + // read DMR slot 1 frames from modem readFramesDMR1(dmr.get(), [&, this]() { if (dmr != nullptr) { @@ -1091,6 +1259,8 @@ int Host::run() /** Project 25 */ if (p25 != nullptr) { + m_mainLoopStage = 3U; // intentional magic number + // read P25 frames from modem readFramesP25(p25.get(), [&, this]() { if (dmr != nullptr) { @@ -1108,6 +1278,8 @@ int Host::run() /** Next Generation Digital Narrowband */ if (nxdn != nullptr) { + m_mainLoopStage = 4U; // intentional magic number + // read NXDN frames from modem readFramesNXDN(nxdn.get(), [&, this]() { if (dmr != nullptr) { @@ -1127,11 +1299,14 @@ int Host::run() // -- Network, DMR, and P25 Clocking -- // ------------------------------------------------------ - if (m_network != nullptr) + if (m_network != nullptr) { + m_mainLoopStage = 5U; // intentional magic number m_network->clock(ms); + } if (dmr != nullptr) { - dmr->clock(ms); + m_mainLoopStage = 6U; // intentional magic number + dmr->clock(); if (m_dmrCtrlChannel) { if (!m_dmrDedicatedTxTestTimer.isRunning()) { @@ -1143,7 +1318,8 @@ int Host::run() } if (p25 != nullptr) { - p25->clock(ms); + m_mainLoopStage = 7U; // intentional magic number + p25->clock(); if (m_p25CtrlChannel) { if (!m_p25DedicatedTxTestTimer.isRunning()) { @@ -1155,7 +1331,8 @@ int Host::run() } if (nxdn != nullptr) { - nxdn->clock(ms); + m_mainLoopStage = 8U; // intentional magic number + nxdn->clock(); if (m_nxdnCtrlChannel) { if (!m_nxdnDedicatedTxTestTimer.isRunning()) { @@ -1173,6 +1350,7 @@ int Host::run() // clock and check CW timer m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { + m_mainLoopStage = 9U; // intentional magic number if (!m_modem->hasTX() && !m_p25CtrlChannel && !m_dmrCtrlChannel && !m_nxdnCtrlChannel) { if (m_dmrBeaconDurationTimer.isRunning() || m_p25BcastDurationTimer.isRunning() || m_nxdnBcastDurationTimer.isRunning()) { @@ -1222,6 +1400,8 @@ int Host::run() } } + m_mainLoopStage = 10U; // intentional magic number + /** Digial Mobile Radio */ if (dmr != nullptr) { if (m_dmrTSCCData && m_dmrCtrlChannel) { @@ -1411,11 +1591,20 @@ int Host::run() } if (g_killed) { - // shutdown writer threads - dmrFrame1WriteThread.wait(); - dmrFrame2WriteThread.wait(); - p25FrameWriteThread.wait(); - nxdnFrameWriteThread.wait(); + // shutdown helper threads + presenceThread.wait(); + siteDataUpdateThread.wait(); + + if (dmr != nullptr) { + dmrFrame1WriteThread.wait(); + dmrFrame2WriteThread.wait(); + } + if (p25 != nullptr) + p25FrameWriteThread.wait(); + if (nxdn != nullptr) + nxdnFrameWriteThread.wait(); + + watchdogThread.wait(); if (dmr != nullptr) { if (m_state == STATE_DMR && m_duplex && m_modem->hasTX()) { diff --git a/src/host/Host.h b/src/host/Host.h index 5fe8a6c6..fabe0321 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -148,6 +148,20 @@ private: Timer m_p25DedicatedTxTestTimer; Timer m_nxdnBcastDurationTimer; Timer m_nxdnDedicatedTxTestTimer; + + Timer m_dmrTx1WatchdogTimer; + uint32_t m_dmrTx1LoopMS; + Timer m_dmrTx2WatchdogTimer; + uint32_t m_dmrTx2LoopMS; + Timer m_p25TxWatchdogTimer; + uint32_t m_p25TxLoopMS; + Timer m_nxdnTxWatchdogTimer; + uint32_t m_nxdnTxLoopMS; + uint8_t m_mainLoopStage; + uint32_t m_mainLoopMS; + Timer m_mainLoopWatchdogTimer; + uint32_t m_adjSiteLoopMS; + Timer m_adjSiteLoopWatchdogTimer; uint8_t m_activeTickDelay; uint8_t m_idleTickDelay; diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index c221b589..ffb0d015 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -371,29 +371,28 @@ uint32_t Control::getFrame(uint32_t slotNo, uint8_t* data) } /// -/// Updates the processor by the passed number of milliseconds. +/// Updates the processor. /// -/// -void Control::clock(uint32_t ms) +void Control::clock() { if (m_network != nullptr) { processNetwork(); } - m_tsccCntInterval.clock(ms); - if (m_tsccCntInterval.isRunning() && m_tsccCntInterval.hasExpired()) { - m_tsccCnt++; - if (m_tsccCnt == TSCC_MAX_CSC_CNT) { - m_tsccCnt = 0U; - } - - m_tsccCntInterval.start(); - } - m_slot1->clock(); m_slot2->clock(); } +/// +/// Updates the adj. site tables. +/// +/// +void Control::clockSiteData(uint32_t ms) +{ + m_slot1->clockSiteData(ms); + m_slot2->clockSiteData(ms); +} + /// /// Sets a flag indicating whether DMR has supervisory functions and can send permit TG to voice channels. /// diff --git a/src/host/dmr/Control.h b/src/host/dmr/Control.h index d1377a56..17ca96f2 100644 --- a/src/host/dmr/Control.h +++ b/src/host/dmr/Control.h @@ -75,7 +75,9 @@ namespace dmr uint32_t getFrame(uint32_t slotNo, uint8_t* data); /// Updates the processor. - void clock(uint32_t ms); + void clock(); + /// Updates the adj. site tables. + void clockSiteData(uint32_t ms); /// Sets a flag indicating whether DMR has supervisory functions and can send permit TG to voice channels. void setSupervisor(bool supervisor); diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index e34929af..7f742571 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -469,10 +469,17 @@ void Slot::clock() // if we have control enabled; do clocking to generate a CC data stream if (m_enableTSCC) { - m_modem->setDMRIgnoreCACH_AT(m_slotNo); + m_dmr->m_tsccCntInterval.clock(ms); + if (m_dmr->m_tsccCntInterval.isRunning() && m_dmr->m_tsccCntInterval.hasExpired()) { + m_dmr->m_tsccCnt++; + if (m_dmr->m_tsccCnt == TSCC_MAX_CSC_CNT) { + m_dmr->m_tsccCnt = 0U; + } - // clock all the grant timers - m_affiliations->clock(ms); + m_dmr->m_tsccCntInterval.start(); + } + + m_modem->setDMRIgnoreCACH_AT(m_slotNo); if (m_ccRunning && !m_ccPacketInterval.isRunning()) { m_ccPacketInterval.start(); @@ -515,50 +522,6 @@ void Slot::clock() } } - // do we need to network announce ourselves? - if (!m_adjSiteUpdateTimer.isRunning()) { - m_control->writeAdjSSNetwork(); - m_adjSiteUpdateTimer.start(); - } - - m_adjSiteUpdateTimer.clock(ms); - if (m_adjSiteUpdateTimer.isRunning() && m_adjSiteUpdateTimer.hasExpired()) { - if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) { - m_control->writeAdjSSNetwork(); - if (m_network != nullptr) { - if (m_affiliations->grpAffSize() > 0) { - auto affs = m_affiliations->grpAffTable(); - m_network->announceAffiliationUpdate(affs); - } - } - m_adjSiteUpdateTimer.start(); - } - } - - // clock adjacent site and SCCB update timers - m_adjSiteUpdateTimer.clock(ms); - if (m_adjSiteUpdateTimer.isRunning() && m_adjSiteUpdateTimer.hasExpired()) { - // update adjacent site data - for (auto& entry : m_adjSiteUpdateCnt) { - uint8_t siteId = entry.first; - uint8_t updateCnt = entry.second; - if (updateCnt > 0U) { - updateCnt--; - } - - if (updateCnt == 0U) { - AdjSiteData siteData = m_adjSiteTable[siteId]; - LogWarning(LOG_NET, "DMR, Adjacent Site Status Expired, no data [FAILED], sysId = $%03X, chNo = %u", - siteData.systemIdentity, siteData.channelNo); - } - - entry.second = updateCnt; - } - - m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval); - m_adjSiteUpdateTimer.start(); - } - if (m_ccPrevRunning && !m_ccRunning) { m_txQueue.clear(); // clear the frame buffer m_ccPrevRunning = m_ccRunning; @@ -709,6 +672,62 @@ void Slot::clock() } } +/// +/// Updates the adj. site tables. +/// +/// +void Slot::clockSiteData(uint32_t ms) +{ + if (m_enableTSCC) { + // clock all the grant timers + m_affiliations->clock(ms); + + // do we need to network announce ourselves? + if (!m_adjSiteUpdateTimer.isRunning()) { + m_control->writeAdjSSNetwork(); + m_adjSiteUpdateTimer.start(); + } + + m_adjSiteUpdateTimer.clock(ms); + if (m_adjSiteUpdateTimer.isRunning() && m_adjSiteUpdateTimer.hasExpired()) { + if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) { + m_control->writeAdjSSNetwork(); + if (m_network != nullptr) { + if (m_affiliations->grpAffSize() > 0) { + auto affs = m_affiliations->grpAffTable(); + m_network->announceAffiliationUpdate(affs); + } + } + m_adjSiteUpdateTimer.start(); + } + } + + // clock adjacent site update timers + m_adjSiteUpdateTimer.clock(ms); + if (m_adjSiteUpdateTimer.isRunning() && m_adjSiteUpdateTimer.hasExpired()) { + // update adjacent site data + for (auto& entry : m_adjSiteUpdateCnt) { + uint8_t siteId = entry.first; + uint8_t updateCnt = entry.second; + if (updateCnt > 0U) { + updateCnt--; + } + + if (updateCnt == 0U) { + AdjSiteData siteData = m_adjSiteTable[siteId]; + LogWarning(LOG_NET, "DMR, Adjacent Site Status Expired, no data [FAILED], sysId = $%03X, chNo = %u", + siteData.systemIdentity, siteData.channelNo); + } + + entry.second = updateCnt; + } + + m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval); + m_adjSiteUpdateTimer.start(); + } + } +} + /// /// Permits a TGID on a non-authoritative host. /// diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 89b59908..def929e3 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -93,8 +93,10 @@ namespace dmr /// Process a data frames from the network. void processNetwork(const data::Data& data); - /// Updates the slot processor. + /// Updates the DMR slot processor. void clock(); + /// Updates the adj. site tables. + void clockSiteData(uint32_t ms); /// Permits a TGID on a non-authoritative host. void permittedTG(uint32_t dstId); diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 40bae491..4145331a 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -116,6 +116,7 @@ Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t q m_netTGHang(1000U, 2U), m_networkWatchdog(1000U, 0U, 1500U), m_ccPacketInterval(1000U, 0U, 80U), + m_interval(), m_frameLossCnt(0U), m_frameLossThreshold(DEFAULT_FRAME_LOSS_THRESHOLD), m_ccFrameCnt(0U), @@ -138,6 +139,8 @@ Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t q assert(idenTable != nullptr); assert(rssiMapper != nullptr); + m_interval.start(); + acl::AccessControl::init(m_ridLookup, m_tidLookup); m_voice = new Voice(this, debug, verbose); @@ -530,11 +533,13 @@ uint32_t Control::getFrame(uint8_t* data) } /// -/// Updates the processor by the passed number of milliseconds. +/// Updates the processor. /// -/// -void Control::clock(uint32_t ms) +void Control::clock() { + uint32_t ms = m_interval.elapsed(); + m_interval.start(); + if (m_network != nullptr) { processNetwork(); @@ -701,10 +706,17 @@ void Control::clock(uint32_t ms) if (m_frameLossCnt >= m_frameLossThreshold && (m_rfState == RS_RF_AUDIO || m_rfState == RS_RF_DATA)) { processFrameLoss(); } +} - // clock data and trunking - if (m_control != nullptr) { - m_control->clock(ms); +/// +/// Updates the adj. site tables and affiliations. +/// +/// +void Control::clockSiteData(uint32_t ms) +{ + if (m_enableControl) { + // clock all the grant timers + m_affiliations.clock(ms); } } diff --git a/src/host/nxdn/Control.h b/src/host/nxdn/Control.h index cbb18fbe..5c6adc42 100644 --- a/src/host/nxdn/Control.h +++ b/src/host/nxdn/Control.h @@ -26,6 +26,7 @@ #include "common/lookups/TalkgroupRulesLookup.h" #include "common/lookups/AffiliationLookup.h" #include "common/RingBuffer.h" +#include "common/StopWatch.h" #include "common/Timer.h" #include "common/yaml/Yaml.h" #include "nxdn/packet/Voice.h" @@ -85,8 +86,10 @@ namespace nxdn /// Get frame data from data ring buffer. uint32_t getFrame(uint8_t* data); - /// Updates the processor by the passed number of milliseconds. - void clock(uint32_t ms); + /// Updates the processor. + void clock(); + /// Updates the adj. site tables and affiliations. + void clockSiteData(uint32_t ms); /// Sets a flag indicating whether NXDN has supervisory functions and can send permit TG to voice channels. void setSupervisor(bool supervisor) { m_supervisor = supervisor; } @@ -184,6 +187,8 @@ namespace nxdn Timer m_ccPacketInterval; + StopWatch m_interval; + uint8_t m_frameLossCnt; uint8_t m_frameLossThreshold; diff --git a/src/host/nxdn/packet/ControlSignaling.cpp b/src/host/nxdn/packet/ControlSignaling.cpp index 81704693..3e128445 100644 --- a/src/host/nxdn/packet/ControlSignaling.cpp +++ b/src/host/nxdn/packet/ControlSignaling.cpp @@ -291,18 +291,6 @@ bool ControlSignaling::processNetwork(uint8_t fct, uint8_t option, lc::RTCH& net return true; } -/// -/// Updates the processor by the passed number of milliseconds. -/// -/// -void ControlSignaling::clock(uint32_t ms) -{ - if (m_nxdn->m_enableControl) { - // clock all the grant timers - m_nxdn->m_affiliations.clock(ms); - } -} - // --------------------------------------------------------------------------- // Protected Class Members // --------------------------------------------------------------------------- diff --git a/src/host/nxdn/packet/ControlSignaling.h b/src/host/nxdn/packet/ControlSignaling.h index 454c81e7..79db92ab 100644 --- a/src/host/nxdn/packet/ControlSignaling.h +++ b/src/host/nxdn/packet/ControlSignaling.h @@ -44,9 +44,6 @@ namespace nxdn /// Process a data frame from the network. bool processNetwork(uint8_t fct, uint8_t option, lc::RTCH& netLC, uint8_t* data, uint32_t len); - /// Updates the processor by the passed number of milliseconds. - void clock(uint32_t ms); - protected: friend class nxdn::packet::Data; friend class nxdn::packet::Voice; diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 8197cf5b..e6829c4a 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -118,6 +118,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q m_networkWatchdog(1000U, 0U, 1500U), m_adjSiteUpdate(1000U, 75U), m_ccPacketInterval(1000U, 0U, 10U), + m_interval(), m_hangCount(3U * 8U), m_tduPreambleCount(8U), m_frameLossCnt(0U), @@ -147,6 +148,8 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q assert(idenTable != nullptr); assert(rssiMapper != nullptr); + m_interval.start(); + acl::AccessControl::init(m_ridLookup, m_tidLookup); m_hangCount = callHang * 4U; @@ -753,11 +756,13 @@ bool Control::writeRF_VoiceEnd() } /// -/// Updates the processor by the passed number of milliseconds. +/// Updates the processor. /// -/// -void Control::clock(uint32_t ms) +void Control::clock() { + uint32_t ms = m_interval.elapsed(); + m_interval.start(); + if (m_network != nullptr) { processNetwork(); @@ -803,26 +808,6 @@ void Control::clock(uint32_t ms) writeRF_ControlEnd(); m_ccPrevRunning = m_ccRunning; } - - // do we need to network announce ourselves? - if (!m_adjSiteUpdate.isRunning()) { - m_control->writeAdjSSNetwork(); - 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_control->writeAdjSSNetwork(); - if (m_network != nullptr) { - if (m_affiliations.grpAffSize() > 0) { - auto affs = m_affiliations.grpAffTable(); - m_network->announceAffiliationUpdate(affs); - } - } - m_adjSiteUpdate.start(); - } - } } // handle timeouts and hang timers @@ -933,9 +918,86 @@ void Control::clock(uint32_t ms) if (m_data != nullptr) { m_data->clock(ms); } +} + +/// +/// Updates the adj. site tables and affiliations. +/// +/// +void Control::clockSiteData(uint32_t ms) +{ + if (m_enableControl) { + // clock all the grant timers + m_affiliations.clock(ms); + } if (m_control != nullptr) { - m_control->clock(ms); + // do we need to network announce ourselves? + if (!m_adjSiteUpdate.isRunning()) { + m_control->writeAdjSSNetwork(); + 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_control->writeAdjSSNetwork(); + if (m_network != nullptr) { + if (m_affiliations.grpAffSize() > 0) { + auto affs = m_affiliations.grpAffTable(); + m_network->announceAffiliationUpdate(affs); + } + } + m_adjSiteUpdate.start(); + } + } + + if (m_enableControl) { + // clock adjacent site and SCCB update timers + m_control->m_adjSiteUpdateTimer.clock(ms); + if (m_control->m_adjSiteUpdateTimer.isRunning() && m_control->m_adjSiteUpdateTimer.hasExpired()) { + if (m_control->m_adjSiteUpdateCnt.size() > 0) { + // update adjacent site data + for (auto &entry : m_control->m_adjSiteUpdateCnt) { + uint8_t siteId = entry.first; + uint8_t updateCnt = entry.second; + if (updateCnt > 0U) { + updateCnt--; + } + + if (updateCnt == 0U) { + SiteData siteData = m_control->m_adjSiteTable[siteId]; + LogWarning(LOG_NET, "P25, Adjacent Site Status Expired, no data [FAILED], sysId = $%03X, rfss = $%02X, site = $%02X, chId = %u, chNo = %u, svcClass = $%02X", + siteData.sysId(), siteData.rfssId(), siteData.siteId(), siteData.channelId(), siteData.channelNo(), siteData.serviceClass()); + } + + entry.second = updateCnt; + } + } + + if (m_control->m_sccbUpdateCnt.size() > 0) { + // update SCCB data + for (auto& entry : m_control->m_sccbUpdateCnt) { + uint8_t rfssId = entry.first; + uint8_t updateCnt = entry.second; + if (updateCnt > 0U) { + updateCnt--; + } + + if (updateCnt == 0U) { + SiteData siteData = m_control->m_sccbTable[rfssId]; + LogWarning(LOG_NET, "P25, Secondary Control Channel Expired, no data [FAILED], sysId = $%03X, rfss = $%02X, site = $%02X, chId = %u, chNo = %u, svcClass = $%02X", + siteData.sysId(), siteData.rfssId(), siteData.siteId(), siteData.channelId(), siteData.channelNo(), siteData.serviceClass()); + } + + entry.second = updateCnt; + } + } + + m_control->m_adjSiteUpdateTimer.setTimeout(m_control->m_adjSiteUpdateInterval); + m_control->m_adjSiteUpdateTimer.start(); + } + } } } diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index 80b80a4d..8856ee2d 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -23,6 +23,7 @@ #include "common/lookups/TalkgroupRulesLookup.h" #include "common/p25/SiteData.h" #include "common/RingBuffer.h" +#include "common/StopWatch.h" #include "common/Timer.h" #include "common/yaml/Yaml.h" #include "p25/packet/Data.h" @@ -89,8 +90,10 @@ namespace p25 /// Helper to write end of voice call frame data. bool writeRF_VoiceEnd(); - /// Updates the processor by the passed number of milliseconds. - void clock(uint32_t ms); + /// Updates the processor. + void clock(); + /// Updates the adj. site tables and affiliations. + void clockSiteData(uint32_t ms); /// Sets a flag indicating whether P25 has supervisory functions and can send permit TG to voice channels. void setSupervisor(bool supervisor) { m_supervisor = supervisor; } @@ -195,6 +198,8 @@ namespace p25 Timer m_ccPacketInterval; + StopWatch m_interval; + uint32_t m_hangCount; uint32_t m_tduPreambleCount; diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index 745c504a..3893b88c 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -148,7 +148,7 @@ using namespace p25::packet; // Constants // --------------------------------------------------------------------------- -const uint32_t ADJ_SITE_TIMER_TIMEOUT = 30U; +const uint32_t ADJ_SITE_TIMER_TIMEOUT = 60U; const uint32_t ADJ_SITE_UPDATE_CNT = 5U; const uint32_t TSDU_CTRL_BURST_COUNT = 2U; const uint32_t TSBK_MBF_CNT = 3U; @@ -997,59 +997,6 @@ void ControlSignaling::writeAdjSSNetwork() } } -/// -/// Updates the processor by the passed number of milliseconds. -/// -/// -void ControlSignaling::clock(uint32_t ms) -{ - if (m_p25->m_enableControl) { - // clock all the grant timers - m_p25->m_affiliations.clock(ms); - - // clock adjacent site and SCCB update timers - m_adjSiteUpdateTimer.clock(ms); - if (m_adjSiteUpdateTimer.isRunning() && m_adjSiteUpdateTimer.hasExpired()) { - // update adjacent site data - for (auto& entry : m_adjSiteUpdateCnt) { - uint8_t siteId = entry.first; - uint8_t updateCnt = entry.second; - if (updateCnt > 0U) { - updateCnt--; - } - - if (updateCnt == 0U) { - SiteData siteData = m_adjSiteTable[siteId]; - LogWarning(LOG_NET, "P25, Adjacent Site Status Expired, no data [FAILED], sysId = $%03X, rfss = $%02X, site = $%02X, chId = %u, chNo = %u, svcClass = $%02X", - siteData.sysId(), siteData.rfssId(), siteData.siteId(), siteData.channelId(), siteData.channelNo(), siteData.serviceClass()); - } - - entry.second = updateCnt; - } - - // update SCCB data - for (auto& entry : m_sccbUpdateCnt) { - uint8_t rfssId = entry.first; - uint8_t updateCnt = entry.second; - if (updateCnt > 0U) { - updateCnt--; - } - - if (updateCnt == 0U) { - SiteData siteData = m_sccbTable[rfssId]; - LogWarning(LOG_NET, "P25, Secondary Control Channel Expired, no data [FAILED], sysId = $%03X, rfss = $%02X, site = $%02X, chId = %u, chNo = %u, svcClass = $%02X", - siteData.sysId(), siteData.rfssId(), siteData.siteId(), siteData.channelId(), siteData.channelNo(), siteData.serviceClass()); - } - - entry.second = updateCnt; - } - - m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval); - m_adjSiteUpdateTimer.start(); - } - } -} - /// /// Helper to write a call alert packet. /// diff --git a/src/host/p25/packet/ControlSignaling.h b/src/host/p25/packet/ControlSignaling.h index 6936134f..e381b6d7 100644 --- a/src/host/p25/packet/ControlSignaling.h +++ b/src/host/p25/packet/ControlSignaling.h @@ -58,9 +58,6 @@ namespace p25 /// Helper to write P25 adjacent site information to the network. void writeAdjSSNetwork(); - /// Updates the processor by the passed number of milliseconds. - void clock(uint32_t ms); - /// Helper to write a call alert packet. void writeRF_TSDU_Call_Alrt(uint32_t srcId, uint32_t dstId); /// Helper to write a radio monitor packet.