优化 CLI 命令,增加获取所有节点和管道属性的功能

This commit is contained in:
2026-06-03 17:31:49 +08:00
parent 233960d8db
commit b7872f29a9
12 changed files with 823 additions and 433 deletions
+1 -4
View File
@@ -5,7 +5,6 @@ import typer
from .formatters import TJWaterGroup
app = typer.Typer(help="TJWater agent CLI", add_completion=False, no_args_is_help=True, cls=TJWaterGroup)
project_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
network_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
component_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
component_option_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
@@ -30,7 +29,6 @@ data_scheme_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
data_extension_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
data_misc_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
app.add_typer(project_app, name="project")
app.add_typer(network_app, name="network")
app.add_typer(component_app, name="component")
component_app.add_typer(component_option_app, name="option")
@@ -56,7 +54,6 @@ data_app.add_typer(data_extension_app, name="extension")
data_app.add_typer(data_misc_app, name="misc")
GROUP_HELP_APPS: list[tuple[typer.Typer, tuple[str, ...]]] = [
(project_app, ("project",)),
(network_app, ("network",)),
(component_app, ("component",)),
(component_option_app, ("component", "option")),
@@ -82,4 +79,4 @@ GROUP_HELP_APPS: list[tuple[typer.Typer, tuple[str, ...]]] = [
(data_misc_app, ("data", "misc")),
]
TOP_LEVEL_COMMANDS = {"help", "project", "network", "component", "simulation", "analysis", "data"}
TOP_LEVEL_COMMANDS = {"help", "network", "component", "simulation", "analysis", "data"}
+4 -6
View File
@@ -106,6 +106,7 @@ def analysis_valve(
element: Annotated[list[str] | None, typer.Option("--element", help="isolation 模式的事故元素,可重复")] = None,
disabled_valve: Annotated[list[str] | None, typer.Option("--disabled-valve", help="故障阀门,可重复")] = None,
duration: Annotated[int | None, typer.Option("--duration", help="close 模式持续秒数")] = None,
scheme: Annotated[str | None, typer.Option("--scheme", help="close 模式的方案名称")] = None,
) -> None:
runtime = runtime_context(ctx)
network = require_network(runtime)
@@ -122,6 +123,7 @@ def analysis_valve(
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"valves": valve,
"duration": duration or 900,
"scheme_name": resolve_scheme(runtime, scheme, required=True),
}
emit_api(
ctx,
@@ -182,10 +184,8 @@ def analysis_flushing(
"drainage_node_ID": drainage_node,
"flush_flow": flow,
"duration": duration or 900,
"scheme_name": resolve_scheme(runtime, scheme, required=True),
}
scheme_name = resolve_scheme(runtime, scheme)
if scheme_name:
params["scheme_name"] = scheme_name
emit_api(
ctx,
summary="冲洗分析执行成功",
@@ -236,10 +236,8 @@ def analysis_contaminant(
"source": source_node,
"concentration": concentration,
"duration": duration,
"scheme_name": resolve_scheme(runtime, scheme, required=True),
}
scheme_name = resolve_scheme(runtime, scheme)
if scheme_name:
params["scheme_name"] = scheme_name
if pattern:
params["pattern"] = pattern
emit_api(
-224
View File
@@ -1,224 +0,0 @@
from __future__ import annotations
from pathlib import Path
from typing import Annotated, Any
import typer
from .apps import component_option_app, network_app, project_app
from .common import emit_api, runtime_context
from .core import CLIError, emit_success, request_bytes, request_json, require_network
@project_app.command("list")
def project_list(ctx: typer.Context) -> None:
emit_api(ctx, summary="读取项目列表成功", method="GET", path="/meta/projects", require_auth=True)
@project_app.command("info")
def project_info(ctx: typer.Context) -> None:
emit_api(
ctx,
summary="读取项目信息成功",
method="GET",
path="/meta/project",
require_auth=True,
require_project=True,
)
@project_app.command("db-health")
def project_db_health(ctx: typer.Context) -> None:
emit_api(
ctx,
summary="读取数据库健康状态成功",
method="GET",
path="/meta/db/health",
require_auth=True,
require_project=True,
)
@project_app.command("data")
def project_data(
ctx: typer.Context,
kind: Annotated[str, typer.Option("--kind", help="scada-info|scheme-list|burst-locate-result")],
) -> None:
kind_map = {
"scada-info": "/scada-info",
"scheme-list": "/scheme-list",
"burst-locate-result": "/burst-locate-result",
}
path = kind_map.get(kind)
if path is None:
raise CLIError(
"CLI 参数错误",
code="INVALID_KIND",
message="kind must be one of: scada-info, scheme-list, burst-locate-result",
exit_code=2,
)
emit_api(
ctx,
summary="读取项目数据成功",
method="GET",
path=path,
require_auth=True,
require_project=True,
)
@project_app.command("export-inp")
def project_export_inp(
ctx: typer.Context,
output: Annotated[Path, typer.Option("--output", help="本地输出路径")],
) -> None:
runtime = runtime_context(ctx)
network = require_network(runtime)
output.parent.mkdir(parents=True, exist_ok=True)
temp_name = f"{output.stem}-{runtime.request_id}.inp"
_, duration_dump = request_json(
runtime,
method="GET",
path="/dumpinp/",
params={"network": network, "inp": temp_name},
require_auth=True,
require_network_ctx=True,
)
content, duration_download = request_bytes(
runtime,
method="GET",
path="/downloadinp/",
params={"name": temp_name},
require_auth=True,
require_network_ctx=True,
)
output.write_bytes(content)
emit_success(
summary="导出 INP 成功",
data={"output": str(output), "bytes": len(content)},
ctx=runtime,
duration_ms=duration_dump + duration_download,
next_commands=["tjwater-cli project info"],
)
@network_app.command("get-node-properties")
def network_get_node_properties(
ctx: typer.Context,
node: Annotated[str, typer.Option("--node", help="节点 ID")],
) -> None:
runtime = runtime_context(ctx)
emit_api(
ctx,
summary="读取节点属性成功",
method="GET",
path="/getnodeproperties/",
params={"network": require_network(runtime), "node": node},
require_auth=True,
require_network_ctx=True,
)
@network_app.command("get-link-properties")
def network_get_link_properties(
ctx: typer.Context,
link: Annotated[str, typer.Option("--link", help="管线 ID")],
) -> None:
runtime = runtime_context(ctx)
emit_api(
ctx,
summary="读取管线属性成功",
method="GET",
path="/getlinkproperties/",
params={"network": require_network(runtime), "link": link},
require_auth=True,
require_network_ctx=True,
)
def _component_option_mapping(kind: str, pump: str | None) -> tuple[str, dict[str, Any]]:
if kind == "time":
return "/gettimeschema", {}
if kind == "energy":
return "/getenergyschema/", {}
if kind == "pump-energy":
if not pump:
raise CLIError(
"CLI 参数错误",
code="PUMP_REQUIRED",
message="--pump is required when --kind pump-energy",
exit_code=2,
)
return "/getpumpenergyschema/", {"pump": pump}
if kind == "network":
return "/getoptionschema/", {}
raise CLIError(
"CLI 参数错误",
code="INVALID_KIND",
message="kind must be one of: time, energy, pump-energy, network",
exit_code=2,
)
def _component_option_get_mapping(kind: str, pump: str | None) -> tuple[str, dict[str, Any]]:
if kind == "time":
return "/gettimeproperties/", {}
if kind == "energy":
return "/getenergyproperties/", {}
if kind == "pump-energy":
if not pump:
raise CLIError(
"CLI 参数错误",
code="PUMP_REQUIRED",
message="--pump is required when --kind pump-energy",
exit_code=2,
)
return "/getpumpenergyproperties/", {"pump": pump}
if kind == "network":
return "/getoptionproperties/", {}
raise CLIError(
"CLI 参数错误",
code="INVALID_KIND",
message="kind must be one of: time, energy, pump-energy, network",
exit_code=2,
)
@component_option_app.command("schema")
def component_option_schema(
ctx: typer.Context,
kind: Annotated[str, typer.Option("--kind", help="time|energy|pump-energy|network")],
pump: Annotated[str | None, typer.Option("--pump", help="泵 ID")] = None,
) -> None:
runtime = runtime_context(ctx)
path, extra = _component_option_mapping(kind, pump)
params = {"network": require_network(runtime)} | extra
emit_api(
ctx,
summary="读取选项 schema 成功",
method="GET",
path=path,
params=params,
require_auth=True,
require_network_ctx=True,
)
@component_option_app.command("get")
def component_option_get(
ctx: typer.Context,
kind: Annotated[str, typer.Option("--kind", help="time|energy|pump-energy|network")],
pump: Annotated[str | None, typer.Option("--pump", help="泵 ID")] = None,
) -> None:
runtime = runtime_context(ctx)
path, extra = _component_option_get_mapping(kind, pump)
params = {"network": require_network(runtime)} | extra
emit_api(
ctx,
summary="读取选项属性成功",
method="GET",
path=path,
params=params,
require_auth=True,
require_network_ctx=True,
)
+144
View File
@@ -0,0 +1,144 @@
from __future__ import annotations
from typing import Annotated
import typer
from .apps import component_option_app, network_app
from .common import emit_api, runtime_context
from .core import CLIError, require_network
@network_app.command("get-node-properties")
def network_get_node_properties(
ctx: typer.Context,
node: Annotated[str, typer.Option("--node", help="节点 ID")],
) -> None:
runtime = runtime_context(ctx)
emit_api(
ctx,
summary="读取节点属性成功",
method="GET",
path="/getnodeproperties/",
params={"network": require_network(runtime), "node": node},
require_auth=True,
require_network_ctx=True,
)
@network_app.command("get-link-properties")
def network_get_link_properties(
ctx: typer.Context,
link: Annotated[str, typer.Option("--link", help="管线 ID")],
) -> None:
runtime = runtime_context(ctx)
emit_api(
ctx,
summary="读取管线属性成功",
method="GET",
path="/getlinkproperties/",
params={"network": require_network(runtime), "link": link},
require_auth=True,
require_network_ctx=True,
)
@network_app.command("get-all-junction-properties")
def network_get_all_junction_properties(ctx: typer.Context) -> None:
runtime = runtime_context(ctx)
emit_api(
ctx,
summary="读取全部节点属性成功",
method="GET",
path="/getalljunctionproperties/",
params={"network": require_network(runtime)},
require_auth=True,
require_network_ctx=True,
)
@network_app.command("get-all-pipe-properties")
def network_get_all_pipe_properties(ctx: typer.Context) -> None:
runtime = runtime_context(ctx)
emit_api(
ctx,
summary="读取全部管道属性成功",
method="GET",
path="/getallpipeproperties/",
params={"network": require_network(runtime)},
require_auth=True,
require_network_ctx=True,
)
@component_option_app.command("schema")
def component_option_schema(
ctx: typer.Context,
kind: Annotated[str, typer.Option("--kind", help="time|energy|pump-energy|network")],
pump: Annotated[str | None, typer.Option("--pump", help="pump-energy 时需要的泵 ID")] = None,
) -> None:
runtime = runtime_context(ctx)
path = _component_option_path(kind, schema=True)
params = {"network": require_network(runtime)}
if kind == "pump-energy" and pump:
params["pump"] = pump
emit_api(
ctx,
summary="读取选项 schema 成功",
method="GET",
path=path,
params=params,
require_auth=True,
require_network_ctx=True,
)
@component_option_app.command("get")
def component_option_get(
ctx: typer.Context,
kind: Annotated[str, typer.Option("--kind", help="time|energy|pump-energy|network")],
pump: Annotated[str | None, typer.Option("--pump", help="pump-energy 时需要的泵 ID")] = None,
) -> None:
runtime = runtime_context(ctx)
path = _component_option_path(kind, schema=False)
params = {"network": require_network(runtime)}
if kind == "pump-energy":
if not pump:
raise CLIError(
"CLI 参数错误",
code="PUMP_REQUIRED",
message="--pump is required when --kind pump-energy",
exit_code=2,
)
params["pump"] = pump
emit_api(
ctx,
summary="读取选项属性成功",
method="GET",
path=path,
params=params,
require_auth=True,
require_network_ctx=True,
)
def _component_option_path(kind: str, *, schema: bool) -> str:
routes = {
("time", True): "/gettimeschema",
("time", False): "/gettimeproperties/",
("energy", True): "/getenergyschema/",
("energy", False): "/getenergyproperties/",
("pump-energy", True): "/getpumpenergyschema/",
("pump-energy", False): "/getpumpenergyproperties//",
("network", True): "/getoptionschema/",
("network", False): "/getoptionproperties/",
}
path = routes.get((kind, schema))
if path is None:
raise CLIError(
"CLI 参数错误",
code="INVALID_KIND",
message="--kind must be one of time, energy, pump-energy, network",
exit_code=2,
)
return path
-1
View File
@@ -97,7 +97,6 @@ def _click_option_docs(path: tuple[str, ...]) -> list[dict[str, Any]]:
def _sample_option_value(path: tuple[str, ...], option_name: str) -> str:
path_specific_samples: dict[tuple[tuple[str, ...], str], str] = {
(("project", "data"), "kind"): "scada-info",
(("component", "option", "schema"), "kind"): "time",
(("component", "option", "get"), "kind"): "time",
(("data", "timeseries", "composite"), "kind"): "scada-simulation",
+1 -1
View File
@@ -8,7 +8,7 @@ import click
import typer
from click.exceptions import NoArgsIsHelpError
from . import commands_analysis, commands_data, commands_project # noqa: F401
from . import commands_analysis, commands_data, commands_readonly # noqa: F401
from .apps import app
from .core import CLIError, DEFAULT_SERVER, DEFAULT_TIMEOUT, emit_failure
from .helping import (
+228 -48
View File
@@ -3,7 +3,6 @@ from __future__ import annotations
from .core import CommandDoc, CommandOptionDoc, SCHEMA_VERSION
GROUP_SUMMARIES: dict[tuple[str, ...], str] = {
("project",): "项目与项目级元数据相关命令。",
("network",): "管网节点、管线等基础属性查询命令。",
("component",): "组件选项与配置读取命令。",
("component", "option"): "组件选项查询命令。",
@@ -35,51 +34,31 @@ HIDDEN_PATH_PREFIXES: tuple[tuple[str, ...], ...] = (
)
COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
("project", "list"): CommandDoc(
path=("project", "list"),
summary="列出当前用户可访问项目",
description="调用 /meta/projects 返回项目列表。",
examples=("tjwater-cli project list",),
next_commands=("tjwater-cli project info",),
),
("project", "info"): CommandDoc(
path=("project", "info"),
summary="查看当前项目摘要信息。",
description="查看当前项目的基础信息。",
examples=("tjwater-cli project info",),
output="项目元数据",
),
("project", "db-health"): CommandDoc(
path=("project", "db-health"),
summary="检查当前项目数据库健康状态",
description="调用 /meta/db/health 返回 PostgreSQL 与 Timescale 健康状态。",
),
("project", "export-inp"): CommandDoc(
path=("project", "export-inp"),
summary="导出当前项目 INP 到本地文件",
description="先调用 /dumpinp/ 在服务端生成 INP,再通过 /downloadinp/ 下载到本地。",
options=(
CommandOptionDoc("output", "本地输出路径", required=True),
),
output="本地文件路径和下载摘要",
),
("project", "data"): CommandDoc(
path=("project", "data"),
summary="读取当前项目业务数据",
description="kind 支持 scada-info、scheme-list、burst-locate-result。",
options=(CommandOptionDoc("kind", "数据类型", required=True),),
),
("network", "get-node-properties"): CommandDoc(
path=("network", "get-node-properties"),
summary="读取节点属性",
description="调用 /getnodeproperties/。",
options=(CommandOptionDoc("node", "节点 ID", required=True),),
examples=("tjwater-cli network get-node-properties --node J1",),
),
("network", "get-link-properties"): CommandDoc(
path=("network", "get-link-properties"),
summary="读取管线属性",
description="调用 /getlinkproperties/。",
options=(CommandOptionDoc("link", "管线 ID", required=True),),
examples=("tjwater-cli network get-link-properties --link P1",),
),
("network", "get-all-junction-properties"): CommandDoc(
path=("network", "get-all-junction-properties"),
summary="读取全部节点属性",
description="调用 /getalljunctionproperties/。",
examples=("tjwater-cli network get-all-junction-properties",),
),
("network", "get-all-pipe-properties"): CommandDoc(
path=("network", "get-all-pipe-properties"),
summary="读取全部管道属性",
description="调用 /getallpipeproperties/。",
examples=("tjwater-cli network get-all-pipe-properties",),
),
("component", "option", "schema"): CommandDoc(
path=("component", "option", "schema"),
@@ -89,6 +68,12 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
CommandOptionDoc("kind", "选项类型", required=True),
CommandOptionDoc("pump", "pump-energy 时需要的泵 ID"),
),
examples=(
"tjwater-cli component option schema --kind time",
"tjwater-cli component option schema --kind energy",
"tjwater-cli component option schema --kind pump-energy --pump PUMP1",
"tjwater-cli component option schema --kind network",
),
),
("component", "option", "get"): CommandDoc(
path=("component", "option", "get"),
@@ -98,15 +83,22 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
CommandOptionDoc("kind", "选项类型", required=True),
CommandOptionDoc("pump", "pump-energy 时需要的泵 ID"),
),
examples=(
"tjwater-cli component option get --kind time",
"tjwater-cli component option get --kind energy",
"tjwater-cli component option get --kind pump-energy --pump PUMP1",
"tjwater-cli component option get --kind network",
),
),
("simulation", "run"): CommandDoc(
path=("simulation", "run"),
summary="触发指定绝对时间的模拟运行",
description="把显式带时区的 RFC3339 start-time 直接传给 /runsimulationmanuallybydate/;服务端按带时区时间处理并统一按 UTC 存储结果,实时数据需后续通过 data timeseries 在对应时间段查询。",
description="把显式带时区的 RFC3339 start-time 直接传给 /runsimulationmanuallybydate/;服务端按带时区时间处理并统一按 UTC 存储结果,实时数据需后续通过 data timeseries 在对应时间段查询。duration 单位为分钟。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("duration", "持续分钟数", required=True),
),
examples=("tjwater-cli simulation run --start-time 2025-01-02T03:04:05+08:00 --duration 30",),
next_commands=(
"tjwater-cli data timeseries realtime links --start-time 2025-01-02T03:04:05+08:00 --end-time 2025-01-02T03:34:05+08:00",
"tjwater-cli data timeseries realtime nodes --start-time 2025-01-02T03:04:05+08:00 --end-time 2025-01-02T03:34:05+08:00",
@@ -116,7 +108,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
("analysis", "burst"): CommandDoc(
path=("analysis", "burst"),
summary="执行爆管分析",
description="读取 burst-file 并转换为 burst_ID[] / burst_size[];接口本身只返回分析执行结果,方案数据需后续通过 data scheme 命令获取。",
description="读取 burst-file 并转换为 burst_ID[] / burst_size[];接口本身只返回分析执行结果,方案数据需后续通过 data scheme 命令获取。duration 单位为秒。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("duration", "持续秒数", required=True),
@@ -124,7 +116,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
CommandOptionDoc("scheme", "方案名称"),
),
examples=(
"tjwater-cli analysis burst --start-time 2025-01-02T03:04:05+08:00 --duration 30 --burst-file ./burst.json --scheme burst_case_01",
"tjwater-cli analysis burst --start-time 2025-01-02T03:04:05+08:00 --duration 900 --burst-file ./burst.json --scheme burst_case_01",
"tjwater-cli data scheme get --name burst_case_01",
"tjwater-cli data scheme list",
),
@@ -132,201 +124,389 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
("analysis", "valve"): CommandDoc(
path=("analysis", "valve"),
summary="阀门工况分析。",
description="指定阀门采取关闭/开启等操作逻辑,并执行定时长模拟。结果写入时序库",
description="close 模式按指定阀门关闭执行定时长模拟;isolation 模式按指定事故元素计算关阀隔离方案。duration 单位为秒",
options=(
CommandOptionDoc(name="mode", description="阀门操作模式:'close''open'", required=True),
CommandOptionDoc(name="start-time", description="起始绝对时间,必须显式带时区偏移", required=True),
CommandOptionDoc(name="valve", description="阀门 ID(可多次指定)", required=True, repeated=True),
CommandOptionDoc(name="duration", description="模拟持续分钟数", required=True),
CommandOptionDoc(name="mode", description="阀门操作模式:'close''isolation'", required=True),
CommandOptionDoc(name="start-time", description="close 模式需要的起始绝对时间,必须显式带时区偏移"),
CommandOptionDoc(name="valve", description="close 模式下需关闭的阀门 ID(可多次指定)", repeated=True),
CommandOptionDoc(name="element", description="isolation 模式下的事故元素 ID(可多次指定)", repeated=True),
CommandOptionDoc(name="disabled-valve", description="isolation 模式下需排除的故障阀门 ID(可多次指定)", repeated=True),
CommandOptionDoc(name="duration", description="close 模式持续秒数,默认 900"),
CommandOptionDoc(name="scheme", description="close 模式方案名称"),
),
examples=(
"tjwater-cli analysis valve --mode close --start-time 2025-01-02T03:04:05+08:00 --valve V1 --duration 900",
"tjwater-cli analysis valve --mode close --start-time 2025-01-02T03:04:05+08:00 --valve V1 --valve V2 --duration 900 --scheme valve_case_01",
"tjwater-cli analysis valve --mode isolation --element E1 --element E2",
"tjwater-cli analysis valve --mode isolation --element E1 --disabled-valve V3",
),
),
("analysis", "flushing"): CommandDoc(
path=("analysis", "flushing"),
summary="执行冲洗分析",
description="读取 valve-setting-file 并转换为 valves[] / valves_k[]。",
description="读取 valve-setting-file 并转换为 valves[] / valves_k[]。duration 单位为秒,默认 900。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("valve-setting-file", "阀门开度 JSON 文件", required=True),
CommandOptionDoc("drainage-node", "排污节点 ID", required=True),
CommandOptionDoc("flow", "冲洗流量", required=True),
CommandOptionDoc("duration", "持续秒数,默认 900"),
CommandOptionDoc("scheme", "方案名称", required=True),
),
examples=("tjwater-cli analysis flushing --start-time 2025-01-02T03:04:05+08:00 --valve-setting-file ./valve.json --drainage-node N1 --flow 100.0 --duration 900 --scheme flush_case_01",),
),
("analysis", "age"): CommandDoc(
path=("analysis", "age"),
summary="执行水龄分析",
description="调用 /age_analysis/。",
description="调用 /age_analysis/。duration 单位为秒。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("duration", "持续秒数", required=True),
),
examples=("tjwater-cli analysis age --start-time 2025-01-02T03:04:05+08:00 --duration 900",),
),
("analysis", "contaminant"): CommandDoc(
path=("analysis", "contaminant"),
summary="执行污染物模拟",
description="调用 /contaminant_simulation/。",
description="调用 /contaminant_simulation/。duration 单位为秒。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("duration", "持续秒数", required=True),
CommandOptionDoc("source-node", "污染源节点 ID", required=True),
CommandOptionDoc("concentration", "浓度值", required=True),
CommandOptionDoc("pattern", "模式 ID"),
CommandOptionDoc("scheme", "方案名称", required=True),
),
examples=("tjwater-cli analysis contaminant --start-time 2025-01-02T03:04:05+08:00 --duration 900 --source-node N1 --concentration 10.0 --scheme contam_case_01",),
),
("analysis", "sensor-placement", "kmeans"): CommandDoc(
path=("analysis", "sensor-placement", "kmeans"),
summary="执行 KMeans 传感器选址",
description="使用 POST /pressure_sensor_placement_kmeans/,补齐 username 和 min_diameter。",
options=(
CommandOptionDoc("count", "传感器数量", required=True),
CommandOptionDoc("min-diameter", "最小管径,默认 0"),
CommandOptionDoc("scheme", "方案名称"),
),
examples=("tjwater-cli analysis sensor-placement kmeans --count 5 --min-diameter 100 --scheme placement_case_01",),
),
("analysis", "leakage", "identify"): CommandDoc(
path=("analysis", "leakage", "identify"),
summary="执行漏损识别",
description="把 CLI 时间映射到 scada_start / scada_end。",
options=(
CommandOptionDoc("start-time", "显式带时区的 SCADA 开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的 SCADA 结束时间", required=True),
CommandOptionDoc("scheme", "方案名称"),
),
examples=("tjwater-cli analysis leakage identify --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --scheme leak_case_01",),
),
("analysis", "leakage", "schemes", "list"): CommandDoc(
path=("analysis", "leakage", "schemes", "list"),
summary="列出漏损方案",
description="调用 /leakage/schemes/。",
examples=("tjwater-cli analysis leakage schemes list",),
),
("analysis", "leakage", "schemes", "get"): CommandDoc(
path=("analysis", "leakage", "schemes", "get"),
summary="读取漏损方案详情",
description="调用 /leakage/schemes/{scheme_name}",
examples=("tjwater-cli analysis leakage schemes get my_scheme",),
),
("analysis", "burst-detection", "detect"): CommandDoc(
path=("analysis", "burst-detection", "detect"),
summary="执行爆管检测",
description="调用 /burst-detection/detect/。",
options=(
CommandOptionDoc("start-time", "显式带时区的 SCADA 开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的 SCADA 结束时间", required=True),
CommandOptionDoc("scheme", "方案名称"),
),
examples=("tjwater-cli analysis burst-detection detect --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --scheme detect_case_01",),
),
("analysis", "burst-detection", "schemes", "list"): CommandDoc(
path=("analysis", "burst-detection", "schemes", "list"),
summary="列出爆管检测方案",
description="调用 /burst-detection/schemes/。",
examples=("tjwater-cli analysis burst-detection schemes list",),
),
("analysis", "burst-detection", "schemes", "get"): CommandDoc(
path=("analysis", "burst-detection", "schemes", "get"),
summary="读取爆管检测方案详情",
description="调用 /burst-detection/schemes/{scheme_name}",
examples=("tjwater-cli analysis burst-detection schemes get my_scheme",),
),
("analysis", "burst-location", "locate"): CommandDoc(
path=("analysis", "burst-location", "locate"),
summary="执行爆管定位",
description="调用 /burst-location/locate/;需要 burst-leakage。",
description="调用 /burst-location/locate/;需要 burst-leakage。支持 monitoring 和 simulation 两种数据源。",
options=(
CommandOptionDoc("start-time", "显式带时区的 SCADA 开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的 SCADA 结束时间", required=True),
CommandOptionDoc("burst-leakage", "爆管漏水量", required=True),
CommandOptionDoc("scheme", "方案名称"),
CommandOptionDoc("data-source", "数据源:monitoring(默认)或 simulation"),
CommandOptionDoc("pressure-scada-id", "压力 SCADA ID(可多次指定)", repeated=True),
CommandOptionDoc("flow-scada-id", "流量 SCADA ID(可多次指定)", repeated=True),
CommandOptionDoc("pressure-file", "包含 burst_pressure/normal_pressure 的 JSON 文件"),
CommandOptionDoc("flow-file", "包含 burst_flow/normal_flow 的 JSON 文件"),
CommandOptionDoc("use-scada-flow", "启用 SCADA 流量"),
),
examples=(
"tjwater-cli analysis burst-location locate --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --burst-leakage 100.0 --scheme locate_case_01",
"tjwater-cli analysis burst-location locate --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --burst-leakage 50.0 --scheme locate_case_01 --data-source simulation --pressure-file ./pressure.json --flow-file ./flow.json",
),
),
("analysis", "burst-location", "schemes", "list"): CommandDoc(
path=("analysis", "burst-location", "schemes", "list"),
summary="列出爆管定位方案",
description="调用 /burst-location/schemes/。",
examples=("tjwater-cli analysis burst-location schemes list",),
),
("analysis", "burst-location", "schemes", "get"): CommandDoc(
path=("analysis", "burst-location", "schemes", "get"),
summary="读取爆管定位方案详情",
description="调用 /burst-location/schemes/{scheme_name}",
examples=("tjwater-cli analysis burst-location schemes get my_scheme",),
),
("analysis", "risk", "pipe-now"): CommandDoc(
path=("analysis", "risk", "pipe-now"),
summary="读取单条管道当前风险",
description="调用 /getpiperiskprobabilitynow/。",
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
examples=("tjwater-cli analysis risk pipe-now --pipe P1",),
),
("analysis", "risk", "pipe-history"): CommandDoc(
path=("analysis", "risk", "pipe-history"),
summary="读取单条管道历史风险",
description="调用 /getpiperiskprobability/。",
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
examples=("tjwater-cli analysis risk pipe-history --pipe P1",),
),
("analysis", "risk", "network"): CommandDoc(
path=("analysis", "risk", "network"),
summary="读取全网风险",
description="组合 /getnetworkpiperiskprobabilitynow/ 与 /getpiperiskprobabilitygeometries/。",
examples=("tjwater-cli analysis risk network",),
),
("data", "timeseries", "realtime", "links"): CommandDoc(
path=("data", "timeseries", "realtime", "links"),
summary="查询实时管道时序",
description="调用 /realtime/links。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
),
examples=("tjwater-cli data timeseries realtime links --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00",),
),
("data", "timeseries", "realtime", "nodes"): CommandDoc(
path=("data", "timeseries", "realtime", "nodes"),
summary="查询实时节点时序",
description="调用 /realtime/nodes。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
),
examples=("tjwater-cli data timeseries realtime nodes --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00",),
),
("data", "timeseries", "realtime", "simulation-by-id-time"): CommandDoc(
path=("data", "timeseries", "realtime", "simulation-by-id-time"),
summary="按元素和时间查询实时模拟结果",
description="调用 /realtime/query/by-id-time。",
options=(
CommandOptionDoc("id", "元素 ID", required=True),
CommandOptionDoc("type", "元素类型:pipe 或 junction", required=True),
CommandOptionDoc("time", "显式带时区的查询时间", required=True),
),
examples=(
"tjwater-cli data timeseries realtime simulation-by-id-time --id J1 --type junction --time 2025-01-02T03:30:00+08:00",
"tjwater-cli data timeseries realtime simulation-by-id-time --id P1 --type pipe --time 2025-01-02T03:30:00+08:00",
),
),
("data", "timeseries", "realtime", "simulation-by-time-property"): CommandDoc(
path=("data", "timeseries", "realtime", "simulation-by-time-property"),
summary="按时间和属性查询实时模拟结果",
description="调用 /realtime/query/by-time-property。",
options=(
CommandOptionDoc("type", "元素类型:pipe 或 junction", required=True),
CommandOptionDoc("time", "显式带时区的查询时间", required=True),
CommandOptionDoc("property", "属性名", required=True),
),
examples=("tjwater-cli data timeseries realtime simulation-by-time-property --type pipe --time 2025-01-02T03:30:00+08:00 --property flow",),
),
("data", "timeseries", "scheme", "links"): CommandDoc(
path=("data", "timeseries", "scheme", "links"),
summary="查询方案管道时序",
description="调用 /scheme/links。",
options=(
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
CommandOptionDoc("scheme", "方案名称"),
CommandOptionDoc("scheme-type", "方案类型"),
),
examples=("tjwater-cli data timeseries scheme links --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --scheme my_scheme",),
),
("data", "timeseries", "scheme", "node-field"): CommandDoc(
path=("data", "timeseries", "scheme", "node-field"),
summary="查询方案节点字段时序",
description="调用 /scheme/nodes/{node_id}/field。",
options=(
CommandOptionDoc("node", "节点 ID", required=True),
CommandOptionDoc("field", "字段名", required=True),
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
CommandOptionDoc("scheme", "方案名称"),
CommandOptionDoc("scheme-type", "方案类型"),
),
examples=("tjwater-cli data timeseries scheme node-field --node J1 --field pressure --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --scheme my_scheme",),
),
("data", "timeseries", "scheme", "simulation"): CommandDoc(
path=("data", "timeseries", "scheme", "simulation"),
summary="查询方案模拟数据",
description="支持 by-id-time 与 by-scheme-time-property 两种查询。",
options=(
CommandOptionDoc("query", "查询模式:by-id-time 或 by-scheme-time-property", required=True),
CommandOptionDoc("scheme", "方案名称"),
CommandOptionDoc("scheme-type", "方案类型"),
CommandOptionDoc("id", "元素 IDby-id-time 时必需)"),
CommandOptionDoc("time", "显式带时区的查询时间", required=True),
CommandOptionDoc("type", "元素类型:pipe 或 junction"),
CommandOptionDoc("property", "属性名(by-scheme-time-property 时必需)"),
),
examples=(
"tjwater-cli data timeseries scheme simulation --query by-id-time --id J1 --time 2025-01-02T03:30:00+08:00 --type junction --scheme my_scheme",
"tjwater-cli data timeseries scheme simulation --query by-scheme-time-property --time 2025-01-02T03:30:00+08:00 --type pipe --property flow --scheme my_scheme",
),
),
("data", "timeseries", "scada", "query"): CommandDoc(
path=("data", "timeseries", "scada", "query"),
summary="查询 SCADA 时序",
description="device-id 会被转换成后端逗号分隔参数。",
options=(
CommandOptionDoc("device-id", "设备 ID(可多次指定)", required=True, repeated=True),
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
CommandOptionDoc("field", "字段名"),
),
examples=(
"tjwater-cli data timeseries scada query --device-id D1 --device-id D2 --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00",
"tjwater-cli data timeseries scada query --device-id D1 --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --field flow",
),
),
("data", "timeseries", "composite"): CommandDoc(
path=("data", "timeseries", "composite"),
summary="执行复合时序查询",
description="kind 支持 scada-simulation、element-simulation、element-scada。",
options=(
CommandOptionDoc("kind", "复合查询类型", required=True),
CommandOptionDoc("feature", "特征值(可多次指定,scada-simulation 为 device_idelement-simulation 为 element_id:propertyelement-scada 为 element_id", repeated=True),
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
CommandOptionDoc("scheme", "方案名称"),
CommandOptionDoc("scheme-type", "方案类型"),
CommandOptionDoc("use-cleaned", "element-scada 使用清洗值"),
),
examples=(
"tjwater-cli data timeseries composite --kind scada-simulation --feature D1 --feature D2 --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --scheme my_scheme",
"tjwater-cli data timeseries composite --kind element-simulation --feature J1:pressure --feature P1:flow --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --scheme my_scheme",
"tjwater-cli data timeseries composite --kind element-scada --feature J1 --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00 --use-cleaned",
),
),
("data", "timeseries", "composite", "pipeline-health"): CommandDoc(
path=("data", "timeseries", "composite", "pipeline-health"),
summary="查询管道健康预测",
description="调用 /composite/pipeline-health-prediction。",
options=(
CommandOptionDoc("pipe", "管道 ID", required=True),
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
),
examples=("tjwater-cli data timeseries composite pipeline-health --pipe P1 --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00",),
),
("data", "scada", "schema"): CommandDoc(
path=("data", "scada", "schema"),
summary="读取 SCADA schema",
description="kind 支持 device、device-data、element、info。",
options=(CommandOptionDoc("kind", "SCADA 数据类型", required=True),),
examples=(
"tjwater-cli data scada schema --kind device",
"tjwater-cli data scada schema --kind device-data",
"tjwater-cli data scada schema --kind element",
"tjwater-cli data scada schema --kind info",
),
),
("data", "scada", "get"): CommandDoc(
path=("data", "scada", "get"),
summary="读取单条 SCADA 元数据",
description="kind 支持 device、device-data、element、info。",
options=(
CommandOptionDoc("kind", "SCADA 数据类型", required=True),
CommandOptionDoc("id", "记录 ID", required=True),
),
examples=(
"tjwater-cli data scada get --kind device --id D1",
"tjwater-cli data scada get --kind element --id E1",
),
),
("data", "scada", "list"): CommandDoc(
path=("data", "scada", "list"),
summary="列出 SCADA 元数据",
description="kind 支持 device、element、infodevice-data 当前后端无 list 接口。",
options=(CommandOptionDoc("kind", "SCADA 数据类型", required=True),),
examples=(
"tjwater-cli data scada list --kind device",
"tjwater-cli data scada list --kind element",
"tjwater-cli data scada list --kind info",
),
),
("data", "scheme", "schema"): CommandDoc(
path=("data", "scheme", "schema"),
summary="读取方案 schema",
description="调用 /getschemeschema/。",
examples=("tjwater-cli data scheme schema",),
),
("data", "scheme", "get"): CommandDoc(
path=("data", "scheme", "get"),
summary="读取单条方案",
description="调用 /getscheme/。",
options=(CommandOptionDoc("name", "方案名称", required=True),),
examples=("tjwater-cli data scheme get --name my_scheme",),
),
("data", "scheme", "list"): CommandDoc(
path=("data", "scheme", "list"),
summary="列出方案",
description="调用 /getallschemes/。",
examples=("tjwater-cli data scheme list",),
),
("data", "extension", "keys"): CommandDoc(
path=("data", "extension", "keys"),
summary="列出扩展数据键",
description="调用 /getallextensiondatakeys/。",
examples=("tjwater-cli data extension keys",),
),
("data", "extension", "get"): CommandDoc(
path=("data", "extension", "get"),
summary="读取扩展数据",
description="调用 /getextensiondata/。",
options=(CommandOptionDoc("key", "扩展键", required=True),),
examples=("tjwater-cli data extension get --key my_key",),
),
("data", "extension", "list"): CommandDoc(
path=("data", "extension", "list"),
summary="列出扩展数据",
description="调用 /getallextensiondata/。",
examples=("tjwater-cli data extension list",),
),
("data", "misc", "sensor-placements"): CommandDoc(
path=("data", "misc", "sensor-placements"),
summary="列出传感器布置结果",
description="调用 /getallsensorplacements/。",
examples=("tjwater-cli data misc sensor-placements",),
),
("data", "misc", "burst-location-results"): CommandDoc(
path=("data", "misc", "burst-location-results"),
summary="列出爆管定位结果",
description="调用 /getallburstlocateresults/。",
examples=("tjwater-cli data misc burst-location-results",),
),
}