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.ByteUtils;
026import com.digi.xbee.api.utils.HexUtils;
027
028/**
029 * This class represents an RX (Receive) 16 Request packet. Packet is built 
030 * using the parameters of the constructor or providing a valid API payload.
031 * 
032 * <p>When the module receives an RF packet, it is sent out the UART using this 
033 * message type</p>
034 * 
035 * <p>This packet is the response to TX (transmit) 16 Request packets.</p>
036 * 
037 * @see TX16Packet
038 * @see com.digi.xbee.api.packet.XBeeAPIPacket
039 *
040 */
041public class RX16Packet extends XBeeAPIPacket {
042
043        // Constants.
044        private static final int MIN_API_PAYLOAD_LENGTH = 5; // 1 (Frame type) + 2 (16-bit address) + 1 (signal strength) + 1 (receive options)
045        
046        // Variables.
047        private final XBee16BitAddress sourceAddress16;
048        
049        private final int rssi;
050        private final int receiveOptions;
051        
052        private byte[] rfData;
053        
054        private Logger logger;
055        
056        /**
057         * Creates a new {@code RX16Packet} object from the given payload.
058         * 
059         * @param payload The API frame payload. It must start with the frame type 
060         *                corresponding to a RX16 packet ({@code 0x81}).
061         *                The byte array must be in {@code OperatingMode.API} mode.
062         * 
063         * @return Parsed RX 16 packet.
064         * 
065         * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.RX_16.getValue()} or
066         *                                  if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or
067         *                                  if {@code rssi < 0} or
068         *                                  if {@code rssi > 100} or
069         *                                  if {@code receiveOptions < 0} or
070         *                                  if {@code receiveOptions > 255}.
071         * @throws NullPointerException if {@code payload == null}.
072         */
073        public static RX16Packet createPacket(byte[] payload) {
074                if (payload == null)
075                        throw new NullPointerException("RX16 packet payload cannot be null.");
076                
077                // 1 (Frame type) + 2 (16-bit address) + 1 (signal strength) + 1 (receive options)
078                if (payload.length < MIN_API_PAYLOAD_LENGTH)
079                        throw new IllegalArgumentException("Incomplete RX16 packet.");
080                
081                if ((payload[0] & 0xFF) != APIFrameType.RX_16.getValue())
082                        throw new IllegalArgumentException("Payload is not a RX16 packet.");
083                
084                // payload[0] is the frame type.
085                int index = 1;
086                
087                // 2 bytes of 16-bit address.
088                XBee16BitAddress sourceAddress16 = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF);
089                index = index + 2;
090                
091                // Signal strength byte.
092                int signalStrength = payload[index] & 0xFF;
093                index = index + 1;
094                
095                // Receive 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 RX16Packet(sourceAddress16, signalStrength, receiveOptions, data);
105        }
106        
107        /**
108         * Class constructor. Instantiates a new {@code RX16Packet} object with
109         * the given parameters.
110         * 
111         * @param sourceAddress16 16-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}.
120         * @throws NullPointerException if {@code sourceAddress16 == null}.
121         * 
122         * @see com.digi.xbee.api.models.XBeeReceiveOptions
123         * @see com.digi.xbee.api.models.XBee16BitAddress
124         */
125        public RX16Packet(XBee16BitAddress sourceAddress16, int rssi, int receiveOptions, byte[] rfData) {
126                super(APIFrameType.RX_16);
127                
128                if (sourceAddress16 == null)
129                        throw new NullPointerException("16-bit source address cannot be null.");
130                if (rssi < 0 || rssi > 100)
131                        throw new IllegalArgumentException("RSSI value must be between 0 and 100.");
132                if (receiveOptions < 0 || receiveOptions > 255)
133                        throw new IllegalArgumentException("Receive options value must be between 0 and 255.");
134                
135                this.sourceAddress16 = sourceAddress16;
136                this.rssi = rssi;
137                this.receiveOptions = receiveOptions;
138                this.rfData = rfData;
139                this.logger = LoggerFactory.getLogger(RX16Packet.class);
140        }
141        
142        /*
143         * (non-Javadoc)
144         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData()
145         */
146        @Override
147        protected byte[] getAPIPacketSpecificData() {
148                ByteArrayOutputStream os = new ByteArrayOutputStream();
149                try {
150                        os.write(sourceAddress16.getValue());
151                        os.write(rssi);
152                        os.write(receiveOptions);
153                        if (rfData != null)
154                                os.write(rfData);
155                } catch (IOException e) {
156                        logger.error(e.getMessage(), e);
157                }
158                return os.toByteArray();
159        }
160        
161        /*
162         * (non-Javadoc)
163         * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID()
164         */
165        @Override
166        public boolean needsAPIFrameID() {
167                return false;
168        }
169        
170        /*
171         * (non-Javadoc)
172         * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast()
173         */
174        @Override
175        public boolean isBroadcast() {
176                return ByteUtils.isBitEnabled(getReceiveOptions(), 1)
177                                || ByteUtils.isBitEnabled(getReceiveOptions(), 2);
178        }
179        
180        /**
181         * Returns the 16-bit sender/source address. 
182         * 
183         * @return The 16-bit sender/source address.
184         * 
185         * @see com.digi.xbee.api.models.XBee16BitAddress
186         */
187        public XBee16BitAddress get16bitSourceAddress() {
188                return sourceAddress16;
189        }
190        
191        /**
192         * Returns the Received Signal Strength Indicator (RSSI).
193         * 
194         * @return The Received Signal Strength Indicator (RSSI).
195         */
196        public int getRSSI() {
197                return rssi;
198        }
199        
200        /**
201         * Returns the receive options bitfield.
202         * 
203         * @return Receive options bitfield.
204         * 
205         * @see com.digi.xbee.api.models.XBeeReceiveOptions
206         */
207        public int getReceiveOptions() {
208                return receiveOptions;
209        }
210        
211        /**
212         * Sets the received RF data.
213         * 
214         * @param rfData Received RF data.
215         */
216        public void setRFData(byte[] rfData) {
217                this.rfData = rfData;
218        }
219        
220        /**
221         * Returns the received RF data.
222         * 
223         * @return Received RF data.
224         */
225        public byte[] getRFData() {
226                return rfData;
227        }
228        
229        /*
230         * (non-Javadoc)
231         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters()
232         */
233        @Override
234        public LinkedHashMap<String, String> getAPIPacketParameters() {
235                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
236                parameters.put("16-bit source address", HexUtils.prettyHexString(sourceAddress16.toString()));
237                parameters.put("RSSI", HexUtils.prettyHexString(HexUtils.integerToHexString(rssi, 1)));
238                parameters.put("Options", HexUtils.prettyHexString(HexUtils.integerToHexString(receiveOptions, 1)));
239                if (rfData != null)
240                        parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData)));
241                return parameters;
242        }
243}