20 import xml.etree.ElementTree
31 "BlackboardCheckDouble",
32 "BlackboardCheckString",
54 "ReinitializeGlobalLocalization",
60 "initialPoseReceived",
81 args = parse_command_line()
82 xml_tree = xml.etree.ElementTree.parse(args.behavior_tree)
83 root_tree_name = find_root_tree_name(xml_tree)
84 behavior_tree = find_behavior_tree(xml_tree, root_tree_name)
85 dot = convert2dot(behavior_tree)
87 legend = make_legend()
89 legend.render(args.legend)
92 print(f
'Saving dot to {args.save_dot}')
93 args.save_dot.write(dot.source)
94 dot.render(args.image_out, view=args.display)
96 def parse_command_line():
97 parser = argparse.ArgumentParser(description=
'Convert a behavior tree XML file to an image')
98 parser.add_argument(
'--behavior_tree', required=
True,
99 help=
'the behavior tree XML file to convert to an image')
100 parser.add_argument(
'--image_out', required=
True,
101 help=
'The name of the output image file. Leave off the .png extension')
102 parser.add_argument(
'--display', action=
"store_true",
103 help=
'If specified, opens the image in the default viewer')
104 parser.add_argument(
'--save_dot', type=argparse.FileType(
'w'),
105 help=
'Saves the intermediate dot source to the specified file')
106 parser.add_argument(
'--legend',
107 help=
'Generate a legend image as well')
108 return parser.parse_args()
110 def find_root_tree_name(xml_tree):
111 return xml_tree.getroot().get(
'main_tree_to_execute')
113 def find_behavior_tree(xml_tree, tree_name):
114 trees = xml_tree.findall(
'BehaviorTree')
116 raise RuntimeError(
"No behavior trees were found in the XML file")
119 if tree_name == tree.get(
'ID'):
122 raise RuntimeError(f
'No behavior tree for name {tree_name} found in the XML file')
125 def convert2dot(behavior_tree):
126 dot = graphviz.Digraph()
128 parent_dot_name = str(hash(root))
129 dot.node(parent_dot_name, root.get(
'ID'), shape=
'box')
130 convert_subtree(dot, root, parent_dot_name)
136 def convert_subtree(dot, parent_node, parent_dot_name):
137 if parent_node.tag ==
"SubTree":
138 add_sub_tree(dot, parent_dot_name, parent_node)
140 add_nodes(dot, parent_dot_name, parent_node)
142 def add_sub_tree(dot, parent_dot_name, parent_node):
143 root_tree_name = parent_node.get(
'ID')
144 dot.node(parent_dot_name, root_tree_name, shape=
'box')
145 behavior_tree = find_behavior_tree(xml_tree, root_tree_name)
146 convert_subtree(dot, behavior_tree, parent_dot_name)
148 def add_nodes(dot, parent_dot_name, parent_node):
149 for node
in list(parent_node):
150 label = make_label(node)
151 dot.node(str(hash(node)), label, color=node_color(node.tag), style=
'filled', shape=
'box')
152 dot_name = str(hash(node))
153 dot.edge(parent_dot_name, dot_name)
154 convert_subtree(dot, node, dot_name)
158 def make_label(node):
159 label =
'< <table border="0" cellspacing="0" cellpadding="0">'
160 label +=
'<tr><td align="text"><i>' + node.tag +
'</i></td></tr>'
161 name = node.get(
'name')
163 label +=
'<tr><td align="text"><b>' + name +
'</b></td></tr>'
165 for (param_name, value)
in node.items():
166 label +=
'<tr><td align="left"><sub>' + param_name +
'=' + value +
'</sub></td></tr>'
167 label +=
'</table> >'
170 def node_color(type):
171 if type
in control_nodes:
173 if type
in action_nodes:
174 return "cornflowerblue"
175 if type
in condition_nodes:
177 if type
in decorator_nodes:
179 if type
in subtree_nodes:
186 legend = graphviz.Digraph(graph_attr={
'rankdir':
'LR'})
187 legend.attr(label=
'Legend')
188 legend.node(
'Unknown', shape=
'box', style=
'filled', color=
"grey")
189 legend.node(
'Action',
'Action Node', shape=
'box', style=
'filled', color=
"cornflowerblue")
190 legend.node(
'Condition',
'Condition Node', shape=
'box', style=
'filled', color=
"yellow2")
191 legend.node(
'Control',
'Control Node', shape=
'box', style=
'filled', color=
"chartreuse4")
196 if __name__ ==
'__main__':