Files
next-tjwater-server/docs/scada-peripheral-replay-design.md
T
jiang 26fa5d1909
Generic Container CI/CD / test-build-publish (push) Successful in 3m39s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m39s
feat(scada): add project monitoring replay
2026-11-04 14:27:21 +08:00

55 KiB
Raw Permalink Blame History

SCADA 外围系统时段回放、主动监测与爆管调查设计

状态:时段回放、主动周期监测、条件式 burst_report 调查及回放安全的爆管定位适配已实现。单步处理由用户操作触发并在当前 API 请求内完成,不部署 SCADA 回放 worker。tjwater_v2 与 tjwater2_v2 尚未确认出厂边界流量角色;仍待完整月度数据和真实 Agent 联调。更新日期:2026-11-03。下面明确区分已实现范围、后续目标与验收要求。

本次将外围 SCADA 定义为一个模拟外部数据源:本地 TSDB 已保存原始观测,通过只读 API 按选定时间区间和后台时钟分批读取 scada.measurements.monitored_value,将这一批直接交给清洗流水线,保存 cleaned_value 后执行异常检测及后续 Agent 调查。原始值、修正值、真实工况异常和处理结论分别保留。浏览器负责控制和展示,关闭页面不停止后台回放。

演示主路径为“TSDB 已存 raw → 用户点击下一步并监测 → 顺序读取一个步长内的观测 → 清洗并更新修正值 → 在步长边界执行多指标监测 → 满足条件时进入爆管调查与定位”。真实外部系统新增观测时,仍先通过入库接口保存 raw,再进入相同的清洗检测流程。演示读取已有数据不再次调用原始值插入接口,也不把同一批数据从一个内部服务读出后再写回原表。

演示操作以用户选定的时间区间为中心:一个月是可选择的数据范围,单次演示可以只覆盖其中几小时或几天。时间轴采用 FM 调频盘样式,玻璃刻度窗内有固定红色指示线,时间刻度带随拨动横向移动。时间轴使用 5 分钟时间戳定位;项目监测频率只决定“下一步并监测”覆盖的业务周期,两者互不替代。

首版实现范围与限制

已实现只读 raw 分页、持久回放控制、按项目监测频率推进、逐时刻清洗检测、质量告警与运行异常事件、周期监测回执、组合判定及调查任务。主动单步只有组合结果为 suspected_burst 时才创建 burst_report。前端改为后端状态投影,主界面只保留选定区间、5 分钟 FM 时间定位、主动周期控制和本周期监测结论,不重复展示 SCADA 设备目录与逐测点读数表。主要代码为 回放服务、组合监测、质量与检测、持久状态 和 前端组件。

本版采用每项目最多 500 个设备、一个完整时间戳一批、至多一个未完成批次的串行处理,不实现 20 批并发队列或大于 500 个设备的分片接收。处理变慢时逐批追赶且不跳过中间观测;后台停机期间最多补算两秒现实时间,不一次性追赶全部停机时长。数据质量以有限数值和显式仪表量程为依据,不自动插值或平滑;缺测判断使用设备档案的采样周期。分组规则的恢复需要该组设备在同一帧都有可信观测,不同采样周期或采样相位应拆成规则。

本版冻结设备、区间、规则及批次输出,按批次原始值摘要校验源变更。完整月度数据清单、全月源版本摘要、独立事件真值和检测效果评估仍是后续数据准备工作;能力接口仅判断回放存储是否存在与当前操作权限,不声明月度数据已经就绪。现有 14 天数据不能当作一个月验收。

Agent 查询本轮已检测的固定批次快照,受任务的 replay_id、knowledge_cutoff、batch_sequence 约束。开始前历史只供后台基线初始化,本版调查查询不返回该部分;爆管定位已支持受回放截止约束的冻结输入适配,其他未适配计算工具继续拒绝。新 SCADA 调查 Skill 要求据实说明这些证据限制。运行异常自动建立调查,数据质量异常仅建立告警,不触发模型;恢复时推进调查代次。真实模型推理与报告效果尚未联调。

控制进度和事件状态使用有界轮询,沿用告警流发布业务告警;本版未新增 scada.replay.changed SSE 主题。演示面板只显示最近一个周期的组合结论,逐测点输出和完整事件历史由分页 API 保留给诊断与调查入口,不在主面板重复铺开。

已确定的设计边界

  • 数据集覆盖一个月,包含多次独立异常及各次事件发生、持续、恢复的过程。
  • 项目配置一个 SCADA 监测频率,只用于一次主动监测所覆盖的周期;时间轴独立使用 5 分钟时间戳,它不关联设备或设备类型,本阶段不据此创建定时任务。
  • 设备采样周期仍属于设备档案。回放实际传入时间以原始观测时间戳为准,不使用项目监测任务间隔生成、重采样或判断设备缺测。
  • 复用 scada.measurements.monitored_value 与 cleaned_value;不另建一套正式 SCADA 原始值和修正值表。
  • 回放读取 API 只返回原始观测,修正值由本地清洗流水线计算和保存;GET 不更新游标、不清洗、不创建检测任务。
  • 真实压力、流量、水质异常保留在历史序列中。事件恢复或调查结束不删除、不抹平历史异常。
  • 数据处理顺序为原始观测持久化、数据质量校验、必要的修正、业务异常检测、事件与调查。
  • 步长内的数据批次按原始时间驱动清洗和基础规则检测;到达步长边界后,用户触发的本周期监测汇总多项指标。正常周期不启动 Agent。
  • 时间范围、设备、数据集版本和检测规则在一次回放创建后固定;改变这些条件需要新建回放。
  • 页面首先选择演示起止时间,再建立回放。月度数据集、设备配置和规则作为来源与设置,不占据主播放区。
  • 本次演示对已存 raw 只读,清洗阶段仅更新同一条观测的 cleaned_value;不导出整月副本、不重复插入原始值。每次回放的质量结论、检测结果和调查证据按回放身份隔离。

项目通用配置与监测频率

MetaDB 的 projects.settings 为项目级通用 JSONB 对象,默认 {},用于保存可扩展的业务参数,不存放密钥。新增参数可直接增加 JSON 键,无需为每项配置新增数据库列;后端为已知参数提供类型校验,并保留其他 JSON 键。

其中 settings.scada_monitoring_interval_seconds 表示项目的 SCADA 监测频率,单位为秒。本阶段只用于主动处理边界和监测周期标识,不控制时间轴刻度,不注册 cron、后台 schedule 或周期性 Agent 任务。它与设备目录、设备类型、设备采样周期和页面刷新周期均无关联。省略或设为 null 表示未配置;有效范围为 60 秒至 365 天。

