ROS 2 rclcpp + rcl - lyrical  lyrical
ROS 2 C++ Client Library with ROS Client Library
registered_entity_cache.hpp
1 // Copyright 2024 Cellumation GmbH.
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 #pragma once
16 
17 #include <unordered_map>
18 #include <utility>
19 #include <memory>
20 #include <vector>
21 
22 #include "scheduler.hpp"
23 #include "timer_manager.hpp"
24 #include <rclcpp/executors/executor_entities_collection.hpp>
25 
26 namespace rclcpp
27 {
28 namespace executors
29 {
30 namespace cbg_executor
31 {
32 template<class EntityType_T>
34 {
36  const std::shared_ptr<EntityType_T> & shr_ptr,
37  const std::function<void(const std::shared_ptr<EntityType_T> & ptr)> & destruction_callback)
38  :executable(shr_ptr),
39  ptr(shr_ptr.get()),
40  destruction_callback(destruction_callback)
41  {
42  }
43 
45  {
46  std::shared_ptr<EntityType_T> shr_ptr = executable.lock();
47  if(shr_ptr) {
48  destruction_callback(shr_ptr);
49  }
50  }
51 
52  std::weak_ptr<EntityType_T> executable;
53 
54  // May be used as key, to identify the removed element
55  // after the weak pointer went out of scope
56  EntityType_T *ptr;
57 
58  std::function<void(const std::shared_ptr<EntityType_T> & ptr)> destruction_callback;
59 };
60 
62 {
63  GuardConditionWithFunction(rclcpp::GuardCondition::SharedPtr gc, std::function<void(void)> fun)
64  : guard_condition(std::move(gc)), handle_guard_condition_fun(std::move(fun))
65  {
66  }
67 
68  rclcpp::GuardCondition::SharedPtr guard_condition;
69 
70  // A function that should be executed if the guard_condition is ready
71  std::function<void(void)> handle_guard_condition_fun;
72 };
73 
74 template<class EntityType_T>
76 {
78 
79  std::unordered_map<const EntityType_T *, std::unique_ptr<CacheType>> entities;
80 
81 public:
82  void update(
83  const std::vector<std::shared_ptr<EntityType_T>> & entityList,
84  const std::function<void(const std::shared_ptr<EntityType_T> &)> & on_add,
85  const std::function<void(const std::shared_ptr<EntityType_T> &)> & on_remove)
86  {
87  std::unordered_map<const EntityType_T *, std::unique_ptr<CacheType>> nextEntities;
88  for(const std::shared_ptr<EntityType_T> & shr_ptr : entityList) {
89  auto it = entities.find(shr_ptr.get());
90  if(it != entities.end()) {
91  nextEntities.insert(std::move(entities.extract(it)));
92  } else {
93  // new entry
94  nextEntities.emplace(std::make_pair(shr_ptr.get(),
95  std::make_unique<CacheType>(shr_ptr, on_remove)));
96 
97  on_add(shr_ptr);
98  }
99  }
100 
101  entities.swap(nextEntities);
102 
103  // NOTE, at this point the non moved unique_ptrs get destructed, and the
104  // remove callbacks are called
105  }
106 
107  void clear()
108  {
109  entities.clear();
110  }
111 };
112 
114 {
116  CBGScheduler & scheduler, TimerManager & timer_manager,
117  const rclcpp::CallbackGroup::SharedPtr & callback_group)
118  : callback_group_weak_ptr(callback_group),
119  scheduler_cbg_handle(*scheduler.add_callback_group(callback_group)),
120  timer_manager(timer_manager)
121  {
122  auto cbg_gc = callback_group->get_notify_guard_condition();
123 
124  if(cbg_gc) {
125  // register guard condition for the callback group with the handler
126  // this makes sure, that we pick up new entities in case the guard
127  // condition is triggered
129  cbg_gc, [&scheduler]() {
130  scheduler.trigger_sync();
131  }
132  );
133  }
134  }
135 
136  EntityCache<rclcpp::TimerBase> timers_cache;
137  EntityCache<rclcpp::SubscriptionBase> subscribers_cache;
138  EntityCache<rclcpp::ClientBase> clients_cache;
139  EntityCache<rclcpp::ServiceBase> services_cache;
140  EntityCache<rclcpp::Waitable> waitables_cache;
141 
142  std::vector<GuardConditionWithFunction> guard_conditions;
143 
144  rclcpp::CallbackGroup::WeakPtr callback_group_weak_ptr;
145  CBGScheduler::CallbackGroupHandle & scheduler_cbg_handle;
146 
147  TimerManager & timer_manager;
148 
150  {
151  for (const auto & gc_ref : guard_conditions) {
152  gc_ref.guard_condition->set_on_trigger_callback(nullptr);
153  }
154 
155  auto cbg_shr_ptr = callback_group_weak_ptr.lock();
156  if(!cbg_shr_ptr) {
157  return;
158  }
159 
160  const auto clear_sub_cb = [](const rclcpp::SubscriptionBase::SharedPtr & s) {
161  s->clear_on_new_message_callback();
162  };
163  const auto clear_timer_cb = [this](const rclcpp::TimerBase::SharedPtr & s) {
164  timer_manager.remove_timer(s);
165  };
166  const auto clear_client_cb = [](const rclcpp::ClientBase::SharedPtr & s) {
167  s->clear_on_new_response_callback();
168  };
169  const auto clear_service_cb = [](const rclcpp::ServiceBase::SharedPtr & s) {
170  s->clear_on_new_request_callback();
171  };
172  const auto clear_waitable_cb = [](const rclcpp::Waitable::SharedPtr & s) {
173  s->clear_on_ready_callback();
174  };
175 
176  // populate all vectors
177  cbg_shr_ptr->collect_all_ptrs(clear_sub_cb, clear_service_cb, clear_client_cb, clear_timer_cb,
178  clear_waitable_cb);
179 
180  if(cbg_shr_ptr->get_notify_guard_condition()) {
181  cbg_shr_ptr->get_notify_guard_condition()->set_on_trigger_callback(nullptr);
182  }
183  }
184 
185  void clear_caches()
186  {
187  timers_cache.clear();
188  subscribers_cache.clear();
189  clients_cache.clear();
190  services_cache.clear();
191  waitables_cache.clear();
192  }
193 
194 
195  bool regenerate_events()
196  {
197  rclcpp::CallbackGroup::SharedPtr callback_group = callback_group_weak_ptr.lock();
198 
199  if(!callback_group) {
200  clear_caches();
201  return false;
202  }
203 
204  std::vector<rclcpp::TimerBase::SharedPtr> timers;
205  std::vector<rclcpp::SubscriptionBase::SharedPtr> subscribers;
206  std::vector<rclcpp::ClientBase::SharedPtr> clients;
207  std::vector<rclcpp::ServiceBase::SharedPtr> services;
208  std::vector<rclcpp::Waitable::SharedPtr> waitables;
209 
210  // we reserve to much memory here, but this should be fine
211  const size_t max_size = callback_group->size();
212  timers.reserve(max_size);
213  subscribers.reserve(max_size);
214  clients.reserve(max_size);
215  services.reserve(max_size);
216  waitables.reserve(max_size);
217 
218  const auto add_sub = [&subscribers](const rclcpp::SubscriptionBase::SharedPtr & s) {
219  subscribers.push_back(s);
220  };
221  const auto add_timer = [&timers](const rclcpp::TimerBase::SharedPtr & s) {
222  timers.push_back(s);
223  };
224  const auto add_client = [&clients](const rclcpp::ClientBase::SharedPtr & s) {
225  clients.push_back(s);
226  };
227  const auto add_service = [&services](const rclcpp::ServiceBase::SharedPtr & s) {
228  services.push_back(s);
229  };
230  const auto add_waitable = [&waitables](const rclcpp::Waitable::SharedPtr & s) {
231  waitables.push_back(s);
232  };
233 
234  // populate all vectors
235  callback_group->collect_all_ptrs(add_sub, add_service, add_client, add_timer, add_waitable);
236 
237  timers_cache.update(timers,
238  [this](const rclcpp::TimerBase::SharedPtr & s) {
239  timer_manager.add_timer(s, scheduler_cbg_handle.get_ready_callback_for_entity(s));
240  },
241  [this](const rclcpp::TimerBase::SharedPtr & s) {
242  timer_manager.remove_timer(s);
243  });
244 
245  subscribers_cache.update(subscribers,
246  [this](const rclcpp::SubscriptionBase::SharedPtr & s) {
247  s->set_on_new_message_callback(
248  scheduler_cbg_handle.get_ready_callback_for_entity(s));
249  },
250  [] (const rclcpp::SubscriptionBase::SharedPtr & shr_ptr) {
251  shr_ptr->clear_on_new_message_callback();
252  });
253 
254  clients_cache.update(clients,
255  [this](const rclcpp::ClientBase::SharedPtr & s) {
256  s->set_on_new_response_callback(
257  scheduler_cbg_handle.get_ready_callback_for_entity(s));
258  },
259  [] (const rclcpp::ClientBase::SharedPtr & shr_ptr) {
260  shr_ptr->clear_on_new_response_callback();
261  });
262  services_cache.update(services,
263  [this](const rclcpp::ServiceBase::SharedPtr & s) {
264  s->set_on_new_request_callback(
265  scheduler_cbg_handle.get_ready_callback_for_entity(s));
266  },
267  [] (const rclcpp::ServiceBase::SharedPtr & shr_ptr) {
268  shr_ptr->clear_on_new_request_callback();
269  });
270  waitables_cache.update(waitables,
271  [this](const rclcpp::Waitable::SharedPtr & s) {
272  s->set_on_ready_callback(
273  scheduler_cbg_handle.get_ready_callback_for_entity(s));
274  for (const auto & t : s->get_timers()) {
275  timer_manager.add_timer(t, scheduler_cbg_handle.get_ready_callback_for_entity(t));
276  }
277  },
278  [this] (const rclcpp::Waitable::SharedPtr & s) {
279  s->clear_on_ready_callback();
280  for (const auto & t : s->get_timers()) {
281  timer_manager.remove_timer(t);
282  }
283  });
284 
285  return true;
286  }
287 
296  rclcpp::GuardCondition::SharedPtr ptr,
297  std::function<void(void)> fun)
298  {
299  auto & new_entry = guard_conditions.emplace_back(std::move(ptr), std::move(fun));
300 
301  if (new_entry.handle_guard_condition_fun) {
302  new_entry.guard_condition->set_on_trigger_callback(
303  scheduler_cbg_handle.get_ready_callback_for_entity(CBGScheduler::CallbackEventType(
304  new_entry.handle_guard_condition_fun)));
305  }
306  }
307 };
308 } // namespace cbg_executor
309 } // namespace executors
310 } // namespace rclcpp
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
void add_guard_condition_event(rclcpp::GuardCondition::SharedPtr ptr, std::function< void(void)> fun)