make the network presence announcment (VC -> CC and CC -> FNE) timing configurable (this value is in seconds); refactor RF channel enumeration, RF channels are now enumerated in their own class ChannelLookup instead of being integrated into AffiliationLookup, this allows the flexibility to update and change channel information at runtime; add support for VC -> CC presence/registration to tell the CC what the REST information is for that VC, this makes the restAddress and restPort entries for the voiceChNo list in the config.yml optional, the only mandatory fields are channelId, channelNo and restPassword;

pull/51/head
Bryan Biedenkapp 2 years ago
parent f5a5d83f4b
commit e0b6da51fb

@ -372,6 +372,9 @@ system:
restSsl: false restSsl: false
# Flag indicating voice channels will notify the control channel of traffic status. # Flag indicating voice channels will notify the control channel of traffic status.
notifyEnable: true notifyEnable: true
# Amount of time between network presence announcements. (seconds)
# NOTE: This value applies to VC -> CC and CC -> FNE presence notification messages.
presence: 120
# #
# Voice Channels # Voice Channels

@ -7,7 +7,7 @@
* @package DVM / Common Library * @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "lookups/AffiliationLookup.h" #include "lookups/AffiliationLookup.h"
@ -15,6 +15,8 @@
using namespace lookups; using namespace lookups;
#include <cassert>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -23,10 +25,9 @@ using namespace lookups;
/// Initializes a new instance of the AffiliationLookup class. /// Initializes a new instance of the AffiliationLookup class.
/// </summary> /// </summary>
/// <param name="name">Name of lookup table.</param> /// <param name="name">Name of lookup table.</param>
/// <param name="channelLookup">Instance of the channel lookup class.</param>
/// <param name="verbose">Flag indicating whether verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether verbose logging is enabled.</param>
AffiliationLookup::AffiliationLookup(const std::string name, bool verbose) : AffiliationLookup::AffiliationLookup(const std::string name, ChannelLookup* channelLookup, bool verbose) :
m_rfChTable(),
m_rfChDataTable(),
m_rfGrantChCnt(0U), m_rfGrantChCnt(0U),
m_unitRegTable(), m_unitRegTable(),
m_grpAffTable(), m_grpAffTable(),
@ -37,11 +38,12 @@ AffiliationLookup::AffiliationLookup(const std::string name, bool verbose) :
m_grantTimers(), m_grantTimers(),
m_releaseGrant(nullptr), m_releaseGrant(nullptr),
m_name(), m_name(),
m_chLookup(channelLookup),
m_verbose(verbose) m_verbose(verbose)
{ {
m_name = name; assert(channelLookup != nullptr);
m_rfChTable.clear(); m_name = name;
m_unitRegTable.clear(); m_unitRegTable.clear();
m_grpAffTable.clear(); m_grpAffTable.clear();
@ -268,13 +270,12 @@ bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTi
return false; return false;
} }
if (!isRFChAvailable()) { if (!m_chLookup->isRFChAvailable()) {
return false; return false;
} }
uint32_t chNo = m_rfChTable.at(0); uint32_t chNo = m_chLookup->getFirstRFChannel();
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo); m_chLookup->removeRFCh(chNo);
m_rfChTable.erase(it);
m_grantChTable[dstId] = chNo; m_grantChTable[dstId] = chNo;
m_grantSrcIdTable[dstId] = srcId; m_grantSrcIdTable[dstId] = srcId;
@ -355,7 +356,7 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll)
m_grantSrcIdTable.erase(dstId); m_grantSrcIdTable.erase(dstId);
m_uuGrantedTable.erase(dstId); m_uuGrantedTable.erase(dstId);
m_netGrantedTable.erase(dstId); m_netGrantedTable.erase(dstId);
m_rfChTable.push_back(chNo); m_chLookup->addRFCh(chNo, true);
if (m_rfGrantChCnt > 0U) { if (m_rfGrantChCnt > 0U) {
m_rfGrantChCnt--; m_rfGrantChCnt--;
@ -514,27 +515,6 @@ uint32_t AffiliationLookup::getGrantedSrcId(uint32_t dstId)
return 0U; return 0U;
} }
/// <summary>
/// Helper to get RF channel data.
/// </summary>
/// <param name="chNo"></param>
/// <returns></returns>
VoiceChData AffiliationLookup::getRFChData(uint32_t chNo) const
{
if (chNo == 0U) {
return VoiceChData();
}
VoiceChData data;
try {
data = m_rfChDataTable.at(chNo);
} catch (...) {
data = VoiceChData();
}
return data;
}
/// <summary> /// <summary>
/// Updates the processor by the passed number of milliseconds. /// Updates the processor by the passed number of milliseconds.
/// </summary> /// </summary>

@ -14,6 +14,7 @@
#define __AFFILIATION_LOOKUP_H__ #define __AFFILIATION_LOOKUP_H__
#include "common/Defines.h" #include "common/Defines.h"
#include "common/lookups/ChannelLookup.h"
#include "common/Timer.h" #include "common/Timer.h"
#include <cstdio> #include <cstdio>
@ -24,79 +25,6 @@
namespace lookups namespace lookups
{ {
// ---------------------------------------------------------------------------
// Class Declaration
// Represents voice channel data.
// ---------------------------------------------------------------------------
class HOST_SW_API VoiceChData {
public:
/// <summary>Initializes a new instance of the VoiceChData class.</summary>
VoiceChData() :
m_chId(0U),
m_chNo(0U),
m_address(),
m_port(),
m_password(),
m_ssl()
{
/* stub */
}
/// <summary>Initializes a new instance of the VoiceChData class.</summary>
/// <param name="chId">Voice Channel Identity.</param>
/// <param name="chNo">Voice Channel Number.</param>
/// <param name="address">REST API Address.</param>
/// <param name="port">REST API Port.</param>
/// <param name="password">REST API Password.</param>
/// <param name="ssl">Flag indicating REST is using SSL.</param>
VoiceChData(uint8_t chId, uint32_t chNo, std::string address, uint16_t port, std::string password, bool ssl) :
m_chId(chId),
m_chNo(chNo),
m_address(address),
m_port(port),
m_password(password),
m_ssl(ssl)
{
/* stub */
}
/// <summary>Equals operator.</summary>
/// <param name="data"></param>
/// <returns></returns>
VoiceChData & operator=(const VoiceChData & data)
{
if (this != &data) {
m_chId = data.m_chId;
m_chNo = data.m_chNo;
m_address = data.m_address;
m_port = data.m_port;
m_password = data.m_password;
m_ssl = data.m_ssl;
}
return *this;
}
/// <summary>Helper to determine if the channel identity is valid.</summary>
bool isValidChId() const { return m_chId != 0U; }
/// <summary>Helper to determine if the channel is valid.</summary>
bool isValidCh() const { return m_chNo != 0U; }
public:
/// <summary>Voice Channel Identity.</summary>
__READONLY_PROPERTY_PLAIN(uint8_t, chId);
/// <summary>Voice Channel Number.</summary>
__READONLY_PROPERTY_PLAIN(uint32_t, chNo);
/// <summary>REST API Address.</summary>
__READONLY_PROPERTY_PLAIN(std::string, address);
/// <summary>REST API Port.</summary>
__READONLY_PROPERTY_PLAIN(uint16_t, port);
/// <summary>REST API Password.</summary>
__READONLY_PROPERTY_PLAIN(std::string, password);
/// <summary>Flag indicating REST is using SSL.</summary>
__READONLY_PROPERTY_PLAIN(bool, ssl);
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements a lookup table class that contains subscriber registration // Implements a lookup table class that contains subscriber registration
@ -106,10 +34,11 @@ namespace lookups
class HOST_SW_API AffiliationLookup { class HOST_SW_API AffiliationLookup {
public: public:
/// <summary>Initializes a new instance of the AffiliationLookup class.</summary> /// <summary>Initializes a new instance of the AffiliationLookup class.</summary>
AffiliationLookup(const std::string name, bool verbose); AffiliationLookup(const std::string name, ChannelLookup* chLookup, bool verbose);
/// <summary>Finalizes a instance of the AffiliationLookup class.</summary> /// <summary>Finalizes a instance of the AffiliationLookup class.</summary>
virtual ~AffiliationLookup(); virtual ~AffiliationLookup();
/** Unit Registrations */
/// <summary>Gets the count of unit registrations.</summary> /// <summary>Gets the count of unit registrations.</summary>
uint8_t unitRegSize() const { return m_unitRegTable.size(); } uint8_t unitRegSize() const { return m_unitRegTable.size(); }
/// <summary>Gets the unit registration table.</summary> /// <summary>Gets the unit registration table.</summary>
@ -123,6 +52,7 @@ namespace lookups
/// <summary>Helper to release unit registrations.</summary> /// <summary>Helper to release unit registrations.</summary>
virtual void clearUnitReg(); virtual void clearUnitReg();
/** Group Affiliations */
/// <summary>Gets the count of affiliations.</summary> /// <summary>Gets the count of affiliations.</summary>
uint8_t grpAffSize() const { return m_grpAffTable.size(); } uint8_t grpAffSize() const { return m_grpAffTable.size(); }
/// <summary>Gets the group affiliation table.</summary> /// <summary>Gets the group affiliation table.</summary>
@ -138,6 +68,7 @@ namespace lookups
/// <summary>Helper to release group affiliations.</summary> /// <summary>Helper to release group affiliations.</summary>
virtual std::vector<uint32_t> clearGroupAff(uint32_t dstId, bool releaseAll); virtual std::vector<uint32_t> clearGroupAff(uint32_t dstId, bool releaseAll);
/** Channel Grants */
/// <summary>Gets the count of grants.</summary> /// <summary>Gets the count of grants.</summary>
uint8_t grantSize() const { return m_grantChTable.size(); } uint8_t grantSize() const { return m_grantChTable.size(); }
/// <summary>Gets the grant table.</summary> /// <summary>Gets the grant table.</summary>
@ -162,23 +93,12 @@ namespace lookups
virtual uint32_t getGrantedBySrcId(uint32_t srcId); virtual uint32_t getGrantedBySrcId(uint32_t srcId);
/// <summary>Helper to get the source ID granted for the given destination ID.</summary> /// <summary>Helper to get the source ID granted for the given destination ID.</summary>
virtual uint32_t getGrantedSrcId(uint32_t dstId); virtual uint32_t getGrantedSrcId(uint32_t dstId);
/// <summary>Helper to set RF channel data.</summary>
void setRFChData(const std::unordered_map<uint32_t, VoiceChData>& chData) { m_rfChDataTable = chData; }
/// <summary>Helper to get RF channel data.</summary>
VoiceChData getRFChData(uint32_t chNo) const;
/// <summary>Helper to add a RF channel.</summary>
void addRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); }
/// <summary>Helper to remove a RF channel.</summary>
void removeRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); }
/// <summary>Gets the count of RF channels.</summary>
uint8_t getRFChCnt() const { return m_rfChTable.size(); }
/// <summary>Helper to determine if there are any RF channels available..</summary>
bool isRFChAvailable() const { return !m_rfChTable.empty(); }
/// <summary>Gets the count of granted RF channels.</summary> /// <summary>Gets the count of granted RF channels.</summary>
uint8_t getGrantedRFChCnt() const { return m_rfGrantChCnt; } uint8_t getGrantedRFChCnt() const { return m_rfGrantChCnt; }
/// <summary>Gets the RF channel lookup class.</summary>
ChannelLookup* rfCh() const { return m_chLookup; }
/// <summary>Updates the processor by the passed number of milliseconds.</summary> /// <summary>Updates the processor by the passed number of milliseconds.</summary>
void clock(uint32_t ms); void clock(uint32_t ms);
@ -186,8 +106,6 @@ namespace lookups
void setReleaseGrantCallback(std::function<void(uint32_t, uint32_t, uint8_t)>&& callback) { m_releaseGrant = callback; } void setReleaseGrantCallback(std::function<void(uint32_t, uint32_t, uint8_t)>&& callback) { m_releaseGrant = callback; }
protected: protected:
std::vector<uint32_t> m_rfChTable;
std::unordered_map<uint32_t, VoiceChData> m_rfChDataTable;
uint8_t m_rfGrantChCnt; uint8_t m_rfGrantChCnt;
std::vector<uint32_t> m_unitRegTable; std::vector<uint32_t> m_unitRegTable;
@ -203,6 +121,7 @@ namespace lookups
std::function<void(uint32_t, uint32_t, uint8_t)> m_releaseGrant; std::function<void(uint32_t, uint32_t, uint8_t)> m_releaseGrant;
std::string m_name; std::string m_name;
ChannelLookup* m_chLookup;
bool m_verbose; bool m_verbose;
}; };

