001package jmri.jmrix.ieee802154; 002 003import jmri.jmrix.AbstractMRListener; 004import jmri.jmrix.AbstractMRMessage; 005import jmri.jmrix.AbstractMRNodeTrafficController; 006import jmri.jmrix.AbstractMRReply; 007import jmri.jmrix.AbstractNode; 008import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Converts Stream-based I/O to/from messages. The "IEEE802154Interface" side 014 * sends/receives message objects. 015 * <p> 016 * The connection to a IEEE802154PortController is via a pair of *Streams, which 017 * then carry sequences of characters for transmission. Note that this 018 * processing is handled in an independent thread. 019 * <p> 020 * This maintains a list of nodes, but doesn't currently do anything with it. 021 * This implementation is complete and can be instantiated, but is not 022 * functional. It will be created e.g. when a default object is needed for 023 * configuring nodes, etc, during the initial configuration. A subclass must be 024 * instantiated to actually communicate with an adapter. 025 * 026 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2005, 2006, 2008 Converted to multiple connection 027 * @author kcameron Copyright (C) 2011 028 * @author Paul Bender Copyright (C) 2013 029 */ 030abstract public class IEEE802154TrafficController extends AbstractMRNodeTrafficController implements IEEE802154Interface { 031 032 /** 033 * Create a new IEEE802154TrafficController instance. 034 */ 035 public IEEE802154TrafficController() { 036 super(); 037 logDebug = log.isDebugEnabled(); 038 039 init(1, 100); 040 041 // not polled at all, so allow unexpected messages, and 042 // use poll delay just to spread out startup 043 setAllowUnexpectedReply(true); 044 mWaitBeforePoll = 1000; // can take a long time to send 045 } 046 047 /** 048 * Get a message of a specific length for filling in. 049 * <p> 050 * This is a default, null implementation, which must be overridden in an 051 * adapter-specific subclass. 052 * 053 * @param length length for new message 054 * @return null since this method should be over-ridden 055 */ 056 public IEEE802154Message getIEEE802154Message(int length) { 057 return null; 058 } 059 060 // have several debug statements in tight loops, e.g. every character; 061 // only want to check once 062 protected boolean logDebug = false; 063 064 // The methods to implement the IEEE802154Interface 065 066 @Override 067 public synchronized void addIEEE802154Listener(IEEE802154Listener l) { 068 this.addListener(l); 069 } 070 071 @Override 072 public synchronized void removeIEEE802154Listener(IEEE802154Listener l) { 073 this.removeListener(l); 074 } 075 076 @Override 077 protected int enterProgModeDelayTime() { 078 // we should to wait at least a second after enabling the programming track 079 return 1000; 080 } 081 082 /** 083 * Forward a IEEE802154Message to all registered IEEE802154Interface 084 * listeners. 085 */ 086 @Override 087 protected void forwardMessage(AbstractMRListener client, AbstractMRMessage m) { 088 ((IEEE802154Listener) client).message((IEEE802154Message) m); 089 } 090 091 /** 092 * Forward a reply to all registered IEEE802154Interface listeners. 093 */ 094 @Override 095 protected void forwardReply(AbstractMRListener client, AbstractMRReply r) { 096 ((IEEE802154Listener) client).reply((IEEE802154Reply) r); 097 } 098 099 /** 100 * Eventually, do initialization if needed 101 */ 102 @Override 103 protected AbstractMRMessage pollMessage() { 104 return null; 105 } 106 107 @Override 108 protected AbstractMRListener pollReplyHandler() { 109 return null; 110 } 111 112 /** 113 * Forward a preformatted message to the actual interface. 114 */ 115 @Override 116 public void sendIEEE802154Message(IEEE802154Message m, IEEE802154Listener reply) { 117 sendMessage(m, reply); 118 } 119 120 @Override 121 protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) { 122 if (logDebug) { 123 log.debug("forward {}", m); 124 } 125 super.forwardToPort(m, reply); 126 } 127 128 @Override 129 protected AbstractMRMessage enterProgMode() { 130 return null; 131 } 132 133 @Override 134 protected AbstractMRMessage enterNormalMode() { 135 return null; 136 } 137 138 public void setAdapterMemo(IEEE802154SystemConnectionMemo adaptermemo) { 139 memo = adaptermemo; 140 } 141 142 public IEEE802154SystemConnectionMemo getAdapterMemo() { 143 return memo; 144 } 145 146 private IEEE802154SystemConnectionMemo memo = null; 147 148 /** 149 * IEEE 802.15.4 messages start with a 0x7E delimiter byte. 150 */ 151 @Override 152 protected void waitForStartOfReply(java.io.DataInputStream istream) throws java.io.IOException { 153 // loop looking for the start character 154 while (readByteProtected(istream) != 0x7E) { 155 } 156 } 157 158 /** 159 * <p> 160 * The length is the first byte of the message payload, The end of the 161 * message occurs when the length of the message is equal to the payload 162 * length. 163 * <p> 164 * NOTE: This function does not work with XBee nodes, which provide a 165 * modified version of the packets sent via the radio. 166 */ 167 @Override 168 protected boolean endOfMessage(AbstractMRReply msg) { 169 return ((msg.getElement(0) + 2) == msg.getNumDataElements()); 170 } 171 172 /** 173 * Add trailer to the outgoing byte stream. This version adds the checksum 174 * to the end of the message. 175 * 176 * @param msg The output byte stream 177 * @param offset the first byte not yet used 178 */ 179 @Override 180 protected void addTrailerToOutput(byte[] msg, int offset, jmri.jmrix.AbstractMRMessage m) { 181 if (m.getNumDataElements() == 0) { 182 return; 183 } 184 ((IEEE802154Message) m).setParity(); 185 } 186 187 /** 188 * Add header to the outgoing byte stream. 189 * 190 * @param msg The output byte stream 191 * @return next location in the stream to fill 192 */ 193 //protected int addHeaderToOutput(byte[] msg, AbstractMRMessage m) { 194 // m.setElement(0)=0x7E; 195 // return 1; 196 //} 197 198 /** 199 * <p> 200 * This is a default, null implementation, which must be overridden in an 201 * adapter-specific subclass. 202 */ 203 //protected AbstractMRReply newReply() {return new IEEE802154Reply();} 204 205 /** 206 * Build a new IEEE802154 Node. 207 * Must be implemented by derived classes 208 * @return new IEEE802154Node. 209 */ 210 abstract public IEEE802154Node newNode(); 211 212 /** 213 * Identify a SerialNode from its node address. 214 * 215 * @param addr hex string for address, numbered from 0 216 * @return serial node id, or 'niull' if a SerialNode with the specified 217 * address was not found 218 */ 219 synchronized public AbstractNode getNodeFromAddress(String addr) { 220 log.debug("String getNodeFromAddress called with {}", addr); 221 byte ba[] = jmri.util.StringUtil.bytesFromHexString(addr); 222 return getNodeFromAddress(ba); 223 } 224 225 /** 226 * Identify a SerialNode from its node address Note: 'addr' 227 * is the node address. 228 * 229 * @param ia int array of node address, numbered from 0 230 * @return serial address, or 'null' if a SerialNode with the specified 231 * address was not found 232 */ 233 synchronized public AbstractNode getNodeFromAddress(int ia[]) { 234 if(logDebug) { 235 String s=""; 236 for( int i=0;i<ia.length;i++) { 237 s=jmri.util.StringUtil.appendTwoHexFromInt(ia[i],s); 238 } 239 log.debug("int array getNodeFromAddress called with {}", s); 240 } 241 byte ba[] = new byte[ia.length]; 242 for (int i = 0; i < ia.length; i++) { 243 ba[i] = (byte) (ia[i] & 0xff); 244 } 245 return getNodeFromAddress(ba); 246 } 247 248 /** 249 * Identify a SerialNode from its node address. 250 * 251 * @param ba array of bytes in hex address 252 * @return serial node id, or 'null' if a SerialNode with the specified 253 * address was not found 254 */ 255 synchronized public AbstractNode getNodeFromAddress(byte ba[]) { 256 log.debug("byte array getNodeFromAddress called with {}", 257 jmri.util.StringUtil.hexStringFromBytes(ba)); 258 for (int i = 0; i < numNodes; i++) { 259 byte bsa[] = ((IEEE802154Node) getNode(i)).getUserAddress(); 260 byte bga[] = ((IEEE802154Node) getNode(i)).getGlobalAddress(); 261 if (bsa.length == ba.length) { 262 int j = 0; 263 for (; j < bsa.length; j++) { 264 if (bsa[j] != ba[j]) { 265 break; 266 } 267 } 268 if (j == bsa.length) { 269 return (getNode(i)); 270 } 271 } else if (bga.length == ba.length) { 272 int j = 0; 273 for (; j < bga.length; j++) { 274 if (bga[j] != ba[j]) { 275 break; 276 } 277 } 278 if (j == bga.length) { 279 return (getNode(i)); 280 } 281 } 282 } 283 return (null); 284 } 285 286 /** 287 * Delete a node by the string representation of its address. 288 * 289 * @param nodeAddress text of hex node address 290 */ 291 @SuppressFBWarnings(value="VO_VOLATILE_INCREMENT", justification="synchronized method provides locking") 292 public synchronized void deleteNode(String nodeAddress) { 293 // find the serial node 294 int index = 0; 295 for (int i = 0; i < numNodes; i++) { 296 if (nodeArray[i] == getNodeFromAddress(nodeAddress)) { 297 index = i; 298 } 299 } 300 if (index == curSerialNodeIndex) { 301 log.warn("Deleting the serial node active in the polling loop"); 302 } 303 // Delete the node from the node list 304 numNodes--; 305 if (index < numNodes) { 306 // did not delete the last node, shift 307 for (int j = index; j < numNodes; j++) { 308 nodeArray[j] = nodeArray[j + 1]; 309 } 310 } 311 nodeArray[numNodes] = null; 312 } 313 314 private final static Logger log = LoggerFactory.getLogger(IEEE802154TrafficController.class); 315 316}