    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                if (get64bitDestinationAddress().equals(XBee64BitAddress.BROADCAST_ADDRESS)
    257                                || get16bitDestinationAddress().equals(XBee16BitAddress.BROADCAST_ADDRESS))
    258                        return true;
    259                return false;
    260        }
    261
    262        /**
    263         * Returns the 64 bit destination address.
    264         *
    265         * @return The 64 bit destination address.
    266         *
    267         * @see com.digi.xbee.api.models.XBee64BitAddress
    268         */
    269        public XBee64BitAddress get64bitDestinationAddress() {
    270                return destAddress64;
    271        }
    272
    273        /**
    274         * Returns the 16 bit destination address.
    275         *
    276         * @return The 16 bit destination address.
    277         *
    278         * @see com.digi.xbee.api.models.XBee16BitAddress
    279         */
    280        public XBee16BitAddress get16bitDestinationAddress() {
    281                return destAddress16;
    282        }
    283
    284        /**
    285         * Returns the transmit options bitfield.
    286         *
    287         * @return Transmit options bitfield.
    288         *
    289         * @see com.digi.xbee.api.models.XBeeTransmitOptions
    290         */
    291        public int getTransmitOptions() {
    292                return transmitOptions;
    293        }
    294
    295        /**
    296         * Returns the AT Command.
    297         *
    298         * @return The AT Command.
    299         */
    300        public String getCommand() {
    301                return command;
    302        }
    303
    304        /**
    305         * Sets the AT command parameter as String.
    306         *
    307         * @param parameter The AT command parameter as String.
    308         */
    309        public void setParameter(String parameter) {
    310                if (parameter == null)
    311                        this.parameter = null;
    312                else
    313                        this.parameter = parameter.getBytes();
    314        }
    315
    316        /**
    317         * Sets the AT command parameter.
    318         *
    319         * @param parameter The AT command parameter.
    320         */
    321        public void setParameter(byte[] parameter) {
    322                this.parameter = parameter;
    323        }
    324
    325        /**
    326         * Returns the AT command parameter.
    327         *
    328         * @return The AT command parameter.
    329         */
    330        public byte[] getParameter() {
    331                return parameter;
    332        }
    333
    334        /**
    335         * Returns the AT command parameter as String.
    336         *
    337         * @return The AT command parameter as String, {@code null} if not
    338         *         parameter is set.
    339         */
    340        public String getParameterAsString() {
    341                if (parameter == null)
    342                        return null;
    343                return new String(parameter);
    344        }
    345
    346        /*
    347         * (non-Javadoc)
    348         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters()
    349         */
    350        @Override
    351        public LinkedHashMap<String, String> getAPIPacketParameters() {
    352                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
    353                parameters.put("64-bit dest. address", HexUtils.prettyHexString(destAddress64.toString()));
    354                parameters.put("16-bit dest. address", HexUtils.prettyHexString(destAddress16.toString()));
    355                parameters.put("Command options", HexUtils.prettyHexString(HexUtils.integerToHexString(transmitOptions, 1)));
    356                parameters.put("AT Command", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(command.getBytes())) + " (" + command + ")");
    357                if (parameter != null) {
    358                        if (ATStringCommands.get(command) != null)
    359                                parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter)) + " (" + new String(parameter) + ")");
    360                        else
    361                                parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter)));
    362                }
    363                return parameters;
    364        }
    365}