1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
//! Read data by Local identifier
use crate::{dynamic_diag::DynamicDiagSession, DiagError, DiagServerResult};
use automotive_diag::kwp2000::KwpCommand;
#[cfg(feature="serde")]
use serde::{Serialize, Deserialize};
/// Development data of the ECU. Used by [super::Kwp2000DiagnosticServer::read_ecu_development_data]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DevelopmentData {
/// ECU Processor type
pub processor_type: u16,
/// Database communication matrix version. Formatted as XX.YY
pub db_comm_matrix_version: String,
/// CAN Driver version. Formatted as XX.YY
pub can_driver_version: String,
/// NM version. Formatted as XX.YY
pub nm_version: String,
/// KWP2000 Module version. Formatted as XX.YY
pub kwp2000_version: String,
/// Transport layer version. Formatted as XX.YY
pub transport_layer_version: String,
/// Database communication version. Formatted as XX.YY
pub db_comm_version: String,
/// Flexer version. Formatted as XX.YY.
/// If the ECU does not support this data field, `99.99` will be stored
pub flexer_version: String,
}
/// ECU DBCom data. Used by [super::Kwp2000DiagnosticServer::read_ecu_dbcom_data]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DBComData {
/// Memory address (Flash)
pub memory_address_flash: u32,
/// Flash data format identifier
pub data_format_flash: u8,
/// Uncompressed memory size of Flash
pub uncompressed_memory_size_flash: u32,
/// Memory address (RAM)
pub memory_address_ram: u32,
/// Uncompressed memory size of RAM
pub uncompressed_memory_size_ram: u32,
/// Memory address (EEPROM)
pub memory_address_eeprom: u32,
/// Uncompressed memory size of EEPROM
pub uncompressed_memory_size_eeprom: u32,
}
/// Vehicle identification. Used by [super::Kwp2000DiagnosticServer::read_ecu_vehicle_info]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct VehicleInfo {
/// Vehicle model year
pub model_year: u8,
/// Vehicle code
pub vehicle_code: u8,
/// Vehicle body style code
pub body_style: u8,
/// Vehicle country code
pub country_code: u8,
}
/// SDCOM information. Used by [super::Kwp2000DiagnosticServer::read_system_diag_general_param_data]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DiagGeneralParamData {
/// Indicates if global process data exists
pub global_process_data_exists: bool,
/// Internal communication mode
pub internal_communication_mode: u8,
/// SDCOM-SW module version. Formatted as XX.YY
pub sdcom_version: String,
/// Year of SDCOM build date
pub sdcom_build_date_year: u16,
/// Month of SDCOM build date
pub sdcom_build_date_month: u8,
/// Day of SDCOM build date
pub sdcom_build_date_day: u8,
/// Version of SDCOM configuration database
pub sdcom_config_nr: u16,
/// Version reference number of SDCOM configuration database
pub sdcom_reference_nr: u16,
/// Checksum
pub checksum: u32,
}
/// Global diagnostic parameter data. Used by [super::Kwp2000DiagnosticServer::read_system_diag_global_param_data]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DiagGlobalParamData {
/// Number of analog modules active on the ECU
pub number_of_global_analog_values: u8,
/// Number of global states
pub number_of_global_states: u8,
/// First position within diagnostic CAN Frame
pub position_in_can_data_frame: u16,
/// List of process data
pub process_data: Vec<GlobalProcessData>,
}
/// Global process data
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GlobalProcessData {
/// Unique data ID for the global process data
pub data_id: u16,
/// Timebase of global process data
pub timebase: u16,
/// Size of global process data
pub size: u8,
}
/// Diagnostic protocol information. Used by [Kwp2000DiagnosticServer::read_diag_protocol_info]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DiagProtocolInfo {
/// Implemented version of KWP2000 specification
pub kwp2000_requirement_definition: u8,
/// Implemented version of the flash reprogramming specification
pub flash_reprogramming_definition_version: u8,
/// KWP2000 maximum diagnostic level supported
pub diagnostic_level_support: u8,
}
impl DynamicDiagSession {
/// Reads development data from the ECU. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_ecu_development_data(&self) -> DiagServerResult<DevelopmentData> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE0])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads the ECU Serial number.
///
/// This function returns the bytes of just the serial number of the ECU, which
/// can be interpreted as either ASCII (Daimler ECUs), or Model line specification (Varies from OEM to OEM)
pub fn kwp_read_ecu_serial_number(&self) -> DiagServerResult<Vec<u8>> {
let mut res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE1])?;
res.drain(0..2);
Ok(res)
}
/// Reads DBCom data from the ECU. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_ecu_dbcom_data(&self) -> DiagServerResult<DBComData> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE2])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads the Operating system version on the ECU. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_ecu_os_version(&self) -> DiagServerResult<String> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE3])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads reprogramming fault report. The format is binary.
/// This is to be interpreted by GSP/SDE.
pub fn kwp_read_ecu_reprogramming_fault_report(&self) -> DiagServerResult<Vec<u8>> {
let mut res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE4])?;
res.drain(0..2);
Ok(res)
}
/// Reads vehicle information from the ECU. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_ecu_vehicle_info(&self) -> DiagServerResult<VehicleInfo> {
let res = self.send_command_with_response(KwpCommand::ReadECUIdentification, &[0xE5])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads flash data from block 1. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_ecu_flash_info_1(&self) -> DiagServerResult<Vec<u8>> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE6])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads flash data from block 2. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_ecu_flash_info_2(&self) -> DiagServerResult<Vec<u8>> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE7])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads general diagnostic parameter data from the ECU (SDCOM). NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_system_diag_general_param_data(
&self,
) -> DiagServerResult<DiagGeneralParamData> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE8])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads global diagnostic parameter data from the ECU. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_system_diag_global_param_data(&self) -> DiagServerResult<DiagGlobalParamData> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xE9])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads the ECU's current configuration status. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_ecu_configuration(&self) -> DiagServerResult<Vec<u8>> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xEA])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads ECU protocol information. NOT IMPLEMENTED YET (Will return [DiagError::NotImplemented])
pub fn kwp_read_diag_protocol_info(&self) -> DiagServerResult<DiagProtocolInfo> {
let res =
self.send_command_with_response(KwpCommand::ReadDataByLocalIdentifier, &[0xEB])?;
Err(DiagError::NotImplemented(format!(
"ECU Response: {res:02X?}"
)))
}
/// Reads data from a custom local identifier
///
/// ## Supported local identifier ranges
/// * 0x01-0x7F - Record local identifier
/// * 0xA0-0xDF - Record local identifier
/// * 0xF0-0xF9 - Dynamically defined local identifier
///
/// ## Important notes:
/// 1. If the ECU supports commands for identification purposes, then asking for an identifier in the range of 0x86-0x9F will
/// return ECU ident data.
pub fn kwp_read_custom_local_identifier(
&self,
local_identifier: u8,
) -> DiagServerResult<Vec<u8>> {
let mut res = self.send_command_with_response(
KwpCommand::ReadDataByLocalIdentifier,
&[local_identifier],
)?;
// Now check identifier in response message was same as our request identifier, if so, strip it
// from the response message
if res.len() < 2 {
// Require Positive SID, IDENT
return Err(DiagError::InvalidResponseLength);
}
if res[1] != local_identifier {
return Err(DiagError::MismatchedIdentResponse{ want: local_identifier as _, received: res[1] as _ });
}
res.drain(0..2);
Ok(res)
}
}