Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose generic inputs and outputs to components #143

Merged
merged 22 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e01d256
feat: support generic add_output
bpapaspyros Sep 27, 2024
a9328b1
feat: support generic publishers
bpapaspyros Sep 28, 2024
08b295d
fix: relax lifecycle template conditions for future proofing
bpapaspyros Sep 28, 2024
1db6d7e
refactor: laying the ground work for generic inputs
bpapaspyros Sep 29, 2024
c3905e3
feat: support generic subscriptions
bpapaspyros Sep 29, 2024
a4d63eb
fix: refine read_message conditions && perform the actual read
bpapaspyros Sep 29, 2024
cd4dc2d
fix: uncomment additional tests
bpapaspyros Sep 29, 2024
5aaea1e
feat: expose generic outputs to components
bpapaspyros Sep 27, 2024
0122720
refactor: reintroduce newline
bpapaspyros Oct 1, 2024
a738bb8
Merge branch 'feat/expose_generic_outputs' into feat/generic_inputs_cpp
bpapaspyros Oct 2, 2024
5e398e7
docs: updated CHANGELOG.md
bpapaspyros Oct 2, 2024
6657f6d
Merge branch 'feat/generic_pubsub_dev' into feat/expose_generic_outputs
bpapaspyros Oct 2, 2024
5c58535
fix: prevent compilation of non-CustomT SubscriberHandler
bpapaspyros Oct 2, 2024
2d0a06b
refactor: clean up code
bpapaspyros Oct 2, 2024
b757ff2
refactor: remove unecessary include
bpapaspyros Oct 2, 2024
f20f9bf
refactor: more cleanup
bpapaspyros Oct 2, 2024
afb863a
refactor: lifecycle publisher configuration to support generic types
bpapaspyros Oct 2, 2024
4e39b08
fix: add missing changes
bpapaspyros Oct 2, 2024
da71c49
refactor: clean up code
bpapaspyros Oct 2, 2024
8ba02d6
refactor: review comments
bpapaspyros Oct 2, 2024
f3aaae3
feat: add communication tests
domire8 Oct 2, 2024
769c2dc
fix: small map fix and clean up tests
bpapaspyros Oct 2, 2024
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
10 changes: 10 additions & 0 deletions source/modulo_components/include/modulo_components/Component.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <rclcpp/node.hpp>

#include "modulo_components/ComponentInterface.hpp"
#include "modulo_core/concepts.hpp"
bpapaspyros marked this conversation as resolved.
Show resolved Hide resolved

namespace modulo_components {

Expand Down Expand Up @@ -155,6 +156,15 @@ inline void Component::add_output(
->create_publisher_interface(message_pair);
break;
}
case MessageType::CUSTOM_MESSAGE: {
if constexpr (modulo_core::concepts::CustomT<DataT>) {
auto publisher = this->create_publisher<DataT>(topic_name, this->get_qos());
this->outputs_.at(parsed_signal_name) =
std::make_shared<PublisherHandler<rclcpp::Publisher<DataT>, DataT>>(PublisherType::PUBLISHER, publisher)
->create_publisher_interface(message_pair);
}
break;
}
}
} catch (const std::exception& ex) {
RCLCPP_ERROR_STREAM(this->get_logger(), "Failed to add output '" << signal_name << "': " << ex.what());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <modulo_core/communication/PublisherHandler.hpp>
#include <modulo_core/communication/PublisherType.hpp>
#include <modulo_core/communication/SubscriptionHandler.hpp>
#include <modulo_core/concepts.hpp>
#include <modulo_core/exceptions.hpp>
#include <modulo_core/translators/parameter_translators.hpp>

Expand Down Expand Up @@ -694,6 +695,16 @@ inline void ComponentInterface::add_input(
subscription_interface = subscription_handler->create_subscription_interface(subscription);
break;
}
case MessageType::CUSTOM_MESSAGE: {
if constexpr (modulo_core::concepts::CustomT<DataT>) {
auto subscription_handler = std::make_shared<SubscriptionHandler<DataT>>(message_pair);
auto subscription = rclcpp::create_subscription<DataT>(
this->node_parameters_, this->node_topics_, topic_name, this->qos_,
subscription_handler->get_callback(user_callback));
subscription_interface = subscription_handler->create_subscription_interface(subscription);
}
break;
}
}
this->inputs_.insert_or_assign(parsed_signal_name, subscription_interface);
} catch (const std::exception& ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,18 @@ class LifecycleComponent : public rclcpp_lifecycle::LifecycleNode, public Compon
using ComponentInterface::publish_outputs;
using ComponentInterface::publish_predicates;
using rclcpp_lifecycle::LifecycleNode::get_parameter;

std::map<
std::string,
std::function<std::shared_ptr<modulo_core::communication::PublisherInterface>(const std::string& topic_name)>>
configuration_callables_;///< Map of configuration callables
};

