ridl_rt/sample.rs
1//! Time, the envelope, and the values a read returns (ridl §3.1, §4.5, §9).
2
3use crate::payload::Violation;
4
5/// A point in time, in microseconds since the PTP epoch, on the TAI time scale
6/// (ridl §3.1).
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
8pub struct Timestamp(pub i64);
9
10/// A length of time, in microseconds.
11///
12/// This is not `core::time::Duration`: generated code writes
13/// `ridl_rt::sample::Duration` in full.
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct Duration(pub i64);
16
17/// The sender's timestamp and sequence number (ridl §3.1). The runtime stamps
18/// it from its clock, and no relay changes it.
19///
20/// Before a signal's first publication, its envelope has `seq` 0 and the time
21/// at which the channel was created. On a call, `seq` is unique for each
22/// caller, not for each channel.
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct Envelope {
25 /// When the sender published, raised or called the instance (ridl §3.1).
26 pub stamp: Timestamp,
27 /// The sender's sequence number. On an event channel, a gap is a loss
28 /// (ridl §3.1).
29 pub seq: u64,
30}
31
32/// Where a signal's value comes from (ridl §4.5).
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum Provenance {
35 /// No publication yet: the value is the init value.
36 Init,
37 /// The value is the latest publication.
38 Live,
39 /// The channel is in the invalid state: the value is the last good value,
40 /// or the init value when there is none.
41 Invalid(Cause),
42}
43
44/// Why a channel is invalid.
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum Cause {
47 /// The provider declared the invalid state (ridl §4.5).
48 Declared,
49 /// The consumer's binding detected an invalid payload.
50 Detected(Detection),
51}
52
53/// What a consumer's binding detected in a payload.
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub enum Detection {
56 /// The payload breaks a typl constraint: `INVALID_VALUE`, ridl §10.2.
57 InvalidValue(Violation),
58 /// The payload is not a well-formed encoding: a serialization failure,
59 /// ridl §10.3.
60 Corrupt,
61}
62
63/// How old a value is, measured against its staleness bound (ridl §9).
64#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65pub enum Freshness {
66 /// Within the bound.
67 Fresh,
68 /// Older than the bound.
69 Stale {
70 /// How far past the bound the value is.
71 by: Duration,
72 },
73 /// The value has no staleness bound: its member's timing has no `max`, as
74 /// under `@[1s..]`. A signal with no `@` annotation is not unbounded,
75 /// because it receives the default range (ridl §9.1).
76 Unbounded,
77}
78
79/// A signal value with its provenance, its freshness and its envelope.
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub struct Sample<T> {
82 /// The value. Never absent: the init value under `Init`, and the last good
83 /// value or the init value under `Invalid`.
84 pub value: T,
85 /// Where the value comes from.
86 pub provenance: Provenance,
87 /// How old the value is.
88 pub freshness: Freshness,
89 /// The sender's timestamp and sequence number.
90 pub envelope: Envelope,
91}
92
93impl<T> Sample<T> {
94 /// `true` when the provenance is `Live` and the freshness is not `Stale`.
95 pub fn usable(&self) -> bool {
96 matches!(self.provenance, Provenance::Live)
97 && !matches!(self.freshness, Freshness::Stale { .. })
98 }
99}
100
101/// An event occurrence as a consumer receives it.
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
103pub struct Occurrence<T> {
104 /// The payload, or what the binding detected when the payload failed its
105 /// check.
106 pub payload: Result<T, Detection>,
107 /// The sender's timestamp and sequence number.
108 pub envelope: Envelope,
109}
110
111#[cfg(test)]
112mod tests {
113 use super::{Cause, Detection, Duration, Envelope, Freshness, Provenance, Sample, Timestamp};
114 use crate::payload::{Rule, Violation};
115
116 fn sample(provenance: Provenance, freshness: Freshness) -> Sample<u8> {
117 Sample {
118 value: 0,
119 provenance,
120 freshness,
121 envelope: Envelope {
122 stamp: Timestamp(0),
123 seq: 0,
124 },
125 }
126 }
127
128 #[test]
129 fn only_a_live_value_that_is_not_stale_is_usable() {
130 let violation = Violation {
131 type_name: "Speed",
132 rule: Rule::Range,
133 };
134 let provenances = [
135 Provenance::Init,
136 Provenance::Live,
137 Provenance::Invalid(Cause::Declared),
138 Provenance::Invalid(Cause::Detected(Detection::InvalidValue(violation))),
139 Provenance::Invalid(Cause::Detected(Detection::Corrupt)),
140 ];
141 let freshnesses = [
142 Freshness::Fresh,
143 Freshness::Stale { by: Duration(1) },
144 Freshness::Unbounded,
145 ];
146 for provenance in provenances {
147 for freshness in freshnesses {
148 let expected =
149 provenance == Provenance::Live && !matches!(freshness, Freshness::Stale { .. });
150 assert_eq!(
151 sample(provenance, freshness).usable(),
152 expected,
153 "{provenance:?} with {freshness:?}"
154 );
155 }
156 }
157 }
158}