$search
00001 #!/usr/bin/env python 00002 # Software License Agreement (BSD License) 00003 # 00004 # Copyright (c) 2008, Willow Garage, Inc. 00005 # All rights reserved. 00006 # 00007 # Redistribution and use in source and binary forms, with or without 00008 # modification, are permitted provided that the following conditions 00009 # are met: 00010 # 00011 # * Redistributions of source code must retain the above copyright 00012 # notice, this list of conditions and the following disclaimer. 00013 # * Redistributions in binary form must reproduce the above 00014 # copyright notice, this list of conditions and the following 00015 # disclaimer in the documentation and/or other materials provided 00016 # with the distribution. 00017 # * Neither the name of Willow Garage, Inc. nor the names of its 00018 # contributors may be used to endorse or promote products derived 00019 # from this software without specific prior written permission. 00020 # 00021 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 # POSSIBILITY OF SUCH DAMAGE. 00033 # 00034 # Revision $Id: impl.py 10203 2010-06-30 20:47:37Z kwc $ 00035 00036 from __future__ import with_statement 00037 00038 import sys 00039 import time 00040 import threading 00041 import traceback 00042 import rospy 00043 import rosgraph.impl.graph 00044 import rxgraphplus.dotcode 00045 from rxgraphplus.dotcode import generate_dotcode, generate_namespaces, NODE_NODE_GRAPH, NODE_TOPIC_GRAPH, NODE_TOPIC_ALL_GRAPH 00046 from rxgraphplus.viewer import RxGraphViewerFrame 00047 00048 import roslib.scriptutil 00049 import rostopic 00050 import rosnode 00051 00052 # have to import wx last due to xdot playing with the wx version 00053 import wx 00054 00055 def get_info_text(selection_url): 00056 if selection_url is None: 00057 return '' 00058 if selection_url.startswith('node:'): 00059 try: 00060 node_name = selection_url[5:] 00061 master = roslib.scriptutil.get_master() 00062 node_api = rosnode.get_api_uri(master, node_name) 00063 return rosnode.get_node_info_description(node_name) + rosnode.get_node_connection_info_description(node_api) 00064 except rosnode.ROSNodeException, e: 00065 return "ERROR: %s"%str(e) 00066 00067 elif selection_url.startswith('topic:'): 00068 try: 00069 return rostopic.get_info_text(selection_url[6:]) 00070 except rostopic.ROSTopicException, e: 00071 return "ERROR: %s"%str(e) 00072 00073 class DotUpdate(threading.Thread): 00074 """Thread to control update of dot file""" 00075 00076 def __init__(self, graph, viewer, output_file=None): 00077 threading.Thread.__init__(self, name="DotUpdate") 00078 self.viewer = viewer 00079 self.graph = graph 00080 self.output_file = output_file 00081 self.selection_url = None 00082 self.selection_update = False 00083 self.config = rospy.get_param('/rxgraph/config', {}) 00084 00085 def select_callback(self, target, event): 00086 try: 00087 url = target.url 00088 if url: 00089 self.selection_url = url 00090 self.selection_update = True 00091 except: 00092 pass 00093 00094 def run(self): 00095 viewer = self.viewer 00096 current_ns_filter = viewer.ns_filter 00097 g = self.graph 00098 output_file = self.output_file 00099 last_graph_mode = NODE_NODE_GRAPH 00100 quiet = False 00101 00102 orientation = rxgraphplus.dotcode.ORIENTATIONS[0] 00103 info_text = '' 00104 00105 g.set_master_stale(5.0) 00106 g.set_node_stale(5.0) 00107 00108 GUPDATE_INTERVAL = 0.5 00109 INFO_UPDATE_INTERVAL = 10. 00110 00111 last_gupdate = time.time() - GUPDATE_INTERVAL 00112 last_info_update = time.time() - GUPDATE_INTERVAL 00113 try: 00114 while not is_shutdown(): 00115 00116 # #2839 by default, changes zoom, but we can cancel this for certain events 00117 zoom = True 00118 00119 # throttle calls to g.update(). we want fast refresh 00120 # on changes to the viewer's ns_filter, less so on the 00121 # graph polling. 00122 now = time.time() 00123 if now - last_gupdate >= GUPDATE_INTERVAL: 00124 changed = g.update() 00125 last_gupdate = now 00126 00127 if now - last_info_update >= INFO_UPDATE_INTERVAL or self.selection_update: 00128 last_info_update = now 00129 if self.selection_url is not None: 00130 info_text = get_info_text(self.selection_url) 00131 00132 graph_mode = NODE_TOPIC_ALL_GRAPH if viewer.topic_boxes else NODE_NODE_GRAPH 00133 00134 changed |= viewer.ns_filter != current_ns_filter 00135 changed |= quiet != viewer.quiet 00136 changed |= graph_mode != last_graph_mode 00137 if self.selection_update: 00138 self.selection_update = False 00139 # only alter the zoom flag if this is the sole change reason 00140 if not changed: 00141 changed = True 00142 zoom = False 00143 00144 quiet = viewer.quiet 00145 last_graph_mode = graph_mode 00146 00147 if changed: 00148 current_ns_filter = viewer.ns_filter 00149 dotcode = generate_dotcode(g, current_ns_filter, graph_mode, orientation, self.config, quiet) 00150 00151 #compute path-combo box 00152 namespaces = generate_namespaces(g, graph_mode, quiet) 00153 viewer.update_namespaces(namespaces) 00154 00155 viewer.set_dotcode(dotcode, zoom=zoom) 00156 viewer.set_info_text(info_text) 00157 00158 # store dotcode if requested 00159 if output_file: 00160 with file(output_file, 'w') as f: 00161 f.write(dotcode) 00162 00163 #information still changing, shorter yield 00164 time.sleep(0.1) 00165 else: 00166 # no new information, let bad nodes update 00167 g.bad_update() 00168 time.sleep(0.1) 00169 00170 changed = False 00171 00172 except wx.PyDeadObjectError: 00173 # shutdown 00174 pass 00175 except: 00176 traceback.print_exc() 00177 00178 _is_shutdown = False 00179 def is_shutdown(): 00180 return _is_shutdown 00181 00182 def set_shutdown(is_shutdown): 00183 global _is_shutdown 00184 _is_shutdown = is_shutdown 00185 00186 def init_frame(): 00187 frame = RxGraphViewerFrame() 00188 frame.set_dotcode(rxgraphplus.dotcode.INIT_DOTCODE) 00189 return frame 00190 00191 def init_updater(frame, node_ns=None, topic_ns=None, output_file=None): 00192 graph = rosgraph.impl.graph.Graph(node_ns, topic_ns) 00193 updater = DotUpdate(graph, frame, output_file=output_file) 00194 frame.register_select_cb(updater.select_callback) 00195 return updater