001/** 002 * Copyright 2017, Digi International Inc. 003 * 004 * This Source Code Form is subject to the terms of the Mozilla Public 005 * License, v. 2.0. If a copy of the MPL was not distributed with this 006 * file, you can obtain one at http://mozilla.org/MPL/2.0/. 007 * 008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 015 */ 016package com.digi.xbee.api.packet; 017 018import java.io.ByteArrayOutputStream; 019import java.io.IOException; 020import java.util.LinkedHashMap; 021 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025import com.digi.xbee.api.utils.HexUtils; 026 027/** 028 * This abstract class provides the basic structure of a ZigBee API frame. 029 * 030 * <p>Derived classes should implement their own methods to generate the API 031 * data and frame ID in case they support it.</p> 032 * 033 * <p>Basic operations such as frame type retrieval are performed in this class. 034 * </p> 035 * 036 * @see XBeePacket 037 */ 038public abstract class XBeeAPIPacket extends XBeePacket { 039 040 // Constants. 041 public final static int NO_FRAME_ID = 9999; 042 043 // Variables. 044 protected int frameID = NO_FRAME_ID; 045 046 private APIFrameType frameType = null; 047 048 private int frameTypeValue; 049 050 private Logger logger; 051 052 /** 053 * Class constructor. Instantiates a new {@code XBeeAPIPacket} object with 054 * the given API frame type. 055 * 056 * @param frameType XBee packet frame type. 057 * 058 * @throws NullPointerException if {@code frameType == null}. 059 * 060 * @see APIFrameType 061 */ 062 protected XBeeAPIPacket(APIFrameType frameType) { 063 super(); 064 065 if (frameType == null) 066 throw new NullPointerException("Frame type cannot be null."); 067 068 this.frameType = frameType; 069 frameTypeValue = frameType.getValue(); 070 071 this.logger = LoggerFactory.getLogger(XBeeAPIPacket.class); 072 } 073 074 /** 075 * Class constructor. Instantiates a new {@code XBeeAPIPacket} object with 076 * the given frame type value. 077 * 078 * @param frameTypeValue XBee packet frame type integer value. 079 * 080 * @throws IllegalArgumentException if {@code frameTypeValue < 0} or 081 * if {@code frameTypeValue > 255}. 082 */ 083 protected XBeeAPIPacket(int frameTypeValue) { 084 super(); 085 086 if (frameTypeValue < 0 || frameTypeValue > 255) 087 throw new IllegalArgumentException("Frame type value must be between 0 and 255."); 088 089 this.frameTypeValue = frameTypeValue; 090 this.frameType = APIFrameType.get(frameTypeValue); 091 092 this.logger = LoggerFactory.getLogger(XBeeAPIPacket.class); 093 } 094 095 /** 096 * Returns the XBee packet frame type. 097 * 098 * If {@code APIFrameType#UNKNOWN} is returned, the real value of the frame 099 * type is returned by {@code #getFrameTypeValue()}. 100 * 101 * @return The XBee packet frame type. 102 * 103 * @see APIFrameType 104 */ 105 public APIFrameType getFrameType() { 106 return frameType; 107 } 108 109 /** 110 * Returns the XBee packet frame type integer value. 111 * 112 * @return The XBee packet frame type integer value. 113 */ 114 public int getFrameTypeValue() { 115 return frameTypeValue; 116 } 117 118 /* 119 * (non-Javadoc) 120 * @see com.digi.xbee.api.packet.XBeePacket#getPacketData() 121 */ 122 @Override 123 public byte[] getPacketData() { 124 ByteArrayOutputStream data = new ByteArrayOutputStream(); 125 126 data.write(frameTypeValue); 127 128 byte[] apiData = getAPIData(); 129 if (apiData == null) 130 apiData = new byte[0]; 131 if (apiData != null && apiData.length > 0) { 132 try { 133 data.write(apiData); 134 } catch (IOException e) { 135 logger.error(e.getMessage(), e); 136 } 137 } 138 139 return data.toByteArray(); 140 } 141 142 /** 143 * Returns the XBee API packet data. 144 * 145 * <p>This does not include the frame ID if it is needed.</p> 146 * 147 * @return The XBee API packet data. 148 */ 149 public byte[] getAPIData() { 150 ByteArrayOutputStream data = new ByteArrayOutputStream(); 151 152 byte[] apiData = getAPIPacketSpecificData(); 153 if (apiData == null) 154 apiData = new byte[0]; 155 156 if (needsAPIFrameID()) 157 data.write(frameID); 158 159 if (apiData != null && apiData.length > 0) { 160 try { 161 data.write(apiData); 162 } catch (IOException e) { 163 logger.error(e.getMessage(), e); 164 } 165 } 166 167 return data.toByteArray(); 168 } 169 170 /** 171 * Returns the XBee API packet specific data. 172 * 173 * <p>This does not include the frame ID if it is needed.</p> 174 * 175 * @return The XBee API packet data. 176 */ 177 protected abstract byte[] getAPIPacketSpecificData(); 178 179 /** 180 * Returns whether the API packet needs API Frame ID or not. 181 * 182 * @return {@code true} if the packet needs API Frame ID, {@code false} 183 * otherwise. 184 */ 185 public abstract boolean needsAPIFrameID(); 186 187 /** 188 * Returns the Frame ID of the API packet. 189 * 190 * <p>If the frame ID is not configured or if the API packet does not need 191 * a Frame ID ({@code if (!needsAPIFrameID())}) this method returns 192 * {@code NO_FRAME_ID} ({@value #NO_FRAME_ID}).</p> 193 * 194 * @return The frame ID. 195 * 196 * @see #NO_FRAME_ID 197 * @see #needsAPIFrameID() 198 * @see #setFrameID(int) 199 */ 200 public int getFrameID() { 201 if (needsAPIFrameID()) 202 return frameID; 203 return NO_FRAME_ID; 204 } 205 206 /** 207 * Sets the frame ID of the API packet. 208 * 209 * <p>If the API packet does not need a frame ID 210 * ({@code if (!needsAPIFrameID())}), this method does nothing.</p> 211 * 212 * @param frameID The frame ID to set. 213 * 214 * @throws IllegalArgumentException if {@code frameID < 0} or 215 * if {@code frameID > 255}. 216 * 217 * @see #getFrameID() 218 */ 219 public void setFrameID(int frameID) { 220 if (frameID < 0 || frameID > 255) 221 throw new IllegalArgumentException("Frame ID must be between 0 and 255."); 222 223 if (needsAPIFrameID()) 224 this.frameID = frameID; 225 } 226 227 /** 228 * Returns whether or not the packet is a broadcast packet. 229 * 230 * @return {@code true} if the packet is a broadcast packet, {@code false} 231 * otherwise. 232 */ 233 public abstract boolean isBroadcast(); 234 235 /** 236 * Returns whether the given ID is the current frame ID. 237 * 238 * @param id The frame id to check. 239 * 240 * @return {@code true} if frame ID is equal to the {@code id} provided, 241 * {@code false} otherwise or if the frame does not need an ID. 242 * 243 * @see #getFrameID() 244 * @see #needsAPIFrameID() 245 * @see #setFrameID(int) 246 */ 247 public boolean checkFrameID(int id) { 248 return needsAPIFrameID() && getFrameID() == id; 249 } 250 251 /* 252 * (non-Javadoc) 253 * @see com.digi.xbee.api.packet.XBeePacket#getPacketParameters() 254 */ 255 @Override 256 protected LinkedHashMap<String, String> getPacketParameters() { 257 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 258 if (getFrameType() != null) 259 parameters.put("Frame type", HexUtils.prettyHexString(HexUtils.integerToHexString(frameTypeValue, 1)) + " (" + getFrameType().getName() + ")"); 260 else 261 parameters.put("Frame type", HexUtils.prettyHexString(HexUtils.integerToHexString(frameTypeValue, 1))); 262 263 if (needsAPIFrameID()) { 264 if (frameID == NO_FRAME_ID) 265 parameters.put("Frame ID", "(NO FRAME ID)"); 266 else 267 parameters.put("Frame ID", HexUtils.prettyHexString(HexUtils.integerToHexString(frameID, 1)) + " (" + frameID + ")"); 268 } 269 270 LinkedHashMap<String, String> apiParams = getAPIPacketParameters(); 271 if (apiParams != null) 272 parameters.putAll(apiParams); 273 return parameters; 274 } 275 276 /** 277 * Returns a map with the XBee packet parameters and their values. 278 * 279 * @return A sorted map containing the XBee packet parameters with their 280 * values. 281 */ 282 protected abstract LinkedHashMap<String, String> getAPIPacketParameters(); 283}