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.
This commit is contained in:
2026-08-26 17:28:41 +08:00
parent 4ec010455b
commit a438433068
6 changed files with 215 additions and 206 deletions
+38 -55
View File
@@ -1,10 +1,9 @@
import { CliError } from "../core/errors.js";
import { emitApi, requestJson } from "../core/http.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 { success } from "../core/output.js";
import type { HandlerMap, RuntimeContext } from "../core/types.js";
function analysisBurst(ctx: RuntimeContext, argv: string[]): Promise<void> {
@@ -22,30 +21,13 @@ function analysisBurst(ctx: RuntimeContext, argv: string[]): Promise<void> {
scheme_name: schemeName,
},
requireProject: true,
}, [`tjwater-cli data scheme get --name ${schemeName}`, "tjwater-cli data scheme list"]);
}, ["tjwater-cli analysis runs list"]);
}
function analysisValve(ctx: RuntimeContext, argv: string[]): Promise<void> {
const { values } = parseOptions(argv, { valve: "repeat", element: "repeat", "disabled-valve": "repeat", duration: "integer" });
const mode = validateChoice(requiredString(values, "mode"), ["close", "isolation"] as const, "--mode");
if (mode === "close") {
const valves = optionalStringArray(values, "valve");
const startTime = optionalString(values, "start-time");
if (!startTime || !valves) throw new CliError("CLI 参数错误", "INVALID_VALVE_CLOSE_ARGS", "close mode requires --start-time and at least one --valve", 2);
return emitApi(ctx, "阀门关闭分析执行成功", {
method: "POST",
path: "/valve-isolation-analyses",
params: {
start_time: parseTime(startTime, "--start-time"),
valves,
duration: optionalNumber(values, "duration") || 900,
scheme_name: resolveScheme(ctx, optionalString(values, "scheme"), true),
},
requireProject: true,
});
}
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", "isolation mode requires at least one --element", 2);
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",
@@ -97,14 +79,16 @@ function analysisContaminant(ctx: RuntimeContext, argv: string[]): Promise<void>
return emitApi(ctx, "污染物模拟执行成功", { method: "POST", path: "/contaminant-simulations", params, requireProject: true });
}
function sensorKmeans(ctx: RuntimeContext, argv: string[]): Promise<void> {
function sensorPlacementRun(ctx: RuntimeContext, argv: string[]): Promise<void> {
const { values } = parseOptions(argv, { count: "integer", "min-diameter": "integer" });
return emitApi(ctx, "传感器选址执行成功", {
method: "POST",
path: "/pressure-sensor-placement-kmeans",
path: "/sensor-placement-runs",
body: {
scheme_name: resolveScheme(ctx, optionalString(values, "scheme"), true),
sensor_number: requiredNumber(values, "count"),
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,
@@ -125,14 +109,25 @@ function schemeAnalysis(ctx: RuntimeContext, argv: string[], summary: string, pa
});
}
function schemeList(ctx: RuntimeContext, summary: string, schemeType: string): Promise<void> {
return emitApi(ctx, summary, { method: "GET", path: "/schemes", params: { scheme_type: schemeType }, 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 schemeGet(ctx: RuntimeContext, argv: string[], summary: string, schemeType: string): Promise<void> {
const { positionals } = parseOptions(argv);
if (!positionals[0]) throw new CliError("CLI 参数错误", "MISSING_ARGUMENT", "Missing argument 'SCHEME_NAME'", 2);
return emitApi(ctx, summary, { method: "GET", path: `/schemes/${positionals[0]}`, params: { scheme_type: schemeType }, 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> {
@@ -156,34 +151,22 @@ function burstLocation(ctx: RuntimeContext, argv: string[]): Promise<void> {
return emitApi(ctx, "爆管定位执行成功", { method: "POST", path: "/burst-locations", body, requireProject: true });
}
function riskPipe(ctx: RuntimeContext, argv: string[], summary: string, path: string): Promise<void> {
const { values } = parseOptions(argv);
return emitApi(ctx, summary, { method: "GET", path, params: { pipe_id: requiredString(values, "pipe") }, requireProject: true });
}
async function riskNetwork(ctx: RuntimeContext): Promise<void> {
const [probabilities, a] = await requestJson(ctx, { method: "GET", path: "/network-pipe-risk-probability-nows", requireProject: true });
const [geometries, b] = await requestJson(ctx, { method: "GET", path: "/pipes/risk-probability-geometries", requireProject: true });
success("读取全网风险成功", { probabilities, geometries }, ctx, a + b);
}
export const analysisHandlers: HandlerMap = {
"analysis burst": analysisBurst,
"analysis valve": analysisValve,
"analysis valve isolation": analysisValveIsolation,
"analysis flushing": analysisFlushing,
"analysis age": analysisAge,
"analysis contaminant": analysisContaminant,
"analysis sensor-placement kmeans": sensorKmeans,
"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 leakage schemes list": (ctx) => schemeList(ctx, "读取漏损方案列表成功", "dma_leak_identification"),
"analysis leakage schemes get": (ctx, argv) => schemeGet(ctx, argv, "读取漏损方案详情成功", "dma_leak_identification"),
"analysis burst-detection detect": (ctx, argv) => schemeAnalysis(ctx, argv, "爆管检测执行成功", "/burst-detections", "scada_start", "scada_end"),
"analysis burst-detection schemes list": (ctx) => schemeList(ctx, "读取爆管检测方案列表成功", "burst_detection"),
"analysis burst-detection schemes get": (ctx, argv) => schemeGet(ctx, argv, "读取爆管检测方案详情成功", "burst_detection"),
"analysis burst-location locate": burstLocation,
"analysis burst-location schemes list": (ctx) => schemeList(ctx, "读取爆管定位方案列表成功", "burst_location"),
"analysis burst-location schemes get": (ctx, argv) => schemeGet(ctx, argv, "读取爆管定位方案详情成功", "burst_location"),
"analysis risk pipe-now": (ctx, argv) => riskPipe(ctx, argv, "读取当前管道风险成功", "/pipes/risk-probability-now"),
"analysis risk pipe-history": (ctx, argv) => riskPipe(ctx, argv, "读取历史管道风险成功", "/pipes/risk-probability"),
"analysis risk network": riskNetwork,
};