001/** 002 * Copyright (c) 2014-2015 Digi International Inc., 003 * All rights not expressly granted are reserved. 004 * 005 * This Source Code Form is subject to the terms of the Mozilla Public 006 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 007 * You can obtain one at http://mozilla.org/MPL/2.0/. 008 * 009 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343 010 * ======================================================================= 011 */ 012package com.digi.xbee.api.packet.common; 013 014import java.io.ByteArrayOutputStream; 015import java.io.IOException; 016import java.util.Arrays; 017import java.util.LinkedHashMap; 018 019import org.slf4j.Logger; 020import org.slf4j.LoggerFactory; 021 022import com.digi.xbee.api.models.XBee16BitAddress; 023import com.digi.xbee.api.models.XBee64BitAddress; 024import com.digi.xbee.api.packet.APIFrameType; 025import com.digi.xbee.api.packet.XBeeAPIPacket; 026import com.digi.xbee.api.utils.HexUtils; 027 028/** 029 * This class represents an Explicit Addressing Command packet. Packet is 030 * built using the parameters of the constructor or providing a valid API 031 * payload. 032 * 033 * <p>Allows application layer fields (endpoint and cluster ID) to be 034 * specified for a data transmission. Similar to the Transmit Request, but 035 * also requires application layer addressing fields to be specified 036 * (endpoints, cluster ID, profile ID). An Explicit Addressing Request API 037 * frame causes the module to send data as an RF packet to the specified 038 * destination, using the specified source and destination endpoints, cluster 039 * ID, and profile ID.</p> 040 * 041 * <p>The 64-bit destination address should be set to 042 * {@code 0x000000000000FFFF} for a broadcast transmission (to all 043 * devices).</p> 044 * 045 * <p>The coordinator can be addressed by either setting the 64-bit address 046 * to all {@code 0x00} and the 16-bit address to {@code 0xFFFE}, OR by 047 * setting the 64-bit address to the coordinator's 64-bit address and the 048 * 16-bit address to {@code 0x0000}.</p> 049 * 050 * <p>For all other transmissions, setting the 16-bit address to the correct 051 * 16-bit address can help improve performance when transmitting to 052 * multiple destinations.</p> 053 * 054 * <p>If a 16-bit address is not known, this field should be set to 055 * {@code 0xFFFE} (unknown).</p>. 056 * 057 * <p>The Transmit Status frame 058 * ({@link com.digi.xbee.api.packet.APIFrameType#TRANSMIT_REQUEST}) will 059 * indicate the discovered 16-bit address, if successful (see 060 * {@link com.digi.xbee.api.packet.common.TransmitStatusPacket}).</p> 061 * 062 * <p>The broadcast radius can be set from {@code 0} up to {@code NH}. If set 063 * to {@code 0}, the value of {@code NH} specifies the broadcast radius 064 * (recommended). This parameter is only used for broadcast transmissions.</p> 065 * 066 * <p>The maximum number of payload bytes can be read with the {@code NP} 067 * command. Note: if source routing is used, the RF payload will be reduced 068 * by two bytes per intermediate hop in the source route.</p> 069 * 070 * <p>Several transmit options can be set using the transmit options bitfield. 071 * </p> 072 * 073 * @see com.digi.xbee.api.models.XBeeTransmitOptions 074 * @see com.digi.xbee.api.models.XBee16BitAddress#COORDINATOR_ADDRESS 075 * @see com.digi.xbee.api.models.XBee16BitAddress#UNKNOWN_ADDRESS 076 * @see com.digi.xbee.api.models.XBee64BitAddress#BROADCAST_ADDRESS 077 * @see com.digi.xbee.api.models.XBee64BitAddress#COORDINATOR_ADDRESS 078 * @see com.digi.xbee.api.packet.common.ExplicitRxIndicatorPacket 079 * @see com.digi.xbee.api.packet.XBeeAPIPacket 080 */ 081public class ExplicitAddressingPacket extends XBeeAPIPacket { 082 083 // Constants 084 private static final int MIN_API_PAYLOAD_LENGTH = 20; // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (source endpoint) + 1 (destination endpoint) + 2 (cluster ID) + 2 (profile ID) + 1 (broadcast radius) + 1 (options) 085 086 // Variables 087 private final XBee64BitAddress destAddress64; 088 089 private final XBee16BitAddress destAddress16; 090 091 private final int broadcastRadius; 092 private final int transmitOptions; 093 094 private final int sourceEndpoint; 095 private final int destEndpoint; 096 private final int clusterID; 097 private final int profileID; 098 private byte[] rfData; 099 100 private Logger logger; 101 102 /** 103 * Creates a new {@code ExplicitAddressingPacket} object from the given 104 * payload. 105 * 106 * @param payload The API frame payload. It must start with the frame type 107 * corresponding to an Explicit Addressing packet 108 * ({@code 0x11}). 109 * The byte array must be in {@code OperatingMode.API} mode. 110 * 111 * @return Parsed Explicit Addressing packet. 112 * 113 * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.EXPLICIT_ADDRESSING_COMMAND_FRAME.getValue()} or 114 * if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or 115 * if {@code frameID < 0} or 116 * if {@code frameID > 255} or 117 * if {@code sourceEndpoint < 0} or 118 * if {@code sourceEndpoint > 255} or 119 * if {@code destEndpoint < 0} or 120 * if {@code destEndpoint > 255} or 121 * if {@code clusterID < 0} or 122 * if {@code clusterID > 65535} or 123 * if {@code profileID < 0} or 124 * if {@code profileID > 65535} or 125 * if {@code broadcastRadius < 0} or 126 * if {@code broadcastRadius > 255} or 127 * if {@code transmitOptions < 0} or 128 * if {@code transmitOptions > 255}. 129 * @throws NullPointerException if {@code payload == null}. 130 */ 131 public static ExplicitAddressingPacket createPacket(byte[] payload) { 132 if (payload == null) 133 throw new NullPointerException("Explicit Addressing packet payload cannot be null."); 134 135 // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (source endpoint) + 1 (destination endpoint) + 2 (cluster ID) + 2 (profile ID) + 1 (broadcast radius) + 1 (options) 136 if (payload.length < MIN_API_PAYLOAD_LENGTH) 137 throw new IllegalArgumentException("Incomplete Explicit Addressing packet."); 138 139 if ((payload[0] & 0xFF) != APIFrameType.EXPLICIT_ADDRESSING_COMMAND_FRAME.getValue()) 140 throw new IllegalArgumentException("Payload is not an Explicit Addressing packet."); 141 142 // payload[0] is the frame type. 143 int index = 1; 144 145 // Frame ID byte. 146 int frameID = payload[index] & 0xFF; 147 index = index + 1; 148 149 // 8 bytes of 64-bit address. 150 XBee64BitAddress destAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8)); 151 index = index + 8; 152 153 // 2 bytes of 16-bit address. 154 XBee16BitAddress destAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF); 155 index = index + 2; 156 157 // Source endpoint byte. 158 int sourceEndpoint = payload[index] & 0xFF; 159 index = index + 1; 160 161 // Destination endpoint byte. 162 int destEndpoint = payload[index] & 0xFF; 163 index = index + 1; 164 165 // 2 bytes of cluster ID. 166 int clusterID = (payload[index] & 0xFF) << 8 | payload[index + 1] & 0xFF; 167 index = index + 2; 168 169 // 2 bytes of profile ID. 170 int profileID = (payload[index] & 0xFF) << 8 | payload[index + 1] & 0xFF; 171 index = index + 2; 172 173 // Broadcast radius byte. 174 int broadcastRadius = payload[index] & 0xFF; 175 index = index + 1; 176 177 // Options byte. 178 int options = payload[index] & 0xFF; 179 index = index + 1; 180 181 // Get RF data. 182 byte[] rfData = null; 183 if (index < payload.length) 184 rfData = Arrays.copyOfRange(payload, index, payload.length); 185 186 return new ExplicitAddressingPacket(frameID, destAddress64, destAddress16, sourceEndpoint, destEndpoint, clusterID, profileID, broadcastRadius, options, rfData); 187 } 188 189 /** 190 * Class constructor. Instantiates a new {@code ExplicitAddressingPacket} 191 * object with the given parameters. 192 * 193 * @param frameID Frame ID. 194 * @param destAddress64 64-bit address of the destination device. 195 * @param destAddress16 16-bit address of the destination device. 196 * @param sourceEndpoint Source endpoint for the transaction. 197 * @param destEndpoint Destination endpoint for the transaction. 198 * @param clusterID Cluster ID used in the transaction. 199 * @param profileID Profile ID used in the transaction. 200 * @param broadcastRadius Maximum number of hops a broadcast transmission 201 * can traverse. Set to 0 to use the network 202 * maximum hops value. 203 * @param transmitOptions Bitfield of supported transmission options. 204 * @param rfData RF Data that is sent to the destination device. 205 * 206 * @throws IllegalArgumentException if {@code frameID < 0} or 207 * if {@code frameID > 255} or 208 * if {@code sourceEndpoint < 0} or 209 * if {@code sourceEndpoint > 255} or 210 * if {@code destEndpoint < 0} or 211 * if {@code destEndpoint > 255} or 212 * if {@code clusterID < 0} or 213 * if {@code clusterID > 65535} or 214 * if {@code profileID < 0} or 215 * if {@code profileID > 65535} or 216 * if {@code broadcastRadius < 0} or 217 * if {@code broadcastRadius > 255} or 218 * if {@code transmitOptions < 0} or 219 * if {@code transmitOptions > 255}. 220 * @throws NullPointerException if {@code destAddress64 == null} or 221 * if {@code destAddress16 == null}. 222 * 223 * @see com.digi.xbee.api.models.XBeeTransmitOptions 224 * @see com.digi.xbee.api.models.XBee16BitAddress 225 * @see com.digi.xbee.api.models.XBee64BitAddress 226 */ 227 public ExplicitAddressingPacket(int frameID, XBee64BitAddress destAddress64, XBee16BitAddress destAddress16, 228 int sourceEndpoint, int destEndpoint, int clusterID, int profileID, int broadcastRadius, 229 int transmitOptions, byte[] rfData) { 230 super(APIFrameType.EXPLICIT_ADDRESSING_COMMAND_FRAME); 231 232 if (destAddress64 == null) 233 throw new NullPointerException("64-bit destination address cannot be null."); 234 if (destAddress16 == null) 235 throw new NullPointerException("16-bit destination address cannot be null."); 236 if (frameID < 0 || frameID > 255) 237 throw new IllegalArgumentException("Frame ID must be between 0 and 255."); 238 if (sourceEndpoint < 0 || sourceEndpoint > 255) 239 throw new IllegalArgumentException("Source endpoint must be between 0 and 255."); 240 if (destEndpoint < 0 || destEndpoint > 255) 241 throw new IllegalArgumentException("Destination endpoint must be between 0 and 255."); 242 if (clusterID < 0 || clusterID > 65535) 243 throw new IllegalArgumentException("Cluster ID must be between 0 and 65535."); 244 if (profileID < 0 || profileID > 65535) 245 throw new IllegalArgumentException("Profile ID must be between 0 and 65535."); 246 if (broadcastRadius < 0 || broadcastRadius > 255) 247 throw new IllegalArgumentException("Broadcast radius must be between 0 and 255."); 248 if (transmitOptions < 0 || transmitOptions > 255) 249 throw new IllegalArgumentException("Transmit options must be between 0 and 255."); 250 251 this.frameID = frameID; 252 this.destAddress64 = destAddress64; 253 this.destAddress16 = destAddress16; 254 this.sourceEndpoint = sourceEndpoint; 255 this.destEndpoint = destEndpoint; 256 this.clusterID = clusterID; 257 this.profileID = profileID; 258 this.broadcastRadius = broadcastRadius; 259 this.transmitOptions = transmitOptions; 260 this.rfData = rfData; 261 this.logger = LoggerFactory.getLogger(ExplicitAddressingPacket.class); 262 } 263 264 /* 265 * (non-Javadoc) 266 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData() 267 */ 268 @Override 269 public byte[] getAPIPacketSpecificData() { 270 ByteArrayOutputStream data = new ByteArrayOutputStream(); 271 try { 272 data.write(destAddress64.getValue()); 273 data.write(destAddress16.getValue()); 274 data.write(sourceEndpoint); 275 data.write(destEndpoint); 276 data.write(clusterID >> 8); 277 data.write(clusterID); 278 data.write(profileID >> 8); 279 data.write(profileID); 280 data.write(broadcastRadius); 281 data.write(transmitOptions); 282 if (rfData != null) 283 data.write(rfData); 284 } catch (IOException e) { 285 logger.error(e.getMessage(), e); 286 } 287 return data.toByteArray(); 288 } 289 290 /* 291 * (non-Javadoc) 292 * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID() 293 */ 294 @Override 295 public boolean needsAPIFrameID() { 296 return true; 297 } 298 299 /* 300 * (non-Javadoc) 301 * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast() 302 */ 303 @Override 304 public boolean isBroadcast() { 305 if (get64BitDestinationAddress().equals(XBee64BitAddress.BROADCAST_ADDRESS) 306 || get16BitDestinationAddress().equals(XBee16BitAddress.BROADCAST_ADDRESS)) 307 return true; 308 return false; 309 } 310 311 /** 312 * Returns the 64-bit destination address. 313 * 314 * @return The 64-bit destination address. 315 * 316 * @see com.digi.xbee.api.models.XBee64BitAddress 317 */ 318 public XBee64BitAddress get64BitDestinationAddress() { 319 return destAddress64; 320 } 321 322 /** 323 * Returns the 16-bit destination address. 324 * 325 * @return The 16-bit destination address. 326 * 327 * @see com.digi.xbee.api.models.XBee16BitAddress 328 */ 329 public XBee16BitAddress get16BitDestinationAddress() { 330 return destAddress16; 331 } 332 333 /** 334 * Returns the source endpoint of the transmission. 335 * 336 * @return The source endpoint of the transmission. 337 */ 338 public int getSourceEndpoint() { 339 return sourceEndpoint; 340 } 341 342 /** 343 * Returns the destination endpoint of the transmission. 344 * 345 * @return The destination endpoint of the transmission. 346 */ 347 public int getDestinationEndpoint() { 348 return destEndpoint; 349 } 350 351 /** 352 * Returns the cluster ID used in the transmission. 353 * 354 * @return The cluster ID used in the transmission. 355 */ 356 public int getClusterID() { 357 return clusterID; 358 } 359 360 /** 361 * Returns the profile ID used in the transmission. 362 * 363 * @return The profile ID used in the transmission. 364 */ 365 public int getProfileID() { 366 return profileID; 367 } 368 369 /** 370 * Returns the broadcast radius. 371 * 372 * @return The broadcast radius. 373 */ 374 public int getBroadcastRadius() { 375 return broadcastRadius; 376 } 377 378 /** 379 * Returns the transmit options bitfield. 380 * 381 * @return The transmit options bitfield. 382 * 383 * @see com.digi.xbee.api.models.XBeeTransmitOptions 384 */ 385 public int getTransmitOptions() { 386 return transmitOptions; 387 } 388 389 /** 390 * Sets the RF Data to send. 391 * 392 * @param rfData RF Data to send. 393 */ 394 public void setRFData(byte[] rfData) { 395 this.rfData = rfData; 396 } 397 398 /** 399 * Returns the RF Data to send. 400 * 401 * @return RF Data to send. 402 */ 403 public byte[] getRFData() { 404 return rfData; 405 } 406 407 /* 408 * (non-Javadoc) 409 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 410 */ 411 @Override 412 public LinkedHashMap<String, String> getAPIPacketParameters() { 413 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 414 parameters.put("Frame ID", HexUtils.prettyHexString(HexUtils.integerToHexString(frameID, 1)) + " (" + frameID + ")"); 415 parameters.put("64-bit dest. address", HexUtils.prettyHexString(destAddress64.toString())); 416 parameters.put("16-bit dest. address", HexUtils.prettyHexString(destAddress16.toString())); 417 parameters.put("Source endpoint", HexUtils.prettyHexString(HexUtils.integerToHexString(sourceEndpoint, 1))); 418 parameters.put("Dest. endpoint", HexUtils.prettyHexString(HexUtils.integerToHexString(destEndpoint, 1))); 419 parameters.put("Cluster ID", HexUtils.prettyHexString(HexUtils.integerToHexString(clusterID, 2))); 420 parameters.put("Profile ID", HexUtils.prettyHexString(HexUtils.integerToHexString(profileID, 2))); 421 parameters.put("Broadcast radius", HexUtils.prettyHexString(HexUtils.integerToHexString(broadcastRadius, 1)) + " (" + broadcastRadius + ")"); 422 parameters.put("Transmit options", HexUtils.prettyHexString(HexUtils.integerToHexString(transmitOptions, 1))); 423 if (rfData != null) 424 parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData))); 425 return parameters; 426 } 427}