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}