Skip to content

Commit

Permalink
Implement simple daily schedule to refresh articles and users
Browse files Browse the repository at this point in the history
  • Loading branch information
zargony committed Dec 6, 2024
1 parent 2abe2a8 commit 7a1e870
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 2 deletions.
2 changes: 2 additions & 0 deletions firmware/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Automatically refresh article and user information once a day

## 0.2.0 - 2024-11-27

- Show random greetings to user
Expand Down
5 changes: 5 additions & 0 deletions firmware/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ mod json;
mod keypad;
mod nfc;
mod pn532;
mod schedule;
mod screen;
mod time;
mod ui;
Expand Down Expand Up @@ -217,6 +218,9 @@ async fn main(spawner: Spawner) {
let mut buzzer = buzzer::Buzzer::new(peripherals.LEDC, peripherals.GPIO4);
let _ = buzzer.startup().await;

// Initialize scheduler
let mut schedule = schedule::Daily::new();

// Create UI
let mut ui = ui::Ui::new(
rng,
Expand All @@ -229,6 +233,7 @@ async fn main(spawner: Spawner) {
&mut vereinsflieger,
&mut articles,
&mut users,
&mut schedule,
);

loop {
Expand Down
70 changes: 70 additions & 0 deletions firmware/src/schedule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use core::fmt;
use embassy_time::Timer;
use embassy_time::{Duration, Instant};
use log::info;

/// Simple time interval of 24h
#[cfg(not(debug_assertions))]
const DAILY_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
#[cfg(debug_assertions)]
const DAILY_INTERVAL: Duration = Duration::from_secs(30 * 60);

/// Duration display helper
struct DisplayDuration(Duration);

impl fmt::Display for DisplayDuration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let hours = self.0.as_secs() / 3600;
let min = self.0.as_secs() % 3600 / 60;
let secs = self.0.as_secs() % 60;
write!(f, "{hours}h{min}m{secs}s")
}
}

/// Scheduler for daily events
#[derive(Debug)]
pub struct Daily {
next: Instant,
}

impl Daily {
/// Create new daily scheduler
pub fn new() -> Self {
let mut daily = Self {
next: Instant::now(),
};
daily.schedule_next();
daily
}

/// Returns true when schedule time is expired
pub fn is_expired(&self) -> bool {
self.next <= Instant::now()
}

/// Time left until schedule time
pub fn time_left(&self) -> Duration {
self.next.saturating_duration_since(Instant::now())
}

/// Timer that can be awaited on to wait for schedule time
pub fn timer(&self) -> Timer {
Timer::at(self.next)
}

/// After expiring, schedule next event
pub fn schedule_next(&mut self) {
if self.is_expired() {
// Simple schedule: run again 24h later
self.next += DAILY_INTERVAL;
}
if self.is_expired() {
// Simple schedule: run in 24h from now
self.next = Instant::now() + DAILY_INTERVAL;
}
info!(
"Schedule: next daily event scheduled in {} from now",
DisplayDuration(self.time_left())
);
}
}
28 changes: 26 additions & 2 deletions firmware/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::error::Error;
use crate::http::Http;
use crate::keypad::{Key, Keypad};
use crate::nfc::Nfc;
use crate::schedule::Daily;
use crate::screen;
use crate::user::{UserId, Users};
use crate::vereinsflieger::Vereinsflieger;
Expand Down Expand Up @@ -49,6 +50,7 @@ pub struct Ui<'a, RNG, I2C, IRQ> {
vereinsflieger: &'a mut Vereinsflieger<'a>,
articles: &'a mut Articles<1>,
users: &'a mut Users,
schedule: &'a mut Daily,
}

impl<'a, RNG: RngCore, I2C: I2c, IRQ: Wait<Error = Infallible>> Ui<'a, RNG, I2C, IRQ> {
Expand All @@ -65,6 +67,7 @@ impl<'a, RNG: RngCore, I2C: I2c, IRQ: Wait<Error = Infallible>> Ui<'a, RNG, I2C,
vereinsflieger: &'a mut Vereinsflieger<'a>,
articles: &'a mut Articles<1>,
users: &'a mut Users,
schedule: &'a mut Daily,
) -> Self {
Self {
rng,
Expand All @@ -77,6 +80,7 @@ impl<'a, RNG: RngCore, I2C: I2c, IRQ: Wait<Error = Infallible>> Ui<'a, RNG, I2C,
vereinsflieger,
articles,
users,
schedule,
}
}

Expand Down Expand Up @@ -191,8 +195,14 @@ impl<'a, RNG: RngCore, I2C: I2c, IRQ: Wait<Error = Infallible>> Ui<'a, RNG, I2C,

/// Run the user interface flow
pub async fn run(&mut self) -> Result<(), Error> {
// Wait for id card and verify identification
let user_id = self.authenticate_user().await?;
// Either wait for id card read or schedule time
let schedule_timer = self.schedule.timer();
let user_id = match select(self.authenticate_user(), schedule_timer).await {
// Id card read
Either::First(res) => res?,
// Schedule time
Either::Second(()) => return self.schedule().await,
};

// Get user information
let user = self.users.get(user_id);
Expand Down Expand Up @@ -222,6 +232,20 @@ impl<'a, RNG: RngCore, I2C: I2c, IRQ: Wait<Error = Infallible>> Ui<'a, RNG, I2C,

Ok(())
}

/// Run schedule
pub async fn schedule(&mut self) -> Result<(), Error> {
if self.schedule.is_expired() {
info!("UI: Running schedule...");

// Schedule next event
self.schedule.schedule_next();

// Refresh article and user information
self.refresh_articles_and_users().await?;
}
Ok(())
}
}

impl<RNG: RngCore, I2C: I2c, IRQ: Wait<Error = Infallible>> Ui<'_, RNG, I2C, IRQ> {
Expand Down

0 comments on commit 7a1e870

Please sign in to comment.