diff --git a/central_backend/Cargo.lock b/central_backend/Cargo.lock index 692306b..83a692a 100644 --- a/central_backend/Cargo.lock +++ b/central_backend/Cargo.lock @@ -626,6 +626,7 @@ dependencies = [ "mime_guess", "openssl", "openssl-sys", + "prettytable-rs", "rand", "reqwest", "rust-embed", @@ -798,6 +799,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.9.2" @@ -840,6 +862,33 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -1118,6 +1167,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hkdf" version = "0.12.4" @@ -1336,6 +1391,17 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1422,6 +1488,16 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1498,7 +1574,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "log", "wasi", @@ -1731,6 +1807,20 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -1788,6 +1878,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.6" @@ -1983,6 +2084,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.18" @@ -2226,6 +2333,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -2466,6 +2584,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + [[package]] name = "universal-hash" version = "0.5.1" @@ -2622,6 +2746,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -2631,6 +2771,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" diff --git a/central_backend/Cargo.toml b/central_backend/Cargo.toml index 67fe668..6a3ccf9 100644 --- a/central_backend/Cargo.toml +++ b/central_backend/Cargo.toml @@ -34,4 +34,5 @@ tokio = { version = "1.39.2", features = ["full"] } tokio_schedule = "0.3.2" mime_guess = "2.0.5" rust-embed = "8.5.0" -jsonwebtoken = { version = "9.3.0", features = ["use_pem"] } \ No newline at end of file +jsonwebtoken = { version = "9.3.0", features = ["use_pem"] } +prettytable-rs = "0.10.0" \ No newline at end of file diff --git a/central_backend/src/energy/energy_actor.rs b/central_backend/src/energy/energy_actor.rs index a6b7183..896aadb 100644 --- a/central_backend/src/energy/energy_actor.rs +++ b/central_backend/src/energy/energy_actor.rs @@ -5,33 +5,15 @@ use crate::devices::device::{ use crate::devices::devices_list::DevicesList; use crate::energy::consumption; use crate::energy::consumption::EnergyConsumption; +use crate::energy::engine::EnergyEngine; use crate::utils::time_utils::time_secs; use actix::prelude::*; use openssl::x509::X509Req; -use std::collections::HashMap; - -#[derive(Default)] -struct DeviceState { - last_ping: u64, -} - -impl DeviceState { - fn is_online(&self) -> bool { - (time_secs() - self.last_ping) < constants::DEVICE_MAX_PING_TIME - } -} - -#[derive(Default)] -struct RelayState { - enabled: bool, - since: usize, -} pub struct EnergyActor { curr_consumption: EnergyConsumption, devices: DevicesList, - devices_state: HashMap, - relays_state: HashMap, + engine: EnergyEngine, } impl EnergyActor { @@ -39,20 +21,10 @@ impl EnergyActor { Ok(Self { curr_consumption: consumption::get_curr_consumption().await?, devices: DevicesList::load()?, - devices_state: Default::default(), - relays_state: Default::default(), + engine: EnergyEngine::default(), }) } - fn device_state(&mut self, dev_id: &DeviceId) -> &mut DeviceState { - if !self.devices_state.contains_key(dev_id) { - self.devices_state - .insert(dev_id.clone(), Default::default()); - } - - self.devices_state.get_mut(dev_id).unwrap() - } - async fn refresh(&mut self) -> anyhow::Result<()> { // Refresh energy self.curr_consumption = consumption::get_curr_consumption() @@ -64,6 +36,9 @@ impl EnergyActor { constants::FALLBACK_PRODUCTION_VALUE }); + self.engine + .refresh(self.curr_consumption, &self.devices.full_list()); + Ok(()) } } @@ -285,8 +260,7 @@ impl Handler for EnergyActor { type Result = anyhow::Result>; fn handle(&mut self, msg: SynchronizeDevice, _ctx: &mut Context) -> Self::Result { - let s = self.device_state(&msg.0); - s.last_ping = time_secs(); + self.engine.device_state(&msg.0).record_ping(); // TODO : implement real code let mut v = vec![]; @@ -319,7 +293,7 @@ impl Handler for EnergyActor { .full_list() .into_iter() .map(|d| { - let s = self.device_state(&d.id); + let s = self.engine.device_state(&d.id); ResDevState { id: d.id, last_ping: time_secs() - s.last_ping, diff --git a/central_backend/src/energy/engine.rs b/central_backend/src/energy/engine.rs new file mode 100644 index 0000000..570cd12 --- /dev/null +++ b/central_backend/src/energy/engine.rs @@ -0,0 +1,83 @@ +use crate::constants; +use crate::devices::device::{Device, DeviceId, 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 { + pub last_ping: u64, +} + +impl DeviceState { + pub fn record_ping(&mut self) { + self.last_ping = time_secs(); + } + + pub fn is_online(&self) -> bool { + (time_secs() - self.last_ping) < constants::DEVICE_MAX_PING_TIME + } +} + +#[derive(Default, Clone)] +struct RelayState { + on: bool, + since: usize, +} + +#[derive(Default)] +pub struct EnergyEngine { + devices_state: HashMap, + relays_state: HashMap, +} + +impl EnergyEngine { + pub fn device_state(&mut self, dev_id: &DeviceId) -> &mut DeviceState { + if !self.devices_state.contains_key(dev_id) { + self.devices_state + .insert(dev_id.clone(), Default::default()); + } + + self.devices_state.get_mut(dev_id).unwrap() + } + + pub fn relay_state(&mut self, relay_id: DeviceRelayID) -> &mut RelayState { + if !self.relays_state.contains_key(&relay_id) { + self.relays_state.insert(relay_id, Default::default()); + } + + self.relays_state.get_mut(&relay_id).unwrap() + } + + fn print_summary(&mut self, curr_consumption: EnergyConsumption, devices: &[Device]) { + log::info!("Current consumption: {curr_consumption}"); + + let mut table = Table::new(); + table.add_row(row!["Device", "Relay", "On", "Since"]); + 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.printstd(); + } + + pub fn refresh(&mut self, curr_consumption: EnergyConsumption, devices: &[Device]) { + let new_relays_state = self.relays_state.clone(); + + // Forcefully turn off relays that belongs to offline devices + + // Forcefully turn off relays with dependency conflicts + + // Virtually turn off all relays that can be stopped + + // Turn on relays based on priority / dependencies + + // Turn on relays with running constraints + + // Commit changes + self.print_summary(curr_consumption, devices); // TODO :replace + } +} diff --git a/central_backend/src/energy/mod.rs b/central_backend/src/energy/mod.rs index 747d93a..c080861 100644 --- a/central_backend/src/energy/mod.rs +++ b/central_backend/src/energy/mod.rs @@ -1,2 +1,3 @@ pub mod consumption; pub mod energy_actor; +pub mod engine;