Initial implementation of SerialPortMessageListener

This commit is contained in:
hedgecrw85 2019-03-15 11:35:44 -05:00
parent 051ba9f8fb
commit 6ec19f472f
3 changed files with 112 additions and 14 deletions

View File

@ -2,7 +2,7 @@
* SerialPort.java
*
* Created on: Feb 25, 2012
* Last Updated on: Mar 07, 2019
* Last Updated on: Mar 15, 2019
* Author: Will Hedgecock
*
* Copyright (C) 2012-2019 Fazecast, Inc.
@ -26,6 +26,7 @@
package com.fazecast.jSerialComm;
import java.lang.ProcessBuilder;
import java.nio.charset.Charset;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
@ -37,12 +38,13 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Date;
import java.util.regex.Pattern;
/**
* This class provides native access to serial ports and devices without requiring external libraries or tools.
*
* @author Will Hedgecock <will.hedgecock@fazecast.com>
* @version 2.4.2
* @version 2.5.0
* @see java.io.InputStream
* @see java.io.OutputStream
*/
@ -679,25 +681,29 @@ public final class SerialPort
* <p>
* Calling this function enables event-based serial port callbacks to be used instead of, or in addition to, direct serial port read/write calls or the {@link java.io.InputStream}/{@link java.io.OutputStream} interface.
* <p>
* The parameter passed into this method must be an implementation of either the {@link SerialPortDataListener} or the {@link SerialPortPacketListener}.
* The {@link SerialPortPacketListener} interface <b>must</b> be used if you plan to use event-based reading of <i>full</i> data packets over the serial port.
* The parameter passed into this method must be an implementation of either {@link SerialPortDataListener}, {@link SerialPortPacketListener}, or {@link SerialPortMessageListener}.
* The {@link SerialPortMessageListener} interface <b>should</b> be used if you plan to use event-based reading of <i>delimited</i> data messages over the serial port.
* The {@link SerialPortPacketListener} interface <b>should</b> be used if you plan to use event-based reading of <i>full</i> data packets over the serial port.
* Otherwise, the simpler {@link SerialPortDataListener} may be used.
* <p>
* Only one listener can be registered at a time; however, that listener can be used to detect multiple types of serial port events.
* Refer to {@link SerialPortDataListener} and {@link SerialPortPacketListener} for more information.
* Refer to {@link SerialPortDataListener}, {@link SerialPortPacketListener}, and {@link SerialPortMessageListener} for more information.
*
* @param listener A {@link SerialPortDataListener} or {@link SerialPortPacketListener}implementation to be used for event-based serial port communications.
* @param listener A {@link SerialPortDataListener}, {@link SerialPortPacketListener}, or {@link SerialPortMessageListener} implementation to be used for event-based serial port communications.
* @return Whether the listener was successfully registered with the serial port.
* @see SerialPortDataListener
* @see SerialPortPacketListener
* @see SerialPortMessageListener
*/
public final boolean addDataListener(SerialPortDataListener listener)
{
if (userDataListener != null)
return false;
userDataListener = listener;
serialEventListener = new SerialPortEventListener((userDataListener instanceof SerialPortPacketListener) ?
((SerialPortPacketListener)userDataListener).getPacketSize() : 0);
serialEventListener = ((userDataListener instanceof SerialPortPacketListener) ? new SerialPortEventListener(((SerialPortPacketListener)userDataListener).getPacketSize()) :
((userDataListener instanceof SerialPortMessageListener) ?
new SerialPortEventListener(((SerialPortMessageListener)userDataListener).getMessageDelimiter(), ((SerialPortMessageListener)userDataListener).getCharacterEncoding()) :
new SerialPortEventListener()));
eventFlags = 0;
if ((listener.getListeningEvents() & LISTENING_EVENT_DATA_AVAILABLE) > 0)
@ -1139,10 +1145,15 @@ public final class SerialPort
{
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 Thread serialEventThread = null;
public SerialPortEventListener(int packetSizeToReceive) { dataPacket = new byte[packetSizeToReceive]; }
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 final void startListening()
{
@ -1192,7 +1203,17 @@ public final class SerialPort
byte[] newBytes = new byte[numBytesAvailable];
newBytesIndex = 0;
bytesRemaining = readBytes(portHandle, newBytes, newBytes.length, 0);
if(dataPacket.length == 0)
if (!delimiter.isEmpty())
{
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)));
}
}
else if (dataPacket.length == 0)
userDataListener.serialEvent(new SerialPortEvent(SerialPort.this, LISTENING_EVENT_DATA_RECEIVED, newBytes.clone()));
else
{

View File

@ -0,0 +1,55 @@
/*
* SerialPortMessageListener.java
*
* Created on: Mar 14, 2019
* Last Updated on: Mar 15, 2019
* Author: Will Hedgecock
*
* Copyright (C) 2012-2019 Fazecast, Inc.
*
* This file is part of jSerialComm.
*
* jSerialComm is free software: you can redistribute it and/or modify
* it under the terms of either the Apache Software License, version 2, or
* the GNU Lesser General Public License as published by the Free Software
* Foundation, version 3 or above.
*
* jSerialComm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of both the GNU Lesser General Public
* License and the Apache Software License along with jSerialComm. If not,
* see <http://www.gnu.org/licenses/> and <http://www.apache.org/licenses/>.
*/
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.
* <p>
* <i>Note</i>: Using this interface will negate any serial port read timeout settings since they make no sense in an asynchronous context.
*
* @author Will Hedgecock &lt;will.hedgecock@fazecast.com&gt;
* @version 2.5.0
* @see com.fazecast.jSerialComm.SerialPortDataListener
* @see java.util.EventListener
*/
public interface SerialPortMessageListener extends SerialPortDataListener
{
/**
* Must be overridden to return the expected message delimiter that <b>must</b> 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.
*/
public abstract String getMessageDelimiter();
/**
* Must be overridden to return the expected character encoding used by your message transfer protocol.
*
* @return A {@link java.nio.charset.Charset} indicating the expected character encoding used by your serial messages.
*/
public abstract Charset getCharacterEncoding();
}

View File

@ -2,10 +2,10 @@
* SerialPortTest.java
*
* Created on: Feb 27, 2015
* Last Updated on: Jan 10, 2018
* Last Updated on: Mar 14, 2019
* Author: Will Hedgecock
*
* Copyright (C) 2012-2018 Fazecast, Inc.
* Copyright (C) 2012-2019 Fazecast, Inc.
*
* This file is part of jSerialComm.
*
@ -26,13 +26,14 @@
package com.fazecast.jSerialComm;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Scanner;
/**
* This class provides a test case for the jSerialComm library.
*
* @author Will Hedgecock &lt;will.hedgecock@gmail.com&gt;
* @version 2.4.1
* @version 2.5.0
* @see java.io.InputStream
* @see java.io.OutputStream
*/
@ -55,6 +56,22 @@ public class SerialPortTest
public int getPacketSize() { return 100; }
}
private static final class MessageListener implements SerialPortMessageListener
{
@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);
}
@Override
public String getMessageDelimiter() { return "\n"; }
@Override
public Charset getCharacterEncoding() { return Charset.forName("UTF-8"); }
}
static public void main(String[] args)
{
SerialPort[] ports = SerialPort.getCommPorts();
@ -169,8 +186,13 @@ public class SerialPortTest
PacketListener listener = new PacketListener();
ubxPort.addDataListener(listener);
try { Thread.sleep(5000); } catch (Exception e) {}
System.out.println("\n\nClosing " + ubxPort.getDescriptivePortName() + ": " + ubxPort.closePort());
ubxPort.removeDataListener();
System.out.println("\nNow listening for newline-delimited string messages\n");
MessageListener messageListener = new MessageListener();
ubxPort.addDataListener(messageListener);
try { Thread.sleep(5000); } catch (Exception e) {}
ubxPort.removeDataListener();
System.out.println("\n\nClosing " + ubxPort.getDescriptivePortName() + ": " + ubxPort.closePort());
try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); }
System.out.println("Reopening " + ubxPort.getDescriptivePortName() + ": " + ubxPort.openPort() + "\n");
ubxPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, 1000, 0);