#[doc(hidden)]
#[cfg(feature = "std")]
extern crate std;
use super::{error::Error, registry::BehaviorRegistry};
#[cfg(feature = "skip_unless_updated")]
use crate::behavior::decorator::EntryUpdated;
#[cfg(feature = "simple_behavior")]
use crate::behavior::{ComplexBhvrTickFn, SimpleBehavior, SimpleBhvrTickFn};
use crate::{
ConstString,
behavior::{BehaviorExecution, SubTree, behavior_description::BehaviorDescription},
tree::BehaviorTree,
xml::parser::XmlParser,
};
#[allow(unused)]
use crate::{
behavior::{Behavior, BehaviorKind, BehaviorState, action, condition, control, decorator},
port::PortList,
};
#[cfg(feature = "mock_behavior")]
use crate::{
behavior::{MockBehavior, MockBehaviorConfig},
factory::registry::SubstitutionRule,
};
#[allow(unused)]
use alloc::string::String;
use alloc::{boxed::Box, string::ToString, vec::Vec};
use databoard::Databoard;
#[cfg(feature = "mock_behavior")]
use nanoserde::DeJson;
pub struct BehaviorTreeFactory {
registry: BehaviorRegistry,
}
impl BehaviorTreeFactory {
#[must_use]
pub const fn registry(&self) -> &BehaviorRegistry {
&self.registry
}
#[must_use]
pub const fn registry_mut(&mut self) -> &mut BehaviorRegistry {
&mut self.registry
}
#[allow(clippy::too_many_lines)]
pub fn new() -> Result<Box<Self>, Error> {
let mut f = Box::new(Self {
registry: BehaviorRegistry::default(),
});
SubTree::register(&mut f, "SubTree")?;
#[cfg(feature = "always_failure")]
MockBehavior::register_with(&mut f, "AlwaysFailure", MockBehaviorConfig::new(BehaviorState::Failure), true)?;
#[cfg(feature = "always_running")]
MockBehavior::register_with(
&mut f,
"AlwaysRunning",
MockBehaviorConfig::new(BehaviorState::Running),
false,
)?;
#[cfg(feature = "always_success")]
MockBehavior::register_with(&mut f, "AlwaysSuccess", MockBehaviorConfig::new(BehaviorState::Success), true)?;
#[cfg(feature = "pop_bool")]
action::PopFromQueue::<bool>::register(&mut f, "PopBool")?;
#[cfg(feature = "pop_double")]
action::PopFromQueue::<f64>::register(&mut f, "PopDouble")?;
#[cfg(feature = "pop_int")]
action::PopFromQueue::<i32>::register(&mut f, "PopInt")?;
#[cfg(feature = "pop_string")]
action::PopFromQueue::<String>::register(&mut f, "PopString")?;
#[cfg(feature = "script")]
action::Script::register(&mut f, "Script")?;
#[cfg(feature = "set_blackboard")]
action::SetBlackboard::<String>::register_with(&mut f, "SetBlackboard", true)?;
#[cfg(feature = "sleep")]
action::Sleep::register(&mut f, "Sleep")?;
#[cfg(feature = "unset_blackboard")]
action::UnsetBlackboard::<String>::register_with(&mut f, "UnsetBlackboard", true)?;
#[cfg(feature = "script_condition")]
condition::ScriptCondition::register(&mut f, "ScriptCondition")?;
#[cfg(feature = "was_entry_updated")]
condition::WasEntryUpdated::register(&mut f, "WasEntryUpdated")?;
#[cfg(feature = "async_fallback")]
control::Fallback::register_with(&mut f, "AsyncFallback", true)?;
#[cfg(feature = "async_sequence")]
control::Sequence::register_with(&mut f, "AsyncSequence", true)?;
#[cfg(feature = "fallback")]
control::Fallback::register_with(&mut f, "Fallback", false)?;
#[cfg(feature = "if_then_else")]
control::IfThenElse::register(&mut f, "IfThenElse")?;
#[cfg(feature = "parallel_all")]
control::ParallelAll::register(&mut f, "ParallelAll")?;
#[cfg(feature = "parallel")]
control::Parallel::register(&mut f, "Parallel")?;
#[cfg(feature = "reactive_fallback")]
control::ReactiveFallback::register(&mut f, "ReactiveFallback")?;
#[cfg(feature = "reactive_sequence")]
control::ReactiveSequence::register(&mut f, "ReactiveSequence")?;
#[cfg(feature = "sequence")]
control::Sequence::register_with(&mut f, "Sequence", false)?;
#[cfg(feature = "sequence_with_memory")]
control::SequenceWithMemory::register(&mut f, "SequenceWithMemory")?;
#[cfg(feature = "switch2")]
control::Switch::<2>::register(&mut f, "Switch2", true)?;
#[cfg(feature = "switch3")]
control::Switch::<3>::register(&mut f, "Switch3", true)?;
#[cfg(feature = "switch4")]
control::Switch::<4>::register(&mut f, "Switch4", true)?;
#[cfg(feature = "switch5")]
control::Switch::<5>::register(&mut f, "Switch5", true)?;
#[cfg(feature = "switch6")]
control::Switch::<6>::register(&mut f, "Switch6", true)?;
#[cfg(feature = "while_do_else")]
control::WhileDoElse::register(&mut f, "WhileDoElse")?;
#[cfg(feature = "delay")]
decorator::Delay::register(&mut f, "Delay")?;
#[cfg(feature = "force_failure")]
decorator::ForceState::register_with(&mut f, "ForceFailure", BehaviorState::Failure, true)?;
#[cfg(feature = "force_running")]
decorator::ForceState::register_with(&mut f, "ForceRunning", BehaviorState::Running, false)?;
#[cfg(feature = "force_success")]
decorator::ForceState::register_with(&mut f, "ForceSuccess", BehaviorState::Success, true)?;
#[cfg(feature = "inverter")]
decorator::Inverter::register(&mut f, "Inverter")?;
#[cfg(feature = "keep_running_until_failure")]
decorator::KeepRunningUntilFailure::register(&mut f, "KeepRunningUntilFailure")?;
#[cfg(feature = "loop_bool")]
decorator::Loop::<bool>::register_with(&mut f, "LoopBool", false)?;
#[cfg(feature = "loop_double")]
decorator::Loop::<f64>::register_with(&mut f, "LoopDouble", true)?;
#[cfg(feature = "loop_int")]
decorator::Loop::<i32>::register_with(&mut f, "LoopInt", false)?;
#[cfg(feature = "loop_string")]
decorator::Loop::<String>::register_with(&mut f, "LoopString", true)?;
#[cfg(feature = "precondition")]
decorator::Precondition::register(&mut f, "Precondition")?;
#[cfg(feature = "repeat")]
decorator::Repeat::register(&mut f, "Repeat")?;
#[cfg(feature = "retry_until_successful")]
decorator::RetryUntilSuccessful::register(&mut f, "RetryUntilSuccessful")?;
#[cfg(feature = "run_once")]
decorator::RunOnce::register(&mut f, "RunOnce")?;
#[cfg(feature = "timeout")]
decorator::Timeout::register(&mut f, "Timeout")?;
#[cfg(feature = "skip_unless_updated")]
EntryUpdated::register_with(&mut f, "SkipUnlessUpdated", BehaviorState::Skipped, true)?;
#[cfg(feature = "wait_value_updated")]
EntryUpdated::register_with(&mut f, "WaitValueUpdated", BehaviorState::Running, true)?;
Ok(f)
}
pub fn register_enum_tuple(&mut self, key: &str, value: i8) -> Result<(), Error> {
self.registry.register_enum_tuple(key, value)
}
pub fn clear_registered_behavior_trees(&mut self) {
self.registry.clear_registered_trees();
}
pub fn create_from_text(&mut self, xml: &str) -> Result<BehaviorTree, Error> {
self.register_behavior_tree_from_text(xml)?;
self.create_main_tree()
}
pub fn create_main_tree(&mut self) -> Result<BehaviorTree, Error> {
if let Some(name) = self.registry.main_tree_id() {
if name.is_empty() {
self.create_tree("MainTree")
} else {
self.create_tree(&name)
}
} else {
self.create_tree("MainTree")
}
}
pub fn create_tree(&mut self, name: &str) -> Result<BehaviorTree, Error> {
let mut parser = XmlParser::default();
match parser.create_tree_from_definition(name, &self.registry, None) {
Ok(root) => Ok(BehaviorTree::new(root, &self.registry)),
Err(err) => Err(Error::Create {
name: name.into(),
error: err.to_string().into(),
}),
}
}
pub fn create_tree_with(&mut self, name: &str, blackboard: &Databoard) -> Result<BehaviorTree, Error> {
let mut parser = XmlParser::default();
match parser.create_tree_from_definition(name, &self.registry, Some(blackboard)) {
Ok(root) => Ok(BehaviorTree::new(root, &self.registry)),
Err(err) => Err(Error::Create {
name: name.into(),
error: err.to_string().into(),
}),
}
}
#[cfg(feature = "std")]
pub fn list_behaviors(&self) {
self.registry.list_behaviors();
}
pub fn register_behavior_tree_from_text(&mut self, xml: &str) -> Result<(), Error> {
#[cfg(feature = "std")]
{
let dir = std::env::current_dir()?.to_string_lossy().into();
match XmlParser::register_document(&mut self.registry, xml, &dir) {
Ok(()) => Ok(()),
Err(err) => Err(Error::RegisterXml {
name: dir,
error: err.to_string().into(),
}),
}
}
#[cfg(not(feature = "std"))]
{
match XmlParser::register_document(&mut self.registry, xml) {
Ok(()) => Ok(()),
Err(err) => Err(Error::RegisterXml {
name: "inline xml".into(),
error: err.to_string().into(),
}),
}
}
}
#[cfg(feature = "std")]
pub fn register_behavior_tree_from_file(&mut self, file: impl Into<std::path::PathBuf>) -> Result<(), Error> {
let file_path: std::path::PathBuf = file.into();
if let Some(file_dir) = file_path.parent() {
let dir: ConstString = if file_path.is_relative() {
let mut dir = std::env::current_dir()?;
dir.push(file_dir);
dir.to_string_lossy().into()
} else {
file_dir.to_string_lossy().into()
};
let xml: ConstString = std::fs::read_to_string(file_path)?.into();
match XmlParser::register_document(&mut self.registry, xml, &dir) {
Ok(()) => Ok(()),
Err(err) => Err(Error::RegisterXml {
name: dir,
error: err.to_string().into(),
}),
}
} else {
Err(Error::RegisterXml {
name: file_path.to_string_lossy().into(),
error: "filepath without parent".into(),
})
}
}
#[must_use]
pub fn registered_behavior_trees(&self) -> Vec<ConstString> {
self.registry.registered_behavior_trees()
}
#[cfg(feature = "std")]
#[allow(unsafe_code)]
pub fn register_from_plugin(&mut self, name: &str) -> Result<(), Error> {
if let Some(path) = std::env::current_exe()?.parent() {
if let Some(str_path) = path.to_str() {
let path = str_path.trim_end_matches("/deps").to_string();
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
todo!("This plattform is not upported!");
#[cfg(target_os = "linux")]
let libname = path + "/lib" + name + ".so";
#[cfg(target_os = "windows")]
let libname = path + "\\" + name + ".dll";
let lib = unsafe {
let lib = libloading::Library::new(libname)?;
let registration_fn: libloading::Symbol<unsafe extern "Rust" fn(&mut Self) -> u32> =
lib.get(b"register")?;
let res = registration_fn(&mut *self);
if res != 0 {
return Err(Error::RegisterLib {
path: name.into(),
code: res,
});
}
lib
};
self.registry.add_library(lib);
Ok(())
} else {
Err(Error::InvalidPath { path: name.into() })
}
} else {
Err(Error::InvalidPath { path: name.into() })
}
}
#[deprecated(since = "0.7.3", note = "use <T>::create(...)")]
#[allow(deprecated, unused)]
pub fn register_behavior_type<T>(&mut self, name: &str) -> Result<(), Error>
where
T: BehaviorExecution,
{
let bhvr_desc = BehaviorDescription::new(name, name, T::kind(), false, T::provided_ports());
let bhvr_creation_fn = T::creation_fn();
self.registry
.add_behavior(bhvr_desc, bhvr_creation_fn)
}
#[cfg(feature = "simple_behavior")]
pub fn register_simple_function(
&mut self,
name: &str,
tick_fn: SimpleBhvrTickFn,
kind: BehaviorKind,
) -> Result<(), Error> {
let bhvr_desc = BehaviorDescription::new(name, name, kind, false, PortList::default());
let bhvr_creation_fn = SimpleBehavior::create(tick_fn);
self.registry
.add_behavior(bhvr_desc, bhvr_creation_fn)
}
#[cfg(feature = "simple_behavior")]
pub fn register_simple_function_with_ports(
&mut self,
name: &str,
tick_fn: ComplexBhvrTickFn,
kind: BehaviorKind,
port_list: PortList,
) -> Result<(), Error> {
let bhvr_desc = BehaviorDescription::new(name, name, kind, false, port_list.clone());
let bhvr_creation_fn = SimpleBehavior::new_create_with_ports(tick_fn, port_list);
self.registry
.add_behavior(bhvr_desc, bhvr_creation_fn)
}
#[cfg(feature = "mock_behavior")]
#[inline]
pub fn add_substitution_rule(&mut self, pattern: &str, rule: SubstitutionRule) -> Result<(), Error> {
self.registry.add_substitution_rule(pattern, rule)
}
#[cfg(feature = "mock_behavior")]
pub fn load_substitution_rules_from_json(&mut self, json: &str) -> Result<(), Error> {
let json: super::json_config::JsonConfig = DeJson::deserialize_json(json)?;
for (pattern, rule) in json.substitution_rules {
self.add_substitution_rule(&pattern, rule)?;
}
Ok(())
}
#[cfg(feature = "mock_behavior")]
#[inline]
pub fn clear_substitution_rules(&mut self) {
self.registry.clear_substitution_rules();
}
}