You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dvmhost/src/dmr/lookups/DMRAffiliationLookup.cpp

326 lines
8.5 KiB

/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "dmr/lookups/DMRAffiliationLookup.h"
#include "Log.h"
using namespace dmr::lookups;
#include <cassert>
#include <cstdio>
#include <cstring>
#include <ctime>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRAffiliationLookup class.
/// </summary>
/// <param name="verbose">Flag indicating whether verbose logging is enabled.</param>
DMRAffiliationLookup::DMRAffiliationLookup(bool verbose) : ::lookups::AffiliationLookup("DMR Affiliation", verbose),
m_grantChSlotTable(),
m_tsccChNo(0U),
m_tsccSlot(0U),
m_releaseGrant(nullptr)
{
/* stub */
}
/// <summary>
/// Finalizes a instance of the DMRAffiliationLookup class.
/// </summary>
DMRAffiliationLookup::~DMRAffiliationLookup()
{
/* stub */
}
/// <summary>
/// Helper to grant a channel.
/// </summary>
/// <param name="dstId"></param>
/// <param name="srcId"></param>
/// <param name="grantTimeout"></param>
/// <returns></returns>
bool DMRAffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTimeout)
{
uint32_t chNo = m_rfChTable.at(0);
uint8_t slot = getAvailableSlotForChannel(chNo);
if (slot == 0U) {
return false;
}
return grantChSlot(dstId, srcId, slot, grantTimeout);
}
/// <summary>
/// Helper to grant a channel and slot.
/// </summary>
/// <param name="dstId"></param>
/// <param name="srcId"></param>
/// <param name="slot"></param>
/// <param name="grantTimeout"></param>
/// <returns></returns>
bool DMRAffiliationLookup::grantChSlot(uint32_t dstId, uint32_t srcId, uint8_t slot, uint32_t grantTimeout)
{
if (dstId == 0U) {
return false;
}
if (!isRFChAvailable()) {
return false;
}
uint32_t chNo = m_rfChTable.at(0);
if (chNo == m_tsccChNo && slot == m_tsccSlot) {
return false;
}
if (getAvailableSlotForChannel(chNo) == 0U || chNo == m_tsccChNo) {
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo);
m_rfChTable.erase(it);
}
m_grantChTable[dstId] = chNo;
m_grantSrcIdTable[dstId] = srcId;
m_grantChSlotTable[dstId] = std::make_tuple(chNo, slot);
m_rfGrantChCnt++;
m_grantTimers[dstId] = Timer(1000U, grantTimeout);
m_grantTimers[dstId].start();
if (m_verbose) {
LogMessage(LOG_HOST, "%s, granting channel, chNo = %u, slot = %u, dstId = %u",
m_name, chNo, slot, dstId);
}
return true;
}
/// <summary>
/// Helper to release the channel grant for the destination ID.
/// </summary>
/// <param name="dstId"></param>
/// <param name="releaseAll"></param>
bool DMRAffiliationLookup::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_HOST, "%s, force releasing all channel grants", m_name);
std::vector<uint32_t> gntsToRel = std::vector<uint32_t>();
for (auto entry : m_grantChTable) {
uint32_t dstId = entry.first;
gntsToRel.push_back(dstId);
}
// release grants
for (uint32_t dstId : gntsToRel) {
releaseGrant(dstId, false);
}
return true;
}
if (isGranted(dstId)) {
uint32_t chNo = m_grantChTable.at(dstId);
std::tuple<uint32_t, uint8_t> slotData = m_grantChSlotTable.at(dstId);
uint8_t slot = std::get<1>(slotData);
if (m_verbose) {
LogMessage(LOG_HOST, "%s, releasing channel grant, chNo = %u, slot = %u, dstId = %u",
m_name, chNo, slot, dstId);
}
if (m_releaseGrant != nullptr) {
m_releaseGrant(chNo, dstId, slot);
}
m_grantChTable.erase(dstId);
m_grantSrcIdTable.erase(dstId);
m_grantChSlotTable.erase(dstId);
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo);
if (it == m_rfChTable.end()) {
m_rfChTable.push_back(chNo);
}
if (m_rfGrantChCnt > 0U) {
m_rfGrantChCnt--;
}
else {
m_rfGrantChCnt = 0U;
}
m_grantTimers[dstId].stop();
return true;
}
return false;
}
/// <summary>
/// Helper to determine if the channel number is busy.
/// </summary>
/// <param name="chNo"></param>
/// <returns></returns>
bool DMRAffiliationLookup::isChBusy(uint32_t chNo) const
{
if (chNo == 0U) {
return false;
}
// lookup dynamic channel grant table entry
for (auto grantEntry : m_grantChTable) {
if (grantEntry.second == chNo) {
uint8_t slotCount = 0U;
if (chNo == m_tsccChNo) {
slotCount++; // one slot is *always* used for TSCC
}
for (auto slotEntry : m_grantChSlotTable) {
uint32_t foundChNo = std::get<0>(slotEntry.second);
if (foundChNo == chNo)
slotCount++;
}
if (slotCount == 2U) {
return true;
} else {
return false;
}
}
}
return false;
}
/// <summary>
/// Helper to get the slot granted for the given destination ID.
/// </summary>
/// <param name="dstId"></param>
/// <returns></returns>
uint8_t DMRAffiliationLookup::getGrantedSlot(uint32_t dstId) const
{
if (dstId == 0U) {
return 0U;
}
// lookup dynamic channel grant table entry
for (auto entry : m_grantChSlotTable) {
if (entry.first == dstId) {
uint8_t slot = std::get<1>(entry.second);
return slot;
}
}
return 0U;
}
/// <summary>
/// Helper to set a slot for the given channel as being the TSCC.
/// </summary>
/// <param name="chNo"></param>
/// <param name="slot"></param>
/// <returns></returns>
void DMRAffiliationLookup::setSlotForChannelTSCC(uint32_t chNo, uint8_t slot)
{
assert(chNo != 0U);
if ((slot == 0U) || (slot > 2U)) {
return;
}
m_tsccChNo = chNo;
m_tsccSlot = slot;
}
/// <summary>
/// Helper to determine the first available slot for given the channel number.
/// </summary>
/// <param name="chNo"></param>
/// <returns></returns>
uint8_t DMRAffiliationLookup::getAvailableSlotForChannel(uint32_t chNo) const
{
if (chNo == 0U) {
return 0U;
}
uint8_t slot = 1U;
// lookup dynamic channel slot grant table entry
bool grantedSlot = false;
int slotCount = 0U;
for (auto entry : m_grantChSlotTable) {
uint32_t foundChNo = std::get<0>(entry.second);
if (foundChNo == chNo)
{
uint8_t foundSlot = std::get<1>(entry.second);
if (slot == foundSlot) {
switch (foundSlot) {
case 1U:
slot = 2U;
break;
case 2U:
slot = 1U;
break;
}
grantedSlot = true;
slotCount++;
}
}
}
if (slotCount == 2U) {
slot = 0U;
return slot;
}
// are we trying to assign the TSCC slot?
if (chNo == m_tsccChNo && slot == m_tsccSlot) {
if (!grantedSlot) {
// since we didn't find a slot being granted out -- utilize the slot opposing the TSCC
switch (m_tsccSlot) {
case 1U:
slot = 2U;
break;
case 2U:
slot = 1U;
break;
}
} else {
slot = 0U; // TSCC is not assignable
}
}
return slot;
}

Powered by TurnKey Linux.