autoscale fields in generated structs (#3444)

* add to grammar

* parser

* parser

* example consumer

* build config tool

* commit the right jar
This commit is contained in:
Matthew Kennedy 2021-11-02 15:59:19 -07:00 committed by GitHub
parent e1a78cce41
commit 980a7cc833
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 81 additions and 29 deletions

View File

@ -35,7 +35,7 @@ void setTestEngineConfiguration(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
setWholeIatCorrTimingTable(0 PASS_CONFIG_PARAMETER_SUFFIX); setWholeIatCorrTimingTable(0 PASS_CONFIG_PARAMETER_SUFFIX);
// Many tests were written when the default target AFR was 14.0, so use that for tests by default. // Many tests were written when the default target AFR was 14.0, so use that for tests by default.
engineConfiguration->stoichRatioPrimary = 140; engineConfiguration->stoichRatioPrimary = 14.0f;
engineConfiguration->ignitionMode = IM_ONE_COIL; engineConfiguration->ignitionMode = IM_ONE_COIL;
setConstantDwell(3 PASS_CONFIG_PARAMETER_SUFFIX); // 50% duty cycle @ 5000 rpm setConstantDwell(3 PASS_CONFIG_PARAMETER_SUFFIX); // 50% duty cycle @ 5000 rpm

View File

@ -192,10 +192,10 @@ void setDefaultFuel(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
* By the way http://users.erols.com/srweiss/tableifc.htm has a LOT of data * By the way http://users.erols.com/srweiss/tableifc.htm has a LOT of data
*/ */
engineConfiguration->injector.flow = 200; engineConfiguration->injector.flow = 200;
engineConfiguration->stoichRatioPrimary = STOICH_RATIO * PACK_MULT_AFR_CFG; engineConfiguration->stoichRatioPrimary = STOICH_RATIO;
// 9.0 = E100 pure ethanol // 9.0 = E100 pure ethanol
engineConfiguration->stoichRatioSecondary = 9.0f * PACK_MULT_AFR_CFG; engineConfiguration->stoichRatioSecondary = 9.0f;
// Injector deadtime // Injector deadtime
setBosch02880155868(PASS_CONFIG_PARAMETER_SIGNATURE); setBosch02880155868(PASS_CONFIG_PARAMETER_SIGNATURE);

View File

@ -20,7 +20,7 @@ mass_t FuelComputerBase::getCycleFuel(mass_t airmass, int rpm, float load) const
FuelComputer::FuelComputer(const ValueProvider3D& lambdaTable) : m_lambdaTable(&lambdaTable) {} FuelComputer::FuelComputer(const ValueProvider3D& lambdaTable) : m_lambdaTable(&lambdaTable) {}
float FuelComputer::getStoichiometricRatio() const { float FuelComputer::getStoichiometricRatio() const {
float primary = (float)CONFIG(stoichRatioPrimary) / PACK_MULT_AFR_CFG; float primary = CONFIG(stoichRatioPrimary);
// Config compatibility: this field may be zero on ECUs with old defaults // Config compatibility: this field may be zero on ECUs with old defaults
if (primary < 5) { if (primary < 5) {
@ -33,7 +33,7 @@ float FuelComputer::getStoichiometricRatio() const {
return primary; return primary;
} }
float secondary = (float)CONFIG(stoichRatioSecondary) / PACK_MULT_AFR_CFG; float secondary = CONFIG(stoichRatioSecondary);
// Config compatibility: this field may be zero on ECUs with old defaults // Config compatibility: this field may be zero on ECUs with old defaults
if (secondary < 5) { if (secondary < 5) {

View File

@ -1427,11 +1427,11 @@ tChargeMode_e tChargeMode;
int16_t idlerpmpid_iTermMin;iTerm min value;"", 1, 0, -30000, 30000, 0 int16_t idlerpmpid_iTermMin;iTerm min value;"", 1, 0, -30000, 30000, 0
spi_device_e tle6240spiDevice; spi_device_e tle6240spiDevice;
uint8_t stoichRatioPrimary;+Stoichiometric ratio for your primary fuel. When Flex Fuel is enabled, this value is used when the Flex Fuel sensor indicates E0.\nE0 = 14.7\nE10 = 14.1\nE85 = 9.9\nE100 = 9.0;":1", {1/@@PACK_MULT_AFR_CFG@@}, 0, 5, 25, 1 uint8_t autoscale<PACK_MULT_AFR_CFG> stoichRatioPrimary;+Stoichiometric ratio for your primary fuel. When Flex Fuel is enabled, this value is used when the Flex Fuel sensor indicates E0.\nE0 = 14.7\nE10 = 14.1\nE85 = 9.9\nE100 = 9.0;":1", {1/@@PACK_MULT_AFR_CFG@@}, 0, 5, 25, 1
int16_t idlerpmpid_iTermMax;iTerm max value;"", 1, 0, -30000, 30000, 0 int16_t idlerpmpid_iTermMax;iTerm max value;"", 1, 0, -30000, 30000, 0
spi_device_e mc33972spiDevice; spi_device_e mc33972spiDevice;
uint8_t stoichRatioSecondary;+Stoichiometric ratio for your secondary fuel. This value is used when the Flex Fuel sensor indicates E100, typically 9.0;":1", {1/@@PACK_MULT_AFR_CFG@@}, 0, 5, 25, 1 uint8_t autoscale<PACK_MULT_AFR_CFG> stoichRatioSecondary;+Stoichiometric ratio for your secondary fuel. This value is used when the Flex Fuel sensor indicates E100, typically 9.0;":1", {1/@@PACK_MULT_AFR_CFG@@}, 0, 5, 25, 1
uint8_t[2] unusedSpiPadding8;;"units", 1, 0, -20, 100, 0 uint8_t[2] unusedSpiPadding8;;"units", 1, 0, -20, 100, 0

Binary file not shown.

View File

@ -24,6 +24,7 @@ Bit: 'bit';
Array: 'array'; Array: 'array';
Scalar: 'scalar'; Scalar: 'scalar';
FsioVisible: 'fsio_visible'; FsioVisible: 'fsio_visible';
Autoscale: 'autoscale';
ArrayDimensionSeparator: 'x'; ArrayDimensionSeparator: 'x';
@ -93,8 +94,10 @@ fieldOptionsList
arrayLengthSpec: numexpr (ArrayDimensionSeparator numexpr)?; arrayLengthSpec: numexpr (ArrayDimensionSeparator numexpr)?;
scalarField: identifier FsioVisible? identifier (fieldOptionsList)?; autoscale: Autoscale '<' numexpr '>';
arrayField: identifier '[' arrayLengthSpec Iterate? ']' identifier SemicolonedString? (fieldOptionsList)?;
scalarField: identifier autoscale? FsioVisible? identifier (fieldOptionsList)?;
arrayField: identifier '[' arrayLengthSpec Iterate? ']' autoscale? identifier SemicolonedString? (fieldOptionsList)?;
bitField: Bit identifier (',' QuotedString ',' QuotedString)? ('(' 'comment' ':' QuotedString ')')? SemicolonedSuffix?; bitField: Bit identifier (',' QuotedString ',' QuotedString)? ('(' 'comment' ':' QuotedString ')')? SemicolonedSuffix?;
unionField: 'union' ENDL+ fields 'end_union'; unionField: 'union' ENDL+ fields 'end_union';

View File

@ -3,6 +3,7 @@ package com.rusefi;
import com.rusefi.util.SystemOut; import com.rusefi.util.SystemOut;
import com.rusefi.test.ConfigFieldParserTest; import com.rusefi.test.ConfigFieldParserTest;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -13,10 +14,10 @@ import java.util.regex.Pattern;
* 1/15/15 * 1/15/15
*/ */
public class ConfigField { public class ConfigField {
public static final ConfigField VOID = new ConfigField(null, "", null, null, null, 1, null, false, false, null, -1, null, null); public static final ConfigField VOID = new ConfigField(null, "", null, null, null, 1, null, false, false, null,null, -1, null, null);
private static final String typePattern = "([\\w\\d_]+)(\\[([\\w\\d]+)(\\s([\\w\\d]+))?\\])?"; private static final String typePattern = "([\\w\\d_]+)(\\[([\\w\\d]+)(\\s([\\w\\d]+))?\\])?";
private static final String namePattern = "[[\\w\\d\\s_]]+"; private static final String namePattern = "[[\\w\\d\\s<>_]]+";
private static final String commentPattern = ";([^;]*)"; private static final String commentPattern = ";([^;]*)";
private static final Pattern FIELD = Pattern.compile(typePattern + "\\s(" + namePattern + ")(" + commentPattern + ")?(;(.*))?"); private static final Pattern FIELD = Pattern.compile(typePattern + "\\s(" + namePattern + ")(" + commentPattern + ")?(;(.*))?");
@ -40,6 +41,7 @@ public class ConfigField {
private final boolean isIterate; private final boolean isIterate;
private final ReaderState state; private final ReaderState state;
private boolean fsioVisible; private boolean fsioVisible;
private final String autoscaleSpec;
private final String individualName; private final String individualName;
private final int indexWithinArray; private final int indexWithinArray;
private final String trueName; private final String trueName;
@ -57,9 +59,11 @@ public class ConfigField {
String tsInfo, String tsInfo,
boolean isIterate, boolean isIterate,
boolean fsioVisible, boolean fsioVisible,
String autoscaleSpec,
String individualName, String individualName,
int indexWithinArray, String trueName, String falseName) { int indexWithinArray, String trueName, String falseName) {
this.fsioVisible = fsioVisible; this.fsioVisible = fsioVisible;
this.autoscaleSpec = autoscaleSpec;
this.individualName = individualName; this.individualName = individualName;
this.indexWithinArray = indexWithinArray; this.indexWithinArray = indexWithinArray;
this.trueName = trueName == null ? "true" : trueName; this.trueName = trueName == null ? "true" : trueName;
@ -136,8 +140,17 @@ public class ConfigField {
String[] nameTokens = nameString.split("\\s"); String[] nameTokens = nameString.split("\\s");
String name = nameTokens[nameTokens.length - 1]; String name = nameTokens[nameTokens.length - 1];
boolean isFsioVisible = nameTokens[0].equalsIgnoreCase("fsio_visible"); boolean isFsioVisible = Arrays.stream(nameTokens).anyMatch(s -> s.equalsIgnoreCase("fsio_visible"));
String autoscaleSpec = null;
for (String autoscaler : nameTokens) {
if (!autoscaler.startsWith("autoscale")) {
continue;
}
autoscaleSpec = autoscaler.split("[<>]")[1];
break;
}
String comment = matcher.group(8); String comment = matcher.group(8);
String type = matcher.group(1); String type = matcher.group(1);
@ -156,7 +169,7 @@ public class ConfigField {
ConfigField field = new ConfigField(state, name, comment, arraySizeAsText, type, arraySize, ConfigField field = new ConfigField(state, name, comment, arraySizeAsText, type, arraySize,
tsInfo, isIterate, isFsioVisible, null, -1, null, null); tsInfo, isIterate, isFsioVisible, autoscaleSpec, null, -1, null, null);
SystemOut.println("type " + type); SystemOut.println("type " + type);
SystemOut.println("name " + name); SystemOut.println("name " + name);
SystemOut.println("comment " + comment); SystemOut.println("comment " + comment);
@ -237,6 +250,8 @@ public class ConfigField {
return fsioVisible; return fsioVisible;
} }
public String autoscaleSpec() { return this.autoscaleSpec; }
public String getUnits() { public String getUnits() {
if (tsInfo == null) if (tsInfo == null)
return ""; return "";

View File

@ -75,7 +75,7 @@ public class ConfigStructure {
if (fillSize != 0) { if (fillSize != 0) {
ConfigField fill = new ConfigField(state, ALIGNMENT_FILL_AT + totalSize, "need 4 byte alignment", ConfigField fill = new ConfigField(state, ALIGNMENT_FILL_AT + totalSize, "need 4 byte alignment",
"" + fillSize, "" + fillSize,
TypesHelper.UINT8_T, fillSize, "\"units\", 1, 0, -20, 100, 0", false, false, null, -1, null, null); TypesHelper.UINT8_T, fillSize, "\"units\", 1, 0, -20, 100, 0", false, false, null, null, -1, null, null);
addBoth(fill); addBoth(fill);
} }
totalSize += fillSize; totalSize += fillSize;
@ -109,7 +109,7 @@ public class ConfigStructure {
return; return;
int sizeAtStartOfPadding = cFields.size(); int sizeAtStartOfPadding = cFields.size();
while (readingBitState.get() < 32) { while (readingBitState.get() < 32) {
ConfigField bitField = new ConfigField(readerState, "unusedBit_" + sizeAtStartOfPadding + "_" + readingBitState.get(), "", null, BOOLEAN_T, 0, null, false, false, null, -1, null, null); ConfigField bitField = new ConfigField(readerState, "unusedBit_" + sizeAtStartOfPadding + "_" + readingBitState.get(), "", null, BOOLEAN_T, 0, null, false, false, null, null, -1, null, null);
addBitField(bitField); addBitField(bitField);
} }
readingBitState.reset(); readingBitState.reset();

View File

@ -52,7 +52,7 @@ public class ReaderState {
String trueName = bitNameParts.length > 1 ? bitNameParts[1].replaceAll("\"", "") : null; String trueName = bitNameParts.length > 1 ? bitNameParts[1].replaceAll("\"", "") : null;
String falseName = bitNameParts.length > 2 ? bitNameParts[2].replaceAll("\"", "") : null; String falseName = bitNameParts.length > 2 ? bitNameParts[2].replaceAll("\"", "") : null;
ConfigField bitField = new ConfigField(state, bitNameParts[0], comment, null, BOOLEAN_T, 0, null, false, false, null, -1, trueName, falseName); ConfigField bitField = new ConfigField(state, bitNameParts[0], comment, null, BOOLEAN_T, 0, null, false, false, null, null, -1, trueName, falseName);
if (state.stack.isEmpty()) if (state.stack.isEmpty())
throw new IllegalStateException("Parent structure expected"); throw new IllegalStateException("Parent structure expected");
ConfigStructure structure = state.stack.peek(); ConfigStructure structure = state.stack.peek();
@ -246,7 +246,7 @@ public class ReaderState {
if (cf == null) { if (cf == null) {
if (ConfigField.isPreprocessorDirective(state, line)) { if (ConfigField.isPreprocessorDirective(state, line)) {
cf = new ConfigField(state, "", line, null, cf = new ConfigField(state, "", line, null,
ConfigField.DIRECTIVE_T, 0, null, false, false, null, 0, null, null); ConfigField.DIRECTIVE_T, 0, null, false, false, null, null, 0, null, null);
} else { } else {
throw new IllegalStateException("Cannot parse line [" + line + "]"); throw new IllegalStateException("Cannot parse line [" + line + "]");
} }
@ -269,7 +269,7 @@ public class ReaderState {
structure.addC(cf); structure.addC(cf);
for (int i = 1; i <= cf.getArraySize(); i++) { for (int i = 1; i <= cf.getArraySize(); i++) {
ConfigField element = new ConfigField(state, cf.getName() + i, cf.getComment(), null, ConfigField element = new ConfigField(state, cf.getName() + i, cf.getComment(), null,
cf.getType(), 1, cf.getTsInfo(), false, false, cf.getName(), i, null, null); cf.getType(), 1, cf.getTsInfo(), false, false, null, cf.getName(), i, null, null);
structure.addTs(element); structure.addTs(element);
} }
} else if (cf.isDirective()) { } else if (cf.isDirective()) {

View File

@ -325,6 +325,11 @@ public class ParseState {
public void exitScalarField(RusefiConfigGrammarParser.ScalarFieldContext ctx) { public void exitScalarField(RusefiConfigGrammarParser.ScalarFieldContext ctx) {
String type = ctx.identifier(0).getText(); String type = ctx.identifier(0).getText();
String name = ctx.identifier(1).getText(); String name = ctx.identifier(1).getText();
boolean autoscale = ctx.autoscale() != null;
if (autoscale) {
evalResults.remove();
}
// First check if this is an instance of a struct // First check if this is an instance of a struct
if (structs.containsKey(type)) { if (structs.containsKey(type)) {
@ -351,7 +356,7 @@ public class ParseState {
// Merge the read-in options list with the default from the typedef (if exists) // Merge the read-in options list with the default from the typedef (if exists)
handleFieldOptionsList(options, ctx.fieldOptionsList()); handleFieldOptionsList(options, ctx.fieldOptionsList());
ScalarField prototype = new ScalarField(arTypedef.type, name, options); ScalarField prototype = new ScalarField(arTypedef.type, name, options, autoscale);
scope.structFields.add(new ArrayField<>(prototype, arTypedef.length, false)); scope.structFields.add(new ArrayField<>(prototype, arTypedef.length, false));
return; return;
} else if (typedef instanceof EnumTypedef) { } else if (typedef instanceof EnumTypedef) {
@ -384,7 +389,7 @@ public class ParseState {
// Merge the read-in options list with the default from the typedef (if exists) // Merge the read-in options list with the default from the typedef (if exists)
handleFieldOptionsList(options, ctx.fieldOptionsList()); handleFieldOptionsList(options, ctx.fieldOptionsList());
scope.structFields.add(new ScalarField(Type.findByCtype(type).get(), name, options)); scope.structFields.add(new ScalarField(Type.findByCtype(type).get(), name, options, autoscale));
} }
@Override @Override
@ -427,6 +432,11 @@ public class ParseState {
int[] length = this.arrayDim; int[] length = this.arrayDim;
// check if the iterate token is present // check if the iterate token is present
boolean iterate = ctx.Iterate() != null; boolean iterate = ctx.Iterate() != null;
boolean autoscale = ctx.autoscale() != null;
if (autoscale) {
evalResults.remove();
}
// First check if this is an array of structs // First check if this is an array of structs
if (structs.containsKey(type)) { if (structs.containsKey(type)) {
@ -483,7 +493,7 @@ public class ParseState {
// Merge the read-in options list with the default from the typedef (if exists) // Merge the read-in options list with the default from the typedef (if exists)
handleFieldOptionsList(options, ctx.fieldOptionsList()); handleFieldOptionsList(options, ctx.fieldOptionsList());
ScalarField prototype = new ScalarField(Type.findByCtype(type).get(), name, options); ScalarField prototype = new ScalarField(Type.findByCtype(type).get(), name, options, autoscale);
scope.structFields.add(new ArrayField<>(prototype, length, iterate)); scope.structFields.add(new ArrayField<>(prototype, length, iterate));
} }

View File

@ -12,11 +12,13 @@ public class ScalarLayout extends Layout {
private String name; private String name;
private Type type; private Type type;
private FieldOptions options; private FieldOptions options;
private boolean autoscale;
public ScalarLayout(ScalarField field) { public ScalarLayout(ScalarField field) {
this.name = field.name; this.name = field.name;
this.options = field.options; this.options = field.options;
this.type = field.type; this.type = field.type;
this.autoscale = field.autoscale;
} }
@Override @Override
@ -86,7 +88,14 @@ public class ScalarLayout extends Layout {
@Override @Override
public void writeCLayout(PrintStream ps) { public void writeCLayout(PrintStream ps) {
this.writeCOffsetHeader(ps, this.options.comment, this.options.units); this.writeCOffsetHeader(ps, this.options.comment, this.options.units);
ps.print("\t" + this.type.cType.replaceAll("^int32_t$", "int") + " " + this.name);
String cTypeName = this.type.cType.replaceAll("^int32_t$", "int");
if (this.autoscale) {
cTypeName = "scaled_channel<" + cTypeName + ", " + Math.round(1 / this.options.scale) + ">";
}
ps.print("\t" + cTypeName + " " + this.name);
if (ConfigDefinition.needZeroInit) { if (ConfigDefinition.needZeroInit) {
ps.print(" = (" + this.type.cType.replaceAll("^int32_t$", "int") + ")0"); ps.print(" = (" + this.type.cType.replaceAll("^int32_t$", "int") + ")0");
@ -108,6 +117,12 @@ public class ScalarLayout extends Layout {
al.append(arrayLength[i]); al.append(arrayLength[i]);
} }
ps.println("\t" + this.type.cType.replaceAll("^int32_t$", "int") + " " + this.name + "[" + al + "];"); String cTypeName = this.type.cType.replaceAll("^int32_t$", "int");
if (this.autoscale) {
cTypeName = "scaled_channel<" + cTypeName + ", " + Math.round(1 / this.options.scale) + ">";
}
ps.println("\t" + cTypeName + " " + this.name + "[" + al + "];");
} }
} }

View File

@ -3,12 +3,14 @@ package com.rusefi.newparse.parsing;
public class ScalarField extends PrototypeField { public class ScalarField extends PrototypeField {
public final Type type; public final Type type;
public final FieldOptions options; public final FieldOptions options;
public final Boolean autoscale;
public ScalarField(Type type, String name, FieldOptions options) { public ScalarField(Type type, String name, FieldOptions options, boolean autoscale) {
super(name); super(name);
this.type = type; this.type = type;
this.options = options; this.options = options;
this.autoscale = autoscale;
} }
@Override @Override

View File

@ -16,16 +16,23 @@ public abstract class BaseCHeaderConsumer implements ConfigurationConsumer {
String cEntry = ConfigDefinition.getComment(configField.getCommentContent(), currentOffset, configField.getUnits()); String cEntry = ConfigDefinition.getComment(configField.getCommentContent(), currentOffset, configField.getUnits());
String typeName = configField.getType();
String autoscaleSpec = configField.autoscaleSpec();
if (autoscaleSpec != null) {
typeName = "scaled_channel<" + typeName + ", " + autoscaleSpec + ">";
}
if (!configField.isArray()) { if (!configField.isArray()) {
// not an array // not an array
cEntry += "\t" + configField.getType() + " " + configField.getName(); cEntry += "\t" + typeName + " " + configField.getName();
if (ConfigDefinition.needZeroInit && TypesHelper.isPrimitive(configField.getType())) { if (ConfigDefinition.needZeroInit && TypesHelper.isPrimitive(configField.getType())) {
// we need this cast in case of enums // we need this cast in case of enums
cEntry += " = (" + configField.getType() + ")0"; cEntry += " = (" + configField.getType() + ")0";
} }
cEntry += ";" + EOL; cEntry += ";" + EOL;
} else { } else {
cEntry += "\t" + configField.getType() + " " + configField.getName() + "[" + configField.arraySizeVariableName + "];" + EOL; cEntry += "\t" + typeName + " " + configField.getName() + "[" + configField.arraySizeVariableName + "];" + EOL;
} }
return cEntry; return cEntry;
} }

View File

@ -48,8 +48,8 @@ TEST(FuelComputer, FlexFuel) {
INJECT_ENGINE_REFERENCE(&dut); INJECT_ENGINE_REFERENCE(&dut);
// easier values for testing // easier values for testing
engineConfiguration->stoichRatioPrimary = 150; engineConfiguration->stoichRatioPrimary = 15;
engineConfiguration->stoichRatioSecondary = 100; engineConfiguration->stoichRatioSecondary = 10;
// No sensor -> returns primary // No sensor -> returns primary
Sensor::resetMockValue(SensorType::FuelEthanolPercent); Sensor::resetMockValue(SensorType::FuelEthanolPercent);

View File

@ -298,7 +298,7 @@ TEST(misc, testRpmCalculator) {
ENGINE(tdcMarkEnabled) = false; ENGINE(tdcMarkEnabled) = false;
// These tests were written when the default target AFR was 14.0, so replicate that // These tests were written when the default target AFR was 14.0, so replicate that
engineConfiguration->stoichRatioPrimary = 140; engineConfiguration->stoichRatioPrimary = 14;
EXPECT_CALL(eth.mockAirmass, getAirmass(_)) EXPECT_CALL(eth.mockAirmass, getAirmass(_))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f})); .WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));