Skip to content

Commit 74f08ea

Browse files
committed
Support to configure feedback subscription content filter for action client
Signed-off-by: Barry Xu <barry.xu@sony.com>
1 parent 7f783cb commit 74f08ea

9 files changed

Lines changed: 356 additions & 20 deletions

File tree

rclcpp_action/include/rclcpp_action/client.hpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,30 @@ class Client : public ClientBase
111111
* This constructs an action client, but it will not work until it has been added to a node.
112112
* Use `rclcpp_action::create_client()` to both construct and add to a node.
113113
*
114+
* If enable_feedback_msg_optimization is set to true, an action client can handle up to 6 goals
115+
* simultaneously.
116+
*
114117
* \param[in] node_base A pointer to the base interface of a node.
115118
* \param[in] node_graph A pointer to an interface that allows getting graph information about
116119
* a node.
117120
* \param[in] node_logging A pointer to an interface that allows getting a node's logger.
118121
* \param[in] action_name The action name.
122+
* \param[in] enable_feedback_msg_optimization Enable feedback subscription content filter to
123+
* reduce network load.
119124
* \param[in] client_options Options to pass to the underlying `rcl_action::rcl_action_client_t`.
120125
*/
121126
Client(
122127
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
123128
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
124129
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
125130
const std::string & action_name,
126-
const rcl_action_client_options_t & client_options = rcl_action_client_get_default_options()
131+
const rcl_action_client_options_t & client_options = rcl_action_client_get_default_options(),
132+
bool enable_feedback_msg_optimization = false
127133
)
128134
: ClientBase(
129135
node_base, node_graph, node_logging, action_name,
130136
rosidl_typesupport_cpp::get_action_type_support_handle<ActionT>(),
131-
client_options)
137+
client_options, enable_feedback_msg_optimization)
132138
{
133139
}
134140

