/*
* 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.io.protocol.obd.iso15765;
import static com.romraider.util.HexUtil.asBytes;
import static com.romraider.util.HexUtil.asHex;
import static com.romraider.util.ParamChecker.checkNotNull;
import static com.romraider.util.ParamChecker.checkNotNullOrEmpty;
import static java.lang.System.arraycopy;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import com.romraider.Settings;
import com.romraider.io.connection.ConnectionProperties;
import com.romraider.io.protocol.Protocol;
import com.romraider.logger.ecu.comms.manager.PollingState;
import com.romraider.logger.ecu.comms.query.EcuInit;
import com.romraider.logger.ecu.comms.query.SSMEcuInit;
import com.romraider.logger.ecu.definition.EcuDefinition;
import com.romraider.logger.ecu.definition.Module;
import com.romraider.logger.ecu.exception.InvalidResponseException;
import com.romraider.logger.ecu.exception.UnsupportedProtocolException;
import com.romraider.util.SettingsManager;
public final class OBDProtocol implements Protocol {
private final ByteArrayOutputStream bb = new ByteArrayOutputStream(255);
public static final byte OBD_INIT_COMMAND = (byte) 0x01;
public static final byte OBD_INIT_RESPONSE = (byte) 0x41;
public static final byte OBD_INFO_COMMAND = (byte) 0x09;
public static final byte OBD_INFO_RESPONSE = (byte) 0x49;
public static final byte OBD_RESET_COMMAND = (byte) 0x04;
public static final byte OBD_RESET_RESPONSE = (byte) 0x44;
public static final byte OBD_NRC = (byte) 0x7F;
public static final int RESPONSE_NON_DATA_BYTES = 5;
public static Module module;
@Override
public byte[] constructEcuInitRequest(Module module) {
checkNotNull(module, "module");
OBDProtocol.module = module;
final byte[] request = buildRequest(
OBD_INFO_COMMAND, true, new byte[]{4});
return request;
}
@Override
public byte[] constructWriteMemoryRequest(
Module module, byte[] address, byte[] values) {
throw new UnsupportedProtocolException(
"Write memory command is not supported on OBD for address: " +
asHex(address));
}
@Override
public byte[] constructWriteAddressRequest(
Module module, byte[] address, byte value) {
throw new UnsupportedProtocolException(
"Write Address command is not supported on OBD for address: " +
asHex(address));
}
@Override
public byte[] constructReadMemoryRequest(
Module module, byte[] address, int numBytes) {
throw new UnsupportedProtocolException(
"Read memory command is not supported on OBD for address: " +
asHex(address));
}
@Override
public byte[] constructReadAddressRequest(Module module, byte[][] addresses) {
checkNotNull(module, "module");
checkNotNullOrEmpty(addresses, "addresses");
OBDProtocol.module = module;
return buildRequest(OBD_INIT_COMMAND, true, addresses);
}
@Override
public byte[] preprocessResponse(
byte[] request, byte[] response, PollingState pollState) {
return OBDResponseProcessor.filterRequestFromResponse(
request, response, pollState);
}
@Override
public byte[] parseResponseData(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
return OBDResponseProcessor.extractResponseData(processedResponse);
}
@Override
public void checkValidEcuInitResponse(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
OBDResponseProcessor.validateResponse(processedResponse);
// four byte - CAN ID
// one byte - Response mode
// one byte - Response pid
// null terminated CAL ID string
// 000007E8 49 .....
byte responseType = processedResponse[4];
if (responseType != OBD_INFO_RESPONSE) {
throw new InvalidResponseException(
"Unexpected " + module.getName() + " Info response type: " +
asHex(new byte[]{responseType}));
}
}
@Override
public EcuInit parseEcuInitResponse(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
final byte[] ecuInitBytes = parseResponseData(processedResponse);
final byte[] calIdBytes = Arrays.copyOf(ecuInitBytes, 8);
final String calIdStr = new String(calIdBytes);
final Settings settings = SettingsManager.getSettings();
final Map<String, EcuDefinition> defMap =
settings.getLoggerEcuDefinitionMap();
byte[] ecuIdBytes = new byte[] {0,0,0,0,0};
// convert CALID to ECUID based on defined ECU defs
for (EcuDefinition ecuDef : defMap.values()) {
if (ecuDef.getCalId().equals(calIdStr)) {
ecuIdBytes = asBytes(ecuDef.getEcuId());
break;
}
}
arraycopy(ecuIdBytes, 0, ecuInitBytes, 3, 5);
return new SSMEcuInit(ecuInitBytes);
}
@Override
public byte[] constructEcuResetRequest(Module module, int resetCode) {
checkNotNull(module, "module");
OBDProtocol.module = module;
// 000007E0 04
return buildRequest((byte) 0, false, new byte[]{OBD_RESET_COMMAND});
}
@Override
public void checkValidEcuResetResponse(byte[] processedResponse) {
checkNotNullOrEmpty(processedResponse, "processedResponse");
// 000007E8 44
byte responseType = processedResponse[4];
if (responseType != OBD_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 ConnectionProperties() {
public int getBaudRate() {
return 500000;
}
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 55;
}
};
}
private byte[] buildRequest(
byte command,
boolean addCommand,
byte[]... content) {
bb.reset();
try {
bb.write(module.getTester());
if (addCommand) {
bb.write(command);
}
for (byte[] tmp : content) {
bb.write(tmp);
}
} catch (IOException e) {
e.printStackTrace();
}
return bb.toByteArray();
}
}