from __future__ import annotations import asyncio import os import struct import time from pathlib import Path from utils.logger import get_logger logger = get_logger(__name__) RECORDINGS_DIR = Path(__file__).resolve().parent.parent / "data" / "recordings" RECORDINGS_DIR.mkdir(parents=True, exist_ok=True) class TelemetryRecorder: def __init__(self, listener): self._listener = listener self._file = None self._recording = False self._auto_mode = False self._start_time = 0.0 self._last_race_on = False @property def is_recording(self) -> bool: return self._recording @property def current_file(self) -> str | None: return str(self._file.name) if self._file else None def start(self): if self._recording: return ts = time.strftime("%Y%m%d_%H%M%S") self._file = open(RECORDINGS_DIR / f"rec_{ts}.tsr", "wb") self._recording = True self._start_time = time.time() logger.info("Recording started: %s", self._file.name) def stop(self): if not self._recording: return self._recording = False if self._file: self._file.close() self._file = None logger.info("Recording stopped") def set_auto(self, enabled: bool): self._auto_mode = enabled self._last_race_on = False if not enabled: self.stop() def write_packet(self, data: bytes): if self._auto_mode: try: is_race = struct.unpack_from(" bool: return self._playing def start(self, filename: str): if self._playing: return path = RECORDINGS_DIR / filename if not path.exists(): logger.error("Recording not found: %s", filename) return loop = asyncio.get_event_loop() self._task = loop.create_task(self._play_loop(path)) self._playing = True logger.info("Playback started: %s", filename) def stop(self): self._playing = False if self._task and not self._task.done(): self._task.cancel() self._task = None logger.info("Playback stopped") async def _play_loop(self, path: Path): try: data = path.read_bytes() offset = 0 play_start = time.time() while offset < len(data) and self._playing: elapsed_ms, length = struct.unpack_from(" 0: await asyncio.sleep(min(delay, 0.5)) self._listener._handle_packet(packet, ("127.0.0.1", 0)) except asyncio.CancelledError: pass except Exception as e: logger.error("Playback error: %s", e) finally: self._playing = False @staticmethod def list_recordings() -> list[dict]: return [{"name": f.name, "size": f.stat().st_size, "time": f.stat().st_mtime} for f in sorted(RECORDINGS_DIR.glob("*.tsr"), key=lambda x: x.stat().st_mtime, reverse=True)]