Procházet zdrojové kódy

2.10.1 交付文档对外版: 去标识化(禁用词硬门+大小写不敏感) + 三份各增多场适用性章节; 表号按章编号+引用闭合检查; 修 figs/表格状态列取值

zhouyang.xie před 2 týdny
rodič
revize
d9c1ecbe91

binární
docs/figures/fig-dat-01-数据族体量.png


binární
docs/figures/fig-dat-02-数据到产物.png


binární
docs/figures/fig-dat-03-收资项覆盖.png


binární
docs/figures/fig-dat-04-时间覆盖.png


binární
docs/figures/fig-des-01-总体架构.png


binární
docs/figures/fig-req-01-需求演进时间轴.png


binární
docs/figures/fig-req-02-角色场景页面.png


binární
docs/figures/fig-req-04-验收门分类.png


+ 50 - 0
docs/src/_guanlan_check.txt

@@ -0,0 +1,50 @@
+观澜·如东样板 v2 自检 @ F:\temp\app

+  [OK] Python ≥ 3.11 — 3.12.10 (F:\temp\app\.venv\Scripts\python.exe)

+  [OK] 源码可编译 (230 个文件, 无非法转义/语法错)

+  [OK] 语言包 (前后端界面文案 zh/en 成对, 845 条)

+  [OK] 子进程口径: run_text(capture_output) 可用 — src/proc.py (capture_output 与显式 stdout 互斥, 冲突会让 job_running 误判任务已死)

+  [OK] 子进程口径: spawn(log=) 输出进日志 — CREATE_NO_WINDOW 会吸走句柄, 不显式继承则日志空白 (重算看起来卡住)

+  [OK] 子进程口径: 无窗口位已设 (CREATE_NO_WINDOW) — flags=0x8000200

+  [OK] 入口脚本引用闭合 (48 条: start.bat→guanlan_start_hidden.py 等) — 每个入口引用的文件都在位

+  [OK] 入口脚本编码守则 (.ps1 = UTF-8 BOM + CRLF; .bat = 纯 ASCII/CRLF/无 BOM/无 `<<`; .sh = LF) — 编码与字符都对 (PS 5.1 才不会把中文按 GBK 解错; 批处理里不放中文/重定向符)

+  [OK] 版本管理: 观澜·如东样板 v2 v2.10.1(记录表与代码一致, 包名 app_guanlang_v2.10.1.zip)

+  [OK] 卸载入口在位 (uninstall.bat / uninstall.sh → scripts/guanlan_uninstall.py) — 2/2 个入口

+  [FAIL] 交付文档三件 (需求分析/设计说明/数据要求说明 · v2.10.1) — 缺 1 件: 需求分析_观澜_2.10.1.docx;同名旧版残留: 需求分析_观澜_2.10.0.docx —— 跑 python scripts/delivery_docs_build.py 重建

+  [OK] 反向呼应 (输出→功能/算法→输入): 成立 3548 件 · 不成立 0 件 · 人工件 0 件 · 无法验证 0 件 · 未归类 0 件 — 每件产物都能指到生成端与输入, 或如实标了 shipped/人工件(docs §13.6)

+  [OK] 页面归口: 页面是否随数据变 / 是否陈旧 (configs/portal_pages.yaml) — 检查 21 项: 不一致 0 · 已知缺口 19 (引用悬空/无溯源, 登记表明写理由)

+  [OK] 配置统一: 目录约定/命名/内容/引用闭合/不手拼路径 — 无不一致 · 已知缺口/白名单 14 · 提示 7

+  [OK] 日志统一: logs/ 唯一落点/命名/行格式/保留策略 — 无不一致 · 已知缺口/白名单 0 · 提示 87

+  [OK] 输入数据放置体检 (data/raw 结构/命名/源类) — 26,503 件源件 · 结构合规 · 9 条提示

+  [OK] 依赖 numpy

+  [OK] 依赖 pandas

+  [OK] 依赖 pyarrow

+  [OK] 依赖 polars

+  [OK] 依赖 yaml

+  [OK] 依赖 matplotlib

+  [OK] 依赖 plotly

+  [OK] 依赖 jinja2

+  [OK] 依赖 docx

+  [OK] L0/L1 产物 (parquet) — outputs/rudong/windscada

+  [OK] 本体对象库 — outputs/rudong/ontology/objects.json

+  [OK] findings — outputs/rudong/sop/findings.json

+  [OK] 事实契约 — outputs/rudong/guanlan/facts_contract_v0.json

+  [OK] 门户 — release/portal.html

+  [OK] 仿真合页服务 — release/sim_sys_server.py

+  [OK] 仿真合页资料包 — release/如东SWT40_控制律仿真台_20260906.zip

+  [OK] 三维 viewer 资产 — release/viewer

+  [OK] 仿真回放资产 — resources/oem_envision_sc1_rudong2014

+  [OK] 治理清单交付件 (可选): release/如东

+  [OK] 原始件目录 (补数据放这里): F:\temp\app\data\raw

+  [运行中] 端口 18033 (detail)

+  [运行中] 端口 18020 (cms)

+  [运行中] 端口 64292 (viewer)

+  [运行中] 端口 18792 (sim_sys)

+  [运行中] 端口 18791 (sim)

+  [运行中] 端口 28084 (gateway)

+  [OK] 本机模型 Ollama 探针 (默认档) — 12.25 s, digest 500a1f067a9f782620b

+  [OK] 模型档 default_qa: qwen3:8b

+  [OK] 模型档 escalate_qa: qwen3:32b

+  [OK] 模型档 review: deepseek-r1:14b

+  [OK] 模型档 embed: bge-m3

+  [OK] 模型可用性检查: 正常; 审计日志 F:\temp\app\logs\audit\llm_audit.jsonl

+结论: 有 FAIL 项, 先按提示处理


+ 28 - 0
docs/src/_rebuild_plan.txt

@@ -0,0 +1,28 @@
+重算全部 · 23 步 · 根目录 F:\temp\app

+  跳过 SCADA: True | 重启服务: True | 等价验收: False

+

+  ①b 输入数据扫描 (新增/变化 → 该跑哪些步)          scripts/raw_scan.py --check --write

+  ② 三门台账                             scripts/rebuild_from_raw.py

+  ④ 月度派生件                            scripts/windscada_monthly_build.py

+  ④b 振动侧摄入 + CMS 报告 + 标量 z (原始导出 → 窗索引/谱/逐台页/融合 z) scripts/vib_raw_build.py

+  ④d 振动在升/换件闭环 (窗索引 → component_history.json) scripts/component_history_build.py

+  ④e 三层基线 (窗索引 → baseline_38.json)   scripts/baseline_38_build.py

+  ⑤b 事实契约: 由重算台账生成 claim             scripts/sop_findings_from_ledger.py

+  ⑤c 事实契约: 构建 + 渲染消费者                scripts/guanlan_facts_contract.py build

+  ⑤a 逐件来源台账 (账实相符: 谁在位、谁有生成端)        scripts/products_restore_missing.py --refresh

+  ⑤ 反向呼应审计 (输出→功能/算法→输入; 不从交付包补齐)    scripts/products_reverse_audit.py --check

+  ⑥ 重启组件服务 (保留网关, stop)              scripts/_ops_stop_keep_gateway.py

+  ⑥ 重启组件服务 (serve)                   guanlan.py serve

+  ⑦ 本体: 码表/手册/文档                     -m src.ontology.kb_ingest

+  ⑦ 本体: 铺开判级与台账                      -m src.ontology.populate

+  ⑦ 本体: 决策链进度                        -m src.ontology.chain_ingest

+  ⑦ 本体: 在升/闭环证据                      -m src.ontology.trend_ingest

+  ⑦ 本体: 检索索引                         -c from src.ontology import retrieval as R; R.build(use_vec=False)

+  ⑦ 本体: 实机参数表                        -c from src.ontology.maintenance import refresh_params as f; print(f())

+  ⑦b 全场状态总览页 (自算烘焙)                  scripts/windscada_overview_build.py

+  ⑧ 本体审计 (期望 0 问题)                   -m src.ontology.audit

+  ⑧b 重装门户 (把新产物灌进门户结论段)              scripts/portal_build.py

+  ⑧c 页面归口审计 (页面陈旧检测)                 scripts/pages_audit.py --check

+  ⑧d 版本记录一致性 (docs 与 src/version.py) scripts/version_log.py --check

+

+(dry-run, 未执行)


+ 289 - 123
docs/src/数据要求说明_观澜_2.10.0.md → docs/src/数据要求说明_观澜_2.10.1.md

@@ -1,10 +1,12 @@
-# 数据要求说明 · 观澜·如东样板 v2 · 版本 2.10.0
+# 数据要求说明 · 观澜 v2 风电场智能分析系统 · 版本 2.10.1
 
 ## 1 文档说明
 
 ### 1.1 目的
 
-本文说明「观澜·如东样板 v2」(版本 2.10.0,海上风电场智能分析离线系统)运行、判级与出件所**实际需要**的输入数据:需要哪些数据分类、每一类里需要哪些测点(字段)、每个测点是什么度量单位、以及这一类到底是**必须**还是**可替代**。
+本文说明「观澜 v2(风电场智能分析系统)」(版本 2.10.1,海上风电场智能分析离线系统)运行、判级与出件所**实际需要**的输入数据:需要哪些数据分类、每一类里需要哪些测点(字段)、每个测点是什么度量单位、以及这一类到底是**必须**还是**可替代**。
+
+全文以一座已投产的海上风电场为样本,称**样本风电场(下称"本场")**;凡出现"本场"之处均指这一样本场。文中的件数、体量、行数、时间覆盖、通道数与缺口结论都取自本场,统一标注为**样本场实测(2026-09-22)**;**换场(本场之外的任何风电场)不沿用这些数字**,须按第 12 章的通用模板与换场适配重新收资与判定。
 
 本文的读者可以直接拿它去现场收资,也可以拿它逐条核对「现场已经给了什么、还缺什么、缺了会怎样」。
 
@@ -14,7 +16,7 @@
 
 | 读者 | 怎么用本文 |
 |---|---|
-| 现场数据管理员 | 按第 8 章与第 11 章的清单收资、按第 7 章的流程落位、按第 10 章核对锚点 |
+| 现场数据管理员 | 按第 8 章与第 11 章的清单收资、按第 7 章的流程落位、按第 10 章核对锚点、按第 12 章做换场裁剪 |
 | 观澜实施与运维人员 | 按第 3 章判断某一族数据有没有消费者、按第 7 章判读体检退出码 |
 | 分析人员 | 按第 5 章确认某个测点是否在契约内、单位与量纲是否正确、哪些列没有取证 |
 | 交付与验收人 | 按第 9 章确认每一条缺口是"现场补件"还是"研发补生成端",按附录 A 追到依据 |
@@ -26,6 +28,7 @@
 - 收资要求 v3 的原文逐字列在附录 C,一条不改、一条不删;
 - 第 8 章把 21 条逐条对照到观澜的数据族、落位目录、消费者脚本与到位状态,并给出缺口与催缴要点;
 - 收资要求 v3 没有区分"必须/可选",本文补上这一层——因为观澜对 21 类的依赖强度**并不相同**:有的缺了整个系统会响亮降级(页面该处显示"缺件"),有的缺了只影响一条辅助轴。
+- 第 12 章把这一层抽象成**与具体场无关的判定规则**(五问定档:四问定必须性、一问定"不催"),并给出场配置字段对照、数据源形态适配、换场收资差异清单与按场裁剪步骤——21 项收资从此是**通用模板**,不是某一个场的专用清单。
 
 一句话口径:**收资要求 v3 是收资面,本文是接入面与依赖面。**
 
@@ -42,9 +45,9 @@
 
 ### 1.5 口径与依据
 
-1. **单位与测点清单以机器可读真源为准**:`configs/canonical/dictionary.yaml`(**778 条** canonical 测点,字段为 `system`、`members`(契约列名)、`n_contract_columns`、`unit_majority`、`unit_variants`)与 `configs/contracts/siemens_swt40_rudong.yaml`(如东 SWT-4.0-130 字段契约)。**查不到的写"未取证",绝不凭常识补单位。**
+1. **单位与测点清单以机器可读真源为准**:`configs/canonical/dictionary.yaml`(**778 条** canonical 测点,字段为 `system`、`members`(契约列名)、`n_contract_columns`、`unit_majority`、`unit_variants`)与 `configs/contracts/<机型>_<场>.yaml`(4.0 MW 级海上机组字段契约,机型代号从略)。**查不到的写"未取证",绝不凭常识补单位。**
 2. **落位与目录以** `docs/数据目录结构与落位约定_v0.2.md` 与 `docs/输入数据放置指导_v0.1.md` 为准。
-3. **体量、件数、行数均以本机 `data/raw/如东` 与 `outputs/rudong` 只读实测为准**(2026-09-22 实测)。
+3. **体量、件数、行数均以样本场本机 `data/raw/<场站>` 与 `outputs/<场>` 只读实测为准,标注「样本场实测(2026-09-22)」**;正文与表中凡写"实测/本机实测"而未另注日期者,均指这一次样本场实测。
 4. 本文中"含窗且确指时间窗口"之处一律写**时间窗**;影响机组数量之处写**影响机组/影响机组数**;英文简写一律写"中文(英文简写)"。
 5. 全文单位用半角,温度用 `℃`,旋转速度用 `r/min`(= 机器可读真源里的 `rpm`,两者同量纲)。
 
@@ -52,29 +55,29 @@
 
 ### 2.1 观澜运行所需的输入全景
 
-观澜不直接读现场原始件,而是先由**摄入链**把 `data/raw/<场站>/` 下的源件转成 `outputs/<场站>/` 下的产物,页面再读产物。因此"输入"分两层:**源件层**(现场交什么)与**产物层**(系统算什么)。
+观澜不直接读现场原始件,而是先由**摄入链**把 `data/raw/<场站>/` 下的源件转成 `outputs/<场>/` 下的产物,页面再读产物。因此"输入"分两层:**源件层**(现场交什么)与**产物层**(系统算什么)。
 
 源件层的落位约定只有一条判据:**摄入脚本按"源类目录 → 再往下一层就是文件"读**。多套一层包名目录(例如 `scada_10min\10分钟数据\WTG01.csv`)等于没放。
 
-如东样板当前在位 9 个族(含与场站并列的共享技术资料族),**如表 2-1 所示**。
+本场当前在位 9 个族(含与场站并列的共享技术资料族),**如表 2-1 所示**。
 
-**表 2-1 在位数据族的件数与体量(依据 `data/raw/如东` 只读实测)**
+**表 2-1 在位数据族的件数与体量(样本场实测 2026-09-22,依据 `data/raw/<场站>` 只读实测)**
 
 | 族 | 落位目录 | 当前件数 | 当前体量 |
 |---|---|---|---|
-| 10 分钟 SCADA | `data/raw/如东/scada_10min/` | 114 | 16,749,788,062 B(约 15.6 GB) |
-| 1 分钟 SCADA | `data/raw/如东/scada_1min/` | 38 | 13,709,173,574 B(约 12.8 GB) |
-| 现场年度归档库 | `data/raw/如东/scada_mdb/` | 100 | 48,247,200,121 B(约 44.9 GB) |
-| 故障报警 | `data/raw/如东/故障报警/` | 16 | 23,844,357 B(约 22.7 MB) |
-| 风机故障记录 | `data/raw/如东/风机故障记录/` | 137 | 149,795,133 B(约 142.9 MB) |
-| 油样报告 | `data/raw/如东/油样报告/` | 404 | 176,226,585 B(约 168.1 MB) |
-| CMS 振动原始导出 | `data/raw/如东/windcms/` | 25,693 | 161,219,600,707 B(约 150.1 GB) |
-| 振动线 handoff | `data/raw/如东/m5_cms_tcm/` | 1 | 21,823,545 B(约 20.8 MB) |
-| 厂商技术资料 | `data/raw/西门子4.0技术资料/` | 318 | 2,936,758,089 B(约 2.7 GB) |
+| 10 分钟 SCADA | `data/raw/<场站>/scada_10min/` | 114 | 16,749,788,062 B(约 15.6 GB) |
+| 1 分钟 SCADA | `data/raw/<场站>/scada_1min/` | 38 | 13,709,173,574 B(约 12.8 GB) |
+| 现场年度归档库 | `data/raw/<场站>/scada_mdb/` | 100 | 48,247,200,121 B(约 44.9 GB) |
+| 故障报警 | `data/raw/<场站>/故障报警/` | 16 | 23,844,357 B(约 22.7 MB) |
+| 风机故障记录 | `data/raw/<场站>/风机故障记录/` | 137 | 149,795,133 B(约 142.9 MB) |
+| 油样报告 | `data/raw/<场站>/油样报告/` | 404 | 176,226,585 B(约 168.1 MB) |
+| CMS 振动原始导出 | `data/raw/<场站>/windcms/` | 25,693 | 161,219,600,707 B(约 150.1 GB) |
+| 振动线 handoff | `data/raw/<场站>/m5_cms_tcm/` | 1 | 21,823,545 B(约 20.8 MB) |
+| 厂商技术资料 | `data/raw/<机型>技术资料/` | 318 | 2,936,758,089 B(约 2.7 GB) |
 
 **如图 2-1 所示**,体量高度集中在 CMS 振动导出(约 150.1 GB,占九族合计 223.8 GB 的绝大多数)与两类 SCADA 导出上,台账类三族合计不到 0.2 GB 却承担了报警、工单、油样三条台账的**逐行对齐**。
 
-![图 2-1 输入数据族件数与体量(依据 data/raw/如东 实测)](figures/fig-dat-01-数据族体量.png)
+![图 2-1 输入数据族件数与体量(样本场实测 2026-09-22)](figures/fig-dat-01-数据族体量.png)
 
 场站目录下另有 1 件散装件 `scada_10min_202607.csv`(943,520,249 B):它是**按月提取/核对件**,不是摄入源。扫描器按名字模式忽略它(不报"未归类"、不计变化),真正进产物的是 `scada_10min/WTG01.csv` 一类的逐台主件与同台补充件。
 
@@ -98,25 +101,25 @@
 | `油样报告` | 油液化验索引、油液时效胶囊、融合面油样轴 | `oil_samples_index.parquet` |
 | `windcms` | 振动窗索引、谱库、CMS 报告与逐台页、在升与换件闭环、三层基线 | `m5_cms_tcm/windows/<窗>/index.parquet`、`spectra/*.npz`、`spectra_meta.parquet`、`windcms/报告_CMS*` |
 | `m5_cms_tcm` | 融合面判级、TCM 兼容件;现场正本优先于观澜自算件 | `handoff_vibration_v2.json`、`component_history.json`、`fusion_38.csv`、`fleet_scalar_z.parquet` |
-| `西门子4.0技术资料` | 本体对象库、检索索引、实机参数表(整定值)、故障码对译 | `ontology/objects.json`、`retrieval_index.json`、`ontology/turbine_params.parquet` |
+| `<机型>技术资料` | 本体对象库、检索索引、实机参数表(整定值)、故障码对译 | `ontology/objects.json`、`retrieval_index.json`、`ontology/turbine_params.parquet` |
 
 ## 3 数据分类总表
 
 **如表 3-1 所示**,观澜的每一个数据族都能追到收资要求 v3 的序号、落位目录、消费者脚本、必须性与当前到位状态。
 
-**表 3-1 观澜数据族总表**
+**表 3-1 观澜数据族总表(样本场实测 2026-09-22)**
 
 | 观澜族 | 收资 v3 序号 | 落位目录 | 消费者脚本 | 必须性 | 当前到位状态 |
 |---|---|---|---|---|---|
-| `scada_10min` | 1、9 | `data/raw/如东/scada_10min/` | `scripts/rebuild_from_raw.py --scada`、`scripts/scada_slim_build.py` | 必须 | 已在位(38 台主件 + 76 件同台补充件) |
-| `scada_1min` | 2 | `data/raw/如东/scada_1min/` | `scripts/pitch_face_build.py` | 建议 | 已在位(38 件,内部台号 01E 至 38B) |
-| `scada_mdb` | 1、2、9 | `data/raw/如东/scada_mdb/` | `src/windscada/scada_source.py`、`scripts/csv_to_mdb.py` | 建议 | 部分到位(25 年 12 个月 12 类;26 年 1 至 8 月,7 月缺 10 个类目库) |
-| `故障报警` | 7 | `data/raw/如东/故障报警/` | `scripts/windscada_alarms_ingest.py` | 必须 | 已在位(16 件季度/年度导出) |
-| `风机故障记录` | 7、18 | `data/raw/如东/风机故障记录/` | `scripts/windscada_workorder_ingest.py` | 必须 | 已在位(137 件,123 件被摄入) |
-| `windcms` | 8 | `data/raw/如东/windcms/` | `scripts/rudong_tcm_index.py`、`scripts/rudong_tcm_spectra.py`、`scripts/vib_raw_build.py` | 必须 | 已在位(25,679 件 decode 导出 + 14 件厂家报告) |
-| `m5_cms_tcm` | 8 | `data/raw/如东/m5_cms_tcm/` | `scripts/rudong_fusion_handoff.py`、`scripts/component_history_build.py` | 替代 | 部分到位(现场仅有 1 份厂家 docx,handoff 正本缺失,用观澜自算件顶替) |
-| `油样报告` | 18 | `data/raw/如东/油样报告/` | `scripts/windscada_watch_channels_build.py` | 建议 | 已在位(404 件,两级目录 台号/部件) |
-| `西门子4.0技术资料` | 5、7、10、12、13、15、20、21 | `data/raw/西门子4.0技术资料/` | `python -m src.ontology.kb_ingest`、`src/ontology/maintenance.py` | 必须 | 已在位(318 件 2.7 GB,含定值表与结构树) |
+| `scada_10min` | 1、9 | `data/raw/<场站>/scada_10min/` | `scripts/rebuild_from_raw.py --scada`、`scripts/scada_slim_build.py` | 必须 | 已在位(38 台主件 + 76 件同台补充件) |
+| `scada_1min` | 2 | `data/raw/<场站>/scada_1min/` | `scripts/pitch_face_build.py` | 建议 | 已在位(38 件,内部台号 01E 至 38B) |
+| `scada_mdb` | 1、2、9 | `data/raw/<场站>/scada_mdb/` | `src/windscada/scada_source.py`、`scripts/csv_to_mdb.py` | 建议 | 部分到位(25 年 12 个月 12 类;26 年 1 至 8 月,7 月缺 10 个类目库) |
+| `故障报警` | 7 | `data/raw/<场站>/故障报警/` | `scripts/windscada_alarms_ingest.py` | 必须 | 已在位(16 件季度/年度导出) |
+| `风机故障记录` | 7、18 | `data/raw/<场站>/风机故障记录/` | `scripts/windscada_workorder_ingest.py` | 必须 | 已在位(137 件,123 件被摄入) |
+| `windcms` | 8 | `data/raw/<场站>/windcms/` | `scripts/<场>_tcm_index.py`、`scripts/<场>_tcm_spectra.py`、`scripts/vib_raw_build.py` | 必须 | 已在位(25,679 件 decode 导出 + 14 件厂家报告) |
+| `m5_cms_tcm` | 8 | `data/raw/<场站>/m5_cms_tcm/` | `scripts/<场>_fusion_handoff.py`、`scripts/component_history_build.py` | 替代 | 部分到位(现场仅有 1 份厂家 docx,handoff 正本缺失,用观澜自算件顶替) |
+| `油样报告` | 18 | `data/raw/<场站>/油样报告/` | `scripts/windscada_watch_channels_build.py` | 建议 | 已在位(404 件,两级目录 台号/部件) |
+| `<机型>技术资料` | 5、7、10、12、13、15、20、21 | `data/raw/<机型>技术资料/` | `python -m src.ontology.kb_ingest`、`src/ontology/maintenance.py` | 必须 | 已在位(318 件 2.7 GB,含定值表与结构树) |
 
 ## 4 逐类数据要求
 
@@ -132,9 +135,9 @@
 
 **单位**:功率 kW、风速 m/s、转速 r/min、桨距角 °、温度 ℃、压力 bar、时间 s、计数 次。
 
-**格式与编码**:`.csv`,平铺、每台一个文件,文件名 = 场配置里的机组号(`WTG01.csv` 至 `WTG38.csv`);同台补充件命名 `<台号>-<YYYYMM>.csv` 或 `<台号>-B2.csv`,取数层按**列名**对齐(新导出去掉了类型前缀,例如 `din_wtc_X` 变成 `wtc_X`)并按时间戳去重(主件优先)。编码 `utf-8-sig`。时间戳 `YYYY-MM-DD HH:MM:SS`。实测主件 598 列(3 列元信息 + 595 通道),补充件 856 列。
+**格式与编码**:`.csv`,平铺、每台一个文件,文件名 = 场配置里的机组号(`WTG01.csv` 至 `WTG38.csv`;`WTG01` 至 `WTG38` 为**样本场内部编号,换场按实际台号替换**);同台补充件命名 `<台号>-<YYYYMM>.csv` 或 `<台号>-B2.csv`,取数层按**列名**对齐(新导出去掉了类型前缀,例如 `din_wtc_X` 变成 `wtc_X`)并按时间戳去重(主件优先)。编码 `utf-8-sig`。时间戳 `YYYY-MM-DD HH:MM:SS`。实测主件 598 列(3 列元信息 + 595 通道),补充件 856 列。
 
-**落位目录**:`data/raw/如东/scada_10min/`。
+**落位目录**:`data/raw/<场站>/scada_10min/`。
 
 **到位现状**:已在位。38 件主件 + 76 件同台补充件 = 114 件,逐台行数一致(WTG01 主件 77,551 行),说明全台同步断录、无单台缺失。
 
@@ -152,7 +155,7 @@
 
 **格式与编码**:`.csv`,平铺,文件名为**内部台号**(`01E.csv` 至 `38B.csv`,字母 A 至 F 为集电线路 string);`utf-8-sig`;实测 79 列中英混合转发点表;时间戳 `YYYY-MM-DD HH:MM:SS`。
 
-**落位目录**:`data/raw/如东/scada_1min/`。
+**落位目录**:`data/raw/<场站>/scada_1min/`。
 
 **到位现状**:已在位(38 件)。但**全场级死列 16 条**已实证(含 `yaw_error1`、`cabin_position`、`twisted_cable_angle`、发电机三相绕组温度 1/2/3、1 至 3 号桨电机温度、自定义可利用率、航空灯状态 AI 等),这些列在本场 1 分钟层没有可用的时序信息。
 
@@ -170,7 +173,7 @@
 
 **格式与编码**:`.mdb` 或 `.zip`(年度归档 `<年>年.zip`,内层 `<年>/<月>/<年-月-类>.zip` → `.mdb`)。中文类目目录名 ↔ 类目码:`风机数据`=tur、`温度数据`=tmp、`压力数据`=prs、`电网数据`=grd、`DigiIn数据`=din、`DigiOut数据`=dot、`计算数据`=cnt、`标志数据`=flg、`内部数据`=int、`统计数据`=scd、`标准数据`=std、`汇总数据`=sum。
 
-**落位目录**:`data/raw/如东/scada_mdb/<年>年/<月>月/<年>-<月>-<类>.mdb`。
+**落位目录**:`data/raw/<场站>/scada_mdb/<年>年/<月>月/<年>-<月>-<类>.mdb`。
 
 **到位现状**:部分到位。原始件 100 件、44.9 GB;体检器报 **24 件缺口**,集中在 `2026年/07月/` 的 10 个类目库(cnt、din、dot、flg、grd、int、prs、scd、std、sum 等)。2026-07 的 10min 库与 tur/tmp 在位。
 
@@ -188,7 +191,7 @@
 
 **格式与编码**:`.xls`(实为 SpreadsheetML XML)、`.xlsx`、`.xml`;不许多套一层目录。
 
-**落位目录**:`data/raw/如东/故障报警/`。
+**落位目录**:`data/raw/<场站>/故障报警/`。
 
 **到位现状**:已在位。产出 `alarms.parquet` 9 列、**39,211 行**(与核对锚点一致)。
 
@@ -206,7 +209,7 @@
 
 **格式与编码**:`.xls`、`.xlsx` 为摄入件;`.rar`、`.jpg`、`.png` 为附件,保留但不摄入。年目录 + 月度表,不限层级。
 
-**落位目录**:`data/raw/如东/风机故障记录/`。
+**落位目录**:`data/raw/<场站>/风机故障记录/`。
 
 **到位现状**:已在位。产出 `workorders.parquet` 26 列、**5,876 行**(与核对锚点一致)。
 
@@ -216,7 +219,7 @@
 
 **用途**:振动侧的**唯一可重算源件**。索引、谱库、CMS 报告与逐台页、在升与换件闭环、三层基线都由它算。
 
-**最小可用量**:**至少一个季度**的秒级振动数据(非加密)及原始振动时域波形。本机实测只有 `CMS_RuDong_CGN_202603-04`(2026-03-16 17:27:22 至 2026-04-21 10:18:33,约 36 天),**不足一个季度**。
+**最小可用量**:**至少一个季度**的秒级振动数据(非加密)及原始振动时域波形。本机实测只有 `CMS_<场站>_202603-04`(现场导出包名,换场按实际包名替换)(2026-03-16 17:27:22 至 2026-04-21 10:18:33,约 36 天),**不足一个季度**。
 
 **必需测点或字段**(9 个传感器/测点,实测):`Main_bearing_front`(主轴前)、`Main_bearing_rear`(主轴后)、`Gear_planet`(行星级)、`Gear_IMS`(中间轴)、`Gear_HS_generator_side`(高速轴发电机侧)、`Gear_HS_rotor_side`(高速轴叶轮侧)、`Generator_DE`(发电机驱动端)、`Generator_NDE`(发电机非驱动端)、`System Monitor`(系统监视)。转速类量(`rpm`、`RpmProfile`、`rawRPM`)在索引里,是叶轮 1P 与阶次跟踪的分母。
 
@@ -224,7 +227,7 @@
 
 **格式与编码**:`*_decode.json`(Brande TCM Enterprise 导出,每件是一次 API 响应 `{body:{body:{"<时间戳>":[{"Record":{...}}]}}}`);厂家报告为 `.docx`(有文本层,可摄入)或 `.pdf`(扫描件,只归档)。`measurement/<年>/<月>/<WTGxx>/` 层级可保留,摄入按 `rglob` 找 `*_decode.json`。
 
-**落位目录**:`data/raw/如东/windcms/`;厂家报告放 `data/raw/如东/windcms/厂家报告/上海电气_月度/`。
+**落位目录**:`data/raw/<场站>/windcms/`;厂家报告放 `data/raw/<场站>/windcms/厂家报告/整机厂商_月度/`。
 
 **到位现状**:已在位(25,679 件 decode 导出 + 14 件厂家报告)。产出窗 `w0316` 索引 **2,066,686 行 × 54 列**、谱 **420,742 条 / 1,701 个 npz 分片 / 3.15 GB**。
 
@@ -234,7 +237,7 @@
 
 **用途**:融合面判级、TCM 兼容件。现场若给出 `handoff_vibration_v2.json` 与 `component_history.json` **正本**,放这里即被优先采用。
 
-**最小可用量**:两件正本各一份;本机实测现场只给了 1 件厂家 docx(大生科技传动链振动分析报告)。
+**最小可用量**:两件正本各一份;本机实测现场只给了 1 件厂家 docx(第三方检测机构传动链振动分析报告,机构名称从略)。
 
 **必需测点或字段**:正本结构含 `meta`(版本/日期/来源/取代关系/原则)、`detectability_prior`、`cross_source_requests`、`baseline_三层`、`per_turbine`(人工裁决 + `claim_window`)、`field_actions_v1`、`calibration_updates_v1`、`open_items`、`tcm16_schedule`。
 
@@ -242,9 +245,9 @@
 
 **格式与编码**:`.json`、`.docx`、`.pdf`、`.md`。
 
-**落位目录**:`data/raw/如东/m5_cms_tcm/`。
+**落位目录**:`data/raw/<场站>/m5_cms_tcm/`。
 
-**到位现状**:**部分到位**。正本缺失,由观澜自算件顶替:`scripts/rudong_fusion_handoff.py`(融合面)与 `scripts/component_history_build.py`(在升与换件闭环)。本机只有 1 个导出色窗,故在升与闭环**如实为空**并把数据边界写进 `meta.★数据边界`。
+**到位现状**:**部分到位**。正本缺失,由观澜自算件顶替:`scripts/<场>_fusion_handoff.py`(融合面)与 `scripts/component_history_build.py`(在升与换件闭环)。本机只有 1 个导出色窗,故在升与闭环**如实为空**并把数据边界写进 `meta.★数据边界`。
 
 **缺失影响**:`/detail` 工作台的**振动融合面**(`/api/fleet` 的 `fus`)如实报"缺件";CMS 报告与评估页那一面已由按口径重建的两步喂上。
 
@@ -260,17 +263,17 @@
 
 **格式与编码**:`.pdf`,**两级目录** `<台号>/<部件>/*.pdf`。
 
-**落位目录**:`data/raw/如东/油样报告/`。
+**落位目录**:`data/raw/<场站>/油样报告/`。
 
-**到位现状**:已在位(404 件)。产出 `oil_samples_index.parquet` **404 行**。**已知缺口**:华标 2026-07 批 102 行(合并报告 `BG-2026-07-YP013 中广核新能源如东海上风电场.pdf`)的源件不在现场包内,从零重算会从 506 行落到 404 行。
+**到位现状**:已在位(404 件)。产出 `oil_samples_index.parquet` **404 行**。**已知缺口**:华标 2026-07 批 102 行(合并报告 `BG-2026-07-YP013 样本风电场.pdf`)的源件不在现场包内,从零重算会从 506 行落到 404 行。
 
 **缺失影响**:缺则油液面不可用;油液与工单的交叉核对("换油是否紧跟检修")做不了。
 
-### 4.9 `西门子4.0技术资料`(厂商技术资料)
+### 4.9 `<机型>技术资料`(厂商技术资料)
 
 **用途**:本体知识层(对象库、检索索引、实机参数表)与故障码对译。
 
-**最小可用量**:至少必须含 `kb_ingest.py` 点名的四个台账文件:`故障处理/故障处理手册.xlsx`、`故障处理/广核如海风电场西门子风机故障代码中英文对译表.xlsx`、`如海故障代码表.xlsx`、`维护相关/维护作业指导书.xlsx`。缺任一个都会打印 `⚠ 源缺失` 并降级,**不静默**。
+**最小可用量**:至少必须含 `kb_ingest.py` 点名的四个台账文件:`故障处理/故障处理手册.xlsx`、`故障处理/场站故障代码中英文对译表.xlsx`、`场站故障代码表.xlsx`、`维护相关/维护作业指导书.xlsx`。缺任一个都会打印 `⚠ 源缺失` 并降级,**不静默**。
 
 **必需测点或字段**:故障码(`WALM_fault_code`)、整定值(`Turbine+Parameters.*.xlsx` 的 `Number`、`Text`、`Min`、`Max`、`Default`、`Current`、`Value`、`Unit`、`Reset`)、图纸与手册的章节结构。
 
