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