| Trees | Indices | Help |
|
|---|
|
|
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Revision $Id: core.py 13839 2011-06-01 01:22:34Z kwc $
34
35 """rospy internal core implementation library"""
36
37 from __future__ import with_statement
38
39 import atexit
40 import logging
41 import os
42 import signal
43 import sys
44 import threading
45 import time
46 import traceback
47 import types
48 import urlparse
49 import xmlrpclib
50
51 import roslib.rosenv
52 import roslib.roslogging
53
54 import rospy.exceptions
55
56 from rospy.names import *
57 from rospy.impl.validators import ParameterInvalid
58
59 from rosgraph_msgs.msg import Log
60
61 _logger = logging.getLogger("rospy.core")
62
63 # number of seconds to wait to join on threads. network issue can
64 # cause joins to be not terminate gracefully, and it's better to
65 # teardown dirty than to hang
66 _TIMEOUT_SHUTDOWN_JOIN = 5.
67
68 import warnings
70 """This is a decorator which can be used to mark functions
71 as deprecated. It will result in a warning being emmitted
72 when the function is used."""
73 def newFunc(*args, **kwargs):
74 warnings.warn("Call to deprecated function %s." % func.__name__,
75 category=DeprecationWarning, stacklevel=2)
76 return func(*args, **kwargs)
77 newFunc.__name__ = func.__name__
78 newFunc.__doc__ = func.__doc__
79 newFunc.__dict__.update(func.__dict__)
80 return newFunc
81
82 #########################################################
83 # ROSRPC
84
85 ROSRPC = "rosrpc://"
86
88 """
89 utility function for parsing ROS-RPC URIs
90 @param uri: ROSRPC URI
91 @type uri: str
92 @return: address, port
93 @rtype: (str, int)
94 @raise ParameterInvalid: if uri is not a valid ROSRPC URI
95 """
96 if uri.startswith(ROSRPC):
97 dest_addr = uri[len(ROSRPC):]
98 else:
99 raise ParameterInvalid("Invalid protocol for ROS service URL: %s"%uri)
100 try:
101 if '/' in dest_addr:
102 dest_addr = dest_addr[:dest_addr.find('/')]
103 dest_addr, dest_port = dest_addr.split(':')
104 dest_port = int(dest_port)
105 except:
106 raise ParameterInvalid("ROS service URL is invalid: %s"%uri)
107 return dest_addr, dest_port
108
109 #########################################################
110
111 # rospy logger
112 _rospy_logger = logging.getLogger("rospy.internal")
113
114 _logdebug_handlers = []
115 _loginfo_handlers = []
116 _logwarn_handlers = []
117 _logerr_handlers = []
118 _logfatal_handlers = []
119
120 # we keep a separate, non-rosout log file to contain stack traces and
121 # other sorts of information that scare users but are essential for
122 # debugging
123
133
135 """
136 Add handler for specified level
137 @param level: log level (use constants from Log)
138 @type level: int
139 @param h: log message handler
140 @type h: fn
141 @raise ROSInternalException: if level is invalid
142 """
143 if level == Log.DEBUG:
144 _logdebug_handlers.append(h)
145 elif level == Log.INFO:
146 _loginfo_handlers.append(h)
147 elif level == Log.WARN:
148 _logwarn_handlers.append(h)
149 elif level == Log.ERROR:
150 _logerr_handlers.append(h)
151 elif level == Log.FATAL:
152 _logfatal_handlers.append(h)
153 else:
154 raise rospy.exceptions.ROSInternalException("invalid log level: %s"%level)
155
157 """
158 Log a debug message to the /rosout topic
159 @param msg: message to log, may include formatting arguments
160 @type msg: str
161 @param args: format-string arguments, if necessary
162 """
163 if args:
164 msg = msg%args
165 for h in _logdebug_handlers:
166 h(msg)
167
169 """
170 Log a warning message to the /rosout topic
171 @param msg: message to log, may include formatting arguments
172 @type msg: str
173 @param args: format-string arguments, if necessary
174 """
175 if args:
176 msg = msg%args
177 for h in _logwarn_handlers:
178 h(msg)
179
181 """
182 Log an info message to the /rosout topic
183 @param msg: message to log, may include formatting arguments
184 @type msg: str
185 @param args: format-string arguments, if necessary
186 """
187 if args:
188 msg = msg%args
189 for h in _loginfo_handlers:
190 h(msg)
191 logout = loginfo # alias deprecated name
192
194 """
195 Log an error message to the /rosout topic
196 @param msg: message to log, may include formatting arguments
197 @type msg: str
198 @param args: format-string arguments, if necessary
199 """
200 if args:
201 msg = msg%args
202 for h in _logerr_handlers:
203 h(msg)
204 logerror = logerr # alias logerr
205
207 """
208 Log an error message to the /rosout topic
209 @param msg: message to log, may include formatting arguments
210 @type msg: str
211 @param args: format-string arguments, if necessary
212 """
213 if args:
214 msg = msg%args
215 for h in _logfatal_handlers:
216 h(msg)
217
218 #########################################################
219 # CONSTANTS
220
221 MASTER_NAME = "master" #master is a reserved node name for the central master
222
224 """
225 Get the value of ROS_ROOT.
226 @param env: override environment dictionary
227 @type env: dict
228 @param required: if True, fails with ROSException
229 @return: Value of ROS_ROOT environment
230 @rtype: str
231 @raise ROSException: if require is True and ROS_ROOT is not set
232 """
233 if env is None:
234 env = os.environ
235 ros_root = env.get(roslib.rosenv.ROS_ROOT, None)
236 if required and not ros_root:
237 raise rospy.exceptions.ROSException('%s is not set'%roslib.rosenv.ROS_ROOT)
238 return ros_root
239
240
241 #########################################################
242 # API
243
244 _uri = None
246 """
247 Get this Node's URI.
248 @return: this Node's XMLRPC URI
249 @rtype: str
250 """
251 return _uri
252
254 """set the URI of the local node.
255 This is an internal API method, it does not actually affect the XMLRPC URI of the Node."""
256 global _uri
257 _uri = uri
258
259 #########################################################
260 # Logging
261
262 _log_filename = None
264 """
265 Setup filesystem logging for this node
266 @param node_name: Node's name
267 @type node_name str
268 @param level: (optional) Python logging level (INFO, DEBUG, etc...). (Default: logging.INFO)
269 @type level: int
270 """
271 global _log_filename
272
273 # #988 __log command-line remapping argument
274 mappings = get_mappings()
275 if '__log' in get_mappings():
276 logfilename_remap = mappings['__log']
277 filename = os.path.abspath(logfilename_remap)
278 else:
279 # fix filesystem-unsafe chars
280 filename = node_name.replace('/', '_') + '.log'
281 if filename[0] == '_':
282 filename = filename[1:]
283 if not filename:
284 raise rospy.exceptions.ROSException('invalid configure_logging parameter: %s'%node_name)
285 _log_filename = roslib.roslogging.configure_logging('rospy', level, filename=filename)
286
290
291 # keep logging happy until we have the node name to configure with
292 logging.getLogger('rospy').addHandler(NullHandler())
293
294
295 #########################################################
296 # Init/Shutdown/Exit API and Handlers
297
298 _client_ready = False
299
300
302 """
303 Get the initialization state of the local node. If True, node has
304 been configured.
305 @return: True if local node initialized
306 @rtype: bool
307 """
308 return _client_ready
310 """
311 set the initialization state of the local node
312 @param initialized: True if node initialized
313 @type initialized: bool
314 """
315 global _client_ready
316 _client_ready = initialized
317
318 _shutdown_lock = threading.RLock()
319
320 # _shutdown_flag flags that rospy is in shutdown mode, in_shutdown
321 # flags that the shutdown routine has started. These are separate
322 # because 'pre-shutdown' hooks require rospy to be in a non-shutdown
323 # mode. These hooks are executed during the shutdown routine.
324 _shutdown_flag = False
325 _in_shutdown = False
326
327 # various hooks to call on shutdown. shutdown hooks are called in the
328 # shutdown state, preshutdown are called just before entering shutdown
329 # state, and client shutdown is called before both of these.
330 _shutdown_hooks = []
331 _preshutdown_hooks = []
332 _client_shutdown_hooks = []
333 # threads that must be joined on shutdown
334 _shutdown_threads = []
335
336 _signalChain = {}
337
339 """
340 @return: True if shutdown flag has been set
341 @rtype: bool
342 """
343 return _shutdown_flag
344
346 """
347 is_shutdown_requested is a state that occurs just before
348 is_shutdown. It is initiated when a shutdown requested is
349 received and continues until client shutdown handlers have been
350 called. After client shutdown handlers have been serviced, the
351 is_shutdown state becomes true.
352
353 @return: True if shutdown has been requested (but possibly not yet initiated)
354 @rtype: bool
355 """
356 return _in_shutdown
357
359 """
360 shared implementation of add_shutdown_hook and add_preshutdown_hook
361 """
362 if type(h) not in [types.FunctionType, types.MethodType]:
363 raise TypeError("shutdown hook [%s] must be a function: %s"%(h, type(h)))
364 if _shutdown_flag:
365 _logger.warn("add_shutdown_hook called after shutdown")
366 h("already shutdown")
367 return
368 with _shutdown_lock:
369 if hooks is None:
370 # race condition check, don't log as we are deep into shutdown
371 return
372 hooks.append(h)
373
375 """
376 Register thread that must be joined() on shutdown
377 """
378 if _shutdown_flag:
379 #TODO
380 return
381 with _shutdown_lock:
382 if _shutdown_threads is None:
383 # race condition check, don't log as we are deep into shutdown
384 return
385 # in order to prevent memory leaks, reap dead threads. The
386 # last thread may not get reaped until shutdown, but this is
387 # relatively minor
388 for other in _shutdown_threads[:]:
389 if not other.isAlive():
390 _shutdown_threads.remove(other)
391 _shutdown_threads.append(t)
392
394 """
395 Add client method to invoke when system shuts down. Unlike
396 L{add_shutdown_hook} and L{add_preshutdown_hooks}, these methods
397 will be called before any rospy internal shutdown code.
398
399 @param h: function that takes in a single string argument (shutdown reason)
400 @type h: fn(str)
401 """
402 _add_shutdown_hook(h, _client_shutdown_hooks)
403
405 """
406 Add method to invoke when system shuts down. Unlike
407 L{add_shutdown_hook}, these methods will be called before any
408 other shutdown hooks.
409
410 @param h: function that takes in a single string argument (shutdown reason)
411 @type h: fn(str)
412 """
413 _add_shutdown_hook(h, _preshutdown_hooks)
414
416 """
417 Add method to invoke when system shuts down.
418
419 Shutdown hooks are called in the order that they are
420 registered. This is an internal API method that is used to
421 cleanup. See the client X{on_shutdown()} method if you wish to
422 register client hooks.
423
424 @param h: function that takes in a single string argument (shutdown reason)
425 @type h: fn(str)
426 """
427 _add_shutdown_hook(h, _shutdown_hooks)
428
430 """
431 Initiates shutdown process by signaling objects waiting on _shutdown_lock.
432 Shutdown and pre-shutdown hooks are invoked.
433 @param reason: human-readable shutdown reason, if applicable
434 @type reason: str
435 """
436 global _shutdown_flag, _in_shutdown, _shutdown_lock, _shutdown_hooks
437 _logger.info("signal_shutdown [%s]"%reason)
438 if _shutdown_flag or _in_shutdown:
439 return
440 with _shutdown_lock:
441 if _shutdown_flag or _in_shutdown:
442 return
443 _in_shutdown = True
444
445 # make copy just in case client re-invokes shutdown
446 for h in _client_shutdown_hooks:
447 try:
448 # client shutdown hooks do not accept a reason arg
449 h()
450 except:
451 traceback.print_exc()
452 del _client_shutdown_hooks[:]
453
454 for h in _preshutdown_hooks:
455 try:
456 h(reason)
457 except:
458 traceback.print_exc()
459 del _preshutdown_hooks[:]
460
461 # now that pre-shutdown hooks have been called, raise shutdown
462 # flag. This allows preshutdown hooks to still publish and use
463 # service calls properly
464 _shutdown_flag = True
465 for h in _shutdown_hooks:
466 try:
467 h(reason)
468 except Exception, e:
469 print >> sys.stderr, "signal_shutdown hook error[%s]"%e
470 del _shutdown_hooks[:]
471
472 threads = _shutdown_threads[:]
473
474 for t in threads:
475 if t.isAlive():
476 t.join(_TIMEOUT_SHUTDOWN_JOIN)
477 del _shutdown_threads[:]
478 try:
479 time.sleep(0.1) #hack for now until we get rid of all the extra threads
480 except KeyboardInterrupt: pass
481
483 signal_shutdown("signal-"+str(sig))
484 prev_handler = _signalChain.get(sig, None)
485 if prev_handler is not None and not type(prev_handler) == int:
486 try:
487 prev_handler(sig, stackframe)
488 except KeyboardInterrupt:
489 pass #filter out generic keyboard interrupt handler
490
492 signal_shutdown('atexit')
493 atexit.register(_ros_atexit)
494
495 # #687
497 """
498 register system signal handlers for SIGTERM and SIGINT
499 """
500 _signalChain[signal.SIGTERM] = signal.signal(signal.SIGTERM, _ros_signal)
501 _signalChain[signal.SIGINT] = signal.signal(signal.SIGINT, _ros_signal)
502
503 # Validators ######################################
504
506 """
507 Validator that checks that parameter is a valid ROS topic name
508 """
509 def validator(param_value, caller_id):
510 v = valid_name_validator_resolved(param_name, param_value, caller_id)
511 if param_value == '/':
512 raise ParameterInvalid("ERROR: parameter [%s] cannot be the global namespace"%param_name)
513 return v
514 return validator
515
517 """
518 @return: instance for calling remote server or None if not a valid URI
519 @rtype: xmlrpclib.ServerProxy
520 """
521 if uri is None:
522 return None
523 uriValidate = urlparse.urlparse(uri)
524 if not uriValidate[0] or not uriValidate[1]:
525 return None
526 return xmlrpclib.ServerProxy(uri)
527
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Jan 11 10:11:53 2013 | http://epydoc.sourceforge.net |