@@ -180,6 +186,19 @@ class Client : public ClientBase
180186
}
181187
return;
182188
}
189+
190+
// If feedback message optimization is enabled, add goal id to feedback subscription
191+
// content filter
192+
if (enable_feedback_msg_optimization_) {
193+
std::lock_guard<std::mutex> lock(configure_feedback_sub_content_filter_mutex_);
194+
if (!configure_feedback_subscription_filter_add_goal_id(goal_request->goal_id.uuid)) {
195+
RCLCPP_ERROR(
196+
this->get_logger(),
197+
"Failed to add goal id to feedback subscription content filter for action client.");
198+
enable_feedback_msg_optimization_ = false;
199+
}
200+
}
201+
183202
GoalInfo goal_info;
184203
goal_info.goal_id.uuid = goal_request->goal_id.uuid;
185204
goal_info.stamp = goal_response->stamp;
@@ -519,6 +538,22 @@ class Client : public ClientBase
519538
wrapped_result.goal_id = goal_handle->get_goal_id();
520539
wrapped_result.code = static_cast<ResultCode>(result_response->status);
521540
goal_handle->set_result(wrapped_result);
541+
542+
// If feedback message optimization is enabled, remove goal id from feedback subscription
543+
// content filter
544+
if (this->enable_feedback_msg_optimization_.load()) {
545+
std::lock_guard<std::mutex> lock(
546+
this->configure_feedback_sub_content_filter_mutex_);
547+
if (!this->configure_feedback_subscription_filter_remove_goal_id(
548+
goal_handle->get_goal_id()))
549+
{
550+
RCLCPP_ERROR(
551+
this->get_logger(),
552+
"Failed to remove goal id from feedback subscription content filter for action "
553+
"client.");
554+
this->enable_feedback_msg_optimization_.store(false);
555+
}
556+
}
522557
std::lock_guard<std::recursive_mutex> lock(goal_handles_mutex_);
523558
goal_handles_.erase(goal_handle->get_goal_id());
524559
});
@@ -539,9 +574,27 @@ class Client : public ClientBase
539574
std::shared_future<typename CancelResponse::SharedPtr> future(promise->get_future());
540575
this->send_cancel_request(
541576
std::static_pointer_cast<void>(cancel_request),
542-
[cancel_callback, promise](std::shared_ptr<void> response) mutable
577+
[this, cancel_callback, promise](std::shared_ptr<void> response) mutable
543578
{
544579
auto cancel_response = std::static_pointer_cast<CancelResponse>(response);
580+
581+
// If feedback message optimization is enabled, remove goal id from feedback subscription
582+
// content filter
583+
if (this->enable_feedback_msg_optimization_.load()) {
584+
for (const auto & goal_info : cancel_response->goals_canceling) {
585+
std::lock_guard<std::mutex> lock(this->configure_feedback_sub_content_filter_mutex_);
586+
if (!this->configure_feedback_subscription_filter_remove_goal_id(
587+
goal_info.goal_id.uuid))
588+
{
589+
RCLCPP_ERROR(
590+
this->get_logger(),
591+
"Failed to remove goal id from feedback subscription content filter for action "
592+
"client.");
593+
}
594+
this->enable_feedback_msg_optimization_.store(false);
595+
}
596+
}
597+
545598
promise->set_value(cancel_response);
546599
if (cancel_callback) {
547600
cancel_callback(cancel_response);

rclcpp_action/include/rclcpp_action/client_base.hpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ class ClientBase : public rclcpp::Waitable
201201
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
202202
const std::string & action_name,
203203
const rosidl_action_type_support_t * type_support,
204-
const rcl_action_client_options_t & options);
204+
const rcl_action_client_options_t & options,
205+
bool enable_feedback_msg_optimization);
205206

206207
/// Wait for action_server_is_ready() to become true, or until the given timeout is reached.
207208
RCLCPP_ACTION_PUBLIC
@@ -295,6 +296,22 @@ class ClientBase : public rclcpp::Waitable
295296
void
296297
handle_feedback_message(std::shared_ptr<void> message) = 0;
297298

299+
/// \internal
300+
/// \return true if goal id was added to feedback subscription content filter successfully
301+
/// \return false if an error occurred during calling rcl function or
302+
/// if the used rmw middleware doesn't support Content Filtering feature.
303+
bool
304+
configure_feedback_subscription_filter_add_goal_id(const GoalUUID & goal_id);
305+
306+
307+
/// \internal
308+
/// \return true if goal id was removed from feedback subscription content filter successfully or
309+
/// goal id was not found in feedback subscription content filter or
310+
/// if the used rmw middleware doesn't support Content Filtering feature.
311+
/// \return false if an error occurred during calling rcl function.
312+
bool
313+
configure_feedback_subscription_filter_remove_goal_id(const GoalUUID & goal_id);
314+
298315
/// \internal
299316
virtual
300317
std::shared_ptr<void>
@@ -322,6 +339,14 @@ class ClientBase : public rclcpp::Waitable
322339
// Storage for std::function callbacks to keep them in scope
323340
std::unordered_map<EntityType, std::function<void(size_t)>> entity_type_to_on_ready_callback_;
324341

342+
// Enable feedback subscription content filter
343+
// Initialization is configured by the user.
344+
// When an error occurs while adding or removing a goal ID from the content filter, it will
345+
// automatically be set to false.
346+
std::atomic_bool enable_feedback_msg_optimization_;
347+
// Mutex to protect configuration of feedback subscription content filter since
348+
std::mutex configure_feedback_sub_content_filter_mutex_;
349+
325350
private:
326351
std::unique_ptr<ClientBaseImpl> pimpl_;
327352

rclcpp_action/include/rclcpp_action/create_client.hpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,16 @@ namespace rclcpp_action
3030
* This function is equivalent to \sa create_client()` however is using the individual
3131
* node interfaces to create the client.
3232
*
33+
* If enable_feedback_msg_optimization is set to true, an action client can handle up to 6 goals
34+
* simultaneously. If the number of goals exceeds the limit, optimization is automatically disabled.
35+
*
3336
* \param[in] node_base_interface The node base interface of the corresponding node.
3437
* \param[in] node_graph_interface The node graph interface of the corresponding node.
3538
* \param[in] node_logging_interface The node logging interface of the corresponding node.
3639
* \param[in] node_waitables_interface The node waitables interface of the corresponding node.
3740
* \param[in] name The action name.
41+
* \param[in] enable_feedback_msg_optimization Enable feedback subscription content filter to
42+
* reduce network load.
3843
* \param[in] group The action client will be added to this callback group.
3944
* If `nullptr`, then the action client is added to the default callback group.
4045
* \param[in] options Options to pass to the underlying `rcl_action_client_t`.
@@ -48,7 +53,8 @@ create_client(
4853
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_interface,
4954
const std::string & name,
5055
rclcpp::CallbackGroup::SharedPtr group = nullptr,
51-
const rcl_action_client_options_t & options = rcl_action_client_get_default_options())
56+
const rcl_action_client_options_t & options = rcl_action_client_get_default_options(),
57+
bool enable_feedback_msg_optimization = false)
5258
{
5359
std::weak_ptr<rclcpp::node_interfaces::NodeWaitablesInterface> weak_node =
5460
node_waitables_interface;
@@ -85,7 +91,8 @@ create_client(
8591
node_graph_interface,
8692
node_logging_interface,
8793
name,
88-
options),
94+
options,
95+
enable_feedback_msg_optimization),
8996
deleter);
9097

9198
node_waitables_interface->add_waitable(action_client, group);
@@ -106,7 +113,8 @@ create_client(
106113
NodeT node,
107114
const std::string & name,
108115
rclcpp::CallbackGroup::SharedPtr group = nullptr,
109-
const rcl_action_client_options_t & options = rcl_action_client_get_default_options())
116+
const rcl_action_client_options_t & options = rcl_action_client_get_default_options(),
117+
bool enable_feedback_msg_optimization = false)
110118
{
111119
return rclcpp_action::create_client<ActionT>(
112120
node->get_node_base_interface(),
@@ -115,7 +123,8 @@ create_client(
115123
node->get_node_waitables_interface(),
116124
name,
117125
group,
118-
options);
126+
options,
127+
enable_feedback_msg_optimization);
119128
}
120129
} // namespace rclcpp_action
121130

rclcpp_action/include/rclcpp_action/create_generic_client.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ create_generic_client(
4747
const std::string & name,
4848
const std::string & type,
4949
rclcpp::CallbackGroup::SharedPtr group = nullptr,
50-
const rcl_action_client_options_t & options = rcl_action_client_get_default_options());
50+
const rcl_action_client_options_t & options = rcl_action_client_get_default_options(),
51+
bool enable_feedback_msg_optimization = false);
5152

5253
/// Create an action generic client.
5354
/**
@@ -66,7 +67,8 @@ create_generic_client(
6667
const std::string & name,
6768
const std::string & type,
6869
rclcpp::CallbackGroup::SharedPtr group = nullptr,
69-
const rcl_action_client_options_t & options = rcl_action_client_get_default_options())
70+
const rcl_action_client_options_t & options = rcl_action_client_get_default_options(),
71+
bool enable_feedback_msg_optimization = false)
7072
{
7173
return rclcpp_action::create_generic_client(
7274
node->get_node_base_interface(),
@@ -76,7 +78,8 @@ create_generic_client(
7678
name,
7779
type,
7880
group,
79-
options);
81+
options,
82+
enable_feedback_msg_optimization);
8083
}
8184
} // namespace rclcpp_action
8285

rclcpp_action/include/rclcpp_action/generic_client.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class GenericClient : public ClientBase
112112
const std::string & action_name,
113113
std::shared_ptr<rcpputils::SharedLibrary> typesupport_lib,
114114
const rosidl_action_type_support_t * action_typesupport_handle,
115-
const rcl_action_client_options_t & client_options = rcl_action_client_get_default_options());
115+
const rcl_action_client_options_t & client_options = rcl_action_client_get_default_options(),
116+
bool enable_feedback_msg_optimization = false);
116117

117118
/// Send an action goal and asynchronously get the result.
118119
/**

rclcpp_action/src/client_base.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ ClientBase::ClientBase(
190190
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
191191
const std::string & action_name,
192192
const rosidl_action_type_support_t * type_support,
193-
const rcl_action_client_options_t & client_options)
194-
: pimpl_(new ClientBaseImpl(
193+
const rcl_action_client_options_t & client_options,
194+
bool enable_feedback_msg_optimization)
195+
: enable_feedback_msg_optimization_(enable_feedback_msg_optimization),
196+
pimpl_(new ClientBaseImpl(
195197
node_base, node_graph, node_logging, action_name, type_support, client_options))
196198
{
197199
}
@@ -830,4 +832,23 @@ ClientBase::configure_introspection(
830832
}
831833
}
832834

835+
bool
836+
ClientBase::configure_feedback_subscription_filter_add_goal_id(
837+
const GoalUUID & goal_id)
838+
{
839+
const rcl_ret_t ret = rcl_action_client_configure_feedback_subscription_filter_add_goal_id(
840+
pimpl_->client_handle.get(), goal_id.data(), goal_id.size());
841+
842+
return ret == RCL_RET_OK;
843+
}
844+
845+
bool
846+
ClientBase::configure_feedback_subscription_filter_remove_goal_id(
847+
const GoalUUID & goal_id)
848+
{
849+
const rcl_ret_t ret = rcl_action_client_configure_feedback_subscription_filter_remove_goal_id(
850+
pimpl_->client_handle.get(), goal_id.data(), goal_id.size());
851+
852+
return ret == RCL_RET_OK;
853+
}
833854
} // namespace rclcpp_action

rclcpp_action/src/create_generic_client.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ create_generic_client(
3232
const std::string & name,
3333
const std::string & type,
3434
rclcpp::CallbackGroup::SharedPtr group,
35-
const rcl_action_client_options_t & options)
35+
const rcl_action_client_options_t & options,
36+
bool enable_feedback_msg_optimization)
3637
{
3738
std::weak_ptr<rclcpp::node_interfaces::NodeWaitablesInterface> weak_node =
3839
node_waitables_interface;
@@ -75,7 +76,8 @@ create_generic_client(
7576
name,
7677
typesupport_lib,
7778
action_typesupport_handle,
78-
options),
79+
options,
80+
enable_feedback_msg_optimization),
7981
deleter);
8082

8183
node_waitables_interface->add_waitable(action_client, group);

rclcpp_action/src/generic_client.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ GenericClient::GenericClient(
3232
const std::string & action_name,
3333
std::shared_ptr<rcpputils::SharedLibrary> typesupport_lib,
3434
const rosidl_action_type_support_t * action_typesupport_handle,
35-
const rcl_action_client_options_t & client_options)
35+
const rcl_action_client_options_t & client_options,
36+
bool enable_feedback_msg_optimization)
3637
: ClientBase(
3738
node_base, node_graph, node_logging, action_name,
3839
action_typesupport_handle,
39-
client_options),
40+
client_options,
41+
enable_feedback_msg_optimization),
4042
ts_lib_(std::move(typesupport_lib))
4143
{
4244
auto goal_service_request_type_support_intro = get_message_typesupport_handle(
@@ -143,6 +145,19 @@ GenericClient::async_send_goal(
143145
return;
144146
}
145147

148+
// If feedback message optimization is enabled, add goal id to feedback subscription
149+
// content filter
150+
if (enable_feedback_msg_optimization_) {
151+
std::lock_guard<std::mutex> lock(configure_feedback_sub_content_filter_mutex_);
152+
if (!configure_feedback_subscription_filter_add_goal_id(uuid)) {
153+
RCLCPP_ERROR(
154+
this->get_logger(),
155+
"Failed to add goal id to feedback subscription content filter for action "
156+
"generic client.");
157+
enable_feedback_msg_optimization_ = false;
158+
}
159+
}
160+
146161
action_msgs::msg::GoalInfo goal_info;
147162
goal_info.goal_id.uuid = uuid;
148163
std::memcpy(
@@ -450,6 +465,22 @@ GenericClient::make_result_aware(typename GenericClientGoalHandle::SharedPtr goa
450465

451466
goal_handle->set_result(wrapped_result);
452467

468+
// If feedback message optimization is enabled, remove goal id from feedback subscription
469+
// content filter
470+
if (this->enable_feedback_msg_optimization_.load()) {
471+
std::lock_guard<std::mutex> lock(
472+
this->configure_feedback_sub_content_filter_mutex_);
473+
if (!this->configure_feedback_subscription_filter_remove_goal_id(
474+
goal_handle->get_goal_id()))
475+
{
476+
RCLCPP_ERROR(
477+
this->get_logger(),
478+
"Failed to remove goal id from feedback subscription content filter for action "
479+
"client.");
480+
this->enable_feedback_msg_optimization_.store(false);
481+
}
482+
}
483+
453484
std::lock_guard<std::recursive_mutex> lock(goal_handles_mutex_);
454485
goal_handles_.erase(goal_handle->get_goal_id());
455486
});
@@ -469,9 +500,27 @@ GenericClient::async_cancel(
469500
std::shared_future<typename CancelResponse::SharedPtr> future(promise->get_future());
470501
this->send_cancel_request(
471502
std::static_pointer_cast<void>(cancel_request),
472-
[cancel_callback, promise](std::shared_ptr<void> response) mutable
503+
[this, cancel_callback, promise](std::shared_ptr<void> response) mutable
473504
{
474505
auto cancel_response = std::static_pointer_cast<CancelResponse>(response);
506+
507+
// If feedback message optimization is enabled, remove goal id from feedback subscription
508+
// content filter
509+
if (this->enable_feedback_msg_optimization_.load()) {
510+
for (const auto & goal_info : cancel_response->goals_canceling) {
511+
std::lock_guard<std::mutex> lock(this->configure_feedback_sub_content_filter_mutex_);
512+
if (!this->configure_feedback_subscription_filter_remove_goal_id(
513+
goal_info.goal_id.uuid))
514+
{
515+
RCLCPP_ERROR(
516+
this->get_logger(),
517+
"Failed to remove goal id from feedback subscription content filter for action "
518+
"client.");
519+
}
520+
this->enable_feedback_msg_optimization_.store(false);
521+
}
522+
}
523+
475524
promise->set_value(cancel_response);
476525
if (cancel_callback) {
477526
cancel_callback(cancel_response);

0 commit comments

Comments
 (0)