@ -0,0 +1,97 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "lookups/ChannelLookup.h"
#include "Log.h"
using namespace lookups;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the ChannelLookup class.
/// </summary>
ChannelLookup::ChannelLookup() :
m_rfChTable(),
m_rfChDataTable()
{
m_rfChTable.clear();
}
/// <summary>
/// Finalizes a instance of the ChannelLookup class.
/// </summary>
ChannelLookup::~ChannelLookup() = default;
/// <summary>
/// Helper to get RF channel data.
/// </summary>
/// <param name="chNo"></param>
/// <returns></returns>
VoiceChData ChannelLookup::getRFChData(uint32_t chNo) const
{
if (chNo == 0U) {
return VoiceChData();
}
VoiceChData data;
try {
data = m_rfChDataTable.at(chNo);
} catch (...) {
data = VoiceChData();
}
return data;
}
/// <summary>
/// Helper to add a RF channel.
/// </summary>
/// <param name="chNo"></param>
/// <param name="force"></param>
/// <returns></returns>
bool ChannelLookup::addRFCh(uint32_t chNo, bool force)
{
if (chNo == 0U) {
return false;
}
if (force) {
m_rfChTable.push_back(chNo);
return true;
}
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo);
if (it == m_rfChTable.end()) {
m_rfChTable.push_back(chNo);
return true;
}
return false;
}
/// <summary>
/// Helper to remove a RF channel.
/// </summary>
/// <param name="chNo"></param>
/// <returns></returns>
void ChannelLookup::removeRFCh(uint32_t chNo)
{
if (chNo == 0U) {
return;
}
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo);
m_rfChTable.erase(it);
}

@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__CHANNEL_LOOKUP_H__)
#define __CHANNEL_LOOKUP_H__
#include "common/Defines.h"
#include "common/Timer.h"
#include <cstdio>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <functional>
namespace lookups
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents voice channel data.
// ---------------------------------------------------------------------------
class HOST_SW_API VoiceChData {
public:
/// <summary>Initializes a new instance of the VoiceChData class.</summary>
VoiceChData() :
m_chId(0U),
m_chNo(0U),
m_address(),
m_port(),
m_password(),
m_ssl()
{
/* stub */
}
/// <summary>Initializes a new instance of the VoiceChData class.</summary>
/// <param name="chId">Voice Channel Identity.</param>
/// <param name="chNo">Voice Channel Number.</param>
/// <param name="address">REST API Address.</param>
/// <param name="port">REST API Port.</param>
/// <param name="password">REST API Password.</param>
/// <param name="ssl">Flag indicating REST is using SSL.</param>
VoiceChData(uint8_t chId, uint32_t chNo, std::string address, uint16_t port, std::string password, bool ssl) :
m_chId(chId),
m_chNo(chNo),
m_address(address),
m_port(port),
m_password(password),
m_ssl(ssl)
{
/* stub */
}
/// <summary>Equals operator.</summary>
/// <param name="data"></param>
/// <returns></returns>
VoiceChData & operator=(const VoiceChData & data)
{
if (this != &data) {
m_chId = data.m_chId;
m_chNo = data.m_chNo;
m_address = data.m_address;
m_port = data.m_port;
m_password = data.m_password;
m_ssl = data.m_ssl;
}
return *this;
}
/// <summary>Helper to determine if the channel identity is valid.</summary>
bool isValidChId() const { return m_chId != 0U; }
/// <summary>Helper to determine if the channel is valid.</summary>
bool isValidCh() const { return m_chNo != 0U; }
public:
/// <summary>Voice Channel Identity.</summary>
__READONLY_PROPERTY_PLAIN(uint8_t, chId);
/// <summary>Voice Channel Number.</summary>
__READONLY_PROPERTY_PLAIN(uint32_t, chNo);
/// <summary>REST API Address.</summary>
__PROPERTY_PLAIN(std::string, address);
/// <summary>REST API Port.</summary>
__PROPERTY_PLAIN(uint16_t, port);
/// <summary>REST API Password.</summary>
__READONLY_PROPERTY_PLAIN(std::string, password);
/// <summary>Flag indicating REST is using SSL.</summary>
__PROPERTY_PLAIN(bool, ssl);
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements a lookup table class that contains RF channel information.
// ---------------------------------------------------------------------------
class HOST_SW_API ChannelLookup {
public:
/// <summary>Initializes a new instance of the ChannelLookup class.</summary>
ChannelLookup();
/// <summary>Finalizes a instance of the ChannelLookup class.</summary>
virtual ~ChannelLookup();
/** RF Channel Data */
/// <summary>Gets the count of RF channel data.</summary>
uint8_t rfChDataSize() const { return m_rfChDataTable.size(); }
/// <summary>Gets the RF channel data table.</summary>
std::unordered_map<uint32_t, VoiceChData> rfChDataTable() const { return m_rfChDataTable; }
/// <summary>Helper to set RF channel data.</summary>
void setRFChData(const std::unordered_map<uint32_t, VoiceChData>& chData) { m_rfChDataTable = chData; }
/// <summary>Helper to set RF channel data.</summary>
void setRFChData(uint32_t chNo, VoiceChData chData) { m_rfChDataTable[chNo] = chData; }
/// <summary>Helper to get RF channel data.</summary>
VoiceChData getRFChData(uint32_t chNo) const;
/// <summary>Helper to get first available channel number.</summary>
uint32_t getFirstRFChannel() const { return m_rfChTable.at(0); }
/// <summary>Gets the count of RF channels.</summary>
uint8_t rfChSize() const { return m_rfChTable.size(); }
/// <summary>Gets the RF channels table.</summary>
std::vector<uint32_t> rfChTable() const { return m_rfChTable; }
/// <summary>Helper to add a RF channel.</summary>
bool addRFCh(uint32_t chNo, bool force = false);
/// <summary>Helper to remove a RF channel.</summary>
void removeRFCh(uint32_t chNo);
/// <summary>Helper to determine if there are any RF channels available..</summary>
bool isRFChAvailable() const { return !m_rfChTable.empty(); }
private:
std::vector<uint32_t> m_rfChTable;
std::unordered_map<uint32_t, VoiceChData> m_rfChDataTable;
};
} // namespace lookups
#endif // __CHANNEL_LOOKUP_H__

@ -24,6 +24,8 @@ using namespace network::udp;
#include <cerrno> #include <cerrno>
#include <cstring> #include <cstring>
#include <ifaddrs.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -578,15 +580,15 @@ void Socket::setPresharedKey(const uint8_t* presharedKey)
/// </summary> /// </summary>
/// <param name="hostname">String containing hostname to resolve.</param> /// <param name="hostname">String containing hostname to resolve.</param>
/// <param name="port">Numeric port number of service to resolve.</param> /// <param name="port">Numeric port number of service to resolve.</param>
/// <param name="addr">Socket address structure.</param> /// <param name="address">Socket address structure.</param>
/// <param name="addrLen"></param> /// <param name="addrLen"></param>
/// <returns>Zero if no error during lookup, otherwise error.</returns> /// <returns>Zero if no error during lookup, otherwise error.</returns>
int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& addr, uint32_t& addrLen) int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& address, uint32_t& addrLen)
{ {
struct addrinfo hints; struct addrinfo hints;
::memset(&hints, 0, sizeof(hints)); ::memset(&hints, 0, sizeof(hints));
return lookup(hostname, port, addr, addrLen, hints); return lookup(hostname, port, address, addrLen, hints);
} }
/// <summary> /// <summary>
@ -594,11 +596,11 @@ int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage&
/// </summary> /// </summary>
/// <param name="hostname">String containing hostname to resolve.</param> /// <param name="hostname">String containing hostname to resolve.</param>
/// <param name="port">Numeric port number of service to resolve.</param> /// <param name="port">Numeric port number of service to resolve.</param>
/// <param name="addr">Socket address structure.</param> /// <param name="address">Socket address structure.</param>
/// <param name="addrLen"></param> /// <param name="addrLen"></param>
/// <param name="hints"></param> /// <param name="hints"></param>
/// <returns>Zero if no error during lookup, otherwise error.</returns> /// <returns>Zero if no error during lookup, otherwise error.</returns>
int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& addr, uint32_t& addrLen, struct addrinfo& hints) int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& address, uint32_t& addrLen, struct addrinfo& hints)
{ {
std::string portstr = std::to_string(port); std::string portstr = std::to_string(port);
struct addrinfo* res; struct addrinfo* res;
@ -608,7 +610,7 @@ int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage&
int err = getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res); int err = getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
if (err != 0) { if (err != 0) {
sockaddr_in* paddr = (sockaddr_in*)& addr; sockaddr_in* paddr = (sockaddr_in*)& address;
::memset(paddr, 0x00U, addrLen = sizeof(sockaddr_in)); ::memset(paddr, 0x00U, addrLen = sizeof(sockaddr_in));
paddr->sin_family = AF_INET; paddr->sin_family = AF_INET;
paddr->sin_port = htons(port); paddr->sin_port = htons(port);
@ -617,13 +619,56 @@ int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage&
return err; return err;
} }
::memcpy(&addr, res->ai_addr, addrLen = res->ai_addrlen); ::memcpy(&address, res->ai_addr, addrLen = res->ai_addrlen);
freeaddrinfo(res); freeaddrinfo(res);
return 0; return 0;
} }
/// <summary>
///
/// </summary>
/// <returns>Zero if no error during lookup, otherwise error.</returns>
std::string Socket::getLocalAddress()
{
struct ifaddrs *ifaddr, *ifa;
int n;
char host[NI_MAXHOST];
std::string address = std::string();
int err = -1;
if ((err = getifaddrs(&ifaddr)) == -1) {
LogError(LOG_NET, "Cannot retreive system network interfaces");
return "0.0.0.0";
}
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (ifa->ifa_addr == NULL)
continue;
int family = ifa->ifa_addr->sa_family;
if (family == AF_INET || family == AF_INET6) {
err = getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (err != 0) {
LogError(LOG_NET, "Cannot retreive system network interfaces, err: %d", errno);
break;
}
address = std::string(host);
if (address == "127.0.0.1" || address == "::1")
continue;
else
break;
}
}
freeifaddrs(ifaddr);
return address;
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>

@ -136,6 +136,9 @@ namespace network
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary> /// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
static int lookup(const std::string& hostName, uint16_t port, sockaddr_storage& address, uint32_t& addrLen, struct addrinfo& hints); static int lookup(const std::string& hostName, uint16_t port, sockaddr_storage& address, uint32_t& addrLen, struct addrinfo& hints);
/// <summary></summary>
static std::string getLocalAddress();
/// <summary></summary> /// <summary></summary>
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT); static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);

