mecha10-core 0.8.1

Mecha10 core runtime and foundational types
Documentation
//! Example usage of Dependency Ordering for Node Startup/Shutdown
//!
//! This example demonstrates how to use the dependency resolution system to
//! ensure nodes start and stop in the correct order based on their dependencies.

use mecha10::prelude::*;
use std::collections::HashSet;

/// Example 1: Simple dependency chain
pub fn simple_chain_example() -> Result<()> {
    info!("Example 1: Simple dependency chain");

    let graph = create_graph(&[
        ("camera", vec![]),
        ("vision", vec!["camera"]),
        ("planner", vec!["vision"]),
        ("controller", vec!["planner"]),
    ])?;

    let startup = graph.topological_sort()?;
    info!("Startup order: {:?}", startup);

    let shutdown = graph.reverse_topological_sort()?;
    info!("Shutdown order: {:?}", shutdown);

    Ok(())
}

/// Example 2: Parallel dependencies
pub fn parallel_dependencies_example() -> Result<()> {
    info!("Example 2: Parallel dependencies");

    let graph = create_graph(&[
        ("camera", vec![]),
        ("lidar", vec![]),
        ("imu", vec![]),
        ("sensor_fusion", vec!["camera", "lidar", "imu"]),
        ("localization", vec!["sensor_fusion"]),
    ])?;

    let plan = graph.startup_plan()?;

    info!("Startup plan with {} levels", plan.level_count());
    for (i, level) in plan.levels.iter().enumerate() {
        info!(
            "  Level {}: {:?} ({} nodes can start in parallel)",
            i,
            level,
            level.len()
        );
    }

    info!("Maximum parallelism: {} nodes", plan.max_parallelism());

    Ok(())
}

/// Example 3: Complex robot system
pub fn complex_system_example() -> Result<()> {
    info!("Example 3: Complex robot system");

    let graph = create_graph(&[
        ("camera_driver", vec![]),
        ("lidar_driver", vec![]),
        ("imu_driver", vec![]),
        ("motor_driver", vec![]),
        ("image_processor", vec!["camera_driver"]),
        ("point_cloud_processor", vec!["lidar_driver"]),
        ("odometry", vec!["imu_driver", "motor_driver"]),
        ("object_detector", vec!["image_processor"]),
        ("lane_detector", vec!["image_processor"]),
        ("obstacle_detector", vec!["point_cloud_processor"]),
        ("localization", vec!["odometry", "point_cloud_processor"]),
        ("path_planner", vec!["localization", "obstacle_detector"]),
        ("behavior_planner", vec!["object_detector", "lane_detector"]),
        ("motion_controller", vec!["path_planner", "behavior_planner"]),
    ])?;

    let plan = graph.startup_plan()?;

    info!("\nComplex system startup plan:");
    info!("Total nodes: {}", plan.node_count());
    info!("Total levels: {}", plan.level_count());
    info!("Max parallelism: {}\n", plan.max_parallelism());

    for (i, level) in plan.levels.iter().enumerate() {
        info!("Level {}: {} nodes", i, level.len());
        info!("  {:?}", level);
    }

    let shutdown = graph.shutdown_plan()?;
    info!("\nShutdown plan (first 2 levels):");
    for (i, level) in shutdown.levels.iter().take(2).enumerate() {
        info!("Level {}: {:?}", i, level);
    }

    Ok(())
}

/// Example 4: Detecting circular dependencies
pub fn circular_dependency_example() -> Result<()> {
    info!("Example 4: Detecting circular dependencies");

    let result = create_graph(&[
        ("node_a", vec!["node_b"]),
        ("node_b", vec!["node_c"]),
        ("node_c", vec!["node_a"]),
    ]);

    match result {
        Ok(_) => warn!("Unexpectedly passed validation"),
        Err(e) => {
            info!("✓ Correctly detected circular dependency:");
            info!("  {}", e);
        }
    }

    Ok(())
}

