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}