15#include "auto_apms_behavior_tree/executor/executor_node.hpp"
21#include "auto_apms_behavior_tree/exceptions.hpp"
22#include "auto_apms_behavior_tree/util/parameter.hpp"
23#include "auto_apms_behavior_tree_core/definitions.hpp"
24#include "auto_apms_util/container.hpp"
25#include "auto_apms_util/string.hpp"
26#include "pluginlib/exceptions.hpp"
27#include "rclcpp/utilities.hpp"
32const std::vector<std::string> EXPLICITLY_ALLOWED_PARAMETERS{
33 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_ALLOW_OTHER_BUILD_HANDLERS,
34 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_ALLOW_DYNAMIC_BLACKBOARD,
35 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_ALLOW_DYNAMIC_SCRIPTING_ENUMS,
36 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_EXCLUDE_PACKAGES_NODE,
37 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_EXCLUDE_PACKAGES_BUILD_HANDLER,
38 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER,
39 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_TICK_RATE,
40 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_GROOT2_PORT,
41 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_STATE_CHANGE_LOGGER};
43const std::vector<std::string> EXPLICITLY_ALLOWED_PARAMETERS_WHILE_BUSY{
44 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_ALLOW_DYNAMIC_BLACKBOARD,
45 _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_STATE_CHANGE_LOGGER};
48: ros_node_options_(ros_node_options)
54 scripting_enum_parameters_from_overrides_ = from_overrides;
55 scripting_enum_parameters_dynamic_ = dynamic;
61 blackboard_parameters_from_overrides_ = from_overrides;
62 blackboard_parameters_dynamic_ = dynamic;
68 custom_default_parameters_[_AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER] = rclcpp::ParameterValue(name);
74 rclcpp::NodeOptions opt(ros_node_options_);
75 opt.automatically_declare_parameters_from_overrides(
76 scripting_enum_parameters_from_overrides_ || blackboard_parameters_from_overrides_);
77 opt.allow_undeclared_parameters(scripting_enum_parameters_dynamic_ || blackboard_parameters_dynamic_);
82:
TreeExecutorBase(std::make_shared<rclcpp::Node>(name, executor_options.getROSNodeOptions())),
83 executor_options_(executor_options),
91 std::vector<rclcpp::Parameter> new_default_parameters;
92 std::map<std::string, rclcpp::ParameterValue> effective_param_overrides =
93 node_ptr_->get_node_parameters_interface()->get_parameter_overrides();
94 for (
const auto & [name, value] : executor_options_.custom_default_parameters_) {
96 if (effective_param_overrides.find(name) == effective_param_overrides.end()) {
97 new_default_parameters.push_back(rclcpp::Parameter(name, value));
100 if (!new_default_parameters.empty()) node_ptr_->set_parameters_atomically(new_default_parameters);
102 const ExecutorParameters initial_params = executor_param_listener_.get_params();
105 rcl_interfaces::msg::ListParametersResult res = node_ptr_->list_parameters({}, 0);
106 std::vector<std::string> unkown_param_names;
107 for (
const std::string & param_name : res.names) {
108 if (!stripPrefixFromParameterName(SCRIPTING_ENUM_PARAM_PREFIX, param_name).empty()) continue;
109 if (!stripPrefixFromParameterName(BLACKBOARD_PARAM_PREFIX, param_name).empty()) continue;
110 if (auto_apms_util::contains(EXPLICITLY_ALLOWED_PARAMETERS, param_name)) continue;
112 node_ptr_->undeclare_parameter(param_name);
113 } catch (
const rclcpp::exceptions::ParameterImmutableException & e) {
116 }
catch (
const rclcpp::exceptions::InvalidParameterTypeException & e) {
120 unkown_param_names.push_back(param_name);
122 if (!unkown_param_names.empty()) {
124 logger_,
"The following initial parameters are not supported and have been removed: [ %s ].",
125 auto_apms_util::join(unkown_param_names,
", ").c_str());
129 tree_node_loader_ptr_ = core::NodeRegistrationLoader::make_shared(
130 std::set<std::string>(initial_params.node_exclude_packages.begin(), initial_params.node_exclude_packages.end()));
133 build_handler_loader_ptr_ = TreeBuildHandlerLoader::make_unique(std::set<std::string>(
134 initial_params.build_handler_exclude_packages.begin(), initial_params.build_handler_exclude_packages.end()));
138 initial_params.build_handler != PARAM_VALUE_NO_BUILD_HANDLER &&
139 !build_handler_loader_ptr_->isClassAvailable(initial_params.build_handler)) {
140 throw exceptions::TreeExecutorError(
141 "Cannot load build handler '" + initial_params.build_handler +
142 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
144 "and registered it by calling auto_apms_behavior_tree_declare_build_handlers() in the CMakeLists.txt of the "
145 "corresponding package.");
147 loadBuildHandler(initial_params.build_handler);
150 const auto initial_scripting_enums = getParameterValuesWithPrefix(SCRIPTING_ENUM_PARAM_PREFIX);
151 if (!initial_scripting_enums.empty()) {
152 if (executor_options_.scripting_enum_parameters_from_overrides_) {
153 updateScriptingEnumsWithParameterValues(initial_scripting_enums);
157 "Initial scripting enums have been provided, but the 'Scripting enums from overrides' option is disabled. "
161 const auto initial_blackboard = getParameterValuesWithPrefix(BLACKBOARD_PARAM_PREFIX);
162 if (!initial_blackboard.empty()) {
163 if (executor_options_.blackboard_parameters_from_overrides_) {
164 updateGlobalBlackboardWithParameterValues(initial_blackboard);
168 "Initial blackboard entries have been provided, but the 'Blackboard from overrides' option is disabled. "
173 using namespace std::placeholders;
174 start_action_ptr_ = rclcpp_action::create_server<StartActionContext::Type>(
175 node_ptr_, std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_START_ACTION_NAME_SUFFIX,
176 std::bind(&TreeExecutorNode::handle_start_goal_,
this, _1, _2),
177 std::bind(&TreeExecutorNode::handle_start_cancel_,
this, _1),
178 std::bind(&TreeExecutorNode::handle_start_accept_,
this, _1));
180 command_action_ptr_ = rclcpp_action::create_server<CommandActionContext::Type>(
181 node_ptr_, std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_COMMAND_ACTION_NAME_SUFFIX,
182 std::bind(&TreeExecutorNode::handle_command_goal_,
this, _1, _2),
183 std::bind(&TreeExecutorNode::handle_command_cancel_,
this, _1),
184 std::bind(&TreeExecutorNode::handle_command_accept_,
this, _1));
192 on_set_parameters_callback_handle_ptr_ =
193 node_ptr_->add_on_set_parameters_callback([
this](
const std::vector<rclcpp::Parameter> & parameters) {
194 return this->on_set_parameters_callback_(parameters);
198 parameter_event_handler_ptr_ = std::make_shared<rclcpp::ParameterEventHandler>(node_ptr_);
199 parameter_event_callback_handle_ptr_ = parameter_event_handler_ptr_->add_parameter_event_callback(
200 [
this](
const rcl_interfaces::msg::ParameterEvent & event) { this->parameter_event_callback_(event); });
203 std::vector<std::string> args_with_ros_arguments = node_ptr_->get_node_options().arguments();
204 int argc = args_with_ros_arguments.size();
205 char ** argv =
new char *[argc + 1];
206 for (
int i = 0; i < argc; ++i) {
207 argv[i] =
const_cast<char *
>(args_with_ros_arguments[i].c_str());
209 argv[argc] =
nullptr;
213 if (
const std::vector<std::string> args = rclcpp::remove_ros_arguments(argc, argv); args.size() > 1) {
215 std::vector<std::string> relevant_args{args.begin() + 1, args.end()};
217 logger_,
"Additional cli arguments in rclcpp::NodeOptions: [ %s ]",
218 auto_apms_util::join(relevant_args,
", ").c_str());
221 startExecution(makeTreeConstructor(relevant_args[0],
""), initial_params.tick_rate, initial_params.groot2_port);
233 const std::string & tree_build_request)
235 const ExecutorParameters params = executor_param_listener_.get_params();
241 const auto res =
node_ptr_->list_parameters({prefix}, 2);
242 std::map<std::string, rclcpp::ParameterValue> value_map;
243 for (
const std::string & name_with_prefix : res.names) {
245 value_map[suffix] =
node_ptr_->get_parameter(name_with_prefix).get_parameter_value();
253 const std::regex reg(
"^" + prefix +
"\\.(\\S+)");
254 if (std::smatch match; std::regex_match(param_name, match, reg))
return match[1].str();
259 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
261 std::map<std::string, std::string> set_successfully_map;
262 for (
const auto & [enum_key, pval] : value_map) {
264 switch (pval.get_type()) {
265 case rclcpp::ParameterType::PARAMETER_BOOL:
266 if (simulate)
continue;
267 scripting_enums_[enum_key] =
static_cast<int>(pval.get<
bool>());
269 case rclcpp::ParameterType::PARAMETER_INTEGER:
270 if (simulate)
continue;
271 scripting_enums_[enum_key] =
static_cast<int>(pval.get<int64_t>());
274 if (simulate)
return false;
275 throw exceptions::ParameterConversionError(
"Parameter to scripting enum conversion is not allowed.");
277 set_successfully_map[enum_key] = rclcpp::to_string(pval);
278 }
catch (
const std::exception & e) {
280 logger_,
"Error setting scripting enum from parameter %s=%s (Type: %s): %s", enum_key.c_str(),
281 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
285 if (!set_successfully_map.empty()) {
287 logger_,
"Updated scripting enums from parameters: { %s }",
294 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
297 std::map<std::string, std::string> set_successfully_map;
298 for (
const auto & [entry_key, pval] : value_map) {
301 BT::Any any(expected.value());
304 if (
const BT::TypeInfo * entry_info = bb.entryInfo(entry_key)) {
305 if (entry_info->isStronglyTyped() && entry_info->type() != any.type())
return false;
309 bb.set(entry_key, any);
312 throw exceptions::ParameterConversionError(expected.error());
314 translated_global_blackboard_entries_[entry_key] = pval;
315 set_successfully_map[entry_key] = rclcpp::to_string(pval);
316 }
catch (
const std::exception & e) {
318 logger_,
"Error updating blackboard from parameter %s=%s (Type: %s): %s", entry_key.c_str(),
319 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
323 if (!set_successfully_map.empty()) {
332 if (build_handler_ptr_ && !executor_param_listener_.get_params().allow_other_build_handlers) {
333 throw std::logic_error(
334 "Executor option 'Allow other build handlers' is disabled, but loadBuildHandler() was called again after "
336 current_build_handler_name_ +
"'.");
338 if (current_build_handler_name_ == name)
return;
339 if (name == PARAM_VALUE_NO_BUILD_HANDLER) {
340 build_handler_ptr_.reset();
344 build_handler_loader_ptr_->createUniqueInstance(name)->makeUnique(
node_ptr_, tree_node_loader_ptr_);
345 }
catch (
const pluginlib::CreateClassException & e) {
346 throw exceptions::TreeExecutorError(
347 "An error occurred when trying to create an instance of tree build handler class '" + name +
348 "'. This might be because you forgot to call the AUTO_APMS_BEHAVIOR_TREE_DECLARE_BUILD_HANDLER macro "
349 "in the source file: " +
351 }
catch (
const std::exception & e) {
352 throw exceptions::TreeExecutorError(
353 "An error occurred when trying to create an instance of tree build handler class '" + name +
"': " + e.what());
356 current_build_handler_name_ = name;
360 const std::string & build_handler_request,
const std::string & root_tree_name,
365 build_handler_ptr_ && !build_handler_ptr_->setBuildRequest(build_handler_request, node_manifest, root_tree_name)) {
366 throw exceptions::TreeBuildError(
367 "Build request '" + build_handler_request +
"' was denied by '" +
368 executor_param_listener_.get_params().build_handler +
"' (setBuildRequest() returned false).");
374 return [
this, root_tree_name, node_manifest, node_overrides](TreeBlackboardSharedPtr bb_ptr) {
378 builder_ptr_.reset(
new TreeBuilder(
385 for (
const auto & [enum_key, val] : scripting_enums_) builder_ptr_->setScriptingEnum(enum_key, val);
388 std::string instantiate_name = root_tree_name;
389 if (build_handler_ptr_) {
390 instantiate_name = build_handler_ptr_->buildTree(*builder_ptr_, *bb_ptr).getName();
394 builder_ptr_->registerNodes(node_overrides,
true);
397 return builder_ptr_->instantiate(instantiate_name, bb_ptr);
401rcl_interfaces::msg::SetParametersResult TreeExecutorNode::on_set_parameters_callback_(
402 const std::vector<rclcpp::Parameter> & parameters)
404 const ExecutorParameters params = executor_param_listener_.get_params();
407 for (
const rclcpp::Parameter & p : parameters) {
408 auto create_rejected = [&p](
const std::string msg) {
409 rcl_interfaces::msg::SetParametersResult result;
410 result.successful =
false;
411 result.reason =
"Rejected to set " + p.get_name() +
" = " + p.value_to_string() +
" (Type: " + p.get_type_name() +
415 const std::string param_name = p.get_name();
421 return create_rejected(
"Scripting enums cannot change while tree executor is running");
423 if (!executor_options_.scripting_enum_parameters_dynamic_ || !params.allow_dynamic_scripting_enums) {
424 return create_rejected(
425 "Cannot set scripting enum '" + enum_key +
"', because the 'Dynamic scripting enums' option is disabled");
429 return create_rejected(
430 "Type of scripting enum must be bool or int. Tried to set enum '" + enum_key +
"' with value '" +
431 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
439 !entry_key.empty()) {
440 if (!executor_options_.blackboard_parameters_dynamic_ || !params.allow_dynamic_blackboard) {
441 return create_rejected(
442 "Cannot set blackboard entry '" + entry_key +
"', because the 'Dynamic blackboard' option is disabled");
446 return create_rejected(
447 "Type of blackboard entries must not change. Tried to set entry '" + entry_key +
449 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
457 return create_rejected(
"Parameter is unkown");
462 return create_rejected(
"Parameter is not allowed to change while tree executor is running");
466 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
467 if (!params.allow_other_build_handlers) {
468 return create_rejected(
469 "This executor operates with tree build handler '" + executor_param_listener_.get_params().build_handler +
470 "' and doesn't allow other build handlers to be loaded since the 'Allow other build handlers' option is "
473 const std::string class_name = p.as_string();
474 if (class_name != PARAM_VALUE_NO_BUILD_HANDLER && !build_handler_loader_ptr_->isClassAvailable(class_name)) {
475 return create_rejected(
476 "Cannot load build handler '" + class_name +
477 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
479 "and registered it by calling auto_apms_behavior_tree_declare_build_handlers() in the CMakeLists.txt of the "
480 "corresponding package");
485 if (!
node_ptr_->has_parameter(param_name)) {
486 return create_rejected(
"Parameter '" + param_name +
"' is not supported");
491 rcl_interfaces::msg::SetParametersResult result;
492 result.successful =
true;
496void TreeExecutorNode::parameter_event_callback_(
const rcl_interfaces::msg::ParameterEvent & event)
499 std::regex re(
node_ptr_->get_fully_qualified_name());
500 if (std::regex_match(event.node, re)) {
502 for (
const rclcpp::Parameter & p : rclcpp::ParameterEventHandler::get_parameters_from_event(event)) {
503 const std::string param_name = p.get_name();
513 !entry_key.empty()) {
518 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
525rclcpp_action::GoalResponse TreeExecutorNode::handle_start_goal_(
526 const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const StartActionContext::Goal> goal_ptr)
531 logger_,
"Goal %s was REJECTED: Tree '%s' is currently executing.", rclcpp_action::to_string(uuid).c_str(),
533 return rclcpp_action::GoalResponse::REJECT;
536 if (!goal_ptr->build_handler.empty()) {
537 if (executor_param_listener_.get_params().allow_other_build_handlers) {
540 }
catch (
const std::exception & e) {
542 logger_,
"Goal %s was REJECTED: Loading tree build handler '%s' failed: %s",
543 rclcpp_action::to_string(uuid).c_str(), goal_ptr->build_handler.c_str(), e.what());
544 return rclcpp_action::GoalResponse::REJECT;
546 }
else if (goal_ptr->build_handler != current_build_handler_name_) {
549 "Goal %s was REJECTED: Current tree build handler '%s' must not change since the 'Allow other build handlers' "
550 "option is disabled.",
551 rclcpp_action::to_string(uuid).c_str(), current_build_handler_name_.c_str());
552 return rclcpp_action::GoalResponse::REJECT;
556 core::NodeManifest node_manifest;
558 node_manifest = core::NodeManifest::decode(goal_ptr->node_manifest);
559 }
catch (
const std::exception & e) {
561 logger_,
"Goal %s was REJECTED: Parsing the initial node manifest failed: %s",
562 rclcpp_action::to_string(uuid).c_str(), e.what());
563 return rclcpp_action::GoalResponse::REJECT;
566 core::NodeManifest node_overrides;
568 node_overrides = core::NodeManifest::decode(goal_ptr->node_overrides);
569 }
catch (
const std::exception & e) {
571 logger_,
"Goal %s was REJECTED: Parsing the node override manifest failed: %s",
572 rclcpp_action::to_string(uuid).c_str(), e.what());
573 return rclcpp_action::GoalResponse::REJECT;
578 makeTreeConstructor(goal_ptr->build_request, goal_ptr->root_tree, node_manifest, node_overrides);
579 }
catch (
const std::exception & e) {
580 RCLCPP_WARN(
logger_,
"Goal %s was REJECTED: %s", rclcpp_action::to_string(uuid).c_str(), e.what());
581 return rclcpp_action::GoalResponse::REJECT;
583 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
586rclcpp_action::CancelResponse TreeExecutorNode::handle_start_cancel_(
587 std::shared_ptr<StartActionContext::GoalHandle> )
590 return rclcpp_action::CancelResponse::ACCEPT;
593void TreeExecutorNode::handle_start_accept_(std::shared_ptr<StartActionContext::GoalHandle> goal_handle_ptr)
596 if (goal_handle_ptr->get_goal()->clear_blackboard) {
597 const auto res =
node_ptr_->list_parameters({BLACKBOARD_PARAM_PREFIX}, 2);
598 for (
const std::string & name : res.names) {
604 const ExecutorParameters params = executor_param_listener_.get_params();
606 startExecution(tree_constructor_, params.tick_rate, params.groot2_port);
607 }
catch (
const std::exception & e) {
608 auto result_ptr = std::make_shared<StartActionContext::Result>();
609 result_ptr->message =
"An error occurred trying to start execution: " + std::string(e.what());
610 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
611 goal_handle_ptr->abort(result_ptr);
612 RCLCPP_ERROR_STREAM(
logger_, result_ptr->message);
615 const std::string started_tree_name =
getTreeName();
619 if (goal_handle_ptr->get_goal()->attach) {
620 start_action_context_.setUp(goal_handle_ptr);
621 RCLCPP_INFO(
logger_,
"Successfully started execution of tree '%s' (Mode: Attached).", started_tree_name.c_str());
623 auto result_ptr = std::make_shared<StartActionContext::Result>();
624 result_ptr->message =
"Successfully started execution of tree '" + started_tree_name +
"' (Mode: Detached).";
625 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
626 result_ptr->terminated_tree_identity = started_tree_name;
627 goal_handle_ptr->succeed(result_ptr);
628 RCLCPP_INFO_STREAM(
logger_, result_ptr->message);
632rclcpp_action::GoalResponse TreeExecutorNode::handle_command_goal_(
633 const rclcpp_action::GoalUUID & , std::shared_ptr<const CommandActionContext::Goal> goal_ptr)
635 if (command_timer_ptr_ && !command_timer_ptr_->is_canceled()) {
636 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because previous one is still busy.");
637 return rclcpp_action::GoalResponse::REJECT;
640 if (
isBusy() && start_action_context_.isValid() && start_action_context_.getGoalHandlePtr()->is_canceling()) {
641 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because tree executor is canceling.");
642 return rclcpp_action::GoalResponse::REJECT;
646 switch (goal_ptr->command) {
647 case CommandActionContext::Goal::COMMAND_RESUME:
652 logger_,
"Requested to RESUME with executor being in state %s. Rejecting request.",
653 toStr(execution_state).c_str());
654 return rclcpp_action::GoalResponse::REJECT;
657 case CommandActionContext::Goal::COMMAND_PAUSE:
662 logger_,
"Requested to PAUSE with executor already being inactive (State: %s).",
663 toStr(execution_state).c_str());
666 case CommandActionContext::Goal::COMMAND_HALT:
673 logger_,
"Requested to HALT with executor already being inactive (State: %s).",
674 toStr(execution_state).c_str());
677 case CommandActionContext::Goal::COMMAND_TERMINATE:
682 logger_,
"Requested to TERMINATE with executor already being inactive (State: %s).",
683 toStr(execution_state).c_str());
687 RCLCPP_WARN(
logger_,
"Executor command %i is undefined. Rejecting request.", goal_ptr->command);
688 return rclcpp_action::GoalResponse::REJECT;
690 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
693rclcpp_action::CancelResponse TreeExecutorNode::handle_command_cancel_(
694 std::shared_ptr<CommandActionContext::GoalHandle> )
696 return rclcpp_action::CancelResponse::ACCEPT;
699void TreeExecutorNode::handle_command_accept_(std::shared_ptr<CommandActionContext::GoalHandle> goal_handle_ptr)
701 const auto command_request = goal_handle_ptr->get_goal()->command;
703 switch (command_request) {
704 case CommandActionContext::Goal::COMMAND_RESUME:
708 case CommandActionContext::Goal::COMMAND_PAUSE:
712 case CommandActionContext::Goal::COMMAND_HALT:
716 case CommandActionContext::Goal::COMMAND_TERMINATE:
721 throw std::logic_error(
"command_request is unkown");
724 command_timer_ptr_ =
node_ptr_->create_wall_timer(
725 std::chrono::duration<double>(executor_param_listener_.get_params().tick_rate),
726 [
this, requested_state, goal_handle_ptr, action_result_ptr = std::make_shared<CommandActionContext::Result>()]() {
728 if (goal_handle_ptr->is_canceling()) {
730 goal_handle_ptr->canceled(action_result_ptr);
731 command_timer_ptr_->cancel();
740 logger_,
"Failed to reach requested state %s due to cancellation of execution timer. Aborting.",
741 toStr(requested_state).c_str());
742 goal_handle_ptr->abort(action_result_ptr);
743 command_timer_ptr_->cancel();
748 if (current_state != requested_state)
return;
750 goal_handle_ptr->succeed(action_result_ptr);
751 command_timer_ptr_->cancel();
757 const ExecutorParameters params = executor_param_listener_.get_params();
766 const ExecutorParameters params = executor_param_listener_.get_params();
772 if (executor_options_.blackboard_parameters_dynamic_ && params.allow_dynamic_blackboard) {
774 std::vector<rclcpp::Parameter> new_parameters;
775 for (
const BT::StringView & str : bb_ptr->getKeys()) {
776 const std::string key = std::string(str);
777 const BT::TypeInfo * type_info = bb_ptr->entryInfo(key);
778 const BT::Any * any = bb_ptr->getAnyLocked(key).get();
785 if (any->empty())
continue;
789 if (translated_global_blackboard_entries_.find(key) == translated_global_blackboard_entries_.end()) {
791 const BT::Expected<rclcpp::ParameterValue> expected =
794 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
795 translated_global_blackboard_entries_[key] = expected.value();
798 logger_,
"Failed to translate new blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
799 type_info->typeName().c_str(), expected.error().c_str());
803 const BT::Expected<rclcpp::ParameterValue> expected =
806 if (expected.value() != translated_global_blackboard_entries_[key]) {
807 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
811 logger_,
"Failed to translate blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
812 type_info->typeName().c_str(), expected.error().c_str());
816 const rcl_interfaces::msg::SetParametersResult result =
node_ptr_->set_parameters_atomically(new_parameters);
817 if (!result.successful) {
818 throw exceptions::TreeExecutorError(
819 "Unexpectedly failed to set parameters inferred from global blackboard. Reason: " + result.reason);
828 if (start_action_context_.isValid()) {
830 auto feedback_ptr = start_action_context_.getFeedbackPtr();
832 feedback_ptr->running_tree_identity =
getTreeName();
834 if (!running_action_history.empty()) {
836 feedback_ptr->running_action_name = auto_apms_util::join(running_action_history,
" + ");
837 feedback_ptr->running_action_timestamp =
838 std::chrono::duration<double>{std::chrono::high_resolution_clock::now().time_since_epoch()}.count();
841 state_observer.
flush();
843 start_action_context_.publishFeedback();
851 if (!start_action_context_.isValid())
854 auto result_ptr = start_action_context_.getResultPtr();
855 result_ptr->terminated_tree_identity =
getTreeName();
858 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_SUCCESS;
859 result_ptr->message =
"Tree execution finished with status SUCCESS";
860 start_action_context_.succeed();
863 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_FAILURE;
864 result_ptr->message =
"Tree execution finished with status FAILURE";
865 start_action_context_.abort();
868 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
869 if (start_action_context_.getGoalHandlePtr()->is_canceling()) {
870 result_ptr->message =
"Tree execution canceled successfully";
871 start_action_context_.cancel();
873 result_ptr->message =
"Tree execution terminated prematurely";
874 start_action_context_.abort();
878 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
879 result_ptr->message =
"An unexpected error occurred during tree execution";
880 start_action_context_.abort();
883 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
884 result_ptr->message =
"Execution result unkown";
885 start_action_context_.abort();
890 start_action_context_.invalidate();
ExecutionState
Enum representing possible behavior tree execution states.
@ STARTING
Initializing execution.
@ RUNNING
Executor is busy and tree has been ticked at least once.
@ PAUSED
Execution routine is active, but tree is not being ticked.
@ HALTED
Execution routine is active, but tree is not being ticked and has been halted before.
ExecutionState getExecutionState()
Get a status code indicating the current state of execution.
bool isBusy()
Determine whether this executor is currently executing a behavior tree.
rclcpp::Node::SharedPtr node_ptr_
Shared pointer to the parent ROS 2 node.
rclcpp::executors::SingleThreadedExecutor::SharedPtr getTreeNodeWaitablesExecutorPtr()
Get the ROS 2 executor instance used for spinning waitables registered by behavior tree nodes.
rclcpp::CallbackGroup::SharedPtr getTreeNodeWaitablesCallbackGroupPtr()
Get the callback group used for all waitables registered by behavior tree nodes.
void setControlCommand(ControlCommand cmd)
Set the command that handles the control flow of the execution routine.
void clearGlobalBlackboard()
Reset the global blackboard and clear all entries.
@ TERMINATE
Halt the currently executing tree and terminate the execution routine.
@ PAUSE
Pause the execution routine.
@ RUN
Start/Resume the execution routine.
@ HALT
Halt the currently executing tree and pause the execution routine.
TreeStateObserver & getStateObserver()
Get a reference to the current behavior tree state observer.
TreeExecutorBase(rclcpp::Node::SharedPtr node_ptr, rclcpp::CallbackGroup::SharedPtr tree_node_callback_group_ptr=nullptr)
Constructor.
TreeBlackboardSharedPtr getGlobalBlackboardPtr()
Get a shared pointer to the global blackboard instance.
std::string getTreeName()
Get the name of the tree that is currently executing.
ExecutionResult
Enum representing possible behavior tree execution results.
@ TREE_SUCCEEDED
Tree completed with BT::NodeStatus::SUCCESS.
@ TERMINATED_PREMATURELY
Execution terminated before the tree was able to propagate the tick to all its nodes.
@ TREE_FAILED
Tree completed with BT::NodeStatus::FAILURE.
@ ERROR
An unexpected error occurred.
const rclcpp::Logger logger_
Logger associated with the parent ROS 2 node.
Configuration options for TreeExecutorNode.
TreeExecutorNodeOptions & enableScriptingEnumParameters(bool from_overrides, bool dynamic)
Configure whether the executor node accepts scripting enum parameters.
rclcpp::NodeOptions getROSNodeOptions() const
Get the ROS 2 node options that comply with the given options.
TreeExecutorNodeOptions(const rclcpp::NodeOptions &ros_node_options)
Constructor.
TreeExecutorNodeOptions & enableGlobalBlackboardParameters(bool from_overrides, bool dynamic)
Configure whether the executor node accepts global blackboard parameters.
TreeExecutorNodeOptions & setDefaultBuildHandler(const std::string &name)
Specify a default behavior tree build handler that will be used initially.
bool updateGlobalBlackboardWithParameterValues(const std::map< std::string, rclcpp::ParameterValue > &value_map, bool simulate=false)
Update the global blackboard using parameter values.
void onTermination(const ExecutionResult &result) override final
Callback invoked when the execution routine terminates.
static std::string stripPrefixFromParameterName(const std::string &prefix, const std::string ¶m_name)
Get the name of a parameter without its prefix.
bool afterTick() override final
Callback invoked every time after the behavior tree is ticked.
virtual void setUpBuilder(TreeBuilder &builder, const core::NodeManifest &node_manifest)
Callback invoked every time before any behavior trees are built.
std::map< std::string, rclcpp::ParameterValue > getParameterValuesWithPrefix(const std::string &prefix)
Assemble all parameters of this node that have a specific prefix.
std::shared_future< ExecutionResult > startExecution(const std::string &tree_build_request)
Start the behavior tree that is specified by a particular build request.
bool onTick() override final
Callback invoked every time before the behavior tree is ticked.
bool updateScriptingEnumsWithParameterValues(const std::map< std::string, rclcpp::ParameterValue > &value_map, bool simulate=false)
Update the internal buffer of scripting enums used when a behavior tree is created.
TreeConstructor makeTreeConstructor(const std::string &build_handler_request, const std::string &root_tree_name, const core::NodeManifest &node_manifest={}, const core::NodeManifest &node_overrides={})
Create a callback that builds a behavior tree according to a specific request.
void loadBuildHandler(const std::string &name)
Load a particular behavior tree build handler plugin.
TreeExecutorNode(const std::string &name, TreeExecutorNodeOptions executor_options)
Constructor allowing to specify a custom node name and executor options.
State observer for a particular behavior tree object that writes introspection and debugging informat...
virtual void flush() override
Reset the internal state variables.
const std::vector< std::string > & getRunningActionHistory() const
Get all names of action nodes that returned BT::NodeStatus::RUNNING since the last time TreeStateObse...
void setLogging(bool active)
Configure whether the observer should write to the logger.
Data structure for information about which behavior tree node plugin to load and how to configure the...
Class for configuring and instantiating behavior trees.
bool contains(const ContainerT< ValueT, AllocatorT > &c, const ValueT &val)
Check whether a particular container structure contains a value.
std::string printMap(const std::map< std::string, std::string > &map, const std::string &key_val_sep="=", const std::string &entry_sep=", ")
Converts a map to a string representation that is suited for printing to console.
const char * toStr(const ActionNodeErrorCode &err)
Convert the action error code to string.
Useful tooling for incorporating behavior trees for task development.
BT::Expected< BT::Any > createAnyFromParameterValue(const rclcpp::ParameterValue &val)
Convert a ROS 2 parameter value to a BT::Any object.
BT::Expected< rclcpp::ParameterValue > createParameterValueFromAny(const BT::Any &any, rclcpp::ParameterType type)
Convert a BT::Any object to a ROS 2 parameter value.