Files
TJWaterAgent/cli/src/commands/analysis.ts
T
jiang a438433068 refactor(cli)!: align commands with analysis run APIs
Replace legacy scheme commands with run-id based analysis and timeseries queries. Update sensor placement and SCADA routes, and refresh CLI help, tests, and usage guidance.

BREAKING CHANGE: legacy scheme, risk, and valve-close CLI command paths are removed.
2026-08-26 17:28:41 +08:00

173 lines
8.4 KiB
TypeScript

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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
const { values } = parseOptions(argv, { duration: "integer", concentration: "number" });
const params: Record<string, unknown> = {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
const { values } = parseOptions(argv, { "burst-leakage": "number", "pressure-scada-id": "repeat", "flow-scada-id": "repeat", "use-scada-flow": "boolean" });
const body: Record<string, unknown> = {
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,
};