diff --git a/charge-controller-supervisor/src/controller/tristar.rs b/charge-controller-supervisor/src/controller/tristar.rs index 685fd8d..d957aae 100644 --- a/charge-controller-supervisor/src/controller/tristar.rs +++ b/charge-controller-supervisor/src/controller/tristar.rs @@ -55,19 +55,38 @@ pub struct Tristar { consecutive_errors: usize, scaling: Scaling, settings_last_read: Option, + transport_settings: crate::config::Transport, } -struct ModbusTimeout(tokio_modbus::client::Context); +struct ModbusTimeout { + context: tokio_modbus::client::Context, + reconnect_required: bool, +} const MODBUS_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3); impl ModbusTimeout { + const fn new(context: tokio_modbus::client::Context) -> Self { + Self { + context, + reconnect_required: false, + } + } + pub async fn write_single_register( &mut self, addr: tokio_modbus::Address, word: u16, ) -> eyre::Result<()> { - tokio::time::timeout(MODBUS_TIMEOUT, self.0.write_single_register(addr, word)).await???; + let r = tokio::time::timeout( + MODBUS_TIMEOUT, + self.context.write_single_register(addr, word), + ) + .await?; + if let Err(tokio_modbus::Error::Transport(_)) = &r { + self.reconnect_required = true; + } + r??; Ok(()) } @@ -76,9 +95,15 @@ impl ModbusTimeout { addr: tokio_modbus::Address, cnt: tokio_modbus::Quantity, ) -> eyre::Result> { - let v = tokio::time::timeout(MODBUS_TIMEOUT, self.0.read_holding_registers(addr, cnt)) - .await???; - Ok(v) + let r = tokio::time::timeout( + MODBUS_TIMEOUT, + self.context.read_holding_registers(addr, cnt), + ) + .await?; + if let Err(tokio_modbus::Error::Transport(_)) = &r { + self.reconnect_required = true; + } + Ok(r??) } } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] @@ -561,27 +586,30 @@ impl ChargeStateGauges { } } +async fn connect_modbus(transport: &crate::config::Transport) -> eyre::Result { + let slave = tokio_modbus::Slave(DEVICE_ID); + + let modbus = match transport { + crate::config::Transport::Serial { port, baud_rate } => { + let modbus_serial = + tokio_serial::SerialStream::open(&tokio_serial::new(port, *baud_rate))?; + tokio_modbus::client::rtu::attach_slave(modbus_serial, slave) + } + crate::config::Transport::Tcp { ip, port } => { + let modbus_tcp = tokio::net::TcpStream::connect((*ip, *port)).await?; + tokio_modbus::client::tcp::attach_slave(modbus_tcp, slave) + } + }; + + Ok(ModbusTimeout::new(modbus)) +} + impl Tristar { pub async fn new( friendly_name: &str, transport: &crate::config::Transport, ) -> eyre::Result { - let slave = tokio_modbus::Slave(DEVICE_ID); - - let modbus = match transport { - crate::config::Transport::Serial { port, baud_rate } => { - let modbus_serial = - tokio_serial::SerialStream::open(&tokio_serial::new(port, *baud_rate))?; - tokio_modbus::client::rtu::attach_slave(modbus_serial, slave) - } - crate::config::Transport::Tcp { ip, port } => { - let modbus_tcp = tokio::net::TcpStream::connect((*ip, *port)).await?; - tokio_modbus::client::tcp::attach_slave(modbus_tcp, slave) - } - }; - - let mut modbus = ModbusTimeout(modbus); - + let mut modbus = connect_modbus(transport).await?; let scaling = { let data = modbus.read_holding_registers(0x0000, 4).await?; Scaling::from(&data) @@ -596,10 +624,14 @@ impl Tristar { consecutive_errors: 0, scaling, settings_last_read: None, + transport_settings: transport.clone(), }) } pub async fn refresh(&mut self) -> eyre::Result { + if self.modbus.reconnect_required { + self.modbus = connect_modbus(&self.transport_settings).await?; + } let new_state = self.get_data().await?; self.scaling = new_state.scaling;