001package jmri.jmrix.lenz.ztc640;
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 ZTC640 connected via an FTDI virtual comm
022 * port. Normally controlled by the lenz.ztc640.ZTC640Frame class.
023 *
024 * @author Bob Jacobsen Copyright (C) 2002
025 * @author Paul Bender, Copyright (C) 2003-2010
026 */
027public class ZTC640Adapter extends XNetSerialPortController {
028
029    public ZTC640Adapter() {
030        super();
031        option1Name = "FlowControl"; // NOI18N
032        options.put(option1Name, new Option(Bundle.getMessage("ZTC640ConnectionLabel"), 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            try {
055                // set timeout
056                activeSerialPort.enableReceiveTimeout(10);
057            } catch (UnsupportedCommOperationException ex) {
058                log.error("Cannot set receive timeout on port {}: {}", portName, ex.getMessage());
059                return "Cannot set receive timeout on port " + portName + ": " + ex.getMessage();
060            }
061            log.debug("Serial timeout was observed as: {} {}", activeSerialPort.getReceiveTimeout(),
062                    activeSerialPort.isReceiveTimeoutEnabled());
063
064            // get and save stream
065            serialStream = activeSerialPort.getInputStream();
066
067            // purge contents, if any
068            purgeStream(serialStream);
069
070            // report status?
071            if (log.isInfoEnabled()) {
072                // report now
073                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());
074            }
075            if (log.isDebugEnabled()) {
076                // report additional status
077                log.debug(" port flow control shows {}", activeSerialPort.getFlowControlMode() == SerialPort.FLOWCONTROL_RTSCTS_OUT ? "hardware flow control" : "no flow control"); // NOI18N
078
079                // log events
080                setPortEventLogging(activeSerialPort);
081            }
082
083            opened = true;
084
085        } catch (NoSuchPortException p) {
086            return handlePortNotFound(p, portName, log);
087        } catch (IOException ex) {
088            log.error("Unexpected exception while opening port {}", portName, ex);
089            return "IO Exception while opening port " + portName + ": " + ex;
090        }
091
092        return null; // normal operation
093    }
094
095    /**
096     * Set up all of the other objects to operate with a ZTC640 connected to
097     * this port.
098     */
099    @Override
100    public void configure() {
101        // connect to a packetizing traffic controller
102        XNetTrafficController packets = new ZTC640XNetPacketizer(new LenzCommandStation());
103        packets.connectPort(this);
104
105        this.getSystemConnectionMemo().setXNetTrafficController(packets);
106        new XNetInitializationManager()
107                .memo(this.getSystemConnectionMemo())
108                .setDefaults()
109                .versionCheck()
110                .setTimeout(30000)
111                .init();
112    }
113
114    // base class methods for the XNetSerialPortController interface
115
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 (IOException e) {
133            log.error("getOutputStream exception: {}", e.getMessage());
134        }
135        return null;
136    }
137
138    @Override
139    public boolean status() {
140        return opened;
141    }
142
143    /**
144     * Local method to do specific configuration.
145     * @throws UnsupportedCommOperationException if the underlying port can't comply with the settings
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        int flow = 0; // default, but also default for getOptionState(option1Name)
156        if (!getOptionState(option1Name).equals(validOption1[0])) {
157            flow = SerialPort.FLOWCONTROL_RTSCTS_OUT;
158        }
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("Baud19200")};
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    InputStream serialStream = null;
191
192    private static final Logger log = LoggerFactory.getLogger(ZTC640Adapter.class);
193
194}