AutoAPMS
Streamlining behaviors in ROS 2
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// http://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_ENTRY_POINT "entry_point"
22#define INPUT_KEY_NODE_MANIFEST "node_manifest"
23#define INPUT_KEY_ATTACH "attach"
24#define INPUT_KEY_CLEAR_BB "clear_blackboard"
25
27{
28
29class StartExecutor : public core::RosActionNode<auto_apms_interfaces::action::StartTreeExecutor>
30{
31public:
32 using RosActionNode::RosActionNode;
33
34 static BT::PortsList providedPorts()
35 {
36 // We do not use the default port for the action name
37 return {
38 BT::InputPort<bool>(
39 INPUT_KEY_ATTACH, true, "Boolean flag wether to attach to the execution process or start in detached mode."),
40 BT::InputPort<bool>(
41 INPUT_KEY_CLEAR_BB, true,
42 "Boolean flag wether to clear the existing blackboard entries before the execution starts or not."),
43 BT::InputPort<std::string>(
44 INPUT_KEY_NODE_MANIFEST, "",
45 "YAML/JSON formatted string encoding the name and the registration options for the tree nodes supposed to be "
46 "loaded before building the tree."),
47 BT::InputPort<std::string>(
48 INPUT_KEY_ENTRY_POINT, "",
49 "Entry point for the behavior. If empty, let the build handler determine it automatically."),
50 BT::InputPort<std::string>(
51 INPUT_KEY_TREE_BUILD_HANDLER, "",
52 "Fully qualified class name of the build handler that is supposed to take care of the request. If empty, use "
53 "the current one."),
54 BT::InputPort<std::string>(
55 INPUT_KEY_TREE_BUILD_REQUEST, "",
56 "String passed to the tree build handler defining which tree is to be built."),
57 BT::InputPort<std::string>(
58 INPUT_KEY_EXECUTOR_NAME,
59 "Name of the executor responsible for building and running the specified behavior tree."),
60 };
61 }
62
63 bool setGoal(Goal & goal) override final
64 {
65 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_TREE_BUILD_REQUEST)) {
66 goal.build_request = expected.value();
67 } else {
68 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
69 return false;
70 }
71 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_TREE_BUILD_HANDLER)) {
72 goal.build_handler = expected.value();
73 } else {
74 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
75 return false;
76 }
77 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_ENTRY_POINT)) {
78 goal.entry_point = expected.value();
79 } else {
80 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
81 return false;
82 }
83 if (const BT::Expected<std::string> expected = getInput<std::string>(INPUT_KEY_NODE_MANIFEST)) {
84 goal.node_manifest = expected.value();
85 } else {
86 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
87 return false;
88 }
89 if (const BT::Expected<bool> expected = getInput<bool>(INPUT_KEY_ATTACH)) {
90 goal.attach = expected.value();
91 } else {
92 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
93 return false;
94 }
95 if (const BT::Expected<bool> expected = getInput<bool>(INPUT_KEY_CLEAR_BB)) {
96 goal.clear_blackboard = expected.value();
97 } else {
98 RCLCPP_ERROR(logger_, "%s", expected.error().c_str());
99 return false;
100 }
101 return true;
102 }
103
104 BT::NodeStatus onResultReceived(const WrappedResult & wr) override final
105 {
106 switch (wr.code) {
107 case rclcpp_action::ResultCode::ABORTED: {
108 const std::string msg =
109 context_.getFullyQualifiedTreeNodeName(this) + " - Received result ABORTED: " + wr.result->message;
110 RCLCPP_ERROR_STREAM(logger_, msg);
111 throw exceptions::RosNodeError(msg);
112 break;
113 }
114 case rclcpp_action::ResultCode::CANCELED:
115 RCLCPP_DEBUG(
116 logger_, "%s - Received result CANCELED: %s", context_.getFullyQualifiedTreeNodeName(this).c_str(),
117 wr.result->message.c_str());
118 // Return value is arbitrary because it is ignored when canceled
119 return BT::NodeStatus::SUCCESS;
120 default:
121 break;
122 }
123
124 /* If action succeeded */
125
126 RCLCPP_DEBUG(
127 logger_, "%s - Tree execution finished successfully with result %i: %s",
128 context_.getFullyQualifiedTreeNodeName(this).c_str(), wr.result->tree_result, wr.result->message.c_str());
129
130 // If started in attached mode
131 if (getInput<bool>(INPUT_KEY_ATTACH).value()) {
132 if (wr.result->tree_result == ActionType::Result::TREE_RESULT_SUCCESS) return BT::NodeStatus::SUCCESS;
133 return BT::NodeStatus::FAILURE;
134 }
135
136 // If started in detached mode
137 if (wr.result->tree_result == ActionType::Result::TREE_RESULT_NOT_SET) return BT::NodeStatus::SUCCESS;
138 const std::string msg = context_.getFullyQualifiedTreeNodeName(this) +
139 " - Expected tree_result to be TREE_RESULT_NOT_SET when started in detached mode.";
140 RCLCPP_ERROR_STREAM(logger_, msg);
141 throw exceptions::RosNodeError(msg);
142 }
143};
144
145} // namespace auto_apms_behavior_tree
146
147AUTO_APMS_BEHAVIOR_TREE_REGISTER_NODE(auto_apms_behavior_tree::StartExecutor)
Generic behavior tree node wrapper for a ROS 2 action client.
#define AUTO_APMS_BEHAVIOR_TREE_REGISTER_NODE(type)
Macro for registering a behavior tree node plugin.
Definition node.hpp:44
Powerful tooling for incorporating behavior trees for task development.
Definition behavior.hpp:32