From b377eca2a56aedeb11b847c09c58621342ca82ce Mon Sep 17 00:00:00 2001 From: Walter Boring Date: Mon, 29 Dec 2025 19:26:39 -0500 Subject: [PATCH] update listen command packet stats tracking --- aprsd/cmds/listen.py | 67 +++++++++++++++++++++++++++++++------------- aprsd/packets/log.py | 3 +- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/aprsd/cmds/listen.py b/aprsd/cmds/listen.py index 1dedd76..4a276fd 100644 --- a/aprsd/cmds/listen.py +++ b/aprsd/cmds/listen.py @@ -22,6 +22,7 @@ from aprsd.main import cli from aprsd.packets import collector as packet_collector from aprsd.packets import core, seen_list from aprsd.packets import log as packet_log +from aprsd.packets import packet_list from aprsd.packets.filter import PacketFilter from aprsd.packets.filters import dupe_filter, packet_type from aprsd.stats import collector @@ -68,7 +69,7 @@ class APRSDListenProcessThread(rx.APRSDFilterThread): def print_packet(self, packet): if self.log_packets: - packet_log.log(packet) + packet_log.log(packet, force_log=True) def process_packet(self, packet: type[core.Packet]): if self.plugin_manager: @@ -83,7 +84,7 @@ class ListenStatsThread(APRSDThread): def __init__(self): super().__init__('PacketStatsLog') self._last_total_rx = 0 - self.period = 31 + self.period = 10 self.start_time = time.time() def loop(self): @@ -96,18 +97,14 @@ class ListenStatsThread(APRSDThread): rx_delta = total_rx - self._last_total_rx rate = rx_delta / self.period - # Get unique callsigns count from packets' from_call field - unique_callsigns = set() - if 'packets' in stats and stats['packets']: - for packet in stats['packets']: - # Handle both Packet objects and dicts (if serializable) - if hasattr(packet, 'from_call'): - if packet.from_call: - unique_callsigns.add(packet.from_call) - elif isinstance(packet, dict) and 'from_call' in packet: - if packet['from_call']: - unique_callsigns.add(packet['from_call']) - unique_callsigns_count = len(unique_callsigns) + # Get unique callsigns count from SeenList stats + seen_list_instance = seen_list.SeenList() + seen_list_stats = seen_list_instance.stats() + seen_list_instance.save() + # we have to copy the seen_list_stats to avoid the lock being held too long + with seen_list_instance.lock: + seen_list_stats = seen_list_stats.copy() + unique_callsigns_count = len(seen_list_stats) # Calculate uptime elapsed = time.time() - self.start_time @@ -119,7 +116,6 @@ class ListenStatsThread(APRSDThread): f'RX Rate: {rate:.2f} pps ' f'Total RX: {total_rx} ' f'RX Last {self.period} secs: {rx_delta} ' - f'Packets in PacketListStats: {packet_count}', ) LOGU.opt(colors=True).info( f'Uptime: {elapsed:.0f}s ({elapsed_minutes:.1f}m / {elapsed_hours:.2f}h) ' @@ -150,6 +146,38 @@ class ListenStatsThread(APRSDThread): f'({percentage_str})', ) + # Extract callsign counts from seen_list stats + callsign_counts = {} + for callsign, data in seen_list_stats.items(): + if isinstance(data, dict) and 'count' in data: + callsign_counts[callsign] = data['count'] + + # Sort callsigns by packet count (descending) and get top 10 + sorted_callsigns = sorted( + callsign_counts.items(), key=lambda x: x[1], reverse=True + )[:10] + + # Log top 10 callsigns + if sorted_callsigns: + LOGU.opt(colors=True).info( + 'Top 10 Callsigns by Packet Count:' + ) + total_ranks = len(sorted_callsigns) + for rank, (callsign, count) in enumerate(sorted_callsigns, 1): + # Use different colors based on rank: most packets (rank 1) = red, + # least packets (last rank) = green, middle = yellow + if rank == 1: + count_color_tag = 'red' + elif rank == total_ranks: + count_color_tag = 'green' + else: + count_color_tag = 'yellow' + LOGU.opt(colors=True).info( + f' {rank:2d}. ' + f'{callsign:<12}: ' + f'<{count_color_tag}>{count:6d} packets', + ) + time.sleep(1) return True @@ -292,10 +320,6 @@ def listen( keepalive_thread = keepalive.KeepAliveThread() - if not CONF.enable_seen_list: - # just deregister the class from the packet collector - packet_collector.PacketCollector().unregister(seen_list.SeenList) - # we don't want the dupe filter to run here. PacketFilter().unregister(dupe_filter.DupePacketFilter) if packet_filter: @@ -326,6 +350,11 @@ def listen( for p in pm.get_plugins(): LOG.info('Loaded plugin %s', p.__class__.__name__) + if log_packets: + LOG.info('Packet Logging is enabled') + else: + LOG.info('Packet Logging is disabled') + stats = stats_thread.APRSDStatsStoreThread() stats.start() diff --git a/aprsd/packets/log.py b/aprsd/packets/log.py index bbee973..a48720d 100644 --- a/aprsd/packets/log.py +++ b/aprsd/packets/log.py @@ -90,8 +90,9 @@ def log( tx: Optional[bool] = False, header: Optional[bool] = True, packet_count: Optional[int] = None, + force_log: Optional[bool] = False, ) -> None: - if not CONF.enable_packet_logging: + if not CONF.enable_packet_logging and not force_log: return if CONF.log_packet_format == 'multiline': log_multiline(packet, tx, header)