WUT Velma robot API
subsystem_widget.py
Go to the documentation of this file.
1 # Copyright (c) 2014, Robot Control and Pattern Recognition Group, Warsaw University of Technology
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
6 # * Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 # * Redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution.
11 # * Neither the name of the Warsaw University of Technology nor the
12 # names of its contributors may be used to endorse or promote products
13 # derived from this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 # DISCLAIMED. IN NO EVENT SHALL <COPYright HOLDER> BE LIABLE FOR ANY
19 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 import os
27 import subprocess
28 import copy
29 import math
30 
31 from python_qt_binding import loadUi
32 from python_qt_binding.QtCore import Qt, Signal, Slot
33 from python_qt_binding.QtWidgets import QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QLabel
34 import rospkg
35 import rospy
36 from rospy.exceptions import ROSException
37 
38 import tempfile
39 
40 import matplotlib.pyplot as plt
41 import numpy as np
42 
43 from subsystem_msgs.srv import *
44 import subsystem_common
45 
46 import behavior_graph
47 import fsm_graph
48 import subsystem_components
49 import subsystem_state_history
50 import dot_graph
51 import latex_equations
52 import subsystem_logs
53 
54 from diagnostic_msgs.msg import DiagnosticArray
55 
56 class SubsystemWidget(QWidget):
57  """
58  main class inherits from the ui window class.
59 
60  TODO: description.
61  """
62 
63  def layout_widgets(self, layout):
64  return (layout.itemAt(i) for i in range(layout.count()))
65 
66  @Slot()
68  self.dialogBehaviorGraph.show()
69 
70  @Slot()
72  self.dialogStateHistory.show()
73 
74  @Slot()
76  self.dialogComponents.show()
77 
78  @Slot()
80  self.dialogStateMachineGraph.show()
81 
82  @Slot()
84  #self.dialogBehaviorGraph.show()
85  if not self.period_histogram is None:
86 
87  ranges = [0.0001, 0.0002, 0.0003, 0.0004, 0.0006, 0.0008, 0.001, 0.0012,
88  0.0014, 0.0016, 0.002, 0.0024, 0.0028, 0.0032, 0.0038, 0.0050,
89  0.0060, 0.0080, 0.01, 0.02, 0.03, 0.05,]
90  ranges_str = []
91  for val in ranges:
92  ranges_str.append( str(val*1000) )
93 
94  x = np.arange(len(self.period_histogram))
95  y = self.period_histogram
96  y_log = []
97  for idx in range(len(y)):
98  val = y[idx]
99  if self.prev_period_histogram is None:
100  prev_val = 0
101  else:
102  prev_val = self.prev_period_histogram[idx]
103  val_rel = val - prev_val
104  if val_rel == 0:
105  y_log.append( 0 )
106  else:
107  y_log.append( val_rel )#math.log(val_rel) )
108  plt.bar(x, y_log)
109  plt.xticks(x[0:-1]+0.5, ranges_str)
110 
111  #plt.yticks(x[0:-1]+0.5, ranges_str)
112 
113  plt.show()
114 
116 
117  @Slot()
118  def on_click_showLogs(self):
119  self.dialogLogs.show()
120 
121  def __init__(self, plugin=None, name=None):
122  """
123  @type selected_topics: list of tuples.
124  @param selected_topics: [($NAME_TOPIC$, $TYPE_TOPIC$), ...]
125  @type select_topic_type: int
126  @param select_topic_type: Can specify either the name of topics or by
127  the type of topic, to filter the topics to
128  show. If 'select_topic_type' argument is
129  None, this arg shouldn't be meaningful.
130  """
131  super(SubsystemWidget, self).__init__()
132 
133  self.initialized = False
134 
135  rp = rospkg.RosPack()
136  ui_file = os.path.join(rp.get_path('rqt_agent'), 'resource', 'SubsystemWidget.ui')
137  loadUi(ui_file, self)
138  self._plugin = plugin
139 
140  print "created Subsystem widget"
141 
142  if name != None:
143  self.SubsystemName.setText(name)
144  else:
145  self.SubsystemName.setText("<could not get subsystem name>")
146 
147  self.subsystem_name = name
148 
149  self.subsystem_info = None
150  self.state = ''
151  self.behavior = ''
152  self.period_histogram = None
153  self.prev_period_histogram = None
154 
155  self.all_buffers = {}
156 
157  self.components = {}
158 
159  self.graph_generated = None
160 
162  self.showBehaviorGraph.clicked.connect(self.on_click_showBehaviorGraph)
163 
165  self.showStateHistory.clicked.connect(self.on_click_showStateHistory)
166 
168  self.showComponentsList.clicked.connect(self.on_click_showComponentsList)
169 
171  self.showStateMachineGraph.clicked.connect(self.on_click_showStateMachineGraph)
172 
173  self.showPeriodHistogram.clicked.connect(self.on_click_showPeriodHistogram)
174 
176  self.showLogs.clicked.connect(self.on_click_showLogs)
177 
178  # Connect to logs ROS topic
179  if self.subsystem_name != None:
180  self._subscriber = rospy.Subscriber('/{}/log'.format( self.subsystem_name ),
181  DiagnosticArray, self.__logMsgCallback)
182 
183  def __logMsgCallback(self, msg):
184  #logger_names = []
185  logs = {}
186 
187  for status in msg.status:
188  logger_name = status.name
189  logs[logger_name] = []
190  for value in status.values:
191  log_str = value.value
192  logs[logger_name].append( log_str )
193 
194  self.dialogLogs.update(logs)
195 
196  def isInitialized(self):
197  return self.initialized
198 
199  def extractConnectionInfo(self, conn, comp_from, comp_to):
200  if (not comp_to) or (not comp_from):
201  print 'WARNING: wrong edge(1): ', conn, comp_from, comp_to
202  return None
203 
204  if conn.find(comp_from.name) != 0:
205  print 'WARNING: wrong edge(2): ', conn, comp_from.name, comp_to.name
206  return None
207 
208  idx = len(comp_from.name)
209  port_from = None
210  for p in comp_from.ports:
211  if conn.find(p.name, idx) == idx:
212  port_from = p.name
213  idx += len(p.name)
214  break
215 
216  if not port_from:
217  print 'WARNING: wrong edge(3): ', conn, comp_from.name, comp_to.name
218  return None
219 
220  if conn.find(comp_to.name, idx) != idx:
221  print 'WARNING: wrong edge(4): ', conn, comp_from.name, comp_to.name
222 
223  idx += len(comp_to.name)
224 
225  port_to = None
226  for p in comp_to.ports:
227  if conn.find(p.name, idx) == idx:
228  port_to = p.name
229  idx += len(p.name)
230  break
231 
232  if not port_to:
233  print 'WARNING: wrong edge(5): ', conn, comp_from.name, comp_to.name
234  return None
235 
236  return (comp_from.name, port_from, comp_to.name, port_to)
237 
238  def exportBehaviorGraph(self, graph_name):
239  self.behavior_graphs[graph_name].exportToPdf(self.subsystem_name+"_"+graph_name+".pdf")
240 
242  behavior_graphs_list = ["<all>"]#, "<always running>"]
243  for behavior in self.subsystem_info.behaviors:
244  behavior_graphs_list.append(behavior.name)
245 
246  for graph_name in behavior_graphs_list:
247  self.exportBehaviorGraph(graph_name)
248 
249  def genBehaviorGraph(self, subsystem_info, behavior_name, hide_converters=True):
250  g = dot_graph.Graph(shape="rounded_box")
251  behavior = None
252  for b in subsystem_info.behaviors:
253  if b.name == behavior_name:
254  behavior = b
255  break
256 
257  if hide_converters:
258  conv_comp = set()
259  for comp in subsystem_info.components:
260  if comp.is_converter:
261  conv_comp.add(comp.name)
262 
263  sw_comp = set()
264  for b in subsystem_info.behaviors:
265  for r in b.running_components:
266  sw_comp.add(r)
267 
268  all_comp = set()
269  for comp in subsystem_info.components:
270  all_comp.add(comp.name)
271 
272  always_running = all_comp - sw_comp
273 
274  conn_set = {}
275  current_running = set()
276  if behavior:
277  for r in behavior.running_components:
278  current_running.add(r)
279  other_behaviors_comp = sw_comp - current_running
280  running = always_running.union(current_running)
281 
282  if hide_converters:
283  conv_connections = {}
284 
285  for c in subsystem_info.component_connections:
286  if ((not c.component_from.strip()) or (not c.component_to.strip())) and not c.unconnected:
287  continue
288  if behavior:
289  if (not c.component_from in current_running) and (not c.component_to in current_running):
290  continue
291  if c.component_from in other_behaviors_comp or c.component_to in other_behaviors_comp:
292  continue
293 # elif name == "<always running>":
294 # if (not c.component_from in always_running) or (not c.component_to in always_running):
295 # continue
296  elif behavior_name == "<all>":
297  pass
298  else:
299  raise Exception('getBehaviorConnectionsSet', 'wrong behavior name: ' + behavior_name)
300 
301  c_from = c.component_from
302  c_to = c.component_to
303  unconnected = c.unconnected
304  c_name = c.name
305  if hide_converters:
306  if c_from in conv_comp:
307  if not c_from in conv_connections:
308  conv_connections[c_from] = (None, c_to)
309  continue
310  else:
311  conv_connections[c_from] = (conv_connections[c_from][0], c_to)
312  c_to = conv_connections[c_from][1]
313  c_from = conv_connections[c_from][0]
314  unconnected = False
315  elif c_to in conv_comp:
316  if not c_to in conv_connections:
317  conv_connections[c_to] = (c_from, None)
318  continue
319  else:
320  conv_connections[c_to] = (c_from, conv_connections[c_to][1])
321  c_from = conv_connections[c_to][0]
322  c_to = conv_connections[c_to][1]
323  unconnected = False
324 
325  if not unconnected:
326  conn_tuple = (c_from, c_to)
327  else:
328  if not c_from.strip():
329  conn_tuple = (None, c_to)
330  else:
331  conn_tuple = (c_from, None)
332 
333  if c_name:
334  cname = c_name
335  elif c.port_from.strip():
336  cname = c.port_from
337  if not unconnected and cname.endswith("_OUTPORT"):
338  cname = cname[:-8]
339  else:
340  cname = c.port_to
341  if not unconnected and cname.endswith("_INPORT"):
342  cname = cname[:-7]
343 
344  latex_name = c.latex
345  if not latex_name:
346  #latex_name = '\\text{' + cname + '}'
347  latex_name = None
348 # if not cname:
349 # continue
350  if conn_tuple in conn_set:
351  #conn_set[conn_tuple] = conn_set[conn_tuple] + "\\n" + cname
352  conn_set[conn_tuple][0].append(cname)
353  conn_set[conn_tuple][1].append(latex_name)
354  else:
355  conn_set[conn_tuple] = [[cname], [latex_name]]
356 
357  shown_components = set()
358  for conn_tuple in conn_set:
359  edge = g.getEdge(conn_tuple[0], conn_tuple[1])
360  if edge == None:
361  edge = dot_graph.Graph.Edge()
362  edge.id_from = conn_tuple[0]
363  edge.id_to = conn_tuple[1]
364  for i in range(len(conn_set[conn_tuple][0])):
365  edge.addLabel(conn_set[conn_tuple][0][i], conn_set[conn_tuple][1][i])
366  if conn_tuple[0] != None:
367  shown_components.add(conn_tuple[0])
368  if conn_tuple[1] != None:
369  shown_components.add(conn_tuple[1])
370  g.edges.append(edge)
371 
372  prev_comp = None
373  for c in subsystem_info.components:
374  if not c.name in shown_components:
375  continue
376  node = dot_graph.Graph.Node()
377  node.label = c.name
378  node.latex_label = c.latex
379  g.nodes[c.name] = node
380 
381  # add edges for exec sequence
382  # this is not good
383 # if prev_comp != None:
384 # edge = dot_graph.Graph.Edge()
385 # edge.id_from = prev_comp
386 # edge.id_to = c.name
387 # g.edges.append(edge)
388 # prev_comp = c.name
389 
390  return g
391 
392  def genStateMachineGraph(self, subsystem_info):
393  g = dot_graph.Graph()
394 
395  print "subsystem name: " + self.subsystem_name
396  #edges_counter = 0
397  for state in subsystem_info.state_machine:
398  # domyslnie przyjmujemy, ze stan jest niepodlaczony
399  # trzeba jeszcze obsluzyc jak faktycznie nie bedzie nastepnikow
400  # print "state.name: ", state.name
402  n.label = state.name
403  g.nodes[state.name] = n
404  print " state: " + state.name
405  print " behaviors:"
406  for b in state.behavior_names:
407  print " " + b
408 
409  for next_state in state.next_states:
410  #print " next state", next_state.name
412  e.id_from = state.name
413  e.id_to = next_state.name
414  #e.addLabel('s' + str(edges_counter), '\sigma_{' + str(edges_counter) + '}')
415  #print " s_" + str(edges_counter) + " = " + next_state.init_cond
416  e.addLabel('s_{' + state.name + "," + next_state.name + "}", '\sigma_{' + latex_equations.toMathText(state.name) + "," + latex_equations.toMathText(next_state.name) + '}')
417  print " s_{" + state.name + "," + next_state.name + "} = " + next_state.init_cond
418  #edges_counter += 1
419  g.edges.append(e)
420  for behavior in subsystem_info.behaviors:
421  print " behavior: " + behavior.name
422  print " terminal_condition: " + behavior.terminal_condition
423  print " error_condition: " + behavior.error_condition
424 
425  return g
426 
428  self.state_machine_graph.exportToPdf(self.subsystem_name+"_fsm.pdf")
430 
431  def extractIdentifiers(self, s):
432  id_positions = []
433  iterating_identifier = False
434  id_begin = None
435  for i in range(len(s)):
436  if not iterating_identifier:
437  if s[i].isalpha() or s[i] == '_':
438  iterating_identifier = True
439  id_begin = i
440  else:
441  if not s[i].isalnum() and s[i] != '_':
442  iterating_identifier = False
443  ident = s[id_begin:i]
444  id_positions.append( (ident, id_begin) )
445  if iterating_identifier:
446  ident = s[id_begin:]
447  id_positions.append( (ident, id_begin) )
448  return id_positions
449 
450  def prepareUsedPredicatesInfo(self, subsystem_info):
451  # extract relevant predicates for every possible transition
452  transitions = {}
453 
454  for state in subsystem_info.state_machine:
455  term_err_cond_ids = []
456  for behavior_name in state.behavior_names:
457  for b in subsystem_info.behaviors:
458  if b.name == behavior_name:
459  id_positions = self.extractIdentifiers(b.terminal_condition)
460  for id_pos in id_positions:
461  if id_pos[0] != 'and' and id_pos[0] != 'or' and not id_pos[0] in term_err_cond_ids:
462  term_err_cond_ids.append(id_pos[0])
463  id_positions = self.extractIdentifiers(b.error_condition)
464  for id_pos in id_positions:
465  if id_pos[0] != 'and' and id_pos[0] != 'or' and not id_pos[0] in term_err_cond_ids:
466  term_err_cond_ids.append(id_pos[0])
467  break
468 
469  for next_state in state.next_states:
470  id_positions = self.extractIdentifiers(next_state.init_cond)
471  init_cond_ids = []
472  for id_pos in id_positions:
473  if id_pos[0] != 'and' and id_pos[0] != 'or' and not id_pos[0] in init_cond_ids:
474  init_cond_ids.append(id_pos[0])
475  transitions[(state.name,next_state.name)] = term_err_cond_ids + init_cond_ids
476  return transitions
477 
478  def exportStateMachineConditionsToPdf(self, subsystem_info):
479 
480  def convertToLatex(condition):
481  cond = copy.copy(condition)
482  id_positions = self.extractIdentifiers(cond)
483 
484  # calculate line breaks (on 'and' and on 'or' only)
485  max_line_len = 50
486  last_line_break = -(len(state.name) + len(next_state.name))/2
487  last_and_or = 0
488  line_breaks = []
489  for id_pos in id_positions:
490  if id_pos[0] == 'and' or id_pos[0] == 'or':
491  if id_pos[1]-last_line_break > max_line_len:
492  line_breaks.append(id_pos[1])
493  last_line_break = id_pos[1]
494  last_and_or = id_pos[1]
495 
496  for br in reversed(line_breaks):
497  cond = cond[0:br] + "\\\\" + cond[br:]
498 
499  # create length-sorted list of unique identifiers
500  ids = []
501  for id_pos in id_positions:
502  if not id_pos[0] in ids:
503  ids.append(id_pos[0])
504  ids.sort(key = lambda s: len(s), reverse=True)
505 
506  for i in range(len(ids)):
507  cond = cond.replace(ids[i], "{" + str(i) + "}")
508 
509  for i in range(len(ids)):
510  if ids[i] == "and":
511  ident = " \\wedge "
512  elif ids[i] == "or":
513  ident = " \\vee "
514  elif ids[i] == "not":
515  ident = " \\neg "
516  else:
517  ident = " \\text{" + ids[i] + "} "
518  cond = cond.replace("{" + str(i) + "}", ident)
519  return cond
520 
521  latex_formulas = []
522  file_names = []
523 
524  print "subsystem name: " + self.subsystem_name
525  for state in subsystem_info.state_machine:
526  print " state: " + state.name
527  for next_state in state.next_states:
528  init_cond = convertToLatex(next_state.init_cond)
529  init_cond = "\\sigma_{" + latex_equations.toMathText(state.name) + "," + latex_equations.toMathText(next_state.name) + "} = " + init_cond
530  latex_formulas.append(init_cond)
531  file_names.append(self.subsystem_name + "_ " + state.name + "_" + next_state.name + "_init")
532 
533  term_cond = None
534  err_cond = None
535  for behavior_name in state.behavior_names:
536  for b in subsystem_info.behaviors:
537  if b.name == behavior_name:
538  if term_cond == None:
539  term_cond = "(" + b.terminal_condition + ")"
540  else:
541  term_cond = term_cond + " or (" + b.terminal_condition + ")"
542  if err_cond == None:
543  err_cond = "(" + b.error_condition + ")"
544  else:
545  err_cond = err_cond + " or (" + b.error_condition + ")"
546  break
547 
548  term_cond = convertToLatex(term_cond)
549  term_cond = "\\tau_{" + latex_equations.toMathText(state.name) + "} = " + term_cond
550  latex_formulas.append(term_cond)
551  file_names.append(self.subsystem_name + "_ " + state.name + "_" + next_state.name + "_term")
552 
553  err_cond = convertToLatex(err_cond)
554  err_cond = "\\epsilon_{" + latex_equations.toMathText(state.name) + "} = " + err_cond
555  latex_formulas.append(err_cond)
556  file_names.append(self.subsystem_name + "_ " + state.name + "_" + next_state.name + "_err")
557 
558  with open("formulas.tex", 'w') as outfile:
559  for i in range(len(latex_formulas)):
560  outfile.write("% " + file_names[i] + "\n")
561  #outfile.write("\\begin{gather*}\n" + latex_formulas[i] + "\n\\end{gather*}\n\n")
562  outfile.write(latex_formulas[i] + "\n")
563 
564  # export to pdf
565 # eps_file_list = []
566 # for i in range(len(latex_formulas)):
567 # handle, path = tempfile.mkstemp(suffix=".eps")
568 # eps_file_list.append( (handle, path) )
569 # latex_equations.exportToEpsList(eps_file_list, latex_formulas)
570 # for i in range(len(latex_formulas)):
571 # (handle, path) = eps_file_list[i]
572 # subprocess.call(['epspdf', path, file_names[i]+".pdf"])
573 
574  def update_subsystem(self, msg):
575  for value in msg.status[1].values:
576  if value.key == 'master_component':
577  self.state = value.value
578  elif value.key[-2:] == 'Rx' or value.key[-2:] == 'Tx':
579  if value.key[0:-2] in self.all_buffers:
580  if value.value == '<data ok>':
581  self.all_buffers[value.key[:-2]].setStyleSheet("background-color: green")
582  else:
583  self.all_buffers[value.key[:-2]].setStyleSheet("background-color: red")
584  self.all_buffers[value.key[:-2]].setToolTip(value.value)
585 
586  if self.graph_generated == None and self.initialized:
587 
588  draw_unconnected = False
589 
591  for conn in self.subsystem_info.component_connections:
592  self.all_component_connections.append( (conn.component_from, conn.port_from, conn.component_to, conn.port_to) )
593 
594  self.dialogStateHistory.setUsedPredicatesInfo(self.prepareUsedPredicatesInfo(self.subsystem_info))
595  # behaviors
596 
597  behavior_graphs_list = ["<all>"]#, "<always running>"]
598  for behavior in self.subsystem_info.behaviors:
599  behavior_graphs_list.append(behavior.name)
600 
601  self.behavior_graphs = {}
602  for graph_name in behavior_graphs_list:
603  self.behavior_graphs[graph_name] = self.genBehaviorGraph(self.subsystem_info, graph_name, hide_converters=False)
604  graph_str = self.behavior_graphs[graph_name].exportToPlain()
605  self.dialogBehaviorGraph.addGraph(graph_name, graph_str)
606 
607  self.dialogBehaviorGraph.showGraph("<all>")
608 
609  # state machine (fsm)
611  graph_str = self.state_machine_graph.exportToPlain()
612  self.dialogStateMachineGraph.addGraph(graph_str)
613  self.dialogStateMachineGraph.showGraph()
614 
615  # end of state machine
616 
617 
618  self.graph_generated = True
619 
620  components_state = {}
621  for value in msg.status[0].values:
622  components_state[value.key] = value.value
623 
624  components_diag_msgs = {}
625  for value in msg.status[1].values:
626  components_diag_msgs[value.key] = value.value
627 
628  #
629  # update dialogs
630  #
631  self.dialogComponents.updateState(components_state, components_diag_msgs)
632  self.dialogBehaviorGraph.updateState(components_state)
633 
634  #rospy.wait_for_service('/' + name = '/getSubsystemInfo')
635  if self.subsystem_info == None:
636  try:
637  self._getSubsystemInfo = rospy.ServiceProxy('/' + self.subsystem_name + '/getSubsystemInfo', GetSubsystemInfo)
638  self.subsystem_info = self._getSubsystemInfo()
639  except rospy.ServiceException, e:
640  print "Service call failed: %s"%e
641 
642  # print "\n\nSUBSYSTEM NAME:",self.subsystem_name, " START OF subsystem_info\n\n"
643 
644  # if self.subsystem_info != None:
645  #print self.subsystem_info
646  # print self.subsystem_info.state_machine
647 
648  # print "\n\nSUBSYSTEM NAME:",self.subsystem_name, " END OF subsystem_info\n\n"
649 
650 
651  self.initialized = True
652 
653  mcd = subsystem_common.parseMasterComponentDiag(self.state)
654  if len(mcd.history) > 0:
655  self.SubsystemState.setText(mcd.history[0].state_name)
656  self.dialogStateHistory.updateState(mcd)
657  self.dialogStateMachineGraph.updateState(mcd)
658  self.PeriodWall.setText(str(mcd.current_period*1000.0) + 'ms')
659  self.period_histogram = mcd.period_histogram
660  else:
661  self.SubsystemState.setText("unknown")
662  self.PeriodWall.setText("unknown")
663 
664  def getCommonBuffers(self, subsystem):
665  if not self.isInitialized() or not subsystem.isInitialized():
666  return None
667  if (subsystem.subsystem_info == None) or (self.subsystem_info == None):
668  return None
669  common_buffers = None
670  for this_index in range(len(self.subsystem_info.upper_inputs)):
671  up_in = self.subsystem_info.upper_inputs[this_index]
672  for index in range(len(subsystem.subsystem_info.lower_outputs)):
673  lo_out = subsystem.subsystem_info.lower_outputs[index]
674  if up_in == lo_out:
675  if common_buffers == None:
676  common_buffers = []
677  common_buffers.append(up_in)
678 
679  for this_index in range(len(self.subsystem_info.upper_outputs)):
680  up_out = self.subsystem_info.upper_outputs[this_index]
681  for index in range(len(subsystem.subsystem_info.lower_inputs)):
682  lo_in = subsystem.subsystem_info.lower_inputs[index]
683  if up_out == lo_in:
684  if common_buffers == None:
685  common_buffers = []
686  common_buffers.append(up_out)
687  return common_buffers
688 
689  def getCommonString(self, str_list):
690  idx = 0
691  while True:
692  character = None
693  for s in str_list:
694  if idx >= len(s):
695  return s
696  if character == None:
697  character = s[idx]
698  elif character != s[idx]:
699  return s[:idx]
700  idx = idx + 1
701  return None # this is never reached
702 
703  def groupBuffers(self, buffer_list, subsystem_name):
704  if buffer_list == None or len(buffer_list) == 0:
705  print "Error in %s.groupBuffers(%s, %s): buffers list is None or empty"%(self.subsystem_name, buffer_list, subsystem_name)
706  return False
707  if subsystem_name in self.buffer_groups:
708  # TODO: remove old buffer widgets
709  return False
710  self.buffer_groups[subsystem_name] = buffer_list
711 
712  lo_in = []
713  up_in = []
714  lo_out = []
715  up_out = []
716 
717  for buf_name in buffer_list:
718  if buf_name in self.subsystem_info.lower_inputs:
719  lo_in.append(buf_name)
720  elif buf_name in self.subsystem_info.upper_inputs:
721  up_in.append(buf_name)
722  elif buf_name in self.subsystem_info.lower_outputs:
723  lo_out.append(buf_name)
724  elif buf_name in self.subsystem_info.upper_outputs:
725  up_out.append(buf_name)
726 
727  # buffer group should be either in lower part or upper part
728  if (len(lo_in) > 0 or len(lo_out) > 0) and (len(up_in) > 0 or len(up_out) > 0):
729  print "Error in %s.groupBuffers(%s, %s): mixed upper and lower buffers"%(self.subsystem_name, buffer_list, subsystem_name)
730  return False
731 
732  # get most common part of buffers' names
733  name_list = []
734  common_name = self.getCommonString(buffer_list)
735  for idx in range(len(buffer_list)):
736  name_list.append( buffer_list[idx][len(common_name):] )
737 
738  print common_name, name_list
739 
740  vbox = QVBoxLayout()
741 
742  hbox1 = QHBoxLayout()
743  hbox1.addStretch()
744  if not (common_name) in self.all_buffers:
745  self.all_buffers[common_name] = QLabel(common_name)
746  hbox1.addWidget(self.all_buffers[common_name])
747  self.all_buffers[common_name].show()
748  hbox1.addStretch()
749 
750  hbox2 = QHBoxLayout()
751  hbox2.addSpacing(20)
752  for buf_name in name_list:
753 # hbox2.addStretch()
754  if common_name+buf_name in lo_in or common_name+buf_name in up_out:
755  suffix = ' /\\'
756  else:
757  suffix = ' \\/'
758 
759  if not (common_name+buf_name) in self.all_buffers:
760  self.all_buffers[common_name+buf_name] = QPushButton(buf_name + suffix)
761  hbox2.addWidget( self.all_buffers[common_name+buf_name] )
762  self.all_buffers[common_name+buf_name].show()
763 # hbox2.addWidget( QPushButton(buf_name + suffix) )
764 # hbox2.addStretch()
765  hbox2.addSpacing(20)
766 
767 
768  if len(lo_in) > 0 or len(lo_out) > 0:
769  vbox.addLayout(hbox1)
770  vbox.addLayout(hbox2)
771  self.lower_buffers_layout.addLayout(vbox)
772  self.lower_subsystems.append(subsystem_name)
773  else:
774  vbox.addLayout(hbox2)
775  vbox.addLayout(hbox1)
776  self.upper_buffers_layout.addLayout(vbox)
777 
778  def getLowerSubsystemPosition(self, subsystem_name):
779  for i in range(len(self.lower_subsystems)):
780  if self.lower_subsystems[i] == subsystem_name:
781  return i
782  return -1
783 
785  return self.lower_subsystems
786 
787  def start(self):
788  """
789  This method needs to be called to start updating topic pane.
790  """
791 
792  def shutdown_plugin(self):
793  for topic in self._topics.values():
794  topic['info'].stop_monitoring()
795  self._timer_refresh_topics.stop()
796 
797 
798  # TODO(Enhancement) Save/Restore tree expansion state
799  def save_settings(self, plugin_settings, instance_settings):
800  header_state = self.topics_tree_widget.header().saveState()
801  instance_settings.set_value('tree_widget_header_state', header_state)
802 
803  def restore_settings(self, pluggin_settings, instance_settings):
804  if instance_settings.contains('tree_widget_header_state'):
805  header_state = instance_settings.value('tree_widget_header_state')
806  if not self.topics_tree_widget.header().restoreState(header_state):
807  rospy.logwarn("rqt_topic: Failed to restore header state.")
808 
809 
def restore_settings(self, pluggin_settings, instance_settings)
def exportStateMachineConditionsToPdf(self, subsystem_info)
def getLowerSubsystemPosition(self, subsystem_name)
def save_settings(self, plugin_settings, instance_settings)
def groupBuffers(self, buffer_list, subsystem_name)
def genStateMachineGraph(self, subsystem_info)
def extractConnectionInfo(self, conn, comp_from, comp_to)
def prepareUsedPredicatesInfo(self, subsystem_info)
def __init__(self, plugin=None, name=None)
def genBehaviorGraph(self, subsystem_info, behavior_name, hide_converters=True)