diff --git a/charge-controller-supervisor/src/controller.rs b/charge-controller-supervisor/src/controller.rs index f24aeae..cb9d465 100644 --- a/charge-controller-supervisor/src/controller.rs +++ b/charge-controller-supervisor/src/controller.rs @@ -5,7 +5,7 @@ pub struct Controller { name: String, interval: std::time::Duration, inner: ControllerInner, - data: std::sync::Arc>, + data: std::sync::Arc>>, voltage_rx: Option>, voltage_tx: Option, } @@ -17,6 +17,29 @@ pub struct CommonData { pub battery_temp: f64, } +#[derive(serde::Serialize, Clone)] +pub enum ControllerState { + Pl(pl::PlState), + Tristar(tristar::TristarState), +} + +impl ControllerState { + pub fn common(&self) -> CommonData { + match self { + Self::Pl(pl_state) => crate::controller::CommonData { + battery_voltage: pl_state.battery_voltage, + target_voltage: pl_state.target_voltage, + battery_temp: pl_state.battery_temp, + }, + Self::Tristar(tristar_state) => crate::controller::CommonData { + battery_voltage: tristar_state.battery_voltage, + target_voltage: tristar_state.target_voltage, + battery_temp: f64::from(tristar_state.battery_temp), + }, + } + } +} + #[derive(Clone, Copy, Debug)] pub enum VoltageCommand { Set(f64), @@ -45,9 +68,7 @@ impl Controller { }, }; - let data = CommonData::default(); - - let data = std::sync::Arc::new(tokio::sync::RwLock::new(data)); + let data = std::sync::Arc::new(tokio::sync::RwLock::new(None)); let (voltage_tx, voltage_rx) = if config.follow_primary { let (a, b) = tokio::sync::mpsc::unbounded_channel(); @@ -69,7 +90,7 @@ impl Controller { )) } - pub fn get_data_ptr(&self) -> std::sync::Arc> { + pub fn get_data_ptr(&self) -> std::sync::Arc>> { self.data.clone() } @@ -81,17 +102,18 @@ impl Controller { .await .enable_secondary_control { + let target = data.common().target_voltage; log::debug!( "tristar {}: primary: sending target voltage {}", self.name, - data.target_voltage + target ); - tx.send_to_all(VoltageCommand::Set(data.target_voltage)); + tx.send_to_all(VoltageCommand::Set(target)); } } - *self.data.write().await = data; + *self.data.write().await = Some(data); Ok(()) } @@ -146,15 +168,17 @@ pub enum ControllerInner { } impl ControllerInner { - pub async fn refresh(&mut self) -> eyre::Result { + pub async fn refresh(&mut self) -> eyre::Result { match self { ControllerInner::Pl(pli) => { let pl_data = pli.refresh().await?; - Ok(pl_data) + + Ok(ControllerState::Pl(pl_data)) } ControllerInner::Tristar(tristar) => { let tristar_data = tristar.refresh().await?; - Ok(tristar_data) + + Ok(ControllerState::Tristar(tristar_data)) } } } diff --git a/charge-controller-supervisor/src/controller/pl.rs b/charge-controller-supervisor/src/controller/pl.rs index b8dd9d0..724a5c5 100644 --- a/charge-controller-supervisor/src/controller/pl.rs +++ b/charge-controller-supervisor/src/controller/pl.rs @@ -129,7 +129,7 @@ impl Pli { }) } - pub async fn refresh(&mut self) -> eyre::Result { + pub async fn refresh(&mut self) -> eyre::Result { let new_state = self.read_state().await?; BATTERY_VOLTAGE .with_label_values(&[&self.friendly_name]) @@ -152,11 +152,7 @@ impl Pli { set_regulator_gauges(new_state.regulator_state, &self.friendly_name); - Ok(crate::controller::CommonData { - battery_voltage: new_state.battery_voltage, - target_voltage: new_state.target_voltage, - battery_temp: new_state.battery_temp, - }) + Ok(new_state) } #[expect(dead_code, reason = "writing settings is not yet implemented")] diff --git a/charge-controller-supervisor/src/controller/tristar.rs b/charge-controller-supervisor/src/controller/tristar.rs index a3da0eb..a2cd697 100644 --- a/charge-controller-supervisor/src/controller/tristar.rs +++ b/charge-controller-supervisor/src/controller/tristar.rs @@ -1,4 +1,5 @@ use prometheus::core::{AtomicI64, GenericGauge}; +use serde::{Deserialize, Serialize}; use tokio_modbus::client::{Reader, Writer}; use crate::gauges::{ @@ -11,7 +12,7 @@ use crate::gauges::{ const DEVICE_ID: u8 = 0x01; const RAM_DATA_SIZE: u16 = 0x005B; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] pub struct Scaling { pub v_scale: f64, pub i_scale: f64, @@ -80,24 +81,24 @@ impl ModbusTimeout { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct TristarState { scaling: Scaling, - battery_voltage: f64, - target_voltage: f64, - input_current: f64, - battery_temp: i16, - heatsink_temp: i16, - charge_state: ChargeState, - tristar_input_voltage: f64, - tristar_charge_current: f64, - tristar_power_out: f64, - tristar_power_in: f64, - tristar_max_array_power: f64, - tristar_max_array_voltage: f64, - tristar_open_circuit_voltage: f64, - daily_charge_amp_hours: f64, - daily_charge_watt_hours: f64, + pub battery_voltage: f64, + pub target_voltage: f64, + pub input_current: f64, + pub battery_temp: i16, + pub heatsink_temp: i16, + pub charge_state: ChargeState, + pub tristar_input_voltage: f64, + pub tristar_charge_current: f64, + pub tristar_power_out: f64, + pub tristar_power_in: f64, + pub tristar_max_array_power: f64, + pub tristar_max_array_voltage: f64, + pub tristar_open_circuit_voltage: f64, + pub daily_charge_amp_hours: f64, + pub daily_charge_watt_hours: f64, } fn signed(val: u16) -> i16 { @@ -131,8 +132,8 @@ impl TristarState { } } -#[derive(Debug, Clone, Copy)] -enum ChargeState { +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum ChargeState { Start, NightCheck, Disconnect, @@ -316,7 +317,7 @@ impl Tristar { }) } - pub async fn refresh(&mut self) -> eyre::Result { + pub async fn refresh(&mut self) -> eyre::Result { let new_state = self.get_data().await?; self.scaling = new_state.scaling; @@ -366,11 +367,7 @@ impl Tristar { self.charge_state_gauges.set(new_state.charge_state); - Ok(crate::controller::CommonData { - battery_voltage: new_state.battery_voltage, - target_voltage: new_state.target_voltage, - battery_temp: f64::from(new_state.battery_temp), - }) + Ok(new_state) } pub async fn set_target_voltage(&mut self, target_voltage: f64) -> eyre::Result<()> { diff --git a/charge-controller-supervisor/src/web.rs b/charge-controller-supervisor/src/web.rs index 925045e..fd315bf 100644 --- a/charge-controller-supervisor/src/web.rs +++ b/charge-controller-supervisor/src/web.rs @@ -6,7 +6,7 @@ pub struct ServerState { primary_name: String, map: std::collections::HashMap< String, - std::sync::Arc>, + std::sync::Arc>>, >, tx_to_controllers: crate::controller::MultiTx, } @@ -16,7 +16,7 @@ impl ServerState { primary_name: &impl ToString, map: std::collections::HashMap< String, - std::sync::Arc>, + std::sync::Arc>>, >, tx_to_controllers: crate::controller::MultiTx, ) -> Self { @@ -75,10 +75,9 @@ async fn primary_interface( .get(&state.primary_name) .ok_or(ServerError::InvalidPrimaryName)? .read() - .await - .clone(); + .await; - Ok(Json(s)) + Ok(Json(s.as_ref().ok_or(ServerError::NoData)?.common())) } #[get("/interfaces/data")] @@ -88,7 +87,9 @@ async fn all_interfaces( let mut data = Vec::new(); for (k, v) in &state.map { - data.push((k.clone(), v.read().await.clone())); + if let Some(v) = v.read().await.as_ref() { + data.push((k.clone(), v.common().clone())); + } } Json(data) @@ -104,9 +105,8 @@ async fn interface( .get(name) .ok_or(ServerError::NotFound)? .read() - .await - .clone(); - Ok(Json(data)) + .await; + Ok(Json(data.as_ref().ok_or(ServerError::NoData)?.common())) } #[get("/metrics")] @@ -152,6 +152,7 @@ enum ServerError { Prometheus, NotFound, InvalidPrimaryName, + NoData, } impl From for ServerError { @@ -163,9 +164,9 @@ impl From for ServerError { impl<'a> rocket::response::Responder<'a, 'a> for ServerError { fn respond_to(self, _: &'a rocket::Request<'_>) -> rocket::response::Result<'a> { Err(match self { - Self::Prometheus => rocket::http::Status::InternalServerError, Self::NotFound => rocket::http::Status::NotFound, Self::InvalidPrimaryName => rocket::http::Status::ServiceUnavailable, + Self::NoData | Self::Prometheus => rocket::http::Status::InternalServerError, }) } }