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