ROS 2 rclcpp + rcl - kilted  kilted
ROS 2 C++ Client Library with ROS Client Library
parameter_client.cpp
1 // Copyright 2015 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "rclcpp/parameter_client.hpp"
16 
17 #include <algorithm>
18 #include <chrono>
19 #include <functional>
20 #include <future>
21 #include <iterator>
22 #include <memory>
23 #include <stdexcept>
24 #include <string>
25 #include <vector>
26 
27 #include "./parameter_service_names.hpp"
28 
31 
32 AsyncParametersClient::AsyncParametersClient(
33  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
34  const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
35  const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
36  const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
37  const std::string & remote_node_name,
38  const rclcpp::QoS & qos_profile,
39  rclcpp::CallbackGroup::SharedPtr group)
40 : node_topics_interface_(node_topics_interface)
41 {
42  if (remote_node_name != "") {
43  remote_node_name_ = remote_node_name;
44  } else {
45  remote_node_name_ = node_base_interface->get_fully_qualified_name();
46  }
47 
49  options.qos = qos_profile.get_rmw_qos_profile();
50 
51  using rclcpp::Client;
52  using rclcpp::ClientBase;
53 
55  node_base_interface.get(),
56  node_graph_interface,
57  remote_node_name_ + "/" + parameter_service_names::get_parameters,
58  options);
59  auto get_parameters_base = std::dynamic_pointer_cast<ClientBase>(get_parameters_client_);
60  node_services_interface->add_client(get_parameters_base, group);
61 
63  node_base_interface.get(),
64  node_graph_interface,
65  remote_node_name_ + "/" + parameter_service_names::get_parameter_types,
66  options);
67  auto get_parameter_types_base =
68  std::dynamic_pointer_cast<ClientBase>(get_parameter_types_client_);
69  node_services_interface->add_client(get_parameter_types_base, group);
70 
72  node_base_interface.get(),
73  node_graph_interface,
74  remote_node_name_ + "/" + parameter_service_names::set_parameters,
75  options);
76  auto set_parameters_base = std::dynamic_pointer_cast<ClientBase>(set_parameters_client_);
77  node_services_interface->add_client(set_parameters_base, group);
78 
79  set_parameters_atomically_client_ =
81  node_base_interface.get(),
82  node_graph_interface,
83  remote_node_name_ + "/" + parameter_service_names::set_parameters_atomically,
84  options);
85  auto set_parameters_atomically_base = std::dynamic_pointer_cast<ClientBase>(
86  set_parameters_atomically_client_);
87  node_services_interface->add_client(set_parameters_atomically_base, group);
88 
90  node_base_interface.get(),
91  node_graph_interface,
92  remote_node_name_ + "/" + parameter_service_names::list_parameters,
93  options);
94  auto list_parameters_base = std::dynamic_pointer_cast<ClientBase>(list_parameters_client_);
95  node_services_interface->add_client(list_parameters_base, group);
96 
98  node_base_interface.get(),
99  node_graph_interface,
100  remote_node_name_ + "/" + parameter_service_names::describe_parameters,
101  options);
102  auto describe_parameters_base =
103  std::dynamic_pointer_cast<ClientBase>(describe_parameters_client_);
104  node_services_interface->add_client(describe_parameters_base, group);
105 }
106 
107 std::shared_future<std::vector<rclcpp::Parameter>>
108 AsyncParametersClient::get_parameters(
109  const std::vector<std::string> & names,
110  std::function<
111  void(std::shared_future<std::vector<rclcpp::Parameter>>)
112  > callback)
113 {
114  auto promise_result =
115  std::make_shared<std::promise<std::vector<rclcpp::Parameter>>>();
116  auto future_result = promise_result->get_future().share();
117 
118  auto request = std::make_shared<rcl_interfaces::srv::GetParameters::Request>();
119  request->names = names;
120 
121  get_parameters_client_->async_send_request(
122  request,
123  [request, promise_result, future_result, callback](
124  rclcpp::Client<rcl_interfaces::srv::GetParameters>::SharedFuture cb_f)
125  {
126  std::vector<rclcpp::Parameter> parameters;
127  auto & pvalues = cb_f.get()->values;
128 
129  for (auto & pvalue : pvalues) {
130  auto i = static_cast<size_t>(&pvalue - &pvalues[0]);
131  rcl_interfaces::msg::Parameter parameter;
132  parameter.name = request->names[i];
133  parameter.value = pvalue;
134  parameters.push_back(rclcpp::Parameter::from_parameter_msg(parameter));
135  }
136 
137  promise_result->set_value(parameters);
138  if (callback != nullptr) {
139  callback(future_result);
140  }
141  }
142  );
143 
144  return future_result;
145 }
146 
147 std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>
148 AsyncParametersClient::describe_parameters(
149  const std::vector<std::string> & names,
150  std::function<
151  void(std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>)
152  > callback)
153 {
154  auto promise_result =
155  std::make_shared<std::promise<std::vector<rcl_interfaces::msg::ParameterDescriptor>>>();
156  auto future_result = promise_result->get_future().share();
157 
158  auto request = std::make_shared<rcl_interfaces::srv::DescribeParameters::Request>();
159  request->names = names;
160 
161  describe_parameters_client_->async_send_request(
162  request,
163  [promise_result, future_result, callback](
164  rclcpp::Client<rcl_interfaces::srv::DescribeParameters>::SharedFuture cb_f)
165  {
166  promise_result->set_value(cb_f.get()->descriptors);
167  if (callback != nullptr) {
168  callback(future_result);
169  }
170  }
171  );
172 
173  return future_result;
174 }
175 
176 std::shared_future<std::vector<rclcpp::ParameterType>>
177 AsyncParametersClient::get_parameter_types(
178  const std::vector<std::string> & names,
179  std::function<
180  void(std::shared_future<std::vector<rclcpp::ParameterType>>)
181  > callback)
182 {
183  auto promise_result =
184  std::make_shared<std::promise<std::vector<rclcpp::ParameterType>>>();
185  auto future_result = promise_result->get_future().share();
186 
187  auto request = std::make_shared<rcl_interfaces::srv::GetParameterTypes::Request>();
188  request->names = names;
189 
190  get_parameter_types_client_->async_send_request(
191  request,
192  [promise_result, future_result, callback](
193  rclcpp::Client<rcl_interfaces::srv::GetParameterTypes>::SharedFuture cb_f)
194  {
195  std::vector<rclcpp::ParameterType> types;
196  auto & pts = cb_f.get()->types;
197  for (auto & pt : pts) {
198  types.push_back(static_cast<rclcpp::ParameterType>(pt));
199  }
200  promise_result->set_value(types);
201  if (callback != nullptr) {
202  callback(future_result);
203  }
204  }
205  );
206 
207  return future_result;
208 }
209 
210 std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
211 AsyncParametersClient::set_parameters(
212  const std::vector<rclcpp::Parameter> & parameters,
213  std::function<
214  void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)
215  > callback)
216 {
217  auto promise_result =
218  std::make_shared<std::promise<std::vector<rcl_interfaces::msg::SetParametersResult>>>();
219  auto future_result = promise_result->get_future().share();
220 
221  auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
222 
223  std::transform(
224  parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
225  [](rclcpp::Parameter p) {return p.to_parameter_msg();}
226  );
227 
228  set_parameters_client_->async_send_request(
229  request,
230  [promise_result, future_result, callback](
231  rclcpp::Client<rcl_interfaces::srv::SetParameters>::SharedFuture cb_f)
232  {
233  promise_result->set_value(cb_f.get()->results);
234  if (callback != nullptr) {
235  callback(future_result);
236  }
237  }
238  );
239 
240  return future_result;
241 }
242 
243 std::shared_future<rcl_interfaces::msg::SetParametersResult>
244 AsyncParametersClient::set_parameters_atomically(
245  const std::vector<rclcpp::Parameter> & parameters,
246  std::function<
247  void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
248  > callback)
249 {
250  auto promise_result =
251  std::make_shared<std::promise<rcl_interfaces::msg::SetParametersResult>>();
252  auto future_result = promise_result->get_future().share();
253 
254  auto request = std::make_shared<rcl_interfaces::srv::SetParametersAtomically::Request>();
255 
256  std::transform(
257  parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
258  [](rclcpp::Parameter p) {return p.to_parameter_msg();}
259  );
260 
261  set_parameters_atomically_client_->async_send_request(
262  request,
263  [promise_result, future_result, callback](
264  rclcpp::Client<rcl_interfaces::srv::SetParametersAtomically>::SharedFuture cb_f)
265  {
266  promise_result->set_value(cb_f.get()->result);
267  if (callback != nullptr) {
268  callback(future_result);
269  }
270  }
271  );
272 
273  return future_result;
274 }
275 
276 std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
278  const std::vector<std::string> & parameters_names)
279 {
280  std::vector<rclcpp::Parameter> parameters;
281  for (const std::string & name : parameters_names) {
282  parameters.push_back(rclcpp::Parameter(name));
283  }
284  auto future_result = set_parameters(parameters);
285 
286  return future_result;
287 }
288 
289 std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
291  const std::string & yaml_filename)
292 {
293  rclcpp::ParameterMap parameter_map =
294  rclcpp::parameter_map_from_yaml_file(yaml_filename, remote_node_name_.c_str());
295 
296  auto iter = parameter_map.find(remote_node_name_);
297  if (iter == parameter_map.end() || iter->second.size() == 0) {
298  throw rclcpp::exceptions::InvalidParametersException("No valid parameter");
299  }
300  auto future_result = set_parameters(iter->second);
301 
302  return future_result;
303 }
304 
305 std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
307  const rclcpp::ParameterMap & parameter_map)
308 {
309  std::vector<rclcpp::Parameter> parameters =
310  rclcpp::parameters_from_map(parameter_map, remote_node_name_.c_str());
311 
312  if (parameters.size() == 0) {
313  throw rclcpp::exceptions::InvalidParametersException("No valid parameter");
314  }
315  auto future_result = set_parameters(parameters);
316 
317  return future_result;
318 }
319 
320 
321 std::shared_future<rcl_interfaces::msg::ListParametersResult>
322 AsyncParametersClient::list_parameters(
323  const std::vector<std::string> & prefixes,
324  uint64_t depth,
325  std::function<
326  void(std::shared_future<rcl_interfaces::msg::ListParametersResult>)
327  > callback)
328 {
329  auto promise_result =
330  std::make_shared<std::promise<rcl_interfaces::msg::ListParametersResult>>();
331  auto future_result = promise_result->get_future().share();
332 
333  auto request = std::make_shared<rcl_interfaces::srv::ListParameters::Request>();
334  request->prefixes = prefixes;
335  request->depth = depth;
336 
337  list_parameters_client_->async_send_request(
338  request,
339  [promise_result, future_result, callback](
340  rclcpp::Client<rcl_interfaces::srv::ListParameters>::SharedFuture cb_f)
341  {
342  promise_result->set_value(cb_f.get()->result);
343  if (callback != nullptr) {
344  callback(future_result);
345  }
346  }
347  );
348 
349  return future_result;
350 }
351 
352 bool
354 {
355  return
356  get_parameters_client_->service_is_ready() &&
357  get_parameter_types_client_->service_is_ready() &&
358  set_parameters_client_->service_is_ready() &&
359  list_parameters_client_->service_is_ready() &&
360  describe_parameters_client_->service_is_ready();
361 }
362 
363 bool
364 AsyncParametersClient::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
365 {
366  const std::vector<std::shared_ptr<rclcpp::ClientBase>> clients = {
367  get_parameters_client_,
368  get_parameter_types_client_,
369  set_parameters_client_,
370  list_parameters_client_,
371  describe_parameters_client_
372  };
373  for (auto & client : clients) {
374  auto stamp = std::chrono::steady_clock::now();
375  if (!client->wait_for_service(timeout)) {
376  return false;
377  }
378  if (timeout > std::chrono::nanoseconds::zero()) {
379  timeout -= std::chrono::duration_cast<std::chrono::nanoseconds>(
380  std::chrono::steady_clock::now() - stamp);
381  if (timeout < std::chrono::nanoseconds::zero()) {
382  timeout = std::chrono::nanoseconds::zero();
383  }
384  }
385  }
386  return true;
387 }
388 
389 std::vector<rclcpp::Parameter>
390 SyncParametersClient::get_parameters(
391  const std::vector<std::string> & parameter_names,
392  std::chrono::nanoseconds timeout)
393 {
394  auto f = async_parameters_client_->get_parameters(parameter_names);
395  using rclcpp::executors::spin_node_until_future_complete;
396  if (
397  spin_node_until_future_complete(
398  *executor_, node_base_interface_, f,
399  timeout) == rclcpp::FutureReturnCode::SUCCESS)
400  {
401  return f.get();
402  }
403  // Return an empty vector if unsuccessful
404  return std::vector<rclcpp::Parameter>();
405 }
406 
407 bool
408 SyncParametersClient::has_parameter(const std::string & parameter_name)
409 {
410  std::vector<std::string> names;
411  names.push_back(parameter_name);
412  auto vars = list_parameters(names, 1);
413  return vars.names.size() > 0;
414 }
415 
416 std::vector<rcl_interfaces::msg::ParameterDescriptor>
417 SyncParametersClient::describe_parameters(
418  const std::vector<std::string> & parameter_names,
419  std::chrono::nanoseconds timeout)
420 {
421  auto f = async_parameters_client_->describe_parameters(parameter_names);
422 
423  using rclcpp::executors::spin_node_until_future_complete;
424  rclcpp::FutureReturnCode future =
425  spin_node_until_future_complete(*executor_, node_base_interface_, f, timeout);
426  if (future == rclcpp::FutureReturnCode::SUCCESS) {
427  return f.get();
428  }
429  return std::vector<rcl_interfaces::msg::ParameterDescriptor>();
430 }
431 
432 std::vector<rclcpp::ParameterType>
433 SyncParametersClient::get_parameter_types(
434  const std::vector<std::string> & parameter_names,
435  std::chrono::nanoseconds timeout)
436 {
437  auto f = async_parameters_client_->get_parameter_types(parameter_names);
438 
439  using rclcpp::executors::spin_node_until_future_complete;
440  if (
441  spin_node_until_future_complete(
442  *executor_, node_base_interface_, f,
443  timeout) == rclcpp::FutureReturnCode::SUCCESS)
444  {
445  return f.get();
446  }
447  return std::vector<rclcpp::ParameterType>();
448 }
449 
450 std::vector<rcl_interfaces::msg::SetParametersResult>
451 SyncParametersClient::set_parameters(
452  const std::vector<rclcpp::Parameter> & parameters,
453  std::chrono::nanoseconds timeout)
454 {
455  auto f = async_parameters_client_->set_parameters(parameters);
456 
457  using rclcpp::executors::spin_node_until_future_complete;
458  if (
459  spin_node_until_future_complete(
460  *executor_, node_base_interface_, f,
461  timeout) == rclcpp::FutureReturnCode::SUCCESS)
462  {
463  return f.get();
464  }
465  return std::vector<rcl_interfaces::msg::SetParametersResult>();
466 }
467 
468 std::vector<rcl_interfaces::msg::SetParametersResult>
470  const std::vector<std::string> & parameters_names,
471  std::chrono::nanoseconds timeout)
472 {
473  auto f = async_parameters_client_->delete_parameters(parameters_names);
474 
475  using rclcpp::executors::spin_node_until_future_complete;
476  if (
477  spin_node_until_future_complete(
478  *executor_, node_base_interface_, f,
479  timeout) == rclcpp::FutureReturnCode::SUCCESS)
480  {
481  return f.get();
482  }
483  return std::vector<rcl_interfaces::msg::SetParametersResult>();
484 }
485 
486 std::vector<rcl_interfaces::msg::SetParametersResult>
488  const std::string & yaml_filename,
489  std::chrono::nanoseconds timeout)
490 {
491  auto f = async_parameters_client_->load_parameters(yaml_filename);
492 
493  using rclcpp::executors::spin_node_until_future_complete;
494  if (
495  spin_node_until_future_complete(
496  *executor_, node_base_interface_, f,
497  timeout) == rclcpp::FutureReturnCode::SUCCESS)
498  {
499  return f.get();
500  }
501  return std::vector<rcl_interfaces::msg::SetParametersResult>();
502 }
503 
504 rcl_interfaces::msg::SetParametersResult
505 SyncParametersClient::set_parameters_atomically(
506  const std::vector<rclcpp::Parameter> & parameters,
507  std::chrono::nanoseconds timeout)
508 {
509  auto f = async_parameters_client_->set_parameters_atomically(parameters);
510 
511  using rclcpp::executors::spin_node_until_future_complete;
512  if (
513  spin_node_until_future_complete(
514  *executor_, node_base_interface_, f,
515  timeout) == rclcpp::FutureReturnCode::SUCCESS)
516  {
517  return f.get();
518  }
519 
520  throw std::runtime_error("Unable to get result of set parameters service call.");
521 }
522 
523 rcl_interfaces::msg::ListParametersResult
524 SyncParametersClient::list_parameters(
525  const std::vector<std::string> & parameter_prefixes,
526  uint64_t depth,
527  std::chrono::nanoseconds timeout)
528 {
529  auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
530 
531  using rclcpp::executors::spin_node_until_future_complete;
532  if (
533  spin_node_until_future_complete(
534  *executor_, node_base_interface_, f,
535  timeout) == rclcpp::FutureReturnCode::SUCCESS)
536  {
537  return f.get();
538  }
539 
540  throw std::runtime_error("Unable to get result of list parameters service call.");
541 }
RCLCPP_PUBLIC std::shared_future< std::vector< rcl_interfaces::msg::SetParametersResult > > load_parameters(const std::string &yaml_filename)
Load parameters from yaml file.
RCLCPP_PUBLIC bool service_is_ready() const
Return if the parameter services are ready.
RCLCPP_PUBLIC std::shared_future< std::vector< rcl_interfaces::msg::SetParametersResult > > delete_parameters(const std::vector< std::string > &parameters_names)
Delete several parameters at once.
RCLCPP_PUBLIC bool service_is_ready() const
Return if the service is ready.
Definition: client.cpp:104
FutureAndRequestId async_send_request(SharedRequest request)
Send a request to the service server.
Definition: client.hpp:620
Structure to store an arbitrary parameter with templated get/set methods.
Definition: parameter.hpp:53
static RCLCPP_PUBLIC Parameter from_parameter_msg(const rcl_interfaces::msg::Parameter &parameter)
Convert a parameter message in a Parameter class object.
Definition: parameter.cpp:145
Encapsulation of Quality of Service settings.
Definition: qos.hpp:116
rmw_qos_profile_t & get_rmw_qos_profile()
Return the rmw qos profile.
Definition: qos.cpp:102
std::vector< rcl_interfaces::msg::SetParametersResult > delete_parameters(const std::vector< std::string > &parameters_names, std::chrono::duration< RepT, RatioT > timeout=std::chrono::duration< RepT, RatioT >(-1))
Delete several parameters at once.
std::vector< rcl_interfaces::msg::SetParametersResult > load_parameters(const std::string &yaml_filename, std::chrono::duration< RepT, RatioT > timeout=std::chrono::duration< RepT, RatioT >(-1))
Load parameters from yaml file.
Thrown if passed parameters are inconsistent or invalid.
Definition: exceptions.hpp:252
RCL_PUBLIC RCL_WARN_UNUSED rcl_client_options_t rcl_client_get_default_options(void)
Return the default client options in a rcl_client_options_t.
Definition: client.c:280
RCLCPP_PUBLIC ParameterMap parameter_map_from_yaml_file(const std::string &yaml_filename, const char *node_fqn=nullptr)
RCLCPP_PUBLIC std::vector< Parameter > parameters_from_map(const ParameterMap &parameter_map, const char *node_fqn=nullptr)
FutureReturnCode
Return codes to be used with spin_until_future_complete.
std::unordered_map< std::string, std::vector< Parameter > > ParameterMap
A map of fully qualified node names to a list of parameters.
Options available for a rcl_client_t.
Definition: client.h:50
rmw_qos_profile_t qos
Middleware quality of service settings for the client.
Definition: client.h:52