| 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 import logging
41 import os
42 import signal
43 import sys
44 import threading
45 import time
46 import traceback
47 import types
48
49 try:
50 import urllib.parse as urlparse #Python 3.x
51 except ImportError:
52 import urlparse
53
54 try:
55 import xmlrpc.client as xmlrpcclient #Python 3.x
56 except ImportError:
57 import xmlrpclib as xmlrpcclient #Python 2.x
58
59 import rospkg
60
61 import rosgraph.roslogging
62
63 import rospy.exceptions
64 import rospy.rostime
65
66 from rospy.names import *
67 from rospy.impl.validators import ParameterInvalid
68
69 from rosgraph_msgs.msg import Log
70
71 _logger = logging.getLogger("rospy.core")
72
73 # number of seconds to wait to join on threads. network issue can
74 # cause joins to be not terminate gracefully, and it's better to
75 # teardown dirty than to hang
76 _TIMEOUT_SHUTDOWN_JOIN = 5.
77
78 import warnings
80 """This is a decorator which can be used to mark functions
81 as deprecated. It will result in a warning being emmitted
82 when the function is used."""
83 def newFunc(*args, **kwargs):
84 warnings.warn("Call to deprecated function %s." % func.__name__,
85 category=DeprecationWarning, stacklevel=2)
86 return func(*args, **kwargs)
87 newFunc.__name__ = func.__name__
88 newFunc.__doc__ = func.__doc__
89 newFunc.__dict__.update(func.__dict__)
90 return newFunc
91
92 #########################################################
93 # ROSRPC
94
95 ROSRPC = "rosrpc://"
98 """
99 utility function for parsing ROS-RPC URIs
100 @param uri: ROSRPC URI
101 @type uri: str
102 @return: address, port
103 @rtype: (str, int)
104 @raise ParameterInvalid: if uri is not a valid ROSRPC URI
105 """
106 if uri.startswith(ROSRPC):
107 dest_addr = uri[len(ROSRPC):]
108 else:
109 raise ParameterInvalid("Invalid protocol for ROS service URL: %s"%uri)
110 try:
111 if '/' in dest_addr:
112 dest_addr = dest_addr[:dest_addr.find('/')]
113 dest_addr, dest_port = dest_addr.split(':')
114 dest_port = int(dest_port)
115 except:
116 raise ParameterInvalid("ROS service URL is invalid: %s"%uri)
117 return dest_addr, dest_port
118
119 #########################################################
120
121 # rospy logger
122 _rospy_logger = logging.getLogger("rospy.internal")
123
124 # we keep a separate, non-rosout log file to contain stack traces and
125 # other sorts of information that scare users but are essential for
126 # debugging
127
128 -def rospydebug(msg, *args):
140
141 logdebug = logging.getLogger('rosout').debug
142
143 logwarn = logging.getLogger('rosout').warning
144
145 loginfo = logging.getLogger('rosout').info
146 logout = loginfo # alias deprecated name
147
148 logerr = logging.getLogger('rosout').error
149 logerror = logerr # alias logerr
150
151 logfatal = logging.getLogger('rosout').critical
152
153 #########################################################
154 # CONSTANTS
155
156 MASTER_NAME = "master" #master is a reserved node name for the central master
157
158 import warnings
160 """This is a decorator which can be used to mark functions
161 as deprecated. It will result in a warning being emmitted
162 when the function is used."""
163 def newFunc(*args, **kwargs):
164 warnings.warn("Call to deprecated function %s." % func.__name__,
165 category=DeprecationWarning, stacklevel=2)
166 return func(*args, **kwargs)
167 newFunc.__name__ = func.__name__
168 newFunc.__doc__ = func.__doc__
169 newFunc.__dict__.update(func.__dict__)
170 return newFunc
171
174 """
175 Get the value of ROS_ROOT.
176 @param env: override environment dictionary
177 @type env: dict
178 @param required: if True, fails with ROSException
179 @return: Value of ROS_ROOT environment
180 @rtype: str
181 @raise ROSException: if require is True and ROS_ROOT is not set
182 """
183 if env is None:
184 env = os.environ
185 ros_root = rospkg.get_ros_root(env)
186 if required and not ros_root:
187 raise rospy.exceptions.ROSException('%s is not set'%rospkg.environment.ROS_ROOT)
188 return ros_root
189
190
191 #########################################################
192 # API
193
194 _uri = None
196 """
197 Get this Node's URI.
198 @return: this Node's XMLRPC URI
199 @rtype: str
200 """
201 return _uri
202
204 """set the URI of the local node.
205 This is an internal API method, it does not actually affect the XMLRPC URI of the Node."""
206 global _uri
207 _uri = uri
208
209 #########################################################
210 # Logging
211
212 _log_filename = None
214 """
215 Setup filesystem logging for this node
216 @param node_name: Node's name
217 @type node_name str
218 @param level: (optional) Python logging level (INFO, DEBUG, etc...). (Default: logging.INFO)
219 @type level: int
220 """
221 global _log_filename
222
223 # #988 __log command-line remapping argument
224 mappings = get_mappings()
225 if '__log' in get_mappings():
226 logfilename_remap = mappings['__log']
227 filename = os.path.abspath(logfilename_remap)
228 else:
229 # fix filesystem-unsafe chars
230 filename = node_name.replace('/', '_') + '.log'
231 if filename[0] == '_':
232 filename = filename[1:]
233 if not filename:
234 raise rospy.exceptions.ROSException('invalid configure_logging parameter: %s'%node_name)
235 _log_filename = rosgraph.roslogging.configure_logging('rospy', level, filename=filename)
236
240
241 # keep logging happy until we have the node name to configure with
242 logging.getLogger('rospy').addHandler(NullHandler())
243
244
245 #########################################################
246 # Init/Shutdown/Exit API and Handlers
247
248 _client_ready = False
252 """
253 Get the initialization state of the local node. If True, node has
254 been configured.
255 @return: True if local node initialized
256 @rtype: bool
257 """
258 return _client_ready
260 """
261 set the initialization state of the local node
262 @param initialized: True if node initialized
263 @type initialized: bool
264 """
265 global _client_ready
266 _client_ready = initialized
267
268 _shutdown_lock = threading.RLock()
269
270 # _shutdown_flag flags that rospy is in shutdown mode, in_shutdown
271 # flags that the shutdown routine has started. These are separate
272 # because 'pre-shutdown' hooks require rospy to be in a non-shutdown
273 # mode. These hooks are executed during the shutdown routine.
274 _shutdown_flag = False
275 _in_shutdown = False
276
277 # various hooks to call on shutdown. shutdown hooks are called in the
278 # shutdown state, preshutdown are called just before entering shutdown
279 # state, and client shutdown is called before both of these.
280 _shutdown_hooks = []
281 _preshutdown_hooks = []
282 _client_shutdown_hooks = []
283 # threads that must be joined on shutdown
284 _shutdown_threads = []
285
286 _signalChain = {}
289 """
290 @return: True if shutdown flag has been set
291 @rtype: bool
292 """
293 return _shutdown_flag
294
296 """
297 is_shutdown_requested is a state that occurs just before
298 is_shutdown. It is initiated when a shutdown requested is
299 received and continues until client shutdown handlers have been
300 called. After client shutdown handlers have been serviced, the
301 is_shutdown state becomes true.
302
303 @return: True if shutdown has been requested (but possibly not yet initiated)
304 @rtype: bool
305 """
306 return _in_shutdown
307
309 """
310 shared implementation of add_shutdown_hook and add_preshutdown_hook
311 """
312 if type(h) not in [types.FunctionType, types.MethodType]:
313 raise TypeError("shutdown hook [%s] must be a function: %s"%(h, type(h)))
314 if _shutdown_flag:
315 _logger.warn("add_shutdown_hook called after shutdown")
316 h("already shutdown")
317 return
318 with _shutdown_lock:
319 if hooks is None:
320 # race condition check, don't log as we are deep into shutdown
321 return
322 hooks.append(h)
323
325 """
326 Register thread that must be joined() on shutdown
327 """
328 if _shutdown_flag:
329 #TODO
330 return
331 with _shutdown_lock:
332 if _shutdown_threads is None:
333 # race condition check, don't log as we are deep into shutdown
334 return
335 # in order to prevent memory leaks, reap dead threads. The
336 # last thread may not get reaped until shutdown, but this is
337 # relatively minor
338 for other in _shutdown_threads[:]:
339 if not other.isAlive():
340 _shutdown_threads.remove(other)
341 _shutdown_threads.append(t)
342
344 """
345 Add client method to invoke when system shuts down. Unlike
346 L{add_shutdown_hook} and L{add_preshutdown_hooks}, these methods
347 will be called before any rospy internal shutdown code.
348
349 @param h: function that takes in a single string argument (shutdown reason)
350 @type h: fn(str)
351 """
352 _add_shutdown_hook(h, _client_shutdown_hooks)
353
355 """
356 Add method to invoke when system shuts down. Unlike
357 L{add_shutdown_hook}, these methods will be called before any
358 other shutdown hooks.
359
360 @param h: function that takes in a single string argument (shutdown reason)
361 @type h: fn(str)
362 """
363 _add_shutdown_hook(h, _preshutdown_hooks)
364
366 """
367 Add method to invoke when system shuts down.
368
369 Shutdown hooks are called in the order that they are
370 registered. This is an internal API method that is used to
371 cleanup. See the client X{on_shutdown()} method if you wish to
372 register client hooks.
373
374 @param h: function that takes in a single string argument (shutdown reason)
375 @type h: fn(str)
376 """
377 _add_shutdown_hook(h, _shutdown_hooks)
378
380 """
381 Initiates shutdown process by signaling objects waiting on _shutdown_lock.
382 Shutdown and pre-shutdown hooks are invoked.
383 @param reason: human-readable shutdown reason, if applicable
384 @type reason: str
385 """
386 global _shutdown_flag, _in_shutdown, _shutdown_lock, _shutdown_hooks
387 _logger.info("signal_shutdown [%s]"%reason)
388 if _shutdown_flag or _in_shutdown:
389 return
390 with _shutdown_lock:
391 if _shutdown_flag or _in_shutdown:
392 return
393 _in_shutdown = True
394
395 # make copy just in case client re-invokes shutdown
396 for h in _client_shutdown_hooks:
397 try:
398 # client shutdown hooks do not accept a reason arg
399 h()
400 except:
401 traceback.print_exc()
402 del _client_shutdown_hooks[:]
403
404 for h in _preshutdown_hooks:
405 try:
406 h(reason)
407 except:
408 traceback.print_exc()
409 del _preshutdown_hooks[:]
410
411 # now that pre-shutdown hooks have been called, raise shutdown
412 # flag. This allows preshutdown hooks to still publish and use
413 # service calls properly
414 _shutdown_flag = True
415 for h in _shutdown_hooks:
416 try:
417 h(reason)
418 except Exception as e:
419 sys.stderr.write("signal_shutdown hook error[%s]\n"%e)
420 del _shutdown_hooks[:]
421
422 threads = _shutdown_threads[:]
423
424 for t in threads:
425 if t.isAlive():
426 t.join(_TIMEOUT_SHUTDOWN_JOIN)
427 del _shutdown_threads[:]
428 try:
429 rospy.rostime.wallsleep(0.1) #hack for now until we get rid of all the extra threads
430 except KeyboardInterrupt: pass
431
433 signal_shutdown("signal-"+str(sig))
434 prev_handler = _signalChain.get(sig, None)
435 if prev_handler is not None and not type(prev_handler) == int:
436 try:
437 prev_handler(sig, stackframe)
438 except KeyboardInterrupt:
439 pass #filter out generic keyboard interrupt handler
440
442 signal_shutdown('atexit')
443 atexit.register(_ros_atexit)
447 """
448 register system signal handlers for SIGTERM and SIGINT
449 """
450 _signalChain[signal.SIGTERM] = signal.signal(signal.SIGTERM, _ros_signal)
451 _signalChain[signal.SIGINT] = signal.signal(signal.SIGINT, _ros_signal)
452
456 """
457 Validator that checks that parameter is a valid ROS topic name
458 """
459 def validator(param_value, caller_id):
460 v = valid_name_validator_resolved(param_name, param_value, caller_id)
461 if param_value == '/':
462 raise ParameterInvalid("ERROR: parameter [%s] cannot be the global namespace"%param_name)
463 return v
464 return validator
465
467 """
468 @return: instance for calling remote server or None if not a valid URI
469 @rtype: xmlrpclib.ServerProxy
470 """
471 if uri is None:
472 return None
473 uriValidate = urlparse.urlparse(uri)
474 if not uriValidate[0] or not uriValidate[1]:
475 return None
476 return xmlrpcclient.ServerProxy(uri)
477
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Aug 28 12:33:29 2015 | http://epydoc.sourceforge.net |