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