#[doc(hidden)]
#[cfg(feature = "std")]
extern crate std;
use crate::Mutex;
#[cfg(feature = "std")]
use crate::tree::observer::groot2_connector::{GROOT_STATE, Groot2ConnectorData, attach_groot_callback};
use crate::{
behavior::{BehaviorError, BehaviorResult, BehaviorState},
factory::BehaviorRegistry,
tree::{
tree_element::TreeElementKind,
tree_iter::{TreeIter, TreeIterMut},
},
};
#[cfg(feature = "std")]
use alloc::vec::Vec;
use alloc::{string::String, sync::Arc};
use databoard::Databoard;
#[cfg(feature = "std")]
use libloading::Library;
#[cfg(feature = "std")]
use thingbuf::mpsc;
use tinyscript::SharedRuntime;
#[cfg(feature = "std")]
use uuid::Uuid;
use super::{error::Error, tree_element::BehaviorTreeElement};
fn print_recursively(level: i8, behavior: &BehaviorTreeElement) -> Result<(), Error> {
if level == i8::MAX {
return Err(Error::RecursionLimit {
behavior: behavior.name().clone(),
});
}
let next_level = level + 1;
let mut indentation = String::new();
for _ in 0..level {
indentation.push_str(" ");
}
#[cfg(feature = "std")]
std::println!("{indentation}{}", behavior.name());
for child in &**behavior.children() {
print_recursively(next_level, child)?;
}
Ok(())
}
#[cfg(feature = "std")]
#[derive(Clone, Default)]
pub enum BehaviorTreeMessage {
#[default]
NothingToDo,
AddGrootCallback(Arc<Mutex<Groot2ConnectorData>>),
RemoveAllGrootHooks,
}
pub struct BehaviorTree {
#[cfg(feature = "std")]
uuid: Uuid,
root: BehaviorTreeElement,
runtime: SharedRuntime,
#[cfg(feature = "std")]
_libraries: Vec<Arc<Library>>,
#[cfg(feature = "std")]
tx: mpsc::Sender<BehaviorTreeMessage>,
#[cfg(feature = "std")]
rx: mpsc::Receiver<BehaviorTreeMessage>,
}
impl BehaviorTree {
#[must_use]
pub fn new(root: BehaviorTreeElement, registry: &BehaviorRegistry) -> Self {
let runtime = Arc::new(Mutex::new(registry.runtime().clone()));
#[cfg(feature = "std")]
let mut libraries = Vec::with_capacity(registry.libraries().capacity() + 1);
#[cfg(feature = "std")]
for lib in registry.libraries() {
libraries.push(lib.clone());
}
#[cfg(feature = "std")]
let (tx, rx) = mpsc::channel::<BehaviorTreeMessage>(5);
Self {
#[cfg(feature = "std")]
uuid: Uuid::new_v4(),
root,
runtime,
#[cfg(feature = "std")]
_libraries: libraries,
#[cfg(feature = "std")]
tx,
#[cfg(feature = "std")]
rx,
}
}
#[must_use]
pub const fn blackboard(&self) -> &Databoard {
self.root.data().blackboard()
}
#[inline]
pub fn print(&self) -> Result<(), Error> {
print_recursively(0, &self.root)
}
pub fn subtree(&self, index: usize) -> Result<&BehaviorTreeElement, Error> {
let mut i = 0_usize;
for element in self.iter() {
if matches!(element.kind(), TreeElementKind::SubTree) {
if i == index {
return Ok(element);
}
i += 1;
}
}
Err(Error::SubtreeNotFound { index })
}
#[cfg(feature = "std")]
#[must_use]
pub const fn uuid(&self) -> Uuid {
self.uuid
}
#[cfg(feature = "std")]
#[must_use]
pub fn sender(&self) -> mpsc::Sender<BehaviorTreeMessage> {
self.tx.clone()
}
#[must_use]
pub fn size(&self) -> u16 {
let mut count = 0;
let iter = self.iter();
for _ in iter {
count += 1;
}
count
}
#[cfg(feature = "std")]
fn handle_message(&mut self, message: BehaviorTreeMessage) {
match message {
BehaviorTreeMessage::RemoveAllGrootHooks => {
for element in self.iter_mut() {
element.remove_pre_state_change_callback(&GROOT_STATE.into());
}
}
BehaviorTreeMessage::AddGrootCallback(data) => {
attach_groot_callback(self, data);
}
BehaviorTreeMessage::NothingToDo => {}
}
}
#[inline]
pub async fn tick_exactly_once(&mut self) -> BehaviorResult {
#[cfg(feature = "std")]
if let Ok(message) = self.rx.try_recv() {
self.handle_message(message);
}
self.root.tick(&self.runtime).await
}
#[inline]
pub async fn tick_once(&mut self) -> BehaviorResult {
#[cfg(feature = "std")]
if let Ok(message) = self.rx.try_recv() {
self.handle_message(message);
}
self.root.tick(&self.runtime).await
}
pub async fn tick_while_running(&mut self) -> BehaviorResult {
let mut state = BehaviorState::Running;
while state == BehaviorState::Running || state == BehaviorState::Idle {
#[cfg(feature = "std")]
while let Ok(message) = self.rx.try_recv() {
self.handle_message(message);
}
state = self.root.tick(&self.runtime).await?;
}
#[cfg(feature = "std")]
{
tokio::task::yield_now().await;
}
#[cfg(feature = "std")]
while let Ok(message) = self.rx.try_recv() {
self.handle_message(message);
}
Ok(state)
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &BehaviorTreeElement> {
TreeIter::new(&self.root)
}
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut BehaviorTreeElement> {
TreeIterMut::new(&mut self.root)
}
pub fn reset(&mut self) -> Result<(), BehaviorError> {
self.root.halt(&self.runtime)?;
self.runtime.lock().clear();
Ok(())
}
}