Skip to content

Commit

Permalink
Implement client-side trait methods for action messages
Browse files Browse the repository at this point in the history
This adds methods to ActionImpl for creating and accessing
action-specific message types. These are needed by the rclrs
ActionClient to generically read and write RMW messages.

Due to issues with qualified paths in certain places
(rust-lang/rust#86935), the generator now
refers directly to its service and message types rather than going
through associated types of the various traits. This also makes the
generated code a little easier to read, with the trait method signatures
from rosidl_runtime_rs still enforcing type-safety.
  • Loading branch information
nwn committed Sep 28, 2024
1 parent cbc1a10 commit 9e6f572
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 23 deletions.
12 changes: 5 additions & 7 deletions rclrs/src/action/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,7 @@ where
mut request_id: rmw_request_id_t,
accepted: bool,
) -> Result<(), RclrsError> {
type RmwResponse<T> = <<<T as ActionImpl>::SendGoalService as Service>::Response as Message>::RmwMsg;
let mut response_rmw = RmwResponse::<T>::default();
<T as ActionImpl>::set_goal_response_accepted(&mut response_rmw, accepted);
let mut response_rmw = <T as ActionImpl>::create_goal_response(accepted, (0, 0));
let handle = &*self.handle.lock();
let result = unsafe {
// SAFETY: The action server handle is locked and so synchronized with other
Expand All @@ -210,7 +208,7 @@ where
rcl_action_send_goal_response(
handle,
&mut request_id,
&mut response_rmw as *mut RmwResponse<T> as *mut _,
&mut response_rmw as *mut _ as *mut _,
)
}
.ok();
Expand Down Expand Up @@ -242,7 +240,7 @@ where
Err(err) => return Err(err),
};

let uuid = GoalUuid(<T as ActionImpl>::get_goal_request_uuid(&request));
let uuid = GoalUuid(*<T as ActionImpl>::get_goal_request_uuid(&request));

let response: GoalResponse = {
todo!("Optionally convert request to an idiomatic type for the user's callback.");
Expand Down Expand Up @@ -522,7 +520,7 @@ where
Err(err) => return Err(err),
};

let uuid = GoalUuid(<T as ActionImpl>::get_result_request_uuid(&request));
let uuid = GoalUuid(*<T as ActionImpl>::get_result_request_uuid(&request));

let goal_exists = unsafe {
// SAFETY: No preconditions
Expand Down Expand Up @@ -640,7 +638,7 @@ where

pub(crate) fn publish_feedback(&self, goal_id: &GoalUuid, feedback: &<T as rosidl_runtime_rs::Action>::Feedback) -> Result<(), RclrsError> {
let feedback_rmw = <<T as rosidl_runtime_rs::Action>::Feedback as Message>::into_rmw_message(std::borrow::Cow::Borrowed(feedback));
let mut feedback_msg = <T as rosidl_runtime_rs::ActionImpl>::create_feedback_message(&goal_id.0, &*feedback_rmw);
let mut feedback_msg = <T as rosidl_runtime_rs::ActionImpl>::create_feedback_message(&goal_id.0, feedback_rmw.into_owned());
unsafe {
// SAFETY: The action server is locked through the handle, meaning that no other
// non-thread-safe functions can be called on it at the same time. The feedback_msg is
Expand Down
67 changes: 56 additions & 11 deletions rosidl_generator_rs/resource/action.rs.em
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ from rosidl_parser.definition import (
ACTION_GOAL_SUFFIX,
ACTION_RESULT_SERVICE_SUFFIX,
ACTION_RESULT_SUFFIX,
SERVICE_REQUEST_MESSAGE_SUFFIX,
SERVICE_RESPONSE_MESSAGE_SUFFIX,
)

action_msg_specs = []
Expand Down Expand Up @@ -100,31 +102,74 @@ impl rosidl_runtime_rs::ActionImpl for @(type_name) {
type CancelGoalService = action_msgs::srv::rmw::CancelGoal;
type GetResultService = crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX);

fn get_goal_request_uuid(request: &<<Self::SendGoalService as rosidl_runtime_rs::Service>::Request as rosidl_runtime_rs::Message>::RmwMsg) -> [u8; 16] {
request.goal_id.uuid
fn create_goal_request(goal_id: &[u8; 16], goal: crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SUFFIX)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id },
goal,
}
}

fn get_goal_request_uuid(request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX)) -> &[u8; 16] {
&request.goal_id.uuid
}

fn create_goal_response(accepted: bool, stamp: (i32, u32)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
accepted,
stamp: builtin_interfaces::msg::rmw::Time {
sec: stamp.0,
nanosec: stamp.1,
},
}
}

