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.XBee16BitAddress; 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 RX16 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 RX16IOPacket extends XBeeAPIPacket { 039 040 // Constants. 041 private static final int MIN_API_PAYLOAD_LENGTH = 5; // 1 (Frame type) + 2 (16-bit address) + 1 (RSSI) + 1 (receive options) 042 043 // Variables. 044 private final XBee16BitAddress sourceAddress16; 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 a new {@code RX16IOPacket} object from the given payload. 057 * 058 * @param payload The API frame payload. It must start with the frame type 059 * corresponding to a RX16 Address IO packet ({@code 0x83}). 060 * The byte array must be in {@code OperatingMode.API} mode. 061 * 062 * @return Parsed RX16 Address IO packet. 063 * 064 * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.RX_16.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 RX16IOPacket createPacket(byte[] payload) { 074 if (payload == null) 075 throw new NullPointerException("RX16 Address IO packet payload cannot be null."); 076 077 // 1 (Frame type) + 2 (16-bit address) + 1 (RSSI) + 1 (receive options) 078 if (payload.length < MIN_API_PAYLOAD_LENGTH) 079 throw new IllegalArgumentException("Incomplete RX16 Address IO packet."); 080 081 if ((payload[0] & 0xFF) != APIFrameType.RX_IO_16.getValue()) 082 throw new IllegalArgumentException("Payload is not a RX16 Address IO 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 // Received Signal Strength Indicator byte. 092 int rssi = payload[index] & 0xFF; 093 index = index + 1; 094 095 // Received Signal Strength Indicator 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 RX16IOPacket(sourceAddress16, rssi, receiveOptions, data); 105 } 106 107 /** 108 * Class constructor. Instantiates a new {@code RX16IOPacket} 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} or 120 * if {@code rfData.length < 5}. 121 * @throws NullPointerException if {@code sourceAddress16 == null}. 122 * 123 * @see com.digi.xbee.api.models.XBeeReceiveOptions 124 * @see com.digi.xbee.api.models.XBee16BitAddress 125 */ 126 public RX16IOPacket(XBee16BitAddress sourceAddress16, int rssi, int receiveOptions, byte[] rfData) { 127 super(APIFrameType.RX_IO_16); 128 129 if (sourceAddress16 == null) 130 throw new NullPointerException("16-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.sourceAddress16 = sourceAddress16; 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(RX16IOPacket.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(sourceAddress16.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 return ByteUtils.isBitEnabled(getReceiveOptions(), 1) 182 || ByteUtils.isBitEnabled(getReceiveOptions(), 2); 183 } 184 185 /** 186 * Returns the 16-bit sender/source address. 187 * 188 * @return The 16-bit sender/source address. 189 * 190 * @see com.digi.xbee.api.models.XBee16BitAddress 191 */ 192 public XBee16BitAddress get16bitSourceAddress() { 193 return sourceAddress16; 194 } 195 196 /** 197 * Returns the Received Signal Strength Indicator (RSSI). 198 * 199 * @return The Received Signal Strength Indicator (RSSI). 200 */ 201 public int getRSSI() { 202 return rssi; 203 } 204 205 /** 206 * Returns the receive options bitfield. 207 * 208 * @return Receive options bitfield. 209 * 210 * @see com.digi.xbee.api.models.XBeeReceiveOptions 211 */ 212 public int getReceiveOptions() { 213 return receiveOptions; 214 } 215 216 /** 217 * Returns the IO sample corresponding to the data contained in the packet. 218 * 219 * @return The IO sample of the packet, {@code null} if the packet has not 220 * any data or if the sample could not be generated correctly. 221 * 222 * @see com.digi.xbee.api.io.IOSample 223 */ 224 public IOSample getIOSample() { 225 return ioSample; 226 } 227 228 /** 229 * Sets the received RF data. 230 * 231 * @param rfData Received RF data. 232 */ 233 public void setRFData(byte[] rfData) { 234 this.rfData = rfData; 235 } 236 237 /** 238 * Returns the received RF data. 239 * 240 * @return Received RF data. 241 */ 242 public byte[] getRFData() { 243 return rfData; 244 } 245 246 /* 247 * (non-Javadoc) 248 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 249 */ 250 @Override 251 public LinkedHashMap<String, String> getAPIPacketParameters() { 252 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 253 parameters.put("16-bit source address", HexUtils.prettyHexString(sourceAddress16.toString())); 254 parameters.put("RSSI", HexUtils.prettyHexString(HexUtils.integerToHexString(rssi, 1))); 255 parameters.put("Options", HexUtils.prettyHexString(HexUtils.integerToHexString(receiveOptions, 1))); 256 if (ioSample != null) { 257 parameters.put("Number of samples", HexUtils.prettyHexString(HexUtils.integerToHexString(1, 1))); // There is always 1 sample. 258 parameters.put("Digital channel mask", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getDigitalMask(), 2))); 259 parameters.put("Analog channel mask", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getAnalogMask(), 2))); 260 for (int i = 0; i < 16; i++) { 261 if (ioSample.hasDigitalValue(IOLine.getDIO(i))) 262 parameters.put(IOLine.getDIO(i).getName() + " digital value", ioSample.getDigitalValue(IOLine.getDIO(i)).getName()); 263 } 264 for (int i = 0; i < 6; i++) { 265 if (ioSample.hasAnalogValue(IOLine.getDIO(i))) 266 parameters.put(IOLine.getDIO(i).getName() + " analog value", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getAnalogValue(IOLine.getDIO(i)), 2))); 267 } 268 } else if (rfData != null) 269 parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData))); 270 return parameters; 271 } 272}