001package jmri.jmrix.dcc4pc.serialdriver;
002
003import java.io.DataInputStream;
004import java.io.DataOutputStream;
005import java.io.IOException;
006import java.io.InputStream;
007import java.util.ArrayList;
008import java.util.List;
009import jmri.SystemConnectionMemo;
010import jmri.jmrix.dcc4pc.Dcc4PcConnectionTypeList;
011import jmri.jmrix.dcc4pc.Dcc4PcPortController;
012import jmri.jmrix.dcc4pc.Dcc4PcSystemConnectionMemo;
013import jmri.jmrix.dcc4pc.Dcc4PcTrafficController;
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016import purejavacomm.CommPortIdentifier;
017import purejavacomm.NoSuchPortException;
018import purejavacomm.PortInUseException;
019import purejavacomm.SerialPort;
020import purejavacomm.UnsupportedCommOperationException;
021
022/**
023 * Implements SerialPortAdapter for the Dcc4Pc system.
024 * <p>
025 * This connects an Dcc4Pc command station via a serial com port.
026 *
027 * @author Kevin Dickerson Copyright (C) 2012
028 */
029public class SerialDriverAdapter extends Dcc4PcPortController {
030
031    public SerialDriverAdapter() {
032        super(new Dcc4PcSystemConnectionMemo());
033        option1Name = "Programmer"; // NOI18N
034        options.put(option1Name, new Option("Programmer : ", validOption1()));
035        setManufacturer(Dcc4PcConnectionTypeList.DCC4PC);
036    }
037
038    SerialPort activeSerialPort = null;
039
040    @Override
041    public String openPort(String portName, String appName) {
042
043        try {
044            CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier(portName);
045            try {
046                activeSerialPort = (SerialPort) portID.open(appName, 2000);
047            } catch (PortInUseException p) {
048                return handlePortBusy(p, portName, log);
049            }
050            try {
051                activeSerialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
052                configureLeadsAndFlowControl(activeSerialPort, SerialPort.FLOWCONTROL_NONE);
053            } catch (UnsupportedCommOperationException e) {
054                log.error("Cannot set serial parameters on port {}: {}", portName, e.getMessage());
055            }
056            log.debug("Serial timeout was observed as: {} {}", activeSerialPort.getReceiveTimeout(), activeSerialPort.isReceiveTimeoutEnabled());
057
058            serialStream = activeSerialPort.getInputStream();
059            // purge contents, if any
060            purgeStream(serialStream);
061
062            // report status?
063            if (log.isInfoEnabled()) {
064                log.info("{} port opened at {} baud, sees  DTR: {} RTS: {} DSR: {} CTS: {}  CD: {}", portName, activeSerialPort.getBaudRate(), activeSerialPort.isDTR(), activeSerialPort.isRTS(), activeSerialPort.isDSR(), activeSerialPort.isCTS(), activeSerialPort.isCD());
065            }
066
067            opened = true;
068
069        } catch (NoSuchPortException p) {
070            return handlePortNotFound(p, portName, log);
071        } catch (IOException ex) {
072            log.error("Unexpected exception while opening port {}", portName, ex);
073            return "Unexpected error while opening port " + portName + ": " + ex;
074        }
075        return null; // indicates OK return
076    }
077
078    public void setHandshake(int mode) {
079        try {
080            activeSerialPort.setFlowControlMode(mode);
081        } catch (UnsupportedCommOperationException ex) {
082            log.error("Unexpected exception while setting COM port handshake mode", ex);
083        }
084    }
085
086    public SerialPort getSerialPort() {
087        return activeSerialPort;
088    }
089
090    /**
091     * Option 1 controls the connection used for programming.
092     * @return array with options for programming.
093     */
094    public String[] validOption1() {
095        List<SystemConnectionMemo> connList = jmri.InstanceManager.getList(SystemConnectionMemo.class);
096        if (!connList.isEmpty()) {
097            ArrayList<String> progConn = new ArrayList<>();
098            progConn.add("");
099            String userName = "Dcc4Pc";
100            if (this.getSystemConnectionMemo() != null) {
101                userName = this.getSystemConnectionMemo().getUserName();
102            }
103            for (int i = 0; i < connList.size(); i++) {
104                SystemConnectionMemo scm = connList.get(i);
105                if ((scm.provides(jmri.AddressedProgrammerManager.class) || scm.provides(jmri.GlobalProgrammerManager.class))
106                        && (!scm.getUserName().equals(userName))) {
107                    progConn.add(scm.getUserName());
108                }
109            }
110            return progConn.toArray(new String[progConn.size()]);
111        } else {
112            return new String[]{""};
113        }
114    }
115
116    /**
117     * Get a String that says what Option 2 represents May be an empty string,
118     * but will not be null
119     * @return option to match detected locos to roster.
120     */
121    public String option2Name() {
122        return "Match Detected Locos to Roster: ";
123    }
124
125    /**
126     * Set the second port option. Only to be used after construction, but
127     * before the openPort call
128     */
129    @Override
130    public void configureOption2(String value) {
131        super.configureOption2(value);
132        log.debug("configureOption2: {}", value);
133        //Not yet implemented
134        /*boolean enable = true;
135         if(value.equals("No"))
136         enable = false;
137         adaptermemo.getRailCommManager().setRosterEntryDiscoveryAllowed(enable);*/
138    }
139
140    // base class methods for the Dcc4PcPortController interface
141    @Override
142    public DataInputStream getInputStream() {
143        if (!opened) {
144            log.error("getInputStream called before load(), stream not available");
145            return null;
146        }
147        return new DataInputStream(serialStream);
148    }
149
150    @Override
151    public DataOutputStream getOutputStream() {
152        if (!opened) {
153            log.error("getOutputStream called before load(), stream not available");
154        }
155        try {
156            return new DataOutputStream(activeSerialPort.getOutputStream());
157        } catch (java.io.IOException e) {
158            log.error("getOutputStream exception", e);
159        }
160        return null;
161    }
162
163    /**
164     * {@inheritDoc}
165     *
166     * Currently only 115,200 bps
167     */
168    @Override
169    public String[] validBaudRates() {
170        return new String[]{"115,200 bps"};
171    }
172
173    /**
174     * {@inheritDoc}
175     */
176    @Override
177    public int[] validBaudNumbers() {
178        return new int[]{115200};
179    }
180
181    @Override
182    public int defaultBaudIndex() {
183        return 0;
184    }
185
186    InputStream serialStream = null;
187
188    /**
189     * set up all of the other objects to operate with an Dcc4Pc command station
190     * connected to this port
191     */
192    @Override
193    public void configure() {
194        // connect to the traffic controller
195        Dcc4PcTrafficController control = new Dcc4PcTrafficController();
196        this.getSystemConnectionMemo().setDcc4PcTrafficController(control);
197        control.connectPort(this);
198        this.getSystemConnectionMemo().configureManagers();
199
200    }
201
202    private final static Logger log = LoggerFactory.getLogger(SerialDriverAdapter.class);
203
204}