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
23use 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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
34pub struct CuInstant(u64);
35
36pub type Instant = CuInstant; impl 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#[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 pub const MIN: CuDuration = CuDuration(0u64);
81 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
133pub 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#[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
218impl<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
231impl<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
246impl 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
256impl 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
266impl 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#[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#[inline(always)]
555pub fn busy_wait_for(duration: CuDuration) {
556 busy_wait_until(CuInstant::now() + duration);
557}
558
559#[inline(always)]
562pub fn busy_wait_until(time: CuInstant) {
563 while CuInstant::now() < time {
564 core::hint::spin_loop();
565 }
566}
567
568#[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#[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
681impl 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#[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#[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#[derive(Clone, Debug)]
763struct InternalClock {
764 mock_state: Option<Arc<AtomicU64>>,
767}
768
769#[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 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 self.now()
838 }
839}
840
841#[derive(Clone, Debug)]
845pub struct RobotClock {
846 inner: InternalClock,
847 ref_time: CuInstant,
848}
849
850#[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 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 pub fn value(&self) -> u64 {
869 let Self(mock_state) = self;
870 mock_state.load(Ordering::Relaxed)
871 }
872
873 pub fn now(&self) -> CuTime {
875 let Self(mock_state) = self;
876 CuTime(mock_state.load(Ordering::Relaxed))
877 }
878
879 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 #[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 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 #[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 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 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 #[inline]
956 pub fn now(&self) -> CuTime {
957 CuTime::from_nanos((self.inner.now() - self.ref_time).as_nanos())
958 }
959
960 #[inline]
962 pub fn recent(&self) -> CuTime {
963 CuTime::from_nanos((self.inner.recent() - self.ref_time).as_nanos())
964 }
965}
966
967#[cfg(feature = "std")]
970impl Default for RobotClock {
971 fn default() -> Self {
972 Self::new()
973 }
974}
975
976pub 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)); }
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); 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); }
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 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 let times = [
1084 CuTime::from(150u64),
1085 CuTime::from(120u64),
1086 CuTime::from(180u64),
1087 ];
1088 let range_from_slice = CuTimeRange::from(×[..]);
1089
1090 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 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 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 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 let time = CuTime::from(100u64);
1123
1124 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 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 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 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 let clock = RobotClock::new();
1181
1182 let recent = clock.recent();
1184 let now = clock.now();
1185
1186 assert!(recent <= now);
1188
1189 let ref_time_ns = 1_000_000_000; let clock = RobotClock::from_ref_time(ref_time_ns);
1192
1193 let now = clock.now();
1195 let now_ns: u64 = now.into();
1196
1197 let tolerance_ns = 50_000_000; 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 let (clock, mock) = RobotClock::mock();
1207
1208 assert_eq!(clock.now(), CuTime::from(0));
1210
1211 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 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 mock.set_value(30_000_000_000); assert_eq!(
1228 clock.now(),
1229 CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1230 );
1231
1232 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 assert_eq!(CuDuration::MIN, CuDuration(0));
1244
1245 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 assert_eq!(a.min(a), a);
1256 assert_eq!(a.max(a), a);
1257
1258 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 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 let (clock, mock) = RobotClock::mock();
1278 let provider = TestClockProvider { clock };
1279
1280 let provider_clock = provider.get_clock();
1282 assert_eq!(provider_clock.now(), CuTime::from(0));
1283
1284 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}