2121#include < limits>
2222#include < memory>
2323#include < set>
24+ #include < unordered_set>
2425#include < string>
2526#include < vector>
2627
@@ -45,7 +46,7 @@ BtActionServer<ActionT, NodeT>::BtActionServer(
4546 OnCompletionCallback on_completion_callback,
4647 const std::vector<std::string> & search_directories)
4748: action_name_(action_name),
48- default_bt_xml_filename_ (default_bt_xml_filename),
49+ default_bt_xml_filename_or_id_ (default_bt_xml_filename),
4950 search_directories_(search_directories),
5051 plugin_lib_names_(plugin_lib_names),
5152 node_(parent),
@@ -60,21 +61,7 @@ BtActionServer<ActionT, NodeT>::BtActionServer(
6061 logger_ = node->get_logger ();
6162 clock_ = node->get_clock ();
6263
63- // Declare this node's parameters
64- if (!node->has_parameter (" bt_loop_duration" )) {
65- node->declare_parameter (" bt_loop_duration" , 10 );
66- }
67- if (!node->has_parameter (" default_server_timeout" )) {
68- node->declare_parameter (" default_server_timeout" , 20 );
69- }
70- if (!node->has_parameter (" always_reload_bt_xml" )) {
71- node->declare_parameter (" always_reload_bt_xml" , false );
72- }
73- if (!node->has_parameter (" wait_for_service_timeout" )) {
74- node->declare_parameter (" wait_for_service_timeout" , 1000 );
75- }
76-
77- std::vector<std::string> error_code_name_prefixes = {
64+ std::vector<std::string> default_error_code_name_prefixes = {
7865 " assisted_teleop" ,
7966 " backup" ,
8067 " compute_path" ,
@@ -94,32 +81,26 @@ BtActionServer<ActionT, NodeT>::BtActionServer(
9481 " Please review migration guide and update your configuration." );
9582 }
9683
97- if (!node->has_parameter (" error_code_name_prefixes" )) {
98- const rclcpp::ParameterValue value = node->declare_parameter (
99- " error_code_name_prefixes" ,
100- rclcpp::PARAMETER_STRING_ARRAY);
101- if (value.get_type () == rclcpp::PARAMETER_NOT_SET) {
102- std::string error_code_name_prefixes_str;
103- for (const auto & error_code_name_prefix : error_code_name_prefixes) {
104- error_code_name_prefixes_str += " " + error_code_name_prefix;
105- }
106- RCLCPP_WARN_STREAM (
107- logger_, " error_code_name_prefixes parameters were not set. Using default values of:"
108- << error_code_name_prefixes_str + " \n "
109- << " Make sure these match your BT and there are not other sources of error codes you"
110- " reported to your application" );
111- rclcpp::Parameter error_code_name_prefixes_param (" error_code_name_prefixes" ,
112- error_code_name_prefixes);
113- node->set_parameter (error_code_name_prefixes_param);
114- } else {
115- error_code_name_prefixes = value.get <std::vector<std::string>>();
116- std::string error_code_name_prefixes_str;
117- for (const auto & error_code_name_prefix : error_code_name_prefixes) {
118- error_code_name_prefixes_str += " " + error_code_name_prefix;
119- }
120- RCLCPP_INFO_STREAM (logger_, " Error_code parameters were set to:"
121- << error_code_name_prefixes_str);
122- }
84+ // Declare and get error code name prefixes parameter
85+ error_code_name_prefixes_ = node->declare_or_get_parameter (
86+ " error_code_name_prefixes" ,
87+ default_error_code_name_prefixes);
88+
89+ // Provide informative logging about error code prefixes
90+ std::string error_code_name_prefixes_str;
91+ for (const auto & error_code_name_prefix : error_code_name_prefixes_) {
92+ error_code_name_prefixes_str += " " + error_code_name_prefix;
93+ }
94+
95+ if (error_code_name_prefixes_ == default_error_code_name_prefixes) {
96+ RCLCPP_WARN_STREAM (
97+ logger_, " error_code_name_prefixes parameters were not set. Using default values of:"
98+ << error_code_name_prefixes_str + " \n "
99+ << " Make sure these match your BT and there are not other sources of error codes you"
100+ << " reported to your application" );
101+ } else {
102+ RCLCPP_INFO_STREAM (logger_, " Error_code parameters were set to:"
103+ << error_code_name_prefixes_str);
123104 }
124105}
125106
@@ -169,16 +150,17 @@ bool BtActionServer<ActionT, NodeT>::on_configure()
169150 nullptr , std::chrono::milliseconds (500 ), false );
170151
171152 // Get parameters for BT timeouts
172- int bt_loop_duration;
173- node->get_parameter (" bt_loop_duration" , bt_loop_duration);
174- bt_loop_duration_ = std::chrono::milliseconds (bt_loop_duration);
175- int default_server_timeout;
176- node->get_parameter (" default_server_timeout" , default_server_timeout);
177- default_server_timeout_ = std::chrono::milliseconds (default_server_timeout);
178- int wait_for_service_timeout;
179- node->get_parameter (" wait_for_service_timeout" , wait_for_service_timeout);
180- wait_for_service_timeout_ = std::chrono::milliseconds (wait_for_service_timeout);
181- node->get_parameter (" always_reload_bt_xml" , always_reload_bt_xml_);
153+ bt_loop_duration_ = std::chrono::milliseconds (
154+ node->declare_or_get_parameter (" bt_loop_duration" , 10 ));
155+
156+ default_server_timeout_ = std::chrono::milliseconds (
157+ node->declare_or_get_parameter (" default_server_timeout" , 20 ));
158+
159+ wait_for_service_timeout_ = std::chrono::milliseconds (
160+ node->declare_or_get_parameter (" wait_for_service_timeout" , 1000 ));
161+
162+ always_reload_bt_ = node->declare_or_get_parameter (
163+ " always_reload_bt_xml" , false );
182164
183165 // Get error code id names to grab off of the blackboard
184166 error_code_name_prefixes_ = node->get_parameter (" error_code_name_prefixes" ).as_string_array ();
@@ -204,8 +186,8 @@ template<class ActionT, class NodeT>
204186bool BtActionServer<ActionT, NodeT>::on_activate()
205187{
206188 resetInternalError ();
207- if (!loadBehaviorTree (default_bt_xml_filename_ )) {
208- RCLCPP_ERROR (logger_, " Error loading XML file : %s" , default_bt_xml_filename_ .c_str ());
189+ if (!loadBehaviorTree (default_bt_xml_filename_or_id_ )) {
190+ RCLCPP_ERROR (logger_, " Error loading BT : %s" , default_bt_xml_filename_or_id_ .c_str ());
209191 return false ;
210192 }
211193 action_server_->activate ();
@@ -228,7 +210,7 @@ bool BtActionServer<ActionT, NodeT>::on_cleanup()
228210 action_server_.reset ();
229211 topic_logger_.reset ();
230212 plugin_lib_names_.clear ();
231- current_bt_xml_filename_ .clear ();
213+ current_bt_file_or_id_ .clear ();
232214 blackboard_.reset ();
233215 bt_->haltAllActions (tree_);
234216 bt_->resetGrootMonitor ();
@@ -246,40 +228,49 @@ void BtActionServer<ActionT, NodeT>::setGrootMonitoring(
246228}
247229
248230template <class ActionT , class NodeT >
249- bool BtActionServer<ActionT, NodeT>::loadBehaviorTree(const std::string & bt_xml_filename )
231+ bool BtActionServer<ActionT, NodeT>::loadBehaviorTree(const std::string & bt_xml_filename_or_id )
250232{
251233 namespace fs = std::filesystem;
252234
253- // Empty filename is default for backward compatibility
254- auto filename = bt_xml_filename.empty () ? default_bt_xml_filename_ : bt_xml_filename;
235+ // Empty argument is default for backward compatibility
236+ auto file_or_id =
237+ bt_xml_filename_or_id.empty () ? default_bt_xml_filename_or_id_ : bt_xml_filename_or_id;
255238
256239 // Use previous BT if it is the existing one and always reload flag is not set to true
257- if (!always_reload_bt_xml_ && current_bt_xml_filename_ == filename ) {
258- RCLCPP_DEBUG (logger_, " BT will not be reloaded as the given xml is already loaded" );
240+ if (!always_reload_bt_ && current_bt_file_or_id_ == file_or_id ) {
241+ RCLCPP_DEBUG (logger_, " BT will not be reloaded as the given xml or ID is already loaded" );
259242 return true ;
260243 }
261244
262245 // Reset any existing Groot2 monitoring
263246 bt_->resetGrootMonitor ();
264247
265- std::ifstream xml_file (filename) ;
266- if (!xml_file. good ()) {
267- setInternalError (ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
268- " Couldn't open BT XML file: " + filename);
269- return false ;
248+ bool is_bt_id = false ;
249+ if ((file_or_id. length () < 4 ) ||
250+ file_or_id. substr (file_or_id. length () - 4 ) != " .xml " )
251+ {
252+ is_bt_id = true ;
270253 }
271254
272- const auto canonical_main_bt = fs::canonical (filename);
273-
274- // Register all XML behavior Subtrees found in the given directories
255+ std::unordered_set<std::string> used_bt_id;
275256 for (const auto & directory : search_directories_) {
276257 try {
277258 for (const auto & entry : fs::directory_iterator (directory)) {
278259 if (entry.path ().extension () == " .xml" ) {
279- // Skip registering the main tree file
280- if (fs::equivalent (fs::canonical (entry.path ()), canonical_main_bt)) {
260+ auto current_bt_id = bt_->extractBehaviorTreeID (entry.path ().string ());
261+ if (current_bt_id.empty ()) {
262+ RCLCPP_ERROR (logger_, " Skipping BT file %s (missing ID)" ,
263+ entry.path ().string ().c_str ());
281264 continue ;
282265 }
266+ auto [it, inserted] = used_bt_id.insert (current_bt_id);
267+ if (!inserted) {
268+ RCLCPP_WARN (
269+ logger_,
270+ " Warning: Duplicate BT IDs found. Make sure to have all BT IDs unique! "
271+ " ID: %s File: %s" ,
272+ current_bt_id.c_str (), entry.path ().string ().c_str ());
273+ }
283274 bt_->registerTreeFromFile (entry.path ().string ());
284275 }
285276 }
@@ -289,18 +280,21 @@ bool BtActionServer<ActionT, NodeT>::loadBehaviorTree(const std::string & bt_xml
289280 return false ;
290281 }
291282 }
292-
293- // Try to load the main BT tree
283+ // Try to load the main BT tree (by ID)
294284 try {
295- tree_ = bt_->createTreeFromFile (filename, blackboard_);
285+ if (!is_bt_id) {
286+ tree_ = bt_->createTreeFromFile (file_or_id, blackboard_);
287+ } else {
288+ tree_ = bt_->createTree (file_or_id, blackboard_);
289+ }
290+
296291 for (auto & subtree : tree_.subtrees ) {
297292 auto & blackboard = subtree->blackboard ;
298293 blackboard->set (" node" , client_node_);
299294 blackboard->set <std::chrono::milliseconds>(" server_timeout" , default_server_timeout_);
300295 blackboard->set <std::chrono::milliseconds>(" bt_loop_duration" , bt_loop_duration_);
301296 blackboard->set <std::chrono::milliseconds>(
302- " wait_for_service_timeout" ,
303- wait_for_service_timeout_);
297+ " wait_for_service_timeout" , wait_for_service_timeout_);
304298 }
305299 } catch (const std::exception & e) {
306300 setInternalError (ActionT::Result::FAILED_TO_LOAD_BEHAVIOR_TREE,
@@ -310,8 +304,7 @@ bool BtActionServer<ActionT, NodeT>::loadBehaviorTree(const std::string & bt_xml
310304
311305 // Optional logging and monitoring
312306 topic_logger_ = std::make_unique<RosTopicLogger>(client_node_, tree_);
313-
314- current_bt_xml_filename_ = filename;
307+ current_bt_file_or_id_ = file_or_id;
315308
316309 if (enable_groot_monitoring_) {
317310 bt_->addGrootMonitoring (&tree_, groot_server_port_);
0 commit comments