feat(workbench): expand SCADA monitoring and investigation presentation

This commit is contained in:
2026-11-07 19:50:14 +08:00
parent 0801e161ec
commit ba71e2d529
133 changed files with 6754 additions and 1438 deletions
@@ -0,0 +1,78 @@
import { INVESTIGATION_DEV_NETWORK } from "./investigation-dev-network";
import type { AnswerUiBlock } from "@/features/agent";
import { createPresentationRecord, createUiRecord, serializeInvestigationDevJsonl } from "./investigation-dev-preview";
export const complexInvestigationDevCases = [
{ id: "complex_investigation", label: "复杂调查:多图多表联动", expected: "3 张图、2 张表与合成覆盖面;可同时放大对比,单位分别展示,不查询真实要素。" },
{ id: "complex_tables", label: "复杂表格:宽表、长文本与边界数据", expected: "50 行 8 列宽表、空表和含零值、负数、重复值的对照表;检查搜索、数值排序和横向滚动。" },
{ id: "complex_charts", label: "复杂图表:多测点与长时序", expected: "96 个时间点、8 条压力序列,另有流量与方案柱状图;可切换数据表、筛选区间并独立放大。" }
] as const;
type ComplexCaseId = (typeof complexInvestigationDevCases)[number]["id"];
type Node = AnswerUiBlock["spec"]["elements"][string];
const ui = (id: string, node: Node) => createUiRecord({
version: "answer-ui@1", block_id: `dev-complex-${id}`, phase: "final",
fallback_text: "本地合成展示数据,不代表实际监测、调查结论或现场方案。",
spec: { root: "result", elements: { result: node } }
});
const table = (id: string, title: string, columns: string[], rows: string[][]) =>
ui(id, { type: "Table", props: { title, columns, rows }, children: [] });
const times = Array.from({ length: 96 }, (_, index) => `${String(Math.floor(index / 4)).padStart(2, "0")}:${String(index % 4 * 15).padStart(2, "0")}`);
const pressure = {
title: "合成全天压力对比", chart_type: "line" as const, x_data: times,
series: Array.from({ length: 8 }, (_, series) => ({
name: `合成节点 ${INVESTIGATION_DEV_NETWORK.nodeAnchors[series].id} · ${series < 4 ? "主干管" : "末端支线"}压力水头(m)`,
data: times.map((_, index) => Number((34 + series * 0.6 + Math.sin(index / 7 + series) * 2.4 - (index >= 32 && index < 44 ? 7 - series * 0.4 : 0)).toFixed(2)))
}))
};
const flow = {
title: "合成分区流量趋势", chart_type: "line" as const, x_data: times,
series: Array.from({ length: 4 }, (_, series) => ({ name: `合成分区 ${series + 1} 流量(m³/h)`,
data: times.map((_, index) => Number((120 + series * 28 + Math.sin(index / 9 + series) * 24 + (index >= 32 && index < 44 ? 38 : 0)).toFixed(1))) }))
};
const comparison = {
title: "合成候选方案影响对比", chart_type: "bar" as const,
x_data: ["合成方案 A:局部隔离", "合成方案 B:扩大核查", "合成方案 C:保持供水", "合成方案 D:分段验证", "合成方案 E:待补充资料"],
series: [{ name: "合成影响用户数(户)", data: [245, 610, 0, 128, 72] }]
};
const chart = (id: string, value: typeof pressure | typeof flow | typeof comparison) =>
ui(id, { type: "Chart", props: { ...value, source: "本地确定性合成数据,仅供展示测试;曲线变化和方案名称均非实际分析结果。" }, children: [] });
const columns = ["序号", "管段 ID", "合成片区", "长度(m)", "压力变化(m)", "影响户数(户)", "核查状态", "核查说明"];
const rows = Array.from({ length: 50 }, (_, index) => [
String(index + 1), INVESTIGATION_DEV_NETWORK.pipeIds[index],
`合成片区 ${index % 5 + 1} · ${index % 2 ? "学校与居民区交界" : "工业园区及长距离末端支线"}`,
String(80 + index * 13.5), (index % 7 === 0 ? 0 : -(index % 13) / 2).toFixed(1),
String(index % 6 === 0 ? 0 : (index * 37) % 600),
["待现场核验", "资料待补充", "合成候选,未确认"][index % 3],
index % 5 === 0
? `仅供本地测试:样例记录 ${String(index + 1).padStart(3, "0")} 包含较长的核查说明,用于检查换行、滚动、筛选及放大后阅读。请分别核对供水边界、阀门状态与现场回执。合成引用 SYNTHETIC_REFERENCE_${"LONG_SEGMENT_".repeat(8)}${index + 1},不是真实证据。`
: `仅为合成候选资料,样例记录 ${String(index + 1).padStart(3, "0")};不代表已确认故障或已执行操作。`
]);
export function createComplexInvestigationDevCase(caseId: ComplexCaseId, [longitude, latitude]: [number, number]): string {
if (caseId === "complex_charts") return serializeInvestigationDevJsonl([
chart("pressure", pressure), chart("flow", flow), chart("comparison", comparison)
]);
if (caseId === "complex_tables") return serializeInvestigationDevJsonl([
table("wide-table", "合成候选核查宽表", columns, rows),
table("empty-table", "合成空结果表", ["对象", "核查结果", "说明"], []),
table("edge-table", "合成排序与缺口对照", ["对象", "变化量", "完成率", "说明"], [
["合成对象 A", "0", "0%", "零值不是缺失"], ["合成对象 B", "-2.5", "50%", "负数与百分比排序"],
["合成对象 C", "10", "100%", "按数值排列,不按文本排列"], ["合成对象 D", "2", "50%", "重复值保持可读"],
["合成对象 E", "2", "50%", "重复值第二行"], ["合成对象 F", "未提供", "未提供", "缺失不替换为零"]
])
]);
const point = (dx: number, dy: number): [number, number] => [Math.max(-180, Math.min(180, longitude + dx)), Math.max(-89.99, Math.min(89.99, latitude + dy))];
return serializeInvestigationDevJsonl([
createPresentationRecord({ action: "set_overlay", mode: "geometry", overlay_id: "dev-complex-coverage", title: "合成分区覆盖示意", evidence_id: "dev:synthetic",
data: { type: "FeatureCollection", features: [-0.002, 0.002].map((dx, index) => ({ type: "Feature", properties: { project_code: INVESTIGATION_DEV_NETWORK.projectCode, origin: "synthetic", label: `合成片区 ${index + 1}` },
geometry: { type: "Polygon", coordinates: [[point(dx - 0.001, -0.001), point(dx + 0.001, -0.001), point(dx + 0.001, 0.001), point(dx - 0.001, 0.001), point(dx - 0.001, -0.001)]] } })) } }),
chart("pressure", pressure), chart("flow", flow),
createPresentationRecord({ action: "open_chart", chart_id: "dev-complex-plans", evidence_refs: ["dev:synthetic"], chart: comparison }),
table("candidates", "合成候选方案核查", columns, rows.slice(0, 24)),
table("valves", "合成阀门核查清单", ["阀门", "合成片区", "位置描述", "核查要求", "当前状态"],
INVESTIGATION_DEV_NETWORK.valveIds.map((id, index) => [id, `合成片区 ${index % 3 + 1}`, "合成居民区与主干管交界处,位置尚未现场核验", "核对可操作性、实际开度及现场回执;不执行关阀", "待核查"]))
]);
}