@ -1092,7 +1092,8 @@ void FNENetwork::createPeerAffiliations(uint32_t peerId, std::string peerName)
erasePeerAffiliations(peerId); erasePeerAffiliations(peerId);
std::lock_guard<std::mutex> lock(m_peerMutex); std::lock_guard<std::mutex> lock(m_peerMutex);
m_peerAffiliations[peerId] = new lookups::AffiliationLookup(peerName, m_verbose); lookups::ChannelLookup* chLookup = new lookups::ChannelLookup();
m_peerAffiliations[peerId] = new lookups::AffiliationLookup(peerName, chLookup, m_verbose);
} }
/// <summary> /// <summary>
@ -1106,8 +1107,12 @@ bool FNENetwork::erasePeerAffiliations(uint32_t peerId)
auto it = std::find_if(m_peerAffiliations.begin(), m_peerAffiliations.end(), [&](PeerAffiliationMapPair x) { return x.first == peerId; }); auto it = std::find_if(m_peerAffiliations.begin(), m_peerAffiliations.end(), [&](PeerAffiliationMapPair x) { return x.first == peerId; });
if (it != m_peerAffiliations.end()) { if (it != m_peerAffiliations.end()) {
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId]; lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
if (aff != nullptr) if (aff != nullptr) {
lookups::ChannelLookup* rfCh = aff->rfCh();
if (rfCh != nullptr)
delete rfCh;
delete aff; delete aff;
}
m_peerAffiliations.erase(peerId); m_peerAffiliations.erase(peerId);
return true; return true;

@ -139,6 +139,7 @@ bool Host::readParams()
** Channel Configuration ** Channel Configuration
*/ */
yaml::Node rfssConfig = systemConf["config"]; yaml::Node rfssConfig = systemConf["config"];
m_channelLookup = new ChannelLookup();
m_channelId = (uint8_t)rfssConfig["channelId"].as<uint32_t>(0U); m_channelId = (uint8_t)rfssConfig["channelId"].as<uint32_t>(0U);
if (m_channelId > 15U) { // clamp to 15 if (m_channelId > 15U) { // clamp to 15
m_channelId = 15U; m_channelId = 15U;
@ -188,6 +189,7 @@ bool Host::readParams()
uint16_t restApiPort = (uint16_t)controlCh["restPort"].as<uint32_t>(REST_API_DEFAULT_PORT); uint16_t restApiPort = (uint16_t)controlCh["restPort"].as<uint32_t>(REST_API_DEFAULT_PORT);
std::string restApiPassword = controlCh["restPassword"].as<std::string>(); std::string restApiPassword = controlCh["restPassword"].as<std::string>();
bool restSsl = controlCh["restSsl"].as<bool>(false); bool restSsl = controlCh["restSsl"].as<bool>(false);
m_presenceTime = controlCh["presence"].as<uint32_t>(120U);
VoiceChData data = VoiceChData(m_channelId, m_channelNo, restApiAddress, restApiPort, restApiPassword, restSsl); VoiceChData data = VoiceChData(m_channelId, m_channelNo, restApiAddress, restApiPort, restApiPassword, restSsl);
m_controlChData = data; m_controlChData = data;
@ -232,7 +234,7 @@ bool Host::readParams()
chNo = 4095U; chNo = 4095U;
} }
std::string restApiAddress = channel["restAddress"].as<std::string>("127.0.0.1"); std::string restApiAddress = channel["restAddress"].as<std::string>("0.0.0.0");
uint16_t restApiPort = (uint16_t)channel["restPort"].as<uint32_t>(REST_API_DEFAULT_PORT); uint16_t restApiPort = (uint16_t)channel["restPort"].as<uint32_t>(REST_API_DEFAULT_PORT);
std::string restApiPassword = channel["restPassword"].as<std::string>(); std::string restApiPassword = channel["restPassword"].as<std::string>();
bool restSsl = channel["restSsl"].as<bool>(false); bool restSsl = channel["restSsl"].as<bool>(false);
@ -240,14 +242,15 @@ bool Host::readParams()
::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u SSL %u", chId, chNo, restApiAddress.c_str(), restApiPort, restSsl); ::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u SSL %u", chId, chNo, restApiAddress.c_str(), restApiPort, restSsl);
VoiceChData data = VoiceChData(chId, chNo, restApiAddress, restApiPort, restApiPassword, restSsl); VoiceChData data = VoiceChData(chId, chNo, restApiAddress, restApiPort, restApiPassword, restSsl);
m_voiceChData[chNo] = data; m_channelLookup->setRFChData(chNo, data);
m_voiceChNo.push_back(chNo); m_channelLookup->addRFCh(chNo);
} }
std::string strVoiceChNo = ""; std::string strVoiceChNo = "";
for (auto it = m_voiceChNo.begin(); it != m_voiceChNo.end(); ++it) { std::vector<uint32_t> voiceChNo = m_channelLookup->rfChTable();
for (auto it = voiceChNo.begin(); it != voiceChNo.end(); ++it) {
uint32_t chNo = ::atoi(std::to_string(*it).c_str()); uint32_t chNo = ::atoi(std::to_string(*it).c_str());
::lookups::VoiceChData voiceChData = m_voiceChData[chNo]; ::lookups::VoiceChData voiceChData = m_channelLookup->getRFChData(chNo);
char hexStr[29]; char hexStr[29];
@ -816,6 +819,8 @@ bool Host::createNetwork()
// initialize network remote command // initialize network remote command
if (restApiEnable) { if (restApiEnable) {
m_restAddress = restApiAddress;
m_restPort = restApiPort;
m_RESTAPI = new RESTAPI(restApiAddress, restApiPort, restApiPassword, restApiSSLKey, restApiSSLCert, restApiEnableSSL, this, restApiDebug); m_RESTAPI = new RESTAPI(restApiAddress, restApiPort, restApiPassword, restApiSSLKey, restApiSSLCert, restApiEnableSSL, this, restApiDebug);
m_RESTAPI->setLookups(m_ridLookup, m_tidLookup); m_RESTAPI->setLookups(m_ridLookup, m_tidLookup);
bool ret = m_RESTAPI->open(); bool ret = m_RESTAPI->open();
@ -827,6 +832,8 @@ bool Host::createNetwork()
} }
} }
else { else {
m_restAddress = "0.0.0.0";
m_restPort = REST_API_DEFAULT_PORT;
m_RESTAPI = nullptr; m_RESTAPI = nullptr;
} }