@@ -278,9 +281,9 @@
 
 **格式与编码**:`.pdf`、`.docx`、`.doc`、`.xlsx`、`.xls`、`.pptx`;整棵树都读(白名单为全部)。
 
-**落位目录**:`data/raw/西门子4.0技术资料/`(与场站目录**并列**的共享资料,不在场站目录下)。
+**落位目录**:`data/raw/<机型>技术资料/`(与场站目录**并列**的共享资料,不在场站目录下)。
 
-**到位现状**:已在位(318 件、2.7 GB、63 个子目录),含 `广核如东海上风电场机组定值表.pdf`(22,614,546 B)、`上海电气4.0MW保护定值参数.pdf`(975,661 B)、`风机参数设定表.xls`、`上海电气+SWT4.0-130+结构树文件+江苏如海.doc`(2,609,152 B)。产出 `turbine_params.parquet` **1,706 条整定值**。
+**到位现状**:已在位(318 件、2.7 GB、63 个子目录),含 `场站机组定值表.pdf`(22,614,546 B)、`整机厂商4.0MW保护定值参数.pdf`(975,661 B)、`风机参数设定表.xls`、`整机厂商结构树文件.doc`(2,609,152 B)。产出 `turbine_params.parquet` **1,706 条整定值**。
 
 **缺失影响**:缺则本体层降级(对象库与检索索引内容缺失),故障码对译与保护定值轴不可用。
 
@@ -294,9 +297,9 @@
 
 **单位**:kW(功率)、m(长度/直径/高度)、无量纲(传动比)、℃(温度)、°(角度)、m/s(风速)。
 
-**格式与编码**:`.docx`、`.pdf`、`.xlsx`,或已固化的 `.json`(`reference/rudong/oem_bearing_freqs.json` 29 个轴承条目、`reference/rudong/gear_mesh_map_measured.json`、`reference/rudong/oem_scan_plan.json` 11 个部件)。
+**格式与编码**:`.docx`、`.pdf`、`.xlsx`,或已固化的 `.json`(`reference/<场>/oem_bearing_freqs.json` 29 个轴承条目、`reference/<场>/gear_mesh_map_measured.json`、`reference/<场>/oem_scan_plan.json` 11 个部件)。
 
-**落位目录**:随族落位(`西门子4.0技术资料/` 或场站族目录)。
+**落位目录**:随族落位(`<机型>技术资料/` 或场站族目录)。
 
 **到位现状**:部分到位。机型级常量已在位(额定功率 4000 kW、叶轮直径 130 m、轮毂高度 87 m、传动比 119.752);部件级只在振动侧成体系(11 个部件的理论特征频率表已验),**逐部件型号与齿数的成册件未交付**。
 
@@ -314,7 +317,7 @@
 
 **格式与编码**:`.xlsx` 或 `.csv`。
 
-**落位目录**:`data/raw/如东/` 下随族落位(本机实测**该件不在位**)。
+**落位目录**:`data/raw/<场站>/` 下随族落位(本机实测**该件不在位**)。
 
 **到位现状**:**未取证**。契约 `materials.coordinates` 写有"风机坐标.xlsx 38 台 + 升压站;A 至 F 为 string;间距中位 6.54D;可研表 6.3-1 为逐台设计基线(2660 h / 尾流 6.8%)可提取",但该件**不在** `data/raw` 任何族内,仓库内也搜不到同名文件。故**坐标本身未交付**;"某台属于哪条集电线路(A 至 F)"这一条可由 1 分钟导出的内部台号反推(`01E` 至 `38B` 的字母)。
 
@@ -386,7 +389,7 @@
 
 **格式与编码**:`.xls`、`.xlsx`(观澜侧以工单台账形式接入)。
 
-**落位目录**:`data/raw/如东/风机故障记录/`。
+**落位目录**:`data/raw/<场站>/风机故障记录/`。
 
 **到位现状**:已在位(检修记录 2025 与 2026;油样 2025)。
 
@@ -404,7 +407,7 @@
 
 **格式与编码**:`.xlsx`(观测到 `大部件维修记录.20240619143912557.xlsx` 一类的台账件)。
 
-**落位目录**:`data/raw/如东/风机故障记录/`。
+**落位目录**:`data/raw/<场站>/风机故障记录/`。
 
 **到位现状**:部分到位(出保备件台账在位;**更换台账不完整**)。在升与换件闭环目前只有一个导出色窗的输入,因此**如实为空**并写明数据边界。
 
@@ -422,9 +425,9 @@
 
 **格式与编码**:`.pdf`、`.docx`、`.xlsx`、`.pptx`。
 
-**落位目录**:`data/raw/西门子4.0技术资料/`。
+**落位目录**:`data/raw/<机型>技术资料/`。
 
-**到位现状**:已在位。实测含 `4.0MW电气图纸/4.0电气原理图.pdf`、`接线表.xlsx`、`4.0MW液压图纸/液压图纸4.0.pdf`(3 份)、`4.0MW主控图纸/4.0MW-kk drawing-V304002_2014-05-28.pdf`、`4.0原理图_2014-05-28&新增设备电气接口图(1).pdf`、`4.0故障逻辑及处理.docx`、`广核如东海上风电场机组定值表.pdf` 等。
+**到位现状**:已在位。实测含 `4.0MW电气图纸/4.0电气原理图.pdf`、`接线表.xlsx`、`4.0MW液压图纸/液压图纸4.0.pdf`(3 份)、`4.0MW主控图纸/4.0MW-kk drawing-V304002_2014-05-28.pdf`、`4.0原理图_2014-05-28&新增设备电气接口图(1).pdf`、`4.0故障逻辑及处理.docx`、`场站机组定值表.pdf` 等。
 
 **缺失影响**:缺则位域语义(例如"刹车状态 AI 的取值 61 是 0b111101,具体位含义")只能停在"须图纸"的未决态。
 
@@ -452,7 +455,7 @@
 
 现场 10 分钟导出按**类目**组织,9 个逐台类目与主件前缀块**一一对应**,合计 **595 通道,0 缺 0 多**。
 
-**表 5-1 逐台类目 → 前缀 → 通道数**
+**表 5-1 逐台类目 → 前缀 → 通道数(样本场实测 2026-09-22)**
 
 | 类目码 | 中文目录名 | 契约列前缀 | 通道数 |
 |---|---|---|---|
@@ -471,9 +474,9 @@
 
 主件 CSV 实测 598 列 = 3 列元信息(`TimeStamp`、`WTG`、`StationId`)+ 595 通道,**自洽**。
 
-观澜的**契约**只覆盖其中一部分:`reference/rudong/windscada_contract.yaml` 登记 10 个组共 **164 列**(活 155 列),**未契约列 435 列**已登记、不假装覆盖。canonical 词典侧共 **778 条**测点,按系统分布如下(前 8):
+观澜的**契约**只覆盖其中一部分:`reference/<场>/windscada_contract.yaml` 登记 10 个组共 **164 列**(活 155 列),**未契约列 435 列**已登记、不假装覆盖。canonical 词典侧共 **778 条**测点,按系统分布如下(前 8):
 
-**表 5-2 canonical 测点的系统分布(`configs/canonical/dictionary.yaml`)**
+**表 5-2 canonical 测点的系统分布(`configs/canonical/dictionary.yaml`,样本场实测 2026-09-22)**
 
 | system | 词条数 | 含义 |
 |---|---|---|
@@ -490,7 +493,7 @@
 
 ### 5.2 判级与曲线实际取用的 48 列公共子集
 
-四个判级面(变桨、偏航、蓄能、温度)与七镜头曲线不是读全部 595 通道,而是读一个**48 列公共子集**(`scripts/scada_slim_build.py` 的 `COLS_CORE` 23 列 + 温度 NBM 的 27 个 `_mean`,去重后 48 列)。该子集落成按台一份的**窄仓** `outputs/rudong/windscada/slim10min/<台号>.parquet`,进重算链 ③b 步。
+四个判级面(变桨、偏航、蓄能、温度)与七镜头曲线不是读全部 595 通道,而是读一个**48 列公共子集**(`scripts/scada_slim_build.py` 的 `COLS_CORE` 23 列 + 温度 NBM 的 27 个 `_mean`,去重后 48 列)。该子集落成按台一份的**窄仓** `outputs/<场>/windscada/slim10min/<台号>.parquet`,进重算链 ③b 步。
 
 #### 5.2.1 48 列公共子集清单
 
@@ -567,7 +570,7 @@
 
 实测的 27 个活温度 `_mean` 通道及其 canonical 依据如下(契约 `B.温度NBM` 组共 58 列,其中 `_mean` 29 列;`GFilB2`/`GFilB3` 为全场恒 200 哨兵、`liveness=恒值哨兵`,**已被排除**,故活通道 27 个)。
 
-**表 5-5 27 个温度通道与单位依据**
+**表 5-5 27 个温度通道与单位依据(样本场实测 2026-09-22)**
 
 | 契约列名 | canonical 编号(词典键) | system | 单位 | 判级中的角色 |
 |---|---|---|---|---|
@@ -619,7 +622,7 @@
 
 **采样与波形**:每件 `*_decode.json` 是一次测量记录;索引 54 列记录 `rpm`、`bandwidth_hz`、`lines`、`n_averages`、`duration_s`、`condition_key`(7 个功率分箱)、`red_alarm`/`yellow_alarm`/`blue_alarm` 与迟滞。原始**时域波形**是 `TimeSeries` 与 `TrackedTimeSeries`(`meas_source` 里以 `Time_` 开头的 30 余类,单点 65,536 或 200,000 点),本机**默认不转**(只转 FFT 谱),因此"原始时域波形"在位但未全部落成谱库件。
 
-**表 5-6 振动频谱轴分辨率(`oem_scan_plan.json` 的 11 个部件)**
+**表 5-6 振动频谱轴分辨率(`oem_scan_plan.json` 的 11 个部件,样本场实测 2026-09-22)**
 
 | 部件 | 测点 | 用哪条谱(shard) | 频率分辨率 Hz |
 |---|---|---|---|
@@ -658,17 +661,17 @@
 
 ### 5.6 机型与部件参数
 
-**表 5-8 机型级参数(本机已取证项)**
+**表 5-8 机型级参数(样本场实测 2026-09-22,已取证项)**
 
 | 参数 | 取值 | 单位 | 依据 |
 |---|---|---|---|
-| 机型 | SWT-4.0-130(上海电气/西门子许可,海上固定式) | — | `configs/contracts/siemens_swt40_rudong.yaml` |
+| 机型 | 4.0 MW 级海上机组(机型代号从略;OEM 许可,海上固定式) | — | `configs/contracts/<机型>_<场>.yaml` |
 | 传动拓扑 | 异步全功率变流 + 齿轮箱 + 液压集中变桨 | — | 同上 |
 | 额定功率 | 4000 | kW | 同上(实证平台 3988 至 3992 kW) |
 | 叶轮直径 | 130 | m | 同上 |
 | 轮毂高度 | 87(= 73 塔筒 + 14 平台) | m | 同上 |
-| 齿轮箱传动比 | 119.752 | 无量纲 | `reference/rudong/gear_mesh_map_measured.json` |
-| 齿轮箱 | 威能极 PZAB 3546,3 级,油约 750 L | — | `reference/rudong/windscada_machine_facts.yaml` |
+| 齿轮箱传动比 | 119.752 | 无量纲 | `reference/<场>/gear_mesh_map_measured.json` |
+| 齿轮箱 | 威能极 PZAB 3546,3 级,油约 750 L | — | `reference/<场>/windscada_machine_facts.yaml` |
 | 变桨形式 | 液压集中变桨(三桨给定极差 p99 ≤ 0.4°) | — | 契约 `meta.pitch_type` |
 | 顺桨角 | 88 至 90 | ° | `windscada_machine_facts.yaml` |
 | 运行恒压 | 约 237 | bar | 契约 `turbine_1min.columns` 注记 |
@@ -677,9 +680,9 @@
 
 **表 5-9 部件级参数(收资要求 v3 第 21 项逐项)**
 
-| 收资要求 v3 的部件参数 | 本机取证情况 |
+| 收资要求 v3 的部件参数 | 样本场取证情况(2026-09-22) |
 |---|---|
-| 主轴轴承型号及厂家 | 部分取证(`reference/rudong/oem_bearing_freqs.json` 有位置与型号 token 级条目,如 `139.1/1060 f-576853.PRL`、`240.1/800 F-605082.PRL`) |
+| 主轴轴承型号及厂家 | 部分取证(`reference/<场>/oem_bearing_freqs.json` 有位置与型号 token 级条目,如 `139.1/1060 f-576853.PRL`、`240.1/800 F-605082.PRL`) |
 | 齿轮箱型号厂家 | 已取证(威能极 PZAB 3546) |
 | 齿轮箱各级传动参数(行星轮内齿圈齿数、行星轮个数、太阳轮齿数、行星轮齿数、平行轮大齿轮齿数、平行轮小齿轮齿数) | 部分取证(`gear_mesh_map_measured.json` 给出各级啮合频率与齿数:一级行星 92、二级行星 91、高速级小齿轮 30,以及各级传动比 7.999、4.24、3.531;**行星轮个数未取证**) |
 | 齿轮箱各级轴承型号 | 部分取证(`oem_bearing_freqs.json` 29 个轴承条目,含 NSK/FAG 候选与位置 token) |
@@ -722,7 +725,7 @@
 | `UserName` | 记录来源通道 |
 | `idKey`、`Comment`、`MailSend`、`Acknowledge`、`Reset` | 部分未用或为死列 |
 
-**故障码表**:如海代码表 **2,793 条**,实际用码 322 个、**覆盖率 100%**;三表(对译表、故障处理手册、如海代码表)直译族一致。事件层用码的字典覆盖率为 alarm 356/364 = 97.8%(未覆盖的 8 个是 50xxx 平台码混入)、event 60/66(缺 170 与 172 至 178 的低频扩展码)。
+**故障码表**:场站故障代码表(样本 **2,793 条**),实际用码 322 个、**覆盖率 100%**;三表(对译表、故障处理手册、场站故障代码表)直译族一致。事件层用码的字典覆盖率为 alarm 356/364 = 97.8%(未覆盖的 8 个是 50xxx 平台码混入)、event 60/66(缺 170 与 172 至 178 的低频扩展码)。
 
 **首发标识缺席,须自算**:按台对 `LogType=0 ∧ AlarmGroup=1` 按 `TimeOn` 升序,链尾取累计 `max(TimeOff)`,`TimeOn > 链尾 + 30 min` 即新链首 = 首发;`X ∈ {10, 30, 60} min` 的敏感性下 `cascade_ratio` 稳定在 3.49 至 3.83。2025-01 至 2026-07 共 **10,506 条首发链**,每台中位 257 条,离群台为 33 号(666)、19 号(512)、08 号(422)。
 
@@ -739,7 +742,7 @@
 | C3 桨距调度差 | 桨距角 × 功率档(500 kW 档)逐台 schedule,与全场同档中位比,平均绝对偏 > 0.5° 判"标定偏移候选" | `tur_wtc_PitcPosA_mean` |
 | C4 尺子巡检 | 满发功率 p99.5、转速封顶、额定桨角(> 3800 kW 时 A 叶桨距角中位)三把尺子的机群散布 | 同上 |
 
-**正式的机组保护定值**在 `outputs/rudong/ontology/turbine_params.parquet`(**1,706 条**整定值,列 `Number`、`Text`、`Min`、`Max`、`Default`、`Current`、`Value`、`Unit`、`Reset`),来源为技术资料里的 `Turbine+Parameters.*.xlsx`。它覆盖温度、压力、速度等保护定值的逐条 `Min`/`Max`/`Default`/`Current` 与单位,因此**定值表本身已在位**(例如 `UP01101: Max ambient temperature` 默认 40 ℃、`UP01202: Tower fan LS tow/nac temp` 默认 25 ℃)。
+**正式的机组保护定值**在 `outputs/<场>/ontology/turbine_params.parquet`(**1,706 条**整定值,列 `Number`、`Text`、`Min`、`Max`、`Default`、`Current`、`Value`、`Unit`、`Reset`),来源为技术资料里的 `Turbine+Parameters.*.xlsx`。它覆盖温度、压力、速度等保护定值的逐条 `Min`/`Max`/`Default`/`Current` 与单位,因此**定值表本身已在位**(例如 `UP01101: Max ambient temperature` 默认 40 ℃、`UP01202: Tower fan LS tow/nac temp` 默认 25 ℃)。
 
 **缺的是"成册交付"**:定值表以整定值清单形式在位,但收资要求 v3 第 10 项点名的是"温度、压力、速度等保护定值 + 偏航控制策略 + 降载荷控制策略"的**策略文档**。偏航控制策略与降载荷控制策略目前**未取证**,只能从控制参数反演与事件层行为间接反映。
 
@@ -757,7 +760,7 @@
 | 现场类目库 | `StationId` 4800167 至 4800204 | 与上一致 |
 | 事件层台号列 | `tid` = `NN` + string 字母 | 装配时加列,非机组行为 NaN |
 
-**要求**:主件文件名必须是场配置里的机组号;1 分钟族用内部台号(按**台数**判完整,不按名字判)。台号别名(`01E` 与 `WTG01` 的对应)必须显式给一张对照表,禁止靠位置或排序猜。
+**要求**:主件文件名必须是场配置里的机组号;1 分钟族用内部台号(按**台数**判完整,不按名字判)。台号别名(`01E` 与 `WTG01` 的对应)必须显式给一张对照表,禁止靠位置或排序猜。表中 `WTG01` 至 `WTG38`、`01E` 至 `38B`、`StationNr` 4800167 至 4800204 都是**样本场内部编号,换场按实际台号替换**;编号形态(`WTG<NN>` 与 `<NN><线路字母>`)可以沿用,但台号与内部台号的对应关系必须显式声明。
 
 ### 6.2 时间戳格式与时区
 
@@ -783,7 +786,7 @@
 
 另有已知的**量化与哨兵族**(用数据前必处理):
 
-**表 6-3 量化与哨兵族**
+**表 6-3 量化与哨兵族(样本场实测 2026-09-22)**
 
 | 现象 | 实测 |
 |---|---|
@@ -813,7 +816,7 @@
 2. **风速**:`m/s` 与 `mps` 并存(55 条变体表中含 `WMET_wind_speed_10s`/`60s`/`inst` 等),**同量纲**;
 3. **风速计原值与补偿风**:`WMET_wind_speed_compensated` 与 `WMET_wind_speed_nacelle2` 都带 `self_ref_risk: true`,**不能**当独立第二证据。
 
-压力类在词典里同时存在 `bar`(23 条)、`kPa`、`Pa`、`hPa` 四种量纲,**必须逐条看词条**,不能按"压力就是 bar"推定:例如 `WMET_pressure` 是 kPa、`WMET_pressure_2` 是 Pa、`WMET_pressure_mast` 是 hPa、`WTRM_oil_pressure`(canonical `WGBX_oil_pressure`)在词典里被标为 `Pa`,而在如东契约里对应列 `prs_wtc_GearPres_mean` 标 `bar` —— 这属**跨源量纲分歧**,用前须按契约的 `valid_range` 复核。
+压力类在词典里同时存在 `bar`(23 条)、`kPa`、`Pa`、`hPa` 四种量纲,**必须逐条看词条**,不能按"压力就是 bar"推定:例如 `WMET_pressure` 是 kPa、`WMET_pressure_2` 是 Pa、`WMET_pressure_mast` 是 hPa、`WTRM_oil_pressure`(canonical `WGBX_oil_pressure`)在词典里被标为 `Pa`,而在本场契约里对应列 `prs_wtc_GearPres_mean` 标 `bar` —— 这属**跨源量纲分歧**,用前须按契约的 `valid_range` 复核。
 
 ### 6.8 编码与列名约定
 
