1use 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 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 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 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 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 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 }
288
289#[derive(Debug, PartialEq)]
290struct DTCSeverityMaskRecord {
291 dtc_status_mask: u8,
292 dtc_severity_mask: u8,
293}
294
295fn 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#[derive(Debug, PartialEq)]
305pub struct ReportNumberOfDTCByMaskResponse {
306 dtc_status_availability_mask: u8,
307 dtc_format_identifier: DTCFormat,
308 dtc_count: u16,
309}
310
311fn 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#[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: u32,
386 status_of_dtc: u8,
388}
389
390fn 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
469fn 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#[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
505fn 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
522fn compose_request_short(sub_function: SubFunction) -> Vec<u8> {
524 vec![READ_DTC_INFORMATION_SID, sub_function as u8]
525}
526
527fn 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
543fn 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]
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}