55import time
66import signal
77import sys
8+ import threading
89import weakref
910from typing import TYPE_CHECKING , Literal , Optional , overload
1011
3637)
3738from rcl_interfaces .srv import ListParameters
3839
40+ #####
41+ #
42+ # There are 4 use cases that should exit the user code:
43+ #
44+ # 1) Running "while true" (and CTRL-C) from commandline
45+ # 2) Running "for 10" from commandline
46+ # 3) Running "while true" (and stop/CTRL-C) from web interface
47+ # 4) Running "for 10" from web interface
48+ #
49+ #####
50+
51+
3952mirte = {}
4053
4154# No QoS Profiles are set, but this might not be required, since they might already behave like ROS 1 persistant.
@@ -101,6 +114,8 @@ def __init__(
101114
102115 # Stop robot when exited
103116 rclpy .get_default_context ().on_shutdown (self ._at_exit )
117+ self ._stopping = False
118+ self ._lock = threading .Lock ()
104119
105120 self .CONTROLLER = "diffbot_base_controller"
106121
@@ -181,6 +196,7 @@ def __init__(
181196 SetMotorSpeed ,
182197 self ._hardware_namespace + "/motor/" + motor + "/set_speed" ,
183198 )
199+ self .motor_services [motor ].wait_for_service ()
184200
185201 # Service for motor speed
186202
@@ -211,6 +227,7 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
211227 SetServoAngle ,
212228 self ._hardware_namespace + "/servo/" + servo + "/set_angle" ,
213229 )
230+ self .servo_services [servo ].wait_for_service ()
214231
215232 ## Sensors
216233 ## The sensors are now just using a blocking service call. This is intentionally
@@ -238,6 +255,7 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
238255 GetRange ,
239256 self ._hardware_namespace + "/distance/" + sensor + "/get_range" ,
240257 )
258+ self .distance_services [sensor ].wait_for_service ()
241259
242260 # Services for oled
243261 oled_future = list_parameters .call_async (
@@ -264,6 +282,8 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
264282 self ._hardware_namespace + "/oled/" + oled + "/set_file" ,
265283 ),
266284 }
285+ self .oled_services [oled ]["text" ].wait_for_service ()
286+ self .oled_services [oled ]["file" ].wait_for_service ()
267287
268288 # Services for intensity sensors (TODO: how to expose the digital version?)
269289 intensity_future = list_parameters .call_async (
@@ -302,6 +322,7 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
302322 + sensor
303323 + "/get_analog" ,
304324 )
325+ self .intensity_services [sensor ].wait_for_service ()
305326 if (
306327 self ._hardware_namespace + "/intensity/" + sensor + "/get_digital"
307328 in service_list
@@ -318,6 +339,7 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
318339 + "/get_digital" ,
319340 )
320341 )
342+ self .intensity_services [sensor + "_digital" ].wait_for_service ()
321343
322344 # Services for encoder sensors
323345 encoder_future = list_parameters .call_async (
@@ -338,6 +360,7 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
338360 GetEncoder ,
339361 self ._hardware_namespace + "/encoder/" + sensor + "/get_encoder" ,
340362 )
363+ self .encoder_services [sensor ].wait_for_service ()
341364
342365 # Services for keypad sensors
343366 keypad_future = list_parameters .call_async (
@@ -358,6 +381,7 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
358381 GetKeypad ,
359382 self ._hardware_namespace + "/keypad/" + sensor + "/get_key" ,
360383 )
384+ self .keypad_services [sensor ].wait_for_service ()
361385
362386 # Services for color sensors
363387 color_future = list_parameters .call_async (
@@ -384,6 +408,9 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
384408 self ._hardware_namespace + "/color/" + sensor + "/get_hsl" ,
385409 ),
386410 }
411+ self .color_services [sensor ]["RGBW" ].wait_for_service ()
412+ self .color_services [sensor ]["HSL" ].wait_for_service ()
413+
387414
388415 self ._node .destroy_client (list_parameters )
389416
@@ -409,10 +436,17 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
409436 def _call_service (
410437 self , client : rclpy .client .Client , request : rclpy .client .SrvTypeRequest
411438 ) -> rclpy .client .SrvTypeResponse :
412- client .wait_for_service ()
413439
414- future_response = client .call_async (request )
415- rclpy .spin_until_future_complete (self ._node , future_response )
440+ with self ._lock :
441+ future_response = client .call_async (request )
442+ while not future_response .done () and not self ._stopping :
443+ rclpy .spin_once (self ._node , timeout_sec = 0.1 )
444+
445+ if self ._stopping :
446+ with self ._lock :
447+ self ._stopping = False
448+ self ._at_exit ()
449+
416450 return future_response .result ()
417451
418452 # FIXME: Check if services are available, if not don't hard error on:
@@ -824,8 +858,13 @@ def stop(self) -> None:
824858 for motor in self .motors :
825859 self .setMotorSpeed (motor , 0 )
826860
861+
827862 def _signal_handler (self , sig , frame ):
828- self ._at_exit ()
863+ self ._stopping = True
864+
865+ if (not self ._lock .locked ()):
866+ self ._at_exit ()
867+
829868
830869 def _at_exit (self ) -> None :
831870 self .stop ()
0 commit comments