mecha10-sim 0.7.0

Mecha10 simulation support — scene generation, asset management
Documentation
//! Environment catalog loading and management

use crate::paths;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentCatalog {
    pub version: String,
    pub environments: Vec<Environment>,
    #[serde(default)]
    pub tags: HashMap<String, String>,
    #[serde(default)]
    pub difficulty_levels: HashMap<String, String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Environment {
    pub id: String,
    pub path: String,
    pub name: String,
    pub description: String,
    pub tags: Vec<String>,
    pub required_sensors: Vec<String>,
    #[serde(default)]
    pub optional_sensors: Vec<String>,
    pub robot_platforms: Vec<String>,
    #[serde(default)]
    pub task_nodes: Vec<String>,
    pub difficulty: String,
    #[serde(default)]
    pub dimensions: Vec<f32>,
    #[serde(default)]
    pub features: EnvironmentFeatures,
    #[serde(default)]
    pub curriculum_stage: i32,
    #[serde(default)]
    pub estimated_duration_s: i32,
    #[serde(default)]
    pub reward_config: HashMap<String, f32>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EnvironmentFeatures {
    #[serde(default)]
    pub floor: bool,
    #[serde(default)]
    pub walls: bool,
    #[serde(default)]
    pub obstacles: String,
    #[serde(default)]
    pub obstacle_count: i32,
    #[serde(default)]
    pub tables: i32,
    #[serde(default)]
    pub waypoints: i32,
    #[serde(default)]
    pub goal_zones: i32,
    #[serde(default)]
    pub npcs: bool,
    #[serde(default)]
    pub npc_count: i32,
    #[serde(default)]
    pub dynamic: bool,
}

impl EnvironmentCatalog {
    /// Load catalog from default location
    pub fn load_default() -> Result<Self> {
        let catalog_path = Self::default_catalog_path()?;
        Self::load(&catalog_path)
    }

    /// Load catalog from specific path
    pub fn load(path: &Path) -> Result<Self> {
        super::load_json(path, "catalog file")
    }

    /// Get default catalog path (monorepo location)
    fn default_catalog_path() -> Result<PathBuf> {
        let current_dir = std::env::current_dir()?;

        let mut search_dir = current_dir.clone();
        loop {
            let catalog_path = search_dir
                .join("packages")
                .join("simulation")
                .join("environments")
                .join("robot-tasks")
                .join("catalog.json");

            if catalog_path.exists() {
                return Ok(catalog_path);
            }

            if let Some(parent) = search_dir.parent() {
                search_dir = parent.to_path_buf();
            } else {
                break;
            }
        }

        Ok(PathBuf::from(paths::framework::ROBOT_TASKS_CATALOG))
    }
}

impl Environment {
    /// Check if environment is compatible with robot (has all required sensors)
    pub fn is_compatible_with_sensors(&self, sensors: &[String]) -> bool {
        self.required_sensors.iter().all(|required| sensors.contains(required))
    }

    /// Check if environment is compatible with platform
    pub fn is_compatible_with_platform(&self, platform: &str) -> bool {
        self.robot_platforms.contains(&platform.to_string())
    }
}