Skip to content

Commit 7cb04b1

Browse files
authored
Merge branch 'master' into fix/exclusive_hw_interface_switching
2 parents bc6ce45 + fbfc01d commit 7cb04b1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+826
-216
lines changed

controller_interface/CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Changelog for package controller_interface
33
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44

5+
4.24.0 (2025-01-13)
6+
-------------------
7+
* Trigger shutdown transition in destructor (`#1979 <https://github.com/ros-controls/ros2_control/issues/1979>`_)
8+
* Contributors: Christoph Fröhlich
9+
510
4.23.0 (2024-12-29)
611
-------------------
712
* Remove boilerplate visibility macros (`#1972 <https://github.com/ros-controls/ros2_control/issues/1972>`_)

controller_interface/CMakeLists.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ if(BUILD_TESTING)
4545
)
4646

4747
ament_add_gmock(test_controller_with_options test/test_controller_with_options.cpp)
48-
install(FILES test/test_controller_node_options.yaml
49-
DESTINATION test)
5048
target_link_libraries(test_controller_with_options
5149
controller_interface
5250
)
51+
target_compile_definitions(
52+
test_controller_with_options
53+
PRIVATE PARAMETERS_FILE_PATH="${CMAKE_CURRENT_LIST_DIR}/test/")
5354

5455
ament_add_gmock(test_chainable_controller_interface test/test_chainable_controller_interface.cpp)
5556
target_link_libraries(test_chainable_controller_interface
@@ -86,6 +87,12 @@ if(BUILD_TESTING)
8687
ament_target_dependencies(test_pose_sensor
8788
geometry_msgs
8889
)
90+
91+
ament_add_gmock(test_gps_sensor test/test_gps_sensor.cpp)
92+
target_link_libraries(test_gps_sensor
93+
controller_interface
94+
hardware_interface::hardware_interface
95+
)
8996
endif()
9097

9198
install(

controller_interface/include/controller_interface/controller_interface_base.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
9999
public:
100100
ControllerInterfaceBase() = default;
101101

102-
virtual ~ControllerInterfaceBase() = default;
102+
virtual ~ControllerInterfaceBase();
103103

104104
/// Get configuration for controller's required command interfaces.
105105
/**
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2025 ros2_control development team
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 SEMANTIC_COMPONENTS__GPS_SENSOR_HPP_
16+
#define SEMANTIC_COMPONENTS__GPS_SENSOR_HPP_
17+
18+
#include <array>
19+
#include <string>
20+
21+
#include "semantic_components/semantic_component_interface.hpp"
22+
#include "sensor_msgs/msg/nav_sat_fix.hpp"
23+
24+
namespace semantic_components
25+
{
26+
27+
enum class GPSSensorOption
28+
{
29+
WithCovariance,
30+
WithoutCovariance
31+
};
32+
33+
template <GPSSensorOption sensor_option>
34+
class GPSSensor : public SemanticComponentInterface<sensor_msgs::msg::NavSatFix>
35+
{
36+
public:
37+
static_assert(
38+
sensor_option == GPSSensorOption::WithCovariance ||
39+
sensor_option == GPSSensorOption::WithoutCovariance,
40+
"Invalid GPSSensorOption");
41+
explicit GPSSensor(const std::string & name)
42+
: SemanticComponentInterface(
43+
name, {{name + "/" + "status"},
44+
{name + "/" + "service"},
45+
{name + "/" + "latitude"},
46+
{name + "/" + "longitude"},
47+
{name + "/" + "altitude"}})
48+
{
49+
if constexpr (sensor_option == GPSSensorOption::WithCovariance)
50+
{
51+
interface_names_.emplace_back(name + "/" + "latitude_covariance");
52+
interface_names_.emplace_back(name + "/" + "longitude_covariance");
53+
interface_names_.emplace_back(name + "/" + "altitude_covariance");
54+
}
55+
}
56+
57+
/**
58+
* Return GPS's status e.g. fix/no_fix
59+
*
60+
* \return Status
61+
*/
62+
int8_t get_status() const { return static_cast<int8_t>(state_interfaces_[0].get().get_value()); }
63+
64+
/**
65+
* Return service used by GPS e.g. fix/no_fix
66+
*
67+
* \return Service
68+
*/
69+
uint16_t get_service() const
70+
{
71+
return static_cast<uint16_t>(state_interfaces_[1].get().get_value());
72+
}
73+
74+
/**
75+
* Return latitude reported by a GPS
76+
*
77+
* \return Latitude.
78+
*/
79+
double get_latitude() const { return state_interfaces_[2].get().get_value(); }
80+
81+
/**
82+
* Return longitude reported by a GPS
83+
*
84+
* \return Longitude.
85+
*/
86+
double get_longitude() const { return state_interfaces_[3].get().get_value(); }
87+
88+
/**
89+
* Return altitude reported by a GPS
90+
*
91+
* \return Altitude.
92+
*/
93+
double get_altitude() const { return state_interfaces_[4].get().get_value(); }
94+
95+
/**
96+
* Return covariance reported by a GPS.
97+
*
98+
* \return Covariance array.
99+
*/
100+
template <
101+
typename U = void,
102+
typename = std::enable_if_t<sensor_option == GPSSensorOption::WithCovariance, U>>
103+
const std::array<double, 9> & get_covariance()
104+
{
105+
covariance_[0] = state_interfaces_[5].get().get_value();
106+
covariance_[4] = state_interfaces_[6].get().get_value();
107+
covariance_[8] = state_interfaces_[7].get().get_value();
108+
return covariance_;
109+
}
110+
111+
/**
112+
* Fills a NavSatFix message from the current values.
113+
*/
114+
bool get_values_as_message(sensor_msgs::msg::NavSatFix & message)
115+
{
116+
message.status.status = get_status();
117+
message.status.service = get_service();
118+
message.latitude = get_latitude();
119+
message.longitude = get_longitude();
120+
message.altitude = get_altitude();
121+
122+
if constexpr (sensor_option == GPSSensorOption::WithCovariance)
123+
{
124+
message.position_covariance = get_covariance();
125+
}
126+
127+
return true;
128+
}
129+
130+
private:
131+
std::array<double, 9> covariance_{{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}};
132+
};
133+
134+
} // namespace semantic_components
135+
136+
#endif // SEMANTIC_COMPONENTS__GPS_SENSOR_HPP_

