chronologic_state.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 
00037 import roslib;roslib.load_manifest('rqt_robot_monitor')
00038 import rospy
00039 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus
00040 from python_qt_binding.QtGui import QTreeWidgetItem
00041 
00042 from .inspector_window import InspectorWindow
00043 from .util_robot_monitor import Util
00044             
00045 class StatusItem(QTreeWidgetItem):
00046     """
00047     Represents a single tree node that is capable of holding children objects 
00048     of its class type.
00049     """
00050         
00051     def __init__(self, status):
00052         '''
00053         @param status: DiagnosticStatus
00054         '''
00055         super(StatusItem, self).__init__(QTreeWidgetItem.UserType)
00056     
00057         self._children_statusitems = []
00058         self.name = status.name
00059         self.level = status.level
00060         self.last_level = None
00061         self.inspector = None
00062         self.status = status # DiagnosticsStatus
00063         
00064         self.warning_id = None
00065         self.error_id = None
00066         
00067         self.setText(0, '/' + Util.get_nice_name(self.name))
00068         
00069     def get_name(self):
00070         return self.name
00071 
00072     def enable(self):
00073         if self.inspector:
00074             self.inspector.enable()
00075         
00076     def disable(self):
00077         if self.inspector:
00078             self.inspector.disable()
00079         
00080     '''
00081     Replace old status with the passed one.
00082         
00083     @param status: DiagnosticsStatus 
00084     '''
00085     def update(self, status):
00086         self.status = status
00087         
00088     def update_children(self, status_new, diag_array):
00089         """
00090         
00091         Recursive for tree node's children.
00092         Update text on treeWidgetItem, set icon on it.
00093         
00094         @param status: DiagnosticStatus
00095         @param msg: DiagnosticArray
00096         """
00097          
00098         self.status = status_new
00099 
00100         if self.inspector:
00101             self.inspector.update_status_display(self.status)
00102         
00103         children_diag_statuses = Util.get_children(self.name, diag_array)
00104 
00105         names_toplevel_local = [s.name for s in self._children_statusitems]
00106         new_statusitems = []
00107         remove = []
00108         errors = 0
00109         warnings = 0
00110         for child_diagnostic_status in children_diag_statuses:
00111             name = child_diagnostic_status.name
00112             device_name = Util.get_nice_name(child_diagnostic_status.name)
00113             headline = "%s : %s" % (child_diagnostic_status.name,
00114                                     child_diagnostic_status.message)
00115             
00116             headline_msg = ''
00117             if (child_diagnostic_status.level != DiagnosticStatus.OK):
00118                 headline_msg = Util.gen_headline_warn_or_err(child_diagnostic_status)
00119                 if (child_diagnostic_status.level == DiagnosticStatus.ERROR):
00120                     errors = errors + 1                
00121                 elif (child_diagnostic_status.level == DiagnosticStatus.WARN):
00122                     warnings = warnings + 1
00123             else:
00124                headline_msg = Util.gen_headline_status_green(child_diagnostic_status)
00125             rospy.logdebug(' update_children level= %s',
00126                            child_diagnostic_status.level)               
00127                 
00128             if name in names_toplevel_local:
00129                 index_child = names_toplevel_local.index(name)                
00130                 status_item = self._children_statusitems[ index_child ]
00131                 status_item.update_children(child_diagnostic_status, diag_array)  # Recursive call.
00132                 Util._update_status_images(child_diagnostic_status, status_item)
00133                 rospy.logdebug(' StatusItem update 33 index= %d dev_name= %s',
00134                                index_child, device_name)
00135                 # status_item.setText(0, headline)
00136                 status_item.setText(0, device_name)
00137                 status_item.setText(1, child_diagnostic_status.message)
00138             elif len(self.strip_child(name).split('/')) <= 2:
00139                 status_item = StatusItem(child_diagnostic_status)
00140                 status_item.update_children(child_diagnostic_status,
00141                                             diag_array)  # Recursive call.
00142                 # status_item.setText(0, headline)
00143                 status_item.setText(0, device_name)
00144                 status_item.setText(1, child_diagnostic_status.message)
00145                 self._children_statusitems.append(status_item)
00146                 # new_statusitems.append(status_item)
00147                 self.addChild(status_item)             
00148  
00149         rospy.logdebug(' ------ Statusitem.update_children err=%d warn=%d',
00150                        errors, warnings)
00151         return {Util._DICTKEY_TIMES_ERROR : errors, 
00152                 Util._DICTKEY_TIMES_WARN : warnings}
00153 
00154     def on_click(self):
00155         if not self.inspector:
00156             self.inspector = InspectorWindow(self.status,
00157                                              self.close_inspector_window)                        
00158         else:
00159             self.inspector.activateWindow()
00160             
00161     '''
00162     @author: Isaac Saito
00163     '''        
00164     def close_inspector_window(self):
00165         rospy.logdebug(' ------ Statusitem close_inspector_window 1')
00166         self.inspector = None
00167         
00168     def strip_child(self, child):
00169         return child.replace(self.name, '')
00170     
00171     def close(self):
00172         """
00173         Because Isaac failed to find a way to call a destructor of a class in
00174         python in general, he made this function, intending it to be called by
00175         its parent object (in this case RobotMonitorWidget's instance)
00176         every timeline when a certain node gets removed.
00177         
00178         @author: Isaac Saito
00179         """
00180         if self.inspector:
00181             # del self.inspector # Doesn't _close the window
00182             self.inspector.close()
00183             
00184         # _close children.
00185         for status_item in self._children_statusitems:
00186             status_item.close()
00187             
00188 class InstantaneousState(object):
00189     """
00190     A container for StatusItem per timeframe (second).
00191     
00192     Copied from robot_monitor.
00193     """
00194     def __init__(self):
00195         self._items = {} # dict of StatusItem
00196         self._msg = None
00197         self._has_warned_no_name = False
00198 
00199     def reset(self):
00200         self._items = {}
00201         self._msg = None
00202         
00203     def get_parent(self, item):
00204         parent_name = get_parent_name(item.status.name)
00205         
00206         if (parent_name not in self._items):
00207             return None
00208         
00209         return self._items[parent_name]
00210     
00211     def get_descendants(self, item):
00212         rospy.logdebug(' Status get_descendants status.name=%s', 
00213                        item.status.name)
00214         child_keys = [k for k in self._items.iterkeys() 
00215                       if k.startswith(item.status.name + "/")]
00216         children = [self._items[k] for k in child_keys]
00217         return children
00218     
00219     def get_items(self):
00220         return self._items
00221         
00222     def update(self, msg):
00223         """        
00224         
00225         @param msg: DiagnosticArray
00226         
00227         Copied from robot_monitor. 
00228         """
00229         
00230         removed = []
00231         added = []
00232         items = {}
00233         
00234         # fill items from new msg, creating new StatusItems for any that don't 
00235         # already exist, and keeping track of those that have been added new
00236         for s in msg.status:
00237             # DiagnosticStatus messages without a name are invalid #3806
00238             if not s.name and not self._has_warned_no_name:
00239                 rospy.logwarn('DiagnosticStatus message with no "name". ' +
00240                               'Unable to add to robot monitor. Message: ' +
00241                               '%s, hardware ID: %s, level: %d' % 
00242                               (s.message, s.hardware_id, s.level))
00243                 self._has_warned_no_name = True
00244 
00245             if not s.name:
00246                 continue
00247                 
00248             if (len(s.name) > 0 and s.name[0] != '/'):
00249                 s.name = '/' + s.name
00250 
00251             if (s.name not in self._items):
00252                 i = StatusItem(s)
00253                 added.append(i)
00254                 items[s.name] = i
00255             else:
00256                 i = self._items[s.name]
00257                 i.update(s)
00258                 items[s.name] = i
00259         
00260         # find anything without a parent already in the items, and add it as a 
00261         # dummy item
00262         to_add = []
00263         dummy_names = []
00264         for i in items.itervalues():
00265             parent = i.status.name
00266             while (len(parent) != 0):
00267                 parent = get_parent_name(parent)
00268                 if (len(parent) > 0 and 
00269                     (parent not in items) and 
00270                     parent not in dummy_names):
00271                     
00272                     pi = None
00273                     if (parent not in self._items):
00274                         s = DiagnosticStatus()
00275                         s.name = parent
00276                         s.message = ""
00277                         pi = StatusItem(s)
00278                     else:
00279                         pi = self._items[parent]
00280                         
00281                     to_add.append(pi)
00282                     dummy_names.append(pi.status.name)
00283                   
00284         for a in to_add:
00285             if (a.status.name not in items):
00286                 items[a.status.name] = a
00287                 
00288                 if (a.status.name not in self._items):
00289                     added.append(a)
00290         
00291         for i in self._items.itervalues():
00292             # determine removed items
00293             if (i.status.name not in items):
00294                 removed.append(i)
00295         
00296         # remove removed items
00297         for r in removed:
00298             del self._items[r.status.name]
00299         
00300         self._items = items
00301         self._msg = msg
00302         
00303         # sort so that parents are always added before children
00304         added.sort(cmp=lambda l,r: cmp(l.status.name, r.status.name))
00305         # sort so that children are always removed before parents
00306         removed.sort(cmp=lambda l,
00307                      r: cmp(l.status.name, r.status.name), 
00308                      reverse=True)
00309         
00310         #added_keys = [a.status.name for a in added]
00311         #if len(added_keys) > 0: print "Added: ", added_keys
00312         #removed_keys = [r.status.name for r in removed]
00313         #if (len(removed_keys) > 0): print "Removed: ", removed_keys
00314         
00315         return (added, removed, self._items)
00316             


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