allow logging multiple warnings at once (#4414)

* allow logging multiple warnings at once

* comment

* .Code

* Revert ".Code"

This reverts commit 2b986bd50035aeec051d11aafd83fdbc1694351c.

* add a better comparison operator instead of conversion

* dot code

* Revert "dot code"

This reverts commit 35f6ae1007c41e3b30ea129a324f33ab4205036d.

* force gcc-10 maybe?

* Revert "force gcc-10 maybe?"

This reverts commit 4ddf8bcefddd354ccdc1da682c5fff1e68d44273.

* vq

* dot code

* dead

* dot code

* return may be null

* static_vector; maybe cyclic_buffer is broken or wrong tool for the job?

* move static vector

* put cyclic buffer back how it was since we don't use it now
This commit is contained in:
Matthew Kennedy 2022-08-16 22:12:25 -07:00 committed by GitHub
parent 1343699a2f
commit 6218edd040
11 changed files with 132 additions and 44 deletions

View File

@ -797,7 +797,7 @@ void updateTunerStudioState() {
tsOutputChannels->hasCriticalError = hasFirmwareError(); tsOutputChannels->hasCriticalError = hasFirmwareError();
#endif // HW_CHECK_MODE #endif // HW_CHECK_MODE
tsOutputChannels->isWarnNow = engine->engineState.warnings.isWarningNow(timeSeconds, true); tsOutputChannels->isWarnNow = engine->engineState.warnings.isWarningNow();
#if EFI_HIP_9011_DEBUG #if EFI_HIP_9011_DEBUG
tsOutputChannels->isKnockChipOk = (instance.invalidResponsesCount == 0); tsOutputChannels->isKnockChipOk = (instance.invalidResponsesCount == 0);
#endif /* EFI_HIP_9011 */ #endif /* EFI_HIP_9011 */
@ -818,7 +818,7 @@ void updateTunerStudioState() {
tsOutputChannels->warningCounter = engine->engineState.warnings.warningCounter; tsOutputChannels->warningCounter = engine->engineState.warnings.warningCounter;
tsOutputChannels->lastErrorCode = engine->engineState.warnings.lastErrorCode; tsOutputChannels->lastErrorCode = engine->engineState.warnings.lastErrorCode;
for (int i = 0; i < 8;i++) { for (int i = 0; i < 8;i++) {
tsOutputChannels->recentErrorCode[i] = engine->engineState.warnings.recentWarnings.get(i); tsOutputChannels->recentErrorCode[i] = engine->engineState.warnings.recentWarnings.get(i).Code;
} }
tsOutputChannels->startStopStateToggleCounter = engine->startStopStateToggleCounter; tsOutputChannels->startStopStateToggleCounter = engine->startStopStateToggleCounter;

View File

@ -34,27 +34,51 @@ WarningCodeState::WarningCodeState() {
void WarningCodeState::clear() { void WarningCodeState::clear() {
warningCounter = 0; warningCounter = 0;
lastErrorCode = 0; lastErrorCode = 0;
timeOfPreviousWarning = DEEP_IN_THE_PAST_SECONDS;
recentWarnings.clear(); recentWarnings.clear();
} }
void WarningCodeState::addWarningCode(obd_code_e code) { void WarningCodeState::addWarningCode(obd_code_e code) {
warningCounter++; warningCounter++;
lastErrorCode = code; lastErrorCode = code;
if (!recentWarnings.contains(code)) {
warning_t* existing = recentWarnings.find(code);
if (!existing) {
chibios_rt::CriticalSectionLocker csl; chibios_rt::CriticalSectionLocker csl;
// We don't bother double checking // Add the code to the list
recentWarnings.add((int)code); existing = recentWarnings.add(warning_t(code));
} }
if (existing) {
// Reset the timer on the code to now
existing->LastTriggered.reset();
}
// Reset the "any warning" timer too
timeSinceLastWarning.reset();
} }
/** /**
* @param forIndicator if we want to retrieving value for TS indicator, this case a minimal period is applued * @param forIndicator if we want to retrieving value for TS indicator, this case a minimal period is applued
*/ */
bool WarningCodeState::isWarningNow(efitimesec_t now, bool forIndicator) const { bool WarningCodeState::isWarningNow() const {
int period = forIndicator ? maxI(3, engineConfiguration->warningPeriod) : engineConfiguration->warningPeriod; int period = maxI(3, engineConfiguration->warningPeriod);
return absI(now - timeOfPreviousWarning) < period;
return !timeSinceLastWarning.hasElapsedSec(period);
}
// Check whether a particular warning is active
bool WarningCodeState::isWarningNow(obd_code_e code) const {
warning_t* warn = recentWarnings.find(code);
// No warning found at all
if (!warn) {
return false;
}
// If the warning is old, it is not active
return !warn->LastTriggered.hasElapsedSec(maxI(3, engineConfiguration->warningPeriod));
} }
void FuelConsumptionState::consumeFuel(float grams, efitick_t nowNt) { void FuelConsumptionState::consumeFuel(float grams, efitick_t nowNt) {

View File

@ -7,7 +7,7 @@
#pragma once #pragma once
#include "cyclic_buffer.h" #include "static_vector.h"
#include "timer.h" #include "timer.h"
#define MOCK_ADC_SIZE 26 #define MOCK_ADC_SIZE 26
@ -43,17 +43,42 @@ public:
gear_e gearSelectorPosition; gear_e gearSelectorPosition;
}; };
typedef cyclic_buffer<int, 8> warningBuffer_t; struct warning_t {
Timer LastTriggered;
obd_code_e Code = OBD_None;
warning_t() { }
explicit warning_t(obd_code_e code)
: Code(code)
{
}
// Equality just checks the code, timer doesn't matter
bool operator ==(const warning_t& other) const {
return other.Code == Code;
}
// Compare against a plain OBD code
bool operator ==(const obd_code_e other) const {
return other == Code;
}
};
typedef static_vector<warning_t, 8> warningBuffer_t;
class WarningCodeState { class WarningCodeState {
public: public:
WarningCodeState(); WarningCodeState();
void addWarningCode(obd_code_e code); void addWarningCode(obd_code_e code);
bool isWarningNow(efitimesec_t now, bool forIndicator) const; bool isWarningNow() const;
bool isWarningNow(obd_code_e code) const;
void clear(); void clear();
int warningCounter; int warningCounter;
int lastErrorCode; int lastErrorCode;
efitimesec_t timeOfPreviousWarning;
Timer timeSinceLastWarning;
// todo: we need a way to post multiple recent warnings into TS // todo: we need a way to post multiple recent warnings into TS
warningBuffer_t recentWarnings; warningBuffer_t recentWarnings;
}; };

View File

@ -144,13 +144,12 @@ bool warning(obd_code_e code, const char *fmt, ...) {
#endif /* EFI_SIMULATOR */ #endif /* EFI_SIMULATOR */
#if EFI_SIMULATOR || EFI_PROD_CODE #if EFI_SIMULATOR || EFI_PROD_CODE
engine->engineState.warnings.addWarningCode(code); // we just had this same warning, let's not spam
if (engine->engineState.warnings.isWarningNow(code) || !warningEnabled) {
return true;
}
// todo: move this logic into WarningCodeState? engine->engineState.warnings.addWarningCode(code);
efitimesec_t now = getTimeNowSeconds();
if (engine->engineState.warnings.isWarningNow(now, false) || !warningEnabled)
return true; // we just had another warning, let's not spam
engine->engineState.warnings.timeOfPreviousWarning = now;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);

View File

@ -0,0 +1,46 @@
#pragma once
template <typename T, int TSlots>
struct static_vector {
void clear() {
m_size = 0;
}
template <typename TSearch>
T* find(const TSearch& search) const {
for (size_t i = 0; i < m_size; i++) {
if (m_storage[i] == search) {
return const_cast<T*>(&m_storage[i]);
}
}
return nullptr;
}
T* add(const T& value) {
if (m_size >= TSlots) {
// vector full, discard
return nullptr;
}
auto& location = m_storage[m_size];
location = value;
m_size++;
return &location;
}
T& get(size_t i) {
return m_storage[i];
}
size_t getCount() const {
return m_size;
}
private:
size_t m_size = 0;
T m_storage[TSlots];
};

View File

@ -42,12 +42,6 @@
// See USF2NT above for when to use MSF2NT // See USF2NT above for when to use MSF2NT
#define MSF2NT(msTimeFloat) USF2NT(MS2US(msTimeFloat)) #define MSF2NT(msTimeFloat) USF2NT(MS2US(msTimeFloat))
/**
* We use this 'deep in past, before ECU has ever started' value as a way to unify
* handling of first ever event and an event which has happened after a large pause in engine activity
*/
#define DEEP_IN_THE_PAST_SECONDS -10
#ifdef __cplusplus #ifdef __cplusplus
/** /**
* Provide a 62-bit counter from a 32-bit counter source that wraps around. * Provide a 62-bit counter from a 32-bit counter source that wraps around.

View File

@ -54,7 +54,7 @@ TEST(trigger, testNoStartUpWarnings) {
eth.fireFall(150); eth.fireFall(150);
} }
EXPECT_EQ( 1, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testNoStartUpWarnings CUSTOM_SYNC_COUNT_MISMATCH expected"; EXPECT_EQ( 1, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testNoStartUpWarnings CUSTOM_SYNC_COUNT_MISMATCH expected";
EXPECT_EQ(CUSTOM_SYNC_ERROR, unitTestWarningCodeState.recentWarnings.get(0)); EXPECT_EQ(CUSTOM_SYNC_ERROR, unitTestWarningCodeState.recentWarnings.get(0).Code);
} }
TEST(trigger, testNoisyInput) { TEST(trigger, testNoisyInput) {
@ -74,8 +74,8 @@ TEST(trigger, testNoisyInput) {
ASSERT_EQ(NOISY_RPM, Sensor::getOrZero(SensorType::Rpm)) << "testNoisyInput RPM should be noisy"; ASSERT_EQ(NOISY_RPM, Sensor::getOrZero(SensorType::Rpm)) << "testNoisyInput RPM should be noisy";
ASSERT_EQ( 2, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testNoisyInput"; ASSERT_EQ( 2, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testNoisyInput";
ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, unitTestWarningCodeState.recentWarnings.get(0)) << "@0"; ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, unitTestWarningCodeState.recentWarnings.get(0).Code) << "@0";
ASSERT_EQ(OBD_Crankshaft_Position_Sensor_A_Circuit_Malfunction, unitTestWarningCodeState.recentWarnings.get(1)) << "@0"; ASSERT_EQ(OBD_Crankshaft_Position_Sensor_A_Circuit_Malfunction, unitTestWarningCodeState.recentWarnings.get(1).Code) << "@0";
} }
TEST(trigger, testCamInput) { TEST(trigger, testCamInput) {
@ -105,7 +105,7 @@ TEST(trigger, testCamInput) {
// asserting that lack of camshaft signal would be detecting // asserting that lack of camshaft signal would be detecting
ASSERT_EQ(1, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testCamInput #2"; ASSERT_EQ(1, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testCamInput #2";
ASSERT_EQ(OBD_Camshaft_Position_Sensor_Circuit_Range_Performance, unitTestWarningCodeState.recentWarnings.get(0)) << "@0"; ASSERT_EQ(OBD_Camshaft_Position_Sensor_Circuit_Range_Performance, unitTestWarningCodeState.recentWarnings.get(0).Code) << "@0";
unitTestWarningCodeState.recentWarnings.clear(); unitTestWarningCodeState.recentWarnings.clear();
for (int i = 0; i < 600;i++) { for (int i = 0; i < 600;i++) {

View File

@ -19,6 +19,6 @@ TEST(issues, issue898) {
ASSERT_EQ(TRUE, engine->triggerCentral.triggerShape.shapeDefinitionError) << "MRE_MIATA_NA6 shapeDefinitionError"; ASSERT_EQ(TRUE, engine->triggerCentral.triggerShape.shapeDefinitionError) << "MRE_MIATA_NA6 shapeDefinitionError";
ASSERT_EQ( 2, eth.recentWarnings()->getCount()) << "warningCounter#testFuelSchedulerBug299smallAndMedium"; ASSERT_EQ( 2, eth.recentWarnings()->getCount()) << "warningCounter#testFuelSchedulerBug299smallAndMedium";
ASSERT_EQ(CUSTOM_ERR_BOTH_FRONTS_REQUIRED, eth.recentWarnings()->get(0)); ASSERT_EQ(CUSTOM_ERR_BOTH_FRONTS_REQUIRED, eth.recentWarnings()->get(0).Code);
ASSERT_EQ(CUSTOM_ERR_TRIGGER_SYNC, eth.recentWarnings()->get(1)); ASSERT_EQ(CUSTOM_ERR_TRIGGER_SYNC, eth.recentWarnings()->get(1).Code);
} }

View File

@ -34,7 +34,7 @@ TEST(realCrankingVQ40, normalCranking) {
// TODO: why warnings? // TODO: why warnings?
ASSERT_EQ(3, eth.recentWarnings()->getCount()); ASSERT_EQ(3, eth.recentWarnings()->getCount());
ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, eth.recentWarnings()->get(0)); ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, eth.recentWarnings()->get(0).Code);
ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, eth.recentWarnings()->get(1)); // this is from a coil being protected by overdwell protection ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, eth.recentWarnings()->get(1).Code); // this is from a coil being protected by overdwell protection
ASSERT_EQ(CUSTOM_SYNC_ERROR, eth.recentWarnings()->get(2)); ASSERT_EQ(CUSTOM_SYNC_ERROR, eth.recentWarnings()->get(2).Code);
} }

View File

@ -29,9 +29,9 @@ TEST(realCrankingNB2, normalCranking) {
ASSERT_EQ(942, round(Sensor::getOrZero(SensorType::Rpm))); ASSERT_EQ(942, round(Sensor::getOrZero(SensorType::Rpm)));
ASSERT_EQ(3, eth.recentWarnings()->getCount()); ASSERT_EQ(3, eth.recentWarnings()->getCount());
ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, eth.recentWarnings()->get(0)); ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, eth.recentWarnings()->get(0).Code);
ASSERT_EQ(CUSTOM_SYNC_ERROR, eth.recentWarnings()->get(1)); ASSERT_EQ(CUSTOM_SYNC_ERROR, eth.recentWarnings()->get(1).Code);
ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, eth.recentWarnings()->get(2)); ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, eth.recentWarnings()->get(2).Code);
} }
TEST(realCrankingNB2, crankingMissingInjector) { TEST(realCrankingNB2, crankingMissingInjector) {
@ -51,7 +51,7 @@ TEST(realCrankingNB2, crankingMissingInjector) {
ASSERT_EQ(668, round(Sensor::getOrZero(SensorType::Rpm))); ASSERT_EQ(668, round(Sensor::getOrZero(SensorType::Rpm)));
ASSERT_EQ(3, eth.recentWarnings()->getCount()); ASSERT_EQ(3, eth.recentWarnings()->getCount());
ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, eth.recentWarnings()->get(0)); ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, eth.recentWarnings()->get(0).Code);
ASSERT_EQ(CUSTOM_SYNC_ERROR, eth.recentWarnings()->get(1)); ASSERT_EQ(CUSTOM_SYNC_ERROR, eth.recentWarnings()->get(1).Code);
ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, eth.recentWarnings()->get(2)); ASSERT_EQ(CUSTOM_SYNC_COUNT_MISMATCH, eth.recentWarnings()->get(2).Code);
} }

View File

@ -867,7 +867,7 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
ASSERT_EQ( 0, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testFuelSchedulerBug299smallAndMedium"; ASSERT_EQ( 0, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testFuelSchedulerBug299smallAndMedium";
/* /*
ASSERT_EQ(CUSTOM_OBD_SKIPPED_FUEL, unitTestWarningCodeState.recentWarnings.get(0)); ASSERT_EQ(CUSTOM_OBD_SKIPPED_FUEL, unitTestWarningCodeState.recentWarnings.get(0).Code);
*/ */
} }
@ -1048,7 +1048,7 @@ TEST(big, testFuelSchedulerBug299smallAndLarge) {
eth.executeActions(); eth.executeActions();
ASSERT_EQ( 0, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testFuelSchedulerBug299smallAndLarge"; ASSERT_EQ( 0, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#testFuelSchedulerBug299smallAndLarge";
/* /*
ASSERT_EQ(CUSTOM_OBD_SKIPPED_FUEL, unitTestWarningCodeState.recentWarnings.get(0)); ASSERT_EQ(CUSTOM_OBD_SKIPPED_FUEL, unitTestWarningCodeState.recentWarnings.get(0).Code);
*/ */
} }
@ -1154,8 +1154,8 @@ TEST(big, testSparkReverseOrderBug319) {
eth.executeActions(); eth.executeActions();
ASSERT_EQ( 0, enginePins.coils[3].outOfOrder) << "out-of-order #8"; ASSERT_EQ( 0, enginePins.coils[3].outOfOrder) << "out-of-order #8";
ASSERT_EQ( 2, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#SparkReverseOrderBug319"; ASSERT_EQ( 2, unitTestWarningCodeState.recentWarnings.getCount()) << "warningCounter#SparkReverseOrderBug319";
ASSERT_EQ(CUSTOM_DWELL_TOO_LONG, unitTestWarningCodeState.recentWarnings.get(0)) << "warning @0"; ASSERT_EQ(CUSTOM_DWELL_TOO_LONG, unitTestWarningCodeState.recentWarnings.get(0).Code) << "warning @0";
ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, unitTestWarningCodeState.recentWarnings.get(1)); ASSERT_EQ(CUSTOM_OUT_OF_ORDER_COIL, unitTestWarningCodeState.recentWarnings.get(1).Code);
} }
TEST(big, testMissedSpark299) { TEST(big, testMissedSpark299) {