001package jmri.jmrix.zimo.mx1;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.io.InputStream;
007import java.util.Arrays;
008import jmri.jmrix.zimo.Mx1CommandStation;
009import jmri.jmrix.zimo.Mx1Packetizer;
010import jmri.jmrix.zimo.Mx1PortController;
011import jmri.jmrix.zimo.Mx1SystemConnectionMemo;
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 Zimo's MX-1 on an attached serial com port. Adapted for
022 * use with Zimo MX-1 by Sip Bosch.
023 *
024 * @author Bob Jacobsen Copyright (C) 2002
025 */
026public class Mx1Adapter extends Mx1PortController {
027
028    public Mx1Adapter() {
029        super(new Mx1SystemConnectionMemo());
030        option1Name = "FlowControl"; // NOI18N
031        options.put(option1Name, new Option("MX-1 connection uses : ", validOption1));
032        this.manufacturerName = jmri.jmrix.zimo.Mx1ConnectionTypeList.ZIMO;
033    }
034
035    SerialPort activeSerialPort = null;
036
037    @Override
038    public String openPort(String portName, String appName) {
039        // open the port in MX-1 mode, 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            // try to set it for Can Net
049            try {
050                setSerialPort();
051            } catch (UnsupportedCommOperationException e) {
052                log.error("Cannot set serial parameters on port {}: {}", portName, e.getMessage());
053                return "Cannot set serial parameters on port " + portName + ": " + e.getMessage();
054            }
055
056            log.debug("Serial timeout was observed as: {} {}", activeSerialPort.getReceiveTimeout(), activeSerialPort.isReceiveTimeoutEnabled());
057
058            // get and save stream
059            serialStream = activeSerialPort.getInputStream();
060
061            // purge contents, if any
062            purgeStream(serialStream);
063
064            // report status?
065            if (log.isInfoEnabled()) {
066                // report now
067                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());
068            }
069            if (log.isDebugEnabled()) {
070                // report additional status
071                log.debug(" port flow control shows {}", activeSerialPort.getFlowControlMode() == SerialPort.FLOWCONTROL_RTSCTS_OUT ? "hardware flow control" : "no flow control"); // NOI18N
072
073                // log events
074                setPortEventLogging(activeSerialPort);
075            }
076
077            opened = true;
078
079        } catch (NoSuchPortException p) {
080            return handlePortNotFound(p, portName, log);
081        } catch (IOException ex) {
082            log.error("Unexpected exception while opening port {} trace follows: ", portName, ex);
083            return "Unexpected error while opening port " + portName + ": " + ex;
084        }
085
086        return null; // normal operation
087    }
088
089    /**
090     * Can the port accept additional characters? The state of CTS determines
091     * this, as there seems to be no way to check the number of queued bytes and
092     * buffer length. This might go false for short intervals, but it might also
093     * stick off if something goes wrong.
094     *
095     * @return true if more data can be sent; false otherwise
096     */
097    @Override
098    public boolean okToSend() {
099        return activeSerialPort.isCTS();
100    }
101
102    /**
103     * set up all of the other objects to operate with a MX-1 connected to this
104     * port
105     */
106    @Override
107    public void configure() {
108        Mx1CommandStation cs = new Mx1CommandStation(getSystemConnectionMemo().getSystemPrefix(), getSystemConnectionMemo().getUserName());
109        // connect to a packetizing traffic controller
110        Mx1Packetizer packets = new Mx1Packetizer(cs, Mx1Packetizer.ASCII);
111        packets.connectPort(this);
112
113        getSystemConnectionMemo().setMx1TrafficController(packets);
114        getSystemConnectionMemo().configureManagers();
115
116        // start operation
117        packets.startThreads();
118    }
119
120    // base class methods for the ZimoPortController interface
121
122    @Override
123    public DataInputStream getInputStream() {
124        if (!opened) {
125            log.error("getInputStream called before load(), stream not available");
126            return null;
127        }
128        return new DataInputStream(serialStream);
129    }
130
131    @Override
132    public DataOutputStream getOutputStream() {
133        if (!opened) {
134            log.error("getOutputStream called before load(), stream not available");
135        }
136        try {
137            return new DataOutputStream(activeSerialPort.getOutputStream());
138        } catch (java.io.IOException e) {
139            log.error("getOutputStream exception: {}", e.getMessage());
140        }
141        return null;
142    }
143
144    @Override
145    public boolean status() {
146        return opened;
147    }
148
149    /**
150     * Local method to do specific configuration.
151     *
152     * @throws purejavacomm.UnsupportedCommOperationException if unable to
153     *                                                        configure the
154     *                                                        serial port
155     */
156    protected void setSerialPort() throws UnsupportedCommOperationException {
157        // find the baud rate value, configure comm options
158        int baud = currentBaudNumber(mBaudRate);
159        activeSerialPort.setSerialPortParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
160
161        // find and configure flow control
162        int flow = SerialPort.FLOWCONTROL_RTSCTS_OUT; // default, but also defaults in selectedOption1
163        if (getOptionState(option1Name).equals(validOption1[1])) {
164            flow = 0;
165        }
166        configureLeadsAndFlowControl(activeSerialPort, flow);
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public String[] validBaudRates() {
174        return Arrays.copyOf(validSpeeds, validSpeeds.length);
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    @Override
181    public int[] validBaudNumbers() {
182        return Arrays.copyOf(validSpeedValues, validSpeedValues.length);
183    }
184
185    protected String[] validSpeeds = new String[]{Bundle.getMessage("Baud1200"),
186            Bundle.getMessage("Baud2400"), Bundle.getMessage("Baud4800"),
187            Bundle.getMessage("Baud9600"), Bundle.getMessage("Baud19200"),
188            Bundle.getMessage("Baud38400")};
189    protected int[] validSpeedValues = new int[]{1200, 2400, 4800, 9600, 19200, 38400};
190
191    @Override
192    public int defaultBaudIndex() {
193        return 0;
194    }
195
196    // meanings are assigned to these above, so make sure the order is consistent
197    protected String[] validOption1 = new String[]{Bundle.getMessage("FlowOptionHwRecomm"), Bundle.getMessage("FlowOptionNo")};
198    protected String[] validOption2 = new String[]{"3", "5"};
199    //protected String selectedOption1=validOption1[0];
200
201    InputStream serialStream = null;
202
203    private final static Logger log = LoggerFactory.getLogger(Mx1Adapter.class);
204
205}