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; 010import jmri.InstanceManager; 011import jmri.JmriException; 012import jmri.NamedBean; 013import jmri.Sensor; 014import jmri.jmrix.AbstractSerialPortController; 015import jmri.jmrix.DefaultSystemConnectionMemo; 016 017import org.slf4j.Logger; 018import org.slf4j.LoggerFactory; 019import purejavacomm.CommPortIdentifier; 020import purejavacomm.NoSuchPortException; 021import purejavacomm.PortInUseException; 022import purejavacomm.SerialPort; 023import purejavacomm.SerialPortEvent; 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 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 = (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, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); 072 } catch (UnsupportedCommOperationException e) { 073 log.error("Cannot set serial parameters on port {}: {}", portName, e.getMessage()); 074 return "Cannot set serial parameters on port " + portName + ": " + e.getMessage(); 075 } 076 077 // disable flow control; hardware lines used for signaling, XON/XOFF might appear in data 078 // set RTS active low, DTR inactive high 079 configureLeadsAndFlowControl(activeSerialPort, 0, true, false); 080 081 // set timeout 082 // activeSerialPort.enableReceiveTimeout(1000); 083 log.debug("Serial timeout was observed as: {} {}", activeSerialPort.getReceiveTimeout(), 084 activeSerialPort.isReceiveTimeoutEnabled()); 085 086 // arrange to notify of sensor changes 087 activeSerialPort.addEventListener(new SerialPortEventListener() { 088 @Override 089 public void serialEvent(SerialPortEvent e) { 090 int type = e.getEventType(); 091 switch (type) { 092 case SerialPortEvent.DSR: 093 log.info("SerialEvent: DSR is {}", e.getNewValue()); 094 notify("1", e.getNewValue()); 095 return; 096 case SerialPortEvent.CD: 097 log.info("SerialEvent: CD is {}", e.getNewValue()); 098 notify("2", e.getNewValue()); 099 return; 100 case SerialPortEvent.CTS: 101 log.info("SerialEvent: CTS is {}", e.getNewValue()); 102 notify("3", e.getNewValue()); 103 return; 104 default: 105 if (log.isDebugEnabled()) { 106 log.debug("SerialEvent of type: {} value: {}", type, e.getNewValue()); 107 } 108 return; 109 } 110 } 111 112 /** 113 * Do a sensor change on the event queue 114 */ 115 public void notify(String sensor, boolean value) { 116 javax.swing.SwingUtilities.invokeLater(new SerialNotifier(sensor, value)); 117 } 118 }); 119 // turn on notification 120 activeSerialPort.notifyOnCTS(true); 121 activeSerialPort.notifyOnDSR(true); 122 activeSerialPort.notifyOnCarrierDetect(true); 123 124 // get and save stream 125 serialStream = activeSerialPort.getInputStream(); 126 127 // purge contents, if any 128 purgeStream(serialStream); 129 130 // report status? 131 if (log.isInfoEnabled()) { 132 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()); 133 } 134 135 opened = true; 136 137 } catch (NoSuchPortException ex1) { 138 log.error("No such port {} ", portName, ex1); 139 return "No such port " + portName + ": " + ex1; 140 } catch (TooManyListenersException ex3) { 141 log.error("Too Many Listeners on port {} ", portName, ex3); 142 return "Too Many Listeners on port " + portName + ": " + ex3; 143 } catch (IOException ex4) { 144 log.error("I/O error on port {} ", portName, ex4); 145 return "I/O error on port " + portName + ": " + ex4; 146 } 147 148 return null; // indicates OK return 149 } 150 151 @Override 152 public DataInputStream getInputStream() { 153 if (!opened) { 154 log.error("getInputStream called before load(), stream not available"); 155 return null; 156 } 157 return new DataInputStream(serialStream); 158 } 159 160 @Override 161 public DataOutputStream getOutputStream() { 162 if (!opened) { 163 log.error("getOutputStream called before load(), stream not available"); 164 } 165 try { 166 return new DataOutputStream(activeSerialPort.getOutputStream()); 167 } catch (java.io.IOException e) { 168 log.error("getOutputStream exception: ", e); 169 } 170 return null; 171 } 172 173 @Override 174 public boolean status() { 175 return opened; 176 } 177 178 /** 179 * {@inheritDoc} 180 * Currently only 19,200 bps. 181 */ 182 @Override 183 public String[] validBaudRates() { 184 return new String[]{"9,600 bps"}; 185 } 186 187 /** 188 * {@inheritDoc} 189 */ 190 @Override 191 public int[] validBaudNumbers() { 192 return new int[]{9600}; 193 } 194 195 @Override 196 public int defaultBaudIndex() { 197 return 0; 198 } 199 200 // private control members 201 private boolean opened = false; 202 InputStream serialStream = null; 203 204 /** 205 * Do a sensor change on the event queue. 206 * @param sensor sensor 207 * @param value true if sensor changes on, else false. 208 */ 209 public void notify(String sensor, boolean value) { 210 } 211 212 /** 213 * Internal class to remember the Message object and destination listener 214 * when a message is queued for notification. 215 */ 216 static class SerialNotifier implements Runnable { 217 218 String mSensor; 219 boolean mValue; 220 221 SerialNotifier(String pSensor, boolean pValue) { 222 mSensor = pSensor; 223 mValue = pValue; 224 } 225 226 @Override 227 public void run() { 228 log.debug("serial sensor notify starts"); 229 int value = Sensor.INACTIVE; 230 if (mValue) { 231 value = Sensor.ACTIVE; 232 } 233 try { 234 InstanceManager.sensorManagerInstance().provideSensor(mSensor) 235 .setKnownState(value); 236 } catch (JmriException | IllegalArgumentException e) { 237 log.error("Exception setting state: ", e); 238 } 239 } 240 } 241 242 private final static Logger log = LoggerFactory.getLogger(SerialSensorAdapter.class); 243 244}