Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
00031 try:
00032
00033 self.topics = rospy.get_param('~topics')
00034
00035 self.hz = rospy.get_param('~hz', None)
00036
00037 self.hzerror = rospy.get_param('~hzerror', None)
00038
00039 self.window_size = float(rospy.get_param('~window_size', 100))
00040
00041 self.diagnostics_name = rospy.get_param('~diagnostics_name')
00042 self.diagnostics_name = self.diagnostics_name.replace('/','_')
00043
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
00058 while not rospy.is_shutdown():
00059 for topic in self.topics:
00060 msg_class, real_topic, _ = rostopic.get_topic_class(topic, blocking=False)
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
00075
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
00084 while not rospy.is_shutdown():
00085
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
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
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 = {}
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
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:
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()