add humble-navigation2

This commit is contained in:
X-lanni
2025-05-27 19:03:40 +08:00
parent 974abb5e1e
commit e74ec539c2
1280 changed files with 204114 additions and 0 deletions
@@ -0,0 +1,95 @@
cmake_minimum_required(VERSION 3.5)
project(nav2_lifecycle_manager)
find_package(ament_cmake REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(lifecycle_msgs REQUIRED)
find_package(nav2_common REQUIRED)
find_package(nav2_msgs REQUIRED)
find_package(nav2_util REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(rclcpp_lifecycle REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(std_srvs REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(bondcpp REQUIRED)
find_package(diagnostic_updater REQUIRED)
nav2_package()
include_directories(
include
)
set(library_name ${PROJECT_NAME}_core)
add_library(${library_name} SHARED
src/lifecycle_manager.cpp
src/lifecycle_manager_client.cpp
)
set(dependencies
geometry_msgs
lifecycle_msgs
nav2_msgs
nav2_util
rclcpp
rclcpp_action
rclcpp_lifecycle
rclcpp_components
std_msgs
std_srvs
tf2_geometry_msgs
bondcpp
diagnostic_updater
)
ament_target_dependencies(${library_name}
${dependencies}
)
add_executable(lifecycle_manager
src/main.cpp
)
target_link_libraries(lifecycle_manager
${library_name}
)
ament_target_dependencies(lifecycle_manager
${dependencies}
)
rclcpp_components_register_nodes(${library_name} "nav2_lifecycle_manager::LifecycleManager")
install(TARGETS
${library_name}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(TARGETS
lifecycle_manager
RUNTIME DESTINATION lib/${PROJECT_NAME}
)
install(DIRECTORY include/ DESTINATION include/)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
find_package(ament_cmake_gtest REQUIRED)
find_package(ament_cmake_pytest REQUIRED)
add_subdirectory(test)
endif()
ament_export_include_directories(include)
ament_export_libraries(${library_name})
ament_export_dependencies(${dependencies})
ament_package()
@@ -0,0 +1,18 @@
### Background on lifecycle enabled nodes
Using ROS2s managed/lifecycle nodes feature allows the system startup to ensure that all required nodes have been instantiated correctly before they begin their execution. Using lifecycle nodes also allows nodes to be restarted or replaced on-line. More details about managed nodes can be found on [ROS2 Design website](https://design.ros2.org/articles/node_lifecycle.html). Several nodes in Nav2, such as map_server, planner_server, and controller_server, are lifecycle enabled. These nodes provide the required overrides of the lifecycle functions: ```on_configure()```, ```on_activate()```, ```on_deactivate()```, ```on_cleanup()```, ```on_shutdown()```, and ```on_error()```.
See its [Configuration Guide Page](https://navigation.ros.org/configuration/packages/configuring-lifecycle.html) for additional parameter descriptions.
### nav2_lifecycle_manager
Nav2's lifecycle manager is used to change the states of the lifecycle nodes in order to achieve a controlled _startup_, _shutdown_, _reset_, _pause_, or _resume_ of the navigation stack. The lifecycle manager presents a ```lifecycle_manager/manage_nodes``` service, from which clients can invoke the startup, shutdown, reset, pause, or resume functions. Based on this service request, the lifecycle manager calls the necessary lifecycle services in the lifecycle managed nodes. Currently, the RVIZ panel uses this ```lifecycle_manager/manage_nodes``` service when user presses the buttons on the RVIZ panel (e.g.,startup, reset, shutdown, etc.), but it is meant to be called on bringup through a production system application.
In order to start the navigation stack and be able to navigate, the necessary nodes must be configured and activated. Thus, for example when _startup_ is requested from the lifecycle manager's manage_nodes service, the lifecycle managers calls _configure()_ and _activate()_ on the lifecycle enabled nodes in the node list. These are all transitioned in ordered groups for bringup transitions, and reverse ordered groups for shutdown transitions.
The lifecycle manager has a default nodes list for all the nodes that it manages. This list can be changed using the lifecycle managers _“node_names”_ parameter.
The diagram below shows an _example_ of a list of managed nodes, and how it interfaces with the lifecycle manager.
<img src="./doc/diagram_lifecycle_manager.JPG" title="" width="100%" align="middle">
The UML diagram below shows the sequence of service calls once the _startup_ is requested from the lifecycle manager.
<img src="./doc/uml_lifecycle_manager.JPG" title="Lifecycle manager UML diagram" width="100%" align="middle">
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

@@ -0,0 +1,237 @@
// Copyright (c) 2019 Intel Corporation
// Copyright (c) 2022 Samsung Research America
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef NAV2_LIFECYCLE_MANAGER__LIFECYCLE_MANAGER_HPP_
#define NAV2_LIFECYCLE_MANAGER__LIFECYCLE_MANAGER_HPP_
#include <map>
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include "nav2_util/lifecycle_service_client.hpp"
#include "nav2_util/node_thread.hpp"
#include "rclcpp/rclcpp.hpp"
#include "std_srvs/srv/empty.hpp"
#include "nav2_msgs/srv/manage_lifecycle_nodes.hpp"
#include "std_srvs/srv/trigger.hpp"
#include "bondcpp/bond.hpp"
#include "diagnostic_updater/diagnostic_updater.hpp"
namespace nav2_lifecycle_manager
{
using namespace std::chrono_literals; // NOLINT
using nav2_msgs::srv::ManageLifecycleNodes;
/**
* @class nav2_lifecycle_manager::LifecycleManager
* @brief Implements service interface to transition the lifecycle nodes of
* Nav2 stack. It receives transition request and then uses lifecycle
* interface to change lifecycle node's state.
*/
class LifecycleManager : public rclcpp::Node
{
public:
/**
* @brief A constructor for nav2_lifecycle_manager::LifecycleManager
* @param options Additional options to control creation of the node.
*/
explicit LifecycleManager(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
/**
* @brief A destructor for nav2_lifecycle_manager::LifecycleManager
*/
~LifecycleManager();
protected:
// Callback group used by services and timers
rclcpp::CallbackGroup::SharedPtr callback_group_;
std::unique_ptr<nav2_util::NodeThread> service_thread_;
// The services provided by this node
rclcpp::Service<ManageLifecycleNodes>::SharedPtr manager_srv_;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr is_active_srv_;
/**
* @brief Lifecycle node manager callback function
* @param request_header Header of the service request
* @param request Service request
* @param reponse Service response
*/
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);
/**
* @brief Trigger callback function checks if the managed nodes are in active
* state.
* @param request_header Header of the request
* @param request Service request
* @param reponse Service response
*/
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);
// Support functions for the service calls
/**
* @brief Start up managed nodes.
* @return true or false
*/
bool startup();
/**
* @brief Deactivate, clean up and shut down all the managed nodes.
* @return true or false
*/
bool shutdown();
/**
* @brief Reset all the managed nodes.
* @return true or false
*/
bool reset(bool hard_reset = false);
/**
* @brief Pause all the managed nodes.
* @return true or false
*/
bool pause();
/**
* @brief Resume all the managed nodes.
* @return true or false
*/
bool resume();
/**
* @brief Perform preshutdown activities before our Context is shutdown.
* Note that this is related to our Context's shutdown sequence, not the
* lifecycle node state machine or shutdown().
*/
void onRclPreshutdown();
// Support function for creating service clients
/**
* @brief Support function for creating service clients
*/
void createLifecycleServiceClients();
// Support functions for shutdown
/**
* @brief Support function for shutdown
*/
void shutdownAllNodes();
/**
* @brief Destroy all the lifecycle service clients.
*/
void destroyLifecycleServiceClients();
// Support function for creating bond timer
/**
* @brief Support function for creating bond timer
*/
void createBondTimer();
// Support function for creating bond connection
/**
* @brief Support function for creating bond connections
*/
bool createBondConnection(const std::string & node_name);
// Support function for killing bond connections
/**
* @brief Support function for killing bond connections
*/
void destroyBondTimer();
// Support function for checking on bond connections
/**
* @ brief Support function for checking on bond connections
* will take down system if there's something non-responsive
*/
void checkBondConnections();
// Support function for checking if bond connections come back after respawn
/**
* @ brief Support function for checking on bond connections
* will bring back the system if something goes from non-responsive to responsive
*/
void checkBondRespawnConnection();
/**
* @brief For a node, transition to the new target state
*/
bool changeStateForNode(
const std::string & node_name,
std::uint8_t transition);
/**
* @brief For each node in the map, transition to the new target state
*/
bool changeStateForAllNodes(std::uint8_t transition, bool hard_change = false);
// Convenience function to highlight the output on the console
/**
* @brief Helper function to highlight the output on the console
*/
void message(const std::string & msg);
// Diagnostics functions
/**
* @brief function to check if the Nav2 system is active
*/
void CreateActiveDiagnostic(diagnostic_updater::DiagnosticStatusWrapper & stat);
/**
* Register our preshutdown callback for this Node's rcl Context.
* The callback fires before this Node's Context is shutdown.
* Note this is not directly related to the lifecycle state machine or the
* shutdown() instance function.
*/
void registerRclPreshutdownCallback();
// Timer thread to look at bond connections
rclcpp::TimerBase::SharedPtr init_timer_;
rclcpp::TimerBase::SharedPtr bond_timer_;
rclcpp::TimerBase::SharedPtr bond_respawn_timer_;
std::chrono::milliseconds bond_timeout_;
// A map of all nodes to check bond connection
std::map<std::string, std::shared_ptr<bond::Bond>> bond_map_;
// A map of all nodes to be controlled
std::map<std::string, std::shared_ptr<nav2_util::LifecycleServiceClient>> node_map_;
std::map<std::uint8_t, std::string> transition_label_map_;
// A map of the expected transitions to primary states
std::unordered_map<std::uint8_t, std::uint8_t> transition_state_map_;
// The names of the nodes to be managed, in the order of desired bring-up
std::vector<std::string> node_names_;
// Whether to automatically start up the system
bool autostart_;
bool attempt_respawn_reconnection_;
bool system_active_{false};
diagnostic_updater::Updater diagnostics_updater_;
rclcpp::Time bond_respawn_start_time_{0};
rclcpp::Duration bond_respawn_max_duration_{10s};
};
} // namespace nav2_lifecycle_manager
#endif // NAV2_LIFECYCLE_MANAGER__LIFECYCLE_MANAGER_HPP_
@@ -0,0 +1,109 @@
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef NAV2_LIFECYCLE_MANAGER__LIFECYCLE_MANAGER_CLIENT_HPP_
#define NAV2_LIFECYCLE_MANAGER__LIFECYCLE_MANAGER_CLIENT_HPP_
#include <memory>
#include <string>
#include "geometry_msgs/msg/pose_with_covariance_stamped.hpp"
#include "geometry_msgs/msg/quaternion.hpp"
#include "nav2_msgs/action/navigate_to_pose.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "std_srvs/srv/empty.hpp"
#include "nav2_msgs/srv/manage_lifecycle_nodes.hpp"
#include "std_srvs/srv/trigger.hpp"
#include "nav2_util/service_client.hpp"
namespace nav2_lifecycle_manager
{
/**
* @enum nav2_lifecycle_manager::SystemStatus
* @brief Enum class representing the status of the system.
*/
enum class SystemStatus {ACTIVE, INACTIVE, TIMEOUT};
/**
* @class nav2_lifecycle_manager::LifeCycleMangerClient
* @brief The LifecycleManagerClient sends requests to the LifecycleManager to
* control the lifecycle state of the navigation modules.
*/
class LifecycleManagerClient
{
public:
/**
* @brief A constructor for LifeCycleMangerClient
* @param name Managed node name
* @param parent_node Node that execute the service calls
*/
explicit LifecycleManagerClient(
const std::string & name,
std::shared_ptr<rclcpp::Node> parent_node);
// Client-side interface to the Nav2 lifecycle manager
/**
* @brief Make start up service call
* @return true or false
*/
bool startup(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/**
* @brief Make shutdown service call
* @return true or false
*/
bool shutdown(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/**
* @brief Make pause service call
* @return true or false
*/
bool pause(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/**
* @brief Make resume service call
* @return true or false
*/
bool resume(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/**
* @brief Make reset service call
* @return true or false
*/
bool reset(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/**
* @brief Check if lifecycle node manager server is active
* @return ACTIVE or INACTIVE or TIMEOUT
*/
SystemStatus is_active(const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
protected:
using ManageLifecycleNodes = nav2_msgs::srv::ManageLifecycleNodes;
/**
* @brief A generic method used to call startup, shutdown, etc.
* @param command
*/
bool callService(
uint8_t command,
const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
// The node to use for the service call
rclcpp::Node::SharedPtr node_;
std::shared_ptr<nav2_util::ServiceClient<ManageLifecycleNodes>> manager_client_;
std::shared_ptr<nav2_util::ServiceClient<std_srvs::srv::Trigger>> is_active_client_;
std::string manage_service_name_;
std::string active_service_name_;
};
} // namespace nav2_lifecycle_manager
#endif // NAV2_LIFECYCLE_MANAGER__LIFECYCLE_MANAGER_CLIENT_HPP_
@@ -0,0 +1,45 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>nav2_lifecycle_manager</name>
<version>1.1.18</version>
<description>A controller/manager for the lifecycle nodes of the Navigation 2 system</description>
<maintainer email="michael.jeronimo@intel.com">Michael Jeronimo</maintainer>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<build_depend>geometry_msgs</build_depend>
<build_depend>lifecycle_msgs</build_depend>
<build_depend>nav2_msgs</build_depend>
<build_depend>nav2_util</build_depend>
<build_depend>rclcpp_action</build_depend>
<build_depend>rclcpp_lifecycle</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>std_srvs</build_depend>
<build_depend>tf2_geometry_msgs</build_depend>
<build_depend>bondcpp</build_depend>
<build_depend>nav2_common</build_depend>
<build_depend>diagnostic_updater</build_depend>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>lifecycle_msgs</exec_depend>
<exec_depend>nav2_msgs</exec_depend>
<exec_depend>nav2_util</exec_depend>
<exec_depend>rclcpp_action</exec_depend>
<exec_depend>rclcpp_lifecycle</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>std_srvs</exec_depend>
<exec_depend>bondcpp</exec_depend>
<exec_depend>tf2_geometry_msgs</exec_depend>
<exec_depend>diagnostic_updater</exec_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_cmake_pytest</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
@@ -0,0 +1,510 @@
// Copyright (c) 2019 Intel Corporation
// Copyright (c) 2022 Samsung Research America
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nav2_lifecycle_manager/lifecycle_manager.hpp"
#include <chrono>
#include <memory>
#include <string>
#include <vector>
#include "rclcpp/rclcpp.hpp"
using namespace std::chrono_literals;
using namespace std::placeholders;
using lifecycle_msgs::msg::Transition;
using lifecycle_msgs::msg::State;
using nav2_util::LifecycleServiceClient;
namespace nav2_lifecycle_manager
{
LifecycleManager::LifecycleManager(const rclcpp::NodeOptions & options)
: Node("lifecycle_manager", options), diagnostics_updater_(this)
{
RCLCPP_INFO(get_logger(), "Creating");
// The list of names is parameterized, allowing this module to be used with a different set
// of nodes
declare_parameter("node_names", rclcpp::PARAMETER_STRING_ARRAY);
declare_parameter("autostart", rclcpp::ParameterValue(false));
declare_parameter("bond_timeout", 4.0);
declare_parameter("bond_respawn_max_duration", 10.0);
declare_parameter("attempt_respawn_reconnection", true);
registerRclPreshutdownCallback();
node_names_ = get_parameter("node_names").as_string_array();
get_parameter("autostart", autostart_);
double bond_timeout_s;
get_parameter("bond_timeout", bond_timeout_s);
bond_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::duration<double>(bond_timeout_s));
double respawn_timeout_s;
get_parameter("bond_respawn_max_duration", respawn_timeout_s);
bond_respawn_max_duration_ = rclcpp::Duration::from_seconds(respawn_timeout_s);
get_parameter("attempt_respawn_reconnection", attempt_respawn_reconnection_);
callback_group_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive, false);
manager_srv_ = create_service<ManageLifecycleNodes>(
get_name() + std::string("/manage_nodes"),
std::bind(&LifecycleManager::managerCallback, this, _1, _2, _3),
rclcpp::ServicesQoS().get_rmw_qos_profile(),
callback_group_);
is_active_srv_ = create_service<std_srvs::srv::Trigger>(
get_name() + std::string("/is_active"),
std::bind(&LifecycleManager::isActiveCallback, this, _1, _2, _3),
rclcpp::ServicesQoS().get_rmw_qos_profile(),
callback_group_);
transition_state_map_[Transition::TRANSITION_CONFIGURE] = State::PRIMARY_STATE_INACTIVE;
transition_state_map_[Transition::TRANSITION_CLEANUP] = State::PRIMARY_STATE_UNCONFIGURED;
transition_state_map_[Transition::TRANSITION_ACTIVATE] = State::PRIMARY_STATE_ACTIVE;
transition_state_map_[Transition::TRANSITION_DEACTIVATE] = State::PRIMARY_STATE_INACTIVE;
transition_state_map_[Transition::TRANSITION_UNCONFIGURED_SHUTDOWN] =
State::PRIMARY_STATE_FINALIZED;
transition_label_map_[Transition::TRANSITION_CONFIGURE] = std::string("Configuring ");
transition_label_map_[Transition::TRANSITION_CLEANUP] = std::string("Cleaning up ");
transition_label_map_[Transition::TRANSITION_ACTIVATE] = std::string("Activating ");
transition_label_map_[Transition::TRANSITION_DEACTIVATE] = std::string("Deactivating ");
transition_label_map_[Transition::TRANSITION_UNCONFIGURED_SHUTDOWN] =
std::string("Shutting down ");
init_timer_ = this->create_wall_timer(
0s,
[this]() -> void {
init_timer_->cancel();
createLifecycleServiceClients();
if (autostart_) {
init_timer_ = this->create_wall_timer(
0s,
[this]() -> void {
init_timer_->cancel();
startup();
},
callback_group_);
}
auto executor = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
executor->add_callback_group(callback_group_, get_node_base_interface());
service_thread_ = std::make_unique<nav2_util::NodeThread>(executor);
});
diagnostics_updater_.setHardwareID("Nav2");
diagnostics_updater_.add("Nav2 Health", this, &LifecycleManager::CreateActiveDiagnostic);
}
LifecycleManager::~LifecycleManager()
{
RCLCPP_INFO(get_logger(), "Destroying %s", get_name());
service_thread_.reset();
}
void
LifecycleManager::managerCallback(
const std::shared_ptr<rmw_request_id_t>/*request_header*/,
const std::shared_ptr<ManageLifecycleNodes::Request> request,
std::shared_ptr<ManageLifecycleNodes::Response> response)
{
switch (request->command) {
case ManageLifecycleNodes::Request::STARTUP:
response->success = startup();
break;
case ManageLifecycleNodes::Request::RESET:
response->success = reset();
break;
case ManageLifecycleNodes::Request::SHUTDOWN:
response->success = shutdown();
break;
case ManageLifecycleNodes::Request::PAUSE:
response->success = pause();
break;
case ManageLifecycleNodes::Request::RESUME:
response->success = resume();
break;
}
}
void
LifecycleManager::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)
{
response->success = system_active_;
}
void
LifecycleManager::CreateActiveDiagnostic(diagnostic_updater::DiagnosticStatusWrapper & stat)
{
if (system_active_) {
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Nav2 is active");
} else {
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, "Nav2 is inactive");
}
}
void
LifecycleManager::createLifecycleServiceClients()
{
message("Creating and initializing lifecycle service clients");
for (auto & node_name : node_names_) {
node_map_[node_name] =
std::make_shared<LifecycleServiceClient>(node_name, shared_from_this());
}
}
void
LifecycleManager::destroyLifecycleServiceClients()
{
message("Destroying lifecycle service clients");
for (auto & kv : node_map_) {
kv.second.reset();
}
}
bool
LifecycleManager::createBondConnection(const std::string & node_name)
{
const double timeout_ns =
std::chrono::duration_cast<std::chrono::nanoseconds>(bond_timeout_).count();
const double timeout_s = timeout_ns / 1e9;
if (bond_map_.find(node_name) == bond_map_.end() && bond_timeout_.count() > 0.0) {
bond_map_[node_name] =
std::make_shared<bond::Bond>("bond", node_name, shared_from_this());
bond_map_[node_name]->setHeartbeatTimeout(timeout_s);
bond_map_[node_name]->setHeartbeatPeriod(0.10);
bond_map_[node_name]->start();
if (
!bond_map_[node_name]->waitUntilFormed(
rclcpp::Duration(rclcpp::Duration::from_nanoseconds(timeout_ns / 2))))
{
RCLCPP_ERROR(
get_logger(),
"Server %s was unable to be reached after %0.2fs by bond. "
"This server may be misconfigured.",
node_name.c_str(), timeout_s);
return false;
}
RCLCPP_INFO(get_logger(), "Server %s connected with bond.", node_name.c_str());
}
return true;
}
bool
LifecycleManager::changeStateForNode(const std::string & node_name, std::uint8_t transition)
{
message(transition_label_map_[transition] + node_name);
if (!node_map_[node_name]->change_state(transition) ||
!(node_map_[node_name]->get_state() == transition_state_map_[transition]))
{
RCLCPP_ERROR(get_logger(), "Failed to change state for node: %s", node_name.c_str());
return false;
}
if (transition == Transition::TRANSITION_ACTIVATE) {
return createBondConnection(node_name);
} else if (transition == Transition::TRANSITION_DEACTIVATE) {
bond_map_.erase(node_name);
}
return true;
}
bool
LifecycleManager::changeStateForAllNodes(std::uint8_t transition, bool hard_change)
{
// Hard change will continue even if a node fails
if (transition == Transition::TRANSITION_CONFIGURE ||
transition == Transition::TRANSITION_ACTIVATE)
{
for (auto & node_name : node_names_) {
try {
if (!changeStateForNode(node_name, transition) && !hard_change) {
return false;
}
} catch (const std::runtime_error & e) {
RCLCPP_ERROR(
get_logger(),
"Failed to change state for node: %s. Exception: %s.", node_name.c_str(), e.what());
return false;
}
}
} else {
std::vector<std::string>::reverse_iterator rit;
for (rit = node_names_.rbegin(); rit != node_names_.rend(); ++rit) {
try {
if (!changeStateForNode(*rit, transition) && !hard_change) {
return false;
}
} catch (const std::runtime_error & e) {
RCLCPP_ERROR(
get_logger(),
"Failed to change state for node: %s. Exception: %s.", (*rit).c_str(), e.what());
return false;
}
}
}
return true;
}
void
LifecycleManager::shutdownAllNodes()
{
message("Deactivate, cleanup, and shutdown nodes");
changeStateForAllNodes(Transition::TRANSITION_DEACTIVATE);
changeStateForAllNodes(Transition::TRANSITION_CLEANUP);
changeStateForAllNodes(Transition::TRANSITION_UNCONFIGURED_SHUTDOWN);
}
bool
LifecycleManager::startup()
{
message("Starting managed nodes bringup...");
if (!changeStateForAllNodes(Transition::TRANSITION_CONFIGURE) ||
!changeStateForAllNodes(Transition::TRANSITION_ACTIVATE))
{
RCLCPP_ERROR(get_logger(), "Failed to bring up all requested nodes. Aborting bringup.");
return false;
}
message("Managed nodes are active");
system_active_ = true;
createBondTimer();
return true;
}
bool
LifecycleManager::shutdown()
{
system_active_ = false;
destroyBondTimer();
message("Shutting down managed nodes...");
shutdownAllNodes();
destroyLifecycleServiceClients();
message("Managed nodes have been shut down");
return true;
}
bool
LifecycleManager::reset(bool hard_reset)
{
system_active_ = false;
destroyBondTimer();
message("Resetting managed nodes...");
// Should transition in reverse order
if (!changeStateForAllNodes(Transition::TRANSITION_DEACTIVATE, hard_reset) ||
!changeStateForAllNodes(Transition::TRANSITION_CLEANUP, hard_reset))
{
if (!hard_reset) {
RCLCPP_ERROR(get_logger(), "Failed to reset nodes: aborting reset");
return false;
}
}
message("Managed nodes have been reset");
return true;
}
bool
LifecycleManager::pause()
{
system_active_ = false;
destroyBondTimer();
message("Pausing managed nodes...");
if (!changeStateForAllNodes(Transition::TRANSITION_DEACTIVATE)) {
RCLCPP_ERROR(get_logger(), "Failed to pause nodes: aborting pause");
return false;
}
message("Managed nodes have been paused");
return true;
}
bool
LifecycleManager::resume()
{
message("Resuming managed nodes...");
if (!changeStateForAllNodes(Transition::TRANSITION_ACTIVATE)) {
RCLCPP_ERROR(get_logger(), "Failed to resume nodes: aborting resume");
return false;
}
message("Managed nodes are active");
system_active_ = true;
createBondTimer();
return true;
}
void
LifecycleManager::createBondTimer()
{
if (bond_timeout_.count() <= 0) {
return;
}
message("Creating bond timer...");
bond_timer_ = this->create_wall_timer(
200ms,
std::bind(&LifecycleManager::checkBondConnections, this),
callback_group_);
}
void
LifecycleManager::destroyBondTimer()
{
if (bond_timer_) {
message("Terminating bond timer...");
bond_timer_->cancel();
bond_timer_.reset();
}
}
void
LifecycleManager::onRclPreshutdown()
{
RCLCPP_INFO(
get_logger(), "Running Nav2 LifecycleManager rcl preshutdown (%s)",
this->get_name());
destroyBondTimer();
/*
* Dropping the bond map is what we really need here, but we drop the others
* to prevent the bond map being used. Likewise, squash the service thread.
*/
service_thread_.reset();
node_names_.clear();
node_map_.clear();
bond_map_.clear();
}
void
LifecycleManager::registerRclPreshutdownCallback()
{
rclcpp::Context::SharedPtr context = get_node_base_interface()->get_context();
context->add_pre_shutdown_callback(
std::bind(&LifecycleManager::onRclPreshutdown, this)
);
}
void
LifecycleManager::checkBondConnections()
{
if (!system_active_ || !rclcpp::ok() || bond_map_.empty()) {
return;
}
for (auto & node_name : node_names_) {
if (!rclcpp::ok()) {
return;
}
if (bond_map_[node_name]->isBroken()) {
message(
std::string(
"Have not received a heartbeat from " + node_name + "."));
// if one is down, bring them all down
RCLCPP_ERROR(
get_logger(),
"CRITICAL FAILURE: SERVER %s IS DOWN after not receiving a heartbeat for %i ms."
" Shutting down related nodes.",
node_name.c_str(), static_cast<int>(bond_timeout_.count()));
reset(true); // hard reset to transition all still active down
// if a server crashed, it won't get cleared due to failed transition, clear manually
bond_map_.clear();
// Initialize the bond respawn timer to check if server comes back online
// after a failure, within a maximum timeout period.
if (attempt_respawn_reconnection_) {
bond_respawn_timer_ = this->create_wall_timer(
1s,
std::bind(&LifecycleManager::checkBondRespawnConnection, this),
callback_group_);
}
return;
}
}
}
void
LifecycleManager::checkBondRespawnConnection()
{
// First attempt in respawn, start maximum duration to respawn
if (bond_respawn_start_time_.nanoseconds() == 0) {
bond_respawn_start_time_ = now();
}
// Note: system_active_ is inverted since this should be in a failure
// condition. If another outside user actives the system again, this should not process.
if (system_active_ || !rclcpp::ok() || node_names_.empty()) {
bond_respawn_start_time_ = rclcpp::Time(0);
bond_respawn_timer_.reset();
return;
}
// Check number of live connections after a bond failure
int live_servers = 0;
const int max_live_servers = node_names_.size();
for (auto & node_name : node_names_) {
if (!rclcpp::ok()) {
return;
}
try {
node_map_[node_name]->get_state(); // Only won't throw if the server exists
live_servers++;
} catch (...) {
break;
}
}
// If all are alive, kill timer and retransition system to active
// Else, check if maximum timeout has occurred
if (live_servers == max_live_servers) {
message("Successfully re-established connections from server respawns, starting back up.");
bond_respawn_start_time_ = rclcpp::Time(0);
bond_respawn_timer_.reset();
startup();
} else if (now() - bond_respawn_start_time_ >= bond_respawn_max_duration_) {
message("Failed to re-establish connection from a server crash after maximum timeout.");
bond_respawn_start_time_ = rclcpp::Time(0);
bond_respawn_timer_.reset();
}
}
#define ANSI_COLOR_RESET "\x1b[0m"
#define ANSI_COLOR_BLUE "\x1b[34m"
void
LifecycleManager::message(const std::string & msg)
{
RCLCPP_INFO(get_logger(), ANSI_COLOR_BLUE "\33[1m%s\33[0m" ANSI_COLOR_RESET, msg.c_str());
}
} // namespace nav2_lifecycle_manager
#include "rclcpp_components/register_node_macro.hpp"
RCLCPP_COMPONENTS_REGISTER_NODE(nav2_lifecycle_manager::LifecycleManager)
@@ -0,0 +1,136 @@
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nav2_lifecycle_manager/lifecycle_manager_client.hpp"
#include <cmath>
#include <memory>
#include <string>
#include <utility>
#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include "nav2_util/geometry_utils.hpp"
namespace nav2_lifecycle_manager
{
using nav2_util::geometry_utils::orientationAroundZAxis;
LifecycleManagerClient::LifecycleManagerClient(
const std::string & name,
std::shared_ptr<rclcpp::Node> parent_node)
{
manage_service_name_ = name + std::string("/manage_nodes");
active_service_name_ = name + std::string("/is_active");
// Use parent node for service call and logging
node_ = parent_node;
// Create the service clients
manager_client_ = std::make_shared<nav2_util::ServiceClient<ManageLifecycleNodes>>(
manage_service_name_, node_);
is_active_client_ = std::make_shared<nav2_util::ServiceClient<std_srvs::srv::Trigger>>(
active_service_name_, node_);
}
bool
LifecycleManagerClient::startup(const std::chrono::nanoseconds timeout)
{
return callService(ManageLifecycleNodes::Request::STARTUP, timeout);
}
bool
LifecycleManagerClient::shutdown(const std::chrono::nanoseconds timeout)
{
return callService(ManageLifecycleNodes::Request::SHUTDOWN, timeout);
}
bool
LifecycleManagerClient::pause(const std::chrono::nanoseconds timeout)
{
return callService(ManageLifecycleNodes::Request::PAUSE, timeout);
}
bool
LifecycleManagerClient::resume(const std::chrono::nanoseconds timeout)
{
return callService(ManageLifecycleNodes::Request::RESUME, timeout);
}
bool
LifecycleManagerClient::reset(const std::chrono::nanoseconds timeout)
{
return callService(ManageLifecycleNodes::Request::RESET, timeout);
}
SystemStatus
LifecycleManagerClient::is_active(const std::chrono::nanoseconds timeout)
{
auto request = std::make_shared<std_srvs::srv::Trigger::Request>();
auto response = std::make_shared<std_srvs::srv::Trigger::Response>();
RCLCPP_DEBUG(
node_->get_logger(), "Waiting for the %s service...",
active_service_name_.c_str());
if (!is_active_client_->wait_for_service(std::chrono::seconds(1))) {
return SystemStatus::TIMEOUT;
}
RCLCPP_DEBUG(
node_->get_logger(), "Sending %s request",
active_service_name_.c_str());
try {
response = is_active_client_->invoke(request, timeout);
} catch (std::runtime_error &) {
return SystemStatus::TIMEOUT;
}
if (response->success) {
return SystemStatus::ACTIVE;
} else {
return SystemStatus::INACTIVE;
}
}
bool
LifecycleManagerClient::callService(uint8_t command, const std::chrono::nanoseconds timeout)
{
auto request = std::make_shared<ManageLifecycleNodes::Request>();
request->command = command;
RCLCPP_DEBUG(
node_->get_logger(), "Waiting for the %s service...",
manage_service_name_.c_str());
while (!manager_client_->wait_for_service(timeout)) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(node_->get_logger(), "Client interrupted while waiting for service to appear");
return false;
}
RCLCPP_DEBUG(node_->get_logger(), "Waiting for service to appear...");
}
RCLCPP_DEBUG(
node_->get_logger(), "Sending %s request",
manage_service_name_.c_str());
try {
auto future_result = manager_client_->invoke(request, timeout);
return future_result->success;
} catch (std::runtime_error &) {
return false;
}
}
} // namespace nav2_lifecycle_manager
@@ -0,0 +1,28 @@
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "nav2_lifecycle_manager/lifecycle_manager.hpp"
#include "rclcpp/rclcpp.hpp"
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<nav2_lifecycle_manager::LifecycleManager>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
@@ -0,0 +1,41 @@
ament_add_gtest_executable(test_lifecycle_gtest
test_lifecycle_manager.cpp
)
target_link_libraries(test_lifecycle_gtest
${library_name}
)
ament_target_dependencies(test_lifecycle_gtest
${dependencies}
)
ament_add_test(test_lifecycle
GENERATE_RESULT_FOR_RETURN_CODE_ZERO
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/launch_lifecycle_test.py"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
TIMEOUT 20
ENV
TEST_EXECUTABLE=$<TARGET_FILE:test_lifecycle_gtest>
)
ament_add_gtest_executable(test_bond_gtest
test_bond.cpp
)
target_link_libraries(test_bond_gtest
${library_name}
)
ament_target_dependencies(test_bond_gtest
${dependencies}
)
ament_add_test(test_bond
GENERATE_RESULT_FOR_RETURN_CODE_ZERO
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/launch_bond_test.py"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
TIMEOUT 20
ENV
TEST_EXECUTABLE=$<TARGET_FILE:test_bond_gtest>
)
@@ -0,0 +1,57 @@
#! /usr/bin/env python3
# Copyright (c) 2020 Samsung Research America
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
from launch import LaunchDescription
from launch import LaunchService
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
from launch_testing.legacy import LaunchTestService
def generate_launch_description():
return LaunchDescription([
Node(
package='nav2_lifecycle_manager',
executable='lifecycle_manager',
name='lifecycle_manager_test',
output='screen',
parameters=[{'use_sim_time': False},
{'autostart': False},
{'node_names': ['bond_tester']}]),
])
def main(argv=sys.argv[1:]):
ld = generate_launch_description()
testExecutable = os.getenv('TEST_EXECUTABLE')
test1_action = ExecuteProcess(
cmd=[testExecutable],
name='test_bond_gtest',
output='screen')
lts = LaunchTestService()
lts.add_test_action(ld, test1_action)
ls = LaunchService(argv=argv)
ls.include_launch_description(ld)
return lts.run(ls)
if __name__ == '__main__':
sys.exit(main())
@@ -0,0 +1,58 @@
#! /usr/bin/env python3
# Copyright (c) 2020 Shivang Patel
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
from launch import LaunchDescription
from launch import LaunchService
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
from launch_testing.legacy import LaunchTestService
def generate_launch_description():
return LaunchDescription([
Node(
package='nav2_lifecycle_manager',
executable='lifecycle_manager',
name='lifecycle_manager_test',
output='screen',
parameters=[{'use_sim_time': False},
{'autostart': False},
{'bond_timeout': 0.0},
{'node_names': ['lifecycle_node_test']}]),
])
def main(argv=sys.argv[1:]):
ld = generate_launch_description()
testExecutable = os.getenv('TEST_EXECUTABLE')
test1_action = ExecuteProcess(
cmd=[testExecutable],
name='test_lifecycle_node_gtest',
output='screen')
lts = LaunchTestService()
lts.add_test_action(ld, test1_action)
ls = LaunchService(argv=argv)
ls.include_launch_description(ld)
return lts.run(ls)
if __name__ == '__main__':
sys.exit(main())
@@ -0,0 +1,199 @@
// Copyright (c) 2020 Samsung Research America
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include <chrono>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "nav2_util/lifecycle_node.hpp"
#include "nav2_util/node_thread.hpp"
#include "nav2_lifecycle_manager/lifecycle_manager.hpp"
#include "nav2_lifecycle_manager/lifecycle_manager_client.hpp"
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
// minimal lifecycle node implementing bond as in rest of navigation servers
class TestLifecycleNode : public nav2_util::LifecycleNode
{
public:
TestLifecycleNode(bool bond, std::string name)
: nav2_util::LifecycleNode(name)
{
state = "";
enable_bond = bond;
}
CallbackReturn on_configure(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Configured!");
state = "configured";
return CallbackReturn::SUCCESS;
}
CallbackReturn on_activate(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Activated!");
state = "activated";
if (enable_bond) {
createBond();
}
return CallbackReturn::SUCCESS;
}
CallbackReturn on_deactivate(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Deactivated!");
state = "deactivated";
if (enable_bond) {
destroyBond();
}
return CallbackReturn::SUCCESS;
}
CallbackReturn on_cleanup(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Cleanup!");
state = "cleaned up";
return CallbackReturn::SUCCESS;
}
CallbackReturn on_shutdown(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Shutdown!");
state = "shut down";
return CallbackReturn::SUCCESS;
}
CallbackReturn on_error(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is encountered an error!");
state = "errored";
return CallbackReturn::SUCCESS;
}
bool bondAllocated()
{
return bond_ ? true : false;
}
void breakBond()
{
bond_->breakBond();
}
std::string getState()
{
return state;
}
bool isBondEnabled()
{
return enable_bond;
}
bool isBondConnected()
{
return bondAllocated() ? !bond_->isBroken() : false;
}
std::string state;
bool enable_bond;
};
class TestFixture
{
public:
TestFixture(bool bond, std::string node_name)
{
lf_node_ = std::make_shared<TestLifecycleNode>(bond, node_name);
lf_thread_ = std::make_unique<nav2_util::NodeThread>(lf_node_->get_node_base_interface());
}
std::shared_ptr<TestLifecycleNode> lf_node_;
std::unique_ptr<nav2_util::NodeThread> lf_thread_;
};
TEST(LifecycleBondTest, POSITIVE)
{
// let the lifecycle server come up
rclcpp::Rate(1).sleep();
auto node = std::make_shared<rclcpp::Node>("lifecycle_manager_test_service_client");
nav2_lifecycle_manager::LifecycleManagerClient client("lifecycle_manager_test", node);
// create node, should be up now
auto fixture = TestFixture(true, "bond_tester");
auto bond_tester = fixture.lf_node_;
EXPECT_TRUE(client.startup());
// check if bond is connected after being activated
rclcpp::Rate(5).sleep();
EXPECT_TRUE(bond_tester->isBondConnected());
EXPECT_EQ(bond_tester->getState(), "activated");
bond_tester->breakBond();
// bond should be disconnected now and lifecycle manager should know and react to reset
rclcpp::Rate(5).sleep();
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::INACTIVE,
client.is_active(std::chrono::nanoseconds(1000000000)));
EXPECT_FALSE(bond_tester->isBondConnected());
EXPECT_EQ(bond_tester->getState(), "cleaned up");
// check that bringing up again is OK
EXPECT_TRUE(client.startup());
EXPECT_EQ(bond_tester->getState(), "activated");
EXPECT_TRUE(bond_tester->isBondConnected());
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::ACTIVE,
client.is_active(std::chrono::nanoseconds(1000000000)));
// clean state for next test.
EXPECT_TRUE(client.reset());
EXPECT_FALSE(bond_tester->isBondConnected());
EXPECT_EQ(bond_tester->getState(), "cleaned up");
}
TEST(LifecycleBondTest, NEGATIVE)
{
auto node = std::make_shared<rclcpp::Node>("lifecycle_manager_test_service_client");
nav2_lifecycle_manager::LifecycleManagerClient client("lifecycle_manager_test", node);
// create node, now without bond setup to connect to. Should fail because no bond
auto fixture = TestFixture(false, "bond_tester");
auto bond_tester = fixture.lf_node_;
EXPECT_FALSE(client.startup());
EXPECT_FALSE(bond_tester->isBondEnabled());
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::INACTIVE,
client.is_active(std::chrono::nanoseconds(1000000000)));
}
int main(int argc, char ** argv)
{
::testing::InitGoogleTest(&argc, argv);
// initialize ROS
rclcpp::init(argc, argv);
bool all_successful = RUN_ALL_TESTS();
// shutdown ROS
rclcpp::shutdown();
return all_successful;
}
@@ -0,0 +1,128 @@
// Copyright (c) 2020 Shivang Patel
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include <chrono>
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_lifecycle/lifecycle_node.hpp"
#include "nav2_util/node_thread.hpp"
#include "nav2_lifecycle_manager/lifecycle_manager_client.hpp"
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
class LifecycleNodeTest : public rclcpp_lifecycle::LifecycleNode
{
public:
LifecycleNodeTest()
: rclcpp_lifecycle::LifecycleNode("lifecycle_node_test") {}
CallbackReturn on_configure(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Configured!");
return CallbackReturn::SUCCESS;
}
CallbackReturn on_activate(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Activated!");
return CallbackReturn::SUCCESS;
}
CallbackReturn on_deactivate(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Deactivated!");
return CallbackReturn::SUCCESS;
}
CallbackReturn on_cleanup(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Cleanup!");
return CallbackReturn::SUCCESS;
}
CallbackReturn on_shutdown(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is Shutdown!");
return CallbackReturn::SUCCESS;
}
CallbackReturn on_error(const rclcpp_lifecycle::State & /*state*/) override
{
RCLCPP_INFO(get_logger(), "Lifecycle Test node is encountered an error!");
return CallbackReturn::SUCCESS;
}
};
class LifecycleClientTestFixture
{
public:
LifecycleClientTestFixture()
{
lf_node_ = std::make_shared<LifecycleNodeTest>();
lf_thread_ = std::make_unique<nav2_util::NodeThread>(lf_node_->get_node_base_interface());
}
private:
std::shared_ptr<LifecycleNodeTest> lf_node_;
std::unique_ptr<nav2_util::NodeThread> lf_thread_;
};
TEST(LifecycleClientTest, BasicTest)
{
LifecycleClientTestFixture fix;
auto node = std::make_shared<rclcpp::Node>("lifecycle_manager_test_service_client");
nav2_lifecycle_manager::LifecycleManagerClient client("lifecycle_manager_test", node);
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::TIMEOUT,
client.is_active(std::chrono::nanoseconds(1000)));
EXPECT_TRUE(client.startup());
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::ACTIVE,
client.is_active(std::chrono::nanoseconds(1000000000)));
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::ACTIVE,
client.is_active());
EXPECT_TRUE(client.pause());
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::INACTIVE,
client.is_active(std::chrono::nanoseconds(1000000000)));
EXPECT_TRUE(client.resume());
EXPECT_TRUE(client.reset());
EXPECT_TRUE(client.shutdown());
}
TEST(LifecycleClientTest, WithoutFixture)
{
auto node = std::make_shared<rclcpp::Node>("lifecycle_manager_test_service_client");
nav2_lifecycle_manager::LifecycleManagerClient client("lifecycle_manager_test", node);
EXPECT_EQ(
nav2_lifecycle_manager::SystemStatus::TIMEOUT,
client.is_active(std::chrono::nanoseconds(1000)));
}
int main(int argc, char ** argv)
{
::testing::InitGoogleTest(&argc, argv);
// initialize ROS
rclcpp::init(argc, argv);
bool all_successful = RUN_ALL_TESTS();
// shutdown ROS
rclcpp::shutdown();
return all_successful;
}