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 & entry_point,
const NodeManifest & node_manifest)
32 if (!entry_point.empty()) {
34 logger_,
"Argument entry_point is not empty. Custom entry_points are not supported and will be ignored.");
36 if (!node_manifest.empty()) {
38 logger_,
"Argument node_manifest is not empty. Custom node manifests are not supported and will be ignored.");
41 mission_config_ = createMissionConfig(build_request);
42 }
catch (
const auto_apms_util::exceptions::ResourceIdentityFormatError & e) {
43 RCLCPP_WARN(
logger_,
"%s", e.what());
45 }
catch (
const auto_apms_util::exceptions::ResourceError & e) {
46 RCLCPP_WARN(
logger_,
"%s", e.what());
48 }
catch (
const auto_apms_util::exceptions::YAMLFormatError & e) {
49 RCLCPP_WARN(
logger_,
"Invalid YAML format of mission configuration '%s': %s", build_request.c_str(), e.what());
54 auto add_tree_name_to_identity = [
this](TreeResource::Identity & identity) {
55 if (identity.tree_name.empty()) {
56 TreeResource r(identity);
57 identity.tree_name = r.getRootTreeName();
61 for (TreeResource::Identity & identity : mission_config_.bringup) add_tree_name_to_identity(identity);
62 for (TreeResource::Identity & identity : mission_config_.mission) add_tree_name_to_identity(identity);
63 for (
auto & [monitor_id, handler_id] : mission_config_.contingency) {
64 add_tree_name_to_identity(monitor_id);
65 add_tree_name_to_identity(handler_id);
67 for (
auto & [monitor_id, handler_id] : mission_config_.emergency) {
68 add_tree_name_to_identity(monitor_id);
69 add_tree_name_to_identity(handler_id);
71 for (TreeResource::Identity & identity : mission_config_.shutdown) add_tree_name_to_identity(identity);
72 }
catch (
const auto_apms_util::exceptions::ResourceError & e) {
73 RCLCPP_WARN(
logger_,
"%s", e.what());
80MissionBuildHandlerBase::TreeDocument::TreeElement MissionBuildHandlerBase::buildTree(
81 TreeDocument & doc, TreeBlackboard & bb)
83 using namespace auto_apms_behavior_tree::model;
86 RCLCPP_DEBUG(
logger_,
"Loading orchestrator tree.");
87 TreeDocument::TreeElement root_tree =
88 doc.newTreeFromResource(
"tree__internal/auto_apms_mission::orchestrator_base::MissionOrchestrator").makeRoot();
90 RCLCPP_DEBUG(
logger_,
"Configuring orchestrator root blackboard.");
91 configureOrchestratorRootBlackboard(bb);
93 TreeDocument::TreeElement is_contingency_tree = doc.getTree(
"__IsContingency__").removeChildren();
94 TreeDocument::TreeElement is_emergency_tree = doc.getTree(
"__IsEmergency__").removeChildren();
96 if (mission_config_.contingency.empty()) {
97 is_contingency_tree.insertNode<model::AlwaysFailure>();
99 model::Fallback fallback = is_contingency_tree.insertNode<model::Fallback>();
100 for (
const auto & [monitor_id, _] : mission_config_.contingency) {
101 fallback.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
105 if (mission_config_.emergency.empty()) {
106 is_emergency_tree.insertNode<model::AlwaysFailure>();
108 model::Fallback fallback = is_emergency_tree.insertNode<model::Fallback>();
109 for (
const auto & [monitor_id, _] : mission_config_.emergency) {
110 fallback.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
115 if (!mission_config_.bringup.empty()) {
116 RCLCPP_DEBUG(
logger_,
"Creating bringup subtree.");
117 TreeDocument::TreeElement bringup_tree = doc.getTree(
"__BringUp__");
118 buildBringUp(bringup_tree, mission_config_.bringup);
119 if (
const BT::Result res = bringup_tree.verify(); !res) {
120 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Bringup tree is not valid: " + res.error());
125 RCLCPP_DEBUG(
logger_,
"Creating mission subtree.");
126 TreeDocument::TreeElement mission_tree = doc.getTree(
"__RunMission__");
127 buildMission(mission_tree, mission_config_.mission);
128 if (
const BT::Result res = mission_tree.verify(); !res) {
129 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Mission tree is not valid: " + res.error());
133 if (!mission_config_.contingency.empty() || !mission_config_.emergency.empty()) {
134 RCLCPP_DEBUG(
logger_,
"Creating event monitor subtree.");
135 TreeDocument::TreeElement event_monitor_tree = doc.getTree(
"__MonitorEvents__");
136 buildEventMonitor(event_monitor_tree, mission_config_.contingency, mission_config_.emergency);
137 if (
const BT::Result res = event_monitor_tree.verify(); !res) {
138 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
139 "Event monitor tree is not valid: " + res.error());
144 if (!mission_config_.contingency.empty()) {
145 RCLCPP_DEBUG(
logger_,
"Creating contingency handler subtree.");
146 TreeDocument::TreeElement contingency_tree = doc.getTree(
"__HandleContingency__");
147 buildContingencyHandling(contingency_tree, mission_config_.contingency);
148 if (
const BT::Result res = contingency_tree.verify(); !res) {
149 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
150 "Contingency handling tree is not valid: " + res.error());
155 if (!mission_config_.emergency.empty()) {
156 RCLCPP_DEBUG(
logger_,
"Creating emergency handler subtree.");
157 TreeDocument::TreeElement emergency_tree = doc.getTree(
"__HandleEmergency__");
158 buildEmergencyHandling(emergency_tree, mission_config_.emergency);
159 if (
const BT::Result res = emergency_tree.verify(); !res) {
160 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
161 "Emergency handling tree is not valid: " + res.error());
166 if (!mission_config_.shutdown.empty()) {
167 RCLCPP_DEBUG(
logger_,
"Creating shutdown subtree.");
168 TreeDocument::TreeElement shutdown_tree = doc.getTree(
"__ShutDown__");
169 buildShutDown(shutdown_tree, mission_config_.shutdown);
170 if (
const BT::Result res = shutdown_tree.verify(); !res) {
171 throw auto_apms_behavior_tree::exceptions::TreeBuildHandlerError(
"Shutdown tree is not valid: " + res.error());
181void MissionBuildHandlerBase::buildBringUp(
182 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
184 sub_tree.removeChildren();
185 for (
const TreeResource::Identity & r : trees) {
186 RCLCPP_DEBUG(
logger_,
"Adding bringup tree '%s'.", r.str().c_str());
187 sub_tree.insertTreeFromResource(r);
191void MissionBuildHandlerBase::buildMission(
192 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
194 sub_tree.removeChildren();
195 for (
const TreeResource::Identity & r : trees) {
196 RCLCPP_DEBUG(
logger_,
"Adding mission tree '%s'.", r.str().c_str());
197 sub_tree.insertTreeFromResource(r);
201void MissionBuildHandlerBase::buildEventMonitor(
202 TreeDocument::TreeElement & sub_tree,
203 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & contingencies,
204 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & emergencies)
206 std::vector<TreeResource::Identity> sorted_monitor_ids;
209 for (
const auto & [monitor_id, _] : emergencies) {
210 sorted_monitor_ids.push_back(monitor_id);
212 for (
const auto & [monitor_id, _] : contingencies) {
217 TreeDocument monitor_doc;
218 for (
const TreeResource::Identity & monitor_id : sorted_monitor_ids) {
219 if (!monitor_doc.hasTreeName(monitor_id.str())) {
220 RCLCPP_DEBUG(
logger_,
"Adding event monitor tree '%s'.", monitor_id.str().c_str());
222 logger_,
"Identity has file stem '%s' and tree name '%s'.", monitor_id.file_stem.c_str(),
223 monitor_id.tree_name.c_str());
224 monitor_doc.newTreeFromResource(monitor_id).setName(monitor_id.str());
228 model::Fallback fallback = sub_tree.removeChildren().insertNode<model::Fallback>();
229 for (
const TreeResource::Identity & monitor_id : sorted_monitor_ids) {
231 const std::string monitor_tree_name = monitor_id.str();
232 model::SubTree subtree_node = fallback.getParentDocument().hasTreeName(monitor_tree_name)
233 ? fallback.insertNode<model::SubTree>(monitor_tree_name)
234 : fallback.insertNode<model::SubTree>(monitor_doc.getTree(monitor_tree_name));
235 subtree_node.
setConditionalScript(BT::PostCond::ON_SUCCESS,
"event_id := '" + monitor_tree_name +
"'");
239void MissionBuildHandlerBase::buildContingencyHandling(
240 TreeDocument::TreeElement & sub_tree,
241 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & contingencies)
244 TreeDocument handler_doc;
245 for (
const auto & [_, handler_id] : contingencies) {
246 if (!handler_doc.hasTreeName(handler_id.str())) {
247 RCLCPP_DEBUG(
logger_,
"Adding contingency handler tree '%s'.", handler_id.str().c_str());
248 handler_doc.newTreeFromResource(handler_id).setName(handler_id.str());
252 model::ReactiveFallback fallback = sub_tree.removeChildren().insertNode<model::ReactiveFallback>();
256 fallback.insertNode<model::AlwaysFailure>()
257 .setName(
"ResetEventHandler")
258 .setConditionalScript(BT::PreCond::SUCCESS_IF,
"event_id == ''");
260 for (
const auto & [monitor_id, handler_id] : contingencies) {
261 model::AsyncSequence seq =
262 fallback.insertNode<model::AsyncSequence>().setName(
"EventHandler (" + handler_id.str() +
")");
263 seq.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
266 const std::string handler_tree_name = handler_id.str();
267 if (seq.getParentDocument().hasTreeName(handler_tree_name)) {
268 seq.insertNode<model::SubTree>(handler_tree_name);
271 seq.insertNode<model::SubTree>(handler_doc.getTree(handler_tree_name));
275 seq.insertNode<model::KeepRunningUntilFailure>()
276 .insertNode<model::ScriptCondition>()
277 .setName(
"IsEventStillActive")
278 .set_code(
"event_id == '" + monitor_id.str() +
"'");
282void MissionBuildHandlerBase::buildEmergencyHandling(
283 TreeDocument::TreeElement & sub_tree,
284 const std::vector<std::pair<TreeResource::Identity, TreeResource::Identity>> & emergencies)
287 TreeDocument handler_doc;
288 for (
const auto & [_, handler_id] : emergencies) {
289 if (!handler_doc.hasTreeName(handler_id.str())) {
290 RCLCPP_DEBUG(
logger_,
"Adding emergency handler tree '%s'.", handler_id.str().c_str());
291 handler_doc.newTreeFromResource(handler_id).setName(handler_id.str());
295 model::ReactiveFallback fallback = sub_tree.removeChildren().insertNode<model::ReactiveFallback>();
296 for (
const auto & [monitor_id, handler_id] : emergencies) {
297 model::AsyncSequence seq =
298 fallback.insertNode<model::AsyncSequence>().setName(
"EventHandler (" + handler_id.str() +
")");
299 seq.insertNode<model::ScriptCondition>().set_code(
"event_id == '" + monitor_id.str() +
"'");
302 const std::string handler_tree_name = handler_id.str();
303 if (seq.getParentDocument().hasTreeName(handler_tree_name)) {
304 seq.insertNode<model::SubTree>(handler_tree_name);
307 seq.insertNode<model::SubTree>(handler_doc.getTree(handler_tree_name));
312void MissionBuildHandlerBase::buildShutDown(
313 TreeDocument::TreeElement & sub_tree,
const std::vector<TreeResource::Identity> & trees)
315 sub_tree.removeChildren();
316 for (
const TreeResource::Identity & r : trees) {
317 RCLCPP_DEBUG(
logger_,
"Adding shutdown tree '%s'.", r.str().c_str());
318 sub_tree.insertTreeFromResource(r);
322void 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...