def __init__(self, baseConfig, pInBoundEventQueue, pOutBoundEventQueue, loggingQueue): super(MQTTClientModule, self).__init__() self.config = baseConfig self.alive = True self.inQueue = pInBoundEventQueue # Module config self.moduleConfig = configLoader.load(self.loggingQueue, __name__) # Constants self._keepAlive = self.moduleConfig['MqttKeepAlive'] self._feedName = self.moduleConfig['MqttFeedName'] self._username = self.moduleConfig['MqttUsername'] self._key = self.moduleConfig['MqttKey'] self._host = self.moduleConfig['MqttHost'] self._port = self.moduleConfig['MqttPort'] self._publishJson = self.moduleConfig['MqttPublishJson'] # MQTT setup self._client = mqtt.Client() self._client.username_pw_set(self._username, self._key) self._client.on_connect = self.onConnect self._client.on_disconnect = self.onDisconnect self._client.on_message = self.onMessage self.mqttConnected = False # Logging setup self.logger = ThreadsafeLogger(loggingQueue, "MQTT")
def __init__(self, collectionPointConfig, loggingQueue): # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.rClients = {} #registered clients self.collectionPointConfig = collectionPointConfig #collection point config
def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue): """ Initialize new CollectionModule instance. """ super(CollectionModule, self).__init__(baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue) # Most collection modules will follow a similar pattern... # 1. Set up some variables on the self object # Queues self.outQueue = pOutBoundQueue self.inQueue= pInBoundQueue self.loggingQueue = loggingQueue self.threadProcessQueue = None self.alive = False self.context = None self.reader = None # 2. Load the module's configuration file # Configs self.moduleConfig = configLoader.load(self.loggingQueue, __name__) self.config = baseConfig # 3. Set some constants to the self object from config parameters (if you want) self._id = self.moduleConfig['CollectionPointId'] self._type = self.moduleConfig['CollectionPointType'] self._port = self.moduleConfig['ReaderPortNumber'] # 4. Create a threadsafe logger object self.logger = ThreadsafeLogger(loggingQueue, __name__)
def __init__(self, callbacks, btleConfig, loggingQueue, debugMode=False): super().__init__() # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.alive = True self.callbacks = self.sanitizeCallbacks(callbacks) self.btleConfig = btleConfig # consts self._majorMin = self.btleConfig['BtleAdvertisingMajorMin'] self._majorMax = self.btleConfig['BtleAdvertisingMajorMax'] self._minorMin = self.btleConfig['BtleAdvertisingMinorMin'] self._minorMax = self.btleConfig['BtleAdvertisingMinorMax'] self._uuidFocusList = self.btleConfig['BtleUuidFocusList'] if ('any' in self._uuidFocusList or 'all' in self._uuidFocusList or len(self._uuidFocusList)==0): self.filterOnUuid = False else: self.filterOnUuid = True # self.queue = queue self.device = BluegigaDevice( self.scanCallback, self.btleConfig, self.loggingQueue)
def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue): """ Initialize new CamCollectionPoint instance. Setup queues, variables, configs, constants and loggers. """ super(BtleCollectionPoint, self).__init__() self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) # Queues self.outQueue = pOutBoundQueue # Messages from this thread to the main process self.inQueue = pInBoundQueue self.queueBLE = mp.Queue() # Configs self.moduleConfig = configLoader.load(self.loggingQueue) self.config = baseConfig # Variables and objects self.registeredClientRegistry = RegisteredClientRegistry( self.moduleConfig, self.loggingQueue) self.eventManager = EventManager(self.moduleConfig, pOutBoundQueue, self.registeredClientRegistry, self.loggingQueue) self.alive = True self.btleThread = None self.BLEThread = None self.repeatTimerSweepClients = None # Constants self._cleanupInterval = self.moduleConfig[ 'AbandonedClientCleanupInterval']
def __init__(self, detectedClient, collectionPointConfig, loggingQueue): self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) # Counters and variables self.clientInRangeTrigerCount = 2 self.prevClientInMsgTime = -1 self.prevClientOutMsgTime = -1 self.numClientInRange = 0 self.numClientOutRange = 0 self.timeInCollectionPointInMilliseconds = 0 self.firstRegisteredTime = time.time() self.collectionPointConfig = collectionPointConfig try: self.uidMap = UIDMap() except Exception as e: print('cant instantiate uid map: %s ' % e) # Constants self._rssiClientInThresh = self.collectionPointConfig[ 'BtleRssiClientInThreshold'] self._rssiErrorVar = self.collectionPointConfig[ 'BtleRssiErrorVariance'] self.__clientOutThresholdMin = int(self._rssiClientInThresh + (self._rssiClientInThresh * self._rssiErrorVar)) # Initiate event when client is detected self.handleNewDetectedClientEvent(detectedClient)
def __init__(self, collectionPointConfig, pOutBoundQueue, registeredClientRegistry, loggingQueue): self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.__stats_totalRemoveEvents = 0 self.__stats_totalNewEvents = 0 self.registeredClientRegistry = registeredClientRegistry self.registeredClientRegistry.eventRegisteredClientAdded += self.__newClientRegistered self.registeredClientRegistry.eventRegisteredClientRemoved += self.__removedRegisteredClient self.collectionPointConfig = collectionPointConfig self.outBoundEventQueue = pOutBoundQueue
def __init__(self, clientEventHandler, btleConfig, loggingQueue, debugMode=False): # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.btleConfig = btleConfig self.clientEventHandler = clientEventHandler self.debug = debugMode # define basic BGAPI parser self.bgapi_rx_buffer = [] self.bgapi_rx_expected_length = 0
def __init__(self, scanCallback, btleConfig, loggingQueue, debugMode=False): # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.btleConfig = btleConfig self.scanCallback = scanCallback self.debug = debugMode # define basic BGAPI parser self.bgapi_rx_buffer = [] self.bgapi_rx_expected_length = 0
def __init__(self, kind="KCF", moduleConfig=None, loggingQueue=None): """ Create an initialize new MultiTracker. Set up constants and parameters. """ self.config = moduleConfig self.trackers = [] # List of trackers self.kind = kind self.focus = None self.loggingQueue = loggingQueue # Constants self._useVelocity = self.config['UseVelocity'] self._closestThreshold = self.config["ClosestThreshold"] self._primaryTarget = self.config['PrimaryTarget'] # Setup logging queue self.logger = ThreadsafeLogger(loggingQueue, __name__)
def __init__(self, moduleConfig, pOutBoundQueue, clientRegistry, loggingQueue): self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.__stats_totalRemoveEvents = 0 self.__stats_totalNewEvents = 0 self.clientRegistry = clientRegistry self.clientRegistry.onClientAdded += self.clientRegistered self.clientRegistry.onClientRemoved += self.clientRemoved self.moduleConfig = moduleConfig self.outBoundEventQueue = pOutBoundQueue self.alive = True self._sendClientInMessages = self.moduleConfig['SendClientInMessages'] self._sendClientOutMessages = self.moduleConfig[ 'SendClientOutMessages'] self._sendUpdateMessages = self.moduleConfig['SendUpdateMessages'] self._rssiClientInThresh = self.moduleConfig[ 'BtleRssiClientInThreshold'] self._rssiErrorVar = self.moduleConfig['BtleRssiErrorVariance'] self.__clientOutThresholdMin = int(self._rssiClientInThresh + (self._rssiClientInThresh * self._rssiErrorVar / 2)) if self._sendUpdateMessages: self._updateFPS = self.moduleConfig['UpdateFPS'] self.updateLoopThread = Thread(target=self.updateLoop) self.updateLoopThread.start()
def load(loggingQueue, name): """ Load module specific config into dictionary, return it""" logger = ThreadsafeLogger(loggingQueue, '{0}-{1}'.format(name, 'ConfigLoader')) thisConfig = {} configParser = configparser.ConfigParser() thisConfig = loadSecrets(thisConfig, logger, configParser) thisConfig = loadModule(thisConfig, logger, configParser) return thisConfig
def __init__(self, bbox, frame, kind, moduleConfig, loggingQueue): """ Create and initialize a new Tracker. Set up constants and parameters. """ if kind in ["KCF", "MIL", "MEDIANFLOW", "GOTURN", "TLD", "BOOSTING"]: self.tracker = cv2.Tracker_create(kind) self.tracker.init(frame, (bbox['x'], bbox['y'], bbox['w'], bbox['h'])) self.created = time() self.bbox = (bbox['x'], bbox['y'], bbox['w'], bbox['h']) self.velocity = (0, 0) self.updateTime = self.created self.config = moduleConfig # Constants self._useVelocity = self.config['UseVelocity'] self._horizontalVelocityBuffer = self.config['HorizontalVelocityBuffer'] self._verticalVelocityBuffer = self.config['VerticalVelocityBuffer'] # Setup logging queue self.logger = ThreadsafeLogger(loggingQueue, __name__) else: self.logger.error("Type %s not supported by mTracker" % kind)
def __init__(self, baseConfig, pInBoundEventQueue, pOutBoundEventQueue, loggingQueue): super(WebsocketClientModule, self).__init__() self.alive = True self.config = baseConfig self.inQueue = pInBoundEventQueue # inQueue are messages from the main process to websocket clients self.outQueue = pOutBoundEventQueue # outQueue are messages from clients to main process self.websocketClient = None self.loggingQueue = loggingQueue self.threadProcessQueue = None # Configs self.moduleConfig = configLoader.load(self.loggingQueue, __name__) # Constants self._port = self.moduleConfig['WebsocketPort'] self._host = self.moduleConfig['WebsocketHost'] # logging setup self.logger = ThreadsafeLogger(loggingQueue, __name__)
def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue): """ Initialize new CamCollectionPoint instance. Setup queues, variables, configs, constants and loggers. """ super().__init__() # super().__init__(baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue) self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) # Queues self.outQueue = pOutBoundQueue # Messages from this thread to the main process self.inQueue = pInBoundQueue # Configs self.moduleConfig = configLoader.load(self.loggingQueue, __name__) self.config = baseConfig # Variables and objects self.alive = True self.callbacks = {_ON_SCAN: self.handleBtleClientEvent} self.clientRegistry = ClientRegistry(self.moduleConfig, self.loggingQueue) self.eventManager = EventManager(self.moduleConfig, pOutBoundQueue, self.clientRegistry, self.loggingQueue) # Threads self.btleThread = None self.repeatTimerSweepClients = None self.lastUpdate = datetime.now() # Constants self._cleanupInterval = self.moduleConfig[ 'AbandonedClientCleanupInterval']
def __init__(self, detectionData, collectionPointConfig, loggingQueue): self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, "BtleRegisteredClient") # Counters and variables self.clientInRangeTrigerCount = 1 self.prevClientInMsgTime = None self.prevClientOutMsgTime = None self.numClientInRange=0 self.numClientOutRange=0 self.timeInCollectionPointInMilliseconds = 0 self.firstRegisteredTime = datetime.now() self.collectionPointConfig = collectionPointConfig self.filter = Filter(detectionData.extraData['rssi']) try: self.uidMap = UIDMap() except Exception as e: self.logger.warning('cant instantiate uid map: %s '%e) # Constants self._proximityEventInterval = self.collectionPointConfig['ProximityEventInterval'] self._outClientThreshold = self.collectionPointConfig['BtleClientOutCountThreshold'] self._gatewayType = self.collectionPointConfig['GatewayType'] self._rssiClientInThresh = self.collectionPointConfig['BtleRssiClientInThreshold'] self._rssiErrorVar = self.collectionPointConfig['BtleRssiErrorVariance'] self.__clientOutThresholdMin = int( self._rssiClientInThresh + (self._rssiClientInThresh * self._rssiErrorVar) ) self._clientInThreshType = self.collectionPointConfig['BtleRssiClientInThresholdType'] self._debugEventManager = self.collectionPointConfig['EventManagerDebug'] # Initiate event when client is detected self.handleNewDetectedClientEvent(detectionData)
class WebsocketClientModule(ModuleProcess): def __init__(self, baseConfig, pInBoundEventQueue, pOutBoundEventQueue, loggingQueue): super(WebsocketClientModule, self).__init__() self.alive = True self.config = baseConfig self.inQueue = pInBoundEventQueue # inQueue are messages from the main process to websocket clients self.outQueue = pOutBoundEventQueue # outQueue are messages from clients to main process self.websocketClient = None self.loggingQueue = loggingQueue self.threadProcessQueue = None # Configs self.moduleConfig = configLoader.load(self.loggingQueue, __name__) # Constants self._port = self.moduleConfig['WebsocketPort'] self._host = self.moduleConfig['WebsocketHost'] # logging setup self.logger = ThreadsafeLogger(loggingQueue, __name__) def run(self): """ Main thread entry point. Sets up websocket server and event callbacks. Starts thread to monitor inbound message queue. """ self.logger.info("Starting %s" % __name__) self.connect() def listen(self): self.threadProcessQueue = Thread(target=self.processQueue) self.threadProcessQueue.setDaemon(True) self.threadProcessQueue.start() def connect(self): #websocket.enableTrace(True) ws = websocket.WebSocketApp("ws://%s:%s"%(self._host, self._port), on_message = self.onMessage, on_error = self.onError, on_close = self.onClose) ws.on_open = self.onOpen ws.run_forever() def onError(self, ws, message): self.logger.error("Error callback fired, message: %s"%message) def onClose(self, ws): if self.alive: self.logger.warn("Closed") self.alive = False # TODO: reconnect timer else: self.logger.info("Closed") def onMessage(self, ws, message): self.logger.info("Message from websocket server: %s"%message) # Could put message on the out queue here to handle incoming coms def onOpen(self, ws): self.alive = True self.websocketClient = ws self.listen() def shutdown(self): """ Handle shutdown message. Close and shutdown websocket server. Join queue processing thread. """ self.logger.info("Shutting down %s"%__name__) try: self.logger.info("Closing websocket") self.websocketClient.close() except Exception as e: self.logger.error("Websocket close error : %s " %e) self.alive = False self.threadProcessQueue.join() time.sleep(1) self.exit = True def sendOutMessage(self, message): """ Send message to server """ self.websocketClient.send(json.dumps(message.__dict__)) def processQueue(self): """ Monitor queue of messages from main process to this thread. """ while self.alive: if (self.inQueue.empty() == False): try: message = self.inQueue.get(block=False,timeout=1) if message is not None: if (message.topic.upper()=="SHUTDOWN" and message.sender_id.lower()=="main"): self.logger.debug("SHUTDOWN handled") self.shutdown() else: self.sendOutMessage(message) except Exception as e: self.logger.error("Websocket unable to read queue : %s " %e) else: time.sleep(.25)
class EventManager(object): def __init__(self, collectionPointConfig, pOutBoundQueue, registeredClientRegistry, loggingQueue): self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.__stats_totalRemoveEvents = 0 self.__stats_totalNewEvents = 0 self.registeredClientRegistry = registeredClientRegistry self.registeredClientRegistry.eventRegisteredClientAdded += self.__newClientRegistered self.registeredClientRegistry.eventRegisteredClientRemoved += self.__removedRegisteredClient self.collectionPointConfig = collectionPointConfig self.outBoundEventQueue = pOutBoundQueue def registerDetectedClient(self, detectedClient): self.logger.debug("Registering detected client %s" % detectedClient.extraData["beaconMac"]) eClient = self.registeredClientRegistry.getRegisteredClient( detectedClient.extraData["beaconMac"]) #check for existing if eClient == None: #Newly found client if self.collectionPointConfig['InterfaceType'] == 'btle': rClient = BtleRegisteredClient(detectedClient, self.collectionPointConfig, self.loggingQueue) self.logger.debug("New client with MAC %s found." % detectedClient.extraData["beaconMac"]) if rClient.shouldSendClientInEvent(): self.__sendEventToController(rClient, "clientIn") elif rClient.shouldSendClientOutEvent(): self.logger.debug( "########################################## SENDING CLIENT OUT eClient ##########################################" ) self.__sendEventToController(rClient, "clientOut") self.registeredClientRegistry.addNewRegisteredClient(rClient) else: eClient.updateWithNewDetectedClientData(detectedClient) if eClient.shouldSendClientInEvent(): #self.logger.debug("########################################## SENDING CLIENT IN ##########################################") self.__sendEventToController(eClient, "clientIn") elif eClient.shouldSendClientOutEvent(): self.logger.debug( "########################################## SENDING CLIENT OUT rClient ##########################################" ) self.__sendEventToController(eClient, "clientOut") self.registeredClientRegistry.updateRegisteredClient(eClient) def registerClients(self, detectedClients): for detectedClient in detectedClients: self.registerDetectedClient(detectedClient) def getEventAuditData(self): """Returns a dict with the total New and Remove events the engine has seen since startup""" return { 'NewEvents': self.__stats_totalNewEvents, 'RemoveEvents': self.__stats_totalRemoveEvents } def __newClientRegistered(self, sender, registeredClient): self.logger.debug( "######### NEW CLIENT REGISTERED %s #########" % registeredClient.detectedClient.extraData["beaconMac"]) #we dont need to count for ever and eat up all the memory if self.__stats_totalNewEvents > 1000000: self.__stats_totalNewEvents = 0 else: self.__stats_totalNewEvents += 1 def __removedRegisteredClient(self, sender, registeredClient): self.logger.debug( "######### REGISTERED REMOVED %s #########" % registeredClient.detectedClient.extraData["beaconMac"]) if registeredClient.sweepShouldSendClientOutEvent(): self.__sendEventToController(registeredClient, "clientOut") #we dont need to count for ever and eat up all the memory if self.__stats_totalRemoveEvents > 1000000: self.__stats_totalRemoveEvents = 0 else: self.__stats_totalRemoveEvents += 1 def __sendEventToController(self, registeredClient, eventType): eventMessage = Message( topic=eventType, sender_id=self.collectionPointConfig['CollectionPointId'], sender_type=self.collectionPointConfig['GatewayType'], extended_data=registeredClient.getExtendedDataForEvent(), timestamp=registeredClient.lastRegisteredTime) if eventType == 'clientIn': registeredClient.setClientInMessageSentToController() elif eventType == 'clientOut': registeredClient.setClientOutMessageSentToController() #update reg self.registeredClientRegistry.updateRegisteredClient(registeredClient) self.outBoundEventQueue.put(eventMessage)
class BtleThreadCollectionPoint(object): def __init__(self, clientEventHandler, btleConfig, loggingQueue, debugMode=False): # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.btleConfig = btleConfig self.clientEventHandler = clientEventHandler self.debug = debugMode # define basic BGAPI parser self.bgapi_rx_buffer = [] self.bgapi_rx_expected_length = 0 def start(self): packet_mode = False # create BGLib object self.ble = BGLib() self.ble.packet_mode = packet_mode self.ble.debug = self.debug # add handler for BGAPI timeout condition (hopefully won't happen) self.ble.on_timeout += self.my_timeout # on busy hander self.ble.on_busy = self.on_busy # add handler for the gap_scan_response event self.ble.ble_evt_gap_scan_response += self.clientEventHandler # create serial port object and flush buffers self.logger.info( "Establishing serial connection to BLED112 on com port %s at baud rate %s" % (self.btleConfig['BtleDeviceId'], self.btleConfig['BtleDeviceBaudRate'])) self.serial = Serial(port=self.btleConfig['BtleDeviceId'], baudrate=self.btleConfig['BtleDeviceBaudRate'], timeout=1) self.serial.flushInput() self.serial.flushOutput() # disconnect if we are connected already self.ble.send_command(self.serial, self.ble.ble_cmd_connection_disconnect(0)) self.ble.check_activity(self.serial, 1) # stop advertising if we are advertising already self.ble.send_command(self.serial, self.ble.ble_cmd_gap_set_mode(0, 0)) self.ble.check_activity(self.serial, 1) # stop scanning if we are scanning already self.ble.send_command(self.serial, self.ble.ble_cmd_gap_end_procedure()) self.ble.check_activity(self.serial, 1) # set the TX # range 0 to 15 (real TX power from -23 to +3dBm) #self.ble.send_command(self.serial, self.ble.ble_cmd_hardware_set_txpower(self.btleConfig['btleDeviceTxPower'])) #self.ble.check_activity(self.serial,1) #ble_cmd_connection_update connection: 0 (0x00) interval_min: 30 (0x001e) interval_max: 46 (0x002e) latency: 0 (0x0000) timeout: 100 (0x0064) #interval_min 6-3200 #interval_man 6-3200 #latency 0-500 #timeout 10-3200 self.ble.send_command( self.serial, self.ble.ble_cmd_connection_update(0x00, 0x001e, 0x002e, 0x0000, 0x0064)) self.ble.check_activity(self.serial, 1) # set scan parameters #scan_interval 0x4 - 0x4000 #Scan interval defines the interval when scanning is re-started in units of 625us # Range: 0x4 - 0x4000 # Default: 0x4B (75ms) # After every scan interval the scanner will change the frequency it operates at # at it will cycle through all the three advertisements channels in a round robin # fashion. According to the Bluetooth specification all three channels must be # used by a scanner. # #scan_window 0x4 - 0x4000 # Scan Window defines how long time the scanner will listen on a certain # frequency and try to pick up advertisement packets. Scan window is defined # as units of 625us # Range: 0x4 - 0x4000 # Default: 0x32 (50 ms) # Scan windows must be equal or smaller than scan interval # If scan window is equal to the scan interval value, then the Bluetooth module # will be scanning at a 100% duty cycle. # If scan window is half of the scan interval value, then the Bluetooth module # will be scanning at a 50% duty cycle. # #active 1=active 0=passive # 1: Active scanning is used. When an advertisement packet is received the # Bluetooth stack will send a scan request packet to the advertiser to try and # read the scan response data. # 0: Passive scanning is used. No scan request is made. #self.ble.send_command(self.serial, self.ble.ble_cmd_gap_set_scan_parameters(0x4B,0x32,1)) self.ble.send_command( self.serial, self.ble.ble_cmd_gap_set_scan_parameters(0xC8, 0xC8, 0)) self.ble.check_activity(self.serial, 1) # start scanning now self.ble.send_command(self.serial, self.ble.ble_cmd_gap_discover(1)) self.ble.check_activity(self.serial, 1) # handler to notify of an API parser timeout condition def my_timeout(self, sender, args): self.logger.error( "BGAPI timed out. Make sure the BLE device is in a known/idle state." ) # might want to try the following lines to reset, though it probably # wouldn't work at this point if it's already timed out: self.ble.send_command(self.serial, self.ble.ble_cmd_system_reset(0)) self.ble.check_activity(self.serial, 1) self.ble.send_command(self.serial, self.ble.ble_cmd_gap_discover(1)) self.ble.check_activity(self.serial, 1) def on_busy(self, sender, args): self.logger.warn("BGAPI device is busy.") def scan(self): # check for all incoming data (no timeout, non-blocking) self.ble.check_activity(self.serial)
class BtleCollectionPoint(ModuleProcess): def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue): """ Initialize new CamCollectionPoint instance. Setup queues, variables, configs, constants and loggers. """ super(BtleCollectionPoint, self).__init__() self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) # Queues self.outQueue = pOutBoundQueue # Messages from this thread to the main process self.inQueue = pInBoundQueue self.queueBLE = mp.Queue() # Configs self.moduleConfig = configLoader.load(self.loggingQueue) self.config = baseConfig # Variables and objects self.registeredClientRegistry = RegisteredClientRegistry( self.moduleConfig, self.loggingQueue) self.eventManager = EventManager(self.moduleConfig, pOutBoundQueue, self.registeredClientRegistry, self.loggingQueue) self.alive = True self.btleThread = None self.BLEThread = None self.repeatTimerSweepClients = None # Constants self._cleanupInterval = self.moduleConfig[ 'AbandonedClientCleanupInterval'] def run(self): ###Pausing Startup to wait for things to start after a system restart self.logger.info( "Pausing execution 15 seconds waiting for other system services to start" ) time.sleep(15) self.logger.info( "Done with our nap. Time to start looking for clients") self.btleThread = BlueGigaBtleCollectionPointThread( self.queueBLE, self.moduleConfig, self.loggingQueue) self.BLEThread = Thread(target=self.btleThread.bleDetect, args=(__name__, 10)) self.BLEThread.daemon = True self.BLEThread.start() # Setup repeat task to run the sweep every X interval self.repeatTimerSweepClients = RepeatedTimer( (self._cleanupInterval / 1000), self.registeredClientRegistry.sweepOldClients) # Process queue from main thread for shutdown messages self.threadProcessQueue = Thread(target=self.processQueue) self.threadProcessQueue.setDaemon(True) self.threadProcessQueue.start() #read the queue while self.alive: if not self.queueBLE.empty(): result = self.queueBLE.get(block=False, timeout=1) self.__handleBtleClientEvents(result) def processQueue(self): self.logger.info( "Starting to watch collection point inbound message queue") while self.alive: if not self.inQueue.empty(): self.logger.info("Queue size is %s" % self.inQueue.qsize()) try: message = self.inQueue.get(block=False, timeout=1) if message is not None: if (message.topic == "SHUTDOWN" and message.sender_id == 'main'): self.logger.info("SHUTDOWN command handled on %s" % __name__) self.shutdown() else: self.handleMessage(message) except Exception as e: self.logger.error("Unable to read queue, error: %s " % e) self.shutdown() self.logger.info("Queue size is %s after" % self.inQueue.qsize()) else: time.sleep(.25) def __handleBtleClientEvents(self, detectedClients): for client in detectedClients: self.logger.debug("--- Found client ---") self.logger.debug(vars(client)) self.logger.debug("--- Found client end ---") self.eventManager.registerDetectedClient(client) def handleMessage(self, msg): # Handle incoming messages, eg. from other collection points pass def shutdown(self): self.logger.info("Shutting down") self.repeatTimerSweepClients.stop() self.btleThread.stop() self.alive = False time.sleep(1) self.exit = True
class BtleCollectionPoint(Thread): def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue): """ Initialize new CamCollectionPoint instance. Setup queues, variables, configs, constants and loggers. """ super().__init__() # super().__init__(baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue) self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) # Queues self.outQueue = pOutBoundQueue # Messages from this thread to the main process self.inQueue = pInBoundQueue # Configs self.moduleConfig = configLoader.load(self.loggingQueue, __name__) self.config = baseConfig # Variables and objects self.alive = True self.callbacks = {_ON_SCAN: self.handleBtleClientEvent} self.clientRegistry = ClientRegistry(self.moduleConfig, self.loggingQueue) self.eventManager = EventManager(self.moduleConfig, pOutBoundQueue, self.clientRegistry, self.loggingQueue) # Threads self.btleThread = None self.repeatTimerSweepClients = None self.lastUpdate = datetime.now() # Constants self._cleanupInterval = self.moduleConfig[ 'AbandonedClientCleanupInterval'] def run(self): """ Main thread entrypoint. Sets up and starts the DeviceThread. Loops repeatedly reading incoming messages. """ # Pause for a bit to let things bootup on host machine self.logger.info("Pausing execution 15 seconds" + " waiting for other system services to start") time.sleep(15) self.logger.info("Done with our nap. " + "Time to start looking for clients") # Start device thread, handles IO self.deviceThread = BlueGigaDeviceThread(self.callbacks, self.moduleConfig, self.loggingQueue) self.deviceThread.start() # Setup repeat task to run the sweep every X interval self.repeatTimerSweepClients = RepeatedTimer( (self._cleanupInterval / 1000), self.clientRegistry.sweepOldClients) self.logger.info( "Starting to watch collection point inbound message queue") while self.alive: if not self.inQueue.empty(): self.logger.info("Queue size is %s" % self.inQueue.qsize()) try: message = self.inQueue.get(block=False, timeout=1) if message is not None: if (message.topic == "SHUTDOWN" and message.sender_id == 'main'): self.logger.info("SHUTDOWN command handled on %s" % __name__) self.shutdown() else: self.handleMessage(message) except Exception as e: self.logger.error("Unable to read queue, error: %s " % e) self.shutdown() self.logger.info("Queue size is %s after" % self.inQueue.qsize()) else: time.sleep(.45) def handleBtleClientEvent(self, detectedClient): self.eventManager.registerDetectedClient(detectedClient) def handleMessage(self, msg): # Handle incoming messages, eg. from other collection points pass def killProcess(self, proc, timeout=1): """ Kill a process, given a timeout to join. """ self.logger.info('Joining process: %s' % proc) proc.join() p_sec = 0 for second in range(timeout): if proc.is_alive(): time.sleep(1) p_sec += 1 if p_sec >= timeout: self.logger.info('Terminating process: %s' % proc) proc.terminate() def shutdown(self): self.logger.info("Shutting down") self.repeatTimerSweepClients.stop() self.eventManager.stop() self.deviceThread.stop() # self.killProcess(self.deviceThread) self.alive = False time.sleep(1) self.exit = True
class MultiTracker(object): def __init__(self, kind="KCF", moduleConfig=None, loggingQueue=None): """ Create an initialize new MultiTracker. Set up constants and parameters. """ self.config = moduleConfig self.trackers = [] # List of trackers self.kind = kind self.focus = None self.loggingQueue = loggingQueue # Constants self._useVelocity = self.config['UseVelocity'] self._closestThreshold = self.config["ClosestThreshold"] self._primaryTarget = self.config['PrimaryTarget'] # Setup logging queue self.logger = ThreadsafeLogger(loggingQueue, __name__) def add(self, bbox, frame, kind="KCF"): """ Add new tracker with default type KCF. """ aTracker = Tracker(bbox, frame, kind, self.config, self.loggingQueue) self.trackers.append(aTracker) def removeAt(self, i): """ Remove Tracker at index i. """ self.trackers.pop(i) def remove(self, aTracker): """ Remove tracker provided as parameter. """ self.trackers.remove(aTracker) def update(self, frame): """ Loop through each tracker updating bounding box, keep track of failures. """ bboxes = [] ind = 0 failed = [] for aTracker in self.trackers: ok, bbox = aTracker.update(frame) if not ok: failed.append(ind) else: bboxes.append(bbox) ind += 1 if len(failed) == 0: return True, bboxes, None else: self.logger.error('Failed to update all trackers') return False, bboxes, failed def clear(self): """ Remove all trackers. """ self.trackers.clear() self.focus = None def bboxContainsPt(self, bbox, pt, vBuffer): """ Check if bbox contains pt. Optionally provide velocity buffer to spread containing space. """ if ((bbox['x'] - vBuffer[0] <= pt[0] <= (bbox['x'] + bbox['w'] + vBuffer[0])) and (bbox['y'] - vBuffer[1] <= pt[1] <= (bbox['y'] + bbox['h'] + vBuffer[1]))): return True else: return False def projectedLocationMatches(self, tracker, bbox): """ Check if the velocity of the tracker could put it in the same spot as the bbox. """ if tracker.velocity: return self.bboxContainsPt(bbox, tracker.getProjectedLocation(time()), tracker.getVelocityBuffer()) else: return False def intersects(self, tracker, bbox): """ Check if the bbox and the trackers bounds intersect. """ if (tracker.right() < bbox['x'] or bbox['x'] + bbox['w'] < tracker.left() or tracker.top() < bbox['y'] or bbox['y'] + bbox['h'] < tracker.bottom()): return False # intersection is empty else: return True # intersection is not empty def contains(self, bbox): """ Check if the MultiTracker already has a tracker for the object detected. Uses intersections and projected locations to determine if the tracker overlaps others. This means objects that overlap when first detected will not _both_ be added to the MultiTracker. """ for aTracker in self.trackers: if self._useVelocity: if self.intersects(aTracker, bbox) and self.projectedLocationMatches( aTracker, bbox): return True elif self.intersects(aTracker, bbox): return True return False def length(self): """ Get number of Trackers in the MultiTracker. """ return len(self.trackers) def getFocus(self): """ Get focal object based on primaryTarget configuration. Currently only closest is supported - checks whether there is a tracker that is larger than the previous closest tracker by the configured threshold. """ if self._primaryTarget == "closest": focusChanged = False if self.focus: # area = self.focus.area() area = self.focus.area() else: area = None for aTracker in self.trackers: # If there's no focus or aTracker is larger than focus, and they aren't the same tracker if not self.focus or ( aTracker.area() > area * (1 + (self._closestThreshold / 100)) and self.focus.getCreated() != aTracker.getCreated()): focusChanged = True self.focus = aTracker area = aTracker.area() if focusChanged: return self.focus else: return None elif self._primaryTarget == "closest_engaged": #TODO self.logger.error('Primary Target %s is not implemented.' % self._primaryTarget) return None else: self.logger.error('Primary Target %s is not implemented.' % self._primaryTarget) return None def checkFocus(self): """ Check if focal Tracker has changed by updating the focus. """ focus = self.getFocus() if focus: return True, focus.bbox else: return False, None
class DeviceThread(Thread): """ Controller thread, manage the instance of BluegigaDevice. """ def __init__(self, callbacks, btleConfig, loggingQueue, debugMode=False): super().__init__() # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.alive = True self.callbacks = self.sanitizeCallbacks(callbacks) self.btleConfig = btleConfig # consts self._majorMin = self.btleConfig['BtleAdvertisingMajorMin'] self._majorMax = self.btleConfig['BtleAdvertisingMajorMax'] self._minorMin = self.btleConfig['BtleAdvertisingMinorMin'] self._minorMax = self.btleConfig['BtleAdvertisingMinorMax'] self._uuidFocusList = self.btleConfig['BtleUuidFocusList'] if ('any' in self._uuidFocusList or 'all' in self._uuidFocusList or len(self._uuidFocusList)==0): self.filterOnUuid = False else: self.filterOnUuid = True # self.queue = queue self.device = BluegigaDevice( self.scanCallback, self.btleConfig, self.loggingQueue) def run(self): """ Main thread entry point. Repeatedly call scan() method on device controller BluegigaDevice. Send results or failures back to main thread via callbacks. """ try: self.device.start() except Exception as e: self.logger.error("Unable to connect to BTLE device: %s"%e) self.sendFailureNotice("Unable to connect to BTLE device") self.stop() while self.alive: # try: self.device.scan() # except Exception as e: # self.logger.error("Unable to scan BTLE device: %s"%e) # self.sendFailureNotice("Unable to connect to BTLE device to perform a scan") # self.stop() # don't burden the CPU time.sleep(0.01) def scanCallback(self,sender,args): """ Callback for the scan event on the device controller. Prints the event in a formatted way for tuning purposes. """ #check to make sure there is enough data to be a beacon if len(args["data"]) > 15: try: majorNumber = args["data"][26] | (args["data"][25] << 8) # self.logger.debug("majorNumber=%i"%majorNumber) except: majorNumber = 0 try: minorNumber = args["data"][28] | (args["data"][27] << 8) # self.logger.debug("minorNumber=%i"%minorNumber) except: minorNumber = 0 try: udid = "%s" % ''.join(['%02X' % b for b in args["data"][9:25]]) except: pass if (self.filterOnUuid and udid not in self._uuidFocusList): return if (not (self._majorMin <= majorNumber <= self._majorMax) or not (self._minorMin <= minorNumber <= self._minorMax)): return rssi = args["rssi"] beaconMac = "%s" % ''.join(['%02X' % b for b in args["sender"][::-1]]) if len(args["data"]) > 29: rawTxPower = args["data"][29] else: rawTxPower = 0 if rawTxPower <= 127: txPower = rawTxPower else: txPower = rawTxPower - 256 if self.btleConfig['BtleTestMode']: self.logger.debug("=============================== eventScanResponse START ===============================") #self.logger.debug("self.btleConfig['BtleAdvertisingMinor'] == %i and self.btleConfig['BtleAdvertisingMinor'] == %i "%(majorNumber,minorNumber)) #self.logger.debug("yep, we care about this major and minor so lets create a detected client and pass it to the event manager") self.logger.debug("Major=%s"%majorNumber) self.logger.debug("Minor=%s"%minorNumber) self.logger.debug("UDID=%s"%udid) self.logger.debug("rssi=%s"%rssi) self.logger.debug("beaconMac=%s"%beaconMac) self.logger.debug("txPower=%i"%txPower) self.logger.debug("rawTxPower=%i"%rawTxPower) self.logger.debug("================================= eventScanResponse END =================================") #package it up for sending to the queue detectionData = DetectionData( 'btle', udid=udid, beaconMac=beaconMac, majorNumber=majorNumber, minorNumber=minorNumber, tx=txPower, rssi=rssi) #put it on the queue for the event manager to pick up self.callbacks[_ON_SCAN](detectionData) def sanitizeCallbacks(self, cbs): """ Make sure required callbacks are included and callable. Return only the required callbacks. """ assert(callable(cbs[_ON_SCAN])) if len(cbs) > 1: return [cbs[_ON_SCAN]] return cbs def stop(self): self.alive = False def sendFailureNotice(self, msg): if len(self.btleConfig['SlackChannelWebhookUrl']) > 10: myMsg = ("Help, I've fallen and can't get up! "+ "\n %s. \nSent from %s"%(msg,platform.node())) payload = {'text': myMsg} r = requests.post( self.btleConfig['SlackChannelWebhookUrl'], data = json.dumps(payload))
class CollectionModule(ModuleProcess): # You can keep these parameters the same, all modules receive the same params # self - reference to self # baseConfig - configuration settings defined in /simplesensor/config/base.conf # (https://github.com/AdobeAtAdobe/SimpleSensor/blob/master/config/base.conf) # pInBoundQueue - messages from handler to this module # pOutBoundQueue - messages from this module to other modules # loggingQueue - logging messages for threadsafe logger def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue): """ Initialize new CollectionModule instance. """ super(CollectionModule, self).__init__(baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue) # Most collection modules will follow a similar pattern... # 1. Set up some variables on the self object # Queues self.outQueue = pOutBoundQueue self.inQueue= pInBoundQueue self.loggingQueue = loggingQueue self.threadProcessQueue = None self.alive = False self.context = None self.reader = None # 2. Load the module's configuration file # Configs self.moduleConfig = configLoader.load(self.loggingQueue, __name__) self.config = baseConfig # 3. Set some constants to the self object from config parameters (if you want) self._id = self.moduleConfig['CollectionPointId'] self._type = self.moduleConfig['CollectionPointType'] self._port = self.moduleConfig['ReaderPortNumber'] # 4. Create a threadsafe logger object self.logger = ThreadsafeLogger(loggingQueue, __name__) def run(self): """ Main process method, run when the thread's start() function is called. Starts monitoring inbound messages to this module, and collection logic goes here. For example, you could put a loop with a small delay to keep polling the sensor, etc. When something is detected that's relevant, put a message on the outbound queue. """ # Monitor inbound queue on own thread self.listen() self.alive = True while self.context == None: self.establish_context() while self.alive: while self.reader == None: self.reader = self.get_reader() if self.reader is None: self.logger.info('Waiting for 5 seconds before ' + 'trying to find readers again. Is it plugged in?') time.sleep(5) # connect to card card = self.get_card() if card is None: continue # get block #10 and 11 of card, # contains the attendee ID msg = [0xFF, 0xB0, 0x00, bytes([10])[0], 0x04] chunk_one = self.send_transmission(card, msg) if chunk_one is None: self.reader = None continue msg = [0xFF, 0xB0, 0x00, bytes([11])[0], 0x04] chunk_two = self.send_transmission(card, msg) if chunk_two is None: self.reader = None continue # the id is in B1-3 of block 10 # and B0-2 of block 11 attendee_id_bytes = bytearray(chunk_one[1:4]+chunk_two[0:3]) attendee_id = attendee_id_bytes.decode('UTF-8') xdata = { 'attendee_id': attendee_id } msg = self.build_message(topic='scan_in', extendedData=xdata) self.logger.info('Sending message: {}'.format(msg)) self.put_message(msg) self.reader = None # sleep for a bit to avoid double scanning time.sleep(5) def establish_context(self): hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) if hresult != SCARD_S_SUCCESS: self.logger.error( 'Unable to establish context: {}'.format( SCardGetErrorMessage(hresult))) return self.context = hcontext def get_reader(self): hresult, readers = SCardListReaders(self.context, []) if hresult != SCARD_S_SUCCESS: self.logger.error( 'Failed to list readers: {}'.format( SCardGetErrorMessage(hresult))) return if len(readers)<1 or len(readers)-1<self._port: self.logger.error( 'Not enough readers attached. {} needed, {} attached'.format( (self._port+1), (len(readers)))) return else: return readers[self._port] def get_card(self, mode=None, protocol=None): hresult, hcard, dwActiveProtocol = SCardConnect( self.context, self.reader, mode or SCARD_SHARE_SHARED, protocol or (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) if hresult != SCARD_S_SUCCESS: return else: return hcard def send_transmission(self, card, msg, protocol=None): hresult, response = SCardTransmit( card, protocol or SCARD_PCI_T1, msg) if hresult != SCARD_S_SUCCESS: self.logger.error( 'Failed to send transmission: {}'.format( SCardGetErrorMessage(hresult))) return else: return response[:-2] def listen(self): """ Start thread to monitor inbound messages, declare module alive. """ self.threadProcessQueue = Thread(target=self.process_queue) self.threadProcessQueue.setDaemon(True) self.threadProcessQueue.start() def build_message(self, topic, extendedData={}, recipients=['communication_modules']): """ Create a Message instance. topic (required): message type sender_id (required): id property of original sender sender_type (optional): type of sender, ie. collection point type, module name, hostname, etc extended_data (optional): payload to deliver to recipient(s) recipients (optional): module name, which module(s) the message will be delivered to, ie. `websocket_server`. use an array of strings to define multiple modules to send to. use 'all' to send to all available modules. use 'local_only' to send only to modules with `low_cost` prop set to True. [DEFAULT] use 'communication_modules' to send only to communication modules. use 'collection_modules' to send only to collection modules. """ msg = Message( topic=topic, sender_id=self._id, sender_type=self._type, extended_data=extendedData, recipients=recipients, timestamp=datetime.datetime.utcnow()) return msg def put_message(self, msg): """ Put message onto outgoing queue. """ self.outQueue.put(msg) def process_queue(self): """ Process inbound messages on separate thread. When a message is encountered, trigger an event to handle it. Sleep for some small amount of time to avoid overloading. Also receives a SHUTDOWN message from the main process when the user presses the esc key. """ self.logger.info("Starting to watch collection point inbound message queue") while self.alive: if (self.inQueue.empty() == False): self.logger.info("Queue size is %s" % self.inQueue.qsize()) try: message = self.inQueue.get(block=False,timeout=1) if message is not None: self.handle_message(message) except Exception as e: self.logger.error("Error, unable to read queue: %s " %e) self.shutdown() self.logger.info("Queue size is %s after" % self.inQueue.qsize()) else: time.sleep(.25) def handle_message(self, message): """ Handle messages from other modules to this one. Switch on the message topic, do something with the data fields. """ if message.topic.upper()=='SHUTDOWN' and message.sender_id=='main': self.shutdown() def shutdown(self): """ Shutdown the collection module. Set alive flag to false so it stops looping. Wait for things to die, then exit. """ self.alive = False print("Shutting down nfc_bcard_reader") time.sleep(1) self.exit = True
class RegisteredClientRegistry(object): eventRegisteredClientRemoved = RegistryEvent() eventRegisteredClientAdded = RegistryEvent() eventRegisteredClientUpdated = RegistryEvent() eventSweepComplete = RegistryEvent() def __init__(self, collectionPointConfig, loggingQueue): # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.rClients = {} #registered clients self.collectionPointConfig = collectionPointConfig #collection point config def getRegisteredClient(self, udid): """Get an existing registered client by udid and if its found return it. If no existing registered client is found return None.""" try: eClient = self.rClients[udid] except KeyError: eClient = None return eClient def sweepOldClients(self): """look at the registry and look for expired inactive clients. Returns a list of removed clients""" self.logger.debug("*** Sweeping clients existing count %s***" % len(self.rClients)) clientsToBeRemoved = [] #list of clients to be cleaned up currentExpireTime = time.time() - ( self.collectionPointConfig['AbandonedClientTimeout'] / 1000) for udid in self.rClients: regClient = self.rClients[udid] if regClient.lastRegisteredTime < currentExpireTime: clientsToBeRemoved.append(regClient) for client in clientsToBeRemoved: self.logger.debug("Client sweep removing udid %s" % client.getUdid()) self.removeRegisteredClient(client) self.logger.debug("*** End of sweeping tags existing count %s***" % len(self.rClients)) self.eventSweepComplete(clientsToBeRemoved) return clientsToBeRemoved def addNewRegisteredClient(self, registeredClient): self.logger.debug("in addNewRegisteredClient with %s" % registeredClient.getUdid()) self.rClients[registeredClient.getUdid()] = registeredClient self.eventRegisteredClientAdded(registeredClient) #throw event def updateRegisteredClient(self, registeredClient): self.logger.debug("in updateRegisteredClient with %s" % registeredClient.getUdid()) self.rClients[registeredClient.getUdid()] = registeredClient self.eventRegisteredClientUpdated(registeredClient) #throw event def removeRegisteredClient(self, registeredClient): self.logger.debug("in removeRegisteredClient with %s" % registeredClient.getUdid()) self.rClients.pop(registeredClient.getUdid()) self.eventRegisteredClientRemoved(registeredClient) #throw event
class ClientRegistry(object): onClientRemoved = RegistryEvent() onClientAdded = RegistryEvent() onClientUpdated = RegistryEvent() onSweepComplete = RegistryEvent() def __init__(self,collectionPointConfig,loggingQueue): # Logger self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, __name__) self.rClients = {} #registered clients self.collectionPointConfig = collectionPointConfig def getAll(self, thresh=-68): """ Get registered clients as dict Only return clients above thresh RSSI """ toret = {} for k, v in self.rClients.items(): xdata = v.getExtendedDataForEvent() if xdata['rssi']>thresh: toret[k] = xdata return toret def getUpdateData(self, thresh=-68): return {'nearby': self.getAll(thresh)} def getClient(self,mac): """ Get an existing registered client by mac and if its found return it. If no existing registered client is found return None. """ try: eClient = self.rClients[mac] except KeyError: eClient = None return eClient def sweepOldClients(self): """ Look at the registry and look for expired and inactive clients. Returns a list of removed clients. """ self.logger.debug("*** Sweeping clients existing count" + " %s***"%len(self.rClients)) clientsToBeRemoved=[] #list of clients to be cleaned up clientTimeout = self.collectionPointConfig['AbandonedClientTimeout'] now = datetime.now() for mac in self.rClients: regClient = self.rClients[mac] # self.logger.debug('now-regClient.lastRegisteredTime ---- clientTimeout >0 : %s ---- %s'%(((now-regClient.lastRegisteredTime).total_seconds()*1000), clientTimeout)) # if regClient.sweepShouldSendClientOutEvent(): if (now-regClient.lastRegisteredTime).total_seconds()*1000-clientTimeout>0: clientsToBeRemoved.append(regClient) for client in clientsToBeRemoved: # self.logger.debug("Client sweep removing mac %s"%client.getMac()) self.removeClient(client) self.logger.debug("*** End of sweeping tags existing count "+ "%s***"%len(self.rClients)) self.onSweepComplete(clientsToBeRemoved) return clientsToBeRemoved def addClient(self,client): #self.logger.debug("in addNewRegisteredClient with %s"%client.getUdid()) self.rClients[client.getMac()] = client self.onClientAdded(client) def updateClient(self,client): #self.logger.debug("in updateRegisteredClient with %s"%client.getUdid()) self.rClients[client.getMac()] = client self.onClientUpdated(client) def removeClient(self,client): #self.logger.debug("in removeRegisteredClient with %s"%client.getUdid()) self.logger.info('length before remove: %s'%len(self.rClients)) self.rClients.pop(client.getMac()) self.logger.info('length after remove: %s'%len(self.rClients)) self.onClientRemoved(client)
class MQTTClientModule(ModuleProcess): """ Threaded MQTT client for processing and publishing outbound messages""" def __init__(self, baseConfig, pInBoundEventQueue, pOutBoundEventQueue, loggingQueue): super(MQTTClientModule, self).__init__() self.config = baseConfig self.alive = True self.inQueue = pInBoundEventQueue # Module config self.moduleConfig = configLoader.load(self.loggingQueue, __name__) # Constants self._keepAlive = self.moduleConfig['MqttKeepAlive'] self._feedName = self.moduleConfig['MqttFeedName'] self._username = self.moduleConfig['MqttUsername'] self._key = self.moduleConfig['MqttKey'] self._host = self.moduleConfig['MqttHost'] self._port = self.moduleConfig['MqttPort'] self._publishJson = self.moduleConfig['MqttPublishJson'] # MQTT setup self._client = mqtt.Client() self._client.username_pw_set(self._username, self._key) self._client.on_connect = self.onConnect self._client.on_disconnect = self.onDisconnect self._client.on_message = self.onMessage self.mqttConnected = False # Logging setup self.logger = ThreadsafeLogger(loggingQueue, "MQTT") def onConnect(self, client, userdata, flags, rc): self.logger.debug('MQTT onConnect called') # Result code 0 is success if rc == 0: self.mqttConnected = True # Subscribe to feed here else: self.logger.error('MQTT failed to connect: %s' % rc) raise RuntimeError('MQTT failed to connect: %s' % rc) def onDisconnect(self, client, userdata, rc): self.logger.debug('MQTT onDisconnect called') self.mqttConnected = False if rc != 0: self.logger.debug('MQTT disconnected unexpectedly: %s' % rc) self.handleReconnect(rc) def onMessage(self, client, userdata, msg): self.logger.debug('MQTT onMessage called for client: %s' % client) def connect(self): """ Connect to MQTT broker Skip calling connect if already connected. """ if self.mqttConnected: return self._client.connect(self._host, port=self._port, keepalive=self._keepAlive) def disconnect(self): """ Check if connected""" if self.mqttConnected: self._client.disconnect() def subscribe(self, feed=False): """Subscribe to feed, defaults to feed specified in config""" if not feed: feed = self._feedName self._client.subscribe('{0}/feeds/{1}'.format(self._username, feed)) def publish(self, value, feed=False): """Publish a value to a feed""" if not feed: feed = self._feedName self._client.publish('{0}/feeds/{1}'.format(self._username, feed), payload=value) def flattenDict(self, aDict): """ Get average of simple dictionary of numerical values """ try: val = float(sum(aDict[key] for key in aDict)) / len(aDict) except Exception as e: self.logger.error('Error flattening dict, returning 0: %s' % e) return 0 return val def publishJsonMessage(self, message): msg_str = self.stringifyMessage(message) self.publish(msg_str) def stringifyMessage(self, message): """ Dump into JSON string """ return json.dumps(message.__dict__).encode('utf8') def processQueue(self): """ Process incoming messages. """ while self.alive: # Pump the loop self._client.loop(timeout=1) if (self.inQueue.empty() == False): try: message = self.inQueue.get(block=False, timeout=1) if message is not None and self.mqttConnected: if (message.topic.upper() == "SHUTDOWN" and message.sender_id.lower() == 'main'): self.logger.debug("SHUTDOWN command handled") self.shutdown() else: # Send message as string or split into channels if self._publishJson: self.publishJsonMessage(message) else: self.publishValues(message) except Exception as e: self.logger.error("MQTT unable to read queue : %s " % e) else: time.sleep(.25) def shutdown(self): self.logger.info("Shutting down") self.alive = False time.sleep(1) self.exit = True def run(self): """ Thread start method""" self.logger.info("Running MQTT") self.connect() self.alive = True # Start queue loop self.processQueue()
class WebsocketServerModule(ModuleProcess): def __init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue): # super(WebsocketServerModule, self).__init__() ModuleProcess.__init__(self, baseConfig, pInBoundQueue, pOutBoundQueue, loggingQueue) self.alive = False self.config = baseConfig self.inQueue = pInBoundQueue # inQueue are messages from the main process to websocket clients self.outQueue = pOutBoundQueue # outQueue are messages from clients to main process self.websocketServer = None self.loggingQueue = loggingQueue self.threadProcessQueue = None # Configs self.moduleConfig = configLoader.load(self.loggingQueue, __name__) # Constants self._port = self.moduleConfig['WebsocketPort'] self._host = self.moduleConfig['WebsocketHost'] # logging setup self.logger = ThreadsafeLogger(loggingQueue, __name__) def run(self): """ Main thread entry point. Sets up websocket server and event callbacks. Starts thread to monitor inbound message queue. """ self.logger.info("Starting websocket server") self.alive = True self.listen() self.websocketServer = WebsocketServer(self._port, host=self._host) self.websocketServer.set_fn_new_client(self.new_websocket_client) self.websocketServer.set_fn_message_received( self.websocket_message_received) self.websocketServer.run_forever() def new_websocket_client(self, client, server): """ Client joined callback - called whenever a new client joins. """ self.logger.debug("Client joined") def websocket_message_received(self, client, server, message): """ Message received callback - called whenever a new message is received. """ self.logger.debug('Message received: %s' % message) message = json.loads(message) self.logger.info("message jsond: %s" % message) _msg = Message(topic=message['topic'], sender_id=message['sender_id']) if 'sender_type' in message: _msg.sender_type = message['sender_type'] if 'recipients' in message: _msg.recipients = message['recipients'] if 'extended_data' in message: _msg.extended_data = message['extended_data'] self.put_message(_msg) def listen(self): self.threadProcessQueue = Thread(target=self.process_queue) self.threadProcessQueue.setDaemon(True) self.threadProcessQueue.start() def shutdown(self): """ Handle shutdown message. Close and shutdown websocket server. Join queue processing thread. """ self.logger.info("Shutting down websocket server") try: self.logger.info("Closing websocket") self.websocketServer.server_close() except Exception as e: self.logger.error("Websocket close error : %s " % e) try: self.logger.info("Shutdown websocket") self.websocketServer.shutdown() except Exception as e: self.logger.error("Websocket shutdown error : %s " % e) self.alive = False self.threadProcessQueue.join() time.sleep(1) self.exit = True def handle_message(self, message): """ Send message to listening clients. """ self.websocketServer.send_message_to_all(json.dumps(message.__dict__)) def process_queue(self): """ Monitor queue of messages from main process to this thread. """ while self.alive: if (self.inQueue.empty() == False): try: message = self.inQueue.get(block=False, timeout=1) if message is not None: if (message.topic.upper() == "SHUTDOWN" and message.sender_id.lower() == 'main'): self.logger.debug("SHUTDOWN handled") self.shutdown() else: self.handle_message(message) except Exception as e: self.logger.error("Websocket unable to read queue : %s " % e) else: time.sleep(.25)
class Tracker(): def __init__(self, bbox, frame, kind, moduleConfig, loggingQueue): """ Create and initialize a new Tracker. Set up constants and parameters. """ if kind in ["KCF", "MIL", "MEDIANFLOW", "GOTURN", "TLD", "BOOSTING"]: self.tracker = cv2.Tracker_create(kind) self.tracker.init(frame, (bbox['x'], bbox['y'], bbox['w'], bbox['h'])) self.created = time() self.bbox = (bbox['x'], bbox['y'], bbox['w'], bbox['h']) self.velocity = (0, 0) self.updateTime = self.created self.config = moduleConfig # Constants self._useVelocity = self.config['UseVelocity'] self._horizontalVelocityBuffer = self.config['HorizontalVelocityBuffer'] self._verticalVelocityBuffer = self.config['VerticalVelocityBuffer'] # Setup logging queue self.logger = ThreadsafeLogger(loggingQueue, __name__) else: self.logger.error("Type %s not supported by mTracker" % kind) def getCreated(self): """ Get created time """ return self.created def right(self): """ Get right bound of tracker """ return self.bbox[0] + self.bbox[2] def top(self): """ Get top bound of tracker """ return self.bbox[1] + self.bbox[3] def bottom(self): """ Get bottom bound of tracker """ return self.bbox[1] def left(self): """ Get left bound of tracker """ return self.bbox[0] def area(self): """ Get area of tracker bounding box """ return abs(self.right() - self.left())*abs(self.top() - self.bottom()) def update(self, frame): """ Update tracker. If velocity hack is being used, calculate the new velocity of the midpoint. """ ok, bbox = self.tracker.update(frame) if self._useVelocity: # Set velocity (pixels/sec) deltaT = time() - self.updateTime centreNow = ((bbox[0]+bbox[2]/2), (bbox[1]+bbox[3]/2)) centreLast = ((self.bbox[0]+self.bbox[2]/2), (self.bbox[1]+self.bbox[3]/2)) Vx = (centreNow[0] - centreLast[0])/deltaT Vy = (centreNow[1] - centreLast[1])/deltaT self.velocity = (Vx, Vy) self.logger.debug('New velocity: %s' % str(self.velocity[0])+', '+str(self.velocity[1])) self.updateTime = time() self.bbox = bbox return ok, bbox def getProjectedLocation(self, time): """ Get the estimated location of the bounding box, based on previous velocity. """ deltaT = max((time - self.updateTime), 1) centreNow = ((self.bbox[0]+self.bbox[2]/2), (self.bbox[1]+self.bbox[3]/2)) projectedX = centreNow[0]+(self.velocity[0]*deltaT) projectedY = centreNow[1]+(self.velocity[1]*deltaT) return (projectedX, projectedY) def getVelocityBuffer(self): ''' Another hack to improve low frame rate tracking. "Spread" out the bounding box based on velocity. ''' return (abs(self.velocity[0])*self._horizontalVelocityBuffer, abs(self.velocity[1])*self._verticalVelocityBuffer)
class BtleClient(object): def __init__(self, detectionData, collectionPointConfig, loggingQueue): self.loggingQueue = loggingQueue self.logger = ThreadsafeLogger(loggingQueue, "BtleRegisteredClient") # Counters and variables self.clientInRangeTrigerCount = 1 self.prevClientInMsgTime = None self.prevClientOutMsgTime = None self.numClientInRange=0 self.numClientOutRange=0 self.timeInCollectionPointInMilliseconds = 0 self.firstRegisteredTime = datetime.now() self.collectionPointConfig = collectionPointConfig self.filter = Filter(detectionData.extraData['rssi']) try: self.uidMap = UIDMap() except Exception as e: self.logger.warning('cant instantiate uid map: %s '%e) # Constants self._proximityEventInterval = self.collectionPointConfig['ProximityEventInterval'] self._outClientThreshold = self.collectionPointConfig['BtleClientOutCountThreshold'] self._gatewayType = self.collectionPointConfig['GatewayType'] self._rssiClientInThresh = self.collectionPointConfig['BtleRssiClientInThreshold'] self._rssiErrorVar = self.collectionPointConfig['BtleRssiErrorVariance'] self.__clientOutThresholdMin = int( self._rssiClientInThresh + (self._rssiClientInThresh * self._rssiErrorVar) ) self._clientInThreshType = self.collectionPointConfig['BtleRssiClientInThresholdType'] self._debugEventManager = self.collectionPointConfig['EventManagerDebug'] # Initiate event when client is detected self.handleNewDetectedClientEvent(detectionData) def updateWithNewDetectedClientData(self, detectionData): """ updateWithNewDetectedClientData part of interface for Registered Client """ self.timeInCollectionPointInMilliseconds = (datetime.now() - self.firstRegisteredTime).total_seconds()*1000 # standard shared methods when we see a detected client self.handleNewDetectedClientEvent(detectionData) # Common methods are handled here for updateWithNewDetectedClientData and init def handleNewDetectedClientEvent(self, detectionData): self.lastRegisteredTime = datetime.now() self.detectionData = detectionData self.txPower = detectionData.extraData['tx'] self.beaconId = detectionData.extraData['udid'] self.filter.update(self.detectionData.extraData['rssi']) self.incrementInternalClientEventCounts(detectionData) def incrementInternalClientEventCounts(self, detectionData): if self._gatewayType == 'proximity': if self._clientInThreshType == 'rssi': # Are they in or are they out of range # Increment internal count, used to normalize events. if (self.detectionData.extraData['rssi'] >= self._rssiClientInThresh): self.numClientInRange += 1 self.numClientOutRange = 0 self.logClientRange("CLIENTIN") elif (self.detectionData.extraData['rssi'] < self.__clientOutThresholdMin): self.numClientOutRange += 1 self.numClientInRange = 0 self.logClientRange("CLIENTOUT") #part of interface for Registered Client def shouldSendClientInEvent(self): if self._gatewayType == 'proximity': if (self.prevClientInMsgTime == None or (self.prevClientOutMsgTime != None and (self.prevClientOutMsgTime-self.prevClientInMsgTime).total_seconds() > 0) or (datetime.now() - self.prevClientInMsgTime).total_seconds()*1000 >= self._proximityEventInterval): if self.numClientInRange > self.clientInRangeTrigerCount: # self.logClientEventSend(" ClientIN event sent to controller ") self.zeroEventRangeCounters() return True #TODO add in other types of gateway types # self.logger.debug("NOT SENDING CLIENT IN") return False #part of interface for Registered Client def shouldSendClientOutEvent(self): if self._gatewayType == 'proximity': #check the time to see if we need to send a message #have we ever sent an IN event? if not we dont need to send an out event if self.prevClientInMsgTime: #have we sent a client out since the last client in? if so we dont need to throw another if (self.prevClientOutMsgTime == None or self.prevClientOutMsgTime < self.prevClientInMsgTime): #do we have enought qualifying out events. we dont want to throw one too soon if (self.numClientOutRange >= self._outClientThreshold): # self.logClientEventSend("ClientOUT event a sent to controller") self.logger.debug("out case B: client %s"%self.detectionData.extraData["beaconMac"]) self.zeroEventRangeCounters() return True elif (self.prevClientOutMsgTime != None and self.prevClientOutMsgTime > self.prevClientInMsgTime): return False #check timing on last event sent if (self.prevClientOutMsgTime is not None and (datetime.now() - self.prevClientOutMsgTime).total_seconds()*1000 > self._proximityEventInterval): # self.logClientEventSend("ClientOUT event b sent to controller") self.logger.debug("out case A: client %s"%self.detectionData.extraData["beaconMac"]) self.zeroEventRangeCounters() return True elif self.prevClientOutMsgTime is not None: return False elif self.numClientOutRange > self._outClientThreshold: # self.logger.debug("Client out count "+ # "%i is past max. Resetting." %self.numClientOutRange) self.numClientOutRange = 0 #TODO add in other types of gateway types return False #part of interface for Registered Client def sweepShouldSendClientOutEvent(self): self.logger.debug("trace 1") if self._gatewayType == 'proximity': # has an in event been sent yet? if not, no sweep needed self.logger.debug("trace 2") if self.prevClientInMsgTime: self.logger.debug("trace 3") # sweep old clients, so check most recent message sent # if no message has been sent in the past proximityEventInterval*3 milliseconds # sweep the client because it is probably gone if (self.prevClientOutMsgTime is None or (self.prevClientInMsgTime>self.prevClientOutMsgTime and (datetime.now() - self.prevClientOutMsgTime).total_seconds()*1000 > self._proximityEventInterval*3)): self.logger.debug("trace 4") self.logger.debug("sweep: client %s"%self.detectionData.extraData["beaconMac"]) # self.logClientEventSend("Sweep case a is sending ClientOUT on") self.zeroEventRangeCounters() return True else: self.logger.debug("trace 5") return False else: self.logger.debug("trace 6") return False #TODO add in other types of gateway types self.logger.debug("trace 7") return False #part of interface for Registered Client def getMac(self): return self.detectionData.extraData["beaconMac"] def getTxPower(self): return self.txPower #zero out the BTLE event counters def zeroEventRangeCounters(self): self.numClientOutRange = 0 self.numClientInRange = 0 def logClientEventSend(self,message): if self._debugEventManager: self.logger.debug("") self.logger.debug("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") self.logger.debug("%%%%%%%%%%%%%%%%%% %s %%%%%%%%%%%%%%%%%%" %message) self.logger.debug(" MAC is %s " %self.getMac()) self.logger.debug(" Beacon ID is %s " %self.beaconId) self.logger.debug(" filtered RSSI %i" %self.filter.state) self.logger.debug(" RSSI %i" %self.detectionData.extraData['rssi']) self.logger.debug(" Major %i" %self.detectionData.extraData['majorNumber']) self.logger.debug(" Minor %i" %self.detectionData.extraData['minorNumber']) self.logger.debug(" BTLE RSSI client in threshold %i" %self.collectionPointConfig['BtleRssiClientInThreshold']) self.logger.debug(" BTLE RSSI client out threshold %i" %self.__clientOutThresholdMin) self.logger.debug(" inCount %i : outCount %i" %(self.numClientInRange,self.numClientOutRange)) self.logger.debug("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") self.logger.debug("") return def logClientRange(self,eventType): if self.collectionPointConfig['ShowClientRangeDebug']: if eventType.upper() == "CLIENTIN": self.logger.debug("<<<<<<<<<<<<<<<<< IN RANGE <<<<<<<<<<<<<<<<<") else: self.logger.debug(">>>>>>>>>>>>>>>>> OUT OF RANGE >>>>>>>>>>>>>>>>>") self.logger.debug(" MAC is %s " %self.getMac()) self.logger.debug(" Beacon ID is %s " %self.beaconId) self.logger.debug(" RSSI %i" %self.detectionData.extraData['rssi']) self.logger.debug(" Major %i" %self.detectionData.extraData['majorNumber']) self.logger.debug(" Minor %i" %self.detectionData.extraData['minorNumber']) self.logger.debug(" BTLE RSSI client in threshold %i" %self.collectionPointConfig['BtleRssiClientInThreshold']) self.logger.debug(" BTLE RSSI client out threshold %i" %self.__clientOutThresholdMin) self.logger.debug(" inCount %i : outCount %i" %(self.numClientInRange,self.numClientOutRange)) if eventType.upper() == "CLIENTIN": self.logger.debug("<<<<<<<<<<<<<<<<< IN RANGE END <<<<<<<<<<<<<<<<<") else: self.logger.debug(">>>>>>>>>>>>>>>>> OUT OF RANGE END >>>>>>>>>>>>>>>>>") self.logger.debug("") return #part of interface for Registered Client def getExtendedDataForEvent(self): extraData = {} extraData['gatewayType'] = self.collectionPointConfig['GatewayType'] extraData['lastRegisteredTime'] = self.lastRegisteredTime if self.lastRegisteredTime==None else self.lastRegisteredTime.isoformat() extraData['firstRegisteredTime'] = self.firstRegisteredTime if self.firstRegisteredTime==None else self.firstRegisteredTime.isoformat() extraData['prevClientInMsgTime'] = self.prevClientInMsgTime if self.prevClientInMsgTime==None else self.prevClientInMsgTime.isoformat() extraData['prevClientOutMsgTime'] = self.prevClientOutMsgTime if self.prevClientOutMsgTime==None else self.prevClientOutMsgTime.isoformat() extraData['timeInCollectionPointInMilliseconds'] = self.timeInCollectionPointInMilliseconds extraData['rssi'] = self.detectionData.extraData['rssi'] extraData['averageRssi'] = self.detectionData.extraData['rssi'] extraData['filteredRssi'] = self.filter.state extraData['txPower'] = self.getTxPower() extraData['beaconId'] = self.beaconId extraData['beaconMac'] = self.detectionData.extraData["beaconMac"] extraData['major'] = self.detectionData.extraData["majorNumber"] extraData['minor'] = self.detectionData.extraData["minorNumber"] if self.collectionPointConfig['CecData']: extraData['industry'] = self.uidMap.get(self.beaconId) return extraData def getExtendedDataForUpdateEvent(self): extraData = {} extraData['rssi'] = self.detectionData.extraData['rssi'] extraData['filteredRssi'] = self.filter.state extraData['beaconId'] = self.beaconId extraData['beaconMac'] = self.detectionData.extraData["beaconMac"] extraData['major'] = self.detectionData.extraData["majorNumber"] extraData['minor'] = self.detectionData.extraData["minorNumber"] if self.collectionPointConfig['CecData']: extraData['industry'] = self.uidMap.get(self.beaconId) return extraData #part of interface for Registered Client def setClientInMessageSentToController(self): self.logger.debug('set client in message sent') self.prevClientInMsgTime = datetime.now() self.numClientInRange = 0 #part of interface for Registered Client def setClientOutMessageSentToController(self): self.logger.debug('set client out message sent') self.prevClientOutMsgTime = datetime.now() self.numClientOutRange = 0