管理员可通过 PATCH /api/v1/admin/projects/{project_id} 配置:

{
  "settings": {
    "scada_monitoring_interval_seconds": 900
  }
}

更新时按 settings 顶层键合并:未传入的键保留,传入的嵌套对象整体替换,settings: {} 不清空已有配置,settings: null 会被拒绝。读取项目详情和管理员项目列表均返回 settings。

组合监测仍复用该 JSON 对象,不新增固定数据库列。后端类型化嵌套对象显式声明项目实际具备的监测角色:

{
  "settings": {
    "scada_monitoring_interval_seconds": 900,
    "scada_monitoring": {
      "rule_version": "burst-monitor-v1",
      "pressure_device_ids": ["明确的压力设备 ID"],
      "outlet_flow_device_ids": ["明确的出厂边界流量设备 ID"]
    }
  }
}

当前代码已类型校验并在创建回放时冻结 scada_monitoring。项目没有出厂流量或不足 5 个完整压力点时保持缺项,能力接口说明原因,监测回执返回 insufficient_data;不能为了让演示通过而把其他流量点冒充出厂流量或虚构设备。tjwater_v2 现有目录只有通用 pipe_flow 类型及模型管段映射,没有足以证明“出厂边界”的角色字段,因此本轮未写入该嵌套配置。

部署前需在 MetaDB 执行 resources/sql/033_metadata_project_settings.sql。迁移会将实验版本可能存在的独立监测间隔列搬入 settings,核对冲突后移除旧列;重复执行不丢失已有配置。当前 tjwater_v2 与 tjwater2_v2 均配置 900 秒,即 15 分钟的主动监测周期。该参数不改变 5 分钟时间轴定位,本阶段明确不实现通用定时任务调度。

实施前基础与原设计差距

下表记录实施前基础,用于说明改动缘由;本次实际实现以首版范围及后文接口表为准。设备目录采样周期为本轮讨论中 2026-11-02 的只读核查快照,与项目监测任务间隔是两个概念。

位置 当前能力 本次需要补齐
外围 API、存储路由 挂载在 /peripheral,使用 Bearer 与 X-Project-Id,按已授权项目解析 BizDB 和 TimescaleDB 回放控制、源批次、回放状态与能力声明
历史观测接口 单点、最多 7 天、最多返回 1000 条并声明截断;当前使用 api_query_id 查历史 回放读取使用规范 device_id,按时间和设备联合游标分页;不能直接拼接现有单点接口来装载一个月
SCADA API、仓储 范围读取、最新值读取、批量插入;当前范围读取返回 raw 和 corrected,缺少回放游标及完整流水线 增加只读 raw 分页契约;复用存储查询,补齐处理检查点、清洗与检测调度
清洗服务 从原始值计算并更新 cleaned_value;目前支持压力和流量 在线质量标记、因果修正及选择值依据;水质不能直接套用现有清洗
压力清洗、流量清洗 包含居中窗口、后向补值、Kalman 平滑;流量路径还会补为 1 分钟序列 在线路径只使用已到达的数据,保留设备频率,不整月预清洗后再模拟检测
外围工单事件同步 持久事件、通知唤醒、后台同步和定期对账 复用可靠处理方式,SCADA 的批次有自己的持久状态
调查 worker 可靠投递 event_investigation,恢复执行租约,管理 Agent 执行代次 接收由 SCADA 异常事件建立的调查;不承担每条观测的检测
任务告警 支持 raised、acknowledged、resolved;当前来源必须携带任务和执行标识 增加真实的回放检测来源,不能为调查前告警伪造 task_id、run_id
MCP 上下文、SCADA 查询 校验任务、项目、执行代次和会话,保存查询证据 在所有回放调查读取入口执行统一的时间与数据版本约束
业务 Skill 映射 目前映射爆管上报、水质投诉、预约跟进 增加 SCADA 回放调查流程,不将检测信号直接等同于已确认爆管或污染
前端 SCADA 区域 旧版同时展示设备表、逐点读数、重复概览和手工合成读数 改为服务端状态投影;主界面只承担时间定位、主动单步和周期结论,详细数据留在诊断与调查接口

实施前只读核查中,tjwater_v2 有压力、流量和水质监测类型,其他项目的类型集合与历史周期并不相同。项目通用配置保存一个监测任务周期,不与设备类型绑定,也不把 5/15 分钟写成枚举;设备原有采样信息仍用于缺测判断。

现有 演示记录 描述的是 14 天合成观测,不能作为一个月数据已经准备好的证明。一个月数据准备与本设计实现分别验收。

运行链路与职责

本地 TSDB 已存月度原始观测;项目 BizDB 保存设备目录
             |
             v
只读 raw API / 同一读取服务 <--- 持久回放状态 <--- 页面“下一步并监测”
             |
             | 返回一批原始观测,不再写入 raw
             v
后台固定本批记录引用、摘要和处理身份
             |
             v
后台质量校验与清洗
             |
             v
本地 TSDB:仅更新 cleaned_value
             |
             v
用同批清洗结果执行基础规则检测
             |
             v
到达步长边界,汇总爆管侦测、压力下降、出厂流量变化
             |
             v
疑似爆管且定位输入具备 ---> burst_report 任务与 MCP Skill
             |
             v
工作台告警、调查进度与结果

SCADA 单步命令在当前 API 请求内按原始时间顺序完成 raw 读取、清洗、基础规则检测和周期组合监测。每个阶段仍写入持久批次,网络中断后的同一幂等请求可以继续未完成步骤,但页面关闭后不会自动推进时间,也不需要单独的 SCADA 回放进程。

现有调查 worker 只处理已经创建的调查执行,计算 worker 继续处理长计算。当前阶段不增加消息中间件,不扩展通用 schedule API,也不注册 SCADA 定时任务。页面的明确单步命令写入持久 step_target,API 只完成这次已授权的周期处理;不能用“每隔几分钟创建一条 Agent 任务”替代。

最小可交付版本必须包含主动周期处理、批次可靠保存、至少一种明确配置的检测规则和结果展示;只移动页面时间轴不能代表数据已经处理。

一个自然月的数据集

数据集用带时区的半开区间定义。例如北京时间 [2026-10-01 00:00, 2026-11-01 00:00) 是 31 天,不能固定按 30 天计算。内部统一保存带时区时间,界面按 Asia/Shanghai 展示。

