125 lines
4.6 KiB
Python
125 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""驾驶舱前端渲染计划编译脚本(issue #131 / PRD 5.5「⑤ 配置化驾驶舱」)。
|
||
|
||
把行业模板的布局 / 告警面板 **YAML 资产**编译成前端可直接 ``fetch`` 的
|
||
**RenderPlan JSON**,落到 ``web/cockpit/plans/``:
|
||
|
||
- ``plans/ti-cl4.json`` ← ``templates/ti-cl4/dashboard/cockpit.ti.yaml``
|
||
- ``plans/resin.json`` ← ``templates/resin/dashboard/cockpit.resin.yaml``
|
||
- ``plans/alarm_panel.ti.json``← ``templates/ti-cl4/dashboard/alarm_panel.ti.yaml``
|
||
|
||
切换行业模板 = 前端加载另一份 plan JSON,**前端代码零改动**(PRD 5.5 验收口径)。
|
||
|
||
编译在开发期离线完成,前端页面保持纯静态(无构建链、无后端依赖)。
|
||
本脚本是唯一依赖 PyYAML 的环节(开发期依赖,不进运行时);渲染逻辑全部复用
|
||
``core/cockpit``(#50 布局校验 / #51 渲染计划 / #52 告警面板 props),
|
||
保证前端看到的 JSON 与后端 Schema 严格一致。
|
||
|
||
用法(仓库根目录下):
|
||
python web/cockpit/scripts/build_plans.py
|
||
退出码:0 = 全部编译成功;1 = 存在失败项。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import os
|
||
import sys
|
||
|
||
import yaml # 开发期依赖(PyYAML),仅本脚本使用;前端运行时不依赖
|
||
|
||
# 仓库根目录加入 sys.path,以便 `from core.cockpit import ...`
|
||
_REPO_ROOT = os.path.dirname(
|
||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
)
|
||
sys.path.insert(0, _REPO_ROOT)
|
||
|
||
from core.cockpit import ( # noqa: E402
|
||
load_alarm_config,
|
||
load_layout,
|
||
plan_to_dict,
|
||
render_alarm_panel_props,
|
||
render_layout,
|
||
)
|
||
|
||
# 布局资产 → plan JSON 文件名
|
||
_LAYOUTS = {
|
||
"ti-cl4": "templates/ti-cl4/dashboard/cockpit.ti.yaml",
|
||
"resin": "templates/resin/dashboard/cockpit.resin.yaml",
|
||
}
|
||
|
||
# 告警面板配置资产 → plan JSON 文件名
|
||
_ALARM_PANELS = {
|
||
"alarm_panel.ti": "templates/ti-cl4/dashboard/alarm_panel.ti.yaml",
|
||
}
|
||
|
||
_OUT_DIR = os.path.join(_REPO_ROOT, "web", "cockpit", "plans")
|
||
|
||
|
||
def _load_yaml(path: str) -> dict:
|
||
with open(path, encoding="utf-8") as f:
|
||
return yaml.safe_load(f)
|
||
|
||
|
||
def _write_json(name: str, data: dict) -> str:
|
||
os.makedirs(_OUT_DIR, exist_ok=True)
|
||
out_path = os.path.join(_OUT_DIR, name + ".json")
|
||
with open(out_path, "w", encoding="utf-8") as f:
|
||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||
return out_path
|
||
|
||
|
||
def _enrich_process_view_stages(plan_dict: dict, layout_data: dict) -> None:
|
||
"""把布局资产里 process_view 的 ``stages``(四状态工艺语义)注入 plan JSON。
|
||
|
||
#51 ``_build_props`` 只透传 ``src``/``states``,四状态清单(氯化→精制→
|
||
还原→蒸馏)留在布局资产里;前端 ProcessView 组件需要它来渲染流程节点,
|
||
因此在编译期按 widget 顺序补进 ``props.stages``(specs 与源 widgets 同序)。
|
||
"""
|
||
src_widgets = layout_data.get("widgets") or []
|
||
specs = plan_dict.get("widgets") or []
|
||
for spec, raw in zip(specs, src_widgets):
|
||
if spec.get("type") == "process_view" and isinstance(raw, dict):
|
||
stages = raw.get("stages")
|
||
if stages:
|
||
spec.setdefault("props", {})["stages"] = stages
|
||
|
||
|
||
def main() -> int:
|
||
failures = 0
|
||
|
||
# 1) 布局资产 → RenderPlan JSON(#50 校验 + #51 渲染计划)
|
||
for name, rel in _LAYOUTS.items():
|
||
try:
|
||
data = _load_yaml(os.path.join(_REPO_ROOT, rel))
|
||
plan = render_layout(load_layout(data))
|
||
plan_dict = plan_to_dict(plan)
|
||
_enrich_process_view_stages(plan_dict, data)
|
||
out = _write_json(name, plan_dict)
|
||
print(f"[OK] {rel} -> {os.path.relpath(out, _REPO_ROOT)} "
|
||
f"({plan.widget_count} widgets, theme={plan.theme})")
|
||
except Exception as exc: # noqa: BLE001 - 编译脚本需聚合全部失败
|
||
failures += 1
|
||
print(f"[FAIL] {rel}: {exc}")
|
||
|
||
# 2) 告警面板配置资产 → alarm_panel props JSON(#52)
|
||
for name, rel in _ALARM_PANELS.items():
|
||
try:
|
||
data = _load_yaml(os.path.join(_REPO_ROOT, rel))
|
||
props = render_alarm_panel_props(load_alarm_config(data))
|
||
out = _write_json(name, props)
|
||
print(f"[OK] {rel} -> {os.path.relpath(out, _REPO_ROOT)} "
|
||
f"(severities={sorted(props['severityStyles'])})")
|
||
except Exception as exc: # noqa: BLE001
|
||
failures += 1
|
||
print(f"[FAIL] {rel}: {exc}")
|
||
|
||
if failures:
|
||
print(f"共 {failures} 项编译失败")
|
||
return 1
|
||
print("全部渲染计划编译完成")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|