mirror of https://github.com/rusefi/rusefi.git
262 lines
8.3 KiB
Java
262 lines
8.3 KiB
Java
package com.rusefi.config;
|
|
|
|
import com.macfaq.io.LittleEndianOutputStream;
|
|
import com.opensr5.ConfigurationImage;
|
|
import com.rusefi.core.FileUtil;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.Objects;
|
|
|
|
import static com.rusefi.config.FieldType.*;
|
|
|
|
/**
|
|
* @see Fields
|
|
*/
|
|
|
|
public class Field {
|
|
public static final int NO_BIT_OFFSET = -1;
|
|
private static final int FIELD_PRECISION = 3;
|
|
|
|
private final String name;
|
|
private final int offset;
|
|
private final int stringSize;
|
|
private final FieldType type;
|
|
private final int bitOffset;
|
|
private final String[] options;
|
|
private double scale = 1;
|
|
|
|
public Field(String name, int offset, FieldType type) {
|
|
this(name, offset, type, NO_BIT_OFFSET);
|
|
}
|
|
|
|
public Field(String name, int offset, FieldType type, String... options) {
|
|
this(name, offset, type, NO_BIT_OFFSET, options);
|
|
}
|
|
|
|
public Field(String name, int offset, FieldType type, int bitOffset) {
|
|
this(name, offset, type, bitOffset, null);
|
|
}
|
|
|
|
public Field(String name, int offset, FieldType type, int bitOffset, String[] options) {
|
|
this(name, offset, 0, type, bitOffset, options);
|
|
}
|
|
|
|
public Field(String name, int offset, int stringSize, FieldType type, int bitOffset, String... options) {
|
|
this.name = name;
|
|
this.offset = offset;
|
|
this.stringSize = stringSize;
|
|
this.type = type;
|
|
this.bitOffset = bitOffset;
|
|
this.options = options;
|
|
}
|
|
|
|
public static Field findField(Field[] values, String instancePrefix, String fieldName) {
|
|
Field field = findFieldOrNull(values, instancePrefix, fieldName);
|
|
if (field == null)
|
|
throw new IllegalStateException("No field: " + fieldName);
|
|
return field;
|
|
}
|
|
|
|
/**
|
|
* Finds field by name, ignoring case
|
|
*/
|
|
public static Field findFieldOrNull(Field[] values, String instancePrefix, String fieldName) {
|
|
Objects.requireNonNull(fieldName);
|
|
for (Field f : values) {
|
|
if (fieldName.equalsIgnoreCase(f.getName()))
|
|
return f;
|
|
}
|
|
// 2nd pass - let's try to find field with prefix if it was not found without prefix
|
|
if (!instancePrefix.isEmpty()) {
|
|
fieldName = instancePrefix + "_" + fieldName;
|
|
for (Field f : values) {
|
|
if (fieldName.equalsIgnoreCase(f.getName()))
|
|
return f;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static int getStructureSize(Field[] values) {
|
|
Field last = values[values.length - 1];
|
|
// todo: at the moment we do not support arrays and
|
|
// todo: a lot of information is missing for example for Bit type, but this implementation is good enough for now
|
|
return last.offset + 4;
|
|
}
|
|
|
|
public static String niceToString(Number value) {
|
|
return niceToString(value, FIELD_PRECISION);
|
|
}
|
|
|
|
public static String niceToString(Number value, int precision) {
|
|
// not enum field
|
|
Number number = value;
|
|
if (number instanceof Float)
|
|
return niceToString(number.floatValue(), precision);
|
|
return number.toString();
|
|
}
|
|
|
|
public static String niceToString(double value, int precision) {
|
|
int scale = (int) Math.log10(value);
|
|
int places = 1 + Math.max(0, precision - scale);
|
|
double toScale = Math.pow(10, places);
|
|
return Double.toString(Math.round(value * toScale) / toScale);
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public String setCommand() {
|
|
if (type == FieldType.BIT)
|
|
return "set_bit " + getOffset() + " " + bitOffset;
|
|
return getType().getStoreCommand() + " " + getOffset();
|
|
}
|
|
|
|
public String getCommand() {
|
|
if (type == FieldType.BIT)
|
|
return "get_bit " + getOffset() + " " + bitOffset;
|
|
return type.getLoadCommand() + " " + getOffset();
|
|
}
|
|
|
|
public int getOffset() {
|
|
return offset;
|
|
}
|
|
|
|
public String[] getOptions() {
|
|
return options;
|
|
}
|
|
|
|
public int getBitOffset() {
|
|
return bitOffset;
|
|
}
|
|
|
|
public FieldType getType() {
|
|
return type;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "Field{" +
|
|
name +
|
|
", o=" + offset +
|
|
", type=" + type +
|
|
'}';
|
|
}
|
|
|
|
public Object getAnyValue(ConfigurationImage ci, double multiplier) {
|
|
if (options == null) {
|
|
// we are here for non-enum types
|
|
return niceToString(getValue(ci, multiplier));
|
|
}
|
|
if (type != INT8)
|
|
throw new IllegalStateException("Unsupported enum " + type);
|
|
int ordinal = ci.getByteBuffer(offset, type.getStorageSize()).get();
|
|
return options[ordinal];
|
|
}
|
|
|
|
public void setValue(byte[] content, boolean value) {
|
|
ByteBuffer wrapped = FileUtil.littleEndianWrap(content, 0, content.length);
|
|
if (bitOffset != NO_BIT_OFFSET) {
|
|
int packed = wrapped.getInt();
|
|
int thisBit = (value ? 1 : 0) << bitOffset;
|
|
int mask = 1 << bitOffset;
|
|
int newValue = (packed & ~mask) | thisBit;
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
LittleEndianOutputStream dout = new LittleEndianOutputStream(baos);
|
|
// wow worst way to modify an integer in byte array? :)
|
|
try {
|
|
dout.writeInt(newValue);
|
|
// dout.flush();
|
|
byte[] src = baos.toByteArray();
|
|
System.arraycopy(src, 0, content, getOffset(), 4);
|
|
baos.close();
|
|
} catch (IOException e) {
|
|
throw new IllegalStateException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* each usage is a potential bug?! we are supposed to have explicit multiplier for each field
|
|
*/
|
|
@NotNull
|
|
@Deprecated
|
|
public Double getValue(ConfigurationImage ci) {
|
|
return getValue(ci, 1);
|
|
}
|
|
|
|
// todo: rename to getNumberValue?
|
|
@NotNull
|
|
public Double getValue(ConfigurationImage ci, double multiplier) {
|
|
Objects.requireNonNull(ci, "ConfigurationImage");
|
|
Number value;
|
|
ByteBuffer wrapped = ci.getByteBuffer(getOffset(), type.getStorageSize());
|
|
if (bitOffset != NO_BIT_OFFSET) {
|
|
int packed = wrapped.getInt();
|
|
value = (packed >> bitOffset) & 1;
|
|
} else if (type == INT8) {
|
|
value = wrapped.get();
|
|
} else if (type == UINT8) {
|
|
byte signed = wrapped.get();
|
|
value = signed & 0xFF;
|
|
} else if (type == INT) {
|
|
value = wrapped.getInt();
|
|
} else if (type == INT16) {
|
|
value = wrapped.getShort();
|
|
} else if (type == UINT16) {
|
|
short signed = wrapped.getShort();
|
|
value = signed & 0xFFFF;
|
|
} else {
|
|
value = wrapped.getFloat();
|
|
}
|
|
return value.doubleValue() * multiplier;
|
|
}
|
|
|
|
@NotNull
|
|
public ByteBuffer getByteBuffer(ConfigurationImage ci) {
|
|
return ci.getByteBuffer(getOffset(), 4);
|
|
}
|
|
|
|
public static Field create(String name, int offset, FieldType type, int bitOffset) {
|
|
return new Field(name, offset, type, bitOffset);
|
|
}
|
|
|
|
public static Field create(String name, int offset, FieldType type, String... options) {
|
|
return new Field(name, offset, type, options);
|
|
}
|
|
|
|
public static Field create(String name, int offset, int stringSize, FieldType type) {
|
|
return new Field(name, offset, stringSize, type, 0);
|
|
}
|
|
|
|
public static Field create(String name, int offset, FieldType type) {
|
|
return new Field(name, offset, type);
|
|
}
|
|
|
|
public String getStringValue(ConfigurationImage image) {
|
|
Objects.requireNonNull(image, "image");
|
|
if (type != STRING)
|
|
throw new IllegalStateException("Not a string parameter " + name);
|
|
ByteBuffer bb = image.getByteBuffer(offset, stringSize);
|
|
byte[] bytes = new byte[stringSize];
|
|
bb.get(bytes);
|
|
return new String(bytes).trim();
|
|
}
|
|
|
|
public boolean getBooleanValue(ConfigurationImage ci) {
|
|
return getValue(ci) != 0.0;
|
|
}
|
|
|
|
public Field setScale(double scale) {
|
|
this.scale = scale;
|
|
return this;
|
|
}
|
|
|
|
public double getScale() {
|
|
return scale;
|
|
}
|
|
} |