mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-17 09:01:30 +10:00
fix: improve acknowledged unicast retry reliability (#11320)
* Improve acknowledged unicast retry reliability * Fix merged next-hop routing tests --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
co-authored by
Ben Meadors
parent
faa2c8fc52
commit
a400143090
@@ -57,12 +57,18 @@ PendingPacket::PendingPacket(meshtastic_MeshPacket *p, uint8_t numRetransmission
|
||||
{
|
||||
packet = p;
|
||||
this->numRetransmissions = numRetransmissions - 1; // We subtract one, because we assume the user just did the first send
|
||||
this->initialNumRetransmissions = this->numRetransmissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet
|
||||
*/
|
||||
ErrorCode NextHopRouter::send(meshtastic_MeshPacket *p)
|
||||
{
|
||||
return sendWithNextHop(p, true);
|
||||
}
|
||||
|
||||
ErrorCode NextHopRouter::sendWithNextHop(meshtastic_MeshPacket *p, bool trackRetransmission)
|
||||
{
|
||||
// Add any messages _we_ send to the seen message list (so we will ignore all retransmissions we see)
|
||||
p->relay_node = nodeDB->getLastByteOfNodeNum(getNodeNum()); // First set the relayer to us
|
||||
@@ -73,7 +79,8 @@ ErrorCode NextHopRouter::send(meshtastic_MeshPacket *p)
|
||||
|
||||
// If it's from us, ReliableRouter already handles retransmissions if want_ack is set. If a next hop is set and hop limit is
|
||||
// not 0 or want_ack is set, start retransmissions
|
||||
if ((!isFromUs(p) || !p->want_ack) && p->next_hop != NO_NEXT_HOP_PREFERENCE && (p->hop_limit > 0 || p->want_ack)) {
|
||||
if (trackRetransmission && (!isFromUs(p) || !p->want_ack) && p->next_hop != NO_NEXT_HOP_PREFERENCE &&
|
||||
(p->hop_limit > 0 || p->want_ack)) {
|
||||
if (auto *copy = packetPool.allocCopy(*p))
|
||||
startRetransmission(copy); // start retransmission for relayed packet
|
||||
}
|
||||
@@ -362,7 +369,7 @@ bool NextHopRouter::stopRetransmission(GlobalPacketId key)
|
||||
auto p = old->packet;
|
||||
/* Only when we already transmitted a packet via LoRa, we will cancel the packet in the Tx queue
|
||||
to avoid canceling a transmission if it was ACKed super fast via MQTT */
|
||||
if (old->numRetransmissions < NUM_RELIABLE_RETX - 1) {
|
||||
if (old->numRetransmissions < old->initialNumRetransmissions) {
|
||||
// We only cancel it if we are the original sender or if we're not a router(_late)
|
||||
if (isFromUs(p) || roleAllowsCancelingFromTxQueue(p)) {
|
||||
// remove the 'original' (identified by originator and packet->id) from the txqueue and free it
|
||||
@@ -475,13 +482,13 @@ int32_t NextHopRouter::doRetransmissions()
|
||||
}
|
||||
} else {
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet)) {
|
||||
if (NextHopRouter::send(copy) == ERRNO_SHOULD_RELEASE)
|
||||
if (sendWithNextHop(copy, false) == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(copy);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet)) {
|
||||
if (NextHopRouter::send(copy) == ERRNO_SHOULD_RELEASE)
|
||||
if (sendWithNextHop(copy, false) == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(copy);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -39,6 +39,9 @@ struct PendingPacket {
|
||||
/** Starts at NUM_RETRANSMISSIONS -1 and counts down. Once zero it will be removed from the list */
|
||||
uint8_t numRetransmissions = 0;
|
||||
|
||||
/** Initial remaining retry count, used to detect whether a retry has fired. */
|
||||
uint8_t initialNumRetransmissions = 0;
|
||||
|
||||
PendingPacket() {}
|
||||
explicit PendingPacket(meshtastic_MeshPacket *p, uint8_t numRetransmissions);
|
||||
};
|
||||
@@ -77,8 +80,8 @@ class GlobalPacketIdHashFunction
|
||||
Namely, in the PacketHistory, we keep track of (up to 3) relayers of a packet. When the ACK is delivered back to us via a node
|
||||
that also relayed the original packet, we use that node as next hop for the destination from then on. This makes sure that only
|
||||
when there’s a two-way connection, we assign a next hop. Both the ReliableRouter and NextHopRouter will do retransmissions (the
|
||||
NextHopRouter only 1 time). For the final retry, if no one actually relayed the packet, it will reset the next hop in order to
|
||||
fall back to the FloodingRouter again. Note that thus also intermediate hops will do a single retransmission if the intended
|
||||
NextHopRouter only a small number of times). For the final retry, if no one actually relayed the packet, it will reset the next
|
||||
hop in order to fall back to the FloodingRouter again. Intermediate hops also do bounded retransmissions if the intended
|
||||
next-hop didn’t relay, in order to fix changes in the middle of the route.
|
||||
*/
|
||||
class NextHopRouter : public FloodingRouter
|
||||
@@ -109,10 +112,12 @@ class NextHopRouter : public FloodingRouter
|
||||
return min(d, r);
|
||||
}
|
||||
|
||||
// The number of retransmissions intermediate nodes will do (actually 1 less than this)
|
||||
constexpr static uint8_t NUM_INTERMEDIATE_RETX = 2;
|
||||
// The number of retransmissions the original sender will do
|
||||
// Total attempts for directed hop-level delivery, including the initial send.
|
||||
constexpr static uint8_t NUM_INTERMEDIATE_RETX = 3;
|
||||
// Existing reliable broadcast budget, including the initial send.
|
||||
constexpr static uint8_t NUM_RELIABLE_RETX = 3;
|
||||
// Total attempts for acknowledged unicast from the originating node.
|
||||
constexpr static uint8_t NUM_RELIABLE_UNICAST_ATTEMPTS = 5;
|
||||
|
||||
// M3: bounded RAM route-health table (reuse-oldest eviction, like PacketHistory)
|
||||
constexpr static uint8_t ROUTE_HEALTH_MAX = 32; // ~12B/slot -> ~384B
|
||||
@@ -155,6 +160,8 @@ class NextHopRouter : public FloodingRouter
|
||||
*/
|
||||
PendingPacket *startRetransmission(meshtastic_MeshPacket *p, uint8_t numReTx = NUM_INTERMEDIATE_RETX);
|
||||
|
||||
ErrorCode sendWithNextHop(meshtastic_MeshPacket *p, bool trackRetransmission);
|
||||
|
||||
// Return true if we're allowed to cancel a packet in the txQueue (so we may never transmit it even once)
|
||||
bool roleAllowsCancelingFromTxQueue(const meshtastic_MeshPacket *p);
|
||||
|
||||
|
||||
@@ -30,8 +30,10 @@ ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p)
|
||||
auto copy = packetPool.allocCopy(*p);
|
||||
DEBUG_HEAP_AFTER("ReliableRouter::send", copy);
|
||||
|
||||
if (copy)
|
||||
startRetransmission(copy, NUM_RELIABLE_RETX);
|
||||
if (copy) {
|
||||
const uint8_t totalAttempts = isBroadcast(p->to) ? NUM_RELIABLE_RETX : NUM_RELIABLE_UNICAST_ATTEMPTS;
|
||||
startRetransmission(copy, totalAttempts);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have pending retransmissions, add the airtime of this packet to it, because during that time we cannot receive an
|
||||
|
||||
Reference in New Issue
Block a user