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
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
56 import struct
57
58 if sys.hexversion > 0x03000000:
60 return isinstance(s, str)
61 else:
63 return isinstance(s, basestring)
64
66 """
67 Put name in canonical form. Double slashes '//' are removed and
68 name is returned without any trailing slash, e.g. /foo/bar
69 @param name: ROS name
70 @type name: str
71 """
72 if not name or name == SEP:
73 return name
74 elif name[0] == SEP:
75 return '/' + '/'.join([x for x in name.split(SEP) if x])
76 else:
77 return '/'.join([x for x in name.split(SEP) if x])
78
79
80
81
82
83
84
85
86
87 _mappings = load_mappings(sys.argv)
88 _resolved_mappings = {}
89
91 """
92 Re-initialize the name remapping table.
93
94 @param argv: Command line arguments to this program. ROS reads
95 these arguments to find renaming params.
96 @type argv: [str]
97 """
98 global _mappings
99 _mappings = load_mappings(argv)
100
101
118
120 """
121 The need for this function is complicated -- Topics and Services can be created before init_node is called.
122 In general, this is okay, unless the name is a ~name, in which
123 case we have to raise an ValueError
124
125 @param name: ROS name to resolve
126 @type name: str
127 @raise ValueError: if name is a ~name
128 @raise ROSInitException: if name is remapped to a ~name
129 """
130 if is_private(name):
131 raise ValueError("~name topics cannot be created before init_node() has been called")
132
133
134 fake_caller_id = ns_join(get_namespace(), 'node')
135 fake_resolved = roslib.names.resolve_name(name, fake_caller_id)
136
137 for m, v in _mappings.items():
138 if roslib.names.resolve_name(m, fake_caller_id) == fake_resolved:
139 if is_private(name):
140 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)
141 else:
142 return roslib.names.resolve_name(v, fake_caller_id)
143 return fake_resolved
144
146 """
147 Get mapping table with unresolved names
148
149 @return: command-line remappings {name: name}
150 @rtype: {str: str}
151 """
152 return _mappings
153
155 """
156 Get mapping table with resolved names
157
158 @return: command-line remappings {name: name}
159 @rtype: {str: str}
160 """
161 return _resolved_mappings
162
163
165 """
166 Resolve a ROS name to its global, canonical form. Private ~names
167 are resolved relative to the node name.
168
169 @param name: name to resolve.
170 @type name: str
171 @param caller_id: node name to resolve relative to. To
172 resolve to local namespace, omit this parameter (or use None)
173 @type caller_id: str
174 @return: Resolved name. If name is empty/None, resolve_name
175 returns parent namespace. If namespace is empty/None,
176 @rtype: str
177 """
178 if not caller_id:
179 caller_id = get_name()
180 if not name:
181 return namespace(caller_id)
182
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 roslib.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 roslib.names.resolve_name(param_value, caller_id, remappings=None)
256 if not param_value or not isstring(param_value):
257 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name)
258
259
260 if ':' in param_value or ' ' in param_value:
261 raise ParameterInvalid("ERROR: parameter [%s] contains illegal chars"%param_name)
262 return param_value
263
265 """
266 Validator that resolves names and also ensures that they are not empty
267 @param param_name: name
268 @type param_name: str
269 @param resolve: if True/omitted, the name will be resolved to
270 a global form. Otherwise, no resolution occurs.
271 @type resolve: bool
272 @return: resolved parameter value
273 @rtype: str
274 """
275 def validator(param_value, caller_id):
276 if resolve:
277 return valid_name_validator_resolved(param_name, param_value, caller_id)
278 return valid_name_validator_unresolved(param_name, param_value, caller_id)
279 return validator
280
282 """
283 Validator that checks for valid, global graph resource name.
284 @return: parameter value
285 @rtype: str
286 """
287 def validator(param_value, caller_id):
288 if not param_value or not isstring(param_value):
289 raise ParameterInvalid("ERROR: parameter [%s] must be a non-empty string"%param_name)
290
291 if not is_global(param_value):
292 raise ParameterInvalid("ERROR: parameter [%s] must be a globally referenced name"%param_name)
293 return param_value
294 return validator
295
296
297
298
299
300 _caller_namespace = get_ros_namespace()
301 _caller_id = _caller_namespace+'unnamed'
302
304 """
305 Get namespace of local node.
306 @return: fully-qualified name of local node or '' if not applicable
307 @rtype: str
308 """
309 return _caller_namespace
310
312 """
313 Get fully resolved name of local node. If this is not a node,
314 use empty string
315 @return: fully-qualified name of local node or '' if not applicable
316 @rtype: str
317 """
318 return _caller_id
319
320
321 get_caller_id = get_name
322
324 """
325 Internal API.
326 Set the global name (i.e. caller_id) and namespace. Methods can
327 check what the name of the current node is by calling get_caller_id.
328
329 The caller_id is important as it is the first parameter to any API
330 call on a remote node. Invoked by ROSNode constructor
331 @param caller_id: new caller ID
332 @type caller_id: str
333 """
334 global _caller_id, _caller_namespace
335 _caller_id = caller_id
336 _caller_namespace = namespace(caller_id)
337