Skip to content

Commit b2d9bf7

Browse files
authored
Add common reusable helper methods header (#2099)
1 parent bd1dffa commit b2d9bf7

File tree

6 files changed

+268
-50
lines changed

6 files changed

+268
-50
lines changed

controller_interface/include/controller_interface/helpers.hpp

+8-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include <string>
2020
#include <vector>
2121

22+
// Add hardware interface helpers here, so all inherited controllers can use them
23+
#include "hardware_interface/helpers.hpp"
24+
2225
namespace controller_interface
2326
{
2427
/// Reorder interfaces with references according to joint names or full interface names.
@@ -79,13 +82,12 @@ inline bool interface_list_contains_interface_type(
7982
}
8083

8184
template <typename T>
82-
void add_element_to_list(std::vector<T> & list, const T & element)
85+
[[deprecated(
86+
"Use ros2_control::add_item method instead. This method will be removed by the ROS 2 Kilted "
87+
"Kaiju release.")]] void
88+
add_element_to_list(std::vector<T> & list, const T & element)
8389
{
84-
if (std::find(list.begin(), list.end(), element) == list.end())
85-
{
86-
// Only add to the list if it doesn't exist
87-
list.push_back(element);
88-
}
90+
ros2_control::add_item(list, element);
8991
}
9092

9193
} // namespace controller_interface

controller_interface/src/chainable_controller_interface.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ ChainableControllerInterface::export_state_interfaces()
9090
state_interfaces_ptrs_vec.clear();
9191
throw std::runtime_error(error_msg);
9292
}
93-
ordered_exported_state_interfaces_.push_back(state_interface);
94-
add_element_to_list(exported_state_interface_names_, interface_name);
93+
ros2_control::add_item(ordered_exported_state_interfaces_, state_interface);
94+
ros2_control::add_item(exported_state_interface_names_, interface_name);
9595
state_interfaces_ptrs_vec.push_back(
9696
std::const_pointer_cast<const hardware_interface::StateInterface>(state_interface));
9797
}
@@ -176,8 +176,8 @@ ChainableControllerInterface::export_reference_interfaces()
176176
reference_interfaces_ptrs_vec.clear();
177177
throw std::runtime_error(error_msg);
178178
}
179-
ordered_exported_reference_interfaces_.push_back(reference_interface);
180-
add_element_to_list(exported_reference_interface_names_, interface_name);
179+
ros2_control::add_item(ordered_exported_reference_interfaces_, reference_interface);
180+
ros2_control::add_item(exported_reference_interface_names_, interface_name);
181181
reference_interfaces_ptrs_vec.push_back(reference_interface);
182182
}
183183

controller_manager/src/controller_manager.cpp

+15-40
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "controller_interface/controller_interface_base.hpp"
2424
#include "controller_manager_msgs/msg/hardware_component_state.hpp"
25+
#include "hardware_interface/helpers.hpp"
2526
#include "hardware_interface/introspection.hpp"
2627
#include "hardware_interface/types/lifecycle_state_names.hpp"
2728
#include "lifecycle_msgs/msg/state.hpp"
@@ -144,26 +145,6 @@ bool is_interface_a_chained_interface(
144145
return true;
145146
}
146147

