vexide_core/
time.rs

1//! Extended VEXos system time APIs.
2
3use core::{
4    ops::{Add, AddAssign, Sub, SubAssign},
5    time::Duration,
6};
7
8use vex_sdk::{vexSystemHighResTimeGet, vexSystemPowerupTimeGet, vexSystemTimeGet};
9
10/// Returns the duration that the brain has been powered on.
11///
12/// # Precision
13///
14/// The returned [`Duration`] has a precision of 1 microsecond.
15#[must_use]
16pub fn system_uptime() -> Duration {
17    Duration::from_micros(unsafe { vexSystemPowerupTimeGet() })
18}
19
20/// Returns the duration that the brain's user processor has been running.
21///
22/// This is effectively the time since the current program was started.
23///
24/// # Precision
25///
26/// The returned [`Duration`] has a precision of 1 microsecond.
27#[must_use]
28pub fn user_uptime() -> Duration {
29    Duration::from_micros(unsafe { vexSystemHighResTimeGet() })
30}
31
32/// A timestamp recorded by the Brain's low-resolution private timer.
33///
34/// This type is not in sync with [`Instant`], which instead uses the brain's global high-resolution
35/// timer.
36///
37/// [`Instant`]: https://doc.rust-lang.org/stable/std/time/struct.Instant.html
38///
39/// # Precision
40///
41/// This type has a precision of 1 millisecond.
42#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct LowResolutionTime {
44    millis: u32,
45}
46
47impl LowResolutionTime {
48    /// An anchor in time which represents the start of the clock.
49    ///
50    /// In practice, the epoch represents the start of the Brain's user processor, meaning the start
51    /// of the current user program.
52    pub const EPOCH: LowResolutionTime = LowResolutionTime { millis: 0 };
53
54    /// Returns a low-resolution timestamp corresponding to "now".
55    ///
56    /// # Examples
57    ///
58    /// ```
59    /// use vexide::time::LowResolutionTime;
60    ///
61    /// let now = LowResolutionTime::now();
62    /// ```
63    #[must_use]
64    pub fn now() -> Self {
65        Self {
66            millis: unsafe { vexSystemTimeGet() },
67        }
68    }
69
70    /// Creates a new timestamp at the provided number of milliseconds since
71    /// [`LowResolutionTime::EPOCH`].
72    ///
73    /// [`EPOCH`]: Self::EPOCH
74    ///
75    /// # Use this sparingly.
76    ///
77    /// This method generally only exists for compatibility with FFI and system APIs. The only clock
78    /// measurement that should be provided to `millis` should be measurements derived from the CPU1
79    /// private timer (e.g. [`vexSystemTimeGet`]) to ensure that clock drift is not a factor.
80    ///
81    /// When possible, prefer using [`LowResolutionTime::now`].
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// use vexide::time::LowResolutionTime;
87    ///
88    /// // Equivalent to `LowResolutionTime::now()`.
89    /// let now = LowResolutionTime::from_millis_since_epoch(unsafe { vex_sdk::vexSystemTimeGet() });
90    /// ```
91    #[must_use]
92    pub const fn from_millis_since_epoch(millis: u32) -> Self {
93        Self { millis }
94    }
95
96    /// Returns the amount of time elapsed from another timestamp to this one, or zero duration if
97    /// that timestamp is later than this one.
98    ///
99    /// # Examples
100    ///
101    /// ```no_run
102    /// use std::time::Duration;
103    ///
104    /// use vexide::{prelude::*, time::LowResolutionTime};
105    ///
106    /// #[vexide::main]
107    /// async fn main(_peripherals: Peripherals) {
108    ///     let now = LowResolutionTime::now();
109    ///     sleep(Duration::new(1, 0)).await;
110    ///
111    ///     let new_now = LowResolutionTime::now();
112    ///     println!("{:?}", new_now.duration_since(now));
113    ///     println!("{:?}", now.duration_since(new_now)); // 0ns
114    /// }
115    /// ```
116    #[must_use]
117    pub fn duration_since(&self, earlier: LowResolutionTime) -> Duration {
118        self.checked_duration_since(earlier).unwrap_or_default()
119    }
120
121    /// Returns the amount of time elapsed from another timestamp to this one, or None if that
122    /// timestamp is later than this one.
123    ///
124    /// # Examples
125    ///
126    /// ```no_run
127    /// use std::time::Duration;
128    ///
129    /// use vexide::{prelude::*, time::LowResolutionTime};
130    ///
131    /// #[vexide::main]
132    /// async fn main(_peripherals: Peripherals) {
133    ///     let now = LowResolutionTime::now();
134    ///     sleep(Duration::new(1, 0)).await;
135    ///
136    ///     let new_now = LowResolutionTime::now();
137    ///     println!("{:?}", new_now.checked_duration_since(now));
138    ///     println!("{:?}", now.checked_duration_since(new_now)); // None
139    /// }
140    /// ```
141    #[must_use]
142    pub const fn checked_duration_since(&self, earlier: LowResolutionTime) -> Option<Duration> {
143        if earlier.millis < self.millis {
144            Some(Duration::from_millis((self.millis - earlier.millis) as _))
145        } else {
146            None
147        }
148    }
149
150    /// Returns the amount of time elapsed from another timestamp to this one, or zero duration if
151    /// that timestamp is later than this one.
152    ///
153    /// # Examples
154    ///
155    /// ```no_run
156    /// use std::time::Duration;
157    ///
158    /// use vexide::{prelude::*, time::LowResolutionTime};
159    ///
160    /// #[vexide::main]
161    /// async fn main(_peripherals: Peripherals) {
162    ///     let now = LowResolutionTime::now();
163    ///     sleep(Duration::new(1, 0)).await;
164    ///     let new_now = LowResolutionTime::now();
165    ///     println!("{:?}", new_now.saturating_duration_since(now));
166    ///     println!("{:?}", now.saturating_duration_since(new_now)); // 0ns
167    /// }
168    /// ```
169    #[must_use]
170    pub fn saturating_duration_since(&self, earlier: LowResolutionTime) -> Duration {
171        self.checked_duration_since(earlier).unwrap_or_default()
172    }
173
174    /// Returns the amount of time elapsed since this timestamp.
175    ///
176    /// # Examples
177    ///
178    /// ```no_run
179    /// use std::time::Duration;
180    ///
181    /// use vexide::{prelude::*, time::LowResolutionTime};
182    ///
183    /// #[vexide::main]
184    /// async fn main(_peripherals: Peripherals) {
185    ///     let start = LowResolutionTime::now();
186    ///     let three_secs = Duration::from_secs(3);
187    ///     sleep(three_secs).await;
188    ///     assert!(start.elapsed() >= three_secs);
189    /// }
190    /// ```
191    #[must_use]
192    pub fn elapsed(&self) -> Duration {
193        Self::now() - *self
194    }
195
196    /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
197    /// `LowResolutionTime` (which means it's inside the bounds of the underlying data structure),
198    /// `None` otherwise.
199    #[must_use]
200    pub fn checked_add(self, rhs: Duration) -> Option<LowResolutionTime> {
201        Some(Self {
202            millis: self.millis.checked_add(rhs.as_millis().try_into().ok()?)?,
203        })
204    }
205
206    /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
207    /// `LowResolutionTime` (which means it's inside the bounds of the underlying data structure),
208    /// `None` otherwise.
209    #[must_use]
210    pub fn checked_sub(self, rhs: Duration) -> Option<LowResolutionTime> {
211        Some(Self {
212            millis: self.millis.checked_sub(rhs.as_millis().try_into().ok()?)?,
213        })
214    }
215}
216
217impl Add<Duration> for LowResolutionTime {
218    type Output = LowResolutionTime;
219
220    /// # Panics
221    ///
222    /// This function may panic if the resulting point in time cannot be represented by the
223    /// underlying data structure. See [`LowResolutionTime::checked_add`] for a version without
224    /// panic.
225    fn add(self, rhs: Duration) -> Self::Output {
226        self.checked_add(rhs)
227            .expect("overflow when adding duration to timestamp")
228    }
229}
230
231impl AddAssign<Duration> for LowResolutionTime {
232    fn add_assign(&mut self, other: Duration) {
233        *self = *self + other;
234    }
235}
236
237impl Sub<Duration> for LowResolutionTime {
238    type Output = LowResolutionTime;
239
240    fn sub(self, other: Duration) -> LowResolutionTime {
241        self.checked_sub(other)
242            .expect("overflow when subtracting duration from timestamp")
243    }
244}
245
246impl SubAssign<Duration> for LowResolutionTime {
247    fn sub_assign(&mut self, other: Duration) {
248        *self = *self - other;
249    }
250}
251
252impl Sub<LowResolutionTime> for LowResolutionTime {
253    type Output = Duration;
254
255    /// Returns the amount of time elapsed from another time to this one, or zero duration if that
256    /// time is later than this one.
257    fn sub(self, other: LowResolutionTime) -> Duration {
258        self.duration_since(other)
259    }
260}