Skip to main content

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}