001package jmri.jmrix.tams.serialdriver;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.io.InputStream;
007import java.util.Arrays;
008import jmri.jmrix.tams.TamsPortController;
009import jmri.jmrix.tams.TamsSystemConnectionMemo;
010import jmri.jmrix.tams.TamsTrafficController;
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013import purejavacomm.CommPortIdentifier;
014import purejavacomm.NoSuchPortException;
015import purejavacomm.PortInUseException;
016import purejavacomm.SerialPort;
017import purejavacomm.UnsupportedCommOperationException;
018
019/**
020 * Implements SerialPortAdapter for the TAMS system.
021 * <p>
022 * This connects an TAMS command station via a serial com port.
023 * <p>
024 * Based on work by Bob Jacobsen
025 *
026 * @author Kevin Dickerson Copyright (C) 2012
027 */
028public class SerialDriverAdapter extends TamsPortController {
029
030    SerialPort activeSerialPort = null;
031
032    public SerialDriverAdapter() {
033        super(new TamsSystemConnectionMemo());
034        setManufacturer(jmri.jmrix.tams.TamsConnectionTypeList.TAMS);
035    }
036
037    @Override
038    public String openPort(String portName, String appName) {
039        // open the port, check ability to set moderators
040        try {
041            // get and open the primary port
042            CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier(portName);
043            try {
044                activeSerialPort = (SerialPort) portID.open(appName, 2000);  // name of program, msec to wait
045            } catch (PortInUseException p) {
046                return handlePortBusy(p, portName, log);
047            }
048
049            // try to set it for communication via SerialDriver
050            try {
051                // find the baud rate value, configure comm options
052                int baud = currentBaudNumber(mBaudRate);
053                activeSerialPort.setSerialPortParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
054            } catch (UnsupportedCommOperationException e) {
055                log.error("Cannot set serial parameters on port {}: {}", portName, e.getMessage());
056                return "Cannot set serial parameters on port " + portName + ": " + e.getMessage();
057            }
058
059            // Hardware flow control
060            //configureLeadsAndFlowControl(activeSerialPort, SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT);
061
062            // Xon/Xoff flow control
063            configureLeadsAndFlowControl(activeSerialPort, 0);
064
065            // set timeout
066            try {
067                activeSerialPort.enableReceiveTimeout(50);  // Set to 50 was 10 mSec timeout before sending chars
068                log.debug("Serial timeout was observed as: {} {}", activeSerialPort.getReceiveTimeout(),
069                        activeSerialPort.isReceiveTimeoutEnabled());
070            } catch (Exception et) {
071                log.info("failed to set serial timeout: ", et);
072            }
073            // get and save stream
074            serialStream = activeSerialPort.getInputStream();
075
076            // purge contents, if any
077            purgeStream(serialStream);
078
079            if (log.isInfoEnabled()) {
080                log.info("{} port opened at {} baud, sees  DTR: {} RTS: {} DSR: {} CTS: {}  CD: {}", portName, activeSerialPort.getBaudRate(), activeSerialPort.isDTR(), activeSerialPort.isRTS(), activeSerialPort.isDSR(), activeSerialPort.isCTS(), activeSerialPort.isCD());
081            }
082
083            // report status
084            if (log.isInfoEnabled()) {
085                log.info("TAMS {} port opened at {} baud", portName,
086                        activeSerialPort.getBaudRate());
087            }
088            opened = true;
089
090        } catch (NoSuchPortException p) {
091            return handlePortNotFound(p, portName, log);
092        } catch (IOException ex) {
093            log.error("Unexpected exception while opening port {}", portName, ex);
094            return "Unexpected error while opening port " + portName + ": " + ex;
095        }
096
097        return null; // indicates OK return
098    }
099
100    /**
101     * set up all of the other objects to operate with an NCE command station
102     * connected to this port
103     */
104    @Override
105    public void configure() {
106        TamsTrafficController tc = new TamsTrafficController();
107        this.getSystemConnectionMemo().setTamsTrafficController(tc);
108        tc.setAdapterMemo(this.getSystemConnectionMemo());
109
110        tc.connectPort(this);
111
112        this.getSystemConnectionMemo().configureManagers();
113    }
114
115    // base class methods for the TamsPortController interface
116    @Override
117    public DataInputStream getInputStream() {
118        if (!opened) {
119            log.error("getInputStream called before load(), stream not available");
120            return null;
121        }
122        return new DataInputStream(serialStream);
123    }
124
125    @Override
126    public DataOutputStream getOutputStream() {
127        if (!opened) {
128            log.error("getOutputStream called before load(), stream not available");
129        }
130        try {
131            return new DataOutputStream(activeSerialPort.getOutputStream());
132        } catch (java.io.IOException e) {
133            log.error("getOutputStream exception: ", e);
134        }
135        return null;
136    }
137
138    @Override
139    public boolean status() {
140        return opened;
141    }
142
143    /**
144     * {@inheritDoc}
145     */
146    @Override
147    public String[] validBaudRates() {
148        return Arrays.copyOf(validSpeeds, validSpeeds.length);
149    }
150
151    /**
152     * {@inheritDoc}
153     */
154    @Override
155    public int[] validBaudNumbers() {
156        return Arrays.copyOf(validSpeedValues, validSpeedValues.length);
157    }
158
159    private final String[] validSpeeds = new String[]{Bundle.getMessage("Baud57600"),
160            Bundle.getMessage("Baud2400"), Bundle.getMessage("Baud9600"),
161            Bundle.getMessage("Baud19200")};
162    private final int[] validSpeedValues = new int[]{57600, 2400, 9600, 19200};
163
164    @Override
165    public int defaultBaudIndex() {
166        return 0;
167    }
168
169    // private control members
170    private boolean opened = false;
171    InputStream serialStream = null;
172
173    private final static Logger log = LoggerFactory.getLogger(SerialDriverAdapter.class);
174
175}