rusefi/java_tools/configuration_definition_base/src/main/java/com/rusefi/output/TsOutput.java

201 lines
9.2 KiB
Java

package com.rusefi.output;
import com.opensr5.ini.field.IniField;
import com.rusefi.ConfigField;
import com.rusefi.ConfigFieldImpl;
import com.rusefi.ReaderState;
import com.rusefi.parse.Type;
import com.rusefi.parse.TypesHelper;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;
import static com.rusefi.ToolUtil.EOL;
import static com.rusefi.output.JavaSensorsConsumer.quote;
/**
* Same code is used to generate [Constants] and [OutputChannels] bodies, with just one flag controlling the minor
* difference in behaviours
*/
@SuppressWarnings({"StringConcatenationInsideStringBufferAppend", "DanglingJavadoc"})
public class TsOutput {
private final StringBuilder settingContextHelp = new StringBuilder();
private final boolean isConstantsSection;
private final StringBuilder tsHeader = new StringBuilder();
private final TreeSet<String> usedNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
public TsOutput(boolean longForm) {
this.isConstantsSection = longForm;
}
public String getContent() {
return tsHeader.toString();
}
public String getSettingContextHelp() {
return settingContextHelp.toString();
}
public int run(ReaderState state, ConfigStructure structure, int sensorTsPosition, String temporaryLineComment, String variableNameSuffix) {
FieldsStrategy strategy = new FieldsStrategy() {
@Override
public int writeOneField(FieldIterator it, String prefix, int tsPosition) {
ConfigField configField = it.cf;
ConfigField next = it.next;
int bitIndex = it.bitState.get();
String nameWithPrefix = prefix + configField.getName() + variableNameSuffix;
/**
* in 'Constants' section we have conditional sections and this check is not smart enough to handle those right
* A simple solution would be to allow only one variable per each conditional section - would be simpler not to check against previous field
*/
if (!usedNames.add(nameWithPrefix)
&& !isConstantsSection
&& !configField.getName().startsWith(ConfigStructureImpl.ALIGNMENT_FILL_AT)
&& !configField.getName().startsWith(ConfigStructure.UNUSED_ANYTHING_PREFIX)) {
throw new IllegalStateException(nameWithPrefix + " already present: " + configField);
}
if (configField.getName().startsWith(ConfigStructureImpl.ALIGNMENT_FILL_AT)) {
tsPosition += configField.getSize(next);
return tsPosition;
}
if (configField.isDirective() && configField.getComment() != null) {
tsHeader.append(configField.getComment());
tsHeader.append(EOL);
return tsPosition;
}
ConfigStructure cs = configField.getStructureType();
if (configField.getComment() != null && configField.getComment().trim().length() > 0 && cs == null) {
String commentContent = configField.getCommentTemplated();
commentContent = ConfigFieldImpl.unquote(commentContent);
settingContextHelp.append(temporaryLineComment + "\t" + nameWithPrefix + " = " + quote(commentContent) + EOL);
}
if (cs != null) {
String extraPrefix = cs.isWithPrefix() ? configField.getName() + "_" : "";
return writeFields(cs.getTsFields(), prefix + extraPrefix, tsPosition);
}
if (configField.isBit()) {
if (!configField.getName().startsWith(ConfigStructureImpl.UNUSED_BIT_PREFIX)) {
tsHeader.append(temporaryLineComment + nameWithPrefix + " = bits, U32,");
tsHeader.append(" " + tsPosition + ", [");
tsHeader.append(bitIndex + ":" + bitIndex);
tsHeader.append("]");
if (isConstantsSection)
tsHeader.append(", \"" + configField.getFalseName() + "\", \"" + configField.getTrueName() + "\"");
tsHeader.append(EOL);
}
tsPosition += configField.getSize(next);
return tsPosition;
}
if (configField.getState().getTsCustomLine().containsKey(configField.getTypeName())) {
// todo: rename 'bits' to 'customLine' or something since _not_ bits for array?
String bits = configField.getState().getTsCustomLine().get(configField.getTypeName());
if (!bits.startsWith("bits")) {
// 'array' would be handled here
bits = handleTsInfo(configField, bits, 5);
}
bits = bits.replaceAll("@OFFSET@", "" + tsPosition);
tsHeader.append(nameWithPrefix + " = " + bits);
if (!configField.getName().equals(next.getName()))
tsPosition += configField.getState().getTsCustomSize().get(configField.getTypeName());
} else if (configField.getArraySizes().length == 0) {
tsHeader.append(temporaryLineComment + nameWithPrefix + " = scalar, ");
tsHeader.append(TypesHelper.convertToTs(configField.getTypeName()) + ",");
tsHeader.append(" " + tsPosition + ",");
tsHeader.append(" " + handleTsInfo(configField, configField.getTsInfo(), 1));
if (!configField.getName().equals(next.getName()))
tsPosition += configField.getSize(next);
} else if (configField.getSize(next) == 0) {
// write nothing for empty array
// TS does not like those
} else {
tsHeader.append(nameWithPrefix + " = array, ");
tsHeader.append(TypesHelper.convertToTs(configField.getTypeName()) + ",");
tsHeader.append(" " + tsPosition + ",");
tsHeader.append(" [");
boolean first = true;
List<Integer> list = Arrays.stream(configField.getArraySizes()).boxed().collect(Collectors.toList());
Collections.reverse(list);
for (int size : list) {
if (first) {
first = false;
} else {
tsHeader.append("x");
}
tsHeader.append(size);
}
tsHeader.append("], " + handleTsInfo(configField, configField.getTsInfo(), 1));
if (!configField.getName().equals(next.getName()))
tsPosition += configField.getSize(next);
}
tsHeader.append(EOL);
return tsPosition;
}
};
sensorTsPosition = strategy.run(state, structure, sensorTsPosition);
if (state.isStackEmpty()) {
tsHeader.append("; total TS size = " + sensorTsPosition + EOL);
}
return sensorTsPosition;
}
private String handleTsInfo(ConfigField configField, String tsInfo, int multiplierIndex) {
if (tsInfo == null || tsInfo.trim().isEmpty()) {
// default units and scale
if (isConstantsSection) {
if (configField.getTypeName().equalsIgnoreCase(Type.U16.cType) || configField.getTypeName().equalsIgnoreCase(Type.S16.cType))
return quote("") + ", 1, 0, 0, 32000, 0";
return quote("") + ", 1, 0, 0, 100, 0";
}
return quote("") + ", 1, 0";
}
try {
String[] fields = tsInfo.split(",");
if (fields.length > multiplierIndex) {
/**
* Evaluate static math on .ini layer to simplify rusEFI java and rusEFI PHP project consumers
* https://github.com/rusefi/web_backend/issues/97
*/
double val = IniField.parseDouble(fields[multiplierIndex]);
if (val == 0) {
fields[multiplierIndex] = " 0";
} else if (val == 1) {
fields[multiplierIndex] = " 1";
} else {
fields[multiplierIndex] = " " + val;
}
}
StringBuilder sb = new StringBuilder();
if (!isConstantsSection) {
String[] subarray = new String[3];
System.arraycopy(fields, 0, subarray, 0, subarray.length);
fields = subarray;
}
for (String f : fields) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(f);
}
return sb.toString();
} catch (Throwable e) {
throw new IllegalStateException("While parsing [" + tsInfo + "] of " + configField, e);
}
}
}