001/** 002 * Copyright (c) 2014-2015 Digi International Inc., 003 * All rights not expressly granted are reserved. 004 * 005 * This Source Code Form is subject to the terms of the Mozilla Public 006 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 007 * You can obtain one at http://mozilla.org/MPL/2.0/. 008 * 009 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343 010 * ======================================================================= 011 */ 012package com.digi.xbee.api.models; 013 014import java.util.LinkedList; 015 016import com.digi.xbee.api.RemoteXBeeDevice; 017import com.digi.xbee.api.packet.APIFrameType; 018import com.digi.xbee.api.packet.XBeeAPIPacket; 019import com.digi.xbee.api.packet.XBeePacket; 020import com.digi.xbee.api.packet.common.ExplicitRxIndicatorPacket; 021import com.digi.xbee.api.packet.common.ReceivePacket; 022import com.digi.xbee.api.packet.common.RemoteATCommandResponsePacket; 023import com.digi.xbee.api.packet.raw.RX16IOPacket; 024import com.digi.xbee.api.packet.raw.RX16Packet; 025import com.digi.xbee.api.packet.raw.RX64IOPacket; 026import com.digi.xbee.api.packet.raw.RX64Packet; 027 028/** 029 * This class represents a queue of XBee packets used for sequential packets 030 * reading within the XBee Java API. 031 * 032 * <p>The class provides some methods to get specific packet types from 033 * different source nodes.</p> 034 * 035 * @see com.digi.xbee.api.packet.XBeePacket 036 */ 037public class XBeePacketsQueue { 038 039 // Constants. 040 /** 041 * Default maximum number of packets to store in the queue 042 * (value: {@value}). 043 */ 044 public static final int DEFAULT_MAX_LENGTH = 50; 045 046 // Variables. 047 private int maxLength = DEFAULT_MAX_LENGTH; 048 049 private LinkedList<XBeePacket> packetsList; 050 051 private Object lock = new Object(); 052 053 /** 054 * Class constructor. Instantiates a new object of type 055 * {@code XBeePacketsQueue}. 056 */ 057 public XBeePacketsQueue() { 058 this(DEFAULT_MAX_LENGTH); 059 } 060 061 /** 062 * Class constructor. Instantiates a new object of type 063 * {@code XBeePacketsQueue} with the given maximum length. 064 * 065 * @param maxLength Maximum length of the queue. 066 * 067 * @throws IllegalArgumentException if {@code maxLength < 1}. 068 */ 069 public XBeePacketsQueue(int maxLength) { 070 if (maxLength < 1) 071 throw new IllegalArgumentException("Queue length must be greater than 0."); 072 073 this.maxLength = maxLength; 074 packetsList = new LinkedList<XBeePacket>(); 075 } 076 077 /** 078 * Adds the provided packet to the list of packets. If the queue is full 079 * the first packet will be discarded to add the given one. 080 * 081 * @param xbeePacket The XBee packet to be added to the list. 082 * 083 * @see com.digi.xbee.api.packet.XBeePacket 084 */ 085 public void addPacket(XBeePacket xbeePacket) { 086 synchronized (lock) { 087 if (packetsList.size() == maxLength) 088 packetsList.removeFirst(); 089 packetsList.add(xbeePacket); 090 } 091 } 092 093 /** 094 * Clears the list of packets. 095 */ 096 public void clearQueue() { 097 synchronized (lock) { 098 packetsList.clear(); 099 } 100 } 101 102 /** 103 * Returns the first packet from the queue waiting up to the specified 104 * timeout if necessary for an XBee packet to become available. 105 * {@code null }if the queue is empty. 106 * 107 * @param timeout The time in milliseconds to wait for an XBee packet to 108 * become available. 0 to return immediately. 109 * @return The first packet from the queue, {@code null} if it is empty. 110 * 111 * @see com.digi.xbee.api.packet.XBeePacket 112 */ 113 public XBeePacket getFirstPacket(int timeout) { 114 if (timeout > 0) { 115 XBeePacket xbeePacket = getFirstPacket(0); 116 // Wait for a timeout or until an XBee packet is read. 117 Long deadLine = System.currentTimeMillis() + timeout; 118 while (xbeePacket == null && deadLine > System.currentTimeMillis()) { 119 sleep(100); 120 xbeePacket = getFirstPacket(0); 121 } 122 return xbeePacket; 123 } 124 synchronized (lock) { 125 if (!packetsList.isEmpty()) 126 return packetsList.pop(); 127 } 128 return null; 129 } 130 131 /** 132 * Returns the first packet from the queue whose 64-bit source address 133 * matches the address of the provided remote XBee device. 134 * 135 * <p>The methods waits up to the specified timeout if necessary for an 136 * XBee packet to become available. Null if the queue is empty or there is 137 * not any XBee packet sent by the provided remote XBee device.</p> 138 * 139 * @param remoteXBeeDevice The remote XBee device containing the 64-bit 140 * address to look for in the list of packets. 141 * @param timeout The time in milliseconds to wait for an XBee packet from 142 * the specified remote XBee device to become available. 143 * 0 to return immediately. 144 * 145 * @return The first XBee packet whose 64-bit address matches the address 146 * of the provided remote XBee device. {@code null} if no packets 147 * from the specified XBee device are found in the queue. 148 * 149 * @see com.digi.xbee.api.RemoteXBeeDevice 150 * @see com.digi.xbee.api.packet.XBeePacket 151 */ 152 public XBeePacket getFirstPacketFrom(RemoteXBeeDevice remoteXBeeDevice, int timeout) { 153 if (timeout > 0) { 154 XBeePacket xbeePacket = getFirstPacketFrom(remoteXBeeDevice, 0); 155 // Wait for a timeout or until an XBee packet from remoteXBeeDevice is read. 156 Long deadLine = System.currentTimeMillis() + timeout; 157 while (xbeePacket == null && deadLine > System.currentTimeMillis()) { 158 sleep(100); 159 xbeePacket = getFirstPacketFrom(remoteXBeeDevice, 0); 160 } 161 return xbeePacket; 162 } else { 163 synchronized (lock) { 164 for (int i = 0; i < packetsList.size(); i++) { 165 XBeePacket xbeePacket = packetsList.get(i); 166 if (addressesMatch(xbeePacket, remoteXBeeDevice)) 167 return packetsList.remove(i); 168 } 169 } 170 } 171 return null; 172 } 173 174 /** 175 * Returns the first data packet from the queue waiting up to the 176 * specified timeout if necessary for an XBee data packet to become 177 * available. {@code null} if the queue is empty or there is not any data 178 * packet inside. 179 * 180 * @param timeout The time in milliseconds to wait for an XBee data packet 181 * to become available. 0 to return immediately. 182 * 183 * @return The first data packet from the queue, {@code null} if it is 184 * empty or no data packets are contained in the queue. 185 * 186 * @see com.digi.xbee.api.packet.XBeePacket 187 */ 188 public XBeePacket getFirstDataPacket(int timeout) { 189 if (timeout > 0) { 190 XBeePacket xbeePacket = getFirstDataPacket(0); 191 // Wait for a timeout or until a data XBee packet is read. 192 Long deadLine = System.currentTimeMillis() + timeout; 193 while (xbeePacket == null && deadLine > System.currentTimeMillis()) { 194 sleep(100); 195 xbeePacket = getFirstDataPacket(0); 196 } 197 return xbeePacket; 198 } else { 199 synchronized (lock) { 200 for (int i = 0; i < packetsList.size(); i++) { 201 XBeePacket xbeePacket = packetsList.get(i); 202 if (isDataPacket(xbeePacket)) 203 return packetsList.remove(i); 204 } 205 } 206 } 207 return null; 208 } 209 210 /** 211 * Returns the first data packet from the queue whose 64-bit source 212 * address matches the address of the provided remote XBee device. 213 * 214 * <p>The methods waits up to the specified timeout if necessary for an 215 * XBee data packet to become available. {@code null} if the queue is 216 * empty or there is not any XBee data packet sent by the provided remote 217 * XBee device.</p> 218 * 219 * @param remoteXBeeDevice The XBee device containing the 64-bit address 220 * to look for in the list of packets. 221 * @param timeout The time in milliseconds to wait for an XBee data packet 222 * from the specified remote XBee device to become 223 * available. 0 to return immediately. 224 * 225 * @return The first XBee data packet whose its 64-bit address matches the 226 * address of the provided remote XBee device. {@code null} if no 227 * data packets from the specified XBee device are found in the 228 * queue. 229 * 230 * @see com.digi.xbee.api.RemoteXBeeDevice 231 * @see com.digi.xbee.api.packet.XBeePacket 232 */ 233 public XBeePacket getFirstDataPacketFrom(RemoteXBeeDevice remoteXBeeDevice, int timeout) { 234 if (timeout > 0) { 235 XBeePacket xbeePacket = getFirstDataPacketFrom(remoteXBeeDevice, 0); 236 // Wait for a timeout or until an XBee packet from remoteXBeeDevice is read. 237 Long deadLine = System.currentTimeMillis() + timeout; 238 while (xbeePacket == null && deadLine > System.currentTimeMillis()) { 239 sleep(100); 240 xbeePacket = getFirstDataPacketFrom(remoteXBeeDevice, 0); 241 } 242 return xbeePacket; 243 } else { 244 synchronized (lock) { 245 for (int i = 0; i < packetsList.size(); i++) { 246 XBeePacket xbeePacket = packetsList.get(i); 247 if (isDataPacket(xbeePacket) && addressesMatch(xbeePacket, remoteXBeeDevice)) 248 return packetsList.remove(i); 249 } 250 } 251 } 252 return null; 253 } 254 255 /** 256 * Returns the first explicit data packet from the queue waiting up to the 257 * specified timeout if necessary for an XBee explicit data packet to 258 * become available. {@code null} if the queue is empty or there is not 259 * any explicit data packet inside. 260 * 261 * @param timeout The time in milliseconds to wait for an XBee explicit 262 * data packet to become available. 0 to return immediately. 263 * 264 * @return The first explicit data packet from the queue, {@code null} if 265 * it is empty or no data packets are contained in the queue. 266 * 267 * @see com.digi.xbee.api.packet.XBeePacket 268 * @see com.digi.xbee.api.packet.common.ExplicitRxIndicatorPacket 269 */ 270 public XBeePacket getFirstExplicitDataPacket(int timeout) { 271 if (timeout > 0) { 272 XBeePacket xbeePacket = getFirstExplicitDataPacket(0); 273 // Wait for a timeout or until an explicit data XBee packet is read. 274 Long deadLine = System.currentTimeMillis() + timeout; 275 while (xbeePacket == null && deadLine > System.currentTimeMillis()) { 276 sleep(100); 277 xbeePacket = getFirstExplicitDataPacket(0); 278 } 279 return xbeePacket; 280 } else { 281 synchronized (lock) { 282 for (int i = 0; i < packetsList.size(); i++) { 283 XBeePacket xbeePacket = packetsList.get(i); 284 if (isExplicitDataPacket(xbeePacket)) 285 return packetsList.remove(i); 286 } 287 } 288 } 289 return null; 290 } 291 292 /** 293 * Returns the first explicit data packet from the queue whose 64-bit 294 * source address matches the address of the provided remote XBee device. 295 * 296 * <p>The methods waits up to the specified timeout if necessary for an 297 * XBee explicit data packet to become available. {@code null} if the 298 * queue is empty or there is not any XBee explicit data packet sent by 299 * the provided remote XBee device.</p> 300 * 301 * @param remoteXBeeDevice The XBee device containing the 64-bit address 302 * to look for in the list of packets. 303 * @param timeout The time in milliseconds to wait for an XBee explicit 304 * data packet from the specified remote XBee device to 305 * become available. 0 to return immediately. 306 * 307 * @return The first XBee explicit data packet whose its 64-bit address 308 * matches the address of the provided remote XBee device. 309 * {@code null} if no explicit data packets from the specified 310 * XBee device are found in the queue. 311 * 312 * @see com.digi.xbee.api.RemoteXBeeDevice 313 * @see com.digi.xbee.api.packet.XBeePacket 314 * @see com.digi.xbee.api.packet.common.ExplicitRxIndicatorPacket 315 */ 316 public XBeePacket getFirstExplicitDataPacketFrom(RemoteXBeeDevice remoteXBeeDevice, int timeout) { 317 if (timeout > 0) { 318 XBeePacket xbeePacket = getFirstExplicitDataPacketFrom(remoteXBeeDevice, 0); 319 // Wait for a timeout or until an XBee explicit data packet from remoteXBeeDevice is read. 320 Long deadLine = System.currentTimeMillis() + timeout; 321 while (xbeePacket == null && deadLine > System.currentTimeMillis()) { 322 sleep(100); 323 xbeePacket = getFirstExplicitDataPacketFrom(remoteXBeeDevice, 0); 324 } 325 return xbeePacket; 326 } else { 327 synchronized (lock) { 328 for (int i = 0; i < packetsList.size(); i++) { 329 XBeePacket xbeePacket = packetsList.get(i); 330 if (isExplicitDataPacket(xbeePacket) && addressesMatch(xbeePacket, remoteXBeeDevice)) 331 return packetsList.remove(i); 332 } 333 } 334 } 335 return null; 336 } 337 338 /** 339 * Returns whether or not the source address of the provided XBee packet 340 * matches the address of the given remote XBee device. 341 * 342 * @param xbeePacket The XBee packet to compare its address with the 343 * remote XBee device. 344 * @param remoteXBeeDevice The remote XBee device to compare its address 345 * with the XBee packet. 346 * 347 * @return {@code true} if the source address of the provided packet (if 348 * it has) matches the address of the remote XBee device. 349 * 350 * @see com.digi.xbee.api.RemoteXBeeDevice 351 * @see com.digi.xbee.api.packet.XBeePacket 352 */ 353 private boolean addressesMatch(XBeePacket xbeePacket, RemoteXBeeDevice remoteXBeeDevice) { 354 if (!(xbeePacket instanceof XBeeAPIPacket)) 355 return false; 356 APIFrameType packetType = ((XBeeAPIPacket)xbeePacket).getFrameType(); 357 switch (packetType) { 358 case RECEIVE_PACKET: 359 if (remoteXBeeDevice.get64BitAddress() != null && ((ReceivePacket)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress())) 360 return true; 361 if (remoteXBeeDevice.get16BitAddress() != null && ((ReceivePacket)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress())) 362 return true; 363 break; 364 case REMOTE_AT_COMMAND_RESPONSE: 365 if (remoteXBeeDevice.get64BitAddress() != null && ((RemoteATCommandResponsePacket)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress())) 366 return true; 367 if (remoteXBeeDevice.get16BitAddress() != null && ((RemoteATCommandResponsePacket)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress())) 368 return true; 369 break; 370 case RX_16: 371 if (((RX16Packet)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress())) 372 return true; 373 break; 374 case RX_64: 375 if (((RX64Packet)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress())) 376 return true; 377 break; 378 case RX_IO_16: 379 if (((RX16IOPacket)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress())) 380 return true; 381 break; 382 case RX_IO_64: 383 if (((RX64IOPacket)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress())) 384 return true; 385 break; 386 case EXPLICIT_RX_INDICATOR: 387 if (((ExplicitRxIndicatorPacket)xbeePacket).get64BitSourceAddress().equals(remoteXBeeDevice.get64BitAddress())) 388 return true; 389 break; 390 default: 391 return false; 392 } 393 return false; 394 } 395 396 /** 397 * Returns whether or not the given XBee packet is a data packet. 398 * 399 * @param xbeePacket The XBee packet to check if is data packet. 400 * 401 * @return {@code true} if the XBee packet is a data packet, {@code false} 402 * otherwise. 403 * 404 * @see com.digi.xbee.api.packet.XBeePacket 405 */ 406 private boolean isDataPacket(XBeePacket xbeePacket) { 407 if (!(xbeePacket instanceof XBeeAPIPacket)) 408 return false; 409 APIFrameType packetType = ((XBeeAPIPacket)xbeePacket).getFrameType(); 410 switch (packetType) { 411 case RECEIVE_PACKET: 412 case RX_16: 413 case RX_64: 414 return true; 415 default: 416 return false; 417 } 418 } 419 420 /** 421 * Returns whether or not the given XBee packet is an explicit data packet. 422 * 423 * @param xbeePacket The XBee packet to check if is an explicit data packet. 424 * 425 * @return {@code true} if the XBee packet is an explicit data packet, 426 * {@code false} otherwise. 427 * 428 * @see com.digi.xbee.api.packet.XBeePacket 429 * @see com.digi.xbee.api.packet.common.ExplicitRxIndicatorPacket 430 */ 431 private boolean isExplicitDataPacket(XBeePacket xbeePacket) { 432 if (!(xbeePacket instanceof XBeeAPIPacket)) 433 return false; 434 APIFrameType packetType = ((XBeeAPIPacket)xbeePacket).getFrameType(); 435 return packetType == APIFrameType.EXPLICIT_RX_INDICATOR; 436 } 437 438 /** 439 * Sleeps the thread for the given number of milliseconds. 440 * 441 * @param milliseconds The number of milliseconds that the thread should 442 * be sleeping. 443 */ 444 private void sleep(int milliseconds) { 445 try { 446 Thread.sleep(milliseconds); 447 } catch (InterruptedException e) { } 448 } 449 450 /** 451 * Returns the maximum size of the XBee packets queue. 452 * 453 * @return The maximum size of the XBee packets queue. 454 */ 455 public int getMaxSize() { 456 return maxLength; 457 } 458 459 /** 460 * Returns the current size of the XBee packets queue. 461 * 462 * @return The current size of the XBee packets queue. 463 */ 464 public int getCurrentSize() { 465 synchronized (lock) { 466 return packetsList.size(); 467 } 468 } 469}