准备数据时冻结清单:dataset_id、dataset_revision、源项目、观测区间、可用历史基线区间、设备规范 ID、类型、单位、设备采样周期、采样相位、指标含义、raw 分片摘要和规则配置版本。由服务端读取项目路由,页面不传物理库名、SQL 或连接信息。摘要只覆盖原始观测与必要元数据,不覆盖会被本轮清洗更新的 cleaned_value。月度摘要由数据集准备流程预先生成,创建回放的 HTTP 请求只校验清单,不同步扫描和加载整个月。

首版演示直接读取已准备在本地 TSDB 的 raw,不另导出一套完整原始数据源。月份数据如果尚未齐备,由单独授权的数据准备过程补齐并生成版本清单;创建或播放回放本身不生成、删除、搬移或重复插入 SCADA 原始观测。演示批次是对已有观测的读取与处理身份,不能在统计中称为本轮新插入条数。

设备档案采样周期与实际观测间隔不一致时,记录配置差异和缺测情况,仍按实际时间戳读取。不会将 15 分钟数据插值成 5 分钟观测,也不会把没有记录的时刻当作正常值。项目监测任务间隔不参与这项判断。

多次异常应同时覆盖:真实压力异常、真实流量变化、真实水质变化、仪表或通信故障、正常调度引起的变化,以及恢复后的再次异常。数据集真值清单独立保存,用于事后计算命中、误报、漏报、重复事件数和发现延迟。真值不得出现在检测输入、Agent 上下文、预告型时间轴标记或普通业务报告中。

水质必须声明具体指标。当前演示中的化学示踪物浓度不能直接解释为余氯、浊度,也不能只凭 mg/L 选择检测阈值。流量保留方向,压力确认 m、kPa、MPa 等单位后再应用规则。

数据集准备负责人需要在运行前提交版本化设备清单、规则参数和独立真值。这是新数据集的输入要求,不表示本次已选定新的 5 分钟设备分组或生成了观测。

时间轴和读取语义

时间与进度

名称 含义
observed_at 原始观测时间,不随回放改写
virtual_now 已完成主动监测的周期边界时间
delivered_until 本轮源区间已读完,且所有分片的记录引用已登记为待处理的时间上限;表示模拟传入进度,不是原始值新插入时间
processed_until 质量校验和检测已完整提交的时间上限
read_at、cleaned_at、processed_at 实际批次读取、清洗提交和检测完成时间,用于延迟、故障与审计

始终满足 processed_until <= delivered_until <= virtual_now <= end_at。还未处理任何批次时,读取和处理进度为空,不能伪装成起点已经处理。GET 返回数据不自动推进任何进度;后台确认本批处理身份后才更新读取进度。表格可显示本轮已读取但仍在清洗的 raw 并标明状态,检测只能消费已完成清洗阶段的批次。主界面的次级进度使用“已读取”和“已检测”。

后台仅在播放状态累计模拟时间。暂停期间不推进;恢复时重新锚定现实时间。进程重启从持久进度重新锚定,不把停机时长一次性折算成需要跳过的数据。

顺序读取

  1. 整次回放选取 [start_at, end_at);首批包含 start_at 的观测。
  2. 时间推进到 T 时,候选记录必须不晚于 T、早于 end_at,且在已成功接收的联合游标 (observed_at, device_id) 之后。
  3. 所有选中设备合并后按 observed_at, device_id 排序;优先按同一观测时刻成批,不按设备分别跑独立时钟。
  4. 每个传输片段最多 500 条、序列化正文最多 2 MiB;超限继续分片。记录片段序号与该时刻是否完整,不在半个时刻接收成功时提前推进时间上限;同一时刻的设备分组检测等待该时刻所有分片收齐。
  5. 后台将本批记录引用和原始摘要登记成功后,才推进读取游标。重试同一批使用相同编号和摘要;清洗或检测失败从该阶段恢复,不重新选择一批、不重新插入 raw。正常路径复用本次读取的 raw 对象计算清洗结果,清洗提交后直接将同批结果用于检测,不再重新查询该批 raw 或 corrected。
  6. 没有观测的时间段按设备档案采样周期与采样相位识别应到时刻。后台提交记录数为零的区间检查批次,推进覆盖进度并触发缺测判断;它不生成伪造观测。不能仅依赖“下一条数据到来”唤醒,否则连续断流永远不会被发现,终点也无法排空完成。

例如设备档案中的压力采样周期为 5 分钟、流量采样周期为 15 分钟。时间轴到 09:10 时,压力设备可传入 09:00、09:05、09:10,流量设备只有 09:00;到 09:15 时,再传入两类设备对应的 09:15 记录。

首版至多保留一个未完成批次,清洗检测提交后才选择下一批。单次请求不跳过周期内的中间观测、质量检查、异常开始和恢复,也不提供播放倍率。

页面操作

操作 约定
创建 固定数据集、范围、设备和规则,进入 ready
播放 ready 或 paused 进入 running,由后台推进
暂停 停止释放新批次;已持久接收的批次继续处理。返回实际生效进度,在途接收与暂停命令通过同一回放版本串行裁决
下一步并监测 在 ready 或 paused 状态推进到下一个项目监测周期边界;周期内的所有观测按原始时间顺序处理,到边界后提交一次本周期监测回执并保持暂停,尾步可短于完整周期
调速 原子更新速度和时间锚点,不改变观测顺序
拖动时间轴 只查看已释放的数据与已产生的事件;未来区间不预览读数,不触发事件
从指定时刻重播 新建 replay_id,独立检测与调查;新起点之前仅作为有声明的历史基线,跳过段不声称已经检测
到达终点 停止新批次,进入 draining;待已接收批次完成后 completed。Agent 调查是否完成另行展示
停止 终止继续传入,保留已接收批次、事件和证据;不自动撤销已经建立的调查
重试故障 修复依赖后校验固定配置,failed 进入 paused,重试原未完成批次;源版本或批次内容冲突需要新建回放,不能靠重试忽略

生命周期为 ready、running、paused、draining、completed、stopped、failed。队列等待、基线不足和依赖暂不可用是处理状态或原因字段,不再增加一套并行生命周期。

首次运行遇到可重试投递错误,按现实时间退避重试同一批次;权限错误、数据集版本变化、同批编号内容冲突和结构缺失进入 failed,明确原因。用户恢复前必须解决原因;原批次事实继续保留。

选定演示区间

