示例#1
0
def resolve_name_without_node_name(name):
    """
    The need for this function is complicated -- Topics and Services can be created before init_node is called.
    In general, this is okay, unless the name is a ~name, in which
    case we have to raise an ValueError

    @param name: ROS name to resolve
    @type  name: str
    @raise ValueError: if name is a ~name
    @raise ROSInitException: if name is remapped to a ~name
    """
    if is_private(name):
        raise ValueError("~name topics cannot be created before init_node() has been called")

    # we use the underlying rosgraph.names.resolve_name to avoid dependencies on nodename/remappings
    fake_caller_id = ns_join(get_namespace(), 'node')
    fake_resolved = rosgraph.names.resolve_name(name, fake_caller_id)

    for m, v in _mappings.items():
        if rosgraph.names.resolve_name(m, fake_caller_id) == fake_resolved:
            if is_private(name):
                raise ROSInitException("due to the way this node is written, %s cannot be remapped to a ~name. \nThe declaration of topics/services must be moved after the call to init_node()"%name)
            else:
                return rosgraph.names.resolve_name(v, fake_caller_id)
    return fake_resolved
示例#2
0
def resolve_name_without_node_name(name):
    """
    The need for this function is complicated -- Topics and Services can be created before init_node is called.
    In general, this is okay, unless the name is a ~name, in which
    case we have to raise an ValueError

    @param name: ROS name to resolve
    @type  name: str
    @raise ValueError: if name is a ~name
    @raise ROSInitException: if name is remapped to a ~name
    """
    if is_private(name):
        raise ValueError("~name topics cannot be created before init_node() has been called")

    # we use the underlying rosgraph.names.resolve_name to avoid dependencies on nodename/remappings
    fake_caller_id = ns_join(get_namespace(), 'node')
    fake_resolved = rosgraph.names.resolve_name(name, fake_caller_id)

    for m, v in _mappings.items():
        if rosgraph.names.resolve_name(m, fake_caller_id) == fake_resolved:
            if is_private(name):
                raise ROSInitException("due to the way this node is written, %s cannot be remapped to a ~name. \nThe declaration of topics/services must be moved after the call to init_node()"%name)
            else:
                return rosgraph.names.resolve_name(v, fake_caller_id)
    return fake_resolved
示例#3
0
文件: test_names.py 项目: Aand1/ROSCH
def test_is_private():
    from rosgraph.names import is_private
    try:
        is_private(None)
        assert False, "is_private should raise exception on invalid param"
    except: pass
    tests = ['~name', '~name/sub']
    for t in tests:
        assert is_private(t)
    fails = ['', 'not_private', 'not/private', 'not/~private', '/not/~private']
    for t in fails:
        assert not is_private(t)
示例#4
0
def test_is_private():
    from rosgraph.names import is_private
    try:
        is_private(None)
        assert False, "is_private should raise exception on invalid param"
    except:
        pass
    tests = ['~name', '~name/sub']
    for t in tests:
        assert is_private(t)
    fails = ['', 'not_private', 'not/private', 'not/~private', '/not/~private']
    for t in fails:
        assert not is_private(t)
