Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add controller node options args to be able to set controller specific node arguments #1713

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions controller_manager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ target_link_libraries(ros2_control_node PRIVATE
if(BUILD_TESTING)
find_package(ament_cmake_gmock REQUIRED)
find_package(ros2_control_test_assets REQUIRED)
find_package(example_interfaces REQUIRED)

# Plugin Libraries that are built and installed for use in testing
add_library(test_controller SHARED test/test_controller/test_controller.cpp)
target_link_libraries(test_controller PUBLIC controller_manager)
ament_target_dependencies(test_controller PUBLIC example_interfaces)
target_compile_definitions(test_controller PRIVATE "CONTROLLER_MANAGER_BUILDING_DLL")
pluginlib_export_plugin_description_file(controller_interface test/test_controller/test_controller.xml)
install(
Expand Down
16 changes: 16 additions & 0 deletions controller_manager/controller_manager/spawner.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
load_controller,
switch_controllers,
unload_controller,
set_controller_parameters,
set_controller_parameters_from_param_file,
bcolors,
)
Expand Down Expand Up @@ -122,6 +123,12 @@ def main(args=None):
action="store_true",
required=False,
)
parser.add_argument(
"--controller-ros-args",
help="The --ros-args to be passed to the controller node for remapping topics etc",
default=None,
required=False,
)

command_line_args = rclpy.utilities.remove_ros_args(args=sys.argv)[1:]
args = parser.parse_args(command_line_args)
Expand Down Expand Up @@ -172,6 +179,15 @@ def main(args=None):
+ bcolors.ENDC
)
else:
if controller_ros_args := args.controller_ros_args:
if not set_controller_parameters(
node,
controller_manager_name,
controller_name,
"node_options_args",
controller_ros_args.split(),
):
return 1
if param_file:
if not set_controller_parameters_from_param_file(
node,
Expand Down
11 changes: 5 additions & 6 deletions controller_manager/doc/userdoc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ There are two scripts to interact with controller manager from launch files:
.. code-block:: console

$ ros2 run controller_manager spawner -h
usage: spawner [-h] [-c CONTROLLER_MANAGER] [-p PARAM_FILE] [-n NAMESPACE] [--load-only] [--inactive] [-t CONTROLLER_TYPE] [-u]
[--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT]
controller_name
usage: spawner [-h] [-c CONTROLLER_MANAGER] [-p PARAM_FILE] [-n NAMESPACE] [--load-only] [--inactive] [-u] [--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT]
[--activate-as-group] [--controller-ros-args CONTROLLER_ROS_ARGS]
controller_names [controller_names ...]

positional arguments:
controller_names List of controllers
Expand All @@ -166,9 +166,8 @@ There are two scripts to interact with controller manager from launch files:
--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT
Time to wait for the controller manager
--activate-as-group Activates all the parsed controllers list together instead of one by one. Useful for activating all chainable controllers altogether
--fallback_controllers FALLBACK_CONTROLLERS [FALLBACK_CONTROLLERS ...]
Fallback controllers list are activated as a fallback strategy when the spawned controllers fail. When the argument is provided, it takes precedence over the fallback_controllers list in the
param file
--controller-ros-args CONTROLLER_ROS_ARGS
The --ros-args to be passed to the controller node for remapping topics etc


``unspawner``
Expand Down
1 change: 1 addition & 0 deletions controller_manager/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<test_depend>python3-coverage</test_depend>
<test_depend>hardware_interface_testing</test_depend>
<test_depend>ros2_control_test_assets</test_depend>
<test_depend>example_interfaces</test_depend>

<export>
<build_type>ament_cmake</build_type>
Expand Down
35 changes: 35 additions & 0 deletions controller_manager/src/controller_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,17 @@ controller_interface::ControllerInterfaceBaseSharedPtr ControllerManager::load_c
controller_spec.info.fallback_controllers_names = fallback_controllers;
}

const std::string node_options_args_param = controller_name + ".node_options_args";
std::vector<std::string> node_options_args;
if (!has_parameter(node_options_args_param))
{
declare_parameter(node_options_args_param, rclcpp::ParameterType::PARAMETER_STRING_ARRAY);
}
if (get_parameter(node_options_args_param, node_options_args) && !node_options_args.empty())
{
controller_spec.info.node_options_args = node_options_args;
}

return add_controller_impl(controller_spec);
}

Expand Down Expand Up @@ -2857,6 +2868,18 @@ rclcpp::NodeOptions ControllerManager::determine_controller_node_options(
node_options_arguments.push_back(arg);
}

// Add deprecation notice if the arguments are from the controller_manager node
if (
check_for_element(node_options_arguments, RCL_REMAP_FLAG) ||
check_for_element(node_options_arguments, RCL_SHORT_REMAP_FLAG))
{
RCLCPP_WARN(
get_logger(),
"The use of remapping arguments to the controller_manager node is deprecated. Please use the "
"'--controller-ros-args' argument of the spawner to pass remapping arguments to the "
"controller node.");
}

