#[cfg(feature = "std")]
extern crate std;
use crate::{
ConstString, Mutex,
behavior::{BehaviorState, behavior_data::BehaviorData},
tree::tree::BehaviorTree,
};
use alloc::{sync::Arc, vec::Vec};
#[cfg(feature = "std")]
use std::time::Instant;
#[derive(Clone)]
pub struct Statistics {
pub last_result: BehaviorState,
pub current_state: BehaviorState,
pub transitions_count: usize,
pub success_count: usize,
pub failure_count: usize,
pub skip_count: usize,
#[cfg(feature = "std")]
pub timestamp: Instant,
}
impl Default for Statistics {
fn default() -> Self {
Self {
last_result: BehaviorState::default(),
current_state: BehaviorState::default(),
transitions_count: Default::default(),
success_count: Default::default(),
failure_count: Default::default(),
skip_count: Default::default(),
#[cfg(feature = "std")]
timestamp: Instant::now(),
}
}
}
impl Statistics {
fn reset(&mut self) {
self.last_result = BehaviorState::default();
self.current_state = BehaviorState::default();
self.transitions_count = Default::default();
self.success_count = Default::default();
self.failure_count = Default::default();
self.skip_count = Default::default();
#[cfg(feature = "std")]
{
self.timestamp = Instant::now();
}
}
}
pub struct BehaviorTreeObserver {
statistics: Arc<Mutex<Vec<Statistics>>>,
}
impl BehaviorTreeObserver {
pub fn new(root: &mut BehaviorTree) -> Self {
let id: ConstString = "statistics".into();
let statistics: Arc<Mutex<Vec<Statistics>>> = Arc::new(Mutex::new(Vec::new()));
for element in root.iter_mut() {
statistics.lock().push(Statistics::default());
let statistics_clone: Arc<Mutex<Vec<Statistics>>> = statistics.clone();
let callback = move |behavior: &BehaviorData, new_state: &mut BehaviorState| {
let mut stats = statistics_clone.lock();
let entry = &mut stats[behavior.uid() as usize];
entry.transitions_count += 1;
match new_state {
BehaviorState::Failure => {
entry.failure_count += 1;
entry.last_result = *new_state;
}
BehaviorState::Idle | BehaviorState::Running => {}
BehaviorState::Skipped => entry.skip_count += 1,
BehaviorState::Success => {
entry.success_count += 1;
entry.last_result = *new_state;
}
}
entry.current_state = *new_state;
#[cfg(feature = "std")]
{
entry.timestamp = Instant::now();
}
drop(stats);
};
element.add_pre_state_change_callback(id.clone(), callback);
}
Self { statistics }
}
#[must_use]
pub fn get_statistics(&self, uid: u16) -> Option<Statistics> {
if self.statistics.lock().len() >= uid as usize {
return Some((self.statistics.lock()[uid as usize]).clone());
}
None
}
pub fn reset(&self) {
for stats in &mut (*self.statistics.lock()) {
stats.reset();
}
}
}