@@ -833,16 +836,16 @@
 | 位置 | 放什么 | 形态要求 |
 |---|---|---|
 | `data\raw\` 顶层 | 场站目录与说明文件 | 只放场站目录与 `README_把原始数据放这里.txt` 一类说明件 |
-| `data\raw\如东\` | 场站目录(下一级目录名 = 场站) | 目录名与场配置 `raw_station` 相同,或与场名互为子串 |
-| `如东\scada_10min\` | 10 分钟导出 | 平铺 `WTG01.csv` 至 `WTG38.csv`,另可放同台补充件 `<台号>-*.csv` |
-| `如东\scada_1min\` | 1 分钟导出 | 平铺内部台号 `01E.csv` 至 `38B.csv` |
-| `如东\scada_mdb\<年>年\<月>月\` | 现场年度归档库 | `<年>-<月>-<类>.mdb`,类目见第 5.1 节 |
-| `如东\故障报警\` | 报警事件导出 | `.xls`(实为 SpreadsheetML XML)、`.xlsx`、`.xml`,不许多套一层目录 |
-| `如东\风机故障记录\` | 检修工单台账 | 年目录 + 月度汇总表 `.xls`/`.xlsx`;`.rar` 与照片为附件 |
-| `如东\油样报告\` | 油液化验报告 | 必须是**两级**:`<台号>\<部件>\*.pdf` |
-| `如东\windcms\` | CMS 原始测量导出 + 厂家报告 | `<包名>\measurement\<年>\<月>\<WTGxx>\*_decode.json`;报告为 `.docx` 或 `.pdf` |
-| `如东\m5_cms_tcm\` | handoff 正本 + 厂家报告 | handoff 正本 `.json` + `厂家报告\` 下的 `.docx` |
-| `西门子4.0技术资料\` | 厂商技术资料(与场站并列) | 整棵树都读,不在场站目录下 |
+| `data\raw\<场站>\` | 场站目录(下一级目录名 = 场站) | 目录名与场配置 `raw_station` 相同,或与场名互为子串 |
+| `<场站>\scada_10min\` | 10 分钟导出 | 平铺 `WTG01.csv` 至 `WTG38.csv`,另可放同台补充件 `<台号>-*.csv` |
+| `<场站>\scada_1min\` | 1 分钟导出 | 平铺内部台号 `01E.csv` 至 `38B.csv` |
+| `<场站>\scada_mdb\<年>年\<月>月\` | 现场年度归档库 | `<年>-<月>-<类>.mdb`,类目见第 5.1 节 |
+| `<场站>\故障报警\` | 报警事件导出 | `.xls`(实为 SpreadsheetML XML)、`.xlsx`、`.xml`,不许多套一层目录 |
+| `<场站>\风机故障记录\` | 检修工单台账 | 年目录 + 月度汇总表 `.xls`/`.xlsx`;`.rar` 与照片为附件 |
+| `<场站>\油样报告\` | 油液化验报告 | 必须是**两级**:`<台号>\<部件>\*.pdf` |
+| `<场站>\windcms\` | CMS 原始测量导出 + 厂家报告 | `<包名>\measurement\<年>\<月>\<WTGxx>\*_decode.json`;报告为 `.docx` 或 `.pdf` |
+| `<场站>\m5_cms_tcm\` | handoff 正本 + 厂家报告 | handoff 正本 `.json` + `厂家报告\` 下的 `.docx` |
+| `<机型>技术资料\` | 厂商技术资料(与场站并列) | 整棵树都读,不在场站目录下 |
 
 ### 7.2 增量三判定
 
@@ -911,7 +914,7 @@
 
 | 步 | 动作 | 说明 |
 |---|---|---|
-| ① | 放数据 `place_raw_data.py` | 原样搬现场包到 `data/raw/<场>/`;同尺寸跳过,冲突拦下 |
+| ① | 放数据 `place_raw_data.py` | 原样搬现场包到 `data/raw/<场站>/`;同尺寸跳过,冲突拦下 |
 | ①b | 输入扫描 `raw_scan.py` | 逐族指纹(件数/体积/最新落盘时间/清单摘要/子目录清单);发现新增或变化就列出"该跑哪几步"并记成新基线 |
 | ② | 三门台账 `rebuild_from_raw.py` | 报警、工单、油样;产物 = 当前源件的函数 |
 | ③ | SCADA 侧 `rebuild_from_raw.py --scada` | 10 个构建器,读约 15 GB 的 10 分钟 CSV,约 15 分钟 |
@@ -944,24 +947,24 @@
 | 7 | 机组状态、事件、故障、告警数据 | 必须 | `故障报警/` + `风机故障记录/` + 事件层 | 已在位 | 码表 2,793 条、用码 322 个覆盖 100%;事件层 364 个 alarm 码中 8 个 50xxx 平台码无字典;event 缺 170 与 172 至 178 |
 | 8 | 机舱 CMS 振动数据 | 必须 | `windcms/` | 已在位 | 要求至少一个季度;实测只有约 36 天(2026-03-16 至 2026-04-21)。**须补**:2026-01 的 decode 导出,以及 `w0707`/`w0711`/`w0724`/`w0805`/`w0811` 五个窗 |
 | 9 | 机组统计数据或运行数据 | 必须 | `scada_10min/` + `scada_mdb/sum` 层 | 已在位 | 发电量、可利用率、限电与损失族由 SCADA 与汇总层重建;`DailySummary` 206 列在 `sum` 库内 |
-| 10 | 机组保护定值及控制参数 | 必须 | `西门子4.0技术资料/` | 部分 | 整定值 1,706 条已在位(含 Min/Max/Default/Current 与单位);**M9 的控制参数是反演值**(功率封顶、转速封顶、桨距调度);**偏航控制策略与降载荷控制策略未取证** |
+| 10 | 机组保护定值及控制参数 | 必须 | `<机型>技术资料/` | 部分 | 整定值 1,706 条已在位(含 Min/Max/Default/Current 与单位);**M9 的控制参数是反演值**(功率封顶、转速封顶、桨距调度);**偏航控制策略与降载荷控制策略未取证** |
 | 11 | 机组故障录波数据 | 可选 | 故意不落(`fastlog数据/`) | 未到位 | 全库只有 2 处注释提到 `fastlog`,且为 **10 Hz 事件快照、仅 4 台**;观澜不消费,无摄入器 |
 | 12 | 可研报告及微观选址报告 | 建议 | 未约定族 | 未到位 | 目录未见交付;可研内有 2009-12 史料塔记录(100 m × 6 层,**非运行期**)与逐台设计基线(2660 h、尾流 6.8%)可提取 |
 | 13 | 风电场简介 | 建议 | 散见契约与技术资料 | 部分 | 场站元信息散见合同与技术资料,未成册 |
 | 14 | 风电场坐标、机位布置图 | 建议 | 技术资料 | 已在位(依契约记载) | "布置图含于技术资料"为契约记载,**本机在 `data/raw` 内未取证到布置图件**;坐标件同第 6 项 |
-| 15 | 风机技术协议及风机合同 | 必须 | `西门子4.0技术资料/` | 已在位 | 含担保曲线附件(图 2-1、图 2-3)、保证率条款 |
+| 15 | 风机技术协议及风机合同 | 必须 | `<机型>技术资料/` | 已在位 | 含担保曲线附件(图 2-1、图 2-3)、保证率条款 |
 | 16 | 并网到现在的生产报表 | 必须 | 随族落位 | 部分 | 日粒度发电账 555 天 + 全寿命月账自 2016-06 起;**"并网至今"完整序列与口径说明未成册** |
 | 17 | 周围已建风电场生产报表 | 可选 | 未约定族 | 未到位 | 未见交付;缺则尾流与场级损耗缺外部对照 |
 | 18 | 运维记录 | 建议 | `风机故障记录/` + `油样报告/` | 已在位 | 检修记录 2025 与 2026、油样 404 件(缺 2026-07 批 102 行的合并报告源件) |
 | 19 | 大部件更换记录 | 建议 | `风机故障记录/` | 部分 | 出保备件台账在位(`大部件维修记录.20240619143912557.xlsx` 一类);**更换台账不完整**,在升与换件闭环目前只有 1 个导出色窗、**如实为空** |
-| 20 | 图纸、技术文件 | 必须 | `西门子4.0技术资料/` | 已在位 | 电气原理图、接线表、液压图纸 3 份、主控图纸、故障逻辑、保护定值表、结构树均到位 |
-| 21 | 设备参数 | 必须 | `西门子4.0技术资料/` + `reference/rudong/` | 部分 | 主轴轴承、齿轮箱型号与各级齿数、发电机轴承、齿轮箱各级轴承部分在位;**叶片参数、变桨与偏航电机参数、变桨/偏航大小齿齿数、发电机型号厂家、行星轮个数未取证** |
+| 20 | 图纸、技术文件 | 必须 | `<机型>技术资料/` | 已在位 | 电气原理图、接线表、液压图纸 3 份、主控图纸、故障逻辑、保护定值表、结构树均到位 |
+| 21 | 设备参数 | 必须 | `<机型>技术资料/` + `reference/<场>/` | 部分 | 主轴轴承、齿轮箱型号与各级齿数、发电机轴承、齿轮箱各级轴承部分在位;**叶片参数、变桨与偏航电机参数、变桨/偏航大小齿齿数、发电机型号厂家、行星轮个数未取证** |
 
 ### 8.2 到位状态分布
 
-**如图 2-3 所示**,21 类中"已在位"10 项、"部分"8 项、"未到位"3 项。
+**如图 2-3 所示**,21 类中"已在位"10 项、"部分"7 项、"未到位"4 项(逐行统计依第 8.1 节表,与图中数字同源)。
 
-![图 2-3 收资要求 v3 的 21 类在观澜的到位状态](figures/fig-dat-03-收资项覆盖.png)
+![图 2-3 收资要求 v3 的 21 类在观澜的到位状态(样本场实测 2026-09-22)](figures/fig-dat-03-收资项覆盖.png)
 
 ### 8.3 优先级建议
 
@@ -969,11 +972,13 @@
 
 | 优先级 | 判定依据 | 数据项 | 现场动作 |
 |---|---|---|---|
-| P0 | 缺则核心分析面不可判或核对锚点对不上 | 1(不足 2 年)、8(不足一季)、3(零交付)、5(切入切出/额定风速/生存温度) | 先补这三样的**缺口部分**:2024 年 10 分钟、2026-01 与 2026-07/08 的 CMS 导出、测风塔全量、机型参数表 |
+| P0 | 缺则核心分析面不可判或核对锚点对不上 | 1(不足 2 年)、8(不足一季)、3(零交付)、5(切入切出/额定风速/生存温度) | 先补这些项的**缺口部分**:2024 年 10 分钟、2026-01 与 2026-07/08 的 CMS 导出、测风塔全量、机型参数表 |
 | P1 | 缺则某一条面降级或不能交叉互证 | 6、4、10、16、19、21 | 坐标件、标准空气密度功率曲线、保护定值成册与偏航/降载荷控制策略、并网至今生产报表、大部件更换台账、部件参数明细 |
 | P2 | 缺则只影响专题或外部对照 | 2、12、13、14、17 | 1 s/5 s 高频、可研与微观选址、风电场简介、布置图、周边风电场报表 |
 | 不催 | 观澜无消费者 | 11 | 故障录波:如将来要做秒级故障回溯,再新增摄入器与判据 |
 
+本表是第 12.1 节规则在样本场的落地;**换场重新定档后,P0/P1/P2 的项号可能不同**,按规则而非按本表的项号执行。
+
 ## 9 缺失与替代清单
 
 **表 9-1 缺失项、影响与替代件**
@@ -981,10 +986,10 @@
 | 缺失项 | 影响 | 替代件与口径 | 性质 |
 |---|---|---|---|
 | 测风塔数据(零交付) | 自由流风速与湍流度缺场级参考;只能有机舱风速计与补偿风 | 用 `tur_wtc_SecAnemo_mean`(原生第二风速计)作曲线 X 轴;湍流度只能用 `WMET_ti` 一类的**未验证**代理量 | 现场补件 |
-| `m5_cms_tcm` handoff 正本缺失 | 工作台振动融合面(`/api/fleet` 的 `fus`)如实报缺件 | 观澜自算件:`scripts/rudong_fusion_handoff.py`(融合面)与 `scripts/component_history_build.py`(在升与换件闭环);**本机只有 1 个导出色窗,故在升与闭环如实为空**,数据边界写进 `meta.★数据边界`;现场正本在位时**正本优先** | 替代 |
+| `m5_cms_tcm` handoff 正本缺失 | 工作台振动融合面(`/api/fleet` 的 `fus`)如实报缺件 | 观澜自算件:`scripts/<场>_fusion_handoff.py`(融合面)与 `scripts/component_history_build.py`(在升与换件闭环);**本机只有 1 个导出色窗,故在升与闭环如实为空**,数据边界写进 `meta.★数据边界`;现场正本在位时**正本优先** | 替代 |
 | 油样 2026-07 批 102 行 | 油液面从 506 行落到 404 行 | 摄入采取**合并语义**:认不出的老行原样保留、绝不删 | 现场补件(那份合并报告 PDF) |
 | CMS 六层链的模型层与融合层标准答案缺失 | `model_run_l6.parquet` 与 `fusion_38.csv` 从未随包,无法逐值对拍 | 已按包内既有判据**按口径重建**(判据不新写、只接线);实测窗 `w0316`:候选 1,026 条 → 过闸 14 条 → 定级参考 11 / 候选 3;`fusion_38.csv` 38 台 → 正常 13 / 参考 18 / 候选 7 | 替代(**不许当"复现"卖**) |
-| `oem_scan` 的峰值拾取口径缺失 | `oem_frequency_scan` 的分子(三对比值列)留空,脚本默认运行返回 rc=3 且**不写盘**(只写分母的件"看着像扫描结果、实则没有分子") | 分母与行骨架已就位并逐值验过(11/11 部件映射;行骨架 2,420 行逐键一致);配置与理论频率表已固化在 `reference/rudong/` | 研发/振动线补口径 |
+| `oem_scan` 的峰值拾取口径缺失 | `oem_frequency_scan` 的分子(三对比值列)留空,脚本默认运行返回 rc=3 且**不写盘**(只写分母的件"看着像扫描结果、实则没有分子") | 分母与行骨架已就位并逐值验过(11/11 部件映射;行骨架 2,420 行逐键一致);配置与理论频率表已固化在 `reference/<场>/` | 研发/振动线补口径 |
 | 秒级高频(1 s/5 s)未用 | 变桨面只用 1 分钟层 | 1 分钟层已含零位三口径所需通道 | 现场补件(1 s/5 s) |
 | 故障录波未接入 | 无(当前分析面不消费) | 无 | 不催 |
 | 厂商月度报告 12 份为纯扫描件 | 「CMS 振动评估报告」的厂商侧数值取不出 | 只登记归档(记 sha256、大小、无文本层);有文本层的 docx 已转录 | 现场给电子件,或上 OCR |
@@ -997,29 +1002,29 @@
 
 ### 10.1 体量与件数实测
 
-**表 10-1 实测体量与件数(2026-09-22)**
+**表 10-1 样本场实测体量与件数(2026-09-22)**
 
 | 位置 | 件数 | 体量 |
 |---|---|---|
-| `data/raw/如东`(含 1 件散装按月核对件) | 26,504 | 约 224.67 GB |
-| `data/raw/如东/scada_10min` | 114 | 16,749,788,062 B(约 15.6 GB) |
-| `data/raw/如东/scada_1min` | 38 | 13,709,173,574 B(约 12.8 GB) |
-| `data/raw/如东/scada_mdb` | 100 | 48,247,200,121 B(约 44.9 GB) |
-| `data/raw/如东/windcms` | 25,693 | 161,219,600,707 B(约 150.1 GB) |
-| `data/raw/如东/故障报警` | 16 | 23,844,357 B(约 22.7 MB) |
-| `data/raw/如东/风机故障记录` | 137 | 149,795,133 B(约 142.9 MB) |
-| `data/raw/如东/油样报告` | 404 | 176,226,585 B(约 168.1 MB) |
-| `data/raw/如东/m5_cms_tcm` | 1 | 21,823,545 B(约 20.8 MB) |
-| `data/raw/西门子4.0技术资料` | 318 | 2,936,758,089 B(约 2.7 GB) |
-| `outputs/rudong` | 3,549 | 7,157.5 MB |
+| `data/raw/<场站>`(含 1 件散装按月核对件) | 26,504 | 约 224.67 GB |
+| `data/raw/<场站>/scada_10min` | 114 | 16,749,788,062 B(约 15.6 GB) |
+| `data/raw/<场站>/scada_1min` | 38 | 13,709,173,574 B(约 12.8 GB) |
+| `data/raw/<场站>/scada_mdb` | 100 | 48,247,200,121 B(约 44.9 GB) |
+| `data/raw/<场站>/windcms` | 25,693 | 161,219,600,707 B(约 150.1 GB) |
+| `data/raw/<场站>/故障报警` | 16 | 23,844,357 B(约 22.7 MB) |
+| `data/raw/<场站>/风机故障记录` | 137 | 149,795,133 B(约 142.9 MB) |
+| `data/raw/<场站>/油样报告` | 404 | 176,226,585 B(约 168.1 MB) |
+| `data/raw/<场站>/m5_cms_tcm` | 1 | 21,823,545 B(约 20.8 MB) |
+| `data/raw/<机型>技术资料` | 318 | 2,936,758,089 B(约 2.7 GB) |
+| `outputs/<场>` | 3,549 | 7,157.5 MB |
 
 ### 10.2 时间覆盖与缺口
 
 **如图 2-4 所示**,关键数据的时间覆盖如下。
 
-![图 2-4 关键数据的时间覆盖与缺口](figures/fig-dat-04-时间覆盖.png)
+![图 2-4 关键数据的时间覆盖与缺口(样本场实测 2026-09-22)](figures/fig-dat-04-时间覆盖.png)
 
-**表 10-2 关键数据的时间覆盖**
+**表 10-2 关键数据的时间覆盖(样本场实测 2026-09-22)**
 
 | 数据 | 起点 | 终点 | 覆盖 | 缺口 |
 |---|---|---|---|---|
@@ -1036,13 +1041,13 @@
 
 ### 10.3 核对锚点
 
-重算后应与下列锚点对齐(本机实测):
+重算后应与下列锚点对齐(样本场实测 2026-09-22):
 
-**表 10-3 核对锚点**
+**表 10-3 核对锚点(样本场实测 2026-09-22)**
 
 | 锚点 | 实测值 | 来源 |
 |---|---|---|
-| 报警台账行数 | 39,211 | `outputs/rudong/windscada/alarms.parquet` |
+| 报警台账行数 | 39,211 | `outputs/<场>/windscada/alarms.parquet` |
 | 检修工单行数 | 5,876 | `workorders.parquet`(26 列) |
 | 油样报告行数 | 404 | `oil_samples_index.parquet` |
 | 温度月表行数 | 19,494(基线口径) | `temp_monthly.parquet`;本机当前实测为 **21,492 行**(含补充件带来的 2026-08/09 新增月),两者差异已如实记录 |
@@ -1058,11 +1063,11 @@
 | 振动窗索引 | 2,066,686 行 × 54 列 | `m5_cms_tcm/windows/w0316/index.parquet` |
 | 振动谱 | 420,742 条 / 1,701 分片 / 3.15 GB | `spectra_meta.parquet` + `spectra/*.npz` |
 | 整定值 | 1,706 条 | `ontology/turbine_params.parquet` |
-| 故障码表 | 2,793 条(用码 322 个,覆盖 100%) | 如海代码表 |
+| 故障码表 | 2,793 条(用码 322 个,覆盖 100%) | 场站故障代码表 |
 
 ## 11 面向现场的收资清单
 
-**表 11-1 可打印收资清单**
+**表 11-1 可打印收资清单(本场口径;换场按第 12 章裁剪)**
 
 | 优先级 | 数据项 | 最小可用量 | 必需测点 | 交付形式与命名 |
 |---|---|---|---|---|
@@ -1083,31 +1088,192 @@
 | P2 | 机位布置图 | 一份 | 全场机组与升压站位置 | `.pdf` 或 `.dwg` |
 | P2 | 周边风电场生产报表 | 覆盖分析时间窗 | 风速、发电量、故障率、限电量 | 表格 |
 
+## 12 通用收资模板与换场适配
+
+第 8 章的逐条结论只对样本场成立。换场能复用的不是这些结论,而是**得出这些结论的规则**。本章把收资做成两部分:**通用必选与可选判定规则**(第 12.1 节,不依赖具体场)与**换场适配件**(第 12.2 至第 12.5 节:场配置、数据源形态、差异清单、裁剪步骤)。
+
+### 12.1 通用必选/可选判定规则
+
+#### 12.1.1 五问定档
+
+一项收资到底是"必须"还是"可选",只问五个与场无关的问题。判定顺序:先问 R5(有没有消费者),命中"无消费者"就直接**不催**;否则依次问 R1、R2、R3、R4,**首个命中即定档**。
+
+**表 12-1 通用判定规则(五问定档)**
+
+| 规则 | 判定问题 | 命中则 | 规则档 | 缺了会怎样 |
+|---|---|---|---|---|
+| R5 无消费者 | 本系统有没有摄入器与产物真的消费它? | 没有 | 不催 | 无影响;将来要做该专题时再新增摄入器与判据 |
+| R1 主线阻断 | 缺了会不会让判级四轴、发电性能、可靠性、台账或合同指标对标中任一条主线面不可判,或让核对锚点对不上? | 会 | P0(必须) | 该主线面显示"缺件/不可判",重算链对应步骤响亮报错或跳过,**不造数** |
+| R2 精度与互证 | 缺了不阻断主线,但会不会让结论精度下降、跨源互证或"检出 → 处置 → 复测"闭环做不了? | 会 | P1(建议) | 结论仍在,但依据变少,跨源互证与闭环缺一环 |
+| R3 有自算替代 | 缺了能不能由本系统**自算件**或**已在位的其他数据**按已登记口径顶替? | 能 | P2(替代) | 用替代件并如实标注来源;现场正本在位时**正本优先** |
+| R4 只影响外部对照 | 缺了是不是只让某个专题或场外对照(邻场报表、可研与微观选址、测风塔外部锚)缺参照? | 是 | P2(可选) | 只有对应专题或外部对照不可做 |
+
+#### 12.1.2 21 项收资的规则化分档
+
+按上表过一遍收资要求 v3 的 21 项,得到**与场无关**的分档(表 12-2)。第 8.3 节的优先级建议是同一套规则在样本场的落地:已在位的 P0 与 P1 项不再列入催缴动作,只保留缺口部分。
+
+**表 12-2 21 项收资的规则化分档**
+
+| 序号 | 收资项 | 命中规则 | 规则档 | 换场动作(与场无关的表述) |
+|---|---|---|---|---|
+| 1 | 10 分钟 SCADA 数据 | R1 | P0 | 逐台导出覆盖分析时间窗;判级参照时间窗与季节匹配时间窗各需一段;缺台即该台不可判 |
+| 2 | 秒级 SCADA 高频数据 | R3 | P2 | 无 1 秒或 5 秒原生高频时,用 1 分钟转发层顶替 |
+| 3 | 测风塔数据 | R1 | P0 | 前期实地测风满一年,运行期一整年分钟级;缺则自由流与湍流度只能用代理量 |
+| 4 | 功率曲线 | R2 | P1 | 标准空气密度曲线与合同担保曲线各一条;缺则只能做机群相对比较 |
+| 5 | 机型参数 | R1 | P0 | 换机型即换表:切入切出风速、额定风速、额定功率、叶轮直径、轮毂高度、传动比、生存温度 |
+| 6 | 机位分布数据 | R2 | P1 | 逐台坐标与海拔(含轮毂高度);坐标不可自算 |
+| 7 | 机组状态、事件、故障、告警数据 | R1 | P0 | 状态码表、故障码表与详细记录按场重收;码表是厂家私有命名空间 |
+| 8 | 机舱 CMS 振动数据 | R1 | P0 | 至少一个季度的秒级振动数据(非加密)与原始振动时域波形;缺则振动侧无源可算 |
+| 9 | 机组统计数据或运行数据 | R1 | P0 | 年月日统计量可由 SCADA 与汇总层重建,但前提是第 1 项到位 |
+| 10 | 机组保护定值及控制参数 | R2 | P1 | 定值表成册加偏航控制策略与降载荷控制策略;控制参数一致性件可先由数据反演顶替 |
+| 11 | 机组故障录波数据 | R5 | 不催 | 本系统不消费;将来要做秒级故障回溯时再新增摄入器与判据 |
+| 12 | 可研报告及微观选址报告 | R4 | P2 | 逐台设计基线与测风依据用作外部对照 |
+| 13 | 风电场简介 | R4 | P2 | 场站元信息(规模、投产时间、海域特性)用于背景与署名 |
+| 14 | 风电场坐标、机位布置图 | R4 | P2 | 布置图可由坐标件生成;第 6 项到位后本项只影响图示对照 |
+| 15 | 风机技术协议及风机合同 | R1 | P0 | 担保曲线与保证率条款是合同指标对标的依据 |
+| 16 | 并网到现在的生产报表 | R2 | P1 | 与 SCADA 侧损失账对拍;口径必须成册 |
+| 17 | 周围已建风电场生产报表 | R4 | P2 | 尾流与场级损耗的外部对照 |
+| 18 | 运维记录 | R2 | P1 | 维护与检修台账,与油样、报警三源交叉 |
+| 19 | 大部件更换记录 | R2 | P1 | 逐次更换的时间、机组、部件、原因;缺则闭环停在处置侧 |
+| 20 | 图纸、技术文件 | R1 | P0 | 电气控制、液压、逻辑图与保护定值表是位域语义与判据的最终依据 |
+| 21 | 设备参数 | R2 | P1 | 逐部件型号与齿数是振动阶次定位与寿命评估的分母 |
+
+小结:按规则分档为 **P0 八项**(1、3、5、7、8、9、15、20)、**P1 七项**(4、6、10、16、18、19、21)、**P2 五项**(2、12、13、14、17)、**不催一项**(11)。
+
+### 12.2 场配置字段对照表
+
+换场的第一件事是写一份 `configs/farms/<场>.yaml`(模板见 `configs/farms/_模板.yaml.example`)。必填键缺任何一个,场定义在加载时**响亮报错**(提示"场配置缺必需键"并列出缺的键名),页面与重算链都不会带着半套配置起来。
+
+**表 12-3 场配置必填键 → 对应收资项 → 缺了会怎样**
+
+| 必填键 | 含义与取值形态 | 对应收资里的哪一类 | 缺了会怎样 |
+|---|---|---|---|
+| `name` | 场站名(对外文档只出现场站名,不出现业主与地域) | 第 13 项 风电场简介、第 15 项 风机技术协议及风机合同 | 页面与报告无处署名,`outputs/<场>/` 无落点,台账筛选缺默认场名 |
+| `n_turbines` | 机组台数(整数) | 第 1 项 10 分钟 SCADA、第 2 项 秒级高频、第 6 项 机位分布 | 台数无从校验,"按台取数"缺完整性判据;整族缺台不报错、只出空白 |
+| `turbines` | 机组号列表或简写(如 `WTG{:02d}:1-38`,或直接列全) | 第 1 项、第 2 项、第 6 项(台号与内部台号对照) | 主件文件名对不上、1 分钟内部台号无法映射,邻域分析失去台号基准 |
+| `src_10min` | 10 分钟源件目录(`data/raw/<场站>/scada_10min`) | 第 1 项 10 分钟 SCADA | 该族扫描不到,判级四轴、七镜头曲线、损失账、停机账同时无输入 |
+| `src_alarm` | 报警事件导出目录(`data/raw/<场站>/故障报警`) | 第 7 项 机组状态、事件、故障、告警数据 | 报警台账为 0 行,停机原因链、限电绑定、首发链不可用 |
+| `store` | 标准仓落点(`outputs/<场>/windscada`) | 第 9 项 机组统计数据或运行数据(产物落点) | 产物无处落盘,页面读不到本场仓 |
+| `rated_kw` | 单机额定功率(kW) | 第 5 项 机型参数 | 功率曲线基准、损失电量折算与限电判定失去额定口径 |
+
+三个 `src_*` 与 `store` 一类键**留空即可由扫描派生**:加载器先按 `raw_station` 辨识 `data/raw/` 下的场站目录,再把各约定子目录补成 `src_10min`、`src_1min`、`src_alarm`、`src_workorder`、`src_oil` 等;显式给了就以显式值为准(现场件目录名与约定不同时走这一条)。两份建议键:`raw_station`(`data/raw/` 下的实际目录名,扫描辨识首选)与 `src_farm_names`(台账里的场名写法别名,集团导出件常与配置名不同);`contract` 指向 `reference/<场>/windscada_contract.yaml`。
+
+### 12.3 不同数据源形态的适配
+
+第 8 章回答"要什么数据",换场还要回答"现场给的是哪种形态"。三种常见形态的**最小可用量**与**列名绑定要求**不同,分别如下。
+
+#### 12.3.1 形态一:CSV 逐台转发层
+
+**形态判据**:源类目录下平铺逐台文件,10 分钟层为 `WTG<NN>.csv`、1 分钟层为 `<NN><线路字母>.csv`,另可有同台补充件 `<台号>-*.csv`。
+
+**最小可用量**:逐台一份,覆盖分析时间窗;10 分钟层必须含第 5.2 节的 48 列公共子集(缺列即对应分析面不可判);采样周期 600 s(10 分钟)或 60 s(1 分钟)。
+
+**列名绑定要求**:一律**按列名**读取、**禁用位置索引**——同一场各台列序互不相同,按位置取数会得到"名字对不上数"的数据;同台补充件与新导出会去掉类型前缀(`din_wtc_X` 变成 `wtc_X`),对齐也只能按列名;缺列写进 `columns_missing`,**不造 0、不静默丢列**;CSV 编码必须是 `utf-8-sig`(含字节顺序标记(BOM)),否则中文列名会读成乱码;时间戳按 `YYYY-MM-DD HH:MM:SS`,同一场两侧必须同一约定。
+
+#### 12.3.2 形态二:现场月度类目归档库
+
+**形态判据**:现场按"月份 × 通道组"交付的类目归档库(`.mdb`),落位为 `data/raw/<场站>/scada_mdb/<年>年/<月>月/<年>-<月>-<类>.mdb`。它是 10 分钟同台补充件的**上游**:取数层不直接读类目库,先合成同台补充件 CSV;某台没有 CSV 时回落到 `<年>年/<月>月/*-10min.mdb`。
+
+**最小可用量**:9 个逐台类目按分析时间窗逐月齐,且与主件**前缀块一一对应、0 缺 0 多**(样本场实测 595 通道)。逐台类目与通道数随测点清单变,**换机型必须重新核对**,不能沿用样本场的数字。
+
+**表 12-4 12 类目 ↔ 中文目录名 ↔ 契约列前缀 ↔ 通道数(样本场实测 2026-09-22)**
+
+| 类目码 | 中文目录名 | 契约列前缀 | 通道数 | 是否逐台 |
+|---|---|---|---|---|
+| `tur` | 风机数据 | `tur_wtc_` | 91 | 逐台 |
+| `din` | DigiIn数据 | `din_wtc_` | 93 | 逐台 |
+| `dot` | DigiOut数据 | `dot_wtc_` | 69 | 逐台 |
+| `cnt` | 计算数据 | `cnt_wtc_` | 69 | 逐台 |
+| `tmp` | 温度数据 | `tmp_wtc_` | 116 | 逐台 |
+| `prs` | 压力数据 | `prs_wtc_` | 52 | 逐台 |
+| `grd` | 电网数据 | `grd_wtc_` | 50 | 逐台 |
+| `flg` | 标志数据 | `flg_wtc_` | 42 | 逐台 |
+| `int` | 内部数据 | `int_wtc_` | 13 | 逐台 |
+| `scd` | 统计数据 | —— | —— | 台账级 |
+| `std` | 标准数据 | —— | —— | 台账级 |
+| `sum` | 汇总数据 | —— | —— | 台账级(含 `AlarmLog` 事件与 `ParkPilot` 限电指令流水、`DailySummary`) |
+| 合计 | 逐台 9 类目 | —— | **595** | —— |
+
+**列名绑定要求**:类目库存 double、时间写 `MM/DD/YYYY HH:MM:SS`,合成补充件时按 **float32 + ISO** 对齐;Access 列名不能含 `.`、`!`、`[`、`]`,建库时被消毒(非法字符换 `_`、截断 60、撞名加 `_1`),读侧必须用**同一套规则**把契约列名翻进翻出,否则会静默少列;行序按 `rid` **数值**排(库内是文本,直接合并会得到 `1,10,100…` 的字典序);类目库覆盖不到 `scd`、`std`、`sum` 三类台账级数据,台账面仍要按第 8 章单独收。
+
+#### 12.3.3 形态三:其它 OEM 导出
+
+**形态判据**:换主机制造商(OEM)或换机型后,现场给的往往不是上面两种:测点命名、类目切分、时间戳与时区、单位与量纲、台号写法都可能不同。
+
+**最小可用量**:能映射到 canonical 测点的列加明确的时间戳加明确的台号,逐台覆盖分析时间窗;状态码表、故障码表各一份;不要求与样本场同列名,但**要求可映射且映射可审计**。
+
+**列名绑定要求**:先建**别名表**(`configs/canonical/alias/<机型>_<场>.yaml`)把现场列名映射到 canonical 测点名,再在机型字段契约(`configs/contracts/<机型>_<场>.yaml`)里登记列名、单位与 `valid_range`;单位按变体表归一(温度的 `degC` 与 `C`、风速的 `m/s` 与 `mps`、频率的 `Hz` 与 `mHz` 都是同量纲变体),压力类 `bar`、`kPa`、`Pa`、`hPa` 四种并存,**必须逐条看词条**、不能按"压力就是 bar"推定;不许按位置索引;查不到单位的写"未取证",绝不凭常识补。术语与单位的中文写法可在 `configs/terms/display_map.yaml` 里按场覆盖。接新 OEM 与新数据形态的扩展点、以及换场会失效的假设,见《设计说明_观澜_2.10.1.docx》第 16 章。
+
+### 12.4 换场收资差异清单
+
+下面六类东西**换场必须重新收**,样本场的件不能沿用(表 12-5);它们也是第 12.5 节裁剪步骤里的必答项。
+
+**表 12-5 换场收资差异清单**
+
+| 差异项 | 换场必须重新收什么 | 为什么 |
+|---|---|---|
+| 机型参数表 | 机型代号与机型级常量:额定功率、叶轮直径、轮毂高度、传动比、切入切出与额定风速、生存温度 | 它们是振动的分母、功率曲线的基准、控制参数的参照;样本场的取值只属样本场机型 |
+| 测点清单 | 逐台类目与通道清单、契约列名、单位与 `valid_range` | 测点随 OEM 与机型变;列名绑定必须先有清单才能对齐,通道数不能照搬样本场 |
+| 故障代码表 | 场站故障代码表与中英文对译表、状态码表及说明 | 故障码是厂家私有命名空间,跨 OEM 不通用;样本场的条数与用码覆盖率不适用于他场 |
+| 坐标件 | 场站坐标件(现场交付件):逐台经度、纬度、海拔(含轮毂高度)、机型,另加升压站 | 邻域与尾流先验、扇区分析、台号与集电线路串的地理对照都以坐标为准,而坐标**不可自算** |
+| 控制与保护定值 | 整定值表(含 `Min`、`Max`、`Default`、`Current` 与单位)、偏航控制策略、降载荷控制策略 | 定值随机型与场址条件(气温、盐雾、电网条件)变;控制参数一致性件的"尺子"必须换成新场的定值 |
+| 阈值口径 | 温度档带宽判据、配对差阈值、界外转缺失的物理界、量化与哨兵族、空气密度与湍流度口径 | 阈值是场与机型相关的标定量;样本场的判据未经新场标定不得引用 |
+
+另有两条不属于"收资件"但换场会一并变的:台号与内部台号的对照表,以及台账里的场名写法(对应场配置的 `src_farm_names`)。
+
+### 12.5 按场裁剪步骤
+
+一页可执行,顺序不能颠倒。
+
+1. **先看**:拿到现场包先判形态(第 12.3 节的哪一种,或混用);数逐台件数与台数、看时间戳写法与时区、抽样看列名与单位;把"形态是什么、缺什么"记成一页备注。
+2. **先写场配置**:复制 `configs/farms/_模板.yaml.example` 为 `configs/farms/<场>.yaml`,填齐第 12.2 节的七个必填键,`raw_station` 写成 `data/raw/` 下的实际目录名。
+3. **按规则定档**:用第 12.1.1 节的五问逐条过 21 项,得到本场的 P0/P1/P2 与"不催"清单;P0 是阻断主线的,必须收齐,P1 是精度与互证的,P2 是专题与外部对照的。
+4. **按形态收最小可用量**:形态一收逐台 CSV(含 48 列公共子集),形态二收齐分析时间窗内逐月逐类目,形态三先收别名映射与单位口径。
+5. **收换场差异六项**:机型参数表、测点清单、故障代码表、坐标件、控制与保护定值、阈值口径(第 12.4 节);这六项的缺口写成"未取证",不猜、不沿用样本场。
+6. **放**:按"源类目录 → 再往下一层就是文件"落位(第 7 章表 7-1);先 `python scripts\raw_data_check.py --src <现场包>` 看计划,再 `python scripts\place_raw_data.py --src <现场包>` 落位,同名不同大小会被拦下等人确认。
+7. **验**:落完立刻跑 `python scripts\raw_data_check.py --deep` 看退出码(0 合规、5 结构或命名违例、6 增量冲突、7 缺源类或时间空洞);再 `python scripts\raw_scan.py --check`(rc=4 是有新数据,不是失败);最后 `python scripts\rebuild_all.py --auto` 重算并逐项核对锚点。
+8. **记**:把本场的实测件数、体量、时间覆盖、锚点与缺口写成一页并标注实测日期;换场前样本场的数字一律不搬。
+
+### 12.6 与需求分析、设计说明的对应关系
+
+本章与《需求分析_观澜_2.10.1.docx》第 11 章(多场适用性需求:场配置化、机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、按场裁剪收资)、与《设计说明_观澜_2.10.1.docx》第 16 章(场抽象层与配置 schema、场无关引擎与场相关参数分层、换场作业单与检查表)是同一件事的"要什么、怎么配、收什么"三面,字段与判定规则以本章第 12.1 与第 12.2 节为准。
+
 ## 附录 A 编写依据
 
 **表 A-1 章节与来源文件对照**
 
 | 章节 | 来源文件 |
 |---|---|
-| 第 1 章 | 本文自定口径;`configs/canonical/dictionary.yaml`、`configs/contracts/siemens_swt40_rudong.yaml` |
+| 第 1 章 | 本文自定口径;`configs/canonical/dictionary.yaml`、`configs/contracts/<机型>_<场>.yaml` |
 | 第 2 章 | `docs/输入数据放置指导_v0.1.md`(族、体量实测)、`docs/系统设计说明.md` §13(输入→功能→产物) |
 | 第 3 章 | `docs/数据目录结构与落位约定_v0.2.md`、`scripts/raw_scan.py`(族表与消费者口径)、`scripts/raw_data_check.py`(后缀约定) |
-| 第 4 章 | `docs/输入数据放置指导_v0.1.md`、`docs/数据目录结构与落位约定_v0.2.md`、`docs/振动数据接入_v0.1.md`、`docs/向振动线取料单_v0.1.md`、`configs/contracts/siemens_swt40_rudong.yaml` |
-| 第 5.1 节 | `docs/输入数据放置指导_v0.1.md` §4.1(595 通道构成)、`configs/contracts/siemens_swt40_rudong.yaml` `native_wps` 节、`reference/rudong/windscada_contract.yaml` |
+| 第 4 章 | `docs/输入数据放置指导_v0.1.md`、`docs/数据目录结构与落位约定_v0.2.md`、`docs/振动数据接入_v0.1.md`、`docs/向振动线取料单_v0.1.md`、`configs/contracts/<机型>_<场>.yaml` |
+| 第 5.1 节 | `docs/输入数据放置指导_v0.1.md` §4.1(595 通道构成)、`configs/contracts/<机型>_<场>.yaml` `native_wps` 节、`reference/<场>/windscada_contract.yaml` |
 | 第 5.2 节 | `scripts/scada_slim_build.py`(48 列公共子集与用途注释)、`src/windscada/data.py`(`load_10min`、`contracted_cols`) |
-| 第 5.3 节 | `src/windscada/subsys/temp_nbm.py`(通道、档带宽判据、季节解耦闸)、`configs/contracts/siemens_swt40_rudong.yaml` |
-| 第 5.4 节 | `docs/振动数据接入_v0.1.md`、`docs/振动六层链_接口规格与缺口_v0.1.md`、`reference/rudong/oem_scan_plan.json`、`outputs/rudong/m5_cms_tcm/windows/w0316/`(只读实测) |
-| 第 5.5 节 | `configs/canonical/dictionary.yaml`(WMET 族)、`configs/contracts/siemens_swt40_rudong.yaml` `materials.mast` |
-| 第 5.6 节 | `configs/contracts/siemens_swt40_rudong.yaml`、`reference/rudong/windscada_machine_facts.yaml`、`reference/rudong/gear_mesh_map_measured.json`、`reference/rudong/oem_bearing_freqs.json` |
-| 第 5.7 节 | `configs/contracts/siemens_swt40_rudong.yaml` `event_log` 节、`reference/rudong/alarm_code_cn.json` |
-| 第 5.8 节 | `src/windscada/perf/control.py`、`outputs/rudong/ontology/turbine_params.parquet`(只读实测) |
-| 第 6 章 | `configs/contracts/siemens_swt40_rudong.yaml`、`docs/输入数据放置指导_v0.1.md` §4.1、`src/windscada/subsys/temp_nbm.py`、`src/windscada/mdb_names.py` |
+| 第 5.3 节 | `src/windscada/subsys/temp_nbm.py`(通道、档带宽判据、季节解耦闸)、`configs/contracts/<机型>_<场>.yaml` |
+| 第 5.4 节 | `docs/振动数据接入_v0.1.md`、`docs/振动六层链_接口规格与缺口_v0.1.md`、`reference/<场>/oem_scan_plan.json`、`outputs/<场>/m5_cms_tcm/windows/w0316/`(只读实测) |
+| 第 5.5 节 | `configs/canonical/dictionary.yaml`(WMET 族)、`configs/contracts/<机型>_<场>.yaml` `materials.mast` |
+| 第 5.6 节 | `configs/contracts/<机型>_<场>.yaml`、`reference/<场>/windscada_machine_facts.yaml`、`reference/<场>/gear_mesh_map_measured.json`、`reference/<场>/oem_bearing_freqs.json` |
+| 第 5.7 节 | `configs/contracts/<机型>_<场>.yaml` `event_log` 节、`reference/<场>/alarm_code_cn.json` |
+| 第 5.8 节 | `src/windscada/perf/control.py`、`outputs/<场>/ontology/turbine_params.parquet`(只读实测) |
+| 第 6 章 | `configs/contracts/<机型>_<场>.yaml`、`docs/输入数据放置指导_v0.1.md` §4.1、`src/windscada/subsys/temp_nbm.py`、`src/windscada/mdb_names.py` |
 | 第 7 章 | `docs/输入数据放置指导_v0.1.md`(增量三判定、退出码、放置后动作)、`scripts/raw_scan.py`、`scripts/rebuild_all.py`、`docs/系统设计说明.md` §13.2 |
-| 第 8 章 | `F:\temp\_h9\req_v3_table.tsv`(收资要求 v3 原文)、`scripts/delivery_docs_figures.py` 的到位状态表、`configs/contracts/siemens_swt40_rudong.yaml` `materials` 节、`data/raw` 实测 |
+| 第 8 章 | 现场《数据分析收资要求-v3.docx》(收资要求 v3 原文)、`scripts/delivery_docs_figures.py` 的到位状态表、`configs/contracts/<机型>_<场>.yaml` `materials` 节、`data/raw` 样本场实测 2026-09-22 |
 | 第 9 章 | `docs/振动数据接入_v0.1.md` §7、`docs/振动六层链_接口规格与缺口_v0.1.md`、`docs/数据目录结构与落位约定_v0.2.md` §4、`docs/系统设计说明.md` §7 |
-| 第 10 章 | `data/raw/如东` 与 `outputs/rudong` 只读实测、`docs/输入数据放置指导_v0.1.md` §4 与 §6、`outputs/rudong/_raw_scan.json` |
-| 附录 B | `configs/canonical/dictionary.yaml` 的 `unit_majority` 与 `unit_variants`、`configs/contracts/siemens_swt40_rudong.yaml` 的 `unit` 字段 |
-| 附录 C | `F:\temp\_h9\req_v3_table.tsv`(逐字) |
+| 第 10 章 | `data/raw/<场站>` 与 `outputs/<场>` 只读实测、`docs/输入数据放置指导_v0.1.md` §4 与 §6、`outputs/<场>/_raw_scan.json` |
+| 第 11 章 | 第 4 章与第 8 章的口径汇总(最小可用量、必需测点、交付形式)、`docs/输入数据放置指导_v0.1.md` §4 |
+| 第 12 章 | `src/windscada/config.py`(`REQUIRED` 必填键与 `src_*` 派生)、`configs/farms/_模板.yaml.example`(场配置模板)、`configs/terms/display_map.yaml`(术语与单位显示映射)、`docs/输入数据放置指导_v0.1.md` §4.1(12 类目 ↔ 类目码 ↔ 595 通道)、`src/windscada/scada_source.py`(CSV 与 MDB 双形态取数)、`configs/canonical/alias/`(别名表) |
+| 附录 B | `configs/canonical/dictionary.yaml` 的 `unit_majority` 与 `unit_variants`、`configs/contracts/<机型>_<场>.yaml` 的 `unit` 字段 |
+| 附录 C | 现场《数据分析收资要求-v3.docx》(逐字) |
+
+**占位符与替换规则**
+
+- `<场站>`:现场在 `data/raw/` 下实际使用的场站目录名,必须与场配置的 `raw_station` 同名(或以 `src_farm_names` 对得上)。
+- `<场>`:场配置名(`configs/farms/<场>.yaml` 的文件名),也是 `outputs/<场>/` 与 `reference/<场>/` 的目录名;当前场可用环境变量 `WINDSCADA_FARM=<场>` 选定。
+- `<机型>`:机型代号(本样本为 4.0 MW 级海上机组,代号从略),出现在 `configs/contracts/<机型>_<场>.yaml`、`data/raw/<机型>技术资料/` 与按场命名的脚本名 `scripts/<场>_*.py` 里。
+- `<台号>`、`<NN>`、`<线路字母>`:场配置 `turbines` 展开后的机组号,以及台号与其集电线路串(`WTG01` 至 `WTG38` 是样本场内部编号,换场按实际台号替换)。
+- `<年>`、`<月>`、`<类>`、`<包名>`、`<部件>` 一类:现场件自带的年、月、类目码、导出包名与部件名,原样保留、不翻译。
+- 替换占位符之后**规则不变**:列名绑定、单位归一、必选与可选判定、落位判据、退出码语义与锚点口径都按本文正文执行。
 
 ## 附录 B 单位与符号表
 

+ 204 - 44
docs/src/设计说明_观澜_2.10.0.md → docs/src/设计说明_观澜_2.10.1.md

@@ -1,22 +1,22 @@
-# 设计说明 · 观澜·如东样板 v2 · 版本 2.10.0
+# 设计说明 · 观澜 v2 风电场智能分析系统 · 版本 2.10.1
 
 ## 1 文档说明
 
 ### 1.1 目的与范围
 
-本文是"观澜·如东样板 v2"(海上风电场智能分析离线系统)的设计说明,面向版本 2.10.0(2026-09-22),说明系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
+本文是"观澜 v2(风电场智能分析系统)"(海上风电场智能分析离线系统)的设计说明(下称"本系统"),面向版本 2.10.1;内容以样本风电场(下称"本场")为依据,样本场实测日期为 2026-09-22,说明本系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
 
-本文覆盖十四个设计面:总体架构与分层、目录结构与路径真源、数据接入与重算链、判级与算法、时间窗口径、服务与前端、本体与知识层、本机模型接入、运维控制台与重算编排、安装与服务化与版本管理、质量保证、安全与离线边界、可移植性与资源占用、已知边界与未实现。
+本文覆盖十五个设计面:总体架构与分层、目录结构与路径真源、数据接入与重算链、判级与算法、时间窗口径、服务与前端、本体与知识层、本机模型接入、运维控制台与重算编排、安装与服务化与版本管理、质量保证、安全与离线边界、可移植性与资源占用、多场适用性与换场迁移、已知边界与未实现。
 
-本文不重复需求条目本身(那是《需求分析_观澜_2.10.0.docx》的职责),也不重复操作步骤的逐步手册(那是 docs/重算操作手册_v0.1.md 与 docs/说明书_观澜如东样板v2_v0.2.md 的职责);本文只回答"设计上为什么这样、落在哪个文件的哪一处、用什么机器守卫保证它不漂移"。
+本文不重复需求条目本身(那是《需求分析_观澜_2.10.1.docx》的职责),也不重复操作步骤的逐步手册(那是 docs/重算操作手册_v0.1.md 与《使用说明书》v0.2(随包 docs/)的职责);本文只回答"设计上为什么这样、落在哪个文件的哪一处、用什么机器守卫保证它不漂移"。
 
 ### 1.2 读者与用法
 
-现场运维与检修人员可看第 3 章、第 7 章、第 8 章、第 11 章,了解自己能点到的页面背后读的是什么口径;场站管理人员可看第 6 章、第 7 章、第 16 章,了解判级与可靠性指标的口径与边界;研发与交付人员应通读全文,重点是第 4 章、第 5 章、第 12 章、第 13 章;验收方可直接按第 13 章的质量门与附录 A 的编写依据逐条复核。
+现场运维与检修人员可看第 3 章、第 7 章、第 8 章、第 11 章,了解自己能点到的页面背后读的是什么口径;场站管理人员与换场交付人员可看第 6 章、第 7 章、第 16 章、第 17 章,了解判级与可靠性指标的口径与边界,以及换场时要重新标定哪些参数;研发与交付人员应通读全文,重点是第 4 章、第 5 章、第 12 章、第 13 章;验收方可直接按第 13 章的质量门与附录 A 的编写依据逐条复核。
 
-### 1.3 与《需求分析_观澜_2.10.0.docx》的对应关系
+### 1.3 与《需求分析_观澜_2.10.1.docx》的对应关系
 
-需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示。
+需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示;与《数据要求说明_观澜_2.10.1.docx》的数据侧口径去向见 16.6 节。
 
 | 本文章节 | 需求分析对应章 | 对应关系说明 |
 |---|---|---|
@@ -34,15 +34,16 @@
 | 13 质量保证 | 8 验收门 | 需求给验收门,本文给审计器矩阵与退出码语义 |
 | 14 安全合规与离线边界 | 6 非功能需求(安全与离线) | 需求要无外网与脱敏,本文给边界与降级路径 |
 | 15 可移植性与资源占用 | 6 非功能需求(可移植) | 需求要零配置启动,本文给离线件与实测体积 |
-| 16 已知边界与未实现 | 8 验收门(遗留项) | 需求要求如实留白,本文逐条给影响与处置建议 |
+| 16 多场适用性与换场迁移 | 11 多场适用性与换场迁移 | 需求要按场配置化并可换场交付,本文给场抽象层、场无关与场相关分层、换场作业单与扩展点 |
+| 17 已知边界与未实现 | 8 验收门(遗留项) | 需求要求如实留白,本文逐条给影响与处置建议 |
 
 ### 1.4 口径与依据
 
-本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 16 章汇总。版本号的唯一真源是 src/version.py 的 VERSION 常量,本版为 2.10.0;打包文件名由 src/version.py 的 package_name() 给出,为 app_guanlang_v2.10.0.zip。
+本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 17 章汇总。本文中"本次实测"与各表"实测"列一律指样本场实测(2026-09-22):即在样本风电场的一套实例上的一次实跑,换场后这些数字会变、方法与口径不变。版本号的唯一真源是 src/version.py 的 VERSION 常量,本版为 2.10.1;打包文件名由 src/version.py 的 package_name() 给出,为 app_guanlang_v2.10.1.zip。
 
-本文遵循三条写作口径:含"窗"且确实指时间窗口的,一律写全"时间窗"(天气窗、作业窗、预览窗、观测窗属领域词,保持原样);影响的是风电机组时写"影响机组"或"影响机组数";使用英文简写时必须写成"中文(英文简写)"形式,如平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT)。
+本文遵循四条写作口径:含"窗"且确实指时间窗口的,一律写全"时间窗"(天气窗、作业窗、预览窗、观测窗属领域词,保持原样);影响的是风电机组时写"影响机组"或"影响机组数";使用英文简写时必须写成"中文(英文简写)"形式,如平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);全文用简体中文与半角数字与单位。
 
-本文用到的取证命令与本次输出要点如表 1-2 所示,全部为秒级只读或自检命令(重算与构建类耗时命令未执行)。
+本文用到的取证命令与样本场实测(2026-09-22)的输出要点如表 1-2 所示,全部为秒级只读或自检命令(重算与构建类耗时命令未执行)。
 
 | 命令 | 用途 | 本次输出要点 |
 |---|---|---|
@@ -155,12 +156,12 @@
 
 ### 3.4 产物仓的生成端与消费端
 
-九个产物仓的生成端与消费端如表 3-3 所示(依据 docs/系统设计说明.md 的产物全景块与本次盘上实测件数)。本次实测 outputs/rudong 合计 3,549 件、7,157.5 MB。
+九个产物仓的生成端与消费端如表 3-3 所示(依据 docs/系统设计说明.md 的产物全景块与本次盘上实测件数)。本次实测 outputs/<场> 合计 3,549 件、7,157.5 MB。
 
 | 产物仓 | 件数(实测) | 体积(实测) | 生成端 | 主要消费端 |
 |---|---|---|---|---|
 | windscada | 57 | 569.7 MB | rebuild_from_raw.py、10 个 SCADA 构建器、windscada_monthly_build.py、scada_slim_build.py | 工作台各视图、taxonomy、subsys/fusion |
-| m5_cms_tcm | 3,417 | 6,555.0 MB | rudong_tcm_index.py、rudong_tcm_spectra.py、vib_raw_build.py、rudong_model_run.py、rudong_fusion_run.py、rudong_fusion_handoff.py、component_history_build.py、baseline_38_build.py | subsys/fusion.py、windcms/data.py、报告构建器 |
+| m5_cms_tcm | 3,417 | 6,555.0 MB | scripts/<场>_tcm_index.py、scripts/<场>_tcm_spectra.py、vib_raw_build.py、scripts/<场>_model_run.py、scripts/<场>_fusion_run.py、scripts/<场>_fusion_handoff.py、component_history_build.py、baseline_38_build.py | subsys/fusion.py、windcms/data.py、报告构建器 |
 | ontology | 9 | 15.2 MB | src.ontology.kb_ingest 至 maintenance.refresh_params 六步 | 本体页、问答、事实契约 |
 | windcms | 49 | 15.2 MB | scripts/windcms.py report 与 kb、vib_reports_build.py | CMS 自服务 18020、taxonomy 转录列 |
 | pitch | 3 | 0.4 MB | scripts/pitch_face_build.py | 变桨面判级、零位卡片 |
@@ -182,7 +183,7 @@ src/paths.py 是全部路径的唯一真源。硬约束有三条:代码与配
 | 助手 | 返回 | 用途 |
 |---|---|---|
 | P.ROOT | 安装根 | 一切解析的基准(WINDSCADA_ROOT 可覆盖) |
-| P.RAW_ROOT | data/raw | 现场原始件根(WINDSCADA_RUDONG_SRC 或 serve.json 的 raw_dir 可覆盖) |
+| P.RAW_ROOT | data/raw | 现场原始件根(可用环境变量 `WINDSCADA_<代号>_SRC` 或 serve.json 的 raw_dir 覆盖;该变量名里**写死了样本场代号**,换场须一并改名 —— 见 16.5 节) |
 | P.station_dir(name) | data/raw/场站名称 | 兜底约定位置,权威值来自场配置的扫描辨识 |
 | P.out_root(name) | outputs/场站 | 产物仓根 |
 | P.store(name) | outputs/场站/windscada | L0 标准仓,页面主取数处 |
@@ -210,10 +211,10 @@ src/paths.py 是全部路径的唯一真源。硬约束有三条:代码与配
 | src | 281 | 6.4 MB | 分析代码(windscada、windcms、ontology、sop)与通用模块(paths、version、proc、logfile、entry_refs) |
 | scripts | 223 | 13.9 MB | 服务与工具脚本、构建器、审计器、打包器 |
 | configs | 171 件配置 | 见下 | 配置:serve.json、models.json、portal_pages.yaml、registry.yaml 与四个域目录 |
-| data | 26,504 | 224.67 GB | 原始件(data/raw/如东 与 data/raw/西门子4.0技术资料),只读输入 |
+| data | 26,504 | 224.67 GB | 原始件(data/raw/<场站> 与 data/raw/<机型>技术资料),只读输入 |
 | outputs | 3,549 | 7,157.5 MB | 产物仓,只由构建器写 |
-| release | 2,898 | 708.4 MB | 门户 portal.html、仿真服务与资料包、三维 viewer 资产、如东治理清单交付件 |
-| resources | 30 | 5.2 MB | 仿真回放资产(resources/oem_envision_sc1_rudong2014) |
+| release | 2,898 | 708.4 MB | 门户 portal.html、仿真服务与资料包、三维 viewer 资产、治理清单交付件 |
+| resources | 30 | 5.2 MB | 仿真回放资产(resources/oem_<OEM>_sc1_<场>2014) |
 | wheels | 42 | 169.4 MB | 离线依赖轮子(wheels/win_amd64) |
 | vendor | 4 | 81.4 MB | 三平台便携 Python 运行时(3.12.14)与 vendor/MANIFEST.json |
 | docs | 32 | 2.6 MB | 交付文档与设计正本(含本文) |
@@ -246,7 +247,7 @@ src/paths.py 是全部路径的唯一真源。硬约束有三条:代码与配
 
 | 位置 | 原样 | 改成 | 为什么要改 |
 |---|---|---|---|
-| scripts/products_restore_missing.py | 判断不存在的常量后落到写死的 rudong | P.out_root() | 多场部署会把如东的随包件补进别的场(静默串场) |
+| scripts/products_restore_missing.py | 判断不存在的常量后落到写死的样本场名 | P.out_root() | 多场部署会把本场的随包件补进别的场(静默串场) |
 | src/ontology/maintenance.py | 自写安装根回溯与自己的显示规则 | P.ROOT 与 P.RAW_ROOT 与 P.disp | 同一文件里出现三处影子真源,显示规则两个实现 |
 | src/sop/wrapup.py | 硬编码 cleaned/turbine.parquet | 读 clean_gate.json 的 section,取不到则目录内唯一 parquet,都没有则响亮报错 | 写侧落的是 cleaned/节名.parquet,读侧硬编码时静默取空并悄悄降级 |
 | src/windscada/subsys/pitch.py | P.store() 跟环境变量 | 配置透传的 store 键 | 多场部署下跨场串数据且不报错 |
@@ -258,19 +259,19 @@ src/paths.py 是全部路径的唯一真源。硬约束有三条:代码与配
 
 ### 5.1 输入数据族与落位约定
 
-输入层是 data/raw/场站名称/ 下的约定目录,多套一层包名目录等于没放(摄入脚本按"源类目录再往下一层就是文件"读)。本次实测各族的件数与体积如表 5-1 所示(data/raw/如东 合计 26,504 件、224.67 GB;guanlan.py check 报 26,503 件源件,差额 1 件是目录内的放置说明文件 README_把原始数据放这里.txt)。
+输入层是 data/raw/场站名称/ 下的约定目录,多套一层包名目录等于没放(摄入脚本按"源类目录再往下一层就是文件"读)。本次实测各族的件数与体积如表 5-1 所示(data/raw/<场站> 合计 26,504 件、224.67 GB;guanlan.py check 报 26,503 件源件,差额 1 件是目录内的放置说明文件 README_把原始数据放这里.txt)。
 
 | 源类 | 件数(实测) | 体积(实测) | 结构要求 | 消费该族的重算步 |
 |---|---|---|---|---|
-| scada_10min | 114 | 15.60 GB | 平铺,每台一个主件(WTG01.csv 等),可含同台补充件 | ③ 与 ③b 与 ④ 与 ④c |
-| scada_1min | 38 | 12.77 GB | 平铺,名字是内部台号 | ④c |
+| scada_10min | 114 | 15.60 GB | 平铺,每台一个主件(台号形态如 WTG01.csv 等;均为样本场内部编号,换场按实际台号替换),可含同台补充件 | ③ 与 ③b 与 ④ 与 ④c |
+| scada_1min | 38 | 12.77 GB | 平铺,名字是内部台号(样本场形如 01E…;均为样本场内部编号,换场按实际台号替换) | ④c |
 | scada_mdb | 100 | 44.93 GB | 按年与月分目录的月度通道组库 | 上游归档(转换后才被消费) |
 | windcms | 25,693 | 150.15 GB | 含解码导出 json,可套包名与 measurement 层 | ④b 与 ④d 与 ④e |
 | 故障报警 | 16 | 0.02 GB | 年度或季度 xls 与 XML 导出 | ② |
 | 风机故障记录 | 137 | 0.14 GB | 年目录加月度汇总表,另含压缩包与现场照片 | ② |
 | 油样报告 | 404 | 0.16 GB | 两级:台号目录下部件目录内的 pdf | ② |
 | m5_cms_tcm | 1 | 0.02 GB | handoff 正本与厂家报告 | ④b 与融合面 |
-| 西门子4.0技术资料(与场站并列) | 318 | 2,800.7 MB | 技术资料与四个台账 | ⑦ 本体六步 |
+| 主机技术资料(4.0 MW 级海上机组;主机制造商(OEM,名称从略)与机型代号从略;与场站并列) | 318 | 2,800.7 MB | 技术资料与四个台账 | ⑦ 本体六步 |
 
 ### 5.2 重算链逐步说明
 
@@ -349,7 +350,7 @@ src/paths.py 是全部路径的唯一真源。硬约束有三条:代码与配
 
 按所选时间窗重算的真实卡点,是每换一个时间窗就要重读 15 GB 级的 10min CSV(实测约 2 分钟每面)。而各面真正需要的列只是公共子集。scripts/scada_slim_build.py 把这份公共列子集抽成逐台一份的窄仓,一次全量扫后,任何时间窗的重算都退化为"窄仓过滤加分组"的秒级操作。
 
-窄仓的实测口径是 48 列(23 条核心列加 25 条温度 NBM 的均值列),分七个分组:功率、风况、转速、变桨、温度 NBM、偏航、润滑液压;不裁剪行(时间范围等于该台原始件全量),缺列如实记进 columns_missing 而不造 0。本次实测 outputs/rudong/windscada/slim10min 下 39 件(38 台加一份 _manifest.json),_manifest.json 记 columns_n 为 48,构建行合计 3,300,822 行,无缺列。窄仓是 v2.9.0 补入 grd_wtc_ActPower_max 与 tur_wtc_GenRpm_max 两列之后的口径,因此文档中旧的"46 列、温度 27 列"写法已过期,实测为 48 列。
+窄仓的实测口径是 48 列(23 条核心列加 25 条温度 NBM 的均值列),分七个分组:功率、风况、转速、变桨、温度 NBM、偏航、润滑液压;不裁剪行(时间范围等于该台原始件全量),缺列如实记进 columns_missing 而不造 0。本次实测 outputs/<场>/windscada/slim10min 下 39 件(38 台加一份 _manifest.json),_manifest.json 记 columns_n 为 48,构建行合计 3,300,822 行,无缺列。窄仓是 v2.9.0 补入 grd_wtc_ActPower_max 与 tur_wtc_GenRpm_max 两列之后的口径,因此文档中旧的"46 列、温度 27 列"写法已过期,实测为 48 列。
 
 ### 5.7 scada_mdb 类目库是上游归档
 
@@ -415,7 +416,7 @@ M9 按所选时间窗重算(control.registry 接受 span 参数),产物是
 
 ### 6.5 可靠性指标
 
-部件可靠性表的口径在 src/windscada/perf/reliability.py 与 src/windscada/perf/faults.py。停机段门是"连续 10min 行不少于 6 行"(即不少于 1 小时),段结束时间是末行加 10 分钟。归因有两级:T1 紧归因时间窗取段起点前后 30 分钟内的首发故障码,T2 宽归因时间窗取前 6 小时内的最近一条;实测紧归因时间窗未命中的段里有 53% 在段前 6 小时内能找到报警,中位段长 2.5 小时、最长 174 小时。调度令 1007 与 1008 判为远程停机(业主与西门子),不计入设备责任。
+部件可靠性表的口径在 src/windscada/perf/reliability.py 与 src/windscada/perf/faults.py。停机段门是"连续 10min 行不少于 6 行"(即不少于 1 小时),段结束时间是末行加 10 分钟。归因有两级:T1 紧归因时间窗取段起点前后 30 分钟内的首发故障码,T2 宽归因时间窗取前 6 小时内的最近一条;实测紧归因时间窗未命中的段里有 53% 在段前 6 小时内能找到报警,中位段长 2.5 小时、最长 174 小时。调度令 1007 与 1008 判为远程停机(业主单位(从略)与主机制造商(OEM,名称从略)),不计入设备责任。
 
 三项指标的口径如表 6-3 所示。外部类(运维操作、风况(外部)、电网或供电(外部))单列统计并明确"外部类不计设备可靠性"。
 
@@ -425,7 +426,7 @@ M9 按所选时间窗重算(control.registry 接受 span 参数),产物是
 | 平均停机间隔(MTBO) | 在上一项基础上再扣除调度令停机后除以事件数 | 停机归因模块的口径,台时已改为实测台时 |
 | 单次停机时长(MDT) | 停机段时长的均值;就地与切入边界类码按口径剔除 | 两个模块同口径 |
 
-台时口径的实测对比很重要:2026 年 1 至 7 月理想日历台时为 194,050,实测台时为 165,475(差 17%),原因是尾月只有 6 天数据;若沿用理想日历,平均停机间隔会虚高 18%(239 小时实为 203 小时)。因此 faults.py 已改为用实测台时(由 loss_monthly 的行数除以每小时 6 个节拍得到),并在不可得时才回退理想日历。需要如实指出:reliability.py 的部件可靠性表仍使用理想日历台时,两处口径目前不一致,属待统一项(见第 16 章)。
+台时口径的实测对比很重要:2026 年 1 至 7 月理想日历台时为 194,050,实测台时为 165,475(差 17%),原因是尾月只有 6 天数据;若沿用理想日历,平均停机间隔会虚高 18%(239 小时实为 203 小时)。因此 faults.py 已改为用实测台时(由 loss_monthly 的行数除以每小时 6 个节拍得到),并在不可得时才回退理想日历。需要如实指出:reliability.py 的部件可靠性表仍使用理想日历台时,两处口径目前不一致,属待统一项(见第 17 章)。
 
 ### 6.6 融合面四源与逐台判级
 
@@ -586,14 +587,14 @@ v2 页面还有一条硬要求:可展开的「查看完整依据」不得为
 
 | 顺序 | 命令 | 输入 | 输出 |
 |---|---|---|---|
-| 1 | python -m src.ontology.kb_ingest | 西门子 4.0 技术资料、四个台账、码表与手册 | 码表、作业指导、故障树、预防性任务等对象 |
+| 1 | python -m src.ontology.kb_ingest | 4.0 MW 级海上机组的技术资料、四个台账、码表与手册 | 码表、作业指导、故障树、预防性任务等对象 |
 | 2 | python -m src.ontology.populate | 上述全部产物与判级矩阵 | 铺开判级与台账对象(含证据与判级对象) |
 | 3 | python -m src.ontology.chain_ingest | 运行中的链盘(/api/fleet) | 逐台一个决策对象,记六步状态与卡点与下一步 |
 | 4 | python -m src.ontology.trend_ingest | component_history.json 与链盘 | 在升与闭环趋势证据(挂到机组与部件) |
 | 5 | python -m src.ontology.retrieval 的 build(use_vec=False) | 对象库 | retrieval_index.json 检索索引 |
 | 6 | src.ontology.maintenance 的 refresh_params | 技术资料与契约 | turbine_params.parquet 实机参数表 |
 
-对象库实测规模如表 9-2 所示(outputs/rudong/ontology/objects.json,4,452,520 字节,对象结构为 id、type、props、links 四个字段)。
+对象库实测规模如表 9-2 所示(outputs/<场>/ontology/objects.json,4,452,520 字节,对象结构为 id、type、props、links 四个字段)。
 
 | 对象类型前缀 | 对象数(实测) | 说明 |
 |---|---|---|
@@ -780,11 +781,11 @@ Windows 安装入口是 install.bat 与 install.ps1,步骤号写死在输出
 | Linux | systemd 单元 guanlan.service(简单类型加始终重启) | scripts/service_main.py(前台守护) | scripts/service_ctl.py install(写单元文件并启用) | root |
 | Windows 备选 | 当前用户登录自启(不需要管理员) | scripts/guanlan_start_hidden.py(无窗口启动器) | scripts/service_ctl.py install --user-login | 无 |
 
-服务名与显示名是固定值(guanlan 与"观澜·如东样板 v2"),服务失败重启策略是 24 小时复位、重启延迟 60 秒。本机实测(appraisal):service_ctl.py status 报"已注册=False 运行中=False",即本机没有注册服务(本机以手工方式在跑,六个端口处于运行中);Windows 服务的真注册与 systemd 的真装在本次环境未取证(非管理员环境只有 --dry-run 与免权限守护自证两条路径,其输出与设计说明一致),属如实留白项。
+服务名与显示名是固定值(guanlan 与"观澜 v2(风电场智能分析系统)"),服务失败重启策略是 24 小时复位、重启延迟 60 秒。本机实测(appraisal):service_ctl.py status 报"已注册=False 运行中=False",即本机没有注册服务(本机以手工方式在跑,六个端口处于运行中);Windows 服务的真注册与 systemd 的真装在本次环境未取证(非管理员环境只有 --dry-run 与免权限守护自证两条路径,其输出与设计说明一致),属如实留白项。
 
 ### 12.3 安装记录与版本三守卫
 
-安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.10.0,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
+安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.10.1,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
 
 | 字段 | 含义 |
 |---|---|
@@ -947,7 +948,7 @@ Windows 安装入口是 install.bat 与 install.ps1,步骤号写死在输出
 | 5 重算 | guanlan.py serve 后从门户「数据重算」触发,或直接跑 rebuild_all.py | 同上 |
 | 6 启动 | 双击"启动观澜"快捷方式(无窗口)或 start.bat | .venv/bin/python guanlan.py serve |
 
-安装根可用环境变量覆盖(路径模块优先读 WINDSCADA_ROOT),原始件根可用 WINDSCADA_RUDONG_SRC 覆盖,场名可用 WINDSCADA_FARM 覆盖;配置里的路径一律写相对安装根的形式,因此换盘、换机不需要改任何路径。
+安装根可用环境变量覆盖(路径模块优先读 WINDSCADA_ROOT),原始件根可用环境变量 `WINDSCADA_<代号>_SRC`(**变量名内写死样本场代号**,换场须改名,见 16.5 节)覆盖,场名可用 `WINDSCADA_FARM=<场>` 覆盖;配置里的路径一律写相对安装根的形式,因此换盘、换机不需要改任何路径。
 
 ### 15.2 Python 与依赖 wheel
 
@@ -966,7 +967,7 @@ Linux 与 macOS 侧包内没有轮子时,安装脚本自动改为联网安装
 
 | 项 | 数值 | 出处 |
 |---|---|---|
-| 内存最低或推荐 | 16 GB 或 32 GB | docs/说明书_观澜如东样板v2_v0.2.md |
+| 内存最低或推荐 | 16 GB 或 32 GB | 《使用说明书》v0.2(随包 docs/) |
 | 磁盘最低 | 5 GB 加三维资产 1 GB | 同上 |
 | 模型占用(可选) | 30 至 60 GB(固态盘) | 同上 |
 | 显存(只影响问答与本地审核) | 12 GB 及以上可跑默认档;24 GB 及以上可开升档;无显卡走纯 CPU 慢档 | 同上 |
@@ -1000,11 +1001,160 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 
 ***
 
-## 16 已知边界与未实现
+## 16 多场适用性与换场迁移
 
-### 16.1 逐条边界与处置建议
+### 16.1 场抽象层
 
-本系统坚持"缺件如实"。边界、影响与处置建议如表 16-1 所示,每条的处置建议都是可执行的一条命令或一次催缴动作。
+本系统的"场"不是散落在代码里的常量,而是一个可加载、可校验、可切换的配置对象。场抽象层分三层落地:配置层是场定义文件 configs/farms/<场>.yaml;路径层是 src/paths.py 的 farm() 与 farm_config() 与 contract();语义层是 src/windscada/config.py 的场定义加载与校验、场站目录扫描辨识与场切换。三层的关系是:配置层给"这个场长什么样",路径层给"这个场的件写到哪、从哪读",语义层给"凭什么认定 data/raw 下的这个目录就是本场"。本章数字均为样本场实测(2026-09-22)。
+
+#### 16.1.1 场定义的 schema
+
+场定义是一个 YAML 文件(历史 JSON 仍可加载,新场一律写 YAML),落在 configs/farms/ 下,文件名去掉后缀即场名。加载器要求七个必填键齐全,缺任何一个都在加载时响亮报错并以非零退出终止,而不是等到分析中途才崩。字段、必填性与模板写法如表 16-1 所示。
+
+| 键 | 必填性 | 含义 | 模板写法 |
+|---|---|---|---|
+| name | 必填 | 场站中文全名,用于页面与服务显示 | `<场站中文名>` |
+| n_turbines | 必填 | 机组台数 | `25` |
+| turbines | 必填 | 机组号列表,或生成式字符串(加载时展开) | `WTG{:02d}:1-25` 或 `["WTG01", "WTG02"]` |
+| src_10min | 必填 | 10 分钟 SCADA 原始件目录 | `data/raw/<场站>/scada_10min` |
+| src_alarm | 必填 | 故障报警原始件目录 | `data/raw/<场站>/故障报警` |
+| store | 必填 | L0 标准仓落点(页面主取数处) | `outputs/<场>/windscada` |
+| rated_kw | 必填 | 单机额定功率,单位 kW | `3000` |
+| raw_station | 建议 | data/raw 下的首选目录名(辨识第一顺位) | `<data/raw 下的目录名>` |
+| src_farm_names | 选填 | 原始件里的场站名写法(别名),用于台账与报警筛本场行 | `[<别名1>, <别名2>]` |
+| contract | 选填 | 机型判据契约落点 | `reference/<场>/windscada_contract.yaml` |
+| src_1min 与 src_workorder 与 src_oil 与 src_windcms 与 src_m5 与 src_mdb | 选填 | 其余源类目录 | `data/raw/<场站>/<源类目录>` |
+
+三条与 schema 配套的加载规则:一是 turbines 的生成式写法是"前缀加占位加起止",加载时展开成列表,n_turbines 不给时按列表长度补齐;二是 src 开头的键与 store 若显式给出即以其为准,留空才由 data/raw 的扫描结果派生——扫描只填空白,不覆盖声明;三是场定义里的相对路径一律按安装根解析成绝对路径,因此安装目录整体拷到别的盘不需要改场定义。
+
+模板文件是 configs/farms/_模板.yaml.example,以底线开头的文件不被当成场。同一个目录里还混着另一类文件:机型或场站的物理约束档(顶层是 meta 加 physical_constraints),schema 与场定义不同,不是场定义;加载到这类文件时加载器会明确报出"该文件存在但不是场定义",并由 foreign_farm_files() 登记在册、由配置审计记账。
+
+#### 16.1.2 场名解析、运行期切换与落点规则
+
+当前场名的解析顺序固定为三条:显式传入的场名,然后环境变量 WINDSCADA_FARM=<场>,最后是内置默认场名。因此同一份代码换一个场名,就能把 outputs 与 reference 两侧的读写整体迁到新场目录,调用点一行不改。
+
+- farm(name):取场配置对象;不带参数时用当前场;结果进进程内缓存,refresh=True 可强制重扫场站目录(放了新数据又不想重启服务时用)。
+- set_current(name):先试着加载该场,加载不通过就报错并列出可用场;通过则把它设为进程内当前场。current() 读回当前场名。
+- available():可用场清单,内置场标"内置",其余来自能加载的场定义文件。
+- P.farm(name):路径层的场名真源,返回场名本身(解析顺序同上);所有按场分目录的落点都以它为入参。
+- P.store(name):返回 outputs/<场>/windscada,即 L0 标准仓;场定义里显式给了 store 时以场定义为准。
+- P.contract(name):返回 reference/<场>/windscada_contract.yaml,即机型判据契约;场定义里显式给了 contract 时以场定义为准。
+
+路径层的三个落点助手 P.farm() 与 P.store() 与 P.contract() 都以"场名"为唯一入参;场名解析函数 farm() 与运行期切换函数 set_current() 与可用场函数 available() 三者共同构成场抽象层对外的一小组接口。
+
+落点规则可以概括成一句:场名决定 outputs/<场>/ 与 reference/<场>/ 两条树,场定义决定原始件根 data/raw/<场站>/;其余固定位置(configs、release、resources、docs、logs、run)不含场名,换场不动。
+
+#### 16.1.3 raw_station 与 src_farm_names 的别名匹配
+
+原始件目录名与场定义里的场站名常常对不上:现场交付的原始件根目录可能写的是场站的另一个名字,而集团口径的月度与年度台账里用的又是另一个写法。加载器因此用四段式辨识,并把"凭什么认出这个场站目录"写进结论供页面与命令行显示:
+
+- raw_station:data/raw 下一级目录名与场定义的 raw_station 完全相同(第一顺位,最可靠)。
+- alias:目录名与 src_farm_names 里的任一别名互为子串(原始件里的场站名写法)。
+- single:data/raw 下只有一个场站目录(单站部署),直接采用它。
+- none:data/raw 下有多个目录但一个都对不上——不猜,页面显示无数据,并写明本场的 raw_station 与别名,让人把目录名或场定义改对。
+
+辨识结论落在场配置的 raw_station_dir 与 station_how 与 station_note 三个字段上;raw_station_dir() 取到辨识结果就用它,辨识不到则回落到约定位置 data/raw/<raw_station>,这样维护页仍能指着正确的地方让人补数据,而不是给一个空路径。实测(样本场实测(2026-09-22)):本场的月度汇总台账里混着十来个其它场站的行,必须靠 src_farm_names 别名把本场行筛出来,否则报警与工单台账会串进别的场的记录。
+
+#### 16.1.4 is_farm_def() 与 foreign_farm_files() 的守门作用
+
+- farm_files():列出 configs/farms/ 下的候选文件(YAML 与 YML 与 JSON),跳过以底线开头的模板。
+- is_farm_def(f):这个文件能不能加载成一份场定义(七个必填键齐全);解析失败也算不是。
+- available():只列"真能加载"的场,因此把另一种 schema 的文件列进可用场这种事被挡住。
+- foreign_farm_files():列出目录下"不是场定义"的文件及其依据(顶层键名或解析失败类型),登记在册但不参与场加载。
+
+这几个函数合起来解决一个真实坑:配置目录里放着另一种 schema 的文件时,若"可用场"只按文件名后缀列,使用方在切换场时就会撞上加载报错。现在的口径是"能加载的才叫场,不能加载的登记为 profile 并如实报出来",由配置审计的场目录规则(每件要么是场定义要么登记为 profile)与加载报错信息两处守住。实测(样本场实测(2026-09-22)):配置审计结论为 171 件配置、5 个域、1 个场定义、10 件机型 profile、0 不一致、14 已知缺口。
+
+### 16.2 场无关与场相关的分层
+
+换场之所以可能,是因为系统把"怎么算"和"按什么算"分开了:判级与算法的结构、服务与页面、审计门属引擎侧,换场不动;阈值、机型参数、时间窗锚点、机组清单、术语库与代码表属参数侧,换场必须重新给或重新标定。分层如表 16-2 所示。
+
+| 设计面 | 场无关(引擎侧,落在代码里,换场不改) | 场相关(参数侧,换场必给或必标定) |
+|---|---|---|
+| 判级矩阵结构 | 五态词表与取严权重、四键取严(报警与良好与优秀与不可判)、系统到问题的归口、未收录不判正常 | 参与判级的系统清单与通道到系统的映射、系统关键词映射表、各面的阈值 |
+| 曲线七镜头 | 七镜头定义(风速与功率、功率与桨距、功率与发电机转速、转矩与转速、风速与风轮转速、功率与功率系数、转速比)、分箱结构、三条物理硬闸、列活性铁律 | 机型常数(风轮半径、扫风面积、铭牌齿比、贝兹极限)、分箱边界与步长、八项显著门与离群门限 |
+| 温度同工况比档 | 同一功率档内比同族温度的算法、稳健标准差与稳健 z 的公式、样本门结构、返服嫌疑与样本不足的判词 | 功率档宽、台级样本门、三级偏差阈值与 z 门限、温度通道清单与均值列集合 |
+| 蓄能与热链 | 按油温档分箱算带宽比的算法、档内样本门结构、轴数与判定链 | 油温档宽、档内样本门、带宽比门限、轴数门、高油温档位与占空样本门 |
+| M9 控制参数一致性 | 四个判据的结构(双封顶、K 聚类、桨距调度、尺子) | 分组容差、中载区区间、离群倍数与稳健标准差下限、满发段门 |
+| 可靠性口径 | 停机段门与段结束规则、两级归因的结构、外部类不计设备可靠性、三项指标公式 | 调度令码表(哪些码算远程停机)、归因时间窗长度、台时口径(理想日历或实测) |
+| 融合四源 | 四源集合与关闭类最先判、禁止按源数加权、claim 时间窗对齐纪律、取严合并 | 各源的 claim 时间窗锚点、系统关键词映射、handoff 正本与厂家报告转录的件名 |
+| 本体与检索 | 六步摄入结构与对象 schema、只读工具与接地闸、只读与模型零写权的守门 | 术语库、场站故障代码表(样本场实测 2,793 条)、作业指导与中间件正本、机型技术资料 |
+| 服务与前端 | 路由族、pending 契约、缓存与产物指纹策略、网关前缀改写、缺件如实页 | 端口与监听地址配置、页面上的场名与台数展示、逐台页与单问题页的台号 |
+| 审计门与质量门 | 审计器矩阵与退出码语义、入口引用闭合、编码守则、质量门闭环节奏 | 场定义与机型 profile 的登记、页面归口登记、按场裁剪的收资项 |
+| 数据落位与摄入 | 源类目录名的接口约定(八个约定子目录)、形态接纳规则(CSV 优先、该台没有 CSV 才回落归档库)、逐族指纹与增量三清单 | 机组清单与台号形态、逐台主件命名、场站坐标件(现场交付件)、原始件里的场站名写法 |
+| 时间窗与口径 | 时间窗词表与含两端的实现、缓存键结构、残月识别公式 | 时间窗锚点(各源数据末端)、限电前干净时间窗与对照时间窗常量、判级时间窗口径常量 |
+
+分层的判据是可机器核对的:引擎侧的东西没有任何一处读场定义里的阈值或机组数,参数侧的东西没有任何一处写死样本场的取值(个别写死的样本场代号属遗留,见 16.5 节)。因此换场的改动面被压到"补一份场定义加重新标定阈值加重新摄入语义件"三件事。
+
+### 16.3 换场作业单
+
+#### 16.3.1 逐步作业单
+
+每一步都给命令与退出码语义;退出码 0 才算过,落在容忍集合内的非零按"如实报出"处理(容忍集合见 5.2 与 5.3 节)。命令一律在安装根下执行。
+
+1. 准备场配置。把 configs/farms/_模板.yaml.example 复制成 configs/farms/<场>.yaml,填齐七个必填键(name、n_turbines、turbines、src_10min、src_alarm、store、rated_kw),并给出 raw_station 与 src_farm_names。校验命令是 `python scripts/config_audit.py`,退出码 0 通过、9 表示场配置不合约定(例如把不是场定义的文件当成场,或场定义缺键)。
+2. 放数据。按八个约定子目录落位,命令是 `python scripts/place_raw_data.py --src <现场包目录> --scope full`,退出码 0 为落位成功;同名不同大小默认拒绝覆盖并列出冲突,必须人确认后加 --force 才覆盖。
+3. 体检。两条命令:`python scripts/raw_scan.py --check --write` 取逐族指纹快照,退出码 0 无变化、4 有新增或变化、5 还没有基线(首次顺手记一份,不是失败);`python scripts/raw_data_check.py` 做放置体检,退出码 0 合规、5 结构或命名违例、6 增量冲突、7 缺源类或时间空洞、8 仅提示。
+4. 重算。先 `python scripts/rebuild_all.py --dry-run` 看本次计划的步数(默认 26 步,加 --skip-scada 为 23 步),再 `python scripts/rebuild_all.py` 真跑;退出码 0 通过,1 表示有模块未起(属降级正常态),落在该步容忍集合内的非零(2 与 4 与 5 与 6 与 7)视为通过并如实记入日志,容忍集之外的非零立即中断整条链。要做等价验收时用 `python scripts/rebuild_all.py --with-verify`;随包基线缺失时该步返回 5,链上会写明"这不是通过"。
+5. 校准阈值。阈值不在引擎里,在机型判据契约与场定义里:契约落 reference/<场>/windscada_contract.yaml(对外交付件的机型契约落 configs/contracts/<机型>_<场>.yaml),场相关阈值与机组清单落场定义。改完复跑 `python scripts/products_reverse_audit.py --check`(退出码 0 全部成立、5 有未归类或判据失败)与 `python scripts/pages_audit.py --check`(0 一致、5 登记缺项或文件缺失、6 溯源缺失、7 数据派生页面陈旧、8 交付件缺版本号、9 分类错误)。
+6. 核对锚点。锚点指"数据末端"与"干净与对照时间窗"两件。命令 `python scripts/inventory_products.py` 校验产物跨度必须落在输入跨度内(退出码 0 呼应正常、5 有 problems);`python scripts/detail_deps.py` 校验页面到接口到产物到来源的依赖台账(0 通过、5 文档与现场不一致或证据核对不通过)。锚点对不上,说明场定义或契约里的时间常量还留着旧场的值。
+7. 页面与门禁验收。逐条跑:`python guanlan.py check` 装机自检(退出码 0 全绿、2 有 FAIL)、`python scripts/check_portability.py` 可移植性门禁(0 通过、1 有严重项)、`python scripts/log_audit.py` 日志口径(0 通过,5 至 9 对应各类问题)、`python scripts/chain_gap_check.py` 振动六层链完整性(0 齐全、5 缺料)、`python scripts/version_log.py --check` 版本记录一致(0 一致或已写、2 文件缺失、6 需重生成)。起服务的命令是 `python guanlan.py serve`,退出码 0 表示六个组件全部就绪、1 表示有模块未起(属降级)、2 表示健康检查 60 秒未就绪。
+
+#### 16.3.2 换场检查表
+
+检查表如表 16-3 所示,每一条都能用一条命令或一次页面查看判定,不通过时同时给出常见原因。
+
+| 检查项 | 判据 | 不通过时的常见原因 |
+|---|---|---|
+| 场定义可加载 | available() 列出新场,farm('<场>') 不报错 | 七个必填键缺项,或文件名与场名不一致 |
+| 场站目录辨识 | station_how 为 raw_station 或 alias | 目录名与 raw_station 和 src_farm_names 都对不上(落 none) |
+| 机组清单 | 场定义的 turbines 与现场台数一致,逐台主件齐全 | 台号形态不同,内部台号与主件命名不对应 |
+| 源类目录齐全 | 八个约定子目录在位,件数与体积与收资清单一致 | 多套一层包名目录,或缺源类 |
+| 指纹与增量 | raw_scan 退出码 0 或 4;raw_data_check 退出码 0 | 结构或命名违例,同名不同大小冲突 |
+| 重算整链 | rebuild_all 退出码落在容忍集合内,无容忍外中断 | 缺原始件,缺基线,契约自检不过 |
+| 阈值标定 | 温度与蓄能与 M9 与曲线的门限经新场数据复核 | 沿用旧场阈值,导致大面积误报或漏报 |
+| 时间窗锚点 | inventory_products 的产物跨度落在输入跨度内 | 干净时间窗与对照时间窗常量仍是旧场的日期 |
+| 代码表与术语库 | 本体对象库按新场交付件重新摄入 | 沿用旧场代码表,问答引不到契约条目 |
+| 坐标与页面 | 场站坐标件在位,页面场名与台数正确 | 坐标件缺失,页面仍显示旧场名 |
+| 服务与入口 | guanlan.py serve 退出码 0,门户与工作台可开 | 端口占用,缺产物 |
+| 门禁全绿 | guanlan.py check 全绿 | 有 FAIL 项,文档未随版本更新 |
+
+### 16.4 扩展点
+
+换场之外,系统还有六类需要动刀的扩展;每一类的落点与改动面如表 16-4 所示。
+
+| 扩展类型 | 落点 | 改动面 |
+|---|---|---|
+| 接新主机制造商(OEM) | 机型判据契约 reference/<场>/windscada_contract.yaml;主机技术资料目录(与场站目录并列);本体摄入第一步 | 契约条目与该机型的判据阈值;曲线七镜头的机型常数;通道到系统的映射与系统关键词映射;术语库;重跑本体六步 |
+| 接新数据形态(CSV 转发层) | 取数层的形态接纳(CSV 优先、该台没有 CSV 才回落归档库),形态来源记在返回表属性里供台账与页面说明来路 | 只加形态识别与回落规则,引擎与判级不动;新形态要能被逐族指纹扫描认到,否则"新数据必进重算"的自动取消跳过会失效 |
+| 接月度类目归档库 | 上游归档件;先按时间戳与机组标识对齐合成为同台 10min 补充件 scada_10min/台号-YYYYMM.csv(列与主件逐列同名同序、时间戳为 ISO 形式) | 只加一层转换与对齐;转换后 ③ 与 ④ 与 ④c 自动纳入;类目库本身只做信息级列出,不计变化、不假装被摄入 |
+| 接其它导出 | 场站目录下的约定子目录接口(八个约定子目录,改名等于换接口) | 约定子目录名要同步源类表、README 与维护页;摄入脚本按"源类目录再往下一层就是文件"读 |
+| 接新判级面 | 子系统的判级函数注册表加进系统集合 | 新增注册表,补四维证据与取严序,加进系统集合,补契约条目,补页面归口登记,补审计器期望 |
+| 接新机型契约 | 契约文件即判据阈值的来源 | 改契约等于改判据,须同步阈值标定与等价验收;旧产物的判词会随契约变化,属预期 |
+
+### 16.5 已知未接通与边界
+
+本节如实列出多场机制当前的缺口与换场时会失效的假设,不含估计。
+
+- 多场机制在样本场之外的实测缺口:可用场清单当前只有内置的一个场,配置目录里其余同名文件不是场定义;16.3 节的作业单是按代码路径与守卫退出码推导出的可执行清单,不是在第二个场上跑通的现场记录。因此"可换场复用"目前有设计证据与单场实测证据,缺第二场的端到端证据。
+- 原生归档库未装配:本机未装配原生的归档库读取驱动(既无该形态的 Python 驱动,也无命令行工具),读月度归档库走系统提供的数据访问组件加命令行(与写库同一套);目标机未装该组件时该形态不可读,且未在无该组件的机器上实测。
+- 原始件根的环境变量名是代码侧遗留:原始件根默认 data/raw,可由 configs/serve.json 的 raw_dir 覆盖,启动器把它导出成一个"内嵌样本场代号"的环境变量,形如 WINDSCADA_<场>_SRC。该变量名与仿真回放资产目录名里各有一处写死的样本场代号,属去标识化未覆盖的代码侧遗留;真正接第二场时这两处需一并中性化或改为按场名派生。
+- 门禁在无文档部署上的口径:交付文档三件不在成品包里时,版本记录一致性门与文档类门禁按"文档这一路缺失"容忍(重算链 ⑧d 步容忍 6,版本记录脚本只定义 0 与 2 与 6),即在只带程序的部署上门禁不会因为"没有文档"而判失败;代价是它也不会替使用方发现文档漂移。
+- 换场后失效的假设之一,阈值标定:温度同工况比档的三级偏差阈值与稳健 z 门限、蓄能带宽比门限、M9 的分组容差与离群倍数、曲线八项显著门与分箱边界,全部是在样本场按该场机型的实测分布标定的。换场后不重新标定,门限过紧会把正常差异判成候选(表现为大面积报警),门限过松会把真实异常漏在门限之下;两者都是标定问题,不是算法问题。
+- 换场后失效的假设之二,锚点与时间窗常量:残月识别与台时口径依赖"每小时 6 个节拍"的 10min 节拍假设;温度面的对照时间窗与同历月早一年对照时间窗、限电前干净时间窗、本体判级时间窗口径(近 90 日固定时间窗)都是与样本场数据末端绑定的常量。换场后必须按新场的数据起止与限电史重设,否则"干净时间窗"不再干净,判级会带上限电造成的功率与转速偏置。
+- 换场后失效的假设之三,术语库与代码表:系统关键词映射、本体对象库的部件名与失效机制、作业指导与中间件正本、场站故障代码表、场站坐标件都是从样本场的交付件摄入的。换场后不重新摄入,本体页、机制链与问答会成片引不到契约条目并显示"未被契约背书";故障代码对不上时,按代码归因的判级面会整体落到不可判。
+- 换场后失效的假设之四,机组清单与台号形态:机组清单、内部台号形态(样本场形如 01E…)、逐台主件命名、窄仓的缺列记录都按台号走;换场后台数变了,页面上"全场 N 台"一类表述与已烘焙进快照件的台数是生成时刻的场相关值,需要重算并重烘焙总览页。
+
+### 16.6 与同批交付文档的对应关系
+
+本章对应《需求分析_观澜_2.10.1.docx》第 11 章(场配置化、机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、跨场统一口径、按场裁剪收资、换场验收检查表)与《数据要求说明_观澜_2.10.1.docx》第 12 章(通用必选与可选的判定规则、场配置字段对照、数据源形态适配、换场收资差异清单、按场裁剪步骤);本章给的是设计侧的落点、换场作业单与检查表,数据侧的收资口径与字段要求以数据要求说明为准,需求侧的验收项以需求分析为准。
+
+***
+
+## 17 已知边界与未实现
+
+### 17.1 逐条边界与处置建议
+
+本系统坚持"缺件如实"。边界、影响与处置建议如表 17-1 所示,每条的处置建议都是可执行的一条命令或一次催缴动作。
 
 | 边界 | 影响 | 处置建议 |
 |---|---|---|
@@ -1026,9 +1176,9 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 | 文档引用的模型离线包不在包内 | vendor 目录实测只有 4 件(清单文件加三份便携运行时),没有模型安装包目录;而说明书与安装脚本多处引用该目录 | 现场需要模型时单独交付模型安装包与模型目录;或在文档中明确"该目录随单独交付件提供" |
 | Windows 服务与 systemd 未在本次环境实测 | 本机未注册服务(状态报已注册为假、运行中为假),Windows 服务真注册与 Linux 单元真装未取证 | 用 --dry-run 预览、用免权限守护自证;具备管理员或 root 权限时执行注册并复核状态 |
 
-### 16.2 口径冲突与待统一项
+### 17.2 口径冲突与待统一项
 
-系统里仍有多处文档或代码口径不一致,如实列出如表 16-2 所示。这些不是"功能坏了",而是同一件事有两种写法,读者与维护方必须知道以哪一处为准。
+系统里仍有多处文档或代码口径不一致,如实列出如表 17-2 所示。这些不是"功能坏了",而是同一件事有两种写法,读者与维护方必须知道以哪一处为准。
 
 | 冲突项 | 两种口径 | 以哪一处为准 |
 |---|---|---|
@@ -1041,7 +1191,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 | 平均无故障间隔(MTBF)台时口径 | 停机归因模块已改为实测台时,部件可靠性表仍用理想日历台时 | 需统一到实测台时(否则同一指标两个值) |
 | 网关离线守卫端口 | 守卫里硬编码判断旧端口 8033,而端口表里工作台是 18033 | 以 configs/serve.json 为准,守卫待同步 |
 | 前端一处外链端口 | 工作台一处外链硬编码 8030,该端口不在端口表内 | 以 configs/serve.json 为准,外链待改 |
-| 多场机制未接通 | 场配置目录下有多件 YAML,但可用场列表只认 JSON,且磁盘上这些 YAML 实为另一条链的机型物理约束档;当前只有如东一场可用 | 以场配置加载函数的实行为准;要么让加载器同时认 YAML,要么把场定义转成 JSON |
+| 多场机制只在样本场跑通 | 场配置目录下有多件 YAML 不是场定义(实为另一条链的机型物理约束档,由 foreign_farm_files() 登记为 profile);场定义加载器已同时认 YAML 与 JSON,但样本场之外没有第二个场做过端到端实跑(见 16.5 节) | 以场配置加载函数的实行为准;新场按 16.3 节作业单实跑一遍并按 16.3 节检查表验收 |
 | 重算操作手册的操作表 | 手册的步骤表仍写"默认不重生成 CMS 报告",而现行链自 2026-09-18 起默认生成报告(要旧行为用 --no-vib-report);手册第⑤步仍写旧的补齐脚本,而现行链第⑤步是反向呼应审计 | 以 scripts/rebuild_all.py 为准,手册待同步 |
 | 运维日志路径写法 | 一处写 logs/ops_动作_时间.log,实际是 logs/ops/ops_动作_时间.log | 以日志口径模块与实盘为准 |
 | 问答档位表 | 工作台服务端保留着旧的档位名(一个未安装的中档与一个混合专家档),配置文件的升档档位已经是本机实装的 32B 档 | 以 configs/models.json 为准(它同时是升档解析的第一顺位),服务端展示表待同步 |
@@ -1050,7 +1200,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 | 本体对象库规模 | 设计文档写 9,613 个对象(2026-09-17 时点),盘上实测 9,791 个(2026-09-21 修改时间) | 引用规模时须注明口径日期,或重测 |
 | 安装说明的口径 | 说明书里仍写"所有服务只监听本机",与后来"门户监听所有网卡"的口径不一致;同一份说明书一处的"不含重算链"与另一处的"重算链已进包"互相矛盾 | 以 configs/serve.json 与 scripts/rebuild_all.py 的现状为准,说明书待修订 |
 
-### 16.3 未实现项
+### 17.3 未实现项
 
 以下三项是"设计上这样定,但功能未实现",如实列出:按所选时间窗的缓存没有过期时间与容量上限(进程内常驻、无淘汰、无持久化),长时间运行且频繁换时间窗时内存会持续增长;后台预热不覆盖 M9(只预热判级与默认时间窗的镜头);时间窗词表没有"今日、近 7 日、本月、本年"这类词(只有六个预设加单月、月区间、自定义起止)。此外判级矩阵本身只在内存里返回,不落 parquet 产物,因此"某一时刻的判级矩阵"没有独立留档件,只能靠当时的页面快照与日志。
 
@@ -1062,7 +1212,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 
 | 章节 | 来源文件 |
 |---|---|
-| 1 文档说明 | src/version.py(VERSION 与 HISTORY 的 2.10.0 条目)、docs/需求分析_观澜_2.10.0.docx(进行中)、scripts/delivery_docs_figures.py |
+| 1 文档说明 | src/version.py(VERSION 与 HISTORY 的 2.10.1 条目)、docs/需求分析_观澜_2.10.1.docx、scripts/delivery_docs_figures.py |
 | 2 设计目标与原则 | docs/系统设计说明.md(四条设计铁律)、src/ontology/mcp_server.py 头部、src/windscada/subsys/fusion.py 头部、src/windscada/perf/reliability.py |
 | 3 总体架构 | configs/serve.json、scripts/guanlan_gateway.py、guanlan.py(组件启动表)、run/pids.json、docs/系统设计说明.md(产物全景) |
 | 4 目录结构与路径真源 | src/paths.py、configs/registry.yaml、docs/系统设计说明.md(路径约定与统一记录)、本次目录实测 |
@@ -1070,16 +1220,19 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 | 6 判级与算法 | src/windscada/taxonomy.py、src/windscada/perf/curves.py、src/windscada/perf/control.py、src/windscada/perf/reliability.py、src/windscada/perf/faults.py、src/windscada/perf/availability.py、src/windscada/subsys/pitch.py、src/windscada/subsys/yaw.py、src/windscada/subsys/hydraulic.py、src/windscada/subsys/temp_nbm.py、src/windscada/subsys/thermal_chain.py、src/windscada/subsys/structure.py、src/windscada/subsys/fusion.py、src/windcms/report_std.py、src/windscada/ui_en.py |
 | 7 时间窗口径 | scripts/windscada_serve.py(词表、按时间窗缓存、pending、预热)、src/windscada/ui/app.js、src/version.py(2.9.0 条目) |
 | 8 服务与前端设计 | scripts/windscada_serve.py(路由与烘焙调用)、scripts/guanlan_gateway.py(改写与豁免)、src/windscada/ui/snapshot.py、src/windscada/ui/app.js、scripts/windscada_overview_build.py |
-| 9 本体与知识层 | src/ontology/kb_ingest.py、populate.py、chain_ingest.py、trend_ingest.py、retrieval.py、maintenance.py、mcp_server.py、scripts/guanlan_facts_contract.py、scripts/sop_findings_from_ledger.py、outputs/rudong/ontology(实测规模) |
+| 9 本体与知识层 | src/ontology/kb_ingest.py、populate.py、chain_ingest.py、trend_ingest.py、retrieval.py、maintenance.py、mcp_server.py、scripts/guanlan_facts_contract.py、scripts/sop_findings_from_ledger.py、outputs/<场>/ontology(实测规模) |
 | 10 本机模型接入 | configs/models.json、src/ontology/fast_agent.py、src/ontology/llm_gate.py、logs/audit/llm_audit.jsonl、docs/版本记录.md(2.9.1 与 2.9.2 条目) |
 | 11 运维控制台与重算编排 | scripts/guanlan_ops.py、scripts/_ops_launch.py、scripts/_ops_run.py、scripts/_ops_stop_keep_gateway.py、run/ops_job.json、logs/ops/、src/logfile.py |
 | 12 安装服务化与版本管理 | install.ps1、install.sh、uninstall.bat、uninstall.sh、scripts/guanlan_uninstall.py、scripts/service_ctl.py、scripts/win_service.py、scripts/service_worker.py、scripts/service_main.py、install-info.json、src/version.py、scripts/pack_dist.py、scripts/version_log.py、docs/版本记录.md |
 | 13 质量保证 | scripts/products_reverse_audit.py、pages_audit.py、config_audit.py、log_audit.py、raw_scan.py、chain_gap_check.py、version_log.py、detail_deps.py、inventory_products.py、check_portability.py、audit_chinese_terms.py、src/entry_refs.py、guanlan.py check(底稿 docs/src/_guanlan_check.txt) |
 | 14 安全合规与离线边界 | scripts/security_scan.py、scripts/deliver_desensitize.py、src/windscada/deid.py、src/windscada/deid_public.py、src/windcms/redact.py、configs/serve.json 注释 |
-| 15 可移植性与资源占用 | docs/移植与独立运行_v0.1.md、docs/说明书_观澜如东样板v2_v0.2.md、requirements.txt、wheels/ 与 vendor/ 实测、install.sh、scripts/service_ctl.py、本次目录实测 |
-| 16 已知边界与未实现 | docs/系统设计说明.md(缺口与边界、变更记录)、docs/源代码化清单_v0.1.md、docs/重算缺口与补件清单_v0.1.md、configs/registry.yaml(known_missing)、src/windscada/scada_source.py、docs/版本记录.md(遗留项) |
+| 15 可移植性与资源占用 | docs/移植与独立运行_v0.1.md、《使用说明书》v0.2(随包 docs/)、requirements.txt、wheels/ 与 vendor/ 实测、install.sh、scripts/service_ctl.py、本次目录实测 |
+| 16 多场适用性与换场迁移 | src/windscada/config.py(场抽象层与七个必填键)、configs/farms/_模板.yaml.example、src/paths.py 的 farm() 与 farm_config() 与 contract()、scripts/config_audit.py、本次场配置与场站目录实测 |
+| 17 已知边界与未实现 | docs/系统设计说明.md(缺口与边界、变更记录)、docs/源代码化清单_v0.1.md、docs/重算缺口与补件清单_v0.1.md、configs/registry.yaml(known_missing)、src/windscada/scada_source.py、docs/版本记录.md(遗留项) |
 | 附录 B 与附录 C | 仓库实测路径与模块清单、docs/系统设计说明.md 的术语用法 |
 
+表 A-1 里的场相关路径与文件名一律写成占位符模板,替换规则是:`<场站>` 换成 data/raw 下的场站目录名(即场定义的 raw_station,辨识不到时用配置值);`<场>` 换成场名(configs/farms/ 下场定义文件的主名,同时是 outputs 与 reference 下的目录名,也是 WINDSCADA_FARM 的取值);`<机型>` 换成机型代号;`<OEM>` 换成主机制造商代号;`<源类目录>` 换成八个约定子目录之一(scada_10min、scada_1min、scada_mdb、故障报警、风机故障记录、油样报告、windcms、m5_cms_tcm)。本文正文与表中一律称样本风电场(下称本场),按上述规则逐条替换即可得到目标场的实例路径;少数被引文档名本身含样本场字样,表中已按对外名写法归一(如 docs/说明书_观澜v2_v0.2.md)。
+
 ## 附录 B 关键模块清单
 
 仓库相对路径到职责的对应关系如表 B-1 所示。
@@ -1087,7 +1240,8 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 | 仓库相对路径 | 职责 |
 |---|---|
 | guanlan.py | 启动器与自检:check 与 serve 与 status 与 stop 与 open 与 qa 与 version 七个子命令 |
-| src/paths.py | 路径唯一真源:安装根解析、各产物仓与配置与解释器助手 |
+| src/paths.py | 路径唯一真源:安装根解析、各产物仓与配置与解释器助手;场名解析与按场落点(farm 与 store 与 contract) |
+| src/windscada/config.py | 场抽象层:场定义加载与必填键校验、场站目录扫描辨识与别名匹配、可用场与场切换 |
 | src/version.py | 版本唯一真源:VERSION 与版本规则与 HISTORY 与包名与安装记录读写 |
 | src/proc.py | 子进程统一口径:无窗口标志、后台起进程、前台短命令 |
 | src/logfile.py | 日志唯一口径:目录与命名与行格式与保留策略与标准输出包装 |
@@ -1125,7 +1279,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 
 | 术语或缩写 | 含义 |
 |---|---|
-| 观澜 | 本系统的产品名;如东样板指以如东海上风电场为样板场站 |
+| 观澜 | 本系统的产品名(观澜 v2 风电场智能分析系统);样板指以样本风电场为依据的第一套落地实例 |
 | 产物 | 由原始件算出来、供页面与接口消费的件(parquet、json、md、html 等),只落在 outputs/场站/ 下 |
 | 产物仓 | outputs/场站/ 下的九个顶层目录(windscada、ontology、windcms、m5_cms_tcm、tcm_compatible_replay、sop、guanlan、pitch、paradigm_r1) |
 | 原始件 | 现场交付的输入数据,只读,落在 data/raw/场站/ 下 |
@@ -1150,3 +1304,9 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 | 升档 | 默认模型被校闸拦下后改用更强档位重答一次 |
 | 中间件 | SOP 中间件与评审落盘件(outputs/场站/sop) |
 | 冻结交付件 | 按交付版本发布、不随输入数据自动变的客户交付件 |
+| 场定义 | configs/farms/<场>.yaml,描述一个场的机组清单、源类目录与产物落点的配置对象(七个必填键) |
+| 场抽象层 | 把"场"做成可加载、可校验、可切换的配置对象的机制:配置层加路径层加语义层 |
+| 场无关引擎 | 换场不需要改动的部分:判级与算法结构、融合纪律、本体与检索、服务与前端、审计门 |
+| 场相关参数 | 换场必须重新给或重新标定的部分:阈值、机型参数、时间窗锚点、机组清单、术语库与代码表、坐标件 |
+| 换场作业单 | 从准备场配置到页面与门禁验收的逐步命令清单,每步带命令与退出码语义 |
+| 场站目录辨识 | 用 raw_station 与 src_farm_names 把 data/raw 下的目录认成本场的四段式判定(raw_station 与 alias 与 single 与 none) |

+ 230 - 68
docs/src/需求分析_观澜_2.10.0.md → docs/src/需求分析_观澜_2.10.1.md

@@ -1,10 +1,10 @@
-# 需求分析 · 观澜·如东样板 v2 · 版本 2.10.0
+# 需求分析 · 观澜 v2 风电场智能分析系统 · 版本 2.10.1
 
 ## 1 文档说明
 
 ### 1.1 目的
 
-本文是「观澜·如东样板 v2」的**需求分析文档**,回答四个问题:这套系统究竟要满足谁的什么需要;这些需要从哪来、经过哪些版本变成现在的样子;每一条需要对应什么功能、什么输入、什么输出、拿什么判据验收;以及哪些事本版明确不做。本文与同批交付的《设计说明_观澜_2.10.0.docx》《数据要求说明_观澜_2.10.0.docx》配套:本文讲「要什么、凭什么算做到了」,设计说明讲「怎么做的」,数据要求说明讲「要哪些数据、什么形态、什么单位」。
+本文是「观澜 v2(风电场智能分析系统)」的**需求分析文档**,回答四个问题:这套系统究竟要满足谁的什么需要;这些需要从哪来、经过哪些版本变成现在的样子;每一条需要对应什么功能、什么输入、什么输出、拿什么判据验收;以及哪些事本版明确不做。本文与同批交付的《设计说明_观澜_2.10.1.docx》《数据要求说明_观澜_2.10.1.docx》配套:本文讲「要什么、凭什么算做到了」,设计说明讲「怎么做的」,数据要求说明讲「要哪些数据、什么形态、什么单位」。
 
 本文的写作口径是**只写能取证的事实**:每个数字、每条结论都能指到仓库里的某个文件、某次实跑输出或某条版本记录;查不到、取不到的一律写明「未取证」或「未到位」,不做推测性补全。因此文中会出现少量「未取证」的说明句,那是刻意留下的诚实边界,不是遗漏。
 
@@ -16,36 +16,36 @@
 
 ### 1.3 版本对应关系
 
-系统版本号只有一个真源:仓库内的 src/version.py 的 VERSION 一行。本版 VERSION = 2.10.0,版本记录(HISTORY)共 12 条,人读的版本表由 scripts/version_log.py 从 HISTORY 生成到 docs/版本记录.md,并由 version_log.py --check 与 guanlan.py check 双重校验「记录表与代码一致」。
+系统版本号只有一个真源:仓库内的 src/version.py 的 VERSION 一行。本版 VERSION = 2.10.1,版本记录(HISTORY)共 13 条,人读的版本表由 scripts/version_log.py 从 HISTORY 生成到 docs/版本记录.md,并由 version_log.py --check 与 guanlan.py check 双重校验「记录表与代码一致」。
 
 表 1-1 版本与文档的对应关系
 
 | 项 | 值 | 取证方式 |
 |---|---|---|
-| 系统版本 | 2.10.0 | src/version.py 的 VERSION;guanlan.py check 实跑报「版本管理: 观澜·如东样板 v2 v2.10.0」 |
-| 交付包名 | app_guanlang_v2.10.0.zip | src/version.py 的 package_name();同一条 check 输出 |
-| 本文版本 | 2.10.0(与系统版本同号) | 本文标题与 src/version.py 的 VERSION |
-| 版本史条目数 | 12 条(最新一条即本次文档交付) | src/version.py 的 HISTORY;docs/版本记录.md |
+| 系统版本 | 2.10.1 | src/version.py 的 VERSION;guanlan.py check 实跑报「版本管理: 观澜 v2(风电场智能分析系统) v2.10.1」 |
+| 交付包名 | app_guanlang_v2.10.1.zip | src/version.py 的 package_name();同一条 check 输出 |
+| 本文版本 | 2.10.1(与系统版本同号) | 本文标题与 src/version.py 的 VERSION |
+| 版本史条目数 | 13 条(最新一条即本次文档交付) | src/version.py 的 HISTORY;docs/版本记录.md |
 | 版本号规则 | v大版本号.中版本号.小版本号;大改方案或架构、中改非核心功能、小为消缺 | src/version.py 的 BUMP_RULE 与 LEVEL_MEANING;docs/系统设计说明.md §15 |
 | 版本级别机器判据 | level_of(旧, 新) 返回 major 或 minor 或 patch 或 same | src/version.py 的 level_of() |
-| 本次变化级别 | 中(minor,非核心功能新增,即三份交付文档与其生成器) | HISTORY 中 2.10.0 条目的 level 字段 |
+| 本次变化级别 | 小(patch,交付文档对外化:去样本场标识与新增多风电场适用性章节,系统功能未变) | HISTORY 中 2.10.1 条目的 level 字段 |
 
-本版相对 2.9.2 的实质变化是**交付物新增**:三份 Word 文档(需求分析、设计说明、数据要求说明)落地,且文档的源件与插图生成器都源代码化(docs/src 下的 markdown 源件加上渲染器与插图生成器)。依据用户令原文:「整理观澜的需求/设计/数据接入,各写一份 Word 文档,要求区分章节目录、文表图并茂、字体字号分类统一;数据要求说明结合现场《数据分析收资要求-v3.docx》」。
+本版相对 2.10.0 的实质变化是**交付文档对外化**:三份交付文档(需求分析、设计说明、数据要求说明)全文不体现具体风电场(去标识化),并新增多风电场适用性一章;系统功能未变,属交付物修订。依据用户令原文:「修改三份文档,内容参考样本风电场,但不体现样本风电场,且具有不同风电场适用性」(引用时把样本场名按去标识化口径写成「样本风电场」)。
 
 ### 1.4 编写依据与取证口径
 
-本文的取证方式分四种,文中出现数字时都能归到其中一种:**读源**(读代码、配置、登记表)、**读文档**(读仓库内已交付的设计与操作文档)、**实跑**(跑只读的自检与只读的审计脚本)、**实测计数**(对 data 与 outputs 目录做文件级计数与求和)。为避免把「文档里写过」当成「现在就是这样」,凡与现状有关的数字,本文优先采用实跑与实测计数,并在文中标注日期。
+本文的取证方式分四种,文中出现数字时都能归到其中一种:**读源**(读代码、配置、登记表)、**读文档**(读仓库内已交付的设计与操作文档)、**实跑**(跑只读的自检与只读的审计脚本)、**实测计数**(对 data 与 outputs 目录做文件级计数与求和)。为避免把「文档里写过」当成「现在就是这样」,凡与现状有关的数字,本文优先采用实跑与实测计数,并在文中标注日期。本文出现的全部实测数字(件数、体量、行数、日期覆盖、耗时、退出码等)都出自**样本场实测(2026-09-22)**:保留它们是为了说明判据与量级,换场后必须按新场重新标定与重跑,不能直接当作新场的预期值——第 11 章的多风电场适用性要求正是围绕「结构不变、参数与数据随场变」写的。
 
-表 1-2 取证方式与本版关键数字
+表 1-2 取证方式与本版关键数字(样本场实测(2026-09-22))
 
 | 取证方式 | 本版用到的实跑或实测 | 关键数字 |
 |---|---|---|
 | 只读自检 | guanlan.py check(2026-09-22 本机) | 结论「全绿,可 serve」,退出码 0,无 FAIL 项,41 条判定行 |
 | 只读审计 | scripts/pages_audit.py --check(2026-09-22) | 检查 21 项,不一致 0,已知缺口 19,退出码 0 |
 | 只读计划 | scripts/rebuild_all.py --dry-run(2026-09-22) | 默认 26 步;加 --skip-scada 为 23 步 |
-| 实测计数 | data/raw/如东 与 outputs/rudong 递归计数(2026-09-22) | 输入 26,504 件 / 224.67 GB;产物 3,549 件 / 7,157.5 MB |
-| 读源 | src/version.py、configs/portal_pages.yaml、configs/serve.json、guanlan.py、scripts/rebuild_all.py | 12 条版本史;25 条顶层页面条目(含 7 条子页共 32 条);6 个服务端口 |
-| 读文档 | docs/系统设计说明.md(56 个标题)、docs/说明书_观澜如东样板v2_v0.2.md、docs/输入数据放置指导_v0.1.md、docs/数据目录结构与落位约定_v0.2.md、docs/重算操作手册_v0.1.md、docs/页面输出体检_v0.1.md、README_先读我.MD | 逐章依据见附录 A |
+| 实测计数 | data/raw/<场站> 与 outputs/<场> 递归计数(2026-09-22) | 输入 26,504 件 / 224.67 GB;产物 3,549 件 / 7,157.5 MB |
+| 读源 | src/version.py、configs/portal_pages.yaml、configs/serve.json、guanlan.py、scripts/rebuild_all.py | 13 条版本史;25 条顶层页面条目(含 7 条子页共 32 条);6 个服务端口 |
+| 读文档 | docs/系统设计说明.md(56 个标题)、docs/说明书_观澜…v0.2.md、docs/输入数据放置指导_v0.1.md、docs/数据目录结构与落位约定_v0.2.md、docs/重算操作手册_v0.1.md、docs/页面输出体检_v0.1.md、README_先读我.MD | 逐章依据见附录 A |
 
 ### 1.5 术语与描述口径约定
 
@@ -59,13 +59,15 @@
 | 影响机组 | 影响风电机组时写「影响机组」 | 表头与说明含报告构建器;数据字段名暂保留 |
 | 英文简写 | 中文(英文简写) | 平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);i18n_en 键同步 |
 
+另外,本文出现的台号(如 WTG01 至 WTG38 一类写法)是**样本场内部编号**,保留编号形态只是为了与产物、台账和依据逐条对得上;换场时按实际台号替换,编号本身不构成接口约定。
+
 术语的完整解释见附录 B。
 
 ## 2 系统概述
 
-### 2.1 如东样板是什么
+### 2.1 本系统是什么
 
-观澜·如东样板 v2 是一套**纯离线的海上风电场智能分析单包系统**,对象是中广核江苏如东海上风电场的 38 台西门子 SWT-4.0-130 机组(容量 4.0 MW 级)。它把现场给来的运行数据、状态监测、报警、工单、油样落到每台机组的正确部件上,给出带证据、带边界、带责任人的检修结论;**判断在代码里,本机模型只负责问答、解释与引用,不发明结论**。软件不含任何联网功能,也不需要现场系统配合改造:原始件从 data/raw 读入,产物落在 outputs 下,页面只读产物,产物由数据算出来。
+本系统(观澜 v2,风电场智能分析系统)是一套**纯离线的海上风电场智能分析单包系统**,对象是**样本风电场(下称「本场」)**的 38 台 4.0 MW 级海上机组;机型代号与主机制造商(OEM)名称从略,业主单位名称从略。它把现场给来的运行数据、状态监测、报警、工单、油样落到每台机组的正确部件上,给出带证据、带边界、带责任人的检修结论;**判断在代码里,本机模型只负责问答、解释与引用,不发明结论**。软件不含任何联网功能,也不需要现场系统配合改造:原始件从 data/raw 读入,产物落在 outputs 下,页面只读产物,产物由数据算出来。
 
 一页话术(给第一次接触的人的三十秒版本):这套系统不控制风机、不下发指令、不改现场任何系统。它做的是「把现场已有的数据读进来,算出一份能说清依据的体检报告,然后用一个门户和三个服务把它讲清楚」。放数据之前,页面会如实显示「无产物」并告诉你缺哪一件、怎么补;放了数据之后,一条命令重算,页面立刻有数。所有结论都能往回指到源件与算法,指不到的地方它会写「不可判」或「缺件」,而不是编一个数字。
 
@@ -89,7 +91,7 @@
 
 ### 2.3 系统能力清单
 
-表 2-2 系统能力清单(能力域、给谁用、载体、验收锚点)
+表 2-2 系统能力清单(能力域、给谁用、载体、验收锚点,样本场实测(2026-09-22))
 
 | 能力域 | 能力 | 主要载体 | 验收锚点或判据 |
 |---|---|---|---|
@@ -111,14 +113,15 @@
 
 需求从三条线汇拢。**第一条是现场运行的需要**:值班要一眼看清全场哪几台机组有问题,点检要顺着问题找到部件与证据,管理要一份能对外的结论。**第二条是交付与运维的需要**:机器上没有网络、现场不会敲命令、数据每月增量、程序要能装能卸能升级。**第三条是用户令**:每一次人工测试或现场事故之后,用户用一句话把要求定下来(例如「所有的计算均要形成观澜的源代码」「对 data/raw 下文件的增减做到监听」「增加自定义时间段」),研发把它落成代码、机器守卫与文档。
 
-三条线里,用户令是最硬的驱动力:它带来的是**可核验的验收判据**,而不是一段描述。本版 12 条版本记录里,绝大多数条目的形态都是「检查到的问题 → 根因 → 解决 → 验证」,其中「验证」一栏写的就是实跑的退出码与逐值对拍结果。
+三条线里,用户令是最硬的驱动力:它带来的是**可核验的验收判据**,而不是一段描述。本版 13 条版本记录里,绝大多数条目的形态都是「检查到的问题 → 根因 → 解决 → 验证」,其中「验证」一栏写的就是实跑的退出码与逐值对拍结果。
 
 ### 3.2 需求演进时间轴
 
-表 3-1 版本史与用户令要点(依据 src/version.py 的 HISTORY,共 12 条)
+表 3-1 版本史与用户令要点(依据 src/version.py 的 HISTORY,共 13 条)
 
 | 版本 | 日期 | 用户令要点 | 变化级别 |
 |---|---|---|---|
+| 2.10.1 | 2026-09-22 | 交付文档对外版:全文去样本场标识,并新增多风电场适用性一章(系统功能未变) | 小(patch) |
 | 2.10.0 | 2026-09-22 | 整理需求、设计、数据接入各写一份 Word 文档,并源代码化其生成器 | 中(minor) |
 | 2.9.2 | 2026-09-22 | 升级后验收补缺:重算链增 ⑧d 版本记录一致性门,版本与文档漂移有了自动拦截点 | 小(patch) |
 | 2.9.1 | 2026-09-22 | 人工测试 7 项消缺:描述口径、依据空白、问题页空、门户链接 404、闭环文案、本机模型升档档位 | 小(patch) |
@@ -132,17 +135,17 @@
 | 0.4.0 | 2026-09-17 | 打包默认不含输入数据与产物与日志与临时文件;无窗口启动;统一配置与日志;页面归口;服务化 | 旧编号(legacy) |
 | 0.2.0 | 2026-09-01 | 含产物的旧交付基线,历史上用作「包内无生成端」那批产物的补救源 | 旧编号(legacy) |
 
-需求的演进如图 1-1 所示。该图依据 src/version.py 的 HISTORY 绘制,覆盖上表 12 条记录(即两级旧编号 0.2.0 与 0.4.0,以及从 2.5.0 起按规则递增的十个版本)。按级别统计,这十个版本里「中」级变化六条(2.5.0、2.6.0、2.7.0、2.8.0、2.9.0、2.10.0),「小」级变化四条(2.8.1、2.8.2、2.9.1、2.9.2),没有大版本升级。这个分布清楚地显示出这条线的性质——**功能面在 2.5.0 到 2.7.0 之间快速铺开,之后转入以消缺与口径统一为主的密集收敛期**。
+需求的演进如图 1-1 所示。该图依据 src/version.py 的 HISTORY 绘制,覆盖上表 13 条记录(即两级旧编号 0.2.0 与 0.4.0,以及从 2.5.0 起按规则递增的十一个版本)。按级别统计,这十一个版本里「中」级变化六条(2.5.0、2.6.0、2.7.0、2.8.0、2.9.0、2.10.0),「小」级变化五条(2.8.1、2.8.2、2.9.1、2.9.2、2.10.1),没有大版本升级。这个分布清楚地显示出这条线的性质——**功能面在 2.5.0 到 2.7.0 之间快速铺开,之后转入以消缺与口径统一为主的密集收敛期**。
 
 ![图 1-1 观澜需求演进时间轴(依据 src/version.py 的 HISTORY)](figures/fig-req-01-需求演进时间轴.png)
 
-从时间轴能看出三个事实。第一,2.5.0 到 2.7.0 三天内出了三个中版本,全部是「装上、卸掉、打包、对外监听」这类交付能力,说明这一阶段的需求主线从「算得对」转向「交付得出去」。第二,2.7.0 之后密集出现小版本消缺,且每一条都带实跑验证,说明需求进入了「现场怎么用就怎么改」的收敛期。第三,2.10.0 是规格化的标志:需求不再只由代码与脚本承载,而是被写成可评审的文档,且文档本身也源代码化,可机器重生成。
+从时间轴能看出三个事实。第一,2.5.0 到 2.7.0 三天内出了三个中版本,全部是「装上、卸掉、打包、对外监听」这类交付能力,说明这一阶段的需求主线从「算得对」转向「交付得出去」。第二,2.7.0 之后密集出现小版本消缺,且每一条都带实跑验证,说明需求进入了「现场怎么用就怎么改」的收敛期。第三,2.10.0 是规格化的标志:需求不再只由代码与脚本承载,而是被写成可评审的文档,且文档本身也源代码化,可机器重生成。2.10.1 在这条线上再走一步:文档去样本场标识并补上多风电场适用性一章,使同一份文档能被别的风电场直接拿去用。
 
 ### 3.3 需求如何被人工程序化
 
 用户令通常是一句话,落到系统里要经过「变成判据、变成机器守卫、变成可复核证据」三步。第一步最关键:把一句中文要求翻译成一个**可判定的条件**。例如「时间窗变化时部件问题要跟着变」,翻译成「给定时间窗 w,判级矩阵的返回必须随 w 而变,且月度件按月取整、日粒度件按日精确」,这样才有办法验。第二步是把它做成每次都会跑的守卫,而不是靠人记得。第三步是留下可复核的证据(日志、退出码、逐值对拍结果、sha256)。
 
-表 3-2 用户令到程序化落点的对照(节选,按版本递进)
+表 3-2 用户令到程序化落点的对照(节选,按版本递进,样本场实测(2026-09-22))
 
 | 用户令(原话要点) | 程序化落点 | 可核验证据 |
 |---|---|---|
@@ -151,7 +154,7 @@
 | 所有的计算均要形成观澜的源代码 | 逐族补生成端:变桨面、在升闭环、三层基线、融合面 handoff、总览页、由台账生成 claim | 反向呼应审计成立 3,548 件、不成立 0 件、未归类 0 件 |
 | 运行期一律不从交付包补齐 | 第 ⑤ 步由「补齐随包件」改为「反向呼应审计」,只报账不搬运 | rebuild_all --dry-run 计划中 ⑤ 步命令为 products_reverse_audit.py --check |
 | 清除产物不留备份 | products_state.py --off --yes 改真删除;--on 与门户恢复按钮移除 | 控制台按钮语义与 409 前置校验;docs/系统设计说明.md §7 |
-| 装成服务并在安装时检查版本 | win_service.py 以 ctypes 直连 SCM;service_main.py 与 systemd 单元;install-info.json 记录版本并比对 | check 实跑报「卸载入口在位 2 个」「版本管理 v2.10.0」 |
+| 装成服务并在安装时检查版本 | win_service.py 以 ctypes 直连 SCM;service_main.py 与 systemd 单元;install-info.json 记录版本并比对 | check 实跑报「卸载入口在位 2 个」「版本管理 v2.10.1」 |
 | 观澜改为监听所有 IP | serve.json 增 public_host 且出厂为 0.0.0.0,仅门户网关用它,组件仍绑本机 | check 实跑显示 6 个端口全部运行中;README 第二节写明无鉴权风险与回退口径 |
 | 描述口径:时间窗、影响机组、中文(英文简写) | 界面文案与报告构建器统一改写;i18n_en 键同步 | docs/系统设计说明.md §17.1 与 §17.2;语言包 845 条前后端成对 |
 | 整理需求与设计与数据接入各写一份 Word 文档 | docs/src 下 markdown 源件;渲染器输出带域目录的 docx;插图生成器生成 14 张图,数字全部从真件取 | HISTORY 2.10.0 条目;本版三份文档 |
@@ -169,7 +172,7 @@
 | 值班运行 | 场站值班员、集控值班员 | 一眼看清全场状态;报警是否集中;有没有正在恶化的机组 | 门户总览、系统状态、工作台 overview 与 energy、振动与 CMS | FR-01 至 FR-05、FR-09 至 FR-12 |
 | 点检与检修 | 点检员、检修工程师、专业工程师 | 从现象定位到部件;要证据、要边界、要处置建议;看换件后是否回落 | 工作台 component 与 vibration 与 decision、问题页、CMS 逐台页 | FR-03 至 FR-05、FR-13 至 FR-17、FR-22 至 FR-25 |
 | 场站管理 | 场长、技术负责人、生产管理 | 结论能否对外;停机损失与可靠性账;治理进展 | 门户经验发现与交付文档、汇报纸与报告导出、治理清单交付件 | FR-03、FR-09 至 FR-12、FR-26 至 FR-28 |
-| 研发与交付 | 观澜维护方、交付工程师、审计人员 | 数据怎么接、重算是否成功、安装升级卸载是否干净、口径是否一致 | 运维控制台、数据重算页、命令行入口、版本记录与设计说明 | FR-29 至 FR-43、NFR 全部 |
+| 研发与交付 | 观澜维护方、交付工程师、审计人员 | 数据怎么接、重算是否成功、安装升级卸载是否干净、口径是否一致 | 运维控制台、数据重算页、命令行入口、版本记录与设计说明 | FR-29 至 FR-52、NFR 全部 |
 
 ### 4.2 场景到任务到页面
 
@@ -198,7 +201,7 @@
 
 ### 5.1 功能需求编号与总览
 
-功能需求按十一个域组织,编号从 FR-01 到 FR-43,共 43 条。编号是**稳定的**:新增需求往后排号,不重排已有编号;需求作废时保留编号并标注作废,不改用它。每条需求都给出五项:需求描述、输入、输出、验收判据、依据文件。其中「验收判据」是可判定的条件,「依据文件」指向仓库内的源或文档,便于复核。
+功能需求按十二个域组织,编号从 FR-01 到 FR-52,共 52 条。编号是**稳定的**:新增需求往后排号,不重排已有编号;需求作废时保留编号并标注作废,不改用它。每条需求都给出五项:需求描述、输入、输出、验收判据、依据文件。其中「验收判据」是可判定的条件,「依据文件」指向仓库内的源或文档,便于复核。
 
 表 5-1 功能需求域与编号分配
 
@@ -215,10 +218,11 @@
 | 门户与检索 | FR-33 至 FR-35 | 3 | 门户装配、页面归口与陈旧检测、检索索引 |
 | 数据接入与体检 | FR-36 至 FR-38 | 3 | 落位与体检、场站辨识、同台多件与上游归档 |
 | 安装与服务化与卸载 | FR-39 至 FR-43 | 5 | 离线安装、服务化、版本检查、卸载、打包与开箱验证 |
+| 多风电场适用性 | FR-44 至 FR-52 | 9 | 场配置化、机组与机型与数据源替换、判据按场标定、跨场口径与换场验收 |
 
 ### 5.2 判级与部件问题
 
-表 5-2 判级与部件问题(FR-01 至 FR-05)
+表 5-2 判级与部件问题(FR-01 至 FR-05,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
@@ -232,7 +236,7 @@
 
 时间窗是本版需求里最容易被误解的一块,因此口径要写死两句话:**月度件按月取整,日粒度件按日精确**;页面上要写明这两句,不让读者以为月度均值能切到天。
 
-表 5-3 时间窗与自定义区间(FR-06 至 FR-08)
+表 5-3 时间窗与自定义区间(FR-06 至 FR-08,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
@@ -242,7 +246,7 @@
 
 ### 5.4 发电性能与可靠性
 
-表 5-4 发电性能与可靠性(FR-09 至 FR-12)
+表 5-4 发电性能与可靠性(FR-09 至 FR-12,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
@@ -255,7 +259,7 @@
 
 振动这一域的写法要特别克制:它是全系统里**人工判断成分最多**的一段,因此需求必须区分「测量数据的函数」与「需要人工裁定的件」,不能把后者当成自动产物来承诺。
 
-表 5-5 振动与融合面(FR-13 至 FR-17)
+表 5-5 振动与融合面(FR-13 至 FR-17,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
@@ -267,7 +271,7 @@
 
 ### 5.6 停机与台账
 
-表 5-6 停机与台账(FR-18 至 FR-21)
+表 5-6 停机与台账(FR-18 至 FR-21,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
@@ -278,14 +282,14 @@
 
 ### 5.7 本体与问答
 
-表 5-7 本体与问答(FR-22 至 FR-25)
+表 5-7 本体与问答(FR-22 至 FR-25,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
-| FR-22 | 本体知识层:把厂商技术资料(手册、对译表、维护作业指导书、图纸)结构化,每条声明出处 | 西门子4.0技术资料(318 件 / 2.94 GB) | 对象库、检索索引、实机参数表 | 故障码覆盖 263 与 322;本体审计 0 问题 | docs/系统设计说明.md §13.1 |
+| FR-22 | 本体知识层:把厂商技术资料(手册、对译表、维护作业指导书、图纸)结构化,每条声明出处 | 主机制造商(OEM)技术资料(318 件 / 2.94 GB) | 对象库、检索索引、实机参数表 | 故障码覆盖 263 与 322;本体审计 0 问题 | docs/系统设计说明.md §13.1 |
 | FR-23 | 本体铺开:零判级权,只转录一层正本;同部件多源判级并存不合并;每条判定带审级与证据回溯 | 判级矩阵、融合表、各产物台账 | 对象库对象与判定条目 | 对象库实测规模为 9,613 对象(另见口径差异说明,§11.3);审计退出码 0 | docs/系统设计说明.md §13.1 |
 | FR-24 | 决策链进度与趋势证据:每台一个决策对象记六步状态、卡点与下一步;在升与闭环证据挂到机组与部件 | 运行时链盘接口、部件历史件 | 决策对象与趋势证据对象 | 需服务在跑;重复执行幂等覆盖 | docs/系统设计说明.md §13.1 |
-| FR-25 | 问答:模型只转述本体与事实契约,不产生新判级;校闸拦不下的句子不出文并给原因;升档目标按「配置档位且确已安装」解析 | 问题文本、本体与契约、本机模型(可选) | 答案与引文块、校闸结论、模型审计日志 | 台号与数字能回抓源记录;两档都拦则不出文并给原因;升档目标没装时响亮回落并打日志 | docs/说明书_观澜如东样板v2_v0.2.md §4.2;HISTORY 2.9.1 |
+| FR-25 | 问答:模型只转述本体与事实契约,不产生新判级;校闸拦不下的句子不出文并给原因;升档目标按「配置档位且确已安装」解析 | 问题文本、本体与契约、本机模型(可选) | 答案与引文块、校闸结论、模型审计日志 | 台号与数字能回抓源记录;两档都拦则不出文并给原因;升档目标没装时响亮回落并打日志 | docs/说明书_观澜…v0.2.md §4.2;HISTORY 2.9.1 |
 
 ### 5.8 报告导出
 
@@ -305,7 +309,7 @@
 |---|---|---|---|---|---|
 | FR-29 | 一条命令重算全部:固定依赖顺序、写死验收锚点、幂等可整条重跑;默认 26 步,加跳过 SCADA 为 23 步 | data/raw 原始件、可选现场包目录 | 各仓产物、每步退出码与耗时汇总 | 实跑 --dry-run 得默认 26 步、跳过 SCADA 23 步;某步失败即停并打印该步说明;各步幂等 | scripts/rebuild_all.py;docs/src/_rebuild_plan.txt |
 | FR-30 | 运维控制台:按钮按真实状态启用,一个时刻只允许一个动作,越权调用返回 409;显示端口、产物件数与来源台账、进度、退出码与日志尾巴 | 服务状态、产物状态、任务状态 | 控制台页面与动作接口 | 组件都在跑时点启动返回 409;有任务在跑时全按钮灰;停组件服务保留控制台 | docs/重算操作手册_v0.1.md §0b |
-| FR-31 | 清除产物为直接删除不留备份,且不动原始件与交付层;运行期一律不从交付包补齐缺件 | 用户确认动作 | 产物清空后的状态与缺件说明 | 清除后再点返回 409;页面显示无产物并给出补件办法;补齐入口定位为离线人工补救 | docs/说明书_观澜如东样板v2_v0.2.md §4.1;docs/系统设计说明.md §13.5 |
+| FR-31 | 清除产物为直接删除不留备份,且不动原始件与交付层;运行期一律不从交付包补齐缺件 | 用户确认动作 | 产物清空后的状态与缺件说明 | 清除后再点返回 409;页面显示无产物并给出补件办法;补齐入口定位为离线人工补救 | docs/说明书_观澜…v0.2.md §4.1;docs/系统设计说明.md §13.5 |
 | FR-32 | 输入数据自动扫描:逐族指纹发现新增与变化,列出该跑哪几步并记基线;支持自动取消跳过与可选看门狗 | data/raw 递归文件树、族表 | 扫描结论与基线快照、看门狗日志 | 退出码 0 表示无变化、4 表示有新数据(不是失败)、5 表示还没有基线;族表外的目录报未归类且不猜 | docs/输入数据放置指导_v0.1.md §4;HISTORY 2.8.0 |
 
 ### 5.10 门户与检索
@@ -320,7 +324,7 @@
 
 ### 5.11 数据接入与体检
 
-表 5-11 数据接入与体检(FR-36 至 FR-38)
+表 5-11 数据接入与体检(FR-36 至 FR-38,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
@@ -330,11 +334,11 @@
 
 ### 5.12 安装、服务化与卸载
 
-表 5-12 安装与服务化与卸载(FR-39 至 FR-43)
+表 5-12 安装与服务化与卸载(FR-39 至 FR-43,样本场实测(2026-09-22))
 
 | 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
 |---|---|---|---|---|---|
-| FR-39 | 离线安装:Windows 侧包内带全部离线依赖轮子可完全离线安装;无合格解释器时用包内便携运行时;安装目录不得含空格与中文 | 交付包、机器上的解释器与架构 | 虚拟环境与配置、安装记录 | 实装实测约 10 分钟量级(本机记录 172 秒);麒麟与统信与 Ubuntu 与 CentOS 走对应脚本 | docs/说明书_观澜如东样板v2_v0.2.md §3;README_先读我.MD 第一节 |
+| FR-39 | 离线安装:Windows 侧包内带全部离线依赖轮子可完全离线安装;无合格解释器时用包内便携运行时;安装目录不得含空格与中文 | 交付包、机器上的解释器与架构 | 虚拟环境与配置、安装记录 | 实装实测约 10 分钟量级(本机记录 172 秒);麒麟与统信与 Ubuntu 与 CentOS 走对应脚本 | docs/说明书_观澜…v0.2.md §3;README_先读我.MD 第一节 |
 | FR-40 | 服务化常驻:Windows 服务或 systemd 单元或登录自启三种形态,两平台共用同一工作体,行为与手工启动一致 | 服务注册器、服务体脚本、服务配置 | 托管进程与开机自启 | 巡检周期 15 秒;无权限时给出可执行建议并返回退出码 3 而不是抛异常 | docs/系统设计说明.md §14.1 与 §14.3 |
 | FR-41 | 安装时版本检查:读安装记录与本包版本比对,分没装过、相同、升级、降级、老安装无记录五类提示并询问是否重装 | install-info.json、本包版本 | 提示与交互、更新的安装记录 | 五类提示文案与动作与规则一致;跳过询问的开关可用;非交互时自动继续并打印说明 | docs/系统设计说明.md §14.2 |
 | FR-42 | 卸载闭环:默认只拆本程序装上去的东西(停服务、注销服务、撤自启、删快捷方式、清运行态、删安装记录),默认保留数据、产物、日志、虚拟环境与程序目录 | 卸载入口与实现脚本 | 卸载结果与手工收尾命令 | 自检两类断言:步骤齐全、只读预演无副作用;实跑断言记录与运行态与快捷方式都被拆掉且数据与产物一件不少 | docs/系统设计说明.md §14.5 |
@@ -344,34 +348,35 @@
 
 ### 6.1 非功能需求清单
 
-非功能需求编号 NFR-01 至 NFR-11,共 11 条。它们大多是「性质」而不是「功能」:判据往往是一次实跑的输出,或者一条机器守卫的退出码。
+非功能需求编号 NFR-01 至 NFR-12,共 12 条。它们大多是「性质」而不是「功能」:判据往往是一次实跑的输出,或者一条机器守卫的退出码。
 
-表 6-1 非功能需求(NFR-01 至 NFR-06)
+表 6-1 非功能需求(NFR-01 至 NFR-06,样本场实测(2026-09-22))
 
 | 编号 | 需求 | 判据 | 依据文件 |
 |---|---|---|---|
-| NFR-01 | 离线性:全部计算与页面在本机完成,不依赖外网;本机模型为可选件,缺它时只有问答与本地初筛不可用 | 组件只绑回环地址;模型服务不可达时探针报失败但其余功能不受影响;远端未装模型时如实记录为遗留项 | docs/说明书_观澜如东样板v2_v0.2.md §3.3 与 §4.2;HISTORY 2.9.2 |
+| NFR-01 | 离线性:全部计算与页面在本机完成,不依赖外网;本机模型为可选件,缺它时只有问答与本地初筛不可用 | 组件只绑回环地址;模型服务不可达时探针报失败但其余功能不受影响;远端未装模型时如实记录为遗留项 | docs/说明书_观澜…v0.2.md §3.3 与 §4.2;HISTORY 2.9.2 |
 | NFR-02 | 单机性能:一条命令重算在单机上可完成,按时间窗重算的单次等待可控 | 实测量级见 6.2;判级约 25 秒每个时间窗、镜头约 10 至 45 秒每个时间窗;模型探针实测 0.77 秒 | docs/系统设计说明.md §16.4;F:\temp\_req_check_out2.txt |
 | NFR-03 | 可移植与零配置:路径只有一个真源,全部按安装根解析而不按当前工作目录;跨 Windows 与国产化 Linux | 路径助手清单与解析基准;便携运行时覆盖三平台;跨平台部署三条硬约定由脚本门禁 | docs/系统设计说明.md §3.1;docs/数据目录结构与落位约定_v0.2.md §7 |
 | NFR-04 | 中文与描述口径:界面文案前后端成对,三条描述口径机器可查 | 语言包实跑 845 条成对;口径清扫覆盖时间窗写法、机组计量说法、英文简写三类 | F:\temp\_req_check_out2.txt;docs/系统设计说明.md §17.1 |
 | NFR-05 | 审计与可追溯:每件产物能说清谁生成、谁消费、来源是原始件重算还是随包补齐 | 逐件来源台账;反向呼应审计成立 3,548 件、不成立 0 件、未归类 0 件;契约每条带自身 sha256 | F:\temp\_req_check_out2.txt;docs/系统设计说明.md §13.4 |
 | NFR-06 | 可用性:没有产物也要能打开页面并说清缺件与补齐办法 | 清产物后工作台与振动页返回结构化缺件说明而不是白屏或掐断;无源件时返回 err=no_source | docs/系统设计说明.md §11.4;docs/数据目录结构与落位约定_v0.2.md §5 |
 
-表 6-2 非功能需求(NFR-07 至 NFR-11)
+表 6-2 非功能需求(NFR-07 至 NFR-12,样本场实测(2026-09-22))
 
 | 编号 | 需求 | 判据 | 依据文件 |
 |---|---|---|---|
 | NFR-07 | 可观测性:日志只有一个落点与统一行格式,服务状态与任务进度可见 | 日志实跑无不一致、提示 87 条;网关提供健康自检与版本指纹;控制台显示退出码与日志尾巴并有心跳 | F:\temp\_req_check_out2.txt;docs/系统设计说明.md §11.2;docs/重算操作手册_v0.1.md §0b |
-| NFR-08 | 数据体量:能在单机上承载本场当前的数据规模并给出资源下限 | 输入实测 26,504 件 / 224.67 GB、产物 3,549 件 / 7,157.5 MB;内存建议 16 GB 起、32 GB 更稳;磁盘 5 GB 加三维资产 1 GB、模型另计 30 至 60 GB | 2026-09-22 实测计数;docs/说明书_观澜如东样板v2_v0.2.md §2 |
+| NFR-08 | 数据体量:能在单机上承载本场当前的数据规模并给出资源下限 | 输入实测 26,504 件 / 224.67 GB、产物 3,549 件 / 7,157.5 MB;内存建议 16 GB 起、32 GB 更稳;磁盘 5 GB 加三维资产 1 GB、模型另计 30 至 60 GB | 2026-09-22 实测计数;docs/说明书_观澜…v0.2.md §2 |
 | NFR-09 | 安全只读:不写回现场系统、不改原始件、不产生控制指令;对外暴露时须自行在网侧限来源 | 原始件按只读输入对待,落位冲突默认拒绝;对外只有一个网关入口;启动日志每次提醒页面无鉴权 | docs/输入数据放置指导_v0.1.md §3;README_先读我.MD 第二节 |
 | NFR-10 | 可维护性:单一真源,改一处不必改多处;关键口径有机器守卫 | 版本号只有一行真源并由三处守卫盯住;路径、配置、日志、语言各有唯一取用口与审计器 | docs/系统设计说明.md §15.2 与 §11.1;docs/系统设计说明.md §3.1 |
 | NFR-11 | 无人值守与自愈:长跑的服务要能被托管并自动拉起 | 服务体每 15 秒巡检并拉起掉线组件;崩溃重启策略;远程部署一律用服务,不用会话前台进程 | docs/系统设计说明.md §14.1 与 §14.4;README_先读我.MD 第二节 |
+| NFR-12 | 收资清单按场裁剪:以通用收资模板为底,按本场的机型与数据源形态与专题范围裁剪,逐条写明必须、建议、可选或可替代以及「不收会怎样」 | 每一族都有必须性分级与缺件后果且可复核;换场时清单随场定义与机型变化,不是照抄样本场 | 本文第 11 章;《数据要求说明_观澜_2.10.1.docx》第 8 章与第 12 章 |
 
 ### 6.2 性能与耗时实测
 
 性能需求不能只写「要快」,要写「在哪台机器上、什么规模、花了多久」。下表把已取证的耗时集中列出,未取证的项如实标注。
 
-表 6-3 耗时实测汇总(来源见「证据」列;未标日期者为 HISTORY 或文档中的既有记录)
+表 6-3 耗时实测汇总(来源见「证据」列;未标日期者为 HISTORY 或文档中的既有记录,样本场实测(2026-09-22))
 
 | 环节 | 耗时 | 证据 |
 |---|---|---|
@@ -384,8 +389,8 @@
 | SCADA 侧十个构建器(逐台读 10min) | 约 15 分钟 | scripts/rebuild_all.py 步说明 |
 | 振动摄入(索引与谱) | 约 14 分钟;153 GB 量级导出约 25 分钟 | docs/系统设计说明.md §13.2;docs/输入数据放置指导_v0.1.md §4.1 |
 | 启动到门户就绪 | 约 7 秒内健康自检就绪;批处理入口约 2.3 秒返回 | docs/系统设计说明.md §4.2 |
-| 安装(Windows 离线) | 约 10 分钟量级;本机记录 172 秒 | docs/说明书_观澜如东样板v2_v0.2.md §3.1;docs/系统设计说明.md §4.2 |
-| 本机问答 | 显存不足时首答 30 至 60 秒;复杂题在升档下 5 至 15 分钟 | docs/说明书_观澜如东样板v2_v0.2.md §2 与 §11 |
+| 安装(Windows 离线) | 约 10 分钟量级;本机记录 172 秒 | docs/说明书_观澜…v0.2.md §3.1;docs/系统设计说明.md §4.2 |
+| 本机问答 | 显存不足时首答 30 至 60 秒;复杂题在升档下 5 至 15 分钟 | docs/说明书_观澜…v0.2.md §2 与 §11 |
 
 ### 6.3 可移植与零配置
 
@@ -397,11 +402,11 @@
 
 单一真源的范围在本版已经覆盖五项:版本号、路径、配置、日志、界面文案。每一项都配了「改一处、机器替你查其余几处」的守卫,例如版本号改动后由版本记录一致性门与打包器读真源保证同步,配置改动由配置审计器检查目录约定、命名、内容与引用闭合,日志改动由日志审计器检查唯一落点与行格式,界面文案改动由语言包检查保证前后端成对。
 
-表 6-4 单一真源与对应守卫
+表 6-4 单一真源与对应守卫(样本场实测(2026-09-22))
 
 | 真源 | 管什么 | 守卫与实跑结果 |
 |---|---|---|
-| src/version.py | 名称、版本、版本规则、版本史、包名 | 版本记录一致性检查;check 实跑报 v2.10.0 且记录表与代码一致 |
+| src/version.py | 名称、版本、版本规则、版本史、包名 | 版本记录一致性检查;check 实跑报 v2.10.1 且记录表与代码一致 |
 | src/paths.py | 一切路径解析的基准与助手 | 配置审计器检查不手拼路径;实跑无不一致 |
 | configs 目录与登记表 | 端口、模型档、场配置、页面归口、配置登记 | 配置审计实跑已知缺口与白名单 14 条、提示 7 条 |
 | src/logfile.py | 日志目录、命名、行格式、保留策略 | 日志审计实跑无不一致、提示 87 条 |
@@ -412,9 +417,9 @@
 
 ### 7.1 数据族与功能映射
 
-系统的数据需求可以概括成一句话:**七类现场源件加一类共享机理资料,喂出九个产物仓,页面只读产物**。每个源类目录名就是摄入接口,改名等于换接口。数据族与功能的对应关系如下表;逐类的字段、单位、必须性与质量要求见同批交付的《数据要求说明_观澜_2.10.0.docx》。
+系统的数据需求可以概括成一句话:**七类现场源件加一类共享机理资料,喂出九个产物仓,页面只读产物**。每个源类目录名就是摄入接口,改名等于换接口。数据族与功能的对应关系如下表;逐类的字段、单位、必须性与质量要求见同批交付的《数据要求说明_观澜_2.10.1.docx》。
 
-表 7-1 数据族到功能的映射
+表 7-1 数据族到功能的映射(样本场实测(2026-09-22))
 
 | 源族(约定落位) | 谁消费 | 喂出什么功能 | 进链步骤 | 现状 |
 |---|---|---|---|---|
@@ -426,16 +431,16 @@
 | scada_mdb | 取数层(CSV 缺失时回落) | 同上(年度归档库) | ③ 与 ④ 与 ④c | 100 件 / 48.25 GB,其中消费 76 件、缺口 24 件 |
 | windcms | 振动摄入与报告转录 | 振动窗索引、谱库、CMS 报告、逐台页、在升闭环、三层基线 | ④b 与 ④d 与 ④e | 25,693 件 / 161.22 GB,其中解码导出 25,679 件 |
 | m5_cms_tcm | 融合面与报告侧 | 现场正本优先的 handoff 与部件历史、厂家报告转录 | ④b 振动摄入 | 1 件 / 21.8 MB;正本缺失时由观澜自算件顶上 |
-| 西门子4.0技术资料(与场站并列的共享目录) | 本体层 | 码表、手册、工单展开、失效树、实机参数、检索索引 | ⑦ 本体层 | 318 件 / 2.94 GB,结构合规 |
+| 主机制造商(OEM)技术资料(与场站并列的共享目录) | 本体层 | 码表、手册、工单展开、失效树、实机参数、检索索引 | ⑦ 本体层 | 318 件 / 2.94 GB,结构合规 |
 
 ### 7.2 体量与现状
 
-表 7-2 数据与产物现状(源族数字取自 2026-09-21 的扫描快照,合计取自 2026-09-22 实测计数)
+表 7-2 数据与产物现状(源族数字取自 2026-09-21 的扫描快照,合计取自 2026-09-22 实测计数,样本场实测(2026-09-22))
 
 | 项 | 数量 | 体积 | 备注 |
 |---|---|---|---|
-| 输入源件(data/raw/如东,递归) | 26,504 件 | 224.67 GB | 同一棵树在 guanlan.py check 里报 26,503 件,两者相差 1 件,计数口径差异未取证 |
-| 产物(outputs/rudong,递归) | 3,549 件 | 7,157.5 MB | 含仓级台账 3 件;反向呼应审计报成立 3,548 件 |
+| 输入源件(data/raw/<场站>,递归) | 26,504 件 | 224.67 GB | 同一棵树在 guanlan.py check 里报 26,503 件,两者相差 1 件,计数口径差异未取证 |
+| 产物(outputs/<场>,递归) | 3,549 件 | 7,157.5 MB | 含仓级台账 3 件;反向呼应审计报成立 3,548 件 |
 | 其中振动窗与谱仓 | 3,417 件 | 占比最大 | 谱分片与窗级分析件 |
 | 其中标准仓 | 57 件 | — | 含窄仓子目录与总览单页 |
 | 未归类件 | 1 件 | — | 场站目录下的一份按月提取核对件,按设计不作为摄入源 |
@@ -445,7 +450,7 @@
 
 ### 7.3 与数据要求说明的分工
 
-本文只回答「要哪些数据、这些数据支撑什么功能」。数据的字段级要求(核心测点的名称、单位、必须性、缺失替代、对齐规则、质量门与核对锚点)以及面向现场的收资清单,写在《数据要求说明_观澜_2.10.0.docx》里。该文档按用户令要求与现场收资文件逐条对照,并对现场收资层面的已知缺失逐条如实记录,例如测风塔数据为零交付、故障录波只有 4 台、振动侧 handoff 正本缺失由观澜自算件顶上、远端机器未安装本机模型等。
+本文只回答「要哪些数据、这些数据支撑什么功能」。数据的字段级要求(核心测点的名称、单位、必须性、缺失替代、对齐规则、质量门与核对锚点)以及面向现场的收资清单,写在《数据要求说明_观澜_2.10.1.docx》里。该文档按用户令要求与现场收资文件逐条对照,并对现场收资层面的已知缺失逐条如实记录,例如测风塔数据为零交付、故障录波只有 4 台、振动侧 handoff 正本缺失由观澜自算件顶上、远端机器未安装本机模型等。
 
 ## 8 页面与信息架构需求
 
@@ -467,7 +472,7 @@
 
 表 8-2 按登记表逐条列出页面与子页的归口结论。登记表顶层条目 25 条,含 7 条子页共 32 条;归口审计实跑输出为「检查 21 项:不一致 0、已知缺口 19」,其中 21 项是审计的**检查行数**(19 条为引用悬空或溯源缺失的已知缺口项,2 条为两个数据派生页面的溯源检查项,均一致),与登记表的条目数不是同一个口径,两者都不矛盾,此处一并写明以免混淆。
 
-表 8-2 门户页面与子页归口表(25 条顶层条目加 7 条子页)
+表 8-2 门户页面与子页归口表(25 条顶层条目加 7 条子页,样本场实测(2026-09-22))
 
 | 页面或子页 | 类型 | 随输入数据变 | 判定依据摘要 |
 |---|---|---|---|
@@ -488,7 +493,7 @@
 | documents 交付文档 | 受管静态 | 否 | 外壳段约 5,690 字节为导航页,一个内嵌指向网关下发的治理清单页,段内不含数据快照 |
 | 子页 治理清单交付包 | 冻结交付件 | 否 | 文件名自带版本号与日期,客户交付件按交付版本冻结 |
 | 子页 全场状态一览(脱敏) | 冻结交付件 | 否 | 性质是数据派生但无指纹无生成端,按用户裁决按冻结交付件管并把会变旧的风险如实记账 |
-| 子页 如东取数单 | 冻结交付件 | 否 | 只有日期没有指纹,无生成端,且它引用的两棵产物树不在包内;保留不删并记账 |
+| 子页 场站取数单 | 冻结交付件 | 否 | 只有日期没有指纹,无生成端,且它引用的两棵产物树不在包内;保留不删并记账 |
 | page_product_cms CMS 报告页与件 | 数据派生 | 是 | 振动服务读的就是产物页面件;已在产物台账中登记并有溯源 |
 | page_product_vib_raw_manifest 振动摄入清单 | 数据派生 | 是 | 每次振动摄入重写,记件数、跨度与未随包缺口;已在台账中登记 |
 | embed_replay_chain 传动链回放(内嵌) | 数据派生 | 是 | 内嵌数据带来源与指纹,源产物在本包不在位,故现只能按生成时间判新旧 |
@@ -520,14 +525,14 @@
 
 系统的验收分两层:**装机自检**看「这台机器能不能用」,**重算链验收**看「这一轮数据算得对不对」。前者是 guanlan.py check 的四十余条判定行,后者是重建命令每一环的退出码与容忍口径。两层的关系是:自检不通过就不该跑重算(环境或依赖有问题),重算链跑完再由自检复核产物与台账。
 
-表 9-1 验收门分类与数量(依据 guanlan.py check 实跑输出,2026-09-22,本机)
+表 9-1 验收门分类与数量(依据 guanlan.py check 实跑输出,2026-09-22,本机,样本场实测(2026-09-22))
 
 | 分类 | 判定条数 | 代表门 | 实跑结果 |
 |---|---|---|---|
 | 运行环境与依赖 | 10 | Python 版本不低于 3.11(实测 3.12.10);九个第三方依赖逐个导入 | 全 OK |
 | 静态质量门 | 4 | 源码可编译 229 个文件;语言包 845 条成对;入口引用闭合 48 条;入口脚本编码守则 | 全 OK |
 | 子进程口径 | 3 | 捕获输出可用;无窗口启动输出进日志;无窗口位已设,标志位 0x8000200 | 全 OK |
-| 版本与卸载入口 | 2 | 版本管理与记录表一致(v2.10.0,包名 app_guanlang_v2.10.0.zip);卸载入口两个都在位 | 全 OK |
+| 版本与卸载入口 | 2 | 版本管理与记录表一致(v2.10.1,包名 app_guanlang_v2.10.1.zip);卸载入口两个都在位 | 全 OK |
 | 制品与台账审计 | 5 | 反向呼应 3,548 件成立;页面归口检查 21 项;配置统一;日志统一;输入数据放置体检 26,503 件结构合规 | 全 OK |
 | 产物与发布件在位 | 10 | 标准仓、本体对象库、findings、事实契约、门户、仿真合页服务与资料包、三维资产、仿真回放资产、治理清单交付件 | 全 OK |
 | 原始件目录 | 1 | 原始件目录存在(无数据时此项不影响页面) | 全 OK |
@@ -542,7 +547,7 @@
 
 重算链默认 26 步,加跳过 SCADA 为 23 步(另有第 ① 步仅在给了现场包目录时才跑)。每一步都是一个验收门:非零退出即停下并打印该步说明,容忍清单里的退出码不算失败,但会在汇总行里如实标出退出码。这个设计的关键在于「容忍不等于通过」:例如月度派生件找不到基线时返回 5,含义是「跳过了等价验收」,而不是「验收通过」。
 
-表 9-2 重算链逐步验收门(默认 26 步)
+表 9-2 重算链逐步验收门(默认 26 步,样本场实测(2026-09-22))
 
 | 序 | 步骤 | 主要脚本或命令 | 容忍退出码 | 非零的含义 |
 |---|---|---|---|---|
@@ -599,7 +604,7 @@
 
 交付质量有两条独立的核验路径:打包后的**开箱验证**(在临时副本上真解压、真安装、真起服务、真核页面)与**卸载核验**(在刚装好的副本上真跑卸载并断言拆除范围与保留范围)。两条路径的意义是:它们都作用在副本上,不打扰正在运行的实例。
 
-表 9-4 开箱验证与卸载核验的项与判据
+表 9-4 开箱验证与卸载核验的项与判据(样本场实测(2026-09-22))
 
 | 核验路径 | 核验项 | 判据 | 实测记录 |
 |---|---|---|---|
@@ -622,7 +627,7 @@
 
 需求的可信度一半来自「写清了不做什么」。本系统的边界不多但很硬:它是**只读分析系统**,不是控制系统,也不是云端服务。
 
-表 10-1 约束、假设与明确不做
+表 10-1 约束、假设与明确不做(样本场实测(2026-09-22))
 
 | 类别 | 条目 | 说明与依据 |
 |---|---|---|
@@ -641,13 +646,148 @@
 | 不做 | 不做知识图谱的自动推断 | 本体层零判级权,只转录一层正本;同部件多源判级并存不合并 |
 | 不做 | 不做实时数据流接入 | 数据以批的方式落位、体检、重算;服务侧看门狗只做到发现变化并列出该跑的步骤,不做流式处理 |
 | 不做 | 不为「无生成端」的件做运行期补齐 | 运行期一律不从交付包补齐;缺件要么放原始件重算,要么由研发补生成端 |
-| 不做 | 不做多场切换的完整支持 | 场配置机制留有接口,但当前只有内置场可用,多场机制尚未接通 |
+| 不做 | 本版不承诺「多场并存运行」的完整交付 | 场配置机制与场定义装载已接通,但本版端到端跑通并留下实测记录的只有样本风电场;新场接入按第 11 章的多风电场适用性需求与换场验收检查表执行,不把「配置里放一个场文件」当作换场完成 |
+
+## 11 多风电场适用性需求
+
+### 11.1 适用性目标
+
+本章回答一个问题:**换一座风电场,要改什么、不要改什么**。目标是同一套系统(同一份代码、同一个交付包)与同一份需求文档,在**不改代码**的前提下换场运行;换场时只改两类东西——**配置**与**数据**。凡是必须改代码才能换场的,都视为不满足本章要求,回到第 5 章走需求变更。本章的功能需求编号接着原有编号往下排,为 FR-44 至 FR-52;非功能需求为 NFR-12。
+
+适用性目标分三层写清。**入口不换**:安装、落位、重算、页面与验收的入口和流程与样本场完全一致,现场人员不需要学第二套操作。**口径不散**:场名、机组数与机组清单、机型参数、原始件路径、判据阈值与界面单位都从配置取,不在代码里写死;一旦写死,多场并存时就会静默串场。**缺件不造**:新场某一族数据没到位时,系统如实标注缺件并按缺族降级,绝不拿别的场的数字顶替,也不把样本场的阈值当成新场的阈值。
+
+本章有两个词要先定义。「本场」指**当前被选中的场**,由配置选定,不必然等于本文取数的样本风电场;「换场」指从一场切到另一场运行的整套动作(改配置、放数据、重跑、按检查表复验),不是把两场的数据混在一棵树里。本章沿用的占位符与附录 A 一致:`<场>`、`<场站>`、`<机型>` 按实际风电场替换。
+
+本章需求与《数据要求说明_观澜_2.10.1.docx》《设计说明_观澜_2.10.1.docx》配套使用:本章讲「换场要满足什么」,数据要求说明讲「要向新场收哪些数据、哪些可以裁剪」,《设计说明》讲「配置在哪一层生效、哪些参数属于场相关层」。
+
+取证边界要如实写一句:FR-44 至 FR-51 与 NFR-12 有既有的场配置层、摄入层与场级字段语义登记作依据;FR-52 的跨场横向对比是本次新增的口径要求,其实跑记录与机器守卫随后续版本补齐——本文不谎称已经通过。
+
+### 11.2 场配置化需求
+
+场相关的一切取值都落在场定义文件 `configs/farms/<场>.yaml` 里,它是场相关事实的**唯一真源**:代码只读它,不在代码里写场名、机组数或原始件路径。当前场由环境变量 `WINDSCADA_FARM=<场>` 或程序内的 set_current() 选择,命令行脚本与服务走同一条路。
+
+表 11-1 场配置化需求(FR-44 至 FR-46)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-44 | 场定义必须有七个必填键:`name`、`n_turbines`、`turbines`、`src_10min`、`src_alarm`、`store`、`rated_kw`;缺任何一个都在装载时响亮报出缺少的键名,绝不带病进入分析 | 场定义文件 `configs/farms/<场>.yaml` | 通过校验的场对象 | 七个必填键逐个在位;人为删去任一键时启动即报错并打印所缺键名 | src/windscada/config.py 的 REQUIRED 与 _expand() |
+| FR-45 | 当前场由 `WINDSCADA_FARM=<场>` 或 set_current() 选择,切换只改选择不改代码;以 `configs/farms/<场>.yaml` 为准(历史 json 兼容);选到未定义的场,或选到「文件存在但不是场定义」的机型与物理约束 profile 时,必须报未知场并列出可用场 | 环境变量或选择调用、场定义文件 | 当前场对象与其派生的原始件根、标准仓与契约路径 | 换到另一份场定义后,重算与页面取到的是新场的路径与机组数,没有一处残留上一场的取值(不串场) | src/windscada/config.py 的 farm() 与 set_current() 与 available() |
+| FR-46 | 场站目录按规则扫描辨识而不写死:场定义里的场站名与目录名完全相同者优先,其次是场名写法互为子串,再次是单站唯一性并标注「凭唯一性」;多个目录且都不匹配时如实报未识别并列出扫到的目录,不猜 | `data/raw` 下一级目录、场定义 | 辨识结论与依据、本场原始件根 | 四条规则的依据逐条可打印;未识别时页面显示无数据并给出约定位置而不是空路径 | src/windscada/config.py 的 station_scan() 与 station_report();FR-37 |
+
+### 11.3 机组与机型可替换
 
-## 11 需求跟踪矩阵
+机组数、台号、机型与主机制造商(OEM)的差异不进入引擎:引擎按场对象给出的机组清单与额定容量工作,机型差异通过**机型—场站数据契约** `configs/contracts/<机型>_<场>.yaml` 落到列名、单位与阈值上。换机型等于换契约,不改引擎。
 
-跟踪矩阵把 43 条功能需求接到实现、页面或产物、验收门三列上。用它的方式很简单:改一条需求,先在矩阵里找到它对应的实现与验收门,改完之后按验收门那一列重跑对应检查;如果某一行的验收门是「人工核」,说明这条目前没有机器守卫,评审时要特别留意。
+表 11-2 机组与机型替换需求(FR-47)
 
-表 11-1 需求跟踪矩阵
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-47 | 机组清单、机组数、额定容量与机型参数一律取自场定义与机型契约;`turbines` 同时支持逐台列举与「前缀加台号区间」两种写法,且 `n_turbines` 与 `turbines` 必须自洽;机型的拓扑差异(例如有无齿轮箱)只体现在契约与阈值层,不产生引擎分支 | 场定义的 `turbines`、`n_turbines`、`rated_kw`;`configs/contracts/<机型>_<场>.yaml` | 逐台循环范围、列名与单位映射、额定容量 | 换一个机组数不同的场后,矩阵行数与台账件数随 `n_turbines` 变化;同一份判据代码在有机型差异的两场上都能跑完 | src/windscada/config.py 的 _expand();configs/farms/<场>.yaml;configs/contracts/<机型>_<场>.yaml |
+
+### 11.4 数据源形态可适配与缺族降级
+
+现场给什么形态,系统都得能接:同一族数据可能以 CSV 转发层、现场月度类目归档库,或其它主机制造商(OEM)的导出形态到场。形态差异由取数层吸收,判据、报告与页面不变。缺族时的口径与「能接」同等重要:如实标注、按缺族降级,不造数。
+
+表 11-3 数据源形态适配与缺族降级需求(FR-48 至 FR-49)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-48 | 数据源形态可适配:同一族数据以 CSV 转发层、现场月度类目归档库(需先落位成同台补充件)或其它主机制造商(OEM)导出形态到场时,取数层按族统一入口读取,字段语义按本场登记对齐,形态差异不下传到判据、报告与页面 | 各族源件与同台补充件、类目归档库、场级字段语义登记 | 统一取数结果与合并后的序列 | 同台多件按列名对齐并按时间戳去重(主件优先),合并行数与重叠行数如实打印;类目库按信息级列出且不计变化;字段语义与登记不一致时如实标注,不照名字硬用 | FR-38;configs/farms/<场>/field_semantic_registry.yaml;docs/输入数据放置指导_v0.1.md §4.1 |
+| FR-49 | 缺族降级口径:某族缺件或缺列时,该族消费者按缺件如实标注并降级,不造数、不用他场数字顶替、不把「认不出」写成「无异常」;缺件清单与补齐办法在页面上可读到 | 各族在位情况、场定义 | 缺件标注、降级后的产物与页面提示 | 每一族的缺失都对应一句可复核的缺件说明;降级不改变其它族的结论;清空产物后页面仍可打开并说清缺件与补齐办法 | FR-31 与 NFR-06;docs/系统设计说明.md §11.4 |
+
+### 11.5 判据与阈值按场标定
+
+同一套判据结构,参数必须按场标定:**结构不变、参数随场变**。引擎与判据的层级、判级四轴与七镜头的算法、口径的写法都不因场而异,变的只是参数值。
+
+至少要按场标定的有六类:判级阈值(偏航与变桨的 P95 告警线、绕组与轴承温度的预警与告警档、健康分档线);额定功率与切入切出风速;时间窗锚点(对照基线与健康期分段的起止);限电窗(可作为干净判别时间窗的起止区间);温度档带宽(对照基线与正常带);机组数与额定容量。标定值落场定义或其引用的机型与物理约束件,不落代码。
+
+参数整组省略是合法写法而不是缺件:直驱机型没有齿轮箱,齿轮箱温度档整组省略即不判该轴;省略要在页面与依据里写明「本场无此判据」,不取一套默认阈值继续判。
+
+表 11-4 判据与阈值按场标定需求(FR-50)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-50 | 阈值、时间窗锚点、限电窗、温度档带宽等场相关参数一律按场标定并可配:参数落场定义或其引用的机型与物理约束件,同一份判据代码用不同参数跑不同场;本场用不到的参数可以整组省略,省略即不判该轴而不是取默认值硬判 | 场定义的参数段、机型与物理约束件 | 本场生效的阈值集合与判据结果 | 同一台同一系统在两场不同阈值下可以得出不同判级且各自可复算;整组省略的参数在页面与依据里写明「本场无此判据」 | configs/farms/<场>.yaml 的参数段与机型契约;docs/系统设计说明.md §16.2 |
+
+### 11.6 术语、单位与语言可配
+
+术语与单位不写在页面里,随场取:部件与系统名的写法、功率与温度与转速与风速的单位、中英界面文案三样都要可配;同一字段在不同主机制造商(OEM)的导出里叫法不同时,按本场的字段语义登记对齐,不按名字猜。写英文简写时一律写成「中文(英文简写)」,英文映射的键与中文成对;某条术语或单位在本场没有对应写法时,页面回落中文并如实标注未配置,不显示空白,也不沿用上一场的写法。
+
+表 11-5 术语、单位与语言可配需求(FR-51)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-51 | 术语、单位与界面语言按场可配:部件与系统名的写法、功率与温度与转速与风速的单位、中英界面文案从配置取;字段语义按本场登记对齐;英文简写一律写成「中文(英文简写)」且中英键成对;缺映射时回落中文并标注 | 场级字段语义登记、机型契约里的单位写法、语言包 | 页面与报告里的术语、单位与文案 | 语言包前后端成对(样本场实测(2026-09-22)为 845 条成对);换场后单位随配置变化且报告与页面一致;缺映射不显示空白 | src/windscada/lang.py;configs/farms/<场>/field_semantic_registry.yaml;NFR-04 |
+
+### 11.7 跨场统一口径与横向对比
+
+多场并用时,最容易被误读的是「同一个指标在不同场之间能不能直接比」。本文的口径是:**先同口径,再横向比**。同口径的前提有四项:同一套判据结构与版本、同一套单位与术语写法、同一套时间窗锚点与样本量判据、同一版本文档与产物口径。四项都成立时,跨场对比只在**同一指标、同一时间窗、同一聚合层级**上做,并同时给出各场样本量;任一项不成立时,该行标注「口径不可比」并写明差在哪一项,不给可被误读的数字。跨场比的是场与场之间的差异;机组绝对值的对照要在场内做(机群基线与绝对限)。
+
+表 11-6 跨场统一口径与横向对比需求(FR-52)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-52 | 多场结果按同一判据结构、同一单位与术语、同一时间窗锚点与同一聚合层级输出,支持同口径横向对比;口径不一致时必须标注「口径不可比」并写明差在哪一项,不给可被误读的数字 | 各场同口径产物、场定义、版本真源 | 跨场对比表与其口径说明 | 对比表逐行写出指标、时间窗、聚合层级与各场样本量;任一口径项不一致时该行标注不可比而不是照常出数 | 第 5 章各条判据;NFR-10 |
+
+### 11.8 按场裁剪的收资清单
+
+收资不是「有多少收多少」,而是**按场裁剪**:先按通用收资模板判定每一族是必须、建议、可选还是可替代,再按本场的机型拓扑、数据源形态与专题范围裁掉用不上的条目(例如直驱机型不收齿轮箱相关件,不做振动专题的场把振动侧整族列为可选)。裁剪后的清单要写清「不收会怎样」,并作为换场验收的数据侧判据。
+
+表 11-7 按场裁剪的收资清单需求(NFR-12)
+
+| 编号 | 需求 | 判据 | 依据文件 |
+|---|---|---|---|
+| NFR-12 | 收资清单按场裁剪:以通用收资模板为底,按本场的机型、数据源形态与专题范围裁剪,逐条写明必须、建议、可选或可替代以及「不收会怎样」 | 每一族都有必须性分级与缺件后果且可复核;换场时清单随场定义与机型变化,不是照抄样本场 | 《数据要求说明_观澜_2.10.1.docx》第 8 章与第 12 章;docs/输入数据放置指导_v0.1.md |
+
+### 11.9 换场验收需求
+
+换场不等于「放完数据能打开页面」,要按固定检查表逐项复验。检查表分五类:**配置**(场定义本身对不对)、**数据**(源件在位且可解析)、**产物**(重算是否真跑出来)、**页面**(换场后能不能用)、**门禁**(机器守卫是否全绿)。每一类的通过口径如表 11-8 所示;任一项不通过即视为换场未完成,不允许「先上线再补验」。
+
+表 11-8 换场验收检查表(配置与数据与产物与页面与门禁)
+
+| 类别 | 检查项 | 通过口径 | 对应需求 |
+|---|---|---|---|
+| 配置 | 场定义七个必填键齐全且自洽 | 装载无报错;`n_turbines` 与 `turbines` 的台数一致;`rated_kw` 与本场容量一致 | FR-44 |
+| 配置 | 选择当前场的方式生效 | `WINDSCADA_FARM=<场>` 或 set_current() 选定后,取到的路径与机组数都属本场,无上一场残留 | FR-45 |
+| 配置 | 场站目录辨识有依据 | 辨识结论与依据可打印;未识别时指向约定位置而不是空路径 | FR-46 |
+| 配置 | 机型契约按机型就位 | `configs/contracts/<机型>_<场>.yaml` 在位,列名与单位可加载 | FR-47 |
+| 配置 | 判据参数按场标定 | 阈值、时间窗锚点、限电窗、温度档带宽逐项有值,或有「本场不判该轴」的明确省略记录 | FR-50 |
+| 数据 | 各族源件按约定目录与命名就位 | 体检结论为结构合规;冲突默认拦下;同尺寸件可反复跳过 | FR-36 |
+| 数据 | 源件形态能被取数层读到 | 各族消费者实跑到数;同台多件合并行数与重叠行数如实打印 | FR-48 |
+| 数据 | 缺族如实记账 | 每一族缺失都有缺件说明与补齐办法且页面可读到;无造数 | FR-49 |
+| 产物 | 一条命令重算全链走通 | 重算按默认步数跑完,每步退出码在容忍口径内或明确报出,整轮退出码 0 | FR-29 |
+| 产物 | 产物件数与输入跨度对得上 | 核对锚点逐项达标;台账等价验收通过,或如实标注「没做验收」 | FR-12 与 FR-21 |
+| 产物 | 逐件有来路 | 反向呼应审计无未归类件 | NFR-05 |
+| 页面 | 门户与各服务按本场数据可用 | 门户、工作台、振动、运维控制台与健康自检五个入口中可用数不少于 4 个;无产物时返回结构化缺件说明 | NFR-06 与 FR-43 |
+| 页面 | 页面归口与陈旧检测通过 | 归口审计无不一致;本场数据派生页面无陈旧项(陈旧检测退出码 7 为 0 项) | FR-34 |
+| 页面 | 术语、单位与语言随场生效 | 页面与报告的单位、术语、语言与配置一致;未配置项回落中文并标注 | FR-51 |
+| 门禁 | 装机自检全绿 | guanlan.py check 无失败项,退出码 0 | NFR-07 与 FR-41 |
+| 门禁 | 版本与文档一致 | 版本记录一致性检查通过;安装记录版本与本包版本一致 | FR-41 与 FR-42 |
+| 门禁 | 对外口径与去标识化 | 交付文档对样本场标识 0 命中;描述口径(时间窗、影响机组、中文(英文简写))机器可查 | NFR-04 |
+
+换场要动的配置与数据,逐条对应到需求、配置项与验收方式,如表 11-9 所示。这张表是换场作业单的底稿:第一列是需求,第二列是要改或要核对的东西,后两列是凭什么算通过、换场时具体做什么。
+
+表 11-9 多风电场适用性需求与配置项与验收对照
+
+| 需求 | 配置项或真源 | 验收方式 | 换场时的动作 |
+|---|---|---|---|
+| FR-44 | `configs/farms/<场>.yaml` 的 `name`、`n_turbines`、`turbines`、`src_10min`、`src_alarm`、`store`、`rated_kw` | 装载报错清单为空;台数与机组清单一致 | 新建或改写场定义并填齐七个必填键 |
+| FR-45 | `WINDSCADA_FARM=<场>` 或 set_current();`configs/farms/<场>.yaml` | 换场后路径与机组数无残留(不串场) | 设定场选择并确认取到本场路径 |
+| FR-46 | `data/raw/<场站>` 的目录名与场名写法 | 辨识依据逐条打印;未识别时报出扫到的目录 | 按约定改名或补场名写法 |
+| FR-47 | `turbines`、`n_turbines`、`rated_kw`;`configs/contracts/<机型>_<场>.yaml` | 机组数变化则矩阵行数与台账件数随之变化 | 换机型契约,台号按实际替换 |
+| FR-48 | 取数层的族入口;类目归档库与同台补充件;场级字段语义登记 | 合并行数与重叠行数打印;类目库不计变化 | 按现场形态落位并体检 |
+| FR-49 | 各族在位情况与缺件标注 | 每一族缺失都有可复核的缺件说明;无造数 | 能补的补件,补不了的如实记账 |
+| FR-50 | 场定义与机型契约里的参数段 | 同系统在两场不同阈值下判级可各自复算;省略项写明不判该轴 | 按新场标定阈值、时间窗锚点、限电窗与温度档带宽 |
+| FR-51 | 场级字段语义登记、单位写法、语言包 | 页面与报告单位一致;语言包成对;缺映射回落中文 | 配置本场的术语与单位写法 |
+| FR-52 | 各场同口径产物 | 对比表逐行写口径与样本量;不一致标不可比 | 口径项齐备后再出对比 |
+| NFR-12 | 通用收资模板加本场裁剪清单 | 逐族必须性分级与缺件后果可复核 | 按本场机型与专题裁剪清单 |
+| 换场验收 | 表 11-8 检查表 | 配置、数据、产物、页面、门禁五类逐项通过 | 换场后逐项复验并留记录 |
+
+## 12 需求跟踪矩阵
+
+跟踪矩阵把 52 条功能需求接到实现、页面或产物、验收门三列上。用它的方式很简单:改一条需求,先在矩阵里找到它对应的实现与验收门,改完之后按验收门那一列重跑对应检查;如果某一行的验收门是「人工核」,说明这条目前没有机器守卫,评审时要特别留意。
+
+表 12-1 需求跟踪矩阵
 
 | 编号 | 实现(脚本或模块) | 页面或产物 | 验收门 |
 |---|---|---|---|
@@ -663,9 +803,9 @@
 | FR-10 | 月度派生件过滤 | 七张月度时序图 | 图注与按月取整口径 |
 | FR-11 | control.registry(span=) | 控制参数一致性卡片 | 按时间窗重算约 0.6 秒;封顶中位随窗变化 |
 | FR-12 | rebuild_from_raw.py --scada | 损失、功率曲线、停机事件 | 与基线逐值对拍;重算链 ③ 步 |
-| FR-13 | rudong_tcm_index.py;rudong_tcm_spectra.py;vib_raw_build.py | 振动窗索引、谱分片、摄入清单 | 振动窗 w0316 索引 2,066,686 行、谱 420,742 条 |
+| FR-13 | <场>_tcm_index.py;<场>_tcm_spectra.py;vib_raw_build.py | 振动窗索引、谱分片、摄入清单 | 振动窗 w0316 索引 2,066,686 行、谱 420,742 条 |
 | FR-14 | windcms.py report;report_std 与 report 构建器 | 报告 md 与 docx、逐台页、总览页 | 38 台判级齐;缺融合级写破折号 |
-| FR-15 | rudong_fusion_run.py;rudong_fusion_handoff.py;fusion 模块 | 融合表、handoff、全场标量 | 反向呼应审计按族逐件成立 |
+| FR-15 | <场>_fusion_run.py;<场>_fusion_handoff.py;fusion 模块 | 融合表、handoff、全场标量 | 反向呼应审计按族逐件成立 |
 | FR-16 | component_history_build.py;trend_ingest | 部件历史件、趋势证据对象 | 重算链 ④d 步;时间窗不足出空表并写边界 |
 | FR-17 | baseline_38_build.py | 三层基线件 | 重算链 ④e 步;样本不足如实空表 |
 | FR-18 | windscada_alarms_ingest.py | 报警台账 | 39,211 行;键集合并集去重 |
@@ -694,6 +834,15 @@
 | FR-41 | 安装脚本的版本检查步骤;src/version.py | 安装记录与提示 | 五类提示与动作;check 版本行一致 |
 | FR-42 | guanlan_uninstall.py;uninstall 入口两个 | 卸载结果 | 自检两类断言;实跑断言拆除与保留范围 |
 | FR-43 | pack_dist.py;pack 入口;开箱验证入口 | 交付包与清单文件 | 两道闸;包内不得有安装记录或快捷方式;页面可用不少于 4 个 |
+| FR-44 | src/windscada/config.py 的 REQUIRED 与 _expand() | 场定义装载与报错清单 | 七个必填键逐个在位;缺键即报并打印键名 |
+| FR-45 | src/windscada/config.py 的 farm() 与 set_current() 与 available() | 当前场对象与派生路径 | 换场后路径与机组数不串场 |
+| FR-46 | src/windscada/config.py 的 station_scan() 与 station_report() | 数据层维护页的辨识结论 | 辨识规则逐条打印;未识别时不猜 |
+| FR-47 | _expand();configs/contracts/<机型>_<场>.yaml | 机组清单、列名与单位映射 | 矩阵行数随 n_turbines 变化;两场同代码可跑完 |
+| FR-48 | 取数层族入口;configs/farms/<场>/field_semantic_registry.yaml | 合并取数结果与字段对齐 | 合并与重叠行数打印;类目库不计变化 |
+| FR-49 | 各族消费者的缺件分支;products_state.py | 缺件标注与降级产物 | 每族缺失有可复核缺件说明;无造数 |
+| FR-50 | 场定义与机型契约的参数段 | 本场阈值集合与判据结果 | 两场不同阈值各自可复算;省略项写明不判该轴 |
+| FR-51 | src/windscada/lang.py;场级字段语义登记;契约单位写法 | 页面与报告的术语与单位 | 语言包成对;单位随场变;缺映射回落中文 |
+| FR-52 | 各场同口径产物 | 跨场对比表与口径说明 | 逐行写口径与样本量;不一致标不可比 |
 
 ## 附录 A 编写依据
 
@@ -712,7 +861,7 @@
 | 版本管理与打包命名、单一真源与三处守卫 | docs/系统设计说明.md 第 15 章 |
 | 时间窗词表、随窗与不随窗、窄仓、等待契约 | docs/系统设计说明.md 第 16 章 |
 | 三条描述口径与服务端链接契约、依据不得为空 | docs/系统设计说明.md 第 17 章 |
-| 面向用户的功能与使用、系统要求、模型档、目录结构、常见故障、已知限制 | docs/说明书_观澜如东样板v2_v0.2.md |
+| 面向用户的功能与使用、系统要求、模型档、目录结构、常见故障、已知限制 | docs/说明书_观澜…v0.2.md |
 | 页面与子页的归口登记(类型、依据、已知缺口) | configs/portal_pages.yaml |
 | 服务端口与监听地址真源、看门狗开关 | configs/serve.json |
 | 模型档位与拉取命令 | configs/models.json |
@@ -727,6 +876,14 @@
 | 自检实跑输出(41 条判定行) | docs/src/_guanlan_check.txt 与本机 2026-09-22 实跑 |
 | 重算计划实跑输出(23 步) | docs/src/_rebuild_plan.txt |
 | 四张插图的数字来源 | docs/figures 下的四张需求分析插图与其生成器 scripts/delivery_docs_figures.py |
+| 场定义必填键、场选择与场站目录辨识 | src/windscada/config.py 的 REQUIRED 与 farm() 与 set_current() 与 station_scan() |
+| 场定义与机型与物理约束参数的写法样例 | configs/farms/<场>.yaml |
+| 机型—场站数据契约与字段单位真源 | configs/contracts/<机型>_<场>.yaml 与 configs/canonical/dictionary.yaml |
+| 场级字段语义登记与其它厂商导出形态的对齐 | configs/farms/<场>/field_semantic_registry.yaml |
+| 按场裁剪的收资清单与必须性分级 | docs/src/数据要求说明_观澜_2.10.1.md |
+| 场无关引擎与场相关参数的分层与换场作业单 | docs/src/设计说明_观澜_2.10.1.md |
+
+表中 `<场>`、`<场站>`、`<机型>` 为占位符,按实际风电场替换。
 
 ## 附录 B 术语表
 
@@ -763,3 +920,8 @@
 | 容忍口径 | tolerate | 重算链允许某步返回特定退出码而不打断整条链,且明确「容忍不等于通过」 | scripts/rebuild_all.py |
 | 核对锚点 | — | 重算后应达到的条数指标,用于快速判断数据是否真的进去了 | docs/输入数据放置指导_v0.1.md §4 |
 | 收资 | — | 向现场收集原始数据与资料的过程与其清单要求 | docs/现场收资接入_v0.1.md |
+| 场定义 | farm config | 一个场的最小事实集合:名称、机组数与机组清单、原始件路径、标准仓与额定容量;落 `configs/farms/<场>.yaml` | src/windscada/config.py 的 REQUIRED |
+| 换场 | — | 从一场切到另一场运行的整套动作:改配置、放数据、重跑、按检查表复验 | 本文 §11.9 |
+| 缺族降级 | — | 某族数据缺失时按缺件如实标注并降低该族功能,不造数、不用他场数字顶替 | 本文 FR-49 |
+| 按场标定 | — | 把阈值、时间窗锚点、限电窗、温度档带宽等场相关参数按本场取值 | 本文 FR-50 |
+| 通用收资模板 | — | 与场无关的收资条目底稿,按场裁剪后作为现场收资清单 | docs/src/数据要求说明_观澜_2.10.1.md |

binární
docs/数据要求说明_观澜_2.10.0.docx


binární
docs/数据要求说明_观澜_2.10.1.docx


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3 - 3
docs/版本记录.md


binární
docs/设计说明_观澜_2.10.0.docx → docs/设计说明_观澜_2.10.1.docx


binární
docs/需求分析_观澜_2.10.0.docx


binární
docs/需求分析_观澜_2.10.1.docx


+ 14 - 5
guanlan.py

@@ -257,7 +257,7 @@ def cmd_check(c):
             _ddb = _ilu4.module_from_spec(_sp4)
             _sp4.loader.exec_module(_ddb)
             _V2 = _ddb.VERSION
-            _miss, _stale, _figmiss = [], [], []
+            _miss, _stale, _figmiss, _leak = [], [], [], {}
             for _k, _spec in _ddb.DOCS.items():
                 _out = ROOT / _spec['out'].format(v=_V2)
                 _src = ROOT / _spec['src'].format(v=_V2)
@@ -268,15 +268,24 @@ def cmd_check(c):
                 if not _src.is_file():
                     _miss.append(_src.name)
                     continue
-                for _rel in re.findall(r'!\[[^\]]*\]\((figures/[^)]+)\)', _src.read_text(encoding='utf-8')):
+                _mdtxt = _src.read_text(encoding='utf-8')
+                for _w, _n in _ddb.forbidden_hits(_mdtxt).items():      # ★对外件去标识化硬门
+                    _leak[_w] = _leak.get(_w, 0) + _n
+                for _rel in re.findall(r'!\[[^\]]*\]\((figures/[^)]+)\)', _mdtxt):
                     if not (ROOT / "docs" / _rel).is_file():
                         _figmiss.append(_rel)
             _miss += _figmiss
-            _ok4 = not _miss
+            # ★图表器源码也要扫:图题是代码里的字符串,源码里带样本场字样就一定会画进图(对外件不许有)
+            _figsrc = ROOT / "scripts" / "delivery_docs_figures.py"
+            if _figsrc.is_file():
+                for _w, _n in _ddb.forbidden_hits(_figsrc.read_text(encoding='utf-8')).items():
+                    _leak['图表器:' + _w] = _leak.get('图表器:' + _w, 0) + _n
+            _ok4 = not _miss and not _leak
             row(f"交付文档三件 (需求分析/设计说明/数据要求说明 · v{_V2})", _ok4,
-                ("3/3 份在位, 文档名含当前版本号, 插图全在位" if _ok4 else
-                 f"缺 {len(_miss)} 件: {', '.join(_miss[:3])}"
+                ("3/3 份在位, 文档名含当前版本号, 插图全在位, 去标识化 clean" if _ok4 else
+                 (f"缺 {len(_miss)} 件: {', '.join(_miss[:3])}" if _miss else '')
                  + (f";同名旧版残留: {', '.join(_stale[:3])}" if _stale else '')
+                 + (f";去标识化泄漏 {_leak}(对外件不得出现样本场标识)" if _leak else '')
                  + " —— 跑 python scripts/delivery_docs_build.py 重建"))
     except Exception as _e:
         row("交付文档三件", False, f"{type(_e).__name__}: {_e}")

+ 80 - 11
scripts/delivery_docs_build.py

@@ -65,13 +65,22 @@ SZ = dict(cover=26, h1=16, h2=14, h3=12, body=12, table=10.5, table_small=9, cap
 
 DOCS = {
     'req': dict(src='docs/src/需求分析_观澜_{v}.md', out='docs/需求分析_观澜_{v}.docx',
-                title='需求分析', subtitle='观澜 · 如东样板 v2'),
+                title='需求分析', subtitle='观澜 v2 · 风电场智能分析系统'),
     'des': dict(src='docs/src/设计说明_观澜_{v}.md', out='docs/设计说明_观澜_{v}.docx',
-                title='设计说明', subtitle='观澜 · 如东样板 v2'),
+                title='设计说明', subtitle='观澜 v2 · 风电场智能分析系统'),
     'dat': dict(src='docs/src/数据要求说明_观澜_{v}.md', out='docs/数据要求说明_观澜_{v}.docx',
-                title='数据要求说明', subtitle='观澜 · 如东样板 v2(结合现场《数据分析收资要求-v3》)'),
+                title='数据要求说明',
+                subtitle='观澜 v2 · 风电场智能分析系统(结合现场《数据分析收资要求-v3》)'),
 }
 
+# ★去标识化词表(用户令 2026-09-22「内容参考如东风电场,但不体现如东风电场」):
+#   三份文档是**对外**交付件, 不得出现样本场的中文名、罗马化形态、业主、地域、OEM 与第三方机构名。
+#   词表放在代码里 ⇒ `--check`/`--verify` 与 `guanlan.py check` 都会机器扫描, 不靠人自觉。
+#   ★2026-09-22 补(数据要求说明起草时实逮): 样本场 CMS 导出包名是 `CMS_RuDong_CGN_202603-04`
+#   —— **大小写混合**的罗马化场名/业主缩写能绕过精确匹配,所以匹配一律**大小写不敏感**(见 forbidden_hits)。
+FORBIDDEN = ('如东', '如海', '广核', '江苏', 'rudong', 'cgn',
+             '西门子', '上海电气', 'swt', '大生', '4.0-130')
+
 SPEC_ROWS = [
     ('封面标题', '黑体', '小一(26pt)', '居中'),
     ('章标题(一级)', '黑体', '三号(16pt)', '左对齐,章前分页'),
@@ -358,6 +367,12 @@ def parse_md(text: str):
     return blocks
 
 
+def forbidden_hits(text: str) -> dict:
+    """扫去标识化词表 → {禁用词: 命中次数}(只回报非零项;**大小写不敏感**)。"""
+    low = text.lower()
+    return {w: low.count(w.lower()) for w in FORBIDDEN if low.count(w.lower())}
+
+
 def count_md(text: str) -> dict:
     blocks = parse_md(text)
     body = re.sub(r'!\[[^\]]*\]\([^)]*\)', '', text)
@@ -397,8 +412,22 @@ def toc(doc):
 
 
 def render_blocks(doc, blocks, fig_root: pathlib.Path):
+    """渲染块序列。表号一律**按章编号**「表 <章>-<序>」(附录按字母 A/B/C)——
+    与三份稿正文里的引用口径一致(实逮: 原先按出现顺序编成「表 1…表 54」, 正文写「见表 9-2」就指不到)。
+    """
     tno = 0
+    chap_label = ''
+    per = 0
+    caps: list[str] = []
     for kind, payload in blocks:
+        if kind == 'h1':
+            head = payload.strip()
+            m = re.match(r'^(\d+)', head)
+            if m:
+                chap_label, per = m.group(1), 0
+            else:
+                m2 = re.match(r'^附录\s*([A-Za-z])', head)
+                chap_label, per = (m2.group(1), 0) if m2 else (chap_label, 0)
         if kind.startswith('h'):
             lvl = int(kind[1])
             style = {1: 'Heading 1', 2: 'Heading 2', 3: 'Heading 3', 4: 'Heading 4'}[lvl]
@@ -422,8 +451,17 @@ def render_blocks(doc, blocks, fig_root: pathlib.Path):
         if kind == 'table':
             cap, rows = payload
             tno += 1
-            para(doc, cap or f'表 {tno}', size=SZ['caption'], ea=HEI, bold=True, align='center',
-                 space_before=8, space_after=4)
+            per += 1
+            label = f'表 {chap_label}-{per}' if chap_label else f'表 {tno}'
+            title = re.sub(r'^表\s*[\dA-Za-z]+\s*[-–—]\s*\d+\s*', '', cap or '').strip()
+            if cap:
+                author_no = re.match(r'^表\s*([\dA-Za-z]+)\s*[-–—]\s*(\d+)', cap)
+                if author_no and f'{author_no.group(1)}-{author_no.group(2)}' != f'{chap_label}-{per}':
+                    WARN.append('表号与作者自编不一致: 原「%s」→ 本器按章编号「%s」'
+                                % (cap[:24], label))
+            caps.append(label)
+            para(doc, (label + (' ' + title if title else '')), size=SZ['caption'], ea=HEI, bold=True,
+                 align='center', space_before=8, space_after=4)
             add_table(doc, rows)
             para(doc, '', space_after=2)
             continue
@@ -444,6 +482,7 @@ def render_blocks(doc, blocks, fig_root: pathlib.Path):
                 para(doc, f'(图缺失:{rel})', size=SZ['caption'], align='center', space_after=4)
             para(doc, cap or rel, size=SZ['caption'], ea=HEI, bold=True, align='center', space_after=8)
             continue
+    doc._tab_labels = caps                     # 供调用方自检(本器 --verify 直接从 docx 回读,不依赖它)
     return tno
 
 
@@ -451,7 +490,7 @@ def header_footer(doc, title: str):
     sec = doc.sections[0]
     hp = sec.header.paragraphs[0]
     hp.alignment = WD_ALIGN_PARAGRAPH.CENTER
-    set_run(hp.add_run(f'{title} · 观澜·如东样板 v2 · 版本 {VERSION}'), SZ['header'])
+    set_run(hp.add_run(f'{title} · 观澜 v2 风电场智能分析系统 · 版本 {VERSION}'), SZ['header'])
     fp = sec.footer.paragraphs[0]
     fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
     set_run(fp.add_run('第 '), SZ['header'])
@@ -527,11 +566,40 @@ def verify_docx(key: str) -> int:
     ok_field = ('TOC' in doc.element.xml) and ('PAGE' in foot) and ('NUMPAGES' in foot)
     # 只把"正文里残留的 markdown 标记"判为不合格;代码内的字面星号是内容本身(glob 通配),只报数。
     bad = [k for k, v in residual.items() if v and k != '代码内字面星号']
+    leak = forbidden_hits(allt + '\n' + head)          # ★对外件不得出现样本场标识
+    # ★表号断链检查(2026-09-22 实逮): 正文写「见表 9-2」,文档里就必须真有「表 9-2」。
+    cap_labels = set()
+    for p in doc.paragraphs:
+        mm = re.match(r'^表\s*([\dA-Za-z]+)\s*[-–—]\s*(\d+)', p.text.strip())
+        if mm:
+            cap_labels.add(f'{mm.group(1)}-{mm.group(2)}')
+
+    def _refs(text: str) -> set:
+        """正文里的「表 X-Y」引用。
+
+        ★两处实逮的误报都要挡:① `年-月`('报表 2026-09');② **跨单元格拼接** —— 把表格单元用换行
+        连起来后, "…如实空表" + "FR-18 至 FR-21" 会拼出 '表\\nFR-18',被当成表号。故分隔符只许空格/制表符。
+        """
+        out = set()
+        for a, b in re.findall(r'表[ \t]*([\dA-Za-z]+)[ \t]*[-–—][ \t]*(\d+)', text):
+            if re.fullmatch(r'(19|20)\d{2}', a):
+                continue
+            out.add(f'{a}-{b}')
+        return out
+
+    refs = _refs('\n'.join(
+        [p.text for p in doc.paragraphs
+         if not re.match(r'^(表|图)\s*[\dA-Za-z]+\s*[-–—]\s*\d+', p.text.strip())] + cells))
+    broken = sorted(refs - cap_labels)
+    noref = sorted(cap_labels - refs)
     print('%-4s %-38s 段落 %3d · 表 %2d · 图 %2d · 标题 %2d · 页眉 %r'
           % (key, out.name, len(doc.paragraphs), len(doc.tables), len(doc.inline_shapes),
              len(heads), head[:28]))
-    print('      域(目录/页码): %s · 残留标记: %s' % ('OK' if ok_field else '缺', bad or '无'))
-    return 0 if ok_field and not bad else 5
+    print('      域(目录/页码): %s · 残留标记: %s · 去标识化: %s · 表号: %s'
+          % ('OK' if ok_field else '缺', bad or '无',
+             'clean' if not leak else '泄漏 %s' % leak,
+             '断链 %s' % broken if broken else '引用闭合(表 %d 张,%d 张无引用)' % (len(cap_labels), len(noref))))
+    return 0 if ok_field and not bad and not leak and not broken else 5
 
 
 def main() -> int:
@@ -558,15 +626,16 @@ def main() -> int:
                 rc = 5
                 continue
             st = count_md(src.read_text(encoding='utf-8'))
-            miss = []
+            miss, leak = [], forbidden_hits(src.read_text(encoding='utf-8'))
             for _cap, rel in re.findall(r'!\[([^\]]*)\]\(([^)]+)\)', src.read_text(encoding='utf-8')):
                 f = ROOT / 'docs' / rel.split('figures/')[-1]
                 if not (ROOT / 'docs' / 'figures' / rel.split('figures/')[-1]).exists():
                     miss.append(rel)
-            print('%-4s 章 %2d · 节 %2d · 小节 %2d · 表 %2d · 图 %2d · 约 %5d 汉字%s'
+            print('%-4s 章 %2d · 节 %2d · 小节 %2d · 表 %2d · 图 %2d · 约 %5d 汉字 · 去标识化 %s%s'
                   % (k, st['章'], st['节'], st['小节'], st['表'], st['图'], st['汉字'],
+                     'clean' if not leak else '泄漏 %s' % leak,
                      '' if not miss else '  ✗ 缺图 %d: %s' % (len(miss), ', '.join(miss))))
-            if miss:
+            if miss or leak:
                 rc = 5
         return rc
     for k in ([a.only] if a.only else sorted(DOCS)):

+ 64 - 38
scripts/delivery_docs_figures.py

@@ -134,11 +134,11 @@ def serve_cfg():
 def raw_station_stats():
     """→ [(族, 件数, GB)],实测 data/raw/<场站>/ 下一层目录。
 
-    场站目录名以**场配置**为准(`windscada.config.farm()['raw_station']`),`P.station_dir()` 只是兜底
-    (它按场配置**名**拼,实测本场是 `data/raw/如东` 而非 `data/raw/rudong`)。
+    场站目录名以**场配置**为准(`windscada.config.farm()['raw_station']`),`P.station_dir()` 只是兜底。
+    ★对外文档口径(用户令 2026-09-22): 真实场站名只用于**取数**,不进图题 —— 图里一律写「样本场」。
     """
     from src.windscada.config import farm as _farm
-    home = P.RAW_ROOT / str(_farm().get('raw_station') or '如东')
+    home = P.RAW_ROOT / str(_farm().get('raw_station') or P.farm())
     out = []
     if not home.exists():
         WARN.append(f'data/raw 场站目录不在位: {home}')
@@ -171,11 +171,12 @@ def md_tables(path: pathlib.Path):
 
 
 def md_find_table(path: pathlib.Path, must_cols, first_cell=None):
-    """找**表头含指定列**(列名在本器里以首行单元格匹配)的表。"""
+    """找**表头含指定列**的表 → (表头单元格, 数据行)。列位置一律**按表头名定位**,不写死列号。"""
     lines = path.read_text(encoding='utf-8').splitlines() if path.exists() else []
     for i, l in enumerate(lines):
         if not l.startswith('|') or not all(c in l for c in must_cols):
             continue
+        head = [c.strip() for c in l.strip().strip('|').split('|')]
         rows, j = [], i + 2
         while j < len(lines) and lines[j].strip().startswith('|'):
             rows.append([c.strip() for c in lines[j].strip().strip('|').split('|')])
@@ -184,8 +185,17 @@ def md_find_table(path: pathlib.Path, must_cols, first_cell=None):
             continue
         if first_cell is not None and rows[0][0] != first_cell:
             continue
-        return rows
-    return []
+        return head, rows
+    return [], []
+
+
+def col_of(head, *names):
+    """按表头名取列号(取第一个命中的;找不到返回 None)。"""
+    for n in names:
+        for i, h in enumerate(head):
+            if n in h:
+                return i
+    return None
 
 
 def ensure_evidence(refresh=False):
@@ -208,7 +218,7 @@ def ensure_evidence(refresh=False):
 
 # ── 需求分析组 ─────────────────────────────────────────────────────────────────
 def fig_req_01():
-    hist, ver = version_history()
+    hist, _ver = version_history()
     fig, ax = plt.subplots(figsize=(9.2, 0.62 * len(hist) + 1.4))
     ax.axis('off')
     n = len(hist)
@@ -226,8 +236,7 @@ def fig_req_01():
                 fontsize=9.5, fontweight='bold', va='center')
         ax.text(1.05, y - 0.20, title, fontsize=9, color='#333333', va='center')
     ax.text(0.7, n - 0.15, '需求演进(自下而上)', fontsize=10, fontweight='bold', ha='center')
-    ax.set_title(f'观澜·如东样板 v2 需求演进时间轴(当前 v{ver};依据 src/version.py 的 HISTORY,'
-                 f'共 {n} 条)', fontsize=11)
+    ax.set_title(f'观澜 v2 需求演进时间轴(样本场;依据 src/version.py 的 HISTORY,共 {n} 条)', fontsize=11)
     save(fig, 'fig-req-01-需求演进时间轴.png')
 
 
@@ -241,7 +250,7 @@ def fig_req_02():
         box(ax, 3, y, 20, 16, name, fc='#e8f1e4', ec='#5b8c4a', fs=11, bold=True)
         box(ax, 30, y, 66, 16, pages, fc='#eef3fb', ec='#3b6ea5', fs=10)
         arrow(ax, (23.5, y + 8), (29.4, y + 8))
-    ax.text(50, 94, '角色 — 场景 — 页面关系(依据 docs/说明书_观澜如东样板v2_v0.2.md 与 configs/portal_pages.yaml)',
+    ax.text(50, 94, '角色 — 场景 — 页面关系(依据 系统使用说明书 与 configs/portal_pages.yaml)',
             ha='center', fontsize=11, fontweight='bold')
     save(fig, 'fig-req-02-角色场景页面.png')
 
@@ -268,7 +277,7 @@ def fig_req_03():
 
 def fig_req_04():
     """验收门分类 —— **与《需求分析》§9.1 表同源**(直接解析那张表,避免图文数字打架)。"""
-    rows = md_find_table(SRC_DIR / f'需求分析_观澜_{VERSION}.md', ('分类', '判定条数'))
+    head, rows = md_find_table(SRC_DIR / f'需求分析_观澜_{VERSION}.md', ('分类', '判定条数'))
     if not rows:
         WARN.append('需求分析 §9.1 的分类表未解析到,图 fig-req-04 用实跑输出兜底')
         ensure_evidence()
@@ -280,11 +289,15 @@ def fig_req_04():
         ks, vals, note = list(cats), list(cats.values()), '依据 guanlan.py check 实跑输出'
     else:
         ks, vals, note = [], [], '依据《需求分析》§9.1 表(同源于 guanlan.py check 实跑输出)'
+        c_name, c_n = col_of(head, '分类') or 0, col_of(head, '判定条数') or 1
+        c_gate = col_of(head, '代表门')
         for r in rows:
-            if r[0] == '合计':
+            if r[c_name] == '合计':
                 break
-            ks.append('%s(%s)' % (r[0], (r[2][:22] + '…') if len(r) > 2 and len(r[2]) > 22 else (r[2] if len(r) > 2 else '')))
-            vals.append(int(re.sub(r'[^0-9]', '', r[1]) or 0))
+            gate = r[c_gate] if c_gate is not None and c_gate < len(r) else ''
+            ks.append('%s(%s)' % (r[c_name], (gate[:22] + '…') if len(gate) > 22 else gate))
+            vals.append(int(re.sub(r'[^0-9]', '', r[c_n]) or 0))
+    print('    §9.1 门分类:', dict(zip(ks, vals)) if ks else '—')
     fig, ax = plt.subplots(figsize=(9.6, 0.62 * len(ks) + 1.9))
     barh(ax, ks, vals, xlabel='判定条数(合计 %d 条)' % sum(vals))
     ax.set_title('验收门分类与数量(%s)' % note, fontsize=11)
@@ -294,7 +307,7 @@ def fig_req_04():
 # ── 设计说明组 ─────────────────────────────────────────────────────────────────
 def fig_des_01():
     fig, ax = canvas(9.6, 7.4)
-    ax.text(50, 96, '观澜·如东样板 v2 总体架构(离线单包,无外网依赖)', ha='center',
+    ax.text(50, 96, '观澜 v2 风电场智能分析系统 总体架构(离线单包,无外网依赖)', ha='center',
             fontsize=11.5, fontweight='bold')
     box(ax, 4, 84, 92, 9, '使用侧:浏览器(门户 / 分析总览 /detail/v2 / 本体网 / 运维控制台 /ops)',
         fc='#f6f2e8', ec='#b08a3e', fs=10)
@@ -316,7 +329,7 @@ def fig_des_01():
     arrow(ax, (50, 41), (50, 36.4))
     box(ax, 3, 13, 46, 9, '输入层 data/raw/<场>/:scada_10min·scada_1min·scada_mdb\n故障报警·风机故障记录·'
                           'windcms·m5_cms_tcm·油样报告', fc='#f6f2e8', ec='#b08a3e', fs=9)
-    box(ax, 51, 13, 46, 9, '共享资料:西门子 4.0 技术资料(本体 kb_ingest 读)\n配置 configs/:serve.json·'
+    box(ax, 51, 13, 46, 9, '共享技术资料(OEM 资料,本体 kb_ingest 读)\n配置 configs/:serve.json·'
                            'models.json·portal_pages.yaml', fc='#f6f2e8', ec='#b08a3e', fs=9)
     arrow(ax, (26, 23), (26, 26.6))
     arrow(ax, (74, 23), (74, 26.6))
@@ -469,7 +482,7 @@ def fig_dat_01():
     axes[1].set_title('体量(GB)', fontsize=10)
     tot_n = sum(s[1] for s in stats)
     tot_g = sum(s[2] for s in stats)
-    fig.suptitle('输入数据族件数与体量(依据实测 data/raw/如东 下一层目录;合计 %d 件 / %.1f GB)'
+    fig.suptitle('输入数据族件数与体量(样本场实测;依据 data/raw/<场站>/ 下一层目录,合计 %d 件 / %.1f GB)'
                  % (tot_n, tot_g), fontsize=11)
     save(fig, 'fig-dat-01-数据族体量.png')
 
@@ -479,7 +492,7 @@ def fig_dat_02():
     ax.text(50, 96, '数据 → 功能 → 产物 流向(只列主干,逐族对应见正文表)', ha='center',
             fontsize=11.5, fontweight='bold')
     left = ['scada_10min / 1min\nscada_mdb(归档)', '故障报警 / 风机故障记录', 'windcms / m5_cms_tcm',
-            '油样报告', '西门子 4.0 技术资料']
+            '油样报告', '共享技术资料(OEM)']
     for i, t in enumerate(left):
         box(ax, 2, 78 - i * 15, 26, 12, t, fc='#f6f2e8', ec='#b08a3e', fs=9)
     mid = ['判级矩阵与时间窗重算', '发电性能与可靠性', '故障与停机台账', '振动六层链与融合面',
@@ -500,41 +513,50 @@ def fig_dat_02():
     save(fig, 'fig-dat-02-数据到产物.png')
 
 
-# 收资要求 v3 的 21 项 → 观澜侧到位状态(依据 docs/输入数据放置指导_v0.1.md 的体检结论、
-# configs/contracts/siemens_swt40_rudong.yaml 的 materials 节、以及 data/raw/如东 实测)
+# 收资要求 v3 的 21 项 → 观澜侧到位状态(**兜底表**:正常走「解析《数据要求说明》§8.1 表」这条路;
+# 这里只在源件/表结构异常时顶上,故同样按对外口径写成中性表述,不出现样本场标识)
 REQ_STATUS = [
-    ('1', '10分钟SCADA数据', '已在位', 'scada_10min(38 件 / 14.7 GB)+ scada_mdb 归档'),
+    ('1', '10分钟SCADA数据', '已在位', 'scada_10min(样本 38 件)+ scada_mdb 归档'),
     ('2', '秒级SCADA高频数据', '部分', 'scada_1min 38 件在位;1 s/5 s 原生高频未见交付'),
-    ('3', '测风塔数据', '未到位', 'mast: FALSE —— 测风塔目录零交付(须催缴)'),
+    ('3', '测风塔数据', '未到位', '契约 mast: FALSE —— 测风塔目录零交付(须催缴)'),
     ('4', '功率曲线', '部分', '合同担保曲线为图片级(须描点提取)'),
-    ('5', '机型参数', '已在位', 'contracts/siemens_swt40_rudong.yaml meta + 理论功率'),
-    ('6', '机位分布数据', '已在位', '风机坐标.xlsx(38 台 + 升压站)'),
-    ('7', '状态、事件、故障、告警', '已在位', '故障报警 + 风机故障记录(代码表 2793 条)'),
-    ('8', '机舱CMS振动数据', '已在位', 'windcms 25,693 件 / 153.8 GB + 厂家报告'),
+    ('5', '机型参数', '已在位', 'configs/contracts/<机型>_<场>.yaml 的 meta + 理论功率'),
+    ('6', '机位分布数据', '已在位', '场站坐标件(依契约记载)'),
+    ('7', '状态、事件、故障、告警', '已在位', '故障报警 + 风机故障记录(代码表 2,793 条)'),
+    ('8', '机舱CMS振动数据', '已在位', 'windcms 约 15.4 万 MB 级 + 厂商报告'),
     ('9', '机组统计数据/运行数据', '已在位', 'SCADA 统计 + sum 层(DailySummary)'),
-    ('10', '保护定值及控制参数', '部分', '控制参数由 M9 反演;定值表未成册交付'),
+    ('10', '保护定值及控制参数', '部分', '整定值表在位;策略文档未交付'),
     ('11', '机组故障录波数据', '未到位', 'fastlog 仅 4 台、10 Hz 事件快照'),
-    ('12', '可研报告及微观选址', '未到位', '目录未见交付(可研另有史料塔记录)'),
+    ('12', '可研报告及微观选址', '未到位', '目录未见交付'),
     ('13', '风电场简介', '部分', '场站元信息散见合同与技术资料'),
-    ('14', '风电场坐标、机位布置图', '已在位', '坐标件在位;布置图含于技术资料'),
-    ('15', '风机技术协议及合同', '已在位', '西门子 4.0 技术资料 + 担保曲线附件'),
+    ('14', '风电场坐标、机位布置图', '已在位', '依契约记载;布置图含于技术资料'),
+    ('15', '风机技术协议及合同', '已在位', 'OEM 技术资料 + 担保曲线附件'),
     ('16', '并网至今生产报表', '部分', '日粒度发电账 555 天 + 全寿命月账(2016-06 起)'),
     ('17', '周围已建风电场生产报表', '未到位', '未见交付(尾流先验缺外部对照)'),
     ('18', '运维记录', '已在位', '检修记录 2025+2026 / 油样 2025'),
     ('19', '大部件更换记录', '部分', '出保备件台账在位;更换台账不完整'),
-    ('20', '图纸、技术文件', '已在位', '西门子 4.0 技术资料(电气/液压/逻辑)'),
+    ('20', '图纸、技术文件', '已在位', 'OEM 技术资料(电气/液压/逻辑)'),
     ('21', '设备参数', '部分', '主轴/齿轮箱/发电机型号与齿数部分在位,明细待补'),
 ]
 
 
 def fig_dat_03():
-    """收资 21 项到位状态 —— **与《数据要求说明》§8.1 表同源**(解析那张表,图文不打架)。"""
+    """收资 21 项到位状态 —— **与《数据要求说明》§8.1 表同源**(解析那张表,图文不打架)。
+
+    ★2026-09-22 实逮(我自己的 bug): 原先按**写死的列号**取"状态",而该表是
+    `序号|数据类型|观澜是否必须|对应数据族/落位|状态|缺口与催缴要点` —— 第 5 列才是状态,
+    写死取第 4 列 ⇒ 21 行全部落进"未标",图照样画得出来(所以没被"生成成功"发现)。
+    现在一律**按表头名定位列**,并把"未标"当告警打出来。
+    """
     from collections import Counter
-    rows = md_find_table(SRC_DIR / f'数据要求说明_观澜_{VERSION}.md', ('序号', '状态'), first_cell='1')
+    head, rows = md_find_table(SRC_DIR / f'数据要求说明_观澜_{VERSION}.md', ('序号', '状态'), first_cell='1')
     items = []
     if rows and len(rows) >= 21:
+        ci_no, ci_name = col_of(head, '序号') or 0, col_of(head, '数据类型') or 1
+        ci_st = col_of(head, '状态')
+        ci_note = col_of(head, '缺口', '催缴')
         for r in rows[:21]:
-            no, name, status = r[0], r[1], r[3] if len(r) > 3 else ''
+            status = r[ci_st] if ci_st is not None and ci_st < len(r) else ''
             if '已在位' in status:
                 st = '已在位'
             elif '部分' in status:
@@ -543,13 +565,17 @@ def fig_dat_03():
                 st = '未到位'
             else:
                 st = '未标'
-            note = r[5] if len(r) > 5 else ''
+            note = r[ci_note] if ci_note is not None and ci_note < len(r) else ''
             qual = '(依契约记载)' if '记载' in status else ''
-            items.append((no, name, st, qual + note[:34]))
+            items.append((r[ci_no], r[ci_name], st, qual + note[:34]))
+        if any(i[2] == '未标' for i in items):
+            WARN.append('§8.1 表有 %d 行状态未识别(表头=%s)'
+                        % (sum(1 for i in items if i[2] == '未标'), head))
     else:
         WARN.append('数据要求说明 §8.1 的 21 项表未解析到,图 fig-dat-03 用内置判定兜底')
         items = REQ_STATUS
     cnt = Counter(r[2] for r in items)
+    print('    §8.1 状态计数:', dict(cnt))
     order = ['已在位', '部分', '未到位']
     colors = {'已在位': '#5b8c4a', '部分': '#d9a441', '未到位': '#c0504d', '未标': '#b8b8b8'}
     fig, axes = plt.subplots(1, 2, figsize=(11.0, 5.8), gridspec_kw={'width_ratios': [1, 1.6]})
@@ -582,7 +608,7 @@ def fig_dat_03():
 
 def fig_dat_04():
     import pandas as pd
-    store = P.store('rudong')
+    store = P.store()      # 当前场(不硬编码场代号,避免进图题)
     out = []
     for fn, label in (('temp_monthly.parquet', '温度月轨迹 temp_monthly'),
                       ('alarms.parquet', '报警台账 alarms')):
@@ -608,7 +634,7 @@ def fig_dat_04():
     ax.set_xticklabels([m[2:] for m in months], rotation=90, fontsize=7.5)
     ax.set_ylabel('行数 / 事件数', fontsize=9)
     ax.legend(fontsize=9)
-    ax.set_title('关键数据的时间覆盖(依据 outputs/rudong/windscada 的 temp_monthly 与 alarms 实读)',
+    ax.set_title('关键数据的时间覆盖(样本场实测;依据 outputs/<场>/windscada 的 temp_monthly 与 alarms 实读)',
                  fontsize=11)
     for s in ('top', 'right'):
         ax.spines[s].set_visible(False)

+ 22 - 1
src/version.py

@@ -19,7 +19,7 @@ import json
 import pathlib
 
 NAME = '观澜·如东样板 v2'
-VERSION = '2.10.0'         # ★ 改版本只改这里
+VERSION = '2.10.1'         # ★ 改版本只改这里
 EDITION = 'offline-single-package'
 PACKAGE_STEM = 'app_guanlang'           # 交付包文件名前缀(用户令 2026-09-17)
 
@@ -49,6 +49,27 @@ BUMP_RULE = ('改动落在"解决方案/架构/核心功能" → 大 +1(中/
 #   level: major/minor/patch 表示这一版**相对上一版**是哪一级变化;legacy 表示该版用的是
 #   旧编号体系(0.x,未按本规则),仅作历史对账用。
 HISTORY: tuple[dict, ...] = (
+    dict(version='2.10.1', date='2026-09-22', level='patch',
+         title='交付文档对外版:去样本场标识(全文不体现具体风电场)+ 新增多场适用性章节',
+         note='纯交付物修订(系统功能未变)⇒ 小版本 +1。用户令: 「修改三份文档,内容参考如东风电场,'
+              '但不体现如东风电场,且具有不同风电场适用性」。做法: '
+              '① **去标识化**(中文与罗马化形态一并去): 场名/业主/地域/OEM/第三方机构/系统名里的样本场代号'
+              '一律换成中性表述(样本风电场 / 本场 / 业主单位(从略)/ 主机制造商(OEM,名称从略)/ '
+              '4.0 MW 级海上机组(机型代号从略)/ 观澜 v2(风电场智能分析系统));'
+              '**路径写成模板**(`data/raw/<场站>/…`、`outputs/<场>/…`、`reference/<场>/…`、'
+              '`configs/contracts/<机型>_<场>.yaml`、`configs/farms/<场>.yaml`、`WINDSCADA_FARM=<场>`),'
+              '并在「编写依据」表下写明占位符替换规则;实测数字与结论全部保留,标注「样本场实测(2026-09-22)」。'
+              '② **多场适用性**(用户令第二句): 需求分析新增第 11 章(FR-44…FR-52 + NFR-12: 场配置化、'
+              '机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、跨场统一口径、按场裁剪收资、'
+              '换场验收检查表);设计说明新增第 16 章(场抽象层与配置 schema、场无关引擎 vs 场相关参数分层表、'
+              '换场作业单与检查表、接新 OEM/新数据形态的扩展点、换场会失效的假设如实列);'
+              '数据要求说明新增第 12 章(通用必选/可选判定规则、场配置字段对照、数据源形态适配、'
+              '换场收资差异清单、按场裁剪步骤)。'
+              '③ **去标识化做成机器可查的硬门**(不靠人自觉): `scripts/delivery_docs_build.py` 增 `FORBIDDEN` '
+              '词表并在 `--check/--verify` 里逐份扫描(命中即报 FAIL),`guanlan.py check` 的「交付文档三件」'
+              '门同步带上该扫描;图表器里出现的样本场字样一并中性化(图题写「样本场实测」)。'
+              '验证: 三份 markdown 源件与 docx 对 12 个禁用词 0 命中;`delivery_docs_build --verify` rc=0;'
+              '`guanlan.py check` 全绿;三份 docx 与插图重渲,图表数字仍与正文同源(图解析文档表)。'),
     dict(version='2.10.0', date='2026-09-22', level='minor',
          title='交付文档三件(需求分析 / 设计说明 / 数据要求说明)与源代码化生成器',
          note='非核心功能新增(交付物)⇒ 中版本 +1。用户令: 「整理观澜的需求/设计/数据接入,各写一份 Word 文档,'

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů