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; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023import java.util.concurrent.ConcurrentHashMap; 024 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import com.digi.xbee.api.exceptions.InterfaceNotOpenException; 029import com.digi.xbee.api.exceptions.OperationNotSupportedException; 030import com.digi.xbee.api.exceptions.TimeoutException; 031import com.digi.xbee.api.exceptions.XBeeException; 032import com.digi.xbee.api.listeners.IDiscoveryListener; 033import com.digi.xbee.api.models.DiscoveryOptions; 034import com.digi.xbee.api.models.XBee16BitAddress; 035import com.digi.xbee.api.models.XBee64BitAddress; 036import com.digi.xbee.api.models.XBeeProtocol; 037import com.digi.xbee.api.utils.ByteUtils; 038 039/** 040 * This class represents an XBee Network. 041 * 042 * <p>The network allows the discovery of remote devices in the same network 043 * as the local one and stores them.</p> 044 */ 045public class XBeeNetwork { 046 047 // Variables. 048 049 private AbstractXBeeDevice localDevice; 050 051 private Map<XBee64BitAddress, RemoteXBeeDevice> remotesBy64BitAddr; 052 private Map<XBee16BitAddress, RemoteXBeeDevice> remotesBy16BitAddr; 053 054 private List<IDiscoveryListener> discoveryListeners = new ArrayList<IDiscoveryListener>(); 055 056 private NodeDiscovery nodeDiscovery; 057 058 protected Logger logger; 059 060 /** 061 * Instantiates a new {@code XBeeNetwork} object. 062 * 063 * @param device Local XBee device to get the network from. 064 * 065 * @throws NullPointerException if {@code device == null}. 066 * @throws IllegalArgumentException If {@code xbeeDevice.isRemote() == true}. 067 * 068 * @see AbstractXBeeDevice 069 */ 070 XBeeNetwork(AbstractXBeeDevice device) { 071 if (device == null) 072 throw new NullPointerException("Local XBee device cannot be null."); 073 if (device.isRemote()) 074 throw new IllegalArgumentException("The given local XBee device is remote."); 075 076 localDevice = device; 077 remotesBy64BitAddr = new ConcurrentHashMap<XBee64BitAddress, RemoteXBeeDevice>(); 078 remotesBy16BitAddr = new ConcurrentHashMap<XBee16BitAddress, RemoteXBeeDevice>(); 079 nodeDiscovery = new NodeDiscovery(localDevice); 080 081 logger = LoggerFactory.getLogger(this.getClass()); 082 } 083 084 /** 085 * Discovers and reports the first remote XBee device that matches the 086 * supplied identifier. 087 * 088 * <p>This method blocks until the device is discovered or the configured 089 * timeout expires. To configure the discovery timeout, use the method 090 * {@link #setDiscoveryTimeout(long)}.</p> 091 * 092 * <p>To configure the discovery options, use the 093 * {@link #setDiscoveryOptions(Set)} method.</p> 094 * 095 * @param id The identifier of the device to be discovered. 096 * 097 * @return The discovered remote XBee device with the given identifier, 098 * {@code null} if the timeout expires and the device was not found. 099 * 100 * @throws IllegalArgumentException if {@code id.length() == 0}. 101 * @throws InterfaceNotOpenException if the device is not open. 102 * @throws NullPointerException if {@code id == null}. 103 * @throws XBeeException if there is an error discovering the device. 104 * 105 * @see #discoverDevices(List) 106 * @see #getDevice(String) 107 * @see RemoteXBeeDevice 108 */ 109 public RemoteXBeeDevice discoverDevice(String id) throws XBeeException { 110 if (id == null) 111 throw new NullPointerException("Device identifier cannot be null."); 112 if (id.length() == 0) 113 throw new IllegalArgumentException("Device identifier cannot be an empty string."); 114 115 logger.debug("{}Discovering '{}' device.", localDevice.toString(), id); 116 117 return nodeDiscovery.discoverDevice(id); 118 } 119 120 /** 121 * Discovers and reports all remote XBee devices that match the supplied 122 * identifiers. 123 * 124 * <p>This method blocks until the configured timeout expires. To configure 125 * the discovery timeout, use the method {@link #setDiscoveryTimeout(long)}. 126 * </p> 127 * 128 * <p>To configure the discovery options, use the 129 * {@link #setDiscoveryOptions(Set)} method.</p> 130 * 131 * @param ids List which contains the identifiers of the devices to be 132 * discovered. 133 * 134 * @return A list of the discovered remote XBee devices with the given 135 * identifiers. 136 * 137 * @throws IllegalArgumentException if {@code ids.size() == 0}. 138 * @throws InterfaceNotOpenException if the device is not open. 139 * @throws NullPointerException if {@code ids == null}. 140 * @throws XBeeException if there is an error discovering the devices. 141 * 142 * @see #discoverDevice(String) 143 * @see RemoteXBeeDevice 144 */ 145 public List<RemoteXBeeDevice> discoverDevices(List<String> ids) throws XBeeException { 146 if (ids == null) 147 throw new NullPointerException("List of device identifiers cannot be null."); 148 if (ids.size() == 0) 149 throw new IllegalArgumentException("List of device identifiers cannot be empty."); 150 151 logger.debug("{}Discovering all '{}' devices.", localDevice.toString(), ids.toString()); 152 153 return nodeDiscovery.discoverDevices(ids); 154 } 155 156 /** 157 * Adds the given discovery listener to the list of listeners to be notified 158 * when the discovery process is running. 159 * 160 * <p>If the listener has already been included, this method does nothing. 161 * </p> 162 * 163 * @param listener Listener to be notified when the discovery process is 164 * running. 165 * 166 * @throws NullPointerException if {@code listener == null}. 167 * 168 * @see com.digi.xbee.api.listeners.IDiscoveryListener 169 * @see #removeDiscoveryListener(IDiscoveryListener) 170 */ 171 public void addDiscoveryListener(IDiscoveryListener listener) { 172 if (listener == null) 173 throw new NullPointerException("Listener cannot be null."); 174 175 synchronized (discoveryListeners) { 176 if (!discoveryListeners.contains(listener)) 177 discoveryListeners.add(listener); 178 } 179 } 180 181 /** 182 * Removes the given discovery listener from the list of discovery 183 * listeners. 184 * 185 * <p>If the listener is not included in the list, this method does nothing. 186 * </p> 187 * 188 * @param listener Discovery listener to remove. 189 * 190 * @throws NullPointerException if {@code listener == null}. 191 * 192 * @see com.digi.xbee.api.listeners.IDiscoveryListener 193 * @see #addDiscoveryListener(IDiscoveryListener) 194 */ 195 public void removeDiscoveryListener(IDiscoveryListener listener) { 196 if (listener == null) 197 throw new NullPointerException("Listener cannot be null."); 198 199 synchronized (discoveryListeners) { 200 if (discoveryListeners.contains(listener)) 201 discoveryListeners.remove(listener); 202 } 203 } 204 205 /** 206 * Starts the discovery process with the configured timeout and options. 207 * 208 * <p>To be notified every time an XBee device is discovered, add a 209 * {@code IDiscoveryListener} using the 210 * {@link #addDiscoveryListener(IDiscoveryListener)} method before starting 211 * the discovery process.</p> 212 * 213 * <p>To configure the discovery timeout, use the 214 * {@link #setDiscoveryTimeout(long)} method.</p> 215 * 216 * <p>To configure the discovery options, use the 217 * {@link #setDiscoveryOptions(Set)} method.</p> 218 * 219 * @throws IllegalStateException if the discovery process is already running. 220 * @throws InterfaceNotOpenException if the device is not open. 221 * 222 * @see #addDiscoveryListener(IDiscoveryListener) 223 * @see #stopDiscoveryProcess() 224 */ 225 public void startDiscoveryProcess() { 226 if (isDiscoveryRunning()) 227 throw new IllegalStateException("The discovery process is already running."); 228 229 synchronized (discoveryListeners) { 230 nodeDiscovery.startDiscoveryProcess(discoveryListeners); 231 } 232 } 233 234 /** 235 * Stops the discovery process if it is running. 236 * 237 * <p>Note that DigiMesh/DigiPoint devices are blocked until the discovery 238 * time configured (NT parameter) has elapsed, so if you try to get/set 239 * any parameter during the discovery process you will receive a timeout 240 * exception.</p> 241 * 242 * @see #isDiscoveryRunning() 243 * @see #removeDiscoveryListener(IDiscoveryListener) 244 * @see #startDiscoveryProcess() 245 */ 246 public void stopDiscoveryProcess() { 247 nodeDiscovery.stopDiscoveryProcess(); 248 } 249 250 /** 251 * Retrieves whether the discovery process is running or not. 252 * 253 * @return {@code true} if the discovery process is running, {@code false} 254 * otherwise. 255 * 256 * @see #startDiscoveryProcess() 257 * @see #stopDiscoveryProcess() 258 */ 259 public boolean isDiscoveryRunning() { 260 return nodeDiscovery.isRunning(); 261 } 262 263 /** 264 * Configures the discovery timeout ({@code NT} parameter) with the given 265 * value. 266 * 267 * <p>Note that in some protocols, the discovery process may take longer 268 * than the value set in this method due to the network propagation time. 269 * </p> 270 * 271 * @param timeout New discovery timeout in milliseconds. 272 * 273 * @throws TimeoutException if there is a timeout setting the discovery 274 * timeout. 275 * @throws XBeeException if there is any other XBee related exception. 276 * 277 * @see #setDiscoveryOptions(Set) 278 */ 279 public void setDiscoveryTimeout(long timeout) throws TimeoutException, XBeeException { 280 if (timeout <= 0) 281 throw new IllegalArgumentException("Timeout must be bigger than 0."); 282 283 localDevice.setParameter("NT", ByteUtils.longToByteArray(timeout / 100)); 284 } 285 286 /** 287 * Configures the discovery options ({@code NO} parameter) with the given 288 * value. 289 * 290 * @param options New discovery options. 291 * 292 * @throws TimeoutException if there is a timeout setting the discovery 293 * options. 294 * @throws XBeeException if there is any other XBee related exception. 295 * 296 * @see #setDiscoveryTimeout(long) 297 * @see DiscoveryOptions 298 */ 299 public void setDiscoveryOptions(Set<DiscoveryOptions> options) throws TimeoutException, XBeeException { 300 if (options == null) 301 throw new NullPointerException("Options cannot be null."); 302 303 int value = DiscoveryOptions.calculateDiscoveryValue(localDevice.getXBeeProtocol(), options); 304 localDevice.setParameter("NO", ByteUtils.intToByteArray(value)); 305 } 306 307 /** 308 * Returns all remote devices already contained in the network. 309 * 310 * <p>Note that this method <b>does not perform a discovery</b>, only 311 * returns the devices that have been previously discovered.</p> 312 * 313 * @return A list with all XBee devices in the network. 314 * 315 * @see #getDevices(String) 316 * @see RemoteXBeeDevice 317 */ 318 public List<RemoteXBeeDevice> getDevices() { 319 List<RemoteXBeeDevice> nodes = new ArrayList<RemoteXBeeDevice>(); 320 nodes.addAll(remotesBy64BitAddr.values()); 321 nodes.addAll(remotesBy16BitAddr.values()); 322 return nodes; 323 } 324 325 /** 326 * Returns all remote devices that match the supplied identifier. 327 * 328 * <p>Note that this method <b>does not perform a discovery</b>, only 329 * returns the devices that have been previously discovered.</p> 330 * 331 * @param id The identifier of the devices to be retrieved. 332 * 333 * @return A list of the remote XBee devices contained in the network with 334 * the given identifier. 335 * 336 * @throws IllegalArgumentException if {@code id.length() == 0}. 337 * @throws NullPointerException if {@code id == null}. 338 * 339 * @see #getDevice(String) 340 * @see RemoteXBeeDevice 341 */ 342 public List<RemoteXBeeDevice> getDevices(String id) { 343 if (id == null) 344 throw new NullPointerException("Device identifier cannot be null."); 345 if (id.length() == 0) 346 throw new IllegalArgumentException("Device identifier cannot be an empty string."); 347 348 List<RemoteXBeeDevice> devices = new ArrayList<RemoteXBeeDevice>(); 349 350 // Look in the 64-bit map. 351 for (RemoteXBeeDevice remote : remotesBy64BitAddr.values()) { 352 if (id.equals(remote.getNodeID())) 353 devices.add(remote); 354 } 355 // Look in the 16-bit map. 356 for (RemoteXBeeDevice remote : remotesBy16BitAddr.values()) { 357 if (id.equals(remote.getNodeID())) 358 devices.add(remote); 359 } 360 // Return the list. 361 return devices; 362 } 363 364 /** 365 * Returns the first remote device that matches the supplied identifier. 366 * 367 * <p>Note that this method <b>does not perform a discovery</b>, only 368 * returns the device that has been previously discovered.</p> 369 * 370 * @param id The identifier of the device to be retrieved. 371 * 372 * @return The remote XBee device contained in the network with the given 373 * identifier, {@code null} if the network does not contain any 374 * device with that Node ID. 375 * 376 * @throws IllegalArgumentException if {@code id.length() == 0}. 377 * @throws NullPointerException if {@code id == null}. 378 * 379 * @see #discoverDevice(String) 380 * @see #getDevices(String) 381 * @see RemoteXBeeDevice 382 */ 383 public RemoteXBeeDevice getDevice(String id) { 384 if (id == null) 385 throw new NullPointerException("Device identifier cannot be null."); 386 if (id.length() == 0) 387 throw new IllegalArgumentException("Device identifier cannot be an empty string."); 388 389 // Look in the 64-bit map. 390 for (RemoteXBeeDevice remote : remotesBy64BitAddr.values()) { 391 if (id.equals(remote.getNodeID())) 392 return remote; 393 } 394 // Look in the 16-bit map. 395 for (RemoteXBeeDevice remote : remotesBy16BitAddr.values()) { 396 if (id.equals(remote.getNodeID())) 397 return remote; 398 } 399 // The given ID is not in the network. 400 return null; 401 } 402 403 /** 404 * Returns the remote device already contained in the network whose 64-bit 405 * address matches the given one. 406 * 407 * <p>Note that this method <b>does not perform a discovery</b>, only 408 * returns the device that has been previously discovered.</p> 409 * 410 * @param address The 64-bit address of the device to be retrieved. 411 * 412 * @return The remote device in the network or {@code null} if it is not 413 * found. 414 * 415 * @throws IllegalArgumentException if {@code address.equals(XBee64BitAddress.UNKNOWN_ADDRESS)}. 416 * @throws NullPointerException if {@code address == null}. 417 */ 418 public RemoteXBeeDevice getDevice(XBee64BitAddress address) { 419 if (address == null) 420 throw new NullPointerException("64-bit address cannot be null."); 421 if (address.equals(XBee64BitAddress.UNKNOWN_ADDRESS)) 422 throw new IllegalArgumentException("64-bit address cannot be unknown."); 423 424 logger.debug("{}Getting device '{}' from network.", localDevice.toString(), address); 425 426 return remotesBy64BitAddr.get(address); 427 } 428 429 /** 430 * Returns the remote device already contained in the network whose 16-bit 431 * address matches the given one. 432 * 433 * <p>Note that this method <b>does not perform a discovery</b>, only 434 * returns the device that has been previously discovered.</p> 435 * 436 * @param address The 16-bit address of the device to be retrieved. 437 * 438 * @return The remote device in the network or {@code null} if it is not 439 * found. 440 * 441 * @throws IllegalArgumentException if {@code address.equals(XBee16BitAddress.UNKNOWN_ADDRESS)}. 442 * @throws NullPointerException if {@code address == null}. 443 * @throws OperationNotSupportedException if the protocol of the local XBee device is DigiMesh or Point-to-Multipoint. 444 */ 445 public RemoteXBeeDevice getDevice(XBee16BitAddress address) throws OperationNotSupportedException { 446 if (localDevice.getXBeeProtocol() == XBeeProtocol.DIGI_MESH) 447 throw new OperationNotSupportedException("DigiMesh protocol does not support 16-bit addressing."); 448 if (localDevice.getXBeeProtocol() == XBeeProtocol.DIGI_POINT) 449 throw new OperationNotSupportedException("Point-to-Multipoint protocol does not support 16-bit addressing."); 450 if (address == null) 451 throw new NullPointerException("16-bit address cannot be null."); 452 if (address.equals(XBee16BitAddress.UNKNOWN_ADDRESS)) 453 throw new IllegalArgumentException("16-bit address cannot be unknown."); 454 455 logger.debug("{}Getting device '{}' from network.", localDevice.toString(), address); 456 457 // The preference order is: 458 // 1.- Look in the 64-bit map 459 // 2.- Then in the 16-bit map. 460 // This should be maintained in the 'addRemoteDevice' method. 461 462 RemoteXBeeDevice devInNetwork = null; 463 464 // Look in the 64-bit map. 465 Collection<RemoteXBeeDevice> devices = remotesBy64BitAddr.values(); 466 for (RemoteXBeeDevice d: devices) { 467 XBee16BitAddress a = get16BitAddress(d); 468 if (a != null && a.equals(address)) { 469 devInNetwork = d; 470 break; 471 } 472 } 473 474 // Look in the 16-bit map. 475 if (devInNetwork == null) 476 devInNetwork = remotesBy16BitAddr.get(address); 477 478 return devInNetwork; 479 } 480 481 /** 482 * Adds the given remote device to the network. 483 * 484 * <p>Notice that this operation does not join the remote XBee device to the 485 * network; it just tells the network that it contains that device. However, 486 * the device has only been added to the device list, and may not be 487 * physically in the same network.</p> 488 * 489 * <p>The way of adding a device to the network is based on the 64-bit 490 * address. If it is not configured:</p> 491 * 492 * <ul> 493 * <li>For 802.15.4 and ZigBee devices, it will use the 16-bit address.</li> 494 * <li>For the rest will return {@code false} as the result of the addition.</li> 495 * </ul> 496 * 497 * @param remoteDevice The remote device to be added to the network. 498 * 499 * @return The remote XBee Device instance in the network, {@code null} if 500 * the device could not be successfully added. 501 * 502 * @throws NullPointerException if {@code remoteDevice == null}. 503 * 504 * @see #addRemoteDevices(List) 505 * @see #removeRemoteDevice(RemoteXBeeDevice) 506 * @see RemoteXBeeDevice 507 */ 508 public RemoteXBeeDevice addRemoteDevice(RemoteXBeeDevice remoteDevice) { 509 if (remoteDevice == null) 510 throw new NullPointerException("Remote device cannot be null."); 511 512 logger.debug("{}Adding device '{}' to network.", localDevice.toString(), remoteDevice.toString()); 513 514 RemoteXBeeDevice devInNetwork = null; 515 XBee64BitAddress addr64 = remoteDevice.get64BitAddress(); 516 XBee16BitAddress addr16 = get16BitAddress(remoteDevice); 517 518 // Check if the device has 64-bit address. 519 if (addr64 != null && !addr64.equals(XBee64BitAddress.UNKNOWN_ADDRESS)) { 520 // The device has 64-bit address, so look in the 64-bit map. 521 devInNetwork = remotesBy64BitAddr.get(addr64); 522 if (devInNetwork != null) { 523 // The device exists in the 64-bit map, so update the reference and return it. 524 logger.debug("{}Existing device '{}' in network.", localDevice.toString(), devInNetwork.toString()); 525 devInNetwork.updateDeviceDataFrom(remoteDevice); 526 return devInNetwork; 527 } else { 528 // The device does not exist in the 64-bit map, so check its 16-bit address. 529 if (addr16 != null && !addr16.equals(XBee16BitAddress.UNKNOWN_ADDRESS)) { 530 // The device has 16-bit address, so look in the 16-bit map. 531 devInNetwork = remotesBy16BitAddr.get(addr16); 532 if (devInNetwork != null) { 533 // The device exists in the 16-bit map, so remove it and add it to the 64-bit map. 534 logger.debug("{}Existing device '{}' in network.", localDevice.toString(), devInNetwork.toString()); 535 devInNetwork = remotesBy16BitAddr.remove(addr16); 536 devInNetwork.updateDeviceDataFrom(remoteDevice); 537 remotesBy64BitAddr.put(addr64, devInNetwork); 538 return devInNetwork; 539 } else { 540 // The device does not exist in the 16-bit map, so add it to the 64-bit map. 541 remotesBy64BitAddr.put(addr64, remoteDevice); 542 return remoteDevice; 543 } 544 } else { 545 // The device has not 16-bit address, so add it to the 64-bit map. 546 remotesBy64BitAddr.put(addr64, remoteDevice); 547 return remoteDevice; 548 } 549 } 550 } 551 552 // If the device has not 64-bit address, check if it has 16-bit address. 553 if (addr16 != null && !addr16.equals(XBee16BitAddress.UNKNOWN_ADDRESS)) { 554 // The device has 16-bit address, so look in the 64-bit map. 555 Collection<RemoteXBeeDevice> devices = remotesBy64BitAddr.values(); 556 for (RemoteXBeeDevice d : devices) { 557 XBee16BitAddress a = get16BitAddress(d); 558 if (a != null && a.equals(addr16)) { 559 devInNetwork = d; 560 break; 561 } 562 } 563 // Check if the device exists in the 64-bit map. 564 if (devInNetwork != null) { 565 // The device exists in the 64-bit map, so update the reference and return it. 566 logger.debug("{}Existing device '{}' in network.", localDevice.toString(), devInNetwork.toString()); 567 devInNetwork.updateDeviceDataFrom(remoteDevice); 568 return devInNetwork; 569 } else { 570 // The device does not exist in the 64-bit map, so look in the 16-bit map. 571 devInNetwork = remotesBy16BitAddr.get(addr16); 572 if (devInNetwork != null) { 573 // The device exists in the 16-bit map, so update the reference and return it. 574 logger.debug("{}Existing device '{}' in network.", localDevice.toString(), devInNetwork.toString()); 575 devInNetwork.updateDeviceDataFrom(remoteDevice); 576 return devInNetwork; 577 } else { 578 // The device does not exist in the 16-bit map, so add it. 579 remotesBy16BitAddr.put(addr16, remoteDevice); 580 return remoteDevice; 581 } 582 } 583 } 584 585 // If the device does not contain a valid address, return null. 586 logger.error("{}Remote device '{}' cannot be added: 64-bit and 16-bit addresses must be specified.", 587 localDevice.toString(), remoteDevice.toString()); 588 return null; 589 } 590 591 /** 592 * Adds the given list of remote devices to the network. 593 * 594 * <p>Notice that this operation does not join the remote XBee devices to 595 * the network; it just tells the network that it contains those devices. 596 * However, the devices have only been added to the device list, and may 597 * not be physically in the same network.</p> 598 * 599 * <p>The way of adding a device to the network is based on the 64-bit 600 * address. If it is not configured:</p> 601 * 602 * <ul> 603 * <li>For 802.15.4 and ZigBee devices, the 16-bit address will be used instead.</li> 604 * <li>For the rest will return {@code false} as the result of the addition.</li> 605 * </ul> 606 * 607 * @param list The list of remote devices to be added to the network. 608 * 609 * @return A list with the successfully added devices to the network. 610 * 611 * @throws NullPointerException if {@code list == null}. 612 * 613 * @see #addRemoteDevice(RemoteXBeeDevice) 614 * @see RemoteXBeeDevice 615 */ 616 public List<RemoteXBeeDevice> addRemoteDevices(List<RemoteXBeeDevice> list) { 617 if (list == null) 618 throw new NullPointerException("The list of remote devices cannot be null."); 619 620 List<RemoteXBeeDevice> addedList = new ArrayList<RemoteXBeeDevice>(list.size()); 621 622 if (list.size() == 0) 623 return addedList; 624 625 logger.debug("{}Adding '{}' devices to network.", localDevice.toString(), list.size()); 626 627 for (int i = 0; i < list.size(); i++) { 628 RemoteXBeeDevice toAdd = list.get(i); 629 if (toAdd == null) 630 continue; 631 632 RemoteXBeeDevice d = addRemoteDevice(toAdd); 633 if (d != null) 634 addedList.add(d); 635 } 636 637 return addedList; 638 } 639 640 /** 641 * Removes the given remote XBee device from the network. 642 * 643 * <p>Notice that this operation does not remove the remote XBee device 644 * from the actual XBee network; it just tells the network object that it 645 * will no longer contain that device. However, next time a discovery is 646 * performed, it could be added again automatically.</p> 647 * 648 * <p>This method will check for a device that matches the 64-bit address 649 * of the provided one, if found, that device will be removed from the 650 * corresponding list. In case the 64-bit address is not defined, it will 651 * use the 16-bit address for DigiMesh and ZigBee devices.</p> 652 * 653 * @param remoteDevice The remote device to be removed from the network. 654 * 655 * @throws NullPointerException if {@code RemoteDevice == null}. 656 * 657 * @see #addRemoteDevice(RemoteXBeeDevice) 658 * @see #clearDeviceList() 659 * @see RemoteXBeeDevice 660 */ 661 public void removeRemoteDevice(RemoteXBeeDevice remoteDevice) { 662 if (remoteDevice == null) 663 throw new NullPointerException("Remote device cannot be null."); 664 665 RemoteXBeeDevice devInNetwork = null; 666 667 // Look in the 64-bit map. 668 XBee64BitAddress addr64 = remoteDevice.get64BitAddress(); 669 if (addr64 != null && !addr64.equals(XBee64BitAddress.UNKNOWN_ADDRESS)) { 670 devInNetwork = remotesBy64BitAddr.get(addr64); 671 672 // Remove the device. 673 if (devInNetwork != null) { 674 remotesBy64BitAddr.remove(addr64); 675 return; 676 } 677 } 678 679 // If not found, look in the 16-bit map. 680 XBee16BitAddress addr16 = get16BitAddress(remoteDevice); 681 if (addr16 != null && !addr16.equals(XBee16BitAddress.UNKNOWN_ADDRESS)) { 682 683 // The preference order is: 684 // 1.- Look in the 64-bit map 685 // 2.- Then in the 16-bit map. 686 // This should be maintained in the 'getDeviceBy16BitAddress' method. 687 688 // Look for the 16-bit address in the 64-bit map. 689 Collection<RemoteXBeeDevice> devices = remotesBy64BitAddr.values(); 690 for (RemoteXBeeDevice d: devices) { 691 XBee16BitAddress a = get16BitAddress(d); 692 if (a != null && a.equals(addr16)) { 693 remotesBy64BitAddr.remove(d.get64BitAddress()); 694 return; 695 } 696 } 697 698 // If not found, look for the 16-bit address in the 16-bit map. 699 devInNetwork = remotesBy16BitAddr.get(addr16); 700 701 // Remove the device. 702 if (devInNetwork != null) { 703 remotesBy16BitAddr.remove(addr16); 704 return; 705 } 706 } 707 708 // If the device does not contain a valid address log an error. 709 if ((addr64 == null || addr64.equals(XBee64BitAddress.UNKNOWN_ADDRESS)) 710 && (addr16 == null || addr16.equals(XBee16BitAddress.UNKNOWN_ADDRESS))) 711 logger.error("{}Remote device '{}' cannot be removed: 64-bit and 16-bit addresses must be specified.", 712 localDevice.toString(), remoteDevice.toString()); 713 } 714 715 /** 716 * Removes all the devices from this network. 717 * 718 * <p>The network will be empty after this call returns.</p> 719 * 720 * <p>Notice that this does not imply removing the XBee devices from the 721 * actual XBee network; it just tells the object that the list should be 722 * empty now. Next time a discovery is performed, the list could be filled 723 * with the remote XBee devices found.</p> 724 * 725 * @see #removeRemoteDevice(RemoteXBeeDevice) 726 */ 727 public void clearDeviceList() { 728 logger.debug("{}Clearing the network.", localDevice.toString()); 729 remotesBy64BitAddr.clear(); 730 remotesBy16BitAddr.clear(); 731 } 732 733 /** 734 * Returns the number of devices already discovered in this network. 735 * 736 * @return The number of devices already discovered in this network. 737 */ 738 public int getNumberOfDevices() { 739 return remotesBy64BitAddr.size() + remotesBy16BitAddr.size(); 740 } 741 742 /** 743 * Retrieves the 16-bit address of the given remote device. 744 * 745 * @param device The remote device to get the 16-bit address. 746 * 747 * @return The 16-bit address of the device, {@code null} if it does not 748 * contain a valid one. 749 */ 750 private XBee16BitAddress get16BitAddress(RemoteXBeeDevice device) { 751 if (device == null) 752 return null; 753 754 XBee16BitAddress address = null; 755 756 switch (device.getXBeeProtocol()) { 757 case RAW_802_15_4: 758 address = ((RemoteRaw802Device)device).get16BitAddress(); 759 break; 760 case ZIGBEE: 761 address = ((RemoteZigBeeDevice)device).get16BitAddress(); 762 break; 763 default: 764 // TODO should we allow this operation for general remote devices? 765 address = device.get16BitAddress(); 766 break; 767 } 768 769 return address; 770 } 771 772 /* 773 * (non-Javadoc) 774 * @see java.lang.Object#toString() 775 */ 776 @Override 777 public String toString(){ 778 return getClass().getName() + " [" + localDevice.toString() + "] @" + 779 Integer.toHexString(hashCode()); 780 } 781}