Skip to main content

mecha10_sim/services/
simulation.rs

1#![allow(dead_code)]
2
3//! Simulation service for scene generation and management
4//!
5//! This service provides operations for generating and managing simulations,
6//! including robot scene generation, environment selection, and validation.
7
8use crate::paths;
9use crate::sim::{EnvironmentSelector, RobotGenerator, RobotProfile};
10use anyhow::Result;
11use std::path::{Path, PathBuf};
12
13pub struct SimulationService {
14    base_path: PathBuf,
15}
16
17impl SimulationService {
18    /// Create a new simulation service with default paths
19    pub fn new() -> Self {
20        Self {
21            base_path: PathBuf::from(paths::project::SIMULATION_OUTPUT_DIR),
22        }
23    }
24
25    /// Create a simulation service with custom base path
26    ///
27    /// # Arguments
28    ///
29    /// * `base_path` - Base directory for simulation output
30    pub fn with_base_path(base_path: impl Into<PathBuf>) -> Self {
31        Self {
32            base_path: base_path.into(),
33        }
34    }
35
36    /// Generate simulation scenes from project configuration
37    ///
38    /// # Arguments
39    ///
40    /// * `config_path` - Path to mecha10.json
41    /// * `output_path` - Output directory for simulation files
42    /// * `max_envs` - Maximum number of environments to generate
43    /// * `min_score` - Minimum compatibility score (0-100)
44    pub fn generate(
45        &self,
46        config_path: &Path,
47        output_path: &Path,
48        max_envs: usize,
49        min_score: i32,
50    ) -> Result<GenerationResult> {
51        let profile = RobotProfile::from_config_file(config_path)?;
52
53        let robot_output = output_path.join("robot.tscn");
54        let robot_generator = RobotGenerator::from_config_file(config_path)?;
55        robot_generator.generate(&robot_output)?;
56
57        let selector = EnvironmentSelector::new()?;
58        let matches = selector.select_environments(&profile, max_envs)?;
59
60        let filtered_matches: Vec<_> = matches.into_iter().filter(|m| m.score >= min_score).collect();
61
62        if filtered_matches.is_empty() {
63            return Err(anyhow::anyhow!(
64                "No matching environments found (min score: {})",
65                min_score
66            ));
67        }
68
69        let _catalog_path = PathBuf::from(paths::framework::ROBOT_TASKS_CATALOG);
70        let base_path = PathBuf::from(paths::framework::ROBOT_TASKS_DIR);
71        let mut available_count = 0;
72
73        for env_match in &filtered_matches {
74            let env_path = base_path.join(&env_match.environment.path);
75            if env_path.exists() {
76                available_count += 1;
77            }
78        }
79
80        Ok(GenerationResult {
81            robot_scene: robot_output,
82            environments: filtered_matches.iter().map(|m| m.environment.id.clone()).collect(),
83            available_count,
84        })
85    }
86
87    /// Generate only the robot scene
88    ///
89    /// # Arguments
90    ///
91    /// * `config_path` - Path to mecha10.json
92    /// * `output_path` - Output path for robot.tscn
93    pub fn generate_robot(&self, config_path: &Path, output_path: &Path) -> Result<()> {
94        let robot_generator = RobotGenerator::from_config_file(config_path)?;
95        robot_generator.generate(output_path)
96    }
97
98    /// List available environments
99    ///
100    /// # Arguments
101    ///
102    /// * `config_path` - Path to mecha10.json
103    /// * `verbose` - Show detailed information
104    pub fn list_environments(&self, config_path: &Path, verbose: bool) -> Result<Vec<EnvironmentInfo>> {
105        let profile = RobotProfile::from_config_file(config_path)?;
106        let selector = EnvironmentSelector::new()?;
107        let matches = selector.select_environments(&profile, 100)?;
108
109        let base_path = PathBuf::from(paths::framework::ROBOT_TASKS_DIR);
110        let mut environments = Vec::new();
111
112        for env_match in matches {
113            let env_path = base_path.join(&env_match.environment.path);
114            let available = env_path.exists();
115
116            if verbose || available {
117                environments.push(EnvironmentInfo {
118                    id: env_match.environment.id.clone(),
119                    name: env_match.environment.name.clone(),
120                    description: env_match.environment.description.clone(),
121                    score: env_match.score,
122                    available,
123                });
124            }
125        }
126
127        Ok(environments)
128    }
129
130    /// Validate project configuration for simulation
131    ///
132    /// # Arguments
133    ///
134    /// * `config_path` - Path to mecha10.json
135    pub fn validate_config(&self, config_path: &Path) -> Result<ValidationResult> {
136        let profile = RobotProfile::from_config_file(config_path)?;
137
138        let mut errors = Vec::new();
139        let mut warnings = Vec::new();
140
141        if profile.platform.is_empty() {
142            errors.push("Platform not specified".to_string());
143        }
144
145        if profile.sensors.is_empty() {
146            warnings.push("No sensors configured".to_string());
147        }
148
149        if profile.task_nodes.is_empty() {
150            warnings.push("No task nodes configured".to_string());
151        }
152
153        let is_valid = errors.is_empty();
154
155        Ok(ValidationResult {
156            is_valid,
157            errors,
158            warnings,
159            platform: profile.platform,
160            sensor_count: profile.sensors.len(),
161            node_count: profile.task_nodes.len(),
162        })
163    }
164
165    /// Check if simulation is properly set up
166    pub fn is_setup(&self) -> bool {
167        self.base_path.exists() && self.base_path.join("robot.tscn").exists()
168    }
169
170    /// Get the base path for simulations
171    pub fn base_path(&self) -> &Path {
172        &self.base_path
173    }
174}
175
176impl Default for SimulationService {
177    fn default() -> Self {
178        Self::new()
179    }
180}
181
182#[derive(Debug)]
183pub struct GenerationResult {
184    pub robot_scene: PathBuf,
185    pub environments: Vec<String>,
186    pub available_count: usize,
187}
188
189#[derive(Debug, Clone)]
190pub struct EnvironmentInfo {
191    pub id: String,
192    pub name: String,
193    pub description: String,
194    pub score: i32,
195    pub available: bool,
196}
197
198#[derive(Debug)]
199pub struct ValidationResult {
200    pub is_valid: bool,
201    pub errors: Vec<String>,
202    pub warnings: Vec<String>,
203    pub platform: String,
204    pub sensor_count: usize,
205    pub node_count: usize,
206}