3#include <rclcpp/node_interfaces/node_interfaces.hpp>
5#include <tf2_ros/buffer.h>
6#include <tf2_ros/static_transform_broadcaster.h>
7#include <tf2_ros/transform_broadcaster.h>
8#include <tf2_ros/transform_listener.h>
10#include <state_representation/parameters/ParameterMap.hpp>
12#include <modulo_core/Predicate.hpp>
13#include <modulo_core/communication/PublisherHandler.hpp>
14#include <modulo_core/communication/PublisherType.hpp>
15#include <modulo_core/communication/SubscriptionHandler.hpp>
16#include <modulo_core/concepts.hpp>
17#include <modulo_core/exceptions.hpp>
18#include <modulo_core/translators/parameter_translators.hpp>
20#include <modulo_interfaces/msg/predicate_collection.hpp>
21#include <modulo_interfaces/srv/empty_trigger.hpp>
22#include <modulo_interfaces/srv/string_trigger.hpp>
24#include <modulo_utils/parsing.hpp>
71 const std::shared_ptr<rclcpp::node_interfaces::NodeInterfaces<ALL_RCLCPP_NODE_INTERFACES>>& interfaces);
106 const std::shared_ptr<state_representation::ParameterInterface>& parameter,
const std::string& description,
107 bool read_only =
false);
121 void add_parameter(
const std::string& name,
const T& value,
const std::string& description,
bool read_only =
false);
129 [[nodiscard]] std::shared_ptr<state_representation::ParameterInterface>
get_parameter(
const std::string& name)
const;
170 void add_predicate(
const std::string& predicate_name,
bool predicate_value);
177 void add_predicate(
const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function);
185 [[nodiscard]]
bool get_predicate(
const std::string& predicate_name)
const;
194 void set_predicate(
const std::string& predicate_name,
bool predicate_value);
203 void set_predicate(
const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function);
216 void trigger(
const std::string& trigger_name);
226 void declare_input(
const std::string& signal_name,
const std::string& default_topic =
"",
bool fixed_topic =
false);
236 void declare_output(
const std::string& signal_name,
const std::string& default_topic =
"",
bool fixed_topic =
false);
246 template<
typename DataT>
248 const std::string& signal_name,
const std::shared_ptr<DataT>& data,
const std::string& default_topic =
"",
249 bool fixed_topic =
false);
260 template<
typename DataT>
262 const std::string& signal_name,
const std::shared_ptr<DataT>& data,
const std::function<
void()>& callback,
263 const std::string& default_topic =
"",
bool fixed_topic =
false);
273 template<
typename MsgT>
275 const std::string& signal_name,
const std::function<
void(
const std::shared_ptr<MsgT>)>& callback,
276 const std::string& default_topic =
"",
bool fixed_topic =
false);
290 template<
typename DataT>
293 const std::shared_ptr<DataT>& data,
const std::string& default_topic,
bool fixed_topic,
bool publish_on_step);
330 const std::string& service_name,
360 void send_transform(
const state_representation::CartesianPose& transform);
366 void send_transforms(
const std::vector<state_representation::CartesianPose>& transforms);
391 const std::string& frame,
const std::string& reference_frame,
const tf2::TimePoint& time_point,
392 const tf2::Duration& duration);
405 const std::string& frame,
const std::string& reference_frame =
"world",
double validity_period = -1.0,
406 const tf2::Duration& duration = tf2::Duration(std::chrono::microseconds(10)));
412 [[nodiscard]] rclcpp::QoS
get_qos()
const;
418 void set_qos(
const rclcpp::QoS& qos);
445 std::map<std::string, std::shared_ptr<modulo_core::communication::SubscriptionInterface>>
inputs_;
446 std::map<std::string, std::shared_ptr<modulo_core::communication::PublisherInterface>>
outputs_;
454 void pre_set_parameters_callback(std::vector<rclcpp::Parameter>& parameters);
462 rcl_interfaces::msg::SetParametersResult on_set_parameters_callback(
const std::vector<rclcpp::Parameter>& parameters);
471 bool validate_parameter(
const std::shared_ptr<state_representation::ParameterInterface>& parameter);
478 modulo_interfaces::msg::Predicate get_predicate_message(
const std::string& name,
bool value)
const;
490 const std::string& signal_name,
const std::string& type,
const std::string& default_topic,
bool fixed_topic);
502 const std::string& signal_name, std::map<std::string, std::shared_ptr<T>>& signal_map,
bool skip_check =
false);
512 std::string validate_service_name(
const std::string& service_name,
const std::string& type)
const;
519 void publish_predicate(
const std::string& predicate_name,
bool value)
const;
529 void publish_transforms(
530 const std::vector<state_representation::CartesianPose>& transforms,
const std::shared_ptr<T>& tf_broadcaster,
531 bool is_static =
false);
543 [[nodiscard]] geometry_msgs::msg::TransformStamped lookup_ros_transform(
544 const std::string& frame,
const std::string& reference_frame,
const tf2::TimePoint& time_point,
545 const tf2::Duration& duration);
549 std::mutex step_mutex_;
551 std::map<std::string, modulo_core::Predicate> predicates_;
552 std::shared_ptr<rclcpp::Publisher<modulo_interfaces::msg::PredicateCollection>>
553 predicate_publisher_;
554 modulo_interfaces::msg::PredicateCollection predicate_message_;
555 std::vector<std::string> triggers_;
557 std::map<std::string, std::shared_ptr<rclcpp::Service<modulo_interfaces::srv::EmptyTrigger>>>
559 std::map<std::string, std::shared_ptr<rclcpp::Service<modulo_interfaces::srv::StringTrigger>>>
562 std::map<std::string, std::function<void(
void)>> periodic_callbacks_;
564 state_representation::ParameterMap parameter_map_;
565 std::unordered_map<std::string, bool> read_only_parameters_;
566 std::shared_ptr<rclcpp::node_interfaces::PreSetParametersCallbackHandle>
567 pre_set_parameter_cb_handle_;
568 std::shared_ptr<rclcpp::node_interfaces::OnSetParametersCallbackHandle>
569 on_set_parameter_cb_handle_;
570 rcl_interfaces::msg::SetParametersResult set_parameters_result_;
571 bool pre_set_parameter_callback_called_ =
false;
573 std::shared_ptr<rclcpp::TimerBase> step_timer_;
574 std::shared_ptr<tf2_ros::Buffer> tf_buffer_;
575 std::shared_ptr<tf2_ros::TransformListener> tf_listener_;
576 std::shared_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
577 std::shared_ptr<tf2_ros::StaticTransformBroadcaster> static_tf_broadcaster_;
579 std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_;
580 std::shared_ptr<rclcpp::node_interfaces::NodeClockInterface> node_clock_;
581 std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_;
582 std::shared_ptr<rclcpp::node_interfaces::NodeParametersInterface> node_parameters_;
583 std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_;
584 std::shared_ptr<rclcpp::node_interfaces::NodeTimersInterface> node_timers_;
585 std::shared_ptr<rclcpp::node_interfaces::NodeTopicsInterface> node_topics_;
586 rclcpp::QoS qos_ = rclcpp::QoS(10);
591 const std::string& name,
const T& value,
const std::string& description,
bool read_only) {
593 RCLCPP_ERROR(this->node_logging_->get_logger(),
"Failed to add parameter: Provide a non empty string as a name.");
596 this->
add_parameter(state_representation::make_shared_parameter(name, value), description, read_only);
603 }
catch (
const state_representation::exceptions::InvalidParameterException& ex) {
605 "Failed to get parameter value of parameter '" + name +
"': " + ex.what());
612 std::vector<rclcpp::Parameter> parameters{
614 this->pre_set_parameters_callback(parameters);
615 this->pre_set_parameter_callback_called_ =
true;
616 auto result = this->node_parameters_->set_parameters(parameters).at(0);
617 if (!result.successful) {
618 RCLCPP_ERROR_STREAM_THROTTLE(
619 this->node_logging_->get_logger(), *this->node_clock_->get_clock(), 1000,
620 "Failed to set parameter value of parameter '" << name <<
"': " << result.reason);
622 }
catch (
const std::exception& ex) {
623 RCLCPP_ERROR_STREAM_THROTTLE(
624 this->node_logging_->get_logger(), *this->node_clock_->get_clock(), 1000,
625 "Failed to set parameter value of parameter '" << name <<
"': " << ex.what());
629template<
typename DataT>
631 const std::string& signal_name,
const std::shared_ptr<DataT>& data,
const std::string& default_topic,
633 this->
add_input(signal_name, data, [] {}, default_topic, fixed_topic);
636template<
typename DataT>
638 const std::string& signal_name,
const std::shared_ptr<DataT>& data,
const std::function<
void()>& user_callback,
639 const std::string& default_topic,
bool fixed_topic) {
642 if (data ==
nullptr) {
645 this->
declare_input(signal_name, default_topic, fixed_topic);
646 std::string parsed_signal_name = modulo_utils::parsing::parse_topic_name(signal_name);
649 this->node_logging_->get_logger(),
650 "Adding input '" << parsed_signal_name <<
"' with topic name '" << topic_name <<
"'.");
651 auto message_pair = make_shared_message_pair(data, this->node_clock_->get_clock());
652 std::shared_ptr<SubscriptionInterface> subscription_interface;
653 switch (message_pair->get_type()) {
654 case MessageType::BOOL: {
655 auto subscription_handler =
656 std::make_shared<SubscriptionHandler<std_msgs::msg::Bool>>(message_pair, this->node_logging_->get_logger());
657 auto subscription = rclcpp::create_subscription<std_msgs::msg::Bool>(
658 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
659 subscription_handler->get_callback(user_callback));
660 subscription_interface = subscription_handler->create_subscription_interface(subscription);
663 case MessageType::FLOAT64: {
664 auto subscription_handler = std::make_shared<SubscriptionHandler<std_msgs::msg::Float64>>(
665 message_pair, this->node_logging_->get_logger());
666 auto subscription = rclcpp::create_subscription<std_msgs::msg::Float64>(
667 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
668 subscription_handler->get_callback(user_callback));
669 subscription_interface = subscription_handler->create_subscription_interface(subscription);
672 case MessageType::FLOAT64_MULTI_ARRAY: {
673 auto subscription_handler = std::make_shared<SubscriptionHandler<std_msgs::msg::Float64MultiArray>>(
674 message_pair, this->node_logging_->get_logger());
675 auto subscription = rclcpp::create_subscription<std_msgs::msg::Float64MultiArray>(
676 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
677 subscription_handler->get_callback(user_callback));
678 subscription_interface = subscription_handler->create_subscription_interface(subscription);
681 case MessageType::INT32: {
682 auto subscription_handler = std::make_shared<SubscriptionHandler<std_msgs::msg::Int32>>(
683 message_pair, this->node_logging_->get_logger());
684 auto subscription = rclcpp::create_subscription<std_msgs::msg::Int32>(
685 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
686 subscription_handler->get_callback(user_callback));
687 subscription_interface = subscription_handler->create_subscription_interface(subscription);
690 case MessageType::STRING: {
691 auto subscription_handler = std::make_shared<SubscriptionHandler<std_msgs::msg::String>>(
692 message_pair, this->node_logging_->get_logger());
693 auto subscription = rclcpp::create_subscription<std_msgs::msg::String>(
694 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
695 subscription_handler->get_callback(user_callback));
696 subscription_interface = subscription_handler->create_subscription_interface(subscription);
699 case MessageType::ENCODED_STATE: {
700 auto subscription_handler = std::make_shared<SubscriptionHandler<modulo_core::EncodedState>>(
701 message_pair, this->node_logging_->get_logger());
702 auto subscription = rclcpp::create_subscription<modulo_core::EncodedState>(
703 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
704 subscription_handler->get_callback(user_callback));
705 subscription_interface = subscription_handler->create_subscription_interface(subscription);
708 case MessageType::CUSTOM_MESSAGE: {
710 auto subscription_handler =
711 std::make_shared<SubscriptionHandler<DataT>>(message_pair, this->node_logging_->get_logger());
712 auto subscription = rclcpp::create_subscription<DataT>(
713 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
714 subscription_handler->get_callback(user_callback));
715 subscription_interface = subscription_handler->create_subscription_interface(subscription);
720 this->
inputs_.insert_or_assign(parsed_signal_name, subscription_interface);
721 }
catch (
const std::exception& ex) {
723 this->node_logging_->get_logger(),
"Failed to add input '" << signal_name <<
"': " << ex.what());
727template<
typename MsgT>
729 const std::string& signal_name,
const std::function<
void(
const std::shared_ptr<MsgT>)>& callback,
730 const std::string& default_topic,
bool fixed_topic) {
733 std::string parsed_signal_name = modulo_utils::parsing::parse_topic_name(signal_name);
734 this->
declare_input(parsed_signal_name, default_topic, fixed_topic);
737 this->node_logging_->get_logger(),
738 "Adding input '" << parsed_signal_name <<
"' with topic name '" << topic_name <<
"'.");
739 auto subscription = rclcpp::create_subscription<MsgT>(
740 this->node_parameters_, this->node_topics_, topic_name, this->qos_,
741 [
this, signal_name, callback](
const std::shared_ptr<MsgT> message) {
744 }
catch (
const std::exception& ex) {
745 RCLCPP_WARN_STREAM_THROTTLE(
746 this->node_logging_->get_logger(), *this->node_clock_->get_clock(), 1000,
747 "Unhandled exception in callback of input '" << signal_name <<
"': " << ex.what());
750 auto subscription_interface =
751 std::make_shared<SubscriptionHandler<MsgT>>()->create_subscription_interface(subscription);
752 this->
inputs_.insert_or_assign(parsed_signal_name, subscription_interface);
753 }
catch (
const std::exception& ex) {
755 this->node_logging_->get_logger(),
"Failed to add input '" << signal_name <<
"': " << ex.what());
759template<
typename DataT>
762 const std::shared_ptr<DataT>& data,
const std::string& default_topic,
bool fixed_topic,
bool publish_on_step) {
765 if (data ==
nullptr) {
769 auto parsed_signal_name = modulo_utils::parsing::parse_topic_name(signal_name);
771 this->node_logging_->get_logger(),
772 "Creating output '" << parsed_signal_name <<
"' (provided signal name was '" << signal_name <<
"').");
773 auto message_pair = make_shared_message_pair(data, this->node_clock_->get_clock());
775 parsed_signal_name, std::make_shared<PublisherInterface>(publisher_type, message_pair));
777 return parsed_signal_name;
780 }
catch (
const std::exception& ex) {
786inline bool ComponentInterface::remove_signal(
787 const std::string& signal_name, std::map<std::string, std::shared_ptr<T>>& signal_map,
bool skip_check) {
788 if (!skip_check && signal_map.find(signal_name) == signal_map.cend()) {
791 RCLCPP_DEBUG_STREAM(this->node_logging_->get_logger(),
"Removing signal '" << signal_name <<
"'.");
792 signal_map.at(signal_name).reset();
793 return signal_map.erase(signal_name);
798inline void ComponentInterface::publish_transforms(
799 const std::vector<state_representation::CartesianPose>& transforms,
const std::shared_ptr<T>& tf_broadcaster,
801 std::string modifier = is_static ?
"static " :
"";
802 if (tf_broadcaster ==
nullptr) {
803 RCLCPP_ERROR_STREAM_THROTTLE(
804 this->node_logging_->get_logger(), *this->node_clock_->get_clock(), 1000,
805 "Failed to send " << modifier <<
"transform: No " << modifier <<
"TF broadcaster configured.");
809 std::vector<geometry_msgs::msg::TransformStamped> transform_messages;
810 transform_messages.reserve(transforms.size());
811 for (
const auto& tf : transforms) {
812 geometry_msgs::msg::TransformStamped transform_message;
814 transform_messages.emplace_back(transform_message);
816 tf_broadcaster->sendTransform(transform_messages);
817 }
catch (
const std::exception& ex) {
818 RCLCPP_ERROR_STREAM_THROTTLE(
819 this->node_logging_->get_logger(), *this->node_clock_->get_clock(), 1000,
820 "Failed to send " << modifier <<
"transform: " << ex.what());
Friend class to the ComponentInterface to allow test fixtures to access protected and private members...
Base interface class for modulo components to wrap a ROS Node with custom behaviour.
double get_rate() const
Get the component rate in Hertz.
void send_transforms(const std::vector< state_representation::CartesianPose > &transforms)
Send a vector of transforms to TF.
virtual bool on_validate_parameter_callback(const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Parameter validation function to be redefined by derived Component classes.
void publish_outputs()
Helper function to publish all output signals.
void remove_input(const std::string &signal_name)
Remove an input from the map of inputs.
void declare_input(const std::string &signal_name, const std::string &default_topic="", bool fixed_topic=false)
Declare an input to create the topic parameter without adding it to the map of inputs yet.
void declare_output(const std::string &signal_name, const std::string &default_topic="", bool fixed_topic=false)
Declare an output to create the topic parameter without adding it to the map of outputs yet.
T get_period() const
Get the component period.
void set_parameter_value(const std::string &name, const T &value)
Set the value of a parameter.
void send_static_transforms(const std::vector< state_representation::CartesianPose > &transforms)
Send a vector of static transforms to TF.
std::map< std::string, std::shared_ptr< modulo_core::communication::PublisherInterface > > outputs_
Map of outputs.
std::shared_ptr< state_representation::ParameterInterface > get_parameter(const std::string &name) const
Get a parameter by name.
void finalize_interfaces()
Finalize all interfaces.
T get_parameter_value(const std::string &name) const
Get a parameter value by name.
virtual ~ComponentInterface()
Virtual destructor.
void add_static_tf_broadcaster()
Configure a static transform broadcaster.
void add_parameter(const std::shared_ptr< state_representation::ParameterInterface > ¶meter, const std::string &description, bool read_only=false)
Add a parameter.
void trigger(const std::string &trigger_name)
Latch the trigger with the provided name.
void add_input(const std::string &signal_name, const std::shared_ptr< DataT > &data, const std::string &default_topic="", bool fixed_topic=false)
Add and configure an input signal of the component.
state_representation::CartesianPose lookup_transform(const std::string &frame, const std::string &reference_frame, const tf2::TimePoint &time_point, const tf2::Duration &duration)
Look up a transform from TF.
virtual void on_step_callback()
Steps to execute periodically. To be redefined by derived Component classes.
void set_predicate(const std::string &predicate_name, bool predicate_value)
Set the value of the predicate given as parameter, if the predicate is not found does not do anything...
virtual void raise_error()
Notify an error in the component.
ComponentInterface(const std::shared_ptr< rclcpp::node_interfaces::NodeInterfaces< ALL_RCLCPP_NODE_INTERFACES > > &interfaces)
Constructor with all node interfaces.
void remove_output(const std::string &signal_name)
Remove an output from the map of outputs.
void publish_output(const std::string &signal_name)
Trigger the publishing of an output.
virtual void step()
Step function that is called periodically.
void publish_predicates()
Helper function to publish all predicates.
void set_qos(const rclcpp::QoS &qos)
Set the Quality of Service for ROS publishers and subscribers.
std::string create_output(modulo_core::communication::PublisherType publisher_type, const std::string &signal_name, const std::shared_ptr< DataT > &data, const std::string &default_topic, bool fixed_topic, bool publish_on_step)
Helper function to parse the signal name and add an unconfigured PublisherInterface to the map of out...
rclcpp::QoS get_qos() const
Getter of the Quality of Service attribute.
void add_predicate(const std::string &predicate_name, bool predicate_value)
Add a predicate to the map of predicates.
void send_transform(const state_representation::CartesianPose &transform)
Send a transform to TF.
void add_trigger(const std::string &trigger_name)
Add a trigger to the component. Triggers are predicates that are always false except when it's trigge...
void add_periodic_callback(const std::string &name, const std::function< void(void)> &callback)
Add a periodic callback function.
std::map< std::string, std::shared_ptr< modulo_core::communication::SubscriptionInterface > > inputs_
Map of inputs.
void add_tf_listener()
Configure a transform buffer and listener.
void evaluate_periodic_callbacks()
Helper function to evaluate all periodic function callbacks.
void add_tf_broadcaster()
Configure a transform broadcaster.
void send_static_transform(const state_representation::CartesianPose &transform)
Send a static transform to TF.
bool get_predicate(const std::string &predicate_name) const
Get the logical value of a predicate.
std::map< std::string, bool > periodic_outputs_
Map of outputs with periodic publishing flag.
void add_service(const std::string &service_name, const std::function< ComponentServiceResponse(void)> &callback)
Add a service to trigger a callback function with no input arguments.
An exception class to notify errors when adding a signal.
An exception class to notify that a certain pointer is null.
An exception class to notify errors with parameters in modulo classes.
Modulo Core communication module for handling messages on publication and subscription interfaces.
PublisherType
Enum of supported ROS publisher types for the PublisherInterface.
void write_message(geometry_msgs::msg::Accel &message, const state_representation::CartesianState &state, const rclcpp::Time &time)
Convert a CartesianState to a ROS geometry_msgs::msg::Accel.
rclcpp::Parameter write_parameter(const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Write a ROS Parameter from a ParameterInterface pointer.
Response structure to be returned by component services.