Skip to main content

sim/runtime/reference_device/
profiles.rs

1use std::rc::Rc;
2
3use sim_kernel::{Expr, Result, Symbol};
4use sim_lib_stream_device::ModeledDeviceCapsSource;
5use sim_lib_view_device::{
6    DeviceProfile, DeviceProfileParts, EncodedScene, LocalAdapter, RateClass,
7};
8use sim_value::build;
9
10/// Symbol naming the rich reference-device profile export.
11pub fn reference_rich_profile_symbol() -> Symbol {
12    Symbol::qualified("device/reference", "rich-profile")
13}
14
15/// Symbol naming the compact glance reference profile export.
16pub fn reference_glance_profile_symbol() -> Symbol {
17    Symbol::qualified("device/reference", "glance-profile")
18}
19
20/// Builds the rich, pose-coupled reference device profile.
21pub fn reference_rich_profile() -> DeviceProfile {
22    DeviceProfile::new(DeviceProfileParts {
23        kind: Symbol::qualified("device", "reference-device"),
24        display: symbols(&["stereo", "hud"]),
25        input: symbols(&["tap"]),
26        output: symbols(&["hud", "haptic"]),
27        links: symbols(&["local"]),
28        streams: symbols(&["pose"]),
29        rate: RateClass::stereo(),
30        policy: build::map(vec![
31            ("consent", build::sym("required")),
32            ("retention-ms", build::uint(100)),
33        ]),
34    })
35}
36
37/// Builds the actuator-tier glance reference profile.
38pub fn reference_glance_profile() -> DeviceProfile {
39    DeviceProfile::new(DeviceProfileParts {
40        kind: Symbol::qualified("device", "reference-glance"),
41        display: symbols(&["round"]),
42        input: symbols(&["tap"]),
43        output: symbols(&["haptic"]),
44        links: symbols(&["local"]),
45        streams: Vec::new(),
46        rate: RateClass::watch(),
47        policy: build::map(vec![
48            ("consent", build::sym("visible")),
49            ("retention-ms", build::uint(50)),
50        ]),
51    })
52}
53
54/// Builds the deterministic modeled stream-facing capability source.
55pub fn reference_caps_source() -> ModeledDeviceCapsSource {
56    ModeledDeviceCapsSource::new(
57        Symbol::qualified("device", "reference-device"),
58        vec![Symbol::qualified("device/stream", "pose")],
59        vec![Symbol::qualified("device/input", "tap")],
60        vec![
61            Symbol::qualified("device/output", "hud"),
62            Symbol::qualified("device/output", "haptic"),
63        ],
64    )
65    .with_seq_base(10)
66}
67
68/// Device-local pose state consumed by the rich reference adapter.
69#[derive(Clone, Debug, PartialEq, Eq)]
70pub struct ReferencePose {
71    /// Modeled pose sample sequence.
72    pub seq: u64,
73    /// Yaw in millidegrees.
74    pub yaw_mdeg: i32,
75}
76
77impl ReferencePose {
78    /// Builds one deterministic pose sample.
79    pub fn new(seq: u64, yaw_mdeg: i32) -> Self {
80        Self { seq, yaw_mdeg }
81    }
82}
83
84/// Bespoke rich-tier adapter for the modeled reference device.
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
86pub struct ReferenceRichAdapter;
87
88impl LocalAdapter for ReferenceRichAdapter {
89    type State = ReferencePose;
90
91    fn adapt(
92        &self,
93        scene: &EncodedScene,
94        state: &Self::State,
95        profile: &DeviceProfile,
96    ) -> Result<Rc<Expr>> {
97        Ok(Rc::new(build::map(vec![
98            ("kind", build::qsym("device/reference", "rich-frame")),
99            ("tier", Expr::Symbol(profile.tier.to_symbol())),
100            ("pose-seq", build::uint(state.seq)),
101            ("yaw-mdeg", build::int(i64::from(state.yaw_mdeg))),
102            ("scene", scene.expr().clone()),
103        ])))
104    }
105}
106
107/// Deterministic content encoder used by the two-rate proof.
108#[derive(Clone, Debug, Default, PartialEq, Eq)]
109pub struct ReferenceSceneEncoder {
110    calls: u64,
111}
112
113impl ReferenceSceneEncoder {
114    /// Builds a fresh encoder.
115    pub fn new() -> Self {
116        Self::default()
117    }
118
119    /// Encodes the reference scene once for any number of local adapters.
120    pub fn encode(&mut self) -> EncodedScene {
121        self.calls = self.calls.saturating_add(1);
122        EncodedScene::new(reference_scene())
123    }
124
125    /// Number of content encoding calls performed.
126    pub fn calls(&self) -> u64 {
127        self.calls
128    }
129}
130
131/// Builds the portable scene used by both reference tiers.
132pub fn reference_scene() -> Expr {
133    build::map(vec![
134        ("kind", build::qsym("scene", "glance")),
135        ("title", build::text("Reference pose")),
136        (
137            "metric",
138            build::map(vec![
139                ("label", build::text("yaw")),
140                ("value", build::text("12 deg")),
141            ]),
142        ),
143        (
144            "action",
145            build::map(vec![
146                ("label", build::text("Acknowledge")),
147                ("target", build::sym("tap")),
148            ]),
149        ),
150        ("urgency", build::sym("info")),
151        ("cells", build::uint(4)),
152        ("bypass-budget", Expr::Bool(false)),
153    ])
154}
155
156fn symbols(names: &[&str]) -> Vec<Symbol> {
157    names.iter().map(|name| Symbol::new(*name)).collect()
158}