示例#5
0
def resolve_name(name, caller_id=None):
    """
    Resolve a ROS name to its global, canonical form. Private ~names
    are resolved relative to the node name. 

    @param name: name to resolve.
    @type  name: str
    @param caller_id: node name to resolve relative to. To
    resolve to local namespace, omit this parameter (or use None)
    @type  caller_id: str
    @return: Resolved name. If name is empty/None, resolve_name
    returns parent namespace. If namespace is empty/None,
    @rtype: str
    """
    if not caller_id:
        caller_id = get_name()
    if not name: #empty string resolves to namespace
        return namespace(caller_id)

    name = canonicalize_name(name)
    if name[0] == SEP: #global name
        resolved_name = name
    elif is_private(name): #~name
        resolved_name = ns_join(caller_id, name[1:])
    else: #relative
        resolved_name = namespace(caller_id) + name

    #Mappings override general namespace-based resolution
    # - do this before canonicalization as remappings are meant to
    #   match the name as specified in the code
    if resolved_name in _resolved_mappings:
        return _resolved_mappings[resolved_name]
    else:
        return resolved_name
    def _param_tag(self, tag, context, ros_config, force_local=False, verbose=True):
        """
        @param force_local: if True, param must be added to context instead of ros_config
        @type  force_local: bool
        """
        try:
            self._check_attrs(tag, context, ros_config, XmlLoader.PARAM_ATTRS)

            # compute name and value
            ptype = (tag.getAttribute('type') or 'auto').lower().strip()
            
            vals = self.opt_attrs(tag, context, ('value', 'textfile', 'binfile', 'command'))
            if len([v for v in vals if v is not None]) != 1:
                raise XmlParseException(
                    "<param> tag must have one and only one of value/textfile/binfile.")

            # compute name. if name is a tilde name, it is placed in
            # the context. otherwise it is placed in the ros config.
            name = self.resolve_args(tag.attributes['name'].value.strip(), context)
            value = self.param_value(verbose, name, ptype, *vals)

            if is_private(name) or force_local:
                p = Param(name, value)
                context.add_param(p)
            else:
                p = Param(ns_join(context.ns, name), value)
                ros_config.add_param(Param(ns_join(context.ns, name), value), filename=context.filename, verbose=verbose)
            return p

        except KeyError as e:
            raise XmlParseException(
                "<param> tag is missing required attribute: %s. \n\nParam xml is %s"%(e, tag.toxml()))
        except ValueError as e:
            raise XmlParseException(
                "Invalid <param> tag: %s. \n\nParam xml is %s"%(e, tag.toxml()))
示例#7
0
    def _param_tag(self, tag, context, ros_config, force_local=False, verbose=True):
        """
        @param force_local: if True, param must be added to context instead of ros_config
        @type  force_local: bool
        """
        try:
            self._check_attrs(tag, context, ros_config, XmlLoader.PARAM_ATTRS)

            # compute name and value
            ptype = (tag.getAttribute('type') or 'auto').lower().strip()
            
            vals = self.opt_attrs(tag, context, ('value', 'textfile', 'binfile', 'command'))
            if len([v for v in vals if v is not None]) != 1:
                raise XmlParseException(
                    "<param> tag must have one and only one of value/textfile/binfile.")

            # compute name. if name is a tilde name, it is placed in
            # the context. otherwise it is placed in the ros config.
            name = self.resolve_args(tag.attributes['name'].value.strip(), context)
            value = self.param_value(verbose, name, ptype, *vals)

            if is_private(name) or force_local:
                p = Param(name, value)
                context.add_param(p)
            else:
                p = Param(ns_join(context.ns, name), value)
                ros_config.add_param(Param(ns_join(context.ns, name), value), filename=context.filename, verbose=verbose)
            return p

        except KeyError as e:
            raise XmlParseException(
                "<param> tag is missing required attribute: %s. \n\nParam xml is %s"%(e, tag.toxml()))
        except ValueError as e:
            raise XmlParseException(
                "Invalid <param> tag: %s. \n\nParam xml is %s"%(e, tag.toxml()))
示例#8
0
def resolve_name(name, caller_id=None):
    """
    Resolve a ROS name to its global, canonical form. Private ~names
    are resolved relative to the node name. 

    @param name: name to resolve.
    @type  name: str
    @param caller_id: node name to resolve relative to. To
    resolve to local namespace, omit this parameter (or use None)
    @type  caller_id: str
    @return: Resolved name. If name is empty/None, resolve_name
    returns parent namespace. If namespace is empty/None,
    @rtype: str
    """
    if not caller_id:
        caller_id = get_name()
    if not name: #empty string resolves to namespace
        return namespace(caller_id)

    name = str(name)  # enforce string conversion else struct.pack might raise UnicodeDecodeError (see #3998)
    name = canonicalize_name(name)
    if name[0] == SEP: #global name
        resolved_name = name
    elif is_private(name): #~name
        resolved_name = ns_join(caller_id, name[1:])
    else: #relative
        resolved_name = namespace(caller_id) + name

    #Mappings override general namespace-based resolution
    # - do this before canonicalization as remappings are meant to
    #   match the name as specified in the code
    if resolved_name in _resolved_mappings:
        return _resolved_mappings[resolved_name]
    else:
        return resolved_name
