Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | #include "pch.h" | |||
2 | ||||
3 | // Decode what OBD code we should use for a particular [sensor, code] problem | |||
4 | 14 | static ObdCode getCode(SensorType type, UnexpectedCode code) { | ||
5 |
4/11✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
14 | switch (type) { | |
6 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case SensorType::Tps1: | |
7 | case SensorType::Tps1Primary: | |||
8 |
1/5✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1/1✓ Decision 'true' taken 2 times.
|
2 | switch (code) { |
9 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case UnexpectedCode::Timeout: return ObdCode::OBD_TPS1_Primary_Timeout; | |
10 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_TPS1_Primary_Low; | ||
11 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_TPS1_Primary_High; | ||
12 | ✗ | case UnexpectedCode::Inconsistent: return ObdCode::OBD_TPS1_Correlation; | ||
13 | ✗ | default: break; | ||
14 | ✗ | } break; | ||
15 |
1/1✓ Decision 'true' taken 4 times.
|
4 | case SensorType::Tps1Secondary: | |
16 |
1/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | switch (code) { | |
17 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_TPS1_Secondary_Timeout; | ||
18 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_TPS1_Secondary_Low; | ||
19 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_TPS1_Secondary_High; | ||
20 |
1/1✓ Decision 'true' taken 4 times.
|
4 | default: break; | |
21 | 4 | } break; | ||
22 | ✗ | case SensorType::Tps2: | ||
23 | case SensorType::Tps2Primary: | |||
24 | ✗ | switch (code) { | ||
25 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_TPS2_Primary_Timeout; | ||
26 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_TPS2_Primary_Low; | ||
27 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_TPS2_Primary_High; | ||
28 | ✗ | case UnexpectedCode::Inconsistent: return ObdCode::OBD_TPS2_Correlation; | ||
29 | ✗ | default: break; | ||
30 | ✗ | } break; | ||
31 | ✗ | case SensorType::Tps2Secondary: | ||
32 | ✗ | switch (code) { | ||
33 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_TPS2_Secondary_Timeout; | ||
34 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_TPS2_Secondary_Low; | ||
35 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_TPS2_Secondary_High; | ||
36 | ✗ | default: break; | ||
37 | ✗ | } break; | ||
38 | ||||
39 |
1/1✓ Decision 'true' taken 4 times.
|
4 | case SensorType::AcceleratorPedal: | |
40 | case SensorType::AcceleratorPedalPrimary: | |||
41 |
2/5✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
1/1✓ Decision 'true' taken 4 times.
|
4 | switch (code) { |
42 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case UnexpectedCode::Timeout: return ObdCode::OBD_PPS_Primary_Timeout; | |
43 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_PPS_Primary_Low; | ||
44 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_PPS_Primary_High; | ||
45 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case UnexpectedCode::Inconsistent: return ObdCode::OBD_PPS_Correlation; | |
46 | ✗ | default: break; | ||
47 | ✗ | } break; | ||
48 |
1/1✓ Decision 'true' taken 4 times.
|
4 | case SensorType::AcceleratorPedalSecondary: | |
49 |
1/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | switch (code) { | |
50 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_PPS_Secondary_Timeout; | ||
51 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_PPS_Secondary_Low; | ||
52 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_PPS_Secondary_High; | ||
53 |
1/1✓ Decision 'true' taken 4 times.
|
4 | default: break; | |
54 | 4 | } break; | ||
55 | ||||
56 | ✗ | case SensorType::Map: | ||
57 | ✗ | switch (code) { | ||
58 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_Map_Timeout; | ||
59 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_Map_Low; | ||
60 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_Map_High; | ||
61 | ✗ | default: break; | ||
62 | ✗ | } break; | ||
63 | ✗ | case SensorType::Clt: | ||
64 | ✗ | switch (code) { | ||
65 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_Clt_Timeout; | ||
66 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_Clt_Low; | ||
67 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_Clt_High; | ||
68 | ✗ | default: break; | ||
69 | ✗ | } break; | ||
70 | ✗ | case SensorType::Iat: | ||
71 | ✗ | switch (code) { | ||
72 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_Iat_Timeout; | ||
73 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_Iat_Low; | ||
74 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_Iat_High; | ||
75 | ✗ | default: break; | ||
76 | ✗ | } break; | ||
77 | ✗ | case SensorType::FuelEthanolPercent: | ||
78 | ✗ | switch (code) { | ||
79 | ✗ | case UnexpectedCode::Timeout: return ObdCode::OBD_FlexSensor_Timeout; | ||
80 | ✗ | case UnexpectedCode::Low: return ObdCode::OBD_FlexSensor_Low; | ||
81 | ✗ | case UnexpectedCode::High: return ObdCode::OBD_FlexSensor_High; | ||
82 | ✗ | default: break; | ||
83 | ✗ | } break; | ||
84 | ✗ | default: | ||
85 | ✗ | break; | ||
86 | } | |||
87 | ||||
88 | 8 | return ObdCode::None; | ||
89 | } | |||
90 | ||||
91 | 6 | inline const char* describeUnexpected(UnexpectedCode code) { | ||
92 |
2/6✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
6 | switch (code) { | |
93 |
1/1✓ Decision 'true' taken 4 times.
|
4 | case UnexpectedCode::Timeout: return "has timed out"; | |
94 | ✗ | case UnexpectedCode::High: return "input too high"; | ||
95 | ✗ | case UnexpectedCode::Low: return "input too low"; | ||
96 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case UnexpectedCode::Inconsistent: return "is inconsistent"; | |
97 | ✗ | case UnexpectedCode::Configuration: return "is misconfigured"; | ||
98 | ✗ | case UnexpectedCode::Unknown: | ||
99 | default: | |||
100 | ✗ | return "unknown"; | ||
101 | } | |||
102 | } | |||
103 | ||||
104 | 1246 | static void check(SensorType type) { | ||
105 | // Don't check sensors we don't have | |||
106 |
3/3✓ Branch 1 taken 1246 times.
✓ Branch 3 taken 1044 times.
✓ Branch 4 taken 202 times.
|
2/2✓ Decision 'true' taken 1044 times.
✓ Decision 'false' taken 202 times.
|
1246 | if (!Sensor::hasSensor(type)) { |
107 | 1044 | return; | ||
108 | } | |||
109 | ||||
110 |
1/1✓ Branch 2 taken 202 times.
|
202 | auto result = Sensor::get(type); | |
111 | ||||
112 | // If the sensor is OK, nothing to check. | |||
113 |
2/2✓ Branch 1 taken 188 times.
✓ Branch 2 taken 14 times.
|
2/2✓ Decision 'true' taken 188 times.
✓ Decision 'false' taken 14 times.
|
202 | if (result) { |
114 | 188 | return; | ||
115 | } | |||
116 | ||||
117 | 14 | ObdCode code = getCode(type, result.Code); | ||
118 | ||||
119 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
|
2/2✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 8 times.
|
14 | if (code != ObdCode::None) { |
120 |
2/2✓ Branch 2 taken 6 times.
✓ Branch 5 taken 6 times.
|
6 | warning(code, "Sensor fault: %s %s", Sensor::getSensorName(type), describeUnexpected(result.Code)); | |
121 | } | |||
122 | } | |||
123 | ||||
124 | #if BOARD_EXT_GPIOCHIPS > 0 && EFI_PROD_CODE | |||
125 | #if EFI_ENGINE_CONTROL | |||
126 | static ObdCode getCodeForInjector(int idx, brain_pin_diag_e diag) { | |||
127 | if (idx < 0 || idx >= MAX_CYLINDER_COUNT) { | |||
128 | return ObdCode::None; | |||
129 | } | |||
130 | ||||
131 | if ((diag & PIN_OPEN) || (diag & PIN_SHORT_TO_GND)) { | |||
132 | return (ObdCode)((int)ObdCode::OBD_Injector_Circuit_1_Low + (idx * 3)); | |||
133 | } else if ((diag & PIN_SHORT_TO_BAT) || (diag & PIN_OVERLOAD)) { | |||
134 | return (ObdCode)((int)ObdCode::OBD_Injector_Circuit_1_High + (idx * 3)); | |||
135 | } | |||
136 | ||||
137 | /* else common error code */ | |||
138 | return (ObdCode)((int)ObdCode::OBD_Injector_Circuit_1 + idx); | |||
139 | } | |||
140 | #endif // EFI_ENGINE_CONTROL | |||
141 | ||||
142 | static ObdCode getCodeForIgnition(int idx, brain_pin_diag_e diag) { | |||
143 | if (idx < 0 || idx >= MAX_CYLINDER_COUNT) { | |||
144 | return ObdCode::None; | |||
145 | } | |||
146 | ||||
147 | // TODO: do something more intelligent with `diag`? | |||
148 | UNUSED(diag); | |||
149 | ||||
150 | return (ObdCode)((int)ObdCode::OBD_Ignition_Circuit_1 + idx); | |||
151 | } | |||
152 | ||||
153 | static uint8_t getTSErrorCode(brain_pin_diag_e diag) { | |||
154 | /* Error codes reported to TS: | |||
155 | * 0 - output is not used | |||
156 | * 1 - ok status/no diagnostic available (TODO: separate codes) | |||
157 | * >1 - see brain_pin_diag_e, first least significant 1-bit position + 1 * | |||
158 | * Keep in sync with outputDiagErrorList in tunerstudio.template.ini | |||
159 | * Note: | |||
160 | * diag can be combination of few errors, | |||
161 | * while we report only one error to simplify handling on TS side | |||
162 | * find position of least significant 1-bit */ | |||
163 | return __builtin_ffs(diag) + TS_ENUM_OFFSET; | |||
164 | } | |||
165 | #endif // BOARD_EXT_GPIOCHIPS > 0 && EFI_PROD_CODE | |||
166 | ||||
167 | 89 | PUBLIC_API_WEAK void boardSensorChecker() { | ||
168 | 89 | } | ||
169 | ||||
170 | 1089 | void SensorChecker::onSlowCallback() { | ||
171 | // Don't check when the ignition is off, or when it was just turned on (let things stabilize) | |||
172 | // TODO: also inhibit checking if we just did a flash burn, since that blocks the ECU for a few seconds. | |||
173 |
3/4✓ Branch 0 taken 89 times.
✓ Branch 1 taken 1000 times.
✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
1089 | bool shouldCheck = m_ignitionIsOn && m_timeSinceIgnOff.hasElapsedSec(5); | |
174 | 1089 | m_analogSensorsShouldWork = shouldCheck; | ||
175 |
2/2✓ Branch 0 taken 1000 times.
✓ Branch 1 taken 89 times.
|
2/2✓ Decision 'true' taken 1000 times.
✓ Decision 'false' taken 89 times.
|
1089 | if (!shouldCheck) { |
176 | 1000 | return; | ||
177 | } | |||
178 | ||||
179 | // Check sensors | |||
180 | 89 | check(SensorType::Tps1Primary); | ||
181 | 89 | check(SensorType::Tps1Secondary); | ||
182 | 89 | check(SensorType::Tps1); | ||
183 | 89 | check(SensorType::Tps2Primary); | ||
184 | 89 | check(SensorType::Tps2Secondary); | ||
185 | 89 | check(SensorType::Tps2); | ||
186 | ||||
187 | 89 | check(SensorType::AcceleratorPedalPrimary); | ||
188 | 89 | check(SensorType::AcceleratorPedalSecondary); | ||
189 | 89 | check(SensorType::AcceleratorPedal); | ||
190 | ||||
191 | 89 | check(SensorType::Map); | ||
192 | 89 | check(SensorType::Map2); | ||
193 | ||||
194 | 89 | check(SensorType::Clt); | ||
195 | 89 | check(SensorType::Iat); | ||
196 | ||||
197 | 89 | check(SensorType::FuelEthanolPercent); | ||
198 | ||||
199 | #if EFI_PROD_CODE | |||
200 | TunerStudioOutputChannels *state = getTunerStudioOutputChannels(); | |||
201 | // only bother checking these if we have GPIO chips actually capable of reporting an error | |||
202 | #if BOARD_EXT_GPIOCHIPS > 0 | |||
203 | #if EFI_ENGINE_CONTROL | |||
204 | // Check injectors | |||
205 | int unhappyInjector = 0; | |||
206 | for (size_t i = 0; i < efi::size(enginePins.injectors); i++) { | |||
207 | InjectorOutputPin& pin = enginePins.injectors[i]; | |||
208 | ||||
209 | // Skip not-configured pins | |||
210 | if (!isBrainPinValid(pin.brainPin)) { | |||
211 | state->injectorDiagnostic[i] = 0; | |||
212 | continue; | |||
213 | } | |||
214 | ||||
215 | auto diag = pin.getDiag(); | |||
216 | if (diag != PIN_OK && diag != PIN_UNKNOWN) { | |||
217 | unhappyInjector = 1 + i; | |||
218 | auto code = getCodeForInjector(i, diag); | |||
219 | ||||
220 | char description[32]; | |||
221 | pinDiag2string(description, efi::size(description), diag); | |||
222 | warning(code, "Injector %d fault: %s", i + 1, description); | |||
223 | } | |||
224 | state->injectorDiagnostic[i] = getTSErrorCode(diag); | |||
225 | } | |||
226 | engine->fuelComputer.brokenInjector = unhappyInjector; | |||
227 | engine->fuelComputer.injectorHwIssue = (unhappyInjector != 0); | |||
228 | #endif // EFI_ENGINE_CONTROL | |||
229 | ||||
230 | // Check ignition | |||
231 | for (size_t i = 0; i < efi::size(enginePins.injectors); i++) { | |||
232 | IgnitionOutputPin& pin = enginePins.coils[i]; | |||
233 | ||||
234 | // Skip not-configured pins | |||
235 | if (!isBrainPinValid(pin.brainPin)) { | |||
236 | state->ignitorDiagnostic[i] = 0; | |||
237 | continue; | |||
238 | } | |||
239 | ||||
240 | auto diag = pin.getDiag(); | |||
241 | if (diag != PIN_OK && diag != PIN_UNKNOWN) { | |||
242 | auto code = getCodeForIgnition(i, diag); | |||
243 | ||||
244 | char description[32]; | |||
245 | pinDiag2string(description, efi::size(description), diag); | |||
246 | warning(code, "Ignition %d fault: %s", i + 1, description); | |||
247 | } | |||
248 | state->ignitorDiagnostic[i] = getTSErrorCode(diag); | |||
249 | } | |||
250 | #endif // BOARD_EXT_GPIOCHIPS > 0 | |||
251 | ||||
252 | // Check ADC(s) and analog inputs | |||
253 | auto code = analogGetDiagnostic(); | |||
254 | if (code != ObdCode::None) { | |||
255 | /* TODO: map to more OBD codes? */ | |||
256 | warning(code, "Analog subsystem fault"); | |||
257 | state->isAnalogFailure = true; | |||
258 | } else { | |||
259 | state->isAnalogFailure = false; | |||
260 | } | |||
261 | #endif // EFI_PROD_CODE | |||
262 | ||||
263 | 89 | boardSensorChecker(); | ||
264 | } | |||
265 | ||||
266 | 3 | void SensorChecker::onIgnitionStateChanged(bool ignitionOn) { | ||
267 | 3 | m_ignitionIsOn = ignitionOn; | ||
268 | ||||
269 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 3 times.
|
3 | if (!ignitionOn) { |
270 | // timer keeps track of how long since the state was turned to on (ie, how long ago was it last off) | |||
271 | ✗ | m_timeSinceIgnOff.reset(); | ||
272 | } | |||
273 | 3 | } | ||
274 |