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(
134 std::set<std::string>(
135 initial_params.build_handler_exclude_packages.begin(), initial_params.build_handler_exclude_packages.end()));
139 initial_params.build_handler != PARAM_VALUE_NO_BUILD_HANDLER &&
140 !build_handler_loader_ptr_->isClassAvailable(initial_params.build_handler)) {
141 throw exceptions::TreeExecutorError(
142 "Cannot load build handler '" + initial_params.build_handler +
143 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
145 "and registered it by calling auto_apms_behavior_tree_declare_build_handlers() in the CMakeLists.txt of the "
146 "corresponding package.");
148 loadBuildHandler(initial_params.build_handler);
151 const auto initial_scripting_enums = getParameterValuesWithPrefix(SCRIPTING_ENUM_PARAM_PREFIX);
152 if (!initial_scripting_enums.empty()) {
153 if (executor_options_.scripting_enum_parameters_from_overrides_) {
154 updateScriptingEnumsWithParameterValues(initial_scripting_enums);
158 "Initial scripting enums have been provided, but the 'Scripting enums from overrides' option is disabled. "
162 const auto initial_blackboard = getParameterValuesWithPrefix(BLACKBOARD_PARAM_PREFIX);
163 if (!initial_blackboard.empty()) {
164 if (executor_options_.blackboard_parameters_from_overrides_) {
165 updateGlobalBlackboardWithParameterValues(initial_blackboard);
169 "Initial blackboard entries have been provided, but the 'Blackboard from overrides' option is disabled. "
174 using namespace std::placeholders;
175 start_action_ptr_ = rclcpp_action::create_server<StartActionContext::Type>(
176 node_ptr_, std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_START_ACTION_NAME_SUFFIX,
177 std::bind(&TreeExecutorNode::handle_start_goal_,
this, _1, _2),
178 std::bind(&TreeExecutorNode::handle_start_cancel_,
this, _1),
179 std::bind(&TreeExecutorNode::handle_start_accept_,
this, _1));
181 command_action_ptr_ = rclcpp_action::create_server<CommandActionContext::Type>(
182 node_ptr_, std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_COMMAND_ACTION_NAME_SUFFIX,
183 std::bind(&TreeExecutorNode::handle_command_goal_,
this, _1, _2),
184 std::bind(&TreeExecutorNode::handle_command_cancel_,
this, _1),
185 std::bind(&TreeExecutorNode::handle_command_accept_,
this, _1));
193 on_set_parameters_callback_handle_ptr_ =
194 node_ptr_->add_on_set_parameters_callback([
this](
const std::vector<rclcpp::Parameter> & parameters) {
195 return this->on_set_parameters_callback_(parameters);
199 parameter_event_handler_ptr_ = std::make_shared<rclcpp::ParameterEventHandler>(node_ptr_);
200 parameter_event_callback_handle_ptr_ = parameter_event_handler_ptr_->add_parameter_event_callback(
201 [
this](
const rcl_interfaces::msg::ParameterEvent & event) { this->parameter_event_callback_(event); });
204 std::vector<std::string> args_with_ros_arguments = node_ptr_->get_node_options().arguments();
205 int argc = args_with_ros_arguments.size();
206 char ** argv =
new char *[argc + 1];
207 for (
int i = 0; i < argc; ++i) {
208 argv[i] =
const_cast<char *
>(args_with_ros_arguments[i].c_str());
210 argv[argc] =
nullptr;
214 if (
const std::vector<std::string> args = rclcpp::remove_ros_arguments(argc, argv); args.size() > 1) {
216 std::vector<std::string> relevant_args{args.begin() + 1, args.end()};
218 logger_,
"Additional cli arguments in rclcpp::NodeOptions: [ %s ]",
219 auto_apms_util::join(relevant_args,
", ").c_str());
222 startExecution(makeTreeConstructor(relevant_args[0],
""), initial_params.tick_rate, initial_params.groot2_port);
234 const std::string & tree_build_request)
236 const ExecutorParameters params = executor_param_listener_.get_params();
242 const auto res =
node_ptr_->list_parameters({prefix}, 2);
243 std::map<std::string, rclcpp::ParameterValue> value_map;
244 for (
const std::string & name_with_prefix : res.names) {
246 value_map[suffix] =
node_ptr_->get_parameter(name_with_prefix).get_parameter_value();
254 const std::regex reg(
"^" + prefix +
"\\.(\\S+)");
255 if (std::smatch match; std::regex_match(param_name, match, reg))
return match[1].str();
260 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
262 std::map<std::string, std::string> set_successfully_map;
263 for (
const auto & [enum_key, pval] : value_map) {
265 switch (pval.get_type()) {
266 case rclcpp::ParameterType::PARAMETER_BOOL:
267 if (simulate)
continue;
268 scripting_enums_[enum_key] =
static_cast<int>(pval.get<
bool>());
270 case rclcpp::ParameterType::PARAMETER_INTEGER:
271 if (simulate)
continue;
272 scripting_enums_[enum_key] =
static_cast<int>(pval.get<int64_t>());
275 if (simulate)
return false;
276 throw exceptions::ParameterConversionError(
"Parameter to scripting enum conversion is not allowed.");
278 set_successfully_map[enum_key] = rclcpp::to_string(pval);
279 }
catch (
const std::exception & e) {
281 logger_,
"Error setting scripting enum from parameter %s=%s (Type: %s): %s", enum_key.c_str(),
282 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
286 if (!set_successfully_map.empty()) {
288 logger_,
"Updated scripting enums from parameters: { %s }",
295 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
298 std::map<std::string, std::string> set_successfully_map;
299 for (
const auto & [entry_key, pval] : value_map) {
302 BT::Any any(expected.value());
305 if (
const BT::TypeInfo * entry_info = bb.entryInfo(entry_key)) {
306 if (entry_info->isStronglyTyped() && entry_info->type() != any.type())
return false;
310 bb.set(entry_key, any);
313 throw exceptions::ParameterConversionError(expected.error());
315 translated_global_blackboard_entries_[entry_key] = pval;
316 set_successfully_map[entry_key] = rclcpp::to_string(pval);
317 }
catch (
const std::exception & e) {
319 logger_,
"Error updating blackboard from parameter %s=%s (Type: %s): %s", entry_key.c_str(),
320 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
324 if (!set_successfully_map.empty()) {
333 if (build_handler_ptr_ && !executor_param_listener_.get_params().allow_other_build_handlers) {
334 throw std::logic_error(
335 "Executor option 'Allow other build handlers' is disabled, but loadBuildHandler() was called again after "
337 current_build_handler_name_ +
"'.");
339 if (current_build_handler_name_ == name)
return;
340 if (name == PARAM_VALUE_NO_BUILD_HANDLER) {
341 build_handler_ptr_.reset();
345 build_handler_loader_ptr_->createUniqueInstance(name)->makeUnique(
node_ptr_, tree_node_loader_ptr_);
346 }
catch (
const pluginlib::CreateClassException & e) {
347 throw exceptions::TreeExecutorError(
348 "An error occurred when trying to create an instance of tree build handler class '" + name +
349 "'. This might be because you forgot to call the AUTO_APMS_BEHAVIOR_TREE_DECLARE_BUILD_HANDLER macro "
350 "in the source file: " +
352 }
catch (
const std::exception & e) {
353 throw exceptions::TreeExecutorError(
354 "An error occurred when trying to create an instance of tree build handler class '" + name +
"': " + e.what());
357 current_build_handler_name_ = name;
361 const std::string & build_handler_request,
const std::string & root_tree_name,
366 build_handler_ptr_ && !build_handler_ptr_->setBuildRequest(build_handler_request, node_manifest, root_tree_name)) {
367 throw exceptions::TreeBuildError(
368 "Build request '" + build_handler_request +
"' was denied by '" +
369 executor_param_listener_.get_params().build_handler +
"' (setBuildRequest() returned false).");
375 return [
this, root_tree_name, node_manifest, node_overrides](TreeBlackboardSharedPtr bb_ptr) {
379 builder_ptr_.reset(
new TreeBuilder(
386 for (
const auto & [enum_key, val] : scripting_enums_) builder_ptr_->setScriptingEnum(enum_key, val);
389 std::string instantiate_name = root_tree_name;
390 if (build_handler_ptr_) {
391 instantiate_name = build_handler_ptr_->buildTree(*builder_ptr_, *bb_ptr).getName();
395 builder_ptr_->registerNodes(node_overrides,
true);
398 return builder_ptr_->instantiate(instantiate_name, bb_ptr);
402rcl_interfaces::msg::SetParametersResult TreeExecutorNode::on_set_parameters_callback_(
403 const std::vector<rclcpp::Parameter> & parameters)
405 const ExecutorParameters params = executor_param_listener_.get_params();
408 for (
const rclcpp::Parameter & p : parameters) {
409 auto create_rejected = [&p](
const std::string msg) {
410 rcl_interfaces::msg::SetParametersResult result;
411 result.successful =
false;
412 result.reason =
"Rejected to set " + p.get_name() +
" = " + p.value_to_string() +
" (Type: " + p.get_type_name() +
416 const std::string param_name = p.get_name();
422 return create_rejected(
"Scripting enums cannot change while tree executor is running");
424 if (!executor_options_.scripting_enum_parameters_dynamic_ || !params.allow_dynamic_scripting_enums) {
425 return create_rejected(
426 "Cannot set scripting enum '" + enum_key +
"', because the 'Dynamic scripting enums' option is disabled");
430 return create_rejected(
431 "Type of scripting enum must be bool or int. Tried to set enum '" + enum_key +
"' with value '" +
432 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
440 !entry_key.empty()) {
441 if (!executor_options_.blackboard_parameters_dynamic_ || !params.allow_dynamic_blackboard) {
442 return create_rejected(
443 "Cannot set blackboard entry '" + entry_key +
"', because the 'Dynamic blackboard' option is disabled");
447 return create_rejected(
448 "Type of blackboard entries must not change. Tried to set entry '" + entry_key +
450 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
458 return create_rejected(
"Parameter is unkown");
463 return create_rejected(
"Parameter is not allowed to change while tree executor is running");
467 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
468 if (!params.allow_other_build_handlers) {
469 return create_rejected(
470 "This executor operates with tree build handler '" + executor_param_listener_.get_params().build_handler +
471 "' and doesn't allow other build handlers to be loaded since the 'Allow other build handlers' option is "
474 const std::string class_name = p.as_string();
475 if (class_name != PARAM_VALUE_NO_BUILD_HANDLER && !build_handler_loader_ptr_->isClassAvailable(class_name)) {
476 return create_rejected(
477 "Cannot load build handler '" + class_name +
478 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
480 "and registered it by calling auto_apms_behavior_tree_declare_build_handlers() in the CMakeLists.txt of the "
481 "corresponding package");
486 if (!
node_ptr_->has_parameter(param_name)) {
487 return create_rejected(
"Parameter '" + param_name +
"' is not supported");
492 rcl_interfaces::msg::SetParametersResult result;
493 result.successful =
true;
497void TreeExecutorNode::parameter_event_callback_(
const rcl_interfaces::msg::ParameterEvent & event)
500 std::regex re(
node_ptr_->get_fully_qualified_name());
501 if (std::regex_match(event.node, re)) {
503 for (
const rclcpp::Parameter & p : rclcpp::ParameterEventHandler::get_parameters_from_event(event)) {
504 const std::string param_name = p.get_name();
514 !entry_key.empty()) {
519 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
526rclcpp_action::GoalResponse TreeExecutorNode::handle_start_goal_(
527 const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const StartActionContext::Goal> goal_ptr)
532 logger_,
"Goal %s was REJECTED: Tree '%s' is currently executing.", rclcpp_action::to_string(uuid).c_str(),
534 return rclcpp_action::GoalResponse::REJECT;
537 if (!goal_ptr->build_handler.empty()) {
538 if (executor_param_listener_.get_params().allow_other_build_handlers) {
541 }
catch (
const std::exception & e) {
543 logger_,
"Goal %s was REJECTED: Loading tree build handler '%s' failed: %s",
544 rclcpp_action::to_string(uuid).c_str(), goal_ptr->build_handler.c_str(), e.what());
545 return rclcpp_action::GoalResponse::REJECT;
547 }
else if (goal_ptr->build_handler != current_build_handler_name_) {
550 "Goal %s was REJECTED: Current tree build handler '%s' must not change since the 'Allow other build handlers' "
551 "option is disabled.",
552 rclcpp_action::to_string(uuid).c_str(), current_build_handler_name_.c_str());
553 return rclcpp_action::GoalResponse::REJECT;
557 core::NodeManifest node_manifest;
559 node_manifest = core::NodeManifest::decode(goal_ptr->node_manifest);
560 }
catch (
const std::exception & e) {
562 logger_,
"Goal %s was REJECTED: Parsing the initial node manifest failed: %s",
563 rclcpp_action::to_string(uuid).c_str(), e.what());
564 return rclcpp_action::GoalResponse::REJECT;
567 core::NodeManifest node_overrides;
569 node_overrides = core::NodeManifest::decode(goal_ptr->node_overrides);
570 }
catch (
const std::exception & e) {
572 logger_,
"Goal %s was REJECTED: Parsing the node override manifest failed: %s",
573 rclcpp_action::to_string(uuid).c_str(), e.what());
574 return rclcpp_action::GoalResponse::REJECT;
579 makeTreeConstructor(goal_ptr->build_request, goal_ptr->root_tree, node_manifest, node_overrides);
580 }
catch (
const std::exception & e) {
581 RCLCPP_WARN(
logger_,
"Goal %s was REJECTED: %s", rclcpp_action::to_string(uuid).c_str(), e.what());
582 return rclcpp_action::GoalResponse::REJECT;
584 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
587rclcpp_action::CancelResponse TreeExecutorNode::handle_start_cancel_(
588 std::shared_ptr<StartActionContext::GoalHandle> )
591 return rclcpp_action::CancelResponse::ACCEPT;
594void TreeExecutorNode::handle_start_accept_(std::shared_ptr<StartActionContext::GoalHandle> goal_handle_ptr)
597 if (goal_handle_ptr->get_goal()->clear_blackboard) {
598 const auto res =
node_ptr_->list_parameters({BLACKBOARD_PARAM_PREFIX}, 2);
599 for (
const std::string & name : res.names) {
605 const ExecutorParameters params = executor_param_listener_.get_params();
607 startExecution(tree_constructor_, params.tick_rate, params.groot2_port);
608 }
catch (
const std::exception & e) {
609 auto result_ptr = std::make_shared<StartActionContext::Result>();
610 result_ptr->message =
"An error occurred trying to start execution: " + std::string(e.what());
611 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
612 goal_handle_ptr->abort(result_ptr);
613 RCLCPP_ERROR_STREAM(
logger_, result_ptr->message);
616 const std::string started_tree_name =
getTreeName();
620 if (goal_handle_ptr->get_goal()->attach) {
621 start_action_context_.setUp(goal_handle_ptr);
622 RCLCPP_INFO(
logger_,
"Successfully started execution of tree '%s' (Mode: Attached).", started_tree_name.c_str());
624 auto result_ptr = std::make_shared<StartActionContext::Result>();
625 result_ptr->message =
"Successfully started execution of tree '" + started_tree_name +
"' (Mode: Detached).";
626 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
627 result_ptr->terminated_tree_identity = started_tree_name;
628 goal_handle_ptr->succeed(result_ptr);
629 RCLCPP_INFO_STREAM(
logger_, result_ptr->message);
633rclcpp_action::GoalResponse TreeExecutorNode::handle_command_goal_(
634 const rclcpp_action::GoalUUID & , std::shared_ptr<const CommandActionContext::Goal> goal_ptr)
636 if (command_timer_ptr_ && !command_timer_ptr_->is_canceled()) {
637 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because previous one is still busy.");
638 return rclcpp_action::GoalResponse::REJECT;
641 if (
isBusy() && start_action_context_.isValid() && start_action_context_.getGoalHandlePtr()->is_canceling()) {
642 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because tree executor is canceling.");
643 return rclcpp_action::GoalResponse::REJECT;
647 switch (goal_ptr->command) {
648 case CommandActionContext::Goal::COMMAND_RESUME:
653 logger_,
"Requested to RESUME with executor being in state %s. Rejecting request.",
654 toStr(execution_state).c_str());
655 return rclcpp_action::GoalResponse::REJECT;
658 case CommandActionContext::Goal::COMMAND_PAUSE:
663 logger_,
"Requested to PAUSE with executor already being inactive (State: %s).",
664 toStr(execution_state).c_str());
667 case CommandActionContext::Goal::COMMAND_HALT:
674 logger_,
"Requested to HALT with executor already being inactive (State: %s).",
675 toStr(execution_state).c_str());
678 case CommandActionContext::Goal::COMMAND_TERMINATE:
683 logger_,
"Requested to TERMINATE with executor already being inactive (State: %s).",
684 toStr(execution_state).c_str());
688 RCLCPP_WARN(
logger_,
"Executor command %i is undefined. Rejecting request.", goal_ptr->command);
689 return rclcpp_action::GoalResponse::REJECT;
691 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
694rclcpp_action::CancelResponse TreeExecutorNode::handle_command_cancel_(
695 std::shared_ptr<CommandActionContext::GoalHandle> )
697 return rclcpp_action::CancelResponse::ACCEPT;
700void TreeExecutorNode::handle_command_accept_(std::shared_ptr<CommandActionContext::GoalHandle> goal_handle_ptr)
702 const auto command_request = goal_handle_ptr->get_goal()->command;
704 switch (command_request) {
705 case CommandActionContext::Goal::COMMAND_RESUME:
709 case CommandActionContext::Goal::COMMAND_PAUSE:
713 case CommandActionContext::Goal::COMMAND_HALT:
717 case CommandActionContext::Goal::COMMAND_TERMINATE:
722 throw std::logic_error(
"command_request is unkown");
725 command_timer_ptr_ =
node_ptr_->create_wall_timer(
726 std::chrono::duration<double>(executor_param_listener_.get_params().tick_rate),
727 [
this, requested_state, goal_handle_ptr, action_result_ptr = std::make_shared<CommandActionContext::Result>()]() {
729 if (goal_handle_ptr->is_canceling()) {
731 goal_handle_ptr->canceled(action_result_ptr);
732 command_timer_ptr_->cancel();
741 logger_,
"Failed to reach requested state %s due to cancellation of execution timer. Aborting.",
742 toStr(requested_state).c_str());
743 goal_handle_ptr->abort(action_result_ptr);
744 command_timer_ptr_->cancel();
749 if (current_state != requested_state)
return;
751 goal_handle_ptr->succeed(action_result_ptr);
752 command_timer_ptr_->cancel();
756bool TreeExecutorNode::onTick()
758 const ExecutorParameters params = executor_param_listener_.get_params();
767 const ExecutorParameters params = executor_param_listener_.get_params();
773 if (executor_options_.blackboard_parameters_dynamic_ && params.allow_dynamic_blackboard) {
775 std::vector<rclcpp::Parameter> new_parameters;
776 for (
const BT::StringView & str : bb_ptr->getKeys()) {
777 const std::string key = std::string(str);
778 const BT::TypeInfo * type_info = bb_ptr->entryInfo(key);
779 const BT::Any * any = bb_ptr->getAnyLocked(key).get();
786 if (any->empty())
continue;
790 if (translated_global_blackboard_entries_.find(key) == translated_global_blackboard_entries_.end()) {
792 const BT::Expected<rclcpp::ParameterValue> expected =
795 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
796 translated_global_blackboard_entries_[key] = expected.value();
799 logger_,
"Failed to translate new blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
800 type_info->typeName().c_str(), expected.error().c_str());
804 const BT::Expected<rclcpp::ParameterValue> expected =
807 if (expected.value() != translated_global_blackboard_entries_[key]) {
808 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
812 logger_,
"Failed to translate blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
813 type_info->typeName().c_str(), expected.error().c_str());
817 if (!new_parameters.empty()) {
818 const rcl_interfaces::msg::SetParametersResult result =
node_ptr_->set_parameters_atomically(new_parameters);
819 if (!result.successful) {
820 throw exceptions::TreeExecutorError(
821 "Unexpectedly failed to set parameters inferred from global blackboard. Reason: " + result.reason);
831 if (start_action_context_.isValid()) {
833 auto feedback_ptr = start_action_context_.getFeedbackPtr();
835 feedback_ptr->running_tree_identity =
getTreeName();
837 if (!running_action_history.empty()) {
839 feedback_ptr->running_action_name = auto_apms_util::join(running_action_history,
" + ");
840 feedback_ptr->running_action_timestamp =
841 std::chrono::duration<double>{std::chrono::high_resolution_clock::now().time_since_epoch()}.count();
844 state_observer.
flush();
846 start_action_context_.publishFeedback();
852void TreeExecutorNode::onTermination(
const ExecutionResult & result)
854 if (!start_action_context_.
isValid())
858 result_ptr->terminated_tree_identity =
getTreeName();
861 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_SUCCESS;
862 result_ptr->message =
"Tree execution finished with status SUCCESS";
863 start_action_context_.
succeed();
866 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_FAILURE;
867 result_ptr->message =
"Tree execution finished with status FAILURE";
868 start_action_context_.
abort();
871 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
873 result_ptr->message =
"Tree execution canceled successfully";
874 start_action_context_.
cancel();
876 result_ptr->message =
"Tree execution terminated prematurely";
877 start_action_context_.
abort();
881 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
882 result_ptr->message =
"An unexpected error occurred during tree execution";
883 start_action_context_.abort();
886 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
887 result_ptr->message =
"Execution result unkown";
888 start_action_context_.abort();
893 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.
@ 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.
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
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 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.
void cancel()
Terminate the current goal and mark it as canceled.
std::shared_ptr< GoalHandle > getGoalHandlePtr()
Get the goal handle managed by this ActionContext instance.
std::shared_ptr< Result > getResultPtr()
Access the internal action result buffer.
void succeed()
Terminate the current goal and mark it as succeeded.
void abort()
Terminate the current goal and mark it as aborted.
bool isValid()
Check if this ActionContext is valid (e.g. is managing a valid action goal handle).
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.