001/** 002 * Copyright (c) 2014 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.XBeeAPIPacket; 025import com.digi.xbee.api.packet.APIFrameType; 026import com.digi.xbee.api.utils.HexUtils; 027 028/** 029 * This class represents a Transmit Packet. Packet is built using the parameters 030 * of the constructor or providing a valid API payload. 031 * 032 * <p>A Transmit Request API frame causes the module to send data as an RF 033 * packet to the specified destination.</p> 034 * 035 * <p>The 64-bit destination address should be set to {@code 0x000000000000FFFF} 036 * for a broadcast transmission (to all devices).</p> 037 * 038 * <p>The coordinator can be addressed by either setting the 64-bit address to 039 * all {@code 0x00} and the 16-bit address to {@code 0xFFFE}, OR by setting the 040 * 64-bit address to the coordinator's 64-bit address and the 16-bit address to 041 * {@code 0x0000}.</p> 042 * 043 * <p>For all other transmissions, setting the 16-bit address to the correct 044 * 16-bit address can help improve performance when transmitting to multiple 045 * destinations.</p> 046 * 047 * <p>If a 16-bit address is not known, this field should be set to 048 * {@code 0xFFFE} (unknown).</p> 049 * 050 * <p>The Transmit Status frame 051 * ({@link com.digi.xbee.api.packet.APIFrameType#TRANSMIT_REQUEST}) will 052 * indicate the discovered 16-bit address, if successful (see 053 * {@link com.digi.xbee.api.packet.common.TransmitStatusPacket}).</p> 054 * 055 * <p>The broadcast radius can be set from {@code 0} up to {@code NH}. If set 056 * to {@code 0}, the value of {@code NH} specifies the broadcast radius 057 * (recommended). This parameter is only used for broadcast transmissions.</p> 058 * 059 * <p>The maximum number of payload bytes can be read with the {@code NP} 060 * command.</p> 061 * 062 * <p>Several transmit options can be set using the transmit options bitfield. 063 * </p> 064 * 065 * @see com.digi.xbee.api.models.XBeeTransmitOptions 066 * @see com.digi.xbee.api.models.XBee16BitAddress#COORDINATOR_ADDRESS 067 * @see com.digi.xbee.api.models.XBee16BitAddress#UNKNOWN_ADDRESS 068 * @see com.digi.xbee.api.models.XBee64BitAddress#BROADCAST_ADDRESS 069 * @see com.digi.xbee.api.models.XBee64BitAddress#COORDINATOR_ADDRESS 070 * @see com.digi.xbee.api.packet.XBeeAPIPacket 071 */ 072public class TransmitPacket extends XBeeAPIPacket { 073 074 // Constants. 075 private static final int MIN_API_PAYLOAD_LENGTH = 14; // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (broadcast radious) + 1 (options) 076 077 // Variables. 078 private final XBee64BitAddress destAddress64; 079 080 private final XBee16BitAddress destAddress16; 081 082 private final int broadcastRadius; 083 private final int transmitOptions; 084 085 private byte[] rfData; 086 087 private Logger logger; 088 089 /** 090 * Creates a new {@code TransmitPacket} object from the given payload. 091 * 092 * @param payload The API frame payload. It must start with the frame type 093 * corresponding to a Transmit packet ({@code 0x10}). 094 * The byte array must be in {@code OperatingMode.API} mode. 095 * 096 * @return Parsed Transmit Request packet. 097 * 098 * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.TRANSMIT_REQUEST.getValue()} or 099 * if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or 100 * if {@code frameID < 0} or 101 * if {@code frameID > 255} or 102 * if {@code broadcastRadius < 0} or 103 * if {@code broadcastRadius > 255} or 104 * if {@code transmitOptions < 0} or 105 * if {@code transmitOptions > 255}. 106 * @throws NullPointerException if {@code payload == null}. 107 */ 108 public static TransmitPacket createPacket(byte[] payload) { 109 if (payload == null) 110 throw new NullPointerException("Transmit packet payload cannot be null."); 111 112 // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (broadcast radious) + 1 (options) 113 if (payload.length < MIN_API_PAYLOAD_LENGTH) 114 throw new IllegalArgumentException("Incomplete Transmit packet."); 115 116 if ((payload[0] & 0xFF) != APIFrameType.TRANSMIT_REQUEST.getValue()) 117 throw new IllegalArgumentException("Payload is not a Transmit packet."); 118 119 // payload[0] is the frame type. 120 int index = 1; 121 122 // Frame ID byte. 123 int frameID = payload[index] & 0xFF; 124 index = index + 1; 125 126 // 8 bytes of 64-bit address. 127 XBee64BitAddress destAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8)); 128 index = index + 8; 129 130 // 2 bytes of 16-bit address. 131 XBee16BitAddress destAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF); 132 index = index + 2; 133 134 // Broadcast radious byte. 135 int broadcastRadius = payload[index] & 0xFF; 136 index = index + 1; 137 138 // Options byte. 139 int options = payload[index] & 0xFF; 140 index = index + 1; 141 142 // Get RF data. 143 byte[] rfData = null; 144 if (index < payload.length) 145 rfData = Arrays.copyOfRange(payload, index, payload.length); 146 147 return new TransmitPacket(frameID, destAddress64, destAddress16, broadcastRadius, options, rfData); 148 } 149 150 /** 151 * Class constructor. Instantiates a new {@code TransmitPacket} object 152 * with the given parameters. 153 * 154 * @param frameID Frame ID. 155 * @param destAddress64 64-bit address of the destination device. 156 * @param destAddress16 16-bit address of the destination device. 157 * @param broadcastRadius maximum number of hops a broadcast transmission 158 * can occur. 159 * @param transmitOptions Bitfield of supported transmission options. 160 * @param rfData RF Data that is sent to the destination device. 161 * 162 * @throws IllegalArgumentException if {@code frameID < 0} or 163 * if {@code frameID > 255} or 164 * if {@code broadcastRadius < 0} or 165 * if {@code broadcastRadius > 255} or 166 * if {@code transmitOptions < 0} or 167 * if {@code transmitOptions > 255}. 168 * @throws NullPointerException if {@code destAddress64 == null} or 169 * if {@code destAddress16 == null}. 170 * 171 * @see com.digi.xbee.api.models.XBeeTransmitOptions 172 * @see com.digi.xbee.api.models.XBee16BitAddress 173 * @see com.digi.xbee.api.models.XBee64BitAddress 174 */ 175 public TransmitPacket(int frameID, XBee64BitAddress destAddress64, XBee16BitAddress destAddress16, 176 int broadcastRadius, int transmitOptions, byte[] rfData) { 177 super(APIFrameType.TRANSMIT_REQUEST); 178 179 if (destAddress64 == null) 180 throw new NullPointerException("64-bit destination address cannot be null."); 181 if (destAddress16 == null) 182 throw new NullPointerException("16-bit destination address cannot be null."); 183 if (frameID < 0 || frameID > 255) 184 throw new IllegalArgumentException("Frame ID must be between 0 and 255."); 185 if (broadcastRadius < 0 || broadcastRadius > 255) 186 throw new IllegalArgumentException("Broadcast radius must be between 0 and 255."); 187 if (transmitOptions < 0 || transmitOptions > 255) 188 throw new IllegalArgumentException("Transmit options must be between 0 and 255."); 189 190 this.frameID = frameID; 191 this.destAddress64 = destAddress64; 192 this.destAddress16 = destAddress16; 193 this.broadcastRadius = broadcastRadius; 194 this.transmitOptions = transmitOptions; 195 this.rfData = rfData; 196 this.logger = LoggerFactory.getLogger(TransmitPacket.class); 197 } 198 199 /* 200 * (non-Javadoc) 201 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData() 202 */ 203 @Override 204 protected byte[] getAPIPacketSpecificData() { 205 ByteArrayOutputStream data = new ByteArrayOutputStream(); 206 try { 207 data.write(destAddress64.getValue()); 208 data.write(destAddress16.getValue()); 209 data.write(broadcastRadius); 210 data.write(transmitOptions); 211 if (rfData != null) 212 data.write(rfData); 213 } catch (IOException e) { 214 logger.error(e.getMessage(), e); 215 } 216 return data.toByteArray(); 217 } 218 219 /* 220 * (non-Javadoc) 221 * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID() 222 */ 223 @Override 224 public boolean needsAPIFrameID() { 225 return true; 226 } 227 228 /* 229 * (non-Javadoc) 230 * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast() 231 */ 232 @Override 233 public boolean isBroadcast() { 234 return get64bitDestinationAddress().equals(XBee64BitAddress.BROADCAST_ADDRESS) 235 || get16bitDestinationAddress().equals(XBee16BitAddress.BROADCAST_ADDRESS); 236 } 237 238 /** 239 * Returns the 64-bit destination address. 240 * 241 * @return The 64-bit destination address. 242 * 243 * @see com.digi.xbee.api.models.XBee64BitAddress 244 */ 245 public XBee64BitAddress get64bitDestinationAddress() { 246 return destAddress64; 247 } 248 249 /** 250 * Returns the 16-bit destination address. 251 * 252 * @return The 16-bit destination address. 253 * 254 * @see com.digi.xbee.api.models.XBee16BitAddress 255 */ 256 public XBee16BitAddress get16bitDestinationAddress() { 257 return destAddress16; 258 } 259 260 /** 261 * Returns the broadcast radius. 262 * 263 * @return The broadcast radius. 264 */ 265 public int getBroadcastRadius() { 266 return broadcastRadius; 267 } 268 269 /** 270 * Returns the transmit options bitfield. 271 * 272 * @return The transmit options bitfield. 273 * 274 * @see com.digi.xbee.api.models.XBeeTransmitOptions 275 */ 276 public int getTransmitOptions() { 277 return transmitOptions; 278 } 279 280 /** 281 * Sets the RF Data to send. 282 * 283 * @param rfData RF Data to send. 284 */ 285 public void setRFData(byte[] rfData) { 286 this.rfData = rfData; 287 } 288 289 /** 290 * Returns the RF Data to send. 291 * 292 * @return RF Data to send. 293 */ 294 public byte[] getRFData() { 295 return rfData; 296 } 297 298 /* 299 * (non-Javadoc) 300 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 301 */ 302 @Override 303 public LinkedHashMap<String, String> getAPIPacketParameters() { 304 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 305 parameters.put("64-bit dest. address", HexUtils.prettyHexString(destAddress64.toString())); 306 parameters.put("16-bit dest. address", HexUtils.prettyHexString(destAddress16.toString())); 307 parameters.put("Broadcast radius", HexUtils.prettyHexString(HexUtils.integerToHexString(broadcastRadius, 1)) + " (" + broadcastRadius + ")"); 308 parameters.put("Options", HexUtils.prettyHexString(HexUtils.integerToHexString(transmitOptions, 1))); 309 if (rfData != null) 310 parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData))); 311 return parameters; 312 } 313}