AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
start_executor.cpp
1// Copyright 2024 Robin Müller
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "auto_apms_behavior_tree_core/node.hpp"
16#include "auto_apms_interfaces/action/start_tree_executor.hpp"
17
18#define INPUT_KEY_EXECUTOR_NAME "executor"
19#define INPUT_KEY_TREE_BUILD_REQUEST "build_request"
20#define INPUT_KEY_TREE_BUILD_HANDLER "build_handler"
21#define INPUT_KEY_ROOT_TREE_NAME "root_tree"
22#define INPUT_KEY_NODE_MANIFEST "node_manifest"
23#define INPUT_KEY_NODE_OVERRIDES "node_overrides"
24#define INPUT_KEY_ATTACH "attach"
25#define INPUT_KEY_CLEAR_BB "clear_blackboard"
26
28{
29
30class StartExecutor : public core::RosActionNode<auto_apms_interfaces::action::StartTreeExecutor>
31{
32public:
33 using RosActionNode::RosActionNode;
34
35 static BT::PortsList providedPorts()
36 {
37 // We do not use the default port for the action name
38 return {
39 BT::InputPort<bool>(
40 INPUT_KEY_ATTACH, true, "Boolean flag wether to attach to the execution process or start in detached mode."),
41 BT::InputPort<bool>(
42 INPUT_KEY_CLEAR_BB, true,
43 "Boolean flag wether to clear the existing blackboard entries before the execution starts or not."),
44 BT::InputPort<std::string>(
45 INPUT_KEY_NODE_OVERRIDES, "",
46 "YAML/JSON formatted string encoding the name and the registration options for any tree nodes supposed to "
47 "override previously loaded ones."),
48 BT::InputPort<std::string>(
49 INPUT_KEY_NODE_MANIFEST, "",
50 "YAML/JSON formatted string encoding the name and the registration options for the tree nodes supposed to be "
51 "loaded before building the tree."),
52 BT::InputPort<std::string>(
53 INPUT_KEY_ROOT_TREE_NAME, "",
54 "Name of the root tree. If empty, let the build handler determine the root tree."),
55 BT::InputPort<std::string>(
56 INPUT_KEY_TREE_BUILD_HANDLER, "",
57 "Fully qualified class name of the build handler that is supposed to take care of the request. If empty, use "
58 "the current one."),
59 BT::InputPort<std::string>(
60 INPUT_KEY_TREE_BUILD_REQUEST, "String passed to the tree build handler defining which tree is to be built."),
61 BT::InputPort<std::string>(
62 INPUT_KEY_EXECUTOR_NAME,
63 "Name of the executor responsible for building and running the specified behavior tree."),
64 };
65 }
66
67 bool setGoal(Goal & goal) override final
68 {
69 const BT::Expected<std::string> expected_build_request = getInput<std::string>(INPUT_KEY_TREE_BUILD_REQUEST);
70 if (!expected_build_request || expected_build_request.value().empty()) {
71 RCLCPP_ERROR(
72 logger_, "%s - You must provide a non-empty build request.",
73 context_.getFullyQualifiedTreeNodeName(this).c_str());
74 RCLCPP_DEBUG_EXPRESSION(
75 logger_, !expected_build_request, "%s - Error message: %s",
76 context_.getFullyQualifiedTreeNodeName(this).c_str(), expected_build_request.error().c_str());
77 return false;
78 }
79 goal.build_request = expected_build_request.value();
80 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_TREE_BUILD_HANDLER)) {
81 goal.build_handler = expected.value();
82 } else {
83 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
84 return false;
85 }
86 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_ROOT_TREE_NAME)) {
87 goal.root_tree = expected.value();
88 } else {
89 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
90 return false;
91 }
92 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_NODE_MANIFEST)) {
93 goal.node_manifest = expected.value();
94 } else {
95 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
96 return false;
97 }
98 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_NODE_OVERRIDES)) {
99 goal.node_overrides = expected.value();
100 } else {
101 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
102 return false;
103 }
104 if (const BT::Expected<bool> expected = getInput<bool>(INPUT_KEY_ATTACH)) {
105 goal.attach = expected.value();
106 } else {
107 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
108 return false;
109 }
110 if (const BT::Expected<bool> expected = getInput<bool>(INPUT_KEY_CLEAR_BB)) {
111 goal.clear_blackboard = expected.value();
112 } else {
113 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
114 return false;
115 }
116 return true;
117 }
118
119 BT::NodeStatus onResultReceived(const WrappedResult & wr) override final
120 {
121 switch (wr.code) {
122 case rclcpp_action::ResultCode::ABORTED: {
123 const std::string msg =
124 context_.getFullyQualifiedTreeNodeName(this) + " - Received result ABORTED: " + wr.result->message;
125 RCLCPP_ERROR_STREAM(logger_, msg);
126 throw exceptions::RosNodeError(msg);
127 break;
128 }
129 case rclcpp_action::ResultCode::CANCELED:
130 RCLCPP_DEBUG(
131 logger_, "%s - Received result CANCELED: %s", context_.getFullyQualifiedTreeNodeName(this).c_str(),
132 wr.result->message.c_str());
133 // Return value is arbitrary because it is ignored when canceled
134 return BT::NodeStatus::SUCCESS;
135 default:
136 break;
137 }
138
139 /* If action succeeded */
140
141 RCLCPP_DEBUG(
142 logger_, "%s - Tree execution finished successfully with result %i: %s",
143 context_.getFullyQualifiedTreeNodeName(this).c_str(), wr.result->tree_result, wr.result->message.c_str());
144
145 // If started in attached mode
146 if (getInput<bool>(INPUT_KEY_ATTACH).value()) {
147 if (wr.result->tree_result == ActionType::Result::TREE_RESULT_SUCCESS) return BT::NodeStatus::SUCCESS;
148 return BT::NodeStatus::FAILURE;
149 }
150
151 // If started in detached mode
152 if (wr.result->tree_result == ActionType::Result::TREE_RESULT_NOT_SET) return BT::NodeStatus::SUCCESS;
153 const std::string msg = context_.getFullyQualifiedTreeNodeName(this) +
154 " - Expected tree_result to be TREE_RESULT_NOT_SET when started in detached mode.";
155 RCLCPP_ERROR_STREAM(logger_, msg);
156 throw exceptions::RosNodeError(msg);
157 }
158};
159
160} // namespace auto_apms_behavior_tree
161
162AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE(auto_apms_behavior_tree::StartExecutor)
Generic behavior tree node wrapper for a ROS 2 action client.
#define AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE(type)
Macro for registering a behavior tree node plugin.
Definition node.hpp:40
Useful tooling for incorporating behavior trees for task development.
Definition builder.hpp:30