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  auto t1 = steady_clock::now();
110  new_status = tick();
111  auto t2 = steady_clock::now();
112  if(monitor_tick)
113  {
114  monitor_tick(*this, new_status, duration_cast<microseconds>(t2 - t1));
115  }
116  }
117  }
118 
119  // injected post callback
120  if(isStatusCompleted(new_status))
121  {
122  checkPostConditions(new_status);
123  }
124 
125  if(post_tick)
126  {
127  auto override_status = post_tick(*this, new_status);
128  if(isStatusCompleted(override_status))
129  {
130  new_status = override_status;
131  }
132  }
133 
134  // preserve the IDLE state if skipped, but communicate SKIPPED to parent
135  if(new_status != NodeStatus::SKIPPED)
136  {
137  setStatus(new_status);
138  }
139  return new_status;
140 }
141 
143 {
144  halt();
145 
146  const auto& parse_executor = _p->post_parsed[size_t(PostCond::ON_HALTED)];
147  if(parse_executor)
148  {
150  parse_executor(env);
151  }
152 }
153 
155 {
156  if(new_status == NodeStatus::IDLE)
157  {
158  throw RuntimeError("Node [", name(),
159  "]: you are not allowed to set manually the status to IDLE. "
160  "If you know what you are doing (?) use resetStatus() instead.");
161  }
162 
163  NodeStatus prev_status;
164  {
165  std::unique_lock<std::mutex> UniqueLock(_p->state_mutex);
166  prev_status = _p->status;
167  _p->status = new_status;
168  }
169  if(prev_status != new_status)
170  {
171  _p->state_condition_variable.notify_all();
172  _p->state_change_signal.notify(std::chrono::high_resolution_clock::now(), *this,
173  prev_status, new_status);
174  }
175 }
176 
178 {
179  return _p->pre_parsed;
180 }
181 
183 {
184  return _p->post_parsed;
185 }
186 
188 {
190 
191  // check the pre-conditions
192  for(size_t index = 0; index < size_t(PreCond::COUNT_); index++)
193  {
194  const auto& parse_executor = _p->pre_parsed[index];
195  if(!parse_executor)
196  {
197  continue;
198  }
199 
200  const PreCond preID = PreCond(index);
201 
202  // Some preconditions are applied only when the node state is IDLE or SKIPPED
203  if(_p->status == NodeStatus::IDLE || _p->status == NodeStatus::SKIPPED)
204  {
205  // what to do if the condition is true
206  if(parse_executor(env).cast<bool>())
207  {
208  if(preID == PreCond::FAILURE_IF)
209  {
210  return NodeStatus::FAILURE;
211  }
212  else if(preID == PreCond::SUCCESS_IF)
213  {
214  return NodeStatus::SUCCESS;
215  }
216  else if(preID == PreCond::SKIP_IF)
217  {
218  return NodeStatus::SKIPPED;
219  }
220  }
221  // if the conditions is false
222  else if(preID == PreCond::WHILE_TRUE)
223  {
224  return NodeStatus::SKIPPED;
225  }
226  }
227  else if(_p->status == NodeStatus::RUNNING && preID == PreCond::WHILE_TRUE)
228  {
229  // what to do if the condition is false
230  if(!parse_executor(env).cast<bool>())
231  {
232  haltNode();
233  return NodeStatus::SKIPPED;
234  }
235  }
236  }
237  return nonstd::make_unexpected(""); // no precondition
238 }
239 
241 {
242  auto ExecuteScript = [this](const PostCond& cond) {
243  const auto& parse_executor = _p->post_parsed[size_t(cond)];
244  if(parse_executor)
245  {
247  parse_executor(env);
248  }
249  };
250 
252  {
253  ExecuteScript(PostCond::ON_SUCCESS);
254  }
255  else if(status == NodeStatus::FAILURE)
256  {
257  ExecuteScript(PostCond::ON_FAILURE);
258  }
259  ExecuteScript(PostCond::ALWAYS);
260 }
261 
263 {
264  NodeStatus prev_status;
265  {
266  std::unique_lock<std::mutex> lock(_p->state_mutex);
267  prev_status = _p->status;
268  _p->status = NodeStatus::IDLE;
269  }
270 
271  if(prev_status != NodeStatus::IDLE)
272  {
273  _p->state_condition_variable.notify_all();
274  _p->state_change_signal.notify(std::chrono::high_resolution_clock::now(), *this,
275  prev_status, NodeStatus::IDLE);
276  }
277 }
278 
280 {
281  std::lock_guard<std::mutex> lock(_p->state_mutex);
282  return _p->status;
283 }
284 
286 {
287  std::unique_lock<std::mutex> lock(_p->state_mutex);
288 
289  while(isHalted())
290  {
291  _p->state_condition_variable.wait(lock);
292  }
293  return _p->status;
294 }
295 
296 const std::string& TreeNode::name() const
297 {
298  return _p->name;
299 }
300 
301 bool TreeNode::isHalted() const
302 {
303  return _p->status == NodeStatus::IDLE;
304 }
305 
308 {
309  return _p->state_change_signal.subscribe(std::move(callback));
310 }
311 
313 {
314  std::unique_lock lk(_p->callback_injection_mutex);
315  _p->pre_tick_callback = callback;
316 }
317 
319 {
320  std::unique_lock lk(_p->callback_injection_mutex);
321  _p->post_tick_callback = callback;
322 }
323 
325 {
326  std::unique_lock lk(_p->callback_injection_mutex);
327  _p->tick_monitor_callback = callback;
328 }
329 
330 uint16_t TreeNode::UID() const
331 {
332  return _p->config.uid;
333 }
334 
335 const std::string& TreeNode::fullPath() const
336 {
337  return _p->config.path;
338 }
339 
340 const std::string& TreeNode::registrationName() const
341 {
342  return _p->registration_ID;
343 }
344 
346 {
347  return _p->config;
348 }
349 
351 {
352  return _p->config;
353 }
354 
355 StringView TreeNode::getRawPortValue(const std::string& key) const
356 {
357  auto remap_it = _p->config.input_ports.find(key);
358  if(remap_it == _p->config.input_ports.end())
359  {
360  remap_it = _p->config.output_ports.find(key);
361  if(remap_it == _p->config.output_ports.end())
362  {
363  throw std::logic_error(StrCat("[", key, "] not found"));
364  }
365  }
366  return remap_it->second;
367 }
368 
370 {
371  if(str.size() < 3)
372  {
373  return false;
374  }
375  // strip leading and following spaces
376  size_t front_index = 0;
377  size_t last_index = str.size() - 1;
378  while(str[front_index] == ' ' && front_index <= last_index)
379  {
380  front_index++;
381  }
382  while(str[last_index] == ' ' && front_index <= last_index)
383  {
384  last_index--;
385  }
386  const auto size = (last_index - front_index) + 1;
387  auto valid = size >= 3 && str[front_index] == '{' && str[last_index] == '}';
388  if(valid && stripped_pointer)
389  {
390  *stripped_pointer = StringView(&str[front_index + 1], size - 2);
391  }
392  return valid;
393 }
394 
396 {
397  StringView out;
398  if(isBlackboardPointer(str, &out))
399  {
400  return out;
401  }
402  return {};
403 }
404 
406  StringView remapped_port)
407 {
408  if(remapped_port == "{=}" || remapped_port == "=")
409  {
410  return { port_name };
411  }
412  StringView stripped;
413  if(isBlackboardPointer(remapped_port, &stripped))
414  {
415  return { stripped };
416  }
417  return nonstd::make_unexpected("Not a blackboard pointer");
418 }
419 
421 {
422  if(_p->wake_up)
423  {
424  _p->wake_up->emitSignal();
425  }
426 }
427 
429 {
430  return bool(_p->wake_up);
431 }
432 
434 {
435  _p->registration_ID.assign(ID.data(), ID.size());
436 }
437 
438 void TreeNode::setWakeUpInstance(std::shared_ptr<WakeUpSignal> instance)
439 {
440  _p->wake_up = instance;
441 }
442 
444 {
445  for(const auto& new_it : new_remapping)
446  {
447  auto it = _p->config.input_ports.find(new_it.first);
448  if(it != _p->config.input_ports.end())
449  {
450  it->second = new_it.second;
451  }
452  it = _p->config.output_ports.find(new_it.first);
453  if(it != _p->config.output_ports.end())
454  {
455  it->second = new_it.second;
456  }
457  }
458 }
459 
460 template <>
461 std::string toStr<PreCond>(const PreCond& pre)
462 {
463  switch(pre)
464  {
465  case PreCond::SUCCESS_IF:
466  return "_successIf";
467  case PreCond::FAILURE_IF:
468  return "_failureIf";
469  case PreCond::SKIP_IF:
470  return "_skipIf";
471  case PreCond::WHILE_TRUE:
472  return "_while";
473  default:
474  return "Undefined";
475  }
476 }
477 
478 template <>
479 std::string toStr<PostCond>(const PostCond& pre)
480 {
481  switch(pre)
482  {
484  return "_onSuccess";
486  return "_onFailure";
487  case PostCond::ALWAYS:
488  return "_post";
489  case PostCond::ON_HALTED:
490  return "_onHalted";
491  default:
492  return "Undefined";
493  }
494 }
495 
497 {
498  if(auto remapped_key = getRemappedKey(key, getRawPortValue(key)))
499  {
500  return _p->config.blackboard->getAnyLocked(std::string(*remapped_key));
501  }
502  return {};
503 }
504 
505 } // 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:330
BT::TreeNode::requiresWakeUp
bool requiresWakeUp() const
Definition: tree_node.cpp:428
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:345
BT::TreeNode::emitWakeUpSignal
void emitWakeUpSignal()
Notify that the tree should be ticked again()
Definition: tree_node.cpp:420
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:312
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:340
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:21
BT::NodeConfig::blackboard
Blackboard::Ptr blackboard
Definition: tree_node.h:79
BT::TreeNode::status
NodeStatus status() const
Definition: tree_node.cpp:279
BT::TreeNode::setPostTickFunction
void setPostTickFunction(PostTickCallback callback)
Definition: tree_node.cpp:318
BT::TreeNode::waitValidStatus
BT::NodeStatus waitValidStatus()
Definition: tree_node.cpp:285
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:154
BT::NodeStatus::SKIPPED
@ SKIPPED
BT::TreeNode::setTickMonitorCallback
void setTickMonitorCallback(TickMonitorCallback callback)
Definition: tree_node.cpp:324
BT::TreeNode::getRawPortValue
StringView getRawPortValue(const std::string &key) const
Definition: tree_node.cpp:355
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:142
BT::PostCond
PostCond
Definition: tree_node.h:55
BT::toStr< PostCond >
std::string toStr< PostCond >(const PostCond &pre)
Definition: tree_node.cpp:479
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:182
BT::TreeNode::getLockedPortContent
AnyPtrLocked getLockedPortContent(const std::string &key)
getLockedPortContent should be used when:
Definition: tree_node.cpp:496
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:433
BT::TreeNode::checkPostConditions
void checkPostConditions(NodeStatus status)
Definition: tree_node.cpp:240
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:296
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:443
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:177
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:405
BT::TreeNode::setWakeUpInstance
void setWakeUpInstance(std::shared_ptr< WakeUpSignal > instance)
Definition: tree_node.cpp:438
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:307
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:262
std
Definition: std.hpp:30
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:335
BT::TreeNode::checkPreConditions
Expected< NodeStatus > checkPreConditions()
Definition: tree_node.cpp:187
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:301
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:461
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:369
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:395
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 Jun 28 2024 02:20:08