版本: v0.2.0 现场版 · 2026-09-11 适用:
<安装目录>(安装目录, 下称<安装目录>) 本文回答三件事: ①离线数据往哪放、系统怎么认;②每个目录干什么用;③放/不放数据时系统分别表现什么。
<安装目录>\
├─ 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, 变更历史由版本库承载)
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), <row> 需带 TimeOn + Alarmcode;「全年/年至今」累计快照件会自动跳过 |
风机故障记录\ |
检修工单台账 | 模板表(表头含 机组编号+故障名称+故障代码);表里 风场名称 必须属本场(集团导出件混着十来个场);按年分组不限层级 |
油样报告\ |
油液化验报告 | .pdf, 文件名须含 日期_台号_部件(如 17072025_10303681_…_1#_主轴后.pdf) |
windcms\ |
CMS 原始测量导出 + 厂商月度评估报告 | 导出为 测量根\<年>\<月>\<WTGxx>\*_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 重抄一遍)。
.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表里保留了这条记录并标注了推翻原因。
用户令: 「数据层里的「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_<uuid>_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 快照", 只有索引/谱/报告/知识库是可重算的。
四类源的消费方式不同 —— 这决定了"放进去"和"用上"之间差哪一步:
| 源 | 运行期是否实时读 | 要跑什么才进系统 | 影响的页面/面板 |
|---|---|---|---|
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) |
命令(在 <安装目录> 下跑):
.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 原始导出 → 窗索引/谱/报告
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 行的源件不在包内) |
场景① 清空 data\raw + 挪开产物 → 系统无数据呈现 ✅ 通过
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 → 系统有数据呈现 ✅ 通过
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不会自动清产物 —— 这是刻意的(产物是"上一次算好的结果", 现场拔盘不该让页面变空)。要验证"无数据呈现", 必须同时挪开产物, 如上。
产物统一在 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 个 <template>:治理清单分册、单机与整机报告、仿真台面板),只有 138 KB 是门户自己的壳。因此:
release\portal_src\shell.html(138 KB,含 <!--@TEMPLATE:id--> 与 <!--@GOVERNANCE_SOURCES--> 标记)、
manifest.json(各件 sha256 + 期望门户 sha256)、README.md —— 入库;release\portal_src\templates\(20.04 MB 交付件正文)、governance_sources.json、release\portal.html ——
按产物处理(.gitignore,磁盘文件随包分发、服务照读);scripts\portal_build.py --extract / (默认装) / --verify / --check;--verify 实测"拆→装"回到
逐字节相同的文件(sha256 9b6aabeb6ca18d15…,20,226,052 B)。行尾必须是 LF —— 三个相关脚本
都用字节级写文件(newline=""),装配后另有 CRLF 自检。<section id="contract-claims"> 是产物(由 guanlan_portal_inject_claims.py 在装配最后注入,
内容来自 outputs\<场>\guanlan\derived\portal_claims.json)—— 产物挪走时装出的门户会缺这一段,属预期。三条硬约定(用户令 2026-09-11;由 scripts\check_portability.py 自动门禁):
/Users/…、/Volumes/…、C:\……)。
整包拷到别的电脑/别的盘、或换 Linux,都不需要改任何路径。src\paths.py 解析;Path('outputs/…') 这种 cwd 相对写法
会让"从别处调用同一个脚本"指到别的地方(本仓实测曾有 20+ 处,已全部归到 paths)。outputs/rudong/…,见 P.rel());电脑间传输不受
分隔符影响。显示给人看时才用本机分隔符(P.disp() / P.disp_dir())。唯一真源:src\paths.py
from src import paths as P
P.ROOT # 安装根 (env WINDSCADA_ROOT → 否则按 src/paths.py 位置回溯)
P.RAW_ROOT # data/raw (离线数据入口)
P.store() # outputs/<场>/windscada P.ont() / P.cms() / P.m5() / P.sop() / P.guanlan() / P.pitch()
P.guanlan()/… P.tcm_replay() P.RELEASE / P.PORTAL / P.VIEWER / P.SIM_DIR / P.LOGS / P.RUN
P.rel(p) # → 'outputs/rudong/windscada/…' (POSIX 相对, 用于清单/契约)
P.disp(p) # → '<安装目录>/…' (显示)
P.venv_python() # 跨平台探测 .venv/Scripts/python.exe 或 .venv/bin/python
configs\serve.json 的 python 现在写相对路径(.venv/Scripts/python.exe;Linux 为 .venv/bin/python)——
换机后由启动器按安装根解析;也支持绝对路径与"留空自动探测"。
换机部署清单
# 目标机: 拷整个安装目录 → 跑安装 → 自检 → 回归比对
.venv\Scripts\python.exe scripts\check_portability.py # 1) 静态门禁: 无机器路径/无 cwd 相对
.venv\Scripts\python.exe guanlan.py check # 2) 依赖/产物/端口自检
.venv\Scripts\python.exe scripts\scan_stations.py # 3) 场站目录扫描辨识 (数据认到了没)
.venv\Scripts\python.exe scripts\page_fingerprint.py --diff <原机导出的指纹.json> # 4) 与基准机逐端点比对
第 4 步是换机验收的硬证据:指纹对每组页面/接口取 status + 归一化 sha256(自动剔掉
checked/ms/repo_head 这类设计上会变的字段,并把安装根前缀归一成 <ROOT>),因此"换了机器所以前缀不同"
不会误报,而"内容真的不对"一定报。基准机导指纹:page_fingerprint.py --save <文件名>。
已知例外(check_portability.py 的 ALLOW 里逐项写了理由):src\sop\farm_paths.py(它就是盘符映射表本体)、
scripts\sim_hub\*(开发期生成器)、outputs\rudong\sop\*.py(历史装配脚本留档)、若干文档字符串/证据文本
里的路径引用,以及交付侧离线工具(scripts\guanlan_cloud_* 等,ROOT 相对但尚未收敛为 paths,属 WARN 级)。
| 日期 | 变更 |
|---|---|
| 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) |
| 2026-09-11 | 路径跨平台化:新增 src\paths.py 唯一真源;清理机器绝对路径(mac 检出/TCM 主仓/写死解释器)与 20+ 处 cwd 相对路径;serve.json 的 python 改相对;新增静态门禁 check_portability.py 与回归尺子 page_fingerprint.py;改造前后 10 个端点指纹逐字节一致;补本文件 §6 产物地图与 §7 跨平台约定 |
| 2026-09-11 | 门户拆包:release\portal.html 拆成受管外壳 release\portal_src\shell.html(138 KB) + 不入库内嵌件 templates\(28 件 20.04 MB)与 governance_sources.json;新增 scripts\portal_build.py(--extract/--verify/--check),实测拆→装逐字节一致(见 §6);配套 git rm --cached release\portal.html |
| 2026-09-11 | 验证闸门退出码修复:控制台 GBK 编不出 ✔ 时降级为 ?(新增 src\console.py),修 page_fingerprint / check_portability / rebuild_from_raw / scan_stations / portal_build —— 此前"全部一致"会因 print 抛异常而退出码 1(通过被报成失败) |
| 2026-09-11 | 从零重算实测 + 落位扩范围:place_raw_data.py 新增 --scope a2\|mech\|full(机理层技术资料 + 对译表 + scada_1min)、修"单文件规则恒不落件"与缺 import os(自 d90da05 起脚本跑不通)、同尺寸文件跳过;src\ontology\ 全层 10 处文本 I/O 补 encoding='utf-8'(中文 Windows 上本体层原本根本不可用:默认 cp936 写 objects.json 崩在 \u200b);check_portability.py 新增 WARN 项 6「文本读写缺 encoding=」;实测数字见 §4 末 |
| 2026-09-11 | 收尾这 18 处同类文本 I/O(scripts\ontology_p2_verify.py、scripts\windscada_serve.py、src\ontology\scenario_29.py、src\windcms\{knowledge,pipeline,plugins}.py、src\windscada\subsys\fusion.py、src\windscada\taxonomy.py),门禁 WARN 项 6 扫描面同时扩到 open('w') 与 open(p)(无 mode = 文本读);WARN 归零 |
| 2026-09-12 | 振动侧落位与摄入(本文 §2b):data\raw\如东\ 新增 windcms(CMS 原始测量导出 25,679 件 150 GB + 上海电气月度报告) / m5_cms_tcm(大生科技 TCM 报告) 两个约定子目录(config.STATION_SUBDIRS 同步);补齐振动线分支缺失的摄入端 scripts\rudong_tcm_index.py(54 列窗索引, 与包内 tcm_index.parquet 同构)与 scripts\rudong_tcm_spectra.py(npz 谱库 + spectra_meta.parquet);新增一键 scripts\vib_raw_build.py(索引→谱→报告/知识库, 并行+窗体清单)与厂商报告摄入 scripts\vib_reports_build.py;place_raw_data.py 新增 --scope vib(含散装件规则与每次开包一次的写法修正: 旧写法对 2.5 万条目是平方复杂度);rebuild_all.py 新增 ④b 步;check_transferable.py 文本扫描排除 data\raw\(现场原始件不随包, 扫它只会把秒级闸门拖成小时级)。细节见 docs\振动数据接入_v0.1.md |
| 2026-09-12 | 不再随包"修复记录/临时"类目录(用户令: 本包已纳入 git 管理): pack_dist.py 的 INCLUDE_DIRS 去掉 _修复记录_20260911(其内容并入 docs\振动数据接入_v0.1.md 与本文 §2b), 相关文档/README 的引用一并改指正式文档 |
| 2026-09-12 | 振动侧实测复核(见 docs\振动数据接入_v0.1.md): 落位 25,693 件/150.2 GB; 摄入出窗 w0316(2,066,686 行 × 54 列 + 420,742 条谱, 全链 875 s); data.windows/load_scalars/spectrum 三处消费实测通过(谱图此前是死的)。★ 同时逮到一条坑: 在本包跑 windcms.py report 会用残缺输入覆盖完整快照(report.md −97%)—— 因为六层链的 model_run/fusion 产物缺失, 故该步改为 --with-report 显式开启, outputs\rudong\windcms 已逐字节还原为随包快照(56 件/0 差异)。新增 src\derived_manifest.py(产物来源自登记)让台账认出新造件 |