系统设计说明.md 177 KB

观澜 · 如东样板 v2 · 系统设计说明

版本 v0.2.0 · 2026-09-16 · 适用 <安装目录>(安装目录) 本文是系统设计正本: ① 产物全景(类型/功用/输出路径/生成端/消费端);② 路径与进程的统一约定; ③ 输入 ↔ 产物呼应关系;④ 重算的两种运行状态与"产物即时进页面"的实现;⑤ 启动与门户。 与本文配套、可复跑的机器校验: python scripts/inventory_products.py(清点 + 呼应校验,--check 出退出码)。


1. 系统是什么(一页)

现场数据 data/raw/<场站>/  ──摄入──▶  outputs/<场>/(产物仓)  ──现读──▶  页面/接口
     ▲  7 类约定目录                        ▲  9 个产物仓                  ▲
     │  scada_10min / scada_1min /           │  windscada  L0 标准仓        │  网关 :28084(门户/运维控制台)
     │  故障报警 / 风机故障记录 /             │  ontology   本体对象库        │  detail :18033(分析工作台)
     │  油样报告 / windcms / m5_cms_tcm      │  windcms   CMS 振动           │  cms    :18020(振动诊断)
     └─ 西门子4.0技术资料(机理层,不在场站目录下)│  m5_cms_tcm 振动线出件/窗     │  sim :18791 · sim_sys :18792 · viewer :64292
                                            │  tcm_compatible_replay      │
                                            │  sop / guanlan / pitch / paradigm_r1

四条设计铁律(贯穿全部代码,改代码时先读这四条):

  1. 路径唯一真源 src/paths.py —— 代码/配置里只写 ROOT 相对路径,运行期由助手解析成绝对路径(基准是安装根,不是 cwd)。
  2. 产物只落在 outputs/<场>/;原始件只读 data/raw/<场站>/;release/ 是交付静态层。三者不互相写。
  3. 判断在代码、模型只转述;每个产物都要能说清"谁生成、谁消费、来源是 raw 重算还是随包补齐"(_provenance.json)。
  4. 窗口/进程统一口径 src/proc.py —— 子进程一律 CREATE_NO_WINDOW,日志落 logs/,不弹命令窗口。

2. 产物全景(自动生成,勿手改)

由 python scripts/inventory_products.py --write-doc 生成;数据源是盘上的实际文件 + outputs/<场>/_provenance.json(逐件来源台账)+ outputs/<场>/_derived_manifest.json(构建脚本自登记)。

自动生成于 2026-09-19 16:07;数据源: outputs/rudong/_provenance.json + _derived_manifest.json + 实际文件

产物仓 输出路径 (相对安装根) 功用 生成端 消费端 件数 大小 来源(raw 重算/随包)
windscada outputs/rudong/windscada/ L0 标准仓: SCADA/台账/派生分析的全部 parquet (页面主取数处) rebuild_from_raw.py (三门台账) + --scada (10 个构建器) + windscada_monthly_build.py scripts/windscada_serve.py 各视图 · src/windscada/taxonomy · subsys/fusion 18 6.9 MB 18 / 0
ontology outputs/rudong/ontology/ 本体对象库: 码表/手册/工单展开/失效树 + 检索索引 + 实机参数 python -m src.ontology.kb_ingest → populate → chain_ingest → trend_ingest → retrieval.build 脚本 windscada_serve.py 本体页/问答 · scripts/guanlan_facts_contract.py 8 14.9 MB 8 / 0
windcms outputs/rudong/windcms/ CMS 振动诊断产物: 状态评估报告/逐台页/工作台页/知识库 + 厂家报告转录 scripts/windcms.py report/kb · scripts/vib_reports_build.py 自服务 :18020 (每请求现读) · taxonomy.system_matrix (转录设备状态) 47 15.2 MB 47 / 0
m5_cms_tcm outputs/rudong/m5_cms_tcm/ 振动线出件与窗级分析: handoff 接口 + 窗索引/谱库 + TCM 兼容件 scripts/vib_raw_build.py (窗索引/谱) · 振动线出件 (handoff, 随包快照) src/windscada/subsys/fusion.py · src/windcms/data.py · scripts/windcms.py report 1713 3278.9 MB 1713 / 0
tcm_compatible_replay outputs/rudong/tcm_compatible_replay/ TCM 兼容链回放资产 (模型表/掩码阈值/裁决记录) 随包快照 (无生成端) src/windcms/report*.py · config.mask_thresholds 2 0.6 MB 2 / 0
sop outputs/rudong/sop/ SOP 中间件/评审/台账与事实契约底稿 随包快照 (无生成端) scripts/guanlan_facts_contract.py · 门户结论段 1 0.0 MB 1 / 0
guanlan outputs/rudong/guanlan/ 事实契约与对外派生 (可上云面孔) scripts/guanlan_facts_contract.py 门户 #findings · /api/facts 5 0.0 MB 5 / 0
pitch outputs/rudong/pitch/ 变桨侧派生件 (日粒度液压/润滑/柱塞 + 零位三口径月表) scripts/pitch_face_build.py (重算链 ④c; scada_10min + scada_1min) 脚本 windscada_serve.py 变桨面 · src/windscada/subsys/pitch.py 3 0.4 MB 3 / 0
paradigm_r1 outputs/rudong/paradigm_r1/ 范式实验件 (E3/E5/E8 底稿, 事实契约输入) 随包快照 (无生成端) scripts/guanlan_facts_contract.py 3 0.0 MB 3 / 0

合计 1800 件 / 3316.8 MB;其中 raw 重算 1800 件、随包补齐 0 件。

2.1 逐件来源口径(_provenance.json)

来源 含义 判据
raw-derived 由 data/raw 重算出来的 该件在 RAW_DERIVED 表里,或由构建脚本自登记(src/derived_manifest.py)
shipped 包内没有生成端 / 规则未复现,用随包件补齐 既不在上表、也无自登记,且件在随包快照里存在

自登记(2026-09-16 新增): 振动侧的产物名随"窗名/分片序号"变化,写不进精确路径表,而按名字通配会误伤 同名旧件(报告_CMS振动状态评估报告_*.md 既有随包/自产的、也有厂家报告转录的)。改成谁算的谁登记: vib_raw_build.py / vib_reports_build.py 落盘后把相对路径写进 _derived_manifest.json, products_restore_missing.py 生成台账时据此记为 raw-derived。漏登记可用 python scripts/vib_raw_build.py --register-only 幂等补登记。


3. 路径约定(唯一真源 src/paths.py)

3.1 全部路径助手(写新代码时只用这些)

助手 返回 用途
P.ROOT <安装目录> 一切解析的基准(WINDSCADA_ROOT 可覆盖,冻结构建时由启动器设)
P.RAW_ROOT data/raw 现场原始件根(WINDSCADA_RUDONG_SRC / serve.json.raw_dir 可覆盖)
P.station_dir(name) data/raw/<场站名称> 兜底约定位置(权威值来自 config.raw_station_dir() 的扫描辨识)
P.out_root(name) outputs/<场> 产物仓根
P.store(name) outputs/<场>/windscada L0 标准仓(页面主取数处)
P.ont(name) outputs/<场>/ontology 本体对象库
P.objects_json(name) …/ontology/objects.json 对象库文件
P.cms(name) outputs/<场>/windcms CMS 振动诊断产物
P.m5(name) outputs/<场>/m5_cms_tcm 振动线出件 / 窗索引 / 谱库
P.tcm_replay(name) outputs/<场>/tcm_compatible_replay TCM 兼容链回放资产
P.sop(name) outputs/<场>/sop SOP 中间件与评审落盘
P.guanlan(name) outputs/<场>/guanlan 事实契约与对外派生
P.pitch(name) outputs/<场>/pitch 变桨侧派生件
P.paradigm(name) outputs/<场>/paradigm_r1 范式实验件(E3/E5/E8 底稿)2026-09-16 新增
P.report_dir(name) outputs/<场>/report 报告交付件(交接单/现场单)2026-09-16 新增
P.cloud(name) outputs/<场>/guanlan/cloud 可上云面孔(脱敏后的契约/派生/页面)2026-09-16 新增
P.contract(name) reference/<场>/windscada_contract.yaml 机型判据契约(属 reference 侧,不在 outputs)
P.rel(p) / P.disp(p) / P.disp_dir(p) 相对 POSIX 串 / 显示串 / 目录显示串 写进产物用 rel();给人看(页面「位置」列)用 disp()
P.resolve(p) 绝对路径 把"可能是相对"的值按 ROOT(不是 cwd)解析
P.venv_python() / P.python_exe() 解释器路径 跨平台探测 .venv/Scripts/python.exe 或 .venv/bin/python

3.2 2026-09-16 统一掉的路径问题(源码已改)

# 位置 原样 改成 为什么要改
1 scripts/products_restore_missing.py P.STORE if hasattr(P,'STORE') else ROOT/'outputs'/'rudong' P.out_root() P.STORE 不存在 ⇒ 恒落到写死的 rudong;多场部署会把 rudong 的随包件补进别的场(静默串场)
2 scripts/ingest_ops_2025.py pathlib.Path('outputs/rudong/ontology/objects.json') + 裸 write_text _P.objects_json() + Store(…).save() cwd 相对 + 写死场名 + 绕过 store 的全库校闸与原子写;且该脚本缺 import os 一直 NameError,属"上膛但没响"的凶器
3 scripts/ontology_p2_verify.py ROOT/'outputs/rudong/ontology/objects.json'、…/guanlan/facts_contract_v0.json P.objects_json()、P.guanlan() 写死场名
4 scripts/guanlan_facts_contract.py 4 条 ROOT/"outputs/rudong/…" P.guanlan()/P.sop()/P.paradigm() 同上(事实契约的输入/输出全在这里)
5 src/ontology/maintenance.py 自写 ROOT=parents[2]、_RAW_STR、disp() P.ROOT / P.RAW_ROOT / P.disp 同一文件里三处"影子真源"(显示规则两个实现)
6 src/sop/wrapup.py …/cleaned/turbine.parquet(硬编码) 读 clean_gate.json 的 section → 否则取目录内唯一 parquet → 都取不到则响亮打印 写侧落的是 cleaned/<section>.parquet(section 可变)⇒ 读侧硬编码时静默 None,柱2 悄悄降级 INSUFFICIENT
7 src/windscada/subsys/pitch.py P.store()/'alarms.parquet' cfg['store'](registry(cfg) 透传) P.store() 跟环境变量,cfg['store'] 跟显式选定的场 ⇒ 多场下跨场串数据且不报错
8 scripts/check_transferable.py 门户期望字节数写死 20225828 运行时从主实例现量 门户外壳一改常量即过期,核验会打印永远不成立的"与主包不同"
9 src/windscada/taxonomy.py、subsys/fusion.py、scripts/windscada_serve.py 读侧硬绑 P.cms() 新增 windcms.config.cms_out(),读侧与写侧同一解析口 WINDCMS_OUT 只有写侧认 ⇒ 设了它就"写到旁路、页面读生产",表现为"重算完了页面还是旧数"且不报错(冻结构建自检正是这个组合)
10 src/windcms/pipeline.py::ingest 任何 .zip/.rar/.7z 都调 rudong_tcm_ingest_raw.py(未随包) 先判包内容:含 *_decode.json → 按"已解码导出"走 rudong_tcm_index/spectra(支持 zip 当 root);否则报明确错误并给两条可行路径 把 CMS 导出 zip 直接指过来时原本必崩 FileNotFoundError,而其实不需要那个脚本

3.3 仍然存在的不统一(如实列出,未擅自大改)

