import { SCADA_FIELDS, type ElementType } from "../core/constants.js"; import { CliError } from "../core/errors.js"; import { emitApi, requestAllPages } from "../core/http.js"; import { fieldsFor, optionalNumber, optionalString, parseOptions, requiredString, requiredStringArray, validateChoice } from "../core/options.js"; import { success } from "../core/output.js"; import { parseTime } from "../core/time.js"; import type { HandlerMap, RuntimeContext } from "../core/types.js"; function backendElementType(type: ElementType): "link" | "node" { return type === "pipe" ? "link" : "node"; } function rangeGet(ctx: RuntimeContext, argv: string[], summary: string, path: string): Promise { const { values } = parseOptions(argv); return emitApi(ctx, summary, { method: "GET", path, params: { start_time: parseTime(requiredString(values, "start-time"), "--start-time"), end_time: parseTime(requiredString(values, "end-time"), "--end-time") }, requireProject: true, }); } function realtimeByIdTime(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv); const type = validateChoice(requiredString(values, "type"), ["pipe", "junction"] as const, "--type"); return emitApi(ctx, "读取实时模拟数据成功", { method: "GET", path: "/timeseries/realtime/simulation-results", params: { id: requiredString(values, "id"), type: backendElementType(type), query_time: parseTime(requiredString(values, "time"), "--time") }, requireProject: true, }); } function realtimeByTimeProperty(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv); const type = validateChoice(requiredString(values, "type"), ["pipe", "junction"] as const, "--type"); return emitApi(ctx, "读取实时属性聚合数据成功", { method: "GET", path: "/timeseries/realtime/records", params: { type: backendElementType(type), query_time: parseTime(requiredString(values, "time"), "--time"), property: validateChoice(requiredString(values, "property"), fieldsFor(type), "--property") }, requireProject: true, }); } function analysisSeries(ctx: RuntimeContext, argv: string[], type: ElementType): Promise { const { values } = parseOptions(argv); const runId = requiredString(values, "run-id"); const idOption = type === "pipe" ? "link" : "node"; const elementId = requiredString(values, idOption); const elementPath = type === "pipe" ? "links" : "nodes"; return emitApi(ctx, type === "pipe" ? "读取分析管道字段成功" : "读取分析节点字段成功", { method: "GET", path: `/timeseries/analysis/runs/${encodeURIComponent(runId)}/${elementPath}/${encodeURIComponent(elementId)}`, params: { field: validateChoice(requiredString(values, "field"), fieldsFor(type), "--field"), start_time: parseTime(requiredString(values, "start-time"), "--start-time"), end_time: parseTime(requiredString(values, "end-time"), "--end-time"), }, requireProject: true, }); } function analysisValues(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv); const type = validateChoice(requiredString(values, "type"), ["pipe", "junction"] as const, "--type"); return emitApi(ctx, "读取分析运行指定时刻结果成功", { method: "GET", path: `/timeseries/analysis/runs/${encodeURIComponent(requiredString(values, "run-id"))}/values`, params: { result_time: parseTime(requiredString(values, "time"), "--time"), element_type: backendElementType(type), field: validateChoice(requiredString(values, "field"), fieldsFor(type), "--field"), }, requireProject: true, }); } function scadaQuery(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { "device-id": "repeat" }); const params: Record = { device_ids: requiredStringArray(values, "device-id").join(","), start_time: parseTime(requiredString(values, "start-time"), "--start-time"), end_time: parseTime(requiredString(values, "end-time"), "--end-time"), }; const field = optionalString(values, "field"); if (field) params.field = validateChoice(field, SCADA_FIELDS, "--field"); return emitApi(ctx, "读取 SCADA 时序成功", { method: "GET", path: field ? "/timeseries/scada-readings/fields" : "/timeseries/scada-readings", params, requireProject: true }); } function composite(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { feature: "repeat", "use-cleaned": "boolean" }); const kind = validateChoice(requiredString(values, "kind"), ["scada-simulation", "element-simulation", "element-scada"] as const, "--kind"); const params: Record = { start_time: parseTime(requiredString(values, "start-time"), "--start-time"), end_time: parseTime(requiredString(values, "end-time"), "--end-time"), }; const runId = optionalString(values, "run-id"); if (runId && kind !== "element-scada") params.run_id = runId; if (kind === "scada-simulation") params.device_ids = requiredStringArray(values, "feature").join(","); else if (kind === "element-simulation") params.feature_infos = requiredStringArray(values, "feature").join(","); else { const feature = requiredStringArray(values, "feature"); if (feature.length !== 1) throw new CliError("CLI 参数错误", "FEATURE_REQUIRED", "element-scada requires exactly one --feature as element_id", 2); params.element_id = feature[0]; params.use_cleaned = Boolean(values["use-cleaned"]); } const paths = { "scada-simulation": "/timeseries/views/scada-simulations", "element-simulation": "/timeseries/views/element-simulations", "element-scada": "/timeseries/views/element-scada-readings", } as const; return emitApi(ctx, kind === "scada-simulation" ? "读取复合 SCADA-模拟数据成功" : kind === "element-simulation" ? "读取复合元素模拟数据成功" : "读取元素关联 SCADA 数据成功", { method: "GET", path: paths[kind], params, requireProject: true }); } function pipelineHealth(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv); return emitApi(ctx, "读取管道健康预测成功", { method: "GET", path: "/pipeline-health-predictions", params: { query_time: parseTime(requiredString(values, "time"), "--time") }, requireProject: true, }); } function dataScadaGet(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv); return emitApi(ctx, "读取 SCADA 设备成功", { method: "GET", path: "/scada-devices/detail", params: { device_id: requiredString(values, "device-id") }, requireProject: true }); } async function dataScadaList(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { "page-size": "integer" }); const pageSize = Math.min(1000, Math.max(1, optionalNumber(values, "page-size") ?? 1000)); const [data, durationMs] = await requestAllPages( ctx, { method: "GET", path: "/scada-devices", requireProject: true }, pageSize, ); success("读取 SCADA 设备列表成功", data, ctx, durationMs); } export const dataHandlers: HandlerMap = { "data timeseries realtime links": (ctx, argv) => rangeGet(ctx, argv, "读取实时管道数据成功", "/timeseries/realtime/links"), "data timeseries realtime nodes": (ctx, argv) => rangeGet(ctx, argv, "读取实时节点数据成功", "/timeseries/realtime/nodes"), "data timeseries realtime simulation-by-id-time": realtimeByIdTime, "data timeseries realtime simulation-by-time-property": realtimeByTimeProperty, "data timeseries analysis link-field": (ctx, argv) => analysisSeries(ctx, argv, "pipe"), "data timeseries analysis node-field": (ctx, argv) => analysisSeries(ctx, argv, "junction"), "data timeseries analysis values": analysisValues, "data timeseries scada query": scadaQuery, "data timeseries composite": composite, "data pipeline-health": pipelineHealth, "data scada get": dataScadaGet, "data scada list": dataScadaList, "data scada schema": (ctx) => emitApi(ctx, "读取 SCADA 设备 schema 成功", { method: "GET", path: "/network-schemas/scada-device", requireProject: true }), };