1#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Dtc {
6 pub code: String,
7 pub category: DtcCategory,
8 pub status: DtcStatus,
9 pub description: Option<String>,
10 pub severity: Option<Severity>,
11 pub source_module: Option<String>,
12 pub notes: Option<String>,
13}
14
15impl Default for Dtc {
16 fn default() -> Self {
17 Self {
18 code: String::new(),
19 category: DtcCategory::Powertrain,
20 status: DtcStatus::Stored,
21 description: None,
22 severity: None,
23 source_module: None,
24 notes: None,
25 }
26 }
27}
28
29impl Dtc {
30 pub fn from_bytes(a: u8, b: u8) -> Self {
33 let category = match (a >> 6) & 0x03 {
34 0 => DtcCategory::Powertrain,
35 1 => DtcCategory::Chassis,
36 2 => DtcCategory::Body,
37 _ => DtcCategory::Network,
38 };
39 let prefix = match category {
40 DtcCategory::Powertrain => 'P',
41 DtcCategory::Chassis => 'C',
42 DtcCategory::Body => 'B',
43 DtcCategory::Network => 'U',
44 };
45 let d2 = (a >> 4) & 0x03;
46 let d3 = a & 0x0F;
47 let d4 = (b >> 4) & 0x0F;
48 let d5 = b & 0x0F;
49 let code = format!("{}{}{:X}{:X}{:X}", prefix, d2, d3, d4, d5);
50
51 let description = universal_dtc_description(&code).map(String::from);
52
53 Self {
54 code,
55 category,
56 status: DtcStatus::Stored,
57 description,
58 severity: None,
59 source_module: None,
60 notes: None,
61 }
62 }
63
64 pub fn from_code(code: &str) -> Self {
66 let category = match code.chars().next() {
67 Some('P') | Some('p') => DtcCategory::Powertrain,
68 Some('C') | Some('c') => DtcCategory::Chassis,
69 Some('B') | Some('b') => DtcCategory::Body,
70 Some('U') | Some('u') => DtcCategory::Network,
71 _ => DtcCategory::Powertrain,
72 };
73 let upper = code.to_uppercase();
74 let description = universal_dtc_description(&upper).map(String::from);
75 Self {
76 code: upper,
77 category,
78 status: DtcStatus::Stored,
79 description,
80 severity: None,
81 source_module: None,
82 notes: None,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89pub enum DtcCategory {
90 Powertrain,
91 Chassis,
92 Body,
93 Network,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum DtcStatus {
99 Stored,
100 Pending,
101 Permanent,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)]
106pub enum Severity {
107 Info,
108 Low,
109 Medium,
110 High,
111 Critical,
112}
113
114#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
116pub struct DtcStatusByte {
117 pub test_failed: bool,
118 pub test_failed_this_cycle: bool,
119 pub pending: bool,
120 pub confirmed: bool,
121 pub test_not_completed_since_clear: bool,
122 pub test_failed_since_clear: bool,
123 pub test_not_completed_this_cycle: bool,
124 pub warning_indicator_requested: bool,
125}
126
127impl DtcStatusByte {
128 pub fn from_byte(b: u8) -> Self {
130 Self {
131 test_failed: (b & 0x01) != 0,
132 test_failed_this_cycle: (b & 0x02) != 0,
133 pending: (b & 0x04) != 0,
134 confirmed: (b & 0x08) != 0,
135 test_not_completed_since_clear: (b & 0x10) != 0,
136 test_failed_since_clear: (b & 0x20) != 0,
137 test_not_completed_this_cycle: (b & 0x40) != 0,
138 warning_indicator_requested: (b & 0x80) != 0,
139 }
140 }
141
142 pub fn to_byte(&self) -> u8 {
144 let mut b = 0u8;
145 if self.test_failed {
146 b |= 0x01;
147 }
148 if self.test_failed_this_cycle {
149 b |= 0x02;
150 }
151 if self.pending {
152 b |= 0x04;
153 }
154 if self.confirmed {
155 b |= 0x08;
156 }
157 if self.test_not_completed_since_clear {
158 b |= 0x10;
159 }
160 if self.test_failed_since_clear {
161 b |= 0x20;
162 }
163 if self.test_not_completed_this_cycle {
164 b |= 0x40;
165 }
166 if self.warning_indicator_requested {
167 b |= 0x80;
168 }
169 b
170 }
171}
172
173pub fn universal_dtc_description(code: &str) -> Option<&'static str> {
177 match code {
178 "P0010" => Some("Intake Camshaft Position Actuator Circuit (Bank 1)"),
180 "P0011" => Some("Intake Camshaft Position Timing Over-Advanced (Bank 1)"),
181 "P0012" => Some("Intake Camshaft Position Timing Over-Retarded (Bank 1)"),
182 "P0014" => Some("Exhaust Camshaft Position Timing Over-Advanced (Bank 1)"),
183 "P0016" => Some("Crankshaft Position / Camshaft Position Correlation (Bank 1 Sensor A)"),
184 "P0030" => Some("HO2S Heater Control Circuit (Bank 1 Sensor 1)"),
186 "P0036" => Some("HO2S Heater Control Circuit (Bank 1 Sensor 2)"),
187 "P0087" => Some("Fuel Rail/System Pressure Too Low"),
189 "P0088" => Some("Fuel Rail/System Pressure Too High"),
190 "P0093" => Some("Fuel System Leak Detected (Large)"),
191 "P0100" => Some("MAF/VAF Circuit Malfunction"),
193 "P0101" => Some("MAF/VAF Circuit Range/Performance"),
194 "P0102" => Some("MAF/VAF Circuit Low Input"),
195 "P0103" => Some("MAF/VAF Circuit High Input"),
196 "P0106" => Some("MAP/Barometric Pressure Circuit Range/Performance"),
197 "P0107" => Some("MAP/Barometric Pressure Circuit Low Input"),
198 "P0108" => Some("MAP/Barometric Pressure Circuit High Input"),
199 "P0110" => Some("Intake Air Temperature Sensor 1 Circuit"),
200 "P0111" => Some("Intake Air Temperature Sensor 1 Circuit Range/Performance"),
201 "P0112" => Some("Intake Air Temperature Sensor 1 Circuit Low"),
202 "P0113" => Some("Intake Air Temperature Sensor 1 Circuit High"),
203 "P0115" => Some("Engine Coolant Temperature Circuit"),
204 "P0116" => Some("Engine Coolant Temperature Circuit Range/Performance"),
205 "P0117" => Some("Engine Coolant Temperature Circuit Low"),
206 "P0118" => Some("Engine Coolant Temperature Circuit High"),
207 "P0120" => Some("Throttle Position Sensor A Circuit"),
208 "P0121" => Some("Throttle Position Sensor A Circuit Range/Performance"),
209 "P0122" => Some("Throttle Position Sensor A Circuit Low"),
210 "P0123" => Some("Throttle Position Sensor A Circuit High"),
211 "P0125" => Some("Insufficient Coolant Temperature for Closed Loop"),
212 "P0128" => Some("Coolant Thermostat Below Operating Temperature"),
213 "P0130" => Some("O2 Sensor Circuit Bank 1 Sensor 1"),
214 "P0131" => Some("O2 Sensor Circuit Low Voltage B1S1"),
215 "P0132" => Some("O2 Sensor Circuit High Voltage B1S1"),
216 "P0133" => Some("O2 Sensor Circuit Slow Response B1S1"),
217 "P0134" => Some("O2 Sensor Circuit No Activity B1S1"),
218 "P0135" => Some("O2 Sensor Heater Circuit B1S1"),
219 "P0136" => Some("O2 Sensor Circuit Bank 1 Sensor 2"),
220 "P0137" => Some("O2 Sensor Circuit Low Voltage B1S2"),
221 "P0138" => Some("O2 Sensor Circuit High Voltage B1S2"),
222 "P0139" => Some("O2 Sensor Circuit Slow Response B1S2"),
223 "P0140" => Some("O2 Sensor Circuit No Activity B1S2"),
224 "P0141" => Some("O2 Sensor Heater Circuit B1S2"),
225 "P0150" => Some("O2 Sensor Circuit Bank 2 Sensor 1"),
226 "P0151" => Some("O2 Sensor Circuit Low Voltage B2S1"),
227 "P0152" => Some("O2 Sensor Circuit High Voltage B2S1"),
228 "P0153" => Some("O2 Sensor Circuit Slow Response B2S1"),
229 "P0154" => Some("O2 Sensor Circuit No Activity B2S1"),
230 "P0155" => Some("O2 Sensor Heater Circuit B2S1"),
231 "P0156" => Some("O2 Sensor Circuit Bank 2 Sensor 2"),
232 "P0157" => Some("O2 Sensor Circuit Low Voltage B2S2"),
233 "P0158" => Some("O2 Sensor Circuit High Voltage B2S2"),
234 "P0159" => Some("O2 Sensor Circuit Slow Response B2S2"),
235 "P0160" => Some("O2 Sensor Circuit No Activity B2S2"),
236 "P0161" => Some("O2 Sensor Heater Circuit B2S2"),
237 "P0171" => Some("System Too Lean Bank 1"),
238 "P0172" => Some("System Too Rich Bank 1"),
239 "P0174" => Some("System Too Lean Bank 2"),
240 "P0175" => Some("System Too Rich Bank 2"),
241 "P0192" => Some("Fuel Rail Pressure Sensor Circuit Low"),
243 "P0193" => Some("Fuel Rail Pressure Sensor Circuit High"),
244 "P0201" => Some("Injector Circuit/Open Cylinder 1"),
246 "P0202" => Some("Injector Circuit/Open Cylinder 2"),
247 "P0203" => Some("Injector Circuit/Open Cylinder 3"),
248 "P0204" => Some("Injector Circuit/Open Cylinder 4"),
249 "P0205" => Some("Injector Circuit/Open Cylinder 5"),
250 "P0206" => Some("Injector Circuit/Open Cylinder 6"),
251 "P0207" => Some("Injector Circuit/Open Cylinder 7"),
252 "P0208" => Some("Injector Circuit/Open Cylinder 8"),
253 "P0234" => Some("Turbo/Supercharger Overboost Condition"),
255 "P0236" => Some("Turbocharger Boost Sensor A Circuit Range/Performance"),
256 "P0299" => Some("Turbo/Supercharger Underboost"),
257 "P0300" => Some("Random/Multiple Cylinder Misfire Detected"),
259 "P0301" => Some("Cylinder 1 Misfire Detected"),
260 "P0302" => Some("Cylinder 2 Misfire Detected"),
261 "P0303" => Some("Cylinder 3 Misfire Detected"),
262 "P0304" => Some("Cylinder 4 Misfire Detected"),
263 "P0305" => Some("Cylinder 5 Misfire Detected"),
264 "P0306" => Some("Cylinder 6 Misfire Detected"),
265 "P0307" => Some("Cylinder 7 Misfire Detected"),
266 "P0308" => Some("Cylinder 8 Misfire Detected"),
267 "P0325" => Some("Knock Sensor 1 Circuit Bank 1"),
268 "P0335" => Some("Crankshaft Position Sensor A Circuit"),
269 "P0336" => Some("Crankshaft Position Sensor A Range/Performance"),
270 "P0340" => Some("Camshaft Position Sensor A Circuit Bank 1"),
271 "P0341" => Some("Camshaft Position Sensor A Range/Performance Bank 1"),
272 "P0365" => Some("Camshaft Position Sensor B Circuit (Bank 1)"),
274 "P0366" => Some("Camshaft Position Sensor B Circuit Range/Performance (Bank 1)"),
275 "P0380" => Some("Glow Plug/Heater Circuit A Malfunction"),
277 "P0400" => Some("EGR Flow Malfunction"),
279 "P0401" => Some("EGR Flow Insufficient"),
280 "P0402" => Some("EGR Flow Excessive"),
281 "P0404" => Some("EGR System Range/Performance"),
282 "P0405" => Some("EGR Position Sensor A Circuit Low"),
283 "P0406" => Some("EGR Position Sensor A Circuit High"),
284 "P0420" => Some("Catalyst System Efficiency Below Threshold Bank 1"),
285 "P0430" => Some("Catalyst System Efficiency Below Threshold Bank 2"),
286 "P0440" => Some("Evaporative Emission System Malfunction"),
287 "P0441" => Some("Evaporative Emission System Incorrect Purge Flow"),
288 "P0442" => Some("Evaporative Emission System Leak Detected (Small)"),
289 "P0443" => Some("Evaporative Emission System Purge Control Valve Circuit"),
290 "P0446" => Some("Evaporative Emission System Vent Control Circuit"),
291 "P0449" => Some("Evaporative Emission System Vent Valve/Solenoid Circuit"),
292 "P0451" => Some("Evaporative Emission System Pressure Sensor Range/Performance"),
293 "P0455" => Some("Evaporative Emission System Leak Detected (Large)"),
294 "P0456" => Some("Evaporative Emission System Leak Detected (Very Small)"),
295 "P0496" => Some("Evaporative Emission System High Purge Flow"),
296 "P0500" => Some("Vehicle Speed Sensor A Malfunction"),
298 "P0503" => Some("Vehicle Speed Sensor Intermittent/Erratic/High"),
299 "P0505" => Some("Idle Air Control System Malfunction"),
300 "P0506" => Some("Idle Air Control System RPM Lower Than Expected"),
301 "P0507" => Some("Idle Air Control System RPM Higher Than Expected"),
302 "P0562" => Some("System Voltage Low"),
304 "P0563" => Some("System Voltage High"),
305 "P0597" => Some("Thermostat Heater Control Circuit Open"),
307 "P0598" => Some("Thermostat Heater Control Circuit Low"),
308 "P0599" => Some("Thermostat Heater Control Circuit High"),
309 "P0600" => Some("Serial Communication Link Malfunction"),
311 "P0601" => Some("Internal Control Module Memory Check Sum Error"),
312 "P0602" => Some("Control Module Programming Error"),
313 "P0606" => Some("ECM/PCM Processor Fault"),
314 "P0700" => Some("Transmission Control System Malfunction"),
316 "P0705" => Some("Transmission Range Sensor Circuit Malfunction"),
317 "P0710" => Some("Transmission Fluid Temperature Sensor Circuit"),
318 "P0711" => Some("Transmission Fluid Temperature Sensor Circuit Range/Performance"),
319 "P0715" => Some("Input/Turbine Speed Sensor Circuit"),
320 "P0717" => Some("Input/Turbine Speed Sensor Circuit No Signal"),
321 "P0720" => Some("Output Speed Sensor Circuit"),
322 "P0725" => Some("Engine Speed Input Circuit"),
323 "P0730" => Some("Incorrect Gear Ratio"),
324 "P0731" => Some("Gear 1 Incorrect Ratio"),
325 "P0732" => Some("Gear 2 Incorrect Ratio"),
326 "P0733" => Some("Gear 3 Incorrect Ratio"),
327 "P0734" => Some("Gear 4 Incorrect Ratio"),
328 "P0735" => Some("Gear 5 Incorrect Ratio"),
329 "P0740" => Some("Torque Converter Clutch Circuit Malfunction"),
330 "P0741" => Some("Torque Converter Clutch System Stuck Off"),
331 "P0742" => Some("Torque Converter Clutch System Stuck On"),
332 "P0743" => Some("Torque Converter Clutch System Electrical"),
333 "P0747" => Some("Pressure Control Solenoid A Stuck On"),
334 "P0748" => Some("Pressure Control Solenoid A Electrical"),
335 "P0750" => Some("Shift Solenoid A Malfunction"),
336 "P0751" => Some("Shift Solenoid A Performance/Stuck Off"),
337 "P0752" => Some("Shift Solenoid A Stuck On"),
338 "P0753" => Some("Shift Solenoid A Electrical"),
339 "P0755" => Some("Shift Solenoid B Malfunction"),
340 "P0756" => Some("Shift Solenoid B Performance/Stuck Off"),
341 "P0757" => Some("Shift Solenoid B Stuck On"),
342 "P0758" => Some("Shift Solenoid B Electrical"),
343 "P1101" => Some("Intake Airflow System Performance"),
345 "P2097" => Some("Post Catalyst Fuel Trim System Too Rich (Bank 1)"),
346 "P2227" => Some("Barometric Pressure Circuit Range/Performance"),
347 "P2270" => Some("O2 Sensor Signal Stuck Lean (Bank 1 Sensor 2)"),
348 "P2271" => Some("O2 Sensor Signal Stuck Rich (Bank 1 Sensor 2)"),
349 "P2797" => Some("Auxiliary Transmission Fluid Pump Performance"),
350 "B0083" => Some("Left Side/Front Impact Sensor Circuit"),
352 "B0092" => Some("Left Side/Rear Impact Sensor Circuit"),
353 "B0096" => Some("Right Side/Rear Impact Sensor Circuit"),
354 "B0408" => Some("Temperature Control A Circuit"),
355 "B1325" => Some("Control Module General Memory Failure"),
356 "B1517" => Some("Steering Wheel Controls Switch 1 Circuit"),
357 "C0035" => Some("Left Front Wheel Speed Sensor Circuit"),
359 "C0040" => Some("Right Front Wheel Speed Sensor Circuit"),
360 "C0045" => Some("Left Rear Wheel Speed Sensor Circuit"),
361 "C0050" => Some("Right Rear Wheel Speed Sensor Circuit"),
362 "C0110" => Some("Pump Motor Circuit Malfunction"),
363 "C0161" => Some("ABS/TCS Brake Switch Circuit Malfunction"),
364 "C0186" => Some("Lateral Accelerometer Circuit"),
365 "C0196" => Some("Yaw Rate Sensor Circuit"),
366 "C0550" => Some("ECU Malfunction (Stability System)"),
367 "C0899" => Some("Device Voltage Low"),
368 "C0900" => Some("Device Voltage High"),
369 "U0001" => Some("High Speed CAN Communication Bus"),
371 "U0073" => Some("Control Module Communication Bus Off"),
372 "U0100" => Some("Lost Communication with ECM/PCM"),
373 "U0101" => Some("Lost Communication with TCM"),
374 "U0121" => Some("Lost Communication with ABS"),
375 "U0140" => Some("Lost Communication with BCM"),
376 "U0146" => Some("Lost Communication with Gateway A"),
377 "U0151" => Some("Lost Communication with SIR Module"),
378 "U0155" => Some("Lost Communication with Instrument Panel Cluster"),
379 "U0168" => Some("Lost Communication with HVAC Control Module"),
380 "U0184" => Some("Lost Communication with Radio"),
381 "U0401" => Some("Invalid Data Received from ECM/PCM A"),
382 _ => None,
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use super::*;
389
390 #[test]
391 fn test_dtc_default() {
392 let dtc = Dtc::default();
393 assert_eq!(dtc.category, DtcCategory::Powertrain);
394 assert_eq!(dtc.status, DtcStatus::Stored);
395 assert!(dtc.description.is_none());
396 }
397
398 #[test]
399 fn test_severity_ordering() {
400 assert!(Severity::Critical > Severity::High);
401 assert!(Severity::High > Severity::Medium);
402 assert!(Severity::Medium > Severity::Low);
403 assert!(Severity::Low > Severity::Info);
404 }
405
406 #[test]
407 fn test_dtc_status_byte_default() {
408 let status = DtcStatusByte::default();
409 assert!(!status.test_failed);
410 assert!(!status.confirmed);
411 assert!(!status.warning_indicator_requested);
412 }
413
414 #[test]
415 fn test_dtc_from_bytes_powertrain() {
416 let dtc = Dtc::from_bytes(0x04, 0x20);
417 assert_eq!(dtc.code, "P0420");
418 assert_eq!(dtc.category, DtcCategory::Powertrain);
419 assert!(dtc.description.is_some());
420 assert!(dtc.description.unwrap().contains("Catalyst"));
421 }
422
423 #[test]
424 fn test_dtc_from_bytes_chassis() {
425 let dtc = Dtc::from_bytes(0x40, 0x35);
426 assert_eq!(dtc.code, "C0035");
427 assert_eq!(dtc.category, DtcCategory::Chassis);
428 }
429
430 #[test]
431 fn test_dtc_from_bytes_body() {
432 let dtc = Dtc::from_bytes(0x80, 0x83);
433 assert_eq!(dtc.code, "B0083");
434 assert_eq!(dtc.category, DtcCategory::Body);
435 }
436
437 #[test]
438 fn test_dtc_from_bytes_network() {
439 let dtc = Dtc::from_bytes(0xC1, 0x00);
440 assert_eq!(dtc.code, "U0100");
441 assert_eq!(dtc.category, DtcCategory::Network);
442 }
443
444 #[test]
445 fn test_dtc_from_code() {
446 let dtc = Dtc::from_code("P0420");
447 assert_eq!(dtc.code, "P0420");
448 assert_eq!(dtc.category, DtcCategory::Powertrain);
449 assert!(dtc.description.is_some());
450 }
451
452 #[test]
453 fn test_dtc_from_code_lowercase() {
454 let dtc = Dtc::from_code("p0171");
455 assert_eq!(dtc.code, "P0171");
456 }
457
458 #[test]
459 fn test_universal_description_known() {
460 assert!(universal_dtc_description("P0420").is_some());
461 assert!(universal_dtc_description("P0300").unwrap().contains("Misfire"));
462 }
463
464 #[test]
465 fn test_universal_description_unknown() {
466 assert!(universal_dtc_description("P9999").is_none());
467 }
468
469 #[test]
470 fn test_status_byte_decode() {
471 let status = DtcStatusByte::from_byte(0x0B); assert!(status.test_failed);
473 assert!(status.test_failed_this_cycle);
474 assert!(!status.pending);
475 assert!(status.confirmed);
476 }
477
478 #[test]
479 fn test_status_byte_roundtrip() {
480 let original: u8 = 0xAF;
481 let status = DtcStatusByte::from_byte(original);
482 assert_eq!(status.to_byte(), original);
483 }
484
485 #[test]
486 fn test_status_byte_all_flags() {
487 let status = DtcStatusByte::from_byte(0xFF);
488 assert!(status.test_failed);
489 assert!(status.test_failed_this_cycle);
490 assert!(status.pending);
491 assert!(status.confirmed);
492 assert!(status.test_not_completed_since_clear);
493 assert!(status.test_failed_since_clear);
494 assert!(status.test_not_completed_this_cycle);
495 assert!(status.warning_indicator_requested);
496 }
497
498 #[test]
499 fn test_status_byte_mil_on() {
500 let status = DtcStatusByte::from_byte(0x80);
501 assert!(status.warning_indicator_requested); assert!(!status.test_failed);
503 }
504}