类别 具体 影响 处置建议
一次性/历史脚本自拼路径 scripts/guanlan_cloud_{face,page,qa}.py、guanlan_portal_inject_claims.py、guanlan_baseline_manifest.py、llm_known_answer.py 里仍有 outputs/rudong/… 字面量 只影响这些一次性派生/云端面孔脚本;多场时需手改 换场前把这几处改走 P.guanlan()/P.cloud()/P.store()(P.paradigm()/P.report_dir()/P.cloud() 已备好)
历史随包留档 scripts/sim_hub/*(含 /Users/yuanying/... mac 路径)、outputs/rudong/sop/*.py 非运行期(装配脚本留档) 已在 check_portability.py 具名登记;随包只为"可重放",不参与运行
多场机制未接通 src/windscada/config.py::available() glob configs/farms/*.json,而磁盘上 11 个场配置是 *.yaml 现在只有内置 rudong 可用;cfg['store'] 恒等于 P.store('rudong') 二选一:把 available() 同时认 *.yaml,或把场配置转成 json
store seam 三种写法 cfg['store'](14 处写点)/ P.store()(1 处,已修)/ ROOT/'outputs'/…(4 处,已修 3 处) 多场前无实际差异,属"潜在缺陷" 新代码一律 cfg['store'] 或 P.store(name)
配置/依赖指向不存在的件 configs/scenario_registry.yaml、configs/<场>/value_assumptions.yaml、configs/analysis_lock*.yaml、scripts/sop_check.py、scripts/analysis_lock_check.py、docs/审核规则_经验固化_v1.md、docs/振动诊断模型_六层_v1.md、.claude/skills/…、m5_cms_tcm/model_run_l6.parquet、m5_cms_tcm/fusion_38.csv SOP 场景模块读注册表必抛;锁闸恒空转;wrapup 电价只能走"假定锚";CMS 报告不能重生成(会掉内容) 见 §7 缺口表;这批是"随包没发全",要么补件要么显式降级(wrapup 已改为响亮说明)
被引用但未随包的脚本(10 个) rudong_tcm_ingest_raw.py(已加前置判断,见 §3.2-10)、rudong_tcm_oem_scan.py、rudong_line_energy_share.py、rudong_model_run.py、rudong_fusion_run.py、rudong_build_baseline.py、dsh_learn_page.py、intake_scan.py、guanlan_cloud_sims.py、deploy_gate_check.py 前五个属振动六层链(见 §7);其余是开发/部署辅助脚本的引用 逐个二选一:补件,或在调用处加"缺失即明确报错 + 替代路径"(pipeline.ingest 已按此改)
有读无写的"孤儿产物"(12 件) genbearing_monthly / mblub_monthly / yaw_dynamic_monthly / yaw1min_liveness / yaw_err_clean / sector_power / duty_monthly / pc_monthly_bins / thermal_monthly / control_monthly / structure.parquet / watch_channels_monthly 趋势件、热链、扇区、偏航/润滑/控制面 维持随包件;要"自己算"须研发给口径(照 temp_monthly 的办法反推 + 逐值验证)
有意重复落盘(不是缺陷,但要知道) ① spectra_meta.parquet 同时写 <窗>/spectra/ 与 <窗>/(约 4.6 MB/窗)—— 为兼容 data.spectra_meta() 的两条读取约定;② 报告_CMS振动状态评估报告_<期>.md 有两个来源(report_std.py 自算 vs vib_reports_build.py 厂家转录),靠日期口径区分(自算=当天、转录=报告期月末) 磁盘占用翻倍(仅 meta,非谱数据);命名空间共享 已在此登记;data.py 优先读 <窗>/spectra_meta.parquet,两份内容逐字节一致
P.VIEWER 常量全仓 0 引用 src/paths.py 定义了 VIEWER,实际取 viewer_dir 走 configs/serve.json 无功能影响 下次统一时二选一(用起来或删掉),避免"看着像真源其实没人用"

4. 进程与命令窗口(统一口径 src/proc.py)

问题: Windows 上"无控制台的父进程 + 裸 spawn 一个控制台程序" = 系统给子进程新建一个可见控制台窗口。 本系统里无控制台的父进程很多(网关、运维动作进程),于是"点页面按钮"会闪窗、跑重算会挂一个 20 分钟的黑窗、 /ops 每 2 s 轮询 tasklist 会反复闪窗。

统一写法: src/proc.py

API 用途
proc.NO_WINDOW / proc.flags() CREATE_NO_WINDOW(Windows)/ 0(POSIX)。★与 DETACHED_PROCESS 互斥,只认这一种
proc.spawn(cmd, log=…, env=…, cwd=…, **popen_kw) 后台起无窗口子进程;log= 落日志,也可自己传 stdout=/stderr= 句柄(透传,不覆盖)
proc.run(cmd, **kw) / proc.run_text(cmd) 前台等待的无窗口子进程(tasklist / git / 短命令);run_text 默认 text=True, errors='replace'(控制台输出是 GBK,严格解码会得到 None)

已收敛的调用点(凡"父进程无可见控制台"的入口都必须走这里): guanlan.py(组件 spawn、tasklist、taskkill)、 scripts/_ops_launch.py、scripts/_ops_run.py(整套重算的宿主进程)、scripts/guanlan_ops.py(启动器调用 + 每 2 s 的 tasklist 轮询)、 scripts/guanlan_gateway.py(git,每次 /api/version)、scripts/_ops_start_and_open.py(起 serve)、 src/windcms/plugins.py(CMS 页面「分析」按钮起的全链)、scripts/windscada_serve.py(详情工作台云端档问答)。

判定口径(新加子进程前先问一句): 我的父进程有没有可见控制台?有(用户从终端跑的 CLI)→ 让它继承,用户能看见进度; 没有(服务/网关/运维动作进程)→ 必须走 src/proc.py,否则 Windows 会给子进程新建一个可见窗口。

4.1 三个"静默故障"坑(都实炸过,已写进 guanlan.py check 作回归守卫)

# 坑 现场表现 处置
1 CREATE_NO_WINDOW 新建的控制台会把子进程标准句柄吸走 从页面点"执行重算":_ops_run 自己那行进了日志,rebuild_all.py 及之后一个字都没有,任务随后挂住(0 CPU / 0 I/O / 1 线程);页面只剩"运行中" src/proc.py::_inherit_stdio() 把父进程当前 sys.stdout/stderr 显式交给子进程(STARTF_USESTDHANDLES)
2 capture_output=True 与显式 stdout= 互斥,同给抛 ValueError 异常被 guanlan_ops.job_running() 的 except 吞成 alive=False ⇒ 任何在跑的任务都被立刻改写成"被强杀",页面显示完成、重算按钮重新可点(可能并发起两个重算) _inherit_stdio 先让路(有 capture_output 就什么都不加);自检加一条"run_text(capture_output) 可用"
3 内嵌版 /ops/recalc 裁掉了「服务」卡,但 JS 仍 $('#b_start').onclick=… 元素为 null → 抛 TypeError → 后面所有按钮绑定与 refresh() 全不执行 ⇒ 用户看到的正是"清除产物/执行重算点了没反应" 所有元素访问与绑定走 set()/on() 容错包装;并加 try/catch 把脚本错误显示在页面上(不再静默)

服务端配套: /ops* 响应现在带 Cache-Control: no-store, must-revalidate —— 否则页面 HTML/脚本更新后浏览器仍用旧版, 表现同样是"改了没反应"(guanlan_gateway.py::_bytes(no_store=True))。

4.2 用户怎么启动(不弹命令窗口)

★ 2026-09-16 用户令"把 VBScript 替换掉":原先靠 start_hidden.vbs(WScript.Shell.Run(..., 0, False)) 隐藏启动。VBScript/Windows 脚本宿主(WSH)已被微软列为弃用特性,且服务器上常被组策略整体禁用, 所以现在包里没有任何 .vbs,无窗口启动由 pythonw.exe(无控制台子系统的解释器)+ Python 启动器承担:

入口 行为 窗口
启动观澜.lnk(推荐;install.ps1 安装时生成,根目录一份 + 桌面一份) 快捷方式直接指向 .venv\Scripts\pythonw.exe + scripts\guanlan_start_hidden.py(工作目录=安装根,窗口样式 7) 一个窗口都不出现(不经过 cmd.exe,也不经过 WSH)
start.bat(双击) 默认 start "" /min ".venv\Scripts\pythonw.exe" "scripts\guanlan_start_hidden.py" 会闪一下(.bat 是控制台程序,Windows 必定先开窗口,脚本内无法消除);start.bat console = 旧前台模式(排障看实时日志,关窗=停服务)
scripts/guanlan_start_hidden.py 启动器本体:端口已在 → 只开浏览器(幂等);否则无窗口起 guanlan.py serve、等 /healthz、开浏览器 无窗口(--selftest 自证:GetConsoleWindow()==0)
运维控制台/门户「数据重算」 动作进程同样无窗口;进度与日志尾巴在页面里看 无窗口

日志去哪了: logs/serve.log(无窗口模式下 serve 的输出)、logs/start_hidden.log(启动器轨迹:窗口状态/就绪耗时/失败原因/是否已开浏览器)、 各组件 logs/<name>.log、运维动作 logs/ops_<动作>_<时间>.log。失败时启动器还会弹一个消息框(无窗口模式下唯一能让人看见的通道)。

实测(2026-09-16): 全停后可见窗口 0;start.bat 2.3 s 返回;启动后可见窗口 0,6 个服务端口全开, 7 s 内 /healthz 就绪。VBScript 移除后复测(2026-09-17,就在这一版源码上): ① 全新副本 install.ps1 rc=0(172 s),安装后目录里生成 启动观澜.lnk(1,683 B); ② pythonw.exe scripts\guanlan_start_hidden.py --selftest → {"console_hwnd": 0, "windowless": true}; ③ 全停后按快捷方式的方式启动(pythonw.exe scripts\guanlan_start_hidden.py):命令立即返回, logs/start_hidden.log 记下 窗口状态: 无控制台窗口 (= 无窗口启动成立),7 s 内 /healthz 就绪, /·/detail/·/ops·/ops/recalc 全部 200; ④ 自动化核验用 install.ps1 -NoDesktopShortcut,避免在桌面留一个指向临时副本的死链。


5. 页面取数与"产物即时进页面"

5.1 产品数据的读取口径

服务 读法 产物改了要不要重启
detail :18033(scripts/windscada_serve.py) 首次请求把产物读进内存 _CACHE 不需要:每次取数前比产物指纹(products_stamp() = 相关产物文件的 (路径, mtime_ns, size) 摘要,TTL 2 s),指纹变了自动重载
cms :18020(src/windcms/serve.py) 报告/页面/文件每请求现读 不需要
cms 的谱元数据(src/windcms/data.py::spectra_meta) 进程内缓存,缓存键含各窗 index/spectra_meta 指纹 不需要(新摄入/重算后自动失效)
门户(网关 :28084) portal.html 按 (mtime, size) 缓存改写结果 不需要(重建门户即生效)
网关的 /ops 页面模块 guanlan_ops 模块级缓存 需要重启网关(改了这个文件才需要)

实测证据: 只改一个产物文件的 mtime,logs/detail.log 立刻出现 [reload] 产物指纹变化 b15e2652… → f0b457f7…, 重载,页面随即用新数(无需重启)。 另有显式入口 GET /detail/api/reload(清缓存并回显新旧指纹)。

5.2 门户菜单

顶部菜单: 总览 | 系统架构 | 方法 | 经验发现 | 案例·液压 | 振动·CMS | 仿真与回放 | 交付文档 | 系统状态 | 数据重算 | 登录 (2026-09-16 用户令: 「数据重算」落在「交付文档」与「登录」之间)。

  • 门外壳正本 release/portal_src/shell.html → 装配 release/portal.html(scripts/portal_build.py)。 改外壳后流程: --check(看漂移)→ --rebaseline(有意改动后重设基准)→ 装配 → --check 应"全部一致 ✔"。
  • #recalc 段落用 iframe 懒加载 http://127.0.0.1:28084/ops/recalc(同源;切到本页才挂 src, 否则隐藏时它还会每 2 s 轮询 /ops/api/state)。
  • /ops/recalc 是 /ops 的内嵌版(只保留 重算 + 产物 + 动作进度;服务卡与页头由门户承担)。 走独立路由而不是 ?embed=:网关转给 guanlan_ops.handle() 的是 u.path,查询串会被丢掉 —— 用查询串 会变成"看起来支持、实际不生效"的静默坑。原 /ops 整页保留不变。

6. 输入 ↔ 产物呼应 与 重算

6.1 呼应关系(机器可校验)

python scripts/inventory_products.py --check 逐类输入算跨度/条数,与它喂出的产物对拍(退出码 5 = 有问题)。 2026-09-16 实测呼应正常:

输入类 输入跨度 件数 对拍产物 产物跨度/条数
scada_10min 2025-01-01 ~ 2026-07-07 38 temp_monthly.parquet / loss_monthly.parquet / powercurve_bins.parquet 2025-01 ~ 2026-07 / 19494 · 3729 行
故障报警 2025 ~ 2026(文件名年粒度) 16 alarms.parquet 2025-01-01 ~ 2026-07-15 / 39211 行
风机故障记录 2021 ~ 2026(年粒度) 135 workorders.parquet 2020-01-03 ~ 2026-07-08 / 5876 行
油样报告 2024-11-19 ~ 2025-08-29 404 oil_samples_index.parquet 同跨度 / 404 行
windcms(CMS 原始导出) 2026-03 ~ 2026-04 25693 m5_cms_tcm/windows/w0316/index.parquet 2026-03-16 ~ 2026-04-21 / 2,066,686 行

★ 年粒度输入(报警/工单的"年度汇总表")不参与"产物落后"判定 —— 用文件名年份推出来的跨度天然是年粒度, 拿它当"输入到 2026-12"会造出假缺口(本脚本第一版就这么误报过 2 条)。

6.2 重算的两种运行状态(都实测过)

状态 入口 说明
系统运行中 门户「数据重算」或 /ops 的「执行重算」按钮;等价命令行 python scripts/rebuild_all.py 动作经 _ops_launch.py 二次启动(不挂网关的父子树,避免 taskkill /T 把自己杀掉);跑完不需要重启就能在页面看到新数(§5.1 指纹重载)。重算期间按钮全灰、并发动作被后端拒(HTTP 409)
系统未运行 同一套命令行(先 guanlan.py stop 或本就关机状态) 全部构建器都是普通 CLI,不依赖服务;跑完再双击 启动观澜 快捷方式(或 start.bat)起来,页面直接读新产物。实测: 全停后跑 rebuild_from_raw.py rc=0、inventory_products.py --check rc=0

一键顺序(rebuild_all.py,14 步): ① 放数据(--src 才跑)/② 三门台账/③ SCADA 侧 10 构建器/④ 月度派生件/ ④b 振动侧摄入/⑤ 补齐随包件(★2026-09-16 用户令"清除产物不留备份"之后, 随包件不再有 _products_off/ 暂存区 ⇒ 该步固定返回 6 跳过并说明; 要补齐须显式给交付包 products_restore_missing.py --stash <交付包.zip>, 不再打断整条链)/ ⑥ 重启组件服务/ ⑦ 本体六步/⑧ 本体审计(+可选等价验收)。


7. 缺口与边界(如实列)

缺口 影响的产物 依据/证据 补齐判据
振动六层链四步脚本未随包(rudong_tcm_oem_scan / rudong_line_energy_share / rudong_model_run / rudong_fusion_run) oem_frequency_scan、gear_freq_scan、blade_1p_*、model_run_l6.parquet、fusion_38.csv 等 outputs/<场>/m5_cms_tcm/vib_raw_manifest.json 的 missing_chain 给脚本或口径;源件已在 data/raw/<场>/windcms/
厂商月度报告 12 份 PDF 是纯扫描件 「CMS 振动评估报告」的厂商侧 pypdf 实测 12 页 12 图、extract_text() 长度 0 现场给电子件(docx/xlsx),或上 OCR(本包不装)
handoff_vibration_v2.json / component_history.json 是随包快照 融合面判级、/cms/ 该件含人工裁决/校准更新,不是测量数据的函数 现场给正本,放 data/raw/<场>/m5_cms_tcm/。★2026-09-19(用户令「所有的计算均要形成观澜的源代码」)已补生成端:component_history.json ← scripts/component_history_build.py(重算链 ④d,逐窗标量 x_self → 在升/换件闭环;窗数不足时如实空表 + 写数据边界)、handoff_vibration_v2.json ← scripts/rudong_fusion_handoff.py;现场正本仍在位时优先(生成端不覆盖正本)
随包件"暂存区"按设计不再存在(2026-09-16 用户令"清除产物不留备份") 第⑤步"补齐随包件"固定返回 6 跳过 旧设计把清掉的产物挪到 _products_off/ 以便还原;现口径 = 真删除 要补回"包内没有生成端"的随包件: python scripts/products_restore_missing.py --stash <交付包.zip>(从交付件按需补齐, 不在安装目录里留备份)。products_state.py --off 需显式 --yes;--on 已移除
台账等价验收基线 outputs/<场>/windscada/_pre_rebuild_20260911/ 曾缺失 第④步与 rebuild_from_raw.py --verify 的逐值比对(缺基线时 ④ 返回 5) 该目录在打包时被"清除产物"挪进了暂存区, 而暂存区随后被清掉 ⇒ 机器上无标准答案 2026-09-16 已重建: 从当天 dist 包里取 4 件(alarms/workorders/oil_samples_index/temp_monthly)+ _来源说明.json 标明来历。★链已加固: ④ 容忍 rc=5 —— 缺基线只跳过"等价验收", 不再打断整条重算链。注意: 「清除产物」会把这个目录一并删掉(它在 windscada/ 里), 届时需按同样办法重建
油样 2026-07 批 102 行(华标合并报告) 数据层「油液化验」 源件 BG-2026-07-YP013 ….pdf 不在现场包 补那份 PDF 后 rebuild_from_raw.py --verify 无人工项
无生成端的组级产物(pc_monthly_bins / duty_monthly / thermal_monthly / sector_power / yaw_* / genbearing_monthly / mblub_monthly 等;pitch_daily 已补) 趋势件、热链、扇区、偏航/润滑面 全库只有读取方、0 处写入方 研发补口径(照 temp_monthly 的办法反推 + 逐值验证)。★2026-09-19 进展:pitch/** 已补生成端(scripts/pitch_face_build.py,重算链 ④c,零位三口径 + 日粒度液压/润滑/柱塞),其余各件仍在清单上(docs/源代码化清单_v0.1.md)
configs/farms/*.yaml 无代码读者;available() 只认 *.json 多场部署 src/windscada/config.py::available() 二选一(见 §3.3)
清过产物 + 包不随产物 ⇒ "无生成端"的那批产物不可恢复 pc_monthly_bins/duty_monthly/thermal_monthly/sector_power/yaw_*/genbearing_monthly/mblub_monthly/structure.parquet/watch_channels_monthly、CMS 页面件等(pitch_daily、handoff_vibration_v2.json、component_history.json 2026-09-19 起已有生成端) 2026-09-17 实测:13:05 执行「清除产物」(真删、不留备份),随后重算产出 1,727 件(有生成端的那批,锚点 39,211/5,876/404/19,494 全部对齐、呼应校验正常),但这批无生成端的件没有任何来源可补 —— 交付包按用户令不含产物,本机也没有旧包。★2026-09-19 复核:本包已无 shipped/human 件(反向呼应审计 1,800 件全部 raw-derived、未归类 0),上表"无生成端"的那几族也逐族在补(见 docs/源代码化清单_v0.1.md) 需要这批件时打一个带产物的包(scripts/pack_dist.py --with-products)留档,再用 products_restore_missing.py --stash <该包.zip> 补回;或由研发补生成端(docs/重算缺口与补件清单_v0.1.md)
重算链在缺这批件时曾整步中断 ⑦ 本体层 2026-09-17 实测:pitch_daily 缺 → pitch.registry() 抛 FileNotFoundError;handoff 缺 → fusion.load_handoff() 按设计抛错("不可静默降级")⇒ populate rc=1,⑧ 全不执行 已修:pitch.load/registry、taxonomy.system_matrix(新增 _cell() 逐格兜底 + _缺件说明 写进结果)、fusion.load_handoff/fusion_table 改为响亮降级(空结构 + 缺件说明,判级不造数);实测缺这两件时 ⑦ 全部步骤跑完、ontology.audit rc=0(对象 9,613,含缺件说明)
门户/交付件引用的产物树 不在本包:cleaned/、gearbox_life/、yaw_verify/、structured/ 传动链回放面板、四系统判据面板、控制律仪表台、如东取数单 scripts/pages_audit.py 报 19 条「引用悬空(已知)」;本包在位的是 outputs/<场>/{windscada,m5_cms_tcm,sop,ontology,paradigm_r1,pitch,guanlan,tcm_compatible_replay,windcms} 六层链/结构化产物随包,或把页面里的引用改到本包在位路径(需研发确认是同一件东西;不改交付件正文)。★2026-09-17 用户裁决「确定无用的才删」→ 本项不删:四棵目录逐个 Test-Path = False,即引用确实悬空,记账是实话;被引的页面又都是客户手里的冻结交付件/门户内嵌件(删了门户裂口、交付缺件),而登记表的 known_gap 一旦删掉,这些引用就变成"没人管的悬空引用"(pages_audit 反而会按 rc=5 报出来)。⇒ 保留记账,待该产物线随包后复检;configs/portal_pages.yaml 的 5 条 known_gap.why 已逐条写上裁决语
2 件数据派生页面没有生成端:release/如东/全场状态一览_脱敏_20260825.html(1.5 MB 场站状态快照)、release/如东/如东取数单_2026-08-21.html 交付文档页里这两个子页 全仓搜不到生成端;页面里也没有 source/指纹 ⇒ 无法做陈旧检测(§10) ★2026-09-17 已裁决(用户令"确定无用的才删"):两页都是有真实内容的交付件,不是无用件 ⇒ 不删,改按冻结交付件管:kind: frozen-delivery + version:(与"release/如东/**/*.html 一律冻结交付"的统一口径对齐,消除"同一页既是 data-derived 又是 frozen-delivery"的自相矛盾),并把"性质是数据派生、无指纹无生成端、重算链不会刷新它、会随时间变旧"如实留在 known_gap.why 里。若将来要它们随数据变,再补生成端 + source_sha256(机制已就绪,见 §10 的 data-derived 规则)
3 件内嵌快照缺指纹:标准面板、覆盖度 ch0、如东取数单 门户内嵌页 只有「生成 2026-09-05 16:28」这类时间戳;覆盖度页的源 e1_base.parquet 不在本包 生成时把 source + source_sha256 写进页面(机制已就绪,见 §10 的 data-derived 规则)

8. 变更记录

