00001
00002 """
00003 Copyright (c) 2012,
00004 Systems, Robotics and Vision Group
00005 University of the Balearican Islands
00006 All rights reserved.
00007
00008 Redistribution and use in source and binary forms, with or without
00009 modification, are permitted provided that the following conditions are met:
00010 * Redistributions of source code must retain the above copyright
00011 notice, this list of conditions and the following disclaimer.
00012 * Redistributions in binary form must reproduce the above copyright
00013 notice, this list of conditions and the following disclaimer in the
00014 documentation and/or other materials provided with the distribution.
00015 * Neither the name of Systems, Robotics and Vision Group, University of
00016 the Balearican Islands nor the names of its contributors may be used to
00017 endorse or promote products derived from this software without specific
00018 prior written permission.
00019
00020 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00021 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00022 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00023 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
00024 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00025 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00026 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00027 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00028 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 """
00031
00032
00033 try:
00034 import os
00035 import sys
00036
00037 import argparse
00038 import gtk
00039 import xdot
00040 import StringIO
00041 import fnmatch
00042 from xml.etree import ElementTree
00043 import roslib.packages
00044 except ImportError:
00045
00046 print((os.linesep * 2).join(["An error found importing one module:",
00047 str(sys.exc_info()[1]), "You need to install it", "Stopping..."]))
00048 sys.exit(-2)
00049
00050 def find_launchfiles(directory, pattern='*.launch'):
00051 for root, dirs, files in os.walk(directory):
00052 for basename in files:
00053 if fnmatch.fnmatch(basename, pattern):
00054 filename = os.path.join(root, basename)
00055 yield root, basename, filename
00056
00057 def check_launchfiles(directory, pattern='*.launch'):
00058 for root, dirs, files in os.walk(directory):
00059 for basename in files:
00060 if fnmatch.fnmatch(basename, pattern):
00061 return True;
00062 return False;
00063
00064 def prepare_file():
00065 g = StringIO.StringIO()
00066 g.write('digraph G {\n rankdir=LR \n')
00067 return g
00068
00069 def show_graph(g,package = 0):
00070 drawable = False
00071 g.write('}')
00072 if len(g.getvalue())>26:
00073 drawable = True
00074 window = xdot.xdot.DotWindow()
00075 window.set_dotcode(g.getvalue())
00076 if package:
00077 window.set_title('Package {0}'.format(package))
00078 window.connect('destroy', gtk.main_quit)
00079 g.close()
00080 return drawable
00081
00082 def start_subgraph(g,boxname):
00083 g.write(' subgraph "{0}"'.format(boxname))
00084 g.write(' {\n')
00085 g.write(' label = "{0}";\n'.format(boxname))
00086
00087 def write_information(g,node_name,node_args=0):
00088 if node_args==0:
00089 for name in node_name:
00090 g.write('"{0}_node" [shape=Mrecord,label="{0}",style="filled",fillcolor="khaki"]\n'.format(name))
00091 else:
00092 g.write('"{0}" [shape=none, margin=0, label=<\n'.format(node_name))
00093 g.write('<table border="0" cellborder="1" cellpadding="4">\n')
00094 g.write('<tr><td BGCOLOR="greenyellow"><b>{0}</b></td></tr>\n'.format(node_name))
00095 if node_args:
00096 g.write('<tr><td BGCOLOR="lemonchiffon">Arguments:</td></tr>\n')
00097 for (arg, arg_def) in node_args:
00098 g.write('<tr><td ALIGN="LEFT" BGCOLOR="lemonchiffon">{0} = {1}</td></tr>\n'.format(arg,arg_def))
00099 g.write('</table>>];\n')
00100
00101
00102 def write_connection(g,origin,destination, isNode=False):
00103 if not isNode:
00104 g.write(' "{0}" -> "{1}"\n'.format(origin,destination))
00105 elif isNode:
00106 g.write(' "{0}" -> "{1}_node"\n'.format(origin,destination))
00107
00108 def get_args_from_parser():
00109 parser = argparse.ArgumentParser(description='Draws graph from launchfiles')
00110 parser.add_argument('--pkg', metavar='package', help='ROS packages you want to inspect',nargs='+')
00111 return parser.parse_args()
00112
00113 def draw_folder(g,folder):
00114 working_directory = folder
00115 if check_launchfiles(folder):
00116 for root,filename,filepath in find_launchfiles(folder):
00117 if root != working_directory:
00118 working_directory = root
00119 f = open(filepath,'rt')
00120 tree = ElementTree.parse(f)
00121 node_name = os.path.splitext(filename)[0]
00122 it = tree.iter()
00123
00124 node_args = []
00125 node_includes = []
00126 node_nodes = []
00127 for node in it:
00128 if node.tag == "arg":
00129
00130 arg_name = node.attrib.get('name')
00131 default_value = node.attrib.get('default')
00132 value = node.attrib.get('value')
00133 if_clause = node.attrib.get('if')
00134 if default_value:
00135
00136 node_args.append((arg_name,default_value))
00137 elif not value:
00138
00139 node_args.append((arg_name,"REQUIRED"))
00140 elif if_clause:
00141 nodelet_args = value.split()[1]
00142 if node.tag == "include":
00143
00144 incl_ext = os.path.basename(node.attrib.get('file'))
00145 incl = os.path.splitext(incl_ext)[0]
00146 node_includes.append(incl)
00147 if node.tag == "node":
00148 node_type = node.attrib.get('type')
00149 if node_type == "nodelet":
00150 node_type = node.attrib.get('args')
00151 if len(node_type.split())>1:
00152 node_type = node_type.split()[1]
00153 if node_type[len(node_type)-1] == ")":
00154 node_type = nodelet_args
00155 node_nodes.append(node_type)
00156 else:
00157 node_nodes.append(node_type)
00158
00159
00160 write_information(g,node_name,node_args)
00161 write_information(g,node_nodes)
00162 for dest in node_nodes:
00163 write_connection(g,node_name,dest,True)
00164 for dest in node_includes:
00165 write_connection(g,node_name,dest)
00166 return g
00167 else:
00168 print "[W]\tNo launchfile was found in {0}".format(os.path.basename(folder))
00169
00170 def xml_process(packages = 0):
00171 g = prepare_file()
00172 if not packages:
00173 working_directory = os.getcwd();
00174 draw_folder(g,working_directory)
00175 else:
00176 for pkg in packages:
00177 directory = roslib.packages.get_pkg_dir(pkg)
00178 draw_folder(g,directory)
00179 return g
00180
00181 def main():
00182 args = get_args_from_parser()
00183 g = xml_process(args.pkg)
00184 if show_graph(g):
00185 gtk.main()
00186
00187 if __name__ == "__main__":
00188 main()