Can set forced state to relays
This commit is contained in:
@@ -8,7 +8,7 @@ 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::energy::engine::{EnergyEngine, RelayForcedState};
|
||||
use crate::utils::time_utils::time_secs;
|
||||
use actix::prelude::*;
|
||||
use openssl::x509::X509Req;
|
||||
@@ -328,6 +328,19 @@ impl Handler<UpdateDeviceRelay> for EnergyActor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "anyhow::Result<()>")]
|
||||
pub struct SetRelayForcedState(pub DeviceRelayID, pub RelayForcedState);
|
||||
|
||||
impl Handler<SetRelayForcedState> for EnergyActor {
|
||||
type Result = anyhow::Result<()>;
|
||||
|
||||
fn handle(&mut self, msg: SetRelayForcedState, _ctx: &mut Context<Self>) -> Self::Result {
|
||||
self.engine.relay_state(msg.0).set_forced(msg.1);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a device relay
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "anyhow::Result<()>")]
|
||||
@@ -408,6 +421,7 @@ pub struct ResRelayState {
|
||||
pub id: DeviceRelayID,
|
||||
pub on: bool,
|
||||
pub r#for: usize,
|
||||
pub forced_state: RelayForcedState,
|
||||
}
|
||||
|
||||
/// Get the state of all relays
|
||||
@@ -427,6 +441,7 @@ impl Handler<GetAllRelaysState> for EnergyActor {
|
||||
id: d.id,
|
||||
on: state.is_on(),
|
||||
r#for: state.state_for(),
|
||||
forced_state: state.actual_forced_state(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,19 +25,56 @@ impl DeviceState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum RelayForcedState {
|
||||
#[default]
|
||||
None,
|
||||
Off {
|
||||
until: u64,
|
||||
},
|
||||
On {
|
||||
until: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct RelayState {
|
||||
on: bool,
|
||||
since: usize,
|
||||
forced_state: RelayForcedState,
|
||||
}
|
||||
|
||||
impl RelayState {
|
||||
/// Get actual forced state (returns None if state is expired)
|
||||
pub fn actual_forced_state(&self) -> RelayForcedState {
|
||||
match self.forced_state {
|
||||
RelayForcedState::Off { until } if until > time_secs() => {
|
||||
RelayForcedState::Off { until }
|
||||
}
|
||||
RelayForcedState::On { until } if until > time_secs() => RelayForcedState::On { until },
|
||||
_ => RelayForcedState::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_on(&self) -> bool {
|
||||
self.on
|
||||
let forced_state = self.actual_forced_state();
|
||||
(self.on || matches!(forced_state, RelayForcedState::On { .. }))
|
||||
&& !matches!(forced_state, RelayForcedState::Off { .. })
|
||||
}
|
||||
|
||||
fn is_off(&self) -> bool {
|
||||
!self.on
|
||||
!self.is_on()
|
||||
}
|
||||
|
||||
/// Check if relay state is enforced
|
||||
pub fn is_forced(&self) -> bool {
|
||||
self.actual_forced_state() != RelayForcedState::None
|
||||
}
|
||||
|
||||
pub fn set_forced(&mut self, s: RelayForcedState) {
|
||||
self.since = time_secs() as usize;
|
||||
self.forced_state = s;
|
||||
}
|
||||
|
||||
pub fn state_for(&self) -> usize {
|
||||
@@ -146,7 +183,11 @@ impl EnergyEngine {
|
||||
r.name,
|
||||
r.consumption,
|
||||
format!("{} / {}", r.minimal_downtime, r.minimal_uptime),
|
||||
status.is_on().to_string(),
|
||||
status.is_on().to_string()
|
||||
+ match status.is_forced() {
|
||||
true => " (Forced)",
|
||||
false => "",
|
||||
},
|
||||
status.since,
|
||||
match dev_online {
|
||||
true => "Online",
|
||||
@@ -192,19 +233,28 @@ impl EnergyEngine {
|
||||
|
||||
let mut new_relays_state = self.relays_state.clone();
|
||||
|
||||
// Forcefully turn off relays that belongs to offline devices
|
||||
// Forcefully turn off disabled relays
|
||||
for d in devices {
|
||||
if !self.device_state(&d.id).is_online() {
|
||||
for r in &d.relays {
|
||||
if !r.enabled || !d.enabled {
|
||||
new_relays_state.get_mut(&r.id).unwrap().on = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forcefully turn off disabled relays
|
||||
// Apply forced relays state
|
||||
for d in devices {
|
||||
for r in &d.relays {
|
||||
if !r.enabled || !d.enabled {
|
||||
if self.relay_state(r.id).is_forced() {
|
||||
new_relays_state.get_mut(&r.id).unwrap().on = self.relay_state(r.id).is_on();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forcefully turn off relays that belongs to offline devices
|
||||
for d in devices {
|
||||
if !self.device_state(&d.id).is_online() {
|
||||
for r in &d.relays {
|
||||
new_relays_state.get_mut(&r.id).unwrap().on = false;
|
||||
}
|
||||
}
|
||||
@@ -216,7 +266,9 @@ impl EnergyEngine {
|
||||
|
||||
for d in devices {
|
||||
for r in &d.relays {
|
||||
if new_relays_state.get(&r.id).unwrap().is_off() {
|
||||
if new_relays_state.get(&r.id).unwrap().is_off()
|
||||
|| new_relays_state.get(&r.id).unwrap().is_forced()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -240,7 +292,7 @@ impl EnergyEngine {
|
||||
for d in devices {
|
||||
for r in &d.relays {
|
||||
let state = new_relays_state.get(&r.id).unwrap();
|
||||
if state.is_off() {
|
||||
if state.is_off() || state.is_forced() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -271,7 +323,9 @@ impl EnergyEngine {
|
||||
continue;
|
||||
}
|
||||
|
||||
if new_relays_state.get(&r.id).unwrap().is_on() {
|
||||
if new_relays_state.get(&r.id).unwrap().is_on()
|
||||
|| new_relays_state.get(&r.id).unwrap().is_forced()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -298,7 +352,7 @@ impl EnergyEngine {
|
||||
}
|
||||
}
|
||||
|
||||
// Order relays
|
||||
// Order relays to select the ones with the most elevated priorities
|
||||
let mut ordered_relays = devices
|
||||
.iter()
|
||||
.filter(|d| self.device_state(&d.id).is_online() && d.enabled)
|
||||
@@ -308,10 +362,13 @@ impl EnergyEngine {
|
||||
ordered_relays.sort_by_key(|r| r.priority);
|
||||
ordered_relays.reverse();
|
||||
|
||||
// Select relays to start, starting with those with highest priorities
|
||||
loop {
|
||||
let mut changed = false;
|
||||
for relay in &ordered_relays {
|
||||
if new_relays_state.get(&relay.id).unwrap().is_on() {
|
||||
if new_relays_state.get(&relay.id).unwrap().is_on()
|
||||
|| new_relays_state.get(&relay.id).unwrap().is_forced()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -231,6 +231,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()>
|
||||
"/web_api/relay/{id}",
|
||||
web::put().to(relays_controller::update),
|
||||
)
|
||||
.route(
|
||||
"/web_api/relay/{id}/forced_state",
|
||||
web::put().to(relays_controller::set_forced_state),
|
||||
)
|
||||
.route(
|
||||
"/web_api/relay/{id}",
|
||||
web::delete().to(relays_controller::delete),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::devices::device::{DeviceId, DeviceRelay, DeviceRelayID};
|
||||
use crate::energy::energy_actor;
|
||||
use crate::energy::engine::RelayForcedState;
|
||||
use crate::server::WebEnergyActor;
|
||||
use crate::server::custom_error::HttpResult;
|
||||
use crate::utils::time_utils::time_secs;
|
||||
use actix_web::{HttpResponse, web};
|
||||
|
||||
/// Get the full list of relays
|
||||
@@ -85,6 +87,50 @@ pub async fn update(
|
||||
Ok(HttpResponse::Accepted().finish())
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum SetRelayForcedStateReq {
|
||||
#[default]
|
||||
None,
|
||||
Off {
|
||||
for_secs: u64,
|
||||
},
|
||||
On {
|
||||
for_secs: u64,
|
||||
},
|
||||
}
|
||||
|
||||
/// Set relay forced status
|
||||
pub async fn set_forced_state(
|
||||
actor: WebEnergyActor,
|
||||
req: web::Json<SetRelayForcedStateReq>,
|
||||
path: web::Path<RelayIDInPath>,
|
||||
) -> HttpResult {
|
||||
// Check if relay exists first
|
||||
let list = actor.send(energy_actor::GetAllRelaysState).await?;
|
||||
if !list.into_iter().any(|r| r.id == path.id) {
|
||||
return Ok(HttpResponse::NotFound().json("Relay not found!"));
|
||||
};
|
||||
|
||||
// Update relay forced state
|
||||
actor
|
||||
.send(energy_actor::SetRelayForcedState(
|
||||
path.id,
|
||||
match &req.0 {
|
||||
SetRelayForcedStateReq::None => RelayForcedState::None,
|
||||
SetRelayForcedStateReq::Off { for_secs } => RelayForcedState::Off {
|
||||
until: time_secs() + for_secs,
|
||||
},
|
||||
SetRelayForcedStateReq::On { for_secs } => RelayForcedState::On {
|
||||
until: time_secs() + for_secs,
|
||||
},
|
||||
},
|
||||
))
|
||||
.await??;
|
||||
|
||||
Ok(HttpResponse::Accepted().finish())
|
||||
}
|
||||
|
||||
/// Delete an existing relay
|
||||
pub async fn delete(actor: WebEnergyActor, path: web::Path<RelayIDInPath>) -> HttpResult {
|
||||
actor
|
||||
|
||||
Reference in New Issue
Block a user