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