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 | 583 | void WaveChart::init() { | ||
79 | 583 | isInitialized = true; | ||
80 | 583 | reset(); | ||
81 | 583 | } | ||
82 | ||||
83 | 583 | void WaveChart::reset() { | ||
84 | 583 | logging.reset(); | ||
85 | 583 | counter = 0; | ||
86 | 583 | startTimeNt = 0; | ||
87 | 583 | collectingData = false; | ||
88 | 583 | logging.appendPrintf( "%s%s", PROTOCOL_ENGINE_SNIFFER, LOG_DELIMITER); | ||
89 | 583 | } | ||
90 | ||||
91 | 1503 | void WaveChart::startDataCollection() { | ||
92 | 1503 | collectingData = true; | ||
93 | 1503 | } | ||
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 | 91033 | bool WaveChart::isFull() const { | ||
107 | 91033 | 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 | 91033 | void WaveChart::addEvent3(const char *name, const char * msg) { | ||
151 | #if EFI_TEXT_LOGGING | |||
152 | 91033 | ScopePerf perf(PE::EngineSniffer); | ||
153 |
1/1✓ Branch 1 taken 91033 times.
|
91033 | efitick_t nowNt = getTimeNowNt(); | |
154 | ||||
155 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 91033 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 91033 times.
|
91033 | if (nowNt < pauseEngineSnifferUntilNt) { |
156 | ✗ | return; | ||
157 | } | |||
158 |
2/3✓ Branch 1 taken 91033 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 91033 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 91033 times.
|
91033 | if (!getTriggerCentral()->isEngineSnifferEnabled) { |
159 | ✗ | return; | ||
160 | } | |||
161 |
2/7✗ Branch 0 not taken.
✓ Branch 1 taken 91033 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 91033 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 91033 times.
|
91033 | 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 91033 times.
✗ Branch 3 not taken.
|
91033 | 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 91033 times.
✗ Branch 3 not taken.
|
91033 | efiAssertVoid(ObdCode::CUSTOM_ERR_6653, isInitialized, "chart not initialized"); | |
175 | ||||
176 |
3/3✓ Branch 1 taken 91033 times.
✓ Branch 3 taken 64452 times.
✓ Branch 4 taken 26581 times.
|
2/2✓ Decision 'true' taken 64452 times.
✓ Decision 'false' taken 26581 times.
|
91033 | if (isFull()) { |
177 | 64452 | 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 115 times.
✓ Branch 1 taken 26466 times.
|
2/2✓ Decision 'true' taken 115 times.
✓ Decision 'false' taken 26466 times.
|
26581 | if (counter == 0) { |
184 | 115 | startTimeNt = nowNt; | ||
185 | } | |||
186 | 26581 | 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 | 26581 | uint32_t diffNt = nowNt - startTimeNt; | ||
198 | 26581 | uint32_t time100 = NT2US(diffNt / ENGINE_SNIFFER_UNIT_US); | ||
199 | ||||
200 |
1/2✓ Branch 1 taken 26581 times.
✗ Branch 2 not taken.
|
1/2✓ Decision 'true' taken 26581 times.
✗ Decision 'false' not taken.
|
26581 | 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 26581 times.
|
26581 | logging.appendFast(name); | |
205 | 26581 | logging.appendChar(CHART_DELIMETER); | ||
206 |
1/1✓ Branch 1 taken 26581 times.
|
26581 | logging.appendFast(msg); | |
207 | 26581 | logging.appendChar(CHART_DELIMETER); | ||
208 | // time100 -= startTime100; | |||
209 | ||||
210 |
1/1✓ Branch 1 taken 26581 times.
|
26581 | itoa10(timeBuffer, time100); | |
211 |
1/1✓ Branch 1 taken 26581 times.
|
26581 | logging.appendFast(timeBuffer); | |
212 | 26581 | logging.appendChar(CHART_DELIMETER); | ||
213 | 26581 | 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 | 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 | ✗ | } | ||
236 | ||||
237 | #endif /* EFI_ENGINE_SNIFFER */ | |||
238 | ||||
239 | 18607 | void addEngineSnifferOutputPinEvent(NamedOutputPin *pin, FrontDirection frontDirection) { | ||
240 |
1/2✓ Branch 0 taken 18607 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 18607 times.
✗ Decision 'false' not taken.
|
18607 | if (!engineConfiguration->engineSnifferFocusOnInputs) { |
241 |
3/4✓ Branch 1 taken 18607 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8918 times.
✓ Branch 4 taken 9689 times.
|
18607 | addEngineSnifferEvent(pin->getShortName(), frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); | |
242 | } | |||
243 | 18607 | } | ||
244 | ||||
245 | 1503 | void addEngineSnifferTdcEvent(int rpm) { | ||
246 | static char rpmBuffer[_MAX_FILLER]; | |||
247 | 1503 | itoa10(rpmBuffer, rpm); | ||
248 | #if EFI_ENGINE_SNIFFER | |||
249 | 1503 | waveChart.startDataCollection(); | ||
250 | #endif | |||
251 |
1/2✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
|
1503 | addEngineSnifferEvent(TOP_DEAD_CENTER_MESSAGE, (char* ) rpmBuffer); | |
252 | 1503 | } | ||
253 | ||||
254 | ✗ | void addEngineSnifferLogicAnalyzerEvent(int laIndex, FrontDirection frontDirection) { | ||
255 | extern const char *laNames[]; | |||
256 | ✗ | const char *name = laNames[laIndex]; | ||
257 | ||||
258 | ✗ | addEngineSnifferEvent(name, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); | ||
259 | ✗ | } | ||
260 | ||||
261 | 67212 | void addEngineSnifferCrankEvent(int wheelIndex, int triggerEventIndex, FrontDirection frontDirection) { | ||
262 | static const char *crankName[2] = { PROTOCOL_CRANK1, PROTOCOL_CRANK2 }; | |||
263 | ||||
264 |
2/2✓ Branch 0 taken 33604 times.
✓ Branch 1 taken 33608 times.
|
67212 | 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 | 67212 | itoa10(&shaft_signal_msg_index[2], triggerEventIndex); | ||
267 | ||||
268 |
1/2✓ Branch 1 taken 67212 times.
✗ Branch 2 not taken.
|
67212 | addEngineSnifferEvent(crankName[wheelIndex], (char* ) shaft_signal_msg_index); | |
269 | 67212 | } | ||
270 | ||||
271 | 3711 | void addEngineSnifferVvtEvent(int vvtIndex, FrontDirection frontDirection) { | ||
272 | extern const char *vvtNames[]; | |||
273 | 3711 | const char *vvtName = vvtNames[vvtIndex]; | ||
274 | ||||
275 |
3/4✓ Branch 1 taken 3711 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1860 times.
✓ Branch 4 taken 1851 times.
|
3711 | addEngineSnifferEvent(vvtName, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN); | |
276 | 3711 | } | ||
277 |