日期 变更
2026-09-16 本文建立(用户令 1): 产物全景(自动生成清单)+ 路径唯一真源与本次统一的 8 处 + 未统一项如实列表 + 进程无窗口口径 src/proc.py + 产物指纹热重载 + 输入↔产物呼应 + 重算两种状态 + 门户「数据重算」
2026-09-16 用户令 2/3/4/5 的落地: inventory_products.py(清点+呼应校验,--check 出码);⑤步不再打断重算链(StashMissing rc=6);start_hidden.vbs + start.bat 默认无窗口;门户菜单新增「数据重算」+ /ops/recalc 内嵌版;portal_build.py --rebaseline
2026-09-16 振动侧接入(详见 docs/振动数据接入_v0.1.md): data/raw/<场>/{windcms,m5_cms_tcm} 两类源件、rudong_tcm_index.py/rudong_tcm_spectra.py/vib_raw_build.py/vib_reports_build.py、窗 w0316
2026-09-16 用户令"清除产物不留备份": products_state.py --off --yes 改为真删除(不再产生 _products_off*/)、--on 与门户「恢复产物」按钮移除;随包件的唯一来源改为交付包 zip(products_restore_missing.py --stash <交付包.zip>);derived_manifest.prune() 清掉陈旧自登记(raw-derived 台账 3444 → 1740 件,回到真实)
2026-09-16 用户令"打包不含 输入数据/产物/日志" → 交付包 v0.4.0(见 §9): pack_dist.py 增 --no-products、VERSION='0.4.0'、dist-manifest.json 记 no_data/no_products/no_logs 与逐条排除理由;guanlan.py check 读该清单,产物缺失显示 [--] 待重算 而非 FAIL
2026-09-16 现场实炸一次: 目标机 <安装目录> 双击 start.bat 报"无法找到脚本文件 start_hidden.vbs" —— 打包器 INCLUDE_FILES 是手工清单,新加的根目录文件(无窗口启动的 .vbs)忘了写进去。修法不是"再补一行",而是从引用反推:新增 src/entry_refs.py,凡入口脚本(.bat/.ps1/.sh)引用的、本机真实存在的文件,打包时自动补入、打包后对着包内条目复核(缺一个就删掉这个包并 rc=1)、开箱验证(--verify 第①b 步)与装机自检(guanlan.py check 一行)各再查一遍。同时修正 size_of() 把 __pycache__/*.pyc 也数进"预演件数"的虚报(src 少报 129 件)
2026-09-16 用户令"把 VBScript 替换掉"(见 §4.2): 删除 start_hidden.vbs;无窗口启动改为 pythonw.exe + scripts/guanlan_start_hidden.py(不经过 wscript/WSH —— 服务器上 WSH 常被组策略禁用/未安装);install.ps1 安装时用 PowerShell 原生 COM 建 启动观澜.lnk(根目录 + 桌面,带 -NoDesktopShortcut 供自动化核验);start.bat 默认改为 start "" /min pythonw.exe …(.bat 会闪一次,快捷方式才是零窗口);guanlan_start_hidden.py --selftest 用 GetConsoleWindow()==0 自证无窗口;打包器新增 *.lnk(安装时才产生、内含绝对路径)与顶层 *.zip 排除(后者原先只写在文档里没实现)
2026-09-17 入口脚本的换行/编码事故两连 —— 这类问题都不会在开发机上暴露,只在目标机双击时炸:
① install.ps1 的 UTF-8 BOM 被编辑工具吃掉(我在规范化之后又改了一次文件)⇒ PS 5.1 按 GBK 解码 ⇒ 中文乱码 + 级联 ParserError,目标机 install 1 秒即退出。开箱验证当场逮到(install 退出码 1, 耗时 1s + The '<' operator is reserved for future use)。
② install.sh 从写出来那天起就是 CRLF(git HEAD 的 blob 就是 CRLF,不是某次编辑造成的)⇒ POSIX 语义下每个词尾粘 \r:set -e 变非法选项、RT="" 变 RT="\r" 让后续 -n "$RT" 判断翻面 ⇒ 此前所有交付包的 Linux/macOS 安装脚本都是坏的。
加固:src/entry_refs.py::encoding_problems() 成文守则(.ps1 = UTF-8 BOM + CRLF;.bat = CRLF 无 BOM;.sh = LF),在打包、开箱验证第①b 步、装机自检三处强制执行(违反就不出包 / 直接判 FAIL);guanlan.py check 增一行;新增 .gitattributes 把 checkout 也钉死(*.bat/*.ps1 eol=crlf、*.sh eol=lf)。变异测试:去掉 BOM / 改 CRLF / .bat 存成 LF 三种改法都被逐条报出
2026-09-17 打包后对着 zip 条目逐件复核(3,733 件全部与工作树逐字节相同)+ 开箱验证:解压 3,734 件 → ①b 闭合与编码守则通过 → 离线安装 rc=0(172 s)→ ②b 快捷方式在位 + console_hwnd=0 无窗口成立 + 包内无 .vbs → 页面 4/5(/ 与主包同字节;/cms/ 503 属无产物预期)
2026-09-17 用户令 1「页面是否属于产物」的落地(见 §10):新建登记表 configs/portal_pages.yaml(20 个页面/子页 + 五类 kind + 逐条判定依据)与检查器 scripts/pages_audit.py(五条机器规则;陈旧检测 rc=7:内嵌快照的 source_sha256 与当前产物不符即报"数据变了页面没变");guanlan.py check 增一行;rebuild_all.py 增 ⑧b「重装门户(把新产物灌进门户结论段)」与 ⑧c「页面归口审计」;文档增 §10(含用户问的三页逐页判定)。结论:#findings 静态、非产物;#sim 本体是图纸派生的冻结资料包、其中"实际运行回放"面板属产物;#documents 里治理清单/报告是冻结交付件,而脱敏状态一览与取数单属产物且缺生成端与溯源(已入 §7 缺口表)
2026-09-17 用户令 2「统一配置与日志」的落地(见 §11):① 配置——src/paths.py 增唯一取用口 P.config()/P.config_dir()/P.farm_config(),9 个模块不再手拼 configs 路径;新增登记表 configs/registry.yaml 与检查器 scripts/config_audit.py(R1–R8);修掉三处真问题:10 个场定义 YAML 因 available() 只 glob *.json 而"配了看不见"(并查明它们其实是另一条链的机型/物理约束 profile,逐件登记在册)、坏模板 _模板.json.example(GBK 乱码 + 写死 /Volumes/WINDDATA)重写为 _模板.yaml.example、sop/wrapup.py 拼了不存在的 configs/<场>/ 路径;删除 serve.json.bak-bomfix;5 处"代码引用但包里没有"的配置登进 known_missing 并写明影响。② 日志——新增 src/logfile.py(目录/命名/行格式唯一口径 + prefix_stdout() 流包装 + 动作日志保留 20 份/30 天 + logs/build 归位口)与 scripts/log_audit.py(L1–L5);6 个服务 + 启动器 + 运维动作全部接入统一格式(viewer/sim 改走新增的 scripts/static_server.py);把 39 个躺在产物目录里的构建日志搬到 logs/build/ 并同步重算产物台账(2309→2270 件,呼应校验 仍 rc=0)、22 份历史动作日志与 8 个服务日志的旧内容归档 logs/legacy/;guanlan.py check 增两行
2026-09-17 用户令 1「装成服务 + 安装时检查版本」的落地(见 §14):新增版本单一真源 src/version.py;安装脚本增"步骤 0"——读 install-info.json 比对已装版本(相同/升级/降级/老安装无记录四类提示),交互式询问是否重装(-Force/--force 跳过,非交互自动继续),装完写安装记录(version/时间/解释器/运行方式);服务化:Windows 服务(scripts/win_service.py ctypes 直连 SCM,不依赖 pywin32)+ systemd 单元(scripts/service_main.py + guanlan.service)+ 免管理员的"登录自启"备选;两平台共用工作体 scripts/service_worker.py(起/守护/停都走现成的 `guanlan.py serve
2026-09-17 清产物 → 重算链走通(并暴露"包不随产物"的连带后果):用户 13:05 执行「清除产物」(真删、无备份),之后重算在 ⑦ 本体层失败 —— pitch_daily.parquet/handoff_vibration_v2.json 属"包内无生成端"的件,而交付包按用户令已不含产物 ⇒ 目标机/清空后的机器上必然缺,代码却按"缺了就抛"处理,把整条链打断(实测 2267 s 后 rc=1)。修法见 §7/§11.4:windcms(空表 + 结构化无产物页,不再掐连接)、pitch、taxonomy.system_matrix(_cell() 逐格兜底 + _缺件说明)、fusion(响亮降级而非抛错);修完把 ⑦⑧ 跑完:对象 9,613、ontology.audit rc=0,SCADA 锚点 39,211/5,876/404/19,494 全对,呼应校验正常。另把开箱验证改为不打扰运行中的实例(见上一条),实测验证期间主实例 6/7 全程在跑、副本 5/5 页面可用。⚠ 后果如实记:这批无生成端的产物已无可恢复来源(包不含产物、本机无旧包),要保留请用 --with-products 打一份带产物的包存档
2026-09-17 现场事故二连(清产物后登录报"模块不可用"):① windcms 在无产物时自毁 —— data.load_scalars 的 pd.concat([]) 抛 ValueError 带走进程、serve.py 读不到产物页 index.html 直接掐断连接;改为返回空表 + 结构化「无产物」页(HTTP 200,页面写清"缺哪件/怎么补"),实测 CMS 在 outputs/ 为空时 GET / → 200 / 48 KB。② pack_dist.py --verify 原先必须占用默认组件端口 ⇒ 要先停掉用户正在跑的服务(用户点「登录」正好撞上验证窗口);把 guanlan_gateway.ROUTES 的上游端口改为可被 configs/serve.json 覆盖(端口单一真源),验证改为副本整套换端口(网关 28700 + 组件 28800+),不再打扰主实例。见 §11.4
2026-09-17 用户令「实现打包功能」的落地(见 §9):打包器默认口径改为一条命令、零开关 —— outputs/(产物/输出数据)由"默认打进包"改为默认不含(例外用 --with-products,旧开关 --no-products 保留兼容),并新增两类排除:运行日志兜底(logs/ run/ 之外,任何位置的 *.log/*.jsonl 都不进包)与临时文件(*_cache/ __pycache__ .pytest_cache .npm-cache .qa-profile、*.tmp *.temp *.bak* *.old *.orig *.rej *.swp *~ ~$*、.DS_Store Thumbs.db desktop.ini 等);dist-manifest.json 增 no_temp 并保留逐条排除理由;新增双击可用的打包入口 pack.bat(dry/verify 子命令)与 pack.sh、scripts/pack_verify_entry.py(自动找最新 zip);pack.bat/pack.sh 纳入入口引用闭合检查。同时按用户令把 README_先读我.txt 改名为 README_先读我.MD 并在其中新增第五节「打包」(命令/输出位置/四类排除逐条理由/打完自查);.bat 全为纯 ASCII(§11.3)。实测:pack.bat dry 报 3,430 件 / 1,032.0 MB (含缓存与日志的旧口径是 3,741 件 / 1,091 MB)
2026-09-17 现场再炸一次: start.bat 的注释被 cmd 当命令执行('本身是控制台程序' 不是内部或外部命令)—— 中文写在批处理里不可靠(代码页 + cmd 解析),我自己还写错过 echo <<shortcut>>(</> 是重定向符)。处置见 §11.3:包里 .bat 一律纯 ASCII,中文说明移入 README/docs;install.ps1 增设 ASCII 名快捷方式 start_guanlan.lnk;编码守则新增「.bat 纯 ASCII / 不含 << / > 后须是重定向目标」三条,变异测试两类都能拦下
2026-09-17 用户令 3「输入数据放置检查 + 指导」的落地(见 docs/输入数据放置指导_v0.1.md):新增 scripts/raw_data_check.py(R1–R8:顶层/子目录约定、各源类后缀与结构、TCM *_decode.json 按摄入脚本真实取法抽样(body.body[时间戳]=[{Record:…}],机组取 Record.Location)、CSV 编码表头抽样、增量三清单(新增/相同跳过/冲突)、时间空洞、体量提示、按新增源类给重算命令);place_raw_data.py 落位前自动体检并拦下冲突(--force 才覆盖,--no-check 可跳过);新增指导文档(正确目录树、逐类规则、增量语义、放完跑什么、常见错误对照表,含自动生成的体检结论块);guanlan.py check 增一行。实测:真实树 26,325 件/177.6 GB 结构合规、TCM 抽样 12/12 通过;合成增量包(同尺寸/改过/新台号各一)三清单齐全、冲突拦下 rc=6
2026-09-17 现场报"重算之后两处没数据"的根因与修复(见 §11.4 / §7):用户在页面上点「数据重算」,1367 s 后 rc=1。查根因不是重算坏了,而是三件事叠加:① 那批"包内没有生成端"的随包件在用户清产物时被真删,交付包按用户令又不含产物 ⇒ 总览「全场状态」的 iframe 正本 outputs/<场>/windscada/index.html 与振动融合面要的 m5_cms_tcm/{handoff_vibration_v2,component_history,baseline_38}.json、tcm_index.parquet、windcms/ 页面件全都不在位(iframe 404 = 一块空白;振动页无数据);② 重算链第 16 步 ⑧c 页面归口审计因这些缺件判 rc=5 → 整条链中断,连最后的 ⑧台账等价验收都没跑(⑧c 原先只容忍 rc=7),于是"重算失败"这件事本身也是被缺件放大的;③ 第 ⑤ 步"补齐随包件"实际只找 _products_off*/ 暂存区,而含产物的包就在仓库根目录却没人去看 ⇒ 打印"跳过",缺件补不回来。修复:从含产物的交付包补齐 567 件(products_restore_missing.py --stash guanlan-rudong-v2_0.2.0_test_win64.zip,来源逐件记进 _provenance.json 的 shipped)⇒ pages_audit 由 rc=5 转 rc=0 全部一致,/detail/static/index.html 由 404 转 HTTP 200 / 94 KB,/detail/api/vibcms 恢复真实数据(全场 38 台判级);products_restore_missing.stash_dir() 增自动找含产物的包(本仓根与上一级 .zip 里挑真含 windscada/temp_monthly.parquet 的那个,实测自动命中 586 件);rebuild_all 的 ⑧c 改为容忍 rc=5 并写明它意味着什么(口径与 ④/⑤/⑥/⑧b 一致:缺的是"随包件"这一路,不是重算本身;缺件仍由 guanlan.py check 报 FAIL);/static/*、/turbines/* 缺件改回结构化缺件页(见 §11.4)。另修两个打包器缺陷:install-info.json 被原样打进包(自动补入把 install.ps1 读它当成了"入口引用的文件"⇒ 目标机首次安装会误报"已安装、版本相同",版本检查失灵)与 --with-products/--with-data 是摆设(keep() 无条件套用 EXCLUDE_GLOBS,实测开关打出来的包与默认包同为 3441 件;修后 3440 → 5697 件 / 4.7 GB)。两条都加了机器守卫:打包后复查"包内不得有机产物"(有则删包 rc=1)
2026-09-17 用户令「增加卸载脚本 + 打包版本管理」的落地(见 §14.5 / §15):① 卸载 —— 新增入口 uninstall.bat(纯 ASCII/CRLF)+ uninstall.sh(LF),共用 scripts/guanlan_uninstall.py;默认只拆"本程序装上去的东西"(停服务 → 注销服务 → 撤开机自启 → 删快捷方式 → 清运行态 → 删安装记录),默认保留 data/ outputs/ logs/ .venv/ 与程序目录,--purge 才删数据(再问一次)、--all 才删程序本体;--dry-run 只打印、--selftest 断言"计划齐全 + dry-run 无副作用"。uninstall.bat 先 cd /d "%TEMP%" 再调 Python(Windows 不允许删"活进程的当前目录"),实测 --all 能连安装根一起删掉。开箱验证新增第 ③b 步:在刚装好的副本上真跑卸载并断言"记录/运行态/快捷方式都拆掉 + 数据与产物一件不少 + 端口已释放"。顺带把 read_installed 改为按 utf-8-sig 读(带 BOM 的 install-info.json 原先会让版本检查"明明装了却报没装过")。② 版本管理 —— src/version.py 增版本号规则(v<大>.<中>.<小>:大=方案/架构/核心功能、中=非核心功能增减改、小=消缺)、level_of() 机器判据、package_name() 与 HISTORY;VERSION 升至 2.5.0(二代架构第 5 次功能性发布);打包器删掉自带的 VERSION = '0.4.0' 常量改读真源,默认包名从 guanlan-v<版本>_dist_<日期>.zip 改为 app_guanlang_v<版本号>.zip;新增 scripts/version_log.py 由 HISTORY 生成 docs/版本记录.md 并支持 --check(guanlan.py check 有一行盯"记录表与代码一致");guanlan.py version 打印版本与规则;uninstall.bat/sh 纳入入口引用闭合与编码守则
2026-09-17 用户令「仍未裁决的 3 项,确定无用的可以删除」的裁决落地:逐项核证后只删确定无用的,其余如实保留并把裁决语写进登记表与本节 —— ① 19 条引用悬空 ⇒ 不删(四棵被引目录逐个 Test-Path=False,引用确实悬空;被引页面是客户冻结交付件/门户内嵌件,登记表条目一删就成"没人管的悬空引用"反而按 rc=5 报错)⇒ 5 条 known_gap.why 逐条补写裁决语;② 2 件无生成端的取数页面 ⇒ 不删,改 kind: frozen-delivery + 补 version:(与"release/如东/**/*.html 一律冻结交付"口径对齐,消除同页两种 kind 的矛盾),残留风险(会变旧、重算不刷新)留在 known_gap.why;③ release/viewer/.npm-cache + .qa-profile ⇒ 真删(64+250 件 / 56.1 MB;非 git 跟踪、无运行时代码读取、本就被打包器排除);④ 顺带核了两处"像死代码"的:scripts/ingest_ops_2025.py(被 check_portability.py 引用)与 configs/contracts/*.yaml(被 src/paths.py 与 registry 引用)都在链上,不删。复检:pages_audit 已知缺口 21 → 18 条(两条不再按"溯源缺失"重复报),rc=6 仅剩两条 —— page_product_cms / page_product_vib_raw_manifest 要的 m5_cms_tcm/windows/w0316/index.parquet 与 m5_cms_tcm/vib_raw_manifest.json 不在位(用户 14:58 亲手清产物所致,属"检查器在正常工作",补齐走 scripts/vib_raw_build.py 或门户「数据重算」,未擅自重建)
2026-09-17 用户令 1「运行不要从含产物的交付包补齐」+ 用户令 2「无生成端的那批能否逆向工程推导」的落地(见 §13.6):① 运行期与交付包解耦 —— rebuild_all.py 第 ⑤ 步由"补齐随包件"改为"反向呼应审计(只报账不搬运)";本会话里我加过的"自动找旁边的 zip"整段删掉(那会把"旁边有个包"变成隐式依赖);运行期一切面向人的提示(工作台缺件页 _gap_page、err=no_products 说明、CMS「无产物」页、taxonomy._缺件说明、运维控制台清产物提示、products_state --status/--on、打包器文字与 README)统一改成"放原始件后重算,或由研发补生成端";products_restore_missing.py 降级为离线人工补救(新增 --find 仅供人工诊断,无 --stash 时不再自动找包)。② 逆向工程可逆性矩阵(机器证据而非印象) —— 判据两条:生成端在不在包内 + 产物里有没有人工判断字段(按字段名匹配,不拿整篇子串;页面 .html 不计入),并区分测量形态件与文本·判断件。512 件的实测分布:混合(测量件可反推/文本件需人定) 71(振动线出件:23 测量形态 + 48 人工件)· 半可逆(需人工裁定) 207(sop 评审底稿)· 不可逆(人工判断) 144(tcm 回放 58 · windscada 评审 47 · paradigm_r1 29 · 本体发布层 5 · 事实契约 5)· 可逆(页面可再生) 52(windcms 页面件)· 可逆(需反推口径) 2(pitch 变桨件)· 应移出产物仓 21 · 需人工裁定 15(谱图) ⇒ 答复:能从原件推出来的是"内容只是原件的函数"那部分(约 125 件),做法照 temp_monthly(反推口径 → 逐值对拍,一致才敢用);含人工裁决/评审/领域正本的 351 件推不出来——逆向工程对它们只能"重做那一步人工判断",正确处置是承认它们是人工件(不该按自动产物管)或由研发补口径

