Пример #1
0
    def start(self):
        self.mainNotificationListenerThread = NotificationListener(
            self, self.PORT)
        self.mainNotificationListenerThread.daemon = True
        self.mainNotificationListenerThread.start()

        self.cacheListenerThread = CacheListener()
        self.cacheListenerThread.daemon = True
        self.cacheListenerThread.start()
Пример #2
0
    def start (self):
        self.mainNotificationListenerThread = NotificationListener(self, self.PORT)
        self.mainNotificationListenerThread.daemon = True
        self.mainNotificationListenerThread.start()

        self.cacheListenerThread = CacheListener()
        self.cacheListenerThread.daemon = True
        self.cacheListenerThread.start()
Пример #3
0
class NotificationHandler:
    def __init__ (self):
        self.lock = threading.RLock() 
        self.PORT = ListenerPort
        self.ignoreSeconds = 5
        self.maxNotifcationsPerNode = 20
        self.mainNotificationListenerThread = None
        self.cacheListenerThread = None
        self.robotLaunchers = []
        self.client = MongoClient('mongodb://localhost:27017/')
        self.nodes = self.client.nodesdb.nodes
        self.notifications = self.client.notificationsdb.notifications
    def getNCB(self, nodeId):
        nodes = self.nodes.find({"nodeId": str(nodeId)})
        if nodes.count() > 0:
            ncbdict = nodes[0]
            ncb = Node()
            ncb.__dict__.update(ncbdict)
        else:
            ncb = Node(str(nodeId))
            self.nodes.insert_one(ncb.__dict__)
        return ncb
    def dump(self):
        report = ""
        report += "Notifications:\n"
        for notification in self.notifications.find():
            report += str(notification)
            report += "\n"
        report += "Nodes:\n"
        for node in self.nodes.find():
            report += str(node)
            report += "\n"
        return report
    def getNodeReport(self, nodeId):
        report = "EMPTY"
        return report 
    def postBatteryValueNotification (self, nodeId, commandClass, fullHex, value):
        with self.lock:
            nodeIdStr = str(nodeId)
            notification = BatteryValueNotification(nodeId, commandClass, fullHex, value)
            logger.info("Created " + str(notification))
            ncb = self.getNCB(nodeId)
            ncb.batteryValue = value
            self.nodes.replace_one({"_id": ncb._id}, ncb.__dict__)
        self.callRobots('battery', notification, None)
    def postWakeupNotification (self, nodeId, commandClass, fullHex, value):
        with self.lock:
            nodeIdStr = str(nodeId)
            notification = WakeupNotification(nodeId, commandClass, fullHex, value)
            logger.info("Created " + str(notification))
            ncb = self.getNCB(nodeId)
            ncb.lastWakeupTime = notification.time
            ncb.wakeupInterval = value
            self.nodes.replace_one({"_id": ncb._id}, ncb.__dict__)
        self.callRobots('wakeup', notification, None)
    def postValueNotification (self, nodeId, commandClass, fullHex, value):
        notification = ValueNotification(nodeId, commandClass, fullHex, value)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)
    def postValueChangeNotification (self, nodeId, commandClass, fullHex, value, previousValue):
        notification = ValueChangeNotification(nodeId, commandClass, fullHex, value, previousValue)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)
    def postNodeEventNotification (self, nodeId, commandClass, fullHex, event):
        notification = NodeEventNotification(nodeId, commandClass, fullHex, event)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)
    def postControlNotification (self, nodeId, notification):
        with self.lock:
            nodeIdStr = str(nodeId)
            previous = self.getLatestNotificationFromNode (nodeIdStr)
            logger.info("Got latest notification to be: " + str(previous))

            if previous is not None:
                diff = notification.time - previous.time
                diffSeconds = diff.total_seconds()
                logger.info("Last notification arrived " + str(diffSeconds) + " seconds ago")

                if (diffSeconds <= self.ignoreSeconds):
                    if (notification.value == previous.value) :
                        logger.info("Ignoring notification: " + str(notification.nid) + " since previous notification with same value came " + str(diffSeconds) + " seconds ago.")
                        notification.ignore = True

                if notification.value == 'False':
                    if previous.value == 'False':
                        # If a sensor is closed, and it is still closed, we
                        # do not really care about it.
                        # However, if a sensor is open, we care about it no
                        # matter what the previous state was.
                        logger.info("Ignoring notification: " + str(notification.nid) + " since previous notification was false and current notification is false")
                        notification.ignore = True
            else:
                logger.info("Making a dummy previous")
                previous = ValueNotification(notification.nodeId, notification.commandClass, notification.fullHex, "False")
                previous.time = datetime.datetime(1970, 1, 1)
                #Insert dummy notification
                self.notifications.insert_one(previous.__dict__)
            print "inserting one: " + str(notification.__dict__)
            self.notifications.insert_one(notification.__dict__)
            ncb = self.getNCB(nodeId)
            ncb.setValue(notification.value)
            self.nodes.replace_one({"_id": ncb._id}, ncb.__dict__)
        #self.callRobots('control', notification, previous)
    def callRobots(self, type, notification, previous):
        # The following code cannot be in the critical section, because it
        # dump and getNodeReport (or any robot apis) can lock.
        if notification.ignore is False:
            r = RobotLauncher(type, notification, previous)
            r.daemon = True
            r.start()
            self.robotLaunchers.append(r)
    def getNotificationFromNodeByIndex (self, nodeId, index, queue = "control"):
        indexInt = int(index)
        nodeIdStr = str(nodeId)
        logger.info("From node(" + nodeIdStr + ") getting notification at index[" + str(index) + "]")
        notifications = self.notifications.find({"nodeId": str(nodeId), "queue": queue})
        notificationdict = notifications.get(indexInt)
        return fromDict(**notificationdict)
    def getNotificationFromNodeById (self, nodeId, notificationId, queue = "control"):
        nodeIdStr = str(nodeId)
        logger.info("From node(" + nodeIdStr + ") getting notification with nid[" + str(notificationId) + "]")
        notifications = self.notifications.find({"nodeId": str(nodeId), "queue": queue, "nid": notificationId})
        notificationdict = notifications.get(0) #There should be exactly one
        return fromDict(**notificationdict)
    def getEarliestNotificationOfCurrentState (self, nodeId, queue = "control"):
        nodeIdStr = str(nodeId)
        logger.info("From node(" + nodeIdStr + ") getting earliest notification of current state")
        topnotification = self.notifications.findOne({"nodeId": str(nodeId), "queue": queue}) # make sure this gets the first one.
        notifications = self.notifications.find({"nodeId": str(nodeId), "queue": queue})
        for n in notifications:
            if n.value == topnotification.value:
                topnotification = n
        return fromDict(**topnotification)
    def getLatestNotificationFromNode (self, nodeId, queue = "control"):
        # Need to reverse sort on time
        notifications = self.notifications.find({"nodeId": str(nodeId), "queue": queue})
        for notification in notifications:
            if (notification["ignore"] == False):
                print str(notification)
                return fromDict(**notification)
        return None
    def getAllNotificationsFromNode(self, nodeId, queue = "control"):
        return list(self.notifications.find({"nodeId": str(nodeId), "queue": queue}))
    def waitForRobotLaunchers (self):
        logger.info("Waiting on any robot launchers")
        [robotLauncher.join() for robotLauncher in self.robotLaunchers]
        logger.info("All robotLaunchers dead!")
    def start (self):
        self.mainNotificationListenerThread = NotificationListener(self, self.PORT)
        self.mainNotificationListenerThread.daemon = True
        self.mainNotificationListenerThread.start()

        self.cacheListenerThread = CacheListener()
        self.cacheListenerThread.daemon = True
        self.cacheListenerThread.start()

    def stop(self):
        logger.info("Stopping NotificationHandler")
        self.waitForRobotLaunchers()

        if self.mainNotificationListenerThread is not None:
            self.mainNotificationListenerThread.stop()
            self.mainNotificationListenerThread.waitForChildren()
            self.mainNotificationListenerThread.join()

        if self.cacheListenerThread is not None:
            self.cacheListenerThread.stop()
            self.cacheListenerThread.join()
        logger.info("NotificationHandler dead.")
