Skip to content

Commit 8602abc

Browse files
committed
thread: add full thread support to the library
- Created ThreadDevice and RemoteThreadDevice classes. - Updated the rest of classes. - Added IPv6Device abstraction class. - Added several enum classes. - Included unit tests for all the changes and additions. Signed-off-by: Héctor González <hector.gonzalez@digi.com>
1 parent d2f70d9 commit 8602abc

28 files changed

+6627
-16
lines changed

library/src/main/java/com/digi/xbee/api/AbstractXBeeDevice.java

Lines changed: 283 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package com.digi.xbee.api;
1717

1818
import java.io.IOException;
19+
import java.net.Inet6Address;
20+
import java.net.UnknownHostException;
1921
import java.util.ArrayList;
2022
import java.util.Arrays;
2123
import java.util.Set;
@@ -55,6 +57,7 @@
5557
import com.digi.xbee.api.models.HardwareVersion;
5658
import com.digi.xbee.api.models.PowerLevel;
5759
import com.digi.xbee.api.models.RemoteATCommandOptions;
60+
import com.digi.xbee.api.models.RestFulStatusEnum;
5861
import com.digi.xbee.api.models.XBee16BitAddress;
5962
import com.digi.xbee.api.models.XBee64BitAddress;
6063
import com.digi.xbee.api.models.OperatingMode;
@@ -73,6 +76,10 @@
7376
import com.digi.xbee.api.packet.raw.RX16IOPacket;
7477
import com.digi.xbee.api.packet.raw.RX64IOPacket;
7578
import com.digi.xbee.api.packet.raw.TXStatusPacket;
79+
import com.digi.xbee.api.packet.thread.CoAPRxResponsePacket;
80+
import com.digi.xbee.api.packet.thread.CoAPTxRequestPacket;
81+
import com.digi.xbee.api.packet.thread.IPv6RemoteATCommandRequestPacket;
82+
import com.digi.xbee.api.packet.thread.IPv6RemoteATCommandResponsePacket;
7683
import com.digi.xbee.api.utils.ByteUtils;
7784
import com.digi.xbee.api.utils.HexUtils;
7885

@@ -133,6 +140,8 @@ public abstract class AbstractXBeeDevice {
133140
protected XBee16BitAddress xbee16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS;
134141
protected XBee64BitAddress xbee64BitAddress = XBee64BitAddress.UNKNOWN_ADDRESS;
135142

143+
protected Inet6Address ipv6Address = null;
144+
136145
protected int currentFrameID = 0xFF;
137146
protected int receiveTimeout = DEFAULT_RECEIVE_TIMETOUT;
138147

@@ -384,6 +393,73 @@ public AbstractXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64,
384393
connectionInterface.getClass().getSimpleName());
385394
}
386395

