119 lines
3.3 KiB
Rust
119 lines
3.3 KiB
Rust
|
|
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 <Q, S> {
|
|
restart: Q,
|
|
cancel: Q,
|
|
done: S, // Send this when the timeout is hit
|
|
duration: Duration,
|
|
send_q: Sender<S>,
|
|
recv_q: Receiver<Q>,
|
|
state: State,
|
|
}
|
|
|
|
impl<Q: PartialEq, S: Clone> MessageTimer<Q, S> {
|
|
pub fn new(
|
|
restart: Q,
|
|
cancel: Q,
|
|
done: S,
|
|
duration:Duration,
|
|
send_q: Sender<S>,
|
|
recv_q: Receiver<Q>
|
|
) -> MessageTimer<Q, S> {
|
|
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<Commands, Commands> {
|
|
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<Q> {
|
|
Ok(self.recv_q.recv().await.expect("Timer command queue unexpectedly failed"))
|
|
}
|
|
|
|
async fn wait_with_timeout(&self, timer: &mut EspAsyncTimer) -> Result<Q> {
|
|
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<Q>) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
} |