diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPort.java b/src/main/java/com/fazecast/jSerialComm/SerialPort.java index 0131fb3..130dea0 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java @@ -2,7 +2,7 @@ * SerialPort.java * * Created on: Feb 25, 2012 - * Last Updated on: Mar 15, 2019 + * Last Updated on: Mar 19, 2019 * Author: Will Hedgecock * * Copyright (C) 2012-2019 Fazecast, Inc. @@ -26,8 +26,8 @@ package com.fazecast.jSerialComm; import java.lang.ProcessBuilder; -import java.nio.charset.Charset; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; @@ -37,6 +37,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.util.Arrays; import java.util.Date; import java.util.regex.Pattern; @@ -702,7 +703,7 @@ public final class SerialPort userDataListener = listener; serialEventListener = ((userDataListener instanceof SerialPortPacketListener) ? new SerialPortEventListener(((SerialPortPacketListener)userDataListener).getPacketSize()) : ((userDataListener instanceof SerialPortMessageListener) ? - new SerialPortEventListener(((SerialPortMessageListener)userDataListener).getMessageDelimiter(), ((SerialPortMessageListener)userDataListener).getCharacterEncoding()) : + new SerialPortEventListener(((SerialPortMessageListener)userDataListener).getMessageDelimiter(), ((SerialPortMessageListener)userDataListener).delimiterIndicatesEndOfMessage()) : new SerialPortEventListener())); eventFlags = 0; @@ -1144,16 +1145,15 @@ public final class SerialPort private final class SerialPortEventListener { private volatile boolean isListening = false; - private final byte[] dataPacket; - private final String delimiter, delimiterRegex; - private final Charset messageCharset; - private volatile int dataPacketIndex = 0; - private String messages = ""; + private final boolean messageEndIsDelimited; + private final byte[] dataPacket, delimiters; + private volatile ByteArrayOutputStream messageBytes = new ByteArrayOutputStream(); + private volatile int dataPacketIndex = 0, delimiterIndex = 0; private Thread serialEventThread = null; - public SerialPortEventListener() { dataPacket = new byte[0]; delimiter = delimiterRegex = ""; messageCharset = Charset.forName("UTF-8"); } - public SerialPortEventListener(int packetSizeToReceive) { dataPacket = new byte[packetSizeToReceive]; delimiter = delimiterRegex = ""; messageCharset = Charset.forName("UTF-8"); } - public SerialPortEventListener(String messageDelimiter, Charset charset) { dataPacket = new byte[0]; delimiter = messageDelimiter; delimiterRegex = Pattern.quote(messageDelimiter); messageCharset = charset; } + public SerialPortEventListener() { dataPacket = new byte[0]; delimiters = new byte[0]; messageEndIsDelimited = true; } + public SerialPortEventListener(int packetSizeToReceive) { dataPacket = new byte[packetSizeToReceive]; delimiters = new byte[0]; messageEndIsDelimited = true; } + public SerialPortEventListener(byte[] messageDelimiters, boolean delimiterForMessageEnd) { dataPacket = new byte[0]; delimiters = messageDelimiters; messageEndIsDelimited = delimiterForMessageEnd; } public final void startListening() { @@ -1203,15 +1203,28 @@ public final class SerialPort byte[] newBytes = new byte[numBytesAvailable]; newBytesIndex = 0; bytesRemaining = readBytes(portHandle, newBytes, newBytes.length, 0); - if (!delimiter.isEmpty()) + if (delimiters.length > 0) { - messages += new String(newBytes, messageCharset); - while (messages.contains(delimiter)) - { - String[] message = messages.split(delimiterRegex, 2); - messages = (message.length > 1) ? message[1] : ""; - userDataListener.serialEvent(new SerialPortEvent(SerialPort.this, LISTENING_EVENT_DATA_RECEIVED, message[0].getBytes(messageCharset))); - } + int startIndex = 0; + for (int offset = 0; offset < bytesRemaining; ++offset) + if (newBytes[offset] == delimiters[delimiterIndex]) + { + if ((++delimiterIndex) == delimiters.length) + { + messageBytes.write(newBytes, startIndex, 1 + offset - startIndex); + byte[] byteArray = (messageEndIsDelimited ? messageBytes.toByteArray() : Arrays.copyOf(messageBytes.toByteArray(), messageBytes.size() - delimiters.length)); + if ((byteArray.length > 0) && (messageEndIsDelimited || (delimiters[0] == byteArray[0]))) + userDataListener.serialEvent(new SerialPortEvent(SerialPort.this, LISTENING_EVENT_DATA_RECEIVED, byteArray)); + startIndex = offset + 1; + messageBytes.reset(); + delimiterIndex = 0; + if (!messageEndIsDelimited) + messageBytes.write(delimiters, 0, delimiters.length); + } + } + else if (delimiterIndex != 0) + delimiterIndex = (newBytes[offset] == delimiters[0]) ? 1 : 0; + messageBytes.write(newBytes, startIndex, bytesRemaining - startIndex); } else if (dataPacket.length == 0) userDataListener.serialEvent(new SerialPortEvent(SerialPort.this, LISTENING_EVENT_DATA_RECEIVED, newBytes.clone())); diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPortMessageListener.java b/src/main/java/com/fazecast/jSerialComm/SerialPortMessageListener.java index 7ac2d65..46b7430 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPortMessageListener.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPortMessageListener.java @@ -2,7 +2,7 @@ * SerialPortMessageListener.java * * Created on: Mar 14, 2019 - * Last Updated on: Mar 15, 2019 + * Last Updated on: Mar 19, 2019 * Author: Will Hedgecock * * Copyright (C) 2012-2019 Fazecast, Inc. @@ -25,10 +25,8 @@ package com.fazecast.jSerialComm; -import java.nio.charset.Charset; - /** - * This interface must be implemented to enable delimited string-based message reads using event-based serial port I/O. + * This interface must be implemented to enable delimited message reads using event-based serial port I/O. *

* Note: Using this interface will negate any serial port read timeout settings since they make no sense in an asynchronous context. * @@ -40,16 +38,16 @@ import java.nio.charset.Charset; public interface SerialPortMessageListener extends SerialPortDataListener { /** - * Must be overridden to return the expected message delimiter that must be encountered before the {@link #serialEvent(SerialPortEvent)} callback is triggered. + * Must be overridden to return the expected message delimiter bytes that must be encountered before the {@link #serialEvent(SerialPortEvent)} callback is triggered. * - * @return A string indicating the expected message delimiter that must be encountered before the {@link #serialEvent(SerialPortEvent)} callback is triggered. + * @return A byte array containing the expected message delimiters that must be encountered before the {@link #serialEvent(SerialPortEvent)} callback is triggered. */ - public abstract String getMessageDelimiter(); - + public abstract byte[] getMessageDelimiter(); + /** - * Must be overridden to return the expected character encoding used by your message transfer protocol. + * Must be overridden to return whether the message delimiter indicates the end or the beginning of a message. * - * @return A {@link java.nio.charset.Charset} indicating the expected character encoding used by your serial messages. + * @return A boolean indicating whether the message delimiter indicates the end or the beginning of a message. */ - public abstract Charset getCharacterEncoding(); + public abstract boolean delimiterIndicatesEndOfMessage(); } diff --git a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java index 52da5d2..5a3b148 100644 --- a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java +++ b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java @@ -2,7 +2,7 @@ * SerialPortTest.java * * Created on: Feb 27, 2015 - * Last Updated on: Mar 14, 2019 + * Last Updated on: Mar 19, 2019 * Author: Will Hedgecock * * Copyright (C) 2012-2019 Fazecast, Inc. @@ -58,18 +58,28 @@ public class SerialPortTest private static final class MessageListener implements SerialPortMessageListener { + public String byteToHex(byte num) + { + char[] hexDigits = new char[2]; + hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16); + hexDigits[1] = Character.forDigit((num & 0xF), 16); + return new String(hexDigits); + } @Override public int getListeningEvents() { return SerialPort.LISTENING_EVENT_DATA_RECEIVED; } @Override public void serialEvent(SerialPortEvent event) { - String newMessage = new String(event.getReceivedData()); - System.out.println("Received the following message: " + newMessage); + byte[] byteArray = event.getReceivedData(); + StringBuffer hexStringBuffer = new StringBuffer(); + for (int i = 0; i < byteArray.length; i++) + hexStringBuffer.append(byteToHex(byteArray[i])); + System.out.println("Received the following message: " + hexStringBuffer.toString()); } @Override - public String getMessageDelimiter() { return "\n"; } + public byte[] getMessageDelimiter() { return new byte[]{ (byte)0xB5, (byte)0x62 }; } @Override - public Charset getCharacterEncoding() { return Charset.forName("UTF-8"); } + public boolean delimiterIndicatesEndOfMessage() { return false; } } static public void main(String[] args) @@ -187,7 +197,7 @@ public class SerialPortTest ubxPort.addDataListener(listener); try { Thread.sleep(5000); } catch (Exception e) {} ubxPort.removeDataListener(); - System.out.println("\nNow listening for newline-delimited string messages\n"); + System.out.println("\nNow listening for byte-delimited binary messages\n"); MessageListener messageListener = new MessageListener(); ubxPort.addDataListener(messageListener); try { Thread.sleep(5000); } catch (Exception e) {}