from __future__ import annotations from .core import CommandDoc, CommandOptionDoc, SCHEMA_VERSION GROUP_SUMMARIES: dict[tuple[str, ...], str] = { ("network",): "管网节点、管线等基础属性查询命令。", ("component",): "组件选项与配置读取命令。", ("component", "option"): "组件选项查询命令。", ("simulation",): "模拟运行与调度相关命令。", ("analysis",): "分析计算与诊断相关命令。", ("analysis", "leakage"): "漏损分析相关命令。", ("analysis", "leakage", "schemes"): "漏损方案查询命令。", ("analysis", "burst-detection"): "爆管检测相关命令。", ("analysis", "burst-detection", "schemes"): "爆管检测方案查询命令。", ("analysis", "burst-location"): "爆管定位相关命令。", ("analysis", "burst-location", "schemes"): "爆管定位方案查询命令。", ("analysis", "risk"): "风险分析相关命令。", ("analysis", "sensor-placement"): "传感器选址相关命令。", ("data",): "时序、SCADA 和方案数据查询命令。", ("data", "timeseries"): "时序数据查询命令。", ("data", "timeseries", "realtime"): "实时模拟时序查询命令。", ("data", "timeseries", "scheme"): "方案时序查询命令。", ("data", "timeseries", "scada"): "SCADA 时序查询命令。", ("data", "timeseries", "composite"): "复合时序查询命令。", ("data", "scada"): "SCADA 元数据查询命令。", ("data", "scheme"): "方案数据查询命令。", } HIDDEN_PATH_PREFIXES: tuple[tuple[str, ...], ...] = ( ("analysis", "burst-location"), ("analysis", "risk"), ) COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = { ("network", "get-junction-properties"): CommandDoc( path=("network", "get-junction-properties"), summary="读取节点属性", description="调用 /getjunctionproperties/。", options=(CommandOptionDoc("junction", "节点 ID", required=True),), examples=("tjwater-cli network get-junction-properties --junction J1",), ), ("network", "get-pipe-properties"): CommandDoc( path=("network", "get-pipe-properties"), summary="读取管道属性", description="调用 /getpipeproperties/。", options=(CommandOptionDoc("pipe", "管道 ID", required=True),), examples=("tjwater-cli network get-pipe-properties --pipe P1",), ), ("network", "get-all-pipes-properties"): CommandDoc( path=("network", "get-all-pipes-properties"), summary="读取全部管道属性", description="调用 /getallpipeproperties/。", examples=("tjwater-cli network get-all-pipes-properties",), ), ("network", "get-reservoir-properties"): CommandDoc( path=("network", "get-reservoir-properties"), summary="读取水库属性", description="调用 /getreservoirproperties/。", options=(CommandOptionDoc("reservoir", "水库 ID", required=True),), examples=("tjwater-cli network get-reservoir-properties --reservoir R1",), ), ("network", "get-all-reservoirs-properties"): CommandDoc( path=("network", "get-all-reservoirs-properties"), summary="读取全部水库属性", description="调用 /getallreservoirproperties/。", examples=("tjwater-cli network get-all-reservoirs-properties",), ), ("network", "get-tank-properties"): CommandDoc( path=("network", "get-tank-properties"), summary="读取水箱属性", description="调用 /gettankproperties/。", options=(CommandOptionDoc("tank", "水箱 ID", required=True),), examples=("tjwater-cli network get-tank-properties --tank T1",), ), ("network", "get-all-tanks-properties"): CommandDoc( path=("network", "get-all-tanks-properties"), summary="读取全部水箱属性", description="调用 /getalltankproperties/。", examples=("tjwater-cli network get-all-tanks-properties",), ), ("network", "get-pump-properties"): CommandDoc( path=("network", "get-pump-properties"), summary="读取水泵属性", description="调用 /getpumpproperties/。", options=(CommandOptionDoc("pump", "水泵 ID", required=True),), examples=("tjwater-cli network get-pump-properties --pump PU1",), ), ("network", "get-all-pumps-properties"): CommandDoc( path=("network", "get-all-pumps-properties"), summary="读取全部水泵属性", description="调用 /getallpumpproperties/。", examples=("tjwater-cli network get-all-pumps-properties",), ), ("network", "get-valve-properties"): CommandDoc( path=("network", "get-valve-properties"), summary="读取阀门属性", description="调用 /getvalveproperties/。", options=(CommandOptionDoc("valve", "阀门 ID", required=True),), examples=("tjwater-cli network get-valve-properties --valve V1",), ), ("network", "get-all-valves-properties"): CommandDoc( path=("network", "get-all-valves-properties"), summary="读取全部阀门属性", description="调用 /getallvalveproperties/。", examples=("tjwater-cli network get-all-valves-properties",), ), ("component", "option", "schema"): CommandDoc( path=("component", "option", "schema"), summary="读取选项 schema", description="kind 支持 time、energy、pump-energy、network。", options=( 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"), summary="读取选项属性", description="kind 支持 time、energy、pump-energy、network。", options=( 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 在对应时间段查询。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", ), output="模拟触发结果;实时数据需通过 data timeseries 命令按时间段查询", ), ("analysis", "burst"): CommandDoc( path=("analysis", "burst"), summary="执行爆管分析", description="读取 burst-file 并转换为 burst_ID[] / burst_size[];接口本身只返回分析执行结果,方案数据需后续通过 data scheme 命令获取。duration 单位为秒。", options=( CommandOptionDoc("start-time", "显式带时区的开始时间", required=True), CommandOptionDoc("duration", "持续秒数", required=True), CommandOptionDoc("burst-file", "爆管输入 JSON 文件", required=True), CommandOptionDoc("scheme", "方案名称"), ), examples=( "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", ), ), ("analysis", "valve"): CommandDoc( path=("analysis", "valve"), summary="阀门工况分析。", description="close 模式按指定阀门关闭执行定时长模拟;isolation 模式按指定事故元素计算关阀隔离方案。duration 单位为秒。", options=( 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 --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[]。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/。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/。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。支持 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;links/nodes 是独立子命令,不是 type 取值", 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。pipe 属性:flow、friction、headloss、quality、reaction、setting、status、velocity;junction 属性:actual_demand、total_head、pressure、quality。", options=( CommandOptionDoc("type", "元素类型:pipe 或 junction;links/nodes 是独立子命令,不是 type 取值", required=True), CommandOptionDoc("time", "显式带时区的查询时间", required=True), CommandOptionDoc("property", "属性名;会按 type 校验可选值", 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。field 仅支持 actual_demand、total_head、pressure、quality。", options=( CommandOptionDoc("node", "节点 ID", required=True), CommandOptionDoc("field", "字段名:actual_demand、total_head、pressure、quality", 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 两种查询。pipe 属性:flow、friction、headloss、quality、reaction、setting、status、velocity;junction 属性:actual_demand、total_head、pressure、quality。", options=( CommandOptionDoc("query", "查询模式:by-id-time 或 by-scheme-time-property", required=True), CommandOptionDoc("scheme", "方案名称"), CommandOptionDoc("scheme-type", "方案类型"), CommandOptionDoc("id", "元素 ID(by-id-time 时必需)"), CommandOptionDoc("time", "显式带时区的查询时间", required=True), CommandOptionDoc("type", "元素类型:pipe 或 junction;links/nodes 是独立子命令,不是 type 取值"), CommandOptionDoc("property", "属性名(by-scheme-time-property 时必需;会按 type 校验可选值)"), ), 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 会被转换成后端逗号分隔参数。field 仅支持 monitored_value、cleaned_value。", options=( CommandOptionDoc("device-id", "设备 ID(可多次指定)", required=True, repeated=True), CommandOptionDoc("start-time", "显式带时区的开始时间", required=True), CommandOptionDoc("end-time", "显式带时区的结束时间", required=True), CommandOptionDoc("field", "字段名:monitored_value、cleaned_value"), ), 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 monitored_value", ), ), ("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_id,element-simulation 为 element_id:property,element-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", "get"): CommandDoc( path=("data", "scada", "get"), summary="读取单条 SCADA 元数据", description="kind 仅支持 info。", options=( CommandOptionDoc("kind", "SCADA 数据类型", required=True), CommandOptionDoc("id", "记录 ID", required=True), ), examples=("tjwater-cli data scada get --kind info --id SCADA-001",), ), ("data", "scada", "list"): CommandDoc( path=("data", "scada", "list"), summary="列出 SCADA 元数据", description="kind 仅支持 info。", options=(CommandOptionDoc("kind", "SCADA 数据类型", required=True),), examples=("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",), ), } def _build_examples(doc: CommandDoc) -> list[str]: return list(doc.examples) if doc.examples else [_build_usage(doc)] def _is_hidden_path(path: tuple[str, ...]) -> bool: return any(path[: len(prefix)] == prefix for prefix in HIDDEN_PATH_PREFIXES) def is_hidden_path(path: tuple[str, ...]) -> bool: return _is_hidden_path(path) def has_subcommands(path_prefix: tuple[str, ...]) -> bool: return any( not _is_hidden_path(doc.path) and doc.path[: len(path_prefix)] == path_prefix and len(doc.path) > len(path_prefix) for doc in COMMAND_DOCS.values() ) def get_group_summary(path_prefix: tuple[str, ...]) -> str: return GROUP_SUMMARIES.get(path_prefix, f"{' '.join(path_prefix)} 可用子命令") def list_capabilities() -> dict[str, object]: seen: set[tuple[str, ...]] = set() commands: list[dict[str, str]] = [] for doc in sorted(COMMAND_DOCS.values(), key=lambda item: item.path): if _is_hidden_path(doc.path): continue prefix = doc.path[:1] if prefix in seen: continue seen.add(prefix) commands.append( { "command": " ".join(prefix), "summary": get_group_summary(prefix), } ) return { "ok": True, "schema_version": SCHEMA_VERSION, "summary": "可用一级菜单", "menu_level": 1, "commands": commands, } def get_command_doc(path: tuple[str, ...]) -> dict[str, object] | None: if _is_hidden_path(path): return None doc = COMMAND_DOCS.get(path) if doc is None: return None return { "ok": True, "schema_version": SCHEMA_VERSION, "summary": doc.summary, "command": " ".join(doc.path), "description": doc.description, "usage": _build_usage(doc), "options": [ { "name": option.name, "description": option.description, "required": option.required, "repeated": option.repeated, "default": option.default, } for option in doc.options ], "examples": _build_examples(doc), "next_commands": list(doc.next_commands), "output": doc.output, } def list_subcommands(path_prefix: tuple[str, ...], summary: str | None = None) -> dict[str, object]: seen: set[str] = set() commands: list[dict[str, str]] = [] for doc in sorted(COMMAND_DOCS.values(), key=lambda item: item.path): if _is_hidden_path(doc.path): continue if doc.path[: len(path_prefix)] != path_prefix or len(doc.path) <= len(path_prefix): continue subcommand = doc.path[len(path_prefix)] if subcommand in seen: continue seen.add(subcommand) current_path = (*path_prefix, subcommand) is_group = has_subcommands(current_path) usage = f"tjwater-cli {' '.join(current_path)} help" if is_group else (doc.examples[0] if doc.examples else _build_usage(doc)) commands.append( { "command": " ".join(current_path), "summary": get_group_summary(current_path) if is_group else doc.summary, "usage": usage, "example": f"tjwater-cli {' '.join(current_path)} help" if is_group else _build_examples(doc)[0], } ) return { "ok": True, "schema_version": SCHEMA_VERSION, "summary": summary or get_group_summary(path_prefix), "commands": commands, } def _build_usage(doc: CommandDoc) -> str: parts = ["tjwater-cli", *doc.path] for option in doc.options: placeholder = option.name.upper().replace("-", "_") if option.required: parts.extend([f"--{option.name}", f"<{placeholder}>"]) else: parts.append(f"[--{option.name} <{placeholder}>]") return " ".join(parts)