Add new NCS logging protocol support

This commit is contained in:
Dale Schultz 2018-12-19 21:15:27 -05:00
parent e51606d7f7
commit 150d10df19
No known key found for this signature in database
GPG Key ID: EA2C8AD6CB5C2AF2
17 changed files with 2819 additions and 0 deletions

View File

@ -0,0 +1,3 @@
# use a : to separate possible table names to match against
ltft_table_column_names=Learning map TP shaft lattice point table:
ltft_table_row_names=Learning map N shaft lattice point table:

BIN
lib/linux/j2534.so Normal file

Binary file not shown.

View File

@ -0,0 +1,298 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.io.j2534.api;
import static com.romraider.io.protocol.ncs.iso14230.NCSChecksumCalculator.calculateChecksum;
import static com.romraider.util.HexUtil.asHex;
import static com.romraider.util.ParamChecker.checkNotNull;
import static java.lang.System.arraycopy;
import static org.apache.log4j.Logger.getLogger;
import org.apache.log4j.Logger;
import com.romraider.io.connection.ConnectionManager;
import com.romraider.io.connection.ConnectionProperties;
import com.romraider.io.connection.KwpConnectionProperties;
import com.romraider.io.j2534.api.J2534Impl.Config;
import com.romraider.io.j2534.api.J2534Impl.Flag;
import com.romraider.io.j2534.api.J2534Impl.Protocol;
import com.romraider.io.j2534.api.J2534Impl.TxFlags;
import com.romraider.logger.ecu.comms.manager.PollingState;
public final class J2534ConnectionISO14230 implements ConnectionManager {
private static final Logger LOGGER = getLogger(J2534ConnectionISO14230.class);
private J2534 api = null;
private int LOOPBACK = 0;
private int channelId;
private int deviceId;
private int msgId;
private byte[] lastResponse;
private long timeout;
private boolean commsStarted;
private final byte[] startReq = {
(byte) 0x81, (byte) 0x10, (byte) 0xFC, (byte) 0x81, (byte) 0x0E};
private final byte[] stopReq = {
(byte) 0x81, (byte) 0x10, (byte) 0xFC, (byte) 0x82, (byte) 0x0F};
public J2534ConnectionISO14230(ConnectionProperties connectionProperties, String library) {
checkNotNull(connectionProperties, "connectionProperties");
deviceId = -1;
commsStarted = false;
timeout = (long)connectionProperties.getConnectTimeout();
initJ2534(connectionProperties, library);
LOGGER.info("J2534/ISO14230 connection initialised");
}
// Send request and wait for response with known length
public void send(byte[] request, byte[] response, PollingState pollState) {
checkNotNull(request, "request");
checkNotNull(response, "response");
checkNotNull(pollState, "pollState");
if (pollState.getCurrentState() == PollingState.State.STATE_0 &&
pollState.getLastState() == PollingState.State.STATE_1) {
clearLine();
}
if (pollState.getCurrentState() == PollingState.State.STATE_0) {
api.writeMsg(channelId, request, timeout, TxFlags.NO_FLAGS);
}
api.readMsg(channelId, response, timeout);
if (pollState.getCurrentState() == PollingState.State.STATE_1){
if ( (response[0] + 2) == response.length
&& response[response.length - 1] == calculateChecksum(response)) {
lastResponse = new byte[response.length];
arraycopy(response, 0, lastResponse, 0, response.length);
}
else{
LOGGER.error(String.format(
"J2534/ISO14230 Bad Data response: %s", asHex(response)));
arraycopy(lastResponse, 0, response, 0, response.length);
pollState.setNewQuery(true);
}
}
}
// Send request and wait specified time for response with unknown length
public byte[] send(byte[] request) {
checkNotNull(request, "request");
api.writeMsg(channelId, request, timeout, TxFlags.NO_FLAGS);
return api.readMsg(channelId, 1, timeout);
}
public void clearLine() {
boolean repeat = true;
while (repeat) {
LOGGER.debug("J2534/ISO14230 sending line break");
int p3_min = getP3Min();
setP3Min(2);
api.writeMsg(
channelId,
stopReq,
0L,
TxFlags.WAIT_P3_MIN_ONLY);
setP3Min(p3_min);
api.clearBuffers(channelId);
boolean empty = false;
int i = 1;
do {
byte[] badBytes = api.readMsg(channelId, 700L);
if (badBytes.length > 0) {
LOGGER.debug(String.format(
"J2534/ISO14230 clearing line (stale data %d): %s", i, asHex(badBytes)));
empty = false;
i++;
}
else {
empty = true;
repeat = false;
}
} while (!empty && i <= 3);
}
try {
fastInit();
}
catch (J2534Exception e) {
// If fastInit fails because the ECU is no longer responding, for
// a variety of reasons, ignore it and close off the connection
// cleanly
LOGGER.error(String.format(
"J2534/ISO14230 Error performing fast initialization after clearing line: %s", e.getMessage()));
}
}
public void close() {
try {
if (commsStarted) stopComms();
commsStarted = false;
}
catch (J2534Exception e) {
// If the stop command fails because the ECU is no longer responding, for
// a variety of reasons, ignore it and close off the connection
// cleanly
LOGGER.error(String.format(
"J2534/ISO14230 Error stopping communications after clearing line: %s", e.getMessage()));
}
stopMsgFilter();
disconnectChannel();
closeDevice();
}
private void initJ2534(ConnectionProperties connectionProperties, String library) {
try {
api = new J2534Impl(Protocol.ISO14230, library);
deviceId = api.open();
try {
version(deviceId);
channelId = api.connect(
deviceId, Flag.ISO9141_NO_CHECKSUM.getValue(),
connectionProperties.getBaudRate());
setConfig(channelId, (KwpConnectionProperties) connectionProperties);
msgId = api.startPassMsgFilter(channelId, (byte) 0x00, (byte) 0x00);
LOGGER.debug(String.format(
"J2534/ISO14230 connection success: deviceId:%d, channelId:%d, msgId:%d, baud:%d",
deviceId, channelId, msgId, connectionProperties.getBaudRate()));
fastInit();
commsStarted = true;
} catch (Exception e) {
LOGGER.debug(String.format(
"J2534/ISO14230 exception: deviceId:%d, channelId:%d, msgId:%d",
deviceId, channelId, msgId));
close();
throw new J2534Exception(String.format(
"J2534/ISO14230 Error opening device: %s",e.getMessage()), e);
}
} catch (J2534Exception e) {
if (deviceId != -1) api.close(deviceId);
api = null;
throw new J2534Exception(e.getMessage(), e);
}
}
private void version(int deviceId) {
if (!LOGGER.isDebugEnabled()) return;
final Version version = api.readVersion(deviceId);
LOGGER.info(String.format(
"J2534 Version => firmware: %s, dll: %s, api: %s",
version.firmware, version.dll, version.api));
}
private void setConfig(int channelId, KwpConnectionProperties connectionProperties) {
final ConfigItem p1Max = new ConfigItem(Config.P1_MAX.getValue(),
(connectionProperties.getP1Max() * 2));
final ConfigItem p3Min = new ConfigItem(Config.P3_MIN.getValue(),
(connectionProperties.getP3Min() * 2));
final ConfigItem p4Min = new ConfigItem(Config.P4_MIN.getValue(),
(connectionProperties.getP4Min() * 2));
final ConfigItem loopback = new ConfigItem(Config.LOOPBACK.getValue(),
LOOPBACK);
final ConfigItem dataBits = new ConfigItem(
Config.DATA_BITS.getValue(),
(connectionProperties.getDataBits() == 8 ? 0 : 1));
final ConfigItem parity = new ConfigItem(
Config.PARITY.getValue(),
connectionProperties.getParity());
api.setConfig(channelId, dataBits, parity, p1Max, p3Min, p4Min, loopback);
LOGGER.debug(String.format("J2534/ISO14230 connection properties: %s",
connectionProperties.toString()));
}
private void stopMsgFilter() {
try {
api.stopMsgFilter(channelId, msgId);
LOGGER.debug(String.format(
"J2534/ISO14230 stopped message filter:%s", msgId));
} catch (Exception e) {
LOGGER.warn(String.format(
"J2534/ISO14230 Error stopping msg filter: %s", e.getMessage()));
}
}
private void disconnectChannel() {
try {
api.disconnect(channelId);
LOGGER.debug(String.format(
"J2534/ISO14230 disconnected channel:%d", channelId));
} catch (Exception e) {
LOGGER.warn(String.format(
"J2534/ISO14230 Error disconnecting channel: %s", e.getMessage()));
}
}
private void closeDevice() {
try {
api.close(deviceId);
LOGGER.info(String.format(
"J2534/ISO14230 closed connection to device:%d", deviceId));
} catch (Exception e) {
LOGGER.warn(String.format(
"J2534/ISO14230 Error closing device: %s", e.getMessage()));
}
}
private void fastInit() {
final byte[] timing = api.fastInit(channelId, startReq);
LOGGER.debug(String.format(
"J2534/ISO14230 Fast Init: deviceId:%d, channelId:%d, timing:%s",
deviceId, channelId, asHex(timing)));
}
private void stopComms() {
final byte[] response = send(stopReq);
LOGGER.debug(String.format("Stop comms Response = %s", asHex(response)));
}
private void setP3Min(int msec) {
final ConfigItem p3_min = new ConfigItem(
Config.P3_MIN.getValue(),
msec);
api.setConfig(channelId, p3_min);
LOGGER.trace(String.format("Config set P3_MIN value = %d msec", msec / 2));
}
private int getP3Min() {
final ConfigItem[] configs = api.getConfig(
channelId,
Config.P3_MIN.getValue());
int i = 10;
for (ConfigItem item : configs) {
if (Config.get(item.parameter) == Config.P3_MIN) {
i = item.value;
}
}
LOGGER.trace(String.format("Config get P3_MIN value = %d msec", i / 2));
return i;
}
//
// private void addressLoadReset() {
// final byte[] loadReset = {
// (byte) 0x02,
// (byte) 0xAC, (byte) 0x81
// (byte) 0x04, (byte) 0x21, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x2A
// };
// //04210004012A
// api.writeMsg(channelId, loadReset, 155L, TxFlags.NO_FLAGS);
// LOGGER.debug(String.format("loadReset Request = %s", asHex(loadReset)));
// final byte[] response = api.readMsg(channelId, 1, 2000L);
// LOGGER.debug(String.format("loadReset Response = %s", asHex(response)));
// }
}