9. 交付包组成(v0.4.0;2026-09-17 用户令:默认不含 输入数据 / 产物 / 运行日志 / 临时文件)

打包器:scripts/pack_dist.py。一条命令、不需要开关:python scripts/pack_dist.py(或 Windows 双击 pack.bat、Linux/macOS sh pack.sh;pack.bat dry 只预演、pack.bat verify 开箱验证)。 默认文件名 <安装目录的上一级>/guanlan-v<VERSION>_dist_<日期>.zip,可用 --out 指定。

进包 内容
src/ scripts/ guanlan.py 程序与全部构建/运维脚本(含 src/entry_refs.py 入口引用闭合检查、scripts/pack_dist.py 打包器自身)
configs/ resources/ reference/ 配置(含 serve.json 端口真源、portal_pages.yaml 页面归口、registry.yaml 配置登记表)
release/ 门户、仿真页、三维资产 viewer/、治理清单交付件 release/如东/(客户交付物,勿外传)
docs/ README_先读我.MD 测试须知.txt 交付文档与说明书(README 已按用户令由 .txt 改名为 .MD)
wheels/win_amd64/(42 件)、vendor/python/(3 平台便携运行时) 离线安装件:Windows 完全离线可装;Linux/macOS 走联网安装(要离线就把轮子放进 wheels/linux_x86_64 / wheels/macos_arm64)
install.bat/.ps1/.sh、check/start/stop.bat、pack.bat pack.sh、requirements.txt 安装、起停与打包入口(无 .vbs:start.bat 默认调 pythonw.exe + scripts/guanlan_start_hidden.py;install.ps1 建两份快捷方式 —— 中文名给人双击、start_guanlan.lnk 供控制台/脚本引用)
不进包(默认口径) 理由(同时写进包内 dist-manifest.json 的 excluded)
① data/ 输入数据:现场原始件,约定不随包分发(例外要带用 --with-data)
② outputs/ 产物/输出数据:目标机放数据后 rebuild_all.py 重算;缺"无生成端"的那批用 products_restore_missing.py --stash <交付包.zip>(例外要带用 --with-products)
③ logs/ run/ + 任何位置的 *.log、*.jsonl 运行日志/审计流水:run/pids.json 里的 PID 到新机器上是无效引用;*.log 兜底规则防止日志从别处漏进包
④ *.tmp *.temp *.bak* *.old *.orig *.rej *.swp *.swo *~ ~$* 临时与备份文件(编辑器/工具留下)
④ *_cache/ __pycache__/ .pytest_cache .mypy_cache .ruff_cache .ipynb_checkpoints .npm-cache .qa-profile 缓存目录(解释器字节码、第三方工具缓存)。★2026-09-17:release/viewer/ 下实测存在的 .npm-cache/(64 件 15.9 MB,npm 缓存)与 .qa-profile/(250 件 40.1 MB,浏览器 QA 用户目录)已按用户令真删(release/viewer 636.0 → 580.0 MB,省 56.1 MB);两目录都不在 git(release/viewer/ 整目录被 ignore)、不被任何运行时代码读取、本就被打包器排除 ⇒ 确定无用。规则留在打包器里当兜底(npm/QA 再跑一次就会重新生成,届时也不会进包)
④ .DS_Store Thumbs.db desktop.ini *.crdownload *.partial 系统/下载临时索引
install-info.json、*.lnk 装机产物(本机装没装 / 本机快捷方式,含安装时间与本机绝对路径)—— 只在本机有效,换机即失效。★ 2026-09-17 实逮:本机的 install-info.json 被原样打进了交付包(INCLUDE_FILES 里没有它,但入口引用闭合兜底把 install.ps1 读它这件事当成了"入口引用的文件"自动补入)。后果是静默误报:目标机第一次装就读到它,安装前检查报"已安装 v2.5.0(装于打包机时间)→ 版本相同,是否重装"。处置:打包器按名字排除 + 自动补入环节过滤 + 打包后复查(包里出现install-info.json或 *.lnk 就删包并 rc=1);.gitignore 也把这两类挡住
.venv/ .git/ .github/ venv 换机必失效(安装时重建);版本库不随交付件分发
_products_off*/ 旧设计的"清除产物"暂存档(2026-09-16 起清除=真删,不再产生;老机器上若有可手工删)
*.lnk __pycache__/ *.pyc *.pyo、顶层 *.zip 安装时建的快捷方式(绝对路径,由目标机 install.ps1 重建)、字节码、旧的交付压缩包(避免包中包)

打包器自带两道闸(不合格的包会被 unlink() 掉并返回非 0,不留"看起来能用"的坏包): 入口脚本引用闭合(.bat/.ps1/.sh 引用的文件必须都在包里)+ 入口脚本编码/字符守则(§11.3)。

开箱验证(一条命令给出"能不能装、能不能跑"的证据):python scripts/pack_dist.py --verify <zip> —— 解压到 临时目录 → ①b 入口引用闭合 + 编码守则核对 → 离线安装 → ②b 快捷方式×2 + console_hwnd=0 无窗口 + 包内无 .vbs → 起服务核验 /·/detail/·/cms/·/ops·/healthz → 收尾清理,要求 ≥4 个页面可用。 ★ 核验要求组件端口(18033/18020/18791/18792/64292)空闲,验证前先 guanlan.py stop,否则整段核验被跳过(打印 [!])。

9.1 入口引用闭合(src/entry_refs.py,2026-09-16 实炸后加)

现场反馈:目标机 <安装目录> 双击 start.bat → 无法找到脚本文件 "<安装目录>\start_hidden.vbs"。 根因是打包器的 INCLUDE_FILES 是手工清单,新加根目录文件时漏了一项 —— 这类清单必然漏,所以改成从引用反推:

凡是入口脚本 (.bat/.ps1/.sh) 里出现的一个路径, 且该路径在本机源码树里真实存在 → 它必须在包里
(本机不存在的 `.venv\Scripts\pythonw.exe` / 系统 exe / `%~dp0` 前缀残片, 按"装机后才有的东西"忽略)

四处落地:打包时自动补入(plan() 打印 [+] 自动补入 (被入口脚本引用))→ 打包后对着 zip 条目复核(缺一个就删包 + rc=1,不留"看起来能用"的坏包)→ --verify 第①b 步在解压目录再查 → guanlan.py check 输出一行 入口脚本引用闭合 (24 条: start.bat→guanlan_start_hidden.py 等),装机后 check.bat 也能看出来。

★ 那个 .vbs 已按用户令删除(§4.2),但这份守卫保留:它守的是"入口引用的文件必须齐全"这条性质,与用什么语言实现无关 —— 现在它盯的是 start.bat/install.ps1 → scripts\guanlan_start_hidden.py 这类引用(这次替换正是它自动发现的:去掉 .vbs 后入口列表变为 6 个、引用 24 条,全部在位)。


10. 页面归口(哪些页面是产物、该不该随数据变)

用户问的三页,结论(依据见下表与 configs/portal_pages.yaml):

页面 该不该随输入数据变 算不算产物 现状与处置
#findings 经验发现 不该 不是 外壳里的静态叙述(2,322 B,0 iframe/fetch/产物引用),内容是公司级方法论而非本场站数据的函数 ⇒ 留在受管外壳。若将来要放本场站计数,必须改成 data-derived 并登记 source
#sim 仿真与回放 页面本体不该 本体不是 叙述+6 个链接:仿真台是图纸/机型派生的冻结资料包(release/如东SWT40_控制律仿真台_20260906.zip,由 release/sim_sys_server.py 从 zip 现读),三维工作台是图纸派生。但其中"实际运行回放"面板(门户内嵌 tpl-如东传动链实际运行诊断_单文件版)把 outputs/<场>/cleaned/turbine_1min.parquet 连 source_sha256 一起烘进了页面 ⇒ 那一件属于产物
#documents 交付文档 交付件按版本变,不随数据自动变 治理清单/报告不是(冻结交付件);取数单与脱敏状态一览是 iframe 指向的治理清单分册自带版本号+日期(v1.2_2026-09-01 / v4.5_2026-09-06)⇒ 冻结交付件。但 全场状态一览_脱敏_20260825.html(1.5 MB 场站状态快照)与 如东取数单(明列产物路径)是数据派生的 ⇒ 属产物,且现在既无生成端也无溯源

五种 kind 与机器规则(登记表头部有完整说明):

kind 该随数据变? 机器查什么
static 否 正文不得引用产物(引用了说明分类错了)
live 是(永远一致) iframe/链接的端口必须在 configs/serve.json 的已知服务里
data-citing 否(正文不变) 被引用的产物路径必须存在(悬空引用 = 挂羊头)
data-derived 是 必须有 source + (source_sha256 或生成时间);有 sha 就跟当前产物逐字节比对 ⇒ 陈旧检测
frozen-delivery 否(按交付版本变) 文件名或正文必须能读到版本号 + 日期

"纳入产物管理"落到本系统的四件事:① 登记(configs/portal_pages.yaml:kind/来源/生成端/依据); ② 溯源(source + source_sha256 或生成时间);③ 陈旧检测(scripts/pages_audit.py,rc=7 = 数据换了页面没换); ④ 进链(rebuild_all.py ⑧b 重装门户把新产物灌进结论段、⑧c 页面归口审计;guanlan.py check 一行)。 实时读产物的页面(/detail/、/cms/、/ops)不需要快照,靠 §5.1 的产物指纹热重载即时更新。

当前如实状态:19 条已知缺口(登记表里逐条写了理由,不装作通过)—— 都是"页面引用了本包没有的产物树" (cleaned/、gearbox_life/、yaw_verify/、structured/,属未随包的六层链/结构化产物线,见 §7), 因此这些页面拿不到当前值做比对;另有 2 件数据派生页面没有生成端(脱敏状态一览、取数单), 以及 3 件内嵌快照缺指纹只能按生成时间判断(标准面板、覆盖度 ch0、取数单)。这三类都记在 §7 缺口表里。

10.1 门户页面归口(自动生成,勿手改)

判据与说明见 configs/portal_pages.yaml 头部;检查器 scripts/pages_audit.py。

