1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 """
36 Support for ROS Names
37
38 See: U{http://www.ros.org/wiki/Names}
39 """
40
41 import sys
42 import os
43 from itertools import ifilter
44
45 from roslib.names import namespace, get_ros_namespace, ns_join, make_global_ns, load_mappings, \
46 SEP, GLOBALNS, TYPE_SEPARATOR, REMAP, ANYTYPE, \
47 is_global, is_private
48 import roslib.names
49
50 from rospy.exceptions import ROSException
51 from rospy.impl.validators import ParameterInvalid
52
53 TOPIC_ANYTYPE = ANYTYPE
54 SERVICE_ANYTYPE = ANYTYPE
55
57 """
58 Put name in canonical form. Double slashes '//' are removed and
59 name is returned without any trailing slash, e.g. /foo/bar
60 @param name: ROS name
61 @type name: str
62 """
63 if not name or name == SEP:
64 return name
65 elif name[0] == SEP:
66 return '/' + '/'.join([x for x in name.split(SEP) if x])
67 else:
68 return '/'.join([x for x in name.split(SEP) if x])
69
70
71
72
73
74
75
76
77
78 _mappings = load_mappings(sys.argv)
79 _resolved_mappings = {}
80
82 """
83 Re-initialize the name remapping table.
84
85 @param argv: Command line arguments to this program. ROS reads
86 these arguments to find renaming params.
87 @type argv: [str]
88 """
89 global _mappings
90 _mappings = load_mappings(argv)
91
92
109
111 """
112 The need for this function is complicated -- Topics and Services can be created before init_node is called.
113 In general, this is okay, unless the name is a ~name, in which
114 case we have to raise an ValueError
115
116 @param name: ROS name to resolve
117 @type name: str
118 @raise ValueError: if name is a ~name
119 @raise ROSInitException: if name is remapped to a ~name
120 """
121 if is_private(name):
122 raise ValueError("~name topics cannot be created before init_node() has been called")
123
124
125 fake_caller_id = ns_join(get_namespace(), 'node')
126 fake_resolved = roslib.names.resolve_name(name, fake_caller_id)
127
128 for m, v in _mappings.iteritems():
129 if roslib.names.resolve_name(m, fake_caller_id) == fake_resolved:
130 if is_private(name):
131 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)
132 else:
133 return roslib.names.resolve_name(v, fake_caller_id)
134 return fake_resolved
135
137 """
138 Get mapping table with unresolved names
139
140 @return: command-line remappings {name: name}
141 @rtype: {str: str}
142 """
143 return _mappings
144
146 """
147 Get mapping table with resolved names
148
149 @return: command-line remappings {name: name}
150 @rtype: {str: str}
151 """
152 return _resolved_mappings
153
154
156 """
157 Resolve a ROS name to its global, canonical form. Private ~names
158 are resolved relative to the node name.
159
160 @param name: name to resolve.
161 @type name: str
162 @param caller_id: node name to resolve relative to. To
163 resolve to local namespace, omit this parameter (or use None)
164 @type caller_id: str
165 @return: Resolved name. If name is empty/None, resolve_name
166 returns parent namespace. If namespace is empty/None,
167 @rtype: str
168 """
169 if not caller_id:
170 caller_id = get_name()
171 if not name:
172 return namespace(caller_id)
173
174 name = canonicalize_name(name)
175 if name[0] == SEP:
176 resolved_name = name
177 elif is_private(name):
178 resolved_name = ns_join(caller_id, name[1:])
179 else:
180 resolved_name = namespace(caller_id) + name
181
182
183
184
185 if resolved_name in _resolved_mappings:
186 return _resolved_mappings[resolved_name]
187 else:
188 return resolved_name
189
190
191 -def remap_name(name, caller_id=None, resolved=True):
192 """
193 Remap a ROS name. This API should be used to instead of
194 resolve_name for APIs in which you don't wish to resolve the name
195 unless it is remapped.
196 @param name: name to remap
197 @type name: str
198
199 @param resolved: if True (default), use resolved names in remappings, which is the standard for ROS.
200 @type resolved: bool
201
202 @return: Remapped name
203 @rtype: str
204 """
205 if not caller_id:
206 caller_id = get_caller_id()
207 if name in _mappings:
208 return roslib.names.resolve_name(_mappings[name], caller_id)
209 return name
210
212 """
213 Convert the global caller_id to a relative name within the namespace. For example, for
214 namespace '/foo' and name '/foo/bar/name', the return value will
215 be 'bar/name'
216
217 WARNING: scoped_name does not validate that name is actually within
218 the supplied namespace.
219 @param caller_id: caller ID, in canonical form
220 @type caller_id: str
221 @param name: name to scope
222 @type name: str
223 @return: name scoped to the caller_id's namespace.
224 @rtype: str
225 """
226 if not is_global(caller_id):
227 raise ROSException("caller_id must be global")
228 return canonicalize_name(name)[len(namespace(caller_id)):]
229
230
231
232
233
234
235
236
238 if not param_value or not isinstance(param_value, basestring):
239 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name)
240
241
242 if ':' in param_value or ' ' in param_value:
243 raise ParameterInvalid("ERROR: parameter [%s] contains illegal chars"%param_name)
244
245 return roslib.names.resolve_name(param_value, caller_id, remappings=None)
247 if not param_value or not isinstance(param_value, basestring):
248 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name)
249
250
251 if ':' in param_value or ' ' in param_value:
252 raise ParameterInvalid("ERROR: parameter [%s] contains illegal chars"%param_name)
253 return param_value
254
256 """
257 Validator that resolves names and also ensures that they are not empty
258 @param param_name: name
259 @type param_name: str
260 @param resolve: if True/omitted, the name will be resolved to
261 a global form. Otherwise, no resolution occurs.
262 @type resolve: bool
263 @return: resolved parameter value
264 @rtype: str
265 """
266 def validator(param_value, caller_id):
267 if resolve:
268 return valid_name_validator_resolved(param_name, param_value, caller_id)
269 return valid_name_validator_unresolved(param_name, param_value, caller_id)
270 return validator
271
273 """
274 Validator that checks for valid, global graph resource name.
275 @return: parameter value
276 @rtype: str
277 """
278 def validator(param_value, caller_id):
279 if not param_value or not isinstance(param_value, basestring):
280 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name)
281
282 if not is_global(param_value):
283 raise ParameterInvalid("ERROR: parameter [%s] must be a globally referenced name"%param_name)
284 return param_value
285 return validator
286
287
288
289
290
291 _caller_namespace = get_ros_namespace()
292 _caller_id = _caller_namespace+'unnamed'
293
295 """
296 Get namespace of local node.
297 @return: fully-qualified name of local node or '' if not applicable
298 @rtype: str
299 """
300 return _caller_namespace
301
303 """
304 Get fully resolved name of local node. If this is not a node,
305 use empty string
306 @return: fully-qualified name of local node or '' if not applicable
307 @rtype: str
308 """
309 return _caller_id
310
311
312 get_caller_id = get_name
313
315 """
316 Internal API.
317 Set the global name (i.e. caller_id) and namespace. Methods can
318 check what the name of the current node is by calling get_caller_id.
319
320 The caller_id is important as it is the first parameter to any API
321 call on a remote node. Invoked by ROSNode constructor
322 @param caller_id: new caller ID
323 @type caller_id: str
324 """
325 global _caller_id, _caller_namespace
326 _caller_id = caller_id
327 _caller_namespace = namespace(caller_id)
328