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 try:
00121 tree = ElementTree.parse(f)
00122 node_name = os.path.splitext(filename)[0]
00123 it = tree.iter()
00124
00125 node_args = []
00126 node_includes = []
00127 node_nodes = []
00128 for node in it:
00129 if node.tag == "arg":
00130
00131 arg_name = node.attrib.get('name')
00132 default_value = node.attrib.get('default')
00133 value = node.attrib.get('value')
00134 if_clause = node.attrib.get('if')
00135 if default_value:
00136
00137 node_args.append((arg_name,default_value))
00138 elif not value:
00139
00140 node_args.append((arg_name,"REQUIRED"))
00141 elif if_clause:
00142 nodelet_args = value.split()[1]
00143 if node.tag == "include":
00144
00145 incl_ext = os.path.basename(node.attrib.get('file'))
00146 incl = os.path.splitext(incl_ext)[0]
00147 node_includes.append(incl)
00148 if node.tag == "node":
00149 node_type = node.attrib.get('type')
00150 if node_type == "nodelet":
00151 node_type = node.attrib.get('args')
00152 if len(node_type.split())>1:
00153 node_type = node_type.split()[1]
00154 if node_type[len(node_type)-1] == ")":
00155 node_type = nodelet_args
00156 node_nodes.append(node_type)
00157 else:
00158 node_nodes.append(node_type)
00159
00160
00161 write_information(g,node_name,node_args)
00162 write_information(g,node_nodes)
00163 for dest in node_nodes:
00164 write_connection(g,node_name,dest,True)
00165 for dest in node_includes:
00166 write_connection(g,node_name,dest)
00167 except ElementTree.ParseError:
00168 print "Error parsing {}".format(filepath)
00169
00170 return g
00171 else:
00172 print "[W]\tNo launchfile was found in {0}".format(os.path.basename(folder))
00173
00174 def xml_process(packages = 0):
00175 g = prepare_file()
00176 if not packages:
00177 working_directory = os.getcwd();
00178 draw_folder(g,working_directory)
00179 else:
00180 for pkg in packages:
00181 directory = roslib.packages.get_pkg_dir(pkg)
00182 draw_folder(g,directory)
00183 return g
00184
00185 def main():
00186 args = get_args_from_parser()
00187 g = xml_process(args.pkg)
00188 if show_graph(g):
00189 gtk.main()
00190
00191 if __name__ == "__main__":
00192 main()