chore: added option to get list of active animations
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Andre Henriques 2023-07-01 23:11:46 +01:00
parent 6a6e171f62
commit 5c84875dd9
4 changed files with 232 additions and 158 deletions

161
src/db.rs Normal file
View File

@ -0,0 +1,161 @@
use std::error::Error;
use crate::DBPool;
pub type GResult = Result<(), Box<dyn Error>>;
const DATAVERSION: &str = "0.0.10";
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(())
}
pub 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(())
}

View File

@ -7,15 +7,18 @@ mod animations;
mod configure; mod configure;
mod render; mod render;
mod utils; mod utils;
mod db;
use animations::Animation; use animations::Animation;
use r2d2::PooledConnection; use r2d2::PooledConnection;
use utils::ApiResponse; use utils::{ApiResponse, ApiResponseActiveList};
use rocket::{serde::json::serde_json::json, State}; use rocket::{serde::json::{serde_json::json, self}, State};
use r2d2_sqlite::SqliteConnectionManager; use r2d2_sqlite::SqliteConnectionManager;
use crate::db::setup_database;
//use std::sync::atomic::AtomicBool; //use std::sync::atomic::AtomicBool;
use std::{ use std::{
@ -30,6 +33,7 @@ pub type DBPool = Arc<r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>>;
pub type DBConn = PooledConnection<r2d2_sqlite::SqliteConnectionManager>; pub type DBConn = PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
pub type GResult = Result<(), Box<dyn Error>>; pub type GResult = Result<(), Box<dyn Error>>;
pub type ASender<T> = Arc<Mutex<Sender<T>>>; pub type ASender<T> = Arc<Mutex<Sender<T>>>;
pub type AReceiver<T> = Arc<Mutex<Receiver<T>>>;
#[get("/")] #[get("/")]
fn index() -> &'static str { fn index() -> &'static str {
@ -40,6 +44,13 @@ pub enum Action {
Reload, Reload,
Stop, Stop,
Clear, Clear,
GetActiveList,
}
pub enum RenderMessage {
ActiveList(Vec<String>),
// TODO remove none where other are added
None
} }
#[get("/quit")] #[get("/quit")]
@ -86,11 +97,48 @@ async fn reload(action: &State<ASender<Action>>) -> String {
.to_string() .to_string()
} }
#[get("/active")]
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();
}
fn ligth_controll( fn ligth_controll(
pool: DBPool, pool: DBPool,
action: Receiver<Action>, action: Receiver<Action>,
start_ani: Receiver<Animation>, start_ani: Receiver<Animation>,
stop_ani: Receiver<String>, stop_ani: Receiver<String>,
message_server: Sender<RenderMessage>,
) { ) {
println!("Data loop started"); println!("Data loop started");
@ -122,6 +170,11 @@ fn ligth_controll(
render.im_render(); render.im_render();
break 'mainloop; break 'mainloop;
} }
Action::GetActiveList => {
if message_server.send(RenderMessage::ActiveList(render.get_active_animations())).is_err() {
println!("Failed to send message ups");
}
}
} }
} }
@ -146,162 +199,6 @@ fn ligth_controll(
println!("stoped main loop"); 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] #[rocket::main]
async fn main() -> GResult { async fn main() -> GResult {
@ -327,6 +224,9 @@ async fn main() -> GResult {
let (stop_animation_sender, stop_animation_receiver): (Sender<String>, Receiver<String>) = let (stop_animation_sender, stop_animation_receiver): (Sender<String>, Receiver<String>) =
mpsc::channel(); mpsc::channel();
let (messager_sender, messager_revicer): (Sender<RenderMessage>, Receiver<RenderMessage>) =
mpsc::channel();
let pool_clone = conn_pool_arc.clone(); let pool_clone = conn_pool_arc.clone();
let ligths_controll = std::thread::spawn(move || { let ligths_controll = std::thread::spawn(move || {
@ -335,6 +235,7 @@ async fn main() -> GResult {
action_receiver, action_receiver,
start_animation_receiver, start_animation_receiver,
stop_animation_receiver, stop_animation_receiver,
messager_sender
); );
}); });
@ -362,6 +263,7 @@ async fn main() -> GResult {
.manage(Arc::new(Mutex::new(action_sender))) .manage(Arc::new(Mutex::new(action_sender)))
.manage(Arc::new(Mutex::new(start_animation_sender))) .manage(Arc::new(Mutex::new(start_animation_sender)))
.manage(Arc::new(Mutex::new(stop_animation_sender))) .manage(Arc::new(Mutex::new(stop_animation_sender)))
.manage(Arc::new(Mutex::new(messager_revicer)))
.launch() .launch()
.await?; .await?;
ligths_controll.join().unwrap(); ligths_controll.join().unwrap();

View File

@ -91,6 +91,10 @@ impl Render {
return Ok(render); return Ok(render);
} }
pub fn get_active_animations(&mut self) -> Vec<String> {
self.animations.iter().map(|animation| animation.name.clone()).collect()
}
pub fn load_config(&mut self) -> Result<(), Box<dyn Error>> { pub fn load_config(&mut self) -> Result<(), Box<dyn Error>> {
println!("Load config"); println!("Load config");
// GPIO Pin 10 is SPI // GPIO Pin 10 is SPI

View File

@ -7,5 +7,12 @@ pub struct ApiResponse {
pub message: String pub message: String
} }
#[derive(Deserialize, Serialize)]
#[serde(crate="rocket::serde")]
pub struct ApiResponseActiveList {
pub code: u32,
pub active_animations: Vec<String>,
}
#[derive(Responder)] #[derive(Responder)]
pub struct ApiResponder(String); pub struct ApiResponder(String);