版本 v0.2.0 · 2026-09-16 · 适用
<安装目录>(安装目录) 本文是系统设计正本: ① 产物全景(类型/功用/输出路径/生成端/消费端);② 路径与进程的统一约定; ③ 输入 ↔ 产物呼应关系;④ 重算的两种运行状态与"产物即时进页面"的实现;⑤ 启动与门户。 与本文配套、可复跑的机器校验:python scripts/inventory_products.py(清点 + 呼应校验,--check出退出码)。
现场数据 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
四条设计铁律(贯穿全部代码,改代码时先读这四条):
src/paths.py —— 代码/配置里只写 ROOT 相对路径,运行期由助手解析成绝对路径(基准是安装根,不是 cwd)。outputs/<场>/;原始件只读 data/raw/<场站>/;release/ 是交付静态层。三者不互相写。_provenance.json)。src/proc.py —— 子进程一律 CREATE_NO_WINDOW,日志落 logs/,不弹命令窗口。由
python scripts/inventory_products.py --write-doc生成;数据源是盘上的实际文件 +outputs/<场>/_provenance.json(逐件来源台账)+outputs/<场>/_derived_manifest.json(构建脚本自登记)。
自动生成于 2026-09-16 16:26;数据源: 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 | 115 | 4.5 MB | 17 / 98 |
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 | 25 | 82.9 MB | 3 / 22 |
windcms |
outputs/rudong/windcms/ |
CMS 振动诊断产物: 状态评估报告/逐台页/工作台页/知识库 + 厂家报告转录 | scripts/windcms.py report/kb · scripts/vib_reports_build.py | 自服务 :18020 (每请求现读) · taxonomy.system_matrix (转录设备状态) | 56 | 30.1 MB | 2 / 54 |
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 | 1808 | 3329.7 MB | 1718 / 90 |
tcm_compatible_replay |
outputs/rudong/tcm_compatible_replay/ |
TCM 兼容链回放资产 (模型表/掩码阈值/裁决记录) | 随包快照 (无生成端) | src/windcms/report*.py · config.mask_thresholds | 59 | 32.7 MB | 0 / 59 |
sop |
outputs/rudong/sop/ |
SOP 中间件/评审/台账与事实契约底稿 | 随包快照 (无生成端) | scripts/guanlan_facts_contract.py · 门户结论段 | 210 | 15.8 MB | 0 / 210 |
guanlan |
outputs/rudong/guanlan/ |
事实契约与对外派生 (可上云面孔) | scripts/guanlan_facts_contract.py | 门户 #findings · /api/facts | 5 | 0.5 MB | 0 / 5 |
pitch |
outputs/rudong/pitch/ |
变桨侧派生件 (零位/日粒度) | 随包快照 + rebuild_from_raw --scada | 脚本 windscada_serve.py 变桨面 | 2 | 0.3 MB | 0 / 2 |
paradigm_r1 |
outputs/rudong/paradigm_r1/ |
范式实验件 (E3/E5/E8 底稿, 事实契约输入) | 随包快照 (无生成端) | scripts/guanlan_facts_contract.py | 29 | 0.5 MB | 0 / 29 |
合计 2309 件 / 3496.9 MB;其中 raw 重算 1740 件、随包补齐 571 件。
_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 幂等补登记。
src/paths.py)| 助手 | 返回 | 用途 |
|---|---|---|
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 |
| # | 位置 | 原样 | 改成 | 为什么要改 |
|---|---|---|---|---|
| 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,而其实不需要那个脚本 |
| 类别 | 具体 | 影响 | 处置建议 |
|---|---|---|---|
| 一次性/历史脚本自拼路径 | 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 |
无功能影响 | 下次统一时二选一(用起来或删掉),避免"看着像真源其实没人用" |
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 会给子进程新建一个可见窗口。
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))。
★ 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,避免在桌面留一个指向临时副本的死链。
| 服务 | 读法 | 产物改了要不要重启 |
|---|---|---|
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(清缓存并回显新旧指纹)。
顶部菜单: 总览 | 系统架构 | 方法 | 经验发现 | 案例·液压 | 振动·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 整页保留不变。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 条)。
| 状态 | 入口 | 说明 |
|---|---|---|
| 系统运行中 | 门户「数据重算」或 /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>, 不再打断整条链)/
⑥ 重启组件服务/
⑦ 本体六步/⑧ 本体审计(+可选等价验收)。
| 缺口 | 影响的产物 | 依据/证据 | 补齐判据 |
|---|---|---|---|
振动六层链四步脚本未随包(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-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 的办法反推 + 逐值验证) |
configs/farms/*.yaml 无代码读者;available() 只认 *.json |
多场部署 | src/windscada/config.py::available() |
二选一(见 §3.3) |
| 清过产物 + 包不随产物 ⇒ "无生成端"的那批产物不可恢复 | 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、CMS 页面件等 |
2026-09-17 实测:13:05 执行「清除产物」(真删、不留备份),随后重算产出 1,727 件(有生成端的那批,锚点 39,211/5,876/404/19,494 全部对齐、呼应校验正常),但这批无生成端的件没有任何来源可补 —— 交付包按用户令不含产物,本机也没有旧包 | 需要这批件时打一个带产物的包(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} |
六层链/结构化产物随包,或把页面里的引用改到本包在位路径(需研发确认是同一件东西;不改交付件正文) |
2 件数据派生页面没有生成端:release/如东/全场状态一览_脱敏_20260825.html(1.5 MB 场站状态快照)、release/如东/如东取数单_2026-08-21.html |
交付文档页里这两个子页 | 全仓搜不到生成端;页面里也没有 source/指纹 ⇒ 无法做陈旧检测(§10) |
补生成端并写成带 source_sha256 的产物(推荐);或明确降级为 frozen-delivery 并接受它会过期(需用户裁定) |
| 3 件内嵌快照缺指纹:标准面板、覆盖度 ch0、如东取数单 | 门户内嵌页 | 只有「生成 2026-09-05 16:28」这类时间戳;覆盖度页的源 e1_base.parquet 不在本包 |
生成时把 source + source_sha256 写进页面(机制已就绪,见 §10 的 data-derived 规则) |
| 日期 | 变更 |
|---|---|
| 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 | 清产物 → 重算链走通(并暴露"包不随产物"的连带后果):用户 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 |
打包器: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 |
缓存目录(解释器字节码、第三方工具缓存) |
④ .DS_Store Thumbs.db desktop.ini *.crdownload *.partial |
系统/下载临时索引 |
.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,否则整段核验被跳过(打印 [!])。
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 条,全部在位)。
用户问的三页,结论(依据见下表与 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 缺口表里。
判据与说明见 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) ⇒ 客户交付件, 随交付版本变; 内容虽由分析产出, 但冻结发布, 不该随输入数据自动改 |
└ 全场状态一览 (脱敏) |
data-derived |
是 | ★这是问题所在: 1.5 MB 的场站状态快照(脱敏版), 内容是实际运行状态的函数 ⇒ 属于产物, 但页面里没有任何指纹/生成时间/来源登记, 全仓也搜不到生成端 ⇒ 目前游离在产物管理之外(无溯源、无陈旧检测、无重算入口) |
└ 如东取数单 |
data-derived |
是 | 取数单是「要哪些数据」的清单页, 明列产物路径 ⇒ 随产物结构变; 现在只有日期没有指纹, 也没有生成端随包 |
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 占位面板 |
唯一取用口:代码只许用 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 |
本次修掉的三处真问题:
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() 的报错写得能看懂("该文件存在但不是场定义…")。configs/farms/_模板.json.example 是 GBK 乱码("绀轰緥椋庣數鍦?")且写死 /Volumes/WINDDATA,
与实物格式(YAML)相反。已重写为 configs/farms/_模板.yaml.example(UTF-8、YAML、<场站名称> 占位),
并同步 docs/数据目录结构与落位约定_v0.2.md。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 条提示)。
统一前(实测,不是推测):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/。
本次的实际搬迁(都做了台账/留档,不是悄悄删):
outputs/**/*.log → logs/build/<场>/<原相对路径>;同时把 _provenance.json 里这 39 条
删掉并重算计数(2309 → 2270 件;shipped 569 → 530;raw-derived 1740 不变),台账里写明"因日志归位移出"。
搬迁后 inventory_products.py --check 仍 rc=0 呼应正常。ops_*.log,2026-09-12 那批)与 _proc_reg.log → logs/legacy/;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 各报一行。
现场反馈:双击 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 实跑无任何报错。
现场症状(2026-09-17):在控制台点了「清除产物」之后,从门户点「登录」→ /detail/v2 显示
"模块不可用: 综合详细分析工作台 … 127.0.0.1:18033 未启动"、日志里 [WinError 10061] 连接被拒。
查下来是两件独立的事叠在一起:
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,页面正常显示"无产物"文案。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,副本端口无一残留。判据三条(与 §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 的检查)。
输入数据(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 |
scripts/rebuild_all.py 把 14 步写死(各步幂等,可整条重跑):
① 放数据 place_raw_data.py 原样搬 现场包 → data/raw/<场>/(同尺寸跳过,冲突拦下)
② 三门台账 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 必须在产物全部就位之后(门户的结论段是产物的渲染结果)。
| 服务 | 读法 | 产物更新后 |
|---|---|---|
工作台 :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 |
刷新即最新 |
outputs/<场>/…,代码只经 src/paths.py 取(§3)。outputs/<场>/_provenance.json 记 raw-derived(由 data/raw 重算,含验证依据)
或 shipped(包内无生成端,用随包件补齐);raw-derived 另由 _derived_manifest.json 自登记。inventory_products.py --check —— 产物跨度必须落在输入跨度内,rc≠0 即不符。configs/portal_pages.yaml 里 data-derived 的页面件,其 source(产物路径)
必须在 _provenance.json 里出现,否则 pages_audit.py 报"未纳入产物台账"。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 打一份带产物的包存档。