import React, { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { hsapApi } from "@/app/hsap-api"; import { Button } from "@/components/ui/Button"; import { Badge, StatusBadge } from "@/components/ui/Badge"; import { PageQueryState } from "@/components/PageQueryState"; type SimJob = Record; const SCENES = [ { key: "urban_highway", label: "城市快速路", icon: "🛣️" }, { key: "urban_street", label: "城市街道", icon: "🏙️" }, { key: "rural_road", label: "乡村道路", icon: "🌾" }, { key: "tunnel", label: "隧道", icon: "🚇" }, { key: "night_city", label: "夜间城市", icon: "🌃" }, { key: "rain_highway", label: "雨天高速", icon: "🌧️" }, { key: "fog_rural", label: "雾天乡村", icon: "🌫️" }, ]; const CAMERAS = [ { key: "truck_front", label: "卡车前视", icon: "🚛", height: "2.5m", fov: "75°" }, { key: "truck_side", label: "卡车侧视", icon: "🚛", height: "2.5m", fov: "100°" }, { key: "car_front", label: "轿车前视", icon: "🚗", height: "1.2m", fov: "60°" }, { key: "car_wide", label: "轿车广角", icon: "🚗", height: "1.2m", fov: "120°" }, ]; const WEATHERS = ["clear", "cloudy", "rain", "fog", "night"]; const WEATHER_LABELS: Record = { clear: "晴天", cloudy: "多云", rain: "雨天", fog: "大雾", night: "夜间" }; const DENSITY_LABELS: Record = { sparse: "稀疏 5-10", medium: "中等 10-30", dense: "密集 30+" }; const ALL_CLASSES = ["Pedestrain", "Car", "Truck", "Bus", "Motor-vehicles", "Tricycle", "cones"]; const JOBS_PAGE = 20; export const SimulationStudioPage: React.FC = () => { // Config form const [scene, setScene] = useState("urban_highway"); const [camera, setCamera] = useState("truck_front"); const [weather, setWeather] = useState("clear"); const [objects, setObjects] = useState(["Pedestrain", "Car", "Truck", "Bus"]); const [density, setDensity] = useState("medium"); const [count, setCount] = useState(100); const [fovVariant, setFovVariant] = useState(false); const [note, setNote] = useState(""); const [generating, setGenerating] = useState(false); const [info, setInfo] = useState(null); // Jobs list const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const loadJobs = async () => { setLoading(true); setError(null); try { const res = await fetch(`/api/v1/simulate/jobs?limit=50`, { headers: { Authorization: `Bearer ${hsapApi.getToken()}` }, cache: "no-store", }).then((r) => r.json()); setJobs((res.items || []) as SimJob[]); } catch (e) { setError(String(e)); } setLoading(false); }; useEffect(() => { loadJobs(); }, []); const toggleObject = (obj: string) => { setObjects((prev) => prev.includes(obj) ? prev.filter((o) => o !== obj) : [...prev, obj]); }; const handleGenerate = async () => { if (objects.length === 0) { setError("请至少选择一种目标类别"); return; } setGenerating(true); setError(null); setInfo(null); try { const res = await fetch("/api/v1/simulate/generate", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${hsapApi.getToken()}` }, body: JSON.stringify({ scene, camera, weather, objects, density, count, fov_variant: fovVariant, note }), }).then((r) => r.json()); setInfo(`任务已提交: ${res.id}`); loadJobs(); } catch (e) { setError(String(e)); } setGenerating(false); }; const handleIngest = async (jobId: string) => { try { const res = await fetch(`/api/v1/simulate/jobs/${jobId}/ingest?task=adas`, { method: "POST", headers: { Authorization: `Bearer ${hsapApi.getToken()}` }, }).then((r) => r.json()); if (res.ok) setInfo(`已入库: ${res.batch}`); else setError(String(res.error)); loadJobs(); } catch (e) { setError(String(e)); } }; return (

仿真工坊

世界模型生成合成数据 — 自动标注,直接入库

{info &&
{info}
} {error &&
{error}
}
{/* Left: Config panel */}
场景配置
{/* Scene */}
{SCENES.map((s) => ( ))}
{/* Camera */}
{CAMERAS.map((c) => ( ))}
{/* Weather */}
{WEATHERS.map((w) => ( ))}
{/* Objects */}
{ALL_CLASSES.map((c) => ( ))}
{/* Density + Count */}
{/* Multi-FOV variant */}
{/* Note */}
setNote(e.target.value)} placeholder="如:补卡车视角雨天行人数据" />
{/* Right: Job history */}
生成历史
{jobs.map((job) => { const params = job.params as Record | undefined; return (
{(job.id as string).slice(0, 20)}...
{params && (
{String(params.scene_label ?? "")} · {String(params.camera_label ?? "")} · {WEATHER_LABELS[String(params.weather ?? "")] || String(params.weather ?? "")}
{(params.objects as string[])?.map((o) => {o})}
{DENSITY_LABELS[params.density as string]} · {params.count as number} 张
{params.note != null && String(params.note) &&
"{String(params.note)}"
}
)} {job.status === "completed" && !Boolean(job.batch_registered) && ( )} {Boolean(job.batch_registered) && ( ✓ 已入库: {String(job.batch_name ?? "")} )}
); })}
); };