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