use log::*; //{trace, debug, info, warn, error} use anyhow::Result; use esp_idf_svc::timer::{EspTaskTimerService, EspAsyncTimer}; use core::time::Duration; use async_channel::{Receiver, Sender}; use futures_lite::FutureExt; use crate::dispatch::Dispatch; use crate::commands::Commands; #[derive(Copy, Clone)] enum State { Running, Stopped, } /// A timer that can be reset or canceled via message queue /// Q is the type of enum on the receive Q, S on the send. pub struct MessageTimer { restart: Q, cancel: Q, done: S, // Send this when the timeout is hit duration: Duration, send_q: Sender, recv_q: Receiver, state: State, } impl MessageTimer { pub fn new( restart: Q, cancel: Q, done: S, duration:Duration, send_q: Sender, recv_q: Receiver ) -> MessageTimer { MessageTimer { restart: restart, cancel: cancel, done: done, duration: duration, send_q: send_q, recv_q: recv_q, state: State::Stopped, } } pub fn new_on_dispatch( restart: Commands, cancel: Commands, done: Commands, ms: u64, dp: &mut Dispatch, ) -> MessageTimer { let cmds = vec![restart.clone(), cancel.clone()]; let s = dp.get_cmd_channel(); let r = dp.get_callback_channel(&cmds); let duration = Duration::from_millis(ms); MessageTimer { restart:restart, cancel: cancel, done: done, duration: duration, send_q: s, recv_q: r, state: State::Stopped, } } async fn wait_for_cmd(&self) -> Result { Ok(self.recv_q.recv().await.expect("Timer command queue unexpectedly failed")) } async fn wait_with_timeout(&self, timer: &mut EspAsyncTimer) -> Result { self.wait_for_cmd().or(async{ timer.after(self.duration).await?; Err(anyhow::Error::from(std::io::Error::new(std::io::ErrorKind::TimedOut, "Timeout"))) }).await } async fn handle_cmd(&mut self, cmd: Result) { match cmd { Ok(msg) => { if msg == self.restart { self.state = State::Running; // timer will automatically reset } else if msg == self.cancel { self.state = State::Stopped; } else { warn!("Spurious messages have reset a timer!"); } } Err(_) => { trace!("Timeout reached"); self.send_q.send(self.done.clone()).await.expect("Failed to send timeout"); } } } pub async fn run(&mut self) -> Result<()> { let timer_service = EspTaskTimerService::new()?; let mut async_timer = timer_service.timer_async()?; loop { let cmd = match self.state { State::Running => { self.wait_with_timeout(&mut async_timer).await } State::Stopped => { self.wait_for_cmd().await } }; self.handle_cmd(cmd).await; } } }