ROS 2 rclcpp + rcl - lyrical  lyrical
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  const 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  const 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  const 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 bool
78 ParameterEventHandler::configure_nodes_filter(const std::vector<std::string> & node_names)
79 {
80  if (!event_subscription_->is_cft_supported()) {
81  return false;
82  }
83 
84  if (node_names.empty()) {
85  // Clear content filter
86  event_subscription_->set_content_filter(std::string());
87  if (event_subscription_->is_cft_enabled()) {
88  return false;
89  }
90  return true;
91  }
92 
93  std::string filter_expression;
94  size_t total = node_names.size();
95  for (size_t i = 0; i < total; ++i) {
96  filter_expression += "node = %" + std::to_string(i);
97  if (i < total - 1) {
98  filter_expression += " OR ";
99  }
100  }
101 
102  // Enclose each node name in "'".
103  std::vector<std::string> quoted_node_names;
104  const std::string delim("'");
105  for (const auto & name : node_names) {
106  quoted_node_names.push_back(delim + resolve_path(name) + delim);
107  }
108 
109  event_subscription_->set_content_filter(filter_expression, quoted_node_names);
110 
111  return event_subscription_->is_cft_enabled();
112 }
113 
114 void
116  const ParameterCallbackHandle::SharedPtr & callback_handle)
117 {
118  std::lock_guard<std::recursive_mutex> lock(callbacks_->mutex_);
119  auto handle = callback_handle.get();
120  auto & container = callbacks_->parameter_callbacks_[{handle->parameter_name, handle->node_name}];
121  auto it = std::find_if(
122  container.begin(),
123  container.end(),
124  [handle](const auto & weak_handle) {
125  return handle == weak_handle.lock().get();
126  });
127  if (it != container.end()) {
128  container.erase(it);
129  if (container.empty()) {
130  callbacks_->parameter_callbacks_.erase({handle->parameter_name, handle->node_name});
131  }
132  } else {
133  throw std::runtime_error("Callback doesn't exist");
134  }
135 }
136 
137 bool
139  const rcl_interfaces::msg::ParameterEvent & event,
140  rclcpp::Parameter & parameter,
141  const std::string & parameter_name,
142  const std::string & node_name)
143 {
144  if (event.node != node_name) {
145  return false;
146  }
147 
148  for (auto & new_parameter : event.new_parameters) {
149  if (new_parameter.name == parameter_name) {
150  parameter = rclcpp::Parameter::from_parameter_msg(new_parameter);
151  return true;
152  }
153  }
154 
155  for (auto & changed_parameter : event.changed_parameters) {
156  if (changed_parameter.name == parameter_name) {
157  parameter = rclcpp::Parameter::from_parameter_msg(changed_parameter);
158  return true;
159  }
160  }
161 
162  return false;
163 }
164 
167  const rcl_interfaces::msg::ParameterEvent & event,
168  const std::string & parameter_name,
169  const std::string & node_name)
170 {
172  if (!get_parameter_from_event(event, p, parameter_name, node_name)) {
173  if (event.node == node_name) {
174  return rclcpp::Parameter(parameter_name, rclcpp::PARAMETER_NOT_SET);
175  } else {
176  throw std::runtime_error(
177  "The node name '" + node_name + "' of parameter '" + parameter_name +
178  +"' doesn't match the node name '" + event.node + "' in parameter event");
179  }
180  }
181  return p;
182 }
183 
184 std::vector<rclcpp::Parameter>
186  const rcl_interfaces::msg::ParameterEvent & event)
187 {
188  std::vector<rclcpp::Parameter> params;
189 
190  for (auto & new_parameter : event.new_parameters) {
191  params.push_back(rclcpp::Parameter::from_parameter_msg(new_parameter));
192  }
193 
194  for (auto & changed_parameter : event.changed_parameters) {
195  params.push_back(rclcpp::Parameter::from_parameter_msg(changed_parameter));
196  }
197 
198  return params;
199 }
200 
201 void
202 ParameterEventHandler::Callbacks::event_callback(const rcl_interfaces::msg::ParameterEvent & event)
203 {
204  std::lock_guard<std::recursive_mutex> lock(mutex_);
205 
206  for (auto it = parameter_callbacks_.begin(); it != parameter_callbacks_.end(); ++it) {
208  if (get_parameter_from_event(event, p, it->first.first, it->first.second)) {
209  for (auto cb = it->second.begin(); cb != it->second.end(); ++cb) {
210  auto shared_handle = cb->lock();
211  if (nullptr != shared_handle) {
212  shared_handle->callback(p);
213  } else {
214  cb = it->second.erase(cb);
215  }
216  }
217  }
218  }
219 
220  for (auto event_cb = event_callbacks_.begin(); event_cb != event_callbacks_.end(); ++event_cb) {
221  auto shared_event_handle = event_cb->lock();
222  if (nullptr != shared_event_handle) {
223  shared_event_handle->callback(event);
224  } else {
225  event_cb = event_callbacks_.erase(event_cb);
226  }
227  }
228 }
229 
230 std::string
231 ParameterEventHandler::resolve_path(const std::string & path)
232 {
233  if (path.empty()) {
234  return node_base_->get_fully_qualified_name();
235  }
236 
237  if (*path.begin() != '/') {
238  auto ns = node_base_->get_namespace();
239  const std::vector<std::string> paths{ns, path};
240  if(ns == std::string("/")) {
241  return ns + path;
242  }
243 
244  return rcpputils::join(paths, "/");
245  }
246 
247  return path;
248 }
249 
250 } // namespace rclcpp
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 RCUTILS_WARN_UNUSED ParameterCallbackHandle::SharedPtr add_parameter_callback(const std::string &parameter_name, const ParameterCallbackType &callback, const std::string &node_name="")
Add a callback for a specified parameter.
RCLCPP_PUBLIC void remove_parameter_callback(const ParameterCallbackHandle::SharedPtr &callback_handle)
Remove a parameter callback registered with add_parameter_callback.
RCLCPP_PUBLIC bool configure_nodes_filter(const std::vector< std::string > &node_names)
Configure which node parameter events will be received.
RCLCPP_PUBLIC void remove_parameter_event_callback(const ParameterEventCallbackHandle::SharedPtr &callback_handle)
Remove parameter event callback registered with add_parameter_event_callback.
RCLCPP_PUBLIC RCUTILS_WARN_UNUSED ParameterEventCallbackHandle::SharedPtr add_parameter_event_callback(const ParameterEventCallbackType &callback)
Set a callback for all parameter events.
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
RCLCPP_PUBLIC bool is_cft_supported() const
Check if content filtered topic feature of the subscription instance is supported.
RCLCPP_PUBLIC void set_content_filter(const std::string &filter_expression, const std::vector< std::string > &expression_parameters={})
Set the filter expression and expression parameters for the subscription.
RCLCPP_PUBLIC bool is_cft_enabled() const
Check if content filtered topic feature of the subscription instance is enabled.
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.