use crate::{
self as behaviortree, Control,
behavior::{Behavior, BehaviorData, BehaviorError, BehaviorResult, BehaviorState},
input_port,
port::PortList,
port_list,
tree::BehaviorTreeElementList,
};
use alloc::boxed::Box;
use alloc::collections::btree_set::BTreeSet;
use tinyscript::SharedRuntime;
const MAX_FAILURES: &str = "max_failures";
#[derive(Control, Debug, Default)]
#[behavior(groot2)]
pub struct ParallelAll {
failure_count: i32,
completed_list: BTreeSet<usize>,
}
#[async_trait::async_trait]
impl Behavior for ParallelAll {
fn on_halt(&mut self) -> Result<(), BehaviorError> {
self.failure_count = 0;
self.completed_list.clear();
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
fn on_start(
&mut self,
behavior: &mut BehaviorData,
children: &mut BehaviorTreeElementList,
_runtime: &SharedRuntime,
) -> Result<(), BehaviorError> {
let failure_threshold = behavior.get(MAX_FAILURES).unwrap_or(-1);
self.failure_count = 0;
if (children.len() as i32) < failure_threshold {
return Err(BehaviorError::Composition {
txt: "Number of children is less than the threshold. Can never fail.".into(),
});
}
behavior.set_state(BehaviorState::Running);
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
async fn tick(
&mut self,
behavior: &mut BehaviorData,
children: &mut BehaviorTreeElementList,
runtime: &SharedRuntime,
) -> BehaviorResult {
let failure_threshold = behavior.get(MAX_FAILURES).unwrap_or(-1);
let children_count = children.len();
let mut skipped_count = 0;
for i in 0..children_count {
if self.completed_list.contains(&i) {
continue;
}
let state = children[i].tick(runtime).await?;
match state {
BehaviorState::Success => {
self.completed_list.insert(i);
}
BehaviorState::Failure => {
self.completed_list.insert(i);
self.failure_count += 1;
}
BehaviorState::Skipped => skipped_count += 1,
BehaviorState::Running => {}
BehaviorState::Idle => {
return Err(BehaviorError::State {
behavior: "ParallelAll".into(),
state,
});
}
}
}
if skipped_count == children_count {
return Ok(BehaviorState::Skipped);
}
let sum = skipped_count + self.completed_list.len();
if sum >= children_count {
let state = if (failure_threshold >= 0) && (self.failure_count > failure_threshold) {
BehaviorState::Failure
} else {
BehaviorState::Success
};
children.halt(runtime)?;
self.completed_list.clear();
return Ok(state);
}
Ok(BehaviorState::Running)
}
fn provided_ports() -> PortList {
port_list![input_port!(i32, MAX_FAILURES)]
}
}