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.ByteUtils; 027import com.digi.xbee.api.utils.HexUtils; 028 029/** 030 * This class represents a Receive Packet. Packet is built using the parameters 031 * of the constructor or providing a valid API payload. 032 * 033 * <p>When the module receives an RF packet, it is sent out the UART using this 034 * message type.</p> 035 * 036 * <p>This packet is received when external devices send transmit request 037 * packets to this module.</p> 038 * 039 * <p>Among received data, some options can also be received indicating 040 * transmission parameters.</p> 041 * 042 * @see TransmitPacket 043 * @see com.digi.xbee.api.models.XBeeReceiveOptions 044 * @see com.digi.xbee.api.packet.XBeeAPIPacket 045 */ 046public class ReceivePacket extends XBeeAPIPacket { 047 048 // Constants. 049 private static final int MIN_API_PAYLOAD_LENGTH = 12; // 1 (Frame type) + 8 (32-bit address) + 2 (16-bit address) + 1 (receive options) 050 051 // Variables. 052 private final XBee64BitAddress sourceAddress64; 053 054 private final XBee16BitAddress sourceAddress16; 055 056 private final int receiveOptions; 057 058 private byte[] rfData; 059 060 private Logger logger; 061 062 /** 063 * Creates a new {@code ReceivePacket} object from the given payload. 064 * 065 * @param payload The API frame payload. It must start with the frame type 066 * corresponding to a Receive packet ({@code 0x90}). 067 * The byte array must be in {@code OperatingMode.API} mode. 068 * 069 * @return Parsed ZigBee Receive packet. 070 * 071 * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.RECEIVE_PACKET.getValue()} or 072 * if {@code payload.length < {@value #MIN_API_PAYLOAD_LENGTH}} or 073 * if {@code receiveOptions < 0} or 074 * if {@code receiveOptions > 255}. 075 * @throws NullPointerException if {@code payload == null}. 076 */ 077 public static ReceivePacket createPacket(byte[] payload) { 078 if (payload == null) 079 throw new NullPointerException("Receive packet payload cannot be null."); 080 081 // 1 (Frame type) + 8 (32-bit address) + 2 (16-bit address) + 1 (receive options) 082 if (payload.length < MIN_API_PAYLOAD_LENGTH) 083 throw new IllegalArgumentException("Incomplete Receive packet."); 084 085 if ((payload[0] & 0xFF) != APIFrameType.RECEIVE_PACKET.getValue()) 086 throw new IllegalArgumentException("Payload is not a Receive packet."); 087 088 // payload[0] is the frame type. 089 int index = 1; 090 091 // 2 bytes of 16-bit address. 092 XBee64BitAddress sourceAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8)); 093 index = index + 8; 094 095 // 2 bytes of 16-bit address. 096 XBee16BitAddress sourceAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF); 097 index = index + 2; 098 099 // Receive options 100 int receiveOptions = payload[index] & 0xFF; 101 index = index + 1; 102 103 // Get data. 104 byte[] data = null; 105 if (index < payload.length) 106 data = Arrays.copyOfRange(payload, index, payload.length); 107 108 return new ReceivePacket(sourceAddress64, sourceAddress16, receiveOptions, data); 109 } 110 111 /** 112 * Class constructor. Instantiates a new {@code ReceivePacket} object 113 * with the given parameters. 114 * 115 * @param sourceAddress64 64-bit address of the sender. 116 * @param sourceAddress16 16-bit address of the sender. 117 * @param receiveOptions Bitfield indicating the receive options. 118 * @param rfData Received RF data. 119 * 120 * @throws IllegalArgumentException if {@code receiveOptions < 0} or 121 * if {@code receiveOptions > 255}. 122 * @throws NullPointerException if {@code sourceAddress64 == null} or 123 * if {@code sourceAddress16 == null}. 124 * 125 * @see com.digi.xbee.api.models.XBeeReceiveOptions 126 * @see com.digi.xbee.api.models.XBee16BitAddress 127 * @see com.digi.xbee.api.models.XBee64BitAddress 128 */ 129 public ReceivePacket(XBee64BitAddress sourceAddress64, XBee16BitAddress sourceAddress16, int receiveOptions, byte[] rfData){ 130 super(APIFrameType.RECEIVE_PACKET); 131 132 if (sourceAddress64 == null) 133 throw new NullPointerException("64-bit source address cannot be null."); 134 if (sourceAddress16 == null) 135 throw new NullPointerException("16-bit source address cannot be null."); 136 if (receiveOptions < 0 || receiveOptions > 255) 137 throw new IllegalArgumentException("Receive options value must be between 0 and 255."); 138 139 this.sourceAddress64 = sourceAddress64; 140 this.sourceAddress16 = sourceAddress16; 141 this.receiveOptions = receiveOptions; 142 this.rfData = rfData; 143 this.logger = LoggerFactory.getLogger(ReceivePacket.class); 144 } 145 146 /* 147 * (non-Javadoc) 148 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData() 149 */ 150 @Override 151 protected byte[] getAPIPacketSpecificData() { 152 ByteArrayOutputStream data = new ByteArrayOutputStream(); 153 try { 154 data.write(sourceAddress64.getValue()); 155 data.write(sourceAddress16.getValue()); 156 data.write(receiveOptions); 157 if (rfData != null) 158 data.write(rfData); 159 } catch (IOException e) { 160 logger.error(e.getMessage(), e); 161 } 162 return data.toByteArray(); 163 } 164 165 /* 166 * (non-Javadoc) 167 * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID() 168 */ 169 @Override 170 public boolean needsAPIFrameID() { 171 return false; 172 } 173 174 /* 175 * (non-Javadoc) 176 * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast() 177 */ 178 @Override 179 public boolean isBroadcast() { 180 return ByteUtils.isBitEnabled(getReceiveOptions(), 1); 181 } 182 183 /** 184 * Returns the 64-bit sender/source address. 185 * 186 * @return The 64-bit sender/source address. 187 * 188 * @see com.digi.xbee.api.models.XBee64BitAddress 189 */ 190 public XBee64BitAddress get64bitSourceAddress() { 191 return sourceAddress64; 192 } 193 194 /** 195 * Returns the 16-bit sender/source address. 196 * 197 * @return The 16-bit sender/source address. 198 * 199 * @see com.digi.xbee.api.models.XBee16BitAddress 200 */ 201 public XBee16BitAddress get16bitSourceAddress() { 202 return sourceAddress16; 203 } 204 205 /** 206 * Returns the receive options bitfield. 207 * 208 * @return Receive options bitfield. 209 * 210 * @see com.digi.xbee.api.models.XBeeReceiveOptions 211 */ 212 public int getReceiveOptions() { 213 return receiveOptions; 214 } 215 216 /** 217 * Sets the received RF data. 218 * 219 * @param rfData Received RF data. 220 */ 221 public void setRFData(byte[] rfData) { 222 this.rfData = rfData; 223 } 224 225 /** 226 * Returns the received RF data. 227 * 228 * @return Received RF data. 229 */ 230 public byte[] getRFData() { 231 return rfData; 232 } 233 234 /* 235 * (non-Javadoc) 236 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 237 */ 238 @Override 239 public LinkedHashMap<String, String> getAPIPacketParameters() { 240 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 241 parameters.put("64-bit source address", HexUtils.prettyHexString(sourceAddress64.toString())); 242 parameters.put("16-bit source address", HexUtils.prettyHexString(sourceAddress16.toString())); 243 parameters.put("Receive options", HexUtils.prettyHexString(HexUtils.integerToHexString(receiveOptions, 1))); 244 if (rfData != null) 245 parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData))); 246 return parameters; 247 } 248}