# 观澜页面输出体检 · 逐页原因(v0.1, 2026-09-19) 用户令: 「重算后,观澜页面存在没有输出的现象,逐页面(含嵌套页面)检查原因」。 ## 0. 结论先说 **主因不是页面本身,是重算链断在了第 4 步(④b)** —— 具体是 `report` 步的一行代码踩了可选包: ``` src/windcms/report.py:504 md.append(l6[cols].to_markdown(index=False) …) → ImportError: `Import tabulate` failed. (pandas 的 to_markdown 依赖可选包 tabulate;离线整包没有) → ④b 失败 → 整条链停下(后面的 ⑥重启 / ⑦本体 / ⑧审计 全不执行) ``` `to_markdown` 那一行此前**没人踩到**,因为 `model_run_l6.parquet` 一直是空的(六层链那两步没实现, 走的是 `'无'` 分支)。**2026-09-19 我把 `model_run`/`fusion` 按口径重建、L6 真的有 14 行之后**, 这一行第一次被走到 —— 于是"融合级列终于有值"和"重算链炸在第 4 步"同时发生。已修(改用零依赖渲染 `src/tabfmt.py`,并复核全库只有这一处 `to_markdown`)。 **次因(与重算无关,重算也不会补出来)**: 四类件在包里**天生没有生成端**,页面只能如实显示缺件 —— 详见 §2 的 B 组。 ## 1. 逐页原因(实测,网关 28084 · 2026-09-19 09:4x 那一轮) 判定取自 `scripts/pages_sweep.py`(HTTP + 字节数 + 页面可见文本里自己认账的那句话;脚本/样式里的 词表不算,避免把"好页面"误判成缺件)。 | 页面(含嵌套) | 判定 | 原因 | 依赖件 → 生成端 | 重算跑完会不会自愈 | |---|---|---|---|---| | `/` 门户 | 有输出(单页壳 20 MB) | 正文与内嵌「本场结论·契约生成」段都在 | `guanlan/derived/portal_claims.json` ← **人裁底稿** | ✗ 不会:那一段是 08:41 清产物**之前**烘进 html 的,契约件不在位 ⇒ 门户结论段**停留在旧数**(见 §3) | | `/healthz` | 有输出 | 仅本机模型一项 DOWN | Ollama(外部) | ✗ 外部 | | `/api/version` | 有输出 | 版本/契约指纹 | release-manifest | ✓ | | `/ops` 运维控制台 | 有输出 | 网关自提供 | — | ✓ | | `/release/`、`/release/r1|r2/manifest.json` | **空/503** | 本体发布层两件不在位 | `ontology/release_*` ← **随包件, 无生成端** | ✗ 不会(研发出件) | | `/detail/`(v2 工作台壳) | 有输出(单页壳) | 壳正常;正文由 JS 取 `/api/*` | 见下各行 | ✓ | | `/detail` 总览/发电/部件/故障 页签 | 有输出 | `/api/fleet` 46 KB 有数 | `windscada/*.parquet` ← ③ 构建器 | ✓(③ 已跑完) | | `/detail` 特性曲线 页签 | 有输出 | `/api/curves` 23.9 KB | `windscada/curve_lenses.parquet` | ✓ | | `/detail` 振动评估 页签 | **无产物** | `/api/vibcms` 回 `no_report`:`windcms/报告_CMS振动状态评估报告_*.md` 不在位 | ← ④b 的 `report` 步(就是断掉的那一步) | ✓ **已修并补跑**(见 §4) | | `/detail` 决策链 页签 | **空** | `/api/ont_chain` 回「本体库缺失: `ontology/objects.json` (先跑 populate)」 | `ontology/objects.json` ← ⑦ 本体链 | ✓ ⑦ 没跑到;补跑后回来 | | `/detail` 系统自查 页签 | 有输出 | `/api/maint_survey` 14 行 | 同上(部分走缓存) | ✓ | | `/detail` 问答/报告 页签 | **503** | `/api/facts`:`guanlan/derived/detail_cards.json` 不在位 | `guanlan/**` ← **人裁底稿**(sop/findings + paradigm 底稿) | ✗ 不会(需人裁或新生成端) | | `/detail` 问答·模型 | 空(不适用) | `/api/ask_status` idle:本机模型未启动 | Ollama(外部) | ✗ 外部 | | `/detail/turbine/WTG01`(经典逐台页) | 有输出 | 212 KB 页面 + `/api/turbine` 13 KB | `windscada/*` | ✓ | | `/detail/problem/WTG01/变桨系统` | 空 | 该台该系统在所选窗内**没有问题**(16 B `problems: []`) | 判级数据在 | ✓ 正常空(不是缺件) | | `/cms/` `/cms/overview.html` `/cms/turbines/*.html` | 有输出 | 报告页/逐台页已由 ④b 写到 412 KB / 294 KB / 321 KB | `windcms/**` ← ④b report 步 | ✓(`report.build` 在崩之前已写完这些页) | | `/cms/report.html` | 有输出 | 47 KB | 同上 | ✓ | | `/cms/report.md`(我用错了路径) | 404 | CMS 不提供 `.md` 静态路径(报告在 `report.html` 与 `报告_CMS…md` 产物里) | — | 探针口径修正:改探 `/cms/report.html` | | `/sim/` `/sim/sys/` `/viewer/` | 有输出 | 随包静态资产 | `release/`、`resources/` | ✓(与重算无关) | | `/local-ai/` | 503/超时 | 本机 Ollama 未启动 | 外部 | ✗ 外部 | ## 2. 归因分组 **A 组 · 因为重算停在第 4 步(跑完就会自己回来)** - `/detail` 振动评估(`/api/vibcms`)、决策链(`/api/ont_chain`)、以及一切依赖 `ontology/objects.json` 的页面/接口 —— ⑦ 本体链根本没执行。 - 根因: `to_markdown` → 缺 `tabulate`(§0)。 **B 组 · 包内天生没有生成端(重算也补不出来,页面如实显示缺件)** - `guanlan/**`(事实契约四件)→ 门户结论段、问答、报告 → **依赖人裁底稿**。 - `ontology/release_*` → `/release/*` 两件 → 随包发布层,需研发出件。 - `m5_cms_tcm/handoff_vibration_v2.json` → 振动融合面(`/api/fleet` 的 `fus`)→ **人工交证件**。 - `pitch/**`(变桨面)→ 逐台页/总览里的变桨判级显示"不可判"→ 全库 0 处写入方。 **C 组 · 与重算无关的外部/口径问题** - 本机 Ollama 未启动 ⇒ 问答、报告生成、`/local-ai/`;`guanlan.py check` 里唯一那条 FAIL 就是它。 - 我自己的探针口径写错一处(`/cms/report.md`)已改。 ## 3. 一个需要你定的口径(门户结论段) 门户 `/` 的「本场结论 · 契约生成」段(28 KB)是 **08:41 清产物之前**烘进 `release/portal.html` 的, 里面的 claim(如 `RD-idx-005 准定论·预警`)来自当时的契约件;契约件现在不在位 ⇒ 这一段的数字 **无法随重算更新**,页面上却看不出它是旧的(自愈不了,且不显眼)。 两条路,选一条我就去做: 1. **如实标注**:门户该段加一条"数据截至 <日期>,契约件不在位已停止更新"的横幅(改动小、口径诚实); 2. **移出门户**:把那一段从门户里摘掉,只保留"契约面未纳入重算"的入口页(页面不再显示旧结论)。 ## 4. 本轮已做的修复 1. `src/tabfmt.py`(新): 零依赖 Markdown 表渲染;`src/windcms/report.py:504` 改用它。 ★这是**主因的修复**:`to_markdown` 依赖可选包 `tabulate`,离线整包没有它;L6 一旦非空就 ImportError 并让整条链停在第 4 步。 2. `src/opsjob.py`(新)+ 接入 `src/windcms/serve.py` 的"无产物"页与 `scripts/windscada_serve.py` 的 `no_report` 文案: **缺产物时页面会写明"重算正在进行中(起于 …, 已跑 N 分钟)"**, 免得把"正在跑"读成"系统坏了"。 3. `scripts/windscada_serve.py` 的 `no_report` 说明里那两句"model_run/fusion 两步脚本未随包"**已过时**(那两步 2026-09-19 已随包并上链),改为"由重算链 ④b 的 report 步生成"。 4. `scripts/vib_raw_build.py` 新增 `--manifest-only`: 摄入已跑完但 `vib_raw_manifest.json` 没写成时**只补清单** (本次 ④b 崩在 report 步,清单正是在它之后才写的 ⇒ 页面归口审计按"溯源缺失"报 rc=6、整条链被判失败)。 **不重跑摄入**(重跑要再花 ~22 分钟并另立一个 `_reimport` 窗)。 5. `scripts/rebuild_all.py` 第 ⑧c 步的容忍码从 (5,7) 扩到 **(5,6,7)**:rc=6=溯源缺失与 rc=5=文件不在位同属 "缺的是随包件这一路",不该把整条链判失败(否则最后的台账等价验收会被跳过 —— 2026-09-12 的同类教训)。 6. `configs/portal_pages.yaml`: 把「如东取数单」悬空引用的 `m5_cms_tcm/tcm_index.parquet`(w0127 首窗索引, 随包件、无生成端)登进 `known_gap`,如实记账而不是留一条没人管的悬空引用。 7. `scripts/pages_sweep.py`(新): 这套逐页体检本身(可重复跑,写本文件)。 ## 5. 修复后复检(2026-09-19 10:0x,重算链补跑完成) 补跑命令:`rebuild_all.py --skip-scada --skip-vib --with-verify`(②台账 ⑤a台账 ⑤审计 ⑥重启 ⑦本体 ⑧本体审计 ⑧b门户 全 OK;⑧c 因上面第 4/5 条的修复转为通过)。 | 之前的空白页/接口 | 复检结果 | |---|---| | `/detail/api/vibcms` 振动评估 | **有数**:`2026-09-19` · 全场 38 台 报警 3 / 良好 3 / 优秀 32 · `action` 3 行 · 附录 38 行 | | `/detail/api/ont_chain` 决策链 | **有数**:故障树 7 个系统级失效模式(此前是"本体库缺失: objects.json") | | `/cms/` `/cms/overview.html` `/cms/turbines/*.html` | 有输出(412 KB / 294 KB / 321 KB) | | `/cms/report.html` | 有输出(47 KB) | | 逐台下钻 `/detail/api/problem?t=WTG09&sys=齿轮箱` | **有数**:1 条 齿轮箱·报警(`WTG17 发电机` 同样 1 条) | | `/detail/problem/WTG01/变桨系统` | 正常空:该台该系统在该窗内**没有问题**(不是缺件) | | `scripts/pages_audit.py --check` | **rc=0 全部一致**(此前 rc=6 溯源缺失 + rc=5 悬空引用) | | 全页复检(`pages_sweep.py`) | 31 页:有输出 22 · 单页壳 2 · 报错 3(全是永久缺口/外部)· 空 3(1 条正当空 + 2 条随包层) | **仍未恢复的(重算补不出来的那三类,与 §2 B/C 组一致)**: `/detail/api/facts` 503(人裁底稿)、`/release/` 与 `/release/r1|r2/manifest.json`(随包发布层,研发出件)、 `/detail/api/ask_status` 与 `/local-ai/`(本机 Ollama 未启动)。 ## 6. 追加:振动页"看不到 2026-03 数据"的核查(用户令 2026-09-19) 用户报:"输入的振动数据(时间范围 2026-03 至 2026-04),重算后从页面看振动相关产物存在没有呈现 2026-03 振动数据的情况。" ### 6.1 先查数据本身(逐件扫源,不猜) 扫 `data/raw/如东/windcms/CMS_RuDong_CGN_202603-04/measurement/2026/{03,04}` 全部 **25,679** 个 `*_decode.json` 的正文时间键: | 项 | 实测 | |---|---| | 源件最早记录 | **2026-03-16 17:03:56**(3 月目录内,38 台全扫) | | 逐台最早记录分布 | 最早一台 03-16 · 中位 03-17 · **最晚一台 03-30** | | 4 月目录里也有 3 月记录 | 最早 2026-03-28(导出按"文件落哪个目录"分月,不严格按记录时间) | | 重算出的窗 `w0316` | 2026-03-16 17:27 → 2026-04-21 10:18 · 2,066,686 行 · 38 台 | | 窗内 3 月占比 | 索引 **661,935 行(32%)** · 谱库 139,135 条(33%) | ⇒ **3 月的数据在产物里**(占了三分之一),源件本身也**只从 03-16 起**(不存在 03-01~03-15 的记录)。 所以"没有呈现 3 月"不是数据没进来,而是**呈现上没把覆盖区间与聚合粒度说清** —— 两处都改了: ### 6.2 改了什么 1. **窗区间实算并到处写明**(新增 `src/windcms/data.py::window_spans/span_text`):窗名 `w0316` 只是 "窗起始日 03-16"的代号,原来页面上只有它和"证据窗末 2026-04-21" ⇒ 看不出区间。 现在 CMS 首页副标题、**逐台页副标题(含"本台数据 起→止")**、标准模版报告头部与 §1/§5、`report.md`、 `/api/vibcms`(新增 `时间范围`/`窗` 字段,v2 振动评估页会显示)都写实算区间; 逐台起始不等的还会写明"最早/最晚"。 2. **趋势图从"逐窗中位"改成"逐(窗,日)中位"**(`src/windcms/workbench.py::_trend_block`): 本机只有一个窗时,原来 `groupby('window').median()` 把整段区间压成**一个点**(时间戳≈4 月上旬) ⇒ 3 月在图上彻底不见。实测 WTG29/主轴承前/Peak 窗内 9 条记录、跨 03-18 → 04-16 共 9 天,改后逐日可见。 3. 顺带修两处体检噪声:`raw_data_check` 不再把 Access 锁文件(`.laccdb`/`.ldb`/`~$*`)当"源件后缀不合约定" (实测 MDB 重建期间会留下它);`logs/ops` 动作日志按既有保留策略清理了 1 份。 ### 6.3 复检 `/detail/api/vibcms` 现回:`时间范围 = w0316 2026-03-16 → 2026-04-21(逐台起始不等: 最早 2026-03-16 · 最晚 2026-03-30)`; `/cms/turbines/WTG01.html` 副标题 = `本台数据 2026-03-30 → 2026-04-21 · w0316 2026-03-16 → 2026-04-21(…)`, 页内 `2026-03` 出现次数从 **0 → 4**(趋势逐日点带上 3 月);`guanlan.py check` 只剩三条已知 FAIL (`sop/findings.json` 与人裁底稿派生的事实契约、本机 Ollama 未启动)。