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