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::string::ToString;
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 &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#[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 pub fn is_mock(&self) -> bool {
890 self.inner.mock_state.is_some()
891 }
892
893 #[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 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 #[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 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 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 #[inline]
962 pub fn now(&self) -> CuTime {
963 CuTime::from_nanos((self.inner.now() - self.ref_time).as_nanos())
964 }
965
966 #[inline]
968 pub fn recent(&self) -> CuTime {
969 CuTime::from_nanos((self.inner.recent() - self.ref_time).as_nanos())
970 }
971}
972
973#[cfg(feature = "std")]
976impl Default for RobotClock {
977 fn default() -> Self {
978 Self::new()
979 }
980}
981
982pub 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)); }
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); 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); }
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 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 let times = [
1090 CuTime::from(150u64),
1091 CuTime::from(120u64),
1092 CuTime::from(180u64),
1093 ];
1094 let range_from_slice = CuTimeRange::from(×[..]);
1095
1096 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 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 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 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 let time = CuTime::from(100u64);
1129
1130 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 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 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 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 let clock = RobotClock::new();
1187
1188 let recent = clock.recent();
1190 let now = clock.now();
1191
1192 assert!(recent <= now);
1194
1195 let ref_time_ns = 1_000_000_000; let clock = RobotClock::from_ref_time(ref_time_ns);
1198
1199 let now = clock.now();
1201 let now_ns: u64 = now.into();
1202
1203 let tolerance_ns = 50_000_000; 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 let (clock, mock) = RobotClock::mock();
1213
1214 assert_eq!(clock.now(), CuTime::from(0));
1216
1217 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 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 mock.set_value(30_000_000_000); assert_eq!(
1234 clock.now(),
1235 CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1236 );
1237
1238 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 assert_eq!(CuDuration::MIN, CuDuration(0));
1250
1251 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 assert_eq!(a.min(a), a);
1262 assert_eq!(a.max(a), a);
1263
1264 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 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 let (clock, mock) = RobotClock::mock();
1284 let provider = TestClockProvider { clock };
1285
1286 let provider_clock = provider.get_clock();
1288 assert_eq!(provider_clock.now(), CuTime::from(0));
1289
1290 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}