diff --git a/central_backend/src/app_config.rs b/central_backend/src/app_config.rs index 3974779..8a76687 100644 --- a/central_backend/src/app_config.rs +++ b/central_backend/src/app_config.rs @@ -76,6 +76,14 @@ pub struct AppConfig { #[arg(short, long, env, default_value = "storage")] storage: String, + /// The minimal production that must be excluded when selecting relays to turn on + #[arg(short('m'), long, env, default_value_t = -500)] + pub production_margin: i32, + + /// Energy refresh operations interval, in seconds + #[arg(short('i'), long, env, default_value_t = 30)] + pub refresh_interval: u64, + /// Consumption backend provider #[clap(subcommand)] pub consumption_backend: Option, diff --git a/central_backend/src/constants.rs b/central_backend/src/constants.rs index 5c44b09..a1a1c0c 100644 --- a/central_backend/src/constants.rs +++ b/central_backend/src/constants.rs @@ -1,11 +1,6 @@ -use std::time::Duration; - /// Name of the cookie that contains session information pub const SESSION_COOKIE_NAME: &str = "X-session-cookie"; -/// Energy refresh operations interval -pub const ENERGY_REFRESH_INTERVAL: Duration = Duration::from_secs(30); - /// Maximum time after a ping during which a device is considered "up" pub const DEVICE_MAX_PING_TIME: u64 = 30; diff --git a/central_backend/src/energy/energy_actor.rs b/central_backend/src/energy/energy_actor.rs index 4b9acca..b169b98 100644 --- a/central_backend/src/energy/energy_actor.rs +++ b/central_backend/src/energy/energy_actor.rs @@ -1,3 +1,4 @@ +use crate::app_config::AppConfig; use crate::constants; use crate::devices::device::{ Device, DeviceGeneralInfo, DeviceId, DeviceInfo, DeviceRelay, DeviceRelayID, @@ -9,6 +10,7 @@ use crate::energy::engine::EnergyEngine; use crate::utils::time_utils::time_secs; use actix::prelude::*; use openssl::x509::X509Req; +use std::time::Duration; pub struct EnergyActor { curr_consumption: EnergyConsumption, @@ -49,12 +51,15 @@ impl Actor for EnergyActor { fn started(&mut self, ctx: &mut Self::Context) { log::info!("Energy actor successfully started!"); - ctx.run_interval(constants::ENERGY_REFRESH_INTERVAL, |act, _ctx| { - log::info!("Performing energy refresh operation"); - if let Err(e) = futures::executor::block_on(act.refresh()) { - log::error!("Energy refresh failed! {e}") - } - }); + ctx.run_interval( + Duration::from_secs(AppConfig::get().refresh_interval), + |act, _ctx| { + log::info!("Performing energy refresh operation"); + if let Err(e) = futures::executor::block_on(act.refresh()) { + log::error!("Energy refresh failed! {e}") + } + }, + ); } fn stopped(&mut self, _ctx: &mut Self::Context) { diff --git a/central_backend/src/energy/engine.rs b/central_backend/src/energy/engine.rs index 8cec247..ad6675d 100644 --- a/central_backend/src/energy/engine.rs +++ b/central_backend/src/energy/engine.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use crate::app_config::AppConfig; use prettytable::{row, Table}; use crate::constants; @@ -87,6 +88,20 @@ impl DeviceRelay { } } +fn sum_relays_consumption(state: &RelaysState, devices: &[Device]) -> usize { + let mut consumption = 0; + + for d in devices { + for r in &d.relays { + if matches!(state.get(&r.id).map(|r| r.on), Some(true)) { + consumption += r.consumption; + } + } + } + + consumption +} + impl EnergyEngine { pub fn device_state(&mut self, dev_id: &DeviceId) -> &mut DeviceState { self.devices_state.entry(dev_id.clone()).or_default(); @@ -102,22 +117,60 @@ impl EnergyEngine { log::info!("Current consumption: {curr_consumption}"); let mut table = Table::new(); - table.add_row(row!["Device", "Relay", "On", "Since"]); + table.add_row(row![ + "Device", + "Relay", + "Consumption", + "Min downtime / uptime", + "On", + "Since", + "Online", + "Enabled device / relay" + ]); for d in devices { + let dev_online = self.device_state(&d.id).is_online(); for r in &d.relays { let status = self.relay_state(r.id); table.add_row(row![ d.name, r.name, + r.consumption, + format!("{} / {}", r.minimal_downtime, r.minimal_uptime), status.is_on().to_string(), - status.since + status.since, + match dev_online { + true => "Online", + false => "Offline", + }, + format!( + "{} / {}", + match d.enabled { + true => "Enabled", + false => "Disabled", + }, + match r.enabled { + true => "Enabled", + false => "Disabled", + } + ) ]); } } table.printstd(); } + pub fn estimated_consumption_without_relays( + &self, + curr_consumption: EnergyConsumption, + devices: &[Device], + ) -> EnergyConsumption { + curr_consumption - sum_relays_consumption(&self.relays_state, devices) as i32 + } + 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}"); + // Force creation of missing relays state for d in devices { for r in &d.relays { @@ -238,7 +291,13 @@ impl EnergyEngine { continue; } - // TODO : check consumption + let new_consumption = base_production + + sum_relays_consumption(&new_relays_state, devices) as EnergyConsumption; + + if new_consumption + relay.consumption as i32 > AppConfig::get().production_margin { + continue; + } + log::info!("Turn on relay {}", relay.name); new_relays_state.get_mut(&relay.id).unwrap().on = true; changed = true;