Files
MicrOBU/microbu-esp32c5/external/vanetza-idf/vanetza/geonet/cbf_packet_buffer.cpp
T
Ashin Walpola 0e9525162d Keep the colleague's microbu-esp32c5 tree in this repository
obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but
that tree was gitignored, so a clone of this repository could not build the
firmware it ships. It is now committed here as ordinary files in its own
folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP
bridge's signature verification (--trust) used on the bench. Nothing is
fetched from or pushed to the colleague's repository; this repository and
its remotes carry everything. The folder's own .gitignore keeps build output,
downloaded components and private key material out, as it did there; the
committed file set is identical to that repository's tracked files.

The ESP32-C5 is still flashed from obu-firmware/, which only takes
vanetza-idf from microbu-esp32c5/, so the two stay separate folders.
FLASHING.md says how to take a newer version of the colleague's tree (copy
it over the folder, rebuild, test, commit).
2026-09-23 17:46:40 +02:00

227 lines
6.2 KiB
C++

#include <vanetza/common/runtime.hpp>
#include <vanetza/geonet/address.hpp>
#include <vanetza/geonet/cbf_counter.hpp>
#include <vanetza/geonet/cbf_packet_buffer.hpp>
#include <vanetza/geonet/pdu.hpp>
#include <vanetza/units/time.hpp>
#include <cassert>
#include <iterator>
namespace vanetza
{
namespace geonet
{
CbfPacket::CbfPacket(PendingPacket<GbcPdu>&& packet, const MacAddress& sender) :
m_packet(std::move(packet)), m_sender(sender)
{
}
CbfPacket::CbfPacket(PendingPacket<GbcPdu, const MacAddress&>&& packet, const MacAddress& sender) :
m_packet(PendingPacket<GbcPdu>(std::move(packet), cBroadcastMacAddress)), m_sender(sender)
{
}
const MacAddress& CbfPacket::sender() const
{
return m_sender;
}
const Address& CbfPacket::source() const
{
return m_packet.pdu().extended().source_position.gn_addr;
}
SequenceNumber CbfPacket::sequence_number() const
{
return m_packet.pdu().extended().sequence_number;
}
Clock::duration CbfPacket::reduce_lifetime(Clock::duration d)
{
return m_packet.reduce_lifetime(d);
}
std::size_t CbfPacket::length() const
{
return m_packet.length();
}
CbfPacketBuffer::CbfPacketBuffer(Runtime& rt, TimerCallback cb, std::unique_ptr<CbfCounter> cnt, std::size_t bytes) :
m_runtime(rt), m_counter(std::move(cnt)),
m_capacity_bytes(bytes), m_stored_bytes(0),
m_timer_callback(cb)
{
}
CbfPacketBuffer::~CbfPacketBuffer()
{
m_runtime.cancel(this);
}
bool CbfPacketBuffer::remove(const Identifier& id)
{
bool packet_dropped = false;
auto& id_map = m_timers.right;
auto found = id_map.find(id);
if (found != id_map.end()) {
auto& packet = found->info;
m_stored_bytes -= packet->length();
m_counter->remove(id);
m_packets.erase(packet);
remove_timer(m_timers.project_left(found));
packet_dropped = true;
}
assert(m_packets.size() == m_timers.size());
return packet_dropped;
}
void CbfPacketBuffer::remove_timer(typename timer_bimap::left_map::iterator timer_it)
{
auto& timer_map = m_timers.left;
auto successor = timer_map.erase(timer_it);
if (successor == timer_map.begin() && !timer_map.empty()) {
// erased timer was scheduled one, reschedule timer trigger
schedule_timer();
}
}
void CbfPacketBuffer::add(CbfPacket&& packet, Clock::duration timeout)
{
if(timeout <= Clock::duration::zero()) return;
m_stored_bytes += packet.length();
const auto first_timer = m_timers.left.begin();
// do head drop if necessary
while (m_stored_bytes > m_capacity_bytes && !m_packets.empty()) {
m_stored_bytes -= m_packets.front().length();
const auto id = identifier(m_packets.front());
m_timers.right.erase(id);
m_counter->remove(id);
m_packets.pop_front();
}
Timer timer = { m_runtime, timeout };
const Identifier id = identifier(packet);
m_packets.emplace_back(std::move(packet));
using timer_value = timer_bimap::value_type;
auto insertion = m_timers.insert(timer_value { timer, id, std::prev(m_packets.end()) });
if (!insertion.second) {
m_stored_bytes -= m_packets.back().length();
m_packets.pop_back();
} else {
m_counter->add(id);
}
// first expirying timer has changed (head drop or added packet)
if (m_timers.left.begin() != first_timer) {
schedule_timer();
}
assert(m_packets.size() == m_timers.size());
}
void CbfPacketBuffer::update(const Identifier& id, Clock::duration timeout)
{
auto& id_map = m_timers.right;
auto found = id_map.find(id);
if (found != id_map.end()) {
const Timer& timer = found->second;
CbfPacket& cbf_packet = *found->info;
reduce_lifetime(timer, cbf_packet);
id_map.replace_data(found, Timer { m_runtime, timeout});
m_counter->increment(id);
}
}
boost::optional<CbfPacket> CbfPacketBuffer::fetch(const Identifier& id)
{
boost::optional<CbfPacket> packet;
auto& id_map = m_timers.right;
auto found = id_map.find(id);
if (found != id_map.end()) {
const Timer& timer = found->second;
CbfPacket& cbf_packet = *found->info;
bool valid_packet = reduce_lifetime(timer, cbf_packet);
m_stored_bytes -= cbf_packet.length();
if (valid_packet) {
packet.emplace(std::move(cbf_packet));
}
m_counter->remove(id);
m_packets.erase(found->info);
remove_timer(m_timers.project_left(found));
}
return packet;
}
const CbfPacket* CbfPacketBuffer::find(const Identifier& id) const
{
const auto& id_map = m_timers.right;
auto found = id_map.find(id);
return found != id_map.end() ? &(*found->info) : nullptr;
}
std::size_t CbfPacketBuffer::counter(const Identifier& id) const
{
return m_counter->counter(id);
}
void CbfPacketBuffer::flush()
{
// fetch all expired timers
const Timer now { m_runtime, std::chrono::seconds(0) };
auto end = m_timers.left.upper_bound(now);
for (auto it = m_timers.left.begin(); it != end;) {
// reduce LT by queuing time
const Timer& timer = it->first;
CbfPacket& packet = *it->info;
bool valid_packet = reduce_lifetime(timer, packet);
m_stored_bytes -= packet.length();
if (valid_packet) {
m_timer_callback(std::move(packet).packet());
}
m_counter->remove(it->second);
m_packets.erase(it->info);
it = m_timers.left.erase(it);
}
// schedule timer if not empty
if (!m_timers.empty()) {
schedule_timer();
}
}
bool CbfPacketBuffer::reduce_lifetime(const Timer& timer, CbfPacket& packet) const
{
const auto queuing_time = m_runtime.now() - timer.start;
return packet.reduce_lifetime(queuing_time) > Clock::duration::zero();
}
void CbfPacketBuffer::schedule_timer()
{
assert(!m_timers.empty());
m_runtime.cancel(this);
Runtime::Callback cb = [this](Clock::time_point) { flush(); };
m_runtime.schedule(m_timers.left.begin()->first.expiry, cb, this);
}
CbfPacketBuffer::Timer::Timer(const Runtime& rt, Clock::duration timeout) :
expiry(rt.now() + timeout), start(rt.now())
{
}
bool CbfPacketBuffer::Timer::operator<(const Timer& other) const
{
return this->expiry < other.expiry;
}
} // namespace geonet
} // namespace vanetza