All checks were successful
continuous-integration/drone/push Build is passing
738 lines
18 KiB
Rust
738 lines
18 KiB
Rust
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<u32>,
|
|
end: Option<u32>,
|
|
tags: Option<String>,
|
|
r: u8,
|
|
b: u8,
|
|
g: u8,
|
|
}
|
|
|
|
impl LigthSetting {
|
|
pub fn get_start(&self) -> Option<u32> {
|
|
self.start
|
|
}
|
|
pub fn get_end(&self) -> Option<u32> {
|
|
self.end
|
|
}
|
|
pub fn get_tags(&self) -> Option<String> {
|
|
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<LigthSetting>,
|
|
}
|
|
|
|
impl KeyFrame {
|
|
pub fn get_settings(&self) -> Vec<LigthSetting> {
|
|
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<KeyFrame>,
|
|
priority: Option<u32>,
|
|
name: String,
|
|
repeat: bool,
|
|
}
|
|
|
|
impl Animation {
|
|
pub fn get_frames(&self) -> Vec<KeyFrame> {
|
|
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<DBPool>) -> 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<Data> = animations.into_iter().map(|a| a.unwrap()).collect();
|
|
|
|
json!(data).to_string()
|
|
}
|
|
|
|
// Add animation
|
|
#[post("/animation", data = "<data>")]
|
|
pub async fn animation(data: Json<Animation>, db: &State<DBPool>) -> 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<Option<u32>, Box<dyn Error>> {
|
|
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<Option<u32>, Box<dyn Error>> {
|
|
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<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()?;
|
|
|
|
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/<name>")]
|
|
pub async fn stop_animation(name: &str, action: &State<ASender<Action>>) -> 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/<name>")]
|
|
pub async fn start_animation(
|
|
name: &str,
|
|
action: &State<ASender<Action>>,
|
|
db: &State<DBPool>,
|
|
) -> 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/<name>")]
|
|
pub async fn get_animation_by_name(name: &str, db: &State<DBPool>) -> 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/<name>")]
|
|
pub async fn clear(
|
|
name: &str,
|
|
db: &State<DBPool>,
|
|
action: &State<ASender<Action>>,
|
|
) -> 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<ASender<Action>>) -> 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<DBPool>) -> Result<Option<Animation>, Box<dyn Error>> {
|
|
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<DBPool>) -> Result<Animation, String> {
|
|
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/<name>")]
|
|
pub async fn delete(name: &str, db: &State<DBPool>) -> 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<DBPool>) -> Result<(), Box<dyn Error>> {
|
|
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<ASender<Action>>,
|
|
messages: &State<AReceiver<RenderMessage>>,
|
|
) -> 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/<name>")]
|
|
pub async fn get_animation_request(name: &str, db: &State<DBPool>) -> 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/<name>")]
|
|
pub async fn toggle_animation(
|
|
name: &str,
|
|
action: &State<ASender<Action>>,
|
|
messages: &State<AReceiver<RenderMessage>>,
|
|
db: &State<DBPool>,
|
|
) -> 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/<name>")]
|
|
pub async fn cleartoggle_animation(
|
|
name: &str,
|
|
action: &State<ASender<Action>>,
|
|
messages: &State<AReceiver<RenderMessage>>,
|
|
db: &State<DBPool>,
|
|
) -> 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");
|
|
}
|