/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2016 RomRaider.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.romraider.logger.ecu.definition.xml;
import static com.romraider.Settings.COMMA;
import static com.romraider.logger.ecu.definition.xml.ConverterMaxMinDefaults.getMax;
import static com.romraider.logger.ecu.definition.xml.ConverterMaxMinDefaults.getMin;
import static com.romraider.logger.ecu.definition.xml.ConverterMaxMinDefaults.getStep;
import static com.romraider.util.HexUtil.asBytes;
import static com.romraider.util.ParamChecker.checkNotNullOrEmpty;
import static com.romraider.util.ParamChecker.isNullOrEmpty;
import static java.lang.Double.parseDouble;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import com.romraider.Settings;
import com.romraider.io.connection.ConnectionProperties;
import com.romraider.io.connection.SerialConnectionProperties;
import com.romraider.logger.ecu.comms.query.EcuInit;
import com.romraider.logger.ecu.definition.EcuAddress;
import com.romraider.logger.ecu.definition.EcuAddressImpl;
import com.romraider.logger.ecu.definition.EcuData;
import com.romraider.logger.ecu.definition.EcuDataConvertor;
import com.romraider.logger.ecu.definition.EcuDerivedParameterConvertor;
import com.romraider.logger.ecu.definition.EcuDerivedParameterConvertorImpl;
import com.romraider.logger.ecu.definition.EcuDerivedParameterImpl;
import com.romraider.logger.ecu.definition.EcuDtcConvertorImpl;
import com.romraider.logger.ecu.definition.EcuParameter;
import com.romraider.logger.ecu.definition.EcuParameterConvertorImpl;
import com.romraider.logger.ecu.definition.EcuParameterImpl;
import com.romraider.logger.ecu.definition.EcuSwitch;
import com.romraider.logger.ecu.definition.EcuSwitchConvertorImpl;
import com.romraider.logger.ecu.definition.EcuSwitchImpl;
import com.romraider.logger.ecu.definition.Module;
import com.romraider.logger.ecu.definition.Transport;
import com.romraider.logger.ecu.ui.handler.dash.GaugeMinMax;
public final class LoggerDefinitionHandler extends DefaultHandler {
private static final String TAG_LOGGER = "logger";
private static final String TAG_PROTOCOL = "protocol";
private static final String TAG_PARAMETER = "parameter";
private static final String TAG_ADDRESS = "address";
private static final String TAG_DEPENDS = "depends";
private static final String TAG_CONVERSION = "conversion";
private static final String TAG_REPLACE = "replace";
private static final String TAG_REF = "ref";
private static final String TAG_SWITCH = "switch";
private static final String TAG_ECUPARAM = "ecuparam";
private static final String TAG_ECU = "ecu";
private static final String TAG_DTCODE = "dtcode";
private static final String TAG_TRANSPORT = "transport";
private static final String TAG_MODULE = "module";
private static final String ATTR_VERSION = "version";
private static final String ATTR_ID = "id";
private static final String ATTR_NAME = "name";
private static final String ATTR_DESC = "desc";
private static final String ATTR_ECUBYTEINDEX = "ecubyteindex";
private static final String ATTR_LENGTH = "length";
private static final String ATTR_ECUBIT = "ecubit";
private static final String ATTR_UNITS = "units";
private static final String ATTR_GROUP = "group";
private static final String ATTR_SUBGROUP = "subgroup";
private static final String ATTR_GROUPSIZE = "groupsize";
private static final String ATTR_EXPRESSION = "expr";
private static final String ATTR_FORMAT = "format";
private static final String ATTR_BYTE = "byte";
private static final String ATTR_BIT = "bit";
private static final String ATTR_PARAMETER = "parameter";
private static final String ATTR_STORAGETYPE = "storagetype";
private static final String ATTR_ENDIAN = "endian";
private static final String ATTR_BAUD = "baud";
private static final String ATTR_DATABITS = "databits";
private static final String ATTR_STOPBITS = "stopbits";
private static final String ATTR_PARITY = "parity";
private static final String ATTR_CONNECT_TIMEOUT = "connect_timeout";
private static final String ATTR_SEND_TIMEOUT = "send_timeout";
private static final String ATTR_VALUE = "value";
private static final String ATTR_WITH = "with";
private static final String ATTR_GAUGE_MIN = "gauge_min";
private static final String ATTR_GAUGE_MAX = "gauge_max";
private static final String ATTR_GAUGE_STEP = "gauge_step";
private static final String ATTR_TARGET = "target";
private static final String ATTR_TMPADDR = "tmpaddr";
private static final String ATTR_MEMADDR = "memaddr";
private static final String ATTR_ADDRESS = "address";
private static final String ATTR_TESTER = "tester";
private static final String ATTR_FASTPOLL = "fastpoll";
private final String protocol;
private final String fileLoggingControllerSwitchId;
private final EcuInit ecuInit;
private List<EcuParameter> params = new ArrayList<EcuParameter>();
private List<EcuSwitch> switches = new ArrayList<EcuSwitch>();
private List<EcuSwitch> dtcodes = new ArrayList<EcuSwitch>();
private EcuSwitch fileLoggingControllerSwitch;
private ConnectionProperties connectionProperties;
private Map<String, EcuData> ecuDataMap;
private Map<String, String> replaceMap;
private String id;
private String name;
private String desc;
private String ecuByteIndex;
private String ecuBit;
private String group;
private String subgroup;
private String groupsize;
private String ecuIds;
private List<String> addrStrings = new ArrayList<String>();
private boolean EcuAddressCreated;
private EcuAddress address;
private Set<String> dependsList;
private Map<String, EcuAddress> ecuAddressMap;
private boolean derived;
private int addressBit;
private int addressLength;
private Set<EcuDataConvertor> convertorList;
private Set<EcuDerivedParameterConvertor> derivedConvertorList;
private StringBuilder charBuffer;
private boolean parseProtocol;
private String conversionUnits;
private String conversionExpression;
private String conversionFormat;
private String conversionStorageType;
private int conversionEndian;
private GaugeMinMax conversionGauge;
private String target;
private String version;
private String protocolId;
private Transport transport;
private Collection<Module> moduleList;
private Map<Transport, Collection<Module>> transportMap;
private Map<String, Map<Transport, Collection<Module>>> protocolMap;
public LoggerDefinitionHandler(String protocol, String fileLoggingControllerSwitchId, EcuInit ecuInit) {
checkNotNullOrEmpty(protocol, "protocol");
checkNotNullOrEmpty(fileLoggingControllerSwitchId, "fileLoggingControllerSwitchId");
this.protocol = protocol;
this.fileLoggingControllerSwitchId = fileLoggingControllerSwitchId;
this.ecuInit = ecuInit;
}
public void startDocument() {
params = new ArrayList<EcuParameter>();
switches = new ArrayList<EcuSwitch>();
ecuDataMap = new HashMap<String, EcuData>();
dtcodes = new ArrayList<EcuSwitch>();
protocolMap = new HashMap<String, Map<Transport, Collection<Module>>>();
}
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (TAG_LOGGER.equals(qName)) {
version = attributes.getValue(ATTR_VERSION);
} else if (TAG_PROTOCOL.equals(qName)) {
protocolId = attributes.getValue(ATTR_ID);
parseProtocol = protocol.equalsIgnoreCase(protocolId);
if (parseProtocol) {
connectionProperties = new SerialConnectionProperties(Integer.parseInt(attributes.getValue(ATTR_BAUD)),
Integer.parseInt(attributes.getValue(ATTR_DATABITS)), Integer.parseInt(attributes.getValue(ATTR_STOPBITS)),
Integer.parseInt(attributes.getValue(ATTR_PARITY)), Integer.parseInt(attributes.getValue(ATTR_CONNECT_TIMEOUT)),
Integer.parseInt(attributes.getValue(ATTR_SEND_TIMEOUT)));
}
transportMap = new HashMap<Transport, Collection<Module>>();
} else if (TAG_TRANSPORT.equals(qName)) {
id = attributes.getValue(ATTR_ID);
name = attributes.getValue(ATTR_NAME);
desc = attributes.getValue(ATTR_DESC);
transport = new Transport(id, name, desc);
moduleList = new ArrayList<Module>();
} else if (TAG_MODULE.equals(qName)) {
id = attributes.getValue(ATTR_ID);
final String modAddr = attributes.getValue(ATTR_ADDRESS);
desc = attributes.getValue(ATTR_DESC);
final String testerAddr = attributes.getValue(ATTR_TESTER);
final String fastpoll = attributes.getValue(ATTR_FASTPOLL);
boolean fp = false;
if (fastpoll != null && fastpoll.equalsIgnoreCase("true")) {
fp = true;
}
final Module module = new Module(
id, asBytes(modAddr), desc, asBytes(testerAddr), fp);
moduleList.add(module);
} else if (parseProtocol) {
if (TAG_PARAMETER.equals(qName)) {
id = attributes.getValue(ATTR_ID);
name = attributes.getValue(ATTR_NAME);
desc = attributes.getValue(ATTR_DESC);
ecuByteIndex = attributes.getValue(ATTR_ECUBYTEINDEX);
ecuBit = attributes.getValue(ATTR_ECUBIT);
group = attributes.getValue(ATTR_GROUP);
subgroup = attributes.getValue(ATTR_SUBGROUP);
groupsize = attributes.getValue(ATTR_GROUPSIZE);
target = attributes.getValue(ATTR_TARGET);
resetLists();
} else if (TAG_ADDRESS.equals(qName)) {
String length = attributes.getValue(ATTR_LENGTH);
addressLength = length == null ? 1 : Integer.valueOf(length);
String bit = attributes.getValue(ATTR_BIT);
addressBit = bit == null ? -1 : Integer.valueOf(bit);
derived = false;
} else if (TAG_DEPENDS.equals(qName)) {
dependsList = new LinkedHashSet<String>();
derived = true;
} else if (TAG_REF.equals(qName)) {
dependsList.add(attributes.getValue(ATTR_PARAMETER));
} else if (TAG_CONVERSION.equals(qName)) {
conversionUnits = attributes.getValue(ATTR_UNITS);
conversionExpression = attributes.getValue(ATTR_EXPRESSION);
conversionFormat = attributes.getValue(ATTR_FORMAT);
conversionStorageType = attributes.getValue(ATTR_STORAGETYPE);
String endian = attributes.getValue(ATTR_ENDIAN);
if (endian != null) {
conversionEndian = endian.equalsIgnoreCase("little") ? Settings.ENDIAN_LITTLE : Settings.ENDIAN_BIG;
}
else {
conversionEndian = Settings.ENDIAN_BIG;
}
double gaugeMin = getConversionMin(attributes, conversionUnits);
double gaugeMax = getConversionMax(attributes, conversionUnits);
double gaugeStep = getConversionStep(attributes, conversionUnits);
conversionGauge = new GaugeMinMax(gaugeMin, gaugeMax, gaugeStep);
replaceMap = new HashMap<String, String>();
if (!derived && !EcuAddressCreated && addrStrings.size() > 0) {
address = new EcuAddressImpl(addrStrings.toArray(new String[0]), addressLength, addressBit);
EcuAddressCreated = true;
}
} else if (TAG_REPLACE.equals(qName)) {
replaceMap.put(attributes.getValue(ATTR_VALUE), attributes.getValue(ATTR_WITH));
} else if (TAG_SWITCH.equals(qName)) {
id = attributes.getValue(ATTR_ID);
name = attributes.getValue(ATTR_NAME);
desc = attributes.getValue(ATTR_DESC);
ecuByteIndex = attributes.getValue(ATTR_ECUBYTEINDEX);
ecuBit = attributes.getValue(ATTR_BIT);
group = attributes.getValue(ATTR_GROUP);
subgroup = attributes.getValue(ATTR_SUBGROUP);
groupsize = attributes.getValue(ATTR_GROUPSIZE);
target = attributes.getValue(ATTR_TARGET);
address = new EcuAddressImpl(attributes.getValue(ATTR_BYTE), 1, Integer.valueOf(attributes.getValue(ATTR_BIT)));
resetLists();
} else if (TAG_ECUPARAM.equals(qName)) {
id = attributes.getValue(ATTR_ID);
name = attributes.getValue(ATTR_NAME);
desc = attributes.getValue(ATTR_DESC);
group = attributes.getValue(ATTR_GROUP);
subgroup = attributes.getValue(ATTR_SUBGROUP);
groupsize = attributes.getValue(ATTR_GROUPSIZE);
target = attributes.getValue(ATTR_TARGET);
resetLists();
ecuAddressMap = new HashMap<String, EcuAddress>();
derived = false;
} else if (TAG_ECU.equals(qName)) {
ecuIds = attributes.getValue(ATTR_ID);
} else if (TAG_DTCODE.equals(qName)) {
id = attributes.getValue(ATTR_ID);
name = attributes.getValue(ATTR_NAME);
desc = attributes.getValue(ATTR_DESC);
address = new EcuAddressImpl(
new String[] {attributes.getValue(ATTR_TMPADDR),
attributes.getValue(ATTR_MEMADDR)
},
1,
Integer.valueOf(attributes.getValue(ATTR_BIT)));
resetLists();
}
}
charBuffer = new StringBuilder();
}
public void characters(char[] ch, int start, int length) {
if (parseProtocol) {
charBuffer.append(ch, start, length);
}
}
public void endElement(String uri, String localName, String qName) {
if (TAG_PROTOCOL.equals(qName)) {
parseProtocol = false;
protocolMap.put(protocolId, transportMap);
} else if (TAG_TRANSPORT.equals(qName)) {
transportMap.put(transport, moduleList);
} else if (parseProtocol) {
if (TAG_ADDRESS.equals(qName)) {
addrStrings.add(charBuffer.toString());
} else if (TAG_PARAMETER.equals(qName)) {
if (derived) {
Set<EcuData> dependencies = new HashSet<EcuData>();
for (String refid : dependsList) {
if (ecuDataMap.containsKey(refid)) {
dependencies.add(ecuDataMap.get(refid));
}
}
if (dependsList.size() == dependencies.size()) {
EcuParameter param = new EcuDerivedParameterImpl(id, name, desc,
dependencies.toArray(new EcuData[dependencies.size()]),
derivedConvertorList.toArray(new EcuDerivedParameterConvertor[derivedConvertorList.size()]));
params.add(param);
ecuDataMap.put(param.getId(), param);
}
} else {
if (ecuByteIndex == null || ecuBit == null || ecuInit == null || isSupportedParameter(ecuInit,
ecuByteIndex, ecuBit)) {
if (convertorList.isEmpty()) {
convertorList.add(new EcuParameterConvertorImpl());
}
EcuParameter param = new EcuParameterImpl(
id, name, desc, address, group, subgroup, groupsize,
convertorList.toArray(new EcuDataConvertor[convertorList.size()]));
params.add(param);
ecuDataMap.put(param.getId(), param);
}
}
} else if (TAG_CONVERSION.equals(qName)) {
if (derived) {
derivedConvertorList.add(new EcuDerivedParameterConvertorImpl(conversionUnits,
conversionExpression, conversionFormat, replaceMap, conversionGauge));
} else {
convertorList.add(new EcuParameterConvertorImpl(
conversionUnits, conversionExpression, conversionFormat,
address.getBit(), conversionStorageType, conversionEndian,
replaceMap, conversionGauge));
}
} else if (TAG_ECUPARAM.equals(qName)) {
if (ecuInit != null && ecuAddressMap.containsKey(ecuInit.getEcuId())) {
if (convertorList.isEmpty()) {
convertorList.add(new EcuParameterConvertorImpl());
}
EcuParameter param = new EcuParameterImpl(
id, name, desc, ecuAddressMap.get(ecuInit.getEcuId()),
group, subgroup, groupsize,
convertorList.toArray(new EcuDataConvertor[convertorList.size()]));
params.add(param);
ecuDataMap.put(param.getId(), param);
}
} else if (TAG_ECU.equals(qName)) {
address = new EcuAddressImpl(addrStrings.toArray(new String[0]), addressLength, addressBit);
EcuAddressCreated = true;
for (String ecuId : ecuIds.split(COMMA)) {
ecuAddressMap.put(ecuId, address);
}
addrStrings.clear();
} else if (TAG_SWITCH.equals(qName)) {
if (ecuByteIndex == null || ecuBit == null || ecuInit == null || isSupportedParameter(ecuInit,
ecuByteIndex, ecuBit)) {
EcuDataConvertor[] convertors = new EcuDataConvertor[]{new EcuSwitchConvertorImpl(address.getBit())};
EcuSwitch ecuSwitch = new EcuSwitchImpl(
id, name, desc, address,
group, subgroup, groupsize, convertors);
switches.add(ecuSwitch);
ecuDataMap.put(ecuSwitch.getId(), ecuSwitch);
if (id.equalsIgnoreCase(fileLoggingControllerSwitchId)) {
fileLoggingControllerSwitch = new EcuSwitchImpl(
id, name, desc, address,
group, subgroup, groupsize, convertors);
}
}
} else if (TAG_DTCODE.equals(qName)) {
final EcuDataConvertor[] convertors = new EcuDataConvertor[]{new EcuDtcConvertorImpl(address.getBit())};
final EcuSwitch ecuSwitch = new EcuSwitchImpl(
id, name, desc, address,
group, subgroup, groupsize, convertors);
dtcodes.add(ecuSwitch);
ecuDataMap.put(ecuSwitch.getId(), ecuSwitch);
}
}
}
public List<EcuParameter> getEcuParameters() {
return params;
}
public List<EcuSwitch> getEcuSwitches() {
return switches;
}
public EcuSwitch getFileLoggingControllerSwitch() {
return fileLoggingControllerSwitch;
}
public ConnectionProperties getConnectionProperties() {
return connectionProperties;
}
public String getVersion() {
return version;
}
public List<EcuSwitch> getEcuCodes() {
return dtcodes;
}
public Map<String, Map<Transport, Collection<Module>>> getProtocols() {
if (protocolMap.get(protocol).isEmpty()) {
final Module module = new Module(
"ecu", new byte[]{0x10}, "Engine Control Unit",
new byte[]{(byte)0xF0}, true);
moduleList = new ArrayList<Module>();
moduleList.add(module);
transport = new Transport("iso9141", "K-Line", "Low speed serial");
transportMap = new HashMap<Transport, Collection<Module>>();
transportMap.put(transport, moduleList);
protocolMap.put(protocol, transportMap);
}
return protocolMap;
}
private void resetLists() {
addrStrings.clear();
EcuAddressCreated = false;
convertorList = new LinkedHashSet<EcuDataConvertor>();
derivedConvertorList = new LinkedHashSet<EcuDerivedParameterConvertor>();
}
private boolean isSupportedParameter(EcuInit ecuInit, String ecuByteIndex, String ecuBit) {
byte[] ecuInitBytes = ecuInit.getEcuInitBytes();
int index = Integer.parseInt(ecuByteIndex);
if (index < ecuInitBytes.length) {
byte[] bytes = new byte[1];
System.arraycopy(ecuInitBytes, index, bytes, 0, 1);
return (bytes[0] & 1 << Integer.parseInt(ecuBit)) > 0;
} else {
return false;
}
}
private double getConversionMin(Attributes attributes, String units) {
String value = attributes.getValue(ATTR_GAUGE_MIN);
if (!isNullOrEmpty(value)) return parseDouble(value);
return getMin(units);
}
private double getConversionMax(Attributes attributes, String units) {
String value = attributes.getValue(ATTR_GAUGE_MAX);
if (!isNullOrEmpty(value)) return parseDouble(value);
return getMax(units);
}
private double getConversionStep(Attributes attributes, String units) {
String value = attributes.getValue(ATTR_GAUGE_STEP);
if (!isNullOrEmpty(value)) return parseDouble(value);
return getStep(units);
}
}