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; 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 * @return The XBee packet frame type. 095 * 096 * @see APIFrameType 097 */ 098 public APIFrameType getFrameType() { 099 return frameType; 100 } 101 102 /** 103 * Returns the XBee packet frame type integer value. 104 * 105 * @return The XBee packet frame type integer value. 106 */ 107 public int getFrameTypeValue() { 108 return frameTypeValue; 109 } 110 111 /* 112 * (non-Javadoc) 113 * @see com.digi.xbee.api.packet.XBeePacket#getPacketData() 114 */ 115 @Override 116 public byte[] getPacketData() { 117 ByteArrayOutputStream data = new ByteArrayOutputStream(); 118 119 data.write(frameTypeValue); 120 121 byte[] apiData = getAPIData(); 122 if (apiData == null) 123 apiData = new byte[0]; 124 if (apiData != null && apiData.length > 0) { 125 try { 126 data.write(apiData); 127 } catch (IOException e) { 128 logger.error(e.getMessage(), e); 129 } 130 } 131 132 return data.toByteArray(); 133 } 134 135 /** 136 * Returns the XBee API packet data. 137 * 138 * <p>This does not include the frame ID if it is needed.</p> 139 * 140 * @return The XBee API packet data. 141 */ 142 public byte[] getAPIData() { 143 ByteArrayOutputStream data = new ByteArrayOutputStream(); 144 145 byte[] apiData = getAPIPacketSpecificData(); 146 if (apiData == null) 147 apiData = new byte[0]; 148 149 if (needsAPIFrameID()) 150 data.write(frameID); 151 152 if (apiData != null && apiData.length > 0) { 153 try { 154 data.write(apiData); 155 } catch (IOException e) { 156 logger.error(e.getMessage(), e); 157 } 158 } 159 160 return data.toByteArray(); 161 } 162 163 /** 164 * Returns the XBee API packet specific data. 165 * 166 * <p>This does not include the frame ID if it is needed.</p> 167 * 168 * @return The XBee API packet data. 169 */ 170 protected abstract byte[] getAPIPacketSpecificData(); 171 172 /** 173 * Returns whether the API packet needs API Frame ID or not. 174 * 175 * @return {@code true} if the packet needs API Frame ID, {@code false} 176 * otherwise. 177 */ 178 public abstract boolean needsAPIFrameID(); 179 180 /** 181 * Returns the Frame ID of the API packet. 182 * 183 * <p>If the frame ID is not configured or if the API packet does not need 184 * a Frame ID ({@code if (!needsAPIFrameID())}) this method returns 185 * {@code NO_FRAME_ID} ({@value #NO_FRAME_ID}).</p> 186 * 187 * @return The frame ID. 188 * 189 * @see #NO_FRAME_ID 190 * @see #needsAPIFrameID() 191 * @see #setFrameID(int) 192 */ 193 public int getFrameID() { 194 if (needsAPIFrameID()) 195 return frameID; 196 return NO_FRAME_ID; 197 } 198 199 /** 200 * Sets the frame ID of the API packet. 201 * 202 * <p>If the API packet does not need a frame ID 203 * ({@code if (!needsAPIFrameID())}), this method does nothing.</p> 204 * 205 * @param frameID The frame ID to set. 206 * 207 * @throws IllegalArgumentException if {@code frameID < 0} or 208 * if {@code frameID > 255}. 209 * 210 * @see #getFrameID() 211 */ 212 public void setFrameID(int frameID) { 213 if (frameID < 0 || frameID > 255) 214 throw new IllegalArgumentException("Frame ID must be between 0 and 255."); 215 216 if (needsAPIFrameID()) 217 this.frameID = frameID; 218 } 219 220 /** 221 * Returns whether or not the packet is a broadcast packet. 222 * 223 * @return {@code true} if the packet is a broadcast packet, {@code false} 224 * otherwise. 225 */ 226 public abstract boolean isBroadcast(); 227 228 /** 229 * Returns whether the given ID is the current frame ID. 230 * 231 * @param id The frame id to check. 232 * 233 * @return {@code true} if frame ID is equal to the {@code id} provided, 234 * {@code false} otherwise or if the frame does not need an ID. 235 * 236 * @see #getFrameID() 237 * @see #needsAPIFrameID() 238 * @see #setFrameID(int) 239 */ 240 public boolean checkFrameID(int id) { 241 return needsAPIFrameID() && getFrameID() == id; 242 } 243 244 /* 245 * (non-Javadoc) 246 * @see com.digi.xbee.api.packet.XBeePacket#getPacketParameters() 247 */ 248 @Override 249 protected LinkedHashMap<String, String> getPacketParameters() { 250 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 251 if (getFrameType() != null) 252 parameters.put("Frame type", HexUtils.prettyHexString(HexUtils.integerToHexString(frameTypeValue, 1)) + " (" + getFrameType().getName() + ")"); 253 else 254 parameters.put("Frame type", HexUtils.prettyHexString(HexUtils.integerToHexString(frameTypeValue, 1))); 255 256 if (needsAPIFrameID()) { 257 if (frameID == NO_FRAME_ID) 258 parameters.put("Frame ID", "(NO FRAME ID)"); 259 else 260 parameters.put("Frame ID", HexUtils.prettyHexString(HexUtils.integerToHexString(frameID, 1)) + " (" + frameID + ")"); 261 } 262 263 LinkedHashMap<String, String> apiParams = getAPIPacketParameters(); 264 if (apiParams != null) 265 parameters.putAll(apiParams); 266 return parameters; 267 } 268 269 /** 270 * Returns a map with the XBee packet parameters and their values. 271 * 272 * @return A sorted map containing the XBee packet parameters with their 273 * values. 274 */ 275 protected abstract LinkedHashMap<String, String> getAPIPacketParameters(); 276}