diff --git a/src/enginuity/Settings.java b/src/enginuity/Settings.java index cfb066ad..13fd9fa4 100644 --- a/src/enginuity/Settings.java +++ b/src/enginuity/Settings.java @@ -38,9 +38,10 @@ public class Settings implements Serializable { private Color warningColor = new Color(255, 0, 0); private int tableClickCount = 2; // number of clicks to open table - private String loggerPort = "COM3"; - - public Settings() { + private String loggerPort = "COM4"; + private String loggerProtocol = "SSM"; + + public Settings() { //center window by default Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); windowLocation.move(((int)(screenSize.getWidth() - windowSize.getWidth()) / 2), @@ -64,7 +65,7 @@ public class Settings implements Serializable { } public Vector getEcuDefinitionFiles() { - if (ecuDefinitionFiles.size() == 0) { + if (ecuDefinitionFiles.isEmpty()) { // if no files defined, add default ecuDefinitionFiles.add(new File("./ecu_defs.xml")); } @@ -204,9 +205,13 @@ public class Settings implements Serializable { } public void setUserLevel(int userLevel) { - this.userLevel = userLevel; - if (userLevel > 5) userLevel = 5; - else if (userLevel < 1) userLevel = 1; + if (userLevel > 5) { + this.userLevel = 5; + } else if (userLevel < 1) { + this.userLevel = 1; + } else { + this.userLevel = userLevel; + } } public int getTableClickCount() { @@ -268,4 +273,8 @@ public class Settings implements Serializable { public void setLoggerPort(String loggerPort) { this.loggerPort = loggerPort; } + + public String getLoggerProtocol() { + return loggerProtocol; + } } \ No newline at end of file diff --git a/src/enginuity/logger/DefaultLoggerController.java b/src/enginuity/logger/DefaultLoggerController.java index 44b4c71b..66e770ee 100644 --- a/src/enginuity/logger/DefaultLoggerController.java +++ b/src/enginuity/logger/DefaultLoggerController.java @@ -7,8 +7,6 @@ import enginuity.logger.manager.DefaultQueryManager; import enginuity.logger.manager.DefaultTransmissionManager; import enginuity.logger.manager.QueryManager; import enginuity.logger.manager.TransmissionManager; -import enginuity.logger.protocol.Protocol; -import enginuity.logger.protocol.ProtocolFactory; import enginuity.logger.query.DefaultRegisteredQuery; import enginuity.logger.query.LoggerCallback; import static enginuity.util.ParamChecker.checkNotNull; @@ -21,8 +19,7 @@ public final class DefaultLoggerController implements LoggerController { private final QueryManager queryManager; public DefaultLoggerController(Settings settings) { - Protocol ssmProtocol = ProtocolFactory.getInstance().getProtocol("SSM"); - TransmissionManager txManager = new DefaultTransmissionManager(settings, ssmProtocol); + TransmissionManager txManager = new DefaultTransmissionManager(settings); queryManager = new DefaultQueryManager(txManager); } diff --git a/src/enginuity/logger/EcuLogger.java b/src/enginuity/logger/EcuLogger.java index 25a27c04..599baa62 100644 --- a/src/enginuity/logger/EcuLogger.java +++ b/src/enginuity/logger/EcuLogger.java @@ -1,6 +1,8 @@ package enginuity.logger; import enginuity.Settings; +import enginuity.logger.query.LoggerCallback; +import static enginuity.util.HexUtil.asHex; import javax.swing.*; 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 { private final Settings settings = new 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(); public EcuLogger(String title) { @@ -34,6 +38,14 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC //add to container 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) { @@ -95,19 +107,30 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC } private JPanel buildControlToolbar() { - refreshPortsComboBox(); JPanel controlPanel = new JPanel(new FlowLayout()); controlPanel.add(buildStartButton()); controlPanel.add(buildStopButton()); - controlPanel.add(portsComboBox); + controlPanel.add(buildPortsComboBox()); 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() { List ports = CONTROLLER.listSerialPorts(); for (String port : ports) { portsComboBox.addItem(port); } + settings.setLoggerPort((String) portsComboBox.getSelectedItem()); } private JButton buildStartButton() { @@ -140,8 +163,8 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC } 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() { diff --git a/src/enginuity/logger/TestEcuLogger.java b/src/enginuity/logger/TestEcuLogger.java index 4ead477f..9ab8958a 100644 --- a/src/enginuity/logger/TestEcuLogger.java +++ b/src/enginuity/logger/TestEcuLogger.java @@ -1,30 +1,16 @@ package enginuity.logger; 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.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 { - private static final int CONNECT_TIMEOUT = 2000; private TestEcuLogger() { } public static void main(String... args) { try { - //testTwoWaySerialComm(); testLoggerController(); } catch (Exception e) { e.printStackTrace(); @@ -38,7 +24,7 @@ public final class TestEcuLogger { addLogger(controller, "0x291C8"); addLogger(controller, "0x291F9"); addLogger(controller, "0x286BB"); - sleep(1000); + sleep(60000); controller.removeLogger("0x291F9"); controller.removeLogger("0x291C8"); controller.removeLogger("0x286BB"); @@ -58,28 +44,6 @@ public final class TestEcuLogger { }); } - private static void testTwoWaySerialComm() { - SerialPortDiscoverer serialPortDiscoverer = new DefaultSerialPortDiscoverer(); - List 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) { System.out.println("Response: " + HexUtil.asHex(value)); } diff --git a/src/enginuity/logger/comms/DefaultSerialConnection.java b/src/enginuity/logger/comms/DefaultSerialConnection.java index e621b916..a4e97947 100644 --- a/src/enginuity/logger/comms/DefaultSerialConnection.java +++ b/src/enginuity/logger/comms/DefaultSerialConnection.java @@ -1,31 +1,194 @@ package enginuity.logger.comms; -import enginuity.logger.query.Query; -import enginuity.util.ParamChecker; +import enginuity.logger.exception.NotConnectedException; +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 { - private SerialWriter serialWriter; - private SerialReader serialReader; + private Protocol protocol; + private SerialPort serialPort; + private OutputStream os; + private InputStream is; - public DefaultSerialConnection(SerialWriter serialWriter, SerialReader serialReader) { - this.serialWriter = serialWriter; - this.serialReader = serialReader; + public DefaultSerialConnection(Protocol protocol, String portName, int connectTimeout) { + checkNotNull(protocol, "protocol"); + 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) { - ParamChecker.checkNotNull(query, "query"); - return query.execute(serialWriter, serialReader); + public byte[] sendEcuInit() { + return send(protocol.constructEcuInitRequest()); + } + + 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 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() { - try { + if (os != null) { try { - serialWriter.close(); - } finally { - serialReader.close(); + os.close(); + } catch (IOException e) { + 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); + } + } + } diff --git a/src/enginuity/logger/comms/DefaultSerialReader.java b/src/enginuity/logger/comms/DefaultSerialReader.java deleted file mode 100644 index ca0f33ed..00000000 --- a/src/enginuity/logger/comms/DefaultSerialReader.java +++ /dev/null @@ -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); - } - -} diff --git a/src/enginuity/logger/comms/DefaultSerialWriter.java b/src/enginuity/logger/comms/DefaultSerialWriter.java deleted file mode 100644 index f958faf0..00000000 --- a/src/enginuity/logger/comms/DefaultSerialWriter.java +++ /dev/null @@ -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; - } - -} diff --git a/src/enginuity/logger/comms/DefaultTwoWaySerialComm.java b/src/enginuity/logger/comms/DefaultTwoWaySerialComm.java deleted file mode 100644 index 3770adf7..00000000 --- a/src/enginuity/logger/comms/DefaultTwoWaySerialComm.java +++ /dev/null @@ -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()); - } - } - -} diff --git a/src/enginuity/logger/comms/SerialConnection.java b/src/enginuity/logger/comms/SerialConnection.java index 3c1666d5..2c4b27f0 100644 --- a/src/enginuity/logger/comms/SerialConnection.java +++ b/src/enginuity/logger/comms/SerialConnection.java @@ -1,10 +1,16 @@ package enginuity.logger.comms; -import enginuity.logger.query.Query; +import enginuity.logger.query.RegisteredQuery; + +import java.util.Collection; public interface SerialConnection { - byte[] transmit(Query query); + byte[] sendEcuInit(); + + byte[] send(byte[] bytes); + + void sendAddressReads(Collection queries); void close(); diff --git a/src/enginuity/logger/comms/SerialReader.java b/src/enginuity/logger/comms/SerialReader.java deleted file mode 100644 index 5aeaa788..00000000 --- a/src/enginuity/logger/comms/SerialReader.java +++ /dev/null @@ -1,9 +0,0 @@ -package enginuity.logger.comms; - -public interface SerialReader extends Runnable { - - byte[] read(); - - void close(); - -} diff --git a/src/enginuity/logger/comms/SerialWriter.java b/src/enginuity/logger/comms/SerialWriter.java deleted file mode 100644 index a7fc29c6..00000000 --- a/src/enginuity/logger/comms/SerialWriter.java +++ /dev/null @@ -1,9 +0,0 @@ -package enginuity.logger.comms; - -public interface SerialWriter extends Runnable { - - void write(byte[] bytes); - - void close(); - -} diff --git a/src/enginuity/logger/comms/TwoWaySerialComm.java b/src/enginuity/logger/comms/TwoWaySerialComm.java deleted file mode 100644 index edf5c0b3..00000000 --- a/src/enginuity/logger/comms/TwoWaySerialComm.java +++ /dev/null @@ -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(); - -} diff --git a/src/enginuity/logger/manager/DefaultQueryManager.java b/src/enginuity/logger/manager/DefaultQueryManager.java index 355eb01e..2a0a7c38 100644 --- a/src/enginuity/logger/manager/DefaultQueryManager.java +++ b/src/enginuity/logger/manager/DefaultQueryManager.java @@ -9,6 +9,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +//TODO: Allow transmission of multiple query objects at a time + public final class DefaultQueryManager implements QueryManager { private final Map queryMap = Collections.synchronizedMap(new HashMap()); private final List addList = new ArrayList(); @@ -34,7 +36,7 @@ public final class DefaultQueryManager implements QueryManager { public void run() { System.out.println("QueryManager started."); - + long start = System.currentTimeMillis(); int count = 0; @@ -43,12 +45,8 @@ public final class DefaultQueryManager implements QueryManager { stop = false; while (!stop) { updateQueryList(); - for (String address : queryMap.keySet()) { - RegisteredQuery registeredQuery = queryMap.get(address); - byte[] response = txManager.queryAddress(registeredQuery.getBytes()); - registeredQuery.setResponse(response); - count++; - } + txManager.sendQueries(queryMap.values()); + count++; } } finally { txManager.stop(); diff --git a/src/enginuity/logger/manager/DefaultTransmissionManager.java b/src/enginuity/logger/manager/DefaultTransmissionManager.java index 93e23592..cd8734ae 100644 --- a/src/enginuity/logger/manager/DefaultTransmissionManager.java +++ b/src/enginuity/logger/manager/DefaultTransmissionManager.java @@ -1,51 +1,81 @@ package enginuity.logger.manager; import enginuity.Settings; -import enginuity.logger.comms.DefaultTwoWaySerialComm; +import enginuity.logger.comms.DefaultSerialConnection; import enginuity.logger.comms.SerialConnection; -import enginuity.logger.comms.TwoWaySerialComm; import enginuity.logger.exception.NotConnectedException; -import enginuity.logger.protocol.ConnectionProperties; +import enginuity.logger.exception.SerialCommunicationException; 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.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 { private static final int CONNECT_TIMEOUT = 2000; private final Settings settings; - private final Protocol protocol; - private final ConnectionProperties connectionProperties; - private final TwoWaySerialComm twoWaySerialComm = new DefaultTwoWaySerialComm(); private SerialConnection serialConnection = null; - public DefaultTransmissionManager(Settings settings, Protocol protocol) { - checkNotNull(settings, protocol, protocol.getConnectionProperties()); + public DefaultTransmissionManager(Settings settings) { + checkNotNull(settings, "settings"); this.settings = settings; - this.protocol = protocol; - this.connectionProperties = protocol.getConnectionProperties(); } public void start() { - serialConnection = twoWaySerialComm.connect(settings.getLoggerPort(), connectionProperties.getBaudRate(), connectionProperties.getDataBits(), - connectionProperties.getStopBits(), connectionProperties.getParity(), CONNECT_TIMEOUT); + try { + 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) { - checkNotNullOrEmpty(address, "address"); + public void sendEcuInit(LoggerCallback callback) { if (serialConnection != null) { - byte[] request = protocol.constructReadAddressRequest(address); - byte[] response = serialConnection.transmit(new DefaultQuery(request)); - //TODO: Enable this once extractResponseData() complete - //return protocol.extractResponseData(response, request); - return response; + callback.callback(serialConnection.sendEcuInit()); } else { throw new NotConnectedException("TransmissionManager must be started before a query can be sent!"); } } + public void sendQueries(Collection 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() { - 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(); + } } } diff --git a/src/enginuity/logger/manager/TransmissionManager.java b/src/enginuity/logger/manager/TransmissionManager.java index daba6211..7f548d0f 100644 --- a/src/enginuity/logger/manager/TransmissionManager.java +++ b/src/enginuity/logger/manager/TransmissionManager.java @@ -1,10 +1,17 @@ package enginuity.logger.manager; +import enginuity.logger.query.LoggerCallback; +import enginuity.logger.query.RegisteredQuery; + +import java.util.Collection; + public interface TransmissionManager { void start(); - byte[] queryAddress(byte[] query); + void sendEcuInit(LoggerCallback callback); + + void sendQueries(Collection query); void stop(); diff --git a/src/enginuity/logger/protocol/Protocol.java b/src/enginuity/logger/protocol/Protocol.java index 7cc745db..ad4562d7 100644 --- a/src/enginuity/logger/protocol/Protocol.java +++ b/src/enginuity/logger/protocol/Protocol.java @@ -1,14 +1,22 @@ package enginuity.logger.protocol; +import enginuity.logger.query.RegisteredQuery; + +import java.util.Collection; + public interface Protocol { - byte[] constructReadMemoryRequest(byte[] fromAddress, int numBytes); - - byte[] constructReadAddressRequest(byte[]... addresses); - byte[] constructEcuInitRequest(); - byte[] extractResponseData(byte[] response, byte[] request); + byte[] constructReadMemoryRequest(RegisteredQuery query, int numBytes); + + byte[] constructReadAddressRequest(Collection queries); + + byte[] constructReadAddressResponse(Collection queries); + + void setResponse(Collection queries, byte[] response); + + byte[] extractResponseData(byte[] response); ConnectionProperties getConnectionProperties(); diff --git a/src/enginuity/logger/protocol/SSMProtocol.java b/src/enginuity/logger/protocol/SSMProtocol.java index 4e17e7a1..63b1960d 100644 --- a/src/enginuity/logger/protocol/SSMProtocol.java +++ b/src/enginuity/logger/protocol/SSMProtocol.java @@ -1,10 +1,17 @@ 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.asHex; import static enginuity.util.ParamChecker.checkGreaterThanZero; +import static enginuity.util.ParamChecker.checkNotNull; import static enginuity.util.ParamChecker.checkNotNullOrEmpty; +import java.util.ArrayList; +import java.util.Collection; + public final class SSMProtocol implements Protocol { private static final byte HEADER = (byte) 0x80; 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_ADDRESS_COMMAND = (byte) 0xA8; 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) { - checkNotNullOrEmpty(fromAddress, "fromAddress"); + public byte[] constructReadMemoryRequest(RegisteredQuery query, int numBytes) { + checkNotNull(query, "query"); checkGreaterThanZero(numBytes, "numBytes"); // 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) { - checkNotNullOrEmpty(addresses, "addresses"); + public byte[] constructReadAddressRequest(Collection queries) { + checkNotNullOrEmpty(queries, "queries"); // 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 queries) { + checkNotNullOrEmpty(queries, "queries"); + return new byte[DATA_SIZE * queries.size() + 5]; + } + + public void setResponse(Collection 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() { @@ -33,7 +61,7 @@ public final class SSMProtocol implements Protocol { return buildRequest(ECU_INIT_COMMAND, false, new byte[0]); } - public byte[] extractResponseData(byte[] response, byte[] request) { + public byte[] extractResponseData(byte[] response) { checkNotNullOrEmpty(response, "response"); // 0x80 0xF0 0x10 data_length response_data checksum //TODO: Take possible echoed request into account when extracting response!! @@ -64,19 +92,29 @@ public final class SSMProtocol implements Protocol { }; } + private byte[][] convertToByteAddresses(Collection 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) { int i = 0; - assertEquals(HEADER, response[i++]); - assertEquals(DIAGNOSTIC_TOOL_ID, response[i++]); - assertEquals(ECU_ID, response[i++]); - assertEquals(asByte(response.length - 5), response[i]); - assertEquals(calculateChecksum(response), response[response.length - 1]); + assertEquals(HEADER, response[i++], "Invalid header"); + assertEquals(DIAGNOSTIC_TOOL_ID, response[i++], "Invalid diagnostic tool id"); + assertEquals(ECU_ID, response[i++], "Invalid ECU id"); + assertEquals(asByte(response.length - 5), response[i], "Invalid response data length"); + 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) { - 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(); 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)); - bytes = protocol.constructReadMemoryRequest(asBytes("0x200000"), 128); - System.out.println("Read Memory (from 0x200000, 128 bytes) = " + asHex(bytes)); - - bytes = protocol.extractResponseData(asBytes("0x80F01003E87DB199"), asBytes("0x8010F008A80000000800001C54")); + bytes = protocol.extractResponseData(asBytes("0x80F01003E87DB199")); System.out.println("Extract Response (0x80F01003E87DB199) = " + asHex(bytes)); } + private static Collection constructRegisteredQueries(String... addresses) { + Collection queries = new ArrayList(); + for (String address : addresses) { + queries.add(new DefaultRegisteredQuery(address, new LoggerCallback() { + public void callback(byte[] value) { + } + })); + } + return queries; + } + } diff --git a/src/enginuity/logger/query/DefaultQuery.java b/src/enginuity/logger/query/DefaultQuery.java deleted file mode 100644 index c7c4c82f..00000000 --- a/src/enginuity/logger/query/DefaultQuery.java +++ /dev/null @@ -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); - } - } - -} diff --git a/src/enginuity/logger/query/DefaultRegisteredQuery.java b/src/enginuity/logger/query/DefaultRegisteredQuery.java index 0cf711bb..b87d9353 100644 --- a/src/enginuity/logger/query/DefaultRegisteredQuery.java +++ b/src/enginuity/logger/query/DefaultRegisteredQuery.java @@ -1,9 +1,12 @@ 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.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 { private final String address; private final LoggerCallback callback; @@ -14,7 +17,7 @@ public final class DefaultRegisteredQuery implements RegisteredQuery { checkNotNull(callback, "callback"); this.address = address; this.callback = callback; - bytes = HexUtil.asBytes(address); + bytes = asBytes(address); } public String getAddress() { diff --git a/src/enginuity/logger/query/Query.java b/src/enginuity/logger/query/Query.java deleted file mode 100644 index bd264074..00000000 --- a/src/enginuity/logger/query/Query.java +++ /dev/null @@ -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); - -} diff --git a/src/enginuity/util/ParamChecker.java b/src/enginuity/util/ParamChecker.java index 22db14e2..ab835b31 100644 --- a/src/enginuity/util/ParamChecker.java +++ b/src/enginuity/util/ParamChecker.java @@ -1,5 +1,7 @@ package enginuity.util; +import java.util.Collection; + public final class 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) { if (param <= 0) { throw new IllegalArgumentException("Parameter " + paramName + " must be > 0");