def sample_worker(self): rc_api = self._rc_api sample_event = self._sample_event Logger.info('DataBusPump: DataBus Sampler Starting') sample_event.clear() if sample_event.wait(SAMPLE_POLL_TEST_TIMEOUT) == True: Logger.info('DataBusPump: Async sampling detected') else: Logger.info('DataBusPump: Synchronous sampling mode enabled') while self._running.is_set(): try: #the timeout here is designed to be longer than the streaming rate of #RaceCapture. If we don't get an asynchronous sample, then we will timeout #and request a sample anyway. rc_api.sample() sample_event.wait(SAMPLE_POLL_EVENT_TIMEOUT) sample_event.clear() sleep(SAMPLE_POLL_INTERVAL_TIMEOUT) except Exception as e: sleep(SAMPLE_POLL_EXCEPTION_RECOVERY) Logger.error('DataBusPump: Exception in sample_worker: ' + str(e)) finally: sample_event.clear() Logger.info('DataBusPump: DataBus Sampler Exiting') safe_thread_exit()
def _connection_thread_message_reader(self, rx_queue, connection, should_run): """This method is designed to be run in a thread, it will loop infinitely as long as should_run.is_set() returns True. In its loop it will attempt to read data from the socket connection :param rx_queue Queue for pushing data read from the socket onto :param connection Socket connection to read data from :param should_run Event to check on each loop to determine if to continue reading :type rx_queue threading.Queue :type connection SocketConnection :type should_run threading.Event :return: None """ Logger.debug('SocketComm: connection thread message reader started') while should_run.is_set(): try: msg = connection.read_line(should_run) if msg: rx_queue.put(msg) except: Logger.error('SocketComm: Exception in connection_process_message_reader') Logger.debug(traceback.format_exc()) should_run.clear() sleep(0.5) Logger.debug('SocketComm: connection process message reader exited') safe_thread_exit()
def _session_recorder_worker(self): Logger.info('SessionRecorder: session recorder worker starting') try: # reset our sample data dict sample_data = {} index = 0 qsize = 0 sample_queue = self._sample_queue # will drain the queue before exiting thread while self.recording or qsize > 0: try: sample = sample_queue.get( True, SessionRecorder.SAMPLE_QUEUE_GET_TIMEOUT) # Merging previous sample with new data to desparsify the # data sample_data.update(sample) self._datastore.insert_sample(sample_data, self._current_session_id) qsize = sample_queue.qsize() if index % SessionRecorder.SAMPLE_QUEUE_BACKLOG_LOG_INTERVAL == 0 and qsize > 0: Logger.info( 'SessionRecorder: queue backlog: {}'.format(qsize)) index += 1 except Empty: pass except Exception as e: Logger.error( 'SessionRecorder: Exception in session recorder worker ' + str(e)) finally: safe_thread_exit() Logger.info('SessionRecorder: session recorder worker ending')
def _connection_message_thread(self, connection, address, rx_queue, tx_queue, command_queue): """ 'Manager' method that is run in a thread that starts up the read and write threads for the socket connection. Watches the command_queue to know when to stop :param connection: Socket connection :param address: IP address to connect to :param rx_queue: Receive queue :param tx_queue: Transmit queue :param command_queue: Command queue :return: None """ Logger.info('SocketComm: connection process starting') try: connection.open(address) connection.flushInput() connection.flushOutput() reader_writer_should_run = threading.Event() reader_writer_should_run.set() reader_thread = threading.Thread(target=self._connection_thread_message_reader, args=(rx_queue, connection, reader_writer_should_run)) reader_thread.start() writer_thread = threading.Thread(target=self._connection_thread_message_writer, args=(tx_queue, connection, reader_writer_should_run)) writer_thread.start() while reader_writer_should_run.is_set(): try: command = command_queue.get(True, self.KEEP_ALIVE_TIMEOUT_S) if command == self.COMMAND_CLOSE: Logger.debug('SocketComm: connection thread: got close command') reader_writer_should_run.clear() except Empty: Logger.debug('SocketComm: keep alive timeout') reader_writer_should_run.clear() Logger.debug('SocketComm: connection worker exiting') reader_thread.join() writer_thread.join() try: connection.close() except: Logger.error('SocketComm: Exception closing connection worker connection') Logger.error(traceback.format_exc()) except socket.timeout as e: Logger.error("SocketComm: got timeout connecting to {}".format(address)) except socket.error as e: Logger.error("SocketComm: got exception connecting to {}".format(address)) Logger.error('SocketComm: socket error setting up connection thread: ' + str(type(e)) + str(e)) except Exception as e: Logger.error('SocketComm: Exception setting up connection thread: ' + str(type(e)) + str(e)) Logger.debug(traceback.format_exc()) Logger.debug('SocketComm: connection worker exited') safe_thread_exit()
def _connection_thread_message_reader(self, rx_queue, connection, should_run): """This method is designed to be run in a thread, it will loop infinitely as long as should_run.is_set() returns True. In its loop it will attempt to read data from the socket connection :param rx_queue Queue for pushing data read from the socket onto :param connection Socket connection to read data from :param should_run Event to check on each loop to determine if to continue reading :type rx_queue threading.Queue :type connection SocketConnection :type should_run threading.Event :return: None """ Logger.debug('SocketComm: connection thread message reader started') while should_run.is_set(): try: msg = connection.read_line(should_run) if msg: rx_queue.put(msg) except: Logger.error( 'SocketComm: Exception in connection_process_message_reader' ) Logger.debug(traceback.format_exc()) should_run.clear() sleep(0.5) Logger.debug('SocketComm: connection process message reader exited') safe_thread_exit()
def _connection_thread_message_writer(self, tx_queue, connection, should_run): """This method is designed to be run in a thread, it will loop infinitely as long as should_run.is_set() returns True. In its loop it will attempt to write pending data from the socket connection :param tx_queue Queue for pulling data to be written to the socket :param connection Socket connection to write data to :param should_run Event to check on each loop to determine if to continue writing :type tx_queue threading.Queue :type connection SocketConnection :type should_run threading.Event :return: None """ Logger.info('SocketComm: connection thread message writer started') while should_run.is_set(): try: message = tx_queue.get(True, 1.0) if message: Logger.debug( "SocketComm: writing message {}".format(message)) connection.write(message) except Empty: pass except Exception as e: Logger.error( 'SocketComm: Exception in connection_thread_message_writer ' + str(e)) Logger.debug(traceback.format_exc()) should_run.clear() sleep(0.5) Logger.debug('SocketComm: connection thread message writer exited') safe_thread_exit()
def msg_rx_worker(self): Logger.info('RCPAPI: msg_rx_worker starting') comms = self.comms error_count = 0 while self._running.is_set(): msg = None try: msg = comms.read_message() if msg: # clean incoming string, and drop illegal characters msg = unicode(msg, errors='ignore') msgJson = json.loads(msg, strict=False) if 's' in msgJson: Logger.trace('RCPAPI: Rx: ' + str(msg)) else: Logger.debug('RCPAPI: Rx: ' + str(msg)) Clock.schedule_once(lambda dt: self.on_rx(True)) error_count = 0 for messageName in msgJson.keys(): Logger.trace('RCPAPI: processing message ' + messageName) listeners = self.msgListeners.get(messageName, None) if listeners: for listener in listeners: try: listener(msgJson) except Exception as e: Logger.error( 'RCPAPI: Message Listener Exception for' ) Logger.debug(traceback.format_exc()) break msg = '' else: sleep(NO_DATA_AVAILABLE_DELAY) except PortNotOpenException: Logger.debug("RCPAPI: Port not open...") msg = '' sleep(1.0) except Exception as e: Logger.warn( 'RCPAPI: Message rx worker exception: {} | {}'.format( repr(msg), str(e))) Logger.debug(traceback.format_exc()) msg = '' error_count += 1 if error_count > 5 and not self._auto_detect_event.is_set(): Logger.warn( "RCPAPI: Too many Rx exceptions; re-opening connection" ) self.recover_connection() self.connected_version = None sleep(5) else: sleep(0.25) safe_thread_exit() Logger.info("RCPAPI: msg_rx_worker exiting")
def _connection_thread_message_writer(self, tx_queue, connection, should_run): """This method is designed to be run in a thread, it will loop infinitely as long as should_run.is_set() returns True. In its loop it will attempt to write pending data from the socket connection :param tx_queue Queue for pulling data to be written to the socket :param connection Socket connection to write data to :param should_run Event to check on each loop to determine if to continue writing :type tx_queue threading.Queue :type connection SocketConnection :type should_run threading.Event :return: None """ Logger.info('SocketComm: connection thread message writer started') while should_run.is_set(): try: message = tx_queue.get(True, 1.0) if message: Logger.debug("SocketComm: writing message {}".format(message)) connection.write(message) except Empty: pass except Exception as e: Logger.error('SocketComm: Exception in connection_thread_message_writer ' + str(e)) Logger.debug(traceback.format_exc()) should_run.clear() sleep(0.5) Logger.debug('SocketComm: connection thread message writer exited') safe_thread_exit()
def _connection_message_thread(self, connection, address, rx_queue, tx_queue, command_queue): """ 'Manager' method that is run in a thread that starts up the read and write threads for the socket connection. Watches the command_queue to know when to stop :param connection: Socket connection :param address: IP address to connect to :param rx_queue: Receive queue :param tx_queue: Transmit queue :param command_queue: Command queue :return: None """ Logger.info('SocketComm: connection process starting') try: connection.open(address) connection.flushInput() connection.flushOutput() reader_writer_should_run = threading.Event() reader_writer_should_run.set() reader_thread = threading.Thread(target=self._connection_thread_message_reader, args=(rx_queue, connection, reader_writer_should_run)) reader_thread.start() writer_thread = threading.Thread(target=self._connection_thread_message_writer, args=(tx_queue, connection, reader_writer_should_run)) writer_thread.start() while reader_writer_should_run.is_set(): try: command = command_queue.get(True, self.KEEP_ALIVE_TIMEOUT_S) if command == self.COMMAND_CLOSE: Logger.debug('SocketComm: connection thread: got close command') reader_writer_should_run.clear() except Empty: Logger.debug('SocketComm: keep alive timeout') Logger.debug('SocketComm: connection worker exiting') reader_thread.join() writer_thread.join() try: connection.close() except: Logger.error('SocketComm: Exception closing connection worker connection') Logger.error(traceback.format_exc()) except socket.timeout as e: Logger.error("SocketComm: got timeout connecting to {}".format(address)) except socket.error as e: Logger.error("SocketComm: got exception connecting to {}".format(address)) Logger.error('SocketComm: socket error setting up connection thread: ' + str(type(e)) + str(e)) except Exception as e: Logger.error('SocketComm: Exception setting up connection thread: ' + str(type(e)) + str(e)) Logger.debug(traceback.format_exc()) Logger.debug('SocketComm: connection worker exited') safe_thread_exit()
def status_worker(self): Logger.info('StatusPump: status_worker starting') self._rc_api.addListener('status', self._on_status_updated) while self._running.is_set(): self._rc_api.get_status() sleep(self.STATUS_QUERY_INTERVAL) Logger.info('StatusPump: status_worker exited') safe_thread_exit()
def _session_recorder_worker(self): Logger.info('SessionRecorder: session recorder worker starting') try: # reset our sample data dict qsize = 0 insert_counter = 0 sample_queue = self._sample_queue index = 0 # will drain the queue before exiting thread while self.recording or qsize > 0: try: sample_data = sample_queue.get( True, SessionRecorder.SAMPLE_QUEUE_GET_TIMEOUT) self._datastore.insert_sample_nocommit( sample_data, self._current_session_id) insert_counter += 1 qsize = sample_queue.qsize() # since the commit is slow, only do the commit once the queue empty to prevent overrunning the buffer. if (qsize == 0) or (insert_counter >= SessionRecorder. SAMPLE_QUEUE_UNCOMMITTED_INSERT_LIMIT): self._datastore.commit() insert_counter = 0 if qsize > 0 and index % SessionRecorder.SAMPLE_QUEUE_BACKLOG_LOG_INTERVAL == 0: Logger.info( 'SessionRecorder: queue backlog: {}, commit backlog: {}, sample send delay: {}ms' .format(qsize, insert_counter, self._sample_send_delay)) if insert_counter > ( SessionRecorder. SAMPLE_QUEUE_UNCOMMITTED_INSERT_LIMIT * SessionRecorder.SAMPLE_QUEUE_BACKLOG_LOG_THRESHOLD ): Logger.debug( 'SessionRecorder: commit backlog: {}'.format( insert_counter)) if qsize > ( SessionRecorder.SAMPLE_QUEUE_MAX_SIZE * SessionRecorder.SAMPLE_QUEUE_BACKLOG_LOG_THRESHOLD ): Logger.debug( 'SessionRecorder: queue backlog: {}'.format(qsize)) index += 1 except Empty: pass except Exception as e: Logger.error( 'SessionRecorder: Exception in session recorder worker ' + str(e)) finally: safe_thread_exit() Logger.info('SessionRecorder: session recorder worker ending')
def status_worker(self): Logger.info('StatusPump: status_worker starting') self._rc_api.addListener('status', self._on_status_updated) while self._running.is_set(): self._rc_api.get_status() sleep(StatusPump.STATUS_QUERY_INTERVAL_SEC) if not self._ready.wait(StatusPump.READY_WAIT_TIMEOUT_SEC): Logger.warn('StatusPump: timed out waiting for status response') Logger.info('StatusPump: status_worker exited') safe_thread_exit()
def msg_rx_worker(self): Logger.info('RCPAPI: msg_rx_worker starting') comms = self.comms error_count = 0 while self._running.is_set(): msg = None try: msg = comms.read_message() if msg: # clean incoming string, and drop illegal characters msg = unicode(msg, errors='ignore') msgJson = json.loads(msg, strict=False) if 's' in msgJson: Logger.trace('RCPAPI: Rx: ' + str(msg)) else: Logger.debug('RCPAPI: Rx: ' + str(msg)) Clock.schedule_once(lambda dt: self.on_rx(True)) error_count = 0 for messageName in msgJson.keys(): Logger.trace('RCPAPI: processing message ' + messageName) listeners = self.msgListeners.get(messageName, None) if listeners: for listener in listeners: try: listener(msgJson) except Exception as e: Logger.error('RCPAPI: Message Listener Exception for') Logger.debug(traceback.format_exc()) break msg = '' else: sleep(NO_DATA_AVAILABLE_DELAY) except PortNotOpenException: Logger.debug("RCPAPI: Port not open...") msg = '' sleep(1.0) except Exception as e: Logger.warn('RCPAPI: Message rx worker exception: {} | {}'.format(repr(msg), str(e))) Logger.debug(traceback.format_exc()) msg = '' error_count += 1 if error_count > 5 and not self._auto_detect_event.is_set(): Logger.warn("RCPAPI: Too many Rx exceptions; re-opening connection") self.recover_connection() self.connected_version = None sleep(5) else: sleep(0.25) safe_thread_exit() Logger.info("RCPAPI: msg_rx_worker exiting")
def sample_worker(self): rc_api = self._rc_api Logger.info('DataBusPump: sample_worker starting') while self._running.is_set(): try: rc_api.sample() sleep(SAMPLE_POLL_INTERVAL_TIMEOUT) except Exception as e: Logger.error('DataBusPump: Exception in sample_worker: ' + str(e)) sleep(SAMPLE_POLL_EXCEPTION_RECOVERY) Logger.info('DataBusPump: sample_worker exiting') safe_thread_exit()
def sample_worker(self): rc_api = self._rc_api Logger.info('DataBusPump: sample_worker polling starting') while self._poll.is_set(): try: rc_api.sample() sleep(1.0 / self.current_sample_rate) except Exception as e: Logger.error('DataBusPump: Exception in sample_worker: ' + str(e)) sleep(DataBusPump.SAMPLE_POLL_EXCEPTION_RECOVERY) Logger.info('DataBusPump: sample_worker exiting') safe_thread_exit()
def auto_detect_worker(self): Logger.info('RCPAPI: auto_detect_worker starting') class VersionResult(object): version_json = None def on_ver_win(value, source): version_result.version_json = value version_result_event.set() while self._running.is_set(): self._auto_detect_event.wait() self._auto_detect_event.clear() self._enable_autodetect.wait() # check again if we're shutting down # to prevent a needless re-detection attempt if not self._running.is_set(): break try: Logger.debug("RCPAPI: Starting auto-detect") self._auto_detect_busy.set() self.sendCommandLock.acquire() self.addListener("ver", on_ver_win) comms = self.comms if comms and comms.isOpen(): comms.close() version_result = VersionResult() version_result_event = Event() version_result_event.clear() if comms.device: devices = [comms.device] else: devices = comms.get_available_devices() last_known_device = self._settings.userPrefs.get_pref( 'preferences', 'last_known_device') # if there was a last known device try it repeatedly while trying the other devices. if last_known_device: Logger.info( 'RCPAPI: trying last known device before each other device: {}' .format(last_known_device)) # ensure we remove it from the existing list try: devices.remove(last_known_device) except ValueError: pass # rebuild the list, with last_known_device as every second entry temp_list = devices devices = [last_known_device] for device in temp_list: devices = devices + [device, last_known_device] Logger.debug('RCPAPI: Searching for device') testVer = VersionConfig() for device in devices: try: if not self._running.is_set(): break Logger.debug('RCPAPI: Trying ' + str(device)) if self.detect_activity_callback: self.detect_activity_callback(str(device)) comms.device = device comms.open() self.sendGetVersion() version_result_event.wait(2) version_result_event.clear() if version_result.version_json != None: testVer.fromJson( version_result.version_json.get('ver', None)) if testVer.is_valid: break # we found something! else: try: Logger.debug('RCPAPI: Giving up on ' + str(device)) comms.close() finally: pass except Exception as detail: Logger.error('RCPAPI: Not found on ' + str(device) + " " + str(detail)) Logger.error(traceback.format_exc()) try: comms.close() finally: pass if testVer.is_valid: Logger.debug("RCPAPI: Found device version " + str(testVer) + " on port: " + str(comms.device)) self.detect_win(testVer) self._auto_detect_event.clear() self._settings.userPrefs.set_pref('preferences', 'last_known_device', comms.device) else: Logger.debug('RCPAPI: Did not find device') comms.close() comms.device = None if self.detect_fail_callback: self.detect_fail_callback() except Exception as e: Logger.error('RCPAPI: Error running auto detect: ' + str(e)) Logger.error(traceback.format_exc()) if self.detect_fail_callback: self.detect_fail_callback() finally: Logger.debug("RCPAPI: auto detect finished. port=" + str(comms.device)) self._auto_detect_busy.clear() self.removeListener("ver", on_ver_win) self.sendCommandLock.release() comms.device = None sleep(AUTODETECT_COOLOFF_TIME) safe_thread_exit() Logger.debug('RCPAPI: auto_detect_worker exiting')
def cmd_sequence_worker(self): Logger.info('RCPAPI: cmd_sequence_worker starting') while self._running.is_set(): try: # Block for 1 second for messages command = self._command_queue.get( True, RcpApi.COMMAND_SEQUENCE_TIMEOUT) command_list = command.command_list rootName = command.rootName winCallback = command.winCallback failCallback = command.failCallback comms = self.comms Logger.debug('RCPAPI: Execute Sequence begin') if not comms.isOpen(): self.run_auto_detect() q = self.cmdSequenceQueue responseResults = {} cmdCount = 0 cmdLength = len(command_list) self.notifyProgress(cmdCount, cmdLength) try: for rcpCmd in command_list: payload = rcpCmd.payload index = rcpCmd.index option = rcpCmd.option last = rcpCmd.last level2Retry = 0 name = rcpCmd.name result = None self.addListener(name, self.rcpCmdComplete) while not result and level2Retry <= self.level_2_retries: args = [] if payload is not None: args.append(payload) if index is not None: args.append(index) if option is not None: args.append(option) if last is not None: args.append(last) rcpCmd.cmd(*args) retry = 0 while not result and retry < DEFAULT_READ_RETRIES: try: result = q.get(True, self.msg_rx_timeout) msgName = result.keys()[0] if not msgName == name: Logger.warn( 'RCPAPI: rx message did not match expected name ' + str(name) + '; ' + str(msgName)) result = None except Exception as e: Logger.warn( 'RCPAPI: Read message timeout waiting for {}' .format(name)) self.recoverTimeout() retry += 1 if not result: Logger.warn('RCPAPI: Level 2 retry for (' + str(level2Retry) + ') ' + name) level2Retry += 1 if not result: raise Exception('Timeout waiting for ' + name) responseResults[name] = result[name] self.removeListener(name, self.rcpCmdComplete) cmdCount += 1 self.notifyProgress(cmdCount, cmdLength) if rootName: callback = self.callback_factory( winCallback, {rootName: responseResults}) else: callback = self.callback_factory( winCallback, responseResults) Clock.schedule_once(callback) except CommsErrorException: self.recover_connection() self.connected_version = None except Exception as detail: Logger.error('RCPAPI: Command sequence exception: ' + str(detail)) Logger.error(traceback.format_exc()) callback = self.callback_factory(failCallback, detail) Clock.schedule_once(callback) self.connected_version = None self.recover_connection() Logger.debug('RCPAPI: Execute Sequence complete') except Queue.Empty: pass except Exception as e: Logger.error('RCPAPI: Execute command exception ' + str(e)) Logger.info('RCPAPI: cmd_sequence_worker exiting') safe_thread_exit()
def cmd_sequence_worker(self): Logger.info('RCPAPI: cmd_sequence_worker starting') while self._running.is_set(): try: # Block for 1 second for messages command = self._command_queue.get(True, RcpApi.COMMAND_SEQUENCE_TIMEOUT) command_list = command.command_list rootName = command.rootName winCallback = command.winCallback failCallback = command.failCallback comms = self.comms Logger.debug('RCPAPI: Execute Sequence begin') if not comms.isOpen(): self.run_auto_detect() q = self.cmdSequenceQueue responseResults = {} cmdCount = 0 cmdLength = len(command_list) self.notifyProgress(cmdCount, cmdLength) try: for rcpCmd in command_list: payload = rcpCmd.payload index = rcpCmd.index option = rcpCmd.option last = rcpCmd.last level2Retry = 0 name = rcpCmd.name result = None self.addListener(name, self.rcpCmdComplete) while not result and level2Retry <= self.level_2_retries: args = [] if payload is not None: args.append(payload) if index is not None: args.append(index) if option is not None: args.append(option) if last is not None: args.append(last) rcpCmd.cmd(*args) retry = 0 while not result and retry < DEFAULT_READ_RETRIES: try: result = q.get(True, self.msg_rx_timeout) msgName = result.keys()[0] if not msgName == name: Logger.warn('RCPAPI: rx message did not match expected name ' + str(name) + '; ' + str(msgName)) result = None except Exception as e: Logger.warn('RCPAPI: Read message timeout waiting for {}'.format(name)) self.recoverTimeout() retry += 1 if not result: Logger.warn('RCPAPI: Level 2 retry for (' + str(level2Retry) + ') ' + name) level2Retry += 1 if not result: raise Exception('Timeout waiting for ' + name) responseResults[name] = result[name] self.removeListener(name, self.rcpCmdComplete) cmdCount += 1 self.notifyProgress(cmdCount, cmdLength) if rootName: callback = self.callback_factory(winCallback, {rootName: responseResults}) else: callback = self.callback_factory(winCallback, responseResults) Clock.schedule_once(callback) except CommsErrorException: self.recover_connection() self.connected_version = None except Exception as detail: Logger.error('RCPAPI: Command sequence exception: ' + str(detail)) Logger.error(traceback.format_exc()) callback = self.callback_factory(failCallback, detail) Clock.schedule_once(callback) self.connected_version = None self.recover_connection() Logger.debug('RCPAPI: Execute Sequence complete') except Queue.Empty: pass except Exception as e: Logger.error('RCPAPI: Execute command exception ' + str(e)) Logger.info('RCPAPI: cmd_sequence_worker exiting') safe_thread_exit()
def auto_detect_worker(self): Logger.info('RCPAPI: auto_detect_worker starting') class VersionResult(object): version_json = None def on_ver_win(value, source): version_result.version_json = value version_result_event.set() while self._running.is_set(): self._auto_detect_event.wait() self._auto_detect_event.clear() self._enable_autodetect.wait() # check again if we're shutting down # to prevent a needless re-detection attempt if not self._running.is_set(): break try: Logger.debug("RCPAPI: Starting auto-detect") self._auto_detect_busy.set() self.sendCommandLock.acquire() self.addListener("ver", on_ver_win) comms = self.comms if comms and comms.isOpen(): comms.close() version_result = VersionResult() version_result_event = Event() version_result_event.clear() if comms.device: devices = [comms.device] else: devices = comms.get_available_devices() last_known_device = self._settings.userPrefs.get_pref('preferences', 'last_known_device') # if there was a last known device try it repeatedly while trying the other devices. if last_known_device: Logger.info('RCPAPI: trying last known device before each other device: {}'.format(last_known_device)) # ensure we remove it from the existing list try: devices.remove(last_known_device) except ValueError: pass # rebuild the list, with last_known_device as every second entry temp_list = devices devices = [last_known_device] for device in temp_list: devices = devices + [device, last_known_device] Logger.debug('RCPAPI: Searching for device') testVer = VersionConfig() for device in devices: try: if not self._running.is_set(): break Logger.debug('RCPAPI: Trying ' + str(device)) if self.detect_activity_callback: self.detect_activity_callback(str(device)) comms.device = device comms.open() self.sendGetVersion() version_result_event.wait(2) version_result_event.clear() if version_result.version_json != None: testVer.fromJson(version_result.version_json.get('ver', None)) if testVer.is_valid: break # we found something! else: try: Logger.debug('RCPAPI: Giving up on ' + str(device)) comms.close() finally: pass except Exception as detail: Logger.error('RCPAPI: Not found on ' + str(device) + " " + str(detail)) Logger.error(traceback.format_exc()) try: comms.close() finally: pass if testVer.is_valid: Logger.debug("RCPAPI: Found device version " + str(testVer) + " on port: " + str(comms.device)) self.detect_win(testVer) self._auto_detect_event.clear() self._settings.userPrefs.set_pref('preferences', 'last_known_device', comms.device) else: Logger.debug('RCPAPI: Did not find device') comms.close() comms.device = None if self.detect_fail_callback: self.detect_fail_callback() except Exception as e: Logger.error('RCPAPI: Error running auto detect: ' + str(e)) Logger.error(traceback.format_exc()) if self.detect_fail_callback: self.detect_fail_callback() finally: Logger.debug("RCPAPI: auto detect finished. port=" + str(comms.device)) self._auto_detect_busy.clear() self.removeListener("ver", on_ver_win) self.sendCommandLock.release() comms.device = None sleep(AUTODETECT_COOLOFF_TIME) safe_thread_exit() Logger.debug('RCPAPI: auto_detect_worker exiting')