feat: TurboSu initial release - racing telemetry dashboard

This commit is contained in:
Yei.J. (AskaEth)
2026-07-25 15:23:40 +08:00
commit d61bb61a83
61 changed files with 5270 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
from __future__ import annotations
import json
import uuid
from dataclasses import dataclass, field, asdict
from datetime import datetime
from pathlib import Path
from typing import Any
from utils.logger import get_logger
logger = get_logger(__name__)
DATA_DIR = Path(__file__).resolve().parent.parent / "data"
SCENES_DIR = DATA_DIR / "scenes"
@dataclass
class DashboardPlacement:
dashboard_id: str = ""
x: float = 0.0
y: float = 0.0
width: int = 400
height: int = 300
z_index: int = 0
scale_x: float = 1.0
scale_y: float = 1.0
aspect_ratio: str = "auto"
render_mode: str = "contain"
custom_config: dict[str, Any] = field(default_factory=dict)
@dataclass
class SceneCanvas:
width: int = 1920
height: int = 1080
label: str = "16:9"
placements: list[DashboardPlacement] = field(default_factory=list)
@dataclass
class Scene:
id: str = field(default_factory=lambda: str(uuid.uuid4()))
name: str = "New Scene"
game_id: str = ""
description: str = ""
canvases: list[SceneCanvas] = field(default_factory=list)
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
def to_dict(self) -> dict[str, Any]:
d = asdict(self)
d["canvases"] = [asdict(c) for c in self.canvases]
return d
@classmethod
def from_dict(cls, d: dict[str, Any]) -> Scene:
canvases = []
for cd in d.get("canvases", []):
placements = [DashboardPlacement(**p) for p in cd.get("placements", [])]
canvases.append(SceneCanvas(
width=cd.get("width", 1920),
height=cd.get("height", 1080),
label=cd.get("label", ""),
placements=placements,
))
return cls(
id=d.get("id", str(uuid.uuid4())),
name=d.get("name", "New Scene"),
game_id=d.get("game_id", ""),
description=d.get("description", ""),
canvases=canvases,
created_at=d.get("created_at", datetime.now().isoformat()),
updated_at=d.get("updated_at", datetime.now().isoformat()),
)
class SceneManager:
def __init__(self):
SCENES_DIR.mkdir(parents=True, exist_ok=True)
def list_all(self, game_id: str | None = None) -> list[dict[str, Any]]:
scenes = []
for f in SCENES_DIR.glob("*.json"):
try:
with open(f, "r", encoding="utf-8") as fp:
data = json.load(fp)
scene = Scene.from_dict(data)
if game_id is None or scene.game_id == game_id:
scenes.append(scene.to_dict())
except Exception as e:
logger.error("Failed to load scene %s: %s", f.name, e)
return scenes
def get(self, scene_id: str) -> dict[str, Any] | None:
filepath = SCENES_DIR / f"{scene_id}.json"
if not filepath.exists():
return None
try:
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
return Scene.from_dict(data).to_dict()
except Exception as e:
logger.error("Failed to read scene %s: %s", scene_id, e)
return None
def save(self, scene: Scene) -> bool:
scene.updated_at = datetime.now().isoformat()
filepath = SCENES_DIR / f"{scene.id}.json"
try:
with open(filepath, "w", encoding="utf-8") as f:
json.dump(scene.to_dict(), f, indent=2, ensure_ascii=False)
logger.info("Scene saved: %s", scene.id)
return True
except Exception as e:
logger.error("Failed to save scene %s: %s", scene.id, e)
return False
def delete(self, scene_id: str) -> bool:
filepath = SCENES_DIR / f"{scene_id}.json"
if filepath.exists():
filepath.unlink()
logger.info("Scene deleted: %s", scene_id)
return True
return False
def export_scene(self, scene_id: str) -> dict[str, Any] | None:
scene = self.get(scene_id)
if not scene:
return None
return {
"type": "scene",
"version": "1.0",
"data": scene,
}
def import_scene(self, data: dict[str, Any]) -> bool:
if data.get("type") != "scene":
return False
scene_data = data.get("data", {})
if "id" not in scene_data:
scene_data["id"] = str(uuid.uuid4())
scene = Scene.from_dict(scene_data)
return self.save(scene)
scene_manager = SceneManager()