def saveConfig(self): configName = str(self.profileCombo.currentText()) saveConfig = {} inputConfig = {'inputdevice': {'axis': []}} for a in self.axismapping: newC = {} if "id" in self.axismapping[a]: newC['id'] = self.axismapping[a]['id'] elif "ids" in self.axismapping[a]: newC['ids'] = self.axismapping[a]['ids'] else: raise Exception("Problem during save") newC['key'] = a newC['name'] = a newC['scale'] = self.axismapping[a]['scale'] newC['type'] = "Input.AXIS" inputConfig['inputdevice']['axis'].append(newC) for a in self.buttonmapping: newC = {} newC['id'] = self.buttonmapping[a]['id'] newC['type'] = "Input.BUTTON" if (a.find("Neg") >= 0): newC['scale'] = -1.0 else: newC['scale'] = 1.0 if ("pitch" in a): newC['key'] = "pitchcal" newC['name'] = a if ("roll" in a): newC['key'] = "rollcal" newC['name'] = a if ("killswitch" in a): newC['key'] = "estop" newC['name'] = a if ("exit" in a): newC['key'] = "exit" newC['name'] = a inputConfig['inputdevice']['axis'].append(newC) inputConfig['inputdevice']['name'] = configName inputConfig['inputdevice']['updateperiod'] = 10 saveConfig['inputconfig'] = inputConfig config_name = self.profileCombo.currentText() filename = ConfigManager().configs_dir + "/%s.json" % config_name logger.info("Saving config to [%s]", filename) json_data = open(filename, 'w') json_data.write(json.dumps(saveConfig, indent=2)) json_data.close() ConfigManager().conf_needs_reload.call(config_name) self.close()
def set_input_map(self, device_name, input_map_name): """Load and set an input device map with the given name""" settings = ConfigManager().get_settings(input_map_name) if settings: self.springy_throttle = settings["springythrottle"] self._input_map = ConfigManager().get_config(input_map_name) self._get_device_from_name(device_name).input_map = self._input_map self._get_device_from_name(device_name).input_map_name = input_map_name Config().get("device_config_mapping")[device_name] = input_map_name
def __init__(self): QThread.__init__(self) # self.moveToThread(self) # TODO: Should be OS dependant print "setting input.py inputdevice" self.inputdevice = PyGameReader() self.startInputSignal.connect(self.startInput) self.stopInputSignal.connect(self.stopInput) self.updateMinMaxThrustSignal.connect(self.updateMinMaxThrust) self.update_trim_roll_signal.connect(self._update_trim_roll) self.update_trim_pitch_signal.connect(self._update_trim_pitch) self.updateMaxRPAngleSignal.connect(self.updateMaxRPAngle) self.updateThrustLoweringSlewrateSignal.connect( self.updateThrustLoweringSlewrate) self.updateMaxYawRateSignal.connect(self.updateMaxYawRate) self.maxRPAngle = 0 self.thrustDownSlew = 0 self.thrustSlewEnabled = False self.slewEnableLimit = 0 self.maxYawRate = 0 self.detectAxis = False self.emergencyStop = False self.oldThrust = 0 self._trim_roll = Config().get("trim_roll") self._trim_pitch = Config().get("trim_pitch") # TODO: The polling interval should be set from config file self.readTimer = QTimer() self.readTimer.setInterval(10) self.connect(self.readTimer, SIGNAL("timeout()"), self.readInput) self._discovery_timer = QTimer() self._discovery_timer.setInterval(1000) self.connect(self._discovery_timer, SIGNAL("timeout()"), self._do_device_discovery) self._discovery_timer.start() self._available_devices = {} # Check if user config exists, otherwise copy files if not os.path.isdir(ConfigManager().configs_dir): logger.info("No user config found, copying dist files") os.makedirs(ConfigManager().configs_dir) for f in glob.glob(sys.path[0] + "/cfclient/configs/input/[A-Za-z]*.json"): shutil.copy2(f, ConfigManager().configs_dir)
def populateDropDown(self): configs = ConfigManager().get_list_of_configs() if len(configs): self.loadButton.setEnabled(True) for c in configs: self.profileCombo.addItem(c) logger.info("Found inputdevice [%s]", c)
def start_input(self, device_name, config_name): """ Start reading input from the device with name device_name using config config_name """ try: device_id = self._available_devices[device_name] self.inputdevice.start_input( device_id, ConfigManager().get_config(config_name)) settings = ConfigManager().get_settings(config_name) self._springy_throttle = settings["springythrottle"] self._read_timer.start() except Exception: self.device_error.call( "Error while opening/initializing input device\n\n%s" % (traceback.format_exc()))
def startInput(self, device_name, config_name): """Start reading input from the device with name device_name using config config_name""" try: device_id = self._available_devices[device_name] self.inputdevice.startInput(device_id, ConfigManager().get_config(config_name)) self.readTimer.start() except Exception: self.inputDeviceErrorSignal.emit("Error while opening/initializing input device\n\n%s" % (traceback.format_exc()))
def _save_config(self): configName = str(self.profileCombo.currentText()) mapping = {'inputconfig': {'inputdevice': {'axis': []}}} # Create intermediate structure for the configuration file funcs = {} for m in self._map: key = self._map[m]["key"] if key not in funcs: funcs[key] = [] funcs[key].append(self._map[m]) # Create a mapping for each axis, take care to handle # split axis configurations for a in funcs: func = funcs[a] axis = {} # Check for split axis if len(func) > 1: axis["ids"] = [func[0]["id"], func[1]["id"]] axis["scale"] = func[1]["scale"] else: axis["id"] = func[0]["id"] axis["scale"] = func[0]["scale"] axis["key"] = func[0]["key"] axis["name"] = func[0]["key"] # Name isn't used... axis["type"] = func[0]["type"] mapping["inputconfig"]["inputdevice"]["axis"].append(axis) mapping["inputconfig"]['inputdevice']['name'] = configName mapping["inputconfig"]['inputdevice']['updateperiod'] = 10 config_name = self.profileCombo.currentText() filename = ConfigManager().configs_dir + "/%s.json" % config_name logger.info("Saving config to [%s]", filename) json_data = open(filename, 'w') json_data.write(json.dumps(mapping, indent=2)) json_data.close() ConfigManager().conf_needs_reload.call(config_name) self.close()
def __init__(self, do_device_discovery=True): # TODO: Should be OS dependant self.inputdevice = PyGameReader() self.maxRPAngle = 0 self.thrustDownSlew = 0 self.thrustSlewEnabled = False self.slewEnableLimit = 0 self.maxYawRate = 0 self.detectAxis = False self.emergencyStop = False self.oldThrust = 0 self._trim_roll = Config().get("trim_roll") self._trim_pitch = Config().get("trim_pitch") # TODO: The polling interval should be set from config file self.readTimer = PeriodicTimer(0.01, self.readInput) if do_device_discovery: self._discovery_timer = PeriodicTimer(1.0, self._do_device_discovery) self._discovery_timer.start() self._available_devices = {} # Check if user config exists, otherwise copy files if not os.path.isdir(ConfigManager().configs_dir): logger.info("No user config found, copying dist files") os.makedirs(ConfigManager().configs_dir) for f in glob.glob(sys.path[0] + "/cfclient/configs/input/[A-Za-z]*.json"): shutil.copy2(f, ConfigManager().configs_dir) ConfigManager().get_list_of_configs() self.input_updated = Caller() self.rp_trim_updated = Caller() self.emergency_stop_updated = Caller() self.device_discovery = Caller() self.device_error = Caller()
def _load_input_data(self): self.joystickReader.stop_input() # Populate combo box with available input device configurations for c in ConfigManager().get_list_of_configs(): node = QAction(c, self._menu_mappings, checkable=True, enabled=False) node.toggled.connect(self._inputconfig_selected) self.configGroup.addAction(node) self._menu_mappings.addAction(node)
def _load_config_from_file(self): loaded_map = ConfigManager().get_config(self.profileCombo.currentText()) if loaded_map: self._input.set_raw_input_map(loaded_map) self._map = loaded_map else: logger.warning("Could not load configfile [%s]", self.profileCombo.currentText()) self._show_error("Could not load config", "Could not load config [%s]" % self.profileCombo.currentText()) self._check_and_enable_saving()
def loadConfig(self): conf = ConfigManager().get_config(self.profileCombo.currentText()) self._reset_mapping() if (conf != None): for c in conf: if (conf[c]['type'] == "Input.BUTTON"): self.parseButtonConfig(conf[c]['key'], conf[c]['id'], conf[c]['scale']) elif (conf[c]['type'] == "Input.AXIS"): self.parseAxisConfig(conf[c]['key'], conf[c]['id'], conf[c]['scale']) else: logger.warning("Could not load configfile [%s]", self.profileCombo.currentText()) self.showError("Could not load config", "Could not load config [%s]" % self.profileCombo.currentText()) self.checkAndEnableSave()
def device_discovery(self, devs): """Called when new devices have been added""" for menu in self._all_role_menus: role_menu = menu["rolemenu"] mux_menu = menu["muxmenu"] dev_group = QActionGroup(role_menu, exclusive=True) for d in devs: dev_node = QAction(d.name, role_menu, checkable=True, enabled=True) role_menu.addAction(dev_node) dev_group.addAction(dev_node) dev_node.toggled.connect(self._inputdevice_selected) map_node = None if d.supports_mapping: map_node = QMenu(" Input map", role_menu, enabled=False) map_group = QActionGroup(role_menu, exclusive=True) # Connect device node to map node for easy # enabling/disabling when selection changes and device # to easily enable it dev_node.setData((map_node, d)) for c in ConfigManager().get_list_of_configs(): node = QAction(c, map_node, checkable=True, enabled=True) node.toggled.connect(self._inputconfig_selected) map_node.addAction(node) # Connect all the map nodes back to the device # action node where we can access the raw device node.setData(dev_node) map_group.addAction(node) # If this device hasn't been found before, then # select the default mapping for it. if d not in self._available_devices: last_map = Config().get("device_config_mapping") if d.name in last_map and last_map[d.name] == c: node.setChecked(True) role_menu.addMenu(map_node) dev_node.setData((map_node, d, mux_menu)) # Update the list of what devices we found # to avoid selecting default mapping for all devices when # a new one is inserted self._available_devices = () for d in devs: self._available_devices += (d,) # Only enable MUX nodes if we have enough devies to cover # the roles for mux_node in self._all_mux_nodes: (mux, sub_nodes) = mux_node.data() if len(mux.supported_roles()) <= len(self._available_devices): mux_node.setEnabled(True) # TODO: Currently only supports selecting default mux if self._all_mux_nodes[0].isEnabled(): self._all_mux_nodes[0].setChecked(True) # If the previous length of the available devies was 0, then select # the default on. If that's not available then select the first # on in the list. # TODO: This will only work for the "Normal" mux so this will be # selected by default if Config().get("input_device") in [d.name for d in devs]: for dev_menu in self._all_role_menus[0]["rolemenu"].actions(): if dev_menu.text() == Config().get("input_device"): dev_menu.setChecked(True) else: # Select the first device in the first mux (will always be "Normal" # mux) self._all_role_menus[0]["rolemenu"].actions()[0].setChecked(True) logger.info("Select first device") self._update_input_device_footer()
def __init__(self, do_device_discovery=True, cf=None): # TODO: Should be OS dependant self.inputdevice = AiController(cf) self._min_thrust = 0 self._max_thrust = 0 self._thrust_slew_rate = 0 self._thrust_slew_enabled = False self._thrust_slew_limit = 0 self._emergency_stop = False self._has_pressure_sensor = False self._old_thrust = 0 self._old_alt_hold = False self._trim_roll = Config().get("trim_roll") self._trim_pitch = Config().get("trim_pitch") if (Config().get("flightmode") is "Normal"): self._max_yaw_rate = Config().get("normal_max_yaw") self._max_rp_angle = Config().get("normal_max_rp") # Values are stored at %, so use the functions to set the values self.set_thrust_limits(Config().get("normal_min_thrust"), Config().get("normal_max_thrust")) self.set_thrust_slew_limiting(Config().get("normal_slew_rate"), Config().get("normal_slew_limit")) else: self._max_yaw_rate = Config().get("max_yaw") self._max_rp_angle = Config().get("max_rp") # Values are stored at %, so use the functions to set the values self.set_thrust_limits(Config().get("min_thrust"), Config().get("max_thrust")) self.set_thrust_slew_limiting(Config().get("slew_rate"), Config().get("slew_limit")) self._dev_blacklist = None if (len(Config().get("input_device_blacklist")) > 0): self._dev_blacklist = re.compile( Config().get("input_device_blacklist")) logger.info("Using device blacklist [{}]".format( Config().get("input_device_blacklist"))) self._available_devices = {} # TODO: The polling interval should be set from config file self._read_timer = PeriodicTimer(0.01, self.read_input) if do_device_discovery: self._discovery_timer = PeriodicTimer(1.0, self._do_device_discovery) self._discovery_timer.start() # Check if user config exists, otherwise copy files if not os.path.exists(ConfigManager().configs_dir): logger.info("No user config found, copying dist files") os.makedirs(ConfigManager().configs_dir) for f in glob.glob(sys.path[0] + "/cfclient/configs/input/[A-Za-z]*.json"): dest = os.path.join(ConfigManager().configs_dir, os.path.basename(f)) if not os.path.isfile(dest): logger.debug("Copying %s", f) shutil.copy2(f, ConfigManager().configs_dir) ConfigManager().get_list_of_configs() self.input_updated = Caller() self.rp_trim_updated = Caller() self.emergency_stop_updated = Caller() self.device_discovery = Caller() self.device_error = Caller() self.althold_updated = Caller()
def __init__(self, do_device_discovery=True): self._input_device = None self._min_thrust = 0 self._max_thrust = 0 self._thrust_slew_rate = 0 self._thrust_slew_enabled = False self._thrust_slew_limit = 0 self._has_pressure_sensor = False self._old_thrust = 0 self._old_raw_thrust = 0 self._old_alt_hold = False self._springy_throttle = True self._prev_values = {} self._trim_roll = Config().get("trim_roll") self._trim_pitch = Config().get("trim_pitch") self._input_map = None self._mux = [ NoMux(self), SelectiveMux(self), TakeOverMux(self), MixMux(self), TakeOverSelectiveMux(self) ] # Set NoMux as default self._selected_mux = self._mux[0] if Config().get("flightmode") is "Normal": self.set_yaw_limit(Config().get("normal_max_yaw")) self.set_rp_limit(Config().get("normal_max_rp")) # Values are stored at %, so use the functions to set the values self.set_thrust_limits(Config().get("normal_min_thrust"), Config().get("normal_max_thrust")) self.set_thrust_slew_limiting(Config().get("normal_slew_rate"), Config().get("normal_slew_limit")) else: self.set_yaw_limit(Config().get("max_yaw")) self.set_rp_limit(Config().get("max_rp")) # Values are stored at %, so use the functions to set the values self.set_thrust_limits(Config().get("min_thrust"), Config().get("max_thrust")) self.set_thrust_slew_limiting(Config().get("slew_rate"), Config().get("slew_limit")) self._dev_blacklist = None if len(Config().get("input_device_blacklist")) > 0: self._dev_blacklist = re.compile( Config().get("input_device_blacklist")) logger.info("Using device blacklist [{}]".format( Config().get("input_device_blacklist"))) self._available_devices = {} # TODO: The polling interval should be set from config file self._read_timer = PeriodicTimer(0.01, self.read_input) if do_device_discovery: self._discovery_timer = PeriodicTimer(1.0, self._do_device_discovery) self._discovery_timer.start() # Check if user config exists, otherwise copy files if not os.path.exists(ConfigManager().configs_dir): logger.info("No user config found, copying dist files") os.makedirs(ConfigManager().configs_dir) for f in glob.glob(sys.path[0] + "/cfclient/configs/input/[A-Za-z]*.json"): dest = os.path.join(ConfigManager().configs_dir, os.path.basename(f)) if not os.path.isfile(dest): logger.debug("Copying %s", f) shutil.copy2(f, ConfigManager().configs_dir) ConfigManager().get_list_of_configs() self.input_updated = Caller() self.rp_trim_updated = Caller() self.emergency_stop_updated = Caller() self.device_discovery = Caller() self.device_error = Caller() self.althold_updated = Caller() self.alt1_updated = Caller() self.alt2_updated = Caller() # Call with 3 bools (rp_limiting, yaw_limiting, thrust_limiting) self.limiting_updated = Caller()
def __init__(self, *args): super(MainUI, self).__init__(*args) self.setupUi(self) ###################################################### ### By lxrocks ### 'Skinny Progress Bar' tweak for Yosemite ### Tweak progress bar - artistic I am not - so pick your own colors !!! ### Only apply to Yosemite ###################################################### import platform if platform.system() == 'Darwin': (Version,junk,machine) = platform.mac_ver() logger.info("This is a MAC - checking if we can apply Progress Bar Stylesheet for Yosemite Skinny Bars ") yosemite = (10,10,0) tVersion = tuple(map(int, (Version.split(".")))) if tVersion >= yosemite: logger.info( "Found Yosemite:") tcss = """ QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; } QProgressBar::chunk { background-color: #05B8CC; } """ self.setStyleSheet(tcss) else: logger.info( "Pre-Yosemite") ###################################################### self.cf = Crazyflie(ro_cache=sys.path[0] + "/cflib/cache", rw_cache=sys.path[1] + "/cache") cflib.crtp.init_drivers(enable_debug_driver=Config() .get("enable_debug_driver")) # Create the connection dialogue self.connectDialogue = ConnectDialogue() # Create and start the Input Reader self._statusbar_label = QLabel("Loading device and configuration.") self.statusBar().addWidget(self._statusbar_label) self.joystickReader = JoystickReader() self._active_device = "" self.configGroup = QActionGroup(self._menu_mappings, exclusive=True) self._load_input_data() ConfigManager().conf_needs_reload.add_callback(self._reload_configs) # Connections for the Connect Dialogue self.connectDialogue.requestConnectionSignal.connect(self.cf.open_link) self.cf.connection_failed.add_callback(self.connectionFailedSignal.emit) self.connectionFailedSignal.connect(self._connection_failed) self._input_device_error_signal.connect(self._display_input_device_error) self.joystickReader.device_error.add_callback( self._input_device_error_signal.emit) self._input_discovery_signal.connect(self.device_discovery) self.joystickReader.device_discovery.add_callback( self._input_discovery_signal.emit) # Connect UI signals self.menuItemConnect.triggered.connect(self._connect) self.logConfigAction.triggered.connect(self._show_connect_dialog) self.connectButton.clicked.connect(self._connect) self.quickConnectButton.clicked.connect(self._quick_connect) self.menuItemQuickConnect.triggered.connect(self._quick_connect) self.menuItemConfInputDevice.triggered.connect(self._show_input_device_config_dialog) self.menuItemExit.triggered.connect(self.closeAppRequest) self.batteryUpdatedSignal.connect(self._update_vbatt) self._menuitem_rescandevices.triggered.connect(self._rescan_devices) self._menuItem_openconfigfolder.triggered.connect(self._open_config_folder) self._auto_reconnect_enabled = Config().get("auto_reconnect") self.autoReconnectCheckBox.toggled.connect( self._auto_reconnect_changed) self.autoReconnectCheckBox.setChecked(Config().get("auto_reconnect")) self.joystickReader.input_updated.add_callback( self.cf.commander.send_setpoint) # Connection callbacks and signal wrappers for UI protection self.cf.connected.add_callback(self.connectionDoneSignal.emit) self.connectionDoneSignal.connect(self._connected) self.cf.disconnected.add_callback(self.disconnectedSignal.emit) self.disconnectedSignal.connect( lambda linkURI: self._update_ui_state(UIState.DISCONNECTED, linkURI)) self.cf.connection_lost.add_callback(self.connectionLostSignal.emit) self.connectionLostSignal.connect(self._connection_lost) self.cf.connection_requested.add_callback( self.connectionInitiatedSignal.emit) self.connectionInitiatedSignal.connect( lambda linkURI: self._update_ui_state(UIState.CONNECTING, linkURI)) self._log_error_signal.connect(self._logging_error) # Connect link quality feedback self.cf.link_quality_updated.add_callback(self.linkQualitySignal.emit) self.linkQualitySignal.connect( lambda percentage: self.linkQualityBar.setValue(percentage)) # Set UI state in disconnected buy default self._update_ui_state(UIState.DISCONNECTED) # Parse the log configuration files self.logConfigReader = LogConfigReader(self.cf) self._current_input_config = None self._active_config = None self._active_config = None self.inputConfig = None # Add things to helper so tabs can access it cfclient.ui.pluginhelper.cf = self.cf cfclient.ui.pluginhelper.inputDeviceReader = self.joystickReader cfclient.ui.pluginhelper.logConfigReader = self.logConfigReader self.logConfigDialogue = LogConfigDialogue(cfclient.ui.pluginhelper) self._bootloader_dialog = BootloaderDialog(cfclient.ui.pluginhelper) self._cf2config_dialog = Cf2ConfigDialog(cfclient.ui.pluginhelper) self._cf1config_dialog = Cf1ConfigDialog(cfclient.ui.pluginhelper) self.menuItemBootloader.triggered.connect(self._bootloader_dialog.show) self._about_dialog = AboutDialog(cfclient.ui.pluginhelper) self.menuItemAbout.triggered.connect(self._about_dialog.show) self._menu_cf2_config.triggered.connect(self._cf2config_dialog.show) self._menu_cf1_config.triggered.connect(self._cf1config_dialog.show) # Loading toolboxes (A bit of magic for a lot of automatic) self.toolboxes = [] self.toolboxesMenuItem.setMenu(QtGui.QMenu()) for t_class in cfclient.ui.toolboxes.toolboxes: toolbox = t_class(cfclient.ui.pluginhelper) dockToolbox = MyDockWidget(toolbox.getName()) dockToolbox.setWidget(toolbox) self.toolboxes += [dockToolbox, ] # Add menu item for the toolbox item = QtGui.QAction(toolbox.getName(), self) item.setCheckable(True) item.triggered.connect(self.toggleToolbox) self.toolboxesMenuItem.menu().addAction(item) dockToolbox.closed.connect(lambda: self.toggleToolbox(False)) # Setup some introspection item.dockToolbox = dockToolbox item.menuItem = item dockToolbox.dockToolbox = dockToolbox dockToolbox.menuItem = item # Load and connect tabs self.tabsMenuItem.setMenu(QtGui.QMenu()) tabItems = {} self.loadedTabs = [] for tabClass in cfclient.ui.tabs.available: tab = tabClass(self.tabs, cfclient.ui.pluginhelper) item = QtGui.QAction(tab.getMenuName(), self) item.setCheckable(True) item.toggled.connect(tab.toggleVisibility) self.tabsMenuItem.menu().addAction(item) tabItems[tab.getTabName()] = item self.loadedTabs.append(tab) if not tab.enabled: item.setEnabled(False) # First instantiate all tabs and then open them in the correct order try: for tName in Config().get("open_tabs").split(","): t = tabItems[tName] if (t != None and t.isEnabled()): # Toggle though menu so it's also marked as open there t.toggle() except Exception as e: logger.warning("Exception while opening tabs [{}]".format(e)) # Check which Input muxes are available self._mux_group = QActionGroup(self._menu_mux, exclusive=True) for m in self.joystickReader.available_mux(): node = QAction(m, self._menu_mux, checkable=True, enabled=True) node.toggled.connect(self._mux_selected) self._mux_group.addAction(node) self._menu_mux.addAction(node) # TODO: Temporary self._input_dev_stack = [] self._menu_mux.actions()[0].setChecked(True) if Config().get("enable_input_muxing"): self._menu_mux.setEnabled(True) else: logger.info("Input device muxing disabled in config") self._mapping_support = True
def __init__(self, do_device_discovery=True): self._input_device = None self._mux = [ NoMux(self), TakeOverSelectiveMux(self), TakeOverMux(self) ] # Set NoMux as default self._selected_mux = self._mux[0] self.min_thrust = 0 self.max_thrust = 0 self._thrust_slew_rate = 0 self.thrust_slew_enabled = False self.thrust_slew_limit = 0 self.has_pressure_sensor = False self._hover_max_height = MAX_TARGET_HEIGHT self.max_rp_angle = 0 self.max_yaw_rate = 0 try: self.set_assisted_control(Config().get("assistedControl")) except KeyError: self.set_assisted_control(JoystickReader.ASSISTED_CONTROL_ALTHOLD) self._old_thrust = 0 self._old_raw_thrust = 0 self.springy_throttle = True self._target_height = INITAL_TAGET_HEIGHT self.trim_roll = Config().get("trim_roll") self.trim_pitch = Config().get("trim_pitch") self._rp_dead_band = 0.1 self._input_map = None if Config().get("flightmode") == "Normal": self.max_yaw_rate = Config().get("normal_max_yaw") self.max_rp_angle = Config().get("normal_max_rp") # Values are stored at %, so use the functions to set the values self.min_thrust = Config().get("normal_min_thrust") self.max_thrust = Config().get("normal_max_thrust") self.thrust_slew_limit = Config().get("normal_slew_limit") self.thrust_slew_rate = Config().get("normal_slew_rate") else: self.max_yaw_rate = Config().get("max_yaw") self.max_rp_angle = Config().get("max_rp") # Values are stored at %, so use the functions to set the values self.min_thrust = Config().get("min_thrust") self.max_thrust = Config().get("max_thrust") self.thrust_slew_limit = Config().get("slew_limit") self.thrust_slew_rate = Config().get("slew_rate") self._dev_blacklist = None if len(Config().get("input_device_blacklist")) > 0: self._dev_blacklist = re.compile( Config().get("input_device_blacklist")) logger.info("Using device blacklist [{}]".format( Config().get("input_device_blacklist"))) self._available_devices = {} # TODO: The polling interval should be set from config file self._read_timer = PeriodicTimer(INPUT_READ_PERIOD, self.read_input) if do_device_discovery: self._discovery_timer = PeriodicTimer(1.0, self._do_device_discovery) self._discovery_timer.start() # Check if user config exists, otherwise copy files if not os.path.exists(ConfigManager().configs_dir): logger.info("No user config found, copying dist files") os.makedirs(ConfigManager().configs_dir) for f in glob.glob(cfclient.module_path + "/configs/input/[A-Za-z]*.json"): dest = os.path.join(ConfigManager().configs_dir, os.path.basename(f)) if not os.path.isfile(dest): logger.debug("Copying %s", f) shutil.copy2(f, ConfigManager().configs_dir) ConfigManager().get_list_of_configs() self.input_updated = Caller() self.assisted_input_updated = Caller() self.heighthold_input_updated = Caller() self.hover_input_updated = Caller() self.rp_trim_updated = Caller() self.emergency_stop_updated = Caller() self.device_discovery = Caller() self.device_error = Caller() self.assisted_control_updated = Caller() self.alt1_updated = Caller() self.alt2_updated = Caller() # Call with 3 bools (rp_limiting, yaw_limiting, thrust_limiting) self.limiting_updated = Caller()
def _populate_config_dropdown(self): configs = ConfigManager().get_list_of_configs() if len(configs): self.loadButton.setEnabled(True) for c in configs: self.profileCombo.addItem(c)
def __init__(self, *args): super(MainUI, self).__init__(*args) self.setupUi(self) self.cf = Crazyflie(ro_cache=sys.path[0] + "/cflib/cache", rw_cache=sys.path[1] + "/cache") cflib.crtp.init_drivers( enable_debug_driver=GuiConfig().get("enable_debug_driver")) # Create the connection dialogue self.connectDialogue = ConnectDialogue() # Create and start the Input Reader self._statusbar_label = QLabel("Loading device and configuration.") self.statusBar().addWidget(self._statusbar_label) self.joystickReader = JoystickReader(cf=self.cf) self._active_device = "" self.configGroup = QActionGroup(self._menu_mappings, exclusive=True) self._load_input_data() self._update_input ConfigManager().conf_needs_reload.add_callback(self._reload_configs) # Connections for the Connect Dialogue self.connectDialogue.requestConnectionSignal.connect(self.cf.open_link) self.connectionDoneSignal.connect(self.connectionDone) self.cf.connection_failed.add_callback( self.connectionFailedSignal.emit) self.connectionFailedSignal.connect(self.connectionFailed) self._input_device_error_signal.connect(self.inputDeviceError) self.joystickReader.device_error.add_callback( self._input_device_error_signal.emit) self._input_discovery_signal.connect(self.device_discovery) self.joystickReader.device_discovery.add_callback( self._input_discovery_signal.emit) # Connect UI signals self.menuItemConnect.triggered.connect(self.connectButtonClicked) self.logConfigAction.triggered.connect(self.doLogConfigDialogue) self.connectButton.clicked.connect(self.connectButtonClicked) self.quickConnectButton.clicked.connect(self.quickConnect) self.menuItemQuickConnect.triggered.connect(self.quickConnect) self.menuItemConfInputDevice.triggered.connect(self.configInputDevice) self.menuItemExit.triggered.connect(self.closeAppRequest) self.batteryUpdatedSignal.connect(self.updateBatteryVoltage) self._menuitem_rescandevices.triggered.connect(self._rescan_devices) self._menuItem_openconfigfolder.triggered.connect( self._open_config_folder) self._auto_reconnect_enabled = GuiConfig().get("auto_reconnect") self.autoReconnectCheckBox.toggled.connect( self._auto_reconnect_changed) self.autoReconnectCheckBox.setChecked( GuiConfig().get("auto_reconnect")) # Do not queue data from the controller output to the Crazyflie wrapper # to avoid latency #self.joystickReader.sendControlSetpointSignal.connect( # self.cf.commander.send_setpoint, # Qt.DirectConnection) self.joystickReader.input_updated.add_callback( self.cf.commander.send_setpoint) # Connection callbacks and signal wrappers for UI protection self.cf.connected.add_callback(self.connectionDoneSignal.emit) self.connectionDoneSignal.connect(self.connectionDone) self.cf.disconnected.add_callback(self.disconnectedSignal.emit) self.disconnectedSignal.connect( lambda linkURI: self.setUIState(UIState.DISCONNECTED, linkURI)) self.cf.connection_lost.add_callback(self.connectionLostSignal.emit) self.connectionLostSignal.connect(self.connectionLost) self.cf.connection_requested.add_callback( self.connectionInitiatedSignal.emit) self.connectionInitiatedSignal.connect( lambda linkURI: self.setUIState(UIState.CONNECTING, linkURI)) self._log_error_signal.connect(self._logging_error) # Connect link quality feedback self.cf.link_quality_updated.add_callback(self.linkQualitySignal.emit) self.linkQualitySignal.connect( lambda percentage: self.linkQualityBar.setValue(percentage)) # Set UI state in disconnected buy default self.setUIState(UIState.DISCONNECTED) # Parse the log configuration files self.logConfigReader = LogConfigReader(self.cf) # Add things to helper so tabs can access it cfclient.ui.pluginhelper.cf = self.cf cfclient.ui.pluginhelper.inputDeviceReader = self.joystickReader cfclient.ui.pluginhelper.logConfigReader = self.logConfigReader self.logConfigDialogue = LogConfigDialogue(cfclient.ui.pluginhelper) self._bootloader_dialog = BootloaderDialog(cfclient.ui.pluginhelper) self.menuItemBootloader.triggered.connect(self._bootloader_dialog.show) self._about_dialog = AboutDialog(cfclient.ui.pluginhelper) self.menuItemAbout.triggered.connect(self._about_dialog.show) # Loading toolboxes (A bit of magic for a lot of automatic) self.toolboxes = [] self.toolboxesMenuItem.setMenu(QtGui.QMenu()) for t_class in cfclient.ui.toolboxes.toolboxes: toolbox = t_class(cfclient.ui.pluginhelper) dockToolbox = MyDockWidget(toolbox.getName()) dockToolbox.setWidget(toolbox) self.toolboxes += [ dockToolbox, ] # Add menu item for the toolbox item = QtGui.QAction(toolbox.getName(), self) item.setCheckable(True) item.triggered.connect(self.toggleToolbox) self.toolboxesMenuItem.menu().addAction(item) dockToolbox.closed.connect(lambda: self.toggleToolbox(False)) # Setup some introspection item.dockToolbox = dockToolbox item.menuItem = item dockToolbox.dockToolbox = dockToolbox dockToolbox.menuItem = item # Load and connect tabs self.tabsMenuItem.setMenu(QtGui.QMenu()) tabItems = {} self.loadedTabs = [] for tabClass in cfclient.ui.tabs.available: tab = tabClass(self.tabs, cfclient.ui.pluginhelper) item = QtGui.QAction(tab.getMenuName(), self) item.setCheckable(True) item.toggled.connect(tab.toggleVisibility) self.tabsMenuItem.menu().addAction(item) tabItems[tab.getTabName()] = item self.loadedTabs.append(tab) if not tab.enabled: item.setEnabled(False) # First instantiate all tabs and then open them in the correct order try: for tName in GuiConfig().get("open_tabs").split(","): t = tabItems[tName] if (t != None and t.isEnabled()): # Toggle though menu so it's also marked as open there t.toggle() except Exception as e: logger.warning("Exception while opening tabs [%s]", e)
def _save_config(self): config_name = str(self.profileCombo.currentText()) ConfigManager().save_config(self._map, config_name) self.close()