import { CliError } from "../core/errors.js"; import { emitApi } from "../core/http.js"; import { assignDatasetKeys, parseBurstFile, parseValveSettingFile } from "../core/files.js"; import { optionalNumber, optionalString, optionalStringArray, parseOptions, requiredNumber, requiredString, validateChoice } from "../core/options.js"; import { resolveScheme } from "../core/runtime.js"; import { parseTime } from "../core/time.js"; import type { HandlerMap, RuntimeContext } from "../core/types.js"; function analysisBurst(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { duration: "integer" }); const [ids, sizes] = parseBurstFile(requiredString(values, "burst-file")); const schemeName = resolveScheme(ctx, optionalString(values, "scheme"), true)!; return emitApi(ctx, "爆管分析执行成功", { method: "POST", path: "/burst-analyses", params: { modify_pattern_start_time: parseTime(requiredString(values, "start-time"), "--start-time"), burst_id: ids, burst_size: sizes, modify_total_duration: requiredNumber(values, "duration"), scheme_name: schemeName, }, requireProject: true, }, ["tjwater-cli analysis runs list"]); } function analysisValveIsolation(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { element: "repeat", "disabled-valve": "repeat" }); const elements = optionalStringArray(values, "element"); if (!elements) throw new CliError("CLI 参数错误", "INVALID_VALVE_ISOLATION_ARGS", "at least one --element is required", 2); return emitApi(ctx, "阀门隔离分析执行成功", { method: "POST", path: "/valve-isolation-analyses", params: { accident_element: elements, disabled_valves: optionalStringArray(values, "disabled-valve") }, requireProject: true, }); } function analysisFlushing(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { flow: "number", duration: "integer" }); const [valves, openings] = parseValveSettingFile(requiredString(values, "valve-setting-file")); return emitApi(ctx, "冲洗分析执行成功", { method: "POST", path: "/flushing-analyses", params: { start_time: parseTime(requiredString(values, "start-time"), "--start-time"), valves, valves_k: openings, drainage_node_id: requiredString(values, "drainage-node"), flush_flow: requiredNumber(values, "flow"), duration: optionalNumber(values, "duration") || 900, scheme_name: resolveScheme(ctx, optionalString(values, "scheme"), true), }, requireProject: true, }); } function analysisAge(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { duration: "integer" }); return emitApi(ctx, "水龄分析执行成功", { method: "POST", path: "/water-age-analyses", params: { start_time: parseTime(requiredString(values, "start-time"), "--start-time"), duration: requiredNumber(values, "duration") }, requireProject: true, }); } function analysisContaminant(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { duration: "integer", concentration: "number" }); const params: Record = { start_time: parseTime(requiredString(values, "start-time"), "--start-time"), source: requiredString(values, "source-node"), concentration: requiredNumber(values, "concentration"), duration: requiredNumber(values, "duration"), scheme_name: resolveScheme(ctx, optionalString(values, "scheme"), true), }; const pattern = optionalString(values, "pattern"); if (pattern) params.pattern = pattern; return emitApi(ctx, "污染物模拟执行成功", { method: "POST", path: "/contaminant-simulations", params, requireProject: true }); } function sensorPlacementRun(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { count: "integer", "min-diameter": "integer" }); return emitApi(ctx, "传感器选址执行成功", { method: "POST", path: "/sensor-placement-runs", body: { run_name: requiredString(values, "run-name"), sensor_type: "pressure", method: validateChoice(requiredString(values, "method"), ["sensitivity", "kmeans"] as const, "--method"), sensor_count: requiredNumber(values, "count"), min_diameter: optionalNumber(values, "min-diameter") || 0, }, requireProject: true, }); } function schemeAnalysis(ctx: RuntimeContext, argv: string[], summary: string, path: string, startKey: string, endKey: string): Promise { const { values } = parseOptions(argv); return emitApi(ctx, summary, { method: "POST", path, body: { [startKey]: parseTime(requiredString(values, "start-time"), "--start-time"), [endKey]: parseTime(requiredString(values, "end-time"), "--end-time"), scheme_name: resolveScheme(ctx, optionalString(values, "scheme"), true), }, requireProject: true, }); } function analysisRunGet(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv); const runId = requiredString(values, "run-id"); return emitApi(ctx, "读取分析运行成功", { method: "GET", path: `/analysis/runs/${encodeURIComponent(runId)}`, requireProject: true, }); } function analysisRunResults(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv); const runId = requiredString(values, "run-id"); return emitApi(ctx, "读取分析结果成功", { method: "GET", path: `/analysis/runs/${encodeURIComponent(runId)}/results`, params: { result_type: optionalString(values, "result-type") }, requireProject: true, }); } function burstLocation(ctx: RuntimeContext, argv: string[]): Promise { const { values } = parseOptions(argv, { "burst-leakage": "number", "pressure-scada-id": "repeat", "flow-scada-id": "repeat", "use-scada-flow": "boolean" }); const body: Record = { scheme_name: resolveScheme(ctx, optionalString(values, "scheme"), true), data_source: optionalString(values, "data-source") || "monitoring", scada_burst_start: parseTime(requiredString(values, "start-time"), "--start-time"), scada_burst_end: parseTime(requiredString(values, "end-time"), "--end-time"), burst_leakage: requiredNumber(values, "burst-leakage"), use_scada_flow: Boolean(values["use-scada-flow"]), }; const pressureIds = optionalStringArray(values, "pressure-scada-id"); const flowIds = optionalStringArray(values, "flow-scada-id"); if (pressureIds) body.pressure_scada_ids = pressureIds; if (flowIds) body.flow_scada_ids = flowIds; const pressureFile = optionalString(values, "pressure-file"); const flowFile = optionalString(values, "flow-file"); if (pressureFile) assignDatasetKeys(body, pressureFile, ["burst_pressure", "normal_pressure"], "pressure"); if (flowFile) assignDatasetKeys(body, flowFile, ["burst_flow", "normal_flow"], "flow"); return emitApi(ctx, "爆管定位执行成功", { method: "POST", path: "/burst-locations", body, requireProject: true }); } export const analysisHandlers: HandlerMap = { "analysis burst": analysisBurst, "analysis valve isolation": analysisValveIsolation, "analysis flushing": analysisFlushing, "analysis age": analysisAge, "analysis contaminant": analysisContaminant, "analysis sensor-placement run": sensorPlacementRun, "analysis sensor-placement list": (ctx) => emitApi(ctx, "读取传感器选址运行列表成功", { method: "GET", path: "/sensor-placement-runs", requireProject: true }), "analysis sensor-placement get": (ctx, argv) => { const { values } = parseOptions(argv); return emitApi(ctx, "读取传感器选址运行成功", { method: "GET", path: `/sensor-placement-runs/${encodeURIComponent(requiredString(values, "run-id"))}`, requireProject: true }); }, "analysis runs list": (ctx) => emitApi(ctx, "读取分析运行列表成功", { method: "GET", path: "/analysis/runs", requireProject: true }), "analysis runs get": analysisRunGet, "analysis runs results": analysisRunResults, "analysis leakage identify": (ctx, argv) => schemeAnalysis(ctx, argv, "漏损识别执行成功", "/leakage-identifications", "scada_start", "scada_end"), "analysis burst-detection detect": (ctx, argv) => schemeAnalysis(ctx, argv, "爆管检测执行成功", "/burst-detections", "scada_start", "scada_end"), "analysis burst-location locate": burstLocation, };