feat: Wafer Map integration
## Backend — Session & playback - Add SessionController (QML-facing live/review controller) with loadedFile property for active-file highlight binding - Add SessionModel, FramePlayer, and CsvRecorder for state machine, frame seek/step/speed, and CSV recording - Add SensorEditor for per-sensor value overrides (replace + offset)
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
"""Continuos serial frame reader (Live-mode source)"""
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
import threading
|
||||
from typing import Callable
|
||||
|
||||
from pygui.backend.frame import Frame
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
ParseLine = Callable[[str, int], Frame]
|
||||
|
||||
class StreamReader:
|
||||
|
||||
def __init__(self, transport, parse_line: ParseLine,
|
||||
on_frame: Callable[[Frame], None],
|
||||
on_error: Callable[[Exception], None] | None = None ) -> None:
|
||||
self._transport = transport
|
||||
self._parse = parse_line
|
||||
self._on_frame = on_frame
|
||||
self._on_error = on_error or (lambda e: None)
|
||||
self._thread: threading.Thread | None = None
|
||||
self._stop = threading.Event()
|
||||
self._seq = 0
|
||||
self.error_count = 0
|
||||
|
||||
def start(self) -> None:
|
||||
self._stop.clear()
|
||||
self._thread = threading.Thread(target=self._run, daemon = True)
|
||||
self._thread.start()
|
||||
|
||||
def _run(self) -> None:
|
||||
try:
|
||||
while not self._stop.is_set():
|
||||
raw = self._transport.readline()
|
||||
if not raw:
|
||||
continue
|
||||
text = raw.decode(errors="ignore").strip()
|
||||
if not text:
|
||||
continue
|
||||
try:
|
||||
frame= self._parse(text, self._seq)
|
||||
self._seq += 1
|
||||
self._on_frame(frame)
|
||||
except Exception as exc:
|
||||
self.error_count += 1
|
||||
self._on_error(exc)
|
||||
finally:
|
||||
try:
|
||||
self._transport.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def stop(self) -> None:
|
||||
self._stop.set()
|
||||
if self._thread is not None and self._thread.is_alive():
|
||||
self._thread.join(timeout = 2.0)
|
||||
self._thread = None
|
||||
Reference in New Issue
Block a user