1use crate::fraction::Fraction;
4use crate::{
5 duration::{self, *},
6 fixed_point::FixedPoint,
7 timer::param::*,
8 ConversionError, Instant, TimeError,
9};
10use core::{convert::TryFrom, marker::PhantomData, ops::Add, prelude::v1::*};
11
12pub mod param {
14 #[derive(Debug, Hash)]
16 pub struct None;
17
18 #[derive(Debug, Hash)]
20 pub struct Armed;
21
22 #[derive(Debug, Hash)]
24 pub struct Running;
25
26 #[derive(Debug, Hash)]
28 pub struct Periodic;
29
30 #[derive(Debug, Hash)]
32 pub struct OneShot;
33}
34
35#[derive(Debug, Hash)]
38pub struct Timer<'a, Type, State, Clock: crate::Clock, Dur: Duration> {
39 clock: &'a Clock,
40 duration: Dur,
41 expiration: Instant<Clock>,
42 _type: PhantomData<Type>,
43 _state: PhantomData<State>,
44}
45
46impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'_, param::None, param::None, Clock, Dur> {
47 #[allow(clippy::new_ret_no_self)]
49 pub fn new(clock: &Clock, duration: Dur) -> Timer<OneShot, Armed, Clock, Dur> {
50 Timer::<OneShot, Armed, Clock, Dur> {
51 clock,
52 duration,
53 expiration: Instant::new(Clock::T::from(0)),
54 _type: PhantomData,
55 _state: PhantomData,
56 }
57 }
58}
59
60impl<'a, Type, State, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, State, Clock, Dur> {
61 pub fn into_oneshot(self) -> Timer<'a, OneShot, State, Clock, Dur> {
63 Timer::<OneShot, State, Clock, Dur> {
64 clock: self.clock,
65 duration: self.duration,
66 expiration: self.expiration,
67 _type: PhantomData,
68 _state: PhantomData,
69 }
70 }
71
72 pub fn into_periodic(self) -> Timer<'a, Periodic, State, Clock, Dur> {
74 Timer::<Periodic, State, Clock, Dur> {
75 clock: self.clock,
76 duration: self.duration,
77 expiration: self.expiration,
78 _type: PhantomData,
79 _state: PhantomData,
80 }
81 }
82}
83
84impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Armed, Clock, Dur> {
85 pub fn start(self) -> Result<Timer<'a, Type, Running, Clock, Dur>, TimeError>
87 where
88 Clock::T: TryFrom<Dur::T>,
89 Dur: FixedPoint,
90 {
91 Ok(Timer::<Type, Running, Clock, Dur> {
92 clock: self.clock,
93 duration: self.duration,
94 expiration: self
95 .clock
96 .try_now()?
97 .checked_add(self.duration)
98 .ok_or(ConversionError::Overflow)?,
99 _type: PhantomData,
100 _state: PhantomData,
101 })
102 }
103}
104
105impl<Type, Clock: crate::Clock, Dur: Duration> Timer<'_, Type, Running, Clock, Dur> {
106 fn _is_expired(&self) -> Result<bool, TimeError> {
107 Ok(self.clock.try_now()? >= self.expiration)
108 }
109
110 pub fn elapsed(&self) -> Result<Dur, TimeError>
116 where
117 Dur: FixedPoint + TryFrom<duration::Generic<Clock::T>, Error = ConversionError>,
118 Dur::T: TryFrom<Clock::T>,
119 Clock::T: TryFrom<Dur::T>,
120 {
121 let generic_duration = self
122 .clock
123 .try_now()?
124 .checked_duration_since(
125 &(self
126 .expiration
127 .checked_sub(self.duration)
128 .ok_or(ConversionError::Overflow)?),
129 )
130 .ok_or(TimeError::Overflow)?;
131
132 Ok(Dur::try_from(generic_duration)?)
133 }
134
135 pub fn remaining(&self) -> Result<Dur, TimeError>
141 where
142 Dur: FixedPoint + TryFrom<duration::Generic<Clock::T>, Error = ConversionError>,
143 Dur::T: TryFrom<u32> + TryFrom<Clock::T>,
144 Clock::T: TryFrom<Dur::T>,
145 {
146 let result = self
147 .expiration
148 .checked_duration_since(&self.clock.try_now()?)
149 .or_else(|| {
150 Some(duration::Generic::<Clock::T>::new(
151 0.into(),
152 Fraction::default(),
153 ))
154 })
155 .ok_or(TimeError::NegDuration)?;
156
157 Ok(Dur::try_from(result)?)
158 }
159}
160
161impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'a, OneShot, Running, Clock, Dur> {
162 pub fn wait(self) -> Result<Timer<'a, OneShot, Armed, Clock, Dur>, TimeError> {
164 while !self._is_expired()? {}
166
167 Ok(Timer::<param::None, param::None, Clock, Dur>::new(
168 self.clock,
169 self.duration,
170 ))
171 }
172
173 pub fn is_expired(&self) -> Result<bool, TimeError> {
177 self._is_expired()
178 }
179}
180
181impl<Clock: crate::Clock, Dur: Duration> Timer<'_, Periodic, Running, Clock, Dur> {
182 pub fn wait(self) -> Result<Self, TimeError>
186 where
187 Instant<Clock>: Add<Dur, Output = Instant<Clock>>,
188 {
189 while !self._is_expired()? {}
191
192 Ok(Self {
193 clock: self.clock,
194 duration: self.duration,
195 expiration: self.expiration + self.duration,
198 _type: PhantomData,
199 _state: PhantomData,
200 })
201 }
202
203 pub fn period_complete(&mut self) -> Result<bool, TimeError>
207 where
208 Instant<Clock>: Add<Dur, Output = Instant<Clock>>,
209 {
210 if self._is_expired()? {
212 self.expiration = self.expiration + self.duration;
215
216 Ok(true)
217 } else {
218 Ok(false)
219 }
220 }
221}
222
223#[cfg(test)]
224mod test {}