mirror of https://github.com/rusefi/RomRaider.git
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:
parent
0ee24d2e0b
commit
aecf05fa39
|
@ -38,7 +38,8 @@ 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
|
||||||
|
@ -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) {
|
||||||
|
if (userLevel > 5) {
|
||||||
|
this.userLevel = 5;
|
||||||
|
} else if (userLevel < 1) {
|
||||||
|
this.userLevel = 1;
|
||||||
|
} else {
|
||||||
this.userLevel = userLevel;
|
this.userLevel = userLevel;
|
||||||
if (userLevel > 5) userLevel = 5;
|
}
|
||||||
else if (userLevel < 1) userLevel = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
if (os != null) {
|
||||||
try {
|
try {
|
||||||
try {
|
os.close();
|
||||||
serialWriter.close();
|
} catch (IOException e) {
|
||||||
} finally {
|
e.printStackTrace();
|
||||||
serialReader.close();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (is != null) {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (serialPort != null) {
|
||||||
|
try {
|
||||||
|
serialPort.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package enginuity.logger.comms;
|
|
||||||
|
|
||||||
public interface SerialReader extends Runnable {
|
|
||||||
|
|
||||||
byte[] read();
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package enginuity.logger.comms;
|
|
||||||
|
|
||||||
public interface SerialWriter extends Runnable {
|
|
||||||
|
|
||||||
void write(byte[] bytes);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
|
|
||||||
}
|
|
|
@ -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>();
|
||||||
|
@ -43,13 +45,9 @@ 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);
|
|
||||||
byte[] response = txManager.queryAddress(registeredQuery.getBytes());
|
|
||||||
registeredQuery.setResponse(response);
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
txManager.stop();
|
txManager.stop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in New Issue