View File

@ -0,0 +1,40 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.io.protocol;
import com.romraider.logger.ecu.comms.manager.PollingState;
import com.romraider.logger.ecu.definition.Module;
public interface ProtocolNCS extends Protocol {
byte[] constructEcuFastInitRequest(Module module);
byte[] constructReadSidPidRequest(Module module, byte sid, byte[][] pid);
byte[] constructLoadAddressRequest(byte[][] addresses);
void validateLoadAddressResponse(byte[] response);
void checkValidSidPidResponse(byte[] response);
byte[] constructReadAddressRequest(Module module, byte[][] bs,
PollingState pollState);
}

View File

@ -0,0 +1,39 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.io.protocol.ncs.iso14230;
import static com.romraider.util.ByteUtil.asByte;
import static com.romraider.util.ByteUtil.asInt;
public final class NCSChecksumCalculator {
private NCSChecksumCalculator() {
throw new UnsupportedOperationException();
}
public static byte calculateChecksum(byte[] bytes) {
int total = 0;
for (int i = 0; i < (bytes.length - 1); i++) {
byte b = bytes[i];
total += asInt(b);
}
return asByte(total - ((total >>> 8) << 8));
}
}

View File

@ -0,0 +1,210 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.io.protocol.ncs.iso14230;
import static com.romraider.io.protocol.ncs.iso14230.NCSResponseProcessor.extractResponseData;
import static com.romraider.io.protocol.ncs.iso14230.NCSResponseProcessor.filterRequestFromResponse;
import static com.romraider.util.ParamChecker.checkNotNull;
import static com.romraider.util.ParamChecker.checkNotNullOrEmpty;
import static java.lang.System.arraycopy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.romraider.io.protocol.Protocol;
import com.romraider.io.protocol.ProtocolNCS;
import com.romraider.logger.ecu.comms.io.protocol.LoggerProtocolNCS;
import com.romraider.logger.ecu.comms.manager.PollingState;
import com.romraider.logger.ecu.comms.query.EcuInit;
import com.romraider.logger.ecu.comms.query.EcuInitCallback;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.comms.query.EcuQueryData;
import com.romraider.logger.ecu.definition.Module;
public final class NCSLoggerProtocol implements LoggerProtocolNCS {
private final ProtocolNCS protocol = new NCSProtocol();
@Override
public byte[] constructEcuFastInitRequest(Module module) {
return protocol.constructEcuFastInitRequest(module);
}
@Override
public byte[] constructEcuInitRequest(Module module) {
return protocol.constructEcuInitRequest(module);
}
@Override
public byte[] constructEcuResetRequest(Module module, int resetCode) {
return protocol.constructEcuResetRequest(module, resetCode);
}
@Override
public byte[] constructReadAddressRequest(Module module,
Collection<EcuQuery> queries) {
return protocol.constructReadAddressRequest(
module, new byte[0][0]);
}
@Override
public byte[] constructReadAddressRequest(
Module module, Collection<EcuQuery> queries, PollingState pollState) {
return protocol.constructReadAddressRequest(
module, new byte[0][0], pollState);
}
@Override
public byte[] constructReadSidPidRequest(Module module, byte sid, byte[] pid) {
final byte[][] request = new byte[1][pid.length];
arraycopy(pid, 0, request[0], 0, pid.length);
return protocol.constructReadSidPidRequest(module, sid, request);
}
@Override
public byte[] constructLoadAddressRequest(Collection<EcuQuery> queries) {
Collection<EcuQuery> filteredQueries = filterDuplicates(queries);
return protocol.constructLoadAddressRequest(
convertToByteAddresses(filteredQueries));
}
@Override
public void validateLoadAddressResponse(byte[] response) {
protocol.validateLoadAddressResponse(response);
}
@Override
public byte[] constructReadAddressResponse(
Collection<EcuQuery> queries, PollingState pollState) {
checkNotNullOrEmpty(queries, "queries");
// length
// one byte - Response sid
// one byte - option
// variable bytes of data defined for pid
// checksum
Collection<EcuQuery> filteredQueries = filterDuplicates(queries);
int numAddresses = 0;
for (EcuQuery ecuQuery : filteredQueries) {
numAddresses += EcuQueryData.getDataLength(ecuQuery);
}
return new byte[(numAddresses + 4)];
}
@Override
public byte[] preprocessResponse(
byte[] request, byte[] response, PollingState pollState) {
return filterRequestFromResponse(request, response, pollState);
}
@Override
public void processEcuInitResponse(EcuInitCallback callback, byte[] response) {
checkNotNull(callback, "callback");
checkNotNullOrEmpty(response, "response");
EcuInit ecuInit = protocol.parseEcuInitResponse(response);
callback.callback(ecuInit);
}
@Override
public void processReadSidPidResponse(byte[] response) {
checkNotNullOrEmpty(response, "response");
protocol.checkValidSidPidResponse(response);
}
@Override
public void processEcuResetResponse(byte[] response) {
checkNotNullOrEmpty(response, "response");
protocol.checkValidEcuResetResponse(response);
}
// processes the response bytes and sets individual responses on corresponding query objects
@Override
public void processReadAddressResponses(
Collection<EcuQuery> queries, byte[] response, PollingState pollState) {
checkNotNullOrEmpty(queries, "queries");
checkNotNullOrEmpty(response, "response");
final byte[] responseData = extractResponseData(response);
final Collection<EcuQuery> filteredQueries = filterDuplicates(queries);
final Map<String, byte[]> addressResults = new HashMap<String, byte[]>();
int i = 0;
for (EcuQuery filteredQuery : filteredQueries) {
final int dataLength = EcuQueryData.getDataLength(filteredQuery);
final byte[] data = new byte[dataLength];
arraycopy(responseData, i, data, 0, dataLength);
addressResults.put(filteredQuery.getHex(), data);
i += dataLength;
}
for (EcuQuery query : queries) {
query.setResponse(addressResults.get(query.getHex()));
}
}
@Override
public Protocol getProtocol() {
return protocol;
}
@Override
public byte[] constructWriteAddressRequest(
Module module, byte[] writeAddress, byte value) {
return protocol.constructWriteAddressRequest(module, writeAddress, value);
}
@Override
public void processWriteResponse(byte[] data, byte[] response) {
checkNotNullOrEmpty(data, "data");
checkNotNullOrEmpty(response, "response");
protocol.checkValidWriteResponse(data, response);
}
private Collection<EcuQuery> filterDuplicates(Collection<EcuQuery> queries) {
Collection<EcuQuery> filteredQueries = new ArrayList<EcuQuery>();
for (EcuQuery query : queries) {
if (!filteredQueries.contains(query)) {
filteredQueries.add(query);
}
}
return filteredQueries;
}
private byte[][] convertToByteAddresses(Collection<EcuQuery> queries) {
int byteCount = 0;
for (EcuQuery query : queries) {
byteCount += query.getAddresses().length;
}
byte[][] addresses = new byte[byteCount][];
int i = 0;
for (EcuQuery query : queries) {
byte[] bytes = query.getBytes();
int addrCount = query.getAddresses().length;
int addrLen = bytes.length / addrCount;
for (int j = 0; j < addrCount; j++) {
final byte[] addr = new byte[addrLen];
arraycopy(bytes, j * addrLen, addr, 0, addr.length);
addresses[i++] = addr;
}
}
return addresses;
}
}

View File