页面/子页 kind 随输入数据变? 依据(为什么这么判)
index 总览 static 否 外壳 #index 段 2,519 B: 0 iframe / 0 fetch / 0 产物引用; 正文是产品承诺与入口导航
architecture 系统架构 static 否 外壳 #architecture 段 3,037 B: 无数据引用; 描述分层架构
method 方法 static 否 外壳 #method 段 2,959 B: 无数据引用; 描述方法论
findings 经验发现 static 否 ★用户问的三页之一。外壳 #findings 段 2,322 B: 0 iframe / 0 fetch / 0 产物引用; 正文是公司级经验叙述(59 项内部检查 → 8 条公开教训, 60+ 个风场), 不是本场站数据的函数 ⇒ 不该随输入数据变, 也不是产物。若将来要放本场站计数, 必须改成 data-derived 并登记 source
case_hydraulic 案例·液压 static 否 外壳 #case_hydraulic 段 6,187 B: 无数据引用; 案例叙述
cms 振动·CMS live 是 外壳 #cms 段里 1 个 iframe + 2 个链接指向组件 :18020 (src/windcms/serve.py), 组件按需读产物 ⇒ 页面本身不存快照, 数据变了刷新即变
recalc 数据重算 live 是 外壳 #recalc 段只有 1 个到 /ops 的链接(网关提供, 后端即真实状态)
login 登录 live 是 外壳 #login 段链接到工作台 :18033/v2
admin 系统状态 live 是 外壳 #admin 段由外壳 JS 拉网关 /healthz 渲染(段内无静态数字)
sim 仿真与回放 static 否 ★用户问的三页之一。外壳 #sim 段 50,767 B 是静态叙述(机理/图纸说明), 段内 0 iframe / 0 fetch; 6 个链接指向 :18792 仿真台页与 :64292 三维工作台。仿真台的输入是图纸/机型参数(configs/machine_packs), 不是 data/raw ⇒ 页面本体不该随输入数据变
  └ 控制律仿真台资料包 (5 页) frozen-delivery 否 release/sim_sys_server.py 直接从 zip 里读这 5 页 ⇒ 冻结资料包(日期在包名里); 内容为图纸/机理仿真, 不随输入数据变
  └ 三维拆装工作台 static 否 release/viewer/** 由图纸/三维模型构建(build-*.py + esbuild), 与 data/raw 无关; rev=hub-review-v1 是评审版本号
  └ 控制律仪表台面板 (门户内嵌) data-citing 否 面板正文是固定的判据/仪表说明, 只把产物当脚注引用('判据与证伪条件全量见 outputs/rudong/yaw_verify/findings.json') ⇒ 正文不随数据变, 但被引用的产物必须在位
  └ 四系统判据面板 (门户内嵌, 4 份) data-citing 否 同仪表台: 静态正文 + findings 引用脚注
documents 交付文档 static 否 ★用户问的三页之一。外壳 #documents 段 5,690 B 是导航页(0 fetch), 1 个 iframe 指向网关下发的治理清单 HTML(release/如东/**)。段内不含数据快照
  └ 治理清单交付包 (分册 + 全册 + 正式报告) frozen-delivery 否 文件名自带版本号与日期(如 如东_液压系统治理清单_v1.2_2026-09-01.html / 全册_v4.5_2026-09-06.html) ⇒ 客户交付件, 随交付版本变; 内容虽由分析产出, 但冻结发布, 不该随输入数据自动改
  └ 全场状态一览 (脱敏) frozen-delivery 否 ★2026-09-17 用户裁决前的问题: 1.5 MB 的场站状态快照(脱敏版), 内容是实际运行状态的函数 ⇒ 属于产物, 却没有任何指纹/生成时间/来源登记, 全仓搜不到生成端 ⇒ 曾游离在产物管理之外(无溯源、无陈旧检测、无重算入口)。保留不删: 它是客户手里的交付件, 删了交付就缺件
  └ 如东取数单 frozen-delivery 否 取数单是「要哪些数据」的清单页, 明列产物路径 ⇒ 性质上随产物结构变; 但只有日期没有指纹, 也没有生成端随包 ⇒ 无法自动刷新。保留不删: 另有 3 处交付侧引用它(release/如东/如东取数单_2026-08-21/README.md、 如东现场一页单*.md), 删了就成悬空引用
page_product_cms CMS 评估报告页/件 (门户「振动·CMS」页读的就是它们) data-derived 是 门户 #cms 的 iframe 指向组件 :18020, 组件页读的就是 outputs/<场>/windcms 下的这些页面件 (index.html / 逐台页 / workbench.html / report_*.md) —— 它们是产物(由 windcms 报告生成器读 TCM 窗索引写成), 不是程序。本包缺六层链的 model_run/fusion 产物, 重生成会掉内容 (report.md −97%, docs §7), 故不在重算链里重建, 交付时按随包件补齐并登记为 shipped。 管理动作: 登记(本条) + 溯源(_provenance.json 的 shipped) + 陈旧检测(源=TCM 窗索引的 mtime/sha)。
page_product_vib_raw_manifest 振动摄入清单 (数据层「CMS 振动评估报告」页引用) data-derived 是 每次振动摄入重写: 记本窗索引/谱库的件数、时间跨度与未随包的六层链缺口(missing_chain)。 数据层页面与 docs §7 都引用它 ⇒ 属产物, 且已在台账中自登记。
embed_replay_chain 传动链实际运行回放 (门户内嵌单文件版) data-derived 是 内嵌 window.RUDONG_ACTUAL_REPLAY 带 source=outputs/rudong/cleaned/turbine_1min.parquet 与 source_sha256 ⇒ 明明白白的产物烘入快照; 已可做陈旧检测(产物在位时)
embed_standard_panel 标准面板 (门户内嵌) data-derived 是 正文带 '生成 2026-09-05 16:28' 与产物引用 ⇒ 某次生成的数据快照; 缺 source_sha ⇒ 只能按生成时间+引用在位判断
embed_coverage_ch0 覆盖度报告 ch0 (门户内嵌) data-derived 是 正文写 '生成 2026-09-05 16:28 · 源: e1_base.parquet e3b531b8ed">@e3b531b8ed (10-min, 2,973,638 格, 38 台)' ⇒ 数据派生; 但 e1_base.parquet 不在本包 ⇒ 溯源链断在包外
embed_u6_sim U6 变桨液压仿真台 (门户内嵌) static 否 图纸派生的物理回路仿真台面板; 无内嵌数据/指纹/产物引用
embed_u6_health U6 液压公共站健康报告 (客户版, 门户内嵌) frozen-delivery 否 984 KB 客户交付报告; 文件名标 '客户版'; 无数据指纹 ⇒ 按交付件冻结(缺版本号/日期, 见 audit 提示)
embed_gearbox_report 整机综合诊断与风险评估 (主轴冲击深挖 V2.1, 门户内嵌) frozen-delivery 否 3.7 MB 综合诊断报告, 文件名带修订版号 V2.1; 报告类交付件按版本发布
embed_reports_4 分系统评估报告 (主轴承/齿轮箱/发电机/变桨, 门户内嵌 4 份) frozen-delivery 否 4 份系统评估报告(0.7–3.4 MB), 无内嵌数据/指纹; 属交付报告(缺版本号/日期登记)
embed_governance_12 治理清单分册 (门户内嵌 12 份) frozen-delivery 否 12 份治理分册(0–11), 与 release/如东 下的分册同源; 冻结交付件
embed_sc1_local SC1 本地占位 (门户内嵌) static 否 0.4 KB 占位面板
detail_tabs 工作台 /detail/v2 的 10 个标签 (SPA) live 是 工作台是单页应用: 外壳一次装载, 每个标签按需 fetch 后端 API, 而 API 现场读产物 (产物指纹 TTL 2s 自动重载) ⇒ 页面本体不存快照、永远与产物一致, 不需要产物管理。 其中 report 标签渲染产物 outputs/<场>/windcms/report.md(结论件), 该件属产物并已在台账里。 ★ 2026-09-17 现场: 清产物后 tab=vibration 空白, 不是页面坏了, 是它要的
m5_cms_tcm/{handoff_vibration_v2,component_history,baseline_38}.json 与 CMS 页面件不在位 (§7/§13.5)。
delivery_pages_all 交付页 (release/如东 下全部 html) frozen-delivery 否 客户交付件(治理清单分册/全册/正式报告/取数单/脱敏状态一览), 文件名自带版本号+日期 ⇒ 按交付版本冻结。 其中"脱敏状态一览"与"如东取数单"是数据派生的(内容随运行状态变) ⇒ 另见 §12 的逐页判定与 known_gap。
viewer_pages_all 三维页 (release/viewer 下全部 html) static 否 图纸/三维模型派生的资产页(工作台/索引/装配演示), 与 data/raw 无关 ⇒ 不是产物。

11. 配置与日志的统一口径(2026-09-17 用户令 2)

11.1 配置目录与配置文件

唯一取用口:代码只许用 src/paths.py 的 P.config(...) / P.config_dir(...) / P.farm_config(...), 不许再手拼 "configs" / …(统一前实测 9 个模块各拼各的:llm_gate、windscada/terms、sop/analysis_lock、 sop/scenarios、sop/wrapup、guanlan.py、guanlan_ops、page_fingerprint、check_transferable, 另有 audit_chinese_terms.py 要试三个位置才找得到 terms 库)。

位置 放什么 谁读
configs/serve.json 运行期单件:端口/路径真源(install 脚本写) guanlan.py、网关、运维控制台、指纹、移植检查
configs/models.json 运行期单件:本机模型档与 pull 命令 guanlan.py check、src/ontology/llm_gate.py
configs/portal_pages.yaml 运行期单件:门户页面归口登记表(§10) scripts/pages_audit.py
configs/registry.yaml 配置登记表:每个域是什么、谁读、缺哪些 scripts/config_audit.py
configs/canonical/(71 别名 + 3 决策) 通道/测点 canonical 字典与别名字典 src/sop/contract_gate.py、discriminators.py;别名表由未随包的分析链消费
configs/contracts/(74) 机型契约(判据参数)与机型-场站对照表 scripts/sim_hub/wake_*.py;(历史)分析链
configs/farms/(15) 场定义(YAML 为准,历史 .json 兼容) src/windscada/config.py
configs/terms/(3) 显示层术语映射、OEM 术语基准、行话规则 src/windscada/terms.py、ui/build.py、audit_chinese_terms.py

本次修掉的三处真问题:

  1. 场定义"配了看不见":configs/farms/ 下 10 个 YAML 里 0 个能被 available() 列出(它只 glob *.json), farm('FUSHAN') 直接报"未知场"。现在 yaml/json 都认、约定 YAML,并且——更重要的——查清了那 10 个 YAML 其实不是场定义而是另一条链的"机型/场站物理约束 profile"(顶层 meta + physical_constraints), 于是 available() 只列能加载的场、foreign_farm_files() 把 profile 逐件报出来(登记表里已声明), 并把 farm() 的报错写得能看懂("该文件存在但不是场定义…")。
  2. 配置模板是坏的:configs/farms/_模板.json.example 是 GBK 乱码("绀轰緥椋庣數鍦?")且写死 /Volumes/WINDDATA, 与实物格式(YAML)相反。已重写为 configs/farms/_模板.yaml.example(UTF-8、YAML、<场站名称> 占位), 并同步 docs/数据目录结构与落位约定_v0.2.md。
  3. 散件与悬空引用:删掉 configs/serve.json.bak-bomfix(历史备份垃圾);sop/wrapup.py 原来拼的是 configs/<场>/value_assumptions.yaml(那个目录根本不存在)→ 改为 configs/value_assumptions/<场>.yaml。 另有 5 处代码引用的配置不在包里(analysis_lock*、scenario_registry、discriminator_registry、 analysis_modules、value_assumptions/<场>),逐条登进 registry.yaml::known_missing 并写明影响 (例如场景注册表缺失 ⇒ SOP 场景解析不可用,但本包无调用方,故不影响页面)。

机器规则(scripts/config_audit.py):R1 顶层只许放登记过的运行期单件配置;R2 无备份垃圾(*.bak* 等); R3 命名(空格判错、中文/大写只提示);R4 内容不得含本机绝对路径——区分字段:说明性字段/注释里记的 "当时数据在哪台机器"算溯源信息(提示),被读取的字段里出现 C:\、/Volumes/… 才是失配(判错); R5 代码引用的配置必须存在(未登记的按悬空引用判错,登记过的记 i);R6 不许手拼 configs 路径; R7 每个域必须声明 consumers 或写明"无人读";R8 configs/farms/ 每个文件要么是能加载的场定义, 要么在登记表里声明为 profile/CSV 数据表。现状:171 件配置、5 个域、1 个场定义、10 件机型 profile、 0 不一致(14 条已知缺口、7 条提示)。

11.2 日志目录、命名与内容格式

统一前(实测,不是推测):logs/ 顶层堆了 34 个文件,其中 22 个是历史动作日志(无保留策略); 39 个构建日志躺在产物目录里(outputs/<场>/windscada/*.log 等),还被 _provenance.json 登记成 shipped 随包件——产物台账里混着日志;detail.log 首行是 b'windscada serve :18033\r\n'(CRLF), cms.log 首行是一条历史 SyntaxWarning;行里没有时间戳也没有级别,import logging 的文件数 = 0。

位置 放什么 命名
logs/<组件>.log 长驻服务与启动器(组件 = serve.json 的键 + gateway/serve/start_hidden) gateway.log、detail.log、cms.log、sim.log、sim_sys.log、viewer.log、serve.log、start_hidden.log
logs/ops/ 运维动作(自动保留最近 20 份 / 30 天,写前清理) <动作>_<YYYYmmdd-HHMMSS>.log
logs/audit/ 机器审计流水(一行一条 JSON) llm_audit.jsonl、cloud_qa.jsonl、terms_audit.json
logs/build/<场>/… 构建/摄入脚本的日志(按产物子路径归档) 沿用原相对路径
logs/legacy/ 统一格式之前的日志(启动器与动作的历史留档,可随时删) 原名 + 时间戳

行格式(每一行都要满足,正则 src/logfile.py::LINE_RE): YYYY-MM-DD HH:MM:SS LEVEL 组件 消息,级别 ∈ DEBUG/INFO/WARN/ERROR;UTF-8 无 BOM、行尾 LF、无 ANSI 颜色码。

怎么保证 6 个服务都合规:不要求各服务改写自己的 print —— 入口处调一次 src/logfile.py::prefix_stdout('<组件>'),之后 stdout/stderr 的每一行自动带前缀(含把 stderr 与含 error/traceback/失败 字样的行标成 ERROR)。viewer/sim 原先是 python -m http.server(Common Log 格式,挂不上前缀),改用新增的 scripts/static_server.py(同样带统一格式)。运维动作日志改由 logfile.action_log() 生成并带保留策略;本体模型闸与云问答审计改落 logs/audit/。

本次的实际搬迁(都做了台账/留档,不是悄悄删):

  • 39 个构建日志 outputs/**/*.log → logs/build/<场>/<原相对路径>;同时把 _provenance.json 里这 39 条 删掉并重算计数(2309 → 2270 件;shipped 569 → 530;raw-derived 1740 不变),台账里写明"因日志归位移出"。 搬迁后 inventory_products.py --check 仍 rc=0 呼应正常。
  • 22 份历史动作日志(ops_*.log,2026-09-12 那批)与 _proc_reg.log → logs/legacy/;
  • 8 个服务日志里"统一格式之前"的内容 → logs/legacy/<名>.<时间戳>.log,现场文件只保留合规行 (服务以 append 持句柄,原地截断不影响继续写);logs/llm_audit.jsonl → logs/audit/。

