Skip to main content

ace_can/ext/
classic.rs

1use crate::{
2    constants::{
3        CLASSIC_CAN_MAX_DLC, CLASSIC_CAN_MIN_LEN, DATA_OFFSET, DLC_OFFSET, EFF_FLAG, ERR_FLAG,
4        EXT_ID_MASK, RTR_FLAG, STD_ID_MASK,
5    },
6    error::CanError,
7    isotp::address::IsoTpAddressingMode,
8};
9use ace_core::AddressMode;
10use ace_proto::can::frame::classic::{CanFrame, CanFrameMut};
11
12// region: Offsets
13
14/// Byte layout of a raw classic CAN frame:
15///
16/// [0..=3] - 32-bit CAN ID word (big-endian)
17///           bit 31: extended frame flag (EFF)
18///           bit 30: remote transmission request (RTR)
19///           bit 29: error frame flag (ERR)
20///           bits 28-0: CAN ID (11-bit in bits 10-0 for standard,
21///                               29-bit in bits 28-0 for extended)
22/// [4]     - DLC (0–8)
23/// [5..=12]- data bytes (0–8 bytes, DLC determines count)
24
25// endregion: Offsets
26
27// region: CanFrameExt
28
29pub trait CanFrameExt {
30    fn as_bytes(&self) -> &[u8];
31
32    // region: Raw field accessors
33
34    fn id_word(&self) -> Option<u32> {
35        let b = self.as_bytes();
36        if b.len() < DATA_OFFSET {
37            return None;
38        }
39        Some(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
40    }
41
42    fn dlc(&self) -> Option<u8> {
43        self.as_bytes().get(DLC_OFFSET).copied()
44    }
45
46    fn data_bytes(&self) -> Option<&[u8]> {
47        let b = self.as_bytes();
48        let dlc = self.dlc()? as usize;
49        let end = DATA_OFFSET + dlc;
50        if b.len() < end {
51            return None;
52        }
53        Some(&b[DATA_OFFSET..end])
54    }
55
56    // endregion: Raw field accessors
57
58    // region: Flags
59
60    fn is_extended_frame(&self) -> bool {
61        self.id_word().map_or(false, |w| w & EFF_FLAG != 0)
62    }
63
64    fn is_rtr(&self) -> bool {
65        self.id_word().map_or(false, |w| w & RTR_FLAG != 0)
66    }
67
68    fn is_error_frame(&self) -> bool {
69        self.id_word().map_or(false, |w| w & ERR_FLAG != 0)
70    }
71
72    // endregion: Flags
73
74    // region: Typed accessors
75
76    fn can_id(&self) -> Option<Result<ace_proto::can::id::CanId, CanError>> {
77        let word = self.id_word()?;
78        if word & EFF_FLAG != 0 {
79            let raw = word & EXT_ID_MASK;
80            Some(
81                ace_proto::can::id::ExtendedCanId::new(raw)
82                    .map(ace_proto::can::id::CanId::Extended)
83                    .map_err(CanError::from),
84            )
85        } else {
86            let raw = (word & STD_ID_MASK) as u16;
87            Some(
88                ace_proto::can::id::StandardCanId::new(raw)
89                    .map(ace_proto::can::id::CanId::Standard)
90                    .map_err(CanError::from),
91            )
92        }
93    }
94
95    // endregion: Typed accessors
96
97    // region: Validation
98
99    fn validate(&self) -> Result<(), CanError> {
100        let b = self.as_bytes();
101
102        if b.len() < CLASSIC_CAN_MIN_LEN {
103            return Err(CanError::BufferTooShort {
104                expected: CLASSIC_CAN_MIN_LEN,
105                actual: b.len(),
106            });
107        }
108
109        let word = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
110
111        // RTR is illegal when data bytes are present
112        if word & RTR_FLAG != 0 && word & EFF_FLAG == 0 {
113            return Err(CanError::InvalidFlags);
114        }
115
116        let dlc = b[DLC_OFFSET];
117        if dlc > CLASSIC_CAN_MAX_DLC {
118            return Err(CanError::InvalidDlc(dlc));
119        }
120
121        let expected_len = DATA_OFFSET + dlc as usize;
122        if b.len() < expected_len {
123            return Err(CanError::BufferTooShort {
124                expected: expected_len,
125                actual: b.len(),
126            });
127        }
128
129        Ok(())
130    }
131
132    fn is_valid(&self) -> bool {
133        self.validate().is_ok()
134    }
135
136    // endregion: Validation
137
138    // region: ISO-TP helpers
139
140    /// Returns the PCI byte offset given the addressing mode.
141    fn pci_offset(mode: &AddressMode) -> usize {
142        match mode {
143            AddressMode::Physical => 0,   // normal addressing
144            AddressMode::Functional => 1, // extended/mixed - first byte is address
145        }
146    }
147
148    /// Returns the data bytes available for ISO-TP PCI + payload
149    /// given the addressing mode, or None if the frame is too short.
150    fn isotp_bytes(&self, mode: &IsoTpAddressingMode) -> Option<&[u8]> {
151        let data = self.data_bytes()?;
152        let offset = mode.pci_offset();
153        if data.len() <= offset {
154            return None;
155        }
156        Some(&data[offset..])
157    }
158    // endregion: ISO-TP helpers
159}
160
161impl<'a> CanFrameExt for CanFrame<'a> {
162    fn as_bytes(&self) -> &[u8] {
163        ace_proto::common::RawFrame::as_bytes(self)
164    }
165}
166
167impl<'a> CanFrameExt for CanFrameMut<'a> {
168    fn as_bytes(&self) -> &[u8] {
169        ace_proto::common::RawFrame::as_bytes(self)
170    }
171}
172
173// endregion: CanFrameExt
174
175// region: CanFrameMutExt
176
177pub trait CanFrameMutExt: CanFrameExt {
178    fn as_bytes_mut(&mut self) -> &mut [u8];
179
180    fn set_id_word(&mut self, word: u32) -> Result<(), CanError> {
181        let b = self.as_bytes_mut();
182        if b.len() < DATA_OFFSET {
183            return Err(CanError::BufferTooShort {
184                expected: DATA_OFFSET,
185                actual: b.len(),
186            });
187        }
188        b[0..4].copy_from_slice(&word.to_be_bytes());
189        Ok(())
190    }
191
192    fn set_standard_id(&mut self, id: &ace_proto::can::id::StandardCanId) -> Result<(), CanError> {
193        let word = id.value() as u32 & STD_ID_MASK;
194        self.set_id_word(word)
195    }
196
197    fn set_extended_id(&mut self, id: &ace_proto::can::id::ExtendedCanId) -> Result<(), CanError> {
198        let word = (id.value() & EXT_ID_MASK) | EFF_FLAG;
199        self.set_id_word(word)
200    }
201
202    fn set_dlc(&mut self, dlc: u8) -> Result<(), CanError> {
203        if dlc > CLASSIC_CAN_MAX_DLC {
204            return Err(CanError::InvalidDlc(dlc));
205        }
206        let b = self.as_bytes_mut();
207        if b.len() <= DLC_OFFSET {
208            return Err(CanError::BufferTooShort {
209                expected: DLC_OFFSET + 1,
210                actual: b.len(),
211            });
212        }
213        b[DLC_OFFSET] = dlc;
214        Ok(())
215    }
216
217    fn write_data(&mut self, data: &[u8]) -> Result<(), CanError> {
218        if data.len() > CLASSIC_CAN_MAX_DLC as usize {
219            return Err(CanError::InvalidDlc(data.len() as u8));
220        }
221        let b = self.as_bytes_mut();
222        let end = DATA_OFFSET + data.len();
223        if b.len() < end {
224            return Err(CanError::BufferTooShort {
225                expected: end,
226                actual: b.len(),
227            });
228        }
229        b[DATA_OFFSET..end].copy_from_slice(data);
230        b[DLC_OFFSET] = data.len() as u8;
231        Ok(())
232    }
233}
234
235impl<'a> CanFrameMutExt for CanFrameMut<'a> {
236    fn as_bytes_mut(&mut self) -> &mut [u8] {
237        ace_proto::common::RawFrameMut::as_bytes_mut(self)
238    }
239}
240
241// endregion: CanFrameMutExt