ccs: abstraction for data storage

This commit is contained in:
Alex Janka 2025-01-10 12:03:38 +11:00
parent 88144693a8
commit ed82c3444e
4 changed files with 78 additions and 27 deletions

View file

@ -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(())
}

View file

@ -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());

View file

@ -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
}
}

View file

@ -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()))
}