Skip to content

Commit d15f334

Browse files
committed
feat: Add an option to use EventsExecutor (backport #1157)
1 parent a8870f6 commit d15f334

File tree

3 files changed

+63
-20
lines changed

3 files changed

+63
-20
lines changed

rosbridge_server/CMakeLists.txt

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,36 @@ if(BUILD_TESTING)
3030
TIMEOUT 60
3131
)
3232

33-
add_launch_test(test/websocket/advertise_action.test.py)
34-
add_launch_test(test/websocket/advertise_action_feedback.test.py)
35-
add_launch_test(test/websocket/advertise_publish.test.py)
36-
add_launch_test(test/websocket/advertise_service.test.py)
37-
add_launch_test(test/websocket/call_service.test.py)
38-
add_launch_test(test/websocket/send_action_goal.test.py)
39-
add_launch_test(test/websocket/smoke.test.py)
40-
add_launch_test(test/websocket/transient_local_publisher.test.py)
41-
add_launch_test(test/websocket/best_effort_publisher.test.py)
42-
add_launch_test(test/websocket/multiple_subscribers_raw.test.py)
33+
# Run each test with both SingleThreadedExecutor and EventsExecutor
34+
set(TEST_FILES
35+
test/websocket/advertise_action.test.py
36+
test/websocket/advertise_action_feedback.test.py
37+
test/websocket/advertise_publish.test.py
38+
test/websocket/advertise_service.test.py
39+
test/websocket/call_service.test.py
40+
test/websocket/send_action_goal.test.py
41+
test/websocket/smoke.test.py
42+
test/websocket/transient_local_publisher.test.py
43+
test/websocket/best_effort_publisher.test.py
44+
test/websocket/multiple_subscribers_raw.test.py
45+
)
46+
47+
foreach(TEST_FILE ${TEST_FILES})
48+
# Extract base name for test naming
49+
get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
50+
51+
# Run with SingleThreadedExecutor (default)
52+
add_launch_test(${TEST_FILE}
53+
TARGET ${TEST_NAME}_singlethreaded
54+
ARGS "use_events_executor:=false"
55+
)
56+
57+
# Run with EventsExecutor
58+
add_launch_test(${TEST_FILE}
59+
TARGET ${TEST_NAME}_events
60+
ARGS "use_events_executor:=true"
61+
)
62+
endforeach()
4363

4464
find_package(ament_cmake_mypy REQUIRED)
4565
ament_mypy()

rosbridge_server/scripts/rosbridge_websocket.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import rclpy
4545
from rcl_interfaces.msg import ParameterDescriptor
4646
from rclpy.executors import SingleThreadedExecutor
47+
from rclpy.experimental import EventsExecutor
4748
from rclpy.node import Node
4849
from rclpy.utilities import remove_ros_args
4950
from tornado.httpserver import HTTPServer
@@ -69,6 +70,8 @@
6970
("websocket_ping_timeout", float, 30.0, "Timeout in seconds for WebSocket ping responses."),
7071
# Websocket handler parameters
7172
("use_compression", bool, False, "Enable compression for WebSocket messages."),
73+
# Executor parameters
74+
("use_events_executor", bool, False, "Use EventsExecutor instead of SingleThreadedExecutor."),
7275
)
7376

7477
PROTOCOL_PARAMETERS = (
@@ -195,6 +198,11 @@ def _handle_parameters(self) -> None:
195198
self.get_parameter("use_compression").get_parameter_value().bool_value
196199
)
197200

201+
# Executor parameters
202+
self.use_events_executor = (
203+
self.get_parameter("use_events_executor").get_parameter_value().bool_value
204+
)
205+
198206
def _start_server(self) -> None:
199207
handlers = [(r"/", RosbridgeWebSocket), (r"", RosbridgeWebSocket)]
200208
if self.url_path != "/":
@@ -231,7 +239,11 @@ async def async_main() -> None:
231239

232240
node = RosbridgeWebsocketNode()
233241

234-
executor = SingleThreadedExecutor()
242+
if node.use_events_executor:
243+
executor = EventsExecutor()
244+
else:
245+
executor = SingleThreadedExecutor()
246+
235247
executor.add_node(node)
236248

237249
spin_thread = threading.Thread(target=executor.spin)

rosbridge_server/test/websocket/common.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import launch_ros
88
import rclpy
99
from autobahn.twisted.websocket import WebSocketClientFactory, WebSocketClientProtocol
10+
from launch.actions import DeclareLaunchArgument
1011
from launch.launch_description import LaunchDescription
12+
from launch.substitutions import LaunchConfiguration
1113
from launch_testing.actions import ReadyToTest
1214
from rcl_interfaces.srv import GetParameters
1315
from rclpy.executors import SingleThreadedExecutor
@@ -49,21 +51,30 @@ def onMessage(self, payload: str, binary: bool) -> None: # noqa: N802
4951
self.message_handler(payload if binary else json.loads(payload))
5052

5153

52-
def _generate_node() -> launch_ros.actions.Node:
53-
return launch_ros.actions.Node(
54-
executable="rosbridge_websocket",
55-
package="rosbridge_server",
56-
parameters=[{"port": 0}],
57-
)
58-
59-
6054
def generate_test_description() -> LaunchDescription:
6155
"""
6256
Generate a launch description that runs the websocket server.
6357
6458
Re-export this from a test file and use add_launch_test() to run the test.
59+
This supports parameterization via the 'use_events_executor' launch argument.
6560
"""
66-
return LaunchDescription([_generate_node(), ReadyToTest()])
61+
return LaunchDescription(
62+
[
63+
DeclareLaunchArgument(
64+
"use_events_executor",
65+
default_value="false",
66+
description="Use EventsExecutor instead of SingleThreadedExecutor",
67+
),
68+
launch_ros.actions.Node(
69+
executable="rosbridge_websocket",
70+
package="rosbridge_server",
71+
parameters=[
72+
{"port": 0, "use_events_executor": LaunchConfiguration("use_events_executor")}
73+
],
74+
),
75+
ReadyToTest(),
76+
]
77+
)
6778

6879

6980
async def get_server_port(node: Node) -> int:

0 commit comments

Comments
 (0)