-
Notifications
You must be signed in to change notification settings - Fork 349
Deactivate controllers with command interfaces to hardware on DEACTIVATE #2334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
for (const auto & controller : controllers) | ||
{ | ||
auto controller_spec = std::find_if( | ||
loaded_controllers.begin(), loaded_controllers.end(), | ||
[&](const controller_manager::ControllerSpec & spec) | ||
{ return spec.c->get_name() == controller; }); | ||
if (controller_spec == loaded_controllers.end()) | ||
{ | ||
RCLCPP_WARN( | ||
get_logger(), | ||
"Deactivate failed to find controller [%s] in loaded controllers. " | ||
"This can happen due to multiple returns of 'DEACTIVATE' from [%s] read()", | ||
controller.c_str(), hardware_name.c_str()); | ||
continue; | ||
} | ||
std::vector<std::string> command_interface_names; | ||
extract_command_interfaces_for_controller( | ||
*controller_spec, resource_manager_, command_interface_names); | ||
// if this controller has command interfaces add it to the deactivate_controllers_list | ||
if (!command_interface_names.empty()) | ||
{ | ||
rt_buffer_.deactivate_controllers_list.push_back(controller); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have suggestion for a more efficient way of finding the active command interfaces to a controller I'd appreciate it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there anything here that could be refactored into a function? the idea being that you've got essentially the same code duplicated below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've got some ideas to optimize this in a better way. I can open a follow-up PR for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes sense to return DEACTIVATE from read() because that interface is expected to continue to work after the hardware goes inactive. What do you guys think about warning users that this is not supported in read()
and to update their code to only return DEACTIVATE in write()
? If that is okay I can remove all of this code here and update the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rethinking about this. When we receive DEACTIVATE from the read, it should work similar to the ERROR state, both read and write calls shouldn't work and for write, only write method shouldn't be called.
- When DEACTIVATE is returned through
read
method, then it should behave exactly likeerror
state (No further read and write calls, unless activated) and all controllers that use any state and command interfaces should be deactivated) - When DEACTIVATE is returned through
write
method, then onlyread
calls are allowed (only those use command interfaces should be deactivated)
What do you think? @MarqRazz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that is how I was thinking it should work, but with the addition of warning the user when they return DEACTIVATE in read
that it is treated the same as ERROR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, but the component should go INACTIVE instead of ERROR right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My takeaway from Marq's comments is that the component should transition to ERROR if the user needs to return DEACTIVATE from read()
. In that case, we could provide an unsupported warning and recommend ERROR as the return value. This simplifies things conceptually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is what I implemented and added comments to the new section of code below. Let me know if we would prefer the hardware go INACTIVE vs ERROR.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2334 +/- ##
==========================================
+ Coverage 88.98% 89.01% +0.03%
==========================================
Files 147 147
Lines 16708 16785 +77
Branches 1433 1438 +5
==========================================
+ Hits 14867 14941 +74
- Misses 1285 1286 +1
- Partials 556 558 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the return type is a slight ABI break but it's internal API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
for (const auto & controller : controllers) | ||
{ | ||
auto controller_spec = std::find_if( | ||
loaded_controllers.begin(), loaded_controllers.end(), | ||
[&](const controller_manager::ControllerSpec & spec) | ||
{ return spec.c->get_name() == controller; }); | ||
if (controller_spec == loaded_controllers.end()) | ||
{ | ||
RCLCPP_WARN( | ||
get_logger(), | ||
"Deactivate failed to find controller [%s] in loaded controllers. " | ||
"This can happen due to multiple returns of 'DEACTIVATE' from [%s] read()", | ||
controller.c_str(), hardware_name.c_str()); | ||
continue; | ||
} | ||
std::vector<std::string> command_interface_names; | ||
extract_command_interfaces_for_controller( | ||
*controller_spec, resource_manager_, command_interface_names); | ||
// if this controller has command interfaces add it to the deactivate_controllers_list | ||
if (!command_interface_names.empty()) | ||
{ | ||
rt_buffer_.deactivate_controllers_list.push_back(controller); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've got some ideas to optimize this in a better way. I can open a follow-up PR for this
if (ret_val == hardware_interface::return_type::DEACTIVATE) | ||
{ | ||
RCLCPP_WARN( | ||
get_logger(), "DEACTIVATE returned from read cycle is treated the same as ERROR."); | ||
ret_val = hardware_interface::return_type::ERROR; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented this as DEACTIVATE == ERROR in read()
. If we want the hardware to go INACTIVE
here it would be a simple change but can't think of a use case where a user would want to return DEACTIVATE vs ERROR in read. Would that mean they can't read from the device but they are still connected to the hardware and want to stop updating state??
Closes #2318
While testing with hardware I found it was easiest to only return DEACTIVATE from write() so that my hardware_interface didn't have to keep track of the number of times DEACTIVATE has been returned in read(). I also found that allowing write() while the hardware is INACTIVE does not make sense because ALL commanding controllers are stopped on deactivation and the hardware would not know which command_interfaces might be allowed returning DEACTIVATE again which would cause the controller_manager to get stuck in a loop attempting to stop any controllers with with command interfaces to the hardware.
I also added 2 new test to make sure the controller_manager correctly handles DEACTIVATE on read/write.