    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#getAPIPacketParameters()
    226         */
    227        @Override
    228        public LinkedHashMap<String, String> getAPIPacketParameters() {
    229                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
    230                parameters.put("AT Command", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(command.getBytes())) + " (" + command + ")");
    231                if (parameter != null) {
    232                        if (ATStringCommands.get(command) != null)
    233                                parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter)) + " (" + new String(parameter) + ")");
    234                        else
    235                                parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter)));
    236                }
    237                return parameters;
    238        }
    239}