001package jmri.jmrix.purejavacomm;
002
003import java.io.InputStream;
004import java.io.IOException;
005import java.io.OutputStream;
006import java.util.TooManyListenersException;
007
008import jmri.jmrix.AbstractSerialPortController;
009
010/**
011 * Serial port.
012 */
013public class SerialPort {
014
015    public static final int DATABITS_8 = 8;
016    public static final int PARITY_NONE = 0;
017    public static final int PARITY_ODD = 1;
018    public static final int PARITY_EVEN = 2;
019    public static final int STOPBITS_1 = 1;
020    public static final int STOPBITS_2 = 2;
021    public static final int FLOWCONTROL_NONE = 0;
022    public static final int FLOWCONTROL_RTSCTS_IN = 1;
023    public static final int FLOWCONTROL_RTSCTS_OUT = 2;
024
025    private jmri.jmrix.SerialPort _serialPort;
026    private SerialPortEventListener _eventListener;
027    private boolean _threadStarted = false;
028    private Thread _inputThread;
029    private volatile boolean _notifyOnDataAvailable;
030
031    public SerialPort(jmri.jmrix.SerialPort serialPort) {
032        this._serialPort = serialPort;
033
034        Runnable runnable = () -> {
035            final int TIMEOUT = Integer.getInteger("purejavacomm.pollperiod", 10);
036            InputStream inputStream = _serialPort.getInputStream();
037
038            while (true) {
039                try {
040                    while (_notifyOnDataAvailable && (_eventListener != null)
041                            && (inputStream.available() > 0)) {
042                        _eventListener.serialEvent(new SerialPortEvent(
043                                this, SerialPortEvent.DATA_AVAILABLE, false, true));
044                    }
045                    Thread.sleep(TIMEOUT);
046                } catch (InterruptedException e) {
047                    log.error("Thread interrupted");
048                    break;
049                } catch (IOException e) {
050                    log.error("An IO error occurred", e);
051                    break;
052                }
053            }
054        };
055        _inputThread = jmri.util.ThreadingUtil.newThread(
056                runnable, "jmri.jmrix.purejavacomm.SerialPort");
057        _inputThread.setDaemon(true);
058    }
059
060    private void setParity(int parity) {
061        switch (parity) {
062            case PARITY_NONE:
063                _serialPort.setParity(jmri.jmrix.SerialPort.Parity.NONE);
064                break;
065
066            case PARITY_EVEN:
067                _serialPort.setParity(jmri.jmrix.SerialPort.Parity.EVEN);
068                break;
069
070            case PARITY_ODD:
071                _serialPort.setParity(jmri.jmrix.SerialPort.Parity.ODD);
072                break;
073
074            default:
075                throw new IllegalArgumentException("Unknown parity: "+Integer.toString(parity));
076        }
077    }
078
079    public void setSerialPortParams(int baudRate, int dataBits, int stopBits, int parity) throws UnsupportedCommOperationException {
080        _serialPort.setBaudRate(baudRate);
081        _serialPort.setNumDataBits(dataBits);
082        _serialPort.setNumStopBits(stopBits);
083        setParity(parity);
084    }
085
086    public void addEventListener(SerialPortEventListener listener) throws TooManyListenersException {
087        if (listener == null) {
088            throw new IllegalArgumentException("listener cannot be null");
089        }
090        if (this._eventListener != null) {
091            throw new TooManyListenersException();
092        }
093        this._eventListener = listener;
094        if (!_threadStarted) {
095            _threadStarted = true;
096            _inputThread.start();
097        }
098    }
099
100    public void notifyOnDataAvailable(boolean value) {
101        _notifyOnDataAvailable = value;
102    }
103
104    public void setFlowControlMode(int mode) throws UnsupportedCommOperationException {
105        switch (mode) {
106            case FLOWCONTROL_NONE:
107                _serialPort.setFlowControl(AbstractSerialPortController.FlowControl.NONE);
108                break;
109
110            case (FLOWCONTROL_RTSCTS_IN | FLOWCONTROL_RTSCTS_OUT):
111                _serialPort.setFlowControl(AbstractSerialPortController.FlowControl.RTSCTS);
112                break;
113
114            default:
115                throw new IllegalArgumentException("Unknown flow control mode: "+Integer.toString(mode));
116        }
117    }
118
119    public InputStream getInputStream() throws IOException {
120        return _serialPort.getInputStream();
121    }
122
123    public OutputStream getOutputStream() throws IOException {
124        return _serialPort.getOutputStream();
125    }
126
127    public int getBaudRate() {
128        return _serialPort.getBaudRate();
129    }
130
131    public void setDTR(boolean value) {
132        if (value) {
133            _serialPort.setDTR();
134        } else {
135            _serialPort.clearDTR();
136        }
137    }
138
139    public void setRTS(boolean value) {
140        if (value) {
141            _serialPort.setRTS();
142        } else {
143            _serialPort.clearRTS();
144        }
145    }
146
147    public boolean isDTR() {
148        return _serialPort.getDTR();
149    }
150
151    public boolean isRTS() {
152        return _serialPort.getRTS();
153    }
154
155    public boolean isDSR() {
156        return _serialPort.getDSR();
157    }
158
159    public boolean isCTS() {
160        return _serialPort.getCTS();
161    }
162
163    public boolean isCD() {
164        return _serialPort.getDCD();
165    }
166
167    public boolean isRI() {
168        return _serialPort.getRI();
169    }
170
171    public boolean isReceiveTimeoutEnabled() {
172        return false;   // Not implemented
173    }
174
175    public int getReceiveTimeout() {
176        return 0;   // Not implemented
177    }
178
179    public void close() {
180        _serialPort.closePort();
181    }
182
183    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SerialPort.class);
184}