3,6 → 3,7 |
import java.io.File; |
import java.io.IOException; |
import java.io.InputStream; |
import java.io.OutputStream; |
import java.nio.file.Files; |
import java.nio.file.Path; |
|
94,6 → 95,36 |
} |
} |
|
private class SerialStateListener implements Runnable{ |
|
private volatile boolean stop; |
private SerialChangeListener listen; |
|
SerialStateListener( SerialChangeListener listen ){ |
stop = false; |
this.listen = listen; |
} |
|
@Override |
public void run() { |
while( !stop ){ |
synchronized( serialListenSync ){ |
try { |
serialListenSync.wait(); |
if( stop ){ |
break; |
} |
listen.serialStateChanged( state ); |
} catch (Exception e) {} |
} |
} |
} |
|
void doStop(){ |
stop = true; |
} |
} |
|
/** |
* Represents the BaudRate that the SerialPort uses. |
*/ |
159,6 → 190,41 |
SOFTWARE |
} |
|
/** |
* Flag to set if you do not want to get any control line notifications |
*/ |
public static final int NO_CONTROL_LINE_CHANGE = 0x00; |
/** |
* Flag to set if you want to get notifications on Data Terminal Ready line change |
*/ |
public static final int CONTROL_LINE_DTR_CHANGE = 0x01; |
/** |
* Flag to set if you want to get notifications on Request To Send line change |
*/ |
public static final int CONTROL_LINE_RTS_CHANGE = 0x02; |
/** |
* Flag to set if you want to get notifications on Carrier Detect line change |
*/ |
public static final int CONTROL_LINE_CD_CHANGE = 0x03; |
/** |
* Flag to set if you want to get notifications on Clear To Send line change |
*/ |
public static final int CONTROL_LINE_CTS_CHANGE = 0x04; |
/** |
* Flag to set if you want to get notifications on Data Set Ready line change |
*/ |
public static final int CONTROL_LINE_DSR_CHANGE = 0x05; |
/** |
* Flag to set if you want to get notifications on Ring Indicagor line change |
*/ |
public static final int CONTROL_LINE_RI_CHANGE = 0x06; |
/** |
* All CONTROL_LINE_XXX_CHANGE values OR'd together |
*/ |
public static final int ALL_CONTROL_LINES = CONTROL_LINE_DTR_CHANGE | |
CONTROL_LINE_RTS_CHANGE | CONTROL_LINE_CD_CHANGE | CONTROL_LINE_CTS_CHANGE | |
CONTROL_LINE_DSR_CHANGE | CONTROL_LINE_RI_CHANGE; |
|
/* The handle to our internal data structure which keeps track of the port settings. |
* We need a special structure, as on windows we have a HANDLE type, which is void*, |
* yet on Linux we have a file descriptor, which is an int. |
165,19 → 231,257 |
* This is not just a pointer to memory, because if we're running on a 64-bit |
* system, then we might have problems putting it in 32-bits. Better safe than sorry. |
*/ |
protected int handle; |
private int handle; |
/* Make sure we don't close ourselves twice */ |
protected boolean closed; |
private boolean closed; |
/* The name of the port that's currently open */ |
private String portName; |
/* Cache of the last gotten serial line state */ |
protected volatile SerialLineState state; |
private volatile SerialLineState state; |
/* The input stream that user code uses to read from the serial port. */ |
private InputStream inputStream; |
/* The buffered serial input stream which filters out events for us. */ |
private BufferedSerialInputStream bis; |
/* The output stream that user code uses to write to the serial port. */ |
private SerialOutputStream outputStream; |
/* Runs in the background to check for serial port events */ |
private SerialStateListener serialListen; |
/* Used for synchronizing serialListen */ |
private Object serialListenSync; |
/* Depending on what control line changes we want to get back, this mask is set. */ |
private int controlLineFlags; |
|
/** |
* Open the specified port, 9600 baud, 8 data bits, 1 stop bit, no parity, no flow control, |
* not ignoring control lines |
* |
* @param portName The name of the port to open |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName ) throws NoSuchPortException, NotASerialPortException { |
this( portName, ALL_CONTROL_LINES ); |
} |
|
/** |
* Open the specified port, 9600 baud, 8 data bits, 1 stop bit, no parity, no flow control, |
* looking for the specified control lines |
* |
* @param portName The name of the port to open |
* @param controlLineFlags The control lines to get a notification for when they change. |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, int controlLineFlags ) throws NoSuchPortException, NotASerialPortException { |
this( portName, BaudRate.B9600, controlLineFlags ); |
} |
|
/** |
* Open up a serial port, but allow the user to keep the current settings of the serial port. |
* Will notify on all control line changes. |
* |
* @param portName The port to open |
* @param keepSettings If true, will simply open the serial port without setting anything. If false, this method |
* acts the same as {@link #SerialPort(String) SerialPort( String portName ) } |
* @throws NoSuchPortException If the port does not exist |
* @throws NotASerialPortException If the port is not in fact a serial port |
*/ |
public SerialPort( String portName, boolean keepSettings ) throws NoSuchPortException, NotASerialPortException { |
this( portName, keepSettings, ALL_CONTROL_LINES ); |
} |
|
/** |
* Open up a serial port, but allow the user to keep the current settings of the serial port. |
* |
* @param portName The port to open |
* @param keepSettings If true, will simply open the serial port without setting anything. If false, this method |
* acts the same as {@link #SerialPort(String) SerialPort( String portName ) } |
* @param controlFlags The control flags to listen for changes for. This is a bitwise-OR of CONTROL_LINE_XXX_CHANGE, or |
* NO_CONTROL_LINE_CHANGE if you don't care about getting notified about the control line changes. |
* @throws NoSuchPortException If the port does not exist |
* @throws NotASerialPortException If the port is not in fact a serial port |
*/ |
public SerialPort( String portName, boolean keepSettings, int controlFlags ) throws NoSuchPortException, NotASerialPortException{ |
if( portName == null ){ |
throw new IllegalArgumentException( "portName must not be null" ); |
} |
|
if( keepSettings ){ |
this.handle = openPort( portName ); |
this.portName = portName; |
if( controlLineFlags == NO_CONTROL_LINE_CHANGE ){ |
inputStream = new SimpleSerialInputStream( handle ); |
}else{ |
SerialInputStream sis = new SerialInputStream( handle ); |
inputStream = sis; |
bis = new BufferedSerialInputStream( sis, this ); |
} |
outputStream = new SerialOutputStream( handle ); |
closed = false; |
this.controlLineFlags = controlFlags; |
|
SerialLineState s = new SerialLineState(); |
int state = getSerialLineStateInternalNonblocking(); |
// do some sort of bitwise operations here.... |
if( ( state & 0x01 ) > 0 ){ |
s.carrierDetect = true; |
} |
if( ( state & (0x01 << 1 ) ) > 0 ){ |
s.clearToSend = true; |
} |
if( ( state & (0x01 << 2 ) ) > 0 ){ |
s.dataSetReady = true; |
} |
if( ( state & (0x01 << 3 ) ) > 0 ){ |
s.dataTerminalReady = true; |
} |
if( ( state & (0x01 << 4 ) ) > 0 ){ |
s.requestToSend = true; |
} |
if( ( state & (0x01 << 5 ) ) > 0 ){ |
s.ringIndicator = true; |
} |
|
this.state = s; |
|
if( controlLineFlags != NO_CONTROL_LINE_CHANGE ){ |
serialListenSync = new Object(); |
new Thread( bis, "BufferedSerialReader-" + portName ).start(); |
} |
}else{ |
doOpenSerialPort( portName, BaudRate.B9600, DataBits.DATABITS_8, |
StopBits.STOPBITS_1, Parity.NONE, FlowControl.NONE, controlFlags ); |
} |
|
|
} |
|
/** |
* Open the specified port, no flow control, 8 data bits |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate ) |
throws NoSuchPortException, NotASerialPortException { |
this( portName, rate, ALL_CONTROL_LINES ); |
} |
|
/** |
* Open the specified port, no flow control |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param controlLines The control lines to be notifie don a change in. |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, int controlLines ) |
throws NoSuchPortException, NotASerialPortException { |
this( portName, rate, DataBits.DATABITS_8, controlLines ); |
} |
|
/** |
* Open the specified port, no flow control |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, DataBits data ) |
throws NoSuchPortException, NotASerialPortException { |
this( portName, rate, data, StopBits.STOPBITS_1, ALL_CONTROL_LINES ); |
} |
|
/** |
* Open the specified port, no flow control |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, DataBits data, int controlLineFlags ) |
throws NoSuchPortException, NotASerialPortException { |
doOpenSerialPort( portName, rate, data, |
StopBits.STOPBITS_1, Parity.NONE, FlowControl.NONE, controlLineFlags ); |
} |
|
/** |
* Open the specified port, no parity or flow control |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @param stop The number of stop bits |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, DataBits data, StopBits stop ) |
throws NoSuchPortException, NotASerialPortException { |
doOpenSerialPort( portName, rate, data, |
stop, Parity.NONE, FlowControl.NONE, ALL_CONTROL_LINES ); |
} |
|
/** |
* Open the specified port, no parity or flow control |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @param stop The number of stop bits |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, int controlFlags ) |
throws NoSuchPortException, NotASerialPortException { |
doOpenSerialPort( portName, rate, data, |
stop, Parity.NONE, FlowControl.NONE, controlFlags ); |
} |
|
/** |
* Open the specified port, no flow control, with all control line flags |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @param stop The number of stop bits |
* @param parity The parity of the line |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity ) |
throws NoSuchPortException, NotASerialPortException { |
doOpenSerialPort( portName, rate, data, stop, parity, FlowControl.NONE, ALL_CONTROL_LINES ); |
} |
|
/** |
* Open the specified port, no flow control, with the specified control line flags |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @param stop The number of stop bits |
* @param parity The parity of the line |
* @param controlLineFlags A bitwise OR of any the CONTORL_LINE_XXX_CHANGE flags, or NO_CONTROL_LINE_CHANGE |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity, int controlLineFlags ) |
throws NoSuchPortException, NotASerialPortException { |
this( portName, rate, data, stop, parity, FlowControl.NONE, controlLineFlags ); |
} |
|
/** |
* Open the specified port, defining all options |
* |
* @param portName The name of the port to open |
* @param rate The Buad Rate to open this port at |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @param stop The number of stop bits |
* @param parity The parity of the line |
185,8 → 489,35 |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
protected SerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity, FlowControl flow ) |
public SerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity, FlowControl flow ) |
throws NoSuchPortException, NotASerialPortException { |
this( portName, rate, data, stop, parity, flow, ALL_CONTROL_LINES ); |
} |
|
/** |
* Open the specified port, defining all options |
* |
* @param portName The name of the port to open |
* @param rate The Baud Rate to open this port at |
* @param data The number of data bits |
* @param stop The number of stop bits |
* @param parity The parity of the line |
* @param flow The flow control of the line |
* @param controlFlags The control flags to listen for changes for. This is a bitwise-OR of CONTROL_LINE_XXX_CHANGE, or |
* NO_CONTROL_LINE_CHANGE if you don't care about getting notified about the control line changes. |
* @throws NoSuchPortException If this port does not exist |
* @throws NotASerialPortException If the specified port is not a serial port |
*/ |
public SerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity, FlowControl flow, int controlFlags ) |
throws NoSuchPortException, NotASerialPortException { |
doOpenSerialPort( portName, rate, data, stop, parity, flow, controlFlags ); |
} |
|
/** |
* The actual method that opens up the serial port, unless we are keeping our settings. |
*/ |
private void doOpenSerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity, FlowControl flow, int controlFlags ) |
throws NoSuchPortException, NotASerialPortException { |
int myRate = 0; |
int myData = 0; |
int myStop = 0; |
194,6 → 525,7 |
int myFlow = 0; |
SerialLineState s; |
int state; |
SerialInputStream sis; |
|
//Check for null values in our arguments |
if( portName == null ){ |
223,6 → 555,7 |
//Okay, looks like we're good! |
this.portName = portName; |
closed = false; |
this.controlLineFlags = controlFlags; |
|
switch( rate ){ |
case B0 : myRate = 0; break; |
268,6 → 601,14 |
} |
|
handle = openPort(portName, myRate, myData, myStop, myParity, myFlow); |
if( controlLineFlags == NO_CONTROL_LINE_CHANGE ){ |
inputStream = new SimpleSerialInputStream( handle ); |
}else{ |
sis = new SerialInputStream( handle ); |
inputStream = sis; |
bis = new BufferedSerialInputStream( sis, this ); |
} |
outputStream = new SerialOutputStream( handle ); |
|
s = new SerialLineState(); |
state = getSerialLineStateInternalNonblocking(); |
291,30 → 632,13 |
} |
|
this.state = s; |
} |
|
/** |
* Open up a serial port, but allow the user to keep the current settings of the serial port. |
* |
* @param portName The port to open |
* @param keepSettings If true, will simply open the serial port without setting anything. If false, this method |
* acts the same as {@link #SerialPort(String) SerialPort( String portName ) } |
* @throws NoSuchPortException If the port does not exist |
* @throws NotASerialPortException If the port is not in fact a serial port |
*/ |
protected SerialPort( String portName, boolean keepSettings ) throws NoSuchPortException, NotASerialPortException{ |
if( portName == null ){ |
throw new IllegalArgumentException( "portName must not be null" ); |
} |
|
if( keepSettings ){ |
this.handle = openPort( portName ); |
this.portName = portName; |
this.closed = false; |
}else{ |
this.handle = openPort( portName, 9600, 8, 1, 0, 0 ); |
if( controlLineFlags != NO_CONTROL_LINE_CHANGE ){ |
serialListenSync = new Object(); |
new Thread( bis, "BufferedSerialReader-" + portName ).start(); |
} |
} |
|
|
/** |
* Set the Baud Rate for this port. |
366,10 → 690,45 |
if( closed ) return; |
closed = true; |
doClose(); |
if( serialListen != null ){ |
serialListen.doStop(); |
} |
|
if( serialListenSync != null ){ |
synchronized( serialListenSync ){ |
serialListenSync.notify(); |
} |
} |
} |
|
/** |
* Get the input stream used to talk to this device. |
*/ |
public InputStream getInputStream(){ |
if( isClosed() ){ |
throw new IllegalStateException( "Cannot get the input stream once the port has been closed." ); |
} |
|
if( controlLineFlags == NO_CONTROL_LINE_CHANGE ){ |
return inputStream; |
} |
|
return bis; |
} |
|
/** |
* Get the OutputStream used to talk to this device. |
*/ |
public OutputStream getOutputStream(){ |
if( isClosed() ){ |
throw new IllegalStateException( "Cannot get the output stream once the port has been closed." ); |
} |
|
return outputStream; |
} |
|
|
/** |
* Set the stop bits of the serial port, after the port has been opened. |
* |
* @param stop |
500,6 → 859,8 |
// this.postSerialChangedEvent( state ); |
// } |
} |
|
|
|
/** |
* Get the baud rate of the serial port. |
666,6 → 1027,15 |
* @param listen The listener which gets events |
*/ |
public void setSerialChangeListener( SerialChangeListener listen ){ |
if( serialListen != null ){ |
serialListen.doStop(); |
} |
|
if( listen != null && !(controlLineFlags == NO_CONTROL_LINE_CHANGE) ){ |
serialListen = new SerialStateListener( listen ); |
new Thread( serialListen, "SerialListen-" + portName ).start(); |
} |
|
} |
|
/** |
676,6 → 1046,52 |
return portName; |
} |
|
/** |
* This method is called when the state of the serial lines is changed. |
* |
* @param newState |
*/ |
void postSerialChangedEvent( SerialLineState newState ){ |
if( !state.equals( newState ) ){ |
SerialLineState oldState = state; |
boolean update = false; |
state = newState; |
|
//At this point, we know what has changed, but we must check our bitmask to see if we should |
//propogate this change back up to the interested class. |
if( oldState.carrierDetect != newState.carrierDetect && |
(controlLineFlags & CONTROL_LINE_CD_CHANGE) != 0 ){ |
update = true; |
} |
if( oldState.clearToSend != newState.clearToSend && |
(controlLineFlags & CONTROL_LINE_CTS_CHANGE) != 0){ |
update = true; |
} |
if( oldState.dataSetReady != newState.dataSetReady && |
(controlLineFlags & CONTROL_LINE_DSR_CHANGE) != 0 ){ |
update = true; |
} |
if( oldState.dataTerminalReady != newState.dataTerminalReady && |
(controlLineFlags & CONTROL_LINE_DTR_CHANGE) != 0 ){ |
update = true; |
} |
if( oldState.requestToSend != newState.requestToSend && |
(controlLineFlags & CONTROL_LINE_RTS_CHANGE) != 0 ){ |
update = true; |
} |
if( oldState.ringIndicator != newState.ringIndicator && |
(controlLineFlags & CONTROL_LINE_RI_CHANGE) != 0 ){ |
update = true; |
} |
|
if( update ){ |
synchronized( serialListenSync ){ |
serialListenSync.notify(); |
} |
} |
} |
} |
|
/** |
* Open the specified port, return an internal handle to the data structure for this port. |
* |