1#[cfg(test)]
2#[macro_use]
3extern crate approx;
4use bincode::de::BorrowDecoder;
5use bincode::de::Decoder;
6use bincode::enc::Encoder;
7use bincode::error::{DecodeError, EncodeError};
8use bincode::BorrowDecode;
9use bincode::{Decode, Encode};
10use core::ops::{Add, Sub};
11pub use quanta::Instant;
12use quanta::{Clock, Mock};
13use serde::{Deserialize, Serialize};
14use std::convert::Into;
15use std::fmt::{Display, Formatter};
16use std::ops::{AddAssign, Div, Mul, SubAssign};
17use std::sync::Arc;
18use std::time::Duration;
19
20#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
23pub struct CuDuration(pub u64);
24
25impl CuDuration {
26 pub const MIN: CuDuration = CuDuration(0u64);
28 pub const MAX: CuDuration = CuDuration(NONE_VALUE - 1);
30 pub fn max(self, other: CuDuration) -> CuDuration {
31 let Self(lhs) = self;
32 let Self(rhs) = other;
33 CuDuration(lhs.max(rhs))
34 }
35
36 pub fn min(self, other: CuDuration) -> CuDuration {
37 let Self(lhs) = self;
38 let Self(rhs) = other;
39 CuDuration(lhs.min(rhs))
40 }
41
42 pub fn as_nanos(&self) -> u64 {
43 let Self(nanos) = self;
44 *nanos
45 }
46}
47
48impl From<Duration> for CuDuration {
50 fn from(duration: Duration) -> Self {
51 CuDuration(duration.as_nanos() as u64)
52 }
53}
54
55impl From<CuDuration> for Duration {
56 fn from(val: CuDuration) -> Self {
57 let CuDuration(nanos) = val;
58 Duration::from_nanos(nanos)
59 }
60}
61
62impl From<u64> for CuDuration {
63 fn from(duration: u64) -> Self {
64 CuDuration(duration)
65 }
66}
67
68impl From<CuDuration> for u64 {
69 fn from(val: CuDuration) -> Self {
70 let CuDuration(nanos) = val;
71 nanos
72 }
73}
74
75impl Sub for CuDuration {
76 type Output = Self;
77
78 fn sub(self, rhs: Self) -> Self::Output {
79 let CuDuration(lhs) = self;
80 let CuDuration(rhs) = rhs;
81 CuDuration(lhs - rhs)
82 }
83}
84
85impl Add for CuDuration {
86 type Output = Self;
87
88 fn add(self, rhs: Self) -> Self::Output {
89 let CuDuration(lhs) = self;
90 let CuDuration(rhs) = rhs;
91 CuDuration(lhs + rhs)
92 }
93}
94
95impl AddAssign for CuDuration {
96 fn add_assign(&mut self, rhs: Self) {
97 let CuDuration(lhs) = self;
98 let CuDuration(rhs) = rhs;
99 *lhs += rhs;
100 }
101}
102
103impl SubAssign for CuDuration {
104 fn sub_assign(&mut self, rhs: Self) {
105 let CuDuration(lhs) = self;
106 let CuDuration(rhs) = rhs;
107 *lhs -= rhs;
108 }
109}
110
111impl<T> Div<T> for CuDuration
114where
115 T: Into<u64>,
116{
117 type Output = Self;
118 fn div(self, rhs: T) -> Self {
119 let CuDuration(lhs) = self;
120 CuDuration(lhs / rhs.into())
121 }
122}
123impl<T> Mul<T> for CuDuration
128where
129 T: Into<u64>,
130{
131 type Output = CuDuration;
132
133 fn mul(self, rhs: T) -> CuDuration {
134 let CuDuration(lhs) = self;
135 CuDuration(lhs * rhs.into())
136 }
137}
138
139impl Mul<CuDuration> for u64 {
141 type Output = CuDuration;
142
143 fn mul(self, rhs: CuDuration) -> CuDuration {
144 let CuDuration(nanos) = rhs;
145 CuDuration(self * nanos)
146 }
147}
148
149impl Mul<CuDuration> for u32 {
151 type Output = CuDuration;
152
153 fn mul(self, rhs: CuDuration) -> CuDuration {
154 let CuDuration(nanos) = rhs;
155 CuDuration(self as u64 * nanos)
156 }
157}
158
159impl Mul<CuDuration> for i32 {
161 type Output = CuDuration;
162
163 fn mul(self, rhs: CuDuration) -> CuDuration {
164 let CuDuration(nanos) = rhs;
165 CuDuration(self as u64 * nanos)
166 }
167}
168
169impl Encode for CuDuration {
170 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
171 let CuDuration(nanos) = self;
172 nanos.encode(encoder)
173 }
174}
175
176impl<Context> Decode<Context> for CuDuration {
177 fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
178 Ok(CuDuration(u64::decode(decoder)?))
179 }
180}
181
182impl<'de, Context> BorrowDecode<'de, Context> for CuDuration {
183 fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
184 Ok(CuDuration(u64::decode(decoder)?))
185 }
186}
187
188impl Display for CuDuration {
189 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
190 let Self(nanos) = *self;
191 if nanos >= 86_400_000_000_000 {
192 write!(f, "{:.3} d", nanos as f64 / 86_400_000_000_000.0)
193 } else if nanos >= 3_600_000_000_000 {
194 write!(f, "{:.3} h", nanos as f64 / 3_600_000_000_000.0)
195 } else if nanos >= 60_000_000_000 {
196 write!(f, "{:.3} m", nanos as f64 / 60_000_000_000.0)
197 } else if nanos >= 1_000_000_000 {
198 write!(f, "{:.3} s", nanos as f64 / 1_000_000_000.0)
199 } else if nanos >= 1_000_000 {
200 write!(f, "{:.3} ms", nanos as f64 / 1_000_000.0)
201 } else if nanos >= 1_000 {
202 write!(f, "{:.3} µs", nanos as f64 / 1_000.0)
203 } else {
204 write!(f, "{nanos} ns")
205 }
206 }
207}
208
209pub type CuTime = CuDuration;
211
212#[derive(Copy, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)]
214pub struct OptionCuTime(CuTime);
215
216const NONE_VALUE: u64 = 0xFFFFFFFFFFFFFFFF;
217
218impl OptionCuTime {
219 #[inline]
220 pub fn is_none(&self) -> bool {
221 let Self(CuDuration(nanos)) = self;
222 *nanos == NONE_VALUE
223 }
224
225 #[inline]
226 pub fn none() -> Self {
227 OptionCuTime(CuDuration(NONE_VALUE))
228 }
229
230 #[inline]
231 pub fn unwrap(self) -> CuTime {
232 if self.is_none() {
233 panic!("called `OptionCuTime::unwrap()` on a `None` value");
234 }
235 self.0
236 }
237}
238
239impl Display for OptionCuTime {
240 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
241 if self.is_none() {
242 write!(f, "None")
243 } else {
244 write!(f, "{}", self.0)
245 }
246 }
247}
248
249impl Default for OptionCuTime {
250 fn default() -> Self {
251 Self::none()
252 }
253}
254
255impl From<Option<CuTime>> for OptionCuTime {
256 #[inline]
257 fn from(duration: Option<CuTime>) -> Self {
258 match duration {
259 Some(duration) => OptionCuTime(duration),
260 None => OptionCuTime(CuDuration(NONE_VALUE)),
261 }
262 }
263}
264
265impl From<OptionCuTime> for Option<CuTime> {
266 #[inline]
267 fn from(val: OptionCuTime) -> Self {
268 let OptionCuTime(CuDuration(nanos)) = val;
269 if nanos == NONE_VALUE {
270 None
271 } else {
272 Some(CuDuration(nanos))
273 }
274 }
275}
276
277impl From<CuTime> for OptionCuTime {
278 #[inline]
279 fn from(val: CuTime) -> Self {
280 Some(val).into()
281 }
282}
283
284#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
286pub struct CuTimeRange {
287 pub start: CuTime,
288 pub end: CuTime,
289}
290
291impl From<&[CuTime]> for CuTimeRange {
294 fn from(slice: &[CuTime]) -> Self {
295 CuTimeRange {
296 start: *slice.iter().min().expect("Empty slice"),
297 end: *slice.iter().max().expect("Empty slice"),
298 }
299 }
300}
301
302#[derive(Default, Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize)]
304pub struct PartialCuTimeRange {
305 pub start: OptionCuTime,
306 pub end: OptionCuTime,
307}
308
309impl Display for PartialCuTimeRange {
310 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
311 let start = if self.start.is_none() {
312 "…"
313 } else {
314 &format!("{}", self.start)
315 };
316 let end = if self.end.is_none() {
317 "…"
318 } else {
319 &format!("{}", self.end)
320 };
321 write!(f, "[{start} – {end}]")
322 }
323}
324
325#[derive(Default, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize, Copy)]
328pub enum Tov {
329 #[default]
330 None,
331 Time(CuTime),
332 Range(CuTimeRange),
333}
334
335impl Display for Tov {
336 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
337 match self {
338 Tov::None => write!(f, "None"),
339 Tov::Time(t) => write!(f, "{t}"),
340 Tov::Range(r) => write!(f, "[{} – {}]", r.start, r.end),
341 }
342 }
343}
344
345impl From<Option<CuDuration>> for Tov {
346 fn from(duration: Option<CuDuration>) -> Self {
347 match duration {
348 Some(duration) => Tov::Time(duration),
349 None => Tov::None,
350 }
351 }
352}
353
354impl From<CuDuration> for Tov {
355 fn from(duration: CuDuration) -> Self {
356 Tov::Time(duration)
357 }
358}
359
360#[derive(Clone, Debug)]
364pub struct RobotClock {
365 inner: Clock, ref_time: Instant, }
368
369#[derive(Debug, Clone)]
371pub struct RobotClockMock(Arc<Mock>); impl RobotClockMock {
374 pub fn increment(&self, amount: Duration) {
375 let Self(mock) = self;
376 mock.increment(amount);
377 }
378
379 pub fn decrement(&self, amount: Duration) {
382 let Self(mock) = self;
383 mock.decrement(amount);
384 }
385
386 pub fn value(&self) -> u64 {
388 let Self(mock) = self;
389 mock.value()
390 }
391
392 pub fn now(&self) -> CuTime {
394 let Self(mock) = self;
395 mock.value().into()
396 }
397
398 pub fn set_value(&self, value: u64) {
400 let Self(mock) = self;
401 let v = mock.value();
402 if v < value {
404 self.increment(Duration::from_nanos(value) - Duration::from_nanos(v));
405 } else {
406 self.decrement(Duration::from_nanos(v) - Duration::from_nanos(value));
407 }
408 }
409}
410
411impl RobotClock {
412 pub fn new() -> Self {
415 let clock = Clock::new();
416 let ref_time = clock.now();
417 RobotClock {
418 inner: clock,
419 ref_time,
420 }
421 }
422
423 pub fn from_ref_time(ref_time_ns: u64) -> Self {
425 let clock = Clock::new();
426 let ref_time = clock.now() - Duration::from_nanos(ref_time_ns);
427 RobotClock {
428 inner: Clock::new(),
429 ref_time,
430 }
431 }
432
433 pub fn mock() -> (Self, RobotClockMock) {
436 let (clock, mock) = Clock::mock();
437 let ref_time = clock.now();
438 (
439 RobotClock {
440 inner: clock,
441 ref_time,
442 },
443 RobotClockMock(mock),
444 )
445 }
446
447 #[inline]
450 pub fn now(&self) -> CuTime {
451 (self.inner.now() - self.ref_time).into()
454 }
455
456 #[inline]
458 pub fn recent(&self) -> CuTime {
459 (self.inner.recent() - self.ref_time).into()
460 }
461}
462
463impl Default for RobotClock {
464 fn default() -> Self {
465 Self::new()
466 }
467}
468
469pub trait ClockProvider {
471 fn get_clock(&self) -> RobotClock;
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477 #[test]
478 fn test_cuduration_comparison_operators() {
479 let a = CuDuration(100);
480 let b = CuDuration(200);
481
482 assert!(a < b);
483 assert!(b > a);
484 assert_ne!(a, b);
485 assert_eq!(a, CuDuration(100));
486 }
487
488 #[test]
489 fn test_cuduration_arithmetic_operations() {
490 let a = CuDuration(100);
491 let b = CuDuration(50);
492
493 assert_eq!(a + b, CuDuration(150));
494 assert_eq!(a - b, CuDuration(50));
495 assert_eq!(a * 2u32, CuDuration(200));
496 assert_eq!(a / 2u32, CuDuration(50));
497 }
498
499 #[test]
500 fn test_option_cutime_handling() {
501 let some_time = OptionCuTime::from(Some(CuTime::from(100u64)));
502 let none_time = OptionCuTime::none();
503
504 assert!(!some_time.is_none());
505 assert!(none_time.is_none());
506 assert_eq!(some_time.unwrap(), CuTime::from(100u64));
507 assert_eq!(
508 Option::<CuTime>::from(some_time),
509 Some(CuTime::from(100u64))
510 );
511 assert_eq!(Option::<CuTime>::from(none_time), None);
512 }
513
514 #[test]
515 fn test_mock() {
516 let (clock, mock) = RobotClock::mock();
517 assert_eq!(clock.now(), Duration::from_secs(0).into());
518 mock.increment(Duration::from_secs(1));
519 assert_eq!(clock.now(), Duration::from_secs(1).into());
520 }
521
522 #[test]
523 fn test_mock_clone() {
524 let (clock, mock) = RobotClock::mock();
525 assert_eq!(clock.now(), Duration::from_secs(0).into());
526 let clock_clone = clock.clone();
527 mock.increment(Duration::from_secs(1));
528 assert_eq!(clock_clone.now(), Duration::from_secs(1).into());
529 }
530
531 #[test]
532 fn test_robot_clock_synchronization() {
533 let (clock1, mock) = RobotClock::mock();
535 let clock2 = clock1.clone();
536
537 assert_eq!(clock1.now(), CuDuration(0));
538 assert_eq!(clock2.now(), CuDuration(0));
539
540 mock.increment(Duration::from_secs(5));
541
542 assert_eq!(clock1.now(), Duration::from_secs(5).into());
543 assert_eq!(clock2.now(), Duration::from_secs(5).into());
544 }
545
546 #[test]
547 fn test_from_ref_time() {
548 let tolerance_ms = 10;
549 let clock = RobotClock::from_ref_time(1_000_000_000);
550 assert_relative_eq!(
551 <CuDuration as Into<Duration>>::into(clock.now()).as_millis() as f64,
552 Duration::from_secs(1).as_millis() as f64,
553 epsilon = tolerance_ms as f64
554 );
555 }
556
557 #[test]
558 fn longest_duration() {
559 let maxcu = CuDuration(u64::MAX);
560 let maxd: Duration = maxcu.into();
561 assert_eq!(maxd.as_nanos(), u64::MAX as u128);
562 let s = maxd.as_secs();
563 let y = s / 60 / 60 / 24 / 365;
564 assert!(y >= 584); }
566
567 #[test]
568 fn test_some_time_arithmetics() {
569 let a: CuDuration = 10.into();
570 let b: CuDuration = 20.into();
571 let c = a + b;
572 assert_eq!(c.0, 30);
573 let d = b - a;
574 assert_eq!(d.0, 10);
575 }
576
577 #[test]
578 fn test_build_range_from_slice() {
579 let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
580 assert_eq!(range.start, 10.into());
581 assert_eq!(range.end, 30.into());
582 }
583
584 #[test]
585 fn test_time_range_operations() {
586 let start = CuTime::from(100u64);
588 let end = CuTime::from(200u64);
589 let range = CuTimeRange { start, end };
590
591 assert_eq!(range.start, start);
592 assert_eq!(range.end, end);
593
594 let times = [
596 CuTime::from(150u64),
597 CuTime::from(120u64),
598 CuTime::from(180u64),
599 ];
600 let range_from_slice = CuTimeRange::from(×[..]);
601
602 assert_eq!(range_from_slice.start, CuTime::from(120u64));
604 assert_eq!(range_from_slice.end, CuTime::from(180u64));
605 }
606
607 #[test]
608 fn test_partial_time_range() {
609 let start = CuTime::from(100u64);
611 let end = CuTime::from(200u64);
612
613 let partial_range = PartialCuTimeRange {
614 start: OptionCuTime::from(start),
615 end: OptionCuTime::from(end),
616 };
617
618 let opt_start: Option<CuTime> = partial_range.start.into();
620 let opt_end: Option<CuTime> = partial_range.end.into();
621
622 assert_eq!(opt_start, Some(start));
623 assert_eq!(opt_end, Some(end));
624
625 let partial_undefined = PartialCuTimeRange::default();
627 assert!(partial_undefined.start.is_none());
628 assert!(partial_undefined.end.is_none());
629 }
630
631 #[test]
632 fn test_tov_conversions() {
633 let time = CuTime::from(100u64);
635
636 let tov_time: Tov = time.into();
638 assert!(matches!(tov_time, Tov::Time(_)));
639
640 if let Tov::Time(t) = tov_time {
641 assert_eq!(t, time);
642 }
643
644 let some_time = Some(time);
646 let tov_some: Tov = some_time.into();
647 assert!(matches!(tov_some, Tov::Time(_)));
648
649 let none_time: Option<CuDuration> = None;
650 let tov_none: Tov = none_time.into();
651 assert!(matches!(tov_none, Tov::None));
652
653 let start = CuTime::from(100u64);
655 let end = CuTime::from(200u64);
656 let range = CuTimeRange { start, end };
657 let tov_range = Tov::Range(range);
658
659 assert!(matches!(tov_range, Tov::Range(_)));
660 }
661
662 #[test]
663 fn test_cuduration_display() {
664 let nano = CuDuration(42);
666 assert_eq!(nano.to_string(), "42 ns");
667
668 let micro = CuDuration(42_000);
669 assert_eq!(micro.to_string(), "42.000 µs");
670
671 let milli = CuDuration(42_000_000);
672 assert_eq!(milli.to_string(), "42.000 ms");
673
674 let sec = CuDuration(1_500_000_000);
675 assert_eq!(sec.to_string(), "1.500 s");
676
677 let min = CuDuration(90_000_000_000);
678 assert_eq!(min.to_string(), "1.500 m");
679
680 let hour = CuDuration(3_600_000_000_000);
681 assert_eq!(hour.to_string(), "1.000 h");
682
683 let day = CuDuration(86_400_000_000_000);
684 assert_eq!(day.to_string(), "1.000 d");
685 }
686
687 #[test]
688 fn test_robot_clock_precision() {
689 let clock = RobotClock::new();
692
693 let recent = clock.recent();
695 let now = clock.now();
696
697 assert!(recent <= now);
699
700 let ref_time_ns = 1_000_000_000; let clock = RobotClock::from_ref_time(ref_time_ns);
703
704 let now = clock.now();
706 let now_ns: u64 = now.into();
707
708 let tolerance_ns = 50_000_000; assert!(now_ns >= ref_time_ns);
711 assert!(now_ns < ref_time_ns + tolerance_ns);
712 }
713
714 #[test]
715 fn test_mock_clock_advanced_operations() {
716 let (clock, mock) = RobotClock::mock();
718
719 assert_eq!(clock.now(), CuDuration(0));
721
722 mock.increment(Duration::from_secs(10));
724 assert_eq!(clock.now(), Duration::from_secs(10).into());
725
726 mock.decrement(Duration::from_secs(5));
728 assert_eq!(clock.now(), Duration::from_secs(5).into());
729
730 mock.set_value(30_000_000_000); assert_eq!(clock.now(), Duration::from_secs(30).into());
733
734 assert_eq!(mock.now(), Duration::from_secs(30).into());
736 assert_eq!(mock.value(), 30_000_000_000);
737 }
738
739 #[test]
740 fn test_cuduration_min_max() {
741 assert_eq!(CuDuration::MIN, CuDuration(0));
743
744 let a = CuDuration(100);
746 let b = CuDuration(200);
747
748 assert_eq!(a.min(b), a);
749 assert_eq!(a.max(b), b);
750 assert_eq!(b.min(a), a);
751 assert_eq!(b.max(a), b);
752
753 assert_eq!(a.min(a), a);
755 assert_eq!(a.max(a), a);
756
757 assert_eq!(a.min(CuDuration::MIN), CuDuration::MIN);
759 assert_eq!(a.max(CuDuration::MAX), CuDuration::MAX);
760 }
761
762 #[test]
763 fn test_clock_provider_trait() {
764 struct TestClockProvider {
766 clock: RobotClock,
767 }
768
769 impl ClockProvider for TestClockProvider {
770 fn get_clock(&self) -> RobotClock {
771 self.clock.clone()
772 }
773 }
774
775 let (clock, mock) = RobotClock::mock();
777 let provider = TestClockProvider { clock };
778
779 let provider_clock = provider.get_clock();
781 assert_eq!(provider_clock.now(), CuDuration(0));
782
783 mock.increment(Duration::from_secs(5));
785 assert_eq!(provider_clock.now(), Duration::from_secs(5).into());
786 }
787}