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