ccs: tristar: proper timeout on serial
tokio_serial::SerialStream ignored timeouts??? and now this will actually prevent charge controllers being added if they don't respond via modbus
This commit is contained in:
parent
47e711f111
commit
8bc7c8e17c
3 changed files with 49 additions and 18 deletions
|
@ -20,7 +20,7 @@ pub enum VoltageCommand {
|
|||
}
|
||||
|
||||
impl Controller {
|
||||
pub fn new(
|
||||
pub async fn new(
|
||||
config: crate::config::ChargeControllerConfig,
|
||||
) -> eyre::Result<(
|
||||
Self,
|
||||
|
@ -28,7 +28,8 @@ impl Controller {
|
|||
)> {
|
||||
let inner = match config.variant {
|
||||
crate::config::ChargeControllerVariant::Tristar => ControllerInner::Tristar(
|
||||
crate::tristar::Tristar::new(&config.serial_port, &config.name, config.baud_rate)?,
|
||||
crate::tristar::Tristar::new(&config.serial_port, &config.name, config.baud_rate)
|
||||
.await?,
|
||||
),
|
||||
crate::config::ChargeControllerVariant::Pl {
|
||||
timeout_milliseconds,
|
||||
|
|
|
@ -87,7 +87,7 @@ async fn watch(args: Args) -> eyre::Result<()> {
|
|||
|
||||
for config in &config.charge_controllers {
|
||||
let n = config.name.clone();
|
||||
match controller::Controller::new(config.clone()) {
|
||||
match controller::Controller::new(config.clone()).await {
|
||||
Ok((v, voltage_tx)) => {
|
||||
map.insert(n, v.get_data_ptr());
|
||||
controllers.push(v);
|
||||
|
|
|
@ -49,15 +49,40 @@ impl Scaling {
|
|||
|
||||
pub struct Tristar {
|
||||
friendly_name: String,
|
||||
modbus: tokio_modbus::client::Context,
|
||||
modbus: ModbusTimeout,
|
||||
charge_state_gauges: ChargeStateGauges,
|
||||
consecutive_errors: usize,
|
||||
scaling: Option<Scaling>,
|
||||
scaling: Scaling,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
struct ModbusTimeout(tokio_modbus::client::Context);
|
||||
|
||||
const MODBUS_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3);
|
||||
|
||||
impl ModbusTimeout {
|
||||
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???;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_holding_registers(
|
||||
&mut self,
|
||||
addr: tokio_modbus::Address,
|
||||
cnt: tokio_modbus::Quantity,
|
||||
) -> eyre::Result<Vec<u16>> {
|
||||
let v = tokio::time::timeout(MODBUS_TIMEOUT, self.0.read_holding_registers(addr, cnt))
|
||||
.await???;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TristarState {
|
||||
scaling: Option<Scaling>,
|
||||
scaling: Scaling,
|
||||
battery_voltage: f64,
|
||||
target_voltage: f64,
|
||||
input_current: f64,
|
||||
|
@ -86,7 +111,7 @@ impl TristarState {
|
|||
fn from_ram(ram: &[u16]) -> Self {
|
||||
let scaling = Scaling::from(ram);
|
||||
Self {
|
||||
scaling: Some(scaling),
|
||||
scaling,
|
||||
battery_voltage: scaling.get_voltage(ram[TristarRamAddress::AdcVbFMed]),
|
||||
target_voltage: scaling.get_voltage(ram[TristarRamAddress::VbRef]),
|
||||
input_current: scaling.get_current(ram[TristarRamAddress::AdcIaFShadow]),
|
||||
|
@ -255,20 +280,28 @@ impl ChargeStateGauges {
|
|||
}
|
||||
|
||||
impl Tristar {
|
||||
pub fn new(serial_port: &str, friendly_name: &str, baud_rate: u32) -> eyre::Result<Self> {
|
||||
pub async fn new(serial_port: &str, friendly_name: &str, baud_rate: u32) -> eyre::Result<Self> {
|
||||
let modbus_serial = tokio_serial::SerialStream::open(
|
||||
&tokio_serial::new(serial_port, baud_rate).timeout(std::time::Duration::from_secs(3)),
|
||||
)?;
|
||||
|
||||
let slave = tokio_modbus::Slave(DEVICE_ID);
|
||||
let modbus = tokio_modbus::client::rtu::attach_slave(modbus_serial, slave);
|
||||
let mut modbus = ModbusTimeout(modbus);
|
||||
|
||||
let scaling = {
|
||||
let data = modbus.read_holding_registers(0x0000, 4).await?;
|
||||
Scaling::from(&data)
|
||||
};
|
||||
|
||||
let charge_state_gauges = ChargeStateGauges::new(friendly_name);
|
||||
|
||||
Ok(Self {
|
||||
friendly_name: friendly_name.to_owned(),
|
||||
modbus,
|
||||
charge_state_gauges,
|
||||
consecutive_errors: 0,
|
||||
scaling: None,
|
||||
scaling,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -330,10 +363,10 @@ impl Tristar {
|
|||
}
|
||||
|
||||
pub async fn set_target_voltage(&mut self, target_voltage: f64) -> eyre::Result<()> {
|
||||
let scaled_voltage: u16 = self.scale_voltage(target_voltage)?;
|
||||
let scaled_voltage: u16 = self.scale_voltage(target_voltage);
|
||||
self.modbus
|
||||
.write_single_register(TristarRamAddress::VbRefSlave as u16, scaled_voltage)
|
||||
.await??;
|
||||
.await?;
|
||||
|
||||
log::debug!(
|
||||
"tristar {} being set to voltage {target_voltage} (scaled: {scaled_voltage:#X?})",
|
||||
|
@ -343,18 +376,15 @@ impl Tristar {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn scale_voltage(&self, voltage: f64) -> eyre::Result<u16> {
|
||||
let Some(scaling) = &self.scaling else {
|
||||
return Err(eyre::eyre!("no scaling data present"));
|
||||
};
|
||||
Ok(scaling.inverse_voltage(voltage))
|
||||
fn scale_voltage(&self, voltage: f64) -> u16 {
|
||||
self.scaling.inverse_voltage(voltage)
|
||||
}
|
||||
|
||||
async fn get_data(&mut self) -> eyre::Result<TristarState> {
|
||||
let data = self
|
||||
.modbus
|
||||
.read_holding_registers(0x0000, RAM_DATA_SIZE + 1)
|
||||
.await??;
|
||||
.await?;
|
||||
Ok(TristarState::from_ram(&data))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue