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