template<typename DataT>
inline void LifecycleComponent::add_output(
const std::string& signal_name, const std::shared_ptr<DataT>& data, const std::string& default_topic,
bool fixed_topic, bool publish_on_step) {

if (this->get_lifecycle_state().id() != lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED
&& this->get_lifecycle_state().id() != lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) {
RCLCPP_WARN_STREAM_THROTTLE(
Expand All @@ -287,9 +293,97 @@ inline void LifecycleComponent::add_output(
return;
}
try {
this->create_output(
auto parsed_signal_name = this->create_output(
modulo_core::communication::PublisherType::LIFECYCLE_PUBLISHER, signal_name, data, default_topic, fixed_topic,
publish_on_step);

using modulo_core::communication::MessageType;
using modulo_core::communication::PublisherHandler;
using modulo_core::communication::PublisherInterface;
using modulo_core::communication::PublisherType;

auto message_pair = this->outputs_.at(parsed_signal_name)->get_message_pair();

switch (this->outputs_.at(parsed_signal_name)->get_message_pair()->get_type()) {
bpapaspyros marked this conversation as resolved.
Show resolved Hide resolved
case MessageType::BOOL: {
this->configuration_callables_.insert_or_assign(
parsed_signal_name, [this, message_pair](const std::string& topic_name) {
auto publisher = this->create_publisher<std_msgs::msg::Bool>(topic_name, this->get_qos());
return std::make_shared<PublisherHandler<
rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::Bool>, std_msgs::msg::Bool>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
});
break;
}
case MessageType::FLOAT64: {
this->configuration_callables_.insert_or_assign(
parsed_signal_name, [this, message_pair](const std::string& topic_name) {
auto publisher = this->create_publisher<std_msgs::msg::Float64>(topic_name, this->get_qos());
return std::make_shared<PublisherHandler<
rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::Float64>, std_msgs::msg::Float64>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
});
break;
}
case MessageType::FLOAT64_MULTI_ARRAY: {
this->configuration_callables_.insert_or_assign(
parsed_signal_name, [this, message_pair](const std::string& topic_name) {
auto publisher = this->create_publisher<std_msgs::msg::Float64MultiArray>(topic_name, this->get_qos());
return std::make_shared<PublisherHandler<
rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::Float64MultiArray>,
std_msgs::msg::Float64MultiArray>>(PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
});
break;
}
case MessageType::INT32: {
this->configuration_callables_.insert_or_assign(
parsed_signal_name, [this, message_pair](const std::string& topic_name) {
auto publisher = this->create_publisher<std_msgs::msg::Int32>(topic_name, this->get_qos());
return std::make_shared<PublisherHandler<
rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::Int32>, std_msgs::msg::Int32>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
});
break;
}
case MessageType::STRING: {
this->configuration_callables_.insert_or_assign(
parsed_signal_name, [this, message_pair](const std::string& topic_name) {
auto publisher = this->create_publisher<std_msgs::msg::String>(topic_name, this->get_qos());
return std::make_shared<PublisherHandler<
rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::String>, std_msgs::msg::String>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
});
break;
}
case MessageType::ENCODED_STATE: {
this->configuration_callables_.insert_or_assign(
parsed_signal_name, [this, message_pair](const std::string& topic_name) {
auto publisher = this->create_publisher<modulo_core::EncodedState>(topic_name, this->get_qos());
return std::make_shared<PublisherHandler<
rclcpp_lifecycle::LifecyclePublisher<modulo_core::EncodedState>, modulo_core::EncodedState>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
});
break;
}
case MessageType::CUSTOM_MESSAGE: {
if constexpr (modulo_core::concepts::CustomT<DataT>) {
this->configuration_callables_.insert_or_assign(
parsed_signal_name, [this, message_pair](const std::string& topic_name) {
auto publisher = this->create_publisher<DataT>(topic_name, this->get_qos());
return std::make_shared<PublisherHandler<rclcpp_lifecycle::LifecyclePublisher<DataT>, DataT>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
});
}
break;
}
}
} catch (const modulo_core::exceptions::AddSignalException& ex) {
RCLCPP_ERROR_STREAM(this->get_logger(), "Failed to add output '" << signal_name << "': " << ex.what());
}
Expand Down
51 changes: 1 addition & 50 deletions source/modulo_components/src/LifecycleComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,56 +243,7 @@ bool LifecycleComponent::configure_outputs() {
auto topic_name = this->get_parameter_value<std::string>(name + "_topic");
RCLCPP_DEBUG_STREAM(
this->get_logger(), "Configuring output '" << name << "' with topic name '" << topic_name << "'.");
auto message_pair = interface->get_message_pair();
switch (message_pair->get_type()) {
case MessageType::BOOL: {
auto publisher = this->create_publisher<std_msgs::msg::Bool>(topic_name, this->get_qos());
interface = std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::Bool>, std_msgs::msg::Bool>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
break;
}
case MessageType::FLOAT64: {
auto publisher = this->create_publisher<std_msgs::msg::Float64>(topic_name, this->get_qos());
interface =
std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::Float64>, std_msgs::msg::Float64>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
break;
}
case MessageType::FLOAT64_MULTI_ARRAY: {
auto publisher = this->create_publisher<std_msgs::msg::Float64MultiArray>(topic_name, this->get_qos());
interface = std::make_shared<PublisherHandler<
LifecyclePublisher<std_msgs::msg::Float64MultiArray>, std_msgs::msg::Float64MultiArray>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
break;
}
case MessageType::INT32: {
auto publisher = this->create_publisher<std_msgs::msg::Int32>(topic_name, this->get_qos());
interface =
std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::Int32>, std_msgs::msg::Int32>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
break;
}
case MessageType::STRING: {
auto publisher = this->create_publisher<std_msgs::msg::String>(topic_name, this->get_qos());
interface =
std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::String>, std_msgs::msg::String>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
break;
}
case MessageType::ENCODED_STATE: {
auto publisher = this->create_publisher<modulo_core::EncodedState>(topic_name, this->get_qos());
interface = std::make_shared<
PublisherHandler<LifecyclePublisher<modulo_core::EncodedState>, modulo_core::EncodedState>>(
PublisherType::LIFECYCLE_PUBLISHER, publisher)
->create_publisher_interface(message_pair);
break;
}
}
interface = this->configuration_callables_.at(name)(topic_name);
} catch (const modulo_core::exceptions::CoreException& ex) {
success = false;
RCLCPP_ERROR_STREAM(this->get_logger(), "Failed to configure output '" << name << "': " << ex.what());
Expand Down
21 changes: 21 additions & 0 deletions source/modulo_components/test/cpp/test_component.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <gtest/gtest.h>

#include <geometry_msgs/msg/twist.hpp>
#include <rclcpp/node_options.hpp>
#include <sensor_msgs/msg/image.hpp>

#include <modulo_core/EncodedState.hpp>

Expand Down Expand Up @@ -70,5 +72,24 @@ TEST_F(ComponentTest, AddRemoveOutput) {
EXPECT_NO_THROW(component_->publish_output("8_teEsTt_#1@3"));
EXPECT_NO_THROW(component_->publish_output("test_13"));
EXPECT_THROW(component_->publish_output(""), modulo_core::exceptions::CoreException);

auto std_msg_data = std::make_shared<std_msgs::msg::String>();
std_msg_data->data = "foo";
component_->add_output("custom_msg_test", std_msg_data);
EXPECT_TRUE(component_->outputs_.find("custom_msg_test") != component_->outputs_.end());
EXPECT_NO_THROW(component_->outputs_.at("custom_msg_test")->publish());
EXPECT_THROW(component_->publish_output("custom_msg_test"), modulo_core::exceptions::CoreException);

auto geometry_msg_data = std::make_shared<geometry_msgs::msg::Twist>();
component_->add_output("geometry_msg_test", geometry_msg_data);
EXPECT_TRUE(component_->outputs_.find("geometry_msg_test") != component_->outputs_.end());
EXPECT_NO_THROW(component_->outputs_.at("geometry_msg_test")->publish());
EXPECT_THROW(component_->publish_output("geometry_msg_test"), modulo_core::exceptions::CoreException);

auto sensor_msg_data = std::make_shared<sensor_msgs::msg::Image>();
component_->add_output("sensor_msg_test", sensor_msg_data);
EXPECT_TRUE(component_->outputs_.find("sensor_msg_test") != component_->outputs_.end());
EXPECT_NO_THROW(component_->outputs_.at("sensor_msg_test")->publish());
EXPECT_THROW(component_->publish_output("sensor_msg_test"), modulo_core::exceptions::CoreException);
}
}// namespace modulo_components
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "test_modulo_components/component_public_interfaces.hpp"

#include <sensor_msgs/msg/image.hpp>

namespace modulo_components {

using namespace std::chrono_literals;
Expand Down Expand Up @@ -107,6 +109,11 @@ TYPED_TEST(ComponentInterfaceTest, AddRemoveInput) {
// remove input
this->component_->remove_input("test_13");
EXPECT_TRUE(this->component_->inputs_.find("test_13") == this->component_->inputs_.end());

auto sensor_msg_data = std::make_shared<sensor_msgs::msg::Image>();
sensor_msg_data->height = 480;
EXPECT_NO_THROW(this->component_->add_input("sensor_msg_data", sensor_msg_data));
EXPECT_FALSE(this->component_->inputs_.find("sensor_msg_data") == this->component_->inputs_.end());
}

TYPED_TEST(ComponentInterfaceTest, AddInputWithUserCallback) {
Expand Down
Loading