示例#9
0
    def _node_tag(self,
                  tag,
                  context,
                  ros_config,
                  default_machine,
                  is_test=False,
                  verbose=True):
        """
        Process XML <node> or <test> tag
        @param tag: DOM node
        @type  tag: Node
        @param context: namespace context
        @type  context: L{LoaderContext}
        @param params: ROS parameter list
        @type  params: [L{Param}]
        @param clear_params: list of ROS parameter names to clear before setting parameters
        @type  clear_params: [str]
        @param default_machine: default machine to assign to node
        @type  default_machine: str
        @param is_test: if set, will load as L{Test} object instead of L{Node} object
        @type  is_test: bool
        """
        try:
            if is_test:
                self._check_attrs(tag, context, ros_config,
                                  XmlLoader.TEST_ATTRS)
                (name, ) = self.opt_attrs(tag, context, ('name', ))
                test_name, time_limit, retry = self._test_attrs(tag, context)
                if not name:
                    name = test_name
            else:
                self._check_attrs(tag, context, ros_config,
                                  XmlLoader.NODE_ATTRS)
                (name, ) = self.reqd_attrs(tag, context, ('name', ))

            if not is_legal_name(name):
                ros_config.add_config_error(
                    "WARN: illegal <node> name '%s'.\nhttp://ros.org/wiki/Names\nThis will likely cause problems with other ROS tools.\nNode xml is %s"
                    % (name, tag.toxml()))

            child_ns = self._ns_clear_params_attr('node',
                                                  tag,
                                                  context,
                                                  ros_config,
                                                  node_name=name)
            param_ns = child_ns.child(name)
            param_ns.params = [
            ]  # This is necessary because child() does not make a copy of the param list.

            # required attributes
            pkg, node_type = self.reqd_attrs(tag, context, ('pkg', 'type'))

            # optional attributes
            machine, args, output, respawn, respawn_delay, cwd, launch_prefix, \
                    required = self.opt_attrs(tag, context, ('machine', 'args',
                        'output', 'respawn', 'respawn_delay', 'cwd',
                        'launch-prefix', 'required'))
            if tag.hasAttribute('machine') and not len(machine.strip()):
                raise XmlParseException(
                    "<node> 'machine' must be non-empty: [%s]" % machine)
            if not machine and default_machine:
                machine = default_machine.name
            # validate respawn, required
            required, respawn = [_bool_attr(*rr) for rr in ((required, False, 'required'),\
                                                                (respawn, False, 'respawn'))]
            respawn_delay = _float_attr(respawn_delay, 0.0, 'respawn_delay')

            # each node gets its own copy of <remap> arguments, which
            # it inherits from its parent
            remap_context = context.child('')

            # each node gets its own copy of <env> arguments, which
            # it inherits from its parent
            env_context = context.child('')

            # nodes can have individual env args set in addition to
            # the ROS-specific ones.
            for t in [
                    c for c in tag.childNodes
                    if c.nodeType == DomNode.ELEMENT_NODE
            ]:
                tag_name = t.tagName.lower()
                if tag_name == 'remap':
                    r = self._remap_tag(t, context, ros_config)
                    if r is not None:
                        remap_context.add_remap(r)
                elif tag_name == 'param':
                    self._param_tag(t,
                                    param_ns,
                                    ros_config,
                                    force_local=True,
                                    verbose=verbose)
                elif tag_name == 'rosparam':
                    self._rosparam_tag(t,
                                       param_ns,
                                       ros_config,
                                       verbose=verbose)
                elif tag_name == 'env':
                    self._env_tag(t, env_context, ros_config)
                else:
                    ros_config.add_config_error(
                        "WARN: unrecognized '%s' child tag in the parent tag element: %s"
                        % (t.tagName, tag.toxml()))

            # #1036 evaluate all ~params in context
            # TODO: can we get rid of force_local (above), remove this for loop, and just rely on param_tag logic instead?
            for p in itertools.chain(context.params, param_ns.params):
                pkey = p.key
                if is_private(pkey):
                    # strip leading ~, which is optional/inferred
                    pkey = pkey[1:]
                pkey = param_ns.ns + pkey
                ros_config.add_param(Param(pkey, p.value), verbose=verbose)

            if not is_test:
                return Node(pkg,
                            node_type,
                            name=name,
                            namespace=child_ns.ns,
                            machine_name=machine,
                            args=args,
                            respawn=respawn,
                            respawn_delay=respawn_delay,
                            remap_args=remap_context.remap_args(),
                            env_args=env_context.env_args,
                            output=output,
                            cwd=cwd,
                            launch_prefix=launch_prefix,
                            required=required,
                            filename=context.filename)
            else:
                return Test(test_name,
                            pkg,
                            node_type,
                            name=name,
                            namespace=child_ns.ns,
                            machine_name=machine,
                            args=args,
                            remap_args=remap_context.remap_args(),
                            env_args=env_context.env_args,
                            time_limit=time_limit,
                            cwd=cwd,
                            launch_prefix=launch_prefix,
                            retry=retry,
                            filename=context.filename)
        except KeyError as e:
            raise XmlParseException(
                "<%s> tag is missing required attribute: %s. Node xml is %s" %
                (tag.tagName, e, tag.toxml()))
        except XmlParseException as e:
            raise XmlParseException(
                "Invalid <node> tag: %s. \n\nNode xml is %s" %
                (e, tag.toxml()))
        except ValueError as e:
            raise XmlParseException(
                "Invalid <node> tag: %s. \n\nNode xml is %s" %
                (e, tag.toxml()))
    def search_param(self, ns, key):
        """
        Search for matching parameter key for search param
        key. Search for key starts at ns and proceeds upwards to
        the root. As such, search_param should only be called with a
        relative parameter name.

        search_param's behavior is to search for the first partial match.
        For example, imagine that there are two 'robot_description' parameters:

         - /robot_description
         -   /robot_description/arm
         -   /robot_description/base

         - /pr2/robot_description
         -   /pr2/robot_description/base

        If I start in the namespace /pr2/foo and search for
        'robot_description', search_param will match
        /pr2/robot_description. If I search for 'robot_description/arm'
        it will return /pr2/robot_description/arm, even though that
        parameter does not exist (yet).

        @param ns: namespace to begin search from.
        @type  ns: str
        @param key: Parameter key. 
        @type  key: str
        @return: key of matching parameter or None if no matching
        parameter.
        @rtype: str
        """
        if not key or is_private(key):
            raise ValueError("invalid key")
        if not is_global(ns):
            raise ValueError("namespace must be global")            
        if is_global(key):
            if self.has_param(key):
                return key
            else:
                return None

        # there are more efficient implementations, but our hiearchy
        # is not very deep and this is fairly clean code to read.

        # - we only search for the first namespace in the key to check for a match
        key_namespaces = [x for x in key.split(SEP) if x]
        key_ns = key_namespaces[0]

        #  - corner case: have to test initial namespace first as
        #    negative indices won't work with 0
        search_key = ns_join(ns, key_ns)
        if self.has_param(search_key):
            # resolve to full key
            return ns_join(ns, key) 
        
        namespaces = [x for x in ns.split(SEP) if x]
        for i in xrange(1, len(namespaces)+1):
            search_key = SEP + SEP.join(namespaces[0:-i] + [key_ns])
            if self.has_param(search_key):
                # we have a match on the namespace of the key, so
                # compose the full key and return it
                full_key = SEP + SEP.join(namespaces[0:-i] + [key]) 
                return full_key
        return None
    def _node_tag(self, tag, context, ros_config, default_machine, is_test=False, verbose=True):
        """
        Process XML <node> or <test> tag
        @param tag: DOM node
        @type  tag: Node
        @param context: namespace context
        @type  context: L{LoaderContext}
        @param params: ROS parameter list
        @type  params: [L{Param}]
        @param clear_params: list of ROS parameter names to clear before setting parameters
        @type  clear_params: [str]
        @param default_machine: default machine to assign to node
        @type  default_machine: str
        @param is_test: if set, will load as L{Test} object instead of L{Node} object
        @type  is_test: bool
        """
        try:
            if is_test:
                self._check_attrs(tag, context, ros_config, XmlLoader.TEST_ATTRS)
                (name,) = self.opt_attrs(tag, context, ('name',)) 
                test_name, time_limit, retry = self._test_attrs(tag, context)
                if not name:
                    name = test_name
            else:
                self._check_attrs(tag, context, ros_config, XmlLoader.NODE_ATTRS)
                (name,) = self.reqd_attrs(tag, context, ('name',))

            if not is_legal_name(name):
                ros_config.add_config_error("WARN: illegal <node> name '%s'.\nhttp://ros.org/wiki/Names\nThis will likely cause problems with other ROS tools.\nNode xml is %s"%(name, tag.toxml()))
                    
            child_ns = self._ns_clear_params_attr('node', tag, context, ros_config, node_name=name)
            param_ns = child_ns.child(name)
                
            # required attributes
            pkg, node_type = self.reqd_attrs(tag, context, ('pkg', 'type'))
            
            # optional attributes
            machine, args, output, respawn, cwd, launch_prefix, required = \
                     self.opt_attrs(tag, context, ('machine', 'args', 'output', 'respawn', 'cwd', 'launch-prefix', 'required'))
            if tag.hasAttribute('machine') and not len(machine.strip()):
                raise XmlParseException("<node> 'machine' must be non-empty: [%s]"%machine)
            if not machine and default_machine:
                machine = default_machine.name
            # validate respawn, required
            required, respawn = [_bool_attr(*rr) for rr in ((required, False, 'required'),\
                                                                (respawn, False, 'respawn'))]

            # each node gets its own copy of <remap> arguments, which
            # it inherits from its parent
            remap_context = context.child('')

            # nodes can have individual env args set in addition to
            # the ROS-specific ones.  
            for t in [c for c in tag.childNodes if c.nodeType == DomNode.ELEMENT_NODE]:
                tag_name = t.tagName.lower()
                if tag_name == 'remap':
                    r = self._remap_tag(t, context, ros_config)
                    if r is not None:
                        remap_context.add_remap(r)
                elif tag_name == 'param':
                    self._param_tag(t, param_ns, ros_config, force_local=True, verbose=verbose)
                elif tag_name == 'rosparam':
                    self._rosparam_tag(t, param_ns, ros_config, verbose=verbose)
                elif tag_name == 'env':
                    self._env_tag(t, context, ros_config)
                else:
                    ros_config.add_config_error("WARN: unrecognized '%s' tag in <node> tag. Node xml is %s"%(t.tagName, tag.toxml()))

            # #1036 evaluate all ~params in context
            # TODO: can we get rid of force_local (above), remove this for loop, and just rely on param_tag logic instead?
            for p in itertools.chain(context.params, param_ns.params):
                pkey = p.key
                if is_private(pkey):
                    # strip leading ~, which is optional/inferred
                    pkey = pkey[1:]
                pkey = param_ns.ns + pkey
                ros_config.add_param(Param(pkey, p.value), verbose=verbose)
                    
            if not is_test:
                return Node(pkg, node_type, name=name, namespace=child_ns.ns, machine_name=machine, 
                            args=args, respawn=respawn, 
                            remap_args=remap_context.remap_args(), env_args=context.env_args,
                            output=output, cwd=cwd, launch_prefix=launch_prefix,
                            required=required, filename=context.filename)
            else:
                return Test(test_name, pkg, node_type, name=name, namespace=child_ns.ns, 
                            machine_name=machine, args=args,
                            remap_args=remap_context.remap_args(), env_args=context.env_args,
                            time_limit=time_limit, cwd=cwd, launch_prefix=launch_prefix,
                            retry=retry, filename=context.filename)
        except KeyError as e:
            raise XmlParseException(
                "<%s> tag is missing required attribute: %s. Node xml is %s"%(tag.tagName, e, tag.toxml()))
        except XmlParseException as e:
            raise XmlParseException(
                "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml()))
        except ValueError as e:
            raise XmlParseException(
                "Invalid <node> tag: %s. \n\nNode xml is %s"%(e, tag.toxml()))