Skip to content

Commit d0cd6d3

Browse files
committed
feat: Add an option to use EventsExecutor
1 parent 728ed0d commit d0cd6d3

File tree

3 files changed

+63
-19
lines changed

3 files changed

+63
-19
lines changed

rosbridge_server/CMakeLists.txt

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,36 @@ install(FILES
2222

2323
if(BUILD_TESTING)
2424
find_package(launch_testing_ament_cmake REQUIRED)
25-
add_launch_test(test/websocket/advertise_action.test.py)
26-
add_launch_test(test/websocket/advertise_action_feedback.test.py)
27-
add_launch_test(test/websocket/advertise_service.test.py)
28-
add_launch_test(test/websocket/call_service.test.py)
29-
add_launch_test(test/websocket/send_action_goal.test.py)
30-
add_launch_test(test/websocket/smoke.test.py)
31-
add_launch_test(test/websocket/transient_local_publisher.test.py)
32-
add_launch_test(test/websocket/best_effort_publisher.test.py)
33-
add_launch_test(test/websocket/multiple_subscribers_raw.test.py)
25+
26+
# Run each test with both SingleThreadedExecutor and EventsExecutor
27+
set(TEST_FILES
28+
test/websocket/advertise_action.test.py
29+
test/websocket/advertise_action_feedback.test.py
30+
test/websocket/advertise_service.test.py
31+
test/websocket/call_service.test.py
32+
test/websocket/send_action_goal.test.py
33+
test/websocket/smoke.test.py
34+
test/websocket/transient_local_publisher.test.py
35+
test/websocket/best_effort_publisher.test.py
36+
test/websocket/multiple_subscribers_raw.test.py
37+
)
38+
39+
foreach(TEST_FILE ${TEST_FILES})
40+
# Extract base name for test naming
41+
get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
42+
43+
# Run with SingleThreadedExecutor (default)
44+
add_launch_test(${TEST_FILE}
45+
TARGET ${TEST_NAME}_singlethreaded
46+
ARGS "use_events_executor:=false"
47+
)
48+
49+
# Run with EventsExecutor
50+
add_launch_test(${TEST_FILE}
51+
TARGET ${TEST_NAME}_events
52+
ARGS "use_events_executor:=true"
53+
)
54+
endforeach()
3455

3556
find_package(ament_cmake_mypy REQUIRED)
3657
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, "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
@@ -47,21 +49,30 @@ def onMessage(self, payload: str, binary: bool) -> None:
4749
self.message_handler(payload if binary else json.loads(payload))
4850

4951

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

6677

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

0 commit comments

Comments
 (0)