use std::{error::Error, u8}; use rocket::{ serde::{ json::{serde_json::json, Json}, Deserialize, Serialize, }, State, }; use crate::{DBPool, utils::{get_error_message, create_message, get_conn, prepare_statement}}; use crate::{ utils::{ApiResponse, ApiResponseActiveList}, AReceiver, ASender, Action, DBConn, RenderMessage, }; #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(crate = "rocket::serde")] #[serde(rename_all = "camelCase")] pub struct LigthSetting { start: Option, end: Option, tags: Option, r: u8, b: u8, g: u8, } impl LigthSetting { pub fn get_start(&self) -> Option { self.start } pub fn get_end(&self) -> Option { self.end } pub fn get_tags(&self) -> Option { if self.tags.as_ref().is_some() { return Some(self.tags.as_ref().unwrap().to_string()); } None } pub fn get_r(&self) -> u8 { self.r } pub fn get_b(&self) -> u8 { self.b } pub fn get_g(&self) -> u8 { self.g } } #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(crate = "rocket::serde")] #[serde(rename_all = "camelCase")] pub struct KeyFrame { duration: u32, settings: Vec, } impl KeyFrame { pub fn get_settings(&self) -> Vec { self.settings.clone() } pub fn get_duration(&self) -> u32 { self.duration } } #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(crate = "rocket::serde")] #[serde(rename_all = "camelCase")] pub struct Animation { key_frames: Vec, priority: Option, name: String, repeat: bool, } impl Animation { pub fn get_frames(&self) -> Vec { self.key_frames.clone() } pub fn get_name(&self) -> String { self.name.to_string() } pub fn get_priority(&self) -> u32 { if self.priority.is_none() { 0 } else { self.priority.unwrap() } } pub fn get_repeat(&self) -> bool { self.repeat } } #[get("/animations")] pub async fn get_animations(db: &State) -> String { let conn = get_conn(db); if let Err(e) = conn { return e; } let conn = conn.unwrap(); let stmt = prepare_statement(&conn, "select id,priority,name,repeat from animation;"); if let Err(e) = stmt { return e; } let mut stmt = stmt.unwrap(); #[derive(Deserialize, Serialize)] #[serde(crate = "rocket::serde")] #[serde(rename_all = "camelCase")] struct Data { id: u32, priority: u32, name: String, repeat: bool, } let animations = stmt.query_map([], |row| { let repeat: u32 = row.get(3)?; Ok(Data { id: row.get(0)?, priority: row.get(1)?, name: row.get(2)?, repeat: 1 == repeat, }) }); if animations.is_err() || animations.as_ref().ok().is_none() { return json!(ApiResponse {code: 500, message: "Clould not get data of db".to_string()}).to_string(); } let animations = animations.unwrap(); let data: Vec = animations.into_iter().map(|a| a.unwrap()).collect(); json!(data).to_string() } // Add animation #[post("/animation", data = "")] pub async fn animation(data: Json, db: &State) -> String { if data.key_frames.is_empty() { return json!(ApiResponse { code: 400, message: "KeyFrame are nessesary".to_string() }) .to_string(); } if data.name.is_empty() { return json!(ApiResponse { code: 400, message: "Name must not be empty".to_string() }) .to_string(); } for key_frame in data.key_frames.iter() { if key_frame.settings.is_empty() { return json!(ApiResponse { code: 400, message: "keyFrames.settings can not be empty".to_string() }) .to_string(); } if key_frame.duration == 0 { return json!(ApiResponse { code: 400, message: "keyFrames.duration can not be 0".to_string() }) .to_string(); } for setting in key_frame.settings.iter() { if setting.tags.is_none() && (setting.start.is_none() || setting.end.is_none()) { return json!(ApiResponse { code: 400, message: "Either tags or start end pair is needed".to_string() }) .to_string(); } if !(setting.start.is_none() && setting.end.is_none()) && (setting.start.is_none() || setting.end.is_none()) { return json!(ApiResponse { code: 400, message: "Both start and end need to be filled".to_string() }) .to_string(); } } } let db_status = add_animation_to_db(data, db); if db_status.is_err() || db_status.as_ref().ok().is_none() { return json!(ApiResponse { code: 500, message: format!("Error setting the animation. Err: {:?}", db_status.err()).to_string() }) .to_string(); } let db_status = db_status.ok().unwrap(); if !db_status.is_empty() { return db_status; } json!(ApiResponse { code: 200, message: "Configuration was successful".to_string() }) .to_string() } fn get_animation_from_name(conn: &DBConn, name: String) -> Result, Box> { let mut stmt = conn.prepare("SELECT id FROM animation WHERE name=?1")?; let next = stmt.query_map([name], |row| row.get(0))?.next(); if next.is_none() { return Ok(None); } Ok(next.unwrap()?) } fn get_keyframe_from_index( conn: &DBConn, animation: u32, index: u32, ) -> Result, Box> { let mut stmt = conn.prepare("SELECT id FROM keyframe WHERE animation=?1 AND i=?2")?; let next = stmt.query_map([animation, index], |row| row.get(0))?.next(); if next.is_none() { return Ok(None); } Ok(next.unwrap()?) } fn add_animation_to_db( data: Json, pool: &State, ) -> Result> { let priority = if data.priority.is_some() { data.priority.unwrap() } else { 0 }; let conn = pool.get()?; println!("g: {}", data.name); let name = get_animation_from_name(&conn, data.name.to_string())?; println!("t: {:?}", name); if name.is_some() { return Ok(json!(ApiResponse { code: 500, message: "Animation with that name is already exists.".to_string() }) .to_string()); } conn.execute( "INSERT INTO animation (priority, name, repeat) VALUES (?1, ?2, ?3);", (priority, data.name.to_string(), data.repeat), )?; let name = get_animation_from_name(&conn, data.name.to_string())?; if name.is_none() { return Ok(json!(ApiResponse { code: 500, message: "Failed to create animation.".to_string() }) .to_string()); } let animation_id = name.unwrap(); let mut i = 0; for key_frame in data.key_frames.clone() { conn.execute( "INSERT INTO keyframe (duration, animation, i) VALUES (?1, ?2, ?3);", (key_frame.duration, animation_id, i), )?; let keyframe = get_keyframe_from_index(&conn, animation_id, i)?; if keyframe.is_none() { return Ok(json!(ApiResponse { code: 500, message: "Failed to create animation.".to_string() }) .to_string()); } let keyframe = keyframe.unwrap(); for setting in key_frame.settings { conn.execute("INSERT INTO lightsetting (start, end, tags, r, b, g, keyframe) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);", (setting.start, setting.end, setting.tags, setting.r, setting.b, setting.g, keyframe))?; } i += 1; } Ok("".to_string()) } /** * * Stop animations * */ #[get("/stop/")] pub async fn stop_animation(name: &str, action: &State>) -> String { let r = action.lock().unwrap().send(Action::Stop(name.to_string())); if r.is_err() || r.ok().is_none() { return get_error_message("Something went wrong"); } create_message(200, "Configuration was successful") } /** * * Start animations * */ #[get("/start/")] pub async fn start_animation( name: &str, action: &State>, db: &State, ) -> String { let animation = get_animation_or_string_error(name, db); if let Err(animation) = animation { return animation; } let animation = animation.unwrap(); let r = action.lock().unwrap().send(Action::Start(animation)); if r.is_err() || r.as_ref().ok().is_none() { return get_error_message("Probelms with the sender"); } create_message(200, "Started animation") } #[get("/animation/")] pub async fn get_animation_by_name(name: &str, db: &State) -> String { let animation = get_animation_or_string_error(name, db); if let Err(animation) = animation { return animation; } let animation = animation.unwrap(); json!(animation).to_string() } /** * * Clear animations * */ #[get("/clear/")] pub async fn clear( name: &str, db: &State, action: &State>, ) -> String { let action = action.lock().unwrap(); let r = action.send(Action::Clear); if r.is_err() || r.ok().is_none() { return get_error_message("Something went wrong"); } let animation = get_animation_or_string_error(name, db); if let Err(error) = animation { return error; } let animation = animation.unwrap(); let r = action.send(Action::Start(animation)); if r.is_err() || r.as_ref().ok().is_none() { return get_error_message("Probelms with the db"); } create_message(200, "Started animation") } #[get("/clearall")] pub async fn clear_all(action: &State>) -> String { let r = action.lock().unwrap().send(Action::Clear); if r.is_err() || r.ok().is_none() { return get_error_message("Something went wrong"); } create_message(200, "Cleared all the animations") } pub fn get_animation(name: &str, db: &State) -> Result, Box> { let conn = db.get()?; let mut stmt = conn.prepare("SELECT id, priority, name, repeat FROM animation WHERE name=?1;")?; struct AnimationId { animation: Animation, id: u8, } let next = stmt .query_map([name], |row| { Ok(AnimationId { animation: Animation { key_frames: Vec::new(), priority: Some(row.get(1)?), name: row.get(2)?, repeat: row.get(3)?, }, id: row.get(0)?, }) })? .next(); if next.is_none() { return Ok(None); } let mut animation = next.unwrap()?; struct KeyFrameId { keyframe: KeyFrame, id: u8, } let mut stmt = conn.prepare("SELECT id, duration from keyframe where animation=?1 order by i asc")?; let mut map = stmt.query_map([animation.id], |row| { Ok(KeyFrameId { id: row.get(0)?, keyframe: KeyFrame { duration: row.get(1)?, settings: Vec::new(), }, }) })?; while let Some(key_frame_id) = map.next() { let mut key_frame_id = key_frame_id?; let mut stmt = conn.prepare("SELECT start, end, tags, r, b, g from lightsetting where keyframe=?1")?; let mut light_setting_map = stmt.query_map([key_frame_id.id], |row| { Ok(LigthSetting { start: row.get(0)?, end: row.get(1)?, tags: row.get(2)?, r: row.get(3)?, b: row.get(4)?, g: row.get(5)?, }) })?; while let Some(light_setting) = light_setting_map.next() { key_frame_id.keyframe.settings.push(light_setting?); } animation.animation.key_frames.push(key_frame_id.keyframe); } Ok(Some(animation.animation)) } fn get_animation_or_string_error(name: &str, db: &State) -> Result { let animation = get_animation(name, db); if animation.is_err() || animation.as_ref().ok().is_none() { return Err(json!(ApiResponse { code: 500, message: format!("Probelms with the db: {:?}", animation.err()).to_string() }) .to_string()); } let animation = animation.ok().unwrap(); if animation.is_none() { return Err(json!(ApiResponse { code: 404, message: "Animation not found".to_string() }) .to_string()); } Ok(animation.unwrap()) } /** * * Delete animations * */ #[get("/delete/")] pub async fn delete(name: &str, db: &State) -> String { let animation = remove_animation(name, db); if animation.is_err() || animation.as_ref().ok().is_none() { return json!(ApiResponse { code: 500, message: format!("Probelms with the db: {:?}", animation.err()).to_string() }) .to_string(); } json!(ApiResponse { code: 200, message: "Deleted animation".to_string() }) .to_string() } pub fn remove_animation(name: &str, db: &State) -> Result<(), Box> { let conn = db.get()?; conn.execute("delete from lightsetting where id in (select l.id from lightsetting as l left join keyframe as k on l.keyframe = k.id left join animation as a on k.animation = a.id where a.name=?1);", [name])?; conn.execute("delete from keyframe where id in (select k.id from keyframe as k left join animation as a on k.animation = a.id where a.name=?1);", [name])?; conn.execute("delete from animation where name=?1;", [name])?; Ok(()) } #[get("/active")] pub async fn get_active( action: &State>, messages: &State>, ) -> String { println!("Getting active"); let send = action.lock().unwrap().send(Action::GetActiveList); if send.is_err() || send.ok().is_none() { return json!(ApiResponse { code: 500, message: "Failed to get list".to_string() }) .to_string(); } let data = messages.lock().unwrap().recv(); if data.is_err() || data.as_ref().ok().is_none() { return json!(ApiResponse { code: 500, message: "Failed to get list".to_string() }) .to_string(); } if let RenderMessage::ActiveList(list) = data.ok().unwrap() { return json!(ApiResponseActiveList { code: 200, active_animations: list }) .to_string(); } return json!(ApiResponse { code: 500, message: "Failed to get list".to_string() }) .to_string(); } /** * * get animation * */ #[get("/get/")] pub async fn get_animation_request(name: &str, db: &State) -> String { let animation = get_animation(name, db); if animation.is_err() { return json!(ApiResponse { code: 500, message: "Failed to get animation".to_string() }) .to_string(); } let animation = animation.ok(); if let Some(animation) = animation { return json!(animation).to_string(); } else { return json!(ApiResponse { code: 500, message: "Animation not found".to_string() }) .to_string(); } } /** * * Start animations * */ #[get("/toggle/")] pub async fn toggle_animation( name: &str, action: &State>, messages: &State>, db: &State, ) -> String { let animation = get_animation_or_string_error(name, db); if let Err(error) = animation { return error; } let animation = animation.unwrap(); let action = action.lock().unwrap(); let send = action.send(Action::GetActiveList); if send.is_err() || send.ok().is_none() { return get_error_message("Failed to get list"); } let data = messages.lock().unwrap().recv(); if data.is_err() || data.as_ref().ok().is_none() { return get_error_message("Failed to get list"); } if let RenderMessage::ActiveList(list) = data.ok().unwrap() { if list.contains(&name.to_string()) { // Disable let r = action.send(Action::Stop(name.to_string())); if r.is_err() || r.ok().is_none() { return get_error_message("Something went wrong"); } return create_message(200, "Configuration was successful"); } else { // Enable let r = action.send(Action::Start(animation)); if r.is_err() || r.as_ref().ok().is_none() { return get_error_message("Probelms with the sender"); } return create_message(200, "Started animation"); } } return get_error_message("Failed to get list"); } /** * * Start animations * */ #[get("/cleartoggle/")] pub async fn cleartoggle_animation( name: &str, action: &State>, messages: &State>, db: &State, ) -> String { let r = action.lock().unwrap().send(Action::Clear); if r.is_err() || r.ok().is_none() { return get_error_message("Something went wrong"); } let animation = get_animation_or_string_error(name, db); if let Err(error) = animation { return error; } let animation = animation.unwrap(); let action = action.lock().unwrap(); let send = action.send(Action::GetActiveList); if send.is_err() || send.ok().is_none() { return get_error_message("Failed to get list"); } let data = messages.lock().unwrap().recv(); if data.is_err() || data.as_ref().ok().is_none() { return get_error_message("Failed to get list"); } if let RenderMessage::ActiveList(list) = data.ok().unwrap() { if list.contains(&name.to_string()) { // Disable let r = action.send(Action::Stop(name.to_string())); if r.is_err() || r.ok().is_none() { return get_error_message("Something went wrong"); } return create_message(200, "Configuration was successful"); } else { // Enable let r = action.send(Action::Start(animation)); if r.is_err() || r.as_ref().ok().is_none() { return get_error_message("Probelms with the sender"); } return create_message(200, "Started animation"); } } return get_error_message("Failed to get list"); }