#![allow(clippy::non_minimal_cfg)]
use crate::{
error::Error,
response::Code,
response::{Response, SubFunction},
utils, Configuration, DTCReportType, DataIdentifier, ResponseData, Service,
};
use std::{collections::HashSet, sync::LazyLock};
pub static READ_DTC_INFO_NEGATIVES: LazyLock<HashSet<Code>> = LazyLock::new(|| {
HashSet::from([
Code::SubFunctionNotSupported,
Code::IncorrectMessageLengthOrInvalidFormat,
Code::RequestOutOfRange,
])
});
#[inline]
fn dtc_ext_data_length(cfg: &Configuration, number: u8) -> Result<usize, Error> {
cfg.dtc.get(&number).copied().ok_or_else(|| {
Error::OtherError(format!(
"the length of DTC ext data record number: 0x{number:02X} is not configured"
))
})
}
fn parse_dtc_ext_data_records(
data: &[u8],
mut offset: usize,
cfg: &Configuration,
) -> Result<Vec<DTCExtDataRecord>, Error> {
let data_len = data.len();
let mut records = Vec::new();
while data_len > offset {
utils::data_length_check(data_len, offset + 1, false)?;
let number = data[offset];
if number > 0xFD {
return Err(Error::InvalidData(hex::encode(data)));
}
offset += 1;
let record_len = dtc_ext_data_length(cfg, number)?;
utils::data_length_check(data_len, offset + record_len, false)?;
records.push(DTCExtDataRecord {
number,
data: data[offset..offset + record_len].to_vec(),
});
offset += record_len;
}
Ok(records)
}
rsutil::enum_extend!(
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum DTCFormatIdentifier {
SAE_J2012_DA_DTCFormat_00 = 0x00,
ISO_14229_1_DTCFormat = 0x01,
SAE_J1939_73_DTCFormat = 0x02,
ISO_11992_4_DTCFormat = 0x03,
SAE_J2012_DA_DTCFormat_04 = 0x04,
},
u8,
Error,
ReservedError
);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DTCAndStatusRecord {
pub dtc: utils::U24,
pub status: u8, }
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCSnapshotIdentification {
pub dtc: utils::U24,
pub number: u8,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCSnapshotRecord {
pub did: DataIdentifier,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCSnapshotRecordByDTCNumber {
pub number: u8, pub number_of_identifier: u8,
pub records: Vec<DTCSnapshotRecord>,
}
#[cfg(feature = "std2006")]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCSnapshotRecordByRecordNumber {
pub status_record: Option<DTCAndStatusRecord>,
pub number_of_identifier: Option<u8>,
pub records: Vec<DTCSnapshotRecord>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCStoredDataRecord {
pub did: DataIdentifier,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ReportDTCStoredDataByRecord {
pub number: u8,
pub record: Option<DTCAndStatusRecord>,
pub number_of_identifier: Option<u8>,
pub records: Vec<DTCStoredDataRecord>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCExtDataRecord {
pub number: u8, pub data: Vec<u8>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCAndSeverityRecord1 {
pub severity: u8,
pub func_unit: u8,
pub dtc: utils::U24,
pub status: u8,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCFaultDetectionCounterRecord {
pub dtc: utils::U24,
pub counter: u8, }
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCExtDataRecordByRecordNumber {
pub status_record: DTCAndStatusRecord,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UserDefDTCSnapshotRecord {
pub number: u8, pub number_of_identifier: u8,
pub records: Vec<DTCSnapshotRecord>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DTCAndSeverityRecord {
pub severity: u8,
pub dtc: utils::U24,
pub status: u8,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DTCInfo {
ReportNumberOfDTCByStatusMask {
avl_mask: u8,
fid: DTCFormatIdentifier,
count: u16,
},
ReportDTCByStatusMask {
avl_mask: u8,
records: Vec<DTCAndStatusRecord>,
},
ReportDTCSnapshotIdentification {
records: Vec<DTCSnapshotIdentification>,
},
ReportDTCSnapshotRecordByDTCNumber {
status_record: DTCAndStatusRecord,
records: Vec<DTCSnapshotRecordByDTCNumber>,
},
#[cfg(feature = "std2006")]
ReportDTCSnapshotRecordByRecordNumber {
number: u8,
records: Vec<DTCSnapshotRecordByRecordNumber>,
},
#[cfg(any(feature = "std2013", feature = "std2020"))]
ReportDTCStoredDataByRecordNumber {
records: Vec<ReportDTCStoredDataByRecord>,
},
ReportDTCExtDataRecordByDTCNumber {
status_record: DTCAndStatusRecord,
records: Vec<DTCExtDataRecord>,
},
#[cfg(any(feature = "std2006", feature = "std2013"))]
ReportMirrorMemoryDTCExtDataRecordByDTCNumber {
status_record: DTCAndStatusRecord,
records: Vec<DTCExtDataRecord>,
},
ReportNumberOfDTCBySeverityMaskRecord {
avl_mask: u8,
fid: DTCFormatIdentifier,
count: u16,
},
ReportDTCBySeverityMaskRecord {
avl_mask: u8,
record: DTCAndSeverityRecord1,
others: Vec<DTCAndSeverityRecord1>,
},
ReportSeverityInformationOfDTC {
avl_mask: u8,
records: Vec<DTCAndSeverityRecord1>,
},
ReportSupportedDTC {
avl_mask: u8,
records: Vec<DTCAndStatusRecord>,
},
ReportFirstTestFailedDTC {
avl_mask: u8,
record: Option<DTCAndStatusRecord>,
},
ReportFirstConfirmedDTC {
avl_mask: u8,
record: Option<DTCAndStatusRecord>,
},
ReportMostRecentTestFailedDTC {
avl_mask: u8,
record: Option<DTCAndStatusRecord>,
},
ReportMostRecentConfirmedDTC {
avl_mask: u8,
record: Option<DTCAndStatusRecord>,
},
#[cfg(any(feature = "std2006", feature = "std2013"))]
ReportMirrorMemoryDTCByStatusMask {
avl_mask: u8,
records: Vec<DTCAndStatusRecord>,
},
#[cfg(any(feature = "std2006", feature = "std2013"))]
ReportNumberOfMirrorMemoryDTCByStatusMask {
avl_mask: u8,
fid: DTCFormatIdentifier,
count: u16,
},
#[cfg(any(feature = "std2006", feature = "std2013"))]
ReportNumberOfEmissionsOBDDTCByStatusMask {
avl_mask: u8,
fid: DTCFormatIdentifier,
count: u16,
},
#[cfg(any(feature = "std2006", feature = "std2013"))]
ReportEmissionsOBDDTCByStatusMask {
avl_mask: u8,
records: Vec<DTCAndStatusRecord>,
},
ReportDTCFaultDetectionCounter {
records: Vec<DTCFaultDetectionCounterRecord>,
},
ReportDTCWithPermanentStatus {
avl_mask: u8,
records: Vec<DTCAndStatusRecord>,
},
#[cfg(any(feature = "std2013", feature = "std2020"))]
ReportDTCExtDataRecordByRecordNumber {
number: u8,
records: Vec<DTCExtDataRecordByRecordNumber>, },
#[cfg(any(feature = "std2013", feature = "std2020"))]
ReportUserDefMemoryDTCByStatusMask {
mem_selection: u8,
avl_mask: u8,
records: Vec<DTCAndStatusRecord>,
},
#[cfg(any(feature = "std2013", feature = "std2020"))]
ReportUserDefMemoryDTCSnapshotRecordByDTCNumber {
mem_selection: u8,
status_record: DTCAndStatusRecord,
records: Vec<UserDefDTCSnapshotRecord>,
},
#[cfg(any(feature = "std2013", feature = "std2020"))]
ReportUserDefMemoryDTCExtDataRecordByDTCNumber {
mem_selection: u8,
status_record: DTCAndStatusRecord,
number: Option<u8>, records: Vec<DTCExtDataRecord>,
},
#[cfg(any(feature = "std2020"))]
ReportSupportedDTCExtDataRecord {
avl_mask: u8,
number: Option<u8>, records: Vec<DTCAndStatusRecord>,
},
#[cfg(any(feature = "std2013", feature = "std2020"))]
ReportWWHOBDDTCByMaskRecord {
func_gid: u8, status_avl_mask: u8,
severity_avl_mask: u8,
fid: DTCFormatIdentifier,
records: Vec<DTCAndSeverityRecord>,
},
#[cfg(any(feature = "std2013", feature = "std2020"))]
ReportWWHOBDDTCWithPermanentStatus {
func_gid: u8, status_avl_mask: u8,
fid: DTCFormatIdentifier,
records: Vec<DTCAndStatusRecord>,
},
#[cfg(any(feature = "std2020"))]
ReportDTCInformationByDTCReadinessGroupIdentifier {
func_gid: u8, status_avl_mask: u8,
format_identifier: u8,
readiness_gid: u8, records: Vec<DTCAndStatusRecord>,
},
}
impl From<DTCInfo> for Vec<u8> {
fn from(val: DTCInfo) -> Self {
let mut result = Vec::new();
match val {
DTCInfo::ReportNumberOfDTCByStatusMask {
avl_mask,
fid,
count,
} => {
result.push(avl_mask);
result.push(fid.into());
result.extend(count.to_be_bytes());
}
DTCInfo::ReportDTCByStatusMask { avl_mask, records } => {
result.push(avl_mask);
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCInfo::ReportMirrorMemoryDTCByStatusMask { avl_mask, records } => {
result.push(avl_mask);
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCInfo::ReportNumberOfMirrorMemoryDTCByStatusMask {
avl_mask,
fid,
count,
} => {
result.push(avl_mask);
result.push(fid.into());
result.extend(count.to_be_bytes());
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCInfo::ReportNumberOfEmissionsOBDDTCByStatusMask {
avl_mask,
fid,
count,
} => {
result.push(avl_mask);
result.push(fid.into());
result.extend(count.to_be_bytes());
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCInfo::ReportEmissionsOBDDTCByStatusMask { avl_mask, records } => {
result.push(avl_mask);
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
DTCInfo::ReportDTCSnapshotIdentification { records } => {
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.number);
});
}
DTCInfo::ReportDTCSnapshotRecordByDTCNumber {
status_record,
records,
} => {
result.append(&mut status_record.dtc.into());
result.push(status_record.status);
records.into_iter().for_each(|v| {
result.push(v.number);
result.push(v.number_of_identifier);
v.records.into_iter().for_each(|mut record| {
let did: u16 = record.did.into();
result.extend(did.to_be_bytes());
result.append(&mut record.data);
})
});
}
#[cfg(feature = "std2006")]
DTCInfo::ReportDTCSnapshotRecordByRecordNumber { number, records } => {
result.push(number);
records.into_iter().for_each(|v| {
if let Some(status_record) = v.status_record {
result.append(&mut status_record.dtc.into());
result.push(status_record.status);
}
if let Some(number_of_identifier) = v.number_of_identifier {
result.push(number_of_identifier);
}
v.records.into_iter().for_each(|mut record| {
let did: u16 = record.did.into();
result.extend(did.to_be_bytes());
result.append(&mut record.data);
});
});
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCInfo::ReportDTCStoredDataByRecordNumber { records } => {
records.into_iter().for_each(|v| {
result.push(v.number);
if let Some(record) = v.record {
result.append(&mut record.dtc.into());
result.push(record.status);
}
if let Some(number_of_identifier) = v.number_of_identifier {
result.push(number_of_identifier);
}
v.records.into_iter().for_each(|mut r| {
let did: u16 = r.did.into();
result.extend(did.to_be_bytes());
result.append(&mut r.data);
});
})
}
DTCInfo::ReportDTCExtDataRecordByDTCNumber {
status_record,
records,
} => {
result.append(&mut status_record.dtc.into());
result.push(status_record.status);
records.into_iter().for_each(|mut v| {
result.push(v.number);
result.append(&mut v.data);
});
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCInfo::ReportMirrorMemoryDTCExtDataRecordByDTCNumber {
status_record,
records,
} => {
result.append(&mut status_record.dtc.into());
result.push(status_record.status);
records.into_iter().for_each(|mut v| {
result.push(v.number);
result.append(&mut v.data);
});
}
DTCInfo::ReportNumberOfDTCBySeverityMaskRecord {
avl_mask,
fid,
count,
} => {
result.push(avl_mask);
result.push(fid.into());
result.extend(count.to_be_bytes());
}
DTCInfo::ReportDTCBySeverityMaskRecord {
avl_mask,
record,
others,
} => {
result.push(avl_mask);
result.push(record.severity);
result.push(record.func_unit);
result.append(&mut record.dtc.into());
result.push(record.status);
others.into_iter().for_each(|v| {
result.push(v.severity);
result.push(v.func_unit);
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
DTCInfo::ReportSeverityInformationOfDTC { avl_mask, records } => {
result.push(avl_mask);
records.into_iter().for_each(|v| {
result.push(v.severity);
result.push(v.func_unit);
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
DTCInfo::ReportSupportedDTC { avl_mask, records } => {
result.push(avl_mask);
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
DTCInfo::ReportFirstTestFailedDTC { avl_mask, record }
| DTCInfo::ReportFirstConfirmedDTC { avl_mask, record }
| DTCInfo::ReportMostRecentTestFailedDTC { avl_mask, record }
| DTCInfo::ReportMostRecentConfirmedDTC { avl_mask, record } => {
result.push(avl_mask);
if let Some(v) = record {
result.append(&mut v.dtc.into());
result.push(v.status);
}
}
DTCInfo::ReportDTCFaultDetectionCounter { records } => {
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.counter);
});
}
DTCInfo::ReportDTCWithPermanentStatus { avl_mask, records } => {
result.push(avl_mask);
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCInfo::ReportDTCExtDataRecordByRecordNumber { number, records } => {
result.push(number);
records.into_iter().for_each(|mut v| {
result.append(&mut v.status_record.dtc.into());
result.push(v.status_record.status);
result.append(&mut v.data);
})
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCInfo::ReportUserDefMemoryDTCByStatusMask {
mem_selection,
avl_mask,
records,
} => {
result.push(mem_selection);
result.push(avl_mask);
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCInfo::ReportUserDefMemoryDTCSnapshotRecordByDTCNumber {
mem_selection,
status_record,
records,
} => {
result.push(mem_selection);
result.append(&mut status_record.dtc.into());
result.push(status_record.status);
records.into_iter().for_each(|v| {
result.push(v.number);
result.push(v.number_of_identifier);
v.records.into_iter().for_each(|mut r| {
let did: u16 = r.did.into();
result.extend(did.to_be_bytes());
result.append(&mut r.data);
});
});
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCInfo::ReportUserDefMemoryDTCExtDataRecordByDTCNumber {
mem_selection,
status_record,
number,
records,
} => {
result.push(mem_selection);
result.append(&mut status_record.dtc.into());
result.push(status_record.status);
if let Some(v) = number {
result.push(v);
}
records.into_iter().for_each(|mut v| {
result.push(v.number);
result.append(&mut v.data);
});
}
#[cfg(any(feature = "std2020"))]
DTCInfo::ReportSupportedDTCExtDataRecord {
avl_mask,
number,
records,
} => {
result.push(avl_mask);
if let Some(number) = number {
result.push(number);
}
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCInfo::ReportWWHOBDDTCByMaskRecord {
func_gid, status_avl_mask,
severity_avl_mask,
fid,
records,
} => {
result.push(func_gid);
result.push(status_avl_mask);
result.push(severity_avl_mask);
result.push(fid.into());
records.into_iter().for_each(|v| {
result.push(v.severity);
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCInfo::ReportWWHOBDDTCWithPermanentStatus {
func_gid, status_avl_mask,
fid,
records,
} => {
result.push(func_gid);
result.push(status_avl_mask);
result.push(fid.into());
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
#[cfg(any(feature = "std2020"))]
DTCInfo::ReportDTCInformationByDTCReadinessGroupIdentifier {
func_gid, status_avl_mask,
format_identifier,
readiness_gid, records,
} => {
result.push(func_gid);
result.push(status_avl_mask);
result.push(format_identifier);
result.push(readiness_gid);
records.into_iter().for_each(|v| {
result.append(&mut v.dtc.into());
result.push(v.status);
});
}
}
result
}
}
impl ResponseData for DTCInfo {
fn new_response<T: AsRef<[u8]>>(
data: T,
sub_func: Option<u8>,
cfg: &Configuration,
) -> Result<Response, Error> {
let data = data.as_ref();
match sub_func {
Some(sub_func) => {
let data_len = data.len();
match DTCReportType::try_from(sub_func)? {
DTCReportType::ReportNumberOfDTCByStatusMask => {
utils::data_length_check(data_len, 4, true)?
}
DTCReportType::ReportDTCByStatusMask => {
utils::data_length_check(data_len, 1, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportDTCSnapshotIdentification => {
if (data_len % 4) != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportDTCSnapshotRecordByDTCNumber => {
utils::data_length_check(data_len, 4, false)?
}
#[cfg(feature = "std2006")]
DTCReportType::ReportDTCSnapshotRecordByRecordNumber => {
utils::data_length_check(data_len, 1, false)?;
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportDTCStoredDataByRecordNumber => {
utils::data_length_check(data_len, 1, false)?;
}
DTCReportType::ReportDTCExtDataRecordByDTCNumber => {
utils::data_length_check(data_len, 4, false)?;
if data_len > 4 {
parse_dtc_ext_data_records(data, 4, cfg)?;
}
}
DTCReportType::ReportNumberOfDTCBySeverityMaskRecord => {
utils::data_length_check(data_len, 4, true)?
}
DTCReportType::ReportDTCBySeverityMaskRecord => {
utils::data_length_check(data_len, 7, false)?
}
DTCReportType::ReportSeverityInformationOfDTC => {
utils::data_length_check(data_len, 1, false)?;
if (data_len - 1) % 6 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportSupportedDTC => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportFirstTestFailedDTC => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportFirstConfirmedDTC => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportMostRecentTestFailedDTC => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportMostRecentConfirmedDTC => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportMirrorMemoryDTCByStatusMask => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportMirrorMemoryDTCExtDataRecordByDTCNumber => {
utils::data_length_check(data_len, 4, false)?;
if data_len > 4 {
parse_dtc_ext_data_records(data, 4, cfg)?;
}
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportNumberOfMirrorMemoryDTCByStatusMask => {
utils::data_length_check(data_len, 4, true)?
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportNumberOfEmissionsOBDDTCByStatusMask => {
utils::data_length_check(data_len, 4, true)?
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportEmissionsOBDDTCByStatusMask => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportDTCFaultDetectionCounter => {
utils::data_length_check(data_len, 4, false)?;
if data_len % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
DTCReportType::ReportDTCWithPermanentStatus => {
utils::data_length_check(data_len, 5, false)?;
if (data_len - 1) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportDTCExtDataRecordByRecordNumber => {
utils::data_length_check(data_len, 1, false)?;
let number = data[0];
if number > 0xEF {
return Err(Error::InvalidData(hex::encode(data)));
}
if data_len > 1 {
let ext_data_len = dtc_ext_data_length(cfg, number)?;
let record_len = 4 + ext_data_len;
utils::data_length_check(data_len, 1 + record_len, false)?;
if (data_len - 1) % record_len != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportUserDefMemoryDTCByStatusMask => {
utils::data_length_check(data_len, 2, false)?;
if (data_len - 2) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportUserDefMemoryDTCSnapshotRecordByDTCNumber => {
utils::data_length_check(data_len, 5, false)?;
if data_len > 5 {
utils::data_length_check(data_len, 7, false)?;
}
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportUserDefMemoryDTCExtDataRecordByDTCNumber => {
utils::data_length_check(data_len, 5, false)?;
if data_len > 5 {
let number = data[5];
if number > 0xFE {
return Err(Error::InvalidData(hex::encode(data)));
}
if data_len > 6 {
parse_dtc_ext_data_records(data, 6, cfg)?;
}
}
}
#[cfg(any(feature = "std2020"))]
DTCReportType::ReportSupportedDTCExtDataRecord => {
utils::data_length_check(data_len, 1, false)?;
if data_len > 1 {
let number = data[1];
if !(0x01..=0xFD).contains(&number) {
return Err(Error::InvalidData(hex::encode(data)));
}
if data_len > 2 && (data_len - 2) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportWWHOBDDTCByMaskRecord => {
utils::data_length_check(data_len, 4, false)?;
if (data_len - 4) % 5 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportWWHOBDDTCWithPermanentStatus => {
utils::data_length_check(data_len, 3, false)?;
if (data_len - 3) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
#[cfg(any(feature = "std2020"))]
DTCReportType::ReportDTCInformationByDTCReadinessGroupIdentifier => {
utils::data_length_check(data_len, 4, false)?;
if (data_len - 4) % 4 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
}
}
Ok(Response {
service: Service::ReadDTCInfo,
negative: false,
sub_func: Some(SubFunction::new(sub_func)),
data: data.to_vec(),
})
}
None => Err(Error::SubFunctionError(Service::ReadDTCInfo)),
}
}
}
impl TryFrom<(&Response, &Configuration)> for DTCInfo {
type Error = Error;
fn try_from((resp, cfg): (&Response, &Configuration)) -> Result<Self, Self::Error> {
let service = resp.service();
if service != Service::ReadDTCInfo || resp.sub_func.is_none() {
return Err(Error::ServiceError(service));
}
let sub_func: DTCReportType = resp.sub_function().unwrap().function()?;
let data = &resp.data;
let data_len = data.len();
let mut offset = 0;
match sub_func {
DTCReportType::ReportNumberOfDTCByStatusMask => {
let avl_mask = data[offset];
offset += 1;
let fid = DTCFormatIdentifier::try_from(data[offset])?;
offset += 1;
let count = u16::from_be_bytes([data[offset], data[offset + 1]]);
Ok(Self::ReportNumberOfDTCByStatusMask {
avl_mask,
fid,
count,
})
}
DTCReportType::ReportDTCByStatusMask => {
let avl_mask = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportDTCByStatusMask { avl_mask, records })
}
DTCReportType::ReportDTCSnapshotIdentification => {
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let number = data[offset];
offset += 1;
records.push(DTCSnapshotIdentification { dtc, number });
}
Ok(Self::ReportDTCSnapshotIdentification { records })
}
DTCReportType::ReportDTCSnapshotRecordByDTCNumber => {
let dtc =
utils::U24::from_be_bytes([data[offset], data[offset + 1], data[offset + 2]]);
offset += 3;
let status = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
utils::data_length_check(data_len, offset + 2, false)?;
let number = data[offset];
offset += 1;
let number_of_identifier = data[offset];
offset += 1;
let mut sub_records = Vec::new();
while sub_records.len() < number_of_identifier as usize {
utils::data_length_check(data_len, offset + 2, false)?;
let did = DataIdentifier::from(u16::from_be_bytes([
data[offset],
data[offset + 1],
]));
offset += 2;
let &did_data_len =
&cfg.did.get(&did).ok_or(Error::DidNotSupported(did))?;
utils::data_length_check(data_len, offset + did_data_len, false)?;
sub_records.push(DTCSnapshotRecord {
did,
data: data[offset..offset + did_data_len].to_vec(),
});
offset += did_data_len;
}
records.push(DTCSnapshotRecordByDTCNumber {
number,
number_of_identifier,
records: sub_records,
});
}
Ok(Self::ReportDTCSnapshotRecordByDTCNumber {
status_record: DTCAndStatusRecord { dtc, status },
records,
})
}
#[cfg(feature = "std2006")]
DTCReportType::ReportDTCSnapshotRecordByRecordNumber => {
let number = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
utils::data_length_check(data_len, offset + 5, false)?;
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
let number_of_identifier = data[offset];
offset += 1;
let mut snapshot_records = Vec::new();
while snapshot_records.len() < number_of_identifier as usize {
utils::data_length_check(data_len, offset + 2, false)?;
let did = DataIdentifier::from(u16::from_be_bytes([
data[offset],
data[offset + 1],
]));
offset += 2;
let &did_data_len = cfg.did.get(&did).ok_or(Error::DidNotSupported(did))?;
utils::data_length_check(data_len, offset + did_data_len, false)?;
snapshot_records.push(DTCSnapshotRecord {
did,
data: data[offset..offset + did_data_len].to_vec(),
});
offset += did_data_len;
}
records.push(DTCSnapshotRecordByRecordNumber {
status_record: Some(DTCAndStatusRecord { dtc, status }),
number_of_identifier: Some(number_of_identifier),
records: snapshot_records,
});
}
if records.is_empty() {
records.push(DTCSnapshotRecordByRecordNumber {
status_record: None,
number_of_identifier: None,
records: vec![],
});
}
Ok(Self::ReportDTCSnapshotRecordByRecordNumber { number, records })
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportDTCStoredDataByRecordNumber => {
let mut records = Vec::new();
while data_len > offset {
let number = data[offset];
offset += 1;
let mut record = None;
let mut number_of_identifier = None;
let mut stored_records = Vec::new();
if data_len > offset {
utils::data_length_check(data_len, offset + 4, false)?;
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
record = Some(DTCAndStatusRecord { dtc, status });
if data_len > offset {
let did_count = data[offset];
offset += 1;
number_of_identifier = Some(did_count);
for _ in 0..did_count {
utils::data_length_check(data_len, offset + 2, false)?;
let did = DataIdentifier::from(u16::from_be_bytes([
data[offset],
data[offset + 1],
]));
offset += 2;
let &did_data_len =
&cfg.did.get(&did).ok_or(Error::DidNotSupported(did))?;
utils::data_length_check(data_len, offset + did_data_len, false)?;
stored_records.push(DTCStoredDataRecord {
did,
data: data[offset..offset + did_data_len].to_vec(),
});
offset += did_data_len;
}
}
}
records.push(ReportDTCStoredDataByRecord {
number,
record,
number_of_identifier,
records: stored_records,
});
}
Ok(Self::ReportDTCStoredDataByRecordNumber { records })
}
DTCReportType::ReportDTCExtDataRecordByDTCNumber => {
let dtc =
utils::U24::from_be_bytes([data[offset], data[offset + 1], data[offset + 2]]);
offset += 3;
let status = data[offset];
offset += 1;
let mut records = Vec::new();
if data_len > offset {
records = parse_dtc_ext_data_records(data, offset, cfg)?;
}
Ok(Self::ReportDTCExtDataRecordByDTCNumber {
status_record: DTCAndStatusRecord { dtc, status },
records,
})
}
DTCReportType::ReportNumberOfDTCBySeverityMaskRecord => {
let avl_mask = data[offset];
offset += 1;
let fid = DTCFormatIdentifier::try_from(data[offset])?;
offset += 1;
let count = u16::from_be_bytes([data[offset], data[offset + 1]]);
Ok(Self::ReportNumberOfDTCBySeverityMaskRecord {
avl_mask,
fid,
count,
})
}
DTCReportType::ReportDTCBySeverityMaskRecord => {
let avl_mask = data[offset];
offset += 1;
if (data_len - offset) % 6 != 0 {
return Err(Error::InvalidData(hex::encode(data)));
}
let severity = data[offset];
offset += 1;
let func_unit = data[offset];
offset += 1;
let dtc =
utils::U24::from_be_bytes([data[offset], data[offset + 1], data[offset + 2]]);
offset += 3;
let status = data[offset];
offset += 1;
let mut others = Vec::new();
while data_len > offset {
let severity = data[offset];
offset += 1;
let func_unit = data[offset];
offset += 1;
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
others.push(DTCAndSeverityRecord1 {
severity,
func_unit,
dtc,
status,
})
}
Ok(Self::ReportDTCBySeverityMaskRecord {
avl_mask,
record: DTCAndSeverityRecord1 {
severity,
func_unit,
dtc,
status,
},
others,
})
}
DTCReportType::ReportSeverityInformationOfDTC => {
let avl_mask = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let severity = data[offset];
offset += 1;
let func_unit = data[offset];
offset += 1;
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndSeverityRecord1 {
severity,
func_unit,
dtc,
status,
})
}
Ok(Self::ReportSeverityInformationOfDTC { avl_mask, records })
}
DTCReportType::ReportSupportedDTC => {
let avl_mask = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportSupportedDTC { avl_mask, records })
}
DTCReportType::ReportFirstTestFailedDTC => {
let avl_mask = data[offset];
offset += 1;
let mut record = None;
if data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
record = Some(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportFirstTestFailedDTC { avl_mask, record })
}
DTCReportType::ReportFirstConfirmedDTC => {
let avl_mask = data[offset];
offset += 1;
let mut record = None;
if data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
record = Some(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportFirstConfirmedDTC { avl_mask, record })
}
DTCReportType::ReportMostRecentTestFailedDTC => {
let avl_mask = data[offset];
offset += 1;
let mut record = None;
if data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
record = Some(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportMostRecentTestFailedDTC { avl_mask, record })
}
DTCReportType::ReportMostRecentConfirmedDTC => {
let avl_mask = data[offset];
offset += 1;
let mut record = None;
if data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
record = Some(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportMostRecentConfirmedDTC { avl_mask, record })
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportMirrorMemoryDTCByStatusMask => {
let avl_mask = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportMirrorMemoryDTCByStatusMask { avl_mask, records })
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportMirrorMemoryDTCExtDataRecordByDTCNumber => {
let dtc =
utils::U24::from_be_bytes([data[offset], data[offset + 1], data[offset + 2]]);
offset += 3;
let status = data[offset];
offset += 1;
let mut records = Vec::new();
if data_len > offset {
records = parse_dtc_ext_data_records(data, offset, cfg)?;
}
Ok(Self::ReportMirrorMemoryDTCExtDataRecordByDTCNumber {
status_record: DTCAndStatusRecord { dtc, status },
records,
})
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportNumberOfMirrorMemoryDTCByStatusMask => {
let avl_mask = data[offset];
offset += 1;
let fid = DTCFormatIdentifier::try_from(data[offset])?;
offset += 1;
match fid {
DTCFormatIdentifier::SAE_J2012_DA_DTCFormat_00 => {}
DTCFormatIdentifier::ISO_14229_1_DTCFormat => {}
DTCFormatIdentifier::SAE_J1939_73_DTCFormat => {}
DTCFormatIdentifier::ISO_11992_4_DTCFormat => {}
DTCFormatIdentifier::SAE_J2012_DA_DTCFormat_04 => {
return Err(Error::InvalidData(hex::encode(data)))
}
}
let count = u16::from_be_bytes([data[offset], data[offset + 1]]);
Ok(Self::ReportNumberOfMirrorMemoryDTCByStatusMask {
avl_mask,
fid,
count,
})
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportNumberOfEmissionsOBDDTCByStatusMask => {
let avl_mask = data[offset];
offset += 1;
let fid = DTCFormatIdentifier::try_from(data[offset])?;
offset += 1;
match fid {
DTCFormatIdentifier::SAE_J2012_DA_DTCFormat_00 => {}
DTCFormatIdentifier::ISO_14229_1_DTCFormat => {}
DTCFormatIdentifier::SAE_J1939_73_DTCFormat => {}
DTCFormatIdentifier::ISO_11992_4_DTCFormat => {}
DTCFormatIdentifier::SAE_J2012_DA_DTCFormat_04 => {
return Err(Error::InvalidData(hex::encode(data)))
}
}
let count = u16::from_be_bytes([data[offset], data[offset + 1]]);
Ok(Self::ReportNumberOfEmissionsOBDDTCByStatusMask {
avl_mask,
fid,
count,
})
}
#[cfg(any(feature = "std2006", feature = "std2013"))]
DTCReportType::ReportEmissionsOBDDTCByStatusMask => {
let avl_mask = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportEmissionsOBDDTCByStatusMask { avl_mask, records })
}
DTCReportType::ReportDTCFaultDetectionCounter => {
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let counter = data[offset];
offset += 1;
records.push(DTCFaultDetectionCounterRecord { dtc, counter });
}
Ok(Self::ReportDTCFaultDetectionCounter { records })
}
DTCReportType::ReportDTCWithPermanentStatus => {
let avl_mask = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportDTCWithPermanentStatus { avl_mask, records })
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportDTCExtDataRecordByRecordNumber => {
let number = data[offset];
offset += 1;
if number > 0xEF {
return Err(Error::InvalidData(hex::encode(data)));
}
let mut records = Vec::new();
if data_len > offset {
let ext_data_len = dtc_ext_data_length(cfg, number)?;
while data_len > offset {
utils::data_length_check(data_len, offset + 4 + ext_data_len, false)?;
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCExtDataRecordByRecordNumber {
status_record: DTCAndStatusRecord { dtc, status },
data: data[offset..offset + ext_data_len].to_vec(),
});
offset += ext_data_len;
}
}
Ok(Self::ReportDTCExtDataRecordByRecordNumber { number, records })
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportUserDefMemoryDTCByStatusMask => {
let mem_selection = data[offset];
offset += 1;
let avl_mask = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status })
}
Ok(Self::ReportUserDefMemoryDTCByStatusMask {
mem_selection,
avl_mask,
records,
})
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportUserDefMemoryDTCSnapshotRecordByDTCNumber => {
let mem_selection = data[offset];
offset += 1;
let dtc =
utils::U24::from_be_bytes([data[offset], data[offset + 1], data[offset + 2]]);
offset += 3;
let status = data[offset];
offset += 1;
let mut records = Vec::new();
while data_len > offset {
let number = data[offset];
offset += 1;
let number_of_identifier = data[offset];
offset += 1;
let mut sub_records = Vec::new();
while sub_records.len() < number_of_identifier as usize {
utils::data_length_check(data_len, offset + 2, false)?;
let did = DataIdentifier::from(u16::from_be_bytes([
data[offset],
data[offset + 1],
]));
offset += 2;
let &did_data_len =
&cfg.did.get(&did).ok_or(Error::DidNotSupported(did))?;
utils::data_length_check(data_len, offset + did_data_len, false)?;
sub_records.push(DTCSnapshotRecord {
did,
data: data[offset..offset + did_data_len].to_vec(),
});
offset += did_data_len;
}
records.push(UserDefDTCSnapshotRecord {
number,
number_of_identifier,
records: sub_records,
});
}
Ok(Self::ReportUserDefMemoryDTCSnapshotRecordByDTCNumber {
mem_selection,
status_record: DTCAndStatusRecord { dtc, status },
records,
})
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportUserDefMemoryDTCExtDataRecordByDTCNumber => {
let mem_selection = data[offset];
offset += 1;
let dtc =
utils::U24::from_be_bytes([data[offset], data[offset + 1], data[offset + 2]]);
offset += 3;
let status = data[offset];
offset += 1;
let number = if data_len > offset {
let number = data[offset];
offset += 1;
if number > 0xFE {
return Err(Error::InvalidData(hex::encode(data)));
}
Some(number)
} else {
None
};
let mut records = Vec::new();
if number.is_some() && data_len > offset {
records = parse_dtc_ext_data_records(data, offset, cfg)?;
}
Ok(Self::ReportUserDefMemoryDTCExtDataRecordByDTCNumber {
mem_selection,
status_record: DTCAndStatusRecord { dtc, status },
number,
records,
})
}
#[cfg(any(feature = "std2020"))]
DTCReportType::ReportSupportedDTCExtDataRecord => {
let avl_mask = data[offset];
offset += 1;
let number = if data_len > offset {
let number = data[offset];
offset += 1;
if !(0x01..=0xFD).contains(&number) {
return Err(Error::InvalidData(hex::encode(data)));
}
Some(number)
} else {
None
};
let mut records = Vec::new();
while data_len > offset {
utils::data_length_check(data_len, offset + 4, false)?;
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status })
}
Ok(Self::ReportSupportedDTCExtDataRecord {
avl_mask,
number,
records,
})
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportWWHOBDDTCByMaskRecord => {
let func_gid = data[offset];
offset += 1;
let status_avl_mask = data[offset];
offset += 1;
let severity_avl_mask = data[offset];
offset += 1;
let fid = DTCFormatIdentifier::try_from(data[offset])?;
offset += 1;
match fid {
DTCFormatIdentifier::SAE_J1939_73_DTCFormat => {}
DTCFormatIdentifier::SAE_J2012_DA_DTCFormat_04 => {}
_ => return Err(Error::InvalidData(hex::encode(data))),
}
let mut records = Vec::new();
while data_len > offset {
let severity = data[offset];
offset += 1;
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndSeverityRecord {
severity,
dtc,
status,
});
}
Ok(Self::ReportWWHOBDDTCByMaskRecord {
func_gid,
status_avl_mask,
severity_avl_mask,
fid,
records,
})
}
#[cfg(any(feature = "std2013", feature = "std2020"))]
DTCReportType::ReportWWHOBDDTCWithPermanentStatus => {
utils::data_length_check(data_len, offset + 3, false)?;
let func_gid = data[offset];
offset += 1;
let status_avl_mask = data[offset];
offset += 1;
let fid = DTCFormatIdentifier::try_from(data[offset])?;
offset += 1;
match fid {
DTCFormatIdentifier::SAE_J1939_73_DTCFormat => {}
DTCFormatIdentifier::SAE_J2012_DA_DTCFormat_04 => {}
_ => return Err(Error::InvalidData(hex::encode(data))),
}
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportWWHOBDDTCWithPermanentStatus {
func_gid,
status_avl_mask,
fid,
records,
})
}
#[cfg(any(feature = "std2020"))]
DTCReportType::ReportDTCInformationByDTCReadinessGroupIdentifier => {
let func_gid = data[offset];
offset += 1;
if func_gid > 0xFE {
return Err(Error::InvalidData(hex::encode(data)));
}
let status_avl_mask = data[offset];
offset += 1;
let format_identifier = data[offset];
offset += 1;
let readiness_gid = data[offset];
offset += 1;
if readiness_gid > 0xFE {
return Err(Error::InvalidData(hex::encode(data)));
}
let mut records = Vec::new();
while data_len > offset {
let dtc = utils::U24::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
]);
offset += 3;
let status = data[offset];
offset += 1;
records.push(DTCAndStatusRecord { dtc, status });
}
Ok(Self::ReportDTCInformationByDTCReadinessGroupIdentifier {
func_gid,
status_avl_mask,
format_identifier,
readiness_gid,
records,
})
}
}
}
}