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