16 #include "nav2_lifecycle_manager/lifecycle_manager.hpp"
23 #include "rclcpp/rclcpp.hpp"
24 #include "nav2_ros_common/interface_factories.hpp"
26 using namespace std::chrono_literals;
27 using namespace std::placeholders;
29 using lifecycle_msgs::msg::Transition;
30 using lifecycle_msgs::msg::State;
33 namespace nav2_lifecycle_manager
36 LifecycleManager::LifecycleManager(
const rclcpp::NodeOptions & options)
37 : Node(
"lifecycle_manager", options), diagnostics_updater_(this)
39 RCLCPP_INFO(get_logger(),
"Creating");
43 node_names_ = nav2::declare_or_get_parameter<std::vector<std::string>>(node,
"node_names");
44 autostart_ = nav2::declare_or_get_parameter(node,
"autostart",
false);
45 double bond_timeout_s = nav2::declare_or_get_parameter(node,
"bond_timeout", 4.0);
46 double service_timeout_s = nav2::declare_or_get_parameter(node,
"service_timeout", 5.0);
47 double respawn_timeout_s = nav2::declare_or_get_parameter(
48 node,
"bond_respawn_max_duration", 10.0);
49 attempt_respawn_reconnection_ = nav2::declare_or_get_parameter(
50 node,
"attempt_respawn_reconnection",
true);
51 bond_heartbeat_period_ = nav2::declare_or_get_parameter(node,
"bond_heartbeat_period", 0.25);
55 bond_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(
56 std::chrono::duration<double>(bond_timeout_s));
57 service_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(
58 std::chrono::duration<double>(service_timeout_s));
59 bond_respawn_max_duration_ = rclcpp::Duration::from_seconds(respawn_timeout_s);
61 callback_group_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive,
false);
63 transition_state_map_[Transition::TRANSITION_CONFIGURE] = State::PRIMARY_STATE_INACTIVE;
64 transition_state_map_[Transition::TRANSITION_CLEANUP] = State::PRIMARY_STATE_UNCONFIGURED;
65 transition_state_map_[Transition::TRANSITION_ACTIVATE] = State::PRIMARY_STATE_ACTIVE;
66 transition_state_map_[Transition::TRANSITION_DEACTIVATE] = State::PRIMARY_STATE_INACTIVE;
67 transition_state_map_[Transition::TRANSITION_UNCONFIGURED_SHUTDOWN] =
68 State::PRIMARY_STATE_FINALIZED;
70 transition_label_map_[Transition::TRANSITION_CONFIGURE] = std::string(
"Configuring ");
71 transition_label_map_[Transition::TRANSITION_CLEANUP] = std::string(
"Cleaning up ");
72 transition_label_map_[Transition::TRANSITION_ACTIVATE] = std::string(
"Activating ");
73 transition_label_map_[Transition::TRANSITION_DEACTIVATE] = std::string(
"Deactivating ");
74 transition_label_map_[Transition::TRANSITION_UNCONFIGURED_SHUTDOWN] =
75 std::string(
"Shutting down ");
77 init_timer_ = nav2::create_timer(
81 init_timer_->cancel();
86 init_timer_ = nav2::create_timer(
90 init_timer_->cancel();
95 auto executor = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
96 executor->add_callback_group(callback_group_, get_node_base_interface());
97 service_thread_ = std::make_unique<nav2::NodeThread>(executor);
99 diagnostics_updater_.setHardwareID(
"Nav2");
105 RCLCPP_INFO(get_logger(),
"Destroying %s", get_name());
106 service_thread_.reset();
111 const std::shared_ptr<rmw_request_id_t>,
112 const std::shared_ptr<ManageLifecycleNodes::Request> request,
113 std::shared_ptr<ManageLifecycleNodes::Response> response)
115 switch (request->command) {
116 case ManageLifecycleNodes::Request::STARTUP:
119 case ManageLifecycleNodes::Request::CONFIGURE:
122 case ManageLifecycleNodes::Request::CLEANUP:
125 case ManageLifecycleNodes::Request::RESET:
126 response->success =
reset();
128 case ManageLifecycleNodes::Request::SHUTDOWN:
131 case ManageLifecycleNodes::Request::PAUSE:
132 response->success =
pause();
134 case ManageLifecycleNodes::Request::RESUME:
135 response->success =
resume();
143 managed_nodes_state_ = state;
150 return managed_nodes_state_ == NodeState::ACTIVE;
156 if (is_active_pub_ && is_active_pub_->is_activated()) {
157 auto message = std::make_unique<std_msgs::msg::Bool>();
159 is_active_pub_->publish(std::move(
message));
165 const std::shared_ptr<rmw_request_id_t>,
166 const std::shared_ptr<std_srvs::srv::Trigger::Request>,
167 std::shared_ptr<std_srvs::srv::Trigger::Response> response)
175 unsigned char error_level;
177 switch (managed_nodes_state_) {
178 case NodeState::ACTIVE:
179 error_level = diagnostic_msgs::msg::DiagnosticStatus::OK;
180 message =
"Managed nodes are active";
182 case NodeState::INACTIVE:
183 error_level = diagnostic_msgs::msg::DiagnosticStatus::OK;
184 message =
"Managed nodes are inactive";
186 case NodeState::UNCONFIGURED:
187 error_level = diagnostic_msgs::msg::DiagnosticStatus::OK;
188 message =
"Managed nodes are unconfigured";
190 case NodeState::FINALIZED:
191 error_level = diagnostic_msgs::msg::DiagnosticStatus::WARN;
192 message =
"Managed nodes have been shut down";
195 error_level = diagnostic_msgs::msg::DiagnosticStatus::ERROR;
196 message =
"An error has occurred during a node state transition";
199 stat.summary(error_level,
message);
205 message(
"Creating and initializing lifecycle service clients");
206 for (
auto & node_name : node_names_) {
207 node_map_[node_name] =
208 std::make_shared<LifecycleServiceClient>(node_name, shared_from_this());
215 message(
"Creating and initializing lifecycle service servers");
219 manager_srv_ = nav2::interfaces::create_service<ManageLifecycleNodes>(
221 get_name() + std::string(
"/manage_nodes"),
225 is_active_srv_ = nav2::interfaces::create_service<std_srvs::srv::Trigger>(
227 get_name() + std::string(
"/is_active"),
235 message(
"Creating and initializing lifecycle publishers");
237 is_active_pub_ = nav2::interfaces::create_publisher<std_msgs::msg::Bool>(
239 get_name() + std::string(
"/managed_nodes_activated"),
242 is_active_pub_->on_activate();
250 message(
"Destroying lifecycle service clients");
251 for (
auto & kv : node_map_) {
259 message(
"Destroying lifecycle publishers");
260 if (is_active_pub_) {
261 is_active_pub_->on_deactivate();
262 is_active_pub_.reset();
269 const double timeout_ns =
270 std::chrono::duration_cast<std::chrono::nanoseconds>(bond_timeout_).count();
271 const double timeout_s = timeout_ns / 1e9;
273 if (bond_map_.find(node_name) == bond_map_.end() && bond_timeout_.count() > 0.0) {
274 bond_map_[node_name] =
275 std::make_shared<bond::Bond>(
"bond", node_name, shared_from_this());
276 bond_map_[node_name]->setHeartbeatTimeout(timeout_s);
277 bond_map_[node_name]->setHeartbeatPeriod(bond_heartbeat_period_);
278 bond_map_[node_name]->start();
280 !bond_map_[node_name]->waitUntilFormed(
281 rclcpp::Duration(rclcpp::Duration::from_nanoseconds(timeout_ns / 2))))
285 "Server %s was unable to be reached after %0.2fs by bond. "
286 "This server may be misconfigured.",
287 node_name.c_str(), timeout_s);
290 RCLCPP_INFO(get_logger(),
"Server %s connected with bond.", node_name.c_str());
299 message(transition_label_map_[transition] + node_name);
301 if (!node_map_[node_name]->change_state(
302 transition, std::chrono::milliseconds(-1),
304 !(node_map_[node_name]->get_state(service_timeout_) == transition_state_map_[transition]))
306 RCLCPP_ERROR(get_logger(),
"Failed to change state for node: %s", node_name.c_str());
310 if (transition == Transition::TRANSITION_ACTIVATE) {
312 }
else if (transition == Transition::TRANSITION_DEACTIVATE) {
313 bond_map_.erase(node_name);
323 if (transition == Transition::TRANSITION_CONFIGURE ||
324 transition == Transition::TRANSITION_ACTIVATE)
326 for (
auto & node_name : node_names_) {
331 }
catch (
const std::runtime_error & e) {
334 "Failed to change state for node: %s. Exception: %s.", node_name.c_str(), e.what());
339 std::vector<std::string>::reverse_iterator rit;
340 for (rit = node_names_.rbegin(); rit != node_names_.rend(); ++rit) {
345 }
catch (
const std::runtime_error & e) {
348 "Failed to change state for node: %s. Exception: %s.", (*rit).c_str(), e.what());
359 message(
"Deactivate, cleanup, and shutdown nodes");
369 message(
"Starting managed nodes bringup...");
373 RCLCPP_ERROR(get_logger(),
"Failed to bring up all requested nodes. Aborting bringup.");
377 message(
"Managed nodes are active");
386 message(
"Configuring managed nodes...");
388 RCLCPP_ERROR(get_logger(),
"Failed to configure all requested nodes. Aborting bringup.");
392 message(
"Managed nodes are now configured");
400 message(
"Cleaning up managed nodes...");
402 RCLCPP_ERROR(get_logger(),
"Failed to cleanup all requested nodes. Aborting cleanup.");
406 message(
"Managed nodes have been cleaned up");
416 message(
"Shutting down managed nodes...");
420 message(
"Managed nodes have been shut down");
429 message(
"Resetting managed nodes...");
435 RCLCPP_ERROR(get_logger(),
"Failed to reset nodes: aborting reset");
441 message(
"Managed nodes have been reset");
451 message(
"Pausing managed nodes...");
453 RCLCPP_ERROR(get_logger(),
"Failed to pause nodes: aborting pause");
458 message(
"Managed nodes have been paused");
466 message(
"Resuming managed nodes...");
468 RCLCPP_ERROR(get_logger(),
"Failed to resume nodes: aborting resume");
473 message(
"Managed nodes are active");
482 if (bond_timeout_.count() <= 0) {
486 message(
"Creating bond timer...");
487 bond_timer_ = nav2::create_timer(
498 message(
"Terminating bond timer...");
499 bond_timer_->cancel();
508 get_logger(),
"Running Nav2 LifecycleManager rcl preshutdown (%s)",
517 service_thread_.reset();
526 rclcpp::Context::SharedPtr context = get_node_base_interface()->get_context();
528 context->add_pre_shutdown_callback(
536 if (!
isActive() || !rclcpp::ok() || bond_map_.empty()) {
540 for (
auto & node_name : node_names_) {
545 if (bond_map_[node_name]->isBroken()) {
548 "Have not received a heartbeat from " + node_name +
"."));
553 "CRITICAL FAILURE: SERVER %s IS DOWN after not receiving a heartbeat for %i ms."
554 " Shutting down related nodes.",
555 node_name.c_str(),
static_cast<int>(bond_timeout_.count()));
562 if (attempt_respawn_reconnection_) {
563 bond_respawn_timer_ = nav2::create_timer(
578 if (bond_respawn_start_time_.nanoseconds() == 0) {
579 bond_respawn_start_time_ = now();
584 if (
isActive() || !rclcpp::ok() || node_names_.empty()) {
585 bond_respawn_start_time_ = rclcpp::Time(0);
586 bond_respawn_timer_.reset();
591 int live_servers = 0;
592 const int max_live_servers = node_names_.size();
593 for (
auto & node_name : node_names_) {
599 node_map_[node_name]->get_state(service_timeout_);
608 if (live_servers == max_live_servers) {
609 message(
"Successfully re-established connections from server respawns, starting back up.");
610 bond_respawn_start_time_ = rclcpp::Time(0);
611 bond_respawn_timer_.reset();
613 }
else if (now() - bond_respawn_start_time_ >= bond_respawn_max_duration_) {
614 message(
"Failed to re-establish connection from a server crash after maximum timeout.");
615 bond_respawn_start_time_ = rclcpp::Time(0);
616 bond_respawn_timer_.reset();
620 #define ANSI_COLOR_RESET "\x1b[0m"
621 #define ANSI_COLOR_BLUE "\x1b[34m"
626 RCLCPP_INFO(get_logger(), ANSI_COLOR_BLUE
"\33[1m%s\33[0m" ANSI_COLOR_RESET, msg.c_str());
631 #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.