From eb571a5a2768f334901d2c115f370896caeb4c0a Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Wed, 14 Jan 2026 20:25:40 +0000 Subject: [PATCH 1/7] updating ROS2 publisher sink to accept NodeInterfaces Template as opposed to splitting constructors to accept lifecycle vs node --- .../data_tamer/sinks/ros2_publisher_sink.hpp | 29 ++++++++++++------- .../src/sinks/ros2_publisher_sink.cpp | 12 -------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index c1c4138..3350842 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -6,21 +6,25 @@ #include "data_tamer_msgs/msg/snapshot.hpp" #include #include -#include +#include +#include namespace DataTamer { +using PublisherNodeInterfaces = + rclcpp::node_interfaces::NodeInterfaces; + class ROS2PublisherSink : public DataSinkBase { public: - ROS2PublisherSink(std::shared_ptr node, const std::string& topic_prefix); - - ROS2PublisherSink(std::shared_ptr node, - const std::string& topic_prefix); + ROS2PublisherSink(PublisherNodeInterfaces& interfaces, const std::string& topic_prefix) + : interfaces_(interfaces) + { + create_publishers(topic_prefix); + } - template - void create_publishers(NodeT& node, const std::string& topic_prefix) + void create_publishers(const std::string& topic_prefix) { rclcpp::QoS schemas_qos{ rclcpp::KeepAll() }; schemas_qos.reliable(); @@ -28,10 +32,10 @@ class ROS2PublisherSink : public DataSinkBase const rclcpp::QoS data_qos{ rclcpp::KeepAll() }; - schema_publisher_ = node->template create_publisher( - topic_prefix + "/schemas", schemas_qos); - data_publisher_ = node->template create_publisher( - topic_prefix + "/data", data_qos); + schema_publisher_ = rclcpp::create_publisher( + interfaces_, topic_prefix + "/schemas", schemas_qos); + data_publisher_ = rclcpp::create_publisher( + interfaces_, topic_prefix + "/data", data_qos); } void addChannel(const std::string& name, const Schema& schema) override; @@ -47,6 +51,9 @@ class ROS2PublisherSink : public DataSinkBase bool schema_changed_ = true; data_tamer_msgs::msg::Snapshot data_msg_; + + // ---- Stored node façade ---- + PublisherNodeInterfaces& interfaces_; }; } // namespace DataTamer diff --git a/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp b/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp index b49c70b..92eaa32 100644 --- a/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp +++ b/data_tamer_cpp/src/sinks/ros2_publisher_sink.cpp @@ -2,18 +2,6 @@ namespace DataTamer { -ROS2PublisherSink::ROS2PublisherSink(std::shared_ptr node, - const std::string& topic_prefix) -{ - create_publishers(node, topic_prefix); -} - -ROS2PublisherSink::ROS2PublisherSink( - std::shared_ptr node, - const std::string& topic_prefix) -{ - create_publishers(node, topic_prefix); -} void ROS2PublisherSink::addChannel(const std::string& channel_name, const Schema& schema) { From c6c4c83760a934394e74fdd41d66ca9fca47b197 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Thu, 15 Jan 2026 14:42:18 +0000 Subject: [PATCH 2/7] pass by value and not reference for node interface --- .../include/data_tamer/sinks/ros2_publisher_sink.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index 3350842..6b5c22f 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -18,7 +18,7 @@ using PublisherNodeInterfaces = class ROS2PublisherSink : public DataSinkBase { public: - ROS2PublisherSink(PublisherNodeInterfaces& interfaces, const std::string& topic_prefix) + ROS2PublisherSink(PublisherNodeInterfaces interfaces, const std::string& topic_prefix) : interfaces_(interfaces) { create_publishers(topic_prefix); @@ -53,7 +53,7 @@ class ROS2PublisherSink : public DataSinkBase data_tamer_msgs::msg::Snapshot data_msg_; // ---- Stored node façade ---- - PublisherNodeInterfaces& interfaces_; + PublisherNodeInterfaces interfaces_; }; } // namespace DataTamer From 85dcf9b3a99bf95cf32eba62a1f6d4df195fb507 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Thu, 15 Jan 2026 15:46:17 +0000 Subject: [PATCH 3/7] dereferencing node in publisher example, and adding move in constructor to transfer Interface handles to Sink --- data_tamer_cpp/examples/ros2_publisher.cpp | 2 +- data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data_tamer_cpp/examples/ros2_publisher.cpp b/data_tamer_cpp/examples/ros2_publisher.cpp index 5e88a59..cba36cb 100644 --- a/data_tamer_cpp/examples/ros2_publisher.cpp +++ b/data_tamer_cpp/examples/ros2_publisher.cpp @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) rclcpp::init(argc, argv); auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(node, "test"); + auto ros2_sink = std::make_shared(*node, "test"); ChannelsRegistry::Global().addDefaultSink(ros2_sink); // Create (or get) a channel using the global registry (singleton) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index 6b5c22f..b31d992 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -19,7 +19,7 @@ class ROS2PublisherSink : public DataSinkBase { public: ROS2PublisherSink(PublisherNodeInterfaces interfaces, const std::string& topic_prefix) - : interfaces_(interfaces) + : interfaces_(std::move(interfaces)) { create_publishers(topic_prefix); } From 8763fd9cd2356be095e59e19bb3490f09be800e2 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Sat, 24 Jan 2026 23:29:25 +0000 Subject: [PATCH 4/7] saving backwards compatibility progress --- .../include/data_tamer/sinks/ros2_publisher_sink.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index b31d992..a33b7ca 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -15,11 +15,18 @@ namespace DataTamer using PublisherNodeInterfaces = rclcpp::node_interfaces::NodeInterfaces; +// Concept: allow Node, LifecycleNode, or NodeInterface +template +concept NodeInterfaceType = std::same_as || + std::same_as || + std::same_as; + class ROS2PublisherSink : public DataSinkBase { public: - ROS2PublisherSink(PublisherNodeInterfaces interfaces, const std::string& topic_prefix) - : interfaces_(std::move(interfaces)) + template + ROS2PublisherSink(NodeType interfaces, const std::string& topic_prefix) + : interfaces_(interfaces) { create_publishers(topic_prefix); } From 57df20f59c5e07dfcf1e4371b159202e8356a05d Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Sun, 25 Jan 2026 21:50:58 +0000 Subject: [PATCH 5/7] adding compatability for all nodelike types, and updating constructor to allow for backwards compatability with previous datatamer versions --- .../data_tamer/sinks/ros2_publisher_sink.hpp | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index a33b7ca..8e71503 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -5,7 +5,9 @@ #include "data_tamer_msgs/msg/schemas.hpp" #include "data_tamer_msgs/msg/snapshot.hpp" #include +#include #include +#include "rclcpp_lifecycle/lifecycle_node.hpp" #include #include @@ -15,18 +17,12 @@ namespace DataTamer using PublisherNodeInterfaces = rclcpp::node_interfaces::NodeInterfaces; -// Concept: allow Node, LifecycleNode, or NodeInterface -template -concept NodeInterfaceType = std::same_as || - std::same_as || - std::same_as; - class ROS2PublisherSink : public DataSinkBase { public: - template - ROS2PublisherSink(NodeType interfaces, const std::string& topic_prefix) - : interfaces_(interfaces) + template + ROS2PublisherSink(NodeT&& nodelike, const std::string& topic_prefix) + : node_interface_(normalize_node(nodelike)) { create_publishers(topic_prefix); } @@ -40,9 +36,9 @@ class ROS2PublisherSink : public DataSinkBase const rclcpp::QoS data_qos{ rclcpp::KeepAll() }; schema_publisher_ = rclcpp::create_publisher( - interfaces_, topic_prefix + "/schemas", schemas_qos); + node_interface_, topic_prefix + "/schemas", schemas_qos); data_publisher_ = rclcpp::create_publisher( - interfaces_, topic_prefix + "/data", data_qos); + node_interface_, topic_prefix + "/data", data_qos); } void addChannel(const std::string& name, const Schema& schema) override; @@ -50,6 +46,20 @@ class ROS2PublisherSink : public DataSinkBase bool storeSnapshot(const Snapshot& snapshot) override; private: + template + static PublisherNodeInterfaces normalize_node(NodeT&& nodelike) + { + using D = std::decay_t; + + if constexpr(std::is_same_v) + return nodelike; + else if constexpr(std::is_same_v> || + std::is_same_v>) + return PublisherNodeInterfaces(*nodelike); + else + return PublisherNodeInterfaces(nodelike); + } + std::unordered_map schemas_; Mutex schema_mutex_; @@ -60,7 +70,7 @@ class ROS2PublisherSink : public DataSinkBase data_tamer_msgs::msg::Snapshot data_msg_; // ---- Stored node façade ---- - PublisherNodeInterfaces interfaces_; + PublisherNodeInterfaces node_interface_; }; } // namespace DataTamer From b2a5d8a010ad1f8079e1b9db7c8b6a23ded37299 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Mon, 26 Jan 2026 23:02:30 +0000 Subject: [PATCH 6/7] removing dereference from ros2 pub example, cleaning up ros2 pub header, adding ros2 pub sink tests --- data_tamer_cpp/examples/ros2_publisher.cpp | 2 +- .../data_tamer/sinks/ros2_publisher_sink.hpp | 2 +- data_tamer_cpp/tests/CMakeLists.txt | 2 + data_tamer_cpp/tests/ros2_publisher_tests.cpp | 83 +++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 data_tamer_cpp/tests/ros2_publisher_tests.cpp diff --git a/data_tamer_cpp/examples/ros2_publisher.cpp b/data_tamer_cpp/examples/ros2_publisher.cpp index cba36cb..5e88a59 100644 --- a/data_tamer_cpp/examples/ros2_publisher.cpp +++ b/data_tamer_cpp/examples/ros2_publisher.cpp @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) rclcpp::init(argc, argv); auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(*node, "test"); + auto ros2_sink = std::make_shared(node, "test"); ChannelsRegistry::Global().addDefaultSink(ros2_sink); // Create (or get) a channel using the global registry (singleton) diff --git a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp index 8e71503..f6b5536 100644 --- a/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp +++ b/data_tamer_cpp/include/data_tamer/sinks/ros2_publisher_sink.hpp @@ -7,7 +7,7 @@ #include #include #include -#include "rclcpp_lifecycle/lifecycle_node.hpp" +#include #include #include diff --git a/data_tamer_cpp/tests/CMakeLists.txt b/data_tamer_cpp/tests/CMakeLists.txt index 6429aa6..80100ea 100644 --- a/data_tamer_cpp/tests/CMakeLists.txt +++ b/data_tamer_cpp/tests/CMakeLists.txt @@ -7,6 +7,7 @@ if(gtest_vendor_FOUND AND ament_cmake_gtest_FOUND) dt_tests.cpp custom_types_tests.cpp parser_tests.cpp + ros2_publisher_tests.cpp trait_tests.cpp) target_include_directories(datatamer_test @@ -28,6 +29,7 @@ else() add_executable(datatamer_test dt_tests.cpp custom_types_tests.cpp + ros2_publisher_tests.cpp parser_tests.cpp) gtest_discover_tests(datatamer_test DISCOVERY_MODE PRE_TEST) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp new file mode 100644 index 0000000..564e54f --- /dev/null +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -0,0 +1,83 @@ +#include "data_tamer/data_tamer.hpp" +#include "data_tamer/sinks/dummy_sink.hpp" +#include "data_tamer/sinks/ros2_publisher_sink.hpp" + +#include + +#include +#include +#include +#include + +using namespace DataTamer; + +TEST(DataTamerROS2Publisher, SharedPointer) +{ + auto node = std::make_shared("test_datatamer"); + auto ros2_sink = std::make_shared(node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) +{ + auto lifecycle_node = std::make_shared("test_" + "datatamer"); + auto ros2_sink = std::make_shared(lifecycle_node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +TEST(DataTamerROS2Publisher, Dereference) +{ + auto node = std::make_shared("test_datatamer"); + auto ros2_sink = std::make_shared(*node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +TEST(DataTamerROS2Publisher, DereferenceLifeCycle) +{ + auto lifecycle_node = std::make_shared("test_" + "datatamer"); + auto ros2_sink = std::make_shared(*lifecycle_node, "test"); + + auto channel = ChannelsRegistry::Global().getChannel("channel"); + + channel->addDataSink(ros2_sink); + + double const value = 1.; + auto id_value = channel->registerValue("value", &value); + + EXPECT_TRUE(channel->takeSnapshot()); +} + +int main(int argc, char** argv) +{ + rclcpp::init(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + rclcpp::shutdown(); + return ret; +} From 8ae1329cb4837fa2f61837d861b47b092921e282 Mon Sep 17 00:00:00 2001 From: Jacob Frazer Date: Tue, 27 Jan 2026 16:40:30 +0000 Subject: [PATCH 7/7] adding ros2 publisher tests to demonstrate backwards compatibility --- data_tamer_cpp/tests/ros2_publisher_tests.cpp | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/data_tamer_cpp/tests/ros2_publisher_tests.cpp b/data_tamer_cpp/tests/ros2_publisher_tests.cpp index 564e54f..dd60f3c 100644 --- a/data_tamer_cpp/tests/ros2_publisher_tests.cpp +++ b/data_tamer_cpp/tests/ros2_publisher_tests.cpp @@ -13,10 +13,10 @@ using namespace DataTamer; TEST(DataTamerROS2Publisher, SharedPointer) { - auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(node, "test"); + auto node = std::make_shared("test_datatamer_shared_pointer"); + auto ros2_sink = std::make_shared(node, "test_shared_pointer"); - auto channel = ChannelsRegistry::Global().getChannel("channel"); + auto channel = ChannelsRegistry::Global().getChannel("channel_shared_pointer"); channel->addDataSink(ros2_sink); @@ -29,10 +29,16 @@ TEST(DataTamerROS2Publisher, SharedPointer) TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) { auto lifecycle_node = std::make_shared("test_" - "datatamer"); - auto ros2_sink = std::make_shared(lifecycle_node, "test"); + "datatamer_" + "shared_" + "pointer_" + "lifecycle"); + auto ros2_sink = std::make_shared(lifecycle_node, "test_shared_" + "pointer_" + "lifecycle"); - auto channel = ChannelsRegistry::Global().getChannel("channel"); + auto channel = ChannelsRegistry::Global().getChannel("channel_shared_pointer_" + "lifecycle"); channel->addDataSink(ros2_sink); @@ -44,10 +50,10 @@ TEST(DataTamerROS2Publisher, SharedPointerLifeCycle) TEST(DataTamerROS2Publisher, Dereference) { - auto node = std::make_shared("test_datatamer"); - auto ros2_sink = std::make_shared(*node, "test"); + auto node = std::make_shared("test_datatamer_dereference"); + auto ros2_sink = std::make_shared(*node, "test_dereference"); - auto channel = ChannelsRegistry::Global().getChannel("channel"); + auto channel = ChannelsRegistry::Global().getChannel("channel_dereference"); channel->addDataSink(ros2_sink); @@ -60,10 +66,14 @@ TEST(DataTamerROS2Publisher, Dereference) TEST(DataTamerROS2Publisher, DereferenceLifeCycle) { auto lifecycle_node = std::make_shared("test_" - "datatamer"); - auto ros2_sink = std::make_shared(*lifecycle_node, "test"); - - auto channel = ChannelsRegistry::Global().getChannel("channel"); + "datatamer_" + "dereference_" + "lifecycle"); + auto ros2_sink = std::make_shared(*lifecycle_node, "test_" + "dereference_" + "lifecycle"); + + auto channel = ChannelsRegistry::Global().getChannel("channel_dereference_lifecycle"); channel->addDataSink(ros2_sink);