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;
29bool MissionBuildHandlerBase::setBuildRequest(
30 const std::string & build_request,
const std::string & entrypoint,
const NodeManifest & node_manifest)
32 if (!entrypoint.empty()) {
33 RCLCPP_WARN(
logger_,
"Argument entrypoint is not empty. Custom entrypoints are not supported and will be ignored.");
35 if (!node_manifest.empty()) {
37 logger_,
"Argument node_manifest is not empty. Custom node manifests are not supported and will be ignored.");
40 mission_config_ = createMissionConfig(build_request);
41 }
catch (
const auto_apms_util::exceptions::ResourceIdentityFormatError & e) {
42 RCLCPP_WARN(
logger_,
"%s", e.what());
44 }
catch (
const auto_apms_util::exceptions::ResourceError & e) {
45 RCLCPP_WARN(
logger_,
"%s", e.what());
47 }
catch (
const auto_apms_util::exceptions::YAMLFormatError & e) {
48 RCLCPP_WARN(
logger_,
"Invalid YAML format of mission configuration '%s': %s", build_request.c_str(), e.what());
53 auto add_tree_name_to_identity = [
this](TreeResource::Identity & identity) {
54 if (identity.tree_name.empty()) {
55 TreeResource r(identity);
56 identity.tree_name = r.getRootTreeName();
60 for (TreeResource::Identity & identity : mission_config_.bringup) add_tree_name_to_identity(identity);
61 for (TreeResource::Identity & identity : mission_config_.mission) add_tree_name_to_identity(identity);
62 for (
auto & [monitor_id, handler_id] : mission_config_.contingency) {
63 add_tree_name_to_identity(monitor_id);
64 add_tree_name_to_identity(handler_id);
66 for (
auto & [monitor_id, handler_id] : mission_config_.emergency) {
67 add_tree_name_to_identity(monitor_id);
68 add_tree_name_to_identity(handler_id);
70 for (TreeResource::Identity & identity : mission_config_.shutdown) add_tree_name_to_identity(identity);
71 }
catch (
const auto_apms_util::exceptions::ResourceError & e) {
72 RCLCPP_WARN(
logger_,
"%s", e.what());
79MissionBuildHandlerBase::TreeDocument::TreeElement MissionBuildHandlerBase::buildTree(
80 TreeDocument & doc, TreeBlackboard & bb)
82 using namespace auto_apms_behavior_tree::model;
85 RCLCPP_DEBUG(
logger_,
"Loading orchestrator tree.");
86 TreeDocument::TreeElement root_tree =
87 doc.newTreeFromResource(
"tree__internal/auto_apms_mission::orchestrator_base::MissionOrchestrator").makeRoot();
89 RCLCPP_DEBUG(
logger_,
"Configuring orchestrator root blackboard.");
90 configureOrchestratorRootBlackboard(bb);
92 TreeDocument::TreeElement is_contingency_tree = doc.getTree(
"__IsContingency__").removeChildren();
93 TreeDocument::TreeElement is_emergency_tree = doc.getTree(
"__IsEmergency__").removeChildren();
95 if (mission_config_.contingency.empty()) {
96 is_contingency_tree.insertNode<model::AlwaysFailure>();
98 model::Fallback fallback = is_contingency_tree.insertNode<model::Fallback>();
99 for (
const auto & [monitor_id, _] : mission_config_.contingency) {
100 fallback.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
104 if (mission_config_.emergency.empty()) {
105 is_emergency_tree.insertNode<model::AlwaysFailure>();
107 model::Fallback fallback = is_emergency_tree.insertNode<model::Fallback>();
108 for (
const auto & [monitor_id, _] : mission_config_.emergency) {
109 fallback.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
114 if (!mission_config_.bringup.empty()) {
115 RCLCPP_DEBUG(
logger_,
"Creating bringup subtree.");
116 TreeDocument::TreeElement bringup_tree = doc.getTree(
"__BringUp__");
117 buildBringUp(bringup_tree, mission_config_.bringup);
118 if (
const BT::Result res = bringup_tree.verify(); !res) {
119 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Bringup tree is not valid: " + res.error());
124 RCLCPP_DEBUG(
logger_,
"Creating mission subtree.");
125 TreeDocument::TreeElement mission_tree = doc.getTree(
"__RunMission__");
126 buildMission(mission_tree, mission_config_.mission);
127 if (
const BT::Result res = mission_tree.verify(); !res) {
128 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Mission tree is not valid: " + res.error());
132 if (!mission_config_.contingency.empty() || !mission_config_.emergency.empty()) {
133 RCLCPP_DEBUG(
logger_,
"Creating event monitor subtree.");
134 TreeDocument::TreeElement event_monitor_tree = doc.getTree(
"__MonitorEvents__");
135 buildEventMonitor(event_monitor_tree, mission_config_.contingency, mission_config_.emergency);
136 if (
const BT::Result res = event_monitor_tree.verify(); !res) {
137 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
138 "Event monitor tree is not valid: " + res.error());
143 if (!mission_config_.contingency.empty()) {
144 RCLCPP_DEBUG(
logger_,
"Creating contingency handler subtree.");
145 TreeDocument::TreeElement contingency_tree = doc.getTree(
"__HandleContingency__");
146 buildContingencyHandling(contingency_tree, mission_config_.contingency);
147 if (
const BT::Result res = contingency_tree.verify(); !res) {
148 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
149 "Contingency handling tree is not valid: " + res.error());
154 if (!mission_config_.emergency.empty()) {
155 RCLCPP_DEBUG(
logger_,
"Creating emergency handler subtree.");
156 TreeDocument::TreeElement emergency_tree = doc.getTree(
"__HandleEmergency__");
157 buildEmergencyHandling(emergency_tree, mission_config_.emergency);
158 if (
const BT::Result res = emergency_tree.verify(); !res) {
159 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
160 "Emergency handling tree is not valid: " + res.error());
165 if (!mission_config_.shutdown.empty()) {
166 RCLCPP_DEBUG(
logger_,
"Creating shutdown subtree.");
167 TreeDocument::TreeElement shutdown_tree = doc.getTree(
"__ShutDown__");
168 buildShutDown(shutdown_tree, mission_config_.shutdown);
169 if (
const BT::Result res = shutdown_tree.verify(); !res) {
170 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Shutdown tree is not valid: " + res.error());
180void MissionBuildHandlerBase::buildBringUp(
181 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
183 sub_tree.removeChildren();
184 for (
const TreeResource::Identity & r : trees) {
185 RCLCPP_DEBUG(
logger_,
"Adding bringup tree '%s'.", r.str().c_str());
186 sub_tree.insertTreeFromResource(r);
190void MissionBuildHandlerBase::buildMission(
191 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
193 sub_tree.removeChildren();
194 for (
const TreeResource::Identity & r : trees) {
195 RCLCPP_DEBUG(
logger_,
"Adding mission tree '%s'.", r.str().c_str());
196 sub_tree.insertTreeFromResource(r);
200void MissionBuildHandlerBase::buildEventMonitor(
201 TreeDocument::TreeElement & sub_tree,
202 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & contingencies,
203 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & emergencies)
205 std::vector<TreeResource::Identity> sorted_monitor_ids;
208 for (
const auto & [monitor_id, _] : emergencies) {
209 sorted_monitor_ids.push_back(monitor_id);
211 for (
const auto & [monitor_id, _] : contingencies) {
216 TreeDocument monitor_doc;
217 for (
const TreeResource::Identity & monitor_id : sorted_monitor_ids) {
218 if (!monitor_doc.hasTreeName(monitor_id.str())) {
219 RCLCPP_DEBUG(
logger_,
"Adding event monitor tree '%s'.", monitor_id.str().c_str());
221 logger_,
"Identity has file stem '%s' and tree name '%s'.", monitor_id.file_stem.c_str(),
222 monitor_id.tree_name.c_str());
223 monitor_doc.newTreeFromResource(monitor_id).setName(monitor_id.str());
227 model::Fallback fallback = sub_tree.removeChildren().insertNode<model::Fallback>();
228 for (
const TreeResource::Identity & monitor_id : sorted_monitor_ids) {
230 const std::string monitor_tree_name = monitor_id.str();
231 model::SubTree subtree_node = fallback.getParentDocument().hasTreeName(monitor_tree_name)
232 ? fallback.insertNode<model::SubTree>(monitor_tree_name)
233 : fallback.insertNode<model::SubTree>(monitor_doc.getTree(monitor_tree_name));
234 subtree_node.
setConditionalScript(BT::PostCond::ON_SUCCESS,
"event_id := '" + monitor_tree_name +
"'");
238void MissionBuildHandlerBase::buildContingencyHandling(
239 TreeDocument::TreeElement & sub_tree,
240 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & contingencies)
243 TreeDocument handler_doc;
244 for (
const auto & [_, handler_id] : contingencies) {
245 if (!handler_doc.hasTreeName(handler_id.str())) {
246 RCLCPP_DEBUG(
logger_,
"Adding contingency handler tree '%s'.", handler_id.str().c_str());
247 handler_doc.newTreeFromResource(handler_id).setName(handler_id.str());
251 model::ReactiveFallback fallback = sub_tree.removeChildren().insertNode<model::ReactiveFallback>();
255 fallback.insertNode<model::AlwaysFailure>()
256 .setName(
"ResetEventHandler")
257 .setConditionalScript(BT::PreCond::SUCCESS_IF,
"event_id == ''");
259 for (
const auto & [monitor_id, handler_id] : contingencies) {
260 model::AsyncSequence seq =
261 fallback.insertNode<model::AsyncSequence>().setName(
"EventHandler (" + handler_id.str() +
")");
262 seq.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
265 const std::string handler_tree_name = handler_id.str();
266 if (seq.getParentDocument().hasTreeName(handler_tree_name)) {
267 seq.insertNode<model::SubTree>(handler_tree_name);
270 seq.insertNode<model::SubTree>(handler_doc.getTree(handler_tree_name));
274 seq.insertNode<model::KeepRunningUntilFailure>()
275 .insertNode<model::ScriptCondition>()
276 .setName(
"IsEventStillActive")
277 .set_code(
"event_id == '" + monitor_id.str() +
"'");
281void MissionBuildHandlerBase::buildEmergencyHandling(
282 TreeDocument::TreeElement & sub_tree,
283 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & emergencies)
286 TreeDocument handler_doc;
287 for (
const auto & [_, handler_id] : emergencies) {
288 if (!handler_doc.hasTreeName(handler_id.str())) {
289 RCLCPP_DEBUG(
logger_,
"Adding emergency handler tree '%s'.", handler_id.str().c_str());
290 handler_doc.newTreeFromResource(handler_id).setName(handler_id.str());
294 model::ReactiveFallback fallback = sub_tree.removeChildren().insertNode<model::ReactiveFallback>();
295 for (
const auto & [monitor_id, handler_id] : emergencies) {
296 model::AsyncSequence seq =
297 fallback.insertNode<model::AsyncSequence>().setName(
"EventHandler (" + handler_id.str() +
")");
298 seq.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
301 const std::string handler_tree_name = handler_id.str();
302 if (seq.getParentDocument().hasTreeName(handler_tree_name)) {
303 seq.insertNode<model::SubTree>(handler_tree_name);
306 seq.insertNode<model::SubTree>(handler_doc.getTree(handler_tree_name));
311void MissionBuildHandlerBase::buildShutDown(
312 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
314 sub_tree.removeChildren();
315 for (
const TreeResource::Identity & r : trees) {
316 RCLCPP_DEBUG(
logger_,
"Adding shutdown tree '%s'.", r.str().c_str());
317 sub_tree.insertTreeFromResource(r);
321void MissionBuildHandlerBase::configureOrchestratorRootBlackboard(TreeBlackboard & ) {}
const rclcpp::Logger logger_
ROS 2 logger initialized with the name of the build handler.
NodeElement & setConditionalScript(BT::PreCond type, const Script &script)
Specify a script that is evaluated before this node is ticked.
bool contains(const ContainerT< ValueT, AllocatorT > &c, const ValueT &val)
Check whether a particular container structure contains a value.
Mission design utilities incorporating behavior trees to model the complexity of arbitrary operations...