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.models.XBee16BitAddress; 024import com.digi.xbee.api.models.XBee64BitAddress; 025import com.digi.xbee.api.packet.APIFrameType; 026import com.digi.xbee.api.packet.XBeeAPIPacket; 027import com.digi.xbee.api.utils.ByteUtils; 028import com.digi.xbee.api.utils.HexUtils; 029 030/** 031 * This class represents a Remote AT Command Request packet. Packet is built 032 * using the parameters of the constructor or providing a valid API payload. 033 * 034 * <p>Used to query or set module parameters on a remote device. For parameter 035 * changes on the remote device to take effect, changes must be applied, either 036 * by setting the apply changes options bit, or by sending an {@code AC} command 037 * to the remote node.</p> 038 * 039 * <p>Remote Command options are set as a bitfield.</p> 040 * 041 * <p>If configured, command response is received as a 042 * {@code RemoteATCommandResponse packet}.</p> 043 * 044 * @see RemoteATCommandResponsePacket 045 * @see com.digi.xbee.api.packet.XBeeAPIPacket 046 */ 047public class RemoteATCommandPacket extends XBeeAPIPacket { 048 049 // Constants. 050 private static final int MIN_API_PAYLOAD_LENGTH = 15; // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (transmit options byte) + 2 (AT command) 051 052 // Variables. 053 private final XBee64BitAddress destAddress64; 054 055 private final XBee16BitAddress destAddress16; 056 057 private final int transmitOptions; 058 059 private final String command; 060 061 private byte[] parameter; 062 063 private Logger logger; 064 065 /** 066 * Creates a new {@code RemoteATCommandPacket} object from the given 067 * payload. 068 * 069 * @param payload The API frame payload. It must start with the frame type 070 * corresponding to a Remote AT Command packet ({@code 0x17}). 071 * The byte array must be in {@code OperatingMode.API} mode. 072 * 073 * @return Parsed Remote AT Command Request packet. 074 * 075 * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.REMOTE_AT_COMMAND_REQUEST.getValue()} or 076 * if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or 077 * if {@code frameID < 0} or 078 * if {@code frameID > 255} or 079 * if {@code transmitOptions < 0} or 080 * if {@code transmitOptions > 255}. 081 * @throws NullPointerException if {@code payload == null}. 082 */ 083 public static RemoteATCommandPacket createPacket(byte[] payload) { 084 if (payload == null) 085 throw new NullPointerException("Remote AT Command packet payload cannot be null."); 086 087 // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (transmit options byte) + 2 (AT command) 088 if (payload.length < MIN_API_PAYLOAD_LENGTH) 089 throw new IllegalArgumentException("Incomplete Remote AT Command packet."); 090 091 if ((payload[0] & 0xFF) != APIFrameType.REMOTE_AT_COMMAND_REQUEST.getValue()) 092 throw new IllegalArgumentException("Payload is not a Remote AT Command packet."); 093 094 // payload[0] is the frame type. 095 int index = 1; 096 097 // Frame ID byte. 098 int frameID = payload[index] & 0xFF; 099 index = index + 1; 100 101 // 8 bytes of 64-bit address. 102 XBee64BitAddress destAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8)); 103 index = index + 8; 104 105 // 2 bytes of 16-bit address. 106 XBee16BitAddress destAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF); 107 index = index + 2; 108 109 // Options byte. 110 int transmitOptions = payload[index] & 0xFF; 111 index = index + 1; 112 113 // 2 bytes of AT command. 114 String command = new String(new byte[]{payload[index], payload[index + 1]}); 115 index = index + 2; 116 117 // Get data. 118 byte[] parameterData = null; 119 if (index < payload.length) 120 parameterData = Arrays.copyOfRange(payload, index, payload.length); 121 122 return new RemoteATCommandPacket(frameID, destAddress64, destAddress16, transmitOptions, 123 command, parameterData); 124 } 125 126 /** 127 * Class constructor. Instantiates a new {@code RemoteATCommandRequest} 128 * object with the given parameters. 129 * 130 * @param frameID The Frame ID. 131 * @param destAddress64 64-bit address of the destination device. 132 * @param destAddress16 16-bit address of the destination device. 133 * @param transmitOptions Bitfield of supported transmission options. 134 * @param command AT command. 135 * @param parameter AT command parameter as String. 136 * 137 * @throws IllegalArgumentException if {@code frameID < 0} or 138 * if {@code frameID > 255} or 139 * if {@code transmitOptions < 0} or 140 * if {@code transmitOptions > 255}. 141 * @throws NullPointerException if {@code destAddress64 == null} or 142 * if {@code destAddress16 == null} or 143 * if {@code command == null}. 144 * 145 * @see com.digi.xbee.api.models.XBeeTransmitOptions 146 * @see com.digi.xbee.api.models.XBee16BitAddress 147 * @see com.digi.xbee.api.models.XBee64BitAddress 148 */ 149 public RemoteATCommandPacket(int frameID, XBee64BitAddress destAddress64, XBee16BitAddress destAddress16, 150 int transmitOptions, String command, String parameter) { 151 super(APIFrameType.REMOTE_AT_COMMAND_REQUEST); 152 153 if (destAddress64 == null) 154 throw new NullPointerException("64-bit destination address cannot be null."); 155 if (destAddress16 == null) 156 throw new NullPointerException("16-bit destination address cannot be null."); 157 if (command == null) 158 throw new NullPointerException("AT command cannot be null."); 159 if (frameID < 0 || frameID > 255) 160 throw new IllegalArgumentException("Frame ID must be between 0 and 255."); 161 if (transmitOptions < 0 || transmitOptions > 255) 162 throw new IllegalArgumentException("Options value must be between 0 and 255."); 163 164 this.frameID = frameID; 165 this.destAddress64 = destAddress64; 166 this.destAddress16 = destAddress16; 167 this.transmitOptions = transmitOptions; 168 this.command = command; 169 if (parameter != null) 170 this.parameter = parameter.getBytes(); 171 this.logger = LoggerFactory.getLogger(RemoteATCommandPacket.class); 172 } 173 174 /** 175 * Class constructor. Instantiates a new {@code RemoteATCommandRequest} 176 * object with the given parameters. 177 * 178 * @param frameID Frame ID. 179 * @param destAddress64 64-bit address of the destination device. 180 * @param destAddress16 16-bit address of the destination device. 181 * @param transmitOptions Bitfield of supported transmission options. 182 * @param command AT command. 183 * @param parameter AT command parameter. 184 * 185 * @throws IllegalArgumentException if {@code frameID < 0} or 186 * if {@code frameID > 255} or 187 * if {@code transmitOptions < 0} or 188 * if {@code transmitOptions > 255}. 189 * @throws NullPointerException if {@code destAddress64 == null} or 190 * if {@code destAddress16 == null} or 191 * if {@code command == null}. 192 * 193 * @see com.digi.xbee.api.models.XBeeTransmitOptions 194 * @see com.digi.xbee.api.models.XBee16BitAddress 195 * @see com.digi.xbee.api.models.XBee64BitAddress 196 */ 197 public RemoteATCommandPacket(int frameID, XBee64BitAddress destAddress64, XBee16BitAddress destAddress16, 198 int transmitOptions, String command, byte[] parameter) { 199 super(APIFrameType.REMOTE_AT_COMMAND_REQUEST); 200 201 if (destAddress64 == null) 202 throw new NullPointerException("64-bit destination address cannot be null."); 203 if (destAddress16 == null) 204 throw new NullPointerException("16-bit destination address cannot be null."); 205 if (command == null) 206 throw new NullPointerException("AT command cannot be null."); 207 if (frameID < 0 || frameID > 255) 208 throw new IllegalArgumentException("Frame ID must be between 0 and 255."); 209 if (transmitOptions < 0 || transmitOptions > 255) 210 throw new IllegalArgumentException("Options value must be between 0 and 255."); 211 212 this.frameID = frameID; 213 this.destAddress64 = destAddress64; 214 this.destAddress16 = destAddress16; 215 this.transmitOptions = transmitOptions; 216 this.command = command; 217 this.parameter = parameter; 218 this.logger = LoggerFactory.getLogger(RemoteATCommandPacket.class); 219 } 220 221 /* 222 * (non-Javadoc) 223 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData() 224 */ 225 @Override 226 protected byte[] getAPIPacketSpecificData() { 227 ByteArrayOutputStream data = new ByteArrayOutputStream(); 228 try { 229 data.write(destAddress64.getValue()); 230 data.write(destAddress16.getValue()); 231 data.write(transmitOptions); 232 data.write(ByteUtils.stringToByteArray(command)); 233 if (parameter != null) 234 data.write(parameter); 235 } catch (IOException e) { 236 logger.error(e.getMessage(), e); 237 } 238 return data.toByteArray(); 239 } 240 241 /* 242 * (non-Javadoc) 243 * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID() 244 */ 245 @Override 246 public boolean needsAPIFrameID() { 247 return true; 248 } 249 250 /* 251 * (non-Javadoc) 252 * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast() 253 */ 254 @Override 255 public boolean isBroadcast() { 256 return get64bitDestinationAddress().equals(XBee64BitAddress.BROADCAST_ADDRESS) 257 || get16bitDestinationAddress().equals(XBee16BitAddress.BROADCAST_ADDRESS); 258 } 259 260 /** 261 * Returns the 64 bit destination address. 262 * 263 * @return The 64 bit destination address. 264 * 265 * @see com.digi.xbee.api.models.XBee64BitAddress 266 */ 267 public XBee64BitAddress get64bitDestinationAddress() { 268 return destAddress64; 269 } 270 271 /** 272 * Returns the 16 bit destination address. 273 * 274 * @return The 16 bit destination address. 275 * 276 * @see com.digi.xbee.api.models.XBee16BitAddress 277 */ 278 public XBee16BitAddress get16bitDestinationAddress() { 279 return destAddress16; 280 } 281 282 /** 283 * Returns the transmit options bitfield. 284 * 285 * @return Transmit options bitfield. 286 * 287 * @see com.digi.xbee.api.models.XBeeTransmitOptions 288 */ 289 public int getTransmitOptions() { 290 return transmitOptions; 291 } 292 293 /** 294 * Returns the AT Command. 295 * 296 * @return The AT Command. 297 */ 298 public String getCommand() { 299 return command; 300 } 301 302 /** 303 * Sets the AT command parameter as String. 304 * 305 * @param parameter The AT command parameter as String. 306 */ 307 public void setParameter(String parameter) { 308 if (parameter == null) 309 this.parameter = null; 310 else 311 this.parameter = parameter.getBytes(); 312 } 313 314 /** 315 * Sets the AT command parameter. 316 * 317 * @param parameter The AT command parameter. 318 */ 319 public void setParameter(byte[] parameter) { 320 this.parameter = parameter; 321 } 322 323 /** 324 * Returns the AT command parameter. 325 * 326 * @return The AT command parameter. 327 */ 328 public byte[] getParameter() { 329 return parameter; 330 } 331 332 /** 333 * Returns the AT command parameter as String. 334 * 335 * @return The AT command parameter as String, {@code null} if not 336 * parameter is set. 337 */ 338 public String getParameterAsString() { 339 if (parameter == null) 340 return null; 341 return new String(parameter); 342 } 343 344 /* 345 * (non-Javadoc) 346 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 347 */ 348 @Override 349 public LinkedHashMap<String, String> getAPIPacketParameters() { 350 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 351 parameters.put("64-bit dest. address", HexUtils.prettyHexString(destAddress64.toString())); 352 parameters.put("16-bit dest. address", HexUtils.prettyHexString(destAddress16.toString())); 353 parameters.put("Command options", HexUtils.prettyHexString(HexUtils.integerToHexString(transmitOptions, 1))); 354 parameters.put("AT Command", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(command.getBytes())) + " (" + command + ")"); 355 if (parameter != null) { 356 if (ATStringCommands.get(command) != null) 357 parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter)) + " (" + new String(parameter) + ")"); 358 else 359 parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter))); 360 } 361 return parameters; 362 } 363}