diff --git a/central_backend/src/constants.rs b/central_backend/src/constants.rs index 4bd652d..5c44b09 100644 --- a/central_backend/src/constants.rs +++ b/central_backend/src/constants.rs @@ -6,6 +6,9 @@ pub const SESSION_COOKIE_NAME: &str = "X-session-cookie"; /// Energy refresh operations interval pub const ENERGY_REFRESH_INTERVAL: Duration = Duration::from_secs(30); +/// Maximum time after a ping during which a device is considered "up" +pub const DEVICE_MAX_PING_TIME: u64 = 30; + /// Fallback value to use if production cannot be fetched pub const FALLBACK_PRODUCTION_VALUE: i32 = 5000; diff --git a/central_backend/src/energy/energy_actor.rs b/central_backend/src/energy/energy_actor.rs index 982cccf..7528601 100644 --- a/central_backend/src/energy/energy_actor.rs +++ b/central_backend/src/energy/energy_actor.rs @@ -5,12 +5,33 @@ use crate::devices::device::{ 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 { @@ -18,9 +39,20 @@ impl EnergyActor { 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() @@ -253,6 +285,9 @@ 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 { @@ -263,3 +298,34 @@ impl Handler for EnergyActor { 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() + } +} diff --git a/central_backend/src/server/servers.rs b/central_backend/src/server/servers.rs index fd3f7c8..1dd4b4d 100644 --- a/central_backend/src/server/servers.rs +++ b/central_backend/src/server/servers.rs @@ -144,6 +144,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()> "/web_api/devices/list_validated", web::get().to(devices_controller::list_validated), ) + .route( + "/web_api/devices/state", + web::get().to(devices_controller::devices_state), + ) .route( "/web_api/device/{id}", web::get().to(devices_controller::get_single), diff --git a/central_backend/src/server/web_api/devices_controller.rs b/central_backend/src/server/web_api/devices_controller.rs index fd65701..cb657c5 100644 --- a/central_backend/src/server/web_api/devices_controller.rs +++ b/central_backend/src/server/web_api/devices_controller.rs @@ -28,6 +28,13 @@ pub async fn list_validated(actor: WebEnergyActor) -> HttpResult { Ok(HttpResponse::Ok().json(list)) } +/// Get the state of devices +pub async fn devices_state(actor: WebEnergyActor) -> HttpResult { + let states = actor.send(energy_actor::GetDevicesState).await?; + + Ok(HttpResponse::Ok().json(states)) +} + #[derive(serde::Deserialize)] pub struct DeviceInPath { id: DeviceId,