/* * 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.ds2.iso9141; import static com.romraider.io.protocol.ds2.iso9141.DS2Protocol.RESPONSE_NON_DATA_BYTES; import static com.romraider.io.protocol.ds2.iso9141.DS2ResponseProcessor.extractResponseData; import static com.romraider.io.protocol.ds2.iso9141.DS2ResponseProcessor.filterRequestFromResponse; import static com.romraider.util.HexUtil.asBytes; 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.ProtocolDS2; import com.romraider.logger.ecu.comms.io.protocol.LoggerProtocolDS2; 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.EcuData; import com.romraider.logger.ecu.definition.Module; public final class DS2LoggerProtocol implements LoggerProtocolDS2 { private final ProtocolDS2 protocol = new DS2Protocol(); @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) { Collection<EcuQuery> filteredQueries = filterDuplicates(queries); return protocol.constructReadAddressRequest( module, convertToByteAddresses(filteredQueries)); } @Override public byte[] constructReadGroupRequest( Module module, String group) { return protocol.constructReadGroupRequest( module, new byte[][]{asBytes(group)}); } @Override public byte[] constructReadMemoryRequest( Module module, Collection<EcuQuery> queries) { Collection<EcuQuery> filteredQueries = filterDuplicates(queries); return protocol.constructReadMemoryRequest( module, convertToByteAddresses(filteredQueries), getDataLength(filteredQueries)); } @Override public byte[] constructReadMemoryRange( Module module, Collection<EcuQuery> queries, int length) { return protocol.constructReadMemoryRequest( module, convertToByteAddresses(queries), length); } @Override public byte[] constructReadAddressResponse(Collection<EcuQuery> queries, PollingState pollState) { return null; } @Override public byte[] constructReadAddressResponse( Collection<EcuQuery> queries, int requestSize) { checkNotNullOrEmpty(queries, "queries"); Collection<EcuQuery> filteredQueries = filterDuplicates(queries); int numAddresses = 0; for (EcuQuery ecuQuery : filteredQueries) { numAddresses += EcuQueryData.getDataLength(ecuQuery); } return new byte[requestSize + RESPONSE_NON_DATA_BYTES + numAddresses]; } @Override public byte[] constructReadMemoryRangeResponse(int requestSize, int length) { return new byte[requestSize + RESPONSE_NON_DATA_BYTES + length]; } @Override public byte[] constructReadGroupResponse( Collection<EcuQuery> queries, int requestSize) { checkNotNullOrEmpty(queries, "queries"); int size = 0; for (EcuQuery ecuQuery : queries) { size = ((EcuData) ecuQuery.getLoggerData()).getGroupSize(); break; } return new byte[requestSize + RESPONSE_NON_DATA_BYTES + size]; } @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"); protocol.checkValidEcuInitResponse(response); EcuInit ecuInit = protocol.parseEcuInitResponse(response); callback.callback(ecuInit); } @Override public void processEcuResetResponse(byte[] response) { checkNotNullOrEmpty(response, "response"); protocol.checkValidEcuResetResponse(response); } /** * Processes the response bytes and set individual response on corresponding * query objects. * If EcuData has a group size value greater than 0 then the response is * the result of a group read and the address is the index into the response * array. **/ @Override public void processReadAddressResponses(Collection<EcuQuery> queries, byte[] response, PollingState pollState) { checkNotNullOrEmpty(queries, "queries"); checkNotNullOrEmpty(response, "response"); byte[] responseData = extractResponseData(response); Collection<EcuQuery> filteredQueries = filterDuplicates(queries); Map<String, byte[]> addressResults = new HashMap<String, byte[]>(); int srcPos = 0; for (EcuQuery filteredQuery : filteredQueries) { byte[] bytes = new byte[EcuQueryData.getDataLength(filteredQuery)]; if (((EcuData) filteredQuery.getLoggerData()).getGroupSize() > 0) { srcPos = filteredQuery.getBytes()[0]; } arraycopy(responseData, srcPos, bytes, 0, bytes.length); addressResults.put(filteredQuery.getHex(), bytes); srcPos = 0; } for (EcuQuery query : queries) { query.setResponse(addressResults.get(query.getHex())); } } /** * Processes the response bytes and set individual response on corresponding * query objects. * The response data is based on the lowest EcuData address and the length * is the result of the difference between the highest and lowest address. * The index into the response array is based in the lowest address. **/ public void processReadMemoryRangeResponse( Collection<EcuQuery> queries, byte[] response) { 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 lowestAddress = Integer.MAX_VALUE; for (EcuQuery filteredQuery : filteredQueries) { final int address = Integer.parseInt(filteredQuery.getHex(), 16); if (address < lowestAddress) { lowestAddress = address; } } int srcPos = 0; for (EcuQuery filteredQuery : filteredQueries) { final byte[] bytes = new byte[EcuQueryData.getDataLength(filteredQuery)]; srcPos = Integer.parseInt(filteredQuery.getHex(), 16) - lowestAddress; arraycopy(responseData, srcPos, bytes, 0, bytes.length); addressResults.put(filteredQuery.getHex(), bytes); } 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; int addressSize = 0; for (EcuQuery query : queries) { byteCount += query.getAddresses().length; addressSize = query.getBytes().length; } final byte[][] addresses = new byte[byteCount][addressSize]; int i = 0; for (EcuQuery query : queries) { final int addrLength = query.getBytes().length; final byte[] bytes = query.getBytes(); for (int j = 0; j < bytes.length / addrLength; j++) { arraycopy(bytes, j * addrLength, addresses[i++], 0, addrLength); } } return addresses; } private int getDataLength(Collection<EcuQuery> queries) { int dataLength = 0; for (EcuQuery query : queries) { dataLength += EcuQueryData.getDataLength(query); } return dataLength; } }