振动数据接入_v0.1.md 17 KB

振动数据接入(CMS / TCM)· v0.1

日期: 2026-09-12 · 适用: <安装目录>(安装目录) 本文回答: ①振动侧的现场件长什么样、放哪;②谁把它变成系统能用的东西;③改了哪些代码、怎么验收; ④哪些还做不到(如实列,不假装)。 相关: docs\数据目录结构与落位约定_v0.2.md(§2b 落位速查)· docs\重算操作手册_v0.1.md(一键重算)


1. 用户令与现场件盘点

用户令原文(路径按本包文档约定写成占位符): 「数据层里的「CMS 振动评估报告」应遵循 <安装目录>\data\raw\如东\windcms、「振动线 handoff」应遵循 <安装目录>\data\raw\如东\m5_cms_tcm 存放; 要求 1. 修改 <安装目录> 下的观澜系统, 支持振动数据参与系统运行、重算等;

  1. 自 <现场包目录> 提取相应振动数据存放至上述目录」;随后用户把 CMS_RuDong_CGN_202603-04.zip 放进现场包 —— 那正是此前缺的原始件。

现场包(<现场包目录>)里的振动相关件共三类:

# 件 形态 能不能算
① CMS_RuDong_CGN_202603-04.zip(38.8 GB 压缩 / 150 GB 解压 / 25,679 件) Brande TCM Enterprise 导出: measurement\<年>\<月>\<WTGxx>\<WTGxx>_<uuid>_decode.json;每件是一次 API 响应 {body:{body:{"<时间戳>":[{"Record":{…}}]}}} 能(索引/谱/报告全部由它算)
② 12 份月度「振动分析报告(用印版)」PDF(2025-06…2026-05) 纯扫描件 不能
③ 上海电气 2026年07月报告 docx(4.87 MB)· 大生科技传动链报告 docx(20.81 MB) Word, 有文本层 能(逐台判级转录)

②为什么不能算(实测, 不是推测): 用 pypdf 打开 2026年5月那份 → len(pages)=12, 每页 len(images)=1, page.extract_text() 长度 0(12 页全 0)。字节面也印证: /Font 0 处、 FlateDecode 0 处、/Image 24 处、DCTDecode 12 处 = 每页一张 JPEG。 ⇒ 没有 OCR 就取不出任何数值。处理方式: 只登记归档(_扫描件清单.json 记 sha256/大小/无文本层), 不参与判级、不假装读过。要它们的数值只有两条路: 现场给电子件(docx/xlsx),或上 OCR(本包不装)。

顺带核过的一条旧判断: v0.2.0 初版把「(8)…\振动分析报告\」列在"故意不落"里,理由是 "成品牌报告而非可再加工的测量数据, 且 windcms 要的测点索引包里没有"。该判断在 2026-09-12 被推翻 (①到货后索引源件具备),故改为落位。现场包里的同件副本(根目录 …2026年5月…(2).pdf、 嵌套包 如东海上振动报告11份.zip)不重复落 —— 落三遍会得到 3 份同名报告。


2. 落位约定

data\raw\如东\
├─ windcms\                                   ← 数据层「CMS 振动评估报告」
│    ├─ CMS_RuDong_CGN_202603-04\
│    │    └─ measurement\2026\{03,04}\WTGxx\*_decode.json     25,679 件 / 150 GB
│    └─ 厂家报告\上海电气_月度\
│         ├─ 中广核如东海上风电场2025年06月…2026年05月振动分析报告用印版.pdf    12 件(扫描件)
│         ├─ 中广核如东海上风电场2026年07月振动分析报告_上海电气.docx
│         └─ _扫描件清单.json
└─ m5_cms_tcm\                                ← 数据层「振动线 handoff」
     └─ 厂家报告\
          └─ 中广核如东海上风电场传动链振动分析报告_大生科技_20260311(1).docx
  • 两个目录名已写进 src\windscada\config.py 的 STATION_SUBDIRS(扫描/落位/维护页从此认它们; 改目录名 = 换接口,要同步维护页与本文)。
  • 原始导出保留包内 measurement\ 这一层(来源可追溯);摄入按 rglob 找 *_decode.json, 套不套这层都能吃。
  • 若现场给出 handoff_vibration_v2.json / component_history.json 正本,放 m5_cms_tcm\ 下即被优先采用 (包内那两份目前仍是随包快照 —— 振动线分支的产物没随包,见 §7)。
  • 落位命令(映射表可复核、可重跑,同尺寸文件自动跳过):

    .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope vib --dry-run
    .venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope vib
    

3. 摄入链(谁把原始件变成系统能用的东西)

