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::format;
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            &format!("{}", self.start)
706        };
707        let end = if self.end.is_none() {
708            "…"
709        } else {
710            &format!("{}", self.end)
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    /// Creates a RobotClock using now as its reference time.
888    /// It will start at 0ns incrementing monotonically.
889    /// This uses the std System Time as a reference clock.
890    #[cfg(feature = "std")]
891    pub fn new() -> Self {
892        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
893        let ref_time = clock.now();
894        RobotClock {
895            inner: clock,
896            ref_time,
897        }
898    }
899
900    /// Builds a RobotClock using a reference RTC clock to calibrate with.
901    /// This is mandatory to use with the no-std platforms as we have no idea where to find a reference clock.
902    pub fn new_with_rtc(
903        read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
904        sleep_ns: impl Fn(u64) + Send + Sync + 'static,
905    ) -> Self {
906        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
907        let ref_time = clock.now();
908        RobotClock {
909            inner: clock,
910            ref_time,
911        }
912    }
913
914    /// Builds a monotonic clock starting at the given reference time.
915    #[cfg(feature = "std")]
916    pub fn from_ref_time(ref_time_ns: u64) -> Self {
917        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
918        let ref_time = clock.now() - CuDuration(ref_time_ns);
919        RobotClock {
920            inner: clock,
921            ref_time,
922        }
923    }
924
925    /// Overrides the RTC with a custom implementation, should be the same as the new_with_rtc.
926    pub fn from_ref_time_with_rtc(
927        read_rtc_ns: fn() -> u64,
928        sleep_ns: fn(u64),
929        ref_time_ns: u64,
930    ) -> Self {
931        let clock = InternalClock::new(read_rtc_ns, sleep_ns);
932        let ref_time = clock.now() - CuDuration(ref_time_ns);
933        RobotClock {
934            inner: clock,
935            ref_time,
936        }
937    }
938
939    /// Build a fake clock with a reference time of 0.
940    /// The RobotMock interface enables you to control all the clones of the clock given.
941    pub fn mock() -> (Self, RobotClockMock) {
942        let (clock, mock_state) = InternalClock::mock();
943        let ref_time = clock.now();
944        (
945            RobotClock {
946                inner: clock,
947                ref_time,
948            },
949            RobotClockMock(mock_state),
950        )
951    }
952
953    /// Now returns the time that passed since the reference time, usually the start time.
954    /// It is a monotonically increasing value.
955    #[inline]
956    pub fn now(&self) -> CuTime {
957        CuTime::from_nanos((self.inner.now() - self.ref_time).as_nanos())
958    }
959
960    /// A less precise but quicker time
961    #[inline]
962    pub fn recent(&self) -> CuTime {
963        CuTime::from_nanos((self.inner.recent() - self.ref_time).as_nanos())
964    }
965}
966
967/// We cannot build a default RobotClock on no-std because we don't know how to find a reference clock.
968/// Use RobotClock::new_with_rtc instead on no-std.
969#[cfg(feature = "std")]
970impl Default for RobotClock {
971    fn default() -> Self {
972        Self::new()
973    }
974}
975
976/// A trait to provide a clock to the runtime.
977pub trait ClockProvider {
978    fn get_clock(&self) -> RobotClock;
979}
980
981#[cfg(test)]
982mod tests {
983    use super::*;
984    use approx::assert_relative_eq;
985
986    #[test]
987    fn test_cuduration_comparison_operators() {
988        let a = CuDuration(100);
989        let b = CuDuration(200);
990
991        assert!(a < b);
992        assert!(b > a);
993        assert_ne!(a, b);
994        assert_eq!(a, CuDuration(100));
995    }
996
997    #[test]
998    fn test_cuduration_arithmetic_operations() {
999        let a = CuDuration(100);
1000        let b = CuDuration(50);
1001
1002        assert_eq!(a + b, CuDuration(150));
1003        assert_eq!(a - b, CuDuration(50));
1004        assert_eq!(a * 2u32, CuDuration(200));
1005        assert_eq!(a / 2u32, CuDuration(50));
1006    }
1007
1008    #[test]
1009    fn test_robot_clock_monotonic() {
1010        let clock = RobotClock::new();
1011        let t1 = clock.now();
1012        let t2 = clock.now();
1013        assert!(t2 >= t1);
1014    }
1015
1016    #[test]
1017    fn test_robot_clock_mock() {
1018        let (clock, mock) = RobotClock::mock();
1019        let t1 = clock.now();
1020        mock.increment(CuDuration::from_millis(100));
1021        let t2 = clock.now();
1022        assert!(t2 > t1);
1023        assert_eq!(t2 - t1, CuDuration(100_000_000)); // 100ms in nanoseconds
1024    }
1025
1026    #[test]
1027    fn test_robot_clock_clone_consistency() {
1028        let (clock1, mock) = RobotClock::mock();
1029        let clock2 = clock1.clone();
1030
1031        mock.set_value(1_000_000_000); // 1 second
1032        assert_eq!(clock1.now(), clock2.now());
1033    }
1034
1035    #[test]
1036    fn test_from_ref_time() {
1037        let tolerance_ms = 10f64;
1038        let clock = RobotClock::from_ref_time(1_000_000_000);
1039        assert_relative_eq!(
1040            clock.now().as_millis() as f64,
1041            CuDuration::from_secs(1).as_millis() as f64,
1042            epsilon = tolerance_ms
1043        );
1044    }
1045
1046    #[test]
1047    fn longest_duration() {
1048        let maxcu = CuDuration(u64::MAX);
1049        assert_eq!(maxcu.as_nanos(), u64::MAX);
1050        let s = maxcu.as_secs();
1051        let y = s / 60 / 60 / 24 / 365;
1052        assert!(y >= 584); // 584 years of robot uptime, we should be good.
1053    }
1054
1055    #[test]
1056    fn test_some_time_arithmetics() {
1057        let a: CuDuration = 10.into();
1058        let b: CuDuration = 20.into();
1059        let c = a + b;
1060        assert_eq!(c.0, 30);
1061        let d = b - a;
1062        assert_eq!(d.0, 10);
1063    }
1064
1065    #[test]
1066    fn test_build_range_from_slice() {
1067        let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
1068        assert_eq!(range.start, 10.into());
1069        assert_eq!(range.end, 30.into());
1070    }
1071
1072    #[test]
1073    fn test_time_range_operations() {
1074        // Test creating a time range and checking its properties
1075        let start = CuTime::from(100u64);
1076        let end = CuTime::from(200u64);
1077        let range = CuTimeRange { start, end };
1078
1079        assert_eq!(range.start, start);
1080        assert_eq!(range.end, end);
1081
1082        // Test creating from a slice
1083        let times = [
1084            CuTime::from(150u64),
1085            CuTime::from(120u64),
1086            CuTime::from(180u64),
1087        ];
1088        let range_from_slice = CuTimeRange::from(&times[..]);
1089
1090        // Range should capture min and max values
1091        assert_eq!(range_from_slice.start, CuTime::from(120u64));
1092        assert_eq!(range_from_slice.end, CuTime::from(180u64));
1093    }
1094
1095    #[test]
1096    fn test_partial_time_range() {
1097        // Test creating a partial time range with defined start/end
1098        let start = CuTime::from(100u64);
1099        let end = CuTime::from(200u64);
1100
1101        let partial_range = PartialCuTimeRange {
1102            start: OptionCuTime::from(start),
1103            end: OptionCuTime::from(end),
1104        };
1105
1106        // Test converting to Option
1107        let opt_start: Option<CuTime> = partial_range.start.into();
1108        let opt_end: Option<CuTime> = partial_range.end.into();
1109
1110        assert_eq!(opt_start, Some(start));
1111        assert_eq!(opt_end, Some(end));
1112
1113        // Test partial range with undefined values
1114        let partial_undefined = PartialCuTimeRange::default();
1115        assert!(partial_undefined.start.is_none());
1116        assert!(partial_undefined.end.is_none());
1117    }
1118
1119    #[test]
1120    fn test_tov_conversions() {
1121        // Test different Time of Validity (Tov) variants
1122        let time = CuTime::from(100u64);
1123
1124        // Test conversion from CuTime
1125        let tov_time: Tov = time.into();
1126        assert!(matches!(tov_time, Tov::Time(_)));
1127
1128        if let Tov::Time(t) = tov_time {
1129            assert_eq!(t, time);
1130        }
1131
1132        // Test conversion from Option<CuTime>
1133        let some_time = Some(time);
1134        let tov_some: Tov = some_time.into();
1135        assert!(matches!(tov_some, Tov::Time(_)));
1136
1137        let none_time: Option<CuDuration> = None;
1138        let tov_none: Tov = none_time.into();
1139        assert!(matches!(tov_none, Tov::None));
1140
1141        // Test range
1142        let start = CuTime::from(100u64);
1143        let end = CuTime::from(200u64);
1144        let range = CuTimeRange { start, end };
1145        let tov_range = Tov::Range(range);
1146
1147        assert!(matches!(tov_range, Tov::Range(_)));
1148    }
1149
1150    #[cfg(feature = "std")]
1151    #[test]
1152    fn test_cuduration_display() {
1153        // Test the display implementation for different magnitudes
1154        let nano = CuDuration(42);
1155        assert_eq!(nano.to_string(), "42 ns");
1156
1157        let micro = CuDuration(42_000);
1158        assert_eq!(micro.to_string(), "42.000 µs");
1159
1160        let milli = CuDuration(42_000_000);
1161        assert_eq!(milli.to_string(), "42.000 ms");
1162
1163        let sec = CuDuration(1_500_000_000);
1164        assert_eq!(sec.to_string(), "1.500 s");
1165
1166        let min = CuDuration(90_000_000_000);
1167        assert_eq!(min.to_string(), "1.500 m");
1168
1169        let hour = CuDuration(3_600_000_000_000);
1170        assert_eq!(hour.to_string(), "1.000 h");
1171
1172        let day = CuDuration(86_400_000_000_000);
1173        assert_eq!(day.to_string(), "1.000 d");
1174    }
1175
1176    #[test]
1177    fn test_robot_clock_precision() {
1178        // Test that RobotClock::now() and RobotClock::recent() return different values
1179        // and that recent() is always <= now()
1180        let clock = RobotClock::new();
1181
1182        // We can't guarantee the exact values, but we can check relationships
1183        let recent = clock.recent();
1184        let now = clock.now();
1185
1186        // recent() should be less than or equal to now()
1187        assert!(recent <= now);
1188
1189        // Test precision of from_ref_time
1190        let ref_time_ns = 1_000_000_000; // 1 second
1191        let clock = RobotClock::from_ref_time(ref_time_ns);
1192
1193        // Clock should start at approximately ref_time_ns
1194        let now = clock.now();
1195        let now_ns: u64 = now.into();
1196
1197        // Allow reasonable tolerance for clock initialization time
1198        let tolerance_ns = 50_000_000; // 50ms tolerance
1199        assert!(now_ns >= ref_time_ns);
1200        assert!(now_ns < ref_time_ns + tolerance_ns);
1201    }
1202
1203    #[test]
1204    fn test_mock_clock_advanced_operations() {
1205        // Test more complex operations with the mock clock
1206        let (clock, mock) = RobotClock::mock();
1207
1208        // Test initial state
1209        assert_eq!(clock.now(), CuTime::from(0));
1210
1211        // Test increment
1212        mock.increment(CuDuration::from_secs(10));
1213        assert_eq!(
1214            clock.now(),
1215            CuTime::from_nanos(CuDuration::from_secs(10).as_nanos())
1216        );
1217
1218        // Test decrement (unusual but supported)
1219        mock.decrement(CuDuration::from_secs(5));
1220        assert_eq!(
1221            clock.now(),
1222            CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1223        );
1224
1225        // Test setting absolute value
1226        mock.set_value(30_000_000_000); // 30 seconds in ns
1227        assert_eq!(
1228            clock.now(),
1229            CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1230        );
1231
1232        // Test that getting the time from the mock directly works
1233        assert_eq!(
1234            mock.now(),
1235            CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1236        );
1237        assert_eq!(mock.value(), 30_000_000_000);
1238    }
1239
1240    #[test]
1241    fn test_cuduration_min_max() {
1242        // Test MIN and MAX constants
1243        assert_eq!(CuDuration::MIN, CuDuration(0));
1244
1245        // Test min/max methods
1246        let a = CuDuration(100);
1247        let b = CuDuration(200);
1248
1249        assert_eq!(a.min(b), a);
1250        assert_eq!(a.max(b), b);
1251        assert_eq!(b.min(a), a);
1252        assert_eq!(b.max(a), b);
1253
1254        // Edge cases
1255        assert_eq!(a.min(a), a);
1256        assert_eq!(a.max(a), a);
1257
1258        // Test with MIN/MAX constants
1259        assert_eq!(a.min(CuDuration::MIN), CuDuration::MIN);
1260        assert_eq!(a.max(CuDuration::MAX), CuDuration::MAX);
1261    }
1262
1263    #[test]
1264    fn test_clock_provider_trait() {
1265        // Test implementing the ClockProvider trait
1266        struct TestClockProvider {
1267            clock: RobotClock,
1268        }
1269
1270        impl ClockProvider for TestClockProvider {
1271            fn get_clock(&self) -> RobotClock {
1272                self.clock.clone()
1273            }
1274        }
1275
1276        // Create a provider with a mock clock
1277        let (clock, mock) = RobotClock::mock();
1278        let provider = TestClockProvider { clock };
1279
1280        // Test that provider returns a clock synchronized with the original
1281        let provider_clock = provider.get_clock();
1282        assert_eq!(provider_clock.now(), CuTime::from(0));
1283
1284        // Advance the mock clock and check that the provider's clock also advances
1285        mock.increment(CuDuration::from_secs(5));
1286        assert_eq!(
1287            provider_clock.now(),
1288            CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1289        );
1290    }
1291}