use std::default;
use crate::uuid;
use enum_dispatch::enum_dispatch;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
mod consts;
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Clone)]
pub struct GeoPoint
{
pub longitude: f32,
pub latitude: f32,
pub altitude: f32,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub enum Speed
{
#[serde(rename = "fast")]
Fast,
#[serde(rename = "standard")]
#[default]
Standard,
#[serde(rename = "slow")]
Slow,
#[serde(rename = "max-speed")]
MaxSpeed(f32),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "name")]
#[enum_dispatch(NodeTrait)]
pub enum Node
{
#[serde(rename = "noop")]
Noop(Noop),
#[serde(rename = "seq")]
Seq(Seq),
#[serde(rename = "conc")]
Conc(Conc),
#[serde(rename = "move-to")]
MoveTo(MoveTo),
#[serde(rename = "search-area")]
SearchArea(SearchArea),
}
impl Node
{
pub fn to_json_string(&self) -> serde_json::Result<String>
{
serde_json::to_string(self)
}
pub fn from_json_string(def: &String) -> serde_json::Result<Node>
{
serde_json::from_str(def)
}
}
impl Default for Node
{
fn default() -> Self
{
Self::Noop(Noop::default())
}
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct CommonParameters
{
pub node_uuid: uuid::Uuid,
pub execunit: Option<String>,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct MoveToParameters
{
pub waypoint: GeoPoint,
pub speed: Option<Speed>,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct SearchAreaParameters
{
pub area: Vec<GeoPoint>,
pub speed: Option<Speed>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct NoParameters {}
#[allow(private_bounds)]
#[derive(Debug, Clone)]
pub struct NodeBuilder<T: CompositeNode, P: NodeBuilderTrait>
{
t: std::rc::Rc<std::cell::RefCell<T>>,
p: Option<P>,
}
#[derive(Clone)]
pub struct NullNodeBuilder;
trait NodeBuilderTrait: Clone
{
fn append_child(&mut self, node: impl Into<Node>);
}
#[enum_dispatch]
pub trait NodeTrait: Clone + Into<Node> + Default
{
fn common_params_ref(&self) -> &CommonParameters;
fn set_common_params(&mut self, params: CommonParameters);
}
trait CompositeNode: NodeTrait
{
fn append_child(&mut self, node: impl Into<Node>);
}
trait LeafNode: NodeTrait
{
type ParametersType: Clone + Default;
fn set_params(&mut self, params: Self::ParametersType);
}
trait LeafNodeParameters
{
type NodeType: Default + LeafNode + Into<Node>;
}
macro_rules! define_composite_node {
($node_type:ident, $node_enum_type:path) => {
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct $node_type
{
pub common_params: CommonParameters,
pub children: Vec<Node>,
}
impl $node_type
{
#[allow(private_interfaces)]
pub fn build(
common_params: Option<CommonParameters>,
) -> NodeBuilder<$node_type, NullNodeBuilder>
{
let mut t = $node_type::default();
if let Some(common_params) = common_params
{
t.common_params = common_params;
}
NodeBuilder::<$node_type, NullNodeBuilder> {
t: std::rc::Rc::new(std::cell::RefCell::new(t)),
p: None,
}
}
}
impl NodeTrait for $node_type
{
fn common_params_ref(&self) -> &CommonParameters
{
&self.common_params
}
fn set_common_params(&mut self, common_params: CommonParameters)
{
self.common_params = common_params;
}
}
impl CompositeNode for $node_type
{
fn append_child(&mut self, node: impl Into<Node>)
{
self.children.push(node.into());
}
}
};
}
macro_rules! define_leaf_node {
($node_type:ident, $node_parameters_type:ident, $node_enum_type:path) => {
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct $node_type
{
pub common_params: CommonParameters,
pub params: $node_parameters_type,
}
impl NodeTrait for $node_type
{
fn common_params_ref(&self) -> &CommonParameters
{
&self.common_params
}
fn set_common_params(&mut self, common_params: CommonParameters)
{
self.common_params = common_params;
}
}
impl LeafNode for $node_type
{
type ParametersType = $node_parameters_type;
fn set_params(&mut self, params: Self::ParametersType)
{
self.params = params;
}
}
impl LeafNodeParameters for $node_parameters_type
{
type NodeType = $node_type;
}
};
}
define_composite_node!(Seq, Node::Seq);
define_composite_node!(Conc, Node::Conc);
define_leaf_node!(MoveTo, MoveToParameters, Node::MoveTo);
define_leaf_node!(SearchArea, SearchAreaParameters, Node::SearchArea);
define_leaf_node!(Noop, NoParameters, Node::Noop);
pub trait SpeedCompute
{
fn compute_velocity(&self, max_velocity: f32) -> f32;
}
impl SpeedCompute for Option<Speed>
{
fn compute_velocity(&self, max_velocity: f32) -> f32
{
match self
{
Some(v) => match v
{
Speed::Slow => consts::VELOCITY_SLOW_RATIO * max_velocity,
Speed::Standard => consts::VELOCITY_STANDARD_RATIO * max_velocity,
Speed::Fast => consts::VELOCITY_FAST_RATIO * max_velocity,
Speed::MaxSpeed(s) => s.min(max_velocity),
},
None => consts::VELOCITY_STANDARD_RATIO * max_velocity,
}
}
}
#[allow(missing_docs)]
#[inline]
pub fn default<T: Default>() -> T
{
Default::default()
}
impl NodeBuilderTrait for NullNodeBuilder
{
fn append_child(&mut self, _node: impl Into<Node>)
{
panic!("Cannot add child to null builder.");
}
}
impl<T: CompositeNode, P: NodeBuilderTrait> NodeBuilderTrait for NodeBuilder<T, P>
{
fn append_child(&mut self, node: impl Into<Node>)
{
self.t.borrow_mut().append_child(node);
}
}
#[allow(private_bounds)]
impl<T: CompositeNode, P: NodeBuilderTrait> NodeBuilder<T, P>
{
pub fn start<U: CompositeNode>(
self,
cp: Option<CommonParameters>,
) -> NodeBuilder<U, NodeBuilder<T, P>>
{
let mut t = U::default();
if let Some(cp) = cp
{
t.set_common_params(cp);
}
NodeBuilder::<U, NodeBuilder<T, P>> {
t: std::rc::Rc::new(std::cell::RefCell::new(t)),
p: Some(self),
}
}
pub fn end(self) -> P
{
let mut p = self.p.unwrap();
p.append_child(self.t.borrow().to_owned());
p
}
#[allow(private_interfaces)]
pub fn add<U>(self, p: U, cp: Option<CommonParameters>) -> Self
where
U: LeafNodeParameters + Into<<<U as LeafNodeParameters>::NodeType as LeafNode>::ParametersType>,
{
let mut t = U::NodeType::default();
t.set_params(p.into());
if let Some(cp) = cp
{
t.set_common_params(cp);
}
self.t.borrow_mut().append_child(t);
self
}
fn to_t(&self) -> T
{
self.t.borrow().clone()
}
}
impl<T: CompositeNode, U: NodeBuilderTrait> From<NodeBuilder<T, U>> for Node
where
T: Into<Node>,
{
fn from(value: NodeBuilder<T, U>) -> Self
{
value.to_t().into()
}
}
#[cfg(test)]
mod tests
{
use crate::definitions::tst::CommonParameters;
use super::GeoPoint;
macro_rules! get_tst_node {
($value:expr, $variant:path) => {
match $value
{
$variant(x) => x,
_ => panic!("Unexpected TST Node."),
}
};
}
macro_rules! check_speed {
($value:expr, $variant:path) => {
match $value.params.speed.as_ref().unwrap()
{
$variant => (),
_ => panic!("Wrong speed"),
}
};
}
macro_rules! check_max_speed {
($value:expr, $max_speed:expr) => {
match $value.params.speed.as_ref().unwrap()
{
super::Speed::MaxSpeed(v) => assert_eq!(*v, $max_speed),
_ => panic!("Wrong speed"),
}
};
}
fn mgp(longitude: f32, latitude: f32, altitude: f32) -> GeoPoint
{
GeoPoint {
longitude: longitude,
latitude: latitude,
altitude: altitude,
}
}
#[test]
fn parse_test_tst()
{
let mut d = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("data/test_tst.json");
let message: String = std::fs::read_to_string(d).unwrap();
let node: super::Node = serde_json::from_str(&message).unwrap();
let node_seq = get_tst_node!(node, super::Node::Seq);
let seq_move_to_0 = get_tst_node!(&node_seq.children[0], super::Node::MoveTo);
assert_eq!(
seq_move_to_0.params.waypoint,
mgp(15.570883999999998, 58.394741999999994, 100.8)
);
check_speed!(seq_move_to_0, super::Speed::Standard);
let seq_conc = get_tst_node!(&node_seq.children[1], super::Node::Conc);
let conc_searc_area = get_tst_node!(&seq_conc.children[0], super::Node::SearchArea);
assert_eq!(
conc_searc_area.params.area[0],
mgp(15.570883999999998, 58.394741999999994, 100.8)
);
assert_eq!(
conc_searc_area.params.area[1],
mgp(15.570883999999998, 58.304741999999994, 100.8)
);
assert_eq!(
conc_searc_area.params.area[2],
mgp(15.500883999999998, 58.304741999999994, 100.8)
);
check_max_speed!(conc_searc_area, 10.0);
let conc_move_to = get_tst_node!(&seq_conc.children[1], super::Node::MoveTo);
assert_eq!(
conc_move_to.params.waypoint,
mgp(15.571883999999998, 58.394742999999994, 100.8)
);
assert!(conc_move_to.params.speed.is_none());
let seq_move_to_2 = get_tst_node!(&node_seq.children[2], super::Node::MoveTo);
assert_eq!(
seq_move_to_2.params.waypoint,
mgp(16.572883999999998, 59.394741999999994, 110.8)
);
check_speed!(seq_move_to_2, super::Speed::Fast);
}
#[test]
fn gen_tst()
{
let node = super::Node::Seq(super::Seq {
common_params: CommonParameters {
node_uuid: crate::uuid::Uuid::from_string("f3bab5e0-a861-4f6e-bf28-37c9e3470181").unwrap(),
..Default::default()
},
..Default::default()
});
assert_eq!(
serde_json::to_string(&node).unwrap(),
r#"{"name":"seq","common_params":{"node_uuid":"f3bab5e0-a861-4f6e-bf28-37c9e3470181"},"children":[]}"#
);
}
#[test]
fn test_builder()
{
let node: super::Node = super::Conc::build(Some(CommonParameters {
node_uuid: crate::uuid::Uuid::from_string("68eb7e83-cc44-4404-bdb1-2fd66ffcbf1c").unwrap(),
..Default::default()
}))
.start::<super::Seq>(Some(CommonParameters {
node_uuid: crate::uuid::Uuid::from_string("a5665c27-e2c6-442f-8ab3-357aa4427945").unwrap(),
..Default::default()
}))
.add(
super::MoveToParameters {
waypoint: GeoPoint {
longitude: 16.4,
latitude: 59.3,
altitude: 110.8,
},
..super::default()
},
Some(CommonParameters {
node_uuid: crate::uuid::Uuid::from_string("0cd50294-611a-4da6-b398-73b0f2e0ee7c").unwrap(),
..Default::default()
}),
)
.add(
super::MoveToParameters {
waypoint: GeoPoint {
longitude: 16.5,
latitude: 59.5,
altitude: 110.7,
},
..super::default()
},
Some(CommonParameters {
node_uuid: crate::uuid::Uuid::from_string("47288783-cfbd-48ad-9bb9-734fae54d2be").unwrap(),
..Default::default()
}),
)
.end()
.add(
super::MoveToParameters {
waypoint: GeoPoint {
longitude: 16.6,
latitude: 59.4,
altitude: 110.9,
},
..super::default()
},
Some(CommonParameters {
node_uuid: crate::uuid::Uuid::from_string("88eae25f-473f-4185-bfcb-ca3ee0ed2247").unwrap(),
..Default::default()
}),
)
.into();
assert_eq!(
serde_json::to_string(&node).unwrap(),
r#"{"name":"conc","common_params":{"node_uuid":"68eb7e83-cc44-4404-bdb1-2fd66ffcbf1c"},"children":[{"name":"seq","common_params":{"node_uuid":"a5665c27-e2c6-442f-8ab3-357aa4427945"},"children":[{"name":"move-to","common_params":{"node_uuid":"0cd50294-611a-4da6-b398-73b0f2e0ee7c"},"params":{"waypoint":{"longitude":16.4,"latitude":59.3,"altitude":110.8}}},{"name":"move-to","common_params":{"node_uuid":"47288783-cfbd-48ad-9bb9-734fae54d2be"},"params":{"waypoint":{"longitude":16.5,"latitude":59.5,"altitude":110.7}}}]},{"name":"move-to","common_params":{"node_uuid":"88eae25f-473f-4185-bfcb-ca3ee0ed2247"},"params":{"waypoint":{"longitude":16.6,"latitude":59.4,"altitude":110.9}}}]}"#
);
}
}