机器规则(scripts/log_audit.py):L1 运行日志只在 logs/ 下(白名单:产物附件 analyze_stdout.log、 第三方工具缓存 .npm-cache/、.qa-profile/、node_modules/,各写了理由);L2 命名;L3 按末尾 200 行校验 行格式(启用前的旧行不算数);L4 行尾/颜色(历史行只提示);L5 logs/ops/ 保留策略 + logs/audit/*.jsonl 每行必须是合法 JSON。现状:8 服务日志 + 39 构建日志 + 1 审计流水,0 不一致(39 条"历史构建日志"提示,写它们的人不在本包);guanlan.py check 各报一行。

11.3 入口脚本的字符守则(.bat 必须纯 ASCII)

现场反馈:双击 start.bat 报 '本身是控制台程序' 不是内部或外部命令 / '模式' 不是内部或外部命令 —— 批处理注释里的中文被 cmd 当成命令执行了。批处理的解析与输出都绑在控制台代码页上 (cp936 / cp65001 / 系统区域设置),多字节字符 + cmd.exe 的既有毛病会凑出这类事故。 另外我自己还写错过一处 echo <<shortcut>> —— > < 在批处理里是重定向符,echo 里出现就报 << was unexpected at this time(本机已复现)。

处置:包里所有 .bat 改写为纯 ASCII(英文),中文说明全部留在 README_先读我.MD 与 docs/; start.bat 功能一字未变(默认 pythonw.exe 无窗口起服务 / console 前台档 / help / .venv 与启动器在位检查)。 install.ps1 现在建两份快捷方式:中文名给人双击,start_guanlan.lnk(ASCII 名)给控制台与脚本引用 —— 这样纯 ASCII 的 start.bat 帮助里也能准确写出它的名字。

机器守则(src/entry_refs.py::encoding_problems(),在打包、开箱验证第①b步、装机自检三处执行):

文件 守则 违反的后果
.bat 纯 ASCII(无 >0x7F 字节)· CRLF · 无 BOM · 不含 << · > 后必须是重定向目标 中文被曲解后当命令执行;<</<<x>> 直接报重定向错
.ps1 UTF-8 带 BOM + CRLF PS 5.1 按 GBK 解码 → 中文乱码 + 级联 ParserError
.sh LF 每个词尾粘 \r:set -e 变非法选项、RT="" 变 RT="\r"

变异测试:往 start.bat 注释里加一句中文 → 报"含 39 个非 ASCII 字节 … 现场出现过'注释被当命令执行'"; 写回 <<shortcut>> → 报"含 <<"+"第 101 行的 > 后面不是重定向目标"。本机 cmd /c start.bat help 实跑无任何报错。

11.4 "无产物"时必须还能打开页面;开箱验证不许打扰运行中的实例

现场症状(2026-09-17):在控制台点了「清除产物」之后,从门户点「登录」→ /detail/v2 显示 "模块不可用: 综合详细分析工作台 … 127.0.0.1:18033 未启动"、日志里 [WinError 10061] 连接被拒。 查下来是两件独立的事叠在一起:

  1. CMS 服务会因为"产物没了"而崩:src/windcms/data.py::load_scalars 在无窗时执行 pd.concat([]) → ValueError: No objects to concatenate(进程直接没了);就算进程活着,src/windcms/serve.py 也要 读产物里的 index.html,缺了就抛 FileNotFoundError,请求连接被掐断(页面白屏)。 处置:load_scalars/load_alarm_counts 无数据时返回空表(列齐、dtype 对、不写空缓存); serve.py 页面路由加兜底 —— 缺产物返回结构化"无产物"页(HTTP 200,与工作台侧 err=no_products 同口径, 页面上写清缺哪件、怎么补:放数据 → 重算 → 核验),其它异常返回 500 页 + 日志,不再出现"连接被直接掐断"。 实测:清空 outputs/ 后起 CMS,GET /?theme=light → HTTP 200 / 48 KB,页面正常显示"无产物"文案。 ★ 2026-09-17 同日补:工作台侧同类静默空白也一起修了 —— /static/*(总览「全场状态」的 iframe 就是 /static/index.html,正本是产物 outputs/<场>/windscada/index.html)与 /turbines/* 缺件时原先回一个 裸 404 文本,浏览器里表现为"这一块没有信息",页面上找不出任何原因(用户当天报的正是这个: "总览『全场状态 一格一台 · 点格定位』没有信息")。现在两者都走 _gap_page():HTTP 200 + 结构化缺件页, 写明缺哪一件、属于哪条产物线、怎么补(有生成端 → rebuild_all.py;包内无生成端 → products_restore_missing.py --stash <含产物的交付包.zip>),并给出运维控制台与 healthz 入口。 实测:/detail/static/不存在的页.html → HTTP 200 / 1,684 B 的缺件说明页(不再是 404)。
  2. 开箱验证会先把用户的组件停掉:scripts/pack_dist.py --verify 的核验步骤要求 18033/18020/18791/18792/64292 空闲(因为 guanlan_gateway.ROUTES 的上游端口当时是写死的), 于是验证窗口里用户正在用的 /detail/、/cms/ 就是断的 —— 用户点「登录」正好撞上。 处置:ROUTES 的上游端口改为可被 configs/serve.json 覆盖(端口单一真源,顺带补上用户令 2 的口径), run_and_probe 于是把副本整套换到空闲端口再核验 —— 主实例照常跑,谁也不打扰谁; 找不到空闲端口才降级为"跳过启动核验"。 实测(主实例全程在跑,验证前后都是 6/7 ok):--verify 打印 副本改用独立端口: gateway=28700, detail=28900, cms=29000, sim=29100, sim_sys=29200, viewer=28800 → 页面 5/5 可用(/·/detail/·/cms/·/ops·/healthz;/cms/ 因为有新的"无产物"页也成了 200) → 收尾只停副本自己的进程;主实例 /detail/v2 验证后仍 200 / 229,643 B,副本端口无一残留。

12. 门户逐页识别:内容是什么、是不是产物、输出在哪

判据三条(与 §10 同一套,登记表在 configs/portal_pages.yaml,检查器 scripts/pages_audit.py): ① 内容是不是由 data/raw 算出来的(是 ⇒ 产物性质);② 页面是现场读产物还是烘成快照(现场读 ⇒ 永远一致,不用管); ③ 交付件按版本冻结还是随数据滚(冻结 ⇒ 不是运行时产物,但要版本登记)。

页面 / 子页 内容是什么 由输入数据重算? 是产物? 若是产物: 输出路径 + 生成端 现状与管理
#index 总览 产品承诺 + 入口导航(外壳静态文本) 否 不是 — 受管外壳(release/portal_src/shell.html)
#architecture 系统架构 分层架构叙述 否 不是 — 同上
#method 方法 方法论叙述 否 不是 — 同上
#findings 经验发现 公司级经验("59 项内部检查 / 60+ 风场")+ 八条教训 否 不是 — 静态;若将来放本场站计数,必须改 data-derived 并登记 source
#case_hydraulic 案例·液压 案例叙述 否 不是 — 静态
#cms 振动·CMS iframe → 组件 :18020(CMS 诊断页 + 逐台页 + 工作台) 是(组件现场读产物) 页面本体不是;它读的 CMS 页面件是产物 outputs/<场>/windcms/*.html、report_CMS振动状态评估报告_*.md、report.md、workbench.html 产物管理: 这些件由 windcms.py report 生成;本包缺六层链产物故不在重算链里重建(vib_raw_build.py --with-report 会掉内容,见 §7),交付时按"随包件"补齐并登记为 shipped
#sim 仿真与回放 机理/图纸叙述 + 6 个链接 否 不是 — 输入是图纸/机型参数(configs/machine_packs、release/viewer),与 data/raw 无关
└ 控制律仿真台(5 页) 图纸派生的物理回路仿真台 否 不是 — 冻结资料包 release/如东SWT40_控制律仿真台_20260906.zip(release/sim_sys_server.py 从 zip 现读)
└ 三维拆装工作台 三维模型/装配(three.js) 否 不是 — 交付资产 release/viewer/**(由图纸 + esbuild 构建,rev= 是评审版本号)
└ 判据/仪表台面板(内嵌 5 块) 固定判据说明 + 产物脚注引用 否(正文) 正文不是;被引用的产物是 引用 outputs/<场>/yaw_verify/findings.json(本包无此目录,见 §7 缺口) data-citing:正文不随数据变,但引用必须在位(审计查悬空)
└ 实际运行回放面板(内嵌) 把实际 1 分钟记录与证据包烘进页面 是 是(快照) 源 outputs/<场>/cleaned/turbine_1min.parquet + 内嵌 source_sha256 产物管理: 已登记 data-derived + 陈旧检测(sha 与当前产物比;源不在包,故现在只能按生成时间判)
#documents 交付文档 交付件导航(0 fetch) 否 不是 — 静态导航
└ 治理清单分册(12 + 全册 + 正式报告) 分析结论的冻结交付版 否(按交付版本) 不是(冻结交付件) — 文件名自带版本号+日期(v1.2_2026-09-01 等);frozen-delivery 规则校验版本/日期
└ 全场状态一览(脱敏) 场站实际状态快照 是 是 无生成端、无指纹(release/如东/全场状态一览_脱敏_20260825.html) 待裁定: 补生成端并加 source_sha256(推荐),或降级为冻结交付件并接受过期(现登记 known_gap)
└ 如东取数单 "要哪些数据"的清单,明列产物路径 是 是 无生成端(release/如东/如东取数单_2026-08-21.html) 同上;审计另查出它引用的 cleaned/、structured/ 两棵树不在本包
#admin 系统状态 外壳 JS 拉 /healthz 渲染 是(实时) 不是(无快照) — live
#recalc 数据重算 门户内嵌 /ops/recalc 是(实时) 不是 — live
#login 登录 链接到工作台 :18033/v2 是(实时) 不是 — live
内嵌交付件(28 个模板) 报告/清单/面板正文 少数是(3 件带数据指纹/时间戳) 1 件是(回放面板) 见上 其余按 frozen-delivery/static 登记

"纳入产物管理"的四件事(§10 已述,这里补页面侧产物):① 登记(portal_pages.yaml);② 溯源(source + source_sha256 或生成时间);③ 陈旧检测(pages_audit.py rc=7);④ 进链(rebuild_all.py ⑧b 重装门户 + ⑧c 页面审计)。 页面侧产物(outputs/<场>/windcms/*)另有一条硬要求:它们必须出现在产物台账 _provenance.json 里(见 §13.4 的检查)。

12.2 全部网页总清点(自动生成,勿手改)

共 262 项(门户锚点 / 工作台标签 / 组件页面件 / 组件结论件 / 产物里的过程留痕 / 交付页 / 三维页 / 门户内嵌件 / 仿真资料包页面),其中判定为产物的 178 项;未被管起来的 0 项。判据:产物树里的件看"在不在产物台账里",网页看"在不在页面登记表里"。

来源 页面/子页 是产物? 依据 已登记?
门户锚点 #index 总览 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #architecture 系统架构 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #method 方法 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #findings 经验发现 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #case_hydraulic 案例·液压 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #cms 振动·CMS 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #sim 仿真与回放 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #documents 交付文档 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #admin 系统状态 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #recalc 数据重算 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
门户锚点 #login 登录 不是 外壳页(静态/iframe/链接);逐条判定见 §10 与 §12 ✔
工作台标签 tab=overview 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=energy 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=component 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=vibration 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=generation 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=fault 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=decision 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=assistant 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=report 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
工作台标签 tab=system 不是 SPA 一次装载、按 tab 现场 fetch 产物 ⇒ 页面本体不存快照 ✔
组件结论件 (.md,页面会渲染) guanlan/derived/report_summary.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/gear_grade_38.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/OEM特征频率复跑_2026-08-17.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/交接_振动→发电机分析.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/交接_振动→齿轮箱_行星级中速轴判级.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/交接_振动→齿轮箱分析.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/取数与送检需求单_20260827.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/回测训练_历史锚三轮深挖_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
组件结论件 (.md,页面会渲染) m5_cms_tcm/对比_大生科技传动链报告_20260311.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/振动板块_windscada融合面_设计v1.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/振动算法复跑记录_2026-08-15.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
组件结论件 (.md,页面会渲染) m5_cms_tcm/收敛报告_如东TCM振动诊断_v4.2.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) m5_cms_tcm/收敛报告_如东TCM振动诊断_v4.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) m5_cms_tcm/收敛报告_如东TCM振动诊断_v5.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/给windscada线_TCM16验收槽规格_20260827.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/给windscada线_工单库缺2024-11之后_20260827.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/给windscada线_油样复核回执_20260827.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/给windscada线_温度轴复核回执_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/给windscada线_温度轴核查请求_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/给windscada线_色标已改_20260828.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) m5_cms_tcm/综合_振动最新×发电机齿轮箱原分析_2026-08-13.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
组件结论件 (.md,页面会渲染) m5_cms_tcm/诊断报告_TCM振动_202601窗.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) m5_cms_tcm/诊断报告_TCM振动_六窗终版.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) m5_cms_tcm/频谱分析报告_轴承特征频率_2026-08-13.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E3_candidate_closure/plan.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E3_candidate_closure/result.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E3_candidate_closure/归宿草案.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E5_coverage_table/plan.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E5_coverage_table/result.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E5_coverage_table/主表.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E8_rudong_rebuild/plan.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E8_rudong_rebuild/result.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E8_rudong_rebuild/S0_盘点.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E8_rudong_rebuild/S1_能问表对拍.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E8_rudong_rebuild/S3_C7干跑.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) paradigm_r1/experiments/E8_rudong_rebuild/S5_对表.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_dual_review_20260726_payload脱敏.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260822_model_additions.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260822_sealed_predictions.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260823_gende16_33.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260823_gende16_33_addendum.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260823_gende16_33_sealed_predictions.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260824_shared_component_fleet.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260824_shared_component_fleet_sealed_predictions.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260824_wtg12_outlier.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_review_pkg_20260824_wtg12_outlier_sealed.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_u6_review_20260810/封存预测_审前.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_u6_review_20260810/送审包_as_sent.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_u6_送审包_20260810.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_u6_送审包_r2_20260810.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_u8_sealed_predictions.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/_u8_送审包_20260812.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/local_review/disposition_14b_20260824.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/local_review/disposition_32b_20260824.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/local_review/review_pkg_20260824_grading_calibration.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260831/lens_A_口径统计.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260831/lens_B_交付表述.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260831/pkg.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260831/SEALED_我的预测_勿送审.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260831/处置台账.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260831/夜间工作总结_0901.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260901/lens_A_事实核查.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260901/lens_B_读者与风险.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260901/pkg_r2.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260901/SEALED_我的预测_勿送审.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260901/处置台账.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/review_20260901/复审处置_r2.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) sop/轴4返修_AdvCI两方向_2026-07-27.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/_争议裁决_封存预判_勿入送审包.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/_争议裁决送审包_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/Codex回复_rms200_mask_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/Codex复现闸回传_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/争议裁决记录_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/复现审计.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/建议采纳清单_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/振动模型规划.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/给Codex的同步_R5_WTG23包络滞后_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/给Codex的同步_rms200证据边界_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/给Codex的回传.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/给Codex的回传_R2_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/给Codex的回信_如东振动研究历程梳理_20260905.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) tcm_compatible_replay/claude_handoff_20260823/给Codex的问题_rms200_mask_20260823.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
组件页面件 (.html) windcms/index.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/index_eng.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/overview.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件结论件 (.md,页面会渲染) windcms/report.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG01.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG02.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG03.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG04.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG05.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG06.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG07.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG08.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG09.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG10.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG11.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG12.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG13.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG14.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG15.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG16.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG17.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG18.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG19.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG20.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG21.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG22.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG23.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG24.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG25.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG26.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG27.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG28.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG29.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG30.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG31.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG32.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG33.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG34.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG35.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG36.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG37.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windcms/turbines/WTG38.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件结论件 (.md,页面会渲染) windcms/报告_CMS振动状态评估报告_2026-08-23.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) windcms/报告_CMS振动状态评估报告_2026-08-24.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) windcms/报告_CMS振动状态评估报告_2026-08-26.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) windcms/报告_CMS振动状态评估报告_2026-08-28.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
组件结论件 (.md,页面会渲染) windcms/评审_离线系统诊断准确度_20260824.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/_design_audit_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/_selfaudit_20260825.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
组件页面件 (.html) windscada/index.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/review/_sealed_predictions_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/review/审核处置_R1_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/review/审核处置_R2_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/review/审核处置_R3_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/review/送审包_windscada_v1.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/review/送审包_windscada_v2.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG01.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG02.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG03.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG04.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG05.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG06.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG07.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG08.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG09.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG10.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG11.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG12.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG13.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG14.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG15.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG16.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG17.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG18.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG19.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG20.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG21.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG22.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG23.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG24.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG25.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG26.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG27.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG28.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG29.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG30.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG31.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG32.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG33.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG34.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG35.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG36.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG37.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
组件页面件 (.html) windscada/turbines/WTG38.html 是 位于 outputs/<场>/ ⇒ 本身就是产物,且是被服务直接当页面呈现的件;台账: 在 ✔
产物里的过程留痕 (.md,非网页) windscada/给振动线_温度轴回复_20260826.md 是 产物,但没有任何页面呈现它(给下游线的交接/复跑记录);台账: 在 ✔
组件结论件 (.md,页面会渲染) windscada/送审_系统结论与判定_20260828.md 是 产物(结论文本);工作台 report tab / CMS report.html 会渲染它;台账: 在 ✔
交付页 (release/如东) 如东/全场状态一览_脱敏_20260825.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东取数单_2026-08-21/如东取数单_2026-08-21.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东取数单_2026-08-21/如东现场一页单.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东取数单_2026-08-21/更正页_2026-08-21晚.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东取数单_2026-08-21.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/01_正式报告/如东_液压系统技术报告_v1.0_2026-09-01.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/02_治理清单/如东_液压系统治理清单_v1.2_2026-09-01.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/02_治理清单/如东风场治理清单_全册_v4.5_2026-09-06.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/00_总目录.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/01_如东_液压系统治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/02_如东_变桨执行治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/03_如东_偏航系统治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/04_如东_变流器治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/05_如东_主控与传感网治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/06_如东_电气并网治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/07_如东_塔架与基础治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/08_如东_润滑与冷却辅助治理清单_v1.1_2026-09-06.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/09_如东_CMS-TCM监测面治理清单_v1.1_2026-09-06.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/10_如东_参数与管理治理清单_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东治理清单_交付_20260901/03_分册全套/11_如东_故障负担榜_v1.0_2026-09-05.html 不是 客户交付件,按版本冻结 ✔
交付页 (release/如东) 如东/如东现场一页单_2026-08-21.html 不是 客户交付件,按版本冻结 ✔
三维页 (release/viewer) viewer/bedframe-demo.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/guanlan-publish-findings.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/guanlan-publish-index.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/guanlan-publish-method.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/guanlan-publish-services.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/guanlan-publish-turbine-explorer.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/guanlan-publish-whitepaper.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/index.html 不是 图纸/模型派生的三维资产 ✔
三维页 (release/viewer) viewer/unit-workbench.html 不是 图纸/模型派生的三维资产 ✔
门户内嵌件 portal_src/templates/tpl-coverage_ch0_zh.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-0.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-1.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-10.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-11.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-2.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-3.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-4.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-5.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-6.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-7.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-8.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-governance-9.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-report-主轴承.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-report-发电机.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-report-变桨.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-report-齿轮箱.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-sc1-local.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-standard_panel_zh.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-swt-subsystem-1.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-swt-subsystem-2.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-swt-subsystem-3.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-swt-subsystem-4.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-swt40_控制律仪表台_外发版.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-U6_变桨液压仿真台.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-U6_液压公共站健康报告_客户版.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-如东传动链实际运行诊断_单文件版.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
门户内嵌件 portal_src/templates/tpl-如东海上风电场_整机综合诊断与风险评估_主轴冲击深挖修订版V2.1.html.html —(逐条判定) 内嵌交付件(§10 逐条判定;个别为数据派生) ✔
仿真资料包 (zip) 2_传动链与功率.html 不是 图纸派生的冻结资料包(sim_sys_server 从 zip 现读) ✔
仿真资料包 (zip) 4_变桨轮毂液压.html 不是 图纸派生的冻结资料包(sim_sys_server 从 zip 现读) ✔
仿真资料包 (zip) 0_四系统合页.html 不是 图纸派生的冻结资料包(sim_sys_server 从 zip 现读) ✔
仿真资料包 (zip) 3_发电机与热管理.html 不是 图纸派生的冻结资料包(sim_sys_server 从 zip 现读) ✔
仿真资料包 (zip) 1_偏航系统.html 不是 图纸派生的冻结资料包(sim_sys_server 从 zip 现读) ✔

13. 输入数据 → 功能 → 算法 → 产物(关系与实现逻辑)

13.1 关系总表(每条都能指到脚本与产物)

输入数据(data/raw/<场>/…) 功能 实现(脚本) 算法/实现逻辑要点 产物(outputs/<场>/…) 验收锚点
故障报警/*.xls(实为 SpreadsheetML XML) 报警台账 scripts/windscada_alarms_ingest.py 逐行解析 XML 报警事件 → 统一列(t_on/机组/文本/类型);列映射按随包件锁定 windscada/alarms.parquet 39,211 行
风机故障记录/**/*.xls(x)(78 张月度/年度同模板表) 检修工单台账 scripts/windscada_workorder_ingest.py 前两行是说明与分组表头、第 3 行列名、数据从第 4 行起;str(cell) 原样字符串化(与随包件一致) windscada/workorders.parquet 5,876 行
油样报告/<台号>/<部件>/*.pdf 油液化验索引 scripts/windscada_watch_channels_build.py 从文件名抽全部关键字段(DDMMYYYY_sampleid_项目_台号_部件),不读 PDF 正文 windscada/oil_samples_index.parquet 404 行(缺 2026-07 批 102 行,源件不在包)
scada_10min/<机组>.csv(14 GB) L0 标准仓 + 10 个 SCADA 构建器 scripts/rebuild_from_raw.py --scada 逐台流式读 CSV → 统一列 → 按窗聚合/派生(温度、功率曲线、损失、控制、偏航、热链、停机事件…) windscada/{temp_bins,temp_monthly,powercurve_bins,powercurve_dev,loss_monthly,control_*,yaw_daily,stop_events,thermal_chain,system_aux,curve_*}.parquet temp_monthly 19,494 行
同上(月度聚合) 月度派生件(15 件无生成端的补上) scripts/windscada_monthly_build.py 从 10min 件重新聚合并逐值对齐随包件(随包件当标准答案,--verify 报命中率);规则是反推+全量比对确认 windscada/temp_monthly、loss_monthly、ctrl_monthly 等同族 逐值相等才落盘
windcms/**/*_decode.json(Brande TCM 导出,25,680 件) 振动窗索引 scripts/rudong_tcm_index.py 解析 body.body[<ISO时间戳>]=[{Record:…}];机组取 Record.Location/LocationName;一行一条记录 m5_cms_tcm/windows/<窗>/index.parquet 窗 w0316 2,066,686 行
同上 振动谱库 scripts/rudong_tcm_spectra.py 每条谱写进 npz 分片 + spectra_meta 指针(shard/shard_row/x_offset/x_delta);取数 x = x_offset + arange(n)*x_delta m5_cms_tcm/windows/<窗>/spectra/*.npz + spectra_meta.parquet 窗 w0316 420,742 条谱 / 1,712 片
同上 + 厂家报告 docx 振动侧一键摄入 scripts/vib_raw_build.py(可 --with-report) 编排"索引→谱→清单";默认不重生成报告(缺六层链,重生成会掉内容 −97%,见 §7) m5_cms_tcm/{index,spectra_meta,vib_raw_manifest}.… vib_raw_manifest.json 自述缺口
厂家报告 docx(PDF 是扫描件,无文本层) 报告转录 scripts/vib_reports_build.py docx 有文本层 ⇒ 逐字转录判级为结构化件;扫描件 PDF 只登记归档、不进判级 windcms/报告_CMS*.md、windcms/*.json 转录件与报告逐条对齐
西门子4.0技术资料/**(含 4 个台账 xlsx) 本体知识层 python -m src.ontology.kb_ingest 四条链(预防/抢修/故障树/计划)图谱化:手册→AlarmCode+作业指导;对译表→中文名;维护指导书→预防性任务;每条声明出处 ontology/objects.json 等 码覆盖 263/322
上述全部产物 + 判级矩阵 本体铺开 python -m src.ontology.populate 零判级权:只转录 L1 正本(taxonomy.system_matrix / fusion_table);每条 Verdict 带审级与 claim_window,Evidence 指回 L1 产物;同部件多源判级并存不合并 ontology/objects.json(9,613 对象) ontology.audit 0 问题
链盘(运行时 /api/fleet) 决策链进度 python -m src.ontology.chain_ingest 每台一个 Decision 对象记六步状态/卡点/下一步(不是 Evidence——它是系统推出来的处置状态) ontology/objects.json 需服务在跑
component_history.json + 链盘 趋势/闭环证据 python -m src.ontology.trend_ingest 在升=末三窗/早三窗 ≥1.3(未闭环);闭环=检出→换件→复测回落;两类都建 Evidence 并挂 about→turbine、affects→component ontology/objects.json 幂等覆盖
sop/findings.json(119 条) 事实契约 + 派生件 scripts/guanlan_facts_contract.py 每条结论 = claim_id + 脱敏来源引用(文件@sha16 + 章节)+ 本条 sha256 + 时间窗 + 聚合层级 + 裁决(六枚举) guanlan/facts_contract_v0.json → 派生 guanlan/derived/{portal_claims,detail_cards,qa_refs,report_summary} 探针:改一条 claim → 四个消费者全变
门户外壳 + 28 个内嵌件 + 契约派生件 门户装配 scripts/portal_build.py(+ guanlan_portal_inject_claims.py) 外壳受管、内嵌件与合成结果不入库;装配先写临时文件再原子替换,失败不破坏在服务的门户;行尾强制 LF release/portal.html(20.2 MB) --verify 逐字节一致
上述全部 产物清点 + 呼应校验 scripts/inventory_products.py --check 对每类输入算跨度/条数,与它喂出来的产物逐项对拍(产物跨度必须落在输入跨度内) docs/系统设计说明.md §2(自动块) rc=0

13.2 端到端顺序(为什么是这个顺序)

scripts/rebuild_all.py 把 14 步写死(各步幂等,可整条重跑):

① 放数据 place_raw_data.py            原样搬 现场包 → data/raw/<场>/(同尺寸跳过,冲突拦下)
①b 输入扫描 raw_scan.py                 逐族指纹(件数/体积/最新/清单摘要/子目录集) ← data/raw 递归;
                                      发现新增/变化就列出"该跑哪几步", 并把本次指纹记成新基线
                                      (rc 0 无变化 · 4 有新增/变化 · 5 首次没快照; 4 不是失败)
② 三门台账 rebuild_from_raw.py         报警/工单/油样 ← 台账类源件(**产物=当前源件的函数**)
③ SCADA 侧 rebuild_from_raw.py --scada 10 个构建器 ← 14 GB 10min CSV(约 15 分钟)
④ 月度派生件 windscada_monthly_build.py 逐值对齐随包件才落盘(缺基线返回 5 只跳过"等价验收")
④b 振动摄入 vib_raw_build.py           索引/谱库 ← TCM 导出(约 14 分钟)
⑤ 补齐随包件 products_restore_missing.py 无生成端的那批(源 = 交付包 zip;缺源返回 6 跳过)
⑥ 重启组件服务                         只重启组件、保留网关(chain_ingest 要吃重新加载的产物)
⑦ 本体六步 kb_ingest → populate → chain_ingest → trend_ingest → 检索索引 → 实机参数表
⑧ 审计 ontology.audit(期望 0 问题) + ⑧b 重装门户(把新产物灌进结论段) + ⑧c 页面归口审计

依赖为什么不能换:⑦ 的 populate 要吃 ②③④④b⑤ 的产物;⑤ 必须在 ⑥ 之前(本体层要读补齐后的 L1 件); ⑧b 必须在产物全部就位之后(门户的结论段是产物的渲染结果)。

13.3 前端取数与"即时进页面"

服务 读法 产物更新后
工作台 :18033(scripts/windscada_serve.py) 首请求把产物读进内存 _CACHE;每次取数前比产物指纹(相关文件的 (路径, mtime_ns, size) 摘要,TTL 2 s) 指纹一变自动重载,不用重启
CMS :18020(src/windcms/serve.py) 直接读产物页面件 + spectra_meta 指纹(src/windcms/data.py::_mtime_stamp) 同上
门户 :28084(scripts/guanlan_gateway.py) 门户按 (mtime,size) 缓存;契约结论段由 §13.1 的事实契约派生 需 portal_build.py 重装(⑧b)
运维控制台 /ops、/ops/recalc 后端即真实状态;Cache-Control: no-store 刷新即最新

13.4 产物管理的四条硬要求(机器执行)

  1. 路径单一真源:产物只落 outputs/<场>/…,代码只经 src/paths.py 取(§3)。
  2. 逐件来源台账:outputs/<场>/_provenance.json 记 raw-derived(由 data/raw 重算,含验证依据) 或 shipped(包内无生成端,用随包件补齐);raw-derived 另由 _derived_manifest.json 自登记。
  3. 呼应校验:inventory_products.py --check —— 产物跨度必须落在输入跨度内,rc≠0 即不符。
  4. 页面侧产物同样要登记:configs/portal_pages.yaml 里 data-derived 的页面件,其 source(产物路径) 必须在 _provenance.json 里出现,否则 pages_audit.py 报"未纳入产物台账"。

13.5 没有生成端的那批(shipped)与补齐办法

pitch_daily、pc_monthly_bins、duty_monthly、thermal_monthly、sector_power、yaw_*、genbearing_monthly、 mblub_monthly、structure.parquet、watch_channels_monthly、振动 handoff_vibration_v2.json / component_history.json / baseline_38.json、sop/**、paradigm_r1/**、guanlan/**、CMS 页面件 —— 全库只有读取方、 0 处写入方(§7)。唯一来源是"含产物的交付包":

python scripts/products_restore_missing.py --stash <含产物的包.zip>    # 只补缺件, 不覆盖 raw 重算件

2026-09-17 实测:用旧版含产物包 guanlan-rudong-v2_0.2.0_test_win64.zip 一次补齐 567 件(其中 39 件是日志, 已按 §11.2 归位到 logs/build/),补齐后与旧包逐字节相同 538 件。 教训(写进 §9 的口径里):交付包默认不含产物 + 清除产物不留备份 ⇒ 这批件一旦清掉就没有来源; 要留退路就用 scripts/pack_dist.py --with-products 打一份带产物的包存档。


14. 服务化安装(Windows 服务 / systemd)与版本检查(2026-09-17 用户令 1)

14.1 装成什么

平台 形态 服务体 注册器 前置
Windows Windows 服务(SCM 托管,SERVICE_WIN32_OWN_PROCESS) scripts/win_service.py(ctypes 直连 advapi32,不依赖 pywin32) scripts/service_ctl.py install(内部 sc.exe create/description/failure/start) 管理员
Linux systemd 单元 guanlan.service(Type=simple + Restart=always) scripts/service_main.py(前台守护) scripts/service_ctl.py install(写 /etc/systemd/system/guanlan.service + systemctl enable --now) root
Windows(备选) 当前用户登录自启(HKCU\...\Run)——不需要管理员 scripts/guanlan_start_hidden.py(无窗口启动器) scripts/service_ctl.py install --user-login 无

为什么 Windows 服务体不用 sc create python xxx.py:SCM 要求被拉起的进程跟它握手 (StartServiceCtrlDispatcher + 控制处理回调),否则起服务报 1053。 实现它有两条路:pywin32(要加新依赖,破坏"单包离线零新依赖")或 ctypes 直连(本实现,只用标准库)。

两平台共用一个工作体(scripts/service_worker.py):起 = guanlan.py serve(幂等,缺谁补谁)、 守护 = 每 15 s 巡检一次同样命令(组件掉了自动拉起)、停 = guanlan.py stop。 这样"服务起的"与"手工起的"口径完全一致,不会出现两套行为。

14.2 版本检查与重装确认(安装时)

版本单一真源 = src/version.py::VERSION(打包器、安装脚本、guanlan.py check、文档都以它为准)。 安装脚本在动手之前做一次检查,读 <安装根>/install-info.json(上次安装写下的记录):

情形 提示 动作
没装过 未检测到已安装 → 全新安装 vX 直接装
版本相同 已安装 vX,与本包版本相同;重装会重建 .venv、覆盖程序文件(data/、outputs/ 不动) 问是否重装
已装更旧 v旧 → v新(升级) 问是否重装
已装更新 已装 v新 > 本包 v旧 ⇒ 降级警告:"除非确知原因,不要用旧包覆盖新装" 问(并明确警告)
老安装无版本记录 按"重装/升级"处理 问
  • 交互式:Read-Host / read 问 y 才继续;非交互(stdin 被重定向,例如自动化核验)自动继续并打印说明;
  • 跳过询问:install.bat -Force(PowerShell 侧 -Force)/ sh install.sh --force;
  • 装完写 install-info.json:version / installed_at / python / mode(manual|windows-service|systemd|startup-entry);
  • guanlan.py check 增一行显示"已装版本 vs 本包版本"。

14.3 没有权限时怎么办(本次实机就是这种情况)

本机当前账号不是管理员,所以"真注册服务"这一步无法在此验证。为此该功能自带三条无权限自证路径:

python scripts/service_ctl.py status                 # 装没装/在跑没在跑(免权限)
python scripts/service_ctl.py install --dry-run      # 前置检查 + 逐条打印将执行的命令 + 打印将写入的 systemd 单元全文
python scripts/win_service.py --debug --once          # 免权限跑一轮守护(起组件→巡检→报"网关在听")
python scripts/win_service.py                        # 直跑服务体: 报 "不是由 SCM 启动(WinError 1063)" ⇒ SCM 接口调用正确

权限不足时 service_ctl.py 不抛 Access is denied,而是打印三条可执行建议(管理员重试 / 先 --dry-run / 改用 --user-login)并返回 rc=3。实机输出(本机,非管理员):

$ python scripts/service_ctl.py install --dry-run
   前置检查: 通过
   $ sc.exe create guanlan binPath= "…\.venv\Scripts\python.exe" "…\scripts\win_service.py" start= auto DisplayName= "观澜·如东样板 v2 …"
   $ sc.exe description guanlan "观澜·如东样板 v2 v0.4.0 本机离线版(观澜风电资产智能)"
   $ sc.exe failure guanlan reset= 86400 actions= restart/60000/restart/60000/restart/60000
   $ sc.exe start guanlan
$ python scripts/win_service.py --debug --once
   一轮巡检结果: 网关在听

14.4 与"手工启动"的关系

装了服务之后不必再双击快捷方式:开机会自动起(Windows 服务)/ systemctl start guanlan(Linux)。 手工路径仍然可用(快捷方式、start.bat、guanlan.py serve),两者互不冲突—— 服务只是"代表整套系统守着"的那一个进程。停止:service_ctl.py stop(或 stop.bat)。

14.5 卸载(uninstall.bat / uninstall.sh;2026-09-17 用户令)

入口两个壳脚本 + 同一份实现 scripts/guanlan_uninstall.py(与 install.bat/install.sh→install.ps1 同构, 免得两平台各写一遍、过两个月行为不一致)。默认只拆"本程序装上去的东西",顺序与依据:

步骤 做什么 依据/注意
① 停服务 service_ctl.py stop → guanlan.py stop 先停常驻体再停手工起的;没注册过就跳过(幂等)
② 注销服务 sc.exe stop/delete guanlan / systemctl disable --now + 删单元文件 需管理员/root;缺权限时照做其余步骤并打印手工命令(rc=3)
③ 撤开机自启 删 HKCU\...\Run\guanlan / systemd 用户单元 + autostart 不需要权限;注册表异常不中断整场卸载
④ 删快捷方式 启动观澜.lnk、start_guanlan.lnk(安装根 + 桌面 + ~/.local/share/applications) 与 §14.2 里 install.ps1 建的那两份一一对应
⑤ 清运行态 run/、残留 *.pid run/pids.json 换台机器就是无效引用(§9 ③ 同理)
⑥ 删安装记录 install-info.json 它是"已装的是哪一版"的唯一凭据;删掉 = 这台机器回到"没装过",重装不再提示版本差异
⑦ --purge 额外删 data/ outputs/ logs/ run/ runtime/ .venv/ 会再问一次(默认答否);--keep-logs 留日志
⑧ --all 额外删程序本体(含整个安装根) 尽力而为,删不掉的逐条列出 + 给一条收尾命令

两条设计取舍(都不是省事,是踩过/想过才这么定):

  1. 默认不删数据、不删程序目录。data/(原始件)、outputs/(产物)是现场资产,"卸载程序 ≠ 销毁数据"; 程序目录不自己删,是因为 Windows 上正在跑的 python.exe 就在 .venv 里,删自己必然只删一半, 留个"删了一半的目录"比留着整棵目录更难查。uninstall.bat 特意先 cd /d "%TEMP%" 再调 Python (Windows 不允许删除"某个活进程的当前目录",而 --all 要删的正是这棵目录;所有路径都用绝对路径, 不依赖 cwd),因此带 --all 时能连安装根一起删掉——实测 安装根还在? False。
  2. --dry-run 不是摆设,是自证口径。--selftest 做两类断言:内容断言(六个步骤 + 保留清单一个不缺)、 副作用断言(跑完 dry-run 后 install-info.json/run/data/outputs/logs/.venv 的存在性与 字节数完全不变)。开箱验证(pack_dist.py --verify 第 ③b 步)会在刚装好的副本上真跑一遍 卸载器,并断言"安装记录已删 / 运行态已清 / 快捷方式已撤 / 数据与产物一件不少 / 端口已释放"。

实测证据(本机,2026-09-17):--selftest 全绿;在临时副本上真卸载 rc=0 且 install-info.json/run//两个 .lnk 全被清掉、data+outputs 件数不变;带 BOM 的 install-info.json(PowerShell 的 Set-Content -Encoding UTF8 会写 BOM)现在也能读出来 (read_installed 改按 utf-8-sig 读 —— 带 BOM 按 utf-8 读会抛异常,后果是"明明装了却报没装过", 版本检查直接失灵)。


15. 版本管理与打包命名(2026-09-17 用户令)

15.1 规则

编号 v<大版本号>.<中版本号>.<小版本号>,例:v2.6.0(本包即此版本)。

级别 含义(用户令原文)
大版本号 系统解决方案、架构或核心功能改变
中版本号 非核心功能新增、减少、修改
小版本号 消缺完善

怎么改:改动落在"解决方案/架构/核心功能" → 大 +1(中/小归 0);落在"非核心功能的新增/减少/修改" → 中 +1(小归 0);只是"消缺完善" → 小 +1;一次发布混了几类,取最高那一级。 机器判据是 src/version.py::level_of(旧, 新) → major|minor|patch|same。

打包文件名 = app_guanlang_v<版本号>.zip,例:app_guanlang_v2.6.0.zip (src/version.py::package_name() 是唯一出处,打包器默认用它命名;--out 可另指定)。

15.2 单一真源与三处机器守卫

版本号只写在 src/version.py 一行(VERSION)。改一处,下面全部自动跟着变:

src/version.py                     ← 唯一真源: VERSION / RULE / HISTORY / package_name()
scripts/pack_dist.py               ← 读 V.VERSION 写进 dist-manifest.json 并拼默认包名(不再自己写死)
install.ps1 / install.sh           ← 读它写 <安装根>/install-info.json,并与已装版本比对(§14.2)
guanlan.py version / check         ← 打印版本与规则;check 里校验"版本记录 md 与代码一致"
scripts/version_log.py             ← 由 HISTORY 生成 docs/版本记录.md(人读的那份)

三条守卫(都是"对不上就报错",不是靠人记得):

  1. version_log.py --check:docs/版本记录.md 的自动块必须与 HISTORY 逐字一致(rc=6 表示要重生成) —— 防"包说 v2.5.0、记录表最后一行还是 v2.4.0"。guanlan.py check 有一行盯它。
  2. 打包器读真源:pack_dist.py 不再自带 VERSION 常量(此前它写着 0.4.0,与 README 抬头、 说明书标题三处并存,改一处忘一处就会出现"包说 0.4.0、安装记录说 0.2.0")。
  3. 入口引用闭合(§11.3 的既有守卫)现在也覆盖 uninstall.bat / uninstall.sh: 它们引用的 scripts/guanlan_uninstall.py 少打进包,后果是"装得上、卸不掉"。

15.3 版本表

完整表格见 docs/版本记录.md(由 scripts/version_log.py --write 生成)。当前:

版本 日期 级别 说明
v2.7.0 2026-09-19 中(第 7 次功能性发布) 远程部署消缺:振动窗发布被杀软/索引占用不再打断整条重算链(_move_tree 退避重试 + 逐件搬 + --publish-only 恢复通道);门户可对外监听(serve.json 的 public_host,组件仍只本机);GBK 控制台打印不炸;现场改注册 Windows 服务 guanlan
v2.6.0 2026-09-19 中(第 6 次功能性发布) 观澜生成端源代码化(MDB 接入 / 六层链四步脚本 / 台账生成 claim / 页面烘焙)+ 交付包输入目录骨架(只带目录不带文件);大版本 2 = 二代架构,中 +1 = 非核心功能新增
v2.5.0 2026-09-17 中(第 5 次功能性发布) 卸载闭环 + 版本管理与打包命名 + 版本记录;大版本 2 = 二代架构(与 NAME 里的 v2 对齐),小版本 0 = 无消缺项
v0.4.0 2026-09-17 旧编号 打包默认不含数据/产物/日志/临时文件;无窗口启动;统一配置与日志;页面归口;输入数据检查;服务化
v0.2.0 2026-09-01 旧编号 含产物的旧交付基线(历史上是"包内无生成端"那批产物的补救源;2026-09-19 用户令从工作树清理 943 MB,仍留在 git 历史里可 git 取回)

★ 0.x 是旧编号体系(未按本规则),只作历史对账;从 v2.5.0 起按本节规则递增。

13.6 反向呼应审计:自输出 → 功能与算法 → 输入(自动生成,勿手改)

台账 1800 件产物逐件回溯:呼应成立 1800 件 · 不成立(无生成端)0 件 · 无法验证 0 件 · 未归类 0 件。判据:① 生成端在位 ② 输入在位 ③ 跨度 ⊆ 输入 / 机组集 ⊆ 输入 / 行数 > 0 / 计数与清单一致。

输出族(产物 glob) 件数 反向判定 功能 算法 / 生成端 输入(data/raw/<场>/) 判据 说明
m5_cms_tcm/windows/*/index.parquet 1 ✓ 呼应成立 振动摄入 · 窗索引 scripts/rudong_tcm_index.py(逐 decode JSON 解析 54 列:turbine/sensor_name/meas_name/trigger_time/rpm/condition_key/alarm_type) windcms span+turbines+rows 跨度 2026-03-16~2026-04-21 ⊆ 输入 2026-03~2026-04;机组 38 台 ⊆ 输入 38 台;件内数据行合计 2,066,686
['m5_cms_tcm/windows/*/spectra/**', 'm5_cms_tcm/windows/*/spectra_meta.parquet'] 1703 ✓ 呼应成立 振动摄入 · 谱库 scripts/rudong_tcmspectra.py(FFT 测量 → npz 幅值数组, 分片存 spectra/p<NN>/) windcms npz 1703 件谱(抽样 5 件 npz 均可读非空)· 输入源 25693 件
m5_cms_tcm/vib_raw_manifest.json 1 ✓ 呼应成立 振动摄入 · 清单 scripts/vib_raw_build.py(汇总窗/谱件数、时间跨度、missing_chain 缺口) windcms manifest 清单自记: farm=rudong, window=w0316, window_dir=outputs\rudong\m5_cms_tcm\windows\w0316
windscada/alarms.parquet 1 ✓ 呼应成立 三门台账 · 报警 scripts/windscada_alarms_ingest.py(SpreadsheetML *.xls → 事件表) 故障报警 span+rows 跨度 2025-01-01~2026-07-15 ⊆ 输入 2025-01~2026-12;件内数据行合计 39,211
windscada/workorders.parquet 1 ✓ 呼应成立 三门台账 · 工单 scripts/windscada_workorder_ingest.py(检修台账 → 工单表) 风机故障记录 span+rows 产物起点 2020-01-03 早于输入起点 2021-01(输入为年粒度, 只作参考: 台账可能含更早的历史行);跨度 2020-01-03~2026-07-08 ⊆ 输入 2021-01~2026-12;件内数据行合计 5,876
windscada/oil_samples_index.parquet 1 ✓ 呼应成立 三门台账 · 油样 scripts/windscada_watch_channels_build.py(油样 PDF → 索引) 油样报告 span+rows 跨度 2024-11-19~2025-08-29 ⊆ 输入 2024-11-19~2025-08-29;件内数据行合计 404
windscada/temp_monthly.parquet 1 ✓ 呼应成立 月度派生件 scripts/windscada_monthly_build.py(逐值对齐随包件 19,494/19,494) scada_10min span+rows 跨度 2025-01-01~2026-07-01 ⊆ 输入 2025-01-01 00:00:00~2026-07-07 00:00:00;件内数据行合计 19,494
windscada/*.parquet 13 ✓ 呼应成立 SCADA 派生分析(功率曲线/损失/曲线/控制/停机/温度/偏航/液压/热链/系统辅助) src/windscada/perf/{powercurve,availability,curves,control,faults} + subsys/{temp_nbm,yaw,hydraulic,thermal_chain} + taxonomy.py scada_10min rows 件内数据行合计 40,095
ontology/{objects.json,retrieval_index.json,turbine_params.parquet} 3 ✓ 呼应成立 本体层 · 码表/对象库/检索索引/实机参数 python -m src.ontology.kb_ingest → populate → chain_ingest → trend_ingest;src.ontology.retrieval.build;src.ontology.maintenance.refresh_params 西门子4.0技术资料 rows 件内数据行合计 1,708
['m5_cms_tcm/model_run_l6.parquet', 'm5_cms_tcm/model_run_summary.json'] 2 ✓ 呼应成立 六层链 model_run 步 · L6 过闸谱线表 + 层小结 scripts/rudong_model_run.py(候选线=oem_scan_plan 的 11 部件×{BPFI,BPFO,BSF}; 过闸=src/sop/discriminators.py::spectral_line_gates G1–G8; 定级=vib_verdict_and_writeback) m5_cms_tcm rows 件内数据行合计 15
m5_cms_tcm/fusion_38.csv 1 ✓ 呼应成立 六层链 fusion 步 · 逐台融合表(报告"融合级"列的来源) scripts/rudong_fusion_run.py::fusion_table(模型侧=model_run_l6 过闸线; CMS 侧=窗索引 RedMask/YellowMask; 裁决=src/sop/fusion_diag.py::fuse) m5_cms_tcm rows 件内数据行合计 1
m5_cms_tcm/line_energy_share.parquet 1 ✓ 呼应成立 六层链 energy_share 步 · 线能量占比 scripts/rudong_line_energy_share.py(±2bin 带内能量 ÷ 全谱能量 ×100; 候选线与分母同 oem_scan_plan) m5_cms_tcm rows 件内数据行合计 1,026
['tcm_compatible_replay/model/mask_thresholds.tsv', 'tcm_compatible_replay/model/mask_thresholds.说明.json'] 2 ✓ 呼应成立 TCM 掩码阈值件 (逐台逐测点逐工况的黄/红线) scripts/tcm_mask_thresholds_build.py(中位 + 3/6×MAD 自适应基线, 基线先净化, 样本门 n≥30; 调 src/sop/discriminators.py::adaptive_baseline_threshold 单一实现) m5_cms_tcm rows 件内数据行合计 2
ontology/release_*/* 5 ✓ 呼应成立 本体发布层 r1/r2(网关 /release/ 只读暴露) scripts/ontology_release_build.py(对象库投影: r1 契约层 = objects.json 快照 + manifest; r2 = 同快照 + layering.json 分层覆盖声明, 不合并主库) 西门子4.0技术资料 rows 件内数据行合计 5
m5_cms_tcm/fleet_scalar_z.parquet 1 ✓ 呼应成立 振动线 · 全场标量 z 值 (fusion 步出件) scripts/rudong_fusion_run.py(逆向工程实现: 窗索引 scalar_value → 同 (传感器,测量,工况) 族中位 → 稳健 z=(val−med)/(1.4826·MAD); 与随包样件 8,887 行逐值对拍 val 列 100% 一致) m5_cms_tcm rows 件内数据行合计 32,580
m5_cms_tcm/handoff_vibration_v2.json 1 ✓ 呼应成立 融合面 handoff (观澜自算, 同结构于振动线手交件) scripts/rudong_fusion_handoff.py(报告状态等级 + fusion_38 融合裁决 + L6 过闸线 → 同结构, meta.from 标明"观澜自算") m5_cms_tcm rows 件内数据行合计 1
['m5_cms_tcm/component_history.json', 'm5_cms_tcm/baseline_38.json'] 2 ✓ 呼应成立 振动在升/换件闭环历史 + 三层基线(自 / 机群 / 绝对) scripts/component_history_build.py(逐窗标量 x_self → 末三窗/早三窗在升 + 换件前后闭环; 窗不足时判据不可算则如实空表)+ scripts/baseline_38_build.py(健康期分段中位 + 正常带 + relMAD → 自基线, 机群同段中位 → fleet 基线, ISO 20816-3 → 绝对限) windcms rows 件内数据行合计 2
windscada/index.html 1 ✓ 呼应成立 总览「全场状态」页 (单文件: 接口响应烤进页面, 离线可开) scripts/windscada_overview_build.py(ctx 取数函数与在线 /api/* 同一实现; 经 src/windscada/ui/snapshot.py::bake 烘焙) scada_10min rows 件内数据行合计 1
windcms/cache/* 1 ✓ 呼应成立 CMS 报告侧 · 标量缓存 src/windcms/data.py::load_scalars(窗索引 → 标量数组, 按内容哈希命名落 cache/) windcms rows 件内数据行合计 168,880
['windcms/报告_CMS振动状态评估报告_*.md', 'windcms/报告_CMS振动状态评估报告_*.docx', 'windcms/登记_三轴_38台.csv'] 3 ✓ 呼应成立 CMS 标准模版评估报告 (三轴: 状态等级 × 证据状态 × 行动等级建议) src/windcms/report_std.py::build(结构化判级 → 报告 md + docx + 登记 csv; 由 scripts/windcms.py report 调用) windcms rows 件内数据行合计 3
['windcms/report.md', 'windcms/index.html', 'windcms/index_eng.html', 'windcms/overview.html', 'windcms/turbines/*'] 42 ✓ 呼应成立 CMS 诊断总览页 + 逐台页 + 结构化层报告 src/windcms/report.py::build(逐台 turbines/<台>.html + 总览 overview/index.html + report.md; 融合级列要 model_run/fusion 出件, 缺则如实写"—") windcms rows 件内数据行合计 42
windcms/kb.json 1 ✓ 呼应成立 CMS 知识库索引 (问答检索用) src/windcms/knowledge.py::build(由 scripts/windcms.py kb 调用; 离线无模型时 0 chunks 也算跑成) windcms rows 件内数据行合计 1
['sop/findings.json', 'paradigm_r1/experiments/*/底稿*.json'] 4 ✓ 呼应成立 事实契约的 claim 本体与底稿 (观澜自算, 替代人裁底稿) scripts/sop_findings_from_ledger.py(六族 claim: 报警集中/重复检修/停机损失/温度离群/功率曲线偏离/振动过闸线; 每条带 coverage·缺证据·证伪判据, 相对判据封顶「候选」) scada_10min rows 件内数据行合计 4
pitch/** 3 ✓ 呼应成立 变桨侧派生件(日粒度液压/润滑/柱塞 + 零位三口径月表) scripts/pitch_face_build.py(10min 开关量 timeon/压力锯齿 + 1min 停机顺桨段/满发段/运行段分档 三叶桨距角中位 → 相对同月全场中位的零位偏差;口径/阈值/证据见同目录 pitch_face_manifest.json) scada_10min rows 件内数据行合计 21,472
guanlan/** 5 ✓ 呼应成立 事实契约与对外派生(门户结论段/取数) scripts/guanlan_facts_contract.py(build→render: 契约 + portal_claims/detail_cards/qa_refs/report_summary; 输入=上游族 ledger_claims 自算的 sop/findings.json + 三份底稿) scada_10min rows 件内数据行合计 5

反向「可逆性」:没有呼应关系的那批,能否从原始件推出来(用户令 2026-09-17 #2)

判据两条机器证据:① 生成端在不在包内 ② 产物正文里有没有人工判断字段(人工/裁决/审核/校准/评审/经验/judge/review/calibrat/verdict,抽样读件统计命中率)。含人工判断的件不是任何输入的纯函数 ⇒ 逆向工程推不出来,只能把那一步人工工作重做。

输出族 件数 可逆性 依据

按件数汇总:已在重算链上 = 1800 件

人工件:含人工判断字段的件已逐件登记在 outputs/<场>/_human_artifacts.json(用户令 2026-09-17 #3),它们不参与呼应校验(人写成/坐实的东西不是任何输入的纯函数);生成方式:python scripts/products_reverse_audit.py --write-human-manifest。

结论口径:可逆(直接) = 包内已有生成端,接上即可;可逆(需反推口径) = 内容是测量数据的函数,照 temp_monthly 的办法反推 + 逐值对拍;不可逆(人工判断) = 逆向工程推不出来,要么由研发补生成端、要么承认它是人工件(不该按产物管)。

16. 时间窗口径:按窗重算(2026-09-21 用户令)

用户令原话:「『部件问题』、『发电性能』应随『时间窗』下拉框值的变化,而相应变化」,并追加 「判级也按所选时间窗重算;时间窗中增加自定义时间段 —— 开始日期+结束日期(含)」。

16.1 窗的词表

窗写法 含义 覆盖到的月(月度件用它) 起止(日粒度件用它,含两端)
近30日 / 近90日 数据末端的 1 / 3 个月 该 1/3 个月 该段的月首 ~ 月末
2026年 / 2026H1 / 2025H2 / 全程 预设段 段内月 段首月首 ~ 段末月末
YYYY-MM 单月 该月 该月 01 ~ 月末
YYYY-MM~YYYY-MM 月区间 区间内月 起月 01 ~ 止月月末
YYYY-MM-DD~YYYY-MM-DD 自定义(新增) 与区间相交的月 原样,含两端

实现:scripts/windscada_serve.py 的 months_of(win)(月度件)+ win_range(win)/in_win(ts, win)(日粒度件); v2 顶栏两个日期输入框 + 「应用」(校验起 ≤ 止,非法拒绝并提示)。月度件按月取整、日粒度件按日精确, 页面上写明这句,不让读者以为月度均值能切到天。

16.2 哪些随窗、哪些不随(单源)

内容 随窗? 机制
报障统计 / 五态控制策略 / 温度月轨迹 / 停机台账 ✔ 月度件按 months_of() 过滤(原有口径)
判级四轴(变桨 / 偏航 / 蓄能 / 温度) ✔(本轮新增) taxonomy.system_matrix(span=…) → 各面 registry(span=…)。变桨的输入是日粒度序列 ⇒ 精确切片;偏航/蓄能/温度走窄仓重算(同代码同阈值,只换数据切片)
部件可靠性表(MTBF/MDT/停机时长) ✔(本轮新增) reliability.overview(span=…):停机事件与报警都是日粒度 ⇒ 精确
发电性能:7 张月度时序图 ✔(本轮新增) 按 months_of(win) 过滤(月度件 ⇒ 按月取整)
发电性能:七镜头曲线 ✔(本轮新增) /api/curves?win= → 按窗重算分箱(curves.build_store(span=…));选到 2025-07~2025-12 时直接用正式产物
温度面的 ref(2025H2 干净窗)与 ref_sm(同历月早一年) ✘(故意固定) 它们是"季节解耦的对照窗",跟着用户窗漂就失去对照意义;页面如实标注
振动判级 / 厂家报告转录 / 工单台账 ✘ 各有自己的数据时点,不随时间窗变(原有口径,页面已标注)

16.3 为什么要有 slim10min 窄仓

按窗重算的真实卡点是"每换一个窗就要重读 15 GB 10min CSV"(实测 ≈2 分钟/面)。新增产物 outputs/<场>/windscada/slim10min/<台号>.parquet(46 列公共子集:功率/限电命令/风况/转速/桨距/ 温度 _mean 27/偏航/液压;不裁剪行),由 scripts/scada_slim_build.py 一次全量扫(≈2 分钟)生成, 进重算链 ③b 步,并登记在反向审计族表 scada_slim10min。之后任何窗的判级/镜头重算都只是 "窄仓过滤 + 分组"(秒级)。口径同源:窄仓就是 load_10min(…, groups=[…]) 的列子集。

16.4 慢就慢在算,不慢在等:pending 契约

判级矩阵 ≈25 s/窗、七镜头 ≈10~45 s/窗。直接同步算会把 HTTP 请求挂几十秒(远端还要过网关), 所以统一口径:进程缓存命中即回;未命中就起后台线程、本次先回 sysmx_pending / building, 页面轮询再取(6 s 一次,至多 40 次)。服务启动时后台预热 6 个预设窗(判级 + 默认窗镜头)。 pending 期间页面在脸上写明"下面这几张卡还是上一份口径",绝不静默把旧口径当新窗。

17. 描述口径与服务端页面链接(2026-09-22 人工测试消缺)

17.1 三条写作口径(用户令)

口径 要求 落地
时间窗 描述里含"窗"且确实指时间窗的,一律写全"时间窗" 预设窗→预设时间窗、判级窗→判级时间窗、窗内→时间窗内、证据窗→证据时间窗、随所选窗→随所选时间窗…;领域词保持原样:天气窗/作业窗(检修作业时间)、文档预览窗、交互窗、振动观测窗
影响机组 影响的是风电机组时写"影响机组" 表头与说明里的"影响台/影响台数"→"影响机组/影响机组数"(含报告构建器);数据字段名 影响台数 暂保留(数据契约/随包快照兼容),显示层统一
英文简写 用简写时必须"中文(英文简写)" 平均停机间隔(MTBO)、平均无故障间隔(MTBF)、单次停机时长(MDT);英文映射(i18n_en)的键同步改,否则英文页回落中文

全量清扫脚本见 F:\temp\_h9\wording_audit.py(按文件点名"裸窗/影响台/英文简写"),只改确实指时间窗/机组的,不误伤领域词。

17.2 服务端页面的两个链接契约

事 口径
系统标识 /problem/<台>/<系统> 与 /api/problem?sys= 一律经 windscada_serve.sys_norm() 双向归一(中文名 / 前端 slug pitch / 大小写变体都认)。认不出时页面写"系统标识无法识别",绝不写"该系统在判级窗内无非常态"(对报警机组那是假陈述)
根链接(返回门户) 网关会把组件页的根相对链接按前缀改写(/ → /detail/),因此组件页要指回网关根时必须在标签上标 data-abs="1" —— guanlan_gateway.rewrite() 对这类 href 跳过前缀改写(哨兵法实现,普通链接行为不变,见其 docstring 与单测)

17.3 v2 页面的依据不得为空

「需要关注」的卡片来自融合面链盘(fus.链盘.rows),而 SCADA 的 watch 只覆盖九系统多报警台; 两者集合不同时旧的 why[t] 会取到 undefined ⇒「查看完整依据」展开是空白(实测 WTG09 齿轮箱)。 现在缺依据时按本行字段自组句:部件 · 链路走到第 k/n 步 · 卡在「某步」 · 证据源,永不空。