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 WebsocketClientModule(Thread): 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 # Constants self._port = self.config['WebsocketPort'] self._host = self.config['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 %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 from websocket client: %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) 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 websocket server %s" % (multiprocessing.current_process().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 == "SHUTDOWN": 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)