用户先在月度数据可用范围内选择起止时间。创建回放后,将该区间固定为当前播放范围;进度比例与剩余时长都相对该区间计算。例如选择 10 月 8 日 08:00 至 14:00,时间轴只覆盖这 6 小时,不能将月内 8 日的位置作为本轮进度。

主要界面常驻显示选定区间和当前监测时间。月份、数据版本、设备范围及规则收进来源与设置区域;需要校验的配置差异仍在开始前明确显示。不存在上一次选择时不自动处理整个月,等待用户确定区间。

演示建立后改变时间区间需要停止并创建新回放;保留旧回放的事件和证据,不把旧回放时间游标直接重设。已完成区间需要再次演示时,新建独立回放;不自动循环处理和重复触发调查。

选定区间之前的可用历史仅用于已声明的基线初始化,不占用主时间轴、不作为新到达批次、不预触发此前事件。基线不足时在当前区间显示具体可用性,不能悄悄扩大演示范围或读取区间之后的数据。

处理到选定终点后停在终点,显示数据处理是否完成及剩余调查数量;不会自动继续处理该月剩余数据。空区间明确显示没有观测,不能通过控件动画假装已经收到 SCADA 数据。

5 分钟时间定位与项目监测周期

时间轴以 5 分钟时间戳组织刻度、标签和选择值,只负责定位和回看,不读取 projects.settings.scada_monitoring_interval_seconds。方向键按 5 分钟移动;鼠标和触摸的拖动轨迹保持连续调频手感,选定值仍按最接近的 5 分钟时间戳归档。标签密度可以随面板宽度合并,不会改变拖动手感或时间选择精度。

能力接口另行返回当前项目监测周期;创建回放时由服务端固定到回放文档及快照的 monitoring_interval_seconds,页面不能提交不同周期。项目配置后续调整只影响新回放,当前演示继续使用固定值;未配置时提示设置。

交互 行为
方向键 前后移动一个 5 分钟时间戳
PageUp / PageDown 移动一个当前可见的主刻度,主刻度始终为 5 分钟的整数倍
拖动回看 拖动轨迹连续无缝,选择值按 5 分钟量化;只读定位不推进后台
主刻度 宽屏可每 5 分钟标注,窄屏按 10、15 分钟等密度显示,细刻线保留 5 分钟定位
下一步并监测 不读取时间轴位置;如后台已监测至 08:07,15 分钟周期处理到 08:22

“下一步并监测”通过持久化 step_target 与 step_pending 管理。单步开始后,API 按原观测时刻读取、清洗和检测到目标边界,完成前禁用重复单步。若周期内有 5 分钟观测,15 分钟监测周期仍处理其中每个时刻;没有观测则提交空区间检查,不补造数据。尾步到达 end_time 时,仍遵守原始区间 [start_time, end_time)。

时间轴以 5 分钟时间戳定位,不能控制或触发周期处理;“下一步并监测”按项目监测频率推进后台周期,不将原始观测重采样。旧回放没有固定周期时,页面要求停止后重新创建,不能把后来修改的项目周期追补为历史事实。

步长边界主动监测与爆管流程

一次主动监测的身份为 (replay_id, period_end, monitor_rule_version)。同一步骤重试复用原回执,不能重复创建告警、调查或计算。监测回执保存在该步最后一个回放批次的 monitoring 中,并将最近一次结果投影到回放快照;内容包含周期起止、知识截止、输入批次上限、数据完整性、各指标状态、组合结论和关联任务。它不是 scheduled_flow,也不需要通用定时器。

步长边界按以下顺序执行:

  1. 确认本周期内所有已到达观测均已完成质量校验和清洗;缺测、无效值和单位冲突进入数据完整性结果,不能按正常值处理。
  2. 汇总压力下降幅度。只使用映射明确、单位一致、质量合格的压力测点,并同时保留异常测点数、覆盖测点数、基线窗口和最大或中位下降幅度。
  3. 汇总出厂流量变化。设备必须在项目配置或设备档案中明确标注为“出厂边界流量”,不能把任意 pipe_flow 测点当作出厂流量;保留方向、单位、基线和增量。
  4. 条件满足时运行爆管侦测。现有侦测要求至少 5 个完整压力点和目标时刻之前 15 天同周期数据;不满足时返回 baseline_not_ready 或 sensor_coverage_insufficient,不能将“未运行”解释为“无异常”。
  5. 组合上述证据形成监测结论;只有疑似爆管才创建 event_investigation,工作流使用现有 burst_report,并绑定本轮 replay_context。正常周期只保存回执,不启动 Agent;数据不足周期保存缺口并在界面展示。
  6. burst_report Skill 核对证据后决定是否调用爆管定位。定位必须有 m³/s 的有依据漏量估计或明确情景假设,以及模型节点映射、异常窗和正常对照窗;输入不具备时保持“疑似爆管、定位待补充”,不能填默认漏量。

组合判定采用可版本化策略,首版演示建议如下:

监测结果 判定条件 后续动作
insufficient_data 必需点位缺测、质量无效、单位或角色未配置,或爆管侦测基线不满足 保存缺口,不声明正常,不启动定位
normal 数据完整,爆管侦测未命中,压力与出厂流量规则均未命中 保存周期回执,不创建 Agent 任务
attention 只有单项指标命中,或指标间方向不一致 保存观察结果;下一步继续监测,必要时由人发起调查
suspected_burst 爆管侦测命中,且压力下降与出厂流量增加在同一业务时间窗内相互支持 创建一次 burst_report 调查;定位输入具备后进入计算

爆管侦测和压力下降存在相关性,因此回执不能把两者描述成完全独立的两票。出厂流量也会受泵站调度、储水设施和正常用水变化影响。组合结论只是调查触发条件,不是现场确诊。

回放中的 MCP 定位已走 Server 持有的安全适配层:按 replay_id、knowledge_cutoff 和 batch_sequence 从已固化清洗输出组装压力与可选流量序列,再调用现有计算队列。回放只允许 monitoring 模式,并要求显式异常窗、正常对照窗和有依据的 burst_leakage;不能直接查询 TSDB 中尚未回放的未来记录。步长爆管侦测同样只读取最近 15 天已固化输出。计算结果保存输入、算法与证据引用,任务恢复或重试仍使用相同知识上限。

FM 调频盘样式

用户明确选择电台调频盘的刻度、红色指示线和玻璃质感。这里的 FM 表示调谐外观,不增加电台、音频、频段切换或拟物旋钮功能。

