From 59dcd24613b78132e44caeed07e4ed5a63784dbe Mon Sep 17 00:00:00 2001 From: bav6096 <benedikt.deike@informatik.uni-hamburg.de> Date: Sun, 31 Oct 2021 18:52:23 +0100 Subject: [PATCH] implemented monitor and watchdog --- CMakeLists.txt | 17 ++++---- launch/monitor_test.launch | 0 launch/watchdog.launch | 0 msg/Metric.msg | 2 +- msg/Monitoring.msg | 3 +- nodes/monitor_test | 21 ++++++++++ nodes/watchdog | 21 ++++++++++ scripts/watchdog.py | 15 ------- setup.py | 11 ++++++ src/monitoring/__init__.py | 0 {scripts => src/monitoring}/monitor.py | 55 ++++++++++++++------------ src/monitoring/watchdog.py | 55 ++++++++++++++++++++++++++ 12 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 launch/monitor_test.launch create mode 100644 launch/watchdog.launch create mode 100755 nodes/monitor_test create mode 100755 nodes/watchdog delete mode 100755 scripts/watchdog.py create mode 100644 setup.py create mode 100644 src/monitoring/__init__.py rename {scripts => src/monitoring}/monitor.py (62%) create mode 100755 src/monitoring/watchdog.py diff --git a/CMakeLists.txt b/CMakeLists.txt index fe7a707..7bd6a6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ find_package(catkin REQUIRED COMPONENTS ## Uncomment this if the package has a setup.py. This macro ensures ## modules and global scripts declared therein get installed ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html -# catkin_python_setup() +catkin_python_setup() ################################################ ## Declare ROS messages, services and actions ## @@ -49,6 +49,7 @@ find_package(catkin REQUIRED COMPONENTS ## Generate messages in the 'msg' folder add_message_files( FILES + Monitoring.msg Metric.msg ) @@ -159,15 +160,15 @@ include_directories( ## Mark executable scripts (Python etc.) for installation ## in contrast to setup.py, you can choose the destination -# catkin_install_python(PROGRAMS -# scripts/my_python_script -# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) +catkin_install_python(PROGRAMS + nodes/monitor_test + DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) ## Mark executables for installation ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html # install(TARGETS ${PROJECT_NAME}_node -# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} # ) ## Mark libraries for installation @@ -187,9 +188,7 @@ include_directories( ## Mark other files for installation (e.g. launch and bag files, etc.) # install(FILES -# # myfile1 -# # myfile2 -# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} # ) ############# diff --git a/launch/monitor_test.launch b/launch/monitor_test.launch new file mode 100644 index 0000000..e69de29 diff --git a/launch/watchdog.launch b/launch/watchdog.launch new file mode 100644 index 0000000..e69de29 diff --git a/msg/Metric.msg b/msg/Metric.msg index 15c828a..26728b1 100644 --- a/msg/Metric.msg +++ b/msg/Metric.msg @@ -1,4 +1,4 @@ -string key # Label of the metric. +string label # Label of the metric. string value # Value of the metric. string unit # Unit of the metric. float32 critical # Critical level of the metric. \ No newline at end of file diff --git a/msg/Monitoring.msg b/msg/Monitoring.msg index 4d9d229..4274d62 100644 --- a/msg/Monitoring.msg +++ b/msg/Monitoring.msg @@ -1,2 +1,3 @@ -string origin # Origin of the monitoring message. +string family # Affiliation of the monitored metric. +string origin # Origin of the monitored metric. Metric metric # Monitored metric. \ No newline at end of file diff --git a/nodes/monitor_test b/nodes/monitor_test new file mode 100755 index 0000000..01d669c --- /dev/null +++ b/nodes/monitor_test @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import rospy +from monitoring.monitor import Monitor + + +def monitor_test(): + rospy.init_node("monitor_test") + monitor = Monitor(1, 1, "test") + + rate = rospy.Rate(10) + while not rospy.is_shutdown(): + monitor.update_metric("test", 10, "test", 0.5) + rate.sleep() + + +if __name__ == '__main__': + try: + monitor_test() + except rospy.ROSInterruptException: + pass diff --git a/nodes/watchdog b/nodes/watchdog new file mode 100755 index 0000000..24ef85b --- /dev/null +++ b/nodes/watchdog @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import rospy +from monitoring.watchdog import Watchdog + + +def watchdog(): + rospy.init_node("watchdog") + watching = Watchdog(1.0) + + rate = rospy.Rate(10) + while not rospy.is_shutdown(): + print(watching.states) + rate.sleep() + + +if __name__ == '__main__': + try: + watchdog() + except rospy.ROSInterruptException: + pass diff --git a/scripts/watchdog.py b/scripts/watchdog.py deleted file mode 100755 index 66758bd..0000000 --- a/scripts/watchdog.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python -import rospy -from monitoring.msg import Metric - -class Watchdog: - def __init__(self): - self.state = 0 - self.observing = {} - rospy.init_node("watchdog") - rospy.Subscriber("metric", Metric, callback) - - def callback(self, monitoring): - - if monitoring.origin in self.observing: - self.observing[metric] \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..73f52d9 --- /dev/null +++ b/setup.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from distutils.core import setup +from catkin_pkg.python_setup import generate_distutils_setup + +setup_args = generate_distutils_setup( + packages=['monitoring'], + package_dir={'': 'src'} +) + +setup(**setup_args) diff --git a/src/monitoring/__init__.py b/src/monitoring/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/monitor.py b/src/monitoring/monitor.py similarity index 62% rename from scripts/monitor.py rename to src/monitoring/monitor.py index cb0120c..54c986a 100755 --- a/scripts/monitor.py +++ b/src/monitoring/monitor.py @@ -1,47 +1,50 @@ #!/usr/bin/env python -import socket + import rospy +import socket from monitoring.msg import Monitoring from monitoring.msg import Metric class Monitor: - def __init__(self, mode, freq): + def __init__(self, mode=1, freq=1.0, family="generic"): self.log = {} # Logging dictionary. Is used to accomplish different publishing modes. - self.pub = rospy.Publisher('metric', Metric, queue_size=1) + self.pub = rospy.Publisher('monitoring', Monitoring, queue_size=1) self.mode = mode - self.monitoring = Monitoring() - self.monitoring.origin = socket.gethostname() + rospy.get_name() - if not freq > 0: + if not freq > 0.0: rospy.logwarn("Frequency must be greater then 0! Using 1 as frequency!") - freq = 1 + freq = 1.0 self.timer = rospy.Timer(rospy.Duration(1.0 / freq), self.publish_monitoring) + self.monitoring = Monitoring() + self.monitoring.family = family + self.monitoring.origin = socket.gethostname() + rospy.get_name() + # At the moment the last metric update ist published over and over. - def publish_monitoring(self): + def publish_monitoring(self, event): self.pub.publish(self.monitoring) - def update_metric(self, key, value, unit, critical): + def update_metric(self, label, value, unit, critical): def set_metric(): metric = Metric() - metric.key = key - metric.value = value - metric.unit = unit + metric.label = str(label) + metric.value = str(value) + metric.unit = str(unit) metric.critical = critical self.monitoring.metric = metric def rst_key(): # Reset the value of a key in the logging dictionary. - self.log[key] = {'num': 0, 'val': 0, 'sum': 0, 'dur': rospy.get_rostime()} + self.log[label] = {'num': 0, 'val': 0, 'sum': 0, 'dur': rospy.get_rostime()} - if " " in key: # A key cannot contain whitespace! - rospy.logwarn("Whitespaces are not allowed in metric keys!") + if " " in label: # A label cannot contain whitespace! + rospy.logwarn("Whitespaces are not allowed in metric labels!") elif critical > 1 or critical < 0: rospy.logwarn("Critical level must be between 1 and 0!") else: - if key in self.log: + if label in self.log: # Mode 1: Set the last obtained value. if self.mode == 1: set_metric() @@ -49,32 +52,32 @@ class Monitor: # Mode 2: Remember and set the minimum value, # obtained after mode selection. elif self.mode == 2: - if value < self.log[key]['val']: - self.log[key]['val'] = value + if value < self.log[label]['val']: + self.log[label]['val'] = value else: - value = self.log[key]['val'] + value = self.log[label]['val'] set_metric() # Mode 3: Remember and set the maximum value, # obtained after mode selection. elif self.mode == 3: - if value > self.log[key]['val']: - self.log[key]['val'] = value + if value > self.log[label]['val']: + self.log[label]['val'] = value else: - value = self.log[key]['val'] + value = self.log[label]['val'] set_metric() # Mode 4: Set the average obtained value over five # or more seconds. elif self.mode == 4: - duration = rospy.get_rostime() - self.log[key]['dur'] + duration = rospy.get_rostime() - self.log[label]['dur'] if duration < rospy.Duration(5): - self.log[key]['num'] += 1 - self.log[key]['sum'] += value + self.log[label]['num'] += 1 + self.log[label]['sum'] += value else: - value = self.log[key]['sum'] / (self.log[key]['num'] + 0.001) + value = self.log[label]['sum'] / (self.log[label]['num'] + 0.001) set_metric() rst_key() else: diff --git a/src/monitoring/watchdog.py b/src/monitoring/watchdog.py new file mode 100755 index 0000000..829d084 --- /dev/null +++ b/src/monitoring/watchdog.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +import rospy +from monitoring.msg import Monitoring + + +class Watchdog: + def __init__(self, freq): + self.states = {} + self.observing = {} + + if not freq > 0.0: + rospy.logwarn("Frequency must be greater then 0! Using 1 as frequency!") + freq = 1.0 + + self.timer = rospy.Timer(rospy.Duration(1.0 / freq), self.update_state) + + rospy.Subscriber("monitoring", Monitoring, self.logging) + + def logging(self, monitoring): + family = monitoring.family + origin = monitoring.origin + metric = monitoring.metric + + label = metric.label + value = metric.value + unit = metric.unit + critical = metric.critical + + if family in self.observing: + if origin in self.observing[family]: + self.observing[family][origin].update({label: {"value": value, "unit": unit, "critical": critical}}) + else: + self.observing[family][origin] = {} + self.observing[family][origin][label] = {"value": value, "unit": unit, "critical": critical} + else: + self.observing[family] = {} + self.observing[family][origin] = {} + self.observing[family][origin][label] = {"value": value, "unit": unit, "critical": critical} + + def update_state(self, event): + for family in self.observing: + aggregation = 0 + number = 0 + + for origin in self.observing[family]: + for label in self.observing[family][origin]: + number = number + 1 + aggregation = aggregation + self.observing[family][origin][label]["critical"] + + quotient = aggregation / number + self.states.update({family: quotient}) + + + -- GitLab