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.raw;
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.XBee16BitAddress;
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 a TX (Transmit) 16 Request packet. Packet is built 
029 * using the parameters of the constructor or providing a valid API payload.
030 * 
031 * <p>A TX Request message will cause the module to transmit data as an RF 
032 * Packet.</p>
033 * 
034 * @see com.digi.xbee.api.packet.XBeeAPIPacket
035 */
036public class TX16Packet extends XBeeAPIPacket {
037
038        // Constants.
039        private static final int MIN_API_PAYLOAD_LENGTH = 5; // 1 (Frame type) + 1 (frame ID) + 2 (address) + 1 (transmit options)
040        
041        // Variables.
042        private final int transmitOptions;
043        
044        private final XBee16BitAddress destAddress16;
045        
046        private byte[] rfData;
047        
048        private Logger logger;
049        
050        /**
051         * Creates a new {@code TX16Packet} object from the given payload.
052         * 
053         * @param payload The API frame payload. It must start with the frame type 
054         *                corresponding to a TX16 Request packet ({@code 0x01}).
055         *                The byte array must be in {@code OperatingMode.API} mode.
056         * 
057         * @return Parsed TX (transmit) 16 Request packet.
058         * 
059         * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.TX_16.getValue()} or
060         *                                  if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or
061         *                                  if {@code frameID < 0} or
062         *                                  if {@code frameID > 255} or
063         *                                  if {@code transmitOptions < 0} or
064         *                                  if {@code transmitOptions > 255}.
065         * @throws NullPointerException if {@code payload == null}.
066         */
067        public static TX16Packet createPacket(byte[] payload) {
068                if (payload == null)
069                        throw new NullPointerException("TX16 Request packet payload cannot be null.");
070                
071                // 1 (Frame type) + 1 (frame ID) + 2 (address) + 1 (transmit options)
072                if (payload.length < MIN_API_PAYLOAD_LENGTH)
073                        throw new IllegalArgumentException("Incomplete TX16 Request packet.");
074                
075                if ((payload[0] & 0xFF) != APIFrameType.TX_16.getValue())
076                        throw new IllegalArgumentException("Payload is not a TX16 Request 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 address, starting at 2nd byte.
086                XBee16BitAddress destAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF);
087                index = index + 2;
088                
089                // Transmit options byte.
090                int transmitOptions = payload[index] & 0xFF;
091                index = index + 1;
092                
093                // Get data.
094                byte[] data = null;
095                if (index < payload.length)
096                        data = Arrays.copyOfRange(payload, index, payload.length);
097                
098                return new TX16Packet(frameID, destAddress16, transmitOptions, data);
099        }
100        
101        /**
102         * Class constructor. Instantiates a new {@code TX16Packet} object with
103         * the given parameters.
104         * 
105         * @param frameID Frame ID.
106         * @param destAddress16 16-bit address of the destination device.
107         * @param transmitOptions Bitfield of supported transmission options.
108         * @param rfData RF Data that is sent to the destination device.
109         * 
110         * @throws IllegalArgumentException if {@code frameID < 0} or
111         *                                  if {@code frameID > 255} or
112         *                                  if {@code transmitOptions < 0} or
113         *                                  if {@code transmitOptions > 255}.
114         * @throws NullPointerException if {@code destAddress == null}.
115         * 
116         * @see com.digi.xbee.api.models.XBeeTransmitOptions
117         * @see com.digi.xbee.api.models.XBee16BitAddress
118         */
119        public TX16Packet(int frameID, XBee16BitAddress destAddress16, int transmitOptions, byte[] rfData) {
120                super(APIFrameType.TX_16);
121                
122                if (destAddress16 == null)
123                        throw new NullPointerException("16-bit destination address cannot be null.");
124                if (frameID < 0 || frameID > 255)
125                        throw new IllegalArgumentException("Frame ID must be between 0 and 255.");
126                if (transmitOptions < 0 || transmitOptions > 255)
127                        throw new IllegalArgumentException("Transmit options must be between 0 and 255.");
128                
129                this.frameID = frameID;
130                this.destAddress16 = destAddress16;
131                this.transmitOptions = transmitOptions;
132                this.rfData = rfData;
133                this.logger = LoggerFactory.getLogger(TX16Packet.class);
134        }
135
136        /*
137         * (non-Javadoc)
138         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData()
139         */
140        @Override
141        protected byte[] getAPIPacketSpecificData() {
142                ByteArrayOutputStream os = new ByteArrayOutputStream();
143                try {
144                        os.write(destAddress16.getValue());
145                        os.write(transmitOptions);
146                        if (rfData != null)
147                                os.write(rfData);
148                } catch (IOException e) {
149                        logger.error(e.getMessage(), e);
150                }
151                return os.toByteArray();
152        }
153
154        /*
155         * (non-Javadoc)
156         * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID()
157         */
158        @Override
159        public boolean needsAPIFrameID() {
160                return true;
161        }
162        
163        /*
164         * (non-Javadoc)
165         * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast()
166         */
167        @Override
168        public boolean isBroadcast() {
169                return get16bitDestinationAddress().equals(XBee16BitAddress.BROADCAST_ADDRESS);
170        }
171        
172        /**
173         * Returns the 16-bit destination address.
174         * 
175         * @return The 16-bit destination address.
176         * 
177         * @see com.digi.xbee.api.models.XBee16BitAddress
178         */
179        public XBee16BitAddress get16bitDestinationAddress() {
180                return destAddress16;
181        }
182        
183        /**
184         * Returns the transmit options bitfield.
185         * 
186         * @return Transmit options bitfield.
187         * 
188         * @see com.digi.xbee.api.models.XBeeTransmitOptions
189         */
190        public int getTransmitOptions() {
191                return transmitOptions;
192        }
193        
194        /**
195         * Sets the RF data to send.
196         * 
197         * @param rfData RF Data to send.
198         */
199        public void setRFData(byte[] rfData) {
200                this.rfData = rfData;
201        }
202        
203        /**
204         * Returns the RF Data to send.
205         * 
206         * @return RF data to send.
207         */
208        public byte[] getRFData() {
209                return rfData;
210        }
211        
212        /*
213         * (non-Javadoc)
214         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters()
215         */
216        @Override
217        public LinkedHashMap<String, String> getAPIPacketParameters() {
218                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
219                parameters.put("16-bit dest. address", HexUtils.prettyHexString(destAddress16.toString()));
220                parameters.put("Options", HexUtils.prettyHexString(HexUtils.integerToHexString(transmitOptions, 1)));
221                if (rfData != null)
222                        parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData)));
223                return parameters;
224        }
225}