WIP engine
This commit is contained in:
		@@ -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<DeviceRelayID, RelayState>;
 | 
			
		||||
 | 
			
		||||
#[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,28 +137,32 @@ 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 {
 | 
			
		||||
                    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)
 | 
			
		||||
                        {
 | 
			
		||||
                    if r.is_missing_dependencies(&new_relays_state) {
 | 
			
		||||
                        new_relays_state.get_mut(&r.id).unwrap().on = false;
 | 
			
		||||
                        changed = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if !changed {
 | 
			
		||||
                break;
 | 
			
		||||
@@ -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::<Vec<_>>();
 | 
			
		||||
        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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user