@ -0,0 +1,405 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.io.protocol.ncs.iso14230;
import static com.romraider.io.protocol.ncs.iso14230.NCSChecksumCalculator.calculateChecksum;
import static com.romraider.util.HexUtil.asHex;
import static com.romraider.util.ParamChecker.checkNotNull;
import static com.romraider.util.ParamChecker.checkNotNullOrEmpty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.romraider.io.connection.ConnectionProperties;
import com.romraider.io.connection.KwpConnectionProperties;
import com.romraider.io.protocol.ProtocolNCS;
import com.romraider.logger.ecu.comms.manager.PollingState;
import com.romraider.logger.ecu.comms.query.EcuInit;
import com.romraider.logger.ecu.comms.query.NCSEcuInit;
import com.romraider.logger.ecu.definition.Module;
import com.romraider.logger.ecu.exception.InvalidResponseException;
import com.romraider.logger.ecu.exception.UnsupportedProtocolException;
public final class NCSProtocol implements ProtocolNCS {
private final ByteArrayOutputStream bb = new ByteArrayOutputStream(255);
public static final byte PHY_ADDR = (byte) 0x80;
public static final byte READ_MEMORY_PADDING = (byte) 0x00;
public static final byte READ_MEMORY_COMMAND = (byte) 0xA0;
public static final byte READ_MEMORY_RESPONSE = (byte) 0xE0;
public static final byte LOAD_ADDRESS_COMMAND = (byte) 0xAC;
public static final byte LOAD_ADDRESS_RESPONSE = (byte) 0xEC;
public static final byte READ_LOAD_COMMAND = (byte) 0x21;
public static final byte READ_LOAD_RESPONSE = (byte) 0x61;
public static final byte WRITE_MEMORY_COMMAND = (byte) 0xB0;
public static final byte WRITE_MEMORY_RESPONSE = (byte) 0xF0;
public static final byte WRITE_ADDRESS_COMMAND = (byte) 0xB8;
public static final byte WRITE_ADDRESS_RESPONSE = (byte) 0xF8;
public static final byte FASTINIT_COMMAND = (byte) 0x81;
public static final byte FASTINIT_RESPONSE = (byte) 0xC1;
public static final byte ECU_ID_SID = (byte) 0x1A;
public static final byte OPTION_81 = (byte) 0x81;
public static final byte FIELD_TYPE_01 = (byte) 0x01;
public static final byte FIELD_TYPE_02 = (byte) 0x02;
public static final byte FIELD_TYPE_83 = (byte) 0x83;
public static final byte SID_21 = (byte) 0x21;
public static final byte SID_22 = (byte) 0x22;
public static final byte ECU_ID_SID_RESPONSE = (byte) 0x5A;
public static final byte READ_SID_GRP_RESPONSE = (byte) 0x62;
public static final byte ECU_RESET_COMMAND = (byte) 0x04;
public static final byte ECU_RESET_RESPONSE = (byte) 0x44;
public static final byte NCS_NRC = (byte) 0x7F;
public static final int RESPONSE_NON_DATA_BYTES = 3;
public static final int ADDRESS_SIZE = 3;
public static Module module;
public byte[] constructEcuFastInitRequest(Module module) {
checkNotNull(module, "module");
NCSProtocol.module = module;
final byte[] request = buildRequest(
FASTINIT_COMMAND, false, new byte[]{});
return request;
}
@Override
public byte[] constructEcuInitRequest(Module module) {
checkNotNull(module, "module");
NCSProtocol.module = module;
// len SID opt chk
// 0x02 0x1A 0x81 0x9D
final byte[] request = buildRequest(
ECU_ID_SID, true, new byte[]{OPTION_81});
return request;
}
@Override
public byte[] constructReadSidPidRequest(Module module, byte sid, byte[][] pid) {
checkNotNull(module, "module");
NCSProtocol.module = module;
final byte[] request = buildSidPidRequest(sid, true, pid);
return request;
}
@Override
//TODO: not yet implemented
public byte[] constructWriteMemoryRequest(
Module module, byte[] address, byte[] values) {
throw new UnsupportedProtocolException(
"Write memory command is not supported on for address: " +
asHex(address));
}
@Override
//TODO: not yet implemented
public byte[] constructWriteAddressRequest(
Module module, byte[] address, byte value) {
throw new UnsupportedProtocolException(
"Write Address command is not supported on for address: " +
asHex(address));
}
@Override
//TODO: not yet implemented
public byte[] constructReadMemoryRequest(
Module module, byte[] address, int numBytes) {
throw new UnsupportedProtocolException(
"Read memory command is not supported on for address: " +
asHex(address));
}
@Override
public byte[] constructLoadAddressRequest(byte[][] addresses) {
checkNotNullOrEmpty(addresses, "addresses");
// len 0xac 0x81 fld_typ address1 [[fld_typ address2] ... [fld_typ addressN]] checksum
return buildLoadAddrRequest(true, addresses);
}
@Override
public byte[] constructReadAddressRequest(Module module, byte[][] addresses) {
// read previously loaded addresses
// len 0x21 0x81 0x04 0x01 checksum
return buildRequest(
READ_LOAD_COMMAND, true, new byte[]{(byte) 0x81, (byte) 0x04, (byte) 0x01});
}
@Override
public byte[] constructReadAddressRequest(Module module, byte[][] bs,
PollingState pollState) {
byte opt_byte3;
if (pollState.isFastPoll()) {
// continuously read response of previously loaded addresses
// len 0x21 0x81 0x06 0x01 checksum
opt_byte3 = (byte) 0x06;
}
else {
// read one response of previously loaded addresses
// len 0x21 0x81 0x04 0x01 checksum
opt_byte3 = (byte) 0x04;
}
return buildRequest(
READ_LOAD_COMMAND, true, new byte[]{(byte) 0x81, opt_byte3, (byte) 0x01});
}
@Override
public byte[] preprocessResponse(
byte[] request, byte[] response, PollingState pollState) {
return NCSResponseProcessor.filterRequestFromResponse(
request, response, pollState);
}
@Override
public byte[] parseResponseData(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
return NCSResponseProcessor.extractResponseData(processedResponse);
}
@Override
public void checkValidEcuInitResponse(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
NCSResponseProcessor.validateResponse(processedResponse);
}
@Override
public EcuInit parseEcuInitResponse(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
//final byte[] ecuInitBytes = parseResponseData(processedResponse);
return new NCSEcuInit(processedResponse);
}
@Override
public void validateLoadAddressResponse(byte[] response) {
checkNotNullOrEmpty(response, "addressLoadResponse");
NCSResponseProcessor.validateResponse(response);
}
@Override
//TODO: not yet implemented
public byte[] constructEcuResetRequest(Module module, int resetCode) {
checkNotNull(module, "module");
NCSProtocol.module = module;
return buildRequest((byte) 0, false, new byte[]{ECU_RESET_COMMAND});
}
@Override
public void checkValidSidPidResponse(byte[] response) {
checkNotNullOrEmpty(response, "SidPidResponse");
NCSResponseProcessor.validateResponse(response);
}
@Override
//TODO: not yet implemented
public void checkValidEcuResetResponse(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
byte responseType = processedResponse[4];
if (responseType != ECU_RESET_RESPONSE) {
throw new InvalidResponseException(
"Unexpected OBD Reset response: " +
asHex(processedResponse));
}
}
@Override
public void checkValidWriteResponse(byte[] data, byte[] processedResponse) {
}
@Override
public ConnectionProperties getDefaultConnectionProperties() {
return new KwpConnectionProperties() {
public int getBaudRate() {
return 10400;
}
public void setBaudRate(int b) {
}
public int getDataBits() {
return 8;
}
public int getStopBits() {
return 1;
}
public int getParity() {
return 0;
}
public int getConnectTimeout() {
return 2000;
}
public int getSendTimeout() {
return 255;
}
public int getP1Max() {
return 0;
}
public int getP3Min() {
return 5;
}
public int getP4Min() {
return 0;
}
};
}
private final byte[] buildRequest(byte command, boolean shortHeader,
byte[]... content) {
int length = 1;
for (byte[] tmp : content) {
length += tmp.length;
}
byte[] request = new byte[0];
try {
bb.reset();
if (shortHeader) {
bb.write(length);
}
else {
bb.write(PHY_ADDR + length);
bb.write(module.getAddress());
bb.write(module.getTester());
}
bb.write(command);
for (byte[] tmp : content) {
bb.write(tmp);
}
bb.write((byte) 0x00);
request = bb.toByteArray();
final byte cs = calculateChecksum(request);
request[request.length - 1] = cs;
}
catch (IOException e) {
e.printStackTrace();
}
return request;
}
private final byte[] buildSidPidRequest(byte command, boolean shortHeader,
byte[]... content) {
int length = 3;
for (byte[] tmp : content) {
length += tmp.length;
}
byte[] request = new byte[0];
try {
bb.reset();
if (shortHeader) {
bb.write(length);
}
else {
bb.write(PHY_ADDR + length);
bb.write(module.getAddress());
bb.write(module.getTester());
}
bb.write(command);
for (byte[] tmp : content) {
bb.write(tmp);
}
bb.write((byte) 0x04);
bb.write((byte) 0x01);
bb.write((byte) 0x00);
request = bb.toByteArray();
final byte cs = calculateChecksum(request);
request[request.length - 1] = cs;
}
catch (IOException e) {
e.printStackTrace();
}
return request;
}
private final byte[] buildLoadAddrRequest(boolean shortHeader,
byte[]... content) {
int length = 2;
byte[] request = new byte[0];
try {
bb.reset();
if (shortHeader) {
bb.write(length);
}
else {
bb.write(PHY_ADDR);
bb.write(module.getAddress());
bb.write(module.getTester());
}
bb.write(LOAD_ADDRESS_COMMAND);
bb.write(OPTION_81);
for (byte[] tmp : content) {
if (tmp[0] == SID_21) {
bb.write(FIELD_TYPE_01);
bb.write(tmp, 1, tmp.length - 1);
continue;
}
if (tmp[0] == SID_22) {
bb.write(FIELD_TYPE_02);
bb.write(tmp, 1, tmp.length - 1);
continue;
}
if (tmp[0] == (byte) 0xFF) {
bb.write(FIELD_TYPE_83);
bb.write((byte) 0xFF);
bb.write(tmp);
}
else { //assume a short length ROM address
bb.write(FIELD_TYPE_83);
bb.write((byte) 0x00);
switch (tmp.length) {
case 1:
bb.write((byte) 0x00);
bb.write((byte) 0x00);
break;
case 2:
bb.write((byte) 0x00);
break;
case 3:
break;
}
bb.write(tmp);
}
}
bb.write(0); // reserve last byte for checksum
request = bb.toByteArray();
if (shortHeader) {
request[0] = (byte)(request.length - 2);
}
else {
request[0] = (byte)(PHY_ADDR + request.length - 4);
}
final byte cs = calculateChecksum(request);
request[request.length - 1] = cs;
}
catch (IOException e) {
e.printStackTrace();
}
return request;
}
}

View File

