-
Notifications
You must be signed in to change notification settings - Fork 412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Skeleton for Action Server and Client #579
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz thank you for pushing this! I left some comments here and there. Feel free to focus on the server, taking care of the client is on me.
rclcpp_action/CMakeLists.txt
Outdated
) | ||
target_link_libraries(test_server | ||
${PROJECT_NAME} | ||
${test_msgs_LIBRARIES} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz I've also seen CMake code using the ament_target_dependencies()
macro to do just this. Which one's the recommended idiom?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ament_target_dependencies()
is the recommended one. I keep forgetting the ament_cmake
macros and fallback to the usual CMake functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
// TODO(sloretz) shared pointer to keep rcl_server_ alive while goal handles are alive | ||
rcl_action_server_t * rcl_server_; | ||
rcl_action_goal_handle_t * rcl_handle_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz I agree we should use smart pointers, for both (I'd initially say shared and unique respectively).
|
||
/// TODO(sloretz) examples have this on client as `async_cancel_goal(goal_handle)` | ||
std::future<bool> | ||
async_cancel(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz I do like the idea of calling cancel on the goal handle, but we have to support cancellation requests that expand multiple goals (e.g. cancel all goals). So I'd propose to keep this async_cancel()
method here, plus async_cancel_all_goals()
and async_cancel_goals_before()
methods in the client, and maybe expose the low level method that just takes goal ID and timestamp for completeness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
// TODO(sloretz) `async_result` to make method names consistent? | ||
std::future<typename ACTION::Result> | ||
result_future(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz hmm, I do prefer async_result
, but it's not a strong preference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. I just need to remember to update this line in the examples https://github.com/ros2/examples/pull/216/files#diff-9df9aa44ab873a3a211a764112190449R37
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
private: | ||
// The templated Server creates goal handles | ||
friend Client<ACTION>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz I don't know if this is a typical pattern in ROS2, but why not using an abstract base class and have the client implement their own?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think using friend
and a private constructor is a typical pattern. The base class approach would work too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I was actually thinking of something like (in client_goal_handle.hpp
):
class ClientGoalHandle {
public:
virtual std::future<bool> async_cancel() = 0;
};
and then (in client.cpp
):
class ClientGoalHandleImpl : public ClientGoalHandle {
public:
std::future<bool> async_cancel() override {
...
}
};
External code never instantiates a GoalHandle
because it's just an interface. We have to watch out for slicing though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think ClientGoalHandleImpl
can be in client.cpp
because std::future<typename ACTION::Result> async_result()
requires knowing the user's type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combined base class with virtual methods and friendship of implementation class with Server in acdfcd9. How does that look?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh, right, there's no common base type for action user defined types, silly me.
I think it's fine. Another alternative (for the sake of completeness :) ) would be to define the goal handle subclass as a private nested class of the client/server class. Same code, no friend
ship.
|
||
// TODO(sloretz) shared pointer to keep rcl_client_ alive while goal handles are alive | ||
rcl_action_client_t * rcl_client_; | ||
rcl_action_goal_info_t rcl_info_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz I agree we should use smart pointers.
|
||
|
||
// TODO(sloretz) goal_handle->result_future() | ||
// TODO(sloretz) goal_handle->cancel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz I think we can remove these TODOs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
private: | ||
// The templated Server creates goal handles | ||
friend Server<ACTION>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sloretz same as above about using friend
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pending green CI, I think this can be merged as a place to iterate from.
I agree with Jacob. |
target_link_libraries(test_server | ||
${PROJECT_NAME} | ||
) | ||
endif() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both of these tests only run with the default RMW impl. It would be good to update this to run the tests for each RMW impl.
* Skeleton for ActionServer and ActionClient
…shutdown` (ros2#579) Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
…2#579) Signed-off-by: Emerson Knapp <eknapp@amazon.com>
This is the start of C++ code matching the API in ros2/examples#216