新建UCD模块DDD重构文件

This commit is contained in:
xinzhu.yin
2026-05-24 10:49:28 +08:00
parent 9b2bc44e17
commit 3ce1574320
4 changed files with 978 additions and 2 deletions

152
app/services/ucd_service.py Normal file
View File

@@ -0,0 +1,152 @@
"""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",
]