1use std::{
2 fmt,
3 time::{Duration, Instant},
4};
5
6use crate::register;
7
8pub type CycleIdValue = u16;
9
10#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
16pub struct CycleId(CycleIdValue);
17
18impl CycleId {
19 #[must_use]
20 pub const fn from_value(value: CycleIdValue) -> Self {
21 Self(value)
22 }
23
24 #[must_use]
25 pub const fn to_value(self) -> CycleIdValue {
26 self.0
27 }
28}
29
30impl From<CycleIdValue> for CycleId {
31 fn from(from: CycleIdValue) -> Self {
32 Self::from_value(from)
33 }
34}
35
36impl From<CycleId> for CycleIdValue {
37 fn from(from: CycleId) -> Self {
38 from.to_value()
39 }
40}
41
42impl fmt::Display for CycleId {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "@{}", self.to_value())
45 }
46}
47
48#[derive(Debug, Clone, Eq, PartialEq)]
49pub struct CycleTimeStamp {
50 pub id: CycleId,
51
52 pub ts: Instant,
53}
54
55impl CycleTimeStamp {
56 #[must_use]
57 pub fn now(id: CycleId) -> Self {
58 Self {
59 id,
60 ts: Instant::now(),
61 }
62 }
63}
64
65#[derive(Debug, Clone)]
66pub struct CyclicRegisterMeasurements<RegisterValue> {
67 pub cycle_id: CycleId,
68
69 pub cycle_ts: CycleTimeStamp,
71
72 pub registers: Vec<register::IndexedMeasurement<RegisterValue>>,
77}
78
79impl<RegisterValue> CyclicRegisterMeasurements<RegisterValue> {
80 #[must_use]
81 pub fn count_number_unique_of_registers(&self) -> usize {
82 let mut register_indices: Vec<_> = self.registers.iter().map(|m| m.index).collect();
83 register_indices.sort_unstable();
84 register_indices.dedup();
85 register_indices.len()
86 }
87
88 #[must_use]
89 pub fn contains_duplicate_registers(&self) -> bool {
90 self.registers.len() > self.count_number_unique_of_registers()
91 }
92}
93
94pub fn skip_missed_cycles(
103 cycle_time: Duration,
104 expected_cycle_start: Instant,
105 actual_cycle_start: Instant,
106) -> Result<Instant, (Instant, u32)> {
107 debug_assert!(cycle_time > Duration::ZERO);
108 if expected_cycle_start >= actual_cycle_start {
109 return Ok(expected_cycle_start);
110 }
111 let elapsed_cycles = actual_cycle_start
112 .duration_since(expected_cycle_start)
113 .as_secs_f64()
114 / cycle_time.as_secs_f64();
115 debug_assert!(elapsed_cycles > 0.0);
116 if elapsed_cycles < 1.0 {
117 return Ok(expected_cycle_start);
118 }
119 let missed_cycles = elapsed_cycles.floor();
121 debug_assert!(missed_cycles <= f64::from(u32::MAX));
122 #[allow(clippy::cast_sign_loss)]
123 let missed_cycles = missed_cycles.min(f64::from(u32::MAX)) as u32;
124 let skipped_cycles_duration = missed_cycles * cycle_time;
126 Err((
127 expected_cycle_start + skipped_cycles_duration,
128 missed_cycles,
129 ))
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 #[allow(clippy::too_many_lines)] fn should_skip_missed_cycles() {
139 let cycle_time = Duration::from_millis(2);
140 let half_cycle_time = cycle_time / 2;
141 let quarter_cycle_time = cycle_time / 4;
142 let three_quarter_cycle_time = half_cycle_time + quarter_cycle_time;
143 let actual_cycle_start = Instant::now();
144
145 assert_eq!(
147 Ok(actual_cycle_start),
148 skip_missed_cycles(cycle_time, actual_cycle_start, actual_cycle_start),
149 );
150
151 assert_eq!(
153 Ok(actual_cycle_start + 100 * cycle_time),
154 skip_missed_cycles(
155 cycle_time,
156 actual_cycle_start + 100 * cycle_time,
157 actual_cycle_start
158 ),
159 );
160
161 assert_eq!(
163 Ok(actual_cycle_start
164 .checked_sub(Duration::from_nanos(1))
165 .unwrap()),
166 skip_missed_cycles(
167 cycle_time,
168 actual_cycle_start
169 .checked_sub(Duration::from_nanos(1))
170 .unwrap(),
171 actual_cycle_start
172 ),
173 );
174 assert_eq!(
175 Ok(actual_cycle_start.checked_sub(quarter_cycle_time).unwrap()),
176 skip_missed_cycles(
177 cycle_time,
178 actual_cycle_start.checked_sub(quarter_cycle_time).unwrap(),
179 actual_cycle_start
180 ),
181 );
182 assert_eq!(
183 Ok(actual_cycle_start.checked_sub(half_cycle_time).unwrap()),
184 skip_missed_cycles(
185 cycle_time,
186 actual_cycle_start.checked_sub(half_cycle_time).unwrap(),
187 actual_cycle_start
188 ),
189 );
190 assert_eq!(
191 Ok(actual_cycle_start
192 .checked_sub(three_quarter_cycle_time)
193 .unwrap()),
194 skip_missed_cycles(
195 cycle_time,
196 actual_cycle_start
197 .checked_sub(three_quarter_cycle_time)
198 .unwrap(),
199 actual_cycle_start
200 ),
201 );
202 assert_eq!(
203 Ok(actual_cycle_start
204 .checked_sub(cycle_time - Duration::from_nanos(1))
205 .unwrap()),
206 skip_missed_cycles(
207 cycle_time,
208 actual_cycle_start
209 .checked_sub(cycle_time - Duration::from_nanos(1))
210 .unwrap(),
211 actual_cycle_start
212 ),
213 );
214
215 for i in 1u32..10u32 {
217 assert_eq!(
218 Err((actual_cycle_start, i)),
219 skip_missed_cycles(
220 cycle_time,
221 actual_cycle_start.checked_sub(i * cycle_time).unwrap(),
222 actual_cycle_start
223 ),
224 );
225 assert_eq!(
226 Err((
227 actual_cycle_start
228 .checked_sub(Duration::from_nanos(1))
229 .unwrap(),
230 i
231 )),
232 skip_missed_cycles(
233 cycle_time,
234 actual_cycle_start
235 .checked_sub(i * cycle_time)
236 .unwrap()
237 .checked_sub(Duration::from_nanos(1))
238 .unwrap(),
239 actual_cycle_start
240 ),
241 );
242 assert_eq!(
243 Err((
244 actual_cycle_start.checked_sub(quarter_cycle_time).unwrap(),
245 i
246 )),
247 skip_missed_cycles(
248 cycle_time,
249 actual_cycle_start
250 .checked_sub(i * cycle_time)
251 .unwrap()
252 .checked_sub(quarter_cycle_time)
253 .unwrap(),
254 actual_cycle_start
255 ),
256 );
257 assert_eq!(
258 Err((actual_cycle_start.checked_sub(half_cycle_time).unwrap(), i)),
259 skip_missed_cycles(
260 cycle_time,
261 actual_cycle_start
262 .checked_sub(i * cycle_time)
263 .unwrap()
264 .checked_sub(half_cycle_time)
265 .unwrap(),
266 actual_cycle_start
267 ),
268 );
269 assert_eq!(
270 Err((
271 actual_cycle_start
272 .checked_sub(three_quarter_cycle_time)
273 .unwrap(),
274 i
275 )),
276 skip_missed_cycles(
277 cycle_time,
278 actual_cycle_start
279 .checked_sub(i * cycle_time)
280 .unwrap()
281 .checked_sub(three_quarter_cycle_time)
282 .unwrap(),
283 actual_cycle_start
284 ),
285 );
286 assert_eq!(
287 Err((
288 actual_cycle_start
289 .checked_sub(cycle_time - Duration::from_nanos(1))
290 .unwrap(),
291 i
292 )),
293 skip_missed_cycles(
294 cycle_time,
295 actual_cycle_start
296 .checked_sub(i * cycle_time)
297 .unwrap()
298 .checked_sub(cycle_time - Duration::from_nanos(1))
299 .unwrap(),
300 actual_cycle_start
301 ),
302 );
303 }
304 }
305}