Skip to content

Commit 32ee77b

Browse files
authored
Fix the reloading controller with failed activation (#2544)
1 parent 0d2d781 commit 32ee77b

File tree

5 files changed

+116
-2
lines changed

5 files changed

+116
-2
lines changed

controller_manager/src/controller_manager.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,8 @@ controller_interface::return_type ControllerManager::configure_controller(
12161216
return controller_interface::return_type::ERROR;
12171217
}
12181218
}
1219+
// For cases, when the controller ends up in the unconfigured state from any other state
1220+
cleanup_controller_exported_interfaces(*found_it);
12191221

12201222
try
12211223
{
@@ -4403,7 +4405,7 @@ rclcpp::NodeOptions ControllerManager::determine_controller_node_options(
44034405

44044406
void ControllerManager::cleanup_controller_exported_interfaces(const ControllerSpec & controller)
44054407
{
4406-
if (is_controller_inactive(controller.c) && controller.c->is_chainable())
4408+
if (!is_controller_active(controller.c) && controller.c->is_chainable())
44074409
{
44084410
RCLCPP_DEBUG(
44094411
get_logger(), "Removing controller '%s' exported interfaces from resource manager.",

controller_manager/test/test_chainable_controller/test_chainable_controller.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ CallbackReturn TestChainableController::on_activate(
172172
(*msg)->data = reference_interfaces_;
173173
}
174174

175-
return CallbackReturn::SUCCESS;
175+
return fail_on_activate ? CallbackReturn::ERROR : CallbackReturn::SUCCESS;
176176
}
177177

178178
CallbackReturn TestChainableController::on_cleanup(

controller_manager/test/test_chainable_controller/test_chainable_controller.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class TestChainableController : public controller_interface::ChainableController
8080
std::vector<double> get_state_interface_data() const;
8181

8282
size_t internal_counter;
83+
bool fail_on_activate = false;
8384
controller_interface::InterfaceConfiguration cmd_iface_cfg_;
8485
controller_interface::InterfaceConfiguration state_iface_cfg_;
8586
std::vector<std::string> reference_interface_names_;

controller_manager/test/test_controller_manager.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,3 +2169,102 @@ TEST_F(
21692169
EXPECT_EQ(controller_interface::return_type::ERROR, switch_future.get());
21702170
}
21712171
}
2172+
2173+
class TestControllerManagerChainableControllerFailedActivation
2174+
: public ControllerManagerFixture<controller_manager::ControllerManager>
2175+
{
2176+
};
2177+
2178+
TEST_F(
2179+
TestControllerManagerChainableControllerFailedActivation,
2180+
test_chainable_controllers_failed_activation_and_then_reconfiguring_it)
2181+
{
2182+
const std::string test_controller_name = "test_chainable_controller_2";
2183+
2184+
const auto strictness = controller_manager_msgs::srv::SwitchController::Request::STRICT;
2185+
controller_interface::InterfaceConfiguration cmd_itfs_cfg;
2186+
controller_interface::InterfaceConfiguration itfs_cfg;
2187+
cmd_itfs_cfg.type = controller_interface::interface_configuration_type::INDIVIDUAL;
2188+
itfs_cfg.type = controller_interface::interface_configuration_type::INDIVIDUAL;
2189+
2190+
// controller 4
2191+
auto test_chainable_controller =
2192+
std::make_shared<test_chainable_controller::TestChainableController>();
2193+
cmd_itfs_cfg.names = {"joint1/position"};
2194+
itfs_cfg.names = {"joint2/velocity"};
2195+
test_chainable_controller->set_command_interface_configuration(cmd_itfs_cfg);
2196+
test_chainable_controller->set_state_interface_configuration(itfs_cfg);
2197+
test_chainable_controller->set_reference_interface_names({"modified_joint1/position"});
2198+
test_chainable_controller->set_exported_state_interface_names({"modified_joint2/velocity"});
2199+
test_chainable_controller->fail_on_activate = true;
2200+
2201+
cm_->add_controller(
2202+
test_chainable_controller, test_chainable_controller::TEST_CONTROLLER_NAME,
2203+
test_chainable_controller::TEST_CONTROLLER_CLASS_NAME);
2204+
2205+
EXPECT_EQ(1u, cm_->get_loaded_controllers().size());
2206+
EXPECT_EQ(2, test_chainable_controller.use_count());
2207+
EXPECT_EQ(
2208+
controller_interface::return_type::OK,
2209+
cm_->update(time_, rclcpp::Duration::from_seconds(0.01)));
2210+
2211+
EXPECT_EQ(
2212+
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED,
2213+
test_chainable_controller->get_lifecycle_state().id());
2214+
2215+
// configure controllers
2216+
{
2217+
ControllerManagerRunner cm_runner(this);
2218+
EXPECT_EQ(
2219+
controller_interface::return_type::OK,
2220+
cm_->configure_controller(test_chainable_controller::TEST_CONTROLLER_NAME));
2221+
}
2222+
2223+
EXPECT_EQ(
2224+
lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE,
2225+
test_chainable_controller->get_lifecycle_state().id());
2226+
2227+
std::vector<std::string> start_controllers = {test_chainable_controller::TEST_CONTROLLER_NAME};
2228+
std::vector<std::string> stop_controllers = {};
2229+
{
2230+
auto switch_future = std::async(
2231+
std::launch::async, &controller_manager::ControllerManager::switch_controller, cm_,
2232+
start_controllers, stop_controllers, strictness, true, rclcpp::Duration(0, 0));
2233+
2234+
ASSERT_EQ(std::future_status::timeout, switch_future.wait_for(std::chrono::milliseconds(100)))
2235+
<< "switch_controller should be blocking until next update cycle";
2236+
EXPECT_EQ(
2237+
controller_interface::return_type::OK,
2238+
cm_->update(time_, rclcpp::Duration::from_seconds(0.01)));
2239+
{
2240+
ControllerManagerRunner cm_runner(this);
2241+
EXPECT_EQ(controller_interface::return_type::ERROR, switch_future.get());
2242+
}
2243+
}
2244+
2245+
EXPECT_EQ(
2246+
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED,
2247+
test_chainable_controller->get_lifecycle_state().id());
2248+
// Now, after reconfiguring it, it should work
2249+
test_chainable_controller->fail_on_activate = false;
2250+
2251+
{
2252+
ControllerManagerRunner cm_runner(this);
2253+
EXPECT_EQ(
2254+
controller_interface::return_type::OK,
2255+
cm_->configure_controller(test_chainable_controller::TEST_CONTROLLER_NAME));
2256+
EXPECT_EQ(
2257+
lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE,
2258+
test_chainable_controller->get_lifecycle_state().id());
2259+
2260+
auto switch_future = std::async(
2261+
std::launch::async, &controller_manager::ControllerManager::switch_controller, cm_,
2262+
start_controllers, stop_controllers, strictness, true, rclcpp::Duration(0, 0));
2263+
ASSERT_EQ(std::future_status::ready, switch_future.wait_for(std::chrono::milliseconds(100)))
2264+
<< "switch_controller should be blocking until next update cycle";
2265+
EXPECT_EQ(controller_interface::return_type::OK, switch_future.get());
2266+
EXPECT_EQ(
2267+
lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE,
2268+
test_chainable_controller->get_lifecycle_state().id());
2269+
}
2270+
}

hardware_interface/src/resource_manager.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,12 @@ void ResourceManager::make_controller_exported_state_interfaces_unavailable(
16521652
void ResourceManager::remove_controller_exported_state_interfaces(
16531653
const std::string & controller_name)
16541654
{
1655+
if (
1656+
resource_storage_->controllers_exported_state_interfaces_map_.find(controller_name) ==
1657+
resource_storage_->controllers_exported_state_interfaces_map_.end())
1658+
{
1659+
return;
1660+
}
16551661
auto interface_names =
16561662
resource_storage_->controllers_exported_state_interfaces_map_.at(controller_name);
16571663
resource_storage_->controllers_exported_state_interfaces_map_.erase(controller_name);
@@ -1714,6 +1720,12 @@ void ResourceManager::make_controller_reference_interfaces_unavailable(
17141720
// CM API: Called in "callback/slow"-thread
17151721
void ResourceManager::remove_controller_reference_interfaces(const std::string & controller_name)
17161722
{
1723+
if (
1724+
resource_storage_->controllers_reference_interfaces_map_.find(controller_name) ==
1725+
resource_storage_->controllers_reference_interfaces_map_.end())
1726+
{
1727+
return;
1728+
}
17171729
auto interface_names =
17181730
resource_storage_->controllers_reference_interfaces_map_.at(controller_name);
17191731
resource_storage_->controllers_reference_interfaces_map_.erase(controller_name);

0 commit comments

Comments
 (0)