1#[cfg(feature = "groot")]
5use alloc::boxed::Box;
6use alloc::string::String;
7#[cfg(feature = "std")]
8use alloc::vec::Vec;
9
10use databoard::Databoard;
11#[cfg(feature = "groot")]
12use embassy_sync::{
13 blocking_mutex::raw::CriticalSectionRawMutex,
14 channel::{Channel, Sender},
15};
16#[cfg(feature = "std")]
17use libloading::Library;
18use tinyscript::SharedRuntime;
19#[cfg(feature = "std")]
20use uuid::Uuid;
21
22#[cfg(feature = "groot")]
23use crate::tree::observer::groot2::{GROOT_STATE, attach_groot_callback, connector_data::Groot2ConnectorData};
24use crate::{
25 Arc, BehaviorResult, Mutex,
26 behavior_state::BehaviorState,
27 behavior_traits::BehaviorRegistry,
28 tree::{
29 error::Error,
30 tree_element::{BehaviorTreeElement, TreeElementKind},
31 tree_iter::{TreeIter, TreeIterMut},
32 },
33};
34
35#[cfg(feature = "groot")]
36use super::CHANNEL_SIZE;
37
38pub type TreeId = [u8; 16];
40
41fn print_recursively(level: i8, behavior: &BehaviorTreeElement) -> Result<(), Error> {
45 if level == i8::MAX {
46 return Err(Error::RecursionLimit {
47 behavior: behavior.name().clone(),
48 });
49 }
50
51 let next_level = level + 1;
52 let mut indentation = String::new();
53 for _ in 0..level {
54 indentation.push_str(" ");
55 }
56
57 #[cfg(feature = "std")]
58 std::println!("{indentation}{}", behavior.name());
59 for child in &**behavior.children() {
60 print_recursively(next_level, child)?;
61 }
62 Ok(())
63}
64
65#[cfg(feature = "groot")]
66#[derive(Clone, Default)]
67pub enum BehaviorTreeMessage {
68 #[default]
69 NothingToDo,
70 AddGrootCallback(Arc<Mutex<Groot2ConnectorData>>),
71 RemoveAllGrootHooks,
72}
73
74pub struct BehaviorTree {
77 uuid: TreeId,
79 root: BehaviorTreeElement,
81 runtime: SharedRuntime,
83 #[cfg(feature = "groot")]
84 channel: &'static Channel<CriticalSectionRawMutex, BehaviorTreeMessage, CHANNEL_SIZE>,
85 #[cfg(feature = "std")]
88 _libraries: Vec<Arc<Library>>,
89}
90
91impl BehaviorTree {
92 #[must_use]
94 pub fn new(root: BehaviorTreeElement, registry: &impl BehaviorRegistry) -> Self {
95 let runtime = Arc::new(Mutex::new(registry.runtime().clone()));
98 #[cfg(feature = "std")]
100 let mut libraries = Vec::with_capacity(registry.libraries().capacity() + 1);
101 #[cfg(feature = "std")]
102 for lib in registry.libraries() {
103 libraries.push(lib.clone());
104 }
105
106 #[cfg(feature = "groot")]
107 let channel: &'static Channel<CriticalSectionRawMutex, BehaviorTreeMessage, CHANNEL_SIZE> =
108 Box::leak(Box::new(Channel::new()));
109 Self {
110 #[cfg(feature = "std")]
111 uuid: Uuid::new_v4().into_bytes(),
112 #[cfg(not(feature = "std"))]
113 uuid: [0u8; 16], root,
115 runtime,
116 #[cfg(feature = "groot")]
117 channel,
118 #[cfg(feature = "std")]
119 _libraries: libraries,
120 }
121 }
122
123 #[must_use]
125 pub const fn blackboard(&self) -> &Databoard {
126 self.root.data().blackboard()
127 }
128
129 #[must_use]
131 pub const fn blackboard_mut(&mut self) -> &mut Databoard {
132 self.root.data_mut().blackboard_mut()
133 }
134
135 pub fn print(&self) -> Result<(), Error> {
139 print_recursively(0, &self.root)
140 }
141
142 pub fn subtree(&self, index: usize) -> Result<&BehaviorTreeElement, Error> {
146 let mut i = 0_usize;
147 for element in self.iter() {
148 if matches!(element.kind(), TreeElementKind::SubTree) {
149 if i == index {
150 return Ok(element);
151 }
152 i += 1;
153 }
154 }
155 Err(Error::SubtreeNotFound { index })
156 }
157
158 #[must_use]
160 pub const fn uuid(&self) -> &TreeId {
161 &self.uuid
162 }
163
164 #[cfg(feature = "groot")]
172 #[must_use]
173 pub fn sender(&self) -> Sender<'static, CriticalSectionRawMutex, BehaviorTreeMessage, CHANNEL_SIZE> {
174 self.channel.sender()
175 }
176
177 #[must_use]
179 pub fn size(&self) -> u16 {
180 let mut count = 0;
181 let iter = self.iter();
182 for _ in iter {
183 count += 1;
184 }
185 count
186 }
187
188 #[cfg(feature = "groot")]
190 fn handle_message(&mut self, message: BehaviorTreeMessage) {
191 match message {
192 BehaviorTreeMessage::RemoveAllGrootHooks => {
193 for element in self.iter_mut() {
194 element.remove_pre_state_change_callback(GROOT_STATE);
195 }
196 }
197 BehaviorTreeMessage::AddGrootCallback(data) => {
198 attach_groot_callback(self, data);
199 }
200 BehaviorTreeMessage::NothingToDo => {}
201 }
202 }
203
204 pub async fn tick_exactly_once(&mut self) -> BehaviorResult {
207 #[cfg(feature = "groot")]
208 while let Ok(message) = self.channel.try_receive() {
209 self.handle_message(message);
210 }
211 self.root.tick(&self.runtime).await
212 }
213
214 pub async fn tick_once(&mut self) -> BehaviorResult {
218 #[cfg(feature = "groot")]
219 while let Ok(message) = self.channel.try_receive() {
220 self.handle_message(message);
221 }
222 self.root.tick(&self.runtime).await
223 }
224
225 pub async fn tick_while_running(&mut self) -> BehaviorResult {
228 let mut state = BehaviorState::Running;
229 while state == BehaviorState::Running || state == BehaviorState::Idle {
230 #[cfg(feature = "groot")]
231 while let Ok(message) = self.channel.try_receive() {
232 self.handle_message(message);
233 }
234 state = self.root.tick(&self.runtime).await?;
235
236 #[cfg(feature = "std")]
243 tokio::task::yield_now().await;
244 #[cfg(not(feature = "std"))]
245 embassy_futures::yield_now().await;
246 }
247
248 #[cfg(feature = "groot")]
250 while let Ok(message) = self.channel.try_receive() {
251 self.handle_message(message);
252 }
253 Ok(state)
254 }
255
256 pub fn iter(&self) -> impl Iterator<Item = &BehaviorTreeElement> {
258 TreeIter::new(&self.root)
259 }
260
261 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut BehaviorTreeElement> {
263 TreeIterMut::new(&mut self.root)
264 }
265
266 pub fn reset(&mut self) -> Result<(), crate::error::Error> {
270 self.root.halt(&self.runtime)?;
271 self.runtime.lock().clear();
272 Ok(())
273 }
274}
275
276#[cfg(all(test, feature = "std"))]
277mod tests {
278 use super::*;
279
280 use core::ops::Range;
281
282 use databoard::Databoard;
283
284 use crate::{
285 BehaviorDescription,
286 behavior_data::BehaviorData,
287 behavior_state::BehaviorState,
288 behaviors::mock_behavior::{MockBehavior, MockBehaviorConfig},
289 tree::tree_element::{BehaviorTreeElement, TreeElementKind},
290 };
291
292 struct TestRegistry {
293 runtime: tinyscript::Runtime,
294 libraries: Vec<Arc<Library>>,
295 }
296
297 impl crate::behavior_traits::BehaviorRegistry for TestRegistry {
298 fn add_behavior(
299 &mut self,
300 _: BehaviorDescription,
301 _: alloc::boxed::Box<crate::BehaviorCreationFn>,
302 ) -> Result<(), crate::error::Error> {
303 Ok(())
304 }
305 fn add_tree_defintion(
306 &mut self,
307 _: &str,
308 _: crate::ConstString,
309 _: Range<usize>,
310 ) -> Result<(), crate::error::Error> {
311 Ok(())
312 }
313 fn libraries(&self) -> &Vec<Arc<Library>> {
314 &self.libraries
315 }
316 fn runtime(&self) -> &tinyscript::Runtime {
317 &self.runtime
318 }
319 fn register_enum_tuple(&mut self, _: &str, _: i8) -> Result<(), crate::error::Error> {
320 Ok(())
321 }
322 }
323
324 fn make_success_leaf() -> BehaviorTreeElement {
325 let config = MockBehaviorConfig {
326 return_state: BehaviorState::Success,
327 ..Default::default()
328 };
329 let behavior: crate::BehaviorPtr = alloc::boxed::Box::new(MockBehavior::new(config));
330 let data = BehaviorData::create(0, Databoard::default(), BehaviorDescription::new("leaf", "leaf", false));
331 BehaviorTreeElement::create(TreeElementKind::Leaf, behavior, data)
332 }
333
334 #[test]
337 fn new_with_library_clones_libraries() {
338 let lib = unsafe {
340 Library::new("/usr/lib/x86_64-linux-gnu/libc.so.6")
341 .or_else(|_| Library::new("libc.so.6"))
342 .or_else(|_| Library::new("libc.so"))
343 };
344 if let Ok(loaded_lib) = lib {
345 let registry = TestRegistry {
346 runtime: tinyscript::Runtime::default(),
347 libraries: alloc::vec![Arc::new(loaded_lib)],
348 };
349 let tree = BehaviorTree::new(make_success_leaf(), ®istry);
351 assert_eq!(tree.size(), 1);
352 }
353 }
355
356 #[test]
359 fn test_registry_unused_trait_methods() {
360 let mut registry = TestRegistry {
361 runtime: tinyscript::Runtime::default(),
362 libraries: Vec::new(),
363 };
364 let desc = BehaviorDescription::new("x", "x", false);
365 let creation_fn: alloc::boxed::Box<crate::BehaviorCreationFn> =
366 alloc::boxed::Box::new(|_| alloc::boxed::Box::new(MockBehavior::new(MockBehaviorConfig::default())));
367 let _ = crate::behavior_traits::BehaviorRegistry::add_behavior(&mut registry, desc, creation_fn);
368 let _ = crate::behavior_traits::BehaviorRegistry::add_tree_defintion(&mut registry, "id", "xml".into(), 0..1);
369 let _ = crate::behavior_traits::BehaviorRegistry::register_enum_tuple(&mut registry, "E", 1);
370 }
371
372 #[tokio::test]
376 async fn tick_while_running_post_loop_drain() {
377 let registry = TestRegistry {
378 runtime: tinyscript::Runtime::default(),
379 libraries: Vec::new(),
380 };
381 let mut tree = BehaviorTree::new(make_success_leaf(), ®istry);
382 let sender = tree.sender();
383
384 tokio::spawn(async move {
386 let _ = sender.try_send(BehaviorTreeMessage::NothingToDo);
387 });
388
389 let state = tree.tick_while_running().await.unwrap();
390 assert_eq!(state, BehaviorState::Success);
391 }
392}