@ -15,6 +15,7 @@
*/ */
#include "Defines.h" #include "Defines.h"
#include "common/lookups/RSSIInterpolator.h" #include "common/lookups/RSSIInterpolator.h"
#include "common/network/udp/Socket.h"
#include "common/Log.h" #include "common/Log.h"
#include "common/StopWatch.h" #include "common/StopWatch.h"
#include "common/Thread.h" #include "common/Thread.h"
@ -86,8 +87,7 @@ Host::Host(const std::string& confFile) :
m_txFrequency(0U), m_txFrequency(0U),
m_channelId(0U), m_channelId(0U),
m_channelNo(0U), m_channelNo(0U),
m_voiceChNo(), m_channelLookup(),
m_voiceChData(),
m_voiceChPeerId(), m_voiceChPeerId(),
m_controlChData(), m_controlChData(),
m_idenTable(nullptr), m_idenTable(nullptr),
@ -102,6 +102,7 @@ Host::Host(const std::string& confFile) :
m_nxdnCCData(false), m_nxdnCCData(false),
m_nxdnCtrlChannel(false), m_nxdnCtrlChannel(false),
m_nxdnCtrlBroadcast(false), m_nxdnCtrlBroadcast(false),
m_presenceTime(120U),
m_siteId(1U), m_siteId(1U),
m_sysId(1U), m_sysId(1U),
m_dmrNetId(1U), m_dmrNetId(1U),
@ -120,6 +121,8 @@ Host::Host(const std::string& confFile) :
m_nxdnBcastDurationTimer(1000U), m_nxdnBcastDurationTimer(1000U),
m_activeTickDelay(5U), m_activeTickDelay(5U),
m_idleTickDelay(5U), m_idleTickDelay(5U),
m_restAddress("0.0.0.0"),
m_restPort(REST_API_DEFAULT_PORT),
m_RESTAPI(nullptr) m_RESTAPI(nullptr)
{ {
/* stub */ /* stub */
@ -404,9 +407,9 @@ int Host::run()
} }
dmr = std::make_unique<dmr::Control>(m_authoritative, m_dmrColorCode, callHang, m_dmrQueueSizeBytes, dmr = std::make_unique<dmr::Control>(m_authoritative, m_dmrColorCode, callHang, m_dmrQueueSizeBytes,
embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_channelLookup, m_ridLookup, m_tidLookup,
m_idenTable, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, dmrDumpCsbkData, dmrDebug, dmrVerbose); m_idenTable, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, dmrDumpCsbkData, dmrDebug, dmrVerbose);
dmr->setOptions(m_conf, m_supervisor, m_voiceChNo, m_voiceChData, m_controlChData, m_dmrNetId, m_siteId, m_channelId, dmr->setOptions(m_conf, m_supervisor, m_controlChData, m_dmrNetId, m_siteId, m_channelId,
m_channelNo, true); m_channelNo, true);
if (dmrCtrlChannel) { if (dmrCtrlChannel) {
@ -475,9 +478,9 @@ int Host::run()
} }
p25 = std::make_unique<p25::Control>(m_authoritative, m_p25NAC, callHang, m_p25QueueSizeBytes, m_modem, p25 = std::make_unique<p25::Control>(m_authoritative, m_p25NAC, callHang, m_p25QueueSizeBytes, m_modem,
m_network, m_timeout, m_rfTalkgroupHang, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, m_network, m_timeout, m_rfTalkgroupHang, m_duplex, m_channelLookup, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket,
p25RepeatDataPacket, p25DumpTsbkData, p25Debug, p25Verbose); p25RepeatDataPacket, p25DumpTsbkData, p25Debug, p25Verbose);
p25->setOptions(m_conf, m_supervisor, m_cwCallsign, m_voiceChNo, m_voiceChData, m_controlChData, p25->setOptions(m_conf, m_supervisor, m_cwCallsign, m_controlChData,
m_p25NetId, m_sysId, m_p25RfssId, m_siteId, m_channelId, m_channelNo, true); m_p25NetId, m_sysId, m_p25RfssId, m_siteId, m_channelId, m_channelNo, true);
if (p25CtrlChannel) { if (p25CtrlChannel) {
@ -537,9 +540,9 @@ int Host::run()
} }
nxdn = std::make_unique<nxdn::Control>(m_authoritative, m_nxdnRAN, callHang, m_nxdnQueueSizeBytes, nxdn = std::make_unique<nxdn::Control>(m_authoritative, m_nxdnRAN, callHang, m_nxdnQueueSizeBytes,
m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_channelLookup, m_ridLookup, m_tidLookup, m_idenTable, rssi,
nxdnDumpRcchData, nxdnDebug, nxdnVerbose); nxdnDumpRcchData, nxdnDebug, nxdnVerbose);
nxdn->setOptions(m_conf, m_supervisor, m_cwCallsign, m_voiceChNo, m_voiceChData, m_controlChData, m_siteId, nxdn->setOptions(m_conf, m_supervisor, m_cwCallsign, m_controlChData, m_siteId,
m_sysId, m_channelId, m_channelNo, true); m_sysId, m_channelId, m_channelNo, true);
if (nxdnCtrlChannel) { if (nxdnCtrlChannel) {
@ -897,9 +900,90 @@ int Host::run()
nxdnFrameWriteThread.run(); nxdnFrameWriteThread.run();
nxdnFrameWriteThread.setName("nxdn:frame-w"); nxdnFrameWriteThread.setName("nxdn:frame-w");
Timer ccRegisterTimer(1000U, 120U); /** Network Presence Notification */
ccRegisterTimer.start(); ThreadFunc presenceThread([&, this]() {
bool hasInitialRegistered = false; if (g_killed)
return;
Timer presenceNotifyTimer(1000U, m_presenceTime);
presenceNotifyTimer.start();
bool hasInitialRegistered = false;
StopWatch stopWatch;
stopWatch.start();
while (!g_killed) {
// scope is intentional
{
uint32_t ms = stopWatch.elapsed();
stopWatch.start();
presenceNotifyTimer.clock(ms);
// VC -> CC presence registration
if (!m_controlChData.address().empty() && m_controlChData.port() != 0 && m_network != nullptr && m_RESTAPI != nullptr) {
if ((presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) || !hasInitialRegistered) {
LogMessage(LOG_HOST, "CC %s:%u, notifying CC of VC registration, peerId = %u", m_controlChData.address().c_str(), m_controlChData.port(), m_network->getPeerId());
hasInitialRegistered = true;
std::string localAddress = network::udp::Socket::getLocalAddress();
if (m_restAddress == "0.0.0.0") {
m_restAddress = localAddress;
}
// callback REST API to release the granted TG on the specified control channel
json::object req = json::object();
req["channelNo"].set<uint32_t>(m_channelNo);
uint32_t peerId = m_network->getPeerId();
req["peerId"].set<uint32_t>(peerId);
req["restAddress"].set<std::string>(m_restAddress);
req["restPort"].set<uint16_t>(m_restPort);
int ret = RESTClient::send(m_controlChData.address(), m_controlChData.port(), m_controlChData.password(),
HTTP_PUT, PUT_REGISTER_CC_VC, req, m_controlChData.ssl(), REST_QUICK_WAIT, false);
if (ret != network::rest::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to notify the CC %s:%u of VC registration", m_controlChData.address().c_str(), m_controlChData.port());
}
presenceNotifyTimer.start();
}
}
// CC -> FNE registered VC announcement
if (m_dmrCtrlChannel || m_p25CtrlChannel || m_nxdnCtrlChannel) {
if (presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) {
if (m_network != nullptr && m_voiceChPeerId.size() > 0) {
LogMessage(LOG_HOST, "notifying FNE of VC registrations, peerId = %u", m_network->getPeerId());
std::vector<uint32_t> peers;
for (auto it : m_voiceChPeerId) {
peers.push_back(it.second);
}
m_network->announceSiteVCs(peers);
}
presenceNotifyTimer.start();
}
}
}
if (m_state != STATE_IDLE)
Thread::sleep(m_activeTickDelay);
if (m_state == STATE_IDLE)
Thread::sleep(m_idleTickDelay);
}
});
if (!m_controlChData.address().empty() && m_controlChData.port() != 0 && m_network != nullptr) {
presenceThread.run();
presenceThread.setName("host:presence");
}
if (m_dmrCtrlChannel || m_p25CtrlChannel || m_nxdnCtrlChannel) {
presenceThread.run();
presenceThread.setName("host:presence");
}
// main execution loop // main execution loop
while (!killed) { while (!killed) {
@ -1049,47 +1133,6 @@ int Host::run()
if (nxdn != nullptr) if (nxdn != nullptr)
nxdn->clock(ms); nxdn->clock(ms);
ccRegisterTimer.clock(ms);
// VC -> CC presence registration
if (!m_controlChData.address().empty() && m_controlChData.port() != 0 && m_network != nullptr) {
if ((ccRegisterTimer.isRunning() && ccRegisterTimer.hasExpired()) || !hasInitialRegistered) {
LogMessage(LOG_HOST, "CC %s:%u, notifying CC of VC registration, peerId = %u", m_controlChData.address().c_str(), m_controlChData.port(), m_network->getPeerId());
hasInitialRegistered = true;
// callback REST API to release the granted TG on the specified control channel
json::object req = json::object();
req["channelNo"].set<uint32_t>(m_channelNo);
uint32_t peerId = m_network->getPeerId();
req["peerId"].set<uint32_t>(peerId);
int ret = RESTClient::send(m_controlChData.address(), m_controlChData.port(), m_controlChData.password(),
HTTP_PUT, PUT_REGISTER_CC_VC, req, m_controlChData.ssl(), REST_QUICK_WAIT, false);
if (ret != network::rest::http::HTTPPayload::StatusType::OK) {
::LogError(LOG_HOST, "failed to notify the CC %s:%u of VC registration", m_controlChData.address().c_str(), m_controlChData.port());
}
ccRegisterTimer.start();
}
}
// CC -> FNE registered VC announcement
if (m_dmrCtrlChannel || m_p25CtrlChannel || m_nxdnCtrlChannel) {
if (ccRegisterTimer.isRunning() && ccRegisterTimer.hasExpired()) {
if (m_network != nullptr && m_voiceChPeerId.size() > 0) {
LogMessage(LOG_HOST, "notifying FNE of VC registrations, peerId = %u", m_network->getPeerId());
std::vector<uint32_t> peers;
for (auto it : m_voiceChPeerId) {
peers.push_back(it.second);
}
m_network->announceSiteVCs(peers);
}
ccRegisterTimer.start();
}
}
// ------------------------------------------------------ // ------------------------------------------------------
// -- Timer Clocking -- // -- Timer Clocking --
// ------------------------------------------------------ // ------------------------------------------------------
@ -1610,6 +1653,10 @@ void Host::setState(uint8_t state)
delete m_RESTAPI; delete m_RESTAPI;
} }
if (m_channelLookup != nullptr) {
delete m_channelLookup;
}
if (m_tidLookup != nullptr) { if (m_tidLookup != nullptr) {
m_tidLookup->stop(); m_tidLookup->stop();
delete m_tidLookup; delete m_tidLookup;

@ -9,7 +9,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2023 Bryan Biedenkapp, N2PLL * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* *
*/ */
#if !defined(__HOST_H__) #if !defined(__HOST_H__)
@ -18,6 +18,7 @@
#include "Defines.h" #include "Defines.h"
#include "common/Timer.h" #include "common/Timer.h"
#include "common/lookups/AffiliationLookup.h" #include "common/lookups/AffiliationLookup.h"
#include "common/lookups/ChannelLookup.h"
#include "common/lookups/IdenTableLookup.h" #include "common/lookups/IdenTableLookup.h"
#include "common/lookups/RadioIdLookup.h" #include "common/lookups/RadioIdLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h" #include "common/lookups/TalkgroupRulesLookup.h"
@ -54,10 +55,8 @@ public:
/// <summary>Executes the main modem host processing loop.</summary> /// <summary>Executes the main modem host processing loop.</summary>
int run(); int run();
/// <summary>Gets the voice channel number list.</summary> /// <summary>Gets the RF channel lookup class.</summary>
std::vector<uint32_t> getVoiceChNo() const { return m_voiceChNo; } lookups::ChannelLookup* rfCh() const { return m_channelLookup; }
/// <summary>Gets the voice channel data.</summary>
std::unordered_map<uint32_t, lookups::VoiceChData> getVoiceChData() const { return m_voiceChData; }
private: private:
const std::string& m_confFile; const std::string& m_confFile;
@ -107,8 +106,7 @@ private:
uint8_t m_channelId; uint8_t m_channelId;
uint32_t m_channelNo; uint32_t m_channelNo;
std::vector<uint32_t> m_voiceChNo; lookups::ChannelLookup* m_channelLookup;
std::unordered_map<uint32_t, lookups::VoiceChData> m_voiceChData;
std::unordered_map<uint32_t, uint32_t> m_voiceChPeerId; std::unordered_map<uint32_t, uint32_t> m_voiceChPeerId;
lookups::VoiceChData m_controlChData; lookups::VoiceChData m_controlChData;
@ -126,6 +124,8 @@ private:
bool m_nxdnCtrlChannel; bool m_nxdnCtrlChannel;
bool m_nxdnCtrlBroadcast; bool m_nxdnCtrlBroadcast;
uint32_t m_presenceTime;
uint8_t m_siteId; uint8_t m_siteId;
uint32_t m_sysId; uint32_t m_sysId;
uint32_t m_dmrNetId; uint32_t m_dmrNetId;
@ -150,7 +150,9 @@ private:
uint8_t m_idleTickDelay; uint8_t m_idleTickDelay;
friend class RESTAPI; friend class RESTAPI;
RESTAPI* m_RESTAPI; std::string m_restAddress;
uint16_t m_restPort;
RESTAPI *m_RESTAPI;
/// <summary>Modem port open callback.</summary> /// <summary>Modem port open callback.</summary>
bool rmtPortModemOpen(modem::Modem* modem); bool rmtPortModemOpen(modem::Modem* modem);

@ -42,6 +42,7 @@ using namespace dmr;
/// <param name="modem">Instance of the Modem class.</param> /// <param name="modem">Instance of the Modem class.</param>
/// <param name="network">Instance of the BaseNetwork class.</param> /// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="duplex">Flag indicating full-duplex operation.</param> /// <param name="duplex">Flag indicating full-duplex operation.</param>
/// <param name="chLookup">Instance of the ChannelLookup class.</param>
/// <param name="ridLookup">Instance of the RadioIdLookup class.</param> /// <param name="ridLookup">Instance of the RadioIdLookup class.</param>
/// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param> /// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param>
/// <param name="idenTable">Instance of the IdenTableLookup class.</param> /// <param name="idenTable">Instance of the IdenTableLookup class.</param>
@ -53,7 +54,7 @@ using namespace dmr;
/// <param name="debug">Flag indicating whether DMR debug is enabled.</param> /// <param name="debug">Flag indicating whether DMR debug is enabled.</param>
/// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param>
Control::Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly, Control::Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly,
bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::Network* network, bool duplex, bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::Network* network, bool duplex, ::lookups::ChannelLookup* chLookup,
::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper,
uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) : uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) :
m_authoritative(authoritative), m_authoritative(authoritative),
@ -78,13 +79,14 @@ Control::Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint
m_debug(debug) m_debug(debug)
{ {
assert(modem != nullptr); assert(modem != nullptr);
assert(chLookup != nullptr);
assert(ridLookup != nullptr); assert(ridLookup != nullptr);
assert(tidLookup != nullptr); assert(tidLookup != nullptr);
assert(idenTable != nullptr); assert(idenTable != nullptr);
assert(rssiMapper != nullptr); assert(rssiMapper != nullptr);
acl::AccessControl::init(m_ridLookup, m_tidLookup); acl::AccessControl::init(m_ridLookup, m_tidLookup);
Slot::init(this, authoritative, colorCode, SiteData(), embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, m_ridLookup, m_tidLookup, m_idenTable, rssiMapper, jitter, verbose); Slot::init(this, authoritative, colorCode, SiteData(), embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, chLookup, m_ridLookup, m_tidLookup, m_idenTable, rssiMapper, jitter, verbose);
lc::CSBK::setVerbose(m_dumpCSBKData); lc::CSBK::setVerbose(m_dumpCSBKData);
m_slot1 = new Slot(1U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose); m_slot1 = new Slot(1U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose);
@ -107,16 +109,14 @@ Control::~Control()
/// </summary> /// </summary>
/// <param name="conf">Instance of the ConfigINI class.</param> /// <param name="conf">Instance of the ConfigINI class.</param>
/// <param name="supervisor">Flag indicating whether the DMR has supervisory functions.</param> /// <param name="supervisor">Flag indicating whether the DMR has supervisory functions.</param>
/// <param name="voiceChNo">Voice Channel Number list.</param>
/// <param name="voiceChData">Voice Channel data map.</param>
/// <param name="controlChData">Control Channel data.</param> /// <param name="controlChData">Control Channel data.</param>
/// <param name="netId">DMR Network ID.</param> /// <param name="netId">DMR Network ID.</param>
/// <param name="siteId">DMR Site ID.</param> /// <param name="siteId">DMR Site ID.</param>
/// <param name="channelId">Channel ID.</param> /// <param name="channelId">Channel ID.</param>
/// <param name="channelNo">Channel Number.</param> /// <param name="channelNo">Channel Number.</param>
/// <param name="printOptions"></param> /// <param name="printOptions"></param>
void Control::setOptions(yaml::Node& conf, bool supervisor, const std::vector<uint32_t> voiceChNo, const std::unordered_map<uint32_t, ::lookups::VoiceChData> voiceChData, void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChData controlChData,
::lookups::VoiceChData controlChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions) uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions)
{ {
yaml::Node systemConf = conf["system"]; yaml::Node systemConf = conf["system"];
yaml::Node dmrProtocol = conf["protocols"]["dmr"]; yaml::Node dmrProtocol = conf["protocols"]["dmr"];
@ -142,7 +142,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::vector<ui
dedicatedTSCC = false; dedicatedTSCC = false;
} }
Slot::setSiteData(voiceChNo, voiceChData, controlChData, netId, siteId, channelId, channelNo, dedicatedTSCC); Slot::setSiteData(controlChData, netId, siteId, channelId, channelNo, dedicatedTSCC);
Slot::setAlohaConfig(nRandWait, backOff); Slot::setAlohaConfig(nRandWait, backOff);
bool disableGrantSourceIdCheck = control["disableGrantSourceIdCheck"].as<bool>(false); bool disableGrantSourceIdCheck = control["disableGrantSourceIdCheck"].as<bool>(false);
@ -481,7 +481,7 @@ void Control::touchGrantTG(uint32_t dstId, uint8_t slot)
/// <summary> /// <summary>
/// Gets instance of the AffiliationLookup class. /// Gets instance of the AffiliationLookup class.
/// </summary> /// </summary>
dmr::lookups::DMRAffiliationLookup Control::affiliations() dmr::lookups::DMRAffiliationLookup* Control::affiliations()
{ {
switch (m_tsccSlotNo) { switch (m_tsccSlotNo) {
case 1U: case 1U:
@ -493,7 +493,7 @@ dmr::lookups::DMRAffiliationLookup Control::affiliations()
break; break;
} }
return 0; // ?? return nullptr;
} }
/// <summary> /// <summary>

@ -45,15 +45,15 @@ namespace dmr
public: public:
/// <summary>Initializes a new instance of the Control class.</summary> /// <summary>Initializes a new instance of the Control class.</summary>
Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly, Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly,
bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::Network* network, bool duplex, bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::Network* network, bool duplex, ::lookups::ChannelLookup* chLookup,
::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssi, ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssi,
uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose); uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Control class.</summary> /// <summary>Finalizes a instance of the Control class.</summary>
~Control(); ~Control();
/// <summary>Helper to set DMR configuration options.</summary> /// <summary>Helper to set DMR configuration options.</summary>
void setOptions(yaml::Node& conf, bool supervisor, const std::vector<uint32_t> voiceChNo, const std::unordered_map<uint32_t, ::lookups::VoiceChData> voiceChData, void setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChData controlChData,
::lookups::VoiceChData controlChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions); uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions);
/// <summary>Gets a flag indicating whether the DMR control channel is running.</summary> /// <summary>Gets a flag indicating whether the DMR control channel is running.</summary>
bool getCCRunning() const { return m_ccRunning; } bool getCCRunning() const { return m_ccRunning; }
@ -87,7 +87,7 @@ namespace dmr
void touchGrantTG(uint32_t dstId, uint8_t slot); void touchGrantTG(uint32_t dstId, uint8_t slot);
/// <summary>Gets instance of the DMRAffiliationLookup class.</summary> /// <summary>Gets instance of the DMRAffiliationLookup class.</summary>
lookups::DMRAffiliationLookup affiliations(); lookups::DMRAffiliationLookup* affiliations();
/// <summary>Helper to return the slot carrying the TSCC.</summary> /// <summary>Helper to return the slot carrying the TSCC.</summary>
Slot* getTSCCSlot() const; Slot* getTSCCSlot() const;

@ -740,7 +740,7 @@ void Slot::releaseGrantTG(uint32_t dstId)
if (m_affiliations->isGranted(dstId)) { if (m_affiliations->isGranted(dstId)) {
uint32_t chNo = m_affiliations->getGrantedCh(dstId); uint32_t chNo = m_affiliations->getGrantedCh(dstId);
uint32_t srcId = m_affiliations->getGrantedSrcId(dstId); uint32_t srcId = m_affiliations->getGrantedSrcId(dstId);
::lookups::VoiceChData voiceCh = m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_DMR, "DMR Slot %u, VC %s:%u, TG grant released, srcId = %u, dstId = %u, chId = %u, chNo = %u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); LogMessage(LOG_DMR, "DMR Slot %u, VC %s:%u, TG grant released, srcId = %u, dstId = %u, chId = %u, chNo = %u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo);
@ -763,7 +763,7 @@ void Slot::touchGrantTG(uint32_t dstId)
if (m_affiliations->isGranted(dstId)) { if (m_affiliations->isGranted(dstId)) {
uint32_t chNo = m_affiliations->getGrantedCh(dstId); uint32_t chNo = m_affiliations->getGrantedCh(dstId);
uint32_t srcId = m_affiliations->getGrantedSrcId(dstId); uint32_t srcId = m_affiliations->getGrantedSrcId(dstId);
::lookups::VoiceChData voiceCh = m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceCh = m_affiliations->rfCh()->getRFChData(chNo);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_DMR, "DMR Slot %u, VC %s:%u, call in progress, srcId = %u, dstId = %u, chId = %u, chNo = %u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); LogMessage(LOG_DMR, "DMR Slot %u, VC %s:%u, call in progress, srcId = %u, dstId = %u, chId = %u, chNo = %u", m_slotNo, voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo);
@ -870,6 +870,7 @@ uint32_t Slot::getLastSrcId() const
/// <param name="modem">Instance of the Modem class.</param> /// <param name="modem">Instance of the Modem class.</param>
/// <param name="network">Instance of the BaseNetwork class.</param> /// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="duplex">Flag indicating full-duplex operation.</param> /// <param name="duplex">Flag indicating full-duplex operation.</param>
/// <param name="chLookup">Instance of the ChannelLookup class.</param>
/// <param name="ridLookup">Instance of the RadioIdLookup class.</param> /// <param name="ridLookup">Instance of the RadioIdLookup class.</param>
/// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param> /// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param>
/// <param name="idenTable">Instance of the IdenTableLookup class.</param> /// <param name="idenTable">Instance of the IdenTableLookup class.</param>
@ -877,11 +878,12 @@ uint32_t Slot::getLastSrcId() const
/// <param name="jitter"></param> /// <param name="jitter"></param>
/// <param name="verbose"></param> /// <param name="verbose"></param>
void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData siteData, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem, void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData siteData, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem,
network::Network* network, bool duplex, ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup, network::Network* network, bool duplex, ::lookups::ChannelLookup* chLookup, ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup,
::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, uint32_t jitter, bool verbose) ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, uint32_t jitter, bool verbose)
{ {
assert(dmr != nullptr); assert(dmr != nullptr);
assert(modem != nullptr); assert(modem != nullptr);
assert(chLookup != nullptr);
assert(ridLookup != nullptr); assert(ridLookup != nullptr);
assert(tidLookup != nullptr); assert(tidLookup != nullptr);
assert(idenTable != nullptr); assert(idenTable != nullptr);
@ -906,7 +908,7 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s
m_idenTable = idenTable; m_idenTable = idenTable;
m_ridLookup = ridLookup; m_ridLookup = ridLookup;
m_tidLookup = tidLookup; m_tidLookup = tidLookup;
m_affiliations = new dmr::lookups::DMRAffiliationLookup(verbose); m_affiliations = new dmr::lookups::DMRAffiliationLookup(chLookup, verbose);
// set the grant release callback // set the grant release callback
m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) {
@ -917,7 +919,7 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s
return; return;
} }
::lookups::VoiceChData voiceChData = tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object(); json::object req = json::object();
req["slot"].set<uint8_t>(slot); req["slot"].set<uint8_t>(slot);
@ -973,16 +975,13 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s
/// <summary> /// <summary>
/// Sets local configured site data. /// Sets local configured site data.
/// </summary> /// </summary>
/// <param name="voiceChNo">Voice Channel Number list.</param>
/// <param name="voiceChData">Voice Channel data map.</param>
/// <param name="controlChData">Control Channel data.</param> /// <param name="controlChData">Control Channel data.</param>
/// <param name="netId">DMR Network ID.</param> /// <param name="netId">DMR Network ID.</param>
/// <param name="siteId">DMR Site ID.</param> /// <param name="siteId">DMR Site ID.</param>
/// <param name="channelId">Channel ID.</param> /// <param name="channelId">Channel ID.</param>
/// <param name="channelNo">Channel Number.</param> /// <param name="channelNo">Channel Number.</param>
/// <param name="requireReg"></param> /// <param name="requireReg"></param>
void Slot::setSiteData(const std::vector<uint32_t> voiceChNo, const std::unordered_map<uint32_t, ::lookups::VoiceChData> voiceChData, void Slot::setSiteData(::lookups::VoiceChData controlChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReg)
::lookups::VoiceChData controlChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReg)
{ {
m_siteData = SiteData(SITE_MODEL_SMALL, netId, siteId, 3U, requireReg); m_siteData = SiteData(SITE_MODEL_SMALL, netId, siteId, 3U, requireReg);
m_channelNo = channelNo; m_channelNo = channelNo;
@ -995,13 +994,6 @@ void Slot::setSiteData(const std::vector<uint32_t> voiceChNo, const std::unorder
} }
} }
for (uint32_t chNo : voiceChNo) {
m_affiliations->addRFCh(chNo);
}
std::unordered_map<uint32_t, ::lookups::VoiceChData> chData = std::unordered_map<uint32_t, ::lookups::VoiceChData>(voiceChData);
m_affiliations->setRFChData(chData);
m_controlChData = controlChData; m_controlChData = controlChData;
lc::CSBK::setSiteData(m_siteData); lc::CSBK::setSiteData(m_siteData);

@ -131,11 +131,10 @@ namespace dmr
/// <summary>Helper to initialize the slot processor.</summary> /// <summary>Helper to initialize the slot processor.</summary>
static void init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData siteData, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem, static void init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData siteData, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem,
network::Network* network, bool duplex, ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup, network::Network* network, bool duplex, ::lookups::ChannelLookup* chLookup, ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup,
::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, uint32_t jitter, bool verbose); ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, uint32_t jitter, bool verbose);
/// <summary>Sets local configured site data.</summary> /// <summary>Sets local configured site data.</summary>
static void setSiteData(const std::vector<uint32_t> voiceChNo, const std::unordered_map<uint32_t, ::lookups::VoiceChData> voiceChData, static void setSiteData(::lookups::VoiceChData controlChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReq);
::lookups::VoiceChData controlChData, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReq);
/// <summary>Sets TSCC Aloha configuration.</summary> /// <summary>Sets TSCC Aloha configuration.</summary>
static void setAlohaConfig(uint8_t nRandWait, uint8_t backOff); static void setAlohaConfig(uint8_t nRandWait, uint8_t backOff);

