Modulo 5.0.0
Loading...
Searching...
No Matches
LifecycleComponent.cpp
1#include "modulo_components/LifecycleComponent.hpp"
2
3using namespace modulo_core::communication;
4using namespace rclcpp_lifecycle;
5
6namespace modulo_components {
7
8LifecycleComponent::LifecycleComponent(const rclcpp::NodeOptions& node_options, const std::string& fallback_name)
9 : LifecycleNode(modulo_utils::parsing::parse_node_name(node_options, fallback_name), node_options),
10 ComponentInterface(std::make_shared<rclcpp::node_interfaces::NodeInterfaces<ALL_RCLCPP_NODE_INTERFACES>>(
11 LifecycleNode::get_node_base_interface(), LifecycleNode::get_node_clock_interface(),
12 LifecycleNode::get_node_graph_interface(), LifecycleNode::get_node_logging_interface(),
13 LifecycleNode::get_node_parameters_interface(), LifecycleNode::get_node_services_interface(),
14 LifecycleNode::get_node_time_source_interface(), LifecycleNode::get_node_timers_interface(),
15 LifecycleNode::get_node_topics_interface(), LifecycleNode::get_node_type_descriptions_interface(),
16 LifecycleNode::get_node_waitables_interface())),
17 has_error_(false) {}
18
19std::shared_ptr<state_representation::ParameterInterface>
20LifecycleComponent::get_parameter(const std::string& name) const {
22}
23
24void LifecycleComponent::step() {
25 try {
26 if (this->get_lifecycle_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
27 this->evaluate_periodic_callbacks();
28 this->on_step_callback();
29 this->publish_outputs();
30 }
31 this->publish_predicates();
32 } catch (const std::exception& ex) {
33 RCLCPP_ERROR_STREAM(this->get_logger(), "Failed to execute step function: " << ex.what());
34 this->raise_error();
35 }
36}
37
38node_interfaces::LifecycleNodeInterface::CallbackReturn LifecycleComponent::on_configure(const State& previous_state) {
39 RCLCPP_DEBUG(this->get_logger(), "on_configure called from previous state %s", previous_state.label().c_str());
40 if (previous_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED) {
41 RCLCPP_WARN(get_logger(), "Invalid transition 'configure' from state %s.", previous_state.label().c_str());
42 return node_interfaces::LifecycleNodeInterface::CallbackReturn::FAILURE;
43 }
44 if (!this->handle_configure()) {
45 RCLCPP_WARN(get_logger(), "Configuration failed! Reverting to the unconfigured state.");
46 // perform cleanup actions to ensure the component is unconfigured
47 if (this->handle_cleanup()) {
48 return node_interfaces::LifecycleNodeInterface::CallbackReturn::FAILURE;
49 } else {
51 get_logger(),
52 "Could not revert to the unconfigured state! Entering into the error processing transition state.");
53 return node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
54 }
55 }
56 return node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
57}
58
59bool LifecycleComponent::handle_configure() {
60 bool result;
61 try {
63 } catch (const std::exception& ex) {
65 return false;
66 }
67 return result && this->configure_outputs();
68}
69
71 return true;
72}
73
74node_interfaces::LifecycleNodeInterface::CallbackReturn LifecycleComponent::on_cleanup(const State& previous_state) {
75 RCLCPP_DEBUG(this->get_logger(), "on_cleanup called from previous state %s", previous_state.label().c_str());
76 if (previous_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) {
77 RCLCPP_WARN(get_logger(), "Invalid transition 'cleanup' from state %s.", previous_state.label().c_str());
78 return node_interfaces::LifecycleNodeInterface::CallbackReturn::FAILURE;
79 }
80 if (!this->handle_cleanup()) {
81 RCLCPP_ERROR(get_logger(), "Cleanup failed! Entering into the error processing transition state.");
82 return node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
83 }
84 return node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
85}
86
87bool LifecycleComponent::handle_cleanup() {
88 try {
89 return this->on_cleanup_callback();
90 } catch (const std::exception& ex) {
92 return false;
93 }
94}
95
97 return true;
98}
99
100node_interfaces::LifecycleNodeInterface::CallbackReturn LifecycleComponent::on_activate(const State& previous_state) {
101 RCLCPP_DEBUG(this->get_logger(), "on_activate called from previous state %s", previous_state.label().c_str());
102 if (previous_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) {
103 RCLCPP_WARN(get_logger(), "Invalid transition 'activate' from state %s.", previous_state.label().c_str());
104 return node_interfaces::LifecycleNodeInterface::CallbackReturn::FAILURE;
105 }
106 if (!this->handle_activate()) {
107 RCLCPP_WARN(get_logger(), "Activation failed! Reverting to the inactive state.");
108 // perform deactivation actions to ensure the component is inactive
109 if (this->handle_deactivate()) {
110 return node_interfaces::LifecycleNodeInterface::CallbackReturn::FAILURE;
111 } else {
113 get_logger(), "Could not revert to the inactive state! Entering into the error processing transition state.");
114 return node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
115 }
116 }
117 return node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
118}
119
120bool LifecycleComponent::handle_activate() {
121 bool result;
122 try {
124 } catch (const std::exception& ex) {
126 return false;
127 }
128 return result && this->activate_outputs();
129}
130
132 return true;
133}
134
135node_interfaces::LifecycleNodeInterface::CallbackReturn LifecycleComponent::on_deactivate(const State& previous_state) {
136 RCLCPP_DEBUG(this->get_logger(), "on_deactivate called from previous state %s", previous_state.label().c_str());
137 if (previous_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
138 RCLCPP_WARN(get_logger(), "Invalid transition 'deactivate' from state %s.", previous_state.label().c_str());
139 return node_interfaces::LifecycleNodeInterface::CallbackReturn::FAILURE;
140 }
141 if (!this->handle_deactivate()) {
142 RCLCPP_ERROR(get_logger(), "Deactivation failed! Entering into the error processing transition state.");
143 return node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
144 }
145 return node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
146}
147
148bool LifecycleComponent::handle_deactivate() {
149 auto result = this->deactivate_outputs();
150 try {
151 return result && this->on_deactivate_callback();
152 } catch (const std::exception& ex) {
154 return false;
155 }
156}
157
159 return true;
160}
161
162node_interfaces::LifecycleNodeInterface::CallbackReturn LifecycleComponent::on_shutdown(const State& previous_state) {
163 RCLCPP_DEBUG(this->get_logger(), "on_shutdown called from previous state %s", previous_state.label().c_str());
164 if (!this->has_error_) {
165 switch (previous_state.id()) {
166 case lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED:
167 return node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
168 case lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE:
169 if (!this->handle_deactivate()) {
170 RCLCPP_DEBUG(get_logger(), "Shutdown failed during intermediate deactivation!");
171 break;
172 }
173 [[fallthrough]];
174 case lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE:
175 if (!this->handle_cleanup()) {
176 RCLCPP_DEBUG(get_logger(), "Shutdown failed during intermediate cleanup!");
177 break;
178 }
179 [[fallthrough]];
180 case lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED:
181 if (!this->handle_shutdown()) {
182 break;
183 }
184 this->finalize_interfaces();
185 return node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
186 default:
187 RCLCPP_WARN(get_logger(), "Invalid transition 'shutdown' from state %s.", previous_state.label().c_str());
188 break;
189 }
190 }
191 RCLCPP_ERROR(get_logger(), "Entering into the error processing transition state.");
192 return node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
193}
194
195bool LifecycleComponent::handle_shutdown() {
196 bool result;
197 try {
199 } catch (const std::exception& ex) {
201 return false;
202 }
203 return result;
204}
205
207 return true;
208}
209
210node_interfaces::LifecycleNodeInterface::CallbackReturn LifecycleComponent::on_error(const State& previous_state) {
211 RCLCPP_DEBUG(this->get_logger(), "on_error called from previous state %s", previous_state.label().c_str());
212 bool error_handled;
213 try {
214 error_handled = this->handle_error();
215 } catch (const std::exception& ex) {
216 RCLCPP_DEBUG(this->get_logger(), "Exception caught during on_error handling: %s", ex.what());
217 error_handled = false;
218 }
219 if (!error_handled) {
220 RCLCPP_ERROR(get_logger(), "Error processing failed! Entering into the finalized state.");
221 this->finalize_interfaces();
222 return node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
223 }
224 this->has_error_ = false;
225 return node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
226}
227
228bool LifecycleComponent::handle_error() {
229 return this->on_error_callback();
230}
231
233 return false;
234}
235
236bool LifecycleComponent::configure_outputs() {
237 bool success = true;
238 for (auto& [name, interface] : this->outputs_) {
239 try {
240 auto topic_name = this->get_parameter_value<std::string>(name + "_topic");
242 this->get_logger(), "Configuring output '" << name << "' with topic name '" << topic_name << "'.");
243 auto message_pair = interface->get_message_pair();
244 switch (message_pair->get_type()) {
245 case MessageType::BOOL: {
247 interface = std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::Bool>, std_msgs::msg::Bool>>(
248 PublisherType::LIFECYCLE_PUBLISHER, publisher)
249 ->create_publisher_interface(message_pair);
250 break;
251 }
252 case MessageType::FLOAT64: {
253 auto publisher = this->create_publisher<std_msgs::msg::Float64>(topic_name, this->get_qos());
254 interface =
255 std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::Float64>, std_msgs::msg::Float64>>(
256 PublisherType::LIFECYCLE_PUBLISHER, publisher)
257 ->create_publisher_interface(message_pair);
258 break;
259 }
260 case MessageType::FLOAT64_MULTI_ARRAY: {
261 auto publisher = this->create_publisher<std_msgs::msg::Float64MultiArray>(topic_name, this->get_qos());
262 interface = std::make_shared<PublisherHandler<
263 LifecyclePublisher<std_msgs::msg::Float64MultiArray>, std_msgs::msg::Float64MultiArray>>(
264 PublisherType::LIFECYCLE_PUBLISHER, publisher)
265 ->create_publisher_interface(message_pair);
266 break;
267 }
268 case MessageType::INT32: {
269 auto publisher = this->create_publisher<std_msgs::msg::Int32>(topic_name, this->get_qos());
270 interface =
271 std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::Int32>, std_msgs::msg::Int32>>(
272 PublisherType::LIFECYCLE_PUBLISHER, publisher)
273 ->create_publisher_interface(message_pair);
274 break;
275 }
276 case MessageType::STRING: {
277 auto publisher = this->create_publisher<std_msgs::msg::String>(topic_name, this->get_qos());
278 interface =
279 std::make_shared<PublisherHandler<LifecyclePublisher<std_msgs::msg::String>, std_msgs::msg::String>>(
280 PublisherType::LIFECYCLE_PUBLISHER, publisher)
281 ->create_publisher_interface(message_pair);
282 break;
283 }
284 case MessageType::ENCODED_STATE: {
285 auto publisher = this->create_publisher<modulo_core::EncodedState>(topic_name, this->get_qos());
286 interface = std::make_shared<
287 PublisherHandler<LifecyclePublisher<modulo_core::EncodedState>, modulo_core::EncodedState>>(
288 PublisherType::LIFECYCLE_PUBLISHER, publisher)
289 ->create_publisher_interface(message_pair);
290 break;
291 }
292 case MessageType::CUSTOM_MESSAGE: {
293 interface = this->custom_output_configuration_callables_.at(name)(topic_name);
294 break;
295 }
296 }
297 } catch (const modulo_core::exceptions::CoreException& ex) {
298 success = false;
299 RCLCPP_ERROR_STREAM(this->get_logger(), "Failed to configure output '" << name << "': " << ex.what());
300 }
301 }
302 return success;
303}
304
305bool LifecycleComponent::activate_outputs() {
306 bool success = true;
307 for (auto const& [name, interface] : this->outputs_) {
308 try {
309 interface->activate();
310 } catch (const modulo_core::exceptions::CoreException& ex) {
311 success = false;
312 RCLCPP_ERROR_STREAM(this->get_logger(), "Failed to activate output '" << name << "': " << ex.what());
313 }
314 }
315 RCLCPP_DEBUG(this->get_logger(), "All outputs activated.");
316 return success;
317}
318
319bool LifecycleComponent::deactivate_outputs() {
320 bool success = true;
321 for (auto const& [name, interface] : this->outputs_) {
322 try {
323 interface->deactivate();
324 } catch (const modulo_core::exceptions::CoreException& ex) {
325 success = false;
326 RCLCPP_ERROR_STREAM(this->get_logger(), "Failed to deactivate output '" << name << "': " << ex.what());
327 }
328 }
329 RCLCPP_DEBUG(this->get_logger(), "All outputs deactivated.");
330 return success;
331}
332
333rclcpp_lifecycle::State LifecycleComponent::get_lifecycle_state() const {
334 return this->get_current_state();
335}
336
337void LifecycleComponent::raise_error() {
338 ComponentInterface::raise_error();
339 this->has_error_ = true;
340 if (get_current_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED) {
341 this->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_UNCONFIGURED_SHUTDOWN);
342 } else if (get_current_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) {
343 this->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_INACTIVE_SHUTDOWN);
344 } else if (get_current_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
345 this->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVE_SHUTDOWN);
346 }
347}
348}// namespace modulo_components
Base interface class for modulo components to wrap a ROS Node with custom behaviour.
std::shared_ptr< state_representation::ParameterInterface > get_parameter(const std::string &name) const
Get a parameter by name.
T get_parameter_value(const std::string &name) const
Get a parameter value by name.
virtual void on_step_callback()
Steps to execute periodically. To be redefined by derived Component classes.
rclcpp::QoS get_qos() const
Getter of the Quality of Service attribute.
void raise_error() override
Trigger the shutdown and error transitions.
virtual bool on_shutdown_callback()
Steps to execute when shutting down the component.
virtual bool on_deactivate_callback()
Steps to execute when deactivating the component.
std::shared_ptr< state_representation::ParameterInterface > get_parameter(const std::string &name) const
Get a parameter by name.
virtual bool on_activate_callback()
Steps to execute when activating the component.
virtual bool on_cleanup_callback()
Steps to execute when cleaning up the component.
LifecycleComponent(const rclcpp::NodeOptions &node_options, const std::string &fallback_name="LifecycleComponent")
Constructor from node options.
virtual bool on_error_callback()
Steps to execute when handling errors.
virtual bool on_configure_callback()
Steps to execute when configuring the component.
rclcpp_lifecycle::State get_lifecycle_state() const
Get the current lifecycle state of the component.
The PublisherHandler handles different types of ROS publishers to activate, deactivate and publish da...
A base class for all core exceptions.
Modulo components.
Definition Component.hpp:9
Modulo Core communication module for handling messages on publication and subscription interfaces.
Modulo Core.