视觉方向:工作台内一块冷白半透明的玻璃刻度窗,红色细线作为唯一醒目的时间定位标记;文字、控件与下方数据沿用现有工作台主题。内容顺序为选定区间、当前时间、刻度窗、控制栏、传入与检测进度。交互特征为固定指示线与横向移动的刻度带,手动拨动直接跟手。

FM 调频式时间轴静态样式示意

图中时间和状态仅为静态样式示意,不是运行结果。玻璃下的底色用于说明透光与边缘高光,运行时仍需验证实际工作台背景下的对比度。

元素 设计约定
演示区间 常驻显示完整起止日期和时间;更换区间是次要入口,月度来源收在设置中
当前时间 刻度窗上方突出显示红线对应的时间,数字等宽;跨日时同时显示日期
时间刻度 主刻度有时间标签,次刻度细密且高低有别;实际密度按区间与容器宽度计算,窄屏优先减少标签而非缩小文字
红色指示线 固定在刻度窗水平中央,1 至 2px 宽,带小型顶端指示;这是时间定位色,不代表告警严重度
玻璃刻度窗 局部半透明、背景柔化、细边缘高光和轻微内阴影,玻璃层不影响数字、刻度和红线的清晰度
控制栏 播放或暂停是唯一主操作,下一批与倍速为辅助;回看时才出现回到当前
次级进度 单行显示已传入与已检测时间,积压时显示等待处理;不增加三块同等醒目的仪表

玻璃效果是本次按用户要求对 SCADA 调频刻度窗的局部材质例外,外围面板外壳、表格、表单、菜单和 Agent 面板仍沿用现有 shadcn/ui 实体设计。新增时间指示颜色使用独立语义变量,不能让正常播放状态套用危险告警颜色。关闭透明效果或不支持背景模糊时,回退到清晰的实体刻度窗。

固定红线表示当前定位时间,刻度带按 5 分钟时间戳移动;只在选定区间内标注时间,两端之外留白,不带出整个月的刻度。该窗是选定区间的只读定位视窗,完整起止范围始终由上方文字明确表达;拨动不改变传入或检测进度。

手动拖动和触屏横拨改变查看位置,不立即改写后台时间。拨动到已释放时间时显示历史快照并标明回看;拨动到尚未释放时间时只显示待传入时间,不展示未来读数,若需从该位置开始则明确新建回放。回到当前只恢复跟随,不补发旧数据、告警或调查。

时间拨动按 slider 语义提供中文名称、时间范围、当前时间文本和键盘操作:方向键按 5 分钟刻度调整,PageUp/PageDown 调整一个主刻度,Home/End 到选定范围两端。键盘操作即时生效;鼠标和触摸拖动轨迹连续无缝、不带惯性,选定时间按 5 分钟量化。减少动态效果时关闭装饰性过渡,保留真实时间定位。状态不用颜色单独表达。

数据质量与业务异常

真实外部系统新增观测时,先通过现有入库边界将 raw 保存到 TSDB。本次演示使用已经入库的原始值,读取 API 校验身份、项目、设备与时间范围后返回 raw,直接进入质量校验和清洗;不为了模拟传入而重复执行原始值 INSERT。清洗只写回同一条观测的 cleaned_value,monitored_value 始终保留。

清洗完成后才启动业务异常检测。valid 可将 cleaned_value 设为原始值,corrected 保存有依据的修正值,无法可靠修正的 invalid 或 missing 保持空值并保存质量结论。suspect 保留 raw 与不确定性,不能自动替换为正常值。是否已经清洗以阶段回执和质量结论为准,不能仅用 cleaned_value 是否为空判断。

质量结论 选择值与后续处理
valid 使用 raw,包括可靠采集的突降、突升、低压和浓度变化
corrected 使用有依据的修正值,同时保留 raw、修正值、规则版本、原因和可用时间
suspect 保留 raw 并声明不确定性,结合其他测点和设备状态复核;不自动改成正常值
invalid 或 missing 保留原始记录或缺测事实,退出要求可信观测的规则输入,形成数据质量事件

质量校验依据结构、有限数值、单位、设备身份、观测顺序、重复记录、量程与设备状态;仅偏离日常水平不能直接判 invalid。负流量、零浓度、低压均需按设备和业务含义解释。缺少仪表状态时明确证据不足,不能凭一次尖峰确认仪表损坏。

当前正式表中的 cleaned_value 可以保留,但不等于本轮已经完成清洗。读取 raw 时不清空已有 corrected,也不将旧 corrected 当作清洗输入。清洗阶段使用本批 raw 和本轮已知历史重新判断,提交本轮清洗版本并保存处理结果;检测只能使用该版本已提交的结果,不能直接取一个来源和时间不明的旧 corrected 当作已校验输入。

在线修正只使用当前已到达数据。当前居中窗口、后向补值、整段 Kalman 平滑和整段统计阈值保留为离线分析能力;不能一次性清洗整个月后将结果送入时间轴。在线清洗更新目标 TSDB 的修正值投影,原始值不变;原修正版本、当前修正版本和依据保留在处理证据中,旧回放仍读取自己的固定版本。

每条参与判断的数据记录其 value_source、质量结论、选用值、原始记录引用、规则版本和计算时间。回放过程不为未知错误强制生成一个修正数值。

这一区分对应 EPA 在线水质监测指南第 6.1、6.2 节 的“先校验、再异常检测,并保留原始值”原则;历史修订留痕可参考 OPC UA Historical Access 第 4.4 节。本项目的具体质量判定仍由指标和设备证据决定。

检测规则和多次事件

首版实时检测使用有版本的类型化规则:压力变化与上下限、流量变化与方向、水质具体指标的上下限或变化,以及数据质量规则。规则输入包含质量结论,输出只表示异常信号及依据,不确认爆管位置或污染原因。

每项启用的规则必须声明:rule_id、指标、方向、单位、阈值、使用的历史窗口、最少有效样本、连续命中点数、恢复阈值、连续恢复点数,以及事件组和最少同时命中的设备数。参数在创建回放前校验并冻结,至少需要一种已配置规则才可创建检测回放。缺失参数时该规则为 not_configured,不能借用统一 30% 或把零到非零统一解释为 100% 污染风险。

同一个事件组由明确的设备集合组成,不使用预设异常真值自动分组。同组、同规则、同一持续异常归并为一个事件;不同事件组可以同时产生不同事件。恢复需要可信的新观测满足恢复条件,缺测不构成恢复。恢复后的再次命中建立新事件。

事件保存首次异常、检测触发、最后异常、恢复时刻、证据引用和调查结论。传感器恢复正常只表示观测恢复,不代表现场维修、污染处置或业务调查已经完成。

