From 5abcc6e91d7b32d4145f69051b80861aaa1b62e5 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 24 Mar 2025 10:02:14 +1000 Subject: [PATCH 1/5] treewide: update typing hints CI now uses updated versions of linting tools that introduced new errors. Fix automatically with `ruff check . --fix`. Signed-off-by: Jordan Yates --- src/infuse_iot/rpc_client.py | 2 +- src/infuse_iot/rpc_wrappers/sym_read.py | 3 +-- src/infuse_iot/tools/gateway.py | 2 +- src/infuse_iot/util/elftools.py | 11 +++++------ src/infuse_iot/util/threading.py | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/infuse_iot/rpc_client.py b/src/infuse_iot/rpc_client.py index c1c380c..ed7e2d4 100644 --- a/src/infuse_iot/rpc_client.py +++ b/src/infuse_iot/rpc_client.py @@ -2,7 +2,7 @@ import ctypes import random -from typing import Callable +from collections.abc import Callable from infuse_iot import rpc from infuse_iot.common import InfuseType diff --git a/src/infuse_iot/rpc_wrappers/sym_read.py b/src/infuse_iot/rpc_wrappers/sym_read.py index 9b56e5e..dca2d65 100644 --- a/src/infuse_iot/rpc_wrappers/sym_read.py +++ b/src/infuse_iot/rpc_wrappers/sym_read.py @@ -2,7 +2,6 @@ import binascii import sys -from typing import Optional import tabulate from elftools.dwarf.die import DIE @@ -29,7 +28,7 @@ def __init__(self, args): # Ignore context-manager warning since ELFFile requires the file to remain opened self.elf_file = open(args.elf, "rb") # noqa: SIM115 self.elf = ELFFile(self.elf_file) - self.symbol_die: Optional[DIE] + self.symbol_die: DIE | None if args.sym: symbols = elftools.symbols_from_name(self.elf, args.sym) diff --git a/src/infuse_iot/tools/gateway.py b/src/infuse_iot/tools/gateway.py index 00f7784..b1879b8 100644 --- a/src/infuse_iot/tools/gateway.py +++ b/src/infuse_iot/tools/gateway.py @@ -13,7 +13,7 @@ import random import threading import time -from typing import Callable +from collections.abc import Callable import cryptography import cryptography.exceptions diff --git a/src/infuse_iot/util/elftools.py b/src/infuse_iot/util/elftools.py index e3c943c..45a6b83 100644 --- a/src/infuse_iot/util/elftools.py +++ b/src/infuse_iot/util/elftools.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import ctypes -from typing import Optional from elftools.dwarf.die import DIE from elftools.dwarf.dwarf_expr import DW_OP_name2opcode @@ -58,7 +57,7 @@ def symbols_from_name(elf: ELFFile, name: str) -> list[Symbol]: return symbols -def symbol_from_address(elf: ELFFile, address: int) -> Optional[Symbol]: +def symbol_from_address(elf: ELFFile, address: int) -> Symbol | None: """Get a list of symbols from an ELF file with names matching the provided string""" symtab = None @@ -104,7 +103,7 @@ def dwarf_die_from_symbol(elf: ELFFile, symbol: Symbol) -> DIE | None: return None -def dwarf_die_file_info(elf: ELFFile, die: DIE) -> tuple[Optional[str], int]: +def dwarf_die_file_info(elf: ELFFile, die: DIE) -> tuple[str | None, int]: file_attr = die.attributes["DW_AT_decl_file"] line_attr = die.attributes["DW_AT_decl_line"] @@ -123,8 +122,8 @@ def __init__( self, name: str, tag: str, - ctype: Optional[type[ctypes._SimpleCData]], - children: Optional[list[Self]], + ctype: type[ctypes._SimpleCData] | None, + children: list[Self] | None, offset: int, ): self.name = name @@ -143,7 +142,7 @@ def _type_from_dwarf_info(dwarfinfo: DWARFInfo, die: DIE): def dwarf_die_variable_inf( - dwarfinfo: DWARFInfo, die: DIE, offset: int = 0, name_override: Optional[str] = None + dwarfinfo: DWARFInfo, die: DIE, offset: int = 0, name_override: str | None = None ) -> dwarf_field: type_die = _type_from_dwarf_info(dwarfinfo, die) diff --git a/src/infuse_iot/util/threading.py b/src/infuse_iot/util/threading.py index e02c04d..840a4de 100644 --- a/src/infuse_iot/util/threading.py +++ b/src/infuse_iot/util/threading.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 import threading -from typing import Callable, Optional +from collections.abc import Callable class SignaledThread(threading.Thread): """Thread that can be signaled to terminate""" - def __init__(self, fn: Callable[[], None], sig: Optional[threading.Event] = None): + def __init__(self, fn: Callable[[], None], sig: threading.Event | None = None): self._fn = fn self._sig = sig if sig is not None else threading.Event() super().__init__(target=self.run_loop) From d2a6dbee4bdc6e6cc8549be29c06c9b8a46234a8 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Mar 2025 21:40:01 +1000 Subject: [PATCH 2/5] tools: cloud: add device query command Add a cloud command to query a device by Infuse ID. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/cloud.py | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/infuse_iot/tools/cloud.py b/src/infuse_iot/tools/cloud.py index 74565a6..d234c88 100644 --- a/src/infuse_iot/tools/cloud.py +++ b/src/infuse_iot/tools/cloud.py @@ -8,17 +8,23 @@ import sys from http import HTTPStatus from json import loads +from typing import Any from tabulate import tabulate from infuse_iot.api_client import Client from infuse_iot.api_client.api.board import ( create_board, + get_board_by_id, get_boards, ) +from infuse_iot.api_client.api.device import ( + get_device_by_device_id, +) from infuse_iot.api_client.api.organisation import ( create_organisation, get_all_organisations, + get_organisation_by_id, ) from infuse_iot.api_client.models import Error, NewBoard, NewOrganisation from infuse_iot.commands import InfuseCommand @@ -147,6 +153,47 @@ def create(self, client): print(f"<{rsp.status_code}>: {c['message']}") +class Device(CloudSubCommand): + @classmethod + def add_parser(cls, parser): + parser_boards = parser.add_parser("device", help="Infuse-IoT devices") + parser_boards.set_defaults(command_class=cls) + + tool_parser = parser_boards.add_subparsers(title="commands", metavar="", required=True) + + info_parser = tool_parser.add_parser("info") + info_parser.set_defaults(command_fn=cls.info) + info_parser.add_argument("--id", type=str, help="Infuse-IoT device ID") + + def run(self): + with self.client() as client: + self.args.command_fn(self, client) + + def info(self, client): + id_int = int(self.args.id, 0) + id_str = f"{id_int:016x}" + info = get_device_by_device_id.sync(client=client, device_id=id_str) + if info is None: + sys.exit(f"No device with Infuse-IoT ID {id_str} found") + metadata: list[tuple[str, Any]] = [] + if info.metadata: + metadata = [(f"Metadata.{k}", v) for k, v in info.metadata.additional_properties.items()] + + org = get_organisation_by_id.sync(client=client, id=info.organisation_id) + board = get_board_by_id.sync(client=client, id=info.board_id) + + table: list[tuple[str, Any]] = [ + ("UUID", info.id), + ("MCU ID", info.mcu_id), + ("Organisation", f"{info.organisation_id} ({org.name if org else 'Unknown'})"), + ("Board", f"{info.board_id} ({board.name if board else 'Unknown'})"), + ("Created", info.created_at), + ("Updated", info.updated_at), + *metadata, + ] + print(tabulate(table)) + + class SubCommand(InfuseCommand): NAME = "cloud" HELP = "Infuse-IoT cloud interaction" @@ -158,6 +205,7 @@ def add_parser(cls, parser): Organisations.add_parser(subparser) Boards.add_parser(subparser) + Device.add_parser(subparser) def __init__(self, args): self.tool = args.command_class(args) From 184b39fc9894598ebbc7532fb5c8d4a1a6b791e6 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Mon, 24 Mar 2025 09:58:23 +1000 Subject: [PATCH 3/5] rpc_wrappers: reboot: cloud support Add support for queueing reboot RPCs through the cloud. Signed-off-by: Jordan Yates --- src/infuse_iot/rpc_wrappers/reboot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/infuse_iot/rpc_wrappers/reboot.py b/src/infuse_iot/rpc_wrappers/reboot.py index 50ed1cc..fdc9962 100644 --- a/src/infuse_iot/rpc_wrappers/reboot.py +++ b/src/infuse_iot/rpc_wrappers/reboot.py @@ -17,6 +17,9 @@ def __init__(self, args): def request_struct(self): return self.request(self._delay_ms) + def request_json(self): + return {"delay_ms": str(self._delay_ms)} + def handle_response(self, return_code, response): if return_code == 0: print(f"Rebooting in {response.delay_ms} ms") From 5a810f22410d2bfdef0eb0e531f7d0d073ca98f5 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 21 Mar 2025 21:39:19 +1000 Subject: [PATCH 4/5] generated: update definitions Regenerate TDF and RPC definitions. Signed-off-by: Jordan Yates --- src/infuse_iot/generated/rpc_definitions.py | 209 ++++++++++++-------- src/infuse_iot/generated/tdf_definitions.py | 48 +++++ 2 files changed, 179 insertions(+), 78 deletions(-) diff --git a/src/infuse_iot/generated/rpc_definitions.py b/src/infuse_iot/generated/rpc_definitions.py index 7d68a58..3b78489 100644 --- a/src/infuse_iot/generated/rpc_definitions.py +++ b/src/infuse_iot/generated/rpc_definitions.py @@ -6,8 +6,10 @@ import ctypes import enum +from infuse_iot.util.ctypes import VLACompatLittleEndianStruct -class rpc_struct_mcuboot_img_sem_ver(ctypes.LittleEndianStructure): + +class rpc_struct_mcuboot_img_sem_ver(VLACompatLittleEndianStruct): """MCUboot semantic versioning struct""" _fields_ = [ @@ -19,18 +21,18 @@ class rpc_struct_mcuboot_img_sem_ver(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_kv_store_value(ctypes.LittleEndianStructure): +class rpc_struct_kv_store_value(VLACompatLittleEndianStruct): """KV store data value""" _fields_ = [ ("id", ctypes.c_uint16), ("len", ctypes.c_int16), - ("data", 0 * ctypes.c_uint8), ] + vla_field = ("data", 0 * ctypes.c_uint8) _pack_ = 1 -class rpc_struct_kv_store_crc(ctypes.LittleEndianStructure): +class rpc_struct_kv_store_crc(VLACompatLittleEndianStruct): """KV store data CRC""" _fields_ = [ @@ -40,7 +42,7 @@ class rpc_struct_kv_store_crc(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_bt_addr_le(ctypes.LittleEndianStructure): +class rpc_struct_bt_addr_le(VLACompatLittleEndianStruct): """Bluetooth LE address""" _fields_ = [ @@ -50,7 +52,7 @@ class rpc_struct_bt_addr_le(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_ipv4_address(ctypes.LittleEndianStructure): +class rpc_struct_ipv4_address(VLACompatLittleEndianStruct): """IPv4 address""" _fields_ = [ @@ -59,7 +61,7 @@ class rpc_struct_ipv4_address(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_ipv6_address(ctypes.LittleEndianStructure): +class rpc_struct_ipv6_address(VLACompatLittleEndianStruct): """IPv6 address""" _fields_ = [ @@ -68,7 +70,7 @@ class rpc_struct_ipv6_address(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_network_state(ctypes.LittleEndianStructure): +class rpc_struct_network_state(VLACompatLittleEndianStruct): """Common network state""" _fields_ = [ @@ -82,7 +84,7 @@ class rpc_struct_network_state(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_wifi_state(ctypes.LittleEndianStructure): +class rpc_struct_wifi_state(VLACompatLittleEndianStruct): """WiFi interface status""" _fields_ = [ @@ -101,7 +103,7 @@ class rpc_struct_wifi_state(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_lte_state(ctypes.LittleEndianStructure): +class rpc_struct_lte_state(VLACompatLittleEndianStruct): """LTE interface status""" _fields_ = [ @@ -123,7 +125,7 @@ class rpc_struct_lte_state(ctypes.LittleEndianStructure): _pack_ = 1 -class rpc_struct_wifi_scan_result(ctypes.LittleEndianStructure): +class rpc_struct_wifi_scan_result(VLACompatLittleEndianStruct): """WiFi interface status""" _fields_ = [ @@ -133,12 +135,12 @@ class rpc_struct_wifi_scan_result(ctypes.LittleEndianStructure): ("rssi", ctypes.c_int8), ("bssid", 6 * ctypes.c_char), ("ssid_len", ctypes.c_uint8), - ("ssid", 0 * ctypes.c_char), ] + vla_field = ("ssid", 0 * ctypes.c_char) _pack_ = 1 -class rpc_struct_xyz_s16(ctypes.LittleEndianStructure): +class rpc_struct_xyz_s16(VLACompatLittleEndianStruct): """Signed 16 bit XYZ vector""" _fields_ = [ @@ -149,6 +151,16 @@ class rpc_struct_xyz_s16(ctypes.LittleEndianStructure): _pack_ = 1 +class rpc_struct_infuse_state(VLACompatLittleEndianStruct): + """Single Infuse-IoT application state""" + + _fields_ = [ + ("state", ctypes.c_uint8), + ("timeout", ctypes.c_uint16), + ] + _pack_ = 1 + + class rpc_enum_bt_le_addr_type(enum.IntEnum): """Bluetooth LE address type""" @@ -189,13 +201,13 @@ class reboot: DESCRIPTION = "Reboot the device after a delay" COMMAND_ID = 1 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("delay_ms", ctypes.c_uint32), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("delay_ms", ctypes.c_uint32), ] @@ -209,14 +221,14 @@ class fault: DESCRIPTION = "Immediately trigger an exception on the device" COMMAND_ID = 2 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("fault", ctypes.c_uint8), ("zero", ctypes.c_uint32), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 @@ -229,12 +241,12 @@ class time_get: DESCRIPTION = "Get the current time knowledge of the device" COMMAND_ID = 3 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("time_source", ctypes.c_uint8), ("epoch_time", ctypes.c_uint64), @@ -250,13 +262,13 @@ class time_set: DESCRIPTION = "Set the current time of the device" COMMAND_ID = 4 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("epoch_time", ctypes.c_uint64), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 @@ -269,17 +281,17 @@ class kv_write: DESCRIPTION = "Write values to the KV store" COMMAND_ID = 5 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("num", ctypes.c_uint8), - ("values", 0 * rpc_struct_kv_store_value), ] + vla_field = ("values", 0 * rpc_struct_kv_store_value) _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ - ("rc", 0 * ctypes.c_int16), ] + vla_field = ("rc", 0 * ctypes.c_int16) _pack_ = 1 @@ -290,17 +302,17 @@ class kv_read: DESCRIPTION = "Read values from the KV store" COMMAND_ID = 6 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("num", ctypes.c_uint8), - ("keys", 0 * ctypes.c_uint16), ] + vla_field = ("keys", 0 * ctypes.c_uint16) _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ - ("values", 0 * rpc_struct_kv_store_value), ] + vla_field = ("values", 0 * rpc_struct_kv_store_value) _pack_ = 1 @@ -311,18 +323,18 @@ class kv_reflect_crcs: DESCRIPTION = "Read KV store CRC's" COMMAND_ID = 7 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("offset", ctypes.c_uint16), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("num", ctypes.c_uint16), ("remaining", ctypes.c_uint16), - ("crcs", 0 * rpc_struct_kv_store_crc), ] + vla_field = ("crcs", 0 * rpc_struct_kv_store_crc) _pack_ = 1 @@ -333,19 +345,19 @@ class zbus_channel_state: DESCRIPTION = "Query current state of zbus channel" COMMAND_ID = 8 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("channel_id", ctypes.c_uint32), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("publish_timestamp", ctypes.c_uint64), ("publish_count", ctypes.c_uint32), ("publish_period_avg_ms", ctypes.c_uint32), - ("data", 0 * ctypes.c_uint8), ] + vla_field = ("data", 0 * ctypes.c_uint8) _pack_ = 1 @@ -356,12 +368,12 @@ class application_info: DESCRIPTION = "Query basic application versions and state" COMMAND_ID = 9 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("application_id", ctypes.c_uint32), ("version", rpc_struct_mcuboot_img_sem_ver), @@ -382,16 +394,16 @@ class wifi_scan: DESCRIPTION = "Scan for WiFi networks" COMMAND_ID = 10 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("network_count", ctypes.c_uint8), - ("networks", 0 * rpc_struct_wifi_scan_result), ] + vla_field = ("networks", 0 * rpc_struct_wifi_scan_result) _pack_ = 1 @@ -402,12 +414,12 @@ class wifi_state: DESCRIPTION = "Get current WiFi interface state" COMMAND_ID = 11 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("common", rpc_struct_network_state), ("wifi", rpc_struct_wifi_state), @@ -422,12 +434,12 @@ class last_reboot: DESCRIPTION = "Retrieve information pertaining to the previous reboot" COMMAND_ID = 12 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("reason", ctypes.c_uint8), ("epoch_time_source", ctypes.c_uint8), @@ -448,13 +460,13 @@ class data_logger_state: DESCRIPTION = "Get state of a data logger" COMMAND_ID = 13 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("logger", ctypes.c_uint8), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("bytes_logged", ctypes.c_uint64), ("logical_blocks", ctypes.c_uint32), @@ -477,7 +489,7 @@ class data_logger_read: DESCRIPTION = "Read data from data logger" COMMAND_ID = 14 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("logger", ctypes.c_uint8), ("start_block", ctypes.c_uint32), @@ -485,7 +497,7 @@ class request(ctypes.LittleEndianStructure): ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("sent_len", ctypes.c_uint32), ("sent_crc", ctypes.c_uint32), @@ -500,13 +512,13 @@ class mem_read: DESCRIPTION = "Read arbitrary memory (NO ADDRESS VALIDATION PERFORMED)" COMMAND_ID = 15 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("address", ctypes.c_uint32), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("sent_len", ctypes.c_uint32), ("sent_crc", ctypes.c_uint32), @@ -514,6 +526,47 @@ class response(ctypes.LittleEndianStructure): _pack_ = 1 +class infuse_states_query: + """Read current Infuse-IoT application states""" + + HELP = "Read current Infuse-IoT application states" + DESCRIPTION = "Read current Infuse-IoT application states" + COMMAND_ID = 16 + + class request(VLACompatLittleEndianStruct): + _fields_ = [ + ("offset", ctypes.c_uint8), + ] + _pack_ = 1 + + class response(VLACompatLittleEndianStruct): + _fields_ = [ + ("remaining", ctypes.c_uint8), + ] + vla_field = ("states", 0 * rpc_struct_infuse_state) + _pack_ = 1 + + +class infuse_states_update: + """Update Infuse-IoT application states""" + + HELP = "Update Infuse-IoT application states" + DESCRIPTION = "Update Infuse-IoT application states" + COMMAND_ID = 17 + + class request(VLACompatLittleEndianStruct): + _fields_ = [ + ("num", ctypes.c_uint8), + ] + vla_field = ("states", 0 * rpc_struct_infuse_state) + _pack_ = 1 + + class response(VLACompatLittleEndianStruct): + _fields_ = [ + ] + _pack_ = 1 + + class lte_at_cmd: """Run AT command against LTE modem""" @@ -521,16 +574,16 @@ class lte_at_cmd: DESCRIPTION = "Run AT command against LTE modem" COMMAND_ID = 20 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ - ("cmd", 0 * ctypes.c_char), ] + vla_field = ("cmd", 0 * ctypes.c_char) _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ - ("rsp", 0 * ctypes.c_char), ] + vla_field = ("rsp", 0 * ctypes.c_char) _pack_ = 1 @@ -541,12 +594,12 @@ class lte_state: DESCRIPTION = "Get current LTE interface state" COMMAND_ID = 21 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("common", rpc_struct_network_state), ("lte", rpc_struct_lte_state), @@ -561,7 +614,7 @@ class coap_download: DESCRIPTION = "Download a file from a COAP server (Infuse-IoT DTLS protected)" COMMAND_ID = 30 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("server_address", 48 * ctypes.c_char), ("server_port", ctypes.c_uint16), @@ -569,11 +622,11 @@ class request(ctypes.LittleEndianStructure): ("action", ctypes.c_uint8), ("resource_len", ctypes.c_uint32), ("resource_crc", ctypes.c_uint32), - ("resource", 0 * ctypes.c_char), ] + vla_field = ("resource", 0 * ctypes.c_char) _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("resource_len", ctypes.c_uint32), ("resource_crc", ctypes.c_uint32), @@ -588,14 +641,14 @@ class file_write_basic: DESCRIPTION = "Write a file to the device" COMMAND_ID = 40 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("action", ctypes.c_uint8), ("file_crc", ctypes.c_uint32), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("recv_len", ctypes.c_uint32), ("recv_crc", ctypes.c_uint32), @@ -610,7 +663,7 @@ class bt_connect_infuse: DESCRIPTION = "Connect to an Infuse-IoT Bluetooth device" COMMAND_ID = 50 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("peer", rpc_struct_bt_addr_le), ("conn_timeout_ms", ctypes.c_uint16), @@ -619,7 +672,7 @@ class request(ctypes.LittleEndianStructure): ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("peer", rpc_struct_bt_addr_le), ("cloud_public_key", 32 * ctypes.c_uint8), @@ -636,13 +689,13 @@ class bt_disconnect: DESCRIPTION = "Disconnect from a Bluetooth device" COMMAND_ID = 51 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("peer", rpc_struct_bt_addr_le), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 @@ -655,13 +708,13 @@ class gravity_reference_update: DESCRIPTION = "Store the current accelerometer vector as the gravity reference" COMMAND_ID = 60 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("max_variance", ctypes.c_uint16), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("reference", rpc_struct_xyz_s16), ("variance", rpc_struct_xyz_s16), @@ -678,20 +731,20 @@ class security_state: DESCRIPTION = "Query current security state and validate identity" COMMAND_ID = 30000 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ("challenge", 16 * ctypes.c_uint8), ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("cloud_public_key", 32 * ctypes.c_uint8), ("device_public_key", 32 * ctypes.c_uint8), ("network_id", ctypes.c_uint32), ("challenge_response_type", ctypes.c_uint8), - ("challenge_response", 0 * ctypes.c_uint8), ] + vla_field = ("challenge_response", 0 * ctypes.c_uint8) _pack_ = 1 @@ -702,12 +755,12 @@ class data_sender: DESCRIPTION = "Send multiple INFUSE_RPC_DATA packets" COMMAND_ID = 32765 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 @@ -720,12 +773,12 @@ class data_receiver: DESCRIPTION = "Receive multiple INFUSE_RPC_DATA packets" COMMAND_ID = 32766 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ ] _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ ("recv_len", ctypes.c_uint32), ("recv_crc", ctypes.c_uint32), @@ -740,15 +793,15 @@ class echo: DESCRIPTION = "Echo any input data in the response" COMMAND_ID = 32767 - class request(ctypes.LittleEndianStructure): + class request(VLACompatLittleEndianStruct): _fields_ = [ - ("array", 0 * ctypes.c_uint8), ] + vla_field = ("array", 0 * ctypes.c_uint8) _pack_ = 1 - class response(ctypes.LittleEndianStructure): + class response(VLACompatLittleEndianStruct): _fields_ = [ - ("array", 0 * ctypes.c_uint8), ] + vla_field = ("array", 0 * ctypes.c_uint8) _pack_ = 1 diff --git a/src/infuse_iot/generated/tdf_definitions.py b/src/infuse_iot/generated/tdf_definitions.py index 7cbb13d..08143e8 100644 --- a/src/infuse_iot/generated/tdf_definitions.py +++ b/src/infuse_iot/generated/tdf_definitions.py @@ -1199,6 +1199,51 @@ class infuse_bluetooth_rssi(TdfReadingBase): "rssi": "{}", } + class adc_raw_8(TdfReadingBase): + """Generic 8bit raw ADC reading""" + + name = "ADC_RAW_8" + _fields_ = [ + ("val", ctypes.c_int8), + ] + _pack_ = 1 + _postfix_ = { + "val": "", + } + _display_fmt_ = { + "val": "{}", + } + + class adc_raw_16(TdfReadingBase): + """Generic 16bit raw ADC reading""" + + name = "ADC_RAW_16" + _fields_ = [ + ("val", ctypes.c_int16), + ] + _pack_ = 1 + _postfix_ = { + "val": "", + } + _display_fmt_ = { + "val": "{}", + } + + class adc_raw_32(TdfReadingBase): + """Generic 32bit raw ADC reading""" + + name = "ADC_RAW_32" + _fields_ = [ + ("val", ctypes.c_int32), + ] + _pack_ = 1 + _postfix_ = { + "val": "", + } + _display_fmt_ = { + "val": "{}", + } + class array_type(TdfReadingBase): """Example array type""" @@ -1252,5 +1297,8 @@ class array_type(TdfReadingBase): 37: readings.nrf9x_gnss_pvt, 38: readings.battery_charge_accumulated, 39: readings.infuse_bluetooth_rssi, + 40: readings.adc_raw_8, + 41: readings.adc_raw_16, + 42: readings.adc_raw_32, 100: readings.array_type, } From 1e63b0e54b2af73cde6527260d9788e489195dad Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sat, 22 Mar 2025 14:20:00 +1000 Subject: [PATCH 5/5] rpc_wrappers: infuse_states_*: added Add wrappers for working with current application states. Signed-off-by: Jordan Yates --- .../rpc_wrappers/infuse_states_query.py | 32 ++++++++++++++ .../rpc_wrappers/infuse_states_update.py | 42 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/infuse_iot/rpc_wrappers/infuse_states_query.py create mode 100644 src/infuse_iot/rpc_wrappers/infuse_states_update.py diff --git a/src/infuse_iot/rpc_wrappers/infuse_states_query.py b/src/infuse_iot/rpc_wrappers/infuse_states_query.py new file mode 100644 index 0000000..d9aedb6 --- /dev/null +++ b/src/infuse_iot/rpc_wrappers/infuse_states_query.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +import tabulate + +import infuse_iot.generated.rpc_definitions as defs +from infuse_iot.commands import InfuseRpcCommand +from infuse_iot.zephyr.errno import errno + + +class infuse_states_query(InfuseRpcCommand, defs.infuse_states_query): + @classmethod + def add_parser(cls, parser): + parser.add_argument("--offset", type=int, default=0) + + def __init__(self, args): + self.args = args + + def request_struct(self): + return self.request(self.args.offset) + + def request_json(self): + return {} + + def handle_response(self, return_code, response): + if return_code != 0: + print(f"Failed to query states ({errno.strerror(-return_code)})") + return + + states = [] + for state in response.states: + states.append([state.state, "Permanent" if state.timeout == 0 else f"{state.timeout} seconds"]) + print(tabulate.tabulate(states, headers=["State", "Duration"])) diff --git a/src/infuse_iot/rpc_wrappers/infuse_states_update.py b/src/infuse_iot/rpc_wrappers/infuse_states_update.py new file mode 100644 index 0000000..2562735 --- /dev/null +++ b/src/infuse_iot/rpc_wrappers/infuse_states_update.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import ctypes + +import infuse_iot.generated.rpc_definitions as defs +from infuse_iot.commands import InfuseRpcCommand +from infuse_iot.util.ctypes import UINT16_MAX +from infuse_iot.zephyr.errno import errno + + +class infuse_states_update(InfuseRpcCommand, defs.infuse_states_update): + @classmethod + def add_parser(cls, parser): + parser.add_argument("--state", "-s", type=int, required=True, help="State ID to update") + option = parser.add_mutually_exclusive_group(required=True) + option.add_argument("--set", action="store_true", help="Enable the state permanently") + option.add_argument("--clear", action="store_true", help="Disable the state") + option.add_argument("--timeout", type=int, help="Enable the state for a duration (seconds)") + + def __init__(self, args): + self.args = args + + def request_struct(self): + class request(ctypes.LittleEndianStructure): + _fields_ = [ + ("num", ctypes.c_uint8), + ("state", defs.rpc_struct_infuse_state), + ] + + timeout = 0 if self.args.set else (UINT16_MAX if self.args.clear else self.args.timeout) + state = defs.rpc_struct_infuse_state(self.args.state, timeout) + + return request(1, state) + + def request_json(self): + return {} + + def handle_response(self, return_code, response): + if return_code != 0: + print(f"Failed to update state {self.args.state} ({errno.strerror(-return_code)})") + else: + print(f"Updated state {self.args.state}")