mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-25 04:00:30 +00:00
* If a packet is heard multiple times, rebroadcast using the highest hop limit Sometimes a packet will be in the TX queue waiting to be transmitted, when it is overheard being rebroadcast by another node, with a higher hop limit remaining. When this occurs, modify the pending packet in the TX queue to avoid unnecessarily wasting hops. * Reprocess instead of modifying queued packet In order to ensure that the traceroute module works correctly, rather than modifying the hop limnit of the existing queued version of the packet, simply drop it ifrom the queue and reprocess the version of the packet with the superior hop limit. * Update protobufs submodule * Merge upstream/develop into overheard-hoptimisation branch Resolved conflicts in: - src/mesh/FloodingRouter.cpp: Integrated hop limit optimization with refactored duplicate handling - src/mesh/MeshPacketQueue.h: Kept both hop_limit_lt parameter and new find() method * Improve method naming and code clarity - Rename findPacket() to getPacketFromQueue() for better clarity - Make code DRY by having find() use getPacketFromQueue() internally - Resolves method overloading conflict with clearer naming * If a packet is heard multiple times, rebroadcast using the highest hop limit Sometimes a packet will be in the TX queue waiting to be transmitted, when it is overheard being rebroadcast by another node, with a higher hop limit remaining. When this occurs, modify the pending packet in the TX queue to avoid unnecessarily wasting hops. * Improve router role checking using IS_ONE_OF macro - Replace multiple individual role checks with cleaner IS_ONE_OF macro - Add CLIENT_BASE support as suggested in PR #7992 - Include MeshTypes.h for IS_ONE_OF macro - Makes code more maintainable and consistent with other parts of codebase * Apply IS_ONE_OF improvement to NextHopRouter.cpp - Replace multiple individual role checks with cleaner IS_ONE_OF macro - Add CLIENT_BASE support for consistency - Include MeshTypes.h for IS_ONE_OF macro - Matches the pattern used in FloodingRouter.cpp * Create and apply IS_ROUTER_ROLE() macro across codebase - Add IS_ROUTER_ROLE() macro to meshUtils.h for consistent router role checking - Update FloodingRouter.cpp to use macro in multiple locations - Update NextHopRouter.cpp to use macro - Include CLIENT_BASE role support * Core Changes: - Add hop_limit field to PacketRecord (17B→20B due to alignment) - Extend wasSeenRecently() with wasUpgraded parameter - Enable router optimization without duplicate app delivery - Handle ROUTER_LATE delayed transmission properly Technical Details: - Memory overhead: ~4000 bytes for 1000 records - Prevents duplicate message delivery while enabling routing optimization - Maintains protocol integrity for ACK/NAK handling - Supports upgrade from hop_limit=0 to hop_limit>0 scenarios * Delete files accdentally added for merge * Trunk formatting * Packets are supposed to be unsigned. Thankfully, it's only a log message. * Upgrade all packets, not just 0 hop packets. * Not just unsigned, but hex. Updating packet log IDs. * Fixed order of operations issue that prevented packetrs from being removed from the queue * Fixing some bugs after testing. Only storing the maximum hop value in PacketRecord which makes sense. Also, updating messaging to make more sense in the logs. * Fixed flow logic about how to handle re-inserting duplicate packets. Removed IS_ROUTER_ROLE macro and replaced it with better isRebroadcaster(). * Add logic to re-run modules, but avoid re-sending to phone. * Refactor how to process the new packet with hops. Only update nodeDB and traceRouteModule. * - Apply changes to both FloodingRouter and NextHopRouter classes to make packets mutable for traceroute - MESHTASTIC_EXCLUDE_TRACEROUTE guard for when we don't want traceroute * Allow MeshPacket to be modified in-place in processUpgradePacket * let's not make a copy where a copy is unncessary. --------- Co-authored-by: Clive Blackledge <clive@ansible.org> Co-authored-by: Clive Blackledge <git@ansible.org> Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
182 lines
6.2 KiB
C++
182 lines
6.2 KiB
C++
#include "MeshPacketQueue.h"
|
|
#include "NodeDB.h"
|
|
#include "configuration.h"
|
|
#include <assert.h>
|
|
|
|
#include <algorithm>
|
|
|
|
/// @return the priority of the specified packet
|
|
inline uint32_t getPriority(const meshtastic_MeshPacket *p)
|
|
{
|
|
auto pri = p->priority;
|
|
return pri;
|
|
}
|
|
|
|
/// @return "true" if "p1" is ordered before "p2"
|
|
bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_MeshPacket *p2)
|
|
{
|
|
assert(p1 && p2);
|
|
|
|
// If one packet is in the late transmit window, prefer the other one
|
|
if ((bool)p1->tx_after != (bool)p2->tx_after) {
|
|
return !p1->tx_after;
|
|
}
|
|
|
|
auto p1p = getPriority(p1), p2p = getPriority(p2);
|
|
// If priorities differ, use that
|
|
// for equal priorities, prefer packets already on mesh.
|
|
return (p1p != p2p) ? (p1p > p2p) : (!isFromUs(p1) && isFromUs(p2));
|
|
}
|
|
|
|
MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {}
|
|
|
|
bool MeshPacketQueue::empty()
|
|
{
|
|
return queue.empty();
|
|
}
|
|
|
|
/**
|
|
* Some clients might not properly set priority, therefore we fix it here.
|
|
*/
|
|
void fixPriority(meshtastic_MeshPacket *p)
|
|
{
|
|
// We might receive acks from other nodes (and since generated remotely, they won't have priority assigned. Check for that
|
|
// and fix it
|
|
if (p->priority == meshtastic_MeshPacket_Priority_UNSET) {
|
|
// if a reliable message give a bit higher default priority
|
|
p->priority = (p->want_ack ? meshtastic_MeshPacket_Priority_RELIABLE : meshtastic_MeshPacket_Priority_DEFAULT);
|
|
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
|
// if acks/naks give very high priority
|
|
if (p->decoded.portnum == meshtastic_PortNum_ROUTING_APP) {
|
|
p->priority = meshtastic_MeshPacket_Priority_ACK;
|
|
// if text or admin, give high priority
|
|
} else if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
|
|
p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) {
|
|
p->priority = meshtastic_MeshPacket_Priority_HIGH;
|
|
// if it is a response, give higher priority to let it arrive early and stop the request being relayed
|
|
} else if (p->decoded.request_id != 0) {
|
|
p->priority = meshtastic_MeshPacket_Priority_RESPONSE;
|
|
// Also if we want a response, give a bit higher priority
|
|
} else if (p->decoded.want_response) {
|
|
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** enqueue a packet, return false if full */
|
|
bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p)
|
|
{
|
|
// no space - try to replace a lower priority packet in the queue
|
|
if (queue.size() >= maxLen) {
|
|
bool replaced = replaceLowerPriorityPacket(p);
|
|
if (!replaced) {
|
|
LOG_WARN("TX queue is full, and there is no lower-priority packet available to evict in favour of 0x%08x", p->id);
|
|
}
|
|
return replaced;
|
|
}
|
|
|
|
// Find the correct position using upper_bound to maintain a stable order
|
|
auto it = std::upper_bound(queue.begin(), queue.end(), p, CompareMeshPacketFunc);
|
|
queue.insert(it, p); // Insert packet at the found position
|
|
return true;
|
|
}
|
|
|
|
meshtastic_MeshPacket *MeshPacketQueue::dequeue()
|
|
{
|
|
if (empty()) {
|
|
return NULL;
|
|
}
|
|
|
|
auto *p = queue.front();
|
|
queue.erase(queue.begin()); // Remove the highest-priority packet
|
|
return p;
|
|
}
|
|
|
|
meshtastic_MeshPacket *MeshPacketQueue::getFront()
|
|
{
|
|
if (empty()) {
|
|
return NULL;
|
|
}
|
|
|
|
auto *p = queue.front();
|
|
return p;
|
|
}
|
|
|
|
/** Get a packet from this queue. Returns a pointer to the packet, or NULL if not found. */
|
|
meshtastic_MeshPacket *MeshPacketQueue::getPacketFromQueue(NodeNum from, PacketId id)
|
|
{
|
|
for (auto it = queue.begin(); it != queue.end(); it++) {
|
|
auto p = (*it);
|
|
if (getFrom(p) == from && p->id == id) {
|
|
return p;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */
|
|
meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id, bool tx_normal, bool tx_late, uint8_t hop_limit_lt)
|
|
{
|
|
for (auto it = queue.begin(); it != queue.end(); it++) {
|
|
auto p = (*it);
|
|
if (getFrom(p) == from && p->id == id && ((tx_normal && !p->tx_after) || (tx_late && p->tx_after)) &&
|
|
(!hop_limit_lt || p->hop_limit < hop_limit_lt)) {
|
|
queue.erase(it);
|
|
return p;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Attempt to find a packet from this queue. Return true if it was found. */
|
|
bool MeshPacketQueue::find(const NodeNum from, const PacketId id)
|
|
{
|
|
return getPacketFromQueue(from, id) != NULL;
|
|
}
|
|
|
|
/**
|
|
* Attempt to find a lower-priority packet in the queue and replace it with the provided one.
|
|
* @return True if the replacement succeeded, false otherwise
|
|
*/
|
|
bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
|
|
{
|
|
|
|
if (queue.empty()) {
|
|
return false; // No packets to replace
|
|
}
|
|
|
|
// Check if the packet at the back has a lower priority than the new packet
|
|
auto *backPacket = queue.back();
|
|
if (!backPacket->tx_after && backPacket->priority < p->priority) {
|
|
LOG_WARN("Dropping packet 0x%08x to make room in the TX queue for higher-priority packet 0x%08x", backPacket->id, p->id);
|
|
// Remove the back packet
|
|
queue.pop_back();
|
|
packetPool.release(backPacket);
|
|
// Insert the new packet in the correct order
|
|
enqueue(p);
|
|
return true;
|
|
}
|
|
|
|
if (backPacket->tx_after) {
|
|
// Check if there's a non-late packet with lower priority
|
|
auto it = queue.end();
|
|
auto refPacket = *--it;
|
|
for (; refPacket->tx_after && it != queue.begin(); refPacket = *--it)
|
|
;
|
|
if (!refPacket->tx_after && refPacket->priority < p->priority) {
|
|
LOG_WARN("Dropping non-late packet 0x%08x to make room in the TX queue for higher-priority packet 0x%08x",
|
|
refPacket->id, p->id);
|
|
queue.erase(it);
|
|
packetPool.release(refPacket);
|
|
// Insert the new packet in the correct order
|
|
enqueue(p);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If the back packet's priority is not lower, no replacement occurs
|
|
return false;
|
|
} |