浏览代码

文档: 记下"从零重算"实测与三个静默 bug

- docs/数据目录结构与落位约定_v0.2.md
  §2: 补 `place_raw_data.py` 的用法与 a2/mech/full 三个范围表, 写清 mech 落什么、依据是什么,
      以及三种"故意不落"(scada数据原始通道导出 / fastlog / 振动线成品牌报告)的理由;
  §4: `objects.json` 从 ⛔ 改为"放进技术资料即可重算", 并说明它取决于 TECH 目录与 rd() 四个文件;
      新增"从零重算实测"表(台账行数、10 个构建器、L0 仓 16 件 vs 随包 118 件、验收结论);
  §8: 变更记录补一行。
- _修复记录_20260911/README.md: 新增「从零重算」一节 —— 做法/命令/结果/仍然缺的那 102 行,
  以及这一轮逮到的三个静默失效(本体层 GBK 文本 I/O、place_raw_data 缺 import os、
  单文件落位规则恒不落件), 附"从零重算本身就是最好的回归测试"这条教训。
zhouyang.xie 1 月之前
父节点
当前提交
9c2e71a59a
共有 2 个文件被更改,包括 77 次插入 和 1 次删除
  1. 36 0
      _修复记录_20260911/README.md
  2. 41 1
      docs/数据目录结构与落位约定_v0.2.md

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

@@ -217,6 +217,42 @@ mac venv 解释器、`kb_ingest` 的 mac 技术资料路径、`ingest_ops_2025`
 `src\windcms\data.py` "No objects to concatenate" 退出 (本该如此); 产物还原后重启即恢复
 (414,139 B, 与基线一致)。**教训: 产物开关与重启顺序有关, 挪/还产物后必须重启一遍。**
 
+## 从零重算 (2026-09-11 下午, 用户令: 不要随包产物, 基于 data/raw 重算)
+
+**做法**: 产物全部挪走 (`products_state.py --off`), 只留 `data/raw` 重算, 一个随包产物都不吃。
+
+```bat
+.venv\Scripts\python.exe scripts\products_state.py --off          :: 产物挪走
+.venv\Scripts\python.exe scripts\rebuild_from_raw.py              :: 三门台账
+.venv\Scripts\python.exe scripts\rebuild_from_raw.py --scada      :: SCADA 侧 10 个构建器 (逐台读 14.7 GB CSV)
+.venv\Scripts\python.exe scripts\rebuild_from_raw.py --verify     :: 等价验收 (需临时放回随包基线)
+```
+
+**结果**: 报警 0→39211 行 · 工单 0→5876 行 · 油样 0→404 行; SCADA 侧 10 个构建器全跑通,
+L0 仓 16 件 1.78 MB (随包 118 件 —— 差额就是"包内没有生成端"那些件, 见 docs §4);
+本体层 `objects.json` 2340 个对象 + 检索索引 + `turbine_params.parquet` 1706 条。
+等价验收: **alarms 39211/39211 逐值完全一致**; workorders 8 行差异全部可归类(旧链时间解析缺陷)
++ 69 行本链新增覆盖; 唯一人工项是油样 506→404。
+
+**用户追问"缺的数据从 F:\temp\如东风场数据 抽"** → `place_raw_data.py --scope mech` 落 355 件 15.5 GB:
+`data\raw\西门子4.0技术资料\`(317 件, 本体层的源件) 与 `<场站>\scada_1min\`(38 件 12.8 GB)。
+抽完本体层就从"包内没有源件⛔"变成**可重算**。**仍然缺的**: 油样那 102 行的源件
+(`BG-2026-07-YP013 … .pdf`) 不在包里(整个包只有 `2025年油样` 一批) → 那 102 行找不回来。
+
+### 这一轮逮到的三个真 bug (都不是参数问题, 是静默失效)
+
+| # | 症状 | 根因 | 影响面 |
+|---|---|---|---|
+| 1 | `python -m src.ontology.kb_ingest` 崩: `UnicodeEncodeError: 'gbk' codec can't encode '\u200b'` | `src/ontology/store.py` 的 `read_text()/write_text()` 没写 `encoding=` → Python 文本 I/O 用**系统 locale 编码**, 中文 Windows = cp936。读会乱码/解码失败, 写遇到 GBK 编不出的字符就崩 | **本体层在中文 Windows 上根本不可用**(开发机是 UTF-8 locale 所以从没暴露)。已修 `src/ontology/` 全层 10 处; 存量 12 处已进 `check_portability.py` 的新 WARN 项 6 |
+| 2 | `place_raw_data.py` 一跑就 `NameError: name 'os' is not defined` | 模块级 `DEFAULT_SRC` 用了 `os.environ` 却从没 `import os` | **该脚本自 `d90da05`(路径跨平台化) 起一直跑不通**; A3 那次落位不是它干的 → 说明"写成表的落位映射"从未被真正执行过 |
+| 3 | A2 规则 `…/大部件维修记录.20240619143912557.xlsx` 从没落过盘 | 包内前缀恰好等于条目本身时, 去前缀后 `rest` 为空, 旧代码把它当"目录条目"`continue` 掉 | 单文件形态的规则**全都静默丢件**: 受害两例 = 上面那个 25.8 MB 工单源 + 本轮新加的对译表。修后按前缀的文件名落位 |
+
+> 教训: 这三个都是"跑一次就暴露、但没人跑过"的类型。**从零重算本身就是最好的回归测试** ——
+> 它不依赖任何随包产物, 因而能照出所有"靠旧产物遮掩"的失效。
+
+**顺带发现**: 落位后工单源件 79→80 张, 但总行数不变(5876)——新件的行与既有来源内容重复,
+按"累计快照/副本必须归并"的纪律合并, 脚本把这类情况逐条打了出来(不是静默丢)。
+
 ## 仍然存在 (不是代码问题)
 
 - `/local-ai/` 仍是 DOWN: 本机 Ollama 没运行, 且 `%USERPROFILE%\.ollama` 下**没有任何模型**

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

