From 8fb728dc49bb148a9c1901ffc6c3b46d89678e7b Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 2 Feb 2026 16:29:30 +1000 Subject: [PATCH 1/4] generated: rpc_definitions: more type information Regenerate definitions to get more RPC type information. Signed-off-by: Jordan Yates --- src/infuse_iot/generated/rpc_definitions.py | 2 ++ src/infuse_iot/generated/tdf_definitions.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/infuse_iot/generated/rpc_definitions.py b/src/infuse_iot/generated/rpc_definitions.py index 18e362c..263b29e 100644 --- a/src/infuse_iot/generated/rpc_definitions.py +++ b/src/infuse_iot/generated/rpc_definitions.py @@ -307,6 +307,8 @@ class RPCDefinitionBase: HELP: str DESCRIPTION: str COMMAND_ID: int + request: type[VLACompatLittleEndianStruct] + response: type[VLACompatLittleEndianStruct] class reboot(RPCDefinitionBase): diff --git a/src/infuse_iot/generated/tdf_definitions.py b/src/infuse_iot/generated/tdf_definitions.py index 407f8cf..2d9dee4 100644 --- a/src/infuse_iot/generated/tdf_definitions.py +++ b/src/infuse_iot/generated/tdf_definitions.py @@ -1330,7 +1330,7 @@ class infuse_bluetooth_rssi(TdfReadingBase): "rssi": "dBm", } _display_fmt_ = { - "infuse_id": "{}", + "infuse_id": "0x{:016x}", "rssi": "{}", } From dc9b2db45d49ae70612b4883c4c77d460a3af865 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 2 Feb 2026 15:47:40 +1000 Subject: [PATCH 2/4] rpc_wrappers: zbus_channel_state: use builtin defs Use the builtin response struct definition, not our own duplicate. Signed-off-by: Jordan Yates --- src/infuse_iot/rpc_wrappers/zbus_channel_state.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/infuse_iot/rpc_wrappers/zbus_channel_state.py b/src/infuse_iot/rpc_wrappers/zbus_channel_state.py index dec321f..e69d3e9 100644 --- a/src/infuse_iot/rpc_wrappers/zbus_channel_state.py +++ b/src/infuse_iot/rpc_wrappers/zbus_channel_state.py @@ -7,19 +7,10 @@ from infuse_iot.commands import InfuseRpcCommand from infuse_iot.definitions import rpc as rpc_defs from infuse_iot.definitions import tdf as tdf_defs -from infuse_iot.util.ctypes import VLACompatLittleEndianStruct from infuse_iot.zephyr.errno import errno class zbus_channel_state(InfuseRpcCommand, rpc_defs.zbus_channel_state): - class response(VLACompatLittleEndianStruct): - _fields_ = [ - ("pub_timestamp", ctypes.c_uint64), - ("pub_count", ctypes.c_uint32), - ("pub_period_ms", ctypes.c_uint32), - ] - vla_field = ("data", 0 * ctypes.c_byte) - class BatteryChannel: id = 0x43210000 data = tdf_defs.readings.battery_state @@ -117,12 +108,12 @@ def handle_response(self, return_code, response): from infuse_iot.time import InfuseTime - pub_time = InfuseTime.unix_time_from_epoch(response.pub_timestamp) + pub_time = InfuseTime.unix_time_from_epoch(response.publish_timestamp) data_bytes = bytes(response.data) print(f"\t Publish time: {InfuseTime.utc_time_string(pub_time)}") - print(f"\t Publish count: {response.pub_count}") - print(f"\tPublish period: {response.pub_period_ms} ms") + print(f"\t Publish count: {response.publish_count}") + print(f"\tPublish period: {response.publish_period_avg_ms} ms") try: if self._channel.data is None: print(f"\t Data: {data_bytes.hex()}") From b5df7b6a2b530545d8092ca12469ae5f0b380d36 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 2 Feb 2026 15:49:56 +1000 Subject: [PATCH 3/4] tools: tdf_list: refactor implementation Refactor the tool to multiple functions. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/tdf_list.py | 107 +++++++++++++++++-------------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/src/infuse_iot/tools/tdf_list.py b/src/infuse_iot/tools/tdf_list.py index fa6ba41..47ca33f 100644 --- a/src/infuse_iot/tools/tdf_list.py +++ b/src/infuse_iot/tools/tdf_list.py @@ -11,7 +11,7 @@ from infuse_iot.commands import InfuseCommand from infuse_iot.common import InfuseType -from infuse_iot.generated.tdf_base import TdfStructBase +from infuse_iot.generated.tdf_base import TdfReadingBase, TdfStructBase from infuse_iot.socket_comms import ( ClientNotificationEpacketReceived, LocalClient, @@ -30,6 +30,63 @@ def __init__(self, _): self._client = LocalClient(default_multicast_address(), 1.0) self._decoder = TDF() + def append_tdf( + self, + table: list[tuple[str | None, str | None, str, str, str]], + name: str | None, + time: str | None, + tdf: TdfReadingBase, + ): + for field in tdf.iter_fields(): + if isinstance(field.val, list): + # Trailing VLA handling + if len(field.val) > 0 and isinstance(field.val[0], TdfStructBase): + for idx, val in enumerate(field.val): + for subfield in val.iter_fields(f"{field.name}[{idx}]"): + table.append( + ( + time, + name, + subfield.name, + subfield.val_fmt(), + subfield.postfix, + ) + ) + name = None + time = None + else: + table.append((time, name, f"{field.name}", field.val_fmt(), field.postfix)) + name = None + time = None + else: + # Standard structs and sub-structs + table.append((time, name, field.name, field.val_fmt(), field.postfix)) + name = None + time = None + + def append_readings(self, table: list[tuple[str | None, str | None, str, str, str]], tdf: TDF.Reading): + t = tdf.data[-1] + num = len(tdf.data) + tdf_name: None | str = None + time_str: None | str = None + if num > 1: + tdf_name = f"{t.NAME}[{num - 1}]" + else: + tdf_name = t.NAME + if tdf.time is not None: + if tdf.period is None: + time_str = InfuseTime.utc_time_string(tdf.time) + else: + offset = (len(tdf.data) - 1) * tdf.period + time_str = InfuseTime.utc_time_string(tdf.time + offset) + else: + if tdf.base_idx is not None: + time_str = f"IDX {tdf.base_idx}" + else: + time_str = InfuseTime.utc_time_string(time.time()) + + self.append_tdf(table, tdf_name, time_str, t) + def run(self) -> None: while True: msg = self._client.receive() @@ -43,53 +100,9 @@ def run(self) -> None: table: list[tuple[str | None, str | None, str, str, str]] = [] + tdf: TDF.Reading for tdf in self._decoder.decode(msg.epacket.payload): - t = tdf.data[-1] - num = len(tdf.data) - tdf_name: None | str = None - time_str: None | str = None - if num > 1: - tdf_name = f"{t.NAME}[{num - 1}]" - else: - tdf_name = t.NAME - if tdf.time is not None: - if tdf.period is None: - time_str = InfuseTime.utc_time_string(tdf.time) - else: - offset = (len(tdf.data) - 1) * tdf.period - time_str = InfuseTime.utc_time_string(tdf.time + offset) - else: - if tdf.base_idx is not None: - time_str = f"IDX {tdf.base_idx}" - else: - time_str = InfuseTime.utc_time_string(time.time()) - - for field in t.iter_fields(): - if isinstance(field.val, list): - # Trailing VLA handling - if len(field.val) > 0 and isinstance(field.val[0], TdfStructBase): - for idx, val in enumerate(field.val): - for subfield in val.iter_fields(f"{field.name}[{idx}]"): - table.append( - ( - time_str, - tdf_name, - subfield.name, - subfield.val_fmt(), - subfield.postfix, - ) - ) - tdf_name = None - time_str = None - else: - table.append((time_str, tdf_name, f"{field.name}", field.val_fmt(), field.postfix)) - tdf_name = None - time_str = None - else: - # Standard structs and sub-structs - table.append((time_str, tdf_name, field.name, field.val_fmt(), field.postfix)) - tdf_name = None - time_str = None + self.append_readings(table, tdf) print(f"Infuse ID: {source.infuse_id:016x}") print(f"Interface: {source.interface.name}") From 857bcb9c734becbad5ba5c5b06767a46cbc7d937 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 2 Feb 2026 16:00:06 +1000 Subject: [PATCH 4/4] tools: tdf_list: `--array-all` option Add the option to display every TDF in an array, instead of just the last value. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/tdf_list.py | 45 +++++++++++++++++++------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/infuse_iot/tools/tdf_list.py b/src/infuse_iot/tools/tdf_list.py index 47ca33f..bf1ff79 100644 --- a/src/infuse_iot/tools/tdf_list.py +++ b/src/infuse_iot/tools/tdf_list.py @@ -26,9 +26,14 @@ class SubCommand(InfuseCommand): HELP = "Display received TDFs in a list" DESCRIPTION = "Display received TDFs in a list" - def __init__(self, _): + @classmethod + def add_parser(cls, parser): + parser.add_argument("--array-all", action="store_true", help="Display all array values, not just the last") + + def __init__(self, args): self._client = LocalClient(default_multicast_address(), 1.0) self._decoder = TDF() + self._array_all = args.array_all def append_tdf( self, @@ -65,27 +70,31 @@ def append_tdf( time = None def append_readings(self, table: list[tuple[str | None, str | None, str, str, str]], tdf: TDF.Reading): - t = tdf.data[-1] num = len(tdf.data) - tdf_name: None | str = None - time_str: None | str = None - if num > 1: - tdf_name = f"{t.NAME}[{num - 1}]" - else: - tdf_name = t.NAME - if tdf.time is not None: - if tdf.period is None: - time_str = InfuseTime.utc_time_string(tdf.time) + iter_start = 0 if self._array_all else -1 + + for idx, t in enumerate(tdf.data[iter_start:]): + tdf_name: None | str = None + time_str: None | str = None + tdf_offset = idx if self._array_all else num - 1 + + if num > 1: + tdf_name = f"{t.NAME}[{tdf_offset}]" else: - offset = (len(tdf.data) - 1) * tdf.period - time_str = InfuseTime.utc_time_string(tdf.time + offset) - else: - if tdf.base_idx is not None: - time_str = f"IDX {tdf.base_idx}" + tdf_name = t.NAME + if tdf.time is not None: + if tdf.period is None: + time_str = InfuseTime.utc_time_string(tdf.time) + else: + time_offset = tdf_offset * tdf.period + time_str = InfuseTime.utc_time_string(tdf.time + time_offset) else: - time_str = InfuseTime.utc_time_string(time.time()) + if tdf.base_idx is not None: + time_str = f"IDX {tdf.base_idx}" + else: + time_str = InfuseTime.utc_time_string(time.time()) - self.append_tdf(table, tdf_name, time_str, t) + self.append_tdf(table, tdf_name, time_str, t) def run(self) -> None: while True: