408 lines
12 KiB
Rust

use crate::app_config::{AppConfig, ConsumptionHistoryType};
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::energy::consumption_cache::ConsumptionCache;
use crate::energy::consumption_history_file::ConsumptionHistoryFile;
use crate::energy::engine::EnergyEngine;
use crate::utils::time_utils::time_secs;
use actix::prelude::*;
use openssl::x509::X509Req;
use std::time::Duration;
pub struct EnergyActor {
consumption_cache: ConsumptionCache,
devices: DevicesList,
engine: EnergyEngine,
last_engine_refresh: u64,
}
impl EnergyActor {
pub async fn new() -> anyhow::Result<Self> {
let consumption_cache_size =
AppConfig::get().refresh_interval / AppConfig::get().energy_fetch_interval;
let curr_consumption = consumption::get_curr_consumption().await?;
let mut consumption_cache = ConsumptionCache::new(consumption_cache_size as usize);
consumption_cache.add_value(curr_consumption);
if consumption_cache_size < 1 {
panic!("Energy fetch interval must be equal or smaller than refresh interval!");
}
Ok(Self {
consumption_cache,
devices: DevicesList::load()?,
engine: EnergyEngine::default(),
last_engine_refresh: 0,
})
}
async fn refresh(&mut self) -> anyhow::Result<()> {
// Refresh energy
let latest_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
});
self.consumption_cache.add_value(latest_consumption);
let devices_list = self.devices.full_list_ref();
let mut history =
ConsumptionHistoryFile::open(time_secs(), ConsumptionHistoryType::GridConsumption)?;
history.set_consumption(time_secs(), latest_consumption)?;
history.save()?;
let mut relays_consumption =
ConsumptionHistoryFile::open(time_secs(), ConsumptionHistoryType::RelayConsumption)?;
relays_consumption.set_consumption(
time_secs(),
self.engine.sum_relays_consumption(&devices_list) as EnergyConsumption,
)?;
relays_consumption.save()?;
if self.last_engine_refresh + AppConfig::get().refresh_interval > time_secs() {
return Ok(());
}
self.last_engine_refresh = time_secs();
self.engine
.refresh(self.consumption_cache.median_value(), &devices_list);
self.engine.persist_relays_state(&devices_list)?;
Ok(())
}
}
impl Actor for EnergyActor {
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
log::info!("Energy actor successfully started!");
ctx.run_interval(
Duration::from_secs(AppConfig::get().energy_fetch_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<EnergyActor>;
/// Get current consumption
#[derive(Message)]
#[rtype(result = "EnergyConsumption")]
pub struct GetCurrConsumption;
impl Handler<GetCurrConsumption> for EnergyActor {
type Result = EnergyConsumption;
fn handle(&mut self, _msg: GetCurrConsumption, _ctx: &mut Context<Self>) -> Self::Result {
self.consumption_cache.median_value()
}
}
/// Get relays consumption
#[derive(Message)]
#[rtype(result = "usize")]
pub struct RelaysConsumption;
impl Handler<RelaysConsumption> for EnergyActor {
type Result = usize;
fn handle(&mut self, _msg: RelaysConsumption, _ctx: &mut Context<Self>) -> Self::Result {
self.engine
.sum_relays_consumption(&self.devices.full_list_ref())
}
}
/// Check if device exists
#[derive(Message)]
#[rtype(result = "bool")]
pub struct CheckDeviceExists(pub DeviceId);
impl Handler<CheckDeviceExists> for EnergyActor {
type Result = bool;
fn handle(&mut self, msg: CheckDeviceExists, _ctx: &mut Context<Self>) -> 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<EnrollDevice> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: EnrollDevice, _ctx: &mut Context<Self>) -> 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<ValidateDevice> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: ValidateDevice, _ctx: &mut Context<Self>) -> 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<UpdateDeviceGeneralInfo> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: UpdateDeviceGeneralInfo, _ctx: &mut Context<Self>) -> 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<DeleteDevice> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: DeleteDevice, _ctx: &mut Context<Self>) -> Self::Result {
log::info!("Requested to delete device {:?}...", &msg.0);
let Some(device) = self.devices.get_single(&msg.0) else {
log::warn!("Requested to delete non-existent device!");
return Ok(());
};
// Delete device relays
for relay in device.relays {
self.devices.relay_delete(relay.id)?;
}
self.devices.delete(&msg.0)?;
Ok(())
}
}
/// Get the list of devices
#[derive(Message)]
#[rtype(result = "Vec<Device>")]
pub struct GetDeviceLists;
impl Handler<GetDeviceLists> for EnergyActor {
type Result = Vec<Device>;
fn handle(&mut self, _msg: GetDeviceLists, _ctx: &mut Context<Self>) -> Self::Result {
self.devices.full_list()
}
}
/// Get the information about a single device
#[derive(Message)]
#[rtype(result = "Option<Device>")]
pub struct GetSingleDevice(pub DeviceId);
impl Handler<GetSingleDevice> for EnergyActor {
type Result = Option<Device>;
fn handle(&mut self, msg: GetSingleDevice, _ctx: &mut Context<Self>) -> Self::Result {
self.devices.get_single(&msg.0)
}
}
/// Get the full list of relays
#[derive(Message)]
#[rtype(result = "Vec<DeviceRelay>")]
pub struct GetRelaysList;
impl Handler<GetRelaysList> for EnergyActor {
type Result = Vec<DeviceRelay>;
fn handle(&mut self, _msg: GetRelaysList, _ctx: &mut Context<Self>) -> 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<CreateDeviceRelay> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: CreateDeviceRelay, _ctx: &mut Context<Self>) -> Self::Result {
self.devices.relay_create(&msg.0, msg.1)
}
}
/// Get the information about a single relay
#[derive(Message)]
#[rtype(result = "Option<DeviceRelay>")]
pub struct GetSingleRelay(pub DeviceRelayID);
impl Handler<GetSingleRelay> for EnergyActor {
type Result = Option<DeviceRelay>;
fn handle(&mut self, msg: GetSingleRelay, _ctx: &mut Context<Self>) -> 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<UpdateDeviceRelay> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: UpdateDeviceRelay, _ctx: &mut Context<Self>) -> 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<DeleteDeviceRelay> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: DeleteDeviceRelay, _ctx: &mut Context<Self>) -> 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<Vec<RelaySyncStatus>>")]
pub struct SynchronizeDevice(pub DeviceId, pub DeviceInfo);
impl Handler<SynchronizeDevice> for EnergyActor {
type Result = anyhow::Result<Vec<RelaySyncStatus>>;
fn handle(&mut self, msg: SynchronizeDevice, _ctx: &mut Context<Self>) -> Self::Result {
self.devices.synchronise_dev_info(&msg.0, msg.1.clone())?;
self.engine.device_state(&msg.0).record_ping();
let Some(device) = self.devices.get_single(&msg.0) else {
return Ok(vec![]);
};
let mut v = vec![];
for d in &device.relays {
v.push(RelaySyncStatus {
enabled: self.engine.relay_state(d.id).is_on(),
});
}
Ok(v)
}
}
#[derive(serde::Serialize)]
pub struct ResDevState {
pub id: DeviceId,
last_ping: u64,
online: bool,
}
/// Get the state of devices
#[derive(Message)]
#[rtype(result = "Vec<ResDevState>")]
pub struct GetDevicesState;
impl Handler<GetDevicesState> for EnergyActor {
type Result = Vec<ResDevState>;
fn handle(&mut self, _msg: GetDevicesState, _ctx: &mut Context<Self>) -> Self::Result {
self.devices
.full_list()
.into_iter()
.map(|d| {
let s = self.engine.device_state(&d.id);
ResDevState {
id: d.id,
last_ping: time_secs() - s.last_ping,
online: s.is_online(),
}
})
.collect()
}
}
#[derive(serde::Serialize)]
pub struct ResRelayState {
pub id: DeviceRelayID,
on: bool,
r#for: usize,
}
/// Get the state of all relays
#[derive(Message)]
#[rtype(result = "Vec<ResRelayState>")]
pub struct GetAllRelaysState;
impl Handler<GetAllRelaysState> for EnergyActor {
type Result = Vec<ResRelayState>;
fn handle(&mut self, _msg: GetAllRelaysState, _ctx: &mut Context<Self>) -> Self::Result {
let mut list = vec![];
for d in &self.devices.relays_list() {
let state = self.engine.relay_state(d.id);
list.push(ResRelayState {
id: d.id,
on: state.is_on(),
r#for: state.state_for(),
})
}
list
}
}