diff --git a/charge-controller-supervisor/src/controller.rs b/charge-controller-supervisor/src/controller.rs index 106c84a..345fa90 100644 --- a/charge-controller-supervisor/src/controller.rs +++ b/charge-controller-supervisor/src/controller.rs @@ -1,3 +1,5 @@ +use crate::storage::ControllerData; + mod pl; mod tristar; @@ -5,7 +7,7 @@ pub struct Controller { name: String, interval: std::time::Duration, inner: ControllerInner, - data: std::sync::Arc<tokio::sync::RwLock<Option<ControllerState>>>, + data: std::sync::Arc<ControllerData>, voltage_rx: Option<tokio::sync::mpsc::UnboundedReceiver<VoltageCommand>>, voltage_tx: Option<MultiTx>, } @@ -69,7 +71,7 @@ impl Controller { }, }; - let data = std::sync::Arc::new(tokio::sync::RwLock::new(None)); + let data = std::sync::Arc::new(ControllerData::new()); let (voltage_tx, voltage_rx) = if config.follow_primary { let (a, b) = tokio::sync::mpsc::unbounded_channel(); @@ -91,7 +93,7 @@ impl Controller { )) } - pub fn get_data_ptr(&self) -> std::sync::Arc<tokio::sync::RwLock<Option<ControllerState>>> { + pub fn get_data_ptr(&self) -> std::sync::Arc<ControllerData> { self.data.clone() } @@ -114,7 +116,7 @@ impl Controller { } } - *self.data.write().await = Some(data); + *self.data.write_state().await = Some(data); Ok(()) } diff --git a/charge-controller-supervisor/src/main.rs b/charge-controller-supervisor/src/main.rs index da2e361..4b894e6 100644 --- a/charge-controller-supervisor/src/main.rs +++ b/charge-controller-supervisor/src/main.rs @@ -26,6 +26,8 @@ enum Commands { mod controller; mod gauges; +mod storage; + mod web; pub const CHARGE_CONTROLLER_LABEL: &str = "charge_controller"; @@ -92,7 +94,7 @@ async fn run() -> eyre::Result<()> { async fn watch(args: Args) -> eyre::Result<()> { let _w = config::init_config(&args.config); - let (map, follow_voltage_tx, mut controller_tasks) = { + let (storage, follow_voltage_tx, mut controller_tasks) = { let config = config::access_config().await; if config .charge_controllers @@ -138,12 +140,16 @@ async fn watch(args: Args) -> eyre::Result<()> { controller_tasks.push(run_loop(controller)); } - (map, follow_voltage_tx, controller_tasks) + ( + storage::AllControllers::new(map), + follow_voltage_tx, + controller_tasks, + ) }; let server = web::rocket(web::ServerState::new( &config::access_config().await.primary_charge_controller, - map, + storage, follow_voltage_tx, )); let server_task = tokio::task::spawn(server.launch()); diff --git a/charge-controller-supervisor/src/storage.rs b/charge-controller-supervisor/src/storage.rs new file mode 100644 index 0000000..48eb84a --- /dev/null +++ b/charge-controller-supervisor/src/storage.rs @@ -0,0 +1,47 @@ +pub struct AllControllers { + map: std::collections::HashMap<String, std::sync::Arc<ControllerData>>, +} + +impl AllControllers { + pub const fn new( + map: std::collections::HashMap<String, std::sync::Arc<ControllerData>>, + ) -> Self { + Self { map } + } + + pub fn controller_names(&self) -> impl Iterator<Item = &String> { + self.map.keys() + } + + pub fn get(&self, name: &str) -> Option<&std::sync::Arc<ControllerData>> { + self.map.get(name) + } + + pub fn all_data(&self) -> impl Iterator<Item = (&String, &std::sync::Arc<ControllerData>)> { + self.map.iter() + } +} + +pub struct ControllerData { + state: tokio::sync::RwLock<Option<crate::controller::ControllerState>>, +} + +impl ControllerData { + pub const fn new() -> Self { + Self { + state: tokio::sync::RwLock::const_new(None), + } + } + + pub async fn write_state( + &self, + ) -> tokio::sync::RwLockWriteGuard<Option<crate::controller::ControllerState>> { + self.state.write().await + } + + pub async fn read_state( + &self, + ) -> tokio::sync::RwLockReadGuard<Option<crate::controller::ControllerState>> { + self.state.read().await + } +} diff --git a/charge-controller-supervisor/src/web.rs b/charge-controller-supervisor/src/web.rs index 57d55f5..30add36 100644 --- a/charge-controller-supervisor/src/web.rs +++ b/charge-controller-supervisor/src/web.rs @@ -1,29 +1,25 @@ use rocket::{get, post, routes, serde::json::Json, State}; +use crate::storage::AllControllers; + mod static_handler; pub struct ServerState { primary_name: String, - map: std::collections::HashMap< - String, - std::sync::Arc<tokio::sync::RwLock<Option<crate::controller::ControllerState>>>, - >, + data: AllControllers, tx_to_controllers: crate::controller::MultiTx, } impl ServerState { pub fn new( primary_name: &impl ToString, - map: std::collections::HashMap< - String, - std::sync::Arc<tokio::sync::RwLock<Option<crate::controller::ControllerState>>>, - >, + data: AllControllers, tx_to_controllers: crate::controller::MultiTx, ) -> Self { let primary_name = primary_name.to_string(); Self { primary_name, - map, + data, tx_to_controllers, } } @@ -65,7 +61,7 @@ pub fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> { #[get("/interfaces")] fn interfaces(state: &State<ServerState>) -> Json<Vec<String>> { - Json(state.map.keys().cloned().collect()) + Json(state.data.controller_names().cloned().collect()) } #[get("/interfaces/primary")] @@ -73,10 +69,10 @@ async fn primary_interface( state: &State<ServerState>, ) -> Result<Json<crate::controller::CommonData>, ServerError> { let s = state - .map + .data .get(&state.primary_name) .ok_or(ServerError::InvalidPrimaryName)? - .read() + .read_state() .await; Ok(Json(s.as_ref().ok_or(ServerError::NoData)?.common())) @@ -88,8 +84,8 @@ async fn all_interfaces( ) -> Json<Vec<(String, crate::controller::CommonData)>> { let mut data = Vec::new(); - for (k, v) in &state.map { - let v = v.read().await; + for (k, v) in state.data.all_data() { + let v = v.read_state().await; if let Some(v) = v.as_ref() { data.push((k.clone(), v.common().clone())); } @@ -104,8 +100,8 @@ async fn all_interfaces_full( ) -> Json<Vec<(String, crate::controller::ControllerState)>> { let mut data = Vec::new(); - for (k, v) in &state.map { - let v = v.read().await; + for (k, v) in state.data.all_data() { + let v = v.read_state().await; if let Some(v) = v.as_ref() { data.push((k.clone(), v.clone())); } @@ -120,10 +116,10 @@ async fn interface( state: &State<ServerState>, ) -> Result<Json<crate::controller::CommonData>, ServerError> { let data = state - .map + .data .get(name) .ok_or(ServerError::NotFound)? - .read() + .read_state() .await; Ok(Json(data.as_ref().ok_or(ServerError::NoData)?.common())) } @@ -134,10 +130,10 @@ async fn interface_full( state: &State<ServerState>, ) -> Result<Json<crate::controller::ControllerState>, ServerError> { let data = state - .map + .data .get(name) .ok_or(ServerError::NotFound)? - .read() + .read_state() .await; Ok(Json(data.as_ref().ok_or(ServerError::NoData)?.clone())) }