From 162b3a612b81095f9386c49649e0ad3c49431e07 Mon Sep 17 00:00:00 2001 From: vanminhpe Date: Fri, 7 Aug 2026 14:19:16 +0800 Subject: [PATCH] feat: add discrete-input support and RPU config-writable check --- drivers/modbus_device.py | 20 +++++++++++++++----- drivers/rpu.py | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/modbus_device.py b/drivers/modbus_device.py index 67df95c..26af425 100644 --- a/drivers/modbus_device.py +++ b/drivers/modbus_device.py @@ -94,6 +94,14 @@ class ModbusDevice: val = self.read_input_register(address) return val - 65536 if val > 32767 else val + def read_discrete_input(self, address): + def _do(): + res = self.client.read_discrete_inputs(address, count=1, device_id=self.slave_id) + if res.isError(): + raise ModbusReadError(f"{self.device_name}: failed to read discrete input {address}") + return res.bits[0] + return self._retry(_do) + def read_input_register_float(self, address): def _do(): res = self.client.read_input_registers(address, count=2, device_id=self.slave_id) @@ -115,14 +123,14 @@ class ModbusDevice: raise ModbusWriteError(f"{self.device_name}: write coil {address}={value} not verified") self._retry(_do) - def write_holding_register(self, address, value): + def write_holding_register(self, address, value, verify=True): value = int(value) & 0xFFFF # two's-complement wrap so negative (signed) values fit the uint16 wire format def _do(): res = self.client.write_register(address, value, device_id=self.slave_id) if res.isError(): raise ModbusWriteError(f"{self.device_name}: failed to write holding {address}") - if self.read_holding_register(address) != value: + if verify and self.read_holding_register(address) != value: raise ModbusWriteError(f"{self.device_name}: write holding {address}={value} not verified") self._retry(_do) @@ -155,6 +163,8 @@ class ModbusDevice: reg = self._register(label) if reg.object_type == "coil": return self.read_coil(reg.address) + if reg.object_type == "discrete": + return self.read_discrete_input(reg.address) if reg.object_type == "input": if reg.data_type == "float": return self.read_input_register_float(reg.address) @@ -169,16 +179,16 @@ class ModbusDevice: return self.read_holding_register(reg.address) raise ModbusDeviceError(f"{self.device_name}: unsupported object_type {reg.object_type!r} for {label!r}") - def write(self, label, value): + def write(self, label, value, verify=True): reg = self._register(label) if reg.object_type == "coil": - self.write_coil(reg.address, value) + self.write_coil(reg.address, value, verify=verify) return if reg.object_type == "holding": if reg.data_type == "float": self.write_holding_register_float(reg.address, value) else: - self.write_holding_register(reg.address, value) + self.write_holding_register(reg.address, value, verify=verify) return raise ModbusDeviceError(f"{self.device_name}: cannot write to object_type {reg.object_type!r} for {label!r}") diff --git a/drivers/rpu.py b/drivers/rpu.py index 0ef8f6d..a0275b2 100644 --- a/drivers/rpu.py +++ b/drivers/rpu.py @@ -1,10 +1,16 @@ import time from common.config_loader import load_garuda_registers -from drivers.modbus_device import ModbusDevice +from drivers.modbus_device import ModbusDevice, ModbusDeviceError DEFAULT_DATASETS = ["Sec_normal", "sec_flash"] +# SEC_STATE values that allow modbus_to_config()/write_all_flash() to run +# (garuda-dsp/app/app_modbus/app_modbus.c update_modbus_input()). +# SEC_RUN (charging) is NOT in this set - config writes are silently ignored +# while charging is active. +CONFIG_WRITABLE_STATES = {1, 9, 238, 240, 241} # READY, PRE_READY, ERROR, DEBUG_READY, DEBUG_FREQ_CAL + class Rpu(ModbusDevice): """Covers every register in Sec_normal + sec_flash by label via read()/write() @@ -37,6 +43,14 @@ class Rpu(ModbusDevice): def read_shadow_error_code(self): return (self.read("Shadow_error_code_hi") << 16) | self.read("Shadow_error_code_lo") + def wait_until_config_writable(self, timeout_s=10, poll_interval_s=0.5): + deadline = time.time() + timeout_s + while time.time() < deadline: + if self.read("Main_state") in CONFIG_WRITABLE_STATES: + return + time.sleep(poll_interval_s) + raise ModbusDeviceError(f"RPU did not reach a config-writable state within {timeout_s}s") + def rewrite_config(self): self.write_holding_register(self._addr("PASSWORD_LO"), self.REWRITE_PASSWORD_LO) self.write_holding_register(self._addr("PASSWORD_HI"), self.REWRITE_PASSWORD_HI)