use crate::error::{Error, TimeoutError};
use core::{future::Future, pin::Pin, task::Poll, time::Duration};
#[cfg(not(feature = "std"))]
pub(crate) type Timer = embassy_time::Timer;
#[cfg(all(not(miri), feature = "std"))]
pub(crate) type Timer = async_io::Timer;
#[cfg(miri)]
pub(crate) type Timer = core::future::Pending<()>;
#[cfg(not(feature = "std"))]
pub(crate) fn timer(timeout: LabeledTimeout) -> Timer {
embassy_time::Timer::after(embassy_time::Duration::from_micros(
timeout.duration.as_micros() as u64,
))
}
#[cfg(all(not(miri), feature = "std"))]
pub(crate) fn timer(timeout: LabeledTimeout) -> Timer {
async_io::Timer::after(timeout.duration)
}
#[cfg(miri)]
pub(crate) fn timer(_timeout: LabeledTimeout) -> Timer {
core::future::pending()
}
pub(crate) trait IntoTimeout<O> {
fn timeout(
self,
timeout: LabeledTimeout,
) -> TimeoutFuture<impl Future<Output = Result<O, Error>>>;
}
impl<T, O> IntoTimeout<O> for T
where
T: Future<Output = Result<O, Error>>,
{
fn timeout(
self,
timeout: LabeledTimeout,
) -> TimeoutFuture<impl Future<Output = Result<O, Error>>> {
TimeoutFuture {
f: self,
timeout: timer(timeout),
duration: timeout,
}
}
}
pub(crate) struct TimeoutFuture<F> {
f: F,
timeout: Timer,
duration: LabeledTimeout,
}
impl<F, O> Future for TimeoutFuture<F>
where
F: Future<Output = Result<O, Error>>,
{
type Output = Result<O, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
let timeout = unsafe { Pin::new_unchecked(&mut this.timeout) };
let f = unsafe { Pin::new_unchecked(&mut this.f) };
#[cfg(miri)]
if this.duration.duration == Duration::ZERO {
return Poll::Ready(Err(Error::Timeout(TimeoutError::from_timeout_kind(
this.duration.kind,
))));
}
if timeout.poll(cx).is_ready() {
return Poll::Ready(Err(Error::Timeout(TimeoutError::from_timeout_kind(
this.duration.kind,
))));
}
if let Poll::Ready(x) = f.poll(cx) {
return Poll::Ready(x);
}
Poll::Pending
}
}
#[derive(Copy, Clone, Debug)]
pub struct Timeouts {
pub state_transition: Duration,
pub pdu: Duration,
pub eeprom: Duration,
pub wait_loop_delay: Duration,
pub mailbox_echo: Duration,
pub mailbox_response: Duration,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum TimeoutKind {
StateTransition,
Pdu,
Eeprom,
WaitLoopDelay,
MailboxEcho,
MailboxResponse,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct LabeledTimeout {
pub duration: Duration,
pub kind: TimeoutKind,
}
impl Timeouts {
pub(crate) async fn loop_tick(&self) {
#[cfg(not(miri))]
timer(self.wait_loop_delay()).await;
#[cfg(miri)]
std::thread::yield_now();
}
pub(crate) fn state_transition(self) -> LabeledTimeout {
LabeledTimeout {
duration: (self.state_transition),
kind: TimeoutKind::StateTransition,
}
}
pub(crate) fn pdu(self) -> LabeledTimeout {
LabeledTimeout {
duration: (self.pdu),
kind: TimeoutKind::Pdu,
}
}
pub(crate) fn eeprom(self) -> LabeledTimeout {
LabeledTimeout {
duration: (self.eeprom),
kind: TimeoutKind::Eeprom,
}
}
pub(crate) fn wait_loop_delay(self) -> LabeledTimeout {
LabeledTimeout {
duration: (self.wait_loop_delay),
kind: TimeoutKind::WaitLoopDelay,
}
}
pub(crate) fn mailbox_echo(self) -> LabeledTimeout {
LabeledTimeout {
duration: (self.mailbox_echo),
kind: TimeoutKind::MailboxEcho,
}
}
pub(crate) fn mailbox_response(self) -> LabeledTimeout {
LabeledTimeout {
duration: (self.mailbox_response),
kind: TimeoutKind::MailboxResponse,
}
}
}
impl Default for Timeouts {
fn default() -> Self {
Self {
state_transition: Duration::from_millis(5000),
pdu: Duration::from_micros(30_000),
eeprom: Duration::from_millis(10),
wait_loop_delay: Duration::from_millis(0),
mailbox_echo: Duration::from_millis(100),
mailbox_response: Duration::from_millis(1000),
}
}
}
#[cfg(test)]
pub(crate) const MAX_TIMEOUT: crate::timer_factory::LabeledTimeout =
crate::timer_factory::LabeledTimeout {
duration: Duration::MAX,
kind: crate::timer_factory::TimeoutKind::Pdu,
};
#[cfg(test)]
pub(crate) const MIN_TIMEOUT: crate::timer_factory::LabeledTimeout =
crate::timer_factory::LabeledTimeout {
duration: Duration::ZERO,
kind: crate::timer_factory::TimeoutKind::Pdu,
};