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.ATCommandStatus; 023import com.digi.xbee.api.models.ATStringCommands; 024import com.digi.xbee.api.models.XBee16BitAddress; 025import com.digi.xbee.api.models.XBee64BitAddress; 026import com.digi.xbee.api.packet.APIFrameType; 027import com.digi.xbee.api.packet.XBeeAPIPacket; 028import com.digi.xbee.api.utils.ByteUtils; 029import com.digi.xbee.api.utils.HexUtils; 030 031/** 032 * This class represents a Remote AT Command Response packet. Packet is built 033 * using the parameters of the constructor or providing a valid API payload. 034 * 035 * <p>If a module receives a remote command response RF data frame in response 036 * to a Remote AT Command Request, the module will send a Remote AT Command 037 * Response message out the UART. Some commands may send back multiple frames-- 038 * for example, Node Discover ({@code ND}) command.</p> 039 * 040 * <p>This packet is received in response of a {@code RemoteATCommandPacket}. 041 * </p> 042 * 043 * <p>Response also includes an {@code ATCommandStatus} object with the status 044 * of the AT command.</p> 045 * 046 * @see RemoteATCommandPacket 047 * @see com.digi.xbee.api.models.ATCommandStatus 048 * @see com.digi.xbee.api.packet.XBeeAPIPacket 049 */ 050public class RemoteATCommandResponsePacket extends XBeeAPIPacket { 051 052 // Constants. 053 private static final int MIN_API_PAYLOAD_LENGTH = 15; // 1 (Frame type) + 1 (frame ID) + 8 (32-bit address) + 2 (16-bit address) + 2 (AT command) + 1 (status) 054 055 // Variables. 056 private final XBee64BitAddress sourceAddress64; 057 058 private final XBee16BitAddress sourceAddress16; 059 060 private final ATCommandStatus status; 061 062 private final String command; 063 064 private byte[] commandValue; 065 066 private Logger logger; 067 068 /** 069 * Creates an new {@code RemoteATCommandResponsePacket} object from the 070 * given payload. 071 * 072 * @param payload The API frame payload. It must start with the frame type 073 * corresponding to a Remote AT Command Response packet ({@code 0x97}). 074 * The byte array must be in {@code OperatingMode.API} mode. 075 * 076 * @return Parsed Remote AT Command Response packet. 077 * 078 * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.REMOTE_AT_COMMAND_RESPONSE.getValue()} or 079 * if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or 080 * if {@code frameID < 0} or 081 * if {@code frameID > 255}. 082 * @throws NullPointerException if {@code payload == null}. 083 */ 084 public static RemoteATCommandResponsePacket createPacket(byte[] payload) { 085 if (payload == null) 086 throw new NullPointerException("Remote AT Command Response packet payload cannot be null."); 087 088 // 1 (Frame type) + 1 (frame ID) + 8 (32-bit address) + 2 (16-bit address) + 2 (AT command) + 1 (status) 089 if (payload.length < MIN_API_PAYLOAD_LENGTH) 090 throw new IllegalArgumentException("Incomplete Remote AT Command Response packet."); 091 092 if ((payload[0] & 0xFF) != APIFrameType.REMOTE_AT_COMMAND_RESPONSE.getValue()) 093 throw new IllegalArgumentException("Payload is not a Remote AT Command Response packet."); 094 095 // payload[0] is the frame type. 096 int index = 1; 097 098 // Frame ID byte. 099 int frameID = payload[index] & 0xFF; 100 index = index + 1; 101 102 // 8 bytes of 64-bit address. 103 XBee64BitAddress sourceAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8)); 104 index = index + 8; 105 106 // 2 bytes of 16-bit address. 107 XBee16BitAddress sourceAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF); 108 index = index + 2; 109 110 // 2 bytes of AT command. 111 String command = new String(new byte[]{payload[index], payload[index + 1]}); 112 index = index + 2; 113 114 // Status byte. 115 int status = payload[index] & 0xFF; 116 index = index + 1; 117 118 // Get data. 119 byte[] commandData = null; 120 if (index < payload.length) 121 commandData = Arrays.copyOfRange(payload, index, payload.length); 122 123 // TODO if ATCommandStatus is unknown???? 124 return new RemoteATCommandResponsePacket(frameID, sourceAddress64, 125 sourceAddress16, command, ATCommandStatus.get(status), commandData); 126 } 127 128 /** 129 * Class constructor. Instantiates a new 130 * {@code RemoteATCommandResponsePacket} object with the given parameters. 131 * 132 * @param frameID frame ID. 133 * @param sourceAddress64 64-bit address of the remote radio returning 134 * response. 135 * @param sourceAddress16 16-bit network address of the remote. 136 * @param command The AT command. 137 * @param status The command status. 138 * @param commandValue The AT command response value. 139 * 140 * @throws IllegalArgumentException if {@code frameID < 0} or 141 * if {@code frameID > 255}. 142 * @throws NullPointerException if {@code sourceAddress64 == null} or 143 * if {@code sourceAddress16 == null} or 144 * if {@code command == null} or 145 * if {@code status == null}. 146 * 147 * @see com.digi.xbee.api.models.ATCommandStatus 148 * @see com.digi.xbee.api.models.XBee16BitAddress 149 * @see com.digi.xbee.api.models.XBee64BitAddress 150 */ 151 public RemoteATCommandResponsePacket(int frameID, XBee64BitAddress sourceAddress64, XBee16BitAddress sourceAddress16, 152 String command, ATCommandStatus status, byte[] commandValue) { 153 super(APIFrameType.REMOTE_AT_COMMAND_RESPONSE); 154 155 if (sourceAddress64 == null) 156 throw new NullPointerException("64-bit source address cannot be null."); 157 if (sourceAddress16 == null) 158 throw new NullPointerException("16-bit source address cannot be null."); 159 if (command == null) 160 throw new NullPointerException("AT command cannot be null."); 161 if (status == null) 162 throw new NullPointerException("AT command status cannot be null."); 163 if (frameID < 0 || frameID > 255) 164 throw new IllegalArgumentException("Frame ID must be between 0 and 255."); 165 166 this.frameID = frameID; 167 this.sourceAddress64 = sourceAddress64; 168 this.sourceAddress16 = sourceAddress16; 169 this.command = command; 170 this.status = status; 171 this.commandValue = commandValue; 172 this.logger = LoggerFactory.getLogger(RemoteATCommandResponsePacket.class); 173 } 174 175 /* 176 * (non-Javadoc) 177 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData() 178 */ 179 @Override 180 protected byte[] getAPIPacketSpecificData() { 181 ByteArrayOutputStream data = new ByteArrayOutputStream(); 182 try { 183 data.write(sourceAddress64.getValue()); 184 data.write(sourceAddress16.getValue()); 185 data.write(ByteUtils.stringToByteArray(command)); 186 data.write(status.getId()); 187 if (commandValue != null) 188 data.write(commandValue); 189 } catch (IOException e) { 190 logger.error(e.getMessage(), e); 191 } 192 return data.toByteArray(); 193 } 194 195 /* 196 * (non-Javadoc) 197 * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID() 198 */ 199 @Override 200 public boolean needsAPIFrameID() { 201 return true; 202 } 203 204 /** 205 * Returns the 64-bit source address. 206 * 207 * @return The 64-bit source address. 208 * 209 * @see com.digi.xbee.api.models.XBee64BitAddress 210 */ 211 public XBee64BitAddress get64bitSourceAddress() { 212 return sourceAddress64; 213 } 214 215 /** 216 * Returns the 16-bit source address. 217 * 218 * @return The 16-bit source address. 219 * 220 * @see com.digi.xbee.api.models.XBee16BitAddress 221 */ 222 public XBee16BitAddress get16bitSourceAddress() { 223 return sourceAddress16; 224 } 225 226 /** 227 * Returns the AT command response status. 228 * 229 * @return The AT command response status. 230 * 231 * @see com.digi.xbee.api.models.ATCommandStatus 232 */ 233 public ATCommandStatus getStatus() { 234 return status; 235 } 236 237 /** 238 * Returns the AT command. 239 * 240 * @return The AT command. 241 */ 242 public String getCommand() { 243 return command; 244 } 245 246 /** 247 * Sets the AT command response value as String. 248 * 249 * @param commandValue The AT command response value as String. 250 */ 251 public void setCommandValue(String commandValue) { 252 if (commandValue == null) 253 this.commandValue = null; 254 else 255 this.commandValue = commandValue.getBytes(); 256 } 257 258 /** 259 * Sets the AT response response value. 260 * 261 * @param commandValue The AT command response value. 262 */ 263 public void setCommandValue(byte[] commandValue) { 264 this.commandValue = commandValue; 265 } 266 267 /** 268 * Retrieves the AT command response value. 269 * 270 * @return The AT command response value. 271 */ 272 public byte[] getCommandValue() { 273 return commandValue; 274 } 275 276 /** 277 * Returns the AT command response value as String. 278 * 279 * @return The AT command response value as String, {@code null} if no 280 * value is set. 281 */ 282 public String getCommandValueAsString() { 283 if (commandValue == null) 284 return null; 285 return new String(commandValue); 286 } 287 288 /* 289 * (non-Javadoc) 290 * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast() 291 */ 292 @Override 293 public boolean isBroadcast() { 294 return false; 295 } 296 297 /* 298 * (non-Javadoc) 299 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 300 */ 301 @Override 302 public LinkedHashMap<String, String> getAPIPacketParameters() { 303 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 304 parameters.put("64-bit source address", HexUtils.prettyHexString(sourceAddress64.toString())); 305 parameters.put("16-bit source address", HexUtils.prettyHexString(sourceAddress16.toString())); 306 parameters.put("AT Command", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(command.getBytes())) + " (" + command + ")"); 307 parameters.put("Status", HexUtils.prettyHexString(HexUtils.integerToHexString(status.getId(), 1)) + " (" + status.getDescription() + ")"); 308 if (commandValue != null) { 309 if (ATStringCommands.get(command) != null) 310 parameters.put("Response", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(commandValue)) + " (" + new String(commandValue) + ")"); 311 else 312 parameters.put("Response", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(commandValue))); 313 } 314 return parameters; 315 } 316}