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 jmri.jmrix.purejavacomm.SerialPortEvent; 009import jmri.jmrix.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 @Override 151 public void serialEvent(SerialPortEvent event) { 152 switch (event.getEventType()) { 153 case SerialPortEvent.BI: 154 case SerialPortEvent.OE: 155 case SerialPortEvent.FE: 156 case SerialPortEvent.PE: 157 case SerialPortEvent.CD: 158 case SerialPortEvent.CTS: 159 case SerialPortEvent.DSR: 160 case SerialPortEvent.RI: 161 case SerialPortEvent.OUTPUT_BUFFER_EMPTY: 162 break; 163 case SerialPortEvent.DATA_AVAILABLE: 164 // we get here if data has been received 165 //fill the current reply with any data received 166 int replyCurrentSize = this.reply.getNumDataElements(); 167 int i; 168 for (i = replyCurrentSize; i < SpeedoReply.maxSize - replyCurrentSize; i++) { 169 try { 170 if (istream.available() == 0) { 171 break; //nothing waiting to be read 172 } 173 byte char1 = istream.readByte(); 174 this.reply.setElement(i, char1); 175 176 } catch (Exception e) { 177 log.debug("Exception handling reply cause {}", e.getCause(), e); 178 } 179 if (endReply(this.reply)) { 180 sendreply(); 181 break; 182 } 183 } 184 185 break; 186 default: 187 log.warn("Unhandled event type: {}", event.getEventType()); 188 break; 189 } 190 } 191 192 /** 193 * Send the current reply - built using data from serialEvent. 194 */ 195 private void sendreply() { 196 //send the reply 197 { 198 final SpeedoReply thisReply = this.reply; 199 if (unsolicited) { 200 thisReply.setUnsolicited(); 201 } 202 final SpeedoTrafficController thisTc = this; 203 // return a notification via the queue to ensure end 204 Runnable r = new Runnable() { 205 206 SpeedoReply msgForLater = thisReply; 207 SpeedoTrafficController myTc = thisTc; 208 209 @Override 210 public void run() { 211 myTc.notifyReply(msgForLater); 212 } 213 }; 214 javax.swing.SwingUtilities.invokeLater(r); 215 } 216 //Create a new reply, ready to be filled 217 this.reply = new SpeedoReply(); 218 } 219 220 private final static Logger log = LoggerFactory.getLogger(SpeedoTrafficController.class); 221 222}