Skip to content

Commit c127451

Browse files
authored
Publish all castable data types to pal_statistics (backport #2633) (#2855)
1 parent 1f75c84 commit c127451

File tree

4 files changed

+145
-11
lines changed

4 files changed

+145
-11
lines changed

hardware_interface/include/hardware_interface/handle.hpp

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
#include <shared_mutex>
2828
#include <string>
2929
#include <utility>
30-
#include <variant>
3130

3231
#include "hardware_interface/hardware_info.hpp"
3332
#include "hardware_interface/introspection.hpp"
@@ -58,8 +57,6 @@ std::string get_type_name()
5857
namespace hardware_interface
5958
{
6059

61-
using HANDLE_DATATYPE = std::variant<std::monostate, double, bool>;
62-
6360
/// A handle used to get and set a value on a given interface.
6461
class Handle
6562
{
@@ -359,6 +356,14 @@ class Handle
359356

360357
HandleDataType get_data_type() const { return data_type_; }
361358

359+
/// Returns true if the handle data type can be casted to double.
360+
bool is_castable_to_double() const { return data_type_.is_castable_to_double(); }
361+
362+
bool is_valid() const
363+
{
364+
return (value_ptr_ != nullptr) || !std::holds_alternative<std::monostate>(value_);
365+
}
366+
362367
private:
363368
void copy(const Handle & other) noexcept
364369
{
@@ -395,7 +400,7 @@ class Handle
395400
HandleDataType data_type_ = HandleDataType::DOUBLE;
396401
// BEGIN (Handle export change): for backward compatibility
397402
// TODO(Manuel) redeclare as HANDLE_DATATYPE * value_ptr_ if old functionality is removed
398-
double * value_ptr_;
403+
double * value_ptr_ = nullptr;
399404
// END
400405
mutable std::shared_mutex handle_mutex_;
401406
};
@@ -410,17 +415,35 @@ class StateInterface : public Handle
410415

411416
void registerIntrospection() const
412417
{
413-
if (value_ptr_ || std::holds_alternative<double>(value_))
418+
if (!is_valid())
419+
{
420+
RCLCPP_WARN(
421+
rclcpp::get_logger(get_name()),
422+
"Cannot register state introspection for state interface: %s without a valid value "
423+
"pointer or initialized value.",
424+
get_name().c_str());
425+
return;
426+
}
427+
if (value_ptr_ || data_type_.is_castable_to_double())
414428
{
415429
std::function<double()> f = [this]()
416-
{ return value_ptr_ ? *value_ptr_ : std::get<double>(value_); };
430+
{
431+
if (value_ptr_)
432+
{
433+
return *value_ptr_;
434+
}
435+
else
436+
{
437+
return data_type_.cast_to_double(value_);
438+
}
439+
};
417440
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION("state_interface." + get_name(), f);
418441
}
419442
}
420443

421444
void unregisterIntrospection() const
422445
{
423-
if (value_ptr_ || std::holds_alternative<double>(value_))
446+
if (is_valid() && (value_ptr_ || data_type_.is_castable_to_double()))
424447
{
425448
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION("state_interface." + get_name());
426449
}
@@ -484,10 +507,28 @@ class CommandInterface : public Handle
484507

485508
void registerIntrospection() const
486509
{
487-
if (value_ptr_ || std::holds_alternative<double>(value_))
510+
if (!is_valid())
511+
{
512+
RCLCPP_WARN(
513+
rclcpp::get_logger(get_name()),
514+
"Cannot register command introspection for command interface: %s without a valid value "
515+
"pointer or initialized value.",
516+
get_name().c_str());
517+
return;
518+
}
519+
if (value_ptr_ || data_type_.is_castable_to_double())
488520
{
489521
std::function<double()> f = [this]()
490-
{ return value_ptr_ ? *value_ptr_ : std::get<double>(value_); };
522+
{
523+
if (value_ptr_)
524+
{
525+
return *value_ptr_;
526+
}
527+
else
528+
{
529+
return data_type_.cast_to_double(value_);
530+
}
531+
};
491532
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name(), f);
492533
DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION(
493534
"command_interface." + get_name() + ".is_limited", &is_command_limited_);
@@ -496,7 +537,7 @@ class CommandInterface : public Handle
496537

497538
void unregisterIntrospection() const
498539
{
499-
if (value_ptr_ || std::holds_alternative<double>(value_))
540+
if (is_valid() && (value_ptr_ || data_type_.is_castable_to_double()))
500541
{
501542
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name());
502543
DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION(

hardware_interface/include/hardware_interface/hardware_info.hpp

Lines changed: 44 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:
@@ -184,6 +189,45 @@ class HandleDataType
184189
}
185190
}
186191

192+
/**
193+
* @brief Check if the HandleDataType can be casted to double.
194+
* @return True if the HandleDataType can be casted to double, false otherwise.
195+
* @note Once we add support for more data types, this function should be updated
196+
*/
197+
bool is_castable_to_double() const
198+
{
199+
switch (value_)
200+
{
201+
case DOUBLE:
202+
return true;
203+
case BOOL:
204+
return true; // bool can be converted to double
205+
default:
206+
return false; // unknown type cannot be converted
207+
}
208+
}
209+
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+
187231
HandleDataType from_string(const std::string & data_type) { return HandleDataType(data_type); }
188232

189233
private:

hardware_interface/test/test_handle.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,51 @@ TEST(TestHandle, move_assignment)
416416
EXPECT_DOUBLE_EQ(moved.get_optional().value(), 0.0);
417417
}
418418
#pragma GCC diagnostic pop
419+
420+
class TestableHandle : public hardware_interface::Handle
421+
{
422+
FRIEND_TEST(TestHandle, handle_castable);
423+
// Use generation of interface names
424+
explicit TestableHandle(const InterfaceDescription & interface_description)
425+
: hardware_interface::Handle(interface_description)
426+
{
427+
}
428+
};
429+
430+
TEST(TestHandle, handle_castable)
431+
{
432+
hardware_interface::InterfaceInfo info;
433+
info.name = "position";
434+
const std::string JOINT_NAME_1 = "joint1";
435+
{
436+
info.data_type = "double";
437+
info.initial_value = "23.0";
438+
hardware_interface::InterfaceDescription interface_description{JOINT_NAME_1, info};
439+
TestableHandle handle{interface_description};
440+
441+
EXPECT_TRUE(handle.is_valid());
442+
EXPECT_TRUE(handle.is_castable_to_double());
443+
EXPECT_EQ(handle.data_type_.cast_to_double(handle.value_), 23.0);
444+
}
445+
{
446+
info.data_type = "bool";
447+
info.initial_value = "false";
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_), 0.0);
454+
455+
handle.value_ = true;
456+
EXPECT_EQ(handle.data_type_.cast_to_double(handle.value_), 1.0);
457+
}
458+
{
459+
// handle with unsupported datatype can't be created right now
460+
// extend with more datatypes once supported in Handle
461+
hardware_interface::HandleDataType dt{"string"};
462+
EXPECT_FALSE(dt.is_castable_to_double());
463+
hardware_interface::HANDLE_DATATYPE value = std::monostate{};
464+
EXPECT_THROW(dt.cast_to_double(value), std::runtime_error);
465+
}
466+
}

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)