use alloc::{boxed::Box, collections::btree_set::BTreeSet};
use behaviortree_derive::Control;
use dataport::{PortArray, PortCollectionAccessors, create_inbound_entry_parseable};
use tinyscript::SharedRuntime;
use crate::{
self as behaviortree_core, BehaviorResult, port_array_init,
tree::BehaviorTreeElementList,
{behavior_data::BehaviorData, behavior_state::BehaviorState, error::Error},
};
#[derive(Control, Debug)]
#[behavior(groot2)]
pub struct Parallel {
success_count: i32,
failure_count: i32,
completed_list: BTreeSet<usize>,
#[behavior(portlist)]
portlist: PortArray<2>,
}
const SUCCESS_COUNT: &str = "success_count";
const FAILURE_COUNT: &str = "failure_count";
#[async_trait::async_trait]
impl crate::behavior_traits::Behavior for Parallel {
fn on_halt(&mut self) -> Result<(), Error> {
self.completed_list.clear();
self.success_count = 0;
self.failure_count = 0;
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<(), Error> {
let children_count = children.len();
let success_threshold = self.portlist.get(SUCCESS_COUNT).unwrap_or(-1);
if (children_count as i32) < success_threshold {
return Err(Error::Composition {
txt: "Number of children is less than success threshold. Can never succeed.".into(),
});
}
let failure_threshold = self.portlist.get(FAILURE_COUNT).unwrap_or(-1);
if (children_count as i32) < failure_threshold {
return Err(Error::Composition {
txt: "Number of children is less than failure threshold. Can never fail.".into(),
});
}
behavior.set_state(BehaviorState::Running);
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
#[allow(clippy::set_contains_or_insert)]
async fn tick(
&mut self,
_behavior: &mut BehaviorData,
children: &mut BehaviorTreeElementList,
runtime: &SharedRuntime,
) -> BehaviorResult {
let success_threshold = self.portlist.get(SUCCESS_COUNT).unwrap_or(-1);
let failure_threshold = self.portlist.get(FAILURE_COUNT).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) {
let child = &mut children[i];
match child.tick(runtime).await? {
BehaviorState::Failure => {
self.completed_list.insert(i);
self.failure_count += 1;
}
BehaviorState::Idle => {
return Err(Error::State {
behavior: "Parallel".into(),
state: BehaviorState::Idle,
});
}
BehaviorState::Running => {}
BehaviorState::Skipped => skipped_count += 1,
BehaviorState::Success => {
self.completed_list.insert(i);
self.success_count += 1;
}
}
}
let sum = self.failure_count + self.success_count + skipped_count;
if sum >= children_count as i32 {
let state = if skipped_count == children_count as i32 {
BehaviorState::Skipped
} else if failure_threshold <= 0 && success_threshold <= 0 {
BehaviorState::Success
} else if failure_threshold <= 0 {
if self.success_count >= success_threshold {
BehaviorState::Success
} else {
BehaviorState::Failure
}
} else if (self.failure_count > failure_threshold) || (self.success_count < success_threshold) {
BehaviorState::Failure
} else {
BehaviorState::Success
};
self.completed_list.clear();
self.success_count = 0;
self.failure_count = 0;
children.halt(runtime)?;
return Ok(state);
}
}
if skipped_count == children_count as i32 {
return Ok(BehaviorState::Skipped);
}
let sum = skipped_count + self.completed_list.len() as i32;
if sum >= children_count as i32 {
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)
}
}
impl Parallel {
port_array_init! {
2,
create_inbound_entry_parseable!(SUCCESS_COUNT, i32, -1),
create_inbound_entry_parseable!(FAILURE_COUNT, i32, -1),
}
}