001package jmri.jmrix.ieee802154.serialdriver;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.io.InputStream;
007import java.util.Arrays;
008import jmri.jmrix.ieee802154.IEEE802154PortController;
009import jmri.jmrix.ieee802154.IEEE802154SystemConnectionMemo;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012import purejavacomm.CommPortIdentifier;
013import purejavacomm.NoSuchPortException;
014import purejavacomm.PortInUseException;
015import purejavacomm.SerialPort;
016import purejavacomm.UnsupportedCommOperationException;
017
018/**
019 * Provide access to IEEE802.15.4 devices via a serial com port.
020 * Derived from the Oaktree code.
021 *
022 * @author Bob Jacobsen Copyright (C) 2006, 2007, 2008
023 * @author Ken Cameron, (C) 2009, sensors from poll replies Converted to
024 * multiple connection
025 * @author kcameron Copyright (C) 2011
026 * @author Paul Bender Copyright (C) 2013
027 */
028public class SerialDriverAdapter extends IEEE802154PortController {
029
030    protected SerialPort activeSerialPort = null;
031
032    public SerialDriverAdapter() {
033        this(new SerialSystemConnectionMemo());
034    }
035
036    protected SerialDriverAdapter(IEEE802154SystemConnectionMemo connectionMemo) {
037        super(connectionMemo);
038        this.manufacturerName = jmri.jmrix.ieee802154.SerialConnectionTypeList.IEEE802154;
039    }
040
041    @Override
042    public String openPort(String portName, String appName) {
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 serial
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            // 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 {}", // NOI18N
073                        (activeSerialPort.getFlowControlMode() == SerialPort.FLOWCONTROL_RTSCTS_OUT ? "hardware flow control" : "no flow control")); // NOI18N
074
075                // log events
076                setPortEventLogging(activeSerialPort);
077            }
078
079            opened = true;
080
081        } catch (NoSuchPortException p) {
082            return handlePortNotFound(p, portName, log);
083        } catch (IOException ex) {
084            log.error("Unexpected exception while opening port {} ", portName, ex);
085            return "Unexpected error while opening port " + portName + ": " + ex;
086        }
087
088        return null; // normal operation
089    }
090
091    /**
092     * Can the port accept additional characters? Yes, always
093     * @return always true
094     */
095    public boolean okToSend() {
096        return true;
097    }
098
099    /**
100     * Set up all of the other objects to operate connected to this port.
101     */
102    @Override
103    public void configure() {
104        log.debug("configure() called.");
105        SerialTrafficController tc = new SerialTrafficController();
106
107        // connect to the traffic controller
108        this.getSystemConnectionMemo().setTrafficController(tc);
109        tc.setAdapterMemo(this.getSystemConnectionMemo());
110        this.getSystemConnectionMemo().configureManagers();
111        tc.connectPort(this);
112        // Configure the form of serial address validation for this connection
113//        adaptermemo.setSerialAddress(new jmri.jmrix.ieee802154.SerialAddress(adaptermemo));
114    }
115
116    // base class methods for the SerialPortController interface
117    @Override
118    public DataInputStream getInputStream() {
119        if (!opened) {
120            log.error("getInputStream called before load(), stream not available");
121            return null;
122        }
123        return new DataInputStream(serialStream);
124    }
125
126    @Override
127    public DataOutputStream getOutputStream() {
128        if (!opened) {
129            log.error("getOutputStream called before load(), stream not available");
130        }
131        try {
132            return new DataOutputStream(activeSerialPort.getOutputStream());
133        } catch (IOException e) {
134            log.error("getOutputStream exception: {}", e.getMessage());
135        }
136        return null;
137    }
138
139    @Override
140    public boolean status() {
141        return opened;
142    }
143
144    /**
145     * Local method to do specific port configuration.
146     *
147     * @throws UnsupportedCommOperationException if options not supported by port
148     */
149    protected void setSerialPort() throws UnsupportedCommOperationException {
150        // find the baud rate value, configure comm options
151        int baud = currentBaudNumber(mBaudRate);
152
153        activeSerialPort.setSerialPortParams(baud, SerialPort.DATABITS_8,
154                SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
155
156        // find and configure flow control
157        int flow = SerialPort.FLOWCONTROL_NONE; // default
158        configureLeadsAndFlowControl(activeSerialPort, flow);
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    @Override
165    public String[] validBaudRates() {
166        return Arrays.copyOf(validSpeeds, validSpeeds.length);
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public int[] validBaudNumbers() {
174        return Arrays.copyOf(validSpeedValues, validSpeedValues.length);
175    }
176
177    String[] stdOption1Values = new String[]{"CM11", "CP290", "Insteon 2412S"}; // NOI18N
178
179    public String[] validOption1() {
180        return Arrays.copyOf(stdOption1Values, stdOption1Values.length);
181    }
182
183    /**
184     * Get a String that says what Option 1 represents.
185     *
186     * @return fixed string 'Adapter'
187     */
188    public String option1Name() {
189        return "Adapter"; // NOI18N
190    }
191
192    private String[] validSpeeds = new String[]{Bundle.getMessage("BaudAutomatic")};
193    private int[] validSpeedValues = new int[]{9600};
194
195    @Override
196    public int defaultBaudIndex() {
197        return 0;
198    }
199
200    /**
201     * Get an array of valid values for "option 2"; used to display valid
202     * options. May not be null, but may have zero entries.
203     *
204     * @return empty string array
205     */
206    public String[] validOption2() {
207        return new String[]{""};
208    }
209
210    /**
211     * Get a String that says what Option 2 represents. May be an empty string,
212     * but will not be null.
213     *
214     * @return empty string
215     */
216    public String option2Name() {
217        return "";
218    }
219
220    // private control members
221    protected InputStream serialStream = null;
222
223    private final static Logger log = LoggerFactory.getLogger(SerialDriverAdapter.class);
224
225}