feat: add discrete-input support and RPU config-writable check
This commit is contained in:
@@ -94,6 +94,14 @@ class ModbusDevice:
|
|||||||
val = self.read_input_register(address)
|
val = self.read_input_register(address)
|
||||||
return val - 65536 if val > 32767 else val
|
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 read_input_register_float(self, address):
|
||||||
def _do():
|
def _do():
|
||||||
res = self.client.read_input_registers(address, count=2, device_id=self.slave_id)
|
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")
|
raise ModbusWriteError(f"{self.device_name}: write coil {address}={value} not verified")
|
||||||
self._retry(_do)
|
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
|
value = int(value) & 0xFFFF # two's-complement wrap so negative (signed) values fit the uint16 wire format
|
||||||
|
|
||||||
def _do():
|
def _do():
|
||||||
res = self.client.write_register(address, value, device_id=self.slave_id)
|
res = self.client.write_register(address, value, device_id=self.slave_id)
|
||||||
if res.isError():
|
if res.isError():
|
||||||
raise ModbusWriteError(f"{self.device_name}: failed to write holding {address}")
|
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")
|
raise ModbusWriteError(f"{self.device_name}: write holding {address}={value} not verified")
|
||||||
self._retry(_do)
|
self._retry(_do)
|
||||||
|
|
||||||
@@ -155,6 +163,8 @@ class ModbusDevice:
|
|||||||
reg = self._register(label)
|
reg = self._register(label)
|
||||||
if reg.object_type == "coil":
|
if reg.object_type == "coil":
|
||||||
return self.read_coil(reg.address)
|
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.object_type == "input":
|
||||||
if reg.data_type == "float":
|
if reg.data_type == "float":
|
||||||
return self.read_input_register_float(reg.address)
|
return self.read_input_register_float(reg.address)
|
||||||
@@ -169,16 +179,16 @@ class ModbusDevice:
|
|||||||
return self.read_holding_register(reg.address)
|
return self.read_holding_register(reg.address)
|
||||||
raise ModbusDeviceError(f"{self.device_name}: unsupported object_type {reg.object_type!r} for {label!r}")
|
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)
|
reg = self._register(label)
|
||||||
if reg.object_type == "coil":
|
if reg.object_type == "coil":
|
||||||
self.write_coil(reg.address, value)
|
self.write_coil(reg.address, value, verify=verify)
|
||||||
return
|
return
|
||||||
if reg.object_type == "holding":
|
if reg.object_type == "holding":
|
||||||
if reg.data_type == "float":
|
if reg.data_type == "float":
|
||||||
self.write_holding_register_float(reg.address, value)
|
self.write_holding_register_float(reg.address, value)
|
||||||
else:
|
else:
|
||||||
self.write_holding_register(reg.address, value)
|
self.write_holding_register(reg.address, value, verify=verify)
|
||||||
return
|
return
|
||||||
raise ModbusDeviceError(f"{self.device_name}: cannot write to object_type {reg.object_type!r} for {label!r}")
|
raise ModbusDeviceError(f"{self.device_name}: cannot write to object_type {reg.object_type!r} for {label!r}")
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -1,10 +1,16 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from common.config_loader import load_garuda_registers
|
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"]
|
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):
|
class Rpu(ModbusDevice):
|
||||||
"""Covers every register in Sec_normal + sec_flash by label via read()/write()
|
"""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):
|
def read_shadow_error_code(self):
|
||||||
return (self.read("Shadow_error_code_hi") << 16) | self.read("Shadow_error_code_lo")
|
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):
|
def rewrite_config(self):
|
||||||
self.write_holding_register(self._addr("PASSWORD_LO"), self.REWRITE_PASSWORD_LO)
|
self.write_holding_register(self._addr("PASSWORD_LO"), self.REWRITE_PASSWORD_LO)
|
||||||
self.write_holding_register(self._addr("PASSWORD_HI"), self.REWRITE_PASSWORD_HI)
|
self.write_holding_register(self._addr("PASSWORD_HI"), self.REWRITE_PASSWORD_HI)
|
||||||
|
|||||||
Reference in New Issue
Block a user