
com.digi.xbee.api.AbstractXBeeDevice Maven / Gradle / Ivy
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.digi.xbee.api;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import android.content.Context;
import com.digi.xbee.api.connection.IConnectionInterface;
import com.digi.xbee.api.connection.DataReader;
import com.digi.xbee.api.connection.android.AndroidUSBPermissionListener;
import com.digi.xbee.api.connection.serial.SerialPortParameters;
import com.digi.xbee.api.exceptions.ATCommandException;
import com.digi.xbee.api.exceptions.InterfaceNotOpenException;
import com.digi.xbee.api.exceptions.InvalidOperatingModeException;
import com.digi.xbee.api.exceptions.OperationNotSupportedException;
import com.digi.xbee.api.exceptions.TimeoutException;
import com.digi.xbee.api.exceptions.TransmitException;
import com.digi.xbee.api.exceptions.XBeeException;
import com.digi.xbee.api.io.IOLine;
import com.digi.xbee.api.io.IOMode;
import com.digi.xbee.api.io.IOSample;
import com.digi.xbee.api.io.IOValue;
import com.digi.xbee.api.listeners.IExplicitDataReceiveListener;
import com.digi.xbee.api.listeners.IIOSampleReceiveListener;
import com.digi.xbee.api.listeners.IModemStatusReceiveListener;
import com.digi.xbee.api.listeners.IIPDataReceiveListener;
import com.digi.xbee.api.listeners.IPacketReceiveListener;
import com.digi.xbee.api.listeners.IDataReceiveListener;
import com.digi.xbee.api.listeners.ISMSReceiveListener;
import com.digi.xbee.api.models.ATCommand;
import com.digi.xbee.api.models.ATCommandResponse;
import com.digi.xbee.api.models.ATCommandStatus;
import com.digi.xbee.api.models.AssociationIndicationStatus;
import com.digi.xbee.api.models.HardwareVersion;
import com.digi.xbee.api.models.PowerLevel;
import com.digi.xbee.api.models.RemoteATCommandOptions;
import com.digi.xbee.api.models.XBee16BitAddress;
import com.digi.xbee.api.models.XBee64BitAddress;
import com.digi.xbee.api.models.OperatingMode;
import com.digi.xbee.api.models.XBeeProtocol;
import com.digi.xbee.api.models.XBeeTransmitStatus;
import com.digi.xbee.api.packet.XBeeAPIPacket;
import com.digi.xbee.api.packet.APIFrameType;
import com.digi.xbee.api.packet.XBeePacket;
import com.digi.xbee.api.packet.common.ATCommandPacket;
import com.digi.xbee.api.packet.common.ATCommandQueuePacket;
import com.digi.xbee.api.packet.common.ATCommandResponsePacket;
import com.digi.xbee.api.packet.common.IODataSampleRxIndicatorPacket;
import com.digi.xbee.api.packet.common.RemoteATCommandPacket;
import com.digi.xbee.api.packet.common.RemoteATCommandResponsePacket;
import com.digi.xbee.api.packet.common.TransmitStatusPacket;
import com.digi.xbee.api.packet.raw.RX16IOPacket;
import com.digi.xbee.api.packet.raw.RX64IOPacket;
import com.digi.xbee.api.packet.raw.TXStatusPacket;
import com.digi.xbee.api.utils.ByteUtils;
import com.digi.xbee.api.utils.HexUtils;
/**
* This class provides common functionality for all XBee devices.
*
* @see XBeeDevice
* @see RemoteXBeeDevice
*/
public abstract class AbstractXBeeDevice {
// Constants.
/**
* Default receive timeout used to wait for a response in synchronous
* operations: {@value} ms.
*
* @see XBeeDevice#getReceiveTimeout()
* @see XBeeDevice#setReceiveTimeout(int)
*/
protected final static int DEFAULT_RECEIVE_TIMETOUT = 2000; // 2.0 seconds of timeout to receive packet and command responses.
/**
* Timeout to wait before entering in command mode: {@value} ms.
*
* It is used to determine the operating mode of the module (this
* library only supports API modes, not transparent mode).
*
* This value depends on the {@code GT}, {@code AT} and/or {@code BT}
* parameters.
*
* @see XBeeDevice#determineOperatingMode()
*/
protected final static int TIMEOUT_BEFORE_COMMAND_MODE = 1200;
/**
* Timeout to wait after entering in command mode: {@value} ms.
*
* It is used to determine the operating mode of the module (this
* library only supports API modes, not transparent mode).
*
* This value depends on the {@code GT}, {@code AT} and/or {@code BT}
* parameters.
*
* @see XBeeDevice#determineOperatingMode()
*/
protected final static int TIMEOUT_ENTER_COMMAND_MODE = 1500;
// Variables.
protected IConnectionInterface connectionInterface;
protected DataReader dataReader = null;
protected XBeeProtocol xbeeProtocol = XBeeProtocol.UNKNOWN;
protected OperatingMode operatingMode = OperatingMode.UNKNOWN;
protected XBee16BitAddress xbee16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS;
protected XBee64BitAddress xbee64BitAddress = XBee64BitAddress.UNKNOWN_ADDRESS;
protected int currentFrameID = 0xFF;
protected int receiveTimeout = DEFAULT_RECEIVE_TIMETOUT;
protected AbstractXBeeDevice localXBeeDevice;
protected Logger logger;
private String nodeID;
private String firmwareVersion;
private HardwareVersion hardwareVersion;
private Object ioLock = new Object();
private boolean ioPacketReceived = false;
private boolean applyConfigurationChanges = true;
private byte[] ioPacketPayload;
/**
* Class constructor. Instantiates a new {@code XBeeDevice} object in the
* given port name and baud rate.
*
* @param port Serial port name where XBee device is attached to.
* @param baudRate Serial port baud rate to communicate with the device.
* Other connection parameters will be set as default (8
* data bits, 1 stop bit, no parity, no flow control).
*
* @throws IllegalArgumentException if {@code baudRate < 0}.
* @throws NullPointerException if {@code port == null}.
*
* @see #AbstractXBeeDevice(IConnectionInterface)
* @see #AbstractXBeeDevice(String, SerialPortParameters)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
* @see #AbstractXBeeDevice(Context, int)
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
*/
public AbstractXBeeDevice(String port, int baudRate) {
this(XBee.createConnectiontionInterface(port, baudRate));
}
/**
* Class constructor. Instantiates a new {@code XBeeDevice} object in the
* given serial port name and settings.
*
* @param port Serial port name where XBee device is attached to.
* @param baudRate Serial port baud rate to communicate with the device.
* @param dataBits Serial port data bits.
* @param stopBits Serial port data bits.
* @param parity Serial port data bits.
* @param flowControl Serial port data bits.
*
* @throws IllegalArgumentException if {@code baudRate < 0} or
* if {@code dataBits < 0} or
* if {@code stopBits < 0} or
* if {@code parity < 0} or
* if {@code flowControl < 0}.
* @throws NullPointerException if {@code port == null}.
*
* @see #AbstractXBeeDevice(IConnectionInterface)
* @see #AbstractXBeeDevice(String, int)
* @see #AbstractXBeeDevice(String, SerialPortParameters)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
* @see #AbstractXBeeDevice(Context, int)
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
*/
public AbstractXBeeDevice(String port, int baudRate, int dataBits, int stopBits, int parity, int flowControl) {
this(port, new SerialPortParameters(baudRate, dataBits, stopBits, parity, flowControl));
}
/**
* Class constructor. Instantiates a new {@code XBeeDevice} object in the
* given serial port name and parameters.
*
* @param port Serial port name where XBee device is attached to.
* @param serialPortParameters Object containing the serial port parameters.
*
* @throws NullPointerException if {@code port == null} or
* if {@code serialPortParameters == null}.
*
* @see #AbstractXBeeDevice(IConnectionInterface)
* @see #AbstractXBeeDevice(String, int)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
* @see #AbstractXBeeDevice(Context, int)
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
* @see com.digi.xbee.api.connection.serial.SerialPortParameters
*/
public AbstractXBeeDevice(String port, SerialPortParameters serialPortParameters) {
this(XBee.createConnectiontionInterface(port, serialPortParameters));
}
/**
* Class constructor. Instantiates a new {@code XBeeDevice} object for
* Android with the given parameters.
*
* @param context The Android context.
* @param baudRate The USB connection baud rate.
*
* @throws IllegalArgumentException if {@code baudRate < 1}.
* @throws NullPointerException if {@code context == null}.
*
* @see #AbstractXBeeDevice(IConnectionInterface)
* @see #AbstractXBeeDevice(String, int)
* @see #AbstractXBeeDevice(String, SerialPortParameters)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
*
* @since 1.2.0
*/
public AbstractXBeeDevice(Context context, int baudRate) {
this(XBee.createConnectiontionInterface(context, baudRate));
}
/**
* Class constructor. Instantiates a new {@code XBeeDevice} object for
* Android with the given parameters.
*
* @param context The Android context.
* @param baudRate The USB connection baud rate.
* @param permissionListener The USB permission listener that will be
* notified when user grants USB permissions.
*
* @throws IllegalArgumentException if {@code baudRate < 1}.
* @throws NullPointerException if {@code context == null}.
*
* @see #AbstractXBeeDevice(IConnectionInterface)
* @see #AbstractXBeeDevice(String, int)
* @see #AbstractXBeeDevice(String, SerialPortParameters)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
* @see #AbstractXBeeDevice(Context, int)
* @see com.digi.xbee.api.connection.android.AndroidUSBPermissionListener
*
* @since 1.2.0
*/
public AbstractXBeeDevice(Context context, int baudRate, AndroidUSBPermissionListener permissionListener) {
this(XBee.createConnectiontionInterface(context, baudRate, permissionListener));
}
/**
* Class constructor. Instantiates a new {@code XBeeDevice} object with the
* given connection interface.
*
* @param connectionInterface The connection interface with the physical
* XBee device.
*
* @throws NullPointerException if {@code connectionInterface == null}.
*
* @see #AbstractXBeeDevice(String, int)
* @see #AbstractXBeeDevice(String, SerialPortParameters)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
* @see #AbstractXBeeDevice(Context, int)
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
* @see com.digi.xbee.api.connection.IConnectionInterface
*/
public AbstractXBeeDevice(IConnectionInterface connectionInterface) {
if (connectionInterface == null)
throw new NullPointerException("ConnectionInterface cannot be null.");
this.connectionInterface = connectionInterface;
this.logger = LoggerFactory.getLogger(this.getClass());
logger.debug(toString() + "Using the connection interface {}.",
connectionInterface.getClass().getSimpleName());
}
/**
* Class constructor. Instantiates a new {@code RemoteXBeeDevice} object
* with the given local {@code XBeeDevice} which contains the connection
* interface to be used.
*
* @param localXBeeDevice The local XBee device that will behave as
* connection interface to communicate with this
* remote XBee device.
* @param addr64 The 64-bit address to identify this XBee device.
*
* @throws IllegalArgumentException If {@code localXBeeDevice.isRemote() == true}.
* @throws NullPointerException if {@code localXBeeDevice == null} or
* if {@code addr64 == null}.
*
* @see #AbstractXBeeDevice(IConnectionInterface)
* @see #AbstractXBeeDevice(String, int)
* @see #AbstractXBeeDevice(String, SerialPortParameters)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
* @see #AbstractXBeeDevice(Context, int)
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
* @see com.digi.xbee.api.models.XBee16BitAddress
*/
public AbstractXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64) {
this(localXBeeDevice, addr64, null, null);
}
/**
* Class constructor. Instantiates a new {@code RemoteXBeeDevice} object
* with the given local {@code XBeeDevice} which contains the connection
* interface to be used.
*
* @param localXBeeDevice The local XBee device that will behave as
* connection interface to communicate with this
* remote XBee device.
* @param addr64 The 64-bit address to identify this XBee device.
* @param addr16 The 16-bit address to identify this XBee device. It might
* be {@code null}.
* @param id The node identifier of this XBee device. It might be
* {@code null}.
*
* @throws IllegalArgumentException If {@code localXBeeDevice.isRemote() == true}.
* @throws NullPointerException If {@code localXBeeDevice == null} or
* if {@code addr64 == null}.
*
* @see #AbstractXBeeDevice(IConnectionInterface)
* @see #AbstractXBeeDevice(String, int)
* @see #AbstractXBeeDevice(String, SerialPortParameters)
* @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
* @see #AbstractXBeeDevice(String, int, int, int, int, int)
* @see #AbstractXBeeDevice(Context, int)
* @see #AbstractXBeeDevice(Context, int, AndroidUSBPermissionListener)
* @see com.digi.xbee.api.models.XBee16BitAddress
* @see com.digi.xbee.api.models.XBee64BitAddress
*/
public AbstractXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64,
XBee16BitAddress addr16, String id) {
if (localXBeeDevice == null)
throw new NullPointerException("Local XBee device cannot be null.");
if (addr64 == null)
throw new NullPointerException("XBee 64-bit address of the device cannot be null.");
if (localXBeeDevice.isRemote())
throw new IllegalArgumentException("The given local XBee device is remote.");
this.localXBeeDevice = localXBeeDevice;
this.connectionInterface = localXBeeDevice.getConnectionInterface();
this.xbee64BitAddress = addr64;
this.xbee16BitAddress = addr16;
if (addr16 == null)
xbee16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS;
this.nodeID = id;
this.logger = LoggerFactory.getLogger(this.getClass());
logger.debug(toString() + "Using the connection interface {}.",
connectionInterface.getClass().getSimpleName());
}
/**
* Returns the connection interface associated to this XBee device.
*
* @return XBee device's connection interface.
*
* @see com.digi.xbee.api.connection.IConnectionInterface
*/
public IConnectionInterface getConnectionInterface() {
return connectionInterface;
}
/**
* Returns whether this XBee device is a remote device.
*
* @return {@code true} if this XBee device is a remote device,
* {@code false} otherwise.
*/
abstract public boolean isRemote();
/**
* Reads some parameters from this device and obtains its protocol.
*
* This method refresh the values of:
*
* - 64-bit address only if it is not initialized.
* - Node Identifier.
* - Hardware version if it is not initialized.
* - Firmware version.
* - XBee device protocol.
* - 16-bit address (not for DigiMesh/DigiPoint modules).
*
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout reading the parameters.
* @throws XBeeException if there is any error trying to read the device information or
* if there is any other XBee related exception.
*
* @see #get16BitAddress()
* @see #get64BitAddress()
* @see #getHardwareVersion()
* @see #getNodeID()
* @see #getFirmwareVersion()
* @see #getXBeeProtocol()
* @see #setNodeID(String)
*/
public void readDeviceInfo() throws TimeoutException, XBeeException {
byte[] response = null;
// Get the 64-bit address.
if (xbee64BitAddress == null || xbee64BitAddress == XBee64BitAddress.UNKNOWN_ADDRESS) {
String addressHigh;
String addressLow;
response = getParameter("SH");
addressHigh = HexUtils.byteArrayToHexString(response);
response = getParameter("SL");
addressLow = HexUtils.byteArrayToHexString(response);
while(addressLow.length() < 8)
addressLow = "0" + addressLow;
xbee64BitAddress = new XBee64BitAddress(addressHigh + addressLow);
}
// Get the Node ID.
response = getParameter("NI");
nodeID = new String(response);
// Get the hardware version.
if (hardwareVersion == null) {
response = getParameter("HV");
hardwareVersion = HardwareVersion.get(response[0]);
}
// Get the firmware version.
response = getParameter("VR");
firmwareVersion = HexUtils.byteArrayToHexString(response);
// Original value of the protocol.
XBeeProtocol origProtocol = getXBeeProtocol();
// Obtain the device protocol.
xbeeProtocol = XBeeProtocol.determineProtocol(hardwareVersion, firmwareVersion);
if (origProtocol != XBeeProtocol.UNKNOWN
&& origProtocol != xbeeProtocol) {
throw new XBeeException("Error reading device information: "
+ "Your module seems to be " + xbeeProtocol
+ " and NOT " + origProtocol + ". Check if you are using"
+ " the appropriate device class.");
}
// Get the 16-bit address. This must be done after obtaining the protocol because
// DigiMesh and Point-to-Multipoint protocols don't have 16-bit addresses.
XBeeProtocol protocol = getXBeeProtocol();
if (protocol != XBeeProtocol.DIGI_MESH
&& protocol != XBeeProtocol.DIGI_POINT
&& protocol != XBeeProtocol.XBEE_WIFI
&& protocol != XBeeProtocol.CELLULAR
&& protocol != XBeeProtocol.UNKNOWN) {
response = getParameter("MY");
xbee16BitAddress = new XBee16BitAddress(response);
}
}
/**
* Returns the 16-bit address of this XBee device.
*
* To refresh this value use the {@link #readDeviceInfo()} method.
*
* @return The 16-bit address of this XBee device.
*
* @see com.digi.xbee.api.models.XBee16BitAddress
*/
public XBee16BitAddress get16BitAddress() {
return xbee16BitAddress;
}
/**
* Returns the 64-bit address of this XBee device.
*
* If this value is {@code null} or
* {@code XBee64BitAddress.UNKNOWN_ADDRESS}, use the
* {@link #readDeviceInfo()} method to get its value.
*
* @return The 64-bit address of this XBee device.
*
* @see com.digi.xbee.api.models.XBee64BitAddress
*/
public XBee64BitAddress get64BitAddress() {
return xbee64BitAddress;
}
/**
* Returns the Operating mode (AT, API or API escaped) of this XBee device
* for a local device, and the operating mode of the local device used as
* communication interface for a remote device.
*
* @return The operating mode of the local XBee device.
*
* @see #isRemote()
* @see com.digi.xbee.api.models.OperatingMode
*/
protected OperatingMode getOperatingMode() {
if (isRemote())
return localXBeeDevice.getOperatingMode();
return operatingMode;
}
/**
* Returns the XBee Protocol of this XBee device.
*
* To refresh this value use the {@link #readDeviceInfo()} method.
*
* @return The XBee device protocol.
*
* @see com.digi.xbee.api.models.XBeeProtocol
*/
public XBeeProtocol getXBeeProtocol() {
return xbeeProtocol;
}
/**
* Returns the node identifier of this XBee device.
*
* To refresh this value use the {@link #readDeviceInfo()} method.
*
* @return The node identifier of this device.
*
* @see #setNodeID(String)
*/
public String getNodeID() {
return nodeID;
}
/**
* Sets the node identifier of this XBee device.
*
* @param nodeID The new node id of the device.
*
* @throws IllegalArgumentException if {@code nodeID.length > 20}.
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code nodeID == null}.
* @throws TimeoutException if there is a timeout setting the node ID value.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getNodeID()
*/
public void setNodeID(String nodeID) throws TimeoutException, XBeeException {
if (nodeID == null)
throw new NullPointerException("Node ID cannot be null.");
if (nodeID.length() > 20)
throw new IllegalArgumentException("Node ID length must be less than 21.");
setParameter("NI", nodeID.getBytes());
this.nodeID = nodeID;
}
/**
* Returns the firmware version (hexadecimal string value) of this XBee
* device.
*
* To refresh this value use the {@link #readDeviceInfo()} method.
*
* @return The firmware version of the XBee device.
*/
public String getFirmwareVersion() {
return firmwareVersion;
}
/**
* Returns the hardware version of this XBee device.
*
* If this value is {@code null}, use the {@link #readDeviceInfo()}
* method to get its value.
*
* @return The hardware version of the XBee device.
*
* @see com.digi.xbee.api.models.HardwareVersion
* @see com.digi.xbee.api.models.HardwareVersionEnum
*/
public HardwareVersion getHardwareVersion() {
return hardwareVersion;
}
/**
* Updates the current device reference with the data provided for the
* given device.
*
* This is only for internal use.
*
* @param device The XBee Device to get the data from.
*/
public void updateDeviceDataFrom(AbstractXBeeDevice device) {
// TODO Should the devices have the same protocol??
// TODO Should be allow to update a local from a remote or viceversa?? Maybe
// this must be in the Local/Remote device class(es) and not here...
// Only update the Node Identifier if the provided is not null.
if (device.getNodeID() != null)
this.nodeID = device.getNodeID();
// Only update the 64-bit address if the original is null or unknown.
XBee64BitAddress addr64 = device.get64BitAddress();
if (addr64 != null && addr64 != XBee64BitAddress.UNKNOWN_ADDRESS
&& !addr64.equals(xbee64BitAddress)
&& (xbee64BitAddress == null
|| xbee64BitAddress.equals(XBee64BitAddress.UNKNOWN_ADDRESS))) {
xbee64BitAddress = addr64;
}
// TODO Change here the 16-bit address or maybe in ZigBee and 802.15.4?
// TODO Should the 16-bit address be always updated? Or following the same rule as the 64-bit address.
XBee16BitAddress addr16 = device.get16BitAddress();
if (addr16 != null && !addr16.equals(xbee16BitAddress)) {
xbee16BitAddress = addr16;
}
//this.deviceType = device.deviceType; // This is not yet done.
// The operating mode: only API/API2. Do we need this for a remote device?
// The protocol of the device should be the same.
// The hardware version should be the same.
// The firmware version can change...
}
/**
* Adds the provided listener to the list of listeners to be notified
* when new packets are received.
*
* If the listener has been already included, this method does nothing.
*
*
* @param listener Listener to be notified when new packets are received.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #removePacketListener(IPacketReceiveListener)
* @see com.digi.xbee.api.listeners.IPacketReceiveListener
*/
protected void addPacketListener(IPacketReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.addPacketReceiveListener(listener);
}
/**
* Removes the provided listener from the list of packets listeners.
*
* If the listener was not in the list this method does nothing.
*
* @param listener Listener to be removed from the list of listeners.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #addPacketListener(IPacketReceiveListener)
* @see com.digi.xbee.api.listeners.IPacketReceiveListener
*/
protected void removePacketListener(IPacketReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.removePacketReceiveListener(listener);
}
/**
* Adds the provided listener to the list of listeners to be notified
* when new data is received.
*
* If the listener has been already included this method does nothing.
*
*
* @param listener Listener to be notified when new data is received.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #removeDataListener(IDataReceiveListener)
* @see com.digi.xbee.api.listeners.IDataReceiveListener
*/
protected void addDataListener(IDataReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.addDataReceiveListener(listener);
}
/**
* Removes the provided listener from the list of data listeners.
*
* If the listener was not in the list this method does nothing.
*
* @param listener Listener to be removed from the list of listeners.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #addDataListener(IDataReceiveListener)
* @see com.digi.xbee.api.listeners.IDataReceiveListener
*/
protected void removeDataListener(IDataReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.removeDataReceiveListener(listener);
}
/**
* Adds the provided listener to the list of listeners to be notified
* when new IO samples are received.
*
* If the listener has been already included this method does nothing.
*
*
* @param listener Listener to be notified when new IO samples are received.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #removeIOSampleListener(IIOSampleReceiveListener)
* @see com.digi.xbee.api.listeners.IIOSampleReceiveListener
*/
protected void addIOSampleListener(IIOSampleReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.addIOSampleReceiveListener(listener);
}
/**
* Removes the provided listener from the list of IO samples listeners.
*
* If the listener was not in the list this method does nothing.
*
* @param listener Listener to be removed from the list of listeners.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #addIOSampleListener(IIOSampleReceiveListener)
* @see com.digi.xbee.api.listeners.IIOSampleReceiveListener
*/
protected void removeIOSampleListener(IIOSampleReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.removeIOSampleReceiveListener(listener);
}
/**
* Adds the provided listener to the list of listeners to be notified
* when new Modem Status events are received.
*
* If the listener has been already included this method does nothing.
*
*
* @param listener Listener to be notified when new Modem Status events are
* received.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #removeModemStatusListener(IModemStatusReceiveListener)
* @see com.digi.xbee.api.listeners.IModemStatusReceiveListener
*/
protected void addModemStatusListener(IModemStatusReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.addModemStatusReceiveListener(listener);
}
/**
* Removes the provided listener from the list of Modem Status listeners.
*
* If the listener was not in the list this method does nothing.
*
* @param listener Listener to be removed from the list of listeners.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #addModemStatusListener(IModemStatusReceiveListener)
* @see com.digi.xbee.api.listeners.IModemStatusReceiveListener
*/
protected void removeModemStatusListener(IModemStatusReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.removeModemStatusReceiveListener(listener);
}
/**
* Adds the provided listener to the list of listeners to be notified
* when new explicit data packets are received.
*
* If the listener has been already included this method does nothing.
*
*
* @param listener Listener to be notified when new explicit data packets
* are received.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #removeExplicitDataListener(IExplicitDataReceiveListener)
* @see com.digi.xbee.api.listeners.IExplicitDataReceiveListener
*/
protected void addExplicitDataListener(IExplicitDataReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.addExplicitDataReceiveListener(listener);
}
/**
* Removes the provided listener from the list of explicit data receive
* listeners.
*
* If the listener was not in the list this method does nothing.
*
* @param listener Listener to be removed from the list of listeners.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #addExplicitDataListener(IExplicitDataReceiveListener)
* @see com.digi.xbee.api.listeners.IExplicitDataReceiveListener
*/
protected void removeExplicitDataListener(IExplicitDataReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.removeExplicitDataReceiveListener(listener);
}
/**
* Adds the provided listener to the list of listeners to be notified
* when new IP data is received.
*
* If the listener has been already included this method does nothing.
*
*
* @param listener Listener to be notified when new IP data is
* received.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #removeIPDataListener(IIPDataReceiveListener)
* @see com.digi.xbee.api.listeners.IIPDataReceiveListener
*
* @since 1.2.0
*/
protected void addIPDataListener(IIPDataReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.addIPDataReceiveListener(listener);
}
/**
* Removes the provided listener from the list of IP data listeners.
*
* If the listener was not in the list this method does nothing.
*
* @param listener Listener to be removed from the list of listeners.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #addIPDataListener(IIPDataReceiveListener)
* @see com.digi.xbee.api.listeners.IIPDataReceiveListener
*
* @since 1.2.0
*/
protected void removeIPDataListener(IIPDataReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.removeIPDataReceiveListener(listener);
}
/**
* Adds the provided listener to the list of listeners to be notified
* when new SMS is received.
*
* If the listener has been already included this method does nothing.
*
*
* @param listener Listener to be notified when new SMS is received.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #removeSMSListener(ISMSReceiveListener)
* @see com.digi.xbee.api.listeners.ISMSReceiveListener
*
* @since 1.2.0
*/
protected void addSMSListener(ISMSReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.addSMSReceiveListener(listener);
}
/**
* Removes the provided listener from the list of SMS listeners.
*
* If the listener was not in the list this method does nothing.
*
* @param listener Listener to be removed from the list of listeners.
*
* @throws NullPointerException if {@code listener == null}
*
* @see #addSMSListener(ISMSReceiveListener)
* @see com.digi.xbee.api.listeners.ISMSReceiveListener
*
* @since 1.2.0
*/
protected void removeSMSListener(ISMSReceiveListener listener) {
if (listener == null)
throw new NullPointerException("Listener cannot be null.");
if (dataReader == null)
return;
dataReader.removeSMSReceiveListener(listener);
}
/**
* Sends the given AT command and waits for answer or until the configured
* receive timeout expires.
*
* The receive timeout is configured using the {@code setReceiveTimeout}
* method and can be consulted with {@code getReceiveTimeout} method.
*
* @param command AT command to be sent.
* @return An {@code ATCommandResponse} object containing the response of
* the command or {@code null} if there is no response.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws InvalidOperatingModeException if the operating mode is different
* than {@link OperatingMode#API} and
* {@link OperatingMode#API_ESCAPE}.
* @throws IOException if an I/O error occurs while sending the AT command.
* @throws NullPointerException if {@code command == null}.
* @throws TimeoutException if the configured time expires while waiting
* for the command reply.
*
* @see XBeeDevice#getReceiveTimeout()
* @see XBeeDevice#setReceiveTimeout(int)
* @see com.digi.xbee.api.models.ATCommand
* @see com.digi.xbee.api.models.ATCommandResponse
*/
protected ATCommandResponse sendATCommand(ATCommand command)
throws InvalidOperatingModeException, TimeoutException, IOException {
// Check if command is null.
if (command == null)
throw new NullPointerException("AT command cannot be null.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
ATCommandResponse response = null;
OperatingMode operatingMode = getOperatingMode();
switch (operatingMode) {
case AT:
case UNKNOWN:
default:
throw new InvalidOperatingModeException(operatingMode);
case API:
case API_ESCAPE:
// Create the corresponding AT command packet depending on if the device is local or remote.
XBeePacket packet;
if (isRemote()) {
XBee16BitAddress remote16BitAddress = get16BitAddress();
if (remote16BitAddress == null)
remote16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS;
int remoteATCommandOptions = RemoteATCommandOptions.OPTION_NONE;
if (isApplyConfigurationChangesEnabled())
remoteATCommandOptions |= RemoteATCommandOptions.OPTION_APPLY_CHANGES;
packet = new RemoteATCommandPacket(getNextFrameID(), get64BitAddress(),
remote16BitAddress, remoteATCommandOptions, command.getCommand(), command.getParameter());
} else {
if (isApplyConfigurationChangesEnabled())
packet = new ATCommandPacket(getNextFrameID(), command.getCommand(), command.getParameter());
else
packet = new ATCommandQueuePacket(getNextFrameID(), command.getCommand(), command.getParameter());
}
if (command.getParameter() == null)
logger.debug(toString() + "Sending AT command '{}'.", command.getCommand());
else
logger.debug(toString() + "Sending AT command '{} {}'.", command.getCommand(),
HexUtils.prettyHexString(command.getParameter()));
try {
// Send the packet and build the corresponding response depending on if the device is local or remote.
XBeePacket answerPacket;
if (isRemote())
answerPacket = localXBeeDevice.sendXBeePacket(packet);
else
answerPacket = sendXBeePacket(packet);
if (answerPacket instanceof ATCommandResponsePacket) {
ATCommandResponsePacket r = (ATCommandResponsePacket)answerPacket;
response = new ATCommandResponse(command, r.getCommandValue(), r.getStatus());
} else if (answerPacket instanceof RemoteATCommandResponsePacket) {
RemoteATCommandResponsePacket r = (RemoteATCommandResponsePacket)answerPacket;
response = new ATCommandResponse(command, r.getCommandValue(), r.getStatus());
}
if (response != null && response.getResponse() != null)
logger.debug(toString() + "AT command response: {}.", HexUtils.prettyHexString(response.getResponse()));
else
logger.debug(toString() + "AT command response: null.");
} catch (ClassCastException e) {
logger.error("Received an invalid packet type after sending an AT command packet." + e);
}
}
return response;
}
/**
* Sends the given XBee packet asynchronously.
*
* The method will not wait for an answer for the packet.
*
* To be notified when the answer is received, use
* {@link #sendXBeePacket(XBeePacket, IPacketReceiveListener)}.
*
* @param packet XBee packet to be sent asynchronously.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws InvalidOperatingModeException if the operating mode is different
* than {@link OperatingMode#API} and
* {@link OperatingMode#API_ESCAPE}.
* @throws IOException if an I/O error occurs while sending the XBee packet.
* @throws NullPointerException if {@code packet == null}.
*
* @see #sendXBeePacket(XBeePacket)
* @see #sendXBeePacket(XBeePacket, IPacketReceiveListener)
* @see #sendXBeePacketAsync(XBeePacket)
* @see com.digi.xbee.api.packet.XBeePacket
*/
protected void sendXBeePacketAsync(XBeePacket packet)
throws InvalidOperatingModeException, IOException {
sendXBeePacket(packet, null);
}
/**
* Sends the given XBee packet asynchronously and registers the given
* packet listener (if not {@code null}) to wait for an answer.
*
* The method will not wait for an answer for the packet, but the given
* listener will be notified when the answer arrives.
*
* @param packet XBee packet to be sent.
* @param packetReceiveListener Listener for the operation, {@code null}
* not to be notified when the answer arrives.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws InvalidOperatingModeException if the operating mode is different
* than {@link OperatingMode#API} and
* {@link OperatingMode#API_ESCAPE}.
* @throws IOException if an I/O error occurs while sending the XBee packet.
* @throws NullPointerException if {@code packet == null}.
*
* @see #sendXBeePacket(XBeePacket)
* @see #sendXBeePacket(XBeePacket, IPacketReceiveListener)
* @see #sendXBeePacketAsync(XBeePacket)
* @see com.digi.xbee.api.listeners.IPacketReceiveListener
* @see com.digi.xbee.api.packet.XBeePacket
*/
protected void sendXBeePacket(XBeePacket packet, IPacketReceiveListener packetReceiveListener)
throws InvalidOperatingModeException, IOException {
// Check if the packet to send is null.
if (packet == null)
throw new NullPointerException("XBee packet cannot be null.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
OperatingMode operatingMode = getOperatingMode();
switch (operatingMode) {
case AT:
case UNKNOWN:
default:
throw new InvalidOperatingModeException(operatingMode);
case API:
case API_ESCAPE:
// Add the required frame ID and subscribe listener if given.
if (packet instanceof XBeeAPIPacket) {
insertFrameID(packet);
XBeeAPIPacket apiPacket = (XBeeAPIPacket)packet;
if (packetReceiveListener != null
&& apiPacket.needsAPIFrameID())
dataReader.addPacketReceiveListener(packetReceiveListener, apiPacket.getFrameID());
else if (packetReceiveListener != null)
dataReader.addPacketReceiveListener(packetReceiveListener);
}
// Write packet data.
writePacket(packet);
break;
}
}
/**
* Sends the given XBee packet synchronously and blocks until response is
* received or receive timeout is reached.
*
* The receive timeout is configured using the {@code setReceiveTimeout}
* method and can be consulted with {@code getReceiveTimeout} method.
*
* Use {@link #sendXBeePacketAsync(XBeePacket)} for non-blocking
* operations.
*
* @param packet XBee packet to be sent.
* @return An {@code XBeePacket} containing the response of the sent packet
* or {@code null} if there is no response.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws InvalidOperatingModeException if the operating mode is different
* than {@link OperatingMode#API} and
* {@link OperatingMode#API_ESCAPE}.
* @throws IOException if an I/O error occurs while sending the XBee packet.
* @throws NullPointerException if {@code packet == null}.
* @throws TimeoutException if the configured time expires while waiting for
* the packet reply.
*
* @see #sendXBeePacket(XBeePacket)
* @see #sendXBeePacket(XBeePacket, IPacketReceiveListener)
* @see #sendXBeePacketAsync(XBeePacket)
* @see XBeeDevice#setReceiveTimeout(int)
* @see XBeeDevice#getReceiveTimeout()
* @see com.digi.xbee.api.packet.XBeePacket
*/
protected XBeePacket sendXBeePacket(final XBeePacket packet)
throws InvalidOperatingModeException, TimeoutException, IOException {
// Check if the packet to send is null.
if (packet == null)
throw new NullPointerException("XBee packet cannot be null.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
OperatingMode operatingMode = getOperatingMode();
switch (operatingMode) {
case AT:
case UNKNOWN:
default:
throw new InvalidOperatingModeException(operatingMode);
case API:
case API_ESCAPE:
// Build response container.
ArrayList responseList = new ArrayList();
// If the packet does not need frame ID, send it async. and return null.
if (packet instanceof XBeeAPIPacket) {
if (!((XBeeAPIPacket)packet).needsAPIFrameID()) {
sendXBeePacketAsync(packet);
return null;
}
} else {
sendXBeePacketAsync(packet);
return null;
}
// Add the required frame ID to the packet if necessary.
insertFrameID(packet);
// Generate a packet received listener for the packet to be sent.
IPacketReceiveListener packetReceiveListener = createPacketReceivedListener(packet, responseList);
// Add the packet listener to the data reader.
addPacketListener(packetReceiveListener);
// Write the packet data.
writePacket(packet);
try {
// Wait for response or timeout.
synchronized (responseList) {
try {
responseList.wait(receiveTimeout);
} catch (InterruptedException e) {}
}
// After the wait check if we received any response, if not throw timeout exception.
if (responseList.size() < 1)
throw new TimeoutException();
// Return the received packet.
return responseList.get(0);
} finally {
// Always remove the packet listener from the list.
removePacketListener(packetReceiveListener);
}
}
}
/**
* Insert (if possible) the next frame ID stored in the device to the
* provided packet.
*
* @param xbeePacket The packet to add the frame ID.
*
* @see com.digi.xbee.api.packet.XBeePacket
*/
private void insertFrameID(XBeePacket xbeePacket) {
if (xbeePacket instanceof XBeeAPIPacket)
return;
XBeeAPIPacket apiPacket = (XBeeAPIPacket)xbeePacket;
if (apiPacket.needsAPIFrameID()
&& apiPacket.getFrameID() == XBeeAPIPacket.NO_FRAME_ID)
apiPacket.setFrameID(getNextFrameID());
}
/**
* Returns the packet listener corresponding to the provided sent packet.
*
* The listener will filter those packets matching with the Frame ID of
* the sent packet storing them in the provided responseList array.
*
* @param sentPacket The packet sent.
* @param responseList List of packets received that correspond to the
* frame ID of the packet sent.
*
* @return A packet receive listener that will filter the packets received
* corresponding to the sent one.
*
* @see com.digi.xbee.api.listeners.IPacketReceiveListener
* @see com.digi.xbee.api.packet.XBeePacket
*/
private IPacketReceiveListener createPacketReceivedListener(final XBeePacket sentPacket, final ArrayList responseList) {
IPacketReceiveListener packetReceiveListener = new IPacketReceiveListener() {
/*
* (non-Javadoc)
* @see com.digi.xbee.api.listeners.IPacketReceiveListener#packetReceived(com.digi.xbee.api.packet.XBeePacket)
*/
@Override
public void packetReceived(XBeePacket receivedPacket) {
// Check if it is the packet we are waiting for.
if (((XBeeAPIPacket)receivedPacket).checkFrameID((((XBeeAPIPacket)sentPacket).getFrameID()))) {
// Security check to avoid class cast exceptions. It has been observed that parallel processes
// using the same connection but with different frame index may collide and cause this exception at some point.
if (sentPacket instanceof XBeeAPIPacket
&& receivedPacket instanceof XBeeAPIPacket) {
XBeeAPIPacket sentAPIPacket = (XBeeAPIPacket)sentPacket;
XBeeAPIPacket receivedAPIPacket = (XBeeAPIPacket)receivedPacket;
// If the packet sent is an AT command, verify that the received one is an AT command response and
// the command matches in both packets.
if (sentAPIPacket.getFrameType() == APIFrameType.AT_COMMAND) {
if (receivedAPIPacket.getFrameType() != APIFrameType.AT_COMMAND_RESPONSE)
return;
if (!((ATCommandPacket)sentAPIPacket).getCommand().equalsIgnoreCase(((ATCommandResponsePacket)receivedPacket).getCommand()))
return;
}
// If the packet sent is a remote AT command, verify that the received one is a remote AT command response and
// the command matches in both packets.
if (sentAPIPacket.getFrameType() == APIFrameType.REMOTE_AT_COMMAND_REQUEST) {
if (receivedAPIPacket.getFrameType() != APIFrameType.REMOTE_AT_COMMAND_RESPONSE)
return;
if (!((RemoteATCommandPacket)sentAPIPacket).getCommand().equalsIgnoreCase(((RemoteATCommandResponsePacket)receivedPacket).getCommand()))
return;
}
}
// Verify that the sent packet is not the received one! This can happen when the echo mode is enabled in the
// serial port.
if (!sentPacket.equals(receivedPacket)) {
responseList.add(receivedPacket);
synchronized (responseList) {
responseList.notify();
}
}
}
}
};
return packetReceiveListener;
}
/**
* Writes the given XBee packet in the connection interface of this device.
*
* @param packet XBee packet to be written.
*
* @throws IOException if an I/O error occurs while writing the XBee packet
* in the connection interface.
*
* @see com.digi.xbee.api.packet.XBeePacket
*/
private void writePacket(XBeePacket packet) throws IOException {
logger.debug(toString() + "Sending XBee packet: \n{}", packet.toPrettyString());
// Write bytes with the required escaping mode.
switch (operatingMode) {
case API:
default:
connectionInterface.writeData(packet.generateByteArray());
break;
case API_ESCAPE:
connectionInterface.writeData(packet.generateByteArrayEscaped());
break;
}
}
/**
* Returns the next Frame ID of this XBee device.
*
* @return The next Frame ID.
*/
protected int getNextFrameID() {
if (isRemote())
return localXBeeDevice.getNextFrameID();
if (currentFrameID == 0xff) {
// Reset counter.
currentFrameID = 1;
} else
currentFrameID ++;
return currentFrameID;
}
/**
* Sends the provided {@code XBeePacket} and determines if the transmission
* status is success for synchronous transmissions.
*
* If the status is not success, an {@code TransmitException} is thrown.
*
* @param packet The {@code XBeePacket} to be sent.
* @param asyncTransmission Determines whether the transmission must be
* asynchronous.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code packet == null}.
* @throws TransmitException if {@code packet} is not an instance of
* {@code TransmitStatusPacket} or
* if {@code packet} is not an instance of
* {@code TXStatusPacket} or
* if its transmit status is different than
* {@code XBeeTransmitStatus.SUCCESS}.
* @throws XBeeException if there is any other XBee related error.
*
* @see com.digi.xbee.api.packet.XBeePacket
*/
protected void sendAndCheckXBeePacket(XBeePacket packet, boolean asyncTransmission) throws TransmitException, XBeeException {
XBeePacket receivedPacket = null;
// Send the XBee packet.
try {
if (asyncTransmission)
sendXBeePacketAsync(packet);
else
receivedPacket = sendXBeePacket(packet);
} catch (IOException e) {
throw new XBeeException("Error writing in the communication interface.", e);
}
// If the transmission is async. we are done.
if (asyncTransmission)
return;
// Check if the packet received is a valid transmit status packet.
if (receivedPacket == null)
throw new TransmitException(null);
XBeeTransmitStatus status = null;
if (receivedPacket instanceof TransmitStatusPacket)
status = ((TransmitStatusPacket)receivedPacket).getTransmitStatus();
else if (receivedPacket instanceof TXStatusPacket)
status = ((TXStatusPacket)receivedPacket).getTransmitStatus();
if (status != XBeeTransmitStatus.SUCCESS
&& status != XBeeTransmitStatus.SELF_ADDRESSED)
throw new TransmitException(status);
}
/**
* Sets the configuration of the given IO line of this XBee device.
*
* @param ioLine The IO line to configure.
* @param ioMode The IO mode to set to the IO line.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code ioLine == null} or
* if {@code ioMode == null}.
* @throws TimeoutException if there is a timeout sending the set
* configuration command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getIOConfiguration(IOLine)
* @see com.digi.xbee.api.io.IOLine
* @see com.digi.xbee.api.io.IOMode
*/
public void setIOConfiguration(IOLine ioLine, IOMode ioMode) throws TimeoutException, XBeeException {
// Check IO line.
if (ioLine == null)
throw new NullPointerException("IO line cannot be null.");
if (ioMode == null)
throw new NullPointerException("IO mode cannot be null.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
setParameter(ioLine.getConfigurationATCommand(), new byte[]{(byte)ioMode.getID()});
}
/**
* Returns the configuration mode of the provided IO line of this XBee
* device.
*
* @param ioLine The IO line to get its configuration.
*
* @return The IO mode (configuration) of the provided IO line.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code ioLine == null}.
* @throws TimeoutException if there is a timeout sending the get
* configuration command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #setIOConfiguration(IOLine, IOMode)
* @see com.digi.xbee.api.io.IOLine
* @see com.digi.xbee.api.io.IOMode
*/
public IOMode getIOConfiguration(IOLine ioLine) throws TimeoutException, XBeeException {
// Check IO line.
if (ioLine == null)
throw new NullPointerException("DIO pin cannot be null.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
// Check if the received configuration mode is valid.
int ioModeValue = getParameter(ioLine.getConfigurationATCommand())[0];
IOMode dioMode = IOMode.getIOMode(ioModeValue, ioLine);
if (dioMode == null)
throw new OperationNotSupportedException("Received configuration mode '" + HexUtils.integerToHexString(ioModeValue, 1) + "' is not valid.");
// Return the configuration mode.
return dioMode;
}
/**
* Sets the digital value (high or low) to the provided IO line of this
* XBee device.
*
* @param ioLine The IO line to set its value.
* @param ioValue The IOValue to set to the IO line ({@code HIGH} or
* {@code LOW}).
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code ioLine == null} or
* if {@code ioValue == null}.
* @throws TimeoutException if there is a timeout sending the set DIO
* command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getIOConfiguration(IOLine)
* @see #setIOConfiguration(IOLine, IOMode)
* @see com.digi.xbee.api.io.IOLine
* @see com.digi.xbee.api.io.IOValue
* @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_HIGH
* @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_LOW
*/
public void setDIOValue(IOLine ioLine, IOValue ioValue) throws TimeoutException, XBeeException {
// Check IO line.
if (ioLine == null)
throw new NullPointerException("IO line cannot be null.");
// Check IO value.
if (ioValue == null)
throw new NullPointerException("IO value cannot be null.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
setParameter(ioLine.getConfigurationATCommand(), new byte[]{(byte)ioValue.getID()});
}
/**
* Returns the digital value of the provided IO line of this XBee device.
*
* The provided IO line must be previously configured as digital I/O
* . To do so, use {@code setIOConfiguration} and the following
* {@code IOMode}:
*
*
* - {@code IOMode.DIGITAL_IN} to configure as digital input.
* - {@code IOMode.DIGITAL_OUT_HIGH} to configure as digital output, high.
*
* - {@code IOMode.DIGITAL_OUT_LOW} to configure as digital output, low.
*
*
*
* @param ioLine The IO line to get its digital value.
*
* @return The digital value corresponding to the provided IO line.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code ioLine == null}.
* @throws TimeoutException if there is a timeout sending the get IO values
* command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getIOConfiguration(IOLine)
* @see #setIOConfiguration(IOLine, IOMode)
* @see com.digi.xbee.api.io.IOLine
* @see com.digi.xbee.api.io.IOValue
* @see com.digi.xbee.api.io.IOMode#DIGITAL_IN
* @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_HIGH
* @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_LOW
*/
public IOValue getDIOValue(IOLine ioLine) throws TimeoutException, XBeeException {
// Check IO line.
if (ioLine == null)
throw new NullPointerException("IO line cannot be null.");
// Obtain an IO Sample from the XBee device.
IOSample ioSample = readIOSample();
// Check if the IO sample contains the expected IO line and value.
if (!ioSample.hasDigitalValues() || !ioSample.getDigitalValues().containsKey(ioLine))
throw new OperationNotSupportedException("Answer does not contain digital data for " + ioLine.getName() + ".");
// Return the digital value.
return ioSample.getDigitalValues().get(ioLine);
}
/**
* Sets the duty cycle (in %) of the provided IO line of this XBee device.
*
* The provided IO line must be:
*
*
* - PWM capable ({@link IOLine#hasPWMCapability()}).
* - Previously configured as PWM Output (use
* {@code setIOConfiguration} and {@code IOMode.PWM}).
*
*
* @param ioLine The IO line to set its duty cycle value.
* @param dutyCycle The duty cycle of the PWM.
*
* @throws IllegalArgumentException if {@code ioLine.hasPWMCapability() == false} or
* if {@code value < 0} or
* if {@code value > 1023}.
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code ioLine == null}.
* @throws TimeoutException if there is a timeout sending the set PWM duty
* cycle command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getIOConfiguration(IOLine)
* @see #getIOConfiguration(IOLine)
* @see #setIOConfiguration(IOLine, IOMode)
* @see #getPWMDutyCycle(IOLine)
* @see com.digi.xbee.api.io.IOLine
* @see com.digi.xbee.api.io.IOMode#PWM
*/
public void setPWMDutyCycle(IOLine ioLine, double dutyCycle) throws TimeoutException, XBeeException {
// Check IO line.
if (ioLine == null)
throw new NullPointerException("IO line cannot be null.");
// Check if the IO line has PWM capability.
if (!ioLine.hasPWMCapability())
throw new IllegalArgumentException("Provided IO line does not have PWM capability.");
// Check duty cycle limits.
if (dutyCycle < 0 || dutyCycle > 100)
throw new IllegalArgumentException("Duty Cycle must be between 0% and 100%.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
// Convert the value.
int finaldutyCycle = (int)(dutyCycle * 1023.0/100.0);
setParameter(ioLine.getPWMDutyCycleATCommand(), ByteUtils.intToByteArray(finaldutyCycle));
}
/**
* Gets the PWM duty cycle (in %) corresponding to the provided IO line of
* this XBee device.
*
* The provided IO line must be:
*
*
* - PWM capable ({@link IOLine#hasPWMCapability()}).
* - Previously configured as PWM Output (use
* {@code setIOConfiguration} and {@code IOMode.PWM}).
*
*
* @param ioLine The IO line to get its PWM duty cycle.
*
* @return The PWM duty cycle value corresponding to the provided IO line
* (0% - 100%).
*
* @throws IllegalArgumentException if {@code ioLine.hasPWMCapability() == false}.
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code ioLine == null}.
* @throws TimeoutException if there is a timeout sending the get PWM duty
* cycle command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getIOConfiguration(IOLine)
* @see #setIOConfiguration(IOLine, IOMode)
* @see #setPWMDutyCycle(IOLine, double)
* @see com.digi.xbee.api.io.IOLine
* @see com.digi.xbee.api.io.IOMode#PWM
*/
public double getPWMDutyCycle(IOLine ioLine) throws TimeoutException, XBeeException {
// Check IO line.
if (ioLine == null)
throw new NullPointerException("IO line cannot be null.");
// Check if the IO line has PWM capability.
if (!ioLine.hasPWMCapability())
throw new IllegalArgumentException("Provided IO line does not have PWM capability.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
byte[] value = getParameter(ioLine.getPWMDutyCycleATCommand());
// Return the PWM duty cycle value.
int readValue = ByteUtils.byteArrayToInt(value);
return Math.round((readValue * 100.0/1023.0) * 100.0) / 100.0;
}
/**
* Returns the analog value of the provided IO line of this XBee device.
*
* The provided IO line must be previously configured as ADC. To
* do so, use {@code setIOConfiguration} and {@code IOMode.ADC}.
*
* @param ioLine The IO line to get its analog value.
*
* @return The analog value corresponding to the provided IO line.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code ioLine == null}.
* @throws TimeoutException if there is a timeout sending the get IO values
* command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getIOConfiguration(IOLine)
* @see #setIOConfiguration(IOLine, IOMode)
* @see com.digi.xbee.api.io.IOLine
* @see com.digi.xbee.api.io.IOMode#ADC
*/
public int getADCValue(IOLine ioLine) throws TimeoutException, XBeeException {
// Check IO line.
if (ioLine == null)
throw new NullPointerException("IO line cannot be null.");
// Obtain an IO Sample from the XBee device.
IOSample ioSample = readIOSample();
// Check if the IO sample contains the expected IO line and value.
if (!ioSample.hasAnalogValues() || !ioSample.getAnalogValues().containsKey(ioLine))
throw new OperationNotSupportedException("Answer does not contain analog data for " + ioLine.getName() + ".");
// Return the analog value.
return ioSample.getAnalogValues().get(ioLine);
}
/**
* Sets the 64-bit destination extended address of this XBee device.
*
* {@link XBee64BitAddress#BROADCAST_ADDRESS} is the broadcast address
* for the PAN. {@link XBee64BitAddress#COORDINATOR_ADDRESS} can be used to
* address the Pan Coordinator.
*
* @param xbee64BitAddress 64-bit destination address to be configured.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code xbee64BitAddress == null}.
* @throws TimeoutException if there is a timeout sending the set
* destination address command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getDestinationAddress()
* @see com.digi.xbee.api.models.XBee64BitAddress
*/
public void setDestinationAddress(XBee64BitAddress xbee64BitAddress) throws TimeoutException, XBeeException {
if (xbee64BitAddress == null)
throw new NullPointerException("Address cannot be null.");
// This method needs to apply changes after modifying the destination
// address, but only if the destination address could be set successfully.
boolean applyChanges = isApplyConfigurationChangesEnabled();
if (applyChanges)
enableApplyConfigurationChanges(false);
byte[] address = xbee64BitAddress.getValue();
try {
setParameter("DH", Arrays.copyOfRange(address, 0, 4));
setParameter("DL", Arrays.copyOfRange(address, 4, 8));
applyChanges();
} finally {
// Always restore the old value of the AC.
enableApplyConfigurationChanges(applyChanges);
}
}
/**
* Returns the 64-bit destination extended address of this XBee device.
*
* {@link XBee64BitAddress#BROADCAST_ADDRESS} is the broadcast address
* for the PAN. {@link XBee64BitAddress#COORDINATOR_ADDRESS} can be used to
* address the Pan Coordinator.
*
* @return 64-bit destination address.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout sending the get
* destination address command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #setDestinationAddress(XBee64BitAddress)
* @see com.digi.xbee.api.models.XBee64BitAddress
*/
public XBee64BitAddress getDestinationAddress() throws TimeoutException, XBeeException {
byte[] dh = getParameter("DH");
byte[] dl = getParameter("DL");
byte[] address = new byte[dh.length + dl.length];
System.arraycopy(dh, 0, address, 0, dh.length);
System.arraycopy(dl, 0, address, dh.length, dl.length);
return new XBee64BitAddress(address);
}
/**
* Sets the IO sampling rate to enable periodic sampling in this XBee
* device.
*
* A sample rate of {@code 0} ms. disables this feature.
*
* All enabled digital IO and analog inputs will be sampled and
* transmitted every {@code rate} milliseconds to the configured destination
* address.
*
* The destination address can be configured using the
* {@code setDestinationAddress(XBee64BitAddress)} method and retrieved by
* {@code getDestinationAddress()}.
*
* @param rate IO sampling rate in milliseconds.
*
* @throws IllegalArgumentException if {@code rate < 0} or {@code rate >
* 0xFFFF}.
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout sending the set IO
* sampling rate command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getDestinationAddress()
* @see #setDestinationAddress(XBee64BitAddress)
* @see #getIOSamplingRate()
*/
public void setIOSamplingRate(int rate) throws TimeoutException, XBeeException {
// Check range.
if (rate < 0 || rate > 0xFFFF)
throw new IllegalArgumentException("Rate must be between 0 and 0xFFFF.");
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
setParameter("IR", ByteUtils.intToByteArray(rate));
}
/**
* Returns the IO sampling rate of this XBee device.
*
* A sample rate of {@code 0} means the IO sampling feature is disabled.
*
*
* Periodic sampling allows this XBee module to take an IO sample and
* transmit it to a remote device (configured in the destination address)
* at the configured periodic rate (ms).
*
* @return IO sampling rate in milliseconds.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout sending the get IO
* sampling rate command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getDestinationAddress()
* @see #setDestinationAddress(XBee64BitAddress)
* @see #setIOSamplingRate(int)
*/
public int getIOSamplingRate() throws TimeoutException, XBeeException {
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
byte[] rate = getParameter("IR");
return ByteUtils.byteArrayToInt(rate);
}
/**
* Sets the digital IO lines of this XBee device to be monitored and
* sampled whenever their status changes.
*
* A {@code null} set of lines disables this feature.
*
* If a change is detected on an enabled digital IO pin, a digital IO
* sample is immediately transmitted to the configured destination address.
*
*
* The destination address can be configured using the
* {@code setDestinationAddress(XBee64BitAddress)} method and retrieved by
* {@code getDestinationAddress()}.
*
* @param lines Set of IO lines to be monitored, {@code null} to disable
* this feature.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout sending the set DIO
* change detection command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getDestinationAddress()
* @see #getDIOChangeDetection()
* @see #setDestinationAddress(XBee64BitAddress)
*/
public void setDIOChangeDetection(Set lines) throws TimeoutException, XBeeException {
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
byte[] bitfield = new byte[2];
if (lines != null) {
for (IOLine line : lines) {
int i = line.getIndex();
if (i < 8)
bitfield[1] = (byte) (bitfield[1] | (1 << i));
else
bitfield[0] = (byte) (bitfield[0] | (1 << i - 8));
}
}
setParameter("IC", bitfield);
}
/**
* Returns the set of IO lines of this device that are monitored for
* change detection.
*
* A {@code null} set means the DIO change detection feature is disabled.
*
*
* Modules can be configured to transmit to the configured destination
* address a data sample immediately whenever a monitored digital IO line
* changes state.
*
* @return Set of digital IO lines that are monitored for change detection,
* {@code null} if there are no monitored lines.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout sending the get DIO
* change detection command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getDestinationAddress()
* @see #setDestinationAddress(XBee64BitAddress)
* @see #setDIOChangeDetection(Set)
*/
public Set getDIOChangeDetection() throws TimeoutException, XBeeException {
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
byte[] bitfield = getParameter("IC");
TreeSet lines = new TreeSet();
int mask = (bitfield[0] << 8) + (bitfield[1] & 0xFF);
for (int i = 0; i < 16; i++) {
if (ByteUtils.isBitEnabled(mask, i))
lines.add(IOLine.getDIO(i));
}
if (lines.size() > 0)
return lines;
return null;
}
/**
* Applies changes to all command registers causing queued command register
* values to be applied.
*
* This method must be invoked if the 'apply configuration changes'
* option is disabled and the changes to this XBee device parameters must
* be applied.
*
* To know if the 'apply configuration changes' option is enabled, use
* the {@code isApplyConfigurationChangesEnabled()} method. And to
* enable/disable this feature, the method
* {@code enableApplyConfigurationChanges(boolean)}.
*
* Applying changes does not imply the modifications will persist
* through subsequent resets. To do so, use the {@code writeChanges()}
* method.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout sending the get Apply
* Changes command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #enableApplyConfigurationChanges(boolean)
* @see #isApplyConfigurationChangesEnabled()
* @see #setParameter(String, byte[])
* @see #writeChanges()
*/
public void applyChanges() throws TimeoutException, XBeeException {
executeParameter("AC");
}
/**
* Checks if the provided {@code ATCommandResponse} is valid throwing an
* {@code ATCommandException} in case it is not.
*
* @param response The {@code ATCommandResponse} to check.
*
* @throws ATCommandException if {@code response == null} or
* if {@code response.getResponseStatus() != ATCommandStatus.OK}.
*
* @see com.digi.xbee.api.models.ATCommandResponse
*/
protected void checkATCommandResponseIsValid(ATCommandResponse response) throws ATCommandException {
if (response == null || response.getResponseStatus() == null)
throw new ATCommandException(null);
else if (response.getResponseStatus() != ATCommandStatus.OK)
throw new ATCommandException(response.getResponseStatus());
}
/**
* Returns an IO sample from this XBee device containing the value of all
* enabled digital IO and analog input channels.
*
* @return An IO sample containing the value of all enabled digital IO and
* analog input channels.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout getting the IO sample.
* @throws XBeeException if there is any other XBee related exception.
*
* @see com.digi.xbee.api.io.IOSample
*/
public IOSample readIOSample() throws TimeoutException, XBeeException {
// Check connection.
if (!connectionInterface.isOpen())
throw new InterfaceNotOpenException();
// Try to build an IO Sample from the sample payload.
byte[] samplePayload = null;
IOSample ioSample;
// The response to the IS command in local 802.15.4 devices is empty,
// so we have to create a packet listener to receive the IO sample.
if (!isRemote() && getXBeeProtocol() == XBeeProtocol.RAW_802_15_4) {
executeParameter("IS");
samplePayload = receiveRaw802IOPacket();
if (samplePayload == null)
throw new TimeoutException("Timeout waiting for the IO response packet.");
} else
samplePayload = getParameter("IS");
try {
ioSample = new IOSample(samplePayload);
} catch (IllegalArgumentException e) {
throw new XBeeException("Couldn't create the IO sample.", e);
} catch (NullPointerException e) {
throw new XBeeException("Couldn't create the IO sample.", e);
}
return ioSample;
}
/**
* Returns the latest 802.15.4 IO packet and returns its value.
*
* @return The value of the latest received 802.15.4 IO packet.
*/
private byte[] receiveRaw802IOPacket() {
ioPacketReceived = false;
ioPacketPayload = null;
addPacketListener(IOPacketReceiveListener);
synchronized (ioLock) {
try {
ioLock.wait(receiveTimeout);
} catch (InterruptedException e) { }
}
removePacketListener(IOPacketReceiveListener);
if (ioPacketReceived)
return ioPacketPayload;
return null;
}
/**
* Custom listener for 802.15.4 IO packets. It will try to receive an
* 802.15.4 IO sample packet.
*
* When an IO sample packet is received, it saves its payload and
* notifies the object that was waiting for the reception.
*/
private IPacketReceiveListener IOPacketReceiveListener = new IPacketReceiveListener() {
/*
* (non-Javadoc)
* @see com.digi.xbee.api.listeners.IPacketReceiveListener#packetReceived(com.digi.xbee.api.packet.XBeePacket)
*/
@Override
public void packetReceived(XBeePacket receivedPacket) {
// Discard non API packets.
if (!(receivedPacket instanceof XBeeAPIPacket))
return;
// If we already have received an IO packet, ignore this packet.
if (ioPacketReceived)
return;
// Save the packet value (IO sample payload)
switch (((XBeeAPIPacket)receivedPacket).getFrameType()) {
case IO_DATA_SAMPLE_RX_INDICATOR:
ioPacketPayload = ((IODataSampleRxIndicatorPacket)receivedPacket).getRFData();
break;
case RX_IO_16:
ioPacketPayload = ((RX16IOPacket)receivedPacket).getRFData();
break;
case RX_IO_64:
ioPacketPayload = ((RX64IOPacket)receivedPacket).getRFData();
break;
default:
return;
}
// Set the IO packet received flag.
ioPacketReceived = true;
// Continue execution by notifying the lock object.
synchronized (ioLock) {
ioLock.notify();
}
}
};
/**
* Performs a software reset on this XBee device and blocks until the
* process is completed.
*
* @throws TimeoutException if the configured time expires while waiting
* for the command reply.
* @throws XBeeException if there is any other XBee related exception.
*/
abstract public void reset() throws TimeoutException, XBeeException;
/**
* Sets the given parameter with the provided value in this XBee device.
*
* If the 'apply configuration changes' option is enabled in this device,
* the configured value for the given parameter will be immediately applied,
* if not the method {@code applyChanges()} must be invoked to apply it.
*
* Use:
*
* - Method {@code isApplyConfigurationChangesEnabled()} to know
* if the 'apply configuration changes' option is enabled.
* - Method {@code enableApplyConfigurationChanges(boolean)} to enable or
* disable this option.
*
*
* To make parameter modifications persist through subsequent resets use
* the {@code writeChanges()} method.
*
* @param parameter The name of the parameter to be set.
* @param parameterValue The value of the parameter to set.
*
* @throws IllegalArgumentException if {@code parameter.length() != 2}.
* @throws NullPointerException if {@code parameter == null} or
* if {@code parameterValue == null}.
* @throws TimeoutException if there is a timeout setting the parameter.
* @throws XBeeException if {@code parameter} is not supported by the module or
* if {@code parameterValue} is not supported or
* if there is any other XBee related exception.
*
* @see #applyChanges()
* @see #enableApplyConfigurationChanges(boolean)
* @see #executeParameter(String)
* @see #getParameter(String)
* @see #isApplyConfigurationChangesEnabled()
* @see #writeChanges()
*/
public void setParameter(String parameter, byte[] parameterValue) throws TimeoutException, XBeeException {
if (parameterValue == null)
throw new NullPointerException("Value of the parameter cannot be null.");
sendParameter(parameter, parameterValue);
}
/**
* Gets the value of the given parameter from this XBee device.
*
* @param parameter The name of the parameter to retrieve its value.
*
* @return A byte array containing the value of the parameter.
*
* @throws IllegalArgumentException if {@code parameter.length() != 2}.
* @throws NullPointerException if {@code parameter == null}.
* @throws TimeoutException if there is a timeout getting the parameter value.
* @throws XBeeException if {@code parameter} is not supported by the module or
* if there is any other XBee related exception.
*
* @see #executeParameter(String)
* @see #setParameter(String, byte[])
*/
public byte[] getParameter(String parameter) throws TimeoutException, XBeeException {
byte[] parameterValue = sendParameter(parameter, null);
// Check if the response is null, if so throw an exception (maybe it was a write-only parameter).
if (parameterValue == null)
throw new OperationNotSupportedException("Couldn't get the '" + parameter + "' value.");
return parameterValue;
}
/**
* Executes the given command in this XBee device.
*
* This method is intended to be used for those AT parameters that cannot
* be read or written, they just execute some action in the XBee module.
*
* @param parameter The AT command to be executed.
*
* @throws IllegalArgumentException if {@code parameter.length() != 2}.
* @throws NullPointerException if {@code parameter == null}.
* @throws TimeoutException if there is a timeout executing the parameter.
* @throws XBeeException if {@code parameter} is not supported by the module or
* if there is any other XBee related exception.
*
* @see #getParameter(String)
* @see #setParameter(String, byte[])
*/
public void executeParameter(String parameter) throws TimeoutException, XBeeException {
sendParameter(parameter, null);
}
/**
* Sends the given AT parameter to this XBee device with an optional
* argument or value and returns the response (likely the value) of that
* parameter in a byte array format.
*
* @param parameter The name of the AT command to be executed.
* @param parameterValue The value of the parameter to set (if any).
*
* @return A byte array containing the value of the parameter.
*
* @throws IllegalArgumentException if {@code parameter.length() != 2}.
* @throws NullPointerException if {@code parameter == null}.
* @throws TimeoutException if there is a timeout executing the parameter.
* @throws XBeeException if {@code parameter} is not supported by the module or
* if {@code parameterValue} is not supported or
* if there is any other XBee related exception.
*
* @see #getParameter(String)
* @see #executeParameter(String)
* @see #setParameter(String, byte[])
*/
private byte[] sendParameter(String parameter, byte[] parameterValue) throws TimeoutException, XBeeException {
if (parameter == null)
throw new NullPointerException("Parameter cannot be null.");
if (parameter.length() != 2)
throw new IllegalArgumentException("Parameter must contain exactly 2 characters.");
ATCommand atCommand = new ATCommand(parameter, parameterValue);
// Create and send the AT Command.
ATCommandResponse response = null;
try {
response = sendATCommand(atCommand);
} catch (IOException e) {
throw new XBeeException("Error writing in the communication interface.", e);
}
// Check if AT Command response is valid.
checkATCommandResponseIsValid(response);
// Return the response value.
return response.getResponse();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return connectionInterface.toString();
}
/**
* Enables or disables the 'apply configuration changes' option for this
* device.
*
* Enabling this option means that when any parameter of this XBee
* device is set, it will be also applied.
*
* If this option is disabled, the method {@code applyChanges()} must be
* used in order to apply the changes in all the parameters that were
* previously set.
*
* @param enabled {@code true} to apply configuration changes when an XBee
* parameter is set, {@code false} otherwise.
*
* @see #applyChanges()
* @see #isApplyConfigurationChangesEnabled()
*/
public void enableApplyConfigurationChanges(boolean enabled) {
applyConfigurationChanges = enabled;
}
/**
* Returns whether the 'apply configuration changes' option is enabled in
* this device.
*
* If this option is enabled, when any parameter of this XBee device is
* set, it will be also applied.
*
* If this option is disabled, the method {@code applyChanges()} must be
* used in order to apply the changes in all the parameters that were
* previously set.
*
* @return {@code true} if the option is enabled, {@code false} otherwise.
*
* @see #applyChanges()
* @see #enableApplyConfigurationChanges(boolean)
*/
public boolean isApplyConfigurationChangesEnabled() {
return applyConfigurationChanges;
}
/**
* Configures the 16-bit address (network address) of this XBee device with
* the provided one.
*
* @param xbee16BitAddress The new 16-bit address.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code xbee16BitAddress == null}.
* @throws TimeoutException if there is a timeout setting the address.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #get16BitAddress()
* @see com.digi.xbee.api.models.XBee16BitAddress
*/
protected void set16BitAddress(XBee16BitAddress xbee16BitAddress) throws TimeoutException, XBeeException {
if (xbee16BitAddress == null)
throw new NullPointerException("16-bit address canot be null.");
setParameter("MY", xbee16BitAddress.getValue());
this.xbee16BitAddress = xbee16BitAddress;
}
/**
* Returns the operating PAN ID (Personal Area Network Identifier) of
* this XBee device.
*
* For modules to communicate they must be configured with the same
* identifier. Only modules with matching IDs can communicate with each
* other.This parameter allows multiple networks to co-exist on the same
* physical channel.
*
* @return The operating PAN ID of this XBee device.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout getting the PAN ID.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #setPANID(byte[])
*/
public byte[] getPANID() throws TimeoutException, XBeeException {
switch (getXBeeProtocol()) {
case ZIGBEE:
return getParameter("OP");
default:
return getParameter("ID");
}
}
/**
* Sets the PAN ID (Personal Area Network Identifier) of this XBee device.
*
* For modules to communicate they must be configured with the same
* identifier. Only modules with matching IDs can communicate with each
* other.This parameter allows multiple networks to co-exist on the same
* physical channel.
*
* @param panID The new PAN ID of this XBee device.
*
* @throws IllegalArgumentException if {@code panID.length == 0} or
* if {@code panID.length > 8}.
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code panID == null}.
* @throws TimeoutException if there is a timeout setting the PAN ID.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getPANID()
*/
public void setPANID(byte[] panID) throws TimeoutException, XBeeException {
if (panID == null)
throw new NullPointerException("PAN ID cannot be null.");
if (panID.length == 0)
throw new IllegalArgumentException("Length of the PAN ID cannot be 0.");
if (panID.length > 8)
throw new IllegalArgumentException("Length of the PAN ID cannot be longer than 8 bytes.");
setParameter("ID", panID);
}
/**
* Returns the output power level at which this XBee device transmits
* conducted power.
*
* @return The output power level of this XBee device.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout getting the power level.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #setPowerLevel(PowerLevel)
* @see com.digi.xbee.api.models.PowerLevel
*/
public PowerLevel getPowerLevel() throws TimeoutException, XBeeException {
byte[] powerLevelValue = getParameter("PL");
return PowerLevel.get(ByteUtils.byteArrayToInt(powerLevelValue));
}
/**
* Sets the output power level at which this XBee device transmits
* conducted power.
*
* @param powerLevel The new output power level to be set in this XBee
* device.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws NullPointerException if {@code powerLevel == null}.
* @throws TimeoutException if there is a timeout setting the power level.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getPowerLevel()
* @see com.digi.xbee.api.models.PowerLevel
*/
public void setPowerLevel(PowerLevel powerLevel) throws TimeoutException, XBeeException {
if (powerLevel == null)
throw new NullPointerException("Power level cannot be null.");
setParameter("PL", ByteUtils.intToByteArray(powerLevel.getValue()));
}
/**
* Returns the current association status of this XBee device.
*
* It indicates occurrences of errors during the last association
* request.
*
* @return The association indication status of the XBee device.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout getting the association
* indication status.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #forceDisassociate()
* @see com.digi.xbee.api.models.AssociationIndicationStatus
*/
protected AssociationIndicationStatus getAssociationIndicationStatus() throws TimeoutException, XBeeException {
byte[] associationIndicationValue = getParameter("AI");
return AssociationIndicationStatus.get(ByteUtils.byteArrayToInt(associationIndicationValue));
}
/**
* Forces this XBee device to immediately disassociate from the network and
* re-attempt to associate.
*
* Only valid for End Devices.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout executing the
* disassociation command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #getAssociationIndicationStatus()
*/
protected void forceDisassociate() throws TimeoutException, XBeeException {
executeParameter("DA");
}
/**
* Writes configurable parameter values to the non-volatile memory of this
* XBee device so that parameter modifications persist through subsequent
* resets.
*
* Parameters values remain in this device's memory until overwritten by
* subsequent use of this method.
*
* If changes are made without writing them to non-volatile memory, the
* module reverts back to previously saved parameters the next time the
* module is powered-on.
*
* Writing the parameter modifications does not mean those values are
* immediately applied, this depends on the status of the 'apply
* configuration changes' option. Use method
* {@code isApplyConfigurationChangesEnabled()} to get its status and
* {@code enableApplyConfigurationChanges(boolean)} to enable/disable the
* option. If it is disable method {@code applyChanges()} can be used in
* order to manually apply the changes.
*
* @throws InterfaceNotOpenException if this device connection is not open.
* @throws TimeoutException if there is a timeout executing the write
* changes command.
* @throws XBeeException if there is any other XBee related exception.
*
* @see #applyChanges()
* @see #enableApplyConfigurationChanges(boolean)
* @see #isApplyConfigurationChangesEnabled()
* @see #setParameter(String, byte[])
*/
public void writeChanges() throws TimeoutException, XBeeException {
executeParameter("WR");
}
}