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