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 return ByteUtils.isBitEnabled(getReceiveOptions(), 1); 190 } 191 192 /** 193 * Returns the 64-bit sender/source address. 194 * 195 * @return The 64-bit sender/source address. 196 * 197 * @see com.digi.xbee.api.models.XBee64BitAddress 198 */ 199 public XBee64BitAddress get64bitSourceAddress() { 200 return sourceAddress64; 201 } 202 203 /** 204 * Returns the 16-bit sender/source address. 205 * 206 * @return 16-bit sender/source address. 207 * 208 * @see com.digi.xbee.api.models.XBee16BitAddress 209 */ 210 public XBee16BitAddress get16bitSourceAddress() { 211 return sourceAddress16; 212 } 213 214 /** 215 * Returns the receive options. 216 * 217 * @return Receive options. 218 * 219 * @see com.digi.xbee.api.models.XBeeReceiveOptions 220 */ 221 public int getReceiveOptions() { 222 return receiveOptions; 223 } 224 225 /** 226 * Returns the IO sample corresponding to the data contained in the packet. 227 * 228 * @return The IO sample of the packet, null if the packet has not any data 229 * or if the sample could not be generated correctly. 230 * 231 * @see com.digi.xbee.api.io.IOSample 232 */ 233 public IOSample getIOSample() { 234 return ioSample; 235 } 236 237 /** 238 * Sets the received RF data. 239 * 240 * @param rfData Received RF data. 241 */ 242 public void setRFData(byte[] rfData) { 243 this.rfData = rfData; 244 } 245 246 /** 247 * Returns the received RF data. 248 * 249 * @return Received RF data. 250 */ 251 public byte[] getRFData() { 252 return rfData; 253 } 254 255 /* 256 * (non-Javadoc) 257 * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters() 258 */ 259 @Override 260 public LinkedHashMap<String, String> getAPIPacketParameters() { 261 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 262 parameters.put("64-bit source address", HexUtils.prettyHexString(sourceAddress64.toString())); 263 parameters.put("16-bit source address", HexUtils.prettyHexString(sourceAddress16.toString())); 264 parameters.put("Receive options", HexUtils.prettyHexString(HexUtils.integerToHexString(receiveOptions, 1))); 265 if (ioSample != null) { 266 parameters.put("Number of samples", HexUtils.prettyHexString(HexUtils.integerToHexString(1, 1))); // There is always 1 sample. 267 parameters.put("Digital channel mask", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getDigitalMask(), 2))); 268 parameters.put("Analog channel mask", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getAnalogMask(), 1))); 269 for (int i = 0; i < 16; i++) { 270 if (ioSample.hasDigitalValue(IOLine.getDIO(i))) 271 parameters.put(IOLine.getDIO(i).getName() + " digital value", ioSample.getDigitalValue(IOLine.getDIO(i)).getName()); 272 } 273 for (int i = 0; i < 6; i++) { 274 if (ioSample.hasAnalogValue(IOLine.getDIO(i))) 275 parameters.put(IOLine.getDIO(i).getName() + " analog value", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getAnalogValue(IOLine.getDIO(i)), 2))); 276 } 277 if (ioSample.hasPowerSupplyValue()) 278 try { 279 parameters.put("Power supply value", HexUtils.prettyHexString(HexUtils.integerToHexString(ioSample.getPowerSupplyValue(), 2))); 280 } catch (OperationNotSupportedException e) { } 281 } else if (rfData != null) 282 parameters.put("RF data", HexUtils.prettyHexString(HexUtils.byteArrayToHexString(rfData))); 283 return parameters; 284 } 285}