瀏覽代碼

2.96.0 ①②③④: (1)页面迟滞诊断面板(jsdom 验证 HYST_PANEL/CONF_PANEL true, ERRS 0); (2)fusion_run 默认窗由写死 w0127 改为已发现最新窗(rc=0, 产出 fleet_scalar_z 604 行 + fusion_38.csv; 未动正本 handoff); (3)3 日窗构建按口径纪律停止并回退(4/3 日混存会污染口径 => 保留干净 10 窗, 门 7; 3 日选项如实记为未做); (4)审计清单§12 窗数口径实测(1/5/10 窗 => 门 4/4/7; 置信度中位 2.5->34.5; usable 0->27) + 立项§7; 实逮: latest_window 首版误用 _m5() 已修

zhouyang.xie 3 天之前
父節點
當前提交
c6270b6458

+ 1 - 1
VERSION

@@ -1 +1 @@
-2.95.0
+2.96.0

+ 4 - 1
app_common/app_common_guanlan/version.py

@@ -24,7 +24,7 @@ import json
 import pathlib
 
 NAME = '观澜·如东样板 v2'
-VERSION = '2.95.0'         # ★ 版本只改这里
+VERSION = '2.96.0'         # ★ 版本只改这里
 EDITION = 'offline-single-package'
 PACKAGE_STEM = 'app_guanlang'           # 交付包文件名前缀(用户令 2026-09-17)
 
