Subversion Repositories Programming Utils

Rev

Rev 97 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.rm5248.serial;

import java.io.Closeable;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * A SerialPort object represents a SerialPort object on the system.  This implementation uses
 * the STREAMS(IO) based method of accessing a serial port.  
 *
 * When creating a SerialPort object, you give the SerialPort the name of the port that you
 * wish to open.
 *
 * When opening a SerialPort and setting values, an {@link IllegalArgumentException} may be thrown
 * if any of the values are NULL.
 *
 * When getting and settings the properties of the SerialPort using one of the {@code getXXX() }
 * or {@code setXXX() } methods, an IllegalStateException will be thrown if the port has been closed.
 *
 * @author rm5248
 *
 */

public class IOSerialPort extends SerialPort  implements Closeable  {

        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;
                }
        }

        /* 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 if we have a BufferedSerialInputStream or a normal InputStream,
         * this will be set.
         * Note that 'ignoring control lines' really means that we ignore CHANGES on
         * the control lines; it should still be possible to get the state of the lines,
         * you just won't be notified immediately.
         */

        private boolean ignoringControlLines;

        /**
         * 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 IOSerialPort( String portName ) throws NoSuchPortException, NotASerialPortException {
                this( portName, BaudRate.B9600 );
        }

        /**
         * 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 ignoreControlLines If true, all control line changes will be ignored and not propogated
         * back through a {@link SerialChangeListener}.  
         * @throws NoSuchPortException If the port does not exist
         * @throws NotASerialPortException If the port is not in fact a serial port
         */

        public IOSerialPort( String portName, boolean keepSettings, boolean ignoreControlLines ) throws NoSuchPortException, NotASerialPortException{
                super( portName, keepSettings );

                this.ignoringControlLines = ignoreControlLines;
                if( ignoreControlLines ){
                        SimpleSerialInputStream ssis = new SimpleSerialInputStream( handle );
                        inputStream = ssis;
                }else{
                        SerialInputStream sis = new SerialInputStream( handle );
                        inputStream = sis;
                        bis = new BufferedSerialInputStream( sis, this );
                }
                outputStream = new SerialOutputStream( handle );

                SerialLineState s = new SerialLineState();
                int state = 0;//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( !ignoreControlLines ){
                        serialListenSync = new Object();
                        new Thread( bis, "BufferedSerialReader-" + portName ).start();
                }
        }

        /**
         * 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
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName, BaudRate rate )throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, DataBits.DATABITS_8 );
        }

        /**
         * 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 IOSerialPort( String portName, BaudRate rate, DataBits data ) throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, data, StopBits.STOPBITS_1 );
        }

        /**
         * 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 IOSerialPort( String portName, BaudRate rate, DataBits data, StopBits stop ) throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, data, stop, Parity.NONE );
        }

        /**
         * 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
         * @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 IOSerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity ) throws NoSuchPortException, NotASerialPortException {
                this( portName, rate, data, stop, parity, FlowControl.NONE );
        }

        /**
         * 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 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
         * @throws NoSuchPortException If this port does not exist
         * @throws NotASerialPortException If the specified port is not a serial port
         */

        public IOSerialPort( String portName, BaudRate rate, DataBits data, StopBits stop, Parity parity, FlowControl flow ) throws NoSuchPortException, NotASerialPortException {
                super( portName, rate, data, stop, parity, flow );
                SerialInputStream sis;
                SerialLineState s;
                int state;

                sis = new SerialInputStream( handle );
                inputStream = sis;
                bis = new BufferedSerialInputStream( sis, this );
                outputStream = new SerialOutputStream( handle );

                s = new SerialLineState();
                state = getSerialLineStateInternalNonblocking();
                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;
                serialListenSync = new Object();
                new Thread( bis, "BufferedSerialReader" ).start();
        }

        /**
         * 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( ignoringControlLines ){
                        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;
        }

        /* (non-Javadoc)
         * @see com.rm5248.serial.SerialPortI#close()
         */

        @Override
        public void close(){
                if( isClosed() ) return;
                super.close();
                if( serialListen != null ){
                        serialListen.doStop();
                }
               
                if( serialListenSync != null ){
                        synchronized( serialListenSync ){
                                serialListenSync.notify();
                        }
                }
        }

        public String toString(){
                return "IOSerial Port " + getPortName() + ":" + getBaudRate();
        }


        /**
         * This method is called when the state of the serial lines is changed.
         *
         * @param newState
         */

        void postSerialChangedEvent( SerialLineState newState ){
                if( !state.equals( newState ) ){
                        state = newState;

                        synchronized( serialListenSync ){
                                serialListenSync.notify();
                        }
                }
        }

        @Override
        public void setSerialChangeListener( SerialChangeListener listen ){
                if( serialListen != null ){
                        serialListen.doStop();
                }

                if( listen != null && !ignoringControlLines ){
                        serialListen = new SerialStateListener( listen );
                        new Thread( serialListen, "SerialListen" ).start();
                }

        }
}