00001
00002 import re
00003 import pydot
00004 import xml.etree.ElementTree as ET
00005 from sys import stdout
00006 import ntpath
00007 from optparse import OptionParser
00008 import sys
00009 import os
00010
00011
00012 node_counter=0;
00013 def map_all_ids(xml):
00014 global node_counter
00015 map_ids = {}
00016 for node in xml.iter():
00017 if "id" not in node.attrib: continue
00018 map_ids[node.attrib["id"]]=str(node_counter)
00019 node_counter+=1
00020 return map_ids
00021
00022 def path_leaf(path):
00023 head, tail = ntpath.split(path)
00024 return tail or ntpath.basename(head)
00025
00026 class NodeShape:
00027 def __init__(self, tag_name):
00028 self.nshape=""
00029 self.nfillcolor="#FFFFFF"
00030 self.npenwidth="1"
00031 if tag_name in ['tsk', 'task']:
00032 self.nshape="box"
00033 self.npenwidth="3"
00034 elif tag_name in ['seq', 'plan', 'pln']:
00035 self.nshape="rarrow"
00036 elif tag_name=='par':
00037 self.nshape="parallelogram"
00038 self.nfillcolor="#FFCC99"
00039 else:
00040 self.nshape="ellipse"
00041
00042 def set(self, node):
00043 node.set_shape(self.nshape)
00044 node.set_fillcolor(self.nfillcolor)
00045
00046
00047
00048 def get_tao_start(tao_node):
00049 return tao_node.attrib["start"]
00050
00051 def get_plan_details(plan_node):
00052
00053 start_condition = "none"
00054 stop_condition = "none"
00055 allocate_protocol = "empty"
00056 next_protocol = "empty"
00057
00058 for subnode in plan_node:
00059 if subnode.tag == "tao_start_condition":
00060 start_condition = subnode.text
00061 if subnode.tag == "tao_stop_condition":
00062 stop_condition = subnode.text
00063 if subnode.tag == "tao_allocate":
00064 allocate_protocol = subnode.attrib["protocol"]
00065 if subnode.tag == "tao_next":
00066 next_protocol = subnode.attrib["protocol"]
00067
00068
00069 return (start_condition, stop_condition, allocate_protocol, next_protocol)
00070
00071
00072 def gen_tao(node, graph):
00073 graph.add_node(pydot.Node("tao_root", label=" ", shape="point"))
00074 gen_tao_nodes(node, graph)
00075 graph.add_edge(pydot.Edge("tao_root", get_tao_start(node)))
00076
00077 def gen_tao_nodes(node, graph):
00078 plans = node[0]
00079 block = pydot.Subgraph('', rank="same")
00080
00081 for plan in plans:
00082 (start_condition, stop_condition, allocate_protocol, next_protocol) = get_plan_details(plan)
00083 import cgi
00084
00085 label_content = """<
00086
00087 <table style="">
00088 <tr>
00089 <td style="background-color: #23a4ff; color: #fff; font-weight: bold; padding: 5px; font-size: 12pt;" align="center">
00090 <b>{planName}</b>
00091 </td>
00092 </tr>
00093 <tr>
00094 <td style="padding: 0; maring: 0;">
00095
00096 <table style="border-width: 1px; border-style: solid; border-color: #999;">
00097 <tr>
00098 <td style="padding: 4px; maring: 0;">
00099 {startCondition}
00100 </td>
00101 <td style="padding: 4px; maring: 0;">
00102 {stopCondition}
00103 </td>
00104 </tr>
00105 <tr>
00106 <td style="padding: 4px; maring: 0;">{allocateProtocol}</td>
00107 <td style="padding: 4px; maring: 0;">{nextProtocol}</td>
00108 </tr>
00109 </table>
00110
00111 </td>
00112 </tr>
00113 </table>
00114 >""".format(
00115 planName = plan.attrib["name"],
00116 startCondition = cgi.escape(start_condition),
00117 stopCondition = cgi.escape(stop_condition),
00118 allocateProtocol = allocate_protocol,
00119 nextProtocol = next_protocol)
00120
00121 plan_node = pydot.Node(str(plan.attrib["id"]), shape="rectangle", label=label_content, URL=plan.attrib["id"])
00122 block.add_node(plan_node)
00123
00124 graph.add_subgraph(block)
00125 gen_tao_edges(node, graph)
00126
00127 for plan in plans:
00128 for plan_subnode in plan:
00129 if plan_subnode.tag == 'tao_allocate':
00130 for subtao in plan_subnode:
00131 gen_tao_nodes(subtao, graph)
00132
00133
00134 def gen_tao_edges(node, graph):
00135 plans = node[0]
00136
00137
00138
00139
00140 for plan in plans:
00141 for plan_subnode in plan:
00142 if plan_subnode.tag == 'tao_next':
00143 for next_op in plan_subnode:
00144 edge = pydot.Edge(plan.attrib["id"], next_op.attrib["name"])
00145 graph.add_edge(edge)
00146
00147
00148
00149
00150 for plan in plans:
00151 for plan_subnode in plan:
00152 if plan_subnode.tag == 'tao_allocate':
00153 for subtao in plan_subnode:
00154 edge = pydot.Edge(plan.attrib["id"], get_tao_start(subtao))
00155 graph.add_edge(edge)
00156
00157
00158 def graph_gen_nodes(xml, node, graph, elem, ids):
00159
00160 if node.tag in ['scxml']:
00161 for chnode in node:
00162 graph_gen_nodes(xml, chnode, graph, elem, ids)
00163 if node.tag in ['parallel']:
00164 for chnode in node:
00165 graph_gen_nodes(xml, chnode, graph, elem, ids)
00166 if node.tag in ['state', 'invoke']:
00167 if len(node.findall('state')+node.findall('invoke')+node.findall('plan')+node.findall('parallel'))==0:
00168
00169 if node.tag == 'invoke':
00170 gr_node=pydot.Node(ids[node.attrib["id"]],label=node.attrib["name"], URL=node.attrib["id"], penwidth="0")
00171 else:
00172 gr_node=pydot.Node(ids[node.attrib["id"]],label=node.attrib["name"], URL=node.attrib["id"])
00173
00174 elem.add_node(gr_node)
00175 else:
00176 has_init_state = 'initialstate' in node.attrib
00177 lbl = node.attrib["name"]
00178 if has_init_state : lbl = 'FSM['+lbl+']'
00179
00180
00181 gr_cluster=pydot.Cluster(ids[node.attrib["id"]],label=lbl, URL=node.attrib["id"])
00182
00183 elem.add_subgraph(gr_cluster)
00184 if has_init_state:
00185 gr_st_node = pydot.Node(ids[node.attrib["id"]]+"start", shape="point")
00186 gr_cluster.add_node(gr_st_node)
00187 for chnode in node:
00188 graph_gen_nodes(xml, chnode, graph, gr_cluster, ids)
00189 if node.tag in ['plan', 'par', 'seq', 'sel', 'dec', 'pln']:
00190 nlabel = node.attrib["name"]
00191 if node.tag in ['plan','pln']: nlabel='BT['+nlabel+']'
00192 gr_node=pydot.Node(ids[node.attrib["id"]],label=nlabel, URL=node.attrib["id"])
00193 shape = NodeShape(node.tag)
00194 shape.set(gr_node)
00195 elem.add_node(gr_node)
00196 for chnode in node:
00197 graph_gen_nodes(xml, chnode, graph, elem, ids)
00198 if node.tag in ['task','tsk']:
00199 if len(node.findall('scxml'))==0:
00200 gr_node=pydot.Node(ids[node.attrib["id"]],label=node.attrib["name"], URL=node.attrib["id"])
00201 shape = NodeShape(node.tag)
00202 shape.set(gr_node)
00203 elem.add_node(gr_node)
00204 for chnode in node:
00205 graph_gen_nodes(xml, chnode, graph, elem, ids)
00206
00207
00208 else:
00209 gr_cluster=pydot.Cluster(ids[node.attrib["id"]],label=node.attrib["name"], URL=node.attrib["id"])
00210 elem.add_subgraph(gr_cluster)
00211 for chnode in node.findall('scxml'):
00212 graph_gen_nodes(xml, chnode, graph, gr_cluster, ids)
00213
00214
00215
00216
00217
00218 if node.tag in ['tao']:
00219 gen_tao(node, graph)
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232 def find_simple_node(state):
00233 if state.tag == "state" and len([x for x in state if x.tag!='transition'])==0 : return state.attrib["id"]
00234 if state.tag == "invoke" and len([x for x in state if x.tag!='transition'])==0 : return state.attrib["id"]
00235 if state.tag in ['plan','pln', 'par', 'dec', 'seq', 'sel' ] : return state.attrib["id"]
00236 if state.tag in ["task","tsk"] and len(state.findall('scxml'))==0 : return state.attrib["id"]
00237 for n in state.iter():
00238 if n!=state:
00239 r = find_simple_node(n)
00240 if r!=None : return r
00241 return None
00242
00243 def find_state(fsm, state_id):
00244 for s in fsm:
00245 if "id" in s.attrib and s.attrib["id"]==state_id: return s
00246 return None
00247
00248 def graph_gen_edges(xml, node, graph, elem, ids, fsm=None):
00249
00250 if node.tag in ['scxml']:
00251 for chnode in node:
00252 graph_gen_edges(xml, chnode, graph, elem, ids, node)
00253 if node.tag in ['parallel']:
00254 for chnode in node:
00255 graph_gen_edges(xml, chnode, graph, elem, ids, fsm)
00256 if node.tag in ['task', 'tsk']:
00257 for chnode in node:
00258
00259 if "id" in node.attrib and "id" in chnode.attrib:
00260 src_state_id = node.attrib["id"]
00261 dst_state_id = chnode.attrib['id']
00262 src = src_state_id
00263 dst = dst_state_id
00264
00265 gr_edge = pydot.Edge(ids[src], ids[dst])
00266 graph.add_edge(gr_edge)
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278 for chnode in node:
00279 graph_gen_edges(xml, chnode, graph, elem, ids, fsm)
00280
00281 if node.tag in ['state', 'invoke']:
00282 has_init_state = 'initialstate' in node.attrib
00283 if has_init_state:
00284 src_state_id = ids[node.attrib["id"]]+'start'
00285 dst_state_id = node.attrib["id"]+"/"+node.attrib['initialstate']
00286 dst_state = find_state(node, dst_state_id)
00287 dst = None
00288 if dst_state == None:
00289 print "ERROR: dst_state == None", dst_state_id
00290 else:
00291 dst = find_simple_node(dst_state)
00292 gr_edge = pydot.Edge(src_state_id, ids[dst])
00293 if dst!=dst_state_id: gr_edge.set_lhead('cluster_'+ids[dst_state_id])
00294
00295 gr_edge.set_fontsize("8")
00296 graph.add_edge(gr_edge)
00297 for t in [ t for t in node.findall('transition') if 'target' in t.attrib ]:
00298 src_state_id = node.attrib["id"]
00299 dst_state_id = t.attrib['target']
00300 src = find_simple_node(node)
00301 dst_state = find_state(fsm, dst_state_id)
00302 dst = None
00303 if dst_state == None:
00304 print "ERROR: dst_state == None"
00305 else:
00306 dst = find_simple_node(dst_state)
00307 gr_edge = pydot.Edge(ids[src], ids[dst])
00308 gr_edge.set_label(t.attrib["event"])
00309 if dst!=dst_state_id: gr_edge.set_lhead('cluster_'+ids[dst_state_id])
00310 if src!=src_state_id: gr_edge.set_ltail('cluster_'+ids[src_state_id])
00311 gr_edge.set_fontsize("8")
00312 graph.add_edge(gr_edge)
00313 for chnode in node:
00314 graph_gen_edges(xml, chnode, graph, elem, ids, node)
00315 if node.tag in ['plan', 'par', 'seq', 'sel', 'dec', 'pln']:
00316 for chnode in node:
00317 src_state_id = node.attrib["id"]
00318 dst_state_id = chnode.attrib['id']
00319 src = find_simple_node(node)
00320 dst_state = chnode
00321 dst = find_simple_node(dst_state)
00322 gr_edge = pydot.Edge(ids[src], ids[dst])
00323
00324 if dst!=dst_state_id: gr_edge.set_lhead('cluster_'+ids[dst_state_id])
00325
00326
00327
00328 graph.add_edge(gr_edge)
00329 graph_gen_edges(xml, chnode, graph, elem, ids, fsm)
00330
00331
00332
00333
00334
00335
00336
00337 if node.tag in ['tao']:
00338 for chnode in node:
00339 graph_gen_edges(xml, chnode, graph, elem, ids, node)
00340
00341 if node.tag in ['tao_plans']:
00342 for chnode in node:
00343 graph_gen_edges(xml, chnode, graph, elem, ids, node)
00344
00345
00346
00347 if __name__ == '__main__':
00348
00349 parser = OptionParser()
00350 parser.add_option("-v", "--verbose", dest="verbose",
00351 help="verbose printouts", nargs=0)
00352 parser.add_option("-q", "--quiet", dest="quite",
00353 help="quiet printouts", nargs=0)
00354 parser.add_option("-i", "--info", dest="info",
00355 help="printouts just locations info", nargs=0)
00356
00357 (options, args) = parser.parse_args()
00358
00359 if len(sys.argv) < 2:
00360 sys.exit('Usage: %s projectName destinationFolder [...xmlFiles]' % sys.argv[0])
00361
00362 if options.verbose is () or options.info is ():
00363 print " -- project location is",args[0]
00364 print " -- share folder is",args[1]
00365 if options.verbose is ():
00366 print " -- FILES: ",'\n'.join(args[2:])
00367 print "-- Start decision making xml parsing"
00368 try:
00369 for fileXML in args[2].split(";"):
00370 fileName = path_leaf(fileXML)
00371 filetype = fileXML[-len("XXxml"):]
00372 if options.verbose is ():
00373 print fileXML, fileName,filetype
00374 xml = ET.parse(fileXML).getroot()
00375 graph = pydot.Dot(graph_type='digraph', compound='true')
00376 graph.set_node_defaults(shape="box");
00377 map_ids = map_all_ids(xml)
00378 if options.verbose is ():
00379 for k,v in map_ids.items(): print k,":",v
00380 graph_gen_nodes(xml, xml, graph, graph, map_ids)
00381 graph_gen_edges(xml, xml, graph, graph, map_ids)
00382
00383 (fname, fextension) = os.path.splitext(os.path.basename(fileName))
00384
00385 graph.write_raw(args[1] + os.sep + fname + ".dot")
00386 except:
00387 print " -- Unexpected error (",fileXML,"):", sys.exc_info()
00388 import traceback
00389 traceback.print_exc()
00390 print "-- End decision making xml parsing"