Преглед изворни кода

文档: 新增《重算缺口与补件清单 v0.1》+ 记录文本 I/O 收尾

从零重算照出来的缺口, 之前只散在聊天与 §4 表里, 现在整理成能拿去对接的独立清单:
`docs/重算缺口与补件清单_v0.1.md`

- §0 缺口总表: 9 项(A1-A4 源件类 / B1-B4 生成端类), 每项写"缺什么 · 卡住哪个页面 · 找谁 · 补齐判据";
- §1 源件缺失: 油样 2026-07 批(102 行) · 振动线 CMS 测点 handoff · SOP 底稿+范式实验件 · L1 判级底座;
- §2 生成端缺失: 逐件给出**实测引用计数与写入迹象**(用脚本在 src/+scripts/ 全量搜的原始数字),
  并订正两处口径 —— 计数已排除 place_raw_data.py 里我自己写的"故意不落"说明清单;
  `yaw_press_monthly`/`yaw1min_liveness`/`genbearing_monthly` 实测是**全库 0 处引用**(连读方都没有),
  补之前该先问"谁要它";
- §3 对照已能重算的部分; §4 补齐后的六步验证命令; §5 边界与纪律(不猜不伪造、累计快照必须归并);
- 校对过的数字: 契约 n_claims=124(定论 20 · 准准定论 11 · 候选 62 · 参考 15 · INSUFFICIENT 9 · 撤回 7),
  sha 45f7b4e3b6c6913a; 包内油样目录准确路径为 `数据收集/更新/(8)…/2025年油样/`,
  全包按 YP013 搜 0 命中。

同时: `docs/数据目录结构与落位约定_v0.2.md` §4 末尾加指引指向该清单;
`_修复记录_20260911/README.md` 补「文本 I/O 的 encoding (收尾)」一节(28 处站点、门禁扩面、冒烟结果)。
zhouyang.xie пре 1 месец
родитељ
комит
920a01f69d

+ 20 - 0
_修复记录_20260911/README.md

