robot_monitor.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2012, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
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
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 #
00033 # Author: Isaac Saito, Ze'ev Klapow
00034 
00035 import os
00036 import rospkg
00037 
00038 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus
00039 from python_qt_binding import loadUi
00040 from python_qt_binding.QtCore import QTimer, Signal, Qt
00041 from python_qt_binding.QtGui import QColor, QPalette
00042 import rospy
00043 
00044 from .abst_status_widget import AbstractStatusWidget
00045 from .chronologic_state import InstantaneousState, StatusItem
00046 from .time_pane import TimelinePane
00047 from .util_robot_monitor import Util
00048 
00049 
00050 class RobotMonitorWidget(AbstractStatusWidget):
00051     """
00052     NOTE: RobotMonitorWidget.shutdown function needs to be called
00053     when the instance of this class terminates.
00054 
00055     RobotMonitorWidget itself doesn't store previous diagnostic states.
00056     It instead delegates that function to TimelinePane class.
00057     """
00058 
00059     _sig_tree_nodes_updated = Signal(int)
00060     _sig_new_diagnostic = Signal(DiagnosticArray)
00061     _TREE_ALL = 1
00062     _TREE_WARN = 2
00063     _TREE_ERR = 3
00064 
00065     def __init__(self, context, topic):
00066         """
00067         :param context: plugin context hook to enable adding widgets as a
00068                         ROS_GUI pane, 'PluginContext'
00069         :param topic: Diagnostic topic to subscribe to 'str'
00070         """
00071 
00072         super(RobotMonitorWidget, self).__init__()
00073         rp = rospkg.RosPack()
00074         ui_file = os.path.join(rp.get_path('rqt_robot_monitor'), 'resource',
00075                                'robotmonitor_mainwidget.ui')
00076         loadUi(ui_file, self, {'TimelinePane': TimelinePane})
00077         #loadUi(ui_file, self)
00078 
00079         obj_name = 'Robot Monitor'
00080         self.setObjectName(obj_name)
00081         self.setWindowTitle(obj_name)
00082 
00083         self._toplevel_statitems = []  # StatusItem
00084         self._warn_statusitems = []  # StatusItem. Contains ALL DEGREES
00085                                 # (device top level, device' _sub) in parallel
00086         self._err_statusitems = []  # StatusItem
00087 
00088         # TODO: Declaring timeline pane.
00089         #      Needs to be stashed away into .ui file but so far failed.
00090         self.timeline_pane.set_timeline_data(Util.SECONDS_TIMELINE,
00091                                              self.get_color_for_value,
00092                                              self.on_pause)
00093 
00094         self.tree_all_devices.itemDoubleClicked.connect(self._tree_clicked)
00095         self.warn_flattree.itemDoubleClicked.connect(self._tree_clicked)
00096         self.err_flattree.itemDoubleClicked.connect(self._tree_clicked)
00097 
00098         self.tree_all_devices.resizeColumnToContents(0)
00099 
00100         self._sig_tree_nodes_updated.connect(self._tree_nodes_updated)
00101 
00102         self.vlayout_top.addWidget(self.timeline_pane)
00103         self.timeline_pane.show()
00104 
00105         self._paused = False
00106         self._is_stale = None
00107         self._last_message_time = 0.0
00108 
00109         self._timer = QTimer()
00110         # self._timer.timerEvent.connect(self._update_message_state)
00111         self._timer.timeout.connect(self._update_message_state)
00112         self._timer.start(1000)
00113 
00114         self._sub = rospy.Subscriber(
00115                                     topic,  # name of the topic
00116                                     DiagnosticArray,  # type of the topic
00117                                     self._cb)
00118 
00119         self._sig_new_diagnostic.connect(self.new_diagnostic)
00120         self._original_base_color = self.tree_all_devices.palette().base().color()
00121         self._original_alt_base_color = self.tree_all_devices.palette().alternateBase().color()
00122 
00123     def _cb(self, msg):
00124         """
00125         Intended to be called from non-Qt thread,
00126         ie. ROS Subscriber in particular.
00127 
00128         :type msg: DiagnosticArray
00129         """
00130 
00131         # Directly calling callback function 'new_diagnostic' here results in
00132         # segfaults.
00133         self._sig_new_diagnostic.emit(msg)
00134 
00135     def new_diagnostic(self, msg, is_forced=False):
00136         """
00137         Overridden from AbstractStatusWidget.
00138 
00139         When monitoring not paused, this public method updates all the
00140         treewidgets contained in this class, and also notifies the StatusItem
00141         instances that are stored in the all-device-tree, which eventually
00142         updates the InspectorWindows in them.
00143 
00144         :type msg: DiagnosticArray
00145         :param is_forced: Intended for non-incoming-msg trigger
00146          (in particular, from child object like TimelinePane).
00147         @author: Isaac Saito
00148          """
00149         if not self._paused and not is_forced:
00150             self.timeline_pane.new_diagnostic(msg)
00151             self._update_devices_tree(msg)
00152             self._update_warns_errors(msg)
00153             self._on_new_message_received(msg)
00154 
00155             self._notify_statitems(msg)
00156 
00157             rospy.logdebug('  RobotMonitorWidget _cb stamp=%s',
00158                        msg.header.stamp)
00159         elif is_forced:
00160             self._update_devices_tree(msg)
00161             self._update_warns_errors(msg)
00162 
00163     def _notify_statitems(self, diag_arr):
00164         """
00165         Notify new message arrival to all existing InespectorWindow instances
00166         that are encapsulated in StatusItem instances contained in
00167         self._toplevel_statitems.
00168         """
00169 
00170         for statitem_new in diag_arr.status:
00171             corresp = Util.get_correspondent(statitem_new.name,
00172                                              self._toplevel_statitems)
00173             statitem_prev = corresp[Util._DICTKEY_STATITEM]
00174             if statitem_prev and statitem_prev.inspector:
00175                 rospy.logdebug('  RobotMonitorWidget _notify_statitems ' +
00176                                'name=%s len toplv items=%d',
00177                               statitem_new.name, len(self._toplevel_statitems))
00178                 return
00179 
00180     def resizeEvent(self, evt):
00181         """Overridden from QWidget"""
00182         rospy.logdebug('RobotMonitorWidget resizeEvent')
00183         self.timeline_pane.redraw()
00184 
00185     def _tree_clicked(self, item, column):
00186         """
00187         Slot to QTreeWidget.itemDoubleClicked
00188 
00189         :type item: QTreeWidgetItem
00190         :type column: int
00191         """
00192         rospy.logdebug('RobotMonitorWidget _tree_clicked col=%d', column)
00193         item.on_click()
00194 
00195     def _update_devices_tree(self, diag_array):
00196         """
00197         Update the tree from the bottom
00198 
00199         :type diag_array: DiagnosticArray
00200         """
00201         # TODO: 11/5/2012 Currently, in case some devices disappear
00202         #            while running this program, there's no way to remove
00203         #            those from the device-tree.
00204 
00205         statusnames_curr_toplevel = [Util.get_grn_resource_name(status.name)
00206                                      for status in self._toplevel_statitems]
00207         # Only the status variable that pops up at the end is
00208         # processed by Util.get_grn_resource_name.
00209 
00210         for status_new in self._get_toplevel_diagnosticstat(diag_array):
00211             name = Util.get_grn_resource_name(status_new.name)
00212             rospy.logdebug('_update_devices_tree 0 name @ toplevel %s', name)
00213             dict_status = 0
00214             if name in statusnames_curr_toplevel:  # No change of names
00215                                                 # in toplevel since last time.
00216                 statusitem = self._toplevel_statitems[
00217                                         statusnames_curr_toplevel.index(name)]
00218 
00219                 dict_status = statusitem.update_children(status_new,
00220                                                          diag_array)
00221                 times_errors = dict_status[Util._DICTKEY_TIMES_ERROR]
00222                 times_warnings = dict_status[Util._DICTKEY_TIMES_WARN]
00223                 Util.update_status_images(status_new, statusitem)
00224 
00225                 # TODO: Update status text on each node using dict_status.
00226                 base_text = Util.gen_headline_status_green(statusitem.status)
00227 
00228                 rospy.logdebug('_update_devices_tree warn_id= %s\n\t\t\t' +
00229                                'diagnostic_status.name = %s\n\t\t\t\t' +
00230                                'Normal status diag_array = %s',
00231                                statusitem.warning_id,
00232                                status_new.name, base_text)
00233 
00234                 if (times_errors > 0 or times_warnings > 0):
00235                     base_text = "(Err: %s, Wrn: %s) %s %s" % (
00236                                    times_errors,
00237                                    times_warnings,
00238                                    Util.get_grn_resource_name(status_new.name),
00239                                    status_new.message)
00240                     rospy.logdebug('_update_dev_tree 1 text to show=%s',
00241                                    base_text)
00242                     statusitem.setText(0, base_text)
00243                     statusitem.setText(1, status_new.message)
00244                 else:
00245                     rospy.logdebug('_update_dev_tree 2 text to show=%s',
00246                                    base_text)
00247                     statusitem.setText(0, base_text)
00248                     statusitem.setText(1, 'OK')
00249 
00250             else:
00251                 new_status_item = StatusItem(status_new)
00252 
00253                 new_status_item.update_children(status_new,
00254                                                 diag_array)
00255 
00256                 # Figure out if a statusitem and its subtree contains errors.
00257                 # new_status_item.setIcon(0, self._error_icon)
00258                 # This shows NG icon at the beginning of each statusitem.
00259                 Util.update_status_images(status_new,
00260                                            new_status_item)
00261 
00262                 self._toplevel_statitems.append(new_status_item)
00263 
00264                 rospy.logdebug(' _update_devices_tree 2 ' +
00265                                'status_new.name %s',
00266                                new_status_item.name)
00267                 self.tree_all_devices.addTopLevelItem(new_status_item)
00268 
00269         self._sig_tree_nodes_updated.emit(self._TREE_ALL)
00270 
00271     def _tree_nodes_updated(self, tree_type):
00272         tree_obj = None
00273         if self._TREE_ALL == tree_type:
00274             tree_obj = self.tree_all_devices
00275         elif self._TREE_WARN == tree_type:
00276             tree_obj = self.warn_flattree
00277         if self._TREE_ERR == tree_type:
00278             tree_obj = self.err_flattree
00279         tree_obj.resizeColumnToContents(0)
00280 
00281     def _get_toplevel_diagnosticstat(self, diag_array):
00282         """
00283         Return an array that contains DiagnosticStatus only at the top level of
00284         the given msg.
00285 
00286         :type msg: DiagnosticArray
00287         :rtype: DiagnosticStatus[]
00288         """
00289 
00290         ret = []
00291         for diagnostic_status in diag_array.status:
00292             if len(diagnostic_status.name.split('/')) == 2:
00293                 rospy.logdebug(" _get_toplevel_diagnosticstat " +
00294                 "TOP lev %s ", diagnostic_status.name)
00295                 ret.append(diagnostic_status)
00296             else:
00297                 rospy.logdebug(" _get_toplevel_diagnosticstat " +
00298                                "Not top lev %s ", diagnostic_status.name)
00299         return ret
00300 
00301     def _update_warns_errors(self, diag_array):
00302         """
00303         Update the warning and error trees.
00304 
00305         Unlike _update_devices_tree function where all DiagnosticStatus
00306         need to be inspected constantly, this function is used in a trial
00307         to reduce unnecessary inspection of the status level for all
00308         DiagnosticStatus contained in the incoming DiagnosticArray msg.
00309 
00310         :type msg: DiagnosticArray
00311         """
00312 
00313         self._update_flat_tree(diag_array)
00314 
00315     def pause(self, msg):
00316         """
00317         Do nothing if already being _paused.
00318 
00319         :type msg: DiagnosticArray
00320         """
00321 
00322         if not self._paused:
00323             self._paused = True
00324             self.new_diagnostic(msg)
00325 
00326     def unpause(self, msg=None):
00327         """
00328         :type msg: DiagnosticArray
00329         """
00330 
00331         self._paused = False
00332 
00333     def on_pause(self, paused, diagnostic_arr):
00334         """
00335         Check if InspectorWindows are set. If they are, pause them.
00336 
00337         Pay attention not to confuse with RobotMonitorWidget.pause.
00338 
00339         :type paused: bool
00340         :type diagnostic_arr: DiagnosticArray
00341         """
00342 
00343         if paused:
00344             self.pause(diagnostic_arr)
00345         elif (len(self._toplevel_statitems) > 0):
00346             diag_array_queue = self.timeline_pane._get_diagnosticarray()
00347             statitems = []
00348             for diag_arr in diag_array_queue:
00349                 state_instant = InstantaneousState()
00350                 state_instant.update(diag_arr)
00351                 statitems.append(state_instant)
00352 
00353         for statitem_toplv in self._toplevel_statitems:
00354             if (paused):
00355                 statitem_toplv.disable()
00356             else:
00357                 statitem_toplv.enable()
00358                 for state_instant in statitems:
00359                     items = state_instant.get_items()
00360                     if statitem_toplv.get_name() in items:
00361                         statitem_toplv.update(
00362                                        items[statitem_toplv.get_name()].status)
00363 
00364     def _update_flat_tree(self, diag_arr):
00365         """
00366         Update the given flat tree (ie. tree that doesn't show children nodes -
00367         all of its elements will be shown on top level) with
00368         all the DiagnosticStatus instances contained in the given
00369         DiagnosticArray, regardless of the level of the device in a device
00370         category.
00371 
00372         For both warn / error trees, StatusItem instances are newly generated.
00373 
00374         :type diag_arr: DiagnosticArray
00375         """
00376 
00377         for diag_stat_new in diag_arr.status:
00378             # Num of loops here should be equal to the num of the top
00379             # DiagnosticStatus item. Ex. in PR2, 9 or so.
00380 
00381             stat_lv_new = diag_stat_new.level
00382             dev_name = diag_stat_new.name
00383             correspondent_warn_curr = Util.get_correspondent(
00384                                          Util.get_grn_resource_name(dev_name),
00385                                          self._warn_statusitems)
00386             dev_index_warn_curr = correspondent_warn_curr[Util._DICTKEY_INDEX]
00387             rospy.logdebug(' dev_index_warn_curr=%s dev_name=%s',
00388                            dev_index_warn_curr, dev_name)
00389             correspondent_err_curr = Util.get_correspondent(
00390                                           Util.get_grn_resource_name(dev_name),
00391                                           self._err_statusitems)
00392             dev_index_err_curr = correspondent_err_curr[Util._DICTKEY_INDEX]
00393             headline = "%s" % diag_stat_new.name
00394             if DiagnosticStatus.OK == stat_lv_new:
00395                 if 0 <= dev_index_warn_curr:
00396                     rospy.logdebug('dev_index_warn_curr=%s name=%s, ' +
00397                                    'stat_lv_new=%d', dev_index_warn_curr,
00398                                    dev_name, stat_lv_new)
00399                     statitem_curr = self._get_statitem(dev_index_warn_curr,
00400                                                        self._warn_statusitems,
00401                                                        self.warn_flattree, 1)
00402                     statitem_curr.warning_id = None
00403                 elif 0 <= dev_index_err_curr:
00404                     statitem_curr = self._get_statitem(dev_index_err_curr,
00405                                                        self._err_statusitems,
00406                                                        self.err_flattree, 1)
00407                     statitem_curr.error_id = None
00408             elif DiagnosticStatus.WARN == stat_lv_new:
00409                 statitem = None
00410                 if 0 <= dev_index_err_curr:
00411                     # If the corresponding statusitem is in error tree,
00412                     # move it to warn tree.
00413                     statitem = self._get_statitem(dev_index_err_curr,
00414                                                   self._err_statusitems,
00415                                                   self.err_flattree)
00416                     self._add_statitem(statitem, self._warn_statusitems,
00417                                       self.warn_flattree, headline,
00418                                       diag_stat_new.message, stat_lv_new)
00419                 elif (dev_index_warn_curr < 0 and dev_index_err_curr < 0):
00420                     # If the corresponding statusitem isn't found,
00421                     # create new obj.
00422                     statitem = StatusItem(diag_stat_new)
00423                     self._add_statitem(statitem, self._warn_statusitems,
00424                                       self.warn_flattree, headline,
00425                                       diag_stat_new.message, stat_lv_new)
00426                     self._warn_statusitems.append(statitem)
00427                 elif (0 < dev_index_warn_curr):
00428                     # If the corresponding statusitem is already in warn tree,
00429                     # obtain the instance.
00430                     statitem = self._get_statitem(dev_index_warn_curr,
00431                                                   self._warn_statusitems)
00432 
00433                 if statitem:  # If not None
00434                     # Updating statusitem will keep popup window also update.
00435                     statitem.update_children(diag_stat_new,
00436                                                            diag_arr)
00437             elif ((DiagnosticStatus.ERROR == stat_lv_new) or
00438                   (DiagnosticStatus.STALE == stat_lv_new)):
00439                 statitem = None
00440                 if 0 <= dev_index_warn_curr:
00441                     # If the corresponding statusitem is in warn tree,
00442                     # move it to err tree.
00443                     statitem = self._get_statitem(dev_index_warn_curr,
00444                                                   self._warn_statusitems,
00445                                                   self.warn_flattree)
00446                     self._add_statitem(statitem, self._err_statusitems,
00447                                       self.err_flattree, headline,
00448                                       diag_stat_new.message, stat_lv_new)
00449                 elif (0 <= dev_index_err_curr):
00450                     # If the corresponding statusitem is already in err tree,
00451                     # obtain the instance.
00452                     statitem = self._get_statitem(dev_index_err_curr,
00453                                                   self._err_statusitems)
00454                 elif (dev_index_warn_curr < 0 and dev_index_err_curr < 0):
00455                     # If the corresponding statusitem isn't found,
00456                     # create new obj.
00457                     statitem = StatusItem(diag_stat_new)
00458                     self._add_statitem(statitem, self._err_statusitems,
00459                                       self.err_flattree, headline,
00460                                       diag_stat_new.message, stat_lv_new)
00461 
00462                 if statitem:  # If not None
00463                     # Updating statusitem will keep popup window also update.
00464                     statitem.update_children(diag_stat_new,
00465                                                            diag_arr)
00466 
00467         self._sig_tree_nodes_updated.emit(self._TREE_WARN)
00468         self._sig_tree_nodes_updated.emit(self._TREE_ERR)
00469 
00470     def _add_statitem(self, statusitem, statitem_list,
00471                       tree, headline, statusmsg, statlevel):
00472 
00473         if 'Warning' == statusmsg or 'Error' == statusmsg:
00474             return
00475 
00476         statusitem.setText(0, headline)
00477         statusitem.setText(1, statusmsg)
00478         statusitem.setIcon(0, Util.IMG_DICT[statlevel])
00479         statitem_list.append(statusitem)
00480         tree.addTopLevelItem(statusitem)
00481         rospy.logdebug(' _add_statitem statitem_list length=%d',
00482                        len(statitem_list))
00483 
00484     def _get_statitem(self, item_index, item_list, tree=None, mode=2):
00485         """
00486         :param mode: 1 = remove from given list, 2 = w/o removing.
00487         """
00488         statitem_existing = item_list[item_index]
00489         if 1 == mode:
00490             tree.takeTopLevelItem(tree.indexOfTopLevelItem(statitem_existing))
00491             item_list.pop(item_index)
00492         return statitem_existing
00493 
00494     def _on_new_message_received(self, msg):
00495         """
00496         Called whenever a new message is received by the timeline.
00497         Different from new_message in that it is called even if the timeline is
00498         _paused, and only when a new message is received, not when the timeline
00499         is scrubbed
00500         """
00501         self._last_message_time = rospy.get_time()
00502 
00503     def _update_message_state(self):
00504 
00505         current_time = rospy.get_time()
00506         time_diff = current_time - self._last_message_time
00507         rospy.logdebug('_update_message_state time_diff= %s ' +
00508                        'self._last_message_time=%s', time_diff,
00509                        self._last_message_time)
00510 
00511         previous_stale_state = self._is_stale
00512         if (time_diff > 10.0):
00513             self.timeline_pane._msg_label.setText("Last message received " +
00514                                                "%s seconds ago"
00515                                                % (int(time_diff)))
00516             self._is_stale = True
00517         else:
00518             seconds_string = "seconds"
00519             if (int(time_diff) == 1):
00520                 seconds_string = "second"
00521             self.timeline_pane._msg_label.setText(
00522                  "Last message received %s %s ago" % (int(time_diff),
00523                                                       seconds_string))
00524             self._is_stale = False
00525         if previous_stale_state != self._is_stale:
00526             self._update_background_color()
00527 
00528     def _update_background_color(self):
00529         p = self.tree_all_devices.palette()
00530         if self._is_stale:
00531             p.setColor(QPalette.Base, Qt.darkGray)
00532             p.setColor(QPalette.AlternateBase, Qt.lightGray)
00533         else:
00534             p.setColor(QPalette.Base, self._original_base_color)
00535             p.setColor(QPalette.AlternateBase, self._original_alt_base_color)
00536         self.tree_all_devices.setPalette(p)
00537         self.warn_flattree.setPalette(p)
00538         self.err_flattree.setPalette(p)
00539 
00540     def shutdown(self):
00541         """
00542         This needs to be called whenever this class terminates.
00543         This closes all the instances on all trees.
00544         Also unregisters ROS' subscriber, stops timer.
00545         """
00546 
00547         rospy.logdebug('RobotMonitorWidget in shutdown')
00548         # Close all StatusItem (and each associated InspectWidget)
00549         # self.tree_all_devices.clear()  # Doesn't work for the purpose
00550                                         # (inspector windows don't get closed)
00551         for item in self._err_statusitems:
00552             item.close()
00553         for item in self._warn_statusitems:
00554             item.close()
00555         for item in self._toplevel_statitems:
00556             item.close()
00557 
00558         self._sub.unregister()
00559 
00560         self._timer.stop()
00561         del self._timer
00562 
00563     def save_settings(self, plugin_settings, instance_settings):
00564         instance_settings.set_value('splitter', self.splitter.saveState())
00565 
00566     def restore_settings(self, plugin_settings, instance_settings):
00567         if instance_settings.contains('splitter'):
00568             self.splitter.restoreState(instance_settings.value('splitter'))
00569         else:
00570             self.splitter.setSizes([100, 100, 200])
00571 
00572     def _clear(self):
00573         rospy.logdebug(' RobotMonitorWidget _clear called ')
00574         self.err_flattree.clear()
00575         self.warn_flattree.clear()
00576 
00577     def get_color_for_value(self, queue_diagnostic, color_index):
00578         """
00579         Overridden from AbstractStatusWidget.
00580 
00581         :type color_index: int
00582         """
00583 
00584         len_q = len(queue_diagnostic)
00585         rospy.logdebug(' get_color_for_value color_index=%d len_q=%d',
00586                       color_index, len_q)
00587         if (color_index == 1 and len_q == 0):
00588             # TODO: Needs to be reverted back
00589             return QColor('grey')
00590         return Util._get_color_for_message(queue_diagnostic[color_index - 1])
00591         # When _queue_diagnostic is empty,
00592         # this yield error when color_index > 0.


rqt_robot_monitor
Author(s): Isaac Saito, Ze'ev Klapow, Kevin Watts, Josh Faust
autogenerated on Mon Oct 6 2014 07:16:09