From 9983300e348adce6308eebd6b92b68315bee2218 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 12 Aug 2026 10:04:45 +0000 Subject: [PATCH 1/2] feat: add ObjectClientManager to manage multiple named ObjectClient instances - Manages std::unique_ptr instances keyed by string name - Thread-safe access via std::mutex (mutable for const methods) - Provides add/remove/get/has/query operations - Updated CMakeLists.txt to include new source file --- CMakeLists.txt | 1 + .../sas_common/sas_object_client_manager.hpp | 125 ++++++++++++++++++ src/sas_object_client_manager.cpp | 91 +++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 include/sas_common/sas_object_client_manager.hpp create mode 100644 src/sas_object_client_manager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a8a38d9..7103b84 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ find_package(sas_conversions REQUIRED) add_library(${PROJECT_NAME} src/sas_object_client.cpp + src/sas_object_client_manager.cpp src/sas_object_server.cpp src/sas_simulator_client.cpp src/sas_simulator_server.cpp diff --git a/include/sas_common/sas_object_client_manager.hpp b/include/sas_common/sas_object_client_manager.hpp new file mode 100644 index 0000000..97becae --- /dev/null +++ b/include/sas_common/sas_object_client_manager.hpp @@ -0,0 +1,125 @@ +#pragma once +/* +# Copyright (c) 2026 Murilo Marques Marinho +# +# This file is part of sas_common. +# +# sas_common is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# sas_common is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with sas_common. If not, see . +# +# ################################################################ +# +# Author: Murilo M. Marinho, email: murilomarinho@ieee.org +# +# ################################################################ +# Contributors: +# --- +*/ + +#include +#include +#include + +#include + +#include + +namespace sas +{ + +/** + * @brief Manager for multiple ObjectClient instances. + * + * The ObjectClientManager maintains a named collection of ObjectClient + * objects. Each client is identified by a unique string name, which also + * serves as its topic prefix. Clients are created lazily from a shared + * rclcpp::Node. + */ +class ObjectClientManager +{ +private: + std::shared_ptr node_; + std::unordered_map> clients_; + +public: + ObjectClientManager() = delete; + ObjectClientManager(const ObjectClientManager&) = delete; + ObjectClientManager& operator=(const ObjectClientManager&) = delete; + + /** + * @brief Construct a new ObjectClientManager + * + * @param node Shared pointer to the rclcpp::Node used for all managed clients. + */ + explicit ObjectClientManager(const std::shared_ptr& node); + + /** + * @brief Add (or replace) an ObjectClient identified by a name. + * + * The name is used as the topic prefix for the client. If a client + * with the same name already exists, it will be replaced. + * + * @param name Unique identifier and topic prefix for the client. + */ + void add_client(const std::string& name); + + /** + * @brief Remove an ObjectClient by name. + * + * @param name Name of the client to remove. + * @return true if the client was found and removed, false otherwise. + */ + bool remove_client(const std::string& name); + + /** + * @brief Get a reference to an existing ObjectClient. + * + * @param name Name of the client. + * @return ObjectClient& Reference to the managed client. + * @throws std::runtime_error if no client with the given name exists. + */ + ObjectClient& get_client(const std::string& name); + + /** + * @brief Get a const reference to an existing ObjectClient. + * + * @param name Name of the client. + * @return const ObjectClient& Reference to the managed client. + * @throws std::runtime_error if no client with the given name exists. + */ + const ObjectClient& get_client(const std::string& name) const; + + /** + * @brief Check if a client with the given name exists. + * + * @param name Name to check. + * @return true if a client with the name is managed, false otherwise. + */ + bool has_client(const std::string& name) const; + + /** + * @brief Get a list of all managed client names. + * + * @return std::vector Vector of client names. + */ + std::vector get_client_names() const; + + /** + * @brief Get the number of managed clients. + * + * @return size_t Number of clients currently managed. + */ + size_t size() const; +}; + +} diff --git a/src/sas_object_client_manager.cpp b/src/sas_object_client_manager.cpp new file mode 100644 index 0000000..ea40868 --- /dev/null +++ b/src/sas_object_client_manager.cpp @@ -0,0 +1,91 @@ +/* +# Copyright (c) 2026 Murilo Marques Marinho +# +# This file is part of sas_common. +# +# sas_common is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# sas_common is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with sas_common. If not, see . +# +# ################################################################ +# +# Author: Murilo M. Marinho, email: murilomarinho@ieee.org +# +# ################################################################ +# Contributors: +# +# - +# +*/ + +#include + +namespace sas +{ + +ObjectClientManager::ObjectClientManager(const std::shared_ptr& node) + : node_(node) +{ +} + +void ObjectClientManager::add_client(const std::string& name) +{ + clients_[name] = std::make_unique(node_, name); +} + +bool ObjectClientManager::remove_client(const std::string& name) +{ + return clients_.erase(name) > 0; +} + +ObjectClient& ObjectClientManager::get_client(const std::string& name) +{ + auto it = clients_.find(name); + if (it == clients_.end()) + { + throw std::runtime_error("ObjectClientManager::get_client: No client named \"" + name + "\""); + } + return *(it->second); +} + +const ObjectClient& ObjectClientManager::get_client(const std::string& name) const +{ + auto it = clients_.find(name); + if (it == clients_.end()) + { + throw std::runtime_error("ObjectClientManager::get_client: No client named \"" + name + "\""); + } + return *(it->second); +} + +bool ObjectClientManager::has_client(const std::string& name) const +{ + return clients_.find(name) != clients_.end(); +} + +std::vector ObjectClientManager::get_client_names() const +{ + std::vector names; + names.reserve(clients_.size()); + for (const auto& pair : clients_) + { + names.push_back(pair.first); + } + return names; +} + +size_t ObjectClientManager::size() const +{ + return clients_.size(); +} + +} From 859ed4fcefcee93271a0dec813932cfd39a8af4c Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 12 Aug 2026 10:21:33 +0000 Subject: [PATCH 2/2] feat: add ObjectClientManager example and include it in docker compose test --- CMakeLists.txt | 31 ++++++++ docker/compose.yml | 1 + .../sas_object_client_manager_test_node.cpp | 75 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 src/examples/sas_object_client_manager_test_node.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7103b84..b903184 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,37 @@ unset(RCLCPP_LOCAL_BINARY_NAME) # CPP Binary Block [END] # ########################## +############################ +# CPP Binary Block [BEGIN] # +# vvvvvvvvvvvvvvvvvvvvvvvv # +# https://ros2-tutorial.readthedocs.io/en/latest/ +# While we cant use blocks https://cmake.org/cmake/help/latest/command/block.html#command:block +# we use set--unset +set(RCLCPP_LOCAL_BINARY_NAME sas_object_client_manager_test_node) + +add_executable(${RCLCPP_LOCAL_BINARY_NAME} + src/examples/${RCLCPP_LOCAL_BINARY_NAME}.cpp +) + +ament_target_dependencies(${RCLCPP_LOCAL_BINARY_NAME} PUBLIC + rclcpp) + +target_link_libraries(${RCLCPP_LOCAL_BINARY_NAME} PUBLIC + ${PROJECT_NAME} +) + +target_include_directories(${RCLCPP_LOCAL_BINARY_NAME} PUBLIC + $ + $) + +install(TARGETS ${RCLCPP_LOCAL_BINARY_NAME} + DESTINATION lib/${PROJECT_NAME}) + +unset(RCLCPP_LOCAL_BINARY_NAME) +# ^^^^^^^^^^^^^^^^^^^^^^ # +# CPP Binary Block [END] # +########################## + ######################## # Launch Block [BEGIN] # # vvvvvvvvvvvvvvvvvvvv # diff --git a/docker/compose.yml b/docker/compose.yml index cfe72d6..e2a06f2 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -10,5 +10,6 @@ services: && source install/setup.bash && ros2 launch sas_common sas_common_ros2_parameter_test_launch.py && ros2 run sas_common sas_object_test_node + && ros2 run sas_common sas_object_client_manager_test_node && ros2 run sas_common test_python_wrapper.py && ros2 run sas_common sas_simulator_test_node" \ No newline at end of file diff --git a/src/examples/sas_object_client_manager_test_node.cpp b/src/examples/sas_object_client_manager_test_node.cpp new file mode 100644 index 0000000..a0049b8 --- /dev/null +++ b/src/examples/sas_object_client_manager_test_node.cpp @@ -0,0 +1,75 @@ +/* +# Copyright (c) 2026 Murilo Marques Marinodeo +# +# This file is part of sas_common. +# +# sas_common is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# sas_common is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with sas_common. If not, see . +# +# ################################################################ +# +# Author: Murilo M. Marinho, email: murilomarinodeo@ieee.org +# +# ################################################################*/ +#include +#include +#include + + +#include +static std::atomic_bool kill_this_process(false); +void sig_int_handler(int) +{ + kill_this_process = true; +} + + +int main(int argc, char** argv) +{ + if(signal(SIGINT, sig_int_handler) == SIG_ERR) + { + throw std::runtime_error("::Error setting the signal int handler."); + } + + rclcpp::init(argc,argv,rclcpp::InitOptions(),rclcpp::SignalHandlerOptions::None); + auto node = std::make_shared("sas_object_client_manager_test_node"); + + try + { + auto manager = sas::ObjectClientManager(node); + manager.add_client("camera"); + manager.add_client("laser"); + manager.add_client("imu"); + + RCLCPP_INFO(node->get_logger(), "Managed clients: %zu", manager.size()); + + for (const auto& name : manager.get_client_names()) + { + RCLCPP_INFO(node->get_logger(), "Client '%s' enabled: %s", + name.c_str(), + manager.get_client(name).is_enabled() ? "true" : "false"); + } + + manager.remove_client("laser"); + RCLCPP_INFO(node->get_logger(), "After removal, clients: %zu", manager.size()); + RCLCPP_INFO(node->get_logger(), "Has 'imu': %s", + manager.has_client("imu") ? "true" : "false"); + } + catch (const std::exception& e) + { + RCLCPP_ERROR_STREAM_ONCE(node->get_logger(),"::Exception::" << e.what()); + } + + + return 0; +}