Files
pyGUI/src/pygui/serialcomm/stream_reader.py
T

59 lines
1.8 KiB
Python

"""Continuos serial frame reader (Live-mode source)"""
from __future__ import annotations
import logging
import threading
from typing import Callable
from pygui.backend.models.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