feat: add record saving

This commit is contained in:
2025-10-24 18:15:10 +02:00
parent db112ada4c
commit 8ad97785b8
2 changed files with 103 additions and 4 deletions

49
src/record_file.py Normal file
View File

@@ -0,0 +1,49 @@
from pathlib import Path
import struct
import time
from typing import Literal
from src.snapshot import Snapshot
class RecordFile:
VERSION = 1
def __init__(self, path: str | Path, mode: Literal["w", "r"]) -> None:
self.path: str | Path = path
self.mode: Literal["w", "r"] = mode
self.file = open(self.path, self.mode + "b")
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.file.close()
def write_header(self, n_snapshots: int):
data: bytes = struct.pack(
">IId", self.VERSION, n_snapshots, time.time())
self.file.write(data)
def write_snapshots(self, snapshots: list[Snapshot]):
self.write_header(len(snapshots))
for snapshot in snapshots:
data: bytes = snapshot.pack()
self.file.write(struct.pack(">I", len(data)) + data)
def read_snapshots(self) -> list[Snapshot]:
version: int = struct.unpack(">I", self.file.read(4))[0]
if version != self.VERSION:
raise ValueError(
f"Cannot parse record file with format version {version} (current version: {self.VERSION})")
n_snapshots: int
timestamp: float
n_snapshots, timestamp = struct.unpack(">Id", self.file.read(12))
snapshots: list[Snapshot] = []
for _ in range(n_snapshots):
size: int = struct.unpack(">I", self.file.read(4))[0]
snapshots.append(Snapshot.unpack(self.file.read(size)))
return snapshots

View File

@@ -1,11 +1,15 @@
import os
from pathlib import Path
import socket
import struct
from typing import Optional
from PyQt6 import uic
from PyQt6.QtCore import QObject, QThread, QTimer, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import QMainWindow
from src.command import CarControl, Command, ControlCommand, RecordingCommand
from src.record_file import RecordFile
from src.recorder_ui import Ui_Recorder
from src.snapshot import Snapshot
@@ -84,10 +88,23 @@ class RecorderClient(QObject):
self.socket.close()
class ThreadedSaver(QThread):
def __init__(self, path: str | Path, snapshots: list[Snapshot]):
super().__init__()
self.path: str | Path = path
self.snapshots: list[Snapshot] = snapshots
def run(self):
with RecordFile(self.path, "w") as f:
f.write_snapshots(self.snapshots)
class RecorderWindow(Ui_Recorder, QMainWindow):
close_signal: pyqtSignal = pyqtSignal()
send_signal: pyqtSignal = pyqtSignal(object)
SAVE_DIR: Path = Path(__file__).parent.parent / "records"
def __init__(self, host: str, port: int) -> None:
super().__init__()
@@ -147,9 +164,10 @@ class RecorderWindow(Ui_Recorder, QMainWindow):
self.saveRecordButton.clicked.connect(self.save_record)
self.saving_worker: Optional[ThreadedSaver] = None
self.recording = False
self.recorded_data = []
self.snapshots: list[Snapshot] = []
self.client_thread.start()
def on_car_controlled(self, control: CarControl, active: bool):
@@ -171,12 +189,44 @@ class RecorderWindow(Ui_Recorder, QMainWindow):
)
def save_record(self):
pass
if self.saving_worker is not None:
print("Already saving !")
return
if len(self.snapshots) == 0:
print("No data to save !")
return
if self.recording:
self.toggle_record()
self.saveRecordButton.setText("Saving ...")
self.SAVE_DIR.mkdir(exist_ok=True)
record_name: str = "record_%d.rec"
fid = 0
while os.path.exists(self.SAVE_DIR / (record_name % fid)):
fid += 1
self.saving_worker = ThreadedSaver(
self.SAVE_DIR / (record_name % fid), self.snapshots)
self.snapshots = []
self.nbrSnapshotSaved.setText("0")
self.saving_worker.finished.connect(self.on_record_save_done)
self.saving_worker.start()
def on_record_save_done(self):
if self.saving_worker is None:
return
print("Recorded data saved to", self.saving_worker.path)
self.saving_worker = None
self.saveRecordButton.setText("Save")
@pyqtSlot(Snapshot)
def on_snapshot_received(self, snapshot: Snapshot):
self.recorded_data.append(snapshot)
self.nbrSnapshotSaved.setText(str(len(self.recorded_data)))
self.snapshots.append(snapshot)
self.nbrSnapshotSaved.setText(str(len(self.snapshots)))
def shutdown(self):
self.close_signal.emit()