#[macro_use] extern crate rocket; //use std::{thread, time::Duration, sync::Arc, io}; use ::std::sync::Arc; mod animations; mod configure; mod render; mod utils; use animations::Animation; use r2d2::PooledConnection; use utils::ApiResponse; use rocket::{serde::json::serde_json::json, State}; use r2d2_sqlite::SqliteConnectionManager; //use std::sync::atomic::AtomicBool; use std::{ error::Error, sync::{ mpsc::{self, Receiver, Sender}, Mutex, }, }; pub type DBPool = Arc>; pub type DBConn = PooledConnection; pub type GResult = Result<(), Box>; pub type ASender = Arc>>; #[get("/")] fn index() -> &'static str { "Hello World" } pub enum Action { Reload, Stop, Clear, } #[get("/quit")] async fn quit(shutdown: rocket::Shutdown, action: &State>) -> String { println!("Got Shutdown"); let send = action.lock().unwrap().send(Action::Stop); if send.is_err() || send.ok().is_none() { return json!(ApiResponse { code: 500, message: "Failed to stop".to_string() }) .to_string(); } shutdown.notify(); json!(ApiResponse { code: 200, message: "Stoped".to_string() }) .to_string() } #[get("/reload")] async fn reload(action: &State>) -> String { println!("Got Shutdown"); let send = action.lock().unwrap().send(Action::Reload); if send.is_err() || send.ok().is_none() { return json!(ApiResponse { code: 500, message: "Failed to reload".to_string() }) .to_string(); } json!(ApiResponse { code: 200, message: "Reloaded".to_string() }) .to_string() } fn ligth_controll( pool: DBPool, action: Receiver, start_ani: Receiver, stop_ani: Receiver, ) { println!("Data loop started"); let render = render::Render::new(pool.clone()); if render.is_err() || render.as_ref().ok().is_none() { println!("something every wrong {:?}", render.err()); return; } let mut render = render.ok().unwrap(); 'mainloop: loop { let action = action.try_recv(); if action.is_ok() { match action.ok().unwrap() { Action::Reload => { let result = render.load_config(); if result.is_err() || result.as_ref().ok().is_none() { println!("Ehh failed to load config i guess: {:?}", result.err()); } } Action::Clear => { render.blank(); } Action::Stop => { render.blank(); render.im_render(); break 'mainloop; } } } let ani = start_ani.try_recv(); if ani.is_ok() { println!("Added animation"); //TODO render.add_animation(ani.ok().unwrap()); //ani.ok().unwrap(). } let ani = stop_ani.try_recv(); if ani.is_ok() { render.remove_animation(ani.ok().unwrap()); } render.render(); } println!("stoped main loop"); } const DATAVERSION: &str = "0.0.10"; fn setup_database(pool: DBPool) -> GResult { let conn = pool.get()?; println!("Trying to get meta data"); let stmt = conn.prepare("SELECT version from meta"); if stmt.is_err() || stmt.as_ref().ok().is_none() { return create_database(pool); } let mut stmt = stmt?; struct Data { version: String, } let version_iter = stmt.query_map([], |row| { Ok(Data { version: row.get(0)?, }) }); println!("Process meta"); if version_iter.is_err() || version_iter.as_ref().ok().is_none() { create_database(pool)?; } else { let version_iter = version_iter.ok().unwrap(); let mut recreate = true; for data in version_iter { let version = data.unwrap().version; println!("Found version {}", version); if !DATAVERSION.to_string().eq(&version) { println!("Mismatched versions recreating database"); recreate = true; break; } recreate = false; } if recreate { create_database(pool)?; } } Ok(()) } fn create_database(pool: DBPool) -> GResult { let conn = pool.get()?; println!("Createing the database"); println!("Createing new meta table. With version {}", DATAVERSION); println!("Drop old meta table"); conn.execute("DROP TABLE IF EXISTS meta;", ())?; // Create the database println!("create new meta table"); conn.execute( "CREATE TABLE meta ( id INTEGER PRIMARY KEY, version TEXT, configured Boolean );", (), )?; println!("insert data"); conn.execute( "INSERT INTO meta (version, configured) VALUES (?1, ?2);", (&DATAVERSION, false), )?; println!( "Createing new configuration table. With version {}", DATAVERSION ); println!("Drop configuration table"); conn.execute("DROP TABLE IF EXISTS configuration", ())?; println!("Create new configuration table"); conn.execute( "CREATE TABLE configuration ( id INTEGER PRIMARY KEY, ledcount INTEGER, directionX INTEGER, directionY INTEGER, directionZ INTEGER, tags VARCHAR(255) );", (), )?; println!( "Createing new Animation table. With version {}", DATAVERSION ); println!("Drop Animation table and references"); conn.execute("DROP TABLE IF EXISTS lightsetting", ())?; conn.execute("DROP TABLE IF EXISTS keyframe", ())?; conn.execute("DROP TABLE IF EXISTS animation", ())?; println!("Create new animation table"); conn.execute( "CREATE TABLE animation ( id INTEGER PRIMARY KEY, priority INTEGER, name VARCHAR(255), repeat BOOLEAN );", (), )?; println!( "Createing new key_frame table. With version {}", DATAVERSION ); println!("Drop key_frame table"); println!("Create new key_frame table"); conn.execute( "CREATE TABLE keyframe ( id INTEGER PRIMARY KEY, i INTEGER, duration INTEGER, animation INTEGER, FOREIGN KEY(animation) REFERENCES animation(id) ON DELETE CASCADE );", (), )?; println!( "Createing new LigthSetting table. With version {}", DATAVERSION ); println!("Drop LigthSetting table"); println!("Create new light_setting table"); conn.execute( "CREATE TABLE lightsetting ( id INTEGER PRIMARY KEY, start INTEGER, end INTEGER, tags VARCHAR(255), r INTEGER, b INTEGER, g INTEGER, keyframe INTEGER, FOREIGN KEY(keyframe) REFERENCES keyframe(id) ON DELETE CASCADE );", (), )?; Ok(()) } #[rocket::main] async fn main() -> GResult { println!("Program start"); println!("Try to connect db"); let sqlite_conn_manager = SqliteConnectionManager::file("ligths.db"); let conn_pool = r2d2::Pool::new(sqlite_conn_manager).expect("Failed to create pool"); let conn_pool_arc = Arc::new(conn_pool); { let pool = conn_pool_arc.clone(); let dd = setup_database(pool); println!("{:?}", dd); dd.ok().unwrap(); } let (action_sender, action_receiver): (Sender, Receiver) = mpsc::channel(); let (start_animation_sender, start_animation_receiver): ( Sender, Receiver, ) = mpsc::channel(); let (stop_animation_sender, stop_animation_receiver): (Sender, Receiver) = mpsc::channel(); let pool_clone = conn_pool_arc.clone(); let ligths_controll = std::thread::spawn(move || { ligth_controll( pool_clone, action_receiver, start_animation_receiver, stop_animation_receiver, ); }); let cfg = rocket::Config::figment() .merge(("port", 3000)) .merge(("address", "0.0.0.0")); let _rocket = rocket::custom(cfg) .mount( "/", routes![ index, quit, reload, configure::configure, animations::animation, animations::start_animation, animations::stop_animation, animations::clear, animations::delete ], ) .manage(conn_pool_arc.clone()) .manage(Arc::new(Mutex::new(action_sender))) .manage(Arc::new(Mutex::new(start_animation_sender))) .manage(Arc::new(Mutex::new(stop_animation_sender))) .launch() .await?; ligths_controll.join().unwrap(); Ok(()) }