controller_interface/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
33
<package format="2">
44
<name>controller_interface</name>
5-
<version>4.23.0</version>
5+
<version>4.24.0</version>
66
<description>Description of controller_interface</description>
77
<maintainer email="[email protected]">Bence Magyar</maintainer>
88
<maintainer email="[email protected]">Denis Štogl</maintainer>

controller_interface/src/controller_interface_base.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@
2222

2323
namespace controller_interface
2424
{
25+
ControllerInterfaceBase::~ControllerInterfaceBase()
26+
{
27+
// check if node is initialized and we still have a valid context
28+
if (
29+
node_.get() &&
30+
get_lifecycle_state().id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED &&
31+
rclcpp::ok())
32+
{
33+
RCLCPP_DEBUG(
34+
get_node()->get_logger(),
35+
"Calling shutdown transition of controller node '%s' due to destruction.",
36+
get_node()->get_name());
37+
node_->shutdown();
38+
}
39+
}
40+
2541
return_type ControllerInterfaceBase::init(
2642
const std::string & controller_name, const std::string & urdf, unsigned int cm_update_rate,
2743
const std::string & node_namespace, const rclcpp::NodeOptions & node_options)
@@ -52,6 +68,11 @@ return_type ControllerInterfaceBase::init(
5268
break;
5369
case LifecycleNodeInterface::CallbackReturn::ERROR:
5470
case LifecycleNodeInterface::CallbackReturn::FAILURE:
71+
RCLCPP_DEBUG(
72+
get_node()->get_logger(),
73+
"Calling shutdown transition of controller node '%s' due to init failure.",
74+
get_node()->get_name());
75+
node_->shutdown();
5576
return return_type::ERROR;
5677
}
5778

@@ -158,6 +179,10 @@ void ControllerInterfaceBase::release_interfaces()
158179

159180
const rclcpp_lifecycle::State & ControllerInterfaceBase::get_lifecycle_state() const
160181
{
182+
if (!node_.get())
183+
{
184+
throw std::runtime_error("Lifecycle node hasn't been initialized yet!");
185+
}
161186
return node_->get_current_state();
162187
}
163188

controller_interface/test/test_controller_interface.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,21 @@ TEST(TestableControllerInterface, init)
3636
rclcpp::init(argc, argv);
3737

3838
TestableControllerInterface controller;
39+
const TestableControllerInterface & const_controller = controller;
3940

4041
// try to get node when not initialized
4142
ASSERT_THROW(controller.get_node(), std::runtime_error);
43+
ASSERT_THROW(const_controller.get_node(), std::runtime_error);
44+
ASSERT_THROW(controller.get_lifecycle_state(), std::runtime_error);
4245

4346
// initialize, create node
4447
const auto node_options = controller.define_custom_node_options();
4548
ASSERT_EQ(
4649
controller.init(TEST_CONTROLLER_NAME, "", 10.0, "", node_options),
4750
controller_interface::return_type::OK);
4851
ASSERT_NO_THROW(controller.get_node());
52+
ASSERT_NO_THROW(const_controller.get_node());
53+
ASSERT_NO_THROW(controller.get_lifecycle_state());
4954

5055
// update_rate is set to controller_manager's rate
5156
ASSERT_EQ(controller.get_update_rate(), 10u);
@@ -54,6 +59,7 @@ TEST(TestableControllerInterface, init)
5459
controller.configure();
5560
ASSERT_EQ(controller.get_update_rate(), 10u);
5661

62+
controller.get_node()->shutdown();
5763
rclcpp::shutdown();
5864
}
5965

@@ -80,6 +86,8 @@ TEST(TestableControllerInterface, setting_negative_update_rate_in_configure)
8086
// The configure should fail and the update rate should stay the same
8187
ASSERT_EQ(controller.configure().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED);
8288
ASSERT_EQ(controller.get_update_rate(), 1000u);
89+
90+
controller.get_node()->shutdown();
8391
rclcpp::shutdown();
8492
}
8593

@@ -149,6 +157,7 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
149157
ASSERT_EQ(controller.get_update_rate(), 623u);
150158

151159
executor->cancel();
160+
controller.get_node()->shutdown();
152161
rclcpp::shutdown();
153162
}
154163

@@ -166,6 +175,8 @@ TEST(TestableControllerInterfaceInitError, init_with_error)
166175
controller.init(TEST_CONTROLLER_NAME, "", 100.0, "", node_options),
167176
controller_interface::return_type::ERROR);
168177

178+
ASSERT_EQ(
179+
controller.get_lifecycle_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
169180
rclcpp::shutdown();
170181
}
171182

@@ -183,6 +194,8 @@ TEST(TestableControllerInterfaceInitFailure, init_with_failure)
183194
controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
184195
controller_interface::return_type::ERROR);
185196

197+
ASSERT_EQ(
198+
controller.get_lifecycle_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
186199
rclcpp::shutdown();
187200
}
188201

0 commit comments

Comments
 (0)