uds_rs/uds/
read_dtc_information.rs

1//! # Implementation of ReadDTCInformation 0x19 service
2//!
3//! This module provides following methods for UdsClient:
4//!
5//! [UdsClient::report_number_of_dtc_by_status_mask]  subfunction 0x01  
6//! [UdsClient::report_dtc_by_status_mask]  subfunction 0x02  
7//! [UdsClient::report_dtc_snapshot_record_by_dtc_number]  subfunction 0x04  
8//! [UdsClient::report_number_of_dtc_by_status_mask]  subfunction 0x06  
9//! [UdsClient::report_most_recent_confirmed_dtc]  subfunction 0x0e  
10//!
11use super::*;
12use crate::uds::uds_definitions::SEND_RECEIVE_SID_OFFSET;
13use num_enum::{IntoPrimitive, TryFromPrimitive};
14
15const READ_DTC_INFORMATION_SID: u8 = 0x19;
16
17#[allow(dead_code)]
18#[repr(u8)]
19#[derive(Debug, PartialEq)]
20pub enum ReadDTCInformationResponse {
21    ReportNumberOfDTCbyStatusMask(ReportNumberOfDTCByMaskResponse),
22    ReportDTCByStatusMask(ReportDTCsResponse),
23    ReportDTCSnapshotIdentification = 0x03,
24    ReportDTCSnapshotRecordByDTCNumber(ReportDTCSnapshotRecordByDTCNumber),
25    ReportDTCStoredDataByRecordNumber = 0x05,
26    ReportDTCExtDataRecordByDTCNumber = 0x06,
27    ReportNumberOfDTCBySeverityMaskRecord(ReportNumberOfDTCByMaskResponse),
28    ReportDTCBySeverityMaskRecord = 0x08,
29    ReportSeverityInformationOfDTC = 0x09,
30    ReportSupportedDTC(ReportDTCsResponse),
31    ReportFirstTestFailedDTC(ReportDTCsResponse),
32    ReportFirstConfirmedDTC(ReportDTCsResponse),
33    ReportMostRecentTestFailedDTC(ReportDTCsResponse),
34    ReportMostRecentConfirmedDTC(ReportDTCsResponse),
35    ReportMirrorMemoryDTCByStatusMask(ReportDTCsResponse),
36    ReportMirrorMemoryDTCExtDataRecordByDTCNumber = 0x10,
37    ReportNumberOfMirrorMemoryDTCByStatusMask(ReportNumberOfDTCByMaskResponse),
38    ReportNumberOfEmissionsOBDDTCByStatusMask(ReportNumberOfDTCByMaskResponse),
39    ReportEmissionsOBDDTCByStatusMask(ReportDTCsResponse),
40    ReportDTCFaultDetectionCounter = 0x14,
41    ReportDTCWithPermanentStatus(ReportDTCsResponse),
42    ReportDTCExtDataRecordByRecordNumber = 0x16,
43    ReportUserDefMemoryDTCByStatusMask = 0x17,
44    ReportUserDefMemoryDTCSnapshotRecordByDTCNumber = 0x18,
45    ReportUserDefMemoryDTCExtDataRecordByDTCNumber = 0x19,
46    ReportWWHOBDDTCByMaskRecord = 0x42,
47    ReportWWHOBDDTCWithPermanentStatus = 0x55,
48}
49
50#[repr(u8)]
51#[derive(TryFromPrimitive, IntoPrimitive, Debug, PartialEq, Clone, Copy)]
52enum SubFunction {
53    ReportNumberOfDTCbyStatusMask = 0x01,
54    ReportDTCByStatusMask = 0x02,
55    ReportDTCSnapshotIdentification = 0x03,
56    ReportDTCSnapshotRecordByDTCNumber = 0x04,
57    ReportDTCStoredDataByRecordNumber = 0x05,
58    ReportDTCExtDataRecordByDTCNumber = 0x06,
59    ReportNumberOfDTCBySeverityMaskRecord = 0x07,
60    ReportDTCBySeverityMaskRecord = 0x08,
61    ReportSeverityInformationOfDTC = 0x09,
62    ReportSupportedDTC = 0x0A,
63    ReportFirstTestFailedDTC = 0x0B,
64    ReportFirstConfirmedDTC = 0x0C,
65    ReportMostRecentTestFailedDTC = 0x0D,
66    ReportMostRecentConfirmedDTC = 0x0E,
67    ReportMirrorMemoryDTCByStatusMask = 0x0F,
68    ReportMirrorMemoryDTCExtDataRecordByDTCNumber = 0x10,
69    ReportNumberOfMirrorMemoryDTCByStatusMask = 0x11,
70    ReportNumberOfEmissionsOBDDTCByStatusMask = 0x12,
71    ReportEmissionsOBDDTCByStatusMask = 0x13,
72    ReportDTCFaultDetectionCounter = 0x14,
73    ReportDTCWithPermanentStatus = 0x15,
74    ReportDTCExtDataRecordByRecordNumber = 0x16,
75    ReportUserDefMemoryDTCByStatusMask = 0x17,
76    ReportUserDefMemoryDTCSnapshotRecordByDTCNumber = 0x18,
77    ReportUserDefMemoryDTCExtDataRecordByDTCNumber = 0x19,
78    ReportWWHOBDDTCByMaskRecord = 0x42,
79    ReportWWHOBDDTCWithPermanentStatus = 0x55,
80}
81
82#[derive(IntoPrimitive, TryFromPrimitive, Debug, PartialEq, Clone, Copy)]
83#[repr(u8)]
84#[allow(non_camel_case_types)]
85enum DTCFormat {
86    SAE_J2012_DA_DTCFormat_00 = 0x00,
87    ISO_14229_1_DTCFormat = 0x01,
88    SAE_J1939_73_DTCFormat = 0x02,
89    ISO_11992_4_DTCFormat = 0x03,
90    SAE_J2012_DA_DTCFormat_04 = 0x04,
91}
92
93impl UdsClient {
94    /// 0x01
95    pub async fn report_number_of_dtc_by_status_mask(
96        &self,
97        dtc_status_mask: u8,
98    ) -> EcuResponseResult {
99        let request = compose_report_number_of_dtc_by_status_mask_request(
100            SubFunction::ReportNumberOfDTCbyStatusMask,
101            dtc_status_mask,
102        );
103        let raw_response = self.send_and_receive(&request).await?;
104        let response = parse_report_number_of_dtc_by_status_mask_response(&raw_response);
105        response
106    }
107
108    /// 0x02
109    pub async fn report_dtc_by_status_mask(&self, dtc_status_mask: u8) -> EcuResponseResult {
110        let request = compose_report_number_of_dtc_by_status_mask_request(
111            SubFunction::ReportDTCByStatusMask,
112            dtc_status_mask,
113        );
114        let raw_response = self.send_and_receive(&request).await?;
115        let response = parse_report_dtcs(&raw_response);
116        response
117    }
118
119    // /// 0x03
120    // #[allow(dead_code)]
121    // async fn report_dtc_snapshot_identification(&self) -> EcuResponseResult {
122    //     Err(UdsError::NotImplemented)
123    // }
124
125    /// 0x04
126    /// dtc_mask_record is 3 byte value - most significant byte will be dropped.
127    /// Needs database to correctly parse the response. Length of snapshotData can't be derived from
128    /// plain response
129    async fn report_dtc_snapshot_record_by_dtc_number(
130        &self,
131        dtc_mask_record: u32,
132        dtc_snapshot_record_number: u8,
133    ) -> EcuResponseResult {
134        let request = compose_report_dtc_snapshot_request(
135            SubFunction::ReportDTCSnapshotRecordByDTCNumber,
136            dtc_mask_record,
137            dtc_snapshot_record_number,
138        );
139        let raw_response = self.send_and_receive(&request).await?;
140        let response = parse_report_dtc_snapshot_record_by_dtc_number_response(&raw_response);
141        response
142    }
143
144    // /// 0x05
145    // #[allow(dead_code)]
146    // async fn report_dtc_stored_data_by_record_number(&self) -> EcuResponseResult {
147    //     Err(UdsError::NotImplemented)
148    // }
149
150    /// 0x06
151    pub async fn report_dtc_ext_data_record_by_dtc_number(
152        &self,
153        dtc_mask_record: u32,
154        dtc_ext_data_record_number: u8,
155    ) -> EcuResponseResult {
156        let request = compose_report_dtc_ext_data_by_dtc_number_request(
157            SubFunction::ReportDTCExtDataRecordByDTCNumber,
158            dtc_mask_record,
159            dtc_ext_data_record_number,
160        );
161        let raw_response = self.send_and_receive(&request).await?;
162        let response = parse_report_dtc_ext_data_by_dtc_number_response(&raw_response);
163        response
164    }
165
166    // /// 0x07
167    // #[allow(dead_code)]
168    // async fn report_number_of_dtc_by_severity_mask_record(&self) -> EcuResponseResult {
169    //     Err(UdsError::NotImplemented)
170    // }
171
172    // /// 0x08
173    // #[allow(dead_code)]
174    // async fn report_dtc_by_severity_mask_record(&self) -> EcuResponseResult {
175    //     Err(UdsError::NotImplemented)
176    // }
177
178    // /// 0x09
179    // #[allow(dead_code)]
180    // async fn report_severity_information_of_dtc(&self) -> EcuResponseResult {
181    //     Err(UdsError::NotImplemented)
182    // }
183
184    // /// 0x0A
185    // #[allow(dead_code)]
186    // async fn report_supported_dtc(&self) -> EcuResponseResult {
187    //     Err(UdsError::NotImplemented)
188    // }
189
190    // /// 0x0B
191    // #[allow(dead_code)]
192    // async fn report_first_test_failed_dtc(&self) -> EcuResponseResult {
193    //     Err(UdsError::NotImplemented)
194    // }
195
196    // /// 0x0C
197    // #[allow(dead_code)]
198    // async fn report_first_confirmed_dtc(&self) -> EcuResponseResult {
199    //     Err(UdsError::NotImplemented)
200    // }
201
202    // /// 0x0D
203    // #[allow(dead_code)]
204    // async fn report_most_recent_test_failed_dtc(&self) -> EcuResponseResult {
205    //     Err(UdsError::NotImplemented)
206    // }
207
208    /// 0x0E
209    pub async fn report_most_recent_confirmed_dtc(&self) -> EcuResponseResult {
210        let request = compose_request_short(SubFunction::ReportMostRecentConfirmedDTC);
211        let raw_response = self.send_and_receive(&request).await?;
212        let response = parse_report_dtcs(&raw_response);
213        response
214    }
215
216    // /// 0x0F
217    // #[allow(dead_code)]
218    // async fn report_mirror_memory_dtc_by_status_mask(&self) -> EcuResponseResult {
219    //     Err(UdsError::NotImplemented)
220    // }
221
222    // /// 0x10
223    // #[allow(dead_code)]
224    // async fn report_mirror_memory_dtc_ext_data_record_by_dtc_number(&self) -> EcuResponseResult {
225    //     Err(UdsError::NotImplemented)
226    // }
227
228    // /// 0x11
229    // #[allow(dead_code)]
230    // async fn report_number_of_mirror_memory_dtc_by_status_mask(&self) -> EcuResponseResult {
231    //     Err(UdsError::NotImplemented)
232    // }
233
234    // /// 0x12
235    // #[allow(dead_code)]
236    // async fn report_number_of_emissions_obddtc_by_status_mask(&self) -> EcuResponseResult {
237    //     Err(UdsError::NotImplemented)
238    // }
239
240    // /// 0x13
241    // #[allow(dead_code)]
242    // async fn report_emissions_obddtc_by_status_mask(&self) -> EcuResponseResult {
243    //     Err(UdsError::NotImplemented)
244    // }
245
246    // /// 0x14
247    // #[allow(dead_code)]
248    // async fn report_dtc_fault_detection_counter(&self) -> EcuResponseResult {
249    //     Err(UdsError::NotImplemented)
250    // }
251
252    // /// 0x15
253    // #[allow(dead_code)]
254    // async fn report_dtc_with_permanent_status(&self) -> EcuResponseResult {
255    //     Err(UdsError::NotImplemented)
256    // }
257
258    // /// 0x16
259    // #[allow(dead_code)]
260    // async fn report_dtc_ext_data_record_by_record_number(&self) -> EcuResponseResult {
261    //     Err(UdsError::NotImplemented)
262    // }
263
264    // /// 0x17
265    // #[allow(dead_code)]
266    // async fn report_user_def_memory_dtc_by_status_mask(&self) -> EcuResponseResult {
267    //     Err(UdsError::NotImplemented)
268    // }
269
270    // /// 0x18
271    // #[allow(dead_code)]
272    // async fn report_user_def_memory_dtc_snapshot_record_by_dtc_number(&self) -> EcuResponseResult {
273    //     Err(UdsError::NotImplemented)
274    // }
275
276    // /// 0x19
277    // #[allow(dead_code)]
278    // async fn report_user_def_memory_dtc_ext_data_record_by_dtc_number(&self) -> EcuResponseResult {
279    //     Err(UdsError::NotImplemented)
280    // }
281
282    // /// 0x42
283    // #[allow(dead_code)]
284    // async fn report_wwhobddtc_by_mask_record(&self) -> EcuResponseResult {
285    //     Err(UdsError::NotImplemented)
286    // }
287}
288
289#[derive(Debug, PartialEq)]
290struct DTCSeverityMaskRecord {
291    dtc_status_mask: u8,
292    dtc_severity_mask: u8,
293}
294
295/// Shared between subfunctions 0x01, 0x02, 0x0F, 0x11, 0x12, 0x13
296fn compose_report_number_of_dtc_by_status_mask_request(
297    subfunction: SubFunction,
298    dtc_status_mask: u8,
299) -> Vec<u8> {
300    vec![READ_DTC_INFORMATION_SID, subfunction as u8, dtc_status_mask]
301}
302
303/// Shared between subfunctions 0x01, 0x07, 0x11, 0x12
304#[derive(Debug, PartialEq)]
305pub struct ReportNumberOfDTCByMaskResponse {
306    dtc_status_availability_mask: u8,
307    dtc_format_identifier: DTCFormat,
308    dtc_count: u16,
309}
310
311/// Shared between subfunctions 0x01, 0x07, 0x11, 0x12
312fn parse_report_number_of_dtc_by_status_mask_response(raw_response: &[u8]) -> EcuResponseResult {
313    let mut response_iter = raw_response.iter();
314    let sid = *response_iter.next().ok_or(UdsError::ResponseEmpty)?;
315    if sid != READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET {
316        return Err(UdsError::SidMismatch {
317            expected: READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET,
318            received: sid,
319            raw_message: raw_response.to_vec(),
320        });
321    }
322    let report_type: SubFunction =
323        SubFunction::try_from(*response_iter.next().ok_or(UdsError::InvalidLength {
324            raw_message: raw_response.to_vec(),
325        })?)
326        .map_err(|_| UdsError::ResponseIncorrect {
327            raw_message: raw_response.to_vec(),
328        })?;
329    let dtc_status_availability_mask: u8 =
330        *response_iter.next().ok_or(UdsError::InvalidLength {
331            raw_message: raw_response.to_vec(),
332        })?;
333    let dtc_format_identifier_byte = *response_iter.next().ok_or(UdsError::InvalidLength {
334        raw_message: raw_response.to_vec(),
335    })?;
336    let dtc_format_identifier =
337        DTCFormat::try_from_primitive(dtc_format_identifier_byte).map_err(|_| {
338            UdsError::ResponseIncorrect {
339                raw_message: raw_response.to_vec(),
340            }
341        })?;
342    let msb = *response_iter.next().ok_or(UdsError::InvalidLength {
343        raw_message: raw_response.to_vec(),
344    })?;
345    let lsb = *response_iter.next().ok_or(UdsError::InvalidLength {
346        raw_message: raw_response.to_vec(),
347    })?;
348    let dtc_count: u16 = ((msb as u16) << 8) + lsb as u16;
349
350    let parsed = ReportNumberOfDTCByMaskResponse {
351        dtc_status_availability_mask,
352        dtc_format_identifier,
353        dtc_count,
354    };
355
356    let response = match report_type {
357        SubFunction::ReportNumberOfDTCbyStatusMask => {
358            ReadDTCInformationResponse::ReportNumberOfDTCbyStatusMask(parsed)
359        }
360        SubFunction::ReportNumberOfDTCBySeverityMaskRecord => {
361            ReadDTCInformationResponse::ReportNumberOfDTCBySeverityMaskRecord(parsed)
362        }
363        SubFunction::ReportNumberOfMirrorMemoryDTCByStatusMask => {
364            ReadDTCInformationResponse::ReportNumberOfMirrorMemoryDTCByStatusMask(parsed)
365        }
366        SubFunction::ReportNumberOfEmissionsOBDDTCByStatusMask => {
367            ReadDTCInformationResponse::ReportNumberOfEmissionsOBDDTCByStatusMask(parsed)
368        }
369        _ => return Err(UdsError::InvalidArgument),
370    };
371    let ret = UdsResponse::ReadDTCInformation(DataFormat::Parsed(response));
372    Ok(ret)
373}
374
375/// Shared between 0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15
376#[derive(Debug, PartialEq)]
377pub struct ReportDTCsResponse {
378    dtc_status_availability_mask: u8,
379    dtc_and_status_records: Vec<DTCAndStatusRecord>,
380}
381
382#[derive(Debug, PartialEq)]
383struct DTCAndStatusRecord {
384    /// dtc has size of 24 bytes, highest byte of u32 is and should be ignored
385    dtc: u32,
386    // TODO each bit in status of DTC has its meaning. It should be represented as different structure, than plain u8
387    status_of_dtc: u8,
388}
389
390/// Shared between 0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15
391fn parse_report_dtcs(raw_response: &[u8]) -> EcuResponseResult {
392    let mut response_iter = raw_response.iter();
393    let sid = *response_iter.next().ok_or(UdsError::ResponseEmpty)?;
394    if sid != READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET {
395        return Err(UdsError::SidMismatch {
396            expected: READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET,
397            received: sid,
398            raw_message: raw_response.to_vec(),
399        });
400    }
401    let report_type = *response_iter.next().ok_or(UdsError::InvalidLength {
402        raw_message: raw_response.to_vec(),
403    })?;
404    let dtc_status_availability_mask = *response_iter.next().ok_or(UdsError::InvalidLength {
405        raw_message: raw_response.to_vec(),
406    })?;
407
408    let mut dtc_and_status_records: Vec<DTCAndStatusRecord> = Vec::new();
409
410    while let Some(&high_byte) = response_iter.next() {
411        let middle_byte = *response_iter.next().ok_or(UdsError::InvalidLength {
412            raw_message: raw_response.to_vec(),
413        })?;
414        let low_byte = *response_iter.next().ok_or(UdsError::InvalidLength {
415            raw_message: raw_response.to_vec(),
416        })?;
417        let status_of_dtc = *response_iter.next().ok_or(UdsError::InvalidLength {
418            raw_message: raw_response.to_vec(),
419        })?;
420        let dtc = ((high_byte as u32) << 16) + ((middle_byte as u32) << 8) + low_byte as u32;
421
422        dtc_and_status_records.push(DTCAndStatusRecord { dtc, status_of_dtc });
423    }
424
425    let parsed = ReportDTCsResponse {
426        dtc_status_availability_mask,
427        dtc_and_status_records,
428    };
429
430    let sub_function =
431        SubFunction::try_from(report_type).map_err(|_| UdsError::ResponseIncorrect {
432            raw_message: raw_response.to_vec(),
433        })?;
434
435    let response = match sub_function {
436        SubFunction::ReportDTCByStatusMask => {
437            ReadDTCInformationResponse::ReportDTCByStatusMask(parsed)
438        }
439        SubFunction::ReportSupportedDTC => ReadDTCInformationResponse::ReportSupportedDTC(parsed),
440        SubFunction::ReportFirstTestFailedDTC => {
441            ReadDTCInformationResponse::ReportFirstTestFailedDTC(parsed)
442        }
443        SubFunction::ReportFirstConfirmedDTC => {
444            ReadDTCInformationResponse::ReportFirstConfirmedDTC(parsed)
445        }
446        SubFunction::ReportMostRecentTestFailedDTC => {
447            ReadDTCInformationResponse::ReportMostRecentTestFailedDTC(parsed)
448        }
449        SubFunction::ReportMostRecentConfirmedDTC => {
450            ReadDTCInformationResponse::ReportMostRecentConfirmedDTC(parsed)
451        }
452        SubFunction::ReportMirrorMemoryDTCByStatusMask => {
453            ReadDTCInformationResponse::ReportMirrorMemoryDTCByStatusMask(parsed)
454        }
455        SubFunction::ReportEmissionsOBDDTCByStatusMask => {
456            ReadDTCInformationResponse::ReportEmissionsOBDDTCByStatusMask(parsed)
457        }
458        SubFunction::ReportDTCWithPermanentStatus => {
459            ReadDTCInformationResponse::ReportDTCWithPermanentStatus(parsed)
460        }
461        _ => return Err(UdsError::InvalidArgument),
462    };
463
464    let ret = UdsResponse::ReadDTCInformation(DataFormat::Parsed(response));
465
466    Ok(ret)
467}
468
469/// Shared between 0x03, 0x04
470fn compose_report_dtc_snapshot_request(
471    sub_function: SubFunction,
472    dtc_mask_record: u32,
473    dtc_snapshot_record_number: u8,
474) -> Vec<u8> {
475    vec![
476        READ_DTC_INFORMATION_SID,
477        sub_function as u8,
478        (dtc_mask_record >> 16) as u8,
479        (dtc_mask_record >> 8) as u8,
480        dtc_mask_record as u8,
481        dtc_snapshot_record_number,
482    ]
483}
484
485/// Used only by 0x04
486#[derive(Debug, PartialEq)]
487pub struct ReportDTCSnapshotRecordByDTCNumber {
488    dtc_and_status_record: DTCAndStatusRecord,
489    snapshot_records: Vec<SnapshotRecord>,
490}
491
492#[derive(Debug, PartialEq)]
493struct SnapshotRecord {
494    dtc_snapshot_record_number: u8,
495    dtc_snapshot_record_number_of_identifiers: u8,
496    dtc_snapshot_record: Vec<SnapshotData>,
497}
498
499#[derive(Debug, PartialEq)]
500struct SnapshotData {
501    data_identifier: u16,
502    snapshot_data: Vec<u8>,
503}
504
505/// Used only by 0x04
506fn parse_report_dtc_snapshot_record_by_dtc_number_response(
507    raw_response: &[u8],
508) -> EcuResponseResult {
509    let mut response = raw_response.iter();
510    let sid = *response.next().ok_or(UdsError::ResponseEmpty)?;
511    if sid != READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET {
512        return Err(UdsError::SidMismatch {
513            expected: READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET,
514            received: sid,
515            raw_message: raw_response.to_vec(),
516        });
517    }
518    let ret = UdsResponse::ReadDTCInformation(DataFormat::Raw(raw_response[1..].to_vec()));
519    Ok(ret)
520}
521
522/// Shared between 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x14, 0x15
523fn compose_request_short(sub_function: SubFunction) -> Vec<u8> {
524    vec![READ_DTC_INFORMATION_SID, sub_function as u8]
525}
526
527/// Shared between 0x06, 0x10
528fn compose_report_dtc_ext_data_by_dtc_number_request(
529    sub_function: SubFunction,
530    dtc_mask_record: u32,
531    dtc_ext_data_record_number: u8,
532) -> Vec<u8> {
533    vec![
534        READ_DTC_INFORMATION_SID,
535        sub_function as u8,
536        (dtc_mask_record >> 16) as u8,
537        (dtc_mask_record >> 8) as u8,
538        dtc_mask_record as u8,
539        dtc_ext_data_record_number,
540    ]
541}
542
543/// shared between 0x06, 0x10
544fn parse_report_dtc_ext_data_by_dtc_number_response(raw_response: &[u8]) -> EcuResponseResult {
545    let mut response = raw_response.iter();
546    let sid = *response.next().ok_or(UdsError::ResponseEmpty)?;
547    if sid != READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET {
548        return Err(UdsError::SidMismatch {
549            expected: READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET,
550            received: sid,
551            raw_message: raw_response.to_vec(),
552        });
553    }
554    let ret = UdsResponse::ReadDTCInformation(DataFormat::Raw(raw_response[1..].to_vec()));
555    Ok(ret)
556}
557
558#[cfg(test)]
559mod tests {
560    use super::*;
561
562    // Test compose for 0x01 - ReportNumberOfDTCbyStatusMask
563    #[test]
564    fn test_compose_request_0x01() {
565        let sub_function: SubFunction = SubFunction::try_from(0x1).unwrap();
566        let dtc_status_mask = 0x42;
567        let result = compose_report_number_of_dtc_by_status_mask_request(
568            SubFunction::ReportNumberOfDTCbyStatusMask,
569            dtc_status_mask,
570        );
571        let expected = vec![
572            READ_DTC_INFORMATION_SID,
573            sub_function as u8,
574            dtc_status_mask,
575        ];
576        assert_eq!(result, expected);
577    }
578
579    #[test]
580    fn test_parse_response_0x01() {
581        let sid = READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET;
582        let report_type = SubFunction::ReportNumberOfDTCbyStatusMask;
583        let dtc_status_availability_mask: u8 = 0x18;
584        let dtc_format = DTCFormat::ISO_14229_1_DTCFormat;
585        let dtc_count: u16 = 0x100f;
586        let raw_response: Vec<u8> = vec![
587            sid,
588            report_type as u8,
589            dtc_status_availability_mask,
590            dtc_format as u8,
591            (dtc_count >> 8) as u8,
592            dtc_count as u8,
593        ];
594        let result = parse_report_number_of_dtc_by_status_mask_response(&raw_response);
595        let expected = UdsResponse::ReadDTCInformation(DataFormat::Parsed(
596            ReadDTCInformationResponse::ReportNumberOfDTCbyStatusMask(
597                ReportNumberOfDTCByMaskResponse {
598                    dtc_status_availability_mask,
599                    dtc_format_identifier: dtc_format,
600                    dtc_count,
601                },
602            ),
603        ));
604        assert_eq!(result, Ok(expected));
605    }
606
607    #[test]
608    fn test_compose_request_0x02() {
609        let sub_function = SubFunction::try_from(0x2).unwrap();
610        let dtc_status_mask = 0x0;
611        let expected = vec![
612            READ_DTC_INFORMATION_SID,
613            sub_function as u8,
614            dtc_status_mask,
615        ];
616        let result = compose_report_number_of_dtc_by_status_mask_request(
617            SubFunction::ReportDTCByStatusMask,
618            dtc_status_mask,
619        );
620        assert_eq!(result, expected);
621    }
622
623    #[test]
624    fn test_parse_response_0x02() {
625        let sid = READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET;
626        let report_type = SubFunction::try_from(0x2).unwrap();
627        let dtc_status_availability_mask: u8 = 0xff;
628        let dtc_and_status_record = vec![
629            DTCAndStatusRecord {
630                dtc: 0x123456,
631                status_of_dtc: 0xff,
632            },
633            DTCAndStatusRecord {
634                dtc: 0x42,
635                status_of_dtc: 0x0,
636            },
637            DTCAndStatusRecord {
638                dtc: 0x0,
639                status_of_dtc: 0xff,
640            },
641            DTCAndStatusRecord {
642                dtc: 0xffffff,
643                status_of_dtc: 0xff,
644            },
645        ];
646        let mut raw_response: Vec<u8> = vec![sid, report_type as u8, dtc_status_availability_mask];
647        for record in &dtc_and_status_record {
648            raw_response.push((record.dtc >> 16) as u8);
649            raw_response.push((record.dtc >> 8) as u8);
650            raw_response.push(record.dtc as u8);
651            raw_response.push(record.status_of_dtc);
652        }
653        let result = parse_report_dtcs(&raw_response);
654        let expected = UdsResponse::ReadDTCInformation(DataFormat::Parsed(
655            ReadDTCInformationResponse::ReportDTCByStatusMask(ReportDTCsResponse {
656                dtc_status_availability_mask,
657                dtc_and_status_records: dtc_and_status_record,
658            }),
659        ));
660        assert_eq!(result, Ok(expected));
661    }
662
663    #[test]
664    fn test_parse_empty_response_0x02() {
665        let sid = READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET;
666        let report_type = SubFunction::try_from(0x2).unwrap();
667        let dtc_status_availability_mask: u8 = 0xff;
668        let dtc_and_status_record: Vec<DTCAndStatusRecord> = vec![];
669        let raw_response = vec![sid, report_type as u8, dtc_status_availability_mask];
670        let result = parse_report_dtcs(&raw_response);
671        let expected = UdsResponse::ReadDTCInformation(DataFormat::Parsed(
672            ReadDTCInformationResponse::ReportDTCByStatusMask(ReportDTCsResponse {
673                dtc_status_availability_mask,
674                dtc_and_status_records: vec![],
675            }),
676        ));
677        assert_eq!(result, Ok(expected));
678    }
679
680    #[test]
681    fn test_compose_request_iso_0x04() {
682        let sid = READ_DTC_INFORMATION_SID;
683        let sub_function = SubFunction::try_from(0x4).unwrap();
684        let raw_dtc_mask_record: u32 = 0x12345678;
685        let dtc_snapshot_record_number: u8 = 0xff;
686        let dtc_mask_record = raw_dtc_mask_record;
687        let result = compose_report_dtc_snapshot_request(
688            sub_function,
689            dtc_mask_record,
690            dtc_snapshot_record_number,
691        );
692        let expected = vec![
693            sid,
694            sub_function as u8,
695            (raw_dtc_mask_record >> 16) as u8,
696            (raw_dtc_mask_record >> 8) as u8,
697            raw_dtc_mask_record as u8,
698            dtc_snapshot_record_number,
699        ];
700        assert_eq!(result, expected);
701    }
702
703    #[test]
704    fn test_parse_response_0x04() {
705        let raw_response = vec![];
706        let result = parse_report_dtc_snapshot_record_by_dtc_number_response(&raw_response);
707        assert_eq!(Err(UdsError::ResponseEmpty), result);
708    }
709
710    #[test]
711    fn test_compose_request_iso_0x06() {
712        let sid = READ_DTC_INFORMATION_SID;
713        let sub_function = SubFunction::try_from(0x4).unwrap();
714        let raw_dtc_mask_record: u32 = 0x12345678;
715        let dtc_ext_data_record_number: u8 = 0xff;
716        let dtc_mask_record = raw_dtc_mask_record;
717        let result = compose_report_dtc_snapshot_request(
718            sub_function,
719            dtc_mask_record,
720            dtc_ext_data_record_number,
721        );
722        let expected = vec![
723            sid,
724            sub_function as u8,
725            (raw_dtc_mask_record >> 16) as u8,
726            (raw_dtc_mask_record >> 8) as u8,
727            raw_dtc_mask_record as u8,
728            dtc_ext_data_record_number,
729        ];
730        assert_eq!(result, expected);
731    }
732
733    #[test]
734    fn test_parse_response_0x06() {
735        let raw_response = vec![];
736        let result = parse_report_dtc_ext_data_by_dtc_number_response(&raw_response);
737        assert_eq!(Err(UdsError::ResponseEmpty), result);
738    }
739
740    fn test_compose_request_0x0e() {
741        let sid = READ_DTC_INFORMATION_SID;
742        let subfunction = SubFunction::try_from(0x0e).unwrap();
743        let result = compose_request_short(subfunction);
744        assert_eq!(vec![sid, 0x0e], result);
745    }
746
747    #[test]
748    fn test_parse_response_0x0e() {
749        let sid = READ_DTC_INFORMATION_SID + SEND_RECEIVE_SID_OFFSET;
750        let report_type = SubFunction::try_from(0xe).unwrap();
751        let dtc_status_availability_mask: u8 = 0xff;
752        let dtc_and_status_record = vec![DTCAndStatusRecord {
753            dtc: 0x123456,
754            status_of_dtc: 0xff,
755        }];
756        let mut raw_response: Vec<u8> = vec![sid, report_type as u8, dtc_status_availability_mask];
757        for record in &dtc_and_status_record {
758            raw_response.push((record.dtc >> 16) as u8);
759            raw_response.push((record.dtc >> 8) as u8);
760            raw_response.push(record.dtc as u8);
761            raw_response.push(record.status_of_dtc);
762        }
763        let result = parse_report_dtcs(&raw_response);
764        let expected = UdsResponse::ReadDTCInformation(DataFormat::Parsed(
765            ReadDTCInformationResponse::ReportMostRecentConfirmedDTC(ReportDTCsResponse {
766                dtc_status_availability_mask,
767                dtc_and_status_records: dtc_and_status_record,
768            }),
769        ));
770        assert_eq!(result, Ok(expected));
771    }
772}