#![forbid(unsafe_code)]
use std::cell::Cell;
use ridl_rt::contract::{
CatalogHash, CatalogRef, EncodedSizes, Interaction, Interface, InterfaceNo, Kind, Member,
Ordinal, PayloadInfo, Signal, Timing, TimingMode,
};
use ridl_rt::encoding::ReprC;
use ridl_rt::error::Contract;
use ridl_rt::payload::{
EncodeError, Encoded, Malformed, Payload, Ref, Rule, VerifyError, Violation,
};
use ridl_rt::port::{
Attached, Clock, RawSample, ReadError, SignalReader, SignalWriter, WriteError,
};
use ridl_rt::sample::{
Cause, Detection, Duration, Envelope, Freshness, Provenance, Sample, Timestamp,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Speed(pub u16);
impl Payload<ReprC> for Speed {
const MAX_SIZE: usize = 2;
type View<'a> = &'a [u8];
fn encode<'o>(&self, out: &'o mut [u8]) -> Result<Encoded<'o, &'o [u8]>, EncodeError> {
let available = out.len();
let Some(front) = out.get_mut(..2) else {
return Err(EncodeError::Capacity {
needed: 2,
available,
});
};
front.copy_from_slice(&self.0.to_le_bytes());
let bytes: &'o [u8] = front;
Ok(Encoded { bytes, view: bytes })
}
fn verify(buf: &[u8]) -> Result<&[u8], VerifyError> {
let bytes: [u8; 2] = buf
.try_into()
.map_err(|_| VerifyError::Structure(Malformed::OutOfBounds))?;
if u16::from_le_bytes(bytes) > 300 {
return Err(VerifyError::Contract(Violation {
type_name: "Speed",
rule: Rule::Range,
}));
}
Ok(buf)
}
fn decode(r: Ref<'_, Self, ReprC>) -> Self {
let b = r.bytes();
Speed(u16::from_le_bytes([b[0], b[1]]))
}
}
pub const VEHICLE: CatalogRef = CatalogRef {
name: "vehicle",
hash: CatalogHash([7; 32]),
};
pub struct Drivetrain;
impl Interface for Drivetrain {
const CATALOG: &'static CatalogRef = &VEHICLE;
const NUMBER: InterfaceNo = InterfaceNo(1);
const PROVISIONAL: bool = false;
const NAME: &'static str = "Drivetrain";
const MEMBERS: &'static [Member] = &[Member {
ordinal: Ordinal(1),
kind: Kind::Signal,
name: "speed",
timing: Some(Timing {
mode: TimingMode::Range,
min: Some(Duration(20_000)),
max: Some(Duration(500_000)),
}),
payloads: &[PayloadInfo {
type_name: "Speed",
max_size: EncodedSizes {
proto3: None,
flatbuffers: None,
repr_c: Some(2),
},
}],
}];
}
pub struct SpeedSignal;
impl Interaction for SpeedSignal {
type Iface = Drivetrain;
const MEMBER: &'static Member = &Drivetrain::MEMBERS[0];
}
impl Signal for SpeedSignal {
type Payload = Speed;
fn init() -> Speed {
Speed(30)
}
}
pub fn read_speed(port: &dyn SignalReader) -> Result<Sample<Speed>, ReadError> {
let mut buf = [0u8; <Speed as Payload<ReprC>>::MAX_SIZE];
let raw = port.read(Drivetrain::NUMBER, SpeedSignal::MEMBER.ordinal, &mut buf)?;
let (value, provenance) = match raw.provenance {
Provenance::Init | Provenance::Invalid(Cause::Declared) if raw.len == 0 => {
(SpeedSignal::init(), raw.provenance)
}
_ => match Ref::<Speed, ReprC>::verify(&buf[..raw.len]) {
Ok(proof) => (proof.decode(), raw.provenance),
Err(VerifyError::Contract(violation)) => (
SpeedSignal::init(),
Provenance::Invalid(Cause::Detected(Detection::InvalidValue(violation))),
),
Err(_) => (
SpeedSignal::init(),
Provenance::Invalid(Cause::Detected(Detection::Corrupt)),
),
},
};
Ok(Sample {
value,
provenance,
freshness: raw.freshness,
envelope: raw.envelope,
})
}
pub struct Memory {
now: Cell<i64>,
bytes: [u8; 2],
len: usize,
provenance: Provenance,
envelope: Envelope,
staged: Option<Staged>,
}
enum Staged {
Value([u8; 2], usize),
Invalid,
Touch,
}
impl Memory {
pub fn new(now: Timestamp, init: &[u8]) -> Memory {
let mut bytes = [0u8; 2];
bytes[..init.len()].copy_from_slice(init);
Memory {
now: Cell::new(now.0),
bytes,
len: init.len(),
provenance: Provenance::Init,
envelope: Envelope { stamp: now, seq: 0 },
staged: None,
}
}
pub fn advance(&self, by: Duration) {
self.now.set(self.now.get() + by.0);
}
fn member(iface: InterfaceNo, ord: Ordinal) -> Option<&'static Member> {
if iface != Drivetrain::NUMBER {
return None;
}
Drivetrain::MEMBERS
.iter()
.find(|m| m.ordinal == ord && m.kind == Kind::Signal)
}
}
impl Attached for Memory {
fn catalog(&self) -> &CatalogRef {
Drivetrain::CATALOG
}
}
impl Clock for Memory {
fn now(&self) -> Timestamp {
Timestamp(self.now.get())
}
}
impl SignalReader for Memory {
fn read(
&self,
iface: InterfaceNo,
ord: Ordinal,
out: &mut [u8],
) -> Result<RawSample, ReadError> {
let member =
Memory::member(iface, ord).ok_or(ReadError::Contract(Contract::UnknownInteraction))?;
let Some(front) = out.get_mut(..self.len) else {
return Err(ReadError::Short { needed: self.len });
};
front.copy_from_slice(&self.bytes[..self.len]);
let freshness = match member.timing.and_then(|t| t.max) {
None => Freshness::Unbounded,
Some(max) => {
let age = self.now().0 - self.envelope.stamp.0;
if age > max.0 {
Freshness::Stale {
by: Duration(age - max.0),
}
} else {
Freshness::Fresh
}
}
};
Ok(RawSample {
provenance: self.provenance,
freshness,
envelope: self.envelope,
len: self.len,
})
}
}
impl SignalWriter for Memory {
fn set(&mut self, iface: InterfaceNo, ord: Ordinal, bytes: &[u8]) -> Result<(), WriteError> {
if Memory::member(iface, ord).is_none() {
return Err(WriteError::Contract(Contract::UnknownInteraction));
}
let mut value = [0u8; 2];
let Some(front) = value.get_mut(..bytes.len()) else {
return Err(WriteError::TooLarge { cap: 2 });
};
front.copy_from_slice(bytes);
self.staged = Some(Staged::Value(value, bytes.len()));
Ok(())
}
fn invalidate(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError> {
if Memory::member(iface, ord).is_none() {
return Err(WriteError::Contract(Contract::UnknownInteraction));
}
self.staged = Some(Staged::Invalid);
Ok(())
}
fn touch(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError> {
if Memory::member(iface, ord).is_none() {
return Err(WriteError::Contract(Contract::UnknownInteraction));
}
if self.staged.is_none() {
self.staged = Some(Staged::Touch);
}
Ok(())
}
fn commit(&mut self) {
let Some(staged) = self.staged.take() else {
return;
};
match staged {
Staged::Value(bytes, len) => {
self.bytes = bytes;
self.len = len;
self.provenance = Provenance::Live;
}
Staged::Invalid => self.provenance = Provenance::Invalid(Cause::Declared),
Staged::Touch => {}
}
self.envelope = Envelope {
stamp: self.now(),
seq: self.envelope.seq + 1,
};
}
}
fn publish(runtime: &mut Memory, speed: Speed) {
let mut buf = [0u8; <Speed as Payload<ReprC>>::MAX_SIZE];
let proof = Ref::<Speed, ReprC>::encode(&speed, &mut buf).expect("the buffer is MAX_SIZE");
runtime
.set(
Drivetrain::NUMBER,
SpeedSignal::MEMBER.ordinal,
proof.bytes(),
)
.expect("the runtime owns speed");
runtime.commit();
}
pub fn walk() -> [Sample<Speed>; 6] {
let mut init = [0u8; <Speed as Payload<ReprC>>::MAX_SIZE];
let proof = Ref::<Speed, ReprC>::encode(&SpeedSignal::init(), &mut init)
.expect("the buffer is MAX_SIZE");
let mut runtime = Memory::new(Timestamp(1_000_000), proof.bytes());
let read = |runtime: &Memory| read_speed(runtime).expect("speed is in the catalog");
let at_init = read(&runtime);
runtime.advance(Duration(10_000));
publish(&mut runtime, Speed(88));
let live = read(&runtime);
runtime.advance(Duration(600_000));
let stale = read(&runtime);
runtime
.invalidate(Drivetrain::NUMBER, SpeedSignal::MEMBER.ordinal)
.expect("the runtime owns speed");
runtime.commit();
let declared = read(&runtime);
runtime
.set(
Drivetrain::NUMBER,
SpeedSignal::MEMBER.ordinal,
&400u16.to_le_bytes(),
)
.expect("the runtime owns speed");
runtime.commit();
let detected = read(&runtime);
runtime
.set(Drivetrain::NUMBER, SpeedSignal::MEMBER.ordinal, &[1])
.expect("the runtime owns speed");
runtime.commit();
let corrupt = read(&runtime);
[at_init, live, stale, declared, detected, corrupt]
}
fn main() {
let steps = [
"init",
"live",
"stale",
"declared invalid",
"detected invalid",
"corrupt",
];
for (step, sample) in steps.iter().zip(walk()) {
println!("{step}: {sample:?} usable={}", sample.usable());
}
}