Support PerformAction event in composed tasks#155
Conversation
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]>
| using ActionExecutor = | ||
| std::function<void( | ||
| const nlohmann::json& action, | ||
| const ActionCompleted& completed) |
There was a problem hiding this comment.
Let's send this argument by value instead of by reference as a kind of encouragement for the user to save a copy of it. There's nothing wrong with passing by reference, but it usually carries the implication that the object is only meant to be viewed rather than used.
| try | ||
| { | ||
| const nlohmann::json action = | ||
| nlohmann::json::parse(msg["action"].get<std::string>()); |
There was a problem hiding this comment.
I don't think this is a good way to use the nlohmann library or JSON more generally. What this is literally saying is that the action field will refer to a string, and that string will contain an entire JSON message inside of it. So that might look something like this:
"action": "{
\"clean\": {
\"zone\": \"terminal_3\"
}
}"
It would become a nightmare of escaping quotes and a very confusing structure.
I recommend the following schema instead:
"perform_action": {
"category": "some_simple_string",
"description": {
"any": 0
"details": { },
"about": [ ],
"this": true
"action": " "
},
"expected_finish_location": <optional>,
"unix_millis_action_duration_estimate": <optional>,
... any other optional task planner details ...
}
The add_performable_action function will have this signature instead:
FleetUpdateHandle& FleetUpdateHandle::add_performable_action(
const std::string& category,
ConsiderRequest consider);
We will maintain a map from category string keys to consider callbacks. When a perform_action event comes in, we will do a look up on that map. If the lookup fails, we reject the task. If the look up succeeds, we trigger the consider callback and then accept or reject based on its response.
The ActionExecutor signature would look like this instead:
using ActionExecutor =
std::function<void(
const std::string& category,
const nlohmann::json& description,
ActionCompleted completed)
This should make it very easy for users to put in their own arbitrary JSON details for the action while still allowing the fleet adapter to do the heavy lifting in weeding out irrelevant task requests.
There was a problem hiding this comment.
Thanks for the feedback. I can see how this is much easier for the user. I have a couple of questions on the schema
- What is the purpose of the
any,detailandaboutfields? - I assume the intention is for the entire
descriptionJSON block to be passed intoPerformAction::Description::make()as theaction. Is this correct?
There was a problem hiding this comment.
The any, details, about, this, action are just examples of how a user can fill the description JSON with any kind of data they want. You can totally disregard them, they're not actually part of the schema, just placeholders.
I assume the intention is for the entire description JSON block to be passed into PerformAction::Description::make() as the action. Is this correct?
That's right, the PerformAction::Description would need to have some kind of action_message field (or something similarly named) to store the message that gets passed to the robot command handle.
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]>
mxgrey
left a comment
There was a problem hiding this comment.
This is generally looking really good! I just have some recommendations for tightening up the API.
| } | ||
| }; | ||
|
|
||
| _pimpl->deserialization.event->add( |
There was a problem hiding this comment.
We shouldn't need to re-add "perform_action" each time the user adds a new category. We can add this once at startup and then just update the action category map when the user wants to add a new action category.
There was a problem hiding this comment.
Moved it into FleetUpdateHandle::Implementation::make(~)
There was a problem hiding this comment.
Moved it into FleetUpdateHandle::Implementation::make(~)
There was a problem hiding this comment.
Sorry to be nitpicky about this, but could we imitate the pattern that's being used in FleetUpdateHandle::Implementation::add_standard_tasks? Basically put this implementation into a events::add_perform_action(~) function and define that function in src/rmf_fleet_adapter/events/PerformAction.cpp?
I think that would organize the code a bit better and reduce the already considerable amount of noise in this make function.
rmf_fleet_adapter/include/rmf_fleet_adapter/agv/RobotUpdateHandle.hpp
Outdated
Show resolved
Hide resolved
rmf_fleet_adapter/include/rmf_fleet_adapter/agv/RobotUpdateHandle.hpp
Outdated
Show resolved
Hide resolved
rmf_fleet_adapter/src/rmf_fleet_adapter/events/PerformAction.cpp
Outdated
Show resolved
Hide resolved
rmf_fleet_adapter/src/rmf_fleet_adapter/events/PerformAction.cpp
Outdated
Show resolved
Hide resolved
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]>
| // TODO: Consider giving access to the participant schedule and | ||
| // traffic negotiation | ||
|
|
||
| ~ActionExecution() |
There was a problem hiding this comment.
We should put the definition of this function into the proper translation unit, i.e. RobotUpdateHandle.cpp. I understand you were probably copying the example code that I provided, but I defined it inline for brevity. It's not actually good to leave this function definition exposed in a public API, because that can lead to poorly defined behavior if we ever change the implementation of the function.
| }, | ||
| "description": { | ||
| "description": "A dictionary describing the action to be performed.", | ||
| "type": "object" |
There was a problem hiding this comment.
Let's not force this to be a dictionary. Maybe a system integrator would like this to be an array. We can just remove this "type": "object" specification and leave it ambiguous.
I'd also recommend changing description to something like `"Additional information that will be passed along to the action executor."
| void RobotContext::action_executor( | ||
| RobotUpdateHandle::ActionExecutor action_executor) | ||
| { | ||
| if (action_executor != nullptr) |
There was a problem hiding this comment.
I'm not actually sure that we should block this. What if the user is trying to erase its action executor for some reason?
I recommend instead of blocking, we simply emit a warning that the action executor is being nullified which will lead to an automatic critical task error if a task requires an action to be performed while this is still set to null.
| } | ||
|
|
||
| static const Implementation& get(const RobotUpdateHandle& handle) | ||
| static const Implementation &get(const RobotUpdateHandle &handle) |
There was a problem hiding this comment.
Looks like some automatic styling has messed things up here.
| _state->update_status(Status::Error); | ||
| const std::string msg = "ActionExecutor not set via RobotUpdateHandle. " | ||
| "Unable to perform the requested action."; | ||
| _state->update_log().info(msg); |
There was a problem hiding this comment.
This should be an error-level log, not info.
| const std::string msg = "ActionExecutor not set via RobotUpdateHandle. " | ||
| "Unable to perform the requested action."; | ||
| _state->update_log().info(msg); | ||
| _finished(); |
There was a problem hiding this comment.
Let's update the status to an error before finishing.
|
This is shaping up very nicely. I just left a few more small remarks to address and then I'll be happy to approve 👌 |
Signed-off-by: Yadunund <[email protected]>
|
Thanks for all the great feedback! |
Signed-off-by: Yadunund <[email protected]>
Signed-off-by: Yadunund <[email protected]> Signed-off-by: Luca Della Vedova <[email protected]> Co-authored-by: Luca Della Vedova <[email protected]>
This PR aims to introduce support for
PerformActionevent. ChangesPerformActionevent descriptionFleetUpdateHandle::add_performable_action(const std::string& category) API: Set the category of actions that robots in this fleet can perform.RobotUpdateHandle::set_action_executor(ActionExecutor action_executor)API: Set the executor which can be used by the fleet adapter to request a robot to perform an action andRobotUpdateHandle::update_action_remaining_time(rmf_traffic::Duration duration)to update the estimate of the time remaining for the action.StandbyandActivedefinitions forPerformActionThis PR requires open-rmf/rmf_task#49