@ -0,0 +1,134 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.io.protocol.ncs.iso14230;
import static com.romraider.io.protocol.ncs.iso14230.NCSChecksumCalculator.calculateChecksum;
import static com.romraider.io.protocol.ncs.iso14230.NCSProtocol.LOAD_ADDRESS_RESPONSE;
import static com.romraider.io.protocol.ncs.iso14230.NCSProtocol.ECU_ID_SID_RESPONSE;
import static com.romraider.io.protocol.ncs.iso14230.NCSProtocol.NCS_NRC;
import static com.romraider.io.protocol.ncs.iso14230.NCSProtocol.READ_LOAD_RESPONSE;
import static com.romraider.io.protocol.ncs.iso14230.NCSProtocol.RESPONSE_NON_DATA_BYTES;
import static com.romraider.io.protocol.ncs.iso14230.NCSProtocol.READ_SID_GRP_RESPONSE;
import static com.romraider.util.ByteUtil.asUnsignedInt;
import static com.romraider.util.HexUtil.asHex;
import static com.romraider.util.ParamChecker.checkNotNullOrEmpty;
import java.util.Arrays;
import com.romraider.logger.ecu.comms.manager.PollingState;
import com.romraider.logger.ecu.exception.InvalidResponseException;
public final class NCSResponseProcessor {
private NCSResponseProcessor() {
throw new UnsupportedOperationException();
}
public final static byte[] filterRequestFromResponse(
byte[] request, byte[] response, PollingState pollState) {
checkNotNullOrEmpty(response, "response");
// If J2534 device Loopback is off, the request is filtered out by J2534 device
// and only the response is present
return response;
}
public final static void validateResponse(byte[] response) {
checkNotNullOrEmpty(response, "response");
assertTrue(response.length > RESPONSE_NON_DATA_BYTES,
"Invalid response length");
validateChecksum(response);
if (response[1] == NCS_NRC) {
assertNrc((byte) (response[2] + 0x40), response[1], response[2], response[3],
"Request type not supported");
}
assertOneOf(new byte[]{ECU_ID_SID_RESPONSE, LOAD_ADDRESS_RESPONSE,
READ_LOAD_RESPONSE, READ_SID_GRP_RESPONSE},
response[1], "Invalid response code");
}
public final static byte[] extractResponseData(byte[] response) {
checkNotNullOrEmpty(response, "response");
// len response_sid option response_data1 ... [response_dataN]
validateResponse(response);
final byte[] data = new byte[response.length - 4];
System.arraycopy(response, RESPONSE_NON_DATA_BYTES, data, 0, data.length);
return data;
}
private final static void validateChecksum(byte[] response) {
final byte calc_chk = calculateChecksum(response);
final byte pkt_cs = response[response.length - 1];
assertTrue(calc_chk == pkt_cs, String.format(
"Response checksum match failure. Expected: %s, Actual: %s",
asHex(calc_chk), asHex(pkt_cs)));
}
private final static void assertTrue(boolean condition, String msg) {
if (!condition) {
throw new InvalidResponseException(msg);
}
}
private final static void assertNrc(
byte expected, byte actual, byte command, byte code, String msg) {
if (actual == expected) {
String ec = "unsupported.";
if (code == 0x10) {
ec = "general reject no specific reason.";
}
if (code == 0x12) {
ec = "request sub-function is not supported or invalid format.";
}
if (code == 0x13) {
ec = "invalid format or length.";
}
if (code == 0x21) {
ec = "busy, repeat request.";
}
if (code == 0x22) {
ec = "conditions not correct or request sequence error.";
}
throw new InvalidResponseException(String.format(
"%s. Command: %s, %s",
msg, asHex(command), ec));
}
}
private final static void assertOneOf(
byte[] validOptions, byte actual, String msg) {
for (byte option : validOptions) {
if (option == actual) {
return;
}
}
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < validOptions.length; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(asHex(validOptions[i]));
}
throw new InvalidResponseException(String.format(
"%s. Expected one of [%s]. Actual: %s.",
msg, builder.toString(), asHex(actual)));
}
}

View File