@ -7,7 +7,7 @@
* @package DVM / Modem Host Software * @package DVM / Modem Host Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "common/Log.h" #include "common/Log.h"
@ -24,8 +24,9 @@ using namespace dmr::lookups;
/// <summary> /// <summary>
/// Initializes a new instance of the DMRAffiliationLookup class. /// Initializes a new instance of the DMRAffiliationLookup class.
/// </summary> /// </summary>
/// <param name="channelLookup">Instance of the channel lookup class.</param>
/// <param name="verbose">Flag indicating whether verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether verbose logging is enabled.</param>
DMRAffiliationLookup::DMRAffiliationLookup(bool verbose) : ::lookups::AffiliationLookup("DMR Affiliation", verbose), DMRAffiliationLookup::DMRAffiliationLookup(::lookups::ChannelLookup* chLookup, bool verbose) : ::lookups::AffiliationLookup("DMR Affiliation", chLookup, verbose),
m_grantChSlotTable(), m_grantChSlotTable(),
m_tsccChNo(0U), m_tsccChNo(0U),
m_tsccSlot(0U) m_tsccSlot(0U)
@ -49,7 +50,7 @@ DMRAffiliationLookup::~DMRAffiliationLookup() = default;
/// <returns></returns> /// <returns></returns>
bool DMRAffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTimeout, bool grp, bool netGranted) bool DMRAffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTimeout, bool grp, bool netGranted)
{ {
uint32_t chNo = m_rfChTable.at(0); uint32_t chNo = m_chLookup->getFirstRFChannel();
uint8_t slot = getAvailableSlotForChannel(chNo); uint8_t slot = getAvailableSlotForChannel(chNo);
if (slot == 0U) { if (slot == 0U) {
@ -75,18 +76,17 @@ bool DMRAffiliationLookup::grantChSlot(uint32_t dstId, uint32_t srcId, uint8_t s
return false; return false;
} }
if (!isRFChAvailable()) { if (!m_chLookup->isRFChAvailable()) {
return false; return false;
} }
uint32_t chNo = m_rfChTable.at(0); uint32_t chNo = m_chLookup->getFirstRFChannel();
if (chNo == m_tsccChNo && slot == m_tsccSlot) { if (chNo == m_tsccChNo && slot == m_tsccSlot) {
return false; return false;
} }
if (getAvailableSlotForChannel(chNo) == 0U || chNo == m_tsccChNo) { if (getAvailableSlotForChannel(chNo) == 0U || chNo == m_tsccChNo) {
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo); m_chLookup->removeRFCh(chNo);
m_rfChTable.erase(it);
} }
m_grantChTable[dstId] = chNo; m_grantChTable[dstId] = chNo;
@ -156,10 +156,7 @@ bool DMRAffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll)
m_grantChSlotTable.erase(dstId); m_grantChSlotTable.erase(dstId);
m_netGrantedTable.erase(dstId); m_netGrantedTable.erase(dstId);
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo); m_chLookup->addRFCh(chNo);
if (it == m_rfChTable.end()) {
m_rfChTable.push_back(chNo);
}
if (m_rfGrantChCnt > 0U) { if (m_rfGrantChCnt > 0U) {
m_rfGrantChCnt--; m_rfGrantChCnt--;

@ -7,7 +7,7 @@
* @package DVM / Modem Host Software * @package DVM / Modem Host Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @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(__DMR_AFFILIATION_LOOKUP_H__) #if !defined(__DMR_AFFILIATION_LOOKUP_H__)
@ -15,6 +15,7 @@
#include "Defines.h" #include "Defines.h"
#include "common/lookups/AffiliationLookup.h" #include "common/lookups/AffiliationLookup.h"
#include "common/lookups/ChannelLookup.h"
#include <tuple> #include <tuple>
@ -31,7 +32,7 @@ namespace dmr
class HOST_SW_API DMRAffiliationLookup : public ::lookups::AffiliationLookup { class HOST_SW_API DMRAffiliationLookup : public ::lookups::AffiliationLookup {
public: public:
/// <summary>Initializes a new instance of the DMRAffiliationLookup class.</summary> /// <summary>Initializes a new instance of the DMRAffiliationLookup class.</summary>
DMRAffiliationLookup(bool verbose); DMRAffiliationLookup(::lookups::ChannelLookup* chLookup, bool verbose);
/// <summary>Finalizes a instance of the DMRAffiliationLookup class.</summary> /// <summary>Finalizes a instance of the DMRAffiliationLookup class.</summary>
~DMRAffiliationLookup() override; ~DMRAffiliationLookup() override;

@ -905,7 +905,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
} }
if (!m_tscc->m_affiliations->isGranted(dstId)) { if (!m_tscc->m_affiliations->isGranted(dstId)) {
if (!m_tscc->m_affiliations->isRFChAvailable()) { if (!m_tscc->m_affiliations->rfCh()->isRFChAvailable()) {
if (grp) { if (grp) {
if (!net) { if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId);
@ -981,7 +981,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
// callback REST API to permit the granted TG on the specified voice channel // callback REST API to permit the granted TG on the specified voice channel
if (m_tscc->m_authoritative && m_tscc->m_supervisor && if (m_tscc->m_authoritative && m_tscc->m_supervisor &&
m_tscc->m_channelNo != chNo) { m_tscc->m_channelNo != chNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object(); json::object req = json::object();
int state = modem::DVM_STATE::STATE_DMR; int state = modem::DVM_STATE::STATE_DMR;
@ -1030,7 +1030,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
// if the channel granted isn't the same as the TSCC; remote activate the payload channel // if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) { if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object(); json::object req = json::object();
req["dstId"].set<uint32_t>(dstId); req["dstId"].set<uint32_t>(dstId);
@ -1059,7 +1059,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
// callback REST API to permit the granted TG on the specified voice channel // callback REST API to permit the granted TG on the specified voice channel
if (m_tscc->m_authoritative && m_tscc->m_supervisor && if (m_tscc->m_authoritative && m_tscc->m_supervisor &&
m_tscc->m_channelNo != chNo) { m_tscc->m_channelNo != chNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object(); json::object req = json::object();
int state = modem::DVM_STATE::STATE_DMR; int state = modem::DVM_STATE::STATE_DMR;
@ -1106,7 +1106,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
// if the channel granted isn't the same as the TSCC; remote activate the payload channel // if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) { if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object(); json::object req = json::object();
req["dstId"].set<uint32_t>(dstId); req["dstId"].set<uint32_t>(dstId);
@ -1196,7 +1196,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
} }
if (!m_tscc->m_affiliations->isGranted(dstId)) { if (!m_tscc->m_affiliations->isGranted(dstId)) {
if (!m_tscc->m_affiliations->isRFChAvailable()) { if (!m_tscc->m_affiliations->rfCh()->isRFChAvailable()) {
if (grp) { if (grp) {
if (!net) { if (!net) {
LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId);
@ -1264,7 +1264,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
// if the channel granted isn't the same as the TSCC; remote activate the payload channel // if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) { if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object(); json::object req = json::object();
req["dstId"].set<uint32_t>(dstId); req["dstId"].set<uint32_t>(dstId);
@ -1311,7 +1311,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
// if the channel granted isn't the same as the TSCC; remote activate the payload channel // if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) { if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object(); json::object req = json::object();
req["dstId"].set<uint32_t>(dstId); req["dstId"].set<uint32_t>(dstId);

@ -662,8 +662,9 @@ void RESTAPI::restAPI_GetVoiceCh(const HTTPPayload& request, HTTPPayload& reply,
setResponseDefaultStatus(response); setResponseDefaultStatus(response);
json::array channels = json::array(); json::array channels = json::array();
if (m_host->m_voiceChData.size() > 0) { if (m_host->rfCh()->rfChDataSize() > 0) {
for (auto entry : m_host->m_voiceChData) { auto voiceChData = m_host->rfCh()->rfChDataTable();
for (auto entry : voiceChData) {
uint32_t chNo = entry.first; uint32_t chNo = entry.first;
lookups::VoiceChData data = entry.second; lookups::VoiceChData data = entry.second;
@ -1109,7 +1110,8 @@ void RESTAPI::restAPI_GetReleaseGrants(const HTTPPayload& request, HTTPPayload&
errorPayload(reply, "OK", HTTPPayload::OK); errorPayload(reply, "OK", HTTPPayload::OK);
if (m_dmr != nullptr) { if (m_dmr != nullptr) {
m_dmr->affiliations().releaseGrant(0, true); if (m_dmr->affiliations() != nullptr)
m_dmr->affiliations()->releaseGrant(0, true);
} }
if (m_p25 != nullptr) { if (m_p25 != nullptr) {
@ -1135,7 +1137,8 @@ void RESTAPI::restAPI_GetReleaseAffs(const HTTPPayload& request, HTTPPayload& re
errorPayload(reply, "OK", HTTPPayload::OK); errorPayload(reply, "OK", HTTPPayload::OK);
if (m_dmr != nullptr) { if (m_dmr != nullptr) {
m_dmr->affiliations().clearGroupAff(0, true); if (m_dmr->affiliations() != nullptr)
m_dmr->affiliations()->clearGroupAff(0, true);
} }
if (m_p25 != nullptr) { if (m_p25 != nullptr) {
@ -1179,7 +1182,7 @@ void RESTAPI::restAPI_PutRegisterCCVC(const HTTPPayload& request, HTTPPayload& r
uint32_t channelNo = req["channelNo"].get<uint32_t>(); uint32_t channelNo = req["channelNo"].get<uint32_t>();
// validate channelNo is a string within the JSON blob // validate peerId is a string within the JSON blob
if (!req["peerId"].is<int>()) { if (!req["peerId"].is<int>()) {
errorPayload(reply, "peerId was not a valid integer"); errorPayload(reply, "peerId was not a valid integer");
return; return;
@ -1189,11 +1192,39 @@ void RESTAPI::restAPI_PutRegisterCCVC(const HTTPPayload& request, HTTPPayload& r
// LogDebug(LOG_REST, "restAPI_PutRegisterCCVC(): callback, channelNo = %u, peerId = %u", channelNo, peerId); // LogDebug(LOG_REST, "restAPI_PutRegisterCCVC(): callback, channelNo = %u, peerId = %u", channelNo, peerId);
if (m_host->m_voiceChData.find(channelNo) != m_host->m_voiceChData.end()) { // validate restAddress is a string within the JSON blob
::lookups::VoiceChData voiceCh = m_host->m_voiceChData[channelNo]; if (!req["restAddress"].is<std::string>()) {
errorPayload(reply, "restAddress was not a valid string");
return;
}
if (!req["restPort"].is<int>()) {
errorPayload(reply, "restPort was not a valid integer");
return;
}
std::string restAddress = req["restAddress"].get<std::string>();
uint16_t restPort = (uint16_t)req["restPort"].get<int>();
auto voiceChData = m_host->rfCh()->rfChDataTable();
if (voiceChData.find(channelNo) != voiceChData.end()) {
::lookups::VoiceChData voiceCh = m_host->rfCh()->getRFChData(channelNo);
if (voiceCh.address() == "0.0.0.0") {
voiceCh.address(restAddress);
}
if (voiceCh.port() == 0U || voiceCh.port() == REST_API_DEFAULT_PORT) {
voiceCh.port(restPort);
}
m_host->rfCh()->setRFChData(channelNo, voiceCh);
m_host->m_voiceChPeerId[channelNo] = peerId; m_host->m_voiceChPeerId[channelNo] = peerId;
LogMessage(LOG_REST, "VC %s:%u, registration notice, peerId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), peerId, voiceCh.chId(), channelNo); LogMessage(LOG_REST, "VC %s:%u, registration notice, peerId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), peerId, voiceCh.chId(), channelNo);
LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Address %s:%u SSL %u", voiceCh.chId(), channelNo, voiceCh.address().c_str(), voiceCh.port(), voiceCh.ssl());
} else {
LogMessage(LOG_REST, "VC, registration rejected, peerId = %u, chNo = %u, VC wasn't a defined member of the CC voice channel list", peerId, channelNo);
} }
} }
@ -1790,17 +1821,19 @@ void RESTAPI::restAPI_GetDMRAffList(const HTTPPayload& request, HTTPPayload& rep
setResponseDefaultStatus(response); setResponseDefaultStatus(response);
json::array affs = json::array(); json::array affs = json::array();
std::unordered_map<uint32_t, uint32_t> affTable = m_dmr->affiliations().grpAffTable(); if (m_dmr->affiliations() != nullptr) {
if (affTable.size() > 0) { std::unordered_map<uint32_t, uint32_t> affTable = m_dmr->affiliations()->grpAffTable();
for (auto entry : affTable) { if (affTable.size() > 0) {
uint32_t srcId = entry.first; for (auto entry : affTable) {
uint32_t grpId = entry.second; uint32_t srcId = entry.first;
uint32_t grpId = entry.second;
json::object aff = json::object();
aff["srcId"].set<uint32_t>(srcId); json::object aff = json::object();
aff["grpId"].set<uint32_t>(grpId); aff["srcId"].set<uint32_t>(srcId);
aff["grpId"].set<uint32_t>(grpId);
affs.push_back(json::value(aff));
affs.push_back(json::value(aff));
}
} }
} }

@ -62,6 +62,7 @@ const uint8_t SCRAMBLER[] = {
/// <param name="modem">Instance of the Modem class.</param> /// <param name="modem">Instance of the Modem class.</param>
/// <param name="network">Instance of the BaseNetwork class.</param> /// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="duplex">Flag indicating full-duplex operation.</param> /// <param name="duplex">Flag indicating full-duplex operation.</param>
/// <param name="chLookup">Instance of the ChannelLookup class.</param>
/// <param name="ridLookup">Instance of the RadioIdLookup class.</param> /// <param name="ridLookup">Instance of the RadioIdLookup class.</param>
/// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param> /// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param>
/// <param name="idenTable">Instance of the IdenTableLookup class.</param> /// <param name="idenTable">Instance of the IdenTableLookup class.</param>
@ -70,7 +71,7 @@ const uint8_t SCRAMBLER[] = {
/// <param name="debug">Flag indicating whether P25 debug is enabled.</param> /// <param name="debug">Flag indicating whether P25 debug is enabled.</param>
/// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param>
Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t queueSize, uint32_t timeout, uint32_t tgHang, Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t queueSize, uint32_t timeout, uint32_t tgHang,
modem::Modem* modem, network::Network* network, bool duplex, lookups::RadioIdLookup* ridLookup, modem::Modem* modem, network::Network* network, bool duplex, lookups::ChannelLookup* chLookup, lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupRulesLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, lookups::TalkgroupRulesLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper,
bool dumpRCCHData, bool debug, bool verbose) : bool dumpRCCHData, bool debug, bool verbose) :
m_voice(nullptr), m_voice(nullptr),
@ -94,7 +95,7 @@ Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t q
m_idenTable(idenTable), m_idenTable(idenTable),
m_ridLookup(ridLookup), m_ridLookup(ridLookup),
m_tidLookup(tidLookup), m_tidLookup(tidLookup),
m_affiliations("NXDN Affiliations", verbose), m_affiliations("NXDN Affiliations", chLookup, verbose),
m_controlChData(), m_controlChData(),
m_idenEntry(), m_idenEntry(),
m_txImmQueue(queueSize, "NXDN Imm Frame"), m_txImmQueue(queueSize, "NXDN Imm Frame"),
@ -131,6 +132,7 @@ Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t q
m_verbose(verbose), m_verbose(verbose),
m_debug(debug) m_debug(debug)
{ {
assert(chLookup != nullptr);
assert(ridLookup != nullptr); assert(ridLookup != nullptr);
assert(tidLookup != nullptr); assert(tidLookup != nullptr);
assert(idenTable != nullptr); assert(idenTable != nullptr);
@ -197,16 +199,13 @@ void Control::reset()
/// <param name="conf">Instance of the yaml::Node class.</param> /// <param name="conf">Instance of the yaml::Node class.</param>
/// <param name="supervisor">Flag indicating whether the DMR has supervisory functions.</param> /// <param name="supervisor">Flag indicating whether the DMR has supervisory functions.</param>
/// <param name="cwCallsign">CW callsign of this host.</param> /// <param name="cwCallsign">CW callsign of this host.</param>
/// <param name="voiceChNo">Voice Channel Number list.</param>
/// <param name="voiceChData">Voice Channel data map.</param>
/// <param name="controlChData">Control Channel data.</param> /// <param name="controlChData">Control Channel data.</param>
/// <param name="siteId">NXDN Site Code.</param> /// <param name="siteId">NXDN Site Code.</param>
/// <param name="sysId">NXDN System Code.</param> /// <param name="sysId">NXDN System Code.</param>
/// <param name="channelId">Channel ID.</param> /// <param name="channelId">Channel ID.</param>
/// <param name="channelNo">Channel Number.</param> /// <param name="channelNo">Channel Number.</param>
/// <param name="printOptions"></param> /// <param name="printOptions"></param>
void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector<uint32_t> voiceChNo, void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, lookups::VoiceChData controlChData,
const std::unordered_map<uint32_t, lookups::VoiceChData> voiceChData, lookups::VoiceChData controlChData,
uint16_t siteId, uint32_t sysId, uint8_t channelId, uint32_t channelNo, bool printOptions) uint16_t siteId, uint32_t sysId, uint8_t channelId, uint32_t channelNo, bool printOptions)
{ {
yaml::Node systemConf = conf["system"]; yaml::Node systemConf = conf["system"];
@ -274,20 +273,13 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
m_siteData = SiteData(locId, channelId, (channelNo & 0x3FF), serviceClass, false); m_siteData = SiteData(locId, channelId, (channelNo & 0x3FF), serviceClass, false);
m_siteData.setCallsign(cwCallsign); m_siteData.setCallsign(cwCallsign);
for (uint32_t ch : voiceChNo) {
m_affiliations.addRFCh(ch);
}
std::unordered_map<uint32_t, ::lookups::VoiceChData> chData = std::unordered_map<uint32_t, ::lookups::VoiceChData>(voiceChData);
m_affiliations.setRFChData(chData);
m_controlChData = controlChData; m_controlChData = controlChData;
// set the grant release callback // set the grant release callback
m_affiliations.setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { 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 // callback REST API to clear TG permit for the granted TG on the specified voice channel
if (m_authoritative && m_supervisor) { if (m_authoritative && m_supervisor) {
::lookups::VoiceChData voiceChData = m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_affiliations.rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 &&
chNo != m_siteData.channelNo()) { chNo != m_siteData.channelNo()) {
json::object req = json::object(); json::object req = json::object();
@ -747,7 +739,7 @@ void Control::releaseGrantTG(uint32_t dstId)
if (m_affiliations.isGranted(dstId)) { if (m_affiliations.isGranted(dstId)) {
uint32_t chNo = m_affiliations.getGrantedCh(dstId); uint32_t chNo = m_affiliations.getGrantedCh(dstId);
uint32_t srcId = m_affiliations.getGrantedSrcId(dstId); uint32_t srcId = m_affiliations.getGrantedSrcId(dstId);
::lookups::VoiceChData voiceCh = m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceCh = m_affiliations.rfCh()->getRFChData(chNo);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NXDN, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); LogMessage(LOG_NXDN, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo);
@ -770,7 +762,7 @@ void Control::touchGrantTG(uint32_t dstId)
if (m_affiliations.isGranted(dstId)) { if (m_affiliations.isGranted(dstId)) {
uint32_t chNo = m_affiliations.getGrantedCh(dstId); uint32_t chNo = m_affiliations.getGrantedCh(dstId);
uint32_t srcId = m_affiliations.getGrantedSrcId(dstId); uint32_t srcId = m_affiliations.getGrantedSrcId(dstId);
::lookups::VoiceChData voiceCh = m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceCh = m_affiliations.rfCh()->getRFChData(chNo);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NXDN, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); LogMessage(LOG_NXDN, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo);

@ -56,7 +56,7 @@ namespace nxdn
public: public:
/// <summary>Initializes a new instance of the Control class.</summary> /// <summary>Initializes a new instance of the Control class.</summary>
Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t queueSize, uint32_t timeout, uint32_t tgHang, Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t queueSize, uint32_t timeout, uint32_t tgHang,
modem::Modem* modem, network::Network* network, bool duplex, lookups::RadioIdLookup* ridLookup, modem::Modem* modem, network::Network* network, bool duplex, lookups::ChannelLookup* chLookup, lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupRulesLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, lookups::TalkgroupRulesLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper,
bool dumpRCCHData, bool debug, bool verbose); bool dumpRCCHData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Control class.</summary> /// <summary>Finalizes a instance of the Control class.</summary>
@ -66,8 +66,7 @@ namespace nxdn
void reset(); void reset();
/// <summary>Helper to set NXDN configuration options.</summary> /// <summary>Helper to set NXDN configuration options.</summary>
void setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector<uint32_t> voiceChNo, void setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, lookups::VoiceChData controlChData,
const std::unordered_map<uint32_t, lookups::VoiceChData> voiceChData, lookups::VoiceChData controlChData,
uint16_t siteId, uint32_t sysId, uint8_t channelId, uint32_t channelNo, bool printOptions); uint16_t siteId, uint32_t sysId, uint8_t channelId, uint32_t channelNo, bool printOptions);
/// <summary>Gets a flag indicating whether the NXDN control channel is running.</summary> /// <summary>Gets a flag indicating whether the NXDN control channel is running.</summary>

@ -518,7 +518,7 @@ bool ControlSignaling::writeRF_Message_Grant(uint32_t srcId, uint32_t dstId, uin
} }
if (!m_nxdn->m_affiliations.isGranted(dstId)) { if (!m_nxdn->m_affiliations.isGranted(dstId)) {
if (!m_nxdn->m_affiliations.isRFChAvailable()) { if (!m_nxdn->m_affiliations.rfCh()->isRFChAvailable()) {
if (grp) { if (grp) {
if (!net) { if (!net) {
LogWarning(LOG_RF, "NXDN, %s queued, no channels available, dstId = %u", rcch->toString().c_str(), dstId); LogWarning(LOG_RF, "NXDN, %s queued, no channels available, dstId = %u", rcch->toString().c_str(), dstId);
@ -593,7 +593,7 @@ bool ControlSignaling::writeRF_Message_Grant(uint32_t srcId, uint32_t dstId, uin
// callback REST API to permit the granted TG on the specified voice channel // callback REST API to permit the granted TG on the specified voice channel
if (m_nxdn->m_authoritative && m_nxdn->m_supervisor) { if (m_nxdn->m_authoritative && m_nxdn->m_supervisor) {
::lookups::VoiceChData voiceChData = m_nxdn->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_nxdn->m_affiliations.rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 &&
chNo != m_nxdn->m_siteData.channelNo()) { chNo != m_nxdn->m_siteData.channelNo()) {
json::object req = json::object(); json::object req = json::object();

@ -55,6 +55,7 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
/// <param name="timeout">Transmit timeout.</param> /// <param name="timeout">Transmit timeout.</param>
/// <param name="tgHang">Amount of time to hang on the last talkgroup mode from RF.</param> /// <param name="tgHang">Amount of time to hang on the last talkgroup mode from RF.</param>
/// <param name="duplex">Flag indicating full-duplex operation.</param> /// <param name="duplex">Flag indicating full-duplex operation.</param>
/// <param name="chLookup">Instance of the ChannelLookup class.</param>
/// <param name="ridLookup">Instance of the RadioIdLookup class.</param> /// <param name="ridLookup">Instance of the RadioIdLookup class.</param>
/// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param> /// <param name="tidLookup">Instance of the TalkgroupRulesLookup class.</param>
/// <param name="idenTable">Instance of the IdenTableLookup class.</param> /// <param name="idenTable">Instance of the IdenTableLookup class.</param>
@ -65,7 +66,7 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
/// <param name="debug">Flag indicating whether P25 debug is enabled.</param> /// <param name="debug">Flag indicating whether P25 debug is enabled.</param>
/// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param>
Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::Network* network, Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::Network* network,
uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::RadioIdLookup* ridLookup, uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::ChannelLookup* chLookup, ::lookups::RadioIdLookup* ridLookup,
::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, ::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) : bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) :
m_voice(nullptr), m_voice(nullptr),
@ -93,7 +94,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q
m_idenTable(idenTable), m_idenTable(idenTable),
m_ridLookup(ridLookup), m_ridLookup(ridLookup),
m_tidLookup(tidLookup), m_tidLookup(tidLookup),
m_affiliations(this, verbose), m_affiliations(this, chLookup, verbose),
m_controlChData(), m_controlChData(),
m_idenEntry(), m_idenEntry(),
m_txImmQueue(queueSize, "P25 Imm Frame"), m_txImmQueue(queueSize, "P25 Imm Frame"),
@ -140,6 +141,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q
m_verbose(verbose), m_verbose(verbose),
m_debug(debug) m_debug(debug)
{ {
assert(chLookup != nullptr);
assert(ridLookup != nullptr); assert(ridLookup != nullptr);
assert(tidLookup != nullptr); assert(tidLookup != nullptr);
assert(idenTable != nullptr); assert(idenTable != nullptr);
@ -207,8 +209,6 @@ void Control::reset()
/// <param name="conf">Instance of the yaml::Node class.</param> /// <param name="conf">Instance of the yaml::Node class.</param>
/// <param name="supervisor">Flag indicating whether the DMR has supervisory functions.</param> /// <param name="supervisor">Flag indicating whether the DMR has supervisory functions.</param>
/// <param name="cwCallsign">CW callsign of this host.</param> /// <param name="cwCallsign">CW callsign of this host.</param>
/// <param name="voiceChNo">Voice Channel Number list.</param>
/// <param name="voiceChData">Voice Channel data map.</param>
/// <param name="controlChData">Control Channel data.</param> /// <param name="controlChData">Control Channel data.</param>
/// <param name="pSuperGroup"></param> /// <param name="pSuperGroup"></param>
/// <param name="netId">P25 Network ID.</param> /// <param name="netId">P25 Network ID.</param>
@ -218,8 +218,7 @@ void Control::reset()
/// <param name="channelId">Channel ID.</param> /// <param name="channelId">Channel ID.</param>
/// <param name="channelNo">Channel Number.</param> /// <param name="channelNo">Channel Number.</param>
/// <param name="printOptions"></param> /// <param name="printOptions"></param>
void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector<uint32_t> voiceChNo, void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const ::lookups::VoiceChData controlChData,
const std::unordered_map<uint32_t, ::lookups::VoiceChData> voiceChData, const ::lookups::VoiceChData controlChData,
uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions) uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions)
{ {
yaml::Node systemConf = conf["system"]; yaml::Node systemConf = conf["system"];
@ -409,13 +408,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
} }
} }
m_siteData.setChCnt((uint8_t)voiceChNo.size()); m_siteData.setChCnt((uint8_t)m_affiliations.rfCh()->rfChSize());
for (uint32_t ch : voiceChNo) {
m_affiliations.addRFCh(ch);
}
std::unordered_map<uint32_t, ::lookups::VoiceChData> chData = std::unordered_map<uint32_t, ::lookups::VoiceChData>(voiceChData);
m_affiliations.setRFChData(chData);
m_controlChData = controlChData; m_controlChData = controlChData;
@ -423,7 +416,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
m_affiliations.setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { 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 // callback REST API to clear TG permit for the granted TG on the specified voice channel
if (m_authoritative && m_supervisor) { if (m_authoritative && m_supervisor) {
::lookups::VoiceChData voiceChData = m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_affiliations.rfCh()->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 && if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0 &&
chNo != m_siteData.channelNo()) { chNo != m_siteData.channelNo()) {
json::object req = json::object(); json::object req = json::object();
@ -999,7 +992,7 @@ void Control::releaseGrantTG(uint32_t dstId)
if (m_affiliations.isGranted(dstId)) { if (m_affiliations.isGranted(dstId)) {
uint32_t chNo = m_affiliations.getGrantedCh(dstId); uint32_t chNo = m_affiliations.getGrantedCh(dstId);
uint32_t srcId = m_affiliations.getGrantedSrcId(dstId); uint32_t srcId = m_affiliations.getGrantedSrcId(dstId);
::lookups::VoiceChData voiceCh = m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceCh = m_affiliations.rfCh()->getRFChData(chNo);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_P25, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); LogMessage(LOG_P25, "VC %s:%u, TG grant released, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo);
@ -1022,7 +1015,7 @@ void Control::touchGrantTG(uint32_t dstId)
if (m_affiliations.isGranted(dstId)) { if (m_affiliations.isGranted(dstId)) {
uint32_t chNo = m_affiliations.getGrantedCh(dstId); uint32_t chNo = m_affiliations.getGrantedCh(dstId);
uint32_t srcId = m_affiliations.getGrantedSrcId(dstId); uint32_t srcId = m_affiliations.getGrantedSrcId(dstId);
::lookups::VoiceChData voiceCh = m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceCh = m_affiliations.rfCh()->getRFChData(chNo);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_P25, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo); LogMessage(LOG_P25, "VC %s:%u, call in progress, srcId = %u, dstId = %u, chId = %u, chNo = %u", voiceCh.address().c_str(), voiceCh.port(), srcId, dstId, voiceCh.chId(), chNo);

@ -57,7 +57,7 @@ namespace p25
public: public:
/// <summary>Initializes a new instance of the Control class.</summary> /// <summary>Initializes a new instance of the Control class.</summary>
Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::Network* network, Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::Network* network,
uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::RadioIdLookup* ridLookup, uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::ChannelLookup* chLookup, ::lookups::RadioIdLookup* ridLookup,
::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, ::lookups::TalkgroupRulesLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose); bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Control class.</summary> /// <summary>Finalizes a instance of the Control class.</summary>
@ -67,8 +67,7 @@ namespace p25
void reset(); void reset();
/// <summary>Helper to set P25 configuration options.</summary> /// <summary>Helper to set P25 configuration options.</summary>
void setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const std::vector<uint32_t> voiceChNo, void setOptions(yaml::Node& conf, bool supervisor, const std::string cwCallsign, const ::lookups::VoiceChData controlChData,
const std::unordered_map<uint32_t, ::lookups::VoiceChData> voiceChData, const ::lookups::VoiceChData controlChData,
uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions); uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions);
/// <summary>Gets a flag indicating whether the P25 control channel is running.</summary> /// <summary>Gets a flag indicating whether the P25 control channel is running.</summary>

@ -7,7 +7,7 @@
* @package DVM / Modem Host Software * @package DVM / Modem Host Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "common/Log.h" #include "common/Log.h"
@ -24,8 +24,9 @@ using namespace p25::lookups;
/// Initializes a new instance of the P25AffiliationLookup class. /// Initializes a new instance of the P25AffiliationLookup class.
/// </summary> /// </summary>
/// <param name="name">Name of lookup table.</param> /// <param name="name">Name of lookup table.</param>
/// <param name="channelLookup">Instance of the channel lookup class.</param>
/// <param name="verbose">Flag indicating whether verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether verbose logging is enabled.</param>
P25AffiliationLookup::P25AffiliationLookup(Control* p25, bool verbose) : ::lookups::AffiliationLookup("P25 Affiliation", verbose), P25AffiliationLookup::P25AffiliationLookup(Control* p25, ::lookups::ChannelLookup* chLookup, bool verbose) : ::lookups::AffiliationLookup("P25 Affiliation", chLookup, verbose),
m_p25(p25) m_p25(p25)
{ {
/* stub */ /* stub */
@ -46,10 +47,10 @@ bool P25AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll)
bool ret = ::lookups::AffiliationLookup::releaseGrant(dstId, releaseAll); bool ret = ::lookups::AffiliationLookup::releaseGrant(dstId, releaseAll);
if (ret) { if (ret) {
if (m_rfGrantChCnt > 0U) { if (m_rfGrantChCnt > 0U) {
m_p25->m_siteData.setChCnt(getRFChCnt() + m_rfGrantChCnt); m_p25->m_siteData.setChCnt(m_chLookup->rfChSize() + m_rfGrantChCnt);
} }
else { else {
m_p25->m_siteData.setChCnt(getRFChCnt()); m_p25->m_siteData.setChCnt(m_chLookup->rfChSize());
} }
} }

@ -7,7 +7,7 @@
* @package DVM / Modem Host Software * @package DVM / Modem Host Software
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* *
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL
* *
*/ */
#if !defined(__P25_AFFILIATION_LOOKUP_H__) #if !defined(__P25_AFFILIATION_LOOKUP_H__)
@ -15,6 +15,7 @@
#include "Defines.h" #include "Defines.h"
#include "common/lookups/AffiliationLookup.h" #include "common/lookups/AffiliationLookup.h"
#include "common/lookups/ChannelLookup.h"
namespace p25 namespace p25
{ {
@ -35,7 +36,7 @@ namespace p25
class HOST_SW_API P25AffiliationLookup : public ::lookups::AffiliationLookup { class HOST_SW_API P25AffiliationLookup : public ::lookups::AffiliationLookup {
public: public:
/// <summary>Initializes a new instance of the P25AffiliationLookup class.</summary> /// <summary>Initializes a new instance of the P25AffiliationLookup class.</summary>
P25AffiliationLookup(Control* p25, bool verbose); P25AffiliationLookup(Control* p25, ::lookups::ChannelLookup* chLookup, bool verbose);
/// <summary>Finalizes a instance of the P25AffiliationLookup class.</summary> /// <summary>Finalizes a instance of the P25AffiliationLookup class.</summary>
~P25AffiliationLookup() override; ~P25AffiliationLookup() override;

@ -2201,7 +2201,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
} }
if (!m_p25->m_affiliations.isGranted(dstId)) { if (!m_p25->m_affiliations.isGranted(dstId)) {
if (!m_p25->m_affiliations.isRFChAvailable()) { if (!m_p25->m_affiliations.rfCh()->isRFChAvailable()) {
if (grp) { if (grp) {
if (!net) { if (!net) {
LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Request) queued, no channels available, dstId = %u", dstId); LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Request) queued, no channels available, dstId = %u", dstId);
@ -2234,7 +2234,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
else { else {
if (m_p25->m_affiliations.grantCh(dstId, srcId, GRANT_TIMER_TIMEOUT, grp, net)) { if (m_p25->m_affiliations.grantCh(dstId, srcId, GRANT_TIMER_TIMEOUT, grp, net)) {
chNo = m_p25->m_affiliations.getGrantedCh(dstId); chNo = m_p25->m_affiliations.getGrantedCh(dstId);
m_p25->m_siteData.setChCnt(m_p25->m_affiliations.getRFChCnt() + m_p25->m_affiliations.getGrantedRFChCnt()); m_p25->m_siteData.setChCnt(m_p25->m_affiliations.rfCh()->rfChSize() + m_p25->m_affiliations.getGrantedRFChCnt());
} }
} }
} }
@ -2274,7 +2274,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
} }
if (chNo > 0U) { if (chNo > 0U) {
::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.rfCh()->getRFChData(chNo);
if (grp) { if (grp) {
if (!net) { if (!net) {
@ -2418,7 +2418,7 @@ void ControlSignaling::writeRF_TSDU_Grant_Update()
uint32_t chNo = entry.second; uint32_t chNo = entry.second;
bool grp = m_p25->m_affiliations.isGroup(dstId); bool grp = m_p25->m_affiliations.isGroup(dstId);
::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.rfCh()->getRFChData(chNo);
if (chNo == 0U) { if (chNo == 0U) {
noData = true; noData = true;
@ -2503,7 +2503,7 @@ bool ControlSignaling::writeRF_TSDU_SNDCP_Grant(uint32_t srcId, uint32_t dstId,
} }
if (!m_p25->m_affiliations.isGranted(srcId)) { if (!m_p25->m_affiliations.isGranted(srcId)) {
if (!m_p25->m_affiliations.isRFChAvailable()) { if (!m_p25->m_affiliations.rfCh()->isRFChAvailable()) {
if (!net) { if (!net) {
LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_ISP_SNDCP_CH_REQ (SNDCP Data Channel Request) queued, no channels available, srcId = %u", srcId); LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_ISP_SNDCP_CH_REQ (SNDCP Data Channel Request) queued, no channels available, srcId = %u", srcId);
writeRF_TSDU_Queue(srcId, dstId, P25_QUE_RSN_CHN_RESOURCE_NOT_AVAIL, TSBK_ISP_SNDCP_CH_REQ); writeRF_TSDU_Queue(srcId, dstId, P25_QUE_RSN_CHN_RESOURCE_NOT_AVAIL, TSBK_ISP_SNDCP_CH_REQ);
@ -2517,18 +2517,18 @@ bool ControlSignaling::writeRF_TSDU_SNDCP_Grant(uint32_t srcId, uint32_t dstId,
else { else {
if (m_p25->m_affiliations.grantCh(srcId, srcId, GRANT_TIMER_TIMEOUT, false, net)) { if (m_p25->m_affiliations.grantCh(srcId, srcId, GRANT_TIMER_TIMEOUT, false, net)) {
uint32_t chNo = m_p25->m_affiliations.getGrantedCh(srcId); uint32_t chNo = m_p25->m_affiliations.getGrantedCh(srcId);
::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.rfCh()->getRFChData(chNo);
osp->setGrpVchId(voiceChData.chId()); osp->setGrpVchId(voiceChData.chId());
osp->setGrpVchNo(chNo); osp->setGrpVchNo(chNo);
osp->setDataChnNo(chNo); osp->setDataChnNo(chNo);
m_p25->m_siteData.setChCnt(m_p25->m_affiliations.getRFChCnt() + m_p25->m_affiliations.getGrantedRFChCnt()); m_p25->m_siteData.setChCnt(m_p25->m_affiliations.rfCh()->rfChSize() + m_p25->m_affiliations.getGrantedRFChCnt());
} }
} }
} }
else { else {
uint32_t chNo = m_p25->m_affiliations.getGrantedCh(srcId); uint32_t chNo = m_p25->m_affiliations.getGrantedCh(srcId);
::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.rfCh()->getRFChData(chNo);
osp->setGrpVchId(voiceChData.chId()); osp->setGrpVchId(voiceChData.chId());
osp->setGrpVchNo(chNo); osp->setGrpVchNo(chNo);

@ -413,7 +413,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
// if voice on control; insert grant updates before voice traffic // if voice on control; insert grant updates before voice traffic
if (m_p25->m_voiceOnControl) { if (m_p25->m_voiceOnControl) {
uint32_t chNo = m_p25->m_affiliations.getGrantedCh(dstId); uint32_t chNo = m_p25->m_affiliations.getGrantedCh(dstId);
::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.rfCh()->getRFChData(chNo);
bool grp = m_p25->m_affiliations.isGroup(dstId); bool grp = m_p25->m_affiliations.isGroup(dstId);
std::unique_ptr<lc::TSBK> osp; std::unique_ptr<lc::TSBK> osp;
@ -516,7 +516,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
// if voice on control; insert group voice channel updates directly after HDU but before LDUs // if voice on control; insert group voice channel updates directly after HDU but before LDUs
if (m_p25->m_voiceOnControl) { if (m_p25->m_voiceOnControl) {
uint32_t chNo = m_p25->m_affiliations.getGrantedCh(dstId); uint32_t chNo = m_p25->m_affiliations.getGrantedCh(dstId);
::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.rfCh()->getRFChData(chNo);
bool grp = m_p25->m_affiliations.isGroup(dstId); bool grp = m_p25->m_affiliations.isGroup(dstId);
std::unique_ptr<lc::TSBK> osp; std::unique_ptr<lc::TSBK> osp;
@ -1676,7 +1676,7 @@ void Voice::writeNet_LDU1()
// if voice on control; insert grant updates before voice traffic // if voice on control; insert grant updates before voice traffic
if (m_p25->m_voiceOnControl) { if (m_p25->m_voiceOnControl) {
uint32_t chNo = m_p25->m_affiliations.getGrantedCh(dstId); uint32_t chNo = m_p25->m_affiliations.getGrantedCh(dstId);
::lookups::VoiceChData voiceChData = m_p25->m_affiliations.getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_p25->m_affiliations.rfCh()->getRFChData(chNo);
bool grp = m_p25->m_affiliations.isGroup(dstId); bool grp = m_p25->m_affiliations.isGroup(dstId);
std::unique_ptr<lc::TSBK> osp; std::unique_ptr<lc::TSBK> osp;

@ -182,6 +182,9 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin
if (address.empty()) { if (address.empty()) {
return ERRNO_NO_ADDRESS; return ERRNO_NO_ADDRESS;
} }
if (address == "0.0.0.0") {
return ERRNO_NO_ADDRESS;
}
if (port <= 0U) { if (port <= 0U) {
return ERRNO_NO_ADDRESS; return ERRNO_NO_ADDRESS;
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.