use crate::{
bcd_decode, bcd_decode_slice, dynamic_diag::DynamicDiagSession, DiagError, DiagServerResult,
};
use automotive_diag::kwp2000::KwpCommand;
#[cfg(feature="serde")]
use serde::{Serialize, Deserialize};
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DiagnosticInfo([u8; 2]);
impl DiagnosticInfo {
pub fn is_production_ecu(&self) -> bool {
self.0[0] & 0b10000000 == 0
}
pub fn get_daimler_mmc_ecu_id(&self) -> u8 {
self.0[0] & 0b01111111
}
pub fn is_boot_sw(&self) -> bool {
self.0[1] >= 0xE0
}
pub fn get_info_id(&self) -> u16 {
(self.0[0] as u16) << 8 | self.0[1] as u16
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DaimlerEcuIdent {
pub part_number: String,
pub ecu_hw_build_week: u8,
pub ecu_hw_build_year: u8,
pub ecu_sw_build_week: u8,
pub ecu_sw_build_year: u8,
pub supplier: u8,
pub diag_info: DiagnosticInfo,
pub ecu_production_year: u8,
pub ecu_production_month: u8,
pub ecu_production_day: u8,
}
impl DaimlerEcuIdent {
pub fn get_production_date_pretty(&self) -> String {
format!(
"{}/{}/{}",
bcd_decode(self.ecu_production_day),
bcd_decode(self.ecu_production_month),
bcd_decode(self.ecu_production_year)
)
}
pub fn get_software_date_pretty(&self) -> String {
format!(
"{}/{}",
bcd_decode(self.ecu_sw_build_week),
bcd_decode(self.ecu_sw_build_year)
)
}
pub fn get_hardware_date_pretty(&self) -> String {
format!(
"{}/{}",
bcd_decode(self.ecu_hw_build_week),
bcd_decode(self.ecu_hw_build_year)
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DaimlerMmcEcuIdent {
pub ecu_origin: u8,
pub supplier: u8,
pub diag_info: DiagnosticInfo,
pub hw_version: String,
pub sw_version: String,
pub part_number: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModuleInformation {
pub active_logical_blocks: u8,
pub module_info: Vec<ModuleBlockInformation>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModuleBlockInformation {
pub tool_supplier_id: u8,
pub programming_date_year: u8,
pub programming_date_month: u8,
pub programming_date_day: u8,
pub tester_serial_number: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SoftwareBlockIdentification {
pub origin: u8,
pub blocks: Vec<BlockIdentification>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BlockIdentification {
pub supplier_id: u8,
pub diag_info: DiagnosticInfo,
pub sw_version: String,
pub part_number: String,
}
fn decode_module_info(res: &mut Vec<u8>) -> DiagServerResult<ModuleInformation> {
let active_logical_blocks = res[3];
res.drain(0..4);
let mut list_of_blocks: Vec<ModuleBlockInformation> = Vec::new();
if res.len() % 8 != 0 {
return Err(DiagError::InvalidResponseLength);
}
for i in (0..res.len()).step_by(8) {
list_of_blocks.push(ModuleBlockInformation {
tool_supplier_id: res[i],
programming_date_year: bcd_decode(res[i + 1]).parse::<u8>().unwrap_or(0),
programming_date_month: bcd_decode(res[i + 2]).parse::<u8>().unwrap_or(0),
programming_date_day: bcd_decode(res[i + 3]).parse::<u8>().unwrap_or(0),
tester_serial_number: format!(
"{:02X}{:02X}{:02X}{:02X}",
res[i + 4],
res[i + 5],
res[i + 6],
res[i + 7]
),
})
}
Ok(ModuleInformation {
active_logical_blocks,
module_info: list_of_blocks,
})
}
fn decode_block_ident(res: &mut Vec<u8>) -> DiagServerResult<SoftwareBlockIdentification> {
let origin = res[3];
res.drain(0..4);
if res.len() % 17 != 0 {
return Err(DiagError::InvalidResponseLength);
}
let mut blocks: Vec<BlockIdentification> = Vec::new();
for x in (0..res.len()).step_by(17) {
blocks.push(BlockIdentification {
supplier_id: res[x],
diag_info: DiagnosticInfo([res[x + 1], res[x + 2]]),
sw_version: bcd_decode_slice(&res[x + 4..x + 7], Some(".")),
part_number: bcd_decode_slice(&res[x + 8..x + 17], None),
})
}
Ok(SoftwareBlockIdentification { origin, blocks })
}
impl DynamicDiagSession {
pub fn kwp_read_daimler_identification(&self) -> DiagServerResult<DaimlerEcuIdent> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x86])?;
if res.len() != 18 {
return Err(DiagError::InvalidResponseLength);
}
Ok(DaimlerEcuIdent {
part_number: bcd_decode_slice(&res[2..=6], None),
ecu_hw_build_week: res[7],
ecu_hw_build_year: res[8],
ecu_sw_build_week: res[9],
ecu_sw_build_year: res[10],
supplier: res[11],
diag_info: DiagnosticInfo([res[12], res[13]]),
ecu_production_year: res[15],
ecu_production_month: res[16],
ecu_production_day: res[17],
})
}
pub fn kwp_read_daimler_mmc_identification(&self) -> DiagServerResult<DaimlerMmcEcuIdent> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x87])?;
if res.len() != 22 {
return Err(DiagError::InvalidResponseLength);
}
Ok(DaimlerMmcEcuIdent {
ecu_origin: res[2],
supplier: res[3],
diag_info: DiagnosticInfo([res[4], res[5]]),
hw_version: bcd_decode_slice(&res[7..=8], Some(".")),
sw_version: bcd_decode_slice(&res[9..=11], Some(".")),
part_number: String::from_utf8_lossy(&res[12..]).to_string(),
})
}
pub fn kwp_read_original_vin(&self) -> DiagServerResult<String> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x88])?;
Ok(String::from_utf8_lossy(&res[2..]).to_string())
}
pub fn kwp_read_diagnostic_variant_code(&self) -> DiagServerResult<u32> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x89])?;
if res.len() != 6 {
return Err(DiagError::InvalidResponseLength);
}
Ok((res[2] as u32) << 24 | (res[3] as u32) << 16 | (res[4] as u32) << 8 | res[5] as u32)
}
pub fn kwp_read_current_vin(&self) -> DiagServerResult<String> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x90])?;
Ok(String::from_utf8_lossy(&res[2..]).to_string())
}
pub fn kwp_read_calibration_id(&self) -> DiagServerResult<String> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x96])?;
Ok(String::from_utf8_lossy(&res[2..]).to_string())
}
pub fn kwp_read_cvn(&self) -> DiagServerResult<[u8; 4]> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x97])?;
if res.len() != 6 {
return Err(DiagError::InvalidResponseLength);
}
Ok([res[2], res[3], res[4], res[5]])
}
pub fn kwp_read_ecu_code_fingerprint(&self) -> DiagServerResult<ModuleInformation> {
let mut res =
self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x9A])?;
decode_module_info(&mut res)
}
pub fn kwp_read_ecu_data_fingerprint(&self) -> DiagServerResult<ModuleInformation> {
let mut res =
self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x9B])?;
decode_module_info(&mut res)
}
pub fn kwp_read_ecu_code_software_id(&self) -> DiagServerResult<SoftwareBlockIdentification> {
let mut res =
self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x9C])?;
decode_block_ident(&mut res)
}
pub fn kwp_read_ecu_data_software_id(&self) -> DiagServerResult<SoftwareBlockIdentification> {
let mut res =
self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x9D])?;
decode_block_ident(&mut res)
}
pub fn kwp_read_ecu_boot_software_id(&self) -> DiagServerResult<SoftwareBlockIdentification> {
let mut res =
self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x9E])?;
decode_block_ident(&mut res)
}
pub fn kwp_read_ecu_boot_fingerprint(&self) -> DiagServerResult<ModuleInformation> {
let mut res =
self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0x9F])?;
decode_module_info(&mut res)
}
}