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 : /** 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 497 : void WaveChart::init() { 79 497 : isInitialized = true; 80 497 : reset(); 81 497 : } 82 : 83 497 : void WaveChart::reset() { 84 497 : logging.reset(); 85 497 : counter = 0; 86 497 : startTimeNt = 0; 87 497 : collectingData = false; 88 497 : logging.appendPrintf( "%s%s", PROTOCOL_ENGINE_SNIFFER, LOG_DELIMITER); 89 497 : } 90 : 91 1342 : void WaveChart::startDataCollection() { 92 1342 : collectingData = true; 93 1342 : } 94 : 95 0 : 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 0 : efidur_t chartDurationNt = getTimeNowNt() - startTimeNt; 103 0 : return startTimeNt != 0 && NT2US(chartDurationNt) > engineConfiguration->engineChartSize * 1000000 / 20; 104 : } 105 : 106 77974 : bool WaveChart::isFull() const { 107 77974 : 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 0 : void WaveChart::publishIfFull() { 130 0 : if (isFull() || isStartedTooLongAgo()) { 131 0 : publish(); 132 0 : reset(); 133 : } 134 0 : } 135 : 136 0 : void WaveChart::publish() { 137 : #if EFI_ENGINE_SNIFFER 138 0 : logging.appendPrintf( LOG_DELIMITER); 139 0 : waveChartUsedSize = logging.loggingSize(); 140 : 141 0 : if (getTriggerCentral()->isEngineSnifferEnabled) { 142 0 : scheduleLogging(&logging); 143 : } 144 : #endif /* EFI_ENGINE_SNIFFER */ 145 0 : } 146 : 147 : /** 148 : * @brief Register an event for digital sniffer 149 : */ 150 77974 : void WaveChart::addEvent3(const char *name, const char * msg) { 151 : #if EFI_TEXT_LOGGING 152 77974 : ScopePerf perf(PE::EngineSniffer); 153 77974 : efitick_t nowNt = getTimeNowNt(); 154 : 155 77974 : if (nowNt < pauseEngineSnifferUntilNt) { 156 0 : return; 157 : } 158 77974 : if (!getTriggerCentral()->isEngineSnifferEnabled) { 159 0 : return; 160 : } 161 77974 : if (skipUntilEngineCycle != 0 && getRevolutionCounter() < skipUntilEngineCycle) 162 0 : return; 163 : #if EFI_SIMULATOR 164 : if (!collectingData) { 165 : return; 166 : } 167 : #endif 168 77974 : 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 77974 : efiAssertVoid(ObdCode::CUSTOM_ERR_6653, isInitialized, "chart not initialized"); 175 : 176 77974 : if (isFull()) { 177 58211 : return; 178 : } 179 : 180 : // we have multiple threads writing to the same output buffer 181 : chibios_rt::CriticalSectionLocker csl; 182 : 183 19763 : if (counter == 0) { 184 93 : startTimeNt = nowNt; 185 : } 186 19763 : 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 19763 : uint32_t diffNt = nowNt - startTimeNt; 198 19763 : uint32_t time100 = NT2US(diffNt / ENGINE_SNIFFER_UNIT_US); 199 : 200 19763 : if (logging.remainingSize() > 35) { 201 : /** 202 : * printf is a heavy method, append is used here as a performance optimization 203 : */ 204 19763 : logging.appendFast(name); 205 19763 : logging.appendChar(CHART_DELIMETER); 206 19763 : logging.appendFast(msg); 207 19763 : logging.appendChar(CHART_DELIMETER); 208 : // time100 -= startTime100; 209 : 210 19763 : itoa10(timeBuffer, time100); 211 19763 : logging.appendFast(timeBuffer); 212 19763 : logging.appendChar(CHART_DELIMETER); 213 19763 : logging.terminate(); 214 : } 215 : #endif /* EFI_TEXT_LOGGING */ 216 : } 217 : 218 0 : void initWaveChart(WaveChart *chart) { 219 0 : strcpy((char*) shaft_signal_msg_index, "x_"); 220 : /** 221 : * constructor does not work because we need specific initialization order 222 : */ 223 0 : chart->init(); 224 : 225 : #if EFI_HISTOGRAMS 226 : initHistogram(&engineSnifferHisto, "engine sniffer"); 227 : #endif /* EFI_HISTOGRAMS */ 228 : 229 : #if ! EFI_UNIT_TEST 230 : printStatus(); 231 : addConsoleActionI("chartsize", setChartSize); 232 : // this is used by HW CI 233 : addConsoleAction(CMD_RESET_ENGINE_SNIFFER, resetNow); 234 : #endif // EFI_UNIT_TEST 235 0 : } 236 : 237 : #endif /* EFI_ENGINE_SNIFFER */ 238 : 239 16253 : void addEngineSnifferOutputPinEvent(NamedOutputPin *pin, FrontDirection frontDirection) { 240 16253 : if (!engineConfiguration->engineSnifferFocusOnInputs) { 241 16253 : addEngineSnifferEvent(pin->getShortName(), frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); 242 : } 243 16253 : } 244 : 245 1342 : void addEngineSnifferTdcEvent(int rpm) { 246 : static char rpmBuffer[_MAX_FILLER]; 247 1342 : itoa10(rpmBuffer, rpm); 248 : #if EFI_ENGINE_SNIFFER 249 1342 : waveChart.startDataCollection(); 250 : #endif 251 1342 : addEngineSnifferEvent(TOP_DEAD_CENTER_MESSAGE, (char* ) rpmBuffer); 252 1342 : } 253 : 254 0 : void addEngineSnifferLogicAnalyzerEvent(int laIndex, FrontDirection frontDirection) { 255 : extern const char *laNames[]; 256 0 : const char *name = laNames[laIndex]; 257 : 258 0 : addEngineSnifferEvent(name, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); 259 0 : } 260 : 261 58187 : void addEngineSnifferCrankEvent(int wheelIndex, int triggerEventIndex, FrontDirection frontDirection) { 262 : static const char *crankName[2] = { PROTOCOL_CRANK1, PROTOCOL_CRANK2 }; 263 : 264 58187 : shaft_signal_msg_index[0] = frontDirection == FrontDirection::UP ? 'u' : 'd'; 265 : // shaft_signal_msg_index[1] is assigned once and forever in the init method below 266 58187 : itoa10(&shaft_signal_msg_index[2], triggerEventIndex); 267 : 268 58187 : addEngineSnifferEvent(crankName[wheelIndex], (char* ) shaft_signal_msg_index); 269 58187 : } 270 : 271 2192 : void addEngineSnifferVvtEvent(int vvtIndex, FrontDirection frontDirection) { 272 : extern const char *vvtNames[]; 273 2192 : const char *vvtName = vvtNames[vvtIndex]; 274 : 275 2192 : addEngineSnifferEvent(vvtName, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); 276 2192 : }