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