001/** 002 * Copyright 2017-2019, Digi International Inc. 003 * 004 * This Source Code Form is subject to the terms of the Mozilla Public 005 * License, v. 2.0. If a copy of the MPL was not distributed with this 006 * file, you can obtain one at http://mozilla.org/MPL/2.0/. 007 * 008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 015 */ 016package com.digi.xbee.api.io; 017 018import java.io.ByteArrayOutputStream; 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.HashMap; 023 024import com.digi.xbee.api.exceptions.OperationNotSupportedException; 025import com.digi.xbee.api.utils.ByteUtils; 026 027/** 028 * This class represents an IO Data Sample. The sample is built using the 029 * the constructor. The sample contains an analog and digital mask indicating 030 * which IO lines are configured with that functionality. 031 * 032 * <p>Depending on the protocol the XBee device is executing, the digital and 033 * analog masks are retrieved in separated bytes (2 bytes for the digital 034 * mask and 1 for the analog mask) or merged (digital and analog masks are 035 * contained in the same 2 bytes).</p> 036 * <br> 037 * <p><b>802.15.4 Protocol</b></p> 038 * <br> 039 * <p>Digital and analog channels masks</p> 040 * <p>------------------------------------------------------------------</p> 041 * <p>Indicates which digital and ADC IO lines are configured in the module. 042 * Each bit corresponds to one digital or ADC IO line on the module:</p> 043 * <br> 044 * <BLOCKQUOTE> 045 * <p>bit 0 = DIO0</p>1 046 * <p>bit 1 = DIO1</p>0 047 * <p>bit 2 = DIO2</p>0 048 * <p>bit 3 = DIO3</p>1 049 * <p>bit 4 = DIO4</p>0 050 * <p>bit 5 = DIO5</p>1 051 * <p>bit 6 = DIO6</p>0 052 * <p>bit 7 = DIO7</p>0 053 * <p>bit 8 = DIO8</p>0 054 * <p>bit 9 = AD0</p>0 055 * <p>bit 10 = AD1</p>1 056 * <p>bit 11 = AD2</p>1 057 * <p>bit 12 = AD3</p>0 058 * <p>bit 13 = AD4</p>0 059 * <p>bit 14 = AD5</p>0 060 * <p>bit 15 = N/A</p>0 061 * <br> 062 * <p>Example: mask of {@code 0x0C29} means DIO0, DIO3, DIO5, AD1 and 063 * AD2 enabled.</p> 064 * <p>0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 1</p> 065 * </BLOCKQUOTE> 066 * <br><br> 067 * <p><b>Other Protocols</b></p> 068 * <br> 069 * <p>Digital Channel Mask</p> 070 * <p>------------------------------------------------------------------</p> 071 * <p>Indicates which digital IO lines are configured in the module. Each bit 072 * corresponds to one digital IO line on the module:</p> 073 * <br> 074 * <BLOCKQUOTE> 075 * <p>bit 0 = DIO0/AD0</p> 076 * <p>bit 1 = DIO1/AD1</p> 077 * <p>bit 2 = DIO2/AD2</p> 078 * <p>bit 3 = DIO3/AD3</p> 079 * <p>bit 4 = DIO4/AD4</p> 080 * <p>bit 5 = DIO5/AD5/ASSOC</p> 081 * <p>bit 6 = DIO6/RTS</p> 082 * <p>bit 7 = DIO7/CTS</p> 083 * <p>bit 8 = DIO8/DTR/SLEEP_RQ</p> 084 * <p>bit 9 = DIO9/ON_SLEEP</p> 085 * <p>bit 10 = DIO10/PWM0/RSSI</p> 086 * <p>bit 11 = DIO11/PWM1</p> 087 * <p>bit 12 = DIO12/CD</p> 088 * <p>bit 13 = DIO13</p> 089 * <p>bit 14 = DIO14</p> 090 * <p>bit 15 = N/A</p> 091 * <br> 092 * <p>Example: mask of {@code 0x040B} means DIO0, DIO1, DIO2, DIO3 and 093 * DIO10 enabled.</p> 094 * <p>0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1</p> 095 * <br><br> 096 * </BLOCKQUOTE> 097 * <p>Analog Channel Mask</p> 098 * <p>-----------------------------------------------------------------------</p> 099 * <p>Indicates which lines are configured as ADC. Each bit in the analog 100 * channel mask corresponds to one ADC line on the module.</p> 101 * <br> 102 * <BLOCKQUOTE> 103 * <p>bit 0 = AD0/DIO0</p> 104 * <p>bit 1 = AD1/DIO1</p> 105 * <p>bit 2 = AD2/DIO2</p> 106 * <p>bit 3 = AD3/DIO3</p> 107 * <p>bit 4 = AD4/DIO4</p> 108 * <p>bit 5 = AD5/DIO5/ASSOC</p> 109 * <p>bit 6 = N/A</p> 110 * <p>bit 7 = Supply Voltage Value</p> 111 * <br> 112 * <p>Example: mask of {@code 0x03} means AD0, and AD1 enabled.</p> 113 * <p>0 0 0 0 0 0 1 1</p> 114 * </BLOCKQUOTE> 115 */ 116public class IOSample { 117 118 // Variables. 119 private final byte[] ioSamplePayload; 120 121 private ByteArrayOutputStream rawMask; 122 private ByteArrayOutputStream rawSample; 123 124 private int digitalHSBMask; 125 private int digitalLSBMask; 126 private int digitalMask; 127 private int analogMask; 128 private int digitalHSBValues; 129 private int digitalLSBValues; 130 private int digitalValues; 131 private int powerSupplyVoltage; 132 133 private final HashMap<IOLine, Integer> analogValuesMap = new HashMap<IOLine, Integer>(); 134 private final HashMap<IOLine, IOValue> digitalValuesMap = new HashMap<IOLine, IOValue>(); 135 136 /** 137 * Class constructor. Instantiates a new object of type {@code IOSample} 138 * with the given IO sample payload. 139 * 140 * @param ioSamplePayload The payload corresponding to an IO sample. 141 * 142 * @throws IllegalArgumentException if {@code ioSamplePayload.length < 5}. 143 * @throws NullPointerException if {@code ioSamplePayload == null}. 144 */ 145 public IOSample(byte[] ioSamplePayload) { 146 if (ioSamplePayload == null) 147 throw new NullPointerException("IO sample payload cannot be null."); 148 149 if (ioSamplePayload.length < 5) 150 throw new IllegalArgumentException("IO sample payload must be longer than 4."); 151 152 this.ioSamplePayload = ioSamplePayload; 153 rawMask = new ByteArrayOutputStream(); 154 rawSample = new ByteArrayOutputStream(); 155 if (ioSamplePayload.length % 2 != 0) 156 parseRawIOSample(); 157 else 158 parseIOSample(); 159 } 160 161 /** 162 * Parses the information contained in the IO sample bytes reading the 163 * value of each configured DIO and ADC. 164 */ 165 private void parseRawIOSample() { 166 int dataIndex = 3; 167 168 // Obtain the digital mask. // Available digital IOs in 802.15.4 169 digitalHSBMask = ioSamplePayload[1] & 0x01; // 0 0 0 0 0 0 0 1 170 rawMask.write(ioSamplePayload[1] & 0xFF); 171 digitalLSBMask = ioSamplePayload[2] & 0xFF; // 1 1 1 1 1 1 1 1 172 rawMask.write(ioSamplePayload[2] & 0xFF); 173 // Combine the masks. 174 digitalMask = (digitalHSBMask << 8) + digitalLSBMask; 175 // Obtain the analog mask. // Available analog IOs in 802.15.4 176 analogMask = (((ioSamplePayload[1] & 0xFF) << 8) + (ioSamplePayload[2] & 0xFF)) & 0x7E00; // 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 177 178 // Read the digital values (if any). There are 9 possible digital lines in 179 // 802.15.4 protocol. The digital mask indicates if there is any digital 180 // line enabled to read its value. If 0, no digital values are received. 181 if (digitalMask > 0) { 182 // Obtain the digital values. 183 digitalHSBValues = ioSamplePayload[3] & 0x7F; 184 rawSample.write(ioSamplePayload[3] & 0xFF); 185 digitalLSBValues = ioSamplePayload[4] & 0xFF; 186 rawSample.write(ioSamplePayload[4] & 0xFF); 187 // Combine the values. 188 digitalValues = (digitalHSBValues << 8) + digitalLSBValues; 189 190 for (int i = 0; i < 16; i++) { 191 if (!ByteUtils.isBitEnabled(digitalMask, i)) 192 continue; 193 if (ByteUtils.isBitEnabled(digitalValues, i)) 194 digitalValuesMap.put(IOLine.getDIO(i), IOValue.HIGH); 195 else 196 digitalValuesMap.put(IOLine.getDIO(i), IOValue.LOW); 197 } 198 // Increase the data index to read the analog values. 199 dataIndex += 2; 200 } 201 202 // Read the analog values (if any). There are 6 possible analog lines. 203 // The analog mask indicates if there is any analog line enabled to read 204 // its value. If 0, no analog values are received. 205 int adcIndex = 9; 206 while ((ioSamplePayload.length - dataIndex) > 1 && adcIndex < 16) { 207 if (!ByteUtils.isBitEnabled(analogMask, adcIndex)) { 208 adcIndex += 1; 209 continue; 210 } 211 // 802.15.4 protocol does not provide power supply value, so get just the ADC data. 212 analogValuesMap.put(IOLine.getDIO(adcIndex - 9), ((ioSamplePayload[dataIndex] & 0xFF) << 8) + (ioSamplePayload[dataIndex + 1] & 0xFF)); 213 rawSample.write(ioSamplePayload[dataIndex] & 0xFF); 214 rawSample.write(ioSamplePayload[dataIndex + 1] & 0xFF); 215 // Increase the data index to read the next analog values. 216 dataIndex += 2; 217 adcIndex += 1; 218 } 219 } 220 221 /** 222 * Parses the information contained in the IO sample bytes reading the 223 * value of each configured DIO and ADC. 224 */ 225 private void parseIOSample() { 226 int dataIndex = 4; 227 228 // Obtain the digital masks. // Available digital IOs 229 digitalHSBMask = ioSamplePayload[1] & 0x7F; // 0 1 1 1 1 1 1 1 230 rawMask.write(ioSamplePayload[1] & 0xFF); 231 digitalLSBMask = ioSamplePayload[2] & 0xFF; // 1 1 1 1 1 1 1 1 232 rawMask.write(ioSamplePayload[2] & 0xFF); 233 // Combine the masks. 234 digitalMask = (digitalHSBMask << 8) + digitalLSBMask; 235 // Obtain the analog mask. // Available analog IOs 236 analogMask = ioSamplePayload[3] & 0xBF; // 1 0 1 1 1 1 1 1 237 rawMask.write(ioSamplePayload[3] & 0xFF); 238 239 // Read the digital values (if any). There are 16 possible digital lines. 240 // The digital mask indicates if there is any digital line enabled to read 241 // its value. If 0, no digital values are received. 242 if (digitalMask > 0) { 243 // Obtain the digital values. 244 digitalHSBValues = ioSamplePayload[4] & 0x7F; 245 rawSample.write(ioSamplePayload[4] & 0xFF); 246 digitalLSBValues = ioSamplePayload[5] & 0xFF; 247 rawSample.write(ioSamplePayload[5] & 0xFF); 248 // Combine the values. 249 digitalValues = (digitalHSBValues << 8) + digitalLSBValues; 250 251 for (int i = 0; i < 16; i++) { 252 if (!ByteUtils.isBitEnabled(digitalMask, i)) 253 continue; 254 if (ByteUtils.isBitEnabled(digitalValues, i)) 255 digitalValuesMap.put(IOLine.getDIO(i), IOValue.HIGH); 256 else 257 digitalValuesMap.put(IOLine.getDIO(i), IOValue.LOW); 258 } 259 // Increase the data index to read the analog values. 260 dataIndex += 2; 261 } 262 263 // Read the analog values (if any). There are 6 possible analog lines. 264 // The analog mask indicates if there is any analog line enabled to read 265 // its value. If 0, no analog values are received. 266 int adcIndex = 0; 267 while ((ioSamplePayload.length - dataIndex) > 1 && adcIndex < 8) { 268 if (!ByteUtils.isBitEnabled(analogMask, adcIndex)) { 269 adcIndex += 1; 270 continue; 271 } 272 // When analog index is 7, it means that the analog value corresponds to the power 273 // supply voltage, therefore this value should be stored in a different value. 274 if (adcIndex == 7) 275 powerSupplyVoltage = ((ioSamplePayload[dataIndex] & 0xFF) << 8) + (ioSamplePayload[dataIndex + 1] & 0xFF); 276 else 277 analogValuesMap.put(IOLine.getDIO(adcIndex), ((ioSamplePayload[dataIndex] & 0xFF) << 8) + (ioSamplePayload[dataIndex + 1] & 0xFF)); 278 rawSample.write(ioSamplePayload[dataIndex] & 0xFF); 279 rawSample.write(ioSamplePayload[dataIndex + 1] & 0xFF); 280 // Increase the data index to read the next analog values. 281 dataIndex += 2; 282 adcIndex += 1; 283 } 284 } 285 286 /** 287 * Returns the HSB of the digital mask. 288 * 289 * @return HSB of the digital mask. 290 * 291 * @see #getDigitalLSBMask() 292 * @see #getDigitalMask() 293 */ 294 public int getDigitalHSBMask() { 295 return digitalHSBMask; 296 } 297 298 /** 299 * Returns the LSB of the digital mask. 300 * 301 * @return LSB of the digital mask. 302 * 303 * @see #getDigitalHSBMask() 304 * @see #getDigitalMask() 305 */ 306 public int getDigitalLSBMask() { 307 return digitalLSBMask; 308 } 309 310 /** 311 * Returns the combined (HSB + LSB) digital mask. 312 * 313 * @return The combined digital mask. 314 * 315 * @see #getDigitalLSBMask() 316 * @see #getDigitalHSBMask() 317 */ 318 public int getDigitalMask() { 319 return digitalMask; 320 } 321 322 /** 323 * Checks whether or not the {@code IOSample} has digital values. 324 * 325 * @return {@code true} if there are digital values, {@code false} 326 * otherwise. 327 */ 328 public boolean hasDigitalValues() { 329 return digitalValuesMap.size() > 0; 330 } 331 332 /** 333 * Returns whether or not this IO sample contains a digital value for 334 * the given IO line. 335 * 336 * @param ioLine The IO line to check if has a digital value. 337 * 338 * @return {@code true} if the given IO line has a digital value, 339 * {@code false} otherwise. 340 * 341 * @see #hasDigitalValues() 342 * @see IOLine 343 */ 344 public boolean hasDigitalValue(IOLine ioLine) { 345 return digitalValuesMap.containsKey(ioLine); 346 } 347 348 /** 349 * Returns the digital values map. 350 * 351 * <p>To verify if this sample contains a valid digital values, use the 352 * method {@code hasDigitalValues()}.</p> 353 * 354 * <pre> 355 * {@code 356 * if (ioSample.hasDigitalValues()) { 357 * HashMap<IOLine, IOValue> values = ioSample.getDigitalValues(); 358 * ... 359 * } else { 360 * ... 361 * } 362 * } 363 * </pre> 364 * 365 * @return {@code HashMap} with the digital value of each configured IO 366 * line. 367 * 368 * @see #getDigitalValue(IOLine) 369 * @see #hasDigitalValues() 370 * @see IOLine 371 * @see IOValue 372 */ 373 public HashMap<IOLine, IOValue> getDigitalValues() { 374 return (HashMap<IOLine, IOValue>) digitalValuesMap.clone(); 375 } 376 377 /** 378 * Returns the digital value of the provided IO line. 379 * 380 * <p>To verify if this sample contains a digital value for the given 381 * {@code IOLine}, use the method {@code hasDigitalValue(IOLine)}.</p> 382 * 383 * <pre> 384 * {@code 385 * if (ioSample.hasDigitalValue(IOLine.DIO0_AD0)) { 386 * IOValue value = ioSample.getDigitalValue(IOLine.DIO0_AD0); 387 * ... 388 * } else { 389 * ... 390 * } 391 * } 392 * </pre> 393 * 394 * @param ioLine The IO line to get its digital value. 395 * 396 * @return The {@code IOValue} of the given IO line or {@code null} if the 397 * IO sample does not contain a digital value for the given IO line. 398 * 399 * @see #getDigitalValues() 400 * @see #hasDigitalValues() 401 * @see IOLine 402 * @see IOValue 403 */ 404 public IOValue getDigitalValue(IOLine ioLine) { 405 if (!digitalValuesMap.containsKey(ioLine)) 406 return null; 407 return digitalValuesMap.get(ioLine); 408 } 409 410 /** 411 * Returns the analog mask. 412 * 413 * @return Analog mask. 414 */ 415 public int getAnalogMask() { 416 return analogMask; 417 } 418 419 /** 420 * Returns whether or not the {@code IOSample} has analog values. 421 * 422 * @return {@code true} if there are analog values, {@code false} otherwise. 423 * 424 * @see #getAnalogValue(IOLine) 425 * @see #getAnalogValues() 426 * @see #hasAnalogValue(IOLine) 427 * @see IOLine 428 */ 429 public boolean hasAnalogValues() { 430 return analogValuesMap.size() > 0; 431 } 432 433 /** 434 * Returns whether or not the given IO line has an analog value. 435 * 436 * @param ioLine The IO line to check if has an analog value. 437 * 438 * @return {@code true} if the given IO line has an analog value, 439 * {@code false} otherwise. 440 * 441 * @see #getAnalogValue(IOLine) 442 * @see #getAnalogValues() 443 * @see #hasAnalogValues() 444 * @see IOLine 445 */ 446 public boolean hasAnalogValue(IOLine ioLine) { 447 return analogValuesMap.containsKey(ioLine); 448 } 449 450 /** 451 * Returns the analog values map. 452 * 453 * <p>To verify if this sample contains a valid analog values, use the 454 * method {@code hasAnalogValues()}.</p> 455 * 456 * <pre> 457 * {@code 458 * if (ioSample.hasAnalogValues()) { 459 * HashMap<IOLine, Integer> values = ioSample.getAnalogValues(); 460 * ... 461 * } else { 462 * ... 463 * } 464 * } 465 * </pre> 466 * 467 * @return {@code HashMap} with the analog value of each configured IO 468 * line. 469 * 470 * @see #getAnalogValue(IOLine) 471 * @see #hasAnalogValue(IOLine) 472 * @see #hasAnalogValues() 473 * @see IOLine 474 */ 475 public HashMap<IOLine, Integer> getAnalogValues() { 476 return (HashMap<IOLine, Integer>) analogValuesMap.clone(); 477 } 478 479 /** 480 * Returns the analog value of the provided IO line. 481 * 482 * <p>To verify if this sample contains an analog value for the given 483 * {@code IOLine}, use the method {@code hasAnalogValue(IOLine)}.</p> 484 * 485 * <pre> 486 * {@code 487 * if (ioSample.hasAnalogValue(IOLine.DIO0_AD0)) { 488 * Integer value = ioSample.getAnalogValue(IOLine.DIO0_AD0); 489 * ... 490 * } else { 491 * ... 492 * } 493 * } 494 * </pre> 495 * 496 * @param ioLine The IO line to get its analog value. 497 * 498 * @return The analog value of the given IO line or {@code null} if the 499 * IO sample does not contain an analog value for the given IO line. 500 * 501 * @see #getAnalogValues() 502 * @see #hasAnalogValue(IOLine) 503 * @see #hasAnalogValues() 504 * @see IOLine 505 */ 506 public Integer getAnalogValue(IOLine ioLine) { 507 if (!analogValuesMap.containsKey(ioLine)) 508 return null; 509 return analogValuesMap.get(ioLine); 510 } 511 512 /** 513 * Returns whether or not the IOSample has power supply value. 514 * 515 * @return {@code true} if the IOSample has power supply value, 516 * {@code false} otherwise. 517 * 518 * @see #getPowerSupplyValue() 519 */ 520 public boolean hasPowerSupplyValue() { 521 return ByteUtils.isBitEnabled(analogMask, 7); 522 } 523 524 /** 525 * Returns the value of the power supply voltage. 526 * 527 * <p>To verify if this sample contains the power supply voltage, use the 528 * method {@code hasPowerSupplyValue()}.</p> 529 * 530 * <pre> 531 * {@code 532 * if (ioSample.hasPowerSupplyValue()) { 533 * int value = ioSample.getPowerSupplyValue(); 534 * ... 535 * } else { 536 * ... 537 * } 538 * } 539 * </pre> 540 * 541 * @return The value of the power supply voltage. 542 * 543 * @throws OperationNotSupportedException if the IOSample does not have 544 * power supply value. 545 * 546 * @see #hasPowerSupplyValue() 547 */ 548 public int getPowerSupplyValue() throws OperationNotSupportedException { 549 if (!ByteUtils.isBitEnabled(analogMask, 7)) 550 throw new OperationNotSupportedException(); 551 return powerSupplyVoltage; 552 } 553 554 /** 555 * Returns the raw mask of the IO sample. 556 * 557 * @return The raw mask of the IO sample. 558 * 559 * @since 1.3.0 560 */ 561 byte[] getRawMask() { 562 return rawMask.toByteArray(); 563 } 564 565 /** 566 * Returns the raw sample of the IO sample. 567 * 568 * @return The raw sample of the IO sample. 569 * 570 * @since 1.3.0 571 */ 572 byte[] getRawSample() { 573 return rawSample.toByteArray(); 574 } 575 576 /** 577 * Parses the IO samples contained in the given payload. 578 * 579 * @param samplePayload The payload corresponding to one or multiple IO 580 * samples. 581 * 582 * @return List of IO samples. 583 * 584 * @since 1.3.0 585 */ 586 public static ArrayList<IOSample> parseIOSamples(byte[] samplePayload) { 587 ArrayList<IOSample> samples = new ArrayList<IOSample>(); 588 if (samplePayload == null) 589 return samples; 590 591 int nSamples = ByteUtils.byteToInt(samplePayload[0]); 592 593 // Parse the first IO sample. 594 samples.add(new IOSample(samplePayload)); 595 596 if (nSamples > 1) { 597 // Get the mask of the first sample (fixed, the same for all samples). 598 byte[] rawMask = samples.get(0).getRawMask(); 599 // Remove the number of samples and mask from the remaining bytes. 600 byte[] remaining = Arrays.copyOfRange(samplePayload, 1 + rawMask.length, samplePayload.length); 601 for (int i = 1; i < nSamples; i++) { 602 // Get the raw sample of the previous sample. 603 byte[] rawSample = samples.get(i - 1).getRawSample(); 604 // Remove the previous sample from the remaining bytes. 605 remaining = Arrays.copyOfRange(remaining, rawSample.length, remaining.length); 606 // Create the byte array for the next sample. 607 ByteArrayOutputStream nextSampleData = new ByteArrayOutputStream(); 608 try { 609 nextSampleData.write(samplePayload[0]); 610 nextSampleData.write(rawMask); 611 nextSampleData.write(remaining); 612 } catch (IOException e) { 613 e.printStackTrace(); 614 break; 615 } 616 // Parse and add the next IO sample. 617 samples.add(new IOSample(nextSampleData.toByteArray())); 618 } 619 } 620 621 return samples; 622 } 623 624 /* 625 * (non-Javadoc) 626 * @see java.lang.Object#toString() 627 */ 628 @Override 629 public String toString() { 630 StringBuilder sb = new StringBuilder("{"); 631 if (hasDigitalValues()) { 632 for (IOLine line : digitalValuesMap.keySet()) { 633 sb.append("[").append(line).append(": ").append(digitalValuesMap.get(line)).append("], "); 634 } 635 } 636 if (hasAnalogValues()) { 637 for (IOLine line : analogValuesMap.keySet()) { 638 sb.append("[").append(line).append(": ").append(analogValuesMap.get(line)).append("], "); 639 } 640 } 641 if (hasPowerSupplyValue()) { 642 try { 643 sb.append("[").append("Power supply voltage: ").append(getPowerSupplyValue()).append("], "); 644 } catch (OperationNotSupportedException e) {} 645 } 646 647 String s = sb.toString(); 648 if (s.endsWith(", ")) 649 s = s.substring(0, s.length() - 2); 650 return s + "}"; 651 } 652 653 /* 654 * (non-Javadoc) 655 * @see java.lang.Object#equals(java.lang.Object) 656 */ 657 @Override 658 public boolean equals(Object obj) { 659 try { 660 IOSample sample = (IOSample)obj; 661 return Arrays.equals(ioSamplePayload, sample.ioSamplePayload); 662 } catch (ClassCastException e) { 663 return false; 664 } 665 } 666}