refactor: implement thread-safe hardware operations, port caching, and refined state management in the device controller

This commit is contained in:
jack
2026-07-02 22:33:44 -07:00
parent da43aeb1f6
commit 9e28a25695
7 changed files with 193 additions and 68 deletions
@@ -232,7 +232,8 @@ class SessionController(QObject):
def setMode(self, mode: str) -> None:
if mode == self._mode:
return
self.stopStream()
if mode != "live":
self.stopStream()
self._play_timer.stop()
self._mode = mode
self.modeChanged.emit()
@@ -394,6 +395,9 @@ class SessionController(QObject):
@Slot(str, str)
@slot_error_boundary
def startStream(self, port: str, family_code: str = "") -> None:
if self._reader is not None:
log.warning("startStream: StreamReader is already running.")
return
from pygui.backend.wafer.wafer_layouts import load_layout_for_wafer_id
from pygui.serialcomm.serial_port import SerialPort # transport open
@@ -449,13 +453,24 @@ class SessionController(QObject):
cmd = b"D2" + SerialPort.COMMAND_PAD.encode()
transport.write(cmd)
import weakref
weak_self = weakref.ref(self)
def on_error(exc: Exception):
log.error("Live stream error: %s", exc)
self._liveError.emit()
ref = weak_self()
if ref is not None:
ref._liveError.emit()
ref.stopStream()
def on_frame(frame: Frame):
ref = weak_self()
if ref is not None:
ref._liveFrame.emit(frame)
self._reader = StreamReader(
transport, parse_binary_frame,
on_frame=lambda f: self._liveFrame.emit(f),
on_frame=on_frame,
on_error=on_error,
family_code=family_code or "A")
self._reader.start()
@@ -474,10 +489,9 @@ class SessionController(QObject):
self.liveStatsChanged.emit()
if self._reader:
transport = self._reader._transport
self._reader.stop()
self._reader = None
# Send 'D2S' command padded to 512 bytes to stop the stream
# Send BEFORE stopping the reader which will close the port on thread exit
if transport and transport.is_open:
try:
cmd = b"D2S" + (b"F" * 509)
@@ -486,6 +500,9 @@ class SessionController(QObject):
except Exception as exc:
log.error("Error sending stop command: %s", exc)
self._reader.stop()
self._reader = None
self.stateChanged.emit()
self.stopRecording()