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