Skip to content

Commit c3e8082

Browse files
authored
Add new Handle constructor for easier initialization (#2253)
1 parent 7f0bd68 commit c3e8082

File tree

3 files changed

+93
-18
lines changed

3 files changed

+93
-18
lines changed

hardware_interface/include/hardware_interface/handle.hpp

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include "hardware_interface/hardware_info.hpp"
3333
#include "hardware_interface/introspection.hpp"
34+
#include "hardware_interface/lexical_casts.hpp"
3435
#include "hardware_interface/macros.hpp"
3536

3637
namespace
@@ -55,43 +56,64 @@ class Handle
5556
{
5657
public:
5758
[[deprecated("Use InterfaceDescription for initializing the Interface")]]
58-
Handle(
59-
const std::string & prefix_name, const std::string & interface_name,
60-
double * value_ptr = nullptr)
59+
Handle(const std::string & prefix_name, const std::string & interface_name, double * value_ptr)
6160
: prefix_name_(prefix_name),
6261
interface_name_(interface_name),
6362
handle_name_(prefix_name_ + "/" + interface_name_),
6463
value_ptr_(value_ptr)
6564
{
6665
}
6766

68-
explicit Handle(const InterfaceDescription & interface_description)
69-
: prefix_name_(interface_description.get_prefix_name()),
70-
interface_name_(interface_description.get_interface_name()),
71-
handle_name_(interface_description.get_name())
67+
explicit Handle(
68+
const std::string & prefix_name, const std::string & interface_name,
69+
const std::string & data_type = "double", const std::string & initial_value = "")
70+
: prefix_name_(prefix_name),
71+
interface_name_(interface_name),
72+
handle_name_(prefix_name_ + "/" + interface_name_),
73+
data_type_(data_type)
7274
{
73-
data_type_ = interface_description.get_data_type();
7475
// As soon as multiple datatypes are used in HANDLE_DATATYPE
7576
// we need to initialize according the type passed in interface description
7677
if (data_type_ == hardware_interface::HandleDataType::DOUBLE)
7778
{
78-
value_ = std::numeric_limits<double>::quiet_NaN();
79-
value_ptr_ = std::get_if<double>(&value_);
79+
try
80+
{
81+
value_ = initial_value.empty() ? std::numeric_limits<double>::quiet_NaN()
82+
: hardware_interface::stod(initial_value);
83+
value_ptr_ = std::get_if<double>(&value_);
84+
}
85+
catch (const std::invalid_argument & err)
86+
{
87+
throw std::invalid_argument(
88+
fmt::format(
89+
FMT_COMPILE(
90+
"Invalid initial value : '{}' parsed for interface : '{}' with type : '{}'"),
91+
initial_value, handle_name_, data_type_.to_string()));
92+
}
8093
}
8194
else if (data_type_ == hardware_interface::HandleDataType::BOOL)
8295
{
8396
value_ptr_ = nullptr;
84-
value_ = false;
97+
value_ = initial_value.empty() ? false : hardware_interface::parse_bool(initial_value);
8598
}
8699
else
87100
{
88101
throw std::runtime_error(
89102
fmt::format(
90-
FMT_COMPILE("Invalid data type : '{}' for interface : {}"),
91-
interface_description.interface_info.data_type, interface_description.get_name()));
103+
FMT_COMPILE(
104+
"Invalid data type : '{}' for interface : {}. Supported types are double and bool."),
105+
data_type, handle_name_));
92106
}
93107
}
94108

109+
explicit Handle(const InterfaceDescription & interface_description)
110+
: Handle(
111+
interface_description.get_prefix_name(), interface_description.get_interface_name(),
112+
interface_description.get_data_type_string(),
113+
interface_description.interface_info.initial_value)
114+
{
115+
}
116+
95117
[[deprecated("Use InterfaceDescription for initializing the Interface")]]
96118

97119
explicit Handle(const std::string & interface_name)

hardware_interface/include/hardware_interface/hardware_info.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ struct InterfaceInfo
3535
*/
3636
std::string name;
3737
/// (Optional) Minimal allowed values of the interface.
38-
std::string min;
38+
std::string min = "";
3939
/// (Optional) Maximal allowed values of the interface.
40-
std::string max;
40+
std::string max = "";
4141
/// (Optional) Initial value of the interface.
42-
std::string initial_value;
42+
std::string initial_value = "";
4343
/// (Optional) The datatype of the interface, e.g. "bool", "int".
4444
std::string data_type = "double";
4545
/// (Optional) If the handle is an array, the size of the array.
@@ -224,6 +224,8 @@ struct InterfaceDescription
224224

225225
const std::string & get_name() const { return interface_name; }
226226

227+
const std::string & get_data_type_string() const { return interface_info.data_type; }
228+
227229
HandleDataType get_data_type() const { return HandleDataType(interface_info.data_type); }
228230
};
229231

hardware_interface/test/test_handle.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ TEST(TestHandle, state_interface)
6262

