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.ByteUtils; 027import com.digi.xbee.api.utils.HexUtils; 028 029/** 030 * This class represents an Explicit RX Indicator packet. Packet is 031 * built using the parameters of the constructor or providing a valid API 032 * payload. 033 * 034 * <p>When the modem receives an RF packet it is sent out the UART using this 035 * message type (when AO=1).</p> 036 * 037 * <p>This packet is received when external devices send explicit addressing 038 * packets to this module.</p> 039 * 040 * <p>Among received data, some options can also be received indicating 041 * transmission parameters.</p> 042 * 043 * @see com.digi.xbee.api.models.XBeeReceiveOptions 044 * @see com.digi.xbee.api.packet.common.ExplicitAddressingPacket 045 * @see com.digi.xbee.api.packet.XBeeAPIPacket 046 */ 047public class ExplicitRxIndicatorPacket extends XBeeAPIPacket { 048 049 // Constants 050 private static final int MIN_API_PAYLOAD_LENGTH = 18; // 1 (Frame type) + 8 (64-bit address) + 2 (16-bit address) + 1 (source endpoint) + 1 (destination endpoint) + 2 (cluster ID) + 2 (profile ID) + 1 (receive options) 051 052 public static final int DATA_ENDPOINT = 0xE8; 053 public static final int DATA_CLUSTER = 0x0011; 054 public static final int DIGI_PROFILE = 0xC105; 055 056 // Variables 057 private final XBee64BitAddress sourceAddress64; 058 059 private final XBee16BitAddress sourceAddress16; 060 061 private final int sourceEndpoint; 062 private final int destEndpoint; 063 private final int clusterID; 064 private final int profileID; 065 private final int receiveOptions; 066 067 private byte[] rfData; 068 069 private Logger logger; 070 071 /** 072 * Creates a new {@code ExplicitRxIndicatorPacket} object from the given 073 * payload. 074 * 075 * @param payload The API frame payload. It must start with the frame type 076 * corresponding to an Explicit RX Indicator packet 077 * ({@code 0x91}). 078 * The byte array must be in {@code OperatingMode.API} mode. 079 * 080 * @return Parsed Explicit RX Indicator packet. 081 * 082 * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.EXPLICIT_RX_INDICATOR.getValue()} or 083 * if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or 084 * if {@code sourceEndpoint < 0} or 085 * if {@code sourceEndpoint > 255} or 086 * if {@code destEndpoint < 0} or 087 * if {@code destEndpoint > 255} or 088 * if {@code clusterID < 0} or 089 * if {@code clusterID > 65535} or 090 * if {@code profileID < 0} or 091 * if {@code profileID > 65535} or 092 * if {@code transmitOptions < 0} or 093 * if {@code transmitOptions > 255}. 094 * @throws NullPointerException if {@code payload == null}. 095 */ 096 public static ExplicitRxIndicatorPacket createPacket(byte[] payload) { 097 if (payload == null) 098 throw new NullPointerException("Explicit Rx Indicator packet payload cannot be null."); 099 100 // 1 (Frame type) + 8 (64-bit address) + 2 (16-bit address) + 1 (source endpoint) + 1 (destination endpoint) + 2 (cluster ID) + 2 (profile ID) + 1 (receive options) 101 if (payload.length < MIN_API_PAYLOAD_LENGTH) 102 throw new IllegalArgumentException("Incomplete Explicit Rx Indicator packet."); 103 104 if ((payload[0] & 0xFF) != APIFrameType.EXPLICIT_RX_INDICATOR.getValue()) 105 throw new IllegalArgumentException("Payload is not an Explicit Rx Indicator packet."); 106 107 // payload[0] is the frame type. 108 int index = 1; 109 110 // 8 bytes of 64-bit address. 111 XBee64BitAddress destAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8)); 112 index = index + 8; 113 114 // 2 bytes of 16-bit address. 115 XBee16BitAddress destAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF); 116 index = index + 2; 117 118 // Source endpoint byte. 119 int sourceEndpoint = payload[index] & 0xFF; 120 index = index + 1; 121 122 // Destination endpoint byte. 123 int destEndpoint = payload[index] & 0xFF; 124 index = index + 1; 125 126 // 2 bytes of cluster ID. 127 int clusterID = (payload[index] & 0xFF) << 8 | payload[index + 1] & 0xFF; 128 index = index + 2; 129 130 // 2 bytes of profile ID. 131 int profileID = (payload[index] & 0xFF) << 8 | payload[index + 1] & 0xFF; 132 index = index + 2; 133 134 // Receive options byte. 135 int receiveOptions = payload[index] & 0xFF; 136 index = index + 1; 137 138 // Get RF data. 139 byte[] rfData = null; 140 if (index < payload.length) 141 rfData = Arrays.copyOfRange(payload, index, payload.length); 142 143 return new ExplicitRxIndicatorPacket(destAddress64, destAddress16, sourceEndpoint, destEndpoint, clusterID, profileID, receiveOptions, rfData); 144 } 145 146 /** 147 * Class constructor. Instantiates a new {@code ExplicitRxIndicatorPacket} 148 * object with the given parameters. 149 * 150 * @param sourceAddress64 64-bit address of the sender device. 151 * @param sourceAddress16 16-bit address of the sender device. 152 * @param sourceEndpoint Endpoint of the source that initiated the 153 * transmission. 154 * @param destEndpoint Endpoint of the destination the message was 155 * addressed to. 156 * @param clusterID Cluster ID the packet was addressed to. 157 * @param profileID Profile ID the packet was addressed to. 158 * @param receiveOptions BitField of receive options. 159 * @param rfData Received RF data. 160 * 161 * @throws IllegalArgumentException if {@code sourceEndpoint < 0} or 162 * if {@code sourceEndpoint > 255} or 163 * if {@code destEndpoint < 0} or 164 * if {@code destEndpoint > 255} or 165 * if {@code clusterID < 0} or 166 * if {@code clusterID > 65535} or 167 * if {@code profileID < 0} or 168 * if {@code profileID > 65535} or 169 * if {@code receiveOptions < 0} or 170 * if {@code receiveOptions > 255}. 171 * @throws NullPointerException if {@code sourceAddress64 == null} or 172 * if {@code sourceAddress16 == null}. 173 * 174 * @see com.digi.xbee.api.models.XBeeReceiveOptions 175 * @see com.digi.xbee.api.models.XBee16BitAddress 176 * @see com.digi.xbee.api.models.XBee64BitAddress 177 */ 178 public ExplicitRxIndicatorPacket(XBee64BitAddress sourceAddress64, XBee16BitAddress sourceAddress16, 179 int sourceEndpoint, int destEndpoint, int clusterID, int profileID, 180 int receiveOptions, byte[] rfData){ 181 super(APIFrameType.EXPLICIT_RX_INDICATOR); 182 183 if (sourceAddress64 == null) 184 throw new NullPointerException("64-bit source address cannot be null."); 185 if (sourceAddress16 == null) 186 throw new NullPointerException("16-bit source address cannot be null."); 187 if (sourceEndpoint < 0 || sourceEndpoint > 255) 188 throw new IllegalArgumentException("Source endpoint must be between 0 and 255."); 189 if (destEndpoint < 0 || destEndpoint > 255) 190 throw new IllegalArgumentException("Destination endpoint must be between 0 and 255."); 191 if (clusterID < 0 || clusterID > 65535) 192 throw new IllegalArgumentException("Cluster ID must be between 0 and 65535."); 193 if (profileID < 0 || profileID > 65535) 194 throw new IllegalArgumentException("Profile ID must be between 0 and 65535."); 195 if (receiveOptions < 0 || receiveOptions > 255) 196 throw new IllegalArgumentException("Receive options must be between 0 and 255."); 197 198 this.sourceAddress64 = sourceAddress64; 199 this.sourceAddress16 = sourceAddress16; 200 this.sourceEndpoint = sourceEndpoint; 201 this.destEndpoint = destEndpoint; 202 this.clusterID = clusterID; 203 this.profileID = profileID; 204 this.receiveOptions = receiveOptions; 205 this.rfData = rfData; 206 this.logger = LoggerFactory.getLogger(ExplicitRxIndicatorPacket.class); 207 } 208 209 /* 210 * (non-Javadoc) 211 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData() 212 */ 213 @Override 214 public byte[] getAPIPacketSpecificData() { 215 ByteArrayOutputStream data = new ByteArrayOutputStream(); 216 try { 217 data.write(sourceAddress64.getValue()); 218 data.write(sourceAddress16.getValue()); 219 data.write(sourceEndpoint); 220 data.write(destEndpoint); 221 data.write(clusterID >> 8); 222 data.write(clusterID); 223 data.write(profileID >> 8); 224 data.write(profileID); 225 data.write(receiveOptions); 226 if (rfData != null) 227 data.write(rfData); 228 } catch (IOException e) { 229 logger.error(e.getMessage(), e); 230 } 231 return data.toByteArray(); 232 } 233 234 /* 235 * (non-Javadoc) 236 * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID() 237 */ 238 @Override 239 public boolean needsAPIFrameID() { 240 return false; 241 } 242 243 /* 244 * (non-Javadoc) 245 * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast() 246 */ 247 @Override 248 public boolean isBroadcast() { 249 if (ByteUtils.isBitEnabled(getReceiveOptions(), 1)) 250 return true; 251 return false; 252 } 253 254 /** 255 * Returns the 64 bit sender/source address. 256 * 257 * @return The 64 bit sender/source address. 258 * 259 * @see com.digi.xbee.api.models.XBee64BitAddress 260 */ 261 public XBee64BitAddress get64BitSourceAddress() { 262 return sourceAddress64; 263 } 264 265 /** 266 * Returns the 16 bit sender/source address. 267 * 268 * @return The 16 bit sender/source address. 269 * 270 * @see com.digi.xbee.api.models.XBee16BitAddress 271 */ 272 public XBee16BitAddress get16BitSourceAddress() { 273 return sourceAddress16; 274 } 275 276 /** 277 * Returns the source endpoint of the transmission. 278 * 279 * @return The source endpoint of the transmission. 280 */ 281 public int getSourceEndpoint() { 282 return sourceEndpoint; 283 } 284 285 /** 286 * Returns the destination endpoint of the transmission. 287 * 288 * @return The destination endpoint of the transmission. 289 */ 290 public int getDestinationEndpoint() { 291 return destEndpoint; 292 } 293 294 /** 295 * Returns the cluster ID used in the transmission. 296 * 297 * @return The cluster ID used in the transmission. 298 */ 299 public int getClusterID() { 300 return clusterID; 301 } 302 303 /** 304 * Returns the profile ID used in the transmission. 305 * 306 * @return The profile ID used in the transmission. 307 */ 308 public int getProfileID() { 309 return profileID; 310 } 311 312 /** 313 * Returns the receive options bitfield. 314 * 315 * @return The receive options bitfield. 316 * 317 * @see com.digi.xbee.api.models.XBeeReceiveOptions 318 */ 319 public int getReceiveOptions() { 320 return receiveOptions; 321 } 322 323 /** 324 * Sets the received RF data. 325 * 326 * @param rfData Received RF data. 327 */ 328 public void setRFData(byte[] rfData) { 329 this.rfData = rfData; 330 } 331 332 /** 333 * Returns the received RF data. 334 * 335 * @return Received RF data. 336 */ 337 public byte[] getRFData() { 338 return rfData; 339 } 340 341 /* 342 * (non-Javadoc) 343 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 344 */ 345 @Override 346 public LinkedHashMap<String, String> getAPIPacketParameters() { 347 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 348 parameters.put("64-bit source address", HexUtils.prettyHexString(sourceAddress64.toString())); 349 parameters.put("16-bit source address", HexUtils.prettyHexString(sourceAddress16.toString())); 350 parameters.put("Source endpoint", HexUtils.prettyHexString(HexUtils.integerToHexString(sourceEndpoint, 1))); 351 parameters.put("Dest. endpoint", HexUtils.prettyHexString(HexUtils.integerToHexString(destEndpoint, 1))); 352 parameters.put("Cluster ID", HexUtils.prettyHexString(HexUtils.integerToHexString(clusterID, 2))); 353 parameters.put("Profile ID", HexUtils.prettyHexString(HexUtils.integerToHexString(profileID, 2))); 354 parameters.put("Receive options", HexUtils.prettyHexString(HexUtils.integerToHexString(receiveOptions, 1))); 355 if (rfData != null) 356 parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData))); 357 return parameters; 358 } 359}