C++ YASMIN (Yet Another State MachINe)
Loading...
Searching...
No Matches
ros_clients_cache.hpp
Go to the documentation of this file.
1// Copyright (C) 2025 Miguel Ángel González Santamarta
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16#ifndef YASMIN_ROS__ROS_CLIENTS_CACHE_HPP_
17#define YASMIN_ROS__ROS_CLIENTS_CACHE_HPP_
18
19#include <functional>
20#include <map>
21#include <memory>
22#include <mutex>
23#include <sstream>
24#include <string>
25#include <tuple>
26#include <typeindex>
27#include <typeinfo>
28
29#include <rclcpp/rclcpp.hpp>
30#include <rclcpp_action/rclcpp_action.hpp>
31
32#include "yasmin/logs.hpp"
33
34namespace yasmin_ros {
35
54public:
64 template <typename ActionT>
65 static typename rclcpp_action::Client<ActionT>::SharedPtr
67 rclcpp::Node::SharedPtr node, const std::string &action_name,
68 rclcpp::CallbackGroup::SharedPtr callback_group = nullptr) {
69
70 // Create a unique key
71 std::string node_name = node->get_name();
72 std::string action_type_name = get_type_name<ActionT>();
73 std::string callback_group_name = get_callback_group_name(callback_group);
74 auto cache_key =
75 std::make_tuple(node_name, action_type_name, action_name,
76 callback_group_name, std::type_index(typeid(ActionT)));
77
78 std::lock_guard<std::recursive_mutex> lock(get_lock());
79
80 // Check if action client already exists in cache
81 auto &action_clients = get_action_clients();
82 auto it = action_clients.find(cache_key);
83
84 if (it != action_clients.end()) {
85 YASMIN_LOG_INFO("Reusing existing action client for '%s' of type '%s'",
86 action_name.c_str(), action_type_name.c_str());
87 return std::static_pointer_cast<rclcpp_action::Client<ActionT>>(
88 it->second);
89 }
90
91 // Create new action client if not in cache
92 YASMIN_LOG_INFO("Creating new action client for '%s' of type '%s'",
93 action_name.c_str(), action_type_name.c_str());
94
95 auto action_client = rclcpp_action::create_client<ActionT>(
96 node, action_name, callback_group);
97
98 // Store in cache (as void pointer for type erasure)
99 action_clients[cache_key] = std::static_pointer_cast<void>(action_client);
100
101 return action_client;
102 }
103
113 template <typename ServiceT>
114 static typename rclcpp::Client<ServiceT>::SharedPtr
116 rclcpp::Node::SharedPtr node, const std::string &service_name,
117 rclcpp::CallbackGroup::SharedPtr callback_group = nullptr) {
118
119 // Create a unique key
120 std::string node_name = node->get_name();
121 std::string service_type_name = get_type_name<ServiceT>();
122 std::string callback_group_name = get_callback_group_name(callback_group);
123 auto cache_key =
124 std::make_tuple(node_name, service_type_name, service_name,
125 callback_group_name, std::type_index(typeid(ServiceT)));
126
127 std::lock_guard<std::recursive_mutex> lock(get_lock());
128
129 // Check if service client already exists in cache
130 auto &service_clients = get_service_clients();
131 auto it = service_clients.find(cache_key);
132
133 if (it != service_clients.end()) {
134 YASMIN_LOG_INFO("Reusing existing service client for '%s' of type '%s'",
135 service_name.c_str(), service_type_name.c_str());
136 return std::static_pointer_cast<rclcpp::Client<ServiceT>>(it->second);
137 }
138
139 // Create new service client if not in cache
140 YASMIN_LOG_INFO("Creating new service client for '%s' of type '%s'",
141 service_name.c_str(), service_type_name.c_str());
142
143#if __has_include("rclcpp/version.h")
144#include "rclcpp/version.h"
145#if RCLCPP_VERSION_GTE(28, 1, 1) // Jazzy, Kilted and Rolling
146 auto qos = rclcpp::QoS(
147 rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_services_default));
148#else // Humble and Iron
149 auto qos = rmw_qos_profile_services_default;
150#endif
151#else // Foxy and Galactic
152 auto qos = rmw_qos_profile_services_default;
153#endif
154
155 auto service_client =
156 node->create_client<ServiceT>(service_name, qos, callback_group);
157
158 // Store in cache (as void pointer for type erasure)
159 service_clients[cache_key] = std::static_pointer_cast<void>(service_client);
160
161 return service_client;
162 }
163
174 template <typename MsgT>
175 static typename rclcpp::Publisher<MsgT>::SharedPtr get_or_create_publisher(
176 rclcpp::Node::SharedPtr node, const std::string &topic_name,
177 const rclcpp::QoS &qos_profile = rclcpp::QoS(10),
178 rclcpp::CallbackGroup::SharedPtr callback_group = nullptr) {
179
180 // Create a unique key
181 std::string node_name = node->get_name();
182 std::string msg_type_name = get_type_name<MsgT>();
183 std::string qos_hash = hash_qos_profile(qos_profile);
184 auto cache_key = std::make_tuple(node_name, msg_type_name, topic_name,
185 qos_hash, std::type_index(typeid(MsgT)));
186
187 std::lock_guard<std::recursive_mutex> lock(get_lock());
188
189 // Check if publisher already exists in cache
190 auto &publishers = get_publishers();
191 auto it = publishers.find(cache_key);
192
193 if (it != publishers.end()) {
194 YASMIN_LOG_INFO("Reusing existing publisher for topic '%s' of type '%s'",
195 topic_name.c_str(), msg_type_name.c_str());
196 return std::static_pointer_cast<rclcpp::Publisher<MsgT>>(it->second);
197 }
198
199 // Create new publisher if not in cache
200 YASMIN_LOG_INFO("Creating new publisher for topic '%s' of type '%s'",
201 topic_name.c_str(), msg_type_name.c_str());
202
203 rclcpp::PublisherOptions options;
204 options.callback_group = callback_group;
205
206 auto publisher =
207 node->create_publisher<MsgT>(topic_name, qos_profile, options);
208
209 // Store in cache (as void pointer for type erasure)
210 publishers[cache_key] = std::static_pointer_cast<void>(publisher);
211
212 return publisher;
213 }
214
218 static void clear_action_clients();
219
223 static void clear_service_clients();
224
228 static void clear_publishers();
229
233 static void clear_all();
234
240 static size_t get_action_clients_count();
241
247 static size_t get_service_clients_count();
248
254 static size_t get_publishers_count();
255
261 static std::map<std::string, size_t> get_cache_stats();
262
263private:
264 // Type alias for cache keys
265 using ActionClientKey = std::tuple<std::string, std::string, std::string,
266 std::string, std::type_index>;
267 using ServiceClientKey = std::tuple<std::string, std::string, std::string,
268 std::string, std::type_index>;
269 using PublisherKey = std::tuple<std::string, std::string, std::string,
270 std::string, std::type_index>;
271
272 // Static cache maps
273 static std::map<ActionClientKey, std::shared_ptr<void>> &get_action_clients();
274 static std::map<ServiceClientKey, std::shared_ptr<void>> &
276 static std::map<PublisherKey, std::shared_ptr<void>> &get_publishers();
277
278 // Static lock for thread-safe access
279 static std::recursive_mutex &get_lock();
280
287 template <typename T> static std::string get_type_name() {
288 return typeid(T).name();
289 }
290
297 static std::string
298 get_callback_group_name(rclcpp::CallbackGroup::SharedPtr callback_group);
299
307 template <typename CallbackT>
308 static std::string get_callback_name(const CallbackT &callback) {
309 // Use the address of the callback for uniqueness
310 std::ostringstream oss;
311 oss << typeid(CallbackT).name() << "_"
312 << reinterpret_cast<const void *>(&callback);
313 return oss.str();
314 }
315
322 static std::string hash_qos_profile(const rclcpp::QoS &qos_profile);
323};
324
325} // namespace yasmin_ros
326
327#endif // YASMIN_ROS__ROS_CLIENTS_CACHE_HPP_
Centralized cache for managing ROS 2 client objects.
Definition ros_clients_cache.hpp:53
static size_t get_service_clients_count()
Get the number of cached service clients.
Definition ros_clients_cache.cpp:77
static std::string get_callback_name(const CallbackT &callback)
Get a string representation of a callback function.
Definition ros_clients_cache.hpp:308
static std::map< std::string, size_t > get_cache_stats()
Get statistics about all caches.
Definition ros_clients_cache.cpp:87
static size_t get_publishers_count()
Get the number of cached publishers.
Definition ros_clients_cache.cpp:82
static void clear_all()
Clear all clients caches.
Definition ros_clients_cache.cpp:64
static std::recursive_mutex & get_lock()
Definition ros_clients_cache.cpp:41
std::tuple< std::string, std::string, std::string, std::string, std::type_index > ServiceClientKey
Definition ros_clients_cache.hpp:267
static rclcpp_action::Client< ActionT >::SharedPtr get_or_create_action_client(rclcpp::Node::SharedPtr node, const std::string &action_name, rclcpp::CallbackGroup::SharedPtr callback_group=nullptr)
Get an existing action client from the cache or create a new one.
Definition ros_clients_cache.hpp:66
static size_t get_action_clients_count()
Get the number of cached action clients.
Definition ros_clients_cache.cpp:72
static std::string get_callback_group_name(rclcpp::CallbackGroup::SharedPtr callback_group)
Get a string representation of a callback group.
Definition ros_clients_cache.cpp:100
static std::map< ActionClientKey, std::shared_ptr< void > > & get_action_clients()
Definition ros_clients_cache.cpp:24
static std::string get_type_name()
Get a string representation of a type.
Definition ros_clients_cache.hpp:287
static void clear_action_clients()
Clear the action clients cache.
Definition ros_clients_cache.cpp:46
static rclcpp::Client< ServiceT >::SharedPtr get_or_create_service_client(rclcpp::Node::SharedPtr node, const std::string &service_name, rclcpp::CallbackGroup::SharedPtr callback_group=nullptr)
Get an existing service client from the cache or create a new one.
Definition ros_clients_cache.hpp:115
std::tuple< std::string, std::string, std::string, std::string, std::type_index > PublisherKey
Definition ros_clients_cache.hpp:269
static std::string hash_qos_profile(const rclcpp::QoS &qos_profile)
Create a hash for a QoS profile.
Definition ros_clients_cache.cpp:112
static void clear_publishers()
Clear the publishers cache.
Definition ros_clients_cache.cpp:58
std::tuple< std::string, std::string, std::string, std::string, std::type_index > ActionClientKey
Definition ros_clients_cache.hpp:265
static rclcpp::Publisher< MsgT >::SharedPtr get_or_create_publisher(rclcpp::Node::SharedPtr node, const std::string &topic_name, const rclcpp::QoS &qos_profile=rclcpp::QoS(10), rclcpp::CallbackGroup::SharedPtr callback_group=nullptr)
Get an existing publisher from the cache or create a new one.
Definition ros_clients_cache.hpp:175
static std::map< ServiceClientKey, std::shared_ptr< void > > & get_service_clients()
Definition ros_clients_cache.cpp:30
static std::map< PublisherKey, std::shared_ptr< void > > & get_publishers()
Definition ros_clients_cache.cpp:36
static void clear_service_clients()
Clear the service clients cache.
Definition ros_clients_cache.cpp:52
#define YASMIN_LOG_INFO(text,...)
Definition logs.hpp:169
Definition action_state.hpp:37