dotcode_behaviour.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # License: BSD
4 # https://raw.github.com/stonier/rqt_py_trees/license/LICENSE
5 #
6 ##############################################################################
7 # Documentation
8 ##############################################################################
9 
10 """
11 .. module:: dotcode_behaviour
12  :platform: Unix
13  :synopsis: Dot code a behaviour tree
14 
15 Dotcode generation for a py_trees behaviour tree.
16 """
17 
18 ##############################################################################
19 # Imports
20 ##############################################################################
21 
22 from __future__ import with_statement, print_function
23 
24 import rospy
25 import py_trees_msgs.msg as py_trees_msgs
26 import unique_id
27 
28 ##############################################################################
29 # Classes
30 ##############################################################################
31 
32 
34 
35  def __init__(self):
36  self.last_drawargs = None
37  self.dotcode = None
38  self.firstcall = True
39  self.rank = None
40  self.rankdir = None
41  self.ranksep = None
42  self.graph = None
43  self.dotcode_factory = None
45  (False, py_trees_msgs.Behaviour.INVALID): '#e4e4e4',
46  (True, py_trees_msgs.Behaviour.INVALID): '#e4e4e4',
47  (False, py_trees_msgs.Behaviour.RUNNING): '#9696c8',
48  (True, py_trees_msgs.Behaviour.RUNNING): '#0000ff',
49  (False, py_trees_msgs.Behaviour.FAILURE): '#c89696',
50  (True, py_trees_msgs.Behaviour.FAILURE): '#ff0000',
51  (False, py_trees_msgs.Behaviour.SUCCESS): '#96c896',
52  (True, py_trees_msgs.Behaviour.SUCCESS): '#00ff00',
53  }
55  (False, py_trees_msgs.Behaviour.INVALID): (228, 228, 228),
56  (True, py_trees_msgs.Behaviour.INVALID): (228, 228, 228),
57  (False, py_trees_msgs.Behaviour.RUNNING): (150, 150, 200),
58  (True, py_trees_msgs.Behaviour.RUNNING): (0, 0, 255),
59  (False, py_trees_msgs.Behaviour.FAILURE): (200, 150, 150),
60  (True, py_trees_msgs.Behaviour.FAILURE): (255, 0, 0),
61  (False, py_trees_msgs.Behaviour.SUCCESS): (150, 200, 150),
62  (True, py_trees_msgs.Behaviour.SUCCESS): (0, 255, 0),
63  }
64 
65  def generate_dotcode(self,
66  dotcode_factory,
67  timer=rospy.Time,
68  behaviours=None,
69  timestamp=None,
70  rank='same', # None, same, min, max, source, sink
71  ranksep=0.2, # vertical distance between layers
72  rankdir='TB', # direction of layout (TB top > bottom, LR left > right)
73  force_refresh=False):
74  """
75  :param py_trees_msgs.Behaviour[] behaviours:
76  :param force_refresh: if False, may return same dotcode as last time
77  """
78  if self.firstcall is True:
79  self.firstcall = False
80  force_refresh = True
81 
82  drawing_args = {
83  'dotcode_factory': dotcode_factory,
84  "rank": rank,
85  "rankdir": rankdir,
86  "ranksep": ranksep}
87 
88  selection_changed = False
89  if self.last_drawargs != drawing_args:
90  selection_changed = True
91  self.last_drawargs = drawing_args
92 
93  self.dotcode_factory = dotcode_factory
94  self.rank = rank
95  self.rankdir = rankdir
96  self.ranksep = ranksep
97 
98  self.graph = self.generate(behaviours, timestamp)
99  self.dotcode = self.dotcode_factory.create_dot(self.graph)
100  return self.dotcode
101 
102  def type_to_shape(self, behaviour_type):
103  """
104  qt_dotgraph.node_item only supports drawing in qt of two
105  shapes - box, ellipse.
106  """
107  if behaviour_type == py_trees_msgs.Behaviour.BEHAVIOUR:
108  return 'ellipse'
109  elif behaviour_type == py_trees_msgs.Behaviour.SEQUENCE:
110  return 'box'
111  elif behaviour_type == py_trees_msgs.Behaviour.SELECTOR:
112  return 'octagon'
113  elif behaviour_type == py_trees_msgs.Behaviour.PARALLEL:
114  return 'note'
115  elif behaviour_type == py_trees_msgs.Behaviour.CHOOSER:
116  return 'doubleoctagon'
117  else:
118  return 'ellipse'
119 
120  def type_to_colour(self, behaviour_type):
121  if behaviour_type == py_trees_msgs.Behaviour.BEHAVIOUR:
122  return None
123  elif behaviour_type == py_trees_msgs.Behaviour.SEQUENCE:
124  return '#ff9900'
125  elif behaviour_type == py_trees_msgs.Behaviour.SELECTOR:
126  return '#808080'
127  elif behaviour_type == py_trees_msgs.Behaviour.PARALLEL:
128  return '#ffd700'
129  elif behaviour_type == py_trees_msgs.Behaviour.CHOOSER:
130  return '#808080'
131  else:
132  return None
133 
134  def type_to_string(self, behaviour_type):
135  if behaviour_type == py_trees_msgs.Behaviour.BEHAVIOUR:
136  return 'Behaviour'
137  elif behaviour_type == py_trees_msgs.Behaviour.SEQUENCE:
138  return 'Sequence'
139  elif behaviour_type == py_trees_msgs.Behaviour.SELECTOR:
140  return 'Selector'
141  elif behaviour_type == py_trees_msgs.Behaviour.PARALLEL:
142  return 'Parallel'
143  elif behaviour_type == py_trees_msgs.Behaviour.CHOOSER:
144  return 'Chooser'
145  else:
146  return None
147 
148  def status_to_string(self, behaviour_status):
149  if behaviour_status == py_trees_msgs.Behaviour.INVALID:
150  return 'Invalid'
151  elif behaviour_status == py_trees_msgs.Behaviour.RUNNING:
152  return 'Running'
153  elif behaviour_status == py_trees_msgs.Behaviour.FAILURE:
154  return 'Failure'
155  elif behaviour_status == py_trees_msgs.Behaviour.SUCCESS:
156  return 'Success'
157  else:
158  return None
159 
160  def behaviour_to_tooltip_string(self, behaviour):
161  to_display = ['class_name', 'type', 'status', 'message'] # should be static
162  string = ''
163 
164  for attr in to_display:
165  if attr == 'type':
166  value = self.type_to_string(getattr(behaviour, attr))
167  elif attr == 'status':
168  value = self.status_to_string(getattr(behaviour, attr))
169  else:
170  value = str(getattr(behaviour, attr))
171 
172  value = "<i>empty</i>" if not value else value
173 
174  string += '<b>' + attr.replace('_', ' ').title() + ':</b> ' + value + "<br>"
175 
176  return "\"" + string + "\""
177 
178  def generate(self, data, timestamp):
179  """
180  :param py_trees_msgs.Behaviour[] data:
181  :param ??? timestamp:
182  """
183  graph = self.dotcode_factory.get_graph(rank=self.rank,
184  rankdir=self.rankdir,
185  ranksep=self.ranksep)
186 
187  if len(data) == 0:
188  self.dotcode_factory.add_node_to_graph(graph, 'No behaviour data received')
189  return graph
190 
191  behaviour_dict_by_id = {}
192  for behaviour in data:
193  behaviour_dict_by_id[behaviour.own_id] = behaviour
194  # first, add nodes to the graph, along with some cached information to
195  # make it easy to create the coloured edges on the second pass
196  states = {}
197  for behaviour in data:
198  self.dotcode_factory.add_node_to_graph(graph,
199  str(behaviour.own_id),
200  nodelabel=behaviour.name,
201  shape=self.type_to_shape(behaviour.type),
202  color=self.active_and_status_colour_hex_map[(behaviour.is_active, behaviour.status)],
203  tooltip=self.behaviour_to_tooltip_string(behaviour))
204  states[unique_id.fromMsg(behaviour.own_id)] = (behaviour.is_active, behaviour.status)
205 
206  for behaviour in data:
207  for child_id in behaviour.child_ids:
208  # edge colour is set using integer tuples, not hexes
209  try:
210  (is_active, status) = states[unique_id.fromMsg(child_id)]
211  except KeyError:
212  # the child isn't part of the 'visible' tree
213  continue
214  edge_colour = self.active_and_status_colour_tuple_map[(is_active, status)]
215  self.dotcode_factory.add_edge_to_graph(graph,
216  str(behaviour.own_id),
217  str(child_id),
218  color=edge_colour)
219 
220  return graph
static boost::uuids::uuid fromMsg(uuid_msgs::UniqueID const &msg)
def generate_dotcode(self, dotcode_factory, timer=rospy.Time, behaviours=None, timestamp=None, rank='same', ranksep=0.2, rankdir='TB', force_refresh=False)


rqt_py_trees
Author(s): Thibault Kruse, Michal Staniaszek, Daniel Stonier, Naveed Usmani
autogenerated on Mon Jun 10 2019 14:55:56