396+
/**
397+
* Class constructor. Instantiates a new {@code RemoteXBeeDevice} object
398+
* with the given local {@code XBeeDevice} which contains the connection
399+
* interface to be used.
400+
*
401+
* @param localXBeeDevice The local XBee device that will behave as
402+
* connection interface to communicate with this
403+
* remote XBee device.
404+
* @param ipv6Addr The IPv6 address to identify this XBee device.
405+
*
406+
* @throws IllegalArgumentException If {@code localXBeeDevice.isRemote() == true}.
407+
* @throws NullPointerException if {@code localXBeeDevice == null} or
408+
* if {@code ipv6Addr == null}.
409+
*
410+
* @see #AbstractXBeeDevice(IConnectionInterface)
411+
* @see #AbstractXBeeDevice(String, int)
412+
* @see #AbstractXBeeDevice(String, SerialPortParameters)
413+
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
414+
* @see #AbstractXBeeDevice(Context, int)
415+
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
416+
* @see java.net.Inet6Address
417+
*/
418+
public AbstractXBeeDevice(XBeeDevice localXBeeDevice, Inet6Address ipv6Addr) {
419+
this(localXBeeDevice, ipv6Addr, null);
420+
}
421+
422+
/**
423+
* Class constructor. Instantiates a new {@code RemoteXBeeDevice} object
424+
* with the given local {@code XBeeDevice} which contains the connection
425+
* interface to be used.
426+
*
427+
* @param localXBeeDevice The local XBee device that will behave as
428+
* connection interface to communicate with this
429+
* remote XBee device.
430+
* @param ipv6Addr The IPv6 address to identify this XBee device.
431+
* @param id The node identifier of this XBee device. It might be
432+
* {@code null}.
433+
*
434+
* @throws IllegalArgumentException If {@code localXBeeDevice.isRemote() == true}.
435+
* @throws NullPointerException if {@code localXBeeDevice == null} or
436+
* if {@code ipv6Addr == null}.
437+
*
438+
* @see #AbstractXBeeDevice(IConnectionInterface)
439+
* @see #AbstractXBeeDevice(String, int)
440+
* @see #AbstractXBeeDevice(String, SerialPortParameters)
441+
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
442+
* @see #AbstractXBeeDevice(Context, int)
443+
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
444+
* @see java.net.Inet6Address
445+
*/
446+
public AbstractXBeeDevice(XBeeDevice localXBeeDevice, Inet6Address ipv6Addr, String id) {
447+
if (localXBeeDevice == null)
448+
throw new NullPointerException("Local XBee device cannot be null.");
449+
if (ipv6Addr == null)
450+
throw new NullPointerException("XBee IPv6 address of the device cannot be null.");
451+
if (localXBeeDevice.isRemote())
452+
throw new IllegalArgumentException("The given local XBee device is remote.");
453+
454+
this.localXBeeDevice = localXBeeDevice;
455+
this.connectionInterface = localXBeeDevice.getConnectionInterface();
456+
this.ipv6Address = ipv6Addr;
457+
this.nodeID = id;
458+
this.logger = LoggerFactory.getLogger(this.getClass());
459+
logger.debug(toString() + "Using the connection interface {}.",
460+
connectionInterface.getClass().getSimpleName());
461+
}
462+
387463
/**
388464
* Returns the connection interface associated to this XBee device.
389465
*
@@ -515,6 +591,19 @@ public XBee64BitAddress get64BitAddress() {
515591
return xbee64BitAddress;
516592
}
517593

594+
/**
595+
* Returns the IPv6 address of this IPv6 device.
596+
*
597+
* <p>To refresh this value use the {@link #readDeviceInfo()} method.</p>
598+
*
599+
* @return The IPv6 address of this IPv6 device.
600+
*
601+
* @see java.net.Inet6Address
602+
*/
603+
public Inet6Address getIPv6Address() {
604+
return ipv6Address;
605+
}
606+
518607
/**
519608
* Returns the Operating mode (AT, API or API escaped) of this XBee device
520609
* for a local device, and the operating mode of the local device used as
@@ -1017,16 +1106,21 @@ protected ATCommandResponse sendATCommand(ATCommand command)
10171106
// Create the corresponding AT command packet depending on if the device is local or remote.
10181107
XBeePacket packet;
10191108
if (isRemote()) {
1020-
XBee16BitAddress remote16BitAddress = get16BitAddress();
1021-
if (remote16BitAddress == null)
1022-
remote16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS;
1023-
10241109
int remoteATCommandOptions = RemoteATCommandOptions.OPTION_NONE;
10251110
if (isApplyConfigurationChangesEnabled())
10261111
remoteATCommandOptions |= RemoteATCommandOptions.OPTION_APPLY_CHANGES;
10271112

1028-
packet = new RemoteATCommandPacket(getNextFrameID(), get64BitAddress(),
1029-
remote16BitAddress, remoteATCommandOptions, command.getCommand(), command.getParameter());
1113+
if (getXBeeProtocol() == XBeeProtocol.THREAD) {
1114+
packet = new IPv6RemoteATCommandRequestPacket(getNextFrameID(), ipv6Address,
1115+
remoteATCommandOptions, command.getCommand(), command.getParameter());
1116+
} else{
1117+
XBee16BitAddress remote16BitAddress = get16BitAddress();
1118+
if (remote16BitAddress == null)
1119+
remote16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS;
1120+
1121+
packet = new RemoteATCommandPacket(getNextFrameID(), get64BitAddress(),
1122+
remote16BitAddress, remoteATCommandOptions, command.getCommand(), command.getParameter());
1123+
}
10301124
} else {
10311125
if (isApplyConfigurationChangesEnabled())
10321126
packet = new ATCommandPacket(getNextFrameID(), command.getCommand(), command.getParameter());
@@ -1052,6 +1146,9 @@ protected ATCommandResponse sendATCommand(ATCommand command)
10521146
} else if (answerPacket instanceof RemoteATCommandResponsePacket) {
10531147
RemoteATCommandResponsePacket r = (RemoteATCommandResponsePacket)answerPacket;
10541148
response = new ATCommandResponse(command, r.getCommandValue(), r.getStatus());
1149+
} else if (answerPacket instanceof IPv6RemoteATCommandResponsePacket) {
1150+
IPv6RemoteATCommandResponsePacket r = (IPv6RemoteATCommandResponsePacket)answerPacket;
1151+
response = new ATCommandResponse(command, r.getCommandValue(), r.getStatus());
10551152
}
10561153

10571154
if (response != null && response.getResponse() != null)
@@ -1422,6 +1519,146 @@ else if (receivedPacket instanceof TXStatusPacket)
14221519
throw new TransmitException(status);
14231520
}
14241521

1522+
/**
1523+
* Sends the provided {@code CoAPTxRequestPacket} and determines if the
1524+
* transmission status and CoAP RX Response are success for synchronous
1525+
* transmissions.
1526+
*
1527+
* <p>If the status or the CoAP response are not successful, an
1528+
* {@code TransmitException} is thrown.</p>
1529+
*
1530+
* @param packet The {@code CoAPTxRequestPacket} to be sent.
1531+
* @param asyncTransmission Determines whether the transmission must be
1532+
* asynchronous.
1533+
*
1534+
* @return A byte array containing the RfData of the CoAP RX Response
1535+
* packet, if that field is not empty. In other cases, it will
1536+
* return {@code null}.
1537+
*
1538+
* @throws InterfaceNotOpenException if this device connection is not open.
1539+
* @throws NullPointerException if {@code packet == null}.
1540+
* @throws TransmitException if {@code packet} is not an instance of
1541+
* {@code TransmitStatusPacket} or
1542+
* if {@code packet} is not an instance of
1543+
* {@code TXStatusPacket} or
1544+
* if its transmit status is different than
1545+
* {@code XBeeTransmitStatus.SUCCESS}.
1546+
* @throws XBeeException if CoAP response packet is not received or it is
1547+
* not successful or if there is any other XBee
1548+
* related error.
1549+
*
1550+
* @see com.digi.xbee.api.packet.XBeePacket
1551+
*/
1552+
protected byte[] sendAndCheckCoAPPacket(CoAPTxRequestPacket packet,
1553+
boolean asyncTransmission) throws TransmitException, XBeeException {
1554+
// Add listener to read CoAP response (when sending a CoAP transmission,
1555+
// a transmit status and a CoAP response are received by the sender)
1556+
ArrayList<CoAPRxResponsePacket> coapResponsePackets = new ArrayList<CoAPRxResponsePacket>();
1557+
IPacketReceiveListener listener = createCoAPResponseListener(coapResponsePackets);
1558+
addPacketListener(listener);
1559+
1560+
// Send the XBee packet.
1561+
XBeePacket receivedPacket = null;
1562+
try {
1563+
if (asyncTransmission)
1564+
sendXBeePacketAsync(packet);
1565+
else
1566+
receivedPacket = sendXBeePacket(packet);
1567+
} catch (IOException e) {
1568+
throw new XBeeException("Error writing in the communication interface.", e);
1569+
}
1570+
1571+
// If the transmission is async. we are done.
1572+
if (asyncTransmission)
1573+
return null;
1574+
1575+
if (receivedPacket == null)
1576+
throw new TransmitException(null);
1577+
1578+
// Verify Transmit status.
1579+
XBeeTransmitStatus status = null;
1580+
if (receivedPacket instanceof TXStatusPacket)
1581+
status = ((TXStatusPacket)receivedPacket).getTransmitStatus();
1582+
1583+
if (status == null ||
1584+
(status != XBeeTransmitStatus.SUCCESS && status != XBeeTransmitStatus.SELF_ADDRESSED))
1585+
throw new TransmitException(status);
1586+
1587+
// Verify CoAP response (only expect one CoAP RX Response packet).
1588+
CoAPRxResponsePacket coapRxPacket = null;
1589+
try {
1590+
coapRxPacket = waitForCoAPRxResponsePacket(coapResponsePackets);
1591+
} finally {
1592+
// Always remove the packet listener from the list.
1593+
removePacketListener(listener);
1594+
}
1595+
if (coapRxPacket == null)
1596+
throw new XBeeException("CoAP response was null.");
1597+
return coapRxPacket.getRfData();
1598+
}
1599+
1600+
/**
1601+
* Returns the CoAP packet listener corresponding to the provided sent CoAP
1602+
* packet.
1603+
*
1604+
* <p>The listener will filter CoAP Rx Response packets (0x9C) and storing
1605+
* them in the provided responseList array.</p>
1606+
*
1607+
* @param coapResponsePackets List of CoAP Rx Response packets received.
1608+
*
1609+
* @return A CoAP packet receive listener.
1610+
*
1611+
* @see com.digi.xbee.api.listeners.IPacketReceiveListener
1612+
* @see com.digi.xbee.api.packet.thread.CoAPRxResponsePacket
1613+
*/
1614+
private IPacketReceiveListener createCoAPResponseListener(final ArrayList<CoAPRxResponsePacket> coapResponsePackets) {
1615+
IPacketReceiveListener listener = new IPacketReceiveListener() {
1616+
1617+
@Override
1618+
public void packetReceived(XBeePacket receivedPacket) {
1619+
if (receivedPacket instanceof CoAPRxResponsePacket) {
1620+
coapResponsePackets.add((CoAPRxResponsePacket)receivedPacket);
1621+
coapResponsePackets.notifyAll();
1622+
}
1623+
}
1624+
};
1625+
return listener;
1626+
}
1627+
1628+
/**
1629+
* Returns the CoAP Rx Response packet that comes in through the serial
1630+
* port in the given timeout.
1631+
*
1632+
* @param coapResponsePackets List of packets where the received packet will be stored.
1633+
*
1634+
* @return CoAP Rx Response packet received.
1635+
*
1636+
* @throws XBeeException if CoAP response packet is not received or it is
1637+
* not successful or if there is any other XBee
1638+
* related error.
1639+
*/
1640+
private CoAPRxResponsePacket waitForCoAPRxResponsePacket(ArrayList<CoAPRxResponsePacket> coapResponsePackets) throws XBeeException {
1641+
synchronized (coapResponsePackets) {
1642+
try {
1643+
coapResponsePackets.wait(receiveTimeout);
1644+
} catch (InterruptedException e) {}
1645+
}
1646+
1647+
if (!coapResponsePackets.isEmpty()) {
1648+
RestFulStatusEnum responseStatus = coapResponsePackets.get(0).getStatus();
1649+
if (responseStatus != RestFulStatusEnum.SUCCESS
1650+
&& responseStatus != RestFulStatusEnum.CREATED
1651+
&& responseStatus != RestFulStatusEnum.ACCEPTED
1652+
&& responseStatus != RestFulStatusEnum.NON_AUTHORITATIVE
1653+
&& responseStatus != RestFulStatusEnum.NO_CONTENT
1654+
&& responseStatus != RestFulStatusEnum.RESET_CONTENT)
1655+
throw new XBeeException("CoAP response had an unexpected status: " + responseStatus.toString());
1656+
} else {
1657+
throw new XBeeException("CoAP response was not received.");
1658+
}
1659+
return coapResponsePackets.get(0);
1660+
}
1661+
14251662
/**
14261663
* Sets the configuration of the given IO line of this XBee device.
14271664
*
@@ -1775,6 +2012,46 @@ public XBee64BitAddress getDestinationAddress() throws TimeoutException, XBeeExc
17752012
return new XBee64BitAddress(address);
17762013
}
17772014

2015+
/**
2016+
* Sets the destination IPv6 address.
2017+
*
2018+
* @param ipv6Address Destination IPv6 address.
2019+
*
2020+
* @throws NullPointerException if {@code address == null}.
2021+
* @throws TimeoutException if there is a timeout setting the destination
2022+
* address.
2023+
* @throws XBeeException if there is any other XBee related exception.
2024+
*
2025+
* @see #getDestinationIPAddress()
2026+
* @see java.net.Inet6Address
2027+
*/
2028+
public void setIPv6DestinationAddress(Inet6Address ipv6Address) throws TimeoutException, XBeeException {
2029+
if (ipv6Address == null)
2030+
throw new NullPointerException("Destination IPv6 address cannot be null.");
2031+
2032+
setParameter("DL", ipv6Address.getAddress());
2033+
}
2034+
2035+
/**
2036+
* Returns the destination IPv6 address.
2037+
*
2038+
* @return The configured destination IPv6 address.
2039+
*
2040+
* @throws TimeoutException if there is a timeout reading the destination
2041+
* address.
2042+
* @throws XBeeException if there is any other XBee related exception.
2043+
*
2044+
* @see #setDestinationIPAddress(Inet6Address)
2045+
* @see java.net.Inet6Address
2046+
*/
2047+
public Inet6Address getIPv6DestinationAddress() throws TimeoutException, XBeeException {
2048+
try {
2049+
return (Inet6Address) Inet6Address.getByAddress(getParameter("DL"));
2050+
} catch (UnknownHostException e) {
2051+
throw new XBeeException(e);
2052+
}
2053+
}
2054+
17782055
/**
17792056
* Sets the IO sampling rate to enable periodic sampling in this XBee
17802057
* device.

0 commit comments

Comments
 (0)