Skip to content

Commit 5bc50b9

Browse files
authored
[HW Interface] Use new handle API inside the hardware components (ros-controls#2092)
1 parent 9c4cd49 commit 5bc50b9

File tree

5 files changed

+201
-18
lines changed

5 files changed

+201
-18
lines changed

hardware_interface/include/hardware_interface/actuator_interface.hpp

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,24 +517,77 @@ class ActuatorInterface : public rclcpp_lifecycle::node_interfaces::LifecycleNod
517517
lifecycle_state_ = new_state;
518518
}
519519

520-
void set_state(const std::string & interface_name, const double & value)
520+
template <typename T>
521+
void set_state(const std::string & interface_name, const T & value)
521522
{
522-
actuator_states_.at(interface_name)->set_value(value);
523+
auto it = actuator_states_.find(interface_name);
524+
if (it == actuator_states_.end())
525+
{
526+
throw std::runtime_error(
527+
"State interface not found: " + interface_name +
528+
" in actuator hardware component: " + info_.name + ". This should not happen.");
529+
}
530+
auto & handle = it->second;
531+
std::unique_lock<std::shared_mutex> lock(handle->get_mutex());
532+
std::ignore = handle->set_value(lock, value);
523533
}
524534

525-
double get_state(const std::string & interface_name) const
535+
template <typename T = double>
536+
T get_state(const std::string & interface_name) const
526537
{
527-
return actuator_states_.at(interface_name)->get_value();
538+
auto it = actuator_states_.find(interface_name);
539+
if (it == actuator_states_.end())
540+
{
541+
throw std::runtime_error(
542+
"State interface not found: " + interface_name +
543+
" in actuator hardware component: " + info_.name + ". This should not happen.");
544+
}
545+
auto & handle = it->second;
546+
std::shared_lock<std::shared_mutex> lock(handle->get_mutex());
547+
const auto opt_value = handle->get_optional<T>(lock);
548+
if (!opt_value)
549+
{
550+
throw std::runtime_error(
551+
"Failed to get state value from interface: " + interface_name +
552+
". This should not happen.");
553+
}
554+
return opt_value.value();
528555
}
529556

530557
void set_command(const std::string & interface_name, const double & value)
531558
{
532-
actuator_commands_.at(interface_name)->set_value(value);
559+
auto it = actuator_commands_.find(interface_name);
560+
if (it == actuator_commands_.end())
561+
{
562+
throw std::runtime_error(
563+
"Command interface not found: " + interface_name +
564+
" in actuator hardware component: " + info_.name + ". This should not happen.");
565+
}
566+
auto & handle = it->second;
567+
std::unique_lock<std::shared_mutex> lock(handle->get_mutex());
568+
std::ignore = handle->set_value(lock, value);
533569
}
534570

535-
double get_command(const std::string & interface_name) const
571+
template <typename T = double>
572+
T get_command(const std::string & interface_name) const
536573
{
537-
return actuator_commands_.at(interface_name)->get_value();
574+
auto it = actuator_commands_.find(interface_name);
575+
if (it == actuator_commands_.end())
576+
{
577+
throw std::runtime_error(
578+
"Command interface not found: " + interface_name +
579+
" in actuator hardware component: " + info_.name + ". This should not happen.");
580+
}
581+
auto & handle = it->second;
582+
std::shared_lock<std::shared_mutex> lock(handle->get_mutex());
583+
const auto opt_value = handle->get_optional<double>(lock);
584+
if (!opt_value)
585+
{
586+
throw std::runtime_error(
587+
"Failed to get command value from interface: " + interface_name +
588+
". This should not happen.");
589+
}
590+
return opt_value.value();
538591
}
539592

540593
/// Get the logger of the ActuatorInterface.

