RomRaider/src/com/romraider/logger/ecu/comms/manager/QueryManagerImpl.java

327 lines
12 KiB
Java

/*
*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2008 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.manager;
import com.romraider.Settings;
import com.romraider.io.connection.EcuConnection;
import com.romraider.io.connection.EcuConnectionImpl;
import com.romraider.io.protocol.Protocol;
import com.romraider.io.protocol.ProtocolFactory;
import com.romraider.logger.ecu.comms.query.EcuInitCallback;
import com.romraider.logger.ecu.comms.query.EcuQuery;
import com.romraider.logger.ecu.comms.query.EcuQueryImpl;
import com.romraider.logger.ecu.comms.query.ExternalQuery;
import com.romraider.logger.ecu.comms.query.ExternalQueryImpl;
import com.romraider.logger.ecu.comms.query.Query;
import com.romraider.logger.ecu.comms.query.Response;
import com.romraider.logger.ecu.comms.query.ResponseImpl;
import com.romraider.logger.ecu.definition.EcuData;
import static com.romraider.logger.ecu.definition.EcuDataType.EXTERNAL;
import com.romraider.logger.ecu.definition.ExternalData;
import com.romraider.logger.ecu.definition.LoggerData;
import com.romraider.logger.ecu.ui.MessageListener;
import com.romraider.logger.ecu.ui.StatusChangeListener;
import com.romraider.logger.ecu.ui.handler.DataUpdateHandler;
import com.romraider.logger.ecu.ui.handler.file.FileLoggerControllerSwitchMonitor;
import static com.romraider.util.HexUtil.asHex;
import static com.romraider.util.ParamChecker.checkNotNull;
import static com.romraider.util.ThreadUtil.runAsDaemon;
import static com.romraider.util.ThreadUtil.sleep;
import org.apache.log4j.Logger;
import javax.swing.SwingUtilities;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import static java.util.Collections.synchronizedList;
import static java.util.Collections.synchronizedMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class QueryManagerImpl implements QueryManager {
private static final Logger LOGGER = Logger.getLogger(QueryManagerImpl.class);
private final DecimalFormat format = new DecimalFormat("0.00");
private final List<StatusChangeListener> listeners = synchronizedList(new ArrayList<StatusChangeListener>());
private final Map<String, Query> queryMap = synchronizedMap(new HashMap<String, Query>());
private final Map<String, Query> addList = new HashMap<String, Query>();
private final List<String> removeList = new ArrayList<String>();
private final Settings settings;
private final EcuInitCallback ecuInitCallback;
private final MessageListener messageListener;
private final DataUpdateHandler[] dataUpdateHandlers;
private FileLoggerControllerSwitchMonitor monitor;
private EcuQuery fileLoggerQuery;
private Thread queryManagerThread;
private boolean started;
private boolean stop;
public QueryManagerImpl(Settings settings, EcuInitCallback ecuInitCallback, MessageListener messageListener,
DataUpdateHandler... dataUpdateHandlers) {
checkNotNull(settings, ecuInitCallback, messageListener, dataUpdateHandlers);
this.settings = settings;
this.ecuInitCallback = ecuInitCallback;
this.messageListener = messageListener;
this.dataUpdateHandlers = dataUpdateHandlers;
}
public synchronized void addListener(StatusChangeListener listener) {
checkNotNull(listener, "listener");
listeners.add(listener);
}
public void setFileLoggerSwitchMonitor(FileLoggerControllerSwitchMonitor monitor) {
checkNotNull(monitor);
this.monitor = monitor;
fileLoggerQuery = new EcuQueryImpl(monitor.getEcuSwitch());
}
public synchronized void addQuery(String callerId, LoggerData loggerData) {
checkNotNull(callerId, loggerData);
//FIXME: This is a hack!!
String queryId = buildQueryId(callerId, loggerData);
if (loggerData.getDataType() == EXTERNAL) {
addList.put(queryId, new ExternalQueryImpl((ExternalData) loggerData));
} else {
addList.put(queryId, new EcuQueryImpl((EcuData) loggerData));
}
}
public synchronized void removeQuery(String callerId, LoggerData loggerData) {
checkNotNull(callerId, loggerData);
removeList.add(buildQueryId(callerId, loggerData));
}
public boolean isRunning() {
return started && !stop;
}
public void run() {
started = true;
queryManagerThread = Thread.currentThread();
LOGGER.debug("QueryManager started.");
try {
stop = false;
while (!stop) {
notifyConnecting();
if (doEcuInit()) {
notifyReading();
runLogger();
} else {
sleep(1000L);
}
}
} catch (Exception e) {
messageListener.reportError(e);
} finally {
notifyStopped();
messageListener.reportMessage("Disconnected.");
LOGGER.debug("QueryManager stopped.");
}
}
private boolean doEcuInit() {
try {
Protocol protocol = ProtocolFactory.getInstance().getProtocol(settings.getLoggerProtocol());
EcuConnection ecuConnection = new EcuConnectionImpl(settings.getLoggerConnectionProperties(), settings.getLoggerPort());
try {
messageListener.reportMessage("Sending ECU Init...");
byte[] request = protocol.constructEcuInitRequest();
LOGGER.debug("Ecu Init Request ---> " + asHex(request));
byte[] response = ecuConnection.send(request);
byte[] processedResponse = protocol.preprocessResponse(request, response);
protocol.checkValidEcuInitResponse(processedResponse);
LOGGER.debug("Ecu Init Response <--- " + asHex(processedResponse));
ecuInitCallback.callback(protocol.parseEcuInitResponse(processedResponse));
messageListener.reportMessage("Sending ECU Init...done.");
return true;
} finally {
ecuConnection.close();
}
} catch (Exception e) {
messageListener.reportMessage("Unable to send ECU init - check correct serial port has been selected, cable is connected and ignition is on.");
logError(e);
return false;
}
}
private void logError(Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Error sending ecu init", e);
} else {
LOGGER.info("Error sending ecu init: " + e.getMessage());
}
}
private void runLogger() {
TransmissionManager txManager = new TransmissionManagerImpl(settings);
long start = System.currentTimeMillis();
int count = 0;
try {
txManager.start();
while (!stop) {
updateQueryList();
if (queryMap.isEmpty()) {
start = System.currentTimeMillis();
count = 0;
messageListener.reportMessage("Select parameters to be logged...");
sleep(1000L);
} else {
sendEcuQueries(txManager);
sendExternalQueries();
handleQueryResponse();
count++;
messageListener.reportMessage("Querying ECU...");
messageListener.reportStats(buildStatsMessage(start, count));
}
}
} catch (Exception e) {
messageListener.reportError(e);
} finally {
txManager.stop();
}
}
private void sendEcuQueries(TransmissionManager txManager) {
List<EcuQuery> ecuQueries = filterEcuQueries(queryMap.values());
if (fileLoggerQuery != null) {
ecuQueries.add(fileLoggerQuery);
}
txManager.sendQueries(ecuQueries);
}
private void sendExternalQueries() {
List<ExternalQuery> externalQueries = filterExternalQueries(queryMap.values());
for (ExternalQuery externalQuery : externalQueries) {
//FIXME: This is a hack!!
externalQuery.setResponse(externalQuery.getLoggerData().getSelectedConvertor().convert(null));
}
}
private void handleQueryResponse() {
monitor.monitorFileLoggerSwitch(fileLoggerQuery.getResponse());
final Response response = buildResponse(queryMap.values());
for (final DataUpdateHandler dataUpdateHandler : dataUpdateHandlers) {
runAsDaemon(new Runnable() {
public void run() {
dataUpdateHandler.handleDataUpdate(response);
}
});
}
}
private Response buildResponse(Collection<Query> queries) {
Response response = new ResponseImpl();
for (Query query : queries) {
response.setDataValue(query.getLoggerData(), query.getResponse());
}
return response;
}
//FIXME: This is a hack!!
private List<EcuQuery> filterEcuQueries(Collection<Query> queries) {
List<EcuQuery> filtered = new ArrayList<EcuQuery>();
for (Query query : queries) {
if (EcuQuery.class.isAssignableFrom(query.getClass())) {
filtered.add((EcuQuery) query);
}
}
return filtered;
}
//FIXME: This is a hack!!
private List<ExternalQuery> filterExternalQueries(Collection<Query> queries) {
List<ExternalQuery> filtered = new ArrayList<ExternalQuery>();
for (Query query : queries) {
if (ExternalQuery.class.isAssignableFrom(query.getClass())) {
filtered.add((ExternalQuery) query);
}
}
return filtered;
}
public void stop() {
stop = true;
if (queryManagerThread != null) {
queryManagerThread.interrupt();
}
}
private String buildQueryId(String callerId, LoggerData loggerData) {
return callerId + "_" + loggerData.getName();
}
private synchronized void updateQueryList() {
addQueries();
removeQueries();
}
private void addQueries() {
for (String queryId : addList.keySet()) {
queryMap.put(queryId, addList.get(queryId));
}
addList.clear();
}
private void removeQueries() {
for (String queryId : removeList) {
queryMap.remove(queryId);
}
removeList.clear();
}
private String buildStatsMessage(long start, int count) {
double duration = ((double) (System.currentTimeMillis() - start)) / 1000.0;
return "[ " + format.format(((double) count) / duration) + " queries/sec, " + format.format(duration / ((double) count)) + " sec/query ]";
}
private void notifyConnecting() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (StatusChangeListener listener : listeners) {
listener.connecting();
}
}
});
}
private void notifyReading() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (StatusChangeListener listener : listeners) {
listener.readingData();
}
}
});
}
private void notifyStopped() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (StatusChangeListener listener : listeners) {
listener.stopped();
}
}
});
}
}