001package jmri.jmrix.bachrus; 002 003import java.io.DataInputStream; 004import java.io.OutputStream; 005import java.util.Vector; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008import purejavacomm.SerialPortEvent; 009import purejavacomm.SerialPortEventListener; 010 011/** 012 * Converts Stream-based I/O to/from Speedo messages. The "SpeedoInterface" side 013 * sends/receives message objects. The connection to a SpeedoPortController is 014 * via a pair of *Streams, which then carry sequences of characters for 015 * transmission. Note that this processing is handled in an independent thread. 016 * <p> 017 * Removed Runnable implementation and methods for it. 018 * 019 * @author Bob Jacobsen Copyright (C) 2001 020 * @author Andrew Crosland Copyright (C) 2010 021 * @author Andrew Berridge Copyright (C) 2010 for gnu io (RXTX) 022 */ 023public class SpeedoTrafficController implements SpeedoInterface, SerialPortEventListener { 024 025 private SpeedoReply reply = new SpeedoReply(); 026 027 /** 028 * Create a new SpeedoTrafficController instance. 029 * 030 * @param adaptermemo the associated SystemConnectionMemo 031 */ 032 public SpeedoTrafficController(SpeedoSystemConnectionMemo adaptermemo) { 033 } 034 035 // The methods to implement the SpeedoInterface 036 037 protected Vector<SpeedoListener> cmdListeners = new Vector<SpeedoListener>(); 038 039 @Override 040 public boolean status() { 041 return (ostream != null && istream != null); 042 } 043 044 @Override 045 public synchronized void addSpeedoListener(SpeedoListener l) { 046 // add only if not already registered 047 if (l == null) { 048 throw new java.lang.NullPointerException(); 049 } 050 if (!cmdListeners.contains(l)) { 051 cmdListeners.addElement(l); 052 } 053 } 054 055 @Override 056 public synchronized void removeSpeedoListener(SpeedoListener l) { 057 if (cmdListeners.contains(l)) { 058 cmdListeners.removeElement(l); 059 } 060 } 061 062 SpeedoListener lastSender = null; 063 064 @SuppressWarnings("unchecked") 065 protected void notifyReply(SpeedoReply r) { 066 // make a copy of the listener vector to synchronized (not needed for transmit?) 067 Vector<SpeedoListener> v; 068 synchronized (this) { 069 v = (Vector<SpeedoListener>) cmdListeners.clone(); 070 } 071 // forward to all listeners 072 int cnt = v.size(); 073 for (int i = 0; i < cnt; i++) { 074 SpeedoListener client = v.elementAt(i); 075 try { 076 // skip forwarding to the last sender for now, we'll get them later 077 if (lastSender != client) { 078 client.reply(r); 079 } 080 } catch (Exception e) { 081 log.warn("notify: During dispatch to {} Exception", client, e); 082 } 083 } 084 085 // Forward to the last listener who send a message. 086 // This is done _second_ so monitoring can have already stored the reply 087 // before a response is sent. 088 if (lastSender != null) { 089 lastSender.reply(r); 090 } 091 } 092 093 // methods to connect/disconnect to a source of data in a LnPortController 094 095 private SpeedoPortController controller = null; 096 097 /** 098 * Make connection to existing PortController object. 099 * @param p speedo port controller. 100 */ 101 public void connectPort(SpeedoPortController p) { 102 istream = p.getInputStream(); 103 ostream = p.getOutputStream(); 104 if (controller != null) { 105 log.warn("connectPort: connect called while connected"); 106 } 107 controller = p; 108 } 109 110 /** 111 * Break connection to existing SpeedoPortController object. 112 * Once broken, attempts to send via "message" member will fail. 113 * @param p speedo port controller. 114 */ 115 public void disconnectPort(SpeedoPortController p) { 116 istream = null; 117 ostream = null; 118 if (controller != p) { 119 log.warn("disconnectPort: disconnect called from non-connected LnPortController"); 120 } 121 controller = null; 122 } 123 124 // data members to hold the streams 125 DataInputStream istream = null; 126 OutputStream ostream = null; 127 128 /* 129 * Speedo replies end with ";" 130 */ 131 boolean endReply(SpeedoReply msg) { 132 // Detect that the reply buffer ends with ";" 133 int num = msg.getNumDataElements(); 134 // ptr is offset of last element in SpeedoReply 135 int ptr = num - 1; 136 if (msg.getElement(ptr) != ';') { 137 return false; 138 } 139 unsolicited = true; 140 return true; 141 } 142 143 private boolean unsolicited; 144 145 /** 146 * Respond to an event triggered by RXTX. In this case we are 147 * only dealing with DATA_AVAILABLE but the other events are left here for 148 * reference. 149 * 150 * @author Andrew Berridge Jan 2010 151 */ 152 @Override 153 public void serialEvent(SerialPortEvent event) { 154 switch (event.getEventType()) { 155 case SerialPortEvent.BI: 156 case SerialPortEvent.OE: 157 case SerialPortEvent.FE: 158 case SerialPortEvent.PE: 159 case SerialPortEvent.CD: 160 case SerialPortEvent.CTS: 161 case SerialPortEvent.DSR: 162 case SerialPortEvent.RI: 163 case SerialPortEvent.OUTPUT_BUFFER_EMPTY: 164 break; 165 case SerialPortEvent.DATA_AVAILABLE: 166 // we get here if data has been received 167 //fill the current reply with any data received 168 int replyCurrentSize = this.reply.getNumDataElements(); 169 int i; 170 for (i = replyCurrentSize; i < SpeedoReply.maxSize - replyCurrentSize; i++) { 171 try { 172 if (istream.available() == 0) { 173 break; //nothing waiting to be read 174 } 175 byte char1 = istream.readByte(); 176 this.reply.setElement(i, char1); 177 178 } catch (Exception e) { 179 log.debug("Exception handling reply cause {}", e.getCause(), e); 180 } 181 if (endReply(this.reply)) { 182 sendreply(); 183 break; 184 } 185 } 186 187 break; 188 default: 189 log.warn("Unhandled event type: {}", event.getEventType()); 190 break; 191 } 192 } 193 194 /** 195 * Send the current reply - built using data from serialEvent. 196 */ 197 private void sendreply() { 198 //send the reply 199 { 200 final SpeedoReply thisReply = this.reply; 201 if (unsolicited) { 202 thisReply.setUnsolicited(); 203 } 204 final SpeedoTrafficController thisTc = this; 205 // return a notification via the queue to ensure end 206 Runnable r = new Runnable() { 207 208 SpeedoReply msgForLater = thisReply; 209 SpeedoTrafficController myTc = thisTc; 210 211 @Override 212 public void run() { 213 myTc.notifyReply(msgForLater); 214 } 215 }; 216 javax.swing.SwingUtilities.invokeLater(r); 217 } 218 //Create a new reply, ready to be filled 219 this.reply = new SpeedoReply(); 220 } 221 222 private final static Logger log = LoggerFactory.getLogger(SpeedoTrafficController.class); 223 224}