hz_monitor.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
00004 #
00005 # Licensed under the Apache License, Version 2.0 (the "License");
00006 # you may not use this file except in compliance with the License.
00007 # You may obtain a copy of the License at
00008 #
00009 #   http://www.apache.org/licenses/LICENSE-2.0
00010 #
00011 # Unless required by applicable law or agreed to in writing, software
00012 # distributed under the License is distributed on an "AS IS" BASIS,
00013 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014 # See the License for the specific language governing permissions and
00015 # limitations under the License.
00016 
00017 
00018 import sys
00019 
00020 import rospy
00021 import rostopic
00022 import copy
00023 
00024 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus, KeyValue
00025 
00026 class HzTest():
00027     def __init__(self):
00028         rospy.init_node("hz_monitor")
00029 
00030         # get parameters
00031         try:
00032             # topic to test
00033             self.topics = rospy.get_param('~topics')
00034             # expected publishing rate
00035             self.hz = rospy.get_param('~hz', None)
00036             # margin of error allowed
00037             self.hzerror = rospy.get_param('~hzerror', None)
00038             # length of test
00039             self.window_size = float(rospy.get_param('~window_size', 100))
00040             # name for diagnostic message
00041             self.diagnostics_name = rospy.get_param('~diagnostics_name')
00042             self.diagnostics_name = self.diagnostics_name.replace('/','_')
00043             # sampling rate
00044             self.sampling_rate = rospy.get_param('~sampling_rate', 1)
00045             if self.hz < 2*self.sampling_rate:
00046                 rospy.logwarn("sampling_rate is probably to low: desired hz rate should at least be double the sampling_rate. (desired hz rate: %.2f, sampling_rate: %.2f)",self.hz, self.sampling_rate)
00047         except KeyError as e:
00048             rospy.logerr('hztest not initialized properly. Parameter [%s] not set. debug[%s] debug[%s]'%(str(e), rospy.get_caller_id(), rospy.resolve_name(e.args[0])))
00049             sys.exit(1)
00050 
00051         self.pub_diagnostics = rospy.Publisher('~diagnostics', DiagnosticArray, queue_size = 1)
00052         self.missing_topics = copy.deepcopy(self.topics)
00053 
00054     def run(self):
00055         r = rospy.Rate(self.sampling_rate)
00056 
00057         # wait for first message
00058         while not rospy.is_shutdown():
00059             for topic in self.topics:
00060                 msg_class, real_topic, _ = rostopic.get_topic_class(topic, blocking=False) #pause hz until topic is published
00061                 if real_topic:
00062                     if real_topic in self.missing_topics:
00063                         self.missing_topics.remove(real_topic)
00064             if len(self.missing_topics) == 0:
00065                 break
00066             rospy.loginfo("hz monitor is waiting for type of topics %s."%str(self.missing_topics))
00067             self.publish_diagnostics()
00068             
00069             try:
00070                 r.sleep()
00071             except rospy.exceptions.ROSInterruptException as e:
00072                 pass
00073 
00074         # call rostopic hz
00075         #rt = rostopic.ROSTopicHz(self.window_size)
00076         rt_HZ_store = []
00077         for topic in self.topics:
00078             rt = rostopic.ROSTopicHz(self.window_size)
00079             rospy.Subscriber(topic, rospy.AnyMsg, rt.callback_hz)
00080             rt_HZ_store.append(rt)
00081             rospy.loginfo("subscribed to [%s]"%topic)
00082 
00083         # publish diagnostics continuously
00084         while not rospy.is_shutdown():
00085             #rt.print_hz() # taken from 'rostopic hz' (/opt/ros/indigo/lib/python2.7/dist-packages/rostopic/__init__.py)
00086             self.publish_diagnostics(rt_HZ_store)
00087             try:
00088                 r.sleep()
00089             except rospy.exceptions.ROSInterruptException as e:
00090                 pass
00091 
00092     def publish_diagnostics(self, rt_HZ_store = []):
00093         # set desired rates
00094         if self.hzerror:
00095             if isinstance(self.hzerror, float) or isinstance(self.hzerror, int):
00096                 min_rate = self.hz - self.hzerror
00097                 max_rate = self.hz + self.hzerror
00098             else:
00099                 rospy.logerr("hzerror not float or int")
00100                 sys.exit(1)
00101         else:
00102             min_rate = None
00103             max_rate = None
00104 
00105         # create diagnostic message
00106         array = DiagnosticArray()
00107         array.header.stamp = rospy.Time.now()
00108         hz_status = DiagnosticStatus()
00109         hz_status.name = self.diagnostics_name
00110         hz_status.values.append(KeyValue("topics", str(self.topics)))
00111         hz_status.values.append(KeyValue("desired_rate", str(self.hz)))
00112         hz_status.values.append(KeyValue("min_rate", str(min_rate)))
00113         hz_status.values.append(KeyValue("max_rate", str(max_rate)))
00114         hz_status.values.append(KeyValue("window_size", str(self.window_size)))
00115         publishing_rate_error = False
00116         rates = []
00117         
00118         consolidated_error_messages = {} ## store and display consolidated erros messages for all the topics
00119         consolidated_error_messages.setdefault("never received message for topics", [])
00120         consolidated_error_messages.setdefault("no messages anymore for topics", [])
00121         consolidated_error_messages.setdefault("publishing rate is too low for topics", [])
00122         consolidated_error_messages.setdefault("publishing rate is too high for topics", [])
00123 
00124         if len(rt_HZ_store) != len(self.topics):
00125             hz_status.level = DiagnosticStatus.WARN
00126             hz_status.message = "could not determine type for topics %s. Probably never published on that topics."%self.missing_topics
00127             array.status.append(hz_status)
00128             self.pub_diagnostics.publish(array)
00129             return
00130 
00131         # calculate actual rates
00132         for rt, topic in zip(rt_HZ_store, self.topics):
00133             if not rt or not rt.times:
00134                 hz_status.level = DiagnosticStatus.ERROR
00135                 publishing_rate_error = True
00136                 rates.append(0.0)
00137                 consolidated_error_messages["never received message for topics"].append(topic)
00138             elif rt.msg_tn == rt.last_printed_tn:
00139                 hz_status.level = DiagnosticStatus.ERROR
00140                 publishing_rate_error = True
00141                 rates.append(0.0)
00142                 consolidated_error_messages["no messages anymore for topics"].append(topic)
00143             else:
00144                 with rt.lock: # calculation taken from /opt/ros/indigo/lib/python2.7/dist-packages/rostopic/__init__.py
00145                     n = len(rt.times)
00146                     mean = sum(rt.times) / n
00147                     rate = 1./mean if mean > 0. else 0
00148                     rt.last_printed_tn = rt.msg_tn
00149                 rates.append(round(rate, 2))
00150                 if min_rate and rate < min_rate:
00151                     hz_status.level = DiagnosticStatus.WARN
00152                     publishing_rate_error = True
00153                     consolidated_error_messages["publishing rate is too low for topics"].append(topic)
00154                 elif max_rate and rate > max_rate:
00155                     hz_status.level = DiagnosticStatus.WARN
00156                     publishing_rate_error = True
00157                     consolidated_error_messages["publishing rate is too high for topics"].append(topic)
00158                 else:
00159                     if not publishing_rate_error:
00160                         hz_status.level = DiagnosticStatus.OK
00161                         hz_status.message = 'all publishing rates are ok'
00162 
00163         if publishing_rate_error:
00164             message = ""
00165             key = "never received message for topics"
00166             if len(consolidated_error_messages[key]) > 0:
00167                 message += key + " " + str(consolidated_error_messages[key]) + ", "
00168             key = "no messages anymore for topics"
00169             if len(consolidated_error_messages[key]) > 0:
00170                 message += key + " " + str(consolidated_error_messages[key]) + ", "
00171             key = "publishing rate is too low for topics"
00172             if len(consolidated_error_messages[key]) > 0:
00173                 message += key + " " + str(consolidated_error_messages[key]) + ", "
00174             key = "publishing rate is too high for topics"
00175             if len(consolidated_error_messages[key]) > 0:
00176                 message += key + " " + str(consolidated_error_messages[key])
00177             hz_status.message = message
00178 
00179         hz_status.values.append(KeyValue("rates", str(rates)))
00180         array.status.append(hz_status)
00181         self.pub_diagnostics.publish(array)
00182 
00183 if __name__ == '__main__':
00184     hzt = HzTest()
00185     hzt.run()


cob_monitoring
Author(s): Florian Weisshardt , Felix Messmer
autogenerated on Sun Jun 9 2019 20:20:19