@@ -70,6 +70,26 @@
 
 机理层(厂商资料)按 A2 仍放 `data\raw\西门子4.0技术资料\`, **不在场站目录下**。
 
+**怎么把现场包落成这个样子**: `scripts\place_raw_data.py` 把"包里的哪个目录 → 落到哪"写成一张表,
+可复核、可重跑(`--dry-run` 先看计划; 同尺寸文件自动跳过, 重跑不会把十几 GB 重抄一遍)。
+
+```bat
+.venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope a2 --dry-run
+.venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope full
+```
+
+| 范围 | 落什么 | 依据 |
+|---|---|---|
+| `--scope a2`(默认) | 上表四类(`scada_10min`/`故障报警`/`风机故障记录`/`油样报告`) | A2 约定的四项数据层 |
+| `--scope mech` | `data\raw\西门子4.0技术资料\`(317 件 2.7 GB,含那份**对译表**)+ `<场站>\scada_1min\`(38 件 12.8 GB) | 本体层构建器 `kb_ingest.py` 的 `TECH` 与 `rd()` 四个文件名;`config.STATION_SUBDIRS` 声明的 `scada_1min` |
+| `--scope full` | 两组一起 | 从零重算要用的全集 |
+
+> 为什么 `scada_1min` 也属"该落的": 它写在 `config.STATION_SUBDIRS` 里, `scan_stations.py` 会把它报成
+> "缺"。但**包内没有消费者** —— 落它是补数据层完整性, 不改任何页面数值。同理, 现场包里的
+> `scada数据(如东)\`(19 个月原始通道导出)、`fastlog数据\`、`(8)…\振动分析报告\` 三种**故意不落**:
+> 前者是 `scada_10min` 的上游且无人读, 中间那种全库只有 2 处注释提到, 后一种是振动线的成品牌报告
+> 而非可再加工的测量数据 —— 理由都逐条写在脚本的 `SKIPPED` 表里。
+
 ---
 
 ## 3. 各目录的功用:谁在读、什么时候读
@@ -116,7 +136,26 @@
 | `pc_monthly_bins` · `duty_monthly` · `thermal_monthly` · `sector_power` | 趋势件/热链/扇区 | 组级产物, v0.2.0 未附构建脚本 |
 | `yaw_dynamic_monthly` · `yaw_press_monthly` · `yaw1min_liveness` · `yaw_err_clean` · `genbearing_monthly` · `mblub_monthly` | 偏航/温度/润滑面 | 同上 |
 | `structure.parquet` · `watch_channels_monthly.parquet` | 结构面/温度联动/融合 | 后者连包内注释都写着"须重建 `scripts\windscada_watch_channels_build.py`" —— 本版该脚本只产油样索引, 未含 watch_channels 月表 |
-| `objects.json`(本体)· `windcms\*` · `m5_cms_tcm\*` | 图谱/CMS/融合 | 来源是 `data\raw\西门子4.0技术资料` 与振动线, **不属于**上面四类 |
+| `objects.json`(本体) | 图谱检索、问答、维护页机理层 7 行 | 来源是厂商资料 `data\raw\西门子4.0技术资料`(**不在**场站目录下)。放进技术资料后**可重算**: `python -m src.ontology.kb_ingest` → 对象 2340 个(Doc 303 · AlarmCode 555 · WI 559 · 失效模式 150 · 维护任务 559); 另有 `python -c "from src.ontology.maintenance import refresh_params as f; f()"` → `turbine_params.parquet` 1706 条 |
+| `windcms\*` · `m5_cms_tcm\*` | CMS/融合 | 来源是**振动线 handoff(CMS 测点索引)**, 现场包里只有成品牌报告 PDF, 没有测点数据 → 仍 ⛔ |
+
+> **本体层的可重算性取决于技术资料在不在**: `kb_ingest.py` 的 `TECH = P.RAW_ROOT/'西门子4.0技术资料'`,
+> 且 `rd()` 要读 `故障处理/故障处理手册.xlsx`、`广核如海风电场西门子风机故障代码中英文对译表.xlsx`、
+> `如海故障代码表.xlsx`、`维护相关/维护作业指导书.xlsx` 四个文件 —— 缺任一个都会打印 `⚠ 源缺失` 并降级,
+> 所以 `place_raw_data.py --scope mech` 连那份不在技术资料目录里的**对译表**都单独补了一条规则。
+> 本体层的其它入口 (`populate` / `chain_ingest` / `trend_ingest` / `scenario_29`) 要从 **L1 产物**转录
+> (判级矩阵 `system_matrix`/`fusion_table`、振动 handoff、链盘 `/api/fleet`), 而 L1 那几件本身 ⛔,
+> 所以从零重算时它们要么报错退役、要么无变化 —— `populate` 实测卡在 `pitch\pitch_daily.parquet`。
+
+**从零重算实测(2026-09-11,只用 `data\raw`,不吃随包产物)**
+
+| 环节 | 结果 |
+|---|---|
+| 三门台账 | 报警 **0→39211** 行 · 工单 **0→5876** 行 · 油样 **0→404** 行(唯一源件数 80, 其中 2 张非台账表跳过) |
+| SCADA 侧 10 个构建器 | 全跑通(`loss_monthly` 3729 · `temp_bins` 24624 · `yaw_daily`/`hydraulic_accum`/`thermal_chain`/`system_aux` 各 38 台 · `curve_lenses` · `control_profile/schedule` · `stop_events` · `powercurve_dev/bins`)→ L0 仓 **16 件 1.78 MB**(随包是 118 件:差额就是 §4 第二张表那些 ⛔ 件) |
+| 本体层 | `objects.json` 1.43 MB + `retrieval_index.json` 0.62 MB + `turbine_params.parquet` |
+| 等价验收 `--verify` | `alarms` 39211/39211 **逐值完全一致**; `workorders` 8 行差异全部可归类(旧链时间解析缺陷)+ 69 行本链新增覆盖; **1 处人工项 = 油样 506→404**(那 102 行的源件不在包内) |
+
 
 ---
 
@@ -251,3 +290,4 @@ P.venv_python()   # 跨平台探测 .venv/Scripts/python.exe 或 .venv/bin/pytho
 | 2026-09-11 | **路径跨平台化**:新增 `src\paths.py` 唯一真源;清理机器绝对路径(mac 检出/TCM 主仓/写死解释器)与 20+ 处 cwd 相对路径;`serve.json` 的 python 改相对;新增静态门禁 `check_portability.py` 与回归尺子 `page_fingerprint.py`;**改造前后 10 个端点指纹逐字节一致**;补本文件 §6 产物地图与 §7 跨平台约定 |
 | 2026-09-11 | **门户拆包**:`release\portal.html` 拆成受管外壳 `release\portal_src\shell.html`(138 KB) + 不入库内嵌件 `templates\`(28 件 20.04 MB)与 `governance_sources.json`;新增 `scripts\portal_build.py`(`--extract/--verify/--check`),实测拆→装**逐字节一致**(见 §6);配套 `git rm --cached release\portal.html` |
 | 2026-09-11 | 验证闸门退出码修复:控制台 GBK 编不出 `✔` 时降级为 `?`(新增 `src\console.py`),修 `page_fingerprint` / `check_portability` / `rebuild_from_raw` / `scan_stations` / `portal_build` —— 此前"全部一致"会因 print 抛异常而**退出码 1(通过被报成失败)** |
+| 2026-09-11 | **从零重算实测** + 落位扩范围:`place_raw_data.py` 新增 `--scope a2\|mech\|full`(机理层技术资料 + 对译表 + `scada_1min`)、修"单文件规则恒不落件"与缺 `import os`(自 d90da05 起脚本跑不通)、同尺寸文件跳过;`src\ontology\` 全层 10 处文本 I/O 补 `encoding='utf-8'`(**中文 Windows 上本体层原本根本不可用**:默认 cp936 写 objects.json 崩在 `\u200b`);`check_portability.py` 新增 WARN 项 6「文本读写缺 encoding=」(存量 12 处);实测数字见 §4 末 |