pub struct Controller { name: String, interval: std::time::Duration, inner: ControllerInner, data: std::sync::Arc>, voltage_rx: Option>, voltage_tx: Option, } #[derive(Default, serde::Serialize, Clone)] pub struct CommonData { pub battery_voltage: f64, pub target_voltage: f64, pub battery_temp: f64, } #[derive(Clone, Copy, Debug)] pub enum VoltageCommand { Set(f64), } impl Controller { pub fn new( config: crate::config::ChargeControllerConfig, ) -> eyre::Result<( Self, Option>, )> { let inner = match config.variant { crate::config::ChargeControllerVariant::Tristar => ControllerInner::Tristar( crate::tristar::Tristar::new(&config.serial_port, &config.name, config.baud_rate)?, ), crate::config::ChargeControllerVariant::Pl { timeout_milliseconds, } => ControllerInner::Pl(crate::pl::Pli::new( &config.serial_port, &config.name, config.baud_rate, timeout_milliseconds, )?), }; let data = CommonData::default(); let data = std::sync::Arc::new(tokio::sync::RwLock::new(data)); let (voltage_tx, voltage_rx) = if config.follow_primary { let (a, b) = tokio::sync::mpsc::unbounded_channel(); (Some(a), Some(b)) } else { (None, None) }; Ok(( Self { name: config.name, interval: std::time::Duration::from_secs(config.watch_interval_seconds), inner, data, voltage_rx, voltage_tx: None, }, voltage_tx, )) } pub fn get_data_ptr(&self) -> std::sync::Arc> { self.data.clone() } pub async fn refresh(&mut self) -> eyre::Result<()> { let data = self.inner.refresh().await?; if let Some(tx) = self.voltage_tx.as_mut() { if crate::config::access_config() .await .enable_secondary_control { log::debug!( "tristar {}: primary: sending target voltage {}", self.name, data.target_voltage ); tx.send_to_all(VoltageCommand::Set(data.target_voltage)); } } *self.data.write().await = data; Ok(()) } pub fn timeout_interval(&self) -> std::time::Duration { self.interval } pub fn name(&self) -> &str { &self.name } pub fn set_tx_to_secondary(&mut self, tx: MultiTx) { assert!( self.voltage_rx.is_none(), "trying to set {} as primary when it is also a secondary!", self.name ); self.voltage_tx = Some(tx); } pub fn get_rx(&mut self) -> Option<&mut tokio::sync::mpsc::UnboundedReceiver> { self.voltage_rx.as_mut() } pub async fn process_command(&mut self, command: VoltageCommand) -> eyre::Result<()> { match command { VoltageCommand::Set(target_voltage) => { self.inner.set_target_voltage(target_voltage).await } } } } #[derive(Clone)] pub struct MultiTx(pub Vec>); impl MultiTx { pub fn send_to_all(&self, command: VoltageCommand) { for sender in &self.0 { if let Err(e) = sender.send(command) { log::error!("failed to send command {command:?}: {e:?}"); } } } } pub enum ControllerInner { Pl(crate::pl::Pli), Tristar(crate::tristar::Tristar), } impl ControllerInner { pub async fn refresh(&mut self) -> eyre::Result { match self { ControllerInner::Pl(pli) => { let pl_data = pli.refresh().await?; Ok(pl_data) } ControllerInner::Tristar(tristar) => { let tristar_data = tristar.refresh().await?; Ok(tristar_data) } } } pub async fn set_target_voltage(&mut self, target_voltage: f64) -> eyre::Result<()> { match self { ControllerInner::Pl(_) => todo!(), ControllerInner::Tristar(tristar) => tristar.set_target_voltage(target_voltage).await, } } }