ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
parameter_event_handler.cpp
1 // Copyright 2019 Intel Corporation
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 <functional>
16 #include <memory>
17 #include <string>
18 #include <unordered_map>
19 #include <utility>
20 #include <vector>
21 
22 #include "rclcpp/parameter_event_handler.hpp"
23 #include "rcpputils/join.hpp"
24 
25 namespace rclcpp
26 {
27 
28 ParameterEventCallbackHandle::SharedPtr
30  ParameterEventCallbackType callback)
31 {
32  std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
33  auto handle = std::make_shared<ParameterEventCallbackHandle>();
34  handle->callback = callback;
35  callbacks_->event_callbacks_.emplace_front(handle);
36 
37  return handle;
38 }
39 
40 void
42  ParameterEventCallbackHandle::SharedPtr callback_handle)
43 {
44  std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
45  auto it = std::find_if(
46  callbacks_->event_callbacks_.begin(),
47  callbacks_->event_callbacks_.end(),
48  [callback_handle](const auto & weak_handle) {
49  return callback_handle.get() == weak_handle.lock().get();
50  });
51  if (it != callbacks_->event_callbacks_.end()) {
52  callbacks_->event_callbacks_.erase(it);
53  } else {
54  throw std::runtime_error("Callback doesn't exist");
55  }
56 }
57 
58 ParameterCallbackHandle::SharedPtr
60  const std::string & parameter_name,
61  ParameterCallbackType callback,
62  const std::string & node_name)
63 {
64  std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
65  auto full_node_name = resolve_path(node_name);
66 
67  auto handle = std::make_shared<ParameterCallbackHandle>();
68  handle->callback = callback;
69  handle->parameter_name = parameter_name;
70  handle->node_name = full_node_name;
71  // the last callback registered is executed first.
72  callbacks_->parameter_callbacks_[{parameter_name, full_node_name}].emplace_front(handle);
73 
74  return handle;
75 }
76 
77 void
79  ParameterCallbackHandle::SharedPtr callback_handle)
80 {
81  std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
82  auto handle = callback_handle.get();
83  auto & container = callbacks_->parameter_callbacks_[{handle->parameter_name, handle->node_name}];
84  auto it = std::find_if(
85  container.begin(),
86  container.end(),
87  [handle](const auto & weak_handle) {
88  return handle == weak_handle.lock().get();
89  });
90  if (it != container.end()) {
91  container.erase(it);
92  if (container.empty()) {
93  callbacks_->parameter_callbacks_.erase({handle->parameter_name, handle->node_name});
94  }
95  } else {
96  throw std::runtime_error("Callback doesn't exist");
97  }
98 }
99 
100 bool
102  const rcl_interfaces::msg::ParameterEvent & event,
103  rclcpp::Parameter & parameter,
104  const std::string & parameter_name,
105  const std::string & node_name)
106 {
107  if (event.node != node_name) {
108  return false;
109  }
110 
111  for (auto & new_parameter : event.new_parameters) {
112  if (new_parameter.name == parameter_name) {
113  parameter = rclcpp::Parameter::from_parameter_msg(new_parameter);
114  return true;
115  }
116  }
117 
118  for (auto & changed_parameter : event.changed_parameters) {
119  if (changed_parameter.name == parameter_name) {
120  parameter = rclcpp::Parameter::from_parameter_msg(changed_parameter);
121  return true;
122  }
123  }
124 
125  return false;
126 }
127 
130  const rcl_interfaces::msg::ParameterEvent & event,
131  const std::string & parameter_name,
132  const std::string & node_name)
133 {
135  if (!get_parameter_from_event(event, p, parameter_name, node_name)) {
136  throw std::runtime_error(
137  "Parameter '" + parameter_name + "' of node '" + node_name +
138  "' is not part of parameter event");
139  }
140  return p;
141 }
142 
143 std::vector<rclcpp::Parameter>
145  const rcl_interfaces::msg::ParameterEvent & event)
146 {
147  std::vector<rclcpp::Parameter> params;
148 
149  for (auto & new_parameter : event.new_parameters) {
150  params.push_back(rclcpp::Parameter::from_parameter_msg(new_parameter));
151  }
152 
153  for (auto & changed_parameter : event.changed_parameters) {
154  params.push_back(rclcpp::Parameter::from_parameter_msg(changed_parameter));
155  }
156 
157  return params;
158 }
159 
160 void
161 ParameterEventHandler::Callbacks::event_callback(const rcl_interfaces::msg::ParameterEvent & event)
162 {
163  std::lock_guard<std::recursive_mutex> lock(mutex_);
164 
165  for (auto it = parameter_callbacks_.begin(); it != parameter_callbacks_.end(); ++it) {
167  if (get_parameter_from_event(event, p, it->first.first, it->first.second)) {
168  for (auto cb = it->second.begin(); cb != it->second.end(); ++cb) {
169  auto shared_handle = cb->lock();
170  if (nullptr != shared_handle) {
171  shared_handle->callback(p);
172  } else {
173  cb = it->second.erase(cb);
174  }
175  }
176  }
177  }
178 
179  for (auto event_cb = event_callbacks_.begin(); event_cb != event_callbacks_.end(); ++event_cb) {
180  auto shared_event_handle = event_cb->lock();
181  if (nullptr != shared_event_handle) {
182  shared_event_handle->callback(event);
183  } else {
184  event_cb = event_callbacks_.erase(event_cb);
185  }
186  }
187 }
188 
189 std::string
190 ParameterEventHandler::resolve_path(const std::string & path)
191 {
192  std::string full_path;
193 
194  if (path == "") {
195  full_path = node_base_->get_fully_qualified_name();
196  } else {
197  full_path = path;
198  if (*path.begin() != '/') {
199  auto ns = node_base_->get_namespace();
200  const std::vector<std::string> paths{ns, path};
201  full_path = (ns == std::string("/")) ? ns + path : rcpputils::join(paths, "/");
202  }
203  }
204 
205  return full_path;
206 }
207 
208 } // namespace rclcpp
RCLCPP_PUBLIC void remove_parameter_callback(ParameterCallbackHandle::SharedPtr callback_handle)
Remove a parameter callback registered with add_parameter_callback.
static RCLCPP_PUBLIC bool get_parameter_from_event(const rcl_interfaces::msg::ParameterEvent &event, rclcpp::Parameter &parameter, const std::string &parameter_name, const std::string &node_name="")
Get an rclcpp::Parameter from a parameter event.
static RCLCPP_PUBLIC std::vector< rclcpp::Parameter > get_parameters_from_event(const rcl_interfaces::msg::ParameterEvent &event)
Get all rclcpp::Parameter values from a parameter event.
RCLCPP_PUBLIC void remove_parameter_event_callback(ParameterEventCallbackHandle::SharedPtr callback_handle)
Remove parameter event callback registered with add_parameter_event_callback.
RCLCPP_PUBLIC ParameterEventCallbackHandle::SharedPtr add_parameter_event_callback(ParameterEventCallbackType callback)
Set a callback for all parameter events.
RCLCPP_PUBLIC ParameterCallbackHandle::SharedPtr add_parameter_callback(const std::string &parameter_name, ParameterCallbackType callback, const std::string &node_name="")
Add a callback for a specified parameter.
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
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC void event_callback(const rcl_interfaces::msg::ParameterEvent &event)
Callback for parameter events subscriptions.