Skip to content

Commit 8b9cabf

Browse files
mauropasseMauro Passerino
andauthored
Add client/service QoS getters (#1784)
Signed-off-by: Mauro Passerino <[email protected]> Co-authored-by: Mauro Passerino <[email protected]>
1 parent 2e39e09 commit 8b9cabf

File tree

6 files changed

+346
-0
lines changed

6 files changed

+346
-0
lines changed

rclcpp/include/rclcpp/client.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "rclcpp/logging.hpp"
4141
#include "rclcpp/macros.hpp"
4242
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
43+
#include "rclcpp/qos.hpp"
4344
#include "rclcpp/type_support_decl.hpp"
4445
#include "rclcpp/utilities.hpp"
4546
#include "rclcpp/visibility_control.hpp"
@@ -218,6 +219,38 @@ class ClientBase
218219
bool
219220
exchange_in_use_by_wait_set_state(bool in_use_state);
220221

222+
/// Get the actual request publsher QoS settings, after the defaults have been determined.
223+
/**
224+
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
225+
* can only be resolved after the creation of the client, and it
226+
* depends on the underlying rmw implementation.
227+
* If the underlying setting in use can't be represented in ROS terms,
228+
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
229+
* May throw runtime_error when an unexpected error occurs.
230+
*
231+
* \return The actual request publsher qos settings.
232+
* \throws std::runtime_error if failed to get qos settings
233+
*/
234+
RCLCPP_PUBLIC
235+
rclcpp::QoS
236+
get_request_publisher_actual_qos() const;
237+
238+
/// Get the actual response subscription QoS settings, after the defaults have been determined.
239+
/**
240+
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
241+
* can only be resolved after the creation of the client, and it
242+
* depends on the underlying rmw implementation.
243+
* If the underlying setting in use can't be represented in ROS terms,
244+
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
245+
* May throw runtime_error when an unexpected error occurs.
246+
*
247+
* \return The actual response subscription qos settings.
248+
* \throws std::runtime_error if failed to get qos settings
249+
*/
250+
RCLCPP_PUBLIC
251+
rclcpp::QoS
252+
get_response_subscription_actual_qos() const;
253+
221254
/// Set a callback to be called when each new response is received.
222255
/**
223256
* The callback receives a size_t which is the number of responses received

rclcpp/include/rclcpp/service.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "rclcpp/expand_topic_or_service_name.hpp"
4040
#include "rclcpp/logging.hpp"
4141
#include "rclcpp/macros.hpp"
42+
#include "rclcpp/qos.hpp"
4243
#include "rclcpp/type_support_decl.hpp"
4344
#include "rclcpp/visibility_control.hpp"
4445

@@ -127,6 +128,38 @@ class ServiceBase
127128
bool
128129
exchange_in_use_by_wait_set_state(bool in_use_state);
129130

131+
/// Get the actual response publisher QoS settings, after the defaults have been determined.
132+
/**
133+
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
134+
* can only be resolved after the creation of the service, and it
135+
* depends on the underlying rmw implementation.
136+
* If the underlying setting in use can't be represented in ROS terms,
137+
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
138+
* May throw runtime_error when an unexpected error occurs.
139+
*
140+
* \return The actual response publisher qos settings.
141+
* \throws std::runtime_error if failed to get qos settings
142+
*/
143+
RCLCPP_PUBLIC
144+
rclcpp::QoS
145+
get_response_publisher_actual_qos() const;
146+
147+
/// Get the actual request subscription QoS settings, after the defaults have been determined.
148+
/**
149+
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
150+
* can only be resolved after the creation of the service, and it
151+
* depends on the underlying rmw implementation.
152+
* If the underlying setting in use can't be represented in ROS terms,
153+
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
154+
* May throw runtime_error when an unexpected error occurs.
155+
*
156+
* \return The actual request subscription qos settings.
157+
* \throws std::runtime_error if failed to get qos settings
158+
*/
159+
RCLCPP_PUBLIC
160+
rclcpp::QoS
161+
get_request_subscription_actual_qos() const;
162+
130163
/// Set a callback to be called when each new request is received.
131164
/**
132165
* The callback receives a size_t which is the number of requests received

rclcpp/src/rclcpp/client.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,44 @@ ClientBase::exchange_in_use_by_wait_set_state(bool in_use_state)
201201
return in_use_by_wait_set_.exchange(in_use_state);
202202
}
203203

204+
rclcpp::QoS
205+
ClientBase::get_request_publisher_actual_qos() const
206+
{
207+
const rmw_qos_profile_t * qos =
208+
rcl_client_request_publisher_get_actual_qos(client_handle_.get());
209+
if (!qos) {
210+
auto msg =
211+
std::string("failed to get client's request publisher qos settings: ") +
212+
rcl_get_error_string().str;
213+
rcl_reset_error();
214+
throw std::runtime_error(msg);
215+
}
216+
217+
rclcpp::QoS request_publisher_qos =
218+
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(*qos), *qos);
219+
220+
return request_publisher_qos;
221+
}
222+
223+
rclcpp::QoS
224+
ClientBase::get_response_subscription_actual_qos() const
225+
{
226+
const rmw_qos_profile_t * qos =
227+
rcl_client_response_subscription_get_actual_qos(client_handle_.get());
228+
if (!qos) {
229+
auto msg =
230+
std::string("failed to get client's response subscription qos settings: ") +
231+
rcl_get_error_string().str;
232+
rcl_reset_error();
233+
throw std::runtime_error(msg);
234+
}
235+
236+
rclcpp::QoS response_subscription_qos =
237+
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(*qos), *qos);
238+
239+
return response_subscription_qos;
240+
}
241+
204242
void
205243
ClientBase::set_on_new_response_callback(rcl_event_callback_t callback, const void * user_data)
206244
{

rclcpp/src/rclcpp/service.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,44 @@ ServiceBase::exchange_in_use_by_wait_set_state(bool in_use_state)
8888
return in_use_by_wait_set_.exchange(in_use_state);
8989
}
9090

91+
rclcpp::QoS
92+
ServiceBase::get_response_publisher_actual_qos() const
93+
{
94+
const rmw_qos_profile_t * qos =
95+
rcl_service_response_publisher_get_actual_qos(service_handle_.get());
96+
if (!qos) {
97+
auto msg =
98+
std::string("failed to get service's response publisher qos settings: ") +
99+
rcl_get_error_string().str;
100+
rcl_reset_error();
101+
throw std::runtime_error(msg);
102+
}
103+
104+
rclcpp::QoS response_publisher_qos =
105+
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(*qos), *qos);
106+
107+
return response_publisher_qos;
108+
}
109+
110+
rclcpp::QoS
111+
ServiceBase::get_request_subscription_actual_qos() const
112+
{
113+
const rmw_qos_profile_t * qos =
114+
rcl_service_request_subscription_get_actual_qos(service_handle_.get());
115+
if (!qos) {
116+
auto msg =
117+
std::string("failed to get service's request subscription qos settings: ") +
118+
rcl_get_error_string().str;
119+
rcl_reset_error();
120+
throw std::runtime_error(msg);
121+
}
122+
123+
rclcpp::QoS request_subscription_qos =
124+
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(*qos), *qos);
125+
126+
return request_subscription_qos;
127+
}
128+
91129
void
92130
ServiceBase::set_on_new_request_callback(rcl_event_callback_t callback, const void * user_data)
93131
{

rclcpp/test/rclcpp/test_client.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "rcl_interfaces/srv/list_parameters.hpp"
2525

2626
#include "../mocking_utils/patch.hpp"
27+
#include "../utils/rclcpp_gtest_macros.hpp"
2728

2829
#include "test_msgs/srv/empty.hpp"
2930

@@ -431,3 +432,105 @@ TEST_F(TestClient, on_new_response_callback) {
431432
std::function<void(size_t)> invalid_cb = nullptr;
432433
EXPECT_THROW(client->set_on_new_response_callback(invalid_cb), std::invalid_argument);
433434
}
435+
436+
TEST_F(TestClient, rcl_client_request_publisher_get_actual_qos_error) {
437+
auto mock = mocking_utils::patch_and_return(
438+
"lib:rclcpp", rcl_client_request_publisher_get_actual_qos, nullptr);
439+
auto client = node->create_client<test_msgs::srv::Empty>("service");
440+
RCLCPP_EXPECT_THROW_EQ(
441+
client->get_request_publisher_actual_qos(),
442+
std::runtime_error("failed to get client's request publisher qos settings: error not set"));
443+
}
444+
445+
TEST_F(TestClient, rcl_client_response_subscription_get_actual_qos_error) {
446+
auto mock = mocking_utils::patch_and_return(
447+
"lib:rclcpp", rcl_client_response_subscription_get_actual_qos, nullptr);
448+
auto client = node->create_client<test_msgs::srv::Empty>("service");
449+
RCLCPP_EXPECT_THROW_EQ(
450+
client->get_response_subscription_actual_qos(),
451+
std::runtime_error("failed to get client's response subscription qos settings: error not set"));
452+
}
453+
454+
TEST_F(TestClient, client_qos) {
455+
rmw_qos_profile_t qos_profile = rmw_qos_profile_services_default;
456+
qos_profile.liveliness = RMW_QOS_POLICY_LIVELINESS_AUTOMATIC;
457+
uint64_t duration = 1;
458+
qos_profile.deadline = {duration, duration};
459+
qos_profile.lifespan = {duration, duration};
460+
qos_profile.liveliness_lease_duration = {duration, duration};
461+
462+
auto client =
463+
node->create_client<test_msgs::srv::Empty>("client", qos_profile);
464+
465+
auto init_qos =
466+
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile), qos_profile);
467+
auto rp_qos = client->get_request_publisher_actual_qos();
468+
auto rs_qos = client->get_response_subscription_actual_qos();
469+
470+
EXPECT_EQ(init_qos, rp_qos);
471+
// Lifespan has no meaning for subscription/readers
472+
rs_qos.lifespan(qos_profile.lifespan);
473+
EXPECT_EQ(init_qos, rs_qos);
474+
}
475+
476+
TEST_F(TestClient, client_qos_depth) {
477+
using namespace std::literals::chrono_literals;
478+
479+
rmw_qos_profile_t client_qos_profile = rmw_qos_profile_default;
480+
client_qos_profile.depth = 2;
481+
482+
auto client = node->create_client<test_msgs::srv::Empty>("test_qos_depth", client_qos_profile);
483+
484+
uint64_t server_cb_count_ = 0;
485+
auto server_callback = [&](
486+
const test_msgs::srv::Empty::Request::SharedPtr,
487+
test_msgs::srv::Empty::Response::SharedPtr) {server_cb_count_++;};
488+
489+
auto server_node = std::make_shared<rclcpp::Node>("server_node", "/ns");
490+
491+
rmw_qos_profile_t server_qos_profile = rmw_qos_profile_default;
492+
493+
auto server = server_node->create_service<test_msgs::srv::Empty>(
494+
"test_qos_depth", std::move(server_callback), server_qos_profile);
495+
496+
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
497+
::testing::AssertionResult request_result = ::testing::AssertionSuccess();
498+
499+
using SharedFuture = rclcpp::Client<test_msgs::srv::Empty>::SharedFuture;
500+
uint64_t client_cb_count_ = 0;
501+
auto client_callback = [&client_cb_count_, &request_result](SharedFuture future_response) {
502+
if (nullptr == future_response.get()) {
503+
request_result = ::testing::AssertionFailure() << "Future response was null";
504+
}
505+
client_cb_count_++;
506+
};
507+
508+
uint64_t client_requests = 5;
509+
for (uint64_t i = 0; i < client_requests; i++) {
510+
client->async_send_request(request, client_callback);
511+
std::this_thread::sleep_for(10ms);
512+
}
513+
514+
auto start = std::chrono::steady_clock::now();
515+
while ((server_cb_count_ < client_requests) &&
516+
(std::chrono::steady_clock::now() - start) < 2s)
517+
{
518+
rclcpp::spin_some(server_node);
519+
std::this_thread::sleep_for(2ms);
520+
}
521+
522+
EXPECT_GT(server_cb_count_, client_qos_profile.depth);
523+
524+
start = std::chrono::steady_clock::now();
525+
while ((client_cb_count_ < client_qos_profile.depth) &&
526+
(std::chrono::steady_clock::now() - start) < 1s)
527+
{
528+
rclcpp::spin_some(node);
529+
}
530+
531+
// Spin an extra time to check if client QoS depth has been ignored,
532+
// so more client callbacks might be called than expected.
533+
rclcpp::spin_some(node);
534+
535+
EXPECT_EQ(client_cb_count_, client_qos_profile.depth);
536+
}

0 commit comments

Comments
 (0)