6#include <controller_interface/controller_interface.hpp>
7#include <controller_interface/helpers.hpp>
8#include <realtime_tools/realtime_buffer.h>
9#include <realtime_tools/realtime_publisher.h>
11#include <state_representation/parameters/ParameterMap.hpp>
13#include <modulo_core/EncodedState.hpp>
14#include <modulo_core/Predicate.hpp>
15#include <modulo_core/communication/MessagePair.hpp>
16#include <modulo_core/exceptions.hpp>
17#include <modulo_core/translators/message_writers.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>
26#include <modulo_core/concepts.hpp>
28namespace modulo_controllers {
31 std::shared_ptr<rclcpp::Subscription<modulo_core::EncodedState>>,
32 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Bool>>,
33 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Float64>>,
34 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Float64MultiArray>>,
35 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Int32>>,
36 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::String>>, std::any>
40 realtime_tools::RealtimeBuffer<std::shared_ptr<modulo_core::EncodedState>>,
41 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>,
42 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>,
43 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>,
44 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>,
45 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>, std::any>
49 std::shared_ptr<state_representation::State>, std::shared_ptr<rclcpp::Publisher<modulo_core::EncodedState>>,
50 realtime_tools::RealtimePublisherSharedPtr<modulo_core::EncodedState>>
51 EncodedStatePublishers;
53 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Bool>>,
54 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Bool>>
57 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Float64>>,
58 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Float64>>
61 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Float64MultiArray>>,
62 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Float64MultiArray>>
65 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Int32>>,
66 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Int32>>
69 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::String>>,
70 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::String>>
72typedef std::pair<std::any, std::any> CustomPublishers;
75 EncodedStatePublishers, BoolPublishers, DoublePublishers, DoubleVecPublishers, IntPublishers, StringPublishers,
85 ControllerInput(BufferVariant buffer_variant) : buffer(std::move(buffer_variant)) {}
87 std::chrono::time_point<std::chrono::steady_clock> timestamp;
135 const std::shared_ptr<state_representation::ParameterInterface>&
parameter,
const std::string&
description,
345 bool validate_parameter(
const std::shared_ptr<state_representation::ParameterInterface>&
parameter);
351 void pre_set_parameters_callback(std::vector<rclcpp::Parameter>&
parameters);
359 rcl_interfaces::msg::SetParametersResult on_set_parameters_callback(
const std::vector<rclcpp::Parameter>&
parameters);
366 modulo_interfaces::msg::Predicate get_predicate_message(
const std::string&
name,
bool value)
const;
378 void publish_predicates();
387 std::string validate_and_declare_signal(
418 std::shared_ptr<rclcpp::Subscription<T>> create_subscription(
const std::string&
name,
const std::string&
topic_name);
424 bool check_input_valid(
const std::string&
name)
const;
429 template<
typename PublisherT,
typename MsgT,
typename T>
430 void write_std_output(
const std::string&
name,
const T&
data);
448 std::string validate_service_name(
const std::string&
service_name,
const std::string&
type)
const;
450 state_representation::ParameterMap parameter_map_;
451 std::unordered_map<std::string, bool> read_only_parameters_;
452 std::shared_ptr<rclcpp::node_interfaces::PreSetParametersCallbackHandle>
453 pre_set_parameter_cb_handle_;
454 std::shared_ptr<rclcpp::node_interfaces::OnSetParametersCallbackHandle>
455 on_set_parameter_cb_handle_;
456 rcl_interfaces::msg::SetParametersResult set_parameters_result_;
457 bool pre_set_parameter_callback_called_ =
false;
459 std::vector<SubscriptionVariant> subscriptions_;
460 std::map<std::string, ControllerInput> inputs_;
461 std::map<std::string, std::shared_ptr<modulo_core::communication::MessagePairInterface>>
462 input_message_pairs_;
463 std::map<std::string, PublisherVariant> outputs_;
464 double input_validity_period_;
465 rclcpp::QoS qos_ = rclcpp::QoS(10);
467 std::map<std::string, std::shared_ptr<rclcpp::Service<modulo_interfaces::srv::EmptyTrigger>>>
469 std::map<std::string, std::shared_ptr<rclcpp::Service<modulo_interfaces::srv::StringTrigger>>>
472 std::map<std::string, modulo_core::Predicate> predicates_;
473 std::shared_ptr<rclcpp::Publisher<modulo_interfaces::msg::PredicateCollection>>
474 predicate_publisher_;
475 std::vector<std::string> triggers_;
476 modulo_interfaces::msg::PredicateCollection predicate_message_;
477 std::shared_ptr<rclcpp::TimerBase> predicate_timer_;
479 std::timed_mutex command_mutex_;
481 std::map<std::string, std::function<
void(CustomPublishers&,
const std::string&)>>
482 custom_output_configuration_callables_;
483 std::map<std::string, std::function<
void(
const std::string&,
const std::string&)>>
484 custom_input_configuration_callables_;
489 const std::string& name,
const T& value,
const std::string& description,
bool read_only) {
501 }
catch (
const state_representation::exceptions::InvalidParameterException&
ex) {
503 "Failed to get parameter value of parameter '" +
name +
"': " +
ex.what());
513 pre_set_parameter_callback_called_ =
true;
518 "Failed to set parameter value of parameter '%s': %s",
name.c_str(),
result.reason.c_str());
520 }
catch (
const std::exception&
ex) {
530 auto buffer = std::make_shared<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>();
535 custom_input_configuration_callables_.insert_or_assign(
536 name, [
this](
const std::string&
name,
const std::string&
topic) {
538 get_node()->create_subscription<
T>(
topic, qos_, [
this,
name](
const std::shared_ptr<T> message) {
540 auto buffer = std::any_cast<std::shared_ptr<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>>(
542 buffer->writeFromNonRT(message);
543 inputs_.at(
name).timestamp = std::chrono::steady_clock::now();
549 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<modulo_core::EncodedState>>();
554 input_message_pairs_.insert_or_assign(
556 modulo_core::communication::make_shared_message_pair(std::make_shared<T>(),
get_node()->
get_clock()));
563 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>();
570 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>();
571 auto input = ControllerInput(buffer);
578 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>();
579 auto input = ControllerInput(buffer);
580 create_input(input, name, topic_name);
585 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>();
586 auto input = ControllerInput(buffer);
591inline void BaseControllerInterface::add_input<std::string>(
const std::string& name,
const std::string& topic_name) {
592 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>();
593 auto input = ControllerInput(buffer);
594 create_input(input, name, topic_name);
598inline std::shared_ptr<rclcpp::Subscription<T>>
599BaseControllerInterface::create_subscription(
const std::string& name,
const std::string& topic_name) {
601 std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>(inputs_.at(
name).buffer).
writeFromNonRT(message);
602 inputs_.at(
name).timestamp = std::chrono::steady_clock::now();
609 typedef std::pair<std::shared_ptr<rclcpp::Publisher<T>>, realtime_tools::RealtimePublisherSharedPtr<T>>
PublisherT;
613 custom_output_configuration_callables_.insert_or_assign(
614 name, [
this](CustomPublishers&
pub,
const std::string&
topic) {
617 pub.second = std::make_shared<realtime_tools::RealtimePublisher<T>>(
publisher);
621 std::shared_ptr<state_representation::State>
state_ptr = std::make_shared<T>();
639 create_output(DoubleVecPublishers(), name, topic_name);
648inline void BaseControllerInterface::add_output<std::string>(
const std::string& name,
const std::string& topic_name) {
649 create_output(StringPublishers(), name, topic_name);
654 if (!check_input_valid(
name)) {
661 auto buffer = std::any_cast<std::shared_ptr<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>>(
buffer_variant);
662 return **(buffer->readFromNonRT());
663 }
catch (
const std::bad_any_cast&
ex) {
669 **std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<modulo_core::EncodedState>>>(inputs_.at(
name).buffer)
671 std::shared_ptr<state_representation::State>
state;
674 message_pair->read<modulo_core::EncodedState, state_representation::State>(message);
675 state =
message_pair->get_message_pair<modulo_core::EncodedState, state_representation::State>()->get_data();
676 }
catch (
const std::exception&
ex) {
679 "Could not read EncodedState message on input '%s': %s",
name.c_str(),
ex.what());
682 if (
state->is_empty()) {
691 "Dynamic cast of message on input '%s' from type '%s' to type '%s' failed.",
name.c_str(),
700 if (!check_input_valid(
name)) {
705 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>>(inputs_.at(
name).buffer)
712 if (!check_input_valid(
name)) {
715 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>>(inputs_.at(
name).buffer)
721inline std::optional<std::vector<double>>
723 if (!check_input_valid(name)) {
726 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>>(
727 inputs_.at(name).buffer)
734 if (!check_input_valid(
name)) {
737 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>>(inputs_.at(
name).buffer)
743inline std::optional<std::string> BaseControllerInterface::read_input<std::string>(
const std::string& name) {
744 if (!check_input_valid(name)) {
747 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>>(inputs_.at(name).buffer)
754 if (outputs_.find(
name) == outputs_.end()) {
764 }
catch (
const std::bad_variant_access&) {
767 "Could not retrieve publisher for output '%s': Invalid output type",
name.c_str());
771 std::shared_ptr<realtime_tools::RealtimePublisher<T>>
rt_pub;
773 rt_pub = std::any_cast<std::shared_ptr<realtime_tools::RealtimePublisher<T>>>(
publishers.second);
774 }
catch (
const std::bad_any_cast&
ex) {
777 "Skipping publication of output '%s' due to wrong data type: %s",
name.c_str(),
ex.what());
782 rt_pub->unlockAndPublish();
785 if (
data.is_empty()) {
788 "Skipping publication of output '%s' due to emptiness of state",
name.c_str());
794 }
catch (
const std::bad_variant_access&) {
797 "Could not retrieve publisher for output '%s': Invalid output type",
name.c_str());
803 "Skipping publication of output '%s' due to wrong data type (expected '%s', got '%s')",
805 state_representation::get_state_type_name(
data.get_type()).c_str(),
name.c_str());
817 rt_pub->unlockAndPublish();
822template<
typename PublisherT,
typename MsgT,
typename T>
823void BaseControllerInterface::write_std_output(
const std::string& name,
const T& data) {
824 if (outputs_.find(
name) == outputs_.end()) {
832 }
catch (
const std::bad_variant_access&) {
835 "Could not retrieve publisher for output '%s': Invalid output type",
name.c_str());
841 rt_pub->unlockAndPublish();
Base controller class to combine ros2_control, control libraries and modulo.
std::shared_ptr< state_representation::ParameterInterface > get_parameter(const std::string &name) const
Get a parameter by name.
CallbackReturn on_configure(const rclcpp_lifecycle::State &previous_state) override
Add signals.
rclcpp::QoS get_qos() const
Getter of the Quality of Service attribute.
bool is_active() const
Check if the controller is currently in state active or not.
void set_parameter_value(const std::string &name, const T &value)
Set the value of a parameter.
T get_parameter_value(const std::string &name) const
Get a parameter value by name.
void add_output(const std::string &name, const std::string &topic_name="")
Add an output to the controller.
void set_input_validity_period(double input_validity_period)
Set the input validity period of input signals.
CallbackReturn on_init() override
Declare parameters and register the on_set_parameters callback.
std::optional< T > read_input(const std::string &name)
Read the most recent message of an input.
void add_parameter(const std::shared_ptr< state_representation::ParameterInterface > ¶meter, const std::string &description, bool read_only=false)
Add a parameter.
void write_output(const std::string &name, const T &data)
Write an object to an output.
void trigger(const std::string &trigger_name)
Latch the trigger with the provided name.
void set_qos(const rclcpp::QoS &qos)
Set the Quality of Service for ROS publishers and subscribers.
void add_input(const std::string &name, const std::string &topic_name="")
Add an input to the controller.
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...
void add_predicate(const std::string &predicate_name, bool predicate_value)
Add a predicate to the map of predicates.
bool get_predicate(const std::string &predicate_name) const
Get the logical value of a predicate.
virtual bool on_validate_parameter_callback(const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Parameter validation function to be redefined by derived controller classes.
BaseControllerInterface()
Default constructor.
void add_service(const std::string &service_name, const std::function< ControllerServiceResponse(void)> &callback)
Add a service to trigger a callback function with no input arguments.
std::timed_mutex & get_command_mutex()
Get the reference to the command mutex.
void add_trigger(const std::string &trigger_name)
Add a trigger to the controller.
An exception class to notify that the translation of a ROS message failed.
An exception class to notify errors with parameters in modulo classes.
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 controller services.