2024-09-10 17:40:06 +00:00
|
|
|
use crate::constants;
|
2024-09-13 20:11:40 +00:00
|
|
|
use crate::devices::device::{Device, DeviceId, DeviceRelay, DeviceRelayID};
|
2024-09-10 17:40:06 +00:00
|
|
|
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)]
|
2024-09-10 17:55:51 +00:00
|
|
|
pub struct RelayState {
|
2024-09-10 17:40:06 +00:00
|
|
|
on: bool,
|
|
|
|
since: usize,
|
|
|
|
}
|
|
|
|
|
2024-09-13 20:11:40 +00:00
|
|
|
type RelaysState = HashMap<DeviceRelayID, RelayState>;
|
|
|
|
|
2024-09-10 17:40:06 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct EnergyEngine {
|
|
|
|
devices_state: HashMap<DeviceId, DeviceState>,
|
2024-09-13 20:11:40 +00:00
|
|
|
relays_state: RelaysState,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DeviceRelay {
|
|
|
|
fn relay_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 {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
2024-09-10 17:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl EnergyEngine {
|
|
|
|
pub fn device_state(&mut self, dev_id: &DeviceId) -> &mut DeviceState {
|
2024-09-10 17:55:51 +00:00
|
|
|
self.devices_state.entry(dev_id.clone()).or_default();
|
2024-09-10 17:40:06 +00:00
|
|
|
self.devices_state.get_mut(dev_id).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn relay_state(&mut self, relay_id: DeviceRelayID) -> &mut RelayState {
|
2024-09-10 17:55:51 +00:00
|
|
|
self.relays_state.entry(relay_id).or_default();
|
2024-09-10 17:40:06 +00:00
|
|
|
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]) {
|
2024-09-12 19:45:58 +00:00
|
|
|
// Force creation of missing relays state
|
|
|
|
for d in devices {
|
|
|
|
for r in &d.relays {
|
|
|
|
// Requesting relay state is enough to trigger relay creation
|
|
|
|
self.relay_state(r.id);
|
|
|
|
}
|
|
|
|
}
|
2024-09-10 17:55:51 +00:00
|
|
|
|
2024-09-12 19:45:58 +00:00
|
|
|
let mut new_relays_state = self.relays_state.clone();
|
2024-09-10 17:40:06 +00:00
|
|
|
|
2024-09-12 19:45:58 +00:00
|
|
|
// Forcefully turn off relays that belongs to offline devices
|
|
|
|
for d in devices {
|
|
|
|
if !self.device_state(&d.id).is_online() {
|
|
|
|
for r in &d.relays {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-09-10 17:40:06 +00:00
|
|
|
|
2024-09-12 19:45:58 +00:00
|
|
|
if !changed {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-09-10 17:40:06 +00:00
|
|
|
|
2024-09-13 20:11:40 +00:00
|
|
|
// Virtually turn off all relays that can be stopped
|
|
|
|
loop {
|
|
|
|
let mut changed = false;
|
|
|
|
|
|
|
|
for d in devices {
|
|
|
|
for r in &d.relays {
|
|
|
|
let state = new_relays_state.get(&r.id).unwrap();
|
|
|
|
if !state.on {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if minimal runtime has not been reached
|
|
|
|
if (state.since + r.minimal_uptime) as i64 > time_secs() as i64 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that no relay that depends on this relay are turned on
|
|
|
|
if r.relay_has_running_dependencies(&new_relays_state, devices) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_relays_state.get_mut(&r.id).unwrap().on = false;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !changed {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-09-10 17:40:06 +00:00
|
|
|
|
2024-09-13 20:11:40 +00:00
|
|
|
// TODO Turn on relays based on priority / dependencies / enabled / min downtime
|
2024-09-10 17:40:06 +00:00
|
|
|
|
2024-09-10 17:55:51 +00:00
|
|
|
// TODO Turn on relays with running constraints
|
|
|
|
|
|
|
|
// TODO 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:?}");
|
|
|
|
}
|
2024-09-10 17:40:06 +00:00
|
|
|
|
2024-09-10 17:55:51 +00:00
|
|
|
self.print_summary(curr_consumption, devices);
|
2024-09-10 17:40:06 +00:00
|
|
|
}
|
|
|
|
}
|