|
34 | 34 | from __future__ import annotations |
35 | 35 |
|
36 | 36 | import argparse |
| 37 | +import asyncio |
| 38 | +import signal |
37 | 39 | import sys |
| 40 | +import threading |
38 | 41 | import time |
39 | 42 | from typing import TYPE_CHECKING, cast |
40 | 43 |
|
41 | 44 | import rclpy |
42 | 45 | from rcl_interfaces.msg import ParameterDescriptor |
| 46 | +from rclpy.executors import SingleThreadedExecutor |
43 | 47 | from rclpy.node import Node |
44 | 48 | from rclpy.utilities import remove_ros_args |
45 | 49 | from tornado.httpserver import HTTPServer |
46 | | -from tornado.ioloop import IOLoop, PeriodicCallback |
47 | 50 | from tornado.netutil import bind_sockets |
48 | 51 | from tornado.web import Application |
49 | 52 |
|
|
53 | 56 | from tornado.routing import _RuleList |
54 | 57 |
|
55 | 58 |
|
56 | | -def start_hook() -> None: |
57 | | - IOLoop.instance().start() |
58 | | - |
59 | | - |
60 | | -def shutdown_hook() -> None: |
61 | | - IOLoop.instance().stop() |
62 | | - |
63 | | - |
64 | 59 | SERVER_PARAMETERS = ( |
65 | 60 | # Server parameters |
66 | 61 | ("port", int, 9090, "Port to listen on for WebSocket connections."), |
@@ -130,6 +125,7 @@ def __init__(self) -> None: |
130 | 125 |
|
131 | 126 | RosbridgeWebSocket.node_handle = self |
132 | 127 | RosbridgeWebSocket.client_manager = ClientManager(self) |
| 128 | + RosbridgeWebSocket.event_loop = asyncio.get_event_loop() |
133 | 129 |
|
134 | 130 | self._handle_parameters() |
135 | 131 |
|
@@ -227,30 +223,42 @@ def _start_server(self) -> None: |
227 | 223 | time.sleep(self.retry_startup_delay) |
228 | 224 |
|
229 | 225 |
|
230 | | -def main() -> None: |
231 | | - rclpy.init() |
| 226 | +async def async_main() -> None: |
| 227 | + rclpy.init(args=sys.argv, signal_handler_options=rclpy.signals.SignalHandlerOptions.NO) |
| 228 | + |
232 | 229 | node = RosbridgeWebsocketNode() |
233 | 230 |
|
234 | | - executor = rclpy.executors.SingleThreadedExecutor() |
| 231 | + executor = SingleThreadedExecutor() |
235 | 232 | executor.add_node(node) |
236 | 233 |
|
237 | | - def spin_ros() -> None: |
238 | | - if not rclpy.ok(): |
239 | | - shutdown_hook() |
| 234 | + spin_thread = threading.Thread(target=executor.spin) |
| 235 | + spin_thread.start() |
| 236 | + |
| 237 | + loop = asyncio.get_running_loop() |
| 238 | + stop_event = asyncio.Event() |
| 239 | + signal_handled = False |
| 240 | + |
| 241 | + def handle_signal() -> None: |
| 242 | + nonlocal signal_handled |
| 243 | + if signal_handled: |
240 | 244 | return |
241 | | - executor.spin_once(timeout_sec=0.01) |
242 | | - |
243 | | - spin_callback = PeriodicCallback(spin_ros, 1) |
244 | | - spin_callback.start() |
245 | | - try: |
246 | | - start_hook() |
247 | | - node.destroy_node() |
248 | | - rclpy.shutdown() |
249 | | - except KeyboardInterrupt: |
250 | 245 | print("Exiting due to SIGINT") |
251 | | - finally: |
252 | | - spin_callback.stop() |
253 | | - shutdown_hook() # shutdown hook to stop the server |
| 246 | + stop_event.set() |
| 247 | + executor.shutdown() |
| 248 | + signal_handled = True |
| 249 | + |
| 250 | + for sig in (signal.SIGINT, signal.SIGTERM): |
| 251 | + loop.add_signal_handler(sig, handle_signal) |
| 252 | + |
| 253 | + await stop_event.wait() |
| 254 | + spin_thread.join() |
| 255 | + |
| 256 | + node.destroy_node() |
| 257 | + rclpy.shutdown() |
| 258 | + |
| 259 | + |
| 260 | +def main() -> None: |
| 261 | + asyncio.run(async_main()) |
254 | 262 |
|
255 | 263 |
|
256 | 264 | if __name__ == "__main__": |
|
0 commit comments