153 lines
4.5 KiB
Python
153 lines
4.5 KiB
Python
"""UCD 信号 / 图案应用服务层。
|
||
|
||
服务层是 GUI ↔ Driver 的唯一通道,负责:
|
||
- 将 UI 字符串("BT.709"、"10bit"、"YCbCr 4:4:4" 等)翻译成 :class:`SignalFormat`;
|
||
- 将各 panel 的 timing 字符串翻译成 :class:`TimingSpec`;
|
||
- 协调 :meth:`IUcdDevice.configure` / ``set_pattern`` / ``apply`` 的调用顺序;
|
||
- 通过 :class:`EventBus` 让 GUI 订阅状态变化,而非主动轮询。
|
||
|
||
本层不直接 import UniTAP,也不读取 :mod:`tkinter` 变量;
|
||
所有输入都是显式参数,便于单测。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
import threading
|
||
|
||
from app.ucd_domain import (
|
||
Colorimetry,
|
||
DynamicRange,
|
||
EventBus,
|
||
PatternKind,
|
||
PatternSpec,
|
||
SignalFormat,
|
||
TimingSpec,
|
||
UcdError,
|
||
bit_depth_str_to_bpc,
|
||
color_space_to_colorimetry,
|
||
data_range_to_dynamic_range,
|
||
output_format_to_color_format,
|
||
parse_timing_str,
|
||
)
|
||
from drivers.ucd_driver import IUcdDevice
|
||
|
||
log = logging.getLogger(__name__)
|
||
|
||
|
||
# ─── 视图字符串 → 值对象 转换工具 ────────────────────────────────
|
||
|
||
|
||
def build_signal_format(
|
||
*,
|
||
color_space: str,
|
||
output_format: str,
|
||
bit_depth: str,
|
||
data_range: str = "Full",
|
||
) -> SignalFormat:
|
||
"""根据下拉框字符串组装 :class:`SignalFormat`。
|
||
|
||
各参数解析失败抛 :class:`UcdConfigError`。
|
||
"""
|
||
return SignalFormat(
|
||
color_format=output_format_to_color_format(output_format),
|
||
colorimetry=color_space_to_colorimetry(color_space),
|
||
bpc=bit_depth_str_to_bpc(bit_depth),
|
||
dynamic_range=data_range_to_dynamic_range(data_range),
|
||
)
|
||
|
||
|
||
def build_timing(timing_str: str) -> TimingSpec:
|
||
"""``"DMT 3840x2160@60Hz"`` → :class:`TimingSpec`。"""
|
||
return parse_timing_str(timing_str)
|
||
|
||
|
||
def solid_rgb_pattern(rgb: tuple[int, int, int] | list[int]) -> PatternSpec:
|
||
r, g, b = rgb[0], rgb[1], rgb[2]
|
||
return PatternSpec(kind=PatternKind.SOLID, solid_rgb=(int(r), int(g), int(b)))
|
||
|
||
|
||
def image_pattern(path: str) -> PatternSpec:
|
||
return PatternSpec(kind=PatternKind.IMAGE, image_path=path)
|
||
|
||
|
||
# ─── 服务 ────────────────────────────────────────────────────────
|
||
|
||
|
||
class SignalService:
|
||
"""协调 SignalFormat / Timing / Pattern 的写入与提交。
|
||
|
||
使用线程锁串行化所有对外的 ``apply_*`` 调用,避免多个测试线程
|
||
同时操作 UCD 造成 SDK 状态错乱。
|
||
"""
|
||
|
||
def __init__(self, device: IUcdDevice, bus: EventBus):
|
||
self._dev = device
|
||
self._bus = bus
|
||
self._lock = threading.RLock()
|
||
|
||
# -- 高层接口 ------------------------------------------------
|
||
|
||
def apply(
|
||
self,
|
||
*,
|
||
signal: SignalFormat,
|
||
timing: TimingSpec,
|
||
pattern: PatternSpec,
|
||
) -> bool:
|
||
"""一次性提交信号格式 + timing + 图案。
|
||
|
||
Returns:
|
||
``format_changed``——本次相对上一次 :meth:`apply` 是否变化。
|
||
"""
|
||
with self._lock:
|
||
log.info(
|
||
"SignalService.apply signal=%s timing=%s pattern=%s",
|
||
signal,
|
||
timing,
|
||
pattern.kind.value,
|
||
)
|
||
changed = self._dev.configure(signal, timing)
|
||
self._dev.set_pattern(pattern)
|
||
self._dev.apply()
|
||
return changed
|
||
|
||
def send_pattern(self, pattern: PatternSpec) -> None:
|
||
"""在已 configure 的信号上仅更新图案后 apply。"""
|
||
with self._lock:
|
||
log.info("SignalService.send_pattern pattern=%s", pattern.kind.value)
|
||
self._dev.set_pattern(pattern)
|
||
self._dev.apply()
|
||
|
||
def send_solid_rgb(self, rgb: tuple[int, int, int] | list[int]) -> None:
|
||
self.send_pattern(solid_rgb_pattern(rgb))
|
||
|
||
def send_image(self, path: str) -> None:
|
||
self.send_pattern(image_pattern(path))
|
||
|
||
# -- 透传给上层的查询 ---------------------------------------
|
||
|
||
@property
|
||
def device(self) -> IUcdDevice:
|
||
return self._dev
|
||
|
||
def current_resolution(self) -> tuple[int, int]:
|
||
return self._dev.current_resolution()
|
||
|
||
|
||
__all__ = [
|
||
"SignalService",
|
||
"build_signal_format",
|
||
"build_timing",
|
||
"solid_rgb_pattern",
|
||
"image_pattern",
|
||
# 重导出常用域类型方便上层 import 一次到位
|
||
"SignalFormat",
|
||
"TimingSpec",
|
||
"PatternSpec",
|
||
"PatternKind",
|
||
"Colorimetry",
|
||
"DynamicRange",
|
||
"UcdError",
|
||
]
|