Line data Source code
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 : //#define DEBUG_WAVE 1 63 : 64 : /** 65 : * We want to skip some engine cycles to skip what was scheduled before parameters were changed 66 : */ 67 : static uint32_t skipUntilEngineCycle = 0; 68 : 69 : #if ! EFI_UNIT_TEST 70 : extern WaveChart waveChart; 71 : static void resetNow() { 72 : skipUntilEngineCycle = getRevolutionCounter() + 3; 73 : waveChart.reset(); 74 : } 75 : #endif // EFI_UNIT_TEST 76 : 77 1 : WaveChart::WaveChart() : logging("wave chart", WAVE_LOGGING_BUFFER, sizeof(WAVE_LOGGING_BUFFER)) { 78 1 : } 79 : 80 353 : void WaveChart::init() { 81 353 : isInitialized = true; 82 353 : reset(); 83 353 : } 84 : 85 353 : void WaveChart::reset() { 86 : #if DEBUG_WAVE 87 : efiPrintf("reset while at ", counter); 88 : #endif /* DEBUG_WAVE */ 89 353 : logging.reset(); 90 353 : counter = 0; 91 353 : startTimeNt = 0; 92 353 : collectingData = false; 93 353 : logging.appendPrintf( "%s%s", PROTOCOL_ENGINE_SNIFFER, LOG_DELIMITER); 94 353 : } 95 : 96 1294 : void WaveChart::startDataCollection() { 97 1294 : collectingData = true; 98 1294 : } 99 : 100 0 : bool WaveChart::isStartedTooLongAgo() const { 101 : /** 102 : * Say at 300rpm we should get at least four events per revolution. 103 : * That's 300/60*4=20 events per second 104 : * engineChartSize/20 is the longest meaningful chart. 105 : * 106 : */ 107 0 : efidur_t chartDurationNt = getTimeNowNt() - startTimeNt; 108 0 : return startTimeNt != 0 && NT2US(chartDurationNt) > engineConfiguration->engineChartSize * 1000000 / 20; 109 : } 110 : 111 73898 : bool WaveChart::isFull() const { 112 73898 : return counter >= engineConfiguration->engineChartSize; 113 : } 114 : 115 1 : int WaveChart::getSize() { 116 1 : return counter; 117 : } 118 : 119 : #if ! EFI_UNIT_TEST 120 : static void printStatus() { 121 : efiPrintf("engine sniffer: %s", boolToString(getTriggerCentral()->isEngineSnifferEnabled)); 122 : efiPrintf("engine sniffer size=%lu", engineConfiguration->engineChartSize); 123 : } 124 : 125 : void setChartSize(int newSize) { 126 : if (newSize < 5) { 127 : return; 128 : } 129 : engineConfiguration->engineChartSize = newSize; 130 : printStatus(); 131 : } 132 : #endif // EFI_UNIT_TEST 133 : 134 0 : void WaveChart::publishIfFull() { 135 0 : if (isFull() || isStartedTooLongAgo()) { 136 0 : publish(); 137 0 : reset(); 138 : } 139 0 : } 140 : 141 0 : void WaveChart::publish() { 142 : #if EFI_ENGINE_SNIFFER 143 0 : logging.appendPrintf( LOG_DELIMITER); 144 0 : waveChartUsedSize = logging.loggingSize(); 145 : #if DEBUG_WAVE 146 : Logging *l = &chart->logging; 147 : efiPrintf("IT'S TIME", strlen(l->buffer)); 148 : #endif // DEBUG_WAVE 149 0 : if (getTriggerCentral()->isEngineSnifferEnabled) { 150 0 : scheduleLogging(&logging); 151 : } 152 : #endif /* EFI_ENGINE_SNIFFER */ 153 0 : } 154 : 155 : /** 156 : * @brief Register an event for digital sniffer 157 : */ 158 73898 : void WaveChart::addEvent3(const char *name, const char * msg) { 159 : #if EFI_TEXT_LOGGING 160 73898 : ScopePerf perf(PE::EngineSniffer); 161 73898 : efitick_t nowNt = getTimeNowNt(); 162 : 163 73898 : if (nowNt < pauseEngineSnifferUntilNt) { 164 0 : return; 165 : } 166 73898 : if (!getTriggerCentral()->isEngineSnifferEnabled) { 167 0 : return; 168 : } 169 73898 : if (skipUntilEngineCycle != 0 && getRevolutionCounter() < skipUntilEngineCycle) 170 0 : return; 171 : #if EFI_SIMULATOR 172 : if (!collectingData) { 173 : return; 174 : } 175 : #endif 176 73898 : efiAssertVoid(ObdCode::CUSTOM_ERR_6651, name!=NULL, "WC: NULL name"); 177 : 178 : #if EFI_PROD_CODE 179 : efiAssertVoid(ObdCode::CUSTOM_ERR_6652, getCurrentRemainingStack() > 32, "lowstck#2c"); 180 : #endif /* EFI_PROD_CODE */ 181 : 182 73898 : efiAssertVoid(ObdCode::CUSTOM_ERR_6653, isInitialized, "chart not initialized"); 183 : #if DEBUG_WAVE 184 : efiPrintf("current", chart->counter); 185 : #endif /* DEBUG_WAVE */ 186 73898 : if (isFull()) { 187 56522 : return; 188 : } 189 : 190 : // we have multiple threads writing to the same output buffer 191 : chibios_rt::CriticalSectionLocker csl; 192 : 193 17376 : if (counter == 0) { 194 87 : startTimeNt = nowNt; 195 : } 196 17376 : counter++; 197 : 198 : /** 199 : * We want smaller times within a chart in order to reduce packet size. 200 : */ 201 : /** 202 : * todo: migrate to binary fractions in order to eliminate 203 : * this division? I do not like division 204 : * 205 : * at least that's 32 bit division now 206 : */ 207 17376 : uint32_t diffNt = nowNt - startTimeNt; 208 17376 : uint32_t time100 = NT2US(diffNt / ENGINE_SNIFFER_UNIT_US); 209 : 210 17376 : if (logging.remainingSize() > 35) { 211 : /** 212 : * printf is a heavy method, append is used here as a performance optimization 213 : */ 214 17376 : logging.appendFast(name); 215 17376 : logging.appendChar(CHART_DELIMETER); 216 17376 : logging.appendFast(msg); 217 17376 : logging.appendChar(CHART_DELIMETER); 218 : // time100 -= startTime100; 219 : 220 17376 : itoa10(timeBuffer, time100); 221 17376 : logging.appendFast(timeBuffer); 222 17376 : logging.appendChar(CHART_DELIMETER); 223 17376 : logging.terminate(); 224 : } 225 : #endif /* EFI_TEXT_LOGGING */ 226 : } 227 : 228 0 : void initWaveChart(WaveChart *chart) { 229 0 : strcpy((char*) shaft_signal_msg_index, "x_"); 230 : /** 231 : * constructor does not work because we need specific initialization order 232 : */ 233 0 : chart->init(); 234 : 235 : #if EFI_HISTOGRAMS 236 : initHistogram(&engineSnifferHisto, "engine sniffer"); 237 : #endif /* EFI_HISTOGRAMS */ 238 : 239 : #if ! EFI_UNIT_TEST 240 : printStatus(); 241 : addConsoleActionI("chartsize", setChartSize); 242 : // this is used by HW CI 243 : addConsoleAction(CMD_RESET_ENGINE_SNIFFER, resetNow); 244 : #endif // EFI_UNIT_TEST 245 0 : } 246 : 247 : #endif /* EFI_ENGINE_SNIFFER */ 248 : 249 14779 : void addEngineSnifferOutputPinEvent(NamedOutputPin *pin, FrontDirection frontDirection) { 250 14779 : if (!engineConfiguration->engineSnifferFocusOnInputs) { 251 14779 : addEngineSnifferEvent(pin->getShortName(), frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); 252 : } 253 14779 : } 254 : 255 1294 : void addEngineSnifferTdcEvent(int rpm) { 256 : static char rpmBuffer[_MAX_FILLER]; 257 1294 : itoa10(rpmBuffer, rpm); 258 : #if EFI_ENGINE_SNIFFER 259 1294 : waveChart.startDataCollection(); 260 : #endif 261 1294 : addEngineSnifferEvent(TOP_DEAD_CENTER_MESSAGE, (char* ) rpmBuffer); 262 1294 : } 263 : 264 0 : void addEngineSnifferLogicAnalyzerEvent(int laIndex, FrontDirection frontDirection) { 265 : extern const char *laNames[]; 266 0 : const char *name = laNames[laIndex]; 267 : 268 0 : addEngineSnifferEvent(name, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); 269 0 : } 270 : 271 55633 : void addEngineSnifferCrankEvent(int wheelIndex, int triggerEventIndex, FrontDirection frontDirection) { 272 : static const char *crankName[2] = { PROTOCOL_CRANK1, PROTOCOL_CRANK2 }; 273 : 274 55633 : shaft_signal_msg_index[0] = frontDirection == FrontDirection::UP ? 'u' : 'd'; 275 : // shaft_signal_msg_index[1] is assigned once and forever in the init method below 276 55633 : itoa10(&shaft_signal_msg_index[2], triggerEventIndex); 277 : 278 55633 : addEngineSnifferEvent(crankName[wheelIndex], (char* ) shaft_signal_msg_index); 279 55633 : } 280 : 281 2192 : void addEngineSnifferVvtEvent(int vvtIndex, FrontDirection frontDirection) { 282 : extern const char *vvtNames[]; 283 2192 : const char *vvtName = vvtNames[vvtIndex]; 284 : 285 2192 : addEngineSnifferEvent(vvtName, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); 286 2192 : }