Modulo 5.0.0
Loading...
Searching...
No Matches
BaseControllerInterface.cpp
1#include "modulo_controllers/BaseControllerInterface.hpp"
2
3#include <chrono>
4
5#include <lifecycle_msgs/msg/state.hpp>
6
7#include <modulo_core/translators/message_readers.hpp>
8
9template<class... Ts>
10struct overloaded : Ts... {
11 using Ts::operator()...;
12};
13template<class... Ts>
14overloaded(Ts...) -> overloaded<Ts...>;
15
16using namespace modulo_core;
17using namespace state_representation;
18using namespace std::chrono_literals;
19
20namespace modulo_controllers {
21
23 : controller_interface::ControllerInterface(), input_validity_period_(std::numeric_limits<double>::quiet_NaN()) {}
24
25rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn BaseControllerInterface::on_init() {
26 // registering set_parameter callbacks is only possible on_init since the lifecycle node is not yet initialized
27 // on construction. This means we might not be able to validate parameter overrides - if they are provided.
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);
33 });
34
35 // these two parameters are declared in the ControllerInterface, need to add them in the internal map as well
36 parameter_map_.set_parameter(std::make_shared<Parameter<int>>("update_rate"));
37 read_only_parameters_.insert_or_assign("update_rate", false);
38 parameter_map_.set_parameter(std::make_shared<Parameter<bool>>("is_async", false));
39 read_only_parameters_.insert_or_assign("is_async", false);
40
41 add_parameter<double>("predicate_publishing_rate", 10.0, "The rate at which to publish controller predicates");
43 "input_validity_period", 1.0, "The maximum age of an input state before discarding it as expired");
44 return CallbackReturn::SUCCESS;
45}
46
47rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
48BaseControllerInterface::on_configure(const rclcpp_lifecycle::State&) {
49 input_validity_period_ = get_parameter_value<double>("input_validity_period");
50 add_inputs();
51 add_outputs();
52
53 if (predicates_.size()) {
54 predicate_publisher_ =
55 get_node()->create_publisher<modulo_interfaces::msg::PredicateCollection>("/predicates", qos_);
56 predicate_message_.node = get_node()->get_fully_qualified_name();
57 predicate_message_.type = modulo_interfaces::msg::PredicateCollection::CONTROLLER;
58
59 predicate_timer_ = get_node()->create_wall_timer(
60 std::chrono::nanoseconds(static_cast<int64_t>(1e9 / get_parameter_value<double>("predicate_publishing_rate"))),
61 [this]() { this->publish_predicates(); });
62 }
63
64 RCLCPP_DEBUG(get_node()->get_logger(), "Configuration of BaseControllerInterface successful");
65 return CallbackReturn::SUCCESS;
66}
67
69 const std::shared_ptr<ParameterInterface>& parameter, const std::string& description, bool read_only) {
70 rclcpp::Parameter ros_param;
71 try {
72 ros_param = translators::write_parameter(parameter);
74 throw modulo_core::exceptions::ParameterException("Failed to add parameter: " + std::string(ex.what()));
75 }
76 if (!get_node()->has_parameter(parameter->get_name())) {
77 RCLCPP_DEBUG(get_node()->get_logger(), "Adding parameter '%s'.", parameter->get_name().c_str());
78 parameter_map_.set_parameter(parameter);
79 read_only_parameters_.insert_or_assign(parameter->get_name(), false);
80 try {
81 rcl_interfaces::msg::ParameterDescriptor descriptor;
82 descriptor.description = description;
83 descriptor.read_only = read_only;
84 // since the pre_set_parameters_callback is not called on parameter declaration, this has to be true
85 set_parameters_result_.successful = true;
86 set_parameters_result_.reason = "";
87 if (parameter->is_empty()) {
88 descriptor.dynamic_typing = true;
89 descriptor.type = translators::get_ros_parameter_type(parameter->get_parameter_type());
90 get_node()->declare_parameter(parameter->get_name(), rclcpp::ParameterValue{}, descriptor);
91 } else {
92 get_node()->declare_parameter(parameter->get_name(), ros_param.get_parameter_value(), descriptor);
93 }
94 std::vector<rclcpp::Parameter> ros_parameters{get_node()->get_parameters({parameter->get_name()})};
95 pre_set_parameters_callback(ros_parameters);
96 auto result = on_set_parameters_callback(ros_parameters);
97 if (!result.successful) {
98 get_node()->undeclare_parameter(parameter->get_name());
100 }
101 read_only_parameters_.at(parameter->get_name()) = read_only;
102 } catch (const std::exception& ex) {
103 parameter_map_.remove_parameter(parameter->get_name());
104 read_only_parameters_.erase(parameter->get_name());
105 throw modulo_core::exceptions::ParameterException("Failed to add parameter: " + std::string(ex.what()));
106 }
107 } else {
108 RCLCPP_DEBUG(get_node()->get_logger(), "Parameter '%s' already exists.", parameter->get_name().c_str());
109 }
110}
111
112std::shared_ptr<ParameterInterface> BaseControllerInterface::get_parameter(const std::string& name) const {
113 try {
114 return parameter_map_.get_parameter(name);
115 } catch (const state_representation::exceptions::InvalidParameterException& ex) {
116 throw modulo_core::exceptions::ParameterException("Failed to get parameter '" + name + "': " + ex.what());
117 }
118}
119
120void BaseControllerInterface::pre_set_parameters_callback(std::vector<rclcpp::Parameter>& parameters) {
121 if (pre_set_parameter_callback_called_) {
122 pre_set_parameter_callback_called_ = false;
123 return;
124 }
125 rcl_interfaces::msg::SetParametersResult result;
126 result.successful = true;
127 for (auto& ros_parameter : parameters) {
128 try {
129 auto parameter = parameter_map_.get_parameter(ros_parameter.get_name());
130 if (read_only_parameters_.at(ros_parameter.get_name())) {
131 RCLCPP_DEBUG(get_node()->get_logger(), "Parameter '%s' is read only.", ros_parameter.get_name().c_str());
132 continue;
133 }
134
135 // convert the ROS parameter into a ParameterInterface without modifying the original
136 auto new_parameter = translators::read_parameter_const(ros_parameter, parameter);
137 if (!this->validate_parameter(new_parameter)) {
138 result.successful = false;
139 result.reason += "Validation of parameter '" + ros_parameter.get_name() + "' returned false!";
140 } else if (!new_parameter->is_empty()) {
141 // update the value of the parameter in the map
142 translators::copy_parameter_value(new_parameter, parameter);
143 ros_parameter = translators::write_parameter(new_parameter);
144 }
145 } catch (const std::exception& ex) {
146 result.successful = false;
147 result.reason += ex.what();
148 }
149 }
150 set_parameters_result_ = result;
151}
152
153rcl_interfaces::msg::SetParametersResult
154BaseControllerInterface::on_set_parameters_callback(const std::vector<rclcpp::Parameter>&) {
155 auto result = set_parameters_result_;
156 set_parameters_result_.successful = true;
157 set_parameters_result_.reason = "";
158 return result;
159}
160
161bool BaseControllerInterface::validate_parameter(const std::shared_ptr<ParameterInterface>& parameter) {
162 if (parameter->get_name() == "predicate_publishing_rate" || parameter->get_name() == "input_validity_period") {
163 auto value = parameter->get_parameter_value<double>();
164 if (value < 0.0 || value > std::numeric_limits<double>::max()) {
165 RCLCPP_ERROR(
166 get_node()->get_logger(), "Parameter value of parameter '%s' should be a positive finite number",
167 parameter->get_name().c_str());
168 return false;
169 }
170 }
171 return on_validate_parameter_callback(parameter);
172}
173
174bool BaseControllerInterface::on_validate_parameter_callback(const std::shared_ptr<ParameterInterface>&) {
175 return true;
176}
177
178void BaseControllerInterface::add_predicate(const std::string& predicate_name, bool predicate_value) {
179 add_predicate(predicate_name, [predicate_value]() { return predicate_value; });
180}
181
183 const std::string& predicate_name, const std::function<bool(void)>& predicate_function) {
184 if (predicate_name.empty()) {
185 RCLCPP_ERROR(get_node()->get_logger(), "Failed to add predicate: Provide a non empty string as a name.");
186 return;
187 }
188 if (predicates_.find(predicate_name) != predicates_.end()) {
189 RCLCPP_WARN(
190 get_node()->get_logger(), "Predicate with name '%s' already exists, overwriting.", predicate_name.c_str());
191 } else {
192 RCLCPP_DEBUG(get_node()->get_logger(), "Adding predicate '%s'.", predicate_name.c_str());
193 }
194 try {
195 this->predicates_.insert_or_assign(predicate_name, Predicate(predicate_function));
196 } catch (const std::exception& ex) {
197 RCLCPP_ERROR(
198 get_node()->get_logger(), "Failed to evaluate callback of predicate '%s', returning false: %s",
199 predicate_name.c_str(), ex.what());
200 }
201}
202
203bool BaseControllerInterface::get_predicate(const std::string& predicate_name) const {
204 auto predicate_it = predicates_.find(predicate_name);
205 if (predicate_it == predicates_.end()) {
206 RCLCPP_ERROR_THROTTLE(
207 get_node()->get_logger(), *get_node()->get_clock(), 1000,
208 "Failed to get predicate '%s': Predicate does not exists, returning false.", predicate_name.c_str());
209 return false;
210 }
211 try {
212 return predicate_it->second.get_value();
213 } catch (const std::exception& ex) {
214 RCLCPP_ERROR_THROTTLE(
215 get_node()->get_logger(), *get_node()->get_clock(), 1000,
216 "Failed to evaluate callback of predicate '%s', returning false: %s", predicate_name.c_str(), ex.what());
217 }
218 return false;
219}
220
221void BaseControllerInterface::set_predicate(const std::string& predicate_name, bool predicate_value) {
222 set_predicate(predicate_name, [predicate_value]() { return predicate_value; });
223}
224
226 const std::string& predicate_name, const std::function<bool(void)>& predicate_function) {
227 auto predicate_it = predicates_.find(predicate_name);
228 if (predicate_it == predicates_.end()) {
229 RCLCPP_ERROR_THROTTLE(
230 get_node()->get_logger(), *get_node()->get_clock(), 1000,
231 "Failed to set predicate '%s': Predicate does not exist.", predicate_name.c_str());
232 return;
233 }
234 predicate_it->second.set_predicate(predicate_function);
235 if (auto new_predicate = predicate_it->second.query(); new_predicate) {
236 publish_predicate(predicate_name, *new_predicate);
237 }
238}
239
240void BaseControllerInterface::add_trigger(const std::string& trigger_name) {
241 if (trigger_name.empty()) {
242 RCLCPP_ERROR(get_node()->get_logger(), "Failed to add trigger: Provide a non empty string as a name.");
243 return;
244 }
245 if (std::find(triggers_.cbegin(), triggers_.cend(), trigger_name) != triggers_.cend()) {
246 RCLCPP_ERROR(
247 get_node()->get_logger(), "Failed to add trigger: there is already a trigger with name '%s'.",
248 trigger_name.c_str());
249 return;
250 }
251 if (predicates_.find(trigger_name) != predicates_.end()) {
252 RCLCPP_ERROR(
253 get_node()->get_logger(), "Failed to add trigger: there is already a predicate with name '%s'.",
254 trigger_name.c_str());
255 return;
256 }
257 triggers_.push_back(trigger_name);
258 add_predicate(trigger_name, false);
259}
260
261void BaseControllerInterface::trigger(const std::string& trigger_name) {
262 if (std::find(triggers_.cbegin(), triggers_.cend(), trigger_name) == triggers_.cend()) {
263 RCLCPP_ERROR(
264 get_node()->get_logger(), "Failed to trigger: could not find trigger with name '%s'.", trigger_name.c_str());
265 return;
266 }
267 set_predicate(trigger_name, true);
268 // reset the trigger to be published on the next step
269 predicates_.at(trigger_name).set_predicate([]() { return false; });
270}
271
272modulo_interfaces::msg::Predicate
273BaseControllerInterface::get_predicate_message(const std::string& name, bool value) const {
274 modulo_interfaces::msg::Predicate message;
275 message.predicate = name;
276 message.value = value;
277 return message;
278}
279
280void BaseControllerInterface::publish_predicate(const std::string& predicate_name, bool value) const {
281 auto message(predicate_message_);
282 message.predicates.push_back(get_predicate_message(predicate_name, value));
283 predicate_publisher_->publish(message);
284}
285
286void BaseControllerInterface::publish_predicates() {
287 auto message(predicate_message_);
288 for (auto predicate_it = predicates_.begin(); predicate_it != predicates_.end(); ++predicate_it) {
289 if (auto new_predicate = predicate_it->second.query(); new_predicate) {
290 message.predicates.push_back(get_predicate_message(predicate_it->first, *new_predicate));
291 }
292 }
293 if (message.predicates.size()) {
294 predicate_publisher_->publish(message);
295 }
296}
297
298std::string BaseControllerInterface::validate_and_declare_signal(
299 const std::string& signal_name, const std::string& type, const std::string& default_topic, bool fixed_topic) {
300 auto parsed_signal_name = modulo_utils::parsing::parse_topic_name(signal_name);
301 if (parsed_signal_name.empty()) {
302 RCLCPP_WARN(
303 get_node()->get_logger(),
304 "The parsed signal name for %s '%s' is empty. Provide a string with valid characters for the signal name "
305 "([a-zA-Z0-9_]).",
306 type.c_str(), signal_name.c_str());
307 return "";
308 }
309 if (signal_name != parsed_signal_name) {
310 RCLCPP_WARN(
311 get_node()->get_logger(),
312 "The parsed signal name for %s '%s' is '%s'. Use the parsed signal name to refer to this %s and its topic "
313 "parameter.",
314 type.c_str(), signal_name.c_str(), parsed_signal_name.c_str(), type.c_str());
315 }
316 if (inputs_.find(parsed_signal_name) != inputs_.end()) {
317 RCLCPP_WARN(get_node()->get_logger(), "Signal '%s' already exists as input.", parsed_signal_name.c_str());
318 return "";
319 }
320 if (outputs_.find(parsed_signal_name) != outputs_.end()) {
321 RCLCPP_WARN(get_node()->get_logger(), "Signal '%s' already exists as output", parsed_signal_name.c_str());
322 return "";
323 }
324 auto topic = default_topic.empty() ? "~/" + parsed_signal_name : default_topic;
325 auto parameter_name = parsed_signal_name + "_topic";
326 if (get_node()->has_parameter(parameter_name) && get_parameter(parameter_name)->is_empty()) {
327 set_parameter_value<std::string>(parameter_name, topic);
328 } else {
330 parameter_name, topic, "Signal topic name of " + type + " '" + parsed_signal_name + "'", fixed_topic);
331 }
332 RCLCPP_DEBUG(
333 get_node()->get_logger(), "Declared %s '%s' and parameter '%s' with value '%s'.", type.c_str(),
334 parsed_signal_name.c_str(), parameter_name.c_str(), topic.c_str());
335 return parsed_signal_name;
336}
337
338void BaseControllerInterface::create_input(
339 const ControllerInput& input, const std::string& name, const std::string& topic_name) {
340 auto parsed_name = validate_and_declare_signal(name, "input", topic_name);
341 if (!parsed_name.empty()) {
342 inputs_.insert_or_assign(parsed_name, input);
343 }
344}
345
346void BaseControllerInterface::add_inputs() {
347 for (auto& [name, input] : inputs_) {
348 try {
349 auto topic = get_parameter_value<std::string>(name + "_topic");
350 std::visit(
351 overloaded{
352 [&](const realtime_tools::RealtimeBuffer<std::shared_ptr<EncodedState>>&) {
353 subscriptions_.push_back(create_subscription<EncodedState>(name, topic));
354 },
355 [&](const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Bool>>&) {
356 subscriptions_.push_back(create_subscription<std_msgs::msg::Bool>(name, topic));
357 },
358 [&](const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64>>&) {
359 subscriptions_.push_back(create_subscription<std_msgs::msg::Float64>(name, topic));
360 },
361 [&](const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Float64MultiArray>>&) {
362 subscriptions_.push_back(create_subscription<std_msgs::msg::Float64MultiArray>(name, topic));
363 },
364 [&](const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::Int32>>&) {
365 subscriptions_.push_back(create_subscription<std_msgs::msg::Int32>(name, topic));
366 },
367 [&](const realtime_tools::RealtimeBuffer<std::shared_ptr<std_msgs::msg::String>>&) {
368 subscriptions_.push_back(create_subscription<std_msgs::msg::String>(name, topic));
369 },
370 [&](const std::any&) {
371 custom_input_configuration_callables_.at(name)(name, topic);
372 }},
373 input.buffer);
374 } catch (const std::exception& ex) {
375 RCLCPP_ERROR(get_node()->get_logger(), "Failed to add input '%s': %s", name.c_str(), ex.what());
376 }
377 }
378}
379
380void BaseControllerInterface::create_output(
381 const PublisherVariant& publishers, const std::string& name, const std::string& topic_name) {
382 auto parsed_name = validate_and_declare_signal(name, "output", topic_name);
383 if (!parsed_name.empty()) {
384 outputs_.insert_or_assign(parsed_name, publishers);
385 }
386}
387
388void BaseControllerInterface::add_outputs() {
389 for (auto& [name, publishers] : outputs_) {
390 try {
391 auto topic = get_parameter_value<std::string>(name + "_topic");
392 std::visit(
393 overloaded{
394 [&](EncodedStatePublishers& pub) {
395 std::get<1>(pub) = get_node()->create_publisher<EncodedState>(topic, qos_);
396 std::get<2>(pub) = std::make_shared<realtime_tools::RealtimePublisher<EncodedState>>(std::get<1>(pub));
397 },
398 [&](BoolPublishers& pub) {
399 pub.first = get_node()->create_publisher<std_msgs::msg::Bool>(topic, qos_);
400 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Bool>>(pub.first);
401 },
402 [&](DoublePublishers& pub) {
403 pub.first = get_node()->create_publisher<std_msgs::msg::Float64>(topic, qos_);
404 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Float64>>(pub.first);
405 },
406 [&](DoubleVecPublishers& pub) {
407 pub.first = get_node()->create_publisher<std_msgs::msg::Float64MultiArray>(topic, qos_);
408 pub.second =
409 std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Float64MultiArray>>(pub.first);
410 },
411 [&](IntPublishers& pub) {
412 pub.first = get_node()->create_publisher<std_msgs::msg::Int32>(topic, qos_);
413 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::Int32>>(pub.first);
414 },
415 [&](StringPublishers& pub) {
416 pub.first = get_node()->create_publisher<std_msgs::msg::String>(topic, qos_);
417 pub.second = std::make_shared<realtime_tools::RealtimePublisher<std_msgs::msg::String>>(pub.first);
418 },
419 [&](CustomPublishers& pub) {
420 custom_output_configuration_callables_.at(name)(pub, name);
421 }},
422 publishers);
423 } catch (const std::bad_any_cast& ex) {
424 RCLCPP_ERROR(get_node()->get_logger(), "Failed to add custom output '%s': %s", name.c_str(), ex.what());
425 } catch (const std::exception& ex) {
426 RCLCPP_ERROR(get_node()->get_logger(), "Failed to add output '%s': %s", name.c_str(), ex.what());
427 }
428 }
429}
430
431void BaseControllerInterface::set_input_validity_period(double input_validity_period) {
432 input_validity_period_ = input_validity_period;
433}
434
435bool BaseControllerInterface::check_input_valid(const std::string& name) const {
436 if (inputs_.find(name) == inputs_.end()) {
437 RCLCPP_WARN_THROTTLE(
438 get_node()->get_logger(), *get_node()->get_clock(), 1000, "Could not find input '%s'", name.c_str());
439 return false;
440 }
441 if (static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(
442 std::chrono::steady_clock::now() - inputs_.at(name).timestamp)
443 .count())
444 / 1e9
445 >= input_validity_period_) {
446 return false;
447 }
448 return true;
449}
450
451std::string
452BaseControllerInterface::validate_service_name(const std::string& service_name, const std::string& type) const {
453 std::string parsed_service_name = modulo_utils::parsing::parse_topic_name(service_name);
454 if (parsed_service_name.empty()) {
455 RCLCPP_WARN(
456 get_node()->get_logger(),
457 "The parsed service name for %s service '%s' is empty. Provide a string with valid characters for the service "
458 "name "
459 "([a-zA-Z0-9_]).",
460 type.c_str(), service_name.c_str());
461 return "";
462 }
463 if (service_name != parsed_service_name) {
464 RCLCPP_WARN(
465 get_node()->get_logger(),
466 "The parsed name for '%s' service '%s' is '%s'. Use the parsed name to refer to this service.", type.c_str(),
467 service_name.c_str(), parsed_service_name.c_str());
468 }
469 if (empty_services_.find(parsed_service_name) != empty_services_.cend()) {
470 RCLCPP_WARN(
471 get_node()->get_logger(), "Service with name '%s' already exists as empty service.",
472 parsed_service_name.c_str());
473 return "";
474 }
475 if (string_services_.find(parsed_service_name) != string_services_.cend()) {
476 RCLCPP_WARN(
477 get_node()->get_logger(), "Service with name '%s' already exists as string service.",
478 parsed_service_name.c_str());
479 return "";
480 }
481 RCLCPP_DEBUG(get_node()->get_logger(), "Adding %s service '%s'.", type.c_str(), parsed_service_name.c_str());
482 return parsed_service_name;
483}
484
486 const std::string& service_name, const std::function<ControllerServiceResponse(void)>& callback) {
487 auto parsed_service_name = validate_service_name(service_name, "empty");
488 if (!parsed_service_name.empty()) {
489 try {
490 auto service = get_node()->create_service<modulo_interfaces::srv::EmptyTrigger>(
491 "~/" + parsed_service_name,
492 [this, callback](
493 const std::shared_ptr<modulo_interfaces::srv::EmptyTrigger::Request>,
494 std::shared_ptr<modulo_interfaces::srv::EmptyTrigger::Response> response) {
495 try {
496 if (this->command_mutex_.try_lock_for(100ms)) {
497 auto callback_response = callback();
498 this->command_mutex_.unlock();
499 response->success = callback_response.success;
500 response->message = callback_response.message;
501 } else {
502 response->success = false;
503 response->message = "Unable to acquire lock for command interface within 100ms";
504 }
505 } catch (const std::exception& ex) {
506 response->success = false;
507 response->message = ex.what();
508 }
509 },
510 qos_);
511 empty_services_.insert_or_assign(parsed_service_name, service);
512 } catch (const std::exception& ex) {
513 RCLCPP_ERROR(get_node()->get_logger(), "Failed to add service '%s': %s", parsed_service_name.c_str(), ex.what());
514 }
515 }
516}
517
519 const std::string& service_name,
520 const std::function<ControllerServiceResponse(const std::string& string)>& callback) {
521 auto parsed_service_name = validate_service_name(service_name, "string");
522 if (!parsed_service_name.empty()) {
523 try {
524 auto service = get_node()->create_service<modulo_interfaces::srv::StringTrigger>(
525 "~/" + parsed_service_name,
526 [this, callback](
527 const std::shared_ptr<modulo_interfaces::srv::StringTrigger::Request> request,
528 std::shared_ptr<modulo_interfaces::srv::StringTrigger::Response> response) {
529 try {
530 if (this->command_mutex_.try_lock_for(100ms)) {
531 auto callback_response = callback(request->payload);
532 this->command_mutex_.unlock();
533 response->success = callback_response.success;
534 response->message = callback_response.message;
535 } else {
536 response->success = false;
537 response->message = "Unable to acquire lock for command interface within 100ms";
538 }
539 } catch (const std::exception& ex) {
540 response->success = false;
541 response->message = ex.what();
542 }
543 },
544 qos_);
545 string_services_.insert_or_assign(parsed_service_name, service);
546 } catch (const std::exception& ex) {
547 RCLCPP_ERROR(get_node()->get_logger(), "Failed to add service '%s': %s", parsed_service_name.c_str(), ex.what());
548 }
549 }
550}
551
553 return qos_;
554}
555
556void BaseControllerInterface::set_qos(const rclcpp::QoS& qos) {
557 qos_ = qos;
558}
559
561 return get_lifecycle_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE;
562}
563
565 return command_mutex_;
566}
567
568}// namespace modulo_controllers
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 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 > &parameter, 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 > &parameter)
Parameter validation function to be redefined by derived controller classes.
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 > &parameter)
Write a ROS Parameter from a ParameterInterface pointer.
rclcpp::ParameterType get_ros_parameter_type(const state_representation::ParameterType &parameter_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 > &parameter)
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 > &parameter)
Update the parameter value of a ParameterInterface from a ROS Parameter object only if the two parame...
Modulo Core.
Response structure to be returned by controller services.