Extract ACK codes for standalone ACKs (i.e. normal/non-flood). Might resolve #278?

This commit is contained in:
jkingsman
2026-06-04 21:34:02 -07:00
parent be2b7fdacc
commit 52243cbc94
3 changed files with 111 additions and 0 deletions
+1
View File
@@ -137,6 +137,7 @@ app/
- Non-final DM attempts use the contact's effective route (`override > direct > flood`). The final retry is intentionally sent as flood even when a routing override exists.
- DM ACK state is terminal on first ACK. Retry attempts may register multiple expected ACK codes for the same message, but sibling pending codes are cleared once one ACK wins so a DM should not accrue multiple delivery confirmations from retries.
- ACKs are delivery state, not routing state. Bundled ACKs inside PATH packets still satisfy pending DM sends, but ACK history does not feed contact route learning.
- DM ACKs are matched from two independent radio emissions, so confirmation does not depend on the radio surfacing a host control frame: (1) the `EventType.ACK`/`SEND_CONFIRMED` host frame via `event_handlers.on_ack`, and (2) the raw RF packet itself via `packet_processor.process_raw_packet`. The packet processor extracts ACK codes both from PATH-return packets (flood replies, ACK embedded in `extra`) and from standalone `PayloadType.ACK` packets (direct replies, 4-byte cleartext payload), feeding both into `apply_dm_ack_code`. This matters for companion firmwares (e.g. pyMC over TCP) that do not reliably emit a separate host ACK frame for direct-routed replies.
### Echo/repeat dedup
+17
View File
@@ -358,6 +358,23 @@ async def process_raw_packet(
elif payload_type == PayloadType.PATH:
await _process_path_packet(raw_bytes, ts, packet_info)
elif payload_type == PayloadType.ACK:
# Standalone ACK packets carry the 4-byte ack code in cleartext (the
# firmware just memcpy's the uint32 into the payload). A contact answers
# a *direct*-routed DM with one of these, whereas a *flood*-routed DM is
# answered with a PATH-return that has the ACK embedded (handled above in
# _process_path_packet). We match directly from the raw RF packet so DM
# delivery confirmation does not depend on the radio also surfacing a
# separate EventType.ACK host control frame, which some companion
# firmwares (e.g. pyMC over TCP) do not reliably emit for direct ACKs.
if packet_info is not None and len(packet_info.payload) >= 4:
ack_code = packet_info.payload[:4].hex()
matched = await apply_dm_ack_code(ack_code, broadcast_fn=broadcast_event)
if matched:
logger.info("Applied standalone ACK %s from raw packet", ack_code)
else:
logger.debug("Buffered/ignored standalone ACK %s from raw packet", ack_code)
# Always broadcast raw packet for the packet feed UI (even duplicates)
# This enables the frontend cracker to see all incoming packets in real-time
broadcast_payload = RawPacketBroadcast(