Annotations in C++ code to produce formulas in rusEfi console #807 (#847)

* Annotations in C++ code to produce formulas in rusEfi console #807
firmware part of the change

* removing unneeded stuff & fixing unit test compilation

* not complete better unit test compilation fix  :(

* better C++ usage
This commit is contained in:
rusefi 2019-06-17 12:18:55 -04:00 committed by GitHub
parent 9b1d57adf6
commit a8f456b807
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 231 additions and 36 deletions

View File

@ -186,7 +186,7 @@ static void bluetoothSPP(const char *baudRate, const char *name, const char *pin
void tunerStudioDebug(const char *msg) {
#if EFI_TUNER_STUDIO_VERBOSE
scheduleMsg(&tsLogger, "%s", msg);
#endif
#endif /* EFI_TUNER_STUDIO_VERBOSE */
}
char *getWorkingPageAddr(int pageIndex) {
@ -277,6 +277,28 @@ static void onlineApplyWorkingCopyBytes(int currentPageId, uint32_t offset, int
}
}
/**
* Read internal structure for Live Doc
* This is somewhat similar to read page and somewhat similar to read outputs
* We can later consider combining this functionality
*/
static void handleGetStructContent(ts_channel_s *tsChannel, int structId, int size) {
tsState.readPageCommandsCounter++;
const void *addr = NULL;
if (structId == LDS_CLT_INDEX) {
addr = static_cast<thermistor_state_s*>(&engine->engineState.cltCurve);
} else if (structId == LDS_IAT_INDEX) {
addr = static_cast<thermistor_state_s*>(&engine->engineState.iatCurve);
}
if (addr == NULL) {
// todo: add warning code - unexpected structId
return;
}
sr5SendResponse(tsChannel, TS_CRC, (const uint8_t *)addr, size);
}
/**
* read log file content for rusEfi console
*/
@ -452,6 +474,7 @@ static bool isKnownCommand(char command) {
|| command == TS_PAGE_COMMAND || command == TS_BURN_COMMAND || command == TS_SINGLE_WRITE_COMMAND
|| command == TS_CHUNK_WRITE_COMMAND || command == TS_EXECUTE
|| command == TS_IO_TEST_COMMAND
|| command == TS_GET_STRUCT
|| command == TS_GET_FILE_RANGE
|| command == TS_TOOTH_COMMAND
|| command == TS_GET_TEXT || command == TS_CRC_CHECK_COMMAND
@ -743,6 +766,10 @@ int tunerStudioHandleCrcCommand(ts_channel_s *tsChannel, char *data, int incomin
} else if (command == TS_PAGE_COMMAND) {
uint16_t page = *(uint16_t *) data;
handlePageSelectCommand(tsChannel, TS_CRC, page);
} else if (command == TS_GET_STRUCT) {
short structId = *(uint16_t *) data;
uint16_t length = *(uint16_t *) (data + 2);
handleGetStructContent(tsChannel, structId, length);
} else if (command == TS_GET_FILE_RANGE) {
short fileId = *(uint16_t *) data;
uint16_t offset = *(uint16_t *) (data + 2);

View File

@ -57,8 +57,9 @@ typedef struct {
// These commands are used exclusively by the rusEfi console
#define TS_TEST_COMMAND 't' // 0x74
#define TS_GET_TEXT 'G' // 0x47
#define TS_GET_FILE_RANGE '2'
#define TS_GET_FILE_RANGE '2' // 0x32
#define TS_EXECUTE 'E' // 0x45
#define TS_GET_STRUCT '9' // 0x39
// These commands are used by TunerStudio and the rusEfi console
#define TS_HELLO_COMMAND 'S' // 0x53 queryCommand

View File

@ -118,12 +118,12 @@ void EngineState::updateSlowSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engineConfiguration->auxTempSensor1.adcChannel != EFI_ADC_NONE) {
engine->sensors.auxTemp1 = getTemperatureC(&engineConfiguration->auxTempSensor1,
&engine->engineState.auxTemp1Curve,
false);
false PASS_ENGINE_PARAMETER_SUFFIX);
}
if (engineConfiguration->auxTempSensor2.adcChannel != EFI_ADC_NONE) {
engine->sensors.auxTemp2 = getTemperatureC(&engineConfiguration->auxTempSensor2,
&engine->engineState.auxTemp2Curve,
false);
false PASS_ENGINE_PARAMETER_SUFFIX);
}
#if EFI_UNIT_TEST

View File

@ -928,7 +928,9 @@ void setDefaultConfiguration(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
engineConfiguration->timingMode = TM_DYNAMIC;
engineConfiguration->fixedModeTiming = 50;
#if !EFI_UNIT_TEST
engineConfiguration->analogInputDividerCoefficient = 2;
#endif
// performance optimization
boardConfiguration->sensorChartMode = SC_OFF;

View File

@ -11,6 +11,7 @@
#include "global.h"
#include "engine_configuration_generated_structures.h"
#include "cyclic_buffer.h"
#include "thermistor.h"
#define MOCK_ADC_SIZE 16
@ -24,7 +25,7 @@ public:
int getMockAdcValue(int hwChannel) const;
};
class ThermistorMath {
class ThermistorMath : public thermistor_state_s {
public:
void setConfig(thermistor_conf_s *config);
void prepareThermistorCurve(thermistor_conf_s *tc);
@ -32,6 +33,7 @@ public:
float s_h_a = 0;
float s_h_b = 0;
float s_h_c = 0;
bool isLinear;
private:
thermistor_conf_s currentConfig = {};
};

View File

@ -12,8 +12,9 @@
#include "global.h"
#include "engine_parts.h"
#include "pid.h"
#include "engine_state_generated.h"
class EngineState {
class EngineState : public engine_state2_s {
public:
EngineState();
void periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE);
@ -32,10 +33,6 @@ public:
WarningCodeState warnings;
/**
* speed-density logic, calculated air mass in grams
*/
float airMass = 0;
/**
* speed-density logic, calculated air flow in kg/h for tCharge Air-Interp. method
*/

View File

@ -32,4 +32,11 @@
#include "loggingcentral.h"
#endif /* __cplusplus */
#define DISPLAY_CONFIG(x) x
#define DISPLAY_FIELD(x) x
#define DISPLAY_TEXT(x)
#define DISPLAY_SENSOR(x) {}
#define DISPLAY_IF(x) {}
#endif /* CONTROLLERS_CORE_COMMON_HEADERS_H_ */

View File

@ -0,0 +1,39 @@
// this section was generated automatically by ConfigDefinition.jar based on integration/engine_state.txt Sun Jun 16 23:32:38 EDT 2019
// begin
#ifndef CONTROLLERS_GENERATED_ENGINE_STATE_GENERATED_H
#define CONTROLLERS_GENERATED_ENGINE_STATE_GENERATED_H
#include "rusefi_types.h"
// start of engine_state2_s
struct engine_state2_s {
/**
offset 0 bit 0 */
bool isTChargeAirModel : 1;
/**
* speed-density logic, calculated air mass in grams
* offset 4
*/
float airMass = 0;
/**
* offset 8
*/
float engineCycleDurationMs = 0;
/**
* offset 12
*/
float Tcharge_coff = 0;
/**
* offset 16
*/
floatms_t airFlow = 0;
/**
* offset 20
*/
float minRpmKcurrentTPS = 0;
/** total size 24*/
};
typedef struct engine_state2_s engine_state2_s;
#endif
// end
// this section was generated automatically by ConfigDefinition.jar based on integration/engine_state.txt Sun Jun 16 23:32:38 EDT 2019

View File

@ -0,0 +1 @@
All the code here was generated by tools.

View File

@ -1312,6 +1312,9 @@
#define knockVThreshold_offset_hex 5f0
#define lcdThreadPeriodMs_offset 720
#define lcdThreadPeriodMs_offset_hex 2d0
#define LDS_CLT_INDEX 0
#define LDS_IAT_INDEX 1
#define LDS_SPEED_DENSITY_INDEX 2
#define LE_COMMAND_LENGTH 200
#define LIS302DLCsPin_offset 2063
#define LIS302DLCsPin_offset_hex 80f

View File

@ -0,0 +1,27 @@
// this section was generated automatically by ConfigDefinition.jar based on integration/thermistor.txt Sat Jun 15 17:39:00 EDT 2019
// begin
#ifndef CONTROLLERS_GENERATED_THERMISTOR_H
#define CONTROLLERS_GENERATED_THERMISTOR_H
#include "rusefi_types.h"
// start of thermistor_state_s
struct thermistor_state_s {
/**
* offset 0
*/
float resistance = 0;
/**
* offset 4
*/
float voltageMCU = 0;
/**
* offset 8
*/
float voltageBoard = 0;
/** total size 12*/
};
typedef struct thermistor_state_s thermistor_state_s;
#endif
// end
// this section was generated automatically by ConfigDefinition.jar based on integration/thermistor.txt Sat Jun 15 17:39:00 EDT 2019

View File

@ -29,42 +29,48 @@ baroCorr_Map3D_t baroCorrMap("baro");
#define tpMin 0
#define tpMax 100
// http://rusefi.com/math/t_charge.html
/***panel:Charge Temperature*/
float getTCharge(int rpm, float tps, float coolantTemp, float airTemp DECLARE_ENGINE_PARAMETER_SUFFIX) {
if (cisnan(coolantTemp) || cisnan(airTemp)) {
warning(CUSTOM_ERR_NAN_TCHARGE, "t-getTCharge NaN");
return coolantTemp;
}
float Tcharge_coff;
if (CONFIG(tChargeMode) == TCHARGE_MODE_AIR_INTERP) {
if ((engine->engineState./*DISPLAY_IF*/isTChargeAirModel = (CONFIG(tChargeMode) == TCHARGE_MODE_AIR_INTERP))) {
const floatms_t gramsPerMsToKgPerHour = (3600.0f * 1000.0f) / 1000.0f;
// We're actually using an 'old' airMass calculated for the previous cycle, but it's ok, we're not having any self-excitaton issues
floatms_t airMassForEngine = engine->engineState.airMass * CONFIG(specs.cylindersCount);
floatms_t airMassForEngine = engine->engineState./***display*/airMass * CONFIG(specs.cylindersCount);
// airMass is in grams per 1 cycle for 1 cyl. Convert it to airFlow in kg/h for the engine.
// And if the engine is stopped (0 rpm), then airFlow is also zero (avoiding NaN division)
floatms_t airFlow = (rpm == 0) ? 0 : airMassForEngine * gramsPerMsToKgPerHour / getEngineCycleDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX);
// just interpolate between user-specified min and max coefs, based on the max airFlow value
Tcharge_coff = interpolateClamped(0.0, CONFIG(tChargeAirCoefMin), CONFIG(tChargeAirFlowMax), CONFIG(tChargeAirCoefMax), airFlow);
engine->engineState.Tcharge_coff = interpolateClamped(0.0, CONFIG(tChargeAirCoefMin), CONFIG(tChargeAirFlowMax), CONFIG(tChargeAirCoefMax), airFlow);
// save it for console output (instead of MAF massAirFlow)
engine->engineState.airFlow = airFlow;
} else {
} else/* DISPLAY_ELSE */ {
// TCHARGE_MODE_RPM_TPS
float minRpmKcurrentTPS = interpolateMsg("minRpm", tpMin, CONFIG(tChargeMinRpmMinTps), tpMax,
CONFIG(tChargeMinRpmMaxTps), tps);
float maxRpmKcurrentTPS = interpolateMsg("maxRpm", tpMin, CONFIG(tChargeMaxRpmMinTps), tpMax,
CONFIG(tChargeMaxRpmMaxTps), tps);
DISPLAY_TEXT("interpolate(")
DISPLAY_SENSOR(RPM)
DISPLAY_SENSOR(TPS)
float minRpmKcurrentTPS = interpolateMsg("minRpm", tpMin, CONFIG(DISPLAY_CONFIG(tChargeMinRpmMinTps)), tpMax,
CONFIG(DISPLAY_CONFIG(tChargeMinRpmMaxTps)), tps);
float maxRpmKcurrentTPS = interpolateMsg("maxRpm", tpMin, CONFIG(DISPLAY_CONFIG(tChargeMaxRpmMinTps)), tpMax,
CONFIG(DISPLAY_CONFIG(tChargeMaxRpmMaxTps)), tps);
Tcharge_coff = interpolateMsg("Kcurr", rpmMin, minRpmKcurrentTPS, rpmMax, maxRpmKcurrentTPS, rpm);
DISPLAY_TEXT(")")
engine->engineState.Tcharge_coff = interpolateMsg("Kcurr", rpmMin, minRpmKcurrentTPS, rpmMax, maxRpmKcurrentTPS, rpm);
/* DISPLAY_ENDIF */
}
if (cisnan(Tcharge_coff)) {
if (cisnan(engine->engineState.Tcharge_coff)) {
warning(CUSTOM_ERR_T2_CHARGE, "t2-getTCharge NaN");
return coolantTemp;
}
// We use a robust interp. function for proper tcharge_coff clamping.
float Tcharge = interpolateClamped(0.0f, coolantTemp, 1.0f, airTemp, Tcharge_coff);
float Tcharge = interpolateClamped(0.0f, coolantTemp, 1.0f, airTemp, engine->engineState.Tcharge_coff);
if (cisnan(Tcharge)) {
// we can probably end up here while resetting engine state - interpolation would fail

View File

@ -77,23 +77,38 @@ float getResistance(ThermistorConf *config, float voltage) {
return resistance;
}
float getTemperatureC(ThermistorConf *config, ThermistorMath *tm, bool useLinear) {
tm->setConfig(&config->config); // implementation checks if configuration has changed or not
float getTemperatureC(ThermistorConf *cfg, ThermistorMath *tm, bool useLinear DECLARE_ENGINE_PARAMETER_SUFFIX) {
tm->setConfig(&cfg->config); // implementation checks if configuration has changed or not
float voltage = getVoltageDivided("term", config->adcChannel);
if (useLinear) {
DISPLAY_TEXT(Analog_MCU_reads);
tm->DISPLAY_FIELD(voltageMCU) = DISPLAY_TEXT(from_pin) getVoltage("term", cfg->DISPLAY_CONFIG(adcChannel));
DISPLAY_TEXT(EOL);
DISPLAY_TEXT(Analog_ECU_read);
#if EFI_UNIT_TEST
// todo: get rid of this branch, unify unit test with real firmware. maybe analogInputDividerCoefficient needs to be set?
tm->voltageBoard = getVoltageDivided("term", cfg->adcChannel);
// CONFIG(analogInputDividerCoefficient) = 1;
// tm->DISPLAY_FIELD(voltageBoard) = DISPLAY_TEXT(Rdivider) tm->voltageMCU * CONFIG(DISPLAY_CONFIG(analogInputDividerCoefficient));
#else
tm->DISPLAY_FIELD(voltageBoard) = DISPLAY_TEXT(Rdivider) tm->voltageMCU * CONFIG(DISPLAY_CONFIG(analogInputDividerCoefficient));
#endif /* EFI_UNIT_TEST */
DISPLAY_TEXT(EOL);
if ((tm->isLinear = useLinear)) {
// todo: fix this horrible code!
// should work as a short term fix.
// todo: move 'useLinearXXXSensor' into termistor configuration record
// todo: move 'useLinearXXXSensor' into thermistor configuration record
// yes, we use 'resistance' setting for 'voltage' here
return interpolateMsg("temp", config->config.resistance_1, config->config.tempC_1,
config->config.resistance_2, config->config.tempC_2,
voltage);
return interpolateMsg("temp", cfg->config.resistance_1, cfg->config.tempC_1,
cfg->config.resistance_2, cfg->config.tempC_2,
tm->voltageBoard);
}
float resistance = getResistance(config, voltage);
DISPLAY_TEXT(Measured_resistance);
tm->DISPLAY_FIELD(resistance) = getResistance(cfg, tm->voltageBoard);
float kelvinTemperature = tm->getKelvinTemperatureByResistance(resistance);
float kelvinTemperature = tm->getKelvinTemperatureByResistance(tm->resistance);
return convertKelvinToCelcius(kelvinTemperature);
}
@ -120,7 +135,7 @@ float getCoolantTemperature(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return NO_CLT_SENSOR_TEMPERATURE;
}
float temperature = getTemperatureC(&engineConfiguration->clt, &engine->engineState.cltCurve,
engineConfiguration->useLinearCltSensor);
engineConfiguration->useLinearCltSensor PASS_ENGINE_PARAMETER_SUFFIX);
if (!isValidCoolantTemperature(temperature)) {
efiAssert(CUSTOM_ERR_ASSERT, engineConfiguration!=NULL, "NULL engineConfiguration", NAN);
warning(OBD_Engine_Coolant_Temperature_Circuit_Malfunction, "unrealistic CLT %.2f", temperature);
@ -201,7 +216,7 @@ float getIntakeAirTemperature(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return NO_IAT_SENSOR_TEMPERATURE;
}
float temperature = getTemperatureC(&engineConfiguration->iat, &engine->engineState.iatCurve,
engineConfiguration->useLinearIatSensor);
engineConfiguration->useLinearIatSensor PASS_ENGINE_PARAMETER_SUFFIX);
if (!isValidIntakeAirTemperature(temperature)) {
efiAssert(CUSTOM_ERR_ASSERT, engineConfiguration!=NULL, "NULL engineConfiguration", NAN);
#if EFI_PROD_CODE || EFI_UNIT_TEST

View File

@ -31,8 +31,8 @@ float convertCelciustoF(float tempC);
float convertFtoCelcius(float tempF);
float getKelvinTemperature(float resistance, ThermistorMath *tm);
float getResistance(ThermistorConf *config, float voltage);
float getTemperatureC(ThermistorConf *config, ThermistorMath *tm, bool useLinear);
float getResistance(ThermistorConf *cfg, float voltage);
float getTemperatureC(ThermistorConf *cfg, ThermistorMath *tm, bool useLinear DECLARE_ENGINE_PARAMETER_SUFFIX);
float getCoolantTemperature(DECLARE_ENGINE_PARAMETER_SIGNATURE);
bool isValidCoolantTemperature(float temperature);
float getIntakeAirTemperature(DECLARE_ENGINE_PARAMETER_SIGNATURE);

17
firmware/gen_config2.bat Normal file
View File

@ -0,0 +1,17 @@
java -jar ../java_tools/ConfigDefinition.jar ^
-definition integration/engine_state.txt ^
-java_destination ../java_console/models/src/com/rusefi/config/generated/EngineState.java ^
-initialize_to_zero yes ^
-c_destination controllers/generated/engine_state_generated.h
java -jar ../java_tools/ConfigDefinition.jar ^
-definition integration/thermistor.txt ^
-initialize_to_zero yes ^
-java_destination ../java_console/models/src/com/rusefi/config/generated/ThermistorState.java ^
-c_destination controllers/generated/thermistor.h
java -cp ../java_tools/ConfigDefinition.jar ^
com.rusefi.ldmp.LiveDocsMetaParser ^
controllers/sensors/thermistors.cpp ^
"../"

View File

@ -0,0 +1,18 @@
Back in 2012 rusEfi was founded with the idea of simplicity and clarity.
In 2013 the approach for clarify of what's going on inside ECU was a set of commands
like _triggerinfo_, _analoginfo_ and _tempinfo_. In 2015 this was supplimented by *debug fields*.
In 2019 it became clear that a better way to look inside the ECU is needed, both to understand the overall logic of the ECU
and explain specific output numbers. **Live Documentation** is the attempt to answer that desire.
Implementation details:
1) rusEfi runtime data structures are defined in `engine_state.txt` `thermistor.txt` etc.
1) ConfigDefinition code generator produces both C++ header and java representation of the runtime data structure.
1) C++ code is annotated using DISPLAY_TEXT(x), DISPLAY_FIELD(y) and DISPLAY_CONFIG(z) macro
1) LiveDocsMetaParser code generator reads annotated C++ code and produces rusEfi console UI code like ThermistorMeta.java
1) rusEfi console periodically reads runtime data from firmware and displays it using generated UI panels.
https://www.youtube.com/watch?v=n_pqH-3P3Qc
TODO: Live Documentation with branching sections.

View File

@ -0,0 +1,20 @@
struct_no_prefix engine_state2_s
bit isTChargeAirModel
float airMass;speed-density logic, calculated air mass in grams
float engineCycleDurationMs;
float Tcharge_coff
floatms_t airFlow
float minRpmKcurrentTPS
end_struct

View File

@ -1151,6 +1151,11 @@ end_struct
#define MOCK_IAT_COMMAND "mock_iat_voltage"
! Live Documentation Structure
#define LDS_CLT_INDEX 0
#define LDS_IAT_INDEX 1
#define LDS_SPEED_DENSITY_INDEX 2
#define GAUGE_NAME_VERSION "firmware"
#define GAUGE_NAME_VVT "VVT postion"
#define GAUGE_NAME_TIMING_ADVANCE "timing"

View File

@ -0,0 +1,8 @@
struct thermistor_state_s
float resistance
float voltageMCU
float voltageBoard
end_struct