tree_node.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved
2  * Copyright (C) 2018-2022 Davide Faconti, Eurecat - All Rights Reserved
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
5 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
6 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
11 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12 */
13 
15 #include <cstring>
16 #include <array>
17 
18 namespace BT
19 {
20 
22 {
23  PImpl(std::string name, NodeConfig config)
24  : name(std::move(name)), config(std::move(config))
25  {}
26 
27  const std::string name;
28 
30 
31  std::condition_variable state_condition_variable;
32 
34 
36 
38 
39  std::string registration_ID;
40 
44 
46 
47  std::shared_ptr<WakeUpSignal> wake_up;
48 
51 };
52 
53 TreeNode::TreeNode(std::string name, NodeConfig config)
54  : _p(new PImpl(std::move(name), std::move(config)))
55 {}
56 
57 TreeNode::TreeNode(TreeNode&& other) noexcept
58 {
59  this->_p = std::move(other._p);
60 }
61 
63 {
64  this->_p = std::move(other._p);
65  return *this;
66 }
67 
69 {}
70 
72 {
73  auto new_status = _p->status;
74  PreTickCallback pre_tick;
75  PostTickCallback post_tick;
76  TickMonitorCallback monitor_tick;
77  {
78  std::scoped_lock lk(_p->callback_injection_mutex);
79  pre_tick = _p->pre_tick_callback;
80  post_tick = _p->post_tick_callback;
81  monitor_tick = _p->tick_monitor_callback;
82  }
83 
84  // a pre-condition may return the new status.
85  // In this case it override the actual tick()
86  if(auto precond = checkPreConditions())
87  {
88  new_status = precond.value();
89  }
90  else
91  {
92  // injected pre-callback
93  bool substituted = false;
94  if(pre_tick && !isStatusCompleted(_p->status))
95  {
96  auto override_status = pre_tick(*this);
97  if(isStatusCompleted(override_status))
98  {
99  // don't execute the actual tick()
100  substituted = true;
101  new_status = override_status;
102  }
103  }
104 
105  // Call the ACTUAL tick
106  if(!substituted)
107  {
108  using namespace std::chrono;
109 
110  auto t1 = steady_clock::now();
111  // trick to prevent the compile from reordering the order of execution. See #861
112  // This makes sure that the code is executed at the end of this scope
113  std::shared_ptr<void> execute_later(nullptr, [&](...) {
114  auto t2 = steady_clock::now();
115  if(monitor_tick)
116  {
117  monitor_tick(*this, new_status, duration_cast<microseconds>(t2 - t1));
118  }
119  });
120 
121  new_status = tick();
122  }
123  }
124 
125  // injected post callback
126  if(isStatusCompleted(new_status))
127  {
128  checkPostConditions(new_status);
129  }
130 
131  if(post_tick)
132  {
133  auto override_status = post_tick(*this, new_status);
134  if(isStatusCompleted(override_status))
135  {
136  new_status = override_status;
137  }
138  }
139 
140  // preserve the IDLE state if skipped, but communicate SKIPPED to parent
141  if(new_status != NodeStatus::SKIPPED)
142  {
143  setStatus(new_status);
144  }
145  return new_status;
146 }
147 
149 {
150  halt();
151 
152  const auto& parse_executor = _p->post_parsed[size_t(PostCond::ON_HALTED)];
153  if(parse_executor)
154  {
156  parse_executor(env);
157  }
158 }
159 
161 {
162  if(new_status == NodeStatus::IDLE)
163  {
164  throw RuntimeError("Node [", name(),
165  "]: you are not allowed to set manually the status to IDLE. "
166  "If you know what you are doing (?) use resetStatus() instead.");
167  }
168 
169  NodeStatus prev_status;
170  {
171  std::unique_lock<std::mutex> UniqueLock(_p->state_mutex);
172  prev_status = _p->status;
173  _p->status = new_status;
174  }
175  if(prev_status != new_status)
176  {
177  _p->state_condition_variable.notify_all();
178  _p->state_change_signal.notify(std::chrono::high_resolution_clock::now(), *this,
179  prev_status, new_status);
180  }
181 }
182 
184 {
185  return _p->pre_parsed;
186 }
187 
189 {
190  return _p->post_parsed;
191 }
192 
194 {
196 
197  // check the pre-conditions
198  for(size_t index = 0; index < size_t(PreCond::COUNT_); index++)
199  {
200  const auto& parse_executor = _p->pre_parsed[index];
201  if(!parse_executor)
202  {
203  continue;
204  }
205 
206  const PreCond preID = PreCond(index);
207 
208  // Some preconditions are applied only when the node state is IDLE or SKIPPED
209  if(_p->status == NodeStatus::IDLE || _p->status == NodeStatus::SKIPPED)
210  {
211  // what to do if the condition is true
212  if(parse_executor(env).cast<bool>())
213  {
214  if(preID == PreCond::FAILURE_IF)
215  {
216  return NodeStatus::FAILURE;
217  }
218  else if(preID == PreCond::SUCCESS_IF)
219  {
220  return NodeStatus::SUCCESS;
221  }
222  else if(preID == PreCond::SKIP_IF)
223  {
224  return NodeStatus::SKIPPED;
225  }
226  }
227  // if the conditions is false
228  else if(preID == PreCond::WHILE_TRUE)
229  {
230  return NodeStatus::SKIPPED;
231  }
232  }
233  else if(_p->status == NodeStatus::RUNNING && preID == PreCond::WHILE_TRUE)
234  {
235  // what to do if the condition is false
236  if(!parse_executor(env).cast<bool>())
237  {
238  haltNode();
239  return NodeStatus::SKIPPED;
240  }
241  }
242  }
243  return nonstd::make_unexpected(""); // no precondition
244 }
245 
247 {
248  auto ExecuteScript = [this](const PostCond& cond) {
249  const auto& parse_executor = _p->post_parsed[size_t(cond)];
250  if(parse_executor)
251  {
253  parse_executor(env);
254  }
255  };
256 
258  {
259  ExecuteScript(PostCond::ON_SUCCESS);
260  }
261  else if(status == NodeStatus::FAILURE)
262  {
263  ExecuteScript(PostCond::ON_FAILURE);
264  }
265  ExecuteScript(PostCond::ALWAYS);
266 }
267 
269 {
270  NodeStatus prev_status;
271  {
272  std::unique_lock<std::mutex> lock(_p->state_mutex);
273  prev_status = _p->status;
274  _p->status = NodeStatus::IDLE;
275  }
276 
277  if(prev_status != NodeStatus::IDLE)
278  {
279  _p->state_condition_variable.notify_all();
280  _p->state_change_signal.notify(std::chrono::high_resolution_clock::now(), *this,
281  prev_status, NodeStatus::IDLE);
282  }
283 }
284 
286 {
287  std::lock_guard<std::mutex> lock(_p->state_mutex);
288  return _p->status;
289 }
290 
292 {
293  std::unique_lock<std::mutex> lock(_p->state_mutex);
294 
295  while(isHalted())
296  {
297  _p->state_condition_variable.wait(lock);
298  }
299  return _p->status;
300 }
301 
302 const std::string& TreeNode::name() const
303 {
304  return _p->name;
305 }
306 
307 bool TreeNode::isHalted() const
308 {
309  return _p->status == NodeStatus::IDLE;
310 }
311 
314 {
315  return _p->state_change_signal.subscribe(std::move(callback));
316 }
317 
319 {
320  std::unique_lock lk(_p->callback_injection_mutex);
321  _p->pre_tick_callback = callback;
322 }
323 
325 {
326  std::unique_lock lk(_p->callback_injection_mutex);
327  _p->post_tick_callback = callback;
328 }
329 
331 {
332  std::unique_lock lk(_p->callback_injection_mutex);
333  _p->tick_monitor_callback = callback;
334 }
335 
336 uint16_t TreeNode::UID() const
337 {
338  return _p->config.uid;
339 }
340 
341 const std::string& TreeNode::fullPath() const
342 {
343  return _p->config.path;
344 }
345 
346 const std::string& TreeNode::registrationName() const
347 {
348  return _p->registration_ID;
349 }
350 
352 {
353  return _p->config;
354 }
355 
357 {
358  return _p->config;
359 }
360 
361 StringView TreeNode::getRawPortValue(const std::string& key) const
362 {
363  auto remap_it = _p->config.input_ports.find(key);
364  if(remap_it == _p->config.input_ports.end())
365  {
366  remap_it = _p->config.output_ports.find(key);
367  if(remap_it == _p->config.output_ports.end())
368  {
369  throw std::logic_error(StrCat("[", key, "] not found"));
370  }
371  }
372  return remap_it->second;
373 }
374 
376 {
377  if(str.size() < 3)
378  {
379  return false;
380  }
381  // strip leading and following spaces
382  size_t front_index = 0;
383  size_t last_index = str.size() - 1;
384  while(str[front_index] == ' ' && front_index <= last_index)
385  {
386  front_index++;
387  }
388  while(str[last_index] == ' ' && front_index <= last_index)
389  {
390  last_index--;
391  }
392  const auto size = (last_index - front_index) + 1;
393  auto valid = size >= 3 && str[front_index] == '{' && str[last_index] == '}';
394  if(valid && stripped_pointer)
395  {
396  *stripped_pointer = StringView(&str[front_index + 1], size - 2);
397  }
398  return valid;
399 }
400 
402 {
403  StringView out;
404  if(isBlackboardPointer(str, &out))
405  {
406  return out;
407  }
408  return {};
409 }
410 
412  StringView remapped_port)
413 {
414  if(remapped_port == "{=}" || remapped_port == "=")
415  {
416  return { port_name };
417  }
418  StringView stripped;
419  if(isBlackboardPointer(remapped_port, &stripped))
420  {
421  return { stripped };
422  }
423  return nonstd::make_unexpected("Not a blackboard pointer");
424 }
425 
427 {
428  if(_p->wake_up)
429  {
430  _p->wake_up->emitSignal();
431  }
432 }
433 
435 {
436  return bool(_p->wake_up);
437 }
438 
440 {
441  _p->registration_ID.assign(ID.data(), ID.size());
442 }
443 
444 void TreeNode::setWakeUpInstance(std::shared_ptr<WakeUpSignal> instance)
445 {
446  _p->wake_up = instance;
447 }
448 
450 {
451  for(const auto& new_it : new_remapping)
452  {
453  auto it = _p->config.input_ports.find(new_it.first);
454  if(it != _p->config.input_ports.end())
455  {
456  it->second = new_it.second;
457  }
458  it = _p->config.output_ports.find(new_it.first);
459  if(it != _p->config.output_ports.end())
460  {
461  it->second = new_it.second;
462  }
463  }
464 }
465 
466 template <>
467 std::string toStr<PreCond>(const PreCond& pre)
468 {
469  switch(pre)
470  {
471  case PreCond::SUCCESS_IF:
472  return "_successIf";
473  case PreCond::FAILURE_IF:
474  return "_failureIf";
475  case PreCond::SKIP_IF:
476  return "_skipIf";
477  case PreCond::WHILE_TRUE:
478  return "_while";
479  default:
480  return "Undefined";
481  }
482 }
483 
484 template <>
485 std::string toStr<PostCond>(const PostCond& pre)
486 {
487  switch(pre)
488  {
490  return "_onSuccess";
492  return "_onFailure";
493  case PostCond::ALWAYS:
494  return "_post";
495  case PostCond::ON_HALTED:
496  return "_onHalted";
497  default:
498  return "Undefined";
499  }
500 }
501 
503 {
504  if(auto remapped_key = getRemappedKey(key, getRawPortValue(key)))
505  {
506  return _p->config.blackboard->getAnyLocked(std::string(*remapped_key));
507  }
508  return {};
509 }
510 
511 } // namespace BT
cx::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: wildcards.hpp:636
BT
Definition: ex01_wrap_legacy.cpp:29
BT::PostCond::ON_SUCCESS
@ ON_SUCCESS
BT::TreeNode::PImpl::post_parsed
std::array< ScriptFunction, size_t(PostCond::COUNT_)> post_parsed
Definition: tree_node.cpp:50
BT::TreeNode::PImpl::pre_tick_callback
PreTickCallback pre_tick_callback
Definition: tree_node.cpp:41
BT::TreeNode::UID
uint16_t UID() const
Definition: tree_node.cpp:336
BT::TreeNode::requiresWakeUp
bool requiresWakeUp() const
Definition: tree_node.cpp:434
BT::PreCond
PreCond
Definition: tree_node.h:45
BT::PreCond::FAILURE_IF
@ FAILURE_IF
BT::TreeNode::PImpl::tick_monitor_callback
TickMonitorCallback tick_monitor_callback
Definition: tree_node.cpp:43
BT::PreCond::SUCCESS_IF
@ SUCCESS_IF
BT::TreeNode::config
const NodeConfig & config() const
Definition: tree_node.cpp:351
BT::TreeNode::emitWakeUpSignal
void emitWakeUpSignal()
Notify that the tree should be ticked again()
Definition: tree_node.cpp:426
BT::TreeNode::StatusChangeSubscriber
StatusChangeSignal::Subscriber StatusChangeSubscriber
Definition: tree_node.h:162
BT::PostCond::ON_FAILURE
@ ON_FAILURE
BT::TreeNode::TickMonitorCallback
std::function< void(TreeNode &, NodeStatus, std::chrono::microseconds)> TickMonitorCallback
Definition: tree_node.h:168
BT::Expected
nonstd::expected< T, std::string > Expected
Definition: basic_types.h:85
BT::TreeNode::executeTick
virtual BT::NodeStatus executeTick()
The method that should be used to invoke tick() and setStatus();.
Definition: tree_node.cpp:71
BT::StringView
std::string_view StringView
Definition: basic_types.h:59
BT::TreeNode::PImpl::status
NodeStatus status
Definition: tree_node.cpp:29
BT::ScriptFunction
std::function< Any(Ast::Environment &env)> ScriptFunction
Definition: script_parser.hpp:43
BT::PreCond::COUNT_
@ COUNT_
BT::TreeNode::setPreTickFunction
void setPreTickFunction(PreTickCallback callback)
Definition: tree_node.cpp:318
BT::TreeNode
Abstract base class for Behavior Tree Nodes.
Definition: tree_node.h:118
BT::TreeNode::PImpl::PImpl
PImpl(std::string name, NodeConfig config)
Definition: tree_node.cpp:23
BT::TreeNode::PImpl::name
const std::string name
Definition: tree_node.cpp:27
BT::TreeNode::PImpl::pre_parsed
std::array< ScriptFunction, size_t(PreCond::COUNT_)> pre_parsed
Definition: tree_node.cpp:49
BT::TreeNode::PImpl::state_condition_variable
std::condition_variable state_condition_variable
Definition: tree_node.cpp:31
BT::TreeNode::PreTickCallback
std::function< NodeStatus(TreeNode &)> PreTickCallback
Definition: tree_node.h:165
mutex
static pthread_mutex_t mutex
Definition: minitrace.cpp:77
BT::TreeNode::registrationName
const std::string & registrationName() const
registrationName is the ID used by BehaviorTreeFactory to create an instance.
Definition: tree_node.cpp:346
BT::TreeNode::PostScripts
std::array< ScriptFunction, size_t(PostCond::COUNT_)> PostScripts
Definition: tree_node.h:395
lexy::callback
constexpr auto callback(Fns &&... fns)
Creates a callback.
Definition: adapter.hpp:48
BT::NodeConfig::blackboard
Blackboard::Ptr blackboard
Definition: tree_node.h:79
BT::TreeNode::status
NodeStatus status() const
Definition: tree_node.cpp:285
BT::TreeNode::setPostTickFunction
void setPostTickFunction(PostTickCallback callback)
Definition: tree_node.cpp:324
BT::TreeNode::waitValidStatus
BT::NodeStatus waitValidStatus()
Definition: tree_node.cpp:291
BT::NodeStatus::FAILURE
@ FAILURE
BT::TreeNode::setStatus
void setStatus(NodeStatus new_status)
setStatus changes the status of the node. it will throw if you try to change the status to IDLE,...
Definition: tree_node.cpp:160
BT::NodeStatus::SKIPPED
@ SKIPPED
BT::TreeNode::setTickMonitorCallback
void setTickMonitorCallback(TickMonitorCallback callback)
Definition: tree_node.cpp:330
BT::TreeNode::getRawPortValue
StringView getRawPortValue(const std::string &key) const
Definition: tree_node.cpp:361
BT::TreeNode::PImpl::state_mutex
std::mutex state_mutex
Definition: tree_node.cpp:33
BT::TreeNode::halt
virtual void halt()=0
BT::TreeNode::PImpl::registration_ID
std::string registration_ID
Definition: tree_node.cpp:39
BT::TreeNode::haltNode
void haltNode()
Definition: tree_node.cpp:148
BT::PostCond
PostCond
Definition: tree_node.h:55
BT::toStr< PostCond >
std::string toStr< PostCond >(const PostCond &pre)
Definition: tree_node.cpp:485
BT::RuntimeError
Definition: exceptions.h:58
BT::TreeNode::PImpl
Definition: tree_node.cpp:21
BT::TreeNode::PImpl::config
NodeConfig config
Definition: tree_node.cpp:37
BT::TreeNode::PImpl::state_change_signal
StatusChangeSignal state_change_signal
Definition: tree_node.cpp:35
BT::TreeNode::postConditionsScripts
PostScripts & postConditionsScripts()
Definition: tree_node.cpp:188
BT::TreeNode::getLockedPortContent
AnyPtrLocked getLockedPortContent(const std::string &key)
getLockedPortContent should be used when:
Definition: tree_node.cpp:502
BT::LockedPtr
The LockedPtr class is used to share a pointer to an object and a mutex that protects the read/write ...
Definition: locked_reference.hpp:16
BT::TreeNode::setRegistrationID
void setRegistrationID(StringView ID)
Definition: tree_node.cpp:439
BT::TreeNode::checkPostConditions
void checkPostConditions(NodeStatus status)
Definition: tree_node.cpp:246
BT::StrCat
std::string StrCat()
Definition: strcat.hpp:46
BT::TreeNode::name
const std::string & name() const
Name of the instance, not the type.
Definition: tree_node.cpp:302
BT::TreeNode::tick
virtual BT::NodeStatus tick()=0
Method to be implemented by the user.
BT::TreeNode::modifyPortsRemapping
void modifyPortsRemapping(const PortsRemapping &new_remapping)
Definition: tree_node.cpp:449
BT::NodeStatus::SUCCESS
@ SUCCESS
BT::PreCond::WHILE_TRUE
@ WHILE_TRUE
BT::TreeNode::operator=
TreeNode & operator=(const TreeNode &other)=delete
BT::TreeNode::preConditionsScripts
PreScripts & preConditionsScripts()
Definition: tree_node.cpp:183
BT::PortsRemapping
std::unordered_map< std::string, std::string > PortsRemapping
Definition: tree_node.h:43
BT::TreeNode::getRemappedKey
static Expected< StringView > getRemappedKey(StringView port_name, StringView remapped_port)
Definition: tree_node.cpp:411
BT::TreeNode::setWakeUpInstance
void setWakeUpInstance(std::shared_ptr< WakeUpSignal > instance)
Definition: tree_node.cpp:444
BT::NodeStatus::RUNNING
@ RUNNING
BT::TreeNode::subscribeToStatusChange
StatusChangeSubscriber subscribeToStatusChange(StatusChangeCallback callback)
subscribeToStatusChange is used to attach a callback to a status change. When StatusChangeSubscriber ...
Definition: tree_node.cpp:313
BT::isStatusCompleted
bool isStatusCompleted(const NodeStatus &status)
Definition: basic_types.h:47
BT::PreCond::SKIP_IF
@ SKIP_IF
BT::TreeNode::PImpl::wake_up
std::shared_ptr< WakeUpSignal > wake_up
Definition: tree_node.cpp:47
BT::PostCond::COUNT_
@ COUNT_
BT::PostCond::ALWAYS
@ ALWAYS
BT::TreeNode::TreeNode
TreeNode(std::string name, NodeConfig config)
TreeNode main constructor.
Definition: tree_node.cpp:53
BT::TreeNode::_p
std::unique_ptr< PImpl > _p
Definition: tree_node.h:404
BT::TreeNode::resetStatus
void resetStatus()
Set the status to IDLE.
Definition: tree_node.cpp:268
std
Definition: std.hpp:31
BT::TreeNode::PreScripts
std::array< ScriptFunction, size_t(PreCond::COUNT_)> PreScripts
Definition: tree_node.h:394
BT::TreeNode::fullPath
const std::string & fullPath() const
Definition: tree_node.cpp:341
BT::TreeNode::checkPreConditions
Expected< NodeStatus > checkPreConditions()
Definition: tree_node.cpp:193
BT::TreeNode::~TreeNode
virtual ~TreeNode()
Definition: tree_node.cpp:68
BT::TreeNode::PostTickCallback
std::function< NodeStatus(TreeNode &, NodeStatus)> PostTickCallback
Definition: tree_node.h:166
BT::NodeStatus::IDLE
@ IDLE
BT::TreeNode::isHalted
bool isHalted() const
Definition: tree_node.cpp:307
BT::TreeNode::PImpl::callback_injection_mutex
std::mutex callback_injection_mutex
Definition: tree_node.cpp:45
BT::toStr< PreCond >
std::string toStr< PreCond >(const PreCond &pre)
Definition: tree_node.cpp:467
BT::PostCond::ON_HALTED
@ ON_HALTED
BT::NodeConfig
Definition: tree_node.h:73
BT::Signal< TimePoint, const TreeNode &, NodeStatus, NodeStatus >
BT::TreeNode::isBlackboardPointer
static bool isBlackboardPointer(StringView str, StringView *stripped_pointer=nullptr)
Check a string and return true if it matches the pattern: {...}.
Definition: tree_node.cpp:375
tree_node.h
BT::NodeConfig::enums
std::shared_ptr< ScriptingEnumsRegistry > enums
Definition: tree_node.h:81
BT::TreeNode::StatusChangeCallback
StatusChangeSignal::CallableFunction StatusChangeCallback
Definition: tree_node.h:163
BT::NodeStatus
NodeStatus
Definition: basic_types.h:33
BT::TreeNode::PImpl::post_tick_callback
PostTickCallback post_tick_callback
Definition: tree_node.cpp:42
BT::TreeNode::stripBlackboardPointer
static StringView stripBlackboardPointer(StringView str)
Definition: tree_node.cpp:401
BT::Ast::Environment
The Environment class is used to encapsulate the information and states needed by the scripting langu...
Definition: script_parser.hpp:31


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Dec 13 2024 03:19:17