112 lines
3.3 KiB
Python
112 lines
3.3 KiB
Python
"""从 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)
|