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 {
pub fn load_default() -> Result<Self> {
let catalog_path = Self::default_catalog_path()?;
Self::load(&catalog_path)
}
pub fn load(path: &Path) -> Result<Self> {
super::load_json(path, "catalog file")
}
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 {
pub fn is_compatible_with_sensors(&self, sensors: &[String]) -> bool {
self.required_sensors.iter().all(|required| sensors.contains(required))
}
pub fn is_compatible_with_platform(&self, platform: &str) -> bool {
self.robot_platforms.contains(&platform.to_string())
}
}