Skip to main content

can_hal/
frame.rs

1use crate::id::CanId;
2
3const CAN_MAX_LEN: usize = 8;
4
5/// A classic CAN 2.0 frame (up to 8 data bytes).
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct CanFrame {
8    id: CanId,
9    len: u8,
10    data: [u8; 8],
11}
12
13impl CanFrame {
14    /// Create a new classic CAN frame.
15    ///
16    /// Returns `None` if `data` is longer than 8 bytes.
17    #[must_use]
18    pub fn new(id: CanId, data: &[u8]) -> Option<Self> {
19        if data.len() > CAN_MAX_LEN {
20            return None;
21        }
22        let mut buf = [0u8; 8];
23        buf[..data.len()].copy_from_slice(data);
24        #[allow(clippy::cast_possible_truncation)] // validated above: data.len() <= 8
25        Some(Self {
26            id,
27            len: data.len() as u8,
28            data: buf,
29        })
30    }
31
32    /// Returns the frame's CAN identifier.
33    #[must_use]
34    pub const fn id(&self) -> CanId {
35        self.id
36    }
37
38    /// Returns the data length in bytes (0--8).
39    ///
40    /// For classic CAN 2.0, the data length and the on-wire DLC field are
41    /// identical in the range 0--8.
42    #[must_use]
43    pub const fn len(&self) -> usize {
44        self.len as usize
45    }
46
47    /// Returns `true` if the frame carries zero data bytes.
48    #[must_use]
49    pub const fn is_empty(&self) -> bool {
50        self.len == 0
51    }
52
53    /// Returns the data payload.
54    #[must_use]
55    pub fn data(&self) -> &[u8] {
56        &self.data[..self.len as usize]
57    }
58}
59
60/// A CAN FD frame (up to 64 data bytes).
61///
62/// The data length is stored as a byte count (0--64), **not** the 4-bit
63/// on-wire DLC code. For DLC values 9--15 the CAN FD specification maps
64/// them to 12, 16, 20, 24, 32, 48, and 64 bytes respectively; this struct
65/// stores the decoded byte count directly.
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct CanFdFrame {
68    id: CanId,
69    len: u8,
70    data: [u8; 64],
71    brs: bool,
72    esi: bool,
73}
74
75impl CanFdFrame {
76    /// Create a new CAN FD frame.
77    ///
78    /// Returns `None` if `data.len()` is not a valid FD data length
79    /// (0, 1, ..., 8, 12, 16, 20, 24, 32, 48, or 64).
80    #[must_use]
81    pub fn new(id: CanId, data: &[u8], brs: bool, esi: bool) -> Option<Self> {
82        if !matches!(data.len(), 0..=8 | 12 | 16 | 20 | 24 | 32 | 48 | 64) {
83            return None;
84        }
85        let mut buf = [0u8; 64];
86        buf[..data.len()].copy_from_slice(data);
87        #[allow(clippy::cast_possible_truncation)] // validated above: data.len() <= 64
88        Some(Self {
89            id,
90            len: data.len() as u8,
91            data: buf,
92            brs,
93            esi,
94        })
95    }
96
97    /// Returns the frame's CAN identifier.
98    #[must_use]
99    pub const fn id(&self) -> CanId {
100        self.id
101    }
102
103    /// Returns the data length in bytes (not the 4-bit DLC code).
104    ///
105    /// The returned value is always one of the valid CAN FD data lengths:
106    /// 0, 1, ..., 8, 12, 16, 20, 24, 32, 48, or 64.
107    #[must_use]
108    pub const fn len(&self) -> usize {
109        self.len as usize
110    }
111
112    /// Returns `true` if the frame carries zero data bytes.
113    #[must_use]
114    pub const fn is_empty(&self) -> bool {
115        self.len == 0
116    }
117
118    /// Returns the data payload.
119    #[must_use]
120    pub fn data(&self) -> &[u8] {
121        &self.data[..self.len as usize]
122    }
123
124    /// Returns `true` if the Bit Rate Switch flag is set.
125    #[must_use]
126    pub const fn brs(&self) -> bool {
127        self.brs
128    }
129
130    /// Returns `true` if the Error State Indicator flag is set.
131    #[must_use]
132    pub const fn esi(&self) -> bool {
133        self.esi
134    }
135}
136
137/// A frame of either type - used when receiving on an FD-capable bus.
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub enum Frame {
140    Can(CanFrame),
141    Fd(CanFdFrame),
142}
143
144impl Frame {
145    /// Returns the frame's CAN identifier regardless of frame type.
146    #[must_use]
147    pub const fn id(&self) -> CanId {
148        match self {
149            Self::Can(f) => f.id(),
150            Self::Fd(f) => f.id(),
151        }
152    }
153
154    /// Returns the data payload regardless of frame type.
155    #[must_use]
156    pub fn data(&self) -> &[u8] {
157        match self {
158            Self::Can(f) => f.data(),
159            Self::Fd(f) => f.data(),
160        }
161    }
162
163    /// Returns the data length in bytes regardless of frame type.
164    #[must_use]
165    pub const fn len(&self) -> usize {
166        match self {
167            Self::Can(f) => f.len(),
168            Self::Fd(f) => f.len(),
169        }
170    }
171
172    /// Returns `true` if the frame carries zero data bytes.
173    #[must_use]
174    pub const fn is_empty(&self) -> bool {
175        match self {
176            Self::Can(f) => f.is_empty(),
177            Self::Fd(f) => f.is_empty(),
178        }
179    }
180}
181
182/// A received frame paired with its timestamp.
183///
184/// The timestamp type `T` is chosen by the backend implementation.
185/// On `std` platforms this is typically `std::time::Instant`; on `no_std`
186/// targets it can be a hardware timer tick count or any `Clone` type.
187#[derive(Debug, Clone)]
188pub struct Timestamped<F, T> {
189    frame: F,
190    timestamp: T,
191}
192
193impl<F, T> Timestamped<F, T> {
194    /// Create a new timestamped frame.
195    #[must_use]
196    pub const fn new(frame: F, timestamp: T) -> Self {
197        Self { frame, timestamp }
198    }
199
200    /// Returns a reference to the inner frame.
201    #[must_use]
202    pub const fn frame(&self) -> &F {
203        &self.frame
204    }
205
206    /// Returns a reference to the timestamp.
207    #[must_use]
208    pub const fn timestamp(&self) -> &T {
209        &self.timestamp
210    }
211
212    /// Consumes self and returns the inner frame.
213    #[must_use]
214    pub fn into_frame(self) -> F {
215        self.frame
216    }
217}