use std::collections::HashMap; use actix::{Actor, Addr, Context, Handler, Message, Recipient}; use emgauwa_common::db::DbController; use emgauwa_common::errors::EmgauwaError; use emgauwa_common::models::{convert_db_list, Controller, Relay}; use emgauwa_common::types::{ControllerWsAction, EmgauwaUid, RelayStates}; use futures::executor::block_on; use sqlx::{Pool, Sqlite}; use crate::handlers::v1::ws::relays::{RelaysWs, SendRelays}; #[derive(Message)] #[rtype(result = "Result<(), EmgauwaError>")] pub struct DisconnectController { pub controller_uid: EmgauwaUid, } #[derive(Message)] #[rtype(result = "Result<(), EmgauwaError>")] pub struct ConnectController { pub address: Recipient, pub controller: Controller, } #[derive(Message)] #[rtype(result = "()")] pub struct UpdateRelayStates { pub controller_uid: EmgauwaUid, pub relay_states: RelayStates, } #[derive(Message)] #[rtype(result = "Result, EmgauwaError>")] pub struct GetRelays {} #[derive(Message)] #[rtype(result = "Result<(), EmgauwaError>")] pub struct Action { pub controller_uid: EmgauwaUid, pub action: ControllerWsAction, } #[derive(Message)] #[rtype(result = "()")] pub struct ConnectRelayClient { pub addr: Addr, } pub struct AppState { pub pool: Pool, pub connected_controllers: HashMap)>, pub connected_relay_clients: Vec>, } impl AppState { pub fn new(pool: Pool) -> AppState { AppState { pool, connected_controllers: HashMap::new(), connected_relay_clients: Vec::new(), } } fn get_relays(&self) -> Result, EmgauwaError> { let mut pool_conn = block_on(self.pool.acquire())?; let db_controllers = block_on(DbController::get_all(&mut pool_conn))?; let mut controllers: Vec = convert_db_list(&mut pool_conn, db_controllers)?; self.connected_controllers .iter() .for_each(|(uid, (connected_controller, _))| { if let Some(c) = controllers.iter_mut().find(|c| c.c.uid == *uid) { c.apply_relay_states(&connected_controller.get_relay_states()); } }); let mut relays: Vec = Vec::new(); controllers.iter().for_each(|c| { relays.extend(c.relays.clone()); }); Ok(relays) } fn notify_relay_clients(&mut self) { self.connected_relay_clients.retain(|addr| addr.connected()); match self.get_relays() { Ok(relays) => match serde_json::to_string(&relays) { Ok(json) => { self.connected_relay_clients.iter_mut().for_each(|addr| { let relays_json = json.clone(); addr.do_send(SendRelays { relays_json }); }); } Err(err) => { log::error!("Failed to serialize relays: {:?}", err); } }, Err(err) => { log::error!("Failed to get relays: {:?}", err); } }; } } impl Actor for AppState { type Context = Context; } impl Handler for AppState { type Result = Result<(), EmgauwaError>; fn handle(&mut self, msg: DisconnectController, _ctx: &mut Self::Context) -> Self::Result { let mut pool_conn = block_on(self.pool.acquire())?; if let Some((controller, address)) = self.connected_controllers.remove(&msg.controller_uid) { if let Err(err) = block_on(controller.c.update_active(&mut pool_conn, false)) { log::error!( "Failed to mark controller {} as inactive: {:?}", controller.c.uid, err ); } // TODO: why does the block_on(send()) version not return? The AppState will be stuck. //block_on(address.send(ControllerWsAction::Disconnect))??; address.do_send(ControllerWsAction::Disconnect); } Ok(()) } } impl Handler for AppState { type Result = Result<(), EmgauwaError>; fn handle(&mut self, msg: ConnectController, _ctx: &mut Self::Context) -> Self::Result { log::debug!("Connecting controller: {}", msg.controller.c.uid); self.connected_controllers .insert(msg.controller.c.uid.clone(), (msg.controller, msg.address)); Ok(()) } } impl Handler for AppState { type Result = (); fn handle(&mut self, msg: UpdateRelayStates, _ctx: &mut Self::Context) -> Self::Result { if let Some((controller, _)) = self.connected_controllers.get_mut(&msg.controller_uid) { controller.apply_relay_states(&msg.relay_states); } self.notify_relay_clients(); } } impl Handler for AppState { type Result = Result, EmgauwaError>; fn handle(&mut self, _msg: GetRelays, _ctx: &mut Self::Context) -> Self::Result { self.get_relays() } } impl Handler for AppState { type Result = Result<(), EmgauwaError>; fn handle(&mut self, msg: Action, _ctx: &mut Self::Context) -> Self::Result { log::debug!("Forwarding action: {:?}", msg.action); if let Some((_, address)) = self.connected_controllers.get(&msg.controller_uid) { // TODO: why does the block_on(send()) version not return? The AppState will be stuck. //block_on(address.send(msg.action))? address.do_send(msg.action); Ok(()) } else { Err(EmgauwaError::Connection(msg.controller_uid)) } } } impl Handler for AppState { type Result = (); fn handle(&mut self, msg: ConnectRelayClient, _ctx: &mut Self::Context) -> Self::Result { self.connected_relay_clients.push(msg.addr); } }