parent
b0e2903fb6
commit
3060769f9b
@ -0,0 +1,213 @@
|
||||
// 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.
|
||||
*
|
||||
* Copyright (C) 2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "common/p25/data/DataRouting.h"
|
||||
|
||||
using namespace p25::data;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Observes a route neighbor with the given link-layer ID and IP address. */
|
||||
|
||||
void RouteNeighborCache::observe(uint32_t llId, uint32_t ipAddress, uint64_t nowMs)
|
||||
{
|
||||
if (llId == 0U || ipAddress == 0U)
|
||||
return;
|
||||
|
||||
RouteNeighbor& neighbor = m_neighbors[llId];
|
||||
neighbor.llId = llId;
|
||||
neighbor.ipAddress = ipAddress;
|
||||
neighbor.lastSeen = nowMs;
|
||||
}
|
||||
|
||||
/* Erases a route neighbor with the given link-layer ID. */
|
||||
|
||||
bool RouteNeighborCache::erase(uint32_t llId)
|
||||
{
|
||||
return m_neighbors.erase(llId) > 0U;
|
||||
}
|
||||
|
||||
/* Clears all observed route neighbors. */
|
||||
|
||||
void RouteNeighborCache::clear()
|
||||
{
|
||||
m_neighbors.clear();
|
||||
}
|
||||
|
||||
/* Finds a route neighbor by its link-layer ID. */
|
||||
|
||||
const RouteNeighbor* RouteNeighborCache::findByLLId(uint32_t llId) const
|
||||
{
|
||||
auto it = m_neighbors.find(llId);
|
||||
return it == m_neighbors.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
/* Finds a route neighbor by its IP address. */
|
||||
|
||||
const RouteNeighbor* RouteNeighborCache::findByIPAddress(uint32_t ipAddress) const
|
||||
{
|
||||
for (const auto& entry : m_neighbors) {
|
||||
if (entry.second.ipAddress == ipAddress)
|
||||
return &entry.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Initializes a new instance of the DataDataLocationRegistry class. */
|
||||
|
||||
DataLocationRegistry::DataLocationRegistry(uint64_t staleAfterMs,
|
||||
bool learnFromInbound, UnknownLocationPolicy unknownLocationPolicy) :
|
||||
m_staleAfterMs(staleAfterMs),
|
||||
m_learnFromInbound(learnFromInbound),
|
||||
m_unknownLocationPolicy(unknownLocationPolicy),
|
||||
m_conventional(),
|
||||
m_trunked(),
|
||||
m_groupPeers()
|
||||
{
|
||||
/* stub */
|
||||
}
|
||||
|
||||
/* Updates the conventional location for a given link-layer ID. */
|
||||
|
||||
bool DataLocationRegistry::updateConventional(uint32_t llId, const ConventionalLocation& location)
|
||||
{
|
||||
if (llId == 0U || location.peerId == 0U)
|
||||
return false;
|
||||
|
||||
m_conventional[llId] = location;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Updates the trunked data route for a given link-layer ID. */
|
||||
|
||||
bool DataLocationRegistry::updateTrunked(uint32_t llId, const DataRoute& route)
|
||||
{
|
||||
if (llId == 0U || route.peerId == 0U || route.mode != AccessMode::TRUNKED)
|
||||
return false;
|
||||
|
||||
m_trunked[llId] = route;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Erases the location or route for a given link-layer ID and access mode. */
|
||||
|
||||
bool DataLocationRegistry::erase(uint32_t llId, AccessMode mode)
|
||||
{
|
||||
if (mode == AccessMode::TRUNKED)
|
||||
return m_trunked.erase(llId) > 0U;
|
||||
return m_conventional.erase(llId) > 0U;
|
||||
}
|
||||
|
||||
/* Clears all subscriber and group route state. */
|
||||
|
||||
void DataLocationRegistry::clear()
|
||||
{
|
||||
m_conventional.clear();
|
||||
m_trunked.clear();
|
||||
m_groupPeers.clear();
|
||||
}
|
||||
|
||||
/* Expires stale conventional and trunked route state based on the current time. */
|
||||
|
||||
void DataLocationRegistry::expire(uint64_t nowMs)
|
||||
{
|
||||
for (auto it = m_conventional.begin(); it != m_conventional.end();) {
|
||||
if (stale(it->second.lastSeen, nowMs))
|
||||
it = m_conventional.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_trunked.begin(); it != m_trunked.end();) {
|
||||
if (stale(it->second.lastSeen, nowMs))
|
||||
it = m_trunked.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolves the data route for a given link-layer ID and access mode. */
|
||||
|
||||
DataRoute DataLocationRegistry::resolve(uint32_t llId, AccessMode mode, uint64_t nowMs) const
|
||||
{
|
||||
DataRoute result;
|
||||
result.mode = mode;
|
||||
|
||||
// check if the requested access mode is trunked and resolve accordingly
|
||||
if (mode == AccessMode::TRUNKED) {
|
||||
auto it = m_trunked.find(llId);
|
||||
if (it == m_trunked.end() || stale(it->second.lastSeen, nowMs))
|
||||
return result;
|
||||
|
||||
result = it->second;
|
||||
result.valid = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto it = m_conventional.find(llId);
|
||||
if (it == m_conventional.end() || stale(it->second.lastSeen, nowMs))
|
||||
return result;
|
||||
|
||||
result.peerId = it->second.peerId;
|
||||
result.channelId = it->second.channelId;
|
||||
result.channelNo = it->second.channelNo;
|
||||
result.lastSeen = it->second.lastSeen;
|
||||
result.valid = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Sets the group peers for group-based routing. */
|
||||
|
||||
void DataLocationRegistry::setGroupPeers(const std::vector<uint32_t>& peerIds)
|
||||
{
|
||||
m_groupPeers.clear();
|
||||
for (uint32_t peerId : peerIds) {
|
||||
if (peerId != 0U)
|
||||
m_groupPeers.insert(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolves the data routes for a group of available peers. */
|
||||
|
||||
std::vector<DataRoute> DataLocationRegistry::resolveGroup(const std::vector<uint32_t>& availablePeerIds) const
|
||||
{
|
||||
std::vector<DataRoute> routes;
|
||||
for (uint32_t peerId : availablePeerIds) {
|
||||
if (peerId == 0U || (!m_groupPeers.empty() && m_groupPeers.count(peerId) == 0U))
|
||||
continue;
|
||||
|
||||
DataRoute route;
|
||||
route.mode = AccessMode::CONVENTIONAL;
|
||||
route.peerId = peerId;
|
||||
route.valid = true;
|
||||
routes.push_back(route);
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Checks if a location is considered stale based on the last seen timestamp and the current time. */
|
||||
|
||||
bool DataLocationRegistry::stale(uint64_t lastSeen, uint64_t nowMs) const
|
||||
{
|
||||
return m_staleAfterMs != 0U && nowMs != 0U && nowMs >= lastSeen && nowMs - lastSeen >= m_staleAfterMs;
|
||||
}
|
||||
@ -0,0 +1,320 @@
|
||||
// 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.
|
||||
*
|
||||
* Copyright (C) 2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file DataRouting.h
|
||||
* @ingroup p25_pdu
|
||||
* @file DataRouting.cpp
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
#if !defined(__P25_DATA__DATAROUTING_H__)
|
||||
#define __P25_DATA__DATAROUTING_H__
|
||||
|
||||
#include "common/Defines.h"
|
||||
#include "common/p25/data/PacketDataState.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace p25
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @addtogroup p25_pdu
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Access mode used to reach a P25 packet data subscriber.
|
||||
*/
|
||||
enum class AccessMode : uint8_t {
|
||||
CONVENTIONAL, //!< Conventional access mode.
|
||||
TRUNKED //!< Trunked access mode.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Policy applied when no current subscriber location is known.
|
||||
*/
|
||||
enum class UnknownLocationPolicy : uint8_t {
|
||||
QUEUE, //!< Queue the packet until location is known.
|
||||
ARP, //!< Attempt ARP resolution.
|
||||
DROP //!< Drop the packet if location is unknown.
|
||||
};
|
||||
/* @} */
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Struct Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Last observed conventional location for a subscriber.
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
struct DVM_COMMON_API ConventionalLocation {
|
||||
/**
|
||||
* @brief Peer ID of the subscriber.
|
||||
*/
|
||||
uint32_t peerId = 0U;
|
||||
/**
|
||||
* @brief Channel ID of the subscriber.
|
||||
*/
|
||||
uint8_t channelId = 0U;
|
||||
/**
|
||||
* @brief Channel number of the subscriber.
|
||||
*/
|
||||
uint16_t channelNo = 0U;
|
||||
/**
|
||||
* @brief Timestamp of the last observation.
|
||||
*/
|
||||
uint64_t lastSeen = 0U;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Struct Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Resolved RF route for a P25 packet data subscriber.
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
struct DVM_COMMON_API DataRoute {
|
||||
/**
|
||||
* @brief Access mode used to reach the subscriber.
|
||||
*/
|
||||
AccessMode mode = AccessMode::CONVENTIONAL;
|
||||
/**
|
||||
* @brief Peer ID of the subscriber.
|
||||
*/
|
||||
uint32_t peerId = 0U;
|
||||
/**
|
||||
* @brief Channel ID of the subscriber.
|
||||
*/
|
||||
uint8_t channelId = 0U;
|
||||
/**
|
||||
* @brief Channel number of the subscriber.
|
||||
*/
|
||||
uint16_t channelNo = 0U;
|
||||
/**
|
||||
* @brief Grant ID associated with the subscriber.
|
||||
*/
|
||||
uint32_t grantId = 0U;
|
||||
/**
|
||||
* @brief Timestamp of the last observation.
|
||||
*/
|
||||
uint64_t lastSeen = 0U;
|
||||
/**
|
||||
* @brief Indicates if the route is valid.
|
||||
*/
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Struct Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Observed ARP neighbor information without authorization semantics.
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
struct DVM_COMMON_API RouteNeighbor {
|
||||
/**
|
||||
* @brief Link-layer ID of the neighbor.
|
||||
*/
|
||||
uint32_t llId = 0U;
|
||||
/**
|
||||
* @brief IP address of the neighbor.
|
||||
*/
|
||||
uint32_t ipAddress = 0U;
|
||||
/**
|
||||
* @brief Timestamp of the last observation.
|
||||
*/
|
||||
uint64_t lastSeen = 0U;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Stores ARP observations separately from authorized bindings.
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
class DVM_COMMON_API RouteNeighborCache {
|
||||
public:
|
||||
/**
|
||||
* @brief Observes a route neighbor with the given link-layer ID and IP address.
|
||||
* @param llId Link-layer ID of the neighbor.
|
||||
* @param ipAddress IP address of the neighbor.
|
||||
* @param nowMs Timestamp of the observation in milliseconds.
|
||||
*/
|
||||
void observe(uint32_t llId, uint32_t ipAddress, uint64_t nowMs = 0U);
|
||||
/**
|
||||
* @brief Erases a route neighbor with the given link-layer ID.
|
||||
* @param llId Link-layer ID of the neighbor to erase.
|
||||
* @return True if the neighbor was found and erased, false otherwise.
|
||||
*/
|
||||
bool erase(uint32_t llId);
|
||||
/**
|
||||
* @brief Clears all observed route neighbors.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief Finds a route neighbor by its link-layer ID.
|
||||
* @param llId Link-layer ID of the neighbor to find.
|
||||
* @return Pointer to the route neighbor if found, nullptr otherwise.
|
||||
*/
|
||||
const RouteNeighbor* findByLLId(uint32_t llId) const;
|
||||
/**
|
||||
* @brief Finds a route neighbor by its IP address.
|
||||
* @param ipAddress IP address of the neighbor to find.
|
||||
* @return Pointer to the route neighbor if found, nullptr otherwise.
|
||||
*/
|
||||
const RouteNeighbor* findByIPAddress(uint32_t ipAddress) const;
|
||||
/**
|
||||
* @brief Returns the number of observed route neighbors.
|
||||
* @return Number of observed route neighbors.
|
||||
*/
|
||||
size_t size() const noexcept { return m_neighbors.size(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32_t, RouteNeighbor> m_neighbors;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Resolves conventional locations and future trunked data routes.
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
class DVM_COMMON_API DataLocationRegistry {
|
||||
public:
|
||||
/**
|
||||
* @brief Initializes a new instance of the LocationRegistry class.
|
||||
* @param staleAfterMs Duration in milliseconds after which locations are considered stale.
|
||||
* @param learnFromInbound Whether to learn locations from inbound traffic.
|
||||
* @param unknownLocationPolicy Policy for handling unknown locations.
|
||||
*/
|
||||
explicit DataLocationRegistry(uint64_t staleAfterMs = 0U, bool learnFromInbound = true,
|
||||
UnknownLocationPolicy unknownLocationPolicy = UnknownLocationPolicy::ARP);
|
||||
|
||||
/**
|
||||
* @brief Updates the conventional location for a given link-layer ID.
|
||||
* @param llId Link-layer ID of the neighbor.
|
||||
* @param location Conventional location to update.
|
||||
* @return True if the location was updated, false otherwise.
|
||||
*/
|
||||
bool updateConventional(uint32_t llId, const ConventionalLocation& location);
|
||||
/**
|
||||
* @brief Updates the trunked data route for a given link-layer ID.
|
||||
* @param llId Link-layer ID of the neighbor.
|
||||
* @param route Trunked data route to update.
|
||||
* @return True if the route was updated, false otherwise.
|
||||
*/
|
||||
bool updateTrunked(uint32_t llId, const DataRoute& route);
|
||||
/**
|
||||
* @brief Erases the location or route for a given link-layer ID and access mode.
|
||||
* @param llId Link-layer ID of the neighbor.
|
||||
* @param mode Access mode (conventional or trunked).
|
||||
* @return True if the entry was erased, false otherwise.
|
||||
*/
|
||||
bool erase(uint32_t llId, AccessMode mode);
|
||||
/**
|
||||
* @brief Clears all subscriber and group route state.
|
||||
*/
|
||||
void clear();
|
||||
/**
|
||||
* @brief Expires stale conventional and trunked route state based on the current time.
|
||||
* @param nowMs Current time in milliseconds.
|
||||
*/
|
||||
void expire(uint64_t nowMs);
|
||||
|
||||
/**
|
||||
* @brief Resolves the data route for a given link-layer ID and access mode.
|
||||
* @param llId Link-layer ID of the neighbor.
|
||||
* @param mode Access mode (conventional or trunked).
|
||||
* @param nowMs Current time in milliseconds.
|
||||
* @return Resolved data route.
|
||||
*/
|
||||
DataRoute resolve(uint32_t llId, AccessMode mode, uint64_t nowMs = 0U) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the group peers for group-based routing.
|
||||
* @param peerIds Vector of peer link-layer IDs.
|
||||
*/
|
||||
void setGroupPeers(const std::vector<uint32_t>& peerIds);
|
||||
/**
|
||||
* @brief Resolves the data routes for a group of available peers.
|
||||
* @param availablePeerIds Vector of available peer link-layer IDs.
|
||||
* @return Vector of resolved data routes for the available peers.
|
||||
*/
|
||||
std::vector<DataRoute> resolveGroup(const std::vector<uint32_t>& availablePeerIds) const;
|
||||
|
||||
/**
|
||||
* @brief Sets whether to learn from inbound data routes.
|
||||
* @param enabled True to enable learning from inbound routes, false to disable.
|
||||
*/
|
||||
void setLearnFromInbound(bool enabled) noexcept { m_learnFromInbound = enabled; }
|
||||
/**
|
||||
* @brief Checks whether learning from inbound data routes is enabled.
|
||||
* @return True if learning from inbound routes is enabled, false otherwise.
|
||||
*/
|
||||
bool learnFromInbound() const noexcept { return m_learnFromInbound; }
|
||||
/**
|
||||
* @brief Sets the policy for handling unknown locations.
|
||||
* @param policy The unknown location policy to set.
|
||||
*/
|
||||
void setUnknownLocationPolicy(UnknownLocationPolicy policy) noexcept { m_unknownLocationPolicy = policy; }
|
||||
/**
|
||||
* @brief Retrieves the current policy for handling unknown locations.
|
||||
* @return The current unknown location policy.
|
||||
*/
|
||||
UnknownLocationPolicy unknownLocationPolicy() const noexcept { return m_unknownLocationPolicy; }
|
||||
|
||||
/**
|
||||
* @brief Retrieves the count of conventional locations.
|
||||
* @return The number of conventional locations.
|
||||
*/
|
||||
size_t conventionalCount() const noexcept { return m_conventional.size(); }
|
||||
/**
|
||||
* @brief Retrieves the count of trunked locations.
|
||||
* @return The number of trunked locations.
|
||||
*/
|
||||
size_t trunkedCount() const noexcept { return m_trunked.size(); }
|
||||
|
||||
private:
|
||||
uint64_t m_staleAfterMs;
|
||||
bool m_learnFromInbound;
|
||||
UnknownLocationPolicy m_unknownLocationPolicy;
|
||||
std::unordered_map<uint32_t, ConventionalLocation> m_conventional;
|
||||
std::unordered_map<uint32_t, DataRoute> m_trunked;
|
||||
std::unordered_set<uint32_t> m_groupPeers;
|
||||
|
||||
/**
|
||||
* @brief Checks if a location is considered stale based on the last seen timestamp and the current time.
|
||||
* @param lastSeen The timestamp when the location was last seen.
|
||||
* @param nowMs The current time in milliseconds.
|
||||
* @return True if the location is stale, false otherwise.
|
||||
*/
|
||||
bool stale(uint64_t lastSeen, uint64_t nowMs) const;
|
||||
};
|
||||
|
||||
} // namespace data
|
||||
} // namespace p25
|
||||
|
||||
#endif // __P25_DATA__DATAROUTING_H__
|
||||
@ -0,0 +1,88 @@
|
||||
// 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.
|
||||
*
|
||||
* Copyright (C) 2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "common/p25/data/PacketScheduler.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace p25::data;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Class Members
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Initializes a new instance of the PacketScheduler class. */
|
||||
|
||||
PacketScheduler::PacketScheduler(uint32_t maxFrames, uint32_t maxBytes) :
|
||||
m_maxFrames(maxFrames),
|
||||
m_maxBytes(maxBytes),
|
||||
m_byteCount(0U),
|
||||
m_packets()
|
||||
{
|
||||
/* stub */
|
||||
}
|
||||
|
||||
/* Enqueues a scheduled packet into the scheduler. */
|
||||
|
||||
uint32_t PacketScheduler::enqueue(ScheduledDataPacket&& packet)
|
||||
{
|
||||
uint32_t dropped = 0U;
|
||||
uint32_t packetBytes = uint32_t(packet.userData.size());
|
||||
if (m_maxBytes != 0U && packetBytes > m_maxBytes)
|
||||
return 1U;
|
||||
|
||||
while (!m_packets.empty() &&
|
||||
((m_maxFrames != 0U && m_packets.size() >= m_maxFrames) ||
|
||||
(m_maxBytes != 0U && m_byteCount + packetBytes > m_maxBytes))) {
|
||||
pop();
|
||||
dropped++;
|
||||
}
|
||||
|
||||
m_byteCount += packetBytes;
|
||||
m_packets.emplace_back(std::move(packet));
|
||||
return dropped;
|
||||
}
|
||||
|
||||
/* Gets the next queued packet. */
|
||||
|
||||
ScheduledDataPacket* PacketScheduler::front()
|
||||
{
|
||||
return m_packets.empty() ? nullptr : &m_packets.front();
|
||||
}
|
||||
|
||||
/* Removes the next queued packet. */
|
||||
|
||||
void PacketScheduler::pop()
|
||||
{
|
||||
if (m_packets.empty())
|
||||
return;
|
||||
|
||||
uint32_t packetBytes = uint32_t(m_packets.front().userData.size());
|
||||
m_byteCount = packetBytes <= m_byteCount ? m_byteCount - packetBytes : 0U;
|
||||
m_packets.pop_front();
|
||||
}
|
||||
|
||||
/* Moves the next queued packet to the back of the queue. */
|
||||
|
||||
void PacketScheduler::rotate()
|
||||
{
|
||||
if (m_packets.size() < 2U)
|
||||
return;
|
||||
|
||||
m_packets.emplace_back(std::move(m_packets.front()));
|
||||
m_packets.pop_front();
|
||||
}
|
||||
|
||||
/* Clears all queued packets from the scheduler. */
|
||||
|
||||
void PacketScheduler::clear()
|
||||
{
|
||||
m_packets.clear();
|
||||
m_byteCount = 0U;
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
// 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.
|
||||
*
|
||||
* Copyright (C) 2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @file PacketScheduler.h
|
||||
* @ingroup p25_pdu
|
||||
* @file PacketScheduler.cpp
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
#if !defined(__P25_DATA__PACKETSCHEDULER_H__)
|
||||
#define __P25_DATA__PACKETSCHEDULER_H__
|
||||
|
||||
#include "common/Defines.h"
|
||||
#include "common/p25/data/DataHeader.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
namespace p25
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Struct Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief One queued packet data downlink.
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
struct DVM_COMMON_API ScheduledDataPacket {
|
||||
DataHeader header; //!< Header information for the P25 packet.
|
||||
std::vector<uint8_t> userData; //!< User data payload for the P25 packet.
|
||||
uint32_t llId = 0U; //!< Link-layer ID associated with the scheduled packet.
|
||||
uint32_t targetIPAddress = 0U; //!< Target IP address for the scheduled packet.
|
||||
uint64_t dueAt = 0U; //!< Timestamp indicating when the packet is due.
|
||||
uint8_t retryCount = 0U; //!< Number of retry attempts for the packet.
|
||||
bool extendedRetry = false; //!< Indicates if extended retry is enabled.
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Owns bounded queued packet data downlinks.
|
||||
* @ingroup p25_pdu
|
||||
*/
|
||||
class DVM_COMMON_API PacketScheduler {
|
||||
public:
|
||||
/**
|
||||
* @brief Initializes a new instance of the PacketScheduler class.
|
||||
* @param maxFrames The maximum number of frames the scheduler can hold.
|
||||
* @param maxBytes The maximum number of bytes the scheduler can hold.
|
||||
*/
|
||||
PacketScheduler(uint32_t maxFrames = 0U, uint32_t maxBytes = 0U);
|
||||
|
||||
/**
|
||||
* @brief Enqueues a scheduled packet into the scheduler.
|
||||
* @param packet The scheduled packet to enqueue.
|
||||
* @return The number of oldest frames dropped to make room.
|
||||
*/
|
||||
uint32_t enqueue(ScheduledDataPacket&& packet);
|
||||
|
||||
/**
|
||||
* @brief Checks if the scheduler is empty.
|
||||
* @return True if the scheduler is empty, false otherwise.
|
||||
*/
|
||||
bool empty() const noexcept { return m_packets.empty(); }
|
||||
/**
|
||||
* @brief Gets the number of packets currently in the scheduler.
|
||||
* @return The number of packets in the scheduler.
|
||||
*/
|
||||
size_t size() const noexcept { return m_packets.size(); }
|
||||
/**
|
||||
* @brief Gets the total byte count of packets currently in the scheduler.
|
||||
* @return The total byte count of packets in the scheduler.
|
||||
*/
|
||||
uint32_t byteCount() const noexcept { return m_byteCount; }
|
||||
|
||||
/**
|
||||
* @brief Gets the next queued packet.
|
||||
* @return A pointer to the next scheduled packet, or nullptr if the scheduler is empty.
|
||||
*/
|
||||
ScheduledDataPacket* front();
|
||||
/**
|
||||
* @brief Removes the next queued packet.
|
||||
*/
|
||||
void pop();
|
||||
/**
|
||||
* @brief Moves the next queued packet to the back of the queue.
|
||||
*/
|
||||
void rotate();
|
||||
/**
|
||||
* @brief Clears all queued packets from the scheduler.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
uint32_t m_maxFrames;
|
||||
uint32_t m_maxBytes;
|
||||
uint32_t m_byteCount;
|
||||
std::deque<ScheduledDataPacket> m_packets;
|
||||
};
|
||||
} // namespace data
|
||||
} // namespace p25
|
||||
|
||||
#endif // __P25_DATA__PACKETSCHEDULER_H__
|
||||
Loading…
Reference in new issue