Skip to content

Commit 632157b

Browse files
bjsowaSpir0u
andauthored
fix: Handle action rejection and server timeout (backport #1139) (#1156)
* add server timeout exception * forward rejected goal handles The goal_response_cb should not throw an exception, so the exception is passed in the result and then raised in send_goal * return an empty result when result was None due to cancellation/abortion * fix lint error --------- Co-authored-by: Spir0u <28966599+Spir0u@users.noreply.github.com>
1 parent 231c6e7 commit 632157b

File tree

2 files changed

+13
-8
lines changed

2 files changed

+13
-8
lines changed

rosbridge_library/src/rosbridge_library/capabilities/advertise_action.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,6 @@ def done_callback(fut: Future) -> None:
9797
if fut.cancelled():
9898
goal.abort()
9999
self.protocol.log("info", f"Aborted goal {goal_id}")
100-
# Send an empty result to avoid stack traces
101-
fut.set_result(
102-
cast("ROSActionResultT", get_action_class(self.action_type).Result())
103-
)
104100
else:
105101
if goal_id not in self.goal_statuses:
106102
goal.abort()
@@ -132,7 +128,9 @@ def done_callback(fut: Future) -> None:
132128

133129
try:
134130
result = await future
135-
assert result is not None, "Action result cannot be None"
131+
if result is None:
132+
# Return empty result when cancelled/aborted
133+
return cast("ROSActionResultT", get_action_class(self.action_type).Result())
136134
return result
137135
finally:
138136
del self.goal_futures[goal_id]

rosbridge_library/src/rosbridge_library/internal/actions.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def args_to_action_goal_instance(inst: ROSMessage, args: list | dict[str, Any] |
148148
class SendGoal(Generic[ROSActionGoalT, ROSActionResultT, ROSActionFeedbackT]):
149149
"""Helper class to send action goals."""
150150

151-
result: GetResultServiceResponse[ROSActionResultT] | None = None
151+
result: GetResultServiceResponse[ROSActionResultT] | Exception | None = None
152152

153153
def __init__(self, server_timeout_time: float = 1.0, sleep_time: float = 0.001) -> None:
154154
self.server_timeout_time = server_timeout_time
@@ -164,7 +164,8 @@ def goal_response_cb(self, future: Future) -> None:
164164
assert self.goal_handle is not None
165165
if not self.goal_handle.accepted:
166166
msg = "Action goal was rejected"
167-
raise Exception(msg)
167+
self.result = Exception(msg)
168+
return
168169
result_future: Future = self.goal_handle.get_result_async()
169170
result_future.add_done_callback(self.get_result_cb)
170171

@@ -189,14 +190,20 @@ def send_goal(
189190

190191
self.result = None
191192
client = ActionClient(node_handle, action_class, action_name)
192-
client.wait_for_server(timeout_sec=self.server_timeout_time)
193+
if not client.wait_for_server(timeout_sec=self.server_timeout_time):
194+
msg = "No action server available"
195+
raise Exception(msg)
193196
send_goal_future = client.send_goal_async(inst, feedback_callback=feedback_cb) # type: ignore[arg-type]
194197
send_goal_future.add_done_callback(self.goal_response_cb)
195198

196199
while self.result is None:
197200
time.sleep(self.sleep_time)
198201

199202
client.destroy()
203+
204+
if isinstance(self.result, Exception):
205+
raise self.result
206+
200207
if self.result is not None:
201208
# Turn the response into JSON and pass to the callback
202209
json_response = extract_values(self.result)

0 commit comments

Comments
 (0)