diff --git a/central_backend/src/energy/engine.rs b/central_backend/src/energy/engine.rs index 85ad257..b1b7ab8 100644 --- a/central_backend/src/energy/engine.rs +++ b/central_backend/src/energy/engine.rs @@ -1,9 +1,11 @@ +use std::collections::HashMap; + +use prettytable::{row, Table}; + use crate::constants; use crate::devices::device::{Device, DeviceId, DeviceRelay, DeviceRelayID}; use crate::energy::consumption::EnergyConsumption; use crate::utils::time_utils::time_secs; -use prettytable::{row, Table}; -use std::collections::HashMap; #[derive(Default)] pub struct DeviceState { @@ -26,6 +28,16 @@ pub struct RelayState { since: usize, } +impl RelayState { + fn is_on(&self) -> bool { + self.on + } + + fn is_off(&self) -> bool { + !self.on + } +} + type RelaysState = HashMap; #[derive(Default)] @@ -35,10 +47,37 @@ pub struct EnergyEngine { } impl DeviceRelay { - fn relay_has_running_dependencies(&self, s: &RelaysState, devices: &[Device]) -> bool { + // Note : this function is not recursive + 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().on { + if r.depends_on.contains(&self.id) && s.get(&r.id).unwrap().is_on() { + return true; + } + } + } + + false + } + + // Note : this function is not recursive + fn is_missing_dependencies(&self, s: &RelaysState) -> bool { + self.depends_on.iter().any(|id| s.get(id).unwrap().is_off()) + } + + fn is_having_conflict(&self, s: &RelaysState, devices: &[Device]) -> bool { + if self + .conflicts_with + .iter() + .any(|id| s.get(id).unwrap().is_on()) + { + return true; + } + + // Reverse search + for device in devices { + for r in &device.relays { + if s.get(&r.id).unwrap().is_on() && r.conflicts_with.contains(&self.id) { return true; } } @@ -67,7 +106,12 @@ impl EnergyEngine { for d in devices { for r in &d.relays { let status = self.relay_state(r.id); - table.add_row(row![d.name, r.name, status.on.to_string(), status.since]); + table.add_row(row![ + d.name, + r.name, + status.is_on().to_string(), + status.since + ]); } } table.printstd(); @@ -93,25 +137,29 @@ impl EnergyEngine { } } + // Forcefully turn off disabled relays + for d in devices { + for r in &d.relays { + if !r.enabled { + new_relays_state.get_mut(&r.id).unwrap().on = false; + } + } + } + // Forcefully turn off relays with missing dependency loop { let mut changed = false; for d in devices { - if !self.device_state(&d.id).is_online() { - for r in &d.relays { - if !new_relays_state.get(&r.id).unwrap().on { - continue; - } + for r in &d.relays { + if new_relays_state.get(&r.id).unwrap().is_off() { + continue; + } - // Check if any dependency of relay is off - if r.depends_on - .iter() - .any(|d| !new_relays_state.get(d).unwrap().on) - { - new_relays_state.get_mut(&r.id).unwrap().on = false; - changed = true; - } + // Check if any dependency of relay is off + if r.is_missing_dependencies(&new_relays_state) { + new_relays_state.get_mut(&r.id).unwrap().on = false; + changed = true; } } } @@ -128,7 +176,7 @@ impl EnergyEngine { for d in devices { for r in &d.relays { let state = new_relays_state.get(&r.id).unwrap(); - if !state.on { + if state.is_off() { continue; } @@ -138,7 +186,7 @@ impl EnergyEngine { } // Check that no relay that depends on this relay are turned on - if r.relay_has_running_dependencies(&new_relays_state, devices) { + if r.has_running_dependencies(&new_relays_state, devices) { continue; } @@ -152,19 +200,63 @@ impl EnergyEngine { } } - // TODO Turn on relays based on priority / dependencies / enabled / min downtime + // TODO Turn on relays with running constraints (only ENABLED) - // TODO Turn on relays with running constraints + // Order relays + let mut ordered_relays = devices + .iter() + .filter(|d| self.device_state(&d.id).is_online() && d.enabled) + .flat_map(|d| &d.relays) + .filter(|r| r.enabled) + .collect::>(); + ordered_relays.sort_by_key(|r| r.priority); + ordered_relays.reverse(); - // TODO Commit changes + loop { + let mut changed = false; + for relay in &ordered_relays { + if new_relays_state.get(&relay.id).unwrap().is_on() { + continue; + } + if !relay.enabled { + continue; + } + + let real_relay_state = self.relays_state.get(&relay.id).unwrap(); + if real_relay_state.is_off() + && (real_relay_state.since + relay.minimal_downtime) as u64 > time_secs() + { + continue; + } + + if relay.is_missing_dependencies(&new_relays_state) { + continue; + } + + if relay.is_having_conflict(&new_relays_state, devices) { + continue; + } + + // TODO : check consumption + log::info!("Turn on relay {}", relay.name); + new_relays_state.get_mut(&relay.id).unwrap().on = true; + changed = true; + } + + if !changed { + break; + } + } + + // Commit changes for (id, new_state) in &new_relays_state { let curr_state = self.relay_state(*id); if curr_state.on != new_state.on { curr_state.on = new_state.on; curr_state.since = time_secs() as usize; + log::info!("Changing state of {id:?} to {}", new_state.on); } - log::info!("Changing state of {id:?}"); } self.print_summary(curr_consumption, devices);