if (controller.info.parameters_file.has_value())
{
if (!check_for_element(node_options_arguments, RCL_ROS_ARGS_FLAG))
Expand All @@ -2879,6 +2902,18 @@ rclcpp::NodeOptions ControllerManager::determine_controller_node_options(
node_options_arguments.push_back("use_sim_time:=true");
}

// Add options parsed through the spawner
saikishor marked this conversation as resolved.
Show resolved Hide resolved
if (
!controller.info.node_options_args.empty() &&
!check_for_element(controller.info.node_options_args, RCL_ROS_ARGS_FLAG))
{
node_options_arguments.push_back(RCL_ROS_ARGS_FLAG);
}
for (const auto & arg : controller.info.node_options_args)
{
node_options_arguments.push_back(arg);
}

std::string arguments;
arguments.reserve(1000);
for (const auto & arg : node_options_arguments)
Expand Down
11 changes: 11 additions & 0 deletions controller_manager/test/test_controller/test_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ CallbackReturn TestController::on_init() { return CallbackReturn::SUCCESS; }

CallbackReturn TestController::on_configure(const rclcpp_lifecycle::State & /*previous_state*/)
{
const std::string service_name = get_node()->get_name() + std::string("/set_bool");
service_ = get_node()->create_service<example_interfaces::srv::SetBool>(
service_name,
[this](
const std::shared_ptr<example_interfaces::srv::SetBool::Request> request,
std::shared_ptr<example_interfaces::srv::SetBool::Response> response)
{
RCLCPP_INFO_STREAM(
get_node()->get_logger(), "Setting response to " << std::boolalpha << request->data);
response->success = request->data;
});
return CallbackReturn::SUCCESS;
}

Expand Down
6 changes: 4 additions & 2 deletions controller_manager/test/test_controller/test_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
#ifndef TEST_CONTROLLER__TEST_CONTROLLER_HPP_
#define TEST_CONTROLLER__TEST_CONTROLLER_HPP_

#include <memory>
#include <string>
#include <vector>

#include "controller_interface/controller_interface.hpp"
#include "controller_manager/visibility_control.h"
#include "example_interfaces/srv/set_bool.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"

namespace test_controller
Expand Down Expand Up @@ -68,8 +71,7 @@ class TestController : public controller_interface::ControllerInterface
CONTROLLER_MANAGER_PUBLIC
std::vector<double> get_state_interface_data() const;

const std::string & getRobotDescription() const;

rclcpp::Service<example_interfaces::srv::SetBool>::SharedPtr service_;
unsigned int internal_counter = 0;
bool simulate_cleanup_failure = false;
// Variable where we store when cleanup was called, pointer because the controller
Expand Down
39 changes: 39 additions & 0 deletions controller_manager/test/test_spawner_unspawner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,45 @@ TEST_F(TestLoadController, spawner_with_many_controllers)
}
}

TEST_F(TestLoadController, test_spawner_parsed_controller_ros_args)
{
ControllerManagerRunner cm_runner(this);
cm_->set_parameter(rclcpp::Parameter("ctrl_1.type", test_controller::TEST_CONTROLLER_CLASS_NAME));
cm_->set_parameter(rclcpp::Parameter("ctrl_2.type", test_controller::TEST_CONTROLLER_CLASS_NAME));
std::stringstream ss;

EXPECT_EQ(call_spawner("ctrl_1 -c test_controller_manager"), 0);
ASSERT_EQ(cm_->get_loaded_controllers().size(), 1ul);

// Now as the controller is active, we can call check for the service
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("set_bool_client");
auto set_bool_service = node->create_client<example_interfaces::srv::SetBool>("/set_bool");
ASSERT_FALSE(set_bool_service->wait_for_service(std::chrono::seconds(2)));
ASSERT_FALSE(set_bool_service->service_is_ready());
// Now check the service availability in the controller namespace
auto ctrl_1_set_bool_service =
node->create_client<example_interfaces::srv::SetBool>("/ctrl_1/set_bool");
ASSERT_TRUE(ctrl_1_set_bool_service->wait_for_service(std::chrono::seconds(2)));
ASSERT_TRUE(ctrl_1_set_bool_service->service_is_ready());

// Now test the remapping of the service name with the controller_ros_args
EXPECT_EQ(
call_spawner(
"ctrl_2 -c test_controller_manager --controller-ros-args '-r /ctrl_2/set_bool:=/set_bool'"),
0);

ASSERT_EQ(cm_->get_loaded_controllers().size(), 2ul);

// Now as the controller is active, we can call check for the remapped service
ASSERT_TRUE(set_bool_service->wait_for_service(std::chrono::seconds(2)));
ASSERT_TRUE(set_bool_service->service_is_ready());
// Now check the service availability in the controller namespace
auto ctrl_2_set_bool_service =
node->create_client<example_interfaces::srv::SetBool>("/ctrl_2/set_bool");
ASSERT_FALSE(ctrl_2_set_bool_service->wait_for_service(std::chrono::seconds(2)));
ASSERT_FALSE(ctrl_2_set_bool_service->service_is_ready());
}

class TestLoadControllerWithoutRobotDescription
: public ControllerManagerFixture<controller_manager::ControllerManager>
{
Expand Down
1 change: 1 addition & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ controller_manager
* The support for the ``description`` parameter for loading the URDF was removed (`#1358 <https://github.com/ros-controls/ros2_control/pull/1358>`_).
* The ``--controller-type`` or ``-t`` spawner arg is removed. Now the controller type is defined in the controller configuration file with ``type`` field (`#1639 <https://github.com/ros-controls/ros2_control/pull/1639>`_).
* The ``--namespace`` or ``-n`` spawner arg is deprecated. Now the spawner namespace can be defined using the ROS 2 standard way (`#1640 <https://github.com/ros-controls/ros2_control/pull/1640>`_).
* The spawner now supports the ``--controller-ros-args`` argument to pass the ROS 2 node arguments to the controller node to be able to remap topics, services and etc (`#1713 <https://github.com/ros-controls/ros2_control/pull/1713>`_).

hardware_interface
******************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ struct ControllerInfo

/// List of fallback controller names to be activated if this controller fails.
std::vector<std::string> fallback_controllers_names;

/// Controller node options arguments
std::vector<std::string> node_options_args;
};

} // namespace hardware_interface
Expand Down
Loading