/// Example 5: Checking if nodes can start/stop
pub fn can_start_stop_example() -> Result<()> {
    info!("Example 5: Checking if nodes can start/stop");

    let graph = create_graph(&[
        ("camera", vec![]),
        ("vision", vec!["camera"]),
        ("planner", vec!["vision"]),
    ])?;

    let mut running = HashSet::new();

    info!("\nInitial state (nothing running):");
    info!("  camera can start: {}", graph.can_start("camera", &running));
    info!("  vision can start: {}", graph.can_start("vision", &running));
    info!("  planner can start: {}", graph.can_start("planner", &running));

    running.insert("camera".to_string());
    info!("\nAfter starting camera:");
    info!("  vision can start: {}", graph.can_start("vision", &running));
    info!("  planner can start: {}", graph.can_start("planner", &running));

    running.insert("vision".to_string());
    info!("\nAfter starting vision:");
    info!("  planner can start: {}", graph.can_start("planner", &running));

    running.insert("planner".to_string());

    info!("\nAll nodes running - checking stop order:");
    info!("  camera can stop: {}", graph.can_stop("camera", &running));
    info!("  vision can stop: {}", graph.can_stop("vision", &running));
    info!("  planner can stop: {}", graph.can_stop("planner", &running));

    running.remove("planner");
    info!("\nAfter stopping planner:");
    info!("  vision can stop: {}", graph.can_stop("vision", &running));
    info!("  camera can stop: {}", graph.can_stop("camera", &running));

    Ok(())
}

/// Example 6: Getting all dependencies and dependents
pub fn transitive_dependencies_example() -> Result<()> {
    info!("Example 6: Transitive dependencies");

    let graph = create_graph(&[
        ("a", vec![]),
        ("b", vec!["a"]),
        ("c", vec!["b"]),
        ("d", vec!["c"]),
        ("e", vec!["c"]),
    ])?;

    let deps = graph.get_all_dependencies("d");
    info!("\nAll dependencies of 'd': {:?}", deps);

    let dependents = graph.get_all_dependents("a");
    info!("All dependents of 'a': {:?}", dependents);

    let direct_deps = graph.get_direct_dependencies("d");
    info!("\nDirect dependencies of 'd': {:?}", direct_deps);

    let direct_dependents = graph.get_direct_dependents("a");
    info!("Direct dependents of 'a': {:?}", direct_dependents);

    Ok(())
}

/// Example 7: From NodeConfig objects
pub fn from_node_configs_example() -> Result<()> {
    info!("Example 7: Building graph from NodeConfig");

    use std::collections::HashMap;

    let configs = vec![
        NodeConfig {
            id: "camera".to_string(),
            name: Some("RGB Camera".to_string()),
            executable: "./camera".to_string(),
            args: vec![],
            env: HashMap::new(),
            mode: NodeMode::Local,
            auto_restart: true,
            health_check_interval_s: 5,
            publishes: vec!["/sensor/camera/rgb".to_string()],
            subscribes: vec![],
            depends_on: vec![],
        },
        NodeConfig {
            id: "vision".to_string(),
            name: Some("Vision Processing".to_string()),
            executable: "./vision".to_string(),
            args: vec![],
            env: HashMap::new(),
            mode: NodeMode::Remote,
            auto_restart: true,
            health_check_interval_s: 5,
            publishes: vec!["/perception/detections".to_string()],
            subscribes: vec!["/sensor/camera/rgb".to_string()],
            depends_on: vec!["camera".to_string()],
        },
        NodeConfig {
            id: "planner".to_string(),
            name: Some("Path Planning".to_string()),
            executable: "./planner".to_string(),
            args: vec![],
            env: HashMap::new(),
            mode: NodeMode::Remote,
            auto_restart: true,
            health_check_interval_s: 5,
            publishes: vec!["/planning/path".to_string()],
            subscribes: vec!["/perception/detections".to_string()],
            depends_on: vec!["vision".to_string()],
        },
    ];

    let graph = DependencyGraph::from_configs(&configs)?;

    info!("\nBuilt graph from {} NodeConfig objects", configs.len());

    let startup = graph.topological_sort()?;
    info!("Startup order: {:?}", startup);

    let plan = graph.startup_plan()?;
    info!("\nStartup plan:");
    for (i, level) in plan.levels.iter().enumerate() {
        info!("  Level {}: {:?}", i, level);
    }

    Ok(())
}

