ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
node_parameters.cpp
1 // Copyright 2016 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/node_interfaces/node_parameters.hpp"
16 
17 #include <rcl_yaml_param_parser/parser.h>
18 
19 #include <array>
20 #include <cmath>
21 #include <cstdlib>
22 #include <cstring>
23 #include <functional>
24 #include <limits>
25 #include <map>
26 #include <memory>
27 #include <sstream>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include "rcl_interfaces/srv/list_parameters.hpp"
33 #include "rclcpp/create_publisher.hpp"
34 #include "rclcpp/parameter_map.hpp"
35 #include "rcutils/logging_macros.h"
36 #include "rmw/qos_profiles.h"
37 
38 #include "../detail/resolve_parameter_overrides.hpp"
39 
41 
42 RCLCPP_LOCAL
43 void
44 local_perform_automatically_declare_parameters_from_overrides(
45  const std::map<std::string, rclcpp::ParameterValue> & parameter_overrides,
46  std::function<bool(const std::string &)> has_parameter,
47  std::function<void(
48  const std::string &,
49  const rclcpp::ParameterValue &,
50  const rcl_interfaces::msg::ParameterDescriptor &,
51  bool)>
52  declare_parameter)
53 {
54  rcl_interfaces::msg::ParameterDescriptor descriptor;
55  descriptor.dynamic_typing = true;
56  for (const auto & pair : parameter_overrides) {
57  if (!has_parameter(pair.first)) {
58  declare_parameter(
59  pair.first,
60  pair.second,
61  descriptor,
62  true);
63  }
64  }
65 }
66 
67 NodeParameters::NodeParameters(
68  const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
69  const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
70  rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
71  const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
72  const rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
73  const std::vector<rclcpp::Parameter> & parameter_overrides,
74  bool start_parameter_services,
75  bool start_parameter_event_publisher,
76  const rclcpp::QoS & parameter_event_qos,
77  const rclcpp::PublisherOptionsBase & parameter_event_publisher_options,
78  bool allow_undeclared_parameters,
79  bool automatically_declare_parameters_from_overrides)
80 : allow_undeclared_(allow_undeclared_parameters),
81  events_publisher_(nullptr),
82  node_logging_(node_logging),
83  node_clock_(node_clock)
84 {
85  using MessageT = rcl_interfaces::msg::ParameterEvent;
86  using PublisherT = rclcpp::Publisher<MessageT>;
87  using AllocatorT = std::allocator<void>;
88  // TODO(wjwwood): expose this allocator through the Parameter interface.
90  parameter_event_publisher_options);
91  publisher_options.allocator = std::make_shared<AllocatorT>();
92 
93  if (start_parameter_services) {
94  parameter_service_ = std::make_shared<ParameterService>(node_base, node_services, this);
95  }
96 
97  if (start_parameter_event_publisher) {
98  // TODO(ivanpauno): Qos of the `/parameters_event` topic should be somehow overridable.
99  events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
100  node_topics,
101  "/parameter_events",
102  parameter_event_qos,
103  publisher_options);
104  }
105 
106  // Get the node options
107  const rcl_node_t * node = node_base->get_rcl_node_handle();
108  if (nullptr == node) {
109  throw std::runtime_error("Need valid node handle in NodeParameters");
110  }
111  const rcl_node_options_t * options = rcl_node_get_options(node);
112  if (nullptr == options) {
113  throw std::runtime_error("Need valid node options in NodeParameters");
114  }
115 
116  const rcl_arguments_t * global_args = nullptr;
117  if (options->use_global_arguments) {
118  auto context_ptr = node_base->get_context()->get_rcl_context();
119  global_args = &(context_ptr->global_arguments);
120  }
121  combined_name_ = node_base->get_fully_qualified_name();
122 
123  parameter_overrides_ = rclcpp::detail::resolve_parameter_overrides(
124  combined_name_, parameter_overrides, &options->arguments, global_args);
125 
126  // If asked, initialize any parameters that ended up in the initial parameter values,
127  // but did not get declared explcitily by this point.
128  if (automatically_declare_parameters_from_overrides) {
129  using namespace std::placeholders;
130  local_perform_automatically_declare_parameters_from_overrides(
131  this->get_parameter_overrides(),
132  std::bind(&NodeParameters::has_parameter, this, _1),
133  [this](
134  const std::string & name,
135  const rclcpp::ParameterValue & default_value,
136  const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
137  bool ignore_override)
138  {
140  name, default_value, parameter_descriptor, ignore_override);
141  }
142  );
143  }
144 }
145 
146 void
147 NodeParameters::perform_automatically_declare_parameters_from_overrides()
148 {
149  local_perform_automatically_declare_parameters_from_overrides(
150  this->get_parameter_overrides(),
151  [this](const std::string & name) {
152  return this->has_parameter(name);
153  },
154  [this](
155  const std::string & name,
156  const rclcpp::ParameterValue & default_value,
157  const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
158  bool ignore_override)
159  {
160  this->declare_parameter(
161  name, default_value, parameter_descriptor, ignore_override);
162  }
163  );
164 }
165 
166 NodeParameters::~NodeParameters()
167 {}
168 
169 RCLCPP_LOCAL
170 bool
171 __lockless_has_parameter(
172  const std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameters,
173  const std::string & name)
174 {
175  return parameters.find(name) != parameters.end();
176 }
177 
178 // see https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
179 RCLCPP_LOCAL
180 bool
181 __are_doubles_equal(double x, double y, double ulp = 100.0)
182 {
183  if (!std::isfinite(x) || !std::isfinite(y)) {
184  return x == y;
185  }
186  return std::abs(x - y) <= std::numeric_limits<double>::epsilon() * std::abs(x + y) * ulp;
187 }
188 
189 static
190 std::string
191 format_range_reason(const std::string & name, const char * range_type)
192 {
193  std::ostringstream ss;
194  ss << "Parameter {" << name << "} doesn't comply with " << range_type << " range.";
195  return ss.str();
196 }
197 
198 RCLCPP_LOCAL
199 rcl_interfaces::msg::SetParametersResult
200 __check_integer_range(
201  const rcl_interfaces::msg::ParameterDescriptor & descriptor,
202  const int64_t value)
203 {
204  rcl_interfaces::msg::SetParametersResult result;
205  result.successful = true;
206  auto integer_range = descriptor.integer_range.at(0);
207  if (value == integer_range.from_value || value == integer_range.to_value) {
208  return result;
209  }
210  if ((value < integer_range.from_value) || (value > integer_range.to_value)) {
211  result.successful = false;
212  result.reason = format_range_reason(descriptor.name, "integer");
213  return result;
214  }
215  if (integer_range.step == 0) {
216  return result;
217  }
218  if (((value - integer_range.from_value) % integer_range.step) == 0) {
219  return result;
220  }
221  result.successful = false;
222  result.reason = format_range_reason(descriptor.name, "integer");
223  return result;
224 }
225 
226 RCLCPP_LOCAL
227 rcl_interfaces::msg::SetParametersResult
228 __check_double_range(
229  const rcl_interfaces::msg::ParameterDescriptor & descriptor,
230  const double value)
231 {
232  rcl_interfaces::msg::SetParametersResult result;
233  result.successful = true;
234  auto fp_range = descriptor.floating_point_range.at(0);
235  if (__are_doubles_equal(value, fp_range.from_value) || __are_doubles_equal(value,
236  fp_range.to_value))
237  {
238  return result;
239  }
240  if (!(value >= fp_range.from_value && value <= fp_range.to_value)) {
241  result.successful = false;
242  result.reason = format_range_reason(descriptor.name, "floating point");
243  return result;
244  }
245  if (fp_range.step == 0.0) {
246  return result;
247  }
248  double rounded_div = std::round((value - fp_range.from_value) / fp_range.step);
249  if (__are_doubles_equal(value, fp_range.from_value + rounded_div * fp_range.step)) {
250  return result;
251  }
252  result.successful = false;
253  result.reason = format_range_reason(descriptor.name, "floating point");
254  return result;
255 }
256 
257 RCLCPP_LOCAL
258 rcl_interfaces::msg::SetParametersResult
259 __check_parameter_value_in_range(
260  const rcl_interfaces::msg::ParameterDescriptor & descriptor,
261  const rclcpp::ParameterValue & value)
262 {
263  rcl_interfaces::msg::SetParametersResult result;
264  result.successful = true;
265  if (!descriptor.integer_range.empty() && value.get_type() == rclcpp::PARAMETER_INTEGER) {
266  result = __check_integer_range(descriptor, value.get<int64_t>());
267  return result;
268  }
269 
270  if (!descriptor.integer_range.empty() && value.get_type() == rclcpp::PARAMETER_INTEGER_ARRAY) {
271  std::vector<int64_t> val_array = value.get<std::vector<int64_t>>();
272  for (const int64_t & val : val_array) {
273  result = __check_integer_range(descriptor, val);
274  if (!result.successful) {
275  return result;
276  }
277  }
278  return result;
279  }
280 
281  if (!descriptor.floating_point_range.empty() && value.get_type() == rclcpp::PARAMETER_DOUBLE) {
282  result = __check_double_range(descriptor, value.get<double>());
283  return result;
284  }
285 
286  if (!descriptor.floating_point_range.empty() &&
287  value.get_type() == rclcpp::PARAMETER_DOUBLE_ARRAY)
288  {
289  std::vector<double> val_array = value.get<std::vector<double>>();
290  for (const double & val : val_array) {
291  result = __check_double_range(descriptor, val);
292  if (!result.successful) {
293  return result;
294  }
295  }
296  return result;
297  }
298 
299  return result;
300 }
301 
302 static
303 std::string
304 format_type_reason(
305  const std::string & name, const std::string & old_type, const std::string & new_type)
306 {
307  std::ostringstream ss;
308  // WARN: A condition later depends on this message starting with "Wrong parameter type",
309  // check `declare_parameter` if you modify this!
310  ss << "Wrong parameter type, parameter {" << name << "} is of type {" << old_type <<
311  "}, setting it to {" << new_type << "} is not allowed.";
312  return ss.str();
313 }
314 
315 // Return true if parameter values comply with the descriptors in parameter_infos.
316 RCLCPP_LOCAL
317 rcl_interfaces::msg::SetParametersResult
318 __check_parameters(
319  std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameter_infos,
320  const std::vector<rclcpp::Parameter> & parameters,
321  bool allow_undeclared)
322 {
323  rcl_interfaces::msg::SetParametersResult result;
324  result.successful = true;
325  for (const rclcpp::Parameter & parameter : parameters) {
326  std::string name = parameter.get_name();
327  rcl_interfaces::msg::ParameterDescriptor descriptor;
328  if (allow_undeclared) {
329  auto it = parameter_infos.find(name);
330  if (it != parameter_infos.cend()) {
331  descriptor = it->second.descriptor;
332  } else {
333  // implicitly declared parameters are dinamically typed!
334  descriptor.dynamic_typing = true;
335  }
336  } else {
337  descriptor = parameter_infos[name].descriptor;
338  }
339  if (descriptor.name.empty()) {
340  descriptor.name = name;
341  }
342  const auto new_type = parameter.get_type();
343  const auto specified_type = static_cast<rclcpp::ParameterType>(descriptor.type);
344  result.successful = descriptor.dynamic_typing || specified_type == new_type;
345  if (!result.successful) {
346  result.reason = format_type_reason(
347  name, rclcpp::to_string(specified_type), rclcpp::to_string(new_type));
348  return result;
349  }
350  result = __check_parameter_value_in_range(
351  descriptor,
352  parameter.get_parameter_value());
353  if (!result.successful) {
354  return result;
355  }
356  }
357  return result;
358 }
359 
360 using PreSetParametersCallbackType =
361  rclcpp::node_interfaces::NodeParametersInterface::PreSetParametersCallbackType;
364 using PreSetCallbacksHandleContainer =
365  rclcpp::node_interfaces::NodeParameters::PreSetCallbacksHandleContainer;
366 
367 using OnSetParametersCallbackType =
368  rclcpp::node_interfaces::NodeParametersInterface::OnSetParametersCallbackType;
371 using OnSetCallbacksHandleContainer =
372  rclcpp::node_interfaces::NodeParameters::OnSetCallbacksHandleContainer;
373 
374 using PostSetParametersCallbackType =
375  rclcpp::node_interfaces::NodeParametersInterface::PostSetParametersCallbackType;
378 using PostSetCallbacksHandleContainer =
379  rclcpp::node_interfaces::NodeParameters::PostSetCallbacksHandleContainer;
380 
381 RCLCPP_LOCAL
382 void
383 __call_pre_set_parameters_callbacks(
384  std::vector<rclcpp::Parameter> & parameters,
385  PreSetCallbacksHandleContainer & callback_container)
386 {
387  if (callback_container.empty()) {
388  return;
389  }
390 
391  auto it = callback_container.begin();
392  while (it != callback_container.end()) {
393  auto shared_handle = it->lock();
394  if (nullptr != shared_handle) {
395  shared_handle->callback(parameters);
396  it++;
397  } else {
398  it = callback_container.erase(it);
399  }
400  }
401 }
402 
403 RCLCPP_LOCAL
404 rcl_interfaces::msg::SetParametersResult
405 __call_on_set_parameters_callbacks(
406  const std::vector<rclcpp::Parameter> & parameters,
407  OnSetCallbacksHandleContainer & callback_container)
408 {
409  rcl_interfaces::msg::SetParametersResult result;
410  result.successful = true;
411  auto it = callback_container.begin();
412  while (it != callback_container.end()) {
413  auto shared_handle = it->lock();
414  if (nullptr != shared_handle) {
415  result = shared_handle->callback(parameters);
416  if (!result.successful) {
417  return result;
418  }
419  it++;
420  } else {
421  it = callback_container.erase(it);
422  }
423  }
424  return result;
425 }
426 
427 RCLCPP_LOCAL
428 void __call_post_set_parameters_callbacks(
429  const std::vector<rclcpp::Parameter> & parameters,
430  PostSetCallbacksHandleContainer & callback_container)
431 {
432  if (callback_container.empty()) {
433  return;
434  }
435 
436  auto it = callback_container.begin();
437  while (it != callback_container.end()) {
438  auto shared_handle = it->lock();
439  if (nullptr != shared_handle) {
440  shared_handle->callback(parameters);
441  it++;
442  } else {
443  it = callback_container.erase(it);
444  }
445  }
446 }
447 
448 RCLCPP_LOCAL
449 rcl_interfaces::msg::SetParametersResult
450 __set_parameters_atomically_common(
451  const std::vector<rclcpp::Parameter> & parameters,
452  std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameter_infos,
453  OnSetCallbacksHandleContainer & on_set_callback_container,
454  PostSetCallbacksHandleContainer & post_set_callback_container,
455  bool allow_undeclared = false)
456 {
457  // Check if the value being set complies with the descriptor.
458  rcl_interfaces::msg::SetParametersResult result = __check_parameters(
459  parameter_infos, parameters, allow_undeclared);
460  if (!result.successful) {
461  return result;
462  }
463  // Call the user callbacks to see if the new value(s) are allowed.
464  result =
465  __call_on_set_parameters_callbacks(parameters, on_set_callback_container);
466  if (!result.successful) {
467  return result;
468  }
469  // If accepted, actually set the values.
470  if (result.successful) {
471  for (size_t i = 0; i < parameters.size(); ++i) {
472  const std::string & name = parameters[i].get_name();
473  parameter_infos[name].descriptor.name = parameters[i].get_name();
474  parameter_infos[name].descriptor.type = parameters[i].get_type();
475  parameter_infos[name].value = parameters[i].get_parameter_value();
476  }
477  // Call the user post set parameter callback
478  __call_post_set_parameters_callbacks(parameters, post_set_callback_container);
479  }
480 
481  // Either way, return the result.
482  return result;
483 }
484 
485 RCLCPP_LOCAL
486 rcl_interfaces::msg::SetParametersResult
487 __declare_parameter_common(
488  const std::string & name,
489  const rclcpp::ParameterValue & default_value,
490  const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
491  std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameters_out,
492  const std::map<std::string, rclcpp::ParameterValue> & overrides,
493  OnSetCallbacksHandleContainer & on_set_callback_container,
494  PostSetCallbacksHandleContainer & post_set_callback_container,
495  rcl_interfaces::msg::ParameterEvent * parameter_event_out,
496  bool ignore_override = false)
497 {
499  std::map<std::string, ParameterInfo> parameter_infos {{name, ParameterInfo()}};
500  parameter_infos.at(name).descriptor = parameter_descriptor;
501 
502  // Use the value from the overrides if available, otherwise use the default.
503  const rclcpp::ParameterValue * initial_value = &default_value;
504  auto overrides_it = overrides.find(name);
505  if (!ignore_override && overrides_it != overrides.end()) {
506  initial_value = &overrides_it->second;
507  }
508 
509  // If there is no initial value, then skip initialization
510  if (initial_value->get_type() == rclcpp::PARAMETER_NOT_SET) {
511  // Add declared parameters to storage (without a value)
512  parameter_infos[name].descriptor.name = name;
513  if (parameter_descriptor.dynamic_typing) {
514  parameter_infos[name].descriptor.type = rclcpp::PARAMETER_NOT_SET;
515  } else {
516  parameter_infos[name].descriptor.type = parameter_descriptor.type;
517  }
518  parameters_out[name] = parameter_infos.at(name);
519  rcl_interfaces::msg::SetParametersResult result;
520  result.successful = true;
521  return result;
522  }
523 
524  // Check with the user's callbacks to see if the initial value can be set.
525  std::vector<rclcpp::Parameter> parameter_wrappers {rclcpp::Parameter(name, *initial_value)};
526  // This function also takes care of default vs initial value.
527  auto result = __set_parameters_atomically_common(
528  parameter_wrappers,
529  parameter_infos,
530  on_set_callback_container,
531  post_set_callback_container
532  );
533 
534  if (!result.successful) {
535  return result;
536  }
537 
538  // Add declared parameters to storage.
539  parameters_out[name] = parameter_infos.at(name);
540 
541  // Extend the given parameter event, if valid.
542  if (parameter_event_out) {
543  parameter_event_out->new_parameters.push_back(parameter_wrappers[0].to_parameter_msg());
544  }
545 
546  return result;
547 }
548 
549 static
551 declare_parameter_helper(
552  const std::string & name,
553  rclcpp::ParameterType type,
554  const rclcpp::ParameterValue & default_value,
555  rcl_interfaces::msg::ParameterDescriptor parameter_descriptor,
556  bool ignore_override,
557  std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameters,
558  const std::map<std::string, rclcpp::ParameterValue> & overrides,
559  OnSetCallbacksHandleContainer & on_set_callback_container,
560  PostSetCallbacksHandleContainer & post_set_callback_container,
562  const std::string & combined_name,
564 {
565  // TODO(sloretz) parameter name validation
566  if (name.empty()) {
567  throw rclcpp::exceptions::InvalidParametersException("parameter name must not be empty");
568  }
569 
570  // Error if this parameter has already been declared and is different
571  if (__lockless_has_parameter(parameters, name)) {
573  "parameter '" + name + "' has already been declared");
574  }
575 
576  if (!parameter_descriptor.dynamic_typing) {
577  if (rclcpp::PARAMETER_NOT_SET == type) {
578  type = default_value.get_type();
579  }
580  if (rclcpp::PARAMETER_NOT_SET == type) {
582  name,
583  "cannot declare a statically typed parameter with an uninitialized value"
584  };
585  }
586  parameter_descriptor.type = static_cast<uint8_t>(type);
587  }
588 
589  rcl_interfaces::msg::ParameterEvent parameter_event;
590  auto result = __declare_parameter_common(
591  name,
592  default_value,
593  parameter_descriptor,
594  parameters,
595  overrides,
596  on_set_callback_container,
597  post_set_callback_container,
598  &parameter_event,
599  ignore_override);
600 
601  // If it failed to be set, then throw an exception.
602  if (!result.successful) {
603  constexpr const char type_error_msg_start[] = "Wrong parameter type";
604  if (
605  0u == std::strncmp(
606  result.reason.c_str(), type_error_msg_start, sizeof(type_error_msg_start) - 1))
607  {
608  // TODO(ivanpauno): Refactor the logic so we don't need the above `strncmp` and we can
609  // detect between both exceptions more elegantly.
610  throw rclcpp::exceptions::InvalidParameterTypeException(name, result.reason);
611  }
613  "parameter '" + name + "' could not be set: " + result.reason);
614  }
615 
616  // Publish if events_publisher_ is not nullptr, which may be if disabled in the constructor.
617  if (nullptr != events_publisher) {
618  parameter_event.node = combined_name;
619  parameter_event.stamp = node_clock.get_clock()->now();
620  events_publisher->publish(parameter_event);
621  }
622 
623  return parameters.at(name).value;
624 }
625 
628  const std::string & name,
629  const rclcpp::ParameterValue & default_value,
630  const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
631  bool ignore_override)
632 {
633  std::lock_guard<std::recursive_mutex> lock(mutex_);
634  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
635 
636  return declare_parameter_helper(
637  name,
638  rclcpp::PARAMETER_NOT_SET,
639  default_value,
640  parameter_descriptor,
641  ignore_override,
642  parameters_,
643  parameter_overrides_,
644  on_set_parameters_callback_container_,
645  post_set_parameters_callback_container_,
646  events_publisher_.get(),
647  combined_name_,
648  *node_clock_);
649 }
650 
653  const std::string & name,
654  rclcpp::ParameterType type,
655  const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
656  bool ignore_override)
657 {
658  std::lock_guard<std::recursive_mutex> lock(mutex_);
659  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
660 
661  if (rclcpp::PARAMETER_NOT_SET == type) {
662  throw std::invalid_argument{
663  "declare_parameter(): the provided parameter type cannot be rclcpp::PARAMETER_NOT_SET"};
664  }
665 
666  if (parameter_descriptor.dynamic_typing == true) {
667  throw std::invalid_argument{
668  "declare_parameter(): cannot declare parameter of specific type and pass descriptor"
669  "with `dynamic_typing=true`"};
670  }
671 
672  return declare_parameter_helper(
673  name,
674  type,
676  parameter_descriptor,
677  ignore_override,
678  parameters_,
679  parameter_overrides_,
680  on_set_parameters_callback_container_,
681  post_set_parameters_callback_container_,
682  events_publisher_.get(),
683  combined_name_,
684  *node_clock_);
685 }
686 
687 void
688 NodeParameters::undeclare_parameter(const std::string & name)
689 {
690  std::lock_guard<std::recursive_mutex> lock(mutex_);
691 
692  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
693 
694  auto parameter_info = parameters_.find(name);
695  if (parameter_info == parameters_.end()) {
697  "cannot undeclare parameter '" + name + "' which has not yet been declared");
698  }
699 
700  if (parameter_info->second.descriptor.read_only) {
702  "cannot undeclare parameter '" + name + "' because it is read-only");
703  }
704  if (!parameter_info->second.descriptor.dynamic_typing) {
706  name, "cannot undeclare a statically typed parameter"};
707  }
708 
709  parameters_.erase(parameter_info);
710 }
711 
712 bool
713 NodeParameters::has_parameter(const std::string & name) const
714 {
715  std::lock_guard<std::recursive_mutex> lock(mutex_);
716 
717  return __lockless_has_parameter(parameters_, name);
718 }
719 
720 std::vector<rcl_interfaces::msg::SetParametersResult>
721 NodeParameters::set_parameters(const std::vector<rclcpp::Parameter> & parameters)
722 {
723  std::vector<rcl_interfaces::msg::SetParametersResult> results;
724  results.reserve(parameters.size());
725 
726  for (const auto & p : parameters) {
727  auto result = set_parameters_atomically({{p}});
728  results.push_back(result);
729  }
730 
731  return results;
732 }
733 
734 template<typename ParameterVectorType>
735 auto
736 __find_parameter_by_name(
737  ParameterVectorType & parameters,
738  const std::string & name)
739 {
740  return std::find_if(
741  parameters.begin(),
742  parameters.end(),
743  [&](auto parameter) {return parameter.get_name() == name;});
744 }
745 
746 rcl_interfaces::msg::SetParametersResult
747 NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters)
748 {
749  std::lock_guard<std::recursive_mutex> lock(mutex_);
750 
751  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
752 
753  rcl_interfaces::msg::SetParametersResult result;
754 
755  // call any user registered pre set parameter callbacks
756  // this callback can make changes to the original parameters list
757  // also check if the changed parameter list is empty or not, if empty return
758  std::vector<rclcpp::Parameter> parameters_after_pre_set_callback(parameters);
759  __call_pre_set_parameters_callbacks(
760  parameters_after_pre_set_callback,
761  pre_set_parameters_callback_container_);
762 
763  if (parameters_after_pre_set_callback.empty()) {
764  result.successful = false;
765  result.reason = "parameter list cannot be empty, this might be due to "
766  "pre_set_parameters_callback modifying the original parameters list.";
767  return result;
768  }
769 
770  // Check if any of the parameters are read-only, or if any parameters are not
771  // declared.
772  // If not declared, keep track of them in order to declare them later, when
773  // undeclared parameters are allowed, and if they're not allowed, fail.
774  std::vector<const rclcpp::Parameter *> parameters_to_be_declared;
775  for (const auto & parameter : parameters_after_pre_set_callback) {
776  const std::string & name = parameter.get_name();
777 
778  // Check to make sure the parameter name is valid.
779  if (name.empty()) {
780  throw rclcpp::exceptions::InvalidParametersException("parameter name must not be empty");
781  }
782 
783  // Check to see if it is declared.
784  auto parameter_info = parameters_.find(name);
785  if (parameter_info == parameters_.end()) {
786  // If not check to see if undeclared paramaters are allowed, ...
787  if (allow_undeclared_) {
788  // If so, mark the parameter to be declared for the user implicitly.
789  parameters_to_be_declared.push_back(&parameter);
790  // continue as it cannot be read-only, and because the declare will
791  // implicitly set the parameter and parameter_infos is for setting only.
792  continue;
793  } else {
794  // If not, then throw the exception as documented.
796  "parameter '" + name + "' cannot be set because it was not declared");
797  }
798  }
799 
800  // Check to see if it is read-only.
801  if (parameter_info->second.descriptor.read_only) {
802  result.successful = false;
803  result.reason = "parameter '" + name + "' cannot be set because it is read-only";
804  return result;
805  }
806  }
807 
808  // Declare parameters into a temporary "staging area", incase one of the declares fail.
809  // We will use the staged changes as input to the "set atomically" action.
810  // We explicitly avoid calling the user callbacks here, so that it may be called once, with
811  // all the other parameters to be set (already declared parameters).
812  std::map<std::string, rclcpp::node_interfaces::ParameterInfo> staged_parameter_changes;
813  rcl_interfaces::msg::ParameterEvent parameter_event_msg;
814  parameter_event_msg.node = combined_name_;
815  OnSetCallbacksHandleContainer empty_on_set_callback_container;
816  PostSetCallbacksHandleContainer empty_post_set_callback_container;
817 
818  // Implicit declare uses dynamic type descriptor.
819  rcl_interfaces::msg::ParameterDescriptor descriptor;
820  descriptor.dynamic_typing = true;
821  for (auto parameter_to_be_declared : parameters_to_be_declared) {
822  // This should not throw, because we validated the name and checked that
823  // the parameter was not already declared.
824  result = __declare_parameter_common(
825  parameter_to_be_declared->get_name(),
826  parameter_to_be_declared->get_parameter_value(),
827  descriptor,
828  staged_parameter_changes,
829  parameter_overrides_,
830  // Only call callbacks once below
831  empty_on_set_callback_container, // callback_container is explicitly empty
832  empty_post_set_callback_container, // callback_container is explicitly empty
833  &parameter_event_msg,
834  true);
835  if (!result.successful) {
836  // Declare failed, return knowing that nothing was changed because the
837  // staged changes were not applied.
838  return result;
839  }
840  }
841 
842  // If there were implicitly declared parameters, then we may need to copy the input parameters
843  // and then assign the value that was selected after the declare (could be affected by the
844  // initial parameter values).
845  const std::vector<rclcpp::Parameter> * parameters_to_be_set = &parameters_after_pre_set_callback;
846  std::vector<rclcpp::Parameter> parameters_copy;
847  if (0 != staged_parameter_changes.size()) { // If there were any implicitly declared parameters.
848  bool any_initial_values_used = false;
849  for (const auto & staged_parameter_change : staged_parameter_changes) {
850  auto it = __find_parameter_by_name(
851  parameters_after_pre_set_callback,
852  staged_parameter_change.first);
853  if (it->get_parameter_value() != staged_parameter_change.second.value) {
854  // In this case, the value of the staged parameter differs from the
855  // input from the user, and therefore we need to update things before setting.
856  any_initial_values_used = true;
857  // No need to search further since at least one initial value needs to be used.
858  break;
859  }
860  }
861  if (any_initial_values_used) {
862  parameters_copy = parameters_after_pre_set_callback;
863  for (const auto & staged_parameter_change : staged_parameter_changes) {
864  auto it = __find_parameter_by_name(parameters_copy, staged_parameter_change.first);
865  *it = Parameter(staged_parameter_change.first, staged_parameter_change.second.value);
866  }
867  parameters_to_be_set = &parameters_copy;
868  }
869  }
870 
871  // Collect parameters who will have had their type changed to
872  // rclcpp::PARAMETER_NOT_SET so they can later be implicitly undeclared.
873  std::vector<const rclcpp::Parameter *> parameters_to_be_undeclared;
874  for (const auto & parameter : *parameters_to_be_set) {
875  if (rclcpp::PARAMETER_NOT_SET == parameter.get_type()) {
876  auto it = parameters_.find(parameter.get_name());
877  if (it != parameters_.end() && rclcpp::PARAMETER_NOT_SET != it->second.value.get_type()) {
878  if (!it->second.descriptor.dynamic_typing) {
879  result.reason = "cannot undeclare a statically typed parameter";
880  result.successful = false;
881  return result;
882  }
883  parameters_to_be_undeclared.push_back(&parameter);
884  }
885  }
886  }
887 
888  // Set all of the parameters including the ones declared implicitly above.
889  result = __set_parameters_atomically_common(
890  // either the original parameters given by the user, or ones updated with initial values
891  *parameters_to_be_set,
892  // they are actually set on the official parameter storage
893  parameters_,
894  // These callbacks are called once. When a callback returns an unsuccessful result,
895  // the remaining aren't called
896  on_set_parameters_callback_container_,
897  post_set_parameters_callback_container_,
898  allow_undeclared_); // allow undeclared
899 
900  // If not successful, then stop here.
901  if (!result.successful) {
902  return result;
903  }
904 
905  // If successful, then update the parameter infos from the implicitly declared parameter's.
906  for (const auto & kv_pair : staged_parameter_changes) {
907  // assumption: the parameter is already present in parameters_ due to the above "set"
908  assert(__lockless_has_parameter(parameters_, kv_pair.first));
909  // assumption: the value in parameters_ is the same as the value resulting from the declare
910  assert(parameters_[kv_pair.first].value == kv_pair.second.value);
911  // This assignment should not change the name, type, or value, but may
912  // change other things from the ParameterInfo.
913  parameters_[kv_pair.first] = kv_pair.second;
914  }
915 
916  // Undeclare parameters that need to be.
917  for (auto parameter_to_undeclare : parameters_to_be_undeclared) {
918  auto it = parameters_.find(parameter_to_undeclare->get_name());
919  // assumption: the parameter to be undeclared should be in the parameter infos map
920  assert(it != parameters_.end());
921  if (it != parameters_.end()) {
922  // Update the parameter event message and remove it.
923  parameter_event_msg.deleted_parameters.push_back(
924  rclcpp::Parameter(it->first, it->second.value).to_parameter_msg());
925  parameters_.erase(it);
926  }
927  }
928 
929  // Update the parameter event message for any parameters which were only set,
930  // and not either declared or undeclared.
931  for (const auto & parameter : *parameters_to_be_set) {
932  if (staged_parameter_changes.find(parameter.get_name()) != staged_parameter_changes.end()) {
933  // This parameter was declared.
934  continue;
935  }
936  auto it = std::find_if(
937  parameters_to_be_undeclared.begin(),
938  parameters_to_be_undeclared.end(),
939  [&parameter](const auto & p) {return p->get_name() == parameter.get_name();});
940  if (it != parameters_to_be_undeclared.end()) {
941  // This parameter was undeclared (deleted).
942  continue;
943  }
944  // This parameter was neither declared nor undeclared.
945  parameter_event_msg.changed_parameters.push_back(parameter.to_parameter_msg());
946  }
947 
948  // Publish if events_publisher_ is not nullptr, which may be if disabled in the constructor.
949  if (nullptr != events_publisher_) {
950  parameter_event_msg.stamp = node_clock_->get_clock()->now();
951  events_publisher_->publish(parameter_event_msg);
952  }
953  return result;
954 }
955 
956 std::vector<rclcpp::Parameter>
957 NodeParameters::get_parameters(const std::vector<std::string> & names) const
958 {
959  std::vector<rclcpp::Parameter> results;
960  results.reserve(names.size());
961 
962  std::lock_guard<std::recursive_mutex> lock(mutex_);
963  for (auto & name : names) {
964  results.emplace_back(this->get_parameter(name));
965  }
966  return results;
967 }
968 
970 NodeParameters::get_parameter(const std::string & name) const
971 {
972  std::lock_guard<std::recursive_mutex> lock(mutex_);
973 
974  auto param_iter = parameters_.find(name);
975  if (parameters_.end() != param_iter) {
976  if (
977  param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET ||
978  param_iter->second.descriptor.dynamic_typing)
979  {
980  return rclcpp::Parameter{name, param_iter->second.value};
981  }
983  } else if (this->allow_undeclared_) {
984  return rclcpp::Parameter{name};
985  } else {
987  }
988 }
989 
990 bool
992  const std::string & name,
993  rclcpp::Parameter & parameter) const
994 {
995  std::lock_guard<std::recursive_mutex> lock(mutex_);
996 
997  auto param_iter = parameters_.find(name);
998  if (
999  parameters_.end() != param_iter &&
1000  param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
1001  {
1002  parameter = {name, param_iter->second.value};
1003  return true;
1004  } else {
1005  return false;
1006  }
1007 }
1008 
1009 bool
1011  const std::string & prefix,
1012  std::map<std::string, rclcpp::Parameter> & parameters) const
1013 {
1014  std::lock_guard<std::recursive_mutex> lock(mutex_);
1015 
1016  std::string prefix_with_dot = prefix.empty() ? prefix : prefix + ".";
1017  bool ret = false;
1018 
1019  for (const auto & param : parameters_) {
1020  if (param.first.find(prefix_with_dot) == 0 && param.first.length() > prefix_with_dot.length()) {
1021  // Found one!
1022  parameters[param.first.substr(prefix_with_dot.length())] = rclcpp::Parameter(param.second);
1023  ret = true;
1024  }
1025  }
1026 
1027  return ret;
1028 }
1029 
1030 std::vector<rcl_interfaces::msg::ParameterDescriptor>
1031 NodeParameters::describe_parameters(const std::vector<std::string> & names) const
1032 {
1033  std::lock_guard<std::recursive_mutex> lock(mutex_);
1034  std::vector<rcl_interfaces::msg::ParameterDescriptor> results;
1035  results.reserve(names.size());
1036 
1037  for (const auto & name : names) {
1038  auto it = parameters_.find(name);
1039  if (it != parameters_.cend()) {
1040  results.push_back(it->second.descriptor);
1041  } else if (allow_undeclared_) {
1042  // parameter not found, but undeclared allowed, so return empty
1043  rcl_interfaces::msg::ParameterDescriptor default_description;
1044  default_description.name = name;
1045  results.push_back(default_description);
1046  } else {
1048  }
1049  }
1050 
1051  if (results.size() != names.size()) {
1052  throw std::runtime_error("results and names unexpectedly different sizes");
1053  }
1054 
1055  return results;
1056 }
1057 
1058 std::vector<uint8_t>
1059 NodeParameters::get_parameter_types(const std::vector<std::string> & names) const
1060 {
1061  std::lock_guard<std::recursive_mutex> lock(mutex_);
1062  std::vector<uint8_t> results;
1063  results.reserve(names.size());
1064 
1065  for (const auto & name : names) {
1066  auto it = parameters_.find(name);
1067  if (it != parameters_.cend()) {
1068  results.push_back(it->second.value.get_type());
1069  } else if (allow_undeclared_) {
1070  // parameter not found, but undeclared allowed, so return not set
1071  results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
1072  } else {
1074  }
1075  }
1076 
1077  if (results.size() != names.size()) {
1078  throw std::runtime_error("results and names unexpectedly different sizes");
1079  }
1080 
1081  return results;
1082 }
1083 
1084 rcl_interfaces::msg::ListParametersResult
1085 NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const
1086 {
1087  std::lock_guard<std::recursive_mutex> lock(mutex_);
1088  rcl_interfaces::msg::ListParametersResult result;
1089 
1090  // TODO(mikaelarguedas) define parameter separator different from "/" to avoid ambiguity
1091  // using "." for now
1092  const char * separator = ".";
1093 
1094  auto separators_less_than_depth = [&depth, &separator](const std::string & str) -> bool {
1095  return static_cast<uint64_t>(std::count(str.begin(), str.end(), *separator)) < depth;
1096  };
1097 
1098  bool recursive = (prefixes.size() == 0) &&
1099  (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
1100 
1101  for (const std::pair<const std::string, ParameterInfo> & kv : parameters_) {
1102  if (!recursive) {
1103  bool get_all = (prefixes.size() == 0) && separators_less_than_depth(kv.first);
1104  if (!get_all) {
1105  bool prefix_matches = std::any_of(
1106  prefixes.cbegin(), prefixes.cend(),
1107  [&kv, &depth, &separator, &separators_less_than_depth](const std::string & prefix) {
1108  if (kv.first == prefix) {
1109  return true;
1110  } else if (kv.first.find(prefix + separator) == 0) {
1111  if (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) {
1112  return true;
1113  }
1114  std::string substr = kv.first.substr(prefix.length() + 1);
1115  return separators_less_than_depth(substr);
1116  }
1117  return false;
1118  });
1119 
1120  if (!prefix_matches) {
1121  continue;
1122  }
1123  }
1124  }
1125 
1126  result.names.push_back(kv.first);
1127  size_t last_separator = kv.first.find_last_of(separator);
1128  if (std::string::npos != last_separator) {
1129  std::string prefix = kv.first.substr(0, last_separator);
1130  if (
1131  std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
1132  result.prefixes.cend())
1133  {
1134  result.prefixes.push_back(prefix);
1135  }
1136  }
1137  }
1138  return result;
1139 }
1140 
1141 void
1143  const PreSetParametersCallbackHandle * const handle)
1144 {
1145  std::lock_guard<std::recursive_mutex> lock(mutex_);
1146  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
1147 
1148  auto it = std::find_if(
1149  pre_set_parameters_callback_container_.begin(),
1150  pre_set_parameters_callback_container_.end(),
1151  [handle](const auto & weak_handle) {
1152  return handle == weak_handle.lock().get();
1153  });
1154  if (it != pre_set_parameters_callback_container_.end()) {
1155  pre_set_parameters_callback_container_.erase(it);
1156  } else {
1157  throw std::runtime_error("Pre set parameter callback doesn't exist");
1158  }
1159 }
1160 
1161 void
1163  const OnSetParametersCallbackHandle * const handle)
1164 {
1165  std::lock_guard<std::recursive_mutex> lock(mutex_);
1166  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
1167 
1168  auto it = std::find_if(
1169  on_set_parameters_callback_container_.begin(),
1170  on_set_parameters_callback_container_.end(),
1171  [handle](const auto & weak_handle) {
1172  return handle == weak_handle.lock().get();
1173  });
1174  if (it != on_set_parameters_callback_container_.end()) {
1175  on_set_parameters_callback_container_.erase(it);
1176  } else {
1177  throw std::runtime_error("On set parameter callback doesn't exist");
1178  }
1179 }
1180 
1181 void
1183  const PostSetParametersCallbackHandle * const handle)
1184 {
1185  std::lock_guard<std::recursive_mutex> lock(mutex_);
1186  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
1187 
1188  auto it = std::find_if(
1189  post_set_parameters_callback_container_.begin(),
1190  post_set_parameters_callback_container_.end(),
1191  [handle](const auto & weak_handle) {
1192  return handle == weak_handle.lock().get();
1193  });
1194  if (it != post_set_parameters_callback_container_.end()) {
1195  post_set_parameters_callback_container_.erase(it);
1196  } else {
1197  throw std::runtime_error("Post set parameter callback doesn't exist");
1198  }
1199 }
1200 
1201 PreSetParametersCallbackHandle::SharedPtr
1202 NodeParameters::add_pre_set_parameters_callback(PreSetParametersCallbackType callback)
1203 {
1204  std::lock_guard<std::recursive_mutex> lock(mutex_);
1205  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
1206 
1207  auto handle = std::make_shared<PreSetParametersCallbackHandle>();
1208  handle->callback = callback;
1209  // the last callback registered is executed first.
1210  pre_set_parameters_callback_container_.emplace_front(handle);
1211  return handle;
1212 }
1213 
1214 OnSetParametersCallbackHandle::SharedPtr
1215 NodeParameters::add_on_set_parameters_callback(OnSetParametersCallbackType callback)
1216 {
1217  std::lock_guard<std::recursive_mutex> lock(mutex_);
1218  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
1219 
1220  auto handle = std::make_shared<OnSetParametersCallbackHandle>();
1221  handle->callback = callback;
1222  // the last callback registered is executed first.
1223  on_set_parameters_callback_container_.emplace_front(handle);
1224  return handle;
1225 }
1226 
1227 PostSetParametersCallbackHandle::SharedPtr
1229  PostSetParametersCallbackType callback)
1230 {
1231  std::lock_guard<std::recursive_mutex> lock(mutex_);
1232  ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
1233 
1234  auto handle = std::make_shared<PostSetParametersCallbackHandle>();
1235  handle->callback = callback;
1236  // the last callback registered is executed first.
1237  post_set_parameters_callback_container_.emplace_front(handle);
1238  return handle;
1239 }
1240 
1241 const std::map<std::string, rclcpp::ParameterValue> &
1243 {
1244  return parameter_overrides_;
1245 }
1246 
1247 void
1249 {
1250  std::lock_guard<std::recursive_mutex> lock(mutex_);
1251  parameter_modification_enabled_ = true;
1252 }
Store the type and value of a parameter.
RCLCPP_PUBLIC ParameterType get_type() const
Return an enum indicating the type of the set value.
Structure to store an arbitrary parameter with templated get/set methods.
Definition: parameter.hpp:53
RCLCPP_PUBLIC rcl_interfaces::msg::Parameter to_parameter_msg() const
Convert the class in a parameter message.
Definition: parameter.cpp:151
A publisher publishes messages of any type to a topic.
Definition: publisher.hpp:81
std::enable_if_t< rosidl_generator_traits::is_message< T >::value &&std::is_same< T, ROSMessageType >::value > publish(std::unique_ptr< T, ROSMessageTypeDeleter > msg)
Publish a message on the topic.
Definition: publisher.hpp:223
Encapsulation of Quality of Service settings.
Definition: qos.hpp:116
Thrown if requested parameter type is invalid.
Definition: exceptions.hpp:271
Thrown if passed parameter value is invalid.
Definition: exceptions.hpp:260
Thrown if passed parameters are inconsistent or invalid.
Definition: exceptions.hpp:252
Thrown if parameter is already declared.
Definition: exceptions.hpp:303
Thrown if parameter is immutable and therefore cannot be undeclared.
Definition: exceptions.hpp:317
Thrown if parameter is not declared, e.g. either set or get was called without first declaring.
Definition: exceptions.hpp:310
Thrown when an uninitialized parameter is accessed.
Definition: exceptions.hpp:331
Pure virtual interface class for the NodeClock part of the Node API.
virtual RCLCPP_PUBLIC rclcpp::Clock::SharedPtr get_clock()=0
Get a ROS clock which will be kept up to date by the node.
Implementation of the NodeParameters part of the Node API.
RCLCPP_PUBLIC RCUTILS_WARN_UNUSED PostSetParametersCallbackHandle::SharedPtr add_post_set_parameters_callback(PostSetParametersCallbackType callback) override
Add a callback that gets triggered after parameters are set successfully.
RCLCPP_PUBLIC const std::map< std::string, rclcpp::ParameterValue > & get_parameter_overrides() const override
Return the initial parameter values used by the NodeParameters to override default values.
RCLCPP_PUBLIC void remove_on_set_parameters_callback(const OnSetParametersCallbackHandle *const handler) override
Remove a callback registered with add_on_set_parameters_callback.
RCLCPP_PUBLIC std::vector< rclcpp::Parameter > get_parameters(const std::vector< std::string > &names) const override
Get descriptions of parameters given their names.
RCLCPP_PUBLIC void undeclare_parameter(const std::string &name) override
Undeclare a parameter.
RCLCPP_PUBLIC const rclcpp::ParameterValue & declare_parameter(const std::string &name, const rclcpp::ParameterValue &default_value, const rcl_interfaces::msg::ParameterDescriptor &parameter_descriptor=rcl_interfaces::msg::ParameterDescriptor{}, bool ignore_override=false) override
Declare and initialize a parameter.
RCLCPP_PUBLIC RCUTILS_WARN_UNUSED OnSetParametersCallbackHandle::SharedPtr add_on_set_parameters_callback(OnSetParametersCallbackType callback) override
Add a callback to validate parameters before they are set.
RCLCPP_PUBLIC bool get_parameters_by_prefix(const std::string &prefix, std::map< std::string, rclcpp::Parameter > &parameters) const override
Get all parameters that have the specified prefix into the parameters map.
RCLCPP_PUBLIC std::vector< rcl_interfaces::msg::SetParametersResult > set_parameters(const std::vector< rclcpp::Parameter > &parameters) override
Set one or more parameters, one at a time.
RCLCPP_PUBLIC void remove_pre_set_parameters_callback(const PreSetParametersCallbackHandle *const handler) override
Remove a callback registered with add_pre_set_parameters_callback.
RCLCPP_PUBLIC void enable_parameter_modification() override
Enable parameter modification recursively during parameter callbacks.
RCLCPP_PUBLIC rcl_interfaces::msg::SetParametersResult set_parameters_atomically(const std::vector< rclcpp::Parameter > &parameters) override
Set one or more parameters, all at once.
RCLCPP_PUBLIC RCUTILS_WARN_UNUSED PreSetParametersCallbackHandle::SharedPtr add_pre_set_parameters_callback(PreSetParametersCallbackType callback) override
Add a callback that gets triggered before parameters are validated.
RCLCPP_PUBLIC rclcpp::Parameter get_parameter(const std::string &name) const override
Get the description of one parameter given a name.
RCLCPP_PUBLIC void remove_post_set_parameters_callback(const PostSetParametersCallbackHandle *const handler) override
Remove a callback registered with add_post_set_parameters_callback.
RCLCPP_PUBLIC bool has_parameter(const std::string &name) const override
Return true if the parameter has been declared, otherwise false.
RCLCPP_PUBLIC std::string to_string(const FutureReturnCode &future_return_code)
String conversion function for FutureReturnCode.
RCL_PUBLIC RCL_WARN_UNUSED const rcl_node_options_t * rcl_node_get_options(const rcl_node_t *node)
Return the rcl node options.
Definition: node.c:443
Hold output of parsing command line arguments.
Definition: arguments.h:36
Structure which encapsulates the options for creating a rcl_node_t.
Definition: node_options.h:35
bool use_global_arguments
If false then only use arguments in this struct, otherwise use global arguments also.
Definition: node_options.h:47
rcl_arguments_t arguments
Command line arguments that apply only to this node.
Definition: node_options.h:50
Structure which encapsulates a ROS Node.
Definition: node.h:45
Non-templated part of PublisherOptionsWithAllocator<Allocator>.
Structure containing optional configuration for Publishers.
std::shared_ptr< Allocator > allocator
Optional custom allocator.
rcl_interfaces::msg::ParameterDescriptor descriptor
A description of the parameter.