1use core::marker::PhantomData;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
60pub struct TimerDuration {
61 millis: u64,
63}
64
65impl TimerDuration {
66 pub const fn from_millis(millis: u64) -> Self {
68 Self { millis }
69 }
70
71 pub const fn from_secs(secs: u64) -> Self {
73 Self {
74 millis: secs * 1000,
75 }
76 }
77
78 pub const fn from_micros(micros: u64) -> Self {
80 Self {
81 millis: micros / 1000,
82 }
83 }
84
85 pub const fn zero() -> Self {
87 Self { millis: 0 }
88 }
89
90 pub const fn as_millis(&self) -> u64 {
92 self.millis
93 }
94
95 pub const fn as_secs(&self) -> u64 {
97 self.millis / 1000
98 }
99
100 pub const fn is_zero(&self) -> bool {
102 self.millis == 0
103 }
104
105 pub const fn saturating_sub(self, rhs: Self) -> Self {
107 Self {
108 millis: self.millis.saturating_sub(rhs.millis),
109 }
110 }
111}
112
113impl From<nros_core::Duration> for TimerDuration {
114 fn from(d: nros_core::Duration) -> Self {
115 let millis = (d.sec as i64 * 1000 + d.nanosec as i64 / 1_000_000) as u64;
116 Self { millis }
117 }
118}
119
120impl From<TimerDuration> for nros_core::Duration {
121 fn from(d: TimerDuration) -> Self {
122 let sec = (d.millis / 1000) as i32;
123 let nanosec = ((d.millis % 1000) * 1_000_000) as u32;
124 nros_core::Duration { sec, nanosec }
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub enum TimerMode {
131 Repeating,
133 OneShot,
135 Inert,
137}
138
139pub type TimerCallbackFn = fn();
141
142pub struct TimerState {
146 period_ms: u64,
148 elapsed_ms: u64,
150 mode: TimerMode,
152 canceled: bool,
154 callback_fn: Option<TimerCallbackFn>,
156}
157
158impl core::fmt::Debug for TimerState {
159 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
160 f.debug_struct("TimerState")
161 .field("period_ms", &self.period_ms)
162 .field("elapsed_ms", &self.elapsed_ms)
163 .field("mode", &self.mode)
164 .field("canceled", &self.canceled)
165 .field("has_callback_fn", &self.callback_fn.is_some())
166 .finish()
167 }
168}
169
170impl TimerState {
171 pub fn new_with_fn(period: TimerDuration, mode: TimerMode, callback: TimerCallbackFn) -> Self {
173 Self {
174 period_ms: period.as_millis(),
175 elapsed_ms: 0,
176 mode,
177 canceled: false,
178 callback_fn: Some(callback),
179 }
180 }
181
182 pub fn new_inert(period: TimerDuration) -> Self {
184 Self {
185 period_ms: period.as_millis(),
186 elapsed_ms: 0,
187 mode: TimerMode::Inert,
188 canceled: false,
189 callback_fn: None,
190 }
191 }
192
193 pub fn period(&self) -> TimerDuration {
195 TimerDuration::from_millis(self.period_ms)
196 }
197
198 pub fn mode(&self) -> TimerMode {
200 self.mode
201 }
202
203 pub fn is_canceled(&self) -> bool {
205 self.canceled
206 }
207
208 pub fn cancel(&mut self) {
210 self.canceled = true;
211 }
212
213 pub fn reset(&mut self) {
215 self.canceled = false;
216 self.elapsed_ms = 0;
217 }
218
219 pub fn is_ready(&self) -> bool {
221 !self.canceled && self.mode != TimerMode::Inert && self.elapsed_ms >= self.period_ms
222 }
223
224 pub fn time_until_next_call(&self) -> TimerDuration {
226 if self.canceled || self.mode == TimerMode::Inert {
227 return TimerDuration::from_millis(u64::MAX);
228 }
229 if self.elapsed_ms >= self.period_ms {
230 TimerDuration::zero()
231 } else {
232 TimerDuration::from_millis(self.period_ms - self.elapsed_ms)
233 }
234 }
235
236 pub fn time_since_last_call(&self) -> TimerDuration {
238 TimerDuration::from_millis(self.elapsed_ms)
239 }
240
241 pub fn set_callback_fn(&mut self, callback: TimerCallbackFn) {
243 self.callback_fn = Some(callback);
244 }
245
246 pub fn set_repeating(&mut self) {
248 self.mode = TimerMode::Repeating;
249 }
250
251 pub fn set_oneshot(&mut self) {
253 self.mode = TimerMode::OneShot;
254 }
255
256 pub fn set_inert(&mut self) {
258 self.mode = TimerMode::Inert;
259 }
260
261 #[allow(dead_code)] pub(crate) fn update(&mut self, delta_ms: u64) -> bool {
264 if self.canceled || self.mode == TimerMode::Inert {
265 return false;
266 }
267
268 self.elapsed_ms = self.elapsed_ms.saturating_add(delta_ms);
269
270 self.elapsed_ms >= self.period_ms
271 }
272
273 #[allow(dead_code)] pub(crate) fn fire(&mut self) {
276 if let Some(ref callback) = self.callback_fn {
278 callback();
279 }
280
281 match self.mode {
283 TimerMode::Repeating => {
284 self.elapsed_ms = self.elapsed_ms.saturating_sub(self.period_ms);
286 }
287 TimerMode::OneShot => {
288 self.mode = TimerMode::Inert;
290 self.elapsed_ms = 0;
291 }
292 TimerMode::Inert => {
293 }
295 }
296 }
297}
298
299#[derive(Debug, Clone, Copy)]
308pub struct TimerHandle<C = TimerCallbackFn> {
309 index: usize,
311 _marker: PhantomData<C>,
313}
314
315impl<C> TimerHandle<C> {
316 #[allow(dead_code)] pub(crate) fn new(index: usize) -> Self {
319 Self {
320 index,
321 _marker: PhantomData,
322 }
323 }
324
325 pub fn index(&self) -> usize {
327 self.index
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_timer_duration() {
337 let d = TimerDuration::from_millis(1500);
338 assert_eq!(d.as_millis(), 1500);
339 assert_eq!(d.as_secs(), 1);
340
341 let d2 = TimerDuration::from_secs(2);
342 assert_eq!(d2.as_millis(), 2000);
343
344 let d3 = TimerDuration::from_micros(5500);
345 assert_eq!(d3.as_millis(), 5);
346 }
347
348 #[test]
349 fn test_timer_duration_conversion() {
350 let ros_dur = nros_core::Duration::from_millis(1500);
351 let timer_dur: TimerDuration = ros_dur.into();
352 assert_eq!(timer_dur.as_millis(), 1500);
353
354 let back: nros_core::Duration = timer_dur.into();
355 assert_eq!(back.sec, 1);
356 assert_eq!(back.nanosec, 500_000_000);
357 }
358
359 fn test_callback() {
360 }
362
363 #[test]
364 fn test_timer_state_repeating() {
365 let mut state = TimerState::new_with_fn(
366 TimerDuration::from_millis(100),
367 TimerMode::Repeating,
368 test_callback,
369 );
370
371 assert_eq!(state.period().as_millis(), 100);
372 assert_eq!(state.mode(), TimerMode::Repeating);
373 assert!(!state.is_canceled());
374 assert!(!state.is_ready());
375
376 assert!(!state.update(50));
378 assert!(!state.is_ready());
379 assert_eq!(state.time_until_next_call().as_millis(), 50);
380 assert_eq!(state.time_since_last_call().as_millis(), 50);
381
382 assert!(state.update(50));
384 assert!(state.is_ready());
385 assert_eq!(state.time_until_next_call().as_millis(), 0);
386
387 state.fire();
389 assert_eq!(state.mode(), TimerMode::Repeating);
390 assert!(!state.is_ready());
391 }
392
393 #[test]
394 fn test_timer_state_oneshot() {
395 let mut state = TimerState::new_with_fn(
396 TimerDuration::from_millis(100),
397 TimerMode::OneShot,
398 test_callback,
399 );
400
401 assert_eq!(state.mode(), TimerMode::OneShot);
402
403 state.update(100);
405 assert!(state.is_ready());
406
407 state.fire();
409 assert_eq!(state.mode(), TimerMode::Inert);
410 assert!(!state.is_ready());
411 }
412
413 #[test]
414 fn test_timer_state_inert() {
415 let state = TimerState::new_inert(TimerDuration::from_millis(100));
416
417 assert_eq!(state.mode(), TimerMode::Inert);
418 assert!(!state.is_ready());
419 }
420
421 #[test]
422 fn test_timer_cancel_reset() {
423 let mut state = TimerState::new_with_fn(
424 TimerDuration::from_millis(100),
425 TimerMode::Repeating,
426 test_callback,
427 );
428
429 state.update(50);
430 state.cancel();
431 assert!(state.is_canceled());
432 assert!(!state.is_ready());
433
434 state.update(100);
435 assert!(!state.is_ready()); state.reset();
438 assert!(!state.is_canceled());
439 assert_eq!(state.time_since_last_call().as_millis(), 0);
440 }
441
442 #[test]
443 fn test_timer_mode_changes() {
444 let mut state = TimerState::new_with_fn(
445 TimerDuration::from_millis(100),
446 TimerMode::Repeating,
447 test_callback,
448 );
449
450 state.set_oneshot();
451 assert_eq!(state.mode(), TimerMode::OneShot);
452
453 state.set_inert();
454 assert_eq!(state.mode(), TimerMode::Inert);
455
456 state.set_repeating();
457 assert_eq!(state.mode(), TimerMode::Repeating);
458 }
459
460 #[test]
461 fn test_timer_handle() {
462 let handle: TimerHandle = TimerHandle::new(5);
463 assert_eq!(handle.index(), 5);
464 }
465}
466
467#[cfg(test)]
472mod ghost_checks {
473 use super::*;
474 use nros_ghost_types::{TimerGhost, TimerModeGhost};
475
476 fn ghost_mode(m: &TimerMode) -> TimerModeGhost {
479 match m {
480 TimerMode::Repeating => TimerModeGhost::Repeating,
481 TimerMode::OneShot => TimerModeGhost::OneShot,
482 TimerMode::Inert => TimerModeGhost::Inert,
483 }
484 }
485
486 fn ghost_from_timer(t: &TimerState) -> TimerGhost {
489 TimerGhost {
490 period_ms: t.period_ms,
491 elapsed_ms: t.elapsed_ms,
492 mode: ghost_mode(&t.mode),
493 canceled: t.canceled,
494 }
495 }
496
497 fn test_callback() {}
498
499 #[test]
500 fn ghost_new_state() {
501 let state = TimerState::new_with_fn(
502 TimerDuration::from_millis(100),
503 TimerMode::Repeating,
504 test_callback,
505 );
506 let ghost = ghost_from_timer(&state);
507 assert_eq!(ghost.period_ms, 100);
508 assert_eq!(ghost.elapsed_ms, 0);
509 assert_eq!(ghost.mode, TimerModeGhost::Repeating);
510 assert!(!ghost.canceled);
511 }
512
513 #[test]
514 fn ghost_update_accumulates() {
515 let mut state = TimerState::new_with_fn(
516 TimerDuration::from_millis(100),
517 TimerMode::Repeating,
518 test_callback,
519 );
520 state.update(30);
521 let ghost = ghost_from_timer(&state);
522 assert_eq!(ghost.elapsed_ms, 30);
523
524 state.update(25);
525 let ghost2 = ghost_from_timer(&state);
526 assert_eq!(ghost2.elapsed_ms, 55);
527 }
528
529 #[test]
530 fn ghost_canceled_no_fire() {
531 let mut state = TimerState::new_with_fn(
532 TimerDuration::from_millis(100),
533 TimerMode::Repeating,
534 test_callback,
535 );
536 state.cancel();
537 let fired = state.update(200);
538 assert!(!fired);
539 let ghost = ghost_from_timer(&state);
540 assert!(ghost.canceled);
541 }
542
543 #[test]
544 fn ghost_inert_no_fire() {
545 let mut state = TimerState::new_inert(TimerDuration::from_millis(100));
546 let fired = state.update(200);
547 assert!(!fired);
548 let ghost = ghost_from_timer(&state);
549 assert_eq!(ghost.mode, TimerModeGhost::Inert);
550 }
551
552 #[test]
553 fn ghost_oneshot_becomes_inert() {
554 let mut state = TimerState::new_with_fn(
555 TimerDuration::from_millis(100),
556 TimerMode::OneShot,
557 test_callback,
558 );
559 state.update(100);
560 state.fire();
561 let ghost = ghost_from_timer(&state);
562 assert_eq!(ghost.mode, TimerModeGhost::Inert);
563 }
564}