147-
template <typename T>
148-
void add_element_to_list(std::vector<T> & list, const T & element)
149-
{
150-
if (std::find(list.begin(), list.end(), element) == list.end())
151-
{
152-
// Only add to the list if it doesn't exist
153-
list.push_back(element);
154-
}
155-
}
156-
157-
template <typename T>
158-
void remove_element_from_list(std::vector<T> & list, const T & element)
159-
{
160-
auto itr = std::find(list.begin(), list.end(), element);
161-
if (itr != list.end())
162-
{
163-
list.erase(itr);
164-
}
165-
}
166-
167148
void controller_chain_spec_cleanup(
168149
std::unordered_map<std::string, controller_manager::ControllerChainSpec> & ctrl_chain_spec,
169150
const std::string & controller)
@@ -172,11 +153,11 @@ void controller_chain_spec_cleanup(
172153
const auto preceding_controllers = ctrl_chain_spec[controller].preceding_controllers;
173154
for (const auto & flwg_ctrl : following_controllers)
174155
{
175-
remove_element_from_list(ctrl_chain_spec[flwg_ctrl].preceding_controllers, controller);
156+
ros2_control::remove_item(ctrl_chain_spec[flwg_ctrl].preceding_controllers, controller);
176157
}
177158
for (const auto & preced_ctrl : preceding_controllers)
178159
{
179-
remove_element_from_list(ctrl_chain_spec[preced_ctrl].following_controllers, controller);
160+
ros2_control::remove_item(ctrl_chain_spec[preced_ctrl].following_controllers, controller);
180161
}
181162
ctrl_chain_spec.erase(controller);
182163
}
@@ -209,7 +190,7 @@ void get_active_controllers_using_command_interfaces_of_controller(
209190
is_controller_active(controller.c) &&
210191
std::find(ctrl_cmd_itfs.begin(), ctrl_cmd_itfs.end(), cmd_itf) != ctrl_cmd_itfs.end())
211192
{
212-
add_element_to_list(controllers_using_command_interfaces, controller.info.name);
193+
ros2_control::add_item(controllers_using_command_interfaces, controller.info.name);
213194
}
214195
}
215196
}
@@ -359,12 +340,6 @@ void get_controller_list_command_interfaces(
359340
}
360341
}
361342
}
362-
template <typename Collection>
363-
[[nodiscard]] bool is_unique(Collection collection)
364-
{
365-
std::sort(collection.begin(), collection.end());
366-
return std::adjacent_find(collection.cbegin(), collection.cend()) == collection.cend();
367-
}
368343
} // namespace
369344

