refactor: standardize codebase formatting, optimize import orders, and update GUI resolution to 1080p
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# ===== Models Sub-package =====
|
||||
from pygui.backend.models.data_model import TemperatureTableModel
|
||||
from pygui.backend.models.frame import Frame
|
||||
from pygui.backend.models.frame_player import FramePlayer, frames_from_wafer_data
|
||||
from pygui.backend.models.frame_stats import Stats, compute_stats
|
||||
from pygui.backend.models.session_model import SessionModel, SessionUpdate
|
||||
from pygui.backend.models.data_model import TemperatureTableModel
|
||||
from pygui.backend.models.sensor_editor import SensorEditor
|
||||
from pygui.backend.models.threshold_classifier import ThresholdConfig, classify, resolve_bounds
|
||||
from pygui.backend.models.session_model import SessionModel, SessionUpdate
|
||||
from pygui.backend.models.stability_detector import StabilityDetector
|
||||
from pygui.backend.models.threshold_classifier import ThresholdConfig, classify, resolve_bounds
|
||||
|
||||
__all__ = [
|
||||
"Frame", "FramePlayer", "frames_from_wafer_data",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from __future__ import annotations
|
||||
from __future__ import annotations
|
||||
|
||||
from pygui.backend.models.frame import Frame
|
||||
from pygui.backend.wafer.zwafer_models import ZWaferData
|
||||
|
||||
|
||||
def frames_from_wafer_data(data: ZWaferData, records) -> list[Frame]:
|
||||
"""Build Frames from parsed DataRecords (records = list[DataRecord].)"""
|
||||
return [Frame(seq=i, time=r.time, values=list(r.values)) for i, r in enumerate(records)]
|
||||
@@ -10,36 +12,36 @@ class FramePlayer:
|
||||
def __init__(self) -> None:
|
||||
self._frames: list[Frame] =[]
|
||||
self._i =0
|
||||
|
||||
|
||||
def load(self, frames: list[Frame]) -> "FramePlayer":
|
||||
self._frames = frames
|
||||
self._i = 0
|
||||
return self
|
||||
|
||||
|
||||
@property
|
||||
def total(self) -> int:
|
||||
return len(self._frames)
|
||||
|
||||
|
||||
@property
|
||||
def index(self) -> int:
|
||||
return self._i
|
||||
|
||||
return self._i
|
||||
|
||||
@property
|
||||
def at_end(self) -> bool:
|
||||
return self._i >= len(self._frames) -1
|
||||
|
||||
|
||||
def current(self) -> Frame | None:
|
||||
if not self._frames:
|
||||
return None
|
||||
return self._frames[self._i]
|
||||
|
||||
|
||||
def _clamp(self, i:int):
|
||||
return max(0, min(i, len(self._frames) - 1)) if self._frames else 0
|
||||
|
||||
|
||||
def step(self, delta: int) -> "FramePlayer":
|
||||
self._i = self._clamp(self._i + delta)
|
||||
return self
|
||||
|
||||
|
||||
def seek(self, i: int) -> "FramePlayer":
|
||||
self._i = self._clamp(i)
|
||||
return self
|
||||
@@ -48,4 +50,4 @@ class FramePlayer:
|
||||
"""Wall-clock ms until the next frame based on recorded timestamps; 0 if at end."""
|
||||
if self._i + 1 >= len(self._frames):
|
||||
return 0.0
|
||||
return (self._frames[self._i + 1].time - self._frames[self._i].time) * 1000.0
|
||||
return (self._frames[self._i + 1].time - self._frames[self._i].time) * 1000.0
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Per-frame descriptive statistics"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@@ -7,16 +7,16 @@ class SensorEditor:
|
||||
def __init__(self) -> None:
|
||||
self._replacements: dict[int, float] = {}
|
||||
self._offsets: dict[int, float] = {}
|
||||
|
||||
|
||||
|
||||
|
||||
def set_replacement(self, index: int, value: float) -> None:
|
||||
"""Force sensor `index` to read `value` for every frame this session."""
|
||||
self._replacements[index] = value
|
||||
|
||||
|
||||
def set_offset(self, index: int, value: float) -> None:
|
||||
"""Shift sensor `index` by `delta` (applied after any replacement)."""
|
||||
self._offsets[index] = value
|
||||
|
||||
|
||||
def clear(self, index: int | None = None) -> None:
|
||||
"""Remove overrides. Pass an index to clear one sensor; omit to clear all."""
|
||||
if index is None:
|
||||
@@ -27,16 +27,16 @@ class SensorEditor:
|
||||
self._offsets.pop(index, None)
|
||||
def has_overrides(self) -> bool:
|
||||
return bool(self._replacements or self._offsets)
|
||||
|
||||
|
||||
def active_indices(self) -> list[int]:
|
||||
"""Return sorted sensor indices that have any override."""
|
||||
return sorted(set(self._replacements) | set(self._offsets))
|
||||
|
||||
|
||||
|
||||
|
||||
def apply(self, values: list[float]) -> list[float]:
|
||||
"""Return a new list wit all overrides applied (original unchanged)"""
|
||||
if not self.has_overrides():
|
||||
return list(values)
|
||||
return list(values)
|
||||
result = list(values)
|
||||
for i in range(len(result)):
|
||||
v = result[i]
|
||||
@@ -45,4 +45,4 @@ class SensorEditor:
|
||||
if i in self._offsets:
|
||||
v += self._offsets[i]
|
||||
result[i] = v
|
||||
return result
|
||||
return result
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from pygui.backend.models.frame import Frame
|
||||
@@ -6,6 +7,7 @@ from pygui.backend.models.frame_stats import Stats, compute_stats
|
||||
from pygui.backend.models.stability_detector import StabilityDetector
|
||||
from pygui.backend.models.threshold_classifier import ThresholdConfig, classify, resolve_bounds
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SessionUpdate:
|
||||
seq: int
|
||||
@@ -15,18 +17,18 @@ class SessionUpdate:
|
||||
state: str
|
||||
target: float
|
||||
margin: float
|
||||
|
||||
|
||||
class SessionModel:
|
||||
def __init__(self, thresholds: ThresholdConfig | None = None) -> None:
|
||||
self._config = thresholds or ThresholdConfig()
|
||||
self._stability = StabilityDetector()
|
||||
|
||||
|
||||
def set_thresholds(self, config: ThresholdConfig) -> None:
|
||||
self._config = config
|
||||
|
||||
|
||||
def reset(self) -> None:
|
||||
self._stability.reset()
|
||||
|
||||
|
||||
def process(self, frame: Frame) -> SessionUpdate:
|
||||
stats = compute_stats(frame.values)
|
||||
target, margin = resolve_bounds(frame.values, self._config)
|
||||
@@ -34,6 +36,6 @@ class SessionModel:
|
||||
# State always track the user's process set_point, even in auto-color mode.
|
||||
state = self._stability.update(stats.avg, frame.time, self._config.set_point)
|
||||
return SessionUpdate(
|
||||
seq=frame.seq, values=frame.values, bands=bands,
|
||||
seq=frame.seq, values=frame.values, bands=bands,
|
||||
stats=stats, state=state, target=target, margin=margin,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Detect process state (Idle, Ramp, Set) from the running average temp"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
STATE_IDLE = "idle"
|
||||
@@ -7,28 +8,27 @@ STATE_RAMP = "ramp"
|
||||
STATE_SET = "set"
|
||||
|
||||
|
||||
class StabilityDetector:
|
||||
def __init__(self, idle_below: float = 50.0, tolerance: float = 1.0,
|
||||
class StabilityDetector:
|
||||
def __init__(self, idle_below: float = 50.0, tolerance: float = 1.0,
|
||||
settle_seconds: float = 10.0) -> None:
|
||||
self._idle_below = idle_below
|
||||
self._tolerance = tolerance
|
||||
self._settle_seconds = settle_seconds
|
||||
self._near_since: Optional[float] = None # When avg entered the +- tolerance band
|
||||
|
||||
|
||||
|
||||
|
||||
def reset(self) -> None:
|
||||
self._near_since = None
|
||||
|
||||
|
||||
def update(self, avg: float, time: float, set_point: float) ->str:
|
||||
if avg < self._idle_below:
|
||||
self._near_since = None
|
||||
return STATE_IDLE
|
||||
if abs(avg - set_point) <= self._tolerance:
|
||||
if self._near_since is None:
|
||||
self._near_since = time
|
||||
self._near_since = time
|
||||
if time - self._near_since >= self._settle_seconds:
|
||||
return STATE_SET
|
||||
return STATE_RAMP
|
||||
self._near_since = None
|
||||
return STATE_RAMP
|
||||
|
||||
@@ -4,11 +4,12 @@ Auto mode derives target=mean, margin=1
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
BAND_IN = "in_range"
|
||||
BAND_HIGH = "high"
|
||||
BAND_HIGH = "high"
|
||||
BAND_LOW = "low"
|
||||
|
||||
|
||||
@@ -39,4 +40,4 @@ def classify(value: float, target: float, margin: float) -> str:
|
||||
|
||||
def classify_all(values: list[float], cfg: ThresholdConfig) -> list[str]:
|
||||
target, margin = resolve_bounds(values, cfg)
|
||||
return [classify(v, target, margin)for v in values ]
|
||||
return [classify(v, target, margin)for v in values ]
|
||||
|
||||
Reference in New Issue
Block a user