feat(simulation): 支持冲洗阀门状态与设置值
Generic Container CI/CD / test-build-publish (push) Successful in 2m39s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 2m40s

This commit is contained in:
2026-08-17 18:28:57 +08:00
parent c250e97b87
commit 2581631b51
7 changed files with 397 additions and 29 deletions
+4
View File
@@ -316,6 +316,7 @@ def flushing_analysis(
flushing_flow: float = 0,
scheme_name: str = None,
username: str | None = None,
valve_control: dict[str, dict] = None,
) -> None:
"""
管道冲洗模拟
@@ -323,6 +324,7 @@ def flushing_analysis(
:param modify_pattern_start_time: 模拟开始时间,格式为'2024-11-25T09:00:00+08:00'
:param modify_total_duration: 模拟总历时,秒
:param modify_valve_opening: dict中包含多个阀门开启度,str为阀门的id,float为修改后的阀门开启度
:param valve_control: dict中可分别指定阀门的status、setting和k
:param drainage_node_ID: 冲洗排放口所在节点ID
:param flushing_flow: 冲洗水量,传入参数单位为m3/h
:param scheme_name: 方案名称
@@ -334,6 +336,7 @@ def flushing_analysis(
scheme_detail: dict = {
"duration": modify_total_duration,
"valve_opening": modify_valve_opening,
"valve_control": valve_control,
"drainage_node_ID": drainage_node_ID,
"flushing_flow": flushing_flow,
}
@@ -450,6 +453,7 @@ def flushing_analysis(
modify_pattern_start_time=modify_pattern_start_time,
modify_total_duration=modify_total_duration,
modify_valve_opening=modify_valve_opening,
valve_control=valve_control,
scheme_type="flushing_analysis",
scheme_name=scheme_name,
)
+80 -9
View File
@@ -1,4 +1,4 @@
from typing import Any, List, Optional
from typing import Any, List, Literal, Optional
from datetime import datetime, timedelta
import json
import threading
@@ -302,12 +302,20 @@ async def valve_isolation_endpoint(
return result
@router.post("/flushing-analyses", response_class=PlainTextResponse, summary="冲洗分析(高级)", description="高级版本的冲洗分析,支持同时开启多个阀门进行冲洗,指定排污节点,并设置固定的冲洗流量。返回纯文本格式的分析结果。")
@router.post("/flushing-analyses", response_class=PlainTextResponse, summary="冲洗分析(高级)", description="高级版本的冲洗分析,支持按状态和设置值控制多个可选阀门,指定排污节点,并设置固定的冲洗流量。返回纯文本格式的分析结果。")
async def fastapi_flushing_analysis(
network: str = Query(..., description="管网名称(或数据库名称)"),
start_time: str = Query(..., description="冲洗开始时间(ISO 8601格式)"),
valves: List[str] = Query(..., description="要开启的阀门ID列表"),
valves_k: List[float] = Query(..., description="对应各阀门的开度列表(0-1"),
valves: List[str] | None = Query(None, description="参与控制的阀门ID列表(可选)"),
valves_k: List[float] | None = Query(
None, description="对应各阀门的开度列表(0-1,可选,与valves同时提供)"
),
valve_statuses: List[Literal["OPEN", "CLOSED", "ACTIVE"]] | None = Query(
None, description="对应各阀门的开关状态列表(OPEN、CLOSED或ACTIVE,可选)"
),
valve_settings: List[str] | None = Query(
None, description="对应各阀门的设置值列表(ACTIVE状态下必填)"
),
drainage_node_ID: str = Query(..., description="排污节点ID"),
flush_flow: float = Query(0, description="冲洗流量(L/s),0表示自动计算"),
duration: int | None = Query(None, description="模拟持续时间(秒),默认900秒"),
@@ -319,8 +327,10 @@ async def fastapi_flushing_analysis(
- **network**: 管网名称(或数据库名称)
- **start_time**: 冲洗开始时间
- **valves**: 要开启的阀门ID列表
- **valves_k**: 各阀门的开度列表(0-1,与valves对应
- **valves**: 参与控制的阀门ID列表(可选)
- **valves_k**: 各阀门的开度列表(0-1,可选,与valves同时提供
- **valve_statuses**: 各阀门的开关状态列表(OPEN、CLOSED或ACTIVE,可选)
- **valve_settings**: 各阀门的设置值列表(ACTIVE状态下必填)
- **drainage_node_ID**: 排污节点ID
- **flush_flow**: 冲洗流量(L/s
- **duration**: 模拟持续时间(秒,可选,默认900)
@@ -328,14 +338,75 @@ async def fastapi_flushing_analysis(
支持多阀联合冲洗操作。
"""
valve_opening = {
valve_id: float(valves_k[idx]) for idx, valve_id in enumerate(valves)
}
valve_opening = None
valve_control = None
if valve_statuses is not None and valves_k is not None:
raise HTTPException(
status_code=422,
detail="valve_statuses 和 valves_k 不能同时提供",
)
if valve_settings is not None and valve_statuses is None:
raise HTTPException(
status_code=422,
detail="valve_settings 必须与 valve_statuses 同时提供",
)
if valves is None:
if (
valves_k is not None
or valve_statuses is not None
or valve_settings is not None
):
raise HTTPException(
status_code=422,
detail="阀门控制参数必须与 valves 同时提供",
)
elif valve_statuses is not None:
if len(valves) != len(valve_statuses):
raise HTTPException(
status_code=422, detail="valves 和 valve_statuses 的数量必须一致"
)
if valve_settings is not None and len(valves) != len(valve_settings):
raise HTTPException(
status_code=422, detail="valves 和 valve_settings 的数量必须一致"
)
settings = valve_settings or [""] * len(valves)
valve_control = {}
for valve_id, raw_status, raw_setting in zip(
valves, valve_statuses, settings
):
status = raw_status
setting = raw_setting.strip()
if status == "ACTIVE" and not setting:
raise HTTPException(
status_code=422,
detail=f"ACTIVE 状态的阀门 {valve_id} 必须提供设置值",
)
control: dict[str, str] = {"status": status}
if status == "ACTIVE":
control["setting"] = setting
valve_control[valve_id] = control
elif valves_k is not None:
if len(valves) != len(valves_k):
raise HTTPException(
status_code=422, detail="valves 和 valves_k 的数量必须一致"
)
valve_opening = {
valve_id: float(valve_k)
for valve_id, valve_k in zip(valves, valves_k)
}
else:
raise HTTPException(
status_code=422,
detail="提供 valves 时必须同时提供 valve_statuses 或 valves_k",
)
result = flushing_analysis(
name=network,
modify_pattern_start_time=start_time,
modify_total_duration=duration or 900,
modify_valve_opening=valve_opening,
valve_control=valve_control,
drainage_node_ID=drainage_node_ID,
flushing_flow=flush_flow,
scheme_name=scheme_name,
+29 -2
View File
@@ -686,6 +686,28 @@ def get_history_pattern_info(project_name, pattern_name):
return flow_list, factor_list
def _apply_valve_control(
project_name: str, valve_control: dict[str, dict]
) -> None:
"""Apply explicit valve status, setting, and opening controls."""
for valve_name, control in valve_control.items():
valve_status = get_status(project_name, valve_name)
if "status" in control:
valve_status["status"] = control["status"]
if "setting" in control:
valve_status["setting"] = control["setting"]
if "k" in control:
valve_k = control["k"]
if valve_k == 0:
valve_status["status"] = "CLOSED"
else:
valve_status["setting"] = 0.1036 * pow(valve_k, -3.105)
cs = ChangeSet()
cs.append(valve_status)
set_status(project_name, cs)
# 2025/01/11
def run_simulation(
name: str,
@@ -701,6 +723,7 @@ def run_simulation(
modify_valve_opening: dict[str, float] = None,
scheme_type: str = None,
scheme_name: str = None,
valve_control: dict[str, dict] = None,
) -> None:
"""
传入需要修改的参数,改变数据库中对应位置的值,然后计算,返回结果
@@ -715,6 +738,7 @@ def run_simulation(
:param modify_fixed_pump_pattern: dict中包含多个水泵模式,str为工频水泵的id,list为修改后的pattern
:param modify_variable_pump_pattern: dict中包含多个水泵模式,str为变频水泵的id,list为修改后的pattern
:param modify_valve_opening: dict中包含多个阀门开启度,str为阀门的id,float为修改后的阀门开启度
:param valve_control: dict中可分别指定阀门的status、setting和k;存在时优先于modify_valve_opening
:param scheme_type: 模拟方案类型
:param scheme_name:模拟方案名称
:return:
@@ -1200,8 +1224,11 @@ def run_simulation(
cs = ChangeSet()
cs.append(pump_pattern)
set_pattern(name_c, cs)
# 修改阀门(valve)的状态setting和status
if modify_valve_opening:
# 显式阀门控制沿用 run_simulation_ex 的处理顺序和覆盖规则。
if valve_control is not None:
_apply_valve_control(name_c, valve_control)
# 保留原开度参数逻辑,兼容现有方案调用。
elif modify_valve_opening:
for valve_name in modify_valve_opening.keys():
if not np.isnan(modify_valve_opening[valve_name]):
valve_status = get_status(name_c, valve_name)