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_);
80 opt.enable_logger_service(
true);
86:
TreeExecutorBase(std::make_shared<rclcpp::Node>(name, executor_options.getROSNodeOptions())),
87 executor_options_(executor_options),
95 std::vector<rclcpp::Parameter> new_default_parameters;
96 std::map<std::string, rclcpp::ParameterValue> effective_param_overrides =
97 node_ptr_->get_node_parameters_interface()->get_parameter_overrides();
98 for (
const auto & [name, value] : executor_options_.custom_default_parameters_) {
100 if (effective_param_overrides.find(name) == effective_param_overrides.end()) {
101 new_default_parameters.push_back(rclcpp::Parameter(name, value));
104 if (!new_default_parameters.empty()) node_ptr_->set_parameters_atomically(new_default_parameters);
106 const ExecutorParameters initial_params = executor_param_listener_.get_params();
109 rcl_interfaces::msg::ListParametersResult res = node_ptr_->list_parameters({}, 0);
110 std::vector<std::string> unkown_param_names;
111 for (
const std::string & param_name : res.names) {
112 if (!stripPrefixFromParameterName(SCRIPTING_ENUM_PARAM_PREFIX, param_name).empty()) continue;
113 if (!stripPrefixFromParameterName(BLACKBOARD_PARAM_PREFIX, param_name).empty()) continue;
114 if (auto_apms_util::contains(EXPLICITLY_ALLOWED_PARAMETERS, param_name)) continue;
116 node_ptr_->undeclare_parameter(param_name);
117 } catch (
const rclcpp::exceptions::ParameterImmutableException & e) {
120 }
catch (
const rclcpp::exceptions::InvalidParameterTypeException & e) {
124 unkown_param_names.push_back(param_name);
126 if (!unkown_param_names.empty()) {
128 logger_,
"The following initial parameters are not supported and have been removed: [ %s ].",
129 auto_apms_util::join(unkown_param_names,
", ").c_str());
133 tree_node_loader_ptr_ = core::NodeRegistrationLoader::make_shared(
134 std::set<std::string>(initial_params.node_exclude_packages.begin(), initial_params.node_exclude_packages.end()));
137 build_handler_loader_ptr_ = TreeBuildHandlerLoader::make_unique(
138 std::set<std::string>(
139 initial_params.build_handler_exclude_packages.begin(), initial_params.build_handler_exclude_packages.end()));
143 initial_params.build_handler != PARAM_VALUE_NO_BUILD_HANDLER &&
144 !build_handler_loader_ptr_->isClassAvailable(initial_params.build_handler)) {
145 throw exceptions::TreeExecutorError(
146 "Cannot load build handler '" + initial_params.build_handler +
147 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
149 "and registered it by calling auto_apms_behavior_tree_register_build_handlers() in the CMakeLists.txt of the "
150 "corresponding package.");
152 loadBuildHandler(initial_params.build_handler);
155 const auto initial_scripting_enums = getParameterValuesWithPrefix(SCRIPTING_ENUM_PARAM_PREFIX);
156 if (!initial_scripting_enums.empty()) {
157 if (executor_options_.scripting_enum_parameters_from_overrides_) {
158 updateScriptingEnumsWithParameterValues(initial_scripting_enums);
162 "Initial scripting enums have been provided, but the 'Scripting enums from overrides' option is disabled. "
166 const auto initial_blackboard = getParameterValuesWithPrefix(BLACKBOARD_PARAM_PREFIX);
167 if (!initial_blackboard.empty()) {
168 if (executor_options_.blackboard_parameters_from_overrides_) {
169 updateGlobalBlackboardWithParameterValues(initial_blackboard);
173 "Initial blackboard entries have been provided, but the 'Blackboard from overrides' option is disabled. "
178 using namespace std::placeholders;
179 start_action_ptr_ = rclcpp_action::create_server<StartActionContext::Type>(
180 node_ptr_, std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_START_ACTION_NAME_SUFFIX,
181 std::bind(&TreeExecutorNode::handle_start_goal_,
this, _1, _2),
182 std::bind(&TreeExecutorNode::handle_start_cancel_,
this, _1),
183 std::bind(&TreeExecutorNode::handle_start_accept_,
this, _1));
185 command_action_ptr_ = rclcpp_action::create_server<CommandActionContext::Type>(
186 node_ptr_, std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_COMMAND_ACTION_NAME_SUFFIX,
187 std::bind(&TreeExecutorNode::handle_command_goal_,
this, _1, _2),
188 std::bind(&TreeExecutorNode::handle_command_cancel_,
this, _1),
189 std::bind(&TreeExecutorNode::handle_command_accept_,
this, _1));
191 clear_blackboard_service_ptr_ = node_ptr_->create_service<std_srvs::srv::Trigger>(
192 std::string(node_ptr_->get_name()) + _AUTO_APMS_BEHAVIOR_TREE__CLEAR_BLACKBOARD_SERVICE_NAME_SUFFIX,
194 const std::shared_ptr<std_srvs::srv::Trigger::Request> ,
195 std::shared_ptr<std_srvs::srv::Trigger::Response> response) {
196 response->success = this->clearGlobalBlackboard();
197 if (response->success) {
198 response->message =
"Blackboard was cleared successfully";
200 response->message =
"Blackboard cannot be cleared, because executor is in state " +
201 toStr(this->getExecutionState()) +
" but must be idling";
203 RCLCPP_DEBUG_STREAM(this->logger_, response->message);
212 on_set_parameters_callback_handle_ptr_ =
213 node_ptr_->add_on_set_parameters_callback([
this](
const std::vector<rclcpp::Parameter> & parameters) {
214 return this->on_set_parameters_callback_(parameters);
218 parameter_event_handler_ptr_ = std::make_shared<rclcpp::ParameterEventHandler>(node_ptr_);
219 parameter_event_callback_handle_ptr_ = parameter_event_handler_ptr_->add_parameter_event_callback(
220 [
this](
const rcl_interfaces::msg::ParameterEvent & event) { this->parameter_event_callback_(event); });
223 std::vector<std::string> args_with_ros_arguments = node_ptr_->get_node_options().arguments();
224 int argc = args_with_ros_arguments.size();
225 char ** argv =
new char *[argc + 1];
226 for (
int i = 0; i < argc; ++i) {
227 argv[i] =
const_cast<char *
>(args_with_ros_arguments[i].c_str());
229 argv[argc] =
nullptr;
233 if (
const std::vector<std::string> args = rclcpp::remove_ros_arguments(argc, argv); args.size() > 1) {
235 std::vector<std::string> relevant_args{args.begin() + 1, args.end()};
237 logger_,
"Additional cli arguments in rclcpp::NodeOptions: [ %s ]",
238 auto_apms_util::join(relevant_args,
", ").c_str());
241 startExecution(makeTreeConstructor(relevant_args[0]), initial_params.tick_rate, initial_params.groot2_port);
257 const std::string & build_request,
const std::string & entrypoint,
const core::NodeManifest & node_manifest)
259 const ExecutorParameters params = executor_param_listener_.get_params();
261 makeTreeConstructor(build_request, entrypoint, node_manifest), params.tick_rate, params.groot2_port);
266 const auto res =
node_ptr_->list_parameters({prefix}, 2);
267 std::map<std::string, rclcpp::ParameterValue> value_map;
268 for (
const std::string & name_with_prefix : res.names) {
270 value_map[suffix] =
node_ptr_->get_parameter(name_with_prefix).get_parameter_value();
278 const std::regex reg(
"^" + prefix +
"\\.(\\S+)");
279 if (std::smatch match; std::regex_match(param_name, match, reg))
return match[1].str();
284 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
286 std::map<std::string, std::string> set_successfully_map;
287 for (
const auto & [enum_key, pval] : value_map) {
289 switch (pval.get_type()) {
290 case rclcpp::ParameterType::PARAMETER_BOOL:
291 if (simulate)
continue;
292 scripting_enums_[enum_key] =
static_cast<int>(pval.get<
bool>());
294 case rclcpp::ParameterType::PARAMETER_INTEGER:
295 if (simulate)
continue;
296 scripting_enums_[enum_key] =
static_cast<int>(pval.get<int64_t>());
299 if (simulate)
return false;
300 throw exceptions::ParameterConversionError(
"Parameter to scripting enum conversion is not allowed.");
302 set_successfully_map[enum_key] = rclcpp::to_string(pval);
303 }
catch (
const std::exception & e) {
305 logger_,
"Error setting scripting enum from parameter %s=%s (Type: %s): %s", enum_key.c_str(),
306 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
310 if (!set_successfully_map.empty()) {
312 logger_,
"Updated scripting enums from parameters: { %s }",
319 const std::map<std::string, rclcpp::ParameterValue> & value_map,
bool simulate)
322 std::map<std::string, std::string> set_successfully_map;
323 for (
const auto & [entry_key, pval] : value_map) {
326 BT::Any any(expected.value());
329 if (
const BT::TypeInfo * entry_info = bb.entryInfo(entry_key)) {
330 if (entry_info->isStronglyTyped() && entry_info->type() != any.type())
return false;
334 bb.set(entry_key, any);
337 throw exceptions::ParameterConversionError(expected.error());
339 translated_global_blackboard_entries_[entry_key] = pval;
340 set_successfully_map[entry_key] = rclcpp::to_string(pval);
341 }
catch (
const std::exception & e) {
343 logger_,
"Error updating blackboard from parameter %s=%s (Type: %s): %s", entry_key.c_str(),
344 rclcpp::to_string(pval).c_str(), rclcpp::to_string(pval.get_type()).c_str(), e.what());
348 if (!set_successfully_map.empty()) {
357 if (build_handler_ptr_ && !executor_param_listener_.get_params().allow_other_build_handlers) {
358 throw std::logic_error(
359 "Executor option 'Allow other build handlers' is disabled, but loadBuildHandler() was called again after "
361 current_build_handler_name_ +
"'.");
363 if (current_build_handler_name_ == name)
return;
364 if (name == PARAM_VALUE_NO_BUILD_HANDLER) {
365 build_handler_ptr_.reset();
369 build_handler_loader_ptr_->createUniqueInstance(name)->makeUnique(
node_ptr_, tree_node_loader_ptr_);
370 }
catch (
const pluginlib::CreateClassException & e) {
371 throw exceptions::TreeExecutorError(
372 "An error occurred when trying to create an instance of tree build handler class '" + name +
373 "'. This might be because you forgot to call the AUTO_APMS_BEHAVIOR_TREE_REGISTER_BUILD_HANDLER macro "
374 "in the source file: " +
376 }
catch (
const std::exception & e) {
377 throw exceptions::TreeExecutorError(
378 "An error occurred when trying to create an instance of tree build handler class '" + name +
"': " + e.what());
381 current_build_handler_name_ = name;
385 const std::string & build_request,
const std::string & entrypoint,
const core::NodeManifest & node_manifest)
388 if (build_handler_ptr_ && !build_handler_ptr_->setBuildRequest(build_request, entrypoint, node_manifest)) {
389 throw exceptions::TreeBuildError(
390 "Build request '" + build_request +
"' was denied by '" + executor_param_listener_.get_params().build_handler +
391 "' (setBuildRequest() returned false).");
397 return [
this, build_request, entrypoint, node_manifest](TreeBlackboardSharedPtr bb_ptr) {
401 builder_ptr_.reset(
new TreeBuilder(
408 for (
const auto & [enum_key, val] : scripting_enums_) builder_ptr_->setScriptingEnum(enum_key, val);
411 std::string instantiate_name =
"";
412 if (build_handler_ptr_) {
413 instantiate_name = build_handler_ptr_->buildTree(*builder_ptr_, *bb_ptr).getName();
417 return instantiate_name.empty() ? builder_ptr_->instantiate(bb_ptr)
418 : builder_ptr_->instantiate(instantiate_name, bb_ptr);
425 const auto res =
node_ptr_->list_parameters({BLACKBOARD_PARAM_PREFIX}, 2);
426 for (
const std::string & name : res.names) {
434rcl_interfaces::msg::SetParametersResult TreeExecutorNode::on_set_parameters_callback_(
435 const std::vector<rclcpp::Parameter> & parameters)
437 const ExecutorParameters params = executor_param_listener_.get_params();
440 for (
const rclcpp::Parameter & p : parameters) {
441 auto create_rejected = [&p](
const std::string msg) {
442 rcl_interfaces::msg::SetParametersResult result;
443 result.successful =
false;
444 result.reason =
"Rejected to set " + p.get_name() +
" = " + p.value_to_string() +
" (Type: " + p.get_type_name() +
448 const std::string param_name = p.get_name();
454 return create_rejected(
"Scripting enums cannot change while tree executor is running");
456 if (!executor_options_.scripting_enum_parameters_dynamic_ || !params.allow_dynamic_scripting_enums) {
457 return create_rejected(
458 "Cannot set scripting enum '" + enum_key +
"', because the 'Dynamic scripting enums' option is disabled");
462 return create_rejected(
463 "Type of scripting enum must be bool or int. Tried to set enum '" + enum_key +
"' with value '" +
464 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
472 !entry_key.empty()) {
473 if (!executor_options_.blackboard_parameters_dynamic_ || !params.allow_dynamic_blackboard) {
474 return create_rejected(
475 "Cannot set blackboard entry '" + entry_key +
"', because the 'Dynamic blackboard' option is disabled");
479 return create_rejected(
480 "Type of blackboard entries must not change. Tried to set entry '" + entry_key +
482 p.value_to_string() +
"' (Type: " + p.get_type_name() +
")");
490 return create_rejected(
"Parameter is unkown");
495 return create_rejected(
"Parameter is not allowed to change while tree executor is running");
499 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
500 if (!params.allow_other_build_handlers) {
501 return create_rejected(
502 "This executor operates with tree build handler '" + executor_param_listener_.get_params().build_handler +
503 "' and doesn't allow other build handlers to be loaded since the 'Allow other build handlers' option is "
506 const std::string class_name = p.as_string();
507 if (class_name != PARAM_VALUE_NO_BUILD_HANDLER && !build_handler_loader_ptr_->isClassAvailable(class_name)) {
508 return create_rejected(
509 "Cannot load build handler '" + class_name +
510 "' because no corresponding ament_index resource was found. Make sure that you spelled the build handler's "
512 "and registered it by calling auto_apms_behavior_tree_register_build_handlers() in the CMakeLists.txt of "
514 "corresponding package");
519 if (!
node_ptr_->has_parameter(param_name)) {
520 return create_rejected(
"Parameter '" + param_name +
"' is not supported");
525 rcl_interfaces::msg::SetParametersResult result;
526 result.successful =
true;
530void TreeExecutorNode::parameter_event_callback_(
const rcl_interfaces::msg::ParameterEvent & event)
533 std::regex re(
node_ptr_->get_fully_qualified_name());
534 if (std::regex_match(event.node, re)) {
536 for (
const rclcpp::Parameter & p : rclcpp::ParameterEventHandler::get_parameters_from_event(event)) {
537 const std::string param_name = p.get_name();
547 !entry_key.empty()) {
552 if (param_name == _AUTO_APMS_BEHAVIOR_TREE__EXECUTOR_PARAM_BUILD_HANDLER) {
559rclcpp_action::GoalResponse TreeExecutorNode::handle_start_goal_(
560 const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const StartActionContext::Goal> goal_ptr)
565 logger_,
"Goal %s was REJECTED: Tree '%s' is currently executing.", rclcpp_action::to_string(uuid).c_str(),
567 return rclcpp_action::GoalResponse::REJECT;
570 if (!goal_ptr->build_handler.empty()) {
571 if (executor_param_listener_.get_params().allow_other_build_handlers) {
574 }
catch (
const std::exception & e) {
576 logger_,
"Goal %s was REJECTED: Loading tree build handler '%s' failed: %s",
577 rclcpp_action::to_string(uuid).c_str(), goal_ptr->build_handler.c_str(), e.what());
578 return rclcpp_action::GoalResponse::REJECT;
580 }
else if (goal_ptr->build_handler != current_build_handler_name_) {
583 "Goal %s was REJECTED: Current tree build handler '%s' must not change since the 'Allow other build "
585 "option is disabled.",
586 rclcpp_action::to_string(uuid).c_str(), current_build_handler_name_.c_str());
587 return rclcpp_action::GoalResponse::REJECT;
591 core::NodeManifest node_manifest;
593 node_manifest = core::NodeManifest::decode(goal_ptr->node_manifest);
594 }
catch (
const std::exception & e) {
596 logger_,
"Goal %s was REJECTED: Parsing the initial node manifest failed: %s",
597 rclcpp_action::to_string(uuid).c_str(), e.what());
598 return rclcpp_action::GoalResponse::REJECT;
602 tree_constructor_ =
makeTreeConstructor(goal_ptr->build_request, goal_ptr->entrypoint, node_manifest);
603 }
catch (
const std::exception & e) {
604 RCLCPP_WARN(
logger_,
"Goal %s was REJECTED: %s", rclcpp_action::to_string(uuid).c_str(), e.what());
605 return rclcpp_action::GoalResponse::REJECT;
607 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
610rclcpp_action::CancelResponse TreeExecutorNode::handle_start_cancel_(
611 std::shared_ptr<StartActionContext::GoalHandle> )
614 return rclcpp_action::CancelResponse::ACCEPT;
617void TreeExecutorNode::handle_start_accept_(std::shared_ptr<StartActionContext::GoalHandle> goal_handle_ptr)
620 if (goal_handle_ptr->get_goal()->clear_blackboard) {
624 const ExecutorParameters params = executor_param_listener_.get_params();
626 startExecution(tree_constructor_, params.tick_rate, params.groot2_port);
627 }
catch (
const std::exception & e) {
628 auto result_ptr = std::make_shared<StartActionContext::Result>();
629 result_ptr->message =
"An error occurred trying to start execution: " + std::string(e.what());
630 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
631 goal_handle_ptr->abort(result_ptr);
632 RCLCPP_ERROR_STREAM(
logger_, result_ptr->message);
635 const std::string started_tree_name =
getTreeName();
639 if (goal_handle_ptr->get_goal()->attach) {
640 start_action_context_.setUp(goal_handle_ptr);
641 RCLCPP_INFO(
logger_,
"Successfully started execution of tree '%s' (Mode: Attached).", started_tree_name.c_str());
643 auto result_ptr = std::make_shared<StartActionContext::Result>();
644 result_ptr->message =
"Successfully started execution of tree '" + started_tree_name +
"' (Mode: Detached).";
645 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
646 result_ptr->terminated_tree_identity = started_tree_name;
647 goal_handle_ptr->succeed(result_ptr);
648 RCLCPP_INFO_STREAM(
logger_, result_ptr->message);
652rclcpp_action::GoalResponse TreeExecutorNode::handle_command_goal_(
653 const rclcpp_action::GoalUUID & , std::shared_ptr<const CommandActionContext::Goal> goal_ptr)
655 if (command_timer_ptr_ && !command_timer_ptr_->is_canceled()) {
656 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because previous one is still busy.");
657 return rclcpp_action::GoalResponse::REJECT;
660 if (
isBusy() && start_action_context_.isValid() && start_action_context_.getGoalHandlePtr()->is_canceling()) {
661 RCLCPP_WARN(
logger_,
"Request for setting tree executor command rejected, because tree executor is canceling.");
662 return rclcpp_action::GoalResponse::REJECT;
666 switch (goal_ptr->command) {
667 case CommandActionContext::Goal::COMMAND_RESUME:
672 logger_,
"Requested to RESUME with executor being in state %s. Rejecting request.",
673 toStr(execution_state).c_str());
674 return rclcpp_action::GoalResponse::REJECT;
677 case CommandActionContext::Goal::COMMAND_PAUSE:
682 logger_,
"Requested to PAUSE with executor already being inactive (State: %s).",
683 toStr(execution_state).c_str());
686 case CommandActionContext::Goal::COMMAND_HALT:
693 logger_,
"Requested to HALT with executor already being inactive (State: %s).",
694 toStr(execution_state).c_str());
697 case CommandActionContext::Goal::COMMAND_TERMINATE:
702 logger_,
"Requested to TERMINATE with executor already being inactive (State: %s).",
703 toStr(execution_state).c_str());
707 RCLCPP_WARN(
logger_,
"Executor command %i is undefined. Rejecting request.", goal_ptr->command);
708 return rclcpp_action::GoalResponse::REJECT;
710 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
713rclcpp_action::CancelResponse TreeExecutorNode::handle_command_cancel_(
714 std::shared_ptr<CommandActionContext::GoalHandle> )
716 return rclcpp_action::CancelResponse::ACCEPT;
719void TreeExecutorNode::handle_command_accept_(std::shared_ptr<CommandActionContext::GoalHandle> goal_handle_ptr)
721 const auto command_request = goal_handle_ptr->get_goal()->command;
723 switch (command_request) {
724 case CommandActionContext::Goal::COMMAND_RESUME:
728 case CommandActionContext::Goal::COMMAND_PAUSE:
732 case CommandActionContext::Goal::COMMAND_HALT:
736 case CommandActionContext::Goal::COMMAND_TERMINATE:
741 throw std::logic_error(
"command_request is unkown");
744 command_timer_ptr_ =
node_ptr_->create_wall_timer(
745 std::chrono::duration<double>(executor_param_listener_.get_params().tick_rate),
746 [
this, requested_state, goal_handle_ptr, action_result_ptr = std::make_shared<CommandActionContext::Result>()]() {
748 if (goal_handle_ptr->is_canceling()) {
750 goal_handle_ptr->canceled(action_result_ptr);
751 command_timer_ptr_->cancel();
760 logger_,
"Failed to reach requested state %s due to cancellation of execution timer. Aborting.",
761 toStr(requested_state).c_str());
762 goal_handle_ptr->abort(action_result_ptr);
763 command_timer_ptr_->cancel();
768 if (current_state != requested_state)
return;
770 goal_handle_ptr->succeed(action_result_ptr);
771 command_timer_ptr_->cancel();
775bool TreeExecutorNode::onTick()
777 const ExecutorParameters params = executor_param_listener_.get_params();
786 const ExecutorParameters params = executor_param_listener_.get_params();
792 if (executor_options_.blackboard_parameters_dynamic_ && params.allow_dynamic_blackboard) {
794 std::vector<rclcpp::Parameter> new_parameters;
795 for (
const BT::StringView & str : bb_ptr->getKeys()) {
796 const std::string key = std::string(str);
797 const BT::TypeInfo * type_info = bb_ptr->entryInfo(key);
798 const BT::Any * any = bb_ptr->getAnyLocked(key).get();
805 if (any->empty())
continue;
809 if (translated_global_blackboard_entries_.find(key) == translated_global_blackboard_entries_.end()) {
811 const BT::Expected<rclcpp::ParameterValue> expected =
814 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
815 translated_global_blackboard_entries_[key] = expected.value();
818 logger_,
"Failed to translate new blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
819 type_info->typeName().c_str(), expected.error().c_str());
823 const BT::Expected<rclcpp::ParameterValue> expected =
826 if (expected.value() != translated_global_blackboard_entries_[key]) {
827 new_parameters.push_back(rclcpp::Parameter(BLACKBOARD_PARAM_PREFIX +
"." + key, expected.value()));
831 logger_,
"Failed to translate blackboard entry '%s' (Type: %s) to parameters: %s", key.c_str(),
832 type_info->typeName().c_str(), expected.error().c_str());
836 if (!new_parameters.empty()) {
837 const rcl_interfaces::msg::SetParametersResult result =
node_ptr_->set_parameters_atomically(new_parameters);
838 if (!result.successful) {
839 throw exceptions::TreeExecutorError(
840 "Unexpectedly failed to set parameters inferred from global blackboard. Reason: " + result.reason);
850 if (start_action_context_.isValid()) {
852 auto feedback_ptr = start_action_context_.getFeedbackPtr();
854 feedback_ptr->running_tree_identity =
getTreeName();
856 if (!running_action_history.empty()) {
858 feedback_ptr->running_action_name = auto_apms_util::join(running_action_history,
" + ");
859 feedback_ptr->running_action_timestamp =
860 std::chrono::duration<double>{std::chrono::high_resolution_clock::now().time_since_epoch()}.count();
863 state_observer.
flush();
865 start_action_context_.publishFeedback();
871void TreeExecutorNode::onTermination(
const ExecutionResult & result)
873 if (!start_action_context_.
isValid())
877 result_ptr->terminated_tree_identity =
getTreeName();
880 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_SUCCESS;
881 result_ptr->message =
"Tree execution finished with status SUCCESS";
882 start_action_context_.
succeed();
885 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_FAILURE;
886 result_ptr->message =
"Tree execution finished with status FAILURE";
887 start_action_context_.
abort();
890 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
892 result_ptr->message =
"Tree execution canceled successfully";
893 start_action_context_.
cancel();
895 result_ptr->message =
"Tree execution terminated prematurely";
896 start_action_context_.
abort();
900 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
901 result_ptr->message =
"An unexpected error occurred during tree execution";
902 start_action_context_.abort();
905 result_ptr->tree_result = StartActionContext::Result::TREE_RESULT_NOT_SET;
906 result_ptr->message =
"Execution result unkown";
907 start_action_context_.abort();
912 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.
virtual bool 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.
std::shared_future< ExecutionResult > startExecution(const std::string &build_request, const std::string &entrypoint="", const core::NodeManifest &node_manifest={})
Start the behavior tree that is specified by a particular build request.
bool updateGlobalBlackboardWithParameterValues(const std::map< std::string, rclcpp::ParameterValue > &value_map, bool simulate=false)
Update the global blackboard using parameter values.
virtual void preconfigureBuilder(TreeBuilder &builder, const std::string &build_request, const std::string &entrypoint, const core::NodeManifest &node_manifest)
Callback invoked every time before any behavior trees are built.
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
TreeConstructor makeTreeConstructor(const std::string &build_request, const std::string &entrypoint="", const core::NodeManifest &node_manifest={})
Create a callback that builds a behavior tree according to a specific request.
virtual bool clearGlobalBlackboard() override
Reset the global blackboard and clear all entries. This also unsets the corresponding parameters.
std::map< std::string, rclcpp::ParameterValue > getParameterValuesWithPrefix(const std::string &prefix)
Assemble all parameters of this node that have a specific prefix.
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.
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.
Powerful 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.