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 jmri.jmrix.purejavacomm.CommPortIdentifier; 020import jmri.jmrix.purejavacomm.NoSuchPortException; 021import jmri.jmrix.purejavacomm.PortInUseException; 022import jmri.jmrix.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 jmri.jmrix.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 = portID.open(getSystemConnectionMemo(), 2000); 049 } catch (PortInUseException p) { 050 return handlePortBusy(p, portName, log); 051 } 052 try { 053 activeSerialPort.setSerialPortParams(115200, 054 jmri.jmrix.purejavacomm.SerialPort.DATABITS_8, 055 jmri.jmrix.purejavacomm.SerialPort.STOPBITS_1, 056 jmri.jmrix.purejavacomm.SerialPort.PARITY_NONE); 057 configureLeadsAndFlowControl(activeSerialPort, 058 jmri.jmrix.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 jmri.jmrix.purejavacomm.SerialPort getSerialPort() { 093 return activeSerialPort; 094 } 095 096 /** 097 * Close the port after it has failed, so it can be opened again with 098 * {@link #openPort}. 099 */ 100 public void closeSerialPort() { 101 opened = false; 102 jmri.jmrix.purejavacomm.SerialPort p = activeSerialPort; 103 if (p != null) { 104 try { 105 p.close(); 106 } catch (RuntimeException e) { 107 log.debug("Closing the failed port", e); 108 } 109 } 110 } 111 112 /** 113 * @return true if the operating system lists this connection's port. A 114 * USB interface's port disappears while it is unplugged. 115 */ 116 public boolean isPortPresent() { 117 return getActualPortNames().contains(getCurrentPortName()); 118 } 119 120 /** 121 * Option 1 controls the connection used for programming. 122 * @return array with options for programming. 123 */ 124 public String[] validOption1() { 125 List<SystemConnectionMemo> connList = jmri.InstanceManager.getList(SystemConnectionMemo.class); 126 if (!connList.isEmpty()) { 127 ArrayList<String> progConn = new ArrayList<>(); 128 progConn.add(""); 129 String userName = "Dcc4Pc"; 130 if (this.getSystemConnectionMemo() != null) { 131 userName = this.getSystemConnectionMemo().getUserName(); 132 } 133 for (int i = 0; i < connList.size(); i++) { 134 SystemConnectionMemo scm = connList.get(i); 135 if ((scm.provides(jmri.AddressedProgrammerManager.class) || scm.provides(jmri.GlobalProgrammerManager.class)) 136 && (!scm.getUserName().equals(userName))) { 137 progConn.add(scm.getUserName()); 138 } 139 } 140 return progConn.toArray(new String[progConn.size()]); 141 } else { 142 return new String[]{""}; 143 } 144 } 145 146 /** 147 * {@inheritDoc} 148 * <p> 149 * The Programmer choices are worked out each time: the list made when 150 * this connection was created can't include connections that start after 151 * it, and LocoNet often does. The saved choice stays in the list even if 152 * that connection isn't running, so opening Preferences doesn't clear it. 153 */ 154 @Override 155 public String[] getOptionChoices(String option) { 156 if (!option1Name.equals(option)) { 157 return super.getOptionChoices(option); 158 } 159 List<String> choices = new ArrayList<>(java.util.Arrays.asList(validOption1())); 160 String current = getOptionState(option1Name); 161 if (current != null && !current.isEmpty() && !choices.contains(current)) { 162 choices.add(current); 163 } 164 return choices.toArray(new String[choices.size()]); 165 } 166 167 /** 168 * Get a String that says what Option 2 represents May be an empty string, 169 * but will not be null 170 * @return option to match detected locos to roster. 171 */ 172 public String option2Name() { 173 return "Match Detected Locos to Roster: "; 174 } 175 176 /** 177 * Set the second port option. Only to be used after construction, but 178 * before the openPort call 179 */ 180 @Override 181 public void configureOption2(String value) { 182 super.configureOption2(value); 183 log.debug("configureOption2: {}", value); 184 //Not yet implemented 185 /*boolean enable = true; 186 if(value.equals("No")) 187 enable = false; 188 adaptermemo.getRailCommManager().setRosterEntryDiscoveryAllowed(enable);*/ 189 } 190 191 // base class methods for the Dcc4PcPortController interface 192 @Override 193 public DataInputStream getInputStream() { 194 if (!opened) { 195 log.error("getInputStream called before load(), stream not available"); 196 return null; 197 } 198 return new DataInputStream(serialStream); 199 } 200 201 @Override 202 public DataOutputStream getOutputStream() { 203 if (!opened) { 204 log.error("getOutputStream called before load(), stream not available"); 205 } 206 try { 207 return new DataOutputStream(activeSerialPort.getOutputStream()); 208 } catch (java.io.IOException e) { 209 log.error("getOutputStream exception", e); 210 } 211 return null; 212 } 213 214 /** 215 * {@inheritDoc} 216 * 217 * Currently only 115,200 bps 218 */ 219 @Override 220 public String[] validBaudRates() { 221 return new String[]{"115,200 bps"}; 222 } 223 224 /** 225 * {@inheritDoc} 226 */ 227 @Override 228 public int[] validBaudNumbers() { 229 return new int[]{115200}; 230 } 231 232 @Override 233 public int defaultBaudIndex() { 234 return 0; 235 } 236 237 InputStream serialStream = null; 238 239 /** 240 * set up all of the other objects to operate with an Dcc4Pc command station 241 * connected to this port 242 */ 243 @Override 244 public void configure() { 245 // connect to the traffic controller 246 Dcc4PcTrafficController control = new Dcc4PcTrafficController(); 247 this.getSystemConnectionMemo().setDcc4PcTrafficController(control); 248 control.connectPort(this); 249 this.getSystemConnectionMemo().configureManagers(); 250 // Programming on Main goes out through another connection (by default, the default 251 // command station's); RailCom brings the reply back 252 this.getSystemConnectionMemo().useProgrammerFrom(getOptionState(option1Name)); 253 254 } 255 256 private static final Logger log = LoggerFactory.getLogger(SerialDriverAdapter.class); 257 258}