Skip to content

Commit 5966a40

Browse files
committed
Ensure that calling closePort() from within a LISTENING_EVENT_PORT_DISCONNECTED callback works
1 parent 3b40bc9 commit 5966a40

File tree

3 files changed

+54
-14
lines changed

3 files changed

+54
-14
lines changed

src/main/java/com/fazecast/jSerialComm/SerialPort.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -729,9 +729,9 @@ public final boolean closePort()
729729
* }
730730
* {@literal @}Override
731731
* public void serialEvent(SerialPortEvent serialPortEvent) {
732-
* if (serialPortEvent.getEventType() == SerialPort.LISTENING_EVENT_PORT_DISCONNECTED)
732+
* if ((serialPortEvent.getEventType() & SerialPort.LISTENING_EVENT_PORT_DISCONNECTED) > 0)
733733
* port.closePort();
734-
* else if (serialPortEvent.getEventType() == SerialPort.LISTENING_EVENT_DATA_RECEIVED)
734+
* else if ((serialPortEvent.getEventType() & SerialPort.LISTENING_EVENT_DATA_RECEIVED) > 0)
735735
* // ... you get the idea
736736
* }
737737
* });
@@ -742,9 +742,12 @@ public final boolean closePort()
742742
* {@link SerialPort} object:
743743
* <pre>
744744
* if (!port.isOpen())
745-
* port.open();
745+
* port.openPort();
746746
* </pre>
747747
* <p>
748+
* Note that you <b>CANNOT</b> call <code>port.openPort()</code> from within the <code>serialEvent()</code>
749+
* handler. Port re-opening <b>must</b> be done within your own application context.
750+
* <p>
748751
* Note that once a USB serial port has been unplugged and reconnected, it's unlikely to work again until the
749752
* port's {@link #closePort()} and {@link openPort(int, int, int)} methods have both been called to
750753
* re-establish the connection.
@@ -1153,6 +1156,9 @@ private SerialPort(String port, String friendly, String description, String loca
11531156
* <p>
11541157
* Only one listener can be registered at a time; however, that listener can be used to detect multiple types of serial port events.
11551158
* Refer to {@link SerialPortDataListener}, {@link SerialPortDataListenerWithExceptions}, {@link SerialPortPacketListener}, {@link SerialPortMessageListener}, and {@link SerialPortMessageListenerWithExceptions} for more information.
1159+
* <p>
1160+
* Note that if you register to listen for {@link SerialPort#LISTENING_EVENT_PORT_DISCONNECTED} events, you <b>CANNOT</b> call <code>openPort()</code> to re-open a disconnected port from within the <code>serialEvent()</code>
1161+
* handler. Port re-opening <b>must</b> be done within your own application context.
11561162
*
11571163
* @param listener A {@link SerialPortDataListener}, {@link SerialPortDataListenerWithExceptions}, {@link SerialPortPacketListener}, {@link SerialPortMessageListener}, or {@link SerialPortMessageListenerWithExceptions} implementation to be used for event-based serial port communications.
11581164
* @return Whether the listener was successfully registered with the serial port.
@@ -2207,9 +2213,17 @@ else if (dataPacket.length == 0)
22072213
}
22082214
if (eventListenerRunning && !isShuttingDown && (event != SerialPort.LISTENING_EVENT_TIMED_OUT))
22092215
{
2210-
userDataListener.serialEvent(new SerialPortEvent(SerialPort.this, event));
2211-
if (event == SerialPort.LISTENING_EVENT_PORT_DISCONNECTED)
2216+
// If disconnected, invoke the user data listener from a new thread to allow them to close the port without blocking
2217+
if ((event & SerialPort.LISTENING_EVENT_PORT_DISCONNECTED) > 0)
2218+
{
22122219
eventListenerRunning = false;
2220+
SerialPortThreadFactory.get().newThread(new Runnable() {
2221+
@Override
2222+
public void run() { userDataListener.serialEvent(new SerialPortEvent(SerialPort.this, SerialPort.LISTENING_EVENT_PORT_DISCONNECTED)); }
2223+
}).start();
2224+
}
2225+
else
2226+
userDataListener.serialEvent(new SerialPortEvent(SerialPort.this, event));
22132227
}
22142228
}
22152229
}

