    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.io.IOLine;
    023import com.digi.xbee.api.io.IOSample;
    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 an RX64 Address IO packet. Packet is built using the
    032 * parameters of the constructor or providing a valid API payload.
    033 *
    034 * <p>I/O data is sent out the UART using an API frame.</p>
    035 *
    036 * @see com.digi.xbee.api.packet.XBeeAPIPacket
    037 */
    038public class RX64IOPacket extends XBeeAPIPacket {
    039
    040        // Constants.
    041        private static final int MIN_API_PAYLOAD_LENGTH = 11; // 1 (Frame type) + 8 (64-bit address) + 1 (RSSI) + 1 (receive options)
    042
    043        // Variables.
    044        private final XBee64BitAddress sourceAddress64;
    045
    046        private final IOSample ioSample;
    047
    048        private final int rssi;
    049        private final int receiveOptions;
    050
    051        private byte[] rfData;
    052
    053        private Logger logger;
    054
    055        /**
    056         * Creates an new {@code RX64IOPacket} object from the given payload.
    057         *
    058         * @param payload The API frame payload. It must start with the frame type
    059         *                corresponding to a RX64 Address IO packet ({@code 0x82}).
    060         *                The byte array must be in {@code OperatingMode.API} mode.
    061         *
    062         * @return Parsed RX64 Address IO packet.
    063         *
    064         * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.RX_64.getValue()} or
    065         *                                  if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or
    066         *                                  if {@code rssi < 0} or
    067         *                                  if {@code rssi > 100} or
    068         *                                  if {@code receiveOptions < 0} or
    069         *                                  if {@code receiveOptions > 255} or
    070         *                                  if {@code rfData.length < 5}.
    071         * @throws NullPointerException if {@code payload == null}.
    072         */
    073        public static RX64IOPacket createPacket(byte[] payload) {
    074                if (payload == null)
    075                        throw new NullPointerException("RX64 Address IO packet payload cannot be null.");
    076
    077                // 1 (Frame type) + 8 (64-bit address) + 1 (RSSI) + 1 (receive options)
    078                if (payload.length < MIN_API_PAYLOAD_LENGTH)
    079                        throw new IllegalArgumentException("Incomplete RX64 Address IO packet.");
    080
    081                if ((payload[0] & 0xFF) != APIFrameType.RX_IO_64.getValue())
    082                        throw new IllegalArgumentException("Payload is not a RX64 Address IO packet.");
    083
    084                // payload[0] is the frame type.
    085                int index = 1;
    086
    087                // 8 bytes of 64-bit address.
    088                XBee64BitAddress sourceAddress64 = new XBee64BitAddress(Arrays.copyOfRange(payload, index, index + 8));
    089                index = index + 8;
    090
    091                // Received Signal Strength Indicator byte.
    092                int rssi = payload[index] & 0xFF;
    093                index = index + 1;
    094
    095                // Received Options byte.
    096                int receiveOptions = payload[index] & 0xFF;
    097                index = index + 1;
    098
    099                // Get data.
    100                byte[] data = null;
    101                if (index < payload.length)
    102                        data = Arrays.copyOfRange(payload, index, payload.length);
    103
    104                return new RX64IOPacket(sourceAddress64, rssi, receiveOptions, data);
    105        }
    106
    107        /**
    108         * Class constructor. Instantiates a new {@code RX64IOPacket} object with
    109         * the given parameters.
    110         *
    111         * @param sourceAddress64 64-bit address of the sender.
    112         * @param rssi Received signal strength indicator.
    113         * @param receiveOptions Bitfield indicating the receive options.
    114         * @param rfData Received RF data.
    115         *
    116         * @throws IllegalArgumentException if {@code rssi < 0} or
    117         *                                  if {@code rssi > 100} or
    118         *                                  if {@code receiveOptions < 0} or
    119         *                                  if {@code receiveOptions > 255} or
    120         *                                  if {@code rfData.length < 5}.
    121         * @throws NullPointerException if {@code sourceAddress64 == null}.
    122         *
    123         * @see com.digi.xbee.api.models.XBeeReceiveOptions
    124         * @see com.digi.xbee.api.models.XBee64BitAddress
    125         */
    126        public RX64IOPacket(XBee64BitAddress sourceAddress64, int rssi, int receiveOptions, byte[] rfData) {
    127                super(APIFrameType.RX_IO_64);
    128
    129                if (sourceAddress64 == null)
    130                        throw new NullPointerException("64-bit source address cannot be null.");
    131                if (rssi < 0 || rssi > 100)
    132                        throw new IllegalArgumentException("RSSI value must be between 0 and 100.");
    133                if (receiveOptions < 0 || receiveOptions > 255)
    134                        throw new IllegalArgumentException("Receive options value must be between 0 and 255.");
    135
    136                this.sourceAddress64 = sourceAddress64;
    137                this.rssi = rssi;
    138                this.receiveOptions = receiveOptions;
    139                this.rfData = rfData;
    140                if (rfData != null)
    141                        ioSample = new IOSample(rfData);
    142                else
    143                        ioSample = null;
    144                this.logger = LoggerFactory.getLogger(RX64Packet.class);
    145        }
    146
    147        /*
    148         * (non-Javadoc)
    149         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData()
    150         */
    151        @Override
    152        protected byte[] getAPIPacketSpecificData() {
    153                ByteArrayOutputStream os = new ByteArrayOutputStream();
    154                try {
    155                        os.write(sourceAddress64.getValue());
    156                        os.write(rssi);
    157                        os.write(receiveOptions);
    158                        if (rfData != null)
    159                                os.write(rfData);
    160                } catch (IOException e) {
    161                        logger.error(e.getMessage(), e);
    162                }
    163                return os.toByteArray();
    164        }
    165
    166        /*
    167         * (non-Javadoc)
    168         * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID()
    169         */
    170        @Override
    171        public boolean needsAPIFrameID() {
    172                return false;
    173        }
    174
    175        /*
    176         * (non-Javadoc)
    177         * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast()
    178         */
    179        @Override
    180        public boolean isBroadcast() {
    181                if (ByteUtils.isBitEnabled(getReceiveOptions(), 1)
    182                                || ByteUtils.isBitEnabled(getReceiveOptions(), 2))
    183                        return true;
    184                return false;
    185        }
    186
    187        /**
    188         * Returns the 64-bit sender/source address.
    189         *
    190         * @return The 64-bit sender/source address.
    191         *
    192         * @see com.digi.xbee.api.models.XBee64BitAddress
    193         */
    194        public XBee64BitAddress get64bitSourceAddress() {
    195                return sourceAddress64;
    196        }
    197
    198        /**
    199         * Returns the Received Signal Strength Indicator (RSSI).
    200         *
    201         * @return The Received Signal Strength Indicator (RSSI).
    202         */
    203        public int getRSSI() {
    204                return rssi;
    205        }
    206
    207        /**
    208         * Returns the receive options bitfield.
    209         *
    210         * @return Receive options bitfield.
    211         *
    212         * @see com.digi.xbee.api.models.XBeeReceiveOptions
    213         */
    214        public int getReceiveOptions() {
    215                return receiveOptions;
    216        }
    217
    218        /**
    219         * Returns the IO sample corresponding to the data contained in the packet.
    220         *
    221         * @return The IO sample of the packet, {@code null} if the packet has not
    222         *         any data or if the sample could not be generated correctly.
    223         *
    224         * @see com.digi.xbee.api.io.IOSample
    225         */
    226        public IOSample getIOSample() {
    227                return ioSample;
    228        }
    229
    230        /**
    231         * Sets the received RF data.
    232         *
    233         * @param rfData Received RF data.
    234         */
    235        public void setRFData(byte[] rfData){
    236                this.rfData = rfData;
    237        }
    238
    239        /**
    240         * Returns the received RF data.
    241         *
    242         * @return Received RF data.
    243         */
    244        public byte[] getRFData(){
    245                return rfData;
    246        }
    247
    248        /*
    249         * (non-Javadoc)
    250         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters()
    251         */
    252        @Override
    253        public LinkedHashMap<String, String> getAPIPacketParameters() {
    254                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
    255                parameters.put("64-bit source address", HexUtils.prettyHexString(sourceAddress64.toString()));
    256                parameters.put("RSSI", HexUtils.prettyHexString(HexUtils.integerToHexString(rssi, 1)));
    257                parameters.put("Options", HexUtils.prettyHexString(HexUtils.integerToHexString(receiveOptions, 1)));
    258                if (ioSample != null) {
    259                        parameters.put("Number of samples", HexUtils.prettyHexString(HexUtils.integerToHexString(1, 1))); // There is always 1 sample.
    260                        parameters.put("Digital channel mask", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getDigitalMask(), 2)));
    261                        parameters.put("Analog channel mask", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getAnalogMask(), 2)));
    262                        for (int i = 0; i < 16; i++) {
    263                                if (ioSample.hasDigitalValue(IOLine.getDIO(i)))
    264                                        parameters.put(IOLine.getDIO(i).getName() + " digital value", ioSample.getDigitalValue(IOLine.getDIO(i)).getName());
    265                        }
    266                        for (int i = 0; i < 6; i++) {
    267                                if (ioSample.hasAnalogValue(IOLine.getDIO(i)))
    268                                        parameters.put(IOLine.getDIO(i).getName() + " analog value", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getAnalogValue(IOLine.getDIO(i)), 2)));
    269                        }
    270                } else if (rfData != null)
    271                        parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData)));
    272                return parameters;
    273        }
    274}