@@ -253,6 +253,26 @@ L0 仓 16 件 1.78 MB (随包 118 件 —— 差额就是"包内没有生成端"
 **顺带发现**: 落位后工单源件 79→80 张, 但总行数不变(5876)——新件的行与既有来源内容重复,
 **顺带发现**: 落位后工单源件 79→80 张, 但总行数不变(5876)——新件的行与既有来源内容重复,
 按"累计快照/副本必须归并"的纪律合并, 脚本把这类情况逐条打了出来(不是静默丢)。
 按"累计快照/副本必须归并"的纪律合并, 脚本把这类情况逐条打了出来(不是静默丢)。
 
 
+**要补什么、找谁补**: 那次重算照出来的缺口整理成了独立清单 ——
+`docs\重算缺口与补件清单_v0.1.md`(按"源件缺失(现场能补)"与"生成端缺失(研发补脚本)"两类,
+每项带证据路径、影响面、补齐判据与验证命令)。
+
+**另一件同类收尾**: 把剩下的 18 处"文本读写缺 `encoding=`"也清了(见下), 门禁 WARN 项 6 归零。
+
+## 文本 I/O 的 encoding (2026-09-11 收尾)
+
+上面 bug ① 只是冰山一角: 全库搜下来共 **28 处**文本 I/O 没写 `encoding=`(本体层 10 + 其余 18)。
+已全部补上 `encoding='utf-8'`, 其中写侧把 `json.dump(x, open(p,'w'))` 这类匿名句柄改成 `with` 块(正确关文件),
+`src\windcms\plugins.py` 里给子进程当 stdout 的那个句柄**不 with-close**(父进程持引用, 免得提前关掉)。
+
+`check_portability.py` 的 WARN 项 6 同时扩了扫描面: 除 `read_text/write_text` 外, 新增
+`open(..., 'w')` 与 `open(p)`(无 mode = 文本读)两种形态(二进制模式不算), 并与 ERROR 扫描共用 `ALLOW` 例外表
+(门禁自己的规则/文档里必须写出这些形态, 整份豁免 —— 否则会误报 10 处)。**实测命中 0。**
+
+冒烟: 11 个文件 `py_compile` 通过 · windcms 五个模块 + fusion/taxonomy/scenario_29 导入成功 ·
+重启服务 5/7 ok · 页面字节与改前逐项一致(`/` 20225828 · `/detail/` 229643 · `/sim/` 50998 ·
+`/sim/sys/` 123455 · `/viewer/` 4986) · 维护页数据层四个数字不变(报警 39211 · 工单 5876 · 油样 404 · 月表 3729)。
+
 ## 仍然存在 (不是代码问题)
 ## 仍然存在 (不是代码问题)
 
 
 - `/local-ai/` 仍是 DOWN: 本机 Ollama 没运行, 且 `%USERPROFILE%\.ollama` 下**没有任何模型**
 - `/local-ai/` 仍是 DOWN: 本机 Ollama 没运行, 且 `%USERPROFILE%\.ollama` 下**没有任何模型**

+ 3 - 0
docs/数据目录结构与落位约定_v0.2.md

@@ -151,6 +151,9 @@
 > (判级矩阵 `system_matrix`/`fusion_table`、振动 handoff、链盘 `/api/fleet`), 而 L1 那几件本身 ⛔,
 > (判级矩阵 `system_matrix`/`fusion_table`、振动 handoff、链盘 `/api/fleet`), 而 L1 那几件本身 ⛔,
 > 所以从零重算时它们要么报错退役、要么无变化 —— `populate` 实测卡在 `pitch\pitch_daily.parquet`。
 > 所以从零重算时它们要么报错退役、要么无变化 —— `populate` 实测卡在 `pitch\pitch_daily.parquet`。
 
 
+> **要补什么、找谁补**: 见 `docs\重算缺口与补件清单_v0.1.md` —— 按"源件缺失(现场能补)"与
+> "生成端缺失(研发补脚本)"两类逐项列了证据路径、影响面与补齐判据。
+
 **从零重算实测(2026-09-11,只用 `data\raw`,不吃随包产物)**
 **从零重算实测(2026-09-11,只用 `data\raw`,不吃随包产物)**
 
 
 | 环节 | 结果 |
 | 环节 | 结果 |

+ 149 - 0
docs/重算缺口与补件清单_v0.1.md

@@ -0,0 +1,149 @@
+# 观澜 · 如东 v0.2.0 重算缺口与补件清单 (v0.1, 2026-09-11)
+
+## 为什么有这份清单
+
+用户令: **"不要随包产物, 基于 `data\raw` 从零重算"**。做完之后浮出来一个事实:
+
+> 页面能不能显示, 取决于两件事 —— **源件在不在 `data\raw`** 与 **生成端在不在包里**。
+> 把产物全挪走之后, 第一个问题现场能解决(把文件放进去), 第二个问题**只能由研发/厂商补脚本**。
+
+本清单是那次从零重算实测照出来的**缺口台账**: 缺什么、卡住哪个页面、证据在哪、找谁要、拿到之后
+按什么标准判定"补齐了"。口径: 只列数据/脚本层面的缺口, 不含环境项(如本机 Ollama 无模型 → `/local-ai/` 503)。
+
+## 0. 缺口总表
+
+| # | 缺什么 | 类型 | 卡住的页面/功能 | 找谁 | 补齐判据 |
+|---|---|---|---|---|---|
+| **A1** | 油样 2026-07 批的源件(1 份**合并报告**) | 源件 | 数据层「油液化验」**506→404**; 油液时效胶囊、油液判级 | 现场 / 化验机构 | 放进 `data\raw\如东\油样报告\` 重跑摄入后, `rebuild_from_raw.py --verify` **无人工项** |
+| **A2** | 振动线 **CMS 测点数据(handoff)**, 随包 90 件 56 MB | 源件 | `/cms/` **503**; 融合面; 台账判级; `windcms` 41 个报告页; `trend_ingest` | 振动线 / 研发 | `/cms/` 回 200; 台账"振动级"列有值 |
+| **A3** | SOP 底稿 + 范式实验件(`sop` 210 件 16.5 MB, `paradigm_r1` 29 件) | 源件(内含脚本) | 门户**契约结论段**、`/detail/api/facts` **503**、**124 条** claim | 研发 | `/api/facts` 回 200; 门户 `#findings` 有结论段 |
+| **A4** | `temp_monthly.parquet` 等 L1 判级底座 | 生成端 | 工作台实时取数面 `err=no_products`(`query`/`turbine`/`fleet`) | 研发 | `/detail/api/query` 返回 `series` 而非 `err` |
+| **B1** | 6 件组级/月度产物(见 §2) | 生成端 | 趋势件、热链、扇区、结构面 | 研发 | 对应页面/接口有数 |
+| **B2** | 6 件偏航/温度/润滑产物(见 §2) | 生成端 | 偏航面、润滑面 | 研发 | 同上。**注**: 源件 `scada_1min` 已落位 12.8 GB, 脚本一到位即可算 |
+| **B3** | `pitch\pitch_daily.parquet` | 生成端 | 变桨面; 本体 `populate` **卡在这里** | 研发 | `python -m src.ontology.populate` 能跑完 |
+| **B4** | CMS/TCM 兼容链(`windcms` 53 件 29 MB · `tcm_compatible_replay` 59 件 34 MB) | 生成端 + 源件 | CMS 报告页、融合面、TCM 兼容链 | 振动线 + 研发 | `windcms analyze` 能出件 |
+
+---
+
+## 1. 类型 A: 源件缺失(现场/厂商能给)
+
+### A1 · 油样 2026-07 批(102 行)
+
+- **缺的**: 一份**合并报告** `BG-2026-07-YP013 中广核新能源如东海上风电场.pdf`(一份覆盖多台, 台号/部件只写在 PDF 表格里)。
+- **证据**: 现场包 `F:\temp\如东风场数据` 里油样只有一批 ——
+  `数据收集/更新/(8)风机振动数据、油液分析记录(2025.1-至今)/2025年油样/`(404 件); 全包按 `YP013` 搜 **0 命中**,
+  按批次目录枚举也只有 `2025年油样` 这一批, 没有 2026-07。从零重算的结果是 `oil_samples_index` 404 行,
+  比随包 506 行少 102 行 —— 这也是 `--verify` 唯一的"需人工看"项。
+- **为什么不能"猜"出来**: 台号/部件在 PDF 表格里, 不是文件名可解析的形态; 摄入脚本对此的态度是
+  **合并语义**——认不出的老行原样保留、绝不删(所以场景②那次是 506→404 的落差, 而不是静默覆盖)。
+- **怎么补**: 把那份合并报告(或该批的单台报告)放进 `data\raw\如东\油样报告\`, 重跑油样摄入。
+- **判定**: `rebuild_from_raw.py --verify` 的结论行不再出现"需人工看"。
+
+### A2 · 振动线 CMS 测点数据(handoff)
+
+- **缺的**: 随包里 `outputs\rudong\m5_cms_tcm\`(90 件 56 MB: handoff + TCM 兼容件 + 窗/谱/模型产物)
+  以及 `outputs\rudong\windcms\`(53 件 29 MB, 41 个 HTML 报告页)。
+- **证据**: `src\windscada\subsys\fusion.py` 的 `load_handoff()` 直接抛
+  `FileNotFoundError: 振动 handoff 接口缺失 … 融合面不可静默降级`;
+  `src\windcms\data.py` 的 `load_scalars()` 读的是 CMS **测点索引**(不是报告); 现场包里只有
+  `(8)…\振动分析报告\` 12 份**月度用印版 PDF** —— 那是成品牌报告, 不是可再加工的测量数据。
+- **影响面**: `/cms/` 503; 融合面判级; 台账里"振动级"这一轴; `trend_ingest`(在升/闭环证据)。
+- **怎么补**: 向振动线要 **CMS 测点导出/handoff 件**(按 `m5_cms_tcm` 的目录约定), 而不是 PDF 报告。
+- **判定**: `/cms/` 回 200 且能列出报告页。
+
+### A3 · SOP 底稿 + 范式实验件(事实契约的输入)
+
+- **缺的**: `outputs\rudong\sop\findings.json`(在 210 件里)与
+  `outputs\rudong\paradigm_r1\experiments\{E5_coverage_table, E3_candidate_closure, E8_rudong_rebuild}\底稿*.json`。
+- **证据**: `scripts\guanlan_facts_contract.py` 的 `SRC` 字典**只**读这四个文件(第 14-15 行);
+  没有它们就没有契约, 也就没有门户结论段与 `/api/facts`。
+- **影响面**: 门户 `#findings` 的"定论与准定论"表、`/detail/api/facts`(503)、**124 条** claim
+  (定论 20 · 准定论·预警 11 · 候选 62 · 参考 15 · INSUFFICIENT 9 · 撤回 7)、
+  以及依赖契约的问答引文。
+- **怎么补**: `sop\` 里那 22 个 `.py` 是**历史装配脚本**(含 mac 死路径, 随包留档) —— 要的是
+  它们跑出来的**底稿产物**, 或可复跑的版本。找研发要。
+- **判定**: `/api/facts` 回 200 且 `n_claims` 与出厂一致(124, 契约 sha `45f7b4e3b6c6913a…`)。
+
+### A4 · L1 判级底座(`temp_monthly.parquet` 等)
+
+- **缺的**: 随包 `outputs\rudong\windscada\` 里 118 件中的**非 16 件**(我们只重算得出 16 件)。
+- **证据**: 工作台取数面直接回结构化"无产物":
+  `{"err":"no_products","note":"temp_monthly.parquet 不存在 → outputs/rudong/windscada/temp_monthly.parquet; 先跑 scripts/rebuild_from_raw.py (SCADA 侧加 --scada) 生成产物, 或用 scripts/products_state.py --on 还原"}`。
+  注意那句提示在**这一件**上是误导的: `temp_monthly` 不在 `rebuild_from_raw.py` 的 10 个构建器里。
+- **怎么补**: 研发补 `temp_monthly` 的构建器(见 B1), 或明确它属于哪个上游产物。
+
+---
+
+## 2. 类型 B: 生成端缺失(研发补脚本)
+
+判据是同一句话: **全库只有读取方、0 处写入方**。下表是 2026-09-11 用脚本在 `src\` + `scripts\`
+全量搜出来的原始计数(判"写入迹象"看同一行有没有 `to_parquet` / `write_parquet` / `to_csv` / `to_excel` /
+`open(...,'w')` 之类的落盘调用)。**计数已排除 `scripts\place_raw_data.py` 里那份"故意不落"说明清单** ——
+那是我写文档时列的产物名, 不是消费方。
+
+| 产物 | 真实引用处(读取侧) | 写入迹象 |
+|---|---|---|
+| `pc_monthly_bins` | `src\windscada\perf\trend.py:18` | **0** |
+| `duty_monthly` | `scripts\windscada_serve.py:540` | **0** |
+| `thermal_monthly` | `src\windscada\subsys\thermal_chain.py:97` | **0** |
+| `sector_power` | `src\windscada\perf\trend.py:91` | **0** |
+| `structure.parquet` | `src\windscada\subsys\structure.py:48` | **0** |
+| `watch_channels_monthly` | `src\ontology\seed_rudong29.py:27,72` 等 6 处 | **0** |
+| `yaw_dynamic_monthly` | `scripts\windscada_serve.py:835,837` 等 4 处 | **0** |
+| `yaw_err_clean` | `src\windscada\subsys\yaw.py:68` | **0** |
+| `mblub_monthly` | `src\ontology\acceptance_fsm.py:20,67` 等 7 处 | **0** |
+| `temp_monthly` | `scripts\windscada_serve.py:517,531` | **0** |
+| `pitch_daily` | `src\windscada\subsys\pitch.py:19` | **0** |
+| `yaw_press_monthly` · `yaw1min_liveness` · `genbearing_monthly` | **全库 0 处引用**(连读方都没有) | **0** |
+
+> 最后一行值得单独说: 这三件在随包产物目录里存在, 但当前代码**从不引用它们**。要么是给外部/下游用的,
+> 要么是早期版本的遗留件 —— 补之前建议先问清"谁要它", 免得补一堆没人读的表。
+
+**B2 的特别说明**: `yaw1min_liveness` / `yaw_dynamic_monthly` 这类要的 1min 源件
+(`data\raw\如东\scada_1min\`, 38 件 12.8 GB)**本轮已按 `place_raw_data.py --scope mech` 落位** ——
+所以它们属于"源件已备、只差脚本", 是最容易补上的一批。
+
+**B3 的证据**: `python -m src.ontology.populate` 实测直接报
+`FileNotFoundError: outputs\rudong\pitch\pitch_daily.parquet` —— 本体铺开这一步就断在这。
+
+**B4 的证据**: `src\windcms\` 里有完整的 `analyze` 链(pipeline/plugins/serve), 但它的输入是
+**CMS 测点索引**(见 A2), 包里没有; `tcm_compatible_replay\` 的 59 件同理。
+
+---
+
+## 3. 对照: 已经从 `data\raw` 重算出来的(2026-09-11 实测)
+
+| 环节 | 结果 |
+|---|---|
+| 三门台账 | 报警 **39211 行** · 工单 **5876 行** · 油样 **404 行** |
+| SCADA 侧 10 个构建器 | `powercurve_dev/bins` · `loss_monthly` · `curve_lenses/liveness` · `control_profile/schedule` · `stop_events` · `temp_bins` · `yaw_daily` · `hydraulic_accum` · `thermal_chain` · `system_aux` |
+| 本体层 | `objects.json` 2340 对象(含 Doc 303) · `retrieval_index.json` · `turbine_params.parquet` 1706 条 |
+| L0 产物仓 | 16 件 1.78 MB(随包 118 件) |
+| 等价验收 | `alarms` 39211/39211 **逐值完全一致**; `workorders` 8 行差异可归类(旧链时间解析缺陷) |
+
+## 4. 补齐之后怎么验(照这个顺序)
+
+```bat
+:: ① 源件落位 (现场包 → 约定目录; 同尺寸自动跳过, 可反复跑)
+.venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope full
+:: ② 重算 (三类台账 + SCADA 侧; SCADA 那步要逐台读 14.7 GB, 分钟级)
+.venv\Scripts\python.exe scripts\rebuild_from_raw.py
+.venv\Scripts\python.exe scripts\rebuild_from_raw.py --scada
+:: ③ 等价验收 (需临时把随包基线放回 outputs\rudong\windscada\_pre_rebuild_20260911\)
+.venv\Scripts\python.exe scripts\rebuild_from_raw.py --verify
+:: ④ 本体层 (技术资料在盘上才有意义)
+.venv\Scripts\python.exe -m src.ontology.kb_ingest
+:: ⑤ 重启服务 (产物变了必须重启, CMS 是启动时加载数据)
+.venv\Scripts\python.exe guanlan.py stop ; .venv\Scripts\python.exe guanlan.py serve
+:: ⑥ 看页面: /detail/ 维护页「数据层 + 机理层」那一屏 + 回归尺子
+.venv\Scripts\python.exe scripts\page_fingerprint.py --diff <基线.json>
+```
+
+## 5. 边界与纪律
+
+- **不猜、不伪造**: 源件/生成端不在, 页面就明确显示"无产物/无源"并给出恢复命令, 绝不用别的数据顶替
+  (工作台那句 `err=no_products` 就是这条纪律的产物)。
+- **累计快照必须归并**: 工单/报警目录里的"全年/年至今"件是季度件的累计快照, 摄入时自动跳过; 跨源内容重复自动归并 ——
+  新加源件不会让台账虚高(本轮 `大部件维修记录` 就是被归并的)。
+- **本清单的维护**: 新增一项的条件是"能用一条命令重复验证它的缺失"; 每项都要写清证据路径与补齐判据。
+- 相关文档: `docs\数据目录结构与落位约定_v0.2.md`(§4 重算边界表) · `_修复记录_20260911\README.md`(从零重算一节)