001package jmri.jmrix.ztc.ztc611;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.io.InputStream;
007import java.util.Arrays;
008import jmri.jmrix.lenz.LenzCommandStation;
009import jmri.jmrix.lenz.XNetInitializationManager;
010import jmri.jmrix.lenz.XNetSerialPortController;
011import jmri.jmrix.lenz.XNetTrafficController;
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014import purejavacomm.CommPortIdentifier;
015import purejavacomm.NoSuchPortException;
016import purejavacomm.PortInUseException;
017import purejavacomm.SerialPort;
018import purejavacomm.UnsupportedCommOperationException;
019
020/**
021 * Provide access to XpressNet via a ZTC611 connected via an FTDI virtual comm
022 * port.
023 *
024 * @author Bob Jacobsen Copyright (C) 2002
025 * @author Paul Bender, Copyright (C) 2003-2017
026 */
027public class ZTC611Adapter extends XNetSerialPortController {
028
029    public ZTC611Adapter() {
030        super();
031        option1Name = "FlowControl"; // NOI18N
032        options.put(option1Name, new Option(Bundle.getMessage("XconnectionUsesLabel", Bundle.getMessage("CSTypeZtc640")), validOption1));
033    }
034
035    @Override
036    public String openPort(String portName, String appName) {
037        // open the port in XpressNet mode, check ability to set moderators
038        try {
039            // get and open the primary port
040            CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier(portName);
041            try {
042                activeSerialPort = (SerialPort) portID.open(appName, 2000);  // name of program, msec to wait
043            } catch (PortInUseException p) {
044                return handlePortBusy(p, portName, log);
045            }
046            // try to set it for XNet
047            try {
048                setSerialPort();
049            } catch (UnsupportedCommOperationException e) {
050                log.error("Cannot set serial parameters on port {}: {}", portName, e.getMessage());
051                return "Cannot set serial parameters on port " + portName + ": " + e.getMessage();
052            }
053
054            // set timeout
055            activeSerialPort.enableReceiveTimeout(10);
056            log.debug("Serial timeout was observed as: {} {}", activeSerialPort.getReceiveTimeout(),
057                    activeSerialPort.isReceiveTimeoutEnabled());
058
059            // get and save stream
060            serialStream = activeSerialPort.getInputStream();
061
062            // purge contents, if any
063            purgeStream(serialStream);
064
065            // report status?
066            if (log.isInfoEnabled()) {
067                // report now
068                log.info("{} port opened at {} baud with DTR: {} RTS: {} DSR: {} CTS: {}  CD: {}", portName, activeSerialPort.getBaudRate(), activeSerialPort.isDTR(), activeSerialPort.isRTS(), activeSerialPort.isDSR(), activeSerialPort.isCTS(), activeSerialPort.isCD());
069            }
070            if (log.isDebugEnabled()) {
071                // report additional status
072                log.debug(" port flow control shows {}", activeSerialPort.getFlowControlMode() == SerialPort.FLOWCONTROL_RTSCTS_OUT ? "hardware flow control" : "no flow control"); // NOI18N
073
074                // log events
075                setPortEventLogging(activeSerialPort);
076            }
077
078            opened = true;
079
080        } catch (NoSuchPortException p) {
081            return handlePortNotFound(p, portName, log);
082        } catch (IOException ex) {
083            log.error("IO exception while opening port {}", portName, ex);
084            return "IO Exception while opening port " + portName + ": " + ex;
085        } catch (UnsupportedCommOperationException ucex) {
086            log.error("unsupported Comm Operation exception while opening port {}", portName, ucex);
087            return "Unsupported Comm Exception while opening port " + portName + ": " + ucex;
088        }
089
090        return null; // normal operation
091    }
092
093    /**
094     * set up all of the other objects to operate with a ZTC611 connected to
095     * this port
096     */
097    @Override
098    public void configure() {
099        // connect to a packetizing traffic controller
100        XNetTrafficController packets = new ZTC611XNetPacketizer(new LenzCommandStation());
101        packets.connectPort(this);
102
103        // start operation
104        // packets.startThreads();
105        this.getSystemConnectionMemo().setXNetTrafficController(packets);
106        new XNetInitializationManager()
107                .memo(this.getSystemConnectionMemo())
108                .setDefaults()
109                .turnoutManager(ZTC611XNetTurnoutManager.class)
110                .init();
111    }
112
113    // base class methods for the XNetSerialPortController interface
114    @Override
115    public DataInputStream getInputStream() {
116        if (!opened) {
117            log.error("getInputStream called before load(), stream not available");
118            return null;
119        }
120        return new DataInputStream(serialStream);
121    }
122
123    @Override
124    public DataOutputStream getOutputStream() {
125        if (!opened) {
126            log.error("getOutputStream called before load(), stream not available");
127        }
128        try {
129            return new DataOutputStream(activeSerialPort.getOutputStream());
130        } catch (IOException e) {
131            log.error("getOutputStream exception: ", e.getMessage());
132        }
133        return null;
134    }
135
136    @Override
137    public boolean status() {
138        return opened;
139    }
140
141    /**
142     * Local method to do specific configuration.
143     *
144     * @throws UnsupportedCommOperationException if there is an error
145     *                                                  configuring the port
146     */
147    protected void setSerialPort() throws UnsupportedCommOperationException {
148        // find the baud rate value, configure comm options
149        int baud = currentBaudNumber(mBaudRate);
150        activeSerialPort.setSerialPortParams(baud,
151                SerialPort.DATABITS_8,
152                SerialPort.STOPBITS_1,
153                SerialPort.PARITY_NONE);
154
155        // find and configure flow control
156        int flow = 0; // default, but also deftaul for getOptionState(option1Name)
157        if (!getOptionState(option1Name).equals(validOption1[0])) {
158            flow = SerialPort.FLOWCONTROL_RTSCTS_OUT;
159        }
160        configureLeadsAndFlowControl(activeSerialPort, flow);
161    }
162
163    /**
164     * {@inheritDoc}
165     */
166    @Override
167    public String[] validBaudRates() {
168        return Arrays.copyOf(validSpeeds, validSpeeds.length);
169    }
170
171    /**
172     * {@inheritDoc}
173     */
174    @Override
175    public int[] validBaudNumbers() {
176        return Arrays.copyOf(validSpeedValues, validSpeedValues.length);
177    }
178
179    protected final String[] validSpeeds = new String[]{Bundle.getMessage("Baud9600")};
180    protected final int[] validSpeedValues = new int[]{19200};
181
182    @Override
183    public int defaultBaudIndex() {
184        return 0;
185    }
186
187    // meanings are assigned to these above, so make sure the order is consistent
188    protected final String[] validOption1 = new String[]{Bundle.getMessage("FlowOptionNoRecomm"), Bundle.getMessage("FlowOptionHw")};
189
190    private boolean opened = false;
191    InputStream serialStream = null;
192
193    @Deprecated
194    static public ZTC611Adapter instance() {
195        if (mInstance == null) {
196            mInstance = new ZTC611Adapter();
197        }
198        return mInstance;
199    }
200    static volatile ZTC611Adapter mInstance = null;
201
202    private final static Logger log = LoggerFactory.getLogger(ZTC611Adapter.class);
203
204}