    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.XBee16BitAddress;
    023import com.digi.xbee.api.models.XBee64BitAddress;
    024import com.digi.xbee.api.packet.XBeeAPIPacket;
    025import com.digi.xbee.api.packet.APIFrameType;
    026import com.digi.xbee.api.utils.HexUtils;
    027
    028/**
    029 * This class represents a Transmit Packet. Packet is built using the parameters
    030 * of the constructor or providing a valid API payload.
    031 *
    032 * <p>A Transmit Request API frame causes the module to send data as an RF
    033 * packet to the specified destination.</p>
    034 *
    035 * <p>The 64-bit destination address should be set to {@code 0x000000000000FFFF}
    036 * for a broadcast transmission (to all devices).</p>
    037 *
    038 * <p>The coordinator can be addressed by either setting the 64-bit address to
    039 * all {@code 0x00} and the 16-bit address to {@code 0xFFFE}, OR by setting the
    040 * 64-bit address to the coordinator's 64-bit address and the 16-bit address to
    041 * {@code 0x0000}.</p>
    042 *
    043 * <p>For all other transmissions, setting the 16-bit address to the correct
    044 * 16-bit address can help improve performance when transmitting to multiple
    045 * destinations.</p>
    046 *
    047 * <p>If a 16-bit address is not known, this field should be set to
    048 * {@code 0xFFFE} (unknown).</p>
    049 *
    050 * <p>The Transmit Status frame
    051 * ({@link com.digi.xbee.api.packet.APIFrameType#TRANSMIT_REQUEST}) will
    052 * indicate the discovered 16-bit address, if successful (see
    053 * {@link com.digi.xbee.api.packet.common.TransmitStatusPacket}).</p>
    054 *
    055 * <p>The broadcast radius can be set from {@code 0} up to {@code NH}. If set
    056 * to {@code 0}, the value of {@code NH} specifies the broadcast radius
    057 * (recommended). This parameter is only used for broadcast transmissions.</p>
    058 *
    059 * <p>The maximum number of payload bytes can be read with the {@code NP}
    060 * command.</p>
    061 *
    062 * <p>Several transmit options can be set using the transmit options bitfield.
    063 * </p>
    064 *
    065 * @see com.digi.xbee.api.models.XBeeTransmitOptions
    066 * @see com.digi.xbee.api.models.XBee16BitAddress#COORDINATOR_ADDRESS
    067 * @see com.digi.xbee.api.models.XBee16BitAddress#UNKNOWN_ADDRESS
    068 * @see com.digi.xbee.api.models.XBee64BitAddress#BROADCAST_ADDRESS
    069 * @see com.digi.xbee.api.models.XBee64BitAddress#COORDINATOR_ADDRESS
    070 * @see com.digi.xbee.api.packet.XBeeAPIPacket
    071 */
    072public class TransmitPacket extends XBeeAPIPacket {
    073
    074        // Constants.
    075        private static final int MIN_API_PAYLOAD_LENGTH = 14; // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (broadcast radious) + 1 (options)
    076
    077        // Variables.
    078        private final XBee64BitAddress destAddress64;
    079
    080        private final XBee16BitAddress destAddress16;
    081
    082        private final int broadcastRadius;
    083        private final int transmitOptions;
    084
    085        private byte[] rfData;
    086
    087        private Logger logger;
    088
    089        /**
    090         * Creates a new {@code TransmitPacket} object from the given payload.
    091         *
    092         * @param payload The API frame payload. It must start with the frame type
    093         *                corresponding to a Transmit packet ({@code 0x10}).
    094         *                The byte array must be in {@code OperatingMode.API} mode.
    095         *
    096         * @return Parsed Transmit Request packet.
    097         *
    098         * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.TRANSMIT_REQUEST.getValue()} or
    099         *                                  if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or
    100         *                                  if {@code frameID < 0} or
    101         *                                  if {@code frameID > 255} or
    102         *                                  if {@code broadcastRadius < 0} or
    103         *                                  if {@code broadcastRadius > 255} or
    104         *                                  if {@code transmitOptions < 0} or
    105         *                                  if {@code transmitOptions > 255}.
    106         * @throws NullPointerException if {@code payload == null}.
    107         */
    108        public static TransmitPacket createPacket(byte[] payload) {
    109                if (payload == null)
    110                        throw new NullPointerException("Transmit packet payload cannot be null.");
    111
    112                // 1 (Frame type) + 1 (frame ID) + 8 (64-bit address) + 2 (16-bit address) + 1 (broadcast radious) + 1 (options)
    113                if (payload.length < MIN_API_PAYLOAD_LENGTH)
    114                        throw new IllegalArgumentException("Incomplete Transmit packet.");
    115
    116                if ((payload[0] & 0xFF) != APIFrameType.TRANSMIT_REQUEST.getValue())
    117                        throw new IllegalArgumentException("Payload is not a Transmit packet.");
    118
    119                // payload[0] is the frame type.
    120                int index = 1;
    121
    122                // Frame ID byte.
    123                int frameID = payload[index] & 0xFF;
    124                index = index + 1;
    125
    126                // 8 bytes of 64-bit address.
    127                XBee64BitAddress destAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8));
    128                index = index + 8;
    129
    130                // 2 bytes of 16-bit address.
    131                XBee16BitAddress destAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF);
    132                index = index + 2;
    133
    134                // Broadcast radious byte.
    135                int broadcastRadius = payload[index] & 0xFF;
    136                index = index + 1;
    137
    138                // Options byte.
    139                int options = payload[index] & 0xFF;
    140                index = index + 1;
    141
    142                // Get RF data.
    143                byte[] rfData = null;
    144                if (index < payload.length)
    145                        rfData = Arrays.copyOfRange(payload, index, payload.length);
    146
    147                return new TransmitPacket(frameID, destAddress64, destAddress16, broadcastRadius, options, rfData);
    148        }
    149
    150        /**
    151         * Class constructor. Instantiates a new {@code TransmitPacket} object
    152         * with the given parameters.
    153         *
    154         * @param frameID Frame ID.
    155         * @param destAddress64 64-bit address of the destination device.
    156         * @param destAddress16 16-bit address of the destination device.
    157         * @param broadcastRadius maximum number of hops a broadcast transmission
    158         *                        can occur.
    159         * @param transmitOptions Bitfield of supported transmission options.
    160         * @param rfData RF Data that is sent to the destination device.
    161         *
    162         * @throws IllegalArgumentException if {@code frameID < 0} or
    163         *                                  if {@code frameID > 255} or
    164         *                                  if {@code broadcastRadius < 0} or
    165         *                                  if {@code broadcastRadius > 255} or
    166         *                                  if {@code transmitOptions < 0} or
    167         *                                  if {@code transmitOptions > 255}.
    168         * @throws NullPointerException if {@code destAddress64 == null} or
    169         *                              if {@code destAddress16 == null}.
    170         *
    171         * @see com.digi.xbee.api.models.XBeeTransmitOptions
    172         * @see com.digi.xbee.api.models.XBee16BitAddress
    173         * @see com.digi.xbee.api.models.XBee64BitAddress
    174         */
    175        public TransmitPacket(int frameID, XBee64BitAddress destAddress64, XBee16BitAddress destAddress16,
    176                        int broadcastRadius, int transmitOptions, byte[] rfData) {
    177                super(APIFrameType.TRANSMIT_REQUEST);
    178
    179                if (destAddress64 == null)
    180                        throw new NullPointerException("64-bit destination address cannot be null.");
    181                if (destAddress16 == null)
    182                        throw new NullPointerException("16-bit destination address cannot be null.");
    183                if (frameID < 0 || frameID > 255)
    184                        throw new IllegalArgumentException("Frame ID must be between 0 and 255.");
    185                if (broadcastRadius < 0 || broadcastRadius > 255)
    186                        throw new IllegalArgumentException("Broadcast radius must be between 0 and 255.");
    187                if (transmitOptions < 0 || transmitOptions > 255)
    188                        throw new IllegalArgumentException("Transmit options must be between 0 and 255.");
    189
    190                this.frameID = frameID;
    191                this.destAddress64 = destAddress64;
    192                this.destAddress16 = destAddress16;
    193                this.broadcastRadius = broadcastRadius;
    194                this.transmitOptions = transmitOptions;
    195                this.rfData = rfData;
    196                this.logger = LoggerFactory.getLogger(TransmitPacket.class);
    197        }
    198
    199        /*
    200         * (non-Javadoc)
    201         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData()
    202         */
    203        @Override
    204        protected byte[] getAPIPacketSpecificData() {
    205                ByteArrayOutputStream data = new ByteArrayOutputStream();
    206                try {
    207                        data.write(destAddress64.getValue());
    208                        data.write(destAddress16.getValue());
    209                        data.write(broadcastRadius);
    210                        data.write(transmitOptions);
    211                        if (rfData != null)
    212                                data.write(rfData);
    213                } catch (IOException e) {
    214                        logger.error(e.getMessage(), e);
    215                }
    216                return data.toByteArray();
    217        }
    218
    219        /*
    220         * (non-Javadoc)
    221         * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID()
    222         */
    223        @Override
    224        public boolean needsAPIFrameID() {
    225                return true;
    226        }
    227
    228        /*
    229         * (non-Javadoc)
    230         * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast()
    231         */
    232        @Override
    233        public boolean isBroadcast() {
    234                if (get64bitDestinationAddress().equals(XBee64BitAddress.BROADCAST_ADDRESS)
    235                                || get16bitDestinationAddress().equals(XBee16BitAddress.BROADCAST_ADDRESS))
    236                        return true;
    237                return false;
    238        }
    239
    240        /**
    241         * Returns the 64-bit destination address.
    242         *
    243         * @return The 64-bit destination address.
    244         *
    245         * @see com.digi.xbee.api.models.XBee64BitAddress
    246         */
    247        public XBee64BitAddress get64bitDestinationAddress() {
    248                return destAddress64;
    249        }
    250
    251        /**
    252         * Returns the 16-bit destination address.
    253         *
    254         * @return The 16-bit destination address.
    255         *
    256         * @see com.digi.xbee.api.models.XBee16BitAddress
    257         */
    258        public XBee16BitAddress get16bitDestinationAddress() {
    259                return destAddress16;
    260        }
    261
    262        /**
    263         * Returns the broadcast radius.
    264         *
    265         * @return The broadcast radius.
    266         */
    267        public int getBroadcastRadius() {
    268                return broadcastRadius;
    269        }
    270
    271        /**
    272         * Returns the transmit options bitfield.
    273         *
    274         * @return The transmit options bitfield.
    275         *
    276         * @see com.digi.xbee.api.models.XBeeTransmitOptions
    277         */
    278        public int getTransmitOptions() {
    279                return transmitOptions;
    280        }
    281
    282        /**
    283         * Sets the RF Data to send.
    284         *
    285         * @param rfData RF Data to send.
    286         */
    287        public void setRFData(byte[] rfData) {
    288                this.rfData = rfData;
    289        }
    290
    291        /**
    292         * Returns the RF Data to send.
    293         *
    294         * @return RF Data to send.
    295         */
    296        public byte[] getRFData() {
    297                return rfData;
    298        }
    299
    300        /*
    301         * (non-Javadoc)
    302         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters()
    303         */
    304        @Override
    305        public LinkedHashMap<String, String> getAPIPacketParameters() {
    306                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
    307                parameters.put("64-bit dest. address", HexUtils.prettyHexString(destAddress64.toString()));
    308                parameters.put("16-bit dest. address", HexUtils.prettyHexString(destAddress16.toString()));
    309                parameters.put("Broadcast radius", HexUtils.prettyHexString(HexUtils.integerToHexString(broadcastRadius, 1)) + " (" + broadcastRadius + ")");
    310                parameters.put("Options", HexUtils.prettyHexString(HexUtils.integerToHexString(transmitOptions, 1)));
    311                if (rfData != null)
    312                        parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData)));
    313                return parameters;
    314        }
    315}