15#include "auto_apms_mission/mission_builder_base.hpp"
17#include "auto_apms_util/exceptions.hpp"
22const std::string MissionBuildHandlerBase::ORCHESTRATOR_EXECUTOR_NAME = _AUTO_APMS_MISSION__ORCHESTRATOR_EXECUTOR_NAME;
23const std::string MissionBuildHandlerBase::MISSION_EXECUTOR_NAME = _AUTO_APMS_MISSION__MISSION_EXECUTOR_NAME;
24const std::string MissionBuildHandlerBase::EVENT_MONITOR_EXECUTOR_NAME =
25 _AUTO_APMS_MISSION__EVENT_MONITOR_EXECUTOR_NAME;
26const std::string MissionBuildHandlerBase::EVENT_HANDLER_EXECUTOR_NAME =
27 _AUTO_APMS_MISSION__EVENT_HANDLER_EXECUTOR_NAME;
29MissionBuildHandlerBase::MissionBuildHandlerBase(
30 rclcpp::Node::SharedPtr ros_node_ptr, NodeLoader::SharedPtr tree_node_loader_ptr)
31: TreeBuildHandler(
"mission_builder", ros_node_ptr, tree_node_loader_ptr)
36 const std::string & build_request,
const NodeManifest & ,
const std::string & root_tree_name)
39 mission_config_ = MissionConfiguration::fromResourceIdentity(build_request);
40 }
catch (
const auto_apms_util::exceptions::ResourceIdentityFormatError & e) {
41 RCLCPP_WARN(
logger_,
"%s", e.what());
43 }
catch (
const auto_apms_util::exceptions::ResourceError & e) {
44 RCLCPP_WARN(
logger_,
"%s", e.what());
46 }
catch (
const auto_apms_util::exceptions::YAMLFormatError & e) {
47 RCLCPP_WARN(
logger_,
"Invalid YAML format of mission configuration '%s': %s", build_request.c_str(), e.what());
50 if (!root_tree_name.empty()) {
51 RCLCPP_WARN(
logger_,
"Argument root_tree_name is not empty. Custom root tree names are not allowed.");
56 auto add_tree_name_to_identity = [
this](TreeResource::Identity & identity) {
57 if (identity.tree_name.empty()) {
58 TreeResource r(identity);
63 for (TreeResource::Identity & identity : mission_config_.bringup) add_tree_name_to_identity(identity);
64 for (TreeResource::Identity & identity : mission_config_.mission) add_tree_name_to_identity(identity);
65 for (
auto & [monitor_id, handler_id] : mission_config_.contingency) {
66 add_tree_name_to_identity(monitor_id);
67 add_tree_name_to_identity(handler_id);
69 for (
auto & [monitor_id, handler_id] : mission_config_.emergency) {
70 add_tree_name_to_identity(monitor_id);
71 add_tree_name_to_identity(handler_id);
73 for (TreeResource::Identity & identity : mission_config_.shutdown) add_tree_name_to_identity(identity);
74 }
catch (
const auto_apms_util::exceptions::ResourceError & e) {
75 RCLCPP_WARN(
logger_,
"%s", e.what());
83 TreeDocument & doc, TreeBlackboard & bb)
88 RCLCPP_DEBUG(
logger_,
"Loading orchestrator tree.");
89 TreeDocument::TreeElement root_tree =
92 RCLCPP_DEBUG(
logger_,
"Configuring orchestrator root blackboard.");
93 configureOrchestratorRootBlackboard(bb);
98 if (mission_config_.contingency.empty()) {
99 is_contingency_tree.insertNode<model::AlwaysFailure>();
101 model::Fallback fallback = is_contingency_tree.insertNode<model::Fallback>();
102 for (
const auto & [monitor_id, _] : mission_config_.contingency) {
103 fallback.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
107 if (mission_config_.emergency.empty()) {
108 is_emergency_tree.insertNode<model::AlwaysFailure>();
110 model::Fallback fallback = is_emergency_tree.insertNode<model::Fallback>();
111 for (
const auto & [monitor_id, _] : mission_config_.emergency) {
112 fallback.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
117 if (!mission_config_.bringup.empty()) {
118 RCLCPP_DEBUG(
logger_,
"Creating bringup subtree.");
119 TreeDocument::TreeElement bringup_tree = doc.
getTree(
"__BringUp__");
120 buildBringUp(bringup_tree, mission_config_.bringup);
121 if (
const BT::Result res = bringup_tree.verify(); !res) {
122 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Bringup tree is not valid: " + res.error());
127 RCLCPP_DEBUG(
logger_,
"Creating mission subtree.");
128 TreeDocument::TreeElement mission_tree = doc.
getTree(
"__RunMission__");
129 buildMission(mission_tree, mission_config_.mission);
130 if (
const BT::Result res = mission_tree.verify(); !res) {
131 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Mission tree is not valid: " + res.error());
135 if (!mission_config_.contingency.empty() || !mission_config_.emergency.empty()) {
136 RCLCPP_DEBUG(
logger_,
"Creating event monitor subtree.");
137 TreeDocument::TreeElement event_monitor_tree = doc.
getTree(
"__MonitorEvents__");
138 buildEventMonitor(event_monitor_tree, mission_config_.contingency, mission_config_.emergency);
139 if (
const BT::Result res = event_monitor_tree.verify(); !res) {
140 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
141 "Event monitor tree is not valid: " + res.error());
146 if (!mission_config_.contingency.empty()) {
147 RCLCPP_DEBUG(
logger_,
"Creating contingency handler subtree.");
148 TreeDocument::TreeElement contingency_tree = doc.
getTree(
"__HandleContingency__");
149 buildContingencyHandling(contingency_tree, mission_config_.contingency);
150 if (
const BT::Result res = contingency_tree.verify(); !res) {
151 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
152 "Contingency handling tree is not valid: " + res.error());
157 if (!mission_config_.emergency.empty()) {
158 RCLCPP_DEBUG(
logger_,
"Creating emergency handler subtree.");
159 TreeDocument::TreeElement emergency_tree = doc.
getTree(
"__HandleEmergency__");
160 buildEmergencyHandling(emergency_tree, mission_config_.emergency);
161 if (
const BT::Result res = emergency_tree.verify(); !res) {
162 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
163 "Emergency handling tree is not valid: " + res.error());
168 if (!mission_config_.shutdown.empty()) {
169 RCLCPP_DEBUG(
logger_,
"Creating shutdown subtree.");
170 TreeDocument::TreeElement shutdown_tree = doc.
getTree(
"__ShutDown__");
171 buildShutDown(shutdown_tree, mission_config_.shutdown);
172 if (
const BT::Result res = shutdown_tree.verify(); !res) {
173 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Shutdown tree is not valid: " + res.error());
183void MissionBuildHandlerBase::buildBringUp(
184 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
186 sub_tree.removeChildren();
187 for (
const TreeResource::Identity & r : trees) {
188 sub_tree.insertTreeFromResource(r);
192void MissionBuildHandlerBase::buildEventMonitor(
193 TreeDocument::TreeElement & sub_tree,
194 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & contingencies,
195 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & emergencies)
197 std::vector<TreeResource::Identity> sorted_monitor_ids;
200 for (
const auto & [monitor_id, _] : emergencies) {
201 sorted_monitor_ids.push_back(monitor_id);
203 for (
const auto & [monitor_id, _] : contingencies) {
208 TreeDocument monitor_doc;
209 for (
const TreeResource::Identity & monitor_id : sorted_monitor_ids) {
210 if (!monitor_doc.hasTreeName(monitor_id.str())) {
211 monitor_doc.newTreeFromResource(monitor_id).setName(monitor_id.str());
215 model::Fallback fallback = sub_tree.removeChildren().insertNode<model::Fallback>();
216 for (
const TreeResource::Identity & monitor_id : sorted_monitor_ids) {
218 const std::string monitor_tree_name = monitor_id.str();
219 model::SubTree subtree_node = fallback.getParentDocument().hasTreeName(monitor_tree_name)
221 : fallback.insertNode<
model::
SubTree>(monitor_doc.getTree(monitor_tree_name));
222 subtree_node.setConditionalScript(BT::PostCond::ON_SUCCESS,
"event_id := '" + monitor_tree_name +
"'");
226void MissionBuildHandlerBase::buildContingencyHandling(
227 TreeDocument::TreeElement & sub_tree,
228 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & contingencies)
231 TreeDocument handler_doc;
232 for (
const auto & [_, handler_id] : contingencies) {
233 if (!handler_doc.hasTreeName(handler_id.str())) {
234 handler_doc.newTreeFromResource(handler_id).setName(handler_id.str());
238 model::ReactiveFallback fallback = sub_tree.removeChildren().insertNode<model::ReactiveFallback>();
242 fallback.insertNode<model::AlwaysFailure>()
243 .setName(
"ResetEventHandler")
244 .setConditionalScript(BT::PreCond::SUCCESS_IF,
"event_id == ''");
246 for (
const auto & [monitor_id, handler_id] : contingencies) {
247 model::AsyncSequence seq =
248 fallback.insertNode<model::AsyncSequence>().setName(
"EventHandler (" + handler_id.str() +
")");
249 seq.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
252 const std::string handler_tree_name = handler_id.str();
253 if (seq.getParentDocument().hasTreeName(handler_tree_name)) {
257 seq.insertNode<
model::SubTree>(handler_doc.getTree(handler_tree_name));
261 seq.insertNode<model::KeepRunningUntilFailure>()
262 .insertNode<model::ScriptCondition>()
263 .setName(
"IsEventStillActive")
264 .set_code(
"event_id == '" + monitor_id.str() +
"'");
268void MissionBuildHandlerBase::buildEmergencyHandling(
269 TreeDocument::TreeElement & sub_tree,
270 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & emergencies)
273 TreeDocument handler_doc;
274 for (
const auto & [_, handler_id] : emergencies) {
275 if (!handler_doc.hasTreeName(handler_id.str())) {
276 handler_doc.newTreeFromResource(handler_id).setName(handler_id.str());
280 model::ReactiveFallback fallback = sub_tree.removeChildren().insertNode<model::ReactiveFallback>();
281 for (
const auto & [monitor_id, handler_id] : emergencies) {
282 model::AsyncSequence seq =
283 fallback.insertNode<model::AsyncSequence>().setName(
"EventHandler (" + handler_id.str() +
")");
284 seq.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
287 const std::string handler_tree_name = handler_id.str();
288 if (seq.getParentDocument().hasTreeName(handler_tree_name)) {
292 seq.insertNode<
model::SubTree>(handler_doc.getTree(handler_tree_name));
297void MissionBuildHandlerBase::buildShutDown(
298 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
300 sub_tree.removeChildren();
301 for (
const TreeResource::Identity & r : trees) {
302 sub_tree.insertTreeFromResource(r);
306void MissionBuildHandlerBase::configureOrchestratorRootBlackboard(TreeBlackboard & ) {}
const rclcpp::Logger logger_
ROS 2 logger initialized with the name of the build handler.
TreeElement & removeChildren()
TreeElement & makeRoot()
Set this behavior tree as the root tree of the parent document.
TreeElement newTreeFromResource(const TreeResource &resource, const std::string &tree_name="")
Create a new behavior tree inside this document with the content of one the trees found inside a part...
TreeElement getTree(const std::string &tree_name)
Get the corresponding behavior tree element for a tree inside this document.
std::string getRootTreeName() const
Get the name of the root tree of this behavior tree resource.
Subtree behavior tree node model.
bool setBuildRequest(const std::string &build_request, const NodeManifest &node_manifest, const std::string &root_tree_name) override final
Specify the behavior tree build request encoded in a string.
TreeDocument::TreeElement buildTree(TreeDocument &doc, TreeBlackboard &bb) override final
Build the behavior tree specified before.
bool contains(const ContainerT< ValueT, AllocatorT > &c, const ValueT &val)
Check whether a particular container structure contains a value.
Models for all available behavior tree nodes.
Mission design utilities incorporating behavior trees to model the complexity of arbitrary operations...