001package jmri.jmrix.serialsensor; 002 003import java.io.DataInputStream; 004import java.io.DataOutputStream; 005import java.io.IOException; 006import java.io.InputStream; 007import java.util.Comparator; 008import java.util.ResourceBundle; 009import java.util.TooManyListenersException; 010 011import jmri.InstanceManager; 012import jmri.JmriException; 013import jmri.NamedBean; 014import jmri.Sensor; 015import jmri.jmrix.AbstractSerialPortController; 016import jmri.jmrix.DefaultSystemConnectionMemo; 017 018import org.slf4j.Logger; 019import org.slf4j.LoggerFactory; 020 021import purejavacomm.CommPortIdentifier; 022import purejavacomm.NoSuchPortException; 023import purejavacomm.PortInUseException; 024import purejavacomm.SerialPortEventListener; 025import purejavacomm.UnsupportedCommOperationException; 026 027/** 028 * Implements SerialPortAdapter for connecting to two sensors via the serial 029 * port. Sensor "1" will be via DCD, and sensor "2" via DSR 030 * 031 * @author Bob Jacobsen Copyright (C) 2003 032 */ 033public class SerialSensorAdapter extends AbstractSerialPortController { 034 035 purejavacomm.SerialPort activeSerialPort = null; 036 037 public SerialSensorAdapter() { 038 super(new DefaultSystemConnectionMemo("S", Bundle.getMessage("TypeSerial")) { 039 040 @Override 041 protected ResourceBundle getActionModelResourceBundle() { 042 return null; 043 } 044 045 @Override 046 public <B extends NamedBean> Comparator<B> getNamedBeanComparator(Class<B> type) { 047 return (o1, o2) -> o1.getSystemName().compareTo(o2.getSystemName()); 048 } 049 }); 050 } 051 052 @Override 053 public void configure() { 054 log.debug("Configure doesn't do anything here"); 055 } 056 057 @Override 058 public String openPort(String portName, String appName) { 059 // open the port, check ability to set moderators 060 try { 061 // get and open the primary port 062 CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier(portName); 063 try { 064 activeSerialPort = (purejavacomm.SerialPort) portID.open(appName, 2000); // name of program, msec to wait 065 } catch (PortInUseException p) { 066 return handlePortBusy(p, portName, log); 067 } 068 069 // try to set it for communication via SerialDriver 070 try { 071 activeSerialPort.setSerialPortParams(9600, 072 purejavacomm.SerialPort.DATABITS_8, 073 purejavacomm.SerialPort.STOPBITS_1, 074 purejavacomm.SerialPort.PARITY_NONE); 075 } catch (UnsupportedCommOperationException e) { 076 log.error("Cannot set serial parameters on port {}: {}", portName, e.getMessage()); 077 return "Cannot set serial parameters on port " + portName + ": " + e.getMessage(); 078 } 079 080 // disable flow control; hardware lines used for signaling, XON/XOFF might appear in data 081 // set RTS active low, DTR inactive high 082 configureLeadsAndFlowControl(activeSerialPort, 0, true, false); 083 084 // set timeout 085 // activeSerialPort.enableReceiveTimeout(1000); 086 log.debug("Serial timeout was observed as: {} {}", activeSerialPort.getReceiveTimeout(), 087 activeSerialPort.isReceiveTimeoutEnabled()); 088 089 // arrange to notify of sensor changes 090 activeSerialPort.addEventListener(new SerialPortEventListener() { 091 @Override 092 public void serialEvent(purejavacomm.SerialPortEvent e) { 093 int type = e.getEventType(); 094 switch (type) { 095 case purejavacomm.SerialPortEvent.DSR: 096 log.info("SerialEvent: DSR is {}", e.getNewValue()); 097 notify("1", e.getNewValue()); 098 return; 099 case purejavacomm.SerialPortEvent.CD: 100 log.info("SerialEvent: CD is {}", e.getNewValue()); 101 notify("2", e.getNewValue()); 102 return; 103 case purejavacomm.SerialPortEvent.CTS: 104 log.info("SerialEvent: CTS is {}", e.getNewValue()); 105 notify("3", e.getNewValue()); 106 return; 107 default: 108 if (log.isDebugEnabled()) { 109 log.debug("SerialEvent of type: {} value: {}", type, e.getNewValue()); 110 } 111 return; 112 } 113 } 114 115 /** 116 * Do a sensor change on the event queue 117 */ 118 public void notify(String sensor, boolean value) { 119 javax.swing.SwingUtilities.invokeLater(new SerialNotifier(sensor, value)); 120 } 121 }); 122 // turn on notification 123 activeSerialPort.notifyOnCTS(true); 124 activeSerialPort.notifyOnDSR(true); 125 activeSerialPort.notifyOnCarrierDetect(true); 126 127 // get and save stream 128 serialStream = activeSerialPort.getInputStream(); 129 130 // purge contents, if any 131 purgeStream(serialStream); 132 133 // report status? 134 if (log.isInfoEnabled()) { 135 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()); 136 } 137 138 opened = true; 139 140 } catch (NoSuchPortException ex1) { 141 log.error("No such port {} ", portName, ex1); 142 return "No such port " + portName + ": " + ex1; 143 } catch (TooManyListenersException ex3) { 144 log.error("Too Many Listeners on port {} ", portName, ex3); 145 return "Too Many Listeners on port " + portName + ": " + ex3; 146 } catch (IOException ex4) { 147 log.error("I/O error on port {} ", portName, ex4); 148 return "I/O error on port " + portName + ": " + ex4; 149 } 150 151 return null; // indicates OK return 152 } 153 154 @Override 155 public DataInputStream getInputStream() { 156 if (!opened) { 157 log.error("getInputStream called before load(), stream not available"); 158 return null; 159 } 160 return new DataInputStream(serialStream); 161 } 162 163 @Override 164 public DataOutputStream getOutputStream() { 165 if (!opened) { 166 log.error("getOutputStream called before load(), stream not available"); 167 } 168 try { 169 return new DataOutputStream(activeSerialPort.getOutputStream()); 170 } catch (java.io.IOException e) { 171 log.error("getOutputStream exception: ", e); 172 } 173 return null; 174 } 175 176 @Override 177 public boolean status() { 178 return opened; 179 } 180 181 /** 182 * {@inheritDoc} 183 * Currently only 19,200 bps. 184 */ 185 @Override 186 public String[] validBaudRates() { 187 return new String[]{"9,600 bps"}; 188 } 189 190 /** 191 * {@inheritDoc} 192 */ 193 @Override 194 public int[] validBaudNumbers() { 195 return new int[]{9600}; 196 } 197 198 @Override 199 public int defaultBaudIndex() { 200 return 0; 201 } 202 203 // private control members 204 private boolean opened = false; 205 InputStream serialStream = null; 206 207 /** 208 * Do a sensor change on the event queue. 209 * @param sensor sensor 210 * @param value true if sensor changes on, else false. 211 */ 212 public void notify(String sensor, boolean value) { 213 } 214 215 /** 216 * Internal class to remember the Message object and destination listener 217 * when a message is queued for notification. 218 */ 219 static class SerialNotifier implements Runnable { 220 221 String mSensor; 222 boolean mValue; 223 224 SerialNotifier(String pSensor, boolean pValue) { 225 mSensor = pSensor; 226 mValue = pValue; 227 } 228 229 @Override 230 public void run() { 231 log.debug("serial sensor notify starts"); 232 int value = Sensor.INACTIVE; 233 if (mValue) { 234 value = Sensor.ACTIVE; 235 } 236 try { 237 InstanceManager.sensorManagerInstance().provideSensor(mSensor) 238 .setKnownState(value); 239 } catch (JmriException | IllegalArgumentException e) { 240 log.error("Exception setting state: ", e); 241 } 242 } 243 } 244 245 private final static Logger log = LoggerFactory.getLogger(SerialSensorAdapter.class); 246 247}