| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file engine_sniffer.cpp | |||
| 3 | * @brief rusEfi console wave sniffer logic | |||
| 4 | * | |||
| 5 | * Here we have our own build-in logic analyzer. The data we aggregate here is sent to the | |||
| 6 | * java UI rusEfi Console so that it can be displayed nicely in the Sniffer tab. | |||
| 7 | * | |||
| 8 | * Both external events (see logic_analyzer.cpp) and internal (see signal executors) are supported | |||
| 9 | * | |||
| 10 | * @date Jun 23, 2013 | |||
| 11 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 12 | * | |||
| 13 | * This file is part of rusEfi - see http://rusefi.com | |||
| 14 | * | |||
| 15 | * rusEfi is free software; you can redistribute it and/or modify it under the terms of | |||
| 16 | * the GNU General Public License as published by the Free Software Foundation; either | |||
| 17 | * version 3 of the License, or (at your option) any later version. | |||
| 18 | * | |||
| 19 | * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without | |||
| 20 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 21 | * GNU General Public License for more details. | |||
| 22 | * | |||
| 23 | * You should have received a copy of the GNU General Public License along with this program. | |||
| 24 | * If not, see <http://www.gnu.org/licenses/>. | |||
| 25 | */ | |||
| 26 | ||||
| 27 | #include "pch.h" | |||
| 28 | ||||
| 29 | #include "engine_sniffer.h" | |||
| 30 | ||||
| 31 | // a bit weird because of conditional compilation | |||
| 32 | static char shaft_signal_msg_index[15]; | |||
| 33 | ||||
| 34 | #if EFI_ENGINE_SNIFFER | |||
| 35 | #define addEngineSnifferEvent(name, msg) { if (getTriggerCentral()->isEngineSnifferEnabled) { waveChart.addEvent3((name), (msg)); } } | |||
| 36 | #else | |||
| 37 | #define addEngineSnifferEvent(name, msg) { UNUSED(name); } | |||
| 38 | #endif /* EFI_ENGINE_SNIFFER */ | |||
| 39 | ||||
| 40 | #if EFI_ENGINE_SNIFFER | |||
| 41 | ||||
| 42 | #include "eficonsole.h" | |||
| 43 | #include "status_loop.h" | |||
| 44 | ||||
| 45 | #define CHART_DELIMETER '!' | |||
| 46 | extern WaveChart waveChart; | |||
| 47 | ||||
| 48 | /** | |||
| 49 | * This is the number of events in the digital chart which would be displayed | |||
| 50 | * on the 'digital sniffer' pane | |||
| 51 | */ | |||
| 52 | #if EFI_PROD_CODE | |||
| 53 | #define WAVE_LOGGING_SIZE 5000 | |||
| 54 | #else | |||
| 55 | #define WAVE_LOGGING_SIZE 35000 | |||
| 56 | #endif | |||
| 57 | ||||
| 58 | static char WAVE_LOGGING_BUFFER[WAVE_LOGGING_SIZE] CCM_OPTIONAL; | |||
| 59 | ||||
| 60 | int waveChartUsedSize; | |||
| 61 | ||||
| 62 | /** | |||
| 63 | * We want to skip some engine cycles to skip what was scheduled before parameters were changed | |||
| 64 | */ | |||
| 65 | static uint32_t skipUntilEngineCycle = 0; | |||
| 66 | ||||
| 67 | #if ! EFI_UNIT_TEST | |||
| 68 | extern WaveChart waveChart; | |||
| 69 | static void resetNow() { | |||
| 70 | skipUntilEngineCycle = getRevolutionCounter() + 3; | |||
| 71 | waveChart.reset(); | |||
| 72 | } | |||
| 73 | #endif // EFI_UNIT_TEST | |||
| 74 | ||||
| 75 | 1 | WaveChart::WaveChart() : logging("wave chart", WAVE_LOGGING_BUFFER, sizeof(WAVE_LOGGING_BUFFER)) { | ||
| 76 | 1 | } | ||
| 77 | ||||
| 78 | 584 | void WaveChart::init() { | ||
| 79 | 584 | isInitialized = true; | ||
| 80 | 584 | reset(); | ||
| 81 | 584 | } | ||
| 82 | ||||
| 83 | 584 | void WaveChart::reset() { | ||
| 84 | 584 | logging.reset(); | ||
| 85 | 584 | counter = 0; | ||
| 86 | 584 | startTimeNt = 0; | ||
| 87 | 584 | collectingData = false; | ||
| 88 | 584 | logging.appendPrintf( "%s%s", PROTOCOL_ENGINE_SNIFFER, LOG_DELIMITER); | ||
| 89 | 584 | } | ||
| 90 | ||||
| 91 | 1696 | void WaveChart::startDataCollection() { | ||
| 92 | 1696 | collectingData = true; | ||
| 93 | 1696 | } | ||
| 94 | ||||
| 95 | ✗ | bool WaveChart::isStartedTooLongAgo() const { | ||
| 96 | /** | |||
| 97 | * Say at 300rpm we should get at least four events per revolution. | |||
| 98 | * That's 300/60*4=20 events per second | |||
| 99 | * engineChartSize/20 is the longest meaningful chart. | |||
| 100 | * | |||
| 101 | */ | |||
| 102 | ✗ | efidur_t chartDurationNt = getTimeNowNt() - startTimeNt; | ||
| 103 | ✗ | return startTimeNt != 0 && NT2US(chartDurationNt) > engineConfiguration->engineChartSize * 1000000 / 20; | ||
| 104 | } | |||
| 105 | ||||
| 106 | 100336 | bool WaveChart::isFull() const { | ||
| 107 | 100336 | return counter >= engineConfiguration->engineChartSize; | ||
| 108 | } | |||
| 109 | ||||
| 110 | 1 | int WaveChart::getSize() { | ||
| 111 | 1 | return counter; | ||
| 112 | } | |||
| 113 | ||||
| 114 | #if ! EFI_UNIT_TEST | |||
| 115 | static void printStatus() { | |||
| 116 | efiPrintf("engine sniffer: %s", boolToString(getTriggerCentral()->isEngineSnifferEnabled)); | |||
| 117 | efiPrintf("engine sniffer size=%lu", engineConfiguration->engineChartSize); | |||
| 118 | } | |||
| 119 | ||||
| 120 | void setChartSize(int newSize) { | |||
| 121 | if (newSize < 5) { | |||
| 122 | return; | |||
| 123 | } | |||
| 124 | engineConfiguration->engineChartSize = newSize; | |||
| 125 | printStatus(); | |||
| 126 | } | |||
| 127 | #endif // EFI_UNIT_TEST | |||
| 128 | ||||
| 129 | ✗ | void WaveChart::publishIfFull() { | ||
| 130 | ✗ | if (isFull() || isStartedTooLongAgo()) { | ||
| 131 | ✗ | publish(); | ||
| 132 | ✗ | reset(); | ||
| 133 | } | |||
| 134 | ✗ | } | ||
| 135 | ||||
| 136 | ✗ | void WaveChart::publish() { | ||
| 137 | #if EFI_ENGINE_SNIFFER | |||
| 138 | ✗ | logging.appendPrintf( LOG_DELIMITER); | ||
| 139 | ✗ | waveChartUsedSize = logging.loggingSize(); | ||
| 140 | ||||
| 141 | ✗ | if (getTriggerCentral()->isEngineSnifferEnabled) { | ||
| 142 | ✗ | scheduleLogging(&logging); | ||
| 143 | } | |||
| 144 | #endif /* EFI_ENGINE_SNIFFER */ | |||
| 145 | ✗ | } | ||
| 146 | ||||
| 147 | /** | |||
| 148 | * @brief Register an event for digital sniffer | |||
| 149 | */ | |||
| 150 | 100336 | void WaveChart::addEvent3(const char *name, const char * msg) { | ||
| 151 | #if EFI_TEXT_LOGGING | |||
| 152 | 100336 | ScopePerf perf(PE::EngineSniffer); | ||
| 153 |
1/1✓ Branch 1 taken 100336 times.
|
100336 | efitick_t nowNt = getTimeNowNt(); | |
| 154 | ||||
| 155 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 100336 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 100336 times.
|
100336 | if (nowNt < pauseEngineSnifferUntilNt) { |
| 156 | ✗ | return; | ||
| 157 | } | |||
| 158 |
2/3✓ Branch 1 taken 100336 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 100336 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 100336 times.
|
100336 | if (!getTriggerCentral()->isEngineSnifferEnabled) { |
| 159 | ✗ | return; | ||
| 160 | } | |||
| 161 |
2/7✗ Branch 0 not taken.
✓ Branch 1 taken 100336 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 100336 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 100336 times.
|
100336 | if (skipUntilEngineCycle != 0 && getRevolutionCounter() < skipUntilEngineCycle) |
| 162 | ✗ | return; | ||
| 163 | #if EFI_SIMULATOR | |||
| 164 | if (!collectingData) { | |||
| 165 | return; | |||
| 166 | } | |||
| 167 | #endif | |||
| 168 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 100336 times.
✗ Branch 3 not taken.
|
100336 | efiAssertVoid(ObdCode::CUSTOM_ERR_6651, name!=NULL, "WC: NULL name"); | |
| 169 | ||||
| 170 | #if EFI_PROD_CODE | |||
| 171 | efiAssertVoid(ObdCode::CUSTOM_ERR_6652, getCurrentRemainingStack() > 32, "lowstck#2c"); | |||
| 172 | #endif /* EFI_PROD_CODE */ | |||
| 173 | ||||
| 174 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 100336 times.
✗ Branch 3 not taken.
|
100336 | efiAssertVoid(ObdCode::CUSTOM_ERR_6653, isInitialized, "chart not initialized"); | |
| 175 | ||||
| 176 |
3/3✓ Branch 1 taken 100336 times.
✓ Branch 3 taken 73364 times.
✓ Branch 4 taken 26972 times.
|
2/2✓ Decision 'true' taken 73364 times.
✓ Decision 'false' taken 26972 times.
|
100336 | if (isFull()) { |
| 177 | 73364 | return; | ||
| 178 | } | |||
| 179 | ||||
| 180 | // we have multiple threads writing to the same output buffer | |||
| 181 | chibios_rt::CriticalSectionLocker csl; | |||
| 182 | ||||
| 183 |
2/2✓ Branch 0 taken 116 times.
✓ Branch 1 taken 26856 times.
|
2/2✓ Decision 'true' taken 116 times.
✓ Decision 'false' taken 26856 times.
|
26972 | if (counter == 0) { |
| 184 | 116 | startTimeNt = nowNt; | ||
| 185 | } | |||
| 186 | 26972 | counter++; | ||
| 187 | ||||
| 188 | /** | |||
| 189 | * We want smaller times within a chart in order to reduce packet size. | |||
| 190 | */ | |||
| 191 | /** | |||
| 192 | * todo: migrate to binary fractions in order to eliminate | |||
| 193 | * this division? I do not like division | |||
| 194 | * | |||
| 195 | * at least that's 32 bit division now | |||
| 196 | */ | |||
| 197 | 26972 | uint32_t diffNt = nowNt - startTimeNt; | ||
| 198 | 26972 | uint32_t time100 = NT2US(diffNt / ENGINE_SNIFFER_UNIT_US); | ||
| 199 | ||||
| 200 |
1/2✓ Branch 1 taken 26972 times.
✗ Branch 2 not taken.
|
1/2✓ Decision 'true' taken 26972 times.
✗ Decision 'false' not taken.
|
26972 | if (logging.remainingSize() > 35) { |
| 201 | /** | |||
| 202 | * printf is a heavy method, append is used here as a performance optimization | |||
| 203 | */ | |||
| 204 |
1/1✓ Branch 1 taken 26972 times.
|
26972 | logging.appendFast(name); | |
| 205 | 26972 | logging.appendChar(CHART_DELIMETER); | ||
| 206 |
1/1✓ Branch 1 taken 26972 times.
|
26972 | logging.appendFast(msg); | |
| 207 | 26972 | logging.appendChar(CHART_DELIMETER); | ||
| 208 | // time100 -= startTime100; | |||
| 209 | ||||
| 210 |
1/1✓ Branch 1 taken 26972 times.
|
26972 | itoa10(timeBuffer, time100); | |
| 211 |
1/1✓ Branch 1 taken 26972 times.
|
26972 | logging.appendFast(timeBuffer); | |
| 212 | 26972 | logging.appendChar(CHART_DELIMETER); | ||
| 213 | 26972 | logging.terminate(); | ||
| 214 | } | |||
| 215 | #endif /* EFI_TEXT_LOGGING */ | |||
| 216 | } | |||
| 217 | ||||
| 218 | ✗ | void initWaveChart(WaveChart *chart) { | ||
| 219 | ✗ | strcpy((char*) shaft_signal_msg_index, "x_"); | ||
| 220 | /** | |||
| 221 | * constructor does not work because we need specific initialization order | |||
| 222 | */ | |||
| 223 | ✗ | chart->init(); | ||
| 224 | ||||
| 225 | #if EFI_HISTOGRAMS | |||
| 226 | initHistogram(&engineSnifferHisto, "engine sniffer"); | |||
| 227 | #endif /* EFI_HISTOGRAMS */ | |||
| 228 | ||||
| 229 | #if ! EFI_UNIT_TEST | |||
| 230 | addConsoleActionI("chartsize", setChartSize); | |||
| 231 | // this is used by HW CI | |||
| 232 | addConsoleAction(CMD_RESET_ENGINE_SNIFFER, resetNow); | |||
| 233 | #endif // EFI_UNIT_TEST | |||
| 234 | ✗ | } | ||
| 235 | ||||
| 236 | #endif /* EFI_ENGINE_SNIFFER */ | |||
| 237 | ||||
| 238 | 20017 | void addEngineSnifferOutputPinEvent(NamedOutputPin *pin, FrontDirection frontDirection) { | ||
| 239 |
1/2✓ Branch 0 taken 20017 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 20017 times.
✗ Decision 'false' not taken.
|
20017 | if (!engineConfiguration->engineSnifferFocusOnInputs) { |
| 240 |
3/4✓ Branch 1 taken 20017 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9687 times.
✓ Branch 4 taken 10330 times.
|
20017 | addEngineSnifferEvent(pin->getShortName(), frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); | |
| 241 | } | |||
| 242 | 20017 | } | ||
| 243 | ||||
| 244 | 1696 | void addEngineSnifferTdcEvent(int rpm) { | ||
| 245 | static char rpmBuffer[_MAX_FILLER]; | |||
| 246 | 1696 | itoa10(rpmBuffer, rpm); | ||
| 247 | #if EFI_ENGINE_SNIFFER | |||
| 248 | 1696 | waveChart.startDataCollection(); | ||
| 249 | #endif | |||
| 250 |
1/2✓ Branch 1 taken 1696 times.
✗ Branch 2 not taken.
|
1696 | addEngineSnifferEvent(TOP_DEAD_CENTER_MESSAGE, (char* ) rpmBuffer); | |
| 251 | 1696 | } | ||
| 252 | ||||
| 253 | ✗ | void addEngineSnifferLogicAnalyzerEvent(int laIndex, FrontDirection frontDirection) { | ||
| 254 | extern const char *laNames[]; | |||
| 255 | ✗ | const char *name = laNames[laIndex]; | ||
| 256 | ||||
| 257 | ✗ | addEngineSnifferEvent(name, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); | ||
| 258 | ✗ | } | ||
| 259 | ||||
| 260 | 71918 | void addEngineSnifferCrankEvent(int wheelIndex, int triggerEventIndex, FrontDirection frontDirection) { | ||
| 261 | static const char *crankName[2] = { PROTOCOL_CRANK1, PROTOCOL_CRANK2 }; | |||
| 262 | ||||
| 263 |
2/2✓ Branch 0 taken 35957 times.
✓ Branch 1 taken 35961 times.
|
71918 | shaft_signal_msg_index[0] = frontDirection == FrontDirection::UP ? 'u' : 'd'; | |
| 264 | // shaft_signal_msg_index[1] is assigned once and forever in the init method below | |||
| 265 | 71918 | itoa10(&shaft_signal_msg_index[2], triggerEventIndex); | ||
| 266 | ||||
| 267 |
1/2✓ Branch 1 taken 71918 times.
✗ Branch 2 not taken.
|
71918 | addEngineSnifferEvent(crankName[wheelIndex], (char* ) shaft_signal_msg_index); | |
| 268 | 71918 | } | ||
| 269 | ||||
| 270 | 6705 | void addEngineSnifferVvtEvent(int vvtIndex, FrontDirection frontDirection) { | ||
| 271 | extern const char *vvtNames[]; | |||
| 272 | 6705 | const char *vvtName = vvtNames[vvtIndex]; | ||
| 273 | ||||
| 274 |
3/4✓ Branch 1 taken 6705 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3357 times.
✓ Branch 4 taken 3348 times.
|
6705 | addEngineSnifferEvent(vvtName, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); | |
| 275 | 6705 | } | ||
| 276 |