veecle_os_data_support_can/
frame.rs

1use tinyvec::ArrayVec;
2
3use crate::id::{Id, PackedId};
4
5/// A frame of CAN data, useful for passing received frames between Veecle OS actors before they get deserialized.
6#[derive(Clone, Copy, serde::Serialize)]
7pub struct Frame {
8    /// The `id` is stored packed to save space, with `PackedId` a `Frame` is 14 bytes, without it, it is 20 bytes.
9    id: PackedId,
10    data: ArrayVec<[u8; 8]>,
11}
12
13mod sealed {
14    /// Stop external implementations, technically not necessary because we only care about implementations on `[u8; N]`
15    /// which no external crate can add. But, having this should hopefully stop anyone attempting to implement
16    /// `FrameSize` on their types
17    pub trait Sealed {}
18    impl Sealed for [u8; 0] {}
19    impl Sealed for [u8; 1] {}
20    impl Sealed for [u8; 2] {}
21    impl Sealed for [u8; 3] {}
22    impl Sealed for [u8; 4] {}
23    impl Sealed for [u8; 5] {}
24    impl Sealed for [u8; 6] {}
25    impl Sealed for [u8; 7] {}
26    impl Sealed for [u8; 8] {}
27}
28
29/// A marker trait for arrays that are valid sizes for CAN frames.
30pub trait FrameSize: sealed::Sealed {}
31impl FrameSize for [u8; 0] {}
32impl FrameSize for [u8; 1] {}
33impl FrameSize for [u8; 2] {}
34impl FrameSize for [u8; 3] {}
35impl FrameSize for [u8; 4] {}
36impl FrameSize for [u8; 5] {}
37impl FrameSize for [u8; 6] {}
38impl FrameSize for [u8; 7] {}
39impl FrameSize for [u8; 8] {}
40
41impl Frame {
42    /// Create a frame with the passed id and data.
43    ///
44    /// Statically checked that `N <= 8` via [`FrameSize`].
45    pub fn new<const N: usize>(id: impl Into<Id>, data: [u8; N]) -> Self
46    where
47        [u8; N]: FrameSize,
48    {
49        Self::new_checked(id, &data).expect("the const generic guarantees it's ok")
50    }
51
52    /// Create a frame with the passed id and data.
53    ///
54    /// Returns `Some` iff `data.len() <= 8`.
55    pub fn new_checked(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
56        let id = PackedId::from(id.into());
57        let data = ArrayVec::try_from(data).ok()?;
58        Some(Self { id, data })
59    }
60
61    /// The id this frame was received with.
62    pub fn id(&self) -> Id {
63        self.id.into()
64    }
65
66    /// The data this frame was received with.
67    pub fn data(&self) -> &[u8] {
68        &self.data
69    }
70}
71
72impl Default for Frame {
73    fn default() -> Self {
74        Self::new(crate::StandardId::new(0).unwrap(), [])
75    }
76}
77
78impl veecle_os_runtime::Storable for Frame {
79    type DataType = Self;
80}
81
82impl core::fmt::Debug for Frame {
83    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84        write!(f, "Frame {{ id: {:?}, data: '", self.id())?;
85        for byte in self.data() {
86            write!(f, "{byte:02x}")?;
87        }
88        f.write_str("' }")?;
89
90        Ok(())
91    }
92}
93
94/// More of an example of the output format than a real test, but as a test to force updating it.
95#[test]
96fn test_debug() {
97    fn to_debug(value: impl core::fmt::Debug) -> std::string::String {
98        std::format!("{value:?}")
99    }
100
101    assert_eq!(
102        to_debug(Frame::new(crate::StandardId::new(0).unwrap(), [])),
103        "Frame { id: Standard(0x0), data: '' }"
104    );
105
106    assert_eq!(
107        to_debug(Frame::new(
108            crate::ExtendedId::new(0x153EAB12).unwrap(),
109            [0x04, 0xA2, 0xC2, 0xED, 0xCA, 0xE3, 0x88, 0x74]
110        )),
111        "Frame { id: Extended(0x153eab12), data: '04a2c2edcae38874' }"
112    );
113
114    assert_eq!(
115        to_debug(Frame::new(
116            crate::ExtendedId::new(0x1B56C72D).unwrap(),
117            [0x40, 0x71, 0xEF, 0x61]
118        )),
119        "Frame { id: Extended(0x1b56c72d), data: '4071ef61' }"
120    );
121}