版本 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) |
| 日期 | 变更 |
|---|---|
| 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 | 现场实炸一次: 目标机 D:\产品\app 双击 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 属无产物预期) |
打包器:scripts/pack_dist.py。默认文件名 <父目录>/guanlan-v<VERSION>_dist_<日期>.zip,可用 --out 指定。
| 进包 | 内容 |
|---|---|
src/ scripts/ guanlan.py |
程序与全部构建/运维脚本(含 src/entry_refs.py 入口引用闭合检查) |
configs/ resources/ reference/ |
配置(含 serve.json 端口真源、场站/通道台账) |
release/ |
门户、仿真页、三维资产 viewer/、治理清单交付件 release/如东/(客户交付物,勿外传) |
docs/ README_先读我.txt 测试须知.txt |
交付文档与说明书 |
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、requirements.txt |
安装与起停入口(无 .vbs:start.bat 默认调 pythonw.exe + scripts/guanlan_start_hidden.py;install.ps1 安装时另建 启动观澜.lnk 供零窗口启动) |
| 不进包 | 理由(同时写进包内 dist-manifest.json 的 excluded) |
|---|---|
data/ |
现场原始输入件;约定"原始件不随包分发"(要带用 --with-data) |
outputs/ |
用户令:产物不进包(--no-products)。目标机放数据后 rebuild_all.py 重算;或缺"无生成端"的随包件时用 products_restore_missing.py --stash <交付包.zip> |
logs/ run/ |
用户令:日志不进包(run/pids.json 里的 PID 到新机器上是无效引用) |
.venv/ .git/ .github/ |
venv 换机必失效(安装时重建);版本库不随交付件分发 |
_products_off*/ |
旧设计的"清除产物"暂存档(2026-09-16 起清除=真删,不再产生;老机器上若有可手工删) |
*.lnk |
安装时建的快捷方式:内含绝对路径,换机必失效,由目标机的 install.ps1 重建 |
__pycache__/ *.pyc、顶层 *.zip |
解释器缓存、旧的交付压缩包(避免包中包) |
开箱验证(一条命令给出"能不能装、能不能跑"的证据):python scripts/pack_dist.py --verify <zip> —— 解压到
临时目录 → ①b 入口引用闭合核对 → 离线安装 → 起服务核验 /·/detail/·/cms/·/ops·/healthz → 收尾清理,
要求 ≥4 个页面可用。
★ 核验要求组件端口(18033/18020/18791/18792/64292)空闲,验证前先 guanlan.py stop,否则整段核验被跳过(打印 [!])。
src/entry_refs.py,2026-09-16 实炸后加)现场反馈:目标机 D:\产品\app 双击 start.bat → 无法找到脚本文件 "D:\产品\app\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 条,全部在位)。