新建UCD模块DDD重构文件
This commit is contained in:
152
app/services/ucd_service.py
Normal file
152
app/services/ucd_service.py
Normal 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",
|
||||
]
|
||||
Reference in New Issue
Block a user