From fe0bc03c03c2e5bb38c238f0e4782b5a04b220d8 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Thu, 19 Sep 2024 21:26:57 +0200 Subject: [PATCH] Implement catchup hours logic --- central_backend/Cargo.lock | 1 + central_backend/Cargo.toml | 1 + central_backend/src/devices/device.rs | 2 +- central_backend/src/energy/energy_actor.rs | 2 +- central_backend/src/energy/engine.rs | 45 ++++++++++++++++++++-- central_backend/src/utils/time_utils.rs | 17 +++++++- 6 files changed, 62 insertions(+), 6 deletions(-) diff --git a/central_backend/Cargo.lock b/central_backend/Cargo.lock index 83a692a..9b7f0a2 100644 --- a/central_backend/Cargo.lock +++ b/central_backend/Cargo.lock @@ -613,6 +613,7 @@ dependencies = [ "actix-web", "anyhow", "asn1", + "chrono", "clap", "env_logger", "foreign-types-shared", diff --git a/central_backend/Cargo.toml b/central_backend/Cargo.toml index e0b8ef1..46351b8 100644 --- a/central_backend/Cargo.toml +++ b/central_backend/Cargo.toml @@ -36,3 +36,4 @@ mime_guess = "2.0.5" rust-embed = "8.5.0" jsonwebtoken = { version = "9.3.0", features = ["use_pem"] } prettytable-rs = "0.10.0" +chrono = "0.4.38" \ No newline at end of file diff --git a/central_backend/src/devices/device.rs b/central_backend/src/devices/device.rs index a334891..3366ef0 100644 --- a/central_backend/src/devices/device.rs +++ b/central_backend/src/devices/device.rs @@ -73,7 +73,7 @@ pub struct DailyMinRuntime { /// The seconds in the days (from 00:00) where the counter is reset pub reset_time: usize, /// The hours during which the relay should be turned on to reach expected runtime - pub catch_up_hours: Vec, + pub catch_up_hours: Vec, } #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)] diff --git a/central_backend/src/energy/energy_actor.rs b/central_backend/src/energy/energy_actor.rs index 9b677cb..4565d07 100644 --- a/central_backend/src/energy/energy_actor.rs +++ b/central_backend/src/energy/energy_actor.rs @@ -40,7 +40,7 @@ impl EnergyActor { let devices_list = self.devices.full_list(); - self.engine.refresh(self.curr_consumption, &devices_list); + self.engine.refresh(self.curr_consumption, &devices_list)?; self.engine.persist_relays_state(&devices_list)?; diff --git a/central_backend/src/energy/engine.rs b/central_backend/src/energy/engine.rs index 65feb13..e21e1af 100644 --- a/central_backend/src/energy/engine.rs +++ b/central_backend/src/energy/engine.rs @@ -6,8 +6,9 @@ use prettytable::{row, Table}; use crate::constants; use crate::devices::device::{Device, DeviceId, DeviceRelay, DeviceRelayID}; use crate::energy::consumption::EnergyConsumption; +use crate::energy::relay_state_history; use crate::energy::relay_state_history::RelayStateHistory; -use crate::utils::time_utils::time_secs; +use crate::utils::time_utils::{curr_hour, time_secs, time_start_of_day}; #[derive(Default)] pub struct DeviceState { @@ -168,7 +169,11 @@ impl EnergyEngine { curr_consumption - sum_relays_consumption(&self.relays_state, devices) as i32 } - pub fn refresh(&mut self, curr_consumption: EnergyConsumption, devices: &[Device]) { + pub fn refresh( + &mut self, + curr_consumption: EnergyConsumption, + devices: &[Device], + ) -> anyhow::Result<()> { let base_production = self.estimated_consumption_without_relays(curr_consumption, devices); log::info!("Estimated base production: {base_production}"); @@ -254,7 +259,39 @@ impl EnergyEngine { } } - // TODO Turn on relays with running constraints (only ENABLED) + // Turn on relays with running constraints (only ENABLED) + for d in devices { + for r in &d.relays { + if !r.enabled || !d.enabled || !self.device_state(&d.id).is_online() { + continue; + } + + if new_relays_state.get(&r.id).unwrap().is_on() { + continue; + } + + let Some(constraints) = &r.daily_runtime else { + continue; + }; + + if !constraints.catch_up_hours.contains(&curr_hour()) { + continue; + } + + let time_start_day = time_start_of_day()?; + let start_time = time_start_day + constraints.reset_time as u64; + let end_time = time_start_day + 3600 * 24 + constraints.reset_time as u64; + let total_runtime = + relay_state_history::relay_total_runtime(r.id, start_time, end_time)?; + + if total_runtime > constraints.min_runtime { + continue; + } + + log::info!("Forcefully turn on relay {} to catch up running constraints (only {}s this day)", r.name, total_runtime); + new_relays_state.get_mut(&r.id).unwrap().on = true; + } + } // Order relays let mut ordered_relays = devices @@ -320,6 +357,8 @@ impl EnergyEngine { } self.print_summary(curr_consumption, devices); + + Ok(()) } /// Save relays state to disk diff --git a/central_backend/src/utils/time_utils.rs b/central_backend/src/utils/time_utils.rs index af8bfd1..91828a6 100644 --- a/central_backend/src/utils/time_utils.rs +++ b/central_backend/src/utils/time_utils.rs @@ -1,3 +1,4 @@ +use chrono::prelude::*; use std::time::{SystemTime, UNIX_EPOCH}; /// Get the current time since epoch @@ -16,11 +17,25 @@ pub fn time_millis() -> u128 { .as_millis() } -/// Get the number of the day since 01-01-1970 of a given UNIX timestamp +/// Get the number of the day since 01-01-1970 of a given UNIX timestamp (UTC) pub fn day_number(time: u64) -> u64 { time / (3600 * 24) } +/// Get current hour, 00 => 23 (local time) +pub fn curr_hour() -> u32 { + let local: DateTime = Local::now(); + local.hour() +} + +/// Get the first second of the day (local time) +pub fn time_start_of_day() -> anyhow::Result { + let local: DateTime = Local::now() + .with_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap()) + .unwrap(); + Ok(local.timestamp() as u64) +} + #[cfg(test)] mod test { use crate::utils::time_utils::day_number;