use std::sync::Arc; use std::time::{Duration, Instant}; use actix::{Actor, Context, Handler, Message}; use emgauwa_common::constants; use emgauwa_common::drivers::RelayDriver; use emgauwa_common::errors::EmgauwaError; use emgauwa_common::models::Controller; use emgauwa_common::types::RelayStates; use futures::executor::block_on; use sqlx::{Pool, Sqlite}; use tokio::sync::Notify; use crate::settings::Settings; #[derive(Message)] #[rtype(result = "Result<(), EmgauwaError>")] pub struct Reload {} #[derive(Message)] #[rtype(result = "()")] pub struct UpdateRelayStates { pub relay_states: RelayStates, } #[derive(Message)] #[rtype(result = "Result<(), EmgauwaError>")] pub struct RelayPulse { pub relay_number: i64, pub duration: Option<u32>, } #[derive(Message)] #[rtype(result = "Controller")] pub struct GetThis {} #[derive(Message)] #[rtype(result = "Arc<Notify>")] pub struct GetControllerNotifier {} #[derive(Message)] #[rtype(result = "Arc<Notify>")] pub struct GetRelayNotifier {} pub struct AppState { pub pool: Pool<Sqlite>, pub this: Controller, pub settings: Settings, pub drivers: Vec<Box<dyn RelayDriver>>, pub controller_notifier: Arc<Notify>, pub relay_notifier: Arc<Notify>, } impl AppState { pub fn new( pool: Pool<Sqlite>, this: Controller, settings: Settings, drivers: Vec<Box<dyn RelayDriver>>, ) -> AppState { AppState { pool, this, settings, drivers, controller_notifier: Arc::new(Notify::new()), relay_notifier: Arc::new(Notify::new()), } } pub fn notify_controller_change(&self) { self.controller_notifier.notify_one(); } pub fn notify_relay_change(&self) { self.relay_notifier.notify_one(); } } impl Actor for AppState { type Context = Context<Self>; } impl Handler<Reload> for AppState { type Result = Result<(), EmgauwaError>; fn handle(&mut self, _msg: Reload, _ctx: &mut Self::Context) -> Self::Result { log::debug!("Reloading controller"); let mut pool_conn = block_on(self.pool.acquire())?; self.this.reload(&mut pool_conn)?; self.notify_controller_change(); Ok(()) } } impl Handler<UpdateRelayStates> for AppState { type Result = (); fn handle(&mut self, msg: UpdateRelayStates, _ctx: &mut Self::Context) -> Self::Result { self.this.apply_relay_states(&msg.relay_states); self.drivers .iter_mut() .zip(msg.relay_states.iter()) .for_each(|(driver, state)| { if let Err(e) = driver.set(state.unwrap_or(false)) { log::error!("Error setting relay: {}", e); } }); self.notify_relay_change(); } } impl Handler<RelayPulse> for AppState { type Result = Result<(), EmgauwaError>; fn handle(&mut self, msg: RelayPulse, _ctx: &mut Self::Context) -> Self::Result { let relay_num = msg.relay_number; let duration = Duration::from_secs( match msg.duration { None => { self.settings .get_relay(relay_num) .ok_or(EmgauwaError::Other(String::from( "Relay not found in settings", )))? .pulse } Some(dur) => Some(dur as u64), } .unwrap_or(constants::RELAY_PULSE_DURATION), ); let now = Instant::now(); let until = now + duration; self.this.relay_pulse(relay_num, until)?; log::debug!( "Pulsing relay {} for {} seconds until {:?}", relay_num, duration.as_secs(), until ); Ok(()) } } impl Handler<GetThis> for AppState { type Result = Controller; fn handle(&mut self, _msg: GetThis, _ctx: &mut Self::Context) -> Self::Result { self.this.clone() } } impl Handler<GetControllerNotifier> for AppState { type Result = Arc<Notify>; fn handle(&mut self, _msg: GetControllerNotifier, _ctx: &mut Self::Context) -> Self::Result { Arc::clone(&self.controller_notifier) } } impl Handler<GetRelayNotifier> for AppState { type Result = Arc<Notify>; fn handle(&mut self, _msg: GetRelayNotifier, _ctx: &mut Self::Context) -> Self::Result { Arc::clone(&self.relay_notifier) } }