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