data/raw/如东/windcms/…/*_decode.json
   │  scripts/rudong_tcm_index.py      → outputs/rudong/m5_cms_tcm/windows/<窗>/index.parquet   (54 列)
   │  scripts/rudong_tcm_spectra.py    → outputs/rudong/m5_cms_tcm/windows/<窗>/spectra/*.npz
   │                                     + <窗>/spectra_meta.parquet(谱库目录内另存一份, 兼顾两种读取路径)
   │  [可选] scripts/windcms.py report / kb  → outputs/rudong/windcms/*(★默认不跑, 见 §3b)
   ▼
data/raw/如东/{windcms,m5_cms_tcm}/…/*.docx
   │  scripts/vib_reports_build.py     → outputs/rudong/windcms/厂家报告提取_<报告期>.json
   │                                     + 报告_CMS振动状态评估报告_<报告期>.md(与自产报告同构)
   │                                     + outputs/rudong/m5_cms_tcm/报告_TCM传动链振动分析_<报告期>.md

一键: scripts/vib_raw_build.py(索引→谱;--skip-spectra 可关;--with-report 才跑报告/知识库)。 它也写 outputs/rudong/m5_cms_tcm/vib_raw_manifest.json(源件路径、窗名、行数、谱数、时间窗、 各步耗时/rc,以及 missing_chain)。

3b. ★ 为什么"重生成 CMS 报告"默认不跑(2026-09-12 实测)

跑一次 scripts/windcms.py report 在本包会让产物变差:

件 随包快照 本包重生成后 变化
windcms/report.md 16,857 B(含逐台融合级表 + L4 过闸谱线) 467 B(融合级表空、L4 写"无") −97.2%
windcms/overview.html 646,218 B 548,466 B −15.1%
windcms/index_eng.html 443,841 B 408,112 B −8.0%
windcms/index.html 389,997 B 382,544 B −1.9%(逐台页仍 38 个)

根因不在数据,在缺件:src/windcms/data.py::load_model() 要读 m5/model_run_l6.parquet 与 m5/fusion_38.csv,而这两件属六层链的 model_run / fusion 两步 —— 那四步脚本没随包(§7),产物也不在。 于是"重生成"= 用残缺输入覆盖完整快照。

处置: 摄入默认只做索引/谱(加性、不覆盖任何随包件);报告/知识库改为 --with-report 显式开启, 且要求先备份 outputs/<场>/windcms/。本次实测后已把 outputs/rudong/windcms 逐字节还原为随包快照 (56 件,哈希比对 0 差异),只保留厂家报告转录那两件。六层链补齐后这个默认值应当翻过来。

来源登记: 摄入与转录产出的每一件都由构建脚本自登记进 outputs/<场>/_derived_manifest.json (src/derived_manifest.py),_provenance.json 生成时据此把它们记成 raw-derived —— 避免"新造的件因不在随包快照里而整条不进台账"。

窗名口径: wMMDD = 数据起始日(与既有 w0127/w0707/w0811 同口径)。 src\windcms\pipeline.py::window_year_gate 会拦"窗名无年份 + 数据过老"的误用(w1226 事故的护栏)。 重复摄入同一批数据时,已存在的窗自动改名为 <窗>_reimport_<时分>,而 data.EXCLUDE_DEFAULT 把带 _reimport 的窗排除在生产集外 —— 重复摄入不会污染分析集。

消费者(无需改一行代码,窗是自动发现的):

消费者 拿什么
src\windcms\data.py::windows() 扫 m5\windows\w????\index.parquet → 新窗进分析集
src\windcms\data.py::load_scalars() 各窗标量(ds_size==1 行)拼接 → CMS 报告/页面
src\windcms\data.py::spectrum() spectra_meta.parquet + npz['values'][shard_row] → 谱图能取到
src\windscada\taxonomy.py / subsys\fusion.py::windcms_grades() 最新 报告_CMS振动状态评估报告_*.md 的 ## 附录 A 表 → 设备状态转录

★ spectra 这一环此前是死的: 包内 m5\spectra\、m5\windows\ 两个目录整个不在, data.spectrum() 只能返回 None。本次摄入让谱图第一次有数据可画。


4. 格式契约与对拍(改这块必看)

摄入产物与包内既有产物是同构关系,基准就是包内那份 outputs\rudong\m5_cms_tcm\tcm_index.parquet (330,308 行 × 54 列,2026-01-27~02-03 窗,同一摄取逻辑的产物):

检查 结果
列名与列序 完全一致(54 列,逐字抄自基准,含列序)
dtype 差异 3 列: rec_i(float64/int64)、ds_dim(float64/str)、parse_error(str/object)—— 消费者按列名取数,不受影响
取值(FFT 行) x_offset=0.0 x_delta=0.9375 x_unit='Hz' y_unit='m/s²' 与基准同行逐格一致
内部一致性 ds_size == lines+1 比例 1.000

TCM 导出里踩过的两个坑(写在这里防复发):

  1. X/Y 轴四件在 Measurement.DataSets 层,不在 DataSet 里。Size/Dimension/Values 在 DataSets.DataSet,而 X-axisOffset/X-axisDelta/X-axisUnit/Y-axisUnit 在上一层。 第一版两层取错 → 四列整列为空,内部一致性检查算出 0.000(本该 1.000)才暴露。先看一致性数再信列。
  2. DataSets.DataSet.Values 是空格分隔的数值字符串(不是数组)。谱: Size=Lines+1; X-axisDelta = 带宽/Lines(例: 6000/6400 = 0.9375)。标量: Size==1 时 Values 本身就是标量值。 另有少数测量(FFT_250/2000/10000_Tr, Lines=400)的 x_delta = 带宽/800 = 半带宽口径 —— 源数据自己的约定,摄入原样转录,不做"修正"。

5. 厂商报告的转录纪律

scripts\vib_reports_build.py 只做转录,不做判级:

  • 合并单元格必须按 tc 恒等还原:python-docx 对合并区会把同一个 tc 在相邻列/行重复给出, 照抄会把 优秀 | 优秀 | 优秀 压成一格(第一版手工 dump 就吃了这个亏: 表里 WTG-01 行显示成 WTG-01 | 优秀 | 无,实际是 主轴承=优秀 齿轮箱=优秀 发电机=优秀 结论=无)。规则: 同行 tc 与左邻 相同 = 横向合并(沿用左值);与上行同列相同 = 纵向合并(沿用上值)。
  • 用厂家自己的总览句当校验和:2026年07月报告原文"…优秀机组34台,良好3台,预警0台,无数据1台, 测点异常1台"。转录结果 {优秀34, 良好3, 不可判1} —— 逐项一致("无数据"记 不可判: 没测到 ≠ 正常)。
  • 厂家没给的不猜:报告按「主轴承/齿轮箱/发电机」给级(不分前后),转录表里主轴承前/后填同一值并注明; 「综合」= 三项取严(危险>报警>良好>优秀>不可判)的聚合规则结果,必须写明"这是我们的规则不是厂家判级"; 「融合级/CMS 红黄/行动等级建议」厂家未给 → 写 —。
  • 两条轴不混:厂商报告转录的 md 与观澜自产报告同构(消费端不必改代码),报告期用文件名月份取月末, 于是它不会顶掉更新的自产报告;随包自产报告缺失时它就是最新版(从零重算的机器上正好用得上)。 大生科技那份只写 报告_TCM传动链振动分析_*.md,不冒充 handoff 判级。
  • 口径不同别直接比:厂家四级(优秀/良好/预警/报警)+「数据异常」≠ 观澜五级。实测两者对 38 台 有 33 台结论不同(厂家 2026-07 vs 观澜 2026-08)—— 来源、日期、判据都不同,属预期,不是谁错。

6. 验收(命令 + 实测数字)

.venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope vib --dry-run   # 先看计划
.venv\Scripts\python.exe scripts\place_raw_data.py --src <现场包目录> --scope vib             # 落位 (150 GB)
.venv\Scripts\python.exe scripts\vib_raw_build.py --jobs 10                                   # 摄入 (索引+谱)
.venv\Scripts\python.exe scripts\vib_reports_build.py --dump                                  # 只打印转录, 人工核对
.venv\Scripts\python.exe scripts\vib_reports_build.py                                         # 写产物
.venv\Scripts\python.exe scripts\scan_stations.py                                             # 两个新目录是否被认到

落位(实测): windcms\CMS_RuDong_CGN_202603-04 25,679 件 150.1 GB + windcms\厂家报告\上海电气_月度 13 件 51.4 MB + m5_cms_tcm\厂家报告 1 件 20.8 MB = 25,693 件 / 150.2 GB (rc=0;余量要求: F: 需 ≥ 165 GB)。重跑幂等(同尺寸跳过;实测第二次跳过 2 件已存在的 docx)。

摄入(实测, 10 并行): 索引步 392.8 s → windows\w0316\index.parquet 2,066,686 行 × 54 列 (标量行 861,917 · 谱行 1,204,769;38 台;时间 2026-03-16 17:27:22 → 2026-04-21 10:18:33); 谱步 477.2 s → 420,742 条谱 / 1,712 个 npz 分片(只转 FFT_,故小于"谱行"数; Size>1 行里另有 Time_* 波形 78 万行未转)。窗体合计 3.27 GB。全链 875 s(15 分钟)。

消费端(实测, 改代码前后都是同一套消费者):

环节 实测结果
data.windows() 发现 ['w0127', 'w0316'] —— 新窗自动进分析集
data.load_scalars() 193,902 行 × 11 列(w0316 贡献 168,880;7 个关键标量齐)
data.spectra_meta() 420,742 行 × 18 列,8 个测点全
data.spectrum() ✅ 取到真实谱: WTG01 / Gear_IMS / FFT_6000_Tr → 6,401 点,x 0→6000 Hz,y 单位 m/s²(此前 m5/spectra 缺失 ⇒ 只能返回 None)
索引列契约 data._read_index() 要的 10 列齐;无缺列

厂商报告转录: 上海电气 2026-07 → 38 台,取严 {优秀34, 良好3, 不可判1} 与报告原文总览句 优秀34/良好3/预警0/无数据1/测点异常1 逐项一致;大生科技 2026-03-11 → 38 台 {优秀29, 良好7, 预警1, 数据异常1}(与其正文"WTG02 未取到数据"一致); 消费端解析器(取 ## 附录 A 后 | WTG 行第 6 列)读到 38 台不缺台。

来源台账(outputs/rudong/_provenance.json): 改造前 17 raw-derived / 572 shipped; 本次摄入+转录后 1,740 / 571(m5_cms_tcm 由 0/90 变 1,718/90、windcms 由 0/56 变 2/54)。 逐件登记由 src/derived_manifest.py 承载(谁算的谁登记),products_restore_missing.py 读它; 已存在的窗若漏登记,用 python scripts/vib_raw_build.py --register-only 幂等补登记。

页面侧(http://127.0.0.1:28084/detail/v2#tab=system 的数据层表,接口 /detail/api/maint_survey): 两行位置已改为 <安装目录>\data\raw\如东\windcms\ 与 <安装目录>\data\raw\如东\m5_cms_tcm\。 ★ 组件服务启动时会把这张表烘进缓存,改完 src/ontology/maintenance.py 必须重启组件才生效 (本次用运营台同一套脚本: _ops_stop_keep_gateway.py + _ops_start_and_open.py --no-open)。

其余闸门: 全库 183 个 py 在 -W error::SyntaxWarning 下编译 0 失败;本体审计 rc=0; check_transferable.py 命中数 350 → 344(data\raw 已排除出文本扫描;我的产出 0 处, manifest 里的源件路径已改为相对安装根)。

7. 仍然做不到的(如实列)

  • 六层链四步未随包: rudong_tcm_oem_scan.py / rudong_line_energy_share.py / rudong_model_run.py / rudong_fusion_run.py(原在振动线分支 claude/vibration-data-diagnosis-32b69e)。 因此扫描线/能量占比/模型层/融合层那几类 parquet(oem_frequency_scan、gear_freq_scan、 blade_1p_*、model_run_l6、fusion_38.csv 等)仍走包内随包快照,不由 data\raw 重算; 连带的后果是 CMS 报告/页面不能重生成(§3b 实测 −97%)。vib_raw_manifest.json 的 missing_chain 字段如实列着这四项;缺口台账里对应 B5。
  • 扫描件 PDF 的数值取不出(无文本层, 本包不带 OCR)。
  • handoff_vibration_v2.json / component_history.json 曾是随包快照(含人工裁决、校准更新与开放项)。 2026-09-19 已补生成端(用户令「所有的计算均要形成观澜的源代码」): scripts/rudong_fusion_handoff.py (融合面,报告状态等级 + fusion_38 + L6 过闸线 → 同结构件)与 scripts/component_history_build.py (逐窗标量 x_self → 在升/换件闭环;本机只有 1 个导出色窗 ⇒ 在升/闭环如实为空并把数据边界写进 meta.★数据边界,绝不用"看起来像"的常数填表)。现场给正本时正本优先(生成端不覆盖正本)。
  • 谱库的磁盘代价:整窗全转(--meas ALL)会显著吃盘(含 Time_* 波形, 单点 65,536/200,000); 默认只转 FFT_ 谱(本轮 42 万条谱 = 3.2 GB)。
  • 一个可选的后续:把纯 Python 的 pypdf 轮子并入 wheels\,让 vib_reports_build.py 能离线 逐件核验扫描件页数/有无文本层(本轮是靠临时装的 pypdf 抽检一件得出的结论,清单里记了这一点)。