370345
namespace controller_manager
@@ -1170,7 +1145,7 @@ controller_interface::return_type ControllerManager::configure_controller(
11701145
const auto state_itfs = controller->state_interface_configuration().names;
11711146

11721147
// Check if the cmd_itfs and the state_itfs are unique
1173-
if (!is_unique(cmd_itfs))
1148+
if (!ros2_control::is_unique(cmd_itfs))
11741149
{
11751150
std::string cmd_itfs_str = std::accumulate(
11761151
std::next(cmd_itfs.begin()), cmd_itfs.end(), cmd_itfs.front(),
@@ -1184,7 +1159,7 @@ controller_interface::return_type ControllerManager::configure_controller(
11841159
return controller_interface::return_type::ERROR;
11851160
}
11861161

1187-
if (!is_unique(state_itfs))
1162+
if (!ros2_control::is_unique(state_itfs))
11881163
{
11891164
std::string state_itfs_str = std::accumulate(
11901165
std::next(state_itfs.begin()), state_itfs.end(), state_itfs.front(),
@@ -1203,11 +1178,11 @@ controller_interface::return_type ControllerManager::configure_controller(
12031178
controller_manager::ControllersListIterator ctrl_it;
12041179
if (is_interface_a_chained_interface(cmd_itf, controllers, ctrl_it))
12051180
{
1206-
add_element_to_list(
1181+
ros2_control::add_item(
12071182
controller_chain_spec_[controller_name].following_controllers, ctrl_it->info.name);
1208-
add_element_to_list(
1183+
ros2_control::add_item(
12091184
controller_chain_spec_[ctrl_it->info.name].preceding_controllers, controller_name);
1210-
add_element_to_list(
1185+
ros2_control::add_item(
12111186
controller_chained_reference_interfaces_cache_[ctrl_it->info.name], controller_name);
12121187
}
12131188
}
@@ -1217,11 +1192,11 @@ controller_interface::return_type ControllerManager::configure_controller(
12171192
controller_manager::ControllersListIterator ctrl_it;
12181193
if (is_interface_a_chained_interface(state_itf, controllers, ctrl_it))
12191194
{
1220-
add_element_to_list(
1195+
ros2_control::add_item(
12211196
controller_chain_spec_[controller_name].preceding_controllers, ctrl_it->info.name);
1222-
add_element_to_list(
1197+
ros2_control::add_item(
12231198
controller_chain_spec_[ctrl_it->info.name].following_controllers, controller_name);
1224-
add_element_to_list(
1199+
ros2_control::add_item(
12251200
controller_chained_state_interfaces_cache_[ctrl_it->info.name], controller_name);
12261201
}
12271202
}
@@ -2857,7 +2832,7 @@ controller_interface::return_type ControllerManager::update(
28572832
rt_buffer_.activate_controllers_using_interfaces_list.begin(),
28582833
rt_buffer_.activate_controllers_using_interfaces_list.end(),
28592834
[this](const std::string & controller)
2860-
{ add_element_to_list(rt_buffer_.deactivate_controllers_list, controller); });
2835+
{ ros2_control::add_item(rt_buffer_.deactivate_controllers_list, controller); });
28612836

28622837
// Retrieve the interfaces to start and stop from the hardware end
28632838
perform_hardware_command_mode_change(
@@ -3593,7 +3568,7 @@ void ControllerManager::controller_activity_diagnostic_callback(
35933568
params_->diagnostics.threshold.controllers.periodicity.standard_deviation.error)
35943569
{
35953570
level = diagnostic_msgs::msg::DiagnosticStatus::ERROR;
3596-
add_element_to_list(bad_periodicity_async_controllers, controllers[i].info.name);
3571+
ros2_control::add_item(bad_periodicity_async_controllers, controllers[i].info.name);
35973572
}
35983573
else if (
35993574
periodicity_error >
@@ -3605,7 +3580,7 @@ void ControllerManager::controller_activity_diagnostic_callback(
36053580
{
36063581
level = diagnostic_msgs::msg::DiagnosticStatus::WARN;
36073582
}
3608-
add_element_to_list(bad_periodicity_async_controllers, controllers[i].info.name);
3583+
ros2_control::add_item(bad_periodicity_async_controllers, controllers[i].info.name);
36093584
}
36103585
}
36113586
const double max_exp_exec_time = is_async ? 1.e6 / controllers[i].c->get_update_rate() : 0.0;

hardware_interface/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ if(BUILD_TESTING)
7373
target_link_libraries(test_joint_handle hardware_interface)
7474
ament_target_dependencies(test_joint_handle rcpputils)
7575

76+
# Test helper methods
77+
ament_add_gmock(test_helpers test/test_helpers.cpp)
78+
target_link_libraries(test_helpers hardware_interface)
79+
7680
ament_add_gmock(test_component_interfaces test/test_component_interfaces.cpp)
7781
target_link_libraries(test_component_interfaces hardware_interface)
7882
ament_target_dependencies(test_component_interfaces ros2_control_test_assets)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright 2025 PAL Robotics S.L.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef HARDWARE_INTERFACE__HELPERS_HPP_
16+
#define HARDWARE_INTERFACE__HELPERS_HPP_
17+
18+
#include <algorithm>
19+
#include <functional>
20+
#include <map>
21+
#include <string>
22+
#include <unordered_map>
23+
#include <vector>
24+
25+
namespace ros2_control
26+
{
27+
28+
/**
29+
* @brief Get the iterator to the item in the container.
30+
* @param container The container to search in.
31+
* @param item The item to search for.
32+
* @return Iterator to the item in the container.
33+
*
34+
* @note Only std::vector, std::map and std::unordered_map are supported.
35+
*/
36+
template <typename Container, typename T>
37+
[[nodiscard]] auto get_item_iterator(const Container & container, const T & item)
38+
{
39+
if constexpr (std::is_same_v<Container, std::vector<T>>)
40+
{
41+
return std::find(container.begin(), container.end(), item);
42+
}
43+
else if constexpr (
44+
std::is_same_v<Container, std::map<T, typename Container::mapped_type>> ||
45+
std::is_same_v<Container, std::unordered_map<T, typename Container::mapped_type>>)
46+
{
47+
return container.find(item);
48+
}
49+
else
50+
{
51+
using is_vector = std::is_same<Container, std::vector<T>>;
52+
using is_map = std::is_same<Container, std::map<T, typename Container::mapped_type>>;
53+
using is_unordered_map =
54+
std::is_same<Container, std::unordered_map<T, typename Container::mapped_type>>;
55+
// Handle unsupported container types
56+
static_assert(
57+
is_vector::value || is_map::value || is_unordered_map::value,
58+
"Only std::vector, std::map and std::unordered_map are supported.");
59+
}
60+
}
61+
62+
/**
63+
* @brief Check if the item is in the container.
64+
* @param container The container to search in.
65+
* @param item The item to search for.
66+
* @return True if the item is in the container, false otherwise.
67+
*/
68+
template <typename Container, typename T>
69+
[[nodiscard]] bool has_item(const Container & container, const T & item)
70+
{
71+
return get_item_iterator(container, item) != container.end();
72+
}
73+
74+
/**
75+
* @brief Add the item to the container if it is not already in it.
76+
* @param vector The container to add the item to.
77+
* @param item The item to add.
78+
*/
79+
template <typename T>
80+
void add_item(std::vector<T> & vector, const T & item)
81+
{
82+
if (!has_item(vector, item))
83+
{
84+
vector.push_back(item);
85+
}
86+
}
87+
88+
/**
89+
* @brief Remove the item from the container if it is in it.
90+
* @param container The container to remove the item from.
91+
* @param item The item to remove.
92+
* @return True if the item was removed, false otherwise. If the item was not in the container, it
93+
* returns false.
94+
*/
95+
template <typename Container>
96+
[[nodiscard]] bool remove_item(Container & container, typename Container::const_reference item)
97+
{
98+
auto it = get_item_iterator(container, item);
99+
if (it != container.end())
100+
{
101+
container.erase(it);
102+
return true;
103+
}
104+
return false;
105+
}
106+
107+
/**
108+
* @brief Check if the container has any of the items.
109+
* @param container The container to search in.
110+
* @param items The items to search for.
111+
* @return True if the container has any of the items, false otherwise.
112+
*/
113+
template <typename Container>
114+
[[nodiscard]] bool has_any_item(
115+
const Container & container, const std::vector<typename Container::key_type> & items)
116+
{
117+
return std::any_of(
118+
items.begin(), items.end(),
119+
[&container](const typename Container::key_type & item) { return has_item(container, item); });
120+
}
121+
122+
/**
123+
* @brief Check if the container has any of the items.
124+
* @param container The container to search in.
125+
* @param items The items to search for.
126+
* @return True if the container has any of the items, false otherwise.
127+
*/
128+
template <typename T>
129+
[[nodiscard]] bool has_any_item(const std::vector<T> & container, const std::vector<T> & items)
130+
{
131+
return std::any_of(
132+
items.begin(), items.end(), [&container](const T & item) { return has_item(container, item); });
133+
}
134+
135+
/**
136+
* @brief Check if the container has all of the items.
137+
* @param container The container to search in.
138+
* @param items The items to search for.
139+
* @return True if the container has all of the items, false otherwise.
140+
*/
141+
template <typename Container>
142+
[[nodiscard]] bool has_all_items(
143+
const Container & container, const std::vector<typename Container::key_type> & items)
144+
{
145+
return std::all_of(
146+
items.begin(), items.end(),
147+
[&container](const typename Container::key_type & item) { return has_item(container, item); });
148+
}
149+
150+
/**
151+
* @brief Check if the container has all of the items.
152+
* @param container The container to search in.
153+
* @param items The items to search for.
154+
* @return True if the container has all of the items, false otherwise.
155+
*/
156+
template <typename T>
157+
[[nodiscard]] bool has_all_items(const std::vector<T> & container, const std::vector<T> & items)
158+
{
159+
return std::all_of(
160+
items.begin(), items.end(), [&container](const T & item) { return has_item(container, item); });
161+
}
162+
163+
/**
164+
* @brief Check if the container has all unique items.
165+
* @param container The container to search in.
166+
* @return True if the container has all unique items, false otherwise.
167+
*
168+
* @note The container must be sortable.
169+
* @note The container must have the begin() and end() methods.
170+
*/
171+
template <typename Container>
172+
[[nodiscard]] bool is_unique(Container container)
173+
{
174+
std::sort(container.begin(), container.end());
175+
return std::adjacent_find(container.cbegin(), container.cend()) == container.cend();
176+
}
177+
178+
} // namespace ros2_control
179+
180+
#endif // HARDWARE_INTERFACE__HELPERS_HPP_

0 commit comments

Comments
 (0)