serial communication rewritten, support added for multiple address querying

git-svn-id: http://svn.3splooges.com/romraider-arch/trunk@202 d2e2e1cd-ba16-0410-be16-b7c4453c7c2d
This commit is contained in:
kascade 2006-08-10 13:18:46 +00:00
parent 0ee24d2e0b
commit aecf05fa39
21 changed files with 388 additions and 454 deletions

View File

@ -38,9 +38,10 @@ public class Settings implements Serializable {
private Color warningColor = new Color(255, 0, 0); private Color warningColor = new Color(255, 0, 0);
private int tableClickCount = 2; // number of clicks to open table private int tableClickCount = 2; // number of clicks to open table
private String loggerPort = "COM3"; private String loggerPort = "COM4";
private String loggerProtocol = "SSM";
public Settings() {
public Settings() {
//center window by default //center window by default
Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
windowLocation.move(((int)(screenSize.getWidth() - windowSize.getWidth()) / 2), windowLocation.move(((int)(screenSize.getWidth() - windowSize.getWidth()) / 2),
@ -64,7 +65,7 @@ public class Settings implements Serializable {
} }
public Vector<File> getEcuDefinitionFiles() { public Vector<File> getEcuDefinitionFiles() {
if (ecuDefinitionFiles.size() == 0) { if (ecuDefinitionFiles.isEmpty()) {
// if no files defined, add default // if no files defined, add default
ecuDefinitionFiles.add(new File("./ecu_defs.xml")); ecuDefinitionFiles.add(new File("./ecu_defs.xml"));
} }
@ -204,9 +205,13 @@ public class Settings implements Serializable {
} }
public void setUserLevel(int userLevel) { public void setUserLevel(int userLevel) {
this.userLevel = userLevel; if (userLevel > 5) {
if (userLevel > 5) userLevel = 5; this.userLevel = 5;
else if (userLevel < 1) userLevel = 1; } else if (userLevel < 1) {
this.userLevel = 1;
} else {
this.userLevel = userLevel;
}
} }
public int getTableClickCount() { public int getTableClickCount() {
@ -268,4 +273,8 @@ public class Settings implements Serializable {
public void setLoggerPort(String loggerPort) { public void setLoggerPort(String loggerPort) {
this.loggerPort = loggerPort; this.loggerPort = loggerPort;
} }
public String getLoggerProtocol() {
return loggerProtocol;
}
} }

View File

@ -7,8 +7,6 @@ import enginuity.logger.manager.DefaultQueryManager;
import enginuity.logger.manager.DefaultTransmissionManager; import enginuity.logger.manager.DefaultTransmissionManager;
import enginuity.logger.manager.QueryManager; import enginuity.logger.manager.QueryManager;
import enginuity.logger.manager.TransmissionManager; import enginuity.logger.manager.TransmissionManager;
import enginuity.logger.protocol.Protocol;
import enginuity.logger.protocol.ProtocolFactory;
import enginuity.logger.query.DefaultRegisteredQuery; import enginuity.logger.query.DefaultRegisteredQuery;
import enginuity.logger.query.LoggerCallback; import enginuity.logger.query.LoggerCallback;
import static enginuity.util.ParamChecker.checkNotNull; import static enginuity.util.ParamChecker.checkNotNull;
@ -21,8 +19,7 @@ public final class DefaultLoggerController implements LoggerController {
private final QueryManager queryManager; private final QueryManager queryManager;
public DefaultLoggerController(Settings settings) { public DefaultLoggerController(Settings settings) {
Protocol ssmProtocol = ProtocolFactory.getInstance().getProtocol("SSM"); TransmissionManager txManager = new DefaultTransmissionManager(settings);
TransmissionManager txManager = new DefaultTransmissionManager(settings, ssmProtocol);
queryManager = new DefaultQueryManager(txManager); queryManager = new DefaultQueryManager(txManager);
} }

View File

@ -1,6 +1,8 @@
package enginuity.logger; package enginuity.logger;
import enginuity.Settings; import enginuity.Settings;
import enginuity.logger.query.LoggerCallback;
import static enginuity.util.HexUtil.asHex;
import javax.swing.*; import javax.swing.*;
import static javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED; import static javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED;
@ -19,6 +21,8 @@ import java.util.List;
public final class EcuLogger extends JFrame implements WindowListener, PropertyChangeListener { public final class EcuLogger extends JFrame implements WindowListener, PropertyChangeListener {
private final Settings settings = new Settings(); private final Settings settings = new Settings();
private final LoggerController CONTROLLER = new DefaultLoggerController(settings); private final LoggerController CONTROLLER = new DefaultLoggerController(settings);
private final JTable dataTable = new JTable();
private final JTextArea dataTextArea = new JTextArea();
private final JComboBox portsComboBox = new JComboBox(); private final JComboBox portsComboBox = new JComboBox();
public EcuLogger(String title) { public EcuLogger(String title) {
@ -34,6 +38,14 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC
//add to container //add to container
getContentPane().add(splitPane); getContentPane().add(splitPane);
// add dummy address to log (0x000008 = coolant temp)
CONTROLLER.addLogger("0x000008", new LoggerCallback() {
public void callback(byte[] value) {
dataTextArea.append("0x000008 => " + asHex(value));
dataTextArea.append("\n");
}
});
} }
public static void main(String... args) { public static void main(String... args) {
@ -95,19 +107,30 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC
} }
private JPanel buildControlToolbar() { private JPanel buildControlToolbar() {
refreshPortsComboBox();
JPanel controlPanel = new JPanel(new FlowLayout()); JPanel controlPanel = new JPanel(new FlowLayout());
controlPanel.add(buildStartButton()); controlPanel.add(buildStartButton());
controlPanel.add(buildStopButton()); controlPanel.add(buildStopButton());
controlPanel.add(portsComboBox); controlPanel.add(buildPortsComboBox());
return controlPanel; return controlPanel;
} }
private JComboBox buildPortsComboBox() {
refreshPortsComboBox();
portsComboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
settings.setLoggerPort((String) portsComboBox.getSelectedItem());
CONTROLLER.stop();
}
});
return portsComboBox;
}
private void refreshPortsComboBox() { private void refreshPortsComboBox() {
List<String> ports = CONTROLLER.listSerialPorts(); List<String> ports = CONTROLLER.listSerialPorts();
for (String port : ports) { for (String port : ports) {
portsComboBox.addItem(port); portsComboBox.addItem(port);
} }
settings.setLoggerPort((String) portsComboBox.getSelectedItem());
} }
private JButton buildStartButton() { private JButton buildStartButton() {
@ -140,8 +163,8 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC
} }
private JComponent buildDataTab() { private JComponent buildDataTab() {
JTable dataTable = new JTable(); //return new JScrollPane(dataTable, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER);
return new JScrollPane(dataTable, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER); return new JScrollPane(dataTextArea, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER);
} }
private JComponent buildGraphTab() { private JComponent buildGraphTab() {

View File

@ -1,30 +1,16 @@
package enginuity.logger; package enginuity.logger;
import enginuity.Settings; import enginuity.Settings;
import enginuity.logger.comms.DefaultSerialPortDiscoverer;
import enginuity.logger.comms.DefaultTwoWaySerialComm;
import enginuity.logger.comms.SerialConnection;
import enginuity.logger.comms.SerialPortDiscoverer;
import enginuity.logger.comms.TwoWaySerialComm;
import enginuity.logger.query.DefaultQuery;
import enginuity.logger.query.LoggerCallback; import enginuity.logger.query.LoggerCallback;
import enginuity.util.HexUtil; import enginuity.util.HexUtil;
import gnu.io.CommPortIdentifier;
import static gnu.io.SerialPort.DATABITS_8;
import static gnu.io.SerialPort.PARITY_NONE;
import static gnu.io.SerialPort.STOPBITS_1;
import java.util.List;
public final class TestEcuLogger { public final class TestEcuLogger {
private static final int CONNECT_TIMEOUT = 2000;
private TestEcuLogger() { private TestEcuLogger() {
} }
public static void main(String... args) { public static void main(String... args) {
try { try {
//testTwoWaySerialComm();
testLoggerController(); testLoggerController();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -38,7 +24,7 @@ public final class TestEcuLogger {
addLogger(controller, "0x291C8"); addLogger(controller, "0x291C8");
addLogger(controller, "0x291F9"); addLogger(controller, "0x291F9");
addLogger(controller, "0x286BB"); addLogger(controller, "0x286BB");
sleep(1000); sleep(60000);
controller.removeLogger("0x291F9"); controller.removeLogger("0x291F9");
controller.removeLogger("0x291C8"); controller.removeLogger("0x291C8");
controller.removeLogger("0x286BB"); controller.removeLogger("0x286BB");
@ -58,28 +44,6 @@ public final class TestEcuLogger {
}); });
} }
private static void testTwoWaySerialComm() {
SerialPortDiscoverer serialPortDiscoverer = new DefaultSerialPortDiscoverer();
List<CommPortIdentifier> portIdentifiers = serialPortDiscoverer.listPorts();
for (CommPortIdentifier commPortIdentifier : portIdentifiers) {
TwoWaySerialComm twoWaySerialComm = new DefaultTwoWaySerialComm();
try {
SerialConnection serialConnection = twoWaySerialComm.connect(commPortIdentifier.getName(), 4800, DATABITS_8,
STOPBITS_1, PARITY_NONE, CONNECT_TIMEOUT);
executeQuery(serialConnection, "FooBar");
executeQuery(serialConnection, "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789");
executeQuery(serialConnection, "HAZAA!!");
} finally {
twoWaySerialComm.disconnect();
}
}
}
private static void executeQuery(SerialConnection serialConnection, String queryString) {
byte[] response = serialConnection.transmit(new DefaultQuery(queryString.getBytes()));
printResponse(response);
}
private static void printResponse(byte[] value) { private static void printResponse(byte[] value) {
System.out.println("Response: " + HexUtil.asHex(value)); System.out.println("Response: " + HexUtil.asHex(value));
} }

View File

@ -1,31 +1,194 @@
package enginuity.logger.comms; package enginuity.logger.comms;
import enginuity.logger.query.Query; import enginuity.logger.exception.NotConnectedException;
import enginuity.util.ParamChecker; import enginuity.logger.exception.PortNotFoundException;
import enginuity.logger.exception.SerialCommunicationException;
import enginuity.logger.exception.UnsupportedPortTypeException;
import enginuity.logger.protocol.ConnectionProperties;
import enginuity.logger.protocol.Protocol;
import enginuity.logger.query.RegisteredQuery;
import static enginuity.util.HexUtil.asHex;
import static enginuity.util.ParamChecker.checkGreaterThanZero;
import static enginuity.util.ParamChecker.checkNotNull;
import static enginuity.util.ParamChecker.checkNotNullOrEmpty;
import gnu.io.CommPortIdentifier;
import static gnu.io.CommPortIdentifier.PORT_SERIAL;
import static gnu.io.CommPortIdentifier.getPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import static gnu.io.SerialPort.FLOWCONTROL_NONE;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
@SuppressWarnings({"ResultOfMethodCallIgnored"})
public final class DefaultSerialConnection implements SerialConnection { public final class DefaultSerialConnection implements SerialConnection {
private SerialWriter serialWriter; private Protocol protocol;
private SerialReader serialReader; private SerialPort serialPort;
private OutputStream os;
private InputStream is;
public DefaultSerialConnection(SerialWriter serialWriter, SerialReader serialReader) { public DefaultSerialConnection(Protocol protocol, String portName, int connectTimeout) {
this.serialWriter = serialWriter; checkNotNull(protocol, "protocol");
this.serialReader = serialReader; checkNotNullOrEmpty(portName, "portName");
checkGreaterThanZero(connectTimeout, "connectTimeout");
this.protocol = protocol;
ConnectionProperties connectionProperties = protocol.getConnectionProperties();
serialPort = resolveSerialPort(portName, connectionProperties.getBaudRate(), connectionProperties.getDataBits(),
connectionProperties.getStopBits(), connectionProperties.getParity(), connectTimeout);
initStreams();
} }
public byte[] transmit(Query query) { public byte[] sendEcuInit() {
ParamChecker.checkNotNull(query, "query"); return send(protocol.constructEcuInitRequest());
return query.execute(serialWriter, serialReader); }
public byte[] send(byte[] bytes) {
try {
readStaleData();
serialPort.setRTS(false);
os.write(bytes, 0, bytes.length);
os.flush();
boolean keepLooking = true;
int available = 0;
long lastChange = System.currentTimeMillis();
while (keepLooking) {
TimeUnit.MILLISECONDS.sleep(5);
if (is.available() != available) {
available = is.available();
lastChange = System.currentTimeMillis();
}
keepLooking = ((System.currentTimeMillis() - lastChange) < 55);
}
byte[] response = new byte[is.available()];
is.read(response, 0, response.length);
return response;
} catch (Exception e) {
close();
throw new SerialCommunicationException(e);
}
}
public void sendAddressReads(Collection<RegisteredQuery> queries) {
try {
byte[] request = protocol.constructReadAddressRequest(queries);
byte[] response = protocol.constructReadAddressResponse(queries);
System.out.println("Raw request = " + asHex(request));
readStaleData();
serialPort.setRTS(false);
os.write(request, 0, request.length);
os.flush();
int timeout = 55;
while(is.available() < response.length) {
TimeUnit.MILLISECONDS.sleep(5);
timeout -= 5;
if(timeout <= 0) {
byte[] badBytes = new byte[is.available()];
is.read(badBytes, 0, badBytes.length);
System.out.println("Bad response (read timeout): " + asHex(badBytes));
break;
}
}
is.read(response, 0, response.length);
System.out.println("Raw response = " + asHex(response));
protocol.setResponse(queries, response);
//markTime();
} catch (Exception e) {
close();
throw new SerialCommunicationException(e);
}
}
private void readStaleData() throws IOException {
if (is.available() > 0) {
byte[] staleBytes = new byte[is.available()];
is.read(staleBytes, 0, is.available());
System.out.println("Stale data read: " + asHex(staleBytes));
}
} }
public void close() { public void close() {
try { if (os != null) {
try { try {
serialWriter.close(); os.close();
} finally { } catch (IOException e) {
serialReader.close(); e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serialPort != null) {
try {
serialPort.close();
} catch (Exception e) {
e.printStackTrace();
} }
} catch (Exception e) {
e.printStackTrace();
} }
} }
private void initStreams() {
try {
os = serialPort.getOutputStream();
is = serialPort.getInputStream();
} catch (IOException e) {
close();
throw new NotConnectedException(e);
}
}
private SerialPort resolveSerialPort(String portName, int baudrate, int dataBits, int stopBits, int parity, int connectTimeout) {
CommPortIdentifier portIdentifier = resolvePortIdentifier(portName);
SerialPort serialPort = openPort(portIdentifier, connectTimeout);
initSerialPort(serialPort, baudrate, dataBits, stopBits, parity);
return serialPort;
}
private CommPortIdentifier resolvePortIdentifier(String portName) {
try {
return getPortIdentifier(portName);
} catch (NoSuchPortException e) {
throw new PortNotFoundException(e);
}
}
private SerialPort openPort(CommPortIdentifier portIdentifier, int connectTimeout) {
checkIsSerialPort(portIdentifier);
try {
return (SerialPort) portIdentifier.open(this.getClass().getName(), connectTimeout);
} catch (PortInUseException e) {
throw new SerialCommunicationException("Port is currently in use: " + portIdentifier.getName());
}
}
private void checkIsSerialPort(CommPortIdentifier portIdentifier) {
if (portIdentifier.getPortType() != PORT_SERIAL) {
throw new UnsupportedPortTypeException("Port type " + portIdentifier.getPortType() + " not supported - must be serial.");
}
}
private void initSerialPort(SerialPort serialPort, int baudrate, int dataBits, int stopBits, int parity) {
try {
serialPort.setFlowControlMode(FLOWCONTROL_NONE);
serialPort.setSerialPortParams(baudrate, dataBits, stopBits, parity);
serialPort.disableReceiveTimeout();
} catch (UnsupportedCommOperationException e) {
throw new UnsupportedOperationException(e);
}
}
} }

View File

@ -1,80 +0,0 @@
package enginuity.logger.comms;
import enginuity.logger.exception.SerialCommunicationException;
import static enginuity.util.ParamChecker.checkNotNull;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import static gnu.io.SerialPortEvent.DATA_AVAILABLE;
import gnu.io.SerialPortEventListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.TooManyListenersException;
public final class DefaultSerialReader implements SerialReader, SerialPortEventListener {
private static final int BUFFER_SIZE = 32;
private final SerialPort port;
private boolean stop = false;
private byte[] readBuffer = new byte[0];
public DefaultSerialReader(SerialPort port) {
checkNotNull(port, "port");
this.port = port;
initListener();
}
public void run() {
System.out.println("SerialReader thread started.");
while (!stop) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("SerialReader thread stopped.");
}
public byte[] read() {
byte[] tmp = new byte[readBuffer.length];
System.arraycopy(readBuffer, 0, tmp, 0, readBuffer.length);
readBuffer = new byte[0];
return tmp;
}
public void close() {
stop = true;
}
@SuppressWarnings({"ResultOfMethodCallIgnored"})
public void serialEvent(SerialPortEvent serialPortEvent) {
checkNotNull(serialPortEvent, "serialPortEvent");
if (serialPortEvent.getEventType() == DATA_AVAILABLE) {
readBuffer = new byte[0];
try {
InputStream is = port.getInputStream();
while (is.available() > 0) {
byte[] tmp = new byte[BUFFER_SIZE];
int i = is.read(tmp);
byte[] tmp2 = new byte[readBuffer.length + i];
System.arraycopy(readBuffer, 0, tmp2, 0, readBuffer.length);
System.arraycopy(tmp, 0, tmp2, readBuffer.length, i);
readBuffer = tmp2;
}
} catch (IOException e) {
throw new SerialCommunicationException(e);
}
}
}
private void initListener() {
try {
port.addEventListener(this);
} catch (TooManyListenersException e) {
throw new SerialCommunicationException(e);
}
port.notifyOnDataAvailable(true);
}
}

View File

@ -1,42 +0,0 @@
package enginuity.logger.comms;
import enginuity.logger.exception.SerialCommunicationException;
import static enginuity.util.ParamChecker.checkNotNull;
import gnu.io.SerialPort;
import java.io.IOException;
public final class DefaultSerialWriter implements SerialWriter {
private final SerialPort port;
private boolean stop = false;
public DefaultSerialWriter(SerialPort port) {
checkNotNull(port, "port");
this.port = port;
}
public void run() {
System.out.println("SerialWriter thread started.");
while (!stop) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("SerialWriter thread stopped.");
}
public void write(byte[] bytes) {
try {
port.getOutputStream().write(bytes);
} catch (IOException e) {
throw new SerialCommunicationException(e);
}
}
public void close() {
stop = true;
}
}

View File

@ -1,134 +0,0 @@
package enginuity.logger.comms;
import enginuity.logger.exception.PortNotFoundException;
import enginuity.logger.exception.SerialCommunicationException;
import enginuity.logger.exception.UnsupportedPortTypeException;
import static enginuity.util.ParamChecker.checkGreaterThanZero;
import static enginuity.util.ParamChecker.checkNotNull;
import gnu.io.CommPortIdentifier;
import static gnu.io.CommPortIdentifier.PORT_SERIAL;
import static gnu.io.CommPortIdentifier.getPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import static gnu.io.SerialPort.FLOWCONTROL_NONE;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public final class DefaultTwoWaySerialComm implements TwoWaySerialComm {
private SerialPort serialPort;
private OutputStream os;
private InputStream is;
private SerialConnection serialConnection;
public SerialConnection connect(String portName, int baudrate, int dataBits, int stopBits, int parity, int connectTimeout) {
checkConnectParams(portName, baudrate, dataBits, stopBits, connectTimeout);
try {
serialPort = resolveSerialPort(portName, connectTimeout);
initSerialPort(serialPort, baudrate, dataBits, stopBits, parity);
resolveIoStreams(serialPort);
serialConnection = doConnect(serialPort);
System.out.println("Connected to " + portName + ".");
return serialConnection;
} catch (Throwable e) {
disconnect();
throw new SerialCommunicationException("Unable to connect to port " + portName + " with following config - [baudrate: "
+ baudrate + "bps, dataBits: " + dataBits + ", stopBits: " + stopBits + ", parity: " + parity + ", connectTimeout: "
+ connectTimeout + "ms]", e);
}
}
public void disconnect() {
if (serialConnection != null) {
serialConnection.close();
}
if (serialPort != null) {
cleanupIoStreams();
serialPort.close();
}
System.out.println("Disconnected.");
}
private void checkConnectParams(String portName, int baudrate, int dataBits, int stopBits, int connectTimeout) {
checkNotNull(portName, "portName");
checkGreaterThanZero(baudrate, "baudrate");
checkGreaterThanZero(dataBits, "dataBits");
checkGreaterThanZero(stopBits, "stopBits");
checkGreaterThanZero(connectTimeout, "connectTimeout");
}
private SerialPort resolveSerialPort(String portName, int connectTimeout) {
CommPortIdentifier portIdentifier = resolvePortIdentifier(portName);
return openPort(portIdentifier, connectTimeout);
}
private void initSerialPort(SerialPort serialPort, int baudrate, int dataBits, int stopBits, int parity) {
try {
serialPort.setSerialPortParams(baudrate, dataBits, stopBits, parity);
serialPort.setFlowControlMode(FLOWCONTROL_NONE);
} catch (UnsupportedCommOperationException e) {
throw new UnsupportedOperationException(e);
}
}
private void resolveIoStreams(SerialPort serialPort) {
try {
os = serialPort.getOutputStream();
is = serialPort.getInputStream();
} catch (IOException e) {
cleanupIoStreams();
throw new SerialCommunicationException(e);
}
}
private void cleanupIoStreams() {
try {
try {
if (os != null) {
os.close();
}
} finally {
if (is != null) {
is.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private SerialConnection doConnect(SerialPort serialPort) {
SerialReader serialReader = new DefaultSerialReader(serialPort);
SerialWriter serialWriter = new DefaultSerialWriter(serialPort);
(new Thread(serialReader)).start();
(new Thread(serialWriter)).start();
return new DefaultSerialConnection(serialWriter, serialReader);
}
private CommPortIdentifier resolvePortIdentifier(String portName) {
try {
return getPortIdentifier(portName);
} catch (NoSuchPortException e) {
throw new PortNotFoundException(e);
}
}
private void checkIsSerialPort(CommPortIdentifier portIdentifier) {
if (portIdentifier.getPortType() != PORT_SERIAL) {
throw new UnsupportedPortTypeException("Port type " + portIdentifier.getPortType() + " not supported - must be serial.");
}
}
private SerialPort openPort(CommPortIdentifier portIdentifier, int connectTimeout) {
checkIsSerialPort(portIdentifier);
try {
return (SerialPort) portIdentifier.open(this.getClass().getName(), connectTimeout);
} catch (PortInUseException e) {
throw new SerialCommunicationException("Port is currently in use: " + portIdentifier.getName());
}
}
}

View File

@ -1,10 +1,16 @@
package enginuity.logger.comms; package enginuity.logger.comms;
import enginuity.logger.query.Query; import enginuity.logger.query.RegisteredQuery;
import java.util.Collection;
public interface SerialConnection { public interface SerialConnection {
byte[] transmit(Query query); byte[] sendEcuInit();
byte[] send(byte[] bytes);
void sendAddressReads(Collection<RegisteredQuery> queries);
void close(); void close();

View File

@ -1,9 +0,0 @@
package enginuity.logger.comms;
public interface SerialReader extends Runnable {
byte[] read();
void close();
}

View File

@ -1,9 +0,0 @@
package enginuity.logger.comms;
public interface SerialWriter extends Runnable {
void write(byte[] bytes);
void close();
}

View File

@ -1,9 +0,0 @@
package enginuity.logger.comms;
public interface TwoWaySerialComm {
SerialConnection connect(String portName, int baudrate, int dataBits, int stopBits, int parity, int connectTimeout);
void disconnect();
}

View File

@ -9,6 +9,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
//TODO: Allow transmission of multiple query objects at a time
public final class DefaultQueryManager implements QueryManager { public final class DefaultQueryManager implements QueryManager {
private final Map<String, RegisteredQuery> queryMap = Collections.synchronizedMap(new HashMap<String, RegisteredQuery>()); private final Map<String, RegisteredQuery> queryMap = Collections.synchronizedMap(new HashMap<String, RegisteredQuery>());
private final List<RegisteredQuery> addList = new ArrayList<RegisteredQuery>(); private final List<RegisteredQuery> addList = new ArrayList<RegisteredQuery>();
@ -34,7 +36,7 @@ public final class DefaultQueryManager implements QueryManager {
public void run() { public void run() {
System.out.println("QueryManager started."); System.out.println("QueryManager started.");
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
int count = 0; int count = 0;
@ -43,12 +45,8 @@ public final class DefaultQueryManager implements QueryManager {
stop = false; stop = false;
while (!stop) { while (!stop) {
updateQueryList(); updateQueryList();
for (String address : queryMap.keySet()) { txManager.sendQueries(queryMap.values());
RegisteredQuery registeredQuery = queryMap.get(address); count++;
byte[] response = txManager.queryAddress(registeredQuery.getBytes());
registeredQuery.setResponse(response);
count++;
}
} }
} finally { } finally {
txManager.stop(); txManager.stop();

View File

@ -1,51 +1,81 @@
package enginuity.logger.manager; package enginuity.logger.manager;
import enginuity.Settings; import enginuity.Settings;
import enginuity.logger.comms.DefaultTwoWaySerialComm; import enginuity.logger.comms.DefaultSerialConnection;
import enginuity.logger.comms.SerialConnection; import enginuity.logger.comms.SerialConnection;
import enginuity.logger.comms.TwoWaySerialComm;
import enginuity.logger.exception.NotConnectedException; import enginuity.logger.exception.NotConnectedException;
import enginuity.logger.protocol.ConnectionProperties; import enginuity.logger.exception.SerialCommunicationException;
import enginuity.logger.protocol.Protocol; import enginuity.logger.protocol.Protocol;
import enginuity.logger.query.DefaultQuery; import enginuity.logger.protocol.ProtocolFactory;
import enginuity.logger.query.LoggerCallback;
import enginuity.logger.query.RegisteredQuery;
import enginuity.util.HexUtil;
import static enginuity.util.ParamChecker.checkNotNull; import static enginuity.util.ParamChecker.checkNotNull;
import static enginuity.util.ParamChecker.checkNotNullOrEmpty;
import java.util.Collection;
//TODO: Make a ssm protocol package containing specific commands. SSM Protocol then returns instances of the command interfaces.
//TODO: Break protocol up into commands which are passed to serial connection. each command has buildQuery() and responseSize() methods which construct the request and indicate the expected response size.
//TODO: Let the serial connection build and execute the commands. the response should be set back on the command.
public final class DefaultTransmissionManager implements TransmissionManager { public final class DefaultTransmissionManager implements TransmissionManager {
private static final int CONNECT_TIMEOUT = 2000; private static final int CONNECT_TIMEOUT = 2000;
private final Settings settings; private final Settings settings;
private final Protocol protocol;
private final ConnectionProperties connectionProperties;
private final TwoWaySerialComm twoWaySerialComm = new DefaultTwoWaySerialComm();
private SerialConnection serialConnection = null; private SerialConnection serialConnection = null;
public DefaultTransmissionManager(Settings settings, Protocol protocol) { public DefaultTransmissionManager(Settings settings) {
checkNotNull(settings, protocol, protocol.getConnectionProperties()); checkNotNull(settings, "settings");
this.settings = settings; this.settings = settings;
this.protocol = protocol;
this.connectionProperties = protocol.getConnectionProperties();
} }
public void start() { public void start() {
serialConnection = twoWaySerialComm.connect(settings.getLoggerPort(), connectionProperties.getBaudRate(), connectionProperties.getDataBits(), try {
connectionProperties.getStopBits(), connectionProperties.getParity(), CONNECT_TIMEOUT); Protocol protocol = ProtocolFactory.getInstance().getProtocol(settings.getLoggerProtocol());
serialConnection = new DefaultSerialConnection(protocol, settings.getLoggerPort(), CONNECT_TIMEOUT);
System.out.println("Connected to: " + settings.getLoggerPort() + "; using protocol: " + settings.getLoggerProtocol());
} catch (Throwable e) {
stop();
throw new SerialCommunicationException("Unable to connect to port: " + settings.getLoggerPort() + ", with protocol: "
+ settings.getLoggerProtocol() + ", and connection timeout: " + CONNECT_TIMEOUT + "ms", e);
}
} }
public byte[] queryAddress(byte[] address) { public void sendEcuInit(LoggerCallback callback) {
checkNotNullOrEmpty(address, "address");
if (serialConnection != null) { if (serialConnection != null) {
byte[] request = protocol.constructReadAddressRequest(address); callback.callback(serialConnection.sendEcuInit());
byte[] response = serialConnection.transmit(new DefaultQuery(request));
//TODO: Enable this once extractResponseData() complete
//return protocol.extractResponseData(response, request);
return response;
} else { } else {
throw new NotConnectedException("TransmissionManager must be started before a query can be sent!"); throw new NotConnectedException("TransmissionManager must be started before a query can be sent!");
} }
} }
public void sendQueries(Collection<RegisteredQuery> queries) {
checkNotNull(queries, "queries");
if (serialConnection != null) {
serialConnection.sendAddressReads(queries);
} else {
throw new NotConnectedException("TransmissionManager must be started before a queries can be sent!");
}
}
public void stop() { public void stop() {
twoWaySerialComm.disconnect(); if (serialConnection != null) {
serialConnection.close();
}
System.out.println("Disconnected.");
}
public static void main(String... args) {
TransmissionManager txManager = new DefaultTransmissionManager(new Settings());
try {
txManager.start();
txManager.sendEcuInit(new LoggerCallback() {
public void callback(byte[] value) {
System.out.println("ECU Init Response = " + HexUtil.asHex(value));
}
});
} finally {
txManager.stop();
}
} }
} }

View File

@ -1,10 +1,17 @@
package enginuity.logger.manager; package enginuity.logger.manager;
import enginuity.logger.query.LoggerCallback;
import enginuity.logger.query.RegisteredQuery;
import java.util.Collection;
public interface TransmissionManager { public interface TransmissionManager {
void start(); void start();
byte[] queryAddress(byte[] query); void sendEcuInit(LoggerCallback callback);
void sendQueries(Collection<RegisteredQuery> query);
void stop(); void stop();

View File

@ -1,14 +1,22 @@
package enginuity.logger.protocol; package enginuity.logger.protocol;
import enginuity.logger.query.RegisteredQuery;
import java.util.Collection;
public interface Protocol { public interface Protocol {
byte[] constructReadMemoryRequest(byte[] fromAddress, int numBytes);
byte[] constructReadAddressRequest(byte[]... addresses);
byte[] constructEcuInitRequest(); byte[] constructEcuInitRequest();
byte[] extractResponseData(byte[] response, byte[] request); byte[] constructReadMemoryRequest(RegisteredQuery query, int numBytes);
byte[] constructReadAddressRequest(Collection<RegisteredQuery> queries);
byte[] constructReadAddressResponse(Collection<RegisteredQuery> queries);
void setResponse(Collection<RegisteredQuery> queries, byte[] response);
byte[] extractResponseData(byte[] response);
ConnectionProperties getConnectionProperties(); ConnectionProperties getConnectionProperties();

View File

@ -1,10 +1,17 @@
package enginuity.logger.protocol; package enginuity.logger.protocol;
import enginuity.logger.query.DefaultRegisteredQuery;
import enginuity.logger.query.LoggerCallback;
import enginuity.logger.query.RegisteredQuery;
import static enginuity.util.HexUtil.asBytes; import static enginuity.util.HexUtil.asBytes;
import static enginuity.util.HexUtil.asHex; import static enginuity.util.HexUtil.asHex;
import static enginuity.util.ParamChecker.checkGreaterThanZero; import static enginuity.util.ParamChecker.checkGreaterThanZero;
import static enginuity.util.ParamChecker.checkNotNull;
import static enginuity.util.ParamChecker.checkNotNullOrEmpty; import static enginuity.util.ParamChecker.checkNotNullOrEmpty;
import java.util.ArrayList;
import java.util.Collection;
public final class SSMProtocol implements Protocol { public final class SSMProtocol implements Protocol {
private static final byte HEADER = (byte) 0x80; private static final byte HEADER = (byte) 0x80;
private static final byte ECU_ID = (byte) 0x10; private static final byte ECU_ID = (byte) 0x10;
@ -13,19 +20,40 @@ public final class SSMProtocol implements Protocol {
private static final byte READ_MEMORY_COMMAND = (byte) 0xA0; private static final byte READ_MEMORY_COMMAND = (byte) 0xA0;
private static final byte READ_ADDRESS_COMMAND = (byte) 0xA8; private static final byte READ_ADDRESS_COMMAND = (byte) 0xA8;
private static final byte ECU_INIT_COMMAND = (byte) 0xBF; private static final byte ECU_INIT_COMMAND = (byte) 0xBF;
private static final int ADDRESS_SIZE = 3;
private static final int DATA_SIZE = 1;
public byte[] constructReadMemoryRequest(byte[] fromAddress, int numBytes) { public byte[] constructReadMemoryRequest(RegisteredQuery query, int numBytes) {
checkNotNullOrEmpty(fromAddress, "fromAddress"); checkNotNull(query, "query");
checkGreaterThanZero(numBytes, "numBytes"); checkGreaterThanZero(numBytes, "numBytes");
// 0x80 0x10 0xF0 data_length 0xA0 padding from_address num_bytes-1 checksum // 0x80 0x10 0xF0 data_length 0xA0 padding from_address num_bytes-1 checksum
return buildRequest(READ_MEMORY_COMMAND, true, fromAddress, new byte[]{asByte(numBytes - 1)}); return buildRequest(READ_MEMORY_COMMAND, true, query.getBytes(), new byte[]{asByte(numBytes - 1)});
} }
public byte[] constructReadAddressRequest(byte[]... addresses) { public byte[] constructReadAddressRequest(Collection<RegisteredQuery> queries) {
checkNotNullOrEmpty(addresses, "addresses"); checkNotNullOrEmpty(queries, "queries");
// 0x80 0x10 0xF0 data_length 0xA8 padding address1 address2 ... addressN checksum // 0x80 0x10 0xF0 data_length 0xA8 padding address1 address2 ... addressN checksum
return buildRequest(READ_ADDRESS_COMMAND, true, addresses); return buildRequest(READ_ADDRESS_COMMAND, true, convertToByteAddresses(queries));
}
//TODO: Query responses are variable length!!
public byte[] constructReadAddressResponse(Collection<RegisteredQuery> queries) {
checkNotNullOrEmpty(queries, "queries");
return new byte[DATA_SIZE * queries.size() + 5];
}
public void setResponse(Collection<RegisteredQuery> queries, byte[] response) {
checkNotNullOrEmpty(queries, "queries");
checkNotNullOrEmpty(response, "response");
byte[] responseData = extractResponseData(response);
int i = 0;
for (RegisteredQuery query : queries) {
byte[] bytes = new byte[DATA_SIZE];
System.arraycopy(responseData, i, bytes, 0, DATA_SIZE);
query.setResponse(bytes);
i += DATA_SIZE;
}
} }
public byte[] constructEcuInitRequest() { public byte[] constructEcuInitRequest() {
@ -33,7 +61,7 @@ public final class SSMProtocol implements Protocol {
return buildRequest(ECU_INIT_COMMAND, false, new byte[0]); return buildRequest(ECU_INIT_COMMAND, false, new byte[0]);
} }
public byte[] extractResponseData(byte[] response, byte[] request) { public byte[] extractResponseData(byte[] response) {
checkNotNullOrEmpty(response, "response"); checkNotNullOrEmpty(response, "response");
// 0x80 0xF0 0x10 data_length response_data checksum // 0x80 0xF0 0x10 data_length response_data checksum
//TODO: Take possible echoed request into account when extracting response!! //TODO: Take possible echoed request into account when extracting response!!
@ -64,19 +92,29 @@ public final class SSMProtocol implements Protocol {
}; };
} }
private byte[][] convertToByteAddresses(Collection<RegisteredQuery> queries) {
byte[][] addresses = new byte[queries.size()][ADDRESS_SIZE];
int i = 0;
for (RegisteredQuery query : queries) {
byte[] bytes = query.getBytes();
System.arraycopy(bytes, 0, addresses[i++], 0, ADDRESS_SIZE);
}
return addresses;
}
private void validateResponse(byte[] response) { private void validateResponse(byte[] response) {
int i = 0; int i = 0;
assertEquals(HEADER, response[i++]); assertEquals(HEADER, response[i++], "Invalid header");
assertEquals(DIAGNOSTIC_TOOL_ID, response[i++]); assertEquals(DIAGNOSTIC_TOOL_ID, response[i++], "Invalid diagnostic tool id");
assertEquals(ECU_ID, response[i++]); assertEquals(ECU_ID, response[i++], "Invalid ECU id");
assertEquals(asByte(response.length - 5), response[i]); assertEquals(asByte(response.length - 5), response[i], "Invalid response data length");
assertEquals(calculateChecksum(response), response[response.length - 1]); assertEquals(calculateChecksum(response), response[response.length - 1], "Invalid checksum");
} }
private void assertEquals(byte expected, byte actual) { private void assertEquals(byte expected, byte actual, String msg) {
if (actual != expected) { if (actual != expected) {
throw new InvalidResponseException(); throw new InvalidResponseException(msg + ". Expected: " + asHex(new byte[]{expected}) + ". Actual: " + asHex(new byte[]{actual}) + ".");
} }
} }
@ -141,14 +179,22 @@ public final class SSMProtocol implements Protocol {
byte[] bytes = protocol.constructEcuInitRequest(); byte[] bytes = protocol.constructEcuInitRequest();
System.out.println("Ecu Init = " + asHex(bytes)); System.out.println("Ecu Init = " + asHex(bytes));
bytes = protocol.constructReadAddressRequest(asBytes("0x000008"), asBytes("0x00001C")); bytes = protocol.constructReadAddressRequest(constructRegisteredQueries("0x000008", "0x00001C"));
System.out.println("Read Address (0x000008 and 0x00001C) = " + asHex(bytes)); System.out.println("Read Address (0x000008 and 0x00001C) = " + asHex(bytes));
bytes = protocol.constructReadMemoryRequest(asBytes("0x200000"), 128); bytes = protocol.extractResponseData(asBytes("0x80F01003E87DB199"));
System.out.println("Read Memory (from 0x200000, 128 bytes) = " + asHex(bytes));
bytes = protocol.extractResponseData(asBytes("0x80F01003E87DB199"), asBytes("0x8010F008A80000000800001C54"));
System.out.println("Extract Response (0x80F01003E87DB199) = " + asHex(bytes)); System.out.println("Extract Response (0x80F01003E87DB199) = " + asHex(bytes));
} }
private static Collection<RegisteredQuery> constructRegisteredQueries(String... addresses) {
Collection<RegisteredQuery> queries = new ArrayList<RegisteredQuery>();
for (String address : addresses) {
queries.add(new DefaultRegisteredQuery(address, new LoggerCallback() {
public void callback(byte[] value) {
}
}));
}
return queries;
}
} }

View File

@ -1,35 +0,0 @@
package enginuity.logger.query;
import enginuity.logger.comms.SerialReader;
import enginuity.logger.comms.SerialWriter;
import enginuity.logger.exception.SerialCommunicationException;
import static enginuity.util.ParamChecker.checkNotNull;
// TODO: Add a read timeout so doesn't wait forever?
public final class DefaultQuery implements Query {
private byte[] request;
public DefaultQuery(byte[] request) {
this.request = request;
}
public byte[] execute(SerialWriter writer, SerialReader reader) {
checkNotNull(writer, reader);
writer.write(request);
byte[] response;
while ((response = reader.read()).length == 0) {
waitForResponse();
}
return response;
}
private void waitForResponse() {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
throw new SerialCommunicationException(e);
}
}
}

View File

@ -1,9 +1,12 @@
package enginuity.logger.query; package enginuity.logger.query;
import enginuity.util.HexUtil; import static enginuity.util.HexUtil.asBytes;
import static enginuity.util.ParamChecker.checkNotNull; import static enginuity.util.ParamChecker.checkNotNull;
import static enginuity.util.ParamChecker.checkNotNullOrEmpty; import static enginuity.util.ParamChecker.checkNotNullOrEmpty;
//TODO: change address into an EcuParameter object with getAddress() & getLength() methods
//TODO: use the getLength() method to do the response data extraction in SSMProtocol
public final class DefaultRegisteredQuery implements RegisteredQuery { public final class DefaultRegisteredQuery implements RegisteredQuery {
private final String address; private final String address;
private final LoggerCallback callback; private final LoggerCallback callback;
@ -14,7 +17,7 @@ public final class DefaultRegisteredQuery implements RegisteredQuery {
checkNotNull(callback, "callback"); checkNotNull(callback, "callback");
this.address = address; this.address = address;
this.callback = callback; this.callback = callback;
bytes = HexUtil.asBytes(address); bytes = asBytes(address);
} }
public String getAddress() { public String getAddress() {

View File

@ -1,10 +0,0 @@
package enginuity.logger.query;
import enginuity.logger.comms.SerialReader;
import enginuity.logger.comms.SerialWriter;
public interface Query {
byte[] execute(SerialWriter writer, SerialReader reader);
}

View File

@ -1,5 +1,7 @@
package enginuity.util; package enginuity.util;
import java.util.Collection;
public final class ParamChecker { public final class ParamChecker {
private ParamChecker() { private ParamChecker() {
@ -29,6 +31,12 @@ public final class ParamChecker {
} }
} }
public static void checkNotNullOrEmpty(Collection<?> param, String paramName) {
if (param == null || param.isEmpty()) {
throw new IllegalArgumentException("Parameter " + paramName + " must not be null or empty");
}
}
public static void checkGreaterThanZero(int param, String paramName) { public static void checkGreaterThanZero(int param, String paramName) {
if (param <= 0) { if (param <= 0) {
throw new IllegalArgumentException("Parameter " + paramName + " must be > 0"); throw new IllegalArgumentException("Parameter " + paramName + " must be > 0");