1#include "modulo_controllers/BaseControllerInterface.hpp"
5#include <lifecycle_msgs/msg/state.hpp>
7#include <modulo_core/translators/message_readers.hpp>
10struct overloaded : Ts... {
11 using Ts::operator()...;
14overloaded(Ts...) -> overloaded<Ts...>;
17using namespace state_representation;
18using namespace std::chrono_literals;
20namespace modulo_controllers {
23 : controller_interface::
ControllerInterface(), input_validity_period_(std::numeric_limits<double>::quiet_NaN()) {}
28 pre_set_parameter_cb_handle_ =
get_node()->add_pre_set_parameters_callback(
29 [
this](std::vector<rclcpp::Parameter>&
parameters) {
return this->pre_set_parameters_callback(
parameters); });
30 on_set_parameter_cb_handle_ =
get_node()->add_on_set_parameters_callback(
31 [
this](
const std::vector<rclcpp::Parameter>&
parameters) -> rcl_interfaces::msg::SetParametersResult {
32 return this->on_set_parameters_callback(
parameters);
34 add_parameter<double>(
"predicate_publishing_rate", 10.0,
"The rate at which to publish controller predicates");
35 return CallbackReturn::SUCCESS;
38rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
43 if (predicates_.size()) {
44 predicate_publisher_ =
45 get_node()->create_publisher<modulo_interfaces::msg::PredicateCollection>(
"/predicates", qos_);
46 predicate_message_.node =
get_node()->get_fully_qualified_name();
47 predicate_message_.type = modulo_interfaces::msg::PredicateCollection::CONTROLLER;
49 predicate_timer_ =
get_node()->create_wall_timer(
51 [
this]() { this->publish_predicates(); });
55 return CallbackReturn::SUCCESS;
59 const std::shared_ptr<ParameterInterface>& parameter,
const std::string& description,
bool read_only) {
69 read_only_parameters_.insert_or_assign(
parameter->get_name(),
false);
71 rcl_interfaces::msg::ParameterDescriptor
descriptor;
75 set_parameters_result_.successful =
true;
76 set_parameters_result_.reason =
"";
92 }
catch (
const std::exception&
ex) {
93 parameter_map_.remove_parameter(
parameter->get_name());
94 read_only_parameters_.erase(
parameter->get_name());
104 return parameter_map_.get_parameter(
name);
105 }
catch (
const state_representation::exceptions::InvalidParameterException&
ex) {
110void BaseControllerInterface::pre_set_parameters_callback(std::vector<rclcpp::Parameter>& parameters) {
111 if (pre_set_parameter_callback_called_) {
112 pre_set_parameter_callback_called_ =
false;
115 rcl_interfaces::msg::SetParametersResult
result;
128 result.successful =
false;
129 result.reason +=
"Validation of parameter '" +
ros_parameter.get_name() +
"' returned false!";
135 }
catch (
const std::exception&
ex) {
136 result.successful =
false;
140 set_parameters_result_ =
result;
143rcl_interfaces::msg::SetParametersResult
144BaseControllerInterface::on_set_parameters_callback(
const std::vector<rclcpp::Parameter>&) {
145 auto result = set_parameters_result_;
146 set_parameters_result_.successful =
true;
147 set_parameters_result_.reason =
"";
151bool BaseControllerInterface::validate_parameter(
const std::shared_ptr<ParameterInterface>& parameter) {
152 if (
parameter->get_name() ==
"activation_timeout" ||
parameter->get_name() ==
"input_validity_period") {
156 get_node()->
get_logger(),
"Parameter value of parameter '%s' should be a positive finite number",
173 const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function) {
186 }
catch (
const std::exception&
ex) {
188 get_node()->
get_logger(),
"Failed to evaluate callback of predicate '%s', returning false: %s",
198 "Failed to get predicate '%s': Predicate does not exists, returning false.",
predicate_name.c_str());
203 }
catch (
const std::exception&
ex) {
206 "Failed to evaluate callback of predicate '%s', returning false: %s",
predicate_name.c_str(),
ex.what());
216 const std::string& predicate_name,
const std::function<
bool(
void)>& predicate_function) {
221 "Failed to set predicate '%s': Predicate does not exist.",
predicate_name.c_str());
235 if (std::find(triggers_.cbegin(), triggers_.cend(),
trigger_name) != triggers_.cend()) {
237 get_node()->
get_logger(),
"Failed to add trigger: there is already a trigger with name '%s'.",
241 if (predicates_.find(
trigger_name) != predicates_.end()) {
243 get_node()->
get_logger(),
"Failed to add trigger: there is already a predicate with name '%s'.",
252 if (std::find(triggers_.cbegin(), triggers_.cend(),
trigger_name) == triggers_.cend()) {
259 predicates_.at(
trigger_name).set_predicate([]() {
return false; });
262modulo_interfaces::msg::Predicate
263BaseControllerInterface::get_predicate_message(
const std::string& name,
bool value)
const {
264 modulo_interfaces::msg::Predicate message;
265 message.predicate =
name;
266 message.value =
value;
270void BaseControllerInterface::publish_predicate(
const std::string& predicate_name,
bool value)
const {
271 auto message(predicate_message_);
273 predicate_publisher_->publish(message);
276void BaseControllerInterface::publish_predicates() {
277 auto message(predicate_message_);
283 if (message.predicates.size()) {
284 predicate_publisher_->publish(message);
288std::string BaseControllerInterface::validate_and_declare_signal(
289 const std::string& signal_name,
const std::string& type,
const std::string& default_topic,
bool fixed_topic) {
294 "The parsed signal name for %s '%s' is empty. Provide a string with valid characters for the signal name "
302 "The parsed signal name for %s '%s' is '%s'. Use the parsed signal name to refer to this %s and its topic "
328void BaseControllerInterface::create_input(
329 const ControllerInput& input,
const std::string& name,
const std::string& topic_name) {
336void BaseControllerInterface::add_inputs() {
342 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<EncodedState>>&) {
345 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>&) {
348 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>&) {
351 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>&) {
354 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>&) {
357 [&](
const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>&) {
360 [&](
const std::any&) {
364 }
catch (
const std::exception&
ex) {
370void BaseControllerInterface::create_output(
371 const PublisherVariant& publishers,
const std::string& name,
const std::string& topic_name) {
378void BaseControllerInterface::add_outputs() {
384 [&](EncodedStatePublishers&
pub) {
386 std::get<2>(
pub) = std::make_shared<realtime_tools::RealtimePublisher<EncodedState>>(std::get<1>(
pub));
388 [&](BoolPublishers&
pub) {
390 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Bool>>(
pub.first);
392 [&](DoublePublishers&
pub) {
393 pub.first =
get_node()->create_publisher<std_msgs::msg::Float64>(
topic, qos_);
394 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Float64>>(
pub.first);
396 [&](DoubleVecPublishers&
pub) {
397 pub.first =
get_node()->create_publisher<std_msgs::msg::Float64MultiArray>(
topic, qos_);
399 std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Float64MultiArray>>(
pub.first);
401 [&](IntPublishers&
pub) {
402 pub.first =
get_node()->create_publisher<std_msgs::msg::Int32>(
topic, qos_);
403 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Int32>>(
pub.first);
405 [&](StringPublishers&
pub) {
406 pub.first =
get_node()->create_publisher<std_msgs::msg::String>(
topic, qos_);
407 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::String>>(
pub.first);
409 [&](CustomPublishers&
pub) {
410 custom_output_configuration_callables_.at(
name)(
pub,
name);
413 }
catch (
const std::bad_any_cast&
ex) {
415 }
catch (
const std::exception&
ex) {
425bool BaseControllerInterface::check_input_valid(
const std::string& name)
const {
426 if (inputs_.find(
name) == inputs_.end()) {
431 if (
static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(
432 std::chrono::steady_clock::now() - inputs_.at(
name).timestamp)
435 >= input_validity_period_) {
442BaseControllerInterface::validate_service_name(
const std::string& service_name,
const std::string& type)
const {
447 "The parsed service name for %s service '%s' is empty. Provide a string with valid characters for the service "
456 "The parsed name for '%s' service '%s' is '%s'. Use the parsed name to refer to this service.",
type.c_str(),
480 auto service =
get_node()->create_service<modulo_interfaces::srv::EmptyTrigger>(
483 const std::shared_ptr<modulo_interfaces::srv::EmptyTrigger::Request>,
484 std::shared_ptr<modulo_interfaces::srv::EmptyTrigger::Response>
response) {
486 if (this->command_mutex_.try_lock_for(100
ms)) {
488 this->command_mutex_.unlock();
493 response->message =
"Unable to acquire lock for command interface within 100ms";
495 }
catch (
const std::exception&
ex) {
502 }
catch (
const std::exception&
ex) {
509 const std::string& service_name,
514 auto service =
get_node()->create_service<modulo_interfaces::srv::StringTrigger>(
517 const std::shared_ptr<modulo_interfaces::srv::StringTrigger::Request>
request,
518 std::shared_ptr<modulo_interfaces::srv::StringTrigger::Response>
response) {
520 if (this->command_mutex_.try_lock_for(100
ms)) {
522 this->command_mutex_.unlock();
527 response->message =
"Unable to acquire lock for command interface within 100ms";
529 }
catch (
const std::exception&
ex) {
536 }
catch (
const std::exception&
ex) {
551 return get_lifecycle_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE;
555 return command_mutex_;
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.
T get_parameter_value(const std::string &name) const
Get a parameter value by name.
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.
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 set_qos(const rclcpp::QoS &qos)
Set the Quality of Service for ROS publishers and subscribers.
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 errors with parameters in modulo classes.
An exception class to notify incompatibility when translating parameters from different sources.
rclcpp::Parameter write_parameter(const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Write a ROS Parameter from a ParameterInterface pointer.
rclcpp::ParameterType get_ros_parameter_type(const state_representation::ParameterType ¶meter_type)
Given a state representation parameter type, get the corresponding ROS parameter type.
void copy_parameter_value(const std::shared_ptr< const state_representation::ParameterInterface > &source_parameter, const std::shared_ptr< state_representation::ParameterInterface > ¶meter)
Copy the value of one parameter interface into another.
std::shared_ptr< state_representation::ParameterInterface > read_parameter_const(const rclcpp::Parameter &ros_parameter, const std::shared_ptr< const state_representation::ParameterInterface > ¶meter)
Update the parameter value of a ParameterInterface from a ROS Parameter object only if the two parame...
Response structure to be returned by controller services.