Skip to content

Commit d7f9bd5

Browse files
Handle hardware errors in Resource Manager (#805) (#837) #ABI-breaking
Co-authored-by: Denis Štogl <[email protected]>
1 parent ba58074 commit d7f9bd5

File tree

8 files changed

+492
-71
lines changed

8 files changed

+492
-71
lines changed

hardware_interface/include/hardware_interface/resource_manager.hpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class SensorInterface;
3636
class SystemInterface;
3737
class ResourceStorage;
3838

39+
struct HardwareReadWriteStatus
40+
{
41+
bool ok;
42+
std::vector<std::string> failed_hardware_names;
43+
};
44+
3945
class HARDWARE_INTERFACE_PUBLIC ResourceManager
4046
{
4147
public:
@@ -176,6 +182,27 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
176182
*/
177183
void remove_controller_reference_interfaces(const std::string & controller_name);
178184

185+
/// Cache mapping between hardware and controllers using it
186+
/**
187+
* Find mapping between controller and hardware based on interfaces controller with
188+
* \p controller_name is using and cache those for later usage.
189+
*
190+
* \param[in] controller_name name of the controller which interfaces are provided.
191+
* \param[in] interfaces list of interfaces controller with \p controller_name is using.
192+
*/
193+
void cache_controller_to_hardware(
194+
const std::string & controller_name, const std::vector<std::string> & interfaces);
195+
196+
/// Return cached controllers for a specific hardware.
197+
/**
198+
* Return list of cached controller names that use the hardware with name \p hardware_name.
199+
*
200+
* \param[in] hardware_name the name of the hardware for which cached controllers should be
201+
* returned. \returns list of cached controller names that depend on hardware with name \p
202+
* hardware_name.
203+
*/
204+
std::vector<std::string> get_cached_controllers_to_hardware(const std::string & hardware_name);
205+
179206
/// Checks whether a command interface is already claimed.
180207
/**
181208
* Any command interface can only be claimed by a single instance.
@@ -355,7 +382,7 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
355382
* Part of the real-time critical update loop.
356383
* It is realtime-safe if used hadware interfaces are implemented adequately.
357384
*/
358-
void read(const rclcpp::Time & time, const rclcpp::Duration & period);
385+
HardwareReadWriteStatus read(const rclcpp::Time & time, const rclcpp::Duration & period);
359386

360387
/// Write all loaded hardware components.
361388
/**
@@ -364,7 +391,7 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
364391
* Part of the real-time critical update loop.
365392
* It is realtime-safe if used hadware interfaces are implemented adequately.
366393
*/
367-
void write(const rclcpp::Time & time, const rclcpp::Duration & period);
394+
HardwareReadWriteStatus write(const rclcpp::Time & time, const rclcpp::Duration & period);
368395

369396
/// Activates all available hardware components in the system.
370397
/**
@@ -383,8 +410,12 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
383410

384411
mutable std::recursive_mutex resource_interfaces_lock_;
385412
mutable std::recursive_mutex claimed_command_interfaces_lock_;
413+
mutable std::recursive_mutex resources_lock_;
386414
std::unique_ptr<ResourceStorage> resource_storage_;
387415

416+
// Structure to store read and write status so it is not initialized in the real-time loop
417+
HardwareReadWriteStatus read_write_status;
418+
388419
bool is_urdf_loaded__ = false;
389420
};
390421

hardware_interface/src/actuator.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ const rclcpp_lifecycle::State & Actuator::get_state() const { return impl_->get_
218218

219219
return_type Actuator::read(const rclcpp::Time & time, const rclcpp::Duration & period)
220220
{
221+
// TODO(destogl): discuss what should be default return value, e.g., "NOT_EXECUTED"
221222
return_type result = return_type::ERROR;
222223
if (
223224
impl_->get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE ||
@@ -234,6 +235,7 @@ return_type Actuator::read(const rclcpp::Time & time, const rclcpp::Duration & p
234235

235236
return_type Actuator::write(const rclcpp::Time & time, const rclcpp::Duration & period)
236237
{
238+
// TODO(destogl): discuss what should be default return value, e.g., "NOT_EXECUTED"
237239
return_type result = return_type::ERROR;
238240
if (
239241
impl_->get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE ||

hardware_interface/src/resource_manager.cpp

Lines changed: 166 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ class ResourceStorage
101101
component_info.class_type = hardware_info.hardware_class_type;
102102

103103
hardware_info_map_.insert(std::make_pair(component_info.name, component_info));
104+
hardware_used_by_controllers_.insert(
105+
std::make_pair(component_info.name, std::vector<std::string>()));
104106
}
105107

106108
template <class HardwareT>
@@ -195,6 +197,58 @@ class ResourceStorage
195197
return result;
196198
}
197199

200+
void remove_all_hardware_interfaces_from_available_list(const std::string & hardware_name)
201+
{
202+
// remove all command interfaces from available list
203+
for (const auto & interface : hardware_info_map_[hardware_name].command_interfaces)
204+
{
205+
auto found_it = std::find(
206+
available_command_interfaces_.begin(), available_command_interfaces_.end(), interface);
207+
208+
if (found_it != available_command_interfaces_.end())
209+
{
210+
available_command_interfaces_.erase(found_it);
211+
RCUTILS_LOG_DEBUG_NAMED(
212+
"resource_manager", "(hardware '%s'): '%s' command interface removed from available list",
213+
hardware_name.c_str(), interface.c_str());
214+
}
215+
else
216+
{
217+
// TODO(destogl): do here error management if interfaces are only partially added into
218+
// "available" list - this should never be the case!
219+
RCUTILS_LOG_WARN_NAMED(
220+
"resource_manager",
221+
"(hardware '%s'): '%s' command interface not in available list. "
222+
"This should not happen (hint: multiple cleanup calls).",
223+
hardware_name.c_str(), interface.c_str());
224+
}
225+
}
226+
// remove all state interfaces from available list
227+
for (const auto & interface : hardware_info_map_[hardware_name].state_interfaces)
228+
{
229+
auto found_it = std::find(
230+
available_state_interfaces_.begin(), available_state_interfaces_.end(), interface);
231+
232+
if (found_it != available_state_interfaces_.end())
233+
{
234+
available_state_interfaces_.erase(found_it);
235+
RCUTILS_LOG_DEBUG_NAMED(
236+
"resource_manager", "(hardware '%s'): '%s' state interface removed from available list",
237+
hardware_name.c_str(), interface.c_str());
238+
}
239+
else
240+
{
241+
// TODO(destogl): do here error management if interfaces are only partially added into
242+
// "available" list - this should never be the case!
243+
RCUTILS_LOG_WARN_NAMED(
244+
"resource_manager",
245+
"(hardware '%s'): '%s' state interface not in available list. "
246+
"This should not happen (hint: multiple cleanup calls).",
247+
hardware_name.c_str(), interface.c_str());
248+
}
249+
}
250+
}
251+
198252
template <class HardwareT>
199253
bool cleanup_hardware(HardwareT & hardware)
200254
{
@@ -204,55 +258,7 @@ class ResourceStorage
204258

205259
if (result)
206260
{
207-
// remove all command interfaces from available list
208-
for (const auto & interface : hardware_info_map_[hardware.get_name()].command_interfaces)
209-
{
210-
auto found_it = std::find(
211-
available_command_interfaces_.begin(), available_command_interfaces_.end(), interface);
212-
213-
if (found_it != available_command_interfaces_.end())
214-
{
215-
available_command_interfaces_.erase(found_it);
216-
RCUTILS_LOG_DEBUG_NAMED(
217-
"resource_manager",
218-
"(hardware '%s'): '%s' command interface removed from available list",
219-
hardware.get_name().c_str(), interface.c_str());
220-
}
221-
else
222-
{
223-
// TODO(destogl): do here error management if interfaces are only partially added into
224-
// "available" list - this should never be the case!
225-
RCUTILS_LOG_WARN_NAMED(
226-
"resource_manager",
227-
"(hardware '%s'): '%s' command interface not in available list."
228-
" This can happen due to multiple calls to 'cleanup'",
229-
hardware.get_name().c_str(), interface.c_str());
230-
}
231-
}
232-
// remove all state interfaces from available list
233-
for (const auto & interface : hardware_info_map_[hardware.get_name()].state_interfaces)
234-
{
235-
auto found_it = std::find(
236-
available_state_interfaces_.begin(), available_state_interfaces_.end(), interface);
237-
238-
if (found_it != available_state_interfaces_.end())
239-
{
240-
available_state_interfaces_.erase(found_it);
241-
RCUTILS_LOG_DEBUG_NAMED(
242-
"resource_manager", "(hardware '%s'): '%s' state interface removed from available list",
243-
hardware.get_name().c_str(), interface.c_str());
244-
}
245-
else
246-
{
247-
// TODO(destogl): do here error management if interfaces are only partially added into
248-
// "available" list - this should never be the case!
249-
RCUTILS_LOG_WARN_NAMED(
250-
"resource_manager",
251-
"(hardware '%s'): '%s' state interface not in available list. "
252-
"This can happen due to multiple calls to 'cleanup'",
253-
hardware.get_name().c_str(), interface.c_str());
254-
}
255-
}
261+
remove_all_hardware_interfaces_from_available_list(hardware.get_name());
256262
}
257263
return result;
258264
}
@@ -629,6 +635,10 @@ class ResourceStorage
629635

630636
std::unordered_map<std::string, HardwareComponentInfo> hardware_info_map_;
631637

638+
/// Mapping between hardware and controllers that are using it (accessing data from it)
639+
std::unordered_map<std::string, std::vector<std::string>> hardware_used_by_controllers_;
640+
641+
/// Mapping between controllers and list of reference interfaces they are using
632642
std::unordered_map<std::string, std::vector<std::string>> controllers_reference_interfaces_map_;
633643

634644
/// Storage of all available state interfaces
@@ -699,6 +709,11 @@ void ResourceManager::load_urdf(const std::string & urdf, bool validate_interfac
699709
{
700710
validate_storage(hardware_info);
701711
}
712+
713+
std::lock_guard<std::recursive_mutex> guard(resources_lock_);
714+
read_write_status.failed_hardware_names.reserve(
715+
resource_storage_->actuators_.size() + resource_storage_->sensors_.size() +
716+
resource_storage_->systems_.size());
702717
}
703718

704719
bool ResourceManager::is_urdf_already_loaded() const { return is_urdf_loaded__; }
@@ -807,6 +822,51 @@ void ResourceManager::remove_controller_reference_interfaces(const std::string &
807822
resource_storage_->remove_command_interfaces(interface_names);
808823
}
809824

825+
void ResourceManager::cache_controller_to_hardware(
826+
const std::string & controller_name, const std::vector<std::string> & interfaces)
827+
{
828+
for (const auto & interface : interfaces)
829+
{
830+
bool found = false;
831+
for (const auto & [hw_name, hw_info] : resource_storage_->hardware_info_map_)
832+
{
833+
auto cmd_itf_it =
834+
std::find(hw_info.command_interfaces.begin(), hw_info.command_interfaces.end(), interface);
835+
if (cmd_itf_it != hw_info.command_interfaces.end())
836+
{
837+
found = true;
838+
}
839+
auto state_itf_it =
840+
std::find(hw_info.state_interfaces.begin(), hw_info.state_interfaces.end(), interface);
841+
if (state_itf_it != hw_info.state_interfaces.end())
842+
{
843+
found = true;
844+
}
845+
846+
if (found)
847+
{
848+
// check if controller exist already in the list and if not add it
849+
auto controllers = resource_storage_->hardware_used_by_controllers_[hw_name];
850+
auto ctrl_it = std::find(controllers.begin(), controllers.end(), controller_name);
851+
if (ctrl_it == controllers.end())
852+
{
853+
// add because it does not exist
854+
controllers.reserve(controllers.size() + 1);
855+
controllers.push_back(controller_name);
856+
}
857+
resource_storage_->hardware_used_by_controllers_[hw_name] = controllers;
858+
break;
859+
}
860+
}
861+
}
862+
}
863+
864+
std::vector<std::string> ResourceManager::get_cached_controllers_to_hardware(
865+
const std::string & hardware_name)
866+
{
867+
return resource_storage_->hardware_used_by_controllers_[hardware_name];
868+
}
869+
810870
// CM API: Called in "update"-thread
811871
bool ResourceManager::command_interface_is_claimed(const std::string & key) const
812872
{
@@ -895,19 +955,31 @@ size_t ResourceManager::sensor_components_size() const
895955
void ResourceManager::import_component(
896956
std::unique_ptr<ActuatorInterface> actuator, const HardwareInfo & hardware_info)
897957
{
958+
std::lock_guard<std::recursive_mutex> guard(resources_lock_);
898959
resource_storage_->initialize_actuator(std::move(actuator), hardware_info);
960+
read_write_status.failed_hardware_names.reserve(
961+
resource_storage_->actuators_.size() + resource_storage_->sensors_.size() +
962+
resource_storage_->systems_.size());
899963
}
900964

901965
void ResourceManager::import_component(
902966
std::unique_ptr<SensorInterface> sensor, const HardwareInfo & hardware_info)
903967
{
968+
std::lock_guard<std::recursive_mutex> guard(resources_lock_);
904969
resource_storage_->initialize_sensor(std::move(sensor), hardware_info);
970+
read_write_status.failed_hardware_names.reserve(
971+
resource_storage_->actuators_.size() + resource_storage_->sensors_.size() +
972+
resource_storage_->systems_.size());
905973
}
906974

907975
void ResourceManager::import_component(
908976
std::unique_ptr<SystemInterface> system, const HardwareInfo & hardware_info)
909977
{
978+
std::lock_guard<std::recursive_mutex> guard(resources_lock_);
910979
resource_storage_->initialize_system(std::move(system), hardware_info);
980+
read_write_status.failed_hardware_names.reserve(
981+
resource_storage_->actuators_.size() + resource_storage_->sensors_.size() +
982+
resource_storage_->systems_.size());
911983
}
912984

913985
size_t ResourceManager::system_components_size() const
@@ -1089,32 +1161,57 @@ return_type ResourceManager::set_component_state(
10891161
return result;
10901162
}
10911163

1092-
void ResourceManager::read(const rclcpp::Time & time, const rclcpp::Duration & period)
1164+
HardwareReadWriteStatus ResourceManager::read(
1165+
const rclcpp::Time & time, const rclcpp::Duration & period)
10931166
{
1094-
for (auto & component : resource_storage_->actuators_)
1095-
{
1096-
component.read(time, period);
1097-
}
1098-
for (auto & component : resource_storage_->sensors_)
1099-
{
1100-
component.read(time, period);
1101-
}
1102-
for (auto & component : resource_storage_->systems_)
1167+
std::lock_guard<std::recursive_mutex> guard(resources_lock_);
1168+
read_write_status.ok = true;
1169+
read_write_status.failed_hardware_names.clear();
1170+
1171+
auto read_components = [&](auto & components)
11031172
{
1104-
component.read(time, period);
1105-
}
1173+
for (auto & component : components)
1174+
{
1175+
if (component.read(time, period) != return_type::OK)
1176+
{
1177+
read_write_status.ok = false;
1178+
read_write_status.failed_hardware_names.push_back(component.get_name());
1179+
resource_storage_->remove_all_hardware_interfaces_from_available_list(component.get_name());
1180+
}
1181+
}
1182+
};
1183+
1184+
read_components(resource_storage_->actuators_);
1185+
read_components(resource_storage_->sensors_);
1186+
read_components(resource_storage_->systems_);
1187+
1188+
return read_write_status;
11061189
}
11071190

1108-
void ResourceManager::write(const rclcpp::Time & time, const rclcpp::Duration & period)
1191+
HardwareReadWriteStatus ResourceManager::write(
1192+
const rclcpp::Time & time, const rclcpp::Duration & period)
11091193
{
1110-
for (auto & component : resource_storage_->actuators_)
1111-
{
1112-
component.write(time, period);
1113-
}
1114-
for (auto & component : resource_storage_->systems_)
1194+
std::lock_guard<std::recursive_mutex> guard(resources_lock_);
1195+
read_write_status.ok = true;
1196+
read_write_status.failed_hardware_names.clear();
1197+
1198+
auto write_components = [&](auto & components)
11151199
{
1116-
component.write(time, period);
1117-
}
1200+
for (auto & component : components)
1201+
{
1202+
if (component.write(time, period) != return_type::OK)
1203+
{
1204+
read_write_status.ok = false;
1205+
read_write_status.failed_hardware_names.push_back(component.get_name());
1206+
resource_storage_->remove_all_hardware_interfaces_from_available_list(component.get_name());
1207+
}
1208+
}
1209+
};
1210+
1211+
write_components(resource_storage_->actuators_);
1212+
write_components(resource_storage_->systems_);
1213+
1214+
return read_write_status;
11181215
}
11191216

11201217
void ResourceManager::validate_storage(

0 commit comments

Comments
 (0)