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