fn get_goal_response_accepted(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> bool {
response.accepted
}

fn set_goal_response_accepted(response: &mut <<Self::SendGoalService as rosidl_runtime_rs::Service>::Response as rosidl_runtime_rs::Message>::RmwMsg, accepted: bool) {
response.accepted = accepted;
fn get_goal_response_stamp(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> (i32, u32) {
(response.stamp.sec, response.stamp.nanosec)
}

fn create_feedback_message(goal_id: &[u8; 16], feedback: &<<Self as rosidl_runtime_rs::Action>::Feedback as rosidl_runtime_rs::Message>::RmwMsg) -> <Self::FeedbackMessage as rosidl_runtime_rs::Message>::RmwMsg {
let mut message = <Self::FeedbackMessage as rosidl_runtime_rs::Message>::RmwMsg::default();
fn create_feedback_message(goal_id: &[u8; 16], feedback: crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX) {
let mut message = crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)::default();
message.goal_id.uuid = *goal_id;
message.feedback = feedback.clone();
message.feedback = feedback;
message
}

fn get_result_request_uuid(request: &<<Self::GetResultService as rosidl_runtime_rs::Service>::Request as rosidl_runtime_rs::Message>::RmwMsg) -> [u8; 16] {
request.goal_id.uuid
fn get_feedback_message_uuid(feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)) -> &[u8; 16] {
&feedback.goal_id.uuid
}

fn get_feedback_message_feedback(feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX) {
&feedback.feedback
}

fn create_result_response(status: i8, result: <<Self as Action>::Result as Message>::RmwMsg) -> <<Self::GetResultService as Service>::Response as Message>::RmwMsg {
<<Self::GetResultService as rosidl_runtime_rs::Service>::Response as rosidl_runtime_rs::Message>::RmwMsg {
fn create_result_request(goal_id: &[u8; 16]) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id },
}
}

fn get_result_request_uuid(request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX)) -> &[u8; 16] {
&request.goal_id.uuid
}

fn create_result_response(status: i8, result: crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
status,
result,
}
}

fn get_result_response_result(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX) {
&response.result
}

fn get_result_response_status(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> i8 {
response.status
}
}

@[end for]
34 changes: 29 additions & 5 deletions rosidl_runtime_rs/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,42 @@ pub trait ActionImpl: 'static + Action {
/// The get_result service associated with this action.
type GetResultService: Service;

/// Create a goal request message with the given UUID and goal.
fn create_goal_request(goal_id: &[u8; 16], goal: <<Self as Action>::Goal as Message>::RmwMsg) -> <<Self::SendGoalService as Service>::Request as Message>::RmwMsg;

/// Get the UUID of a goal request.
fn get_goal_request_uuid(request: &<<Self::SendGoalService as Service>::Request as Message>::RmwMsg) -> [u8; 16];
fn get_goal_request_uuid(request: &<<Self::SendGoalService as Service>::Request as Message>::RmwMsg) -> &[u8; 16];

/// Create a goal response message with the given acceptance and timestamp.
fn create_goal_response(accepted: bool, stamp: (i32, u32)) -> <<Self::SendGoalService as Service>::Response as Message>::RmwMsg;

/// Sets the `accepted` field of a goal response.
fn set_goal_response_accepted(response: &mut <<Self::SendGoalService as Service>::Response as Message>::RmwMsg, accepted: bool);
/// Get the `accepted` field of a goal response.
fn get_goal_response_accepted(response: &<<Self::SendGoalService as Service>::Response as Message>::RmwMsg) -> bool;

/// Get the `stamp` field of a goal response.
fn get_goal_response_stamp(response: &<<Self::SendGoalService as Service>::Response as Message>::RmwMsg) -> (i32, u32);

/// Create a feedback message with the given goal ID and contents.
fn create_feedback_message(goal_id: &[u8; 16], feedback: &<<Self as Action>::Feedback as Message>::RmwMsg) -> <Self::FeedbackMessage as Message>::RmwMsg;
fn create_feedback_message(goal_id: &[u8; 16], feedback: <<Self as Action>::Feedback as Message>::RmwMsg) -> <Self::FeedbackMessage as Message>::RmwMsg;

/// Get the UUID of a feedback message.
fn get_feedback_message_uuid(feedback: &<Self::FeedbackMessage as Message>::RmwMsg) -> &[u8; 16];

/// Get the feedback of a feedback message.
fn get_feedback_message_feedback(feedback: &<Self::FeedbackMessage as Message>::RmwMsg) -> &<<Self as Action>::Feedback as Message>::RmwMsg;

/// Create a result request message with the given goal ID.
fn create_result_request(goal_id: &[u8; 16]) -> <<Self::GetResultService as Service>::Request as Message>::RmwMsg;

/// Get the UUID of a result request.
fn get_result_request_uuid(request: &<<Self::GetResultService as Service>::Request as Message>::RmwMsg) -> [u8; 16];
fn get_result_request_uuid(request: &<<Self::GetResultService as Service>::Request as Message>::RmwMsg) -> &[u8; 16];

/// Create a result response message with the given status and contents.
fn create_result_response(status: i8, result: <<Self as Action>::Result as Message>::RmwMsg) -> <<Self::GetResultService as Service>::Response as Message>::RmwMsg;

/// Get the result of a result response.
fn get_result_response_result(response: &<<Self::GetResultService as Service>::Response as Message>::RmwMsg) -> &<<Self as Action>::Result as Message>::RmwMsg;

/// Get the status of a result response.
fn get_result_response_status(response: &<<Self::GetResultService as Service>::Response as Message>::RmwMsg) -> i8;
}

0 comments on commit 9e6f572

Please sign in to comment.