001/**
002 * Copyright 2017, Digi International Inc.
003 *
004 * This Source Code Form is subject to the terms of the Mozilla Public
005 * License, v. 2.0. If a copy of the MPL was not distributed with this
006 * file, you can obtain one at http://mozilla.org/MPL/2.0/.
007 *
008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
015 */
016package com.digi.xbee.api.packet;
017
018import java.io.ByteArrayOutputStream;
019import java.io.IOException;
020import java.util.Arrays;
021import java.util.LinkedHashMap;
022
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026import com.digi.xbee.api.utils.HexUtils;
027
028/**
029 * This class represents a basic and Generic XBee packet where the payload is
030 * set as a byte array without a defined structure.
031 * 
032 * @see XBeeAPIPacket
033 */
034public class GenericXBeePacket extends XBeeAPIPacket {
035        
036        // Constants.
037        private static final int MIN_API_PAYLOAD_LENGTH = 1; // 1 (Frame type)
038        
039        // Variables.
040        private byte[] rfData;
041        
042        private Logger logger;
043        
044        /**
045         * Creates a new {@code GenericXBeePacket} from the given payload.
046         * 
047         * @param payload The API frame payload. It must start with the frame type 
048         *                corresponding to a Generic packet ({@code 0xFF}).
049         *                The byte array must be in {@code OperatingMode.API} mode.
050         * 
051         * @return Parsed Generic packet.
052         * 
053         * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.GENERIC.getValue()} or
054         *                                  if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH}.
055         * @throws NullPointerException if {@code payload == null}.
056         */
057        public static GenericXBeePacket createPacket(byte[] payload) {
058                if (payload == null)
059                        throw new NullPointerException("Generic packet payload cannot be null.");
060                
061                // 1 (Frame type)
062                if (payload.length < MIN_API_PAYLOAD_LENGTH)
063                        throw new IllegalArgumentException("Incomplete Generic packet.");
064                
065                if ((payload[0] & 0xFF) != APIFrameType.GENERIC.getValue())
066                        throw new IllegalArgumentException("Payload is not a Generic packet.");
067                
068                // payload[0] is the frame type.
069                int index = 1;
070                
071                byte[] commandData = null;
072                if (index < payload.length)
073                        commandData = Arrays.copyOfRange(payload, index, payload.length);
074                
075                return new GenericXBeePacket(commandData);
076        }
077        
078        /**
079         * Class constructor. Instantiates an XBee packet with the given packet 
080         * data.
081         * 
082         * @param rfData The XBee RF Data.
083         */
084        public GenericXBeePacket(byte[] rfData) {
085                super(APIFrameType.GENERIC);
086                this.rfData = rfData;
087                this.logger = LoggerFactory.getLogger(GenericXBeePacket.class);
088        }
089        
090        /*
091         * (non-Javadoc)
092         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData()
093         */
094        @Override
095        protected byte[] getAPIPacketSpecificData() {
096                ByteArrayOutputStream data = new ByteArrayOutputStream();
097                try {
098                        if (rfData != null)
099                                data.write(rfData);
100                } catch (IOException e) {
101                        logger.error(e.getMessage(), e);
102                }
103                return data.toByteArray();
104        }
105        
106        /*
107         * (non-Javadoc)
108         * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID()
109         */
110        @Override
111        public boolean needsAPIFrameID() {
112                return false;
113        }
114        
115        /**
116         * Sets the XBee RF Data.
117         * 
118         * @param rfData The new XBee RF Data.
119         */
120        public void setRFData(byte[] rfData) {
121                if (rfData == null)
122                        this.rfData = null;
123                else
124                        this.rfData = Arrays.copyOf(rfData, rfData.length);
125        }
126        
127        /**
128         * Returns the XBee RF Data of the packet.
129         * 
130         * @return The RF Data.
131         */
132        public byte[] getRFData() {
133                if (rfData == null)
134                        return null;
135                return Arrays.copyOf(rfData, rfData.length);
136        }
137        
138        /*
139         * (non-Javadoc)
140         * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast()
141         */
142        @Override
143        public boolean isBroadcast() {
144                return false;
145        }
146        
147        /*
148         * (non-Javadoc)
149         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters()
150         */
151        @Override
152        protected LinkedHashMap<String, String> getAPIPacketParameters() {
153                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
154                if (rfData != null)
155                        parameters.put("RF Data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData)));
156                return parameters;
157        }
158}