# 观澜 · 如东样板 v2 目录结构设计与落位约定 > 版本: v0.2.0 现场版 · 2026-09-11 > 适用: `<安装目录>`(安装目录, 下称 `<安装目录>`) > 本文回答三件事: ①离线数据往哪放、系统怎么认;②每个目录干什么用;③放/不放数据时系统分别表现什么。 --- ## 1. 一页速览 ``` <安装目录>\ ├─ data\raw\ ★离线数据唯一入口 (现场数据只放这里) │ └─ <场站名称>\ ← 扫描辨识的抓手: 下一级目录就是场站名 │ ├─ scada_10min\ SCADA 10min 导出 (逐台 WTG01.csv…WTG38.csv) │ ├─ scada_1min\ (可选) 1min 导出 │ ├─ 故障报警\ 报警事件导出 (SpreadsheetML *.xls) │ ├─ 风机故障记录\ 检修工单台账 (*.xls/xlsx, 内按 {年}年故障记录\ 分年) │ ├─ 油样报告\ 油液化验报告 (*.pdf) │ ├─ windcms\ ★振动侧: CMS 原始测量导出 + 厂商月度评估报告 (2026-09-12 新增) │ └─ m5_cms_tcm\ ★振动侧: TCM 侧深度分析报告 + 现场给的 handoff 正本 ├─ outputs\rudong\ ★系统取数用的**产物仓** (页面 99% 读这里, 不直接读 raw) │ ├─ windscada\ L0 标准仓: 37 个 parquet + 索引/日志 │ ├─ ontology\ 本体对象库 objects.json + 检索索引 + release_r1/r2 │ ├─ windcms\ CMS 振动诊断产物 (报告/缓存/单机页) │ ├─ m5_cms_tcm\ 振动线 handoff 与 TCM 兼容件 │ ├─ guanlan\facts_contract_v0.json 事实契约 (问答引文的依据) │ └─ sop\ SOP 中间件/评审/台账 (含少量历史装配脚本) ├─ configs\ 端口/模型/场配置/机型契约 (目录约定见 系统设计说明 §11) │ ├─ serve.json 端口、发布目录、Python 路径 (install 脚本写) │ ├─ models.json Ollama 档位与 pull 命令 │ ├─ portal_pages.yaml 门户页面归口登记表 (哪些页是产物/该不该随数据变, §10) │ ├─ registry.yaml 配置登记表 (每个域是什么、谁读它、缺哪些) │ ├─ farms\<场名>.yaml 外场配置 (**YAML**, 内置 rudong 可不配); _模板.yaml.example 是模板 │ │ ★ 同目录还有一类 schema 不同的"机型/物理约束 profile"(meta+physical_constraints), │ │ 不是场定义, 不参与场加载 (见 §11) │ ├─ contracts\ 机型契约 (变桨/偏航/发电机…判据参数) │ ├─ canonical\ terms\ 术语与词典 (canonical 决策/字典/手册; terms 显示映射) ├─ src\ 库源码: windscada(分析) / windcms(振动) / ontology(本体) / sop(方法) ├─ scripts\ 服务与工具: 网关/工作台/CMS + 摄入链 + 扫描/落位 ├─ release\ 交付层: portal.html(门户) viewer(三维) sim_sys_server.py │ └─ 如东\ 客户交付件 (治理清单/取数单), 按约定不入 git ├─ resources\oem_envision_sc1_rudong2014\ 仿真回放资产 ├─ reference\rudong\ 契约与语言资产 (windscada_contract.yaml / 英文语言库 / 报警码表 …) ├─ wheels\win_amd64\ 离线轮子 (随包分发, 不入 git) ├─ vendor\ 便携 Python / Ollama 离线包 (不入 git) ├─ docs\ 说明书、落位约定、振动接入与本文 └─ (不随包: `_修复记录_*`/临时目录 —— 本包已纳入 git, 变更历史由版本库承载) ``` --- ## 2. `data\raw\` 落位约定与扫描辨识 **一句话**: `data\raw\` 的**下一级目录就是场站名称**, 系统在取数时**扫描辨识**, 不写死目录名。 辨识规则(逐条命中即止, 依据会打印出来, 见 `scripts\scan_stations.py` 与维护页那一行「场站数据目录 (扫描辨识)」): | # | 规则 | 例 | |---|---|---| | ① | 目录名与场配置 `raw_station` **完全相同** | 配置 `"raw_station": "如东"` ↔ 目录 `data\raw\如东\` | | ② | 目录名与场名写法 `src_farm_names` **互为子串** | 配置 `["如海","如东"]` ↔ 目录 `如东海上风电场\` 也能认 | | ③ | `data\raw\` 下**只有这一个**场站目录(单站部署) | 采用它, 但报告里标"凭单站唯一性", 不假装精确匹配 | | ④ | 多目录且都不匹配 | **不猜**: 报"未识别", 页面显示无数据并列出扫到的目录 | 约定子目录(这些名字是摄入接口, 改名等于换接口; 前五个是 SCADA/台账侧, 后两个是振动侧, 见 §2b): | 子目录 | 放什么 | 文件形态要求 | |---|---|---| | `scada_10min\` | SCADA 10min 导出 | **逐台平铺** `WTG01.csv…WTG38.csv`(放文件本身, 不是压缩包) | | `scada_1min\` | 1min 导出(可选) | 同上, 台号须在场配置的 38 台内 | | `故障报警\` | 报警事件导出 | `.xls` 实为 SpreadsheetML(XML), `` 需带 `TimeOn` + `Alarmcode`;「全年/年至今」累计快照件会自动跳过 | | `风机故障记录\` | 检修工单台账 | 模板表(表头含 `机组编号`+`故障名称`+`故障代码`);表里 `风场名称` 必须属本场(集团导出件混着十来个场);按年分组不限层级 | | `油样报告\` | 油液化验报告 | `.pdf`, 文件名须含 `日期_台号_部件`(如 `17072025_10303681_…_1#_主轴后.pdf`) | | `windcms\` | CMS 原始测量导出 + 厂商月度评估报告 | 导出为 `测量根\<年>\<月>\\*_decode.json`(Brande TCM 导出, 目录层级不限); 报告为 `.docx`(可解析) 或 `.pdf`(扫描件只归档) | | `m5_cms_tcm\` | TCM 侧深度分析报告 + handoff 正本 | `.docx` 报告; 若现场给出 `handoff_vibration_v2.json` / `component_history.json` 正本, 放这里即被优先采用 | 机理层(厂商资料)按 A2 仍放 `data\raw\西门子4.0技术资料\`, **不在场站目录下**。 **怎么把现场包落成这个样子**: `scripts\place_raw_data.py` 把"包里的哪个目录 → 落到哪"写成一张表, 可复核、可重跑(`--dry-run` 先看计划; 同尺寸文件自动跳过, 重跑不会把十几 GB 重抄一遍)。 ```bat .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope a2 --dry-run .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope full ``` | 范围 | 落什么 | 依据 | |---|---|---| | `--scope a2`(默认) | 上表四类(`scada_10min`/`故障报警`/`风机故障记录`/`油样报告`) | A2 约定的四项数据层 | | `--scope mech` | `data\raw\西门子4.0技术资料\`(317 件 2.7 GB,含那份**对译表**)+ `<场站>\scada_1min\`(38 件 12.8 GB) | 本体层构建器 `kb_ingest.py` 的 `TECH` 与 `rd()` 四个文件名;`config.STATION_SUBDIRS` 声明的 `scada_1min` | | `--scope vib` | `<场站>\windcms\`(CMS 原始测量导出 25,679 件 150 GB + 上海电气月度报告)+ `<场站>\m5_cms_tcm\`(大生科技 TCM 报告) | §2b | | `--scope full` | 三组一起 | 从零重算要用的全集 | > 为什么 `scada_1min` 也属"该落的": 它写在 `config.STATION_SUBDIRS` 里, `scan_stations.py` 会把它报成 > "缺"。但**包内没有消费者** —— 落它是补数据层完整性, 不改任何页面数值。同理, 现场包里的 > `scada数据(如东)\`(19 个月原始通道导出)、`fastlog数据\` 两种**故意不落**: > 前者是 `scada_10min` 的上游且无人读, 后者全库只有 2 处注释提到 —— 理由都逐条写在脚本的 `SKIPPED` 表里。 > > ★ 关于 `(8)…\振动分析报告\`(12 份月度用印版 PDF): v0.2.0 初版把它列在"故意不落"里, 理由是 > "成品牌报告而非可再加工的测量数据, 且 windcms 要的测点索引包里没有"。**那条判断在 2026-09-12 被推翻**: > 用户把 CMS 原始测量导出 (`CMS_RuDong_CGN_202603-04.zip`) 补进了现场包, 索引源件已具备, 于是 > 报告作为数据层证据一并落位(见 §2b)。脚本的 `SKIPPED` 表里保留了这条记录并标注了推翻原因。 --- ## 2b. 振动侧落位与摄入链(2026-09-12 新增) **用户令**: 「数据层里的「CMS 振动评估报告」应遵循 `<安装目录>\data\raw\如东\windcms`、 「振动线 handoff」应遵循 `<安装目录>\data\raw\如东\m5_cms_tcm` 存放; 修改系统支持振动数据参与运行、重算; 自现场包提取相应振动数据存放至上述目录」。 落位后的实际形态(`--scope vib`): ``` data\raw\如东\ ├─ windcms\ │ ├─ CMS_RuDong_CGN_202603-04\ ← 原始测量导出原样保留包内层级 (可追溯"哪来的") │ │ └─ measurement\2026\{03,04}\WTGxx\WTGxx__decode.json 25,679 件 / 150 GB │ └─ 厂家报告\上海电气_月度\ │ ├─ 中广核如东海上风电场2025年06月…2026年05月振动分析报告用印版.pdf 12 件 (扫描件, 只归档) │ ├─ 中广核如东海上风电场2026年07月振动分析报告_上海电气.docx (有文本层, 摄入) │ └─ _扫描件清单.json (登记: sha256/大小/无文本层) └─ m5_cms_tcm\厂家报告\ └─ 中广核如东海上风电场传动链振动分析报告_大生科技_20260311(1).docx (TCM M-system, 摄入) ``` **摄入链(两条, 分工不同, 别混)**: | 源 | 摄入命令 | 产出 | 谁消费 | |---|---|---|---| | CMS 原始测量导出(`*_decode.json`) | `python scripts\vib_raw_build.py`(= `rudong_tcm_index.py` → `rudong_tcm_spectra.py`;★**不**跑 `windcms.py report` —— 缺六层链产物时重生成会掉内容, 实测 `report.md` −97%, 见 `docs\振动数据接入_v0.1.md` §3b) | `outputs\rudong\m5_cms_tcm\windows\<窗>\index.parquet`(54 列)+ `…\<窗>\spectra\*.npz` + `spectra_meta.parquet` | `src\windcms\data.py`(窗/标量/谱图自动收录)、`scripts\windcms.py report/serve`、`src\windscada\taxonomy.py`(设备状态转录) | | 厂商 docx 评估报告 | `python scripts\vib_reports_build.py` | `outputs\rudong\windcms\厂家报告提取_<报告期>.json` + `报告_CMS振动状态评估报告_<报告期>.md`;`outputs\rudong\m5_cms_tcm\报告_TCM传动链振动分析_<报告期>.md` | 同上(报告 md 与自产报告**同构**, 消费端不必改代码) | 窗名口径: `wMMDD` = **数据起始日**(与既有 `w0127`/`w0707`/`w0811` 同口径, `pipeline.window_year_gate` 会拦"窗名无年份"的历史误用)。同一批数据重复摄入时, 已存在的窗会改名为 `<窗>_reimport_<时分>`, 而 `data.EXCLUDE_DEFAULT` 会把带 `_reimport` 的窗排除在生产集外 —— 重复摄入不会污染分析集(2026-08-24 的教训)。 **这一步之后"振动数据就真的参与了"的证据链**: `data.windows()` 自动发现新窗 → `load_scalars()` 把 新窗标量并进 CMS 报告 → `data.spectrum()` 能取到谱(此前 `m5\spectra` 整个目录缺失, 谱图是死的)→ `fusion.windcms_grades()` / `taxonomy.system_matrix()` 读最新报告 md 做设备状态转录。 **仍然缺的(如实标注, 不冒充)**: 六层链的四步脚本 `rudong_tcm_oem_scan.py` / `rudong_line_energy_share.py` / `rudong_model_run.py` / `rudong_fusion_run.py` **未随包**(原在振动线分支 `claude/vibration-data-diagnosis-32b69e`)。 `vib_raw_build.py` 产出的 `outputs\rudong\m5_cms_tcm\vib_raw_manifest.json` 里 `missing_chain` 字段列明这四项。 因此扫描线/能量占比/模型层/融合层那几类 parquet 仍走"包内 shipped 快照", 只有**索引/谱/报告/知识库**是可重算的。 --- ## 3. 各目录的功用:谁在读、什么时候读 四类源的消费方式不同 —— 这决定了"放进去"和"用上"之间差哪一步: | 源 | 运行期是否实时读 | 要跑什么才进系统 | 影响的页面/面板 | |---|---|---|---| | `scada_10min\` | **是**(`windscada_serve.py` 的 `adhoc_query` 请求时现读 `WTGnn.csv`) | 派生产物需 `rebuild_from_raw.py --scada` | 工作台「台内即时查询」等原始窗**立即**生效;损失/可用率/温度/偏航等派生面需重建 | | `故障报警\` | 否(只有摄入脚本读) | `scripts\windscada_alarms_ingest.py` | 数据层「报警事件」、故障分析、停机事件、限电绑定 | | `风机故障记录\` | 否 | `scripts\windscada_workorder_ingest.py` | 数据层「检修工单台账」、检修面、闭环验证 | | `油样报告\` | 否 | `scripts\windscada_watch_channels_build.py` | 数据层「油液化验」、油液时效胶囊、融合面油样轴 | | `windcms\`(CMS 原始导出) | 否(窗是**摄入时**落盘的) | `scripts\vib_raw_build.py`(索引→谱→报告/知识库) | CMS 系统的谱图/标量与报告(`windcms.py serve`);`taxonomy` 的设备状态转录 | | `windcms\`/`m5_cms_tcm\`(厂商报告) | 否 | `scripts\vib_reports_build.py` | 数据层的厂商评估报告转录(与自产报告同构的 md) | 命令(在 `<安装目录>` 下跑): ```powershell .venv\Scripts\python.exe scripts\scan_stations.py # 看扫到了什么、辨识依据 .venv\Scripts\python.exe scripts\rebuild_from_raw.py # 跑三类台账摄入 (秒级~分钟级) .venv\Scripts\python.exe scripts\rebuild_from_raw.py --scada # 再加 SCADA 侧 10 个构建器 (慢) .venv\Scripts\python.exe scripts\rebuild_from_raw.py --verify# 与随包基线逐值等价验收 .venv\Scripts\python.exe scripts\place_raw_data.py # 现场压缩包 → 约定子目录 (映射表) .venv\Scripts\python.exe scripts\vib_raw_build.py # 振动侧: CMS 原始导出 → 窗索引/谱/报告 ``` --- ## 4. `data\raw` 能重建什么、不能重建什么(实测, 2026-09-11) `outputs\rudong\windscada\` 是页面取数的**产物仓**。其中哪些能由 `data\raw` 重算出来, 是"数据驱动程度"的边界: **✅ 能从 `data\raw` 重算**(构建器都在包内, 已实测等价) | 产物 | 来源 | 入口 | |---|---|---| | `alarms.parquet` | 故障报警 | `windscada_alarms_ingest.py`(39211/39211 行逐值一致) | | `workorders.parquet` | 风机故障记录 | `windscada_workorder_ingest.py`(1574 种内容复现 1566 种, 8 种差异已逐条定位) | | `oil_samples_index.parquet` | 油样报告 | `windscada_watch_channels_build.py`——**部分可重建**: 404/506 行(SGS 批, 文件名可解析)逐值一致; 见下表缺口 | | `powercurve_dev/bins` · `loss_monthly` · `curve_lenses/liveness` · `control_profile/schedule` · `stop_events` · `temp_bins` · `yaw_daily` · `hydraulic_accum` · `thermal_chain` · `system_aux` | scada_10min + alarms | `rebuild_from_raw.py --scada`(实测: `loss_monthly` 3729/3729、`powercurve_dev` 38/38、`powercurve_bins` 912/912 与随包基线**逐值完全一致**) | | `objects.json`(本体 2340 个对象)· `retrieval_index.json` | **厂商技术资料** `data\raw\西门子4.0技术资料`(不在场站目录下) | `python -m src.ontology.kb_ingest`;检索索引 `python -c "from src.ontology import retrieval as R; R.build(use_vec=False)"`(BM25 词法索引; 向量那半要 Ollama 的 `bge-m3`, 本机没装模型时会跳过并保持纯词法可用) | | `turbine_params.parquet`(1706 条整定值) | 同上, `Turbine+Parameters.*.xlsx` | `python -c "from src.ontology.maintenance import refresh_params as f; f()"` | | `m5_cms_tcm\windows\<窗>\index.parquet`(54 列标量索引) | **CMS 原始测量导出** `data\raw\如东\windcms\**\*_decode.json` | `scripts\rudong_tcm_index.py`(旧包缺这个脚本 → 2026-09-12 补齐; 列名列序 dtype 与包内 `tcm_index.parquet` 逐列对齐, 实测 FFT 行取值逐格一致) | | `m5_cms_tcm\windows\<窗>\spectra\*.npz` + `spectra_meta.parquet` | 同上 | `scripts\rudong_tcm_spectra.py`(`DataSets.DataSet.Values` 是空格分隔字符串: `Size=Lines+1`, `X-axisDelta=带宽/Lines`) | | `windcms\报告_CMS振动状态评估报告_<报告期>.md`(厂商报告转录) | 厂商 docx 评估报告 | `scripts\vib_reports_build.py`(合并单元格还原 + 厂家总览句作**校验和**: 实测 34优秀/3良好/1无数据逐项一致) | > 本体层这三件的前提是**技术资料在盘上**(`place_raw_data.py --scope mech` 会落, 见 §2);技术资料不在时 > `kb_ingest` 会打印 `⚠ 源缺失` 并降级 —— 不静默。 **⛔ 包内没有生成端 / 源件不在数据包内**(只读不写, 因此 `data\raw` 换数据不会更新它们) | 产物 | 谁在读它 | 说明 | |---|---|---| | `oil_samples_index` 里的 **102 行华标 2026-07 批** | 数据层「油液化验」、油液时效胶囊 | 源件是一份**合并报告** `BG-2026-07-YP013 中广核新能源如东海上风电场.pdf`(一份覆盖多台, 台号/部件只在 PDF 表格里), **且该文件不在现场数据包里** → 清空重建会丢这 102 行。摄入脚本因此采取**合并语义**: 认不出的老行原样保留、绝不删(场景② 实测就出现了 506→404 的落差, 需现场补那份 PDF 才能从 raw 全量重建) | | `pc_monthly_bins` · `duty_monthly` · `thermal_monthly` · `sector_power` | 趋势件/热链/扇区 | 组级产物, v0.2.0 未附构建脚本(全库只有读取方, 0 处写入方) | | `yaw_dynamic_monthly` · `yaw_press_monthly` · `yaw1min_liveness` · `yaw_err_clean` · `genbearing_monthly` · `mblub_monthly` | 偏航/温度/润滑面 | 同上 | | `structure.parquet` · `watch_channels_monthly.parquet` | 结构面/温度联动/融合 | 后者连包内注释都写着"须重建 `scripts\windscada_watch_channels_build.py`" —— 本版该脚本只产油样索引, 未含 watch_channels 月表 | | `windcms\*` · `m5_cms_tcm\*` · `tcm_compatible_replay\*` | CMS/融合/TCM 兼容链 | 来源是**振动线 handoff(CMS 测点索引)**; 现场包里只有成品牌报告 PDF, 没有测点数据 → 仍 ⛔(`/cms/` 因此 503) | > **本体层的可重算性取决于技术资料在不在**: `kb_ingest.py` 的 `TECH = P.RAW_ROOT/'西门子4.0技术资料'`, > 且 `rd()` 要读 `故障处理/故障处理手册.xlsx`、`广核如海风电场西门子风机故障代码中英文对译表.xlsx`、 > `如海故障代码表.xlsx`、`维护相关/维护作业指导书.xlsx` 四个文件 —— 缺任一个都会打印 `⚠ 源缺失` 并降级, > 所以 `place_raw_data.py --scope mech` 连那份不在技术资料目录里的**对译表**都单独补了一条规则。 > 本体层的其它入口 (`populate` / `chain_ingest` / `trend_ingest` / `scenario_29`) 要从 **L1 产物**转录 > (判级矩阵 `system_matrix`/`fusion_table`、振动 handoff、链盘 `/api/fleet`), 而 L1 那几件本身 ⛔, > 所以从零重算时它们要么报错退役、要么无变化 —— `populate` 实测卡在 `pitch\pitch_daily.parquet`。 > **要补什么、找谁补**: 见 `docs\重算缺口与补件清单_v0.1.md` —— 按"源件缺失(现场能补)"与 > "生成端缺失(研发补脚本)"两类逐项列了证据路径、影响面与补齐判据。 > **手工重算怎么敲**: 见 `docs\重算操作手册_v0.1.md`(逐步命令 + 期望输出 + 会踩的坑)。 **从零重算实测(2026-09-11,只用 `data\raw`,不吃随包产物)** | 环节 | 结果 | |---|---| | 三门台账 | 报警 **0→39211** 行 · 工单 **0→5876** 行 · 油样 **0→404** 行(唯一源件数 80, 其中 2 张非台账表跳过) | | SCADA 侧 10 个构建器 | 全跑通(`loss_monthly` 3729 · `temp_bins` 24624 · `yaw_daily`/`hydraulic_accum`/`thermal_chain`/`system_aux` 各 38 台 · `curve_lenses` · `control_profile/schedule` · `stop_events` · `powercurve_dev/bins`)→ L0 仓 **16 件 1.78 MB**(随包是 118 件:差额就是 §4 第二张表那些 ⛔ 件) | | 本体层 | `objects.json` 1.43 MB + `retrieval_index.json` 0.62 MB + `turbine_params.parquet` | | 等价验收 `--verify` | `alarms` 39211/39211 **逐值完全一致**; `workorders` 8 行差异全部可归类(旧链时间解析缺陷)+ 69 行本链新增覆盖; **1 处人工项 = 油样 506→404**(那 102 行的源件不在包内) | --- ## 5. 两个验收场景(已实测, 2026-09-11 14:2x) **场景① 清空 `data\raw` + 挪开产物 → 系统无数据呈现** ✅ 通过 ```powershell Move-Item .\data\raw\如东 <暂存目录> # 数据挪走 # 把「这四类源产出的产物」也挪走 (只清源不动产物, 页面照旧显示旧数 —— 设计如此, 见 §3) Move-Item .\outputs\rudong\windscada\{alarms,workorders,oil_samples_index,loss_monthly,powercurve_bins,powercurve_dev}.parquet <暂存目录>\ .venv\Scripts\python.exe guanlan.py stop; .venv\Scripts\python.exe guanlan.py serve ``` 实测结果: | 检查项 | 结果 | |---|---| | `scan_stations.py` | `扫描到 0 个场站目录`;`辨识结论 how=none`;依据: “data\raw 下没有任何场站目录 → 系统无数据可呈现” | | 维护页「数据层」 | 「场站数据目录 (扫描辨识)」`存在=False / 覆盖=—`;四项 `条数=0 / 覆盖=— / 存在=False` | | 工作台实时原始窗 `/api/query` | 首次暴露 **HTTP 500**(异常直接抛给前端,人分不清"没放数据"还是"程序坏了")→ **已修**: 改为 HTTP 200 + 结构化无数据 `err=no_source`,并给出"本机应在 `<…>\scada_10min\WTG01.csv`、放入后重跑 rebuild_from_raw.py --scada" | **场景② 按约定结构放回 `data\raw` → 系统有数据呈现** ✅ 通过 ```powershell Move-Item <暂存目录> .\data\raw\如东 .venv\Scripts\python.exe scripts\rebuild_from_raw.py # 三类台账 (实测: 报警 0→39211, 工单 0→5876, 油样 0→404) # loss_monthly 另跑 SCADA 两步 (powercurve 31s + availability 88s) .venv\Scripts\python.exe guanlan.py serve ``` 实测结果: | 检查项 | 结果 | |---|---| | `scan_stations.py` | `扫描到 1 个场站目录: 如东 (592 件)`;`how=raw_station`(依据: 目录名与场配置 raw_station 完全相同);四个子目录件数 38 / 16 / 134 / 404 | | 维护页「数据层」 | 扫描行 `覆盖=如东 / 存在=True`;10min `2025-01 ~ 2026-07 / 3729`;报警 `39211`;工单 `5876`;油样 `506`(恢复完整件后) | | 工作台实时原始窗 | `err=None, months=7, series 7 点` —— 数据回来了 | | 产物等价 | `loss_monthly` 3729/3729、`powercurve_dev` 38/38、`powercurve_bins` 912/912、`alarms` 39211/39211 **逐值完全一致**;`workorders` 5876 行仅 1 条记录有**纳秒尾差**(同一时刻 `…16:23:00.0000015` vs `…0000010`);`oil_samples_index` 需恢复那 102 行(见 §4) | > 注意: 清 `data\raw` **不会**自动清产物 —— 这是刻意的(产物是"上一次算好的结果", 现场拔盘不该让页面变空)。要验证"无数据呈现", 必须同时挪开产物, 如上。 --- ## 6. 产物地图(谁生成、谁能重算、谁在读) 产物统一在 `outputs\<场名>\` 下(当前只有 `rudong`),按"线"分仓;门户与交付件在 `release\`: | 仓 | 件数/体积 | 功用 | 生成端 | 能否由 `data\raw` 重算 | 谁在读 | |---|---|---|---|---|---| | `outputs\<场>\windscada\` | 118 件 5.6 MB | **L0 标准仓**(34 个 parquet + index.html + build 日志 + `turbines\` `review\`) | `scripts\rebuild_from_raw.py`(三类摄入 + `--scada` 十个构建器) | ✅ 大部分(见 §4) | 工作台、分台页、网关 `/release/`、所有分析面 | | `outputs\<场>\ontology\` | 24 件 100 MB | 本体对象库 `objects.json` + 检索索引 `retrieval_vec.npy` + `release_r1/r2` | `python -m src.ontology.kb_ingest` / `retrieval` / `chain_ingest` …(读 `data\raw\西门子4.0技术资料` 与仓) | ⛔(源是厂商资料,不在这四类里) | 图谱检索、问答、台账对象 | | `outputs\<场>\m5_cms_tcm\` | 90 件 53.5 MB | 振动线 handoff、TCM 兼容件、窗/谱/模型产物 | 振动线(`windcms` 会话)出件 | ⛔ | 融合面、台账判级、验收件 | | `outputs\<场>\windcms\` | 53 件 27.7 MB | CMS 报告与单机页(HTML×41) | `windcms analyze` | ⛔ | 数据层「CMS 振动评估报告」、温度/振动互证 | | `outputs\<场>\tcm_compatible_replay\` | 59 件 32.7 MB | TCM 兼容链模型表(`mask_thresholds.tsv` 等) | 振动线 | ⛔ | `windcms` 配置引用 | | `outputs\<场>\sop\` | 210 件 15.8 MB | SOP 中间件/评审/台账 + 22 个**历史装配脚本** | `src\sop\farm_pipeline.py` 等 | ⛔(历史脚本含 mac 死路径,仅留档) | 判据链、事实契约的输入 | | `outputs\<场>\paradigm_r1\` | 29 件 0.5 MB | 范式实验底稿(E3/E5/E8…) | 实验脚本 | ⛔ | 事实契约的 `source_refs` | | `outputs\<场>\guanlan\` | 5 件 0.5 MB | 事实契约 `facts_contract_v0.json` + `derived\` + `cloud\`(可上云面孔) | `scripts\guanlan_facts_contract.py` 等 | ⛔ | 问答引文、门户契约段 | | `outputs\<场>\pitch\` | 2 件 0.3 MB | 变桨零位月表 | 变桨线 | ⛔ | 变桨面 | | `release\` | — | 门户 `portal.html`(20.2 MB **装配产物**)、三维 `viewer\`、`sim_sys_server.py`、交付件 `如东\`、受管外壳 `portal_src\` | `scripts\portal_build.py`(拆 / 装) | ⛔ | 网关首页、三维页、交付 | | `logs\` `run\` | — | 运行日志、`pids.json` | 启动器/服务 | — | 排障 | > **"能重算"这一列才是关键**:`data\raw` 换数据只会更新 ✅ 那几项;其余要么来自别的源(厂商资料/振动线),要么包内没有生成端(见 §4 的 ⛔ 表)。 **门户的"壳/件分离"(2026-09-11)**:`release\portal.html` 20.23 MB 里 99.3% 是内嵌的交付文档正文 (28 个 `