Pārlūkot izejas kodu

2.12.1 源码梳理与死代码清理: 新增 docs/源代码梳理_v0.1.md(761件/437py/72787行取证); 删 cms_availability(275行,另有实现)+壳; 摘 api 公开名; 清本地残留 15.4MB(frames 37PNG+46个__pycache__)

zhouyang.xie 1 nedēļu atpakaļ
vecāks
revīzija
7e1bf5778b

+ 4 - 1
app_common/common/app_common_guanlan/version.py

@@ -24,7 +24,7 @@ import json
 import pathlib
 
 NAME = '观澜·如东样板 v2'
-VERSION = '2.12.0'         # ★ 版本只改这里
+VERSION = '2.12.1'         # ★ 版本只改这里
 EDITION = 'offline-single-package'
 PACKAGE_STEM = 'app_guanlang'           # 交付包文件名前缀(用户令 2026-09-17)
 
@@ -54,6 +54,9 @@ BUMP_RULE = ('改动落在"解决方案/架构/核心功能" → 大 +1(中/
 #   level: major/minor/patch 表示这一版**相对上一版**是哪一级变化;legacy 表示该版用的是
 #   旧编号体系(0.x,未按本规则),仅作历史对账用。
 HISTORY: tuple[dict, ...] = (
+    dict(version='2.12.1', date='2026-09-28', level='patch',
+         title='源码梳理与死代码清理:删 cms_availability(275 行,另有实现)+ 清本地残留 15.4 MB',
+         note='清理类改动(不改行为口径)⇒ 小版本 +1。用户令「梳理源代码,及清理无用源代码程序」。新增 docs/源代码梳理_v0.1.md(761 受管件 / 437 .py / 72,787 行 / 158 兼容壳的规模与分层、十条根入口、23 个质量门、依赖纪律 R1–R8,以及按"引用计数 + 出处"取证的五类无用件清单)。① 删死代码: app_ontology/.../sop/cms_availability.py(275 行)与壳 src/sop/cms_availability.py(23 行)——全仓无调用,L0 数据可用性判据的实际实现在 scripts/rudong_model_run.py::l0_layer;并从 app_ontology/api.py 摘掉 sop_cms_availability 公开名。④ 清本地残留: scripts/sim_hub/frames/(37 张 PNG,8.7 MB,本就 .gitignore)+ 全仓 46 个 __pycache__ 与散落 .pyc,共释放约 15.4 MB(.venv 未动)。②③ 两批(8 件零引用一次性生成器、33 个无人使用的旧路径壳)经用户裁示保留;C 类接入点契约(MinIO objectstore / Redis cache)保留。验收: 全门禁 rc=0(模块边界 R1–R8 / 配置统一 / 页面归口 / 产物反向呼应 / 链路缺口 / 可移植 / 版本记录 / 依赖台账)+ guanlan.py check 全绿 + 起服务逐页探活 + 打包开箱 5/5 + 卸载核验。'),
     dict(version='2.12.0', date='2026-09-28', level='minor',
          title='模块化重构收官 P7:质量门与打包安装迁移(23 件 / 7,426 行)——七个模块齐备',
          note='重构收官的架构里程碑(七个模块目录 + 公开面 + 兼容壳齐备)⇒ 中版本 +1。P7 把质量门与打包安装 23 件 / 7,426 行迁入 app_qualityGate/common/app_qualityGate_guanlan/{audits,docs,pack}/;旧路径 scripts/<件>.py 留 CLI 壳(公开名同面转发 + main(),guanlan.py check 的 importlib 载入照旧)。**根引导脚本 install.ps1/install.sh/uninstall.*/check.bat/pack.*/start|stop.bat 原地不动**("解压即用"引导层,其 Python 实现已迁入模块)。实逮: ① 子包内 __file__ 取根(parents[1])失效 22 处 ⇒ 统一 install_root();② 文件间 bare import 4 处改相对导入;③ audit_chinese_terms 的 here.parent 是仓库根语义 ⇒ 换 install_root();④ 补 P4 遗留:该审计器被审件 scripts/windscada_serve.py 早已是壳 ⇒ 改读实现位置。验收: 8 条质量门 stdout 逐字节等于迁移前 + 23 个 CLI 壳 importlib 公开名同面 + 新路径 -m 可跑 + 打包器从新位置出包并开箱验证 5/5 + 卸载核验通过 + guanlan.py check 全绿。至此 P0–P7 全部完成:公共/数据接入/算法/本体/前端/后端/质量门各一个目录与 api 公开面,旧路径全部留兼容壳。'),

+ 5 - 1
app_ontology/README.md

@@ -18,7 +18,7 @@ common/app_ontology_guanlan/
                 survey · goem · vib_impact · vib_confidence · fusion_diag · cases · scenarios ·
                 ledger_dates · price · backfill · wrapup · learn_loop · schema_guard · fleet_query ·
                 assistant(_llm) · agent_query_common · 各类 *_validation · validate_pair · stat_clean ·
-                derive10min · farm_paths · analysis_lock · schedule · wind_coupling · xlsx_read · cms_availability
+                derive10min · farm_paths · analysis_lock · schedule · wind_coupling · xlsx_read
 ```
 
 **迁移状态**:**P6 已完成(2026-09-28)** —— 58 个模块(20 本体件 + 38 SOP 件,约 19,775 行)实体迁入本模块;
@@ -35,4 +35,8 @@ common/app_ontology_guanlan/
 `/api/ont_list` / `/api/ont_chain` / `/api/maint_survey` / `/api/maint_std` / `/api/maint_framework` /
 `/api/dq_findings` / `/api/facts`)响应体 sha256 与迁移前一致;全门禁 rc=0。
 
+**清理(2.12.1 / 2026-09-28)**:`sop/cms_availability.py`(275 行,L0 数据可用性判据)**已删除** —— 全仓无调用,
+且 L0 判据的实际实现是 `scripts/rudong_model_run.py::l0_layer`(重算链跑的是后者);`api.py` 的 `sop_cms_availability`
+公开名与旧路径壳 `src/sop/cms_availability.py` 一并摘除。取证见 `docs/源代码梳理_v0.1.md` §5。
+
 > 依据:`docs/重构方案_模块化_v0.1.md`(用户令 2026-09-22:只重构目录与接口、零行为变化、逐版本可回滚)。

+ 0 - 1
app_ontology/common/app_ontology_guanlan/api.py

@@ -56,7 +56,6 @@ _TARGETS: dict[str, str] = {
     'sop_assistant': 'app_ontology.common.app_ontology_guanlan.sop.assistant',
     'sop_assistant_llm': 'app_ontology.common.app_ontology_guanlan.sop.assistant_llm',
     'sop_agent_query_common': 'app_ontology.common.app_ontology_guanlan.sop.agent_query_common',
-    'sop_cms_availability': 'app_ontology.common.app_ontology_guanlan.sop.cms_availability',
     'sop_fatigue_validation': 'app_ontology.common.app_ontology_guanlan.sop.fatigue_validation',
     'sop_hydraulic_validation': 'app_ontology.common.app_ontology_guanlan.sop.hydraulic_validation',
     'sop_icing_validation': 'app_ontology.common.app_ontology_guanlan.sop.icing_validation',

+ 0 - 275
app_ontology/common/app_ontology_guanlan/sop/cms_availability.py

@@ -1,275 +0,0 @@
-"""CMS/TCM 数据可用性判据 (六层振动诊断模型 L0 层)。
-
-★为什么 L0 必须第一层跑、且不通过则短路后续所有层 (如东 27# 戒, 2026-08-21):
-  一条坏掉的测量链会**同时**制造假阳与假阴 ——
-    假阳: 27# Generator_DE 速度域 69.7 mm/s (ISO 红线 6.3 倍), 实为低频段测量链问题
-          经积分放大; 同测点加速度域 `Rms_HP` 反而是**全场最低** (分位 0.0%)。
-    假阴: 盲区台不进筛查集 ⇒ **永远不会被标异常**, 系统也不会报"我没在测"。
-  27# 被判"换轴承后谱面清洁·销案"并当作全场唯一正样本锚, 而它的 CMS 侧
-  `iso_rms_vel` 从 2026-01-18 报到 08-19、红 237 次无人确认。**L0 未过而 L1~L5 被当真。**
-
-★判据分层原则: 物理量 > 派生量 > 自检标志。
-  IEPE 偏置电压是**电气物理量** (恒流源供电的加速度计静态偏置), 是本层唯一硬锚;
-  `health` 字段是厂商自检标志, **只反映连通性不反映采集在不在跑**, 单独不可作判据
-  (memory cms-health-flag-not-data-liveness: 如东 3 台监测盲区期间 Health 全报 100)。
-
-数据源 (如东 CMS 服务器 `msys` 库, 2026-08-21 热拷贝解出):
-  sensor_health 194,623 行 (480 传感器 × 逐周 2016W01–2026W34) / hosts 44 /
-  syslog 9,301 / alarm 7,501 / measurements 1,091。
-"""
-from __future__ import annotations
-
-import pandas as pd
-
-from .analysis_kit import episode_segments, near_miss
-
-# ── IEPE 物理门 ────────────────────────────────────────────────────────────
-# 实测依据 (如东全场 155,728 条振动通道 bias 记录):
-#   99.83% 落在 12–14 V (中位 12.67), 正常带宽仅 ±1 V;
-#   故障态呈**双峰**: ≈27.5 V (n=265, 恒流源开路 = 传感器/电缆断) 与 0 V (n=16, 短路)。
-#   带宽取 [8, 15] 而非 [12, 14]: 留出不同 OEM 标称偏置差异, 只拦真故障态。
-IEPE_BIAS_MIN = 8.0
-IEPE_BIAS_MAX = 15.0
-
-# 主机离线判定: TCM 调度每 10 分钟拉一次数, last_seen 超过 7 天必属异常
-HOST_STALE_DAYS = 7
-
-# 通讯/登录类系统事件 —— 这些 Active 时该台无 SCADA 工况量, 谱采集触发不了
-COMM_FAULT_TYPES = (
-    'DP-Logon error',
-    'No Communication',
-    'M-System Invalid Communication',
-    'Operational Values Missing',
-)
-
-VERDICTS = ('可用', '部分盲区', '全盲', '测量链故障', '需谱层输入')
-
-
-def iepe_bias_gate(bias, *, lo=IEPE_BIAS_MIN, hi=IEPE_BIAS_MAX):
-    """IEPE 偏置电压物理门 → (是否通过, 故障型)。
-
-    故障型区分对现场处置直接有用:
-      'open'  偏置 ≥ hi  → 恒流源开路 = 传感器断线/接头松脱 (如东 12 台次 Generator_DE,
-                            2023W47–2025W51, bias≈27.5 V, 其中 WTG09 断 39 周 / WTG18 断 35 周)
-      'short' 偏置 ≤ lo  → 短路/接地 (如东 WTG05 四测点同时 0 V, 2017W48–W51 = 采集器侧)
-    """
-    s = pd.Series(bias, dtype='float64').dropna()
-    if s.empty:
-        return False, 'no_data'
-    if (s >= hi).any():
-        return False, 'open'
-    if (s <= lo).any():
-        return False, 'short'
-    return True, None
-
-
-def blind_windows(present_weeks, all_weeks, *, min_len=1):
-    """周记录缺失段 → 盲区形态。
-
-    ★为什么要分段而不是只数缺周数 (承 analysis_kit.episode_segments 的 27# 戒):
-      "缺 4 周"可能是**一次 4 周的连续断线**, 也可能是**4 次单周抖动**, 机制与处置相反。
-      如东 WTG18 近 6 周缺 2026W29/32/33/34 → 分段后是 1+3 两段 = 反复失败而非一次性断,
-      与其 `data#WTG18` 表 05-27→08-06 崩溃 **9 次**的形态一致 (每次 1–7 天后自动恢复)。
-    """
-    present = set(int(w) for w in present_weeks)
-    order = sorted(int(w) for w in all_weeks)
-    missing_flag = [w not in present for w in order]
-    segs = episode_segments(missing_flag, min_len=min_len)
-    return {
-        'n_missing': sum(missing_flag),
-        'n_segments': len(segs),
-        'segments': [(order[a], order[b], n) for a, b, n in segs],
-        'weeks_checked': len(order),
-    }
-
-
-def alarm_reachability(measurements):
-    """G6 告警可达性: 哪些判据的红报警能写回风机控制器。
-
-    ★如东实测: 1,091 个测量中**只有 8 个**带 `RedAction=SendWtcCodes`
-      (WTC 码 8455 ← Indicator_1P/1S/2P/2S; 8459 ← Indicator_HSP/HSW),
-      且 `YellowAction` **全部为空** ⇒ 黄级一律不写回。
-      后果: 2025-01 起未绑码判据红计数 493 (iso_rms_vel 241 + rms_200 206) 全部传不出去,
-      而主轴承与发电机轴承**恰好只有这两个无码判据**。
-    ⇒ "现场没收到告警"不等于"CMS 没检出", 归因前必先查本表。
-    """
-    m = measurements.copy()
-    red = m.get('WTCCodeRed')
-    if red is None:
-        m['reachable'] = False
-    else:
-        m['reachable'] = red.notna() & (red.astype(str).str.strip() != '') & (red.astype(str) != '0')
-    return (m.groupby('MeasurementName')['reachable'].any()
-             .rename('writes_back_to_wtc').reset_index())
-
-
-# 告警"持续"的时长锚 —— **不是拍的**: 取自厂商配置 `TrendEstimationTimeDays`,
-# 全库 175 个测量一致为 60 天, 是 Gram & Juhl 定义的"估计一条趋势所需的时间窗"。
-# 一条告警若持续超过厂商认为足以判断趋势的时长, 它已不是"事件"而是"状态"。
-TREND_ESTIMATION_DAYS = 60.0
-
-
-def alarm_burst_pattern(alarm, *, asof, months=12, sustained_days=TREND_ESTIMATION_DAYS):
-    """G6 告警发作形态 → 是否"持续"(单一判据), 附条数等描述量。
-
-    ★这条判据是为补 L0 的**已知假阴**而加的 (如东 27# 戒):
-      27# Generator_DE 的 IEPE 偏置正常、周记录连续、主机在线、health=100 ——
-      前五道门**全过**, L0 判"可用"。但 alarm 表里它 2026-01-18 → 08-19 报了
-      **40 条、红计数 237、大量 UnAck, 而每一条都自动关闭** (未关闭 0 条)。
-      这个"反复发作但每次自愈"的形态正是测量链问题的签名, 与真劣化相反。
-
-    判据只有一条: **存在单条告警持续 > sustained_days** (默认 60, 锚厂商 TrendEstimationTimeDays)。
-
-    ★★★ 为什么只剩一条 —— "告警条数多"这个判据经检验**不成立, 已删除** (2026-08-21):
-      首版还有 `burst_n=10` (条数) 与 `burst_median_days=14` (中位时长) 两个参数, 都是拍的。
-      按外部建议 (振动线: 别用本场检出效果反调阈值) 做独立检验后发现:
-        · 泊松本底不可用 —— 近 12 月 per(台,测点) 告警条数 **方差/均值 = 33.7**
-          (泊松应≈1), 告警强烈成簇非独立事件, 参数化尾概率无意义。
-        · 稳健离群检验 —— 对 log1p(条数) 取 mad_z, **全 63 个通道对 z ∈ [−0.67, 1.79]**,
-          z>2.0 命中 **0 个**, 口径 2.0/2.5/3.0/3.5 全稳健 (sensitivity_sweep)。
-        ⇒ **没有任何通道的告警条数算异常多**; 分布本身就是重尾的。
-        ⇒ 原 `burst_n=10` 选出 21 个通道, 统计上站得住的判据选出 **0 个** —— 那 21 个
-          完全是拍的阈值造出来的。用泊松给它披统计外衣只会更糟 (先验伪装成实测)。
-      删掉它不损失检出: 27# 最长 106.5 天 > 60, 靠"持续"这一条照样被抓。
-
-    条数 / 中位时长 / 未确认数仍然照报, 但**只作描述量供人看, 不进判据**。
-    """
-    asof = pd.Timestamp(asof)
-    a = alarm.copy()
-    a['FromTime'] = pd.to_datetime(a['FromTime'], errors='coerce')
-    a['ToTime'] = pd.to_datetime(a['ToTime'], errors='coerce')
-    a = a[a['FromTime'] >= asof - pd.DateOffset(months=months)]
-    a['dur_d'] = ((a['ToTime'].fillna(asof) - a['FromTime']).dt.total_seconds() / 86400)
-    rows = []
-    for (loc, sensor), g in a.groupby(['LocationName', 'SensorName']):
-        n = len(g)
-        med = float(g['dur_d'].median())
-        mx = float(g['dur_d'].max())
-        red = int(g['CountRed'].fillna(0).sum())
-        sustained = mx > sustained_days
-        rows.append({'location': loc, 'sensor': sensor,
-                     'alarm_pattern': '持续' if sustained else '零星',
-                     'is_sustained': sustained,
-                     # 以下四列为描述量, 不进判据 (见 docstring: 条数判据经检验不成立)
-                     'alarm_n': n, 'alarm_red': red, 'alarm_dur_median_d': round(med, 2),
-                     'alarm_dur_max_d': round(mx, 2),
-                     'alarm_unacked': int((g['AckStatus'] == 'UnAck').sum())})
-    return pd.DataFrame(rows)
-
-
-def cms_l0_availability(sensor_health, hosts, syslog, *, asof, alarm=None,
-                        recent_weeks=8, host_stale_days=HOST_STALE_DAYS):
-    """L0 数据可用性裁决 → per (台 × 测点) 一行。
-
-    判据顺序即短路顺序 —— 先判"整台还在不在", 再判"这个测点的链路好不好"::
-
-        G1 主机在线      hosts.last_seen 距 asof ≤ host_stale_days
-        G2 通讯无故障    syslog 无 Active 的 COMM_FAULT_TYPES 事件
-        G3 周记录连续    近 recent_weeks 周无缺周
-        G4 IEPE 偏置     bias ∈ [8, 15] V                    ← 唯一物理硬锚
-        G5 health 标志   == 100                              ← 仅提示, 不单独定罪
-        G6 告警形态      无单条告警持续 > 60 天 (锚厂商 TrendEstimationTimeDays; 传 alarm= 才启用)
-
-    verdict: 全盲 (G1/G2 断) > 测量链故障 (G4 断) > 部分盲区 (G3 断)
-             > 需谱层输入 (G6 断) > 可用。
-
-    ⚠ **本层有已知假阴, 不可单独使用**: 振动线那两条需标量/谱输入的检验 ——
-      双态检验 (同测点逐日在极高与极低间跳变) 与域间矛盾检验 (加速度域正常而速度域极端)
-      —— 本层做不了。实测: 不传 alarm 时 27# 十个通道 G1–G5 全过、判"可用",
-      而它是全场最严重的测量链故障。传 alarm 启用 G6 后才被降级标出。
-      即便如此, G6 也只提示"分不开", 定性仍须谱层。
-    """
-    asof = pd.Timestamp(asof)
-    sh = sensor_health.copy()
-    sh['yw3'] = sh['yw3'].astype(int)
-    all_weeks = sorted(sh['yw3'].unique())[-recent_weeks:]
-    recent = sh[sh['yw3'].isin(all_weeks)]
-
-    # G1 主机在线 —— hosts 表一台可能有多条历史记录 (换过机箱), 取最新的
-    h = hosts.copy()
-    h['last_seen'] = pd.to_datetime(h['last_seen'], errors='coerce')
-    h['tid'] = h['hostname'].astype(str).str.rsplit('.', n=1).str[-1]
-    host_last = h.groupby('tid')['last_seen'].max()
-
-    # G2 当前 Active 的通讯类故障
-    # ★ dropna 不可省 (memory arrow-str-backend-astype-nan-footgun): pandas≥3 下
-    #   astype(str) 会把缺失值变成字面 'nan' 混进集合。如东 syslog 有 9 行 LocationName 为空。
-    sl = syslog.dropna(subset=['LocationName']).copy()
-    sl['tid_name'] = sl['LocationName'].astype(str).str.replace('CGN Rudong/', '', regex=False)
-    active_comm = set(sl[(sl['State'] == 'Active')
-                         & (sl['Type'].isin(COMM_FAULT_TYPES))]['tid_name'].str.split('/').str[0])
-
-    # G6 告警形态 (可选) —— 不传 alarm 则本门恒过, 且已知会放走 27# 型假阴
-    pat = {}
-    if alarm is not None:
-        bp = alarm_burst_pattern(alarm, asof=asof)
-        pat = {(r['location'], r['sensor']): r for _, r in bp.iterrows()}
-
-    rows = []
-    for (loc, sensor), g in recent.groupby(['locationname', 'sensor_name']):
-        tid_num = ''.join(ch for ch in str(loc) if ch.isdigit()).lstrip('0') or '0'
-        seen = host_last.get(tid_num)
-        host_days = (asof - seen).days if pd.notna(seen) else None
-        g1 = host_days is not None and host_days <= host_stale_days
-        g2 = loc not in active_comm
-        bw = blind_windows(g['yw3'], all_weeks)
-        g3 = bw['n_missing'] == 0
-        g4, fault = iepe_bias_gate(g['bias']) if sensor not in ('WPS', 'System Monitor') else (True, None)
-        g5 = bool((g['health'] == 100).all())
-        p = pat.get((loc, sensor))
-        g6 = True if p is None else not p['is_sustained']
-
-        if not g1:
-            verdict, reason = '全盲', f'主机离线 {host_days} 天'
-        elif not g2:
-            verdict, reason = '全盲', '通讯故障 Active'
-        elif not g4:
-            verdict, reason = '测量链故障', f'IEPE 偏置异常 ({fault})'
-        elif not g3:
-            verdict, reason = '部分盲区', f"近 {len(all_weeks)} 周缺 {bw['n_missing']} 周 / {bw['n_segments']} 段"
-        elif not g6:
-            verdict, reason = ('需谱层输入',
-                               f"告警{p['alarm_pattern']}: {p['alarm_n']}条/红{p['alarm_red']}/"
-                               f"中位{p['alarm_dur_median_d']}d/未确认{p['alarm_unacked']}")
-        else:
-            verdict, reason = '可用', ''
-
-        rows.append({
-            'location': loc, 'sensor': sensor, 'verdict': verdict, 'reason': reason,
-            'G1_host_online': g1, 'G2_no_comm_fault': g2, 'G3_weeks_continuous': g3,
-            'G4_iepe_bias_ok': g4, 'G5_health_flag_100': g5, 'G6_alarm_pattern_ok': g6,
-            'host_stale_days': host_days, 'bias_min': float(g['bias'].min()),
-            'bias_max': float(g['bias'].max()), 'bias_fault_mode': fault,
-            'missing_weeks': bw['n_missing'], 'blind_segments': bw['n_segments'],
-            'weeks_checked': bw['weeks_checked'],
-            'alarm_pattern': (p['alarm_pattern'] if p is not None else None),
-            'alarm_n': (int(p['alarm_n']) if p is not None else 0),
-            'alarm_red': (int(p['alarm_red']) if p is not None else 0),
-            'spectral_checks_pending': '双态检验+域间矛盾检验(需标量/谱输入)',
-        })
-    out = pd.DataFrame(rows).sort_values(['verdict', 'location', 'sensor'])
-    return out.reset_index(drop=True)
-
-
-def l0_summary(avail):
-    """裁决汇总 + 门边披露。
-
-    ★带 near_miss 是硬要求 (memory exact-set-assertion-self-review-blind):
-      "全场只有 N 台不可用"这类精确集断言若来自硬 cutoff, 交付前必须同时报刀刃带,
-      否则被阈值刚好挡住的台在报告里**完全不可见**。
-    """
-    by_v = avail['verdict'].value_counts().to_dict()
-    bad = avail[avail['verdict'] != '可用']
-    # 门边: 主机 last_seen 落在 [门-3, 门] 天内的台 —— 再过几天就要翻面
-    stale = avail.dropna(subset=['host_stale_days']).set_index(
-        avail.dropna(subset=['host_stale_days'])['location'] + '/' + avail.dropna(subset=['host_stale_days'])['sensor']
-    )['host_stale_days']
-    return {
-        'verdict_counts': by_v,
-        'n_units': int(avail['location'].nunique()),
-        'n_channels': int(len(avail)),
-        'unavailable_units': sorted(bad['location'].unique().tolist()),
-        'unavailable_detail': bad[['location', 'sensor', 'verdict', 'reason']].to_dict('records'),
-        'host_days_near_miss': near_miss(stale, gate=HOST_STALE_DAYS,
-                                         band=(HOST_STALE_DAYS - 3, HOST_STALE_DAYS)),
-    }

+ 1 - 1
docs/src/数据要求说明_观澜_2.12.0.md → docs/src/数据要求说明_观澜_2.12.1.md

@@ -1,4 +1,4 @@
-# 数据要求说明 · 观澜 v2 风电场智能分析系统 · 版本 2.12.0
+# 数据要求说明 · 观澜 v2 风电场智能分析系统 · 版本 2.12.1
 
 ## 1 文档说明
 

+ 9 - 9
docs/src/系统设计说明_观澜_2.12.0.md → docs/src/系统设计说明_观澜_2.12.1.md

@@ -1,4 +1,4 @@
-# 系统设计说明 · 观澜 v2 风电场智能分析系统 · 版本 2.12.0
+# 系统设计说明 · 观澜 v2 风电场智能分析系统 · 版本 2.12.1
 
 ## 1 文档说明
 
@@ -6,19 +6,19 @@
 
 ### 1.1 目的与范围
 
-本文是"观澜 v2(风电场智能分析系统)"(海上风电场智能分析离线系统)的设计说明(下称"本系统"),面向版本 2.12.0;内容以样本风电场(下称"本场")为依据,样本场实测日期为 2026-09-22,说明本系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
+本文是"观澜 v2(风电场智能分析系统)"(海上风电场智能分析离线系统)的设计说明(下称"本系统"),面向版本 2.12.1;内容以样本风电场(下称"本场")为依据,样本场实测日期为 2026-09-22,说明本系统"由哪些部分组成、各部分怎么实现、数据从哪来到哪去、判据写在哪里、怎么验证、边界在哪里"。
 
 本文覆盖十五个设计面:总体架构与分层、目录结构与路径真源、数据接入与重算链、判级与算法、时间窗口径、服务与前端、本体与知识层、本机模型接入、运维控制台与重算编排、安装与服务化与版本管理、质量保证、安全与离线边界、可移植性与资源占用、多场适用性与换场迁移、已知边界与未实现。
 
-本文不重复需求条目本身(那是《需求分析_观澜_2.12.0.docx》的职责),也不重复操作步骤的逐步手册(那是 docs/重算操作手册_v0.1.md 与《使用说明书》v0.2(随包 docs/)的职责);本文只回答"设计上为什么这样、落在哪个文件的哪一处、用什么机器守卫保证它不漂移"。
+本文不重复需求条目本身(那是《需求分析_观澜_2.12.1.docx》的职责),也不重复操作步骤的逐步手册(那是 docs/重算操作手册_v0.1.md 与《使用说明书》v0.2(随包 docs/)的职责);本文只回答"设计上为什么这样、落在哪个文件的哪一处、用什么机器守卫保证它不漂移"。
 
 ### 1.2 读者与用法
 
 现场运维与检修人员可看第 3 章、第 7 章、第 8 章、第 11 章,了解自己能点到的页面背后读的是什么口径;场站管理人员与换场交付人员可看第 6 章、第 7 章、第 16 章、第 17 章,了解判级与可靠性指标的口径与边界,以及换场时要重新标定哪些参数;研发与交付人员应通读全文,重点是第 4 章、第 5 章、第 12 章、第 13 章;验收方可直接按第 13 章的质量门与附录 A 的编写依据逐条复核。
 
-### 1.3 与《需求分析_观澜_2.12.0.docx》的对应关系
+### 1.3 与《需求分析_观澜_2.12.1.docx》的对应关系
 
-需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示;与《数据要求说明_观澜_2.12.0.docx》的数据侧口径去向见 16.6 节。
+需求分析写"要什么、为谁、优先级与验收门",设计说明写"怎么实现、落在哪、如何自证"。两文的章节对应关系如表 1-1 所示;与《数据要求说明_观澜_2.12.1.docx》的数据侧口径去向见 16.6 节。
 
 | 本文章节 | 需求分析对应章 | 对应关系说明 |
 |---|---|---|
@@ -41,7 +41,7 @@
 
 ### 1.4 口径与依据
 
-本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 17 章汇总。本文中"本次实测"与各表"实测"列一律指样本场实测(2026-09-22):即在样本风电场的一套实例上的一次实跑,换场后这些数字会变、方法与口径不变。版本号的唯一真源是 src/version.py 的 VERSION 常量,本版为 2.12.0;打包文件名由 src/version.py 的 package_name() 给出,为 app_guanlang_v2.12.0.zip。
+本文所有数字来自仓库文件或命令的实跑输出,不采用估算与推测;查不到、未实现的,一律写"未取证"或"未实现",并在第 17 章汇总。本文中"本次实测"与各表"实测"列一律指样本场实测(2026-09-22):即在样本风电场的一套实例上的一次实跑,换场后这些数字会变、方法与口径不变。版本号的唯一真源是 src/version.py 的 VERSION 常量,本版为 2.12.1;打包文件名由 src/version.py 的 package_name() 给出,为 app_guanlang_v2.12.1.zip。
 
 本文遵循四条写作口径:含"窗"且确实指时间窗口的,一律写全"时间窗"(天气窗、作业窗、预览窗、观测窗属领域词,保持原样);影响的是风电机组时写"影响机组"或"影响机组数";使用英文简写时必须写成"中文(英文简写)"形式,如平均无故障间隔(MTBF)、平均停机间隔(MTBO)、单次停机时长(MDT);全文用简体中文与半角数字与单位。
 
@@ -828,7 +828,7 @@ Windows 安装入口是 install.bat 与 install.ps1,步骤号写死在输出
 
 ### 12.3 安装记录与版本三守卫
 
-安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.12.0,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
+安装记录 install-info.json 是"这台机器装的是哪一版"的唯一凭据,字段如表 12-3 所示。装完写、卸载时删(删掉等于这台机器回到"没装过")。需要如实指出:本机该文件记录的版本是 2.5.0(装于 2026-09-17),落后于当前代码版本 2.12.1,因此它正是"安装前检查会提示版本差异"的活样本;重装或升级后会随之更新。
 
 | 字段 | 含义 |
 |---|---|
@@ -1189,7 +1189,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 
 ### 16.6 与同批交付文档的对应关系
 
-本章对应《需求分析_观澜_2.12.0.docx》第 11 章(场配置化、机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、跨场统一口径、按场裁剪收资、换场验收检查表)与《数据要求说明_观澜_2.12.0.docx》第 12 章(通用必选与可选的判定规则、场配置字段对照、数据源形态适配、换场收资差异清单、按场裁剪步骤);本章给的是设计侧的落点、换场作业单与检查表,数据侧的收资口径与字段要求以数据要求说明为准,需求侧的验收项以需求分析为准。
+本章对应《需求分析_观澜_2.12.1.docx》第 11 章(场配置化、机组与机型可替换、数据源形态可适配、阈值按场标定、术语与单位可配、跨场统一口径、按场裁剪收资、换场验收检查表)与《数据要求说明_观澜_2.12.1.docx》第 12 章(通用必选与可选的判定规则、场配置字段对照、数据源形态适配、换场收资差异清单、按场裁剪步骤);本章给的是设计侧的落点、换场作业单与检查表,数据侧的收资口径与字段要求以数据要求说明为准,需求侧的验收项以需求分析为准。
 
 ***
 
@@ -1255,7 +1255,7 @@ Linux 侧的适配程度如表 15-4 所示,全部为"已有实现但在本次
 
 | 章节 | 来源文件 |
 |---|---|
-| 1 文档说明 | src/version.py(VERSION 与 HISTORY 的 2.12.0 条目)、docs/需求分析_观澜_2.12.0.docx、scripts/delivery_docs_figures.py |
+| 1 文档说明 | src/version.py(VERSION 与 HISTORY 的 2.12.1 条目)、docs/需求分析_观澜_2.12.1.docx、scripts/delivery_docs_figures.py |
 | 2 设计目标与原则 | docs/系统设计说明.md(四条设计铁律)、app_ontology/.../ontology/mcp_server.py 头部、src/windscada/subsys/fusion.py 头部、src/windscada/perf/reliability.py |
 | 3 总体架构 | configs/serve.json、scripts/guanlan_gateway.py、guanlan.py(组件启动表)、run/pids.json、docs/系统设计说明.md(产物全景) |
 | 4 目录结构与路径真源 | src/paths.py、configs/registry.yaml、docs/系统设计说明.md(路径约定与统一记录)、本次目录实测 |

+ 21 - 20
docs/src/需求分析_观澜_2.12.0.md → docs/src/需求分析_观澜_2.12.1.md

@@ -1,4 +1,4 @@
-# 需求分析 · 观澜 v2 风电场智能分析系统 · 版本 2.12.0
+# 需求分析 · 观澜 v2 风电场智能分析系统 · 版本 2.12.1
 
 ## 1 文档说明
 
@@ -6,7 +6,7 @@
 
 ### 1.1 目的
 
-本文是「观澜 v2(风电场智能分析系统)」的**需求分析文档**,回答四个问题:这套系统究竟要满足谁的什么需要;这些需要从哪来、经过哪些版本变成现在的样子;每一条需要对应什么功能、什么输入、什么输出、拿什么判据验收;以及哪些事本版明确不做。本文与同批交付的《系统设计说明_观澜_2.12.0.docx》《数据要求说明_观澜_2.12.0.docx》配套:本文讲「要什么、凭什么算做到了」,设计说明讲「怎么做的」,数据要求说明讲「要哪些数据、什么形态、什么单位」。
+本文是「观澜 v2(风电场智能分析系统)」的**需求分析文档**,回答四个问题:这套系统究竟要满足谁的什么需要;这些需要从哪来、经过哪些版本变成现在的样子;每一条需要对应什么功能、什么输入、什么输出、拿什么判据验收;以及哪些事本版明确不做。本文与同批交付的《系统设计说明_观澜_2.12.1.docx》《数据要求说明_观澜_2.12.1.docx》配套:本文讲「要什么、凭什么算做到了」,设计说明讲「怎么做的」,数据要求说明讲「要哪些数据、什么形态、什么单位」。
 
 本文的写作口径是**只写能取证的事实**:每个数字、每条结论都能指到仓库里的某个文件、某次实跑输出或某条版本记录;查不到、取不到的一律写明「未取证」或「未到位」,不做推测性补全。因此文中会出现少量「未取证」的说明句,那是刻意留下的诚实边界,不是遗漏。
 
@@ -18,19 +18,19 @@
 
 ### 1.3 版本对应关系
 
-系统版本号只有一个真源:仓库内的 src/version.py 的 VERSION 一行。本版 VERSION = 2.12.0,版本记录(HISTORY)共 27 条,人读的版本表由 scripts/version_log.py 从 HISTORY 生成到 docs/版本记录.md,并由 version_log.py --check 与 guanlan.py check 双重校验「记录表与代码一致」。
+系统版本号只有一个真源:仓库内的 src/version.py 的 VERSION 一行。本版 VERSION = 2.12.1,版本记录(HISTORY)共 28 条,人读的版本表由 scripts/version_log.py 从 HISTORY 生成到 docs/版本记录.md,并由 version_log.py --check 与 guanlan.py check 双重校验「记录表与代码一致」。
 
 表 1-1 版本与文档的对应关系
 
 | 项 | 值 | 取证方式 |
 |---|---|---|
-| 系统版本 | 2.12.0 | src/version.py 的 VERSION;guanlan.py check 实跑报「版本管理: 观澜 v2(风电场智能分析系统) v2.12.0」 |
-| 交付包名 | app_guanlang_v2.12.0.zip | src/version.py 的 package_name();同一条 check 输出 |
-| 本文版本 | 2.12.0(与系统版本同号) | 本文标题与 src/version.py 的 VERSION |
+| 系统版本 | 2.12.1 | src/version.py 的 VERSION;guanlan.py check 实跑报「版本管理: 观澜 v2(风电场智能分析系统) v2.12.1」 |
+| 交付包名 | app_guanlang_v2.12.1.zip | src/version.py 的 package_name();同一条 check 输出 |
+| 本文版本 | 2.12.1(与系统版本同号) | 本文标题与 src/version.py 的 VERSION |
 | 版本史条目数 | 13 条(最新一条即本次文档交付) | src/version.py 的 HISTORY;docs/版本记录.md |
 | 版本号规则 | v大版本号.中版本号.小版本号;大改方案或架构、中改非核心功能、小为消缺 | src/version.py 的 BUMP_RULE 与 LEVEL_MEANING;docs/系统设计说明.md §15 |
 | 版本级别机器判据 | level_of(旧, 新) 返回 major 或 minor 或 patch 或 same | src/version.py 的 level_of() |
-| 本次变化级别 | 小(patch,源码模块化重构推进 P1:公共层九个平台件实体迁移,旧路径留兼容转发壳;系统功能与运行为未变) | HISTORY 中 2.12.0 条目的 level 字段 |
+| 本次变化级别 | 小(patch,源码模块化重构推进 P1:公共层九个平台件实体迁移,旧路径留兼容转发壳;系统功能与运行为未变) | HISTORY 中 2.12.1 条目的 level 字段 |
 
 本版相对 2.10.0 的实质变化是**交付文档对外化**:三份交付文档(需求分析、系统设计说明、数据要求说明)全文不体现具体风电场(去标识化),并新增多风电场适用性一章;系统功能未变,属交付物修订。依据用户令原文:「修改三份文档,内容参考样本风电场,但不体现样本风电场,且具有不同风电场适用性」(引用时把样本场名按去标识化口径写成「样本风电场」)。
 
@@ -119,10 +119,11 @@
 
 ### 3.2 需求演进时间轴
 
-表 3-1 版本史与用户令要点(依据 src/version.py 的 HISTORY,共 27 条)
+表 3-1 版本史与用户令要点(依据 src/version.py 的 HISTORY,共 28 条)
 
 | 版本 | 日期 | 用户令要点 | 变化级别 |
 |---|---|---|---|
+| 2.12.1 | 2026-09-28 | 源码梳理与死代码清理:删 cms_availability+ 清本地残留 15.4 MB | 小(patch) |
 | 2.12.0 | 2026-09-28 | 模块化重构收官 P7:质量门与打包安装迁移——七个模块齐备 | 中(minor) |
 | 2.11.9 | 2026-09-28 | 模块化重构 P6:本体与知识层实体迁移 | 小(patch) |
 | 2.11.8 | 2026-09-28 | 消缺:guanlan.py stop 在 pids.json 读不动/删不掉时抛异常中断 | 小(patch) |
@@ -151,7 +152,7 @@
 | 0.4.0 | 2026-09-17 | 打包默认不含输入数据与产物与日志与临时文件;无窗口启动;统一配置与日志;页面归口;服务化 | 旧编号(legacy) |
 | 0.2.0 | 2026-09-01 | 含产物的旧交付基线,历史上用作「包内无生成端」那批产物的补救源 | 旧编号(legacy) |
 
-需求的演进如图 1-1 所示。该图依据 src/version.py 的 HISTORY 绘制,覆盖上表 27 条记录(即两级旧编号 0.4.0 与 0.2.0,以及从 2.5.0 起按规则递增的二十五个版本)。按级别统计,这二十五个版本里「中」级变化8条(2.12.0、2.11.0、2.10.0、2.9.0、2.8.0、2.7.0、2.6.0、2.5.0),「小」级变化17条(2.11.9、2.11.8、2.11.7、2.11.6、2.11.5、2.11.4、2.11.3、2.11.2、2.11.1、2.10.4、2.10.3、2.10.2、2.10.1、2.9.2、2.9.1、2.8.2、2.8.1),没有大版本升级。这个分布清楚地显示出这条线的性质——**功能面在 2.5.0 到 2.7.0 之间快速铺开,之后转入以消缺与口径统一为主的密集收敛期**。
+需求的演进如图 1-1 所示。该图依据 src/version.py 的 HISTORY 绘制,覆盖上表 28 条记录(即两级旧编号 0.4.0 与 0.2.0,以及从 2.5.0 起按规则递增的二十六个版本)。按级别统计,这二十六个版本里「中」级变化8条(2.12.0、2.11.0、2.10.0、2.9.0、2.8.0、2.7.0、2.6.0、2.5.0),「小」级变化18条(2.12.1、2.11.9、2.11.8、2.11.7、2.11.6、2.11.5、2.11.4、2.11.3、2.11.2、2.11.1、2.10.4、2.10.3、2.10.2、2.10.1、2.9.2、2.9.1、2.8.2、2.8.1),没有大版本升级。这个分布清楚地显示出这条线的性质——**功能面在 2.5.0 到 2.7.0 之间快速铺开,之后转入以消缺与口径统一为主的密集收敛期**。
 
 ![图 1-1 观澜需求演进时间轴(依据 src/version.py 的 HISTORY)](figures/fig-req-01-需求演进时间轴.png)
 
@@ -170,7 +171,7 @@
 | 所有的计算均要形成观澜的源代码 | 逐族补生成端:变桨面、在升闭环、三层基线、融合面 handoff、总览页、由台账生成 claim | 反向呼应审计成立 3,548 件、不成立 0 件、未归类 0 件 |
 | 运行期一律不从交付包补齐 | 第 ⑤ 步由「补齐随包件」改为「反向呼应审计」,只报账不搬运 | rebuild_all --dry-run 计划中 ⑤ 步命令为 products_reverse_audit.py --check |
 | 清除产物不留备份 | products_state.py --off --yes 改真删除;--on 与门户恢复按钮移除 | 控制台按钮语义与 409 前置校验;docs/系统设计说明.md §7 |
-| 装成服务并在安装时检查版本 | win_service.py 以 ctypes 直连 SCM;service_main.py 与 systemd 单元;install-info.json 记录版本并比对 | check 实跑报「卸载入口在位 2 个」「版本管理 v2.12.0」 |
+| 装成服务并在安装时检查版本 | win_service.py 以 ctypes 直连 SCM;service_main.py 与 systemd 单元;install-info.json 记录版本并比对 | check 实跑报「卸载入口在位 2 个」「版本管理 v2.12.1」 |
 | 观澜改为监听所有 IP | serve.json 增 public_host 且出厂为 0.0.0.0,仅门户网关用它,组件仍绑本机 | check 实跑显示 6 个端口全部运行中;README 第二节写明无鉴权风险与回退口径 |
 | 描述口径:时间窗、影响机组、中文(英文简写) | 界面文案与报告构建器统一改写;i18n_en 键同步 | docs/系统设计说明.md §17.1 与 §17.2;语言包 845 条前后端成对 |
 | 整理需求与设计与数据接入各写一份 Word 文档 | docs/src 下 markdown 源件;渲染器输出带域目录的 docx;插图生成器生成 14 张图,数字全部从真件取 | HISTORY 2.10.0 条目;本版三份文档 |
@@ -386,7 +387,7 @@
 | NFR-09 | 安全只读:不写回现场系统、不改原始件、不产生控制指令;对外暴露时须自行在网侧限来源 | 原始件按只读输入对待,落位冲突默认拒绝;对外只有一个网关入口;启动日志每次提醒页面无鉴权 | docs/输入数据放置指导_v0.1.md §3;README_先读我.MD 第二节 |
 | NFR-10 | 可维护性:单一真源,改一处不必改多处;关键口径有机器守卫 | 版本号只有一行真源并由三处守卫盯住;路径、配置、日志、语言各有唯一取用口与审计器 | docs/系统设计说明.md §15.2 与 §11.1;docs/系统设计说明.md §3.1 |
 | NFR-11 | 无人值守与自愈:长跑的服务要能被托管并自动拉起 | 服务体每 15 秒巡检并拉起掉线组件;崩溃重启策略;远程部署一律用服务,不用会话前台进程 | docs/系统设计说明.md §14.1 与 §14.4;README_先读我.MD 第二节 |
-| NFR-12 | 收资清单按场裁剪:以通用收资模板为底,按本场的机型与数据源形态与专题范围裁剪,逐条写明必须、建议、可选或可替代以及「不收会怎样」 | 每一族都有必须性分级与缺件后果且可复核;换场时清单随场定义与机型变化,不是照抄样本场 | 本文第 11 章;《数据要求说明_观澜_2.12.0.docx》第 8 章与第 12 章 |
+| NFR-12 | 收资清单按场裁剪:以通用收资模板为底,按本场的机型与数据源形态与专题范围裁剪,逐条写明必须、建议、可选或可替代以及「不收会怎样」 | 每一族都有必须性分级与缺件后果且可复核;换场时清单随场定义与机型变化,不是照抄样本场 | 本文第 11 章;《数据要求说明_观澜_2.12.1.docx》第 8 章与第 12 章 |
 
 ### 6.2 性能与耗时实测
 
@@ -422,7 +423,7 @@
 
 | 真源 | 管什么 | 守卫与实跑结果 |
 |---|---|---|
-| src/version.py | 名称、版本、版本规则、版本史、包名 | 版本记录一致性检查;check 实跑报 v2.12.0 且记录表与代码一致 |
+| src/version.py | 名称、版本、版本规则、版本史、包名 | 版本记录一致性检查;check 实跑报 v2.12.1 且记录表与代码一致 |
 | src/paths.py | 一切路径解析的基准与助手 | 配置审计器检查不手拼路径;实跑无不一致 |
 | configs 目录与登记表 | 端口、模型档、场配置、页面归口、配置登记 | 配置审计实跑已知缺口与白名单 14 条、提示 7 条 |
 | src/logfile.py | 日志目录、命名、行格式、保留策略 | 日志审计实跑无不一致、提示 87 条 |
@@ -433,7 +434,7 @@
 
 ### 7.1 数据族与功能映射
 
-系统的数据需求可以概括成一句话:**七类现场源件加一类共享机理资料,喂出九个产物仓,页面只读产物**。每个源类目录名就是摄入接口,改名等于换接口。数据族与功能的对应关系如下表;逐类的字段、单位、必须性与质量要求见同批交付的《数据要求说明_观澜_2.12.0.docx》。
+系统的数据需求可以概括成一句话:**七类现场源件加一类共享机理资料,喂出九个产物仓,页面只读产物**。每个源类目录名就是摄入接口,改名等于换接口。数据族与功能的对应关系如下表;逐类的字段、单位、必须性与质量要求见同批交付的《数据要求说明_观澜_2.12.1.docx》。
 
 表 7-1 数据族到功能的映射(样本场实测(2026-09-22))
 
@@ -466,7 +467,7 @@
 
 ### 7.3 与数据要求说明的分工
 
-本文只回答「要哪些数据、这些数据支撑什么功能」。数据的字段级要求(核心测点的名称、单位、必须性、缺失替代、对齐规则、质量门与核对锚点)以及面向现场的收资清单,写在《数据要求说明_观澜_2.12.0.docx》里。该文档按用户令要求与现场收资文件逐条对照,并对现场收资层面的已知缺失逐条如实记录,例如测风塔数据为零交付、故障录波只有 4 台、振动侧 handoff 正本缺失由观澜自算件顶上、远端机器未安装本机模型等。
+本文只回答「要哪些数据、这些数据支撑什么功能」。数据的字段级要求(核心测点的名称、单位、必须性、缺失替代、对齐规则、质量门与核对锚点)以及面向现场的收资清单,写在《数据要求说明_观澜_2.12.1.docx》里。该文档按用户令要求与现场收资文件逐条对照,并对现场收资层面的已知缺失逐条如实记录,例如测风塔数据为零交付、故障录波只有 4 台、振动侧 handoff 正本缺失由观澜自算件顶上、远端机器未安装本机模型等。
 
 ## 8 页面与信息架构需求
 
@@ -548,7 +549,7 @@
 | 运行环境与依赖 | 10 | Python 版本不低于 3.11(实测 3.12.10);九个第三方依赖逐个导入 | 全 OK |
 | 静态质量门 | 4 | 源码可编译 229 个文件;语言包 845 条成对;入口引用闭合 48 条;入口脚本编码守则 | 全 OK |
 | 子进程口径 | 3 | 捕获输出可用;无窗口启动输出进日志;无窗口位已设,标志位 0x8000200 | 全 OK |
-| 版本与卸载入口 | 2 | 版本管理与记录表一致(v2.12.0,包名 app_guanlang_v2.12.0.zip);卸载入口两个都在位 | 全 OK |
+| 版本与卸载入口 | 2 | 版本管理与记录表一致(v2.12.1,包名 app_guanlang_v2.12.1.zip);卸载入口两个都在位 | 全 OK |
 | 制品与台账审计 | 5 | 反向呼应 3,548 件成立;页面归口检查 21 项;配置统一;日志统一;输入数据放置体检 26,503 件结构合规 | 全 OK |
 | 产物与发布件在位 | 10 | 标准仓、本体对象库、findings、事实契约、门户、仿真合页服务与资料包、三维资产、仿真回放资产、治理清单交付件 | 全 OK |
 | 原始件目录 | 1 | 原始件目录存在(无数据时此项不影响页面) | 全 OK |
@@ -674,7 +675,7 @@
 
 本章有两个词要先定义。「本场」指**当前被选中的场**,由配置选定,不必然等于本文取数的样本风电场;「换场」指从一场切到另一场运行的整套动作(改配置、放数据、重跑、按检查表复验),不是把两场的数据混在一棵树里。本章沿用的占位符与附录 A 一致:`<场>`、`<场站>`、`<机型>` 按实际风电场替换。
 
-本章需求与《数据要求说明_观澜_2.12.0.docx》《系统设计说明_观澜_2.12.0.docx》配套使用:本章讲「换场要满足什么」,数据要求说明讲「要向新场收哪些数据、哪些可以裁剪」,《设计说明》讲「配置在哪一层生效、哪些参数属于场相关层」。
+本章需求与《数据要求说明_观澜_2.12.1.docx》《系统设计说明_观澜_2.12.1.docx》配套使用:本章讲「换场要满足什么」,数据要求说明讲「要向新场收哪些数据、哪些可以裁剪」,《设计说明》讲「配置在哪一层生效、哪些参数属于场相关层」。
 
 取证边界要如实写一句:FR-44 至 FR-51 与 NFR-12 有既有的场配置层、摄入层与场级字段语义登记作依据;FR-52 的跨场横向对比是本次新增的口径要求,其实跑记录与机器守卫随后续版本补齐——本文不谎称已经通过。
 
@@ -753,7 +754,7 @@
 
 | 编号 | 需求 | 判据 | 依据文件 |
 |---|---|---|---|
-| NFR-12 | 收资清单按场裁剪:以通用收资模板为底,按本场的机型、数据源形态与专题范围裁剪,逐条写明必须、建议、可选或可替代以及「不收会怎样」 | 每一族都有必须性分级与缺件后果且可复核;换场时清单随场定义与机型变化,不是照抄样本场 | 《数据要求说明_观澜_2.12.0.docx》第 8 章与第 12 章;docs/输入数据放置指导_v0.1.md |
+| NFR-12 | 收资清单按场裁剪:以通用收资模板为底,按本场的机型、数据源形态与专题范围裁剪,逐条写明必须、建议、可选或可替代以及「不收会怎样」 | 每一族都有必须性分级与缺件后果且可复核;换场时清单随场定义与机型变化,不是照抄样本场 | 《数据要求说明_观澜_2.12.1.docx》第 8 章与第 12 章;docs/输入数据放置指导_v0.1.md |
 
 ### 11.9 换场验收需求
 
@@ -896,8 +897,8 @@
 | 场定义与机型与物理约束参数的写法样例 | configs/farms/<场>.yaml |
 | 机型—场站数据契约与字段单位真源 | configs/contracts/<机型>_<场>.yaml 与 configs/canonical/dictionary.yaml |
 | 场级字段语义登记与其它厂商导出形态的对齐 | configs/farms/<场>/field_semantic_registry.yaml |
-| 按场裁剪的收资清单与必须性分级 | docs/src/数据要求说明_观澜_2.12.0.md |
-| 场无关引擎与场相关参数的分层与换场作业单 | docs/src/系统设计说明_观澜_2.12.0.md |
+| 按场裁剪的收资清单与必须性分级 | docs/src/数据要求说明_观澜_2.12.1.md |
+| 场无关引擎与场相关参数的分层与换场作业单 | docs/src/系统设计说明_观澜_2.12.1.md |
 
 表中 `<场>`、`<场站>`、`<机型>` 为占位符,按实际风电场替换。
 
@@ -940,4 +941,4 @@
 | 换场 | — | 从一场切到另一场运行的整套动作:改配置、放数据、重跑、按检查表复验 | 本文 §11.9 |
 | 缺族降级 | — | 某族数据缺失时按缺件如实标注并降低该族功能,不造数、不用他场数字顶替 | 本文 FR-49 |
 | 按场标定 | — | 把阈值、时间窗锚点、限电窗、温度档带宽等场相关参数按本场取值 | 本文 FR-50 |
-| 通用收资模板 | — | 与场无关的收资条目底稿,按场裁剪后作为现场收资清单 | docs/src/数据要求说明_观澜_2.12.0.md |
+| 通用收资模板 | — | 与场无关的收资条目底稿,按场裁剪后作为现场收资清单 | docs/src/数据要求说明_观澜_2.12.1.md |

BIN
docs/数据要求说明_观澜_2.12.0.docx


BIN
docs/数据要求说明_观澜_2.12.1.docx


+ 194 - 0
docs/源代码梳理_v0.1.md

@@ -0,0 +1,194 @@
+# 源代码梳理 v0.1(2026-09-28)
+
+> 用户令:**「梳理源代码,及清理无用源代码程序。」**
+> 本文是**可核对底稿**:先把"现在有哪些源代码、怎么跑起来、谁被谁引用"讲清,再逐件列出**零引用件**与**建议保留的架构占位**,每一条都带机器可复核的证据(引用计数 / 出处文件)。
+
+## 1 规模总览(`git ls-files` 口径)
+
+| 项 | 数量 |
+|---|---|
+| 受管文件 | **761 件** |
+| Python 文件 | **437 件 / 72,787 行** |
+| 其中**兼容转发壳** | **158 件**(src 109 · scripts 47 · 根 `guanlan.py` 1 · app_qualityGate 1) |
+| 模块目录 | 7 个(公共 / 数据接入 / 算法 / 本体 / 前端 / 后端 / 质量门) |
+| 模块公开面公开名 | **136 名**(7 个 `api.py` 的 `_TARGETS` 合计) |
+| 配置 | configs 172 件 · reference 38 件 · resources 30 件 |
+| 文档 | docs 35 件(含三份交付文档源与 17 份专题说明) |
+
+## 2 分层结构(每模块一个目录 + 一个公开面)
+
+| 模块 | 件数 | 行数 | 职责 | 允许依赖 | 阶段 |
+|---|---|---|---|---|---|
+| `app_common/` | 14(含壳 0) | 1,768 | 路径/版本/日志/进程/入口引用/运行态作业/派生台账(公共层,不依赖任何业务模块) | (无) | 已完成 |
+| `app_ETL/` | 23(含壳 0) | 5,442 | 源件摄入、放置与增量体检、标准仓、机型与场站契约、canonical 词典 + 12 构建器 | app_common | 已完成 |
+| `app_algorithmModel/` | 31(含壳 0) | 3,953 | 七系统判级矩阵、七镜头曲线、可靠性、控制参数一致性、融合面、振动与温度面、趋势与可用率 | app_common, app_ETL | 已完成 |
+| `app_ontology/` | 64(含壳 0) | 20,035 | 本体(对象库/码表/摄入六步/检索/决策台/模型闸)与 SOP 知识层(契约闸/判别器/调查/案例/台账) | app_common | 已完成 |
+| `app_backEnd/` | 26(含壳 0) | 6,833 | 组件服务、统一网关、运维控制台与重算编排、作业状态、CLI 与进程管理 | app_common, app_ETL, app_algorithmModel, app_ontology, app_frontEnd | 已完成 |
+| `app_frontEnd/` | 15(含壳 0) | 2,164 | 工作台单页与资源件、页面装配与经典页模板、设计系统、界面文案与术语、门户装配器 | app_common | 已完成 |
+| `app_qualityGate/` | 30(含壳 1) | 7,663 | 审计器、交付文档三检、版本记录、打包器与开箱验证、卸载器 | 全部业务模块 | 已完成 |
+
+**`src/` 的定位(P0–P7 之后)**:140 件里 **109 件是旧路径兼容壳**,31 件是**尚未划归模块**的既有实体(`src/windscada/maint/*`、`src/windcms/*` 剩余件等)。壳的作用只有一个:让 `from src.<旧路径> import …` 与`python -m src.<旧路径>` 这两种写法继续可用;P0–P7 的每一阶段都以"行为逐字节不变"验收,靠的就是它们。
+
+**`scripts/` 的定位**:102 件 = 47 个 CLI 兼容壳 + 48 个独立工具(重算链脚本、词汇表构建、收资与上云工具、仿真页生成器等)+ 7 个服务体/辅助(`win_service.py`、`service_main.py`、`service_worker.py` 等)。
+
+## 3 入口与运行链(十条根入口都在原位)
+
+```
+install.bat/.ps1 / install.sh      安装(建 .venv、装依赖、自检、注册服务)
+check.bat                          → guanlan.py check(自检门)
+start.bat / stop.bat               启动 / 停止(调 scripts/guanlan_start_hidden.py、guanlan.py stop)
+pack.bat / pack.sh                 打包 + 开箱验证入口(scripts/pack_dist.py、pack_verify_entry.py)
+uninstall.bat / uninstall.sh       卸载(scripts/guanlan_uninstall.py)
+guanlan.py                         CLI:check · serve · status · stop · open · qa
+  └─ app_backEnd/common/app_backEnd_guanlan/cli.py
+       ├─ 组件:detail(18033) / cms(18020) / viewer(64292) / sim(18791) / sim_sys(18792)
+       ├─ 网关:gateway(28084)  → 统一入口(门户 / detail / cms / sim / viewer / ops)
+       └─ 模块公开面:app_ETL.api · app_algorithmModel.api · app_ontology.api · app_frontEnd.api
+```
+
+## 4 质量门清单(23 个工具,全部走 `scripts/<件>.py` 壳调用,实现在 `app_qualityGate/`)
+
+| 工具 | 查什么 |
+|---|---|
+| `config_audit.py` | 配置统一:目录约定 / 命名 / 内容 / 引用闭合 / 不手拼路径(rc=8 为配置违规) |
+| `pages_audit.py` | 页面归口:每一页该不该随输入数据变、算不算产物 |
+| `products_reverse_audit.py` | 产物反向呼应:无生成端的件能否从原件推出来 |
+| `chain_gap_check.py` | 重算链缺口:四步脚本与关键产物是否都在位 |
+| `check_portability.py` | 可移植:无机器相关绝对路径 / 无 cwd 相对路径 / 不手拼库存字符串 |
+| `check_transferable.py` | 换机可跑性大扫描(虚拟环境 / 轮子 / 便携运行时) |
+| `module_boundary_audit.py` | 模块边界 R1–R8(结构 / 只经 api / 依赖方向 / 公共层纯净 / 接口在位 / 入口解耦 / 编码 / 壳同面) |
+| `detail_deps.py` | /detail 页 → 接口 → 产物 → 来源 台账一致性 |
+| `page_fingerprint.py` | 页面/接口指纹(改造前后与换机部署的回归判据) |
+| `pages_sweep.py` | 逐页"有没有输出"体检 |
+| `inventory_products.py` | 产物清点与输入↔产物呼应 |
+| `products_state.py` | 产物状态总览 |
+| `products_restore_missing.py` | 补齐无生成端产物(只补缺件) |
+| `security_scan.py` | 安全扫描(密钥 / 越界写 / 敏感路径) |
+| `log_audit.py` | 日志规范与保留策略 |
+| `audit_chinese_terms.py` | 中文表达审核(OEM 术语基准 + 行话规则) |
+| `guanlan_baseline_manifest.py` | 工作台视觉基线清单(从源重建 zh 页比对) |
+| `delivery_docs_build.py` | 三件交付文档构建 + 自检(去标识化 / 表号引用闭合 / 域 / 页眉) |
+| `delivery_docs_figures.py` | 交付文档插图(10 张) |
+| `version_log.py` | 版本记录与 `HISTORY` 一致 |
+| `pack_dist.py` | 打包(白名单 + 入口引用闭合 + 编码守则 + 无装机产物) |
+| `pack_verify_entry.py` | 开箱验证入口(解压 → 安装 → 页面 5/5 → 卸载核验) |
+| `guanlan_uninstall.py` | 卸载(停服 / 撤自启 / 删快捷方式 / 清运行态 / 删安装记录) |
+
+`guanlan.py check` 把其中关键几门串成一条自检;**当前全绿**(唯一已知项:远端机器未装 Ollama)。
+
+## 5 无用件取证结果(逐件可复核)
+
+取证口径见 §7。分五类:**A 确证死代码** · **B 零引用一次性生成器** · **C 架构接入点(建议保留)** · **D 兼容壳可收敛** · **E 本地未跟踪残留**。
+
+### A. 确证死代码(零引用 + 功能另有实现)
+
+| 件 | 行数 | 证据 |
+|---|---|---|
+| `app_ontology/common/app_ontology_guanlan/sop/cms_availability.py` | 275 | 全仓无调用;L0 数据可用性判据在 `scripts/rudong_model_run.py::l0_layer` **另有实现**(重算链跑的是后者);只在 `app_ontology/api.py` 的公开面里挂了个名字 |
+| `src/sop/cms_availability.py` | 23 | 上面那件的兼容壳;旧路径无人使用 |
+
+### B. 零引用一次性生成器(产物已随包;删了不影响运行,但失去"可重生成")
+
+| 件 | 行数 | 自述 / 现状 |
+|---|---|---|
+| `scripts/guanlan_cloud_page.py` | 178 | 云端版·方案 A 中文单文件(脱敏面孔)生成器 |
+| `scripts/guanlan_cloud_qa.py` | 324 | 云端 `/api/qa` 代理**草案(未部署)** |
+| `scripts/sim_hub/apply_theme.py` | 79 | 给仿真页注入统一外壳(顶栏 + 配色 token) |
+| `scripts/sim_hub/build_collapse_3d.py` | 160 | 生成「运行形态塌陷·动态三维」页 |
+| `scripts/sim_hub/build_viv_page.py` | 173 | 生成「涡激振荡仿真台」单页(自述数据源写死 macOS 路径) |
+| `scripts/sim_hub/pain_pitch_imbalance.py` | 89 | 变桨/叶轮不平衡机理页数据生成 |
+| `scripts/sim_hub/pain_viv.py` | 82 | VIV 原理演示页数据生成 |
+| `scripts/sim_hub/pain_yawslip.py` | 115 | 偏航滑移实测页数据生成 |
+
+> 注:`sim_hub/pain_*.py` 的产物(`assets/pain_*.png`)**被 `build_hub.py` 以 PNG 资产引用**,`build_hub.py` / `mk_share.py` / `wake*.py` 本身仍被 `scripts/sim_hub/README.md` 记录为内容源 —— 所以"生成器一删,这三张图就只能手改,不能再重生成"。
+
+### C. 架构接入点(零引用但**建议保留**)
+
+| 件 | 行数 | 说明 |
+|---|---|---|
+| `app_ETL/common/app_ETL_guanlan/objectstore.py` | 26 | **MinIO** 接入点契约(源件与产物对象存储) |
+| `app_backEnd/common/app_backEnd_guanlan/cache.py` | 30 | **Redis** 接入点契约(按时间窗缓存与作业状态) |
+
+它们与已被引用的另两件同批(`app_ETL/.../store.py` = TiDB、`app_backEnd/.../gateway_contract.py` = Nginx),是"四组件可插拔"的接口面;删掉它们等于删掉后续接入的落点。
+
+### D. 兼容壳可收敛(旧路径已无人使用的 33 件)
+
+壳总 158 件;其中 **33 件的旧路径在所有受管文件里都没有任何引用**(含文档与配置),即"不会再有人从旧路径进来":
+
+```
+src/logfile.py
+src/opsjob.py
+src/tabfmt.py
+src/ontology/acceptance_fsm.py
+src/ontology/codes.py
+src/ontology/decisions.py
+src/ontology/llm_gate.py
+src/ontology/schema.py
+src/sop/cms_availability.py
+src/sop/contract_gate.py
+src/sop/derive10min.py
+src/sop/farm_pipeline.py
+src/sop/fatigue_validation.py
+src/sop/fleet_query.py
+src/sop/fusion_diag.py
+src/sop/goem.py
+src/sop/hydraulic_validation.py
+src/sop/icing_validation.py
+src/sop/pitch_validation.py
+src/sop/reactive_validation.py
+src/sop/scenarios.py
+src/sop/sensor_validation.py
+src/sop/survey.py
+src/sop/thermreg_validation.py
+src/sop/vib_confidence.py
+src/sop/wind_coupling.py
+src/sop/xlsx_read.py
+src/sop/yaw_validation.py
+src/windcms/agent.py
+src/windscada/lang.py
+src/windscada/scada_source.py
+src/windscada/slim.py
+src/windscada/ui/snapshot.py
+```
+
+**建议**:这一级是"兼容性 vs 清爽"的取舍 —— 删了旧路径就断(但模块路径/api 全通、门禁全绿)。若确定后续只按模块路径开发,可整批删除,并在 `docs/重构方案_模块化_v0.1.md` 记"P8 壳收敛"。
+
+### E. 本地未跟踪残留(不进版本库,但占空间/易误入包)
+
+- `scripts/sim_hub/frames/*.png`:**37 张**,已被 `.gitignore` 忽略(构建中间件,可重生成)。
+- 各目录 `__pycache__/`(含 `scripts/sim_hub/__pycache__/` 11 个 .pyc)。
+- 历史交付 zip(`F:\temp\app_guanlang_v2*.zip`、`app_guanlang_v2.9.2.zip`)不在仓库内,另行处置。
+
+## 6 清理建议与执行顺序
+
+| 批次 | 内容 | 影响面 | 需要的前置 |
+|---|---|---|---|
+| ① | A 类:删 `cms_availability` 实现 + 壳,并从 `app_ontology/api.py` 摘掉 `sop_cms_availability` | 无(无调用) | 全门禁 + 三份文档重出 |
+| ② | B 类:删 8 件零引用一次性生成器(1,200 行) | 失去"重生成"能力;运行与页面不受影响 | 需确认云上/仿真的产物是否还要重建 |
+| ③ | D 类:整批删 33 个无人使用的旧路径壳 | 旧路径写法失效(模块路径与 api 全通) | 需确认后续开发不再走旧路径 |
+| ④ | E 类:清本地 `frames/`、`__pycache__/` | 本地磁盘 | 无 |
+
+**C 类(MinIO / Redis 接入点)建议一律保留**:它们是架构接口,不是"没用的代码"。
+
+## 7 取证口径(可复跑,不靠印象)
+
+1. **受管件**=`git ls-files`(761 件);**文本受管件**=后缀在 `.py/.md/.yaml/.yml/.json/.bat/.sh/.ps1/.txt/.html/.js` 且 < 3 MB。
+2. **引用**=在**除自身外**的全部文本受管件里出现下列任一形态:
+   - 实路径 `a/b/c.py`;· 点号名 `a.b.c`;· 点号斜杠 `a.b.c/leaf.py`(P1–P7 壳的 docstring 写法);
+   - 导入语句 `from a.b import c`、`import a.b.c`、裸名 `from x import …` / `import x`(`scripts/` 下工具常被这样用);
+   - **包内相对导入**按本文件所在包解析:`from . import x`、`from .x import y`、`from ..pkg import z`。
+3. **api 公开面**=各 `api.py` 的 `_TARGETS`(`AnnAssign`/`Assign` 都算)值,也是引用。
+4. **分类**:零引用(无代码/文档/壳/api 提及)=候选;只被**自己的壳**提及且壳路径无人当命令行用=等价零引用;只被文档提及="文档工具"(保留,删文档前不动)。
+5. 复跑脚本:`F:\temp\_h9\p8_scan5.py`(引用计数)、`F:\temp\_h9\p8_extra2.py`(可疑件引用面 + 壳体检)、`F:\temp\_h9\p8_stats.py`(本文表格取数)。
+
+## 8 清理执行记录(2026-09-28,用户批 ①+④ 两批)
+
+| 批次 | 处置 | 具体 |
+|---|---|---|
+| ① A 类 | **已删** | `app_ontology/common/app_ontology_guanlan/sop/cms_availability.py`(275 行)与壳 `src/sop/cms_availability.py`(23 行);并从 `app_ontology/api.py` 摘掉公开名 `sop_cms_availability` |
+| ④ E 类 | **已清** | `scripts/sim_hub/frames/`(37 张 PNG,8.7 MB,本已 .gitignore)+ 全仓 46 个 `__pycache__/` 与散落 `.pyc`,合计释放 **约 15.4 MB**(`.venv/` 未动) |
+| ② B 类 | **保留(待定)** | 8 件零引用一次性生成器仍在原位;若要删,需先确认云端页与仿真页产物**不再需要重生成** |
+| ③ D 类 | **保留** | 33 个"旧路径已无人使用"的兼容壳仍在原位(保留旧路径写法可用) |
+| C 类 | **保留** | `objectstore.py`(MinIO)、`cache.py`(Redis)接入点契约不动 |
+
+清理后的复检:全门禁 rc=0(含模块边界 R1–R8、配置统一、页面归口)+ `guanlan.py check` 全绿 + 起服务逐页探活 + 打包开箱验证 5/5 + 卸载核验通过。

+ 6 - 5
docs/版本记录.md

@@ -7,7 +7,7 @@
 <!-- VERSION-LOG:BEGIN -->
 ## 版本号规则(用户令 2026-09-17)
 
-编号 **`v<大版本号>.<中版本号>.<小版本号>`**,例如 `v2.12.0`。
+编号 **`v<大版本号>.<中版本号>.<小版本号>`**,例如 `v2.12.1`。
 
 | 级别 | 含义 |
 |---|---|
@@ -17,12 +17,13 @@
 
 **怎么改**:改动落在"解决方案/架构/核心功能" → 大 +1(中/小归 0);落在"非核心功能的新增/减少/修改" → 中 +1(小归 0);只是"消缺完善" → 小 +1;一次发布混了几类,取最高那一级
 
-**打包文件名** = `app_guanlang_v2.12.0.zip`(规则:`app_guanlang_v<版本号>.zip`)。
+**打包文件名** = `app_guanlang_v2.12.1.zip`(规则:`app_guanlang_v<版本号>.zip`)。
 
-版本号只写在 `src/version.py` 一行(`VERSION = '2.12.0'`);打包器、安装脚本、安装记录 `install-info.json`、自检都读它,不另设真源。
+版本号只写在 `src/version.py` 一行(`VERSION = '2.12.1'`);打包器、安装脚本、安装记录 `install-info.json`、自检都读它,不另设真源。
 
 | 版本 | 日期 | 级别 | 主要变更 | 说明 |
 |---|---|---|---|---|
+| `v2.12.1` | 2026-09-28 | 小 | 源码梳理与死代码清理:删 cms_availability(275 行,另有实现)+ 清本地残留 15.4 MB | 清理类改动(不改行为口径)⇒ 小版本 +1。用户令「梳理源代码,及清理无用源代码程序」。新增 docs/源代码梳理_v0.1.md(761 受管件 / 437 .py / 72,787 行 / 158 兼容壳的规模与分层、十条根入口、23 个质量门、依赖纪律 R1–R8,以及按"引用计数 + 出处"取证的五类无用件清单)。① 删死代码: app_ontology/.../sop/cms_availability.py(275 行)与壳 src/sop/cms_availability.py(23 行)——全仓无调用,L0 数据可用性判据的实际实现在 scripts/rudong_model_run.py::l0_layer;并从 app_ontology/api.py 摘掉 sop_cms_availability 公开名。④ 清本地残留: scripts/sim_hub/frames/(37 张 PNG,8.7 MB,本就 .gitignore)+ 全仓 46 个 __pycache__ 与散落 .pyc,共释放约 15.4 MB(.venv 未动)。②③ 两批(8 件零引用一次性生成器、33 个无人使用的旧路径壳)经用户裁示保留;C 类接入点契约(MinIO objectstore / Redis cache)保留。验收: 全门禁 rc=0(模块边界 R1–R8 / 配置统一 / 页面归口 / 产物反向呼应 / 链路缺口 / 可移植 / 版本记录 / 依赖台账)+ guanlan.py check 全绿 + 起服务逐页探活 + 打包开箱 5/5 + 卸载核验。 |
 | `v2.12.0` | 2026-09-28 | 中 | 模块化重构收官 P7:质量门与打包安装迁移(23 件 / 7,426 行)——七个模块齐备 | 重构收官的架构里程碑(七个模块目录 + 公开面 + 兼容壳齐备)⇒ 中版本 +1。P7 把质量门与打包安装 23 件 / 7,426 行迁入 app_qualityGate/common/app_qualityGate_guanlan/{audits,docs,pack}/;旧路径 scripts/<件>.py 留 CLI 壳(公开名同面转发 + main(),guanlan.py check 的 importlib 载入照旧)。**根引导脚本 install.ps1/install.sh/uninstall.*/check.bat/pack.*/start|stop.bat 原地不动**("解压即用"引导层,其 Python 实现已迁入模块)。实逮: ① 子包内 __file__ 取根(parents[1])失效 22 处 ⇒ 统一 install_root();② 文件间 bare import 4 处改相对导入;③ audit_chinese_terms 的 here.parent 是仓库根语义 ⇒ 换 install_root();④ 补 P4 遗留:该审计器被审件 scripts/windscada_serve.py 早已是壳 ⇒ 改读实现位置。验收: 8 条质量门 stdout 逐字节等于迁移前 + 23 个 CLI 壳 importlib 公开名同面 + 新路径 -m 可跑 + 打包器从新位置出包并开箱验证 5/5 + 卸载核验通过 + guanlan.py check 全绿。至此 P0–P7 全部完成:公共/数据接入/算法/本体/前端/后端/质量门各一个目录与 api 公开面,旧路径全部留兼容壳。 |
 | `v2.11.9` | 2026-09-28 | 小 | 模块化重构 P6:本体与知识层实体迁移(58 模块 / 19,775 行) | 重构推进(源码组织层)⇒ 小版本 +1。本体 20 件 + SOP 38 件迁入 app_ontology/common/app_ontology_guanlan/{ontology,sop}/;旧路径 src/ontology/<件>.py、src/sop/<件>.py 全部留兼容壳 (模块别名 + `python -m` 经 runpy 重跑实现,重算链 ⑦ 步命令不变)。实逮: ① 包内 `from .. import paths`(23 处)改绝对路径;② 19 处 __file__ 取根改 install_root();③ `-m` 入口必须由壳 runpy.run_module(run_name="__main__") 兜住;④ 按源码路径读本层的审计器与登记表(audit_chinese_terms、check_portability 的 ALLOW 键、products_reverse_audit 的生成端、configs/{registry,terms/jargon_rules}.yaml)同步改位置。验收: 58/58 模块新旧路径同体导入 + 取根抽查正确 + 十条本体/SOP 路由响应体逐字节等于迁移前 + `-m` 旧入口冒烟通过 + 全门禁 rc=0 + 开箱验证 5/5。 |
 | `v2.11.8` | 2026-09-28 | 小 | 消缺:guanlan.py stop 在 pids.json 读不动/删不掉时抛异常中断 | 消缺(不改行为口径,只修坏掉的路径)⇒ 小版本 +1。根因: `cli.py::cmd_stop()` 直接 `PIDS.unlink()`,而**服务宿主与启动器并存**时 run/pids.json 可能已被清掉、被句柄占用或读到一半;2026-09-28 远端同步 2.11.7 时实逮:`guanlan.py stop` 以 FileNotFoundError 中断,组件其实已停掉。修法: 读记录失败按空记录处理(如实报一句);删记录失败只报一句,不影响停机结果与退出码;`v.get("pid")` 容忍记录缺字段。回归: pids.json 不存在 / 是目录(读+删都失败)/ 内容损坏 三种情形均不抛异常且 rc=0。验收: 本机 `guanlan.py stop` 正常停六个组件并删记录、再跑一次给友好提示 rc=0;三情形回归 3/3 通过;全门禁 rc=0。 |
@@ -53,8 +54,8 @@
 
 ## 本次交付
 
-* 版本:`v2.12.0`(观澜·如东样板 v2)
-* 交付包:`app_guanlang_v2.12.0.zip`
+* 版本:`v2.12.1`(观澜·如东样板 v2)
+* 交付包:`app_guanlang_v2.12.1.zip`
 * 服务名:`guanlan`(Windows 服务 / systemd 单元)
 * 安装 / 卸载:`install.bat`、`install.sh` / `uninstall.bat`、`uninstall.sh`
 <!-- VERSION-LOG:END -->

BIN
docs/系统设计说明_观澜_2.12.0.docx → docs/系统设计说明_观澜_2.12.1.docx


BIN
docs/需求分析_观澜_2.12.0.docx → docs/需求分析_观澜_2.12.1.docx


+ 0 - 23
src/sop/cms_availability.py

@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-r"""兼容转发(P6,2026-09-28):实现已迁到 `app_ontology.common.app_ontology_guanlan/sop/cms_availability.py`。**本文件不得新增逻辑**。
-
-两种用法都保持原样:
-* `from src.sop import cms_availability` / `from src.sop.cms_availability import X` —— 本模块整体指向实现(并改写父包属性);
-* `python -m src.sop.cms_availability` —— `runpy` 按 `__main__` 重跑实现(实现里的 `__main__` 守卫照旧生效)。
-"""
-from __future__ import annotations
-
-import pathlib
-import runpy
-import sys
-
-sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2]))      # 安装根
-
-if __name__ == '__main__':
-    runpy.run_module('app_ontology.common.app_ontology_guanlan.sop.cms_availability', run_name='__main__')
-else:
-    from app_ontology.common.app_ontology_guanlan.sop import cms_availability as _impl
-    _parent = sys.modules.get(__package__ or '')
-    if _parent is not None:
-        setattr(_parent, 'cms_availability', _impl)
-    sys.modules[__name__] = _impl