hardware_interface/include/hardware_interface/handle.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,22 @@ class Handle
172172
[[nodiscard]] std::optional<T> get_optional() const
173173
{
174174
std::shared_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
175+
return get_optional<T>(lock);
176+
}
177+
/**
178+
* @brief Get the value of the handle.
179+
* @tparam T The type of the value to be retrieved.
180+
* @param lock The lock to access the value.
181+
* @return The value of the handle if it accessed successfully, std::nullopt otherwise.
182+
*
183+
* @note The method is thread-safe and non-blocking.
184+
* @note When different threads access the same handle at same instance, and if they are unable to
185+
* lock the handle to access the value, the handle returns std::nullopt. If the operation is
186+
* successful, the value is returned.
187+
*/
188+
template <typename T = double>
189+
[[nodiscard]] std::optional<T> get_optional(std::shared_lock<std::shared_mutex> & lock) const
190+
{
175191
if (!lock.owns_lock())
176192
{
177193
return std::nullopt;
@@ -259,6 +275,24 @@ class Handle
259275
[[nodiscard]] bool set_value(const T & value)
260276
{
261277
std::unique_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
278+
return set_value(lock, value);
279+
}
280+
281+
/**
282+
* @brief Set the value of the handle.
283+
* @tparam T The type of the value to be set.
284+
* @param lock The lock to set the value.
285+
* @param value The value to be set.
286+
* @return true if the value is set successfully, false otherwise.
287+
*
288+
* @note The method is thread-safe and non-blocking.
289+
* @note When different threads access the same handle at same instance, and if they are unable to
290+
* lock the handle to set the value, the handle returns false. If the operation is successful, the
291+
* handle is updated and returns true.
292+
*/
293+
template <typename T>
294+
[[nodiscard]] bool set_value(std::unique_lock<std::shared_mutex> & lock, const T & value)
295+
{
262296
if (!lock.owns_lock())
263297
{
264298
return false;
@@ -285,6 +319,8 @@ class Handle
285319
// END
286320
}
287321

322+
std::shared_mutex & get_mutex() { return handle_mutex_; }
323+
288324
HandleDataType get_data_type() const { return data_type_; }
289325

290326
private:

hardware_interface/include/hardware_interface/sensor_interface.hpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,14 +325,41 @@ class SensorInterface : public rclcpp_lifecycle::node_interfaces::LifecycleNodeI
325325
lifecycle_state_ = new_state;
326326
}
327327

328-
void set_state(const std::string & interface_name, const double & value)
328+
template <typename T>
329+
void set_state(const std::string & interface_name, const T & value)
329330
{
330-
sensor_states_map_.at(interface_name)->set_value(value);
331+
auto it = sensor_states_map_.find(interface_name);
332+
if (it == sensor_states_map_.end())
333+
{
334+
throw std::runtime_error(
335+
"State interface not found: " + interface_name +
336+
" in sensor hardware component: " + info_.name + ". This should not happen.");
337+
}
338+
auto & handle = it->second;
339+
std::unique_lock<std::shared_mutex> lock(handle->get_mutex());
340+
std::ignore = handle->set_value(lock, value);
331341
}
332342

333-
double get_state(const std::string & interface_name) const
343+
template <typename T = double>
344+
T get_state(const std::string & interface_name) const
334345
{
335-
return sensor_states_map_.at(interface_name)->get_value();
346+
auto it = sensor_states_map_.find(interface_name);
347+
if (it == sensor_states_map_.end())
348+
{
349+
throw std::runtime_error(
350+
"State interface not found: " + interface_name +
351+
" in sensor hardware component: " + info_.name + ". This should not happen.");
352+
}
353+
auto & handle = it->second;
354+
std::shared_lock<std::shared_mutex> lock(handle->get_mutex());
355+
const auto opt_value = handle->get_optional<T>(lock);
356+
if (!opt_value)
357+
{
358+
throw std::runtime_error(
359+
"Failed to get state value from interface: " + interface_name +
360+
". This should not happen.");
361+
}
362+
return opt_value.value();
336363
}
337364

338365
/// Get the logger of the SensorInterface.

hardware_interface/include/hardware_interface/system_interface.hpp

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -547,24 +547,77 @@ class SystemInterface : public rclcpp_lifecycle::node_interfaces::LifecycleNodeI
547547
lifecycle_state_ = new_state;
548548
}
549549

550-
void set_state(const std::string & interface_name, const double & value)
550+
template <typename T>
551+
void set_state(const std::string & interface_name, const T & value)
551552
{
552-
system_states_.at(interface_name)->set_value(value);
553+
auto it = system_states_.find(interface_name);
554+
if (it == system_states_.end())
555+
{
556+
throw std::runtime_error(
557+
"State interface not found: " + interface_name +
558+
" in system hardware component: " + info_.name + ". This should not happen.");
559+
}
560+
auto & handle = it->second;
561+
std::unique_lock<std::shared_mutex> lock(handle->get_mutex());
562+
std::ignore = handle->set_value(lock, value);
553563
}
554564

555-
double get_state(const std::string & interface_name) const
565+
template <typename T = double>
566+
T get_state(const std::string & interface_name) const
556567
{
557-
return system_states_.at(interface_name)->get_value();
568+
auto it = system_states_.find(interface_name);
569+
if (it == system_states_.end())
570+
{
571+
throw std::runtime_error(
572+
"State interface not found: " + interface_name +
573+
" in system hardware component: " + info_.name + ". This should not happen.");
574+
}
575+
auto & handle = it->second;
576+
std::shared_lock<std::shared_mutex> lock(handle->get_mutex());
577+
const auto opt_value = handle->get_optional<T>(lock);
578+
if (!opt_value)
579+
{
580+
throw std::runtime_error(
581+
"Failed to get state value from interface: " + interface_name +
582+
". This should not happen.");
583+
}
584+
return opt_value.value();
558585
}
559586

560587
void set_command(const std::string & interface_name, const double & value)
561588
{
562-
system_commands_.at(interface_name)->set_value(value);
589+
auto it = system_commands_.find(interface_name);
590+
if (it == system_commands_.end())
591+
{
592+
throw std::runtime_error(
593+
"Command interface not found: " + interface_name +
594+
" in system hardware component: " + info_.name + ". This should not happen.");
595+
}
596+
auto & handle = it->second;
597+
std::unique_lock<std::shared_mutex> lock(handle->get_mutex());
598+
std::ignore = handle->set_value(lock, value);
563599
}
564600

565-
double get_command(const std::string & interface_name) const
601+
template <typename T = double>
602+
T get_command(const std::string & interface_name) const
566603
{
567-
return system_commands_.at(interface_name)->get_value();
604+
auto it = system_commands_.find(interface_name);
605+
if (it == system_commands_.end())
606+
{
607+
throw std::runtime_error(
608+
"Command interface not found: " + interface_name +
609+
" in system hardware component: " + info_.name + ". This should not happen.");
610+
}
611+
auto & handle = it->second;
612+
std::shared_lock<std::shared_mutex> lock(handle->get_mutex());
613+
const auto opt_value = handle->get_optional<double>(lock);
614+
if (!opt_value)
615+
{
616+
throw std::runtime_error(
617+
"Failed to get command value from interface: " + interface_name +
618+
". This should not happen.");
619+
}
620+
return opt_value.value();
568621
}
569622

570623
/// Get the logger of the SystemInterface.

hardware_interface/test/test_component_interfaces.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ class DummyActuatorDefault : public hardware_interface::ActuatorInterface
186186
{
187187
set_command("joint1/velocity", 0.0);
188188
}
189+
// Should throw as the interface is unknown
190+
EXPECT_THROW(get_state("joint1/nonexisting/interface"), std::runtime_error);
191+
EXPECT_THROW(get_command("joint1/nonexisting/interface"), std::runtime_error);
192+
EXPECT_THROW(set_state("joint1/nonexisting/interface", 0.0), std::runtime_error);
193+
EXPECT_THROW(set_command("joint1/nonexisting/interface", 0.0), std::runtime_error);
189194

190195
read_calls_ = 0;
191196
write_calls_ = 0;
@@ -329,6 +334,10 @@ class DummySensorDefault : public hardware_interface::SensorInterface
329334
CallbackReturn on_configure(const rclcpp_lifecycle::State & /*previous_state*/) override
330335
{
331336
set_state("sens1/voltage", 0.0);
337+
// Should throw as the interface is unknown
338+
EXPECT_THROW(get_state("joint1/nonexisting/interface"), std::runtime_error);
339+
EXPECT_THROW(set_state("joint1/nonexisting/interface", 0.0), std::runtime_error);
340+
332341
read_calls_ = 0;
333342
return CallbackReturn::SUCCESS;
334343
}
@@ -600,6 +609,11 @@ class DummySystemDefault : public hardware_interface::SystemInterface
600609
set_command(velocity_commands_[i], 0.0);
601610
}
602611
}
612+
// Should throw as the interface is unknown
613+
EXPECT_THROW(get_state("joint1/nonexisting/interface"), std::runtime_error);
614+
EXPECT_THROW(get_command("joint1/nonexisting/interface"), std::runtime_error);
615+
EXPECT_THROW(set_state("joint1/nonexisting/interface", 0.0), std::runtime_error);
616+
EXPECT_THROW(set_command("joint1/nonexisting/interface", 0.0), std::runtime_error);
603617

604618
read_calls_ = 0;
605619
write_calls_ = 0;

0 commit comments

Comments
 (0)