GCC Code Coverage Report


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