001/*
002 * Copyright 2017-2019, Digi International Inc.
003 *
004 * This Source Code Form is subject to the terms of the Mozilla Public
005 * License, v. 2.0. If a copy of the MPL was not distributed with this
006 * file, you can obtain one at http://mozilla.org/MPL/2.0/.
007 *
008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
015 */
016package com.digi.xbee.api;
017
018import java.net.Inet6Address;
019import java.net.UnknownHostException;
020
021import com.digi.xbee.api.connection.IConnectionInterface;
022import com.digi.xbee.api.connection.serial.SerialPortParameters;
023import com.digi.xbee.api.exceptions.InterfaceNotOpenException;
024import com.digi.xbee.api.exceptions.OperationNotSupportedException;
025import com.digi.xbee.api.exceptions.TimeoutException;
026import com.digi.xbee.api.exceptions.XBeeException;
027import com.digi.xbee.api.listeners.IDataReceiveListener;
028import com.digi.xbee.api.listeners.IIPDataReceiveListener;
029import com.digi.xbee.api.models.IPMessage;
030import com.digi.xbee.api.models.IPProtocol;
031import com.digi.xbee.api.models.XBee16BitAddress;
032import com.digi.xbee.api.models.XBee64BitAddress;
033import com.digi.xbee.api.models.XBeeMessage;
034import com.digi.xbee.api.models.XBeePacketsQueue;
035import com.digi.xbee.api.packet.XBeeAPIPacket;
036import com.digi.xbee.api.packet.XBeePacket;
037import com.digi.xbee.api.packet.thread.RXIPv6Packet;
038import com.digi.xbee.api.packet.thread.TXIPv6Packet;
039import com.digi.xbee.api.utils.ByteUtils;
040import com.digi.xbee.api.utils.HexUtils;
041
042/**
043 * This class provides common functionality for XBee IPv6 devices.
044 * 
045 * @see ThreadDevice
046 * 
047 * @since 1.2.1
048 */
049public class IPv6Device extends XBeeDevice {
050
051        // Constants
052        private static final String OPERATION_EXCEPTION = "Operation not supported in this module.";
053        
054        protected static final short DEFAULT_SOURCE_PORT = 9750;
055        
056        protected static final IPProtocol DEFAULT_PROTOCOL = IPProtocol.TCP;
057        
058        // Variables
059        protected int sourcePort = DEFAULT_SOURCE_PORT;
060        
061        /**
062         * Class constructor. Instantiates a new {@code IPv6Device} object in 
063         * the given port name and baud rate.
064         * 
065         * @param port Serial port name where IPv6 device is attached to.
066         * @param baudRate Serial port baud rate to communicate with the device. 
067         *                 Other connection parameters will be set as default (8 
068         *                 data bits, 1 stop bit, no parity, no flow control).
069         * 
070         * @throws IllegalArgumentException if {@code baudRate < 0}.
071         * @throws NullPointerException if {@code port == null}.
072         * 
073         * @see #IPv6Device(IConnectionInterface)
074         * @see #IPv6Device(String, SerialPortParameters)
075         * @see #IPv6Device(String, int, int, int, int, int)
076         */
077        protected IPv6Device(String port, int baudRate) {
078                this(XBee.createConnectiontionInterface(port, baudRate));
079        }
080        
081        /**
082         * Class constructor. Instantiates a new {@code IPv6Device} object in 
083         * the given serial port name and settings.
084         * 
085         * @param port Serial port name where IPv6 device is attached to.
086         * @param baudRate Serial port baud rate to communicate with the device.
087         * @param dataBits Serial port data bits.
088         * @param stopBits Serial port stop bits.
089         * @param parity Serial port parity.
090         * @param flowControl Serial port flow control.
091         * 
092         * @throws IllegalArgumentException if {@code baudRate < 0} or
093         *                                  if {@code dataBits < 0} or
094         *                                  if {@code stopBits < 0} or
095         *                                  if {@code parity < 0} or
096         *                                  if {@code flowControl < 0}.
097         * @throws NullPointerException if {@code port == null}.
098         * 
099         * @see #IPv6Device(IConnectionInterface)
100         * @see #IPv6Device(String, int)
101         * @see #IPv6Device(String, SerialPortParameters)
102         */
103        protected IPv6Device(String port, int baudRate, int dataBits, int stopBits,
104                        int parity, int flowControl) {
105                this(port, new SerialPortParameters(baudRate, dataBits, stopBits, parity, flowControl));
106        }
107        
108        /**
109         * Class constructor. Instantiates a new {@code IPv6Device} object in 
110         * the given serial port name and parameters.
111         * 
112         * @param port Serial port name where IPv6 device is attached to.
113         * @param serialPortParameters Object containing the serial port parameters.
114         * 
115         * @throws NullPointerException if {@code port == null} or
116         *                              if {@code serialPortParameters == null}.
117         * 
118         * @see #IPv6Device(IConnectionInterface)
119         * @see #IPv6Device(String, int)
120         * @see #IPv6Device(String, int, int, int, int, int)
121         * @see com.digi.xbee.api.connection.serial.SerialPortParameters
122         */
123        protected IPv6Device(String port, SerialPortParameters serialPortParameters) {
124                this(XBee.createConnectiontionInterface(port, serialPortParameters));
125        }
126        
127        /**
128         * Class constructor. Instantiates a new {@code IPv6Device} object with 
129         * the given connection interface.
130         * 
131         * @param connectionInterface The connection interface with the physical 
132         *                            IPv6 device.
133         * 
134         * @throws NullPointerException if {@code connectionInterface == null}
135         * 
136         * @see #IPv6Device(String, int)
137         * @see #IPv6Device(String, SerialPortParameters)
138         * @see #IPv6Device(String, int, int, int, int, int)
139         * @see com.digi.xbee.api.connection.IConnectionInterface
140         */
141        protected IPv6Device(IConnectionInterface connectionInterface) {
142                super(connectionInterface);
143        }
144        
145        /**
146         * @deprecated This protocol does not support the network functionality.
147         */
148        @Override
149        public XBeeNetwork getNetwork() {
150                // IPv6 modules do not have a network of devices.
151                return null;
152        }
153        
154        /*
155         * (non-Javadoc)
156         * @see com.digi.xbee.api.AbstractXBeeDevice#readDeviceInfo()
157         */
158        @Override
159        public void readDeviceInfo() throws TimeoutException, XBeeException {
160                super.readDeviceInfo();
161                // Generate the Mesh-Local IPv6 address.
162                byte[] response = getParameter("MY");
163                try {
164                        ipv6Address = (Inet6Address) Inet6Address.getByAddress(response);
165                } catch (UnknownHostException e) {
166                        throw new XBeeException(e);
167                }
168                
169                // Read the source port.
170                try {
171                        response = getParameter("C0");
172                        sourcePort = ByteUtils.byteArrayToInt(response);
173                } catch (TimeoutException e) {
174                        // Do not refresh the source port value if there is an error reading
175                        // it from the module.
176                } catch (XBeeException e) {
177                        // Do not refresh the source port value if there is an error reading
178                        // it from the module.
179                }
180        }
181        
182        /**
183         * @deprecated This protocol does not have an associated 16-bit address.
184         */
185        @Override
186        public XBee16BitAddress get16BitAddress() {
187                // IPv6 modules do not have 16-bit address.
188                return null;
189        }
190        
191        /**
192         * @deprecated Operation not supported in this protocol. Use
193         *             {@link #getIPv6DestinationAddress()} instead.
194         *             This method will raise an 
195         *             {@link UnsupportedOperationException}.
196         */
197        @Override
198        public XBee64BitAddress getDestinationAddress() throws TimeoutException,
199                        XBeeException {
200                // Not supported in IPv6 modules.
201                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
202        }
203        
204        /**
205         * @deprecated Operation not supported in this protocol. Use
206         *             {@link #setIPv6DestinationAddress(Inet6Address)} instead.
207         *             This method will raise an 
208         *             {@link UnsupportedOperationException}.
209         */
210        @Override
211        public void setDestinationAddress(XBee64BitAddress xbee64BitAddress)
212                        throws TimeoutException, XBeeException {
213                // Not supported in IPv6 modules.
214                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
215        }
216        
217        /**
218         * @deprecated Operation not supported in this protocol. This method will
219         *             raise an {@link UnsupportedOperationException}.
220         */
221        @Override
222        public void addDataListener(IDataReceiveListener listener) {
223                // Not supported in IPv6 modules.
224                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
225        }
226        
227        /**
228         * @deprecated Operation not supported in this protocol. This method will
229         *             raise an {@link UnsupportedOperationException}.
230         */
231        @Override
232        public void removeDataListener(IDataReceiveListener listener) {
233                // Not supported in IPv6 modules.
234                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
235        }
236        
237        /**
238         * @deprecated Operation not supported in this protocol. This method will
239         *             raise an {@link UnsupportedOperationException}.
240         */
241        @Override
242        public XBeeMessage readData() {
243                // Not supported in IPv6 modules.
244                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
245        }
246        
247        /**
248         * @deprecated Operation not supported in this protocol. This method will
249         *             raise an {@link UnsupportedOperationException}.
250         */
251        @Override
252        public XBeeMessage readData(int timeout) {
253                // Not supported in IPv6 modules.
254                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
255        }
256        
257        /**
258         * @deprecated Operation not supported in this protocol. This method will
259         *             raise an {@link UnsupportedOperationException}.
260         */
261        @Override
262        public XBeeMessage readDataFrom(RemoteXBeeDevice remoteXBeeDevice) {
263                // Not supported in IPv6 modules.
264                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
265        }
266        
267        /**
268         * @deprecated Operation not supported in this protocol. This method will
269         *             raise an {@link UnsupportedOperationException}.
270         */
271        @Override
272        public XBeeMessage readDataFrom(RemoteXBeeDevice remoteXBeeDevice,
273                        int timeout) {
274                // Not supported in IPv6 modules.
275                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
276        }
277        
278        /**
279         * @deprecated Operation not supported in this protocol. This method will
280         *             raise an {@link UnsupportedOperationException}.
281         */
282        @Override
283        public void sendBroadcastData(byte[] data) throws TimeoutException,
284                        XBeeException {
285                // Not supported in IPv6 modules.
286                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
287        }
288        
289        /**
290         * @deprecated Operation not supported in this protocol. This method will
291         *             raise an {@link UnsupportedOperationException}.
292         */
293        @Override
294        public void sendData(RemoteXBeeDevice remoteXBeeDevice, byte[] data)
295                        throws TimeoutException, XBeeException {
296                // Not supported in IPv6 modules.
297                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
298        }
299        
300        /**
301         * @deprecated Operation not supported in this protocol. This method will
302         *             raise an {@link UnsupportedOperationException}.
303         */
304        @Override
305        public void sendDataAsync(RemoteXBeeDevice remoteXBeeDevice, byte[] data)
306                        throws XBeeException {
307                // Not supported in IPv6 modules.
308                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
309        }
310        
311        /*
312         * (non-Javadoc)
313         * @see com.digi.xbee.api.AbstractXBeeDevice#addIPDataListener(com.digi.xbee.api.listeners.IIPDataReceiveListener)
314         */
315        @Override
316        public void addIPDataListener(IIPDataReceiveListener listener) {
317                super.addIPDataListener(listener);
318        }
319        
320        /*
321         * (non-Javadoc)
322         * @see com.digi.xbee.api.AbstractXBeeDevice#removeIPDataListener(com.digi.xbee.api.listeners.IIPDataReceiveListener)
323         */
324        @Override
325        public void removeIPDataListener(IIPDataReceiveListener listener) {
326                super.removeIPDataListener(listener);
327        }
328        
329        /**
330         * Starts listening for incoming IPv6 transmissions in the provided port.
331         * 
332         * @param sourcePort Port to listen for incoming transmissions.
333         * 
334         * @throws IllegalArgumentException if {@code sourcePort < 0} or 
335         *                                  if {@code sourcePort > 65535}.
336         * @throws TimeoutException if there is a timeout setting the source port.
337         * @throws XBeeException if there is any error setting the source port.
338         * 
339         * @see #stopListening()
340         */
341        public void startListening(int sourcePort) throws TimeoutException, XBeeException {
342                if (sourcePort < 0 || sourcePort > 65535)
343                        throw new IllegalArgumentException("Source port must be between 0 and 65535.");
344                
345                setParameter("C0", ByteUtils.shortToByteArray((short)sourcePort));
346                this.sourcePort = sourcePort;
347        }
348        
349        /**
350         * Stops listening for incoming IPv6 transmissions.
351         * 
352         * @throws TimeoutException if there is a timeout processing the operation.
353         * @throws XBeeException if there is any other XBee related exception.
354         * 
355         * @see #startListening(int)
356         */
357        public void stopListening() throws TimeoutException, XBeeException {
358                setParameter("C0", ByteUtils.shortToByteArray((short)0));
359                sourcePort = 0;
360        }
361        
362        /**
363         * Sends the provided IPv6 data to the given IPv6 address and port using 
364         * the specified IPv6 protocol.
365         * 
366         * <p>This method blocks till a success or error response arrives or the 
367         * configured receive timeout expires.</p>
368         * 
369         * <p>The receive timeout is configured using the {@code setReceiveTimeout}
370         * method and can be consulted with {@code getReceiveTimeout} method.</p>
371         * 
372         * <p>For non-blocking operations use the method 
373         * {@link #sendIPDataAsync(Inet6Address, int, IPProtocol, byte[])}.</p>
374         * 
375         * @param ipv6Address The IPv6 address to send IPv6 data to.
376         * @param destPort The destination port of the transmission.
377         * @param protocol The IPv6 protocol used for the transmission.
378         * @param data Byte array containing the IPv6 data to be sent.
379         * 
380         * @throws IllegalArgumentException if {@code destPort < 0} or 
381         *                                  if {@code destPort > 65535}
382         * @throws InterfaceNotOpenException if this device connection is not open.
383         * @throws NullPointerException if {@code ipv6Address == null} or 
384         *                              if {@code protocol == null} or 
385         *                              if {@code data == null}.
386         * @throws TimeoutException if there is a timeout sending the data.
387         * @throws XBeeException if there is any other XBee related exception.
388         * 
389         * @see #getReceiveTimeout()
390         * @see #sendIPDataAsync(Inet6Address, int, IPProtocol, byte[])
391         * @see #setReceiveTimeout(int)
392         * @see com.digi.xbee.api.models.IPProtocol
393         * @see java.net.Inet6Address
394         */
395        public void sendIPData(Inet6Address ipv6Address, int destPort, 
396                        IPProtocol protocol, byte[] data) throws TimeoutException, XBeeException {
397                sendIPData(ipv6Address, destPort, protocol, data, false);
398        }
399        
400        /**
401         * Sends the provided IPv6 data to the given IPv6 address and port 
402         * asynchronously using the specified IPv6 protocol.
403         * 
404         * <p>Asynchronous transmissions do not wait for answer from the remote 
405         * device or for transmit status packet.</p>
406         * 
407         * @param ipv6Address The IPv6 address to send IPv6 data to.
408         * @param destPort The destination port of the transmission.
409         * @param protocol The IPv6 protocol used for the transmission.
410         * @param data Byte array containing the IPv6 data to be sent.
411         * 
412         * @throws IllegalArgumentException if {@code destPort < 0} or 
413         *                                  if {@code destPort > 65535}
414         * @throws InterfaceNotOpenException if this device connection is not open.
415         * @throws NullPointerException if {@code ipv6Address == null} or 
416         *                              if {@code protocol == null} or 
417         *                              if {@code data == null}.
418         * @throws TimeoutException if there is a timeout sending the data.
419         * @throws XBeeException if there is any other XBee related exception.
420         * 
421         * @see #sendIPData(Inet6Address, int, IPProtocol, byte[])
422         * @see com.digi.xbee.api.models.IPProtocol
423         * @see java.net.Inet6Address
424         */
425        public void sendIPDataAsync(Inet6Address ipv6Address, int destPort, 
426                        IPProtocol protocol, byte[] data) throws XBeeException {
427                sendIPData(ipv6Address, destPort, protocol, data, true);
428        }
429        
430        /**
431         * Sends the provided IPv6 data to the given IPv6 address and port using 
432         * the specified IPv6 protocol.
433         * 
434         * <p>Transmissions can be performed synchronously or asynchronously. 
435         * Synchronous operation blocks till a success or error response arrives 
436         * or the configured receive timeout expires. Asynchronous transmissions 
437         * do not wait for answer from the remote device or for transmit status 
438         * packet.</p>
439         * 
440         * <p>The receive timeout is configured using the {@code setReceiveTimeout}
441         * method and can be consulted with {@code getReceiveTimeout} method.</p>
442         * 
443         * <p>For synchronous operations use this method:</p>
444         * <ul>
445         *   <li>{@link #sendIPData(Inet6Address, int, IPProtocol, byte[])}.</li>
446         * </ul>
447         * <p>For asynchronous operations use this one:</p>
448         * <ul>
449         *   <li>{@link #sendIPDataAsync(Inet6Address, int, IPProtocol, byte[])}.</li>
450         * </ul>
451         * 
452         * @param ipv6Address The IPv6 address to send IPv6 data to.
453         * @param destPort The destination port of the transmission.
454         * @param protocol The IPv6 protocol used for the transmission.
455         * @param data Byte array containing the IPv6 data to be sent.
456         * @param async Boolean that should be set to {@code true} if the 
457         *        transmission should be asynchronous, and {@code false} otherwise.
458         * 
459         * @throws IllegalArgumentException if {@code destPort < 0} or 
460         *                                  if {@code destPort > 65535}
461         * @throws InterfaceNotOpenException if this device connection is not open.
462         * @throws NullPointerException if {@code ipv6Address == null} or 
463         *                              if {@code protocol == null} or 
464         *                              if {@code data == null}.
465         * @throws TimeoutException if there is a timeout sending the data.
466         * @throws XBeeException if there is any other XBee related exception.
467         * 
468         * @see #getReceiveTimeout()
469         * @see #sendIPData(Inet6Address, int, IPProtocol, byte[])
470         * @see #sendIPDataAsync(Inet6Address, int, IPProtocol, byte[])
471         * @see #setReceiveTimeout(int)
472         * @see com.digi.xbee.api.models.IPProtocol
473         * @see java.net.Inet6Address
474         */
475        private void sendIPData(Inet6Address ipv6Address, int destPort, 
476                        IPProtocol protocol, byte[] data, boolean async) throws XBeeException {
477                if (ipv6Address == null)
478                        throw new NullPointerException("IPv6 address cannot be null");
479                if (data == null)
480                        throw new NullPointerException("Data cannot be null");
481                if (destPort < 0 || destPort > 65535)
482                        throw new IllegalArgumentException("Destination port must be between 0 and 65535.");
483                
484                // Check if device is remote.
485                if (isRemote())
486                        throw new OperationNotSupportedException("Cannot send IPv6 data from a remote device.");
487                
488                // The source port value depends on the protocol used in the transmission. For UDP, source port 
489                // value must be the same as 'C0' one. For TCP it must be 0.
490                int sourcePort = this.sourcePort;
491                if (protocol != IPProtocol.UDP)
492                        sourcePort = 0;
493                
494                if (async)
495                        logger.debug(toString() + "Sending IPv6 data asynchronously to {}:{} >> {}.", ipv6Address,
496                                        destPort, HexUtils.prettyHexString(data));
497                else
498                        logger.debug(toString() + "Sending IPv6 data to {}:{} >> {}.", ipv6Address,
499                                        destPort, HexUtils.prettyHexString(data));
500                
501                XBeePacket xbeePacket = new TXIPv6Packet(getNextFrameID(), ipv6Address, destPort,
502                                sourcePort, protocol, data);
503                
504                sendAndCheckXBeePacket(xbeePacket, async);
505        }
506        
507        /**
508         * Reads new IPv6 data received by this XBee device during the 
509         * configured receive timeout.
510         * 
511         * <p>This method blocks until new IPv6 data is received or the 
512         * configured receive timeout expires.</p>
513         * 
514         * <p>The receive timeout is configured using the {@code setReceiveTimeout}
515         * method and can be consulted with {@code getReceiveTimeout} method.</p>
516         * 
517         * <p>For non-blocking operations, register a {@code IIPDataReceiveListener} 
518         * using the method {@link #addIPDataListener(IIPDataReceiveListener)}.</p>
519         * 
520         * <p>Before reading IPv6 data you need to start listening for incoming 
521         * IPv6 data at a specific port. Use the {@code startListening} method 
522         * for that purpose. When finished, you can use the {@code stopListening} 
523         * method to stop listening for incoming IPv6 data.</p>
524         * 
525         * @return A {@code IPMessage} object containing the IPv6 data and 
526         *         the IPv6 address that sent the data. {@code null} if this did not 
527         *         receive new IPv6 data during the configured receive timeout.
528         * 
529         * @throws InterfaceNotOpenException if this device connection is not open.
530         * 
531         * @see #getReceiveTimeout()
532         * @see #readIPData(int)
533         * @see #readIPDataFrom(Inet6Address)
534         * @see #readIPDataFrom(Inet6Address, int)
535         * @see #setReceiveTimeout(int)
536         * @see #startListening(int)
537         * @see #stopListening()
538         * @see com.digi.xbee.api.models.IPMessage
539         */
540        public IPMessage readIPData() {
541                return readIPDataPacket(null, TIMEOUT_READ_PACKET);
542        }
543        
544        /**
545         * Reads new IPv6 data received by this XBee device during the provided 
546         * timeout.
547         * 
548         * <p>This method blocks until new IPv6 data is received or the provided 
549         * timeout expires.</p>
550         * 
551         * <p>For non-blocking operations, register a {@code IIPDataReceiveListener} 
552         * using the method {@link #addIPDataListener(IIPDataReceiveListener)}.</p>
553         * 
554         * <p>Before reading IPv6 data you need to start listening for incoming 
555         * IPv6 data at a specific port. Use the {@code startListening} method 
556         * for that purpose. When finished, you can use the {@code stopListening} 
557         * method to stop listening for incoming IPv6 data.</p>
558         * 
559         * @param timeout The time to wait for new IPv6 data in milliseconds.
560         * 
561         * @return A {@code IPMessage} object containing the data and the IPv6 
562         *         address that sent the data. {@code null} if this device did not 
563         *         receive new data during {@code timeout} milliseconds.
564         * 
565         * @throws IllegalArgumentException if {@code timeout < 0}.
566         * @throws InterfaceNotOpenException if this device connection is not open.
567         * 
568         * @see #readIPData()
569         * @see #readIPDataFrom(Inet6Address)
570         * @see #readIPDataFrom(Inet6Address, int)
571         * @see #startListening(int)
572         * @see #stopListening()
573         * @see com.digi.xbee.api.models.IPMessage
574         */
575        public IPMessage readIPData(int timeout) {
576                if (timeout < 0)
577                        throw new IllegalArgumentException("Read timeout must be 0 or greater.");
578                
579                return readIPDataPacket(null, timeout);
580        }
581        
582        /**
583         * Reads new IPv6 data received from the given IPv6 address during the 
584         * configured receive timeout.
585         * 
586         * <p>This method blocks until new data from the provided IPv6 address is 
587         * received or the configured receive timeout expires.</p>
588         * 
589         * <p>The receive timeout is configured using the {@code setReceiveTimeout}
590         * method and can be consulted with {@code getReceiveTimeout} method.</p>
591         * 
592         * <p>For non-blocking operations, register a {@code IIPDataReceiveListener} 
593         * using the method {@link #addIPDataListener(IIPDataReceiveListener)}.</p>
594         * 
595         * <p>Before reading IPv6 data you need to start listening for incoming 
596         * IPv6 data at a specific port. Use the {@code startListening} method 
597         * for that purpose. When finished, you can use the {@code stopListening} 
598         * method to stop listening for incoming IPv6 data.</p>
599         * 
600         * @param ipv6Address The IPv6 address to read data from.
601         * 
602         * @return A {@code IPMessage} object containing the IPv6 data and 
603         *         the IPv6 address of the remote node that sent the data. 
604         *         {@code null} if this device did not receive new IPv6 data 
605         *         from the provided IPv6 address during the configured receive 
606         *         timeout.
607         * 
608         * @throws InterfaceNotOpenException if this device connection is not open.
609         * @throws NullPointerException if {@code ipv6Address == null}.
610         * 
611         * @see #getReceiveTimeout()
612         * @see #readIPData()
613         * @see #readIPData(int)
614         * @see #readIPDataFrom(Inet6Address, int)
615         * @see #setReceiveTimeout(int)
616         * @see #startListening(int)
617         * @see #stopListening()
618         * @see com.digi.xbee.api.models.IPMessage
619         * @see java.net.Inet6Address
620         */
621        public IPMessage readIPDataFrom(Inet6Address ipv6Address) {
622                if (ipv6Address == null)
623                        throw new NullPointerException("IPv6 address cannot be null.");
624                
625                return readIPDataPacket(ipv6Address, TIMEOUT_READ_PACKET);
626        }
627        
628        /**
629         * Reads new IPv6 data received from the given IPv6 address during the 
630         * provided timeout.
631         * 
632         * <p>This method blocks until new IPv6 data from the provided IPv6 
633         * address is received or the given timeout expires.</p>
634         * 
635         * <p>For non-blocking operations, register a {@code IIPDataReceiveListener} 
636         * using the method {@link #addIPDataListener(IIPDataReceiveListener)}.</p>
637         * 
638         * <p>Before reading IPv6 data you need to start listening for incoming 
639         * IPv6 data at a specific port. Use the {@code startListening} method 
640         * for that purpose. When finished, you can use the {@code stopListening} 
641         * method to stop listening for incoming IPv6 data.</p>
642         * 
643         * @param ipv6Address The IPv6 address to read data from.
644         * @param timeout The time to wait for new IPv6 data in milliseconds.
645         * 
646         * @return An {@code IPMessage} object containing the IPv6 data and 
647         *         the IPv6 address that sent the data. {@code null} if this device 
648         *         did not receive new IPv6 data from the provided IPv6 address 
649         *         during {@code timeout} milliseconds.
650         * 
651         * @throws IllegalArgumentException if {@code timeout < 0}.
652         * @throws InterfaceNotOpenException if this device connection is not open.
653         * @throws NullPointerException if {@code ipv6Address == null}.
654         * 
655         * @see #readIPDataFrom(Inet6Address)
656         * @see #readIPData()
657         * @see #readIPData(int)
658         * @see #startListening(int)
659         * @see #stopListening()
660         * @see com.digi.xbee.api.models.IPMessage
661         * @see java.net.Inet6Address
662         */
663        public IPMessage readIPDataFrom(Inet6Address ipv6Address, int timeout) {
664                if (ipv6Address == null)
665                        throw new NullPointerException("IPv6 address cannot be null.");
666                if (timeout < 0)
667                        throw new IllegalArgumentException("Read timeout must be 0 or greater.");
668                
669                return readIPDataPacket(ipv6Address, timeout);
670        }
671        
672        /**
673         * Reads a new IPv6 data packet received by this IPv6 XBee device during 
674         * the provided timeout.
675         * 
676         * <p>This method blocks until new IPv6 data is received or the given 
677         * timeout expires.</p>
678         * 
679         * <p>If the provided IPv6 address is {@code null} the method returns 
680         * the first IPv6 data packet read from any IPv6 address.
681         * <br>
682         * If the IPv6 address is not {@code null} the method returns the first 
683         * data package read from the provided IPv6 address.
684         * </p>
685         * 
686         * @param remoteIPAddress The IPv6 address to get an IPv6 data packet from. 
687         *                        {@code null} to read an IPv6 data packet from 
688         *                        any IPv6 address.
689         * @param timeout The time to wait for a IPv6 data packet in milliseconds.
690         * 
691         * @return A {@code IPMessage} received by this device, containing the 
692         *         data and the source IPv6 address that sent the IPv6 data. 
693         *         {@code null} if this device did not receive new IPv6 data 
694         *         during {@code timeout} milliseconds, or if any error occurs while
695         *         trying to get the source of the message.
696         * 
697         * @throws InterfaceNotOpenException if this device connection is not open.
698         * 
699         * @see com.digi.xbee.api.models.IPMessage
700         * @see java.net.Inet6Address
701         */
702        private IPMessage readIPDataPacket(Inet6Address remoteIPAddress, int timeout) {
703                // Check connection.
704                if (!connectionInterface.isOpen())
705                        throw new InterfaceNotOpenException();
706                
707                XBeePacketsQueue xbeePacketsQueue = dataReader.getXBeePacketsQueue();
708                XBeePacket xbeePacket = null;
709                
710                if (remoteIPAddress != null)
711                        xbeePacket = xbeePacketsQueue.getFirstIPv6DataPacketFrom(remoteIPAddress, timeout);
712                else
713                        xbeePacket = xbeePacketsQueue.getFirstIPv6DataPacket(timeout);
714                
715                if (xbeePacket == null)
716                        return null;
717                
718                // Obtain the data and IPv6 address from the packet.
719                byte[] data = null;
720                Inet6Address ipv6Address = null;
721                int sourcePort;
722                int destPort;
723                IPProtocol protocol = IPProtocol.TCP;
724                
725                switch (((XBeeAPIPacket)xbeePacket).getFrameType()) {
726                case RX_IPV6:
727                        RXIPv6Packet receivePacket = (RXIPv6Packet)xbeePacket;
728                        data = receivePacket.getData();
729                        ipv6Address = receivePacket.getSourceAddress();
730                        sourcePort = receivePacket.getSourcePort();
731                        destPort = receivePacket.getDestPort();
732                        break;
733                default:
734                        return null;
735                }
736                
737                // Create and return the IPv6 message.
738                return new IPMessage(ipv6Address, sourcePort, destPort, protocol, data);
739        }
740}