    001/**
    002 * Copyright (c) 2014 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.ReceivePacket;
    021import com.digi.xbee.api.packet.common.RemoteATCommandResponsePacket;
    022import com.digi.xbee.api.packet.raw.RX16IOPacket;
    023import com.digi.xbee.api.packet.raw.RX16Packet;
    024import com.digi.xbee.api.packet.raw.RX64IOPacket;
    025import com.digi.xbee.api.packet.raw.RX64Packet;
    026
    027/**
    028 * This class represents a queue of XBee packets used for sequential packets
    029 * reading within the XBee Java API.
    030 *
    031 * <p>The class provides some methods to get specific packet types from
    032 * different source nodes.</p>
    033 *
    034 * @see com.digi.xbee.api.packet.XBeePacket
    035 */
    036public class XBeePacketsQueue {
    037
    038        // Constants.
    039        /**
    040         * Default maximum number of packets to store in the queue
    041         * (value: {@value}).
    042         */
    043        public static final int DEFAULT_MAX_LENGTH = 50;
    044
    045        // Variables.
    046        private int maxLength = DEFAULT_MAX_LENGTH;
    047
    048        private LinkedList<XBeePacket> packetsList;
    049
    050        /**
    051         * Class constructor. Instantiates a new object of type
    052         * {@code XBeePacketsQueue}.
    053         */
    054        public XBeePacketsQueue() {
    055                this(DEFAULT_MAX_LENGTH);
    056        }
    057
    058        /**
    059         * Class constructor. Instantiates a new object of type
    060         * {@code XBeePacketsQueue} with the given maximum length.
    061         *
    062         * @param maxLength Maximum length of the queue.
    063         *
    064         * @throws IllegalArgumentException if {@code maxLength < 1}.
    065         */
    066        public XBeePacketsQueue(int maxLength) {
    067                if (maxLength < 1)
    068                        throw new IllegalArgumentException("Queue length must be greater than 0.");
    069
    070                this.maxLength = maxLength;
    071                packetsList = new LinkedList<XBeePacket>();
    072        }
    073
    074        /**
    075         * Adds the provided packet to the list of packets. If the queue is full
    076         * the first packet will be discarded to add the given one.
    077         *
    078         * @param xbeePacket The XBee packet to be added to the list.
    079         *
    080         * @see com.digi.xbee.api.packet.XBeePacket
    081         */
    082        public void addPacket(XBeePacket xbeePacket) {
    083                if (packetsList.size() == maxLength)
    084                        packetsList.removeFirst();
    085                packetsList.add(xbeePacket);
    086        }
    087
    088        /**
    089         * Clears the list of packets.
    090         */
    091        public void clearQueue() {
    092                packetsList.clear();
    093        }
    094
    095        /**
    096         * Returns the first packet from the queue waiting up to the specified
    097         * timeout if  necessary for an XBee packet to become available.
    098         * {@code null }if the queue is empty.
    099         *
    100         * @param timeout The time in milliseconds to wait for an XBee packet to
    101         *                become available. 0 to return immediately.
    102         * @return The first packet from the queue, {@code null} if it is empty.
    103         *
    104         * @see com.digi.xbee.api.packet.XBeePacket
    105         */
    106        public XBeePacket getFirstPacket(int timeout) {
    107                if (timeout > 0) {
    108                        XBeePacket xbeePacket = getFirstPacket(0);
    109                        // Wait for a timeout or until an XBee packet is read.
    110                        Long deadLine = System.currentTimeMillis() + timeout;
    111                        while (xbeePacket == null && deadLine > System.currentTimeMillis()) {
    112                                sleep(100);
    113                                xbeePacket = getFirstPacket(0);
    114                        }
    115                        return xbeePacket;
    116                } else if (!packetsList.isEmpty())
    117                        return packetsList.pop();
    118                return null;
    119        }
    120
    121        /**
    122         * Returns the first packet from the queue whose 64-bit source address
    123         * matches the address of the provided remote XBee device.
    124         *
    125         * <p>The methods waits up to the specified timeout if necessary for an
    126         * XBee packet to become available. Null if the queue is empty or there is
    127         * not any XBee packet sent by the provided remote XBee device.</p>
    128         *
    129         * @param remoteXBeeDevice The remote XBee device containing the 64-bit
    130         *                         address to look for in the list of packets.
    131         * @param timeout The time in milliseconds to wait for an XBee packet from
    132         *                the specified remote XBee device to become available.
    133         *                0 to return immediately.
    134         *
    135         * @return The first XBee packet whose 64-bit address matches the address
    136         *         of the provided remote XBee device. {@code null} if no packets
    137         *         from the specified XBee device are found in the queue.
    138         *
    139         * @see com.digi.xbee.api.RemoteXBeeDevice
    140         * @see com.digi.xbee.api.packet.XBeePacket
    141         */
    142        public XBeePacket getFirstPacketFrom(RemoteXBeeDevice remoteXBeeDevice, int timeout) {
    143                if (timeout > 0) {
    144                        XBeePacket xbeePacket = getFirstPacketFrom(remoteXBeeDevice, 0);
    145                        // Wait for a timeout or until an XBee packet from remoteXBeeDevice is read.
    146                        Long deadLine = System.currentTimeMillis() + timeout;
    147                        while (xbeePacket == null && deadLine > System.currentTimeMillis()) {
    148                                sleep(100);
    149                                xbeePacket = getFirstPacketFrom(remoteXBeeDevice, 0);
    150                        }
    151                        return xbeePacket;
    152                } else {
    153                        for (int i = 0; i < packetsList.size(); i++) {
    154                                XBeePacket xbeePacket = packetsList.get(i);
    155                                if (addressesMatch(xbeePacket, remoteXBeeDevice))
    156                                        return packetsList.remove(i);
    157                        }
    158                }
    159                return null;
    160        }
    161
    162        /**
    163         * Returns the first data packet from the queue waiting up to the
    164         * specified timeout if necessary for an XBee data packet to become
    165         * available. {@code null} if the queue is empty or there is not any data
    166         * packet inside.
    167         *
    168         * @param timeout The time in milliseconds to wait for an XBee data packet
    169         *                to become available. 0 to return immediately.
    170         *
    171         * @return The first data packet from the queue, {@code null} if it is
    172         *         empty or no data packets are contained in the queue.
    173         *
    174         * @see com.digi.xbee.api.packet.XBeePacket
    175         */
    176        public XBeePacket getFirstDataPacket(int timeout) {
    177                if (timeout > 0) {
    178                        XBeePacket xbeePacket = getFirstDataPacket(0);
    179                        // Wait for a timeout or until a data XBee packet is read.
    180                        Long deadLine = System.currentTimeMillis() + timeout;
    181                        while (xbeePacket == null && deadLine > System.currentTimeMillis()) {
    182                                sleep(100);
    183                                xbeePacket = getFirstDataPacket(0);
    184                        }
    185                        return xbeePacket;
    186                } else {
    187                        for (int i = 0; i < packetsList.size(); i++) {
    188                                XBeePacket xbeePacket = packetsList.get(i);
    189                                if (isDataPacket(xbeePacket))
    190                                        return packetsList.remove(i);
    191                        }
    192                }
    193                return null;
    194        }
    195
    196        /**
    197         * Returns the first data packet from the queue whose 64-bit source
    198         * address matches the address of the provided remote XBee device.
    199         *
    200         * <p>The methods waits up to the specified timeout if necessary for an
    201         * XBee data packet to become available. {@code null} if the queue is
    202         * empty or there is not any XBee data packet sent by the provided remote
    203         * XBee device.</p>
    204         *
    205         * @param remoteXBeeDevice The XBee device containing the 64-bit address
    206         *                         to look for in the list of packets.
    207         * @param timeout The time in milliseconds to wait for an XBee data packet
    208         *                from the specified remote XBee device to become
    209         *                available. 0 to return immediately.
    210         *
    211         * @return The first XBee data packet whose its 64-bit address matches the
    212         *         address of the provided remote XBee device. {@code null} if no
    213         *         data packets from the specified XBee device are found in the
    214         *         queue.
    215         *
    216         * @see com.digi.xbee.api.RemoteXBeeDevice
    217         * @see com.digi.xbee.api.packet.XBeePacket
    218         */
    219        public XBeePacket getFirstDataPacketFrom(RemoteXBeeDevice remoteXBeeDevice, int timeout) {
    220                if (timeout > 0) {
    221                        XBeePacket xbeePacket = getFirstDataPacketFrom(remoteXBeeDevice, 0);
    222                        // Wait for a timeout or until an XBee packet from remoteXBeeDevice is read.
    223                        Long deadLine = System.currentTimeMillis() + timeout;
    224                        while (xbeePacket == null && deadLine > System.currentTimeMillis()) {
    225                                sleep(100);
    226                                xbeePacket = getFirstDataPacketFrom(remoteXBeeDevice, 0);
    227                        }
    228                        return xbeePacket;
    229                } else {
    230                        for (int i = 0; i < packetsList.size(); i++) {
    231                                XBeePacket xbeePacket = packetsList.get(i);
    232                                if (isDataPacket(xbeePacket) && addressesMatch(xbeePacket, remoteXBeeDevice))
    233                                        return packetsList.remove(i);
    234                        }
    235                }
    236                return null;
    237        }
    238
    239        /**
    240         * Returns whether or not the source address of the provided XBee packet
    241         * matches the address of the given remote XBee device.
    242         *
    243         * @param xbeePacket The XBee packet to compare its address with the
    244         *                   remote XBee device.
    245         * @param remoteXBeeDevice The remote XBee device to compare its address
    246         *                         with the XBee packet.
    247         *
    248         * @return {@code true} if the source address of the provided packet (if
    249         *         it has) matches the address of the remote XBee device.
    250         *
    251         * @see com.digi.xbee.api.RemoteXBeeDevice
    252         * @see com.digi.xbee.api.packet.XBeePacket
    253         */
    254        private boolean addressesMatch(XBeePacket xbeePacket, RemoteXBeeDevice remoteXBeeDevice) {
    255                if (!(xbeePacket instanceof XBeeAPIPacket))
    256                        return false;
    257                APIFrameType packetType = ((XBeeAPIPacket)xbeePacket).getFrameType();
    258                switch (packetType) {
    259                case RECEIVE_PACKET:
    260                        if (remoteXBeeDevice.get64BitAddress() != null && ((ReceivePacket)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress()))
    261                                return true;
    262                        if (remoteXBeeDevice.get16BitAddress() != null && ((ReceivePacket)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress()))
    263                                return true;
    264                        break;
    265                case REMOTE_AT_COMMAND_RESPONSE:
    266                        if (remoteXBeeDevice.get64BitAddress() != null && ((RemoteATCommandResponsePacket)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress()))
    267                                return true;
    268                        if (remoteXBeeDevice.get16BitAddress() != null && ((RemoteATCommandResponsePacket)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress()))
    269                                return true;
    270                        break;
    271                case RX_16:
    272                        if (((RX16Packet)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress()))
    273                                return true;
    274                        break;
    275                case RX_64:
    276                        if (((RX64Packet)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress()))
    277                                return true;
    278                        break;
    279                case RX_IO_16:
    280                        if (((RX16IOPacket)xbeePacket).get16bitSourceAddress().equals(remoteXBeeDevice.get16BitAddress()))
    281                                return true;
    282                        break;
    283                case RX_IO_64:
    284                        if (((RX64IOPacket)xbeePacket).get64bitSourceAddress().equals(remoteXBeeDevice.get64BitAddress()))
    285                                return true;
    286                        break;
    287                default:
    288                        return false;
    289                }
    290                return false;
    291        }
    292
    293        /**
    294         * Returns whether or not the given XBee packet is a data packet.
    295         *
    296         * @param xbeePacket The XBee packet to check if is data packet.
    297         *
    298         * @return {@code true} if the XBee packet is a data packet, {@code false}
    299         *         otherwise.
    300         *
    301         * @see com.digi.xbee.api.packet.XBeePacket
    302         */
    303        private boolean isDataPacket(XBeePacket xbeePacket) {
    304                if (!(xbeePacket instanceof XBeeAPIPacket))
    305                        return false;
    306                APIFrameType packetType = ((XBeeAPIPacket)xbeePacket).getFrameType();
    307                switch (packetType) {
    308                        case RECEIVE_PACKET:
    309                        case RX_16:
    310                        case RX_64:
    311                                return true;
    312                        default:
    313                                return false;
    314                }
    315        }
    316
    317        /**
    318         * Sleeps the thread for the given number of milliseconds.
    319         *
    320         * @param milliseconds The number of milliseconds that the thread should
    321         *        be sleeping.
    322         */
    323        private void sleep(int milliseconds) {
    324                try {
    325                        Thread.sleep(milliseconds);
    326                } catch (InterruptedException e) { }
    327        }
    328
    329        /**
    330         * Returns the maximum size of the XBee packets queue.
    331         *
    332         * @return The maximum size of the XBee packets queue.
    333         */
    334        public int getMaxSize() {
    335                return maxLength;
    336        }
    337
    338        /**
    339         * Returns the current size of the XBee packets queue.
    340         *
    341         * @return The current size of the XBee packets queue.
    342         */
    343        public int getCurrentSize() {
    344                return packetsList.size();
    345        }
    346}