    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#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                parameters.put("Status", HexUtils.prettyHexString(HexUtils.integerToHexString(status.getId(), 1)) + " (" + status.getDescription() + ")");
    241                if (commandValue != null) {
    242                        if (ATStringCommands.get(command) != null)
    243                                parameters.put("Response", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(commandValue)) + " (" + new String(commandValue) + ")");
    244                        else
    245                                parameters.put("Response", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(commandValue)));
    246                }
    247                return parameters;
    248        }
    249}