# 版本记录(观澜·如东样板 v2) > 本文件由 `scripts/version_log.py` 生成,**勿手改**:事实(版本号、级别、变更)写在 > `src/version.py` 的 `HISTORY` 里,改完跑 `python scripts/version_log.py --write`。 > `guanlan.py check` 与打包器都会校验本文件与代码里的版本号是否一致。 ## 版本号规则(用户令 2026-09-17) 编号 **`v<大版本号>.<中版本号>.<小版本号>`**,例如 `v2.9.0`。 | 级别 | 含义 | |---|---| | 大版本号 | 系统解决方案、架构或核心功能改变 | | 中版本号 | 非核心功能新增、减少、修改 | | 小版本号 | 消缺完善 | **怎么改**:改动落在"解决方案/架构/核心功能" → 大 +1(中/小归 0);落在"非核心功能的新增/减少/修改" → 中 +1(小归 0);只是"消缺完善" → 小 +1;一次发布混了几类,取最高那一级 **打包文件名** = `app_guanlang_v2.9.0.zip`(规则:`app_guanlang_v<版本号>.zip`)。 版本号只写在 `src/version.py` 一行(`VERSION = '2.9.0'`);打包器、安装脚本、安装记录 `install-info.json`、自检都读它,不另设真源。 | 版本 | 日期 | 级别 | 主要变更 | 说明 | |---|---|---|---|---| | `v2.9.0` | 2026-09-21 | 中 | 时间窗真正生效(用户令):「部件问题」与「发电性能」随所选窗重算,并新增自定义起止日期窗 | 功能增减改 ⇒ 中版本 +1。要点: ① 窗口词表新增**自定义起止日期**(`YYYY-MM-DD~YYYY-MM-DD`,**含两端**):`months_of()` 取相交月、新增 `win_range()/in_win()` 供日粒度件做含端过滤;v2 顶栏加两个日期输入框 + 应用(校验 a≤b),月度件按所跨月取整、日粒度件按日精确,页上写明; ② 判级四轴按窗重算:`taxonomy.system_matrix(span=…)` 把窗透到 变桨(日粒度件切片, 精确)/偏航/蓄能/温度(走新窄仓重算);`reliability.overview(span=…)` 让部件可靠性表按窗;温度面的 ref/ref_sm 对照窗**仍固定**(季节解耦基线,跟着漂就失去对照意义); ③ 发电性能按窗:`/api/curves?win=` 七镜头按窗重算 + 7 张月度时序图按窗过滤;选到 2025-07~2025-12(限电前干净判别窗)时直接用正式产物不重算;④ 新产物 `windscada/slim10min/*`(公共列子集,不裁剪行)作**按窗重算底座**:一次全量扫≈2 分钟,把"每换一个窗重读 15 GB"降到秒级;已进重算链 ③b 并登记反向审计族表; ④b **M9 控制参数一致性**(就摆在发电性能页上)此前仍钉死 2025-H2 ⇒ `control.registry(span=…)` 按窗重算(窄仓补 `grd_wtc_ActPower_max`/`tur_wtc_GenRpm_max` 两列 ⇒ 48 列;≈0.6 s/窗,P封顶中位 4190(干净窗) → 4181.7(2026年) / 4177.5(2026Q1)),fleet 响应加 `m9_pending/win_pending` 并由页面轮询(5 s)—— 至此判级四轴 + 可靠性 + 控制参数 + 七镜头 + 7 张月度图**全部随窗**;远端两窗对比只剩 all_months/note/*_pending 三个元数据键不变; ⑤ 按窗重算一律"后台算+进程缓存"(判级≈25 s/窗、镜头≈10~45 s/窗):命中即回,未命中先回 `pending/building` 并由页面轮询,**不静默拿旧口径当新窗**;启动时后台预热 6 个预设窗(判级+默认窗镜头); ⑥ 实测(本机 38 台): 判级逐系统报警台数随窗变(变桨 15/13/10、偏航 15/11/5、齿轮箱 7/4/4 对应2026年/2025H2/2026-07),曲线图注窗=所选窗且样本量随窗变;验收: 反向审计 3548 件 0 未归类、pages_audit/detail_deps/config_audit/raw_scan(selftest,rc=0)/chain_gap 全绿;⑦ 远端部署(D:\产品\app,2026-09-21)实逮两处并已修: (a) 预热与请求各自起线程 ⇒ 同时算两份按窗中间帧,改为**重活全局串行**(`_HEAVY_LOCK`)并打印可用内存;(b) 退化窗("近30日"=2026-09只有 1 个节拍 ⇒ 比档件为空)时 `groupby().apply()` 返回空表 ⇒ `.rename("dev_K")` 抛 TypeError 把整面判级打断,改为**如实"不可判(样本不足)"**;另: 远端进程须由 Windows 服务 `guanlan` 托管(SSH 会话里起的进程会随会话关闭而死,实测全端口消失 ⇒ 已装服务并 RUNNING),远端核验: fleet 两窗 pending→就位、变桨报警 15→7 / 偏航 15→3、rel 窗随窗、curves 图注"窗 2026-07-01 ~ 2026-07-31 (随所选窗)"、v2 页含自定义起止控件 | | `v2.8.2` | 2026-09-21 | 小 | 消缺:扫描器的"变化"判据被两类**非摄入件**常年占住 —— 现场按类目交付的月度通道组库(`scada_mdb/<年>年/<月>月/<年>-<月>-<类>.mdb`)被当成"数据没人读·缺口",用户指定的按月提取件(`<场站>/scada_10min_.csv`)被当成"未归类";两者每次扫描都计一次变化 ⇒ `raw_scan --check` 永远 rc=4,`rebuild_all.py --auto` 与 raw_watch 会**每轮都以为来了新数据** | 纯消缺(无功能变化)⇒ 小版本 +1。要点: ① `CONSUME` 新增 `upstream` 口径(信息级):类目库是 10min 同台补充件的**上游**,取数层不直接读它 ⇒ 不再算缺口、不计变化,改按"上游归档件(要先转换才被消费)"列出并写清转换口径; ② 新增 `IGNORE_PATTERNS`:`scada_\d+min_\d{6}\.csv` 是按月提取/核对件(位置由用户指定、不是摄入源)⇒ 不报未归类、不计变化;③ 实测(2026-09-21 落位 7-8 月交付后):修前 `--check` rc=4 且每轮都报"2 处变化",修后 `--check` rc=0「与上次快照一致,没有新数据」,`--selftest` 通过,24 件类目库如实以信息级列出; ④ 口径写进 `docs/输入数据放置指导_v0.1.md` §4.1(含 12 组中文目录↔类目码、9 类↔主件 595 通道一一对应、float32+ISO 对齐、以及"类目库→同台补充件"的落位转换) | | `v2.8.1` | 2026-09-20 | 小 | 消缺:重算链末步「台账等价验收」在全新机器上必然 rc=5(交付包不含产物 ⇒ 没有随包基线),原先把它判成失败 ⇒ 门户上写"重算失败"(实测服务器 2.8.0 一轮 24/24 步全 OK 却整轮 rc=1) | 纯消缺(无功能变化)⇒ 小版本 +1。要点: ① `rebuild_all.py` 的 ⑧ 台账等价验收由 `tolerate=(4,)` 改成 `tolerate=(4, 5)` —— rc=5 = "找不到随包基线 ⇒ 没做验收",与 ④ 的口径对齐(④ 一直容忍 5 并写明"跳过等价验收≠通过");步名与步说明都改写清楚"rc=4 = 有需人工看的差异 / rc=5 = 没基线 ⇒ 没验收(不是失败,也不是通过)",并指路 `scripts/set_baseline.py`(用已核实的当前产物快照登记基线,之后这一项才会真比对); ② 远端复核(服务器 D:\产品\app): 该轮 24 个实质步骤全绿(①b 扫描 53.7s · ② 164s · ③ 1025s · ④ 99s · ④b 2118s · ④c 385s · ④d 41.6s · ④e 15.3s · ⑤b/⑤c/⑤a/⑤ · ⑥ · ⑦×6 · ⑦b 78s · ⑧ 7.1s · ⑧b 3s · ⑧c 9.5s),只有第 25 步判失败;且同批数据已真进产物:`temp_monthly` 21 个月、末四格 2026-06/07/**08**/09、2026-08 = 1,026 行(服务器 `scada_10min` 也有 76 件含 38 个 `WTG??-B2.csv` 同台补充件,说明步骤 B 的同台多件合并已在服务器上生效); ③ 已把修好的 `scripts/rebuild_all.py`(+ `src/version.py` / `docs/版本记录.md`)单文件部署到服务器,无需重装整包。交付包 app_guanlang_v2.8.1.zip | | `v2.8.0` | 2026-09-20 | 中 | 输入数据自动扫描识别:<安装目录>/data/raw 逐族指纹 → 发现新增/变化 → 指明该跑哪几步;重算链加 ①b 步 + `rebuild_all.py --auto`(新数据不被 --skip-* 漏掉)+ 服务侧可选看门狗 | 非核心功能新增 ⇒ 中版本 +1(无架构改动)。要点: ① 新增 `scripts/raw_scan.py`(用户令 2026-09-19「对 <安装目录>/data/raw 目录下的接入数据处理,增加自动扫描识别机制,以能发现新增数据,并纳入重算」):逐族指纹 = 件数/体积/最新落盘时间/清单摘要(`--deep` 再叠内容哈希),与快照 `outputs/<场>/_raw_scan.json` 比;rc: 0 无变化 · 4 有新增/变化 · 5 还没有基线; `--write` 记基线, `--json` 给机器读, `--selftest` 对账"族表覆盖反查族表里所有 raw 输入 + 步骤名都能在链上找到"(防两处漂移); ② 族表把每个 raw 子目录映射到链上步骤(故障报警/工单/油样→②;scada_10min→③④④c⑤b;scada_1min→④c;scada_mdb→③④④c;windcms→④b④d④e;m5_cms_tcm→④b;西门子4.0技术资料→⑦),`data/raw/<场>/` 下出现族表没有的目录则如实报"未归类、没有已知消费者"(不猜); ③ 进重算链作 **①b 步**(`--check --write`,容忍 rc=4/5,并在步说明里写清"4 = 有新数据不是失败"); ④ `rebuild_all.py --auto`:先扫一遍,新数据落在被 `--skip-scada`/`--skip-vib` 跳过的族里就**自动取消跳过**(实测: scada_10min 新增 1 件 → 计划从 22 步变 24 步并打印取消原因); ⑤ 服务侧看门狗(`scripts/service_worker.py::raw_watch`,配置 `configs/serve.json` 的 `raw_watch`,**默认关**):每 N 分钟扫一次,有新数据写 logs/service.log 并列出该跑的步;`auto_rebuild=true` 时顺手发起一次重算(走 ops 同一条路;`run/ops_job.json` 显示在跑则不重复发起)。★2026-09-20 按用户令「对 data/raw(**含嵌套子目录**)下文件的增减做到监听;重算要按该目录**最新的变化**算」逐条查证并补了三处: ⑥ 扫描改成**递归统计全部文件**(不限扩展名; 白名单命中的另记 matched, 差额单列为"信息,不计变化")+ **子目录清单进指纹**(新建空目录也发现) + 未归类改成**全树逐件**找(不再只看一层); ⑦ **产物 = 当前源件的函数**(删/换源件后, 那些行不再产出并大声报出"哪几件不在盘、少多少行、涉及哪些月"): 报警/工单/油样三个摄入器原先是"只替换本次涉及的件、其余原样保留" ⇒ 源件删了行还在; ⑧★报警台账改**键集合并集**去重(键=Name/Alarmcode/TimeOn, 归属取最窄的源件)—— 原先按"逐件累加、没新键就跳过"的贪心判快照, 实测**删一个源件反而让总行数从 39,211 涨到 56,593**(被跳过的大快照件整件摄入, 重复计数) ⇒ 既不是源集的函数也会虚增; 现在 39,211 键稳定、重复 3.6 万行如实去掉; ⑨ 振动窗: 同名窗已存在时原来一律改名 `_reimport`(被 EXCLUDE_DEFAULT 排除 ⇒ **补进来的 CMS 数据根本进不了生产集**), 现在默认**替换同名窗**(旧窗留档 `_superseded_<时分>`, 已加入排除表; 要旧行为用 `--reimport-as-new`)。★另按用户令「按你的建议执行 先 C 再 B」补完"同台多件 10min 进不来"这条链: ⑩ **步骤 C(让"放了没人读"当场可见)**:`raw_scan.py` 新增 `CONSUME` 表(逐族写清**消费者真实取数口径**),判据从"扩展名白名单没命中"改成"**全部文件里没被消费者读的**"—— 实测 `data/raw/如东/scada_10min/WTG01-B2.csv`(856 列 / 2026-08 数据)扩展名 `*.csv` 命中白名单,旧判据**永远发现不了**它;现在报成**缺口级**(计入变化 rc=4 + ★[缺口] 块写明消费者口径与件例),附件类(.rar/.jpg/厂商软件)仍只列信息;建议行也分流说明"缺口类重算也纳入不了"。`raw_data_check.py` 同步: 同台补充件不再误报"文件名不是本场机组",改为 info 行。⑪ **步骤 B(真正把新月份的 10min 件吃进来)**:`scada_source.load` 取数从"只认 `<台号>.csv`"扩到 **`<台号>.csv` + 同台补充件 `<台号>-*.csv`/`<台号>_*.csv`**:按列名对齐(新导出把类型前缀去掉了: `din_wtc_HydLevel_timeon` → `wtc_HydLevel_timeon`,实测老件 598 列里 597 列能这样对上)、按时间戳排序去重(**主件优先**,重叠条数写进 `attrs.scada_overlap` 并打印)、来路件名与各件行数写进 `attrs.scada_files/scada_rows`。实测 WTG01:主件 77,551 行 + 补充件 5,820 行 → 合并 83,353 行(重叠 18 行去重),时间范围由 2026-07-07 延到 **2026-09-01**,2026-08 的 5,819 行关键通道非空率 77%(**空白是源件自带的**:该月 144 行/日齐全但约 23% 行的通道为空,如实保留 NaN)。交付包 app_guanlang_v2.8.0.zip | | `v2.7.0` | 2026-09-19 | 中 | 远程部署消缺:振动窗发布被杀软/索引占用不再打断整条重算链(+ --publish-only 恢复通道);门户可对外监听(public_host,组件仍只本机);GBK 控制台打印不炸 | 一次消缺 + 一项非核心功能新增(对外监听)⇒ 按规则取最高一级(minor)。要点: ① ★远端实逮(服务器 D:\产品\app):索引 719 s + 谱 894 s 落进 windows\_staging_ingest 后,`staging.rename(final)` 回 `PermissionError: [WinError 5] 拒绝访问`(360 实时扫描/索引器持着刚写的 1710 件小文件的句柄,Windows 的目录改名要求树里无打开句柄)⇒ ④b 失败 ⇒ 链停在第 4 步,页面表现为「需要关注 0 台 + 融合面缺件」。修法:发布改 `_move_tree()`(整目录 rename 退避重试 → 逐件搬,rename 不行就 copy+删 → 仍锁住的逐条如实报出);并加 `--publish-only`:已算完的暂存窗可直接发布,不必白等 25 分钟重跑索引/谱; ② 用户令「观澜改为监听所有 IP」:`configs/serve.json` 增 `public_host`(空=跟 host=只本机;设 0.0.0.0=所有网卡),**只有门户网关**用它,detail/cms/viewer/sim 仍绑 127.0.0.1 ⇒ 对外只有 28084 一个入口;启动时打印对外地址并提示"页面无鉴权,请在防火墙侧限来源"; ③ 消缺: 新生成端在 GBK 控制台打印 m/s² / ⇒ 时抛 UnicodeEncodeError(远端 baseline_38 整件没出)⇒ 四个生成端加 stdout 守卫;trend_ingest 的"台账止 2024-11"过期断言改实话;CMS 三层基线卡的"健康期窗 N"重复措辞; ④ 部署口径: 用 SSH 起的服务在会话断开时会被一起收掉 ⇒ 现场改注册 Windows 服务(`guanlan`)(`scripts/service_ctl.py install`),开机自启、SCM 崩溃重启。交付包 app_guanlang_v2.7.0.zip | | `v2.6.0` | 2026-09-19 | 中 | 「所有的计算均要形成观澜的源代码」:融合面/总览页/事实契约/发布层/掩码阈值/六层链两步/变桨面/振动在升与三层基线 全部落成观澜自算;SCADA 接入兼容 CSV+MDB;交付包带输入数据目录结构(不含文件) | 功能新增为主、夹带多项消缺 ⇒ 按规则取最高一级(minor)。要点: ① 六层链 model_run/fusion 两步按口径重建(报告的"融合级"列从此有值)+ energy_share 步补上 + 峰值拾取口径定案(观澜口径,四件 *_freq_scan 形式不再复刻); ② 融合面 handoff 与总览页 windscada/index.html 由观澜自算生成(原为"包内无生成端",新机器上 /detail/v2 的"需要关注/全场状态"必空白); ③ 事实契约改为**从重算台账生成 claim**(门户结论段/问答/报告随重算刷新,/api/facts 不再 503); ④ 本体发布层 r1/r2、TCM 掩码阈值(观澜自算口径,非厂商)分别落成生成端; ⑤ SCADA 接入统一取数层兼容 CSV 与 MDB(含老库列位错位如实拒绝;37 个月库按修好的建库脚本重建并逐分片对拍); ⑥ 变桨面 pitch/** 落成生成端(10min 开关量/压力锯齿 + 1min 桨距角;零位口径按用户令**停机段/满发段分列**,并据实测反推补上**运行段同工况分档**这一真正的判据轴 —— 它能把现场确诊的 19# 单独拎出来); ⑦ 振动 component_history.json(在升/换件闭环)与 baseline_38.json(自/机群/绝对三层基线)落成生成端(窗数不足时如实空表并写明数据边界); ⑧ 打包:**带输入数据目录结构**(30 个空目录, 含 data/raw/西门子4.0技术资料)+ 放置说明文件随包, 仍不含数据文件; ⑨ 消缺: report 步踩可选包 tabulate 致整链中断、语言包闸拦下的 /v2 500、振动页看不出数据区间与"3 月数据消失"(趋势按日聚合)、账目单位错(kWh/MW)、Access 锁文件误报、日志保留策略等。交付包 app_guanlang_v2.6.0.zip | | `v2.5.0` | 2026-09-17 | 中 | 卸载闭环(uninstall.bat / uninstall.sh)+ 版本管理与打包命名规则 + 版本记录 | 二代架构(大版本 2,与 NAME 里的 v2 对齐)的第 5 次功能性发布,无消缺项;交付包 app_guanlang_v2.5.0.zip | | `v0.4.0` | 2026-09-17 | 旧编号 | 打包默认不含 输入数据/产物/日志/临时文件;无窗口启动(去掉 VBScript);统一配置与日志;页面归口审计;输入数据放置检查;安装时版本检查与服务化 | 旧编号体系;交付包 app_guanlan_v2_0.4.0.zip(= guanlan-v0.4.0_dist_20260917.zip) | | `v0.2.0` | 2026-09-01 | 旧编号 | 含产物的旧交付基线(历史上用作"包内无生成端"那批产物的补救源;该用途 2026-09-19 起已失效) | 旧编号体系;交付包 guanlan-rudong-v2_0.2.0_test_win64.zip —— ★2026-09-19 用户令从工作树清理(943 MB):① 「所有的计算均要形成观澜的源代码」落地后无生成端件 = 0(反向呼应审计 1,800 件全部 raw-derived)⇒ 它作为"补救源"的用途消失;② 该包仍留在 git 历史里(blob 可随时 git 取回),需要时用 git show 恢复即可 | ## 本次交付 * 版本:`v2.9.0`(观澜·如东样板 v2) * 交付包:`app_guanlang_v2.9.0.zip` * 服务名:`guanlan`(Windows 服务 / systemd 单元) * 安装 / 卸载:`install.bat`、`install.sh` / `uninstall.bat`、`uninstall.sh`