Skip to content

Commit 8e13d86

Browse files
authored
Publish all castable data types to pal_statistics (#2633) (#2854)
1 parent 95a58fe commit 8e13d86

File tree

4 files changed

+124
-11
lines changed

4 files changed

+124
-11
lines changed

hardware_interface/include/hardware_interface/handle.hpp

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#include <stdexcept>
2929
#include <string>
3030
#include <utility>
31-
#include <variant>
3231

3332
#include "hardware_interface/hardware_info.hpp"
3433
#include "hardware_interface/introspection.hpp"
@@ -61,8 +60,6 @@ std::string get_type_name()
6160
namespace hardware_interface
6261
{
6362

64-
using HANDLE_DATATYPE = std::variant<std::monostate, double, bool>;
65-
6663
/// A handle used to get and set a value on a given interface.
6764
class Handle
6865
{
@@ -313,6 +310,11 @@ class Handle
313310
/// Returns true if the handle data type can be casted to double.
314311
bool is_castable_to_double() const { return data_type_.is_castable_to_double(); }
315312

313+
bool is_valid() const
314+
{
315+
return (value_ptr_ != nullptr) || !std::holds_alternative<std::monostate>(value_);
316+
}
317+
316318
private:
317319
void copy(const Handle & other) noexcept
318320
{
@@ -349,7 +351,7 @@ class Handle
349351
HandleDataType data_type_ = HandleDataType::DOUBLE;
350352
// BEGIN (Handle export change): for backward compatibility
351353
// TODO(Manuel) redeclare as HANDLE_DATATYPE * value_ptr_ if old functionality is removed
352-
double * value_ptr_;
354+
double * value_ptr_ = nullptr;
353355
// END
354356
mutable std::shared_mutex handle_mutex_;
355357

@@ -370,17 +372,35 @@ class StateInterface : public Handle
370372

371373
void registerIntrospection() const
372374
{
373-
if (value_ptr_ || std::holds_alternative<double>(value_))
375+
if (!is_valid())
376+
{
377+
RCLCPP_WARN(
378+
rclcpp::get_logger(get_name()),
379+
"Cannot register state introspection for state interface: %s without a valid value "
380+
"pointer or initialized value.",
381+
get_name().c_str());
382+
return;
383+
}
384+
if (value_ptr_ || data_type_.is_castable_to_double())
374385
{
375386
std::function<double()> f = [this]()
376-
{ return value_ptr_ ? *value_ptr_ : std::get<double>(value_); };
387+
{
388+
if (value_ptr_)
389+
{
390+
return *value_ptr_;
391+
}
392+
else
393+
{
394+
return data_type_.cast_to_double(value_);
395+
}
396+
};
377397
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION("state_interface." + get_name(), f);
378398
}
379399
}
380400

381401
void unregisterIntrospection() const
382402
{
383-
if (value_ptr_ || std::holds_alternative<double>(value_))
403+
if (is_valid() && (value_ptr_ || data_type_.is_castable_to_double()))
384404
{
385405
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION("state_interface." + get_name());
386406
}
@@ -444,10 +464,28 @@ class CommandInterface : public Handle
444464

445465
void registerIntrospection() const
446466
{
447-
if (value_ptr_ || std::holds_alternative<double>(value_))
467+
if (!is_valid())
468+
{
469+
RCLCPP_WARN(
470+
rclcpp::get_logger(get_name()),
471+
"Cannot register command introspection for command interface: %s without a valid value "
472+
"pointer or initialized value.",
473+
get_name().c_str());
474+
return;
475+
}
476+
if (value_ptr_ || data_type_.is_castable_to_double())
448477
{
449478
std::function<double()> f = [this]()
450-
{ return value_ptr_ ? *value_ptr_ : std::get<double>(value_); };
479+
{
480+
if (value_ptr_)
481+
{
482+
return *value_ptr_;
483+
}
484+
else
485+
{
486+
return data_type_.cast_to_double(value_);
487+
}
488+
};
451489
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name(), f);
452490
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION(
453491
"command_interface." + get_name() + ".is_limited", &is_command_limited_);
@@ -456,7 +494,7 @@ class CommandInterface : public Handle
456494

457495
void unregisterIntrospection() const
458496
{
459-
if (value_ptr_ || std::holds_alternative<double>(value_))
497+
if (is_valid() && (value_ptr_ || data_type_.is_castable_to_double()))
460498
{
461499
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name());
462500
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION(

hardware_interface/include/hardware_interface/hardware_info.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
#ifndef HARDWARE_INTERFACE__HARDWARE_INFO_HPP_
1616
#define HARDWARE_INTERFACE__HARDWARE_INFO_HPP_
1717

18+
#include <fmt/compile.h>
19+
1820
#include <string>
1921
#include <unordered_map>
22+
#include <variant>
2023
#include <vector>
2124

2225
#include "joint_limits/joint_limits.hpp"
@@ -133,6 +136,8 @@ struct TransmissionInfo
133136
/**
134137
* Hardware handles supported types
135138
*/
139+
140+
using HANDLE_DATATYPE = std::variant<std::monostate, double, bool>;
136141
class HandleDataType
137142
{
138143
public:
@@ -202,6 +207,27 @@ class HandleDataType
202207
}
203208
}
204209

210+
/**
211+
* @brief Cast the given value to double.
212+
* @param value The value to be casted.
213+
* @return The casted value.
214+
* @throw std::runtime_error if the HandleDataType cannot be casted to double.
215+
* @note Once we add support for more data types, this function should be updated
216+
*/
217+
double cast_to_double(const HANDLE_DATATYPE & value) const
218+
{
219+
switch (value_)
220+
{
221+
case DOUBLE:
222+
return std::get<double>(value);
223+
case BOOL:
224+
return static_cast<double>(std::get<bool>(value));
225+
default:
226+
throw std::runtime_error(
227+
fmt::format(FMT_COMPILE("Data type : '{}' cannot be casted to double."), to_string()));
228+
}
229+
}
230+
205231
HandleDataType from_string(const std::string & data_type) { return HandleDataType(data_type); }
206232

207233
private:

hardware_interface/test/test_handle.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,51 @@ TEST(TestHandle, move_assignment)
426426
EXPECT_DOUBLE_EQ(moved.get_optional().value(), 0.0);
427427
}
428428
#pragma GCC diagnostic pop
429+
430+
class TestableHandle : public hardware_interface::Handle
431+
{
432+
FRIEND_TEST(TestHandle, handle_castable);
433+
// Use generation of interface names
434+
explicit TestableHandle(const InterfaceDescription & interface_description)
435+
: hardware_interface::Handle(interface_description)
436+
{
437+
}
438+
};
439+
440+
TEST(TestHandle, handle_castable)
441+
{
442+
hardware_interface::InterfaceInfo info;
443+
info.name = "position";
444+
const std::string JOINT_NAME_1 = "joint1";
445+
{
446+
info.data_type = "double";
447+
info.initial_value = "23.0";
448+
hardware_interface::InterfaceDescription interface_description{JOINT_NAME_1, info};
449+
TestableHandle handle{interface_description};
450+
451+
EXPECT_TRUE(handle.is_valid());
452+
EXPECT_TRUE(handle.is_castable_to_double());
453+
EXPECT_EQ(handle.data_type_.cast_to_double(handle.value_), 23.0);
454+
}
455+
{
456+
info.data_type = "bool";
457+
info.initial_value = "false";
458+
hardware_interface::InterfaceDescription interface_description{JOINT_NAME_1, info};
459+
TestableHandle handle{interface_description};
460+
461+
EXPECT_TRUE(handle.is_valid());
462+
EXPECT_TRUE(handle.is_castable_to_double());
463+
EXPECT_EQ(handle.data_type_.cast_to_double(handle.value_), 0.0);
464+
465+
handle.value_ = true;
466+
EXPECT_EQ(handle.data_type_.cast_to_double(handle.value_), 1.0);
467+
}
468+
{
469+
// handle with unsupported datatype can't be created right now
470+
// extend with more datatypes once supported in Handle
471+
hardware_interface::HandleDataType dt{"string"};
472+
EXPECT_FALSE(dt.is_castable_to_double());
473+
hardware_interface::HANDLE_DATATYPE value = std::monostate{};
474+
EXPECT_THROW(dt.cast_to_double(value), std::runtime_error);
475+
}
476+
}

hardware_interface_testing/test/test_components/test_actuator.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class TestActuator : public ActuatorInterface
7575
&velocity_state_));
7676
state_interfaces.emplace_back(
7777
hardware_interface::StateInterface(
78-
get_hardware_info().joints[0].name, "some_unlisted_interface", nullptr));
78+
get_hardware_info().joints[0].name, "some_unlisted_interface", &unlisted_interface_));
7979

8080
return state_interfaces;
8181
}
@@ -187,6 +187,7 @@ class TestActuator : public ActuatorInterface
187187
double velocity_state_ = 0.0;
188188
double velocity_command_ = 0.0;
189189
double max_velocity_command_ = 0.0;
190+
double unlisted_interface_ = std::numeric_limits<double>::quiet_NaN();
190191
};
191192

192193
class TestUninitializableActuator : public TestActuator

0 commit comments

Comments
 (0)