/// Example 8: Warehouse robot with complex dependencies
pub fn warehouse_robot_example() -> Result<()> {
    info!("Example 8: Warehouse robot system");

    let graph = create_graph(&[
        ("sick_lidar", vec![]),
        ("realsense_camera", vec![]),
        ("imu_sensor", vec![]),
        ("odrive_motors", vec![]),
        ("gripper_controller", vec![]),
        ("lidar_processor", vec!["sick_lidar"]),
        ("rgb_processor", vec!["realsense_camera"]),
        ("depth_processor", vec!["realsense_camera"]),
        ("imu_filter", vec!["imu_sensor"]),
        ("wheel_odometry", vec!["odrive_motors", "imu_sensor"]),
        ("slam", vec!["lidar_processor", "wheel_odometry"]),
        ("object_detection", vec!["rgb_processor"]),
        ("shelf_detection", vec!["depth_processor"]),
        ("global_planner", vec!["slam"]),
        ("local_planner", vec!["global_planner", "object_detection"]),
        ("grasp_planner", vec!["shelf_detection", "object_detection"]),
        ("navigation_controller", vec!["local_planner"]),
        ("manipulation_controller", vec!["grasp_planner", "gripper_controller"]),
        (
            "task_coordinator",
            vec!["navigation_controller", "manipulation_controller"],
        ),
    ])?;

    let plan = graph.startup_plan()?;

    info!("\nWarehouse robot startup plan:");
    info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    info!("Total nodes: {}", plan.node_count());
    info!("Total levels: {}", plan.level_count());
    info!("Max parallelism: {} nodes", plan.max_parallelism());
    info!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");

    for (i, level) in plan.levels.iter().enumerate() {
        let layer_name = match i {
            0 => "Hardware",
            1 => "Processing",
            2 => "Perception",
            3 => "Planning",
            4 => "Control",
            5 => "Coordination",
            _ => "Advanced",
        };

        info!("Level {} - {} Layer ({} nodes):", i, layer_name, level.len());
        for node in level {
            info!("  • {}", node);
        }
        info!("");
    }

    Ok(())
}

fn main() -> Result<()> {
    tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).init();

    println!("Dependency Ordering Usage Examples");
    println!("===================================\n");
    println!("Dependency ordering ensures nodes start and stop in the correct order.");
    println!("Dependencies must start before dependents, and stop after them.\n");

    simple_chain_example()?;
    println!();

    parallel_dependencies_example()?;
    println!();

    complex_system_example()?;
    println!();

    circular_dependency_example()?;
    println!();

    can_start_stop_example()?;
    println!();

    transitive_dependencies_example()?;
    println!();

    from_node_configs_example()?;
    println!();

    warehouse_robot_example()?;
    println!();

    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    println!("All examples completed!");
    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");

    println!("Usage in your code:");
    println!("  use mecha10::prelude::*;");
    println!();
    println!("  // Build graph from configs");
    println!("  let graph = DependencyGraph::from_configs(&node_configs)?;");
    println!();
    println!("  // Get startup order");
    println!("  let plan = graph.startup_plan()?;");
    println!("  for level in &plan.levels {{");
    println!("      // Start nodes in this level (can be parallel)");
    println!("      for node_id in level {{");
    println!("          runtime.start_node(node_id).await?;");
    println!("      }}");
    println!("  }}");
    println!();
    println!("  // Get shutdown order");
    println!("  let shutdown = graph.shutdown_plan()?;");
    println!("  for level in &shutdown.levels {{");
    println!("      // Stop nodes in this level");
    println!("  }}");

    Ok(())
}