Пример #4
0
class NotificationHandler:
    def __init__ (self):
        self.lock = threading.RLock() 
        self.PORT = ListenerPort
        self.ignoreSeconds = 5
        self.maxNotifcationsPerNode = 20
        self.shelf = shelve.open(nhShelfLocation)
        self.mainNotificationListenerThread = None
        self.cacheListenerThread = None
        self.robotLaunchers = []
    def dump(self):
        report = ""
        with self.lock:
            for key in self.shelf.keys():
                ncb = self.shelf.get(key, None)
                if ncb is not None:
                    report += "Node ID: " + nodeId + ": " + getNodeName(nodeId) + "\n"
                    report += "  Control Value: " + str(ncb.value) + "\n"
                    report += "  State: " + str(ncb.state) + "\n"
                    report += "  Battery Level: " + str(ncb.batteryValue) + "\n"
                    report += "  Last Wakeup Time: " + str(ncb.lastWakeupTime) + "\n"
                    report += "  Wakeup Interval: " + str(ncb.wakeupInterval) + "\n"
                    report += "  Control Notifications:\n"
                    for notification in ncb.notifications:
                        report += "    " + str(notification) + "\n"
                    report += "  Battery Notifications:\n"
                    for notification in ncb.batteryNotifications:
                        report += "    " + str(notification) + "\n"
                    report += "  Wakeup Notifications:\n"
                    for notification in ncb.wakeupNotifications:
                        report += "    " + str(notification) + "\n"
        return report
    def getNodeReport(self, nodeId):
        report = ""
        with self.lock:
            ncb = self.shelf.get(nodeId, None)
            if ncb is not None:
                report += "Node ID: " + nodeId + ": " + getNodeName(nodeId) + "\n"
                report += "  Control Value: " + str(ncb.value) + "\n"
                report += "  Battery Level: " + str(ncb.batteryValue) + "\n"
                report += "  Last Wakeup Time: " + str(ncb.lastWakeupTime) + "\n"
                report += "  Wakeup Interval: " + str(ncb.wakeupInterval) + "\n"
                report += "  Control Notifications:\n"
                for notification in ncb.notifications:
                    report += "    " + str(notification) + "\n"
                report += "  Battery Notifications:\n"
                for notification in ncb.batteryNotifications:
                    report += "    " + str(notification) + "\n"
                report += "  Wakeup Notifications:\n"
                for notification in ncb.wakeupNotifications:
                    report += "    " + str(notification) + "\n"
        return report 
    def postBatteryValueNotification (self, nodeId, commandClass, fullHex, value):
        with self.lock:
            nodeIdStr = str(nodeId)
            notification = BatteryValueNotification(nodeId, commandClass, fullHex, value)
            logger.info("Created " + str(notification))
            ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
            l = ncb.batteryNotifications
            # Truncate list to maxNotifcationsPerNode size
            l.insert(0,notification)
            if len(l)>self.maxNotifcationsPerNode:
                ncb.batteryNotifications = l[:self.maxNotifcationsPerNode]

            # Set overall battery value for this node and put it on the shelf
            ncb.batteryValue = value
            self.shelf[nodeIdStr] = ncb
            self.shelf.sync()
        self.callRobots('battery', notification, None)
    def postWakeupNotification (self, nodeId, commandClass, fullHex, value):
        with self.lock:
            nodeIdStr = str(nodeId)
            notification = WakeupNotification(nodeId, commandClass, fullHex, value)
            logger.info("Created " + str(notification))
            ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
            l = ncb.wakeupNotifications
            # Truncate list to maxNotifcationsPerNode size
            l.insert(0,notification)
            if len(l)>self.maxNotifcationsPerNode:
                ncb.wakeupNotifications = l[:self.maxNotifcationsPerNode]

            # Set overall wakeup value for this node and put it on the shelf
            ncb.lastWakeupTime = notification.time
            ncb.wakeupInterval = value
            self.shelf[nodeIdStr] = ncb
            self.shelf.sync()
        self.callRobots('wakeup', notification, None)
    def postValueNotification (self, nodeId, commandClass, fullHex, value):
        notification = ValueNotification(nodeId, commandClass, fullHex, value)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)
    def postValueChangeNotification (self, nodeId, commandClass, fullHex, value, previousValue):
        notification = ValueChangeNotification(nodeId, commandClass, fullHex, value, previousValue)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)
    def postNodeEventNotification (self, nodeId, commandClass, fullHex, event):
        notification = NodeEventNotification(nodeId, commandClass, fullHex, event)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)
    def postControlNotification (self, nodeId, notification):
        with self.lock:
            #Critical Section
            nodeIdStr = str(nodeId)
            previous = self.getLatestNotificationFromNode (nodeIdStr)
            logger.info("Got latest notification to be: " + str(previous))

            if previous is not None:
                diff = notification.time - previous.time
                diffSeconds = diff.total_seconds()
                logger.info("Last notification arrived " + str(diffSeconds) + " seconds ago")

                if (diffSeconds <= self.ignoreSeconds):
                    if (notification.value == previous.value) :
                        logger.info("Ignoring notification: " + str(notification.nid) + " since previous notification with same value came " + str(diffSeconds) + " seconds ago.")
                        notification.ignore = True

                if notification.value == 'False':
                    if previous.value == 'False':
                        # If a sensor is closed, and it is still closed, we
                        # do not really care about it.
                        # However, if a sensor is open, we care about it no
                        # matter what the previous state was.
                        logger.info("Ignoring notification: " + str(notification.nid) + " since previous notification was false and current notification is false")
                        notification.ignore = True
            else:
                logger.info("Making a dummy previous")
                previous = ValueNotification(notification.nodeId, notification.commandClass, notification.fullHex, "False")
                previous.time = datetime.datetime(1970, 1, 1)
                ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
                l = ncb.notifications
                l.insert(0,previous)
                self.shelf[nodeIdStr] = ncb

            ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
            l = ncb.notifications
            # Truncate list to maxNotifcationsPerNode size
            l.insert(0,notification)
            if len(l)>self.maxNotifcationsPerNode:
                ncb.notifications = l[:self.maxNotifcationsPerNode]

            # Set overall control value for this node and put it on the shelf
            ncb.setValue(notification.value)
            self.shelf[nodeIdStr] = ncb
            self.shelf.sync()
            #End critical section
        self.callRobots('control', notification, previous)
    def callRobots(self, type, notification, previous):
        # The following code cannot be in the critical section, because it
        # dump and getNodeReport (or any robot apis) can lock.
        if notification.ignore is False:
            r = RobotLauncher(type, notification, previous)
            r.daemon = True
            r.start()
            self.robotLaunchers.append(r)
    def getNotificationFromNodeByIndex (self, nodeId, index, queue = "control"):
        indexInt = int(index)
        nodeIdStr = str(nodeId)
        logger.info("From node(" + nodeIdStr + ") getting notification at index[" + str(index) + "]")
        ncb = self.shelf.get(nodeIdStr, None)
        if not ncb:
            logger.info("no ncb")
            return None
        else:
            l = None
            if queue == 'battery':
                l = ncb.batteryNotifications
            elif queue == 'wakeup':
                l = ncb.wakeupNotifications
            else:
                l = ncb.notifications
            if indexInt >= len(l):
                logger.info( "length is short: " + str(len(l)))
                return None
            return l[indexInt]
    def getNotificationFromNodeById (self, nodeId, notificationId, queue = "control"):
        nodeIdStr = str(nodeId)
        logger.info("From node(" + nodeIdStr + ") getting notification with nid[" + str(notificationId) + "]")
        ncb = self.shelf.get(nodeIdStr, None)
        if not ncb:
            logger.info("no ncb")
            return None
        else: 
            l = None
            if queue == 'battery':
                l = ncb.batteryNotifications
            elif queue == 'wakeup':
                l = ncb.wakeupNotifications
            else:
                l = ncb.notifications
            for notification in l:
                if notificationId == notification.nid:
                    logger.info("Found notification: " + str(notification))
                    return notification
            return None
    def getEarliestNotificationOfCurrentState (self, nodeId, queue = "control"):
        notification = self.getNotificationFromNodeByIndex(nodeId, 0, queue)
        if notification is not None:
            for i in range(1, self.maxNotifcationsPerNode): 
                n = self.getNotificationFromNodeByIndex(nodeId, i, queue)
                if n and n.value == notification.value:
                    notification = n
                else:
                    break
        return notification
    def getLatestNotificationFromNode (self, nodeId, queue = "control"):
        notification = self.getNotificationFromNodeByIndex(nodeId, 0, queue)
        i = 1
        while (notification is not None) and (notification.ignore is True) and (i < self.maxNotifcationsPerNode):
            notification = self.getNotificationFromNodeByIndex(nodeId, i, queue)
            i += 1
        return notification
    def getAllNotificationsFromNode(self, nodeId, queue = "control"):
        nodeIdStr = str(nodeId)
        ncb = self.shelf.get(nodeIdStr, None)
        if ncb is None:
            return []
        else:
            if queue == 'battery':
                return ncb.batteryNotifications
            elif queue == 'wakeup':
                return ncb.wakeupNotifications
            else:
                return ncb.notifications
    def waitForRobotLaunchers (self):
        logger.info("Waiting on any robot launchers")
        [robotLauncher.join() for robotLauncher in self.robotLaunchers]
        logger.info("All robotLaunchers dead!")
    def start (self):
        self.mainNotificationListenerThread = NotificationListener(self, self.PORT)
        self.mainNotificationListenerThread.daemon = True
        self.mainNotificationListenerThread.start()

        self.cacheListenerThread = CacheListener()
        self.cacheListenerThread.daemon = True
        self.cacheListenerThread.start()

    def stop(self):
        logger.info("Stopping NotificationHandler")
        self.shelf.close()
        self.waitForRobotLaunchers()

        if self.mainNotificationListenerThread is not None:
            self.mainNotificationListenerThread.stop()
            self.mainNotificationListenerThread.waitForChildren()
            self.mainNotificationListenerThread.join()

        if self.cacheListenerThread is not None:
            self.cacheListenerThread.stop()
            self.cacheListenerThread.join()
        logger.info("NotificationHandler dead.")
