1use core::marker::PhantomData;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
60pub struct TimerDuration {
61 micros: u64,
70}
71
72impl TimerDuration {
73 pub const fn from_millis(millis: u64) -> Self {
75 Self {
76 micros: millis.saturating_mul(1000),
77 }
78 }
79
80 pub const fn from_secs(secs: u64) -> Self {
82 Self {
83 micros: secs.saturating_mul(1_000_000),
84 }
85 }
86
87 pub const fn from_micros(micros: u64) -> Self {
89 Self { micros }
90 }
91
92 pub const fn zero() -> Self {
94 Self { micros: 0 }
95 }
96
97 pub const fn as_millis(&self) -> u64 {
100 self.micros / 1000
101 }
102
103 pub const fn as_micros(&self) -> u64 {
105 self.micros
106 }
107
108 pub const fn as_secs(&self) -> u64 {
110 self.micros / 1_000_000
111 }
112
113 pub const fn is_zero(&self) -> bool {
115 self.micros == 0
116 }
117
118 pub const fn saturating_sub(self, rhs: Self) -> Self {
120 Self {
121 micros: self.micros.saturating_sub(rhs.micros),
122 }
123 }
124}
125
126impl From<nros_core::Duration> for TimerDuration {
127 fn from(d: nros_core::Duration) -> Self {
128 let micros = (d.sec as i64 * 1_000_000 + d.nanosec as i64 / 1_000) as u64;
129 Self { micros }
130 }
131}
132
133impl From<TimerDuration> for nros_core::Duration {
134 fn from(d: TimerDuration) -> Self {
135 let sec = (d.micros / 1_000_000) as i32;
136 let nanosec = ((d.micros % 1_000_000) * 1_000) as u32;
137 nros_core::Duration { sec, nanosec }
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum TimerMode {
144 Repeating,
146 OneShot,
148 Inert,
150}
151
152pub type TimerCallbackFn = fn();
154
155pub struct TimerState {
159 period_ms: u64,
161 elapsed_ms: u64,
163 mode: TimerMode,
165 canceled: bool,
167 callback_fn: Option<TimerCallbackFn>,
169}
170
171impl core::fmt::Debug for TimerState {
172 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173 f.debug_struct("TimerState")
174 .field("period_ms", &self.period_ms)
175 .field("elapsed_ms", &self.elapsed_ms)
176 .field("mode", &self.mode)
177 .field("canceled", &self.canceled)
178 .field("has_callback_fn", &self.callback_fn.is_some())
179 .finish()
180 }
181}
182
183impl TimerState {
184 pub fn new_with_fn(period: TimerDuration, mode: TimerMode, callback: TimerCallbackFn) -> Self {
186 Self {
187 period_ms: period.as_millis(),
188 elapsed_ms: 0,
189 mode,
190 canceled: false,
191 callback_fn: Some(callback),
192 }
193 }
194
195 pub fn new_inert(period: TimerDuration) -> Self {
197 Self {
198 period_ms: period.as_millis(),
199 elapsed_ms: 0,
200 mode: TimerMode::Inert,
201 canceled: false,
202 callback_fn: None,
203 }
204 }
205
206 pub fn period(&self) -> TimerDuration {
208 TimerDuration::from_millis(self.period_ms)
209 }
210
211 pub fn mode(&self) -> TimerMode {
213 self.mode
214 }
215
216 pub fn is_canceled(&self) -> bool {
218 self.canceled
219 }
220
221 pub fn cancel(&mut self) {
223 self.canceled = true;
224 }
225
226 pub fn reset(&mut self) {
228 self.canceled = false;
229 self.elapsed_ms = 0;
230 }
231
232 pub fn is_ready(&self) -> bool {
234 !self.canceled && self.mode != TimerMode::Inert && self.elapsed_ms >= self.period_ms
235 }
236
237 pub fn time_until_next_call(&self) -> TimerDuration {
239 if self.canceled || self.mode == TimerMode::Inert {
240 return TimerDuration::from_millis(u64::MAX);
241 }
242 if self.elapsed_ms >= self.period_ms {
243 TimerDuration::zero()
244 } else {
245 TimerDuration::from_millis(self.period_ms - self.elapsed_ms)
246 }
247 }
248
249 pub fn time_since_last_call(&self) -> TimerDuration {
251 TimerDuration::from_millis(self.elapsed_ms)
252 }
253
254 pub fn set_callback_fn(&mut self, callback: TimerCallbackFn) {
256 self.callback_fn = Some(callback);
257 }
258
259 pub fn set_repeating(&mut self) {
261 self.mode = TimerMode::Repeating;
262 }
263
264 pub fn set_oneshot(&mut self) {
266 self.mode = TimerMode::OneShot;
267 }
268
269 pub fn set_inert(&mut self) {
271 self.mode = TimerMode::Inert;
272 }
273
274 #[allow(dead_code)] pub(crate) fn update(&mut self, delta_ms: u64) -> bool {
277 if self.canceled || self.mode == TimerMode::Inert {
278 return false;
279 }
280
281 self.elapsed_ms = self.elapsed_ms.saturating_add(delta_ms);
282
283 self.elapsed_ms >= self.period_ms
284 }
285
286 #[allow(dead_code)] pub(crate) fn fire(&mut self) {
289 if let Some(ref callback) = self.callback_fn {
291 callback();
292 }
293
294 match self.mode {
296 TimerMode::Repeating => {
297 self.elapsed_ms = self.elapsed_ms.saturating_sub(self.period_ms);
299 }
300 TimerMode::OneShot => {
301 self.mode = TimerMode::Inert;
303 self.elapsed_ms = 0;
304 }
305 TimerMode::Inert => {
306 }
308 }
309 }
310}
311
312#[derive(Debug, Clone, Copy)]
321pub struct TimerHandle<C = TimerCallbackFn> {
322 index: usize,
324 _marker: PhantomData<C>,
326}
327
328impl<C> TimerHandle<C> {
329 #[allow(dead_code)] pub(crate) fn new(index: usize) -> Self {
332 Self {
333 index,
334 _marker: PhantomData,
335 }
336 }
337
338 pub fn index(&self) -> usize {
340 self.index
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 #[test]
349 fn test_timer_duration() {
350 let d = TimerDuration::from_millis(1500);
351 assert_eq!(d.as_millis(), 1500);
352 assert_eq!(d.as_secs(), 1);
353
354 let d2 = TimerDuration::from_secs(2);
355 assert_eq!(d2.as_millis(), 2000);
356
357 let d3 = TimerDuration::from_micros(5500);
358 assert_eq!(d3.as_millis(), 5);
359 }
360
361 #[test]
362 fn test_timer_duration_conversion() {
363 let ros_dur = nros_core::Duration::from_millis(1500);
364 let timer_dur: TimerDuration = ros_dur.into();
365 assert_eq!(timer_dur.as_millis(), 1500);
366
367 let back: nros_core::Duration = timer_dur.into();
368 assert_eq!(back.sec, 1);
369 assert_eq!(back.nanosec, 500_000_000);
370 }
371
372 fn test_callback() {
373 }
375
376 #[test]
377 fn test_timer_state_repeating() {
378 let mut state = TimerState::new_with_fn(
379 TimerDuration::from_millis(100),
380 TimerMode::Repeating,
381 test_callback,
382 );
383
384 assert_eq!(state.period().as_millis(), 100);
385 assert_eq!(state.mode(), TimerMode::Repeating);
386 assert!(!state.is_canceled());
387 assert!(!state.is_ready());
388
389 assert!(!state.update(50));
391 assert!(!state.is_ready());
392 assert_eq!(state.time_until_next_call().as_millis(), 50);
393 assert_eq!(state.time_since_last_call().as_millis(), 50);
394
395 assert!(state.update(50));
397 assert!(state.is_ready());
398 assert_eq!(state.time_until_next_call().as_millis(), 0);
399
400 state.fire();
402 assert_eq!(state.mode(), TimerMode::Repeating);
403 assert!(!state.is_ready());
404 }
405
406 #[test]
407 fn test_timer_state_oneshot() {
408 let mut state = TimerState::new_with_fn(
409 TimerDuration::from_millis(100),
410 TimerMode::OneShot,
411 test_callback,
412 );
413
414 assert_eq!(state.mode(), TimerMode::OneShot);
415
416 state.update(100);
418 assert!(state.is_ready());
419
420 state.fire();
422 assert_eq!(state.mode(), TimerMode::Inert);
423 assert!(!state.is_ready());
424 }
425
426 #[test]
427 fn test_timer_state_inert() {
428 let state = TimerState::new_inert(TimerDuration::from_millis(100));
429
430 assert_eq!(state.mode(), TimerMode::Inert);
431 assert!(!state.is_ready());
432 }
433
434 #[test]
435 fn test_timer_cancel_reset() {
436 let mut state = TimerState::new_with_fn(
437 TimerDuration::from_millis(100),
438 TimerMode::Repeating,
439 test_callback,
440 );
441
442 state.update(50);
443 state.cancel();
444 assert!(state.is_canceled());
445 assert!(!state.is_ready());
446
447 state.update(100);
448 assert!(!state.is_ready()); state.reset();
451 assert!(!state.is_canceled());
452 assert_eq!(state.time_since_last_call().as_millis(), 0);
453 }
454
455 #[test]
456 fn test_timer_mode_changes() {
457 let mut state = TimerState::new_with_fn(
458 TimerDuration::from_millis(100),
459 TimerMode::Repeating,
460 test_callback,
461 );
462
463 state.set_oneshot();
464 assert_eq!(state.mode(), TimerMode::OneShot);
465
466 state.set_inert();
467 assert_eq!(state.mode(), TimerMode::Inert);
468
469 state.set_repeating();
470 assert_eq!(state.mode(), TimerMode::Repeating);
471 }
472
473 #[test]
474 fn test_timer_handle() {
475 let handle: TimerHandle = TimerHandle::new(5);
476 assert_eq!(handle.index(), 5);
477 }
478}
479
480#[cfg(test)]
485mod ghost_checks {
486 use super::*;
487 use nros_ghost_types::{TimerGhost, TimerModeGhost};
488
489 fn ghost_mode(m: &TimerMode) -> TimerModeGhost {
492 match m {
493 TimerMode::Repeating => TimerModeGhost::Repeating,
494 TimerMode::OneShot => TimerModeGhost::OneShot,
495 TimerMode::Inert => TimerModeGhost::Inert,
496 }
497 }
498
499 fn ghost_from_timer(t: &TimerState) -> TimerGhost {
502 TimerGhost {
503 period_ms: t.period_ms,
504 elapsed_ms: t.elapsed_ms,
505 mode: ghost_mode(&t.mode),
506 canceled: t.canceled,
507 }
508 }
509
510 fn test_callback() {}
511
512 #[test]
513 fn ghost_new_state() {
514 let state = TimerState::new_with_fn(
515 TimerDuration::from_millis(100),
516 TimerMode::Repeating,
517 test_callback,
518 );
519 let ghost = ghost_from_timer(&state);
520 assert_eq!(ghost.period_ms, 100);
521 assert_eq!(ghost.elapsed_ms, 0);
522 assert_eq!(ghost.mode, TimerModeGhost::Repeating);
523 assert!(!ghost.canceled);
524 }
525
526 #[test]
527 fn ghost_update_accumulates() {
528 let mut state = TimerState::new_with_fn(
529 TimerDuration::from_millis(100),
530 TimerMode::Repeating,
531 test_callback,
532 );
533 state.update(30);
534 let ghost = ghost_from_timer(&state);
535 assert_eq!(ghost.elapsed_ms, 30);
536
537 state.update(25);
538 let ghost2 = ghost_from_timer(&state);
539 assert_eq!(ghost2.elapsed_ms, 55);
540 }
541
542 #[test]
543 fn ghost_canceled_no_fire() {
544 let mut state = TimerState::new_with_fn(
545 TimerDuration::from_millis(100),
546 TimerMode::Repeating,
547 test_callback,
548 );
549 state.cancel();
550 let fired = state.update(200);
551 assert!(!fired);
552 let ghost = ghost_from_timer(&state);
553 assert!(ghost.canceled);
554 }
555
556 #[test]
557 fn ghost_inert_no_fire() {
558 let mut state = TimerState::new_inert(TimerDuration::from_millis(100));
559 let fired = state.update(200);
560 assert!(!fired);
561 let ghost = ghost_from_timer(&state);
562 assert_eq!(ghost.mode, TimerModeGhost::Inert);
563 }
564
565 #[test]
566 fn ghost_oneshot_becomes_inert() {
567 let mut state = TimerState::new_with_fn(
568 TimerDuration::from_millis(100),
569 TimerMode::OneShot,
570 test_callback,
571 );
572 state.update(100);
573 state.fire();
574 let ghost = ghost_from_timer(&state);
575 assert_eq!(ghost.mode, TimerModeGhost::Inert);
576 }
577}