mirror of
https://github.com/meshcore-dev/meshcore_py.git
synced 2026-04-20 22:13:49 +00:00
Modify statistics handling in MessageReader to support binary parsing for core, radio, and packet stats. Update DeviceCommands to send appropriate commands for each stats type.
This commit is contained in:
parent
f82ed89c02
commit
4a97dd0968
2 changed files with 87 additions and 35 deletions
|
|
@ -208,12 +208,15 @@ class DeviceCommands(CommandHandlerBase):
|
|||
|
||||
async def get_stats_core(self) -> Event:
|
||||
logger.debug("Getting core statistics")
|
||||
return await self.send(b"\x38", [EventType.STATS_CORE, EventType.ERROR])
|
||||
# CMD_GET_STATS (56) + STATS_TYPE_CORE (0)
|
||||
return await self.send(b"\x38\x00", [EventType.STATS_CORE, EventType.ERROR])
|
||||
|
||||
async def get_stats_radio(self) -> Event:
|
||||
logger.debug("Getting radio statistics")
|
||||
return await self.send(b"\x39", [EventType.STATS_RADIO, EventType.ERROR])
|
||||
# CMD_GET_STATS (56) + STATS_TYPE_RADIO (1)
|
||||
return await self.send(b"\x38\x01", [EventType.STATS_RADIO, EventType.ERROR])
|
||||
|
||||
async def get_stats_packets(self) -> Event:
|
||||
logger.debug("Getting packet statistics")
|
||||
return await self.send(b"\x3a", [EventType.STATS_PACKETS, EventType.ERROR])
|
||||
# CMD_GET_STATS (56) + STATS_TYPE_PACKETS (2)
|
||||
return await self.send(b"\x38\x02", [EventType.STATS_PACKETS, EventType.ERROR])
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import json
|
||||
import struct
|
||||
import time
|
||||
import io
|
||||
from typing import Any, Dict
|
||||
|
|
@ -285,38 +286,86 @@ class MessageReader:
|
|||
logger.debug(f"got custom vars : {res}")
|
||||
await self.dispatcher.dispatch(Event(EventType.CUSTOM_VARS, res))
|
||||
|
||||
elif packet_type_value == PacketType.STATS_CORE.value:
|
||||
logger.debug(f"received stats core response: {data.hex()}")
|
||||
rawdata = dbuf.read().decode("utf-8", "ignore")
|
||||
try:
|
||||
res = json.loads(rawdata)
|
||||
logger.debug(f"parsed stats core: {res}")
|
||||
await self.dispatcher.dispatch(Event(EventType.STATS_CORE, res))
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error parsing stats core JSON: {e}, raw data: {rawdata}")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"json_parse_error: {e}"}))
|
||||
|
||||
elif packet_type_value == PacketType.STATS_RADIO.value:
|
||||
logger.debug(f"received stats radio response: {data.hex()}")
|
||||
rawdata = dbuf.read().decode("utf-8", "ignore")
|
||||
try:
|
||||
res = json.loads(rawdata)
|
||||
logger.debug(f"parsed stats radio: {res}")
|
||||
await self.dispatcher.dispatch(Event(EventType.STATS_RADIO, res))
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error parsing stats radio JSON: {e}, raw data: {rawdata}")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"json_parse_error: {e}"}))
|
||||
|
||||
elif packet_type_value == PacketType.STATS_PACKETS.value:
|
||||
logger.debug(f"received stats packets response: {data.hex()}")
|
||||
rawdata = dbuf.read().decode("utf-8", "ignore")
|
||||
try:
|
||||
res = json.loads(rawdata)
|
||||
logger.debug(f"parsed stats packets: {res}")
|
||||
await self.dispatcher.dispatch(Event(EventType.STATS_PACKETS, res))
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error parsing stats packets JSON: {e}, raw data: {rawdata}")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"json_parse_error: {e}"}))
|
||||
elif packet_type_value == PacketType.STATS_CORE.value: # RESP_CODE_STATS (24)
|
||||
logger.debug(f"received stats response: {data.hex()}")
|
||||
# RESP_CODE_STATS: All stats responses use code 24 with sub-type byte
|
||||
# Byte 0: response_code (24), Byte 1: stats_type (0=core, 1=radio, 2=packets)
|
||||
if len(data) < 2:
|
||||
logger.error(f"Stats response too short: {len(data)} bytes, need at least 2 for header")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
||||
return
|
||||
|
||||
stats_type = data[1]
|
||||
|
||||
if stats_type == 0: # STATS_TYPE_CORE
|
||||
# RESP_CODE_STATS + STATS_TYPE_CORE: 11 bytes total
|
||||
# Format: <B B H I H B (response_code, stats_type, battery_mv, uptime_secs, errors, queue_len)
|
||||
if len(data) < 11:
|
||||
logger.error(f"Stats core response too short: {len(data)} bytes, expected 11")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
||||
else:
|
||||
try:
|
||||
battery_mv, uptime_secs, errors, queue_len = struct.unpack('<H I H B', data[2:11])
|
||||
res = {
|
||||
'battery_mv': battery_mv,
|
||||
'uptime_secs': uptime_secs,
|
||||
'errors': errors,
|
||||
'queue_len': queue_len
|
||||
}
|
||||
logger.debug(f"parsed stats core: {res}")
|
||||
await self.dispatcher.dispatch(Event(EventType.STATS_CORE, res))
|
||||
except struct.error as e:
|
||||
logger.error(f"Error parsing stats core binary frame: {e}, data: {data.hex()}")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"binary_parse_error: {e}"}))
|
||||
|
||||
elif stats_type == 1: # STATS_TYPE_RADIO
|
||||
# RESP_CODE_STATS + STATS_TYPE_RADIO: 14 bytes total
|
||||
# Format: <B B h b b I I (response_code, stats_type, noise_floor, last_rssi, last_snr, tx_air_secs, rx_air_secs)
|
||||
if len(data) < 14:
|
||||
logger.error(f"Stats radio response too short: {len(data)} bytes, expected 14")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
||||
else:
|
||||
try:
|
||||
noise_floor, last_rssi, last_snr_scaled, tx_air_secs, rx_air_secs = struct.unpack('<h b b I I', data[2:14])
|
||||
res = {
|
||||
'noise_floor': noise_floor,
|
||||
'last_rssi': last_rssi,
|
||||
'last_snr': last_snr_scaled / 4.0, # Unscale SNR (was multiplied by 4)
|
||||
'tx_air_secs': tx_air_secs,
|
||||
'rx_air_secs': rx_air_secs
|
||||
}
|
||||
logger.debug(f"parsed stats radio: {res}")
|
||||
await self.dispatcher.dispatch(Event(EventType.STATS_RADIO, res))
|
||||
except struct.error as e:
|
||||
logger.error(f"Error parsing stats radio binary frame: {e}, data: {data.hex()}")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"binary_parse_error: {e}"}))
|
||||
|
||||
elif stats_type == 2: # STATS_TYPE_PACKETS
|
||||
# RESP_CODE_STATS + STATS_TYPE_PACKETS: 26 bytes total
|
||||
# Format: <B B I I I I I I (response_code, stats_type, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx)
|
||||
if len(data) < 26:
|
||||
logger.error(f"Stats packets response too short: {len(data)} bytes, expected 26")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": "invalid_frame_length"}))
|
||||
else:
|
||||
try:
|
||||
recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = struct.unpack('<I I I I I I', data[2:26])
|
||||
res = {
|
||||
'recv': recv,
|
||||
'sent': sent,
|
||||
'flood_tx': flood_tx,
|
||||
'direct_tx': direct_tx,
|
||||
'flood_rx': flood_rx,
|
||||
'direct_rx': direct_rx
|
||||
}
|
||||
logger.debug(f"parsed stats packets: {res}")
|
||||
await self.dispatcher.dispatch(Event(EventType.STATS_PACKETS, res))
|
||||
except struct.error as e:
|
||||
logger.error(f"Error parsing stats packets binary frame: {e}, data: {data.hex()}")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"binary_parse_error: {e}"}))
|
||||
|
||||
else:
|
||||
logger.error(f"Unknown stats type: {stats_type}, data: {data.hex()}")
|
||||
await self.dispatcher.dispatch(Event(EventType.ERROR, {"reason": f"unknown_stats_type: {stats_type}"}))
|
||||
|
||||
elif packet_type_value == PacketType.CHANNEL_INFO.value:
|
||||
logger.debug(f"received channel info response: {data.hex()}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue