com.digi.xbee.api.io.IOSample Maven / Gradle / Ivy
/**
* Copyright 2017, Digi International Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.digi.xbee.api.io;
import java.util.Arrays;
import java.util.HashMap;
import com.digi.xbee.api.exceptions.OperationNotSupportedException;
import com.digi.xbee.api.utils.ByteUtils;
/**
* This class represents an IO Data Sample. The sample is built using the
* the constructor. The sample contains an analog and digital mask indicating
* which IO lines are configured with that functionality.
*
* Depending on the protocol the XBee device is executing, the digital and
* analog masks are retrieved in separated bytes (2 bytes for the digital
* mask and 1 for the analog mask) or merged (digital and analog masks are
* contained in the same 2 bytes).
*
* 802.15.4 Protocol
*
* Digital and analog channels masks
* ------------------------------------------------------------------
* Indicates which digital and ADC IO lines are configured in the module.
* Each bit corresponds to one digital or ADC IO line on the module:
*
*
* bit 0 = DIO0
1
* bit 1 = DIO1
0
* bit 2 = DIO2
0
* bit 3 = DIO3
1
* bit 4 = DIO4
0
* bit 5 = DIO5
1
* bit 6 = DIO6
0
* bit 7 = DIO7
0
* bit 8 = DIO8
0
* bit 9 = AD0
0
* bit 10 = AD1
1
* bit 11 = AD2
1
* bit 12 = AD3
0
* bit 13 = AD4
0
* bit 14 = AD5
0
* bit 15 = N/A
0
*
* Example: mask of {@code 0x0C29} means DIO0, DIO3, DIO5, AD1 and
* AD2 enabled.
* 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 1
*
*
* Other Protocols
*
* Digital Channel Mask
* ------------------------------------------------------------------
* Indicates which digital IO lines are configured in the module. Each bit
* corresponds to one digital IO line on the module:
*
*
* bit 0 = DIO0/AD0
* bit 1 = DIO1/AD1
* bit 2 = DIO2/AD2
* bit 3 = DIO3/AD3
* bit 4 = DIO4/AD4
* bit 5 = DIO5/AD5/ASSOC
* bit 6 = DIO6/RTS
* bit 7 = DIO7/CTS
* bit 8 = DIO8/DTR/SLEEP_RQ
* bit 9 = DIO9/ON_SLEEP
* bit 10 = DIO10/PWM0/RSSI
* bit 11 = DIO11/PWM1
* bit 12 = DIO12/CD
* bit 13 = DIO13
* bit 14 = DIO14
* bit 15 = N/A
*
* Example: mask of {@code 0x040B} means DIO0, DIO1, DIO2, DIO3 and
* DIO10 enabled.
* 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1
*
*
* Analog Channel Mask
* -----------------------------------------------------------------------
* Indicates which lines are configured as ADC. Each bit in the analog
* channel mask corresponds to one ADC line on the module.
*
*
* bit 0 = AD0/DIO0
* bit 1 = AD1/DIO1
* bit 2 = AD2/DIO2
* bit 3 = AD3/DIO3
* bit 4 = AD4/DIO4
* bit 5 = AD5/DIO5/ASSOC
* bit 6 = N/A
* bit 7 = Supply Voltage Value
*
* Example: mask of {@code 0x03} means AD0, and AD1 enabled.
* 0 0 0 0 0 0 1 1
*
*/
public class IOSample {
// Variables.
private final byte[] ioSamplePayload;
private int digitalHSBMask;
private int digitalLSBMask;
private int digitalMask;
private int analogMask;
private int digitalHSBValues;
private int digitalLSBValues;
private int digitalValues;
private int powerSupplyVoltage;
private final HashMap analogValuesMap = new HashMap();
private final HashMap digitalValuesMap = new HashMap();
/**
* Class constructor. Instantiates a new object of type {@code IOSample}
* with the given IO sample payload.
*
* @param ioSamplePayload The payload corresponding to an IO sample.
*
* @throws IllegalArgumentException if {@code ioSamplePayload.length < 5}.
* @throws NullPointerException if {@code ioSamplePayload == null}.
*/
public IOSample(byte[] ioSamplePayload) {
if (ioSamplePayload == null)
throw new NullPointerException("IO sample payload cannot be null.");
if (ioSamplePayload.length < 5)
throw new IllegalArgumentException("IO sample payload must be longer than 4.");
this.ioSamplePayload = ioSamplePayload;
if (ioSamplePayload.length % 2 != 0)
parseRawIOSample();
else
parseIOSample();
}
/**
* Parses the information contained in the IO sample bytes reading the
* value of each configured DIO and ADC.
*/
private void parseRawIOSample() {
int dataIndex = 3;
// Obtain the digital mask. // Available digital IOs in 802.15.4
digitalHSBMask = ioSamplePayload[1] & 0x01; // 0 0 0 0 0 0 0 1
digitalLSBMask = ioSamplePayload[2] & 0xFF; // 1 1 1 1 1 1 1 1
// Combine the masks.
digitalMask = (digitalHSBMask << 8) + digitalLSBMask;
// Obtain the analog mask. // Available analog IOs in 802.15.4
analogMask = ((ioSamplePayload[1] << 8) + (ioSamplePayload[2] & 0xFF)) & 0x7E00; // 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
// Read the digital values (if any). There are 9 possible digital lines in
// 802.15.4 protocol. The digital mask indicates if there is any digital
// line enabled to read its value. If 0, no digital values are received.
if (digitalMask > 0) {
// Obtain the digital values.
digitalHSBValues = ioSamplePayload[3] & 0x7F;
digitalLSBValues = ioSamplePayload[4] & 0xFF;
// Combine the values.
digitalValues = (digitalHSBValues << 8) + digitalLSBValues;
for (int i = 0; i < 16; i++) {
if (!ByteUtils.isBitEnabled(digitalMask, i))
continue;
if (ByteUtils.isBitEnabled(digitalValues, i))
digitalValuesMap.put(IOLine.getDIO(i), IOValue.HIGH);
else
digitalValuesMap.put(IOLine.getDIO(i), IOValue.LOW);
}
// Increase the data index to read the analog values.
dataIndex += 2;
}
// Read the analog values (if any). There are 6 possible analog lines.
// The analog mask indicates if there is any analog line enabled to read
// its value. If 0, no analog values are received.
int adcIndex = 9;
while ((ioSamplePayload.length - dataIndex) > 1 && adcIndex < 16) {
if (!ByteUtils.isBitEnabled(analogMask, adcIndex)) {
adcIndex += 1;
continue;
}
// 802.15.4 protocol does not provide power supply value, so get just the ADC data.
analogValuesMap.put(IOLine.getDIO(adcIndex - 9), ((ioSamplePayload[dataIndex] & 0xFF) << 8) + (ioSamplePayload[dataIndex + 1] & 0xFF));
// Increase the data index to read the next analog values.
dataIndex += 2;
adcIndex += 1;
}
}
/**
* Parses the information contained in the IO sample bytes reading the
* value of each configured DIO and ADC.
*/
private void parseIOSample() {
int dataIndex = 4;
// Obtain the digital masks. // Available digital IOs
digitalHSBMask = ioSamplePayload[1] & 0x7F; // 0 1 1 1 1 1 1 1
digitalLSBMask = ioSamplePayload[2] & 0xFF; // 1 1 1 1 1 1 1 1
// Combine the masks.
digitalMask = (digitalHSBMask << 8) + digitalLSBMask;
// Obtain the analog mask. // Available analog IOs
analogMask = ioSamplePayload[3] & 0xBF; // 1 0 1 1 1 1 1 1
// Read the digital values (if any). There are 16 possible digital lines.
// The digital mask indicates if there is any digital line enabled to read
// its value. If 0, no digital values are received.
if (digitalMask > 0) {
// Obtain the digital values.
digitalHSBValues = ioSamplePayload[4] & 0x7F;
digitalLSBValues = ioSamplePayload[5] & 0xFF;
// Combine the values.
digitalValues = (digitalHSBValues << 8) + digitalLSBValues;
for (int i = 0; i < 16; i++) {
if (!ByteUtils.isBitEnabled(digitalMask, i))
continue;
if (ByteUtils.isBitEnabled(digitalValues, i))
digitalValuesMap.put(IOLine.getDIO(i), IOValue.HIGH);
else
digitalValuesMap.put(IOLine.getDIO(i), IOValue.LOW);
}
// Increase the data index to read the analog values.
dataIndex += 2;
}
// Read the analog values (if any). There are 6 possible analog lines.
// The analog mask indicates if there is any analog line enabled to read
// its value. If 0, no analog values are received.
int adcIndex = 0;
while ((ioSamplePayload.length - dataIndex) > 1 && adcIndex < 8) {
if (!ByteUtils.isBitEnabled(analogMask, adcIndex)) {
adcIndex += 1;
continue;
}
// When analog index is 7, it means that the analog value corresponds to the power
// supply voltage, therefore this value should be stored in a different value.
if (adcIndex == 7)
powerSupplyVoltage = ((ioSamplePayload[dataIndex] & 0xFF) << 8) + (ioSamplePayload[dataIndex + 1] & 0xFF);
else
analogValuesMap.put(IOLine.getDIO(adcIndex), ((ioSamplePayload[dataIndex] & 0xFF) << 8) + (ioSamplePayload[dataIndex + 1] & 0xFF));
// Increase the data index to read the next analog values.
dataIndex += 2;
adcIndex += 1;
}
}
/**
* Returns the HSB of the digital mask.
*
* @return HSB of the digital mask.
*
* @see #getDigitalLSBMask()
* @see #getDigitalMask()
*/
public int getDigitalHSBMask() {
return digitalHSBMask;
}
/**
* Returns the LSB of the digital mask.
*
* @return LSB of the digital mask.
*
* @see #getDigitalHSBMask()
* @see #getDigitalMask()
*/
public int getDigitalLSBMask() {
return digitalLSBMask;
}
/**
* Returns the combined (HSB + LSB) digital mask.
*
* @return The combined digital mask.
*
* @see #getDigitalLSBMask()
* @see #getDigitalHSBMask()
*/
public int getDigitalMask() {
return digitalMask;
}
/**
* Checks whether or not the {@code IOSample} has digital values.
*
* @return {@code true} if there are digital values, {@code false}
* otherwise.
*/
public boolean hasDigitalValues() {
return digitalValuesMap.size() > 0;
}
/**
* Returns whether or not this IO sample contains a digital value for
* the given IO line.
*
* @param ioLine The IO line to check if has a digital value.
*
* @return {@code true} if the given IO line has a digital value,
* {@code false} otherwise.
*
* @see #hasDigitalValues()
* @see IOLine
*/
public boolean hasDigitalValue(IOLine ioLine) {
return digitalValuesMap.containsKey(ioLine);
}
/**
* Returns the digital values map.
*
* To verify if this sample contains a valid digital values, use the
* method {@code hasDigitalValues()}.
*
*
* {@code
* if (ioSample.hasDigitalValues()) {
* HashMap values = ioSample.getDigitalValues();
* ...
* } else {
* ...
* }
* }
*
*
* @return {@code HashMap} with the digital value of each configured IO
* line.
*
* @see #getDigitalValue(IOLine)
* @see #hasDigitalValues()
* @see IOLine
* @see IOValue
*/
public HashMap getDigitalValues() {
return (HashMap) digitalValuesMap.clone();
}
/**
* Returns the digital value of the provided IO line.
*
* To verify if this sample contains a digital value for the given
* {@code IOLine}, use the method {@code hasDigitalValue(IOLine)}.
*
*
* {@code
* if (ioSample.hasDigitalValue(IOLine.DIO0_AD0)) {
* IOValue value = ioSample.getDigitalValue(IOLine.DIO0_AD0);
* ...
* } else {
* ...
* }
* }
*
*
* @param ioLine The IO line to get its digital value.
*
* @return The {@code IOValue} of the given IO line or {@code null} if the
* IO sample does not contain a digital value for the given IO line.
*
* @see #getDigitalValues()
* @see #hasDigitalValues()
* @see IOLine
* @see IOValue
*/
public IOValue getDigitalValue(IOLine ioLine) {
if (!digitalValuesMap.containsKey(ioLine))
return null;
return digitalValuesMap.get(ioLine);
}
/**
* Returns the analog mask.
*
* @return Analog mask.
*/
public int getAnalogMask() {
return analogMask;
}
/**
* Returns whether or not the {@code IOSample} has analog values.
*
* @return {@code true} if there are analog values, {@code false} otherwise.
*
* @see #getAnalogValue(IOLine)
* @see #getAnalogValues()
* @see #hasAnalogValue(IOLine)
* @see IOLine
*/
public boolean hasAnalogValues() {
return analogValuesMap.size() > 0;
}
/**
* Returns whether or not the given IO line has an analog value.
*
* @param ioLine The IO line to check if has an analog value.
*
* @return {@code true} if the given IO line has an analog value,
* {@code false} otherwise.
*
* @see #getAnalogValue(IOLine)
* @see #getAnalogValues()
* @see #hasAnalogValues()
* @see IOLine
*/
public boolean hasAnalogValue(IOLine ioLine) {
return analogValuesMap.containsKey(ioLine);
}
/**
* Returns the analog values map.
*
* To verify if this sample contains a valid analog values, use the
* method {@code hasAnalogValues()}.
*
*
* {@code
* if (ioSample.hasAnalogValues()) {
* HashMap values = ioSample.getAnalogValues();
* ...
* } else {
* ...
* }
* }
*
*
* @return {@code HashMap} with the analog value of each configured IO
* line.
*
* @see #getAnalogValue(IOLine)
* @see #hasAnalogValue(IOLine)
* @see #hasAnalogValues()
* @see IOLine
*/
public HashMap getAnalogValues() {
return (HashMap) analogValuesMap.clone();
}
/**
* Returns the analog value of the provided IO line.
*
* To verify if this sample contains an analog value for the given
* {@code IOLine}, use the method {@code hasAnalogValue(IOLine)}.
*
*
* {@code
* if (ioSample.hasAnalogValue(IOLine.DIO0_AD0)) {
* Integer value = ioSample.getAnalogValue(IOLine.DIO0_AD0);
* ...
* } else {
* ...
* }
* }
*
*
* @param ioLine The IO line to get its analog value.
*
* @return The analog value of the given IO line or {@code null} if the
* IO sample does not contain an analog value for the given IO line.
*
* @see #getAnalogValues()
* @see #hasAnalogValue(IOLine)
* @see #hasAnalogValues()
* @see IOLine
*/
public Integer getAnalogValue(IOLine ioLine) {
if (!analogValuesMap.containsKey(ioLine))
return null;
return analogValuesMap.get(ioLine);
}
/**
* Returns whether or not the IOSample has power supply value.
*
* @return {@code true} if the IOSample has power supply value,
* {@code false} otherwise.
*
* @see #getPowerSupplyValue()
*/
public boolean hasPowerSupplyValue() {
return ByteUtils.isBitEnabled(analogMask, 7);
}
/**
* Returns the value of the power supply voltage.
*
* To verify if this sample contains the power supply voltage, use the
* method {@code hasPowerSupplyValue()}.
*
*
* {@code
* if (ioSample.hasPowerSupplyValue()) {
* int value = ioSample.getPowerSupplyValue();
* ...
* } else {
* ...
* }
* }
*
*
* @return The value of the power supply voltage.
*
* @throws OperationNotSupportedException if the IOSample does not have
* power supply value.
*
* @see #hasPowerSupplyValue()
*/
public int getPowerSupplyValue() throws OperationNotSupportedException {
if (!ByteUtils.isBitEnabled(analogMask, 7))
throw new OperationNotSupportedException();
return powerSupplyVoltage;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
if (hasDigitalValues()) {
for (IOLine line : digitalValuesMap.keySet()) {
sb.append("[").append(line).append(": ").append(digitalValuesMap.get(line)).append("], ");
}
}
if (hasAnalogValues()) {
for (IOLine line : analogValuesMap.keySet()) {
sb.append("[").append(line).append(": ").append(analogValuesMap.get(line)).append("], ");
}
}
if (hasPowerSupplyValue()) {
try {
sb.append("[").append("Power supply voltage: ").append(getPowerSupplyValue()).append("], ");
} catch (OperationNotSupportedException e) {}
}
String s = sb.toString();
if (s.endsWith(", "))
s = s.substring(0, s.length() - 2);
return s + "}";
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
try {
IOSample sample = (IOSample)obj;
return Arrays.equals(ioSamplePayload, sample.ioSamplePayload);
} catch (ClassCastException e) {
return false;
}
}
}