@ -0,0 +1,262 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.comms.io.connection;
import static com.romraider.util.HexUtil.asHex;
import static com.romraider.util.ParamChecker.checkNotNull;
import static com.romraider.util.ThreadUtil.sleep;
import static org.apache.log4j.Logger.getLogger;
import java.util.Collection;
import java.util.Map;
import org.apache.log4j.Logger;
import com.romraider.Settings;
import com.romraider.util.SettingsManager;
import com.romraider.io.connection.ConnectionManager;
import com.romraider.io.protocol.ProtocolFactory;
import com.romraider.logger.ecu.comms.io.protocol.LoggerProtocolNCS;
import com.romraider.logger.ecu.comms.manager.PollingState;
import com.romraider.logger.ecu.comms.manager.PollingStateImpl;
import com.romraider.logger.ecu.comms.query.EcuInitCallback;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.definition.Module;
import com.romraider.logger.ecu.exception.SerialCommunicationException;
public final class NCSLoggerConnection implements LoggerConnection {
private static final Logger LOGGER = getLogger(NCSLoggerConnection.class);
private final LoggerProtocolNCS protocol;
private final ConnectionManager manager;
private int queryCount;
public NCSLoggerConnection(ConnectionManager manager) {
checkNotNull(manager, "manager");
this.manager = manager;
final Settings settings = SettingsManager.getSettings();
this.protocol = (LoggerProtocolNCS) ProtocolFactory.getProtocol(
settings.getLoggerProtocol(),
settings.getTransportProtocol());
}
@Override
//TODO: not yet implemented
public void ecuReset(Module module, int resetCode) {
byte[] request = protocol.constructEcuResetRequest(module, resetCode);
LOGGER.debug(String.format("%s Reset Request ---> %s",
module, asHex(request)));
byte[] response = manager.send(request);
byte[] processedResponse = protocol.preprocessResponse(
request, response, new PollingStateImpl());
LOGGER.debug(String.format("%s Reset Response <--- %s",
module, asHex(processedResponse)));
protocol.processEcuResetResponse(processedResponse);
}
@Override
// Build an init string similar to the SSM version so the logger definition
// can reference supported parameters with ecubyte/bit attributes.
public void ecuInit(EcuInitCallback callback, Module module) {
final byte[] initResponse = new byte[422];
byte[] request = protocol.constructEcuInitRequest(module);
LOGGER.debug(String.format("%s ID Request ---> %s",
module, asHex(request)));
byte[] response = new byte[9];
manager.send(request, response, new PollingStateImpl());
LOGGER.debug(String.format("%s ID Response <--- %s",
module, asHex(response)));
System.arraycopy(response, 0, initResponse, 0, response.length - 1);
sleep(55L);
final byte[] supportedPidsPid = {
(byte) 0x00, (byte) 0x20, (byte) 0x40, (byte) 0x60,
(byte) 0x80, (byte) 0xA0, (byte) 0xC0, (byte) 0xE0};
int i = 8;
byte sid = (byte) 0x21;
boolean test_grp = true;
for (byte pid : supportedPidsPid) {
if (test_grp) {
request = protocol.constructReadSidPidRequest(
module, sid, new byte[]{pid});
LOGGER.debug(String.format("%s SID %02X, PID Group %02X Request ---> %s",
module, sid, pid, asHex(request)));
response = new byte[8];
manager.send(request, response, new PollingStateImpl());
LOGGER.debug(String.format("%s SID %02X, PID Group %02X Response <--- %s",
module, sid ,pid, asHex(response)));
// Validate response
protocol.processReadSidPidResponse(response);
System.arraycopy(response, 3, initResponse, i, 4);
// Check lsb to see if next PID group is supported
if ((response[response[0]] & 0x01) == 0) {
test_grp = false;
}
}
i = i + 4;
}
sid = (byte) 0x22;
final byte[] highBytes = {
(byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14,
(byte) 0x15};
for (byte hb : highBytes) {
if (hb == (byte) 0x13) {
test_grp = true;
for (byte pid : supportedPidsPid) {
if (test_grp) {
request = protocol.constructReadSidPidRequest(
module, sid, new byte[]{hb, pid});
LOGGER.debug(String.format("%s SID %02X, PID Group %02X%02X Request ---> %s",
module, sid, hb, pid, asHex(request)));
response = new byte[9];
manager.send(request, response, new PollingStateImpl());
LOGGER.debug(String.format("%s SID %02X, PID Group %02X%02X Response <--- %s",
module, sid ,hb, pid, asHex(response)));
// Validate response
protocol.processReadSidPidResponse(response);
// Check lsb to see if next PID group is supported
if ((response[response[0]] & 0x01) == 0) {
test_grp = false;
}
final short[] supported = new short[2];
for (int j = 0; j < 2; j++) {
supported[j] = (short) ((short)(response[j*2+4] << 8) + ((short)response[j*2+5] & 0x00FF));
}
for (int k = 0; k < supported.length; k++) {
// ex: 7FFC2000
for (int shift = 15; shift > -1; shift--) {
if (((1 << shift) & supported[k]) > 0) {
byte cid = (byte) ((16 - shift) + (k * 16));
request = protocol.constructReadSidPidRequest(
module, sid, new byte[]{hb, cid});
LOGGER.debug(String.format("%s SID %02X, PID %02X%02X Request ---> %s",
module, sid, hb, cid, asHex(request)));
response = new byte[7];
manager.send(request, response, new PollingStateImpl());
LOGGER.debug(String.format("%s SID %02X, PID %02X%02X Response <--- %s",
module, sid ,hb, cid, asHex(response)));
// Validate response
protocol.processReadSidPidResponse(response);
System.arraycopy(response, 5, initResponse, i, 1);
}
i++;
}
}
}
else {
i = i + 32;
}
}
i--; // move back one unused index byte position
}
else {
test_grp = true;
for (byte pid : supportedPidsPid) {
if (test_grp) {
request = protocol.constructReadSidPidRequest(
module, sid, new byte[]{hb, pid});
LOGGER.debug(String.format("%s SID %02X, PID Group %02X%02X Request ---> %s",
module, sid, hb, pid, asHex(request)));
response = new byte[9];
manager.send(request, response, new PollingStateImpl());
LOGGER.debug(String.format("%s SID %02X, PID Group %02X%02X Response <--- %s",
module, sid ,hb, pid, asHex(response)));
// Validate response
protocol.processReadSidPidResponse(response);
System.arraycopy(response, 4, initResponse, i, 4);
// Check lsb to see if next PID group is supported
if ((response[response[0]] & 0x01) == 0) {
test_grp = false;
}
}
i = i + 4;
}
}
}
LOGGER.debug(String.format("%s Init Response <--- %s",
module, asHex(initResponse))); // contains ECUID
protocol.processEcuInitResponse(callback, initResponse);
}
@Override
public final void sendAddressReads(
Collection<EcuQuery> queries,
Module module,
PollingState pollState) {
if (queries.size() != queryCount
|| pollState.isNewQuery()) {
// max data bytes 255 including TX loopback
int dataLength = 0;
for (EcuQuery query : queries) {
dataLength += query.getBytes().length;
}
// if length is too big then notify user to un-select some parameters
if (dataLength > 60) {
throw new SerialCommunicationException(
"Request message too large, un-select some parameters");
}
final byte[] request = protocol.constructLoadAddressRequest(queries);
LOGGER.debug(String.format("Mode:%s %s Load address request ---> %s",
pollState.getCurrentState(), module, asHex(request)));
final byte[] response = new byte[4];
manager.send(request, response, pollState);
LOGGER.debug(String.format("Mode:%s %s Load address response <--- %s",
pollState.getCurrentState(), module, asHex(response)));
protocol.validateLoadAddressResponse(response);
queryCount = queries.size();
}
final byte[] request = protocol.constructReadAddressRequest(
module, queries, pollState);
if (pollState.getCurrentState() == PollingState.State.STATE_0) {
LOGGER.debug(String.format("Mode:%s %s Read request ---> %s",
pollState.getCurrentState(), module, asHex(request)));
pollState.setLastState(PollingState.State.STATE_0);
}
final byte[] response = protocol.constructReadAddressResponse(
queries, pollState);
manager.send(request, response, pollState);
LOGGER.debug(String.format("Mode:%s %s Read response <--- %s",
pollState.getCurrentState(), module, asHex(response)));
protocol.processReadAddressResponses(
queries, response, pollState);
}
public void clearQueryCount() {
queryCount = -1;
}
@Override
public void clearLine() {
clearQueryCount();
manager.clearLine();
}
@Override
public void close() {
clearQueryCount();
manager.close();
}
@Override
public void sendAddressWrites(Map<EcuQuery, byte[]> writeQueries, Module module) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,43 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.comms.io.protocol;
import java.util.Collection;
import com.romraider.logger.ecu.comms.manager.PollingState;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.definition.Module;
public interface LoggerProtocolNCS extends LoggerProtocol {
byte[] constructEcuFastInitRequest(Module module);
byte[] constructReadSidPidRequest(Module module, byte sid, byte[] pid);
byte[] constructLoadAddressRequest(Collection<EcuQuery> queries);
void validateLoadAddressResponse(byte[] response);
void processReadSidPidResponse(byte[] response);
byte[] constructReadAddressRequest(Module module,
Collection<EcuQuery> queries, PollingState pollState);
}

View File

@ -0,0 +1,620 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.comms.learning;
import static com.romraider.logger.ecu.comms.io.connection.LoggerConnectionFactory.getConnection;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.WARNING_MESSAGE;
import static javax.swing.JOptionPane.showMessageDialog;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.swing.SwingWorker;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.romraider.Settings;
import com.romraider.logger.ecu.EcuLogger;
import com.romraider.logger.ecu.comms.io.connection.LoggerConnection;
import com.romraider.logger.ecu.comms.learning.parameter.NCSParameter;
import com.romraider.logger.ecu.comms.learning.parameter.NCSParameterCrossReference;
import com.romraider.logger.ecu.comms.learning.parameter.ParameterIdComparator;
import com.romraider.logger.ecu.comms.learning.tableaxis.NCSTableAxisQueryParameterSet;
import com.romraider.logger.ecu.comms.learning.tables.NCSLtftTableQueryBuilder;
import com.romraider.logger.ecu.comms.manager.PollingStateImpl;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.comms.query.EcuQueryImpl;
import com.romraider.logger.ecu.definition.EcuData;
import com.romraider.logger.ecu.definition.EcuDefinition;
import com.romraider.logger.ecu.definition.Module;
import com.romraider.logger.ecu.definition.Transport;
import com.romraider.logger.ecu.definition.xml.EcuDefinitionDocumentLoader;
import com.romraider.logger.ecu.definition.xml.EcuDefinitionInheritanceList;
import com.romraider.logger.ecu.definition.xml.EcuTableDefinitionHandler;
import com.romraider.logger.ecu.ui.MessageListener;
import com.romraider.logger.ecu.ui.paramlist.ParameterListTableModel;
import com.romraider.logger.ecu.ui.paramlist.ParameterRow;
import com.romraider.logger.ecu.ui.swing.tools.NCSLearningTableValuesResultsPanel;
import com.romraider.util.ParamChecker;
/**
* This class manages the building of ECU queries and retrieving the data to
* populate the table models which will be used by the Learning Table Values
* display panel.
*/
public final class NCSLearningTableValues extends SwingWorker<Void, Void>
implements LearningTableValues {
private static final Logger LOGGER =
Logger.getLogger(NCSLearningTableValues.class);
private static final List<String> AF_TABLE_NAMES = Arrays.asList(
"A/F Learning #1 Airflow Ranges",
"A/F Learning #1 Airflow Ranges ",
"A/F Learning Airflow Ranges");
// LTFT table column and row names can be overridden in the
// ./customize/ncslearning.properties file
private static List<String> LTFT_TABLE_COLUMN_NAMES = Arrays.asList(
"Learning map TP shaft lattice point table");
private static List<String> LTFT_TABLE_ROW_NAMES = Arrays.asList(
"Learning map N shaft lattice point table");
private final Map<String, Object> vehicleInfo =
new LinkedHashMap<String, Object>();
private final List<List<Object>> afLearning = new ArrayList<List<Object>>();
private EcuLogger logger;
private Settings settings;
private MessageListener messageListener;
private ParameterListTableModel parmeterList;
private EcuDefinition ecuDef;
private ParameterRow ltftTrim;
private int ltftTrimAddr;
private ParameterRow ltftCnt;
private int ltftCntAddr;
public NCSLearningTableValues() {}
public void init(
EcuLogger logger,
ParameterListTableModel dataTabParamListTableModel,
EcuDefinition ecuDef) {
ParamChecker.checkNotNull(logger, "EcuLogger");
ParamChecker.checkNotNull(dataTabParamListTableModel,
"ParameterListTableModel");
this.logger = logger;
this.settings = logger.getSettings();
this.messageListener = logger;
this.parmeterList = dataTabParamListTableModel;
this.ecuDef = ecuDef;
this.ltftTrim = null;
this.ltftTrimAddr = 0;
this.ltftCnt = null;
this.ltftCntAddr = 0;
loadProperties();
}
@Override
public final Void doInBackground() {
Document document = null;
if (ecuDef.getEcuDefFile() == null) {
showMessageDialog(logger,
"ECU definition file not found or undefined. Learning\n" +
"Table Values cannot be properly retrieved until an ECU\n" +
"defintion is defined in the Editor's Definition Manager.",
"ECU Defintion Missing", WARNING_MESSAGE);
return null;
}
else {
document = EcuDefinitionDocumentLoader.getDocument(ecuDef);
}
final String transport = settings.getTransportProtocol();
final Module module = settings.getDestinationTarget();
if (settings.isCanBus()) {
settings.setTransportProtocol("ISO14230");
final Module ecuModule = getModule("ECU");
settings.setDestinationTarget(ecuModule);
}
final boolean logging = logger.isLogging();
if (logging) logger.stopLogging();
String message = "Retrieving vehicle info & LTFT values...";
messageListener.reportMessage(message);
buildVehicleInfoMap(ecuDef);
try {
LoggerConnection connection = getConnection(
settings.getLoggerProtocol(), settings.getLoggerPort(),
settings.getLoggerConnectionProperties());
try {
Collection<EcuQuery> queries = buildLearningQueries();
// Break queries into two sets to avoid the ECU packet limit
int setSize = queries.size() / 2;
Collection<EcuQuery> querySet1 = new ArrayList<EcuQuery>();
Collection<EcuQuery> querySet2 = new ArrayList<EcuQuery>();
int s = 0;
for (EcuQuery q : queries) {
if (s < setSize) {
querySet1.add(q);
}
else {
querySet2.add(q);
}
s++;
}
LOGGER.trace(
String.format(
"Queries:%d, Set size:%d, Set 1 size:%d, Set 2 size:%d",
queries.size(), setSize, querySet1.size(), querySet2.size()));
LOGGER.info(message);
connection.sendAddressReads(
querySet1,
settings.getDestinationTarget(),
new PollingStateImpl());
connection.sendAddressReads(
querySet2,
settings.getDestinationTarget(),
new PollingStateImpl());
LOGGER.info("Current vehicle info & LTFT values retrieved.");
Collections.sort(
(List<EcuQuery>)queries, new ParameterIdComparator());
processEcuQueryResponses((List<EcuQuery>) queries);
//TODO: afRanges to be replaced with Knock tables once known how they operate
String[] afRanges = new String[0];
message = "Retrieving LTFT column ranges...";
messageListener.reportMessage(message);
String[] ltftCol = new String[0];
queries.clear();
queries = getTableAxisRanges(document, ecuDef, LTFT_TABLE_COLUMN_NAMES);
if (queries != null && !queries.isEmpty()) {
LOGGER.info(message);
connection.sendAddressReads(
queries,
settings.getDestinationTarget(),
new PollingStateImpl());
LOGGER.info("LTFT column ranges retrieved.");
ltftCol = formatRanges(queries, "%.2f");
}
message = "Retrieving LTFT row ranges...";
messageListener.reportMessage(message);
String[] ltftRow = new String[0];
queries.clear();
queries = getTableAxisRanges(document, ecuDef, LTFT_TABLE_ROW_NAMES);
if (queries != null && !queries.isEmpty()) {
LOGGER.info(message);
connection.sendAddressReads(
queries,
settings.getDestinationTarget(),
new PollingStateImpl());
LOGGER.info("LTFT row ranges retrieved.");
ltftRow = formatRpmRanges(queries);
}
List<List<List<EcuQuery>>> ltftQueryTables = new ArrayList<List<List<EcuQuery>>>();
List<List<EcuQuery>> ltftQueryGroups = new ArrayList<List<EcuQuery>>();
if (ltftTrim != null) {
for (int k = 0; k < 256; k += 128) {
ltftQueryGroups = new NCSLtftTableQueryBuilder().build(
ltftTrim,
ltftTrimAddr + k,
ltftRow.length,
ltftCol.length - 1);
ltftQueryTables.add(ltftQueryGroups);
for (int i = 0; i < ltftQueryGroups.size(); i++) {
queries.clear();
for (int j = 0; j < ltftQueryGroups.get(i).size(); j++) {
if (ltftQueryGroups.get(i).get(j) != null) {
queries.add(ltftQueryGroups.get(i).get(j));
}
}
message = String.format(
"Retrieving Table %d LTFT row %d values...",
(k/128+1), i);
messageListener.reportMessage(message);
LOGGER.info(message);
setSize = queries.size() / 2;
querySet1 = new ArrayList<EcuQuery>();
querySet2 = new ArrayList<EcuQuery>();
s = 0;
for (EcuQuery q : queries) {
if (s < setSize) {
querySet1.add(q);
}
else {
querySet2.add(q);
}
s++;
}
LOGGER.trace(
String.format(
"Queries:%d, Set size:%d, Set 1 size:%d, Set 2 size:%d",
queries.size(), setSize, querySet1.size(), querySet2.size()));
LOGGER.info(message);
connection.sendAddressReads(
querySet1,
settings.getDestinationTarget(),
new PollingStateImpl());
connection.sendAddressReads(
querySet2,
settings.getDestinationTarget(),
new PollingStateImpl());
LOGGER.info(String.format(
"Table %d row %d LTFT values retrieved.",
(k/128+1), i));
queries.clear();
}
}
}
else {
message = String.format(
"Error retrieving LTFT data values, missing LTFT reference");
messageListener.reportMessage(message);
LOGGER.error(message);
}
if (ltftCnt != null) {
for (int k = 0; k < 128; k += 64) {
ltftQueryGroups = new NCSLtftTableQueryBuilder().build(
ltftCnt,
ltftCntAddr + k,
ltftRow.length,
ltftCol.length - 1);
ltftQueryTables.add(ltftQueryGroups);
for (int i = 0; i < ltftQueryGroups.size(); i++) {
queries.clear();
for (int j = 0; j < ltftQueryGroups.get(i).size(); j++) {
if (ltftQueryGroups.get(i).get(j) != null) {
queries.add(ltftQueryGroups.get(i).get(j));
}
}
message = String.format("Retrieving Table %d LTFT row %d values...",
(k/64+3), i);
messageListener.reportMessage(message);
LOGGER.info(message);
setSize = queries.size() / 2;
querySet1 = new ArrayList<EcuQuery>();
querySet2 = new ArrayList<EcuQuery>();
s = 0;
for (EcuQuery q : queries) {
if (s < setSize) {
querySet1.add(q);
}
else {
querySet2.add(q);
}
s++;
}
LOGGER.trace(
String.format("Queries:%d, Set size:%d, Set 1 size:%d, Set 2 size:%d",
queries.size(), setSize, querySet1.size(), querySet2.size()));
LOGGER.info(message);
connection.sendAddressReads(
querySet1,
settings.getDestinationTarget(),
new PollingStateImpl());
connection.sendAddressReads(
querySet2,
settings.getDestinationTarget(),
new PollingStateImpl());
LOGGER.info(String.format("Table %d row %d LTFT values retrieved.",
(k/64+3), i));
queries.clear();
}
}
}
else {
message = String.format("Error retrieving LTFT data values, missing LTFT reference");
messageListener.reportMessage(message);
LOGGER.error(message);
}
messageListener.reportMessage(
"Learning Table Values retrieved successfully.");
final NCSLearningTableValuesResultsPanel results =
new NCSLearningTableValuesResultsPanel(
logger, vehicleInfo,
afRanges, afLearning,
ltftCol, ltftRow, ltftQueryTables);
results.displayLearningResultsPanel();
}
finally {
connection.close();
settings.setTransportProtocol(transport);
settings.setDestinationTarget(module);
if (logging) logger.startLogging();
}
}
catch (Exception e) {
messageListener.reportError(
"Unable to retrieve current ECU learning values");
LOGGER.error(message + " Error retrieving values", e);
showMessageDialog(logger,
message +
"\nError performing Learning Table Values read.\n" +
"Check the following:\n" +
"* Logger has successfully conencted to the ECU\n" +
"* Correct COM port is selected (if not Openport 2)\n" +
"* Cable is connected properly\n* Ignition is ON\n",
"Learning Table Values",
ERROR_MESSAGE);
}
return null;
}
/**
* Build a collection of queries based on the initialized values of
* parameters defined for this ECU. Also identify the LTFT Trim and Count
* parameters used to locate the LTFT tables.
* @return the supported parameter list filtered for only the Learning Table
* Value parameters needed.
*/
private final Collection<EcuQuery> buildLearningQueries() {
final Collection<EcuQuery> query = new ArrayList<EcuQuery>();
final List<ParameterRow> parameterRows = parmeterList.getParameterRows();
if (!ParamChecker.isNullOrEmpty(parameterRows)) {
for (ParameterRow parameterRow : parameterRows) {
final NCSParameter parameterId =
NCSParameter.fromValue(parameterRow.getLoggerData().getId());
if (parameterId != null) {
switch (parameterId) {
case E173:
ltftTrimAddr = getParameterAddr(parameterRow);
ltftTrim = parameterRow;
break;
case E174:
ltftCntAddr = getParameterAddr(parameterRow);
ltftCnt = parameterRow;
break;
default:
query.add(buildEcuQuery(parameterRow));
break;
}
}
}
}
return query;
}
/**
* Build a query object for a parameter item.
* @return a new EcuQuery.
*/
private final EcuQuery buildEcuQuery(ParameterRow parameterRow) {
final EcuQuery ecuQuery =
new EcuQueryImpl((EcuData) parameterRow.getLoggerData());
return ecuQuery;
}
/**
* Return the parameter's integer address.
*/
private final int getParameterAddr(ParameterRow parameterRow) {
final EcuData ecudata = (EcuData) parameterRow.getLoggerData();
final String addrStr = ecudata.getAddress().getAddresses()[0];
final String addrHexStr = addrStr.replaceAll("0x", "");
return Integer.parseInt(addrHexStr, 16);
}
/**
* Start populating the vehicle information map with passed values.
*/
private final void buildVehicleInfoMap(EcuDefinition ecuDef) {
vehicleInfo.put("CAL ID", ecuDef.getCalId());
vehicleInfo.put("ECU ID", ecuDef.getEcuId());
vehicleInfo.put("Description",
ecuDef.getCarString().replaceAll("Nissan ", ""));
}
/**
* Retrieve the table axis values from the ECU definition. First try the
* 4-cyl table names, if still empty try the 6-cyl table name.
*/
private final List<EcuQuery> getTableAxisRanges(
Document document,
EcuDefinition ecuDef,
List<String> tableNames) {
List<EcuQuery> tableAxis = new ArrayList<EcuQuery>();
for (String tableName : tableNames) {
tableAxis = loadTable(document, ecuDef, tableName);
if (!tableAxis.isEmpty()) {
break;
}
}
return tableAxis;
}
/**
* Once values from the ECU have been populated add the values to the
* table models datasets.
*/
private final void processEcuQueryResponses(List<EcuQuery> queries) {
final NCSParameterCrossReference parameterMap = new NCSParameterCrossReference();
for (EcuQuery query : queries) {
final NCSParameter parameterId =
NCSParameter.fromValue(query.getLoggerData().getId());
final String paramDesc = parameterMap.getValue(parameterId);
String result = String.format("%.2f %s",
query.getResponse(),
query.getLoggerData().getSelectedConvertor().getUnits());
switch (parameterId) {
default:
vehicleInfo.put(paramDesc, result);
break;
}
}
}
/**
* Build a List of EcuQueries to retrieve the axis and scaling of a table.
* A table is found when the storageaddress parameter has been identified.
*/
private final List<EcuQuery> loadTable(
Document document,
EcuDefinition ecuDef,
String tableName) {
final List<Node> inheritanceList =
EcuDefinitionInheritanceList.getInheritanceList(document, ecuDef);
final Map<String, String> tableMap =
EcuTableDefinitionHandler.getTableDefinition(
document,
inheritanceList,
tableName);
List<EcuQuery> tableAxisQuery = new ArrayList<EcuQuery>();
if (tableMap.containsKey("storageaddress")) {
tableAxisQuery = NCSTableAxisQueryParameterSet.build(
tableMap.get("storageaddress"),
tableMap.get("storagetype"),
tableMap.get("expression"),
tableMap.get("units"),
tableMap.get("sizey")
);
}
return tableAxisQuery;
}
/**
* Format the range data to be used as table column header values.
*/
private final String[] formatRanges(
Collection<EcuQuery> axisRanges,
String numberFormat) {
final List<String> ranges = new ArrayList<String>();
ranges.add(" ");
double value = 0;
for (EcuQuery ecuQuery : axisRanges) {
value = ecuQuery.getResponse();
final String range = String.format(
numberFormat,
value);
ranges.add(range);
}
return ranges.toArray(new String[0]);
}
/**
* Format the RPM range data to be used as FLKC table row header values.
*/
private final String[] formatRpmRanges(Collection<EcuQuery> axisRanges) {
final List<String> ranges = new ArrayList<String>();
double value = 0;
for (EcuQuery ecuQuery : axisRanges) {
value = ecuQuery.getResponse();
final String range = String.format(
"%.0f", value);
ranges.add(range);
}
return ranges.toArray(new String[0]);
}
/**
* Return a Transport based on its String ID.
*/
private Transport getTransportById(String id) {
for (Transport transport : getTransportMap().keySet()) {
if (transport.getId().equalsIgnoreCase(id))
return transport;
}
return null;
}
/**
* Return a Map of Transport and associated Modules for the current protocol.
*/
private Map<Transport, Collection<Module>> getTransportMap() {
return logger.getProtocolList().get(settings.getLoggerProtocol());
}
/**
* Return a Module based on its String name.
*/
private Module getModule(String name) {
final Collection<Module> modules = getTransportMap().get(
getTransportById(settings.getTransportProtocol()));
for (Module module: modules) {
if (module.getName().equalsIgnoreCase(name))
return module;
}
return null;
}
/**
* Load ECU def table names from a user customized properties file.
* The file will replace the Class defined names if it is present.
* Names in the file should be separated by the : character
* @exception FileNotFoundException if the directory or file is not present
* @exception IOException if there's some kind of IO error
*/
private void loadProperties() {
final Properties learning = new Properties();
FileInputStream propFile;
try {
propFile = new FileInputStream("./customize/ncslearning.properties");
learning.load(propFile);
final String ltft_table_column_names =
learning.getProperty("ltft_table_column_names");
String[] names = ltft_table_column_names.split(":", 0);
LTFT_TABLE_COLUMN_NAMES = new ArrayList<String>();
for (String name : names) {
if (ParamChecker.isNullOrEmpty(name)) continue;
LTFT_TABLE_COLUMN_NAMES.add(name);
}
final String ltft_table_row_names =
learning.getProperty("ltft_table_row_names");
names = ltft_table_row_names.split(":", 0);
LTFT_TABLE_ROW_NAMES = new ArrayList<String>();
for (String name : names) {
if (ParamChecker.isNullOrEmpty(name)) continue;
LTFT_TABLE_ROW_NAMES.add(name);
}
propFile.close();
LOGGER.info("NCSLearningTableValues loaded table names from file: ./customize/ncslearning.properties");
} catch (FileNotFoundException e) {
LOGGER.error("NCSLearningTableValues properties file: " + e.getLocalizedMessage());
} catch (IOException e) {
LOGGER.error("NCSLearningTableValues IOException: " + e.getLocalizedMessage());
}
}
}

View File

@ -0,0 +1,68 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.comms.learning.parameter;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
/**
* This Emun defines all the possible parameters used to query the Learning
* Table Values of an ECU.
*/
public enum NCSParameter {
P2("P2"), // ECT
P11("P11"), // IAT
P17("P17"), // Battery Volts
E173("E173"), // mixed ratio learning map trim
E174("E174"); // mixed ratio learning map count
private static final Map<String, NCSParameter> lookup
= new HashMap<String, NCSParameter>();
static {
for(NCSParameter s : EnumSet.allOf(NCSParameter.class))
lookup.put(s.toString(), s);
}
private NCSParameter(final String text) {
this.text = text;
}
private final String text;
@Override
public final String toString() {
return text;
}
/**
* Retrieve the Parameter that has the given value.
* @param value - the value of the Parameter in String format
* @return the Parameter that has the given value or null if undefined.
*/
public static NCSParameter fromValue(String value) {
NCSParameter result = null;
if (lookup.containsKey(value)) {
result = lookup.get(value);
}
return result;
}
}

View File

@ -0,0 +1,50 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.comms.learning.parameter;
import static com.romraider.logger.ecu.comms.learning.parameter.NCSParameter.P11;
import static com.romraider.logger.ecu.comms.learning.parameter.NCSParameter.P17;
import static com.romraider.logger.ecu.comms.learning.parameter.NCSParameter.P2;
import java.util.HashMap;
import java.util.Map;
/**
* A Map of Parameter and value specific to the Vehicle Information Table.
*/
public class NCSParameterCrossReference {
final Map<NCSParameter, String> map;
public NCSParameterCrossReference() {
map = new HashMap<NCSParameter, String>();
map.put(P17, "Battery");
map.put(P11, "IAT");
map.put(P2, "ECT");
}
/**
* Retrieve the string value associated with the supplied Parameter.
* @param parameter - Parameter to lookup value for.
* @return the value of the Parameter.
*/
public final String getValue(NCSParameter parameter) {
return map.get(parameter);
}
}

View File

@ -0,0 +1,91 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2015 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.comms.learning.tableaxis;
import static com.romraider.logger.ecu.definition.xml.ConverterMaxMinDefaults.getDefault;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.romraider.Settings;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.comms.query.EcuQueryData;
import com.romraider.logger.ecu.comms.query.EcuQueryImpl;
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.EcuParameterConvertorImpl;
import com.romraider.logger.ecu.definition.EcuParameterImpl;
import com.romraider.util.HexUtil;
/**
* Build a List of ECU Queries to retrieve a Table's axis values.
*/
public class NCSTableAxisQueryParameterSet {
private NCSTableAxisQueryParameterSet() {
}
/**
* Build a List of ECU Queries.
* @param storageAddress - the starting address of the Table.
* @param storageType - the data storage type (big endian assumed).
* @param expression - the equation to convert byte data to a real number.
* @param units - the value's unit of measure.
* @param size - the length of the Table's axis.
* @param endian - the data endian.
* @return a List of ECU Query items.
*/
public static final List<EcuQuery> build(
String storageAddress,
String storageType,
String expression,
String units,
String size) {
final List<EcuQuery> tableAxisQuery = new ArrayList<EcuQuery>();
final String tableAddrStr = storageAddress.replaceAll("0x", "");
final int tableAddrBase = Integer.parseInt(tableAddrStr, 16);
int dataSize = EcuQueryData.getDataLength(storageType);
final int count = Integer.parseInt(size, 10);
for (int i = 0; i < count; i++) {
final String addrStr =
HexUtil.intToHexString(tableAddrBase + (i * dataSize));
final String id = addrStr + "-" + i;
final EcuAddress ea = new EcuAddressImpl(addrStr, dataSize, -1);
final EcuParameterImpl epi =
new EcuParameterImpl(id, addrStr, id, ea, null, null, null,
new EcuDataConvertor[] {
new EcuParameterConvertorImpl(
units, expression, "0.000", -1, storageType,
Settings.ENDIAN_BIG, new HashMap<String, String>(),
getDefault()
)
}
);
tableAxisQuery.add(new EcuQueryImpl((EcuData) epi));
}
return tableAxisQuery;
}
}

View File

@ -0,0 +1,103 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.comms.learning.tables;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.comms.query.EcuQueryData;
import com.romraider.logger.ecu.comms.query.EcuQueryImpl;
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.EcuParameterImpl;
import com.romraider.logger.ecu.ui.paramlist.ParameterRow;
import com.romraider.util.HexUtil;
/**
* Build an EcuQuery for each of the cells in the FLKC RAM table.
*/
public class NCSLtftTableQueryBuilder {
private static final Logger LOGGER =
Logger.getLogger(NCSLtftTableQueryBuilder.class);
public NCSLtftTableQueryBuilder() {
}
/**
* Build an EcuQuery for each cell of the LTFT RAM table. <i>Note this
* returns an extra null query for column 0 of each row which is later
* populated with the row header (RPM Ranges) data.</i>
* @param ltft - a ParameterRow item that helps to identify the
* ECU bitness and provide a Converter for the raw data.
* @param ltftAddr - the address in RAM of the start of the table.
* @param rows - the number of rows in the table.
* @param columns - the number of columns in the table.
* @return EcuQueries divided into groups to query each row separately to
* avoid maxing out the ECU send/receive buffer.
*/
public final List<List<EcuQuery>> build(
ParameterRow ltft,
int ltftAddr,
int rows,
int columns) {
final List<List<EcuQuery>> ltftQueryRows = new ArrayList<List<EcuQuery>>();
final EcuData parameter = (EcuData) ltft.getLoggerData();
int dataSize = EcuQueryData.getDataLength(parameter);
LOGGER.debug(
String.format(
"LTFT Data format rows:%d col:%d " +
"dataSize:%d LTFT:%s",
rows, columns, dataSize,
ltft.getLoggerData().getId()));
int i = 0;
for (int j = 0; j < rows; j++) {
final List<EcuQuery> ltftQueryCols = new ArrayList<EcuQuery>();
ltftQueryCols.add(null);
for (int k = 0; k < columns; k++) {
String id = "flkc-r" + j + "c" + k;
final String addrStr =
HexUtil.intToHexString(
ltftAddr + (i * dataSize));
LOGGER.debug(
String.format(
"LTFT Data row:%d col:%d addr:%s",
j, k, addrStr));
final EcuAddress ea = new EcuAddressImpl(addrStr, dataSize, -1);
final EcuParameterImpl epi =
new EcuParameterImpl(id, addrStr, id, ea, null, null, null,
new EcuDataConvertor[] {
ltft.getLoggerData().getSelectedConvertor()
}
);
ltftQueryCols.add(new EcuQueryImpl((EcuData) epi));
i++;
}
ltftQueryRows.add(ltftQueryCols);
}
return ltftQueryRows;
}
}

View File

@ -0,0 +1,44 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.comms.query;
import static com.romraider.util.ParamChecker.checkNotNullOrEmpty;
import static java.lang.System.arraycopy;
public final class NCSEcuInit implements EcuInit {
private byte[] ecuInitBytes;
private String ecuId;
public NCSEcuInit(byte[] ecuInitBytes) {
checkNotNullOrEmpty(ecuInitBytes, "ecuInitBytes");
this.ecuInitBytes = ecuInitBytes;
final byte[] ecuIdBytes = new byte[5];
arraycopy(ecuInitBytes, 3, ecuIdBytes, 0, 5);
ecuId = new String(ecuIdBytes);
}
public String getEcuId() {
return ecuId;
}
public byte[] getEcuInitBytes() {
return ecuInitBytes;
}
}

View File

@ -0,0 +1,409 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2018 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.ui.swing.tools;
import static com.romraider.Settings.COMMA;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
import static javax.swing.JOptionPane.showMessageDialog;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import com.romraider.logger.ecu.EcuLogger;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.ui.swing.tools.tablemodels.FineLearningKnockCorrectionTableModel;
import com.romraider.logger.ecu.ui.swing.tools.tablemodels.VehicleInformationTableModel;
import com.romraider.logger.ecu.ui.swing.tools.tablemodels.renderers.CentreRenderer;
import com.romraider.logger.ecu.ui.swing.tools.tablemodels.renderers.LtvCellRenderer;
import com.romraider.logger.ecu.ui.swing.vertical.VerticalLabelUI;
import com.romraider.swing.SetFont;
import com.romraider.util.FormatFilename;
import com.romraider.util.SettingsManager;
/**
* This class is used to build and display the Learning Table Values
* retrieved from the ECU.
*/
public class NCSLearningTableValuesResultsPanel extends JDialog {
private static final long serialVersionUID = 6716454297236022709L;
private final String DIALOG_TITLE = "Learning Table Values";
private final String DT_FORMAT = "%1$tY%1$tm%1$td-%1$tH%1$tM%1$tS";
private final int LTV_WIDTH = 720;
private final int LTV_HEIGHT = 450;
private final JPanel contentPanel = new JPanel();
private JTable vehicleInfoTable;
//TODO: replace AF learning with a knock table
private JTable afLearningTable;
private List<JTable> ltftTables = new ArrayList<JTable>();
private final String LTFT_NAME = "Long Term Fuel Trim Tables";
public NCSLearningTableValuesResultsPanel(
EcuLogger logger,
Map<String, Object> vehicleInfo,
String[] afRanges,
List<List<Object>> afLearning,
String[] ltftCol,
String[] ltftRow,
List<List<List<EcuQuery>>> ltftData) {
super(logger, false);
setIconImage(logger.getIconImage());
setTitle(DIALOG_TITLE);
setBounds(
(logger.getWidth() > LTV_WIDTH) ?
logger.getX() + (logger.getWidth() - LTV_WIDTH) / 2 : 0,
(logger.getHeight() > LTV_HEIGHT) ?
logger.getY() + ((logger.getHeight() - LTV_HEIGHT) / 2) : 0,
LTV_WIDTH,
LTV_HEIGHT);
getContentPane().setLayout(new BorderLayout());
contentPanel.setBorder(new EmptyBorder(2, 2, 2, 2));
contentPanel.setLayout(null);
contentPanel.add(buildVehicleInfoPanel(vehicleInfo));
// contentPanel.add(buildAfLearningPanel(afRanges, afLearning));
contentPanel.add(buildLtftPanel(ltftCol, ltftRow, ltftData));
getContentPane().add(contentPanel, BorderLayout.CENTER);
getContentPane().add(buildSaveReultsPanel(), BorderLayout.SOUTH);
}
/**
* This method is called to display the Learning Table Values
* retrieved from the ECU.
*/
public final void displayLearningResultsPanel() {
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setVisible(true);
}
private final JPanel buildVehicleInfoPanel(
Map<String, Object> vehicleInfo) {
final JPanel vehicleInfoTitlePanel = new JPanel();
vehicleInfoTitlePanel.setBorder(
BorderFactory.createTitledBorder("Vehicle Information"));
vehicleInfoTitlePanel.setBounds(10, 2, 692, 70);
vehicleInfoTitlePanel.setLayout(new BorderLayout(0, 0));
JPanel vehicleInfoTablePanel = new JPanel();
vehicleInfoTablePanel.setBorder(
new EtchedBorder(EtchedBorder.LOWERED, null, null));
vehicleInfoTablePanel.setLayout(new BorderLayout(0, 0));
final VehicleInformationTableModel tableModel =
new VehicleInformationTableModel();
tableModel.setVehicleInfo(vehicleInfo);
vehicleInfoTable = new JTable(tableModel);
setTableBehaviour(vehicleInfoTable);
TableColumn column = null;
for (int i = 0; i < vehicleInfoTable.getColumnCount(); i++) {
column = vehicleInfoTable.getColumnModel().getColumn(i);
if (i == 0 || i ==1) {
column.setPreferredWidth(85);
}
else if (i == 2) {
column.setPreferredWidth(200);
}
}
vehicleInfoTablePanel.add(
formatTableHeader(vehicleInfoTable),
BorderLayout.PAGE_START);
vehicleInfoTablePanel.add(vehicleInfoTable);
vehicleInfoTitlePanel.add(vehicleInfoTablePanel, BorderLayout.CENTER);
return vehicleInfoTitlePanel;
}
private final JPanel buildLtftPanel(
String[] ltftLoad,
String[] ltftRpm,
List<List<List<EcuQuery>>> ltftQueryTables) {
final JPanel ltftTitlePanel = new JPanel();
ltftTitlePanel.setBorder(
new TitledBorder(null,
LTFT_NAME,
TitledBorder.LEADING,
TitledBorder.TOP, null, null));
ltftTitlePanel.setBounds(10, 72, 692, 226);
ltftTitlePanel.setLayout(new BorderLayout(0, 0));
final JLabel xLabel = new JLabel("Injector Pule Width (msec)");
SetFont.plain(xLabel);
xLabel.setHorizontalAlignment(SwingConstants.CENTER);
ltftTitlePanel.add(xLabel, BorderLayout.NORTH);
final JLabel yLabel = new JLabel("Engine Speed (RPM)");
SetFont.plain(yLabel);
yLabel.setUI(new VerticalLabelUI(false));
ltftTitlePanel.add(yLabel, BorderLayout.WEST);
final JTabbedPane tabs = new JTabbedPane();
for (int i = 0; i < ltftQueryTables.size(); i++) {
final FineLearningKnockCorrectionTableModel tableModel =
new FineLearningKnockCorrectionTableModel();
tableModel.setColumnHeadings(ltftLoad);
tableModel.setRomHeadings(ltftRpm);
tableModel.setFlkcData(ltftQueryTables.get(i));
final JTable ltftTable = new JTable(tableModel);
ltftTables.add(ltftTable);
setTableBehaviour(ltftTable);
formatTableHeader(ltftTable);
final JScrollPane ltftTablePanel = new JScrollPane(ltftTable);
ltftTablePanel.setBorder(
new EtchedBorder(EtchedBorder.LOWERED, null, null));
switch (i) {
case 0:
tabs.addTab("Left Bank - Trim (%)", ltftTablePanel);
break;
case 1:
tabs.addTab("Right Bank - Trim (%)", ltftTablePanel);
break;
case 2:
tabs.addTab("Left Bank - Count", ltftTablePanel);
break;
case 3:
tabs.addTab("Right Bank - Count", ltftTablePanel);
break;
}
}
if (ltftQueryTables.size() > 0) {
ltftTitlePanel.add(tabs, BorderLayout.CENTER);
}
else {
ltftTitlePanel.removeAll();
ltftTitlePanel.add(new JLabel(
" No data - Knock Adaptation parameter ID not defined"),
BorderLayout.CENTER);
}
return ltftTitlePanel;
}
private final void setTableBehaviour(JTable table) {
table.setBorder(null);
table.setColumnSelectionAllowed(false);
table.setRowSelectionAllowed(false);
table.setFont(new Font("Tahoma", Font.PLAIN, 11));
table.setFillsViewportHeight(true);
table.setDefaultRenderer(Double.class, new LtvCellRenderer());
table.setDefaultRenderer(String.class, new LtvCellRenderer());
}
private final JTableHeader formatTableHeader(JTable table) {
final JTableHeader th = table.getTableHeader();
th.setReorderingAllowed(false);
th.setDefaultRenderer(new CentreRenderer(table));
SetFont.bold(th, 11);
return th;
}
private final JPanel buildSaveReultsPanel() {
final JPanel controlPanel = new JPanel();
final JButton toFile = new JButton("Save to File");
toFile.setToolTipText("Save tables to a text file");
toFile.setMnemonic(KeyEvent.VK_F);
toFile.addActionListener(new ActionListener() {
@Override
public final void actionPerformed(ActionEvent actionEvent) {
saveTableText();
}
});
final JButton toImage = new JButton("Save as Image");
toImage.setToolTipText("Save tables as an image");
toImage.setMnemonic(KeyEvent.VK_I);
toImage.addActionListener(new ActionListener() {
@Override
public final void actionPerformed(ActionEvent actionEvent) {
saveTableImage();
}
});
controlPanel.add(toFile);
controlPanel.add(toImage);
return controlPanel;
}
private final void saveTableText() {
final String nowStr = String.format(DT_FORMAT, System.currentTimeMillis());
final String fileName = String.format("%s%sromraiderLTV_%s.csv",
SettingsManager.getSettings().getLoggerOutputDirPath(),
File.separator,
nowStr);
try {
final File csvFile = new File(fileName);
final String EOL = System.getProperty("line.separator");
final BufferedWriter bw = new BufferedWriter(
new FileWriter(csvFile));
bw.write("Learning Table Values" + EOL);
Object result = 0;
int columnCount = vehicleInfoTable.getColumnCount();
for (int i = 0; i < columnCount; i++ ) {
result = vehicleInfoTable.getTableHeader().getColumnModel().
getColumn(i).getHeaderValue();
bw.append(result.toString());
bw.append(COMMA);
}
bw.append(EOL);
for (int i = 0; i < columnCount; i++ ) {
result = vehicleInfoTable.getValueAt(0, i);
bw.append(result.toString());
bw.append(COMMA);
}
bw.append(EOL + EOL);
// bw.write("A/F Adaptation (Stored)" + EOL);
// columnCount = afLearningTable.getColumnCount();
// int rowCount = afLearningTable.getRowCount();
// for (int i = 0; i < columnCount; i++) {
// result = afLearningTable.getTableHeader().getColumnModel().
// getColumn(i).getHeaderValue();
// bw.append(result.toString());
// bw.append(COMMA);
// }
// bw.append(EOL);
// for (int i = 0; i < rowCount; i++) {
// for (int j = 0; j < columnCount; j++) {
// result = afLearningTable.getValueAt(i, j);
// bw.append(result.toString());
// bw.append(COMMA);
// }
// bw.append(EOL);
// }
bw.append(EOL);
bw.write(LTFT_NAME + EOL);
int k = 1;
for (JTable ltftTable : ltftTables) {
columnCount = ltftTable.getColumnCount();
for (int i = 0; i < columnCount; i++) {
result = ltftTable.getTableHeader().getColumnModel().
getColumn(i).getHeaderValue();
if (result.toString().equals(" ")) {
switch (k) {
case 1:
result = "Left Bank - Trim (%)";
break;
case 2:
result = "Right Bank - Trim (%)";
break;
case 3:
result = "Left Bank - Count";
break;
case 4:
result = "Right Bank - Count";
break;
}
}
bw.append(result.toString());
bw.append(COMMA);
}
k++;
}
bw.append(EOL);
int rowCount = ltftTables.get(0).getRowCount();
for (int i = 0; i < rowCount; i++) {
for (JTable ltftTable : ltftTables) {
columnCount = ltftTable.getColumnCount();
for (int j = 0; j < columnCount; j++) {
result = ltftTable.getValueAt(i, j);
bw.append(result.toString());
bw.append(COMMA);
}
}
bw.append(EOL);
}
bw.close();
final String shortName = FormatFilename.getShortName(fileName);
showMessageDialog(
null,
"Table's text saved to: " + shortName,
"Save Success",
INFORMATION_MESSAGE);
}
catch (Exception e) {
showMessageDialog(
null,
"Failed to save tables, check path:\n" + fileName,
"Save Failed",
ERROR_MESSAGE);
}
}
private final void saveTableImage() {
final BufferedImage resultsImage = new BufferedImage(
contentPanel.getWidth(),
contentPanel.getHeight(),
BufferedImage.TYPE_INT_ARGB);
contentPanel.paint(resultsImage.createGraphics());
final String nowStr = String.format(DT_FORMAT, System.currentTimeMillis());
final String fileName = String.format("%s%sromraiderLTV_%s.png",
SettingsManager.getSettings().getLoggerOutputDirPath(),
File.separator,
nowStr);
final String shortName = FormatFilename.getShortName(fileName);
try {
final File imageFile = new File(fileName);
ImageIO.write(
resultsImage,
"png",
imageFile);
showMessageDialog(
null,
"Learning Table Values image saved to: " + shortName,
"Save Success",
INFORMATION_MESSAGE);
}
catch (Exception e) {
showMessageDialog(
null,
"Failed to save image, check path:\n" + fileName,
"Save Failed",
ERROR_MESSAGE);
}
}
}