cu29_clock/
lib.rs

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/// For Robot times, the underlying type is a u64 representing nanoseconds.
21/// It is always positive to simplify the reasoning on the user side.
22#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
23pub struct CuDuration(pub u64);
24
25impl CuDuration {
26    // Lowest value a CuDuration can have.
27    pub const MIN: CuDuration = CuDuration(0u64);
28    // Highest value a CuDuration can have reserving the max value for None.
29    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
48/// bridge the API with standard Durations.
49impl 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
111// a way to divide a duration by a scalar.
112// useful to compute averages for example.
113impl<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}
123//
124// a way to multiply a duration by a scalar.
125// useful to compute offsets for example.
126// CuDuration * scalar
127impl<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
139// u64 * CuDuration
140impl 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
149// u32 * CuDuration
150impl 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
159// i32 * CuDuration
160impl 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
209/// A robot time is just a duration from a fixed point in time.
210pub type CuTime = CuDuration;
211
212/// Homebrewed `Option<CuDuration>` to avoid using 128bits just to represent an Option.
213#[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/// Represents a time range.
285#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
286pub struct CuTimeRange {
287    pub start: CuTime,
288    pub end: CuTime,
289}
290
291/// Builds a time range from a slice of CuTime.
292/// This is an O(n) operation.
293impl 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/// Represents a time range with possible undefined start or end or both.
303#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize)]
304pub struct PartialCuTimeRange {
305    pub start: OptionCuTime,
306    pub end: OptionCuTime,
307}
308
309impl Default for PartialCuTimeRange {
310    fn default() -> Self {
311        PartialCuTimeRange {
312            start: OptionCuTime::none(),
313            end: OptionCuTime::none(),
314        }
315    }
316}
317
318/// The time of validity of a message can be more than one time but can be a time range of Tovs.
319/// For example a sub scan for a lidar, a set of images etc... can have a range of validity.
320#[derive(Default, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize, Copy)]
321pub enum Tov {
322    #[default]
323    None,
324    Time(CuTime),
325    Range(CuTimeRange),
326}
327
328impl From<Option<CuDuration>> for Tov {
329    fn from(duration: Option<CuDuration>) -> Self {
330        match duration {
331            Some(duration) => Tov::Time(duration),
332            None => Tov::None,
333        }
334    }
335}
336
337impl From<CuDuration> for Tov {
338    fn from(duration: CuDuration) -> Self {
339        Tov::Time(duration)
340    }
341}
342
343/// A running Robot clock.
344/// The clock is a monotonic clock that starts at an arbitrary reference time.
345/// It is clone resilient, ie a clone will be the same clock, even when mocked.
346#[derive(Clone, Debug)]
347pub struct RobotClock {
348    inner: Clock,      // This is a wrapper on quanta::Clock today.
349    ref_time: Instant, // The reference instant on which this clock is based.
350}
351
352/// A mock clock that can be controlled by the user.
353#[derive(Debug, Clone)]
354pub struct RobotClockMock(Arc<Mock>); // wraps the Mock from quanta today.
355
356impl RobotClockMock {
357    pub fn increment(&self, amount: Duration) {
358        let Self(mock) = self;
359        mock.increment(amount);
360    }
361
362    /// Decrements the time by the given amount.
363    /// Be careful this brakes the monotonicity of the clock.
364    pub fn decrement(&self, amount: Duration) {
365        let Self(mock) = self;
366        mock.decrement(amount);
367    }
368
369    /// Gets the current value of time.
370    pub fn value(&self) -> u64 {
371        let Self(mock) = self;
372        mock.value()
373    }
374
375    /// A convenient way to get the current time from the mocking side.
376    pub fn now(&self) -> CuTime {
377        let Self(mock) = self;
378        mock.value().into()
379    }
380
381    /// Sets the absolute value of the time.
382    pub fn set_value(&self, value: u64) {
383        let Self(mock) = self;
384        let v = mock.value();
385        // had to work around the quata API here.
386        if v < value {
387            self.increment(Duration::from_nanos(value) - Duration::from_nanos(v));
388        } else {
389            self.decrement(Duration::from_nanos(v) - Duration::from_nanos(value));
390        }
391    }
392}
393
394impl RobotClock {
395    /// Creates a RobotClock using now as its reference time.
396    /// It will start a 0ns incrementing monotonically.
397    pub fn new() -> Self {
398        let clock = Clock::new();
399        let ref_time = clock.now();
400        RobotClock {
401            inner: clock,
402            ref_time,
403        }
404    }
405
406    /// Builds a monotonic clock starting at the given reference time.
407    pub fn from_ref_time(ref_time_ns: u64) -> Self {
408        let clock = Clock::new();
409        let ref_time = clock.now() - Duration::from_nanos(ref_time_ns);
410        RobotClock {
411            inner: Clock::new(),
412            ref_time,
413        }
414    }
415
416    /// Build a fake clock with a reference time of 0.
417    /// The RobotMock interface enables you to control all the clones of the clock given.
418    pub fn mock() -> (Self, RobotClockMock) {
419        let (clock, mock) = Clock::mock();
420        let ref_time = clock.now();
421        (
422            RobotClock {
423                inner: clock,
424                ref_time,
425            },
426            RobotClockMock(mock),
427        )
428    }
429
430    // Now returns the time that passed since the reference time, usually the start time.
431    // It is a monotonically increasing value.
432    #[inline]
433    pub fn now(&self) -> CuTime {
434        // TODO: this could be further optimized to avoid this constant conversion from 2 fields to one under the hood.
435        // Let's say this is the default implementation.
436        (self.inner.now() - self.ref_time).into()
437    }
438
439    // A less precise but quicker time
440    #[inline]
441    pub fn recent(&self) -> CuTime {
442        (self.inner.recent() - self.ref_time).into()
443    }
444}
445
446impl Default for RobotClock {
447    fn default() -> Self {
448        Self::new()
449    }
450}
451
452/// A trait to provide a clock to the runtime.
453pub trait ClockProvider {
454    fn get_clock(&self) -> RobotClock;
455}
456
457#[cfg(test)]
458mod tests {
459    use super::*;
460
461    #[test]
462    fn test_mock() {
463        let (clock, mock) = RobotClock::mock();
464        assert_eq!(clock.now(), Duration::from_secs(0).into());
465        mock.increment(Duration::from_secs(1));
466        assert_eq!(clock.now(), Duration::from_secs(1).into());
467    }
468
469    #[test]
470    fn test_mock_clone() {
471        let (clock, mock) = RobotClock::mock();
472        assert_eq!(clock.now(), Duration::from_secs(0).into());
473        let clock_clone = clock.clone();
474        mock.increment(Duration::from_secs(1));
475        assert_eq!(clock_clone.now(), Duration::from_secs(1).into());
476    }
477
478    #[test]
479    fn test_from_ref_time() {
480        let tolerance_ms = 10;
481        let clock = RobotClock::from_ref_time(1_000_000_000);
482        assert_relative_eq!(
483            <CuDuration as Into<Duration>>::into(clock.now()).as_millis() as f64,
484            Duration::from_secs(1).as_millis() as f64,
485            epsilon = tolerance_ms as f64
486        );
487    }
488
489    #[test]
490    fn longest_duration() {
491        let maxcu = CuDuration(u64::MAX);
492        let maxd: Duration = maxcu.into();
493        assert_eq!(maxd.as_nanos(), u64::MAX as u128);
494        let s = maxd.as_secs();
495        let y = s / 60 / 60 / 24 / 365;
496        assert!(y >= 584); // 584 years of robot uptime, we should be good.
497    }
498
499    #[test]
500    fn test_some_time_arithmetics() {
501        let a: CuDuration = 10.into();
502        let b: CuDuration = 20.into();
503        let c = a + b;
504        assert_eq!(c.0, 30);
505        let d = b - a;
506        assert_eq!(d.0, 10);
507    }
508
509    #[test]
510    fn test_build_range_from_slice() {
511        let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
512        assert_eq!(range.start, 10.into());
513        assert_eq!(range.end, 30.into());
514    }
515}