-
Notifications
You must be signed in to change notification settings - Fork 166
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
Action server implementation #323
Conversation
c33d94b
to
3406328
Compare
3406328
to
40c2ce3
Compare
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.
Is the calling library responsible for updating the goal handle (i.e. to transition to execute, to succeeded, etc.)?
Does this PR depend on #325 too? |
In the current implementation, yes. I was hoping to build on top of this (or refactor) after ros2/rosidl#314 in order to encapsulate more logic.
Yes, those commits are included in this PR for testing purposes, but I'll rebase after #325 is merged. |
539bd5e
to
5b1a55f
Compare
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.
LGTM but for a few small comments. Awesome work @jacobperron !
// Try to initialize another action server with the same name | ||
// FIXME(jacobperron): test failing | ||
// rcl_action_server_t same_name_action_server = rcl_action_get_zero_initialized_server(); | ||
// ret = rcl_action_server_init(&same_name_action_server, &node, ts, action_name, &options); |
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.
@jacobperron I presume we're aiming to catch this by means of invalid service name checks, but it doesn't seem like that outcome means the service name is repeated (see here)?
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.
That's right, I realized later that it doesn't care if service names are repeated and meant to remove this commented test. But I can add a test for invalid names.
I also found a bug where empty action names are acceptable because we are appending the additional service/topic strings to it (e.g. "/_action/send_goal").
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.
Oh, that's a bug in rcl_action/names.cc
.
} | ||
RCL_CHECK_ARGUMENT_FOR_NULL(goal_handles, RCL_RET_INVALID_ARGUMENT); | ||
RCL_CHECK_ARGUMENT_FOR_NULL(num_goals, RCL_RET_INVALID_ARGUMENT); | ||
*goal_handles = action_server->impl->goal_handles; |
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.
@jacobperron should we check for NULL == *goal_handles
?
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.
No. We can set *goal_handles
to a valid pointer, regardless of it's initial value.
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.
Partial review, I'll continue reviewing but wanted to give some feedback first.
rcl_action/package.xml
Outdated
@@ -18,6 +18,7 @@ | |||
<test_depend>ament_cmake_gtest</test_depend> | |||
<test_depend>ament_lint_common</test_depend> | |||
<test_depend>ament_lint_auto</test_depend> | |||
<test_depend>test_msgs</test_depend> | |||
|
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.
Also add <build_depend>
+ <build_export_depend>
for rosidl_generator_c
, and <build_depend>
for rmw
+ rcutils
.
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.
Addressed in 00ecdf0
// Initialize clock | ||
rcl_ret_t ret = rcl_clock_init(options->clock_type, &action_server->impl->clock, &allocator); | ||
if (RCL_RET_OK != ret) { | ||
return RCL_RET_ERROR; |
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.
Need to deallocate action_server->impl
before returning
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.
Addressed in c6b436e
action_server->impl, "allocating memory failed", return RCL_RET_BAD_ALLOC); | ||
|
||
// Initialize clock | ||
rcl_ret_t ret = rcl_clock_init(options->clock_type, &action_server->impl->clock, &allocator); |
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 recommend allowing clock to be passed in to this function. A ROS clock created here won't receive sim time updates. The time source API's in the client libraries currently need the higher level clock type to do that.
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.
Okay. Then I suppose it will not be the action servers responsibility to finalize the clock and we hope that it stays valid as long as the action server is valid. Does this sound right?
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.
Does this sound right?
I think so
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.
Addressed in 4eb7fdb
default_options.status_topic_qos = rcl_action_qos_profile_status_default; | ||
default_options.allocator = rcl_get_default_allocator(); | ||
default_options.clock_type = RCL_ROS_TIME; | ||
default_options.result_timeout.nanoseconds = (rcl_duration_value_t)9e11; // 15 minutes |
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.
Recommend RCUTILS_S_TO_NS(15 * 60)
for readability and to avoid the cast from a floating point literal.
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.
Addressed in c6b436e
_goal_info_stamp_to_nanosec(const rcl_action_goal_info_t * goal_info) | ||
{ | ||
assert(goal_info); | ||
return (int64_t)(goal_info->stamp.sec * 1e9) + (int64_t)goal_info->stamp.nanosec; |
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.
RCUTILS_S_TO_NS(goal_info->stamp.sec) + ...
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.
Addressed in c6b436e
{ | ||
assert(nanosec); | ||
assert(goal_info); | ||
goal_info->stamp.sec = (int32_t) (*nanosec / (int64_t)1e9); |
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.
Addressed in c6b436e
assert(nanosec); | ||
assert(goal_info); | ||
goal_info->stamp.sec = (int32_t) (*nanosec / (int64_t)1e9); | ||
goal_info->stamp.nanosec = (uint32_t)(*nanosec - (int64_t)(goal_info->stamp.sec * 1e9)); |
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.
nanosec % RCUTILS_S_TO_NS(1)
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.
Addressed in c6b436e
goal_handles = (rcl_action_goal_handle_t **)tmp_ptr; | ||
|
||
// Allocate space for a new goal handle | ||
tmp_ptr = allocator.allocate(sizeof(rcl_action_goal_handle_t), allocator.state); |
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 think this allocation could be removed by changing action_server->impl->goal_handles
could be changed to an array of goal_handles
instead of an array of pointers to goal handles.
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.
True, the main reason I went with an array of pointers was that is made it easier for removing expired goals from the middle of the list:
rcl/rcl_action/src/rcl_action/action_server.c
Lines 533 to 535 in 06df1fd
// Fill in any gaps left in the array with pointers from the end | |
action_server->impl->goal_handles[i] = action_server->impl->goal_handles[num_goal_handles]; | |
action_server->impl->goal_handles[num_goal_handles--] = NULL; |
But I guess it wouldn't be too much overhead to copy the entire goal handle in this case (instead of just the pointer).
ret = rcl_action_goal_handle_get_info( | ||
action_server->impl->goal_handles[i], &status_message->msg.status_list.data[i].goal_info); | ||
if (RCL_RET_OK != ret) { | ||
return RCL_RET_ERROR; |
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 think this needs to .._fini()
status_message
before returning
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.
Addressed in c6b436e
ret = rcl_action_goal_handle_get_status( | ||
action_server->impl->goal_handles[i], &status_message->msg.status_list.data[i].status); | ||
if (RCL_RET_OK != ret) { | ||
return RCL_RET_ERROR; |
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.
Same comment about deallocating status_message
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.
Addressed in c6b436e
rcl_ret_t | ||
rcl_action_clear_expired_goals( | ||
const rcl_action_server_t * action_server, | ||
size_t * num_expired) |
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.
If there is a C++ wrapper for a goal handle then it might be helpful for the C++ API to separate this API into two: one that says which goals are expired, and another that expires a goal.
This function will fini()
goal handles, but a user may have kept a reference to a language specific wrapper rclcpp_action::GoalHandle
. It would be nice if the rcl_action
goal handle was deallcated when the rclcpp_action::GoalHandle
is destructed. Currently this deallocation could happen sooner and calls to the higher level wrapper might use a free'd object.
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.
My original thinking was that since the action server is the entity creating the goal handles, it should be the one responsible for destroying them.
If a rcl goal handle is finalized, then the goal handle becomes invalid (ie. rcl_action_goal_handle_is_valid()
returns false) and so, I'm pretty sure, any rcl operations involving the goal handle should fail with an error code. So at least the C++ wrapper of a goal handle wouldn't be using a free'd goal handle without getting an explicit error.
Is it enough for the client libraries to check their goal handles with rcl_action_goal_handle_is_valid()
and/or get a fresh list of goal handles with rcl_action_server_get_goal_handles()
?
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.
Is it enough for the client libraries to check their goal handles with
rcl_action_goal_handle_is_valid()
I guess the question is what does a C++ goal handle wrapper have a reference to? If it has an rcl_action_goal_handle_t * rcl_handle_;
pointing to storage owned by rcl_action_server_impl_t
, then rcl_action_goal_handle_is_valid(rcl_handle_)
isn't safe because the memory it's pointing to could change any time impl->goal_handles
is resized. It seems like it needs to be the other way around where the C++
goal handle wrapper has an rcl_action_goal_handle_t
and rcl_action_server_t
is the one with a pointer to it.
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 see; makes sense now.
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, so just to be clear, rcl* will handle all incoming result requests by first checking for goal handle validity, rejecting the request once the goal handle is no longer valid and only then deallocating it, right? I'd be somewhat more comfortable with just marking the goal handle as expired to keep finalization and deallocation close together, but this will do to. Only downside is that an error in that case isn't really an error, but the normal logic flow, so we have to be careful not to throw/assert/abort.
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, then how does the calling library know when a goal handle has expired?
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, then how does the calling library know when a goal handle has expired?
What about if we return a list of expired goal handles as part of the expire function?
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 like an option. Though judging by @sloretz rclcpp API skeleton, I'd imagine that having the client library wrappers checking if the goal handle they own has expired or not may be simpler. But we can somehow reverse the mapping to know which wrappers have to go away when goal handles expire.
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.
The client library can call rcl_action_server_goal_exists()
to check if a previously accepted goal has expired.
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.
Removed finalize of expired goal handles in 1140112.
It is expected that the client library will finalize goal handles either after they expire (which can be checked with rcl_action_server_goal_exists()
) or after the action server is finalized.
Note, the rcl goal handles are still initialized in rcl_action_accept_new_goal()
. I was going to update the API to take an already initialized goal handle as an argument, but then we lose the time stamp created when the goal was accepted:
rcl/rcl_action/src/rcl_action/action_server.c
Lines 362 to 370 in 1140112
// Re-stamp goal info with current time | |
rcl_action_goal_info_t goal_info_stamp_now = rcl_action_get_zero_initialized_goal_info(); | |
goal_info_stamp_now = *goal_info; | |
rcl_time_point_value_t now_time_point; | |
rcl_ret_t ret = rcl_clock_get_now(&action_server->impl->clock, &now_time_point); | |
if (RCL_RET_OK != ret) { | |
return NULL; // Error already set | |
} | |
_nanosec_to_goal_info_stamp(&now_time_point, &goal_info_stamp_now); |
If we want the client library to pass the goal handle to rcl, then we can
- Create a setter function for the goal handle's info and override the info in
rcl_action_accept_goal_info
, or - Leave the responsibility to the client library to use the same clock and stamp the goal handles properly.
} | ||
|
||
// Shrink goal handle array if some goals expired | ||
if (*num_expired > 0u) { |
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.
This should deallocate if the new size is zero because allocator.reallocate()
is supposed to behave like realloc() which has undefined behavior when the new size is zero.
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.
Addressed in c6b436e
for (size_t i = 0; i < total_num_goals; ++i) { | ||
goal_handle = action_server->impl->goal_handles[i]; | ||
rcl_ret_t ret = rcl_action_goal_handle_get_info(goal_handle, &goal_info); | ||
assert(RCL_RET_OK == ret); |
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.
IIUC assert is compiled out in release builds. Is this an assert()
because its calling out an assumption that the above call will always succeed? Is it possible the above call could fail in normal use?
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 thought it should always succeed, but after thinking about it some more, this can fail if the goal handle is finalized outside of the action server's control.
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.
Addressed in 8f9041e
|
||
if (uuidcmp(request_uuid, goal_info.uuid)) { | ||
if (rcl_action_goal_handle_is_cancelable(goal_handle)) { | ||
goal_handles_to_cancel[num_goals_to_cancel++] = goal_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.
Intentional postfix increment? I think the style guide says to always use prefix increment.
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.
Yeah, this is intentional. Assigning the goal handle to index num_goals_to_cancel
and then increment.
for (size_t i = 0; i < total_num_goals; ++i) { | ||
goal_handle = action_server->impl->goal_handles[i]; | ||
rcl_ret_t ret = rcl_action_goal_handle_get_info(goal_handle, &goal_info); | ||
assert(RCL_RET_OK == ret); |
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.
same comment about assert
if (rcl_action_goal_handle_is_cancelable(goal_handle) && | ||
((goal_nanosec <= request_nanosec) || uuidcmp(request_uuid, goal_info.uuid))) | ||
{ | ||
goal_handles_to_cancel[num_goals_to_cancel++] = goal_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.
comment++
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.
ditto
Looks like this PR needs a rebase. |
c6b436e
to
06df1fd
Compare
Rebased. |
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.
Small comments, awesome work Jacob!
allocator.deallocate(action_server->impl->goal_handles, allocator.state); | ||
} else { | ||
void * tmp_ptr = allocator.reallocate( | ||
action_server->impl->goal_handles, num_goal_handles, allocator.state); |
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.
@jacobperron num_goal_handles
--> num_goal_handles * sizeof(rcl_action_goal_handle_t *)
?
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.
Yep, good catch.
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.
Addressed 7aadb55
rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request(); | ||
for (int i = 0; i < UUID_SIZE; ++i) { | ||
cancel_request.goal_info.uuid[i] = static_cast<uint8_t>(i + 2); | ||
} |
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.
@jacobperron just curious, why not reusing one of the init_test_uuid*()
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.
I can reuse init_test_uui0()
, but the init_test_uuid1()
wouldn't do because the ID doesn't exist in this test case.
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.
Addressed 7aadb55
rcl_action_goal_info_t * goal_info = &cancel_response.msg.goals_canceling.data[0]; | ||
for (int i = 0; i < UUID_SIZE; ++i) { | ||
EXPECT_EQ(goal_info->uuid[i], static_cast<uint8_t>(i + 2)); | ||
} |
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.
@jacobperron could be EXPECT_TRUE(uuidcmp(goal_info->uuid, cancel_request.goal_info.uuid));
as well.
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.
Addressed 7aadb55
goal_info_out = &cancel_response.msg.goals_canceling.data[i]; | ||
for (size_t j = 0; j < UUID_SIZE; ++j) { | ||
EXPECT_EQ(goal_info_out->uuid[j], static_cast<uint8_t>(i + j)); | ||
} |
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.
@jacobperron same as above but using goals_info_out
instead.
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.
In this case, we would need to reconstruct the uuid in a similar loop before comparing anyways.
goal_info_out = &cancel_response.msg.goals_canceling.data[num_goals_canceling - 1]; | ||
for (int i = 0; i < UUID_SIZE; ++i) { | ||
EXPECT_EQ(goal_info_out->uuid[i], static_cast<uint8_t>(i + goal_index)); | ||
} |
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.
@jacobperron same as above.
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.
Addressed 7aadb55
* Move communcation tests to separate file * Define UUID_SIZE * Remove const for action server in rcl_action_accept_new_goal() * Make private function static * Add missing override specifiers
Introduce rcl_clock_t to action server implementation.
* Change internal goal handle array to be an array of pointers. * Add check for invalid action names
Rather than initializing internally.
The test is not checking expected behavior
bc9a5df
to
7028e3f
Compare
Instead, leave it up to the caller to finalize goal handles. Renamed the function to rcl_action_expire_goals.
Bugfix: Ensure options (containing allocator) are copied before a finalize occurs (e.g. goto fail).
@jacobperron One more CI run and I think this one is ready to go. |
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.
Just tiny changes that can be done later.
@@ -49,8 +53,11 @@ add_library(${PROJECT_NAME} | |||
) | |||
|
|||
ament_target_dependencies(${PROJECT_NAME} | |||
"rcl" | |||
"rosidl_generator_c" |
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.
nit, alphabetical order
target_include_directories(test_action_communication PUBLIC | ||
include | ||
${rcl_INCLUDE_DIRS} | ||
${test_msgs_INCLUDE_DIRS} |
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.
nit, This is taken care of by ament_target_dependencies()
below
target_include_directories(test_action_server PUBLIC | ||
include | ||
${rcl_INCLUDE_DIRS} | ||
${test_msgs_INCLUDE_DIRS} |
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.
nit, This is taken care of by ament_target_dependencies() below
@sloretz Thanks, I'll take note and include fixes for the small things in the next PR. |
* Implement action server init, fini, and is_valid functions * Add macros for initializing services and publishers * Implement rcl_action_server_get_default_options() * Implement rcl_action_accept_new_goal() * Add function, rcl_action_server_goal_exists(), for checking if goal is already being tracked by an action server * Add unit tests * Implement rcl_action_server_get_goal_handles() * Implement rcl_action_server_get_options() * Implement rcl_action_server_get_action_name() * Implement rcl_action_get_goal_status_array() * Bugfix: reset pointers and size in type finalize functions Also let finalize functions be called on already finalized objects * Implement send/take functions for action server services * Implement action server publishers for feedback and status * Implement rcl_action_process_cancel_request() * Add partial communication tests * Define UUID_SIZE * Use type-erased pointer for rcl_action_publish_status() * Implement rcl_action_clear_expired_goals() Introduce rcl_clock_t to action server implementation. * Change internal goal handle array to be an array of pointers. * Add check for invalid action names * Do heap allocation of temporary array to satisfy MSVC compiler * Bugfix: finalize node in test tear downs and reset expected errors * Update documentation * Update package.xml * Pass in rcl_clock_t to action server Rather than initializing internally. * Do not finalize goal handles in expire function Instead, leave it up to the caller to finalize goal handles. Renamed the function to rcl_action_expire_goals.
This reverts commit cd89209.
This reverts commit cd89209.
@@ -86,6 +94,34 @@ if(BUILD_TESTING) | |||
) | |||
ament_target_dependencies(test_action_client "rcl" "test_msgs") | |||
endif() | |||
ament_add_gtest(test_action_communication | |||
test/rcl_action/test_action_communication.cpp | |||
) |
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.
This test (as well as the one below) is only covering the default rmw impl. It would be good if they test would be invoked for every rmw impl.
First iteration of implementation.
Connects to #306
Depends on ros2/rcl_interfaces#47
Depends on ros2/rosidl#310