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 copy
00037 import random
00038 
00039 import roslib;roslib.load_manifest('rqt_robot_monitor')
00040 import rospy
00041 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus
00042 from python_qt_binding import loadUi
00043 from python_qt_binding.QtCore import Qt, QTimer, Signal
00044 from python_qt_binding.QtGui import QIcon, QTextEdit, QWidget
00045 
00046 from .abst_status_widget import AbstractStatusWidget
00047 from .chronologic_state import InstantaneousState, StatusItem
00048 from .time_pane import TimelinePane
00049 from .util_robot_monitor import Util
00050 
00051 class RobotMonitorWidget(AbstractStatusWidget):
00052     """
00053     shutdown function needs to be called when the class terminates.
00054     
00055     RobotMonitorWidget itself doesn't store previous states. It instead
00056      delegates that function to TimelinePane class. 
00057     """
00058     
00059     _sig_clear = Signal()    
00060     _sig_tree_nodes_updated = Signal(int)
00061     _TREE_ALL = 1
00062     _TREE_WARN = 2
00063     _TREE_ERR = 3
00064 
00065     '''
00066     @param context:
00067     @param topic:  
00068     '''
00069     def __init__(self, context, topic):
00070         super(RobotMonitorWidget, self).__init__()
00071         ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),
00072                                'rqt_robot_monitor_mainwidget.ui')
00073         loadUi(ui_file, self)
00074         
00075         obj_name = 'Robot Monitor'
00076         self.setObjectName(obj_name)
00077         self.setWindowTitle(obj_name)        
00078         
00079         self._toplv_statusitems = []  # StatusItem
00080         self._warn_statusitems = []  # StatusItem. Contains ALL DEGREES 
00081                                  # (device top level, device' _sub) in parallel. 
00082         self._err_statusitems = []  # StatusItem
00083 
00084         self.tree_all_devices.itemDoubleClicked.connect(self._tree_clicked)
00085         self.warn_tree.itemDoubleClicked.connect(self._tree_clicked)
00086         self.err_tree.itemDoubleClicked.connect(self._tree_clicked)
00087         
00088         self.tree_all_devices.resizeColumnToContents(0)
00089         
00090         self._sig_clear.connect(self._clear)
00091         self._sig_tree_nodes_updated.connect(self._tree_nodes_updated)
00092 
00093         # self.tree_all_devices.sortByColumn(0, Qt.AscendingOrder) 
00094         ##      No effect. Sorting didn't get enabled.
00095         
00096         # TODO Declaring timeline pane. 
00097         #      Needs to be stashed away into .ui file but so far failed. 
00098         self.timeline_pane = TimelinePane(self, Util._SECONDS_TIMELINE, 
00099                                           self._cb, 
00100                                           self.get_color_for_value, 
00101                                           self.on_pause)
00102         self.vlayout_top.addWidget(self.timeline_pane)
00103         self.timeline_pane.show()                
00104 
00105         self.paused = False
00106         self._is_stale = False
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.num_diag_msg_received = 0  # For debug
00115         
00116         self.topic = topic
00117         self._sub = rospy.Subscriber(
00118                                     self.topic,  # name of the topic 
00119                                     DiagnosticArray,  # type of the topic
00120                                     self._cb)        
00121         
00122     def _cb(self, msg, is_forced = False):
00123         """
00124         @param msg: DiagnosticArray
00125         @param is_forced: bool. Intended for non-incoming-msg trigger 
00126                           (in particular, from child object like TimelinePane). 
00127         @author: Isaac Saito
00128         """
00129         if not self.paused and not is_forced:
00130             self.timeline_pane.new_diagnostic(msg)
00131             self._update_devices_tree(msg)       
00132             self._update_warns_errors(msg)
00133             self._on_new_message_received(msg)
00134             
00135             self._notify_statitems(msg)
00136                         
00137             rospy.logdebug('  RobotMonitorWidget _cb stamp=%s',
00138                        msg.header.stamp)
00139         elif is_forced:
00140             self._update_devices_tree(msg)       
00141             self._update_warns_errors(msg)
00142         
00143         self.num_diag_msg_received += 1
00144         rospy.logdebug('  RobotMonitorWidget _cb #%d',
00145                        self.num_diag_msg_received)
00146     
00147     def _notify_statitems(self, diag_arr):
00148         """
00149         
00150         @summary: Notify new message arrival to all existing InespectorWindow 
00151         instances that are encapsulated in StatusItem instances contained 
00152         in self._toplv_statusitems.
00153         """
00154         
00155         for statitem_new in diag_arr.status:
00156             corresp = Util.get_correspondent(statitem_new.name,
00157                                               self._toplv_statusitems)
00158             statitem_prev = corresp[Util._DICTKEY_STATITEM] 
00159             if statitem_prev and statitem_prev.inspector:
00160                 #statitem_prev.update_children(statitem_new, diag_arr)
00161                 rospy.loginfo('  RobotMonitorWidget _notify_statitems name=%s ' +
00162                               'len toplv items=%d',
00163                                statitem_new.name, len(self._toplv_statusitems))  
00164                 return
00165                        
00166     def resizeEvent(self, evt):
00167         rospy.logdebug('RobotMonitorWidget resizeEvent')
00168         self.timeline_pane.redraw()
00169                  
00170     def _tree_clicked(self, item, column):
00171         """
00172         @param item: QTreeWidgetItem
00173         @param column: int
00174         """
00175 
00176         rospy.logdebug('RobotMonitorWidget _tree_clicked col=%d', column) 
00177         item.on_click()
00178         
00179     def _update_devices_tree(self, diag_array):
00180         """
00181         Update the tree from the bottom
00182         @param diag_array: DiagnosticArray
00183         
00184         @todo: 11/5/2012 Currently, in case some devices disappear 
00185                      while running this program, there's no way to remove 
00186                      those from the device-tree.
00187         """
00188          
00189         statusnames_curr_toplevel = [Util.get_nice_name(k.name) 
00190                                      for k in self._toplv_statusitems]  
00191         # Only the k variable that pops up at the end is 
00192         # processed by Util.get_nice_name.
00193         
00194         for diagnostic_status_new in self._get_toplv_diagnosticstatus_from_new_msg(
00195                                                                    diag_array):
00196             name = Util.get_nice_name(diagnostic_status_new.name)
00197             rospy.logdebug('_update_devices_tree 0 name @ toplevel %s', name)
00198             dict_status = 0
00199             if name in statusnames_curr_toplevel:  # No change of names 
00200                                                  # in toplevel since last time. 
00201                 statusitem = self._toplv_statusitems[
00202                                         statusnames_curr_toplevel.index(name)]
00203 
00204                 dict_status = statusitem.update_children(diagnostic_status_new,
00205                                                          diag_array)
00206                 times_errors = dict_status[Util._DICTKEY_TIMES_ERROR]
00207                 times_warnings = dict_status[Util._DICTKEY_TIMES_WARN]
00208                 Util._update_status_images(diagnostic_status_new, statusitem)
00209                                                 
00210                 # TODO Update status text on each node using dict_status.
00211                 base_text = Util.gen_headline_status_green(statusitem.status)
00212                 # errwarn_text = Util.gen_headline_warn_or_err(statusitem.status)
00213  
00214                 rospy.logdebug('_update_devices_tree warn_id= %s\n\t\t\t' + 
00215                                'diagnostic_status.name = %s\n\t\t\t\t' + 
00216                                'Normal status diag_array = %s',
00217                                statusitem.warning_id,
00218                                diagnostic_status_new.name, base_text)
00219                 
00220                 if (times_errors > 0 or times_warnings > 0):                    
00221                     base_text = "(Err: %s, Wrn: %s) %s %s" % (
00222                                    times_errors,
00223                                    times_warnings,
00224                                    Util.get_nice_name(diagnostic_status_new.name),
00225                                    diagnostic_status_new.message)
00226                     rospy.logdebug('_update_dev_tree 1 text to show=%s',
00227                                    base_text)
00228                     statusitem.setText(0, base_text)
00229                     statusitem.setText(1, diagnostic_status_new.message)
00230                 else:
00231                     rospy.logdebug('_update_dev_tree 2 text to show=%s',
00232                                    base_text)
00233                     statusitem.setText(0, base_text)
00234                     statusitem.setText(1, 'OK')
00235        
00236             else:
00237                 new_status_item = StatusItem(diagnostic_status_new)
00238      
00239                 # TODO receive return value set and use them.
00240                 new_status_item.update_children(diagnostic_status_new,
00241                                                 diag_array)
00242                 
00243                 # TODO Figure out if a statusitem and 
00244                 #      its subtree contains errors.
00245                 # new_status_item.setIcon(0, self._error_icon) 
00246                 #      This shows NG icon at the beginning of each statusitem.
00247                 Util._update_status_images(diagnostic_status_new,
00248                                            new_status_item)
00249                 
00250                 self._toplv_statusitems.append(new_status_item)
00251                 
00252                 # TODO new_statusitems_toplv might not be necessary - 
00253                 #                        _toplv_statusitems might substitute.
00254                 # new_statusitems_toplv.append(new_status_item)
00255                 rospy.logdebug(' _update_devices_tree 2 ' + 
00256                                'diagnostic_status_new.name %s',
00257                                new_status_item.name)
00258                 self.tree_all_devices.addTopLevelItem(new_status_item)
00259 
00260         self._sig_tree_nodes_updated.emit(self._TREE_ALL)
00261     
00262     def _tree_nodes_updated(self, tree_type):
00263         tree_obj = None
00264         if self._TREE_ALL == tree_type:
00265             tree_obj = self.tree_all_devices
00266         elif self._TREE_WARN == tree_type:
00267             tree_obj = self.warn_tree       
00268         if self._TREE_ERR == tree_type:
00269             tree_obj = self.err_tree
00270         tree_obj.resizeColumnToContents(0)
00271             
00272     def _get_toplv_diagnosticstatus_from_new_msg(self, diag_array):
00273         """
00274         
00275         Return an array that contains DiagnosticStatus only at the top level of
00276         the given msg.
00277       
00278         @param msg: DiagnosticArray 
00279         @return array of 'status' (ie. array of DiagnosticStatus[])
00280         """
00281         
00282         ret = []
00283         for diagnostic_status in diag_array.status:
00284             if len(diagnostic_status.name.split('/')) == 2:
00285                 rospy.logdebug(" _get_toplv_diagnosticstatus_from_new_msg " + 
00286                 "TOP lev %s ", diagnostic_status.name)
00287                 ret.append(diagnostic_status)        
00288             else:
00289                 rospy.logdebug(" _get_toplv_diagnosticstatus_from_new_msg " + 
00290                                "Not top lev %s ", diagnostic_status.name)
00291         return ret
00292  
00293     """
00294     Update the warning and error trees. 
00295     
00296     Unlike _update_devices_tree function where all DiagnosticStatus need to be 
00297     inspected constantly, this function is used in a trial to reduce 
00298     unnecessary inspection of the status level for all DiagnosticStatus 
00299     contained in the incoming DiagnosticArray msg.
00300     
00301     @param msg: DiagnosticArray 
00302     """
00303     def _update_warns_errors(self, diag_array):
00304         self._update_flat_tree(diag_array, self._toplv_statusitems)
00305         # self._update_flat_tree(diag_array)
00306        
00307     def pause(self, msg):
00308         """
00309         Ignored if already being paused.
00310         
00311         @param msg: DiagnosticArray 
00312         """
00313         
00314         if not self.paused:
00315             self.paused = True
00316             self._cb(msg)
00317                             
00318     def unpause(self, msg = None):
00319         self.paused = False
00320 
00321     def on_pause(self, paused, diagnostic_arr):
00322         """
00323         Check if InspectorWindows are set. If they are, pause them.
00324         
00325         Pay attention not to confuse with _pause func.
00326         
00327         @param paused: bool
00328         @param diagnostic_arr: 
00329          
00330         Copied from robot_monitor.
00331         """
00332         
00333         if paused:
00334             self.pause(diagnostic_arr)
00335         # if (not paused and len(self._viewers) > 0):
00336         elif (len(self._toplv_statusitems) > 0):
00337             diag_array_queue = self.timeline_pane._get_diagnosticarray()
00338             statitems = []
00339             for diag_arr in diag_array_queue:
00340                 state_instant = InstantaneousState()
00341                 state_instant.update(diag_arr)
00342                 statitems.append(state_instant)
00343         
00344         for statitem_toplv in self._toplv_statusitems:
00345             if (paused):
00346                 statitem_toplv.disable()
00347             else:
00348                 statitem_toplv.enable()    
00349                 for state_instant in statitems:
00350                     all = state_instant.get_items()
00351                     if (all.has_key(statitem_toplv.get_name())):
00352                         statitem_toplv.update(
00353                                         all[statitem_toplv.get_name()].status)
00354                         
00355     def _update_flat_tree(self, diag_arr, statusitems_curr_toplevel):
00356         """
00357         
00358         Update the given flat tree (ie. tree that doesn't show children nodes - 
00359         all of its elements will be shown on top level) with 
00360         all the DiagnosticStatus instances contained in the given DiagnosticArray, 
00361         regardless of the level of the device in a device category.
00362         
00363         For both warn / error trees, StatusItem instances are newly generated.
00364     
00365         @param diag_arr: a DiagnosticArray instance.
00366         @param statusitems_curr_toplevel: list of StatusItem.
00367     
00368         @author: Isaac Saito
00369         """
00370         
00371         for diag_stat_new in diag_arr.status:
00372             # Num of loops here should be equal to the num of the top 
00373             # DiagnosticStatus item. Ex. in PR2, 9 or so.  
00374             
00375             stat_lv_new = diag_stat_new.level
00376             dev_name = diag_stat_new.name
00377             correspondent_warn_curr = Util.get_correspondent(
00378                                                   Util.get_nice_name(dev_name),
00379                                                   self._warn_statusitems)
00380             dev_index_warn_curr = correspondent_warn_curr[Util._DICTKEY_INDEX]
00381             rospy.logdebug('###_update_flat_tree dev_index_warn_curr=%s dev_name=%s',
00382                           dev_index_warn_curr, dev_name)         
00383             correspondent_err_curr = Util.get_correspondent(
00384                                                  Util.get_nice_name(dev_name),
00385                                                  self._err_statusitems)
00386             dev_index_err_curr = correspondent_err_curr[Util._DICTKEY_INDEX]            
00387             headline = "%s" % diag_stat_new.name
00388             if DiagnosticStatus.OK == stat_lv_new:
00389                 if 0 <= dev_index_warn_curr:
00390                     rospy.logdebug('#_update_flat_tree QQQ dev_index_warn_curr=%s dev_name=%s, stat_lv_new=%d',
00391                           dev_index_warn_curr, dev_name, stat_lv_new)
00392                     statitem_curr = self._get_statitem(dev_index_warn_curr,
00393                                                        self._warn_statusitems,
00394                                                        self.warn_tree, 1)
00395                     statitem_curr.warning_id = None
00396                 elif 0 <= dev_index_err_curr:
00397                     statitem_curr = self._get_statitem(dev_index_err_curr,
00398                                                        self._err_statusitems,
00399                                                        self.err_tree, 1)
00400                     statitem_curr.error_id = None
00401             elif DiagnosticStatus.WARN == stat_lv_new:
00402                 statitem = None
00403                 if 0 <= dev_index_err_curr:
00404                     # If the corresponding statusitem is in error tree,
00405                     # move it to warn tree.
00406                     statitem = self._get_statitem(dev_index_err_curr,
00407                                                   self._err_statusitems,
00408                                                   self.err_tree)
00409                     self._add_statitem(statitem, self._warn_statusitems,
00410                                       self.warn_tree, headline, 
00411                                       diag_stat_new.message, stat_lv_new)
00412                 elif (dev_index_warn_curr < 0 and dev_index_err_curr < 0):
00413                     # If the corresponding statusitem isn't found, 
00414                     # create new obj.
00415                     statitem = StatusItem(diag_stat_new)
00416                     self._add_statitem(statitem, self._warn_statusitems,
00417                                       self.warn_tree, headline,
00418                                       diag_stat_new.message, stat_lv_new)
00419                     self._warn_statusitems.append(statitem)
00420                 elif (0 < dev_index_warn_curr):
00421                     # If the corresponding statusitem is already in warn tree,
00422                     # obtain the instance.
00423                     statitem = self._get_statitem(dev_index_warn_curr,
00424                                                   self._warn_statusitems)
00425 
00426                 if statitem: # If not None
00427                     # Updating statusitem will keep popup window also update.
00428                     dict_status = statitem.update_children(diag_stat_new, 
00429                                                            diag_arr)
00430             elif ((DiagnosticStatus.ERROR == stat_lv_new) or
00431                   (DiagnosticStatus.STALE == stat_lv_new)):
00432                 statitem = None
00433                 if 0 <= dev_index_warn_curr:
00434                     # If the corresponding statusitem is in warn tree,
00435                     # move it to err tree.
00436                     statitem = self._get_statitem(dev_index_warn_curr,
00437                                                   self._warn_statusitems,
00438                                                   self.warn_tree)
00439                     self._add_statitem(statitem, self._err_statusitems,
00440                                       self.err_tree, headline,
00441                                       diag_stat_new.message, stat_lv_new)
00442                 elif (dev_index_warn_curr < 0 and dev_index_err_curr < 0):
00443                     # If the corresponding statusitem isn't found, 
00444                     # create new obj.
00445                     statitem = StatusItem(diag_stat_new)
00446                     self._add_statitem(statitem, self._err_statusitems,
00447                                       self.err_tree, 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 
00455                 if statitem: # If not None
00456                     # Updating statusitem will keep popup window also update.
00457                     dict_status = statitem.update_children(diag_stat_new, 
00458                                                            diag_arr)                    
00459         
00460         self._sig_tree_nodes_updated.emit(self._TREE_WARN)
00461         self._sig_tree_nodes_updated.emit(self._TREE_ERR)
00462         
00463     '''
00464     @author: Isaac Saito
00465     '''
00466     def _add_statitem(self, statusitem, statitem_list,
00467                       tree, headline, statusmsg, statlevel):
00468         
00469         if 'Warning' == statusmsg or 'Error' == statusmsg:
00470             return 
00471         
00472         statusitem.setText(0, headline)
00473         statusitem.setText(1, statusmsg)
00474         statusitem.setIcon(0, Util._IMG_DICT[statlevel])
00475         statitem_list.append(statusitem)                
00476         tree.addTopLevelItem(statusitem)
00477         rospy.logdebug(' _add_statitem statitem_list length=%d',
00478                        len(statitem_list))
00479            
00480     def _get_statitem(self, item_index, item_list, tree = None, mode = 2):
00481         """
00482         @param mode: 1 = remove from given list, 2 = w/o removing.
00483         @author: Isaac Saito
00484         """
00485         statitem_existing = item_list[item_index]
00486         if 1 == mode:
00487             tree.takeTopLevelItem(tree.indexOfTopLevelItem(statitem_existing))
00488             item_list.pop(item_index)
00489         #elif 2 == mode:
00490         return statitem_existing
00491 
00492     def _on_new_message_received(self, msg):
00493         """
00494         Copied from robot_monitor
00495     
00496         \brief Called whenever a new message is received by the timeline.  
00497         Different from new_message in that it
00498         is called even if the timeline is paused, and only when a new message is 
00499         received, not when the timeline is scrubbed
00500         """    
00501         self.last_message_time = rospy.get_time()
00502 
00503     def _update_message_state(self):
00504         """
00505         @author: Isaac Saito (ported from robot_monitor)
00506         """
00507     
00508         current_time = rospy.get_time()
00509         time_diff = current_time - self.last_message_time
00510         rospy.logdebug('_update_message_state time_diff= %s ' + 
00511                        'self.last_message_time=%s', time_diff,
00512                        self.last_message_time)
00513         if (time_diff > 10.0):
00514             self.timeline_pane._msg_label.setText("Last message received " + 
00515                                                "%s seconds ago"
00516                                                % (int(time_diff)))
00517             self._is_stale = True
00518         else:
00519             seconds_string = "seconds"
00520             if (int(time_diff) == 1):
00521                 seconds_string = "second"
00522             self.timeline_pane._msg_label.setText(
00523                  "Last message received %s %s ago" % (int(time_diff),
00524                                                       seconds_string))
00525             self._is_stale = False        
00526             
00527     # 11/19/2012/Isaac _close will be deleted.        
00528 #    def _close(self):  # 10/24/Isaac/When this is called?
00529 #        rospy.logdebug('RobotMonitorWidget in _close')
00530 #        if self._sub:
00531 #            self._sub.unregister()
00532 #            self._sub = None
00533 
00534     '''
00535     This needs to be called whenever this class terminates.
00536     '''
00537     def shutdown(self):
00538         rospy.logdebug('RobotMonitorWidget in shutdown')
00539         # Close all StatusItem (and each associated InspectWidget)        
00540         # self.tree_all_devices.clear()  # Doesn't work for the purpose 
00541                                          # (inspector windows don't get closed)
00542               
00543         for item in self._err_statusitems:
00544             item.close()
00545         for item in self._warn_statusitems:
00546             item.close()
00547         for item in self._toplv_statusitems:
00548             item.close()
00549 
00550         # unsubscribe from Topics
00551         self._sub.unregister()
00552 
00553         # stop timers 
00554         self._timer.stop()
00555         del self._timer
00556 
00557     def get_color_for_value(self, queue_diagnostic, color_index):
00558         """
00559         Taken from robot_monitor
00560         
00561         Overridden.
00562         
00563         @param color_index: int 
00564         """
00565         len_q = len(queue_diagnostic)
00566         rospy.logdebug(' get_color_for_value color_index=%d len_q=%d',
00567                       color_index, len_q)
00568         # if (color_index == 1 and len_q == 0): #TODO Needs to be reverted back.
00569         if (color_index <= 2 and len_q == 0):  # TODO This line is Debug only
00570             return QColor('grey')
00571         return Util._get_color_for_message(queue_diagnostic[color_index - 1])
00572                                # When _queue_diagnostic is empty,
00573                                # this yield error when color_index > 0.   
00574                 
00575 
00576     def save_settings(self, plugin_settings, instance_settings):
00577         instance_settings.set_value('splitter', self.splitter.saveState())
00578 
00579     def restore_settings(self, plugin_settings, instance_settings):
00580         if instance_settings.contains('splitter'):
00581             self.splitter.restoreState(instance_settings.value('splitter'))
00582         else:
00583             self.splitter.setSizes([100, 100, 200])
00584 
00585     def _clear(self):
00586         rospy.logdebug(' RobotMonitorWidget _clear called ')
00587         self.err_tree.clear()
00588         self.warn_tree.clear()


rqt_robot_monitor
Author(s): Isaac Saito, Ze'ev Klapow
autogenerated on Fri Jan 3 2014 11:56:17