Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -179,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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

install(TARGETS ${RCLCPP_LOCAL_BINARY_NAME}
DESTINATION lib/${PROJECT_NAME})

unset(RCLCPP_LOCAL_BINARY_NAME)
# ^^^^^^^^^^^^^^^^^^^^^^ #
# CPP Binary Block [END] #
##########################

########################
# Launch Block [BEGIN] #
# vvvvvvvvvvvvvvvvvvvv #
Expand Down
1 change: 1 addition & 0 deletions docker/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
125 changes: 125 additions & 0 deletions include/sas_common/sas_object_client_manager.hpp
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
#
# ################################################################
#
# Author: Murilo M. Marinho, email: murilomarinho@ieee.org
#
# ################################################################
# Contributors:
# ---
*/

#include <string>
#include <unordered_map>
#include <vector>

#include <rclcpp/rclcpp.hpp>

#include <sas_common/sas_object_client.hpp>

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<rclcpp::Node> node_;
std::unordered_map<std::string, std::unique_ptr<ObjectClient>> 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<rclcpp::Node>& 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<std::string> Vector of client names.
*/
std::vector<std::string> get_client_names() const;

/**
* @brief Get the number of managed clients.
*
* @return size_t Number of clients currently managed.
*/
size_t size() const;
};

}
75 changes: 75 additions & 0 deletions src/examples/sas_object_client_manager_test_node.cpp
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
#
# ################################################################
#
# Author: Murilo M. Marinho, email: murilomarinodeo@ieee.org
#
# ################################################################*/
#include <exception>
#include <rclcpp/rclcpp.hpp>
#include <sas_common/sas_object_client_manager.hpp>


#include<signal.h>
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<rclcpp::Node>("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;
}
91 changes: 91 additions & 0 deletions src/sas_object_client_manager.cpp
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
#
# ################################################################
#
# Author: Murilo M. Marinho, email: murilomarinho@ieee.org
#
# ################################################################
# Contributors:
#
# -
#
*/

#include <sas_common/sas_object_client_manager.hpp>

namespace sas
{

ObjectClientManager::ObjectClientManager(const std::shared_ptr<rclcpp::Node>& node)
: node_(node)
{
}

void ObjectClientManager::add_client(const std::string& name)
{
clients_[name] = std::make_unique<ObjectClient>(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<std::string> ObjectClientManager::get_client_names() const
{
std::vector<std::string> 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();
}

}