use emgauwa_lib::constants::WEBSOCKET_RETRY_TIMEOUT; use emgauwa_lib::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule}; use emgauwa_lib::errors::DatabaseError; use emgauwa_lib::models::{Controller, FromDbModel}; use emgauwa_lib::types::{ControllerUid, ControllerWsAction}; use emgauwa_lib::{db, utils}; use futures::{SinkExt, StreamExt}; use sqlx::pool::PoolConnection; use sqlx::Sqlite; use tokio::time; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::protocol::Message; use tokio_tungstenite::tungstenite::Error; use utils::init_logging; use crate::relay_loop::run_relay_loop; use crate::settings::Settings; mod driver; mod relay_loop; mod settings; async fn create_this_controller( conn: &mut PoolConnection, settings: &Settings, ) -> DbController { DbController::create( conn, &ControllerUid::default(), &settings.name, settings.relays.len() as i64, ) .await .expect("Failed to create controller") } async fn create_this_relay( conn: &mut PoolConnection, this_controller: &DbController, settings_relay: &settings::Relay, ) -> Result { let relay = DbRelay::create( conn, &settings_relay.name, settings_relay.number.expect("Relay number is missing"), this_controller, ) .await?; let off = DbSchedule::get_off(conn).await?; for weekday in 0..7 { DbJunctionRelaySchedule::set_schedule(conn, &relay, &off, weekday).await?; } Ok(relay) } async fn run_websocket(this: Controller, url: &str) { match connect_async(url).await { Ok(connection) => { let (ws_stream, _) = connection; let (mut write, read) = ws_stream.split(); let ws_action = ControllerWsAction::Register(this.clone()); let ws_action_json = serde_json::to_string(&ws_action).expect("Failed to serialize action"); if let Err(err) = write.send(Message::text(ws_action_json)).await { log::error!("Failed to register at websocket: {}", err); return; } let read_handler = read.for_each(handle_message); read_handler.await; log::warn!("Lost connection to websocket"); } Err(err) => { log::warn!("Failed to connect to websocket: {}", err,); } } } #[tokio::main] async fn main() { let settings = settings::init(); init_logging(&settings.logging.level); let pool = db::init(&settings.database).await; let mut conn = pool .acquire() .await .expect("Failed to get database connection"); let db_controller = match DbController::get_all(&mut conn) .await .expect("Failed to get controller from database") .pop() { None => futures::executor::block_on(create_this_controller(&mut conn, &settings)), Some(c) => c, }; for relay in &settings.relays { if DbRelay::get_by_controller_and_num( &mut conn, &db_controller, relay.number.expect("Relay number is missing"), ) .await .expect("Failed to get relay from database") .is_none() { create_this_relay(&mut conn, &db_controller, relay) .await .expect("Failed to create schedule."); } } let db_controller = db_controller .update(&mut conn, &db_controller.name, settings.relays.len() as i64) .await .expect("Failed to update controller"); let this = Controller::from_db_model(&mut conn, db_controller) .expect("Failed to convert database models"); let url = format!( "ws://{}:{}/api/v1/ws/controllers", settings.core.host, settings.core.port ); tokio::spawn(run_relay_loop(settings)); loop { run_websocket(this.clone(), &url).await; log::info!( "Retrying to connect in {} seconds...", WEBSOCKET_RETRY_TIMEOUT.as_secs() ); time::sleep(WEBSOCKET_RETRY_TIMEOUT).await; } } async fn handle_message(message_result: Result) { match message_result { Ok(message) => { if let Message::Text(msg_text) = message { log::debug!("{}", msg_text) } } Err(err) => log::debug!("Error: {}", err), } }