6363
TEST(TestHandle, name_getters_work)
6464
{
65-
StateInterface handle{JOINT_NAME, FOO_INTERFACE};
65+
StateInterface handle{JOINT_NAME, FOO_INTERFACE, nullptr};
6666
EXPECT_EQ(handle.get_name(), std::string(JOINT_NAME) + "/" + std::string(FOO_INTERFACE));
6767
EXPECT_EQ(handle.get_interface_name(), FOO_INTERFACE);
6868
EXPECT_EQ(handle.get_prefix_name(), JOINT_NAME);
6969
}
7070

7171
TEST(TestHandle, value_methods_throw_for_nullptr)
7272
{
73-
CommandInterface handle{JOINT_NAME, FOO_INTERFACE};
73+
CommandInterface handle{JOINT_NAME, FOO_INTERFACE, nullptr};
7474
EXPECT_ANY_THROW(handle.get_optional().value());
7575
EXPECT_ANY_THROW(std::ignore = handle.set_value(0.0));
7676
}
@@ -234,6 +234,55 @@ TEST(TestHandle, interface_description_bool_data_type)
234234
ASSERT_THROW({ std::ignore = handle.get_optional<double>(); }, std::runtime_error);
235235
}
236236

237+
TEST(TestHandle, handle_constructor_double_data_type)
238+
{
239+
const std::string POSITION_INTERFACE = "position";
240+
const std::string JOINT_NAME_1 = "joint1";
241+
StateInterface handle{JOINT_NAME_1, POSITION_INTERFACE, "double", "23.0"};
242+
243+
ASSERT_EQ(hardware_interface::HandleDataType::DOUBLE, handle.get_data_type());
244+
EXPECT_EQ(handle.get_name(), JOINT_NAME_1 + "/" + POSITION_INTERFACE);
245+
EXPECT_EQ(handle.get_interface_name(), POSITION_INTERFACE);
246+
EXPECT_EQ(handle.get_prefix_name(), JOINT_NAME_1);
247+
EXPECT_NO_THROW({ std::ignore = handle.get_optional<double>(); });
248+
ASSERT_EQ(handle.get_optional<double>().value(), 23.0);
249+
ASSERT_TRUE(handle.get_optional<double>().has_value());
250+
ASSERT_TRUE(handle.set_value(0.0));
251+
ASSERT_EQ(handle.get_optional<double>().value(), 0.0);
252+
ASSERT_TRUE(handle.set_value(1.0));
253+
ASSERT_EQ(handle.get_optional<double>().value(), 1.0);
254+
255+
// Test the assertions
256+
ASSERT_THROW({ std::ignore = handle.get_optional<bool>(); }, std::runtime_error);
257+
ASSERT_THROW({ std::ignore = handle.set_value(true); }, std::runtime_error);
258+
EXPECT_ANY_THROW({ StateInterface bad_itf("joint1", POSITION_INTERFACE, "double", "233NaN0"); })
259+
<< "Invalid double value should throw";
260+
}
261+
262+
TEST(TestHandle, handle_constructor_bool_data_type)
263+
{
264+
const std::string collision_interface = "collision";
265+
const std::string itf_name = "joint1";
266+
StateInterface handle{itf_name, collision_interface, "bool", "true"};
267+
268+
ASSERT_EQ(hardware_interface::HandleDataType::BOOL, handle.get_data_type());
269+
EXPECT_EQ(handle.get_name(), itf_name + "/" + collision_interface);
270+
EXPECT_EQ(handle.get_interface_name(), collision_interface);
271+
EXPECT_EQ(handle.get_prefix_name(), itf_name);
272+
EXPECT_NO_THROW({ std::ignore = handle.get_optional<bool>(); });
273+
ASSERT_TRUE(handle.get_optional<bool>().value())
274+
<< "Default value should be true as it is initialized";
275+
ASSERT_TRUE(handle.set_value(false));
276+
ASSERT_FALSE(handle.get_optional<bool>().value());
277+
ASSERT_TRUE(handle.set_value(true));
278+
ASSERT_TRUE(handle.get_optional<bool>().value());
279+
280+
// Test the assertions
281+
ASSERT_THROW({ std::ignore = handle.set_value(-1.0); }, std::runtime_error);
282+
ASSERT_THROW({ std::ignore = handle.set_value(0.0); }, std::runtime_error);
283+
ASSERT_THROW({ std::ignore = handle.get_optional<double>(); }, std::runtime_error);
284+
}
285+
237286
TEST(TestHandle, interface_description_unknown_data_type)
238287
{
239288
const std::string collision_interface = "collision";
@@ -245,6 +294,8 @@ TEST(TestHandle, interface_description_unknown_data_type)
245294

246295
ASSERT_EQ(hardware_interface::HandleDataType::UNKNOWN, interface_descr.get_data_type());
247296
EXPECT_ANY_THROW({ StateInterface handle{interface_descr}; }) << "Unknown data type should throw";
297+
EXPECT_ANY_THROW({ StateInterface handle("joint1", "collision", "UNKNOWN"); })
298+
<< "Unknown data type should throw";
248299
}
249300

250301
TEST(TestHandle, interface_description_command_interface_name_getters_work)

0 commit comments

Comments
 (0)