Added a test app to exercise J2534/ISO15765 messaging

Can be called from the commandline as follows:
java -cp RomRaider.jar com.romraider.io.j2534.api.TestJ2534IsoTp [J2534dll]
This commit is contained in:
Dale Schultz 2013-03-23 16:29:24 -04:00
parent e4903b2b4a
commit 40a9ecaca3
1 changed files with 361 additions and 0 deletions

View File

@ -0,0 +1,361 @@
/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2013 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.util.ByteUtil.asUnsignedInt;
import static com.romraider.util.HexUtil.asHex;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.romraider.io.j2534.api.J2534Impl.Config;
import com.romraider.io.j2534.api.J2534Impl.Protocol;
import com.romraider.io.j2534.api.J2534Impl.TxFlags;
import com.romraider.logger.ecu.exception.InvalidResponseException;
import com.romraider.util.HexUtil;
import com.romraider.util.LogManager;
/**
* This class is used to exercise the J2534 API against a real J2534 device and
* an active ECU using the ISO15765-2 protocol.
*/
public final class TestJ2534IsoTp {
private static J2534 api ;
private static final int LOOPBACK = 0;
private static final byte[] mask1 = {
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
private static final byte[] match1 = {
(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0xe8};
private static final byte[] fc1 = {
(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0xe0};
private static final byte ECU_NRC = (byte) 0x7F;
private static final byte READ_MODE9_RESPONSE = (byte) 0x49;
private static final byte READ_MODE9_COMMAND = (byte) 0x09;
private static final byte[] READ_MODE9_PIDS = {
(byte) 0x02, (byte) 0x04, (byte) 0x06};
private static final byte READ_MODE3_RESPONSE = (byte) 0x43;
private static final byte READ_MODE3_COMMAND = (byte) 0x03;
private static final byte ECU_INIT_COMMAND = (byte) 0x01;
private static final byte ECU_INIT_RESPONSE = (byte) 0x41;
private static final StringBuilder sb = new StringBuilder();
public TestJ2534IsoTp() throws InterruptedException {
final int deviceId = api.open();
sb.delete(0, sb.capacity());
try {
version(deviceId);
final int channelId = api.connect(deviceId, 0, 500000);
final double vBatt = api.getVbattery(deviceId);
// System.out.println("Pin 16: " + vBatt + " VDC");
sb.append(String.format(
"J2534 Interface Pin 16: %sVDC%n", vBatt));
final byte[] mask2 = {
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
final byte[] match2 = {
(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0xdf};
final byte[] fc2 = {
(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0xdf}; //Tester
final byte[] mask3 = {
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
final byte[] match3 = {
(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0xe0};
final byte[] fc3 = {
(byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0xe0};
final int msgId = api.startFlowCntrlFilter(
channelId, mask1, match1, fc1, TxFlags.ISO15765_FRAME_PAD);
// final int msgId1 = api.startFlowCntrlFilter(
// channelId, mask2, match2, fc2, TxFlags.ISO15765_FRAME_PAD);
// final int msgId2 = api.startFlowCntrlFilter(
// channelId, mask3, match3, fc3, TxFlags.ISO15765_FRAME_PAD);
try {
setConfig(channelId);
getConfig(channelId);
sb.append(String.format("%n--- Vehicle Information ---%n"));
for (byte pid : READ_MODE9_PIDS) {
final byte[] mode9 = buildRequest(
READ_MODE9_COMMAND, // mode
pid, // pid
true, // pid valid
fc1); // source
api.writeMsg(channelId,
mode9,
1000L,
TxFlags.ISO15765_FRAME_PAD);
final byte[] response;
response = api.readMsg(channelId, 1, 1000L);
// System.out.println("Response = " +
// HexUtil.asHex(response));
handleResponse(response);
}
final byte[] mode3 = buildRequest(
READ_MODE3_COMMAND, // mode
(byte) 0x00, // pid
false, // pid valid
fc1); // source
api.writeMsg(channelId,
mode3,
1000L,
TxFlags.ISO15765_FRAME_PAD);
final byte[] response;
response = api.readMsg(channelId, 1, 1000L);
// System.out.println("Response = " +
// HexUtil.asHex(response));
handleResponse(response);
}
catch (Exception e) {
// System.out.println(e);
sb.append(e);
}
finally {
api.stopMsgFilter(channelId, msgId);
// api.stopMsgFilter(channelId, msgId1);
// api.stopMsgFilter(channelId, msgId2);
api.disconnect(channelId);
}
} finally {
api.close(deviceId);
}
}
public final String toString() {
return sb.toString();
}
private final static void handleResponse(byte[] response) {
validateResponse(response);
final int responseLen = response.length;
if (response[4] == READ_MODE9_RESPONSE) {
final byte[] data = new byte[responseLen - 7];
System.arraycopy(response, 7, data, 0, data.length);
if (response[5] == 0x02) {
// System.out.println("VIN: " + new String(data));
sb.append(String.format(
"VIN: %s%n", new String(data)));
}
if (response[5] == 0x04) {
int i;
for (i = 0; i < data.length && data[i] != 0; i++) { }
final String str = new String(data, 0, i);
// System.out.println("CAL ID: " + str);
sb.append(String.format(
"CAL ID: %s%n", str));
}
if (response[5] == 0x06) {
// System.out.println("CVN: " + HexUtil.asHex(data));
sb.append(String.format(
"CVN: %s%n", HexUtil.asHex(data)));
}
if (response[5] == 0x08) {
// System.out.println("PID_8: " + HexUtil.asHex(data));
}
if (response[5] == 0x0A) {
int i;
for (i = 0; i < data.length && data[i] != 0; i++) { }
final String str = new String(data, 0, i);
// System.out.println("Module: " + str);
sb.append(String.format(
"Module: %s%n", str));
}
}
if (response[4] == READ_MODE3_RESPONSE) {
if (response[5] > 0x00) {
final byte[] data = new byte[responseLen - 6];
System.arraycopy(response, 6, data, 0, data.length);
int i;
int j = 1;
final byte[] codeHex = new byte[2];
for (i = 0; i < data.length; i = i + 2) {
System.arraycopy(data, i, codeHex, 0, 2);
final byte module = (byte) ((codeHex[0] & 0xC0) >> 6);
String moduleTxt = null;
switch (module) {
case 0:
moduleTxt = "P";
break;
case 1:
moduleTxt = "C";
break;
case 2:
moduleTxt = "B";
break;
case 3:
moduleTxt = "U";
break;
}
final byte dtcB1 = (byte) ((codeHex[0] & 0x30) >> 4);
final byte dtcB2 = (byte) (codeHex[0] & 0x0F);
final byte dtcB3 = (byte) ((codeHex[1] & 0xF0) >> 4);
final byte dtcB4 = (byte) (codeHex[1] & 0x0F);
// System.out.print(
// String.format("DTC %d: %s%s%s%s%s%n",
// j,
// moduleTxt,
// Character.forDigit(dtcB1, 16),
// Character.forDigit(dtcB2, 16),
// Character.forDigit(dtcB3, 16),
// Character.forDigit(dtcB4, 16)));
sb.append(String.format(
"DTC %d: %s%s%s%s%s%n",
j,
moduleTxt,
Character.forDigit(dtcB1, 16),
Character.forDigit(dtcB2, 16),
Character.forDigit(dtcB3, 16),
Character.forDigit(dtcB4, 16)));
j++;
}
}
}
}
private final static void version(final int deviceId) {
final Version version = api.readVersion(deviceId);
// System.out.printf("Version => Firmware:[%s], DLL:[%s], API:[%s]%n",
// version.firmware, version.dll, version.api);
sb.append(String.format(
"J2534 Firmware:[%s], DLL:[%s], API:[%s]%n",
version.firmware, version.dll, version.api));
}
private final static void setConfig(int channelId) {
final ConfigItem loopback = new ConfigItem(Config.LOOPBACK.getValue(), LOOPBACK);
final ConfigItem bs = new ConfigItem(Config.ISO15765_BS.getValue(), 0);
final ConfigItem stMin = new ConfigItem(Config.ISO15765_STMIN.getValue(), 0);
final ConfigItem bs_tx = new ConfigItem(Config.BS_TX.getValue(), 0xffff);
final ConfigItem st_tx = new ConfigItem(Config.STMIN_TX.getValue(), 0xffff);
final ConfigItem wMax = new ConfigItem(Config.ISO15765_WFT_MAX.getValue(), 0);
api.setConfig(channelId, loopback, bs, stMin, bs_tx, st_tx, wMax);
}
private final static void getConfig(int channelId) {
final ConfigItem[] configs = api.getConfig(
channelId,
Config.LOOPBACK.getValue(),
Config.ISO15765_BS.getValue(),
Config.ISO15765_STMIN.getValue(),
Config.BS_TX.getValue(),
Config.STMIN_TX.getValue(),
Config.ISO15765_WFT_MAX.getValue()
);
int i = 1;
for (ConfigItem item : configs) {
// System.out.printf("J2534 Config item %d: Parameter: %s, value:%d%n",
// i, Config.get(item.parameter), item.value);
sb.append(String.format(
"J2534 Config item %d: Parameter: %s, value:%d%n",
i, Config.get(item.parameter), item.value));
i++;
}
}
private final static void validateResponse(byte[] response) {
assertEquals(match1, response, "Invalid ECU id");
if (response.length == 7) {
assertNrc(ECU_NRC, response[4], response[5], response[6],"Request type not supported");
}
assertOneOf(new byte[]{ECU_INIT_RESPONSE, READ_MODE3_RESPONSE, READ_MODE9_RESPONSE}, response[4], "Invalid response code");
}
private final static void assertNrc(byte expected, byte actual, byte command, byte code, String msg) {
if (actual == expected) {
String ec = " unsupported.";
if (code == 0x13) {
ec = " invalid format or length.";
}
throw new InvalidResponseException(
msg + ". Command: " + asHex(new byte[]{command}) + ec);
}
}
private final static void assertEquals(byte[] expected, byte[] actual, String msg) {
final byte[] idBytes = new byte[4];
System.arraycopy(actual, 0, idBytes, 0, 4);
final int idExpected = asUnsignedInt(expected);
final int idActual = asUnsignedInt(idBytes);
if (idActual != idExpected) {
throw new InvalidResponseException(
msg + ". Expected: " + asHex(expected) +
". Actual: " + asHex(idBytes) + ".");
}
}
private final static void assertOneOf(byte[] validOptions, byte actual, String msg) {
for (byte option : validOptions) {
if (option == actual) {
return;
}
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < validOptions.length; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(asHex(new byte[]{validOptions[i]}));
}
throw new InvalidResponseException(msg + ". Expected one of [" + builder.toString() + "]. Actual: " + asHex(new byte[]{actual}) + ".");
}
private final static byte[] buildRequest(
byte mode,
byte pid,
boolean pidValid,
byte[]... content) {
final ByteArrayOutputStream bb = new ByteArrayOutputStream(6);
try {
for (byte[] tmp : content) {
bb.write(tmp);
}
bb.write(mode);
if (pidValid) {
bb.write(pid);
}
} catch (IOException e) {
e.printStackTrace();
}
return bb.toByteArray();
}
public final static void main(String args[]) throws InterruptedException{
LogManager.initDebugLogging();
if (args.length == 0) { //op20pt32 MONGI432
api = new J2534Impl(
Protocol.ISO15765, "op20pt32");
}
else {
api = new J2534Impl(
Protocol.ISO15765, args[0]);
}
final TestJ2534IsoTp test1 = new TestJ2534IsoTp();
System.out.print(test1.toString());
}
}