Skip to main content

ace_uds/ext/
mod.rs

1use ace_core::codec::FrameRead;
2use ace_proto::common::{RawFrame, RawFrameMut};
3use ace_proto::uds::{UdsFrame, UdsFrameMut};
4
5use crate::constants::{
6    MIN_FRAME_LEN, MIN_SUB_FUNCTION_FRAME_LEN, NEGATIVE_RESPONSE_NRC_OFFSET,
7    NEGATIVE_RESPONSE_REQUESTED_SID_OFFSET, SUB_FUNCTION_OFFSET, SUB_FUNCTION_VALUE_MASK,
8    SUPPRESS_POSITIVE_RESPONSE_MASK,
9};
10use crate::error::{UdsError, ValidationError};
11use crate::message::ServiceIdentifier;
12use crate::message::UdsMessage;
13use crate::message::{decode_message, NegativeResponseCode};
14
15// region: UdsFrameExt
16
17/// Semantic extension trait for [`UdsFrame`].
18///
19/// Provides protocol-level interpretation of the raw bytes in a `UdsFrame` -
20/// service identification, sub-function parsing, response classification,
21/// payload access, and message conversion.
22///
23/// Implemented for [`UdsFrame`] in `ace-uds`. The raw frame type in
24/// `ace-proto` carries no protocol knowledge - all UDS semantics are
25/// provided here.
26pub trait UdsFrameExt<'a> {
27    // region: Service Identification
28
29    /// Returns the `ServiceIdentifier` for this frame if the first byte
30    /// corresponds to a known UDS service.
31    ///
32    /// Returns `None` if the frame is empty or the first byte does not
33    /// match a known service identifier. Per ISO 14229, an absent SID
34    /// implies a periodic data response.
35    #[must_use]
36    fn service_identifier(&self) -> Option<ServiceIdentifier>;
37
38    // endregion: Service Identification
39
40    // region: Payload Access
41
42    /// Returns the payload bytes of the frame, excluding the SID byte.
43    ///
44    /// If no service identifier is present the entire buffer is returned,
45    /// consistent with the periodic data response convention.
46    #[must_use]
47    fn payload(&self) -> &[u8];
48
49    /// Returns an iterator over the payload bytes, excluding the SID byte.
50    fn data_iter(&self) -> core::slice::Iter<'_, u8>;
51
52    // endregion: Payload Access
53
54    // region: Sub-Function
55
56    /// Returns the raw sub-function byte for services that carry one,
57    /// including the suppress positive response bit.
58    ///
59    /// Returns `None` for services that do not define a sub-function byte,
60    /// or if the frame is too short to contain one.
61    #[must_use]
62    fn sub_function(&self) -> Option<u8>;
63
64    /// Returns the sub-function value with the suppress positive response
65    /// bit masked off.
66    #[must_use]
67    fn sub_function_value(&self) -> Option<u8> {
68        self.sub_function().map(|sf| sf & SUB_FUNCTION_VALUE_MASK)
69    }
70
71    /// Returns `true` if the suppress positive response bit is set in the
72    /// sub-function byte (`bit 7 = 1`).
73    ///
74    /// Always returns `false` if the service does not define a sub-function.
75    #[must_use]
76    fn is_suppressed(&self) -> bool;
77
78    // endregion: Sub-Function
79
80    // region: Response Classification
81
82    /// Returns `true` if this frame is a positive response.
83    ///
84    /// A frame is a positive response if it is not a negative response -
85    /// i.e. the SID is not `0x7F`.
86    #[must_use]
87    fn is_positive_response(&self) -> bool {
88        !self.is_negative_response()
89    }
90
91    /// Returns `true` if this frame is a negative response (SID `0x7F`).
92    #[must_use]
93    fn is_negative_response(&self) -> bool;
94
95    /// Returns the `NegativeResponseCode` if this is a negative response.
96    ///
97    /// Negative response format: `[0x7F, RequestedSID, NRC]`
98    ///
99    /// Returns `None` if this is not a negative response or the frame
100    /// is too short to contain an NRC byte.
101    #[must_use]
102    fn negative_response_code(&self) -> Option<NegativeResponseCode>;
103
104    /// Returns the requested `ServiceIdentifier` from a negative response.
105    ///
106    /// Negative response format: `[0x7F, RequestedSID, NRC]`
107    ///
108    /// Returns `None` if this is not a negative response or the SID byte
109    /// at position 1 is not a known service identifier.
110    #[must_use]
111    fn requested_service_identifier(&self) -> Option<ServiceIdentifier>;
112
113    // endregion: Response Classification
114
115    // region: Validation
116
117    /// Validates the frame against UDS protocol rules.
118    ///
119    /// Checks that the SID is known, the payload length is appropriate
120    /// for the service, and the sub-function (if present) is valid.
121    ///
122    /// Individual service implementations may provide deeper validation
123    /// via their own `validate()` methods.
124    fn validate(&self) -> Result<(), UdsError>;
125
126    /// Returns `true` if [`validate`](UdsFrameExt::validate) succeeds.
127    #[must_use]
128    #[inline]
129    fn is_valid(&self) -> bool {
130        self.validate().is_ok()
131    }
132
133    // endregion: Validation
134
135    // region: Conversion
136
137    /// Parses this frame into a structured [`UdsMessage`].
138    fn to_message(&'a self) -> Result<UdsMessage<'a>, UdsError>;
139
140    // endregion: Conversion
141}
142
143// endregion: UdsFrameExt
144
145// region: UdsFrameMutExt
146
147/// Semantic extension trait for [`UdsFrameMut`].
148///
149/// Extends [`UdsFrameExt`] with mutation methods that require protocol
150/// knowledge - suppress/un-suppress operates on the sub-function byte
151/// whose position is service-dependent.
152pub trait UdsFrameMutExt<'a>: UdsFrameExt<'a> {
153    /// Sets the suppress positive response bit in the sub-function byte.
154    ///
155    /// Has no effect if the service does not define a sub-function, or
156    /// if the bit is already set.
157    fn suppress(&mut self);
158
159    /// Clears the suppress positive response bit in the sub-function byte.
160    ///
161    /// Has no effect if the service does not define a sub-function, or
162    /// if the bit is already clear.
163    fn un_suppress(&mut self);
164}
165
166// endregion: UdsFrameMutExt
167
168// region: UdsFrameExt impl for UdsFrame
169
170impl<'a> UdsFrameExt<'a> for UdsFrame<'a> {
171    // region: Service Identification
172
173    fn service_identifier(&self) -> Option<ServiceIdentifier> {
174        let byte = self.as_bytes().first()?;
175        let mut buf = core::slice::from_ref(byte);
176        ServiceIdentifier::decode(&mut buf).ok()
177    }
178
179    // endregion: Service Identification
180
181    // region: Payload Access
182
183    fn payload(&self) -> &[u8] {
184        match self.service_identifier() {
185            Some(_) => &self.as_bytes()[MIN_FRAME_LEN..],
186            None => self.as_bytes(),
187        }
188    }
189
190    fn data_iter(&self) -> core::slice::Iter<'_, u8> {
191        self.payload().iter()
192    }
193
194    // endregion: Payload Access
195
196    // region: Sub-Function
197
198    fn sub_function(&self) -> Option<u8> {
199        let sid = self.service_identifier()?;
200        if !sid.has_sub_function() {
201            return None;
202        }
203        self.as_bytes().get(SUB_FUNCTION_OFFSET).copied()
204    }
205
206    fn is_suppressed(&self) -> bool {
207        match self.sub_function() {
208            Some(sf) => sf & SUPPRESS_POSITIVE_RESPONSE_MASK != 0,
209            None => false,
210        }
211    }
212
213    // endregion: Sub-Function
214
215    // region: Response Classification
216
217    fn is_negative_response(&self) -> bool {
218        self.service_identifier() == Some(ServiceIdentifier::NegativeResponse)
219    }
220
221    fn negative_response_code(&self) -> Option<NegativeResponseCode> {
222        if !self.is_negative_response() {
223            return None;
224        }
225        let byte = self.as_bytes().get(NEGATIVE_RESPONSE_NRC_OFFSET)?;
226        let mut buf = core::slice::from_ref(byte);
227        NegativeResponseCode::decode(&mut buf).ok()
228    }
229
230    fn requested_service_identifier(&self) -> Option<ServiceIdentifier> {
231        if !self.is_negative_response() {
232            return None;
233        }
234        let byte = self
235            .as_bytes()
236            .get(NEGATIVE_RESPONSE_REQUESTED_SID_OFFSET)?;
237        let mut buf = core::slice::from_ref(byte);
238        ServiceIdentifier::decode(&mut buf).ok()
239    }
240
241    // endregion: Response Classification
242
243    // region: Validation
244
245    fn validate(&self) -> Result<(), UdsError> {
246        // Frame must have at least a SID byte
247        if self.as_bytes().is_empty() {
248            return Err(ValidationError::InvalidLength {
249                expected: MIN_FRAME_LEN,
250                actual: 0,
251            }
252            .into());
253        }
254
255        // SID must be a known service identifier
256        let sid = self
257            .service_identifier()
258            .ok_or_else(|| ValidationError::UnsupportedService(self.as_bytes()[0]))?;
259
260        // If the service has a sub-function the frame must be long enough
261        // to contain it
262        if sid.has_sub_function() && self.as_bytes().len() < MIN_SUB_FUNCTION_FRAME_LEN {
263            return Err(ValidationError::InvalidLength {
264                expected: MIN_SUB_FUNCTION_FRAME_LEN,
265                actual: self.as_bytes().len(),
266            }
267            .into());
268        }
269
270        Ok(())
271    }
272
273    // endregion: Validation
274
275    // region: Conversion
276
277    fn to_message(&'a self) -> Result<UdsMessage<'a>, UdsError> {
278        decode_message(self.as_bytes())
279    }
280
281    // endregion: Conversion
282}
283
284// endregion: UdsFrameExt impl for UdsFrame
285
286// region: UdsFrameExt impl for UdsFrameMut
287
288/// `UdsFrameMut` delegates all read accessors to its immutable counterpart,
289/// keeping the semantic logic in one place.
290impl<'a> UdsFrameExt<'a> for UdsFrameMut<'a> {
291    fn service_identifier(&self) -> Option<ServiceIdentifier> {
292        UdsFrame::from_slice(self.as_bytes()).service_identifier()
293    }
294
295    fn payload(&self) -> &[u8] {
296        match self.service_identifier() {
297            Some(_) => &self.as_bytes()[MIN_FRAME_LEN..],
298            None => self.as_bytes(),
299        }
300    }
301
302    fn data_iter(&self) -> core::slice::Iter<'_, u8> {
303        self.payload().iter()
304    }
305
306    fn sub_function(&self) -> Option<u8> {
307        UdsFrame::from_slice(self.as_bytes()).sub_function()
308    }
309
310    fn is_suppressed(&self) -> bool {
311        UdsFrame::from_slice(self.as_bytes()).is_suppressed()
312    }
313
314    fn is_negative_response(&self) -> bool {
315        UdsFrame::from_slice(self.as_bytes()).is_negative_response()
316    }
317
318    fn negative_response_code(&self) -> Option<NegativeResponseCode> {
319        UdsFrame::from_slice(self.as_bytes()).negative_response_code()
320    }
321
322    fn requested_service_identifier(&self) -> Option<ServiceIdentifier> {
323        UdsFrame::from_slice(self.as_bytes()).requested_service_identifier()
324    }
325
326    fn validate(&self) -> Result<(), UdsError> {
327        UdsFrame::from_slice(self.as_bytes()).validate()
328    }
329
330    fn to_message(&'a self) -> Result<UdsMessage<'a>, UdsError> {
331        decode_message(self.as_bytes())
332    }
333}
334
335// endregion: UdsFrameExt impl for UdsFrameMut
336
337// region: UdsFrameMutExt impl for UdsFrameMut
338
339impl<'a> UdsFrameMutExt<'a> for UdsFrameMut<'a> {
340    fn suppress(&mut self) {
341        if self.is_suppressed() || self.sub_function().is_none() {
342            return;
343        }
344        if let Some(sf) = self.as_bytes_mut().get_mut(SUB_FUNCTION_OFFSET) {
345            *sf |= SUPPRESS_POSITIVE_RESPONSE_MASK;
346        }
347    }
348
349    fn un_suppress(&mut self) {
350        if !self.is_suppressed() || self.sub_function().is_none() {
351            return;
352        }
353        if let Some(sf) = self.as_bytes_mut().get_mut(SUB_FUNCTION_OFFSET) {
354            *sf &= SUB_FUNCTION_VALUE_MASK;
355        }
356    }
357}
358
359// endregion: UdsFrameMutExt impl for UdsFrameMut