Skip to content

Commit 8c7fb71

Browse files
committed
Change action goal sending to async and add rosbridge_server test
1 parent 76debda commit 8c7fb71

File tree

5 files changed

+105
-10
lines changed

5 files changed

+105
-10
lines changed

rosbridge_library/src/rosbridge_library/capabilities/advertise_action.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
# POSSIBILITY OF SUCH DAMAGE.
3232

3333
import fnmatch
34-
import time
3534

3635
import rclpy
3736
from rclpy.action import ActionServer
@@ -67,11 +66,12 @@ def next_id(self):
6766
self.id_counter += 1
6867
return id
6968

70-
def execute_callback(self, goal):
69+
async def execute_callback(self, goal):
7170
# generate a unique ID
7271
goal_id = f"action_goal:{self.action_name}:{self.next_id()}"
7372

7473
future = rclpy.task.Future()
74+
future.add_done_callback(lambda _: goal.succeed())
7575
self.goal_handles[goal_id] = goal
7676
self.goal_futures[goal_id] = future
7777

@@ -85,13 +85,11 @@ def execute_callback(self, goal):
8585
}
8686
self.protocol.send(goal_message)
8787

88-
while not future.done():
89-
time.sleep(self.sleep_time)
90-
result = future.result()
91-
goal.succeed()
92-
del self.goal_futures[goal_id]
93-
del self.goal_handles[goal_id]
94-
return result
88+
try:
89+
return await future
90+
finally:
91+
del self.goal_futures[goal_id]
92+
del self.goal_handles[goal_id]
9593

9694
def handle_feedback(self, goal_id, feedback):
9795
"""

rosbridge_library/src/rosbridge_library/capabilities/send_action_goal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def _failure(self, cid, action, exc):
160160
# send response with result: false
161161
outgoing_message = {
162162
"op": "action_result",
163-
"service": action,
163+
"action": action,
164164
"values": str(exc),
165165
"result": False,
166166
}

rosbridge_server/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ install(FILES
2323

2424
if(BUILD_TESTING)
2525
find_package(launch_testing_ament_cmake REQUIRED)
26+
add_launch_test(test/websocket/advertise_action.test.py)
2627
add_launch_test(test/websocket/advertise_service.test.py)
2728
add_launch_test(test/websocket/call_service.test.py)
2829
add_launch_test(test/websocket/smoke.test.py)

rosbridge_server/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<exec_depend>rosbridge_msgs</exec_depend>
2525
<exec_depend>rosapi</exec_depend>
2626

27+
<test_depend>example_interfaces</test_depend>
2728
<test_depend>python3-autobahn</test_depend>
2829
<test_depend>launch</test_depend>
2930
<test_depend>launch_ros</test_depend>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
import unittest
5+
6+
from example_interfaces.action import Fibonacci
7+
from rclpy.action import ActionClient
8+
from rclpy.node import Node
9+
from twisted.python import log
10+
11+
sys.path.append(os.path.dirname(__file__)) # enable importing from common.py in this directory
12+
13+
import common # noqa: E402
14+
from common import expect_messages, websocket_test # noqa: E402
15+
16+
log.startLogging(sys.stderr)
17+
18+
generate_test_description = common.generate_test_description
19+
20+
21+
class TestAdvertiseAction(unittest.TestCase):
22+
def goal1_response_callback(self, future):
23+
goal_handle = future.result()
24+
if not goal_handle.accepted:
25+
return
26+
self.goal1_result_future = goal_handle.get_result_async()
27+
28+
def goal2_response_callback(self, future):
29+
goal_handle = future.result()
30+
if not goal_handle.accepted:
31+
return
32+
self.goal2_result_future = goal_handle.get_result_async()
33+
34+
@websocket_test
35+
async def test_two_concurrent_calls(self, node: Node, make_client):
36+
ws_client = await make_client()
37+
ws_client.sendJson(
38+
{
39+
"op": "advertise_action",
40+
"action": "/test_fibonacci_action",
41+
"type": "example_interfaces/Fibonacci",
42+
}
43+
)
44+
client = ActionClient(node, Fibonacci, "/test_fibonacci_action")
45+
client.wait_for_server()
46+
47+
requests_future, ws_client.message_handler = expect_messages(
48+
2, "WebSocket", node.get_logger()
49+
)
50+
requests_future.add_done_callback(lambda _: node.executor.wake())
51+
52+
self.goal1_result_future = None
53+
goal1_future = client.send_goal_async(Fibonacci.Goal(order=3))
54+
goal1_future.add_done_callback(self.goal1_response_callback)
55+
56+
self.goal2_result_future = None
57+
goal2_future = client.send_goal_async(Fibonacci.Goal(order=5))
58+
goal2_future.add_done_callback(self.goal2_response_callback)
59+
60+
requests = await requests_future
61+
62+
self.assertEqual(requests[0]["op"], "send_action_goal")
63+
self.assertEqual(requests[0]["action"], "/test_fibonacci_action")
64+
self.assertEqual(requests[0]["action_type"], "example_interfaces/Fibonacci")
65+
self.assertEqual(requests[0]["args"], {"order": 3})
66+
ws_client.sendJson(
67+
{
68+
"op": "action_result",
69+
"action": "/test_fibonacci_action",
70+
"values": {"sequence": [1, 1, 2]},
71+
"id": requests[0]["id"],
72+
"result": True,
73+
}
74+
)
75+
76+
self.assertEqual(requests[1]["op"], "send_action_goal")
77+
self.assertEqual(requests[1]["action"], "/test_fibonacci_action")
78+
self.assertEqual(requests[1]["action_type"], "example_interfaces/Fibonacci")
79+
self.assertEqual(requests[1]["args"], {"order": 5})
80+
ws_client.sendJson(
81+
{
82+
"op": "action_result",
83+
"action": "/test_fibonacci_action",
84+
"values": {"sequence": [1, 1, 2, 3, 5]},
85+
"id": requests[1]["id"],
86+
"result": True,
87+
}
88+
)
89+
90+
result1 = await self.goal1_result_future
91+
self.assertEqual(result1.result, Fibonacci.Result(sequence=[1, 1, 2]))
92+
result2 = await self.goal2_result_future
93+
self.assertEqual(result2.result, Fibonacci.Result(sequence=[1, 1, 2, 3, 5]))
94+
95+
node.destroy_client(client)

0 commit comments

Comments
 (0)