mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-08-03 15:32:35 +02:00
feat: implement duplicate packet recording for UI visibility in RepeaterHandler
- Added record_duplicate method to RepeaterHandler to log known duplicate packets without forwarding. - Enhanced RepeaterDaemon to subscribe to raw packets for deduplication logging, ensuring all path variants are visible in the UI. - Updated recent_packets management to group duplicates under their original packets for better tracking.
This commit is contained in:
@@ -458,6 +458,62 @@ class RepeaterHandler(BaseHandler):
|
||||
if len(self.recent_packets) > self.max_recent_packets:
|
||||
self.recent_packets.pop(0)
|
||||
|
||||
def record_duplicate(self, packet: Packet, rssi: int = 0, snr: float = 0.0) -> None:
|
||||
"""Record a known-duplicate packet for UI/storage visibility without forwarding.
|
||||
|
||||
Called by the raw_packet_subscriber path so that path variants blocked
|
||||
by the Dispatcher's payload-based dedup still appear in the UI.
|
||||
"""
|
||||
self.rx_count += 1
|
||||
route_type = packet.header & PH_ROUTE_MASK
|
||||
if route_type in (ROUTE_TYPE_FLOOD, ROUTE_TYPE_TRANSPORT_FLOOD):
|
||||
self.recv_flood_count += 1
|
||||
self.flood_dup_count += 1
|
||||
elif route_type in (ROUTE_TYPE_DIRECT, ROUTE_TYPE_TRANSPORT_DIRECT):
|
||||
self.recv_direct_count += 1
|
||||
self.direct_dup_count += 1
|
||||
|
||||
header_info = PacketHeaderUtils.parse_header(packet.header)
|
||||
payload_type = header_info["payload_type"]
|
||||
route_type_parsed = header_info["route_type"]
|
||||
|
||||
original_path_hashes = packet.get_path_hashes_hex()
|
||||
path_hash_size = packet.get_path_hash_size()
|
||||
path_hash = self._path_hash_display(original_path_hashes)
|
||||
src_hash, dst_hash = self._packet_record_src_dst(packet, payload_type)
|
||||
|
||||
packet_record = self._build_packet_record(
|
||||
packet, payload_type, route_type_parsed, rssi, snr,
|
||||
original_path_hashes, path_hash_size, path_hash,
|
||||
src_hash, dst_hash,
|
||||
transmitted=False,
|
||||
drop_reason="Duplicate",
|
||||
is_duplicate=True,
|
||||
)
|
||||
|
||||
if self.storage:
|
||||
try:
|
||||
self.storage.record_packet(packet_record, skip_letsmesh_if_invalid=False)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to store duplicate record: {e}")
|
||||
|
||||
# Group under original in recent_packets
|
||||
if len(self.recent_packets) > 0:
|
||||
for idx in range(len(self.recent_packets) - 1, -1, -1):
|
||||
prev_pkt = self.recent_packets[idx]
|
||||
if prev_pkt.get("packet_hash") == packet_record["packet_hash"]:
|
||||
if "duplicates" not in prev_pkt:
|
||||
prev_pkt["duplicates"] = []
|
||||
prev_pkt["duplicates"].append(packet_record)
|
||||
break
|
||||
else:
|
||||
self.recent_packets.append(packet_record)
|
||||
else:
|
||||
self.recent_packets.append(packet_record)
|
||||
|
||||
if len(self.recent_packets) > self.max_recent_packets:
|
||||
self.recent_packets.pop(0)
|
||||
|
||||
def cleanup_cache(self):
|
||||
|
||||
now = time.time()
|
||||
|
||||
@@ -306,6 +306,10 @@ class RepeaterDaemon:
|
||||
n,
|
||||
)
|
||||
|
||||
# Subscribe to parsed packets (pre-dedup) so duplicate path variants
|
||||
# still appear in the web UI even though the Dispatcher blocks them.
|
||||
self.dispatcher.add_raw_packet_subscriber(self._on_raw_packet_for_dedup_logging)
|
||||
|
||||
# When trace reaches final node, push PUSH_CODE_TRACE_DATA (0x89) to companion clients (firmware onTraceRecv)
|
||||
self.trace_helper.on_trace_complete = self._on_trace_complete_for_companions
|
||||
|
||||
@@ -717,6 +721,21 @@ class RepeaterDaemon:
|
||||
except Exception as e:
|
||||
logger.debug("Push RX raw to companion: %s", e)
|
||||
|
||||
def _on_raw_packet_for_dedup_logging(self, pkt, data: bytes, analysis: dict) -> None:
|
||||
"""Record duplicate packets for UI visibility.
|
||||
|
||||
Called by Dispatcher's raw_packet_subscriber (pre-dedup) so we see
|
||||
all path variants. Only records packets the engine has already seen;
|
||||
novel packets are left for the normal handler path.
|
||||
"""
|
||||
if not self.repeater_handler:
|
||||
return
|
||||
if not self.repeater_handler.is_duplicate(pkt):
|
||||
return # First variant — will reach engine via normal handler path
|
||||
rssi = getattr(pkt, "_rssi", 0) or 0
|
||||
snr = getattr(pkt, "_snr", 0.0) or 0.0
|
||||
self.repeater_handler.record_duplicate(pkt, rssi=rssi, snr=snr)
|
||||
|
||||
async def deliver_control_data(
|
||||
self,
|
||||
snr: float,
|
||||
|
||||
Reference in New Issue
Block a user