use crate::constants; use crate::devices::device::{ Device, DeviceGeneralInfo, DeviceId, DeviceInfo, DeviceRelay, DeviceRelayID, }; use crate::devices::devices_list::DevicesList; use crate::energy::consumption; use crate::energy::consumption::EnergyConsumption; 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, } impl EnergyActor { pub async fn new() -> anyhow::Result { Ok(Self { curr_consumption: consumption::get_curr_consumption().await?, devices: DevicesList::load()?, devices_state: Default::default(), relays_state: Default::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() .await .unwrap_or_else(|e| { log::error!( "Failed to fetch latest consumption value, will use fallback value! {e}" ); constants::FALLBACK_PRODUCTION_VALUE }); Ok(()) } } impl Actor for EnergyActor { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { log::info!("Energy actor successfully started!"); ctx.run_interval(constants::ENERGY_REFRESH_INTERVAL, |act, _ctx| { log::info!("Performing energy refresh operation"); if let Err(e) = futures::executor::block_on(act.refresh()) { log::error!("Energy refresh failed! {e}") } }); } fn stopped(&mut self, _ctx: &mut Self::Context) { log::info!("Energy actor successfully stopped!"); } } pub type EnergyActorAddr = Addr; /// Get current consumption #[derive(Message)] #[rtype(result = "EnergyConsumption")] pub struct GetCurrConsumption; impl Handler for EnergyActor { type Result = EnergyConsumption; fn handle(&mut self, _msg: GetCurrConsumption, _ctx: &mut Context) -> Self::Result { self.curr_consumption } } /// Get current consumption #[derive(Message)] #[rtype(result = "bool")] pub struct CheckDeviceExists(pub DeviceId); impl Handler for EnergyActor { type Result = bool; fn handle(&mut self, msg: CheckDeviceExists, _ctx: &mut Context) -> Self::Result { self.devices.exists(&msg.0) } } /// Enroll device #[derive(Message)] #[rtype(result = "anyhow::Result<()>")] pub struct EnrollDevice(pub DeviceId, pub DeviceInfo, pub X509Req); impl Handler for EnergyActor { type Result = anyhow::Result<()>; fn handle(&mut self, msg: EnrollDevice, _ctx: &mut Context) -> Self::Result { self.devices.enroll(&msg.0, &msg.1, &msg.2) } } /// Validate a device #[derive(Message)] #[rtype(result = "anyhow::Result<()>")] pub struct ValidateDevice(pub DeviceId); impl Handler for EnergyActor { type Result = anyhow::Result<()>; fn handle(&mut self, msg: ValidateDevice, _ctx: &mut Context) -> Self::Result { log::info!("Requested to validate device {:?}...", &msg.0); self.devices.validate(&msg.0)?; Ok(()) } } /// Update a device general information #[derive(Message)] #[rtype(result = "anyhow::Result<()>")] pub struct UpdateDeviceGeneralInfo(pub DeviceId, pub DeviceGeneralInfo); impl Handler for EnergyActor { type Result = anyhow::Result<()>; fn handle(&mut self, msg: UpdateDeviceGeneralInfo, _ctx: &mut Context) -> Self::Result { log::info!( "Requested to update device general info {:?}... {:#?}", &msg.0, &msg.1 ); self.devices.update_general_info(&msg.0, msg.1)?; Ok(()) } } /// Delete a device #[derive(Message)] #[rtype(result = "anyhow::Result<()>")] pub struct DeleteDevice(pub DeviceId); impl Handler for EnergyActor { type Result = anyhow::Result<()>; fn handle(&mut self, msg: DeleteDevice, _ctx: &mut Context) -> Self::Result { log::info!("Requested to delete device {:?}...", &msg.0); self.devices.delete(&msg.0)?; // TODO : delete energy related information Ok(()) } } /// Get the list of devices #[derive(Message)] #[rtype(result = "Vec")] pub struct GetDeviceLists; impl Handler for EnergyActor { type Result = Vec; fn handle(&mut self, _msg: GetDeviceLists, _ctx: &mut Context) -> Self::Result { self.devices.full_list() } } /// Get the information about a single device #[derive(Message)] #[rtype(result = "Option")] pub struct GetSingleDevice(pub DeviceId); impl Handler for EnergyActor { type Result = Option; fn handle(&mut self, msg: GetSingleDevice, _ctx: &mut Context) -> Self::Result { self.devices.get_single(&msg.0) } } /// Get the full list of relays #[derive(Message)] #[rtype(result = "Vec")] pub struct GetRelaysList; impl Handler for EnergyActor { type Result = Vec; fn handle(&mut self, _msg: GetRelaysList, _ctx: &mut Context) -> Self::Result { self.devices.relays_list() } } /// Create a new device relay #[derive(Message)] #[rtype(result = "anyhow::Result<()>")] pub struct CreateDeviceRelay(pub DeviceId, pub DeviceRelay); impl Handler for EnergyActor { type Result = anyhow::Result<()>; fn handle(&mut self, msg: CreateDeviceRelay, _ctx: &mut Context) -> Self::Result { self.devices.relay_create(&msg.0, msg.1) } } /// Get the information about a single relay #[derive(Message)] #[rtype(result = "Option")] pub struct GetSingleRelay(pub DeviceRelayID); impl Handler for EnergyActor { type Result = Option; fn handle(&mut self, msg: GetSingleRelay, _ctx: &mut Context) -> Self::Result { self.devices.relay_get_single(msg.0) } } /// Update a device relay #[derive(Message)] #[rtype(result = "anyhow::Result<()>")] pub struct UpdateDeviceRelay(pub DeviceRelay); impl Handler for EnergyActor { type Result = anyhow::Result<()>; fn handle(&mut self, msg: UpdateDeviceRelay, _ctx: &mut Context) -> Self::Result { self.devices.relay_update(msg.0) } } /// Delete a device relay #[derive(Message)] #[rtype(result = "anyhow::Result<()>")] pub struct DeleteDeviceRelay(pub DeviceRelayID); impl Handler for EnergyActor { type Result = anyhow::Result<()>; fn handle(&mut self, msg: DeleteDeviceRelay, _ctx: &mut Context) -> Self::Result { self.devices.relay_delete(msg.0) } } #[derive(serde::Serialize)] pub struct RelaySyncStatus { enabled: bool, } /// Synchronize a device #[derive(Message)] #[rtype(result = "anyhow::Result>")] pub struct SynchronizeDevice(pub DeviceId, pub DeviceInfo); 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(); // TODO : implement real code let mut v = vec![]; for i in 0..msg.1.max_relays { v.push(RelaySyncStatus { enabled: i % 2 == 0, }); } Ok(v) } } #[derive(serde::Serialize)] pub struct ResDevState { id: DeviceId, last_ping: u64, online: bool, } /// Get the state of devices #[derive(Message)] #[rtype(result = "Vec")] pub struct GetDevicesState; impl Handler for EnergyActor { type Result = Vec; fn handle(&mut self, _msg: GetDevicesState, _ctx: &mut Context) -> Self::Result { self.devices .full_list() .into_iter() .map(|d| { let s = self.device_state(&d.id); ResDevState { id: d.id, last_ping: time_secs() - s.last_ping, online: s.is_online(), } }) .collect() } }