added clearall
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Andre Henriques 2023-04-14 19:01:49 +01:00
parent f8e2aa91ea
commit df820fa7d8
2 changed files with 256 additions and 99 deletions

View File

@ -1,9 +1,15 @@
use std::{u8, error::Error}; use std::{error::Error, u8};
use rocket::{serde::{json::{Json, serde_json::json}, Deserialize, Serialize}, State}; use rocket::{
serde::{
json::{serde_json::json, Json},
Deserialize, Serialize,
},
State,
};
use crate::{utils::ApiResponse, DBConn, ASender, Action};
use crate::DBPool; use crate::DBPool;
use crate::{utils::ApiResponse, ASender, Action, DBConn};
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
@ -18,13 +24,13 @@ pub struct LigthSetting {
} }
impl LigthSetting { impl LigthSetting {
pub fn get_start(&self) -> Option<u32>{ pub fn get_start(&self) -> Option<u32> {
self.start self.start
} }
pub fn get_end(&self) -> Option<u32>{ pub fn get_end(&self) -> Option<u32> {
self.end self.end
} }
pub fn get_tags(&self) -> Option<String>{ pub fn get_tags(&self) -> Option<String> {
if self.tags.as_ref().is_some() { if self.tags.as_ref().is_some() {
return Some(self.tags.as_ref().unwrap().to_string()); return Some(self.tags.as_ref().unwrap().to_string());
} }
@ -41,13 +47,12 @@ impl LigthSetting {
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct KeyFrame { pub struct KeyFrame {
duration: u32, duration: u32,
settings: Vec<LigthSetting> settings: Vec<LigthSetting>,
} }
impl KeyFrame { impl KeyFrame {
@ -56,7 +61,7 @@ impl KeyFrame {
} }
pub fn get_duration(&self) -> u32 { pub fn get_duration(&self) -> u32 {
self.duration self.duration
} }
} }
@ -71,7 +76,6 @@ pub struct Animation {
} }
impl Animation { impl Animation {
pub fn get_frames(&self) -> Vec<KeyFrame> { pub fn get_frames(&self) -> Vec<KeyFrame> {
self.key_frames.clone() self.key_frames.clone()
} }
@ -79,9 +83,13 @@ impl Animation {
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {
self.name.to_string() self.name.to_string()
} }
pub fn get_priority(&self) -> u32 { pub fn get_priority(&self) -> u32 {
if self.priority.is_none() { 0 } else {self.priority.unwrap()} if self.priority.is_none() {
0
} else {
self.priority.unwrap()
}
} }
pub fn get_repeat(&self) -> bool { pub fn get_repeat(&self) -> bool {
@ -89,42 +97,69 @@ impl Animation {
} }
} }
#[post("/animation", data= "<data>")] #[post("/animation", data = "<data>")]
pub async fn animation(data: Json<Animation>, db: &State<DBPool>) -> String { pub async fn animation(data: Json<Animation>, db: &State<DBPool>) -> String {
if data.key_frames.is_empty() { if data.key_frames.is_empty() {
return json!(ApiResponse {code: 400, message: "KeyFrame are nessesary".to_string()}).to_string(); return json!(ApiResponse {
} code: 400,
message: "KeyFrame are nessesary".to_string()
})
.to_string();
}
if data.name.is_empty() { if data.name.is_empty() {
return json!(ApiResponse {code: 400, message: "Name must not be empty".to_string()}).to_string(); return json!(ApiResponse {
code: 400,
message: "Name must not be empty".to_string()
})
.to_string();
} }
for key_frame in data.key_frames.iter() { for key_frame in data.key_frames.iter() {
if key_frame.settings.is_empty() { if key_frame.settings.is_empty() {
return json!(ApiResponse {code: 400, message: "keyFrames.settings can not be empty".to_string()}).to_string(); return json!(ApiResponse {
code: 400,
message: "keyFrames.settings can not be empty".to_string()
})
.to_string();
} }
if key_frame.duration == 0 { if key_frame.duration == 0 {
return json!(ApiResponse {code: 400, message: "keyFrames.duration can not be 0".to_string()}).to_string(); return json!(ApiResponse {
code: 400,
message: "keyFrames.duration can not be 0".to_string()
})
.to_string();
} }
for setting in key_frame.settings.iter() { for setting in key_frame.settings.iter() {
if setting.tags.is_none() && (setting.start.is_none() || setting.end.is_none()) { 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(); 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()) { if !(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(); && (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); let db_status = add_animation_to_db(data, db);
if db_status.is_err() || db_status.as_ref().ok().is_none() { 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(); 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(); let db_status = db_status.ok().unwrap();
@ -133,15 +168,17 @@ pub async fn animation(data: Json<Animation>, db: &State<DBPool>) -> String {
return db_status; return db_status;
} }
json!(ApiResponse {code: 200, message: "Configuration was successful".to_string()}).to_string() json!(ApiResponse {
code: 200,
message: "Configuration was successful".to_string()
})
.to_string()
} }
fn get_animation_from_name(conn: &DBConn, name: String) -> Result<Option<u32>, Box<dyn Error>> { fn get_animation_from_name(conn: &DBConn, name: String) -> Result<Option<u32>, Box<dyn Error>> {
let mut stmt = conn.prepare("SELECT id FROM animation WHERE name=?1")?; let mut stmt = conn.prepare("SELECT id FROM animation WHERE name=?1")?;
let next = stmt.query_map([name], |row| { let next = stmt.query_map([name], |row| row.get(0))?.next();
row.get(0)
})?.next();
if next.is_none() { if next.is_none() {
return Ok(None); return Ok(None);
@ -150,12 +187,14 @@ fn get_animation_from_name(conn: &DBConn, name: String) -> Result<Option<u32>, B
Ok(next.unwrap()?) Ok(next.unwrap()?)
} }
fn get_keyframe_from_index(conn: &DBConn, animation: u32, index: u32) -> Result<Option<u32>, Box<dyn Error>> { fn get_keyframe_from_index(
conn: &DBConn,
animation: u32,
index: u32,
) -> Result<Option<u32>, Box<dyn Error>> {
let mut stmt = conn.prepare("SELECT id FROM keyframe WHERE animation=?1 AND i=?2")?; let mut stmt = conn.prepare("SELECT id FROM keyframe WHERE animation=?1 AND i=?2")?;
let next = stmt.query_map([animation, index], |row| { let next = stmt.query_map([animation, index], |row| row.get(0))?.next();
row.get(0)
})?.next();
if next.is_none() { if next.is_none() {
return Ok(None); return Ok(None);
@ -164,8 +203,15 @@ fn get_keyframe_from_index(conn: &DBConn, animation: u32, index: u32) -> Result<
Ok(next.unwrap()?) Ok(next.unwrap()?)
} }
fn add_animation_to_db(data: Json<Animation>, pool: &State<DBPool>) -> Result<String, Box<dyn Error>>{ fn add_animation_to_db(
let priority = if data.priority.is_some() { data.priority.unwrap() } else { 0 }; data: Json<Animation>,
pool: &State<DBPool>,
) -> Result<String, Box<dyn Error>> {
let priority = if data.priority.is_some() {
data.priority.unwrap()
} else {
0
};
let conn = pool.get()?; let conn = pool.get()?;
@ -176,25 +222,43 @@ fn add_animation_to_db(data: Json<Animation>, pool: &State<DBPool>) -> Result<St
println!("t: {:?}", name); println!("t: {:?}", name);
if name.is_some() { if name.is_some() {
return Ok(json!(ApiResponse {code: 500, message: "Animation with that name is already exists.".to_string()}).to_string()); 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 ))?; 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())?; let name = get_animation_from_name(&conn, data.name.to_string())?;
if name.is_none() { if name.is_none() {
return Ok(json!(ApiResponse {code: 500, message: "Failed to create animation.".to_string()}).to_string()); return Ok(json!(ApiResponse {
code: 500,
message: "Failed to create animation.".to_string()
})
.to_string());
} }
let animation_id = name.unwrap(); let animation_id = name.unwrap();
let mut i = 0; let mut i = 0;
for key_frame in data.key_frames.clone() { 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))?; 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)?; let keyframe = get_keyframe_from_index(&conn, animation_id, i)?;
if keyframe.is_none() { if keyframe.is_none() {
return Ok(json!(ApiResponse {code: 500, message: "Failed to create animation.".to_string()}).to_string()); return Ok(json!(ApiResponse {
code: 500,
message: "Failed to create animation.".to_string()
})
.to_string());
} }
let keyframe = keyframe.unwrap(); let keyframe = keyframe.unwrap();
@ -209,111 +273,181 @@ fn add_animation_to_db(data: Json<Animation>, pool: &State<DBPool>) -> Result<St
} }
/** /**
* *
* Stop animations * Stop animations
* *
*/ */
#[get("/stop/<name>")] #[get("/stop/<name>")]
pub async fn stop_animation(name: &str, stop_sender: &State<ASender<String>>) -> String { pub async fn stop_animation(name: &str, stop_sender: &State<ASender<String>>) -> String {
let r = stop_sender.lock().unwrap().send(name.to_string()); let r = stop_sender.lock().unwrap().send(name.to_string());
if r.is_err() || r.ok().is_none() { if r.is_err() || r.ok().is_none() {
return json!(ApiResponse {code: 500, message: "Something went wrong".to_string()}).to_string() return json!(ApiResponse {
code: 500,
message: "Something went wrong".to_string()
})
.to_string();
} }
json!(ApiResponse {code: 200, message: "Configuration was successful".to_string()}).to_string() json!(ApiResponse {
code: 200,
message: "Configuration was successful".to_string()
})
.to_string()
} }
/** /**
* *
* Start animations * Start animations
* *
*/ */
#[get("/start/<name>")] #[get("/start/<name>")]
pub async fn start_animation(name: &str, start_sender: &State<ASender<Animation>>, db: &State<DBPool>) -> String { pub async fn start_animation(
name: &str,
start_sender: &State<ASender<Animation>>,
db: &State<DBPool>,
) -> String {
let animation = get_animation(name, db); let animation = get_animation(name, db);
if animation.is_err() || animation.as_ref().ok().is_none() { 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() return json!(ApiResponse {
code: 500,
message: format!("Probelms with the db: {:?}", animation.err()).to_string()
})
.to_string();
} }
let animation = animation.ok().unwrap(); let animation = animation.ok().unwrap();
if animation.is_none() { if animation.is_none() {
return json!(ApiResponse {code: 404, message: "Animation not found".to_string()}).to_string() return json!(ApiResponse {
code: 404,
message: "Animation not found".to_string()
})
.to_string();
} }
let animation = animation.unwrap(); let animation = animation.unwrap();
let r = start_sender.lock().unwrap().send(animation); let r = start_sender.lock().unwrap().send(animation);
if r.is_err() || r.as_ref().ok().is_none() { if r.is_err() || r.as_ref().ok().is_none() {
return json!(ApiResponse {code: 500, message: "Probelms with the db".to_string()}).to_string() return json!(ApiResponse {
code: 500,
message: "Probelms with the db".to_string()
})
.to_string();
} }
json!(ApiResponse {code: 200, message: "Started animation".to_string()}).to_string() json!(ApiResponse {
code: 200,
message: "Started animation".to_string()
})
.to_string()
} }
/** /**
* *
* Clear animations * Clear animations
* *
*/ */
#[get("/clear/<name>")] #[get("/clear/<name>")]
pub async fn clear(name: &str, start_sender: &State<ASender<Animation>>, db: &State<DBPool>, action: &State<ASender<Action>>) -> String { pub async fn clear(
name: &str,
start_sender: &State<ASender<Animation>>,
db: &State<DBPool>,
action: &State<ASender<Action>>,
) -> String {
let r = action.lock().unwrap().send(Action::Clear); let r = action.lock().unwrap().send(Action::Clear);
if r.is_err() || r.ok().is_none() { if r.is_err() || r.ok().is_none() {
return json!(ApiResponse {code: 500, message: "Something went wrong".to_string()}).to_string() return json!(ApiResponse {
code: 500,
message: "Something went wrong".to_string()
})
.to_string();
} }
let animation = get_animation(name, db); let animation = get_animation(name, db);
if animation.is_err() || animation.as_ref().ok().is_none() { 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() return json!(ApiResponse {
code: 500,
message: format!("Probelms with the db: {:?}", animation.err()).to_string()
})
.to_string();
} }
let animation = animation.ok().unwrap(); let animation = animation.ok().unwrap();
if animation.is_none() { if animation.is_none() {
return json!(ApiResponse {code: 404, message: "Animation not found".to_string()}).to_string() return json!(ApiResponse {
code: 404,
message: "Animation not found".to_string()
})
.to_string();
} }
let animation = animation.unwrap(); let animation = animation.unwrap();
let r = start_sender.lock().unwrap().send(animation); let r = start_sender.lock().unwrap().send(animation);
if r.is_err() || r.as_ref().ok().is_none() { if r.is_err() || r.as_ref().ok().is_none() {
return json!(ApiResponse {code: 500, message: "Probelms with the db".to_string()}).to_string() return json!(ApiResponse {
code: 500,
message: "Probelms with the db".to_string()
})
.to_string();
} }
json!(ApiResponse {code: 200, message: "Started animation".to_string()}).to_string() json!(ApiResponse {
code: 200,
message: "Started animation".to_string()
})
.to_string()
}
#[get("/clearall")]
pub async fn clearAll(action: &State<ASender<Action>>) -> String {
let r = action.lock().unwrap().send(Action::Clear);
if r.is_err() || r.ok().is_none() {
return json!(ApiResponse {
code: 500,
message: "Something went wrong".to_string()
})
.to_string();
}
json!(ApiResponse {
code: 200,
message: "Cleared all the animations".to_string()
})
.to_string()
} }
pub fn get_animation(name: &str, db: &State<DBPool>) -> Result<Option<Animation>, Box<dyn Error>> { pub fn get_animation(name: &str, db: &State<DBPool>) -> Result<Option<Animation>, Box<dyn Error>> {
let conn = db.get()?; let conn = db.get()?;
let mut stmt = conn.prepare("SELECT id, priority, name, repeat FROM animation WHERE name=?1;")?; let mut stmt =
conn.prepare("SELECT id, priority, name, repeat FROM animation WHERE name=?1;")?;
struct AnimationId { struct AnimationId {
animation: Animation, animation: Animation,
id: u8 id: u8,
} }
let next = stmt.query_map([name], |row| { let next = stmt
Ok(AnimationId { .query_map([name], |row| {
animation: Animation { Ok(AnimationId {
key_frames: Vec::new(), animation: Animation {
priority: Some(row.get(1)?), key_frames: Vec::new(),
name: row.get(2)?, priority: Some(row.get(1)?),
repeat: row.get(3)? name: row.get(2)?,
}, repeat: row.get(3)?,
id: row.get(0)? },
}) id: row.get(0)?,
})?.next(); })
})?
.next();
if next.is_none() { if next.is_none() {
return Ok(None); return Ok(None);
@ -326,22 +460,24 @@ pub fn get_animation(name: &str, db: &State<DBPool>) -> Result<Option<Animation>
id: u8, id: u8,
} }
let mut stmt = conn.prepare("SELECT id, duration from keyframe where animation=?1 order by i asc")?; 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| { let mut map = stmt.query_map([animation.id], |row| {
Ok(KeyFrameId { Ok(KeyFrameId {
id: row.get(0)?, id: row.get(0)?,
keyframe: KeyFrame { keyframe: KeyFrame {
duration: row.get(1)?, duration: row.get(1)?,
settings: Vec::new() settings: Vec::new(),
} },
}) })
})?; })?;
while let Some(key_frame_id) = map.next() { while let Some(key_frame_id) = map.next() {
let mut key_frame_id = key_frame_id?; 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 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| { let mut light_setting_map = stmt.query_map([key_frame_id.id], |row| {
Ok(LigthSetting { Ok(LigthSetting {
@ -350,7 +486,7 @@ pub fn get_animation(name: &str, db: &State<DBPool>) -> Result<Option<Animation>
tags: row.get(2)?, tags: row.get(2)?,
r: row.get(3)?, r: row.get(3)?,
b: row.get(4)?, b: row.get(4)?,
g: row.get(5)? g: row.get(5)?,
}) })
})?; })?;
@ -365,24 +501,30 @@ pub fn get_animation(name: &str, db: &State<DBPool>) -> Result<Option<Animation>
} }
/** /**
* *
* Delete animations * Delete animations
* *
*/ */
#[get("/delete/<name>")] #[get("/delete/<name>")]
pub async fn delete(name: &str, db: &State<DBPool>) -> String { pub async fn delete(name: &str, db: &State<DBPool>) -> String {
let animation = remove_animation(name, db); let animation = remove_animation(name, db);
if animation.is_err() || animation.as_ref().ok().is_none() { 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() 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() json!(ApiResponse {
code: 200,
message: "Deleted animation".to_string()
})
.to_string()
} }
pub fn remove_animation(name: &str, db: &State<DBPool>) -> Result<(), Box<dyn Error>> { pub fn remove_animation(name: &str, db: &State<DBPool>) -> Result<(), Box<dyn Error>> {
let conn = db.get()?; 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 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])?;

View File

@ -12,7 +12,7 @@ use animations::Animation;
use r2d2::PooledConnection; use r2d2::PooledConnection;
use utils::ApiResponse; use utils::ApiResponse;
use rocket::{State, serde::json::serde_json::json}; use rocket::{serde::json::serde_json::json, State};
use r2d2_sqlite::SqliteConnectionManager; use r2d2_sqlite::SqliteConnectionManager;
@ -23,7 +23,7 @@ use std::{
sync::{ sync::{
mpsc::{self, Receiver, Sender}, mpsc::{self, Receiver, Sender},
Mutex, Mutex,
} },
}; };
pub type DBPool = Arc<r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>>; pub type DBPool = Arc<r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>>;
@ -39,7 +39,7 @@ fn index() -> &'static str {
pub enum Action { pub enum Action {
Reload, Reload,
Stop, Stop,
Clear Clear,
} }
#[get("/quit")] #[get("/quit")]
@ -49,12 +49,20 @@ async fn quit(shutdown: rocket::Shutdown, action: &State<ASender<Action>>) -> St
let send = action.lock().unwrap().send(Action::Stop); let send = action.lock().unwrap().send(Action::Stop);
if send.is_err() || send.ok().is_none() { if send.is_err() || send.ok().is_none() {
return json!(ApiResponse {code: 500, message: "Failed to stop".to_string()}).to_string() return json!(ApiResponse {
code: 500,
message: "Failed to stop".to_string()
})
.to_string();
} }
shutdown.notify(); shutdown.notify();
json!(ApiResponse {code: 200, message: "Stoped".to_string()}).to_string() json!(ApiResponse {
code: 200,
message: "Stoped".to_string()
})
.to_string()
} }
#[get("/reload")] #[get("/reload")]
@ -64,10 +72,18 @@ async fn reload(action: &State<ASender<Action>>) -> String {
let send = action.lock().unwrap().send(Action::Reload); let send = action.lock().unwrap().send(Action::Reload);
if send.is_err() || send.ok().is_none() { if send.is_err() || send.ok().is_none() {
return json!(ApiResponse {code: 500, message: "Failed to reload".to_string()}).to_string() return json!(ApiResponse {
code: 500,
message: "Failed to reload".to_string()
})
.to_string();
} }
json!(ApiResponse {code: 200, message: "Reloaded".to_string()}).to_string() json!(ApiResponse {
code: 200,
message: "Reloaded".to_string()
})
.to_string()
} }
fn ligth_controll( fn ligth_controll(
@ -97,15 +113,15 @@ fn ligth_controll(
if result.is_err() || result.as_ref().ok().is_none() { if result.is_err() || result.as_ref().ok().is_none() {
println!("Ehh failed to load config i guess: {:?}", result.err()); println!("Ehh failed to load config i guess: {:?}", result.err());
} }
}, }
Action::Clear => { Action::Clear => {
render.blank(); render.blank();
}, }
Action::Stop => { Action::Stop => {
render.blank(); render.blank();
render.im_render(); render.im_render();
break 'mainloop; break 'mainloop;
}, }
} }
} }
@ -125,7 +141,6 @@ fn ligth_controll(
} }
render.render(); render.render();
} }
println!("stoped main loop"); println!("stoped main loop");