事件身份由 replay_id + rule_id + event_group_id + first_trigger_at 确定;同一事件更新不创建多条调查。批次重试不重复创建事件,新一次回放的事件与旧回放独立。

现有 爆管侦测服务 可作为满足条件后的进一步分析工具,不能直接等同于逐批在线检测器。当前 target 模式需要 15 天数据及至少 5 个完整压力测点。回放前没有足够历史时,展示 baseline_not_ready;支持短历史的规则仍可运行,不能用未来几天补足参考窗口。

正常基线只使用当时已知、质量合格且适用于该规则的历史。已确认异常可以从正常基线中排除,原始记录仍保留。事后确认或重新标注产生新版本,不改写此前检测“当时知道了什么”。

持久化和事务

观测数据以项目目标 TSDB 的 scada.measurements 为权威存储,沿用 time、device_id、monitored_value 和 cleaned_value。业务库不替代 TSDB 保存正式观测。

建议在 BizDB 增加以下处理对象:

对象 权威信息
peripheral_sample.scada_replays 回放身份、项目、创建者、冻结配置、生命周期、版本、模拟时间锚点、读取与处理进度、工作租约和恢复检查点
peripheral_sample.scada_replay_batches 本批 TSDB 记录键、raw 摘要、固定规则版本、清洗输出、质量结论、检测结果、处理阶段和重试信息

两个对象分别具有控制生命周期与批次处理生命周期。现有 test_data 是手工单条演练 CRUD,缺少顺序、批次和消费语义;任务表表示业务调查,也不能存正常观测批次。这里保存记录引用和处理结果,不复制一整套 raw;需要固化异常证据或修正依据时,只保存必要的原始片段。由于本次不重新摄入 raw,不新增 TSDB 原始值收件表。表和列同时写清数据库注释。

事件及当前告警复用 workbench.alert_events、workbench.alerts,调查及其证据复用现有 operational 表。告警来源扩展为判别联合:保留现有任务来源,新增 kind=scada_replay、replay_id、dataset_revision、规则组与触发批次,避免伪造调查身份。质量故障也用同一事件存储,按类别区分。

批次处理与提交

不使用一个长期跨库事务。批次按以下阶段单调推进,重试保持原批次身份:

  1. selected:后台读取 raw 后,BizDB 固定记录键、输入摘要、规则与知识截止时间,并在同一事务登记处理身份和读取检查点。GET 本身不做此写入。
  2. clean_prepared:直接对已读入的这一批 raw 和已加载的历史状态计算质量与修正结果,先将确定的输出及输入摘要保存在 BizDB。正常路径不调用会重新查询同批 raw 的整段清洗入口。
  3. cleaned:TSDB 用一个有界事务按记录键更新 cleaned_value,并校验 raw 仍与本批输入一致;不更新 monitored_value。只有 TSDB 提交成功且 BizDB 记录已提交阶段后,才允许检测。单行结果相同时不必重写字段。
  4. 若 TSDB 已提交但 BizDB 阶段尚未更新,恢复时核验预存的输入与输出;已经匹配则补记 cleaned,未写入则幂等应用预存结果,raw 已变则停止该批次。只在这种恢复或校验冲突路径增加读取,不为每批正常处理增加一次重复查询。
  5. detected:直接使用本批已提交的清洗结果做检测,BizDB 在同一事务保存检测结果、事件状态、调查意图、检查点和 processed_until,然后发布业务事件。检测失败只重试本阶段,不重新清洗或重写 raw。

以上阶段保存在批次中。页面继续突出模拟时间、已读取进度和已检测进度,清洗中或失败作为当前批次状态展示。正常路径对新批次读取一次 raw、必要时批量更新一次修正值,再复用这批处理结果完成检测;界面后续查看与故障恢复属于独立的必要读取。

BizDB 批次状态是持久待处理队列,通知只负责唤醒。后台启动、通知中断与定期对账均扫描未结束批次,按已提交阶段恢复;同库状态与事件在同一事务保存,不能假定两个数据库天然原子。PostgreSQL NOTIFY 和 SELECT 锁定语义 提供通知及领取基础。

调用慢算法和外部服务前结束数据库事务,不持有事务锁等待 HTTP。清洗结果未提交,不执行异常检测;中断后从已有检查点恢复,不丢弃批次身份重新处理一遍。

重复读取与重复回放

读取 API 的同一次游标请求可以安全重试,但相同数据不能被当作新的处理批次。后台以项目、回放、批次序号和分片序号登记幂等身份,记录级使用规范设备 ID 与原始时间;同键同摘要复用现有处理状态,不同摘要返回冲突。

新一轮演示按新的 replay_id 建立独立处理状态,可以重新读取和分析选定区间,但仍不插入 raw。界面统计已读取条数和已处理条数,不能称为新写入 SCADA 条数。

首版同一项目只允许一条活动的读取清洗流水线,避免不同回放同时改写同一 cleaned_value。历史结果仍可同时查看。修正字段表示最近一次已发布清洗结果,旧版结果和依据保留在批次输出及必要的原始证据中;旧 Agent 执行和历史回看不直接读取被后续处理替换的修正投影。

工作台事件流只发布回放控制状态变化、故障与业务告警;不为每个测点值发布 SSE,也不把全部原始数据放入事件日志。未来若新增 scada.replay.changed 主题,只用于控制状态,并需同步主题模型、约束和消费契约;当前控制进度由轮询读取。

未读取数据所在 raw 分片摘要变化时停止该回放,要求新数据集版本;本轮 cleaned_value 更新不计作源版本漂移。空检查批次也需要保存处理阶段,记录数为零不代表可以绕过缺测判断。

每个回放同一时刻只有一个有效处理租约,采用数据库会话锁阻止两个请求重复推进同一周期。每个批次提交后再读取下一原始时间戳,不持久保存进程计时器值。

源数据和相关回放证据在本次演示中不自动过期;一个月是数据覆盖范围,不是保存期限。事件结束、暂停或重播不清理已有证据。生产保留策略单独制定。

Agent 调查与时间隔离

主动单步在边界形成组合监测回执:一般关注和数据不足只保存回执,只有 suspected_burst 进入 burst_report。SCADA 演示请求只负责建立持久调查任务,不要求先生成一条合成 test_data,也不在请求内执行 Agent。

