feat: implement DTW comparison, add data segmentation and persistence, and update QML UI for file imports and graphing.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""QML-facing controller for the live/review wafer dashboard."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
@@ -35,12 +36,14 @@ class SessionController(QObject):
|
||||
sensorsChanged = Signal()
|
||||
loadedFileChanged = Signal()
|
||||
clusterAveragingEnabledChanged = Signal()
|
||||
settingsChanged = Signal() # emitted when session state changes that should be persisted
|
||||
# trend: per-frame avg for live graph
|
||||
trendData = Signal(str) # JSON array of floats
|
||||
# private: marshal a worker-thread frame onto the main thread
|
||||
_liveFrame = Signal(object) # Frame
|
||||
|
||||
def __init__(self, parent: QObject | None = None) -> None:
|
||||
def __init__(self, parent: QObject | None = None,
|
||||
settings: dict | None = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._mode = MODE_REVIEW
|
||||
self._model = SessionModel()
|
||||
@@ -50,6 +53,13 @@ class SessionController(QObject):
|
||||
self._sensors: list[Sensor] = []
|
||||
self._last = None # last SessionUpdate
|
||||
self._elapsed = 0.0
|
||||
self._trend_buffer: list[float] = [] # rolling window
|
||||
self._trend_timestamps: list[float] = [] # monotonic timestamps
|
||||
|
||||
# Trend refresh timer - update every 1s
|
||||
self._trend_timer = QTimer(self)
|
||||
self._trend_timer.setInterval(1000)
|
||||
self._trend_timer.timeout.connect(self._flush_trend)
|
||||
|
||||
self._play_timer = QTimer(self)
|
||||
self._play_timer.timeout.connect(self._advance)
|
||||
@@ -70,6 +80,26 @@ class SessionController(QObject):
|
||||
self._active_clusters: list[list[int]] = []
|
||||
self._stats_tracker = ReplayStatsTracker()
|
||||
|
||||
# Restore persisted state if available
|
||||
self._load_settings(settings or {})
|
||||
|
||||
# ---- persisted settings ----
|
||||
|
||||
def _load_settings(self, settings: dict) -> None:
|
||||
"""Restore session state from persisted settings dict."""
|
||||
last_file = settings.get("session_last_file", "")
|
||||
if last_file:
|
||||
try:
|
||||
self.loadFile(last_file)
|
||||
except Exception:
|
||||
log.warning("Could not restore last session file: %s", last_file)
|
||||
|
||||
def collect_settings(self) -> dict:
|
||||
"""Return a dict of session state to persist."""
|
||||
return {
|
||||
"session_last_file": self._loaded_file,
|
||||
}
|
||||
|
||||
# ---- properties QML binds to ----
|
||||
@Property(str, notify=modeChanged)
|
||||
def mode(self) -> str: return self._mode
|
||||
@@ -257,6 +287,11 @@ class SessionController(QObject):
|
||||
self.loadedFileChanged.emit()
|
||||
self.sensorsChanged.emit()
|
||||
self._emit_current()
|
||||
# Populate stats tracker from all loaded frames for trend chart (P3.1)
|
||||
for frame in self._player._frames:
|
||||
self._stats_tracker.track(frame.seq, frame.values)
|
||||
self.trendData.emit(json.dumps(self._stats_tracker.get_trend()))
|
||||
self.settingsChanged.emit()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
@@ -383,6 +418,9 @@ class SessionController(QObject):
|
||||
|
||||
# Clear out any old data from the prev sessions
|
||||
self._model.reset()
|
||||
self._trend_buffer.clear()
|
||||
self._trend_timestamps.clear()
|
||||
self._trend_timer.start()
|
||||
self._stats_tracker.reset()
|
||||
self._last = None
|
||||
self._last_raw_frame = None
|
||||
@@ -410,6 +448,7 @@ class SessionController(QObject):
|
||||
@slot_error_boundary
|
||||
def stopStream(self) -> None:
|
||||
self._repaint_timer.stop()
|
||||
self._trend_timer.stop()
|
||||
if self._reader:
|
||||
transport = self._reader._transport
|
||||
self._reader.stop()
|
||||
@@ -442,6 +481,21 @@ class SessionController(QObject):
|
||||
self._dirty = True
|
||||
self.trendData.emit(json.dumps(self._stats_tracker.get_trend()))
|
||||
|
||||
if self._last and self._last.stats:
|
||||
avg = self._last.stats.avg
|
||||
now = time.monotonic()
|
||||
self._trend_buffer.append(avg)
|
||||
self._trend_timestamps.append(now)
|
||||
|
||||
# drop entries older than 60 secs
|
||||
cutoff = now -60
|
||||
while self._trend_timestamps and self._trend_timestamps[0] < cutoff:
|
||||
self._trend_timestamps.pop(0)
|
||||
self._trend_buffer.pop(0)
|
||||
|
||||
|
||||
|
||||
|
||||
def _flush_repaint(self) -> None:
|
||||
if not self._dirty:
|
||||
return
|
||||
@@ -491,4 +545,8 @@ class SessionController(QObject):
|
||||
self._sensor_editor.clear()
|
||||
self._reprocess_current()
|
||||
|
||||
def _flush_trend(self) -> None:
|
||||
import json
|
||||
self.trendData.emit(json.dumps(self._trend_buffer))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user