| Home | 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$
34
35 """rospy internal core implementation library"""
36
37
38
39 import atexit
40 try:
41 import cPickle as pickle
42 except ImportError:
43 import pickle
44 import inspect
45 import logging
46 import os
47 import signal
48 import sys
49 import threading
50 import time
51 import traceback
52 import types
53
54 try:
55 import urllib.parse as urlparse #Python 3.x
56 except ImportError:
57 import urlparse
58
59 try:
60 import xmlrpc.client as xmlrpcclient #Python 3.x
61 except ImportError:
62 import xmlrpclib as xmlrpcclient #Python 2.x
63
64 import rospkg
65
66 import rosgraph.roslogging
67
68 import rospy.exceptions
69 import rospy.rostime
70
71 from rospy.names import *
72 from rospy.impl.validators import ParameterInvalid
73
74 from rosgraph_msgs.msg import Log
75
76 _logger = logging.getLogger("rospy.core")
77
78 # number of seconds to wait to join on threads. network issue can
79 # cause joins to be not terminate gracefully, and it's better to
80 # teardown dirty than to hang
81 _TIMEOUT_SHUTDOWN_JOIN = 5.
82
83 import warnings
85 """This is a decorator which can be used to mark functions
86 as deprecated. It will result in a warning being emmitted
87 when the function is used."""
88 def newFunc(*args, **kwargs):
89 warnings.warn("Call to deprecated function %s." % func.__name__,
90 category=DeprecationWarning, stacklevel=2)
91 return func(*args, **kwargs)
92 newFunc.__name__ = func.__name__
93 newFunc.__doc__ = func.__doc__
94 newFunc.__dict__.update(func.__dict__)
95 return newFunc
96
97 #########################################################
98 # ROSRPC
99
100 ROSRPC = "rosrpc://"
103 """
104 utility function for parsing ROS-RPC URIs
105 @param uri: ROSRPC URI
106 @type uri: str
107 @return: address, port
108 @rtype: (str, int)
109 @raise ParameterInvalid: if uri is not a valid ROSRPC URI
110 """
111 if uri.startswith(ROSRPC):
112 dest_addr = uri[len(ROSRPC):]
113 else:
114 raise ParameterInvalid("Invalid protocol for ROS service URL: %s"%uri)
115 try:
116 if '/' in dest_addr:
117 dest_addr = dest_addr[:dest_addr.find('/')]
118 dest_addr, dest_port = dest_addr.split(':')
119 dest_port = int(dest_port)
120 except:
121 raise ParameterInvalid("ROS service URL is invalid: %s"%uri)
122 return dest_addr, dest_port
123
124 #########################################################
125
126 # rospy logger
127 _rospy_logger = logging.getLogger("rospy.internal")
128
129 # we keep a separate, non-rosout log file to contain stack traces and
130 # other sorts of information that scare users but are essential for
131 # debugging
132
133 -def rospydebug(msg, *args):
145
146 logdebug = logging.getLogger('rosout').debug
147
148 logwarn = logging.getLogger('rosout').warning
149
150 loginfo = logging.getLogger('rosout').info
151 logout = loginfo # alias deprecated name
152
153 logerr = logging.getLogger('rosout').error
154 logerror = logerr # alias logerr
155
156 logfatal = logging.getLogger('rosout').critical
160
161 last_logging_time_table = {}
162
164 """Do logging specified message periodically.
165
166 - caller_id (str): Id to identify the caller
167 - logging_func (function): Function to do logging.
168 - period (float): Period to do logging in second unit.
169 - msg (object): Message to do logging.
170 """
171 now = rospy.Time.now()
172
173 last_logging_time = self.last_logging_time_table.get(caller_id)
174
175 if (last_logging_time is None or
176 (now - last_logging_time) > rospy.Duration(period)):
177 logging_func(msg)
178 self.last_logging_time_table[caller_id] = now
179 elif last_logging_time > now:
180 logging_func(msg)
181 self.last_logging_time_table = {}
182 self.last_logging_time_table[caller_id] = now
183
184
185 _logging_throttle = LoggingThrottle()
189 caller_id = (
190 inspect.getabsfile(frame),
191 frame.f_lineno,
192 frame.f_lasti,
193 )
194 return pickle.dumps(caller_id)
195
198 caller_id = _frame_to_caller_id(inspect.currentframe().f_back)
199 _logging_throttle(caller_id, logdebug, period, msg)
200
203 caller_id = _frame_to_caller_id(inspect.currentframe().f_back)
204 _logging_throttle(caller_id, loginfo, period, msg)
205
208 caller_id = _frame_to_caller_id(inspect.currentframe().f_back)
209 _logging_throttle(caller_id, logwarn, period, msg)
210
213 caller_id = _frame_to_caller_id(inspect.currentframe().f_back)
214 _logging_throttle(caller_id, logerr, period, msg)
215
218 caller_id = _frame_to_caller_id(inspect.currentframe().f_back)
219 _logging_throttle(caller_id, logfatal, period, msg)
220
221
222 #########################################################
223 # CONSTANTS
224
225 MASTER_NAME = "master" #master is a reserved node name for the central master
226
227 import warnings
228 import functools
230 """This is a decorator which can be used to mark functions
231 as deprecated. It will result in a warning being emmitted
232 when the function is used."""
233 @functools.wraps(func)
234 def newFunc(*args, **kwargs):
235 warnings.warn("Call to deprecated function %s." % func.__name__,
236 category=DeprecationWarning, stacklevel=2)
237 return func(*args, **kwargs)
238 return newFunc
239
242 """
243 Get the value of ROS_ROOT.
244 @param env: override environment dictionary
245 @type env: dict
246 @param required: if True, fails with ROSException
247 @return: Value of ROS_ROOT environment
248 @rtype: str
249 @raise ROSException: if require is True and ROS_ROOT is not set
250 """
251 if env is None:
252 env = os.environ
253 ros_root = rospkg.get_ros_root(env)
254 if required and not ros_root:
255 raise rospy.exceptions.ROSException('%s is not set'%rospkg.environment.ROS_ROOT)
256 return ros_root
257
258
259 #########################################################
260 # API
261
262 _uri = None
264 """
265 Get this Node's URI.
266 @return: this Node's XMLRPC URI
267 @rtype: str
268 """
269 return _uri
270
272 """set the URI of the local node.
273 This is an internal API method, it does not actually affect the XMLRPC URI of the Node."""
274 global _uri
275 _uri = uri
276
277 #########################################################
278 # Logging
279
280 _log_filename = None
282 """
283 Setup filesystem logging for this node
284 @param node_name: Node's name
285 @type node_name str
286 @param level: (optional) Python logging level (INFO, DEBUG, etc...). (Default: logging.INFO)
287 @type level: int
288 """
289 global _log_filename
290
291 # #988 __log command-line remapping argument
292 mappings = get_mappings()
293 if '__log' in get_mappings():
294 logfilename_remap = mappings['__log']
295 filename = os.path.abspath(logfilename_remap)
296 else:
297 # fix filesystem-unsafe chars
298 filename = node_name.replace('/', '_') + '.log'
299 if filename[0] == '_':
300 filename = filename[1:]
301 if not filename:
302 raise rospy.exceptions.ROSException('invalid configure_logging parameter: %s'%node_name)
303 _log_filename = rosgraph.roslogging.configure_logging('rospy', level, filename=filename)
304
308
309 # keep logging happy until we have the node name to configure with
310 logging.getLogger('rospy').addHandler(NullHandler())
311
312
313 #########################################################
314 # Init/Shutdown/Exit API and Handlers
315
316 _client_ready = False
320 """
321 Get the initialization state of the local node. If True, node has
322 been configured.
323 @return: True if local node initialized
324 @rtype: bool
325 """
326 return _client_ready
328 """
329 set the initialization state of the local node
330 @param initialized: True if node initialized
331 @type initialized: bool
332 """
333 global _client_ready
334 _client_ready = initialized
335
336 _shutdown_lock = threading.RLock()
337
338 # _shutdown_flag flags that rospy is in shutdown mode, in_shutdown
339 # flags that the shutdown routine has started. These are separate
340 # because 'pre-shutdown' hooks require rospy to be in a non-shutdown
341 # mode. These hooks are executed during the shutdown routine.
342 _shutdown_flag = False
343 _in_shutdown = False
344
345 # various hooks to call on shutdown. shutdown hooks are called in the
346 # shutdown state, preshutdown are called just before entering shutdown
347 # state, and client shutdown is called before both of these.
348 _shutdown_hooks = []
349 _preshutdown_hooks = []
350 _client_shutdown_hooks = []
351 # threads that must be joined on shutdown
352 _shutdown_threads = []
353
354 _signalChain = {}
357 """
358 @return: True if shutdown flag has been set
359 @rtype: bool
360 """
361 return _shutdown_flag
362
364 """
365 is_shutdown_requested is a state that occurs just before
366 is_shutdown. It is initiated when a shutdown requested is
367 received and continues until client shutdown handlers have been
368 called. After client shutdown handlers have been serviced, the
369 is_shutdown state becomes true.
370
371 @return: True if shutdown has been requested (but possibly not yet initiated)
372 @rtype: bool
373 """
374 return _in_shutdown
375
377 """
378 shared implementation of add_shutdown_hook and add_preshutdown_hook
379 """
380 if not callable(h):
381 raise TypeError("shutdown hook [%s] must be a function or callable object: %s"%(h, type(h)))
382 if _shutdown_flag:
383 _logger.warn("add_shutdown_hook called after shutdown")
384 if pass_reason_argument:
385 h("already shutdown")
386 else:
387 h()
388 return
389 with _shutdown_lock:
390 if hooks is None:
391 # race condition check, don't log as we are deep into shutdown
392 return
393 hooks.append(h)
394
396 """
397 Register thread that must be joined() on shutdown
398 """
399 if _shutdown_flag:
400 #TODO
401 return
402 with _shutdown_lock:
403 if _shutdown_threads is None:
404 # race condition check, don't log as we are deep into shutdown
405 return
406 # in order to prevent memory leaks, reap dead threads. The
407 # last thread may not get reaped until shutdown, but this is
408 # relatively minor
409 for other in _shutdown_threads[:]:
410 if not other.isAlive():
411 _shutdown_threads.remove(other)
412 _shutdown_threads.append(t)
413
415 """
416 Add client method to invoke when system shuts down. Unlike
417 L{add_shutdown_hook} and L{add_preshutdown_hooks}, these methods
418 will be called before any rospy internal shutdown code.
419
420 @param h: function with zero args
421 @type h: fn()
422 """
423 _add_shutdown_hook(h, _client_shutdown_hooks, pass_reason_argument=False)
424
426 """
427 Add method to invoke when system shuts down. Unlike
428 L{add_shutdown_hook}, these methods will be called before any
429 other shutdown hooks.
430
431 @param h: function that takes in a single string argument (shutdown reason)
432 @type h: fn(str)
433 """
434 _add_shutdown_hook(h, _preshutdown_hooks)
435
437 """
438 Add method to invoke when system shuts down.
439
440 Shutdown hooks are called in the order that they are
441 registered. This is an internal API method that is used to
442 cleanup. See the client X{on_shutdown()} method if you wish to
443 register client hooks.
444
445 @param h: function that takes in a single string argument (shutdown reason)
446 @type h: fn(str)
447 """
448 _add_shutdown_hook(h, _shutdown_hooks)
449
451 """
452 Initiates shutdown process by signaling objects waiting on _shutdown_lock.
453 Shutdown and pre-shutdown hooks are invoked.
454 @param reason: human-readable shutdown reason, if applicable
455 @type reason: str
456 """
457 global _shutdown_flag, _in_shutdown, _shutdown_lock, _shutdown_hooks
458 _logger.info("signal_shutdown [%s]"%reason)
459 if _shutdown_flag or _in_shutdown:
460 return
461 with _shutdown_lock:
462 if _shutdown_flag or _in_shutdown:
463 return
464 _in_shutdown = True
465
466 # make copy just in case client re-invokes shutdown
467 for h in _client_shutdown_hooks:
468 try:
469 # client shutdown hooks do not accept a reason arg
470 h()
471 except:
472 traceback.print_exc()
473 del _client_shutdown_hooks[:]
474
475 for h in _preshutdown_hooks:
476 try:
477 h(reason)
478 except:
479 traceback.print_exc()
480 del _preshutdown_hooks[:]
481
482 # now that pre-shutdown hooks have been called, raise shutdown
483 # flag. This allows preshutdown hooks to still publish and use
484 # service calls properly
485 _shutdown_flag = True
486 for h in _shutdown_hooks:
487 try:
488 h(reason)
489 except Exception as e:
490 sys.stderr.write("signal_shutdown hook error[%s]\n"%e)
491 del _shutdown_hooks[:]
492
493 threads = _shutdown_threads[:]
494
495 for t in threads:
496 if t.isAlive():
497 t.join(_TIMEOUT_SHUTDOWN_JOIN)
498 del _shutdown_threads[:]
499 try:
500 rospy.rostime.wallsleep(0.1) #hack for now until we get rid of all the extra threads
501 except KeyboardInterrupt: pass
502
504 signal_shutdown("signal-"+str(sig))
505 prev_handler = _signalChain.get(sig, None)
506 if prev_handler is not None and not type(prev_handler) == int:
507 try:
508 prev_handler(sig, stackframe)
509 except KeyboardInterrupt:
510 pass #filter out generic keyboard interrupt handler
511
513 signal_shutdown('atexit')
514 atexit.register(_ros_atexit)
518 """
519 register system signal handlers for SIGTERM and SIGINT
520 """
521 _signalChain[signal.SIGTERM] = signal.signal(signal.SIGTERM, _ros_signal)
522 _signalChain[signal.SIGINT] = signal.signal(signal.SIGINT, _ros_signal)
523
527 """
528 Validator that checks that parameter is a valid ROS topic name
529 """
530 def validator(param_value, caller_id):
531 v = valid_name_validator_resolved(param_name, param_value, caller_id)
532 if param_value == '/':
533 raise ParameterInvalid("ERROR: parameter [%s] cannot be the global namespace"%param_name)
534 return v
535 return validator
536
538 """
539 @return: instance for calling remote server or None if not a valid URI
540 @rtype: xmlrpclib.ServerProxy
541 """
542 if uri is None:
543 return None
544 uriValidate = urlparse.urlparse(uri)
545 if not uriValidate[0] or not uriValidate[1]:
546 return None
547 return xmlrpcclient.ServerProxy(uri)
548
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Nov 2 03:52:39 2020 | http://epydoc.sourceforge.net |