告警事实与“待建立调查”意图在事件事务中保存。创建调查失败时后台对账重试;组合监测调查的来源稳定标识使用 scada_replay:<replay_id>:monitor:<period_end>:<rule_version>,一般事件继续使用 scada_replay:<replay_id>:event:<event_id>,均沿用现有外部来源幂等约束。事件不因 Agent 不可用而丢失。

已新增 scada_anomaly 工作流与对应 Skill,要求先核验数据质量、点位和单位,再读取事件窗口及正常参考。疑似爆管复用现有 burst_report Skill;它会区分侦测、定位候选和现场确诊,并要求漏量依据。run_burst_location 在回放任务中通过冻结输出适配执行,其他未适配计算继续拒绝;不能仅修改 Skill 文本或 MCP allowlist 绕过知识截止。重计算沿用 后台计算队列,保留外层 HTTP 限制。

新增 Server 持有的回放查询上下文:replay_id、数据集版本、知识截止时刻 knowledge_cutoff、已接收批次上限、处理版本和来源事件。它与任务及执行代次绑定,不能由模型或客户端自行扩大。

每代调查开始时冻结其可用批次与时间上限;同一执行中后续回放推进不会偷偷改变证据。确有新的重要证据时,按现有 调查轮次规则 创建或合并后续调查轮次,而不是每批次追加一次模型执行。

时间隔离必须覆盖 MCP 的 SCADA 查询、要素历史、任务数据快照、外围观测,以及检测算法内部读取。raw 源读取 API 只允许读取不晚于模拟时钟的观测;检测只使用已完成本轮清洗的批次结果,原始表格可以显示本轮已读取且尚在清洗的 raw。历史回看和旧代调查读取对应固定版本或原始记录引用,异常证据使用已经固化的必要片段。即使 TSDB 存有整个月记录,也必须按本轮读取检查点、设备集合和知识截止时间过滤,不能只在提示词中要求遵守。

所有回放调查证据保留真实来源:合成数据仍为 synthetic,历史回放有明确标识,不能因为读取的是正式表就改称现场实时观测。普通历史数据管理仍可按原权限查看数据集,但不能作为回放会话绕过截止时间的途径。

回放调查只生成演示分析、计划和报告;真实外围工单和现场控制不属于自动回放动作。Server 动作执行边界必须识别回放上下文,不能仅靠 Agent 提示阻止将演示事件发成真实业务工单。

接口与界面

以下为已经加入 Server OpenAPI 的路径,统一使用 Bearer 与 X-Project-Id。保留原有 /peripheral/api/sample/v1 工单与手工演练接口,回放不增加一层外围代理。

接口 语义
GET /api/v1/scada-replays/capabilities 回放表可用性、写权限、监测频率、爆管组合监测角色是否就绪及最近一轮状态;不代表完整月度数据就绪
POST /api/v1/scada-replays 固定区间、设备、规则和质量量程,返回 ready;携带稳定 request_id
GET /api/v1/scada-replays/{replay_id} 权威控制、时间、计数、设备与固定规则、当前输出及故障
POST /api/v1/scada-replays/{replay_id}/commands play、pause、step、set_speed、stop、retry;request_id + expected_revision
GET /api/v1/scada-replays/{replay_id}/observations?through=... 仅本轮已读取时间范围内的历史快照,每设备最近一条、最多 500 个
GET /api/v1/timeseries/scada-readings/raw 当前项目 TSDB 原始值联合游标分页;不插入、不隐式清洗
GET /api/v1/scada-replays/{replay_id}/events?offset=0&limit=100 检测变化与调查关联,返回 items、next_offset

raw API 允许参数为 start_time、end_time、device_ids、cursor、limit;回放请求携带经服务端验证的 replay_id,任务请求由已有绑定解析回放身份。有效范围为所选区间与模拟时间上限的交集,不能因省略 replay_id 绕过已绑定的任务限制。返回 items(time、device_id、monitored_value、quality)、next_cursor 和 scanned_through;本版不返回尚未准备的 dataset_revision,不返回 corrected 作为源输入。

next_cursor 绑定项目、设备、查询区间并固定本轮分页时间上限;本版不为整个查询提供 MVCC 源版本快照,按 time、device_id 排序,每页最多 500 条。读取窗口未结束但当前页为空时,scanned_through 表示已经确认没有观测的区间上限,供后台登记空检查批次。分页、刷新和网络重试均是无副作用读取。

查询和检查点更新使用类型化服务方法及已验证项目上下文。清洗函数接受已经读入的批次,不再调用当前会重新查询 raw 的整段清洗入口;检测函数接受这批已提交清洗结果。页面的单步 POST 命令同步完成一个监测周期,GET 预览和回看不启动任务。

回放读取要求 scada.view,创建及控制要求 scada.clean;普通 raw API 沿用 timeseries SCADA 路由的读权限。API 不自动创建表,不回退默认项目,也不隐式枚举全部活跃项目。

前端 SCADA 页签按以下顺序组织:

  1. 选定演示区间:开始时间、结束时间和项目监测步长;设备范围、规则及基线收纳在折叠的检测参数区域。
  2. FM 调频式时间轴:局部玻璃刻度窗、固定红色指示线、两侧渐暗及中央放大;按 5 分钟时间戳定位,不读取逐测点列表,也不触发监测。
  3. 主动监测操作:以“开始并监测”或“下一步并监测”为唯一主操作,按项目频率同步处理一个周期。
  4. 本周期结论:显示正常、关注、数据不足或疑似爆管,以及是否已经进入爆管调查与定位流程。

设备目录、raw 与修正值明细、完整事件列表和手工单点异常录入不在演示主面板展示。底层数据与 API 继续保留,供诊断、追溯和调查任务按权限读取。

沿用现有 workbench-shadcn-surface、日期控件和实体表面,调频刻度窗采用上文明确的局部玻璃例外;时间轴支持键盘操作和中文可访问名称。拖动回看不触发后台写操作。

页面可见时每 5 秒读取一次权威控制快照;单步请求执行期间由操作本身返回最终快照。页面隐藏时停止界面采样。演示面板不轮询完整事件列表,调查入口需要时再按分页 API 查询。

实施范围与验证

这是跨 Server、供水前端和 Agent 契约的变更,不新增后台进程。数据、存储、身份及网络复用现有部署。

