mecha10-nodes-behavior-executor 0.1.48

Behavior tree executor node for autonomous robot behaviors
Documentation
//! Configuration for BehaviorExecutor node

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Configuration for the behavior executor node
///
/// This controls which behavior tree is loaded and executed.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BehaviorExecutorConfig {
    /// Name of the behavior to execute (e.g., "idle_wander")
    ///
    /// This should match a JSON file in the behaviors directory.
    pub behavior_name: String,

    /// Directory containing behavior tree templates
    #[serde(default = "default_behaviors_dir")]
    pub behaviors_dir: String,

    /// Tick rate in Hz (how many times per second to tick the behavior tree)
    #[serde(default = "default_tick_rate_hz")]
    pub tick_rate_hz: f32,

    /// Maximum number of ticks before stopping (None = run forever)
    #[serde(default)]
    pub max_ticks: Option<usize>,

    /// Whether to log execution statistics
    #[serde(default = "default_log_stats")]
    pub log_stats: bool,

    /// Topics configuration
    #[serde(default)]
    pub topics: Topics,
}

/// Topics configuration for behavior executor
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Topics {
    /// Topics this node publishes to
    /// Format: [{ "status": "/behavior/status" }]
    #[serde(default)]
    pub publishes: Vec<HashMap<String, String>>,

    /// Topics this node subscribes to
    /// Format: [{ "control": "/behavior/control" }]
    #[serde(default)]
    pub subscribes: Vec<HashMap<String, String>>,
}

impl Default for Topics {
    fn default() -> Self {
        let mut publishes = Vec::new();
        let mut subscribes = Vec::new();

        // Default publish topic
        let mut status_map = HashMap::new();
        status_map.insert("status".to_string(), "/behavior/status".to_string());
        publishes.push(status_map);

        // Default subscribe topic
        let mut control_map = HashMap::new();
        control_map.insert("control".to_string(), "/behavior/control".to_string());
        subscribes.push(control_map);

        Self { publishes, subscribes }
    }
}

impl BehaviorExecutorConfig {
    /// Get control topic path from topics config
    pub fn control_topic(&self) -> String {
        self.topics
            .subscribes
            .iter()
            .find_map(|t| t.get("control"))
            .cloned()
            .unwrap_or_else(|| "/behavior/control".to_string())
    }

    /// Get status topic path from topics config
    pub fn status_topic(&self) -> String {
        self.topics
            .publishes
            .iter()
            .find_map(|t| t.get("status"))
            .cloned()
            .unwrap_or_else(|| "/behavior/status".to_string())
    }
}

impl Default for BehaviorExecutorConfig {
    fn default() -> Self {
        Self {
            behavior_name: "idle_wander".to_string(),
            behaviors_dir: default_behaviors_dir(),
            tick_rate_hz: default_tick_rate_hz(),
            max_ticks: None,
            log_stats: default_log_stats(),
            topics: Topics::default(),
        }
    }
}

fn default_behaviors_dir() -> String {
    "behaviors".to_string()
}

fn default_tick_rate_hz() -> f32 {
    10.0
}

fn default_log_stats() -> bool {
    true
}