autocore_std/fb/simple_timer.rs
1use std::time::{Duration, Instant};
2
3/// Simple One-Shot Timer
4///
5/// A simple timer for one-shot timing operations. Unlike [`super::Ton`], this timer
6/// does NOT follow the IEC 61131-3 standard and is designed for imperative
7/// "start then check" patterns rather than cyclic function block calls.
8///
9/// **Note:** For standard PLC timer patterns (delay while condition is true),
10/// use [`super::Ton`] instead. `SimpleTimer` is intended for one-shot use cases
11/// like "do X, wait, then do Y".
12///
13/// # Safety
14///
15/// This timer uses [`Instant`] (monotonic clock) internally, making it immune
16/// to system clock adjustments (NTP sync, manual changes, DST). This is
17/// critical for reliable timing in industrial control applications.
18///
19/// # Example
20///
21/// ```
22/// use autocore_std::fb::SimpleTimer;
23/// use std::time::Duration;
24///
25/// let mut timer = SimpleTimer::new(Duration::from_millis(100));
26///
27/// // Timer hasn't started yet
28/// assert_eq!(timer.is_done(), false);
29/// assert_eq!(timer.elapsed(), Duration::ZERO);
30///
31/// // Start the timer
32/// timer.start();
33///
34/// // Check if done (will be false initially)
35/// assert_eq!(timer.is_done(), false);
36///
37/// // After waiting...
38/// std::thread::sleep(Duration::from_millis(110));
39/// assert_eq!(timer.is_done(), true);
40///
41/// // Reset for reuse
42/// timer.reset();
43/// assert_eq!(timer.is_done(), false);
44/// ```
45///
46/// # Use Cases
47///
48/// - One-shot delays ("wait 500ms then continue")
49/// - Tracking time since an event occurred
50/// - Non-cyclic async contexts
51/// - Simple timeout checks
52///
53/// # Comparison with Ton
54///
55/// | Aspect | `Ton` | `SimpleTimer` |
56/// |--------|-------|---------------|
57/// | IEC 61131-3 | Yes | No |
58/// | Pattern | Cyclic `call()` | Imperative `start()`/`is_done()` |
59/// | Auto-reset on disable | Yes | No (explicit `reset()`) |
60/// | Best for | PLC-style control | One-shot operations |
61#[derive(Debug, Clone)]
62pub struct SimpleTimer {
63 start_time: Option<Instant>,
64 preset: Duration,
65}
66
67impl SimpleTimer {
68 /// Creates a new simple timer with the specified preset duration.
69 ///
70 /// The timer starts in the stopped state and must be explicitly started
71 /// with [`start()`](Self::start).
72 ///
73 /// # Arguments
74 ///
75 /// * `preset` - The duration after which [`is_done()`](Self::is_done) returns `true`
76 ///
77 /// # Example
78 ///
79 /// ```
80 /// use autocore_std::fb::SimpleTimer;
81 /// use std::time::Duration;
82 ///
83 /// let timer = SimpleTimer::new(Duration::from_secs(5));
84 /// assert_eq!(timer.is_done(), false);
85 /// ```
86 pub fn new(preset: Duration) -> Self {
87 Self {
88 start_time: None,
89 preset,
90 }
91 }
92
93 /// Starts (or restarts) the timer.
94 ///
95 /// Records the current time as the start time. If the timer was already
96 /// running, this restarts it from zero.
97 ///
98 /// # Example
99 ///
100 /// ```
101 /// use autocore_std::fb::SimpleTimer;
102 /// use std::time::Duration;
103 ///
104 /// let mut timer = SimpleTimer::new(Duration::from_millis(100));
105 /// timer.start();
106 /// // Timer is now counting...
107 /// ```
108 pub fn start(&mut self) {
109 self.start_time = Some(Instant::now());
110 }
111
112 /// Checks if the preset time has elapsed since the timer was started.
113 ///
114 /// Returns `false` if the timer hasn't been started yet.
115 ///
116 /// # Returns
117 ///
118 /// `true` if the timer was started and the preset duration has elapsed,
119 /// `false` otherwise.
120 ///
121 /// # Example
122 ///
123 /// ```
124 /// use autocore_std::fb::SimpleTimer;
125 /// use std::time::Duration;
126 ///
127 /// let mut timer = SimpleTimer::new(Duration::from_millis(50));
128 ///
129 /// // Not started yet
130 /// assert_eq!(timer.is_done(), false);
131 ///
132 /// timer.start();
133 /// std::thread::sleep(Duration::from_millis(60));
134 /// assert_eq!(timer.is_done(), true);
135 /// ```
136 pub fn is_done(&self) -> bool {
137 self.start_time
138 .map(|t| t.elapsed() >= self.preset)
139 .unwrap_or(false)
140 }
141
142 /// Returns the elapsed time since the timer was started.
143 ///
144 /// Returns [`Duration::ZERO`] if the timer hasn't been started yet.
145 ///
146 /// # Returns
147 ///
148 /// The elapsed time since [`start()`](Self::start) was called, or
149 /// `Duration::ZERO` if not started.
150 ///
151 /// # Example
152 ///
153 /// ```
154 /// use autocore_std::fb::SimpleTimer;
155 /// use std::time::Duration;
156 ///
157 /// let mut timer = SimpleTimer::new(Duration::from_secs(10));
158 ///
159 /// // Not started
160 /// assert_eq!(timer.elapsed(), Duration::ZERO);
161 ///
162 /// timer.start();
163 /// std::thread::sleep(Duration::from_millis(50));
164 /// assert!(timer.elapsed() >= Duration::from_millis(50));
165 /// ```
166 pub fn elapsed(&self) -> Duration {
167 self.start_time
168 .map(|t| t.elapsed())
169 .unwrap_or(Duration::ZERO)
170 }
171
172 /// Resets the timer to its initial (stopped) state.
173 ///
174 /// After calling this, [`is_done()`](Self::is_done) will return `false`
175 /// and [`elapsed()`](Self::elapsed) will return zero until
176 /// [`start()`](Self::start) is called again.
177 ///
178 /// # Example
179 ///
180 /// ```
181 /// use autocore_std::fb::SimpleTimer;
182 /// use std::time::Duration;
183 ///
184 /// let mut timer = SimpleTimer::new(Duration::from_millis(50));
185 /// timer.start();
186 /// std::thread::sleep(Duration::from_millis(60));
187 /// assert_eq!(timer.is_done(), true);
188 ///
189 /// timer.reset();
190 /// assert_eq!(timer.is_done(), false);
191 /// assert_eq!(timer.elapsed(), Duration::ZERO);
192 /// ```
193 pub fn reset(&mut self) {
194 self.start_time = None;
195 }
196
197 /// Returns the preset duration for this timer.
198 ///
199 /// # Example
200 ///
201 /// ```
202 /// use autocore_std::fb::SimpleTimer;
203 /// use std::time::Duration;
204 ///
205 /// let timer = SimpleTimer::new(Duration::from_secs(5));
206 /// assert_eq!(timer.preset(), Duration::from_secs(5));
207 /// ```
208 pub fn preset(&self) -> Duration {
209 self.preset
210 }
211
212 /// Sets a new preset duration.
213 ///
214 /// This does not reset or restart the timer. If the timer is running,
215 /// the new preset takes effect immediately for [`is_done()`](Self::is_done)
216 /// checks.
217 ///
218 /// # Arguments
219 ///
220 /// * `preset` - The new duration after which `is_done()` returns `true`
221 ///
222 /// # Example
223 ///
224 /// ```
225 /// use autocore_std::fb::SimpleTimer;
226 /// use std::time::Duration;
227 ///
228 /// let mut timer = SimpleTimer::new(Duration::from_secs(5));
229 /// timer.set_preset(Duration::from_secs(10));
230 /// assert_eq!(timer.preset(), Duration::from_secs(10));
231 /// ```
232 pub fn set_preset(&mut self, preset: Duration) {
233 self.preset = preset;
234 }
235
236 /// Returns whether the timer is currently running (has been started but not reset).
237 ///
238 /// # Example
239 ///
240 /// ```
241 /// use autocore_std::fb::SimpleTimer;
242 /// use std::time::Duration;
243 ///
244 /// let mut timer = SimpleTimer::new(Duration::from_secs(5));
245 /// assert_eq!(timer.is_running(), false);
246 ///
247 /// timer.start();
248 /// assert_eq!(timer.is_running(), true);
249 ///
250 /// timer.reset();
251 /// assert_eq!(timer.is_running(), false);
252 /// ```
253 pub fn is_running(&self) -> bool {
254 self.start_time.is_some()
255 }
256}
257
258impl Default for SimpleTimer {
259 /// Creates a timer with a preset of zero (will be immediately done when started).
260 fn default() -> Self {
261 Self::new(Duration::ZERO)
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn test_simple_timer_basic() {
271 let mut timer = SimpleTimer::new(Duration::from_millis(50));
272
273 // Not started yet
274 assert_eq!(timer.is_done(), false);
275 assert_eq!(timer.is_running(), false);
276 assert_eq!(timer.elapsed(), Duration::ZERO);
277
278 // Start
279 timer.start();
280 assert_eq!(timer.is_running(), true);
281 assert_eq!(timer.is_done(), false);
282
283 // Wait for timer
284 std::thread::sleep(Duration::from_millis(60));
285 assert_eq!(timer.is_done(), true);
286 assert!(timer.elapsed() >= Duration::from_millis(50));
287
288 // Reset
289 timer.reset();
290 assert_eq!(timer.is_done(), false);
291 assert_eq!(timer.is_running(), false);
292 assert_eq!(timer.elapsed(), Duration::ZERO);
293 }
294
295 #[test]
296 fn test_simple_timer_restart() {
297 let mut timer = SimpleTimer::new(Duration::from_millis(100));
298
299 timer.start();
300 std::thread::sleep(Duration::from_millis(30));
301
302 // Restart resets the timer
303 timer.start();
304 assert!(timer.elapsed() < Duration::from_millis(20));
305 }
306
307 #[test]
308 fn test_simple_timer_preset() {
309 let mut timer = SimpleTimer::new(Duration::from_secs(5));
310
311 assert_eq!(timer.preset(), Duration::from_secs(5));
312
313 timer.set_preset(Duration::from_secs(10));
314 assert_eq!(timer.preset(), Duration::from_secs(10));
315 }
316
317 #[test]
318 fn test_simple_timer_default() {
319 let mut timer = SimpleTimer::default();
320
321 // Default preset is zero, so immediately done when started
322 assert_eq!(timer.preset(), Duration::ZERO);
323
324 timer.start();
325 assert_eq!(timer.is_done(), true);
326 }
327}