仓库 主要落点
Server app/api/v1/endpoints/scada_replay.py 的回放控制与能力;app/services/ 和 app/infra/db/ 的批次、质量和事件;现有 task_alert、operational_task、MCP 上下文、查询和 Skill
Server 契约与存储 resources/sql/ 的 BizDB 两张回放表迁移;contracts/ 的 raw 读取、Server OpenAPI 和清单;外围协议不变;TSDB 沿用已有观测表
前端 src/features/workbench/peripheral-sample/ 的回放控制与状态 hook;SCADA 客户端与生成契约;告警来源和事件解析
Agent src/product/ 的生成契约及任务上下文适配,保留已有原生 MCP 与执行权限边界

验收用例必须覆盖:

  • 5/15 分钟设备混合、乱序源记录、同一时间戳跨页、空片段、结束边界和多次异常。
  • 重复批次、相同编号不同内容、响应丢失、请求中断和恢复重试;不漏读、不重复事件。
  • GET 重试、分页重复请求、清洗输出预存后中断、TSDB 更新提交而 BizDB 阶段未更新、检测失败;逐阶段恢复,不提前检测。
  • 正常处理每批只读一次 raw,清洗后检测复用结果;回放不执行 raw INSERT,不在读取时清空旧 corrected,空数据不伪造观测。
  • 周期内多批次单步、重复单步拒绝、并发请求互斥和尾步完成状态。
  • 5 分钟时间定位与 15 分钟监测周期相互独立、非整点起点、周期配置变更不影响当前回放、尾步不足完整周期和未配置时禁止开始。
  • 每个步长边界只产生一份幂等监测回执;正常、数据不足、单指标关注和疑似爆管分别处理,不为正常周期创建 Agent。
  • 爆管侦测基线不足、压力下降、出厂流量变化、设备角色错误、指标方向冲突和多指标同时命中;只有疑似爆管建立 burst_report。
  • 回放 MCP 侦测与定位只能读取固定批次和知识截止前数据;定位缺少漏量依据时停止在待补充,不调用算法。
  • 选定小时区间、跨日区间和再次演示;刻度与进度只对应选定区间,基线初始化不释放旧事件,到终点不自动播放整月。
  • FM 红线固定、刻度带移动、拖动回看不重复投递、未来位置不泄露读数;桌面与 375px、320px 窄屏下的长日期、键盘、减少动态效果及玻璃回退。
  • 真实突降不被当作采集误差修掉;负流量、零水质值、空值、错误单位和缺测恢复分别处理。
  • 首个异常、持续异常、可信恢复、再次异常及两个事件组并发;不会逐点创建调查。
  • 缺少基线、缺少水质指标配置、历史 corrected 无可用时间,以及 source_revision 变化。
  • MCP、任务数据接口及算法内部的未来查询均被拒绝;下一代调查才能使用新增证据。
  • 合成来源、历史回放来源、事件真值隔离;回放不发布真实现场动作。
  • 合成测试链路、真实项目 API、常驻后台、真实 Agent 执行和浏览器分别记录验证结果。

后续实现的验证命令从各仓库执行:Server 运行新增回放、批次、质量、事件与查询边界的聚焦 pytest,并保留现有 SCADA/告警/调查回归;前端运行 pnpm lint、pnpm typecheck、pnpm test 和聚焦 pnpm test:browser;Agent 修改后运行 bun run check 及对应聚焦测试。具体已执行结果见下方,不把待部署链路计为通过。

数据库应用遵守 工作台数据库变更门禁:先核对目标项目,备份和回滚演练,再按明确范围应用及验收;不得默认向全部项目写入。本地 MetaDB 已对明确的演示项目应用并复验 033_metadata_project_settings.sql,其他项目保持原值;tjwater2_v2 BizDB 已应用 031_scada_replays.sql,不据此推断其他项目已迁移。本次没有生成一个月观测或启动常驻进程。

回退应用前暂停新批次读取,核对 clean_prepared、cleaned 与 detected 阶段,保留原始观测、已提交修正值、回放表、告警与调查证据;升级恢复后从原阶段继续。BizDB 流程记录和 TSDB 修正值不在同一事务,回滚应用不能假定另一库结果也被撤回。不能通过删除事件、重置时间游标或清空 SCADA 原始表来伪装成从未处理。若需要撤回数据库变更,另按备份与新增业务写入情况制定恢复步骤,不在仍有写入时覆盖数据库。

本方案依赖的关键前提是源历史能提供稳定设备身份、可信时间戳和固定数据版本。缺少完整月度观测时,能力接口只能声明实际可用范围;缺少修正来源时采用 raw;缺少检测参数时该类检测不启用。任何这些缺口都不能由时间轴、插值或预设事件答案掩盖。

部署准备与验证记录

迁移脚本为 031_scada_replays.sql,显式限制物理数据库 tjwater_v2 与 tjwater2_v2,依赖既有 peripheral_sample schema、告警及调查结构。仅新增两张 BizDB 表,不新增 TSDB 观测表。tjwater2_v2 已应用该迁移并配置 settings.scada_monitoring_interval_seconds=900。

真实数据库验证使用 SCADA_REPLAY_STORAGE_TEST=1 conda run -n server python -m pytest tests/integration/test_scada_replay_storage.py -q。该测试在项目路由选出的 BizDB 与 TSDB 双回滚事务中临时建表、写测试观测,覆盖真实 SQL 的 cleaned 更新、事件持久化和主动周期推进,退出全部回滚;它不证明身份认证 HTTP 和真实 Agent 推理已经验收。浏览器测试以 mock Server 验证交互与窄屏布局,不能作为现场检测效果证明。

本轮验证结果(2026-11-03,当前工作区):

层级 结果与边界
Server 主动监测、条件式爆管调查、冻结定位适配、MCP、项目配置、raw、处理及 OpenAPI 聚焦回归共 120 项通过
数据库与契约 本地 MetaDB 的 033 迁移已单项目应用并重复执行验证;OpenAPI 279 条路径与清单摘要一致;本轮未运行 031 的真实双库集成测试
前端 lint、typecheck、全量 148 文件共 884 项 Vitest 通过;设备列表、手工单点演练及对应触发适配已移除
浏览器 1 项 mock Server 场景通过,包含 5 分钟刻度、选择值与键盘步进、连续拖动轨迹、15 分钟主动监测、中央放大、两侧渐暗、固定红针及 1280/375/320px 布局;未作为真实检测效果证明
Agent 同步 Server OpenAPI 并生成 server-api.d.ts,bun run check 通过;无真实模型推理
仓库 Server、供水前端、Agent 的 git diff --check 通过;未提交、推送或部署

生产联调仍需核验真实 HTTP 接口、调查执行链路和 Agent 报告,并单独准备完整月度数据及事件真值。不能将以上局部测试解释为这些环节已经完成。