1use serde::{Deserialize, Serialize};
2use std::ffi::c_char;
3use thiserror::Error;
4
5#[repr(u32)]
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum SimStatusRaw {
8 Ok = 0,
9 NotInitialized = 1,
10 InvalidArg = 2,
11 InvalidSignal = 3,
12 TypeMismatch = 4,
13 BufferTooSmall = 5,
14 Internal = 255,
15}
16
17impl TryFrom<u32> for SimStatusRaw {
18 type Error = ();
19
20 fn try_from(value: u32) -> Result<Self, Self::Error> {
21 match value {
22 0 => Ok(Self::Ok),
23 1 => Ok(Self::NotInitialized),
24 2 => Ok(Self::InvalidArg),
25 3 => Ok(Self::InvalidSignal),
26 4 => Ok(Self::TypeMismatch),
27 5 => Ok(Self::BufferTooSmall),
28 255 => Ok(Self::Internal),
29 _ => Err(()),
30 }
31 }
32}
33
34#[repr(u32)]
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum SimTypeRaw {
37 Bool = 0,
38 U32 = 1,
39 I32 = 2,
40 F32 = 3,
41 F64 = 4,
42}
43
44impl TryFrom<u32> for SimTypeRaw {
45 type Error = ();
46
47 fn try_from(value: u32) -> Result<Self, Self::Error> {
48 match value {
49 0 => Ok(Self::Bool),
50 1 => Ok(Self::U32),
51 2 => Ok(Self::I32),
52 3 => Ok(Self::F32),
53 4 => Ok(Self::F64),
54 _ => Err(()),
55 }
56 }
57}
58
59#[repr(C)]
60#[derive(Clone, Copy)]
61pub union SimValueDataRaw {
62 pub b: bool,
63 pub u32: u32,
64 pub i32: i32,
65 pub f32: f32,
66 pub f64: f64,
67}
68
69#[repr(C)]
70#[derive(Clone, Copy)]
71pub struct SimValueRaw {
72 pub signal_type: u32,
73 pub data: SimValueDataRaw,
74}
75
76#[repr(C)]
77#[derive(Clone, Copy)]
78pub struct SimSignalDescRaw {
79 pub id: u32,
80 pub name: *const c_char,
81 pub signal_type: u32,
82 pub units: *const c_char,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87pub enum SignalType {
88 Bool,
89 U32,
90 I32,
91 F32,
92 F64,
93}
94
95impl SignalType {
96 pub fn parse(value: &str) -> Option<Self> {
97 match value {
98 "bool" => Some(Self::Bool),
99 "u32" => Some(Self::U32),
100 "i32" => Some(Self::I32),
101 "f32" => Some(Self::F32),
102 "f64" => Some(Self::F64),
103 _ => None,
104 }
105 }
106}
107
108impl std::fmt::Display for SignalType {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 let value = match self {
111 Self::Bool => "bool",
112 Self::U32 => "u32",
113 Self::I32 => "i32",
114 Self::F32 => "f32",
115 Self::F64 => "f64",
116 };
117 write!(f, "{value}")
118 }
119}
120
121impl TryFrom<u32> for SignalType {
122 type Error = ();
123
124 fn try_from(value: u32) -> Result<Self, Self::Error> {
125 Ok(match SimTypeRaw::try_from(value)? {
126 SimTypeRaw::Bool => SignalType::Bool,
127 SimTypeRaw::U32 => SignalType::U32,
128 SimTypeRaw::I32 => SignalType::I32,
129 SimTypeRaw::F32 => SignalType::F32,
130 SimTypeRaw::F64 => SignalType::F64,
131 })
132 }
133}
134
135#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136#[serde(untagged)]
137pub enum SignalValue {
138 Bool(bool),
139 U32(u32),
140 I32(i32),
141 F32(f32),
142 F64(f64),
143}
144
145impl SignalValue {
146 pub fn signal_type(&self) -> SignalType {
147 match self {
148 Self::Bool(_) => SignalType::Bool,
149 Self::U32(_) => SignalType::U32,
150 Self::I32(_) => SignalType::I32,
151 Self::F32(_) => SignalType::F32,
152 Self::F64(_) => SignalType::F64,
153 }
154 }
155
156 pub fn to_raw(&self) -> SimValueRaw {
157 match self {
158 Self::Bool(v) => SimValueRaw {
159 signal_type: SimTypeRaw::Bool as u32,
160 data: SimValueDataRaw { b: *v },
161 },
162 Self::U32(v) => SimValueRaw {
163 signal_type: SimTypeRaw::U32 as u32,
164 data: SimValueDataRaw { u32: *v },
165 },
166 Self::I32(v) => SimValueRaw {
167 signal_type: SimTypeRaw::I32 as u32,
168 data: SimValueDataRaw { i32: *v },
169 },
170 Self::F32(v) => SimValueRaw {
171 signal_type: SimTypeRaw::F32 as u32,
172 data: SimValueDataRaw { f32: *v },
173 },
174 Self::F64(v) => SimValueRaw {
175 signal_type: SimTypeRaw::F64 as u32,
176 data: SimValueDataRaw { f64: *v },
177 },
178 }
179 }
180
181 pub unsafe fn from_raw(raw: SimValueRaw) -> Option<Self> {
187 match SimTypeRaw::try_from(raw.signal_type).ok()? {
188 SimTypeRaw::Bool => Some(Self::Bool(unsafe { raw.data.b })),
189 SimTypeRaw::U32 => Some(Self::U32(unsafe { raw.data.u32 })),
190 SimTypeRaw::I32 => Some(Self::I32(unsafe { raw.data.i32 })),
191 SimTypeRaw::F32 => Some(Self::F32(unsafe { raw.data.f32 })),
192 SimTypeRaw::F64 => Some(Self::F64(unsafe { raw.data.f64 })),
193 }
194 }
195}
196
197#[derive(Debug, Clone)]
198pub struct SignalMeta {
199 pub id: u32,
200 pub name: String,
201 pub signal_type: SignalType,
202 pub units: Option<String>,
203}
204
205#[repr(C)]
206#[derive(Clone, Copy, Debug)]
207pub struct SimCanFrameRaw {
208 pub arb_id: u32,
209 pub len: u8,
210 pub flags: u8,
211 pub _pad: [u8; 2],
212 pub data: [u8; 64],
213}
214
215#[repr(C)]
216#[derive(Clone, Copy, Debug)]
217pub struct SimCanBusDescRaw {
218 pub id: u32,
219 pub name: *const c_char,
220 pub bitrate: u32,
221 pub bitrate_data: u32,
222 pub flags: u8,
223 pub _pad: [u8; 3],
224}
225
226pub const CAN_FLAG_EXTENDED: u8 = 1 << 0;
227pub const CAN_FLAG_FD: u8 = 1 << 1;
228pub const CAN_FLAG_BRS: u8 = 1 << 2;
229pub const CAN_FLAG_ESI: u8 = 1 << 3;
230pub const CAN_FLAG_RTR: u8 = 1 << 4;
231pub const CAN_FLAG_RESERVED_MASK: u8 = 0b1110_0000;
232
233#[derive(Debug, Clone, PartialEq, Eq)]
234pub struct SimCanFrame {
235 pub arb_id: u32,
236 pub len: u8,
237 pub flags: u8,
238 pub data: [u8; 64],
239}
240
241impl SimCanFrame {
242 pub fn to_raw(&self) -> SimCanFrameRaw {
243 SimCanFrameRaw {
244 arb_id: self.arb_id,
245 len: self.len,
246 flags: self.flags,
247 _pad: [0, 0],
248 data: self.data,
249 }
250 }
251
252 pub fn from_raw(raw: SimCanFrameRaw) -> Self {
253 Self {
254 arb_id: raw.arb_id,
255 len: raw.len,
256 flags: raw.flags,
257 data: raw.data,
258 }
259 }
260
261 pub fn payload(&self) -> &[u8] {
262 let len = usize::from(self.len.min(64));
263 &self.data[..len]
264 }
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
268pub struct SimCanBusDesc {
269 pub id: u32,
270 pub name: String,
271 pub bitrate: u32,
272 pub bitrate_data: u32,
273 pub fd_capable: bool,
274}
275
276#[repr(C)]
277#[derive(Clone, Copy, Debug)]
278pub struct SimSharedDescRaw {
279 pub id: u32,
280 pub name: *const c_char,
281 pub slot_count: u32,
282}
283
284#[repr(C)]
285#[derive(Clone, Copy)]
286pub struct SimSharedSlotRaw {
287 pub slot_id: u32,
288 pub signal_type: u32,
289 pub value: SimValueRaw,
290}
291
292impl Default for SimSharedSlotRaw {
293 fn default() -> Self {
294 let value = SignalValue::F64(0.0).to_raw();
295 Self {
296 slot_id: 0,
297 signal_type: SimTypeRaw::F64 as u32,
298 value,
299 }
300 }
301}
302
303#[derive(Debug, Clone, PartialEq, Eq)]
304pub struct SimSharedDesc {
305 pub id: u32,
306 pub name: String,
307 pub slot_count: u32,
308}
309
310#[derive(Debug, Clone, PartialEq)]
311pub struct SimSharedSlot {
312 pub slot_id: u32,
313 pub value: SignalValue,
314}
315
316#[derive(Debug, Clone, PartialEq, Eq, Error)]
317pub enum SharedSlotDecodeError {
318 #[error("shared slot {slot_id} uses invalid type tag {signal_type}")]
319 InvalidTypeTag { slot_id: u32, signal_type: u32 },
320 #[error("shared slot {slot_id} has mismatched type tags: slot={slot_type} value={value_type}")]
321 MismatchedTypeTags {
322 slot_id: u32,
323 slot_type: u32,
324 value_type: u32,
325 },
326}
327
328impl SimSharedSlot {
329 pub fn to_raw(&self) -> SimSharedSlotRaw {
330 let raw = self.value.to_raw();
331 SimSharedSlotRaw {
332 slot_id: self.slot_id,
333 signal_type: raw.signal_type,
334 value: raw,
335 }
336 }
337
338 pub fn try_from_raw(raw: SimSharedSlotRaw) -> Result<Self, SharedSlotDecodeError> {
339 let slot_type = SimTypeRaw::try_from(raw.signal_type).map_err(|_| {
340 SharedSlotDecodeError::InvalidTypeTag {
341 slot_id: raw.slot_id,
342 signal_type: raw.signal_type,
343 }
344 })?;
345 let value_type = SimTypeRaw::try_from(raw.value.signal_type).map_err(|_| {
346 SharedSlotDecodeError::InvalidTypeTag {
347 slot_id: raw.slot_id,
348 signal_type: raw.value.signal_type,
349 }
350 })?;
351
352 if slot_type != value_type {
353 return Err(SharedSlotDecodeError::MismatchedTypeTags {
354 slot_id: raw.slot_id,
355 slot_type: raw.signal_type,
356 value_type: raw.value.signal_type,
357 });
358 }
359
360 Ok(Self {
361 slot_id: raw.slot_id,
362 value: unsafe { SignalValue::from_raw(raw.value) }.expect("validated type tag"),
363 })
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::{SharedSlotDecodeError, SignalValue, SimSharedSlot, SimSharedSlotRaw, SimTypeRaw};
370
371 #[test]
372 fn shared_slot_decoding_rejects_mismatched_type_tags() {
373 let raw = SimSharedSlotRaw {
374 slot_id: 1,
375 signal_type: SimTypeRaw::Bool as u32,
376 value: SignalValue::F32(1.5).to_raw(),
377 };
378
379 let err = SimSharedSlot::try_from_raw(raw).expect_err("mismatched tags must fail");
380 assert_eq!(
381 err,
382 SharedSlotDecodeError::MismatchedTypeTags {
383 slot_id: 1,
384 slot_type: SimTypeRaw::Bool as u32,
385 value_type: SimTypeRaw::F32 as u32,
386 }
387 );
388 }
389
390 #[test]
391 fn shared_slot_decoding_accepts_matching_tags() {
392 let raw = SimSharedSlot {
393 slot_id: 3,
394 value: SignalValue::U32(42),
395 }
396 .to_raw();
397
398 let slot = SimSharedSlot::try_from_raw(raw).expect("matching tags must decode");
399 assert_eq!(slot.slot_id, 3);
400 assert_eq!(slot.value, SignalValue::U32(42));
401 }
402}