mecha10-behavior-runtime 0.9.0

Behavior tree runtime for Mecha10 - unified AI and logic composition system
Documentation
//! Stub navigation action nodes for path planning, following, and arrival checks.

use crate::{BehaviorNode, Context, NodeStatus};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;

macro_rules! stub_tick_success {
    ($msg:literal) => {{
        tracing::debug!($msg);
        Ok(NodeStatus::Success)
    }};
}

#[derive(Debug)]
pub struct PlanPathNode {
    #[allow(dead_code)]
    target_position: Vec<f64>,
    #[allow(dead_code)]
    algorithm: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct PlanPathConfig {
    target_position: Vec<f64>,
    #[serde(default = "default_algorithm")]
    path_planning_algorithm: String,
}

fn default_algorithm() -> String {
    "a_star".to_string()
}

impl PlanPathNode {
    /// Create a new PlanPathNode from JSON configuration
    pub fn from_json(config: Value) -> anyhow::Result<Self> {
        let cfg: PlanPathConfig = serde_json::from_value(config)?;
        Ok(Self {
            target_position: cfg.target_position,
            algorithm: cfg.path_planning_algorithm,
        })
    }
}

#[async_trait]
impl BehaviorNode for PlanPathNode {
    async fn tick(&mut self, _ctx: &Context) -> anyhow::Result<NodeStatus> {
        tracing::debug!("PlanPathNode: planning path to {:?} (stub)", self.target_position);
        Ok(NodeStatus::Success)
    }

    fn name(&self) -> &str {
        "PlanPathNode"
    }
}

#[derive(Debug)]
pub struct FollowPathNode {
    #[allow(dead_code)]
    topic: String,
    #[allow(dead_code)]
    max_speed: f64,
    #[allow(dead_code)]
    turn_rate: f64,
}

#[derive(Debug, Serialize, Deserialize)]
struct FollowPathConfig {
    #[serde(default = "default_motor_topic")]
    topic: String,
    #[serde(default = "default_max_speed")]
    max_speed: f64,
    #[serde(default = "default_turn_rate")]
    turn_rate: f64,
}

fn default_motor_topic() -> String {
    "/motor/cmd_vel".to_string()
}

fn default_max_speed() -> f64 {
    1.0
}

fn default_turn_rate() -> f64 {
    0.5
}

impl FollowPathNode {
    /// Create a new FollowPathNode from JSON configuration
    pub fn from_json(config: Value) -> anyhow::Result<Self> {
        let cfg: FollowPathConfig = serde_json::from_value(config)?;
        Ok(Self {
            topic: cfg.topic,
            max_speed: cfg.max_speed,
            turn_rate: cfg.turn_rate,
        })
    }
}

#[async_trait]
impl BehaviorNode for FollowPathNode {
    async fn tick(&mut self, _ctx: &Context) -> anyhow::Result<NodeStatus> {
        stub_tick_success!("FollowPathNode: following path (stub)")
    }

    fn name(&self) -> &str {
        "FollowPathNode"
    }
}

#[derive(Debug)]
pub struct CheckArrivalNode {
    #[allow(dead_code)]
    position_tolerance: f64,
    #[allow(dead_code)]
    angular_tolerance: f64,
}

#[derive(Debug, Serialize, Deserialize)]
struct CheckArrivalConfig {
    position_tolerance: f64,
    angular_tolerance: f64,
}

impl CheckArrivalNode {
    /// Create a new CheckArrivalNode from JSON configuration
    pub fn from_json(config: Value) -> anyhow::Result<Self> {
        let cfg: CheckArrivalConfig = serde_json::from_value(config)?;
        Ok(Self {
            position_tolerance: cfg.position_tolerance,
            angular_tolerance: cfg.angular_tolerance,
        })
    }
}

#[async_trait]
impl BehaviorNode for CheckArrivalNode {
    async fn tick(&mut self, _ctx: &Context) -> anyhow::Result<NodeStatus> {
        stub_tick_success!("CheckArrivalNode: checking arrival (stub)")
    }

    fn name(&self) -> &str {
        "CheckArrivalNode"
    }
}