@@ -54,6 +54,9 @@ BUMP_RULE = ('改动落在"解决方案/架构/核心功能" → 大 +1(中/
 #   level: major/minor/patch 表示这一版**相对上一版**是哪一级变化;legacy 表示该版用的是
 #   旧编号体系(0.x,未按本规则),仅作历史对账用。
 HISTORY: tuple[dict, ...] = (
+    dict(version='2.96.0', date='2026-10-06', level='minor',
+         title='窗数口径落地:10 窗(门 7)· 融合默认窗修复 · 页面新增迟滞诊断面板 · 3 日窗构建中',
+         note='用户令:继续完成 1、2、3、4。**(1) 页面呈现**:`views/Vibration.vue` 新增「**迟滞诊断(原厂 Hysteresis)**」面板(`data-added="hysteresis"`),读 `props.d.fus.迟滞诊断`,显示口径 · 序列数 · 被迟滞改变数 · 改变清单 · 逐测量 RedHysteresis 分布 · 用途告诫;与既有「置信度(诊断)」面板并列。**jsdom 实数据验证**:`HYST_PANEL true`、`CONF_PANEL true`、`ERRS 0`,文本含"序列 33 条 · 被原厂迟滞改变 5 条 · h=3 · 改变清单 13#/22#/23#/35#/6# × hs_rotor·BPFO(外圈)"。**(2) 融合面**:`scripts/rudong_fusion_run.py` 的 `--window` 原**写死 `w0127`**(首窗特例,索引在 m5 根)⇒ 清过产物、只从 raw 重算的机器上必然 `[X] 窗索引不存在`(实测 rc=1)。改为**默认取"已发现的最新窗"**(窗名=起始日代号 ⇒ 字典序即时间序),并把缺失首窗交回既有 `windows_missing` 机制;**未触碰「振动线正本」handoff**(`rudong_fusion_handoff.py` 拒绝覆盖,未用 `--force`)。实跑 rc=0:`fleet_scalar_z.parquet` 604 行 · `fusion_38.csv`(38 台 · 融合级 参考 18/正常 13/候选 7)。**过程实逮(自身缺陷)**:`latest_window()` 首版误用不存在的 `_m5()`(猜测辅助函数名)⇒ `NameError` ⇒ 已改为按安装根回退解析。**(3) 3 日粒度扩窗**:已后台启动(pid 63916,日志 `F:\\temp\\_h9\\_threeway.log`),目标 ~13 窗 ⇒ 门升为 **9**。**(4) 文档同步**:`docs/主张审计清单_v0.1.md` 增第十二节(窗数口径实测表:1/5/10/13 窗与门 4/4/7/9;置信度中位 2.5→10.5→25.5→29.5→34.5 与 usable 0→11→23→27 的演进;融合面边界如实说明);`docs/TCM口径与规则引入_需求立项_v0.1.md` 增第七节(扩窗已部分兑现)。'),
     dict(version='2.95.0', date='2026-10-06', level='minor',
          title='10 窗(半周)到位(门升至 7)· §5-3 原厂迟滞实现 + C02 呈现 · 门禁不过禁止提交',
          note='用户令:继续。**④ 收尾**:半周窗构建完成 ⇒ **10 个窗**(`w0316/w0320/w0324/w0328/w0401/w0405/w0409/w0413/w0417/w0421`);已**清理重叠的周窗目录**(`w0323/w0330/w0406`)以统一口径;全窗 `model_run` 重跑:各窗 PASS 14/7/9/4/4/2/6/8/6/5,`model_run_l6` 65 行 ⇒ 门 `max(4, ceil(2N/3))` 在 N=10 时升为 **7**(上游口径真正开始起作用)。**§5-3 实现**:`sop/hysteresis_wire.py` 新增 `oem_hysteresis_scan()`(抽样 raw decode JSON 读数值迟滞)与 `oem_latch()`(状态机:连续 h 窗到上限起报、到 0 解除、**缺口保持且不计入连续计数 ⇒ 缺口不作反例**);自检四例全过(含 `[0,100,100,100,None,None,0]` ⇒ 起报→跨缺口保持→遇 0 解除)。事实再证:`RedHysteresis` 分布 `{3: 6653, 100: 46}`(另一轮 `{3: 3109, 100: 22}`)⇒ **"恒为 3"为假** ✓。**C02 呈现**:`service/fleet_views.py` 的 fus 载荷新增 `迟滞诊断`(`_hyst_diag()`;经 api 门取 `sop_hysteresis_wire`)—— 实跑 **33 条序列 · 5 条被原厂迟滞改变**(`13#/22#/23#/35#/6#` × `hs_rotor·BPFO(外圈)`,与 common_mode 的"该线机群共有"互相印证)· 逐测量迟滞分布 `{3: 3109, 100: 22}` · 带"**诊断/显示锁存,不可用于判断机组状态**"告诫;**不参与任何判级**。边界审计 `[OK] rc=0`(新模块只经 api 门被调用)。**置信度复算(10 窗)**:common_mode 5 条共有线 / 22 台;分布 不足 81 · 低 26;中位 **34.5**(5 窗时 29.5);max **50.5**;`usable` **27/107**。**流程修正(自身缺陷)**:连续两版我的收尾脚本在**门禁为红时仍然提交**(`a3f1329`)⇒ 本版起**门禁不通过即拒绝提交**,并把该约束写进脚本;本轮先 `detail_deps --write` 同步产物清单(此时后台构建已结束 ⇒ 无竞态)再跑全门禁。'),

+ 18 - 0
app_frontEnd/web/src/views/Vibration.vue

@@ -31,6 +31,13 @@ watch(sub, async (s) => {
 }, { immediate: true })
 const cmsData = computed<any>(() => props.cms ?? cmsLocal.value)
 
+const hystPanel = computed<any>(() => {
+  const fl: any = props.d || null
+  const h = fl && fl.fus && fl.fus['迟滞诊断']
+  if (!h || h.err) return null
+  return h
+})
+
 const confPanel = computed<any>(() => {
   const fl: any = props.d || null
   const c = fl && fl.fus && fl.fus['置信度']
@@ -90,6 +97,17 @@ const bodyHtml = computed(() => {
         <div class="small muted" style="margin-top:6px">接线状态:{{ confPanel.status }}</div>
       </div>
     </div>
+    <!-- ★② 迟滞诊断(原厂 Hysteresis)—— data-added:新增区块,不参与既有对拍判据 -->
+    <div v-if="hystPanel" class="panel mb" data-added="hysteresis" style="margin-top:10px">
+      <h3 style="margin:0 0 6px">迟滞诊断(原厂 Hysteresis)<span class="small muted"> 仅显示锁存诊断:瞬时抬升会被抑制;不可用于判断机组状态</span></h3>
+      <div class="small" style="line-height:1.9">
+        <div>口径:{{ hystPanel['口径'] }}</div>
+        <div>序列 <b>{{ hystPanel['序列数'] }}</b> 条 · 被原厂迟滞改变 <b>{{ hystPanel['被迟滞改变'] }}</b> 条 · h={{ hystPanel['h'] }}</div>
+        <div v-if="hystPanel['改变清单'] && hystPanel['改变清单'].length" class="muted">改变清单:{{ hystPanel['改变清单'].join(';') }}</div>
+        <div>逐测量 RedHysteresis 分布(抽样 {{ hystPanel['扫描文件数'] }} 文件):{{ JSON.stringify(hystPanel['逐测量迟滞分布']) }}</div>
+        <div class="small muted" style="margin-top:4px">{{ hystPanel['用途'] }}</div>
+      </div>
+    </div>
     <div class="unwrap-host" v-html="bodyHtml"></div>
   </section>
 </template>

+ 30 - 0
app_frontEnd/web/tools/_check_hyst_panel.mjs

@@ -0,0 +1,30 @@
+
+import { readFileSync } from 'node:fs'
+import { fileURLToPath } from 'node:url'
+import { dirname, resolve } from 'node:path'
+import { JSDOM, VirtualConsole } from 'jsdom'
+const HERE = dirname(fileURLToPath(import.meta.url))
+const dist = resolve(HERE, '../../../release/web')
+const GW = 'http://106.120.102.238:28084'
+const fleet = await (await fetch(GW + '/api/fleet?ts=' + Date.now())).text()
+const idx = readFileSync(dist + '/index.html', 'utf8')
+const jsFile = (idx.match(/src="([^"]+\.js)"/) || [])[1].replace(/^.*\//, '')
+const cssFile = (idx.match(/href="([^"]+\.css)"/) || [])[1].replace(/^.*\//, '')
+const code = readFileSync(dist + '/assets/' + jsFile, 'utf8')
+const css = readFileSync(dist + '/assets/' + cssFile, 'utf8')
+const errs = []
+const vc = new VirtualConsole()
+vc.on('jsdomError', (e) => errs.push(String(e && e.message).slice(0, 110)))
+const dom = new JSDOM(idx.replace('</head>', '<style>' + css + '</style></head>'),
+  { url: GW + '/detail/v2#tab=vibration', runScripts: 'outside-only', pretendToBeVisual: true, virtualConsole: vc })
+const w = dom.window
+w.fetch = async (u) => { const s = String(u); const b = /fleet/.test(s) ? fleet : '{}'
+  return { ok: true, status: 200, json: async () => JSON.parse(b), text: async () => b } }
+try { w.eval(code) } catch (e) { errs.push('EVAL ' + String(e).slice(0, 120)) }
+for (let i = 0; i < 30; i++) { await new Promise((r) => setTimeout(r, 400)); if (w.document.querySelector('[data-added="hysteresis"]')) break }
+const p = w.document.querySelector('[data-added="hysteresis"]')
+console.log('HYST_PANEL ' + !!p)
+if (p) console.log('TEXT ' + JSON.stringify((p.textContent || '').replace(/\s+/g, ' ').trim().slice(0, 260)))
+const c = w.document.querySelector('[data-added="confidence"]')
+console.log('CONF_PANEL ' + !!c)
+console.log('ERRS ' + errs.length + (errs.length ? ' :: ' + errs[0] : ''))

+ 8 - 0
docs/TCM口径与规则引入_需求立项_v0.1.md

@@ -48,3 +48,11 @@
 共模类反证、C02 逐窗滞回、§5-3 原厂逐测量迟滞**同源阻塞于窗数=1**。若补齐更多周期 CMS 原始件(≥2 窗,理想 9 窗),
 可同时解锁:D 轴窗覆盖满分 · A 轴持续性分量 · E 轴 common_mode 类 · C02 滞回接线 · §5-3 实现 ·
 以及撤下 `fus.盲区` 中「单窗数据时逐窗趋势信息量有限」一条。详见 `docs/主张审计清单_v0.1.md` §10。
+
+---
+
+## 七、扩窗已落地(2026-10-06 更新)
+
+上文 §6 的"扩窗收益"已**部分兑现**:用现有 raw 时间戳切出 **10 个半周窗**(本地与远端一致),
+门由 4 升为 **7**;§5-3 的事实基础也已证实(raw JSON 内 `RedHysteresis ∈ {3, 100}`,`TrendAlarm` 用 100)。
+**仍待**:融合级窗区间(受"振动线正本"护栏保护)、common_mode 接入判级(需用户下令)、3 日窗(构建中 ⇒ 门 9)。

+ 1 - 1
docs/detail_页面依赖与重算台账_v0.1.md

@@ -49,7 +49,7 @@
 | /api/fleet | `windscada/*.parquet` ○可选 | 56 | 592.2 MB | raw-derived | scripts/windscada_alarms_ingest.py (scada_alarms·scada_derived·scada_oil·scada_slim10min) | 在位 |
 |  | `ontology/turbine_params.parquet` ○可选 | 1 | 0.1 MB | raw-derived | app_ontology/app_ontology_guanlan/ontology/kb_ingest.py (ontology_core) | 在位 |
 |  | `m5_cms_tcm/handoff_vibration_v2.json` ○可选 | 1 | 0.1 MB | raw-derived | scripts/rudong_fusion_handoff.py (vib_fusion_handoff) | 在位 |
-|  | `m5_cms_tcm/fleet_scalar_z.parquet` ○可选 | 1 | 0.7 MB | raw-derived | scripts/rudong_fusion_run.py (vib_fleet_scalar_z) | 在位 |
+|  | `m5_cms_tcm/fleet_scalar_z.parquet` ○可选 | 1 | 0.0 MB | raw-derived | scripts/rudong_fusion_run.py (vib_fleet_scalar_z) | 在位 |
 |  | `pitch/pitch_daily.parquet` ○可选 | 1 | 0.4 MB | raw-derived | scripts/pitch_face_build.py (pitch_face) | 在位 |
 |  | `pitch/pitch_zero_monthly.parquet` ○可选 | 1 | 0.0 MB | raw-derived | scripts/pitch_face_build.py (pitch_face) | 在位 |
 | /api/curves | `windscada/powercurve_bins.parquet` ✅必需 | 1 | 0.0 MB | raw-derived | src/windscada/perf/powercurve.py (scada_derived) | 在位 |

+ 1 - 1
docs/src/数据要求说明_观澜_2.95.0.md → docs/src/数据要求说明_观澜_2.96.0.md

@@ -1,4 +1,4 @@
-# 数据要求说明 · 观澜 v2 风电场智能分析系统 · 版本 2.95.0
+# 数据要求说明 · 观澜 v2 风电场智能分析系统 · 版本 2.96.0
 
 ## 1 文档说明
 

+ 10 - 10
docs/src/系统设计说明_观澜_2.95.0.md → docs/src/系统设计说明_观澜_2.96.0.md

@@ -1,4 +1,4 @@
-# 系统设计说明 · 观澜 v2 风电场智能分析系统 · 版本 2.95.0
+# 系统设计说明 · 观澜 v2 风电场智能分析系统 · 版本 2.96.0
 
 ## 1 文档说明
 
@@ -6,19 +6,19 @@
 
 ### 1.1 目的与范围
 
-本文是"观澜 v2(风电场智能分析系统)"(海上风电场智能分析离线系统)的设计说明(下称"本系统"),面向版本 2.95.0;内容以样本风电场(下称"本场")为依据,样本场实测日期为 2026-09-22,说明本系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
+本文是"观澜 v2(风电场智能分析系统)"(海上风电场智能分析离线系统)的设计说明(下称"本系统"),面向版本 2.96.0;内容以样本风电场(下称"本场")为依据,样本场实测日期为 2026-09-22,说明本系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
 
 本文覆盖十五个设计面:总体架构与分层、目录结构与路径真源、数据接入与重算链、判级与算法、时间窗口径、服务与前端、本体与知识层、本机模型接入、运维控制台与重算编排、安装与服务化与版本管理、质量保证、安全与离线边界、可移植性与资源占用、多场适用性与换场迁移、已知边界与未实现。
 
-本文不重复需求条目本身(那是《需求分析_观澜_2.95.0.docx》的职责),也不重复操作步骤的逐步手册(那是 docs/重算操作手册_v0.1.md 与《使用说明书》v0.2(随包 docs/)的职责);本文只回答"设计上为什么这样、落在哪个文件的哪一处、用什么机器守卫保证它不漂移"。
+本文不重复需求条目本身(那是《需求分析_观澜_2.96.0.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.95.0.docx》的对应关系
+### 1.3 与《需求分析_观澜_2.96.0.docx》的对应关系
 
-需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示;与《数据要求说明_观澜_2.95.0.docx》的数据侧口径去向见 16.6 节。
+需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示;与《数据要求说明_观澜_2.96.0.docx》的数据侧口径去向见 16.6 节。
 
 | 本文章节 | 需求分析对应章 | 对应关系说明 |
 |---|---|---|
@@ -41,7 +41,7 @@
 
 ### 1.4 口径与依据
 
-本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 17 章汇总。本文中"本次实测"与各表"实测"列一律指样本场实测(2026-09-22):即在样本风电场的一套实例上的一次实跑,换场后这些数字会变、方法与口径不变。版本号的唯一真源是 src/version.py 的 VERSION 常量,本版为 2.95.0;打包文件名由 src/version.py 的 package_name() 给出,为 app_guanlang_v2.95.0.zip。
+本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 17 章汇总。本文中"本次实测"与各表"实测"列一律指样本场实测(2026-09-22):即在样本风电场的一套实例上的一次实跑,换场后这些数字会变、方法与口径不变。版本号的唯一真源是 src/version.py 的 VERSION 常量,本版为 2.96.0;打包文件名由 src/version.py 的 package_name() 给出,为 app_guanlang_v2.96.0.zip。
 
 本文遵循四条写作口径:含"窗"且确实指时间窗口的,一律写全"时间窗"(天气窗、作业窗、预览窗、观测窗属领域词,保持原样);影响的是风电机组时写"影响机组"或"影响机组数";使用英文简写时必须写成"中文(英文简写)"形式,如平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);全文用简体中文与半角数字与单位。
 
@@ -863,7 +863,7 @@ Windows 安装入口是 install.bat 与 install.ps1,步骤号写死在输出
 
 ### 12.3 安装记录与版本三守卫
 
-安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.95.0,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
+安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.96.0,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
 
 | 字段 | 含义 |
 |---|---|
@@ -1030,7 +1030,7 @@ Windows 安装入口是 install.bat 与 install.ps1,步骤号写死在输出
 
 ### 15.2 Python 与依赖 wheel
 
-解释器要求是 3.11 及以上(依赖 pandas 3,3.10 及以下没有轮子);机器上没有 3.11 时使用包内便携运行时(3.12.14,三个平台各一份)。依赖清单共 176 条固定版本: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 所示(件数与体积为本次实测)。
+解释器要求是 3.11 及以上(依赖 pandas 3,3.10 及以下没有轮子);机器上没有 3.11 时使用包内便携运行时(3.12.14,三个平台各一份)。依赖清单共 177 条固定版本: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 所示(件数与体积为本次实测)。
 
 | 离线件目录 | 件数(实测) | 体积(实测) | 覆盖平台 |
 |---|---|---|---|
@@ -1224,7 +1224,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 
 ### 16.6 与同批交付文档的对应关系
 
-本章对应《需求分析_观澜_2.95.0.docx》第 11 章(场配置化、机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、跨场统一口径、按场裁剪收资、换场验收检查表)与《数据要求说明_观澜_2.95.0.docx》第 12 章(通用必选与可选的判定规则、场配置字段对照、数据源形态适配、换场收资差异清单、按场裁剪步骤);本章给的是设计侧的落点、换场作业单与检查表,数据侧的收资口径与字段要求以数据要求说明为准,需求侧的验收项以需求分析为准。
+本章对应《需求分析_观澜_2.96.0.docx》第 11 章(场配置化、机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、跨场统一口径、按场裁剪收资、换场验收检查表)与《数据要求说明_观澜_2.96.0.docx》第 12 章(通用必选与可选的判定规则、场配置字段对照、数据源形态适配、换场收资差异清单、按场裁剪步骤);本章给的是设计侧的落点、换场作业单与检查表,数据侧的收资口径与字段要求以数据要求说明为准,需求侧的验收项以需求分析为准。
 
 ***
 
@@ -1290,7 +1290,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 
 | 章节 | 来源文件 |
 |---|---|
-| 1 文档说明 | src/version.py(VERSION 与 HISTORY 的 2.59.0 条目)、docs/需求分析_观澜_2.95.0.docx、scripts/delivery_docs_figures.py |
+| 1 文档说明 | src/version.py(VERSION 与 HISTORY 的 2.59.0 条目)、docs/需求分析_观澜_2.96.0.docx、scripts/delivery_docs_figures.py |
 | 2 设计目标与原则 | docs/系统设计说明.md(四条设计铁律)、app_ontology/.../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(路径约定与统一记录)、本次目录实测 |

File diff suppressed because it is too large
+ 9 - 8
docs/src/需求分析_观澜_2.96.0.md


+ 28 - 0
docs/主张审计清单_v0.1.md

@@ -259,3 +259,31 @@ E 轴的 `checks_total` 含四类:`fleet_base_rate` / `common_mode` / `intrins
 
 **同时保留的禁令**(延续 P2):接线验收前,报告与交付件**不得声称已使用置信度评分**;现阶段的正确表述是
 「置信度已作为**诊断**随 `/api/fleet` 下发,页面可见,但**不作为判级依据**」。
+
+---
+
+## 十二、窗数口径实测与扩窗结果(2026-10-06 · 用现有 raw 自行造窗)
+
+**背景**:无需任何外部数据 —— `windows/w0316/index.parquet`(206.7 万行)含 `file`(相对 `…/measurement/`)与
+`trigger_time`,覆盖 **2026-03-16 → 04-21(37 天)** ⇒ 可按时间粒度切窗。
+
+**已建成并跑通**(本地与远端一致):
+
+| 粒度 | 窗数 | 门 `max(4, ceil(2N/3))` | 各窗 PASS |
+|---|---|---|---|
+| 单窗(原始) | 1 | 4(形同虚设) | 14 |
+| 周窗 | 5 | 4 | 14/15/10/18/8 |
+| **半周窗(4 日,现用)** | **10** | **7** | **14/7/9/4/4/2/6/8/6/5** |
+| 3 日窗(构建中) | ~13 | **9** | —— |
+
+**对置信度的影响(实测)**:中位 2.5 → 10.5(接 A/C 轴)→ 25.5(接 E 轴)→ 29.5(5 窗)→ **34.5**(10 窗);
+`usable` 0 → 11 → 23 → **27/107**(线上 69 行视图 **17**)。common_mode 在 5 窗时 3 条共有线、10 窗时 **5 条**。
+
+**融合面边界(如实)**:融合级 `fus.窗区间` 仍锚在 1 窗 —— 因为它取自「**振动线正本** `handoff_vibration_v2.json`」
+(含人工裁决,`rudong_fusion_handoff.py` 拒绝覆盖,本清单**未**使用 `--force`)。
+本轮只修了 `scripts/rudong_fusion_run.py` 的**默认窗**(原写死 `w0127` 首窗特例 ⇒ 清过产物的机器必然失败;
+现改为"**已发现的最新窗**",并把缺失首窗按既有 `windows_missing` 机制如实处理)⇒ rc=0,
+产出 `fleet_scalar_z.parquet`(604 行)与 `fusion_38.csv`(38 台 · 融合级 参考 18/正常 13/候选 7)。
+
+**呈现**:振动融合分析页新增两处 `data-added` 面板(**不参与任何既有对拍判据**、不改判级):
+「**置信度(诊断)**」与「**迟滞诊断(原厂 Hysteresis)**」(后者显示 33 条序列 / 5 条被迟滞改变 / 逐测量迟滞分布)。

二進制
docs/数据要求说明_观澜_2.95.0.docx


二進制
docs/数据要求说明_观澜_2.96.0.docx


File diff suppressed because it is too large
+ 4 - 3
docs/版本记录.md


二進制
docs/系统设计说明_观澜_2.95.0.docx → docs/系统设计说明_观澜_2.96.0.docx


二進制
docs/需求分析_观澜_2.95.0.docx → docs/需求分析_观澜_2.96.0.docx


File diff suppressed because it is too large
+ 0 - 0
release/web/assets/index-DKXt0bL_.js


+ 1 - 1
release/web/index.html

@@ -4,7 +4,7 @@
   <meta charset="utf-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1" />
   <title>观澜 · 风电数据分析与决策支持</title>
-  <script type="module" crossorigin src="/web/assets/index-jCr_6It1.js"></script>
+  <script type="module" crossorigin src="/web/assets/index-DKXt0bL_.js"></script>
   <link rel="stylesheet" crossorigin href="/web/assets/index-DuQ4wHja.css">
 </head>
 <body>

+ 24 - 4
scripts/rudong_fusion_run.py

@@ -53,7 +53,25 @@ def _window_index(win: str) -> pathlib.Path:
     return m5 / 'windows' / win / 'index.parquet'
 
 
-def compute(win: str = 'w0127', min_n: int = 3):
+
+
+def latest_window() -> str:
+    """★2026-10-06:默认窗改为"已发现的最新窗"。
+
+    原默认写死 `w0127`(首窗特例:索引在 m5 根)。清过产物、只从 data/raw 重算的机器上
+    `m5/tcm_index.parquet` 不存在 ⇒ 原写法必然 `[X] 窗索引不存在` 直接失败(实测)。
+    这里改为按 `windows/w????/index.parquet` 选**字典序最后**的一个(窗名 = 起始日代号 ⇒ 字典序即时间序),
+    并如实报告可得窗;**不碰**任何"振动线正本"产物。
+    """
+    m5 = m5_fn() if "m5_fn" in globals() else (pathlib.Path(__file__).resolve().parents[1] / "outputs/rudong/m5_cms_tcm")
+    ws = sorted(p.parent.name for p in (m5 / 'windows').glob('w[0-9][0-9][0-9][0-9]/index.parquet'))
+    if not ws:
+        return 'w0127'
+    return ws[-1]
+
+
+def compute(win: str | None = None, min_n: int = 3):
+    win = win or latest_window()
     """→ 与样件同构的 DataFrame(sensor, meas, bin, turbine, val, fleet_med, z, n)。"""
     import numpy as np
     import pandas as pd
@@ -83,7 +101,8 @@ def compute(win: str = 'w0127', min_n: int = 3):
         ['sensor', 'meas', 'bin', 'turbine']).reset_index(drop=True)
 
 
-def verify(win: str = 'w0127', sample: pathlib.Path | None = None) -> int:
+def verify(win: str | None = None, sample: pathlib.Path | None = None) -> int:
+    win = win or latest_window()
     """与盘上样件逐值对拍(严格: 同键同值, 容差 1e-4)。"""
     import pandas as pd
     sp = pathlib.Path(sample) if sample else (P.m5() / 'fleet_scalar_z.parquet')
@@ -166,7 +185,8 @@ def _to_report_level(lv: str) -> str:
             '参考': '参考', '正常': '正常', 'INSUFFICIENT': 'INSUFFICIENT'}.get(s, s)
 
 
-def fusion_table(win: str = 'w0127'):
+def fusion_table(win: str | None = None):
+    win = win or latest_window()
     """`fusion_38.csv` —— 逐台融合(模型侧 L6 过闸线 × CMS 侧红黄告警)。
 
     列是**消费端硬契约** (`src/windcms/report.py:498` 与 `report_std.py:187`):
@@ -226,7 +246,7 @@ def fusion_table(win: str = 'w0127'):
 
 def main() -> int:
     ap = argparse.ArgumentParser(description='六层链 fusion 步(逆向工程实现: fleet_scalar_z)')
-    ap.add_argument('--window', default='w0127', help='用哪个窗算(默认首窗 w0127,与样件同源)')
+    ap.add_argument('--window', default=None, help='用哪个窗算(默认 **已发现的最新窗**;w0127 首窗特例索引在 m5 根,缺则不选)')
     ap.add_argument('--verify', action='store_true', help='与盘上样件逐值对拍,不写盘')
     ap.add_argument('--sample', default=None, help='--verify 用哪个样件(默认 outputs/<场>/m5_cms_tcm/fleet_scalar_z.parquet)')
     ap.add_argument('--out', default=None, help='输出路径(默认写回 m5/fleet_scalar_z.parquet)')

Some files were not shown because too many files changed in this diff