Skip to main content

cu29_clock/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4#[cfg(test)]
5extern crate approx;
6
7mod calibration;
8#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
9#[cfg_attr(all(target_os = "none", target_arch = "arm"), path = "cortexm.rs")]
10#[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")]
11#[cfg_attr(
12    all(feature = "std", target_arch = "wasm32", target_os = "unknown"),
13    path = "wasm.rs"
14)]
15#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
16#[cfg_attr(
17    not(any(
18        target_arch = "x86_64",
19        target_arch = "aarch64",
20        all(target_os = "none", target_arch = "arm"),
21        target_arch = "riscv64",
22        all(feature = "std", target_arch = "wasm32", target_os = "unknown")
23    )),
24    path = "fallback.rs"
25)]
26mod raw_counter;
27
28pub use raw_counter::*;
29
30#[cfg(feature = "reflect")]
31use bevy_reflect::Reflect;
32use bincode::BorrowDecode;
33use bincode::de::BorrowDecoder;
34use bincode::de::Decoder;
35use bincode::enc::Encoder;
36use bincode::error::{DecodeError, EncodeError};
37use bincode::{Decode, Encode};
38use core::ops::{Add, Sub};
39use serde::{Deserialize, Serialize};
40
41// We use this to be able to support embedded 32bit platforms
42use portable_atomic::{AtomicU64, Ordering};
43
44use alloc::format;
45use alloc::sync::Arc;
46use alloc::vec::Vec;
47use core::fmt::{Display, Formatter};
48use core::ops::{AddAssign, Div, Mul, SubAssign};
49
50/// High-precision instant in time, represented as nanoseconds since an arbitrary epoch
51#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
52pub struct CuInstant(u64);
53
54pub type Instant = CuInstant; // Backward compatibility
55
56impl CuInstant {
57    pub fn now() -> Self {
58        CuInstant(calibration::counter_to_nanos(read_raw_counter))
59    }
60
61    pub fn as_nanos(&self) -> u64 {
62        self.0
63    }
64}
65
66impl Sub for CuInstant {
67    type Output = CuDuration;
68
69    fn sub(self, other: CuInstant) -> CuDuration {
70        CuDuration(self.0.saturating_sub(other.0))
71    }
72}
73
74impl Sub<CuDuration> for CuInstant {
75    type Output = CuInstant;
76
77    fn sub(self, duration: CuDuration) -> CuInstant {
78        CuInstant(self.0.saturating_sub(duration.as_nanos()))
79    }
80}
81
82impl Add<CuDuration> for CuInstant {
83    type Output = CuInstant;
84
85    fn add(self, duration: CuDuration) -> CuInstant {
86        CuInstant(self.0.saturating_add(duration.as_nanos()))
87    }
88}
89
90/// For Robot times, the underlying type is a u64 representing nanoseconds.
91/// It is always positive to simplify the reasoning on the user side.
92#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
93#[cfg_attr(feature = "reflect", derive(Reflect))]
94pub struct CuDuration(pub u64);
95
96impl CuDuration {
97    // Lowest value a CuDuration can have.
98    pub const MIN: CuDuration = CuDuration(0u64);
99    // Highest value a CuDuration can have reserving the max value for None.
100    pub const MAX: CuDuration = CuDuration(NONE_VALUE - 1);
101
102    pub fn max(self, other: CuDuration) -> CuDuration {
103        let Self(lhs) = self;
104        let Self(rhs) = other;
105        CuDuration(lhs.max(rhs))
106    }
107
108    pub fn min(self, other: CuDuration) -> CuDuration {
109        let Self(lhs) = self;
110        let Self(rhs) = other;
111        CuDuration(lhs.min(rhs))
112    }
113
114    pub fn as_nanos(&self) -> u64 {
115        let Self(nanos) = self;
116        *nanos
117    }
118
119    pub fn as_micros(&self) -> u64 {
120        let Self(nanos) = self;
121        nanos / 1_000
122    }
123
124    pub fn as_millis(&self) -> u64 {
125        let Self(nanos) = self;
126        nanos / 1_000_000
127    }
128
129    pub fn as_secs(&self) -> u64 {
130        let Self(nanos) = self;
131        nanos / 1_000_000_000
132    }
133
134    pub fn from_nanos(nanos: u64) -> Self {
135        CuDuration(nanos)
136    }
137
138    pub fn from_micros(micros: u64) -> Self {
139        CuDuration(micros * 1_000)
140    }
141
142    pub fn from_millis(millis: u64) -> Self {
143        CuDuration(millis * 1_000_000)
144    }
145
146    pub fn from_secs(secs: u64) -> Self {
147        CuDuration(secs * 1_000_000_000)
148    }
149}
150
151/// Saturating subtraction for time and duration types.
152pub trait SaturatingSub {
153    fn saturating_sub(self, other: Self) -> Self;
154}
155
156impl SaturatingSub for CuDuration {
157    fn saturating_sub(self, other: Self) -> Self {
158        let Self(lhs) = self;
159        let Self(rhs) = other;
160        CuDuration(lhs.saturating_sub(rhs))
161    }
162}
163
164/// bridge the API with standard Durations.
165#[cfg(feature = "std")]
166impl From<std::time::Duration> for CuDuration {
167    fn from(duration: std::time::Duration) -> Self {
168        CuDuration(duration.as_nanos() as u64)
169    }
170}
171
172#[cfg(not(feature = "std"))]
173impl From<core::time::Duration> for CuDuration {
174    fn from(duration: core::time::Duration) -> Self {
175        CuDuration(duration.as_nanos() as u64)
176    }
177}
178
179#[cfg(feature = "std")]
180impl From<CuDuration> for std::time::Duration {
181    fn from(val: CuDuration) -> Self {
182        let CuDuration(nanos) = val;
183        std::time::Duration::from_nanos(nanos)
184    }
185}
186
187impl From<u64> for CuDuration {
188    fn from(duration: u64) -> Self {
189        CuDuration(duration)
190    }
191}
192
193impl From<CuDuration> for u64 {
194    fn from(val: CuDuration) -> Self {
195        let CuDuration(nanos) = val;
196        nanos
197    }
198}
199
200impl Sub for CuDuration {
201    type Output = Self;
202
203    fn sub(self, rhs: Self) -> Self::Output {
204        let CuDuration(lhs) = self;
205        let CuDuration(rhs) = rhs;
206        CuDuration(lhs - rhs)
207    }
208}
209
210impl Add for CuDuration {
211    type Output = Self;
212
213    fn add(self, rhs: Self) -> Self::Output {
214        let CuDuration(lhs) = self;
215        let CuDuration(rhs) = rhs;
216        CuDuration(lhs + rhs)
217    }
218}
219
220impl AddAssign for CuDuration {
221    fn add_assign(&mut self, rhs: Self) {
222        let CuDuration(lhs) = self;
223        let CuDuration(rhs) = rhs;
224        *lhs += rhs;
225    }
226}
227
228impl SubAssign for CuDuration {
229    fn sub_assign(&mut self, rhs: Self) {
230        let CuDuration(lhs) = self;
231        let CuDuration(rhs) = rhs;
232        *lhs -= rhs;
233    }
234}
235
236// a way to divide a duration by a scalar.
237// useful to compute averages for example.
238impl<T> Div<T> for CuDuration
239where
240    T: Into<u64>,
241{
242    type Output = Self;
243    fn div(self, rhs: T) -> Self {
244        let CuDuration(lhs) = self;
245        CuDuration(lhs / rhs.into())
246    }
247}
248
249// a way to multiply a duration by a scalar.
250// useful to compute offsets for example.
251// CuDuration * scalar
252impl<T> Mul<T> for CuDuration
253where
254    T: Into<u64>,
255{
256    type Output = CuDuration;
257
258    fn mul(self, rhs: T) -> CuDuration {
259        let CuDuration(lhs) = self;
260        CuDuration(lhs * rhs.into())
261    }
262}
263
264// u64 * CuDuration
265impl Mul<CuDuration> for u64 {
266    type Output = CuDuration;
267
268    fn mul(self, rhs: CuDuration) -> CuDuration {
269        let CuDuration(nanos) = rhs;
270        CuDuration(self * nanos)
271    }
272}
273
274// u32 * CuDuration
275impl Mul<CuDuration> for u32 {
276    type Output = CuDuration;
277
278    fn mul(self, rhs: CuDuration) -> CuDuration {
279        let CuDuration(nanos) = rhs;
280        CuDuration(self as u64 * nanos)
281    }
282}
283
284// i32 * CuDuration
285impl Mul<CuDuration> for i32 {
286    type Output = CuDuration;
287
288    fn mul(self, rhs: CuDuration) -> CuDuration {
289        let CuDuration(nanos) = rhs;
290        CuDuration(self as u64 * nanos)
291    }
292}
293
294impl Encode for CuDuration {
295    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
296        let CuDuration(nanos) = self;
297        nanos.encode(encoder)
298    }
299}
300
301impl<Context> Decode<Context> for CuDuration {
302    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
303        Ok(CuDuration(u64::decode(decoder)?))
304    }
305}
306
307impl<'de, Context> BorrowDecode<'de, Context> for CuDuration {
308    fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
309        Ok(CuDuration(u64::decode(decoder)?))
310    }
311}
312
313impl Display for CuDuration {
314    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
315        let Self(nanos) = *self;
316        if nanos >= 86_400_000_000_000 {
317            write!(f, "{:.3} d", nanos as f64 / 86_400_000_000_000.0)
318        } else if nanos >= 3_600_000_000_000 {
319            write!(f, "{:.3} h", nanos as f64 / 3_600_000_000_000.0)
320        } else if nanos >= 60_000_000_000 {
321            write!(f, "{:.3} m", nanos as f64 / 60_000_000_000.0)
322        } else if nanos >= 1_000_000_000 {
323            write!(f, "{:.3} s", nanos as f64 / 1_000_000_000.0)
324        } else if nanos >= 1_000_000 {
325            write!(f, "{:.3} ms", nanos as f64 / 1_000_000.0)
326        } else if nanos >= 1_000 {
327            write!(f, "{:.3} µs", nanos as f64 / 1_000.0)
328        } else {
329            write!(f, "{nanos} ns")
330        }
331    }
332}
333
334/// A robot time is a monotonic timestamp in nanoseconds from the robot clock epoch.
335#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
336#[cfg_attr(feature = "reflect", derive(Reflect))]
337#[repr(transparent)]
338pub struct CuTime(pub u64);
339
340impl CuTime {
341    pub const MIN: CuTime = CuTime(0u64);
342    pub const MAX: CuTime = CuTime(NONE_VALUE - 1);
343
344    pub fn max(self, other: CuTime) -> CuTime {
345        let Self(lhs) = self;
346        let Self(rhs) = other;
347        CuTime(lhs.max(rhs))
348    }
349
350    pub fn min(self, other: CuTime) -> CuTime {
351        let Self(lhs) = self;
352        let Self(rhs) = other;
353        CuTime(lhs.min(rhs))
354    }
355
356    pub fn as_nanos(&self) -> u64 {
357        let Self(nanos) = self;
358        *nanos
359    }
360
361    pub fn as_micros(&self) -> u64 {
362        self.as_nanos() / 1_000
363    }
364
365    pub fn as_millis(&self) -> u64 {
366        self.as_nanos() / 1_000_000
367    }
368
369    pub fn as_secs(&self) -> u64 {
370        self.as_nanos() / 1_000_000_000
371    }
372
373    pub fn from_nanos(nanos: u64) -> Self {
374        CuTime(nanos)
375    }
376
377    pub fn from_micros(micros: u64) -> Self {
378        CuTime::from(CuDuration::from_micros(micros))
379    }
380
381    pub fn from_millis(millis: u64) -> Self {
382        CuTime::from(CuDuration::from_millis(millis))
383    }
384
385    pub fn from_secs(secs: u64) -> Self {
386        CuTime::from(CuDuration::from_secs(secs))
387    }
388}
389
390impl SaturatingSub for CuTime {
391    fn saturating_sub(self, other: Self) -> Self {
392        let Self(lhs) = self;
393        let Self(rhs) = other;
394        CuTime(lhs.saturating_sub(rhs))
395    }
396}
397
398#[cfg(feature = "std")]
399impl From<std::time::Duration> for CuTime {
400    fn from(duration: std::time::Duration) -> Self {
401        CuTime::from(CuDuration::from(duration))
402    }
403}
404
405#[cfg(not(feature = "std"))]
406impl From<core::time::Duration> for CuTime {
407    fn from(duration: core::time::Duration) -> Self {
408        CuTime::from(CuDuration::from(duration))
409    }
410}
411
412#[cfg(feature = "std")]
413impl From<CuTime> for std::time::Duration {
414    fn from(val: CuTime) -> Self {
415        std::time::Duration::from_nanos(val.as_nanos())
416    }
417}
418
419impl From<u64> for CuTime {
420    fn from(time: u64) -> Self {
421        CuTime(time)
422    }
423}
424
425impl From<CuTime> for u64 {
426    fn from(val: CuTime) -> Self {
427        let CuTime(nanos) = val;
428        nanos
429    }
430}
431
432impl From<CuDuration> for CuTime {
433    fn from(duration: CuDuration) -> Self {
434        CuTime(duration.as_nanos())
435    }
436}
437
438impl From<CuTime> for CuDuration {
439    fn from(time: CuTime) -> Self {
440        CuDuration(time.as_nanos())
441    }
442}
443
444impl Add for CuTime {
445    type Output = Self;
446
447    fn add(self, rhs: Self) -> Self::Output {
448        CuTime(self.as_nanos().saturating_add(rhs.as_nanos()))
449    }
450}
451
452impl Add<CuDuration> for CuTime {
453    type Output = Self;
454
455    fn add(self, rhs: CuDuration) -> Self::Output {
456        CuTime(self.as_nanos().saturating_add(rhs.as_nanos()))
457    }
458}
459
460impl AddAssign<CuDuration> for CuTime {
461    fn add_assign(&mut self, rhs: CuDuration) {
462        *self = *self + rhs;
463    }
464}
465
466impl AddAssign for CuTime {
467    fn add_assign(&mut self, rhs: Self) {
468        *self = *self + rhs;
469    }
470}
471
472impl Sub<CuDuration> for CuTime {
473    type Output = Self;
474
475    fn sub(self, rhs: CuDuration) -> Self::Output {
476        CuTime(self.as_nanos().saturating_sub(rhs.as_nanos()))
477    }
478}
479
480impl SubAssign<CuDuration> for CuTime {
481    fn sub_assign(&mut self, rhs: CuDuration) {
482        *self = *self - rhs;
483    }
484}
485
486impl SubAssign for CuTime {
487    fn sub_assign(&mut self, rhs: Self) {
488        *self = CuTime(self.as_nanos().saturating_sub(rhs.as_nanos()));
489    }
490}
491
492impl Sub for CuTime {
493    type Output = CuDuration;
494
495    fn sub(self, rhs: Self) -> Self::Output {
496        CuDuration(self.as_nanos().saturating_sub(rhs.as_nanos()))
497    }
498}
499
500impl<T> Div<T> for CuTime
501where
502    T: Into<u64>,
503{
504    type Output = Self;
505
506    fn div(self, rhs: T) -> Self::Output {
507        CuTime(self.as_nanos() / rhs.into())
508    }
509}
510
511impl<T> Mul<T> for CuTime
512where
513    T: Into<u64>,
514{
515    type Output = Self;
516
517    fn mul(self, rhs: T) -> Self::Output {
518        CuTime(self.as_nanos() * rhs.into())
519    }
520}
521
522impl Mul<CuTime> for u64 {
523    type Output = CuTime;
524
525    fn mul(self, rhs: CuTime) -> Self::Output {
526        CuTime(self * rhs.as_nanos())
527    }
528}
529
530impl Mul<CuTime> for u32 {
531    type Output = CuTime;
532
533    fn mul(self, rhs: CuTime) -> Self::Output {
534        CuTime(self as u64 * rhs.as_nanos())
535    }
536}
537
538impl Mul<CuTime> for i32 {
539    type Output = CuTime;
540
541    fn mul(self, rhs: CuTime) -> Self::Output {
542        CuTime(self as u64 * rhs.as_nanos())
543    }
544}
545
546impl Encode for CuTime {
547    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
548        self.as_nanos().encode(encoder)
549    }
550}
551
552impl<Context> Decode<Context> for CuTime {
553    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
554        Ok(CuTime(u64::decode(decoder)?))
555    }
556}
557
558impl<'de, Context> BorrowDecode<'de, Context> for CuTime {
559    fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
560        Ok(CuTime(u64::decode(decoder)?))
561    }
562}
563
564impl Display for CuTime {
565    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
566        CuDuration(self.as_nanos()).fmt(f)
567    }
568}
569
570/// A busy looping function based on this clock for a duration.
571/// Mainly useful for embedded to spinlocking.
572#[inline(always)]
573pub fn busy_wait_for(duration: CuDuration) {
574    busy_wait_until(CuInstant::now() + duration);
575}
576
577/// A busy looping function based on this until a specific time.
578/// Mainly useful for embedded to spinlocking.
579#[inline(always)]
580pub fn busy_wait_until(time: CuInstant) {
581    while CuInstant::now() < time {
582        core::hint::spin_loop();
583    }
584}
585
586/// Homebrewed `Option<CuDuration>` to avoid using 128bits just to represent an Option.
587#[derive(Copy, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)]
588#[cfg_attr(feature = "reflect", derive(Reflect))]
589pub struct OptionCuTime(CuTime);
590
591const NONE_VALUE: u64 = 0xFFFFFFFFFFFFFFFF;
592
593impl OptionCuTime {
594    pub const NONE_SENTINEL_NANOS: u64 = NONE_VALUE;
595
596    #[inline]
597    pub fn is_none(&self) -> bool {
598        let Self(CuTime(nanos)) = self;
599        *nanos == NONE_VALUE
600    }
601
602    #[inline]
603    pub const fn none() -> Self {
604        OptionCuTime(CuTime(NONE_VALUE))
605    }
606
607    #[inline]
608    pub fn unwrap(self) -> CuTime {
609        if self.is_none() {
610            panic!("called `OptionCuTime::unwrap()` on a `None` value");
611        }
612        self.0
613    }
614}
615
616impl Display for OptionCuTime {
617    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
618        if self.is_none() {
619            write!(f, "None")
620        } else {
621            write!(f, "{}", self.0)
622        }
623    }
624}
625
626impl Default for OptionCuTime {
627    fn default() -> Self {
628        Self::none()
629    }
630}
631
632impl From<Option<CuTime>> for OptionCuTime {
633    #[inline]
634    fn from(duration: Option<CuTime>) -> Self {
635        match duration {
636            Some(duration) => OptionCuTime(duration),
637            None => OptionCuTime(CuTime(NONE_VALUE)),
638        }
639    }
640}
641
642impl From<OptionCuTime> for Option<CuTime> {
643    #[inline]
644    fn from(val: OptionCuTime) -> Self {
645        let OptionCuTime(CuTime(nanos)) = val;
646        if nanos == NONE_VALUE {
647            None
648        } else {
649            Some(CuTime(nanos))
650        }
651    }
652}
653
654impl From<CuTime> for OptionCuTime {
655    #[inline]
656    fn from(val: CuTime) -> Self {
657        Some(val).into()
658    }
659}
660
661#[derive(Debug, Clone, Copy, PartialEq, Eq)]
662pub enum ClockDebugScalarKind {
663    Time,
664    OptionalTime,
665    Duration,
666}
667
668#[derive(Debug, Clone, Copy, PartialEq, Eq)]
669pub struct ClockDebugScalarRegistration {
670    pub type_path: &'static str,
671    pub kind: ClockDebugScalarKind,
672}
673
674pub fn debug_scalar_registrations() -> Vec<ClockDebugScalarRegistration> {
675    alloc::vec![
676        ClockDebugScalarRegistration {
677            type_path: core::any::type_name::<CuDuration>(),
678            kind: ClockDebugScalarKind::Duration,
679        },
680        ClockDebugScalarRegistration {
681            type_path: core::any::type_name::<CuTime>(),
682            kind: ClockDebugScalarKind::Time,
683        },
684        ClockDebugScalarRegistration {
685            type_path: core::any::type_name::<OptionCuTime>(),
686            kind: ClockDebugScalarKind::OptionalTime,
687        },
688    ]
689}
690
691/// Represents a time range.
692#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
693#[cfg_attr(feature = "reflect", derive(Reflect))]
694pub struct CuTimeRange {
695    pub start: CuTime,
696    pub end: CuTime,
697}
698
699/// Builds a time range from a slice of CuTime.
700/// This is an O(n) operation.
701impl From<&[CuTime]> for CuTimeRange {
702    fn from(slice: &[CuTime]) -> Self {
703        CuTimeRange {
704            start: *slice.iter().min().expect("Empty slice"),
705            end: *slice.iter().max().expect("Empty slice"),
706        }
707    }
708}
709
710/// Represents a time range with possible undefined start or end or both.
711#[derive(Default, Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize)]
712#[cfg_attr(feature = "reflect", derive(Reflect))]
713pub struct PartialCuTimeRange {
714    pub start: OptionCuTime,
715    pub end: OptionCuTime,
716}
717
718impl Display for PartialCuTimeRange {
719    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
720        let start = if self.start.is_none() {
721            "…"
722        } else {
723            &format!("{}", self.start)
724        };
725        let end = if self.end.is_none() {
726            "…"
727        } else {
728            &format!("{}", self.end)
729        };
730        write!(f, "[{start} – {end}]")
731    }
732}
733
734/// The time of validity of a message can be more than one time but can be a time range of Tovs.
735/// For example a sub scan for a lidar, a set of images etc... can have a range of validity.
736#[derive(Default, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize, Copy)]
737#[cfg_attr(feature = "reflect", derive(Reflect))]
738pub enum Tov {
739    #[default]
740    None,
741    Time(CuTime),
742    Range(CuTimeRange),
743}
744
745impl Display for Tov {
746    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
747        match self {
748            Tov::None => write!(f, "None"),
749            Tov::Time(t) => write!(f, "{t}"),
750            Tov::Range(r) => write!(f, "[{} – {}]", r.start, r.end),
751        }
752    }
753}
754
755impl From<Option<CuDuration>> for Tov {
756    fn from(duration: Option<CuDuration>) -> Self {
757        duration.map(CuTime::from).map_or(Tov::None, Tov::Time)
758    }
759}
760
761impl From<Option<CuTime>> for Tov {
762    fn from(time: Option<CuTime>) -> Self {
763        time.map_or(Tov::None, Tov::Time)
764    }
765}
766
767impl From<CuDuration> for Tov {
768    fn from(duration: CuDuration) -> Self {
769        Tov::Time(duration.into())
770    }
771}
772
773impl From<CuTime> for Tov {
774    fn from(time: CuTime) -> Self {
775        Tov::Time(time)
776    }
777}
778
779/// Internal clock implementation that provides high-precision timing
780#[derive(Clone, Debug)]
781struct InternalClock {
782    // For real clocks, this stores the initialization time
783    // For mock clocks, this references the mock state
784    mock_state: Option<Arc<AtomicU64>>,
785}
786
787// Implements the std version of the RTC clock
788#[cfg(all(
789    feature = "std",
790    not(all(target_arch = "wasm32", target_os = "unknown"))
791))]
792#[inline(always)]
793fn read_rtc_ns() -> u64 {
794    std::time::SystemTime::now()
795        .duration_since(std::time::UNIX_EPOCH)
796        .unwrap()
797        .as_nanos() as u64
798}
799
800#[cfg(all(feature = "std", target_arch = "wasm32", target_os = "unknown"))]
801#[inline(always)]
802fn read_rtc_ns() -> u64 {
803    read_raw_counter()
804}
805
806#[cfg(all(
807    feature = "std",
808    not(all(target_arch = "wasm32", target_os = "unknown"))
809))]
810#[inline(always)]
811fn sleep_ns(ns: u64) {
812    std::thread::sleep(std::time::Duration::from_nanos(ns));
813}
814
815#[cfg(all(feature = "std", target_arch = "wasm32", target_os = "unknown"))]
816#[inline(always)]
817fn sleep_ns(ns: u64) {
818    let start = read_raw_counter();
819    while read_raw_counter().saturating_sub(start) < ns {
820        core::hint::spin_loop();
821    }
822}
823
824impl InternalClock {
825    fn new(
826        read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
827        sleep_ns: impl Fn(u64) + Send + Sync + 'static,
828    ) -> Self {
829        initialize();
830
831        // Initialize the frequency calibration
832        calibration::calibrate(read_raw_counter, read_rtc_ns, sleep_ns);
833        InternalClock { mock_state: None }
834    }
835
836    fn mock() -> (Self, Arc<AtomicU64>) {
837        let mock_state = Arc::new(AtomicU64::new(0));
838        let clock = InternalClock {
839            mock_state: Some(Arc::clone(&mock_state)),
840        };
841        (clock, mock_state)
842    }
843
844    fn now(&self) -> CuInstant {
845        if let Some(ref mock_state) = self.mock_state {
846            CuInstant(mock_state.load(Ordering::Relaxed))
847        } else {
848            CuInstant::now()
849        }
850    }
851
852    fn recent(&self) -> CuInstant {
853        // For simplicity, we use the same implementation as now()
854        // In a more sophisticated implementation, this could use a cached value
855        self.now()
856    }
857}
858
859/// A running Robot clock.
860/// The clock is a monotonic clock that starts at an arbitrary reference time.
861/// It is clone resilient, ie a clone will be the same clock, even when mocked.
862#[derive(Clone, Debug)]
863pub struct RobotClock {
864    inner: InternalClock,
865    ref_time: CuInstant,
866}
867
868/// A mock clock that can be controlled by the user.
869#[derive(Debug, Clone)]
870pub struct RobotClockMock(Arc<AtomicU64>);
871
872impl RobotClockMock {
873    pub fn increment(&self, amount: CuDuration) {
874        let Self(mock_state) = self;
875        mock_state.fetch_add(amount.as_nanos(), Ordering::Relaxed);
876    }
877
878    /// Decrements the time by the given amount.
879    /// Be careful this breaks the monotonicity of the clock.
880    pub fn decrement(&self, amount: CuDuration) {
881        let Self(mock_state) = self;
882        mock_state.fetch_sub(amount.as_nanos(), Ordering::Relaxed);
883    }
884
885    /// Gets the current value of time.
886    pub fn value(&self) -> u64 {
887        let Self(mock_state) = self;
888        mock_state.load(Ordering::Relaxed)
889    }
890
891    /// A convenient way to get the current time from the mocking side.
892    pub fn now(&self) -> CuTime {
893        let Self(mock_state) = self;
894        CuTime(mock_state.load(Ordering::Relaxed))
895    }
896
897    /// Sets the absolute value of the time.
898    pub fn set_value(&self, value: u64) {
899        let Self(mock_state) = self;
900        mock_state.store(value, Ordering::Relaxed);
901    }
902}
903
904impl RobotClock {
905    /// Creates a RobotClock using now as its reference time.
906    /// It will start at 0ns incrementing monotonically.
907    /// This uses the std System Time as a reference clock.
908    #[cfg(feature = "std")]
909    pub fn new() -> Self {
910        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
911        let ref_time = clock.now();
912        RobotClock {
913            inner: clock,
914            ref_time,
915        }
916    }
917
918    /// Builds a RobotClock using a reference RTC clock to calibrate with.
919    /// This is mandatory to use with the no-std platforms as we have no idea where to find a reference clock.
920    pub fn new_with_rtc(
921        read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
922        sleep_ns: impl Fn(u64) + Send + Sync + 'static,
923    ) -> Self {
924        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
925        let ref_time = clock.now();
926        RobotClock {
927            inner: clock,
928            ref_time,
929        }
930    }
931
932    /// Builds a monotonic clock starting at the given reference time.
933    #[cfg(feature = "std")]
934    pub fn from_ref_time(ref_time_ns: u64) -> Self {
935        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
936        let ref_time = clock.now() - CuDuration(ref_time_ns);
937        RobotClock {
938            inner: clock,
939            ref_time,
940        }
941    }
942
943    /// Overrides the RTC with a custom implementation, should be the same as the new_with_rtc.
944    pub fn from_ref_time_with_rtc(
945        read_rtc_ns: fn() -> u64,
946        sleep_ns: fn(u64),
947        ref_time_ns: u64,
948    ) -> Self {
949        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
950        let ref_time = clock.now() - CuDuration(ref_time_ns);
951        RobotClock {
952            inner: clock,
953            ref_time,
954        }
955    }
956
957    /// Build a fake clock with a reference time of 0.
958    /// The RobotMock interface enables you to control all the clones of the clock given.
959    pub fn mock() -> (Self, RobotClockMock) {
960        let (clock, mock_state) = InternalClock::mock();
961        let ref_time = clock.now();
962        (
963            RobotClock {
964                inner: clock,
965                ref_time,
966            },
967            RobotClockMock(mock_state),
968        )
969    }
970
971    /// Now returns the time that passed since the reference time, usually the start time.
972    /// It is a monotonically increasing value.
973    #[inline]
974    pub fn now(&self) -> CuTime {
975        CuTime::from_nanos((self.inner.now() - self.ref_time).as_nanos())
976    }
977
978    /// A less precise but quicker time
979    #[inline]
980    pub fn recent(&self) -> CuTime {
981        CuTime::from_nanos((self.inner.recent() - self.ref_time).as_nanos())
982    }
983}
984
985/// We cannot build a default RobotClock on no-std because we don't know how to find a reference clock.
986/// Use RobotClock::new_with_rtc instead on no-std.
987#[cfg(feature = "std")]
988impl Default for RobotClock {
989    fn default() -> Self {
990        Self::new()
991    }
992}
993
994/// A trait to provide a clock to the runtime.
995pub trait ClockProvider {
996    fn get_clock(&self) -> RobotClock;
997}
998
999#[cfg(test)]
1000mod tests {
1001    use super::*;
1002    use approx::assert_relative_eq;
1003
1004    #[test]
1005    fn test_cuduration_comparison_operators() {
1006        let a = CuDuration(100);
1007        let b = CuDuration(200);
1008
1009        assert!(a < b);
1010        assert!(b > a);
1011        assert_ne!(a, b);
1012        assert_eq!(a, CuDuration(100));
1013    }
1014
1015    #[test]
1016    fn test_cuduration_arithmetic_operations() {
1017        let a = CuDuration(100);
1018        let b = CuDuration(50);
1019
1020        assert_eq!(a + b, CuDuration(150));
1021        assert_eq!(a - b, CuDuration(50));
1022        assert_eq!(a * 2u32, CuDuration(200));
1023        assert_eq!(a / 2u32, CuDuration(50));
1024    }
1025
1026    #[test]
1027    fn test_robot_clock_monotonic() {
1028        let clock = RobotClock::new();
1029        let t1 = clock.now();
1030        let t2 = clock.now();
1031        assert!(t2 >= t1);
1032    }
1033
1034    #[test]
1035    fn test_robot_clock_mock() {
1036        let (clock, mock) = RobotClock::mock();
1037        let t1 = clock.now();
1038        mock.increment(CuDuration::from_millis(100));
1039        let t2 = clock.now();
1040        assert!(t2 > t1);
1041        assert_eq!(t2 - t1, CuDuration(100_000_000)); // 100ms in nanoseconds
1042    }
1043
1044    #[test]
1045    fn test_robot_clock_clone_consistency() {
1046        let (clock1, mock) = RobotClock::mock();
1047        let clock2 = clock1.clone();
1048
1049        mock.set_value(1_000_000_000); // 1 second
1050        assert_eq!(clock1.now(), clock2.now());
1051    }
1052
1053    #[test]
1054    fn test_from_ref_time() {
1055        let tolerance_ms = 10f64;
1056        let clock = RobotClock::from_ref_time(1_000_000_000);
1057        assert_relative_eq!(
1058            clock.now().as_millis() as f64,
1059            CuDuration::from_secs(1).as_millis() as f64,
1060            epsilon = tolerance_ms
1061        );
1062    }
1063
1064    #[test]
1065    fn longest_duration() {
1066        let maxcu = CuDuration(u64::MAX);
1067        assert_eq!(maxcu.as_nanos(), u64::MAX);
1068        let s = maxcu.as_secs();
1069        let y = s / 60 / 60 / 24 / 365;
1070        assert!(y >= 584); // 584 years of robot uptime, we should be good.
1071    }
1072
1073    #[test]
1074    fn test_some_time_arithmetics() {
1075        let a: CuDuration = 10.into();
1076        let b: CuDuration = 20.into();
1077        let c = a + b;
1078        assert_eq!(c.0, 30);
1079        let d = b - a;
1080        assert_eq!(d.0, 10);
1081    }
1082
1083    #[test]
1084    fn test_build_range_from_slice() {
1085        let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
1086        assert_eq!(range.start, 10.into());
1087        assert_eq!(range.end, 30.into());
1088    }
1089
1090    #[test]
1091    fn test_time_range_operations() {
1092        // Test creating a time range and checking its properties
1093        let start = CuTime::from(100u64);
1094        let end = CuTime::from(200u64);
1095        let range = CuTimeRange { start, end };
1096
1097        assert_eq!(range.start, start);
1098        assert_eq!(range.end, end);
1099
1100        // Test creating from a slice
1101        let times = [
1102            CuTime::from(150u64),
1103            CuTime::from(120u64),
1104            CuTime::from(180u64),
1105        ];
1106        let range_from_slice = CuTimeRange::from(&times[..]);
1107
1108        // Range should capture min and max values
1109        assert_eq!(range_from_slice.start, CuTime::from(120u64));
1110        assert_eq!(range_from_slice.end, CuTime::from(180u64));
1111    }
1112
1113    #[test]
1114    fn test_partial_time_range() {
1115        // Test creating a partial time range with defined start/end
1116        let start = CuTime::from(100u64);
1117        let end = CuTime::from(200u64);
1118
1119        let partial_range = PartialCuTimeRange {
1120            start: OptionCuTime::from(start),
1121            end: OptionCuTime::from(end),
1122        };
1123
1124        // Test converting to Option
1125        let opt_start: Option<CuTime> = partial_range.start.into();
1126        let opt_end: Option<CuTime> = partial_range.end.into();
1127
1128        assert_eq!(opt_start, Some(start));
1129        assert_eq!(opt_end, Some(end));
1130
1131        // Test partial range with undefined values
1132        let partial_undefined = PartialCuTimeRange::default();
1133        assert!(partial_undefined.start.is_none());
1134        assert!(partial_undefined.end.is_none());
1135    }
1136
1137    #[test]
1138    fn test_tov_conversions() {
1139        // Test different Time of Validity (Tov) variants
1140        let time = CuTime::from(100u64);
1141
1142        // Test conversion from CuTime
1143        let tov_time: Tov = time.into();
1144        assert!(matches!(tov_time, Tov::Time(_)));
1145
1146        if let Tov::Time(t) = tov_time {
1147            assert_eq!(t, time);
1148        }
1149
1150        // Test conversion from Option<CuTime>
1151        let some_time = Some(time);
1152        let tov_some: Tov = some_time.into();
1153        assert!(matches!(tov_some, Tov::Time(_)));
1154
1155        let none_time: Option<CuDuration> = None;
1156        let tov_none: Tov = none_time.into();
1157        assert!(matches!(tov_none, Tov::None));
1158
1159        // Test range
1160        let start = CuTime::from(100u64);
1161        let end = CuTime::from(200u64);
1162        let range = CuTimeRange { start, end };
1163        let tov_range = Tov::Range(range);
1164
1165        assert!(matches!(tov_range, Tov::Range(_)));
1166    }
1167
1168    #[cfg(feature = "std")]
1169    #[test]
1170    fn test_cuduration_display() {
1171        // Test the display implementation for different magnitudes
1172        let nano = CuDuration(42);
1173        assert_eq!(nano.to_string(), "42 ns");
1174
1175        let micro = CuDuration(42_000);
1176        assert_eq!(micro.to_string(), "42.000 µs");
1177
1178        let milli = CuDuration(42_000_000);
1179        assert_eq!(milli.to_string(), "42.000 ms");
1180
1181        let sec = CuDuration(1_500_000_000);
1182        assert_eq!(sec.to_string(), "1.500 s");
1183
1184        let min = CuDuration(90_000_000_000);
1185        assert_eq!(min.to_string(), "1.500 m");
1186
1187        let hour = CuDuration(3_600_000_000_000);
1188        assert_eq!(hour.to_string(), "1.000 h");
1189
1190        let day = CuDuration(86_400_000_000_000);
1191        assert_eq!(day.to_string(), "1.000 d");
1192    }
1193
1194    #[test]
1195    fn test_robot_clock_precision() {
1196        // Test that RobotClock::now() and RobotClock::recent() return different values
1197        // and that recent() is always <= now()
1198        let clock = RobotClock::new();
1199
1200        // We can't guarantee the exact values, but we can check relationships
1201        let recent = clock.recent();
1202        let now = clock.now();
1203
1204        // recent() should be less than or equal to now()
1205        assert!(recent <= now);
1206
1207        // Test precision of from_ref_time
1208        let ref_time_ns = 1_000_000_000; // 1 second
1209        let clock = RobotClock::from_ref_time(ref_time_ns);
1210
1211        // Clock should start at approximately ref_time_ns
1212        let now = clock.now();
1213        let now_ns: u64 = now.into();
1214
1215        // Allow reasonable tolerance for clock initialization time
1216        let tolerance_ns = 50_000_000; // 50ms tolerance
1217        assert!(now_ns >= ref_time_ns);
1218        assert!(now_ns < ref_time_ns + tolerance_ns);
1219    }
1220
1221    #[test]
1222    fn test_mock_clock_advanced_operations() {
1223        // Test more complex operations with the mock clock
1224        let (clock, mock) = RobotClock::mock();
1225
1226        // Test initial state
1227        assert_eq!(clock.now(), CuTime::from(0));
1228
1229        // Test increment
1230        mock.increment(CuDuration::from_secs(10));
1231        assert_eq!(
1232            clock.now(),
1233            CuTime::from_nanos(CuDuration::from_secs(10).as_nanos())
1234        );
1235
1236        // Test decrement (unusual but supported)
1237        mock.decrement(CuDuration::from_secs(5));
1238        assert_eq!(
1239            clock.now(),
1240            CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1241        );
1242
1243        // Test setting absolute value
1244        mock.set_value(30_000_000_000); // 30 seconds in ns
1245        assert_eq!(
1246            clock.now(),
1247            CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1248        );
1249
1250        // Test that getting the time from the mock directly works
1251        assert_eq!(
1252            mock.now(),
1253            CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1254        );
1255        assert_eq!(mock.value(), 30_000_000_000);
1256    }
1257
1258    #[test]
1259    fn test_cuduration_min_max() {
1260        // Test MIN and MAX constants
1261        assert_eq!(CuDuration::MIN, CuDuration(0));
1262
1263        // Test min/max methods
1264        let a = CuDuration(100);
1265        let b = CuDuration(200);
1266
1267        assert_eq!(a.min(b), a);
1268        assert_eq!(a.max(b), b);
1269        assert_eq!(b.min(a), a);
1270        assert_eq!(b.max(a), b);
1271
1272        // Edge cases
1273        assert_eq!(a.min(a), a);
1274        assert_eq!(a.max(a), a);
1275
1276        // Test with MIN/MAX constants
1277        assert_eq!(a.min(CuDuration::MIN), CuDuration::MIN);
1278        assert_eq!(a.max(CuDuration::MAX), CuDuration::MAX);
1279    }
1280
1281    #[test]
1282    fn test_clock_provider_trait() {
1283        // Test implementing the ClockProvider trait
1284        struct TestClockProvider {
1285            clock: RobotClock,
1286        }
1287
1288        impl ClockProvider for TestClockProvider {
1289            fn get_clock(&self) -> RobotClock {
1290                self.clock.clone()
1291            }
1292        }
1293
1294        // Create a provider with a mock clock
1295        let (clock, mock) = RobotClock::mock();
1296        let provider = TestClockProvider { clock };
1297
1298        // Test that provider returns a clock synchronized with the original
1299        let provider_clock = provider.get_clock();
1300        assert_eq!(provider_clock.now(), CuTime::from(0));
1301
1302        // Advance the mock clock and check that the provider's clock also advances
1303        mock.increment(CuDuration::from_secs(5));
1304        assert_eq!(
1305            provider_clock.now(),
1306            CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1307        );
1308    }
1309}