# 观澜 · 如东 v2 重算操作手册 (v0.1, 2026-09-11) **给谁看**: 要手工把 `data\raw` 下的原始数据算成系统能显示的东西的人。 **前提**: 已经跑过 `install.bat`(有 `.venv`)、`.venv\Scripts\python.exe` 存在。 **所有命令都在安装根目录下敲**(`<安装目录>\`) —— 脚本按自身位置解析路径, 但相对路径参数 (如 `--src`)会按当前目录解释, 站在根目录最省事。 > 一句话原理: 页面的数来自 `outputs\rudong\` 里的**产物**; 产物由 `data\raw\` 下的**原始件**算出来; > `data\raw\` 的下一级目录名就是**场站名称**(扫描辨识, 不写死)。所以流程永远是: > **放数据 → 算 → 重启服务 → 看页面**。 --- ## 0. 一条命令重算全部 (`scripts/rebuild_all.py`, 2026-09-12) 前面那些步骤已经串成一个入口 —— **依赖顺序、验收锚点、子进程编码都写死在脚本里**, 不用记顺序: ```bat cd /d <安装目录> :: 全套 (含 SCADA 侧 10 个构建器, 逐台读 ~14 GB, 约 15 分钟) .venv\Scripts\python.exe scripts\rebuild_all.py :: 常用档: 只换了台账类数据, 没动 scada_10min .venv\Scripts\python.exe scripts\rebuild_all.py --skip-scada :: 连"放数据"一起 (现场包目录) .venv\Scripts\python.exe scripts\rebuild_all.py --src <现场包目录> :: 其它开关 .venv\Scripts\python.exe scripts\rebuild_all.py --dry-run :: 只打印计划, 不执行 .venv\Scripts\python.exe scripts\rebuild_all.py --no-restart :: 不碰服务(自己管启停) .venv\Scripts\python.exe scripts\rebuild_all.py --with-verify :: 末尾加台账等价验收(需随包基线在位) ``` **它按什么顺序做**(为什么是这个顺序见脚本头部注释): | 步 | 做什么 | 脚本 | |---|---|---| | ① | 放数据(给了 `--src` 才跑) | `place_raw_data.py --scope full` | | ② | 三门台账(报警/工单/油样) | `rebuild_from_raw.py` | | ③ | SCADA 侧 10 个构建器 | `rebuild_from_raw.py --scada` | | ④ | 月度派生件 | `windscada_monthly_build.py`(★无随包基线时返回 5 = "无法比对, 这不是通过" → 本步**容忍 5 并跳过等价验收**, 不再打断整条链; 要验收请先把随包件恢复成暂存区/基线) | | ④b | **振动侧摄入**(CMS 原始导出 → 窗索引/谱) | `vib_raw_build.py`(`--skip-vib` 可关;没振动原始件时空跑退出 0。★**不**重生成 CMS 报告/页面: 缺六层链产物时那会用残缺输入覆盖随包快照, 实测 `report.md` −97%, 见 `docs\振动数据接入_v0.1.md` §3b; 要跑用 `--vib-report`) | | ⑤ | 补齐"包内没有生成端"的产物 | `products_restore_missing.py`(★没有暂存区时返回 6 = "这一步没得做" → **跳过并说明**, 不打断链条; 想补齐先点"清除产物"生成暂存区) | | ⑥ | 重启服务(**必须**: ⑦ 要吃 `/api/fleet`) | `guanlan.py stop` / `serve` | | ⑦ | 本体层: 码表→铺开→决策链→趋势→检索→参数表 | `-m src.ontology.*` | | ⑧ | 本体审计 + (可选)等价验收 | `-m src.ontology.audit` 等 | 每步都是**幂等**的, 中途失败就按日志处理完再重跑同一条命令; 某步失败会立刻停下(后续步骤依赖它), 最后打印每步的 `OK/FAIL` 与耗时。 > 手工分步执行的完整版在 §1~§5b(换现场包、单跑某一环、或想知道每一步期望输出时看那些)。 --- ## 0a. 先摸清现状(三条只读命令, 不改任何东西) ```bat .venv\Scripts\python.exe scripts\scan_stations.py :: 系统认到哪个场站、四类源各多少件、有没有"缺" .venv\Scripts\python.exe scripts\rebuild_from_raw.py --dry-run :: 重算会做哪几步(不写盘) .venv\Scripts\python.exe scripts\products_state.py --status :: 产物在位还是被挪走 ``` `scan_stations.py` 的期望输出(当前这台机器): ``` 扫描到 1 个场站目录: 如东 (631 件) ...\data\raw\如东 ← 就绪 scada_10min 38 件 scada_1min 38 件 故障报警 16 件 风机故障记录 135 件 油样报告 404 件 辨识结论 : how=raw_station ← 目录名与场配置 raw_station 完全相同 ``` ## 0b. 可视化运维控制台 (`http://127.0.0.1:28084/ops`, 2026-09-12) 不想敲命令就用它 —— 一件事一个按钮, **按钮的可用/不可用是按真实状态判的**(不是样子货): | 按钮 | 什么时候可用 | 点了做什么 | |---|---|---| | **启动服务(并打开门户)** | 有组件服务未运行 | `guanlan.py serve`, 等门户就绪后**自动打开** `http://127.0.0.1:28084/` | | **停止组件服务(保留控制台)** | 有组件服务在运行 | 只停 detail/cms/sim/sim_sys/viewer —— **保留网关**, 否则按钮点完页面就没了 | | **完整重启(含网关)** | 有组件服务在运行 | 分离进程先停再起; 页面断开约 15 s 后自动恢复 | | **执行重算** | 没有任务在跑 | `rebuild_all.py`(默认跳过 SCADA; 可勾选含 SCADA / 末尾加等价验收) | | **清除产物(直接删除,不留备份)** | 产物在位 且 没有任务在跑 | `products_state.py --off --yes` —— ★2026-09-16 用户令: **不留备份、不可恢复**; 需要随包件时用 `products_restore_missing.py --stash <交付包.zip>` 从交付件补齐 | 页面还实时显示: 各服务端口通不通 · 产物件数与分仓 · **来源台账**(raw 重算多少件 / 随包补齐多少件) · **验收锚点**(报警 39211 行 · 工单 5876 · temp_monthly 19494 · 本体 9702 对象) · 最近一次动作的状态、 退出码、耗时与**日志尾巴**(边跑边刷新, 2 s 一次)。 三条纪律(写在后端, 前端禁用只是提示 —— **直接打 API 也会被拒**): 1. **一次只允许一个动作**: 有任务在跑时, 五个按钮全灰; 违规调用回 `409 已有任务在跑 (…)`。 2. **语义前置也拦**: 组件都在跑时点"启动"→ `409 组件服务都在运行, 无需启动`; 组件都停了点"停止"→ `409 组件服务都已停止, 没有可停的`; 产物已清空再点"清除"→ `409 …无需再清`。 3. **停服务不自杀**: 控制台由网关(28084)提供, 所以"停组件服务"保留网关 —— 否则页面刚点完就没了, 再也没法"启动服务"。要连网关一起停, 用"完整重启"。 实现: 页面与 API 在 `scripts/guanlan_ops.py`(网关只做转发); 动作由 `scripts/_ops_run.py` 执行 (把**真实退出码**写回 `run/ops_job.json`, 页面据此显示"完成(退出码 0)"或失败); 启动经 `scripts/_ops_launch.py` **二次启动** —— 这一层是必须的, 见下面第 4 条。 **为什么要有"二次启动"这一层**(2026-09-12 实逮, 后果是"把栈停在半路"): 从控制台点"执行重算" → 重算跑到第 6 步"重启服务" → `guanlan.py stop` 用 `taskkill /PID <网关> /T /F` → `/T` 沿父子树把子孙一起杀 → **正在执行的重算(网关的子进程)被自己杀掉**, 日志停在 stop 那行, 服务也停在半路(实测就是这样)。修法两条: ① 动作经 `_ops_launch.py` 再启动一次: 执行器的父进程变成"已退出的启动器", 不再是网关的子孙; ② `rebuild_all.py` 的第 6 步改成**只重启组件服务**(`_ops_stop_keep_gateway.py` + `guanlan.py serve`), 网关留着 —— chain_ingest 要的是 detail(18033) 重新加载产物, 网关不缓存产物。 另外任务加了**心跳**(`_ops_run.py` 每 15 s 更新 job 文件): 被强杀时页面不再永远显示"执行中", 而是标成"心跳停了 N 秒 —— 任务多半被强杀, 未拿到退出码"。 **实测**(逐个按钮, 都用真实请求打): | 动作 | 结果 | |---|---| | 停组件服务 | 5 个组件 down、网关 up、**页面仍 200**; 按钮翻成 `stop=false start=true`; job `rc=0` ✅ | | 启动服务 | 全部 up(`rc=0`, 4.6 s), 日志 `webbrowser.open → True`(**门户已自动打开**) ✅ | | 完整重启(含网关) | `restart_all`, 页面断开约 15 s 后**自己回来**, 全栈 up、各页 200、`rc=0` ✅(修好自伤之后) | | 执行重算 | 12 步全跑完 `rc=0`(117.5 s), 期间五个按钮全灰、并发点"清除"→ **409** ✅ | | 清除产物 | 592 件 → **0 件**, 按钮翻成 `products_off=false products_on=true`; 重复清除 → **409** ✅ | | 恢复产物 | 回到 **592 件**, 锚点 39211/5876/19494/9702 全在 ✅ | | 语义前置 | 组件都在跑点"启动"→ **409**; 组件都停点"停止"→ **409** ✅ | | 页面本身 | `node --check` 通过(内联 JS 无语法错); 六个按钮 id 与 2 s 轮询都在 ✅ | --- ## 1. 放数据(现场包 → 约定目录) 现场给的通常是几个 GBK 名的大压缩包。**不要手工拖拽**, 用映射表脚本落位(可复核、可反复跑): ```bat :: 先看要落什么 .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope full --dry-run :: 真落位 .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope full ``` | 范围 | 落什么 | |---|---| | `--scope a2` | 四项数据层: `scada_10min` / `故障报警` / `风机故障记录` / `油样报告` | | `--scope mech` | 机理层源件 `data\raw\西门子4.0技术资料\`(含那份**对译表**) + `<场站>\scada_1min\` | | `--scope full` | 两组一起(从零重算要用的全集) | 同尺寸文件会自动跳过, 所以**重复跑很便宜**(不用怕重抄十几 GB)。`--src` 不给会去找 `data\_incoming\`(默认)。 ### 放数据的四条硬规矩 1. 下一级目录名 = **场站名称**, 且要与 `configs\` 场配置里的 `raw_station` 一致(当前 `如东`)。 2. 四个(加可选 1min 共五个)子目录名固定: `scada_10min` / `scada_1min` / `故障报警` / `风机故障记录` / `油样报告`。 3. `scada_10min` 必须是**逐台平铺**的 `WTG01.csv…WTG38.csv` 本身 —— 不能是压缩包、不能再套一层目录。 4. 机理层资料放 `data\raw\西门子4.0技术资料\`(**不在**场站目录下)。 ## 2. 重算 ```bat :: ① 三门台账(报警/工单/油样): 秒级~分钟级 .venv\Scripts\python.exe scripts\rebuild_from_raw.py :: ② SCADA 侧 10 个构建器(逐台读 38 个 ~380MB CSV, **约 15 分钟**, 建议放后台或别关窗口) .venv\Scripts\python.exe scripts\rebuild_from_raw.py --scada ``` ②跑的 10 步(顺序 = 依赖顺序): 功率曲线 → 可用率与损失 → 七镜头曲线 / 控制策略件 / 停机事件 → 温度 NBM / 偏航 / 液压蓄能 / 热链 / 系统辅助。 **期望结果**(本机实测): | 产物 | 行数 | |---|---| | `alarms.parquet` | 39,211 | | `workorders.parquet` | 5,876 | | `oil_samples_index.parquet` | 404(见 §7 的 102 行说明) | | `loss_monthly.parquet` | 3,729 | | `temp_bins.parquet` | 24,624 | | `curve_lenses.parquet` | 7,676 | | `stop_events.parquet` | 2,318 | | `powercurve_bins.parquet` | 912 | | `curve_liveness` / `control_schedule` | 304 | | `yaw_daily` / `hydraulic_accum` / `thermal_chain` / `system_aux` / `control_profile` / `powercurve_dev` | 各 38(逐台一行) | **没换 SCADA 数据就不用跑 ②**(① 是吃台账表的, 现场按月给表时跑 ① 就够)。 ## 2b. 月度派生件 + "包内没有生成端"的产物(2026-09-12 新增) `rebuild_from_raw.py` 只覆盖 16 件; 随包里另有 15 件 parquet **全库没有生成端**, 而工作台主数据 `/api/fleet` / 单机页 / 振动融合页都依赖它们 —— 缺了就是"页面没数据"。两条路补齐: ```bat :: ① 能从 raw 重算的: 月度派生件(逐值对齐随包件才落盘) .venv\Scripts\python.exe scripts\windscada_monthly_build.py --verify :: 先看对齐情况 .venv\Scripts\python.exe scripts\windscada_monthly_build.py :: 算并写盘 :: ② 没有生成端、或规则未复现的: 从随包件补齐(只补缺件, 不动 raw 重算件), 并落来源台账 .venv\Scripts\python.exe scripts\products_restore_missing.py --dry-run .venv\Scripts\python.exe scripts\products_restore_missing.py ``` `②` 会在 `outputs\<场>\_provenance.json` 写一份**逐件来源台账**: `raw-derived`(由 data/raw 重算, 含验证依据) 与 `shipped`(包内无生成端, 用随包件补齐)。 任何人想知道"这一页的数是自己算的还是随包带的", 查这份台账即可。 **已确认可重算的月度件**: `temp_monthly`(每台×每月×每个温度通道, 在 **发电态 p≥500kW** 行上的**中位数**, 19494/19494 逐值一致 —— 口径出处 `src/windscada/subsys/temp_nbm.py` 的"发电态×功率档(500kW)")。 **未复现、暂用随包件**: `pitch_daily`(液压四列 `hyd_mean`/`over_relief`/`under_pump` 的规则试了 日界位移与越限穿越计数都对不上, 不猜)、`pc_monthly_bins`/`duty_monthly`/`thermal_monthly`/`sector_power`/ `structure`/`watch_channels_monthly`/`control_monthly`/`yaw_*`/`genbearing_monthly`/`mblub_monthly`。 ## 3. 本体层(机理资料那一层, 前提是技术资料已放好) ```bat :: 对象库(技术资料 + 故障处理手册 + 码表 + 工单/报警) → objects.json .venv\Scripts\python.exe -m src.ontology.kb_ingest :: 铺开: 38台×九系统判级 + 振动 handoff + 工单 + 油样 + 机制库 (需 ②的产物已在位) .venv\Scripts\python.exe -m src.ontology.populate .venv\Scripts\python.exe -m src.ontology.chain_ingest :: 决策链进度(需 windscada 服务在跑) .venv\Scripts\python.exe -m src.ontology.trend_ingest :: 在升/闭环证据 :: 检索索引(BM25 词法索引; 向量那半要 Ollama 的 bge-m3, 本机没模型时会跳过, 纯词法仍可用) .venv\Scripts\python.exe -c "from src.ontology import retrieval as R; R.build(use_vec=False)" :: 实机参数表(1706 条整定值, 源件是技术资料里的 Turbine+Parameters.*.xlsx) .venv\Scripts\python.exe -c "from src.ontology.maintenance import refresh_params as f; print(f())" :: ★验收: 官方本体审计(悬空引用/孤岛对象/声明面=证据面), 期望 0 问题 .venv\Scripts\python.exe -m src.ontology.audit ``` 期望: `kb_ingest` 打印 `{'AlarmCode': 555, 'WorkInstruction': 559, 'FailureMode': 150, 'MaintTask': 559, 'Doc': 303, 'Component': 9, ...}`; `populate` 打 `2341 → 9669`; 全链跑完 `audit` 报 **9701 对象 / 0 问题**(本机实测)。 ## 4. 等价验收(可选, 但强烈建议做一次) `--verify` 拿**随包基线**逐键逐值比对重算件, 把差异分成"旧链已知缺陷 / 本链新增覆盖 / 无法归类"三类, 只有第三类才算不通过(退出码 4)。 基线是**标准答案**, 不参与运行; 它就在 `outputs\rudong\windscada\_pre_rebuild_20260911\`(4 件)。 若该目录不在(例如刚清过产物), 先从交付包把它取回来: ```bat :: ① 基线不在时: 从交付包取回台账件(或直接从同事的安装目录拷这个目录) .venv\Scripts\python.exe scripts\products_restore_missing.py --stash <交付包.zip> :: ② 验收(只比不算) .venv\Scripts\python.exe scripts\rebuild_from_raw.py --verify ``` **期望结论**: `alarms` 39211/39211 逐值完全一致; `workorders` 8 行差异全部可归类(旧链的时间解析缺陷) + 69 行本链新增覆盖; **1 处"需人工看" = 油样 506→404**(那 102 行的源件不在包里)。 > ★ 基线目录位于 `windscada/` 内 ⇒ **「清除产物」会把它一并删掉**。要长期保留, 把它复制到包外 > (例如交付包 zip 里那份), 或清完再用上面第 ① 步取回。 ## 5. 重启服务(通常**不必**) 2026-09-16 起, 产物变了**不需要重启**: detail 服务每次取数前比"产物指纹", 变了自动重载 (实测日志 `[reload] 产物指纹变化 …`); CMS 的报告/页面是**每请求现读**; 门户按 `(mtime,size)` 失效。 所以重算完直接刷新页面即可。以下两种情况才需要重启: - 改的是 `scripts/guanlan_ops.py`(运维控制台页面/接口) → **必须重启网关**(它被 `_ops_module()` 缓存); - 产物被清空期间起的 CMS 服务可能已崩(表现 `/cms/` 503) → 点一次"启动服务"。 接口层的显式重载入口: `GET /detail/api/reload`(清缓存并回显新旧指纹)。 ```bat .venv\Scripts\python.exe guanlan.py stop .venv\Scripts\python.exe guanlan.py serve ``` 期望: `状态 degraded offline 模块 5/7 ok`, 并列出 DOWN 的项。 ## 5b. 清掉产物 / 再重算(完整操作, 2026-09-16 口径) ### 清掉产物(人工检查空状态用) > ★ 2026-09-16 用户令: **清除产物不留备份**。清掉就是**真删除**, 不再有 `_products_off/` 可 `--on` 还原; > 要随包件就从**交付包 zip** 按需补齐。演练实测(2026-09-16): 清 11 仓 / 2316 件 / 3499 MB 用时 <10 s; > 从交付包补回 589 件用时 <1 min。 ```bat :: ① 清掉 (真删除, 不可恢复; 必须显式加 --yes 防手滑) .venv\Scripts\python.exe scripts\products_state.py --off --yes :: ② 看空状态: / 门户仍 200 (门户在 release\, 不是产物); :: /detail/api/fleet 回 err=no_products; /cms/ 变 503; 维护页「数据层」各项 条数=0 / 存在=False :: (服务不必重启: 产物指纹变了页面会自动重载; 见 §5c) :: ③ 恢复: 从**交付包 zip** 按需补回"包内没有生成端"的随包件 .venv\Scripts\python.exe scripts\products_restore_missing.py --stash <交付包.zip> :: 再把 raw 派生件重算出来 (或按需只跑某几环): .venv\Scripts\python.exe scripts\rebuild_all.py --skip-scada ``` **不删产物、只想验证"没有产物会怎样"**: 把整个 `outputs\<场>` 目录改个名再改回来(手工), 效果等同清空且随时可逆: ```bat Rename-Item outputs\rudong outputs\rudong_off :: 相当于清空(页面立刻呈现无产物) Rename-Item outputs\rudong_off outputs\rudong :: 还原 ``` > 清的是 `outputs\<场>\`(产物仓), **不动** `data\raw\`(现场数据)、`release\`(门户外壳与交付件)、 > `logs\`/`run\`(运行期)。所以清完门户首页照旧打开 —— 那是静态交付页, 本来就不读产物。 ### 清完再重算(从零) ```bat :: ① 放数据 (现场包 → 约定目录; 同尺寸自动跳过) .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope full :: ② 台账 + SCADA 侧 10 个构建器 (后者约 15 分钟) .venv\Scripts\python.exe scripts\rebuild_from_raw.py .venv\Scripts\python.exe scripts\rebuild_from_raw.py --scada :: ③ 月度派生件 (能从 raw 重算的那批; --verify 先看与随包件逐值是否一致) .venv\Scripts\python.exe scripts\windscada_monthly_build.py --verify .venv\Scripts\python.exe scripts\windscada_monthly_build.py :: ④ 本体层 (技术资料须已放好) .venv\Scripts\python.exe -m src.ontology.kb_ingest .venv\Scripts\python.exe -m src.ontology.populate .venv\Scripts\python.exe -m src.ontology.chain_ingest :: 需 windscada 服务在跑 .venv\Scripts\python.exe -m src.ontology.trend_ingest .venv\Scripts\python.exe -c "from src.ontology import retrieval as R; R.build(use_vec=False)" .venv\Scripts\python.exe -c "from src.ontology.maintenance import refresh_params as f; print(f())" .venv\Scripts\python.exe -m src.ontology.audit :: 期望 0 问题 :: ⑤ "包内没有生成端"的那批: 从随包件补齐(只补缺件, 不动上面重算出来的), 并写来源台账 .venv\Scripts\python.exe scripts\products_restore_missing.py --dry-run .venv\Scripts\python.exe scripts\products_restore_missing.py :: ⑥ 重启 + 核验 .venv\Scripts\python.exe guanlan.py stop ; .venv\Scripts\python.exe guanlan.py serve .venv\Scripts\python.exe -m src.ontology.maintenance :: 数据层/机理层的条数与末次更新 ``` **只想要"能打开页面"的最小集**: ② 的 `rebuild_from_raw.py` + ③ + ⑤ 三步即可 (⑤ 把没有生成端的件补上, 页面就有数); ① 只在换了现场包时需要。 **验收锚点**: `outputs\rudong\windscada\alarms.parquet` 39211 行 · `temp_monthly.parquet` 19494 行(与随包件逐值一致) · 本体 `objects.json` 9701 对象 / audit 0 问题 · `_provenance.json` 记着每件是 raw 重算还是随包补齐。 ## 6. 怎么看"数据真的进去了" **命令行(不开浏览器)**: ```bat .venv\Scripts\python.exe -m src.ontology.maintenance :: 数据层+机理层逐行: 覆盖/条数/末次更新/摄入命令 .venv\Scripts\python.exe guanlan.py status :: 各服务端口与进程 .venv\Scripts\python.exe guanlan.py check :: 自检(依赖/产物/发布件/模型) ``` **浏览器**: `http://127.0.0.1:28084/detail/` → 维护页「数据层 + 机理层」那两屏 —— 每行都写着**它自己的摄入命令**, 以后忘了敲什么直接看页面。 **回归尺子**(与出厂基线比对 10 个端点): ```bat .venv\Scripts\python.exe scripts\page_fingerprint.py --save <本次指纹.json> .venv\Scripts\python.exe scripts\page_fingerprint.py --diff <基线指纹.json> ``` ## 7. 会遇到的"算不出来"(都不是操作错) | 现象 | 原因 | 怎么办 | |---|---|---| | `/cms/` 503 | CMS 的源是**振动线 handoff(测点索引)**, 现场包里只有成品牌报告 PDF | 找振动线要测点导出件 | | `/detail/api/query|turbine|fleet` 回 `err=no_products` | 它们依赖**没有生成端**的 L1 底座件(`temp_monthly.parquet` 那批多数已补,见清单) | 见 `docs\重算缺口与补件清单_v0.1.md` | | 油样只有 404 行(出厂 506) | 差的那 102 行来自一份**合并报告**, 该 PDF 不在现场包里 | 把该批报告放进 `油样报告\` 后重跑 ① | | 门户没有"契约结论段"、`/api/facts` 503 | ~~契约要人裁底稿~~ 2026-09-19 起 claim 由重算台账生成(`scripts\sop_findings_from_ledger.py`,链上 ⑤b/⑤c) | 跑一次重算(⑤b → ⑤c)即得 | | `populate` / `audit` 退出 1 | ~~卡在 `pitch\pitch_daily.parquet`(变桨产物无生成端)~~ 变桨面 2026-09-19 起有生成端;若仍退 1,先看该件在不在(链上 ④c) | 跑 `python scripts\pitch_face_build.py`(读 `data\raw\如东\{scada_10min,scada_1min}`,约 12 分钟)或整链 `rebuild_all.py` | | `/local-ai/` 503 | 本机 Ollama 未运行或没有模型(环境项, 与数据无关) | `ollama pull` 见 `configs\models.json` | ## 8. 三个必须知道的坑 1. **「清除产物」= 真删除, 没有备份**(2026-09-16 用户令)。`products_state.py --off` 需显式 `--yes`; 旧的 `--on` 已移除。清完要恢复: `products_restore_missing.py --stash <交付包.zip>`(补随包件) + `rebuild_all.py`(重算 raw 派生件)。 清之前如果想留一份回退路, 就把整个 `outputs\<场>` 改名(§5b 的手工做法) —— 那是**你自己的临时目录**, 不是系统备份。 2. **产物变了通常不用重启服务**(2026-09-16 起): detail 按产物指纹自动重载、CMS 每请求现读、门户按 mtime 失效。 只有改 `scripts/guanlan_ops.py`(控制台本身)才必须重启**网关**。 3. **重算中的按钮是灰的, 这是设计**: 有任务在跑时「执行重算/清除产物/停服务」全禁用, 防止并发; 此时页面显示"正在执行 … "与实时日志尾巴(2 s 刷新)。**日志真的一直空白**才是异常(2026-09-16 修过一次: `CREATE_NO_WINDOW` 会吸走子进程句柄)。 --- ## 一页速记(最短路径) ```bat :: 现状 .venv\Scripts\python.exe scripts\scan_stations.py :: 放数据(现场包还在 <现场包目录>) .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope full :: 算 .venv\Scripts\python.exe scripts\rebuild_from_raw.py .venv\Scripts\python.exe scripts\rebuild_from_raw.py --scada :: 约 15 分钟 .venv\Scripts\python.exe -m src.ontology.kb_ingest :: 重启 .venv\Scripts\python.exe guanlan.py stop .venv\Scripts\python.exe guanlan.py serve :: 看 .venv\Scripts\python.exe -m src.ontology.maintenance :: 浏览器 http://127.0.0.1:28084/detail/ → 维护页「数据层」 ``` 相关文档: `docs\数据目录结构与落位约定_v0.2.md`(§2 落位 · §4 重算边界表) · `docs\重算缺口与补件清单_v0.1.md`(缺什么找谁补) · `docs\振动数据接入_v0.1.md`(振动侧 CMS/TCM 落位与摄入)