WIP engine
This commit is contained in:
		@@ -1,9 +1,11 @@
 | 
				
			|||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use prettytable::{row, Table};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::constants;
 | 
					use crate::constants;
 | 
				
			||||||
use crate::devices::device::{Device, DeviceId, DeviceRelay, DeviceRelayID};
 | 
					use crate::devices::device::{Device, DeviceId, DeviceRelay, DeviceRelayID};
 | 
				
			||||||
use crate::energy::consumption::EnergyConsumption;
 | 
					use crate::energy::consumption::EnergyConsumption;
 | 
				
			||||||
use crate::utils::time_utils::time_secs;
 | 
					use crate::utils::time_utils::time_secs;
 | 
				
			||||||
use prettytable::{row, Table};
 | 
					 | 
				
			||||||
use std::collections::HashMap;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
pub struct DeviceState {
 | 
					pub struct DeviceState {
 | 
				
			||||||
@@ -26,6 +28,16 @@ pub struct RelayState {
 | 
				
			|||||||
    since: usize,
 | 
					    since: usize,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl RelayState {
 | 
				
			||||||
 | 
					    fn is_on(&self) -> bool {
 | 
				
			||||||
 | 
					        self.on
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn is_off(&self) -> bool {
 | 
				
			||||||
 | 
					        !self.on
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RelaysState = HashMap<DeviceRelayID, RelayState>;
 | 
					type RelaysState = HashMap<DeviceRelayID, RelayState>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
@@ -35,10 +47,37 @@ pub struct EnergyEngine {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl DeviceRelay {
 | 
					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 d in devices {
 | 
				
			||||||
            for r in &d.relays {
 | 
					            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;
 | 
					                    return true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -67,7 +106,12 @@ impl EnergyEngine {
 | 
				
			|||||||
        for d in devices {
 | 
					        for d in devices {
 | 
				
			||||||
            for r in &d.relays {
 | 
					            for r in &d.relays {
 | 
				
			||||||
                let status = self.relay_state(r.id);
 | 
					                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();
 | 
					        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
 | 
					        // Forcefully turn off relays with missing dependency
 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            let mut changed = false;
 | 
					            let mut changed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for d in devices {
 | 
					            for d in devices {
 | 
				
			||||||
                if !self.device_state(&d.id).is_online() {
 | 
					 | 
				
			||||||
                for r in &d.relays {
 | 
					                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;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Check if any dependency of relay is off
 | 
					                    // Check if any dependency of relay is off
 | 
				
			||||||
                        if r.depends_on
 | 
					                    if r.is_missing_dependencies(&new_relays_state) {
 | 
				
			||||||
                            .iter()
 | 
					 | 
				
			||||||
                            .any(|d| !new_relays_state.get(d).unwrap().on)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                        new_relays_state.get_mut(&r.id).unwrap().on = false;
 | 
					                        new_relays_state.get_mut(&r.id).unwrap().on = false;
 | 
				
			||||||
                        changed = true;
 | 
					                        changed = true;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if !changed {
 | 
					            if !changed {
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
@@ -128,7 +176,7 @@ impl EnergyEngine {
 | 
				
			|||||||
            for d in devices {
 | 
					            for d in devices {
 | 
				
			||||||
                for r in &d.relays {
 | 
					                for r in &d.relays {
 | 
				
			||||||
                    let state = new_relays_state.get(&r.id).unwrap();
 | 
					                    let state = new_relays_state.get(&r.id).unwrap();
 | 
				
			||||||
                    if !state.on {
 | 
					                    if state.is_off() {
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,7 +186,7 @@ impl EnergyEngine {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Check that no relay that depends on this relay are turned on
 | 
					                    // 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;
 | 
					                        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 {
 | 
					        for (id, new_state) in &new_relays_state {
 | 
				
			||||||
            let curr_state = self.relay_state(*id);
 | 
					            let curr_state = self.relay_state(*id);
 | 
				
			||||||
            if curr_state.on != new_state.on {
 | 
					            if curr_state.on != new_state.on {
 | 
				
			||||||
                curr_state.on = new_state.on;
 | 
					                curr_state.on = new_state.on;
 | 
				
			||||||
                curr_state.since = time_secs() as usize;
 | 
					                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);
 | 
					        self.print_summary(curr_consumption, devices);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user