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 Library for manipulating ROS Names. See U{http://ros.org/wiki/Names}.
37 """
38
39 import os
40 import sys
41
42 import roslib.exceptions
43 from roslib.rosenv import ROS_NAMESPACE
44
45
46 MSG_EXT = '.msg'
47 SRV_EXT = '.srv'
48
49
50 PRN_SEPARATOR = '/'
51 TYPE_SEPARATOR = PRN_SEPARATOR
52 SEP = '/'
53 GLOBALNS = '/'
54 PRIV_NAME = '~'
55 REMAP = ":="
56 ANYTYPE = '*'
57
59 """
60 @param env: environment dictionary (defaults to os.environ)
61 @type env: dict
62 @param argv: command-line arguments (defaults to sys.argv)
63 @type argv: [str]
64 @return: ROS namespace of current program
65 @rtype: str
66 """
67
68 if argv is None:
69 argv = sys.argv
70 for a in argv:
71 if a.startswith('__ns:='):
72 return make_global_ns(a[len('__ns:='):])
73 if env is None:
74 env = os.environ
75 return make_global_ns(env.get(ROS_NAMESPACE, GLOBALNS))
76
78 """
79 Resolve a local name to the caller ID based on ROS environment settings (i.e. ROS_NAMESPACE)
80
81 @param name: local name to calculate caller ID from, e.g. 'camera', 'node'
82 @type name: str
83 @return: caller ID based on supplied local name
84 @rtype: str
85 """
86 return make_global_ns(ns_join(get_ros_namespace(), name))
87
89 """
90 Convert name to a global name with a trailing namespace separator.
91
92 @param name: ROS resource name. Cannot be a ~name.
93 @type name: str
94 @return str: name as a global name, e.g. 'foo' -> '/foo/'.
95 This does NOT resolve a name.
96 @rtype: str
97 @raise ValueError: if name is a ~name
98 """
99 if is_private(name):
100 raise ValueError("cannot turn [%s] into a global name"%name)
101 if not is_global(name):
102 name = SEP + name
103 if name[-1] != SEP:
104 name = name + SEP
105 return name
106
108 """
109 Test if name is a global graph resource name.
110
111 @param name: must be a legal name in canonical form
112 @type name: str
113 @return: True if name is a globally referenced name (i.e. /ns/name)
114 @rtype: bool
115 """
116 return name and name[0] == SEP
117
119 """
120 Test if name is a private graph resource name.
121
122 @param name: must be a legal name in canonical form
123 @type name: str
124 @return bool: True if name is a privately referenced name (i.e. ~name)
125 """
126 return name and name[0] == PRIV_NAME
127
129 """
130 Get the namespace of name. The namespace is returned with a
131 trailing slash in order to favor easy concatenation and easier use
132 within the global context.
133
134 @param name: name to return the namespace of. Must be a legal
135 name. NOTE: an empty name will return the global namespace.
136 @type name: str
137 @return str: Namespace of name. For example, '/wg/node1' returns '/wg/'. The
138 global namespace is '/'.
139 @rtype: str
140 @raise ValueError: if name is invalid
141 """
142 "map name to its namespace"
143 if name is None:
144 raise ValueError('name')
145 if not isinstance(name, basestring):
146 raise TypeError('name')
147 if not name:
148 return SEP
149 elif name[-1] == SEP:
150 name = name[:-1]
151 return name[:name.rfind(SEP)+1] or SEP
152
154 """
155 Join a namespace and name. If name is unjoinable (i.e. ~private or
156 /global) it will be returned without joining
157
158 @param ns: namespace ('/' and '~' are both legal). If ns is the empty string, name will be returned.
159 @type ns: str
160 @param name str: a legal name
161 @return str: name concatenated to ns, or name if it is
162 unjoinable.
163 @rtype: str
164 """
165 if is_private(name) or is_global(name):
166 return name
167 if ns == PRIV_NAME:
168 return PRIV_NAME + name
169 if not ns:
170 return name
171 if ns[-1] == SEP:
172 return ns + name
173 return ns + SEP + name
174
176 """
177 Load name mappings encoded in command-line arguments. This will filter
178 out any parameter assignment mappings (see roslib.param.load_param_mappings()).
179
180 @param argv: command-line arguments
181 @type argv: [str]
182 @return: name->name remappings.
183 @rtype: dict {str: str}
184 """
185 mappings = {}
186 for arg in argv:
187 if REMAP in arg:
188 try:
189 src, dst = [x.strip() for x in arg.split(REMAP)]
190 if src and dst:
191 if len(src) > 1 and src[0] == '_' and src[1] != '_':
192
193 pass
194 else:
195 mappings[src] = dst
196 except:
197 print >> sys.stderr, "ERROR: Invalid remapping argument '%s'"%arg
198 return mappings
199
200
201
202
203
205 """
206 Convert package name + resource into a fully qualified resource name
207
208 @param res_pkg_name: name of package resource is located in
209 @type res_pkg_name: str
210 @param name: resource base name
211 @type name: str
212 @param my_pkg: name of package resource is being referred to
213 in. If specified, name will be returned in local form if
214 res_pkg_name is my_pkg
215 @type my_pkg: str
216 @return: name for resource
217 @rtype: str
218 """
219 if res_pkg_name != my_pkg:
220 return res_pkg_name+PRN_SEPARATOR+name
221 return name
222
224 """
225 pkg/typeName -> typeName, typeName -> typeName
226
227 Convert fully qualified resource name into the package-less resource name
228 @param name: package resource name, e.g. 'std_msgs/String'
229 @type name: str
230 @return: resource name sans package-name scope
231 @rtype: str
232 """
233
234 return name[name.rfind(PRN_SEPARATOR)+1:]
235
237 """
238 pkg/typeName -> pkg, typeName -> None
239
240 @param name: package resource name, e.g. 'std_msgs/String'
241 @type name: str
242 @return: package name of resource
243 @rtype: str
244 """
245
246 if not PRN_SEPARATOR in name:
247 return None
248 return name[:name.find(PRN_SEPARATOR)]
249
251 """
252 Split a name into its package and resource name parts, e.g. 'std_msgs/String -> std_msgs, String'
253
254 @param name: package resource name, e.g. 'std_msgs/String'
255 @type name: str
256 @return: package name, resource name
257 @rtype: str
258 @raise ValueError: if name is invalid
259 """
260 if PRN_SEPARATOR in name:
261 val = tuple(name.split(PRN_SEPARATOR))
262 if len(val) != 2:
263 raise ValueError("invalid name [%s]"%name)
264 else:
265 return val
266 else:
267 return '', name
268
274
275
276
277
278 import re
279
280 RESOURCE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w_\/]*$')
282 """
283 Check if name is a legal ROS name for filesystem resources
284 (alphabetical character followed by alphanumeric, underscore, or
285 forward slashes). This constraint is currently not being enforced,
286 but may start getting enforced in later versions of ROS.
287
288 @param name: Name
289 @type name: str
290 """
291
292 if name is None or not isinstance(name, basestring):
293 return False
294 m = RESOURCE_NAME_LEGAL_CHARS_P.match(name)
295
296 return m is not None and m.group(0) == name and not '//' in name
297
298
299 NAME_LEGAL_CHARS_P = re.compile('^[\~\/A-Za-z][\w_\/]*$')
301 """
302 Check if name is a legal ROS name for graph resources
303 (alphabetical character followed by alphanumeric, underscore, or
304 forward slashes). This constraint is currently not being enforced,
305 but may start getting enforced in later versions of ROS.
306
307 @param name: Name
308 @type name: str
309 """
310
311 if name is None or not isinstance(name, basestring):
312 return False
313
314 if name == '':
315 return True
316 m = NAME_LEGAL_CHARS_P.match(name)
317 return m is not None and m.group(0) == name and not '//' in name
318
319 BASE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w_]*$')
321 """
322 Validates that name is a legal base name for a graph resource. A base name has
323 no namespace context, e.g. "node_name".
324 """
325 if name is None or not isinstance(name, basestring):
326 return False
327 m = BASE_NAME_LEGAL_CHARS_P.match(name)
328 return m is not None and m.group(0) == name
329
330 BASE_RESOURCE_NAME_LEGAL_CHARS_P = re.compile('^[A-Za-z][\w_]*$')
332 """
333 Validates that name is a legal resource base name. A base name has
334 no package context, e.g. "String".
335 """
336
337 if name is None or not isinstance(name, basestring):
338 return False
339 m = BASE_NAME_LEGAL_CHARS_P.match(name)
340 return m is not None and m.group(0) == name
341
343 """
344 Put name in canonical form. Extra slashes '//' are removed and
345 name is returned without any trailing slash, e.g. /foo/bar
346 @param name: ROS name
347 @type name: str
348 """
349 if not name or name == SEP:
350 return name
351 elif name[0] == SEP:
352 return '/' + '/'.join([x for x in name.split(SEP) if x])
353 else:
354 return '/'.join([x for x in name.split(SEP) if x])
355
357 """
358 Resolve a ROS name to its global, canonical form. Private ~names
359 are resolved relative to the node name.
360
361 @param name: name to resolve.
362 @type name: str
363 @param namespace_: node name to resolve relative to.
364 @type namespace_: str
365 @param remappings: Map of resolved remappings. Use None to indicate no remapping.
366 @return: Resolved name. If name is empty/None, resolve_name
367 returns parent namespace_. If namespace_ is empty/None,
368 @rtype: str
369 """
370 if not name:
371 return namespace(namespace_)
372
373 name = canonicalize_name(name)
374 if name[0] == SEP:
375 resolved_name = name
376 elif is_private(name):
377
378 resolved_name = canonicalize_name(namespace_ + SEP + name[1:])
379 else:
380 resolved_name = namespace(namespace_) + name
381
382
383
384
385 if remappings and resolved_name in remappings:
386 return remappings[resolved_name]
387 else:
388 return resolved_name
389
391 """
392 Generate a ROS-legal 'anonymous' name
393
394 @param id: prefix for anonymous name
395 @type id: str
396 """
397 import socket, random
398 name = "%s_%s_%s_%s"%(id, socket.gethostname(), os.getpid(), random.randint(0, sys.maxint))
399
400
401
402 name = name.replace('.', '_')
403 name = name.replace('-', '_')
404 return name.replace(':', '_')
405