src/main/java/com/fazecast/jSerialComm/SerialPortDataListener.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public interface SerialPortDataListener extends EventListener
5353
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{@link SerialPort#LISTENING_EVENT_SOFTWARE_OVERRUN_ERROR}<br>
5454
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{@link SerialPort#LISTENING_EVENT_PARITY_ERROR}<br>
5555
* <p>
56-
* Two or more events may be OR'd together to listen for multiple events; however, if {@link SerialPort#LISTENING_EVENT_DATA_AVAILABLE} is OR'd with {@link SerialPort#LISTENING_EVENT_DATA_RECEIVED}, the {@link SerialPort#LISTENING_EVENT_DATA_RECEIVED} flag will take precedence.
56+
* Two or more events may be OR'd together to listen for multiple events; however, if {@link SerialPort#LISTENING_EVENT_DATA_AVAILABLE} is OR'd with
57+
* {@link SerialPort#LISTENING_EVENT_DATA_RECEIVED}, the {@link SerialPort#LISTENING_EVENT_DATA_RECEIVED} flag will take precedence.
5758
* <p>
5859
* Note that event-based <i>write</i> callbacks are only supported on Windows operating systems. As such, the {@link SerialPort#LISTENING_EVENT_DATA_WRITTEN}
5960
* event will never be called on a non-Windows system.
@@ -83,7 +84,23 @@ public interface SerialPortDataListener extends EventListener
8384
/**
8485
* Called whenever one or more of the serial port events specified by the {@link #getListeningEvents()} method occurs.
8586
* <p>
86-
* Note that your implementation of this function should always perform as little data processing as possible, as the speed at which this callback will fire is at the mercy of the underlying operating system. If you need to collect a large amount of data, application-level buffering should be implemented and data processing should occur on a separate thread.
87+
* Note that your implementation of this function should always perform as little data processing as possible,
88+
* as the speed at which this callback will fire is at the mercy of the underlying operating system. If you need to collect a large amount of data,
89+
* application-level buffering should be implemented and data processing should occur on a separate thread.
90+
* <p>
91+
* Also note that if you are listening for multiple types of events (for instance, {@link SerialPort#LISTENING_EVENT_DATA_AVAILABLE} and
92+
* {@link SerialPort#LISTENING_EVENT_PORT_DISCONNECTED}) at the same time, this callback <i>may<i> be invoked only once for multiple event
93+
* types. As such, you should test for your event of interest by bit-masking and not by testing for equivalence. The following code shows
94+
* the correct way to do this:
95+
* <pre>
96+
* {@literal @}Override
97+
* public void serialEvent(SerialPortEvent serialPortEvent) {
98+
* if ((serialPortEvent.getEventType() & SerialPort.LISTENING_EVENT_PORT_DISCONNECTED) > 0)
99+
* port.closePort();
100+
* else if ((serialPortEvent.getEventType() & SerialPort.LISTENING_EVENT_DATA_AVAILABLE) > 0)
101+
* // ... you get the idea
102+
* }
103+
* </pre>
87104
*
88105
* @param event A {@link SerialPortEvent} object containing information and/or data about the serial events that occurred.
89106
* @see SerialPortEvent

src/test/java/com/fazecast/jSerialComm/SerialPortTest.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ public int getListeningEvents() { return SerialPort.LISTENING_EVENT_PARITY_ERROR
280280
public void serialEvent(SerialPortEvent event)
281281
{
282282
System.out.println("Received event type: " + event.toString());
283-
if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
283+
if ((event.getEventType() & SerialPort.LISTENING_EVENT_DATA_AVAILABLE) > 0)
284284
{
285285
byte[] buffer = new byte[event.getSerialPort().bytesAvailable()];
286286
event.getSerialPort().readBytes(buffer, buffer.length);
@@ -302,20 +302,29 @@ public void serialEvent(SerialPortEvent event)
302302
ubxPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
303303
ubxPort.addDataListener(new SerialPortDataListener() {
304304
@Override
305-
public int getListeningEvents() { return SerialPort.LISTENING_EVENT_DATA_AVAILABLE; }
305+
public int getListeningEvents() { return SerialPort.LISTENING_EVENT_DATA_AVAILABLE | SerialPort.LISTENING_EVENT_PORT_DISCONNECTED; }
306306
@Override
307307
public void serialEvent(SerialPortEvent event)
308308
{
309309
SerialPort comPort = event.getSerialPort();
310-
System.out.println("Available: " + comPort.bytesAvailable() + " bytes.");
311-
byte[] newData = new byte[comPort.bytesAvailable()];
312-
int numRead = comPort.readBytes(newData, newData.length);
313-
System.out.println("Read " + numRead + " bytes.");
310+
if ((event.getEventType() & SerialPort.LISTENING_EVENT_DATA_AVAILABLE) > 0)
311+
{
312+
System.out.println("Available: " + comPort.bytesAvailable() + " bytes.");
313+
byte[] newData = new byte[comPort.bytesAvailable()];
314+
int numRead = comPort.readBytes(newData, newData.length);
315+
System.out.println("Read " + numRead + " bytes.");
316+
}
317+
else if ((event.getEventType() & SerialPort.LISTENING_EVENT_PORT_DISCONNECTED) > 0)
318+
{
319+
System.out.println("Serial port was physically disconnected!");
320+
System.out.println("\nClosing from within event callback: " + comPort.closePort());
321+
}
314322
}
315323
});
316324
try { Thread.sleep(10000); } catch (Exception e) {}
317325
ubxPort.removeDataListener();
318-
System.out.println("\nClosing " + ubxPort.getDescriptivePortName() + ": " + ubxPort.closePort());
326+
if (ubxPort.isOpen())
327+
System.out.println("\nClosing " + ubxPort.getDescriptivePortName() + ": " + ubxPort.closePort());
319328

320329
/*System.out.println("\n\nAttempting to read from two serial ports simultaneously\n");
321330
System.out.println("\nAvailable Ports:\n");

0 commit comments

Comments
 (0)