def load(self, argv): ''' @param argv: the list with argv parameter needed to load the launch file. The name and value are separated by C{:=} @type argv: C{[str]} @return True, if the launch file was loaded @rtype boolean @raise LaunchConfigException: on load errors ''' try: roscfg = roslaunch.ROSLaunchConfig() loader = roslaunch.XmlLoader() self.argv = self.resolveArgs(argv) loader.load(self.Filename, roscfg, verbose=False, argv=self.argv) self.__roscfg = roscfg nm.file_watcher().add_launch(self.__masteruri, self.__launchFile, self.__launch_id, self.getIncludedFiles(self.Filename)) if not nm.is_local(nm.nameres().getHostname(self.__masteruri)): files = self.getIncludedFiles(self.Filename, regexp_list=[QRegExp("\\bdefault\\b"), QRegExp("\\bvalue=.*pkg:\/\/\\b"), QRegExp("\\bvalue=.*package:\/\/\\b"), QRegExp("\\bvalue=.*\$\(find\\b")]) nm.file_watcher_param().add_launch(self.__masteruri, self.__launchFile, self.__launch_id, files) except roslaunch.XmlParseException, e: test = list(re.finditer(r"environment variable '\w+' is not set", str(e))) message = str(e) if test: message = ''.join([message, '\n', 'environment substitution is not supported, use "arg" instead!']) raise LaunchConfigException(message)
def __init__(self, launch_file, package=None, masteruri=None, argv=[]): ''' Creates the LaunchConfig object. The launch file will be not loaded on creation, first on request of Roscfg value. @param launch_file: The absolute or relative path with the launch file. By using relative path a package must be valid for remote launches. @type launch_file: C{str} @param package: the package containing the launch file. If None the launch_file will be used to determine the launch file. No remote launches a possible without a valid package. @type package: C{str} or C{None} @param masteruri: The URL of the ROS master. @type masteruri: C{str} or C{None} @param argv: the list the arguments needed for loading the given launch file @type argv: C{[str]} @raise roslaunch.XmlParseException: if the launch file can't be found. ''' QObject.__init__(self) self.__launchFile = launch_file self.__package = package_name(os.path.dirname(self.__launchFile))[0] if package is None else package self.__masteruri = masteruri if masteruri is not None else 'localhost' self.__roscfg = None self.argv = argv self.__reqTested = False self.__argv_values = dict() self.global_param_done = [] # masteruri's where the global parameters are registered self.hostname = nm.nameres().getHostname(self.__masteruri) self.__launch_id = '%.9f' % time.time() nm.file_watcher().add_launch(self.__masteruri, self.__launchFile, self.__launch_id, [self.__launchFile])
def __del__(self): # Delete to avoid segfault if the LaunchConfig class is destroyed recently # after creation and xmlrpclib.ServerProxy process a method call. nm.file_watcher().rem_launch(self.__masteruri, self.__launchFile, self.__launch_id)
def runNode(cls, runcfg): ''' Start the node with given name from the given configuration. @param node: the name of the node (with name space) @type node: str @param launch_config: the configuration containing the node @type launch_config: LaunchConfig @param force2host: start the node on given host. @type force2host: str @param masteruri: force the masteruri. @type masteruri: str @param auto_pw_request: opens question dialog directly, use True only if the method is called from the main GUI thread @type auto_pw_request: bool @raise StartException: if the screen is not available on host. @raise Exception: on errors while resolving host @see: L{node_manager_fkie.is_local()} ''' # 'print "RUN node", node, time.time() n = runcfg.roslaunch_config.getNode(runcfg.node) if n is None: raise StartException(''.join(["Node '", runcfg.node, "' not found!"])) env = list(n.env_args) # set logging options if runcfg.logging is not None: if not runcfg.logging.is_default('console_format'): env.append(('ROSCONSOLE_FORMAT', '%s' % runcfg.logging.console_format)) if n.respawn: # set the respawn environment variables respawn_params = cls._get_respawn_params(rospy.names.ns_join(n.namespace, n.name), runcfg.roslaunch_config.Roscfg.params) if respawn_params['max'] > 0: env.append(('RESPAWN_MAX', '%d' % respawn_params['max'])) if respawn_params['min_runtime'] > 0: env.append(('RESPAWN_MIN_RUNTIME', '%d' % respawn_params['min_runtime'])) if respawn_params['delay'] > 0: env.append(('RESPAWN_DELAY', '%d' % respawn_params['delay'])) prefix = n.launch_prefix if n.launch_prefix is not None else '' if prefix.lower() == 'screen' or prefix.lower().find('screen ') != -1: rospy.loginfo("SCREEN prefix removed before start!") prefix = '' args = ['__ns:=%s' % n.namespace.rstrip(rospy.names.SEP), '__name:=%s' % n.name] if n.cwd is not None: args.append('__cwd:=%s' % n.cwd) # add remaps for remap in n.remap_args: args.append(''.join([remap[0], ':=', remap[1]])) # get host of the node host = runcfg.roslaunch_config.hostname env_loader = '' if n.machine_name: machine = runcfg.roslaunch_config.Roscfg.machines[n.machine_name] if machine.address not in ['localhost', '127.0.0.1']: host = machine.address if runcfg.masteruri is None: runcfg.masteruri = nm.nameres().masteruri(n.machine_name) # TODO: env-loader support? # if hasattr(machine, "env_loader") and machine.env_loader: # env_loader = machine.env_loader # set the host to the given host if runcfg.force2host is not None: host = runcfg.force2host # set the ROS_MASTER_URI if runcfg.masteruri is None: runcfg.masteruri = masteruri_from_ros() env.append(('ROS_MASTER_URI', runcfg.masteruri)) ros_hostname = NameResolution.get_ros_hostname(runcfg.masteruri) if ros_hostname: env.append(('ROS_HOSTNAME', ros_hostname)) abs_paths = list() # tuples of (parameter name, old value, new value) not_found_packages = list() # package names # set the global parameter if runcfg.masteruri is not None and runcfg.masteruri not in runcfg.roslaunch_config.global_param_done: global_node_names = cls.getGlobalParams(runcfg.roslaunch_config.Roscfg) rospy.loginfo("Register global parameter:\n %s", '\n '.join("%s%s" % (str(v)[:80], '...' if len(str(v)) > 80 else'') for v in global_node_names.values())) abs_paths[len(abs_paths):], not_found_packages[len(not_found_packages):] = cls._load_parameters(runcfg.masteruri, global_node_names, [], runcfg.user, runcfg.pw, runcfg.auto_pw_request) runcfg.roslaunch_config.global_param_done.append(runcfg.masteruri) # add params if runcfg.masteruri is not None: nodens = ''.join([n.namespace, n.name, rospy.names.SEP]) params = dict() for param, value in runcfg.roslaunch_config.Roscfg.params.items(): if param.startswith(nodens): params[param] = value clear_params = [] for cparam in runcfg.roslaunch_config.Roscfg.clear_params: if cparam.startswith(nodens): clear_params.append(cparam) rospy.loginfo("Delete parameter:\n %s", '\n '.join(clear_params)) rospy.loginfo("Register parameter:\n %s", '\n '.join("%s%s" % (str(v)[:80], '...' if len(str(v)) > 80 else'') for v in params.values())) abs_paths[len(abs_paths):], not_found_packages[len(not_found_packages):] = cls._load_parameters(runcfg.masteruri, params, clear_params, runcfg.user, runcfg.pw, runcfg.auto_pw_request) # 'print "RUN prepared", node, time.time() if nm.is_local(host, wait=True): nm.screen().testScreen() if runcfg.executable: cmd_type = runcfg.executable else: try: cmd = roslib.packages.find_node(n.package, n.type) except (Exception, roslib.packages.ROSPkgException) as starterr: # multiple nodes, invalid package raise StartException("Can't find resource: %s" % starterr) # handle diferent result types str or array of string if isinstance(cmd, types.StringTypes): cmd = [cmd] cmd_type = '' if cmd is None or len(cmd) == 0: raise StartException("%s in package [%s] not found!\n\nThe package " "was created?\nIs the binary executable?\n" % (n.type, n.package)) if len(cmd) > 1: if runcfg.auto_pw_request: # Open selection for executables, only if the method is called from the main GUI thread try: try: from python_qt_binding.QtGui import QInputDialog except: from python_qt_binding.QtWidgets import QInputDialog item, result = QInputDialog.getItem(None, 'Multiple executables %s in %s' % (n.type, n.package), 'Select an executable', cmd, 0, False) if result: # open the selected screen cmd_type = item else: return except: raise StartException('Multiple executables with same name in package found!') else: err = BinarySelectionRequest(cmd, 'Multiple executables') raise nm.InteractionNeededError(err, cls.runNode, (runcfg,)) else: cmd_type = cmd[0] # determine the current working path, Default: the package of the node cwd = get_ros_home() if not (n.cwd is None): if n.cwd == 'ROS_HOME': cwd = get_ros_home() elif n.cwd == 'node': cwd = os.path.dirname(cmd_type) cls._prepareROSMaster(runcfg.masteruri) node_cmd = [nm.settings().respawn_script if n.respawn else '', prefix, cmd_type] cmd_args = [nm.screen().getSceenCmd(runcfg.node)] cmd_args[len(cmd_args):] = node_cmd cmd_args.append(str(n.args)) cmd_args[len(cmd_args):] = args rospy.loginfo("RUN: %s", ' '.join(cmd_args)) new_env = dict(os.environ) new_env['ROS_MASTER_URI'] = runcfg.masteruri ros_hostname = NameResolution.get_ros_hostname(runcfg.masteruri) if ros_hostname: new_env['ROS_HOSTNAME'] = ros_hostname # add the namespace environment parameter to handle some cases, # e.g. rqt_cpp plugins if n.namespace: new_env['ROS_NAMESPACE'] = n.namespace # set logging options if runcfg.logging is not None: if not runcfg.logging.is_default('loglevel'): env.append(('ROSCONSOLE_CONFIG_FILE', nm.settings().rosconsole_cfg_file(n.package))) for key, value in env: new_env[key] = value SupervisedPopen(shlex.split(str(' '.join(cmd_args))), cwd=cwd, env=new_env, object_id="Run node", description="Run node " "[%s]%s" % (str(n.package), str(n.type))) nm.file_watcher().add_binary(cmd_type, runcfg.node, runcfg.masteruri, runcfg.roslaunch_config.Filename) else: # 'print "RUN REMOTE", node, time.time() # start remote if runcfg.roslaunch_config.PackageName is None: raise StartException("Can't run remote without a valid package name!") # thus the prefix parameters while the transfer are not separated if prefix: prefix = ''.join(['"', prefix, '"']) # setup environment env_command = '' if env_loader: rospy.logwarn("env_loader in machine tag currently not supported") raise StartException("env_loader in machine tag currently not supported") if env: new_env = dict() try: for (k, v) in env: v_value, is_abs_path, found, package = cls._resolve_abs_paths(v, host, runcfg.user, runcfg.pw, runcfg.auto_pw_request) new_env[k] = v_value if is_abs_path: abs_paths.append(('ENV', "%s=%s" % (k, v), "%s=%s" % (k, v_value))) if not found and package: not_found_packages.append(package) env_command = "env " + ' '.join(["%s=\'%s\'" % (k, v) for (k, v) in new_env.items()]) except nm.AuthenticationRequest as e: raise nm.InteractionNeededError(e, cls.runNode, (runcfg,)) startcmd = [env_command, nm.settings().start_remote_script, '--package', str(n.package), '--node_type', str(n.type), '--node_name', str(runcfg.node), '--node_respawn true' if n.respawn else ''] if runcfg.masteruri is not None: startcmd.append('--masteruri') startcmd.append(runcfg.masteruri) if prefix: startcmd[len(startcmd):] = ['--prefix', prefix] if runcfg.logging is not None: if not runcfg.logging.is_default('loglevel'): startcmd.append('--loglevel') startcmd.append(nm.settings().logging.loglevel) # rename the absolute paths in the args of the node node_args = [] try: for a in n.args.split(): a_value, is_abs_path, found, package = cls._resolve_abs_paths(a, host, runcfg.user, runcfg.pw, runcfg.auto_pw_request) node_args.append(a_value) if is_abs_path: abs_paths.append(('ARGS', a, a_value)) if not found and package: not_found_packages.append(package) startcmd[len(startcmd):] = node_args startcmd[len(startcmd):] = args rospy.loginfo("Run remote on %s: %s", host, str(' '.join(startcmd))) # 'print "RUN CALL", node, time.time() _, stdout, stderr, ok = nm.ssh().ssh_exec(host, startcmd, runcfg.user, runcfg.pw, runcfg.auto_pw_request, close_stdin=True) output = stdout.read() error = stderr.read() stdout.close() stderr.close() # 'print "RUN CALLOK", node, time.time() except nm.AuthenticationRequest as e: raise nm.InteractionNeededError(e, cls.runNode, (runcfg,)) if ok: if error: rospy.logwarn("ERROR while start '%s': %s", runcfg.node, error) raise StartException(str(''.join(['The host "', host, '" reports:\n', error]))) if output: rospy.loginfo("STDOUT while start '%s': %s", runcfg.node, output) # inform about absolute paths in parameter value if len(abs_paths) > 0: rospy.loginfo("Absolute paths found while start:\n%s", str('\n'.join([''.join([p, '\n OLD: ', ov, '\n NEW: ', nv]) for p, ov, nv in abs_paths]))) if len(not_found_packages) > 0: packages = '\n'.join(not_found_packages) raise StartException(str('\n'.join(['Some absolute paths are not renamed because following packages are not found on remote host:', packages])))