GCC Code Coverage Report


Directory: ./
File: firmware/controllers/bench_test.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 100.0% 4 0 4
Functions: 100.0% 2 0 2
Branches: -% 0 0 0
Decisions: -% 0 - 0

Line Branch Decision Exec Source
1 /**
2 * @file bench_test.cpp
3 * @brief Utility methods related to bench testing.
4 *
5 *
6 * @date Sep 8, 2013
7 * @author Andrey Belomutskiy, (c) 2012-2020
8 *
9 * This file is part of rusEfi - see http://rusefi.com
10 *
11 * rusEfi is free software; you can redistribute it and/or modify it under the terms of
12 * the GNU General Public License as published by the Free Software Foundation; either
13 * version 3 of the License, or (at your option) any later version.
14 *
15 * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
16 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along with this program.
20 * If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "pch.h"
24 #include "tunerstudio.h"
25 #include "tunerstudio_calibration_channel.h"
26 #include "long_term_fuel_trim.h"
27 #include "can_common.h"
28 #include "can_rx.h"
29 #include "value_lookup.h"
30 #include "can_msg_tx.h"
31 #include "board_overrides.h"
32
33 static bool isRunningBench = false;
34 static OutputPin *outputOnTheBenchTest = nullptr;
35
36 2091 bool isRunningBenchTest() {
37 2091 return isRunningBench;
38 }
39
40 39029 const OutputPin *getOutputOnTheBenchTest() {
41 39029 return outputOnTheBenchTest;
42 }
43
44 #if !EFI_UNIT_TEST
45
46 #include "flash_main.h"
47 #include "bench_test.h"
48 #include "main_trigger_callback.h"
49 #include "periodic_thread_controller.h"
50 #include "electronic_throttle.h"
51 #include "electronic_throttle_impl.h"
52 #include "malfunction_central.h"
53 #include "trigger_emulator_algo.h"
54 #include "vvt.h"
55 #include "microsecond_timer.h"
56 #include "rusefi_wideband.h"
57
58 #if EFI_PROD_CODE
59 #include "rusefi.h"
60 #include "mpu_util.h"
61 #endif /* EFI_PROD_CODE */
62
63 #if (BOARD_TLE8888_COUNT > 0)
64 #include "gpio/tle8888.h"
65 #endif // BOARD_TLE8888_COUNT
66
67 #if EFI_FILE_LOGGING
68 #include "mmc_card.h"
69 #endif
70
71 static scheduling_s benchSchedStart;
72 static scheduling_s benchSchedEnd;
73
74 #if EFI_SIMULATOR
75 static int savedPinToggleCounter = 0;
76 static uint32_t savedDurationsInStateMs[2] = { 0, 0 };
77 #endif // EFI_SIMULATOR
78
79
80 #define BENCH_MSG "bench"
81
82 static void benchOn(OutputPin* output) {
83 output->setValue(BENCH_MSG, true, /*isForce*/ true);
84 }
85
86 static void benchOff(OutputPin* output) {
87 #if EFI_PROD_CODE && (BOARD_EXT_GPIOCHIPS > 0)
88 static char pin_error[64];
89
90 brain_pin_diag_e diag = output->getDiag();
91 if (diag == PIN_UNKNOWN) {
92 efiPrintf("No Diag on this pin");
93 } else {
94 pinDiag2string(pin_error, sizeof(pin_error), diag);
95 efiPrintf("Diag says %s", pin_error);
96 }
97 #endif // EFI_PROD_CODE
98 output->setValue(BENCH_MSG, false, /*isForce*/ true);
99 }
100
101 static void runBench(OutputPin *output, float onTimeMs, float offTimeMs, int count, bool swapOnOff) {
102 int onTimeUs = MS2US(std::max(0.1f, onTimeMs));
103 int offTimeUs = MS2US(std::max(0.1f, offTimeMs));
104
105 if (onTimeUs > TOO_FAR_INTO_FUTURE_US) {
106 firmwareError(ObdCode::CUSTOM_ERR_BENCH_PARAM, "onTime above limit %dus", TOO_FAR_INTO_FUTURE_US);
107 return;
108 }
109
110 efiPrintf("Running bench: ON_TIME=%d us OFF_TIME=%d us Counter=%d", onTimeUs, offTimeUs, count);
111 efiPrintf("output on %s", hwPortname(output->brainPin));
112
113 isRunningBench = true;
114 outputOnTheBenchTest = output;
115
116 for (int i = 0; isRunningBench && i < count; i++) {
117 engine->outputChannels.testBenchIter = i;
118 efitick_t nowNt = getTimeNowNt();
119 // start in a short time so the scheduler can precisely schedule the start event
120 efitick_t startTime = nowNt + US2NT(50);
121 efitick_t endTime = startTime + US2NT(onTimeUs);
122
123 auto const bstartAction{ swapOnOff ? action_s::make<benchOff>(output) : action_s::make<benchOn>(output) };
124 auto const bendAction{ swapOnOff ? action_s::make<benchOn>(output) : action_s::make<benchOff>(output) };
125
126 // Schedule both events
127 engine->scheduler.schedule("bstart", &benchSchedStart, startTime, bstartAction);
128 engine->scheduler.schedule("bend", &benchSchedEnd, endTime, bendAction);
129
130 // Wait one full cycle time for the event + delay to happen
131 chThdSleepMicroseconds(onTimeUs + offTimeUs);
132 }
133 /* last */
134 engine->outputChannels.testBenchIter++;
135
136 #if EFI_SIMULATOR
137 // save the current counters and durations after the test while the pin is still controlled
138 savedPinToggleCounter = output->pinToggleCounter;
139 savedDurationsInStateMs[0] = output->durationsInStateMs[0];
140 savedDurationsInStateMs[1] = output->durationsInStateMs[1];
141 #endif // EFI_SIMULATOR
142
143 efiPrintf("Done!");
144 outputOnTheBenchTest = nullptr;
145 isRunningBench = false;
146 }
147
148 // todo: migrate to smarter getOutputOnTheBenchTest() approach?
149 static volatile bool isBenchTestPending = false;
150 static bool widebandUpdatePending = false;
151 static uint8_t widebandUpdateHwId = 0;
152 static float globalOnTimeMs;
153 static float globalOffTimeMs;
154 static int globalCount;
155 static OutputPin* pinX;
156 static bool swapOnOff = false;
157
158 static chibios_rt::CounterSemaphore benchSemaphore(0);
159
160 static void pinbench(float ontimeMs, float offtimeMs, int iterations,
161 OutputPin* pinParam, bool p_swapOnOff = false)
162 {
163 globalOnTimeMs = ontimeMs;
164 globalOffTimeMs = offtimeMs;
165 #if EFI_SIMULATOR
166 globalCount = maxI(2, iterations);
167 #else
168 globalCount = iterations;
169 #endif // EFI_SIMULATOR
170 pinX = pinParam;
171 swapOnOff = p_swapOnOff;
172 // let's signal bench thread to wake up
173 isBenchTestPending = true;
174 benchSemaphore.signal();
175 }
176
177 static void cancelBenchTest() {
178 isRunningBench = false;
179 }
180
181 /*==========================================================================*/
182
183 static void doRunFuelInjBench(size_t humanIndex, float onTimeMs, float offTimeMs, int count) {
184 if (humanIndex < 1 || humanIndex > engineConfiguration->cylindersCount) {
185 efiPrintf("Invalid index: %d", humanIndex);
186 return;
187 }
188 pinbench(onTimeMs, offTimeMs, count,
189 &enginePins.injectors[humanIndex - 1]);
190 }
191
192 static void doRunSparkBench(size_t humanIndex, float onTime, float offTime, int count) {
193 if (humanIndex < 1 || humanIndex > engineConfiguration->cylindersCount) {
194 efiPrintf("Invalid index: %d", humanIndex);
195 return;
196 }
197 pinbench(onTime, offTime, count, &enginePins.coils[humanIndex - 1]);
198 }
199
200 static void doRunSolenoidBench(size_t humanIndex, float onTime, float offTime, int count) {
201 if (humanIndex < 1 || humanIndex > TCU_SOLENOID_COUNT) {
202 efiPrintf("Invalid index: %d", humanIndex);
203 return;
204 }
205 pinbench(onTime, offTime, count, &enginePins.tcuSolenoids[humanIndex - 1]);
206 }
207
208 static void doRunBenchTestLuaOutput(size_t humanIndex, float onTimeMs, float offTimeMs, int count) {
209 if (humanIndex < 1 || humanIndex > LUA_PWM_COUNT) {
210 efiPrintf("Invalid index: %d", humanIndex);
211 return;
212 }
213 pinbench(onTimeMs, offTimeMs, count,
214 &enginePins.luaOutputPins[humanIndex - 1]);
215 }
216
217 /**
218 * cylinder #2, 5ms ON, 1000ms OFF, repeat 3 times
219 * fuelInjBenchExt 2 5 1000 3
220 */
221 static void fuelInjBenchExt(float humanIndex, float onTimeMs, float offTimeMs, float count) {
222 doRunFuelInjBench((int)humanIndex, onTimeMs, offTimeMs, (int)count);
223 }
224
225 /**
226 * fuelbench 5 1000 2
227 */
228 static void fuelInjBench(float onTimeMs, float offTimeMs, float count) {
229 fuelInjBenchExt(1, onTimeMs, offTimeMs, count);
230 }
231
232 /**
233 * sparkbench2 1 5 1000 2
234 */
235 static void sparkBenchExt(float humanIndex, float onTime, float offTimeMs, float count) {
236 doRunSparkBench((int)humanIndex, onTime, offTimeMs, (int)count);
237 }
238
239 /**
240 * sparkbench 5 400 2
241 * 5 ms ON, 400 ms OFF, two times
242 */
243 static void sparkBench(float onTime, float offTimeMs, float count) {
244 sparkBenchExt(1, onTime, offTimeMs, count);
245 }
246
247 /**
248 * solenoid #2, 1000ms ON, 1000ms OFF, repeat 3 times
249 * tcusolbench 2 1000 1000 3
250 */
251 static void tcuSolenoidBench(float humanIndex, float onTime, float offTimeMs, float count) {
252 doRunSolenoidBench((int)humanIndex, onTime, offTimeMs, (int)count);
253 }
254
255 static void fanBenchExt(float onTimeMs) {
256 pinbench(onTimeMs, 100.0, 1, &enginePins.fanRelay);
257 }
258
259 void fanBench() {
260 fanBenchExt(BENCH_FAN_DURATION);
261 }
262
263 void fan2Bench() {
264 pinbench(3000.0, 100.0, 1, &enginePins.fanRelay2);
265 }
266
267 /**
268 * we are blinking for 16 seconds so that one can click the button and walk around to see the light blinking
269 */
270 void milBench() {
271 pinbench(500.0, 500.0, 16, &enginePins.checkEnginePin);
272 }
273
274 void starterRelayBench() {
275 pinbench(BENCH_STARTER_DURATION, 100.0, 1, &enginePins.starterControl);
276 }
277
278 static void fuelPumpBenchExt(float durationMs) {
279 pinbench(durationMs, 100.0, 1,
280 &enginePins.fuelPumpRelay);
281 }
282
283 void acRelayBench() {
284 pinbench(BENCH_AC_RELAY_DURATION, 100.0, 1, &enginePins.acRelay);
285 }
286
287 static void mainRelayBench() {
288 pinbench(BENCH_MAIN_RELAY_DURATION, 100.0, 1, &enginePins.mainRelay, true);
289 }
290
291 static void hpfpValveBench() {
292 pinbench(engineConfiguration->benchTestOnTime, engineConfiguration->benchTestOffTime, engineConfiguration->benchTestCount,
293 &enginePins.hpfpValve);
294 }
295
296 void fuelPumpBench() {
297 fuelPumpBenchExt(BENCH_FUEL_PUMP_DURATION);
298 }
299
300 static void vvtValveBench(int vvtIndex) {
301 #if EFI_VVT_PID
302 pinbench(BENCH_VVT_DURATION, 100.0, 1, getVvtOutputPin(vvtIndex));
303 #endif // EFI_VVT_PID
304 }
305
306 static void requestWidebandUpdate(int hwIndex)
307 {
308 widebandUpdateHwId = hwIndex;
309 widebandUpdatePending = true;
310 benchSemaphore.signal();
311 }
312
313 class BenchController : public ThreadController<UTILITY_THREAD_STACK_SIZE> {
314 public:
315 BenchController() : ThreadController("BenchTest", PRIO_BENCH_TEST) { }
316 private:
317 void ThreadTask() override {
318 while (true) {
319 benchSemaphore.wait();
320
321 assertStackVoid("Bench", ObdCode::STACK_USAGE_MISC, EXPECTED_REMAINING_STACK);
322
323 if (isBenchTestPending) {
324 isBenchTestPending = false;
325 runBench(pinX, globalOnTimeMs, globalOffTimeMs, globalCount, swapOnOff);
326 }
327
328 if (widebandUpdatePending) {
329 #if EFI_WIDEBAND_FIRMWARE_UPDATE && EFI_CAN_SUPPORT
330 updateWidebandFirmware(widebandUpdateHwId);
331 #endif
332 widebandUpdatePending = false;
333 }
334 }
335 }
336 };
337
338 static BenchController instance;
339
340 static void auxOutBench(int index) {
341 // todo!
342 UNUSED(index);
343 }
344
345 #if EFI_HD_ACR
346 static void hdAcrBench(int index) {
347 OutputPin* pin = index == 0 ? &enginePins.harleyAcr : &enginePins.harleyAcr2;
348 pinbench(BENCH_AC_RELAY_DURATION, 100.0, 1, pin);
349 }
350 #endif // EFI_HD_ACR
351
352 int luaCommandCounters[LUA_BUTTON_COUNT] = {};
353
354 void handleBenchCategory(uint16_t index) {
355 switch(index) {
356 case BENCH_VVT0_VALVE:
357 vvtValveBench(0);
358 return;
359 case BENCH_VVT1_VALVE:
360 vvtValveBench(1);
361 return;
362 case BENCH_VVT2_VALVE:
363 vvtValveBench(2);
364 return;
365 case BENCH_VVT3_VALVE:
366 vvtValveBench(3);
367 return;
368 case BENCH_AUXOUT0:
369 auxOutBench(0);
370 return;
371 case BENCH_AUXOUT1:
372 auxOutBench(1);
373 return;
374 case BENCH_AUXOUT2:
375 auxOutBench(2);
376 return;
377 case BENCH_AUXOUT3:
378 auxOutBench(3);
379 return;
380 case BENCH_AUXOUT4:
381 auxOutBench(4);
382 return;
383 case BENCH_AUXOUT5:
384 auxOutBench(5);
385 return;
386 case BENCH_AUXOUT6:
387 auxOutBench(6);
388 return;
389 case BENCH_AUXOUT7:
390 auxOutBench(7);
391 return;
392 case LUA_COMMAND_1:
393 luaCommandCounters[0]++;
394 return;
395 case LUA_COMMAND_2:
396 luaCommandCounters[1]++;
397 return;
398 case LUA_COMMAND_3:
399 luaCommandCounters[2]++;
400 return;
401 case LUA_COMMAND_4:
402 luaCommandCounters[3]++;
403 return;
404 #if EFI_LTFT_CONTROL
405 case LTFT_RESET:
406 resetLongTermFuelTrim();
407 return;
408 case LTFT_APPLY_TO_VE:
409 applyLongTermFuelTrimToVe();
410 return;
411 case LTFT_DEV_POKE:
412 devPokeLongTermFuelTrim();
413 return;
414 #endif // EFI_LTFT_CONTROL
415 #if EFI_HD_ACR
416 case HD_ACR:
417 hdAcrBench(0);
418 return;
419 case HD_ACR2:
420 hdAcrBench(1);
421 return;
422 #endif // EFI_HD_ACR
423 case BENCH_HPFP_VALVE:
424 hpfpValveBench();
425 return;
426 case BENCH_FUEL_PUMP:
427 // cmd_test_fuel_pump
428 fuelPumpBench();
429 return;
430 case BENCH_MAIN_RELAY:
431 mainRelayBench();
432 return;
433 case BENCH_STARTER_ENABLE_RELAY:
434 starterRelayBench();
435 return;
436 case BENCH_CHECK_ENGINE_LIGHT:
437 // cmd_test_check_engine_light
438 milBench();
439 return;
440 case BENCH_AC_COMPRESSOR_RELAY:
441 acRelayBench();
442 return;
443 case BENCH_FAN_RELAY:
444 fanBench();
445 return;
446 case BENCH_IDLE_VALVE:
447 // cmd_test_idle_valve
448 #if EFI_IDLE_CONTROL
449 startIdleBench();
450 #endif /* EFI_IDLE_CONTROL */
451 return;
452 case BENCH_FAN_RELAY_2:
453 fan2Bench();
454 return;
455 case BENCH_CANCEL:
456 cancelBenchTest();
457 return;
458 default:
459 criticalError("Unexpected bench function %d", index);
460 }
461 }
462
463 int getSavedBenchTestPinStates(uint32_t durationsInStateMs[2]) {
464 #if EFI_SIMULATOR
465 durationsInStateMs[0] = savedDurationsInStateMs[0];
466 durationsInStateMs[1] = savedDurationsInStateMs[1];
467 return savedPinToggleCounter;
468 #else
469 UNUSED(durationsInStateMs);
470 return 0;
471 #endif // EFI_SIMULATOR
472 }
473
474 static void handleCommandX14(uint16_t index) {
475 // todo: define ts_14_command magic constants and use those in tunerstudio.template.ini file!
476 switch (index) {
477 case TS_GRAB_PEDAL_UP:
478 grabPedalIsUp();
479 return;
480 case TS_GRAB_PEDAL_WOT:
481 grabPedalIsWideOpen();
482 return;
483 case TS_GRAB_TPS_CLOSED:
484 grapTps1PrimaryIsClosed();
485 return;
486 case TS_GRAB_TPS_OPEN:
487 grapTps1PrimaryIsOpen();
488 return;
489 case TS_RESET_TLE8888:
490 #if (BOARD_TLE8888_COUNT > 0)
491 tle8888_req_init();
492 #endif
493 return;
494 case TS_RESET_MC33810:
495 #if EFI_PROD_CODE && (BOARD_MC33810_COUNT > 0)
496 mc33810_req_init();
497 #endif
498 return;
499 #if EFI_SHAFT_POSITION_INPUT
500 case TS_START_STOP_ENGINE:
501 // this is different from starter relay bench test!
502 startStopButtonToggle();
503 return;
504 #endif // EFI_SHAFT_POSITION_INPUT
505 case TS_WRITE_FLASH:
506 // cmd_write_config
507 #if EFI_CONFIGURATION_STORAGE
508 writeToFlashNow();
509 #endif /* EFI_CONFIGURATION_STORAGE */
510 return;
511 case TS_TRIGGER_STIMULATOR_ENABLE:
512 #if EFI_EMULATE_POSITION_SENSORS == TRUE
513 enableTriggerStimulator();
514 #endif /* EFI_EMULATE_POSITION_SENSORS == TRUE */
515 return;
516 case TS_TRIGGER_STIMULATOR_DISABLE:
517 #if EFI_EMULATE_POSITION_SENSORS == TRUE
518 disableTriggerStimulator();
519 #endif /* EFI_EMULATE_POSITION_SENSORS == TRUE */
520 return;
521 case TS_EXTERNAL_TRIGGER_STIMULATOR_ENABLE:
522 #if EFI_EMULATE_POSITION_SENSORS == TRUE
523 enableExternalTriggerStimulator();
524 #endif /* EFI_EMULATE_POSITION_SENSORS == TRUE */
525 return;
526 /*
527 case TS_ETB_RESET:
528 #if EFI_ELECTRONIC_THROTTLE_BODY == TRUE
529 #if EFI_PROD_CODE
530 etbPidReset();
531 #endif
532 #endif // EFI_ELECTRONIC_THROTTLE_BODY
533 return;
534 */
535 #if EFI_ELECTRONIC_THROTTLE_BODY
536 case TS_ETB_AUTOCAL_0:
537 etbAutocal(DC_Throttle1);
538 return;
539 case TS_ETB_AUTOCAL_1:
540 etbAutocal(DC_Throttle2);
541 return;
542 case TS_ETB_AUTOCAL_0_FAST:
543 etbAutocal(DC_Throttle1, false);
544 return;
545 case TS_ETB_AUTOCAL_1_FAST:
546 etbAutocal(DC_Throttle2, false);
547 return;
548 case TS_ETB_START_AUTOTUNE:
549 engine->etbAutoTune = true;
550 return;
551 case TS_ETB_STOP_AUTOTUNE:
552 engine->etbAutoTune = false;
553 #if EFI_TUNER_STUDIO
554 tsCalibrationSetIdle();
555 #endif // EFI_TUNER_STUDIO
556 return;
557 case TS_ETB_DISABLE_JAM_DETECT:
558 engine->etbIgnoreJamProtection = true;
559 return;
560 case TS_EWG_AUTOCAL_0:
561 etbAutocal(DC_Wastegate);
562 return;
563 case TS_EWG_AUTOCAL_0_FAST:
564 etbAutocal(DC_Wastegate, false);
565 return;
566 #endif // EFI_ELECTRONIC_THROTTLE_BODY
567 case TS_WIDEBAND_UPDATE:
568 // broadcast, for old WBO FWs
569 requestWidebandUpdate(0xff);
570 return;
571 case COMMAND_X14_UNUSED_15:
572 return;
573
574 #if EFI_PROD_CODE && EFI_FILE_LOGGING
575 case TS_SD_MOUNT_PC:
576 sdCardRequestMode(SD_MODE_PC);
577 return;
578 case TS_SD_MOUNT_ECU:
579 sdCardRequestMode(SD_MODE_ECU);
580 return;
581 case TS_SD_UNMOUNT:
582 sdCardRequestMode(SD_MODE_UNMOUNT);
583 return;
584 case TS_SD_FORMAT:
585 sdCardRequestMode(SD_MODE_FORMAT);
586 return;
587 case TS_SD_DELETE_REPORTS:
588 sdCardRemoveReportFiles();
589 return;
590 #endif // EFI_FILE_LOGGING
591
592 default:
593 criticalError("Unexpected bench x14 %d", index);
594 }
595 }
596
597 extern bool rebootForPresetPending;
598
599 static void applyPreset(int index) {
600 setEngineType(index);
601 #if EFI_TUNER_STUDIO
602 onApplyPreset();
603 #endif // EFI_TUNER_STUDIO
604 }
605
606 // placeholder to force custom_board_ts_command migration
607 void boardTsAction(uint16_t index) { UNUSED(index); }
608
609 #if EFI_CAN_SUPPORT
610 /**
611 * for example to bench test injector 1
612 * 0x77000C 0x66 0x00 ?? ?? ?? ??
613 * 0x77000C 0x66 0x00 0x14 0x00 0x09 0x00 start/stop engine
614 *
615 * See also more complicated ISO-TP CANBus wrapper of complete TS protocol
616 */
617 static void processCanUserControl(const CANRxFrame& frame) {
618 // reserved data8[1]
619 uint16_t subsystem = getTwoBytesLsb(frame, 2);
620 uint16_t index = getTwoBytesLsb(frame, 4);
621 executeTSCommand(subsystem, index);
622 }
623
624 union FloatIntBytes {
625 float f;
626 int i;
627 uint8_t bytes[sizeof(float)];
628 };
629
630 static void processCanSetCalibration(const CANRxFrame& frame) {
631 // todo allow changes of scalar settings via CANbus
632 UNUSED(frame);
633 }
634 /**
635 * CANbus protocol to query scalar calibrations using hash keys
636 *
637 * see fields_api.txt for well-known fields
638 * see generated_fields_api_header.h for corresponding hashes
639 */
640 static void processCanRequestCalibration(const CANRxFrame& frame) {
641 #if EFI_LUA_LOOKUP
642 int hash = getFourBytesLsb(frame, 2);
643 efiPrintf("processCanRequestCalibration=%x", hash);
644 FloatIntBytes fb;
645 fb.f = getConfigValueByHash(hash);
646
647 CanTxMessage msg(CanCategory::BENCH_TEST, (int)bench_test_packet_ids_e::ECU_GET_CALIBRATION, 8, /*bus*/0, /*isExtended*/true);
648 for (size_t i = 0;i<sizeof(float);i++) {
649 msg[4 + i] = fb.bytes[i];
650 }
651
652 fb.i = hash;
653 // first half of the packed is copy of that hash value so that recipients know what they are processing
654 for (size_t i = 0;i<sizeof(float);i++) {
655 msg[i] = fb.bytes[i];
656 }
657 #endif // EFI_LUA_LOOKUP
658 }
659
660 void processCanEcuControl(const CANRxFrame& frame) {
661 if (frame.data8[0] != (int)bench_test_magic_numbers_e::BENCH_HEADER) {
662 return;
663 }
664
665 int eid = CAN_EID(frame);
666 if (eid == (int)bench_test_packet_ids_e::ECU_SET_CALIBRATION) {
667 processCanSetCalibration(frame);
668 } else if (eid == (int)bench_test_packet_ids_e::ECU_REQ_CALIBRATION) {
669 processCanRequestCalibration(frame);
670 } else if (eid == (int)bench_test_packet_ids_e::ECU_CAN_BUS_USER_CONTROL) {
671 processCanUserControl(frame);
672 }
673 }
674
675 #endif // EFI_CAN_SUPPORT
676
677 std::optional<setup_custom_board_ts_command_override_type> custom_board_ts_command;
678
679 void executeTSCommand(uint16_t subsystem, uint16_t index) {
680 efiPrintf("IO test subsystem=%d index=%d", subsystem, index);
681
682 bool running = !engine->rpmCalculator.isStopped();
683
684 switch (subsystem) {
685 case TS_CLEAR_WARNINGS:
686 clearWarnings();
687 break;
688
689 case TS_DEBUG_MODE:
690 engineConfiguration->debugMode = (debug_mode_e)index;
691 break;
692
693 case TS_IGNITION_CATEGORY:
694 if (!running) {
695 doRunSparkBench(index, engineConfiguration->benchTestOnTime,
696 engineConfiguration->benchTestOffTime, engineConfiguration->benchTestCount);
697 }
698 break;
699
700 case TS_INJECTOR_CATEGORY:
701 if (!running) {
702 doRunFuelInjBench(index, engineConfiguration->benchTestOnTime,
703 engineConfiguration->benchTestOffTime, engineConfiguration->benchTestCount);
704 }
705 break;
706
707 case TS_SOLENOID_CATEGORY:
708 if (!running) {
709 doRunSolenoidBench(index, 1000.0,
710 1000.0, engineConfiguration->benchTestCount);
711 }
712 break;
713
714 case TS_LUA_OUTPUT_CATEGORY:
715 if (!running) {
716 doRunBenchTestLuaOutput(index, 4.0,
717 engineConfiguration->benchTestOffTime, engineConfiguration->benchTestCount);
718 }
719 break;
720
721 case TS_X14:
722 handleCommandX14(index);
723 break;
724 #if EFI_CAN_SUPPORT
725 case TS_WIDEBAND:
726 setWidebandOffset(0xff, index);
727 break;
728 case TS_WIDEBAND_SET_IDX_BY_ID:
729 {
730 uint8_t hwIndex = index >> 8;
731 uint8_t canIndex = index & 0xff;
732
733 // Hack until we fix canReWidebandHwIndex and set "Broadcast" to 0xff
734 // TODO:
735 hwIndex = hwIndex < 8 ? hwIndex : 0xff;
736 setWidebandOffset(hwIndex, canIndex);
737 }
738 break;
739 case TS_WIDEBAND_SET_SENS_BY_ID:
740 {
741 uint8_t hwIndex = index >> 8;
742 uint8_t sensType = index & 0xff;
743
744 // Hack until we fix canReWidebandHwIndex and set "Broadcast" to 0xff
745 // TODO:
746 hwIndex = hwIndex < 8 ? hwIndex : 0xff;
747 setWidebandSensorType(hwIndex, sensType);
748 }
749 break;
750 case TS_WIDEBAND_PING_BY_ID:
751 pingWideband(index >> 8);
752 break;
753
754 case TS_WIDEBAND_FLASH_BY_ID:
755 {
756 uint8_t hwIndex = index >> 8;
757
758 // Hack until we fix canReWidebandHwIndex and set "Broadcast" to 0xff
759 // TODO:
760 widebandUpdateHwId = hwIndex < 8 ? hwIndex : 0xff;
761 requestWidebandUpdate(widebandUpdateHwId);
762 }
763 break;
764 #endif // EFI_CAN_SUPPORT
765 case TS_BENCH_CATEGORY:
766 handleBenchCategory(index);
767 break;
768
769 case TS_SET_ENGINE_TYPE:
770 applyPreset(index);
771 break;
772
773 case TS_BOARD_ACTION:
774 if (custom_board_ts_command.has_value()) {
775 custom_board_ts_command.value()(subsystem, index);
776 }
777 break;
778
779 case TS_SET_DEFAULT_ENGINE:
780 applyPreset((int)DEFAULT_ENGINE_TYPE);
781 break;
782
783 case TS_STOP_ENGINE:
784 doScheduleStopEngine(StopRequestedReason::TsCommand);
785 break;
786
787 case 0xba:
788 #if EFI_PROD_CODE && EFI_DFU_JUMP
789 jump_to_bootloader();
790 #endif /* EFI_DFU_JUMP */
791 break;
792
793 case REBOOT_COMMAND:
794 #if EFI_PROD_CODE
795 rebootNow();
796 #endif /* EFI_PROD_CODE */
797 break;
798
799 #if EFI_USE_OPENBLT
800 case 0xbc:
801 /* Jump to OpenBLT if present */
802 jump_to_openblt();
803 break;
804 #endif
805
806 default:
807 criticalError("Unexpected bench subsystem %d %d", subsystem, index);
808 }
809 }
810
811 void onConfigurationChangeBenchTest() {
812 // default values if configuration was not specified
813 if (engineConfiguration->benchTestOnTime == 0) {
814 engineConfiguration->benchTestOnTime = 4;
815 }
816
817 if (engineConfiguration->benchTestOffTime < 5) {
818 engineConfiguration->benchTestOffTime = 500;
819 }
820
821 if (engineConfiguration->benchTestCount < 1) {
822 engineConfiguration->benchTestCount = 3;
823 }
824 }
825
826 void initBenchTest() {
827 addConsoleAction("fuelpumpbench", fuelPumpBench);
828 addConsoleActionF("fuelpumpbench2", fuelPumpBenchExt);
829
830 addConsoleActionFFF(CMD_FUEL_BENCH, fuelInjBench);
831 addConsoleActionFFFF("fuelbench2", fuelInjBenchExt);
832
833 addConsoleActionFFF(CMD_SPARK_BENCH, sparkBench);
834 addConsoleActionFFFF("sparkbench2", sparkBenchExt);
835
836 addConsoleActionFFFF("tcusolbench", tcuSolenoidBench);
837
838 addConsoleAction(CMD_AC_RELAY_BENCH, acRelayBench);
839
840 addConsoleAction(CMD_FAN_BENCH, fanBench);
841 addConsoleAction(CMD_FAN2_BENCH, fan2Bench);
842 addConsoleActionF("fanbench2", fanBenchExt);
843
844 addConsoleAction("mainrelaybench", mainRelayBench);
845
846 #if EFI_CAN_SUPPORT
847 #if EFI_WIDEBAND_FIRMWARE_UPDATE
848 addConsoleActionI("update_wideband", requestWidebandUpdate);
849 #endif // EFI_WIDEBAND_FIRMWARE_UPDATE
850 addConsoleActionII("set_wideband_index", [](int hwIndex, int index) { setWidebandOffset(hwIndex, index); });
851 #endif // EFI_CAN_SUPPORT
852
853 addConsoleAction(CMD_STARTER_BENCH, starterRelayBench);
854 addConsoleAction(CMD_MIL_BENCH, milBench);
855 addConsoleAction(CMD_HPFP_BENCH, hpfpValveBench);
856
857 #if EFI_CAN_SUPPORT
858 addConsoleActionI("ping_wideband", [](int index) {
859 pingWideband(index);
860 });
861 #endif // EFI_CAN_SUPPORT
862
863 #if EFI_LUA
864 // this commands facilitates TS Lua Button scripts development
865 addConsoleActionI("lua_button", [](int index) {
866 if (index < 0 || index > LUA_BUTTON_COUNT)
867 return;
868 luaCommandCounters[index - 1]++;
869 });
870 addConsoleActionFFFF("luabench2", [](float humanIndex, float onTime, float offTimeMs, float count) {
871 doRunBenchTestLuaOutput((int)humanIndex, onTime, offTimeMs, (int)count);
872 });
873 #endif // EFI_LUA
874 instance.start();
875 onConfigurationChangeBenchTest();
876 }
877
878 #endif /* EFI_UNIT_TEST */
879