feat(scada): add project monitoring replay
Generic Container CI/CD / test-build-publish (push) Successful in 3m39s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m39s

This commit is contained in:
2026-11-04 14:27:21 +08:00
parent f25423dec4
commit 26fa5d1909
53 changed files with 5153 additions and 20 deletions
@@ -19,13 +19,18 @@ CREATE TABLE IF NOT EXISTS projects (
gs_workspace VARCHAR(100) UNIQUE NOT NULL,
map_extent JSONB,
map_config JSONB NOT NULL DEFAULT '{}'::jsonb,
settings JSONB NOT NULL DEFAULT '{}'::jsonb,
status VARCHAR(20) DEFAULT 'active' NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
CONSTRAINT projects_status_check CHECK (status IN ('active', 'inactive', 'archived'))
CONSTRAINT projects_status_check CHECK (status IN ('active', 'inactive', 'archived')),
CONSTRAINT projects_settings_object_check CHECK (jsonb_typeof(settings) = 'object')
);
ALTER TABLE projects ADD COLUMN IF NOT EXISTS map_config JSONB NOT NULL DEFAULT '{}'::jsonb;
ALTER TABLE projects ADD COLUMN IF NOT EXISTS settings JSONB NOT NULL DEFAULT '{}'::jsonb;
COMMENT ON COLUMN projects.settings IS
'项目级通用配置(JSON 对象,不存放密钥);scada_monitoring_interval_seconds 为监测任务间隔秒数。';
COMMENT ON COLUMN projects.map_config IS
'项目级地图配置(JSON);place_search 保存地名搜索启用状态、服务商、行政区编码及区域名称;保留既有图例与坐标参考配置。';
+46
View File
@@ -0,0 +1,46 @@
BEGIN;
DO $$ BEGIN
IF current_database() NOT IN ('tjwater_v2', 'tjwater2_v2') THEN
RAISE EXCEPTION '031_scada_replays requires an explicitly verified SCADA demo project';
END IF;
END $$;
CREATE TABLE IF NOT EXISTS peripheral_sample.scada_replays (
project_id uuid NOT NULL,
replay_id uuid NOT NULL,
request_id uuid NOT NULL,
status text NOT NULL CHECK (status IN ('ready','paused','completed','stopped','failed')),
document jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (project_id,replay_id),
UNIQUE (project_id,request_id)
);
CREATE INDEX IF NOT EXISTS scada_replays_status_idx
ON peripheral_sample.scada_replays(project_id,status,created_at DESC);
CREATE TABLE IF NOT EXISTS peripheral_sample.scada_replay_batches (
project_id uuid NOT NULL,
replay_id uuid NOT NULL,
sequence bigint NOT NULL,
stage text NOT NULL CHECK(stage IN ('selected','clean_prepared','cleaned','detected')),
document jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY(project_id,replay_id,sequence),
FOREIGN KEY(project_id,replay_id) REFERENCES peripheral_sample.scada_replays(project_id,replay_id)
);
COMMENT ON TABLE peripheral_sample.scada_replays IS 'SCADA 演示回放的权威控制状态与处理检查点,不保存正式原始观测';
COMMENT ON COLUMN peripheral_sample.scada_replays.project_id IS '经认证与路由校验的所属项目';
COMMENT ON COLUMN peripheral_sample.scada_replays.replay_id IS '独立回放身份,不等同 Agent 执行编号';
COMMENT ON COLUMN peripheral_sample.scada_replays.request_id IS '创建请求的稳定幂等编号';
COMMENT ON COLUMN peripheral_sample.scada_replays.status IS '供领取和约束使用的 document.status 查询投影';
COMMENT ON COLUMN peripheral_sample.scada_replays.document IS '冻结配置、设备快照、时钟、检查点、规则状态和命令回执';
COMMENT ON COLUMN peripheral_sample.scada_replays.created_at IS '实际创建时间';
COMMENT ON COLUMN peripheral_sample.scada_replays.updated_at IS '实际控制或进度更新时间';
COMMENT ON TABLE peripheral_sample.scada_replay_batches IS 'SCADA raw 引用、清洗输出及检测阶段的恢复凭证,禁止重写 raw';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.project_id IS '所属项目';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.replay_id IS '所属回放';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.sequence IS '回放内单调批次序号';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.stage IS '已提交处理阶段';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.document IS '观测键、raw 摘要、确定的清洗结果与检测事件';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.created_at IS '实际登记批次时间';
COMMIT;
@@ -0,0 +1,54 @@
BEGIN;
DO $$
BEGIN
IF to_regclass('public.project_databases') IS NULL
OR to_regclass('public.user_project_membership') IS NULL THEN
RAISE EXCEPTION '033_metadata_project_settings must run in the project metadata database';
END IF;
END $$;
ALTER TABLE public.projects
ADD COLUMN IF NOT EXISTS settings jsonb NOT NULL DEFAULT '{}'::jsonb;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conrelid = 'public.projects'::regclass
AND conname = 'projects_settings_object_check'
) THEN
ALTER TABLE public.projects
ADD CONSTRAINT projects_settings_object_check
CHECK (jsonb_typeof(settings) = 'object');
END IF;
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'projects'
AND column_name = 'scada_monitoring_interval_seconds'
) THEN
IF EXISTS (
SELECT 1 FROM public.projects
WHERE scada_monitoring_interval_seconds IS NOT NULL
AND settings ? 'scada_monitoring_interval_seconds'
AND settings->'scada_monitoring_interval_seconds'
IS DISTINCT FROM to_jsonb(scada_monitoring_interval_seconds)
) THEN
RAISE EXCEPTION 'Conflicting SCADA monitoring intervals; resolve before migration';
END IF;
UPDATE public.projects
SET settings = settings || jsonb_build_object(
'scada_monitoring_interval_seconds', scada_monitoring_interval_seconds
)
WHERE scada_monitoring_interval_seconds IS NOT NULL;
ALTER TABLE public.projects DROP COLUMN scada_monitoring_interval_seconds;
END IF;
END $$;
COMMENT ON COLUMN public.projects.settings IS
'项目级通用配置(JSON 对象,不存放密钥);scada_monitoring_interval_seconds 为监测任务间隔秒数。';
COMMIT;
@@ -2,7 +2,7 @@
name: burst-report
description: 调查供水爆管上报,形成有依据的候选位置、隔离阀门、影响范围和后续处置建议。
metadata:
version: "2.0.6"
version: "2.0.8"
---
# 爆管上报处置
@@ -13,6 +13,8 @@ metadata:
读取当前任务、事件时刻、地点、来源、已有报告、证据、动作和反馈。新一轮优先检查新增事实和上轮未决项,按时间、来源、对象及输入比较新旧结果,不按 ID 推断先后,也不因原工单未更新就断言没有新证据。保留本流程名称和版本。
若任务来源带有 `replay_context`,这是 SCADA 历史回放中的疑似爆管演示。先读取任务摘要中的步长监测结论,再用 `query_scada_readings` 核对冻结批次;不得查询知识截止之后的数据。回放中的 `run_burst_location` 只接受 monitoring 模式,并要求显式提供本轮已处理范围内的异常窗和正常对照窗;Server 会从冻结清洗输出构造计算输入。缺少有依据的 `burst_leakage` 时保留“定位待补充”,不得用出厂流量增量、压力下降或默认值替代。回放结果保持 synthetic,不发布真实工单或设备动作。
## 定位与影响分析
根据现有线索自主选择必要步骤:
@@ -26,9 +28,19 @@ metadata:
查询错误、权限拒绝或接口故障不是缺数证据。必要分析无法执行时,说明失败步骤和已经完成的部分,不借有限摘要宣布调查完成。
## 事件前后影响说明
每轮按实际证据说明以下阶段的影响,给出结果或具体限制;尚未发生的处置列为预测和待验证项:
1. **事件前与爆管后、隔离前**:核对正常基线和异常窗口,说明压力、流量变化及已覆盖的异常点位、资产和区域。候选管段及破口面积有依据时,可用 `run_burst_simulation` 补充情景水力影响,标明假设。漏量估计须说明边界流量、正常用水及储量变化等依据;有可靠漏量序列和时间覆盖时可累计漏水量,保留单位、方法和不确定性。定位工具的 `burst_leakage` 是输入,不能当成反演所得漏量。
2. **关阀隔离方案**:说明止漏目标、直接隔离节点和关阀后新增失去水源连接的节点,分别保留 `affected_nodes` 与 `supply_affected_nodes` 及其计数。结合关阀水力结果说明预计停水、低压或供水不足的范围和覆盖限制;有用户关联资料时说明受影响用户、重要设施及保障需求。拓扑失源、模型预测和现场实际停水分别标注,不把节点数当户数。
3. **实际隔离与恢复供水后**:后续轮次按实际关阀、抢修和恢复时间读取前后监测与现场反馈,对照预测核查止漏效果、实际影响范围和恢复情况,说明持续低压、异常流量、再次异常及尚待验证的影响。核对正常基线、观察窗口和判定依据,工单状态或单个正常读数不足以证明影响消除。
比较须核对对象、单位、时间对齐、模型版本、需水与水源边界、泵阀状态及计算设置;区分实测变化与模型方案差异。关阀工具不会自动继承爆管情景,条件不同的独立计算不能直接作为同一事故前后效果。可比时给出变化量和方案差异,缺少可比基准时说明当前结果及缺口,不补造改善比例。必要的紧急核验、通知和供水保障不等待可选的完整模拟。
## 处置与报告
报告分别给出位置候选及依据、隔离阀门及可用性、替代方案、拓扑与水力影响、用户影响边界,以及处置和闭环缺口。只有初步结果时明确范围,不宣称已经定位、确诊或闭环。位置、候选、监测、影响和阀门方案按实际调查步骤同步展示,遵循后台展示规则,不用报告定位链接代替过程展示。覆盖面保留分组、缺失几何和示意边界,不能当作实际停水、水力影响或用户服务边界。无数据或展示失败时说明具体限制;展示不替代证据,也不要求等待浏览器。
报告分别给出位置候选及依据、隔离阀门及可用性、替代方案、事件前后分阶段的拓扑与水力影响、用户影响边界,以及处置和闭环缺口。影响说明保留时间窗、对象、证据、实测或预测属性及限制。只有初步结果时明确范围,不宣称已经定位、确诊或闭环。位置、候选、监测、影响和阀门方案按实际调查步骤同步展示,遵循后台展示规则,不用报告定位链接代替过程展示。覆盖面保留分组、缺失几何和示意边界,不能当作实际停水、水力影响或用户服务边界。无数据或展示失败时说明具体限制;展示不替代证据,也不要求等待浏览器。
形成具体建议后,先保存阶段结论,再提议必要动作。人工核验、通知、定位隔离、抢修、适用的清洗复测和恢复回访使用关联的 `work_order_type=general` 草稿;原爆管事件保持独立。草稿写明位置、任务、依赖、执行前核验和回执要求。没有通知适配器时明确由人工实施。设备命令仅用于真实可控目标,审批和执行由 Server 控制。
@@ -0,0 +1,18 @@
---
name: scada-anomaly
description: 调查 SCADA 回放中的异常信号,区分读数质量与运行异常,形成可追溯的演示报告。
metadata:
version: "1.0.1"
---
# SCADA 回放异常调查
先读取任务上下文和来源中的 replay_context,核对规则、设备、单位、异常时刻及知识截止时间。数据来自已存 SCADA 的演示回放,不代表新采集的现场故障。
通过 query_scada_readings 读取事件前后本轮已处理的观测;查询结束时间不得晚于 knowledge_cutoff。结合 monitored_value 与 cleaned_value 判断数值变化,不将被可靠采集的低压或水质变化当作数据错误。缺测、无效值和未完成清洗应明确记录。
报告说明事件前后影响:在已开放且已处理的观测范围内,核对异常前参考、异常期间及已有恢复观测,列出压力、流量或水质变化、持续时间、涉及测点和已核实的资产关联。只有对象、单位、时间与运行条件可比时才给出变化量;说明参考窗口、证据和覆盖限制。尚无恢复观测时列为待验证,读数恢复不能证明现场修复或用户影响消除。停水范围、关阀影响及泄漏量需要相应拓扑、水力、流量平衡或现场依据;当前证据无法支持时明确待分析,不由单点低压推定停水区、受影响户数或漏量。
当前回放调查仅开放冻结 SCADA 证据、设备与模型目录;不支持原生计算或绕过回放上限的其他数据入口。回放前历史用于后台基线初始化,当前证据接口不返回该部分。需要更多历史或进一步计算时,报告具体缺口,不能读取未来或虚构分析结果。
报告列出观测事实、规则信号、质量问题、尚未确认的原因和建议核查。压力异常不等于已确认爆管,浓度变化不等于已确认污染。回放只生成演示分析与报告,不创建真实现场工单、不下发设备命令。