"""从 UniTAP Pattern Generator 读取状态并格式化为日志行。""" from __future__ import annotations import logging import os import re from typing import Any import UniTAP from UniTAP.common import ColorInfo log = logging.getLogger(__name__) def _enum_display_name(name: str) -> str: spaced = re.sub(r"([a-z])([A-Z])", r"\1 \2", name) return spaced.replace("_", " ") def _format_pattern_name(pg, pattern_hint: str | None = None) -> str: if pattern_hint: return pattern_hint pattern = getattr(pg, "_pattern", None) if isinstance(pattern, UniTAP.VideoPattern): return _enum_display_name(pattern.name) if isinstance(pattern, str): return os.path.basename(pattern) or "Image File" pattern_id = getattr(pg, "_pattern_id", None) if pattern_id is not None: return _enum_display_name(getattr(pattern_id, "name", str(pattern_id))) return "—" def _format_cef(ci: ColorInfo) -> str: fmt_map = { ColorInfo.ColorFormat.CF_RGB: "RGB", ColorInfo.ColorFormat.CF_YCbCr_422: "YCbCr 4:2:2", ColorInfo.ColorFormat.CF_YCbCr_444: "YCbCr 4:4:4", ColorInfo.ColorFormat.CF_YCbCr_420: "YCbCr 4:2:0", ColorInfo.ColorFormat.CF_Y_ONLY: "Y only", ColorInfo.ColorFormat.CF_RAW: "RAW", ColorInfo.ColorFormat.CF_DSC: "DSC", } fmt = fmt_map.get( ci.color_format, ci.color_format.name.replace("CF_", "").replace("_", " "), ) if ci.color_format == ColorInfo.ColorFormat.CF_RGB: range_str = ( "Legacy RGB mode" if ci.dynamic_range == ColorInfo.DynamicRange.DR_VESA else "Limited range" ) elif ci.dynamic_range == ColorInfo.DynamicRange.DR_VESA: range_str = "Full range" else: range_str = "Limited range" return f"{fmt}/{range_str}" def _max_bitrate_gbps(pg, bpp: int) -> float: caps = pg._caps() effective_bpp = bpp if bpp > 0 else 24 return round(caps.max_pix_rate * effective_bpp / 1_000_000_000.0, 3) def collect_pattern_generator_status( pg, *, pattern_hint: str | None = None, ) -> dict[str, Any]: vm = pg.get_stream_video_mode() pg_status = pg.status() ci = vm.color_info refresh_hz = vm.timing.frame_rate / 1000.0 if vm.timing.frame_rate else 0.0 timing = f"{vm.timing.hactive} x {vm.timing.vactive} @ {refresh_hz:g}Hz" current_br = vm.bit_rate max_br = _max_bitrate_gbps(pg, ci.bpp) return { "total_bitrate": f"{current_br:.3f} / {max_br:.3f} Gbps", "timing": timing, "pattern": _format_pattern_name(pg, pattern_hint), "cef": _format_cef(ci), "bpc": str(ci.bpc), "status": str(pg_status.error), } def format_pattern_generator_status_log(status: dict[str, Any]) -> list[str]: return [ f"Total bitrate: {status['total_bitrate']}", f" Timing: {status['timing']}", f" Pattern: {status['pattern']}", f" CEF: {status['cef']}", f" BPC: {status['bpc']}", f" Status: {status['status']}", ] def read_pattern_generator_status_lines( pg, *, pattern_hint: str | None = None, ) -> list[str]: status = collect_pattern_generator_status(pg, pattern_hint=pattern_hint) return format_pattern_generator_status_log(status)