From 7681f9043245ebf03c10472d73ea6fb285c310fe Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 28 May 2022 19:46:17 +0900 Subject: [PATCH 01/25] [audible_warning] Add diagnostics_level_to_str --- jsk_tools/src/jsk_tools/diagnostics_utils.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/jsk_tools/src/jsk_tools/diagnostics_utils.py b/jsk_tools/src/jsk_tools/diagnostics_utils.py index 34d4ea8bb..a65c506bc 100644 --- a/jsk_tools/src/jsk_tools/diagnostics_utils.py +++ b/jsk_tools/src/jsk_tools/diagnostics_utils.py @@ -4,6 +4,8 @@ from itertools import izip_longest as zip_longest import re +from diagnostic_msgs.msg import DiagnosticStatus + cached_paths = {} cached_result = {} @@ -75,3 +77,16 @@ def filter_diagnostics_status_list(status_list, blacklist, continue filtered_status.append(s) return filtered_status + + +def diagnostics_level_to_str(level): + if level == DiagnosticStatus.OK: + return 'OK' + elif level == DiagnosticStatus.WARN: + return 'WARN' + elif level == DiagnosticStatus.ERROR: + return 'ERROR' + elif level == DiagnosticStatus.STALE: + return 'STALE' + else: + raise ValueError('Not valid level {}'.format(level)) From d0b8222ba4d435763ccb1f857e9961c08eccd43b Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 28 May 2022 19:48:15 +0900 Subject: [PATCH 02/25] [audible_warning] Add dynamic reconfigure parameters --- jsk_tools/CMakeLists.txt | 21 +++++++++++++++++++-- jsk_tools/cfg/AudibleWarning.cfg | 19 +++++++++++++++++++ jsk_tools/package.xml | 4 ++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100755 jsk_tools/cfg/AudibleWarning.cfg diff --git a/jsk_tools/CMakeLists.txt b/jsk_tools/CMakeLists.txt index 6ae62abfa..1140b01fa 100644 --- a/jsk_tools/CMakeLists.txt +++ b/jsk_tools/CMakeLists.txt @@ -1,11 +1,19 @@ cmake_minimum_required(VERSION 2.8.3) project(jsk_tools) -find_package(catkin REQUIRED) +find_package(catkin REQUIRED + COMPONENTS + message_generation + dynamic_reconfigure) catkin_python_setup() set(ROS_BUILD_TYPE RelWithDebInfo) + +generate_dynamic_reconfigure_options( + cfg/AudibleWarning.cfg +) + catkin_package( - CATKIN_DEPENDS # + CATKIN_DEPENDS message_runtime # LIBRARIES # INCLUDE_DIRS # DEPENDS # @@ -25,6 +33,15 @@ foreach(exec ${BIN_EXECUTABLES}) install(PROGRAMS bin/${exec} DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}) endforeach(exec) +file(GLOB SCRIPT_PROGRAMS src/*.py) +catkin_install_python( + PROGRAMS ${SCRIPT_PROGRAMS} + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts/) +file(GLOB SCRIPT_BIN_PROGRAMS bin/*.py) +catkin_install_python( + PROGRAMS ${SCRIPT_BIN_PROGRAMS} + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/bin/) + if (CATKIN_ENABLE_TESTING) find_package(roslint REQUIRED) roslint_python(src/audible_warning.py) diff --git a/jsk_tools/cfg/AudibleWarning.cfg b/jsk_tools/cfg/AudibleWarning.cfg new file mode 100755 index 000000000..21e4997bc --- /dev/null +++ b/jsk_tools/cfg/AudibleWarning.cfg @@ -0,0 +1,19 @@ +#! /usr/bin/env python + + +from dynamic_reconfigure.parameter_generator_catkin import * + +PACKAGE = 'jsk_tools' + +gen = ParameterGenerator() +speak_group = gen.add_group("speak flag") +speak_group.add("enable", bool_t, 0, "Flag of speak", True) +speak_group.add("speak_ok", bool_t, 0, "Speak ok level diagnostics", False) +speak_group.add("speak_stale", bool_t, 0, "Speak stale level diagnostics", True) +speak_group.add("speak_warn", bool_t, 0, "Speak warn level diagnostics", True) +speak_group.add("speak_error", bool_t, 0, "Speak error level diagnostics", True) + +gen.add("volume", double_t, 0, "Volume of sound.", 1.0, 0.0, 1.0) +gen.add("speak_interval", double_t, 0, "Volume of sound.", 120.0, 0.0, 3600.0) + +exit(gen.generate(PACKAGE, PACKAGE, "AudibleWarning")) diff --git a/jsk_tools/package.xml b/jsk_tools/package.xml index ef0a9d455..afeeb7a2d 100644 --- a/jsk_tools/package.xml +++ b/jsk_tools/package.xml @@ -15,12 +15,16 @@ catkin + dynamic_reconfigure git + message_generation rosgraph_msgs cv_bridge diagnostic_aggregator diagnostic_msgs diagnostic_updater + dynamic_reconfigure + message_runtime python-percol python-colorama python3-colorama From aebc2a984f275418741304673fede7141e092110 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 28 May 2022 19:48:39 +0900 Subject: [PATCH 03/25] [audible_warning/sample] Fixed audible_warning node name --- jsk_tools/sample/sample_audible_warning.launch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_tools/sample/sample_audible_warning.launch b/jsk_tools/sample/sample_audible_warning.launch index 7ebc1a4d8..236c97e14 100644 --- a/jsk_tools/sample/sample_audible_warning.launch +++ b/jsk_tools/sample/sample_audible_warning.launch @@ -22,7 +22,7 @@ - From 4ae214f19b6ecef7b39bd606c836b871ba491519 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 28 May 2022 19:49:23 +0900 Subject: [PATCH 04/25] [audible_warning] Use dynamic reconfigure params --- jsk_tools/src/audible_warning.py | 85 +++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/jsk_tools/src/audible_warning.py b/jsk_tools/src/audible_warning.py index 5dbf904a6..f93186283 100755 --- a/jsk_tools/src/audible_warning.py +++ b/jsk_tools/src/audible_warning.py @@ -12,11 +12,14 @@ import actionlib from diagnostic_msgs.msg import DiagnosticArray from diagnostic_msgs.msg import DiagnosticStatus +from dynamic_reconfigure.server import Server import rospy from sound_play.msg import SoundRequest from sound_play.msg import SoundRequestAction from sound_play.msg import SoundRequestGoal +from jsk_tools.cfg import AudibleWarningConfig as Config +from jsk_tools.diagnostics_utils import diagnostics_level_to_str from jsk_tools.diagnostics_utils import filter_diagnostics_status_list from jsk_tools.diagnostics_utils import is_leaf @@ -33,7 +36,8 @@ def __init__(self, rate=1.0, wait=True, language='', volume=1.0, speak_interval=0, - wait_speak_duration_time=10): + wait_speak_duration_time=10, + diagnostics_level_list=None): super(SpeakThread, self).__init__() self.wait_speak_duration_time = wait_speak_duration_time self.event = Event() @@ -43,9 +47,11 @@ def __init__(self, rate=1.0, wait=True, self.lock = Lock() self.status_list = [] self.speak_interval = speak_interval + self.diagnostics_level_list = diagnostics_level_list or [] tm = rospy.Time.now().to_sec() \ - speak_interval self.previous_spoken_time = defaultdict(lambda tm=tm: tm) + self.speak_flag = True self.language = language self.talk = actionlib.SimpleActionClient( @@ -55,6 +61,36 @@ def __init__(self, rate=1.0, wait=True, def stop(self): self.event.set() + def set_diagnostics_level_list(self, level_list): + self.diagnostics_level_list = level_list + + def set_speak_flag(self, flag): + if flag is True and self.speak_flag is False: + # clear queue before start speaking. + with self.lock: + self.status_list = [] + self.speak_flag = flag + if self.speak_flag is True: + rospy.loginfo('audible warning is enabled. speak [{}] levels' + .format(', '.join(map(diagnostics_level_to_str, + self.diagnostics_level_list)))) + else: + rospy.loginfo('audible warning is disabled.') + + def set_volume(self, volume): + volume = min(max(0.0, volume), 1.0) + if self.volume != volume: + self.volume = volume + rospy.loginfo("audible warning's volume was set to {}".format( + self.volume)) + + def set_speak_interval(self, interval): + interval = max(0.0, interval) + if self.speak_interval != interval: + self.speak_interval = interval + rospy.loginfo("audible warning's speak interval was set to {}" + .format(self.speak_interval)) + def add(self, status_list): with self.lock: for status in status_list: @@ -79,7 +115,14 @@ def run(self): while not self.event.wait(self.rate): e = self.pop() if e: - if e.level == DiagnosticStatus.WARN: + if self.speak_flag is False: + continue + if e.level not in self.diagnostics_level_list: + continue + + if e.level == DiagnosticStatus.OK: + prefix = 'ok.' + elif e.level == DiagnosticStatus.WARN: prefix = 'warning.' elif e.level == DiagnosticStatus.ERROR: prefix = 'error.' @@ -112,9 +155,7 @@ class AudibleWarning(object): def __init__(self): speak_rate = rospy.get_param("~speak_rate", 1.0) - speak_interval = rospy.get_param("~speak_interval", 120.0) wait_speak = rospy.get_param("~wait_speak", True) - volume = rospy.get_param("~volume", 1.0) language = rospy.get_param('~language', '') seconds_to_start_speaking = rospy.get_param( '~seconds_to_start_speaking', 0) @@ -127,14 +168,6 @@ def __init__(self): < seconds_to_start_speaking: rate.sleep() - self.diagnostics_list = [] - if rospy.get_param("~speak_warn", True): - self.diagnostics_list.append(DiagnosticStatus.WARN) - if rospy.get_param("~speak_error", True): - self.diagnostics_list.append(DiagnosticStatus.ERROR) - if rospy.get_param("~speak_stale", True): - self.diagnostics_list.append(DiagnosticStatus.STALE) - blacklist = rospy.get_param("~blacklist", []) self.blacklist_names = [] self.blacklist_messages = [] @@ -149,9 +182,11 @@ def __init__(self): else: message = re.compile(bl['message']) self.blacklist_messages.append(message) - self.speak_thread = SpeakThread(speak_rate, wait_speak, - language, volume, speak_interval, - seconds_to_start_speaking) + self.speak_thread = SpeakThread( + speak_rate, wait_speak, + language, + wait_speak_duration_time=seconds_to_start_speaking) + self.srv = Server(Config, self.config_callback) # run-stop self.speak_when_runstopped = rospy.get_param( @@ -190,6 +225,22 @@ def __init__(self): self.diag_cb, queue_size=1) self.speak_thread.start() + def config_callback(self, config, level): + level_list = [] + if config.speak_ok: + level_list.append(DiagnosticStatus.OK) + if config.speak_warn: + level_list.append(DiagnosticStatus.WARN) + if config.speak_error: + level_list.append(DiagnosticStatus.ERROR) + if config.speak_stale: + level_list.append(DiagnosticStatus.STALE) + self.speak_thread.set_diagnostics_level_list(level_list) + self.speak_thread.set_speak_flag(config.enable) + self.speak_thread.set_volume(config.volume) + self.speak_thread.set_speak_interval(config.speak_interval) + return config + def run_stop_callback(self, msg): if isinstance(msg, rospy.msg.AnyMsg): package, msg_type = msg._connection_header['type'].split('/') @@ -208,9 +259,7 @@ def on_shutdown(self): self.speak_thread.join() def diag_cb(self, msg): - target_status_list = filter( - lambda n: n.level in self.diagnostics_list, - msg.status) + target_status_list = msg.status if self.run_stop: if self.speak_when_runstopped is False: rospy.logdebug('RUN STOP is pressed. Do not speak warning.') From 4c77697fa76e86d761f398c34032af3d1bb52d44 Mon Sep 17 00:00:00 2001 From: iory Date: Fri, 3 Jun 2022 23:48:44 +0900 Subject: [PATCH 05/25] [audible_warning] Add inflection_utils for camel_to_snake case --- jsk_tools/src/audible_warning.py | 2 ++ jsk_tools/src/jsk_tools/inflection_utils.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 jsk_tools/src/jsk_tools/inflection_utils.py diff --git a/jsk_tools/src/audible_warning.py b/jsk_tools/src/audible_warning.py index f93186283..6d4fecefd 100755 --- a/jsk_tools/src/audible_warning.py +++ b/jsk_tools/src/audible_warning.py @@ -22,6 +22,7 @@ from jsk_tools.diagnostics_utils import diagnostics_level_to_str from jsk_tools.diagnostics_utils import filter_diagnostics_status_list from jsk_tools.diagnostics_utils import is_leaf +from jsk_tools.inflection_utils import camel_to_snake def expr_eval(expr): @@ -131,6 +132,7 @@ def run(self): else: prefix = 'ok.' sentence = prefix + e.name + ' ' + e.message + sentence = camel_to_snake(sentence) sentence = sentence.replace('/', ' ') sentence = sentence.replace('_', ' ') rospy.loginfo('audible warning error name "{}"'.format(e.name)) diff --git a/jsk_tools/src/jsk_tools/inflection_utils.py b/jsk_tools/src/jsk_tools/inflection_utils.py new file mode 100644 index 000000000..455869920 --- /dev/null +++ b/jsk_tools/src/jsk_tools/inflection_utils.py @@ -0,0 +1,15 @@ +import re + + +def camel_to_snake(word): + """Convert Camel case to snake case. + + Examples + -------- + >>> camel_to_snake('clusterPointIndices') + 'cluster_point_indices' + """ + word = re.sub(r"([A-Z]+)([A-Z][a-z])", r'\1_\2', word) + word = re.sub(r"([a-z\d])([A-Z])", r'\1_\2', word) + word = word.replace("-", "_") + return word.lower() From e8f8ef062da9d94a4e8fd4b57346a92e17aafb94 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 28 May 2022 20:56:20 +0900 Subject: [PATCH 06/25] [audible_warning] Add ignore after runstop time --- jsk_tools/cfg/AudibleWarning.cfg | 6 ++++++ jsk_tools/src/audible_warning.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/jsk_tools/cfg/AudibleWarning.cfg b/jsk_tools/cfg/AudibleWarning.cfg index 21e4997bc..bd1144e44 100755 --- a/jsk_tools/cfg/AudibleWarning.cfg +++ b/jsk_tools/cfg/AudibleWarning.cfg @@ -15,5 +15,11 @@ speak_group.add("speak_error", bool_t, 0, "Speak error level diagnostics", True) gen.add("volume", double_t, 0, "Volume of sound.", 1.0, 0.0, 1.0) gen.add("speak_interval", double_t, 0, "Volume of sound.", 120.0, 0.0, 3600.0) +gen.add("ignore_time_after_runstop_is_enabled", double_t, 0, + "Time to ignore diagnostics after runstop is enabled.", + 0.0, 0.0, 3600.0) +gen.add("ignore_time_after_runstop_is_disabled", double_t, 0, + "Time to ignore diagnostics after runstop is disabled.", + 0.0, 0.0, 3600.0) exit(gen.generate(PACKAGE, PACKAGE, "AudibleWarning")) diff --git a/jsk_tools/src/audible_warning.py b/jsk_tools/src/audible_warning.py index 6d4fecefd..456347362 100755 --- a/jsk_tools/src/audible_warning.py +++ b/jsk_tools/src/audible_warning.py @@ -163,6 +163,9 @@ def __init__(self): '~seconds_to_start_speaking', 0) # Wait until seconds_to_start_speaking the time has passed. + self.run_stop_enabled_time = None + self.run_stop_disabled_time = None + self.run_stop_time = None rate = rospy.Rate(10) start_time = rospy.Time.now() while not rospy.is_shutdown() \ @@ -241,6 +244,10 @@ def config_callback(self, config, level): self.speak_thread.set_speak_flag(config.enable) self.speak_thread.set_volume(config.volume) self.speak_thread.set_speak_interval(config.speak_interval) + self.ignore_time_after_runstop_is_enabled = \ + config.ignore_time_after_runstop_is_enabled + self.ignore_time_after_runstop_is_disabled = \ + config.ignore_time_after_runstop_is_disabled return config def run_stop_callback(self, msg): @@ -253,8 +260,15 @@ def run_stop_callback(self, msg): self.run_stop_topic, msg_class, self.run_stop_callback) self.run_stop_sub = deserialized_sub return - self.run_stop = self.run_stop_condition( - self.run_stop_topic, msg, rospy.Time.now()) + tm = rospy.Time.now() + run_stop = self.run_stop_condition( + self.run_stop_topic, msg, tm) + if run_stop != self.run_stop: + if run_stop is True: + self.run_stop_enabled_time = tm + else: + self.run_stop_disabled_time = tm + self.run_stop = run_stop def on_shutdown(self): self.speak_thread.stop() @@ -262,6 +276,16 @@ def on_shutdown(self): def diag_cb(self, msg): target_status_list = msg.status + + if self.ignore_time_after_runstop_is_enabled > 0.0: + if ((rospy.Time.now() - self.run_stop_enabled_time).to_sec < + self.ignore_time_after_runstop_is_enabled): + return + if self.ignore_time_after_runstop_is_disabled > 0.0: + if ((rospy.Time.now() - self.run_stop_disabled_time).to_sec < + self.ignore_time_after_runstop_is_disabled): + return + if self.run_stop: if self.speak_when_runstopped is False: rospy.logdebug('RUN STOP is pressed. Do not speak warning.') From a7b8285a679b24bd819c8d5e02eaf892c3fe8f43 Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 4 Jun 2022 02:16:44 +0900 Subject: [PATCH 07/25] [audible_warning] Ignore if self.run_stop_(disabled|enabled)_time is None --- jsk_tools/src/audible_warning.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/jsk_tools/src/audible_warning.py b/jsk_tools/src/audible_warning.py index 456347362..787dfc6df 100755 --- a/jsk_tools/src/audible_warning.py +++ b/jsk_tools/src/audible_warning.py @@ -165,7 +165,6 @@ def __init__(self): # Wait until seconds_to_start_speaking the time has passed. self.run_stop_enabled_time = None self.run_stop_disabled_time = None - self.run_stop_time = None rate = rospy.Rate(10) start_time = rospy.Time.now() while not rospy.is_shutdown() \ @@ -278,12 +277,16 @@ def diag_cb(self, msg): target_status_list = msg.status if self.ignore_time_after_runstop_is_enabled > 0.0: - if ((rospy.Time.now() - self.run_stop_enabled_time).to_sec < - self.ignore_time_after_runstop_is_enabled): + if self.run_stop_enabled_time is not None \ + and ((rospy.Time.now() + - self.run_stop_enabled_time).to_sec < + self.ignore_time_after_runstop_is_enabled): return if self.ignore_time_after_runstop_is_disabled > 0.0: - if ((rospy.Time.now() - self.run_stop_disabled_time).to_sec < - self.ignore_time_after_runstop_is_disabled): + if self.run_stop_disabled_time is not None \ + and ((rospy.Time.now() + - self.run_stop_disabled_time).to_sec < + self.ignore_time_after_runstop_is_disabled): return if self.run_stop: From dbb8f70e66f090a3560a5ddf1a145eb4df946a7c Mon Sep 17 00:00:00 2001 From: iory Date: Sat, 4 Jun 2022 02:20:15 +0900 Subject: [PATCH 08/25] [audible_warning] Add docs for dynamic reconfigure's parameters --- doc/jsk_tools/scripts/audible_warning.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/jsk_tools/scripts/audible_warning.md b/doc/jsk_tools/scripts/audible_warning.md index 61aadee5c..75888827f 100644 --- a/doc/jsk_tools/scripts/audible_warning.md +++ b/doc/jsk_tools/scripts/audible_warning.md @@ -49,6 +49,14 @@ Robots using diagnostics can use this node. This is useful for ignoring errors that occur when the robot starts. +* `~enable` (`Bool`, default: `True`) + + If `True`, speak diagnositcs. If `False`, this node don't speak. + +* `~speak_ok` (`Bool`, default: `False`) + + If `True`, speak ok level diagnostics. + * `~speak_warn` (`Bool`, default: `True`) If `True`, speak warning level diagnostics. @@ -81,6 +89,14 @@ Robots using diagnostics can use this node. run_stop_condition: "m.runstopped is True" ``` +* `~ignore_time_after_runstop_is_enabled` (`Float`, default: `0.0`) + + Time to ignore diagnostics after runstop is enabled. + +* `~ignore_time_after_runstop_is_disabled` (`Float`, default: `0.0`) + + Time to ignore diagnostics after runstop is disabled. + - `~blacklist` (`Yaml`, required) User must always specify `name`. You can specify `message` as an option. From bb44d7e67257d681367933ce5a3387082118c989 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 7 Jun 2022 00:58:28 +0900 Subject: [PATCH 09/25] [audible_warning] Move audible_warning.py to node_scripts. --- jsk_tools/CMakeLists.txt | 14 ++------------ jsk_tools/{src => node_scripts}/audible_warning.py | 0 jsk_tools/package.xml | 2 -- 3 files changed, 2 insertions(+), 14 deletions(-) rename jsk_tools/{src => node_scripts}/audible_warning.py (100%) diff --git a/jsk_tools/CMakeLists.txt b/jsk_tools/CMakeLists.txt index 1140b01fa..c0b5db08e 100644 --- a/jsk_tools/CMakeLists.txt +++ b/jsk_tools/CMakeLists.txt @@ -3,7 +3,6 @@ project(jsk_tools) find_package(catkin REQUIRED COMPONENTS - message_generation dynamic_reconfigure) catkin_python_setup() set(ROS_BUILD_TYPE RelWithDebInfo) @@ -13,7 +12,7 @@ generate_dynamic_reconfigure_options( ) catkin_package( - CATKIN_DEPENDS message_runtime # + CATKIN_DEPENDS dynamic_reconfigure # LIBRARIES # INCLUDE_DIRS # DEPENDS # @@ -33,15 +32,6 @@ foreach(exec ${BIN_EXECUTABLES}) install(PROGRAMS bin/${exec} DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}) endforeach(exec) -file(GLOB SCRIPT_PROGRAMS src/*.py) -catkin_install_python( - PROGRAMS ${SCRIPT_PROGRAMS} - DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/scripts/) -file(GLOB SCRIPT_BIN_PROGRAMS bin/*.py) -catkin_install_python( - PROGRAMS ${SCRIPT_BIN_PROGRAMS} - DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/bin/) - if (CATKIN_ENABLE_TESTING) find_package(roslint REQUIRED) roslint_python(src/audible_warning.py) @@ -93,7 +83,7 @@ install(DIRECTORY test USE_SOURCE_PERMISSIONS PATTERN "*.test" EXCLUDE ) -install(DIRECTORY src dot-files cmake launch sample +install(DIRECTORY src dot-files cmake launch sample node_scripts DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} USE_SOURCE_PERMISSIONS ) diff --git a/jsk_tools/src/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py similarity index 100% rename from jsk_tools/src/audible_warning.py rename to jsk_tools/node_scripts/audible_warning.py diff --git a/jsk_tools/package.xml b/jsk_tools/package.xml index afeeb7a2d..082dc1b2b 100644 --- a/jsk_tools/package.xml +++ b/jsk_tools/package.xml @@ -17,14 +17,12 @@ dynamic_reconfigure git - message_generation rosgraph_msgs cv_bridge diagnostic_aggregator diagnostic_msgs diagnostic_updater dynamic_reconfigure - message_runtime python-percol python-colorama python3-colorama From 21d2a2a08e644f6efed72787837dcb5a61358a43 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 7 Jun 2022 01:09:14 +0900 Subject: [PATCH 10/25] [audible_warning] Fixed lint test target path --- jsk_tools/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_tools/CMakeLists.txt b/jsk_tools/CMakeLists.txt index c0b5db08e..451998060 100644 --- a/jsk_tools/CMakeLists.txt +++ b/jsk_tools/CMakeLists.txt @@ -34,7 +34,7 @@ endforeach(exec) if (CATKIN_ENABLE_TESTING) find_package(roslint REQUIRED) - roslint_python(src/audible_warning.py) + roslint_python(node_scripts/audible_warning.py) roslint_python(src/jsk_tools/cltool.py) roslint_python(src/test_topic_published.py) roslint_python(src/test_rosparam_set.py) From 21b5af830ad017d80304592599db997249ea3c74 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 7 Jun 2022 01:09:26 +0900 Subject: [PATCH 11/25] [audible_warning] Fixed lint --- jsk_tools/node_scripts/audible_warning.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 787dfc6df..49b604fd3 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -278,14 +278,14 @@ def diag_cb(self, msg): if self.ignore_time_after_runstop_is_enabled > 0.0: if self.run_stop_enabled_time is not None \ - and ((rospy.Time.now() - - self.run_stop_enabled_time).to_sec < + and ((rospy.Time.now() - + self.run_stop_enabled_time).to_sec < self.ignore_time_after_runstop_is_enabled): return if self.ignore_time_after_runstop_is_disabled > 0.0: if self.run_stop_disabled_time is not None \ - and ((rospy.Time.now() - - self.run_stop_disabled_time).to_sec < + and ((rospy.Time.now() - + self.run_stop_disabled_time).to_sec < self.ignore_time_after_runstop_is_disabled): return From ae0a59cc06f75d32e92059c30ed6e87f0167d5d1 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 7 Jun 2022 13:14:18 +0900 Subject: [PATCH 12/25] [audible_warning] Convert multiple space to one --- jsk_tools/node_scripts/audible_warning.py | 2 ++ jsk_tools/src/jsk_tools/string_utils.py | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 jsk_tools/src/jsk_tools/string_utils.py diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 49b604fd3..eefe464a8 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -23,6 +23,7 @@ from jsk_tools.diagnostics_utils import filter_diagnostics_status_list from jsk_tools.diagnostics_utils import is_leaf from jsk_tools.inflection_utils import camel_to_snake +from jsk_tools.string_utils import multiple_whitespace_to_one def expr_eval(expr): @@ -135,6 +136,7 @@ def run(self): sentence = camel_to_snake(sentence) sentence = sentence.replace('/', ' ') sentence = sentence.replace('_', ' ') + sentence = multiple_whitespace_to_one(sentence) rospy.loginfo('audible warning error name "{}"'.format(e.name)) rospy.loginfo("audible warning talking: %s" % sentence) diff --git a/jsk_tools/src/jsk_tools/string_utils.py b/jsk_tools/src/jsk_tools/string_utils.py new file mode 100644 index 000000000..fe9459ce5 --- /dev/null +++ b/jsk_tools/src/jsk_tools/string_utils.py @@ -0,0 +1,2 @@ +def multiple_whitespace_to_one(text): + return ' '.join(text.split()) From ad6a9090b56016a32292de0e4bc79574443275f3 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 7 Jun 2022 13:14:35 +0900 Subject: [PATCH 13/25] [audible_warning] Convert colon symbol to english text --- jsk_tools/node_scripts/audible_warning.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index eefe464a8..da2bb8444 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -136,6 +136,7 @@ def run(self): sentence = camel_to_snake(sentence) sentence = sentence.replace('/', ' ') sentence = sentence.replace('_', ' ') + sentence = sentence.replace(':', ' colon ') sentence = multiple_whitespace_to_one(sentence) rospy.loginfo('audible warning error name "{}"'.format(e.name)) rospy.loginfo("audible warning talking: %s" % sentence) From ca0753f4af891696f80fb0d18763d8e5736e17ed Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 7 Jun 2022 13:21:30 +0900 Subject: [PATCH 14/25] [audible_warning] Add publishing text --- jsk_tools/node_scripts/audible_warning.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index da2bb8444..769ee5b9a 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -13,6 +13,7 @@ from diagnostic_msgs.msg import DiagnosticArray from diagnostic_msgs.msg import DiagnosticStatus from dynamic_reconfigure.server import Server +import std_msgs.msg import rospy from sound_play.msg import SoundRequest from sound_play.msg import SoundRequestAction @@ -56,6 +57,13 @@ def __init__(self, rate=1.0, wait=True, self.speak_flag = True self.language = language + self.pub_original_text = rospy.Publisher('~output/original_text', + std_msgs.msg.String, + queue_size=1) + self.pub_speak_text = rospy.Publisher('~output/text', + std_msgs.msg.String, + queue_size=1) + self.talk = actionlib.SimpleActionClient( "/robotsound", SoundRequestAction) self.talk.wait_for_server() @@ -140,6 +148,9 @@ def run(self): sentence = multiple_whitespace_to_one(sentence) rospy.loginfo('audible warning error name "{}"'.format(e.name)) rospy.loginfo("audible warning talking: %s" % sentence) + self.pub_original_text.publish(e.message) + self.pub_speak_text.publish( + "audible warning talking: %s" % sentence) goal = SoundRequestGoal() goal.sound_request.sound = SoundRequest.SAY From d7cdc9183582987a746386aca1d3e134389ad95c Mon Sep 17 00:00:00 2001 From: iory Date: Wed, 8 Jun 2022 18:53:30 +0900 Subject: [PATCH 15/25] [audible_warning] Add AudibleWarningClient for reconfigure --- jsk_tools/src/jsk_tools/clients/__init__.py | 1 + .../clients/audible_warninig_client.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 jsk_tools/src/jsk_tools/clients/__init__.py create mode 100644 jsk_tools/src/jsk_tools/clients/audible_warninig_client.py diff --git a/jsk_tools/src/jsk_tools/clients/__init__.py b/jsk_tools/src/jsk_tools/clients/__init__.py new file mode 100644 index 000000000..91a96e71c --- /dev/null +++ b/jsk_tools/src/jsk_tools/clients/__init__.py @@ -0,0 +1 @@ +from .audible_warninig_client import AudibleWarningReconfigureClient diff --git a/jsk_tools/src/jsk_tools/clients/audible_warninig_client.py b/jsk_tools/src/jsk_tools/clients/audible_warninig_client.py new file mode 100644 index 000000000..0bc575505 --- /dev/null +++ b/jsk_tools/src/jsk_tools/clients/audible_warninig_client.py @@ -0,0 +1,18 @@ +from dynamic_reconfigure.client import Client + + +class AudibleWarningReconfigureClient(object): + + def __init__(self, client_name='audible_warning'): + super(AudibleWarningReconfigureClient).__init__() + self.client_name = client_name + self.client = Client(client_name, timeout=3) + + def reconfigure(self, **kwargs): + return self.client.update_configuration(kwargs) + + def enable(self): + return self.client.update_configuration({'enable': True}) + + def disable(self): + return self.client.update_configuration({'enable': False}) From 2e1ddc17fdcce73cceae7df6c004f5509cca8ca4 Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 9 Jun 2022 01:35:47 +0900 Subject: [PATCH 16/25] [audible_warning] Make speak_when_runstopped dynamic params --- jsk_tools/cfg/AudibleWarning.cfg | 1 + jsk_tools/node_scripts/audible_warning.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_tools/cfg/AudibleWarning.cfg b/jsk_tools/cfg/AudibleWarning.cfg index bd1144e44..705682213 100755 --- a/jsk_tools/cfg/AudibleWarning.cfg +++ b/jsk_tools/cfg/AudibleWarning.cfg @@ -12,6 +12,7 @@ speak_group.add("speak_ok", bool_t, 0, "Speak ok level diagnostics", False) speak_group.add("speak_stale", bool_t, 0, "Speak stale level diagnostics", True) speak_group.add("speak_warn", bool_t, 0, "Speak warn level diagnostics", True) speak_group.add("speak_error", bool_t, 0, "Speak error level diagnostics", True) +speak_group.add("speak_when_runstopped", bool_t, 0, "Speak when runstop is enabled", True) gen.add("volume", double_t, 0, "Volume of sound.", 1.0, 0.0, 1.0) gen.add("speak_interval", double_t, 0, "Volume of sound.", 120.0, 0.0, 3600.0) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 769ee5b9a..9d33a910a 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -207,8 +207,6 @@ def __init__(self): self.srv = Server(Config, self.config_callback) # run-stop - self.speak_when_runstopped = rospy.get_param( - '~speak_when_runstopped', True) self.run_stop = False self.run_stop_topic = rospy.get_param('~run_stop_topic', None) if self.run_stop_topic: @@ -261,6 +259,7 @@ def config_callback(self, config, level): config.ignore_time_after_runstop_is_enabled self.ignore_time_after_runstop_is_disabled = \ config.ignore_time_after_runstop_is_disabled + self.speak_when_runstopped = config.speak_when_runstopped return config def run_stop_callback(self, msg): From eb278c274f8171f0aa990488cc01d964f9d0376e Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 9 Jun 2022 01:36:37 +0900 Subject: [PATCH 17/25] [audible_warning] Don't add heap at first --- jsk_tools/node_scripts/audible_warning.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 9d33a910a..a58f4eeca 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -104,6 +104,12 @@ def set_speak_interval(self, interval): def add(self, status_list): with self.lock: for status in status_list: + if is_leaf(status.name) is False: + continue + if rospy.Time.now().to_sec() \ + - self.previous_spoken_time[status.name] \ + < self.speak_interval: + continue heapq.heappush( self.status_list, (rospy.Time.now().to_sec(), status)) From 449ada1d9d7394516e81c698a722459b215d8ddd Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 9 Jun 2022 01:36:59 +0900 Subject: [PATCH 18/25] [audible_warning] Deserialize image --- jsk_tools/node_scripts/audible_warning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index a58f4eeca..7d2a34412 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -277,7 +277,7 @@ def run_stop_callback(self, msg): deserialized_sub = rospy.Subscriber( self.run_stop_topic, msg_class, self.run_stop_callback) self.run_stop_sub = deserialized_sub - return + msg = msg_class().deserialize(msg._buff) tm = rospy.Time.now() run_stop = self.run_stop_condition( self.run_stop_topic, msg, tm) From c141412197f51caf34a6858df09ab5d34e35f8aa Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 9 Jun 2022 01:37:34 +0900 Subject: [PATCH 19/25] [audible_warning] Fixed typo by adding () --- jsk_tools/node_scripts/audible_warning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 7d2a34412..5b23575ee 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -298,13 +298,13 @@ def diag_cb(self, msg): if self.ignore_time_after_runstop_is_enabled > 0.0: if self.run_stop_enabled_time is not None \ and ((rospy.Time.now() - - self.run_stop_enabled_time).to_sec < + self.run_stop_enabled_time).to_sec() < self.ignore_time_after_runstop_is_enabled): return if self.ignore_time_after_runstop_is_disabled > 0.0: if self.run_stop_disabled_time is not None \ and ((rospy.Time.now() - - self.run_stop_disabled_time).to_sec < + self.run_stop_disabled_time).to_sec() < self.ignore_time_after_runstop_is_disabled): return From adac4884f3f41d9bb3a34e4ac1f8a8d1113e7dd6 Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 9 Jun 2022 01:38:42 +0900 Subject: [PATCH 20/25] [audible_warning] Add loginfo to display runstop state --- jsk_tools/node_scripts/audible_warning.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 5b23575ee..ec048a451 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -283,9 +283,11 @@ def run_stop_callback(self, msg): self.run_stop_topic, msg, tm) if run_stop != self.run_stop: if run_stop is True: - self.run_stop_enabled_time = tm + self.run_stop_enabled_time = rospy.Time.now() + rospy.loginfo('Audible Warning: Runstop is enabled.') else: - self.run_stop_disabled_time = tm + self.run_stop_disabled_time = rospy.Time.now() + rospy.loginfo('Audible Warning: Runstop is disabled.') self.run_stop = run_stop def on_shutdown(self): From 42c3c3327c7ea85ce46bf837423b59926c5d494e Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 9 Jun 2022 01:39:20 +0900 Subject: [PATCH 21/25] [audible_warning] Add pseudo runstop publisher --- jsk_tools/sample/pseudo_runstop_publisher.py | 41 +++++++++++++++++++ .../sample/sample_audible_warning.launch | 14 ++++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100755 jsk_tools/sample/pseudo_runstop_publisher.py diff --git a/jsk_tools/sample/pseudo_runstop_publisher.py b/jsk_tools/sample/pseudo_runstop_publisher.py new file mode 100755 index 000000000..7091fc95f --- /dev/null +++ b/jsk_tools/sample/pseudo_runstop_publisher.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +from distutils.version import LooseVersion as version + +import pkg_resources +import rospy +import std_msgs.msg + + +class PseudoRunstopPublisher(object): + + def __init__(self): + duration_time = rospy.get_param('~duration_time', 30) + self.runstop = True + self.pub = rospy.Publisher('~runstop', std_msgs.msg.Bool, + queue_size=1) + kwargs = dict( + period=rospy.Duration(duration_time), + callback=self.publish_callback, + oneshot=False, + ) + if version(pkg_resources.get_distribution('rospy').version) \ + >= version('1.12.0'): + # on >=kinetic, it raises ROSTimeMovedBackwardsException + # when we use rosbag play --loop. + kwargs['reset'] = True + self.timer = rospy.Timer(**kwargs) + + def publish_callback(self, event): + if self.runstop is True: + rospy.loginfo('Runstop is enabled.') + else: + rospy.loginfo('Runstop is disabled.') + self.pub.publish(self.runstop) + self.runstop = not self.runstop + + +if __name__ == '__main__': + rospy.init_node('pseudo_runstop_publisher') + pub = PseudoRunstopPublisher() + rospy.spin() diff --git a/jsk_tools/sample/sample_audible_warning.launch b/jsk_tools/sample/sample_audible_warning.launch index 236c97e14..f9fb14ed0 100644 --- a/jsk_tools/sample/sample_audible_warning.launch +++ b/jsk_tools/sample/sample_audible_warning.launch @@ -9,6 +9,14 @@ output="screen"> + + + duration_time: 30 + + + @@ -27,8 +35,12 @@ output="screen" > - speak_interval: 0 + run_stop_topic: /pseudo_runstop_publisher/runstop + speak_when_runstopped: true + speak_interval: 0.0 seconds_to_start_speaking: 5 + ignore_time_after_runstop_is_enabled: 10.0 + ignore_time_after_runstop_is_disabled: 10.0 From ae46a33e03fae09ebc43b79635e709d095b60d16 Mon Sep 17 00:00:00 2001 From: iory Date: Tue, 14 Jun 2022 16:02:49 +0900 Subject: [PATCH 22/25] [audible_warning] Add prefix + error name to original text --- jsk_tools/node_scripts/audible_warning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index ec048a451..7f1659cbe 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -154,7 +154,7 @@ def run(self): sentence = multiple_whitespace_to_one(sentence) rospy.loginfo('audible warning error name "{}"'.format(e.name)) rospy.loginfo("audible warning talking: %s" % sentence) - self.pub_original_text.publish(e.message) + self.pub_original_text.publish(prefix + e.name + ' ' + e.message) self.pub_speak_text.publish( "audible warning talking: %s" % sentence) From a63720c9580e2218e4b9130672b44cd7df5189d9 Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 7 Jul 2022 02:05:18 +0900 Subject: [PATCH 23/25] [jsk_tools/audible_warning] Modified default speak rate to speed up. --- doc/jsk_tools/scripts/audible_warning.md | 4 ++-- jsk_tools/node_scripts/audible_warning.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/jsk_tools/scripts/audible_warning.md b/doc/jsk_tools/scripts/audible_warning.md index 75888827f..72849c0d8 100644 --- a/doc/jsk_tools/scripts/audible_warning.md +++ b/doc/jsk_tools/scripts/audible_warning.md @@ -23,9 +23,9 @@ Robots using diagnostics can use this node. ## Parameter -* `~speak_rate` (`Float`, default: `1.0`) +* `~speak_rate` (`Float`, default: `1.0 / 100.0`) - Rate of speak loop. + Rate of speak loop. If `~wait_speak` is `True`, wait until a robot finish speaking. * `~speak_interval` (`Float`, default: `120.0`) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 7f1659cbe..6da9ef6b5 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -176,7 +176,7 @@ def run(self): class AudibleWarning(object): def __init__(self): - speak_rate = rospy.get_param("~speak_rate", 1.0) + speak_rate = rospy.get_param("~speak_rate", 1.0 / 100.0) wait_speak = rospy.get_param("~wait_speak", True) language = rospy.get_param('~language', '') seconds_to_start_speaking = rospy.get_param( From 19b2b1522212dbc00a1dc6448276eb25f3b2c621 Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 7 Jul 2022 02:49:32 +0900 Subject: [PATCH 24/25] [jsk_tools/audible_warning] Add space to published original text. --- jsk_tools/node_scripts/audible_warning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 6da9ef6b5..4f9cb98dd 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -154,7 +154,7 @@ def run(self): sentence = multiple_whitespace_to_one(sentence) rospy.loginfo('audible warning error name "{}"'.format(e.name)) rospy.loginfo("audible warning talking: %s" % sentence) - self.pub_original_text.publish(prefix + e.name + ' ' + e.message) + self.pub_original_text.publish(prefix + ' ' + e.name + ' ' + e.message) self.pub_speak_text.publish( "audible warning talking: %s" % sentence) From f254d774d5a6d2ce9d1c745ad28eba7d8457d275 Mon Sep 17 00:00:00 2001 From: iory Date: Thu, 7 Jul 2022 03:04:28 +0900 Subject: [PATCH 25/25] [jsk_tools/audible_warning] Set wait_speak_duration_time to wait action server result. --- doc/jsk_tools/scripts/audible_warning.md | 4 ++++ jsk_tools/node_scripts/audible_warning.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/jsk_tools/scripts/audible_warning.md b/doc/jsk_tools/scripts/audible_warning.md index 72849c0d8..95fb4d3a9 100644 --- a/doc/jsk_tools/scripts/audible_warning.md +++ b/doc/jsk_tools/scripts/audible_warning.md @@ -49,6 +49,10 @@ Robots using diagnostics can use this node. This is useful for ignoring errors that occur when the robot starts. +* `~wait_speak_duration_time` (`Float`, default: `30.0`) + + Waiting time in `robotsound` action. + * `~enable` (`Bool`, default: `True`) If `True`, speak diagnositcs. If `False`, this node don't speak. diff --git a/jsk_tools/node_scripts/audible_warning.py b/jsk_tools/node_scripts/audible_warning.py index 4f9cb98dd..f1bd39845 100755 --- a/jsk_tools/node_scripts/audible_warning.py +++ b/jsk_tools/node_scripts/audible_warning.py @@ -181,6 +181,8 @@ def __init__(self): language = rospy.get_param('~language', '') seconds_to_start_speaking = rospy.get_param( '~seconds_to_start_speaking', 0) + wait_speak_duration_time = rospy.get_param( + '~wait_speak_duration_time', 30.0) # Wait until seconds_to_start_speaking the time has passed. self.run_stop_enabled_time = None @@ -209,7 +211,7 @@ def __init__(self): self.speak_thread = SpeakThread( speak_rate, wait_speak, language, - wait_speak_duration_time=seconds_to_start_speaking) + wait_speak_duration_time=wait_speak_duration_time) self.srv = Server(Config, self.config_callback) # run-stop