7#include <controller_interface/controller_interface.hpp>
8#include <controller_interface/helpers.hpp>
9#include <realtime_tools/realtime_buffer.h>
10#include <realtime_tools/realtime_publisher.h>
11#include <tf2_ros/buffer.h>
12#include <tf2_ros/static_transform_broadcaster.h>
13#include <tf2_ros/transform_broadcaster.h>
14#include <tf2_ros/transform_listener.h>
16#include <state_representation/parameters/ParameterMap.hpp>
18#include <modulo_core/EncodedState.hpp>
19#include <modulo_core/Predicate.hpp>
20#include <modulo_core/communication/MessagePair.hpp>
21#include <modulo_core/exceptions.hpp>
22#include <modulo_core/translators/message_writers.hpp>
23#include <modulo_core/translators/parameter_translators.hpp>
25#include <modulo_interfaces/msg/assignment.hpp>
26#include <modulo_interfaces/msg/predicate_collection.hpp>
27#include <modulo_interfaces/srv/empty_trigger.hpp>
28#include <modulo_interfaces/srv/string_trigger.hpp>
30#include <modulo_utils/parsing.hpp>
32#include <modulo_core/concepts.hpp>
34namespace modulo_controllers {
37 std::shared_ptr<rclcpp::Subscription<modulo_core::EncodedState>>,
38 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Bool>>,
39 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Float64>>,
40 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Float64MultiArray>>,
41 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::Int32>>,
42 std::shared_ptr<rclcpp::Subscription<std_msgs::msg::String>>, std::any>
46 realtime_tools::RealtimeBuffer<std::shared_ptr<modulo_core::EncodedState>>,
47 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>,
48 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>,
49 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>,
50 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>,
51 realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>, std::any>
55 std::shared_ptr<state_representation::State>, std::shared_ptr<rclcpp::Publisher<modulo_core::EncodedState>>,
56 realtime_tools::RealtimePublisherSharedPtr<modulo_core::EncodedState>>
57 EncodedStatePublishers;
59 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Bool>>,
60 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Bool>>
63 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Float64>>,
64 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Float64>>
67 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Float64MultiArray>>,
68 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Float64MultiArray>>
71 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Int32>>,
72 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::Int32>>
75 std::shared_ptr<rclcpp::Publisher<std_msgs::msg::String>>,
76 realtime_tools::RealtimePublisherSharedPtr<std_msgs::msg::String>>
78typedef std::pair<std::any, std::any> CustomPublishers;
81 EncodedStatePublishers, BoolPublishers, DoublePublishers, DoubleVecPublishers, IntPublishers, StringPublishers,
91 ControllerInput(BufferVariant buffer_variant) : buffer(std::move(buffer_variant)) {}
93 std::chrono::time_point<std::chrono::steady_clock> timestamp;
122 CallbackReturn
on_init()
override;
129 CallbackReturn
on_configure(
const rclcpp_lifecycle::State& previous_state)
override;
141 const std::shared_ptr<state_representation::ParameterInterface>& parameter,
const std::string& description,
142 bool read_only =
false);
155 void add_parameter(
const std::string& name,
const T& value,
const std::string& description,
bool read_only =
false);
175 [[nodiscard]] std::shared_ptr<state_representation::ParameterInterface>
get_parameter(
const std::string& name)
const;
212 void set_assignment(
const std::string& assignment_name,
const T& assignment_value);
230 void add_predicate(
const std::string& predicate_name,
bool predicate_value);
237 void add_predicate(
const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function);
245 [[nodiscard]]
bool get_predicate(
const std::string& predicate_name)
const;
254 void set_predicate(
const std::string& predicate_name,
bool predicate_value);
263 void set_predicate(
const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function);
277 void trigger(
const std::string& trigger_name);
288 void add_input(
const std::string& name,
const std::string& topic_name =
"");
299 void add_output(
const std::string& name,
const std::string& topic_name =
"");
309 std::optional<T>
read_input(
const std::string& name);
320 void write_output(
const std::string& name,
const T& data);
338 const std::string& service_name,
353 const std::string& service_name,
372 const std::string& frame,
const std::string& reference_frame,
const tf2::TimePoint& time_point,
373 const tf2::Duration& duration);
386 const std::string& frame,
const std::string& reference_frame =
"world",
double validity_period = -1.0,
387 const tf2::Duration& duration = tf2::Duration(std::chrono::microseconds(10)));
403 void send_transform(
const state_representation::CartesianPose& transform);
409 void send_transforms(
const std::vector<state_representation::CartesianPose>& transforms);
427 [[nodiscard]] rclcpp::QoS
get_qos()
const;
433 void set_qos(
const rclcpp::QoS& qos);
455 bool validate_parameter(
const std::shared_ptr<state_representation::ParameterInterface>& parameter);
461 void pre_set_parameters_callback(std::vector<rclcpp::Parameter>& parameters);
469 rcl_interfaces::msg::SetParametersResult on_set_parameters_callback(
const std::vector<rclcpp::Parameter>& parameters);
476 modulo_interfaces::msg::Predicate get_predicate_message(
const std::string& name,
bool value)
const;
483 void publish_predicate(
const std::string& predicate_name,
bool value)
const;
488 void publish_predicates();
497 std::string validate_and_declare_signal(
498 const std::string& signal_name,
const std::string& type,
const std::string& default_topic,
499 bool fixed_topic =
false);
509 void create_input(
const ControllerInput& input,
const std::string& name,
const std::string& topic_name);
519 void create_output(
const PublisherVariant& publishers,
const std::string& name,
const std::string& topic_name);
528 std::shared_ptr<rclcpp::Subscription<T>> create_subscription(
const std::string& name,
const std::string& topic_name);
534 bool check_input_valid(
const std::string& name)
const;
539 template<
typename PublisherT,
typename MsgT,
typename T>
540 void write_std_output(
const std::string& name,
const T& data);
559 template<
bool acquire_lock,
typename CallbackT>
560 void create_service(
const std::string& service_name,
const std::function<CallbackT>& callback)
571 std::string validate_service_name(
const std::string& service_name,
const std::string& type)
const;
583 [[nodiscard]] geometry_msgs::msg::TransformStamped lookup_ros_transform(
584 const std::string& frame,
const std::string& reference_frame,
const tf2::TimePoint& time_point,
585 const tf2::Duration& duration);
595 void publish_transforms(
596 const std::vector<state_representation::CartesianPose>& transforms,
const std::shared_ptr<T>& tf_broadcaster,
597 bool is_static =
false);
599 state_representation::ParameterMap parameter_map_;
600 std::unordered_map<std::string, bool> read_only_parameters_;
601 std::shared_ptr<rclcpp::node_interfaces::PreSetParametersCallbackHandle>
602 pre_set_parameter_cb_handle_;
603 std::shared_ptr<rclcpp::node_interfaces::OnSetParametersCallbackHandle>
604 on_set_parameter_cb_handle_;
605 rcl_interfaces::msg::SetParametersResult set_parameters_result_;
606 bool pre_set_parameter_callback_called_ =
false;
608 std::vector<SubscriptionVariant> subscriptions_;
609 std::map<std::string, ControllerInput> inputs_;
610 std::map<std::string, std::shared_ptr<modulo_core::communication::MessagePairInterface>>
611 input_message_pairs_;
612 std::map<std::string, PublisherVariant> outputs_;
613 double input_validity_period_;
614 rclcpp::QoS qos_ = rclcpp::QoS(10);
616 std::map<std::string, std::shared_ptr<rclcpp::Service<modulo_interfaces::srv::EmptyTrigger>>>
618 std::map<std::string, std::shared_ptr<rclcpp::Service<modulo_interfaces::srv::StringTrigger>>>
621 state_representation::ParameterMap assignments_map_;
622 std::shared_ptr<rclcpp::Publisher<modulo_interfaces::msg::Assignment>> assignment_publisher_;
624 std::map<std::string, modulo_core::Predicate> predicates_;
625 std::shared_ptr<rclcpp::Publisher<modulo_interfaces::msg::PredicateCollection>>
626 predicate_publisher_;
627 std::vector<std::string> triggers_;
628 modulo_interfaces::msg::PredicateCollection predicate_message_;
629 std::shared_ptr<rclcpp::TimerBase> predicate_timer_;
631 std::timed_mutex command_mutex_;
633 std::map<std::string, std::function<void(CustomPublishers&,
const std::string&)>>
634 custom_output_configuration_callables_;
635 std::map<std::string, std::function<void(
const std::string&,
const std::string&)>>
636 custom_input_configuration_callables_;
638 std::shared_ptr<tf2_ros::Buffer> tf_buffer_;
639 std::shared_ptr<tf2_ros::TransformListener> tf_listener_;
640 std::shared_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
641 std::shared_ptr<tf2_ros::StaticTransformBroadcaster> static_tf_broadcaster_;
646 const std::string& name,
const T& value,
const std::string& description,
bool read_only) {
648 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to add parameter: Provide a non empty string as a name.");
651 add_parameter(state_representation::make_shared_parameter(name, value), description, read_only);
658 }
catch (
const state_representation::exceptions::InvalidParameterException& ex) {
660 "Failed to get parameter value of parameter '" + name +
"': " + ex.what());
667 std::vector<rclcpp::Parameter> parameters{
669 pre_set_parameters_callback(parameters);
670 pre_set_parameter_callback_called_ =
true;
671 auto result = get_node()->set_parameters(parameters).at(0);
672 if (!result.successful) {
673 RCLCPP_ERROR_THROTTLE(
674 get_node()->get_logger(), *get_node()->get_clock(), 1000,
675 "Failed to set parameter value of parameter '%s': %s", name.c_str(), result.reason.c_str());
677 }
catch (
const std::exception& ex) {
678 RCLCPP_ERROR_THROTTLE(
679 get_node()->get_logger(), *get_node()->get_clock(), 1000,
"Failed to set parameter value of parameter '%s': %s",
680 name.c_str(), ex.what());
687 auto buffer = std::make_shared<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>();
689 auto parsed_name = validate_and_declare_signal(name,
"input", topic_name);
690 if (!parsed_name.empty()) {
691 inputs_.insert_or_assign(parsed_name, input);
692 custom_input_configuration_callables_.insert_or_assign(
693 name, [
this](
const std::string& name,
const std::string& topic) {
695 get_node()->create_subscription<T>(topic, qos_, [
this, name](
const std::shared_ptr<T> message) {
696 auto buffer_variant = std::get<std::any>(inputs_.at(name).buffer);
697 auto buffer = std::any_cast<std::shared_ptr<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>>(
699 buffer->writeFromNonRT(message);
700 inputs_.at(name).timestamp = std::chrono::steady_clock::now();
702 subscriptions_.push_back(subscription);
706 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<modulo_core::EncodedState>>();
708 auto parsed_name = validate_and_declare_signal(name,
"input", topic_name);
709 if (!parsed_name.empty()) {
710 inputs_.insert_or_assign(parsed_name, input);
711 input_message_pairs_.insert_or_assign(
713 modulo_core::communication::make_shared_message_pair(std::make_shared<T>(), get_node()->get_clock()));
720 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>();
722 create_input(input, name, topic_name);
727 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>();
728 auto input = ControllerInput(buffer);
729 create_input(input, name, topic_name);
735 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>();
736 auto input = ControllerInput(buffer);
737 create_input(input, name, topic_name);
742 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>();
743 auto input = ControllerInput(buffer);
744 create_input(input, name, topic_name);
748inline void BaseControllerInterface::add_input<std::string>(
const std::string& name,
const std::string& topic_name) {
749 auto buffer = realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>();
750 auto input = ControllerInput(buffer);
751 create_input(input, name, topic_name);
755inline std::shared_ptr<rclcpp::Subscription<T>>
756BaseControllerInterface::create_subscription(
const std::string& name,
const std::string& topic_name) {
757 return get_node()->create_subscription<T>(topic_name, qos_, [
this, name](
const std::shared_ptr<T> message) {
758 std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>(inputs_.at(name).buffer).writeFromNonRT(message);
759 inputs_.at(name).timestamp = std::chrono::steady_clock::now();
766 typedef std::pair<std::shared_ptr<rclcpp::Publisher<T>>, realtime_tools::RealtimePublisherSharedPtr<T>> PublisherT;
767 auto parsed_name = validate_and_declare_signal(name,
"output", topic_name);
768 if (!parsed_name.empty()) {
769 outputs_.insert_or_assign(parsed_name, PublisherT());
770 custom_output_configuration_callables_.insert_or_assign(
771 name, [
this](CustomPublishers& pub,
const std::string& topic) {
772 auto publisher = get_node()->create_publisher<T>(topic, qos_);
773 pub.first = publisher;
774 pub.second = std::make_shared<realtime_tools::RealtimePublisher<T>>(publisher);
778 std::shared_ptr<state_representation::State> state_ptr = std::make_shared<T>();
779 create_output(EncodedStatePublishers(state_ptr, {}, {}), name, topic_name);
785 create_output(BoolPublishers(), name, topic_name);
790 create_output(DoublePublishers(), name, topic_name);
796 create_output(DoubleVecPublishers(), name, topic_name);
801 create_output(IntPublishers(), name, topic_name);
805inline void BaseControllerInterface::add_output<std::string>(
const std::string& name,
const std::string& topic_name) {
806 create_output(StringPublishers(), name, topic_name);
811 if (!check_input_valid(name)) {
817 auto buffer_variant = std::get<std::any>(inputs_.at(name).buffer);
818 auto buffer = std::any_cast<std::shared_ptr<realtime_tools::RealtimeBuffer<std::shared_ptr<T>>>>(buffer_variant);
819 return **(buffer->readFromNonRT());
820 }
catch (
const std::bad_any_cast& ex) {
821 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to read custom input: %s", ex.what());
826 **std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<modulo_core::EncodedState>>>(inputs_.at(name).buffer)
828 std::shared_ptr<state_representation::State> state;
830 auto message_pair = input_message_pairs_.at(name);
831 message_pair->read<modulo_core::EncodedState, state_representation::State>(message);
832 state = message_pair->get_message_pair<modulo_core::EncodedState, state_representation::State>()->get_data();
833 }
catch (
const std::exception& ex) {
834 RCLCPP_WARN_THROTTLE(
835 get_node()->get_logger(), *get_node()->get_clock(), 1000,
836 "Could not read EncodedState message on input '%s': %s", name.c_str(), ex.what());
839 if (state->is_empty()) {
842 auto cast_ptr = std::dynamic_pointer_cast<T>(state);
843 if (cast_ptr !=
nullptr) {
846 RCLCPP_WARN_THROTTLE(
847 get_node()->get_logger(), *get_node()->get_clock(), 1000,
848 "Dynamic cast of message on input '%s' from type '%s' to type '%s' failed.", name.c_str(),
849 get_state_type_name(state->get_type()).c_str(), get_state_type_name(T().get_type()).c_str());
857 if (!check_input_valid(name)) {
862 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>>(inputs_.at(name).buffer)
869 if (!check_input_valid(name)) {
872 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>>(inputs_.at(name).buffer)
878inline std::optional<std::vector<double>>
880 if (!check_input_valid(name)) {
883 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>>(
884 inputs_.at(name).buffer)
891 if (!check_input_valid(name)) {
894 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>>(inputs_.at(name).buffer)
900inline std::optional<std::string> BaseControllerInterface::read_input<std::string>(
const std::string& name) {
901 if (!check_input_valid(name)) {
904 return (*std::get<realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>>(inputs_.at(name).buffer)
911 if (outputs_.find(name) == outputs_.end()) {
912 RCLCPP_WARN_THROTTLE(
913 get_node()->get_logger(), *get_node()->get_clock(), 1000,
"Could not find output '%s'", name.c_str());
918 CustomPublishers publishers;
920 publishers = std::get<CustomPublishers>(outputs_.at(name));
921 }
catch (
const std::bad_variant_access&) {
922 RCLCPP_WARN_THROTTLE(
923 get_node()->get_logger(), *get_node()->get_clock(), 1000,
924 "Could not retrieve publisher for output '%s': Invalid output type", name.c_str());
928 std::shared_ptr<realtime_tools::RealtimePublisher<T>> rt_pub;
930 rt_pub = std::any_cast<std::shared_ptr<realtime_tools::RealtimePublisher<T>>>(publishers.second);
931 }
catch (
const std::bad_any_cast& ex) {
932 RCLCPP_ERROR_THROTTLE(
933 get_node()->get_logger(), *get_node()->get_clock(), 1000,
934 "Skipping publication of output '%s' due to wrong data type: %s", name.c_str(), ex.what());
937 if (rt_pub && rt_pub->trylock()) {
939 rt_pub->unlockAndPublish();
942 if (data.is_empty()) {
943 RCLCPP_DEBUG_THROTTLE(
944 get_node()->get_logger(), *get_node()->get_clock(), 1000,
945 "Skipping publication of output '%s' due to emptiness of state", name.c_str());
948 EncodedStatePublishers publishers;
950 publishers = std::get<EncodedStatePublishers>(outputs_.at(name));
951 }
catch (
const std::bad_variant_access&) {
952 RCLCPP_WARN_THROTTLE(
953 get_node()->get_logger(), *get_node()->get_clock(), 1000,
954 "Could not retrieve publisher for output '%s': Invalid output type", name.c_str());
957 if (
const auto output_type = std::get<0>(publishers)->get_type(); output_type != data.get_type()) {
958 RCLCPP_WARN_THROTTLE(
959 get_node()->get_logger(), *get_node()->get_clock(), 1000,
960 "Skipping publication of output '%s' due to wrong data type (expected '%s', got '%s')",
961 state_representation::get_state_type_name(output_type).c_str(),
962 state_representation::get_state_type_name(data.get_type()).c_str(), name.c_str());
965 auto rt_pub = std::get<2>(publishers);
966 if (rt_pub && rt_pub->trylock()) {
970 RCLCPP_ERROR_THROTTLE(
971 get_node()->get_logger(), *get_node()->get_clock(), 1000,
"Failed to publish output '%s': %s", name.c_str(),
974 rt_pub->unlockAndPublish();
979template<
typename PublisherT,
typename MsgT,
typename T>
980void BaseControllerInterface::write_std_output(
const std::string& name,
const T& data) {
981 if (outputs_.find(name) == outputs_.end()) {
982 RCLCPP_WARN_THROTTLE(
983 get_node()->get_logger(), *get_node()->get_clock(), 1000,
"Could not find output '%s'", name.c_str());
986 PublisherT publishers;
988 publishers = std::get<PublisherT>(outputs_.at(name));
989 }
catch (
const std::bad_variant_access&) {
990 RCLCPP_WARN_THROTTLE(
991 get_node()->get_logger(), *get_node()->get_clock(), 1000,
992 "Could not retrieve publisher for output '%s': Invalid output type", name.c_str());
995 auto rt_pub = publishers.second;
996 if (rt_pub && rt_pub->trylock()) {
997 rt_pub->msg_.data = data;
998 rt_pub->unlockAndPublish();
1004 write_std_output<BoolPublishers, std_msgs::msg::Bool, bool>(name, data);
1009 write_std_output<DoublePublishers, std_msgs::msg::Float64, double>(name, data);
1014 write_std_output<DoubleVecPublishers, std_msgs::msg::Float64MultiArray, std::vector<double>>(name, data);
1019 write_std_output<IntPublishers, std_msgs::msg::Int32, int>(name, data);
1024 write_std_output<StringPublishers, std_msgs::msg::String, std::string>(name, data);
1027template<
bool acquire_lock,
typename CallbackT>
1029BaseControllerInterface::create_service(
const std::string& service_name,
const std::function<CallbackT>& callback)
1031 std::is_same_v<CallbackT, ControllerServiceResponse(
const std::string&)>
1032 || std::is_same_v<CallbackT, ControllerServiceResponse(
void)>)
1034 constexpr bool is_empty = std::is_same_v<CallbackT, ControllerServiceResponse(
void)>;
1036 std::conditional_t<is_empty, modulo_interfaces::srv::EmptyTrigger, modulo_interfaces::srv::StringTrigger>;
1038 auto parsed_service_name = validate_service_name(service_name, is_empty ?
"empty" :
"string");
1039 if (parsed_service_name.empty()) {
1044 auto service = get_node()->create_service<ServiceT>(
1045 "~/" + parsed_service_name,
1047 const std::shared_ptr<typename ServiceT::Request> request,
1048 std::shared_ptr<typename ServiceT::Response> response) {
1050 auto run_callback = [&]() {
1051 ControllerServiceResponse callback_response;
1052 if constexpr (is_empty) {
1053 callback_response = callback();
1055 callback_response = callback(request->payload);
1057 response->success = callback_response.success;
1058 response->message = callback_response.message;
1061 if constexpr (!acquire_lock) {
1064 std::unique_lock<std::timed_mutex> lock(this->command_mutex_, std::defer_lock);
1065 if (lock.try_lock_for(std::chrono::milliseconds{100})) {
1068 response->success =
false;
1069 response->message =
"Unable to acquire lock for command interface within 100ms";
1072 }
catch (
const std::exception& ex) {
1073 response->success =
false;
1074 response->message = ex.what();
1078 if constexpr (is_empty) {
1079 empty_services_.insert_or_assign(parsed_service_name, service);
1081 string_services_.insert_or_assign(parsed_service_name, service);
1083 }
catch (
const std::exception& ex) {
1084 RCLCPP_ERROR(get_node()->get_logger(),
"Failed to add service '%s': %s", parsed_service_name.c_str(), ex.what());
1090 std::string parsed_name = modulo_utils::parsing::parse_topic_name(assignment_name);
1091 if (parsed_name.empty()) {
1092 RCLCPP_ERROR_STREAM(
1093 get_node()->get_logger(),
1094 "The parsed name for assignment '" + assignment_name
1095 +
"' is empty. Provide a string with valid characters for the assignment name ([a-z0-9_]).");
1098 if (assignment_name != parsed_name) {
1100 get_node()->get_logger(),
1101 "The parsed name for assignment '" + assignment_name +
"' is '" + parsed_name
1102 +
"'. Use the parsed name to refer to this assignment.");
1105 this->assignments_map_.get_parameter(parsed_name);
1107 get_node()->get_logger(),
"Assignment with name '" + parsed_name +
"' already exists, overwriting.");
1108 }
catch (
const state_representation::exceptions::InvalidParameterException& ex) {
1109 RCLCPP_DEBUG_STREAM(get_node()->get_logger(),
"Adding assignment '" << parsed_name <<
"'.");
1112 assignments_map_.set_parameter(state_representation::make_shared_parameter<T>(parsed_name));
1113 }
catch (
const std::exception& ex) {
1114 RCLCPP_ERROR_STREAM_THROTTLE(
1115 get_node()->get_logger(), *get_node()->get_clock(), 1000,
1116 "Failed to add assignment '" << parsed_name <<
"': " << ex.what());
1122 modulo_interfaces::msg::Assignment message;
1123 std::shared_ptr<state_representation::ParameterInterface> assignment;
1125 assignment = assignments_map_.get_parameter(assignment_name);
1126 }
catch (
const state_representation::exceptions::InvalidParameterException&) {
1127 RCLCPP_ERROR_STREAM_THROTTLE(
1128 get_node()->get_logger(), *get_node()->get_clock(), 1000,
1129 "Failed to set assignment '" << assignment_name <<
"': Assignment does not exist.");
1133 assignment->set_parameter_value<T>(assignment_value);
1134 }
catch (
const state_representation::exceptions::InvalidParameterCastException&) {
1135 RCLCPP_ERROR_STREAM_THROTTLE(
1136 get_node()->get_logger(), *get_node()->get_clock(), 1000,
1137 "Failed to set assignment '" << assignment_name <<
"': Incompatible value type.");
1140 if (assignment_publisher_ ==
nullptr) {
1141 RCLCPP_ERROR_STREAM_THROTTLE(
1142 get_node()->get_logger(), *get_node()->get_clock(), 1000,
1143 "No assignment publisher configured. Make sure to add assignments `on_init` of the controller.");
1146 message.node = get_node()->get_fully_qualified_name();
1148 assignment_publisher_->publish(message);
1153 std::shared_ptr<state_representation::ParameterInterface> assignment;
1155 assignment = assignments_map_.get_parameter(assignment_name);
1156 }
catch (
const state_representation::exceptions::InvalidParameterException&) {
1158 "Failed to get value of assignment '" + assignment_name +
"': Assignment does not exist.");
1161 return assignment->get_parameter_value<T>();
1162 }
catch (
const state_representation::exceptions::InvalidParameterCastException&) {
1163 auto expected_type = state_representation::get_parameter_type_name(assignment->get_parameter_type());
1165 "Incompatible type for assignment '" + assignment_name +
"' defined with type '" + expected_type +
"'.");
1170inline void BaseControllerInterface::publish_transforms(
1171 const std::vector<state_representation::CartesianPose>& transforms,
const std::shared_ptr<T>& tf_broadcaster,
1173 std::string modifier = is_static ?
"static " :
"";
1174 if (tf_broadcaster ==
nullptr) {
1175 RCLCPP_ERROR_STREAM_THROTTLE(
1176 this->get_node()->get_logger(), *this->get_node()->get_clock(), 1000,
1177 "Failed to send " << modifier <<
"transform: No " << modifier <<
"TF broadcaster configured.");
1181 std::vector<geometry_msgs::msg::TransformStamped> transform_messages;
1182 transform_messages.reserve(transforms.size());
1183 for (
const auto& tf : transforms) {
1184 geometry_msgs::msg::TransformStamped transform_message;
1186 transform_messages.emplace_back(transform_message);
1188 tf_broadcaster->sendTransform(transform_messages);
1189 }
catch (
const std::exception& ex) {
1190 RCLCPP_ERROR_STREAM_THROTTLE(
1191 this->get_node()->get_logger(), *this->get_node()->get_clock(), 1000,
1192 "Failed to send " << modifier <<
"transform: " << ex.what());
Base controller class to combine ros2_control, control libraries and modulo.
void send_static_transform(const state_representation::CartesianPose &transform)
Send a static transform to TF.
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.
void add_static_tf_broadcaster()
Configure a static transform broadcaster.
rclcpp::QoS get_qos() const
Getter of the Quality of Service attribute.
void add_service_lockfree(const std::string &service_name, const std::function< ControllerServiceResponse(void)> &callback)
void send_static_transforms(const std::vector< state_representation::CartesianPose > &transforms)
Send a vector of static transforms to TF.
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.
void add_tf_broadcaster()
Configure a transform broadcaster.
bool is_active() const
Check if the controller is currently in state active or not.
void send_transform(const state_representation::CartesianPose &transform)
Send a transform to TF.
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 add_assignment(const std::string &assignment_name)
Add an assignment to the map of assignments.
void set_assignment(const std::string &assignment_name, const T &assignment_value)
Set an assignment.
CallbackReturn on_init() override
Declare parameters and register the on_set_parameters callback.
T get_assignment(const std::string &assignment_name) const
Get the value of an assignment.
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 add_tf_listener()
Configure a transform buffer and listener.
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.
void send_transforms(const std::vector< state_representation::CartesianPose > &transforms)
Send a vector of transforms to TF.
An exception class to notify errors when getting the value of an assignment.
An exception class to notify that the translation of a ROS message failed.
An exception class to notify errors with parameters in modulo classes.
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 controller services.