数据目录结构与落位约定_v0.2.md 13 KB

观澜 · 如东样板 v2 目录结构设计与落位约定

版本: v0.2.0 现场版 · 2026-09-11 适用: F:\temp\guanlan-rudong-v2_0.2.0(安装目录, 下称 <安装目录>) 本文回答三件事: ①离线数据往哪放、系统怎么认;②每个目录干什么用;③放/不放数据时系统分别表现什么。


1. 一页速览

<安装目录>\
├─ data\raw\                         ★离线数据唯一入口 (现场数据只放这里)
│    └─ <场站名称>\                    ← 扫描辨识的抓手: 下一级目录就是场站名
│         ├─ scada_10min\             SCADA 10min 导出 (逐台 WTG01.csv…WTG38.csv)
│         ├─ scada_1min\              (可选) 1min 导出
│         ├─ 故障报警\                 报警事件导出 (SpreadsheetML *.xls)
│         ├─ 风机故障记录\             检修工单台账 (*.xls/xlsx, 内按 {年}年故障记录\ 分年)
│         └─ 油样报告\                 油液化验报告 (*.pdf)
├─ 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\                          端口/模型/场配置/机型契约
│    ├─ serve.json                   端口、发布目录、Python 路径 (install 脚本写)
│    ├─ models.json                  Ollama 档位与 pull 命令
│    ├─ farms\<场名>.json             外场配置 (内置 rudong 可不配); _模板.json.example 是模板
│    ├─ 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\                             说明书与本文
├─ logs\ run\                        运行日志 / pids.json (不入 git)
└─ _修复记录_20260911\                现场修复记录与可重放脚本

2. data\raw\ 落位约定与扫描辨识

一句话: data\raw\ 的下一级目录就是场站名称, 系统在取数时扫描辨识, 不写死目录名。

辨识规则(逐条命中即止, 依据会打印出来, 见 scripts\scan_stations.py 与维护页那一行「场站数据目录 (扫描辨识)」):

# 规则 例
① 目录名与场配置 raw_station 完全相同 配置 "raw_station": "如东" ↔ 目录 data\raw\如东\
② 目录名与场名写法 src_farm_names 互为子串 配置 ["如海","如东"] ↔ 目录 如东海上风电场\ 也能认
③ data\raw\ 下只有这一个场站目录(单站部署) 采用它, 但报告里标"凭单站唯一性", 不假装精确匹配
④ 多目录且都不匹配 不猜: 报"未识别", 页面显示无数据并列出扫到的目录

约定子目录(这五个名字是摄入接口, 改名等于换接口):

子目录 放什么 文件形态要求
scada_10min\ SCADA 10min 导出 逐台平铺 WTG01.csv…WTG38.csv(放文件本身, 不是压缩包)
scada_1min\ 1min 导出(可选) 同上, 台号须在场配置的 38 台内
故障报警\ 报警事件导出 .xls 实为 SpreadsheetML(XML), <row> 需带 TimeOn + Alarmcode;「全年/年至今」累计快照件会自动跳过
风机故障记录\ 检修工单台账 模板表(表头含 机组编号+故障名称+故障代码);表里 风场名称 必须属本场(集团导出件混着十来个场);按年分组不限层级
油样报告\ 油液化验报告 .pdf, 文件名须含 日期_台号_部件(如 17072025_10303681_…_1#_主轴后.pdf)

机理层(厂商资料)按 A2 仍放 data\raw\西门子4.0技术资料\, 不在场站目录下。


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 数据层「油液化验」、油液时效胶囊、融合面油样轴

命令(在 <安装目录> 下跑):

.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           # 现场压缩包 → 约定子目录 (映射表)

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 与随包基线逐值完全一致)

⛔ 包内没有生成端 / 源件不在数据包内(只读不写, 因此 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 未附构建脚本
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 月表
objects.json(本体)· windcms\* · m5_cms_tcm\* 图谱/CMS/融合 来源是 data\raw\西门子4.0技术资料 与振动线, 不属于上面四类

5. 两个验收场景(已实测, 2026-09-11 14:2x)

场景① 清空 data\raw + 挪开产物 → 系统无数据呈现 ✅ 通过

Move-Item .\data\raw\如东 F:\temp\_raw_off            # 数据挪走
# 把「这四类源产出的产物」也挪走 (只清源不动产物, 页面照旧显示旧数 —— 设计如此, 见 §3)
Move-Item .\outputs\rudong\windscada\{alarms,workorders,oil_samples_index,loss_monthly,powercurve_bins,powercurve_dev}.parquet F:\temp\_prod_off\
.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 → 系统有数据呈现 ✅ 通过

Move-Item F:\temp\_raw_off .\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. 变更记录

日期 变更
2026-09-11 A2: 四项数据源改为 data\raw\<场站名称>\…
2026-09-11 场站目录改为扫描辨识(config.scan_stations() / station_scan(),不再写死目录名),维护页新增「场站数据目录 (扫描辨识)」一行
2026-09-11 补三类摄入(报警/工单/油样)+ 总入口 rebuild_from_raw.py + 等价验收 --verify;新增 scan_stations.py、place_raw_data.py
2026-09-11 无数据时实时接口不再抛 500,改为结构化"无数据"回答(场景① 逮到)
2026-09-11 两个验收场景实测通过(见 §5);记录油样 102 行华标批的源件缺口(见 §4);补本文件