From f565f907c7bf16d4416bff060a940403a7c9d0d3 Mon Sep 17 00:00:00 2001 From: Christopher Piggott Date: Mon, 23 Mar 2015 16:59:22 -0400 Subject: [PATCH] Adding a simple logging framework that will let users adapt to any real logger they choose. --- .../autofrog/xbee/api/XbeeMessageParser.java | 1 - .../xbee/api/cache/PlantUmlGenerator.java | 97 ++++++++++++++++++ .../cache/XbeeConcurrentDeviceAddressMap.java | 11 ++- .../xbee/api/cache/XbeeNodeCache.java | 98 +++++++++++++------ .../api/messages/XbeeAddressableMessage.java | 63 ++++++++++++ .../api/messages/XbeeExplicitRxMessage.java | 47 +++++---- .../xbee/api/messages/XbeeNodeDiscovery.java | 37 ++++--- .../messages/XbeeRouteRecordIndicator.java | 47 +++------ .../xbee/api/parsers/XbeeRootParser.java | 15 ++- .../xbee/api/protocol/XbeeApiConstants.java | 26 ++++- .../xbee/api/util/XbeeLogListener.java | 32 ++++++ .../autofrog/xbee/api/util/XbeeLogger.java | 75 ++++++++++++++ 12 files changed, 435 insertions(+), 114 deletions(-) create mode 100644 src/main/java/com/autofrog/xbee/api/cache/PlantUmlGenerator.java create mode 100644 src/main/java/com/autofrog/xbee/api/messages/XbeeAddressableMessage.java create mode 100644 src/main/java/com/autofrog/xbee/api/util/XbeeLogListener.java create mode 100644 src/main/java/com/autofrog/xbee/api/util/XbeeLogger.java diff --git a/src/main/java/com/autofrog/xbee/api/XbeeMessageParser.java b/src/main/java/com/autofrog/xbee/api/XbeeMessageParser.java index 6eea680..bb877d2 100644 --- a/src/main/java/com/autofrog/xbee/api/XbeeMessageParser.java +++ b/src/main/java/com/autofrog/xbee/api/XbeeMessageParser.java @@ -300,7 +300,6 @@ private XbeeMessageBase processByte(byte b) { } catch (Exception e) { /* Notify of some kind of parsing error */ notifyListenersOfException(new XbeeException()); - return null; } finally { rxState = RxState.WAITING_FOR_FLAG; diff --git a/src/main/java/com/autofrog/xbee/api/cache/PlantUmlGenerator.java b/src/main/java/com/autofrog/xbee/api/cache/PlantUmlGenerator.java new file mode 100644 index 0000000..cf06059 --- /dev/null +++ b/src/main/java/com/autofrog/xbee/api/cache/PlantUmlGenerator.java @@ -0,0 +1,97 @@ +package com.autofrog.xbee.api.cache; + +import com.autofrog.xbee.api.messages.XbeeNodeDiscovery; + +import java.util.Map; + +/** + * Generates diagrams and reports from a node database + * + *

+ *

+ * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ */ +public class PlantUmlGenerator { + + private final XbeeConcurrentDeviceAddressMap addressMap; + private final Map discoveries; + + public enum PlantUmlOpts { + SHOW_ALL, + SHOW_NAME, + SHOW_PROFILE, + SHOW_MANUFACTURER, + SHOW_TYPE + } + + public PlantUmlGenerator(XbeeConcurrentDeviceAddressMap addressMap, Map discoveries) { + this.addressMap = addressMap; + this.discoveries = discoveries; + } + + + public String generatePlantUML(PlantUmlOpts... options) { + + /* + * Example PlantUML Output + * + * @startuml + * + * skinparam state { + * BackgroundColor<> lightblue + * BackgroundColor<> red + * BackgroundColor<> yellow + * } + * state 01E3 as "Router 01E3" <> { + * 01E3: Coordinator + * 01E3: 0x00A01020340303FD + * 01E3: Manufacturer: 0x0001 + * 01E3: Profile ID: 0x0001 + * 01E3: Name: xxxHere is its name + * } + * state 01E2 as "Router 01E3" <> { + * 01E2 : Coordinator + * 01E2 : Router + * 01E2 : Manufacturer: 0x0001 + * 01E2 : Profile ID: 0x0001 + * 01E2 : Name: Here is its name + * } + * + * 01E3 --> 01E2 + * + * @enduml + */ + + + StringBuilder sb = new StringBuilder(); + + for (int id : discoveries.keySet()) { + XbeeNodeDiscovery d = discoveries.get(id); + + String name; + + if (d.getDeviceName() != null) { + name = d.getDeviceName(); + } else { + name = String.format("%04X", d.getAddress()); + } + + sb.append(name); + } + + + return sb.toString(); + } + +} diff --git a/src/main/java/com/autofrog/xbee/api/cache/XbeeConcurrentDeviceAddressMap.java b/src/main/java/com/autofrog/xbee/api/cache/XbeeConcurrentDeviceAddressMap.java index d55792d..1ffcaad 100644 --- a/src/main/java/com/autofrog/xbee/api/cache/XbeeConcurrentDeviceAddressMap.java +++ b/src/main/java/com/autofrog/xbee/api/cache/XbeeConcurrentDeviceAddressMap.java @@ -56,7 +56,7 @@ public synchronized void clear() { * * @return copy of this table */ - public synchronized Map getMap() { + public synchronized Map copyMap() { Map temp = new HashMap(); for(int key : forward.keySet()) { temp.put(key, forward.get(key)); @@ -73,7 +73,7 @@ public synchronized Map getMap() { * value is different than the new address being provided this is an indication * that the address has changed. */ - public synchronized int replace(byte[] deviceId, int newAddress) { + public synchronized Integer replace(byte[] deviceId, int newAddress) { Integer oldAddress = null; if (deviceId != null) { @@ -90,8 +90,15 @@ public synchronized int replace(byte[] deviceId, int newAddress) { } if(oldAddress == null) { + /* + * There wasn't an old record, so just put the new one + */ reverse.put(deviceId, newAddress); } else if(oldAddress != newAddress) { + /* + * There was an old record - remove it + */ + reverse.remove(oldAddress); reverse.put(deviceId, newAddress); } else { /* Do nothing - they are the same. */ diff --git a/src/main/java/com/autofrog/xbee/api/cache/XbeeNodeCache.java b/src/main/java/com/autofrog/xbee/api/cache/XbeeNodeCache.java index 5e1fed1..56cac7e 100644 --- a/src/main/java/com/autofrog/xbee/api/cache/XbeeNodeCache.java +++ b/src/main/java/com/autofrog/xbee/api/cache/XbeeNodeCache.java @@ -1,28 +1,33 @@ package com.autofrog.xbee.api.cache; import com.autofrog.xbee.api.listeners.XbeeMessageListener; +import com.autofrog.xbee.api.messages.XbeeAddressableMessage; import com.autofrog.xbee.api.messages.XbeeExplicitRxMessage; import com.autofrog.xbee.api.messages.XbeeNodeDiscovery; import com.autofrog.xbee.api.messages.XbeeRouteRecordIndicator; +import com.autofrog.xbee.api.protocol.XbeeApiConstants; +import com.autofrog.xbee.api.util.XbeeLogListener; +import com.autofrog.xbee.api.util.XbeeLogger; +import com.autofrog.xbee.api.util.XbeeUtilities; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** - * A cache of doscpvered device information. - * + * A cache of discovered device information. + *

* The purpose if this cache is to be able to collect short to long addresses, * routes, and other node information. - * + *

* The Xbee modules (including the coordinator) add device IDs to messages when * possible, but because of memory limitations they cannot do it reliably on very * large networks. - * + *

* Part of he challenge is that the 16 bit address of a device can change under certain * conditions. This includes address conflicts or if a device leaves and rejoins the * network. Since the 16 bit address is not static, it is not a reliable way to identify * a device. - * + *

*

  * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
  *
@@ -60,35 +65,65 @@ public class XbeeNodeCache {
     private final Set unknownAddresses = Collections.synchronizedSet(new HashSet());
 
 
-    /**
-     * Listener of node discovery packets
-     */
-    private final XbeeMessageListener discoveryListener = new XbeeMessageListener() {
-        @Override
-        public void onXbeeMessage(Object sender, XbeeNodeDiscovery msg) {
-            /* TODO: figure out if the device ID could be invalid in a node discovery packet */
-            addressMap.replace(msg.getDeviceId(), msg.getAddress());
+    private final static XbeeLogger log = XbeeLogger.getLogger(XbeeNodeCache.class);
 
-            discoveries.put(msg.getAddress(), msg);
-        }
-    };
+    public XbeeAddressableMessage filter(XbeeAddressableMessage input) {
 
-    /**
-     * Listener of route indication messages
-     */
-    private final XbeeMessageListener routeListener = new XbeeMessageListener() {
-        @Override
-        public void onXbeeMessage(Object sender, XbeeRouteRecordIndicator route) {
-            /* TODO: figure out if the device id can be invalid in a route record indicator.
-             * For example, could it be a null address like 0x0000000000000000 or 0xFFFFFFFFFFFFFFFF
-             * or a broadcast address like 0x000000000000FFFF?
-             */
-            addressMap.replace(route.getDeviceId(), route.getAddress());
+        final int address = input.getAddress();
+        final byte[] deviceId = input.getDeviceId();
 
-            /* TODO: this is NOT safe - the network (short) address can change */
-            routes.put(route.getAddress(), route);
+        final boolean device_id_is_unknown = (Arrays.equals(XbeeApiConstants.UNKNOWN_DEVICE_ID, deviceId));
+        final boolean device_id_is_known = !device_id_is_unknown;
+        final boolean device_id_is_broadcast = (Arrays.equals(XbeeApiConstants.BROADCAST_DEVICE_ID, deviceId));
+
+        final boolean network_address_is_known = (address != XbeeApiConstants.UNKNOWN_ADDR);
+        final boolean network_address_is_broadcast = (address == XbeeApiConstants.BROADCAST_ADDR);
+
+        final boolean is_broadcast = network_address_is_broadcast || device_id_is_broadcast;
+        final boolean is_resolved = device_id_is_known && network_address_is_known;
+        /**
+         * Do not modify broadcasts.
+         */
+        if (is_broadcast) {
+            log.log(XbeeLogListener.Level.TRACE, "this is a broadcast", null);
+            return input;
+        } else if (is_resolved) {
+            addressMap.replace(deviceId, address);
+
+            if (input instanceof XbeeRouteRecordIndicator) {
+                XbeeRouteRecordIndicator route = (XbeeRouteRecordIndicator) input;
+                log.log(XbeeLogListener.Level.DEBUG, "Adding " + route, null);
+                routes.put(route.getAddress(), route);
+            }
+
+            if (input instanceof XbeeNodeDiscovery) {
+                XbeeNodeDiscovery discovery = (XbeeNodeDiscovery) input;
+                log.log(XbeeLogListener.Level.DEBUG, "Adding " + discovery, null);
+                discoveries.put(input.getAddress(), discovery);
+            }
+
+            /*
+             * Since this packet came in fully resolved there's nothing else to do
+             * to it.
+             */
+            return input;
+        } else {
+            /*
+             * This packet did NOT come in fully resolved.
+             */
+            byte[] newDeviceId = addressMap.get(address);
+            if (newDeviceId != null) {
+                log.log(XbeeLogListener.Level.TRACE, "Replacing device id", null);
+                return input.cloneWithNewDeviceId(newDeviceId);
+            } else {
+                /*
+                 * We could not find it - nothing to do but return the original message
+                 */
+                log.log(XbeeLogListener.Level.TRACE, "Doing nothing", null);
+                return input;
+            }
         }
-    };
+    }
 
     /**
      * Listener of route indication messages
@@ -98,7 +133,7 @@ public void onXbeeMessage(Object sender, XbeeRouteRecordIndicator route) {
         public void onXbeeMessage(Object sender, XbeeExplicitRxMessage msg) {
 
             /* TODO: update the address map if the device id has changed */
-            if(msg.getDeviceId() != null) {
+            if (msg.getDeviceId() != null) {
                 addressMap.replace(msg.getDeviceId(), msg.getAddress());
             }
         }
@@ -109,4 +144,5 @@ public XbeeNodeDiscovery get(int networkAddress) {
         return discoveries.get(networkAddress);
     }
 
+
 }
diff --git a/src/main/java/com/autofrog/xbee/api/messages/XbeeAddressableMessage.java b/src/main/java/com/autofrog/xbee/api/messages/XbeeAddressableMessage.java
new file mode 100644
index 0000000..cbc2aa4
--- /dev/null
+++ b/src/main/java/com/autofrog/xbee/api/messages/XbeeAddressableMessage.java
@@ -0,0 +1,63 @@
+package com.autofrog.xbee.api.messages;
+
+/**
+ * 
+ * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ */ +public abstract class XbeeAddressableMessage extends XbeeMessageBase { + protected final byte[] deviceId; + protected final int address; + + + public XbeeAddressableMessage(byte rawFrameType, byte[] deviceId, int address) { + super(rawFrameType); + this.deviceId = deviceId; + this.address = address; + } + + /** + * Get the network (16 bit) address of the device. + * @note The 16 bit address is not static. It can change under certain conditions, + * such as an address conflict or when a device leaves then re-joins the network. + * To properly identify a device use its full deviceId. + * + * @return + */ + public final int getAddress() { + return address; + } + + public final byte[] getDeviceId() { + return deviceId; + } + + + /** + * Messages must implement this - essentially a clone of the original message with + * a new network (16-bit) address. + * @param newDeviceId new address + * @return + */ + protected abstract XbeeAddressableMessage doCloneWithNewDeviceId(byte [] newDeviceId); + + /** + * Return a copy of the object with a modified network address + * @param newDeviceId + * @return + */ + public XbeeAddressableMessage cloneWithNewDeviceId(byte[] newDeviceId) { + return doCloneWithNewDeviceId(newDeviceId); + } + +} diff --git a/src/main/java/com/autofrog/xbee/api/messages/XbeeExplicitRxMessage.java b/src/main/java/com/autofrog/xbee/api/messages/XbeeExplicitRxMessage.java index c98a7c2..ecf2a27 100644 --- a/src/main/java/com/autofrog/xbee/api/messages/XbeeExplicitRxMessage.java +++ b/src/main/java/com/autofrog/xbee/api/messages/XbeeExplicitRxMessage.java @@ -18,10 +18,9 @@ * Lesser General Public License for more details. *
*/ -public class XbeeExplicitRxMessage extends XbeeMessageBase { +public class XbeeExplicitRxMessage extends XbeeAddressableMessage { + - private final byte[] deviceId; - private final int address; private final byte sourceEndpoint; private final byte destEndpoint; private final short profileId; @@ -32,8 +31,8 @@ public class XbeeExplicitRxMessage extends XbeeMessageBase { private final boolean isEndDevice; private final byte[] payload; - public XbeeExplicitRxMessage(byte[] deviceId, - short sourceNetworkAddress, + public XbeeExplicitRxMessage(byte[] deviceId, + int address, byte sourceEndpoint, byte destEndpoint, short profileId, @@ -43,9 +42,7 @@ public XbeeExplicitRxMessage(byte[] deviceId, boolean isEncrypted, boolean isEndDevice, byte[] payload) { - super(XbeeMessageType.EXPLICIT_RX.frameType); - this.deviceId = deviceId; - this.address = sourceNetworkAddress; + super(XbeeMessageType.EXPLICIT_RX.frameType, deviceId, address); this.sourceEndpoint = sourceEndpoint; this.destEndpoint = destEndpoint; this.profileId = profileId; @@ -58,10 +55,8 @@ public XbeeExplicitRxMessage(byte[] deviceId, } public XbeeExplicitRxMessage(XbeeExplicitRxMessage orig, - byte[] deviceId) { - super(XbeeMessageType.EXPLICIT_RX.frameType); - this.deviceId = deviceId; - this.address = orig.address; + byte[] deviceId) { + super(XbeeMessageType.EXPLICIT_RX.frameType, deviceId, orig.address); this.sourceEndpoint = orig.sourceEndpoint; this.destEndpoint = orig.destEndpoint; this.profileId = orig.profileId; @@ -101,24 +96,26 @@ public boolean isEndDevice() { return isEndDevice; } - public byte[] getDeviceId() { - return deviceId; - } public byte getSourceEndpoint() { return sourceEndpoint; } - /** - * Get the network (16 bit) address of the device. - * @note The 16 bit address is not static. It can change under certain conditions, - * such as an address conflict or when a device leaves then re-joins the network. - * To properly identify a device use its full deviceId. - * - * @return - */ - public int getAddress() { - return address; + + @Override + protected XbeeExplicitRxMessage doCloneWithNewDeviceId(byte [] newDeviceId) { + return new XbeeExplicitRxMessage( + newDeviceId, + this.address, + this.sourceEndpoint, + this.destEndpoint, + this.profileId, + this.clusterId, + this.isAck, + this.isBroadcast, + this.isEncrypted, + this.isEndDevice, + this.payload); } public byte[] getPayload() { diff --git a/src/main/java/com/autofrog/xbee/api/messages/XbeeNodeDiscovery.java b/src/main/java/com/autofrog/xbee/api/messages/XbeeNodeDiscovery.java index 1958e39..7a3b193 100644 --- a/src/main/java/com/autofrog/xbee/api/messages/XbeeNodeDiscovery.java +++ b/src/main/java/com/autofrog/xbee/api/messages/XbeeNodeDiscovery.java @@ -4,7 +4,7 @@ /** * Message recei=ved when a node joins the network, or possibly when one is queried - * + *

*

  * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
  *
@@ -19,7 +19,7 @@
  * Lesser General Public License for more details.
  * 
*/ -public class XbeeNodeDiscovery extends XbeeMessageBase { +public class XbeeNodeDiscovery extends XbeeAddressableMessage { /** * Types of devices that can send node discovery messages @@ -41,8 +41,6 @@ public static enum EventType { } - private int address; - private byte[] deviceId; private String deviceName; private int parentDeviceAddress; private DeviceType deviceType; @@ -50,12 +48,10 @@ public static enum EventType { private int profileId; private int manufacturerId; - public XbeeNodeDiscovery(int networkAddress, byte[] deviceId, String deviceName, + public XbeeNodeDiscovery(int address, byte[] deviceId, String deviceName, int parentDeviceAddress, DeviceType deviceType, EventType event, int profileId, int manufacturerId) { - super(XbeeMessageType.NODE_DISCOVERY.frameType); - this.address = networkAddress; - this.deviceId = deviceId; + super(XbeeMessageType.NODE_DISCOVERY.frameType, deviceId, address); this.deviceName = deviceName; this.parentDeviceAddress = parentDeviceAddress; this.deviceType = deviceType; @@ -64,26 +60,17 @@ public XbeeNodeDiscovery(int networkAddress, byte[] deviceId, String deviceName, this.manufacturerId = manufacturerId; } - - public int getAddress() { - return address; - } - - public byte[] getDeviceId() { - return deviceId; - } - public String getDeviceName() { return deviceName; } /** * Get the network (16 bit) address of the parent. + * + * @return * @note The 16 bit address is not static. It can change under certain conditions, * such as an address conflict or when a device leaves then re-joins the network. * To properly identify a device use its full deviceId. - * - * @return */ public int getParentDeviceAddress() { return parentDeviceAddress; @@ -105,6 +92,18 @@ public int getManufacturerId() { return manufacturerId; } + @Override + protected XbeeAddressableMessage doCloneWithNewDeviceId(byte[] newDeviceId) { + return new XbeeNodeDiscovery(address, + newDeviceId, + this.deviceName, + this.parentDeviceAddress, + this.deviceType, + this.event, + this.profileId, + this.manufacturerId); + } + @Override public String toString() { return "XbeeNodeDiscovery{" + diff --git a/src/main/java/com/autofrog/xbee/api/messages/XbeeRouteRecordIndicator.java b/src/main/java/com/autofrog/xbee/api/messages/XbeeRouteRecordIndicator.java index 5ebe00f..2b2a4a6 100644 --- a/src/main/java/com/autofrog/xbee/api/messages/XbeeRouteRecordIndicator.java +++ b/src/main/java/com/autofrog/xbee/api/messages/XbeeRouteRecordIndicator.java @@ -19,10 +19,8 @@ * Lesser General Public License for more details. * */ -public class XbeeRouteRecordIndicator extends XbeeMessageBase { +public class XbeeRouteRecordIndicator extends XbeeAddressableMessage { - private final byte[] deviceId; - private final int address; private final boolean isAck; private final boolean isBroadcast; private final int[] route; @@ -33,34 +31,12 @@ public XbeeRouteRecordIndicator(byte[] deviceId, boolean isAck, boolean isBroadcast, int[] route) { - super(XbeeMessageType.ROUTE_RECORD_INDICATOR.frameType); - this.deviceId = deviceId; - this.address = address; + super(XbeeMessageType.ROUTE_RECORD_INDICATOR.frameType, deviceId, address); this.isAck = isAck; this.isBroadcast = isBroadcast; this.route = route; } - /** - * 64-bit address of the device - * @return - */ - public byte[] getDeviceId() { - return deviceId; - } - - /** - * Get the network (16 bit) address of the device. - * @note The 16 bit address is not static. It can change under certain conditions, - * such as an address conflict or when a device leaves then re-joins the network. - * To properly identify a device use its full deviceId. - * - * @return - */ - public int getAddress() { - return address; - } - public boolean isAck() { return isAck; } @@ -72,14 +48,12 @@ public boolean isBroadcast() { /** * Get the route. * + * @return array of hops, excluding the source and destination. Will be empty if + * the route is direct. * @note The route is a list of 16 bit network address of devices. As with other messages, * this address is not static. It can change under certain conditions, * such as an address conflict or when a device leaves then re-joins the network. * To properly identify a device use its full deviceId. - * - * - * @return array of hops, excluding the source and destination. Will be empty if - * the route is direct. */ public int[] getRoute() { return route; @@ -124,7 +98,7 @@ public String toString() { first = false; } - if(r == 0x0000) { + if (r == 0x0000) { sb.append(String.format("coordinator")); } else { sb.append(String.format("0x%04X", r)); @@ -136,4 +110,15 @@ public String toString() { return sb.toString(); } + + @Override + protected XbeeRouteRecordIndicator doCloneWithNewDeviceId(byte[] newDeviceId) { + return new XbeeRouteRecordIndicator( + newDeviceId, + this.address, + this.isAck, + this.isBroadcast, + this.route + ); + } } diff --git a/src/main/java/com/autofrog/xbee/api/parsers/XbeeRootParser.java b/src/main/java/com/autofrog/xbee/api/parsers/XbeeRootParser.java index 891d27e..dc91b20 100644 --- a/src/main/java/com/autofrog/xbee/api/parsers/XbeeRootParser.java +++ b/src/main/java/com/autofrog/xbee/api/parsers/XbeeRootParser.java @@ -1,6 +1,8 @@ package com.autofrog.xbee.api.parsers; +import com.autofrog.xbee.api.cache.XbeeNodeCache; import com.autofrog.xbee.api.exceptions.XbeeException; +import com.autofrog.xbee.api.messages.XbeeAddressableMessage; import com.autofrog.xbee.api.messages.XbeeMessageBase; import com.autofrog.xbee.api.messages.XbeeUnknownMessage; import com.autofrog.xbee.api.protocol.XbeeMessageType; @@ -11,7 +13,6 @@ import java.util.Map; /** - * *
  * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
  *
@@ -28,8 +29,10 @@
 public class XbeeRootParser {
 
     private final Map parsers;
+    private final XbeeNodeCache cache;
 
     public XbeeRootParser() throws XbeeException {
+        cache = new XbeeNodeCache();
         parsers = new HashMap();
 
         for (XbeeMessageType msgType : EnumSet.allOf(XbeeMessageType.class)) {
@@ -52,8 +55,14 @@ public XbeeRootParser() throws XbeeException {
     public final XbeeMessageBase parse(byte frameType, byte[] bytes) throws XbeeException, IOException {
 
         XbeeMessageParserBase parser = parsers.get(frameType);
-        if(parser != null) {
-            return parser.parse(bytes);
+
+        if (parser != null) {
+            XbeeMessageBase message = parser.parse(bytes);
+
+            if (XbeeAddressableMessage.class.isAssignableFrom(message.getClass())) {
+                message = cache.filter((XbeeAddressableMessage) message);
+            }
+            return message;
         } else {
             return new XbeeUnknownMessage(frameType, bytes);
         }
diff --git a/src/main/java/com/autofrog/xbee/api/protocol/XbeeApiConstants.java b/src/main/java/com/autofrog/xbee/api/protocol/XbeeApiConstants.java
index 9ec9f3d..6eb7939 100644
--- a/src/main/java/com/autofrog/xbee/api/protocol/XbeeApiConstants.java
+++ b/src/main/java/com/autofrog/xbee/api/protocol/XbeeApiConstants.java
@@ -1,7 +1,6 @@
 package com.autofrog.xbee.api.protocol;
 
 /**
- *
  * 
  * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
  *
@@ -46,11 +45,34 @@ public class XbeeApiConstants {
     /**
      * Broadcast address - for sending to all nodes.
      */
-    public final static long ADDR_BROADCAST = 0xFFFF;
+    public final static int BROADCAST_ADDR = 0xFFFF;
+
+    /**
+     * Network address for unknown
+     */
+    public final static int UNKNOWN_ADDR = 0x0000;
 
 
+    /**
+     * Device ID used for broadcasts
+     */
+    public static final byte[] BROADCAST_DEVICE_ID = new byte[]{
+            0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, (byte) 0xFF, (byte) 0xFF
+    };
+
+    /**
+     * Device ID for unknown messages
+     */
+    public static final byte[] UNKNOWN_DEVICE_ID = new byte[]{
+            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+    };
+
     /**
      * Digi's default profile id, used for many of their messages
      */
     public static final short DIGI_PROFILE_ID = (short) (0xC105);
+
 }
diff --git a/src/main/java/com/autofrog/xbee/api/util/XbeeLogListener.java b/src/main/java/com/autofrog/xbee/api/util/XbeeLogListener.java
new file mode 100644
index 0000000..42e83b8
--- /dev/null
+++ b/src/main/java/com/autofrog/xbee/api/util/XbeeLogListener.java
@@ -0,0 +1,32 @@
+package com.autofrog.xbee.api.util;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * An interface for receiving log and debug messages from components of the Xbee library
+ * that can be adapted to any logger of your choosing.
+ *
+ * 

+ *

+ * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ */ +public interface XbeeLogListener { + + public static enum Level { + ALL, DEBUG, ERROR, FATAL, INFO, TRACE, WARN, OFF + } + + public void xbeeLog(String loggerName, Level level, String message, Throwable t); + +} diff --git a/src/main/java/com/autofrog/xbee/api/util/XbeeLogger.java b/src/main/java/com/autofrog/xbee/api/util/XbeeLogger.java new file mode 100644 index 0000000..a5c559d --- /dev/null +++ b/src/main/java/com/autofrog/xbee/api/util/XbeeLogger.java @@ -0,0 +1,75 @@ +package com.autofrog.xbee.api.util; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A simple loggerName for internal components. + * + *

+ *

+ * (C) Copyright 2015 Christopher Piggott (cpiggott@gmail.com)
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * 
+ */ +public class XbeeLogger { + + private final static ConcurrentHashMap loggers = + new ConcurrentHashMap(); + + private final static CopyOnWriteArrayList listeners + = new CopyOnWriteArrayList(); + + private final String loggerName; + + + public static XbeeLogger getLogger(String name) { + if(loggers.containsKey(name) == false) { + loggers.put(name, new XbeeLogger(name)); + } + return loggers.get(name); + } + + + public static XbeeLogger getLogger(Class clazz) { + return getLogger(clazz.getCanonicalName()); + } + + public static XbeeLogger getLogger(Object o) { + return getLogger(o.getClass()); + } + + + private XbeeLogger(String name) { + this.loggerName = name; + } + + public boolean addListener(XbeeLogListener listener) { + return listeners.add(listener); + } + + public boolean removeListener(XbeeLogListener listener) { + return listeners.remove(listener); + } + + public void log(XbeeLogListener.Level level, String message) { + log(level, message, null); + } + + public void log(XbeeLogListener.Level level, + String message, + Throwable exception) { + for(XbeeLogListener listener : listeners) { + listener.xbeeLog(loggerName, level, message, exception); + } + } +}