16 #include "nav2_lifecycle_manager/lifecycle_manager.hpp"
23 #include "rclcpp/callback_group.hpp"
24 #include "rclcpp/context.hpp"
25 #include "rclcpp/duration.hpp"
26 #include "rclcpp/executors/single_threaded_executor.hpp"
27 #include "rclcpp/logging.hpp"
28 #include "rclcpp/node.hpp"
29 #include "rclcpp/node_options.hpp"
30 #include "rclcpp/time.hpp"
31 #include "rclcpp/utilities.hpp"
32 #include "nav2_ros_common/interface_factories.hpp"
34 using namespace std::chrono_literals;
35 using namespace std::placeholders;
37 using lifecycle_msgs::msg::Transition;
38 using lifecycle_msgs::msg::State;
41 namespace nav2_lifecycle_manager
44 LifecycleManager::LifecycleManager(
const rclcpp::NodeOptions & options)
45 : Node(
"lifecycle_manager", options), diagnostics_updater_(this)
47 RCLCPP_INFO(get_logger(),
"Creating");
51 node_names_ = nav2::declare_or_get_parameter<std::vector<std::string>>(node,
"node_names");
52 autostart_ = nav2::declare_or_get_parameter(node,
"autostart",
false);
53 double bond_timeout_s = nav2::declare_or_get_parameter(node,
"bond_timeout", 4.0);
54 double service_timeout_s = nav2::declare_or_get_parameter(node,
"service_timeout", 5.0);
55 double respawn_timeout_s = nav2::declare_or_get_parameter(
56 node,
"bond_respawn_max_duration", 10.0);
57 attempt_respawn_reconnection_ = nav2::declare_or_get_parameter(
58 node,
"attempt_respawn_reconnection",
true);
59 bond_heartbeat_period_ = nav2::declare_or_get_parameter(node,
"bond_heartbeat_period", 0.25);
63 bond_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(
64 std::chrono::duration<double>(bond_timeout_s));
65 service_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(
66 std::chrono::duration<double>(service_timeout_s));
67 bond_respawn_max_duration_ = rclcpp::Duration::from_seconds(respawn_timeout_s);
69 callback_group_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive,
false);
71 transition_state_map_[Transition::TRANSITION_CONFIGURE] = State::PRIMARY_STATE_INACTIVE;
72 transition_state_map_[Transition::TRANSITION_CLEANUP] = State::PRIMARY_STATE_UNCONFIGURED;
73 transition_state_map_[Transition::TRANSITION_ACTIVATE] = State::PRIMARY_STATE_ACTIVE;
74 transition_state_map_[Transition::TRANSITION_DEACTIVATE] = State::PRIMARY_STATE_INACTIVE;
75 transition_state_map_[Transition::TRANSITION_UNCONFIGURED_SHUTDOWN] =
76 State::PRIMARY_STATE_FINALIZED;
78 transition_label_map_[Transition::TRANSITION_CONFIGURE] = std::string(
"Configuring ");
79 transition_label_map_[Transition::TRANSITION_CLEANUP] = std::string(
"Cleaning up ");
80 transition_label_map_[Transition::TRANSITION_ACTIVATE] = std::string(
"Activating ");
81 transition_label_map_[Transition::TRANSITION_DEACTIVATE] = std::string(
"Deactivating ");
82 transition_label_map_[Transition::TRANSITION_UNCONFIGURED_SHUTDOWN] =
83 std::string(
"Shutting down ");
85 init_timer_ = nav2::create_timer(
89 init_timer_->cancel();
94 init_timer_ = nav2::create_timer(
98 init_timer_->cancel();
103 auto executor = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
104 executor->add_callback_group(callback_group_, get_node_base_interface());
105 service_thread_ = std::make_unique<nav2::NodeThread>(executor);
107 diagnostics_updater_.setHardwareID(
"Nav2");
113 RCLCPP_INFO(get_logger(),
"Destroying %s", get_name());
114 service_thread_.reset();
119 const std::shared_ptr<rmw_request_id_t>,
120 const std::shared_ptr<ManageLifecycleNodes::Request> request,
121 std::shared_ptr<ManageLifecycleNodes::Response> response)
123 switch (request->command) {
124 case ManageLifecycleNodes::Request::STARTUP:
127 case ManageLifecycleNodes::Request::CONFIGURE:
130 case ManageLifecycleNodes::Request::CLEANUP:
133 case ManageLifecycleNodes::Request::RESET:
134 response->success =
reset();
136 case ManageLifecycleNodes::Request::SHUTDOWN:
139 case ManageLifecycleNodes::Request::PAUSE:
140 response->success =
pause();
142 case ManageLifecycleNodes::Request::RESUME:
143 response->success =
resume();
151 managed_nodes_state_ = state;
158 return managed_nodes_state_ == NodeState::ACTIVE;
164 if (is_active_pub_ && is_active_pub_->is_activated()) {
165 auto message = std::make_unique<std_msgs::msg::Bool>();
167 is_active_pub_->publish(std::move(
message));
173 const std::shared_ptr<rmw_request_id_t>,
174 const std::shared_ptr<std_srvs::srv::Trigger::Request>,
175 std::shared_ptr<std_srvs::srv::Trigger::Response> response)
183 unsigned char error_level;
185 switch (managed_nodes_state_) {
186 case NodeState::ACTIVE:
187 error_level = diagnostic_msgs::msg::DiagnosticStatus::OK;
188 message =
"Managed nodes are active";
190 case NodeState::INACTIVE:
191 error_level = diagnostic_msgs::msg::DiagnosticStatus::OK;
192 message =
"Managed nodes are inactive";
194 case NodeState::UNCONFIGURED:
195 error_level = diagnostic_msgs::msg::DiagnosticStatus::OK;
196 message =
"Managed nodes are unconfigured";
198 case NodeState::FINALIZED:
199 error_level = diagnostic_msgs::msg::DiagnosticStatus::WARN;
200 message =
"Managed nodes have been shut down";
203 error_level = diagnostic_msgs::msg::DiagnosticStatus::ERROR;
204 message =
"An error has occurred during a node state transition";
207 stat.summary(error_level,
message);
213 message(
"Creating and initializing lifecycle service clients");
214 for (
auto & node_name : node_names_) {
215 node_map_[node_name] =
216 std::make_shared<LifecycleServiceClient>(node_name, shared_from_this());
223 message(
"Creating and initializing lifecycle service servers");
227 manager_srv_ = nav2::interfaces::create_service<ManageLifecycleNodes>(
229 get_name() + std::string(
"/manage_nodes"),
233 is_active_srv_ = nav2::interfaces::create_service<std_srvs::srv::Trigger>(
235 get_name() + std::string(
"/is_active"),
243 message(
"Creating and initializing lifecycle publishers");
245 is_active_pub_ = nav2::interfaces::create_publisher<std_msgs::msg::Bool>(
247 get_name() + std::string(
"/managed_nodes_activated"),
250 is_active_pub_->on_activate();
258 message(
"Destroying lifecycle service clients");
259 for (
auto & kv : node_map_) {
267 message(
"Destroying lifecycle publishers");
268 if (is_active_pub_) {
269 is_active_pub_->on_deactivate();
270 is_active_pub_.reset();
277 const double timeout_ns =
278 std::chrono::duration_cast<std::chrono::nanoseconds>(bond_timeout_).count();
279 const double timeout_s = timeout_ns / 1e9;
281 if (bond_map_.find(node_name) == bond_map_.end() && bond_timeout_.count() > 0.0) {
282 bond_map_[node_name] =
283 std::make_shared<bond::Bond>(
"bond", node_name, shared_from_this());
284 bond_map_[node_name]->setHeartbeatTimeout(timeout_s);
285 bond_map_[node_name]->setHeartbeatPeriod(bond_heartbeat_period_);
286 bond_map_[node_name]->start();
288 !bond_map_[node_name]->waitUntilFormed(
289 rclcpp::Duration(rclcpp::Duration::from_nanoseconds(timeout_ns / 2))))
293 "Server %s was unable to be reached after %0.2fs by bond. "
294 "This server may be misconfigured.",
295 node_name.c_str(), timeout_s);
298 RCLCPP_INFO(get_logger(),
"Server %s connected with bond.", node_name.c_str());
307 message(transition_label_map_[transition] + node_name);
309 if (!node_map_[node_name]->change_state(
310 transition, std::chrono::milliseconds(-1),
312 !(node_map_[node_name]->get_state(service_timeout_) == transition_state_map_[transition]))
314 RCLCPP_ERROR(get_logger(),
"Failed to change state for node: %s", node_name.c_str());
318 if (transition == Transition::TRANSITION_ACTIVATE) {
320 }
else if (transition == Transition::TRANSITION_DEACTIVATE) {
321 bond_map_.erase(node_name);
331 if (transition == Transition::TRANSITION_CONFIGURE ||
332 transition == Transition::TRANSITION_ACTIVATE)
334 for (
auto & node_name : node_names_) {
339 }
catch (
const std::runtime_error & e) {
342 "Failed to change state for node: %s. Exception: %s.", node_name.c_str(), e.what());
347 std::vector<std::string>::reverse_iterator rit;
348 for (rit = node_names_.rbegin(); rit != node_names_.rend(); ++rit) {
353 }
catch (
const std::runtime_error & e) {
356 "Failed to change state for node: %s. Exception: %s.", (*rit).c_str(), e.what());
367 message(
"Deactivate, cleanup, and shutdown nodes");
377 message(
"Starting managed nodes bringup...");
381 RCLCPP_ERROR(get_logger(),
"Failed to bring up all requested nodes. Aborting bringup.");
385 message(
"Managed nodes are active");
394 message(
"Configuring managed nodes...");
396 RCLCPP_ERROR(get_logger(),
"Failed to configure all requested nodes. Aborting bringup.");
400 message(
"Managed nodes are now configured");
408 message(
"Cleaning up managed nodes...");
410 RCLCPP_ERROR(get_logger(),
"Failed to cleanup all requested nodes. Aborting cleanup.");
414 message(
"Managed nodes have been cleaned up");
424 message(
"Shutting down managed nodes...");
428 message(
"Managed nodes have been shut down");
437 message(
"Resetting managed nodes...");
443 RCLCPP_ERROR(get_logger(),
"Failed to reset nodes: aborting reset");
449 message(
"Managed nodes have been reset");
459 message(
"Pausing managed nodes...");
461 RCLCPP_ERROR(get_logger(),
"Failed to pause nodes: aborting pause");
466 message(
"Managed nodes have been paused");
474 message(
"Resuming managed nodes...");
476 RCLCPP_ERROR(get_logger(),
"Failed to resume nodes: aborting resume");
481 message(
"Managed nodes are active");
490 if (bond_timeout_.count() <= 0) {
494 message(
"Creating bond timer...");
495 bond_timer_ = nav2::create_timer(
506 message(
"Terminating bond timer...");
507 bond_timer_->cancel();
516 get_logger(),
"Running Nav2 LifecycleManager rcl preshutdown (%s)",
525 service_thread_.reset();
534 rclcpp::Context::SharedPtr context = get_node_base_interface()->get_context();
536 context->add_pre_shutdown_callback(
544 if (!
isActive() || !rclcpp::ok() || bond_map_.empty()) {
548 for (
auto & node_name : node_names_) {
553 if (bond_map_[node_name]->isBroken()) {
556 "Have not received a heartbeat from " + node_name +
"."));
561 "CRITICAL FAILURE: SERVER %s IS DOWN after not receiving a heartbeat for %i ms."
562 " Shutting down related nodes.",
563 node_name.c_str(),
static_cast<int>(bond_timeout_.count()));
570 if (attempt_respawn_reconnection_) {
571 bond_respawn_timer_ = nav2::create_timer(
586 if (bond_respawn_start_time_.nanoseconds() == 0) {
587 bond_respawn_start_time_ = now();
592 if (
isActive() || !rclcpp::ok() || node_names_.empty()) {
593 bond_respawn_start_time_ = rclcpp::Time(0);
594 bond_respawn_timer_.reset();
599 int live_servers = 0;
600 const int max_live_servers = node_names_.size();
601 for (
auto & node_name : node_names_) {
607 node_map_[node_name]->get_state(service_timeout_);
616 if (live_servers == max_live_servers) {
617 message(
"Successfully re-established connections from server respawns, starting back up.");
618 bond_respawn_start_time_ = rclcpp::Time(0);
619 bond_respawn_timer_.reset();
621 }
else if (now() - bond_respawn_start_time_ >= bond_respawn_max_duration_) {
622 message(
"Failed to re-establish connection from a server crash after maximum timeout.");
623 bond_respawn_start_time_ = rclcpp::Time(0);
624 bond_respawn_timer_.reset();
628 #define ANSI_COLOR_RESET "\x1b[0m"
629 #define ANSI_COLOR_BLUE "\x1b[34m"
634 RCLCPP_INFO(get_logger(), ANSI_COLOR_BLUE
"\33[1m%s\33[0m" ANSI_COLOR_RESET, msg.c_str());
639 #include "rclcpp_components/register_node_macro.hpp"
A QoS profile for latched, reliable topics with a history of 1 messages.
Implements service interface to transition the lifecycle nodes of Nav2 stack. It receives transition ...
void createBondTimer()
Support function for creating bond timer.
void destroyLifecycleServiceClients()
Destroy all the lifecycle service clients.
void destroyBondTimer()
Support function for killing bond connections.
bool cleanup()
Cleanups the managed nodes.
bool pause()
Pause all the managed nodes.
bool shutdown()
Deactivate, clean up and shut down all the managed nodes.
bool resume()
Resume all the managed nodes.
void createLifecycleServiceServers()
Support function for creating service servers.
void onRclPreshutdown()
Perform preshutdown activities before our Context is shutdown. Note that this is related to our Conte...
void checkBondConnections()
void destroyLifecyclePublishers()
Destroy all the lifecycle publishers.
bool isActive()
function to check if managed nodes are active
bool createBondConnection(const std::string &node_name)
Support function for creating bond connections.
void createLifecyclePublishers()
Support function for creating publishers.
void CreateDiagnostic(diagnostic_updater::DiagnosticStatusWrapper &stat)
function to check the state of Nav2 nodes
void publishIsActiveState()
Publish the is_active state.
void registerRclPreshutdownCallback()
void message(const std::string &msg)
Helper function to highlight the output on the console.
void setState(const NodeState &state)
Set the state of managed nodes.
~LifecycleManager()
A destructor for nav2_lifecycle_manager::LifecycleManager.
bool reset(bool hard_reset=false)
Reset all the managed nodes.
bool startup()
Start up managed nodes.
void shutdownAllNodes()
Support function for shutdown.
void checkBondRespawnConnection()
void managerCallback(const std::shared_ptr< rmw_request_id_t > request_header, const std::shared_ptr< ManageLifecycleNodes::Request > request, std::shared_ptr< ManageLifecycleNodes::Response > response)
Lifecycle node manager callback function.
bool changeStateForNode(const std::string &node_name, std::uint8_t transition)
For a node, transition to the new target state.
bool configure()
Configures the managed nodes.
bool changeStateForAllNodes(std::uint8_t transition, bool hard_change=false)
For each node in the map, transition to the new target state.
void createLifecycleServiceClients()
Support function for creating service clients.
void isActiveCallback(const std::shared_ptr< rmw_request_id_t > request_header, const std::shared_ptr< std_srvs::srv::Trigger::Request > request, std::shared_ptr< std_srvs::srv::Trigger::Response > response)
Trigger callback function checks if the managed nodes are in active state.
Helper functions to interact with a lifecycle node.