nx_obd_dtc.nx source
↩ module page · 107 lines · 4649 B
1// nx_obd_dtc.nx -- OBD-II diagnostic-trouble-code decoder (the AUTOMOTIVE
2// vehicle-obd capability). Decodes a 5-char DTC (e.g. "P0301") into its
3// structure -- system / SAE-generic-vs-manufacturer / subsystem / fault -- then
4// CLASSIFIES severity and routes it to the platform action rung (DIY vs pro).
5//
6// Operator (2026-06-23): "cars" -- the first-listed maintenance domain; the CIQ
7// top-build vehicle-obd-diagnostics (beat FIXD).
8//
9// THE EXCEED (on-thesis): FIXD reads the code + shows a generic blurb. We decode
10// the structure, classify severity by system+subsystem, and turn the cryptic
11// code into a routed ACTION (monitor / fix-DIY / fix-pro) via nx_maint_action --
12// and the platform's INDEPENDENT physics (battery via electrical, oil/brakes via
13// consumable, bearings via rotating, fluids via fluid-seal) is the trust layer
14// that cross-checks the car's self-report. The OBD code is one ingest, not gospel.
15//
16// HONEST: the DTC STRUCTURE is the SAE J2012 standard (decoded exactly). The
17// severity-by-subsystem mapping is a reasoned starter heuristic (real per-code
18// severity varies; the full code DB is a data-layer follow-up #11).
19//
20// NEVER-BRICK (#26): decodes a string; no syscalls, no vehicle writes, no code
21// clearing. Read-only by construction.
22//
23// genealogy_id: project-maintenance-platform-sclass-2026-06-23 (automotive / vehicle-obd)
24// license_tier: ORIGINAL
25
26import "nx_maint_action.nx" // NX_HEALTH_* + NX_ACT_*
27
28const NX_OBD_SYS_POWERTRAIN: i64 = 0 // 'P'
29const NX_OBD_SYS_CHASSIS: i64 = 1 // 'C'
30const NX_OBD_SYS_BODY: i64 = 2 // 'B'
31const NX_OBD_SYS_NETWORK: i64 = 3 // 'U'
32const NX_OBD_SYS_N: i64 = 4
33
34struct ObdDtc {
35 system: i64, // NX_OBD_SYS_*
36 is_generic: i64, // 1 = SAE generic, 0 = manufacturer-specific
37 subsystem: i64, // 3rd hex digit (0..15)
38 fault_num: i64, // last two hex digits (0..255)
39 severity: i64, // NX_HEALTH_*
40 action: i64, // NX_ACT_*
41}
42
43// ascii hex char -> value 0..15, or -1 if not a hex digit.
44func obd_hex(c: i64) -> i64 {
45 if c >= 48 { if c <= 57 { return c - 48 } } // '0'..'9'
46 if c >= 65 { if c <= 70 { return c - 65 + 10 } } // 'A'..'F'
47 return 0 - 1
48}
49
50// severity by system + powertrain subsystem (reasoned starter heuristic).
51func obd_severity(sys: i64, sub: i64) -> i64 {
52 if sys == NX_OBD_SYS_NETWORK { return NX_HEALTH_URGENT } // lost module comms
53 if sys == NX_OBD_SYS_CHASSIS { return NX_HEALTH_DEGRADED } // ABS / brakes / steering
54 if sys == NX_OBD_SYS_BODY { return NX_HEALTH_WATCH }
55 // powertrain:
56 if sub == 3 { return NX_HEALTH_DEGRADED } // ignition / misfire (cat damage)
57 if sub == 7 { return NX_HEALTH_DEGRADED } // transmission
58 if sub == 8 { return NX_HEALTH_DEGRADED } // transmission
59 return NX_HEALTH_WATCH // fuel/air, emissions, speed, computer
60}
61
62// recommended action by system + powertrain subsystem.
63func obd_action(sys: i64, sub: i64) -> i64 {
64 if sys == NX_OBD_SYS_NETWORK { return NX_ACT_FIX_PRO }
65 if sys == NX_OBD_SYS_CHASSIS { return NX_ACT_FIX_PRO } // brakes/ABS = pro
66 if sys == NX_OBD_SYS_BODY { return NX_ACT_MONITOR }
67 // powertrain:
68 if sub == 3 { return NX_ACT_FIX_DIY } // misfire: plugs / coils, common DIY
69 if sub == 4 { return NX_ACT_FIX_DIY } // emissions: gas cap / EVAP, common DIY
70 return NX_ACT_FIX_PRO // fuel/air sensors, transmission = pro
71}
72
73// Decode a NUL-terminated 5-char DTC -> *out. Returns 1 if valid, 0 if malformed.
74func nx_obd_decode(dtc: *u8, out: *ObdDtc) -> i64 {
75 out.system = 0
76 out.is_generic = 0
77 out.subsystem = 0
78 out.fault_num = 0
79 out.severity = NX_HEALTH_UNKNOWN
80 out.action = NX_ACT_NONE
81
82 let c0: i64 = dtc[0]
83 var sys: i64 = 0 - 1
84 if c0 == 80 { sys = NX_OBD_SYS_POWERTRAIN } // 'P'
85 if c0 == 67 { sys = NX_OBD_SYS_CHASSIS } // 'C'
86 if c0 == 66 { sys = NX_OBD_SYS_BODY } // 'B'
87 if c0 == 85 { sys = NX_OBD_SYS_NETWORK } // 'U'
88 if sys < 0 { return 0 }
89
90 let d1: i64 = obd_hex(dtc[1])
91 let d2: i64 = obd_hex(dtc[2])
92 let d3: i64 = obd_hex(dtc[3])
93 let d4: i64 = obd_hex(dtc[4])
94 if d1 < 0 { return 0 }
95 if d2 < 0 { return 0 }
96 if d3 < 0 { return 0 }
97 if d4 < 0 { return 0 }
98 if dtc[5] != (0 as u8) { return 0 } // exactly 5 chars
99
100 out.system = sys
101 if d1 == 1 { out.is_generic = 0 } else { out.is_generic = 1 } // digit 1 = mfg (simplified)
102 out.subsystem = d2
103 out.fault_num = d3 * 16 + d4
104 out.severity = obd_severity(sys, d2)
105 out.action = obd_action(sys, d2)
106 return 1
107}