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