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