mecha10_sim/sim/
catalog.rs1use crate::paths;
4use anyhow::{Context, Result};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::fs;
8use std::path::{Path, PathBuf};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct EnvironmentCatalog {
12 pub version: String,
13 pub environments: Vec<Environment>,
14 #[serde(default)]
15 pub tags: HashMap<String, String>,
16 #[serde(default)]
17 pub difficulty_levels: HashMap<String, String>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct Environment {
22 pub id: String,
23 pub path: String,
24 pub name: String,
25 pub description: String,
26 pub tags: Vec<String>,
27 pub required_sensors: Vec<String>,
28 #[serde(default)]
29 pub optional_sensors: Vec<String>,
30 pub robot_platforms: Vec<String>,
31 #[serde(default)]
32 pub task_nodes: Vec<String>,
33 pub difficulty: String,
34 #[serde(default)]
35 pub dimensions: Vec<f32>,
36 #[serde(default)]
37 pub features: EnvironmentFeatures,
38 #[serde(default)]
39 pub curriculum_stage: i32,
40 #[serde(default)]
41 pub estimated_duration_s: i32,
42 #[serde(default)]
43 pub reward_config: HashMap<String, f32>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize, Default)]
47pub struct EnvironmentFeatures {
48 #[serde(default)]
49 pub floor: bool,
50 #[serde(default)]
51 pub walls: bool,
52 #[serde(default)]
53 pub obstacles: String,
54 #[serde(default)]
55 pub obstacle_count: i32,
56 #[serde(default)]
57 pub tables: i32,
58 #[serde(default)]
59 pub waypoints: i32,
60 #[serde(default)]
61 pub goal_zones: i32,
62 #[serde(default)]
63 pub npcs: bool,
64 #[serde(default)]
65 pub npc_count: i32,
66 #[serde(default)]
67 pub dynamic: bool,
68}
69
70impl EnvironmentCatalog {
71 pub fn load_default() -> Result<Self> {
73 let catalog_path = Self::default_catalog_path()?;
74 Self::load(&catalog_path)
75 }
76
77 pub fn load(path: &Path) -> Result<Self> {
79 let content =
80 fs::read_to_string(path).with_context(|| format!("Failed to read catalog file: {}", path.display()))?;
81
82 let catalog: EnvironmentCatalog =
83 serde_json::from_str(&content).with_context(|| "Failed to parse catalog.json")?;
84
85 Ok(catalog)
86 }
87
88 fn default_catalog_path() -> Result<PathBuf> {
90 let current_dir = std::env::current_dir()?;
92
93 let mut search_dir = current_dir.clone();
95 loop {
96 let catalog_path = search_dir
97 .join("packages")
98 .join("simulation")
99 .join("environments")
100 .join("robot-tasks")
101 .join("catalog.json");
102
103 if catalog_path.exists() {
104 return Ok(catalog_path);
105 }
106
107 if let Some(parent) = search_dir.parent() {
109 search_dir = parent.to_path_buf();
110 } else {
111 break;
112 }
113 }
114
115 Ok(PathBuf::from(paths::framework::ROBOT_TASKS_CATALOG))
117 }
118}
119
120impl Environment {
121 pub fn is_compatible_with_sensors(&self, sensors: &[String]) -> bool {
123 self.required_sensors.iter().all(|required| sensors.contains(required))
124 }
125
126 pub fn is_compatible_with_platform(&self, platform: &str) -> bool {
128 self.robot_platforms.contains(&platform.to_string())
129 }
130}