diff --git a/central_backend/src/devices/devices_list.rs b/central_backend/src/devices/devices_list.rs index 0f5763e..9f196de 100644 --- a/central_backend/src/devices/devices_list.rs +++ b/central_backend/src/devices/devices_list.rs @@ -115,6 +115,11 @@ impl DevicesList { self.0.clone().into_values().collect() } + /// Get a reference on the full list of devices + pub fn full_list_ref(&self) -> Vec<&Device> { + self.0.values().collect() + } + /// Get the information about a single device pub fn get_single(&self, id: &DeviceId) -> Option { self.0.get(id).cloned() diff --git a/central_backend/src/energy/energy_actor.rs b/central_backend/src/energy/energy_actor.rs index 9d3de54..35426be 100644 --- a/central_backend/src/energy/energy_actor.rs +++ b/central_backend/src/energy/energy_actor.rs @@ -53,7 +53,7 @@ impl EnergyActor { }); self.consumption_cache.add_value(latest_consumption); - let devices_list = self.devices.full_list(); + let devices_list = self.devices.full_list_ref(); let mut history = ConsumptionHistoryFile::open(time_secs(), ConsumptionHistoryType::GridConsumption)?; @@ -119,7 +119,21 @@ impl Handler for EnergyActor { } } -/// Get current consumption +/// Get relays consumption +#[derive(Message)] +#[rtype(result = "usize")] +pub struct RelaysConsumption; + +impl Handler for EnergyActor { + type Result = usize; + + fn handle(&mut self, _msg: RelaysConsumption, _ctx: &mut Context) -> Self::Result { + self.engine + .sum_relays_consumption(&self.devices.full_list_ref()) + } +} + +/// Check if device exists #[derive(Message)] #[rtype(result = "bool")] pub struct CheckDeviceExists(pub DeviceId); diff --git a/central_backend/src/energy/engine.rs b/central_backend/src/energy/engine.rs index 46bcd94..1f8c236 100644 --- a/central_backend/src/energy/engine.rs +++ b/central_backend/src/energy/engine.rs @@ -55,7 +55,7 @@ pub struct EnergyEngine { impl DeviceRelay { // Note : this function is not recursive - fn has_running_dependencies(&self, s: &RelaysState, devices: &[Device]) -> bool { + fn has_running_dependencies(&self, s: &RelaysState, devices: &[&Device]) -> bool { for d in devices { for r in &d.relays { if r.depends_on.contains(&self.id) && s.get(&r.id).unwrap().is_on() { @@ -72,7 +72,7 @@ impl DeviceRelay { self.depends_on.iter().any(|id| s.get(id).unwrap().is_off()) } - fn is_having_conflict(&self, s: &RelaysState, devices: &[Device]) -> bool { + fn is_having_conflict(&self, s: &RelaysState, devices: &[&Device]) -> bool { if self .conflicts_with .iter() @@ -94,7 +94,7 @@ impl DeviceRelay { } } -fn sum_relays_consumption(state: &RelaysState, devices: &[Device]) -> usize { +fn sum_relays_consumption(state: &RelaysState, devices: &[&Device]) -> usize { let mut consumption = 0; for d in devices { @@ -119,11 +119,11 @@ impl EnergyEngine { self.relays_state.get_mut(&relay_id).unwrap() } - pub fn sum_relays_consumption(&self, devices: &[Device]) -> usize { + pub fn sum_relays_consumption(&self, devices: &[&Device]) -> usize { sum_relays_consumption(&self.relays_state, devices) } - fn print_summary(&mut self, curr_consumption: EnergyConsumption, devices: &[Device]) { + fn print_summary(&mut self, curr_consumption: EnergyConsumption, devices: &[&Device]) { log::info!("Current consumption: {curr_consumption}"); let mut table = Table::new(); @@ -172,13 +172,13 @@ impl EnergyEngine { pub fn estimated_consumption_without_relays( &self, curr_consumption: EnergyConsumption, - devices: &[Device], + devices: &[&Device], ) -> EnergyConsumption { curr_consumption - self.sum_relays_consumption(devices) as i32 } /// Refresh energy engine; this method shall never fail ! - pub fn refresh(&mut self, curr_consumption: EnergyConsumption, devices: &[Device]) { + pub fn refresh(&mut self, curr_consumption: EnergyConsumption, devices: &[&Device]) { let base_production = self.estimated_consumption_without_relays(curr_consumption, devices); log::info!("Estimated base production: {base_production}"); @@ -366,7 +366,7 @@ impl EnergyEngine { } /// Save relays state to disk - pub fn persist_relays_state(&mut self, devices: &[Device]) -> anyhow::Result<()> { + pub fn persist_relays_state(&mut self, devices: &[&Device]) -> anyhow::Result<()> { // Save all relays state for d in devices { for r in &d.relays { diff --git a/central_backend/src/server/servers.rs b/central_backend/src/server/servers.rs index 1dbced0..c0325f3 100644 --- a/central_backend/src/server/servers.rs +++ b/central_backend/src/server/servers.rs @@ -139,6 +139,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()> "/web_api/energy/cached_consumption", web::get().to(energy_controller::cached_consumption), ) + .route( + "/web_api/energy/relays_consumption", + web::get().to(energy_controller::relays_consumption), + ) .route( "/web_api/energy/relays_consumption/history", web::get().to(energy_controller::relays_consumption_history), diff --git a/central_backend/src/server/web_api/energy_controller.rs b/central_backend/src/server/web_api/energy_controller.rs index 4147321..86c19ed 100644 --- a/central_backend/src/server/web_api/energy_controller.rs +++ b/central_backend/src/server/web_api/energy_controller.rs @@ -1,4 +1,5 @@ use crate::app_config::ConsumptionHistoryType; +use crate::energy::consumption::EnergyConsumption; use crate::energy::consumption_history_file::ConsumptionHistoryFile; use crate::energy::{consumption, energy_actor}; use crate::server::custom_error::HttpResult; @@ -36,6 +37,14 @@ pub async fn cached_consumption(energy_actor: WebEnergyActor) -> HttpResult { Ok(HttpResponse::Ok().json(Consumption { consumption })) } +/// Get current relays consumption +pub async fn relays_consumption(energy_actor: WebEnergyActor) -> HttpResult { + let consumption = + energy_actor.send(energy_actor::RelaysConsumption).await? as EnergyConsumption; + + Ok(HttpResponse::Ok().json(Consumption { consumption })) +} + pub async fn relays_consumption_history() -> HttpResult { let history = ConsumptionHistoryFile::get_history( ConsumptionHistoryType::RelayConsumption, diff --git a/central_frontend/src/api/EnergyApi.ts b/central_frontend/src/api/EnergyApi.ts index f202348..bd5640c 100644 --- a/central_frontend/src/api/EnergyApi.ts +++ b/central_frontend/src/api/EnergyApi.ts @@ -35,6 +35,18 @@ export class EnergyApi { return data.data.consumption; } + /** + * Get relays consumption + */ + static async RelaysConsumption(): Promise { + return ( + await APIClient.exec({ + method: "GET", + uri: "/energy/relays_consumption", + }) + ).data.consumption; + } + /** * Get relays consumption history */ diff --git a/central_frontend/src/api/RelayApi.ts b/central_frontend/src/api/RelayApi.ts index 53d68a4..0620541 100644 --- a/central_frontend/src/api/RelayApi.ts +++ b/central_frontend/src/api/RelayApi.ts @@ -83,7 +83,7 @@ export class RelayApi { return ( await APIClient.exec({ method: "GET", - uri: `/relay/${relay.id}/state`, + uri: `/relay/${relay.id}/status`, }) ).data; } diff --git a/central_frontend/src/routes/HomeRoute.tsx b/central_frontend/src/routes/HomeRoute.tsx index a01d284..40b97f0 100644 --- a/central_frontend/src/routes/HomeRoute.tsx +++ b/central_frontend/src/routes/HomeRoute.tsx @@ -2,6 +2,7 @@ import { Typography } from "@mui/material"; import { CurrConsumptionWidget } from "./HomeRoute/CurrConsumptionWidget"; import Grid from "@mui/material/Grid2"; import { CachedConsumptionWidget } from "./HomeRoute/CachedConsumptionWidget"; +import { RelayConsumptionWidget } from "./HomeRoute/RelayConsumptionWidget"; export function HomeRoute(): React.ReactElement { return ( @@ -18,6 +19,9 @@ export function HomeRoute(): React.ReactElement { + + + diff --git a/central_frontend/src/routes/HomeRoute/RelayConsumptionWidget.tsx b/central_frontend/src/routes/HomeRoute/RelayConsumptionWidget.tsx new file mode 100644 index 0000000..8004fb1 --- /dev/null +++ b/central_frontend/src/routes/HomeRoute/RelayConsumptionWidget.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { EnergyApi } from "../../api/EnergyApi"; +import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider"; +import StatCard from "../../widgets/StatCard"; + +export function RelayConsumptionWidget(): React.ReactElement { + const snackbar = useSnackbar(); + + const [val, setVal] = React.useState(); + const [history, setHistory] = React.useState(); + + const refresh = async () => { + try { + const s = await EnergyApi.RelaysConsumption(); + const history = await EnergyApi.RelaysConsumptionHistory(); + setVal(s); + setHistory(history); + } catch (e) { + console.error(e); + snackbar("Failed to refresh current relays consumption!"); + } + }; + + React.useEffect(() => { + const i = setInterval(() => refresh(), 3000); + + return () => clearInterval(i); + }); + + return ( + + ); +} diff --git a/central_frontend/src/widgets/TimeWidget.tsx b/central_frontend/src/widgets/TimeWidget.tsx index 9e0548c..f590a48 100644 --- a/central_frontend/src/widgets/TimeWidget.tsx +++ b/central_frontend/src/widgets/TimeWidget.tsx @@ -61,7 +61,10 @@ export function TimeWidget(p: { }): React.ReactElement { if (!p.time) return <>; return ( - + {p.diff ? timeDiff(0, p.time) : timeDiffFromNow(p.time)} );