Initial rough-in of motor controller
This commit is contained in:
120
gem-remotes-esp32/src/message_timer.rs
Normal file
120
gem-remotes-esp32/src/message_timer.rs
Normal file
@ -0,0 +1,120 @@
|
||||
|
||||
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: Copy> 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, cancel];
|
||||
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.unwrap())
|
||||
}
|
||||
|
||||
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).await.expect("Failed to send timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
let mut cmd;
|
||||
let timer_service = EspTaskTimerService::new()?;
|
||||
let mut async_timer = timer_service.timer_async()?;
|
||||
loop {
|
||||
match self.state {
|
||||
State::Running => {
|
||||
cmd = self.wait_with_timeout(&mut async_timer).await;
|
||||
}
|
||||
State::Stopped => {
|
||||
cmd = self.wait_for_cmd().await;
|
||||
}
|
||||
}
|
||||
self.handle_cmd(cmd).await;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user