diff --git a/' b/' new file mode 100644 index 0000000..5985589 --- /dev/null +++ b/' @@ -0,0 +1,83 @@ +use std::{ + sync::{Arc, Mutex}, + thread, +}; + +use chrono::Local; +use cron_tab::Cron; +use gpio_cdev::{Chip, LineRequestFlags}; + +use ringer::Ringer; + +use crate::{ringer::BeepRinger, scheduler::Scheduler}; + +mod handler; +mod router; +mod scheduler; +mod ringer; + +#[derive(Clone)] +struct AppState { + chip: Arc>, + cron: Arc>>, + scheduler: Arc> +} + +fn app_state() -> AppState { + let chip = Arc::new(Mutex::new(Chip::new("/dev/gpiochip0").unwrap())); + let cron = Arc::new(Mutex::new(Cron::new(chrono::Local))); + AppState { + chip: chip.clone(), + cron: cron.clone(), + scheduler: Arc::new(Scheduler::new( + BeepRinger::new(chip.clone()), + cron.clone() + )) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let chip = Arc::new(Mutex::new(Chip::new("/dev/gpiochip0")?)); + let mut cron = Cron::new(chrono::Local); + let static_time = "0 30 7 * * *"; + let app_state = app_state(); + + println!("Adding alarm {}", static_time); + let job_result = cron.add_fn(static_time, move || { + let mut guard = chip.lock().unwrap(); + alarm(&mut *guard).unwrap(); + }); + + let _ = job_result.expect("Failed to add job"); + + cron.start(); + + start_server(app_state).await; + + Ok(()) +} + + +async fn start_server(app_state: AppState) { + let router = router::router(app_state); + let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap(); + + println!("Listening on http://0.0.0.0:8080"); + axum::serve(listener, router).await.unwrap(); +} + +fn alarm(chip: &mut Chip) -> Result<(), Box> { + let beeper = chip + .get_line(17)? + .request(LineRequestFlags::OUTPUT, 0, "my-gpio")?; + + for _ in 0..5 { + beeper.set_value(1)?; + thread::sleep(std::time::Duration::from_secs(1)); + beeper.set_value(0)?; + thread::sleep(std::time::Duration::from_secs(1)); + } + + Ok(()) +} diff --git a/Cargo.lock b/Cargo.lock index 0e38a49..e0e0ca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -423,6 +423,15 @@ version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -484,6 +493,29 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -520,6 +552,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -532,6 +573,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.228" @@ -603,6 +650,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.11" @@ -679,9 +735,12 @@ version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ + "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.61.2", diff --git a/Cargo.toml b/Cargo.toml index 3d1c623..839f48e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,4 @@ gpio-cdev = "0.6.0" cron_tab = { version = "0.2", features = ["async"] } chrono = "0.4.42" axum = { version = "0.8.8", features = ["macros"] } -tokio = "1.49.0" +tokio = { version = "1.49.0", features = ["full"] } diff --git a/src/main.rs b/src/main.rs index cee015e..50d7413 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,54 +1,51 @@ use std::{ sync::{Arc, Mutex}, - thread, }; use cron_tab::Cron; -use gpio_cdev::{Chip, LineRequestFlags}; +use gpio_cdev::Chip; + +use crate::{ringer::BeepRinger, scheduler::Scheduler}; mod handler; mod router; +mod scheduler; +mod ringer; + +#[derive(Clone)] +struct AppState { + scheduler: Arc> +} + +fn app_state() -> AppState { + let chip: Arc> = Arc::new(Mutex::new(Chip::new("/dev/gpiochip0").unwrap())); + let cron = Arc::new(Mutex::new(Cron::new(chrono::Local))); + AppState { + scheduler: Arc::new(Scheduler::new( + Arc::new(Mutex::new(BeepRinger::new(chip.clone()))), + cron.clone() + )) + } +} #[tokio::main] async fn main() -> Result<(), Box> { - let chip = Arc::new(Mutex::new(Chip::new("/dev/gpiochip0")?)); - let mut cron = Cron::new(chrono::Local); let static_time = "0 30 7 * * *"; + let app_state = app_state(); - println!("Adding alarm {}", static_time); - let job_result = cron.add_fn(static_time, move || { - let mut guard = chip.lock().unwrap(); - alarm(&mut *guard).unwrap(); - }); + app_state.scheduler.schedule(static_time)?; + app_state.scheduler.start(); - let _ = job_result.expect("Failed to add job"); - - cron.start(); - - start_server().await; + start_server(app_state).await; Ok(()) } -async fn start_server() { - let router = router::router(); + +async fn start_server(app_state: AppState) { + let router = router::router(app_state); let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap(); println!("Listening on http://0.0.0.0:8080"); axum::serve(listener, router).await.unwrap(); } - -fn alarm(chip: &mut Chip) -> Result<(), Box> { - let beeper = chip - .get_line(17)? - .request(LineRequestFlags::OUTPUT, 0, "my-gpio")?; - - for _ in 0..5 { - beeper.set_value(1)?; - thread::sleep(std::time::Duration::from_secs(1)); - beeper.set_value(0)?; - thread::sleep(std::time::Duration::from_secs(1)); - } - - Ok(()) -} diff --git a/src/ringer.rs b/src/ringer.rs new file mode 100644 index 0000000..6add61c --- /dev/null +++ b/src/ringer.rs @@ -0,0 +1,70 @@ +use std::{sync::{Arc, Mutex}, thread}; + +use gpio_cdev::{Chip, LineRequestFlags}; + +pub trait Ringer: Send + Sync { + fn ring(&self) -> Result<(), String>; +} + +#[derive(Debug)] +pub struct BeepRinger { + chip: Arc>, +} + +impl BeepRinger { + + fn beep(times: u32, chip: &mut Chip) -> Result<(), String> { + let beeper = chip.get_line(17); + + let beeper = match beeper { + Ok(beeper) => beeper, + Err(e) => { + println!("Error opening line: {}", e); + return Err("Could not open Line to Beeper".to_string()); + } + }; + + + let beeper = beeper + .request(LineRequestFlags::OUTPUT, 0, "my-gpio"); + + let beeper = match beeper { + Ok(beeper) => beeper, + Err(e) => { + println!("Error requesting line: {}", e); + return Err("Could not request Line to Beeper".to_string()); + } + }; + + for _ in 0..times { + beeper.set_value(1).map_err(|e| e.to_string())?; + thread::sleep(std::time::Duration::from_secs(1)); + beeper.set_value(0).map_err(|e| e.to_string())?; + thread::sleep(std::time::Duration::from_secs(1)); + } + + return Ok(()); + } + + pub fn new(chip: Arc>) -> Self { + Self { chip } + } +} + +impl Ringer for BeepRinger { + fn ring(&self) -> Result<(), String> { + let chip = self.chip.lock(); + + let mut chip = match chip { + Ok(chip) => chip, + Err(e) => { + println!("Error opening chip: {}", e); + return Err("Could not access Chip".to_string()); + } + }; + + BeepRinger::beep(5, &mut *chip)?; + + Ok(()) + } +} diff --git a/src/router/alarm.rs b/src/router/alarm.rs index 3cde634..22cd665 100644 --- a/src/router/alarm.rs +++ b/src/router/alarm.rs @@ -1,6 +1,6 @@ -use axum::{Router, routing::post}; -use crate::handler::alarm::create_alarm; +use crate::{handler::alarm::create_alarm, AppState}; +use axum::{routing::post, Router}; -pub fn router() -> Router { +pub fn router() -> Router { Router::new().route("/", post(create_alarm)) } diff --git a/src/router/mod.rs b/src/router/mod.rs index 2223d78..3c55dce 100644 --- a/src/router/mod.rs +++ b/src/router/mod.rs @@ -2,7 +2,8 @@ use axum::Router; mod alarm; -pub fn router() -> Router { +pub fn router(app_state: crate::AppState) -> Router { Router::new() - .nest("/alarm", alarm::router()) + .nest("/alarm", alarm::router()) + .with_state(app_state) } diff --git a/src/scheduler.rs b/src/scheduler.rs new file mode 100644 index 0000000..1fa8a1c --- /dev/null +++ b/src/scheduler.rs @@ -0,0 +1,40 @@ +use std::sync::{Arc, Mutex}; + +use chrono::Local; +use cron_tab::Cron; + +use crate::ringer::Ringer; + +#[derive(Debug)] +pub struct Scheduler { + ringer: Arc>, + cron: Arc>>, +} + +impl Scheduler { + pub fn new(ringer: Arc>, cron: Arc>>) -> Self { + Self { ringer, cron } + } + + pub fn schedule(&self, cron_schedule: &str) -> Result<(), String> { + let ringer = self.ringer.clone(); + let cron = self.cron.clone(); + + let job_result = cron + .lock() + .map_err(|e| e.to_string())? + .add_fn(cron_schedule, move || { + let guard = ringer.lock(); + if let Ok(ringer) = guard { + ringer.ring().unwrap(); + } + }); + + job_result.expect("Faild to add job"); + todo!() + } + + pub fn start(&self) { + self.cron.lock().expect("Failed to lock cron").start(); + } +}