Ver código fonte

2.10.2 数据要求说明改为纯数据需求规格(去现状/去实现细节/测点按业务含义四列表/新增SCADA秒级要求); 加严格模式硬门; dat 不再配图

zhouyang.xie 2 semanas atrás
pai
commit
8db8272526

BIN
docs/figures/fig-dat-01-数据族体量.png


BIN
docs/figures/fig-dat-02-数据到产物.png


BIN
docs/figures/fig-dat-03-收资项覆盖.png


BIN
docs/figures/fig-dat-04-时间覆盖.png


+ 0 - 1325
docs/src/数据要求说明_观澜_2.10.1.md

@@ -1,1325 +0,0 @@
-# 数据要求说明 · 观澜 v2 风电场智能分析系统 · 版本 2.10.1
-
-## 1 文档说明
-
-### 1.1 目的
-
-本文说明「观澜 v2(风电场智能分析系统)」(版本 2.10.1,海上风电场智能分析离线系统)运行、判级与出件所**实际需要**的输入数据:需要哪些数据分类、每一类里需要哪些测点(字段)、每个测点是什么度量单位、以及这一类到底是**必须**还是**可替代**。
-
-全文以一座已投产的海上风电场为样本,称**样本风电场(下称"本场")**;凡出现"本场"之处均指这一样本场。文中的件数、体量、行数、时间覆盖、通道数与缺口结论都取自本场,统一标注为**样本场实测(2026-09-22)**;**换场(本场之外的任何风电场)不沿用这些数字**,须按第 12 章的通用模板与换场适配重新收资与判定。
-
-本文的读者可以直接拿它去现场收资,也可以拿它逐条核对「现场已经给了什么、还缺什么、缺了会怎样」。
-
-### 1.2 读者
-
-**表 1-1 本文读者的使用方式**
-
-| 读者 | 怎么用本文 |
-|---|---|
-| 现场数据管理员 | 按第 8 章与第 11 章的清单收资、按第 7 章的流程落位、按第 10 章核对锚点、按第 12 章做换场裁剪 |
-| 观澜实施与运维人员 | 按第 3 章判断某一族数据有没有消费者、按第 7 章判读体检退出码 |
-| 分析人员 | 按第 5 章确认某个测点是否在契约内、单位与量纲是否正确、哪些列没有取证 |
-| 交付与验收人 | 按第 9 章确认每一条缺口是"现场补件"还是"研发补生成端",按附录 A 追到依据 |
-
-### 1.3 与《数据分析收资要求 v3》的关系
-
-现场《数据分析收资要求-v3.docx》给出 **21 类**收资项,是**现场视角的"要什么"**。本文是**观澜视角的"怎么用"**,两者是同一件事的两面:
-
-- 收资要求 v3 的原文逐字列在附录 C,一条不改、一条不删;
-- 第 8 章把 21 条逐条对照到观澜的数据族、落位目录、消费者脚本与到位状态,并给出缺口与催缴要点;
-- 收资要求 v3 没有区分"必须/可选",本文补上这一层——因为观澜对 21 类的依赖强度**并不相同**:有的缺了整个系统会响亮降级(页面该处显示"缺件"),有的缺了只影响一条辅助轴。
-- 第 12 章把这一层抽象成**与具体场无关的判定规则**(五问定档:四问定必须性、一问定"不催"),并给出场配置字段对照、数据源形态适配、换场收资差异清单与按场裁剪步骤——21 项收资从此是**通用模板**,不是某一个场的专用清单。
-
-一句话口径:**收资要求 v3 是收资面,本文是接入面与依赖面。**
-
-### 1.4 必须性分级定义
-
-**表 1-2 必须性分级定义**
-
-| 分级 | 含义 | 缺了会怎样 |
-|---|---|---|
-| 必须 | 观澜的核心分析面(判级、发电性能、可靠性、台账)直接依赖;缺则该面不可判或整步降级 | 页面该面显示"缺件/不可判",重算链对应步骤响亮报错或跳过,**不造数** |
-| 建议 | 支撑交叉校验与二次确认;缺了不阻断主线,但会削弱结论的可信度与可追溯性 | 结论仍在,但依据变少,跨源互证做不了 |
-| 可选 | 增强型输入,只在特定专题里用;不参与常规重算 | 只有对应专题不可做 |
-| 替代 | 可由观澜**自算件**或**已在位的其他数据**顶替,且替代口径必须写在产物与依据里 | 用替代件并如实标注来源,现场正本在位时正本优先 |
-
-### 1.5 口径与依据
-
-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/<场>` 只读实测为准,标注「样本场实测(2026-09-22)」**;正文与表中凡写"实测/本机实测"而未另注日期者,均指这一次样本场实测。
-4. 本文中"含窗且确指时间窗口"之处一律写**时间窗**;影响机组数量之处写**影响机组/影响机组数**;英文简写一律写"中文(英文简写)"。
-5. 全文单位用半角,温度用 `℃`,旋转速度用 `r/min`(= 机器可读真源里的 `rpm`,两者同量纲)。
-
-## 2 数据接入总览
-
-### 2.1 观澜运行所需的输入全景
-
-观澜不直接读现场原始件,而是先由**摄入链**把 `data/raw/<场站>/` 下的源件转成 `outputs/<场>/` 下的产物,页面再读产物。因此"输入"分两层:**源件层**(现场交什么)与**产物层**(系统算什么)。
-
-源件层的落位约定只有一条判据:**摄入脚本按"源类目录 → 再往下一层就是文件"读**。多套一层包名目录(例如 `scada_10min\10分钟数据\WTG01.csv`)等于没放。
-
-本场当前在位 9 个族(含与场站并列的共享技术资料族),**如表 2-1 所示**。
-
-**表 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/<机型>技术资料/` | 318 | 2,936,758,089 B(约 2.7 GB) |
-
-**如图 2-1 所示**,体量高度集中在 CMS 振动导出(约 150.1 GB,占九族合计 223.8 GB 的绝大多数)与两类 SCADA 导出上,台账类三族合计不到 0.2 GB 却承担了报警、工单、油样三条台账的**逐行对齐**。
-
-![图 2-1 输入数据族件数与体量(样本场实测 2026-09-22)](figures/fig-dat-01-数据族体量.png)
-
-场站目录下另有 1 件散装件 `scada_10min_202607.csv`(943,520,249 B):它是**按月提取/核对件**,不是摄入源。扫描器按名字模式忽略它(不报"未归类"、不计变化),真正进产物的是 `scada_10min/WTG01.csv` 一类的逐台主件与同台补充件。
-
-### 2.2 数据 → 功能 → 产物
-
-**如图 2-2 所示**,观澜的链路是"源件 → 摄入/构建 → 产物 → 页面/交付件",每一环都能指到脚本与产物。
-
-![图 2-2 数据 → 功能 → 产物 流向](figures/fig-dat-02-数据到产物.png)
-
-### 2.3 族 → 支撑功能 → 产物
-
-**表 2-2 族 → 支撑功能 → 产物**
-
-| 观澜数据族 | 支撑的观澜功能 | 主要产物 |
-|---|---|---|
-| `scada_10min` | 判级四轴(变桨/偏航/蓄能/温度)、发电性能七镜头、功率曲线偏差、损失电量、停机事件、热链与冷却执行器、控制参数一致性 | `temp_bins`、`temp_monthly`、`powercurve_bins`、`powercurve_dev`、`loss_monthly`、`control_profile`、`control_schedule`、`stop_events`、`yaw_daily`、`hydraulic_accum`、`thermal_chain`、`system_aux`、`curve_lenses`、`curve_liveness` |
-| `scada_1min` | 变桨面零位三口径与越线计数 | `pitch/**`(日粒度液压/润滑/柱塞 + 零位三口径月表) |
-| `scada_mdb` | SCADA 侧的**上游归档**与 CSV 缺失时的回落源;按类目交付的月度通道组是"同台 10 分钟补充件"的上游 | 同 `scada_10min`(回落路径) |
-| `故障报警` | 报警集中、停机账、限电绑定、首发链 | `alarms.parquet` |
-| `风机故障记录` | 检修工单台账、重复检修与停机损失、闭环验证 | `workorders.parquet` |
-| `油样报告` | 油液化验索引、油液时效胶囊、融合面油样轴 | `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` |
-| `<机型>技术资料` | 本体对象库、检索索引、实机参数表(整定值)、故障码对译 | `ontology/objects.json`、`retrieval_index.json`、`ontology/turbine_params.parquet` |
-
-## 3 数据分类总表
-
-**如表 3-1 所示**,观澜的每一个数据族都能追到收资要求 v3 的序号、落位目录、消费者脚本、必须性与当前到位状态。
-
-**表 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/<场>_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 逐类数据要求
-
-本章逐类给出:用途、最小可用量(时间跨度、采样频率、台数)、必需测点或字段、单位、格式与编码、落位目录、到位现状、缺失影响。
-
-### 4.1 `scada_10min`(10 分钟 SCADA 数据)
-
-**用途**:观澜的**主数据层**。判级四轴(变桨、偏航、蓄能、温度)、发电性能七镜头、功率曲线偏差、损失电量、停机事件、热链与冷却执行器、控制参数一致性全部由它派生。
-
-**最小可用量**:38 台,逐台一个文件,采样周期 600 s;时间跨度**建议不少于 21 个月**(判级用的 `ref` 固定窗为 2025-07-01 至 2025-12-31,`ref_sm` 季节匹配窗为同历月早一年)。本机实测主件覆盖 2025-01-01 00:00:00 至 2026-07-07 00:00:00,同台补充件覆盖 2026-08-01 00:00:00 至 2026-09-01 00:00:00。
-
-**必需测点或字段**:见第 5 章。判级与曲线实际取用 **48 列公共子集**(第 5.2 节),其中功率、风况、转速、桨距、温度 `_mean` 27 列、偏航压力、液压压力、润滑与限电命令族是必需项。
-
-**单位**:功率 kW、风速 m/s、转速 r/min、桨距角 °、温度 ℃、压力 bar、时间 s、计数 次。
-
-**格式与编码**:`.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/`。
-
-**到位现状**:已在位。38 件主件 + 76 件同台补充件 = 114 件,逐台行数一致(WTG01 主件 77,551 行),说明全台同步断录、无单台缺失。
-
-**缺失影响**:缺台则"按台取数"为空白(页面显示空白,不报错);整族缺失则第 ③、④、④c、⑤b 四步全部无输入,判级矩阵、七镜头曲线、损失账、停机账、温度月轨迹同时不可用,**影响机组数 = 38 台**。
-
-### 4.2 `scada_1min`(1 分钟 SCADA 数据)
-
-**用途**:变桨面零位三口径与越线计数(`scripts/pitch_face_build.py`)。它是 1 分钟转发层,风速是**补偿风**(`WMET_wind_speed_compensated`,与原生 `SecAnemo` 差约 −1.35 m/s),因此**不能**拿它做功率曲线或湍流度(NTF)分析。
-
-**最小可用量**:38 台,采样周期 60 s;本机实测覆盖 2025-01-01 00:00:00 至 2026-06-30 23:59:00(01E 实测 785,250 行)。
-
-**必需测点或字段**(契约 `turbine_1min.required`):`active_power`(有功功率,kW)、`wind_velocity`(补偿风速,m/s)、`rotor_speed`(叶轮转速,r/min)、`pitch_angle_blade_1`(1 号叶片桨距角,°)。
-
-**单位**:同 4.1;桨距角 °、桨叶油压 bar。
-
-**格式与编码**:`.csv`,平铺,文件名为**内部台号**(`01E.csv` 至 `38B.csv`,字母 A 至 F 为集电线路 string);`utf-8-sig`;实测 79 列中英混合转发点表;时间戳 `YYYY-MM-DD HH:MM:SS`。
-
-**落位目录**:`data/raw/<场站>/scada_1min/`。
-
-**到位现状**:已在位(38 件)。但**全场级死列 16 条**已实证(含 `yaw_error1`、`cabin_position`、`twisted_cable_angle`、发电机三相绕组温度 1/2/3、1 至 3 号桨电机温度、自定义可利用率、航空灯状态 AI 等),这些列在本场 1 分钟层没有可用的时序信息。
-
-**缺失影响**:缺则变桨面退化为只用 10 分钟层,零位三口径不可判;**影响机组数 = 38 台**。
-
-### 4.3 `scada_mdb`(现场类目归档库)
-
-**用途**:现场按"月份 × 通道组"交付的 Access 归档库。它是 `scada_10min` 同台补充件的**上游**:取数层不直接读类目库,必须先合成同台 10 分钟补充件 CSV。同时,当某台没有 CSV 时,取数层回落到 `scada_mdb/<年>年/<月>月/*-10min.mdb`。
-
-**最小可用量**:按月的 10 分钟库(`<年>-<月>-10min.mdb`)优先;类目库按需。本机实测 2025 年 12 个月齐、2026 年 1 至 8 月齐。
-
-**必需测点或字段**:9 个逐台类目与主件**前缀块一一对应**:`tur` 91、`din` 93、`dot` 69、`cnt` 69、`tmp` 116、`prs` 52、`grd` 50、`flg` 42、`int` 13,合计 **595 通道,0 缺 0 多**。另有 `scd`、`std`、`sum` 三类是 farm 级与台账级,不在逐台 10 分钟布局内。
-
-**单位**:同 4.1。注意 MDB 存 double、时间写 `MM/DD/YYYY HH:MM:SS`;现场 CSV 导出是 float32、时间写 `YYYY-MM-DD HH:MM:SS+00:00`,合成补充件时按 **float32 + ISO** 对齐。
-
-**格式与编码**:`.mdb` 或 `.zip`(年度归档 `<年>年.zip`,内层 `<年>/<月>/<年-月-类>.zip` → `.mdb`)。中文类目目录名 ↔ 类目码:`风机数据`=tur、`温度数据`=tmp、`压力数据`=prs、`电网数据`=grd、`DigiIn数据`=din、`DigiOut数据`=dot、`计算数据`=cnt、`标志数据`=flg、`内部数据`=int、`统计数据`=scd、`标准数据`=std、`汇总数据`=sum。
-
-**落位目录**:`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 在位。
-
-**缺失影响**:取数层会回落到 CSV,**当前不影响页面数值**;但"按类目逐列回溯到现场原始寄存器"这条链路对缺的类目不可用。另外,直接读类目库会覆盖不到 `scd`/`std`/`sum` 三类台账级数据。
-
-### 4.4 `故障报警`(报警事件导出)
-
-**用途**:报警台账(报警集中、停机账、限电绑定、首发链)。
-
-**最小可用量**:覆盖分析时间窗的年度或季度导出即可;本机实测 16 件,含 `2025Q1.xls` 至 `2025Q4.xls`、`2025年全年故障记录.xls`、`2026.1-3.xls`、`2026.4.xls`、`2026.5.xls` 等。
-
-**必需测点或字段**:每行必须带 `TimeOn`(激活时刻)与 `Alarmcode`(报警码);缺这两项的统计报表件会被摄入器**跳过**(不是报错)。「全年/年至今」累计快照件也会被自动跳过,避免同一事件算两遍。
-
-**单位**:时间戳为秒级绝对时刻;时长 s;报警码为无量纲编码(`code`)。
-
-**格式与编码**:`.xls`(实为 SpreadsheetML XML)、`.xlsx`、`.xml`;不许多套一层目录。
-
-**落位目录**:`data/raw/<场站>/故障报警/`。
-
-**到位现状**:已在位。产出 `alarms.parquet` 9 列、**39,211 行**(与核对锚点一致)。
-
-**缺失影响**:缺则报警台账为 0 行,停机事件的"原因链"轴、限电绑定、首发链全部不可用;`stop_events` 的报警窗计数降级。
-
-### 4.5 `风机故障记录`(检修工单台账)
-
-**用途**:检修工单台账(重复检修、停机损失、闭环验证)。
-
-**最小可用量**:覆盖分析时间窗的月度汇总表;本机实测 137 件(123 件被摄入,14 件是 `.rar` 与现场照片等附件,不摄入)。按年目录分组(2021 年至 2026 年)另加"业主统计故障"等。
-
-**必需测点或字段**(模板表表头):必须含 `机组编号`、`故障名称`、`故障代码`;表里 `风场名称` 必须属本场(集团导出件混着十来个场)。摄入器按第 3 行为列名、数据从第 4 行起读。
-
-**单位**:`发电量损失(kWh)` 为 kWh,`维修成本(元)` 为元,`故障停机时间`、`维修时间` 为时间;其余为文本。
-
-**格式与编码**:`.xls`、`.xlsx` 为摄入件;`.rar`、`.jpg`、`.png` 为附件,保留但不摄入。年目录 + 月度表,不限层级。
-
-**落位目录**:`data/raw/<场站>/风机故障记录/`。
-
-**到位现状**:已在位。产出 `workorders.parquet` 26 列、**5,876 行**(与核对锚点一致)。
-
-**缺失影响**:缺则检修面与闭环验证不可用,油样与工单的交叉核对做不了。
-
-### 4.6 `windcms`(CMS 振动原始导出)
-
-**用途**:振动侧的**唯一可重算源件**。索引、谱库、CMS 报告与逐台页、在升与换件闭环、三层基线都由它算。
-
-**最小可用量**:**至少一个季度**的秒级振动数据(非加密)及原始振动时域波形。本机实测只有 `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 与阶次跟踪的分母。
-
-**单位**:振动量纲见第 5.4 节,`y_unit` 实测取值 `1`、`m/s`、`m/s²`、`RPM`、`%`;频率轴 `x_unit` 为 `Hz`;波形时间轴 `x_unit` 为 `s`;灵敏度 mV/g(`sensitivity_mvpg`)。
-
-**格式与编码**:`*_decode.json`(Brande TCM Enterprise 导出,每件是一次 API 响应 `{body:{body:{"<时间戳>":[{"Record":{...}}]}}}`);厂家报告为 `.docx`(有文本层,可摄入)或 `.pdf`(扫描件,只归档)。`measurement/<年>/<月>/<WTGxx>/` 层级可保留,摄入按 `rglob` 找 `*_decode.json`。
-
-**落位目录**:`data/raw/<场站>/windcms/`;厂家报告放 `data/raw/<场站>/windcms/厂家报告/整机厂商_月度/`。
-
-**到位现状**:已在位(25,679 件 decode 导出 + 14 件厂家报告)。产出窗 `w0316` 索引 **2,066,686 行 × 54 列**、谱 **420,742 条 / 1,701 个 npz 分片 / 3.15 GB**。
-
-**缺失影响**:缺则振动侧无源可算。**具体缺口**:2026-01 的 decode 导出(`w0127` 窗)不在本机,导致叶片 1P 那一族无法重建;`w0707`、`w0711`、`w0724`、`w0805`、`w0811` 五个窗的导出件也不在本机。
-
-### 4.7 `m5_cms_tcm`(振动线 handoff 正本)
-
-**用途**:融合面判级、TCM 兼容件。现场若给出 `handoff_vibration_v2.json` 与 `component_history.json` **正本**,放这里即被优先采用。
-
-**最小可用量**:两件正本各一份;本机实测现场只给了 1 件厂家 docx(第三方检测机构传动链振动分析报告,机构名称从略)。
-
-**必需测点或字段**:正本结构含 `meta`(版本/日期/来源/取代关系/原则)、`detectability_prior`、`cross_source_requests`、`baseline_三层`、`per_turbine`(人工裁决 + `claim_window`)、`field_actions_v1`、`calibration_updates_v1`、`open_items`、`tcm16_schedule`。
-
-**单位**:本件是判断与待办,不是测量量;内含的判级为枚举。
-
-**格式与编码**:`.json`、`.docx`、`.pdf`、`.md`。
-
-**落位目录**:`data/raw/<场站>/m5_cms_tcm/`。
-
-**到位现状**:**部分到位**。正本缺失,由观澜自算件顶替:`scripts/<场>_fusion_handoff.py`(融合面)与 `scripts/component_history_build.py`(在升与换件闭环)。本机只有 1 个导出色窗,故在升与闭环**如实为空**并把数据边界写进 `meta.★数据边界`。
-
-**缺失影响**:`/detail` 工作台的**振动融合面**(`/api/fleet` 的 `fus`)如实报"缺件";CMS 报告与评估页那一面已由按口径重建的两步喂上。
-
-### 4.8 `油样报告`(油液化验报告)
-
-**用途**:油液化验索引、油液时效胶囊、融合面油样轴。
-
-**最小可用量**:覆盖分析时间窗的逐台逐部件报告;本机实测 404 件、涉及 38 个台号目录与多个部件目录。
-
-**必需测点或字段**:摄入器从**文件名**抽字段(`DDMMYYYY_sampleid_项目_台号_部件`),不读 PDF 正文。因此文件名必须含日期、台号、部件。
-
-**单位**:无(索引为文本与日期);油液化验数值本身在 PDF 正文内,观澜不解析。
-
-**格式与编码**:`.pdf`,**两级目录** `<台号>/<部件>/*.pdf`。
-
-**落位目录**:`data/raw/<场站>/油样报告/`。
-
-**到位现状**:已在位(404 件)。产出 `oil_samples_index.parquet` **404 行**。**已知缺口**:华标 2026-07 批 102 行(合并报告 `BG-2026-07-YP013 样本风电场.pdf`)的源件不在现场包内,从零重算会从 506 行落到 404 行。
-
-**缺失影响**:缺则油液面不可用;油液与工单的交叉核对("换油是否紧跟检修")做不了。
-
-### 4.9 `<机型>技术资料`(厂商技术资料)
-
-**用途**:本体知识层(对象库、检索索引、实机参数表)与故障码对译。
-
-**最小可用量**:至少必须含 `kb_ingest.py` 点名的四个台账文件:`故障处理/故障处理手册.xlsx`、`故障处理/场站故障代码中英文对译表.xlsx`、`场站故障代码表.xlsx`、`维护相关/维护作业指导书.xlsx`。缺任一个都会打印 `⚠ 源缺失` 并降级,**不静默**。
-
-**必需测点或字段**:故障码(`WALM_fault_code`)、整定值(`Turbine+Parameters.*.xlsx` 的 `Number`、`Text`、`Min`、`Max`、`Default`、`Current`、`Value`、`Unit`、`Reset`)、图纸与手册的章节结构。
-
-**单位**:整定值表的 `Unit` 列实测 51 种取值,含 `℃`、`min`、`s`、`hrs`、`mHz`、`°`、`°/s`、`RPM`、`m/s`、`%`、`ms`、`Hz`、`kW`、`rpm`、`ccm`、`h`、`bar`、`V`、`A`、`m` 等。
-
-**格式与编码**:`.pdf`、`.docx`、`.doc`、`.xlsx`、`.xls`、`.pptx`;整棵树都读(白名单为全部)。
-
-**落位目录**:`data/raw/<机型>技术资料/`(与场站目录**并列**的共享资料,不在场站目录下)。
-
-**到位现状**:已在位(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 条整定值**。
-
-**缺失影响**:缺则本体层降级(对象库与检索索引内容缺失),故障码对译与保护定值轴不可用。
-
-### 4.10 `机型与部件参数`
-
-**用途**:机型常量(额定功率、传动比、轮毂高度、叶轮直径)与部件参数(轴承、齿轮、齿数)是振动的分母、功率曲线的基准、控制参数的参照。
-
-**最小可用量**:一份机型级参数表 + 一份部件级参数表(主轴轴承、齿轮箱各级、发电机轴承、叶片、变桨与偏航电机与大小齿齿数)。
-
-**必需测点或字段**:见第 5.6 节。
-
-**单位**:kW(功率)、m(长度/直径/高度)、无量纲(传动比)、℃(温度)、°(角度)、m/s(风速)。
-
-**格式与编码**:`.docx`、`.pdf`、`.xlsx`,或已固化的 `.json`(`reference/<场>/oem_bearing_freqs.json` 29 个轴承条目、`reference/<场>/gear_mesh_map_measured.json`、`reference/<场>/oem_scan_plan.json` 11 个部件)。
-
-**落位目录**:随族落位(`<机型>技术资料/` 或场站族目录)。
-
-**到位现状**:部分到位。机型级常量已在位(额定功率 4000 kW、叶轮直径 130 m、轮毂高度 87 m、传动比 119.752);部件级只在振动侧成体系(11 个部件的理论特征频率表已验),**逐部件型号与齿数的成册件未交付**。
-
-**缺失影响**:机型常量缺失会让振动分母与功率曲线基准不可复核;部件参数缺失会让"哪个部件、哪一级"的定位停在候选级。
-
-### 4.11 `机位坐标`
-
-**用途**:邻域与尾流先验、扇区分析、台号与集电线路 string 的地理对照。
-
-**最小可用量**:38 台 + 升压站的坐标与海拔(含轮毂高度)。
-
-**必需测点或字段**:台号、经度、纬度、海拔高度(含轮毂高度)、机型。
-
-**单位**:(°)(经纬度,东经/北纬);海拔 m;机型为文本。
-
-**格式与编码**:`.xlsx` 或 `.csv`。
-
-**落位目录**:`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` 的字母)。
-
-**缺失影响**:绝对扇区分析与风玫瑰不可用(另有独立原因:风向为**伪地理**,机舱编码器基准未对北,跨台偏移 ±161° 散布,契约已明令禁用绝对扇区闸);尾流先验缺外部对照。
-
-### 4.12 `功率曲线`
-
-**用途**:发电性能偏差判别的基准(`powercurve_dev`、`powercurve_bins`)。
-
-**最小可用量**:标准空气密度功率曲线 + 合同担保功率曲线各一条,覆盖切入至切出风速段。
-
-**必需测点或字段**:风速(m/s)与对应功率(kW)的成对点;合同担保曲线的附属条件(空气密度、湍流度、风切变)与保证条款(可用率 K ≥ 95%、6 个月整改、违约金)。
-
-**单位**:风速 m/s,功率 kW,空气密度 kg/m³,湍流度无量纲。
-
-**格式与编码**:数值表(`.csv`/`.xlsx`)最佳;图片级(PDF 图)须描点提取。
-
-**落位目录**:随族落位。
-
-**到位现状**:部分到位。契约 `materials.guarantee_curve` 记录合同附件图 2-1(ρ = 1.223、湍流度 0.1、风切变 0.2)与推力图 2-3,**为图片级,须描点提取**;标准空气密度曲线本身未交付。
-
-**缺失影响**:缺则发电性能偏差只能做**机群相对**比较(同场同工况横比),不能做**对标准曲线**的绝对偏差;页面会标注口径。
-
-### 4.13 `测风塔`(未交付)
-
-**用途**:自由流风速与湍流度的场级参考,是功率曲线与尾流分析的独立对照。
-
-**最小可用量**:前期实地测风至少满一年(5 分钟或 15 分钟);运行期一整年的分钟级数据。
-
-**必需测点或字段**:温度、湿度、气压,以及不同层高的风速、风速标准差、风向;若分钟级不具备风速标准差,则需 1 秒采样。
-
-**单位**:温度 ℃、湿度 %、气压 kPa(或 Pa/hPa)、风速 m/s、风向 °、风速标准差 m/s。
-
-**格式与编码**:(未取证——该族零交付,无从约定)。
-
-**落位目录**:(未约定;若交付需新增族并配摄入器)。
-
-**到位现状**:**未到位**。契约 `materials.mast` 明确写 `FALSE`——**测风塔目录零交付**。可研内有 2009-12 史料塔记录(100 m × 6 层),但**非运行期**数据。
-
-**缺失影响**:缺则自由流风速只有机舱风速计(`SecAnemo`)与补偿风(1 分钟层),湍流度只能用代理量(契约里 `WMET_ti`、`WMET_wind_ti_std` 属**未验证**级);尾流与场级损耗只能靠场内机群互比,缺外部绝对锚。
-
-### 4.14 `生产报表`(并网至今)
-
-**用途**:日粒度与月粒度的发电账,用于与 SCADA 侧损失账交叉核对。
-
-**最小可用量**:并网至今;本机实测日粒度发电账 555 天、全寿命月账自 2016-06 起。
-
-**必需测点或字段**:风速、发电量、故障率、限电量。
-
-**单位**:风速 m/s、发电量 kWh、故障率 %、限电量 kWh。
-
-**格式与编码**:(现场报表形态;观澜侧以台账件读取)。
-
-**落位目录**:随族落位(本机实测该件以台账形式散见技术资料与故障记录目录)。
-
-**到位现状**:部分到位(日粒度发电账 + 全寿命月账在位;"并网至今"完整序列与口径说明未成册)。
-
-**缺失影响**:缺则损失账只能单源(SCADA 侧),不能与生产账对拍,限电损失的经济口径无法外部锚定。
-
-### 4.15 `运维记录`(维护与检修台账)
-
-**用途**:检修面与闭环验证;与油样、报警三源交叉。
-
-**最小可用量**:覆盖分析时间窗的维护台账与检修台账。
-
-**必需测点或字段**:时间、机组、部件、作业内容、作业类型。
-
-**单位**:时间为绝对时刻;其余为文本。
-
-**格式与编码**:`.xls`、`.xlsx`(观澜侧以工单台账形式接入)。
-
-**落位目录**:`data/raw/<场站>/风机故障记录/`。
-
-**到位现状**:已在位(检修记录 2025 与 2026;油样 2025)。
-
-**缺失影响**:缺则"检出 → 处置 → 复测回落"的闭环链断在处置侧。
-
-### 4.16 `大部件更换`
-
-**用途**:部件在升与换件闭环的判定输入(`component_history.json` 的换件侧)。
-
-**最小可用量**:逐次更换的时间、机组、部件、原因。
-
-**必需测点或字段**:时间、机组、部件、更换原因、新旧件标识。
-
-**单位**:时间为绝对时刻;其余为文本。
-
-**格式与编码**:`.xlsx`(观测到 `大部件维修记录.20240619143912557.xlsx` 一类的台账件)。
-
-**落位目录**:`data/raw/<场站>/风机故障记录/`。
-
-**到位现状**:部分到位(出保备件台账在位;**更换台账不完整**)。在升与换件闭环目前只有一个导出色窗的输入,因此**如实为空**并写明数据边界。
-
-**缺失影响**:缺则"在升/换件"两类证据无法闭环,部件可靠性表只能反映测量面,不能反映处置面。
-
-### 4.17 `图纸与技术文件`
-
-**用途**:故障码位域语义、控制逻辑、液压与电气回路的最终依据;本体层与判据的出处。
-
-**最小可用量**:风机电气控制相关图纸、液压站图纸、逻辑图或控制方式文件。
-
-**必需测点或字段**:回路图、端子与接线表、PLC 逻辑、液压原理图、保护定值表、结构树。
-
-**单位**:图纸为无量纲(标注带 V、A、bar、℃ 等)。
-
-**格式与编码**:`.pdf`、`.docx`、`.xlsx`、`.pptx`。
-
-**落位目录**:`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` 等。
-
-**缺失影响**:缺则位域语义(例如"刹车状态 AI 的取值 61 是 0b111101,具体位含义")只能停在"须图纸"的未决态。
-
-### 4.18 `故障录波`(未交付)
-
-**用途**:故障前后机组数据的高分辨率快照,用于故障机理的秒级回溯。
-
-**最小可用量**:每次机组故障后详细记录故障前后机组数据的文件。
-
-**必需测点或字段**:(现场口径;观澜侧未接入)。
-
-**单位**:(现场口径)。
-
-**格式与编码**:契约 `materials.events` 记录 `fastlog` 为 **10 Hz 事件快照且仅 4 台**。
-
-**落位目录**:现场包里的 `fastlog数据/` **故意不落**(全库只有 2 处注释提到,无消费者)。
-
-**到位现状**:**未到位**。观澜**不消费**该类数据(无摄入器、无产物)。
-
-**缺失影响**:无(对当前分析面无影响);若将来要做秒级故障回溯,需要新增摄入器与判据。
-
-## 5 核心测点与字段明细
-
-### 5.1 10 分钟 SCADA 的通道类目与通道数
-
-现场 10 分钟导出按**类目**组织,9 个逐台类目与主件前缀块**一一对应**,合计 **595 通道,0 缺 0 多**。
-
-**表 5-1 逐台类目 → 前缀 → 通道数(样本场实测 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 |
-| 合计 | 逐台 10 分钟 | — | **595** |
-
-另有 3 个类目不在逐台 10 分钟布局内:`scd`(统计数据)、`std`(标准数据)、`sum`(汇总数据)——它们是 farm 级与台账级(`sum` 含 `AlarmLog` 事件与 `ParkPilot` 限电指令流水、`DailySummary`)。
-
-主件 CSV 实测 598 列 = 3 列元信息(`TimeStamp`、`WTG`、`StationId`)+ 595 通道,**自洽**。
-
-观澜的**契约**只覆盖其中一部分:`reference/<场>/windscada_contract.yaml` 登记 10 个组共 **164 列**(活 155 列),**未契约列 435 列**已登记、不假装覆盖。canonical 词典侧共 **778 条**测点,按系统分布如下(前 8):
-
-**表 5-2 canonical 测点的系统分布(`configs/canonical/dictionary.yaml`,样本场实测 2026-09-22)**
-
-| system | 词条数 | 含义 |
-|---|---|---|
-| WROT | 136 | 叶轮与变桨(含轮毂、桨距、主轴) |
-| WTRM | 113 | 传动链与齿轮箱(含机械制动) |
-| WMET | 107 | 气象与风测量 |
-| WTUR | 96 | 机组级(功率、状态、可用率、电量) |
-| WGEN | 88 | 发电机 |
-| WNAC | 54 | 机舱与主控柜 |
-| WCNV | 51 | 变流器 |
-| WYAW | 47 | 偏航 |
-
-词典中 `unit_majority` 为空(未取证单位)的词条 **198 条**;温度类单位取值以 `degC`(182 条)与 `C`(61 条)两种写法并存,**两者同量纲**,本文统一写作 ℃。另有 **55 条**词条带 `unit_variants`(例如 ℃ 的 `C`/`degC`、风速的 `m/s`/`mps`、频率的 `Hz`/`mHz`),跨源合并时必须按变体表归一。
-
-### 5.2 判级与曲线实际取用的 48 列公共子集
-
-四个判级面(变桨、偏航、蓄能、温度)与七镜头曲线不是读全部 595 通道,而是读一个**48 列公共子集**(`scripts/scada_slim_build.py` 的 `COLS_CORE` 23 列 + 温度 NBM 的 27 个 `_mean`,去重后 48 列)。该子集落成按台一份的**窄仓** `outputs/<场>/windscada/slim10min/<台号>.parquet`,进重算链 ③b 步。
-
-#### 5.2.1 48 列公共子集清单
-
-**表 5-3 48 列公共子集**
-
-| 列名 | 中文含义 | 单位 | 必须性 | 用途 |
-|---|---|---|---|---|
-| `grd_wtc_ActPower_mean` | 网侧有功功率 均值 | kW | 必须 | 五态判定、功率分箱、发电态门(全场每条分析线的输入) |
-| `grd_wtc_ActPower_max` | 网侧有功功率 最大值 | kW | 必须 | 控制参数 M9:功率封顶 p_cap = 99.5 分位 |
-| `tur_wtc_GenRpm_max` | 发电机转速 最大值 | r/min | 必须 | 控制参数 M9:转速封顶 w_cap |
-| `tur_wtc_PowerRef_endvalue` | 有功功率给定 期末值 | kW | 必须 | 限电命令面分类(缺它直接 KeyError) |
-| `tur_wtc_SecAnemo_mean` | 第二风速计(原生)均值 | m/s | 必须 | 曲线 X 轴 + 低风待机门(唯一活风源) |
-| `tur_wtc_GenRpm_mean` | 发电机转速 均值 | r/min | 必须 | 曲线、转矩、叶尖速比 |
-| `tur_wtc_MainSRpm_mean` | 叶轮转速 均值 | r/min | 必须 | 曲线、转矩、叶尖速比 |
-| `tur_wtc_PitcPosA_mean` | A 叶片桨距角 均值 | ° | 必须 | 曲线、三叶不平衡 |
-| `tur_wtc_PitcPosB_mean` | B 叶片桨距角 均值 | ° | 必须 | 曲线、三叶不平衡 |
-| `tur_wtc_PitcPosC_mean` | C 叶片桨距角 均值 | ° | 必须 | 曲线、三叶不平衡 |
-| `tmp_wtc_AmbieTmp_mean` | 环境温度 均值 | ℃ | 必须 | 空气密度口径、热汇参考(季节解耦闸的基准) |
-| `prs_wtc_YawPress_mean` | 偏航压力 均值 | bar | 必须 | 偏航面 |
-| `prs_wtc_YawPress_max` | 偏航压力 最大值 | bar | 必须 | 偏航面 |
-| `prs_wtc_YawPress_min` | 偏航压力 最小值 | bar | 必须 | 偏航面 |
-| `dot_wtc_YawLubPu_timeon` | 偏航润滑泵 通电时长 | s | 必须 | 偏航面(润滑 duty) |
-| `din_wtc_Y1LubPis_timeon` | 1 号润滑柱塞 通电时长 | s | 必须 | 偏航面/润滑柱塞 |
-| `flg_wtc_ScYawOpe_counts` | 偏航动作 计数 | 次 | 必须 | 偏航面(动作次数) |
-| `cnt_wtc_WindFauS_endvalue` | 风速故障(S 侧)累计值 | 次 | 必须 | 主控与传感网面(测风故障计数) |
-| `cnt_wtc_WindFauT_endvalue` | 风速故障(T 侧)累计值 | 次 | 必须 | 主控与传感网面(测风故障计数) |
-| `prs_wtc_HydPress_mean` | 液压系统压力 均值 | bar | 必须 | 蓄能面 |
-| `prs_wtc_HydPress_max` | 液压系统压力 最大值 | bar | 必须 | 蓄能面(封顶与保压) |
-| `prs_wtc_HydPress_min` | 液压系统压力 最小值 | bar | 必须 | 蓄能面(衰减与预充压力) |
-| `tmp_wtc_HydOilTm_mean` | 液压油温 均值 | ℃ | 必须 | 蓄能面(同油温档) |
-| `tmp_wtc_A1ExtTmp_mean` | A21 主开关板柜外温 均值 | ℃ | 建议 | 主控与传感网(柜温轴) |
-| `tmp_wtc_A21IntTm_mean` | A21 柜内温 均值 | ℃ | 建议 | 主控与传感网 |
-| `tmp_wtc_A3LefTmp_mean` | A3 柜左温 均值 | ℃ | 建议 | 主控与传感网 |
-| `tmp_wtc_A3RigTmp_mean` | A3 柜右温 均值 | ℃ | 建议 | 主控与传感网 |
-| `tmp_wtc_BrkTmpGn_mean` | 刹车温度(发电机侧)均值 | ℃ | 建议 | 齿轮箱侧(高速轴刹车) |
-| `tmp_wtc_BrkTmpGr_mean` | 刹车温度(叶轮侧)均值 | ℃ | 建议 | 齿轮箱侧(高速轴刹车) |
-| `tmp_wtc_ConvWTmp_mean` | 变流器冷却水温度 均值 | ℃ | 必须 | 热链热汇(绕组−冷却水、齿箱油−冷却水) |
-| `tmp_wtc_GFilB1Tm_mean` | 齿轮箱油滤 B1 温度 均值 | ℃ | 建议 | 冷却回路冷侧(GeOil 减 GFilB1 = 冷却器温降) |
-| `tmp_wtc_GeOilTmp_mean` | 齿轮箱油温 均值 | ℃ | 必须 | 齿轮箱即时热汇(ΔT 判据主用) |
-| `tmp_wtc_Gen1U1Tm_mean` | 发电机定子 U 相绕组温度 均值 | ℃ | 必须 | 发电机面、热链节点、三相极差 |
-| `tmp_wtc_Gen1V1Tm_mean` | 发电机定子 V 相绕组温度 均值 | ℃ | 必须 | 发电机面、热链节点、三相极差 |
-| `tmp_wtc_Gen1W1Tm_mean` | 发电机定子 W 相绕组温度 均值 | ℃ | 必须 | 发电机面、热链节点、三相极差 |
-| `tmp_wtc_GenBeGTm_mean` | 发电机轴承 NDE(非驱动端)温度 均值 | ℃ | 必须 | 发电机面、配对差(NDE 减 DE) |
-| `tmp_wtc_GenBeRTm_mean` | 发电机轴承 DE(驱动端)温度 均值 | ℃ | 必须 | 发电机面、配对差(NDE 减 DE) |
-| `tmp_wtc_HSGenTmp_mean` | 齿轮箱高速轴(发电机侧)温度 均值 | ℃ | 必须 | 齿轮箱面、热链(高速轴−油) |
-| `tmp_wtc_HSRotTmp_mean` | 齿轮箱高速轴(叶轮侧)温度 均值 | ℃ | 必须 | 齿轮箱面、热链(高速轴−油) |
-| `tmp_wtc_HubTemp_mean` | 轮毂温度 均值 | ℃ | 建议 | 轮毂热环境 |
-| `tmp_wtc_IMSGenTm_mean` | 齿轮箱中间轴(发电机侧)温度 均值 | ℃ | 必须 | 齿轮箱面、热链(中速轴−油) |
-| `tmp_wtc_IMSRotTm_mean` | 齿轮箱中间轴(叶轮侧)温度 均值 | ℃ | 必须 | 齿轮箱面、热链(中速轴−油) |
-| `tmp_wtc_MBearGTm_mean` | 主轴承(齿轮箱侧)温度 均值 | ℃ | 必须 | 主轴承面、配对差(双温差) |
-| `tmp_wtc_MBearHTm_mean` | 主轴承(轮毂侧)温度 均值 | ℃ | 必须 | 主轴承面、配对差(双温差) |
-| `tmp_wtc_NacelTmp_mean` | 机舱温度 均值 | ℃ | 建议 | 热汇参考、主控与传感网 |
-| `tmp_wtc_TraOilTF_mean` | 机舱变压器油温 均值 | ℃ | 建议 | 变流器/变压器面(油温—室温温升) |
-| `tmp_wtc_TraRooTF_mean` | 变压器层空气温 均值 | ℃ | 建议 | 变流器/变压器面(温升口径) |
-| `tmp_wtc_TrafoTMa_mean` | 变压器油温(同源副本)均值 | ℃ | 可选 | 与 `TraOilTF` 为**同一条信号**(完全相等 69.67%、相关系数 0.99998757),**禁作第二独立证据** |
-
-**关于单位取证的如实说明**:上表 48 列中,**42 列**能追到 `configs/canonical/dictionary.yaml` 的词条(见第 5.3 节与 5.4 节的逐条 canonical 编号);余下 **6 列**(`tmp_wtc_A21IntTm_mean`、`tmp_wtc_A3LefTmp_mean`、`tmp_wtc_A3RigTmp_mean`、`tmp_wtc_BrkTmpGn_mean`、`tmp_wtc_BrkTmpGr_mean`、`tmp_wtc_ConvWTmp_mean`)在词典里**查不到同名列**,属于"契约 `columns` 段之外"。它们的量纲可由通道命名族(`tmp_` = 温度类目)判定,但**本文不据此补单位**,一律标注 **未取证**,需现场或厂商图纸确认后再入契约。
-
-### 5.3 温度通道(27 个 `_mean` 温度测点与档带宽判据)
-
-温度 NBM(Normal Behavior Model,正常行为模型)按"发电态 × 功率档(500 kW)"做**同工况机群相对偏差**:`ref` 为 2025-07-01 至 2025-12-31 的干净时间窗,`cur` 为末 90 天(或用户所选时间窗),`ref_sm` 为与 `cur` 同历月、早一年的季节匹配时间窗。每台每通道按 `pbin` 分组取中位数(组内样本 `n ≥ 18`),对该通道的机群中位数取偏差,再按 `n` 加权得 `dev_K`(K 即开尔文差,与 ℃ 温差同量纲);档带宽判据为:
-
-**表 5-4 温度 NBM 档带宽判据**
-
-| 判据 | 阈值 | 结论 |
-|---|---|---|
-| 样本不足 | 行数 < 500 | 不可判(样本不足/返服嫌疑) |
-| 传感器不可信 | `dev_K ≥ 15.0 K` | 传感器候选(A 类,非热) |
-| 温度候选 | `dev_K ≥ 4.0 K` 且 `z ≥ 3.0` | 温度候选(定向查) |
-| 记基线观察 | `dev_K ≥ 2.5 K` 且 `z ≥ 2.0` | 记基线观察 |
-| 其余 | — | 不偏离机群(**判"—"不等于该台正常**) |
-
-`z` 的尺度为 `1.4826 × MAD`(中位绝对偏差,稳健尺度),分母下限 0.3 K。另有**批次盲声明**:`dev_K` 是机群相对量,全场同步平移时逐值不变,因此本表**看不见批次退化**(需看共模图 `common_shift()`)。
-
-实测的 27 个活温度 `_mean` 通道及其 canonical 依据如下(契约 `B.温度NBM` 组共 58 列,其中 `_mean` 29 列;`GFilB2`/`GFilB3` 为全场恒 200 哨兵、`liveness=恒值哨兵`,**已被排除**,故活通道 27 个)。
-
-**表 5-5 27 个温度通道与单位依据(样本场实测 2026-09-22)**
-
-| 契约列名 | canonical 编号(词典键) | system | 单位 | 判级中的角色 |
-|---|---|---|---|---|
-| `tmp_wtc_A1ExtTmp_mean` | `WNAC_cabinet_ext_temp` | WNAC | ℃ | 主控与传感网(柜外温) |
-| `tmp_wtc_A21IntTm_mean` | 未取证 | — | 未取证 | 主控与传感网 |
-| `tmp_wtc_A3LefTmp_mean` | 未取证 | — | 未取证 | 主控与传感网 |
-| `tmp_wtc_A3RigTmp_mean` | 未取证 | — | 未取证 | 主控与传感网 |
-| `tmp_wtc_AmbieTmp_mean` | `WMET_ambient_temp` | WMET | ℃ | 热汇参考 + 季节解耦闸基准 |
-| `tmp_wtc_BrkTmpGn_mean` | 未取证 | — | 未取证 | 齿轮箱(高速轴刹车) |
-| `tmp_wtc_BrkTmpGr_mean` | 未取证 | — | 未取证 | 齿轮箱(高速轴刹车) |
-| `tmp_wtc_ConvWTmp_mean` | 未取证 | — | 未取证 | 热链热汇(冷却水) |
-| `tmp_wtc_GFilB1Tm_mean` | `WTRM_oilfilter_temp_1_b` | WTRM | ℃ | 齿轮箱(冷却回路冷侧) |
-| `tmp_wtc_GeOilTmp_mean` | `WTRM_oil_temp` | WTRM | ℃ | 齿轮箱即时热汇 |
-| `tmp_wtc_Gen1U1Tm_mean` | `WGEN_stator_temp_u` | WGEN | ℃ | 发电机(定子 U 相) |
-| `tmp_wtc_Gen1V1Tm_mean` | `WGEN_stator_temp_v` | WGEN | ℃ | 发电机(定子 V 相) |
-| `tmp_wtc_Gen1W1Tm_mean` | `WGEN_stator_temp_w` | WGEN | ℃ | 发电机(定子 W 相) |
-| `tmp_wtc_GenBeGTm_mean` | `WGEN_bearing_temp_nde` | WGEN | ℃ | 发电机(NDE 轴承) |
-| `tmp_wtc_GenBeRTm_mean` | `WGEN_bearing_temp_de` | WGEN | ℃ | 发电机(DE 轴承) |
-| `tmp_wtc_HSGenTmp_mean` | `WTRM_hss_gen_temp` | WTRM | ℃ | 齿轮箱(高速轴发电机侧) |
-| `tmp_wtc_HSRotTmp_mean` | `WTRM_hss_rot_temp` | WTRM | ℃ | 齿轮箱(高速轴叶轮侧) |
-| `tmp_wtc_HubTemp_mean` | `WROT_hub_temp` | WROT | ℃ | 轮毂热环境 |
-| `tmp_wtc_HydOilTm_mean` | `WHYD_oil_temp` | WHYD | ℃ | 变桨/蓄能(同油温档) |
-| `tmp_wtc_IMSGenTm_mean` | `WTRM_ims_gen_temp` | WTRM | ℃ | 齿轮箱(中速轴发电机侧) |
-| `tmp_wtc_IMSRotTm_mean` | `WTRM_ims_rot_temp` | WTRM | ℃ | 齿轮箱(中速轴叶轮侧) |
-| `tmp_wtc_MBearGTm_mean` | `WROT_mainbrg_g_temp` | WROT | ℃ | 主轴承(齿轮箱侧) |
-| `tmp_wtc_MBearHTm_mean` | `WROT_mainbrg_h_temp` | WROT | ℃ | 主轴承(轮毂侧) |
-| `tmp_wtc_NacelTmp_mean` | `WNAC_temp` | WNAC | ℃ | 机舱(热汇参考之一) |
-| `tmp_wtc_TraOilTF_mean` | `WTRF_oil_temp` | WTRF | ℃ | 变压器(油温) |
-| `tmp_wtc_TraRooTF_mean` | `WTRF_room_air_temp` | WTRF | ℃ | 变压器(层空气温) |
-| `tmp_wtc_TrafoTMa_mean` | `WTRF_oil_temp_dup` | WTRF | ℃ | 变压器(与上者同源,禁作独立证据) |
-
-温度通道 → 系统归属(用于分系统取严合并):主轴承 = `MBearGTm`、`MBearHTm`;齿轮箱 = `GeOilTmp`、`IMSGenTm`、`IMSRotTm`、`HSGenTmp`、`HSRotTmp`、`GFilB1Tm`、`GFilB2Tm`、`GFilB3Tm`、`BrkTmpGn`、`BrkTmpGr`;发电机 = `GenBeGTm`、`GenBeRTm`、`Gen1U1Tm`、`Gen1V1Tm`、`Gen1W1Tm`;变流器 = `ConvWTmp`、`TrafoTMa`、`TraOilTF`、`TraRooTF`;主控与传感网 = `A1ExtTmp`、`A21IntTm`、`A3LefTmp`、`A3RigTmp`、`NacelTmp`、`HubTemp`;变桨 = `HydOilTm`;环境温度 `AmbieTmp` 为热汇参考**不归系统**。
-
-**两组配对差**(同台双测点,用于区分"局部摩擦"与"测量偏差"):主轴承双温差(`MBearHTm` 减 `MBearGTm`)、发电机轴承 NDE 减 DE(`GenBeGTm` 减 `GenBeRTm`)。配对差的机群相对偏移 `|pair_dev| ≥ 4 K` 时计入对应系统的"良好"级证据。
-
-**已知的实测边界**(用数据前必须处理,契约已逐条记):`GFilB2Tm`/`GFilB3Tm` 全场 38 台恒 200(哨兵,未接线或未标定)→ 已清零为缺失;`AmbieTmp` 实测上界 123.4 ℃(物理不可能,仅 1 行);`NacelTmp` 上界 122.9 ℃(1 行);`HubTemp` 下界 −26.7 ℃(1 行);`HydOilTm` 上界 146.6 ℃(2 行)——界外一律转缺失(**不是**压到边界上)。
-
-### 5.4 CMS 振动测点与采样、波形
-
-**测点(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`。
-
-收资要求 v3 第 8 项点名的是"主轴、齿轮箱、发电机、叶轮转速"——上表覆盖前三者;**叶轮转速**在导出里以 `rpm` 标量、`RpmProfile`、`rawRPM` 三条形式存在(是 1P 与阶次跟踪的分母)。
-
-**测量类型(`meas_type`,15 类)**:`AutoSpectrum`、`TrackedAutoSpectrum`、`TrackedCepstrum`、`TrackedEnvelopeAutoSpectrum`、`TrackedEnvelopeTimeSeries`、`TrackedTimeSeries`、`TimeSeries`、`CrestFactor`、`Kurtosis`、`Peak`、`RmsOverAll`、`RpmProfile`、`Health`、`Disk Usage`、`Memory Usage`。
-
-**测量名(`meas_name`,126 类)**;**测量源(`meas_source`,46 类)**。谱的测量名只转 `FFT_*`,实测 13 类进了谱库(`FFT_1000`、`FFT_1000_vel`、`FFT_16000`、`FFT_2000_Tr`、`FFT_2000_fast_Tr`、`FFT_250_Tr`、`FFT_250_fast_Tr`、`FFT_4_Tr`、`FFT_6000_Tr`、`FFT_62_Tr`、`FFT_8_Tr`、`FFT_10000_Tr`、`FFT_rawRPM`)。
-
-**单位**:`y_unit` 实测 5 种取值——`1`(无量纲指标,含 `CrestFactor`、`Kurtosis`、`Peak`、`Rms_*`、`iso_rms*`、`Indicator_*`、`Tooth_damage_indicator_*`)、`m/s`(速度)、`m/s²`(加速度)、`RPM`(转速)、`%`(磁盘与内存占用)。`x_unit` 3 种:`Hz`(谱)、`s`(波形)、`1`。**量纲闸**是这个链路的关键:标量分析只留 `y_unit ∈ {1, m/s, m/s²}`——这一条是逐值验过的(加它之后 `val` 列做到了 8,887 对 8,887 全一致)。
-
-**采样与波形**:每件 `*_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 个部件,样本场实测 2026-09-22)**
-
-| 部件 | 测点 | 用哪条谱(shard) | 频率分辨率 Hz |
-|---|---|---|---|
-| `generator_de_6338` | `Generator_DE` | `Env_6000_4000_850_Tr` | 1.0625 |
-| `generator_nde_6338` | `Generator_NDE` | `Env_6000_4000_850_Tr` | 1.0625 |
-| `hs_generator_left` | `Gear_HS_generator_side` | `FFT_6000_Tr` | 0.9375 |
-| `hs_generator_right_fag` | `Gear_HS_generator_side` | `FFT_6000_Tr` | 0.9375 |
-| `hs_generator_right_nsk` | `Gear_HS_generator_side` | `FFT_6000_Tr` | 0.9375 |
-| `hs_rotor` | `Gear_HS_rotor_side` | `FFT_6000_Tr` | 0.9375 |
-| `ims_generator_fag` | `Gear_IMS` | `FFT_2000_fast_Tr` | 0.625 |
-| `ims_generator_nsk` | `Gear_IMS` | `FFT_2000_fast_Tr` | 0.625 |
-| `ims_rotor` | `Gear_IMS` | `FFT_2000_fast_Tr` | 0.625 |
-| `main_front` | `Main_bearing_front` | `FFT_62_Tr` | 0.03875 |
-| `main_rear` | `Main_bearing_rear` | `FFT_62_Tr` | 0.03875 |
-
-谱轴取数配方:`x = x_offset + arange(n_points) * x_delta`,`n_points = lines + 1`(实测内部一致性比例 1.000)。
-
-**实测覆盖**:本机只有一个窗 `w0316`(2026-03-16 17:27:22 至 2026-04-21 10:18:33,约 36 天,**不足一个季度**),38 台齐;索引 2,066,686 行(标量行 861,917、谱行 1,204,769);谱 420,742 条、1,701 个 npz 分片、3.15 GB。
-
-### 5.5 测风塔测点
-
-**该族未交付**,因此下表是"按收资要求 v3 第 3 项写出的要求清单",观澜侧的取数口径**为未取证**;凡本机词典或契约里能找到同族证据的,逐条标出依据。
-
-**表 5-7 测风塔测点要求(对照 canonical 词典里已存在的同族测点)**
-
-| 收资要求 v3 的测点 | 本机是否有同族 canonical 测点 | 依据 | 单位 |
-|---|---|---|---|
-| 温度 | 有(机舱/环境族) | `WMET_ambient_temp`、`WMET_env_temp` | ℃ |
-| 湿度 | 有 | `WMET_humidity` | % |
-| 气压 | 有 | `WMET_pressure`(kPa)、`WMET_pressure_2`(Pa)、`WMET_pressure_mast`(hPa) | kPa 或 Pa 或 hPa |
-| 不同层高风速 | 有(层高 10/30/50/70 m 与轮毂) | `WMET_ws_10m`、`WMET_ws_30m`、`WMET_ws_50m`、`WMET_ws_70m`、`WMET_ws_hub`、`WMET_mast_ws_10m`、`WMET_mast_ws_80m`、`WMET_wind_speed_mast_hub` | m/s |
-| 不同层高风向 | 有 | `WMET_wd_10m`、`WMET_wd_30m`、`WMET_wd_50m`、`WMET_wd_70m`、`WMET_wd_hub`、`WMET_mast_wd_10m`、`WMET_mast_wd_80m` | ° |
-| 风速标准差 | **未取证** | 词典有 `WMET_TI`(ratio)、`WMET_turbulence_intensity`(frac)、`WMET_wind_ti`、`WMET_wind_ti_std`,但均为**未验证**级,且未确认来自测风塔 | 未取证 |
-
-**要求补充说明**:收资要求 v3 允许两条路径——若运行期分钟级数据**具备**风速标准差,则字段为"温度、湿度、气压、不同层高的风速、风速标准差、风向";若**不具备**,则需采样频率 **1 秒**的一整年分钟级数据,字段为"温度、湿度、气压、不同层高的风速、风向"。前期实地测风要求与可研报告时间一致、**至少满一年**。
-
-### 5.6 机型与部件参数
-
-**表 5-8 机型级参数(样本场实测 2026-09-22,已取证项)**
-
-| 参数 | 取值 | 单位 | 依据 |
-|---|---|---|---|
-| 机型 | 4.0 MW 级海上机组(机型代号从略;OEM 许可,海上固定式) | — | `configs/contracts/<机型>_<场>.yaml` |
-| 传动拓扑 | 异步全功率变流 + 齿轮箱 + 液压集中变桨 | — | 同上 |
-| 额定功率 | 4000 | kW | 同上(实证平台 3988 至 3992 kW) |
-| 叶轮直径 | 130 | m | 同上 |
-| 轮毂高度 | 87(= 73 塔筒 + 14 平台) | m | 同上 |
-| 齿轮箱传动比 | 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` 注记 |
-| 场站机组数 | 38 | 台 | 场配置 |
-| 切入风速、切出风速、额定风速、生存温度 | **未取证** | — | 契约与词典均无该键;需现场技术协议或机型参数表 |
-
-**表 5-9 部件级参数(收资要求 v3 第 21 项逐项)**
-
-| 收资要求 v3 的部件参数 | 样本场取证情况(2026-09-22) |
-|---|---|
-| 主轴轴承型号及厂家 | 部分取证(`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) |
-| 发电机型号厂家 | **未取证**(技术资料中未见机型级发电机型号条目) |
-| 发电机轴承型号 | 部分取证(`oem_bearing_freqs.json` 中 DE/NDE 的 `6338 M/C3`) |
-| 叶片参数 | **未取证** |
-| 变桨电机参数 | **未取证** |
-| 变桨驱动小齿齿数、大齿齿数 | **未取证** |
-| 偏航电机参数 | **未取证** |
-| 偏航驱动小齿齿数、大齿齿数 | **未取证** |
-
-振动侧另有 7 个随机型常量固化的扫描配置(`oem_scan_plan.json` 的 11 个部件),其 `option` 串到理论频率表的映射规则已实测 **11/11 逐值一致**。
-
-### 5.7 状态、事件与故障码表
-
-**状态与状态位**:
-
-**表 5-10 状态与状态位通道**
-
-| 通道 | 语义 | 依据 |
-|---|---|---|
-| `WTUR_fault_code`(`风机SC状态`) | 220 个取值的事件/故障码;0 = 无故障主态(占 86.4%,对应功率中位 711 kW);1xxx 为停机码;2006、19094、2132 为发电中信息码 | 1 分钟契约 |
-| `WTUR_state_static`(`风机状态遥测转发`) | **台常数**元数据(01E=8,04E/05C/17B=19,其余 34 台=10),无时序信息 | 1 分钟契约(`dead_channel: constant_per_turbine`) |
-| `WTUR_brake_state`(`刹车状态AI`) | 活状态位,取值 0/61/80/98/112;**61 = 发电态**(功率均值 1227 至 1446 kW),80 与 98 = 停机态(功率约 −10 kW) | 契约(2026-07-26 修正) |
-| `WTUR_availability`、`WTUR_avail_builtin_daily` | 可用率,%;**1 分钟层的 `风机可利用率` 是日粒度快照**(每日唯一值 1),采信序为 counter 重建 > 本列 > 自定义列(死列) | 契约 |
-
-**事件层(`sum` 层 `AlarmLog`)**:事件驱动、无固定采样率。主键为 `ID`(全场库自增、全表唯一)。机组行筛选取 `StationNr ∈ [4800167, 4800204] ≡ 台 01 至 38`(等于 4800166 + 台号);非机组行有场级 9999、电能质量仪 91、场控子实体 88/90/100001。
-
-**表 5-11 事件层(AlarmLog)列语义**
-
-| 事件列 | 语义 |
-|---|---|
-| `TimeOn` | 激活时刻(微秒精度) |
-| `TimeOff` | 复位时刻,美式秒级字符串 `MM/DD/YY HH:MM:SS`;机组未闭合仅 16 条(0.03%) |
-| `LogType` | 0 = 告警区间账;1 = 瞬时事件/操作日志;2 = 停机相关状态区间账(候选) |
-| `AlarmGroup` | 0 = OPC 信号与远程操作;1 = 机组本体告警事件;5 = 场控(ParkPilot) |
-| `StationNr` | 实体标识(见上) |
-| `Alarmcode` | **三个命名空间禁混判**:alarm(机组,364 个用码)/ event(机组,66 个用码)/ 平台 50xxx 与 73xxx(19 个码,无字典) |
-| `Parameter` | 辅助文本(定宽右填空格,须 strip) |
-| `UserName` | 记录来源通道 |
-| `idKey`、`Comment`、`MailSend`、`Acknowledge`、`Reset` | 部分未用或为死列 |
-
-**故障码表**:场站故障代码表(样本 **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)。
-
-### 5.8 控制与保护定值(M9 实际取用的控制参数)
-
-控制参数一致性件(M9)**不读定值表**,而是从 SCADA 数据**反演**四轴:
-
-**表 5-12 控制参数一致性件(M9)的四轴**
-
-| 轴 | 口径 | 取用通道 |
-|---|---|---|
-| C1 双封顶 | 功率封顶 = 有功最大值 p99.5;转速封顶 = 发电机转速最大值 p99.5;按容差 25 kW / 8 r/min 分组,离群台列出 | `grd_wtc_ActPower_max`、`tur_wtc_GenRpm_max` |
-| C2 转矩系数聚类 | `K = P / ω³`(中载区 1200 至 2800 kW),取中位数 × 10⁶ | `grd_wtc_ActPower_mean`、`tur_wtc_GenRpm_mean` |
-| C3 桨距调度差 | 桨距角 × 功率档(500 kW 档)逐台 schedule,与全场同档中位比,平均绝对偏 > 0.5° 判"标定偏移候选" | `tur_wtc_PitcPosA_mean` |
-| C4 尺子巡检 | 满发功率 p99.5、转速封顶、额定桨角(> 3800 kW 时 A 叶桨距角中位)三把尺子的机群散布 | 同上 |
-
-**正式的机组保护定值**在 `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 项点名的是"温度、压力、速度等保护定值 + 偏航控制策略 + 降载荷控制策略"的**策略文档**。偏航控制策略与降载荷控制策略目前**未取证**,只能从控制参数反演与事件层行为间接反映。
-
-## 6 数据质量与对齐要求
-
-### 6.1 台号命名与别名
-
-**表 6-1 台号命名与别名**
-
-| 场景 | 命名形态 | 说明 |
-|---|---|---|
-| 场配置机组号 | `WTG01` 至 `WTG38` | 10 分钟族用;缺台即空白 |
-| 10 分钟内部台号 | `01E` 至 `38B` | 1 分钟族用;字母 A 至 F 为集电线路 string,**组内不连续** → 邻域分析须用坐标 |
-| 事件层实体号 | `StationNr` 4800167 至 4800204 | 等于 4800166 + 台号 |
-| 现场类目库 | `StationId` 4800167 至 4800204 | 与上一致 |
-| 事件层台号列 | `tid` = `NN` + string 字母 | 装配时加列,非机组行为 NaN |
-
-**要求**:主件文件名必须是场配置里的机组号;1 分钟族用内部台号(按**台数**判完整,不按名字判)。台号别名(`01E` 与 `WTG01` 的对应)必须显式给一张对照表,禁止靠位置或排序猜。表中 `WTG01` 至 `WTG38`、`01E` 至 `38B`、`StationNr` 4800167 至 4800204 都是**样本场内部编号,换场按实际台号替换**;编号形态(`WTG<NN>` 与 `<NN><线路字母>`)可以沿用,但台号与内部台号的对应关系必须显式声明。
-
-### 6.2 时间戳格式与时区
-
-**表 6-2 时间戳格式与时区**
-
-| 源 | 时间戳写法 | 处理 |
-|---|---|---|
-| 10 分钟 CSV 导出 | `YYYY-MM-DD HH:MM:SS+00:00` | 按 ISO 解析;落成补充件时统一为 `YYYY-MM-DD HH:MM:SS` |
-| 10 分钟 MDB | `MM/DD/YYYY HH:MM:SS` | 解析后按 float32 + ISO 对齐 |
-| 1 分钟 CSV | `YYYY-MM-DD HH:MM:SS` | 契约 `layout.time_format` |
-| 事件层 `TimeOn` | datetime64 微秒 | 直接解析 |
-| 事件层 `TimeOff` | `MM/DD/YY HH:MM:SS` 美式秒级字符串 | 按 `%m/%d/%y %H:%M:%S` 解析(零损失) |
-
-**时区**:导出带 `+00:00` 的按 UTC 记录,落位后统一为无时区的本地时刻字符串——**两侧必须同一约定**,否则同一列会跨日分层。
-
-### 6.3 列名绑定,禁位置索引
-
-**硬要求:一切读取按列名绑定,禁用位置索引。** 依据:本场 38 台**列序互不相同**(契约 `layout.column_binding: name_based_only`),按位置取数会得到"名字对不上数"的数据。补充件与新导出还会去掉类型前缀(`din_wtc_X` 变成 `wtc_X`),因此对齐也**只能按列名**。
-
-### 6.4 float32 量化与精度
-
-现场 CSV 导出以 **float32** 落盘、MDB 存 double。合成同台补充件时按 **float32** 对齐。实测重叠 **19,555,865 格**中,"两侧都有值却不等"的格数为 **0**——即两种形态在 float32 口径下逐值一致。
-
-另有已知的**量化与哨兵族**(用数据前必处理):
-
-**表 6-3 量化与哨兵族(样本场实测 2026-09-22)**
-
-| 现象 | 实测 |
-|---|---|
-| 1 分钟风速为**补偿风** | 与原生 `SecAnemo` 差约 −1.35 m/s |
-| 1 分钟温度量化 | 1 ℃ |
-| 1 分钟功率量化 | 1 kW |
-| 哨兵 `999` | `active_power`、`generator_speed` 等列 |
-| 哨兵 `200` | 齿箱滤芯 B2/B3(全场恒 200)、`outside_cabin_temperature`(p99 = 31,global_max = 200) |
-| 哨兵 `0.0` | 变压器油温族(采集掉线填充:与 `TraRooTF`/`TrafoTMa`/`A1ExtTmp` 同时刻同为 0 共 15 行) |
-| 负压族 | 桨叶油压实测低至 −621 bar → 转缺失 |
-| 界外 → 缺失(**非** clamp) | 契约 `sop/contract_gate.py` 口径:越物理界 = 无效测量 |
-
-### 6.5 去重与重叠格
-
-同台主件与补充件按**时间戳去重,主件优先**;件名、各件行数、重叠格数会打印并记进产物 `attrs`。报警台账按**键集合并集**去重(键 = `Name`/`Alarmcode`/`TimeOn`),因此累计快照件不会把同一事件算两遍,加减文件也不会让总数乱跳。
-
-### 6.6 残月与时间空洞
-
-- **尾月样本很少也如实出**:实测补充件最后一个时间戳是 `2026-09-01 00:00`,于是温度月表里会多出一个**只有 1 行的 2026-09**,时间覆盖图里也就多一格。口径是:**产物是当前 `data/raw` 的函数**——源件里有这个时间戳就该看得见,加"最少样本"门会让"数据到没到"变成猜的。
-- **时间空洞**:体检器 R 系列规则会报"缺源类或时间空洞"(退出码 7)。整族缺台报"按台取数会空白"。
-
-### 6.7 单位与量纲
-
-单位只认两处真源:`dictionary.yaml` 的 `unit_majority`/`unit_variants` 与契约的 `unit` 字段。注意三个容易混的点:
-
-1. **温度**:词典里 `degC`(182 条)与 `C`(61 条)并存,**同量纲**;
-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` 复核。
-
-### 6.8 编码与列名约定
-
-- **CSV 编码必须 `utf-8-sig`**(含 BOM),否则中文列名会读成乱码;1 分钟层的列名为**中英混合**。
-- **缺列如实记**:窄仓构建时缺列写进 `columns_missing`,**不造 0、不静默丢列**;各面按列名取数,缺了就当"不可判"。
-- **Access 列名消毒**:库内列名不能含 `.` `!` `[` `]`,建库时被消毒(非法字符换 `_`、截断 60、撞名加 `_1`);读侧必须用同一套规则把契约列名翻成库内列名再翻回,否则会**静默少列**。
-- **行序按 `rid` 数值排**(库内是文本,直接合并会得到 `1,10,100…` 的字典序)。
-
-## 7 交付方式与落位流程
-
-### 7.1 放置指导
-
-正确形态只有一条判据:**源类目录 → 再往下一层就是文件**。
-
-**表 7-1 约定的目录形态**
-
-| 位置 | 放什么 | 形态要求 |
-|---|---|---|
-| `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` |
-| `<机型>技术资料\` | 厂商技术资料(与场站并列) | 整棵树都读,不在场站目录下 |
-
-### 7.2 增量三判定
-
-**"增量" = 只补新数据,不要重放全量。** 放置工具与体检器都按**同名同尺寸**判断:
-
-**表 7-2 增量三判定**
-
-| 情况 | 判定 | 处置 |
-|---|---|---|
-| 目标不存在 | 新增 | 拷进去 |
-| 目标存在、大小相同 | 相同(跳过) | 不重复拷(源件是只读输入,同尺寸视为同一份) |
-| 目标存在、大小不同 | 冲突 | **必须人确认**:是"现场更正了数据"还是"这是另一批";落位工具默认**拒绝**落位,要覆盖须显式加 `--force` |
-
-**为什么不让它悄悄覆盖**:源件是**只读输入**,覆盖不可逆;而台账类产物(报警 39,211 / 工单 5,876 / 油样 404)是**逐行对齐源件**的,覆盖后若没重算,页面与源件就对不上了。
-
-### 7.3 `raw_data_check.py` 与 `place_raw_data.py` 用法
-
-用法分三步,顺序不能颠倒:
-
-1. 先看会新增、跳过、冲突什么(不写盘):`python scripts\raw_data_check.py --src D:\现场增量包`;
-2. 自动落位(落位前会再体检一次,有冲突会被拦下):`python scripts\place_raw_data.py --src D:\现场增量包`;确认要覆盖时才加 `--force`;
-3. 落完再体检一次(含可读性抽样:CSV 编码与表头、TCM json 字段):`python scripts\raw_data_check.py --deep`。
-
-`place_raw_data.py` 另有范围开关:`--scope a2`(默认,四类台账与 SCADA)、`--scope mech`(技术资料 + 1 分钟)、`--scope vib`(振动侧两族)、`--scope full`(三组一起);`--dry-run` 先看计划,重跑幂等(同尺寸自动跳过)。
-
-### 7.4 体检退出码
-
-**表 7-3 `raw_data_check.py` 的退出码**
-
-| 退出码 | 含义 | 处置 |
-|---|---|---|
-| 0 | 合规(可能带提示) | 可以继续 |
-| 5 | 结构或命名违例 | 按提示把文件上提一层、改名或建两级目录 |
-| 6 | 增量冲突(同名不同大小) | 人工确认是更正还是另一批;确认覆盖才加 `--force` |
-| 7 | 缺源类或时间空洞 | 补对应台号或对应月份的件 |
-| 8 | 仅有提示(体量/无消费者目录/散装件) | 信息级,不阻断;但"无消费者"的件不会进产物 |
-
-另有一套**扫描器退出码**(`raw_scan.py --check`):0 = 无变化 · 4 = 有新数据(它会列出"该跑哪几步",**4 不是失败**)· 5 = 还没基线。
-
-### 7.5 放完之后跑什么
-
-放完先跑一次扫描,再按扫描结论重算:
-
-1. `python scripts\raw_scan.py --check` —— rc=0 无变化、rc=4 有新数据、rc=5 还没基线;
-2. `python scripts\rebuild_all.py --auto` —— 先扫一遍再重算;新数据落在被跳过(skip)的族里时会自动取消跳过。
-
-**表 7-4 新增源类 → 该跑什么**
-
-| 新增的源类 | 该跑的重算 | 预计 |
-|---|---|---|
-| `scada_10min` / `scada_1min` | `python scripts\rebuild_all.py`(含 10 个构建器) | 约 15 分钟 |
-| 同台补充件(如 `WTG01-B2.csv`) | 同上(取数层一并读入:按列名对齐 + 按时间戳去重、主件优先) | 同上 |
-| `故障报警` / `风机故障记录` / `油样报告` | `python scripts\rebuild_all.py --skip-scada` | 约 2 分钟 |
-| `windcms` / `m5_cms_tcm` | 同上(第 ④b 步自动摄入索引与谱 + 报告/在升闭环/三层基线) | 按数据量(153 GB 导出约 25 分钟索引与谱) |
-| `scada_mdb`(现场年度归档库) | 同 `scada_10min`(CSV 缺失时取数层回落到 MDB) | 同 ③ 与 ④ |
-| 删除或换掉上面任一类 | 同上(产物随之更新,删掉的行不再出现) | 同对应行 |
-| 只放了文档/技术资料 | 无需重算(不影响页面数值),但技术资料变了要重跑第 ⑦ 步本体层 | — |
-
-重算后核对锚点:报警 **39,211** · 工单 **5,876** · 油样 **404** · `temp_monthly` **19,494**(基线口径)· 本体 **9,702** 对象(审计 0 问题)。
-
-### 7.6 重算链的 14 步(为什么是这个顺序)
-
-重算链把 14 步写死,每步幂等、可整条重跑:
-
-**表 7-5 重算链的 14 步**
-
-| 步 | 动作 | 说明 |
-|---|---|---|
-| ① | 放数据 `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 分钟 |
-| ③b | SCADA 10 分钟窄仓 `scada_slim_build.py` | 48 列公共子集,按时间窗重算的底座,不裁剪行 |
-| ④ | 月度派生件 `windscada_monthly_build.py` | 逐值对齐随包件才落盘(缺基线返回 5,只跳过等价验收) |
-| ④b | 振动摄入 `vib_raw_build.py` | 索引与谱库,读 TCM 导出,约 14 分钟 |
-| ④c | 变桨面 `pitch_face_build.py` | 零位三口径 + 日粒度液压/润滑/柱塞 |
-| ④d | 振动在升与换件闭环 `component_history_build.py` | 窗数不足时如实空表并写数据边界 |
-| ⑤ | 补齐随包件 `products_restore_missing.py` | 无生成端的件从交付包按需补齐(缺源返回 6 跳过) |
-| ⑥ | 重启组件服务 | 只重启组件、保留网关 |
-| ⑦ | 本体六步 | `kb_ingest` → `populate` → `chain_ingest` → `trend_ingest` → 检索索引 → 实机参数表 |
-| ⑧ | 审计与交付 | `ontology.audit` 期望 0 问题;重装门户;页面归口审计;版本记录一致性 |
-
-**依赖不能换**:第 ⑦ 步的 `populate` 要吃第 ②③④④b⑤ 步的产物;第 ⑤ 步必须在第 ⑥ 步之前;第 ⑧ 步必须在产物全部就位之后。
-
-## 8 收资要求 v3 的 21 项逐条对照
-
-### 8.1 逐条对照主表
-
-**表 8-1 收资要求 v3 的 21 类 → 观澜侧判定**
-
-| 序号 | 数据类型 | 观澜是否必须 | 对应数据族/落位 | 状态 | 缺口与催缴要点 |
-|---|---|---|---|---|---|
-| 1 | 10 分钟 SCADA 数据 | 必须 | `scada_10min/` + `scada_mdb/` | 已在位 | 要求统计周期 2 年;实测主件加补充件覆盖 2025-01-01 至 2026-09-01,**未满 2 年**,建议补 2024 年 |
-| 2 | 秒级 SCADA 高频数据 | 建议 | `scada_1min/`(1 分钟) | 部分 | 要求 1 s 或 5 s、至少 3 个月;本机只有 **1 分钟**导出(38 件、2025-01-01 至 2026-06-30),1 s/5 s 原生高频未见交付 |
-| 3 | 测风塔数据 | 建议 | 未约定族 | 未到位 | 契约 `mast: FALSE` —— **零交付**。须催缴:前期实地测风至少满一年(5 或 15 分钟);运行期一整年分钟级;测点含温度、湿度、气压、不同层高风速与风向(有风速标准差则不必 1 s,无则需 1 s) |
-| 4 | 功率曲线 | 必须 | 随族落位 | 部分 | 合同担保曲线为**图片级**(须描点提取),标准空气密度曲线未见 |
-| 5 | 机型参数 | 必须 | 契约 meta + 事实卷 | 已在位 | 额定功率 4000 kW、叶轮直径 130 m、轮毂高度 87 m、传动比 119.752 已取证;**切入切出风速、额定风速、生存温度未取证**,须补 |
-| 6 | 机位分布数据 | 建议 | 未约定族 | 已在位(依契约记载) | 契约 `materials.coordinates` 记有"风机坐标.xlsx 38 台 + 升压站",但**该件不在 `data/raw` 任何族内、仓库内也搜不到**;坐标本身**未取证**,须现场实体交付 |
-| 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 | 机组保护定值及控制参数 | 必须 | `<机型>技术资料/` | 部分 | 整定值 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 | 风机技术协议及风机合同 | 必须 | `<机型>技术资料/` | 已在位 | 含担保曲线附件(图 2-1、图 2-3)、保证率条款 |
-| 16 | 并网到现在的生产报表 | 必须 | 随族落位 | 部分 | 日粒度发电账 555 天 + 全寿命月账自 2016-06 起;**"并网至今"完整序列与口径说明未成册** |
-| 17 | 周围已建风电场生产报表 | 可选 | 未约定族 | 未到位 | 未见交付;缺则尾流与场级损耗缺外部对照 |
-| 18 | 运维记录 | 建议 | `风机故障记录/` + `油样报告/` | 已在位 | 检修记录 2025 与 2026、油样 404 件(缺 2026-07 批 102 行的合并报告源件) |
-| 19 | 大部件更换记录 | 建议 | `风机故障记录/` | 部分 | 出保备件台账在位(`大部件维修记录.20240619143912557.xlsx` 一类);**更换台账不完整**,在升与换件闭环目前只有 1 个导出色窗、**如实为空** |
-| 20 | 图纸、技术文件 | 必须 | `<机型>技术资料/` | 已在位 | 电气原理图、接线表、液压图纸 3 份、主控图纸、故障逻辑、保护定值表、结构树均到位 |
-| 21 | 设备参数 | 必须 | `<机型>技术资料/` + `reference/<场>/` | 部分 | 主轴轴承、齿轮箱型号与各级齿数、发电机轴承、齿轮箱各级轴承部分在位;**叶片参数、变桨与偏航电机参数、变桨/偏航大小齿齿数、发电机型号厂家、行星轮个数未取证** |
-
-### 8.2 到位状态分布
-
-**如图 2-3 所示**,21 类中"已在位"10 项、"部分"7 项、"未到位"4 项(逐行统计依第 8.1 节表,与图中数字同源)。
-
-![图 2-3 收资要求 v3 的 21 类在观澜的到位状态(样本场实测 2026-09-22)](figures/fig-dat-03-收资项覆盖.png)
-
-### 8.3 优先级建议
-
-**表 8-2 收资优先级建议**
-
-| 优先级 | 判定依据 | 数据项 | 现场动作 |
-|---|---|---|---|
-| 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 缺失项、影响与替代件**
-
-| 缺失项 | 影响 | 替代件与口径 | 性质 |
-|---|---|---|---|
-| 测风塔数据(零交付) | 自由流风速与湍流度缺场级参考;只能有机舱风速计与补偿风 | 用 `tur_wtc_SecAnemo_mean`(原生第二风速计)作曲线 X 轴;湍流度只能用 `WMET_ti` 一类的**未验证**代理量 | 现场补件 |
-| `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/<场>/` | 研发/振动线补口径 |
-| 秒级高频(1 s/5 s)未用 | 变桨面只用 1 分钟层 | 1 分钟层已含零位三口径所需通道 | 现场补件(1 s/5 s) |
-| 故障录波未接入 | 无(当前分析面不消费) | 无 | 不催 |
-| 厂商月度报告 12 份为纯扫描件 | 「CMS 振动评估报告」的厂商侧数值取不出 | 只登记归档(记 sha256、大小、无文本层);有文本层的 docx 已转录 | 现场给电子件,或上 OCR |
-| 大部件更换台账不完整 | 在升与换件闭环证据不足 | 用出保备件台账与工单台账交叉;闭环**如实为空** | 现场补件 |
-| 坐标件未交付 | 绝对扇区与风玫瑰不可用 | 集电线路 string(A 至 F)可由 1 分钟内部台号反推;风向本身为**伪地理**(未对北,跨台偏移 ±161°),契约已禁绝对扇区 | 现场补件 |
-| 无生成端的组级产物(`pc_monthly_bins`、`duty_monthly`、`thermal_monthly`、`sector_power`、`yaw_*`、`genbearing_monthly`、`mblub_monthly`、`structure.parquet`、`watch_channels_monthly` 等) | 趋势件、热链月度、扇区、偏航与润滑面 | 逐族在补生成端(见 `docs/源代码化清单_v0.1.md`);补齐前如实标"无生成端" | 研发补生成端 |
-| 门户引用的产物树不在本包(`cleaned/`、`gearbox_life/`、`yaw_verify/`、`structured/`) | 传动链回放、四系统判据、控制律仪表台、取数单四个页面引用悬空 | 保留记账(`configs/portal_pages.yaml` 的 `known_gap`),不删交付件正文 | 待该产物线随包后复检 |
-
-## 10 数据量、时间覆盖与核对锚点
-
-### 10.1 体量与件数实测
-
-**表 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/<机型>技术资料` | 318 | 2,936,758,089 B(约 2.7 GB) |
-| `outputs/<场>` | 3,549 | 7,157.5 MB |
-
-### 10.2 时间覆盖与缺口
-
-**如图 2-4 所示**,关键数据的时间覆盖如下。
-
-![图 2-4 关键数据的时间覆盖与缺口(样本场实测 2026-09-22)](figures/fig-dat-04-时间覆盖.png)
-
-**表 10-2 关键数据的时间覆盖(样本场实测 2026-09-22)**
-
-| 数据 | 起点 | 终点 | 覆盖 | 缺口 |
-|---|---|---|---|---|
-| 10 分钟 SCADA 主件 | 2025-01-01 00:00:00 | 2026-07-07 00:00:00 | 约 18.2 个月 | 逐台行数一致(77,551 行),无单台缺失 |
-| 10 分钟 SCADA 同台补充件 | 2026-08-01 00:00:00 | 2026-09-01 00:00:00 | 1 个月 | 尾月只有 1 行(2026-09-01 00:00),如实出 |
-| 1 分钟 SCADA | 2025-01-01 00:00:00 | 2026-06-30 23:59:00 | 18 个月 | — |
-| 月度派生件(损失与温度月表) | 2025-01 | 2026-09 | 21 个月 | 2026-09 为残月 |
-| 报警台账 | 2025-01 | 2026-07 前后 | 按源件 | 累计快照件自动跳过 |
-| CMS 振动导出 | 2026-03-16 17:27:22 | 2026-04-21 10:18:33 | 约 36 天 | **不足一个季度**;`w0127` 与其余 5 窗的导出件不在本机 |
-| CMS 厂家报告 | 2025-06 | 2026-07 | 14 个月 | 12 份为扫描件(只归档) |
-| 油样报告 | 2024-11-19 | 2025-08-29 | 按文件名日期 | 2026-07 批 102 行源件缺 |
-| 技术资料 | — | — | 318 件 | 定值表与图纸在位 |
-| 可研与测风塔 | — | — | **未交付** | 见第 9 章 |
-
-### 10.3 核对锚点
-
-重算后应与下列锚点对齐(样本场实测 2026-09-22):
-
-**表 10-3 核对锚点(样本场实测 2026-09-22)**
-
-| 锚点 | 实测值 | 来源 |
-|---|---|---|
-| 报警台账行数 | 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 新增月),两者差异已如实记录 |
-| 本体对象数 | 9,702 | 本体对象库(审计 0 问题) |
-| 损失月表行数 | 3,955 | `loss_monthly.parquet`(21 个月) |
-| 温度比档件行数 | 24,624 | `temp_bins.parquet`(三个时间窗:`ref`/`cur`/`ref_sm`) |
-| 功率曲线偏差 | 38 台 | `powercurve_dev.parquet` |
-| 功率曲线分箱 | 912 行 | `powercurve_bins.parquet` |
-| 停机事件 | 2,755 行 | `stop_events.parquet` |
-| 变桨零位与曲线受检列 | 304 行 | `curve_liveness.parquet`(38 台 × 8 列) |
-| 控制参数剖面 | 38 行 | `control_profile.parquet`(`p_cap`、`w_cap`、`k_med`、`pitch_rated`) |
-| 控制桨距调度 | 304 行 | `control_schedule.parquet`(38 台 × 8 个功率档) |
-| 振动窗索引 | 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%) | 场站故障代码表 |
-
-## 11 面向现场的收资清单
-
-**表 11-1 可打印收资清单(本场口径;换场按第 12 章裁剪)**
-
-| 优先级 | 数据项 | 最小可用量 | 必需测点 | 交付形式与命名 |
-|---|---|---|---|---|
-| P0 | 10 分钟 SCADA | 38 台,600 s,时间跨度 2 年 | 48 列公共子集(功率/风况/转速/桨距/温度 27/偏航压力/液压压力/润滑/限电命令) | 平铺 `.csv`,`WTG01.csv` 至 `WTG38.csv`,`utf-8-sig`,时间戳 `YYYY-MM-DD HH:MM:SS` |
-| P0 | CMS 振动导出 | 至少一个季度、秒级、含原始时域波形 | 主轴前/后、行星级、中间轴、高速轴两侧、发电机 DE/NDE、系统监视、叶轮转速 | `measurement/<年>/<月>/<WTGxx>/*_decode.json` |
-| P0 | 测风塔 | 前期至少满一年(5 或 15 分钟);运行期一整年分钟级 | 温度、湿度、气压、不同层高风速、风速标准差、风向(无标准差则需 1 s 采样) | 未约定族,须现场明确形态 |
-| P0 | 机型参数 | 一份机型级参数表 | 切入切出风速、额定风速、额定功率、叶轮直径、轮毂高度、传动比、生存温度 | `.xlsx` 或 `.pdf` |
-| P1 | 机位坐标 | 38 台 + 升压站 | 台号、经度、纬度、海拔(含轮毂高度)、机型 | `.xlsx`,建议命名含"坐标" |
-| P1 | 功率曲线 | 2 条曲线 | 标准空气密度 + 合同担保;担保曲线附空气密度、湍流度、风切变 | 数值表优先(`.csv`/`.xlsx`),避免只给图 |
-| P1 | 保护定值与控制策略 | 成册 | 温度/压力/速度保护定值、偏航控制策略、降载荷控制策略 | `.xlsx` 或 `.pdf` |
-| P1 | 生产报表(并网至今) | 并网至今 | 风速、发电量、故障率、限电量 | 年/月报表格,注明口径 |
-| P1 | 大部件更换记录 | 逐次 | 时间、机组、部件、原因 | `.xlsx` |
-| P1 | 设备参数明细 | 逐部件 | 叶片参数、变桨电机参数、变桨驱动大小齿齿数、偏航电机参数、偏航驱动大小齿齿数、发电机型号厂家、行星轮个数 | `.xlsx` 或 `.pdf` |
-| P1 | 故障报警与故障记录(增量) | 覆盖分析时间窗 | 每行须带 `TimeOn` 与 `Alarmcode`;工单表须含机组编号、故障名称、故障代码 | `.xls`/`.xlsx`/`.xml`,不许多套一层目录 |
-| P2 | 秒级高频 SCADA | 1 s 或 5 s,至少 3 个月 | 全量测点 | `.csv`,内部台号命名 |
-| P2 | 可研与微观选址 | 全本 | 测风依据、逐台设计基线(发电小时、尾流折减) | `.pdf` 或 `.docx` |
-| P2 | 风电场简介 | 一份 | 场站规模、投产时间、海域特性 | `.docx` 或 `.pdf` |
-| 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/<机型>_<场>.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/<机型>_<场>.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/<机型>_<场>.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 章 | 现场《数据分析收资要求-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/<场>` 只读实测、`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 单位与符号表
-
-**表 B-1 单位与符号**
-
-| 符号 | 中文名称 | 量纲 | 在本系统中的典型测点 |
-|---|---|---|---|
-| ℃ | 摄氏度 | 温度 | 温度 NBM 全 27 通道(canonical 里写作 `degC` 或 `C`) |
-| kW | 千瓦 | 功率 | `grd_wtc_ActPower_mean`(网侧有功)、`p_cap`(功率封顶) |
-| m/s | 米每秒 | 速度 | `tur_wtc_SecAnemo_mean`(风速)、`WMET_wind_speed_*` |
-| r/min | 转每分 | 旋转速度 | `tur_wtc_GenRpm_mean`(发电机转速)、`tur_wtc_MainSRpm_mean`(叶轮转速);canonical 里写作 `rpm` |
-| ° | 度 | 平面角 | 桨距角、风向、偏航误差、扭缆角 |
-| bar | 巴 | 压力 | 偏航压力、液压系统压力、桨叶油压;词典另有 kPa/Pa/hPa 三种并存 |
-| s | 秒 | 时间 | 润滑泵通电时长、报警持续时长 |
-| 次 | 次 | 计数 | 偏航动作计数、风速故障计数、谱线计数 |
-| kWh | 千瓦时 | 电量 | 发电量、损失电量、整定值表里的 `kW.` |
-| % | 百分比 | 无量纲比 | 可用率、湿度、磁盘占用 |
-| h | 小时 | 时间 | 利用小时、停机小时 |
-| —— | —— | —— | 以上为本表限定的 11 个符号;其余单位(V、A、Hz、mHz、g、m/s²、kg/m³、Nm、kVar、ccm、m)在个别表内单独标注,取值依据见附录 A |
-
-## 附录 C 收资要求 v3 原文 21 项
-
-以下为现场《数据分析收资要求-v3》21 项的**逐字**原文(换行以";"分隔呈现,不改字、不删项)。
-
-**表 C-1 收资要求 v3 原文**
-
-| 序号 | 数据类型 | 备注(逐字) |
-|---|---|---|
-| 1 | 10分钟SCADA数据 | 统计周期为2年的全量测点10分钟SCADA数据。 |
-| 2 | 秒级SCADA高频数据 | 采样频率为1秒或5秒;统计时间为至少3个月的全量测点秒级SCADA高频数据; |
-| 3 | 测风塔数据 | 采样频率为5分钟或15分钟;1.项目前期实地测风塔测风数据(与可研报告中时间一致,需测风至少满一年),测点至少包含风速、风向、温度、气压、湿度。2.运行周期内统计时间为一整年的分钟级测风塔数据;测点包括:温度、湿度、气压、以及不同层高的风速、风速标准差、风向;若分钟级数据不具备"风速标准差",则需按如下要求:采样频率为1秒;统计时间为一整年的分钟级测风塔数据;测点包括:温度、湿度、气压、以及不同层高的风速、风向; |
-| 4 | 功率曲线 | 标准空气密度功率曲线;合同担保功率曲线 |
-| 5 | 机型参数 | 机型整体参数,如切入切出风速、额定风速、额定功率、叶轮直径、轮毂高度、传动比、生存温度等。 |
-| 6 | 机位分布数据 | 机位坐标、机位海拔高度(含轮毂高度)、机组机型的机位分布信息。 |
-| 7 | 机组状态、事件、故障、告警数据 | 机组故障代码、状态代码表及详细说明,详细故障记录、事件记录、告警记录数据。 |
-| 8 | 机舱CMS振动数据 | CMS厂家出具且已确诊报告。统计时间为至少一个季度的秒级振动数据(非加密数据)及原始振动时域波形;测点包括:主轴、齿轮箱、发电机、叶轮转速 |
-| 9 | 机组统计数据或运行数据 | 年月日机组统计运行数据,包括发电量,可利用率,利用小时数、发电量、平均风速、可利用率,故障停机时间,维护停机时间,各类损失电量情况,偏航时间、偏航次数等;参见附录两个示例文件 |
-| 10 | 机组保护定值及控制参数 | 机组各项温度、压力、速度等保护定值,机组偏航控制策略、机组降载荷控制策略等控制参数 |
-| 11 | 机组故障录波数据 | 每次机组故障后,详细记录故障前后机组数据的文件 |
-| 12 | 可研报告及微观选址报告 | (原文无备注) |
-| 13 | 风电场简介 | (原文无备注) |
-| 14 | 风电场坐标、机位布置图 | (原文无备注) |
-| 15 | 风机技术协议及风机合同 | (原文无备注) |
-| 16 | 并网到现在的生产报表 | 包含风速、发电量、故障率、限电量等信息 |
-| 17 | 周围已建风电场生产报表 | 包含风速、发电量、故障率、限电量等信息 |
-| 18 | 运维记录 | 维护台账、检修台账 |
-| 19 | 大部件更换记录 | (原文无备注) |
-| 20 | 图纸、技术文件 | 风机电气控制相关图纸、液压站图纸、逻辑图或控制方式文件 |
-| 21 | 设备参数 | 主轴轴承型号及厂家、齿轮箱型号厂家、齿轮箱各级传动参数(行星轮内齿圈齿数、行星轮个数、太阳轮齿数、行星轮齿数、平行轮大齿轮齿数、平行轮小齿轮齿数)、齿轮箱各级轴承型号、发电机型号厂家、发电机轴承型号;叶片参数、变桨电机参数、变桨驱动小齿齿数、变桨驱动大齿齿数、偏航电机参数、偏航驱动小齿齿数、偏航驱动大齿齿数 |

+ 1364 - 0
docs/src/数据要求说明_观澜_2.10.2.md

@@ -0,0 +1,1364 @@
+# 数据要求说明 · 观澜 v2 风电场智能分析系统 · 版本 2.10.2
+
+## 1 文档说明
+
+### 1.1 目的
+
+本文说明「观澜 v2(风电场智能分析系统)」在运行、判级与出件时**实际需要**的输入数据:需要哪些数据类别、每一类里需要哪些测点(字段)、每个测点是什么业务含义、以什么度量单位提交、以及这一类到底是**必须**、**建议**还是**可选**。
+
+本文是一份**对外、通用**的数据需求规格:任何一座风电场都可以直接按本文收资,也可以按本文逐条核对「要什么、为什么要、缺了会怎样」。本文的表述与任何具体场站无关,收资的落地口径由现场按本文第 8 章的清单执行。
+
+### 1.2 读者
+
+**表 1-1 本文读者的使用方式**
+
+| 读者 | 怎么用本文 |
+|---|---|
+| 现场数据管理员 | 按第 8 章的清单收资、按第 4 章的索引核对测点是否齐全、按第 5 章检查质量是否达标 |
+| 本系统实施与运维人员 | 按第 2 章判断某一类数据支撑哪些功能、按第 6 章判断某一类是否阻断主线 |
+| 分析人员 | 按第 3 章的测点清单与第 4 章的索引确认测点业务含义与度量单位 |
+| 交付与验收人 | 按第 7 章逐条对照收资要求、按第 6 章确认必选与可选的边界、按附录 C 追到依据来源 |
+
+### 1.3 必须性分级定义
+
+本文对每一类数据、每一个测点都给出**必须性**,只取三个取值:必须、建议、可选。
+
+**表 1-2 必须性分级定义**
+
+| 分级 | 含义 | 缺了会怎样 |
+|---|---|---|
+| 必须 | 核心分析面(判级、发电性能、可靠性、台账)直接依赖;缺则该面不可判或整步降级 | 该分析面显示缺件或不可判,重算链对应步骤响亮报错或跳过,**不造数、不猜数** |
+| 建议 | 支撑交叉校验与二次确认;缺了不阻断主线,但会削弱结论的可信度与可追溯性 | 结论仍在,但依据变少,跨源互证与闭环核对做不了 |
+| 可选 | 增强型输入,只在特定专题或外部对照里使用;不参与常规重算 | 只有对应专题不可做 |
+
+测点表中的「是否必须」指该测点**在本类数据之内**的必需程度;某一类的整体分档见第 6 章第 2 节。若某一类由本系统自算件或已提交的其他数据顶替,顶替口径必须随产物写明;现场正本提交后,正本优先。
+
+### 1.4 口径声明
+
+本文只写对数据的要求,不描述任何具体场站的任何数据情况。全文遵守以下口径:
+
+1. 不出现任何具体场站的数据提交情况、件数、体量、行数、时间覆盖与日期。
+2. 不出现任何具体台号、机组编号与机组数量;需要表述范围时写「全场机组(按场实际台数)」或「逐台」。
+3. 不出现任何具体文件名、文件扩展名与目录路径;需要说明「怎么交」时只写**交付形态**,例如「逐台一张表,一行一个时间戳」「一个代码表文件,两列:代码、说明」。
+4. 不出现英文测点标识、英文列名与英文缩写点表名;测点一律用**中文业务含义名称**称呼。
+5. 必要出现的公文式英文简写一律写成「中文(英文简写)」形式。
+6. 度量单位只取自已取证的口径;查不到的写「未取证」,**不凭常识补单位**。
+7. 含「窗」且确指时间范围之处一律写「时间窗」;涉及波及范围之处写「影响机组/影响机组数」。
+8. 全文用简体中文与半角数字单位。
+
+### 1.5 术语约定
+
+**表 1-3 术语与约定**
+
+| 术语 | 约定 |
+|---|---|
+| 测点(字段) | 源件中的一个数据列。本文一律以中文业务含义名称称呼,不以英文标识称呼 |
+| 业务含义名称 | 对该测点所测物理量及其在分析中所起作用的中文表述 |
+| 时间窗 | 一段时间范围。本文凡含「窗」且确指时间范围之处均写「时间窗」 |
+| 影响机组/影响机组数 | 某类数据缺失时被波及的机组,或该机组的数量 |
+| 全场机组(按场实际台数) | 不指任何具体台数;实际台数按场确定 |
+| 逐台 | 每一台机组各一份记录或各一份表 |
+| 交付形态 | 数据以什么形态提交(例如逐台一张表、一行一个时间戳);**不指定**具体文件名与存放位置 |
+| 收资要求 | 现场《数据分析收资要求-v3》所列的收资项;本文按其 21 类逐类给出要求 |
+| 数据采集与监视控制系统(SCADA) | 机组与场级运行数据的常规采集系统;本文的 10 分钟数据与秒级数据均属此列 |
+| 状态监测系统(CMS) | 机舱与传动链振动的在线监测系统;本文的机舱振动数据属此列 |
+| 平均无故障间隔(MTBF) | 可靠性指标的中文(英文简写)写法示例;本文一律采用该写法 |
+
+## 2 数据分类总表
+
+**表 2-1 数据分类总表**
+
+| 序号 | 数据类别 | 支撑功能 | 最小可用量(时长/采样/范围) | 是否必须 |
+|---|---|---|---|---|
+| 1 | 10 分钟 SCADA 数据 | 判级四轴(变桨、偏航、蓄能、温度)、发电性能与功率曲线、损失电量、停机事件、热链与冷却、控制参数一致性 | 逐台一份,采样 600 秒,统计时长建议不少于 2 年;范围全场机组 | 必须 |
+| 2 | 秒级 SCADA 高频数据 | 瞬态与启停过程、限电指令响应、振动与工况关联、故障前后的秒级回溯 | 采样 1 秒或 5 秒(二选一),统计时长至少 3 个月;范围全场机组、全测点 | 可选 |
+| 3 | 测风塔数据 | 自由流风速与湍流度、风廓线、功率曲线与尾流的外部锚 | 前期实地测风满一年(采样 5 分钟或 15 分钟);运行期一整年分钟级;无风速标准差时须采样 1 秒 | 必须 |
+| 4 | 功率曲线 | 发电性能偏差判别与合同指标对标的基准 | 标准空气密度曲线与合同担保曲线各一条,覆盖切入至切出风速段 | 建议 |
+| 5 | 机型参数 | 振动分母、功率曲线基准、控制参数参照 | 一份机型级参数表 | 必须 |
+| 6 | 机位分布数据 | 邻域与尾流先验、扇区分析、台号与集电线路的地理对照 | 逐台坐标与海拔(含轮毂高度)、机型;另加升压站 | 建议 |
+| 7 | 机组状态、事件、故障、告警数据 | 报警台账、停机账、限电绑定、首发链、检修工单与闭环验证 | 覆盖分析时间窗的码表与逐条记录;范围全场机组 | 必须 |
+| 8 | 机舱 CMS 振动数据 | 振动索引与谱库、部件状态评估、在升与换件闭环、机群基线 | 至少一个季度、秒级、含原始时域波形;范围全场机组 | 必须 |
+| 9 | 机组统计数据或运行数据 | 发电账、可利用率与利用小时、损失电量、偏航与停机统计 | 覆盖分析时间窗的年、月、日统计量 | 必须 |
+| 10 | 机组保护定值及控制参数 | 保护判据的最终依据、控制参数一致性、偏航与降载策略 | 定值表成册,另加偏航控制策略与降载控制策略 | 建议 |
+| 11 | 机组故障录波数据 | 故障前后高分辨率快照的秒级回溯 | 每次机组故障后触发前后一段记录,采样秒级或更高 | 可选 |
+| 12 | 可研报告及微观选址报告 | 逐台设计基线与测风依据,用作外部对照 | 全本 | 可选 |
+| 13 | 风电场简介 | 场站元信息,用于背景、署名与台账筛选 | 一份 | 可选 |
+| 14 | 风电场坐标、机位布置图 | 图示化的机位与电气布置对照 | 全场坐标件与布置图 | 可选 |
+| 15 | 风机技术协议及风机合同 | 合同指标对标(担保曲线、保证率与考核条款) | 全本,含担保曲线附件与保证率条款 | 必须 |
+| 16 | 并网到现在的生产报表 | 与运行数据侧损失账交叉核对 | 并网至今,日粒度或月粒度 | 建议 |
+| 17 | 周围已建风电场生产报表 | 尾流与场级损耗的外部对照 | 覆盖分析时间窗的统计报表 | 可选 |
+| 18 | 运维记录 | 检修面与闭环验证,与油样、报警三源交叉 | 覆盖分析时间窗的维护台账与检修台账 | 建议 |
+| 19 | 大部件更换记录 | 部件在升与换件闭环的判定输入 | 逐次更换记录 | 建议 |
+| 20 | 图纸、技术文件 | 位域语义、控制逻辑与回路判据的最终依据 | 电气控制、液压、逻辑图与保护定值表 | 必须 |
+| 21 | 设备参数 | 振动阶次定位与寿命评估的分母 | 逐部件型号、厂家与齿数 | 建议 |
+
+## 3 逐类数据要求与测点清单
+
+本章按收资要求的 21 类逐类给出要求。每一节先写用途与最小可用量,再给出一张测点清单表;表头固定四列:测点/字段名、业务含义、度量单位、是否必须。
+
+### 3.1 10 分钟 SCADA 数据
+
+**用途**:本系统的**主数据层**。判级四轴(变桨、偏航、蓄能、温度)、发电性能与功率曲线、损失电量、停机事件、热链与冷却、控制参数一致性全部由它派生。
+
+**最小可用量**:全场机组(按场实际台数)逐台一份,采样周期 600 秒,一行一个时间戳;统计时长建议不少于 2 年,其中判级用的参照时间窗与季节匹配时间窗各需一段。测点为全量测点,其中下述清单为核心必选集。
+
+**交付形态**:逐台一张表,一行一个时间戳;同一台可另附同台补充件;一律**按列名**读取,禁用位置索引;文本编码必须带字节顺序标记,否则中文列名会读成乱码。
+
+**单位口径**:功率 kW、电量 kWh、风速 m/s、转速 r/min、角度 °、温度 ℃、压力 bar、时长 s、计数 次。查不到单位依据的测点写「未取证」。
+
+#### 3.1.1 功率与电量
+
+**表 3-1 10 分钟 SCADA 的功率与电量测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 网侧有功功率(区间均值) | 并网点有功功率的十分钟平均值,是功率分箱、发电态判定、功率曲线与发电性能分析的主输入 | kW | 必须 |
+| 网侧有功功率(区间最大值) | 十分钟区间内有功功率的最大值,用于识别功率封顶与控制参数一致性 | kW | 必须 |
+| 网侧有功功率(区间最小值) | 十分钟区间内有功功率的最小值,用于识别夜间自耗与停机态 | kW | 建议 |
+| 有功功率给定(区间期末值) | 主控下发的有功给定在区间末端的取值,用于限电命令面分类 | kW | 必须 |
+| 可用功率参照 | 机组当前可发功率的参照值,用于限电损失与可发出力口径核对 | kW | 建议 |
+| 区间发电量 | 十分钟区间内的发电量增量,用于电量账与损失电量对拍 | kWh | 建议 |
+| 累计发电量 | 自并网起的累计有功电量,用于电量账闭合核对 | kWh | 建议 |
+| 机组自用电量 | 机组自身耗电量(含夜间自耗),用于净发电量口径核对 | kWh | 可选 |
+| 无功功率 | 并网点无功功率,用于电压支撑与功率因数核对 | kVar | 建议 |
+| 无功电量 | 并网点累计无功电量,用于并网性能核对 | kVarh | 可选 |
+| 功率因数 | 并网点功率因数,用于并网品质核对 | — | 可选 |
+
+#### 3.1.2 风况
+
+**表 3-2 10 分钟 SCADA 的风况测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机舱风速(原生风速计) | 机舱顶部风速计测得的原始风速,是功率曲线横轴与低风待机判定的主要风源 | m/s | 必须 |
+| 独立风速计风速 | 第二路独立风速计的测得风速,用于风速量测互证与测风链路故障判别 | m/s | 必须 |
+| 补偿风速 | 由功率反推得到的补偿风速,仅作运行参考;不得用于功率曲线与湍流度分析 | m/s | 建议 |
+| 风速标准差 | 区间内风速的标准差,是湍流度分析的直接输入 | 未取证 | 建议 |
+| 机舱风向 | 机舱风向标的瞬时风向(以机舱编码器为基准),用于相对风向与对风偏差分析 | ° | 必须 |
+| 绝对风向估计 | 偏航系统输出的滤波绝对风向估计;其基准是否为地理北必须由现场明确 | ° | 建议 |
+| 空气密度 | 按环境温度与气压折算的空气密度,是功率曲线归一化的前提 | kg/m³ | 建议 |
+| 大气压力 | 场址大气压力,用于空气密度折算 | kPa | 可选 |
+
+#### 3.1.3 转速
+
+**表 3-3 10 分钟 SCADA 的转速测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 发电机转速(区间均值) | 发电机转速的十分钟平均值,用于转矩、叶尖速比与功率曲线校核 | r/min | 必须 |
+| 发电机转速(区间最大值) | 发电机转速的十分钟最大值,用于识别转速封顶与控制参数一致性 | r/min | 必须 |
+| 叶轮转速(区间均值) | 叶轮(主传动)转速的十分钟平均值,用于叶尖速比与转矩计算 | r/min | 必须 |
+| 发电机转速给定 | 主控下发的发电机转速给定,用于控制律与跟随误差分析 | r/min | 可选 |
+
+#### 3.1.4 桨距与变桨
+
+**表 3-4 10 分钟 SCADA 的桨距与变桨测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 1 号叶片桨距角(区间均值) | 1 号叶片桨距角的十分钟平均值,用于功率曲线、三叶不平衡与变桨面判级 | ° | 必须 |
+| 2 号叶片桨距角(区间均值) | 2 号叶片桨距角的十分钟平均值,用途同上 | ° | 必须 |
+| 3 号叶片桨距角(区间均值) | 3 号叶片桨距角的十分钟平均值,用途同上 | ° | 必须 |
+| 三叶桨距角极差 | 三个叶片桨距角的最大值与最小值之差,是变桨同步性与三叶一致性的直接判据 | ° | 必须 |
+| 桨距角给定值 | 变桨系统接收的桨距角给定,用于跟随误差与执行器可用性判断 | ° | 建议 |
+| 桨距角变化速率 | 桨距角的变化速率,用于变桨速率与载荷分析 | °/s | 可选 |
+| 变桨电机电流 | 变桨电机电流,用于变桨负载与电气异常判断 | A | 可选 |
+| 变桨电机转矩 | 变桨电机输出转矩,用于变桨驱动能力判断 | Nm | 可选 |
+| 变桨柜温度 | 变桨柜内部温度,用于变桨电气部件的热环境判断 | ℃ | 可选 |
+
+#### 3.1.5 温度
+
+**表 3-5 10 分钟 SCADA 的温度测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 环境温度 | 机舱外部环境温度,是空气密度口径与热汇参考,也是季节解耦的基准 | ℃ | 必须 |
+| 机舱温度 | 机舱内部温度,是热汇参考之一 | ℃ | 建议 |
+| 轮毂温度 | 轮毂内部温度,反映轮毂热环境 | ℃ | 建议 |
+| 主轴承温度(轮毂侧) | 主轴承靠轮毂侧测点温度,用于主轴承面判级与双温差配对 | ℃ | 必须 |
+| 主轴承温度(齿轮箱侧) | 主轴承靠齿轮箱侧测点温度,用于主轴承面判级与双温差配对 | ℃ | 必须 |
+| 齿轮箱油温 | 齿轮箱润滑油温度,是齿轮箱即时热汇与温升判据的主用测点 | ℃ | 必须 |
+| 齿轮箱高速轴温度(发电机侧) | 齿轮箱高速轴发电机侧轴承温度,用于齿轮箱面与热链计算 | ℃ | 必须 |
+| 齿轮箱高速轴温度(叶轮侧) | 齿轮箱高速轴叶轮侧轴承温度,用于齿轮箱面与热链计算 | ℃ | 必须 |
+| 齿轮箱中间轴温度(发电机侧) | 齿轮箱中间轴发电机侧轴承温度,用于齿轮箱面与热链计算 | ℃ | 必须 |
+| 齿轮箱中间轴温度(叶轮侧) | 齿轮箱中间轴叶轮侧轴承温度,用于齿轮箱面与热链计算 | ℃ | 必须 |
+| 齿轮箱油滤温度 | 齿轮箱冷却回路冷侧温度,与油温之差为冷却器温降口径 | ℃ | 建议 |
+| 齿轮箱油滤温度(备用测点) | 第二条油滤回路温度;若为恒值或未接线必须显式标注并排除 | ℃ | 可选 |
+| 发电机定子 U 相绕组温度 | 发电机定子 U 相绕组温度,用于发电机面、热链节点与三相极差 | ℃ | 必须 |
+| 发电机定子 V 相绕组温度 | 发电机定子 V 相绕组温度,用途同上 | ℃ | 必须 |
+| 发电机定子 W 相绕组温度 | 发电机定子 W 相绕组温度,用途同上 | ℃ | 必须 |
+| 发电机轴承温度(驱动端) | 发电机驱动端轴承温度,用于发电机面与配对差 | ℃ | 必须 |
+| 发电机轴承温度(非驱动端) | 发电机非驱动端轴承温度,用于发电机面与配对差 | ℃ | 必须 |
+| 液压油温 | 液压站油温,用于蓄能面的同油温档比较 | ℃ | 必须 |
+| 机舱变压器油温 | 机舱变压器油温,用于变压器温升与冷却分析 | ℃ | 建议 |
+| 变压器层空气温度 | 变压器所在层的空气温度,用于温升口径计算 | ℃ | 建议 |
+| 变压器油温(同源副本) | 与机舱变压器油温为同一条信号的副本;**禁作第二独立证据** | ℃ | 可选 |
+| 主开关板柜外温 | 主开关板柜外侧温度,用于柜体热环境监控 | ℃ | 建议 |
+| 主开关板柜内温 | 主开关板柜内侧温度,用于柜内热环境监控 | 未取证 | 建议 |
+| 控制柜左列温度 | 控制柜左侧温度,用于柜内热环境监控 | 未取证 | 建议 |
+| 控制柜右列温度 | 控制柜右侧温度,用于柜内热环境监控 | 未取证 | 建议 |
+| 刹车温度(发电机侧) | 高速轴刹车发电机侧温度,用于高速轴刹车热状态判断 | 未取证 | 建议 |
+| 刹车温度(叶轮侧) | 高速轴刹车叶轮侧温度,用途同上 | 未取证 | 建议 |
+| 变流器冷却水温度 | 变流器冷却水温度,是热链的热汇节点 | 未取证 | 必须 |
+
+温度类测点的判级口径为**同工况机群相对偏差**:按发电态与功率档分组,对每一测点取机群中位数,再按偏差与稳健尺度分档;分档结论只能是候选、记基线观察或不可判,判为「不偏离机群」不等于该机组正常。另有配对差判据(同一台机组的两个相近测点相减)用于区分局部摩擦与测量偏差。
+
+#### 3.1.6 偏航
+
+**表 3-6 10 分钟 SCADA 的偏航测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 偏航压力(区间均值) | 偏航液压系统压力的十分钟平均值,是偏航面判级的主输入 | bar | 必须 |
+| 偏航压力(区间最大值) | 偏航压力最大值,用于保压上限与封顶判断 | bar | 必须 |
+| 偏航压力(区间最小值) | 偏航压力最小值,用于压力衰减与预充压力判断 | bar | 必须 |
+| 偏航动作计数 | 区间内偏航动作次数的累计,是偏航负载与磨损剂量的分母 | 次 | 必须 |
+| 偏航润滑泵通电时长 | 偏航润滑泵在区间内的通电时长,是偏航润滑占空比的直接量 | s | 必须 |
+| 机舱方位角 | 机舱相对基准的方位角,用于偏航位置与扇区分析 | ° | 建议 |
+| 偏航误差 | 机舱风向与机舱方位之差,用于对风偏差统计 | ° | 建议 |
+| 偏航动作时长(顺时针) | 偏航电机在区间内顺时针方向的累计动作时长 | s | 建议 |
+| 偏航动作时长(逆时针) | 偏航电机在区间内逆时针方向的累计动作时长 | s | 建议 |
+| 扭缆角度 | 电缆扭转角度,用于解缆与扭缆保护分析 | ° | 建议 |
+| 自动解缆标志 | 标识机组当前处于自动解缆过程 | — | 可选 |
+
+#### 3.1.7 液压与润滑
+
+**表 3-7 10 分钟 SCADA 的液压与润滑测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 液压系统压力(区间均值) | 液压站压力均值,是蓄能面判级的主输入 | bar | 必须 |
+| 液压系统压力(区间最大值) | 液压站压力最大值,用于保压上限与封顶判断 | bar | 必须 |
+| 液压系统压力(区间最小值) | 液压站压力最小值,用于压力衰减与预充压力判断 | bar | 必须 |
+| 润滑柱塞通电时长 | 润滑柱塞在区间内的通电时长,是润滑动作剂量的直接量 | s | 必须 |
+| 齿轮箱油路入口压力 | 齿轮箱润滑油入口压力,用于润滑供给是否正常 | bar | 建议 |
+| 齿轮箱主滤前压力 | 主滤芯入口压力,与主滤后压力之差为滤芯压差(物理应不小于零) | bar | 建议 |
+| 齿轮箱主滤后压力 | 主滤芯出口压力,用于滤芯压差与堵塞判断 | bar | 建议 |
+| 齿轮箱旁路滤油压力 | 旁路过滤回路压力,用于旁路过滤效率判断 | bar | 建议 |
+| 桨叶油压(A) | A 叶片变桨液压缸压力,用于变桨液压供给与桨叶间比较 | bar | 建议 |
+| 桨叶油压(B) | B 叶片变桨液压缸压力,用途同上 | bar | 建议 |
+| 桨叶油压(C) | C 叶片变桨液压缸压力,用途同上 | bar | 建议 |
+| 机械制动压力 | 机械制动液压压力,用于制动系统可用性判断 | bar | 建议 |
+
+桨叶油压与液压站压力类测点可能出现负值越界,必须按物理界转为缺失,**不得**压到边界上。
+
+#### 3.1.8 电网
+
+**表 3-8 10 分钟 SCADA 的电网测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 网侧相电流(R 相) | 并网点 R 相电流,用于三相平衡度与电气异常分析 | A | 建议 |
+| 网侧相电流(S 相) | 并网点 S 相电流,用途同上 | A | 建议 |
+| 网侧相电流(T 相) | 并网点 T 相电流,用途同上 | A | 建议 |
+| 网侧相电压(R 相) | 并网点 R 相电压,用于电压偏差与失压事件分析 | V | 建议 |
+| 网侧相电压(S 相) | 并网点 S 相电压,用途同上 | V | 建议 |
+| 网侧相电压(T 相) | 并网点 T 相电压,用途同上 | V | 建议 |
+| 电网频率 | 并网点频率,用于电网事件与脱网分析 | Hz | 建议 |
+| 无功功率 | 并网点无功功率,用于电压支撑核对 | kVar | 建议 |
+| 功率因数 | 并网点功率因数,用于并网品质核对 | — | 可选 |
+
+电网类测点中的零值可能合法且具有诊断意义(例如全失压事件),是否把零值当无效必须**逐列**与现场确认,不得统一设为零值哨兵。
+
+#### 3.1.9 状态与限电
+
+**表 3-9 10 分钟 SCADA 的状态与限电测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机组主状态码 | 机组主状态与故障码;无故障主态与停机码、发电中信息码必须按码表区分 | 文本 | 必须 |
+| 刹车状态位 | 反映机组处于发电态或停机态的状态位,用于停机态交叉判据 | 文本 | 必须 |
+| 运行状态字 | 以位域表示的机组运行状态字,其逐位含义必须由现场图纸释义 | 文本 | 建议 |
+| 子状态字 | 机组子状态的分组标识,用于细分停机与维护状态 | 文本 | 建议 |
+| 限电标志 | 标识机组当前是否受场控限电指令约束 | — | 必须 |
+| 限电功率给定(有功设定值) | 场控或主控下发的有功功率设定值 | kW | 必须 |
+| 限电功率给定(百分比) | 以额定功率百分比形式下发的限电给定 | % | 建议 |
+| 降载反馈功率 | 降载控制生效后的反馈功率,用于降载控制闭环核对 | kW | 可选 |
+| 故障标志字 | 以位域表示的故障标志字,逐位含义必须由现场图纸释义 | 文本 | 建议 |
+| 可用率(机内统计) | 机组内部统计的可用率,口径必须与统计类数据保持一致 | % | 建议 |
+
+#### 3.1.10 计数与统计
+
+**表 3-10 10 分钟 SCADA 的计数与统计测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 风速故障计数(第一路) | 第一路风速量测的故障累计计数,用于测风链路健康度 | 次 | 必须 |
+| 风速故障计数(第二路) | 第二路风速量测的故障累计计数,用于测风链路健康度 | 次 | 必须 |
+| 累计运行时长 | 自并网起的累计运行时长,用于运行小时账 | s | 建议 |
+| 累计发电时长 | 自并网起的累计发电时长,用于发电小时账 | s | 建议 |
+| 故障停机时长 | 区间内故障停机时长,用于停机账与可利用率核算 | min | 建议 |
+| 电网故障时长 | 区间内电网原因停机时长,用于责任剥离口径 | 未取证 | 可选 |
+
+### 3.2 秒级 SCADA 高频数据
+
+**用途**:本版新增的重点要求。10 分钟数据是统计与判级的主层,但它**看不见**启停瞬间、限电指令响应、故障前的过程量与振动关联。秒级数据是唯一能支撑瞬态分析、启停过程分析、限电响应分析与振动工况标注的输入,也是与故障录波、机舱振动数据配合的公共时间基准。
+
+**本节「必须」的含义**:一旦现场提交秒级数据,下表标注「必须」的测点必须全部包含在其中;若现场暂不提交秒级数据,则按第 6 章的分档以分钟级转发层顶替,此时本节的「必须」测点转为对顶替件的要求。
+
+#### 3.2.1 采样频率、统计时长与范围
+
+1. **采样频率**:1 秒或 5 秒,**二选一**;现场必须在提交说明中明确实际频率,不得以「约」「大致」表述,也不得以分钟级数据冒充秒级数据。
+2. **统计时长**:至少 **3 个月**,且必须连续;不得用零散时段拼接成 3 个月。
+3. **范围**:**全场机组(按场实际台数)、全测点**;逐台一份,一行一个时间戳。
+4. **交付形态**:逐台一张表,一行一个时间戳;时间戳统一为「年-月-日 时:分:秒」的本地时刻;同一场的两侧(秒级表与 10 分钟表)必须采用同一时间约定;一律按列名读取,禁用位置索引。
+5. **附加要求**:源件的无效值(哨兵值)清单必须随件给出;量化步长与有效位数必须逐列说明。
+
+#### 3.2.2 与 10 分钟数据的分工
+
+**表 3-11 秒级数据与 10 分钟数据的分工**
+
+| 用途 | 秒级数据 | 10 分钟数据 |
+|---|---|---|
+| 统计与判级 | 不直接用于统计判级;可作为瞬态证据 | 统计、判级、月度轨迹与曲线的**主输入** |
+| 启停过程 | 唯一可用:从并网到满发的全过程量 | 只能给出区间结果,看不见过程 |
+| 限电与功率给定响应 | 唯一可用:给定与反馈的跟随过程与超调 | 只能给出给定与反馈的区间取值 |
+| 振动与工况关联 | 唯一可用:为每个振动窗标注同期转速、功率与桨距 | 只能提供同工况的统计分箱 |
+| 湍流与阵风 | 可算秒级风速标准差与阵风 | 可算区间风速标准差 |
+| 数据量 | 大,按需保留时间窗 | 小,长期保留 |
+
+分工的硬要求:**能用 10 分钟数据做的统计与判级不得改用秒级数据**(避免口径漂移);**只有秒级能做的瞬态与关联分析不得用 10 分钟数据替代**(避免把过程量当成区间量)。两侧同时提交时,同一测点的物理量必须在同一时刻可对齐,且单位与量纲一致。
+
+#### 3.2.3 与故障录波、机舱振动数据的配合
+
+1. **与故障录波配合**:故障录波给出故障触发前后一段时间的高分辨率通道快照;秒级数据给出同期的全场运行量。两者的时间基准必须一致,录波触发时刻必须能与秒级时间戳逐值对齐;录波未覆盖的通道,可以用同期秒级数据补齐过程描述,但**必须标注来源不同**。
+2. **与机舱振动数据配合**:每一次振动测量记录都必须能配上同期的秒级工况(转速、有功功率、桨距角、风速),用于工况分箱与同工况比较;振动报警事件必须能回查到同期的秒级工况序列,否则该报警只能作为孤立记录、不能进入因果分析。
+3. **三者的时间窗要求**:三个来源的时间窗必须至少重叠一个完整的分析时间窗;重叠区必须显式记录起止,不得默认「按上报时段」推定重叠。
+
+#### 3.2.4 功率与电量
+
+**表 3-12 秒级 SCADA 的功率与电量测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 网侧有功功率 | 秒级有功功率,用于启停瞬态、限电响应与振动工况标注 | kW | 必须 |
+| 有功功率给定(有功设定值) | 主控或场控下发的有功给定,用于给定与反馈的跟随分析 | kW | 必须 |
+| 限电功率给定(百分比) | 以额定功率百分比下发的限电给定 | % | 建议 |
+| 可用功率参照 | 机组当前可发功率的参照值,用于限电损失口径 | kW | 建议 |
+| 无功功率 | 秒级无功功率,用于电压支撑瞬态 | kVar | 建议 |
+
+#### 3.2.5 风况
+
+**表 3-13 秒级 SCADA 的风况测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机舱风速 | 机舱风速计的秒级风速,用于阵风与湍流分析 | m/s | 必须 |
+| 独立风速计风速 | 独立风速计的秒级风速,用于风速互证与瞬态比较 | m/s | 必须 |
+| 秒级窗口风速标准差 | 秒级窗口内风速的标准差,是阵风与湍流度分析的基础 | 未取证 | 建议 |
+| 机舱风向 | 机舱风向标的秒级风向,用于对风瞬态分析 | ° | 必须 |
+| 偏航误差 | 机舱风向与机舱方位之差,用于对风偏差的秒级过程 | ° | 建议 |
+
+#### 3.2.6 转速
+
+**表 3-14 秒级 SCADA 的转速测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 发电机转速 | 秒级发电机转速,用于启停与超速瞬态 | r/min | 必须 |
+| 主轴转速(有则列) | 主传动轴转速;若现场不单独测量则本项不列,由叶轮转速代替 | 未取证 | 可选 |
+| 叶轮转速 | 秒级叶轮转速,用于叶尖速比与振动阶次跟踪 | r/min | 必须 |
+
+#### 3.2.7 桨距与变桨
+
+**表 3-15 秒级 SCADA 的桨距与变桨测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 1 号叶片桨距角 | 1 号叶片桨距角的秒级值,用于变桨瞬态与三叶一致性 | ° | 必须 |
+| 2 号叶片桨距角 | 2 号叶片桨距角的秒级值,用途同上 | ° | 必须 |
+| 3 号叶片桨距角 | 3 号叶片桨距角的秒级值,用途同上 | ° | 必须 |
+| 三叶桨距角极差 | 三叶桨距角最大值与最小值之差,是三叶一致性与变桨不同步的直接判据 | ° | 必须 |
+| 桨距角给定值(三叶) | 变桨系统接收的三叶桨距角给定,用于跟随误差与卡涩识别 | ° | 必须 |
+| 桨距角变化速率 | 秒级变桨速率,用于变桨速率与载荷分析 | °/s | 建议 |
+| 变桨电机电流 | 秒级变桨电机电流,用于变桨负载与卡涩识别 | A | 建议 |
+| 桨叶油压(A) | A 叶片变桨液压缸压力,用于变桨液压瞬态 | bar | 建议 |
+| 桨叶油压(B) | B 叶片变桨液压缸压力,用途同上 | bar | 建议 |
+| 桨叶油压(C) | C 叶片变桨液压缸压力,用途同上 | bar | 建议 |
+
+#### 3.2.8 温度
+
+**表 3-16 秒级 SCADA 的温度测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 发电机定子 U 相绕组温度 | 发电机 U 相绕组温度,用于三相极差与热瞬态 | ℃ | 必须 |
+| 发电机定子 V 相绕组温度 | 发电机 V 相绕组温度,用途同上 | ℃ | 必须 |
+| 发电机定子 W 相绕组温度 | 发电机 W 相绕组温度,用途同上 | ℃ | 必须 |
+| 发电机轴承温度(驱动端) | 发电机驱动端轴承温度,用于配对差与瞬态温升 | ℃ | 必须 |
+| 发电机轴承温度(非驱动端) | 发电机非驱动端轴承温度,用途同上 | ℃ | 必须 |
+| 齿轮箱油温 | 齿轮箱润滑油温度,用于瞬态热行为 | ℃ | 必须 |
+| 齿轮箱高速轴温度(发电机侧) | 齿轮箱高速轴发电机侧轴承温度 | ℃ | 必须 |
+| 齿轮箱高速轴温度(叶轮侧) | 齿轮箱高速轴叶轮侧轴承温度 | ℃ | 必须 |
+| 齿轮箱中间轴温度(发电机侧) | 齿轮箱中间轴发电机侧轴承温度 | ℃ | 必须 |
+| 齿轮箱中间轴温度(叶轮侧) | 齿轮箱中间轴叶轮侧轴承温度 | ℃ | 必须 |
+| 主轴承温度(轮毂侧) | 主轴承靠轮毂侧温度,用于配对差 | ℃ | 必须 |
+| 主轴承温度(齿轮箱侧) | 主轴承靠齿轮箱侧温度,用于配对差 | ℃ | 必须 |
+| 机舱温度 | 机舱内部温度,用于热环境判断 | ℃ | 必须 |
+| 环境温度 | 机舱外部环境温度,用于空气密度与热汇口径 | ℃ | 必须 |
+| 液压油温 | 液压站油温,用于液压瞬态热行为 | ℃ | 建议 |
+| 变桨柜温度 | 变桨柜内部温度,用于变桨电气热环境 | ℃ | 可选 |
+
+#### 3.2.9 偏航
+
+**表 3-17 秒级 SCADA 的偏航测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机舱方位角 | 机舱的秒级方位角,用于偏航动作过程与扇区分析 | ° | 必须 |
+| 偏航误差 | 秒级偏航误差,用于对风控制品质判断 | ° | 必须 |
+| 偏航压力 | 秒级偏航液压压力,用于偏航瞬态与建压过程 | bar | 必须 |
+| 扭缆角度 | 秒级电缆扭转角度,用于解缆过程分析 | ° | 建议 |
+| 偏航动作标志 | 标识机组当前是否正在偏航动作 | — | 建议 |
+| 偏航动作方向 | 标识当前偏航动作方向(顺时针或逆时针) | 文本 | 建议 |
+
+#### 3.2.10 液压与润滑
+
+**表 3-18 秒级 SCADA 的液压与润滑测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 液压系统压力 | 秒级液压站压力,用于建压、保压与泄压过程分析 | bar | 必须 |
+| 储能(蓄能器)压力 | 蓄能器的液压储能压力,用于储能衰减与建压时间判断 | 未取证 | 必须 |
+| 液压油温 | 秒级液压站油温 | ℃ | 建议 |
+| 润滑柱塞动作状态 | 润滑柱塞的动作状态与通电时长,用于润滑剂量核对 | s | 建议 |
+| 机械制动压力 | 秒级机械制动液压压力,用于制动投入过程 | bar | 建议 |
+
+#### 3.2.11 电网
+
+**表 3-19 秒级 SCADA 的电网测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 网侧相电流(R 相) | 秒级并网点 R 相电流,用于暂态电流与不平衡分析 | A | 建议 |
+| 网侧相电流(S 相) | 秒级并网点 S 相电流,用途同上 | A | 建议 |
+| 网侧相电流(T 相) | 秒级并网点 T 相电流,用途同上 | A | 建议 |
+| 网侧相电压(R 相) | 秒级并网点 R 相电压,用于电压暂降与失压过程 | V | 建议 |
+| 网侧相电压(S 相) | 秒级并网点 S 相电压,用途同上 | V | 建议 |
+| 网侧相电压(T 相) | 秒级并网点 T 相电压,用途同上 | V | 建议 |
+| 电网频率 | 秒级频率,用于频率事件与脱网过程 | Hz | 建议 |
+
+#### 3.2.12 状态与限电
+
+**表 3-20 秒级 SCADA 的状态与限电测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机组运行状态位 | 以位或枚举表示的运行状态,须能区分发电、停机、维护、限电等状态 | 文本 | 必须 |
+| 机组主状态码 | 机组主状态与故障码,用于状态迁移与事件对齐 | 文本 | 必须 |
+| 故障标志字(位掩码) | 以位域表示的故障标志字,逐位含义必须由现场图纸释义 | 文本 | 建议 |
+| 限电标志 | 标识机组当前是否受场控限电指令约束 | — | 必须 |
+| 限电指令流水 | 场控下发的限电指令序列(含单机挂靠区间) | 文本 | 建议 |
+| 并网标志 | 标识机组当前是否并网 | — | 建议 |
+| 停机原因码 | 停机时刻对应的原因码,用于停机账归因 | 文本 | 建议 |
+
+#### 3.2.13 振动触发量
+
+**表 3-21 秒级 SCADA 的振动触发量测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机舱振动(纵向) | 机舱纵向振动量,用于振动触发与机舱—塔架耦合分析 | g(词典亦并存 m/s²) | 建议 |
+| 机舱振动(横向) | 机舱横向振动量,用途同上 | g(词典亦并存 m/s²) | 建议 |
+| 机舱振动(垂向) | 机舱垂向振动量,用途同上 | g(词典亦并存 m/s²) | 建议 |
+| 塔架前后向摆动 | 塔架前后向摆动量,用于结构瞬态与共振分析 | % | 建议 |
+| 塔架侧向摆动 | 塔架侧向摆动量,用途同上 | % | 建议 |
+| 传动链加速度(有则列) | 传动链(齿轮箱、发电机)加速度振动触发量;若由独立的振动监测链路提供则不重复列 | 未取证 | 可选 |
+
+#### 3.2.14 计数与统计
+
+**表 3-22 秒级 SCADA 的计数与统计测点**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 偏航动作计数增量 | 秒级偏航动作计数增量,用于偏航剂量的高分辨率统计 | 次 | 建议 |
+| 运行时长增量 | 秒级运行计时增量,用于运行小时账的高分辨率核对 | s | 建议 |
+| 发电量增量 | 秒级电量增量,用于启停过程的电量细分 | kWh | 建议 |
+
+### 3.3 测风塔数据
+
+**用途**:自由流风速与湍流度的场级参考,是功率曲线与尾流分析的独立外部锚,不参与机组级判级。
+
+**最小可用量**:前期实地测风数据必须与可研报告的时间一致,且**至少满一年**,采样频率 5 分钟或 15 分钟;运行期数据为**一整年**的分钟级数据。若分钟级数据不具备风速标准差,则必须按采样频率 **1 秒**提交一整年的数据,测点为温度、湿度、气压、不同层高的风速与风向。
+
+**交付形态**:逐层高一张表或一张表内含层高标识,一行一个时间戳;层高必须显式给出。
+
+**表 3-23 测风塔数据的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 温度 | 各层高气温,用于空气密度折算与结冰判断 | ℃ | 必须 |
+| 湿度 | 相对湿度,用于空气密度折算与结冰判断 | % | 必须 |
+| 气压 | 大气压力,用于空气密度折算与风廓线修正 | kPa | 必须 |
+| 不同层高风速 | 各测量层高的水平风速,是自由流风速与风廓线的直接输入 | m/s | 必须 |
+| 不同层高风向 | 各测量层高的风向,用于风向玫瑰与扇区分析 | ° | 必须 |
+| 层高标识 | 每个测点所处的高度层,用于风廓线与切变计算 | m | 必须 |
+| 风速标准差 | 各层高风速的标准差,是湍流度的直接输入;分钟级数据不具备时必须用 1 秒采样替代 | 未取证 | 建议 |
+| 数据有效标志 | 标识该时刻各通道数据是否有效 | — | 建议 |
+
+### 3.4 功率曲线
+
+**用途**:发电性能偏差判别的基准,也是合同指标对标的基准。
+
+**最小可用量**:标准空气密度功率曲线与合同担保功率曲线各一条,覆盖切入至切出风速段;担保曲线的附属条件与保证条款必须同时给出。曲线**不得只给图片**;若只有图片,必须描点提取为数值表。
+
+**交付形态**:一个数值表,两列:风速、功率;附属条件与保证条款用一张说明表。
+
+**表 3-24 功率曲线的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 标准空气密度功率曲线 | 标准空气密度条件下的风速与功率成对点,是发电性能绝对偏差的基准 | m/s、kW | 建议 |
+| 合同担保功率曲线 | 合同保证的风速与功率成对点,是合同指标对标的基准 | m/s、kW | 建议 |
+| 空气密度 | 担保曲线所适用的空气密度 | kg/m³ | 建议 |
+| 湍流度 | 担保曲线所适用的湍流度 | — | 建议 |
+| 风切变 | 担保曲线所适用的风切变 | — | 建议 |
+| 保证可用率 | 合同保证的可用率下限及其考核口径 | % | 建议 |
+| 曲线口径说明 | 曲线是否含尾流、是否按空气密度归一、是否按湍流度分段 | 文本 | 建议 |
+
+### 3.5 机型参数
+
+**用途**:机型常量是振动的分母、功率曲线的基准、控制参数的参照;缺则振动阶次定位与曲线基准不可复核。
+
+**最小可用量**:一份机型级参数表,逐项给出下表全部必须项。
+
+**交付形态**:一个参数表,两列:参数、取值与单位。
+
+**表 3-25 机型参数的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机型代号 | 机型标识,用于机型级常量与字段契约的对应 | 文本 | 必须 |
+| 传动拓扑 | 传动形式与是否含齿轮箱 | 文本 | 必须 |
+| 变桨形式 | 集中变桨或独立变桨 | 文本 | 必须 |
+| 额定功率 | 单机额定有功功率 | kW | 必须 |
+| 叶轮直径 | 叶轮扫风直径 | m | 必须 |
+| 轮毂高度 | 轮毂中心高度,其构成(塔筒、基础平台)必须写明 | m | 必须 |
+| 齿轮箱传动比 | 齿轮箱总传动比 | — | 必须 |
+| 顺桨角 | 顺桨位置角度范围 | ° | 必须 |
+| 机组台数 | 全场机组台数 | 台 | 必须 |
+| 切入风速 | 机组切入风速 | 未取证 | 必须 |
+| 切出风速 | 机组切出风速 | 未取证 | 必须 |
+| 额定风速 | 机组额定风速 | 未取证 | 必须 |
+| 生存温度 | 机组设计生存环境温度范围 | 未取证 | 必须 |
+| 齿轮箱形式与型号 | 齿轮箱级数、型号与润滑油量 | 文本 | 建议 |
+| 液压系统运行压力 | 液压系统设计运行压力 | bar | 建议 |
+| 塔筒与基础形式 | 塔筒段数与基础形式 | 文本 | 可选 |
+
+### 3.6 机位分布数据
+
+**用途**:邻域与尾流先验、扇区分析、台号与集电线路的地理对照。坐标**不可自算**,必须由现场实体提交。
+
+**最小可用量**:逐台坐标、海拔(含轮毂高度)与机型,另加升压站。
+
+**交付形态**:一个逐台表,一行一台机组,列含台号标识、经度、纬度、海拔、轮毂高度、机型、集电线路归属。
+
+**表 3-26 机位分布数据的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 机组编号 | 逐台机组标识,用于与各类源件的台号对照 | 文本 | 建议 |
+| 经度 | 机位经度 | ° | 建议 |
+| 纬度 | 机位纬度 | ° | 建议 |
+| 海拔高度 | 机位地面海拔 | m | 建议 |
+| 轮毂高度 | 机位轮毂中心高度 | m | 建议 |
+| 机组机型 | 该机位所属机型,混排场必须逐台给出 | 文本 | 建议 |
+| 集电线路归属 | 该机位所属集电线路(串),用于邻域与分组分析 | 文本 | 建议 |
+| 升压站坐标与海拔 | 升压站位置与海拔,用于场级参考 | °、m | 可选 |
+
+### 3.7 机组状态、事件、故障、告警数据
+
+**用途**:报警台账、停机账、限电绑定、首发链、检修工单台账与闭环验证。
+
+**最小可用量**:覆盖分析时间窗的故障码表、状态码表及其说明,另加逐条故障记录、事件记录与告警记录。
+
+**交付形态**:一套代码表(两列:代码、说明)加逐条记录表;每条记录一行,必须带激活时刻与代码列。
+
+**表 3-27 状态、事件、故障、告警数据的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 激活时刻 | 报警或事件的激活时间戳 | s | 必须 |
+| 复位时刻 | 报警或事件的复位时间戳;尚未闭合的必须显式留空,不得填零 | s | 必须 |
+| 持续时长 | 由激活与复位时刻得到的区间时长 | s | 建议 |
+| 事件类型 | 标识该条记录属于告警区间账、瞬时事件还是停机状态账 | 文本 | 必须 |
+| 事件分组 | 标识记录来源(机组本体、远程操作、场控系统) | 文本 | 必须 |
+| 实体标识 | 标识记录所属对象(机组或场级);机组分析必须先按此过滤 | 文本 | 必须 |
+| 报警码 | 报警码,必须同时给出其所属命名空间(不同命名空间的码不得混判) | 文本 | 必须 |
+| 故障码 | 机组故障码 | 文本 | 必须 |
+| 状态码 | 机组状态码 | 文本 | 必须 |
+| 码表说明 | 故障码与状态码的中文说明及其分类 | 文本 | 必须 |
+| 辅助参数 | 随码值携带的辅助文本(定宽右填时必须先去空格) | 文本 | 建议 |
+| 记录来源通道 | 该条记录的采集来源或写入者 | 文本 | 建议 |
+| 首发标识 | 同一停机链的首条事件标识;源件不提供时必须说明,并由本系统按登记口径自算 | 文本 | 建议 |
+| 责任方分类 | 故障责任归属的分类口径与枚举含义 | 文本 | 可选 |
+
+### 3.8 机舱 CMS 振动数据
+
+**用途**:振动侧的**唯一可重算源件**。振动索引、谱库、部件状态评估、在升与换件闭环、机群基线都由它算。
+
+**最小可用量**:**至少一个季度**的秒级振动数据(非加密)与原始振动时域波形;测点须覆盖主轴、齿轮箱、发电机与叶轮转速。
+
+**交付形态**:逐次测量一条记录,记录内含测点、测量类型、时间戳、转速、波形或谱数据;另附厂家的振动评估与确诊结论。
+
+**单位口径**:振动加速度 m/s²、振动速度 m/s、无量纲指标无单位、转速 r/min、频率 Hz、时长 s、灵敏度 mV/g。
+
+**表 3-28 机舱 CMS 振动数据的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 主轴前轴承振动 | 主轴前轴承测点振动,用于主轴承状态评估 | m/s²、m/s | 必须 |
+| 主轴后轴承振动 | 主轴后轴承测点振动,用于主轴承状态评估 | m/s²、m/s | 必须 |
+| 行星级振动 | 齿轮箱行星级测点振动,用于行星级啮合状态评估 | m/s²、m/s | 必须 |
+| 中间轴振动 | 齿轮箱中间轴测点振动,用于中间轴与轴承状态评估 | m/s²、m/s | 必须 |
+| 高速轴(发电机侧)振动 | 齿轮箱高速轴发电机侧测点振动 | m/s²、m/s | 必须 |
+| 高速轴(叶轮侧)振动 | 齿轮箱高速轴叶轮侧测点振动 | m/s²、m/s | 必须 |
+| 发电机驱动端振动 | 发电机驱动端测点振动 | m/s²、m/s | 必须 |
+| 发电机非驱动端振动 | 发电机非驱动端测点振动 | m/s²、m/s | 必须 |
+| 系统监视量 | 采集系统自身的运行与存储占用监视量 | % | 建议 |
+| 叶轮转速(阶次跟踪分母) | 振动测量同步记录的转速,是叶轮转频与阶次跟踪的分母 | r/min | 必须 |
+| 原始时域波形 | 加速度或速度的原始时域波形,用于重算谱与故障回溯 | m/s²、m/s | 必须 |
+| 频谱数据 | 各测点的自谱与包络谱,用于特征频率比对 | m/s²、m/s | 必须 |
+| 无量纲指标 | 峰值、有效值、峭度、峰值因子等标量指标 | — | 必须 |
+| 频率分辨率与分析带宽 | 谱线的频率分辨率与分析带宽,是特征频率能否分辨的前提 | Hz | 必须 |
+| 波形时长与平均次数 | 每次测量的波形时长与平均次数 | s | 建议 |
+| 传感器灵敏度 | 传感器灵敏度,用于量纲换算 | mV/g | 建议 |
+| 报警阈值与迟滞 | 红、黄、蓝各级报警阈值与迟滞设置 | 文本 | 建议 |
+| 工况分箱键 | 每次测量所属的功率或转速分箱,用于同工况比较 | 文本 | 必须 |
+| 测量类型与测量名 | 每次测量的类型与名称(自谱、包络谱、时域波形等) | 文本 | 必须 |
+| 厂家评估结论 | 厂家出具的振动评估与确诊结论 | 文本 | 建议 |
+
+### 3.9 机组统计数据或运行数据
+
+**用途**:发电账、可利用率与利用小时、损失电量、偏航与停机统计,用于与运行数据侧损失账交叉核对。
+
+**最小可用量**:覆盖分析时间窗的年、月、日统计量,并注明统计口径。
+
+**交付形态**:一张统计表,一行一个统计粒度(年、月或日),逐台或全场汇总必须写明。
+
+**表 3-29 机组统计数据或运行数据的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 发电量 | 统计期内的发电量,是电量账的主指标 | kWh | 必须 |
+| 平均风速 | 统计期内的平均风速,用于发电量口径核实 | m/s | 必须 |
+| 可利用率 | 统计期内的机组可利用率,其口径必须成文 | % | 必须 |
+| 故障停机时长 | 统计期内因故障导致的停机时长 | min | 必须 |
+| 限电损失电量 | 统计期内因限电损失的电量 | kWh | 必须 |
+| 上网电量或计量电量 | 关口计量电量,用于与机组发电量对拍 | kWh | 建议 |
+| 维护停机时长 | 统计期内因维护导致的停机时长 | min | 建议 |
+| 故障损失电量 | 统计期内因故障损失的电量 | kWh | 建议 |
+| 偏航时长 | 统计期内偏航动作累计时长 | s | 建议 |
+| 偏航次数 | 统计期内偏航动作累计次数 | 次 | 建议 |
+| 利用小时数 | 统计期内的等效利用小时数;其单位口径必须由现场明确 | 未取证 | 可选 |
+| 机组自用电量 | 统计期内机组自身耗电量 | kWh | 可选 |
+
+### 3.10 机组保护定值及控制参数
+
+**用途**:保护判据的最终依据、控制参数一致性的参照、偏航与降载控制策略的依据。
+
+**最小可用量**:一份成册的定值表(含每条定值的下限、上限、默认值、当前值与单位),另加偏航控制策略与降载控制策略文档。控制参数一致性件可先由运行数据反演顶替,但顶替口径必须写明。
+
+**交付形态**:一个定值表,一行一条定值,列为编号、名称、下限、上限、默认值、当前值、单位、复位方式;策略文档另册。
+
+**表 3-30 机组保护定值及控制参数的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 定值编号 | 定值的唯一编号,用于跨源对照 | 文本 | 建议 |
+| 定值名称 | 定值的中文名称 | 文本 | 建议 |
+| 定值下限 | 该定值允许的最小值 | 随定值(℃、bar、m/s、r/min、°、s、Hz、kW、V、A 等) | 建议 |
+| 定值上限 | 该定值允许的最大值 | 随定值(℃、bar、m/s、r/min、°、s、Hz、kW、V、A 等) | 建议 |
+| 定值默认值 | 出厂默认取值 | 随定值(℃、bar、m/s、r/min、°、s、Hz、kW、V、A 等) | 建议 |
+| 定值当前值 | 机组当前生效取值 | 随定值(℃、bar、m/s、r/min、°、s、Hz、kW、V、A 等) | 建议 |
+| 定值单位 | 每条定值自带的单位 | 文本 | 建议 |
+| 复位方式 | 定值的复位或恢复方式 | 文本 | 可选 |
+| 温度保护定值 | 温度类保护的动作与报警定值 | ℃ | 建议 |
+| 压力保护定值 | 压力类保护的动作与报警定值 | bar | 建议 |
+| 速度保护定值 | 转速与超速类保护的动作定值 | r/min | 建议 |
+| 偏航控制策略 | 偏航动作条件、对风死区、解缆策略的成文说明 | 文本 | 建议 |
+| 降载控制策略 | 降载与限功率控制的成文说明 | 文本 | 建议 |
+
+### 3.11 机组故障录波数据
+
+**用途**:故障前后机组数据的高分辨率快照,用于故障机理的秒级回溯。
+
+**最小可用量**:每次机组故障后,触发前后一段的记录文件;触发前与触发后的时长、采样频率与通道清单必须随件给出。
+
+**交付形态**:逐次触发一条记录,含触发时刻、触发原因、通道清单与波形数据;时间基准必须与秒级数据一致。
+
+**表 3-31 机组故障录波数据的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 触发时刻 | 录波触发的时间戳,必须能与秒级时间戳对齐 | s | 可选 |
+| 触发原因 | 触发录波的事件或码值 | 文本 | 可选 |
+| 采样频率 | 录波的采样频率,必须明确实际值 | Hz | 可选 |
+| 记录时长 | 触发前后各自的记录时长 | s | 可选 |
+| 通道清单 | 录波包含的通道及其业务含义 | 文本 | 可选 |
+| 转速类通道 | 录波中的发电机转速与叶轮转速 | r/min | 可选 |
+| 功率类通道 | 录波中的有功功率与无功功率 | kW、kVar | 可选 |
+| 桨距与液压通道 | 录波中的桨距角与液压压力 | °、bar | 可选 |
+| 温度类通道 | 录波中的温度量 | ℃ | 可选 |
+| 振动类通道 | 录波中的振动量(若有) | m/s²、g | 可选 |
+
+### 3.12 可研报告及微观选址报告
+
+**用途**:逐台设计基线与测风依据,用作发电性能的外部对照。
+
+**最小可用量**:全本,含测风依据、逐台设计基线、尾流折减与机型选型结论。
+
+**交付形态**:成册文档,可提取的结论必须同时给出数值表。
+
+**表 3-32 可研报告及微观选址报告的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 场址测风依据 | 可研采用的测风塔位置、层高与测风时段说明 | 文本 | 可选 |
+| 逐台设计发电小时数 | 设计阶段逐台年发电小时数,作为外部对照基线 | 未取证 | 可选 |
+| 尾流折减 | 设计阶段尾流损失比例 | % | 可选 |
+| 机型选型依据 | 机型选择与安全等级 | 文本 | 可选 |
+| 场址环境条件 | 气温、盐雾、雷电、地震等设计条件 | 文本 | 可选 |
+| 微观选址结论 | 逐台机位微调与限制条件 | 文本 | 可选 |
+
+### 3.13 风电场简介
+
+**用途**:场站元信息,用于背景、署名与台账筛选。
+
+**最小可用量**:一份,含场站规模、投产时间与场址特性。
+
+**交付形态**:一份说明文档。
+
+**表 3-33 风电场简介的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 场站名称 | 对外正式名称,用于署名与台账筛选 | 文本 | 可选 |
+| 装机容量 | 全场装机容量 | kW | 可选 |
+| 机组台数 | 全场机组台数 | 台 | 可选 |
+| 投产时间 | 全场或分批投产时间 | 文本 | 可选 |
+| 场址与地形特性 | 陆上或海上、地形与海域特性 | 文本 | 可选 |
+| 并网方式 | 集电线路与升压站构成 | 文本 | 可选 |
+
+### 3.14 风电场坐标、机位布置图
+
+**用途**:图示化的机位与电气布置对照,用于邻域、尾流与扇区分析的图面核对。
+
+**最小可用量**:全场坐标总图与机位布置图各一份,含升压站与集电线路走向。
+
+**交付形态**:图纸的电子版,另附坐标表;坐标系统与图纸基准必须写明。
+
+**表 3-34 风电场坐标、机位布置图的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 全场坐标总图 | 全场机位与升压站的位置总图 | 文本 | 可选 |
+| 机位布置图 | 逐台机位编号与相互位置关系 | 文本 | 可选 |
+| 升压站与集电线路图 | 升压站、集电线路走向与接入关系 | 文本 | 可选 |
+| 坐标系统与基准 | 图纸与坐标表所用的坐标系统与高程基准 | 文本 | 可选 |
+| 图纸比例与图例 | 图纸比例尺与图例说明 | 文本 | 可选 |
+| 可编辑矢量图 | 可编辑的矢量图件,用于叠加分析 | 文本 | 可选 |
+
+### 3.15 风机技术协议及风机合同
+
+**用途**:合同指标对标,包括担保曲线、保证率与考核条款。
+
+**最小可用量**:合同全本,含担保曲线附件、保证率条款、整改与考核条款。
+
+**交付形态**:成册文本;担保曲线的附属条件必须与曲线同册给出。
+
+**表 3-35 风机技术协议及风机合同的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 合同担保功率曲线 | 合同附件的风速与功率成对点,是合同指标对标的基准 | m/s、kW | 必须 |
+| 保证可用率 | 合同保证的可用率指标及其考核口径 | % | 必须 |
+| 担保曲线适用条件 | 担保曲线适用的空气密度、湍流度与风切变 | 文本 | 必须 |
+| 整改与考核条款 | 指标不达标时的整改期与考核方式 | 文本 | 建议 |
+| 违约金条款 | 违约认定与赔偿条款 | 文本 | 建议 |
+| 电气与噪声性能条款 | 电能质量、无功能力、电压穿越与噪声要求 | 文本 | 建议 |
+
+### 3.16 并网到现在的生产报表
+
+**用途**:日粒度与月粒度的发电账,用于与运行数据侧损失账交叉核对。
+
+**最小可用量**:并网至今的连续序列,日粒度或月粒度;口径说明必须成文。
+
+**交付形态**:一张统计表,一行一个统计粒度,含风速、发电量、故障率与限电量。
+
+**表 3-36 并网到现在的生产报表的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 风速 | 统计期平均风速 | m/s | 建议 |
+| 发电量 | 统计期发电量 | kWh | 建议 |
+| 故障率 | 统计期故障率 | % | 建议 |
+| 限电量 | 统计期限电损失电量 | kWh | 建议 |
+| 统计粒度 | 日粒度与月粒度报表的起止与对应关系 | 文本 | 建议 |
+| 口径说明 | 发电量是否含厂用电、是否含限电、是否含尾流修正 | 文本 | 建议 |
+
+### 3.17 周围已建风电场生产报表
+
+**用途**:尾流与场级损耗的外部对照。
+
+**最小可用量**:覆盖分析时间窗的统计报表,含风速、发电量、故障率与限电量。
+
+**交付形态**:一张统计表,一行一个统计期。
+
+**表 3-37 周围已建风电场生产报表的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 风速 | 邻场统计期平均风速 | m/s | 可选 |
+| 发电量 | 邻场统计期发电量 | kWh | 可选 |
+| 故障率 | 邻场统计期故障率 | % | 可选 |
+| 限电量 | 邻场统计期限电损失电量 | kWh | 可选 |
+| 场站基础信息 | 邻场装机容量、机组台数与投产时间 | 文本 | 可选 |
+
+### 3.18 运维记录
+
+**用途**:检修面与闭环验证,与状态事件、油样三源交叉。
+
+**最小可用量**:覆盖分析时间窗的维护台账与检修台账,另加油液化验报告。
+
+**交付形态**:一个作业台账表,一行一次作业;油样单独一张化验表,一行一个化验项目。
+
+**表 3-38 运维记录的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 作业时刻 | 维护或检修作业的发生时刻 | s | 建议 |
+| 机组编号 | 作业对象机组 | 文本 | 建议 |
+| 部件 | 作业涉及的部件与部位 | 文本 | 建议 |
+| 作业内容 | 作业描述与处置动作 | 文本 | 建议 |
+| 作业类型 | 定期维护、故障处置、技术改造等分类 | 文本 | 建议 |
+| 停机与恢复时刻 | 作业引起的停机起止时刻 | s | 建议 |
+| 油样取样日期 | 油样取样与化验日期 | 文本 | 建议 |
+| 油样部件 | 取样的部件与部位 | 文本 | 建议 |
+| 化验项目 | 化验项目名称 | 文本 | 建议 |
+| 化验结果 | 各化验项目的数值与判定 | 文本 | 建议 |
+
+### 3.19 大部件更换记录
+
+**用途**:部件在升与换件闭环的判定输入,用于部件可靠性与寿期评估。
+
+**最小可用量**:逐次更换的时间、机组、部件与原因。
+
+**交付形态**:一个更换台账表,一行一次更换。
+
+**表 3-39 大部件更换记录的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 更换时刻 | 部件更换的发生时刻 | s | 建议 |
+| 机组编号 | 更换对象机组 | 文本 | 建议 |
+| 更换部件 | 更换的部件与安装位置 | 文本 | 建议 |
+| 更换原因 | 更换原因与失效模式 | 文本 | 建议 |
+| 新旧件标识 | 拆下件与装上件的序列号或批次 | 文本 | 建议 |
+| 备件来源 | 备件批次与供应商 | 文本 | 可选 |
+| 拆下件累计运行时长 | 拆下件自投运或上次更换起的累计运行时长 | s | 可选 |
+
+### 3.20 图纸、技术文件
+
+**用途**:故障码位域语义、控制逻辑、液压与电气回路的最终依据,也是判据的出处。
+
+**最小可用量**:风机电气控制相关图纸、液压站图纸、逻辑图或控制方式文件、保护定值表。
+
+**交付形态**:图纸与文件的电子版;文本层的图纸必须有可检索文字,扫描件必须同时给出关键数值表。
+
+**表 3-40 图纸、技术文件的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 电气控制原理图 | 主控与电气控制原理,是位域语义与逻辑判据的最终依据 | 文本 | 必须 |
+| 端子与接线表 | 端子号与接线对照,是通道与测点对应关系的依据 | 文本 | 必须 |
+| 主控逻辑图或控制方式文件 | 控制逻辑与状态机说明 | 文本 | 必须 |
+| 液压站原理图 | 液压与偏航系统的回路构成 | 文本 | 必须 |
+| 保护定值表 | 温度、压力、速度等保护定值清单 | 文本 | 必须 |
+| 故障码位域释义 | 状态字与故障标志字的逐位含义 | 文本 | 必须 |
+| 结构树文件 | 机组结构树与部件层级,是部件归集的依据 | 文本 | 建议 |
+| 图纸标注量纲 | 图纸标注所含的电压、电流、压力、温度等量纲 | 文本 | 建议 |
+
+### 3.21 设备参数
+
+**用途**:振动阶次定位与寿命评估的分母;缺则「哪个部件、哪一级」的定位只能停在候选级。
+
+**最小可用量**:逐部件的型号、厂家与齿数参数;随机型常量固化的特征频率表。
+
+**交付形态**:一个参数表,一行一个部件或一项参数;齿数与个数类必须给出整数。
+
+**表 3-41 设备参数的测点清单**
+
+| 测点/字段名 | 业务含义 | 度量单位 | 是否必须 |
+|---|---|---|---|
+| 主轴轴承型号及厂家 | 主轴轴承型号与制造厂家,是主轴承特征频率的分母 | 文本 | 建议 |
+| 齿轮箱型号厂家 | 齿轮箱型号与制造厂家 | 文本 | 建议 |
+| 齿轮箱传动比 | 齿轮箱各级与总传动比 | — | 建议 |
+| 行星轮内齿圈齿数 | 行星级内齿圈齿数 | 个 | 建议 |
+| 行星轮个数 | 行星级行星轮个数 | 个 | 建议 |
+| 太阳轮齿数 | 行星级太阳轮齿数 | 个 | 建议 |
+| 行星轮齿数 | 行星级行星轮齿数 | 个 | 建议 |
+| 平行轮大齿轮齿数 | 平行级大齿轮齿数 | 个 | 建议 |
+| 平行轮小齿轮齿数 | 平行级小齿轮齿数 | 个 | 建议 |
+| 齿轮箱各级轴承型号 | 齿轮箱各级轴承型号,是各级轴承特征频率的分母 | 文本 | 建议 |
+| 发电机型号厂家 | 发电机型号与制造厂家 | 文本 | 建议 |
+| 发电机轴承型号 | 发电机两端轴承型号 | 文本 | 建议 |
+| 叶片参数 | 叶片长度、气动外形与质量参数 | m、文本 | 建议 |
+| 变桨电机参数 | 变桨电机型号与主要参数 | 文本 | 建议 |
+| 变桨驱动齿数 | 变桨驱动小齿与大齿齿数 | 个 | 建议 |
+| 偏航电机参数 | 偏航电机型号与主要参数 | 文本 | 建议 |
+| 偏航驱动齿数 | 偏航驱动小齿与大齿齿数 | 个 | 建议 |
+
+## 4 测点汇总索引
+
+本章把第 3 章的测点按业务域汇总,供现场按域核对与按域收资。同一测点在不同数据类别中出现时分行列出,所属数据类别一列指明其出处。
+
+**表 4-1 测点汇总索引**
+
+| 业务域 | 测点/字段名 | 度量单位 | 是否必须 | 所属数据类别 |
+|---|---|---|---|---|
+| 功率与电量 | 网侧有功功率(区间均值) | kW | 必须 | 10 分钟 SCADA |
+| 功率与电量 | 网侧有功功率(区间最大值) | kW | 必须 | 10 分钟 SCADA |
+| 功率与电量 | 网侧有功功率(区间最小值) | kW | 建议 | 10 分钟 SCADA |
+| 功率与电量 | 有功功率给定(区间期末值) | kW | 必须 | 10 分钟 SCADA |
+| 功率与电量 | 可用功率参照 | kW | 建议 | 10 分钟 SCADA |
+| 功率与电量 | 区间发电量 | kWh | 建议 | 10 分钟 SCADA |
+| 功率与电量 | 累计发电量 | kWh | 建议 | 10 分钟 SCADA |
+| 功率与电量 | 机组自用电量 | kWh | 可选 | 10 分钟 SCADA |
+| 功率与电量 | 无功功率 | kVar | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 功率与电量 | 无功电量 | kVarh | 可选 | 10 分钟 SCADA |
+| 功率与电量 | 功率因数 | — | 可选 | 10 分钟 SCADA、电网 |
+| 功率与电量 | 网侧有功功率(秒级) | kW | 必须 | 秒级 SCADA |
+| 功率与电量 | 有功功率给定(秒级) | kW | 必须 | 秒级 SCADA |
+| 功率与电量 | 秒级电量增量 | kWh | 建议 | 秒级 SCADA |
+| 风况 | 机舱风速(原生风速计) | m/s | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 风况 | 独立风速计风速 | m/s | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 风况 | 补偿风速 | m/s | 建议 | 10 分钟 SCADA |
+| 风况 | 风速标准差 | 未取证 | 建议 | 10 分钟 SCADA |
+| 风况 | 秒级窗口风速标准差 | 未取证 | 建议 | 秒级 SCADA |
+| 风况 | 机舱风向 | ° | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 风况 | 绝对风向估计 | ° | 建议 | 10 分钟 SCADA |
+| 风况 | 空气密度 | kg/m³ | 建议 | 10 分钟 SCADA |
+| 风况 | 大气压力 | kPa | 可选 | 10 分钟 SCADA |
+| 转速 | 发电机转速(区间均值) | r/min | 必须 | 10 分钟 SCADA |
+| 转速 | 发电机转速(区间最大值) | r/min | 必须 | 10 分钟 SCADA |
+| 转速 | 叶轮转速(区间均值) | r/min | 必须 | 10 分钟 SCADA |
+| 转速 | 发电机转速给定 | r/min | 可选 | 10 分钟 SCADA |
+| 转速 | 发电机转速(秒级) | r/min | 必须 | 秒级 SCADA |
+| 转速 | 叶轮转速(秒级) | r/min | 必须 | 秒级 SCADA |
+| 转速 | 主轴转速(有则列) | 未取证 | 可选 | 秒级 SCADA |
+| 桨距与变桨 | 叶片桨距角(1 号) | ° | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 桨距与变桨 | 叶片桨距角(2 号) | ° | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 桨距与变桨 | 叶片桨距角(3 号) | ° | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 桨距与变桨 | 三叶桨距角极差 | ° | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 桨距与变桨 | 桨距角给定值 | ° | 建议 | 10 分钟 SCADA |
+| 桨距与变桨 | 桨距角给定值(三叶) | ° | 必须 | 秒级 SCADA |
+| 桨距与变桨 | 桨距角变化速率 | °/s | 可选 | 10 分钟 SCADA、秒级 SCADA |
+| 桨距与变桨 | 变桨电机电流 | A | 可选 | 10 分钟 SCADA、秒级 SCADA |
+| 桨距与变桨 | 变桨电机转矩 | Nm | 可选 | 10 分钟 SCADA |
+| 温度 | 环境温度 | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 机舱温度 | ℃ | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 轮毂温度 | ℃ | 建议 | 10 分钟 SCADA |
+| 温度 | 主轴承温度(轮毂侧) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 主轴承温度(齿轮箱侧) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 齿轮箱油温 | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 齿轮箱高速轴温度(发电机侧) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 齿轮箱高速轴温度(叶轮侧) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 齿轮箱中间轴温度(发电机侧) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 齿轮箱中间轴温度(叶轮侧) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 齿轮箱油滤温度 | ℃ | 建议 | 10 分钟 SCADA |
+| 温度 | 齿轮箱油滤温度(备用测点) | ℃ | 可选 | 10 分钟 SCADA |
+| 温度 | 发电机定子绕组温度(U 相) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 发电机定子绕组温度(V 相) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 发电机定子绕组温度(W 相) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 发电机轴承温度(驱动端) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 发电机轴承温度(非驱动端) | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 液压油温 | ℃ | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 温度 | 机舱变压器油温 | ℃ | 建议 | 10 分钟 SCADA |
+| 温度 | 变压器层空气温度 | ℃ | 建议 | 10 分钟 SCADA |
+| 温度 | 变压器油温(同源副本) | ℃ | 可选 | 10 分钟 SCADA |
+| 温度 | 主开关板柜外温 | ℃ | 建议 | 10 分钟 SCADA |
+| 温度 | 主开关板柜内温 | 未取证 | 建议 | 10 分钟 SCADA |
+| 温度 | 控制柜左列温度 | 未取证 | 建议 | 10 分钟 SCADA |
+| 温度 | 控制柜右列温度 | 未取证 | 建议 | 10 分钟 SCADA |
+| 温度 | 刹车温度(发电机侧) | 未取证 | 建议 | 10 分钟 SCADA |
+| 温度 | 刹车温度(叶轮侧) | 未取证 | 建议 | 10 分钟 SCADA |
+| 温度 | 变流器冷却水温度 | 未取证 | 必须 | 10 分钟 SCADA |
+| 温度 | 变桨柜温度 | ℃ | 可选 | 10 分钟 SCADA、秒级 SCADA |
+| 偏航 | 偏航压力(区间均值) | bar | 必须 | 10 分钟 SCADA |
+| 偏航 | 偏航压力(区间最大值) | bar | 必须 | 10 分钟 SCADA |
+| 偏航 | 偏航压力(区间最小值) | bar | 必须 | 10 分钟 SCADA |
+| 偏航 | 偏航压力(秒级) | bar | 必须 | 秒级 SCADA |
+| 偏航 | 偏航动作计数 | 次 | 必须 | 10 分钟 SCADA |
+| 偏航 | 偏航润滑泵通电时长 | s | 必须 | 10 分钟 SCADA |
+| 偏航 | 机舱方位角 | ° | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 偏航 | 偏航误差 | ° | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 偏航 | 偏航动作时长(顺时针) | s | 建议 | 10 分钟 SCADA |
+| 偏航 | 偏航动作时长(逆时针) | s | 建议 | 10 分钟 SCADA |
+| 偏航 | 扭缆角度 | ° | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 偏航 | 自动解缆标志 | — | 可选 | 10 分钟 SCADA |
+| 偏航 | 偏航动作方向 | 文本 | 建议 | 秒级 SCADA |
+| 液压与润滑 | 液压系统压力(区间均值) | bar | 必须 | 10 分钟 SCADA |
+| 液压与润滑 | 液压系统压力(区间最大值) | bar | 必须 | 10 分钟 SCADA |
+| 液压与润滑 | 液压系统压力(区间最小值) | bar | 必须 | 10 分钟 SCADA |
+| 液压与润滑 | 液压系统压力(秒级) | bar | 必须 | 秒级 SCADA |
+| 液压与润滑 | 储能(蓄能器)压力 | 未取证 | 必须 | 秒级 SCADA |
+| 液压与润滑 | 润滑柱塞通电时长 | s | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 液压与润滑 | 齿轮箱油路入口压力 | bar | 建议 | 10 分钟 SCADA |
+| 液压与润滑 | 齿轮箱主滤前压力 | bar | 建议 | 10 分钟 SCADA |
+| 液压与润滑 | 齿轮箱主滤后压力 | bar | 建议 | 10 分钟 SCADA |
+| 液压与润滑 | 齿轮箱旁路滤油压力 | bar | 建议 | 10 分钟 SCADA |
+| 液压与润滑 | 桨叶油压(A) | bar | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 液压与润滑 | 桨叶油压(B) | bar | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 液压与润滑 | 桨叶油压(C) | bar | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 液压与润滑 | 机械制动压力 | bar | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 电网 | 网侧相电流(三相) | A | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 电网 | 网侧相电压(三相) | V | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 电网 | 电网频率 | Hz | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 状态与限电 | 机组主状态码 | 文本 | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 状态与限电 | 机组运行状态位 | 文本 | 必须 | 秒级 SCADA |
+| 状态与限电 | 刹车状态位 | 文本 | 必须 | 10 分钟 SCADA |
+| 状态与限电 | 运行状态字 | 文本 | 建议 | 10 分钟 SCADA |
+| 状态与限电 | 子状态字 | 文本 | 建议 | 10 分钟 SCADA |
+| 状态与限电 | 故障标志字 | 文本 | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 状态与限电 | 限电标志 | — | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 状态与限电 | 限电功率给定(有功设定值) | kW | 必须 | 10 分钟 SCADA、秒级 SCADA |
+| 状态与限电 | 限电功率给定(百分比) | % | 建议 | 10 分钟 SCADA、秒级 SCADA |
+| 状态与限电 | 降载反馈功率 | kW | 可选 | 10 分钟 SCADA |
+| 状态与限电 | 限电指令流水 | 文本 | 建议 | 秒级 SCADA |
+| 状态与限电 | 并网标志 | — | 建议 | 秒级 SCADA |
+| 状态与限电 | 停机原因码 | 文本 | 建议 | 秒级 SCADA |
+| 计数与统计 | 风速故障计数(两路) | 次 | 必须 | 10 分钟 SCADA |
+| 计数与统计 | 累计运行时长 | s | 建议 | 10 分钟 SCADA |
+| 计数与统计 | 累计发电时长 | s | 建议 | 10 分钟 SCADA |
+| 计数与统计 | 故障停机时长 | min | 建议 | 10 分钟 SCADA、机组统计 |
+| 计数与统计 | 电网故障时长 | 未取证 | 可选 | 10 分钟 SCADA |
+| 计数与统计 | 偏航动作计数增量 | 次 | 建议 | 秒级 SCADA |
+| 计数与统计 | 运行时长增量 | s | 建议 | 秒级 SCADA |
+| 振动 | 主轴前轴承振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 主轴后轴承振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 行星级振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 中间轴振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 高速轴(发电机侧)振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 高速轴(叶轮侧)振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 发电机驱动端振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 发电机非驱动端振动 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 原始时域波形 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 频谱数据 | m/s²、m/s | 必须 | 机舱 CMS 振动 |
+| 振动 | 无量纲指标 | — | 必须 | 机舱 CMS 振动 |
+| 振动 | 叶轮转速(阶次跟踪分母) | r/min | 必须 | 机舱 CMS 振动 |
+| 振动 | 频率分辨率与分析带宽 | Hz | 必须 | 机舱 CMS 振动 |
+| 振动 | 传感器灵敏度 | mV/g | 建议 | 机舱 CMS 振动 |
+| 振动 | 报警阈值与迟滞 | 文本 | 建议 | 机舱 CMS 振动 |
+| 振动 | 工况分箱键 | 文本 | 必须 | 机舱 CMS 振动 |
+| 振动 | 机舱振动(三向) | g、m/s² | 建议 | 秒级 SCADA |
+| 振动 | 塔架前后向摆动 | % | 建议 | 秒级 SCADA |
+| 振动 | 塔架侧向摆动 | % | 建议 | 秒级 SCADA |
+| 振动 | 传动链加速度(有则列) | 未取证 | 可选 | 秒级 SCADA |
+| 台账与事件 | 激活时刻 | s | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 复位时刻 | s | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 持续时长 | s | 建议 | 状态、事件、故障、告警 |
+| 台账与事件 | 事件类型 | 文本 | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 事件分组 | 文本 | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 实体标识 | 文本 | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 报警码 | 文本 | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 故障码 | 文本 | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 状态码 | 文本 | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 码表说明 | 文本 | 必须 | 状态、事件、故障、告警 |
+| 台账与事件 | 作业时刻 | s | 建议 | 运维记录 |
+| 台账与事件 | 机组编号 | 文本 | 建议 | 运维记录、大部件更换、机位分布 |
+| 台账与事件 | 部件 | 文本 | 建议 | 运维记录、大部件更换 |
+| 台账与事件 | 作业内容 | 文本 | 建议 | 运维记录 |
+| 台账与事件 | 作业类型 | 文本 | 建议 | 运维记录 |
+| 台账与事件 | 更换时刻 | s | 建议 | 大部件更换 |
+| 台账与事件 | 更换原因 | 文本 | 建议 | 大部件更换 |
+| 台账与事件 | 新旧件标识 | 文本 | 建议 | 大部件更换 |
+| 参数与图纸 | 机型代号 | 文本 | 必须 | 机型参数 |
+| 参数与图纸 | 额定功率 | kW | 必须 | 机型参数 |
+| 参数与图纸 | 叶轮直径 | m | 必须 | 机型参数 |
+| 参数与图纸 | 轮毂高度 | m | 必须 | 机型参数 |
+| 参数与图纸 | 齿轮箱传动比 | — | 必须 | 机型参数 |
+| 参数与图纸 | 顺桨角 | ° | 必须 | 机型参数 |
+| 参数与图纸 | 切入风速 | 未取证 | 必须 | 机型参数 |
+| 参数与图纸 | 切出风速 | 未取证 | 必须 | 机型参数 |
+| 参数与图纸 | 额定风速 | 未取证 | 必须 | 机型参数 |
+| 参数与图纸 | 生存温度 | 未取证 | 必须 | 机型参数 |
+| 参数与图纸 | 定值编号与名称 | 文本 | 建议 | 保护定值及控制参数 |
+| 参数与图纸 | 定值上下限与默认值 | 随定值 | 建议 | 保护定值及控制参数 |
+| 参数与图纸 | 温度保护定值 | ℃ | 建议 | 保护定值及控制参数 |
+| 参数与图纸 | 压力保护定值 | bar | 建议 | 保护定值及控制参数 |
+| 参数与图纸 | 速度保护定值 | r/min | 建议 | 保护定值及控制参数 |
+| 参数与图纸 | 偏航控制策略 | 文本 | 建议 | 保护定值及控制参数 |
+| 参数与图纸 | 降载控制策略 | 文本 | 建议 | 保护定值及控制参数 |
+| 参数与图纸 | 电气控制原理图 | 文本 | 必须 | 图纸、技术文件 |
+| 参数与图纸 | 端子与接线表 | 文本 | 必须 | 图纸、技术文件 |
+| 参数与图纸 | 主控逻辑图或控制方式文件 | 文本 | 必须 | 图纸、技术文件 |
+| 参数与图纸 | 液压站原理图 | 文本 | 必须 | 图纸、技术文件 |
+| 参数与图纸 | 保护定值表 | 文本 | 必须 | 图纸、技术文件 |
+| 参数与图纸 | 故障码位域释义 | 文本 | 必须 | 图纸、技术文件 |
+| 参数与图纸 | 行星轮内齿圈齿数 | 个 | 建议 | 设备参数 |
+| 参数与图纸 | 行星轮个数 | 个 | 建议 | 设备参数 |
+| 参数与图纸 | 太阳轮齿数 | 个 | 建议 | 设备参数 |
+| 参数与图纸 | 行星轮齿数 | 个 | 建议 | 设备参数 |
+| 参数与图纸 | 平行轮大齿轮齿数 | 个 | 建议 | 设备参数 |
+| 参数与图纸 | 平行轮小齿轮齿数 | 个 | 建议 | 设备参数 |
+| 参数与图纸 | 齿轮箱各级轴承型号 | 文本 | 建议 | 设备参数 |
+| 参数与图纸 | 发电机型号厂家 | 文本 | 建议 | 设备参数 |
+| 参数与图纸 | 发电机轴承型号 | 文本 | 建议 | 设备参数 |
+| 参数与图纸 | 叶片参数 | m、文本 | 建议 | 设备参数 |
+| 参数与图纸 | 变桨与偏航驱动齿数 | 个 | 建议 | 设备参数 |
+| 测风塔 | 温度 | ℃ | 必须 | 测风塔数据 |
+| 测风塔 | 湿度 | % | 必须 | 测风塔数据 |
+| 测风塔 | 气压 | kPa | 必须 | 测风塔数据 |
+| 测风塔 | 不同层高风速 | m/s | 必须 | 测风塔数据 |
+| 测风塔 | 不同层高风向 | ° | 必须 | 测风塔数据 |
+| 测风塔 | 层高标识 | m | 必须 | 测风塔数据 |
+| 测风塔 | 风速标准差 | 未取证 | 建议 | 测风塔数据 |
+
+## 5 数据质量与对齐要求
+
+本章的要求与具体场站无关,适用于任何形态的源件。
+
+### 5.1 时间戳与采样一致性
+
+1. **时间戳统一写法**:所有类别的时间戳统一为「年-月-日 时:分:秒」的本地时刻。台账类源件若采用「月/日/年 时:分/秒」一类写法,必须按同一约定解析,并与统一写法逐值核对后再入分析。
+2. **时区同一约定**:源件若带时区偏移,必须与不带偏移的时间戳统一为同一约定;**两侧必须同一约定**,否则同一列会跨日分层。
+3. **采样周期逐台核对**:10 分钟数据的采样周期为 600 秒,秒级数据的采样周期为 1 秒或 5 秒;周期必须逐台逐月核对,不得只抽一台代表全场。周期跳变与断录必须显式记录。
+4. **区间归属明确**:10 分钟数据的时间戳究竟表示区间起点还是区间终点,必须由现场明确并写成口径;秒级数据的时间戳为瞬时时刻。
+5. **秒级与 10 分钟可对齐**:秒级数据必须能与 10 分钟数据在同一时间基准下对齐;对齐方式(秒级聚合到 10 分钟,或 10 分钟展开到秒级)必须写明。
+
+### 5.2 单位与量纲
+
+1. **单位真源唯一**:单位只认两处真源——测点词典中的单位取值,以及机型字段契约中逐列登记的单位;本文第 3 章凡写「未取证」之处,即为该两处真源中都查不到依据的测点,必须由现场或厂家资料确认后才能入契约,**不得凭常识补**。
+2. **变体归一**:同一物理量在不同源件中可能出现多种写法(例如温度的两种写法、风速的两种写法、频率的两种写法),必须先归一为同一量纲再合并。
+3. **压力类必须逐条看**:压力类在词典中同时存在多种量纲,必须逐条比对词条,不能按「压力就是巴」推定。
+4. **同一测点同名同量纲**:同一个业务含义的测点,在不同数据类别中必须使用同一量纲;若源件量纲不同,必须在提交说明中写明换算关系。
+5. **派生量的单位随源量**:由源量相减或相除得到的派生量(例如三叶桨距角极差、滤芯压差、配对温差),其单位随源量,不得自造新单位。
+
+**表 5-1 常见单位变体与归一要求**
+
+| 物理量 | 源件可能出现的多种写法 | 归一要求 |
+|---|---|---|
+| 温度 | 摄氏度符号写法与字母写法两种 | 统一为摄氏度,两者同量纲 |
+| 风速 | 米每秒的两种写法 | 统一为米每秒,两者同量纲 |
+| 频率 | 赫兹与毫赫兹 | 按词条逐条看,两者不同量纲 |
+| 压力 | 巴、千帕、帕、百帕 | 必须逐条看词条,不得推定 |
+| 转速 | 转每分的两种写法 | 统一为转每分,两者同量纲 |
+
+### 5.3 缺测与哨兵值
+
+1. **哨兵值清单随件给出**:源件用什么数值表示无效、掉线或未接线,必须由现场给出清单与含义;未给出清单的源件按缺测处理,不得把哨兵值当作有效测量值。
+2. **界外值一律转缺失**:越出物理界或设计界的取值一律转为缺失,**不得**压到边界上(压边界会扭曲统计)。
+3. **零值的合法性逐列确认**:零值可能合法且具有诊断意义(例如停机态功率为零、失压事件电压接近零),是否把零当无效必须**逐列**与现场确认,禁止统一设为零值哨兵。
+4. **恒值列必须标注**:全场恒值或长时间恒值的测点(未接线、未标定、采集掉线)必须在提交说明中标注,并在分析前排除。
+5. **缺列如实记**:源件缺少本文要求的测点时,必须如实记录缺列清单,**不造零、不静默丢列**;缺列对应的分析面按不可判处理。
+6. **量化步长说明**:源件若按固定步长量化,必须逐列给出量化步长与有效位数;跨形态合并时按同一精度口径逐值比对,重叠区不一致的格数必须记入产物属性。
+
+### 5.4 命名一致性
+
+1. **测点映射表必须显式给出**:源件列名与其业务含义的对应关系必须以映射表提交,一份测点一个业务含义;**禁止靠位置或排序猜**。
+2. **按列名绑定、禁用位置索引**:同一场各台机组的列序可能互不相同,因此一切读取必须按列名绑定;按位置取数会得到「名字对不上数」的数据。
+3. **台号对照表必须显式给出**:同一台机组在不同源件中可能使用不同写法,台号与其内部编号的对应关系必须显式提交对照表。
+4. **代码表独立成件**:状态码、故障码、报警码各一份代码表,两列:代码、说明;同一代码在不同命名空间中的含义不得混判。
+5. **同源副本必须标注**:若两个测点实为同一条信号,必须在提交说明中标注,并**禁作第二独立证据**。
+
+### 5.5 同期对齐
+
+1. **统一时间基准**:跨源对齐一律以第 5.1 节的时间基准为准;台账类源件的事件时刻必须能与其他源件在同一基准下对齐。
+2. **时间窗重叠必须显式记录**:多源数据的时间窗重叠区必须写明起止,不得默认按上报时段推定重叠。
+3. **时间空洞如实记录**:源件中的时间空洞必须逐段记录(起止时刻与长度),并在产物中标注;不得插值填补后当作有效测量值。
+4. **残段如实出**:若某个时间窗内只有极少样本,产物仍应如实输出并标注样本量,不得用「最少样本」门把「数据有没有到」变成猜测。
+5. **台账逐行对齐**:台账类产物必须逐行对齐源件;源件变更后必须重算,否则页面与源件会对不上。
+
+### 5.6 源件只读与不可覆盖
+
+1. **源件只读**:现场提交的源件是只读输入,分析侧不得改写、不得覆盖、不得就地清洗。
+2. **增量判定按同名同尺寸**:目标不存在为新增;同名同尺寸视为同一份并跳过;同名不同尺寸为冲突,**必须人工确认**是现场更正还是另一批数据,默认拒绝覆盖。
+3. **覆盖不可逆**:任何覆盖都必须显式确认,并在覆盖后立即重算相关产物;覆盖前必须留底。
+4. **交付形态按「源类目录,再往下一层就是文件」**:多套一层包名目录等于没放;同一类数据的交付形态必须统一,不得混用多种层级。
+5. **口径可追溯**:每一份产物都必须能追到其源件、单位口径与时间窗口;顶替件与正本必须在产物中区分标注。
+
+## 6 必须性分级与优先级
+
+### 6.1 分档规则
+
+一项收资到底是必须、建议还是可选,只问五个与场无关的问题。判定顺序:**先问 R5**(有没有消费者),命中就直接定档为不催;否则依次问 R1、R2、R3、R4,**首个命中即定档**。
+
+**表 6-1 通用判定规则(五问定档)**
+
+| 规则 | 判定问题 | 命中则 | 分档 | 缺了会怎样 |
+|---|---|---|---|---|
+| R5 无消费者 | 本系统有没有摄入器与产物真的消费它? | 没有 | 不催(本文按可选登记) | 无影响;将来要做该专题时再新增摄入与判据 |
+| R1 主线阻断 | 缺了会不会让判级四轴、发电性能、可靠性、台账或合同指标对标中任一条主线面不可判,或让核对锚点对不上? | 会 | P0(必须) | 该主线面显示缺件或不可判,重算链对应步骤响亮报错或跳过,不造数 |
+| R2 精度与互证 | 缺了不阻断主线,但会不会让结论精度下降、跨源互证或「检出、处置、复测」闭环做不了? | 会 | P1(建议) | 结论仍在,但依据变少,跨源互证与闭环缺一环 |
+| R3 有自算替代 | 缺了能不能由本系统自算件或已提交的其他数据按已登记口径顶替? | 能 | P2(可选) | 用替代件并如实标注来源;现场正本提交后正本优先 |
+| R4 只影响外部对照 | 缺了是不是只让某个专题或场外对照(邻场报表、可研与微观选址、测风塔外部锚)缺参照? | 是 | P2(可选) | 只有对应专题或外部对照不可做 |
+
+分档与必须性的对应:P0 对应「必须」,P1 对应「建议」,P2 对应「可选」。
+
+### 6.2 21 类分档表
+
+**表 6-2 收资要求 21 类的规则化分档**
+
+| 序号 | 数据类别 | 命中规则 | 分档 | 分档说明 |
+|---|---|---|---|---|
+| 1 | 10 分钟 SCADA 数据 | R1 | P0(必须) | 逐台导出覆盖分析时间窗;判级参照时间窗与季节匹配时间窗各需一段;缺台即该台不可判 |
+| 2 | 秒级 SCADA 高频数据 | R3 | P2(可选) | 无 1 秒或 5 秒原生高频时,可用分钟级转发层顶替;本节测点表内的「必须」指提交秒级数据时的必需测点 |
+| 3 | 测风塔数据 | R1 | P0(必须) | 前期实地测风满一年,运行期一整年分钟级;缺则自由流与湍流度只能用代理量 |
+| 4 | 功率曲线 | R2 | P1(建议) | 标准空气密度曲线与合同担保曲线各一条;缺则只能做机群相对比较 |
+| 5 | 机型参数 | R1 | P0(必须) | 换机型即换表:切入切出风速、额定风速、额定功率、叶轮直径、轮毂高度、传动比、生存温度 |
+| 6 | 机位分布数据 | R2 | P1(建议) | 逐台坐标与海拔(含轮毂高度);坐标不可自算 |
+| 7 | 机组状态、事件、故障、告警数据 | R1 | P0(必须) | 状态码表、故障码表与详细记录按场重收;码表是厂家私有命名空间 |
+| 8 | 机舱 CMS 振动数据 | R1 | P0(必须) | 至少一个季度的秒级振动数据(非加密)与原始时域波形;缺则振动侧无源可算 |
+| 9 | 机组统计数据或运行数据 | R1 | P0(必须) | 年、月、日统计量可由运行数据重建,但前提是第 1 项已提交 |
+| 10 | 机组保护定值及控制参数 | R2 | P1(建议) | 定值表成册加偏航控制策略与降载控制策略;控制参数一致性件可先由数据反演顶替 |
+| 11 | 机组故障录波数据 | R5 | 不催(本文按可选登记) | 本系统不消费;将来要做秒级故障回溯时再新增摄入与判据 |
+| 12 | 可研报告及微观选址报告 | R4 | P2(可选) | 逐台设计基线与测风依据用作外部对照 |
+| 13 | 风电场简介 | R4 | P2(可选) | 场站元信息(规模、投产时间、场址特性)用于背景与署名 |
+| 14 | 风电场坐标、机位布置图 | R4 | P2(可选) | 布置图可由坐标件生成;第 6 项提交后本项只影响图示对照 |
+| 15 | 风机技术协议及风机合同 | R1 | P0(必须) | 担保曲线与保证率条款是合同指标对标的依据 |
+| 16 | 并网到现在的生产报表 | R2 | P1(建议) | 与运行数据侧损失账对拍;口径必须成文 |
+| 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,本文按可选登记)。
+
+本表是规则对 21 类的定档结果,与具体场站无关;任何场站按本节规则重新过一遍,即可得到该场自己的优先级。
+
+## 7 收资要求 v3 的 21 项逐条对照
+
+本章把收资要求的 21 项逐条对照到本文的要求:是否必须、支撑什么功能、最小可用量与要点。**本表不设状态列**。
+
+**表 7-1 收资要求 21 项逐条对照**
+
+| 序号 | 数据类型 | 是否必须 | 支撑功能 | 最小可用量 | 要点 |
+|---|---|---|---|---|---|
+| 1 | 10 分钟 SCADA 数据 | 必须 | 判级四轴、发电性能与功率曲线、损失电量、停机事件、热链与冷却、控制参数一致性 | 逐台一份、采样 600 秒、统计时长建议不少于 2 年、全场机组、全量测点 | 核心必选集为第 3.1 节各域清单;按列名绑定;时间戳口径与区间归属必须写明 |
+| 2 | 秒级 SCADA 高频数据 | 可选 | 瞬态与启停、限电响应、振动工况标注、故障前后回溯 | 采样 1 秒或 5 秒(二选一)、至少 3 个月连续、全场机组、全测点 | 本版新增重点;实际采样频率必须明确;必须写清与 10 分钟数据的分工及与故障录波、机舱振动数据的配合 |
+| 3 | 测风塔数据 | 必须 | 自由流风速与湍流度、风廓线、功率曲线与尾流的外部锚 | 前期实地测风满一年(5 分钟或 15 分钟);运行期一整年分钟级 | 不同层高的风速、风向与层高标识必须齐全;无风速标准差时须按 1 秒采样 |
+| 4 | 功率曲线 | 建议 | 发电性能偏差判别与合同指标对标 | 标准空气密度曲线与合同担保曲线各一条,覆盖切入至切出风速段 | 不得只给图片;担保曲线须附空气密度、湍流度与风切变 |
+| 5 | 机型参数 | 必须 | 振动分母、功率曲线基准、控制参数参照 | 一份机型级参数表 | 切入切出风速、额定风速、生存温度在本系统中尚无单位依据,须随件明确 |
+| 6 | 机位分布数据 | 建议 | 邻域与尾流先验、扇区分析、地理对照 | 逐台坐标与海拔(含轮毂高度)、机型;另加升压站 | 坐标不可自算;必须逐台给出,混排场不得只给一种机型 |
+| 7 | 机组状态、事件、故障、告警数据 | 必须 | 报警台账、停机账、限电绑定、首发链、工单与闭环验证 | 覆盖分析时间窗的码表与逐条记录 | 每条记录必须带激活时刻与代码列;不同命名空间的码不得混判 |
+| 8 | 机舱 CMS 振动数据 | 必须 | 振动索引与谱库、部件状态评估、在升与换件闭环、机群基线 | 至少一个季度、秒级、含原始时域波形、全场机组 | 测点须覆盖主轴、齿轮箱、发电机与叶轮转速;工况分箱键必须随件给出 |
+| 9 | 机组统计数据或运行数据 | 必须 | 发电账、可利用率、损失电量、偏航与停机统计 | 覆盖分析时间窗的年、月、日统计量 | 统计口径必须成文;利用小时数的单位口径须由现场明确 |
+| 10 | 机组保护定值及控制参数 | 建议 | 保护判据依据、控制参数一致性、偏航与降载策略 | 定值表成册,另加偏航控制策略与降载控制策略 | 每条定值须含上下限、默认值、当前值与单位;控制参数一致性件可先由数据反演顶替 |
+| 11 | 机组故障录波数据 | 可选 | 故障前后高分辨率快照的秒级回溯 | 每次故障后触发前后一段记录,采样秒级或更高 | 本系统当前不消费;若启用,触发时刻必须能与秒级数据对齐 |
+| 12 | 可研报告及微观选址报告 | 可选 | 逐台设计基线与测风依据的外部对照 | 全本 | 可提取的结论须同时给出数值表;逐台设计发电小时数的单位须明确 |
+| 13 | 风电场简介 | 可选 | 背景、署名与台账筛选 | 一份 | 场站名称与台账中的场名写法必须一致 |
+| 14 | 风电场坐标、机位布置图 | 可选 | 图示化的机位与电气布置对照 | 全场坐标件与布置图 | 坐标系统与图例必须写明;可编辑矢量图优先 |
+| 15 | 风机技术协议及风机合同 | 必须 | 合同指标对标 | 全本,含担保曲线附件与保证率条款 | 担保曲线的适用条件与考核、整改条款必须同册给出 |
+| 16 | 并网到现在的生产报表 | 建议 | 与运行数据侧损失账交叉核对 | 并网至今,日粒度或月粒度 | 发电量是否含厂用电与限电必须注明 |
+| 17 | 周围已建风电场生产报表 | 可选 | 尾流与场级损耗的外部对照 | 覆盖分析时间窗的统计报表 | 统计口径须与所对照的场站可比 |
+| 18 | 运维记录 | 建议 | 检修面与闭环验证,与油样、报警三源交叉 | 覆盖分析时间窗的维护台账与检修台账 | 油样化验结果须给出项目与数值;取样部件必须可归集 |
+| 19 | 大部件更换记录 | 建议 | 部件在升与换件闭环 | 逐次更换记录 | 时间、机组、部件、原因、新旧件标识必须齐全 |
+| 20 | 图纸、技术文件 | 必须 | 位域语义、控制逻辑与回路判据的最终依据 | 电气控制、液压、逻辑图与保护定值表 | 扫描件须另附关键数值表;状态字与故障标志字须有逐位释义 |
+| 21 | 设备参数 | 建议 | 振动阶次定位与寿命评估的分母 | 逐部件型号、厂家与齿数 | 齿数与个数须为整数;各级传动比与轴承型号须逐级给出 |
+
+## 8 面向现场的收资清单
+
+本章为可打印清单,供现场按优先级逐项准备。
+
+**表 8-1 面向现场的收资清单**
+
+| 优先级 | 数据类别 | 最小可用量 | 必需测点 |
+|---|---|---|---|
+| P0 | 10 分钟 SCADA 数据 | 逐台一份、采样 600 秒、统计时长建议不少于 2 年、全场机组 | 有功功率(均值与最大值)、有功功率给定、机舱风速、独立风速计风速、机舱风向、发电机转速(均值与最大值)、叶轮转速、三叶桨距角、环境温度、主轴承温度(两侧)、齿轮箱油温与各轴温度、发电机三相绕组温度、发电机两端轴承温度、液压油温、变流器冷却水温度、偏航压力(均、最大、最小)、偏航动作计数、偏航润滑泵通电时长、润滑柱塞通电时长、液压系统压力(均、最大、最小)、风速故障计数、机组主状态码、刹车状态位、限电标志、限电功率给定 |
+| P0 | 测风塔数据 | 前期实地测风满一年(5 分钟或 15 分钟);运行期一整年分钟级 | 温度、湿度、气压、不同层高风速、不同层高风向、层高标识;无风速标准差时须按 1 秒采样 |
+| P0 | 机型参数 | 一份机型级参数表 | 机型代号、传动拓扑、变桨形式、额定功率、叶轮直径、轮毂高度、齿轮箱传动比、顺桨角、机组台数、切入与切出风速、额定风速、生存温度 |
+| P0 | 机组状态、事件、故障、告警数据 | 覆盖分析时间窗的码表与逐条记录 | 激活时刻、复位时刻、事件类型、事件分组、实体标识、报警码、故障码、状态码、码表说明 |
+| P0 | 机舱 CMS 振动数据 | 至少一个季度、秒级、含原始时域波形、全场机组 | 主轴前后轴承、行星级、中间轴、高速轴两侧、发电机驱动端与非驱动端、系统监视、叶轮转速、原始时域波形、频谱数据、无量纲指标、频率分辨率、工况分箱键、测量类型与名称 |
+| P0 | 机组统计数据或运行数据 | 覆盖分析时间窗的年、月、日统计量 | 发电量、平均风速、可利用率、故障停机时长、限电损失电量 |
+| P0 | 风机技术协议及风机合同 | 全本 | 合同担保功率曲线、保证可用率、担保曲线适用条件 |
+| P0 | 图纸、技术文件 | 电气控制、液压、逻辑图与保护定值表 | 电气控制原理图、端子与接线表、主控逻辑图或控制方式文件、液压站原理图、保护定值表、故障码位域释义 |
+| P1 | 功率曲线 | 两条曲线,覆盖切入至切出风速段 | 标准空气密度功率曲线、合同担保功率曲线、空气密度、湍流度、风切变、保证可用率、曲线口径说明 |
+| P1 | 机位分布数据 | 逐台坐标与海拔(含轮毂高度)、机型;另加升压站 | 机组编号、经度、纬度、海拔高度、轮毂高度、机组机型、集电线路归属 |
+| P1 | 机组保护定值及控制参数 | 定值表成册,另加策略文档 | 定值编号与名称、上下限与默认值、定值单位、温度保护定值、压力保护定值、速度保护定值、偏航控制策略、降载控制策略 |
+| P1 | 并网到现在的生产报表 | 并网至今,日粒度或月粒度 | 风速、发电量、故障率、限电量、统计粒度、口径说明 |
+| P1 | 运维记录 | 覆盖分析时间窗的维护与检修台账 | 作业时刻、机组编号、部件、作业内容、作业类型、停机与恢复时刻、油样取样日期与部件、化验项目与结果 |
+| P1 | 大部件更换记录 | 逐次更换记录 | 更换时刻、机组编号、更换部件、更换原因、新旧件标识 |
+| P1 | 设备参数 | 逐部件型号、厂家与齿数 | 主轴轴承型号及厂家、齿轮箱型号厂家与各级传动参数、齿轮箱各级轴承型号、发电机型号厂家与轴承型号、叶片参数、变桨与偏航电机参数及其驱动齿数 |
+| P2 | 秒级 SCADA 高频数据 | 采样 1 秒或 5 秒(二选一)、至少 3 个月连续、全场机组、全测点 | 有功功率、有功功率给定、风速(机舱与独立风速计)、风向、发电机转速、主轴转速(有则列)、叶轮转速、三叶桨距角与三叶极差、桨距角给定、发电机三相绕组温度、发电机两端轴承温度、齿轮箱油温与各轴温度、主轴承两侧温度、机舱温度与环境温度、液压油温、液压系统压力、储能(蓄能器)压力、机舱方位角、偏航误差、偏航压力、机组运行状态位、机组主状态码、限电标志、限电功率给定、机舱振动三向、塔架摆动、传动链加速度(有则列) |
+| P2 | 可研报告及微观选址报告 | 全本 | 场址测风依据、逐台设计发电小时数、尾流折减、机型选型依据、场址环境条件、微观选址结论 |
+| P2 | 风电场简介 | 一份 | 场站名称、装机容量、机组台数、投产时间、场址与地形特性、并网方式 |
+| P2 | 风电场坐标、机位布置图 | 全场坐标件与布置图 | 全场坐标总图、机位布置图、升压站与集电线路图、坐标系统与基准、图纸比例与图例、可编辑矢量图 |
+| P2 | 周围已建风电场生产报表 | 覆盖分析时间窗的统计报表 | 风速、发电量、故障率、限电量、场站基础信息 |
+| P2 | 机组故障录波数据 | 每次故障后触发前后一段记录,采样秒级或更高 | 触发时刻、触发原因、采样频率、记录时长、通道清单 |
+
+## 附录 A 单位与符号表
+
+**表 A-1 单位与符号**
+
+| 符号 | 中文名称 | 量纲 | 典型测点 |
+|---|---|---|---|
+| ℃ | 摄氏度 | 温度 | 环境温度、机舱温度、齿轮箱油温、发电机绕组温度、液压油温 |
+| kW | 千瓦 | 功率 | 网侧有功功率、有功功率给定、可用功率参照 |
+| kWh | 千瓦时 | 电量 | 区间发电量、累计发电量、损失电量、机组自用电量 |
+| kVar | 千乏 | 无功功率 | 并网点无功功率 |
+| kVarh | 千乏时 | 无功电量 | 并网点累计无功电量 |
+| m/s | 米每秒 | 速度 | 机舱风速、独立风速计风速、各层高风速 |
+| r/min | 转每分 | 旋转速度 | 发电机转速、叶轮转速、主轴转速 |
+| ° | 度 | 平面角 | 桨距角、风向、偏航误差、扭缆角度、机位经度与纬度 |
+| °/s | 度每秒 | 角速度 | 桨距角变化速率 |
+| bar | 巴 | 压力 | 液压系统压力、偏航压力、桨叶油压、制动压力 |
+| kPa | 千帕 | 压力 | 大气压力;压力类另见巴、帕、百帕 |
+| V | 伏 | 电压 | 网侧相电压、定值表中的电压类定值 |
+| kV | 千伏 | 电压 | 以千伏标注的一次侧电压量,按现场件标注 |
+| A | 安 | 电流 | 网侧相电流、变桨电机电流 |
+| Hz | 赫兹 | 频率 | 电网频率、振动谱频率与分析带宽 |
+| s | 秒 | 时间 | 通电时长、动作时长、持续时长、累计运行时长 |
+| min | 分 | 时间 | 故障停机时长、维护停机时长 |
+| m | 米 | 长度 | 叶轮直径、轮毂高度、海拔高度、层高标识、叶片长度 |
+| kg/m³ | 千克每立方米 | 密度 | 空气密度 |
+| Nm | 牛米 | 力矩 | 变桨电机转矩 |
+| g | 重力加速度 | 振动加速度 | 机舱振动量 |
+| m/s² | 米每二次方秒 | 振动加速度 | 振动加速度谱与时域波形 |
+| mV/g | 毫伏每重力加速度 | 灵敏度 | 振动传感器灵敏度 |
+| % | 百分比 | 无量纲比 | 可利用率、湿度、限电给定百分比、塔架摆动 |
+| 台 | 台 | 计数 | 机组台数 |
+| 个 | 个 | 计数 | 齿数、行星轮个数 |
+| 次 | 次 | 计数 | 偏航动作计数、风速故障计数 |
+| 文本 | 文本 | 编码与枚举 | 状态码、故障码、说明类字段、图纸与文档类条目 |
+| 未取证 | 未取证 | 尚无依据 | 本文中查不到单位依据的测点,须由现场或厂家资料确认后再入契约 |
+
+## 附录 B 收资要求 v3 原文 21 项
+
+以下为现场《数据分析收资要求-v3》21 项的**逐字**原文(换行以「;」分隔呈现,不改字、不删项)。
+
+**表 B-1 收资要求 v3 原文**
+
+| 序号 | 数据类型 | 备注(逐字) |
+|---|---|---|
+| 1 | 10分钟SCADA数据 | 统计周期为2年的全量测点10分钟SCADA数据。 |
+| 2 | 秒级SCADA高频数据 | 采样频率为1秒或5秒;统计时间为至少3个月的全量测点秒级SCADA高频数据; |
+| 3 | 测风塔数据 | 采样频率为5分钟或15分钟;1.项目前期实地测风塔测风数据(与可研报告中时间一致,需测风至少满一年),测点至少包含风速、风向、温度、气压、湿度。2.运行周期内统计时间为一整年的分钟级测风塔数据;测点包括:温度、湿度、气压、以及不同层高的风速、风速标准差、风向;若分钟级数据不具备"风速标准差",则需按如下要求:采样频率为1秒;统计时间为一整年的分钟级测风塔数据;测点包括:温度、湿度、气压、以及不同层高的风速、风向; |
+| 4 | 功率曲线 | 标准空气密度功率曲线;合同担保功率曲线 |
+| 5 | 机型参数 | 机型整体参数,如切入切出风速、额定风速、额定功率、叶轮直径、轮毂高度、传动比、生存温度等。 |
+| 6 | 机位分布数据 | 机位坐标、机位海拔高度(含轮毂高度)、机组机型的机位分布信息。 |
+| 7 | 机组状态、事件、故障、告警数据 | 机组故障代码、状态代码表及详细说明,详细故障记录、事件记录、告警记录数据。 |
+| 8 | 机舱CMS振动数据 | CMS厂家出具且已确诊报告。统计时间为至少一个季度的秒级振动数据(非加密数据)及原始振动时域波形;测点包括:主轴、齿轮箱、发电机、叶轮转速 |
+| 9 | 机组统计数据或运行数据 | 年月日机组统计运行数据,包括发电量,可利用率,利用小时数、发电量、平均风速、可利用率,故障停机时间,维护停机时间,各类损失电量情况,偏航时间、偏航次数等;参见附录两个示例文件 |
+| 10 | 机组保护定值及控制参数 | 机组各项温度、压力、速度等保护定值,机组偏航控制策略、机组降载荷控制策略等控制参数 |
+| 11 | 机组故障录波数据 | 每次机组故障后,详细记录故障前后机组数据的文件 |
+| 12 | 可研报告及微观选址报告 | (原文无备注) |
+| 13 | 风电场简介 | (原文无备注) |
+| 14 | 风电场坐标、机位布置图 | (原文无备注) |
+| 15 | 风机技术协议及风机合同 | (原文无备注) |
+| 16 | 并网到现在的生产报表 | 包含风速、发电量、故障率、限电量等信息 |
+| 17 | 周围已建风电场生产报表 | 包含风速、发电量、故障率、限电量等信息 |
+| 18 | 运维记录 | 维护台账、检修台账 |
+| 19 | 大部件更换记录 | (原文无备注) |
+| 20 | 图纸、技术文件 | 风机电气控制相关图纸、液压站图纸、逻辑图或控制方式文件 |
+| 21 | 设备参数 | 主轴轴承型号及厂家、齿轮箱型号厂家、齿轮箱各级传动参数(行星轮内齿圈齿数、行星轮个数、太阳轮齿数、行星轮齿数、平行轮大齿轮齿数、平行轮小齿轮齿数)、齿轮箱各级轴承型号、发电机型号厂家、发电机轴承型号;叶片参数、变桨电机参数、变桨驱动小齿齿数、变桨驱动大齿齿数、偏航电机参数、偏航驱动小齿齿数、偏航驱动大齿齿数 |
+
+## 附录 C 编写依据
+
+本文只写要求,不描述任何场站的任何数据情况;其依据来源如下:
+
+1. 现场《数据分析收资要求-v3》所列 21 类收资项及其原文备注(逐字保留于附录 B)。
+2. 本系统的数据接入与判级设计口径,包括测点业务含义与度量单位的登记口径、判级与发电性能分析的输入口径、数据质量与对齐口径,以及收资定档规则。
+3. 本系统已登记的测点单位依据与机型字段契约中的逐列单位登记;凡无登记依据者,本文一律写「未取证」。
+

+ 1312 - 1312
docs/src/设计说明_观澜_2.10.1.md → docs/src/设计说明_观澜_2.10.2.md

@@ -1,1312 +1,1312 @@
-# 设计说明 · 观澜 v2 风电场智能分析系统 · 版本 2.10.1
-
-## 1 文档说明
-
-### 1.1 目的与范围
-
-本文是"观澜 v2(风电场智能分析系统)"(海上风电场智能分析离线系统)的设计说明(下称"本系统"),面向版本 2.10.1;内容以样本风电场(下称"本场")为依据,样本场实测日期为 2026-09-22,说明本系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
-
-本文覆盖十五个设计面:总体架构与分层、目录结构与路径真源、数据接入与重算链、判级与算法、时间窗口径、服务与前端、本体与知识层、本机模型接入、运维控制台与重算编排、安装与服务化与版本管理、质量保证、安全与离线边界、可移植性与资源占用、多场适用性与换场迁移、已知边界与未实现。
-
-本文不重复需求条目本身(那是《需求分析_观澜_2.10.1.docx》的职责),也不重复操作步骤的逐步手册(那是 docs/重算操作手册_v0.1.md 与《使用说明书》v0.2(随包 docs/)的职责);本文只回答"设计上为什么这样、落在哪个文件的哪一处、用什么机器守卫保证它不漂移"。
-
-### 1.2 读者与用法
-
-现场运维与检修人员可看第 3 章、第 7 章、第 8 章、第 11 章,了解自己能点到的页面背后读的是什么口径;场站管理人员与换场交付人员可看第 6 章、第 7 章、第 16 章、第 17 章,了解判级与可靠性指标的口径与边界,以及换场时要重新标定哪些参数;研发与交付人员应通读全文,重点是第 4 章、第 5 章、第 12 章、第 13 章;验收方可直接按第 13 章的质量门与附录 A 的编写依据逐条复核。
-
-### 1.3 与《需求分析_观澜_2.10.1.docx》的对应关系
-
-需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示;与《数据要求说明_观澜_2.10.1.docx》的数据侧口径去向见 16.6 节。
-
-| 本文章节 | 需求分析对应章 | 对应关系说明 |
-|---|---|---|
-| 2 设计目标与原则 | 2 设计目标与约束 | 需求给目标,本文给落地机制与机器守卫 |
-| 3 总体架构 | 4 系统架构需求 | 需求要分层与统一入口,本文给分层图与组件端口表 |
-| 4 目录结构与路径真源 | 6 非功能需求(可移植性) | 需求要换机可用,本文给路径唯一真源与助手表 |
-| 5 数据接入与重算链 | 3 数据与功能需求 | 需求列功能,本文给逐步重算链与退出码容忍 |
-| 6 判级与算法 | 3 数据与功能需求(判级部分) | 需求要"带证据的判级",本文给判据、阈值与产物 |
-| 7 时间窗口径 | 3 数据与功能需求(时间窗) | 需求要随所选时间窗变化,本文给词表、缓存与 pending 契约 |
-| 8 服务与前端 | 5 页面与信息架构 | 需求给页面清单,本文给路由族与链接契约 |
-| 9 本体与知识层 | 3 数据与功能需求(知识与问答) | 需求要机制链与决策台,本文给六步摄入与只读工具表 |
-| 10 本机模型接入 | 6 非功能需求(离线模型) | 需求要离线问答,本文给档位、校闸、升档与审计 |
-| 11 运维控制台与重算编排 | 5 页面与信息架构(数据重算) | 需求要一个按钮一件事,本文给动作、状态文件与冲突码 |
-| 12 安装服务化与版本管理 | 7 交付与安装需求 | 需求要可安装可卸载可升级,本文给步骤与三重守卫 |
-| 13 质量保证 | 8 验收门 | 需求给验收门,本文给审计器矩阵与退出码语义 |
-| 14 安全合规与离线边界 | 6 非功能需求(安全与离线) | 需求要无外网与脱敏,本文给边界与降级路径 |
-| 15 可移植性与资源占用 | 6 非功能需求(可移植) | 需求要零配置启动,本文给离线件与实测体积 |
-| 16 多场适用性与换场迁移 | 11 多场适用性与换场迁移 | 需求要按场配置化并可换场交付,本文给场抽象层、场无关与场相关分层、换场作业单与扩展点 |
-| 17 已知边界与未实现 | 8 验收门(遗留项) | 需求要求如实留白,本文逐条给影响与处置建议 |
-
-### 1.4 口径与依据
-
-本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 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);全文用简体中文与半角数字与单位。
-
-本文用到的取证命令与样本场实测(2026-09-22)的输出要点如表 1-2 所示,全部为秒级只读或自检命令(重算与构建类耗时命令未执行)。
-
-| 命令 | 用途 | 本次输出要点 |
-|---|---|---|
-| python scripts/rebuild_all.py --dry-run | 取重算链实际步骤 | 输出"重算全部 · 26 步";加 --skip-scada 为 23 步 |
-| python guanlan.py check | 装机自检(40 余行结论) | 结论"全绿, 可 serve";底稿落在 docs/src/_guanlan_check.txt |
-| python scripts/products_reverse_audit.py --check | 反向呼应审计 | 呼应成立 3,548 件、不成立 0 件、未归类 0 件、rc=0 |
-| python scripts/pages_audit.py --check | 页面归口审计 | 2 项一致、19 项已知缺口、0 项要处理、rc=0 |
-| python scripts/config_audit.py | 配置统一审计 | 171 件配置、5 个域、0 不一致、14 已知缺口、rc=0 |
-| python scripts/raw_scan.py --check | 输入数据指纹比对 | "与上次快照一致,没有新数据"、rc=0 |
-| python scripts/log_audit.py | 日志口径审计 | 0 不一致、87 条提示、rc=0 |
-| python scripts/chain_gap_check.py | 振动六层链完整性 | 四步脚本齐全且已落地、rc=0 |
-| python scripts/version_log.py --check | 版本记录一致性 | 版本记录与代码一致、rc=0 |
-| python scripts/service_ctl.py status | 服务化状态 | 本机"已注册=False 运行中=False",服务名为 guanlan |
-
-***
-
-## 2 设计目标与原则
-
-### 2.1 六条设计原则
-
-**离线单包**:整套系统在一台不接外网的机器上可安装、可运行、可重算、可出报告。程序与依赖随包(wheels/win_amd64 42 件、vendor/python 三平台便携运行时),模型为可选项(未装即降级并如实标注)。落地证据:install.ps1 第 3 步"装依赖(优先包内离线轮子 wheels\win_amd64)",包内无轮子才改为联网安装。
-
-**单一真源**:路径真源是 src/paths.py,版本真源是 src/version.py 的 VERSION,端口与路径真源是 configs/serve.json,页面归口真源是 configs/portal_pages.yaml,配置登记真源是 configs/registry.yaml。落地证据:src/version.py 注释写明"改版本只改这里",打包器 scripts/pack_dist.py 不再自带版本常量而改为读 VERSION。
-
-**产物自算**:页面与接口要用到的每一件数据,都应由包内源代码从 data/raw 算出来,而不是靠随包快照顶上。落地证据:重算链的 ④b 会生成振动时间窗索引与谱库、④c 生成变桨面、④d 生成振动在升与换件闭环、④e 生成三层基线、⑤b 由重算台账生成事实契约的 claim、⑦b 自算烘焙总览页;反向呼应审计当前 rc=0,3,548 件产物全部能指到生成端与输入。
-
-**口径先于结论**:判据写在代码里、模型只做转述;相对判据封顶"候选";不同观测面不因为数量多就当作多源印证;外部原因造成的结果不写成设备不可靠。落地证据:src/ontology/mcp_server.py 头部写明"全部工具只读(L1)——模型零写权;判级零生成——只转述对象里的 verdict 原文与审级";src/windscada/subsys/fusion.py 明确禁止按源数加权;src/windscada/perf/reliability.py 把运维操作、风况(外部)、电网/供电(外部)单列为外部类并写明"外部类不计设备可靠性"。
-
-**缺件如实**:缺数据、缺配置、缺模型时,页面与日志要明确写出缺哪一件、属于哪条产物线、怎么补,不允许静默降级或伪造内容。落地证据:工作台与 CMS 在无产物时返回 HTTP 200 的结构化缺件页(而非裸 404 或直接掐断连接);src/windscada/subsys/temp_nbm.py 在样本不足时判"不可判(样本不足/返服嫌疑)"而不是判正常。
-
-**可审计**:每一条结论都能回抓到源记录,每一件产物都能说明谁生成、谁消费、来源是原始件重算还是随包补齐。落地证据:outputs/场站/_provenance.json 逐件记来源(raw-derived 或 shipped),outputs/场站/_derived_manifest.json 由构建脚本自登记,模型调用逐条写 logs/audit/llm_audit.jsonl。
-
-### 2.2 原则与机器守卫的对应关系
-
-原则不靠人记,靠检查器与退出码守住。对应关系如表 2-1 所示。
-
-| 设计原则 | 落地机制 | 守卫或检查器 | 违反时的表现 |
-|---|---|---|---|
-| 离线单包 | 包内轮子与便携运行时、可选模型 | install.ps1 第 3 步、guanlan.py check 依赖项 | 依赖安装失败即 exit 3,不继续启动 |
-| 单一真源 | paths.py、version.py、serve.json、registry.yaml | config_audit.py R1 至 R8、version_log.py --check | 手拼配置路径或版本不一致即报错(rc=6) |
-| 产物自算 | 构建器 + 自登记清单 | products_reverse_audit.py --check | 有件无生成端或无输入即报 rc=5 |
-| 口径先于结论 | 判据在 taxonomy 与 perf 与 subsys 内 | 反向可逆性矩阵、llm 校闸(校闸未过不出文) | 引用不到契约条目时明写"未被契约背书" |
-| 缺件如实 | 结构化缺件页、缺件说明字段 | pages_audit.py、guanlan.py check | 缺件被登记为已知缺口而不是装作通过 |
-| 可审计 | _provenance.json、llm_audit.jsonl | products_reverse_audit.py、log_audit.py L1 至 L5 | 台账查不到来路即报"溯源缺失"(rc=6) |
-
-### 2.3 关键设计取舍
-
-**判断在代码、模型只转述**:把判级放进模型会让同一份数据在不同机器上给出不同结论,也会让结论无法回溯到证据。因此模型只在问答、解释、引用三个位置出现,且必须过接地闸:src/ontology/mcp_server.py 的 claim_check 工具要求"最终回答前必须调用;台号与数字须来自本会话工具事实,越界即拒"。
-
-**慢就慢在算,不慢在等**:按所选时间窗重算一次判级矩阵约 25 秒、七镜头约 9 至 44 秒每时间窗。若同步等待,HTTP 请求会挂几十秒(对外访问还要过网关)。因此统一为"后台算加进程内缓存,未命中先回上一份口径并明确标注 pending",而不是静默等待或静默给旧口径。
-
-**缺件不静默降级**:缺 handoff 正本时由观澜自算件顶上并在 meta.from 标明"观澜自算";缺振动时间窗索引时按缺件如实返回空表并写明数据边界;缺模型时页面提示"本机模型未启动"(HTTP 503),绝不改用别的来源。
-
-**运行期不从交付包补齐**:2026-09-17 的口径是"运行链只报账、不搬运"——缺件要么放原始件后重算,要么由研发补生成端。交付包默认不含产物,因此"旁边有个含产物的压缩包"不能成为隐式依赖。
-
-***
-
-## 3 总体架构
-
-### 3.1 分层结构
-
-系统分为输入层、摄入构建层、产物层、服务层、前端层,另有贯穿的本体与模型层和两套贯穿全链的守卫(单一真源与审计门),如图 2-1 所示。
-
-![图 2-1 观澜总体架构](figures/fig-des-01-总体架构.png)
-
-输入层是 data/raw/场站名称/ 下的七类约定目录(scada_10min、scada_1min、scada_mdb、故障报警、风机故障记录、油样报告、windcms,另有 m5_cms_tcm 与场站并列的共享技术资料目录),原始件只读、不随包分发。
-
-摄入构建层是 scripts/rebuild_all.py 串起来的重算链(默认 26 步),把原始件算成产物,并把每一步的日志、退出码、耗时写进日志目录与任务状态文件。
-
-产物层是 outputs/场站/ 下的九个产物仓(windscada、ontology、windcms、m5_cms_tcm、tcm_compatible_replay、sop、guanlan、pitch、paradigm_r1),另有 report 与 guanlan/cloud 作为报告交付与可上云面孔的落点。
-
-服务层是六个常驻进程加一个可选的本机模型服务:网关 28084、分析工作台 18033、振动诊断 18020、仿真与回放 18791、仿真合页 18792、三维工作台 64292,以及可选的 Ollama 11434。
-
-前端层是门户(release/portal.html)、工作台单页(/detail/v2)、CMS 页面、仿真页与三维页,全部通过网关的同一前缀与同一入口访问。
-
-### 3.2 组件清单
-
-组件、端口、入口与职责如表 3-1 所示;端口取自 configs/serve.json(第 4 至 10 行),入口脚本取自 guanlan.py 的组件启动表。
-
-| 组件 | 端口 | 入口 | 职责 |
-|---|---|---|---|
-| 门户网关 | 28084 | scripts/guanlan_gateway.py | 统一入口;前缀改写;data-abs 豁免;/ops 与 /release 只读暴露;门户静态件按 mtime 缓存 |
-| 综合详细分析工作台 | 18033 | scripts/windscada_serve.py | 判级矩阵、部件问题、该问题页、发电性能、可靠性、本体页、问答、报告导出 |
-| CMS 振动诊断 | 18020 | scripts/windcms.py serve | 六层振动模型逐台判读、报告与逐台页、谱图与标量 |
-| 仿真与回放 | 18791 | scripts/static_server.py | 静态服务 release 与 resources 下的仿真页 |
-| 仿真·四系统合页 | 18792 | release/sim_sys_server.py | 从资料包 zip 现读四系统合页与 5 页资料 |
-| 三维拆装工作台 | 64292 | scripts/static_server.py | 三维资产页(release/viewer) |
-| 本机模型(可选) | 11434 | Ollama 本体进程 | 只服务问答、解释与初筛;未装即降级 |
-
-三处部署形态说明:组件(分析、振动、仿真、三维)出厂只绑 127.0.0.1(configs/serve.json 的 host),只有门户网关使用 public_host(出厂 0.0.0.0);因此对外只有 28084 一个入口,且页面没有鉴权,配置注释明确提示"对外监听请同时限来源(防火墙白名单或反代认证),别直接挂公网"。本次实测监听状态与之一致:网关在所有网卡的 28084 上监听,而工作台 18033、振动 18020、仿真 18791、四系统合页 18792、三维 64292 与本机模型 11434 都只在 127.0.0.1 上监听。
-
-### 3.3 端口与进程拓扑
-
-端口与进程拓扑如图 3-2 所示。网关按前缀把请求转给内部组件,组件端口可被 configs/serve.json 的同名键覆盖(scripts/guanlan_gateway.py 的上游端口表 _DEFAULT_ROUTES 与 _upstream_ports())。
-
-![图 3-2 端口与进程拓扑(依据 configs/serve.json)](figures/fig-des-02-端口与进程.png)
-
-网关的前缀路由表(依据 scripts/guanlan_gateway.py 第 31 至 38 行)为:/detail 转 18033 默认路径 /v2;/cms 转 18020 默认路径 /?theme=light;/sim/sys 转 18792 默认路径 /0_四系统合页.html;/sim 转 18791 默认路径 /sc1_sim_demo.html;/viewer 转 64292 默认路径 /unit-workbench.html;/local-ai 转 11434 探活路径 /api/tags。另有 /healthz、/api/version、/ops、/release/ 与门户根路径由网关自己处理。
-
-运行态实测(run/pids.json,本机一次运行中)如表 3-2 所示。该文件是"当前跑着谁"的唯一凭据,服务停止时删除;它换一台机器就是无效引用,因此交付包默认排除 run 目录。
-
-| 组件键 | 端口 | 进程号(实测) | 说明 |
-|---|---|---|---|
-| gateway | 28084 | 16448 | 对外唯一入口 |
-| detail | 18033 | 35436 | 工作台 |
-| cms | 18020 | 56720 | 振动诊断 |
-| sim | 18791 | 21780 | 仿真与回放 |
-| sim_sys | 18792 | 25936 | 四系统合页 |
-| viewer | 64292 | 58488 | 三维工作台 |
-
-### 3.4 产物仓的生成端与消费端
-
-九个产物仓的生成端与消费端如表 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 | 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 | 变桨面判级、零位卡片 |
-| guanlan | 5 | 0.0 MB | scripts/guanlan_facts_contract.py | 门户结论段、/api/facts、报告摘要 |
-| sop | 1 | 0.0 MB | scripts/sop_findings_from_ledger.py | 事实契约输入 |
-| paradigm_r1 | 3 | 0.0 MB | scripts/sop_findings_from_ledger.py | 事实契约输入 |
-| tcm_compatible_replay | 2 | 0.6 MB | scripts/tcm_mask_thresholds_build.py | 报告构建器、掩码阈值 |
-
-***
-
-## 4 目录结构与路径真源
-
-### 4.1 路径唯一真源与助手表
-
-src/paths.py 是全部路径的唯一真源。硬约束有三条:代码与配置里只写相对路径(相对安装根),不写机器相关绝对路径;运行时由该模块解析成绝对路径,解析基准是安装根而不是当前工作目录;需要写进产物与清单的路径字符串用 POSIX 相对形式(rel()),只在给人看的显示场景用本机分隔符(disp() 与 disp_dir())。安装根由环境变量 WINDSCADA_ROOT 决定,未设置时按 src/paths.py 的位置回溯上一级,因此整个安装目录可以整体拷到别的电脑或别的盘而不改任何路径。
-
-路径助手的功能如表 4-1 所示。
-
-| 助手 | 返回 | 用途 |
-|---|---|---|
-| P.ROOT | 安装根 | 一切解析的基准(WINDSCADA_ROOT 可覆盖) |
-| 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 标准仓,页面主取数处 |
-| P.ont(name) 与 P.objects_json(name) | outputs/场站/ontology 与其 objects.json | 本体对象库 |
-| P.cms(name) | outputs/场站/windcms | CMS 振动诊断产物 |
-| P.m5(name) | outputs/场站/m5_cms_tcm | 振动线出件、时间窗索引、谱库 |
-| P.tcm_replay(name) | outputs/场站/tcm_compatible_replay | TCM 兼容链回放资产 |
-| P.sop(name) | outputs/场站/sop | SOP 中间件与评审落盘 |
-| P.guanlan(name) 与 P.cloud(name) | outputs/场站/guanlan 与其 cloud | 事实契约与可上云面孔 |
-| P.pitch(name) | outputs/场站/pitch | 变桨侧派生件 |
-| P.paradigm(name) | outputs/场站/paradigm_r1 | 范式实验件(E3/E5/E8 底稿) |
-| P.report_dir(name) | outputs/场站/report | 报告交付件 |
-| P.contract(name) | reference/场站/windscada_contract.yaml | 机型判据契约(属 reference 侧) |
-| P.config() 与 P.config_dir() 与 P.farm_config() | configs 下的配置路径 | 配置的唯一取用口 |
-| P.rel(p) 与 P.disp(p) 与 P.disp_dir(p) | 相对 POSIX 串与显示串 | 写进产物用 rel(),给人看用 disp() |
-| P.resolve(p) | 绝对路径 | 把可能是相对的值按安装根解析 |
-| P.venv_python() 与 P.python_exe() | 解释器路径 | 跨平台探测 .venv/Scripts/python.exe 或 .venv/bin/python |
-
-### 4.2 安装根逐目录说明
-
-安装根下的目录分工如表 4-2 所示,体积与件数为本次递归实测值。
-
-| 目录 | 件数(实测) | 体积(实测) | 放什么 |
-|---|---|---|---|
-| 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/<机型>技术资料),只读输入 |
-| outputs | 3,549 | 7,157.5 MB | 产物仓,只由构建器写 |
-| 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 | 交付文档与设计正本(含本文) |
-| logs | 155 | 3.3 MB | 长驻服务日志、运维动作日志、构建日志、审计流水、历史留档 |
-| run | 2 | 0.0 MB | 运行态:pids.json 与 ops_job.json |
-| .venv | 17,779 | 688.1 MB | 虚拟环境(换机必失效,安装时重建) |
-
-### 4.3 配置、日志、运行态与交付件的落点
-
-四类运行期文件的落点与命名是唯一口径,如表 4-3 所示。配置只允许经 src/paths.py 的 config() 与 config_dir() 取路径,不许手拼 configs 字符串(config_audit.py 的 R6 规则专门查这一条)。
-
-| 类别 | 落点 | 命名或字段 | 谁读 |
-|---|---|---|---|
-| 运行期单件配置 | configs/serve.json | host、public_host、gateway、detail、cms、sim、sim_sys、viewer、ollama、release_dir、viewer_dir、sim_dir、python、raw_watch、raw_dir | guanlan.py、网关、运维控制台、指纹、移植检查 |
-| 运行期单件配置 | configs/models.json | runtime、endpoint、tiers、profiles、pull_commands | guanlan.py check、src/ontology/llm_gate.py、fast_agent.py |
-| 运行期单件配置 | configs/portal_pages.yaml | 25 条主条目与 7 条子条目的 id 与 kind | scripts/pages_audit.py |
-| 配置登记表 | configs/registry.yaml | top_level、domains、known_missing | scripts/config_audit.py |
-| 长驻服务日志 | logs 组件名.log | gateway、detail、cms、sim、sim_sys、viewer、serve、start_hidden、service | 现场排障、log_audit.py |
-| 运维动作日志 | logs/ops/ | ops_动作_YYYYmmdd-HHMMSS.log,保留最近 20 份或 30 天 | 运维控制台页面、现场排障 |
-| 构建日志 | logs/build/场站/原相对路径 | 沿用原相对路径 | 构建排障 |
-| 审计流水 | logs/audit/ | llm_audit.jsonl、cloud_qa.jsonl、terms_audit.json | 审计与复核 |
-| 运行态 | run/ | pids.json(组件到进程号与端口)、ops_job.json(当前任务) | guanlan.py stop、运维控制台 |
-| 交付件 | release/ 与 outputs/场站/report 与 outputs/场站/guanlan/cloud | 门户、治理清单、报告交付件、可上云面孔 | 门户、网关 /release 只读暴露 |
-
-日志行格式统一为"时间戳 级别 组件 消息"(级别取值 DEBUG、INFO、WARN、ERROR),UTF-8 无 BOM、行尾 LF、无 ANSI 颜色码。六个服务不需要各自改写打印语句,入口处调用一次 src/logfile.py 的 prefix_stdout() 即可让每一行自动带前缀。
-
-### 4.4 已统一的路径问题(选列)
-
-2026-09-16 曾集中统一十处路径问题,选列五处如表 4-4 所示,用以说明"为什么必须只有一个真源"。
-
-| 位置 | 原样 | 改成 | 为什么要改 |
-|---|---|---|---|
-| 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 键 | 多场部署下跨场串数据且不报错 |
-| src/windcms/pipeline.py 的 ingest | 任何压缩包都调未随包的脚本 | 先判断包内容:含解码导出则按已解码路线走,否则报明确错误并给两条可行路径 | 把 CMS 导出包直接指过来时原本必然崩,而其中一条路径并不需要那个脚本 |
-
-***
-
-## 5 数据接入与重算链设计
-
-### 5.1 输入数据族与落位约定
-
-输入层是 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 | 平铺,名字是内部台号(样本场形如 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 MW 级海上机组;主机制造商(OEM,名称从略)与机型代号从略;与场站并列) | 318 | 2,800.7 MB | 技术资料与四个台账 | ⑦ 本体六步 |
-
-### 5.2 重算链逐步说明
-
-重算链由 scripts/rebuild_all.py 写死顺序、依赖与退出码容忍。默认口径(不含 --src、不含 --with-verify)实跑为 26 步;加 --skip-scada 为 23 步(少了 SCADA 侧的 ③、③b、④c 三步);加 --src 为 27 步;再加 --with-verify 为 28 步。重算链的步骤关系如图 5-1 所示,该图按 --skip-scada 口径绘制,因此图中的步骤数为 23 步。
-
-![图 5-1 重算链(依据 scripts/rebuild_all.py --dry-run)](figures/fig-des-03-重算链.png)
-
-逐步说明如表 5-2 所示(步号顺序即执行顺序;⑦ 的六个子步合并为一行列出)。
-
-| 步号 | 脚本 | 输入 | 输出 | 退出码容忍 |
-|---|---|---|---|---|
-| ① 放数据(仅 --src 时) | scripts/place_raw_data.py --src 包目录 --scope full | 现场包 | data/raw/场站/ | 0 |
-| ①b 输入数据扫描 | scripts/raw_scan.py --check --write | data/raw 逐族指纹 | outputs/场站/_raw_scan.json 快照 | 容忍 4 与 5(4 有新数据不是失败;5 首次无快照顺手记一份) |
-| ② 三门台账 | scripts/rebuild_from_raw.py | 故障报警、风机故障记录、油样报告 | alarms.parquet、workorders.parquet、oil_samples_index.parquet | 0 |
-| ③ SCADA 侧 10 个构建器 | scripts/rebuild_from_raw.py --scada | scada_10min 逐台约 14 GB | 温度、功率曲线、损失、控制、偏航、热链、停机事件等 parquet | 0,实测约 15 分钟 |
-| ③b SCADA 10min 窄仓 | scripts/scada_slim_build.py | 同上,一次全量 | windscada/slim10min/台号.parquet 与 _manifest.json | 0,实测一次扫约 2 分钟 |
-| ④ 月度派生件 | scripts/windscada_monthly_build.py | raw 与随包基线 | temp_monthly 等同族月表 | 容忍 5(找不到随包基线即跳过等价验收,不是通过) |
-| ④b 振动侧摄入与报告与标量 | scripts/vib_raw_build.py | windcms 解码导出 | 时间窗索引、谱库、CMS 报告与逐台页、融合 z | 0;现场无振动原始件时空跑退出 0 |
-| ④c 变桨面 | scripts/pitch_face_build.py | scada_10min 与 scada_1min | pitch/pitch_daily.parquet 与 pitch_zero_monthly.parquet | 容忍 4(缺 SCADA 原始件按缺件如实报) |
-| ④d 振动在升与换件闭环 | scripts/component_history_build.py | 振动时间窗索引 | m5_cms_tcm/component_history.json | 容忍 4 |
-| ④e 三层基线 | scripts/baseline_38_build.py | 振动时间窗索引 | m5_cms_tcm/baseline_38.json | 容忍 4 |
-| ⑤b 事实契约 claim 生成 | scripts/sop_findings_from_ledger.py | 重算台账 | sop/findings.json 与 paradigm_r1 三份底稿 | 0 |
-| ⑤c 事实契约构建与渲染 | scripts/guanlan_facts_contract.py build | findings 与底稿 | guanlan/facts_contract_v0.json 与 derived 四件 | 容忍 2(契约自检不过需人看) |
-| ⑤a 逐件来源台账 | scripts/products_restore_missing.py --refresh | 盘上产物与族表 | outputs/场站/_provenance.json | 0;只记账不搬件 |
-| ⑤ 反向呼应审计 | scripts/products_reverse_audit.py --check | 产物与生成端 | 审计清单(只报账) | 容忍 5 |
-| ⑥ 重启组件服务(停) | scripts/_ops_stop_keep_gateway.py | run/pids.json | 组件停、网关留 | 0 |
-| ⑥ 重启组件服务(起) | guanlan.py serve | configs/serve.json | run/pids.json 与六个服务 | 容忍 1(有模块 DOWN 属 degraded 正常) |
-| ⑦ 本体六步 | src.ontology.kb_ingest、populate、chain_ingest、trend_ingest、retrieval.build、maintenance.refresh_params | 上级产物与技术资料 | objects.json、检索索引、turbine_params.parquet | 0 |
-| ⑦b 全场状态总览页 | scripts/windscada_overview_build.py | 重算产物与 objects.json | windscada/index.html | 0,缺产物时响亮报错不产出半张页 |
-| ⑧ 本体审计 | src.ontology.audit | objects.json | 审计结论(期望 0 问题) | 0 |
-| ⑧b 重装门户 | scripts/portal_build.py | 契约产物 | release/portal.html | 容忍 1(缺契约产物时门户保留原样) |
-| ⑧c 页面归口审计 | scripts/pages_audit.py --check | 页面登记表与产物 | 陈旧检测结论 | 容忍 5 与 6 与 7 |
-| ⑧d 版本记录一致性 | scripts/version_log.py --check | 版本记录与 version.py 的 HISTORY | 一致性结论 | 容忍 6(属文档同步这一路,不打断整条链) |
-| ⑧ 台账等价验收(仅 --with-verify) | scripts/rebuild_from_raw.py --verify | 随包基线 | 差异分类 | 容忍 4 与 5(4 有需人工看的差异;5 没基线即没验收) |
-
-退出码容忍的语义汇总如表 5-3 所示。主循环的判定是"退出码为 0 或落在该步容忍集合内即视为通过",非容忍的非零退出立即中断整条链。
-
-| 退出码 | 语义 | 典型来源 |
-|---|---|---|
-| 0 | 通过 | 各步正常完成 |
-| 1 | 有模块未起(degraded 正常态)或门户缺契约产物 | ⑥ 重启服务、⑧b 重装门户 |
-| 2 | 契约自检不过(sha 或脱敏或枚举) | ⑤c |
-| 4 | 有新数据,或缺现场原始件而按缺件如实报 | ①b、④c、④d、④e、⑧台账等价验收 |
-| 5 | 随包件这一路缺失(无基线、有未归类、被引产物不在位) | ④、⑤、⑧c |
-| 6 | 溯源缺失,或版本记录与代码不一致 | ⑧c、⑧d |
-| 7 | 数据派生页面陈旧(内嵌快照指纹与当前产物不一致) | ⑧c |
-
-### 5.3 幂等与可重入
-
-链上各步都是普通命令行程序,幂等且可整条重跑:② 与 ③ 与 ④b 按"产物是当前原始件的函数"重建(源件删了行也随之消失并大声报出缺了哪件、少了多少行、涉及哪些月);③b 全量重写窄仓;⑤a 由盘上账目重算台账;⑦ 的六个子步幂等覆盖。重算可以在系统运行中做(门户的「数据重算」或运维控制台按钮),也可以在系统全停时做(先 guanlan.py stop,跑完再启动,页面直接读新产物)。
-
-为让"新数据必进重算"不靠人记,链上有两条设计:①b 步每次扫描 data/raw 的逐族指纹并把本次指纹记成新基线;rebuild_all.py --auto 会先扫一遍,发现新数据落在被 --skip-scada 或 --skip-vib 跳过的族里就自动取消跳过并打印原因(实测示例:scada_10min 新增 1 件时计划从 22 步变 24 步)。
-
-### 5.4 等价验收与基线
-
-④ 步与可选的 ⑧ 台账等价验收都以"随包基线"为标准答案,逐值比对随包件与自算件。基线目录是 outputs/场站/windscada/_pre_rebuild_20260911/,缺基线时 ④ 返回 5(跳过等价验收,不是通过);全新机器上交付包按口径不含产物,因此 ⑧ 在全新机器上必然返回 5,这与"重算失败"是两件事,链上如实标出退出码并写明"这不是通过"。要真做验收,用 scripts/set_baseline.py 把已核实的当前产物快照登记成基线,之后这一项才会真比对。
-
-### 5.5 raw_scan 指纹与增量
-
-逐族指纹的构成是:递归统计该族目录下全部文件(不限扩展名)的件数、体积、最新落盘时间,加上清单摘要与子目录清单;每件按"相对路径、字节数、微秒级修改时间"排序串联后取 sha1,默认不叠内容哈希(--deep 才对不大于 8 MiB 的件逐件加内容 sha1)。快照落在 outputs/场站/_raw_scan.json,退出码为 0 无变化、4 有新增或变化、5 还没有基线。指纹字段与含义如表 5-4 所示。
-
-| 字段 | 含义 |
-|---|---|
-| files、bytes、newest | 该族递归文件数、总字节、最新落盘时间 |
-| matched | 扩展名白名单命中的件数(差额单列为"信息,不计变化") |
-| dirs | 子目录清单(新建空目录也能被发现) |
-| unmatched、unmatched_n | 族表之外的件与件数(按"未归类、没有已知消费者"如实报出,不猜) |
-| consumed、consume_how | 被消费者读取的件数与消费者的真实取数口径 |
-| upstream、upstream_how | 上游归档件(要先转换才被消费)与其转换口径 |
-| gaps、gaps_n | 缺口级件(放了但没有任何消费者读) |
-| digest、top | 整族指纹与样例件 |
-
-"增量"在放置侧另有三清单口径(scripts/raw_data_check.py 与 scripts/place_raw_data.py):目标不存在为新增;目标存在且大小相同为"相同(跳过)"(源件是只读输入,同尺寸视为同一份);目标存在但大小不同为冲突,默认拒绝落位并列出,必须人确认后加 --force 才覆盖,体检器的退出码语义是 0 合规、5 结构或命名违例、6 增量冲突、7 缺源类或时间空洞、8 仅提示。
-
-### 5.6 slim10min 窄仓为什么存在
-
-按所选时间窗重算的真实卡点,是每换一个时间窗就要重读 15 GB 级的 10min CSV(实测约 2 分钟每面)。而各面真正需要的列只是公共子集。scripts/scada_slim_build.py 把这份公共列子集抽成逐台一份的窄仓,一次全量扫后,任何时间窗的重算都退化为"窄仓过滤加分组"的秒级操作。
-
-窄仓的实测口径是 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 类目库是上游归档
-
-现场会按"月份乘通道组"打包交付 SCADA 归档,例如某月目录下的通道组库,共 12 个类目(中文目录名与类目码对应关系是:风机数据对应 tur、温度数据对应 tmp、压力数据对应 prs、电网数据对应 grd、DigiIn 数据对应 din、DigiOut 数据对应 dot、计算数据对应 cnt、标志数据对应 flg、内部数据对应 int、统计数据对应 scd、标准数据对应 std、汇总数据对应 sum)。
-
-取数层不直接读类目库(它只认按月分片的主件与同台补充件),因此类目库是上游归档:扫描器按信息级列出、不计变化、不假装被摄入(这是 v2.8.2 的消缺点,修前 raw_scan --check 会永远返回 4)。要让它进产物,需按时间戳与机组标识把逐台类目表对齐合成同台 10min 补充件 scada_10min/台号-YYYYMM.csv(列与主件逐列同名同序、时间戳为 ISO 形式),之后 ③ 与 ④ 与 ④c 会自动纳入。实测 9 个逐台类目与主件的前缀块一一对应(tur 91、din 93、dot 69、cnt 69、tmp 116、prs 52、grd 50、flg 42、int 13,合计 595 通道,0 缺 0 多),精度与格式按 float32 加 ISO 对齐(实测重叠 19,555,865 格,两侧都有值却不相等的为 0 格)。
-
-SCADA 形态的接纳规则是"CSV 优先、该台没有 CSV 才回落 MDB",并把这次从哪种形态取的记在返回表的属性里供台账与页面说明来路,不静默换源。MDB 侧有两个硬限制:单表不超过 255 列,因此库内按 250 列拆表;2026-09-19 之前建的老库缺机组列,10min 老库还能用源件自带的机组列兜底,1min 老库救不了(源件没有机组标识列),需重跑 scripts/csv_to_mdb.py 重建。
-
-***
-
-## 6 判级与算法设计
-
-### 6.1 判级矩阵与四维证据
-
-判级矩阵覆盖九个系统(src/windscada/taxonomy.py 的 SYSTEMS):变桨、偏航、主轴承、齿轮箱、发电机、变流器、主控与传感网、叶片与叶根、塔架与基础。其中叶片与叶根、塔架与基础不参与 SCADA 侧判级(代码里列为无 SCADA 数据的两个系统),因此页面上常见的"七系统判级矩阵"指的是可由 SCADA 判级的七个系统;液压(蓄能)并入变桨,温度 NBM 是方法而不是系统,这两点写在代码注释里。
-
-判级词表有五态:报警、良好、不可判、优秀、无数据;取严合并的权重是报警最严、其次良好、再次不可判、优秀与无数据并列(src/windscada/taxonomy.py)。子系统面另用四键取严(报警、良好、优秀、不可判),偏航面先过滤不可判再取三键。七系统判级矩阵与四源融合数据流如图 6-1 所示。
-
-![图 6-1 判级矩阵与四源融合数据流](figures/fig-des-04-判级与融合.png)
-
-各系统的判级函数与证据维度如表 6-1 所示。四个子判定面各自有四个维度(四维证据),逐维度给出判词,再按取严序归到系统级。
-
-| 系统 | 判级函数 | 四维证据或依据 | 判词示例 |
-|---|---|---|---|
-| 变桨 | src/windscada/subsys/pitch.py 的 registry | 蓄能与压力调节、变桨轴承与轮毂润滑、执行与位置反馈、油路与密封 | 蓄能失效候选(定向查)、记基线观察 |
-| 偏航 | src/windscada/subsys/yaw.py 的 registry | 偏航轴承与润滑、驱动装置、液压制动、对风控制 | 报警、良好、优秀 |
-| 主轴承与齿轮箱与发电机 | 温度通道经 temp_nbm.registry,再按通道到系统的映射归口 | 温度 NBM 同工况比档与相对离群 | 温度候选(定向查)、传感器候选(A 类,非热)、记基线观察 |
-| 变流器 | 可靠性归因码到部件的映射与状态统计 | 停机段归因、故障码统计 | 报警、良好 |
-| 主控与传感网 | 报警台账与系统辅助件 | 多报警台、传感器偏差、通信错误 | 报警、良好 |
-| 叶片与叶根、塔架与基础 | src/windscada/subsys/structure.py 的 registry | 塔筒频率、塔筒振动、塔筒湿度、基础与法兰;叶片 A 与 B 与 C、叶根螺栓 | 报警、不可判 |
-
-温度判词到矩阵级的映射是:温度候选与传感器不可信(A 类)归为报警,记基线观察归为良好,不可判(返服嫌疑)归为不可判。融合面的矩阵级枚举是 ok、meta、bad、warn、note 五种,关闭类最先判,取严权重为 bad 最重、其次 warn、再次 note、ok 最轻。
-
-### 6.2 温度 NBM 与同油温档带宽
-
-温度 NBM 的口径是"同一功率档内比同族温度",功率档按 500 kW 一档划分(0 至 4500 kW)。样本门是同一单元样本数不少于 18;台级样本少于 500 行时判"不可判(样本不足或返服嫌疑)"。偏差与稳健 z 值为:偏差等于实测中位减去基准中位,稳健标准差取 1.4826 倍中位绝对偏差并从下限 0.3 起算,z 等于偏差除以该稳健标准差。判级阈值为:偏差不小于 15 K 判 A 类(传感器不可信,非热);偏差不小于 4.0 K 且 z 不小于 3.0 判温度候选;偏差不小于 2.5 K 且 z 不小于 2.0 判记基线观察。
-
-同油温档带宽的判据在蓄能(液压)面而不是温度 NBM 文件里:按油温 5 K 一档分箱(发电态功率大于 100 kW 的行),同一档内样本数不少于 24 时计算压力带宽(段内最大值减最小值);带宽比值不小于 1.5 时入轴,轴数不少于 2 时判"蓄能失效候选(定向查)"。热链面另按高油温档统计占空:档位不小于 45 摄氏度且样本数不少于 30 才出值。
-
-温度面还有两个故意固定的对照时间窗:ref 是 2025-07-01 至 2026-01-01(限电前干净时间窗),ref_sm 是同历月早一年;它们不随用户所选时间窗漂移,因为季节解耦的对照基准一旦跟着漂就失去对照意义,页面上如实标注这一点。
-
-### 6.3 七镜头曲线与限电前干净时间窗
-
-七镜头曲线的定义与口径在 src/windscada/perf/curves.py:L1 风速与功率、L2 功率与桨距(含三叶互比)、L3 功率与发电机转速、L4 转矩与转速(转矩等于功率除以发电机角速度,理论形态为转速平方族)、L5 风速与风轮转速(叶尖速比)、L6 功率与功率系数、L7 发电机转速与风轮转速之比。机型常数是风轮半径 65.0 米、扫风面积 13,273 平方米、铭牌齿比 119.752、贝兹极限 0.593。
-
-分箱边界为:风速 3.0 至 15.5 米每秒、步长 0.5;功率 0 至 4250 kW、步长 250;转速比 600 至 1760、步长 40。显著门共八项按绝对值锚定;离群判据是稳健 z 绝对值不小于 3 且残差不小于对应门限;工况段闸是功率分箱不小于 250 kW、转速比分箱不小于 900。另有三条物理硬闸:功率系数不得超过贝兹极限、齿比恒等(偏差超过 1.5 判离群)、叶尖速比须呈平台。列活性铁律要求同一列取值数大于 1、卡死比例小于 0.5 且落在物理域内,否则该列不参与镜头。
-
-限电前干净时间窗是 2025-07-01 至 2026-01-01(2025-H2),代码里把它写成常量并注明这是"限电前干净判别时间窗",与七镜头曲线的判别时间窗一致。选到 2025-07 至 2025-12 这一段时,发电性能页直接使用正式产物而不重算。
-
-### 6.4 M9 控制参数一致性
-
-M9 是控制参数一致性检查,摆在发电性能页上,口径在 src/windscada/perf/control.py。四个判据如表 6-2 所示。P 封顶取有功功率上限通道的 99.5 百分位,ω 封顶取发电机转速上限通道的 99.5 百分位,只取有功功率均值大于 100 kW 的行参与统计。
-
-| 判据 | 输入列 | 方法 | 阈值 |
-|---|---|---|---|
-| C1 双封顶 | grd_wtc_ActPower_max、tur_wtc_GenRpm_max | 按容差分组(容差 25 kW 与 8 rpm),组内计数不少于 2,逐台与组中位比 | 偏差超过 3 倍容差即离群,判"版本或参数差异·准定论载体" |
-| C2 K 聚类 | 有功功率均值与发电机转速均值 | 中载区 1200 至 2800 kW 内计算 K 等于功率除以转速立方,取中位乘 100 万 | 离群门为 5 倍 1.4826 倍中位绝对偏差,稳健标准差下限为百万分之一 |
-| C3 桨距调度 | 功率分箱与桨距角 | 同功率档内比桨距角 | 同档差门 0.5 度 |
-| C4 尺子 | 满发功率 99.5 百分位、转速封顶、额定桨角 | 满发段取功率大于 3800 kW 且样本不少于 50 | 出 pitch_rated,供交叉核对 |
-
-M9 按所选时间窗重算(control.registry 接受 span 参数),产物是 control_profile.parquet(逐台的 P 封顶、ω 封顶、样本数、K 中位、额定桨角)与 control_schedule.parquet(逐台逐功率档的桨距)。实测(38 台):P 封顶中位从干净时间窗的 4190 kW 变为 2026 年的 4181.7 kW 与 2026 年一季度的 4177.5 kW。
-
-### 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 判为远程停机(业主单位(从略)与主机制造商(OEM,名称从略)),不计入设备责任。
-
-三项指标的口径如表 6-3 所示。外部类(运维操作、风况(外部)、电网或供电(外部))单列统计并明确"外部类不计设备可靠性"。
-
-| 指标 | 公式或口径 | 注意点 |
-|---|---|---|
-| 平均无故障间隔(MTBF) | 台时减去停机小时再除以事件数;无记录停机不给值 | 部件可靠性模块的口径,台时目前用理想日历 |
-| 平均停机间隔(MTBO) | 在上一项基础上再扣除调度令停机后除以事件数 | 停机归因模块的口径,台时已改为实测台时 |
-| 单次停机时长(MDT) | 停机段时长的均值;就地与切入边界类码按口径剔除 | 两个模块同口径 |
-
-台时口径的实测对比很重要:2026 年 1 至 7 月理想日历台时为 194,050,实测台时为 165,475(差 17%),原因是尾月只有 6 天数据;若沿用理想日历,平均停机间隔会虚高 18%(239 小时实为 203 小时)。因此 faults.py 已改为用实测台时(由 loss_monthly 的行数除以每小时 6 个节拍得到),并在不可得时才回退理想日历。需要如实指出:reliability.py 的部件可靠性表仍使用理想日历台时,两处口径目前不一致,属待统一项(见第 17 章)。
-
-### 6.6 融合面四源与逐台判级
-
-融合面的四源是振动、温度、润滑、油液,口径写在 src/windscada/subsys/fusion.py 的模块头。融合面读取的输入件包括:handoff_vibration_v2.json、oil_samples_index.parquet、watch_channels_monthly.parquet、由 temp_nbm.registry 出的温度比档结果、mblub_monthly.parquet、alarms.parquet、workorders.parquet、油样合并报告 json,以及 CMS 报告转录件。
-
-融合纪律有两条:一是禁止按源数加权(同一机制的多个观测面不等于多源印证,源多不代表结论更强);二是 claim 时间窗对齐(振动数据到 2026-08、SCADA 数据到 2026-07-07、油样到 2025-08),避免拿不同时点的证据互相印证。逐台判级的产物字段包含台号、部件、振动结论、语义类、级别、设备状态、证据状态、振动信号、温度通道、温度偏差、温度判词、时间窗区间、末次油样、油样新鲜度、claim 时间窗等。
-
-四源的权重不是数值表而是优先级:关闭类最先判(销案、正样本、无新证、可降、降观察、无异常先归为正常),其余按取严合并;振动侧判 bad 或 warn 且非 meta 时,对应系统升级为报警;windcms 报告三方对拍也取严(危险与报警抬级,不可判且本层无异常证据则判不可判,良好与优秀不动)。系统关键词映射表把部件名归到系统,未收录的单元写"未收录"而不是写正常。
-
-### 6.7 残月识别
-
-"产物是当前原始件的函数"这条原则要求尾月样本很少时也如实出值,而不是加最小样本门去猜。同时,样本严重不足的月份必须能被识别出来,否则环比与均值会被拉偏。残月识别的实现在服务层:覆盖度等于该月行数合计除以每小时 6 个节拍再除以机组台数再除以当月日历小时;日条数等于该月条数除以覆盖度与当月天数的乘积,仅当覆盖度大于 0.02 才出值;覆盖度小于 0.5 的月份列为残月。
-
-实测的典型残月是 2026-07:只有 6 天数据,覆盖度 19.4%。页面上残月以灰虚线柱表示,并写"仅 N 天"(N 等于覆盖度乘 30 后取整);报告导出侧对覆盖度小于 0.5 的月份写"(不满月)",并声明环比不纳入残月。融合面与温度面也在各自口径里写明"6 天时间窗"的样本事实(例如温度面记"应有 288 个节拍、实测 145 个")。
-
-### 6.8 算法到输入列到判据到产物
-
-各算法与输入列、判据、产物的对应关系如表 6-4 所示,用以支撑"每条判据都能指到输入列"这一要求。
-
-| 算法或模块 | 关键输入列 | 判据要点 | 产物 |
-|---|---|---|---|
-| 温度比档(subsys/temp_nbm.py) | 各温度通道的均值列(按 500 kW 功率档分箱) | 偏差与稳健 z 双门限;样本门 18 | windscada/temp_bins.parquet,注册表出判词与依据 |
-| 蓄能带宽(subsys/hydraulic.py) | 液压油温均值、液压压力最大最小均值 | 5 K 油温档内带宽比不小于 1.5 且轴数不少于 2 | windscada/hydraulic_accum.parquet |
-| 变桨面(subsys/pitch.py) | 开关量 on 秒、柱塞动作次数、压力、三叶桨距角与有功与转速 | 停机顺桨段与满发段与运行段同工况分档的零位偏差 | pitch/pitch_daily.parquet、pitch/pitch_zero_monthly.parquet |
-| 偏航面(subsys/yaw.py) | 偏航压力均值、偏航润滑与柱塞时间、偏航计数、风偏差 | 四维取严;偏差清洗与相关位置 | windscada/yaw_daily.parquet、yaw_err_clean.parquet |
-| 七镜头(perf/curves.py) | 有功功率均值、限电命令、风速、发电机转速、风轮转速、三叶桨距角、环境温度 | 八项显著门;离群 z 与残差双门;三条物理硬闸 | windscada/curve_lenses.parquet、curve_liveness.parquet |
-| M9(perf/control.py) | 有功功率上限、发电机转速上限、有功功率均值、转速均值 | C1 至 C4 四判据 | windscada/control_profile.parquet、control_schedule.parquet |
-| 停机与归因(perf/faults.py) | 停机事件、报警台账、调度令码 | 段不少于 1 小时;T1 与 T2 两级归因 | windscada/stop_events.parquet |
-| 损失与可用率(perf/availability.py) | 风速、有功功率、功率曲线分箱 | 理论功率插值后算损失;风速低于 3.0 米每秒记 0 | windscada/loss_monthly.parquet |
-| 融合面(subsys/fusion.py) | 振动 handoff、温度比档、润滑月度、油样索引、报警与工单 | 关闭类最先判加取严;禁按源数加权 | 内存逐台表(融合面链盘)与 handoff 出件 |
-| 系统矩阵(taxonomy.py) | 各面上级产物与 windcms 报告转录 | 取严合并;未收录不判正常 | system_aux.parquet 与内存矩阵 |
-
-***
-
-## 7 时间窗口径设计
-
-### 7.1 时间窗词表
-
-2026-09-21 的用户令要求"部件问题与发电性能随所选时间窗变化",并新增自定义起止日期(含两端)。实现的词表如表 7-1 所示(scripts/windscada_serve.py 的 WINDOWS 与 months_of 与 win_range)。
-
-| 时间窗写法 | 含义 | 对月度件的作用 | 对日粒度件的作用(含两端) |
-|---|---|---|---|
-| 近30日 | 数据末端 1 个月 | 该 1 个月 | 该段月首至月末 |
-| 近90日 | 数据末端 3 个月 | 该 3 个月 | 该段月首至月末 |
-| 2026年 | 预设段 | 2026 年 1 月起至数据末端 | 段首月首至段末月末 |
-| 2026H1 | 预设段 | 2026-01 至 2026-06 | 2026-01-01 至 2026-06-30 |
-| 2025H2 | 预设段 | 2025-07 至 2025-12 | 2025-07-01 至 2025-12-31 |
-| 全程 | 全部月份 | 全部 | 0001-01-01 至 9999-12-31 |
-| YYYY-MM | 单月 | 该月 | 该月 1 日至月末 |
-| YYYY-MM~YYYY-MM | 月区间 | 区间内各月 | 起月 1 日至止月月末 |
-| YYYY-MM-DD~YYYY-MM-DD | 自定义起止(含两端) | 与之相交的月 | 原样,含两端 |
-
-含两端的实现方式是:win_range 原样返回日期串并按起不大于止排序,日粒度过滤在 in_win 里用"上界加 1 天再减 1 秒"来含住止日当天。月度件按所跨月份取整,日粒度件按日精确,页面上写明这句,避免读者以为月度均值能切到天。默认时间窗是 2026年(服务端取参数默认值与前端一致),工作台 v2 顶栏提供两个日期输入框与「应用」按钮,校验起不大于止,非法输入拒绝并提示。
-
-### 7.2 哪些随所选时间窗、哪些不随
-
-随与不随的划分如表 7-2 所示。需要强调的是:不随时间窗变化的内容不是"忘了改",而是各有自己的数据时点或对照意义。
-
-| 内容 | 是否随时间窗 | 机制 |
-|---|---|---|
-| 报障统计、五态控制策略、温度月轨迹、停机台账 | 随 | 月度件按 months_of 过滤 |
-| 判级四轴(变桨、偏航、蓄能、温度) | 随 | taxonomy.system_matrix 接受 span,各面 registry 接受 span;变桨用日粒度件精确切片,偏航与蓄能与温度走窄仓重算 |
-| 部件可靠性表(平均无故障间隔(MTBF)、单次停机时长(MDT)、停机时长) | 随 | reliability.overview 接受 span;停机事件与报警都是日粒度 |
-| 发电性能的 7 张月度时序图 | 随 | 按 months_of 过滤(月度件按月取整) |
-| 发电性能的七镜头曲线 | 随 | /api/curves 带时间窗参数,按时间窗重算分箱;选到 2025-07 至 2025-12 时直接用正式产物 |
-| M9 控制参数一致性 | 随 | control.registry 接受 span,走窄仓 |
-| 温度面的 ref 与 ref_sm 对照时间窗 | 不随(故意固定) | 季节解耦的对照基准,跟着漂就失去对照意义 |
-| 振动判级与厂家报告转录与工单台账 | 不随 | 各有自己的数据时点;页面上如实标注"不随本时间窗" |
-
-### 7.3 按时间窗缓存与 pending 契约
-
-按所选时间窗重算一律走"后台算加进程内缓存":缓存分三类(判级矩阵、曲线、M9),键由起止日期组成;命中即回,未命中则起后台线程并把"正在算"的状态回给页面,页面轮询再取。三类缓存的状态变量是 sysmx_pending、m9_pending 与 win_pending(win_pending 等于前两者之一为真),曲线另用 building 标记。缓存是进程内常驻字典,没有过期时间也没有容量上限;另有产物指纹缓存,过期时间为 2 秒,用于产物更新后自动重载。
-
-同一时间窗只算一次靠"在算集合"去重;算失败会记名并保留错误摘要,供页面与日志查看。为避免"预热线程与请求线程同时算两份中间帧"把内存吃光(远端实测问题),重活加了一把全局串行锁(_HEAVY_LOCK),并在拿锁时打印可用内存。服务启动末尾会起一个名为 win-warm 的守护线程做后台预热:先算六个预设时间窗的判级矩阵(每时间窗等待上限 180 秒),再算默认时间窗的七镜头(等待上限 240 秒);M9 不预热。预热可用环境变量关闭。
-
-pending 契约的关键是"绝不静默把旧口径当新时间窗":在算期间页面在脸上写明"下面这几张卡还是上一份口径",并在轮询到缓存就位后整段替换。时间窗与 pending 契约的时序如图 7-1 所示。
-
-![图 7-1 时间窗按窗重算与 pending 契约](figures/fig-des-05-时间窗与pending.png)
-
-前后端的等待参数如表 7-3 所示。
-
-| 环节 | 参数 | 实测或口径值 |
-|---|---|---|
-| 判级矩阵与曲线按时间窗重算 | 预估耗时 | 判级约 25 秒每时间窗;七镜头约 9 至 44 秒每时间窗 |
-| M9 控制参数按时间窗重算 | 预估耗时 | 服务注释约 5 秒每时间窗,版本记录约 0.6 秒每时间窗(两处不一致,待统一) |
-| 前端判级 pending 轮询 | 间隔与次数 | 每 5 秒一次,至多 40 次 |
-| 前端曲线 building 轮询 | 间隔与次数 | 每 6 秒一次,至多 40 次 |
-| 前端问答轮询 | 间隔 | 每 2 秒一次 |
-| 后台预热 | 覆盖范围 | 六个预设时间窗的判级加默认时间窗的镜头(不含 M9) |
-| 运维控制台 | 轮询间隔 | 每 2 秒一次 |
-
-### 7.4 时间窗口径下的实测对比
-
-按所选时间窗重算生效后,逐系统报警机组数会随时间窗变化,这是"判级真的按所选时间窗重算"的直接证据。本机实测(38 台):变桨报警为 15 与 13 与 10 台,偏航报警为 15 与 11 与 5 台,齿轮箱报警为 7 与 4 与 4 台,分别对应 2026年、2025H2、2026-07 三个时间窗;曲线图注随所选时间窗走且样本量随时间窗变化。远端复核(2026-09-21):变桨报警从 15 台变 7 台、偏航从 15 台变 3 台,曲线图注写"时间窗 2026-07-01 至 2026-07-31(随所选时间窗)"。
-
-***
-
-## 8 服务与前端设计
-
-### 8.1 HTTP 服务与路由族
-
-工作台服务用 Python 标准库的线程化 HTTP 服务实现(scripts/windscada_serve.py 引入 ThreadingHTTPServer 与 BaseHTTPRequestHandler),首请求把产物读进内存,之后每次取数前比对产物指纹(相关文件的路径、微秒级修改时间与大小的摘要,过期时间 2 秒),指纹变了自动重载,因此产物更新后不需要重启服务。路由族如表 8-1 所示。
-
-| 路由族 | 代表路由 | 说明 |
-|---|---|---|
-| 页面族 | / 与 /v2 与 /v2/snapshot 与 /turbine/台号 与 /problem/台号/系统 | 经典总览、v2 单页、快照下载、逐台页、单问题页 |
-| 取数族 | /api/fleet 与 /api/curves 与 /api/problem 与 /api/turbine 与 /api/vibcms 与 /api/maint_survey 与 /api/maint_std 与 /api/maint_framework 与 /api/dq_findings | 判级矩阵与链盘、七镜头、单问题、逐台、振动融合、检修四链与数据质量 |
-| 本体族 | /api/ontology 与 /api/ont_list 与 /api/ont_obj 与 /api/ont_chain 与 /api/channels | 本体页与机制链;v2 实际只懒取 /api/ont_chain |
-| 模型与报告族 | POST /api/ask 与 /api/ask_status 与 /api/ask_models 与 /api/rpt_compose 与 /api/rpt_export | 问答、状态轮询、档位列表、汇报纸组装与导出 |
-| 快照与事实与运维族 | /api/snapshot 与 /api/facts 与 /api/facts/claim 与 /api/reload | 快照、事实契约与单条 claim、显式清缓存并回显新旧指纹 |
-| 静态件族 | /static/* 与 /turbines/* 与 /windcms/* | 总览页与逐台静态件、振动页面件(限目录只读) |
-| 兜底 | 其余路径 | 缺件走结构化缺件页(HTTP 200),其它异常返回 500 页并记日志 |
-
-### 8.2 网关前缀改写与 data-abs 豁免
-
-网关把组件页里的根相对链接按前缀改写,否则组件页里的 / 会指向网关根而不是组件根。改写规则如表 8-2 所示(scripts/guanlan_gateway.py 的 rewrite())。
-
-| 改写对象 | 规则 | 例外 |
-|---|---|---|
-| 属性类 | href、src、action、content 后紧跟单个斜杠的根相对写法加前缀 | 双斜杠协议相对与 data: 不动 |
-| 样式类 | url() 内的单个斜杠根相对写法加前缀 | 同上 |
-| 脚本调用类 | fetch、open、EventSource、WebSocket、assign、replace 后字符串字面量里的根相对写法加前缀 | 同上 |
-| 赋值类 | 裸 location 赋值的根相对写法加前缀 | location.href 已被属性类规则覆盖 |
-| 字符串常量类 | 字符串里出现的 /api/ 加前缀(避免拼接出的接口地址漏改) | 同上 |
-| 绝对地址类 | 指向本机旧端口与新端口的绝对地址归到对应前缀 | 旧端口映射表保留在代码里 |
-| data-abs 豁免 | 标签上标 data-abs="1" 的 href 先换成哨兵、改写完再摘掉,即这条链接不被前缀改写 | 两种书写顺序(href 在前或 data-abs 在前)都认 |
-
-data-abs 豁免的实际用途是"组件页要指回网关根":工作台页面里的「返回观澜门户」用根相对写法并在标签上标 data-abs="1",否则它会被改写成 /detail/ 而永远回不到门户。此外网关对 /ops 系列的响应加 no-store 缓存头(否则页面脚本更新后浏览器仍用旧版,表现为"改了没反应"),门户静态件按修改时间与大小缓存,/release/ 提供只读暴露且不合并主库。
-
-### 8.3 页面烘焙与静态件
-
-除实时取数外,系统还有一条"烘焙"路线:把接口响应烤进单个 HTML 文件,使其离线也能打开。烘焙器是 src/windscada/ui/snapshot.py 的 bake(),注入 snap 标记、六个预设时间窗与语言对等文件指针,并放行问答与报告导出类接口走网络。总览「全场状态」页就是这条路线:重算链的 ⑦b 步用组件同一套取数函数生成 outputs/场站/windscada/index.html,工作台 v2 的 iframe 指向它。
-
-静态件落点是:/static/* 从 L0 标准仓读,/turbines/* 从同一仓读并注入浅色主题,/windcms/* 只读振动产物目录并限制在本目录内。三者缺件时都返回结构化缺件页(HTTP 200),写明缺哪一件、属于哪条产物线、怎么补,并给出运维控制台与自检入口。
-
-### 8.4 前端单页、轮询、中英切换与链接契约
-
-工作台 v2 是单页应用(src/windscada/ui/app.js,133 KB,另有 charts.js 30.8 KB 与 app.css 24.2 KB)。首屏只取 /api/fleet(判级矩阵与链盘),其余标签按需懒取;待算状态按第 7 章的间隔轮询。中英切换用查询参数 ?lang=en(语言来源是页面的 lang 属性,服务端据此投影权威英文名),localStorage 中只有一个键 guanlan.portal,用于记住门户来路;静态快照模式下查询参数不生效,改用对等语言的真实文件指针。前端契约如表 8-3 所示。
-
-| 契约 | 写法 | 说明 |
-|---|---|---|
-| 标签切换 | #tab=标签名 | 十个工作台标签;切换触发 hashchange |
-| 系统下钻 | #tab=component 与 sys=系统 slug | 从系统格下钻到该系统的问题清单 |
-| 逐台页 | /turbine/台号 | 独立页,含返回门户链接(标 data-abs) |
-| 单问题页 | /problem/台号/系统 | 系统标识经 sys_norm 双向归一(中文名、前端 slug、大小写都认) |
-| 回经典页 | / 加 lang 与 win 参数 | 从 v2 回到经典总览并保留语言与时间窗 |
-| 系统标识认不出时 | 页面写"系统标识无法识别" | 绝不写"该系统在判级时间窗内无非常态"(对报警机组那是假陈述) |
-
-v2 页面还有一条硬要求:可展开的「查看完整依据」不得为空。该类卡片的行来自融合面链盘,而 SCADA 侧只覆盖多报警机组,两边集合不同时旧的查表会取空;现在的口径是缺依据时按本行字段自组句(部件、链路走到第几步、卡在哪一步、证据源),永不空。
-
-***
-
-## 9 本体与知识层设计
-
-### 9.1 六个摄入步与对象库规模
-
-本体层的构建是重算链 ⑦ 的六个子步,逐步说明如表 9-1 所示。本体层"零判级权":它只转录 L1 正本(判级矩阵与融合面)的结论,每条判级对象带审级与 claim 时间窗,证据指回 L1 产物,同部件多源判级并存不合并。
-
-| 顺序 | 命令 | 输入 | 输出 |
-|---|---|---|---|
-| 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/<场>/ontology/objects.json,4,452,520 字节,对象结构为 id、type、props、links 四个字段)。
-
-| 对象类型前缀 | 对象数(实测) | 说明 |
-|---|---|---|
-| workorder | 5,567 | 检修工单展开 |
-| component | 580 | 部件与机群级系统对象 |
-| wi | 559 | 作业指导步骤 |
-| task | 559 | 预防性任务 |
-| alarmcode | 555 | 报警码(含中文码义) |
-| verdict | 411 | 判级对象(带审级与证据) |
-| evidence | 411 | 证据对象(指回 L1 产物) |
-| oilsample | 404 | 油样索引对象 |
-| catalog | 207 | 目录与分册 |
-| doc | 303 | 文档与图纸索引 |
-| failmode | 150 | 失效模式 |
-| turbine | 38 | 机组 |
-| decision | 39 | 决策链进度(一台一个) |
-| mechanism | 8 | 失效机制 |
-| 合计 | 9,791 | 对象库当前对象数 |
-
-按对象自身的类型字段统计(与上表按顶层键前缀统计口径略有差异,差异来自 Doc 类含文档与目录两种前缀):工单 5,567、部件 580、预防性任务 559、作业指导 559、报警码 555、文档类 510、判级 411、证据 411、油样 404、失效模式 150、决策 39、机组 38、失效机制 8。对比参考:docs/系统设计说明.md 记录的 2026-09-17 时点为 9,613 个对象,本次实测为 9,791 个(对象库文件修改时间 2026-09-21),两个数字都如实保留并注明口径日期;检索索引文件 2,842,037 字节,实机参数表 58,397 字节。本体审计(python -m src.ontology.audit)的期望值是 0 问题,判级时间窗口径常量为"近 90 日固定时间窗(至 2026-07-07,曲线轴为 2025H2)"。
-
-### 9.2 MCP 纯函数只读复用
-
-本体层对外暴露一组只读工具(src/ontology/mcp_server.py),全部为纯函数、模型零写权、判级零生成,只转述对象里的判级原文与审级。工具共 18 个,另有 1 个接地闸,如表 9-3 所示。
-
-| 工具 | 用途 |
-|---|---|
-| ont_stats | 本体库概览与判读纪律(全场级问题开局) |
-| ont_fleet_verdicts | 全场判级速查(一次给全 38 台的台号、部件、级别、审级) |
-| ont_turbine_brief | 台级汇总(判级带审级、机制、工单、油样、决策) |
-| ont_brief_pack | 一站式取证包(台级问题首选,一轮取齐) |
-| ont_get | 按 id 取对象全文(props 与 links) |
-| ont_query | 按类型、台号、关键词过滤查询 |
-| ont_links | 对象的正向链接与反向被引 |
-| ont_search | 混合检索(术语加语义召回相关对象 id) |
-| ont_mechanism_search | 失效机制库检索 |
-| ont_scenario | 排期沙盘推演结果(建议级) |
-| ont_loss | 损失分析(近 N 月分态与台级损失结构) |
-| ont_risk | 风险跟踪面(监视读数、机制状态、验收状态机、盲区纪律) |
-| ont_workorder | 检修工单查询(按台、系统、关键词) |
-| ont_chain_fault | 链二抢修:报警码到码义到失效模式到手册分步处置 |
-| ont_chain_preventive | 链一预防:系统或部件到失效模式到预防性任务到作业指导步骤 |
-| ont_fault_tree | 链三故障树:系统失效模式按实证量排序加征兆码 |
-| ont_maint_plan | 链四计划:按风险级与命中数排序的检修排程建议 |
-| ont_doc_search | 技术支持:图纸、原理、手册检索并返回现场可调阅路径 |
-| claim_check | 接地闸:最终回答前必须调用,台号与数字须来自本会话工具事实,越界即拒 |
-
-这组工具同时被多处复用,且"只读"不是口头声明而是可核事实:整份模块里文件写入、目录创建、进程调用与网络请求四类接口零命中;对象库缺失时直接响亮报错并提示先跑铺开步,不静默降级;读库按对象库文件的修改时间失效重载,避免读到静默陈旧的库。复用方有五处:离线问答代理(同进程直连,工具与接地闸零复制)、工作台本体页的三个服务端函数、工作台机制链接口的按类派发、问答服务的取证调用,以及总览页烘焙时的本体数据注入。工作台侧的回答会自动附结构化事实契约引文块,引不到契约条时明写"未被契约背书"。
-
-### 9.3 机制链、决策台与对象浏览器
-
-机制链(chain_ingest)把逐台处置进度落成决策对象,记六个步骤的状态、卡点与下一步;它不是证据对象,而是系统推出来的处置状态,因此需要服务在跑(它从工作台的链盘接口取数)。本体层不设独立页面标签,承载页是工作台的第 7 个标签「检修决策」(口径原话:"检修决策等于本体展现的窗口"),页内四段分别是「机制链视图 · 29 号范例(证据到机制到判级到沙盘到动作到验收)」「失效机制库」「决策台(决策捕获加月度滚动)」「对象浏览器」。这四段的取数接口是本体概览、对象列表、对象详情与机制链派发(按故障、预防、故障树、计划、文档五类分别派给对应的只读工具);这些接口在生成单文件快照时也会被逐个烘焙进页面,因此离线打开也能看。页面页脚写着一句纪律:本体是只读语义层,判级权在 L1 加审级,模型与网页均零判级权。
-
-事实契约是把结论对外化的那一层:每条结论由 scripts/guanlan_facts_contract.py 生成,包含 claim 编号、脱敏来源引用(文件名加 sha16 与章节)、本条 sha256、时间窗、聚合层级与裁决(六枚举之一),并派生出四类消费者件(门户结论段、工作台卡片、问答引用、报告摘要)。当前口径是"由重算台账生成 claim"(⑤b 步),因此门户结论段、问答与报告都随重算刷新;claim 六族是报警集中、重复检修、停机损失集中、温度相对离群、功率曲线偏离、振动面过闸线,相对判据一律封顶"候选"。
-
-***
-
-## 10 本机模型接入设计
-
-### 10.1 档位与配置
-
-模型接入完全可选:判断在代码,模型只做问答、解释与初筛。档位与显存需求写在 configs/models.json,如表 10-1 所示;配置注释明确"换模型改这里加 ollama pull,不改结论"。
-
-| 档位键 | 模型 | 显存需求(写入配置) | 用途 |
-|---|---|---|---|
-| default_qa | qwen3:8b | 6 GB | 默认问答,关闭思考模式;校闸不过自动升档一次 |
-| escalate_qa | qwen3:32b | 22 GB | 复杂题升档(2026-09-22 由本机未装的旧档名改为本机实装档) |
-| review | deepseek-r1:14b | 10 GB | 交叉审核初筛票(非独立审级) |
-| embed | bge-m3 | 2 GB | 检索向量 |
-
-配置文件另给出三套显存档位组合(24 GB 及以上四档、12 至 16 GB 三档、纯 CPU 两档)与四条拉取命令,运行地址是本机 11434。本机实测四档全部可用(自检逐档打印档位与模型名)。
-
-### 10.2 问答校闸与升档
-
-问答链路(src/ontology/fast_agent.py)的口径是"校闸不过不出正文":回答里引用的对象编号必须存在、引用判级对象的句子必须写出该对象的判级词、句中的系统名必须与所引对象一致;拦截原因会明确写给用户(换问法、点名机组号或结论编号、查看契约结论清单)。慢档(32B 级)单轮改写超过 60 秒时只给一次改写机会,避免为一个不过闸的答案烧掉十几分钟。
-
-升档的规则是:默认档被闸拦下则用升档目标重答一次;升档也拦则照旧不出文,但把两次拦截原因都带回。升档目标不再硬编码为单一档名,而是按"配置档位且确已安装"解析(先取 configs/models.json 的 escalate_qa 且验证本机确有该模型,再回落到代码内的映射表,都没有则返回空并响亮回落同时打日志)。这一改动来自 2026-09-22 的人工测试消缺:原先升档目标写的是本机未安装的档名,导致升档调用拿不到摘要、问答终态为 error,用户只看到"复核中"。
-
-### 10.3 耗时预算
-
-耗时预算按档位给出(src/ontology/fast_agent.py 的 BUDGET_S):8B 档 30 秒、另一轻档 30 秒、32B 档 120 秒(32B 权重约 18.8 GB,单轮可达数分钟,属"离线慢审")。超预算后不再改写,直接交给升档或不出文。实测参考:本机自检的 Ollama 探针耗时 171.8 秒(同一底稿另一次记录为 114.09 秒),审计流水里也有探针超时 90 秒的失败记录,说明本机模型可用但慢,属纯 CPU 或显存不足场景。
-
-### 10.4 审计日志
-
-每次模型调用写一条 JSON 到 logs/audit/llm_audit.jsonl,字段如表 10-2 所示(依据该文件当前 40,751 字节内容与末两条实样)。
-
-| 字段 | 含义 |
-|---|---|
-| ts | 调用时间(ISO 形式) |
-| purpose | 用途(如 probe、ask、review) |
-| model | 模型名 |
-| digest | 模型摘要指纹 |
-| prompt_sha16 | 提示指纹前 16 位 |
-| options | 参数(温度、随机种子、生成长度上限) |
-| think | 是否开启思考模式 |
-| tools | 是否带工具 |
-| attempt | 第几次尝试 |
-| latency_s | 耗时(秒) |
-| status | 结果(ok 或 fail) |
-| error | 失败原因(异常类型与消息,截断保存) |
-| output_sha16 | 输出指纹前 16 位 |
-| out_chars | 输出字数(可用于判断是否"零字成文") |
-
-审计流水与模型闸的其他纪律(固定随机种子与温度使同问同证据同答、同一时刻只有一个生成请求、瞬断自动重试一次、连续三次失败熔断 60 秒并明示、探针成功即清熔断)共同保证"模型部分可复核"。两条如实记录的观测:一是本机的审计流水在服务运行期间持续追加(本次只读观测中从 108 行增至 114 行),因此它是"活的"证据而不是静态文件;二是成功记录里会残留上一次尝试的失败原因(重试成功的那一行仍带超时字样),这是字段清理不彻底的小瑕疵,已列入待统一项。
-
-### 10.5 未装模型时的降级与如实标注
-
-未装模型时的降级分两层,口径不同。检索层是"真降级":向量检索不可用时自动改为纯词法检索并打印说明,绝不让检索层因为缺模型而整个不可用;重算链因此刻意只建词法索引(不建向量索引),所以离线机器上的对象库、机制链、对象浏览器与图纸检索全部照常可用。模型层是"响亮报错、不静默回落":模型闸在连续三次失败后熔断 60 秒并明确报"本机模型未就绪(调用方应报 503 或明示,不得静默回落)";页面把连接被拒一类的底层错误改写成"本机模型未启动(Ollama 127.0.0.1:11434 拒连);离线版不转云,请先启动 Ollama",问答接口返回 503,档位列表接口给出本机可用档位,自检里的模型探针判 FAIL 并记录原因。远端部署机(2026-09-22)实测没有装 Ollama(无二进制、命令不在 PATH、11434 端口拒绝连接),因此远端的问答与升档功能不可用,其余页面不受影响;这一点已记入版本记录的遗留项,处置建议是"装 Ollama 并把有网机器上的模型目录整体拷到目标机同位置",是否安装由用户决定。
-
-***
-
-## 11 运维控制台与重算编排
-
-### 11.1 运维控制台与动作
-
-运维控制台由网关提供两个入口:整页 /ops 与内嵌版 /ops/recalc(只保留重算、产物与动作进度三块,是门户「数据重算」页的 iframe 目标)。接口是 GET /ops/api/state(真实状态:各服务端口通不通、产物在不在、有没有任务在跑、上次结果)与 POST /ops/api/动作。动作只有 POST,其它方法返回 405(避免链接被预取或刷新时误触发),未知动作返回 404。四个动作如表 11-1 所示。
-
-| 动作 | 语义 | 语义冲突时的返回 |
-|---|---|---|
-| stop_services | 停止组件服务(保留控制台本身) | 组件都已停止时回 409 |
-| start_services | 启动组件服务并打开门户 | 组件都在运行时回 409 |
-| rebuild | 执行重算(等价于 python scripts/rebuild_all.py) | 已有任务在跑时回 409 |
-| products_off | 清除产物(真删除,不留备份) | 产物已清空时回 409 |
-
-按钮的禁用只是提示,后端一律再校验一次;直接打接口也绕不过并发与语义前置。页面每 2 秒轮询一次状态。
-
-### 11.2 任务为什么必须脱离网关的父子树
-
-若网关直接用子进程方式起重算,任务就是网关的子进程;而停止服务用的是"按进程号并沿父子树强杀",会连带把正在执行的重算一起杀掉(实测日志停在第六步"重启组件服务(停)"那一行,之后什么都没有,服务也停在半路)。因此运维动作经一个只做一件事的启动器二次拉起执行器后立即退出:执行器的父进程变成一个已退出进程,于是它不再是网关的子孙,沿树强杀不会再连带杀掉它。启动器把执行器的进程号打到标准输出的最后一行,供父进程记账。
-
-与此配套,重算链的第六步只重启组件服务、保留网关,理由有二:控制台页面本身由网关提供,连它一起停会把用户正在看的界面弄没;重算本身可能就是从控制台起的任务,停网关会沿父子树把重算一起杀掉。
-
-### 11.3 状态文件、心跳与日志
-
-任务状态写在 run/ops_job.json,字段与本次实测内容如表 11-2 所示。执行器每 15 秒更新该文件的心跳;网关侧超过 150 秒没有心跳就把运行中收尾为完成并写"心跳停了 N 秒,任务多半被强杀,未拿到退出码";判失败前会重读一次状态文件,防止轮询竞态把退出码覆写。
-
-| 字段 | 含义 | 本次实测值 |
-|---|---|---|
-| kind | 动作类型 | rebuild |
-| cmd | 实际命令 | scripts/rebuild_all.py --with-verify |
-| pid | 执行器进程号 | 54848 |
-| log | 动作日志路径 | logs/ops/ops_rebuild_20260919-095635.log |
-| status | 任务状态 | done |
-| rc | 退出码 | 1 |
-| started、finished | 起止时间 | 2026-09-19 09:56:35 至 2026-09-19 10:43:31 |
-| seconds | 耗时(秒) | 2815.5 |
-| note | 备注 | 空 |
-
-动作日志落在 logs/ops/,命名为 ops_动作_YYYYmmdd-HHMMSS.log,保留最近 20 份或 30 天(写前清理);本次实测该目录恰有 20 个文件,符合保留策略。运行态文件 run/pids.json 与 run/ops_job.json 是"当前跑着谁、当前在做什么"的唯一凭据,交付包默认排除它们。
-
-### 11.4 服务化重启与端口占用处置
-
-停止与启动的口径是:停止用按进程号并沿树强杀(Windows)或按进程组发信号(类 Unix),成功后删除运行态文件;启动按配置起六个组件并等待健康检查就绪(40 次 1 秒探测),全部就绪返回 0,有模块未起返回 1(属预期降级态),健康检查 60 秒未就绪返回 2。端口占用时的处置顺序是:先停服务再启动;仍被占用则在运维控制台看端口状态与进程,按端口清理占用进程;仍不行则改 configs/serve.json 的端口并同步网关路由表(网关的上游端口表默认值与 serve.json 可覆盖的键名一一对应)。
-
-***
-
-## 12 安装、服务化与版本管理设计
-
-### 12.1 安装与卸载
-
-Windows 安装入口是 install.bat 与 install.ps1,步骤号写死在输出里,如表 12-1 所示(依据 install.ps1 的实际打印文案)。类 Unix 入口是 install.sh,口径与 Windows 侧同构(选解释器、建虚拟环境、装依赖、写配置、可选装 systemd 单元、写安装记录、跑自检)。卸载入口是 uninstall.bat 与 uninstall.sh,共用同一份实现 scripts/guanlan_uninstall.py。
-
-| 步骤 | 做什么 | 失败时的处置 |
-|---|---|---|
-| 0/7 安装前检查 | 读 src/version.py 取本包版本,读安装根下的安装记录比版本差异(同版本、升级、降级、老安装无记录四类),交互式询问是否重装 | 交互式答非 y 即取消且不改任何文件;加 -Force 跳过询问;非交互自动继续 |
-| 1/7 选 Python | 需 3.11 及以上(依赖 pandas 3,3.10 及以下没有轮子);机器上没有则解包内便携运行时 | 两者都没有则打印两条办法后退出 |
-| 2/7 建虚拟环境 | 建 .venv 并升级 pip | 失败即停 |
-| 3/7 装依赖 | 优先用包内离线轮子 wheels/win_amd64,无轮子才联网安装 | 依赖安装失败退出码 3,并提示先解决再启动 |
-| 4/7 写配置 | 把 configs/serve.json 的 python 字段写成相对安装根的 POSIX 路径 | 写配置失败退出码 4 |
-| 5/7 建启动快捷方式 | 建"启动观澜.lnk"(中文名给人)与"start_guanlan.lnk"(ASCII 名给脚本)与桌面快捷方式,指向无窗口启动器 | 建不出来只告警不影响使用,并给手工建法与 start.bat 替代 |
-| 6/7 服务化(可选) | 调 services/service_ctl.py install 注册 Windows 服务,或加 --user-login 写登录自启 | 注册失败只告警,并提示需要管理员权限与 --dry-run 预览 |
-| 7/7 模型(可选) | 包内有离线安装包则静默安装 Ollama,并提示模型目录的拷贝办法 | 未装且包内无离线包时明确写"问答不可用",其余页面不受影响 |
-| 收尾 | 写安装记录 install-info.json 并跑一次自检 | 自检输出作为装机结论 |
-
-安装时间为整轮口径"首次约 10 分钟"(说明书),逐步耗时无实测证据。卸载的默认口径是"只拆本程序装上去的东西":停服务、注销服务、撤开机自启、删快捷方式、清运行态、删安装记录;默认保留 data、outputs、logs、.venv 与程序目录,加 --purge 才删数据(会再问一次),加 --all 才删程序本体。之所以默认不删程序目录,是因为 Windows 上正在运行的 python.exe 就在 .venv 里,"删自己必然只删一半",留个删了一半的目录比留着整棵目录更难查;卸载脚本因此先切到临时目录再调 Python。
-
-### 12.2 服务化形态
-
-两平台的服务化形态如表 12-2 所示。两平台共用同一个工作体(起等于 guanlan.py serve、守护等于每 15 秒巡检同样命令、停等于 guanlan.py stop),因此"服务起的"与"手工起的"口径完全一致,不会出现两套行为。
-
-| 平台 | 形态 | 服务体 | 注册器 | 前置条件 |
-|---|---|---|---|---|
-| Windows | Windows 服务(SCM 托管,独立进程) | scripts/win_service.py(用标准库直连服务控制管理器,不依赖第三方库) | scripts/service_ctl.py install(内部调系统命令创建、描述、配置失败重启、启动) | 管理员 |
-| 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 与免权限守护自证两条路径,其输出与设计说明一致),属如实留白项。
-
-### 12.3 安装记录与版本三守卫
-
-安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.10.1,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
-
-| 字段 | 含义 |
-|---|---|
-| name | 系统名 |
-| version | 已装版本(唯一凭据) |
-| edition | 版本性质(离线单包) |
-| installed_at | 安装时间 |
-| python | 安装时使用的解释器绝对路径 |
-| mode | 运行方式(手工、Windows 服务、systemd、登录自启) |
-| host | 安装根 |
-
-版本号只写在 src/version.py 的 VERSION 一行,改动自动传导到各处;三条守卫如表 12-4 所示,都是"对不上就报错",不靠人记得。
-
-| 守卫 | 读哪里比对 | 违反时的表现 |
-|---|---|---|
-| 版本真源守卫 | scripts/pack_dist.py 读 src/version.py 的 VERSION 写进包清单并拼默认包名 | 打包器不再自带版本常量,避免"包说一个版本、安装记录说另一个版本" |
-| 版本记录守卫 | scripts/version_log.py --check 比对 docs/版本记录.md 的自动块与 src/version.py 的 HISTORY | 不一致即 rc=6,提示重生成;重算链 ⑧d 步容忍 6 但报出来 |
-| 安装前检查守卫 | install.ps1 与 install.sh 从 src/version.py 取版本并与安装记录比对 | 同版本、升级、降级、无记录四类分别提示,降级会明确警告 |
-
-另有一条与版本同级的守卫是入口引用闭合与入口脚本编码守则(见 13.3 节):打包、开箱验证、装机自检三处都查,缺一个文件就不出包。
-
-### 12.4 打包器与开箱验证
-
-打包器 scripts/pack_dist.py 的默认口径是"一条命令、零开关":程序与脚本、配置、发布件、文档、离线依赖件与入口脚本进包;输入数据、产物、运行日志与临时文件不进包(例外用 --with-data 与 --with-products),另排除装机产物(安装记录与快捷方式,因为它们只在本机有效)。包内清单 dist-manifest.json 逐条记版本、构建时间、包含项、逐条排除理由与四类标志(不含数据、不含产物、不含日志、不含临时文件)。
-
-打包实测口径:一次打包报 3,430 件、1,032.0 MB(该数字来自变更记录,非本次实测;旧口径含缓存与日志为 3,741 件、1,091 MB)。开箱验证一条命令给出"能不能装、能不能跑"的证据,步骤如表 12-5 所示。
-
-| 步骤 | 核验内容 | 判定 |
-|---|---|---|
-| ① 解压 | 解压到临时目录并打印件数与耗时 | 解压失败即失败 |
-| ①b 闭合与编码 | 入口脚本引用闭合、编码守则核对 | 任一不通过即 rc=1 |
-| ①c 数据骨架 | 输入数据目录骨架逐个在位,且除放置说明外无混入文件 | 不通过即失败 |
-| ② 离线安装 | 在副本上真跑安装 | 非 0 即失败 |
-| ②b 无窗口与快捷方式 | 两份快捷方式在位、无窗口自证通过、包内无脚本宿主脚本 | 不通过即失败 |
-| ③ 起服务核验 | 核验门户根、工作台、振动、运维控制台、健康检查五个地址 | 要求至少 4 个可用 |
-| ③b 卸载核验 | 在刚装好的副本上真跑卸载,断言记录、运行态、快捷方式都拆掉且数据与产物一件不少 | 不通过即失败 |
-| ④ 收尾 | 只停副本自己的进程、清理临时目录 | 副本端口无残留 |
-
-一项现场教训写进了设计:开箱验证原先必须占用默认组件端口,于是验证期间用户正在用的工作台与振动页是断的(用户点门户「登录」正好撞上)。现在网关的上游端口表可被 configs/serve.json 覆盖,验证改为副本整套换到空闲端口(例如网关 28700、组件 28800 起),主实例照常运行;找不到空闲端口才降级为"跳过启动核验"并打印提示。
-
-***
-
-## 13 质量保证设计
-
-### 13.1 审计脚本矩阵
-
-系统的质量保证是"一条链加重算里的审计门加一个装机自检"。审计器矩阵如表 13-1 所示,退出码与本次实测结论一并列出。
-
-| 审计器 | 查什么 | 退出码语义 | 本次实测 |
-|---|---|---|---|
-| scripts/products_reverse_audit.py | 逐件回溯"输出到功能与算法到输入",族表匹配与人工件识别 | 0 全部成立;5 有未归类或判据失败 | rc=0,呼应成立 3,548 件、不成立 0、人工件 0、未归类 0 |
-| scripts/pages_audit.py | 页面归口五条机器规则(静态页不得引产物、实时页端口须在端口表内、引用产物须在位、数据派生页须有来源与指纹、冻结交付件须有版本与日期) | 0 一致;5 登记缺项或文件缺失;6 溯源缺失;7 数据派生页面陈旧;8 交付件缺版本号;9 分类错误 | rc=0,检查 21 项、2 项一致、19 项已知缺口、0 项要处理 |
-| scripts/config_audit.py | 配置登记表 R1 至 R8(顶层只放登记过的运行期单件、无备份垃圾、命名、内容不得含本机绝对路径、代码引用的配置必须存在、不许手拼配置路径、每域须声明消费者、场目录每件要么是场定义要么登记为 profile) | 0 通过;5 结构问题;6 悬空引用;7 内容含本机绝对路径;8 手拼路径;9 场配置不合约定 | rc=0,171 件配置、5 个域、1 个场定义、10 件机型 profile、0 不一致、14 已知缺口、7 提示 |
-| scripts/log_audit.py | 日志 L1 至 L5(落点唯一、命名、按末尾 200 行校验行格式、行尾与颜色、保留策略与审计流水每行合法 JSON) | 0 通过;5 日志跑到 logs 之外;6 命名不合口径;7 行格式不合规;8 行尾或颜色问题;9 保留策略超限 | rc=0,0 不一致、87 条提示(历史日志与旧行只提示) |
-| scripts/raw_scan.py | 输入数据逐族指纹比对与增量识别 | 0 无变化;4 有新增或变化;5 还没有基线 | rc=0,"与上次快照一致,没有新数据";另 --selftest rc=0(族表覆盖反向族表的 7 个输入、10 个步骤标签都能在链上找到) |
-| scripts/chain_gap_check.py | 振动六层链四步脚本是否齐全、关键产物是否在位、能否逐值对拍 | 0 齐全;5 缺料 | rc=0,四步脚本齐全;两件关键产物按口径重建但无标准答案可对拍 |
-| scripts/version_log.py | 版本记录自动块与代码 HISTORY 是否逐字一致 | 0 一致或已写;2 文件缺失;6 需重生成 | rc=0 |
-| scripts/detail_deps.py | 工作台各页面与接口的产物依赖台账(页面到接口到产物件到来源与生成端) | 0 通过;5 文档与现场不一致或证据核对不通过 | rc=0,清单 10 页签、16 接口、缺口 0 件、产物仓 3,549 件,文档与现场一致 |
-| scripts/inventory_products.py | 产物清点与输入到产物呼应校验(产物跨度必须落在输入跨度内) | 0 呼应正常;5 有 problems | rc=0(抽样:振动输入 25,693 件对时间窗索引 2,066,686 行、跨度 2026-03-16 至 2026-04-21) |
-| scripts/check_portability.py | 换机可移植性门禁三条硬规则(不许机器相关绝对路径、不许工作目录相对路径字面量、不许用系统分隔符拼库存字符串)加三条提示规则 | 0 通过;1 有严重项 | rc=0,通过(无机器相关绝对路径、无工作目录相对路径、未用系统分隔符拼串),例外登记 16 项加 2 类 |
-| scripts/check_transferable.py | 移植性大扫描五项(全类型文件的机器相关路径、虚拟环境解释器指向、各平台轮子是否备齐、便携运行时与清单、配置与端口是否相对) | 0 通过;1 有机器相关路径 | rc=1,290 处机器相关路径(HTML 烘入绝对路径 0 处,文档旧口径写 350 处)、提到网关端口的文本文件 43 个;轮子与便携运行时两项判通过 |
-| scripts/audit_chinese_terms.py | 中文表达审核(OEM 术语基准与行话规则),机器给候选人裁 | 0 通过;2 仅严格模式下有已裁改条目仍命中 | rc=0,扫 5 个文件与 7,322 条中文串,规则命中 123 条,其中显示层已覆盖 111 条、内部面豁免 42 条、仍待处理 0 条;无根词 3,902 条 |
-| guanlan.py check | 装机自检(约 40 行结论:解释器、源码可编译、语言包成对、子进程口径、入口闭合与编码、版本、卸载入口、反向呼应、页面归口、配置、日志、输入数据、依赖、产物与本体与契约与门户、六个端口、模型探针与四档、模型可用性) | 0 全绿;2 有 FAIL | rc=0,结论"全绿, 可 serve";底稿 docs/src/_guanlan_check.txt |
-
-自检底稿里的几个可核对数字:Python 3.12.10;源码 229 个文件可编译、无非法转义或语法错;语言包前后端 zh 与 en 成对 845 条;入口脚本引用闭合 48 条;入口脚本编码守则通过;反向呼应 3,548 件;页面归口检查 21 项;配置与日志无一致性问题;输入数据 26,503 件源件结构合规;依赖九项(numpy、pandas、pyarrow、polars、yaml、matplotlib、plotly、jinja2、docx)全部就位;L0 与 L1 产物、本体对象库、findings、事实契约、门户、仿真合页服务与资料包、三维资产、仿真回放资产、治理清单交付件全部在位;六个端口处于运行中;本机模型探针通过(耗时 171.8 秒)。
-
-有一处过程值得记录:本轮取证期间,工作台依赖台账门与可移植性门禁各自返回过一次"未通过"(一个是文档清单与现场不一致,一个是运行期源码里用系统分隔符拼串),随后两处都按同一口径修好并复跑转绿(可移植性门禁复跑输出"通过,路径可移植:只有相对路径加经路径模块解析",依赖台账复跑输出"文档与现场一致")。这正好说明"每条门都能独立重跑并给出退出码"的价值:问题是被跑出来的,不是被猜出来的。
-
-### 13.2 退出码语义与质量门闭环
-
-各审计器的退出码语义汇总如表 13-2 所示,语义与重算链里的容忍集合一一对应(见 5.2 与 5.3 节)。
-
-| 退出码 | 含义 | 谁在用 |
-|---|---|---|
-| 0 | 通过或无变化 | 全部审计器与构建器 |
-| 1 | 有严重可移植性问题;或链级失败汇总 | 可移植性检查器;重算链汇总非零即返回 1 |
-| 2 | 自检有 FAIL;契约自检不过;非交互下需要确认;文件缺失;严格模式下术语命中 | guanlan.py check、事实契约构建、卸载器、版本记录、中文表达审核 |
-| 3 | 权限不足 | 安装脚本与服务化注册器、卸载器 |
-| 4 | 有新数据;或按缺件如实报 | 输入数据扫描、变桨面、振动历史与基线、等价验收 |
-| 5 | 随包件这一路缺失(无基线、有未归类、被引产物不在位、文档与现场不一致、缺料、结构问题) | 月度派生件、反向呼应审计、页面归口审计、等价验收、链完整性检查、工作台依赖台账、产物清点、配置审计、日志审计 |
-| 6 | 溯源缺失;文档与代码版本记录不一致;增量冲突;悬空引用;命名不合口径 | 页面归口审计、版本记录一致性、输入数据结构体检、配置审计、日志审计 |
-| 7 | 数据派生页面陈旧;内容含本机绝对路径;行格式不合规;缺源类或时间空洞 | 页面归口审计、配置审计、日志审计、输入数据结构体检 |
-| 8 | 交付件缺版本号;手拼配置路径;行尾或颜色问题;输入结构仅提示 | 页面归口审计、配置审计、日志审计、输入数据结构体检 |
-| 9 | 分类错误;场配置不合约定;保留策略超限 | 页面归口审计、配置审计、日志审计 |
-
-需要如实说明的是:多数审计器只定义了自己会用到的那几个码,未列出的码在对应脚本里并未定义。例如反向呼应审计与链完整性检查只定义 0 与 5,版本记录只定义 0 与 2 与 6,装机自检只定义 0 与 2,可移植性门禁只定义 0 与 1。读退出码时应同时看该脚本自己的说明,不要假设"非零都一样"。
-
-质量门形成闭环:改代码或改配置后,重算链在⑤、⑧、⑧b、⑧c、⑧d 五处设审计门;装机时 guanlan.py check 再统一体检一遍;打包时入口闭合与编码守则在打包、开箱验证、装机自检三处强制执行;出包以后在目标机上安装再由自检复核。闭环如图 13-1 所示。
-
-![图 13-1 质量门与审计闭环](figures/fig-des-06-质量门闭环.png)
-
-### 13.3 入口脚本的字符守则
-
-三条编码守则由 src/entry_refs.py 实现,并在打包、开箱验证第①b 步、装机自检三处强制执行,违反就不出包或直接判 FAIL,如表 13-3 所示。
-
-| 文件类型 | 守则 | 违反的后果(现场实炸过) |
-|---|---|---|
-| .bat | 纯 ASCII、CRLF、无 BOM、不含连续小于号、大于号后必须是重定向目标 | 批处理注释里的中文被命令行解释器当成命令执行;连续小于号直接报重定向错 |
-| .ps1 | UTF-8 带 BOM 加 CRLF | PowerShell 5.1 按本地代码页解码导致中文乱码与级联语法错,安装一秒即退出 |
-| .sh | 行尾 LF | 每个词尾粘回车,set -e 变非法选项,变量判空翻转,脚本从一开始就是坏的 |
-
-与此配套的一条纪律是"入口脚本引用的文件必须齐全":凡入口脚本里出现的路径且在本机源码树真实存在,打包时自动补入,打包后对着包内条目复核(缺一个就删包并返回非 0),开箱验证与装机自检各再查一遍。这条守卫的价值在替换无窗口启动实现时体现过:去掉脚本宿主方案后入口列表与引用条目自动重算且全部在位。
-
-### 13.4 文档纪律
-
-文档与代码同级受管:版本记录由 scripts/version_log.py 从代码里的 HISTORY 生成,自动块禁止手改;重算链的 ⑧d 步以 rc=6 容忍但不静默的方式盯"文档与代码版本一致"。这条门的由来是一次实逮:远端升级的差异盘点只覆盖源码、脚本与配置,文档不在盘点面上,于是代码升到新版本而版本记录与系统设计说明留在旧版,而重算链当时只到 ⑧c,导致 24 步全绿、控制台写"重算完成",文档漂移没有任何自动拦截点。现在的口径是"升级差异盘点必须包含 docs",并由 ⑧d 步兜底。
-
-本文本身也按同一纪律编写:每章的事实都能在附录 A 找到来源文件,数字都能按第 1.4 节的命令复跑复现;本版同时新增三份交付文档(需求分析、设计说明、数据要求说明)与其生成端(文档渲染器与图表构建器),图表里的数字全部从真件取数而不是手画。
-
-***
-
-## 14 安全、合规与离线边界
-
-### 14.1 离线边界与只读输入
-
-系统的安全边界是四个方面,如表 14-1 所示。
-
-| 边界 | 设计口径 | 落地证据 |
-|---|---|---|
-| 只读输入 | 原始件只读,摄入器不写 data/raw;放置工具的增量落位对"同名不同大小"默认拒绝并拦下,必须显式确认才覆盖 | scripts/place_raw_data.py 默认拒绝冲突;产物是"当前原始件的函数"(源件删了行也随之消失并报出) |
-| 默认离线运行 | 全部计算在本机;依赖随包离线安装;模型可选且指向本机 | wheels/win_amd64 42 件、vendor/python 三平台便携运行时;模型端点是本机 11434 |
-| 唯一外发通道且强制脱敏 | 代码内保留一条显式的"可上云问答"通道(三个兼容云端接口,需环境变量提供密钥);未配置密钥时回落本机模型;外发前强制对每条消息做脱敏,脱敏模块不可用时直接拒绝外发而不是照发 | 离线模型代理模块的外发分支与脱敏调用;脱敏实体映射 16 条加三条正则;产物脱敏词表 32 条加 11 类回扫 |
-| 监听范围 | 组件只绑本机回环地址,只有门户网关使用 public_host(出厂为所有网卡) | configs/serve.json 的 host 与 public_host 及注释;启动时打印对外地址并提示"页面无鉴权,请在防火墙侧限来源";实测网关在所有网卡监听,其余六个进程只在回环监听 |
-| 无鉴权风险如实告知 | 页面没有登录与权限体系,对外监听等于对同网开放 | 配置注释与启动提示;处置建议是防火墙白名单或反向代理认证,不要直接挂公网 |
-
-### 14.2 脱敏与可上云面孔
-
-对外披露的内容走独立的一层:可上云面孔落在 outputs/场站/guanlan/cloud,由专门的面孔与页面与问答脚本生成;事实契约的每条结论带脱敏来源引用(文件名加内容摘要前 16 位与章节)与本条 sha256,因此"这句话出自哪份文件的哪一节、当时那份文件是什么内容"都可核对。脱敏实现分布在若干模块,口径可以概括为四层:一是外发消息的实体映射(16 条,把业主集团、整机厂、部件供应商、场站名与机型泛化,另有本机路径、电价与机群规模三条正则);二是产物与交付侧的公开词表(32 条替换词加 11 类独立回扫规则,替换与回扫分开写,并要求交叉自检"回扫认识每个替换词、替换后回扫干净",有残留即抛错拒绝出件);三是振动产物里唯一的字段级黑名单(8 个频率类键整值遮蔽,另有 17 条阈值或频率正则),交付件使用的是不看开关的强制脱敏版本;四是机组代号化(按定序摘要映射,不可由编号反推)。对外面孔生成后仍要回扫一次,命中本机绝对路径、邮箱、手机、身份证、精确坐标、密钥格式等任一类即返回 2 不出件;云问答出口同一口径,脱敏模块导入失败即拒绝启动。脱敏的边界是"对外面孔这一层做,内部产物不脱敏",两层由目录与生成端区分;机组号与机群台数在对外面孔里只报不拦,是否代号化由使用方裁决并写进面孔清单。需要如实说明的是,交付前另有一个安全核查器(密钥、个人信息、金额与业主名、内部件四类规则),但它目前尚未接入打包链,属手工步骤。
-
-### 14.3 日志与审计
-
-日志是审计的载体:长驻服务与启动器的日志落在 logs 组件名.log 并按统一行格式写;运维动作落在 logs/ops/ 并按保留策略清理;构建与摄入日志落在 logs/build/场站/ 下按产物子路径归档;机器审计流水落在 logs/audit/(模型调用流水 llm_audit.jsonl、云问答流水 cloud_qa.jsonl、术语审计 terms_audit.json)。统一格式之前的日志全部搬到 logs/legacy/ 留档,不悄悄删除。
-
-### 14.4 权限不足时的降级路径
-
-权限不足时不抛"访问被拒绝",而是给出可执行的替代路径,如表 14-2 所示。
-
-| 场景 | 无权限时的行为 | 可用替代 |
-|---|---|---|
-| 注册 Windows 服务 | 打印前置检查结果、逐条将执行的命令,返回码 3 | 管理员重试;先用 --dry-run 看它要做什么;改用登录自启(--user-login) |
-| 注销服务(卸载第②步) | 缺权限时照做其余步骤并打印手工命令,返回码 3 | 管理员/root 执行注销命令 |
-| 注册 systemd 单元 | 打印将写入的单元全文,不写入 | root 重试 |
-| 端口被占用 | 不静默重试,给出占用排查与改端口路径 | stop 后重启;按端口清理;改配置端口并同步网关路由表 |
-| 缺产物或缺模型 | 返回结构化缺件页或明确的降级提示,不伪造内容 | 放原始件后重算;补齐生成端;安装并拉取模型 |
-
-***
-
-## 15 可移植性与资源占用
-
-### 15.1 另一台机器零配置启动
-
-迁移到另一台机器的步骤如表 15-1 所示。要点是"程序与配置路径无关(全部相对安装根),但虚拟环境不能直接搬"——虚拟环境的配置文件绑定基础解释器路径,因此换机后跑一次安装即离线自足。
-
-| 步骤 | Windows | Linux 或 macOS |
-|---|---|---|
-| 1 解包 | 解压交付包到安装目录(目录不要带空格与中文) | 解包到目标目录(同样避免空格与中文) |
-| 2 安装 | 双击 install.bat(或调用 install.ps1) | sh install.sh(可用 GUANLAN_PY 指定解释器,--service 装 systemd) |
-| 3 自检 | 双击 check.bat 或跑 guanlan.py check | .venv/bin/python guanlan.py check |
-| 4 放数据 | 把原始件放进 data/raw/场站名/ 下并按源类目录落位 | 同上 |
-| 5 重算 | guanlan.py serve 后从门户「数据重算」触发,或直接跑 rebuild_all.py | 同上 |
-| 6 启动 | 双击"启动观澜"快捷方式(无窗口)或 start.bat | .venv/bin/python guanlan.py serve |
-
-安装根可用环境变量覆盖(路径模块优先读 WINDSCADA_ROOT),原始件根可用环境变量 `WINDSCADA_<代号>_SRC`(**变量名内写死样本场代号**,换场须改名,见 16.5 节)覆盖,场名可用 `WINDSCADA_FARM=<场>` 覆盖;配置里的路径一律写相对安装根的形式,因此换盘、换机不需要改任何路径。
-
-### 15.2 Python 与依赖 wheel
-
-解释器要求是 3.11 及以上(依赖 pandas 3,3.10 及以下没有轮子);机器上没有 3.11 时使用包内便携运行时(3.12.14,三个平台各一份)。依赖清单共 19 条固定版本:polars 1.42.0、pandas 3.0.3、pyarrow 24.0.0、numpy 2.5.0、pyyaml 6.0.3、python-frontmatter 1.3.0、matplotlib 3.11.0、plotly 6.8.0、jinja2 3.1.6、python-docx 1.2.0、docxtpl 0.20.2、python-dotenv 1.2.2、requests 2.34.2、rich 15.0.0、tqdm 4.68.3、openpyxl 3.1.5、scipy 1.18.0、xlrd 2.0.2,另有一条不锁版本的 tzdata(Windows 无系统时区库,而 pandas 3 硬依赖它)。离线件如表 15-2 所示(件数与体积为本次实测)。
-
-| 离线件目录 | 件数(实测) | 体积(实测) | 覆盖平台 |
-|---|---|---|---|
-| wheels/win_amd64 | 42 | 169.4 MB | Windows 64 位完全离线可装 |
-| vendor/python | 3 | 81.4 MB(含清单文件共 4 件) | Windows x86_64、Linux x86_64、Linux aarch64 便携运行时 3.12.14 |
-
-Linux 与 macOS 侧包内没有轮子时,安装脚本自动改为联网安装;要完全离线需把对应平台的轮子放进相应目录。模型离线件(Ollama 安装包与模型目录)单独交付,不在主包内;未装时问答不可用,其余页面不受影响。
-
-### 15.3 磁盘、内存与算力估算
-
-资源需求与实测体积如表 15-3 所示。内存与磁盘的硬性要求来自说明书(最低内存 16 GB、推荐 32 GB;磁盘最低 5 GB 加三维资产 1 GB,另加模型 30 至 60 GB);目录体积为本次递归实测。
-
-| 项 | 数值 | 出处 |
-|---|---|---|
-| 内存最低或推荐 | 16 GB 或 32 GB | 《使用说明书》v0.2(随包 docs/) |
-| 磁盘最低 | 5 GB 加三维资产 1 GB | 同上 |
-| 模型占用(可选) | 30 至 60 GB(固态盘) | 同上 |
-| 显存(只影响问答与本地审核) | 12 GB 及以上可跑默认档;24 GB 及以上可开升档;无显卡走纯 CPU 慢档 | 同上 |
-| data(原始件,本机实测) | 26,504 件、224.67 GB(其中振动原始导出 25,693 件、150.15 GB) | 本次递归实测 |
-| outputs(产物,本机实测) | 3,549 件、7,157.5 MB | 本次递归实测 |
-| release(发布件,本机实测) | 2,898 件、708.4 MB | 本次递归实测 |
-| 虚拟环境(本机实测) | 17,779 件、688.1 MB | 本次递归实测 |
-| 日志与配置(本机实测) | 日志 154 件、3.3 MB;配置 171 件、1.6 MB | 本次递归实测 |
-| 全仓不含版本库(本机实测) | 52,049 件、241,707.5 MB | 本次递归实测 |
-| 运行内存(本机实测快照) | 七个监听进程工作集合计 568 MB(网关 238.9、工作台 198.6、振动 53.8、本机模型 33.7、三维 15.4、四系统合页 13.9、仿真 13.7 兆字节);同批私有提交合计 3,293.7 MB | 本次实测快照,非稳态峰值,仅供量级参考 |
-| 交付包(口径) | 3,430 件、1,032.0 MB | 变更记录,非本次实测 |
-| 重算耗时(口径) | SCADA 侧 10 个构建器约 15 分钟;振动摄入按现场导出体量约 14 分钟(另有 25 分钟口径);变桨面约 10 至 12 分钟;窄仓一次扫约 2 至 3 分钟 | 代码注释与手册,多口径未统一 |
-| 单次完整重算(实测) | 本机一次带等价验收的重算 2,815.5 秒(约 47 分钟),该次以退出码 1 结束(末步等价验收因无随包基线一路属预期) | run/ops_job.json |
-| 按所选时间窗重算 | 判级约 25 秒每时间窗;七镜头约 9 至 44 秒每时间窗 | 服务注释 |
-
-算力方面的判断是:系统的重活集中在重算链(一次性、可后台跑),页面侧的实时计算只有按所选时间窗的分组与过滤,最重的两类已经用窄仓与进程内缓存压到秒级;模型问答是唯一可能占用数分钟的交互,且已按档位给预算并在超时后明确不出文。
-
-### 15.4 Linux 适配现状
-
-Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次环境未实测"或"已在本机验证"两类之一,不含估计。
-
-| 项 | 现状 |
-|---|---|
-| 安装脚本 | install.sh 已有,逻辑与 Windows 同构(选解释器、建虚拟环境、装依赖、写配置、可选装 systemd、写安装记录、自检) |
-| 便携运行时 | vendor/python 下已含 Linux x86_64 与 aarch64 两份 3.12.14 |
-| 依赖轮子 | 包内只有 win_amd64 轮子;Linux 需联网安装或自行放轮子 |
-| 服务化 | systemd 单元 guanlan.service 由注册器写入(简单类型、始终重启、停止超时 120 秒、混合杀进程模式、环境变量强制 UTF-8) |
-| 路径与换行 | 路径统一用 pathlib 与 POSIX 相对串;入口脚本按类型守行尾(.sh 用 LF) |
-| 进程与日志 | 无窗口子进程的口径在类 Unix 上是 0 标志位;日志行格式与保留策略平台无关 |
-| 未取证项 | Linux 安装、systemd 真装、国产化发行版首装均未在本次环境实测(历史交付件的 Linux 安装脚本曾有过行尾问题,已由编码守则与打包校验拦住) |
-
-***
-
-## 16 多场适用性与换场迁移
-
-### 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 所示,每条的处置建议都是可执行的一条命令或一次催缴动作。
-
-| 边界 | 影响 | 处置建议 |
-|---|---|---|
-| 本机未装配原生 MDB 驱动 | 本机无 pyodbc 与 mdbtools,读 MDB 走 ACE 提供程序加 PowerShell(与写库同一套);若目标机未装该提供程序,MDB 形态不可读(未在无 ACE 的机器上实测) | 目标机安装 ACE 提供程序;或让现场导出 CSV(取数层本就 CSV 优先) |
-| 测风塔数据零交付 | 风资源与尾流类分析缺外部对照(收资要求第 3 项记为未到位) | 按收资要求催缴测风塔数据;到位后重算对应族 |
-| 油样 2026-07 批 102 行缺源件 | 油样索引少该批,等价验收会报"有需人工看的差异" | 现场补该份合并报告 PDF 后重算 ② 步 |
-| 远端未装 Ollama | 远端问答与升档不可用,模型探针必然 FAIL;其余页面不受影响 | 安装 Ollama 并把有网机器上的模型目录整体拷到目标机同位置;是否安装由用户决定 |
-| SOP 相关配置悬空 6 条 | 分析锁校验不可用;场景解析不可用(本包无调用方,不影响页面);案例判别器外键校验与模块键校验缺单源;经济性换算退回内置假设并在结果里标注 | 需要哪一场就建对应配置;或明确删除该模块的入口引用;逐条影响已登记在配置登记表的 known_missing |
-| 振动六层链扫描件与正本类产物 | 四步脚本齐全且关键两件已按口径重建,但"无样件可对拍"(链完整性检查自述) | 由研发给口径或提供样件做逐值对拍 |
-| 数据派生页面无生成端 | 脱敏状态一览与取数单是客户手里的交付件,性质上随运行状态变但没有指纹与生成端,重算不会刷新它们 | 已按冻结交付件管理并把"会变旧"写进登记理由;将来若要随数据变,再补生成端与来源指纹 |
-| 页面引用的四棵产物树不在包内 | 传动链回放、四系统判据、控制律仪表台、取数单等页面存在 19 条已知的引用悬空 | 保留记账不删(删了门户裂口、交付缺件);待该产物线随包后复检 |
-| 3 件内嵌快照缺指纹 | 只能按生成时间判断新旧,无法做逐字节陈旧检测 | 下次生成时把来源与来源摘要写进页面(机制已就绪) |
-| 无生成端的历史组级产物清单 | 趋势件、热链、扇区、偏航与润滑面等族的历史随包件没有生成端;当前反向审计显示盘上 3,548 件全部呼应成立、未归类 0,但清单本身仍在文档里留档 | 由研发补口径(照月度派生件的办法反推并逐值验证);或承认其为人工件不按产物管 |
-| 收资要求其余未到位项 | 故障录波仅 4 台、原生秒级高频数据未见交付、保护定值未成册、可行性研究与微观选址未交付、周围已建风电场报表未交付、大部件更换台账不完整 | 按收资要求逐项催缴;到位后按族重算 |
-| 本轮曾出现的两处门禁未通过(已修复留档) | 取证期间出现两个未通过:工作台依赖台账门返回 5(文档清单与现场不一致)、运行期可移植性门禁返回 1(源码里用系统分隔符拼库存字符串) | 两处都已按原口径修好并复跑转绿(依赖台账输出"文档与现场一致"、可移植性门禁输出"通过");留档的目的是提醒:这两处都不是靠人记得,而是被检查器跑出来的 |
-| 移植性大扫描仍报机器相关路径 | 第二个可移植性扫描器返回 1:290 处机器相关路径(HTML 烘入绝对路径 0 处),均在交付留档脚本、历史文档与注释里,不在运行路径上;同一份扫描同时确认虚拟环境绑定本机基础解释器路径,换机必须重跑一次安装 | 出包前把该扫描器纳入打包前置检查,或把这份清单当具名白名单维护并逐条写明理由;不要靠"人工记得它不影响运行" |
-| 交付前安全核查器未接入打包链 | 该脚本有四类规则(密钥、个人信息、金额与业主名、内部件)与严格模式退出码,但打包器、打包壳脚本里都没有调用它 | 在打包器里加一步调用(严格模式),命中即删包并返回非零;否则它只是手工步骤 |
-| 包内离线轮子只覆盖 Windows | wheels 下只有 win_amd64 一个平台目录(42 件、169.4 MB);Linux 与 macOS 安装会自动改为联网 | 要在国产化平台上完全离线安装,需在有网同类机器上取件后放进对应平台目录 |
-| 文档引用的模型离线包不在包内 | vendor 目录实测只有 4 件(清单文件加三份便携运行时),没有模型安装包目录;而说明书与安装脚本多处引用该目录 | 现场需要模型时单独交付模型安装包与模型目录;或在文档中明确"该目录随单独交付件提供" |
-| Windows 服务与 systemd 未在本次环境实测 | 本机未注册服务(状态报已注册为假、运行中为假),Windows 服务真注册与 Linux 单元真装未取证 | 用 --dry-run 预览、用免权限守护自证;具备管理员或 root 权限时执行注册并复核状态 |
-
-### 17.2 口径冲突与待统一项
-
-系统里仍有多处文档或代码口径不一致,如实列出如表 17-2 所示。这些不是"功能坏了",而是同一件事有两种写法,读者与维护方必须知道以哪一处为准。
-
-| 冲突项 | 两种口径 | 以哪一处为准 |
-|---|---|---|
-| 重算链步数 | 系统设计说明写 14 步、重算操作手册写 8 步、移植文档写 12 步,实测默认 26 步(加 --skip-scada 为 23 步) | 以 scripts/rebuild_all.py --dry-run 的实跑输出为准 |
-| 窄仓列数 | 文档写 46 列(温度均值 27 列),实测 48 列(温度均值 25 列,另补两列控制通道) | 以脚本 --check 输出与 _manifest.json 的 columns_n 为准 |
-| 窄仓一次扫耗时 | 一处写约 2 分钟,一处写约 3 分钟 | 以现场实测为准,两处都保留为口径区间 |
-| 变桨面耗时 | 一处写约 10 分钟,手册写约 12 分钟 | 同上 |
-| 振动摄入耗时 | 一处写约 14 分钟,放置指导写约 25 分钟(对应 153 GB 导出) | 同上(体量不同) |
-| M9 按时间窗耗时 | 服务注释约 5 秒每时间窗,版本记录约 0.6 秒每时间窗 | 以本机实测为准;两处已记入待统一 |
-| 平均无故障间隔(MTBF)台时口径 | 停机归因模块已改为实测台时,部件可靠性表仍用理想日历台时 | 需统一到实测台时(否则同一指标两个值) |
-| 网关离线守卫端口 | 守卫里硬编码判断旧端口 8033,而端口表里工作台是 18033 | 以 configs/serve.json 为准,守卫待同步 |
-| 前端一处外链端口 | 工作台一处外链硬编码 8030,该端口不在端口表内 | 以 configs/serve.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 为准(它同时是升档解析的第一顺位),服务端展示表待同步 |
-| 审计流水的字段清理 | 重试成功的那条记录里仍带着上一次尝试的失败原因 | 以字段语义为准(成功行不应带错误文本),生成端待修正 |
-| 检索索引文件形态 | 一处注释写索引落 json 与 npz 两种,而代码只写 json 与向量文件 npy,盘上也没有 npz | 以代码与盘上实物为准,注释待改 |
-| 本体对象库规模 | 设计文档写 9,613 个对象(2026-09-17 时点),盘上实测 9,791 个(2026-09-21 修改时间) | 引用规模时须注明口径日期,或重测 |
-| 安装说明的口径 | 说明书里仍写"所有服务只监听本机",与后来"门户监听所有网卡"的口径不一致;同一份说明书一处的"不含重算链"与另一处的"重算链已进包"互相矛盾 | 以 configs/serve.json 与 scripts/rebuild_all.py 的现状为准,说明书待修订 |
-
-### 17.3 未实现项
-
-以下三项是"设计上这样定,但功能未实现",如实列出:按所选时间窗的缓存没有过期时间与容量上限(进程内常驻、无淘汰、无持久化),长时间运行且频繁换时间窗时内存会持续增长;后台预热不覆盖 M9(只预热判级与默认时间窗的镜头);时间窗词表没有"今日、近 7 日、本月、本年"这类词(只有六个预设加单月、月区间、自定义起止)。此外判级矩阵本身只在内存里返回,不落 parquet 产物,因此"某一时刻的判级矩阵"没有独立留档件,只能靠当时的页面快照与日志。
-
-***
-
-## 附录 A 编写依据
-
-本文每章的主要来源文件如表 A-1 所示(章节与来源文件)。
-
-| 章节 | 来源文件 |
-|---|---|
-| 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(路径约定与统一记录)、本次目录实测 |
-| 5 数据接入与重算链 | scripts/rebuild_all.py(--dry-run 实跑)、scripts/scada_slim_build.py 与 slim10min/_manifest.json、scripts/raw_scan.py、scripts/raw_data_check.py、scripts/place_raw_data.py、docs/输入数据放置指导_v0.1.md、src/windscada/scada_source.py、scripts/csv_to_mdb.py |
-| 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/<场>/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、《使用说明书》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 所示。
-
-| 仓库相对路径 | 职责 |
-|---|---|
-| guanlan.py | 启动器与自检:check 与 serve 与 status 与 stop 与 open 与 qa 与 version 七个子命令 |
-| src/paths.py | 路径唯一真源:安装根解析、各产物仓与配置与解释器助手;场名解析与按场落点(farm 与 store 与 contract) |
-| src/windscada/config.py | 场抽象层:场定义加载与必填键校验、场站目录扫描辨识与别名匹配、可用场与场切换 |
-| src/version.py | 版本唯一真源:VERSION 与版本规则与 HISTORY 与包名与安装记录读写 |
-| src/proc.py | 子进程统一口径:无窗口标志、后台起进程、前台短命令 |
-| src/logfile.py | 日志唯一口径:目录与命名与行格式与保留策略与标准输出包装 |
-| src/entry_refs.py | 入口引用闭合与入口脚本编码守则 |
-| src/derived_manifest.py | 由构建脚本自登记的派生产物清单 |
-| scripts/rebuild_all.py | 重算链编排(默认 26 步)与逐步退出码容忍 |
-| scripts/rebuild_from_raw.py | 三门台账与 SCADA 侧 10 个构建器的入口 |
-| scripts/scada_slim_build.py | 10min 公共列子集窄仓(按所选时间窗重算的底座) |
-| scripts/windscada_monthly_build.py | 月度派生件(与随包基线逐值对齐后才落盘) |
-| scripts/vib_raw_build.py | 振动侧一键摄入:时间窗索引与谱库与报告与逐台页与融合标量 |
-| scripts/pitch_face_build.py | 变桨面生成端(日粒度液压润滑柱塞与零位三口径月表) |
-| scripts/component_history_build.py 与 baseline_38_build.py | 振动在升与换件闭环、三层基线 |
-| scripts/sop_findings_from_ledger.py | 由重算台账生成事实契约 claim 族 |
-| scripts/guanlan_facts_contract.py | 事实契约构建与四类消费者派生件 |
-| scripts/products_restore_missing.py | 逐件来源台账维护(只记账不搬件) |
-| scripts/products_reverse_audit.py | 反向呼应审计与可逆性矩阵 |
-| scripts/pages_audit.py 与 configs/portal_pages.yaml | 页面归口登记与五条机器规则 |
-| scripts/config_audit.py 与 configs/registry.yaml | 配置登记与八条机器规则 |
-| scripts/raw_scan.py 与 raw_data_check.py 与 place_raw_data.py | 输入数据指纹扫描、放置体检与增量落位 |
-| scripts/windscada_serve.py | 工作台服务:路由族、按所选时间窗重算、pending 契约、页面烘焙调用 |
-| scripts/guanlan_gateway.py | 门户网关:前缀改写、data-abs 豁免、只读发布层、运维控制台 |
-| scripts/guanlan_ops.py 与 _ops_launch.py 与 _ops_run.py | 运维控制台后端、动作启动器与重算执行器 |
-| scripts/windcms.py 与 src/windcms | 振动诊断服务与报告、逐台页、工作台 |
-| src/windscada/taxonomy.py | 判级矩阵与系统归口与取严合并 |
-| src/windscada/perf 与 subsys | 曲线、控制、可靠性、停机、可用率、变桨、偏航、蓄能、温度、热链、结构、融合各面算法 |
-| src/ontology | 本体摄入六步、对象库、检索、MCP 只读工具、离线问答代理与校闸 |
-| install.ps1 与 install.sh 与 uninstall.bat 与 uninstall.sh | 安装与卸载入口 |
-| scripts/service_ctl.py 与 win_service.py 与 service_main.py 与 service_worker.py | 服务化注册器与服务体 |
-| scripts/pack_dist.py 与 pack.bat 与 pack.sh | 打包器、预演与开箱验证入口 |
-| scripts/delivery_docs_build.py 与 delivery_docs_figures.py | 交付文档渲染器与图表构建器(本版新增) |
-
-## 附录 C 术语与缩写
-
-本文使用的术语与缩写如表 C-1 所示。
-
-| 术语或缩写 | 含义 |
-|---|---|
-| 观澜 | 本系统的产品名(观澜 v2 风电场智能分析系统);样板指以样本风电场为依据的第一套落地实例 |
-| 产物 | 由原始件算出来、供页面与接口消费的件(parquet、json、md、html 等),只落在 outputs/场站/ 下 |
-| 产物仓 | outputs/场站/ 下的九个顶层目录(windscada、ontology、windcms、m5_cms_tcm、tcm_compatible_replay、sop、guanlan、pitch、paradigm_r1) |
-| 原始件 | 现场交付的输入数据,只读,落在 data/raw/场站/ 下 |
-| 重算链 | scripts/rebuild_all.py 串起来的构建步骤序列(默认 26 步) |
-| 窄仓 | 10min 公共列子集的逐台 parquet 仓,用于把按所选时间窗重算从分钟级降到秒级 |
-| 判级矩阵 | 逐台乘各系统的判级表,由各面判级取严合并得到 |
-| 四维证据 | 变桨与偏航等面内部用于取严合并的四个判据维度 |
-| 融合面 | 把振动、温度、润滑、油液四源对齐后逐台判级的分析面 |
-| 残月 | 样本覆盖度低于 0.5 的月份(如只有 6 天数据的月份),页面与报告均如实标注 |
-| 平均无故障间隔(MTBF) | 日历或实测台时扣除停机后除以事件数得到的平均无故障间隔 |
-| 平均停机间隔(MTBO) | 进一步扣除调度令停机后除以事件数得到的平均停机间隔 |
-| 单次停机时长(MDT) | 停机段时长的平均值 |
-| 时间窗 | 页面顶部可选的数据范围(预设段、单月、月区间、自定义起止日期,含两端) |
-| pending 契约 | 按所选时间窗重算未完成时,先回上一份口径并用 pending 标记明示,页面轮询再取的约定 |
-| 单一真源 | 同一事实只允许一处权威定义(路径、版本、端口、页面归口、配置登记) |
-| 质量门 | 重算链与自检里的审计检查点,每处都能独立重跑并给出退出码 |
-| 开箱验证 | 打包器对交付包做解压、安装、无窗口启动、页面可用、卸载的整链核验 |
-| 骨架 | 交付包里只带目录结构不带文件的输入数据目录骨架 |
-| MCP | 本体层的只读工具接口,供离线模型按纯函数取证 |
-| claim | 事实契约里的一条结论,带来源引用、本条摘要、时间窗与裁决 |
-| 校闸 | 问答结果的接地核查,不过闸不出正文 |
-| 升档 | 默认模型被校闸拦下后改用更强档位重答一次 |
-| 中间件 | SOP 中间件与评审落盘件(outputs/场站/sop) |
-| 冻结交付件 | 按交付版本发布、不随输入数据自动变的客户交付件 |
-| 场定义 | configs/farms/<场>.yaml,描述一个场的机组清单、源类目录与产物落点的配置对象(七个必填键) |
-| 场抽象层 | 把"场"做成可加载、可校验、可切换的配置对象的机制:配置层加路径层加语义层 |
-| 场无关引擎 | 换场不需要改动的部分:判级与算法结构、融合纪律、本体与检索、服务与前端、审计门 |
-| 场相关参数 | 换场必须重新给或重新标定的部分:阈值、机型参数、时间窗锚点、机组清单、术语库与代码表、坐标件 |
-| 换场作业单 | 从准备场配置到页面与门禁验收的逐步命令清单,每步带命令与退出码语义 |
-| 场站目录辨识 | 用 raw_station 与 src_farm_names 把 data/raw 下的目录认成本场的四段式判定(raw_station 与 alias 与 single 与 none) |
+# 设计说明 · 观澜 v2 风电场智能分析系统 · 版本 2.10.2
+
+## 1 文档说明
+
+### 1.1 目的与范围
+
+本文是"观澜 v2(风电场智能分析系统)"(海上风电场智能分析离线系统)的设计说明(下称"本系统"),面向版本 2.10.2;内容以样本风电场(下称"本场")为依据,样本场实测日期为 2026-09-22,说明本系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
+
+本文覆盖十五个设计面:总体架构与分层、目录结构与路径真源、数据接入与重算链、判级与算法、时间窗口径、服务与前端、本体与知识层、本机模型接入、运维控制台与重算编排、安装与服务化与版本管理、质量保证、安全与离线边界、可移植性与资源占用、多场适用性与换场迁移、已知边界与未实现。
+
+本文不重复需求条目本身(那是《需求分析_观澜_2.10.2.docx》的职责),也不重复操作步骤的逐步手册(那是 docs/重算操作手册_v0.1.md 与《使用说明书》v0.2(随包 docs/)的职责);本文只回答"设计上为什么这样、落在哪个文件的哪一处、用什么机器守卫保证它不漂移"。
+
+### 1.2 读者与用法
+
+现场运维与检修人员可看第 3 章、第 7 章、第 8 章、第 11 章,了解自己能点到的页面背后读的是什么口径;场站管理人员与换场交付人员可看第 6 章、第 7 章、第 16 章、第 17 章,了解判级与可靠性指标的口径与边界,以及换场时要重新标定哪些参数;研发与交付人员应通读全文,重点是第 4 章、第 5 章、第 12 章、第 13 章;验收方可直接按第 13 章的质量门与附录 A 的编写依据逐条复核。
+
+### 1.3 与《需求分析_观澜_2.10.2.docx》的对应关系
+
+需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示;与《数据要求说明_观澜_2.10.2.docx》的数据侧口径去向见 16.6 节。
+
+| 本文章节 | 需求分析对应章 | 对应关系说明 |
+|---|---|---|
+| 2 设计目标与原则 | 2 设计目标与约束 | 需求给目标,本文给落地机制与机器守卫 |
+| 3 总体架构 | 4 系统架构需求 | 需求要分层与统一入口,本文给分层图与组件端口表 |
+| 4 目录结构与路径真源 | 6 非功能需求(可移植性) | 需求要换机可用,本文给路径唯一真源与助手表 |
+| 5 数据接入与重算链 | 3 数据与功能需求 | 需求列功能,本文给逐步重算链与退出码容忍 |
+| 6 判级与算法 | 3 数据与功能需求(判级部分) | 需求要"带证据的判级",本文给判据、阈值与产物 |
+| 7 时间窗口径 | 3 数据与功能需求(时间窗) | 需求要随所选时间窗变化,本文给词表、缓存与 pending 契约 |
+| 8 服务与前端 | 5 页面与信息架构 | 需求给页面清单,本文给路由族与链接契约 |
+| 9 本体与知识层 | 3 数据与功能需求(知识与问答) | 需求要机制链与决策台,本文给六步摄入与只读工具表 |
+| 10 本机模型接入 | 6 非功能需求(离线模型) | 需求要离线问答,本文给档位、校闸、升档与审计 |
+| 11 运维控制台与重算编排 | 5 页面与信息架构(数据重算) | 需求要一个按钮一件事,本文给动作、状态文件与冲突码 |
+| 12 安装服务化与版本管理 | 7 交付与安装需求 | 需求要可安装可卸载可升级,本文给步骤与三重守卫 |
+| 13 质量保证 | 8 验收门 | 需求给验收门,本文给审计器矩阵与退出码语义 |
+| 14 安全合规与离线边界 | 6 非功能需求(安全与离线) | 需求要无外网与脱敏,本文给边界与降级路径 |
+| 15 可移植性与资源占用 | 6 非功能需求(可移植) | 需求要零配置启动,本文给离线件与实测体积 |
+| 16 多场适用性与换场迁移 | 11 多场适用性与换场迁移 | 需求要按场配置化并可换场交付,本文给场抽象层、场无关与场相关分层、换场作业单与扩展点 |
+| 17 已知边界与未实现 | 8 验收门(遗留项) | 需求要求如实留白,本文逐条给影响与处置建议 |
+
+### 1.4 口径与依据
+
+本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 17 章汇总。本文中"本次实测"与各表"实测"列一律指样本场实测(2026-09-22):即在样本风电场的一套实例上的一次实跑,换场后这些数字会变、方法与口径不变。版本号的唯一真源是 src/version.py 的 VERSION 常量,本版为 2.10.2;打包文件名由 src/version.py 的 package_name() 给出,为 app_guanlang_v2.10.2.zip。
+
+本文遵循四条写作口径:含"窗"且确实指时间窗口的,一律写全"时间窗"(天气窗、作业窗、预览窗、观测窗属领域词,保持原样);影响的是风电机组时写"影响机组"或"影响机组数";使用英文简写时必须写成"中文(英文简写)"形式,如平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);全文用简体中文与半角数字与单位。
+
+本文用到的取证命令与样本场实测(2026-09-22)的输出要点如表 1-2 所示,全部为秒级只读或自检命令(重算与构建类耗时命令未执行)。
+
+| 命令 | 用途 | 本次输出要点 |
+|---|---|---|
+| python scripts/rebuild_all.py --dry-run | 取重算链实际步骤 | 输出"重算全部 · 26 步";加 --skip-scada 为 23 步 |
+| python guanlan.py check | 装机自检(40 余行结论) | 结论"全绿, 可 serve";底稿落在 docs/src/_guanlan_check.txt |
+| python scripts/products_reverse_audit.py --check | 反向呼应审计 | 呼应成立 3,548 件、不成立 0 件、未归类 0 件、rc=0 |
+| python scripts/pages_audit.py --check | 页面归口审计 | 2 项一致、19 项已知缺口、0 项要处理、rc=0 |
+| python scripts/config_audit.py | 配置统一审计 | 171 件配置、5 个域、0 不一致、14 已知缺口、rc=0 |
+| python scripts/raw_scan.py --check | 输入数据指纹比对 | "与上次快照一致,没有新数据"、rc=0 |
+| python scripts/log_audit.py | 日志口径审计 | 0 不一致、87 条提示、rc=0 |
+| python scripts/chain_gap_check.py | 振动六层链完整性 | 四步脚本齐全且已落地、rc=0 |
+| python scripts/version_log.py --check | 版本记录一致性 | 版本记录与代码一致、rc=0 |
+| python scripts/service_ctl.py status | 服务化状态 | 本机"已注册=False 运行中=False",服务名为 guanlan |
+
+***
+
+## 2 设计目标与原则
+
+### 2.1 六条设计原则
+
+**离线单包**:整套系统在一台不接外网的机器上可安装、可运行、可重算、可出报告。程序与依赖随包(wheels/win_amd64 42 件、vendor/python 三平台便携运行时),模型为可选项(未装即降级并如实标注)。落地证据:install.ps1 第 3 步"装依赖(优先包内离线轮子 wheels\win_amd64)",包内无轮子才改为联网安装。
+
+**单一真源**:路径真源是 src/paths.py,版本真源是 src/version.py 的 VERSION,端口与路径真源是 configs/serve.json,页面归口真源是 configs/portal_pages.yaml,配置登记真源是 configs/registry.yaml。落地证据:src/version.py 注释写明"改版本只改这里",打包器 scripts/pack_dist.py 不再自带版本常量而改为读 VERSION。
+
+**产物自算**:页面与接口要用到的每一件数据,都应由包内源代码从 data/raw 算出来,而不是靠随包快照顶上。落地证据:重算链的 ④b 会生成振动时间窗索引与谱库、④c 生成变桨面、④d 生成振动在升与换件闭环、④e 生成三层基线、⑤b 由重算台账生成事实契约的 claim、⑦b 自算烘焙总览页;反向呼应审计当前 rc=0,3,548 件产物全部能指到生成端与输入。
+
+**口径先于结论**:判据写在代码里、模型只做转述;相对判据封顶"候选";不同观测面不因为数量多就当作多源印证;外部原因造成的结果不写成设备不可靠。落地证据:src/ontology/mcp_server.py 头部写明"全部工具只读(L1)——模型零写权;判级零生成——只转述对象里的 verdict 原文与审级";src/windscada/subsys/fusion.py 明确禁止按源数加权;src/windscada/perf/reliability.py 把运维操作、风况(外部)、电网/供电(外部)单列为外部类并写明"外部类不计设备可靠性"。
+
+**缺件如实**:缺数据、缺配置、缺模型时,页面与日志要明确写出缺哪一件、属于哪条产物线、怎么补,不允许静默降级或伪造内容。落地证据:工作台与 CMS 在无产物时返回 HTTP 200 的结构化缺件页(而非裸 404 或直接掐断连接);src/windscada/subsys/temp_nbm.py 在样本不足时判"不可判(样本不足/返服嫌疑)"而不是判正常。
+
+**可审计**:每一条结论都能回抓到源记录,每一件产物都能说明谁生成、谁消费、来源是原始件重算还是随包补齐。落地证据:outputs/场站/_provenance.json 逐件记来源(raw-derived 或 shipped),outputs/场站/_derived_manifest.json 由构建脚本自登记,模型调用逐条写 logs/audit/llm_audit.jsonl。
+
+### 2.2 原则与机器守卫的对应关系
+
+原则不靠人记,靠检查器与退出码守住。对应关系如表 2-1 所示。
+
+| 设计原则 | 落地机制 | 守卫或检查器 | 违反时的表现 |
+|---|---|---|---|
+| 离线单包 | 包内轮子与便携运行时、可选模型 | install.ps1 第 3 步、guanlan.py check 依赖项 | 依赖安装失败即 exit 3,不继续启动 |
+| 单一真源 | paths.py、version.py、serve.json、registry.yaml | config_audit.py R1 至 R8、version_log.py --check | 手拼配置路径或版本不一致即报错(rc=6) |
+| 产物自算 | 构建器 + 自登记清单 | products_reverse_audit.py --check | 有件无生成端或无输入即报 rc=5 |
+| 口径先于结论 | 判据在 taxonomy 与 perf 与 subsys 内 | 反向可逆性矩阵、llm 校闸(校闸未过不出文) | 引用不到契约条目时明写"未被契约背书" |
+| 缺件如实 | 结构化缺件页、缺件说明字段 | pages_audit.py、guanlan.py check | 缺件被登记为已知缺口而不是装作通过 |
+| 可审计 | _provenance.json、llm_audit.jsonl | products_reverse_audit.py、log_audit.py L1 至 L5 | 台账查不到来路即报"溯源缺失"(rc=6) |
+
+### 2.3 关键设计取舍
+
+**判断在代码、模型只转述**:把判级放进模型会让同一份数据在不同机器上给出不同结论,也会让结论无法回溯到证据。因此模型只在问答、解释、引用三个位置出现,且必须过接地闸:src/ontology/mcp_server.py 的 claim_check 工具要求"最终回答前必须调用;台号与数字须来自本会话工具事实,越界即拒"。
+
+**慢就慢在算,不慢在等**:按所选时间窗重算一次判级矩阵约 25 秒、七镜头约 9 至 44 秒每时间窗。若同步等待,HTTP 请求会挂几十秒(对外访问还要过网关)。因此统一为"后台算加进程内缓存,未命中先回上一份口径并明确标注 pending",而不是静默等待或静默给旧口径。
+
+**缺件不静默降级**:缺 handoff 正本时由观澜自算件顶上并在 meta.from 标明"观澜自算";缺振动时间窗索引时按缺件如实返回空表并写明数据边界;缺模型时页面提示"本机模型未启动"(HTTP 503),绝不改用别的来源。
+
+**运行期不从交付包补齐**:2026-09-17 的口径是"运行链只报账、不搬运"——缺件要么放原始件后重算,要么由研发补生成端。交付包默认不含产物,因此"旁边有个含产物的压缩包"不能成为隐式依赖。
+
+***
+
+## 3 总体架构
+
+### 3.1 分层结构
+
+系统分为输入层、摄入构建层、产物层、服务层、前端层,另有贯穿的本体与模型层和两套贯穿全链的守卫(单一真源与审计门),如图 2-1 所示。
+
+![图 2-1 观澜总体架构](figures/fig-des-01-总体架构.png)
+
+输入层是 data/raw/场站名称/ 下的七类约定目录(scada_10min、scada_1min、scada_mdb、故障报警、风机故障记录、油样报告、windcms,另有 m5_cms_tcm 与场站并列的共享技术资料目录),原始件只读、不随包分发。
+
+摄入构建层是 scripts/rebuild_all.py 串起来的重算链(默认 26 步),把原始件算成产物,并把每一步的日志、退出码、耗时写进日志目录与任务状态文件。
+
+产物层是 outputs/场站/ 下的九个产物仓(windscada、ontology、windcms、m5_cms_tcm、tcm_compatible_replay、sop、guanlan、pitch、paradigm_r1),另有 report 与 guanlan/cloud 作为报告交付与可上云面孔的落点。
+
+服务层是六个常驻进程加一个可选的本机模型服务:网关 28084、分析工作台 18033、振动诊断 18020、仿真与回放 18791、仿真合页 18792、三维工作台 64292,以及可选的 Ollama 11434。
+
+前端层是门户(release/portal.html)、工作台单页(/detail/v2)、CMS 页面、仿真页与三维页,全部通过网关的同一前缀与同一入口访问。
+
+### 3.2 组件清单
+
+组件、端口、入口与职责如表 3-1 所示;端口取自 configs/serve.json(第 4 至 10 行),入口脚本取自 guanlan.py 的组件启动表。
+
+| 组件 | 端口 | 入口 | 职责 |
+|---|---|---|---|
+| 门户网关 | 28084 | scripts/guanlan_gateway.py | 统一入口;前缀改写;data-abs 豁免;/ops 与 /release 只读暴露;门户静态件按 mtime 缓存 |
+| 综合详细分析工作台 | 18033 | scripts/windscada_serve.py | 判级矩阵、部件问题、该问题页、发电性能、可靠性、本体页、问答、报告导出 |
+| CMS 振动诊断 | 18020 | scripts/windcms.py serve | 六层振动模型逐台判读、报告与逐台页、谱图与标量 |
+| 仿真与回放 | 18791 | scripts/static_server.py | 静态服务 release 与 resources 下的仿真页 |
+| 仿真·四系统合页 | 18792 | release/sim_sys_server.py | 从资料包 zip 现读四系统合页与 5 页资料 |
+| 三维拆装工作台 | 64292 | scripts/static_server.py | 三维资产页(release/viewer) |
+| 本机模型(可选) | 11434 | Ollama 本体进程 | 只服务问答、解释与初筛;未装即降级 |
+
+三处部署形态说明:组件(分析、振动、仿真、三维)出厂只绑 127.0.0.1(configs/serve.json 的 host),只有门户网关使用 public_host(出厂 0.0.0.0);因此对外只有 28084 一个入口,且页面没有鉴权,配置注释明确提示"对外监听请同时限来源(防火墙白名单或反代认证),别直接挂公网"。本次实测监听状态与之一致:网关在所有网卡的 28084 上监听,而工作台 18033、振动 18020、仿真 18791、四系统合页 18792、三维 64292 与本机模型 11434 都只在 127.0.0.1 上监听。
+
+### 3.3 端口与进程拓扑
+
+端口与进程拓扑如图 3-2 所示。网关按前缀把请求转给内部组件,组件端口可被 configs/serve.json 的同名键覆盖(scripts/guanlan_gateway.py 的上游端口表 _DEFAULT_ROUTES 与 _upstream_ports())。
+
+![图 3-2 端口与进程拓扑(依据 configs/serve.json)](figures/fig-des-02-端口与进程.png)
+
+网关的前缀路由表(依据 scripts/guanlan_gateway.py 第 31 至 38 行)为:/detail 转 18033 默认路径 /v2;/cms 转 18020 默认路径 /?theme=light;/sim/sys 转 18792 默认路径 /0_四系统合页.html;/sim 转 18791 默认路径 /sc1_sim_demo.html;/viewer 转 64292 默认路径 /unit-workbench.html;/local-ai 转 11434 探活路径 /api/tags。另有 /healthz、/api/version、/ops、/release/ 与门户根路径由网关自己处理。
+
+运行态实测(run/pids.json,本机一次运行中)如表 3-2 所示。该文件是"当前跑着谁"的唯一凭据,服务停止时删除;它换一台机器就是无效引用,因此交付包默认排除 run 目录。
+
+| 组件键 | 端口 | 进程号(实测) | 说明 |
+|---|---|---|---|
+| gateway | 28084 | 16448 | 对外唯一入口 |
+| detail | 18033 | 35436 | 工作台 |
+| cms | 18020 | 56720 | 振动诊断 |
+| sim | 18791 | 21780 | 仿真与回放 |
+| sim_sys | 18792 | 25936 | 四系统合页 |
+| viewer | 64292 | 58488 | 三维工作台 |
+
+### 3.4 产物仓的生成端与消费端
+
+九个产物仓的生成端与消费端如表 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 | 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 | 变桨面判级、零位卡片 |
+| guanlan | 5 | 0.0 MB | scripts/guanlan_facts_contract.py | 门户结论段、/api/facts、报告摘要 |
+| sop | 1 | 0.0 MB | scripts/sop_findings_from_ledger.py | 事实契约输入 |
+| paradigm_r1 | 3 | 0.0 MB | scripts/sop_findings_from_ledger.py | 事实契约输入 |
+| tcm_compatible_replay | 2 | 0.6 MB | scripts/tcm_mask_thresholds_build.py | 报告构建器、掩码阈值 |
+
+***
+
+## 4 目录结构与路径真源
+
+### 4.1 路径唯一真源与助手表
+
+src/paths.py 是全部路径的唯一真源。硬约束有三条:代码与配置里只写相对路径(相对安装根),不写机器相关绝对路径;运行时由该模块解析成绝对路径,解析基准是安装根而不是当前工作目录;需要写进产物与清单的路径字符串用 POSIX 相对形式(rel()),只在给人看的显示场景用本机分隔符(disp() 与 disp_dir())。安装根由环境变量 WINDSCADA_ROOT 决定,未设置时按 src/paths.py 的位置回溯上一级,因此整个安装目录可以整体拷到别的电脑或别的盘而不改任何路径。
+
+路径助手的功能如表 4-1 所示。
+
+| 助手 | 返回 | 用途 |
+|---|---|---|
+| P.ROOT | 安装根 | 一切解析的基准(WINDSCADA_ROOT 可覆盖) |
+| 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 标准仓,页面主取数处 |
+| P.ont(name) 与 P.objects_json(name) | outputs/场站/ontology 与其 objects.json | 本体对象库 |
+| P.cms(name) | outputs/场站/windcms | CMS 振动诊断产物 |
+| P.m5(name) | outputs/场站/m5_cms_tcm | 振动线出件、时间窗索引、谱库 |
+| P.tcm_replay(name) | outputs/场站/tcm_compatible_replay | TCM 兼容链回放资产 |
+| P.sop(name) | outputs/场站/sop | SOP 中间件与评审落盘 |
+| P.guanlan(name) 与 P.cloud(name) | outputs/场站/guanlan 与其 cloud | 事实契约与可上云面孔 |
+| P.pitch(name) | outputs/场站/pitch | 变桨侧派生件 |
+| P.paradigm(name) | outputs/场站/paradigm_r1 | 范式实验件(E3/E5/E8 底稿) |
+| P.report_dir(name) | outputs/场站/report | 报告交付件 |
+| P.contract(name) | reference/场站/windscada_contract.yaml | 机型判据契约(属 reference 侧) |
+| P.config() 与 P.config_dir() 与 P.farm_config() | configs 下的配置路径 | 配置的唯一取用口 |
+| P.rel(p) 与 P.disp(p) 与 P.disp_dir(p) | 相对 POSIX 串与显示串 | 写进产物用 rel(),给人看用 disp() |
+| P.resolve(p) | 绝对路径 | 把可能是相对的值按安装根解析 |
+| P.venv_python() 与 P.python_exe() | 解释器路径 | 跨平台探测 .venv/Scripts/python.exe 或 .venv/bin/python |
+
+### 4.2 安装根逐目录说明
+
+安装根下的目录分工如表 4-2 所示,体积与件数为本次递归实测值。
+
+| 目录 | 件数(实测) | 体积(实测) | 放什么 |
+|---|---|---|---|
+| 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/<机型>技术资料),只读输入 |
+| outputs | 3,549 | 7,157.5 MB | 产物仓,只由构建器写 |
+| 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 | 交付文档与设计正本(含本文) |
+| logs | 155 | 3.3 MB | 长驻服务日志、运维动作日志、构建日志、审计流水、历史留档 |
+| run | 2 | 0.0 MB | 运行态:pids.json 与 ops_job.json |
+| .venv | 17,779 | 688.1 MB | 虚拟环境(换机必失效,安装时重建) |
+
+### 4.3 配置、日志、运行态与交付件的落点
+
+四类运行期文件的落点与命名是唯一口径,如表 4-3 所示。配置只允许经 src/paths.py 的 config() 与 config_dir() 取路径,不许手拼 configs 字符串(config_audit.py 的 R6 规则专门查这一条)。
+
+| 类别 | 落点 | 命名或字段 | 谁读 |
+|---|---|---|---|
+| 运行期单件配置 | configs/serve.json | host、public_host、gateway、detail、cms、sim、sim_sys、viewer、ollama、release_dir、viewer_dir、sim_dir、python、raw_watch、raw_dir | guanlan.py、网关、运维控制台、指纹、移植检查 |
+| 运行期单件配置 | configs/models.json | runtime、endpoint、tiers、profiles、pull_commands | guanlan.py check、src/ontology/llm_gate.py、fast_agent.py |
+| 运行期单件配置 | configs/portal_pages.yaml | 25 条主条目与 7 条子条目的 id 与 kind | scripts/pages_audit.py |
+| 配置登记表 | configs/registry.yaml | top_level、domains、known_missing | scripts/config_audit.py |
+| 长驻服务日志 | logs 组件名.log | gateway、detail、cms、sim、sim_sys、viewer、serve、start_hidden、service | 现场排障、log_audit.py |
+| 运维动作日志 | logs/ops/ | ops_动作_YYYYmmdd-HHMMSS.log,保留最近 20 份或 30 天 | 运维控制台页面、现场排障 |
+| 构建日志 | logs/build/场站/原相对路径 | 沿用原相对路径 | 构建排障 |
+| 审计流水 | logs/audit/ | llm_audit.jsonl、cloud_qa.jsonl、terms_audit.json | 审计与复核 |
+| 运行态 | run/ | pids.json(组件到进程号与端口)、ops_job.json(当前任务) | guanlan.py stop、运维控制台 |
+| 交付件 | release/ 与 outputs/场站/report 与 outputs/场站/guanlan/cloud | 门户、治理清单、报告交付件、可上云面孔 | 门户、网关 /release 只读暴露 |
+
+日志行格式统一为"时间戳 级别 组件 消息"(级别取值 DEBUG、INFO、WARN、ERROR),UTF-8 无 BOM、行尾 LF、无 ANSI 颜色码。六个服务不需要各自改写打印语句,入口处调用一次 src/logfile.py 的 prefix_stdout() 即可让每一行自动带前缀。
+
+### 4.4 已统一的路径问题(选列)
+
+2026-09-16 曾集中统一十处路径问题,选列五处如表 4-4 所示,用以说明"为什么必须只有一个真源"。
+
+| 位置 | 原样 | 改成 | 为什么要改 |
+|---|---|---|---|
+| 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 键 | 多场部署下跨场串数据且不报错 |
+| src/windcms/pipeline.py 的 ingest | 任何压缩包都调未随包的脚本 | 先判断包内容:含解码导出则按已解码路线走,否则报明确错误并给两条可行路径 | 把 CMS 导出包直接指过来时原本必然崩,而其中一条路径并不需要那个脚本 |
+
+***
+
+## 5 数据接入与重算链设计
+
+### 5.1 输入数据族与落位约定
+
+输入层是 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 | 平铺,名字是内部台号(样本场形如 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 MW 级海上机组;主机制造商(OEM,名称从略)与机型代号从略;与场站并列) | 318 | 2,800.7 MB | 技术资料与四个台账 | ⑦ 本体六步 |
+
+### 5.2 重算链逐步说明
+
+重算链由 scripts/rebuild_all.py 写死顺序、依赖与退出码容忍。默认口径(不含 --src、不含 --with-verify)实跑为 26 步;加 --skip-scada 为 23 步(少了 SCADA 侧的 ③、③b、④c 三步);加 --src 为 27 步;再加 --with-verify 为 28 步。重算链的步骤关系如图 5-1 所示,该图按 --skip-scada 口径绘制,因此图中的步骤数为 23 步。
+
+![图 5-1 重算链(依据 scripts/rebuild_all.py --dry-run)](figures/fig-des-03-重算链.png)
+
+逐步说明如表 5-2 所示(步号顺序即执行顺序;⑦ 的六个子步合并为一行列出)。
+
+| 步号 | 脚本 | 输入 | 输出 | 退出码容忍 |
+|---|---|---|---|---|
+| ① 放数据(仅 --src 时) | scripts/place_raw_data.py --src 包目录 --scope full | 现场包 | data/raw/场站/ | 0 |
+| ①b 输入数据扫描 | scripts/raw_scan.py --check --write | data/raw 逐族指纹 | outputs/场站/_raw_scan.json 快照 | 容忍 4 与 5(4 有新数据不是失败;5 首次无快照顺手记一份) |
+| ② 三门台账 | scripts/rebuild_from_raw.py | 故障报警、风机故障记录、油样报告 | alarms.parquet、workorders.parquet、oil_samples_index.parquet | 0 |
+| ③ SCADA 侧 10 个构建器 | scripts/rebuild_from_raw.py --scada | scada_10min 逐台约 14 GB | 温度、功率曲线、损失、控制、偏航、热链、停机事件等 parquet | 0,实测约 15 分钟 |
+| ③b SCADA 10min 窄仓 | scripts/scada_slim_build.py | 同上,一次全量 | windscada/slim10min/台号.parquet 与 _manifest.json | 0,实测一次扫约 2 分钟 |
+| ④ 月度派生件 | scripts/windscada_monthly_build.py | raw 与随包基线 | temp_monthly 等同族月表 | 容忍 5(找不到随包基线即跳过等价验收,不是通过) |
+| ④b 振动侧摄入与报告与标量 | scripts/vib_raw_build.py | windcms 解码导出 | 时间窗索引、谱库、CMS 报告与逐台页、融合 z | 0;现场无振动原始件时空跑退出 0 |
+| ④c 变桨面 | scripts/pitch_face_build.py | scada_10min 与 scada_1min | pitch/pitch_daily.parquet 与 pitch_zero_monthly.parquet | 容忍 4(缺 SCADA 原始件按缺件如实报) |
+| ④d 振动在升与换件闭环 | scripts/component_history_build.py | 振动时间窗索引 | m5_cms_tcm/component_history.json | 容忍 4 |
+| ④e 三层基线 | scripts/baseline_38_build.py | 振动时间窗索引 | m5_cms_tcm/baseline_38.json | 容忍 4 |
+| ⑤b 事实契约 claim 生成 | scripts/sop_findings_from_ledger.py | 重算台账 | sop/findings.json 与 paradigm_r1 三份底稿 | 0 |
+| ⑤c 事实契约构建与渲染 | scripts/guanlan_facts_contract.py build | findings 与底稿 | guanlan/facts_contract_v0.json 与 derived 四件 | 容忍 2(契约自检不过需人看) |
+| ⑤a 逐件来源台账 | scripts/products_restore_missing.py --refresh | 盘上产物与族表 | outputs/场站/_provenance.json | 0;只记账不搬件 |
+| ⑤ 反向呼应审计 | scripts/products_reverse_audit.py --check | 产物与生成端 | 审计清单(只报账) | 容忍 5 |
+| ⑥ 重启组件服务(停) | scripts/_ops_stop_keep_gateway.py | run/pids.json | 组件停、网关留 | 0 |
+| ⑥ 重启组件服务(起) | guanlan.py serve | configs/serve.json | run/pids.json 与六个服务 | 容忍 1(有模块 DOWN 属 degraded 正常) |
+| ⑦ 本体六步 | src.ontology.kb_ingest、populate、chain_ingest、trend_ingest、retrieval.build、maintenance.refresh_params | 上级产物与技术资料 | objects.json、检索索引、turbine_params.parquet | 0 |
+| ⑦b 全场状态总览页 | scripts/windscada_overview_build.py | 重算产物与 objects.json | windscada/index.html | 0,缺产物时响亮报错不产出半张页 |
+| ⑧ 本体审计 | src.ontology.audit | objects.json | 审计结论(期望 0 问题) | 0 |
+| ⑧b 重装门户 | scripts/portal_build.py | 契约产物 | release/portal.html | 容忍 1(缺契约产物时门户保留原样) |
+| ⑧c 页面归口审计 | scripts/pages_audit.py --check | 页面登记表与产物 | 陈旧检测结论 | 容忍 5 与 6 与 7 |
+| ⑧d 版本记录一致性 | scripts/version_log.py --check | 版本记录与 version.py 的 HISTORY | 一致性结论 | 容忍 6(属文档同步这一路,不打断整条链) |
+| ⑧ 台账等价验收(仅 --with-verify) | scripts/rebuild_from_raw.py --verify | 随包基线 | 差异分类 | 容忍 4 与 5(4 有需人工看的差异;5 没基线即没验收) |
+
+退出码容忍的语义汇总如表 5-3 所示。主循环的判定是"退出码为 0 或落在该步容忍集合内即视为通过",非容忍的非零退出立即中断整条链。
+
+| 退出码 | 语义 | 典型来源 |
+|---|---|---|
+| 0 | 通过 | 各步正常完成 |
+| 1 | 有模块未起(degraded 正常态)或门户缺契约产物 | ⑥ 重启服务、⑧b 重装门户 |
+| 2 | 契约自检不过(sha 或脱敏或枚举) | ⑤c |
+| 4 | 有新数据,或缺现场原始件而按缺件如实报 | ①b、④c、④d、④e、⑧台账等价验收 |
+| 5 | 随包件这一路缺失(无基线、有未归类、被引产物不在位) | ④、⑤、⑧c |
+| 6 | 溯源缺失,或版本记录与代码不一致 | ⑧c、⑧d |
+| 7 | 数据派生页面陈旧(内嵌快照指纹与当前产物不一致) | ⑧c |
+
+### 5.3 幂等与可重入
+
+链上各步都是普通命令行程序,幂等且可整条重跑:② 与 ③ 与 ④b 按"产物是当前原始件的函数"重建(源件删了行也随之消失并大声报出缺了哪件、少了多少行、涉及哪些月);③b 全量重写窄仓;⑤a 由盘上账目重算台账;⑦ 的六个子步幂等覆盖。重算可以在系统运行中做(门户的「数据重算」或运维控制台按钮),也可以在系统全停时做(先 guanlan.py stop,跑完再启动,页面直接读新产物)。
+
+为让"新数据必进重算"不靠人记,链上有两条设计:①b 步每次扫描 data/raw 的逐族指纹并把本次指纹记成新基线;rebuild_all.py --auto 会先扫一遍,发现新数据落在被 --skip-scada 或 --skip-vib 跳过的族里就自动取消跳过并打印原因(实测示例:scada_10min 新增 1 件时计划从 22 步变 24 步)。
+
+### 5.4 等价验收与基线
+
+④ 步与可选的 ⑧ 台账等价验收都以"随包基线"为标准答案,逐值比对随包件与自算件。基线目录是 outputs/场站/windscada/_pre_rebuild_20260911/,缺基线时 ④ 返回 5(跳过等价验收,不是通过);全新机器上交付包按口径不含产物,因此 ⑧ 在全新机器上必然返回 5,这与"重算失败"是两件事,链上如实标出退出码并写明"这不是通过"。要真做验收,用 scripts/set_baseline.py 把已核实的当前产物快照登记成基线,之后这一项才会真比对。
+
+### 5.5 raw_scan 指纹与增量
+
+逐族指纹的构成是:递归统计该族目录下全部文件(不限扩展名)的件数、体积、最新落盘时间,加上清单摘要与子目录清单;每件按"相对路径、字节数、微秒级修改时间"排序串联后取 sha1,默认不叠内容哈希(--deep 才对不大于 8 MiB 的件逐件加内容 sha1)。快照落在 outputs/场站/_raw_scan.json,退出码为 0 无变化、4 有新增或变化、5 还没有基线。指纹字段与含义如表 5-4 所示。
+
+| 字段 | 含义 |
+|---|---|
+| files、bytes、newest | 该族递归文件数、总字节、最新落盘时间 |
+| matched | 扩展名白名单命中的件数(差额单列为"信息,不计变化") |
+| dirs | 子目录清单(新建空目录也能被发现) |
+| unmatched、unmatched_n | 族表之外的件与件数(按"未归类、没有已知消费者"如实报出,不猜) |
+| consumed、consume_how | 被消费者读取的件数与消费者的真实取数口径 |
+| upstream、upstream_how | 上游归档件(要先转换才被消费)与其转换口径 |
+| gaps、gaps_n | 缺口级件(放了但没有任何消费者读) |
+| digest、top | 整族指纹与样例件 |
+
+"增量"在放置侧另有三清单口径(scripts/raw_data_check.py 与 scripts/place_raw_data.py):目标不存在为新增;目标存在且大小相同为"相同(跳过)"(源件是只读输入,同尺寸视为同一份);目标存在但大小不同为冲突,默认拒绝落位并列出,必须人确认后加 --force 才覆盖,体检器的退出码语义是 0 合规、5 结构或命名违例、6 增量冲突、7 缺源类或时间空洞、8 仅提示。
+
+### 5.6 slim10min 窄仓为什么存在
+
+按所选时间窗重算的真实卡点,是每换一个时间窗就要重读 15 GB 级的 10min CSV(实测约 2 分钟每面)。而各面真正需要的列只是公共子集。scripts/scada_slim_build.py 把这份公共列子集抽成逐台一份的窄仓,一次全量扫后,任何时间窗的重算都退化为"窄仓过滤加分组"的秒级操作。
+
+窄仓的实测口径是 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 类目库是上游归档
+
+现场会按"月份乘通道组"打包交付 SCADA 归档,例如某月目录下的通道组库,共 12 个类目(中文目录名与类目码对应关系是:风机数据对应 tur、温度数据对应 tmp、压力数据对应 prs、电网数据对应 grd、DigiIn 数据对应 din、DigiOut 数据对应 dot、计算数据对应 cnt、标志数据对应 flg、内部数据对应 int、统计数据对应 scd、标准数据对应 std、汇总数据对应 sum)。
+
+取数层不直接读类目库(它只认按月分片的主件与同台补充件),因此类目库是上游归档:扫描器按信息级列出、不计变化、不假装被摄入(这是 v2.8.2 的消缺点,修前 raw_scan --check 会永远返回 4)。要让它进产物,需按时间戳与机组标识把逐台类目表对齐合成同台 10min 补充件 scada_10min/台号-YYYYMM.csv(列与主件逐列同名同序、时间戳为 ISO 形式),之后 ③ 与 ④ 与 ④c 会自动纳入。实测 9 个逐台类目与主件的前缀块一一对应(tur 91、din 93、dot 69、cnt 69、tmp 116、prs 52、grd 50、flg 42、int 13,合计 595 通道,0 缺 0 多),精度与格式按 float32 加 ISO 对齐(实测重叠 19,555,865 格,两侧都有值却不相等的为 0 格)。
+
+SCADA 形态的接纳规则是"CSV 优先、该台没有 CSV 才回落 MDB",并把这次从哪种形态取的记在返回表的属性里供台账与页面说明来路,不静默换源。MDB 侧有两个硬限制:单表不超过 255 列,因此库内按 250 列拆表;2026-09-19 之前建的老库缺机组列,10min 老库还能用源件自带的机组列兜底,1min 老库救不了(源件没有机组标识列),需重跑 scripts/csv_to_mdb.py 重建。
+
+***
+
+## 6 判级与算法设计
+
+### 6.1 判级矩阵与四维证据
+
+判级矩阵覆盖九个系统(src/windscada/taxonomy.py 的 SYSTEMS):变桨、偏航、主轴承、齿轮箱、发电机、变流器、主控与传感网、叶片与叶根、塔架与基础。其中叶片与叶根、塔架与基础不参与 SCADA 侧判级(代码里列为无 SCADA 数据的两个系统),因此页面上常见的"七系统判级矩阵"指的是可由 SCADA 判级的七个系统;液压(蓄能)并入变桨,温度 NBM 是方法而不是系统,这两点写在代码注释里。
+
+判级词表有五态:报警、良好、不可判、优秀、无数据;取严合并的权重是报警最严、其次良好、再次不可判、优秀与无数据并列(src/windscada/taxonomy.py)。子系统面另用四键取严(报警、良好、优秀、不可判),偏航面先过滤不可判再取三键。七系统判级矩阵与四源融合数据流如图 6-1 所示。
+
+![图 6-1 判级矩阵与四源融合数据流](figures/fig-des-04-判级与融合.png)
+
+各系统的判级函数与证据维度如表 6-1 所示。四个子判定面各自有四个维度(四维证据),逐维度给出判词,再按取严序归到系统级。
+
+| 系统 | 判级函数 | 四维证据或依据 | 判词示例 |
+|---|---|---|---|
+| 变桨 | src/windscada/subsys/pitch.py 的 registry | 蓄能与压力调节、变桨轴承与轮毂润滑、执行与位置反馈、油路与密封 | 蓄能失效候选(定向查)、记基线观察 |
+| 偏航 | src/windscada/subsys/yaw.py 的 registry | 偏航轴承与润滑、驱动装置、液压制动、对风控制 | 报警、良好、优秀 |
+| 主轴承与齿轮箱与发电机 | 温度通道经 temp_nbm.registry,再按通道到系统的映射归口 | 温度 NBM 同工况比档与相对离群 | 温度候选(定向查)、传感器候选(A 类,非热)、记基线观察 |
+| 变流器 | 可靠性归因码到部件的映射与状态统计 | 停机段归因、故障码统计 | 报警、良好 |
+| 主控与传感网 | 报警台账与系统辅助件 | 多报警台、传感器偏差、通信错误 | 报警、良好 |
+| 叶片与叶根、塔架与基础 | src/windscada/subsys/structure.py 的 registry | 塔筒频率、塔筒振动、塔筒湿度、基础与法兰;叶片 A 与 B 与 C、叶根螺栓 | 报警、不可判 |
+
+温度判词到矩阵级的映射是:温度候选与传感器不可信(A 类)归为报警,记基线观察归为良好,不可判(返服嫌疑)归为不可判。融合面的矩阵级枚举是 ok、meta、bad、warn、note 五种,关闭类最先判,取严权重为 bad 最重、其次 warn、再次 note、ok 最轻。
+
+### 6.2 温度 NBM 与同油温档带宽
+
+温度 NBM 的口径是"同一功率档内比同族温度",功率档按 500 kW 一档划分(0 至 4500 kW)。样本门是同一单元样本数不少于 18;台级样本少于 500 行时判"不可判(样本不足或返服嫌疑)"。偏差与稳健 z 值为:偏差等于实测中位减去基准中位,稳健标准差取 1.4826 倍中位绝对偏差并从下限 0.3 起算,z 等于偏差除以该稳健标准差。判级阈值为:偏差不小于 15 K 判 A 类(传感器不可信,非热);偏差不小于 4.0 K 且 z 不小于 3.0 判温度候选;偏差不小于 2.5 K 且 z 不小于 2.0 判记基线观察。
+
+同油温档带宽的判据在蓄能(液压)面而不是温度 NBM 文件里:按油温 5 K 一档分箱(发电态功率大于 100 kW 的行),同一档内样本数不少于 24 时计算压力带宽(段内最大值减最小值);带宽比值不小于 1.5 时入轴,轴数不少于 2 时判"蓄能失效候选(定向查)"。热链面另按高油温档统计占空:档位不小于 45 摄氏度且样本数不少于 30 才出值。
+
+温度面还有两个故意固定的对照时间窗:ref 是 2025-07-01 至 2026-01-01(限电前干净时间窗),ref_sm 是同历月早一年;它们不随用户所选时间窗漂移,因为季节解耦的对照基准一旦跟着漂就失去对照意义,页面上如实标注这一点。
+
+### 6.3 七镜头曲线与限电前干净时间窗
+
+七镜头曲线的定义与口径在 src/windscada/perf/curves.py:L1 风速与功率、L2 功率与桨距(含三叶互比)、L3 功率与发电机转速、L4 转矩与转速(转矩等于功率除以发电机角速度,理论形态为转速平方族)、L5 风速与风轮转速(叶尖速比)、L6 功率与功率系数、L7 发电机转速与风轮转速之比。机型常数是风轮半径 65.0 米、扫风面积 13,273 平方米、铭牌齿比 119.752、贝兹极限 0.593。
+
+分箱边界为:风速 3.0 至 15.5 米每秒、步长 0.5;功率 0 至 4250 kW、步长 250;转速比 600 至 1760、步长 40。显著门共八项按绝对值锚定;离群判据是稳健 z 绝对值不小于 3 且残差不小于对应门限;工况段闸是功率分箱不小于 250 kW、转速比分箱不小于 900。另有三条物理硬闸:功率系数不得超过贝兹极限、齿比恒等(偏差超过 1.5 判离群)、叶尖速比须呈平台。列活性铁律要求同一列取值数大于 1、卡死比例小于 0.5 且落在物理域内,否则该列不参与镜头。
+
+限电前干净时间窗是 2025-07-01 至 2026-01-01(2025-H2),代码里把它写成常量并注明这是"限电前干净判别时间窗",与七镜头曲线的判别时间窗一致。选到 2025-07 至 2025-12 这一段时,发电性能页直接使用正式产物而不重算。
+
+### 6.4 M9 控制参数一致性
+
+M9 是控制参数一致性检查,摆在发电性能页上,口径在 src/windscada/perf/control.py。四个判据如表 6-2 所示。P 封顶取有功功率上限通道的 99.5 百分位,ω 封顶取发电机转速上限通道的 99.5 百分位,只取有功功率均值大于 100 kW 的行参与统计。
+
+| 判据 | 输入列 | 方法 | 阈值 |
+|---|---|---|---|
+| C1 双封顶 | grd_wtc_ActPower_max、tur_wtc_GenRpm_max | 按容差分组(容差 25 kW 与 8 rpm),组内计数不少于 2,逐台与组中位比 | 偏差超过 3 倍容差即离群,判"版本或参数差异·准定论载体" |
+| C2 K 聚类 | 有功功率均值与发电机转速均值 | 中载区 1200 至 2800 kW 内计算 K 等于功率除以转速立方,取中位乘 100 万 | 离群门为 5 倍 1.4826 倍中位绝对偏差,稳健标准差下限为百万分之一 |
+| C3 桨距调度 | 功率分箱与桨距角 | 同功率档内比桨距角 | 同档差门 0.5 度 |
+| C4 尺子 | 满发功率 99.5 百分位、转速封顶、额定桨角 | 满发段取功率大于 3800 kW 且样本不少于 50 | 出 pitch_rated,供交叉核对 |
+
+M9 按所选时间窗重算(control.registry 接受 span 参数),产物是 control_profile.parquet(逐台的 P 封顶、ω 封顶、样本数、K 中位、额定桨角)与 control_schedule.parquet(逐台逐功率档的桨距)。实测(38 台):P 封顶中位从干净时间窗的 4190 kW 变为 2026 年的 4181.7 kW 与 2026 年一季度的 4177.5 kW。
+
+### 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 判为远程停机(业主单位(从略)与主机制造商(OEM,名称从略)),不计入设备责任。
+
+三项指标的口径如表 6-3 所示。外部类(运维操作、风况(外部)、电网或供电(外部))单列统计并明确"外部类不计设备可靠性"。
+
+| 指标 | 公式或口径 | 注意点 |
+|---|---|---|
+| 平均无故障间隔(MTBF) | 台时减去停机小时再除以事件数;无记录停机不给值 | 部件可靠性模块的口径,台时目前用理想日历 |
+| 平均停机间隔(MTBO) | 在上一项基础上再扣除调度令停机后除以事件数 | 停机归因模块的口径,台时已改为实测台时 |
+| 单次停机时长(MDT) | 停机段时长的均值;就地与切入边界类码按口径剔除 | 两个模块同口径 |
+
+台时口径的实测对比很重要:2026 年 1 至 7 月理想日历台时为 194,050,实测台时为 165,475(差 17%),原因是尾月只有 6 天数据;若沿用理想日历,平均停机间隔会虚高 18%(239 小时实为 203 小时)。因此 faults.py 已改为用实测台时(由 loss_monthly 的行数除以每小时 6 个节拍得到),并在不可得时才回退理想日历。需要如实指出:reliability.py 的部件可靠性表仍使用理想日历台时,两处口径目前不一致,属待统一项(见第 17 章)。
+
+### 6.6 融合面四源与逐台判级
+
+融合面的四源是振动、温度、润滑、油液,口径写在 src/windscada/subsys/fusion.py 的模块头。融合面读取的输入件包括:handoff_vibration_v2.json、oil_samples_index.parquet、watch_channels_monthly.parquet、由 temp_nbm.registry 出的温度比档结果、mblub_monthly.parquet、alarms.parquet、workorders.parquet、油样合并报告 json,以及 CMS 报告转录件。
+
+融合纪律有两条:一是禁止按源数加权(同一机制的多个观测面不等于多源印证,源多不代表结论更强);二是 claim 时间窗对齐(振动数据到 2026-08、SCADA 数据到 2026-07-07、油样到 2025-08),避免拿不同时点的证据互相印证。逐台判级的产物字段包含台号、部件、振动结论、语义类、级别、设备状态、证据状态、振动信号、温度通道、温度偏差、温度判词、时间窗区间、末次油样、油样新鲜度、claim 时间窗等。
+
+四源的权重不是数值表而是优先级:关闭类最先判(销案、正样本、无新证、可降、降观察、无异常先归为正常),其余按取严合并;振动侧判 bad 或 warn 且非 meta 时,对应系统升级为报警;windcms 报告三方对拍也取严(危险与报警抬级,不可判且本层无异常证据则判不可判,良好与优秀不动)。系统关键词映射表把部件名归到系统,未收录的单元写"未收录"而不是写正常。
+
+### 6.7 残月识别
+
+"产物是当前原始件的函数"这条原则要求尾月样本很少时也如实出值,而不是加最小样本门去猜。同时,样本严重不足的月份必须能被识别出来,否则环比与均值会被拉偏。残月识别的实现在服务层:覆盖度等于该月行数合计除以每小时 6 个节拍再除以机组台数再除以当月日历小时;日条数等于该月条数除以覆盖度与当月天数的乘积,仅当覆盖度大于 0.02 才出值;覆盖度小于 0.5 的月份列为残月。
+
+实测的典型残月是 2026-07:只有 6 天数据,覆盖度 19.4%。页面上残月以灰虚线柱表示,并写"仅 N 天"(N 等于覆盖度乘 30 后取整);报告导出侧对覆盖度小于 0.5 的月份写"(不满月)",并声明环比不纳入残月。融合面与温度面也在各自口径里写明"6 天时间窗"的样本事实(例如温度面记"应有 288 个节拍、实测 145 个")。
+
+### 6.8 算法到输入列到判据到产物
+
+各算法与输入列、判据、产物的对应关系如表 6-4 所示,用以支撑"每条判据都能指到输入列"这一要求。
+
+| 算法或模块 | 关键输入列 | 判据要点 | 产物 |
+|---|---|---|---|
+| 温度比档(subsys/temp_nbm.py) | 各温度通道的均值列(按 500 kW 功率档分箱) | 偏差与稳健 z 双门限;样本门 18 | windscada/temp_bins.parquet,注册表出判词与依据 |
+| 蓄能带宽(subsys/hydraulic.py) | 液压油温均值、液压压力最大最小均值 | 5 K 油温档内带宽比不小于 1.5 且轴数不少于 2 | windscada/hydraulic_accum.parquet |
+| 变桨面(subsys/pitch.py) | 开关量 on 秒、柱塞动作次数、压力、三叶桨距角与有功与转速 | 停机顺桨段与满发段与运行段同工况分档的零位偏差 | pitch/pitch_daily.parquet、pitch/pitch_zero_monthly.parquet |
+| 偏航面(subsys/yaw.py) | 偏航压力均值、偏航润滑与柱塞时间、偏航计数、风偏差 | 四维取严;偏差清洗与相关位置 | windscada/yaw_daily.parquet、yaw_err_clean.parquet |
+| 七镜头(perf/curves.py) | 有功功率均值、限电命令、风速、发电机转速、风轮转速、三叶桨距角、环境温度 | 八项显著门;离群 z 与残差双门;三条物理硬闸 | windscada/curve_lenses.parquet、curve_liveness.parquet |
+| M9(perf/control.py) | 有功功率上限、发电机转速上限、有功功率均值、转速均值 | C1 至 C4 四判据 | windscada/control_profile.parquet、control_schedule.parquet |
+| 停机与归因(perf/faults.py) | 停机事件、报警台账、调度令码 | 段不少于 1 小时;T1 与 T2 两级归因 | windscada/stop_events.parquet |
+| 损失与可用率(perf/availability.py) | 风速、有功功率、功率曲线分箱 | 理论功率插值后算损失;风速低于 3.0 米每秒记 0 | windscada/loss_monthly.parquet |
+| 融合面(subsys/fusion.py) | 振动 handoff、温度比档、润滑月度、油样索引、报警与工单 | 关闭类最先判加取严;禁按源数加权 | 内存逐台表(融合面链盘)与 handoff 出件 |
+| 系统矩阵(taxonomy.py) | 各面上级产物与 windcms 报告转录 | 取严合并;未收录不判正常 | system_aux.parquet 与内存矩阵 |
+
+***
+
+## 7 时间窗口径设计
+
+### 7.1 时间窗词表
+
+2026-09-21 的用户令要求"部件问题与发电性能随所选时间窗变化",并新增自定义起止日期(含两端)。实现的词表如表 7-1 所示(scripts/windscada_serve.py 的 WINDOWS 与 months_of 与 win_range)。
+
+| 时间窗写法 | 含义 | 对月度件的作用 | 对日粒度件的作用(含两端) |
+|---|---|---|---|
+| 近30日 | 数据末端 1 个月 | 该 1 个月 | 该段月首至月末 |
+| 近90日 | 数据末端 3 个月 | 该 3 个月 | 该段月首至月末 |
+| 2026年 | 预设段 | 2026 年 1 月起至数据末端 | 段首月首至段末月末 |
+| 2026H1 | 预设段 | 2026-01 至 2026-06 | 2026-01-01 至 2026-06-30 |
+| 2025H2 | 预设段 | 2025-07 至 2025-12 | 2025-07-01 至 2025-12-31 |
+| 全程 | 全部月份 | 全部 | 0001-01-01 至 9999-12-31 |
+| YYYY-MM | 单月 | 该月 | 该月 1 日至月末 |
+| YYYY-MM~YYYY-MM | 月区间 | 区间内各月 | 起月 1 日至止月月末 |
+| YYYY-MM-DD~YYYY-MM-DD | 自定义起止(含两端) | 与之相交的月 | 原样,含两端 |
+
+含两端的实现方式是:win_range 原样返回日期串并按起不大于止排序,日粒度过滤在 in_win 里用"上界加 1 天再减 1 秒"来含住止日当天。月度件按所跨月份取整,日粒度件按日精确,页面上写明这句,避免读者以为月度均值能切到天。默认时间窗是 2026年(服务端取参数默认值与前端一致),工作台 v2 顶栏提供两个日期输入框与「应用」按钮,校验起不大于止,非法输入拒绝并提示。
+
+### 7.2 哪些随所选时间窗、哪些不随
+
+随与不随的划分如表 7-2 所示。需要强调的是:不随时间窗变化的内容不是"忘了改",而是各有自己的数据时点或对照意义。
+
+| 内容 | 是否随时间窗 | 机制 |
+|---|---|---|
+| 报障统计、五态控制策略、温度月轨迹、停机台账 | 随 | 月度件按 months_of 过滤 |
+| 判级四轴(变桨、偏航、蓄能、温度) | 随 | taxonomy.system_matrix 接受 span,各面 registry 接受 span;变桨用日粒度件精确切片,偏航与蓄能与温度走窄仓重算 |
+| 部件可靠性表(平均无故障间隔(MTBF)、单次停机时长(MDT)、停机时长) | 随 | reliability.overview 接受 span;停机事件与报警都是日粒度 |
+| 发电性能的 7 张月度时序图 | 随 | 按 months_of 过滤(月度件按月取整) |
+| 发电性能的七镜头曲线 | 随 | /api/curves 带时间窗参数,按时间窗重算分箱;选到 2025-07 至 2025-12 时直接用正式产物 |
+| M9 控制参数一致性 | 随 | control.registry 接受 span,走窄仓 |
+| 温度面的 ref 与 ref_sm 对照时间窗 | 不随(故意固定) | 季节解耦的对照基准,跟着漂就失去对照意义 |
+| 振动判级与厂家报告转录与工单台账 | 不随 | 各有自己的数据时点;页面上如实标注"不随本时间窗" |
+
+### 7.3 按时间窗缓存与 pending 契约
+
+按所选时间窗重算一律走"后台算加进程内缓存":缓存分三类(判级矩阵、曲线、M9),键由起止日期组成;命中即回,未命中则起后台线程并把"正在算"的状态回给页面,页面轮询再取。三类缓存的状态变量是 sysmx_pending、m9_pending 与 win_pending(win_pending 等于前两者之一为真),曲线另用 building 标记。缓存是进程内常驻字典,没有过期时间也没有容量上限;另有产物指纹缓存,过期时间为 2 秒,用于产物更新后自动重载。
+
+同一时间窗只算一次靠"在算集合"去重;算失败会记名并保留错误摘要,供页面与日志查看。为避免"预热线程与请求线程同时算两份中间帧"把内存吃光(远端实测问题),重活加了一把全局串行锁(_HEAVY_LOCK),并在拿锁时打印可用内存。服务启动末尾会起一个名为 win-warm 的守护线程做后台预热:先算六个预设时间窗的判级矩阵(每时间窗等待上限 180 秒),再算默认时间窗的七镜头(等待上限 240 秒);M9 不预热。预热可用环境变量关闭。
+
+pending 契约的关键是"绝不静默把旧口径当新时间窗":在算期间页面在脸上写明"下面这几张卡还是上一份口径",并在轮询到缓存就位后整段替换。时间窗与 pending 契约的时序如图 7-1 所示。
+
+![图 7-1 时间窗按窗重算与 pending 契约](figures/fig-des-05-时间窗与pending.png)
+
+前后端的等待参数如表 7-3 所示。
+
+| 环节 | 参数 | 实测或口径值 |
+|---|---|---|
+| 判级矩阵与曲线按时间窗重算 | 预估耗时 | 判级约 25 秒每时间窗;七镜头约 9 至 44 秒每时间窗 |
+| M9 控制参数按时间窗重算 | 预估耗时 | 服务注释约 5 秒每时间窗,版本记录约 0.6 秒每时间窗(两处不一致,待统一) |
+| 前端判级 pending 轮询 | 间隔与次数 | 每 5 秒一次,至多 40 次 |
+| 前端曲线 building 轮询 | 间隔与次数 | 每 6 秒一次,至多 40 次 |
+| 前端问答轮询 | 间隔 | 每 2 秒一次 |
+| 后台预热 | 覆盖范围 | 六个预设时间窗的判级加默认时间窗的镜头(不含 M9) |
+| 运维控制台 | 轮询间隔 | 每 2 秒一次 |
+
+### 7.4 时间窗口径下的实测对比
+
+按所选时间窗重算生效后,逐系统报警机组数会随时间窗变化,这是"判级真的按所选时间窗重算"的直接证据。本机实测(38 台):变桨报警为 15 与 13 与 10 台,偏航报警为 15 与 11 与 5 台,齿轮箱报警为 7 与 4 与 4 台,分别对应 2026年、2025H2、2026-07 三个时间窗;曲线图注随所选时间窗走且样本量随时间窗变化。远端复核(2026-09-21):变桨报警从 15 台变 7 台、偏航从 15 台变 3 台,曲线图注写"时间窗 2026-07-01 至 2026-07-31(随所选时间窗)"。
+
+***
+
+## 8 服务与前端设计
+
+### 8.1 HTTP 服务与路由族
+
+工作台服务用 Python 标准库的线程化 HTTP 服务实现(scripts/windscada_serve.py 引入 ThreadingHTTPServer 与 BaseHTTPRequestHandler),首请求把产物读进内存,之后每次取数前比对产物指纹(相关文件的路径、微秒级修改时间与大小的摘要,过期时间 2 秒),指纹变了自动重载,因此产物更新后不需要重启服务。路由族如表 8-1 所示。
+
+| 路由族 | 代表路由 | 说明 |
+|---|---|---|
+| 页面族 | / 与 /v2 与 /v2/snapshot 与 /turbine/台号 与 /problem/台号/系统 | 经典总览、v2 单页、快照下载、逐台页、单问题页 |
+| 取数族 | /api/fleet 与 /api/curves 与 /api/problem 与 /api/turbine 与 /api/vibcms 与 /api/maint_survey 与 /api/maint_std 与 /api/maint_framework 与 /api/dq_findings | 判级矩阵与链盘、七镜头、单问题、逐台、振动融合、检修四链与数据质量 |
+| 本体族 | /api/ontology 与 /api/ont_list 与 /api/ont_obj 与 /api/ont_chain 与 /api/channels | 本体页与机制链;v2 实际只懒取 /api/ont_chain |
+| 模型与报告族 | POST /api/ask 与 /api/ask_status 与 /api/ask_models 与 /api/rpt_compose 与 /api/rpt_export | 问答、状态轮询、档位列表、汇报纸组装与导出 |
+| 快照与事实与运维族 | /api/snapshot 与 /api/facts 与 /api/facts/claim 与 /api/reload | 快照、事实契约与单条 claim、显式清缓存并回显新旧指纹 |
+| 静态件族 | /static/* 与 /turbines/* 与 /windcms/* | 总览页与逐台静态件、振动页面件(限目录只读) |
+| 兜底 | 其余路径 | 缺件走结构化缺件页(HTTP 200),其它异常返回 500 页并记日志 |
+
+### 8.2 网关前缀改写与 data-abs 豁免
+
+网关把组件页里的根相对链接按前缀改写,否则组件页里的 / 会指向网关根而不是组件根。改写规则如表 8-2 所示(scripts/guanlan_gateway.py 的 rewrite())。
+
+| 改写对象 | 规则 | 例外 |
+|---|---|---|
+| 属性类 | href、src、action、content 后紧跟单个斜杠的根相对写法加前缀 | 双斜杠协议相对与 data: 不动 |
+| 样式类 | url() 内的单个斜杠根相对写法加前缀 | 同上 |
+| 脚本调用类 | fetch、open、EventSource、WebSocket、assign、replace 后字符串字面量里的根相对写法加前缀 | 同上 |
+| 赋值类 | 裸 location 赋值的根相对写法加前缀 | location.href 已被属性类规则覆盖 |
+| 字符串常量类 | 字符串里出现的 /api/ 加前缀(避免拼接出的接口地址漏改) | 同上 |
+| 绝对地址类 | 指向本机旧端口与新端口的绝对地址归到对应前缀 | 旧端口映射表保留在代码里 |
+| data-abs 豁免 | 标签上标 data-abs="1" 的 href 先换成哨兵、改写完再摘掉,即这条链接不被前缀改写 | 两种书写顺序(href 在前或 data-abs 在前)都认 |
+
+data-abs 豁免的实际用途是"组件页要指回网关根":工作台页面里的「返回观澜门户」用根相对写法并在标签上标 data-abs="1",否则它会被改写成 /detail/ 而永远回不到门户。此外网关对 /ops 系列的响应加 no-store 缓存头(否则页面脚本更新后浏览器仍用旧版,表现为"改了没反应"),门户静态件按修改时间与大小缓存,/release/ 提供只读暴露且不合并主库。
+
+### 8.3 页面烘焙与静态件
+
+除实时取数外,系统还有一条"烘焙"路线:把接口响应烤进单个 HTML 文件,使其离线也能打开。烘焙器是 src/windscada/ui/snapshot.py 的 bake(),注入 snap 标记、六个预设时间窗与语言对等文件指针,并放行问答与报告导出类接口走网络。总览「全场状态」页就是这条路线:重算链的 ⑦b 步用组件同一套取数函数生成 outputs/场站/windscada/index.html,工作台 v2 的 iframe 指向它。
+
+静态件落点是:/static/* 从 L0 标准仓读,/turbines/* 从同一仓读并注入浅色主题,/windcms/* 只读振动产物目录并限制在本目录内。三者缺件时都返回结构化缺件页(HTTP 200),写明缺哪一件、属于哪条产物线、怎么补,并给出运维控制台与自检入口。
+
+### 8.4 前端单页、轮询、中英切换与链接契约
+
+工作台 v2 是单页应用(src/windscada/ui/app.js,133 KB,另有 charts.js 30.8 KB 与 app.css 24.2 KB)。首屏只取 /api/fleet(判级矩阵与链盘),其余标签按需懒取;待算状态按第 7 章的间隔轮询。中英切换用查询参数 ?lang=en(语言来源是页面的 lang 属性,服务端据此投影权威英文名),localStorage 中只有一个键 guanlan.portal,用于记住门户来路;静态快照模式下查询参数不生效,改用对等语言的真实文件指针。前端契约如表 8-3 所示。
+
+| 契约 | 写法 | 说明 |
+|---|---|---|
+| 标签切换 | #tab=标签名 | 十个工作台标签;切换触发 hashchange |
+| 系统下钻 | #tab=component 与 sys=系统 slug | 从系统格下钻到该系统的问题清单 |
+| 逐台页 | /turbine/台号 | 独立页,含返回门户链接(标 data-abs) |
+| 单问题页 | /problem/台号/系统 | 系统标识经 sys_norm 双向归一(中文名、前端 slug、大小写都认) |
+| 回经典页 | / 加 lang 与 win 参数 | 从 v2 回到经典总览并保留语言与时间窗 |
+| 系统标识认不出时 | 页面写"系统标识无法识别" | 绝不写"该系统在判级时间窗内无非常态"(对报警机组那是假陈述) |
+
+v2 页面还有一条硬要求:可展开的「查看完整依据」不得为空。该类卡片的行来自融合面链盘,而 SCADA 侧只覆盖多报警机组,两边集合不同时旧的查表会取空;现在的口径是缺依据时按本行字段自组句(部件、链路走到第几步、卡在哪一步、证据源),永不空。
+
+***
+
+## 9 本体与知识层设计
+
+### 9.1 六个摄入步与对象库规模
+
+本体层的构建是重算链 ⑦ 的六个子步,逐步说明如表 9-1 所示。本体层"零判级权":它只转录 L1 正本(判级矩阵与融合面)的结论,每条判级对象带审级与 claim 时间窗,证据指回 L1 产物,同部件多源判级并存不合并。
+
+| 顺序 | 命令 | 输入 | 输出 |
+|---|---|---|---|
+| 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/<场>/ontology/objects.json,4,452,520 字节,对象结构为 id、type、props、links 四个字段)。
+
+| 对象类型前缀 | 对象数(实测) | 说明 |
+|---|---|---|
+| workorder | 5,567 | 检修工单展开 |
+| component | 580 | 部件与机群级系统对象 |
+| wi | 559 | 作业指导步骤 |
+| task | 559 | 预防性任务 |
+| alarmcode | 555 | 报警码(含中文码义) |
+| verdict | 411 | 判级对象(带审级与证据) |
+| evidence | 411 | 证据对象(指回 L1 产物) |
+| oilsample | 404 | 油样索引对象 |
+| catalog | 207 | 目录与分册 |
+| doc | 303 | 文档与图纸索引 |
+| failmode | 150 | 失效模式 |
+| turbine | 38 | 机组 |
+| decision | 39 | 决策链进度(一台一个) |
+| mechanism | 8 | 失效机制 |
+| 合计 | 9,791 | 对象库当前对象数 |
+
+按对象自身的类型字段统计(与上表按顶层键前缀统计口径略有差异,差异来自 Doc 类含文档与目录两种前缀):工单 5,567、部件 580、预防性任务 559、作业指导 559、报警码 555、文档类 510、判级 411、证据 411、油样 404、失效模式 150、决策 39、机组 38、失效机制 8。对比参考:docs/系统设计说明.md 记录的 2026-09-17 时点为 9,613 个对象,本次实测为 9,791 个(对象库文件修改时间 2026-09-21),两个数字都如实保留并注明口径日期;检索索引文件 2,842,037 字节,实机参数表 58,397 字节。本体审计(python -m src.ontology.audit)的期望值是 0 问题,判级时间窗口径常量为"近 90 日固定时间窗(至 2026-07-07,曲线轴为 2025H2)"。
+
+### 9.2 MCP 纯函数只读复用
+
+本体层对外暴露一组只读工具(src/ontology/mcp_server.py),全部为纯函数、模型零写权、判级零生成,只转述对象里的判级原文与审级。工具共 18 个,另有 1 个接地闸,如表 9-3 所示。
+
+| 工具 | 用途 |
+|---|---|
+| ont_stats | 本体库概览与判读纪律(全场级问题开局) |
+| ont_fleet_verdicts | 全场判级速查(一次给全 38 台的台号、部件、级别、审级) |
+| ont_turbine_brief | 台级汇总(判级带审级、机制、工单、油样、决策) |
+| ont_brief_pack | 一站式取证包(台级问题首选,一轮取齐) |
+| ont_get | 按 id 取对象全文(props 与 links) |
+| ont_query | 按类型、台号、关键词过滤查询 |
+| ont_links | 对象的正向链接与反向被引 |
+| ont_search | 混合检索(术语加语义召回相关对象 id) |
+| ont_mechanism_search | 失效机制库检索 |
+| ont_scenario | 排期沙盘推演结果(建议级) |
+| ont_loss | 损失分析(近 N 月分态与台级损失结构) |
+| ont_risk | 风险跟踪面(监视读数、机制状态、验收状态机、盲区纪律) |
+| ont_workorder | 检修工单查询(按台、系统、关键词) |
+| ont_chain_fault | 链二抢修:报警码到码义到失效模式到手册分步处置 |
+| ont_chain_preventive | 链一预防:系统或部件到失效模式到预防性任务到作业指导步骤 |
+| ont_fault_tree | 链三故障树:系统失效模式按实证量排序加征兆码 |
+| ont_maint_plan | 链四计划:按风险级与命中数排序的检修排程建议 |
+| ont_doc_search | 技术支持:图纸、原理、手册检索并返回现场可调阅路径 |
+| claim_check | 接地闸:最终回答前必须调用,台号与数字须来自本会话工具事实,越界即拒 |
+
+这组工具同时被多处复用,且"只读"不是口头声明而是可核事实:整份模块里文件写入、目录创建、进程调用与网络请求四类接口零命中;对象库缺失时直接响亮报错并提示先跑铺开步,不静默降级;读库按对象库文件的修改时间失效重载,避免读到静默陈旧的库。复用方有五处:离线问答代理(同进程直连,工具与接地闸零复制)、工作台本体页的三个服务端函数、工作台机制链接口的按类派发、问答服务的取证调用,以及总览页烘焙时的本体数据注入。工作台侧的回答会自动附结构化事实契约引文块,引不到契约条时明写"未被契约背书"。
+
+### 9.3 机制链、决策台与对象浏览器
+
+机制链(chain_ingest)把逐台处置进度落成决策对象,记六个步骤的状态、卡点与下一步;它不是证据对象,而是系统推出来的处置状态,因此需要服务在跑(它从工作台的链盘接口取数)。本体层不设独立页面标签,承载页是工作台的第 7 个标签「检修决策」(口径原话:"检修决策等于本体展现的窗口"),页内四段分别是「机制链视图 · 29 号范例(证据到机制到判级到沙盘到动作到验收)」「失效机制库」「决策台(决策捕获加月度滚动)」「对象浏览器」。这四段的取数接口是本体概览、对象列表、对象详情与机制链派发(按故障、预防、故障树、计划、文档五类分别派给对应的只读工具);这些接口在生成单文件快照时也会被逐个烘焙进页面,因此离线打开也能看。页面页脚写着一句纪律:本体是只读语义层,判级权在 L1 加审级,模型与网页均零判级权。
+
+事实契约是把结论对外化的那一层:每条结论由 scripts/guanlan_facts_contract.py 生成,包含 claim 编号、脱敏来源引用(文件名加 sha16 与章节)、本条 sha256、时间窗、聚合层级与裁决(六枚举之一),并派生出四类消费者件(门户结论段、工作台卡片、问答引用、报告摘要)。当前口径是"由重算台账生成 claim"(⑤b 步),因此门户结论段、问答与报告都随重算刷新;claim 六族是报警集中、重复检修、停机损失集中、温度相对离群、功率曲线偏离、振动面过闸线,相对判据一律封顶"候选"。
+
+***
+
+## 10 本机模型接入设计
+
+### 10.1 档位与配置
+
+模型接入完全可选:判断在代码,模型只做问答、解释与初筛。档位与显存需求写在 configs/models.json,如表 10-1 所示;配置注释明确"换模型改这里加 ollama pull,不改结论"。
+
+| 档位键 | 模型 | 显存需求(写入配置) | 用途 |
+|---|---|---|---|
+| default_qa | qwen3:8b | 6 GB | 默认问答,关闭思考模式;校闸不过自动升档一次 |
+| escalate_qa | qwen3:32b | 22 GB | 复杂题升档(2026-09-22 由本机未装的旧档名改为本机实装档) |
+| review | deepseek-r1:14b | 10 GB | 交叉审核初筛票(非独立审级) |
+| embed | bge-m3 | 2 GB | 检索向量 |
+
+配置文件另给出三套显存档位组合(24 GB 及以上四档、12 至 16 GB 三档、纯 CPU 两档)与四条拉取命令,运行地址是本机 11434。本机实测四档全部可用(自检逐档打印档位与模型名)。
+
+### 10.2 问答校闸与升档
+
+问答链路(src/ontology/fast_agent.py)的口径是"校闸不过不出正文":回答里引用的对象编号必须存在、引用判级对象的句子必须写出该对象的判级词、句中的系统名必须与所引对象一致;拦截原因会明确写给用户(换问法、点名机组号或结论编号、查看契约结论清单)。慢档(32B 级)单轮改写超过 60 秒时只给一次改写机会,避免为一个不过闸的答案烧掉十几分钟。
+
+升档的规则是:默认档被闸拦下则用升档目标重答一次;升档也拦则照旧不出文,但把两次拦截原因都带回。升档目标不再硬编码为单一档名,而是按"配置档位且确已安装"解析(先取 configs/models.json 的 escalate_qa 且验证本机确有该模型,再回落到代码内的映射表,都没有则返回空并响亮回落同时打日志)。这一改动来自 2026-09-22 的人工测试消缺:原先升档目标写的是本机未安装的档名,导致升档调用拿不到摘要、问答终态为 error,用户只看到"复核中"。
+
+### 10.3 耗时预算
+
+耗时预算按档位给出(src/ontology/fast_agent.py 的 BUDGET_S):8B 档 30 秒、另一轻档 30 秒、32B 档 120 秒(32B 权重约 18.8 GB,单轮可达数分钟,属"离线慢审")。超预算后不再改写,直接交给升档或不出文。实测参考:本机自检的 Ollama 探针耗时 171.8 秒(同一底稿另一次记录为 114.09 秒),审计流水里也有探针超时 90 秒的失败记录,说明本机模型可用但慢,属纯 CPU 或显存不足场景。
+
+### 10.4 审计日志
+
+每次模型调用写一条 JSON 到 logs/audit/llm_audit.jsonl,字段如表 10-2 所示(依据该文件当前 40,751 字节内容与末两条实样)。
+
+| 字段 | 含义 |
+|---|---|
+| ts | 调用时间(ISO 形式) |
+| purpose | 用途(如 probe、ask、review) |
+| model | 模型名 |
+| digest | 模型摘要指纹 |
+| prompt_sha16 | 提示指纹前 16 位 |
+| options | 参数(温度、随机种子、生成长度上限) |
+| think | 是否开启思考模式 |
+| tools | 是否带工具 |
+| attempt | 第几次尝试 |
+| latency_s | 耗时(秒) |
+| status | 结果(ok 或 fail) |
+| error | 失败原因(异常类型与消息,截断保存) |
+| output_sha16 | 输出指纹前 16 位 |
+| out_chars | 输出字数(可用于判断是否"零字成文") |
+
+审计流水与模型闸的其他纪律(固定随机种子与温度使同问同证据同答、同一时刻只有一个生成请求、瞬断自动重试一次、连续三次失败熔断 60 秒并明示、探针成功即清熔断)共同保证"模型部分可复核"。两条如实记录的观测:一是本机的审计流水在服务运行期间持续追加(本次只读观测中从 108 行增至 114 行),因此它是"活的"证据而不是静态文件;二是成功记录里会残留上一次尝试的失败原因(重试成功的那一行仍带超时字样),这是字段清理不彻底的小瑕疵,已列入待统一项。
+
+### 10.5 未装模型时的降级与如实标注
+
+未装模型时的降级分两层,口径不同。检索层是"真降级":向量检索不可用时自动改为纯词法检索并打印说明,绝不让检索层因为缺模型而整个不可用;重算链因此刻意只建词法索引(不建向量索引),所以离线机器上的对象库、机制链、对象浏览器与图纸检索全部照常可用。模型层是"响亮报错、不静默回落":模型闸在连续三次失败后熔断 60 秒并明确报"本机模型未就绪(调用方应报 503 或明示,不得静默回落)";页面把连接被拒一类的底层错误改写成"本机模型未启动(Ollama 127.0.0.1:11434 拒连);离线版不转云,请先启动 Ollama",问答接口返回 503,档位列表接口给出本机可用档位,自检里的模型探针判 FAIL 并记录原因。远端部署机(2026-09-22)实测没有装 Ollama(无二进制、命令不在 PATH、11434 端口拒绝连接),因此远端的问答与升档功能不可用,其余页面不受影响;这一点已记入版本记录的遗留项,处置建议是"装 Ollama 并把有网机器上的模型目录整体拷到目标机同位置",是否安装由用户决定。
+
+***
+
+## 11 运维控制台与重算编排
+
+### 11.1 运维控制台与动作
+
+运维控制台由网关提供两个入口:整页 /ops 与内嵌版 /ops/recalc(只保留重算、产物与动作进度三块,是门户「数据重算」页的 iframe 目标)。接口是 GET /ops/api/state(真实状态:各服务端口通不通、产物在不在、有没有任务在跑、上次结果)与 POST /ops/api/动作。动作只有 POST,其它方法返回 405(避免链接被预取或刷新时误触发),未知动作返回 404。四个动作如表 11-1 所示。
+
+| 动作 | 语义 | 语义冲突时的返回 |
+|---|---|---|
+| stop_services | 停止组件服务(保留控制台本身) | 组件都已停止时回 409 |
+| start_services | 启动组件服务并打开门户 | 组件都在运行时回 409 |
+| rebuild | 执行重算(等价于 python scripts/rebuild_all.py) | 已有任务在跑时回 409 |
+| products_off | 清除产物(真删除,不留备份) | 产物已清空时回 409 |
+
+按钮的禁用只是提示,后端一律再校验一次;直接打接口也绕不过并发与语义前置。页面每 2 秒轮询一次状态。
+
+### 11.2 任务为什么必须脱离网关的父子树
+
+若网关直接用子进程方式起重算,任务就是网关的子进程;而停止服务用的是"按进程号并沿父子树强杀",会连带把正在执行的重算一起杀掉(实测日志停在第六步"重启组件服务(停)"那一行,之后什么都没有,服务也停在半路)。因此运维动作经一个只做一件事的启动器二次拉起执行器后立即退出:执行器的父进程变成一个已退出进程,于是它不再是网关的子孙,沿树强杀不会再连带杀掉它。启动器把执行器的进程号打到标准输出的最后一行,供父进程记账。
+
+与此配套,重算链的第六步只重启组件服务、保留网关,理由有二:控制台页面本身由网关提供,连它一起停会把用户正在看的界面弄没;重算本身可能就是从控制台起的任务,停网关会沿父子树把重算一起杀掉。
+
+### 11.3 状态文件、心跳与日志
+
+任务状态写在 run/ops_job.json,字段与本次实测内容如表 11-2 所示。执行器每 15 秒更新该文件的心跳;网关侧超过 150 秒没有心跳就把运行中收尾为完成并写"心跳停了 N 秒,任务多半被强杀,未拿到退出码";判失败前会重读一次状态文件,防止轮询竞态把退出码覆写。
+
+| 字段 | 含义 | 本次实测值 |
+|---|---|---|
+| kind | 动作类型 | rebuild |
+| cmd | 实际命令 | scripts/rebuild_all.py --with-verify |
+| pid | 执行器进程号 | 54848 |
+| log | 动作日志路径 | logs/ops/ops_rebuild_20260919-095635.log |
+| status | 任务状态 | done |
+| rc | 退出码 | 1 |
+| started、finished | 起止时间 | 2026-09-19 09:56:35 至 2026-09-19 10:43:31 |
+| seconds | 耗时(秒) | 2815.5 |
+| note | 备注 | 空 |
+
+动作日志落在 logs/ops/,命名为 ops_动作_YYYYmmdd-HHMMSS.log,保留最近 20 份或 30 天(写前清理);本次实测该目录恰有 20 个文件,符合保留策略。运行态文件 run/pids.json 与 run/ops_job.json 是"当前跑着谁、当前在做什么"的唯一凭据,交付包默认排除它们。
+
+### 11.4 服务化重启与端口占用处置
+
+停止与启动的口径是:停止用按进程号并沿树强杀(Windows)或按进程组发信号(类 Unix),成功后删除运行态文件;启动按配置起六个组件并等待健康检查就绪(40 次 1 秒探测),全部就绪返回 0,有模块未起返回 1(属预期降级态),健康检查 60 秒未就绪返回 2。端口占用时的处置顺序是:先停服务再启动;仍被占用则在运维控制台看端口状态与进程,按端口清理占用进程;仍不行则改 configs/serve.json 的端口并同步网关路由表(网关的上游端口表默认值与 serve.json 可覆盖的键名一一对应)。
+
+***
+
+## 12 安装、服务化与版本管理设计
+
+### 12.1 安装与卸载
+
+Windows 安装入口是 install.bat 与 install.ps1,步骤号写死在输出里,如表 12-1 所示(依据 install.ps1 的实际打印文案)。类 Unix 入口是 install.sh,口径与 Windows 侧同构(选解释器、建虚拟环境、装依赖、写配置、可选装 systemd 单元、写安装记录、跑自检)。卸载入口是 uninstall.bat 与 uninstall.sh,共用同一份实现 scripts/guanlan_uninstall.py。
+
+| 步骤 | 做什么 | 失败时的处置 |
+|---|---|---|
+| 0/7 安装前检查 | 读 src/version.py 取本包版本,读安装根下的安装记录比版本差异(同版本、升级、降级、老安装无记录四类),交互式询问是否重装 | 交互式答非 y 即取消且不改任何文件;加 -Force 跳过询问;非交互自动继续 |
+| 1/7 选 Python | 需 3.11 及以上(依赖 pandas 3,3.10 及以下没有轮子);机器上没有则解包内便携运行时 | 两者都没有则打印两条办法后退出 |
+| 2/7 建虚拟环境 | 建 .venv 并升级 pip | 失败即停 |
+| 3/7 装依赖 | 优先用包内离线轮子 wheels/win_amd64,无轮子才联网安装 | 依赖安装失败退出码 3,并提示先解决再启动 |
+| 4/7 写配置 | 把 configs/serve.json 的 python 字段写成相对安装根的 POSIX 路径 | 写配置失败退出码 4 |
+| 5/7 建启动快捷方式 | 建"启动观澜.lnk"(中文名给人)与"start_guanlan.lnk"(ASCII 名给脚本)与桌面快捷方式,指向无窗口启动器 | 建不出来只告警不影响使用,并给手工建法与 start.bat 替代 |
+| 6/7 服务化(可选) | 调 services/service_ctl.py install 注册 Windows 服务,或加 --user-login 写登录自启 | 注册失败只告警,并提示需要管理员权限与 --dry-run 预览 |
+| 7/7 模型(可选) | 包内有离线安装包则静默安装 Ollama,并提示模型目录的拷贝办法 | 未装且包内无离线包时明确写"问答不可用",其余页面不受影响 |
+| 收尾 | 写安装记录 install-info.json 并跑一次自检 | 自检输出作为装机结论 |
+
+安装时间为整轮口径"首次约 10 分钟"(说明书),逐步耗时无实测证据。卸载的默认口径是"只拆本程序装上去的东西":停服务、注销服务、撤开机自启、删快捷方式、清运行态、删安装记录;默认保留 data、outputs、logs、.venv 与程序目录,加 --purge 才删数据(会再问一次),加 --all 才删程序本体。之所以默认不删程序目录,是因为 Windows 上正在运行的 python.exe 就在 .venv 里,"删自己必然只删一半",留个删了一半的目录比留着整棵目录更难查;卸载脚本因此先切到临时目录再调 Python。
+
+### 12.2 服务化形态
+
+两平台的服务化形态如表 12-2 所示。两平台共用同一个工作体(起等于 guanlan.py serve、守护等于每 15 秒巡检同样命令、停等于 guanlan.py stop),因此"服务起的"与"手工起的"口径完全一致,不会出现两套行为。
+
+| 平台 | 形态 | 服务体 | 注册器 | 前置条件 |
+|---|---|---|---|---|
+| Windows | Windows 服务(SCM 托管,独立进程) | scripts/win_service.py(用标准库直连服务控制管理器,不依赖第三方库) | scripts/service_ctl.py install(内部调系统命令创建、描述、配置失败重启、启动) | 管理员 |
+| 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 与免权限守护自证两条路径,其输出与设计说明一致),属如实留白项。
+
+### 12.3 安装记录与版本三守卫
+
+安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.10.2,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
+
+| 字段 | 含义 |
+|---|---|
+| name | 系统名 |
+| version | 已装版本(唯一凭据) |
+| edition | 版本性质(离线单包) |
+| installed_at | 安装时间 |
+| python | 安装时使用的解释器绝对路径 |
+| mode | 运行方式(手工、Windows 服务、systemd、登录自启) |
+| host | 安装根 |
+
+版本号只写在 src/version.py 的 VERSION 一行,改动自动传导到各处;三条守卫如表 12-4 所示,都是"对不上就报错",不靠人记得。
+
+| 守卫 | 读哪里比对 | 违反时的表现 |
+|---|---|---|
+| 版本真源守卫 | scripts/pack_dist.py 读 src/version.py 的 VERSION 写进包清单并拼默认包名 | 打包器不再自带版本常量,避免"包说一个版本、安装记录说另一个版本" |
+| 版本记录守卫 | scripts/version_log.py --check 比对 docs/版本记录.md 的自动块与 src/version.py 的 HISTORY | 不一致即 rc=6,提示重生成;重算链 ⑧d 步容忍 6 但报出来 |
+| 安装前检查守卫 | install.ps1 与 install.sh 从 src/version.py 取版本并与安装记录比对 | 同版本、升级、降级、无记录四类分别提示,降级会明确警告 |
+
+另有一条与版本同级的守卫是入口引用闭合与入口脚本编码守则(见 13.3 节):打包、开箱验证、装机自检三处都查,缺一个文件就不出包。
+
+### 12.4 打包器与开箱验证
+
+打包器 scripts/pack_dist.py 的默认口径是"一条命令、零开关":程序与脚本、配置、发布件、文档、离线依赖件与入口脚本进包;输入数据、产物、运行日志与临时文件不进包(例外用 --with-data 与 --with-products),另排除装机产物(安装记录与快捷方式,因为它们只在本机有效)。包内清单 dist-manifest.json 逐条记版本、构建时间、包含项、逐条排除理由与四类标志(不含数据、不含产物、不含日志、不含临时文件)。
+
+打包实测口径:一次打包报 3,430 件、1,032.0 MB(该数字来自变更记录,非本次实测;旧口径含缓存与日志为 3,741 件、1,091 MB)。开箱验证一条命令给出"能不能装、能不能跑"的证据,步骤如表 12-5 所示。
+
+| 步骤 | 核验内容 | 判定 |
+|---|---|---|
+| ① 解压 | 解压到临时目录并打印件数与耗时 | 解压失败即失败 |
+| ①b 闭合与编码 | 入口脚本引用闭合、编码守则核对 | 任一不通过即 rc=1 |
+| ①c 数据骨架 | 输入数据目录骨架逐个在位,且除放置说明外无混入文件 | 不通过即失败 |
+| ② 离线安装 | 在副本上真跑安装 | 非 0 即失败 |
+| ②b 无窗口与快捷方式 | 两份快捷方式在位、无窗口自证通过、包内无脚本宿主脚本 | 不通过即失败 |
+| ③ 起服务核验 | 核验门户根、工作台、振动、运维控制台、健康检查五个地址 | 要求至少 4 个可用 |
+| ③b 卸载核验 | 在刚装好的副本上真跑卸载,断言记录、运行态、快捷方式都拆掉且数据与产物一件不少 | 不通过即失败 |
+| ④ 收尾 | 只停副本自己的进程、清理临时目录 | 副本端口无残留 |
+
+一项现场教训写进了设计:开箱验证原先必须占用默认组件端口,于是验证期间用户正在用的工作台与振动页是断的(用户点门户「登录」正好撞上)。现在网关的上游端口表可被 configs/serve.json 覆盖,验证改为副本整套换到空闲端口(例如网关 28700、组件 28800 起),主实例照常运行;找不到空闲端口才降级为"跳过启动核验"并打印提示。
+
+***
+
+## 13 质量保证设计
+
+### 13.1 审计脚本矩阵
+
+系统的质量保证是"一条链加重算里的审计门加一个装机自检"。审计器矩阵如表 13-1 所示,退出码与本次实测结论一并列出。
+
+| 审计器 | 查什么 | 退出码语义 | 本次实测 |
+|---|---|---|---|
+| scripts/products_reverse_audit.py | 逐件回溯"输出到功能与算法到输入",族表匹配与人工件识别 | 0 全部成立;5 有未归类或判据失败 | rc=0,呼应成立 3,548 件、不成立 0、人工件 0、未归类 0 |
+| scripts/pages_audit.py | 页面归口五条机器规则(静态页不得引产物、实时页端口须在端口表内、引用产物须在位、数据派生页须有来源与指纹、冻结交付件须有版本与日期) | 0 一致;5 登记缺项或文件缺失;6 溯源缺失;7 数据派生页面陈旧;8 交付件缺版本号;9 分类错误 | rc=0,检查 21 项、2 项一致、19 项已知缺口、0 项要处理 |
+| scripts/config_audit.py | 配置登记表 R1 至 R8(顶层只放登记过的运行期单件、无备份垃圾、命名、内容不得含本机绝对路径、代码引用的配置必须存在、不许手拼配置路径、每域须声明消费者、场目录每件要么是场定义要么登记为 profile) | 0 通过;5 结构问题;6 悬空引用;7 内容含本机绝对路径;8 手拼路径;9 场配置不合约定 | rc=0,171 件配置、5 个域、1 个场定义、10 件机型 profile、0 不一致、14 已知缺口、7 提示 |
+| scripts/log_audit.py | 日志 L1 至 L5(落点唯一、命名、按末尾 200 行校验行格式、行尾与颜色、保留策略与审计流水每行合法 JSON) | 0 通过;5 日志跑到 logs 之外;6 命名不合口径;7 行格式不合规;8 行尾或颜色问题;9 保留策略超限 | rc=0,0 不一致、87 条提示(历史日志与旧行只提示) |
+| scripts/raw_scan.py | 输入数据逐族指纹比对与增量识别 | 0 无变化;4 有新增或变化;5 还没有基线 | rc=0,"与上次快照一致,没有新数据";另 --selftest rc=0(族表覆盖反向族表的 7 个输入、10 个步骤标签都能在链上找到) |
+| scripts/chain_gap_check.py | 振动六层链四步脚本是否齐全、关键产物是否在位、能否逐值对拍 | 0 齐全;5 缺料 | rc=0,四步脚本齐全;两件关键产物按口径重建但无标准答案可对拍 |
+| scripts/version_log.py | 版本记录自动块与代码 HISTORY 是否逐字一致 | 0 一致或已写;2 文件缺失;6 需重生成 | rc=0 |
+| scripts/detail_deps.py | 工作台各页面与接口的产物依赖台账(页面到接口到产物件到来源与生成端) | 0 通过;5 文档与现场不一致或证据核对不通过 | rc=0,清单 10 页签、16 接口、缺口 0 件、产物仓 3,549 件,文档与现场一致 |
+| scripts/inventory_products.py | 产物清点与输入到产物呼应校验(产物跨度必须落在输入跨度内) | 0 呼应正常;5 有 problems | rc=0(抽样:振动输入 25,693 件对时间窗索引 2,066,686 行、跨度 2026-03-16 至 2026-04-21) |
+| scripts/check_portability.py | 换机可移植性门禁三条硬规则(不许机器相关绝对路径、不许工作目录相对路径字面量、不许用系统分隔符拼库存字符串)加三条提示规则 | 0 通过;1 有严重项 | rc=0,通过(无机器相关绝对路径、无工作目录相对路径、未用系统分隔符拼串),例外登记 16 项加 2 类 |
+| scripts/check_transferable.py | 移植性大扫描五项(全类型文件的机器相关路径、虚拟环境解释器指向、各平台轮子是否备齐、便携运行时与清单、配置与端口是否相对) | 0 通过;1 有机器相关路径 | rc=1,290 处机器相关路径(HTML 烘入绝对路径 0 处,文档旧口径写 350 处)、提到网关端口的文本文件 43 个;轮子与便携运行时两项判通过 |
+| scripts/audit_chinese_terms.py | 中文表达审核(OEM 术语基准与行话规则),机器给候选人裁 | 0 通过;2 仅严格模式下有已裁改条目仍命中 | rc=0,扫 5 个文件与 7,322 条中文串,规则命中 123 条,其中显示层已覆盖 111 条、内部面豁免 42 条、仍待处理 0 条;无根词 3,902 条 |
+| guanlan.py check | 装机自检(约 40 行结论:解释器、源码可编译、语言包成对、子进程口径、入口闭合与编码、版本、卸载入口、反向呼应、页面归口、配置、日志、输入数据、依赖、产物与本体与契约与门户、六个端口、模型探针与四档、模型可用性) | 0 全绿;2 有 FAIL | rc=0,结论"全绿, 可 serve";底稿 docs/src/_guanlan_check.txt |
+
+自检底稿里的几个可核对数字:Python 3.12.10;源码 229 个文件可编译、无非法转义或语法错;语言包前后端 zh 与 en 成对 845 条;入口脚本引用闭合 48 条;入口脚本编码守则通过;反向呼应 3,548 件;页面归口检查 21 项;配置与日志无一致性问题;输入数据 26,503 件源件结构合规;依赖九项(numpy、pandas、pyarrow、polars、yaml、matplotlib、plotly、jinja2、docx)全部就位;L0 与 L1 产物、本体对象库、findings、事实契约、门户、仿真合页服务与资料包、三维资产、仿真回放资产、治理清单交付件全部在位;六个端口处于运行中;本机模型探针通过(耗时 171.8 秒)。
+
+有一处过程值得记录:本轮取证期间,工作台依赖台账门与可移植性门禁各自返回过一次"未通过"(一个是文档清单与现场不一致,一个是运行期源码里用系统分隔符拼串),随后两处都按同一口径修好并复跑转绿(可移植性门禁复跑输出"通过,路径可移植:只有相对路径加经路径模块解析",依赖台账复跑输出"文档与现场一致")。这正好说明"每条门都能独立重跑并给出退出码"的价值:问题是被跑出来的,不是被猜出来的。
+
+### 13.2 退出码语义与质量门闭环
+
+各审计器的退出码语义汇总如表 13-2 所示,语义与重算链里的容忍集合一一对应(见 5.2 与 5.3 节)。
+
+| 退出码 | 含义 | 谁在用 |
+|---|---|---|
+| 0 | 通过或无变化 | 全部审计器与构建器 |
+| 1 | 有严重可移植性问题;或链级失败汇总 | 可移植性检查器;重算链汇总非零即返回 1 |
+| 2 | 自检有 FAIL;契约自检不过;非交互下需要确认;文件缺失;严格模式下术语命中 | guanlan.py check、事实契约构建、卸载器、版本记录、中文表达审核 |
+| 3 | 权限不足 | 安装脚本与服务化注册器、卸载器 |
+| 4 | 有新数据;或按缺件如实报 | 输入数据扫描、变桨面、振动历史与基线、等价验收 |
+| 5 | 随包件这一路缺失(无基线、有未归类、被引产物不在位、文档与现场不一致、缺料、结构问题) | 月度派生件、反向呼应审计、页面归口审计、等价验收、链完整性检查、工作台依赖台账、产物清点、配置审计、日志审计 |
+| 6 | 溯源缺失;文档与代码版本记录不一致;增量冲突;悬空引用;命名不合口径 | 页面归口审计、版本记录一致性、输入数据结构体检、配置审计、日志审计 |
+| 7 | 数据派生页面陈旧;内容含本机绝对路径;行格式不合规;缺源类或时间空洞 | 页面归口审计、配置审计、日志审计、输入数据结构体检 |
+| 8 | 交付件缺版本号;手拼配置路径;行尾或颜色问题;输入结构仅提示 | 页面归口审计、配置审计、日志审计、输入数据结构体检 |
+| 9 | 分类错误;场配置不合约定;保留策略超限 | 页面归口审计、配置审计、日志审计 |
+
+需要如实说明的是:多数审计器只定义了自己会用到的那几个码,未列出的码在对应脚本里并未定义。例如反向呼应审计与链完整性检查只定义 0 与 5,版本记录只定义 0 与 2 与 6,装机自检只定义 0 与 2,可移植性门禁只定义 0 与 1。读退出码时应同时看该脚本自己的说明,不要假设"非零都一样"。
+
+质量门形成闭环:改代码或改配置后,重算链在⑤、⑧、⑧b、⑧c、⑧d 五处设审计门;装机时 guanlan.py check 再统一体检一遍;打包时入口闭合与编码守则在打包、开箱验证、装机自检三处强制执行;出包以后在目标机上安装再由自检复核。闭环如图 13-1 所示。
+
+![图 13-1 质量门与审计闭环](figures/fig-des-06-质量门闭环.png)
+
+### 13.3 入口脚本的字符守则
+
+三条编码守则由 src/entry_refs.py 实现,并在打包、开箱验证第①b 步、装机自检三处强制执行,违反就不出包或直接判 FAIL,如表 13-3 所示。
+
+| 文件类型 | 守则 | 违反的后果(现场实炸过) |
+|---|---|---|
+| .bat | 纯 ASCII、CRLF、无 BOM、不含连续小于号、大于号后必须是重定向目标 | 批处理注释里的中文被命令行解释器当成命令执行;连续小于号直接报重定向错 |
+| .ps1 | UTF-8 带 BOM 加 CRLF | PowerShell 5.1 按本地代码页解码导致中文乱码与级联语法错,安装一秒即退出 |
+| .sh | 行尾 LF | 每个词尾粘回车,set -e 变非法选项,变量判空翻转,脚本从一开始就是坏的 |
+
+与此配套的一条纪律是"入口脚本引用的文件必须齐全":凡入口脚本里出现的路径且在本机源码树真实存在,打包时自动补入,打包后对着包内条目复核(缺一个就删包并返回非 0),开箱验证与装机自检各再查一遍。这条守卫的价值在替换无窗口启动实现时体现过:去掉脚本宿主方案后入口列表与引用条目自动重算且全部在位。
+
+### 13.4 文档纪律
+
+文档与代码同级受管:版本记录由 scripts/version_log.py 从代码里的 HISTORY 生成,自动块禁止手改;重算链的 ⑧d 步以 rc=6 容忍但不静默的方式盯"文档与代码版本一致"。这条门的由来是一次实逮:远端升级的差异盘点只覆盖源码、脚本与配置,文档不在盘点面上,于是代码升到新版本而版本记录与系统设计说明留在旧版,而重算链当时只到 ⑧c,导致 24 步全绿、控制台写"重算完成",文档漂移没有任何自动拦截点。现在的口径是"升级差异盘点必须包含 docs",并由 ⑧d 步兜底。
+
+本文本身也按同一纪律编写:每章的事实都能在附录 A 找到来源文件,数字都能按第 1.4 节的命令复跑复现;本版同时新增三份交付文档(需求分析、设计说明、数据要求说明)与其生成端(文档渲染器与图表构建器),图表里的数字全部从真件取数而不是手画。
+
+***
+
+## 14 安全、合规与离线边界
+
+### 14.1 离线边界与只读输入
+
+系统的安全边界是四个方面,如表 14-1 所示。
+
+| 边界 | 设计口径 | 落地证据 |
+|---|---|---|
+| 只读输入 | 原始件只读,摄入器不写 data/raw;放置工具的增量落位对"同名不同大小"默认拒绝并拦下,必须显式确认才覆盖 | scripts/place_raw_data.py 默认拒绝冲突;产物是"当前原始件的函数"(源件删了行也随之消失并报出) |
+| 默认离线运行 | 全部计算在本机;依赖随包离线安装;模型可选且指向本机 | wheels/win_amd64 42 件、vendor/python 三平台便携运行时;模型端点是本机 11434 |
+| 唯一外发通道且强制脱敏 | 代码内保留一条显式的"可上云问答"通道(三个兼容云端接口,需环境变量提供密钥);未配置密钥时回落本机模型;外发前强制对每条消息做脱敏,脱敏模块不可用时直接拒绝外发而不是照发 | 离线模型代理模块的外发分支与脱敏调用;脱敏实体映射 16 条加三条正则;产物脱敏词表 32 条加 11 类回扫 |
+| 监听范围 | 组件只绑本机回环地址,只有门户网关使用 public_host(出厂为所有网卡) | configs/serve.json 的 host 与 public_host 及注释;启动时打印对外地址并提示"页面无鉴权,请在防火墙侧限来源";实测网关在所有网卡监听,其余六个进程只在回环监听 |
+| 无鉴权风险如实告知 | 页面没有登录与权限体系,对外监听等于对同网开放 | 配置注释与启动提示;处置建议是防火墙白名单或反向代理认证,不要直接挂公网 |
+
+### 14.2 脱敏与可上云面孔
+
+对外披露的内容走独立的一层:可上云面孔落在 outputs/场站/guanlan/cloud,由专门的面孔与页面与问答脚本生成;事实契约的每条结论带脱敏来源引用(文件名加内容摘要前 16 位与章节)与本条 sha256,因此"这句话出自哪份文件的哪一节、当时那份文件是什么内容"都可核对。脱敏实现分布在若干模块,口径可以概括为四层:一是外发消息的实体映射(16 条,把业主集团、整机厂、部件供应商、场站名与机型泛化,另有本机路径、电价与机群规模三条正则);二是产物与交付侧的公开词表(32 条替换词加 11 类独立回扫规则,替换与回扫分开写,并要求交叉自检"回扫认识每个替换词、替换后回扫干净",有残留即抛错拒绝出件);三是振动产物里唯一的字段级黑名单(8 个频率类键整值遮蔽,另有 17 条阈值或频率正则),交付件使用的是不看开关的强制脱敏版本;四是机组代号化(按定序摘要映射,不可由编号反推)。对外面孔生成后仍要回扫一次,命中本机绝对路径、邮箱、手机、身份证、精确坐标、密钥格式等任一类即返回 2 不出件;云问答出口同一口径,脱敏模块导入失败即拒绝启动。脱敏的边界是"对外面孔这一层做,内部产物不脱敏",两层由目录与生成端区分;机组号与机群台数在对外面孔里只报不拦,是否代号化由使用方裁决并写进面孔清单。需要如实说明的是,交付前另有一个安全核查器(密钥、个人信息、金额与业主名、内部件四类规则),但它目前尚未接入打包链,属手工步骤。
+
+### 14.3 日志与审计
+
+日志是审计的载体:长驻服务与启动器的日志落在 logs 组件名.log 并按统一行格式写;运维动作落在 logs/ops/ 并按保留策略清理;构建与摄入日志落在 logs/build/场站/ 下按产物子路径归档;机器审计流水落在 logs/audit/(模型调用流水 llm_audit.jsonl、云问答流水 cloud_qa.jsonl、术语审计 terms_audit.json)。统一格式之前的日志全部搬到 logs/legacy/ 留档,不悄悄删除。
+
+### 14.4 权限不足时的降级路径
+
+权限不足时不抛"访问被拒绝",而是给出可执行的替代路径,如表 14-2 所示。
+
+| 场景 | 无权限时的行为 | 可用替代 |
+|---|---|---|
+| 注册 Windows 服务 | 打印前置检查结果、逐条将执行的命令,返回码 3 | 管理员重试;先用 --dry-run 看它要做什么;改用登录自启(--user-login) |
+| 注销服务(卸载第②步) | 缺权限时照做其余步骤并打印手工命令,返回码 3 | 管理员/root 执行注销命令 |
+| 注册 systemd 单元 | 打印将写入的单元全文,不写入 | root 重试 |
+| 端口被占用 | 不静默重试,给出占用排查与改端口路径 | stop 后重启;按端口清理;改配置端口并同步网关路由表 |
+| 缺产物或缺模型 | 返回结构化缺件页或明确的降级提示,不伪造内容 | 放原始件后重算;补齐生成端;安装并拉取模型 |
+
+***
+
+## 15 可移植性与资源占用
+
+### 15.1 另一台机器零配置启动
+
+迁移到另一台机器的步骤如表 15-1 所示。要点是"程序与配置路径无关(全部相对安装根),但虚拟环境不能直接搬"——虚拟环境的配置文件绑定基础解释器路径,因此换机后跑一次安装即离线自足。
+
+| 步骤 | Windows | Linux 或 macOS |
+|---|---|---|
+| 1 解包 | 解压交付包到安装目录(目录不要带空格与中文) | 解包到目标目录(同样避免空格与中文) |
+| 2 安装 | 双击 install.bat(或调用 install.ps1) | sh install.sh(可用 GUANLAN_PY 指定解释器,--service 装 systemd) |
+| 3 自检 | 双击 check.bat 或跑 guanlan.py check | .venv/bin/python guanlan.py check |
+| 4 放数据 | 把原始件放进 data/raw/场站名/ 下并按源类目录落位 | 同上 |
+| 5 重算 | guanlan.py serve 后从门户「数据重算」触发,或直接跑 rebuild_all.py | 同上 |
+| 6 启动 | 双击"启动观澜"快捷方式(无窗口)或 start.bat | .venv/bin/python guanlan.py serve |
+
+安装根可用环境变量覆盖(路径模块优先读 WINDSCADA_ROOT),原始件根可用环境变量 `WINDSCADA_<代号>_SRC`(**变量名内写死样本场代号**,换场须改名,见 16.5 节)覆盖,场名可用 `WINDSCADA_FARM=<场>` 覆盖;配置里的路径一律写相对安装根的形式,因此换盘、换机不需要改任何路径。
+
+### 15.2 Python 与依赖 wheel
+
+解释器要求是 3.11 及以上(依赖 pandas 3,3.10 及以下没有轮子);机器上没有 3.11 时使用包内便携运行时(3.12.14,三个平台各一份)。依赖清单共 19 条固定版本:polars 1.42.0、pandas 3.0.3、pyarrow 24.0.0、numpy 2.5.0、pyyaml 6.0.3、python-frontmatter 1.3.0、matplotlib 3.11.0、plotly 6.8.0、jinja2 3.1.6、python-docx 1.2.0、docxtpl 0.20.2、python-dotenv 1.2.2、requests 2.34.2、rich 15.0.0、tqdm 4.68.3、openpyxl 3.1.5、scipy 1.18.0、xlrd 2.0.2,另有一条不锁版本的 tzdata(Windows 无系统时区库,而 pandas 3 硬依赖它)。离线件如表 15-2 所示(件数与体积为本次实测)。
+
+| 离线件目录 | 件数(实测) | 体积(实测) | 覆盖平台 |
+|---|---|---|---|
+| wheels/win_amd64 | 42 | 169.4 MB | Windows 64 位完全离线可装 |
+| vendor/python | 3 | 81.4 MB(含清单文件共 4 件) | Windows x86_64、Linux x86_64、Linux aarch64 便携运行时 3.12.14 |
+
+Linux 与 macOS 侧包内没有轮子时,安装脚本自动改为联网安装;要完全离线需把对应平台的轮子放进相应目录。模型离线件(Ollama 安装包与模型目录)单独交付,不在主包内;未装时问答不可用,其余页面不受影响。
+
+### 15.3 磁盘、内存与算力估算
+
+资源需求与实测体积如表 15-3 所示。内存与磁盘的硬性要求来自说明书(最低内存 16 GB、推荐 32 GB;磁盘最低 5 GB 加三维资产 1 GB,另加模型 30 至 60 GB);目录体积为本次递归实测。
+
+| 项 | 数值 | 出处 |
+|---|---|---|
+| 内存最低或推荐 | 16 GB 或 32 GB | 《使用说明书》v0.2(随包 docs/) |
+| 磁盘最低 | 5 GB 加三维资产 1 GB | 同上 |
+| 模型占用(可选) | 30 至 60 GB(固态盘) | 同上 |
+| 显存(只影响问答与本地审核) | 12 GB 及以上可跑默认档;24 GB 及以上可开升档;无显卡走纯 CPU 慢档 | 同上 |
+| data(原始件,本机实测) | 26,504 件、224.67 GB(其中振动原始导出 25,693 件、150.15 GB) | 本次递归实测 |
+| outputs(产物,本机实测) | 3,549 件、7,157.5 MB | 本次递归实测 |
+| release(发布件,本机实测) | 2,898 件、708.4 MB | 本次递归实测 |
+| 虚拟环境(本机实测) | 17,779 件、688.1 MB | 本次递归实测 |
+| 日志与配置(本机实测) | 日志 154 件、3.3 MB;配置 171 件、1.6 MB | 本次递归实测 |
+| 全仓不含版本库(本机实测) | 52,049 件、241,707.5 MB | 本次递归实测 |
+| 运行内存(本机实测快照) | 七个监听进程工作集合计 568 MB(网关 238.9、工作台 198.6、振动 53.8、本机模型 33.7、三维 15.4、四系统合页 13.9、仿真 13.7 兆字节);同批私有提交合计 3,293.7 MB | 本次实测快照,非稳态峰值,仅供量级参考 |
+| 交付包(口径) | 3,430 件、1,032.0 MB | 变更记录,非本次实测 |
+| 重算耗时(口径) | SCADA 侧 10 个构建器约 15 分钟;振动摄入按现场导出体量约 14 分钟(另有 25 分钟口径);变桨面约 10 至 12 分钟;窄仓一次扫约 2 至 3 分钟 | 代码注释与手册,多口径未统一 |
+| 单次完整重算(实测) | 本机一次带等价验收的重算 2,815.5 秒(约 47 分钟),该次以退出码 1 结束(末步等价验收因无随包基线一路属预期) | run/ops_job.json |
+| 按所选时间窗重算 | 判级约 25 秒每时间窗;七镜头约 9 至 44 秒每时间窗 | 服务注释 |
+
+算力方面的判断是:系统的重活集中在重算链(一次性、可后台跑),页面侧的实时计算只有按所选时间窗的分组与过滤,最重的两类已经用窄仓与进程内缓存压到秒级;模型问答是唯一可能占用数分钟的交互,且已按档位给预算并在超时后明确不出文。
+
+### 15.4 Linux 适配现状
+
+Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次环境未实测"或"已在本机验证"两类之一,不含估计。
+
+| 项 | 现状 |
+|---|---|
+| 安装脚本 | install.sh 已有,逻辑与 Windows 同构(选解释器、建虚拟环境、装依赖、写配置、可选装 systemd、写安装记录、自检) |
+| 便携运行时 | vendor/python 下已含 Linux x86_64 与 aarch64 两份 3.12.14 |
+| 依赖轮子 | 包内只有 win_amd64 轮子;Linux 需联网安装或自行放轮子 |
+| 服务化 | systemd 单元 guanlan.service 由注册器写入(简单类型、始终重启、停止超时 120 秒、混合杀进程模式、环境变量强制 UTF-8) |
+| 路径与换行 | 路径统一用 pathlib 与 POSIX 相对串;入口脚本按类型守行尾(.sh 用 LF) |
+| 进程与日志 | 无窗口子进程的口径在类 Unix 上是 0 标志位;日志行格式与保留策略平台无关 |
+| 未取证项 | Linux 安装、systemd 真装、国产化发行版首装均未在本次环境实测(历史交付件的 Linux 安装脚本曾有过行尾问题,已由编码守则与打包校验拦住) |
+
+***
+
+## 16 多场适用性与换场迁移
+
+### 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.2.docx》第 11 章(场配置化、机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、跨场统一口径、按场裁剪收资、换场验收检查表)与《数据要求说明_观澜_2.10.2.docx》第 12 章(通用必选与可选的判定规则、场配置字段对照、数据源形态适配、换场收资差异清单、按场裁剪步骤);本章给的是设计侧的落点、换场作业单与检查表,数据侧的收资口径与字段要求以数据要求说明为准,需求侧的验收项以需求分析为准。
+
+***
+
+## 17 已知边界与未实现
+
+### 17.1 逐条边界与处置建议
+
+本系统坚持"缺件如实"。边界、影响与处置建议如表 17-1 所示,每条的处置建议都是可执行的一条命令或一次催缴动作。
+
+| 边界 | 影响 | 处置建议 |
+|---|---|---|
+| 本机未装配原生 MDB 驱动 | 本机无 pyodbc 与 mdbtools,读 MDB 走 ACE 提供程序加 PowerShell(与写库同一套);若目标机未装该提供程序,MDB 形态不可读(未在无 ACE 的机器上实测) | 目标机安装 ACE 提供程序;或让现场导出 CSV(取数层本就 CSV 优先) |
+| 测风塔数据零交付 | 风资源与尾流类分析缺外部对照(收资要求第 3 项记为未到位) | 按收资要求催缴测风塔数据;到位后重算对应族 |
+| 油样 2026-07 批 102 行缺源件 | 油样索引少该批,等价验收会报"有需人工看的差异" | 现场补该份合并报告 PDF 后重算 ② 步 |
+| 远端未装 Ollama | 远端问答与升档不可用,模型探针必然 FAIL;其余页面不受影响 | 安装 Ollama 并把有网机器上的模型目录整体拷到目标机同位置;是否安装由用户决定 |
+| SOP 相关配置悬空 6 条 | 分析锁校验不可用;场景解析不可用(本包无调用方,不影响页面);案例判别器外键校验与模块键校验缺单源;经济性换算退回内置假设并在结果里标注 | 需要哪一场就建对应配置;或明确删除该模块的入口引用;逐条影响已登记在配置登记表的 known_missing |
+| 振动六层链扫描件与正本类产物 | 四步脚本齐全且关键两件已按口径重建,但"无样件可对拍"(链完整性检查自述) | 由研发给口径或提供样件做逐值对拍 |
+| 数据派生页面无生成端 | 脱敏状态一览与取数单是客户手里的交付件,性质上随运行状态变但没有指纹与生成端,重算不会刷新它们 | 已按冻结交付件管理并把"会变旧"写进登记理由;将来若要随数据变,再补生成端与来源指纹 |
+| 页面引用的四棵产物树不在包内 | 传动链回放、四系统判据、控制律仪表台、取数单等页面存在 19 条已知的引用悬空 | 保留记账不删(删了门户裂口、交付缺件);待该产物线随包后复检 |
+| 3 件内嵌快照缺指纹 | 只能按生成时间判断新旧,无法做逐字节陈旧检测 | 下次生成时把来源与来源摘要写进页面(机制已就绪) |
+| 无生成端的历史组级产物清单 | 趋势件、热链、扇区、偏航与润滑面等族的历史随包件没有生成端;当前反向审计显示盘上 3,548 件全部呼应成立、未归类 0,但清单本身仍在文档里留档 | 由研发补口径(照月度派生件的办法反推并逐值验证);或承认其为人工件不按产物管 |
+| 收资要求其余未到位项 | 故障录波仅 4 台、原生秒级高频数据未见交付、保护定值未成册、可行性研究与微观选址未交付、周围已建风电场报表未交付、大部件更换台账不完整 | 按收资要求逐项催缴;到位后按族重算 |
+| 本轮曾出现的两处门禁未通过(已修复留档) | 取证期间出现两个未通过:工作台依赖台账门返回 5(文档清单与现场不一致)、运行期可移植性门禁返回 1(源码里用系统分隔符拼库存字符串) | 两处都已按原口径修好并复跑转绿(依赖台账输出"文档与现场一致"、可移植性门禁输出"通过");留档的目的是提醒:这两处都不是靠人记得,而是被检查器跑出来的 |
+| 移植性大扫描仍报机器相关路径 | 第二个可移植性扫描器返回 1:290 处机器相关路径(HTML 烘入绝对路径 0 处),均在交付留档脚本、历史文档与注释里,不在运行路径上;同一份扫描同时确认虚拟环境绑定本机基础解释器路径,换机必须重跑一次安装 | 出包前把该扫描器纳入打包前置检查,或把这份清单当具名白名单维护并逐条写明理由;不要靠"人工记得它不影响运行" |
+| 交付前安全核查器未接入打包链 | 该脚本有四类规则(密钥、个人信息、金额与业主名、内部件)与严格模式退出码,但打包器、打包壳脚本里都没有调用它 | 在打包器里加一步调用(严格模式),命中即删包并返回非零;否则它只是手工步骤 |
+| 包内离线轮子只覆盖 Windows | wheels 下只有 win_amd64 一个平台目录(42 件、169.4 MB);Linux 与 macOS 安装会自动改为联网 | 要在国产化平台上完全离线安装,需在有网同类机器上取件后放进对应平台目录 |
+| 文档引用的模型离线包不在包内 | vendor 目录实测只有 4 件(清单文件加三份便携运行时),没有模型安装包目录;而说明书与安装脚本多处引用该目录 | 现场需要模型时单独交付模型安装包与模型目录;或在文档中明确"该目录随单独交付件提供" |
+| Windows 服务与 systemd 未在本次环境实测 | 本机未注册服务(状态报已注册为假、运行中为假),Windows 服务真注册与 Linux 单元真装未取证 | 用 --dry-run 预览、用免权限守护自证;具备管理员或 root 权限时执行注册并复核状态 |
+
+### 17.2 口径冲突与待统一项
+
+系统里仍有多处文档或代码口径不一致,如实列出如表 17-2 所示。这些不是"功能坏了",而是同一件事有两种写法,读者与维护方必须知道以哪一处为准。
+
+| 冲突项 | 两种口径 | 以哪一处为准 |
+|---|---|---|
+| 重算链步数 | 系统设计说明写 14 步、重算操作手册写 8 步、移植文档写 12 步,实测默认 26 步(加 --skip-scada 为 23 步) | 以 scripts/rebuild_all.py --dry-run 的实跑输出为准 |
+| 窄仓列数 | 文档写 46 列(温度均值 27 列),实测 48 列(温度均值 25 列,另补两列控制通道) | 以脚本 --check 输出与 _manifest.json 的 columns_n 为准 |
+| 窄仓一次扫耗时 | 一处写约 2 分钟,一处写约 3 分钟 | 以现场实测为准,两处都保留为口径区间 |
+| 变桨面耗时 | 一处写约 10 分钟,手册写约 12 分钟 | 同上 |
+| 振动摄入耗时 | 一处写约 14 分钟,放置指导写约 25 分钟(对应 153 GB 导出) | 同上(体量不同) |
+| M9 按时间窗耗时 | 服务注释约 5 秒每时间窗,版本记录约 0.6 秒每时间窗 | 以本机实测为准;两处已记入待统一 |
+| 平均无故障间隔(MTBF)台时口径 | 停机归因模块已改为实测台时,部件可靠性表仍用理想日历台时 | 需统一到实测台时(否则同一指标两个值) |
+| 网关离线守卫端口 | 守卫里硬编码判断旧端口 8033,而端口表里工作台是 18033 | 以 configs/serve.json 为准,守卫待同步 |
+| 前端一处外链端口 | 工作台一处外链硬编码 8030,该端口不在端口表内 | 以 configs/serve.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 为准(它同时是升档解析的第一顺位),服务端展示表待同步 |
+| 审计流水的字段清理 | 重试成功的那条记录里仍带着上一次尝试的失败原因 | 以字段语义为准(成功行不应带错误文本),生成端待修正 |
+| 检索索引文件形态 | 一处注释写索引落 json 与 npz 两种,而代码只写 json 与向量文件 npy,盘上也没有 npz | 以代码与盘上实物为准,注释待改 |
+| 本体对象库规模 | 设计文档写 9,613 个对象(2026-09-17 时点),盘上实测 9,791 个(2026-09-21 修改时间) | 引用规模时须注明口径日期,或重测 |
+| 安装说明的口径 | 说明书里仍写"所有服务只监听本机",与后来"门户监听所有网卡"的口径不一致;同一份说明书一处的"不含重算链"与另一处的"重算链已进包"互相矛盾 | 以 configs/serve.json 与 scripts/rebuild_all.py 的现状为准,说明书待修订 |
+
+### 17.3 未实现项
+
+以下三项是"设计上这样定,但功能未实现",如实列出:按所选时间窗的缓存没有过期时间与容量上限(进程内常驻、无淘汰、无持久化),长时间运行且频繁换时间窗时内存会持续增长;后台预热不覆盖 M9(只预热判级与默认时间窗的镜头);时间窗词表没有"今日、近 7 日、本月、本年"这类词(只有六个预设加单月、月区间、自定义起止)。此外判级矩阵本身只在内存里返回,不落 parquet 产物,因此"某一时刻的判级矩阵"没有独立留档件,只能靠当时的页面快照与日志。
+
+***
+
+## 附录 A 编写依据
+
+本文每章的主要来源文件如表 A-1 所示(章节与来源文件)。
+
+| 章节 | 来源文件 |
+|---|---|
+| 1 文档说明 | src/version.py(VERSION 与 HISTORY 的 2.10.2 条目)、docs/需求分析_观澜_2.10.2.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(路径约定与统一记录)、本次目录实测 |
+| 5 数据接入与重算链 | scripts/rebuild_all.py(--dry-run 实跑)、scripts/scada_slim_build.py 与 slim10min/_manifest.json、scripts/raw_scan.py、scripts/raw_data_check.py、scripts/place_raw_data.py、docs/输入数据放置指导_v0.1.md、src/windscada/scada_source.py、scripts/csv_to_mdb.py |
+| 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/<场>/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、《使用说明书》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 所示。
+
+| 仓库相对路径 | 职责 |
+|---|---|
+| guanlan.py | 启动器与自检:check 与 serve 与 status 与 stop 与 open 与 qa 与 version 七个子命令 |
+| src/paths.py | 路径唯一真源:安装根解析、各产物仓与配置与解释器助手;场名解析与按场落点(farm 与 store 与 contract) |
+| src/windscada/config.py | 场抽象层:场定义加载与必填键校验、场站目录扫描辨识与别名匹配、可用场与场切换 |
+| src/version.py | 版本唯一真源:VERSION 与版本规则与 HISTORY 与包名与安装记录读写 |
+| src/proc.py | 子进程统一口径:无窗口标志、后台起进程、前台短命令 |
+| src/logfile.py | 日志唯一口径:目录与命名与行格式与保留策略与标准输出包装 |
+| src/entry_refs.py | 入口引用闭合与入口脚本编码守则 |
+| src/derived_manifest.py | 由构建脚本自登记的派生产物清单 |
+| scripts/rebuild_all.py | 重算链编排(默认 26 步)与逐步退出码容忍 |
+| scripts/rebuild_from_raw.py | 三门台账与 SCADA 侧 10 个构建器的入口 |
+| scripts/scada_slim_build.py | 10min 公共列子集窄仓(按所选时间窗重算的底座) |
+| scripts/windscada_monthly_build.py | 月度派生件(与随包基线逐值对齐后才落盘) |
+| scripts/vib_raw_build.py | 振动侧一键摄入:时间窗索引与谱库与报告与逐台页与融合标量 |
+| scripts/pitch_face_build.py | 变桨面生成端(日粒度液压润滑柱塞与零位三口径月表) |
+| scripts/component_history_build.py 与 baseline_38_build.py | 振动在升与换件闭环、三层基线 |
+| scripts/sop_findings_from_ledger.py | 由重算台账生成事实契约 claim 族 |
+| scripts/guanlan_facts_contract.py | 事实契约构建与四类消费者派生件 |
+| scripts/products_restore_missing.py | 逐件来源台账维护(只记账不搬件) |
+| scripts/products_reverse_audit.py | 反向呼应审计与可逆性矩阵 |
+| scripts/pages_audit.py 与 configs/portal_pages.yaml | 页面归口登记与五条机器规则 |
+| scripts/config_audit.py 与 configs/registry.yaml | 配置登记与八条机器规则 |
+| scripts/raw_scan.py 与 raw_data_check.py 与 place_raw_data.py | 输入数据指纹扫描、放置体检与增量落位 |
+| scripts/windscada_serve.py | 工作台服务:路由族、按所选时间窗重算、pending 契约、页面烘焙调用 |
+| scripts/guanlan_gateway.py | 门户网关:前缀改写、data-abs 豁免、只读发布层、运维控制台 |
+| scripts/guanlan_ops.py 与 _ops_launch.py 与 _ops_run.py | 运维控制台后端、动作启动器与重算执行器 |
+| scripts/windcms.py 与 src/windcms | 振动诊断服务与报告、逐台页、工作台 |
+| src/windscada/taxonomy.py | 判级矩阵与系统归口与取严合并 |
+| src/windscada/perf 与 subsys | 曲线、控制、可靠性、停机、可用率、变桨、偏航、蓄能、温度、热链、结构、融合各面算法 |
+| src/ontology | 本体摄入六步、对象库、检索、MCP 只读工具、离线问答代理与校闸 |
+| install.ps1 与 install.sh 与 uninstall.bat 与 uninstall.sh | 安装与卸载入口 |
+| scripts/service_ctl.py 与 win_service.py 与 service_main.py 与 service_worker.py | 服务化注册器与服务体 |
+| scripts/pack_dist.py 与 pack.bat 与 pack.sh | 打包器、预演与开箱验证入口 |
+| scripts/delivery_docs_build.py 与 delivery_docs_figures.py | 交付文档渲染器与图表构建器(本版新增) |
+
+## 附录 C 术语与缩写
+
+本文使用的术语与缩写如表 C-1 所示。
+
+| 术语或缩写 | 含义 |
+|---|---|
+| 观澜 | 本系统的产品名(观澜 v2 风电场智能分析系统);样板指以样本风电场为依据的第一套落地实例 |
+| 产物 | 由原始件算出来、供页面与接口消费的件(parquet、json、md、html 等),只落在 outputs/场站/ 下 |
+| 产物仓 | outputs/场站/ 下的九个顶层目录(windscada、ontology、windcms、m5_cms_tcm、tcm_compatible_replay、sop、guanlan、pitch、paradigm_r1) |
+| 原始件 | 现场交付的输入数据,只读,落在 data/raw/场站/ 下 |
+| 重算链 | scripts/rebuild_all.py 串起来的构建步骤序列(默认 26 步) |
+| 窄仓 | 10min 公共列子集的逐台 parquet 仓,用于把按所选时间窗重算从分钟级降到秒级 |
+| 判级矩阵 | 逐台乘各系统的判级表,由各面判级取严合并得到 |
+| 四维证据 | 变桨与偏航等面内部用于取严合并的四个判据维度 |
+| 融合面 | 把振动、温度、润滑、油液四源对齐后逐台判级的分析面 |
+| 残月 | 样本覆盖度低于 0.5 的月份(如只有 6 天数据的月份),页面与报告均如实标注 |
+| 平均无故障间隔(MTBF) | 日历或实测台时扣除停机后除以事件数得到的平均无故障间隔 |
+| 平均停机间隔(MTBO) | 进一步扣除调度令停机后除以事件数得到的平均停机间隔 |
+| 单次停机时长(MDT) | 停机段时长的平均值 |
+| 时间窗 | 页面顶部可选的数据范围(预设段、单月、月区间、自定义起止日期,含两端) |
+| pending 契约 | 按所选时间窗重算未完成时,先回上一份口径并用 pending 标记明示,页面轮询再取的约定 |
+| 单一真源 | 同一事实只允许一处权威定义(路径、版本、端口、页面归口、配置登记) |
+| 质量门 | 重算链与自检里的审计检查点,每处都能独立重跑并给出退出码 |
+| 开箱验证 | 打包器对交付包做解压、安装、无窗口启动、页面可用、卸载的整链核验 |
+| 骨架 | 交付包里只带目录结构不带文件的输入数据目录骨架 |
+| MCP | 本体层的只读工具接口,供离线模型按纯函数取证 |
+| claim | 事实契约里的一条结论,带来源引用、本条摘要、时间窗与裁决 |
+| 校闸 | 问答结果的接地核查,不过闸不出正文 |
+| 升档 | 默认模型被校闸拦下后改用更强档位重答一次 |
+| 中间件 | SOP 中间件与评审落盘件(outputs/场站/sop) |
+| 冻结交付件 | 按交付版本发布、不随输入数据自动变的客户交付件 |
+| 场定义 | configs/farms/<场>.yaml,描述一个场的机组清单、源类目录与产物落点的配置对象(七个必填键) |
+| 场抽象层 | 把"场"做成可加载、可校验、可切换的配置对象的机制:配置层加路径层加语义层 |
+| 场无关引擎 | 换场不需要改动的部分:判级与算法结构、融合纪律、本体与检索、服务与前端、审计门 |
+| 场相关参数 | 换场必须重新给或重新标定的部分:阈值、机型参数、时间窗锚点、机组清单、术语库与代码表、坐标件 |
+| 换场作业单 | 从准备场配置到页面与门禁验收的逐步命令清单,每步带命令与退出码语义 |
+| 场站目录辨识 | 用 raw_station 与 src_farm_names 把 data/raw 下的目录认成本场的四段式判定(raw_station 与 alias 与 single 与 none) |

+ 928 - 927
docs/src/需求分析_观澜_2.10.1.md → docs/src/需求分析_观澜_2.10.2.md

@@ -1,927 +1,928 @@
-# 需求分析 · 观澜 v2 风电场智能分析系统 · 版本 2.10.1
-
-## 1 文档说明
-
-### 1.1 目的
-
-本文是「观澜 v2(风电场智能分析系统)」的**需求分析文档**,回答四个问题:这套系统究竟要满足谁的什么需要;这些需要从哪来、经过哪些版本变成现在的样子;每一条需要对应什么功能、什么输入、什么输出、拿什么判据验收;以及哪些事本版明确不做。本文与同批交付的《设计说明_观澜_2.10.1.docx》《数据要求说明_观澜_2.10.1.docx》配套:本文讲「要什么、凭什么算做到了」,设计说明讲「怎么做的」,数据要求说明讲「要哪些数据、什么形态、什么单位」。
-
-本文的写作口径是**只写能取证的事实**:每个数字、每条结论都能指到仓库里的某个文件、某次实跑输出或某条版本记录;查不到、取不到的一律写明「未取证」或「未到位」,不做推测性补全。因此文中会出现少量「未取证」的说明句,那是刻意留下的诚实边界,不是遗漏。
-
-### 1.2 读者与用法
-
-本文面向四类读者:场站侧的运行、点检、管理人员用它核对「系统答应做的事是否都做到了」;研发与交付人员用它做需求变更的基线(改需求先改本文的 FR 编号与验收判据);验收与审计人员用第 9 章的门清单与退出码语义逐条核对;新加入项目的工程师用第 2、3 章在半小时内建立全局认识。
-
-需要提醒的是:本文不是操作手册。放数据、重算、安装、卸载的具体命令在《重算操作手册》《输入数据放置指导》《README_先读我.MD》里,本文只在功能需求条目里给出判据与依据文件的位置。
-
-### 1.3 版本对应关系
-
-系统版本号只有一个真源:仓库内的 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.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() |
-| 本次变化级别 | 小(patch,交付文档对外化:去样本场标识与新增多风电场适用性章节,系统功能未变) | HISTORY 中 2.10.1 条目的 level 字段 |
-
-本版相对 2.10.0 的实质变化是**交付文档对外化**:三份交付文档(需求分析、设计说明、数据要求说明)全文不体现具体风电场(去标识化),并新增多风电场适用性一章;系统功能未变,属交付物修订。依据用户令原文:「修改三份文档,内容参考样本风电场,但不体现样本风电场,且具有不同风电场适用性」(引用时把样本场名按去标识化口径写成「样本风电场」)。
-
-### 1.4 编写依据与取证口径
-
-本文的取证方式分四种,文中出现数字时都能归到其中一种:**读源**(读代码、配置、登记表)、**读文档**(读仓库内已交付的设计与操作文档)、**实跑**(跑只读的自检与只读的审计脚本)、**实测计数**(对 data 与 outputs 目录做文件级计数与求和)。为避免把「文档里写过」当成「现在就是这样」,凡与现状有关的数字,本文优先采用实跑与实测计数,并在文中标注日期。本文出现的全部实测数字(件数、体量、行数、日期覆盖、耗时、退出码等)都出自**样本场实测(2026-09-22)**:保留它们是为了说明判据与量级,换场后必须按新场重新标定与重跑,不能直接当作新场的预期值——第 11 章的多风电场适用性要求正是围绕「结构不变、参数与数据随场变」写的。
-
-表 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/<场> 递归计数(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 术语与描述口径约定
-
-本版全文遵守三条写作口径,它们是用户令落成的硬要求,也是机器清扫过的(依据 docs/系统设计说明.md §17.1):**第一**,描述里含「窗」且确实指时间窗口的,一律写全「时间窗」——预设时间窗、判级时间窗、证据时间窗、随所选时间窗、时间窗内;而领域词里的天气窗、作业窗、预览窗、观测窗保持原样,不误改。**第二**,影响的是风电机组时统一写「影响机组」或「影响机组数」,不使用以「台」计数的旧写法;数据字段名里的旧写法暂留以兼容数据契约,显示层统一。**第三**,用英文简写时必须写成「中文(英文简写)」,例如平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);英文映射的键同步改,否则英文页会回落中文。
-
-表 1-3 三条描述口径与落地位置
-
-| 口径 | 要求 | 落地与证据 |
-|---|---|---|
-| 时间窗 | 含「窗」且指时间窗口者写全「时间窗」 | docs/系统设计说明.md §17.1;v2 页面顶栏控件与图注均写「随所选时间窗」 |
-| 影响机组 | 影响风电机组时写「影响机组」 | 表头与说明含报告构建器;数据字段名暂保留 |
-| 英文简写 | 中文(英文简写) | 平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);i18n_en 键同步 |
-
-另外,本文出现的台号(如 WTG01 至 WTG38 一类写法)是**样本场内部编号**,保留编号形态只是为了与产物、台账和依据逐条对得上;换场时按实际台号替换,编号本身不构成接口约定。
-
-术语的完整解释见附录 B。
-
-## 2 系统概述
-
-### 2.1 本系统是什么
-
-本系统(观澜 v2,风电场智能分析系统)是一套**纯离线的海上风电场智能分析单包系统**,对象是**样本风电场(下称「本场」)**的 38 台 4.0 MW 级海上机组;机型代号与主机制造商(OEM)名称从略,业主单位名称从略。它把现场给来的运行数据、状态监测、报警、工单、油样落到每台机组的正确部件上,给出带证据、带边界、带责任人的检修结论;**判断在代码里,本机模型只负责问答、解释与引用,不发明结论**。软件不含任何联网功能,也不需要现场系统配合改造:原始件从 data/raw 读入,产物落在 outputs 下,页面只读产物,产物由数据算出来。
-
-一页话术(给第一次接触的人的三十秒版本):这套系统不控制风机、不下发指令、不改现场任何系统。它做的是「把现场已有的数据读进来,算出一份能说清依据的体检报告,然后用一个门户和三个服务把它讲清楚」。放数据之前,页面会如实显示「无产物」并告诉你缺哪一件、怎么补;放了数据之后,一条命令重算,页面立刻有数。所有结论都能往回指到源件与算法,指不到的地方它会写「不可判」或「缺件」,而不是编一个数字。
-
-### 2.2 部署形态与运行边界
-
-交付形态是**离线单包**(EDITION = offline-single-package):一个安装目录里同时是程序、配置、交付件与数据入口。安装目录不要带空格与中文。程序侧有 6 个常驻服务,全部由安装目录下的启动器或系统服务托管;组件的内部接口只监听本机回环地址,只有门户网关按配置可对外监听。
-
-表 2-1 常驻服务与端口(依据 configs/serve.json 与 guanlan.py 内置默认,两者口径一致)
-
-| 服务 | 默认端口 | 监听地址 | 作用 |
-|---|---|---|---|
-| 门户网关 | 28084 | host 与 public_host(出厂 public_host 为 0.0.0.0) | 门户单文件页、路由与改写、运维控制台、/healthz、/api/version |
-| 分析工作台 | 18033 | host(127.0.0.1) | 十个标签的分析工作台与全部 /api 取数 |
-| 振动诊断 | 18020 | host(127.0.0.1) | CMS 振动报告页、逐台页、工作台页、谱图 |
-| 仿真与回放 | 18791 | host(127.0.0.1) | 整机联调仿真台静态资产 |
-| 仿真四系统合页 | 18792 | host(127.0.0.1) | 从冻结资料包读取的四系统合页 |
-| 三维拆装工作台 | 64292 | host(127.0.0.1) | 三维资产页 |
-| 本机模型(可选) | 11434 | 本机 | 本地大模型推理服务,只影响问答与本地初筛 |
-
-对外只有一个入口 28084。这不是省事,而是边界:页面没有鉴权,把组件直接暴露出去等于把全场的运行数据摊开。要对外访问时的正确做法是限来源(防火墙白名单或反向代理加认证),或把 public_host 改回空串只用本机加隧道。
-
-### 2.3 系统能力清单
-
-表 2-2 系统能力清单(能力域、给谁用、载体、验收锚点,样本场实测(2026-09-22))
-
-| 能力域 | 能力 | 主要载体 | 验收锚点或判据 |
-|---|---|---|---|
-| 判级 | 38 台乘九系统判级矩阵,四轴随时间窗重算 | 工作台系统矩阵、逐台页、问题页 | 判级报警机组数随所选时间窗变化;样本不足如实写不可判 |
-| 部件问题 | 部件可靠性表、问题下钻、完整依据 | 工作台 component 与 vibration 标签 | 平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT)随所选时间窗变化;依据展开永不为空 |
-| 发电性能 | 七镜头曲线、七张月度时序图、控制参数一致性 | 工作台 energy 与 generation 标签 | 图注写「随所选时间窗」;样本量随时间窗变化;控制参数按所选时间窗重算 |
-| 振动 | 振动窗索引、谱库、CMS 报告、逐台页、在升与换件闭环、三层基线 | CMS 服务、工作台 vibration 标签 | 振动窗 w0316 索引 2,066,686 行;谱 420,742 条;缺件时如实空表并写边界 |
-| 台账 | 报警、工单、油样三门台账与停机事件 | 工作台 fault 标签、数据层维护页 | 报警 39,211 行、工单 5,876 行、油样 404 行 |
-| 本体与问答 | 对象库、检索索引、决策链、趋势证据、问答与校闸 | 工作台 assistant 与 decision 标签 | 本体审计 0 问题;两档模型都拦则不出文并给原因 |
-| 报告 | CMS 标准模版报告、事实契约与派生、门户结论段 | 报告页、门户、qc 派生件 | 契约改一条 claim 则四个消费端全变 |
-| 运维 | 一条命令重算、控制台按钮、清产物、数据扫描与看门狗 | 门户数据重算页、运维控制台 | 默认 26 步计划;并发动作返回 409;扫描退出码 0 与 4 与 5 |
-| 门户与检索 | 单文件门户、页面归口与陈旧检测、知识检索 | 门户、页面归口登记表 | 归口审计退出码 0;页面陈旧报 7 |
-| 数据接入 | 落位、体检、增量三清单、场站辨识 | 命令行脚本与维护页 | 结构合规;冲突拦下;场站辨识四条规则 |
-| 交付与运维 | 打包、开箱验证、服务化、版本检查、卸载 | install、pack、uninstall 入口与服务注册器 | 入口引用闭合 48 条;卸载入口 2 个;版本记录与代码一致 |
-
-## 3 需求来源与演进
-
-### 3.1 需求来源的三条线
-
-需求从三条线汇拢。**第一条是现场运行的需要**:值班要一眼看清全场哪几台机组有问题,点检要顺着问题找到部件与证据,管理要一份能对外的结论。**第二条是交付与运维的需要**:机器上没有网络、现场不会敲命令、数据每月增量、程序要能装能卸能升级。**第三条是用户令**:每一次人工测试或现场事故之后,用户用一句话把要求定下来(例如「所有的计算均要形成观澜的源代码」「对 data/raw 下文件的增减做到监听」「增加自定义时间段」),研发把它落成代码、机器守卫与文档。
-
-三条线里,用户令是最硬的驱动力:它带来的是**可核验的验收判据**,而不是一段描述。本版 13 条版本记录里,绝大多数条目的形态都是「检查到的问题 → 根因 → 解决 → 验证」,其中「验证」一栏写的就是实跑的退出码与逐值对拍结果。
-
-### 3.2 需求演进时间轴
-
-表 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) |
-| 2.9.0 | 2026-09-21 | 时间窗真正生效:部件问题与发电性能随所选时间窗重算,新增自定义起止日期时间窗 | 中(minor) |
-| 2.8.2 | 2026-09-21 | 扫描器的变化判据被两类非摄入件常年占住:新増上游归档口径与忽略模式 | 小(patch) |
-| 2.8.1 | 2026-09-20 | 台账等价验收在全新机器上必然非零:改为容忍并写明「没做验收」 | 小(patch) |
-| 2.8.0 | 2026-09-20 | 输入数据自动扫描识别:逐族指纹发现新增与变化,指明该跑哪几步并纳入重算 | 中(minor) |
-| 2.7.0 | 2026-09-19 | 远程部署消缺:振动窗发布被占用不再打断重算链;门户可对外监听;控制台打印不炸 | 中(minor) |
-| 2.6.0 | 2026-09-19 | 所有的计算均要形成观澜的源代码:融合面、总览页、事实契约、发布层、变桨面、振动基线与在升全部自算 | 中(minor) |
-| 2.5.0 | 2026-09-17 | 卸载闭环、版本管理与打包命名规则、版本记录 | 中(minor) |
-| 0.4.0 | 2026-09-17 | 打包默认不含输入数据与产物与日志与临时文件;无窗口启动;统一配置与日志;页面归口;服务化 | 旧编号(legacy) |
-| 0.2.0 | 2026-09-01 | 含产物的旧交付基线,历史上用作「包内无生成端」那批产物的补救源 | 旧编号(legacy) |
-
-需求的演进如图 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.10.1 在这条线上再走一步:文档去样本场标识并补上多风电场适用性一章,使同一份文档能被别的风电场直接拿去用。
-
-### 3.3 需求如何被人工程序化
-
-用户令通常是一句话,落到系统里要经过「变成判据、变成机器守卫、变成可复核证据」三步。第一步最关键:把一句中文要求翻译成一个**可判定的条件**。例如「时间窗变化时部件问题要跟着变」,翻译成「给定时间窗 w,判级矩阵的返回必须随 w 而变,且月度件按月取整、日粒度件按日精确」,这样才有办法验。第二步是把它做成每次都会跑的守卫,而不是靠人记得。第三步是留下可复核的证据(日志、退出码、逐值对拍结果、sha256)。
-
-表 3-2 用户令到程序化落点的对照(节选,按版本递进,样本场实测(2026-09-22))
-
-| 用户令(原话要点) | 程序化落点 | 可核验证据 |
-|---|---|---|
-| 部件问题与发电性能应随下拉框值变化,并增加自定义时间段 | months_of(win) 与 win_range 与 in_win;taxonomy.system_matrix(span=);reliability.overview(span=);/api/curves?win= | 变桨报警 15 与 13 与 10、偏航 15 与 11 与 5、齿轮箱 7 与 4 与 4 分别对应 2026年与 2025H2 与 2026-07 |
-| 对 data/raw 含嵌套子目录下文件的增减做到监听 | scripts/raw_scan.py 逐族指纹(件数、体积、最新落盘、清单摘要、子目录清单);重算链 ①b 步;rebuild_all --auto | 实测 --check 由 rc=4 转 rc=0(与上次快照一致);空目录新建也能发现 |
-| 所有的计算均要形成观澜的源代码 | 逐族补生成端:变桨面、在升闭环、三层基线、融合面 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.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 条目;本版三份文档 |
-
-## 4 用户与场景
-
-### 4.1 角色
-
-系统没有账号体系,角色划分是**使用场景的划分**,不是权限的划分。这一点必须在需求层面写清:当前版本没有页面鉴权,谁打开浏览器谁就能看到全场的运行数据;对外可达时的风险控制靠网络层(防火墙白名单、反向代理加认证、只绑内网地址)而不是靠系统内部。角色表的价值在于把「谁要什么」变成可核对的功能覆盖清单。
-
-表 4-1 角色与核心诉求
-
-| 角色 | 典型身份 | 核心诉求 | 主要页面与入口 | 对应功能需求 |
-|---|---|---|---|---|
-| 值班运行 | 场站值班员、集控值班员 | 一眼看清全场状态;报警是否集中;有没有正在恶化的机组 | 门户总览、系统状态、工作台 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-52、NFR 全部 |
-
-### 4.2 场景到任务到页面
-
-场景不必多,八个够覆盖日常。每个场景都要能回答「谁、在什么时刻、要完成什么、打开哪一页、看到什么算成功」。这一步是需求分析里最有用的动作:它把抽象的「系统要好用」变成「点开这页三秒内能看出哪几台报警」。角色、场景与页面的关系如图 2-1 所示。
-
-表 4-2 场景到任务到页面
-
-| 场景 | 任务 | 页面或入口 | 成功的样子 |
-|---|---|---|---|
-| 交班巡检 | 看全场状态与报警集中度 | 门户总览、系统状态、数据重算页 | 一屏内看清哪几台报警、服务是否在跑 |
-| 例行点检 | 逐台看判级与四轴 | 工作台系统矩阵、逐台页 | 点台号即出该台各系统判级与依据 |
-| 异常追因 | 从报警下钻到部件与证据 | 问题页、部件问题页 | 依据展开有部件、链路进度、卡点、证据源 |
-| 振动专题 | 看振动判级、谱图、在升与换件闭环 | CMS 服务、工作台 vibration | 六层判读清楚,未闭环时明写「没有成对记录」 |
-| 时间窗对比 | 换一个时间窗看判级与性能是否变化 | v2 顶栏时间窗与两个日期输入框 | 图注写「随所选时间窗」;样本量随之变化 |
-| 月度汇报 | 出结论与报告 | 汇报纸、CMS 标准模版报告、门户结论段 | 结论带证据级、审级与处置措施 |
-| 数据接入 | 放新数据、体检、重算 | 命令行落位与体检脚本、运维控制台 | 冲突被拦下;重算结束出每步 OK 与耗时 |
-| 交付与升级 | 打包、开箱验证、装服务、卸载 | pack 与 install 与 uninstall 入口、服务注册器 | 版本检查提示正确;卸载后数据一件不少 |
-
-![图 2-1 角色—场景—页面关系](figures/fig-req-02-角色场景页面.png)
-
-### 4.3 角色与可用性边界
-
-与角色相关的一条硬需求是「**没有产物也要能打开页面**」。现场第一次装完、或者在控制台上点了「清除产物」之后,页面必须仍然可访问,并明确说出缺哪一件、怎么补,而不是白屏或掐断连接。这条需求来自两次现场事故:振动服务在无产物时曾因空表拼接抛异常带走进程,工作台在缺件时曾直接返回 500 让人分不清「没放数据」还是「程序坏了」。现在的口径是:缺件时返回结构化缺件页与 err=no_source,页面写清缺件与补齐办法。相关功能需求见 FR-33 与 NFR-06。
-
-## 5 功能需求
-
-### 5.1 功能需求编号与总览
-
-功能需求按十二个域组织,编号从 FR-01 到 FR-52,共 52 条。编号是**稳定的**:新增需求往后排号,不重排已有编号;需求作废时保留编号并标注作废,不改用它。每条需求都给出五项:需求描述、输入、输出、验收判据、依据文件。其中「验收判据」是可判定的条件,「依据文件」指向仓库内的源或文档,便于复核。
-
-表 5-1 功能需求域与编号分配
-
-| 域 | 编号区间 | 条数 | 一句话概括 |
-|---|---|---|---|
-| 判级与部件问题 | FR-01 至 FR-05 | 5 | 38 台乘九系统判级、四轴随时间窗重算、部件问题与依据 |
-| 时间窗与自定义区间 | FR-06 至 FR-08 | 3 | 时间窗词表、自定义起止日期、按时间窗重算的等待契约 |
-| 发电性能与可靠性 | FR-09 至 FR-12 | 4 | 七镜头、月度时序、控制参数一致性、损失与功率曲线 |
-| 振动与融合面 | FR-13 至 FR-17 | 5 | 摄入、报告、融合判级、在升闭环、三层基线 |
-| 停机与台账 | FR-18 至 FR-21 | 4 | 报警、工单、油样、停机与损失 |
-| 本体与问答 | FR-22 至 FR-25 | 4 | 知识层、铺开、决策链与趋势、问答与校闸 |
-| 报告导出 | FR-26 至 FR-28 | 3 | 标准模版报告、事实契约与派生、门户结论段与汇报纸 |
-| 运维控制台与重算 | FR-29 至 FR-32 | 4 | 一条命令重算、控制台、清产物、数据扫描 |
-| 门户与检索 | 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,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-01 | 给出 38 台机组乘九系统的判级矩阵,逐台逐系统带判级与依据,四轴判级随所选时间窗重算 | scada_10min 与 scada_1min 序列、报警台账、振动报告转录、机型契约 | 工作台系统矩阵、逐台页、问题页;taxonomy.system_matrix 结果 | 判级报警机组数随所选时间窗变化(实测变桨 15 与 13 与 10、偏航 15 与 11 与 5、齿轮箱 7 与 4 与 4);样本不足时如实写不可判 | docs/系统设计说明.md §16.2;src/windscada/taxonomy.py |
-| FR-02 | 判级四轴(变桨、偏航、蓄能、温度)随时间窗重算,其中变桨按日粒度精确切片、其余走窄仓重算 | 窄仓 slim10min 公共列子集;pitch 面的日粒度件;1min 桨距角 | 各面 registry(span=) 结果与判级单元格 | 同一台同一系统在不同时间窗下可给出不同判级;温度面的对照基线 ref 与 ref_sm 故意固定并在页面标注 | docs/系统设计说明.md §16.2 与 §16.3;scripts/scada_slim_build.py |
-| FR-03 | 部件可靠性表按所选时间窗重算,给出平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT)与停机时长 | 停机事件、报警事件(均为日粒度) | 可靠性表与部件问题页表格 | 停机事件与报警按时间窗精确过滤;三个指标随时间窗变化 | docs/系统设计说明.md §16.2;src/windscada/perf/availability |
-| FR-04 | 部件问题下钻:问题页与问题接口按中文系统名取数,服务端做双向归一,认不出时如实报「系统标识无法识别」 | 台号、系统标识(中文名或前端标识或大小写变体)、时间窗 | 问题页、/api/problem 响应 | 传前端标识与中文名都能取到同一份问题;认不出时不写「本时间窗无异常」这类假陈述 | docs/系统设计说明.md §17.2;scripts/windscada_serve.py 的 sys_norm() |
-| FR-05 | 需要关注的卡片必须给出完整依据,依据按本行字段自组句,永不为空 | 融合面链盘行、SCADA 关注集合、部件与链路进度 | 卡片依据文本 | 实测曾出现依据空白(WTG09 齿轮箱),现行口径为按部件、链路走到第几步、卡在哪一步、证据源自组句 | docs/系统设计说明.md §17.3;HISTORY 2.9.1 |
-
-### 5.3 时间窗与自定义区间
-
-时间窗是本版需求里最容易被误解的一块,因此口径要写死两句话:**月度件按月取整,日粒度件按日精确**;页面上要写明这两句,不让读者以为月度均值能切到天。
-
-表 5-3 时间窗与自定义区间(FR-06 至 FR-08,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-06 | 提供预设时间窗词表:近30日、近90日、2026年、2026H1、2025H2、全程、单月、月区间 | 用户选择 | months_of(win) 与 win_range(win) 结果 | 每种写法都能落到覆盖的月集合与起止日期;近30日在末月只有 1 个节拍时如实按样本不足处理 | docs/系统设计说明.md §16.1 |
-| FR-07 | 新增自定义起止日期时间窗,写法为起日期~止日期,**含两端**;顶栏提供两个日期输入框与「应用」并校验起不晚于止 | 两个日期输入框 | 与区间相交的月集合(月度件)、原样的日区间(日粒度件) | 非法输入被拒绝并给出提示;相交月取整与按日精确两套口径同时生效 | docs/系统设计说明.md §16.1;HISTORY 2.9.0 |
-| FR-08 | 按时间窗重算一律「命中进程缓存即回,未命中先回等待状态并由页面轮询」,绝不静默拿旧口径当新时间窗 | 时间窗参数、进程缓存、后台重算线程 | 判级矩阵与七镜头结果、等待与构建中的状态标记 | 判级约 25 秒每个时间窗、镜头约 10 至 45 秒每个时间窗;等待期间页面写明「下面这几张卡还是上一份口径」;选到 2025-07 至 2025-12 时直接用正式产物不重算 | docs/系统设计说明.md §16.4;docs/系统设计说明.md §16.2 |
-
-### 5.4 发电性能与可靠性
-
-表 5-4 发电性能与可靠性(FR-09 至 FR-12,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-09 | 发电性能七镜头曲线随所选时间窗重算,曲线图注写明随所选时间窗且样本量随时间窗变化 | 窄仓 10min 子集、风速与功率通道、限电命令 | /api/curves 响应与七张镜头图 | 换时间窗后分箱结果变化,样本量变化;限电前干净判别窗(2025-07 至 2025-12)走正式产物 | docs/系统设计说明.md §16.2;HISTORY 2.9.0 |
-| FR-10 | 发电性能七张月度时序图随时间窗过滤 | 月度派生件 | 七张月度时序图 | 图注写「随所选时间窗」;月度件按月取整 | docs/系统设计说明.md §16.2 |
-| FR-11 | 控制参数一致性随时间窗重算,输出功率封顶与转速上限等一致性结论 | 窄仓补入的控制参数列(窄仓共 48 列) | 控制参数一致性卡片 | 按时间窗重算约 0.6 秒;功率封顶中位实测由 4,190(干净时间窗)变为 4,181.7(2026年)与 4,177.5(2026Q1) | HISTORY 2.9.0;docs/系统设计说明.md §16.3 |
-| FR-12 | 损失、功率曲线与可用率派生件由原始件重算,替换随包件时逐值对拍 | scada_10min 与报警台账 | loss_monthly、powercurve_bins、powercurve_dev、stop_events 等 | 实测损失月表 3,729 行、功率曲线偏差 38 行、功率曲线分箱 912 行与基线逐值一致 | docs/数据目录结构与落位约定_v0.2.md §4 |
-
-### 5.5 振动与融合面
-
-振动这一域的写法要特别克制:它是全系统里**人工判断成分最多**的一段,因此需求必须区分「测量数据的函数」与「需要人工裁定的件」,不能把后者当成自动产物来承诺。
-
-表 5-5 振动与融合面(FR-13 至 FR-17,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-13 | 振动窗索引与谱库摄入:解析厂家导出文件,按振动窗落索引表与谱分片,并登记清单 | windcms 下的解码导出文件(25,693 件) | 振动窗索引表(54 列)、谱分片、谱元数据、摄入清单 | 振动窗 w0316 索引 2,066,686 行、谱 420,742 条与 1,712 分片;重复摄入同名时间窗默认替换并留档旧窗 | docs/系统设计说明.md §13.1;docs/数据目录结构与落位约定_v0.2.md §2b |
-| FR-14 | 生成 CMS 标准模版评估报告与逐台页、工作台页,缺融合级时如实写破折号 | 振动窗索引、标量缓存、三层基线 | 报告 md 与 docx、逐台页、总览页、登记 csv | 38 台判级齐全(实测报警 3 台、优秀 35 台);缺融合级列时写破折号而不是编造 | docs/系统设计说明.md §13.1;HISTORY 2.6.0 |
-| FR-15 | 融合面判级:由链盘、手交件与模型过闸线生成融合表与融合级,作为报告与判级矩阵的输入 | 振动窗索引、模型过闸线、厂家报告转录 | 融合表、handoff、全场标量 z 值 | 融合表 38 台逐台有裁决;handoff 与手交件同结构且标注由观澜自算 | docs/系统设计说明.md §13.1 与 §13.6 |
-| FR-16 | 在升与换件闭环证据:逐振动窗标量算在升判据,给出检出、换件、复测回落的成对记录 | 振动窗索引、融合面链盘 | 部件历史件、趋势证据对象 | 时间窗数不足时如实出空表并写明数据边界;现场提交的数据包里没有成对记录时文案写「暂无端到端闭环证据」 | HISTORY 2.6.0 与 HISTORY 2.9.1 |
-| FR-17 | 三层基线:自基线、机群基线与绝对限,用于振动判级的对照 | 振动窗索引标量、健康期分段 | 三层基线件 | 健康期分段中位与正常带按件算;绝对限引用标准限值;样本不足时如实空表 | docs/系统设计说明.md §13.1 |
-
-### 5.6 停机与台账
-
-表 5-6 停机与台账(FR-18 至 FR-21,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-18 | 报警台账摄入,按事件键集合并集去重,归属取最窄源件;源件删改后产物随之变化并大声报出 | 故障报警导出件(16 件) | 报警台账 | 39,211 行稳定;同一事件不重复计数;删一个源件不会让总数虚增 | HISTORY 2.8.0;docs/系统设计说明.md §13.1 |
-| FR-19 | 工单台账摄入,按固定模板解析(第三行为列名,数据自第四行起),字符串化口径与随包件一致 | 风机故障记录(137 件,其中台账 123 件) | 工单台账 | 5,876 行;与基线差异逐条可归类 | docs/系统设计说明.md §13.1 |
-| FR-20 | 油液化验索引,从文件名抽取台号、部件、日期与样品号,不读正文;采合并语义,认不出的历史行原样保留 | 油样报告(404 件) | 油样索引 | 404 行;2026-07 批 102 行的源件不在现场包内,清空重建会缺这 102 行,如实记账 | docs/系统设计说明.md §7 与 §13.1 |
-| FR-21 | 停机事件与停机损失:由报警与运行状态派生停机记录并绑定损失,作为可靠性表的输入 | scada_10min、报警台账 | 停机事件件、损失月表 | 停机事件按台落盘;损失月表与输入跨度对拍通过 | docs/系统设计说明.md §13.1;docs/数据目录结构与落位约定_v0.2.md §4 |
-
-### 5.7 本体与问答
-
-表 5-7 本体与问答(FR-22 至 FR-25,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| 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/说明书_观澜…v0.2.md §4.2;HISTORY 2.9.1 |
-
-### 5.8 报告导出
-
-表 5-8 报告导出(FR-26 至 FR-28)
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-26 | 生成 CMS 标准模版评估报告,三轴表述:状态等级、证据状态、行动等级建议;同时出登记表 | 振动窗索引标量、三层基线、融合级 | 报告 md 与 docx、登记 csv | 38 台逐台有条目;报告章节可跳转;相对判据封顶为候选 | docs/系统设计说明.md §13.1;HISTORY 2.6.0 |
-| FR-27 | 事实契约:每条结论含结论编号、脱敏来源引用(文件加 sha16 与章节)、本条 sha256、时间窗、聚合层级与裁决枚举;派生四个消费端 | 由重算台账生成的 claim、评审底稿 | 契约件与门户结论段、工作台卡片、问答引用、报告摘要 | 实测改一条 claim 则四个消费端全变;契约自检不过时报退出码 2 并需人看 | docs/系统设计说明.md §13.1 与 §13.2 |
-| FR-28 | 门户结论段重装与汇报纸导出:产物变化后门户结论段随重装刷新;工作台可导出汇报纸 | 契约派生件、门户外壳与内嵌件 | 门户单文件页、汇报纸 | 装配先写临时文件再原子替换,失败不破坏在服务的门户;行尾强制 LF;产物挪走时缺该段属预期 | docs/数据目录结构与落位约定_v0.2.md §6;docs/系统设计说明.md §13.2 |
-
-### 5.9 运维控制台与重算
-
-表 5-9 运维控制台与重算(FR-29 至 FR-32)
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| 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/说明书_观澜…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 门户与检索
-
-表 5-10 门户与检索(FR-33 至 FR-35)
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-33 | 门户为单文件装配产物,壳与件分离;顶部菜单含总览、系统架构、方法、经验发现、案例、振动与 CMS、仿真与回放、交付文档、系统状态、数据重算、登录 | 受管外壳、内嵌交付件、契约派生件 | 门户单文件页 | 拆装回到逐字节相同;行尾 LF 有自检;数据重算页用内嵌版懒加载避免空转轮询 | docs/系统设计说明.md §5.2;docs/数据目录结构与落位约定_v0.2.md §6 |
-| FR-34 | 页面归口:登记每一页与子页的内容类型、是否随输入数据变、凭什么这么判;对数据派生页面做陈旧检测 | 页面登记表、产物台账、页面实物 | 归口表与审计结论 | 实跑检查 21 项、不一致 0、已知缺口 19;陈旧检测报退出码 7;未登记页单独列出 | configs/portal_pages.yaml;scripts/pages_audit.py |
-| FR-35 | 检索索引:由本体与知识层构建检索索引,词法检索在任何机器上可用,向量检索在装有嵌入模型时启用 | 对象库、CMS 知识库、嵌入模型(可选) | 检索索引件 | 无模型时保持纯词法可用并写明;索引缺失时页面如实说明而不是报错 | docs/数据目录结构与落位约定_v0.2.md §4;docs/系统设计说明.md §13.1 |
-
-### 5.11 数据接入与体检
-
-表 5-11 数据接入与体检(FR-36 至 FR-38,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| FR-36 | 现场包按映射表落位,落位前自动体检:结构、命名、后缀与增量三清单(新增、相同跳过、冲突) | 现场包目录、约定目录树 | 落位结果与体检结论 | 同名不同大小判为冲突并默认拒绝落位;覆盖需显式确认;同尺寸自动跳过可反复跑 | docs/输入数据放置指导_v0.1.md §2 与 §3 |
-| FR-37 | 场站扫描辨识:按四条规则辨识 data/raw 下的场站目录,多目录且都不匹配时报未识别并列出扫到的目录 | data/raw 下一级目录、场配置 | 辨识结论与依据 | 依据逐条打印;单站唯一性也要标注为凭唯一性而不是精确匹配;不猜 | docs/数据目录结构与落位约定_v0.2.md §2 |
-| FR-38 | 同台多件的 10min 件合并与上游归档口径:同台补充件按列名对齐并按时间戳去重(主件优先);类目库按信息级列出且不计变化 | 同台补充件、现场年度归档库、类目库 | 合并后的取数结果与扫描结论 | 实测 WTG01 主件 77,551 行加补充件 5,820 行合并 83,353 行、重叠 18 行去重;类目库不再造成每轮误报变化 | HISTORY 2.8.0 与 HISTORY 2.8.2;docs/输入数据放置指导_v0.1.md §4.1 |
-
-### 5.12 安装、服务化与卸载
-
-表 5-12 安装与服务化与卸载(FR-39 至 FR-43,样本场实测(2026-09-22))
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| 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 |
-| FR-43 | 打包与开箱验证:一条命令出包,默认不含输入数据、产物、运行日志与临时文件;打包器自带入口引用闭合与编码守则两道闸;开箱验证解压后真装真起 | 源码树、版本真源 | 交付包与清单文件、开箱验证结论 | 入口引用闭合实跑 48 条全在位;包内出现安装记录或快捷方式即删包并非零退出;页面可用数要求不少于 4 个 | docs/系统设计说明.md §9 与 §9.1 |
-
-## 6 非功能需求
-
-### 6.1 非功能需求清单
-
-非功能需求编号 NFR-01 至 NFR-12,共 12 条。它们大多是「性质」而不是「功能」:判据往往是一次实跑的输出,或者一条机器守卫的退出码。
-
-表 6-1 非功能需求(NFR-01 至 NFR-06,样本场实测(2026-09-22))
-
-| 编号 | 需求 | 判据 | 依据文件 |
-|---|---|---|---|
-| 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-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/说明书_观澜…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 或文档中的既有记录,样本场实测(2026-09-22))
-
-| 环节 | 耗时 | 证据 |
-|---|---|---|
-| 自检整体与模型探针 | 模型探针 0.77 秒;同一次 check 结论为全绿 | guanlan.py check 实跑(2026-09-22,本机) |
-| 一行命令重算(远端一轮 24 个实质步骤汇总) | 单步量级:输入扫描 53.7 秒、三门台账 164 秒、SCADA 侧 1,025 秒、月度派生 99 秒、振动摄入 2,118 秒、变桨面 385 秒、在升 41.6 秒、基线 15.3 秒、总览页 78 秒、本体审计 7.1 秒、门户重装 3 秒、页面归口 9.5 秒 | HISTORY 2.8.1(远端复核记录) |
-| 窄仓全量扫(按时间窗重算的底座) | 约 2 分钟一次全量 | docs/系统设计说明.md §16.3 |
-| 判级矩阵按时间窗重算 | 约 25 秒每个时间窗 | docs/系统设计说明.md §16.4 |
-| 七镜头曲线按时间窗重算 | 约 10 至 45 秒每个时间窗 | docs/系统设计说明.md §16.4 |
-| 控制参数一致性按时间窗重算 | 约 0.6 秒每个时间窗 | HISTORY 2.9.0 |
-| 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/说明书_观澜…v0.2.md §3.1;docs/系统设计说明.md §4.2 |
-| 本机问答 | 显存不足时首答 30 至 60 秒;复杂题在升档下 5 至 15 分钟 | docs/说明书_观澜…v0.2.md §2 与 §11 |
-
-### 6.3 可移植与零配置
-
-「零配置」在本系统里有一个明确的技术含义:**路径只有一个真源,且解析基准是安装根而不是当前工作目录**。这条需求来自一串真实缺陷:写死场站名的路径在多场部署时会静默串场;按当前目录解析的相对路径在从别处启动时会读不到;同一个规则在三个文件里各写一遍就会出现两套显示口径。现在的口径是:代码与配置里只写相对路径,运行期由路径助手解析成绝对路径;产物只落在产物仓,原始件只从原始件目录读,交付静态层只为分发而存在;三者不互相写。
-
-与可移植相关的还有两条:一是解释器探测顺序(先看配置里指定的解释器,再看安装目录下的虚拟环境,最后回落到当前解释器),这让安装目录整体拷到别的盘或别的机器后仍然可用;二是输入输出脚本的字符守则(安装脚本用带 BOM 的 UTF-8 加 CRLF、批处理纯 ASCII 且 CRLF 无 BOM、shell 脚本 LF),三条都是现场真实事故换来的,并由打包、开箱验证与装机自检三处强制执行。
-
-### 6.4 可维护性与单一真源
-
-单一真源的范围在本版已经覆盖五项:版本号、路径、配置、日志、界面文案。每一项都配了「改一处、机器替你查其余几处」的守卫,例如版本号改动后由版本记录一致性门与打包器读真源保证同步,配置改动由配置审计器检查目录约定、命名、内容与引用闭合,日志改动由日志审计器检查唯一落点与行格式,界面文案改动由语言包检查保证前后端成对。
-
-表 6-4 单一真源与对应守卫(样本场实测(2026-09-22))
-
-| 真源 | 管什么 | 守卫与实跑结果 |
-|---|---|---|
-| src/version.py | 名称、版本、版本规则、版本史、包名 | 版本记录一致性检查;check 实跑报 v2.10.1 且记录表与代码一致 |
-| src/paths.py | 一切路径解析的基准与助手 | 配置审计器检查不手拼路径;实跑无不一致 |
-| configs 目录与登记表 | 端口、模型档、场配置、页面归口、配置登记 | 配置审计实跑已知缺口与白名单 14 条、提示 7 条 |
-| src/logfile.py | 日志目录、命名、行格式、保留策略 | 日志审计实跑无不一致、提示 87 条 |
-| src/windscada/lang.py | 中英界面文案成对 | 语言包实跑 845 条成对 |
-| src/entry_refs.py | 入口脚本引用的文件必须都在包里 | check 实跑入口引用闭合 48 条全在位;编码守则通过 |
-
-## 7 数据需求概述
-
-### 7.1 数据族与功能映射
-
-系统的数据需求可以概括成一句话:**七类现场源件加一类共享机理资料,喂出九个产物仓,页面只读产物**。每个源类目录名就是摄入接口,改名等于换接口。数据族与功能的对应关系如下表;逐类的字段、单位、必须性与质量要求见同批交付的《数据要求说明_观澜_2.10.1.docx》。
-
-表 7-1 数据族到功能的映射(样本场实测(2026-09-22))
-
-| 源族(约定落位) | 谁消费 | 喂出什么功能 | 进链步骤 | 现状 |
-|---|---|---|---|---|
-| 故障报警 | 报警摄入器 | 报警台账、停机损失、限电绑定 | ② 三门台账 | 16 件 / 23.8 MB,结构合规 |
-| 风机故障记录 | 工单摄入器 | 检修工单台账、重复检修、闭环验证 | ② 三门台账 | 137 件 / 149.8 MB,其中台账 123 件、附件 14 件 |
-| 油样报告 | 油样索引摄入器 | 油液化验索引、油液时效、融合面油样轴 | ② 三门台账 | 404 件 / 176.2 MB,两级目录合规 |
-| scada_10min | 取数层与十个构建器 | 功率曲线、损失、温度、停机、偏航、液压、热链、变桨面日粒度 | ③ 与 ④ 与 ④c 与 ⑤b | 114 件 / 16.75 GB,含同台补充件 |
-| scada_1min | 取数层 | 变桨面零位三口径与越线计数 | ④c 变桨面 | 38 件 / 13.71 GB |
-| 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;正本缺失时由观澜自算件顶上 |
-| 主机制造商(OEM)技术资料(与场站并列的共享目录) | 本体层 | 码表、手册、工单展开、失效树、实机参数、检索索引 | ⑦ 本体层 | 318 件 / 2.94 GB,结构合规 |
-
-### 7.2 体量与现状
-
-表 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/<场>,递归) | 3,549 件 | 7,157.5 MB | 含仓级台账 3 件;反向呼应审计报成立 3,548 件 |
-| 其中振动窗与谱仓 | 3,417 件 | 占比最大 | 谱分片与窗级分析件 |
-| 其中标准仓 | 57 件 | — | 含窄仓子目录与总览单页 |
-| 未归类件 | 1 件 | — | 场站目录下的一份按月提取核对件,按设计不作为摄入源 |
-| 缺口 | 24 项 | — | 年度归档库中 7 月的十个类目库,扫描器按缺口级列出并写明消费者口径 |
-
-数据现状里有两处必须如实说明。第一处是**缺口**:年度归档库 2026 年 7 月有 10 个类目库不在位(每类一件,扫描器按缺口级列出并提供消费者口径),另外类目库本身是上游归档件,取数层不直接读它,需要先合成为同台补充件才会被摄入。第二处是**按月提取核对件**:场站目录下有一份按月提取的核对件,位置由用户指定、不是摄入源,扫描器按名字模式忽略,既不报未归类也不计入变化。
-
-### 7.3 与数据要求说明的分工
-
-本文只回答「要哪些数据、这些数据支撑什么功能」。数据的字段级要求(核心测点的名称、单位、必须性、缺失替代、对齐规则、质量门与核对锚点)以及面向现场的收资清单,写在《数据要求说明_观澜_2.10.1.docx》里。该文档按用户令要求与现场收资文件逐条对照,并对现场收资层面的已知缺失逐条如实记录,例如测风塔数据为零交付、故障录波只有 4 台、振动侧 handoff 正本缺失由观澜自算件顶上、远端机器未安装本机模型等。
-
-## 8 页面与信息架构需求
-
-### 8.1 页面归口规则
-
-页面归口要回答两个问题:这一页该不该随输入数据变化;它算不算产物。答案落在五类内容类型上,每一类都有机器可查的规则,登记表里逐条写明判定依据(看的是页面里有没有内嵌数据、有没有实时取数、有没有产物引用,以及内容是不是由原始件算出来的)。要改分类,先改证据,这是需求层面的一条纪律。
-
-表 8-1 五类页面内容类型与机器规则
-
-| 类型 | 该随数据变 | 算不算产物 | 机器查什么 |
-|---|---|---|---|
-| 受管静态 | 否 | 不是 | 正文不得引用产物,引用了说明分类错了 |
-| 实时取数 | 是,永远一致 | 页面本体不是 | 内嵌或链接的端口必须在已知服务表里 |
-| 产物引用脚注 | 否,正文不变 | 正文不是,被引产物是 | 被引用的产物路径必须存在,悬空即报 |
-| 数据派生 | 是 | 是 | 必须有来源加指纹或生成时间;有指纹就与当前产物比对做陈旧检测 |
-| 冻结交付件 | 否,按交付版本变 | 不是运行时产物 | 文件名或正文必须能读到版本号与日期 |
-
-### 8.2 门户页面归口表
-
-表 8-2 按登记表逐条列出页面与子页的归口结论。登记表顶层条目 25 条,含 7 条子页共 32 条;归口审计实跑输出为「检查 21 项:不一致 0、已知缺口 19」,其中 21 项是审计的**检查行数**(19 条为引用悬空或溯源缺失的已知缺口项,2 条为两个数据派生页面的溯源检查项,均一致),与登记表的条目数不是同一个口径,两者都不矛盾,此处一并写明以免混淆。
-
-表 8-2 门户页面与子页归口表(25 条顶层条目加 7 条子页,样本场实测(2026-09-22))
-
-| 页面或子页 | 类型 | 随输入数据变 | 判定依据摘要 |
-|---|---|---|---|
-| index 总览 | 受管静态 | 否 | 外壳段约 2,519 字节,无内嵌与取数与产物引用,正文是产品承诺与入口导航 |
-| architecture 系统架构 | 受管静态 | 否 | 外壳段约 3,037 字节,无数据引用,描述分层架构 |
-| method 方法 | 受管静态 | 否 | 外壳段约 2,959 字节,无数据引用 |
-| findings 经验发现 | 受管静态 | 否 | 外壳段约 2,322 字节,内容是公司级经验与内部检查条数,不是本场站数据的函数 |
-| case_hydraulic 案例与液压 | 受管静态 | 否 | 外壳段约 6,187 字节,无数据引用 |
-| cms 振动与 CMS | 实时取数 | 是 | 一个内嵌加两个链接指向振动服务,服务按需读产物,页面本身不存快照 |
-| recalc 数据重算 | 实时取数 | 是 | 链接到运维控制台,后端即真实状态 |
-| login 登录 | 实时取数 | 是 | 链接到工作台 |
-| admin 系统状态 | 实时取数 | 是 | 外壳脚本拉健康自检接口渲染,段内无静态数字 |
-| sim 仿真与回放 | 受管静态 | 否 | 外壳段约 50,767 字节为静态叙述,六个链接指向仿真台与三维台;输入是图纸与机型参数 |
-| 子页 控制律仿真台资料包(5 页) | 冻结交付件 | 否 | 仿真合页服务直接从冻结资料包读取,日期在包名里 |
-| 子页 三维拆装工作台 | 受管静态 | 否 | 三维资产由图纸与模型构建,与原始件无关 |
-| 子页 控制律仪表台面板(门户内嵌) | 产物引用脚注 | 否 | 静态判据说明加一条产物脚注引用,被引目录不在包内,已按悬空如实记账 |
-| 子页 四系统判据面板(门户内嵌 4 份) | 产物引用脚注 | 否 | 同上,静态正文加产物脚注 |
-| documents 交付文档 | 受管静态 | 否 | 外壳段约 5,690 字节为导航页,一个内嵌指向网关下发的治理清单页,段内不含数据快照 |
-| 子页 治理清单交付包 | 冻结交付件 | 否 | 文件名自带版本号与日期,客户交付件按交付版本冻结 |
-| 子页 全场状态一览(脱敏) | 冻结交付件 | 否 | 性质是数据派生但无指纹无生成端,按用户裁决按冻结交付件管并把会变旧的风险如实记账 |
-| 子页 场站取数单 | 冻结交付件 | 否 | 只有日期没有指纹,无生成端,且它引用的两棵产物树不在包内;保留不删并记账 |
-| page_product_cms CMS 报告页与件 | 数据派生 | 是 | 振动服务读的就是产物页面件;已在产物台账中登记并有溯源 |
-| page_product_vib_raw_manifest 振动摄入清单 | 数据派生 | 是 | 每次振动摄入重写,记件数、跨度与未随包缺口;已在台账中登记 |
-| embed_replay_chain 传动链回放(内嵌) | 数据派生 | 是 | 内嵌数据带来源与指纹,源产物在本包不在位,故现只能按生成时间判新旧 |
-| embed_standard_panel 标准面板(内嵌) | 数据派生 | 是 | 正文带生成时间与产物引用,缺指纹;被引的发布件不在包内 |
-| embed_coverage_ch0 覆盖度报告(内嵌) | 数据派生 | 是 | 正文写有来源与指纹,但那份源件不在包内,溯源链断在包外 |
-| embed_u6_sim 变桨液压仿真台(内嵌) | 受管静态 | 否 | 图纸派生的物理回路仿真面板,无内嵌数据与指纹 |
-| embed_u6_health 液压公共站健康报告(内嵌) | 冻结交付件 | 否 | 客户交付报告,无数据指纹,按交付件冻结 |
-| embed_gearbox_report 整机综合诊断报告(内嵌) | 冻结交付件 | 否 | 报告类交付件,文件名带修订版号 |
-| embed_reports_4 分系统评估报告(内嵌 4 份) | 冻结交付件 | 否 | 交付报告,无内嵌数据与指纹 |
-| embed_governance_12 治理清单分册(内嵌 12 份) | 冻结交付件 | 否 | 与交付层分册同源,冻结交付件 |
-| embed_sc1_local 本地占位(内嵌) | 受管静态 | 否 | 小体积占位面板 |
-| detail_tabs 工作台十个标签 | 实时取数 | 是 | 单页应用按标签现场取数,产物指纹变化自动重载,页面本体不存快照 |
-| delivery_pages_all 交付页 | 冻结交付件 | 否 | 客户交付件自带版本号与日期;其中两页性质上是数据派生,另见逐页判定 |
-| viewer_pages_all 三维页 | 受管静态 | 否 | 图纸与模型派生的资产页,与原始件无关 |
-
-按类型统计的分布如图 3-1 所示。本表口径下 32 条条目中,受管静态 11 条、实时取数 5 条、数据派生 5 条、冻结交付件 9 条、产物引用脚注 2 条。这张统计的意义在于:**真正需要随输入数据变的只有 5 条数据派生条目与 5 条实时取数条目**,其余 22 条要么是受管正文、要么是按版本冻结的交付件,把它们混进「产物管理」只会制造无谓的维护负担。
-
-![图 3-1 门户页面归口分布(依据 configs/portal_pages.yaml)](figures/fig-req-03-页面归口分布.png)
-
-### 8.3 页面陈旧检测要求
-
-数据派生页面的核心风险是「数据换了页面没换」,也就是页面里烘进去的快照与当前产物不一致。需求因此要求三件事:一是每条数据派生条目必须有来源路径,并且有指纹或生成时间;二是有指纹的必须与当前产物比对(逐字节比对哈希),不一致即报陈旧;三是登记表里写清「为什么这么判」,可复核。检查器的退出码语义为:0 表示一致,5 表示缺文件或未登记,6 表示溯源缺失,7 表示页面陈旧,8 表示交付件缺版本号,9 表示分类错。
-
-当前如实状态是:登记表里 19 条已知缺口全部是「页面引用了本包没有的产物树」,逐条写明理由而不装作通过;这些页面因此拿不到当前值做比对,其中两条数据派生页面没有生成端、三条内嵌快照缺指纹只能按生成时间判断。这三类都记账在案,待相应产物线随包后复检。
-
-## 9 验收需求与质量门
-
-### 9.1 质量门全景
-
-系统的验收分两层:**装机自检**看「这台机器能不能用」,**重算链验收**看「这一轮数据算得对不对」。前者是 guanlan.py check 的四十余条判定行,后者是重建命令每一环的退出码与容忍口径。两层的关系是:自检不通过就不该跑重算(环境或依赖有问题),重算链跑完再由自检复核产物与台账。
-
-表 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.1,包名 app_guanlang_v2.10.1.zip);卸载入口两个都在位 | 全 OK |
-| 制品与台账审计 | 5 | 反向呼应 3,548 件成立;页面归口检查 21 项;配置统一;日志统一;输入数据放置体检 26,503 件结构合规 | 全 OK |
-| 产物与发布件在位 | 10 | 标准仓、本体对象库、findings、事实契约、门户、仿真合页服务与资料包、三维资产、仿真回放资产、治理清单交付件 | 全 OK |
-| 原始件目录 | 1 | 原始件目录存在(无数据时此项不影响页面) | 全 OK |
-| 模型闸 | 6 | 默认档探针 0.77 秒;四个模型档逐个在位;模型可用性正常并记录审计日志 | 全 OK |
-| 合计 | 41 | 另有 6 条端口状态行不计入判定 | 结论全绿,可 serve,退出码 0 |
-
-验收门的分类与数量如图 7-1 所示。该图与上表同源,都取自本次自检实跑的 41 条判定行;按上表的分类口径,环境与依赖一类共 10 条、静态质量门 4 条、子进程口径 3 条、版本与卸载入口 2 条、制品与台账审计 5 条、产物与发布件在位 10 条、原始件目录 1 条、模型闸 6 条。其中真正体现「这个系统有没有把口径管住」的是制品与台账审计那五条门与四条静态质量门。
-
-![图 7-1 验收门分类与数量(依据 guanlan.py check 实跑输出)](figures/fig-req-04-验收门分类.png)
-
-### 9.2 重算链的逐步验收门
-
-重算链默认 26 步,加跳过 SCADA 为 23 步(另有第 ① 步仅在给了现场包目录时才跑)。每一步都是一个验收门:非零退出即停下并打印该步说明,容忍清单里的退出码不算失败,但会在汇总行里如实标出退出码。这个设计的关键在于「容忍不等于通过」:例如月度派生件找不到基线时返回 5,含义是「跳过了等价验收」,而不是「验收通过」。
-
-表 9-2 重算链逐步验收门(默认 26 步,样本场实测(2026-09-22))
-
-| 序 | 步骤 | 主要脚本或命令 | 容忍退出码 | 非零的含义 |
-|---|---|---|---|---|
-| 1 | ① 放数据(仅当给了现场包目录) | place_raw_data.py | 无 | 落位失败或冲突被拦下 |
-| 2 | ①b 输入数据扫描 | raw_scan.py --check --write | 4 与 5 | 4 表示有新数据(不是失败),5 表示还没有快照 |
-| 3 | ② 三门台账 | rebuild_from_raw.py | 无 | 报警或工单或油样摄入失败 |
-| 4 | ③ SCADA 侧十个构建器 | rebuild_from_raw.py --scada | 无 | 原始件缺失或读取失败 |
-| 5 | ③b 标准仓窄仓 | scada_slim_build.py | 无 | 按时间窗重算的底座没建起来 |
-| 6 | ④ 月度派生件 | windscada_monthly_build.py | 5 | 找不到随包基线,跳过等价验收 |
-| 7 | ④b 振动摄入与报告与标量 | vib_raw_build.py | 无 | 解析出错;没有振动原始件时空跑属正常 |
-| 8 | ④c 变桨面 | pitch_face_build.py | 4 | 找不到 SCADA 原始件,按缺件如实报 |
-| 9 | ④d 振动在升与换件闭环 | component_history_build.py | 4 | 本机没有振动窗索引,按缺件如实报 |
-| 10 | ④e 三层基线 | baseline_38_build.py | 4 | 同上 |
-| 11 | ⑤b 由重算台账生成 claim | sop_findings_from_ledger.py | 无 | 台账不足以生成结论 |
-| 12 | ⑤c 事实契约构建与渲染 | guanlan_facts_contract.py build | 2 | 契约自检不过,需要人看 |
-| 13 | ⑤a 逐件来源台账 | products_restore_missing.py --refresh | 无 | 账实不符 |
-| 14 | ⑤ 反向呼应审计 | products_reverse_audit.py --check | 5 | 有未归类件或判据失败,停下看清单 |
-| 15 | ⑥ 重启组件服务(停,保留网关) | _ops_stop_keep_gateway.py | 无 | 停服务失败 |
-| 16 | ⑥ 重启组件服务(起) | guanlan.py serve | 1 | 有模块未就绪属降级正常状态 |
-| 17 | ⑦ 本体码表与手册与文档 | kb_ingest | 无 | 技术资料缺失会降级并打印提示 |
-| 18 | ⑦ 本体铺开判级与台账 | populate | 无 | 一层正本缺失或来源不足 |
-| 19 | ⑦ 本体决策链进度 | chain_ingest | 无 | 服务未在跑则取不到链盘 |
-| 20 | ⑦ 本体在升与闭环证据 | trend_ingest | 无 | 源缺失 |
-| 21 | ⑦ 本体检索索引 | retrieval.build | 无 | 索引构建失败 |
-| 22 | ⑦ 本体实机参数表 | maintenance.refresh_params | 无 | 参数源缺失 |
-| 23 | ⑦b 全场状态总览页 | windscada_overview_build.py | 无 | 缺产物时响亮报错,不产出半张页 |
-| 24 | ⑧ 本体审计 | ontology.audit | 无 | 期望 0 问题 |
-| 25 | ⑧b 重装门户 | portal_build.py | 1 | 缺契约产物时属预期,门户保留原样 |
-| 26 | ⑧c 页面归口审计 | pages_audit.py --check | 5 与 6 与 7 | 7 表示有页面陈旧;5 表示被引产物不在位;6 表示溯源缺失 |
-| 27 | ⑧d 版本记录一致性 | version_log.py --check | 6 | 文档与版本真源不一致,需同步文档 |
-| 28 | ⑧ 台账等价验收(仅加开关时) | rebuild_from_raw.py --verify | 4 与 5 | 4 表示有需人工看的差异;5 表示没基线即没做验收 |
-
-关于步数需要两点说明以免误读。第一,表中共列出 28 条命令行,其中第 1 步(放数据)仅在给了现场包目录时才执行,最后一步(台账等价验收)仅在加了对应开关时才执行,因此默认计划的实跑步数是 26 步,加跳过 SCADA 选项后是 23 步。第二,「门」与「步」不是一一对应:第 ⑥ 步拆成停与起两条命令各算一步,第 ⑦ 步拆成六条命令各算一步,所以表里的命令行数多于环节数。
-
-### 9.3 检查器的退出码语义
-
-表 9-3 主要检查器的退出码语义与容忍口径
-
-| 检查器 | 退出码语义 | 链上容忍 |
-|---|---|---|
-| guanlan.py check | 0 全绿可启动;2 有失败项 | 不作为链上步骤,装机后人工核 |
-| rebuild_all.py | 0 全部完成;1 有步骤失败(失败即停) | 汇总行逐条标 OK 或 OK 带退出码或 FAIL |
-| pages_audit.py | 0 一致;5 缺文件或未登记;6 溯源缺失;7 页面陈旧;8 交付件缺版本;9 分类错 | 容忍 5 与 6 与 7 |
-| products_reverse_audit.py | 0 全部有来路;5 有未归类件或判据失败 | 容忍 5 |
-| version_log.py | 0 与版本真源一致;6 记录表需重生成 | 容忍 6 |
-| config_audit.py | 0 无不一致;8 存在旧副本等结构问题 | 未进链,由 check 报 |
-| raw_scan.py | 0 无变化;4 有新增或变化(不是失败);5 还没有基线 | 容忍 4 与 5 |
-| raw_data_check.py | 0 合规;5 结构或命名违例;6 增量冲突;7 缺源类或时间空洞;8 仅有提示 | 未进链,由 check 报 |
-| rebuild_from_raw.py --verify | 0 逐值一致;4 有需人工看的差异;5 找不到基线即没做验收 | 容忍 4 与 5 |
-| ontology.audit | 0 无问题 | 不容忍 |
-| service_ctl.py | 0 成功;3 权限不足(给出可执行建议) | 未进链,人工执行 |
-
-### 9.4 开箱验证与卸载核验
-
-交付质量有两条独立的核验路径:打包后的**开箱验证**(在临时副本上真解压、真安装、真起服务、真核页面)与**卸载核验**(在刚装好的副本上真跑卸载并断言拆除范围与保留范围)。两条路径的意义是:它们都作用在副本上,不打扰正在运行的实例。
-
-表 9-4 开箱验证与卸载核验的项与判据(样本场实测(2026-09-22))
-
-| 核验路径 | 核验项 | 判据 | 实测记录 |
-|---|---|---|---|
-| 开箱验证 | 入口引用闭合与编码守则 | 入口脚本引用的文件必须都在包内;编码与字符守则全部通过 | 解压 3,734 件,入口引用闭合通过(2026-09-17 记录) |
-| 开箱验证 | 离线安装 | 安装脚本退出码 0 | 本机记录 172 秒、退出码 0 |
-| 开箱验证 | 快捷方式与无窗口 | 两份快捷方式在位;无控制台窗口断言成立;包内无被弃用的脚本宿主文件 | 窗口句柄为 0,断言通过 |
-| 开箱验证 | 页面可用性 | 核验门户与工作台与振动与运维控制台与健康自检五个入口,要求不少于 4 个可用 | 实测 4 个可用(振动入口因无产物返回 503,属预期) |
-| 开箱验证 | 逐件复核 | 包内每个文件与工作树逐字节相同 | 3,733 件逐字节相同 |
-| 卸载核验 | 拆除范围 | 安装记录、运行态、快捷方式都被拆掉 | 在临时副本上真跑卸载,退出码 0 |
-| 卸载核验 | 保留范围 | 数据与产物一件不少,端口已释放 | 件数不变,断言通过 |
-| 卸载核验 | 只读预演 | 内容断言(步骤齐全)与副作用断言(预演后关键目录与文件的存在性与字节数完全不变) | 自检全绿 |
-
-关于「五个入口全可用」这一条,需要如实说明:判据本身是「不少于 4 个可用」,实测记录为 4 个可用,第 5 个(振动入口)在没有产物的机器上按设计返回 503 并给出缺件说明。本次编写未取到 5 个全部可用的实测记录,因此本文不写「5 个全通过」,只写「判据为不少于 4 个、实测 4 个」,并把「在产物在位的机器上复跑取证」列为后续动作(未取证)。
-
-### 9.5 本次实跑结论
-
-本次编写期间对只读项做了三次实跑:自检(guanlan.py check)结论为全绿可启动、退出码 0、无失败项;归口审计(pages_audit.py --check)结论为全部一致、退出码 0、21 项检查里 19 项是已登记的已知缺口;重算计划(rebuild_all.py --dry-run)打印默认 26 步、跳过 SCADA 为 23 步,全部步骤命令与脚本都在位。没有执行任何重算或耗时计算,本文中与耗时有关的数字全部来自既有的 HISTORY 记录与设计文档,来源逐条列在表 6-3。
-
-## 10 约束、假设与明确不做
-
-需求的可信度一半来自「写清了不做什么」。本系统的边界不多但很硬:它是**只读分析系统**,不是控制系统,也不是云端服务。
-
-表 10-1 约束、假设与明确不做(样本场实测(2026-09-22))
-
-| 类别 | 条目 | 说明与依据 |
-|---|---|---|
-| 约束 | 不写回现场系统 | 全部产物落在本机产物仓,不下发指令、不修改现场任何系统;原始件按只读输入对待,落位冲突默认拒绝 |
-| 约束 | 不做实时控制 | 最小时间粒度是分钟级数据(10min 与 1min),系统的输出是分析与建议(带证据级与处置措施),没有控制回路 |
-| 约束 | 无外网 | 计算、页面、检索都在本机完成;Windows 侧离线安装件与便携运行时随包;本机模型为可选件且单独交付 |
-| 约束 | 不做云端 | 事实契约层留有可上云面孔(脱敏派生件),但本版不部署云服务、不做账号体系、不做多租户 |
-| 约束 | 页面无鉴权 | 这是知情取舍而非疏忽:对外监听等于把全场运行数据摊开,必须在网侧限来源或在反向代理加认证,启动日志每次提醒 |
-| 约束 | 安装目录不得含空格与中文 | 安装脚本、批处理与部分第三方组件的既有约束,写在说明书的系统要求一节 |
-| 约束 | 数据规模受单机资源限制 | 当前输入 26,504 件 / 224.67 GB;整条重算在单机上以小时计,建议内存 16 GB 起 |
-| 假设 | 现场数据按约定目录与命名交付 | 源类目录名即摄入接口,改名或多套一层会导致摄入静默读不到,因此用体检器与落位脚本前置把关 |
-| 假设 | 现场具备解释器或允许使用包内便携运行时 | 麒麟与统信自带解释器多为 3.7 与 3.9,跑不了本项目依赖,故包内带便携运行时 |
-| 假设 | 有至少一份能解析的厂商报告与至少一段原始运行数据 | 否则相关面按缺件如实报,不产出假数;这也是标定一次基线的前提 |
-| 假设 | 无人值守场景会使用服务而非会话前台进程 | 会话里起的进程会随会话关闭被一起收掉,远程部署必须用服务 |
-| 不做 | 不做训练回路 | 本版不含训练,模型只做转述与解释,判断在代码里 |
-| 不做 | 不做知识图谱的自动推断 | 本体层零判级权,只转录一层正本;同部件多源判级并存不合并 |
-| 不做 | 不做实时数据流接入 | 数据以批的方式落位、体检、重算;服务侧看门狗只做到发现变化并列出该跑的步骤,不做流式处理 |
-| 不做 | 不为「无生成端」的件做运行期补齐 | 运行期一律不从交付包补齐;缺件要么放原始件重算,要么由研发补生成端 |
-| 不做 | 本版不承诺「多场并存运行」的完整交付 | 场配置机制与场定义装载已接通,但本版端到端跑通并留下实测记录的只有样本风电场;新场接入按第 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 机组与机型可替换
-
-机组数、台号、机型与主机制造商(OEM)的差异不进入引擎:引擎按场对象给出的机组清单与额定容量工作,机型差异通过**机型—场站数据契约** `configs/contracts/<机型>_<场>.yaml` 落到列名、单位与阈值上。换机型等于换契约,不改引擎。
-
-表 11-2 机组与机型替换需求(FR-47)
-
-| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
-|---|---|---|---|---|---|
-| 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 需求跟踪矩阵
-
-| 编号 | 实现(脚本或模块) | 页面或产物 | 验收门 |
-|---|---|---|---|
-| FR-01 | taxonomy.system_matrix;scripts/windscada_serve.py | 系统矩阵、逐台页、问题页 | 判级随所选时间窗变化的实测记录;check 归口与反向呼应 |
-| FR-02 | scada_slim_build.py;subsys 各面 registry | 窄仓、判级单元格 | 重算链 ③b 步;窄仓列数与口径同源 |
-| FR-03 | perf/availability;reliability.overview | 可靠性表、部件问题页 | 判级与可靠性随时间窗变化的实测记录 |
-| FR-04 | windscada_serve.py 的 sys_norm() | 问题页与问题接口 | 传前端标识与中文名都能取到同一份问题(HTTP 抽样) |
-| FR-05 | 组件页面自组句逻辑 | 需要关注卡片 | HTTP 抽样:依据不再为空(WTG09 齿轮箱) |
-| FR-06 | months_of;win_range;in_win | v2 顶栏时间窗控件 | 时间窗词表逐项验证;退化时间窗按样本不足处理 |
-| FR-07 | 顶栏两个日期输入框与校验 | v2 页面自定义起止控件 | 起晚于止时拒绝并提示 |
-| FR-08 | 进程缓存与后台线程与预热 | 等待与构建中状态标记 | 判级约 25 秒每个时间窗;等待期页面写明旧口径 |
-| FR-09 | curves.build_store;/api/curves | 七镜头曲线 | 图注写随所选时间窗;样本量随时间窗变化 |
-| FR-10 | 月度派生件过滤 | 七张月度时序图 | 图注与按月取整口径 |
-| FR-11 | control.registry(span=) | 控制参数一致性卡片 | 按时间窗重算约 0.6 秒;封顶中位随窗变化 |
-| FR-12 | rebuild_from_raw.py --scada | 损失、功率曲线、停机事件 | 与基线逐值对拍;重算链 ③ 步 |
-| 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 | <场>_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 行;键集合并集去重 |
-| FR-19 | windscada_workorder_ingest.py | 工单台账 | 5,876 行;差异可归类 |
-| FR-20 | windscada_watch_channels_build.py | 油样索引 | 404 行;102 行缺口如实记账 |
-| FR-21 | 停机与损失构建器 | 停机事件件、损失月表 | 与输入跨度对拍 |
-| FR-22 | src/ontology/kb_ingest.py | 对象库、检索索引、实机参数表 | 本体审计退出码 0;故障码覆盖 263 与 322 |
-| FR-23 | src/ontology/populate.py | 对象库对象 | 本体审计退出码 0 |
-| FR-24 | chain_ingest;trend_ingest | 决策对象与趋势证据对象 | 幂等覆盖;服务在跑时取链盘 |
-| FR-25 | src/ontology/fast_agent.py;llm_gate;检索 | 问答区与引文块、模型审计日志 | 已知答案回放;两档都拦时不出文并给原因 |
-| FR-26 | src/windcms/report_std.py;report.py | 报告 md 与 docx、登记 csv | 38 台逐台有条目;相对判据封顶候选 |
-| FR-27 | guanlan_facts_contract.py | 契约件与四个消费端派生件 | 改一条 claim 四个消费端全变;自检不过报 2 |
-| FR-28 | portal_build.py;门户装配脚本 | 门户单文件页、汇报纸 | 拆装逐字节相同;产物挪走时缺结论段属预期 |
-| FR-29 | scripts/rebuild_all.py | 各仓产物与步汇总 | 计划步数实跑;失败即停;幂等 |
-| FR-30 | guanlan_ops.py;_ops_run.py;_ops_launch.py | 运维控制台 | 并发动作返回 409;停组件保留控制台 |
-| FR-31 | products_state.py;products_restore_missing.py | 产物状态与缺件说明 | 清除后再点返回 409;补齐定位为离线人工补救 |
-| FR-32 | raw_scan.py;rebuild_all --auto;service_worker 的看门狗 | 扫描结论与基线快照、看门狗日志 | 退出码 0 与 4 与 5;族表外目录不猜 |
-| FR-33 | portal_build.py;shell 与模板装配 | 门户单文件页 | 拆装逐字节相同;行尾 LF 自检 |
-| FR-34 | configs/portal_pages.yaml;pages_audit.py | 归口表与审计结论 | 检查 21 项、不一致 0;陈旧报 7 |
-| FR-35 | retrieval.build;windcms 知识库构建 | 检索索引件 | 无模型时纯词法可用;索引缺失如实说明 |
-| FR-36 | place_raw_data.py;raw_data_check.py | 落位结果与体检结论 | 冲突默认拒绝;同尺寸跳过 |
-| FR-37 | scan_stations.py;config 场站辨识 | 辨识结论与依据 | 四条规则逐条打印;多目录不匹配报未识别 |
-| FR-38 | scada_source 取数层;raw_scan 族表 | 合并取数结果与扫描结论 | 主件与补充件合并行数与重叠数打印;类目库不计变化 |
-| FR-39 | install.bat 与 install.ps1 与 install.sh;便携运行时 | 虚拟环境、配置、安装记录 | 离线安装退出码 0;入口引用闭合 |
-| FR-40 | service_ctl.py;win_service.py;service_main.py;service_worker.py | 托管进程与自启项 | 巡检 15 秒;无权限返回 3 并给建议 |
-| 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 编写依据
-
-表 A-1 编写依据(章节或事实与来源文件)
-| 章节或事实 | 来源文件(仓库相对路径) |
-|---|---|
-| 版本号真源、版本规则、12 条版本史、包名 | src/version.py |
-| 系统是什么、产物全景、路径约定、进程口径、页面取数 | docs/系统设计说明.md 第 1 至第 6 章 |
-| 缺口与边界(未随包脚本、缺件、源件缺失) | docs/系统设计说明.md 第 7 章 |
-| 变更记录(按日期逐条事故与处置) | docs/系统设计说明.md 第 8 章 |
-| 交付包组成与入口引用闭合 | docs/系统设计说明.md 第 9 章与 §9.1 |
-| 页面归口规则、五类内容类型、门户逐页识别、全部网页总清点 | docs/系统设计说明.md 第 10 章与第 12 章 |
-| 配置与日志统一口径、无产物时的可用性 | docs/系统设计说明.md 第 11 章 |
-| 输入到功能到算法到产物、端到端顺序、反向呼应审计、可逆性 | docs/系统设计说明.md 第 13 章与 §13.6 |
-| 服务化安装与卸载、安装时版本检查 | docs/系统设计说明.md 第 14 章 |
-| 版本管理与打包命名、单一真源与三处守卫 | docs/系统设计说明.md 第 15 章 |
-| 时间窗词表、随窗与不随窗、窄仓、等待契约 | docs/系统设计说明.md 第 16 章 |
-| 三条描述口径与服务端链接契约、依据不得为空 | docs/系统设计说明.md 第 17 章 |
-| 面向用户的功能与使用、系统要求、模型档、目录结构、常见故障、已知限制 | docs/说明书_观澜…v0.2.md |
-| 页面与子页的归口登记(类型、依据、已知缺口) | configs/portal_pages.yaml |
-| 服务端口与监听地址真源、看门狗开关 | configs/serve.json |
-| 模型档位与拉取命令 | configs/models.json |
-| 自检门清单与退出码、服务定义 | guanlan.py |
-| 重算链步骤、容忍口径、自动取消跳过 | scripts/rebuild_all.py |
-| 归口审计规则与退出码语义 | scripts/pages_audit.py |
-| 输入数据放置规则、增量语义、放完跑什么 | docs/输入数据放置指导_v0.1.md |
-| 落位约定、场站辨识四条规则、各目录功用、能重建与不能重建 | docs/数据目录结构与落位约定_v0.2.md |
-| 手工重算步骤、运维控制台三条纪律、二次启动原因 | docs/重算操作手册_v0.1.md |
-| 页面输出体检的判定口径与逐页原因 | docs/页面输出体检_v0.1.md 与 docs/页面输出体检_逐页原因_v0.1.md |
-| 打包与安装与卸载与版本检查的用户视角说明 | README_先读我.MD |
-| 自检实跑输出(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 术语表
-
-表 B-1 术语表(按主题分组)
-
-| 术语 | 英文或写法 | 含义 | 出处 |
-|---|---|---|---|
-| 时间窗 | — | 一个由用户选择或自定义的数据观察区间;月度件按月取整、日粒度件按日精确 | docs/系统设计说明.md §16 |
-| 预设时间窗 | — | 词表内置的时间窗:近30日、近90日、2026年、2026H1、2025H2、全程 | docs/系统设计说明.md §16.1 |
-| 自定义起止日期时间窗 | — | 写法为起日期~止日期的时间窗,含两端 | docs/系统设计说明.md §16.1 |
-| 判级时间窗 | — | 判级矩阵与四轴重算所用的时间窗 | docs/系统设计说明.md §17.1 |
-| 证据时间窗 | — | 支撑某条结论的证据所在的时间窗,随判定一并给出 | docs/系统设计说明.md §17.1 |
-| 天气窗 | — | 领域词,指气象条件适合的时段,不属于时间窗口径的统一范围 | docs/系统设计说明.md §17.1 |
-| 作业窗 | — | 领域词,指检修作业可实施的时段,保持原样 | docs/系统设计说明.md §17.1 |
-| 平均无故障间隔(MTBF) | MTBF | 相邻两次故障之间的平均运行时长,按所选时间窗内的停机事件与报警计算 | docs/系统设计说明.md §16.2 与 §17.1 |
-| 平均停机间隔(MTBO) | MTBO | 相邻两次停机之间的平均间隔,与可靠性表同源 | docs/系统设计说明.md §17.1 |
-| 单次停机时长(MDT) | MDT | 每次停机的平均持续时长 | docs/系统设计说明.md §17.1 |
-| 窄仓 | slim10min | 由十分钟数据抽出的公共列子集(不裁剪行),作为按时间窗重算的秒级底座 | docs/系统设计说明.md §16.3 |
-| 等待契约 | pending | 按时间窗重算未命中缓存时先回等待或构建中,由页面轮询,绝不静默用旧口径 | docs/系统设计说明.md §16.4 |
-| 判级四轴 | — | 变桨、偏航、蓄能、温度四个随所选时间窗重算的判级方向 | docs/系统设计说明.md §16.2 |
-| 融合面 | — | 把模型过闸线与振动窗索引裁决合到一起的判级面,输出融合级 | docs/系统设计说明.md §13.1 |
-| 链盘 | — | 运行时的决策链进度集合,每台一个对象记六步状态、卡点与下一步 | docs/系统设计说明.md §13.1 |
-| 产物 | outputs | 由原始件算出来、页面只读的输出数据,统一落在产物仓 | docs/系统设计说明.md §3 与 §13.4 |
-| 逐件来源台账 | — | 记录每件产物属于原始件重算还是随包补齐的台账 | docs/系统设计说明.md §2.1 |
-| 反向呼应审计 | — | 从输出回溯到生成端与输入的审计,检查每件产物有没有来路 | docs/系统设计说明.md §13.6 |
-| 页面归口 | — | 判定每一页是否随输入数据变、是否算产物,并逐条写明依据 | configs/portal_pages.yaml |
-| 陈旧检测 | — | 对数据派生页面比对内嵌指纹与当前产物,不一致即报页面陈旧 | scripts/pages_audit.py |
-| 数据派生 | data-derived | 五类页面内容类型之一:把产物烘进页面,属于产物,必须登记来源并做陈旧检测 | configs/portal_pages.yaml 头部 |
-| 受管静态 | static | 受管外壳正文,不随输入数据变,也不得引用产物 | configs/portal_pages.yaml 头部 |
-| 实时取数 | live | 页面本体不存快照,永远与产物一致 | configs/portal_pages.yaml 头部 |
-| 产物引用脚注 | data-citing | 静态正文加产物引用,正文不变但被引产物必须在位 | configs/portal_pages.yaml 头部 |
-| 冻结交付件 | frozen-delivery | 带版本号与日期的客户交付件,按交付版本冻结,不随数据自动变 | configs/portal_pages.yaml 头部 |
-| 开箱验证 | — | 打包后在临时副本上真解压真安装真起服务的核验路径 | docs/系统设计说明.md §9 |
-| 容忍口径 | 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 |
+# 需求分析 · 观澜 v2 风电场智能分析系统 · 版本 2.10.2
+
+## 1 文档说明
+
+### 1.1 目的
+
+本文是「观澜 v2(风电场智能分析系统)」的**需求分析文档**,回答四个问题:这套系统究竟要满足谁的什么需要;这些需要从哪来、经过哪些版本变成现在的样子;每一条需要对应什么功能、什么输入、什么输出、拿什么判据验收;以及哪些事本版明确不做。本文与同批交付的《设计说明_观澜_2.10.2.docx》《数据要求说明_观澜_2.10.2.docx》配套:本文讲「要什么、凭什么算做到了」,设计说明讲「怎么做的」,数据要求说明讲「要哪些数据、什么形态、什么单位」。
+
+本文的写作口径是**只写能取证的事实**:每个数字、每条结论都能指到仓库里的某个文件、某次实跑输出或某条版本记录;查不到、取不到的一律写明「未取证」或「未到位」,不做推测性补全。因此文中会出现少量「未取证」的说明句,那是刻意留下的诚实边界,不是遗漏。
+
+### 1.2 读者与用法
+
+本文面向四类读者:场站侧的运行、点检、管理人员用它核对「系统答应做的事是否都做到了」;研发与交付人员用它做需求变更的基线(改需求先改本文的 FR 编号与验收判据);验收与审计人员用第 9 章的门清单与退出码语义逐条核对;新加入项目的工程师用第 2、3 章在半小时内建立全局认识。
+
+需要提醒的是:本文不是操作手册。放数据、重算、安装、卸载的具体命令在《重算操作手册》《输入数据放置指导》《README_先读我.MD》里,本文只在功能需求条目里给出判据与依据文件的位置。
+
+### 1.3 版本对应关系
+
+系统版本号只有一个真源:仓库内的 src/version.py 的 VERSION 一行。本版 VERSION = 2.10.2,版本记录(HISTORY)共 14 条,人读的版本表由 scripts/version_log.py 从 HISTORY 生成到 docs/版本记录.md,并由 version_log.py --check 与 guanlan.py check 双重校验「记录表与代码一致」。
+
+表 1-1 版本与文档的对应关系
+
+| 项 | 值 | 取证方式 |
+|---|---|---|
+| 系统版本 | 2.10.2 | src/version.py 的 VERSION;guanlan.py check 实跑报「版本管理: 观澜 v2(风电场智能分析系统) v2.10.2」 |
+| 交付包名 | app_guanlang_v2.10.2.zip | src/version.py 的 package_name();同一条 check 输出 |
+| 本文版本 | 2.10.2(与系统版本同号) | 本文标题与 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() |
+| 本次变化级别 | 小(patch,数据要求说明改为纯数据需求规格:去样本场接入现状与实现细节、测点按业务含义列四列表、新增 SCADA 秒级数据要求;系统功能未变) | HISTORY 中 2.10.2 条目的 level 字段 |
+
+本版相对 2.10.0 的实质变化是**交付文档对外化**:三份交付文档(需求分析、设计说明、数据要求说明)全文不体现具体风电场(去标识化),并新增多风电场适用性一章;系统功能未变,属交付物修订。依据用户令原文:「修改三份文档,内容参考样本风电场,但不体现样本风电场,且具有不同风电场适用性」(引用时把样本场名按去标识化口径写成「样本风电场」)。
+
+### 1.4 编写依据与取证口径
+
+本文的取证方式分四种,文中出现数字时都能归到其中一种:**读源**(读代码、配置、登记表)、**读文档**(读仓库内已交付的设计与操作文档)、**实跑**(跑只读的自检与只读的审计脚本)、**实测计数**(对 data 与 outputs 目录做文件级计数与求和)。为避免把「文档里写过」当成「现在就是这样」,凡与现状有关的数字,本文优先采用实跑与实测计数,并在文中标注日期。本文出现的全部实测数字(件数、体量、行数、日期覆盖、耗时、退出码等)都出自**样本场实测(2026-09-22)**:保留它们是为了说明判据与量级,换场后必须按新场重新标定与重跑,不能直接当作新场的预期值——第 11 章的多风电场适用性要求正是围绕「结构不变、参数与数据随场变」写的。
+
+表 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/<场> 递归计数(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 术语与描述口径约定
+
+本版全文遵守三条写作口径,它们是用户令落成的硬要求,也是机器清扫过的(依据 docs/系统设计说明.md §17.1):**第一**,描述里含「窗」且确实指时间窗口的,一律写全「时间窗」——预设时间窗、判级时间窗、证据时间窗、随所选时间窗、时间窗内;而领域词里的天气窗、作业窗、预览窗、观测窗保持原样,不误改。**第二**,影响的是风电机组时统一写「影响机组」或「影响机组数」,不使用以「台」计数的旧写法;数据字段名里的旧写法暂留以兼容数据契约,显示层统一。**第三**,用英文简写时必须写成「中文(英文简写)」,例如平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);英文映射的键同步改,否则英文页会回落中文。
+
+表 1-3 三条描述口径与落地位置
+
+| 口径 | 要求 | 落地与证据 |
+|---|---|---|
+| 时间窗 | 含「窗」且指时间窗口者写全「时间窗」 | docs/系统设计说明.md §17.1;v2 页面顶栏控件与图注均写「随所选时间窗」 |
+| 影响机组 | 影响风电机组时写「影响机组」 | 表头与说明含报告构建器;数据字段名暂保留 |
+| 英文简写 | 中文(英文简写) | 平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);i18n_en 键同步 |
+
+另外,本文出现的台号(如 WTG01 至 WTG38 一类写法)是**样本场内部编号**,保留编号形态只是为了与产物、台账和依据逐条对得上;换场时按实际台号替换,编号本身不构成接口约定。
+
+术语的完整解释见附录 B。
+
+## 2 系统概述
+
+### 2.1 本系统是什么
+
+本系统(观澜 v2,风电场智能分析系统)是一套**纯离线的海上风电场智能分析单包系统**,对象是**样本风电场(下称「本场」)**的 38 台 4.0 MW 级海上机组;机型代号与主机制造商(OEM)名称从略,业主单位名称从略。它把现场给来的运行数据、状态监测、报警、工单、油样落到每台机组的正确部件上,给出带证据、带边界、带责任人的检修结论;**判断在代码里,本机模型只负责问答、解释与引用,不发明结论**。软件不含任何联网功能,也不需要现场系统配合改造:原始件从 data/raw 读入,产物落在 outputs 下,页面只读产物,产物由数据算出来。
+
+一页话术(给第一次接触的人的三十秒版本):这套系统不控制风机、不下发指令、不改现场任何系统。它做的是「把现场已有的数据读进来,算出一份能说清依据的体检报告,然后用一个门户和三个服务把它讲清楚」。放数据之前,页面会如实显示「无产物」并告诉你缺哪一件、怎么补;放了数据之后,一条命令重算,页面立刻有数。所有结论都能往回指到源件与算法,指不到的地方它会写「不可判」或「缺件」,而不是编一个数字。
+
+### 2.2 部署形态与运行边界
+
+交付形态是**离线单包**(EDITION = offline-single-package):一个安装目录里同时是程序、配置、交付件与数据入口。安装目录不要带空格与中文。程序侧有 6 个常驻服务,全部由安装目录下的启动器或系统服务托管;组件的内部接口只监听本机回环地址,只有门户网关按配置可对外监听。
+
+表 2-1 常驻服务与端口(依据 configs/serve.json 与 guanlan.py 内置默认,两者口径一致)
+
+| 服务 | 默认端口 | 监听地址 | 作用 |
+|---|---|---|---|
+| 门户网关 | 28084 | host 与 public_host(出厂 public_host 为 0.0.0.0) | 门户单文件页、路由与改写、运维控制台、/healthz、/api/version |
+| 分析工作台 | 18033 | host(127.0.0.1) | 十个标签的分析工作台与全部 /api 取数 |
+| 振动诊断 | 18020 | host(127.0.0.1) | CMS 振动报告页、逐台页、工作台页、谱图 |
+| 仿真与回放 | 18791 | host(127.0.0.1) | 整机联调仿真台静态资产 |
+| 仿真四系统合页 | 18792 | host(127.0.0.1) | 从冻结资料包读取的四系统合页 |
+| 三维拆装工作台 | 64292 | host(127.0.0.1) | 三维资产页 |
+| 本机模型(可选) | 11434 | 本机 | 本地大模型推理服务,只影响问答与本地初筛 |
+
+对外只有一个入口 28084。这不是省事,而是边界:页面没有鉴权,把组件直接暴露出去等于把全场的运行数据摊开。要对外访问时的正确做法是限来源(防火墙白名单或反向代理加认证),或把 public_host 改回空串只用本机加隧道。
+
+### 2.3 系统能力清单
+
+表 2-2 系统能力清单(能力域、给谁用、载体、验收锚点,样本场实测(2026-09-22))
+
+| 能力域 | 能力 | 主要载体 | 验收锚点或判据 |
+|---|---|---|---|
+| 判级 | 38 台乘九系统判级矩阵,四轴随时间窗重算 | 工作台系统矩阵、逐台页、问题页 | 判级报警机组数随所选时间窗变化;样本不足如实写不可判 |
+| 部件问题 | 部件可靠性表、问题下钻、完整依据 | 工作台 component 与 vibration 标签 | 平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT)随所选时间窗变化;依据展开永不为空 |
+| 发电性能 | 七镜头曲线、七张月度时序图、控制参数一致性 | 工作台 energy 与 generation 标签 | 图注写「随所选时间窗」;样本量随时间窗变化;控制参数按所选时间窗重算 |
+| 振动 | 振动窗索引、谱库、CMS 报告、逐台页、在升与换件闭环、三层基线 | CMS 服务、工作台 vibration 标签 | 振动窗 w0316 索引 2,066,686 行;谱 420,742 条;缺件时如实空表并写边界 |
+| 台账 | 报警、工单、油样三门台账与停机事件 | 工作台 fault 标签、数据层维护页 | 报警 39,211 行、工单 5,876 行、油样 404 行 |
+| 本体与问答 | 对象库、检索索引、决策链、趋势证据、问答与校闸 | 工作台 assistant 与 decision 标签 | 本体审计 0 问题;两档模型都拦则不出文并给原因 |
+| 报告 | CMS 标准模版报告、事实契约与派生、门户结论段 | 报告页、门户、qc 派生件 | 契约改一条 claim 则四个消费端全变 |
+| 运维 | 一条命令重算、控制台按钮、清产物、数据扫描与看门狗 | 门户数据重算页、运维控制台 | 默认 26 步计划;并发动作返回 409;扫描退出码 0 与 4 与 5 |
+| 门户与检索 | 单文件门户、页面归口与陈旧检测、知识检索 | 门户、页面归口登记表 | 归口审计退出码 0;页面陈旧报 7 |
+| 数据接入 | 落位、体检、增量三清单、场站辨识 | 命令行脚本与维护页 | 结构合规;冲突拦下;场站辨识四条规则 |
+| 交付与运维 | 打包、开箱验证、服务化、版本检查、卸载 | install、pack、uninstall 入口与服务注册器 | 入口引用闭合 48 条;卸载入口 2 个;版本记录与代码一致 |
+
+## 3 需求来源与演进
+
+### 3.1 需求来源的三条线
+
+需求从三条线汇拢。**第一条是现场运行的需要**:值班要一眼看清全场哪几台机组有问题,点检要顺着问题找到部件与证据,管理要一份能对外的结论。**第二条是交付与运维的需要**:机器上没有网络、现场不会敲命令、数据每月增量、程序要能装能卸能升级。**第三条是用户令**:每一次人工测试或现场事故之后,用户用一句话把要求定下来(例如「所有的计算均要形成观澜的源代码」「对 data/raw 下文件的增减做到监听」「增加自定义时间段」),研发把它落成代码、机器守卫与文档。
+
+三条线里,用户令是最硬的驱动力:它带来的是**可核验的验收判据**,而不是一段描述。本版 13 条版本记录里,绝大多数条目的形态都是「检查到的问题 → 根因 → 解决 → 验证」,其中「验证」一栏写的就是实跑的退出码与逐值对拍结果。
+
+### 3.2 需求演进时间轴
+
+表 3-1 版本史与用户令要点(依据 src/version.py 的 HISTORY,共 14 条)
+
+| 版本 | 日期 | 用户令要点 | 变化级别 |
+|---|---|---|---|
+| 2.10.2 | 2026-09-22 | 数据要求说明改为纯数据需求规格:去样本场现状与实现细节、测点按业务含义列表、新增 SCADA 秒级数据要求(系统功能未变) | 小(patch) |
+| 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) |
+| 2.9.0 | 2026-09-21 | 时间窗真正生效:部件问题与发电性能随所选时间窗重算,新增自定义起止日期时间窗 | 中(minor) |
+| 2.8.2 | 2026-09-21 | 扫描器的变化判据被两类非摄入件常年占住:新増上游归档口径与忽略模式 | 小(patch) |
+| 2.8.1 | 2026-09-20 | 台账等价验收在全新机器上必然非零:改为容忍并写明「没做验收」 | 小(patch) |
+| 2.8.0 | 2026-09-20 | 输入数据自动扫描识别:逐族指纹发现新增与变化,指明该跑哪几步并纳入重算 | 中(minor) |
+| 2.7.0 | 2026-09-19 | 远程部署消缺:振动窗发布被占用不再打断重算链;门户可对外监听;控制台打印不炸 | 中(minor) |
+| 2.6.0 | 2026-09-19 | 所有的计算均要形成观澜的源代码:融合面、总览页、事实契约、发布层、变桨面、振动基线与在升全部自算 | 中(minor) |
+| 2.5.0 | 2026-09-17 | 卸载闭环、版本管理与打包命名规则、版本记录 | 中(minor) |
+| 0.4.0 | 2026-09-17 | 打包默认不含输入数据与产物与日志与临时文件;无窗口启动;统一配置与日志;页面归口;服务化 | 旧编号(legacy) |
+| 0.2.0 | 2026-09-01 | 含产物的旧交付基线,历史上用作「包内无生成端」那批产物的补救源 | 旧编号(legacy) |
+
+需求的演进如图 1-1 所示。该图依据 src/version.py 的 HISTORY 绘制,覆盖上表 14 条记录(即两级旧编号 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.10.2),没有大版本升级。这个分布清楚地显示出这条线的性质——**功能面在 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.10.1 在这条线上再走一步:文档去样本场标识并补上多风电场适用性一章,使同一份文档能被别的风电场直接拿去用。2.10.2 把《数据要求说明》改成纯数据需求规格:不写样本场的接入现状、不写英文测点名与文件名与落盘位置,测点一律用业务含义名称按类列表(含单位与必须性),并补上 SCADA 秒级数据的要求。
+
+### 3.3 需求如何被人工程序化
+
+用户令通常是一句话,落到系统里要经过「变成判据、变成机器守卫、变成可复核证据」三步。第一步最关键:把一句中文要求翻译成一个**可判定的条件**。例如「时间窗变化时部件问题要跟着变」,翻译成「给定时间窗 w,判级矩阵的返回必须随 w 而变,且月度件按月取整、日粒度件按日精确」,这样才有办法验。第二步是把它做成每次都会跑的守卫,而不是靠人记得。第三步是留下可复核的证据(日志、退出码、逐值对拍结果、sha256)。
+
+表 3-2 用户令到程序化落点的对照(节选,按版本递进,样本场实测(2026-09-22))
+
+| 用户令(原话要点) | 程序化落点 | 可核验证据 |
+|---|---|---|
+| 部件问题与发电性能应随下拉框值变化,并增加自定义时间段 | months_of(win) 与 win_range 与 in_win;taxonomy.system_matrix(span=);reliability.overview(span=);/api/curves?win= | 变桨报警 15 与 13 与 10、偏航 15 与 11 与 5、齿轮箱 7 与 4 与 4 分别对应 2026年与 2025H2 与 2026-07 |
+| 对 data/raw 含嵌套子目录下文件的增减做到监听 | scripts/raw_scan.py 逐族指纹(件数、体积、最新落盘、清单摘要、子目录清单);重算链 ①b 步;rebuild_all --auto | 实测 --check 由 rc=4 转 rc=0(与上次快照一致);空目录新建也能发现 |
+| 所有的计算均要形成观澜的源代码 | 逐族补生成端:变桨面、在升闭环、三层基线、融合面 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.2」 |
+| 观澜改为监听所有 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 条目;本版三份文档 |
+
+## 4 用户与场景
+
+### 4.1 角色
+
+系统没有账号体系,角色划分是**使用场景的划分**,不是权限的划分。这一点必须在需求层面写清:当前版本没有页面鉴权,谁打开浏览器谁就能看到全场的运行数据;对外可达时的风险控制靠网络层(防火墙白名单、反向代理加认证、只绑内网地址)而不是靠系统内部。角色表的价值在于把「谁要什么」变成可核对的功能覆盖清单。
+
+表 4-1 角色与核心诉求
+
+| 角色 | 典型身份 | 核心诉求 | 主要页面与入口 | 对应功能需求 |
+|---|---|---|---|---|
+| 值班运行 | 场站值班员、集控值班员 | 一眼看清全场状态;报警是否集中;有没有正在恶化的机组 | 门户总览、系统状态、工作台 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-52、NFR 全部 |
+
+### 4.2 场景到任务到页面
+
+场景不必多,八个够覆盖日常。每个场景都要能回答「谁、在什么时刻、要完成什么、打开哪一页、看到什么算成功」。这一步是需求分析里最有用的动作:它把抽象的「系统要好用」变成「点开这页三秒内能看出哪几台报警」。角色、场景与页面的关系如图 2-1 所示。
+
+表 4-2 场景到任务到页面
+
+| 场景 | 任务 | 页面或入口 | 成功的样子 |
+|---|---|---|---|
+| 交班巡检 | 看全场状态与报警集中度 | 门户总览、系统状态、数据重算页 | 一屏内看清哪几台报警、服务是否在跑 |
+| 例行点检 | 逐台看判级与四轴 | 工作台系统矩阵、逐台页 | 点台号即出该台各系统判级与依据 |
+| 异常追因 | 从报警下钻到部件与证据 | 问题页、部件问题页 | 依据展开有部件、链路进度、卡点、证据源 |
+| 振动专题 | 看振动判级、谱图、在升与换件闭环 | CMS 服务、工作台 vibration | 六层判读清楚,未闭环时明写「没有成对记录」 |
+| 时间窗对比 | 换一个时间窗看判级与性能是否变化 | v2 顶栏时间窗与两个日期输入框 | 图注写「随所选时间窗」;样本量随之变化 |
+| 月度汇报 | 出结论与报告 | 汇报纸、CMS 标准模版报告、门户结论段 | 结论带证据级、审级与处置措施 |
+| 数据接入 | 放新数据、体检、重算 | 命令行落位与体检脚本、运维控制台 | 冲突被拦下;重算结束出每步 OK 与耗时 |
+| 交付与升级 | 打包、开箱验证、装服务、卸载 | pack 与 install 与 uninstall 入口、服务注册器 | 版本检查提示正确;卸载后数据一件不少 |
+
+![图 2-1 角色—场景—页面关系](figures/fig-req-02-角色场景页面.png)
+
+### 4.3 角色与可用性边界
+
+与角色相关的一条硬需求是「**没有产物也要能打开页面**」。现场第一次装完、或者在控制台上点了「清除产物」之后,页面必须仍然可访问,并明确说出缺哪一件、怎么补,而不是白屏或掐断连接。这条需求来自两次现场事故:振动服务在无产物时曾因空表拼接抛异常带走进程,工作台在缺件时曾直接返回 500 让人分不清「没放数据」还是「程序坏了」。现在的口径是:缺件时返回结构化缺件页与 err=no_source,页面写清缺件与补齐办法。相关功能需求见 FR-33 与 NFR-06。
+
+## 5 功能需求
+
+### 5.1 功能需求编号与总览
+
+功能需求按十二个域组织,编号从 FR-01 到 FR-52,共 52 条。编号是**稳定的**:新增需求往后排号,不重排已有编号;需求作废时保留编号并标注作废,不改用它。每条需求都给出五项:需求描述、输入、输出、验收判据、依据文件。其中「验收判据」是可判定的条件,「依据文件」指向仓库内的源或文档,便于复核。
+
+表 5-1 功能需求域与编号分配
+
+| 域 | 编号区间 | 条数 | 一句话概括 |
+|---|---|---|---|
+| 判级与部件问题 | FR-01 至 FR-05 | 5 | 38 台乘九系统判级、四轴随时间窗重算、部件问题与依据 |
+| 时间窗与自定义区间 | FR-06 至 FR-08 | 3 | 时间窗词表、自定义起止日期、按时间窗重算的等待契约 |
+| 发电性能与可靠性 | FR-09 至 FR-12 | 4 | 七镜头、月度时序、控制参数一致性、损失与功率曲线 |
+| 振动与融合面 | FR-13 至 FR-17 | 5 | 摄入、报告、融合判级、在升闭环、三层基线 |
+| 停机与台账 | FR-18 至 FR-21 | 4 | 报警、工单、油样、停机与损失 |
+| 本体与问答 | FR-22 至 FR-25 | 4 | 知识层、铺开、决策链与趋势、问答与校闸 |
+| 报告导出 | FR-26 至 FR-28 | 3 | 标准模版报告、事实契约与派生、门户结论段与汇报纸 |
+| 运维控制台与重算 | FR-29 至 FR-32 | 4 | 一条命令重算、控制台、清产物、数据扫描 |
+| 门户与检索 | 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,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-01 | 给出 38 台机组乘九系统的判级矩阵,逐台逐系统带判级与依据,四轴判级随所选时间窗重算 | scada_10min 与 scada_1min 序列、报警台账、振动报告转录、机型契约 | 工作台系统矩阵、逐台页、问题页;taxonomy.system_matrix 结果 | 判级报警机组数随所选时间窗变化(实测变桨 15 与 13 与 10、偏航 15 与 11 与 5、齿轮箱 7 与 4 与 4);样本不足时如实写不可判 | docs/系统设计说明.md §16.2;src/windscada/taxonomy.py |
+| FR-02 | 判级四轴(变桨、偏航、蓄能、温度)随时间窗重算,其中变桨按日粒度精确切片、其余走窄仓重算 | 窄仓 slim10min 公共列子集;pitch 面的日粒度件;1min 桨距角 | 各面 registry(span=) 结果与判级单元格 | 同一台同一系统在不同时间窗下可给出不同判级;温度面的对照基线 ref 与 ref_sm 故意固定并在页面标注 | docs/系统设计说明.md §16.2 与 §16.3;scripts/scada_slim_build.py |
+| FR-03 | 部件可靠性表按所选时间窗重算,给出平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT)与停机时长 | 停机事件、报警事件(均为日粒度) | 可靠性表与部件问题页表格 | 停机事件与报警按时间窗精确过滤;三个指标随时间窗变化 | docs/系统设计说明.md §16.2;src/windscada/perf/availability |
+| FR-04 | 部件问题下钻:问题页与问题接口按中文系统名取数,服务端做双向归一,认不出时如实报「系统标识无法识别」 | 台号、系统标识(中文名或前端标识或大小写变体)、时间窗 | 问题页、/api/problem 响应 | 传前端标识与中文名都能取到同一份问题;认不出时不写「本时间窗无异常」这类假陈述 | docs/系统设计说明.md §17.2;scripts/windscada_serve.py 的 sys_norm() |
+| FR-05 | 需要关注的卡片必须给出完整依据,依据按本行字段自组句,永不为空 | 融合面链盘行、SCADA 关注集合、部件与链路进度 | 卡片依据文本 | 实测曾出现依据空白(WTG09 齿轮箱),现行口径为按部件、链路走到第几步、卡在哪一步、证据源自组句 | docs/系统设计说明.md §17.3;HISTORY 2.9.1 |
+
+### 5.3 时间窗与自定义区间
+
+时间窗是本版需求里最容易被误解的一块,因此口径要写死两句话:**月度件按月取整,日粒度件按日精确**;页面上要写明这两句,不让读者以为月度均值能切到天。
+
+表 5-3 时间窗与自定义区间(FR-06 至 FR-08,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-06 | 提供预设时间窗词表:近30日、近90日、2026年、2026H1、2025H2、全程、单月、月区间 | 用户选择 | months_of(win) 与 win_range(win) 结果 | 每种写法都能落到覆盖的月集合与起止日期;近30日在末月只有 1 个节拍时如实按样本不足处理 | docs/系统设计说明.md §16.1 |
+| FR-07 | 新增自定义起止日期时间窗,写法为起日期~止日期,**含两端**;顶栏提供两个日期输入框与「应用」并校验起不晚于止 | 两个日期输入框 | 与区间相交的月集合(月度件)、原样的日区间(日粒度件) | 非法输入被拒绝并给出提示;相交月取整与按日精确两套口径同时生效 | docs/系统设计说明.md §16.1;HISTORY 2.9.0 |
+| FR-08 | 按时间窗重算一律「命中进程缓存即回,未命中先回等待状态并由页面轮询」,绝不静默拿旧口径当新时间窗 | 时间窗参数、进程缓存、后台重算线程 | 判级矩阵与七镜头结果、等待与构建中的状态标记 | 判级约 25 秒每个时间窗、镜头约 10 至 45 秒每个时间窗;等待期间页面写明「下面这几张卡还是上一份口径」;选到 2025-07 至 2025-12 时直接用正式产物不重算 | docs/系统设计说明.md §16.4;docs/系统设计说明.md §16.2 |
+
+### 5.4 发电性能与可靠性
+
+表 5-4 发电性能与可靠性(FR-09 至 FR-12,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-09 | 发电性能七镜头曲线随所选时间窗重算,曲线图注写明随所选时间窗且样本量随时间窗变化 | 窄仓 10min 子集、风速与功率通道、限电命令 | /api/curves 响应与七张镜头图 | 换时间窗后分箱结果变化,样本量变化;限电前干净判别窗(2025-07 至 2025-12)走正式产物 | docs/系统设计说明.md §16.2;HISTORY 2.9.0 |
+| FR-10 | 发电性能七张月度时序图随时间窗过滤 | 月度派生件 | 七张月度时序图 | 图注写「随所选时间窗」;月度件按月取整 | docs/系统设计说明.md §16.2 |
+| FR-11 | 控制参数一致性随时间窗重算,输出功率封顶与转速上限等一致性结论 | 窄仓补入的控制参数列(窄仓共 48 列) | 控制参数一致性卡片 | 按时间窗重算约 0.6 秒;功率封顶中位实测由 4,190(干净时间窗)变为 4,181.7(2026年)与 4,177.5(2026Q1) | HISTORY 2.9.0;docs/系统设计说明.md §16.3 |
+| FR-12 | 损失、功率曲线与可用率派生件由原始件重算,替换随包件时逐值对拍 | scada_10min 与报警台账 | loss_monthly、powercurve_bins、powercurve_dev、stop_events 等 | 实测损失月表 3,729 行、功率曲线偏差 38 行、功率曲线分箱 912 行与基线逐值一致 | docs/数据目录结构与落位约定_v0.2.md §4 |
+
+### 5.5 振动与融合面
+
+振动这一域的写法要特别克制:它是全系统里**人工判断成分最多**的一段,因此需求必须区分「测量数据的函数」与「需要人工裁定的件」,不能把后者当成自动产物来承诺。
+
+表 5-5 振动与融合面(FR-13 至 FR-17,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-13 | 振动窗索引与谱库摄入:解析厂家导出文件,按振动窗落索引表与谱分片,并登记清单 | windcms 下的解码导出文件(25,693 件) | 振动窗索引表(54 列)、谱分片、谱元数据、摄入清单 | 振动窗 w0316 索引 2,066,686 行、谱 420,742 条与 1,712 分片;重复摄入同名时间窗默认替换并留档旧窗 | docs/系统设计说明.md §13.1;docs/数据目录结构与落位约定_v0.2.md §2b |
+| FR-14 | 生成 CMS 标准模版评估报告与逐台页、工作台页,缺融合级时如实写破折号 | 振动窗索引、标量缓存、三层基线 | 报告 md 与 docx、逐台页、总览页、登记 csv | 38 台判级齐全(实测报警 3 台、优秀 35 台);缺融合级列时写破折号而不是编造 | docs/系统设计说明.md §13.1;HISTORY 2.6.0 |
+| FR-15 | 融合面判级:由链盘、手交件与模型过闸线生成融合表与融合级,作为报告与判级矩阵的输入 | 振动窗索引、模型过闸线、厂家报告转录 | 融合表、handoff、全场标量 z 值 | 融合表 38 台逐台有裁决;handoff 与手交件同结构且标注由观澜自算 | docs/系统设计说明.md §13.1 与 §13.6 |
+| FR-16 | 在升与换件闭环证据:逐振动窗标量算在升判据,给出检出、换件、复测回落的成对记录 | 振动窗索引、融合面链盘 | 部件历史件、趋势证据对象 | 时间窗数不足时如实出空表并写明数据边界;现场提交的数据包里没有成对记录时文案写「暂无端到端闭环证据」 | HISTORY 2.6.0 与 HISTORY 2.9.1 |
+| FR-17 | 三层基线:自基线、机群基线与绝对限,用于振动判级的对照 | 振动窗索引标量、健康期分段 | 三层基线件 | 健康期分段中位与正常带按件算;绝对限引用标准限值;样本不足时如实空表 | docs/系统设计说明.md §13.1 |
+
+### 5.6 停机与台账
+
+表 5-6 停机与台账(FR-18 至 FR-21,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-18 | 报警台账摄入,按事件键集合并集去重,归属取最窄源件;源件删改后产物随之变化并大声报出 | 故障报警导出件(16 件) | 报警台账 | 39,211 行稳定;同一事件不重复计数;删一个源件不会让总数虚增 | HISTORY 2.8.0;docs/系统设计说明.md §13.1 |
+| FR-19 | 工单台账摄入,按固定模板解析(第三行为列名,数据自第四行起),字符串化口径与随包件一致 | 风机故障记录(137 件,其中台账 123 件) | 工单台账 | 5,876 行;与基线差异逐条可归类 | docs/系统设计说明.md §13.1 |
+| FR-20 | 油液化验索引,从文件名抽取台号、部件、日期与样品号,不读正文;采合并语义,认不出的历史行原样保留 | 油样报告(404 件) | 油样索引 | 404 行;2026-07 批 102 行的源件不在现场包内,清空重建会缺这 102 行,如实记账 | docs/系统设计说明.md §7 与 §13.1 |
+| FR-21 | 停机事件与停机损失:由报警与运行状态派生停机记录并绑定损失,作为可靠性表的输入 | scada_10min、报警台账 | 停机事件件、损失月表 | 停机事件按台落盘;损失月表与输入跨度对拍通过 | docs/系统设计说明.md §13.1;docs/数据目录结构与落位约定_v0.2.md §4 |
+
+### 5.7 本体与问答
+
+表 5-7 本体与问答(FR-22 至 FR-25,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| 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/说明书_观澜…v0.2.md §4.2;HISTORY 2.9.1 |
+
+### 5.8 报告导出
+
+表 5-8 报告导出(FR-26 至 FR-28)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-26 | 生成 CMS 标准模版评估报告,三轴表述:状态等级、证据状态、行动等级建议;同时出登记表 | 振动窗索引标量、三层基线、融合级 | 报告 md 与 docx、登记 csv | 38 台逐台有条目;报告章节可跳转;相对判据封顶为候选 | docs/系统设计说明.md §13.1;HISTORY 2.6.0 |
+| FR-27 | 事实契约:每条结论含结论编号、脱敏来源引用(文件加 sha16 与章节)、本条 sha256、时间窗、聚合层级与裁决枚举;派生四个消费端 | 由重算台账生成的 claim、评审底稿 | 契约件与门户结论段、工作台卡片、问答引用、报告摘要 | 实测改一条 claim 则四个消费端全变;契约自检不过时报退出码 2 并需人看 | docs/系统设计说明.md §13.1 与 §13.2 |
+| FR-28 | 门户结论段重装与汇报纸导出:产物变化后门户结论段随重装刷新;工作台可导出汇报纸 | 契约派生件、门户外壳与内嵌件 | 门户单文件页、汇报纸 | 装配先写临时文件再原子替换,失败不破坏在服务的门户;行尾强制 LF;产物挪走时缺该段属预期 | docs/数据目录结构与落位约定_v0.2.md §6;docs/系统设计说明.md §13.2 |
+
+### 5.9 运维控制台与重算
+
+表 5-9 运维控制台与重算(FR-29 至 FR-32)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| 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/说明书_观澜…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 门户与检索
+
+表 5-10 门户与检索(FR-33 至 FR-35)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-33 | 门户为单文件装配产物,壳与件分离;顶部菜单含总览、系统架构、方法、经验发现、案例、振动与 CMS、仿真与回放、交付文档、系统状态、数据重算、登录 | 受管外壳、内嵌交付件、契约派生件 | 门户单文件页 | 拆装回到逐字节相同;行尾 LF 有自检;数据重算页用内嵌版懒加载避免空转轮询 | docs/系统设计说明.md §5.2;docs/数据目录结构与落位约定_v0.2.md §6 |
+| FR-34 | 页面归口:登记每一页与子页的内容类型、是否随输入数据变、凭什么这么判;对数据派生页面做陈旧检测 | 页面登记表、产物台账、页面实物 | 归口表与审计结论 | 实跑检查 21 项、不一致 0、已知缺口 19;陈旧检测报退出码 7;未登记页单独列出 | configs/portal_pages.yaml;scripts/pages_audit.py |
+| FR-35 | 检索索引:由本体与知识层构建检索索引,词法检索在任何机器上可用,向量检索在装有嵌入模型时启用 | 对象库、CMS 知识库、嵌入模型(可选) | 检索索引件 | 无模型时保持纯词法可用并写明;索引缺失时页面如实说明而不是报错 | docs/数据目录结构与落位约定_v0.2.md §4;docs/系统设计说明.md §13.1 |
+
+### 5.11 数据接入与体检
+
+表 5-11 数据接入与体检(FR-36 至 FR-38,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| FR-36 | 现场包按映射表落位,落位前自动体检:结构、命名、后缀与增量三清单(新增、相同跳过、冲突) | 现场包目录、约定目录树 | 落位结果与体检结论 | 同名不同大小判为冲突并默认拒绝落位;覆盖需显式确认;同尺寸自动跳过可反复跑 | docs/输入数据放置指导_v0.1.md §2 与 §3 |
+| FR-37 | 场站扫描辨识:按四条规则辨识 data/raw 下的场站目录,多目录且都不匹配时报未识别并列出扫到的目录 | data/raw 下一级目录、场配置 | 辨识结论与依据 | 依据逐条打印;单站唯一性也要标注为凭唯一性而不是精确匹配;不猜 | docs/数据目录结构与落位约定_v0.2.md §2 |
+| FR-38 | 同台多件的 10min 件合并与上游归档口径:同台补充件按列名对齐并按时间戳去重(主件优先);类目库按信息级列出且不计变化 | 同台补充件、现场年度归档库、类目库 | 合并后的取数结果与扫描结论 | 实测 WTG01 主件 77,551 行加补充件 5,820 行合并 83,353 行、重叠 18 行去重;类目库不再造成每轮误报变化 | HISTORY 2.8.0 与 HISTORY 2.8.2;docs/输入数据放置指导_v0.1.md §4.1 |
+
+### 5.12 安装、服务化与卸载
+
+表 5-12 安装与服务化与卸载(FR-39 至 FR-43,样本场实测(2026-09-22))
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| 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 |
+| FR-43 | 打包与开箱验证:一条命令出包,默认不含输入数据、产物、运行日志与临时文件;打包器自带入口引用闭合与编码守则两道闸;开箱验证解压后真装真起 | 源码树、版本真源 | 交付包与清单文件、开箱验证结论 | 入口引用闭合实跑 48 条全在位;包内出现安装记录或快捷方式即删包并非零退出;页面可用数要求不少于 4 个 | docs/系统设计说明.md §9 与 §9.1 |
+
+## 6 非功能需求
+
+### 6.1 非功能需求清单
+
+非功能需求编号 NFR-01 至 NFR-12,共 12 条。它们大多是「性质」而不是「功能」:判据往往是一次实跑的输出,或者一条机器守卫的退出码。
+
+表 6-1 非功能需求(NFR-01 至 NFR-06,样本场实测(2026-09-22))
+
+| 编号 | 需求 | 判据 | 依据文件 |
+|---|---|---|---|
+| 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-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/说明书_观澜…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.2.docx》第 8 章与第 12 章 |
+
+### 6.2 性能与耗时实测
+
+性能需求不能只写「要快」,要写「在哪台机器上、什么规模、花了多久」。下表把已取证的耗时集中列出,未取证的项如实标注。
+
+表 6-3 耗时实测汇总(来源见「证据」列;未标日期者为 HISTORY 或文档中的既有记录,样本场实测(2026-09-22))
+
+| 环节 | 耗时 | 证据 |
+|---|---|---|
+| 自检整体与模型探针 | 模型探针 0.77 秒;同一次 check 结论为全绿 | guanlan.py check 实跑(2026-09-22,本机) |
+| 一行命令重算(远端一轮 24 个实质步骤汇总) | 单步量级:输入扫描 53.7 秒、三门台账 164 秒、SCADA 侧 1,025 秒、月度派生 99 秒、振动摄入 2,118 秒、变桨面 385 秒、在升 41.6 秒、基线 15.3 秒、总览页 78 秒、本体审计 7.1 秒、门户重装 3 秒、页面归口 9.5 秒 | HISTORY 2.8.1(远端复核记录) |
+| 窄仓全量扫(按时间窗重算的底座) | 约 2 分钟一次全量 | docs/系统设计说明.md §16.3 |
+| 判级矩阵按时间窗重算 | 约 25 秒每个时间窗 | docs/系统设计说明.md §16.4 |
+| 七镜头曲线按时间窗重算 | 约 10 至 45 秒每个时间窗 | docs/系统设计说明.md §16.4 |
+| 控制参数一致性按时间窗重算 | 约 0.6 秒每个时间窗 | HISTORY 2.9.0 |
+| 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/说明书_观澜…v0.2.md §3.1;docs/系统设计说明.md §4.2 |
+| 本机问答 | 显存不足时首答 30 至 60 秒;复杂题在升档下 5 至 15 分钟 | docs/说明书_观澜…v0.2.md §2 与 §11 |
+
+### 6.3 可移植与零配置
+
+「零配置」在本系统里有一个明确的技术含义:**路径只有一个真源,且解析基准是安装根而不是当前工作目录**。这条需求来自一串真实缺陷:写死场站名的路径在多场部署时会静默串场;按当前目录解析的相对路径在从别处启动时会读不到;同一个规则在三个文件里各写一遍就会出现两套显示口径。现在的口径是:代码与配置里只写相对路径,运行期由路径助手解析成绝对路径;产物只落在产物仓,原始件只从原始件目录读,交付静态层只为分发而存在;三者不互相写。
+
+与可移植相关的还有两条:一是解释器探测顺序(先看配置里指定的解释器,再看安装目录下的虚拟环境,最后回落到当前解释器),这让安装目录整体拷到别的盘或别的机器后仍然可用;二是输入输出脚本的字符守则(安装脚本用带 BOM 的 UTF-8 加 CRLF、批处理纯 ASCII 且 CRLF 无 BOM、shell 脚本 LF),三条都是现场真实事故换来的,并由打包、开箱验证与装机自检三处强制执行。
+
+### 6.4 可维护性与单一真源
+
+单一真源的范围在本版已经覆盖五项:版本号、路径、配置、日志、界面文案。每一项都配了「改一处、机器替你查其余几处」的守卫,例如版本号改动后由版本记录一致性门与打包器读真源保证同步,配置改动由配置审计器检查目录约定、命名、内容与引用闭合,日志改动由日志审计器检查唯一落点与行格式,界面文案改动由语言包检查保证前后端成对。
+
+表 6-4 单一真源与对应守卫(样本场实测(2026-09-22))
+
+| 真源 | 管什么 | 守卫与实跑结果 |
+|---|---|---|
+| src/version.py | 名称、版本、版本规则、版本史、包名 | 版本记录一致性检查;check 实跑报 v2.10.2 且记录表与代码一致 |
+| src/paths.py | 一切路径解析的基准与助手 | 配置审计器检查不手拼路径;实跑无不一致 |
+| configs 目录与登记表 | 端口、模型档、场配置、页面归口、配置登记 | 配置审计实跑已知缺口与白名单 14 条、提示 7 条 |
+| src/logfile.py | 日志目录、命名、行格式、保留策略 | 日志审计实跑无不一致、提示 87 条 |
+| src/windscada/lang.py | 中英界面文案成对 | 语言包实跑 845 条成对 |
+| src/entry_refs.py | 入口脚本引用的文件必须都在包里 | check 实跑入口引用闭合 48 条全在位;编码守则通过 |
+
+## 7 数据需求概述
+
+### 7.1 数据族与功能映射
+
+系统的数据需求可以概括成一句话:**七类现场源件加一类共享机理资料,喂出九个产物仓,页面只读产物**。每个源类目录名就是摄入接口,改名等于换接口。数据族与功能的对应关系如下表;逐类的字段、单位、必须性与质量要求见同批交付的《数据要求说明_观澜_2.10.2.docx》。
+
+表 7-1 数据族到功能的映射(样本场实测(2026-09-22))
+
+| 源族(约定落位) | 谁消费 | 喂出什么功能 | 进链步骤 | 现状 |
+|---|---|---|---|---|
+| 故障报警 | 报警摄入器 | 报警台账、停机损失、限电绑定 | ② 三门台账 | 16 件 / 23.8 MB,结构合规 |
+| 风机故障记录 | 工单摄入器 | 检修工单台账、重复检修、闭环验证 | ② 三门台账 | 137 件 / 149.8 MB,其中台账 123 件、附件 14 件 |
+| 油样报告 | 油样索引摄入器 | 油液化验索引、油液时效、融合面油样轴 | ② 三门台账 | 404 件 / 176.2 MB,两级目录合规 |
+| scada_10min | 取数层与十个构建器 | 功率曲线、损失、温度、停机、偏航、液压、热链、变桨面日粒度 | ③ 与 ④ 与 ④c 与 ⑤b | 114 件 / 16.75 GB,含同台补充件 |
+| scada_1min | 取数层 | 变桨面零位三口径与越线计数 | ④c 变桨面 | 38 件 / 13.71 GB |
+| 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;正本缺失时由观澜自算件顶上 |
+| 主机制造商(OEM)技术资料(与场站并列的共享目录) | 本体层 | 码表、手册、工单展开、失效树、实机参数、检索索引 | ⑦ 本体层 | 318 件 / 2.94 GB,结构合规 |
+
+### 7.2 体量与现状
+
+表 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/<场>,递归) | 3,549 件 | 7,157.5 MB | 含仓级台账 3 件;反向呼应审计报成立 3,548 件 |
+| 其中振动窗与谱仓 | 3,417 件 | 占比最大 | 谱分片与窗级分析件 |
+| 其中标准仓 | 57 件 | — | 含窄仓子目录与总览单页 |
+| 未归类件 | 1 件 | — | 场站目录下的一份按月提取核对件,按设计不作为摄入源 |
+| 缺口 | 24 项 | — | 年度归档库中 7 月的十个类目库,扫描器按缺口级列出并写明消费者口径 |
+
+数据现状里有两处必须如实说明。第一处是**缺口**:年度归档库 2026 年 7 月有 10 个类目库不在位(每类一件,扫描器按缺口级列出并提供消费者口径),另外类目库本身是上游归档件,取数层不直接读它,需要先合成为同台补充件才会被摄入。第二处是**按月提取核对件**:场站目录下有一份按月提取的核对件,位置由用户指定、不是摄入源,扫描器按名字模式忽略,既不报未归类也不计入变化。
+
+### 7.3 与数据要求说明的分工
+
+本文只回答「要哪些数据、这些数据支撑什么功能」。数据的字段级要求(核心测点的名称、单位、必须性、缺失替代、对齐规则、质量门与核对锚点)以及面向现场的收资清单,写在《数据要求说明_观澜_2.10.2.docx》里。该文档按用户令要求与现场收资文件逐条对照,并对现场收资层面的已知缺失逐条如实记录,例如测风塔数据为零交付、故障录波只有 4 台、振动侧 handoff 正本缺失由观澜自算件顶上、远端机器未安装本机模型等。
+
+## 8 页面与信息架构需求
+
+### 8.1 页面归口规则
+
+页面归口要回答两个问题:这一页该不该随输入数据变化;它算不算产物。答案落在五类内容类型上,每一类都有机器可查的规则,登记表里逐条写明判定依据(看的是页面里有没有内嵌数据、有没有实时取数、有没有产物引用,以及内容是不是由原始件算出来的)。要改分类,先改证据,这是需求层面的一条纪律。
+
+表 8-1 五类页面内容类型与机器规则
+
+| 类型 | 该随数据变 | 算不算产物 | 机器查什么 |
+|---|---|---|---|
+| 受管静态 | 否 | 不是 | 正文不得引用产物,引用了说明分类错了 |
+| 实时取数 | 是,永远一致 | 页面本体不是 | 内嵌或链接的端口必须在已知服务表里 |
+| 产物引用脚注 | 否,正文不变 | 正文不是,被引产物是 | 被引用的产物路径必须存在,悬空即报 |
+| 数据派生 | 是 | 是 | 必须有来源加指纹或生成时间;有指纹就与当前产物比对做陈旧检测 |
+| 冻结交付件 | 否,按交付版本变 | 不是运行时产物 | 文件名或正文必须能读到版本号与日期 |
+
+### 8.2 门户页面归口表
+
+表 8-2 按登记表逐条列出页面与子页的归口结论。登记表顶层条目 25 条,含 7 条子页共 32 条;归口审计实跑输出为「检查 21 项:不一致 0、已知缺口 19」,其中 21 项是审计的**检查行数**(19 条为引用悬空或溯源缺失的已知缺口项,2 条为两个数据派生页面的溯源检查项,均一致),与登记表的条目数不是同一个口径,两者都不矛盾,此处一并写明以免混淆。
+
+表 8-2 门户页面与子页归口表(25 条顶层条目加 7 条子页,样本场实测(2026-09-22))
+
+| 页面或子页 | 类型 | 随输入数据变 | 判定依据摘要 |
+|---|---|---|---|
+| index 总览 | 受管静态 | 否 | 外壳段约 2,519 字节,无内嵌与取数与产物引用,正文是产品承诺与入口导航 |
+| architecture 系统架构 | 受管静态 | 否 | 外壳段约 3,037 字节,无数据引用,描述分层架构 |
+| method 方法 | 受管静态 | 否 | 外壳段约 2,959 字节,无数据引用 |
+| findings 经验发现 | 受管静态 | 否 | 外壳段约 2,322 字节,内容是公司级经验与内部检查条数,不是本场站数据的函数 |
+| case_hydraulic 案例与液压 | 受管静态 | 否 | 外壳段约 6,187 字节,无数据引用 |
+| cms 振动与 CMS | 实时取数 | 是 | 一个内嵌加两个链接指向振动服务,服务按需读产物,页面本身不存快照 |
+| recalc 数据重算 | 实时取数 | 是 | 链接到运维控制台,后端即真实状态 |
+| login 登录 | 实时取数 | 是 | 链接到工作台 |
+| admin 系统状态 | 实时取数 | 是 | 外壳脚本拉健康自检接口渲染,段内无静态数字 |
+| sim 仿真与回放 | 受管静态 | 否 | 外壳段约 50,767 字节为静态叙述,六个链接指向仿真台与三维台;输入是图纸与机型参数 |
+| 子页 控制律仿真台资料包(5 页) | 冻结交付件 | 否 | 仿真合页服务直接从冻结资料包读取,日期在包名里 |
+| 子页 三维拆装工作台 | 受管静态 | 否 | 三维资产由图纸与模型构建,与原始件无关 |
+| 子页 控制律仪表台面板(门户内嵌) | 产物引用脚注 | 否 | 静态判据说明加一条产物脚注引用,被引目录不在包内,已按悬空如实记账 |
+| 子页 四系统判据面板(门户内嵌 4 份) | 产物引用脚注 | 否 | 同上,静态正文加产物脚注 |
+| documents 交付文档 | 受管静态 | 否 | 外壳段约 5,690 字节为导航页,一个内嵌指向网关下发的治理清单页,段内不含数据快照 |
+| 子页 治理清单交付包 | 冻结交付件 | 否 | 文件名自带版本号与日期,客户交付件按交付版本冻结 |
+| 子页 全场状态一览(脱敏) | 冻结交付件 | 否 | 性质是数据派生但无指纹无生成端,按用户裁决按冻结交付件管并把会变旧的风险如实记账 |
+| 子页 场站取数单 | 冻结交付件 | 否 | 只有日期没有指纹,无生成端,且它引用的两棵产物树不在包内;保留不删并记账 |
+| page_product_cms CMS 报告页与件 | 数据派生 | 是 | 振动服务读的就是产物页面件;已在产物台账中登记并有溯源 |
+| page_product_vib_raw_manifest 振动摄入清单 | 数据派生 | 是 | 每次振动摄入重写,记件数、跨度与未随包缺口;已在台账中登记 |
+| embed_replay_chain 传动链回放(内嵌) | 数据派生 | 是 | 内嵌数据带来源与指纹,源产物在本包不在位,故现只能按生成时间判新旧 |
+| embed_standard_panel 标准面板(内嵌) | 数据派生 | 是 | 正文带生成时间与产物引用,缺指纹;被引的发布件不在包内 |
+| embed_coverage_ch0 覆盖度报告(内嵌) | 数据派生 | 是 | 正文写有来源与指纹,但那份源件不在包内,溯源链断在包外 |
+| embed_u6_sim 变桨液压仿真台(内嵌) | 受管静态 | 否 | 图纸派生的物理回路仿真面板,无内嵌数据与指纹 |
+| embed_u6_health 液压公共站健康报告(内嵌) | 冻结交付件 | 否 | 客户交付报告,无数据指纹,按交付件冻结 |
+| embed_gearbox_report 整机综合诊断报告(内嵌) | 冻结交付件 | 否 | 报告类交付件,文件名带修订版号 |
+| embed_reports_4 分系统评估报告(内嵌 4 份) | 冻结交付件 | 否 | 交付报告,无内嵌数据与指纹 |
+| embed_governance_12 治理清单分册(内嵌 12 份) | 冻结交付件 | 否 | 与交付层分册同源,冻结交付件 |
+| embed_sc1_local 本地占位(内嵌) | 受管静态 | 否 | 小体积占位面板 |
+| detail_tabs 工作台十个标签 | 实时取数 | 是 | 单页应用按标签现场取数,产物指纹变化自动重载,页面本体不存快照 |
+| delivery_pages_all 交付页 | 冻结交付件 | 否 | 客户交付件自带版本号与日期;其中两页性质上是数据派生,另见逐页判定 |
+| viewer_pages_all 三维页 | 受管静态 | 否 | 图纸与模型派生的资产页,与原始件无关 |
+
+按类型统计的分布如图 3-1 所示。本表口径下 32 条条目中,受管静态 11 条、实时取数 5 条、数据派生 5 条、冻结交付件 9 条、产物引用脚注 2 条。这张统计的意义在于:**真正需要随输入数据变的只有 5 条数据派生条目与 5 条实时取数条目**,其余 22 条要么是受管正文、要么是按版本冻结的交付件,把它们混进「产物管理」只会制造无谓的维护负担。
+
+![图 3-1 门户页面归口分布(依据 configs/portal_pages.yaml)](figures/fig-req-03-页面归口分布.png)
+
+### 8.3 页面陈旧检测要求
+
+数据派生页面的核心风险是「数据换了页面没换」,也就是页面里烘进去的快照与当前产物不一致。需求因此要求三件事:一是每条数据派生条目必须有来源路径,并且有指纹或生成时间;二是有指纹的必须与当前产物比对(逐字节比对哈希),不一致即报陈旧;三是登记表里写清「为什么这么判」,可复核。检查器的退出码语义为:0 表示一致,5 表示缺文件或未登记,6 表示溯源缺失,7 表示页面陈旧,8 表示交付件缺版本号,9 表示分类错。
+
+当前如实状态是:登记表里 19 条已知缺口全部是「页面引用了本包没有的产物树」,逐条写明理由而不装作通过;这些页面因此拿不到当前值做比对,其中两条数据派生页面没有生成端、三条内嵌快照缺指纹只能按生成时间判断。这三类都记账在案,待相应产物线随包后复检。
+
+## 9 验收需求与质量门
+
+### 9.1 质量门全景
+
+系统的验收分两层:**装机自检**看「这台机器能不能用」,**重算链验收**看「这一轮数据算得对不对」。前者是 guanlan.py check 的四十余条判定行,后者是重建命令每一环的退出码与容忍口径。两层的关系是:自检不通过就不该跑重算(环境或依赖有问题),重算链跑完再由自检复核产物与台账。
+
+表 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.2,包名 app_guanlang_v2.10.2.zip);卸载入口两个都在位 | 全 OK |
+| 制品与台账审计 | 5 | 反向呼应 3,548 件成立;页面归口检查 21 项;配置统一;日志统一;输入数据放置体检 26,503 件结构合规 | 全 OK |
+| 产物与发布件在位 | 10 | 标准仓、本体对象库、findings、事实契约、门户、仿真合页服务与资料包、三维资产、仿真回放资产、治理清单交付件 | 全 OK |
+| 原始件目录 | 1 | 原始件目录存在(无数据时此项不影响页面) | 全 OK |
+| 模型闸 | 6 | 默认档探针 0.77 秒;四个模型档逐个在位;模型可用性正常并记录审计日志 | 全 OK |
+| 合计 | 41 | 另有 6 条端口状态行不计入判定 | 结论全绿,可 serve,退出码 0 |
+
+验收门的分类与数量如图 7-1 所示。该图与上表同源,都取自本次自检实跑的 41 条判定行;按上表的分类口径,环境与依赖一类共 10 条、静态质量门 4 条、子进程口径 3 条、版本与卸载入口 2 条、制品与台账审计 5 条、产物与发布件在位 10 条、原始件目录 1 条、模型闸 6 条。其中真正体现「这个系统有没有把口径管住」的是制品与台账审计那五条门与四条静态质量门。
+
+![图 7-1 验收门分类与数量(依据 guanlan.py check 实跑输出)](figures/fig-req-04-验收门分类.png)
+
+### 9.2 重算链的逐步验收门
+
+重算链默认 26 步,加跳过 SCADA 为 23 步(另有第 ① 步仅在给了现场包目录时才跑)。每一步都是一个验收门:非零退出即停下并打印该步说明,容忍清单里的退出码不算失败,但会在汇总行里如实标出退出码。这个设计的关键在于「容忍不等于通过」:例如月度派生件找不到基线时返回 5,含义是「跳过了等价验收」,而不是「验收通过」。
+
+表 9-2 重算链逐步验收门(默认 26 步,样本场实测(2026-09-22))
+
+| 序 | 步骤 | 主要脚本或命令 | 容忍退出码 | 非零的含义 |
+|---|---|---|---|---|
+| 1 | ① 放数据(仅当给了现场包目录) | place_raw_data.py | 无 | 落位失败或冲突被拦下 |
+| 2 | ①b 输入数据扫描 | raw_scan.py --check --write | 4 与 5 | 4 表示有新数据(不是失败),5 表示还没有快照 |
+| 3 | ② 三门台账 | rebuild_from_raw.py | 无 | 报警或工单或油样摄入失败 |
+| 4 | ③ SCADA 侧十个构建器 | rebuild_from_raw.py --scada | 无 | 原始件缺失或读取失败 |
+| 5 | ③b 标准仓窄仓 | scada_slim_build.py | 无 | 按时间窗重算的底座没建起来 |
+| 6 | ④ 月度派生件 | windscada_monthly_build.py | 5 | 找不到随包基线,跳过等价验收 |
+| 7 | ④b 振动摄入与报告与标量 | vib_raw_build.py | 无 | 解析出错;没有振动原始件时空跑属正常 |
+| 8 | ④c 变桨面 | pitch_face_build.py | 4 | 找不到 SCADA 原始件,按缺件如实报 |
+| 9 | ④d 振动在升与换件闭环 | component_history_build.py | 4 | 本机没有振动窗索引,按缺件如实报 |
+| 10 | ④e 三层基线 | baseline_38_build.py | 4 | 同上 |
+| 11 | ⑤b 由重算台账生成 claim | sop_findings_from_ledger.py | 无 | 台账不足以生成结论 |
+| 12 | ⑤c 事实契约构建与渲染 | guanlan_facts_contract.py build | 2 | 契约自检不过,需要人看 |
+| 13 | ⑤a 逐件来源台账 | products_restore_missing.py --refresh | 无 | 账实不符 |
+| 14 | ⑤ 反向呼应审计 | products_reverse_audit.py --check | 5 | 有未归类件或判据失败,停下看清单 |
+| 15 | ⑥ 重启组件服务(停,保留网关) | _ops_stop_keep_gateway.py | 无 | 停服务失败 |
+| 16 | ⑥ 重启组件服务(起) | guanlan.py serve | 1 | 有模块未就绪属降级正常状态 |
+| 17 | ⑦ 本体码表与手册与文档 | kb_ingest | 无 | 技术资料缺失会降级并打印提示 |
+| 18 | ⑦ 本体铺开判级与台账 | populate | 无 | 一层正本缺失或来源不足 |
+| 19 | ⑦ 本体决策链进度 | chain_ingest | 无 | 服务未在跑则取不到链盘 |
+| 20 | ⑦ 本体在升与闭环证据 | trend_ingest | 无 | 源缺失 |
+| 21 | ⑦ 本体检索索引 | retrieval.build | 无 | 索引构建失败 |
+| 22 | ⑦ 本体实机参数表 | maintenance.refresh_params | 无 | 参数源缺失 |
+| 23 | ⑦b 全场状态总览页 | windscada_overview_build.py | 无 | 缺产物时响亮报错,不产出半张页 |
+| 24 | ⑧ 本体审计 | ontology.audit | 无 | 期望 0 问题 |
+| 25 | ⑧b 重装门户 | portal_build.py | 1 | 缺契约产物时属预期,门户保留原样 |
+| 26 | ⑧c 页面归口审计 | pages_audit.py --check | 5 与 6 与 7 | 7 表示有页面陈旧;5 表示被引产物不在位;6 表示溯源缺失 |
+| 27 | ⑧d 版本记录一致性 | version_log.py --check | 6 | 文档与版本真源不一致,需同步文档 |
+| 28 | ⑧ 台账等价验收(仅加开关时) | rebuild_from_raw.py --verify | 4 与 5 | 4 表示有需人工看的差异;5 表示没基线即没做验收 |
+
+关于步数需要两点说明以免误读。第一,表中共列出 28 条命令行,其中第 1 步(放数据)仅在给了现场包目录时才执行,最后一步(台账等价验收)仅在加了对应开关时才执行,因此默认计划的实跑步数是 26 步,加跳过 SCADA 选项后是 23 步。第二,「门」与「步」不是一一对应:第 ⑥ 步拆成停与起两条命令各算一步,第 ⑦ 步拆成六条命令各算一步,所以表里的命令行数多于环节数。
+
+### 9.3 检查器的退出码语义
+
+表 9-3 主要检查器的退出码语义与容忍口径
+
+| 检查器 | 退出码语义 | 链上容忍 |
+|---|---|---|
+| guanlan.py check | 0 全绿可启动;2 有失败项 | 不作为链上步骤,装机后人工核 |
+| rebuild_all.py | 0 全部完成;1 有步骤失败(失败即停) | 汇总行逐条标 OK 或 OK 带退出码或 FAIL |
+| pages_audit.py | 0 一致;5 缺文件或未登记;6 溯源缺失;7 页面陈旧;8 交付件缺版本;9 分类错 | 容忍 5 与 6 与 7 |
+| products_reverse_audit.py | 0 全部有来路;5 有未归类件或判据失败 | 容忍 5 |
+| version_log.py | 0 与版本真源一致;6 记录表需重生成 | 容忍 6 |
+| config_audit.py | 0 无不一致;8 存在旧副本等结构问题 | 未进链,由 check 报 |
+| raw_scan.py | 0 无变化;4 有新增或变化(不是失败);5 还没有基线 | 容忍 4 与 5 |
+| raw_data_check.py | 0 合规;5 结构或命名违例;6 增量冲突;7 缺源类或时间空洞;8 仅有提示 | 未进链,由 check 报 |
+| rebuild_from_raw.py --verify | 0 逐值一致;4 有需人工看的差异;5 找不到基线即没做验收 | 容忍 4 与 5 |
+| ontology.audit | 0 无问题 | 不容忍 |
+| service_ctl.py | 0 成功;3 权限不足(给出可执行建议) | 未进链,人工执行 |
+
+### 9.4 开箱验证与卸载核验
+
+交付质量有两条独立的核验路径:打包后的**开箱验证**(在临时副本上真解压、真安装、真起服务、真核页面)与**卸载核验**(在刚装好的副本上真跑卸载并断言拆除范围与保留范围)。两条路径的意义是:它们都作用在副本上,不打扰正在运行的实例。
+
+表 9-4 开箱验证与卸载核验的项与判据(样本场实测(2026-09-22))
+
+| 核验路径 | 核验项 | 判据 | 实测记录 |
+|---|---|---|---|
+| 开箱验证 | 入口引用闭合与编码守则 | 入口脚本引用的文件必须都在包内;编码与字符守则全部通过 | 解压 3,734 件,入口引用闭合通过(2026-09-17 记录) |
+| 开箱验证 | 离线安装 | 安装脚本退出码 0 | 本机记录 172 秒、退出码 0 |
+| 开箱验证 | 快捷方式与无窗口 | 两份快捷方式在位;无控制台窗口断言成立;包内无被弃用的脚本宿主文件 | 窗口句柄为 0,断言通过 |
+| 开箱验证 | 页面可用性 | 核验门户与工作台与振动与运维控制台与健康自检五个入口,要求不少于 4 个可用 | 实测 4 个可用(振动入口因无产物返回 503,属预期) |
+| 开箱验证 | 逐件复核 | 包内每个文件与工作树逐字节相同 | 3,733 件逐字节相同 |
+| 卸载核验 | 拆除范围 | 安装记录、运行态、快捷方式都被拆掉 | 在临时副本上真跑卸载,退出码 0 |
+| 卸载核验 | 保留范围 | 数据与产物一件不少,端口已释放 | 件数不变,断言通过 |
+| 卸载核验 | 只读预演 | 内容断言(步骤齐全)与副作用断言(预演后关键目录与文件的存在性与字节数完全不变) | 自检全绿 |
+
+关于「五个入口全可用」这一条,需要如实说明:判据本身是「不少于 4 个可用」,实测记录为 4 个可用,第 5 个(振动入口)在没有产物的机器上按设计返回 503 并给出缺件说明。本次编写未取到 5 个全部可用的实测记录,因此本文不写「5 个全通过」,只写「判据为不少于 4 个、实测 4 个」,并把「在产物在位的机器上复跑取证」列为后续动作(未取证)。
+
+### 9.5 本次实跑结论
+
+本次编写期间对只读项做了三次实跑:自检(guanlan.py check)结论为全绿可启动、退出码 0、无失败项;归口审计(pages_audit.py --check)结论为全部一致、退出码 0、21 项检查里 19 项是已登记的已知缺口;重算计划(rebuild_all.py --dry-run)打印默认 26 步、跳过 SCADA 为 23 步,全部步骤命令与脚本都在位。没有执行任何重算或耗时计算,本文中与耗时有关的数字全部来自既有的 HISTORY 记录与设计文档,来源逐条列在表 6-3。
+
+## 10 约束、假设与明确不做
+
+需求的可信度一半来自「写清了不做什么」。本系统的边界不多但很硬:它是**只读分析系统**,不是控制系统,也不是云端服务。
+
+表 10-1 约束、假设与明确不做(样本场实测(2026-09-22))
+
+| 类别 | 条目 | 说明与依据 |
+|---|---|---|
+| 约束 | 不写回现场系统 | 全部产物落在本机产物仓,不下发指令、不修改现场任何系统;原始件按只读输入对待,落位冲突默认拒绝 |
+| 约束 | 不做实时控制 | 最小时间粒度是分钟级数据(10min 与 1min),系统的输出是分析与建议(带证据级与处置措施),没有控制回路 |
+| 约束 | 无外网 | 计算、页面、检索都在本机完成;Windows 侧离线安装件与便携运行时随包;本机模型为可选件且单独交付 |
+| 约束 | 不做云端 | 事实契约层留有可上云面孔(脱敏派生件),但本版不部署云服务、不做账号体系、不做多租户 |
+| 约束 | 页面无鉴权 | 这是知情取舍而非疏忽:对外监听等于把全场运行数据摊开,必须在网侧限来源或在反向代理加认证,启动日志每次提醒 |
+| 约束 | 安装目录不得含空格与中文 | 安装脚本、批处理与部分第三方组件的既有约束,写在说明书的系统要求一节 |
+| 约束 | 数据规模受单机资源限制 | 当前输入 26,504 件 / 224.67 GB;整条重算在单机上以小时计,建议内存 16 GB 起 |
+| 假设 | 现场数据按约定目录与命名交付 | 源类目录名即摄入接口,改名或多套一层会导致摄入静默读不到,因此用体检器与落位脚本前置把关 |
+| 假设 | 现场具备解释器或允许使用包内便携运行时 | 麒麟与统信自带解释器多为 3.7 与 3.9,跑不了本项目依赖,故包内带便携运行时 |
+| 假设 | 有至少一份能解析的厂商报告与至少一段原始运行数据 | 否则相关面按缺件如实报,不产出假数;这也是标定一次基线的前提 |
+| 假设 | 无人值守场景会使用服务而非会话前台进程 | 会话里起的进程会随会话关闭被一起收掉,远程部署必须用服务 |
+| 不做 | 不做训练回路 | 本版不含训练,模型只做转述与解释,判断在代码里 |
+| 不做 | 不做知识图谱的自动推断 | 本体层零判级权,只转录一层正本;同部件多源判级并存不合并 |
+| 不做 | 不做实时数据流接入 | 数据以批的方式落位、体检、重算;服务侧看门狗只做到发现变化并列出该跑的步骤,不做流式处理 |
+| 不做 | 不为「无生成端」的件做运行期补齐 | 运行期一律不从交付包补齐;缺件要么放原始件重算,要么由研发补生成端 |
+| 不做 | 本版不承诺「多场并存运行」的完整交付 | 场配置机制与场定义装载已接通,但本版端到端跑通并留下实测记录的只有样本风电场;新场接入按第 11 章的多风电场适用性需求与换场验收检查表执行,不把「配置里放一个场文件」当作换场完成 |
+
+## 11 多风电场适用性需求
+
+### 11.1 适用性目标
+
+本章回答一个问题:**换一座风电场,要改什么、不要改什么**。目标是同一套系统(同一份代码、同一个交付包)与同一份需求文档,在**不改代码**的前提下换场运行;换场时只改两类东西——**配置**与**数据**。凡是必须改代码才能换场的,都视为不满足本章要求,回到第 5 章走需求变更。本章的功能需求编号接着原有编号往下排,为 FR-44 至 FR-52;非功能需求为 NFR-12。
+
+适用性目标分三层写清。**入口不换**:安装、落位、重算、页面与验收的入口和流程与样本场完全一致,现场人员不需要学第二套操作。**口径不散**:场名、机组数与机组清单、机型参数、原始件路径、判据阈值与界面单位都从配置取,不在代码里写死;一旦写死,多场并存时就会静默串场。**缺件不造**:新场某一族数据没到位时,系统如实标注缺件并按缺族降级,绝不拿别的场的数字顶替,也不把样本场的阈值当成新场的阈值。
+
+本章有两个词要先定义。「本场」指**当前被选中的场**,由配置选定,不必然等于本文取数的样本风电场;「换场」指从一场切到另一场运行的整套动作(改配置、放数据、重跑、按检查表复验),不是把两场的数据混在一棵树里。本章沿用的占位符与附录 A 一致:`<场>`、`<场站>`、`<机型>` 按实际风电场替换。
+
+本章需求与《数据要求说明_观澜_2.10.2.docx》《设计说明_观澜_2.10.2.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 机组与机型可替换
+
+机组数、台号、机型与主机制造商(OEM)的差异不进入引擎:引擎按场对象给出的机组清单与额定容量工作,机型差异通过**机型—场站数据契约** `configs/contracts/<机型>_<场>.yaml` 落到列名、单位与阈值上。换机型等于换契约,不改引擎。
+
+表 11-2 机组与机型替换需求(FR-47)
+
+| 编号 | 需求描述 | 输入 | 输出 | 验收判据 | 依据文件 |
+|---|---|---|---|---|---|
+| 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.2.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 需求跟踪矩阵
+
+| 编号 | 实现(脚本或模块) | 页面或产物 | 验收门 |
+|---|---|---|---|
+| FR-01 | taxonomy.system_matrix;scripts/windscada_serve.py | 系统矩阵、逐台页、问题页 | 判级随所选时间窗变化的实测记录;check 归口与反向呼应 |
+| FR-02 | scada_slim_build.py;subsys 各面 registry | 窄仓、判级单元格 | 重算链 ③b 步;窄仓列数与口径同源 |
+| FR-03 | perf/availability;reliability.overview | 可靠性表、部件问题页 | 判级与可靠性随时间窗变化的实测记录 |
+| FR-04 | windscada_serve.py 的 sys_norm() | 问题页与问题接口 | 传前端标识与中文名都能取到同一份问题(HTTP 抽样) |
+| FR-05 | 组件页面自组句逻辑 | 需要关注卡片 | HTTP 抽样:依据不再为空(WTG09 齿轮箱) |
+| FR-06 | months_of;win_range;in_win | v2 顶栏时间窗控件 | 时间窗词表逐项验证;退化时间窗按样本不足处理 |
+| FR-07 | 顶栏两个日期输入框与校验 | v2 页面自定义起止控件 | 起晚于止时拒绝并提示 |
+| FR-08 | 进程缓存与后台线程与预热 | 等待与构建中状态标记 | 判级约 25 秒每个时间窗;等待期页面写明旧口径 |
+| FR-09 | curves.build_store;/api/curves | 七镜头曲线 | 图注写随所选时间窗;样本量随时间窗变化 |
+| FR-10 | 月度派生件过滤 | 七张月度时序图 | 图注与按月取整口径 |
+| FR-11 | control.registry(span=) | 控制参数一致性卡片 | 按时间窗重算约 0.6 秒;封顶中位随窗变化 |
+| FR-12 | rebuild_from_raw.py --scada | 损失、功率曲线、停机事件 | 与基线逐值对拍;重算链 ③ 步 |
+| 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 | <场>_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 行;键集合并集去重 |
+| FR-19 | windscada_workorder_ingest.py | 工单台账 | 5,876 行;差异可归类 |
+| FR-20 | windscada_watch_channels_build.py | 油样索引 | 404 行;102 行缺口如实记账 |
+| FR-21 | 停机与损失构建器 | 停机事件件、损失月表 | 与输入跨度对拍 |
+| FR-22 | src/ontology/kb_ingest.py | 对象库、检索索引、实机参数表 | 本体审计退出码 0;故障码覆盖 263 与 322 |
+| FR-23 | src/ontology/populate.py | 对象库对象 | 本体审计退出码 0 |
+| FR-24 | chain_ingest;trend_ingest | 决策对象与趋势证据对象 | 幂等覆盖;服务在跑时取链盘 |
+| FR-25 | src/ontology/fast_agent.py;llm_gate;检索 | 问答区与引文块、模型审计日志 | 已知答案回放;两档都拦时不出文并给原因 |
+| FR-26 | src/windcms/report_std.py;report.py | 报告 md 与 docx、登记 csv | 38 台逐台有条目;相对判据封顶候选 |
+| FR-27 | guanlan_facts_contract.py | 契约件与四个消费端派生件 | 改一条 claim 四个消费端全变;自检不过报 2 |
+| FR-28 | portal_build.py;门户装配脚本 | 门户单文件页、汇报纸 | 拆装逐字节相同;产物挪走时缺结论段属预期 |
+| FR-29 | scripts/rebuild_all.py | 各仓产物与步汇总 | 计划步数实跑;失败即停;幂等 |
+| FR-30 | guanlan_ops.py;_ops_run.py;_ops_launch.py | 运维控制台 | 并发动作返回 409;停组件保留控制台 |
+| FR-31 | products_state.py;products_restore_missing.py | 产物状态与缺件说明 | 清除后再点返回 409;补齐定位为离线人工补救 |
+| FR-32 | raw_scan.py;rebuild_all --auto;service_worker 的看门狗 | 扫描结论与基线快照、看门狗日志 | 退出码 0 与 4 与 5;族表外目录不猜 |
+| FR-33 | portal_build.py;shell 与模板装配 | 门户单文件页 | 拆装逐字节相同;行尾 LF 自检 |
+| FR-34 | configs/portal_pages.yaml;pages_audit.py | 归口表与审计结论 | 检查 21 项、不一致 0;陈旧报 7 |
+| FR-35 | retrieval.build;windcms 知识库构建 | 检索索引件 | 无模型时纯词法可用;索引缺失如实说明 |
+| FR-36 | place_raw_data.py;raw_data_check.py | 落位结果与体检结论 | 冲突默认拒绝;同尺寸跳过 |
+| FR-37 | scan_stations.py;config 场站辨识 | 辨识结论与依据 | 四条规则逐条打印;多目录不匹配报未识别 |
+| FR-38 | scada_source 取数层;raw_scan 族表 | 合并取数结果与扫描结论 | 主件与补充件合并行数与重叠数打印;类目库不计变化 |
+| FR-39 | install.bat 与 install.ps1 与 install.sh;便携运行时 | 虚拟环境、配置、安装记录 | 离线安装退出码 0;入口引用闭合 |
+| FR-40 | service_ctl.py;win_service.py;service_main.py;service_worker.py | 托管进程与自启项 | 巡检 15 秒;无权限返回 3 并给建议 |
+| 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 编写依据
+
+表 A-1 编写依据(章节或事实与来源文件)
+| 章节或事实 | 来源文件(仓库相对路径) |
+|---|---|
+| 版本号真源、版本规则、12 条版本史、包名 | src/version.py |
+| 系统是什么、产物全景、路径约定、进程口径、页面取数 | docs/系统设计说明.md 第 1 至第 6 章 |
+| 缺口与边界(未随包脚本、缺件、源件缺失) | docs/系统设计说明.md 第 7 章 |
+| 变更记录(按日期逐条事故与处置) | docs/系统设计说明.md 第 8 章 |
+| 交付包组成与入口引用闭合 | docs/系统设计说明.md 第 9 章与 §9.1 |
+| 页面归口规则、五类内容类型、门户逐页识别、全部网页总清点 | docs/系统设计说明.md 第 10 章与第 12 章 |
+| 配置与日志统一口径、无产物时的可用性 | docs/系统设计说明.md 第 11 章 |
+| 输入到功能到算法到产物、端到端顺序、反向呼应审计、可逆性 | docs/系统设计说明.md 第 13 章与 §13.6 |
+| 服务化安装与卸载、安装时版本检查 | docs/系统设计说明.md 第 14 章 |
+| 版本管理与打包命名、单一真源与三处守卫 | docs/系统设计说明.md 第 15 章 |
+| 时间窗词表、随窗与不随窗、窄仓、等待契约 | docs/系统设计说明.md 第 16 章 |
+| 三条描述口径与服务端链接契约、依据不得为空 | docs/系统设计说明.md 第 17 章 |
+| 面向用户的功能与使用、系统要求、模型档、目录结构、常见故障、已知限制 | docs/说明书_观澜…v0.2.md |
+| 页面与子页的归口登记(类型、依据、已知缺口) | configs/portal_pages.yaml |
+| 服务端口与监听地址真源、看门狗开关 | configs/serve.json |
+| 模型档位与拉取命令 | configs/models.json |
+| 自检门清单与退出码、服务定义 | guanlan.py |
+| 重算链步骤、容忍口径、自动取消跳过 | scripts/rebuild_all.py |
+| 归口审计规则与退出码语义 | scripts/pages_audit.py |
+| 输入数据放置规则、增量语义、放完跑什么 | docs/输入数据放置指导_v0.1.md |
+| 落位约定、场站辨识四条规则、各目录功用、能重建与不能重建 | docs/数据目录结构与落位约定_v0.2.md |
+| 手工重算步骤、运维控制台三条纪律、二次启动原因 | docs/重算操作手册_v0.1.md |
+| 页面输出体检的判定口径与逐页原因 | docs/页面输出体检_v0.1.md 与 docs/页面输出体检_逐页原因_v0.1.md |
+| 打包与安装与卸载与版本检查的用户视角说明 | README_先读我.MD |
+| 自检实跑输出(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.2.md |
+| 场无关引擎与场相关参数的分层与换场作业单 | docs/src/设计说明_观澜_2.10.2.md |
+
+表中 `<场>`、`<场站>`、`<机型>` 为占位符,按实际风电场替换。
+
+## 附录 B 术语表
+
+表 B-1 术语表(按主题分组)
+
+| 术语 | 英文或写法 | 含义 | 出处 |
+|---|---|---|---|
+| 时间窗 | — | 一个由用户选择或自定义的数据观察区间;月度件按月取整、日粒度件按日精确 | docs/系统设计说明.md §16 |
+| 预设时间窗 | — | 词表内置的时间窗:近30日、近90日、2026年、2026H1、2025H2、全程 | docs/系统设计说明.md §16.1 |
+| 自定义起止日期时间窗 | — | 写法为起日期~止日期的时间窗,含两端 | docs/系统设计说明.md §16.1 |
+| 判级时间窗 | — | 判级矩阵与四轴重算所用的时间窗 | docs/系统设计说明.md §17.1 |
+| 证据时间窗 | — | 支撑某条结论的证据所在的时间窗,随判定一并给出 | docs/系统设计说明.md §17.1 |
+| 天气窗 | — | 领域词,指气象条件适合的时段,不属于时间窗口径的统一范围 | docs/系统设计说明.md §17.1 |
+| 作业窗 | — | 领域词,指检修作业可实施的时段,保持原样 | docs/系统设计说明.md §17.1 |
+| 平均无故障间隔(MTBF) | MTBF | 相邻两次故障之间的平均运行时长,按所选时间窗内的停机事件与报警计算 | docs/系统设计说明.md §16.2 与 §17.1 |
+| 平均停机间隔(MTBO) | MTBO | 相邻两次停机之间的平均间隔,与可靠性表同源 | docs/系统设计说明.md §17.1 |
+| 单次停机时长(MDT) | MDT | 每次停机的平均持续时长 | docs/系统设计说明.md §17.1 |
+| 窄仓 | slim10min | 由十分钟数据抽出的公共列子集(不裁剪行),作为按时间窗重算的秒级底座 | docs/系统设计说明.md §16.3 |
+| 等待契约 | pending | 按时间窗重算未命中缓存时先回等待或构建中,由页面轮询,绝不静默用旧口径 | docs/系统设计说明.md §16.4 |
+| 判级四轴 | — | 变桨、偏航、蓄能、温度四个随所选时间窗重算的判级方向 | docs/系统设计说明.md §16.2 |
+| 融合面 | — | 把模型过闸线与振动窗索引裁决合到一起的判级面,输出融合级 | docs/系统设计说明.md §13.1 |
+| 链盘 | — | 运行时的决策链进度集合,每台一个对象记六步状态、卡点与下一步 | docs/系统设计说明.md §13.1 |
+| 产物 | outputs | 由原始件算出来、页面只读的输出数据,统一落在产物仓 | docs/系统设计说明.md §3 与 §13.4 |
+| 逐件来源台账 | — | 记录每件产物属于原始件重算还是随包补齐的台账 | docs/系统设计说明.md §2.1 |
+| 反向呼应审计 | — | 从输出回溯到生成端与输入的审计,检查每件产物有没有来路 | docs/系统设计说明.md §13.6 |
+| 页面归口 | — | 判定每一页是否随输入数据变、是否算产物,并逐条写明依据 | configs/portal_pages.yaml |
+| 陈旧检测 | — | 对数据派生页面比对内嵌指纹与当前产物,不一致即报页面陈旧 | scripts/pages_audit.py |
+| 数据派生 | data-derived | 五类页面内容类型之一:把产物烘进页面,属于产物,必须登记来源并做陈旧检测 | configs/portal_pages.yaml 头部 |
+| 受管静态 | static | 受管外壳正文,不随输入数据变,也不得引用产物 | configs/portal_pages.yaml 头部 |
+| 实时取数 | live | 页面本体不存快照,永远与产物一致 | configs/portal_pages.yaml 头部 |
+| 产物引用脚注 | data-citing | 静态正文加产物引用,正文不变但被引产物必须在位 | configs/portal_pages.yaml 头部 |
+| 冻结交付件 | frozen-delivery | 带版本号与日期的客户交付件,按交付版本冻结,不随数据自动变 | configs/portal_pages.yaml 头部 |
+| 开箱验证 | — | 打包后在临时副本上真解压真安装真起服务的核验路径 | docs/系统设计说明.md §9 |
+| 容忍口径 | 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.2.md |

BIN
docs/数据要求说明_观澜_2.10.1.docx


BIN
docs/数据要求说明_观澜_2.10.2.docx


Diferenças do arquivo suprimidas por serem muito extensas
+ 3 - 3
docs/版本记录.md


BIN
docs/设计说明_观澜_2.10.1.docx → docs/设计说明_观澜_2.10.2.docx


BIN
docs/需求分析_观澜_2.10.1.docx → docs/需求分析_观澜_2.10.2.docx


+ 3 - 0
guanlan.py

@@ -271,6 +271,9 @@ def cmd_check(c):
                 _mdtxt = _src.read_text(encoding='utf-8')
                 for _w, _n in _ddb.forbidden_hits(_mdtxt).items():      # ★对外件去标识化硬门
                     _leak[_w] = _leak.get(_w, 0) + _n
+                if _spec.get('strict_terms'):                            # ★数据要求说明: 去实现细节/去现状
+                    for _w, _n in _ddb.strict_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)

+ 37 - 8
scripts/delivery_docs_build.py

@@ -70,9 +70,23 @@ DOCS = {
                 title='设计说明', subtitle='观澜 v2 · 风电场智能分析系统'),
     'dat': dict(src='docs/src/数据要求说明_观澜_{v}.md', out='docs/数据要求说明_观澜_{v}.docx',
                 title='数据要求说明',
-                subtitle='观澜 v2 · 风电场智能分析系统(结合现场《数据分析收资要求-v3》)'),
+                subtitle='观澜 v2 · 风电场智能分析系统(结合现场《数据分析收资要求-v3》)',
+                # ★2026-09-22 用户令: 这份是**纯数据需求规格** —— 不体现样本场接入情况, 也不体现实现细节。
+                #   下面这组模式由 `--check`/`--verify` 机器扫描, 命中即 FAIL(不靠人自觉)。
+                strict_terms=True),
 }
 
+# 数据要求说明的"不得出现"模式(用户令 2026-09-22):
+#   英文测点名称 / 文件名称 / 落盘位置 / 机组名称编号 / 样本场现状字样。
+#   中文业务含义名称与「中文(英文简写)」(如 平均无故障间隔(MTBF))不在禁止之列。
+STRICT_PATTERNS = (
+    ('英文测点标识(snake_case)', r'\b[a-z][a-z0-9]*_[a-z0-9_]+\b'),
+    ('文件扩展名', r'\.(csv|parquet|json|yaml|yml|xlsx|xls|pdf|mdb|xml|docx|doc)\b'),
+    ('目录/落盘位置', r'(data/raw|outputs/|configs/|scripts/|reference/|docs/|data\\)'),
+    ('机组名称/编号', r'(WTG\d+|\b\d{2}[A-Z]\b)'),
+    ('样本场现状字样', r'(已在位|未到位|现状|实测|样本场|本场|催缴)'),
+)
+
 # ★去标识化词表(用户令 2026-09-22「内容参考如东风电场,但不体现如东风电场」):
 #   三份文档是**对外**交付件, 不得出现样本场的中文名、罗马化形态、业主、地域、OEM 与第三方机构名。
 #   词表放在代码里 ⇒ `--check`/`--verify` 与 `guanlan.py check` 都会机器扫描, 不靠人自觉。
@@ -373,6 +387,12 @@ def forbidden_hits(text: str) -> dict:
     return {w: low.count(w.lower()) for w in FORBIDDEN if low.count(w.lower())}
 
 
+def strict_hits(text: str) -> dict:
+    """数据要求说明的"不得出现实现细节/现状"扫描 → {模式名: 命中次数}(只回报非零项)。"""
+    return {name: len(re.findall(rx, text, re.I)) for name, rx in STRICT_PATTERNS
+            if re.search(rx, text, re.I)}
+
+
 def count_md(text: str) -> dict:
     blocks = parse_md(text)
     body = re.sub(r'!\[[^\]]*\]\([^)]*\)', '', text)
@@ -397,7 +417,9 @@ def cover(doc, title: str, subtitle: str):
         para(doc, '', space_after=0)
     para(doc, f'版本 {VERSION}(与系统版本一致)', size=14, align='center', space_after=6)
     para(doc, f'编制:观澜项目组  日期:2026-09-22', size=12, align='center', space_after=6)
-    para(doc, '本文件由 scripts/delivery_docs_build.py 依 docs/src 下的 markdown 源件生成(可重跑、可 diff)',
+    # ★落款不写生成器文件名与源件路径:那会把实现细节带进"对外、不体现文件名/落盘位置"的文档里
+    #   (2026-09-22 实逮: `--check` 扫源件是 clean, 但 `--verify` 扫渲染结果命中 1 处 snake_case + 2 处路径)。
+    para(doc, '本文件由统一的交付文档生成器排版(样式表固定,可重跑、可 diff;正文源件随交付包提供)',
          size=9, align='center', space_after=0)
     doc.add_page_break()
 
@@ -567,6 +589,7 @@ def verify_docx(key: str) -> int:
     # 只把"正文里残留的 markdown 标记"判为不合格;代码内的字面星号是内容本身(glob 通配),只报数。
     bad = [k for k, v in residual.items() if v and k != '代码内字面星号']
     leak = forbidden_hits(allt + '\n' + head)          # ★对外件不得出现样本场标识
+    strict = strict_hits(allt) if spec.get('strict_terms') else {}   # ★数据要求说明: 去实现细节/去现状
     # ★表号断链检查(2026-09-22 实逮): 正文写「见表 9-2」,文档里就必须真有「表 9-2」。
     cap_labels = set()
     for p in doc.paragraphs:
@@ -595,11 +618,13 @@ def verify_docx(key: str) -> int:
     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 · 去标识化: %s · 表号: %s'
+    print('      域(目录/页码): %s · 残留标记: %s · 去标识化: %s%s · 表号: %s'
           % ('OK' if ok_field else '缺', bad or '无',
              'clean' if not leak else '泄漏 %s' % leak,
+             '' if not spec.get('strict_terms') else
+             ' · 去实现细节: %s' % ('clean' if not strict else '命中 %s' % strict),
              '断链 %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
+    return 0 if ok_field and not bad and not leak and not broken and not strict else 5
 
 
 def main() -> int:
@@ -626,16 +651,20 @@ def main() -> int:
                 rc = 5
                 continue
             st = count_md(src.read_text(encoding='utf-8'))
-            miss, leak = [], forbidden_hits(src.read_text(encoding='utf-8'))
-            for _cap, rel in re.findall(r'!\[([^\]]*)\]\(([^)]+)\)', src.read_text(encoding='utf-8')):
+            _src_txt = src.read_text(encoding='utf-8')
+            miss, leak = [], forbidden_hits(_src_txt)
+            strict = strict_hits(_src_txt) if spec.get('strict_terms') else {}
+            for _cap, rel in re.findall(r'!\[([^\]]*)\]\(([^)]+)\)', _src_txt):
                 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%s'
+            print('%-4s 章 %2d · 节 %2d · 小节 %2d · 表 %2d · 图 %2d · 约 %5d 汉字 · 去标识化 %s%s%s'
                   % (k, st['章'], st['节'], st['小节'], st['表'], st['图'], st['汉字'],
                      'clean' if not leak else '泄漏 %s' % leak,
+                     '' if not spec.get('strict_terms') else
+                     ' · 去实现细节 %s' % ('clean' if not strict else '命中 %s' % strict),
                      '' if not miss else '  ✗ 缺图 %d: %s' % (len(miss), ', '.join(miss))))
-            if miss or leak:
+            if miss or leak or strict:
                 rc = 5
         return rc
     for k in ([a.only] if a.only else sorted(DOCS)):

+ 519 - 692
scripts/delivery_docs_figures.py

@@ -1,692 +1,519 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-r"""交付文档图表构建器(需求分析 / 设计说明 / 数据要求说明 三份 docx 的插图)。
-
-## 为什么单开一个器
-三份交付文档要求"文表图并茂"。图里出现的每个数字都必须能追到仓库里的真件,所以图**不手画**:
-本器从 `src/version.py` 的 HISTORY、`configs/portal_pages.yaml`、`configs/serve.json`、
-`data/raw/<场>/**`、`outputs/<场>/**` 与 `guanlan.py check` / `rebuild_all.py --dry-run` 的**实跑输出**
-里取数,落到 `docs/figures/*.png`。纯示意图(架构/链路/时序)也在图注里写明"依据"。
-
-## 落点与口径
-    docs/figures/fig-req-01..04 / fig-des-01..06 / fig-dat-01..04 .png
-    docs/src/_guanlan_check.txt        (验收门实跑原文,图的取证底稿)
-    docs/src/_rebuild_plan.txt         (重算链 dry-run 原文,图的取证底稿)
-
-## 用法
-    python scripts/delivery_docs_figures.py            # 全画
-    python scripts/delivery_docs_figures.py --only req # 只画某一组 (req/des/dat)
-    python scripts/delivery_docs_figures.py --check    # 只报缺哪些图
-
-退出码: 0 全部生成 · 5 有图没生成(源件缺或绘图失败,逐条打印原因)
-"""
-from __future__ import annotations
-
-import argparse
-import json
-import pathlib
-import re
-import subprocess
-import sys
-import time
-
-ROOT = pathlib.Path(__file__).resolve().parents[1]
-sys.path.insert(0, str(ROOT))
-
-import matplotlib                                                          # noqa: E402
-matplotlib.use('Agg')
-import matplotlib.pyplot as plt                                            # noqa: E402
-from matplotlib.patches import FancyArrowPatch, FancyBboxPatch             # noqa: E402
-
-from src import paths as P                                                 # noqa: E402
-
-FIG_DIR = ROOT / 'docs' / 'figures'
-SRC_DIR = ROOT / 'docs' / 'src'
-CJK = 'Microsoft YaHei'
-plt.rcParams['font.sans-serif'] = [CJK, 'SimHei']
-plt.rcParams['axes.unicode_minus'] = False
-plt.rcParams['figure.dpi'] = 200
-
-WARN: list[str] = []
-VERSION = __import__('importlib').import_module('src.version').VERSION
-
-
-# ── 通用绘图元件 ────────────────────────────────────────────────────────────────
-def canvas(w: float, h: float):
-    fig, ax = plt.subplots(figsize=(w, h))
-    ax.set_xlim(0, 100)
-    ax.set_ylim(0, 100)
-    ax.axis('off')
-    return fig, ax
-
-
-def box(ax, x, y, w, h, text, fc='#eef3fb', ec='#3b6ea5', fs=10, bold=False, align='center'):
-    ax.add_patch(FancyBboxPatch((x, y), w, h, boxstyle='round,pad=0.6,rounding_size=1.4',
-                                linewidth=1.2, edgecolor=ec, facecolor=fc))
-    ax.text(x + w / 2, y + h / 2, text, ha='center', va='center', fontsize=fs,
-            wrap=True, fontweight='bold' if bold else 'normal', linespacing=1.5)
-
-
-def arrow(ax, p, q, color='#555555', style='-|>', lw=1.3, rad=0.0):
-    ax.add_patch(FancyArrowPatch(p, q, arrowstyle=style, mutation_scale=12,
-                                 linewidth=lw, color=color,
-                                 connectionstyle=f'arc3,rad={rad}'))
-
-
-def save(fig, name: str):
-    """落盘前统一做一次**字形兜底**:把 YaHei 缺字的符号换成等价可显字符。
-
-    实逮(2026-09-22): 图里引用的原文(如 `configs/portal_pages.yaml` 的说明)含 `⇒`(U+21D2),
-    matplotlib 报 `Glyph 8658 missing from font(s) Microsoft YaHei` 并画成方框 —— 交付文档里不能有方框。
-    """
-    swap = {'⇒': '→', '⇐': '←', '↔': '↔', '⇔': '↔', '✓': '√', '✗': '×', '≥': '≥'}
-    import matplotlib.text as _mt
-    for t in fig.findobj(_mt.Text):
-        s = t.get_text()
-        if any(k in s for k in swap):
-            for k, v in swap.items():
-                s = s.replace(k, v)
-            t.set_text(s)
-    FIG_DIR.mkdir(parents=True, exist_ok=True)
-    out = FIG_DIR / name
-    fig.tight_layout()
-    fig.savefig(out, bbox_inches='tight', facecolor='white')
-    plt.close(fig)
-    print('  ✓', out.relative_to(ROOT).as_posix(), '%.0f KB' % (out.stat().st_size / 1024))
-
-
-def barh(ax, labels, values, color='#3b6ea5', fmt='{:.0f}', xlabel=''):
-    y = range(len(labels))
-    ax.barh(list(y), values, color=color, height=0.62)
-    ax.set_yticks(list(y))
-    ax.set_yticklabels(labels, fontsize=9)
-    ax.invert_yaxis()
-    for i, v in enumerate(values):
-        ax.text(v, i, ' ' + fmt.format(v), va='center', fontsize=8.5)
-    ax.set_xlabel(xlabel, fontsize=9)
-    ax.tick_params(axis='x', labelsize=8.5)
-    for s in ('top', 'right'):
-        ax.spines[s].set_visible(False)
-
-
-# ── 取数(全部真读,不猜)────────────────────────────────────────────────────────
-def version_history():
-    from src import version as V
-    return list(V.HISTORY), V.VERSION
-
-
-def portal_pages():
-    import yaml
-    d = yaml.safe_load((P.config('portal_pages.yaml')).read_text(encoding='utf-8'))
-    pages = d.get('pages') or d.get('entries') or []
-    if isinstance(d, dict) and not pages:
-        for k, v in d.items():
-            if isinstance(v, list) and v and isinstance(v[0], dict):
-                pages = v
-                break
-    return pages
-
-
-def serve_cfg():
-    return json.loads((P.config('serve.json')).read_text(encoding='utf-8'))
-
-
-def raw_station_stats():
-    """→ [(族, 件数, GB)],实测 data/raw/<场站>/ 下一层目录。
-
-    场站目录名以**场配置**为准(`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 P.farm())
-    out = []
-    if not home.exists():
-        WARN.append(f'data/raw 场站目录不在位: {home}')
-        return out
-    for d in sorted(p for p in home.iterdir() if p.is_dir()):
-        fs = [p for p in d.rglob('*') if p.is_file()]
-        out.append((d.name, len(fs), sum(p.stat().st_size for p in fs) / 1e9))
-    return out
-
-
-def md_tables(path: pathlib.Path):
-    """→ [ [ [cell,...], ... ] ]:把 markdown 里的表逐个解析成二维列表(不含表头分隔行)。"""
-    if not path.exists():
-        WARN.append('源件不在位: %s' % path.relative_to(ROOT).as_posix())
-        return []
-    lines = path.read_text(encoding='utf-8').splitlines()
-    tables, i = [], 0
-    while i < len(lines):
-        if lines[i].startswith('|') and i + 1 < len(lines) and re.match(r'^\|[\s:\-|]+\|$', lines[i + 1].strip()):
-            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('|')])
-                j += 1
-            tables.append(rows)
-            i = j
-        else:
-            i += 1
-    return tables
-
-
-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('|')])
-            j += 1
-        if not rows:
-            continue
-        if first_cell is not None and rows[0][0] != first_cell:
-            continue
-        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):
-    """把两张取证底稿跑出来(已存在且未 --refresh 时复用)。"""
-    SRC_DIR.mkdir(parents=True, exist_ok=True)
-    py = str(ROOT / '.venv' / 'Scripts' / 'python.exe')
-    chk, plan = SRC_DIR / '_guanlan_check.txt', SRC_DIR / '_rebuild_plan.txt'
-    if refresh or not chk.exists():
-        t0 = time.time()
-        r = subprocess.run([py, '-X', 'utf8', 'guanlan.py', 'check'], cwd=str(ROOT),
-                           capture_output=True, timeout=1800)
-        chk.write_text((r.stdout or b'').decode('utf-8', 'replace'), encoding='utf-8')
-        print('  · guanlan.py check 底稿写入 (%.0fs, rc=%d)' % (time.time() - t0, r.returncode))
-    if refresh or not plan.exists():
-        r = subprocess.run([py, '-X', 'utf8', 'scripts/rebuild_all.py', '--dry-run', '--skip-scada'],
-                           cwd=str(ROOT), capture_output=True, timeout=600)
-        plan.write_text((r.stdout or b'').decode('utf-8', 'replace'), encoding='utf-8')
-        print('  · rebuild_all --dry-run 底稿写入')
-
-
-# ── 需求分析组 ─────────────────────────────────────────────────────────────────
-def fig_req_01():
-    hist, _ver = version_history()
-    fig, ax = plt.subplots(figsize=(9.2, 0.62 * len(hist) + 1.4))
-    ax.axis('off')
-    n = len(hist)
-    ax.set_ylim(-0.6, n - 0.4)
-    ax.set_xlim(0, 10)
-    ax.plot([0.7, 0.7], [0, n - 1], color='#3b6ea5', lw=2.2, zorder=1)
-    lvl_cn = {'major': '大', 'minor': '中', 'patch': '小', 'legacy': '旧编号'}
-    for i, h in enumerate(hist):
-        y = n - 1 - i
-        ax.scatter([0.7], [y], s=90, color='#3b6ea5', zorder=3)
-        title = h.get('title', '')
-        title = re.sub(r'(.*?)', '', title)
-        title = title[:46] + ('…' if len(title) > 46 else '')
-        ax.text(1.05, y + 0.18, f"v{h.get('version')}  {h.get('date')}  [{lvl_cn.get(h.get('level'), '')}版本]",
-                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 需求演进时间轴(样本场;依据 src/version.py 的 HISTORY,共 {n} 条)', fontsize=11)
-    save(fig, 'fig-req-01-需求演进时间轴.png')
-
-
-def fig_req_02():
-    fig, ax = canvas(9.4, 6.2)
-    roles = [('值班运行', 88, '今日态势/部件问题/该问题页/报警与故障统计'),
-             ('点检与检修', 65, '机组页/振动与融合面/油样与工单/报告导出'),
-             ('场站管理', 42, '发电性能/可靠性(MTBF·MTBO·MDT)/可用率/限电'),
-             ('研发与交付', 19, '本体与机制链/数据重算/运行体检/版本与打包')]
-    for name, y, pages in roles:
-        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, '角色 — 场景 — 页面关系(依据 系统使用说明书 与 configs/portal_pages.yaml)',
-            ha='center', fontsize=11, fontweight='bold')
-    save(fig, 'fig-req-02-角色场景页面.png')
-
-
-def fig_req_03():
-    pages = portal_pages()
-    if not pages:
-        WARN.append('portal_pages.yaml 未解析出页面条目')
-        return
-    from collections import Counter
-    kinds = Counter(str(p.get('kind', '未标')) for p in pages)
-    order = ['data-derived', 'source-derived', 'frozen-delivery', 'static', 'shipped']
-    ks = [k for k in order if k in kinds] + [k for k in kinds if k not in order]
-    vals = [kinds[k] for k in ks]
-    fig, ax = plt.subplots(figsize=(8.6, 0.62 * len(ks) + 2.0))
-    cn = {'data-derived': 'data-derived(随数据变,须重算)',
-          'source-derived': 'source-derived(随源代码变)',
-          'frozen-delivery': 'frozen-delivery(冻结交付件)',
-          'static': 'static(静态)', 'shipped': 'shipped(随包件)'}
-    barh(ax, [cn.get(k, k) for k in ks], vals, xlabel='页面/子页数(共 %d 项)' % len(pages))
-    ax.set_title('门户页面归口分布(依据 configs/portal_pages.yaml)', fontsize=11)
-    save(fig, 'fig-req-03-页面归口分布.png')
-
-
-def fig_req_04():
-    """验收门分类 —— **与《需求分析》§9.1 表同源**(直接解析那张表,避免图文数字打架)。"""
-    head, rows = md_find_table(SRC_DIR / f'需求分析_观澜_{VERSION}.md', ('分类', '判定条数'))
-    if not rows:
-        WARN.append('需求分析 §9.1 的分类表未解析到,图 fig-req-04 用实跑输出兜底')
-        ensure_evidence()
-        txt = (SRC_DIR / '_guanlan_check.txt').read_text(encoding='utf-8', errors='replace')
-        marks = re.findall(r'^\s*\[(OK|FAIL|--|!|i|\?)\]\s*([^—\n]+)', txt, re.M)
-        cats = {'通过': sum(1 for m, _ in marks if m == 'OK'),
-                '需处置': sum(1 for m, _ in marks if m == 'FAIL'),
-                '跳过或提示': sum(1 for m, _ in marks if m not in ('OK', 'FAIL'))}
-        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[c_name] == '合计':
-                break
-            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)
-    save(fig, 'fig-req-04-验收门分类.png')
-
-
-# ── 设计说明组 ─────────────────────────────────────────────────────────────────
-def fig_des_01():
-    fig, ax = canvas(9.6, 7.4)
-    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)
-    box(ax, 24, 71, 52, 9, '接入层:组件网关 28084(前缀改写 + data-abs 豁免 + 静态件)',
-        fc='#eef3fb', ec='#3b6ea5', fs=10)
-    box(ax, 3, 56, 18, 11, '分析组件\n18033 /detail', fs=9)
-    box(ax, 23, 56, 17, 11, '振动组件\n18020 /cms', fs=9)
-    box(ax, 42, 56, 16, 11, '仿真台\n18791·18792', fs=9)
-    box(ax, 60, 56, 15, 11, '三维 viewer\n64292', fs=9)
-    box(ax, 77, 56, 20, 11, '本机模型档\nOllama 11434\n(可选,未装即降级)', fs=9,
-        fc='#f4eef6', ec='#7d5ba6')
-    for x in (12, 31.5, 50, 67.5, 87):
-        arrow(ax, (50, 71), (x, 67.4), color='#8a8a8a')
-    box(ax, 3, 41, 94, 10, '产物层 outputs/<场>/:windscada 判级与曲线 · slim10min 窄仓 · 振动六层链 · '
-                           '本体对象库 · SOP/findings · guanlan 契约', fc='#e8f1e4', ec='#5b8c4a', fs=9.5)
-    arrow(ax, (50, 56), (50, 51.4))
-    box(ax, 3, 27, 94, 9, '摄入与构建层:rebuild_all.py 23 步(place_raw_data → 10min/1min 摄入 → '
-                          '10 个 SCADA 构建器 → 振动摄入 → 本体六步 → 审计)', fc='#e8f1e4', ec='#5b8c4a', fs=9.5)
-    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, '共享技术资料(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))
-    box(ax, 3, 2, 94, 7, '贯穿全链的守卫:单一真源(src/paths.py·src/version.py) · 审计门(guanlan.py check / '
-                         '7 个审计器) · 逐件来源台账(_provenance.json)', fc='#ffffff', ec='#555555', fs=9)
-    save(fig, 'fig-des-01-总体架构.png')
-
-
-def fig_des_02():
-    """serve.json 是**扁平**结构(`gateway`/`detail`/… 直接是端口整数),不是嵌套 dict。"""
-    cfg = serve_cfg()
-    host, pub = cfg.get('host', '?'), cfg.get('public_host', '')
-    gw_port = cfg.get('gateway', '?')
-    comps = [(k, cfg.get(k)) for k in ('detail', 'cms', 'sim', 'sim_sys', 'viewer')]
-    fig, ax = canvas(9.6, 5.2)
-    box(ax, 30, 82, 40, 12, '网关 gateway\n%s:%s(对外唯一入口)' % (pub or host, gw_port),
-        fc='#eef3fb', ec='#3b6ea5', fs=10, bold=True)
-    n = max(1, len(comps))
-    for i, (name, port) in enumerate(comps):
-        x = 3 + i * (94 / n)
-        w = 94 / n - 3
-        box(ax, x, 52, w, 14, '%s\n%s:%s' % (name, host, port), fs=9)
-        arrow(ax, (50, 82), (x + w / 2, 66.6), color='#8a8a8a')
-    box(ax, 3, 30, 94, 12, '绑定与自启:guanlan 服务(Windows 服务 / systemd)· start.bat 静默启动 · '
-                           'run/*.pid 运行态 · logs/ 统一落点', fc='#f6f2e8', ec='#b08a3e', fs=9)
-    arrow(ax, (50, 52), (50, 42.6))
-    box(ax, 3, 12, 94, 12, '端口冲突处置:netstat -ano 找占用进程 → 按端口清理 → 重启服务\n'
-                           '(service_ctl stop 不杀子进程,只按端口杀会留下孤儿实例)', fc='#ffffff',
-        ec='#555555', fs=9)
-    arrow(ax, (50, 30), (50, 24.6))
-    ax.text(50, 96, '端口与进程拓扑(依据 configs/serve.json 实读)', ha='center', fontsize=11.5,
-            fontweight='bold')
-    save(fig, 'fig-des-02-端口与进程.png')
-
-
-def fig_des_03():
-    ensure_evidence()
-    txt = (SRC_DIR / '_rebuild_plan.txt').read_text(encoding='utf-8', errors='replace')
-    steps = []
-    for line in txt.splitlines():
-        m = re.match(r'^\s{2}(\S.*?)\s{2,}(\S.*)$', line)
-        if m and not line.strip().startswith('('):
-            steps.append((m.group(1).strip(), m.group(2).strip()))
-    if not steps:
-        WARN.append('rebuild_all --dry-run 未解析出步骤')
-        return
-    fig, ax = plt.subplots(figsize=(9.6, 0.42 * len(steps) + 1.8))
-    ax.axis('off')
-    ax.set_xlim(0, 10)
-    ax.set_ylim(-0.5, len(steps) - 0.2)
-    for i, (name, cmd) in enumerate(steps):
-        y = len(steps) - 1 - i
-        ax.add_patch(plt.Rectangle((0.2, y - 0.34), 1.75, 0.68, facecolor='#eef3fb',
-                                   edgecolor='#3b6ea5', lw=0.9))
-        ax.text(1.07, y, name[:22], fontsize=8.6, ha='center', va='center')
-        ax.text(2.15, y, cmd[:92], fontsize=8.2, va='center', color='#333333', family='monospace')
-        if i < len(steps) - 1:
-            ax.annotate('', xy=(1.07, y - 0.42), xytext=(1.07, y - 0.30),
-                        arrowprops=dict(arrowstyle='-|>', color='#8a8a8a', lw=0.9))
-    ax.set_title('重算链步骤(依据 scripts/rebuild_all.py --dry-run --skip-scada,共 %d 步)' % len(steps),
-                 fontsize=11)
-    save(fig, 'fig-des-03-重算链.png')
-
-
-def fig_des_04():
-    fig, ax = canvas(9.6, 6.4)
-    ax.text(50, 96, '判级矩阵与四源融合数据流(每条判据都能指到输入列)', ha='center',
-            fontsize=11.5, fontweight='bold')
-    srcs = ['SCADA 10min\n(slim10min 48 列)', 'CMS 振动\n(六层链)', '状态·故障·告警\n(台账)', '油样与工单\n(现场记录)']
-    for i, s in enumerate(srcs):
-        box(ax, 1.5 + i * 24.6, 78, 22, 12, s, fc='#f6f2e8', ec='#b08a3e', fs=9)
-    box(ax, 6, 58, 40, 12, '七系统判级矩阵\n变桨·偏航·主轴承·齿轮箱·发电机·变流器·主控与传感网',
-        fc='#eef3fb', ec='#3b6ea5', fs=9.5)
-    box(ax, 52, 58, 42, 12, '融合面四源对齐\n振动 × SCADA × 台账 × 人工\n(逐台判级 + 证据链)',
-        fc='#eef3fb', ec='#3b6ea5', fs=9.5)
-    for x in (12, 36, 62, 86):
-        arrow(ax, (x, 78), (26 if x < 50 else 73, 70.6), color='#8a8a8a')
-    box(ax, 6, 40, 88, 11, '时间窗:判级与曲线按所选时间窗重算(月/年/自定义起止日期,含两端);'
-                           '温度与停机台账按真窗取月', fc='#e8f1e4', ec='#5b8c4a', fs=9.5)
-    arrow(ax, (26, 58), (26, 51.6))
-    arrow(ax, (73, 58), (73, 51.6))
-    outs = ['部件问题清单\n(按系统×机组)', '该问题页\n(单问题+完整依据)', '发电性能与可靠性\n(MTBF/MTBO/MDT)',
-            '控制参数一致性\n(P/ω 封顶·K 聚类)', '报告导出\n(docx/md/页面)', '本体机制链\n与决策台']
-    for i, o in enumerate(outs):
-        x = 2 + (i % 3) * 32.3
-        y = 24 if i < 3 else 8
-        box(ax, x, y, 30, 12, o, fc='#f4eef6', ec='#7d5ba6', fs=9)
-    arrow(ax, (50, 40), (50, 36.6), color='#5b8c4a')
-    box(ax, 2, 21.5, 96, 2.6, '', fc='#ffffff', ec='#ffffff')
-    ax.text(50, 36, '产物(outputs/<场>/…,逐件登记生成端与输入)', ha='center', fontsize=9, color='#444444')
-    save(fig, 'fig-des-04-判级与融合.png')
-
-
-def fig_des_05():
-    fig, ax = canvas(9.6, 5.6)
-    ax.text(50, 96, '时间窗按窗重算与 pending 契约(实测:冷窗首查 pending,约 30 s 后落位)',
-            ha='center', fontsize=11.5, fontweight='bold')
-    lanes = [('前端 app.js', 82), ('服务 windscada_serve.py', 60), ('按窗缓存 _WIN_CACHE', 38), ('重算线程(_HEAVY_LOCK 串行)', 16)]
-    for name, y in lanes:
-        box(ax, 2, y, 24, 11, name, fc='#eef3fb', ec='#3b6ea5', fs=9)
-        ax.plot([26.5, 97], [y + 5.5, y + 5.5], color='#d0d0d0', lw=0.9, zorder=0)
-    msgs = [(82, 39, 'GET /api/fleet?win=2026-07'),
-            (60, 82, 'win 未命中 → 起后台线程,先回上一份口径并标 pending=1'),
-            (38, 60, '缺 → 入队 sysmx/curves/m9'),
-            (16, 38, '算(变桨/偏航/蓄能/温度 + 曲线 + M9)'),
-            (38, 16, '算完写入缓存'),
-            (60, 38, '5 s 轮询再取 → 命中'),
-            (82, 60, 'pending=0,页面换成该时间窗口径')]
-    for y1, y2, text in msgs:
-        arrow(ax, (36, y1 + 5.5), (36, y2 + 5.5), color='#3b6ea5')
-        ax.text(38.5, (y1 + y2) / 2 + 5.5, text, fontsize=8.8, va='center')
-    ax.text(50, 5, '为什么这样:算得慢(每面 5~120 s),不能把页面卡住;宁可先给旧口径并**如实标注**,也不静默等待。',
-            ha='center', fontsize=9, color='#444444')
-    save(fig, 'fig-des-05-时间窗与pending.png')
-
-
-def fig_des_06():
-    fig, ax = canvas(9.0, 6.0)
-    cx, cy, r = 50, 50, 30
-    items = [('改代码/配置', '#eef3fb'), ('重算 rebuild_all 23 步', '#e8f1e4'),
-             ('审计器 7 件(反向/页面/配置/链路/版本…)', '#f6f2e8'),
-             ('guanlan.py check 全绿', '#f4eef6'), ('打包 pack_dist + 开箱验证', '#eef3fb'),
-             ('装到目标机 install.ps1', '#e8f1e4')]
-    import math
-    for i, (t, fc) in enumerate(items):
-        a = math.radians(90 - i * 360 / len(items))
-        x, y = cx + r * math.cos(a), cy + r * math.sin(a)
-        box(ax, x - 15, y - 6, 30, 12, t, fc=fc, ec='#3b6ea5', fs=9)
-        a2 = math.radians(90 - (i + 1) * 360 / len(items))
-        arrow(ax, (cx + (r - 6) * math.cos(a), cy + (r - 6) * math.sin(a)),
-              (cx + (r - 6) * math.cos(a2), cy + (r - 6) * math.sin(a2)),
-              color='#8a8a8a', rad=-0.25)
-    box(ax, 38, 44, 24, 12, '版本纪律\n单一真源 src/version.py', fc='#ffffff', ec='#555555', fs=9, bold=True)
-    ax.text(50, 96, '质量门与审计闭环(每环都能独立重跑、逐条给退出码)', ha='center', fontsize=11.5,
-            fontweight='bold')
-    save(fig, 'fig-des-06-质量门闭环.png')
-
-
-# ── 数据要求说明组 ─────────────────────────────────────────────────────────────
-def fig_dat_01():
-    stats = raw_station_stats()
-    if not stats:
-        return
-    stats = sorted(stats, key=lambda x: -x[1])
-    labels = ['%s\n(%d 件)' % (s[0], s[1]) for s in stats]
-    fig, axes = plt.subplots(1, 2, figsize=(10.6, 0.55 * len(stats) + 2.2))
-    barh(axes[0], labels, [s[1] for s in stats], xlabel='件数')
-    axes[0].set_title('件数', fontsize=10)
-    barh(axes[1], labels, [s[2] for s in stats], color='#b08a3e', fmt='{:.2f}', xlabel='体量 GB')
-    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)'
-                 % (tot_n, tot_g), fontsize=11)
-    save(fig, 'fig-dat-01-数据族体量.png')
-
-
-def fig_dat_02():
-    fig, ax = canvas(9.6, 5.8)
-    ax.text(50, 96, '数据 → 功能 → 产物 流向(只列主干,逐族对应见正文表)', ha='center',
-            fontsize=11.5, fontweight='bold')
-    left = ['scada_10min / 1min\nscada_mdb(归档)', '故障报警 / 风机故障记录', 'windcms / m5_cms_tcm',
-            '油样报告', '共享技术资料(OEM)']
-    for i, t in enumerate(left):
-        box(ax, 2, 78 - i * 15, 26, 12, t, fc='#f6f2e8', ec='#b08a3e', fs=9)
-    mid = ['判级矩阵与时间窗重算', '发电性能与可靠性', '故障与停机台账', '振动六层链与融合面',
-           '本体对象库与问答']
-    for i, t in enumerate(mid):
-        box(ax, 37, 78 - i * 15, 26, 12, t, fc='#eef3fb', ec='#3b6ea5', fs=9)
-    right = ['部件问题 / 该问题页', '可靠性表(MTBF·MTBO·MDT)', '停机台账与五态', '融合面逐台判级',
-             '机制链 / 决策台 / 报告']
-    for i, t in enumerate(right):
-        box(ax, 72, 78 - i * 15, 26, 12, t, fc='#e8f1e4', ec='#5b8c4a', fs=9)
-    for i in range(5):
-        y = 84 - i * 15
-        arrow(ax, (28.4, y), (36.4, y), color='#8a8a8a')
-        arrow(ax, (63.4, y), (71.4, y), color='#8a8a8a')
-    ax.text(15, 8, '输入层', ha='center', fontsize=10, fontweight='bold')
-    ax.text(50, 8, '功能与算法层', ha='center', fontsize=10, fontweight='bold')
-    ax.text(85, 8, '产物与页面层', ha='center', fontsize=10, fontweight='bold')
-    save(fig, 'fig-dat-02-数据到产物.png')
-
-
-# 收资要求 v3 的 21 项 → 观澜侧到位状态(**兜底表**:正常走「解析《数据要求说明》§8.1 表」这条路;
-# 这里只在源件/表结构异常时顶上,故同样按对外口径写成中性表述,不出现样本场标识)
-REQ_STATUS = [
-    ('1', '10分钟SCADA数据', '已在位', 'scada_10min(样本 38 件)+ scada_mdb 归档'),
-    ('2', '秒级SCADA高频数据', '部分', 'scada_1min 38 件在位;1 s/5 s 原生高频未见交付'),
-    ('3', '测风塔数据', '未到位', '契约 mast: FALSE —— 测风塔目录零交付(须催缴)'),
-    ('4', '功率曲线', '部分', '合同担保曲线为图片级(须描点提取)'),
-    ('5', '机型参数', '已在位', 'configs/contracts/<机型>_<场>.yaml 的 meta + 理论功率'),
-    ('6', '机位分布数据', '已在位', '场站坐标件(依契约记载)'),
-    ('7', '状态、事件、故障、告警', '已在位', '故障报警 + 风机故障记录(代码表 2,793 条)'),
-    ('8', '机舱CMS振动数据', '已在位', 'windcms 约 15.4 万 MB 级 + 厂商报告'),
-    ('9', '机组统计数据/运行数据', '已在位', 'SCADA 统计 + sum 层(DailySummary)'),
-    ('10', '保护定值及控制参数', '部分', '整定值表在位;策略文档未交付'),
-    ('11', '机组故障录波数据', '未到位', 'fastlog 仅 4 台、10 Hz 事件快照'),
-    ('12', '可研报告及微观选址', '未到位', '目录未见交付'),
-    ('13', '风电场简介', '部分', '场站元信息散见合同与技术资料'),
-    ('14', '风电场坐标、机位布置图', '已在位', '依契约记载;布置图含于技术资料'),
-    ('15', '风机技术协议及合同', '已在位', 'OEM 技术资料 + 担保曲线附件'),
-    ('16', '并网至今生产报表', '部分', '日粒度发电账 555 天 + 全寿命月账(2016-06 起)'),
-    ('17', '周围已建风电场生产报表', '未到位', '未见交付(尾流先验缺外部对照)'),
-    ('18', '运维记录', '已在位', '检修记录 2025+2026 / 油样 2025'),
-    ('19', '大部件更换记录', '部分', '出保备件台账在位;更换台账不完整'),
-    ('20', '图纸、技术文件', '已在位', 'OEM 技术资料(电气/液压/逻辑)'),
-    ('21', '设备参数', '部分', '主轴/齿轮箱/发电机型号与齿数部分在位,明细待补'),
-]
-
-
-def fig_dat_03():
-    """收资 21 项到位状态 —— **与《数据要求说明》§8.1 表同源**(解析那张表,图文不打架)。
-
-    ★2026-09-22 实逮(我自己的 bug): 原先按**写死的列号**取"状态",而该表是
-    `序号|数据类型|观澜是否必须|对应数据族/落位|状态|缺口与催缴要点` —— 第 5 列才是状态,
-    写死取第 4 列 ⇒ 21 行全部落进"未标",图照样画得出来(所以没被"生成成功"发现)。
-    现在一律**按表头名定位列**,并把"未标"当告警打出来。
-    """
-    from collections import Counter
-    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]:
-            status = r[ci_st] if ci_st is not None and ci_st < len(r) else ''
-            if '已在位' in status:
-                st = '已在位'
-            elif '部分' in status:
-                st = '部分'
-            elif '未到位' in status:
-                st = '未到位'
-            else:
-                st = '未标'
-            note = r[ci_note] if ci_note is not None and ci_note < len(r) else ''
-            qual = '(依契约记载)' if '记载' in status else ''
-            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]})
-    a = axes[0]
-    vals = [cnt.get(k, 0) for k in order]
-    a.bar(order, vals, color=[colors[k] for k in order], width=0.55)
-    for i, v in enumerate(vals):
-        a.text(i, v, ' %d 项' % v, ha='center', va='bottom', fontsize=10)
-    a.set_ylim(0, max(vals) + 2)
-    a.set_title('21 类收资项到位状态(合计 %d 项)' % len(items), fontsize=10.5)
-    a.set_ylabel('项数', fontsize=9)
-    for s in ('top', 'right'):
-        a.spines[s].set_visible(False)
-    b = axes[1]
-    b.axis('off')
-    b.set_xlim(0, 10)
-    b.set_ylim(0, len(items))
-    for i, (no, name, st, note) in enumerate(items):
-        y = len(items) - 1 - i
-        b.add_patch(plt.Rectangle((0, y - 0.4), 0.9, 0.8, facecolor='#f0f0f0', edgecolor='none'))
-        b.text(0.45, y, no, ha='center', va='center', fontsize=8)
-        b.add_patch(plt.Rectangle((0.95, y - 0.4), 0.38, 0.8, facecolor=colors.get(st, '#b8b8b8'),
-                                  edgecolor='none'))
-        b.text(1.45, y, name[:16], fontsize=8.4, va='center')
-        b.text(5.3, y, note[:36], fontsize=7.6, va='center', color='#444444')
-    b.set_title('逐项判定(状态与缺口取自《数据要求说明》§8.1 表)', fontsize=10.5)
-    fig.suptitle('收资要求 v3 的 21 类数据在观澜的到位状态', fontsize=11.5)
-    save(fig, 'fig-dat-03-收资项覆盖.png')
-
-
-def fig_dat_04():
-    import pandas as pd
-    store = P.store()      # 当前场(不硬编码场代号,避免进图题)
-    out = []
-    for fn, label in (('temp_monthly.parquet', '温度月轨迹 temp_monthly'),
-                      ('alarms.parquet', '报警台账 alarms')):
-        f = store / fn
-        if not f.exists():
-            WARN.append('缺产物 %s' % f.relative_to(ROOT).as_posix())
-            continue
-        df = pd.read_parquet(f, columns=['month'] if fn.startswith('temp') else None)
-        col = 'month' if 'month' in df.columns else df.columns[0]
-        g = df.groupby(df[col].astype(str)).size()
-        out.append((label, g))
-    if not out:
-        return
-    months = sorted({m for _, g in out for m in g.index if re.match(r'^\d{4}-\d{2}', str(m))})
-    fig, ax = plt.subplots(figsize=(11.0, 4.6))
-    w = 0.4
-    import numpy as np
-    x = np.arange(len(months))
-    for i, (label, g) in enumerate(out):
-        vals = [int(g.get(m, 0)) for m in months]
-        ax.bar(x + (i - 0.5) * w, vals, width=w, label=label)
-    ax.set_xticks(x)
-    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/<场>/windscada 的 temp_monthly 与 alarms 实读)',
-                 fontsize=11)
-    for s in ('top', 'right'):
-        ax.spines[s].set_visible(False)
-    save(fig, 'fig-dat-04-时间覆盖.png')
-
-
-GROUPS = {
-    'req': [fig_req_01, fig_req_02, fig_req_03, fig_req_04],
-    'des': [fig_des_01, fig_des_02, fig_des_03, fig_des_04, fig_des_05, fig_des_06],
-    'dat': [fig_dat_01, fig_dat_02, fig_dat_03, fig_dat_04],
-}
-EXPECTED = ['fig-req-01-需求演进时间轴.png', 'fig-req-02-角色场景页面.png',
-            'fig-req-03-页面归口分布.png', 'fig-req-04-验收门分类.png',
-            'fig-des-01-总体架构.png', 'fig-des-02-端口与进程.png', 'fig-des-03-重算链.png',
-            'fig-des-04-判级与融合.png', 'fig-des-05-时间窗与pending.png', 'fig-des-06-质量门闭环.png',
-            'fig-dat-01-数据族体量.png', 'fig-dat-02-数据到产物.png', 'fig-dat-03-收资项覆盖.png',
-            'fig-dat-04-时间覆盖.png']
-
-
-def main() -> int:
-    ap = argparse.ArgumentParser()
-    ap.add_argument('--only', default=None, choices=sorted(GROUPS))
-    ap.add_argument('--refresh', action='store_true',
-                    help='重跑取证底稿(guanlan.py check / rebuild_all --dry-run)后再画图')
-    ap.add_argument('--check', action='store_true', help='只报缺哪些图')
-    a = ap.parse_args()
-    if a.refresh:
-        for p in (SRC_DIR / '_guanlan_check.txt', SRC_DIR / '_rebuild_plan.txt'):
-            p.unlink(missing_ok=True)
-    if a.check:
-        miss = [f for f in EXPECTED if not (FIG_DIR / f).exists()]
-        print('图表体检: %d/%d 在位' % (len(EXPECTED) - len(miss), len(EXPECTED)))
-        for f in miss:
-            print('  ✗', f)
-        return 0 if not miss else 5
-    groups = [a.only] if a.only else sorted(GROUPS)
-    for g in groups:
-        print('── %s 组 ──' % g)
-        for fn in GROUPS[g]:
-            try:
-                fn()
-            except Exception as e:  # noqa: BLE001
-                WARN.append('%s 失败: %s' % (fn.__name__, e))
-                print('  ✗', fn.__name__, e)
-    miss = [f for f in EXPECTED if not (FIG_DIR / f).exists()]
-    if WARN:
-        print('\n告警/缺件:')
-        for w in WARN:
-            print(' -', w)
-    print('\n图表: %d/%d 在位' % (len(EXPECTED) - len(miss), len(EXPECTED)))
-    return 0 if not miss else 5
-
-
-if __name__ == '__main__':
-    raise SystemExit(main())
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+r"""交付文档图表构建器(需求分析 / 设计说明 / 数据要求说明 三份 docx 的插图)。
+
+## 为什么单开一个器
+三份交付文档要求"文表图并茂"。图里出现的每个数字都必须能追到仓库里的真件,所以图**不手画**:
+本器从 `src/version.py` 的 HISTORY、`configs/portal_pages.yaml`、`configs/serve.json`、
+`data/raw/<场>/**`、`outputs/<场>/**` 与 `guanlan.py check` / `rebuild_all.py --dry-run` 的**实跑输出**
+里取数,落到 `docs/figures/*.png`。纯示意图(架构/链路/时序)也在图注里写明"依据"。
+
+## 落点与口径
+    docs/figures/fig-req-01..04 / fig-des-01..06 / fig-dat-01..04 .png
+    docs/src/_guanlan_check.txt        (验收门实跑原文,图的取证底稿)
+    docs/src/_rebuild_plan.txt         (重算链 dry-run 原文,图的取证底稿)
+
+## 用法
+    python scripts/delivery_docs_figures.py            # 全画
+    python scripts/delivery_docs_figures.py --only req # 只画某一组 (req/des/dat)
+    python scripts/delivery_docs_figures.py --check    # 只报缺哪些图
+
+退出码: 0 全部生成 · 5 有图没生成(源件缺或绘图失败,逐条打印原因)
+"""
+from __future__ import annotations
+
+import argparse
+import json
+import pathlib
+import re
+import subprocess
+import sys
+import time
+
+ROOT = pathlib.Path(__file__).resolve().parents[1]
+sys.path.insert(0, str(ROOT))
+
+import matplotlib                                                          # noqa: E402
+matplotlib.use('Agg')
+import matplotlib.pyplot as plt                                            # noqa: E402
+from matplotlib.patches import FancyArrowPatch, FancyBboxPatch             # noqa: E402
+
+from src import paths as P                                                 # noqa: E402
+
+FIG_DIR = ROOT / 'docs' / 'figures'
+SRC_DIR = ROOT / 'docs' / 'src'
+CJK = 'Microsoft YaHei'
+plt.rcParams['font.sans-serif'] = [CJK, 'SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+plt.rcParams['figure.dpi'] = 200
+
+WARN: list[str] = []
+VERSION = __import__('importlib').import_module('src.version').VERSION
+
+
+# ── 通用绘图元件 ────────────────────────────────────────────────────────────────
+def canvas(w: float, h: float):
+    fig, ax = plt.subplots(figsize=(w, h))
+    ax.set_xlim(0, 100)
+    ax.set_ylim(0, 100)
+    ax.axis('off')
+    return fig, ax
+
+
+def box(ax, x, y, w, h, text, fc='#eef3fb', ec='#3b6ea5', fs=10, bold=False, align='center'):
+    ax.add_patch(FancyBboxPatch((x, y), w, h, boxstyle='round,pad=0.6,rounding_size=1.4',
+                                linewidth=1.2, edgecolor=ec, facecolor=fc))
+    ax.text(x + w / 2, y + h / 2, text, ha='center', va='center', fontsize=fs,
+            wrap=True, fontweight='bold' if bold else 'normal', linespacing=1.5)
+
+
+def arrow(ax, p, q, color='#555555', style='-|>', lw=1.3, rad=0.0):
+    ax.add_patch(FancyArrowPatch(p, q, arrowstyle=style, mutation_scale=12,
+                                 linewidth=lw, color=color,
+                                 connectionstyle=f'arc3,rad={rad}'))
+
+
+def save(fig, name: str):
+    """落盘前统一做一次**字形兜底**:把 YaHei 缺字的符号换成等价可显字符。
+
+    实逮(2026-09-22): 图里引用的原文(如 `configs/portal_pages.yaml` 的说明)含 `⇒`(U+21D2),
+    matplotlib 报 `Glyph 8658 missing from font(s) Microsoft YaHei` 并画成方框 —— 交付文档里不能有方框。
+    """
+    swap = {'⇒': '→', '⇐': '←', '↔': '↔', '⇔': '↔', '✓': '√', '✗': '×', '≥': '≥'}
+    import matplotlib.text as _mt
+    for t in fig.findobj(_mt.Text):
+        s = t.get_text()
+        if any(k in s for k in swap):
+            for k, v in swap.items():
+                s = s.replace(k, v)
+            t.set_text(s)
+    FIG_DIR.mkdir(parents=True, exist_ok=True)
+    out = FIG_DIR / name
+    fig.tight_layout()
+    fig.savefig(out, bbox_inches='tight', facecolor='white')
+    plt.close(fig)
+    print('  ✓', out.relative_to(ROOT).as_posix(), '%.0f KB' % (out.stat().st_size / 1024))
+
+
+def barh(ax, labels, values, color='#3b6ea5', fmt='{:.0f}', xlabel=''):
+    y = range(len(labels))
+    ax.barh(list(y), values, color=color, height=0.62)
+    ax.set_yticks(list(y))
+    ax.set_yticklabels(labels, fontsize=9)
+    ax.invert_yaxis()
+    for i, v in enumerate(values):
+        ax.text(v, i, ' ' + fmt.format(v), va='center', fontsize=8.5)
+    ax.set_xlabel(xlabel, fontsize=9)
+    ax.tick_params(axis='x', labelsize=8.5)
+    for s in ('top', 'right'):
+        ax.spines[s].set_visible(False)
+
+
+# ── 取数(全部真读,不猜)────────────────────────────────────────────────────────
+def version_history():
+    from src import version as V
+    return list(V.HISTORY), V.VERSION
+
+
+def portal_pages():
+    import yaml
+    d = yaml.safe_load((P.config('portal_pages.yaml')).read_text(encoding='utf-8'))
+    pages = d.get('pages') or d.get('entries') or []
+    if isinstance(d, dict) and not pages:
+        for k, v in d.items():
+            if isinstance(v, list) and v and isinstance(v[0], dict):
+                pages = v
+                break
+    return pages
+
+
+def serve_cfg():
+    return json.loads((P.config('serve.json')).read_text(encoding='utf-8'))
+
+
+def raw_station_stats():
+    """→ [(族, 件数, GB)],实测 data/raw/<场站>/ 下一层目录。
+
+    场站目录名以**场配置**为准(`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 P.farm())
+    out = []
+    if not home.exists():
+        WARN.append(f'data/raw 场站目录不在位: {home}')
+        return out
+    for d in sorted(p for p in home.iterdir() if p.is_dir()):
+        fs = [p for p in d.rglob('*') if p.is_file()]
+        out.append((d.name, len(fs), sum(p.stat().st_size for p in fs) / 1e9))
+    return out
+
+
+def md_tables(path: pathlib.Path):
+    """→ [ [ [cell,...], ... ] ]:把 markdown 里的表逐个解析成二维列表(不含表头分隔行)。"""
+    if not path.exists():
+        WARN.append('源件不在位: %s' % path.relative_to(ROOT).as_posix())
+        return []
+    lines = path.read_text(encoding='utf-8').splitlines()
+    tables, i = [], 0
+    while i < len(lines):
+        if lines[i].startswith('|') and i + 1 < len(lines) and re.match(r'^\|[\s:\-|]+\|$', lines[i + 1].strip()):
+            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('|')])
+                j += 1
+            tables.append(rows)
+            i = j
+        else:
+            i += 1
+    return tables
+
+
+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('|')])
+            j += 1
+        if not rows:
+            continue
+        if first_cell is not None and rows[0][0] != first_cell:
+            continue
+        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):
+    """把两张取证底稿跑出来(已存在且未 --refresh 时复用)。"""
+    SRC_DIR.mkdir(parents=True, exist_ok=True)
+    py = str(ROOT / '.venv' / 'Scripts' / 'python.exe')
+    chk, plan = SRC_DIR / '_guanlan_check.txt', SRC_DIR / '_rebuild_plan.txt'
+    if refresh or not chk.exists():
+        t0 = time.time()
+        r = subprocess.run([py, '-X', 'utf8', 'guanlan.py', 'check'], cwd=str(ROOT),
+                           capture_output=True, timeout=1800)
+        chk.write_text((r.stdout or b'').decode('utf-8', 'replace'), encoding='utf-8')
+        print('  · guanlan.py check 底稿写入 (%.0fs, rc=%d)' % (time.time() - t0, r.returncode))
+    if refresh or not plan.exists():
+        r = subprocess.run([py, '-X', 'utf8', 'scripts/rebuild_all.py', '--dry-run', '--skip-scada'],
+                           cwd=str(ROOT), capture_output=True, timeout=600)
+        plan.write_text((r.stdout or b'').decode('utf-8', 'replace'), encoding='utf-8')
+        print('  · rebuild_all --dry-run 底稿写入')
+
+
+# ── 需求分析组 ─────────────────────────────────────────────────────────────────
+def fig_req_01():
+    hist, _ver = version_history()
+    fig, ax = plt.subplots(figsize=(9.2, 0.62 * len(hist) + 1.4))
+    ax.axis('off')
+    n = len(hist)
+    ax.set_ylim(-0.6, n - 0.4)
+    ax.set_xlim(0, 10)
+    ax.plot([0.7, 0.7], [0, n - 1], color='#3b6ea5', lw=2.2, zorder=1)
+    lvl_cn = {'major': '大', 'minor': '中', 'patch': '小', 'legacy': '旧编号'}
+    for i, h in enumerate(hist):
+        y = n - 1 - i
+        ax.scatter([0.7], [y], s=90, color='#3b6ea5', zorder=3)
+        title = h.get('title', '')
+        title = re.sub(r'(.*?)', '', title)
+        title = title[:46] + ('…' if len(title) > 46 else '')
+        ax.text(1.05, y + 0.18, f"v{h.get('version')}  {h.get('date')}  [{lvl_cn.get(h.get('level'), '')}版本]",
+                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 需求演进时间轴(样本场;依据 src/version.py 的 HISTORY,共 {n} 条)', fontsize=11)
+    save(fig, 'fig-req-01-需求演进时间轴.png')
+
+
+def fig_req_02():
+    fig, ax = canvas(9.4, 6.2)
+    roles = [('值班运行', 88, '今日态势/部件问题/该问题页/报警与故障统计'),
+             ('点检与检修', 65, '机组页/振动与融合面/油样与工单/报告导出'),
+             ('场站管理', 42, '发电性能/可靠性(MTBF·MTBO·MDT)/可用率/限电'),
+             ('研发与交付', 19, '本体与机制链/数据重算/运行体检/版本与打包')]
+    for name, y, pages in roles:
+        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, '角色 — 场景 — 页面关系(依据 系统使用说明书 与 configs/portal_pages.yaml)',
+            ha='center', fontsize=11, fontweight='bold')
+    save(fig, 'fig-req-02-角色场景页面.png')
+
+
+def fig_req_03():
+    pages = portal_pages()
+    if not pages:
+        WARN.append('portal_pages.yaml 未解析出页面条目')
+        return
+    from collections import Counter
+    kinds = Counter(str(p.get('kind', '未标')) for p in pages)
+    order = ['data-derived', 'source-derived', 'frozen-delivery', 'static', 'shipped']
+    ks = [k for k in order if k in kinds] + [k for k in kinds if k not in order]
+    vals = [kinds[k] for k in ks]
+    fig, ax = plt.subplots(figsize=(8.6, 0.62 * len(ks) + 2.0))
+    cn = {'data-derived': 'data-derived(随数据变,须重算)',
+          'source-derived': 'source-derived(随源代码变)',
+          'frozen-delivery': 'frozen-delivery(冻结交付件)',
+          'static': 'static(静态)', 'shipped': 'shipped(随包件)'}
+    barh(ax, [cn.get(k, k) for k in ks], vals, xlabel='页面/子页数(共 %d 项)' % len(pages))
+    ax.set_title('门户页面归口分布(依据 configs/portal_pages.yaml)', fontsize=11)
+    save(fig, 'fig-req-03-页面归口分布.png')
+
+
+def fig_req_04():
+    """验收门分类 —— **与《需求分析》§9.1 表同源**(直接解析那张表,避免图文数字打架)。"""
+    head, rows = md_find_table(SRC_DIR / f'需求分析_观澜_{VERSION}.md', ('分类', '判定条数'))
+    if not rows:
+        WARN.append('需求分析 §9.1 的分类表未解析到,图 fig-req-04 用实跑输出兜底')
+        ensure_evidence()
+        txt = (SRC_DIR / '_guanlan_check.txt').read_text(encoding='utf-8', errors='replace')
+        marks = re.findall(r'^\s*\[(OK|FAIL|--|!|i|\?)\]\s*([^—\n]+)', txt, re.M)
+        cats = {'通过': sum(1 for m, _ in marks if m == 'OK'),
+                '需处置': sum(1 for m, _ in marks if m == 'FAIL'),
+                '跳过或提示': sum(1 for m, _ in marks if m not in ('OK', 'FAIL'))}
+        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[c_name] == '合计':
+                break
+            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)
+    save(fig, 'fig-req-04-验收门分类.png')
+
+
+# ── 设计说明组 ─────────────────────────────────────────────────────────────────
+def fig_des_01():
+    fig, ax = canvas(9.6, 7.4)
+    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)
+    box(ax, 24, 71, 52, 9, '接入层:组件网关 28084(前缀改写 + data-abs 豁免 + 静态件)',
+        fc='#eef3fb', ec='#3b6ea5', fs=10)
+    box(ax, 3, 56, 18, 11, '分析组件\n18033 /detail', fs=9)
+    box(ax, 23, 56, 17, 11, '振动组件\n18020 /cms', fs=9)
+    box(ax, 42, 56, 16, 11, '仿真台\n18791·18792', fs=9)
+    box(ax, 60, 56, 15, 11, '三维 viewer\n64292', fs=9)
+    box(ax, 77, 56, 20, 11, '本机模型档\nOllama 11434\n(可选,未装即降级)', fs=9,
+        fc='#f4eef6', ec='#7d5ba6')
+    for x in (12, 31.5, 50, 67.5, 87):
+        arrow(ax, (50, 71), (x, 67.4), color='#8a8a8a')
+    box(ax, 3, 41, 94, 10, '产物层 outputs/<场>/:windscada 判级与曲线 · slim10min 窄仓 · 振动六层链 · '
+                           '本体对象库 · SOP/findings · guanlan 契约', fc='#e8f1e4', ec='#5b8c4a', fs=9.5)
+    arrow(ax, (50, 56), (50, 51.4))
+    box(ax, 3, 27, 94, 9, '摄入与构建层:rebuild_all.py 23 步(place_raw_data → 10min/1min 摄入 → '
+                          '10 个 SCADA 构建器 → 振动摄入 → 本体六步 → 审计)', fc='#e8f1e4', ec='#5b8c4a', fs=9.5)
+    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, '共享技术资料(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))
+    box(ax, 3, 2, 94, 7, '贯穿全链的守卫:单一真源(src/paths.py·src/version.py) · 审计门(guanlan.py check / '
+                         '7 个审计器) · 逐件来源台账(_provenance.json)', fc='#ffffff', ec='#555555', fs=9)
+    save(fig, 'fig-des-01-总体架构.png')
+
+
+def fig_des_02():
+    """serve.json 是**扁平**结构(`gateway`/`detail`/… 直接是端口整数),不是嵌套 dict。"""
+    cfg = serve_cfg()
+    host, pub = cfg.get('host', '?'), cfg.get('public_host', '')
+    gw_port = cfg.get('gateway', '?')
+    comps = [(k, cfg.get(k)) for k in ('detail', 'cms', 'sim', 'sim_sys', 'viewer')]
+    fig, ax = canvas(9.6, 5.2)
+    box(ax, 30, 82, 40, 12, '网关 gateway\n%s:%s(对外唯一入口)' % (pub or host, gw_port),
+        fc='#eef3fb', ec='#3b6ea5', fs=10, bold=True)
+    n = max(1, len(comps))
+    for i, (name, port) in enumerate(comps):
+        x = 3 + i * (94 / n)
+        w = 94 / n - 3
+        box(ax, x, 52, w, 14, '%s\n%s:%s' % (name, host, port), fs=9)
+        arrow(ax, (50, 82), (x + w / 2, 66.6), color='#8a8a8a')
+    box(ax, 3, 30, 94, 12, '绑定与自启:guanlan 服务(Windows 服务 / systemd)· start.bat 静默启动 · '
+                           'run/*.pid 运行态 · logs/ 统一落点', fc='#f6f2e8', ec='#b08a3e', fs=9)
+    arrow(ax, (50, 52), (50, 42.6))
+    box(ax, 3, 12, 94, 12, '端口冲突处置:netstat -ano 找占用进程 → 按端口清理 → 重启服务\n'
+                           '(service_ctl stop 不杀子进程,只按端口杀会留下孤儿实例)', fc='#ffffff',
+        ec='#555555', fs=9)
+    arrow(ax, (50, 30), (50, 24.6))
+    ax.text(50, 96, '端口与进程拓扑(依据 configs/serve.json 实读)', ha='center', fontsize=11.5,
+            fontweight='bold')
+    save(fig, 'fig-des-02-端口与进程.png')
+
+
+def fig_des_03():
+    ensure_evidence()
+    txt = (SRC_DIR / '_rebuild_plan.txt').read_text(encoding='utf-8', errors='replace')
+    steps = []
+    for line in txt.splitlines():
+        m = re.match(r'^\s{2}(\S.*?)\s{2,}(\S.*)$', line)
+        if m and not line.strip().startswith('('):
+            steps.append((m.group(1).strip(), m.group(2).strip()))
+    if not steps:
+        WARN.append('rebuild_all --dry-run 未解析出步骤')
+        return
+    fig, ax = plt.subplots(figsize=(9.6, 0.42 * len(steps) + 1.8))
+    ax.axis('off')
+    ax.set_xlim(0, 10)
+    ax.set_ylim(-0.5, len(steps) - 0.2)
+    for i, (name, cmd) in enumerate(steps):
+        y = len(steps) - 1 - i
+        ax.add_patch(plt.Rectangle((0.2, y - 0.34), 1.75, 0.68, facecolor='#eef3fb',
+                                   edgecolor='#3b6ea5', lw=0.9))
+        ax.text(1.07, y, name[:22], fontsize=8.6, ha='center', va='center')
+        ax.text(2.15, y, cmd[:92], fontsize=8.2, va='center', color='#333333', family='monospace')
+        if i < len(steps) - 1:
+            ax.annotate('', xy=(1.07, y - 0.42), xytext=(1.07, y - 0.30),
+                        arrowprops=dict(arrowstyle='-|>', color='#8a8a8a', lw=0.9))
+    ax.set_title('重算链步骤(依据 scripts/rebuild_all.py --dry-run --skip-scada,共 %d 步)' % len(steps),
+                 fontsize=11)
+    save(fig, 'fig-des-03-重算链.png')
+
+
+def fig_des_04():
+    fig, ax = canvas(9.6, 6.4)
+    ax.text(50, 96, '判级矩阵与四源融合数据流(每条判据都能指到输入列)', ha='center',
+            fontsize=11.5, fontweight='bold')
+    srcs = ['SCADA 10min\n(slim10min 48 列)', 'CMS 振动\n(六层链)', '状态·故障·告警\n(台账)', '油样与工单\n(现场记录)']
+    for i, s in enumerate(srcs):
+        box(ax, 1.5 + i * 24.6, 78, 22, 12, s, fc='#f6f2e8', ec='#b08a3e', fs=9)
+    box(ax, 6, 58, 40, 12, '七系统判级矩阵\n变桨·偏航·主轴承·齿轮箱·发电机·变流器·主控与传感网',
+        fc='#eef3fb', ec='#3b6ea5', fs=9.5)
+    box(ax, 52, 58, 42, 12, '融合面四源对齐\n振动 × SCADA × 台账 × 人工\n(逐台判级 + 证据链)',
+        fc='#eef3fb', ec='#3b6ea5', fs=9.5)
+    for x in (12, 36, 62, 86):
+        arrow(ax, (x, 78), (26 if x < 50 else 73, 70.6), color='#8a8a8a')
+    box(ax, 6, 40, 88, 11, '时间窗:判级与曲线按所选时间窗重算(月/年/自定义起止日期,含两端);'
+                           '温度与停机台账按真窗取月', fc='#e8f1e4', ec='#5b8c4a', fs=9.5)
+    arrow(ax, (26, 58), (26, 51.6))
+    arrow(ax, (73, 58), (73, 51.6))
+    outs = ['部件问题清单\n(按系统×机组)', '该问题页\n(单问题+完整依据)', '发电性能与可靠性\n(MTBF/MTBO/MDT)',
+            '控制参数一致性\n(P/ω 封顶·K 聚类)', '报告导出\n(docx/md/页面)', '本体机制链\n与决策台']
+    for i, o in enumerate(outs):
+        x = 2 + (i % 3) * 32.3
+        y = 24 if i < 3 else 8
+        box(ax, x, y, 30, 12, o, fc='#f4eef6', ec='#7d5ba6', fs=9)
+    arrow(ax, (50, 40), (50, 36.6), color='#5b8c4a')
+    box(ax, 2, 21.5, 96, 2.6, '', fc='#ffffff', ec='#ffffff')
+    ax.text(50, 36, '产物(outputs/<场>/…,逐件登记生成端与输入)', ha='center', fontsize=9, color='#444444')
+    save(fig, 'fig-des-04-判级与融合.png')
+
+
+def fig_des_05():
+    fig, ax = canvas(9.6, 5.6)
+    ax.text(50, 96, '时间窗按窗重算与 pending 契约(实测:冷窗首查 pending,约 30 s 后落位)',
+            ha='center', fontsize=11.5, fontweight='bold')
+    lanes = [('前端 app.js', 82), ('服务 windscada_serve.py', 60), ('按窗缓存 _WIN_CACHE', 38), ('重算线程(_HEAVY_LOCK 串行)', 16)]
+    for name, y in lanes:
+        box(ax, 2, y, 24, 11, name, fc='#eef3fb', ec='#3b6ea5', fs=9)
+        ax.plot([26.5, 97], [y + 5.5, y + 5.5], color='#d0d0d0', lw=0.9, zorder=0)
+    msgs = [(82, 39, 'GET /api/fleet?win=2026-07'),
+            (60, 82, 'win 未命中 → 起后台线程,先回上一份口径并标 pending=1'),
+            (38, 60, '缺 → 入队 sysmx/curves/m9'),
+            (16, 38, '算(变桨/偏航/蓄能/温度 + 曲线 + M9)'),
+            (38, 16, '算完写入缓存'),
+            (60, 38, '5 s 轮询再取 → 命中'),
+            (82, 60, 'pending=0,页面换成该时间窗口径')]
+    for y1, y2, text in msgs:
+        arrow(ax, (36, y1 + 5.5), (36, y2 + 5.5), color='#3b6ea5')
+        ax.text(38.5, (y1 + y2) / 2 + 5.5, text, fontsize=8.8, va='center')
+    ax.text(50, 5, '为什么这样:算得慢(每面 5~120 s),不能把页面卡住;宁可先给旧口径并**如实标注**,也不静默等待。',
+            ha='center', fontsize=9, color='#444444')
+    save(fig, 'fig-des-05-时间窗与pending.png')
+
+
+def fig_des_06():
+    fig, ax = canvas(9.0, 6.0)
+    cx, cy, r = 50, 50, 30
+    items = [('改代码/配置', '#eef3fb'), ('重算 rebuild_all 23 步', '#e8f1e4'),
+             ('审计器 7 件(反向/页面/配置/链路/版本…)', '#f6f2e8'),
+             ('guanlan.py check 全绿', '#f4eef6'), ('打包 pack_dist + 开箱验证', '#eef3fb'),
+             ('装到目标机 install.ps1', '#e8f1e4')]
+    import math
+    for i, (t, fc) in enumerate(items):
+        a = math.radians(90 - i * 360 / len(items))
+        x, y = cx + r * math.cos(a), cy + r * math.sin(a)
+        box(ax, x - 15, y - 6, 30, 12, t, fc=fc, ec='#3b6ea5', fs=9)
+        a2 = math.radians(90 - (i + 1) * 360 / len(items))
+        arrow(ax, (cx + (r - 6) * math.cos(a), cy + (r - 6) * math.sin(a)),
+              (cx + (r - 6) * math.cos(a2), cy + (r - 6) * math.sin(a2)),
+              color='#8a8a8a', rad=-0.25)
+    box(ax, 38, 44, 24, 12, '版本纪律\n单一真源 src/version.py', fc='#ffffff', ec='#555555', fs=9, bold=True)
+    ax.text(50, 96, '质量门与审计闭环(每环都能独立重跑、逐条给退出码)', ha='center', fontsize=11.5,
+            fontweight='bold')
+    save(fig, 'fig-des-06-质量门闭环.png')
+
+
+
+GROUPS = {
+    'req': [fig_req_01, fig_req_02, fig_req_03, fig_req_04],
+    'des': [fig_des_01, fig_des_02, fig_des_03, fig_des_04, fig_des_05, fig_des_06],
+}
+# ★2026-09-22 用户令: 《数据要求说明》改成纯数据需求规格、不再放插图(原 dat 组四张是样本场现状图,
+#   不再体现)—— 组与期望清单同步去掉, 四个 PNG 也从 docs/figures 删除。
+EXPECTED = ['fig-req-01-需求演进时间轴.png', 'fig-req-02-角色场景页面.png',
+            'fig-req-03-页面归口分布.png', 'fig-req-04-验收门分类.png',
+            'fig-des-01-总体架构.png', 'fig-des-02-端口与进程.png', 'fig-des-03-重算链.png',
+            'fig-des-04-判级与融合.png', 'fig-des-05-时间窗与pending.png', 'fig-des-06-质量门闭环.png']
+
+
+def main() -> int:
+    ap = argparse.ArgumentParser()
+    ap.add_argument('--only', default=None, choices=sorted(GROUPS))
+    ap.add_argument('--refresh', action='store_true',
+                    help='重跑取证底稿(guanlan.py check / rebuild_all --dry-run)后再画图')
+    ap.add_argument('--check', action='store_true', help='只报缺哪些图')
+    a = ap.parse_args()
+    if a.refresh:
+        for p in (SRC_DIR / '_guanlan_check.txt', SRC_DIR / '_rebuild_plan.txt'):
+            p.unlink(missing_ok=True)
+    if a.check:
+        miss = [f for f in EXPECTED if not (FIG_DIR / f).exists()]
+        print('图表体检: %d/%d 在位' % (len(EXPECTED) - len(miss), len(EXPECTED)))
+        for f in miss:
+            print('  ✗', f)
+        return 0 if not miss else 5
+    groups = [a.only] if a.only else sorted(GROUPS)
+    for g in groups:
+        print('── %s 组 ──' % g)
+        for fn in GROUPS[g]:
+            try:
+                fn()
+            except Exception as e:  # noqa: BLE001
+                WARN.append('%s 失败: %s' % (fn.__name__, e))
+                print('  ✗', fn.__name__, e)
+    miss = [f for f in EXPECTED if not (FIG_DIR / f).exists()]
+    if WARN:
+        print('\n告警/缺件:')
+        for w in WARN:
+            print(' -', w)
+    print('\n图表: %d/%d 在位' % (len(EXPECTED) - len(miss), len(EXPECTED)))
+    return 0 if not miss else 5
+
+
+if __name__ == '__main__':
+    raise SystemExit(main())

+ 22 - 1
src/version.py

@@ -19,7 +19,7 @@ import json
 import pathlib
 
 NAME = '观澜·如东样板 v2'
-VERSION = '2.10.1'         # ★ 改版本只改这里
+VERSION = '2.10.2'         # ★ 版本只改这里
 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.2', date='2026-09-22', level='patch',
+         title='数据要求说明改成纯数据需求规格(去现状/去实现细节)+ 新增 SCADA 秒级数据要求',
+         note='纯交付物修订 ⇒ 小版本 +1。用户令: 「数据要求说明不要体现样本场的接入情况、英文测点名称、'
+              '机组名称、文件名称、落盘位置,只体现测点真实业务含义名称;每类数据的测点以列表呈现'
+              '(测点/字段名、业务含义、度量单位、是否必须);增加 SCADA 秒级数据的要求」。做法: '
+              '① **删掉一切样本场现状**: 到位情况/缺口/催缴状态、件数体量、行数、时间覆盖、日期全部不再出现'
+              '(该文只写"对数据的要求",不描述任何具体场站);机组范围改写为「全场机组(按场实际台数)」。'
+              '② **去实现细节**: 正文与表格不再出现英文测点名(snake_case)、文件名与扩展名、目录与落盘位置、'
+              '机组编号;测点一律用中文业务含义名称(有功功率、齿轮箱油温、机舱风速…),单位取自'
+              '`configs/canonical/dictionary.yaml` 的 unit_majority 与机型—场站契约的 unit,查不到就写「未取证」。'
+              '③ **测点列表化**: 每类数据一节 + 一张四列表(测点/字段名 | 业务含义 | 度量单位 | 是否必须,'
+              '分级只在必须/建议/可选三档),另加"测点汇总索引"章。'
+              '④ **新增 SCADA 秒级数据要求**: 1 s 或 5 s、至少 3 个月、全场全测点,并给出必需测点清单'
+              '(功率、风速风向、发电机/主轴/叶轮转速、三叶桨距与一致性、发电机绕组与齿轮箱温度、机舱与环境温度、'
+              '偏航角度与压力、液压与蓄能压力、运行状态位、限电与功率给定、振动触发量…),写清它与 10 分钟数据、'
+              '故障录波、机舱振动数据的分工与配合。'
+              '⑤ **机器硬门**: `delivery_docs_build.py` 为该文加 `strict_terms` 检查(snake_case 标识/文件扩展名/'
+              '目录路径/机组编号/现状字样),`--check`(扫源件)与 `--verify`(扫渲染后的 docx)命中即 FAIL;'
+              '该文不再配插图(原四张是样本场现状图,按用户令不再体现),图表器移除 dat 组与对应四张 PNG。'
+              '验证: 新稿 markdown 与 docx 对上述各类 0 命中,21 类每类都有四列测点表,秒级节含测点表;'
+              '`guanlan.py check` 全绿;三份 docx 重渲(req/des 内容不变,仅版本号与文件名随批次升到 2.10.2)。'),
     dict(version='2.10.1', date='2026-09-22', level='patch',
          title='交付文档对外版:去样本场标识(全文不体现具体风电场)+ 新增多场适用性章节',
          note='纯交付物修订(系统功能未变)⇒ 小版本 +1。用户令: 「修改三份文档,内容参考如东风电场,'

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff