feat: implement slot_error_boundary decorator and add unit tests for session and device controllers
This commit is contained in:
@@ -14,6 +14,7 @@ from pygui.backend.models.frame_player import FramePlayer, frames_from_wafer_dat
|
||||
from pygui.backend.models.sensor_editor import SensorEditor
|
||||
from pygui.backend.models.session_model import SessionModel
|
||||
from pygui.backend.models.threshold_classifier import ThresholdConfig
|
||||
from pygui.backend.utils import slot_error_boundary
|
||||
from pygui.backend.wafer.zwafer_models import Sensor
|
||||
from pygui.backend.wafer.zwafer_parser import ZWaferParser
|
||||
from pygui.serialcomm.stream_reader import StreamReader
|
||||
@@ -182,6 +183,7 @@ class SessionController(QObject):
|
||||
|
||||
# ---- mode + thresholds ----
|
||||
@Slot(str)
|
||||
@slot_error_boundary
|
||||
def setMode(self, mode: str) -> None:
|
||||
if mode == self._mode:
|
||||
return
|
||||
@@ -191,6 +193,7 @@ class SessionController(QObject):
|
||||
self.modeChanged.emit()
|
||||
|
||||
@Slot(float, float, bool)
|
||||
@slot_error_boundary
|
||||
def setThresholds(self, set_point: float, margin: float, auto: bool) -> None:
|
||||
self._model.set_thresholds(ThresholdConfig(set_point, margin, auto))
|
||||
if self._last: # re-band current frame in place
|
||||
@@ -198,6 +201,7 @@ class SessionController(QObject):
|
||||
|
||||
# ---- review: file load + playback ----
|
||||
@Slot(str)
|
||||
@slot_error_boundary
|
||||
def loadFile(self, file_path: str) -> None:
|
||||
from pathlib import Path
|
||||
|
||||
@@ -250,30 +254,37 @@ class SessionController(QObject):
|
||||
self._emit_current()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
def play(self) -> None:
|
||||
self._play_timer.start(self._next_interval_ms())
|
||||
|
||||
@Slot()
|
||||
def pause(self) -> None: self._play_timer.stop()
|
||||
@slot_error_boundary
|
||||
def pause(self) -> None:
|
||||
self._play_timer.stop()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
def stop(self) -> None:
|
||||
self._play_timer.stop()
|
||||
self._player.seek(0)
|
||||
self._emit_current()
|
||||
|
||||
@Slot(int)
|
||||
@slot_error_boundary
|
||||
def step(self, delta: int) -> None:
|
||||
self._play_timer.stop()
|
||||
self._player.step(delta)
|
||||
self._emit_current()
|
||||
|
||||
@Slot(int)
|
||||
@slot_error_boundary
|
||||
def seek(self, i: int) -> None:
|
||||
self._player.seek(i)
|
||||
self._emit_current()
|
||||
|
||||
@Slot(float)
|
||||
@slot_error_boundary
|
||||
def setSpeed(self, x: float) -> None:
|
||||
self._speed = max(0.1, x)
|
||||
if self._play_timer.isActive():
|
||||
@@ -325,6 +336,7 @@ class SessionController(QObject):
|
||||
|
||||
# ---- live: stream start/stop ----
|
||||
@Slot(str, str)
|
||||
@slot_error_boundary
|
||||
def startStream(self, port: str, family_code: str = "") -> None:
|
||||
|
||||
from pygui.backend.wafer.wafer_layouts import load_layout_for_wafer_id
|
||||
@@ -389,6 +401,7 @@ class SessionController(QObject):
|
||||
self.stateChanged.emit()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
def stopStream(self) -> None:
|
||||
self._repaint_timer.stop()
|
||||
if self._reader:
|
||||
@@ -409,6 +422,7 @@ class SessionController(QObject):
|
||||
self.stopRecording()
|
||||
|
||||
@Slot(object)
|
||||
@slot_error_boundary
|
||||
def _on_live_frame(self, frame: Frame) -> None:
|
||||
# Main thread (queued). Record raw, process edited; mark dirty for repaint.
|
||||
self._last_raw_frame = frame
|
||||
@@ -428,35 +442,41 @@ class SessionController(QObject):
|
||||
|
||||
# ---- recording ----
|
||||
@Slot(str, str)
|
||||
@slot_error_boundary
|
||||
def startRecording(self, path: str, serial: str = "") -> None:
|
||||
self._recorder.start(path, self._sensors, serial)
|
||||
self.recordingChanged.emit()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
def stopRecording(self) -> None:
|
||||
if self._recorder.is_recording:
|
||||
self._recorder.stop()
|
||||
self.recordingChanged.emit()
|
||||
|
||||
@Slot(int, float)
|
||||
@slot_error_boundary
|
||||
def replaceSensor(self, index: int, value: float) -> None:
|
||||
"""Override sensor `index` to display `value` every frame."""
|
||||
self._sensor_editor.set_replacement(index, value)
|
||||
self._reprocess_current()
|
||||
|
||||
@Slot(int, float)
|
||||
@slot_error_boundary
|
||||
def offsetSensor(self, index: int, delta: float) -> None:
|
||||
"""Shift sensor `index` by `delta` every frame."""
|
||||
self._sensor_editor.set_offset(index, delta)
|
||||
self._reprocess_current()
|
||||
|
||||
@Slot(int)
|
||||
@slot_error_boundary
|
||||
def clearSensorEdit(self, index: int) -> None:
|
||||
"""Remove all overrides for sensor `index`."""
|
||||
self._sensor_editor.clear(index)
|
||||
self._reprocess_current()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
def clearSensorEdits(self) -> None:
|
||||
"""Remove all sensor overrides."""
|
||||
self._sensor_editor.clear()
|
||||
|
||||
Reference in New Issue
Block a user