mecha10-sim 0.7.0

Mecha10 simulation support — scene generation, asset management
Documentation
#![allow(dead_code)]

//! Simulation service for scene generation and management
//!
//! This service provides operations for generating and managing simulations,
//! including robot scene generation, environment selection, and validation.

use crate::paths;
use crate::sim::{EnvironmentSelector, RobotGenerator, RobotProfile};
use anyhow::Result;
use std::path::{Path, PathBuf};

pub struct SimulationService {
    base_path: PathBuf,
}

impl SimulationService {
    /// Create a new simulation service with default paths
    pub fn new() -> Self {
        Self {
            base_path: PathBuf::from(paths::project::SIMULATION_OUTPUT_DIR),
        }
    }

    /// Create a simulation service with custom base path
    ///
    /// # Arguments
    ///
    /// * `base_path` - Base directory for simulation output
    pub fn with_base_path(base_path: impl Into<PathBuf>) -> Self {
        Self {
            base_path: base_path.into(),
        }
    }

    /// Generate simulation scenes from project configuration
    ///
    /// # Arguments
    ///
    /// * `config_path` - Path to mecha10.json
    /// * `output_path` - Output directory for simulation files
    /// * `max_envs` - Maximum number of environments to generate
    /// * `min_score` - Minimum compatibility score (0-100)
    pub fn generate(
        &self,
        config_path: &Path,
        output_path: &Path,
        max_envs: usize,
        min_score: i32,
    ) -> Result<GenerationResult> {
        let profile = RobotProfile::from_config_file(config_path)?;

        let robot_output = output_path.join("robot.tscn");
        let robot_generator = RobotGenerator::from_config_file(config_path)?;
        robot_generator.generate(&robot_output)?;

        let selector = EnvironmentSelector::new()?;
        let matches = selector.select_environments(&profile, max_envs)?;

        let filtered_matches: Vec<_> = matches.into_iter().filter(|m| m.score >= min_score).collect();

        if filtered_matches.is_empty() {
            return Err(anyhow::anyhow!(
                "No matching environments found (min score: {})",
                min_score
            ));
        }

        let _catalog_path = PathBuf::from(paths::framework::ROBOT_TASKS_CATALOG);
        let base_path = PathBuf::from(paths::framework::ROBOT_TASKS_DIR);
        let mut available_count = 0;

        for env_match in &filtered_matches {
            let env_path = base_path.join(&env_match.environment.path);
            if env_path.exists() {
                available_count += 1;
            }
        }

        Ok(GenerationResult {
            robot_scene: robot_output,
            environments: filtered_matches.iter().map(|m| m.environment.id.clone()).collect(),
            available_count,
        })
    }

    /// Generate only the robot scene
    ///
    /// # Arguments
    ///
    /// * `config_path` - Path to mecha10.json
    /// * `output_path` - Output path for robot.tscn
    pub fn generate_robot(&self, config_path: &Path, output_path: &Path) -> Result<()> {
        let robot_generator = RobotGenerator::from_config_file(config_path)?;
        robot_generator.generate(output_path)
    }

    /// List available environments
    ///
    /// # Arguments
    ///
    /// * `config_path` - Path to mecha10.json
    /// * `verbose` - Show detailed information
    pub fn list_environments(&self, config_path: &Path, verbose: bool) -> Result<Vec<EnvironmentInfo>> {
        let profile = RobotProfile::from_config_file(config_path)?;
        let selector = EnvironmentSelector::new()?;
        let matches = selector.select_environments(&profile, 100)?;

        let base_path = PathBuf::from(paths::framework::ROBOT_TASKS_DIR);
        let mut environments = Vec::new();

        for env_match in matches {
            let env_path = base_path.join(&env_match.environment.path);
            let available = env_path.exists();

            if verbose || available {
                environments.push(EnvironmentInfo {
                    id: env_match.environment.id.clone(),
                    name: env_match.environment.name.clone(),
                    description: env_match.environment.description.clone(),
                    score: env_match.score,
                    available,
                });
            }
        }

        Ok(environments)
    }

    /// Validate project configuration for simulation
    ///
    /// # Arguments
    ///
    /// * `config_path` - Path to mecha10.json
    pub fn validate_config(&self, config_path: &Path) -> Result<ValidationResult> {
        let profile = RobotProfile::from_config_file(config_path)?;

        let mut errors = Vec::new();
        let mut warnings = Vec::new();

        if profile.platform.is_empty() {
            errors.push("Platform not specified".to_string());
        }

        if profile.sensors.is_empty() {
            warnings.push("No sensors configured".to_string());
        }

        if profile.task_nodes.is_empty() {
            warnings.push("No task nodes configured".to_string());
        }

        let is_valid = errors.is_empty();

        Ok(ValidationResult {
            is_valid,
            errors,
            warnings,
            platform: profile.platform,
            sensor_count: profile.sensors.len(),
            node_count: profile.task_nodes.len(),
        })
    }

    /// Check if simulation is properly set up
    pub fn is_setup(&self) -> bool {
        self.base_path.exists() && self.base_path.join("robot.tscn").exists()
    }

    /// Get the base path for simulations
    pub fn base_path(&self) -> &Path {
        &self.base_path
    }
}

impl Default for SimulationService {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Debug)]
pub struct GenerationResult {
    pub robot_scene: PathBuf,
    pub environments: Vec<String>,
    pub available_count: usize,
}

#[derive(Debug, Clone)]
pub struct EnvironmentInfo {
    pub id: String,
    pub name: String,
    pub description: String,
    pub score: i32,
    pub available: bool,
}

#[derive(Debug)]
pub struct ValidationResult {
    pub is_valid: bool,
    pub errors: Vec<String>,
    pub warnings: Vec<String>,
    pub platform: String,
    pub sensor_count: usize,
    pub node_count: usize,
}