diff --git a/toos-dashboard/src-tauri/assets/audio-old.mp3 b/toos-dashboard/src-tauri/assets/audio-old.mp3 new file mode 100644 index 0000000..32871cf Binary files /dev/null and b/toos-dashboard/src-tauri/assets/audio-old.mp3 differ diff --git a/toos-dashboard/src-tauri/assets/audio.mp3 b/toos-dashboard/src-tauri/assets/audio.mp3 index 32871cf..db5e13c 100644 Binary files a/toos-dashboard/src-tauri/assets/audio.mp3 and b/toos-dashboard/src-tauri/assets/audio.mp3 differ diff --git a/toos-dashboard/src-tauri/src/animation.rs b/toos-dashboard/src-tauri/src/animation.rs new file mode 100644 index 0000000..7471fea --- /dev/null +++ b/toos-dashboard/src-tauri/src/animation.rs @@ -0,0 +1,110 @@ +use std::time::Duration; +use std::thread; +use std::fs::File; +use std::io::BufReader; +use rodio::{Decoder, OutputStream, Sink}; +use std::sync::Mutex; +use once_cell::sync::Lazy; + +use crate::serial; + +struct Timings { + charging_start_delay: u64, + charging_delay: u64, + charging_end_delay: u64, +} + +static TIMINGS: Mutex> = Mutex::new(Lazy::new(|| Timings { charging_start_delay: 1600, charging_delay: 450, charging_end_delay: 400 } )); + +#[tauri::command] +pub fn start() { + thread::spawn(move || { + println!("Starting animation..."); + + + play_sound(); + thread::sleep(Duration::from_millis(500)); + + let _ = serial::write_serial(format!("SET_LIGHT:0;").as_str()); + stage_one(); + stage_two(); + }); +} + +fn play_sound() { + thread::spawn(|| { + let (_stream, stream_handle) = OutputStream::try_default().unwrap(); + let sink = Sink::try_new(&stream_handle).unwrap(); + + let file = BufReader::new(File::open("assets/audio.mp3").unwrap()); + let source = Decoder::new(file).unwrap(); + + sink.append(source); + + sink.sleep_until_end(); + }); +} + +fn stage_one() { + let timings = TIMINGS.lock().unwrap(); + + thread::sleep(Duration::from_millis(timings.charging_start_delay)); + + for i in 0..17 { + let _ = serial::write_serial(format!("SET_LED:{},0,128,255;", i).as_str()); + let _ = serial::write_serial(format!("SET_LED:{},0,128,255;", 33 - i).as_str()); + + let _ = serial::write_serial(format!("SET_LED:{},0,128,255;", i + 34).as_str()); + let _ = serial::write_serial(format!("SET_LED:{},0,128,255;", 67 - i).as_str()); + + thread::sleep(Duration::from_millis(timings.charging_delay)); + } +} + +fn stage_two() { + let timings = TIMINGS.lock().unwrap(); + + thread::sleep(Duration::from_millis(timings.charging_end_delay)); + let _ = serial::write_serial(format!("SET_LED_OFF;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,0,0;").as_str()); + + thread::sleep(Duration::from_millis(200)); + let _ = serial::write_serial(format!("SET_LED_COLOR:0,128,255;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,128,255;").as_str()); + + thread::sleep(Duration::from_millis(100)); + let _ = serial::write_serial(format!("SET_LED_OFF;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,0,0;").as_str()); + + thread::sleep(Duration::from_millis(200)); + let _ = serial::write_serial(format!("SET_LED_COLOR:0,128,255;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,128,255;").as_str()); + + thread::sleep(Duration::from_millis(200)); + let _ = serial::write_serial(format!("SET_LED_OFF;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,0,0;").as_str()); + + thread::sleep(Duration::from_millis(100)); + let _ = serial::write_serial(format!("SET_LED_COLOR:0,128,255;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,128,255;").as_str()); + + thread::sleep(Duration::from_millis(100)); + let _ = serial::write_serial(format!("SET_LED_OFF;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,0,0;").as_str()); + + thread::sleep(Duration::from_millis(300)); + let _ = serial::write_serial(format!("SET_LED_COLOR:0,128,255;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,128,255;").as_str()); + + thread::sleep(Duration::from_millis(100)); + let _ = serial::write_serial(format!("SET_LED_OFF;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,0,0;").as_str()); + + thread::sleep(Duration::from_millis(100)); + let _ = serial::write_serial(format!("SET_LED_COLOR:0,128,255;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,128,255;").as_str()); + + thread::sleep(Duration::from_millis(200)); + let _ = serial::write_serial(format!("SET_LED_OFF;").as_str()); + let _ = serial::write_serial(format!("SET_STRIP_COLOR:0,0,0;").as_str()); +} diff --git a/toos-dashboard/src-tauri/src/main.rs b/toos-dashboard/src-tauri/src/main.rs index 46685bb..93fbda3 100644 --- a/toos-dashboard/src-tauri/src/main.rs +++ b/toos-dashboard/src-tauri/src/main.rs @@ -1,124 +1,12 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use std::io::ErrorKind; -use std::thread; -use std::time::Duration; -use std::collections::HashMap; -use std::sync::Mutex; -use tauri::State; -use tauri::Manager; -use std::fs::File; -use std::io::BufReader; -use rodio::{Decoder, OutputStream, Sink}; - -#[derive(Default)] -struct PortMap(Mutex>>); - -#[derive(Clone, serde::Serialize)] -struct PayloadPortState { - open: bool, -} - -#[tauri::command] -async fn write_serial(input: &str, port_name: &str, port_map: State<'_, PortMap>) -> Result<(), String> { - match port_map.0.lock().unwrap().get(port_name) { - Some(port) => { - let mut clone = port.try_clone().expect("Failed to clone"); - println!("Writing to serial"); - match clone.write(input.as_bytes()) { - Ok(_) => { - Ok(()) - }, - Err(err) => Err(err.to_string()), - } - } - None => { - Err(String::from("Port is closed")) - } - } -} - - -#[tauri::command] -fn get_serial_ports() -> Vec { - let ports = serialport::available_ports().expect("No ports found"); - - let mut vec: Vec = Vec::new(); - - for p in ports { - vec.push(p.port_name); - } - vec -} - -#[tauri::command] -fn close_port(app_handle: tauri::AppHandle, port_name: &str, port_map: State<'_, PortMap>) { - match port_map.0.lock().unwrap().remove(port_name) { - Some(_) => { - println!("Port {} closed", port_name); - app_handle.emit_all("port-state", PayloadPortState { open: false }).unwrap(); - }, - None => println!("Port {} was already closed", port_name), - } -} - -#[tauri::command] -async fn open_port(app_handle: tauri::AppHandle, port_name: &str, baud: u32, port_map: State<'_, PortMap>) -> Result<(), String> { - match serialport::new(port_name, baud).timeout(Duration::from_millis(10)).open() { - Ok(port) => { - port_map.0.lock().unwrap().insert(port_name.to_string(), port.try_clone().expect("Error cloning")); - - let mut clone = port.try_clone().expect("Failed to clone"); - let mut buffer: Vec = vec![0; 1024]; - - println!("Port {} is open", port_name); - - app_handle.emit_all("port-state", PayloadPortState { open: true }).unwrap(); - - loop { - match clone.read(buffer.as_mut_slice()) { - Ok(bytes_read) => { - if bytes_read > 0 { - let data = &buffer[..bytes_read]; - let data = String::from_utf8_lossy(data).to_string(); - if !data.trim().is_empty() { - println!("{}", data.trim()); - if data.trim() == "1" { - play_sound() - // app_handle.emit_all("button-on", PayloadPortState { open: true }).unwrap(); - } - } - } - }, - Err(ref e) if e.kind() == ErrorKind::TimedOut => (), - Err(e) => return Err(e.to_string()), - }; - - } - }, - Err(err) => return Err(err.to_string()), - } -} - -fn play_sound() { - thread::spawn(|| { - let (_stream, stream_handle) = OutputStream::try_default().unwrap(); - let sink = Sink::try_new(&stream_handle).unwrap(); - - let file = BufReader::new(File::open("assets/audio.mp3").unwrap()); - let source = Decoder::new(file).unwrap(); - - sink.append(source); - - sink.sleep_until_end(); - }); -} +mod serial; +mod animation; fn main() { tauri::Builder::default() - .manage(PortMap::default()) - .invoke_handler(tauri::generate_handler![get_serial_ports, write_serial, open_port, close_port]) + .invoke_handler(tauri::generate_handler![serial::get_serial_ports, serial::write_serial, serial::open_port, serial::close_port, animation::start]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/toos-dashboard/src-tauri/src/serial.rs b/toos-dashboard/src-tauri/src/serial.rs new file mode 100644 index 0000000..abd759f --- /dev/null +++ b/toos-dashboard/src-tauri/src/serial.rs @@ -0,0 +1,103 @@ +use std::io::ErrorKind; +use std::time::Duration; +use std::collections::HashMap; +use std::sync::Mutex; +use tauri::Manager; +use once_cell::sync::Lazy; + +use crate::animation; + +#[derive(Default)] +pub struct PortMap(Mutex>>); + +static PORT_MAP: Mutex>>> = Mutex::new(Lazy::new(|| HashMap::new())); +static PORT_NAME: Mutex> = Mutex::new(Lazy::new(|| String::from("/dev/ttyUSB0"))); + +#[derive(Clone, serde::Serialize)] +pub struct PayloadPortState { + open: bool, +} + +#[tauri::command] +pub fn write_serial(input: &str) -> Result<(), String> { + let port_name = PORT_NAME.lock().unwrap().to_string(); + match PORT_MAP.lock().unwrap().get(&port_name) { + Some(port) => { + let mut clone = port.try_clone().expect("Failed to clone"); + match clone.write(input.as_bytes()) { + Ok(_) => { + Ok(()) + }, + Err(err) => Err(err.to_string()), + } + } + None => { + Err(String::from("Port is closed")) + } + } +} + +#[tauri::command] +pub fn get_serial_ports() -> Vec { + let ports = serialport::available_ports().expect("No ports found"); + + let mut vec: Vec = Vec::new(); + + for p in ports { + vec.push(p.port_name); + } + vec +} + +#[tauri::command] +pub fn close_port(app_handle: tauri::AppHandle) { + let port_name = PORT_NAME.lock().unwrap().to_string(); + match PORT_MAP.lock().unwrap().remove(&port_name) { + Some(_) => { + println!("Port {:?} closed", port_name); + app_handle.emit_all("port-state", PayloadPortState { open: false }).unwrap(); + }, + None => println!("Port {:?} was already closed", port_name), + } +} + +#[tauri::command] +pub async fn open_port(app_handle: tauri::AppHandle, baud: u32) -> Result<(), String> { + let port_name = PORT_NAME.lock().unwrap().to_string(); + + match serialport::new(&port_name, baud).timeout(Duration::from_millis(10)).open() { + Ok(port) => { + // port_map.0.lock().unwrap().insert(port_name.to_string(), port.try_clone().expect("Error cloning")); + PORT_MAP.lock().unwrap().insert(port_name.clone(), port.try_clone().expect("Error cloning port")); + + let mut clone = port.try_clone().expect("Failed to clone"); + let mut buffer: Vec = vec![0; 1024]; + + println!("Port {:?} is open", port_name); + + app_handle.emit_all("port-state", PayloadPortState { open: true }).unwrap(); + + loop { + match clone.read(buffer.as_mut_slice()) { + Ok(bytes_read) => { + if bytes_read > 0 { + let data = &buffer[..bytes_read]; + let data = String::from_utf8_lossy(data).to_string(); + if !data.trim().is_empty() { + println!("{}", data.trim()); + if data.trim() == "1" { + animation::start(); + app_handle.emit_all("button-on", PayloadPortState { open: true }).unwrap(); + } + } + } + }, + Err(ref e) if e.kind() == ErrorKind::TimedOut => (), + Err(e) => return Err(e.to_string()), + }; + + } + }, + Err(err) => return Err(err.to_string()), + } +} diff --git a/toos-dashboard/src/App.vue b/toos-dashboard/src/App.vue index eb70e5a..169294f 100644 --- a/toos-dashboard/src/App.vue +++ b/toos-dashboard/src/App.vue @@ -40,10 +40,5 @@ onMounted(async () => { await listen('port-state', (event) => { serialConfig.value.open = event.payload.open }); - - await listen('button-on', async () => { - const audioContainer = document.getElementById("audioContainer"); - await audioContainer.play(); - }); }) diff --git a/toos-dashboard/src/components/Main.vue b/toos-dashboard/src/components/Main.vue index 83caea9..a42fa35 100644 --- a/toos-dashboard/src/components/Main.vue +++ b/toos-dashboard/src/components/Main.vue @@ -1,3 +1,23 @@ + + diff --git a/toos-dashboard/src/components/Test.vue b/toos-dashboard/src/components/Test.vue index 119159f..d498259 100644 --- a/toos-dashboard/src/components/Test.vue +++ b/toos-dashboard/src/components/Test.vue @@ -36,7 +36,7 @@ -