Пример #5
0
class NotificationHandler:
    def __init__(self):
        self.lock = threading.RLock()
        self.PORT = ListenerPort
        self.ignoreSeconds = 5
        self.maxNotifcationsPerNode = 20
        self.shelf = shelve.open(nhShelfLocation)
        self.mainNotificationListenerThread = None
        self.cacheListenerThread = None
        self.robotLaunchers = []

    def dump(self):
        report = ""
        with self.lock:
            for key in self.shelf.keys():
                ncb = self.shelf.get(key, None)
                if ncb is not None:
                    report += "Node ID: " + nodeId + ": " + getNodeName(
                        nodeId) + "\n"
                    report += "  Control Value: " + str(ncb.value) + "\n"
                    report += "  State: " + str(ncb.state) + "\n"
                    report += "  Battery Level: " + str(
                        ncb.batteryValue) + "\n"
                    report += "  Last Wakeup Time: " + str(
                        ncb.lastWakeupTime) + "\n"
                    report += "  Wakeup Interval: " + str(
                        ncb.wakeupInterval) + "\n"
                    report += "  Control Notifications:\n"
                    for notification in ncb.notifications:
                        report += "    " + str(notification) + "\n"
                    report += "  Battery Notifications:\n"
                    for notification in ncb.batteryNotifications:
                        report += "    " + str(notification) + "\n"
                    report += "  Wakeup Notifications:\n"
                    for notification in ncb.wakeupNotifications:
                        report += "    " + str(notification) + "\n"
        return report

    def getNodeReport(self, nodeId):
        report = ""
        with self.lock:
            ncb = self.shelf.get(nodeId, None)
            if ncb is not None:
                report += "Node ID: " + nodeId + ": " + getNodeName(
                    nodeId) + "\n"
                report += "  Control Value: " + str(ncb.value) + "\n"
                report += "  Battery Level: " + str(ncb.batteryValue) + "\n"
                report += "  Last Wakeup Time: " + str(
                    ncb.lastWakeupTime) + "\n"
                report += "  Wakeup Interval: " + str(
                    ncb.wakeupInterval) + "\n"
                report += "  Control Notifications:\n"
                for notification in ncb.notifications:
                    report += "    " + str(notification) + "\n"
                report += "  Battery Notifications:\n"
                for notification in ncb.batteryNotifications:
                    report += "    " + str(notification) + "\n"
                report += "  Wakeup Notifications:\n"
                for notification in ncb.wakeupNotifications:
                    report += "    " + str(notification) + "\n"
        return report

    def postBatteryValueNotification(self, nodeId, commandClass, fullHex,
                                     value):
        with self.lock:
            nodeIdStr = str(nodeId)
            notification = BatteryValueNotification(nodeId, commandClass,
                                                    fullHex, value)
            logger.info("Created " + str(notification))
            ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
            l = ncb.batteryNotifications
            # Truncate list to maxNotifcationsPerNode size
            l.insert(0, notification)
            if len(l) > self.maxNotifcationsPerNode:
                ncb.batteryNotifications = l[:self.maxNotifcationsPerNode]

            # Set overall battery value for this node and put it on the shelf
            ncb.batteryValue = value
            self.shelf[nodeIdStr] = ncb
            self.shelf.sync()
        self.callRobots('battery', notification, None)

    def postWakeupNotification(self, nodeId, commandClass, fullHex, value):
        with self.lock:
            nodeIdStr = str(nodeId)
            notification = WakeupNotification(nodeId, commandClass, fullHex,
                                              value)
            logger.info("Created " + str(notification))
            ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
            l = ncb.wakeupNotifications
            # Truncate list to maxNotifcationsPerNode size
            l.insert(0, notification)
            if len(l) > self.maxNotifcationsPerNode:
                ncb.wakeupNotifications = l[:self.maxNotifcationsPerNode]

            # Set overall wakeup value for this node and put it on the shelf
            ncb.lastWakeupTime = notification.time
            ncb.wakeupInterval = value
            self.shelf[nodeIdStr] = ncb
            self.shelf.sync()
        self.callRobots('wakeup', notification, None)

    def postValueNotification(self, nodeId, commandClass, fullHex, value):
        notification = ValueNotification(nodeId, commandClass, fullHex, value)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)

    def postValueChangeNotification(self, nodeId, commandClass, fullHex, value,
                                    previousValue):
        notification = ValueChangeNotification(nodeId, commandClass, fullHex,
                                               value, previousValue)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)

    def postNodeEventNotification(self, nodeId, commandClass, fullHex, event):
        notification = NodeEventNotification(nodeId, commandClass, fullHex,
                                             event)
        logger.info("Created " + str(notification))
        self.postControlNotification(nodeId, notification)

    def postControlNotification(self, nodeId, notification):
        with self.lock:
            #Critical Section
            nodeIdStr = str(nodeId)
            previous = self.getLatestNotificationFromNode(nodeIdStr)
            logger.info("Got latest notification to be: " + str(previous))

            if previous is not None:
                diff = notification.time - previous.time
                diffSeconds = diff.total_seconds()
                logger.info("Last notification arrived " + str(diffSeconds) +
                            " seconds ago")

                if (diffSeconds <= self.ignoreSeconds):
                    if (notification.value == previous.value):
                        logger.info(
                            "Ignoring notification: " + str(notification.nid) +
                            " since previous notification with same value came "
                            + str(diffSeconds) + " seconds ago.")
                        notification.ignore = True

                if notification.value == 'False':
                    if previous.value == 'False':
                        # If a sensor is closed, and it is still closed, we
                        # do not really care about it.
                        # However, if a sensor is open, we care about it no
                        # matter what the previous state was.
                        logger.info(
                            "Ignoring notification: " + str(notification.nid) +
                            " since previous notification was false and current notification is false"
                        )
                        notification.ignore = True
            else:
                logger.info("Making a dummy previous")
                previous = ValueNotification(notification.nodeId,
                                             notification.commandClass,
                                             notification.fullHex, "False")
                previous.time = datetime.datetime(1970, 1, 1)
                ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
                l = ncb.notifications
                l.insert(0, previous)
                self.shelf[nodeIdStr] = ncb

            ncb = self.shelf.get(nodeIdStr, NodeControlBlock(nodeIdStr))
            l = ncb.notifications
            # Truncate list to maxNotifcationsPerNode size
            l.insert(0, notification)
            if len(l) > self.maxNotifcationsPerNode:
                ncb.notifications = l[:self.maxNotifcationsPerNode]

            # Set overall control value for this node and put it on the shelf
            ncb.setValue(notification.value)
            self.shelf[nodeIdStr] = ncb
            self.shelf.sync()
            #End critical section
        self.callRobots('control', notification, previous)

    def callRobots(self, type, notification, previous):
        # The following code cannot be in the critical section, because it
        # dump and getNodeReport (or any robot apis) can lock.
        if notification.ignore is False:
            r = RobotLauncher(type, notification, previous)
            r.daemon = True
            r.start()
            self.robotLaunchers.append(r)

    def getNotificationFromNodeByIndex(self, nodeId, index, queue="control"):
        indexInt = int(index)
        nodeIdStr = str(nodeId)
        logger.info("From node(" + nodeIdStr +
                    ") getting notification at index[" + str(index) + "]")
        ncb = self.shelf.get(nodeIdStr, None)
        if not ncb:
            logger.info("no ncb")
            return None
        else:
            l = None
            if queue == 'battery':
                l = ncb.batteryNotifications
            elif queue == 'wakeup':
                l = ncb.wakeupNotifications
            else:
                l = ncb.notifications
            if indexInt >= len(l):
                logger.info("length is short: " + str(len(l)))
                return None
            return l[indexInt]

    def getNotificationFromNodeById(self,
                                    nodeId,
                                    notificationId,
                                    queue="control"):
        nodeIdStr = str(nodeId)
        logger.info("From node(" + nodeIdStr +
                    ") getting notification with nid[" + str(notificationId) +
                    "]")
        ncb = self.shelf.get(nodeIdStr, None)
        if not ncb:
            logger.info("no ncb")
            return None
        else:
            l = None
            if queue == 'battery':
                l = ncb.batteryNotifications
            elif queue == 'wakeup':
                l = ncb.wakeupNotifications
            else:
                l = ncb.notifications
            for notification in l:
                if notificationId == notification.nid:
                    logger.info("Found notification: " + str(notification))
                    return notification
            return None

    def getEarliestNotificationOfCurrentState(self, nodeId, queue="control"):
        notification = self.getNotificationFromNodeByIndex(nodeId, 0, queue)
        if notification is not None:
            for i in range(1, self.maxNotifcationsPerNode):
                n = self.getNotificationFromNodeByIndex(nodeId, i, queue)
                if n and n.value == notification.value:
                    notification = n
                else:
                    break
        return notification

    def getLatestNotificationFromNode(self, nodeId, queue="control"):
        notification = self.getNotificationFromNodeByIndex(nodeId, 0, queue)
        i = 1
        while (notification
               is not None) and (notification.ignore is
                                 True) and (i < self.maxNotifcationsPerNode):
            notification = self.getNotificationFromNodeByIndex(
                nodeId, i, queue)
            i += 1
        return notification

    def getAllNotificationsFromNode(self, nodeId, queue="control"):
        nodeIdStr = str(nodeId)
        ncb = self.shelf.get(nodeIdStr, None)
        if ncb is None:
            return []
        else:
            if queue == 'battery':
                return ncb.batteryNotifications
            elif queue == 'wakeup':
                return ncb.wakeupNotifications
            else:
                return ncb.notifications

    def waitForRobotLaunchers(self):
        logger.info("Waiting on any robot launchers")
        [robotLauncher.join() for robotLauncher in self.robotLaunchers]
        logger.info("All robotLaunchers dead!")

    def start(self):
        self.mainNotificationListenerThread = NotificationListener(
            self, self.PORT)
        self.mainNotificationListenerThread.daemon = True
        self.mainNotificationListenerThread.start()

        self.cacheListenerThread = CacheListener()
        self.cacheListenerThread.daemon = True
        self.cacheListenerThread.start()

    def stop(self):
        logger.info("Stopping NotificationHandler")
        self.shelf.close()
        self.waitForRobotLaunchers()

        if self.mainNotificationListenerThread is not None:
            self.mainNotificationListenerThread.stop()
            self.mainNotificationListenerThread.waitForChildren()
            self.mainNotificationListenerThread.join()

        if self.cacheListenerThread is not None:
            self.cacheListenerThread.stop()
            self.cacheListenerThread.join()
        logger.info("NotificationHandler dead.")