#[doc(hidden)]
#[cfg(feature = "std")]
extern crate std;
use alloc::{
collections::btree_map::BTreeMap,
string::{String, ToString},
vec::Vec,
};
use core::convert::TryFrom;
use crate::{
ConstString, ID, NAME, SUBTREE,
behavior::{
behavior_description::BehaviorDescription,
pre_post_conditions::{POST_CONDITIONS, PRE_CONDITIONS},
},
factory::BehaviorTreeFactory,
tree::{BehaviorTree, BehaviorTreeElement, TreeElementKind},
};
use woxml::{Write, XmlWriter};
#[derive(Default)]
pub struct XmlCreator;
impl XmlCreator {
pub fn write_tree_nodes_model(factory: &BehaviorTreeFactory, pretty: bool) -> Result<ConstString, woxml::Error> {
let mut writer = if pretty {
XmlWriter::pretty_mode(Vec::new())
} else {
XmlWriter::compact_mode(Vec::new())
};
writer.begin_elem("root")?;
writer.attr("BTCPP_format", "4")?;
writer.begin_elem("TreeNodesModel")?;
for item in factory.registry().behaviors() {
if !item.1.0.groot2() {
writer.begin_elem(item.1.0.kind_str())?;
writer.attr(ID, item.0)?;
for port in &item.1.0.ports().0 {
writer.begin_elem(port.direction().type_str())?;
writer.attr(NAME, port.name())?;
writer.attr("type", port.type_name())?;
writer.end_elem()?;
}
writer.end_elem()?;
}
}
writer.end_elem()?; writer.end_elem()?; writer.flush()?;
Ok(String::try_from(writer)?.into())
}
pub fn write_tree(
tree: &BehaviorTree,
metadata: bool,
builtin_models: bool,
pretty: bool,
) -> Result<ConstString, woxml::Error> {
let mut writer = if pretty {
XmlWriter::pretty_mode(Vec::new())
} else {
XmlWriter::compact_mode(Vec::new())
};
writer.begin_elem("root")?;
writer.attr("BTCPP_format", "4")?;
let (behaviors, subtrees) = Self::scan_tree(tree, builtin_models);
{
Self::create_behavior_trees(&mut writer, &subtrees, metadata)?;
Self::create_tree_nodes_model(&mut writer, &behaviors, builtin_models, pretty, false)?;
}
writer.end_elem()?; writer.flush()?;
Ok(String::try_from(writer)?.into())
}
fn create_behavior_trees<'a>(
writer: &mut XmlWriter<'a, impl Write>,
subtrees: &'a Vec<&BehaviorTreeElement>,
metadata: bool,
) -> Result<(), woxml::Error> {
for subtree in subtrees {
writer.begin_elem("BehaviorTree")?;
writer.attr(ID, subtree.name())?;
writer.attr("_fullpath", subtree.groot2_path())?;
for element in subtree.children().iter() {
Self::write_subtree(element, writer, metadata)?;
}
writer.end_elem()?; }
Ok(())
}
fn write_subtree<'a>(
element: &'a BehaviorTreeElement,
writer: &mut XmlWriter<'a, impl Write>,
metadata: bool,
) -> Result<(), woxml::Error> {
let is_subtree = match element.kind() {
TreeElementKind::Leaf | TreeElementKind::Node => {
writer.begin_elem(element.id())?;
writer.attr(NAME, element.name())?;
false
}
TreeElementKind::SubTree => {
writer.begin_elem(SUBTREE)?;
writer.attr(ID, element.name())?;
if metadata {
writer.attr("_fullpath", element.groot2_path())?;
}
true
}
};
if metadata {
writer.attr("_uid", &element.uid().to_string())?;
}
if is_subtree {
if let Some(remappings) = element.blackboard().remappings() {
for remapping in remappings.iter() {
writer.attr(&remapping.0, &remapping.1.to_string())?;
}
}
} else {
for remapping in element.remappings().iter() {
writer.attr(&remapping.0, &remapping.1.to_string())?;
}
}
if let Some(conditions) = &element.pre_conditions().0 {
for i in 0..PRE_CONDITIONS.len() {
if let Some(cond) = &conditions[i] {
writer.attr(PRE_CONDITIONS[i], cond)?;
}
}
}
if let Some(conditions) = &element.post_conditions().0 {
for i in 0..POST_CONDITIONS.len() {
if let Some(cond) = &conditions[i] {
writer.attr(POST_CONDITIONS[i], cond)?;
}
}
}
if !is_subtree {
for element in element.children().iter() {
Self::write_subtree(element, writer, metadata)?;
}
}
writer.end_elem()?;
Ok(())
}
fn create_tree_nodes_model<'a>(
writer: &mut XmlWriter<'a, impl Write>,
behaviors: &'a BTreeMap<ConstString, BehaviorDescription>,
builtin_models: bool,
pretty: bool,
groot: bool,
) -> Result<(), woxml::Error> {
writer.begin_elem("TreeNodesModel")?;
for (name, item) in behaviors {
if builtin_models || !item.groot2() {
writer.begin_elem(item.kind_str())?;
writer.attr(ID, name)?;
for port in &item.ports().0 {
writer.begin_elem(port.direction().type_str())?;
writer.attr(NAME, port.name())?;
if groot {
writer.attr("type", Self::groot_map_types(port.type_name()))?;
} else {
writer.attr("type", port.type_name())?;
}
if !port.description().is_empty() {
writer.set_compact_mode();
writer.text(port.description())?;
}
writer.end_elem()?;
if pretty {
writer.set_pretty_mode();
}
}
writer.end_elem()?;
}
}
writer.end_elem()?;
Ok(())
}
fn scan_tree(
tree: &BehaviorTree,
builtin_models: bool,
) -> (BTreeMap<ConstString, BehaviorDescription>, Vec<&BehaviorTreeElement>) {
let mut behaviors: BTreeMap<ConstString, BehaviorDescription> = BTreeMap::new();
let mut subtrees: Vec<&BehaviorTreeElement> = Vec::new();
for item in tree.iter() {
match item.kind() {
TreeElementKind::Leaf | TreeElementKind::Node => {
let desc = item.data().description();
if builtin_models || !desc.groot2() {
behaviors.insert(desc.name().clone(), desc.clone());
}
}
TreeElementKind::SubTree => {
subtrees.push(item);
}
}
}
(behaviors, subtrees)
}
pub fn groot_write_tree(tree: &BehaviorTree) -> Result<bytes::Bytes, woxml::Error> {
let mut writer = XmlWriter::compact_mode(bytes::BytesMut::new());
writer.begin_elem("root")?;
writer.attr("BTCPP_format", "4")?;
let (behaviors, subtrees) = Self::scan_tree(tree, false);
{
Self::create_behavior_trees(&mut writer, &subtrees, true)?;
Self::create_tree_nodes_model(&mut writer, &behaviors, false, false, true)?;
}
writer.end_elem()?; writer.flush()?;
Ok(String::try_from(writer)?.into())
}
fn groot_map_types(input: &str) -> &str {
match input {
"char" => "char",
"i16" => "short",
"u16" => "unsigned short",
"i32" => "int",
"u32" => "unsigned int",
"i64" => "long",
"u64" => "unsigned long",
"f32" => "float",
"f64" => "double",
"String" => "std::string",
"BehaviorState" => "BT::NodeStatus",
_ => "BT::Any",
}
}
}