pamoja_can/frame.rs
1//! The CAN data frame and the length-to-DLC encoding it uses.
2
3use crate::error::CanError;
4use crate::id::CanId;
5
6// The largest payload a CAN-FD frame can carry.
7const MAX_FD_DATA: usize = 64;
8
9/// Maps a data length to the data-length code that represents it.
10///
11/// Classic CAN and the first nine CAN-FD codes are the length itself, 0 through 8. Above
12/// that CAN-FD jumps in steps, so a length between two steps rounds up to the next code.
13///
14/// # Arguments
15///
16/// * `len` - the data length in bytes.
17///
18/// # Returns
19///
20/// The 4-bit data-length code.
21///
22/// # Examples
23///
24/// ```
25/// use pamoja_can::len_to_dlc;
26///
27/// assert_eq!(len_to_dlc(8), 8);
28/// assert_eq!(len_to_dlc(12), 9);
29/// assert_eq!(len_to_dlc(64), 15);
30/// ```
31pub fn len_to_dlc(len: usize) -> u8 {
32 match len {
33 0..=8 => len as u8,
34 9..=12 => 9,
35 13..=16 => 10,
36 17..=20 => 11,
37 21..=24 => 12,
38 25..=32 => 13,
39 33..=48 => 14,
40 _ => 15,
41 }
42}
43
44/// Maps a data-length code to the number of bytes it represents.
45///
46/// # Arguments
47///
48/// * `dlc` - the data-length code; only its low four bits are used.
49///
50/// # Returns
51///
52/// The data length in bytes.
53///
54/// # Examples
55///
56/// ```
57/// use pamoja_can::dlc_to_len;
58///
59/// assert_eq!(dlc_to_len(8), 8);
60/// assert_eq!(dlc_to_len(15), 64);
61/// ```
62pub fn dlc_to_len(dlc: u8) -> usize {
63 match dlc & 0x0F {
64 small @ 0..=8 => small as usize,
65 9 => 12,
66 10 => 16,
67 11 => 20,
68 12 => 24,
69 13 => 32,
70 14 => 48,
71 _ => 64,
72 }
73}
74
75// Reports whether a length is one a CAN-FD frame can carry exactly.
76fn is_fd_length(len: usize) -> bool {
77 matches!(len, 0..=8 | 12 | 16 | 20 | 24 | 32 | 48 | 64)
78}
79
80/// A CAN frame: an identifier and its data.
81///
82/// Holds a classic CAN 2.0 frame (up to 8 bytes), a CAN-FD frame (up to 64 bytes at the
83/// discrete CAN-FD lengths), or a classic remote frame, which requests data and carries
84/// none. The data lives in a fixed buffer, so building a frame never allocates.
85///
86/// # Examples
87///
88/// ```
89/// use pamoja_can::{CanId, Frame};
90///
91/// let frame = Frame::new(CanId::standard(0x100), &[0x01, 0x02, 0x03]).unwrap();
92/// assert_eq!(frame.data(), &[0x01, 0x02, 0x03]);
93/// assert_eq!(frame.dlc(), 3);
94/// assert!(!frame.is_fd());
95/// ```
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub struct Frame {
98 id: CanId,
99 data: [u8; MAX_FD_DATA],
100 len: usize,
101 fd: bool,
102 remote: bool,
103}
104
105impl Frame {
106 /// Builds a classic CAN 2.0 data frame.
107 ///
108 /// # Arguments
109 ///
110 /// * `id` - the arbitration identifier.
111 /// * `data` - the payload, at most 8 bytes.
112 ///
113 /// # Returns
114 ///
115 /// The frame.
116 ///
117 /// # Errors
118 ///
119 /// Returns [`CanError::DataTooLong`] if `data` is longer than 8 bytes.
120 pub fn new(id: CanId, data: &[u8]) -> Result<Frame, CanError> {
121 if data.len() > 8 {
122 return Err(CanError::DataTooLong);
123 }
124 Ok(Self::store(id, data, false, false))
125 }
126
127 /// Builds a CAN-FD data frame.
128 ///
129 /// # Arguments
130 ///
131 /// * `id` - the arbitration identifier.
132 /// * `data` - the payload, at one of the discrete CAN-FD lengths up to 64 bytes.
133 ///
134 /// # Returns
135 ///
136 /// The frame.
137 ///
138 /// # Errors
139 ///
140 /// Returns [`CanError::DataTooLong`] if `data` is longer than 64 bytes, or
141 /// [`CanError::InvalidFdLength`] if its length is not one CAN-FD can carry.
142 pub fn fd(id: CanId, data: &[u8]) -> Result<Frame, CanError> {
143 if data.len() > MAX_FD_DATA {
144 return Err(CanError::DataTooLong);
145 }
146 if !is_fd_length(data.len()) {
147 return Err(CanError::InvalidFdLength);
148 }
149 Ok(Self::store(id, data, true, false))
150 }
151
152 /// Builds a classic remote frame, which requests data of a given length and carries
153 /// none.
154 ///
155 /// # Arguments
156 ///
157 /// * `id` - the arbitration identifier.
158 /// * `len` - the data length being requested, clamped to 8 bytes.
159 ///
160 /// # Returns
161 ///
162 /// The remote frame.
163 pub fn remote(id: CanId, len: usize) -> Frame {
164 Frame {
165 id,
166 data: [0; MAX_FD_DATA],
167 len: len.min(8),
168 fd: false,
169 remote: true,
170 }
171 }
172
173 fn store(id: CanId, data: &[u8], fd: bool, remote: bool) -> Frame {
174 let mut buf = [0; MAX_FD_DATA];
175 buf[..data.len()].copy_from_slice(data);
176 Frame {
177 id,
178 data: buf,
179 len: data.len(),
180 fd,
181 remote,
182 }
183 }
184
185 /// Returns the arbitration identifier.
186 ///
187 /// # Returns
188 ///
189 /// The identifier.
190 pub fn id(&self) -> CanId {
191 self.id
192 }
193
194 /// Returns the frame's data.
195 ///
196 /// # Returns
197 ///
198 /// The payload bytes, or an empty slice for a remote frame.
199 pub fn data(&self) -> &[u8] {
200 if self.remote {
201 &[]
202 } else {
203 &self.data[..self.len]
204 }
205 }
206
207 /// Reads the frame's data as J1939 signals.
208 ///
209 /// A J1939 message always carries exactly eight bytes, addressed by the offsets its
210 /// parameter group publishes, so this hands back a view that reads them by offset
211 /// rather than a slice a caller has to index.
212 ///
213 /// # Returns
214 ///
215 /// The eight data bytes as [`Signals`](crate::Signals), or `None` if this frame does
216 /// not carry exactly eight, which a J1939 message always does.
217 pub fn signals(&self) -> Option<crate::Signals> {
218 let bytes: [u8; 8] = self.data().try_into().ok()?;
219 Some(crate::Signals::from_bytes(bytes))
220 }
221
222 /// Returns the data length: the payload length, or the requested length for a remote
223 /// frame.
224 ///
225 /// # Returns
226 ///
227 /// The length in bytes.
228 pub fn len(&self) -> usize {
229 self.len
230 }
231
232 /// Reports whether the frame carries no data.
233 ///
234 /// # Returns
235 ///
236 /// `true` if the length is zero.
237 pub fn is_empty(&self) -> bool {
238 self.len == 0
239 }
240
241 /// Returns the data-length code for this frame's length.
242 ///
243 /// # Returns
244 ///
245 /// The 4-bit data-length code.
246 pub fn dlc(&self) -> u8 {
247 len_to_dlc(self.len)
248 }
249
250 /// Reports whether this is a CAN-FD frame.
251 ///
252 /// # Returns
253 ///
254 /// `true` for a CAN-FD frame.
255 pub fn is_fd(&self) -> bool {
256 self.fd
257 }
258
259 /// Reports whether this is a remote frame.
260 ///
261 /// # Returns
262 ///
263 /// `true` for a remote frame.
264 pub fn is_remote(&self) -> bool {
265 self.remote
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn a_classic_frame_holds_its_data() {
275 let frame = Frame::new(CanId::standard(0x100), &[1, 2, 3, 4]).unwrap();
276 assert_eq!(frame.data(), &[1, 2, 3, 4]);
277 assert_eq!(frame.len(), 4);
278 assert_eq!(frame.dlc(), 4);
279 assert!(!frame.is_fd());
280 assert!(!frame.is_remote());
281 }
282
283 #[test]
284 fn a_classic_frame_rejects_more_than_eight_bytes() {
285 assert_eq!(
286 Frame::new(CanId::standard(0x100), &[0; 9]),
287 Err(CanError::DataTooLong)
288 );
289 }
290
291 #[test]
292 fn an_fd_frame_carries_up_to_sixty_four_bytes() {
293 let frame = Frame::fd(CanId::extended(0x1234), &[0xAB; 64]).unwrap();
294 assert_eq!(frame.len(), 64);
295 assert_eq!(frame.dlc(), 15);
296 assert!(frame.is_fd());
297 }
298
299 #[test]
300 fn an_fd_frame_rejects_a_length_it_cannot_carry() {
301 // 9 bytes is not a valid CAN-FD length.
302 assert_eq!(
303 Frame::fd(CanId::standard(0x1), &[0; 9]),
304 Err(CanError::InvalidFdLength)
305 );
306 }
307
308 #[test]
309 fn an_fd_frame_rejects_more_than_sixty_four_bytes() {
310 assert_eq!(
311 Frame::fd(CanId::standard(0x1), &[0; 65]),
312 Err(CanError::DataTooLong)
313 );
314 }
315
316 #[test]
317 fn a_remote_frame_requests_a_length_and_carries_no_data() {
318 let frame = Frame::remote(CanId::standard(0x200), 8);
319 assert!(frame.is_remote());
320 assert_eq!(frame.data(), &[]);
321 assert_eq!(frame.len(), 8);
322 assert_eq!(frame.dlc(), 8);
323 }
324
325 #[test]
326 fn the_dlc_encoding_round_trips_at_each_step() {
327 for &len in &[0usize, 1, 8, 12, 16, 20, 24, 32, 48, 64] {
328 assert_eq!(dlc_to_len(len_to_dlc(len)), len);
329 }
330 }
331
332 #[test]
333 fn a_length_between_steps_rounds_up() {
334 assert_eq!(len_to_dlc(9), 9); // the code for 12 bytes
335 assert_eq!(dlc_to_len(9), 12);
336 assert_eq!(len_to_dlc(33), 14); // the code for 48 bytes
337 assert_eq!(dlc_to_len(14), 48);
338 }
339
340 #[test]
341 fn a_zero_length_fd_frame_is_valid() {
342 let frame = Frame::fd(CanId::standard(0x1), &[]).unwrap();
343 assert!(frame.is_fd());
344 assert!(frame.is_empty());
345 assert_eq!(frame.len(), 0);
346 assert_eq!(frame.dlc(), 0);
347 assert_eq!(frame.data(), &[]);
348 }
349
350 #[test]
351 fn a_classic_frame_can_be_empty() {
352 let frame = Frame::new(CanId::standard(0x1), &[]).unwrap();
353 assert!(frame.is_empty());
354 assert_eq!(frame.dlc(), 0);
355 }
356}