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#isBroadcast()
226         */
227        @Override
228        public boolean isBroadcast() {
229                return false;
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                if (parameter != null) {
241                        if (ATStringCommands.get(command) != null)
242                                parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter)) + " (" + new String(parameter) + ")");
243                        else
244                                parameters.put("Parameter", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(parameter)));
245                }
246                return parameters;
247        }
248}