Skip to content

Commit 555b267

Browse files
committed
Move goal acceptance logic back into execute_goal_request()
This trims the send_goal_response() function down to only be a wrapper around the rcl_action equivalent. In addition to improving logical separation, this also simplifies control flow when handling rejection.
1 parent 5cce85f commit 555b267

File tree

1 file changed

+99
-76
lines changed

1 file changed

+99
-76
lines changed

rclrs/src/action/server.rs

Lines changed: 99 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -175,100 +175,115 @@ where
175175
&mut request_id,
176176
&mut request_rmw as *mut RmwRequest<T> as *mut _,
177177
)
178-
}.ok()?;
178+
}
179+
.ok()?;
179180

180181
Ok((request_rmw, request_id))
181182
}
182183

183-
fn send_goal_response(&self, response: GoalResponse, request: &<<T::SendGoalService as Service>::Request as Message>::RmwMsg, mut request_id: rmw_request_id_t) -> Result<(), RclrsError> {
184-
let accepted = response != GoalResponse::Reject;
184+
fn send_goal_response(
185+
&self,
186+
mut request_id: rmw_request_id_t,
187+
accepted: bool,
188+
) -> Result<(), RclrsError> {
189+
type RmwResponse<T> = <<<T as rosidl_runtime_rs::ActionImpl>::SendGoalService as Service>::Response as Message>::RmwMsg;
190+
let mut response_rmw = RmwResponse::<T>::default();
191+
// TODO(nwn): Set the `accepted` field through a trait, similarly to how we extracted the UUID.
192+
// response_rmw.accepted = accepted;
193+
let handle = &*self.handle.lock();
194+
let result = unsafe {
195+
// SAFETY: The action server handle is locked and so synchronized with other
196+
// functions. The request_id and response message are uniquely owned, and so will
197+
// not mutate during this function call.
198+
// Also, when appropriate, `rcl_action_accept_new_goal()` has been called beforehand,
199+
// as specified in the `rcl_action` docs.
200+
rcl_action_send_goal_response(
201+
handle,
202+
&mut request_id,
203+
&mut response_rmw as *mut RmwResponse<T> as *mut _,
204+
)
205+
}
206+
.ok();
207+
match result {
208+
Ok(()) => Ok(()),
209+
Err(RclrsError::RclError {
210+
code: RclReturnCode::Timeout,
211+
..
212+
}) => {
213+
// TODO(nwn): Log an error and continue.
214+
// (See https://github.com/ros2/rclcpp/pull/2215 for reasoning.)
215+
Ok(())
216+
}
217+
_ => result,
218+
}
219+
}
220+
221+
fn execute_goal_request(&self) -> Result<(), RclrsError> {
222+
let (request, request_id) = match self.take_goal_request() {
223+
Ok(res) => res,
224+
Err(RclrsError::RclError {
225+
code: RclReturnCode::ServiceTakeFailed,
226+
..
227+
}) => {
228+
// Spurious wakeup – this may happen even when a waitset indicated that this
229+
// action was ready, so it shouldn't be an error.
230+
return Ok(());
231+
}
232+
Err(err) => return Err(err),
233+
};
234+
235+
let mut uuid = GoalUuid::default();
236+
rosidl_runtime_rs::ExtractUuid::extract_uuid(&request, &mut uuid.0);
237+
238+
let response: GoalResponse = {
239+
todo!("Optionally convert request to an idiomatic type for the user's callback.");
240+
todo!("Call self.goal_callback(uuid, request)");
241+
};
242+
243+
// Don't continue if the goal was rejected by the user.
244+
if response == GoalResponse::Reject {
245+
return self.send_goal_response(request_id, false);
246+
}
185247

186248
// SAFETY: No preconditions
187249
let mut goal_info = unsafe { rcl_action_get_zero_initialized_goal_info() };
188250
// Populate the goal UUID; the other fields will be populated by rcl_action later on.
189251
// TODO(nwn): Check this claim.
190-
rosidl_runtime_rs::ExtractUuid::extract_uuid(request, &mut goal_info.goal_id.uuid);
252+
goal_info.goal_id.uuid = uuid.0;
191253

192-
let goal_handle = if accepted {
254+
let goal_handle = {
193255
let server_handle = &mut *self.handle.lock();
194256
let goal_handle_ptr = unsafe {
195257
// SAFETY: The action server handle is locked and so synchronized with other
196258
// functions. The request_id and response message are uniquely owned, and so will
197259
// not mutate during this function call. The returned goal handle pointer should be
198260
// valid unless it is null.
199-
rcl_action_accept_new_goal(
200-
server_handle,
201-
&goal_info,
202-
)
261+
rcl_action_accept_new_goal(server_handle, &goal_info)
203262
};
204263
if goal_handle_ptr.is_null() {
205264
// Other than rcl_get_error_string(), there's no indication what happened.
206265
panic!("Failed to accept goal");
207266
} else {
208-
Some(ServerGoalHandle::<T>::new(goal_handle_ptr, todo!(""), GoalUuid(goal_info.goal_id.uuid)))
267+
ServerGoalHandle::<T>::new(
268+
goal_handle_ptr,
269+
todo!(""),
270+
GoalUuid(goal_info.goal_id.uuid),
271+
)
209272
}
210-
} else {
211-
None
212273
};
213274

214-
{
215-
type RmwResponse<T> = <<<T as rosidl_runtime_rs::ActionImpl>::SendGoalService as Service>::Response as Message>::RmwMsg;
216-
let mut response_rmw = RmwResponse::<T>::default();
217-
// TODO(nwn): Set the `accepted` field through a trait, similarly to how we extracted the UUID.
218-
// response_rmw.accepted = accepted;
219-
let handle = &*self.handle.lock();
220-
unsafe {
221-
// SAFETY: The action server handle is locked and so synchronized with other
222-
// functions. The request_id and response message are uniquely owned, and so will
223-
// not mutate during this function call.
224-
// Also, `rcl_action_accept_new_goal()` has been called beforehand, as specified in
225-
// the `rcl_action` docs.
226-
rcl_action_send_goal_response(
227-
handle,
228-
&mut request_id,
229-
&mut response_rmw as *mut RmwResponse<T> as *mut _,
230-
)
231-
}.ok()?; // TODO(nwn): Suppress RclReturnCode::Timeout?
232-
}
233-
234-
if let Some(goal_handle) = goal_handle {
235-
// Goal was accepted
236-
237-
// TODO: Add a UUID->goal_handle entry to a server goal map.
275+
self.send_goal_response(request_id, true)?;
238276

239-
if response == GoalResponse::AcceptAndExecute {
240-
goal_handle.execute()?;
241-
}
242-
243-
// TODO: Call publish_status()
277+
// TODO: Add a UUID->goal_handle entry to a server goal map.
244278

245-
// TODO: Call the goal_accepted callback
279+
if response == GoalResponse::AcceptAndExecute {
280+
goal_handle.execute()?;
246281
}
247282

248-
Ok(())
249-
}
283+
self.publish_status()?;
250284

251-
fn execute_goal_request(&self) -> Result<(), RclrsError> {
252-
let (request, request_id) = match self.take_goal_request() {
253-
Ok(res) => res,
254-
Err(RclrsError::RclError { code: RclReturnCode::ServiceTakeFailed, .. }) => {
255-
// Spurious wakeup – this may happen even when a waitset indicated that this
256-
// action was ready, so it shouldn't be an error.
257-
return Ok(());
258-
},
259-
Err(err) => return Err(err),
260-
};
261-
262-
let response: GoalResponse =
263-
{
264-
let mut uuid = GoalUuid::default();
265-
rosidl_runtime_rs::ExtractUuid::extract_uuid(&request, &mut uuid.0);
266-
267-
todo!("Optionally convert request to an idiomatic type for the user's callback.");
268-
todo!("Call self.goal_callback(uuid, request)");
269-
};
270-
271-
self.send_goal_response(response, &request, request_id)?;
285+
// TODO: Call the user's goal_accepted callback.
286+
todo!("Call self.accepted_callback(goal_handle)");
272287

273288
Ok(())
274289
}
@@ -286,26 +301,34 @@ where
286301
}
287302

288303
fn publish_status(&self) -> Result<(), RclrsError> {
289-
let mut goal_statuses = DropGuard::new(unsafe {
290-
// SAFETY: No preconditions
291-
rcl_action_get_zero_initialized_goal_status_array()
292-
}, |mut goal_status| unsafe {
293-
// SAFETY: The goal_status array is either zero-initialized and empty or populated by
294-
// `rcl_action_get_goal_status_array`. In either case, it can be safely finalized.
295-
rcl_action_goal_status_array_fini(&mut goal_status);
296-
});
304+
let mut goal_statuses = DropGuard::new(
305+
unsafe {
306+
// SAFETY: No preconditions
307+
rcl_action_get_zero_initialized_goal_status_array()
308+
},
309+
|mut goal_statuses| unsafe {
310+
// SAFETY: The goal_status array is either zero-initialized and empty or populated by
311+
// `rcl_action_get_goal_status_array`. In either case, it can be safely finalized.
312+
rcl_action_goal_status_array_fini(&mut goal_statuses);
313+
},
314+
);
297315

298316
unsafe {
299317
// SAFETY: The action server is locked through the handle and goal_statuses is
300318
// zero-initialized.
301319
rcl_action_get_goal_status_array(&*self.handle.lock(), &mut *goal_statuses)
302-
}.ok()?;
320+
}
321+
.ok()?;
303322

304323
unsafe {
305324
// SAFETY: The action server is locked through the handle and goal_statuses.msg is a
306325
// valid `action_msgs__msg__GoalStatusArray` by construction.
307-
rcl_action_publish_status(&*self.handle.lock(), &goal_statuses.msg as *const _ as *const std::ffi::c_void)
308-
}.ok()
326+
rcl_action_publish_status(
327+
&*self.handle.lock(),
328+
&goal_statuses.msg as *const _ as *const std::ffi::c_void,
329+
)
330+
}
331+
.ok()
309332
}
310333
}
311334

0 commit comments

Comments
 (0)