feat(simulation): 支持冲洗阀门状态与设置值
This commit is contained in:
@@ -383,6 +383,7 @@ def test_flushing_endpoint_passes_required_scheme_name(monkeypatch):
|
||||
"modify_pattern_start_time": "2025-01-02T03:04:05+08:00",
|
||||
"modify_total_duration": 900,
|
||||
"modify_valve_opening": {"V1": 0.5},
|
||||
"valve_control": None,
|
||||
"drainage_node_ID": "N1",
|
||||
"flushing_flow": 100.0,
|
||||
"scheme_name": "flush_case_01",
|
||||
@@ -390,6 +391,145 @@ def test_flushing_endpoint_passes_required_scheme_name(monkeypatch):
|
||||
}
|
||||
|
||||
|
||||
def test_flushing_endpoint_allows_omitting_valves(monkeypatch):
|
||||
module = _load_simulation_module(monkeypatch)
|
||||
captured = {}
|
||||
|
||||
def fake_flushing_analysis(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return "ok"
|
||||
|
||||
monkeypatch.setattr(module, "flushing_analysis", fake_flushing_analysis)
|
||||
client = _build_authenticated_client(module)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/flushing-analyses",
|
||||
params={
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"drainage_node_ID": "N1",
|
||||
"scheme_name": "flush_without_valves",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.text == "ok"
|
||||
assert captured["modify_valve_opening"] is None
|
||||
assert captured["valve_control"] is None
|
||||
assert captured["drainage_node_ID"] == "N1"
|
||||
|
||||
|
||||
def test_flushing_endpoint_passes_explicit_valve_control(monkeypatch):
|
||||
module = _load_simulation_module(monkeypatch)
|
||||
captured = {}
|
||||
|
||||
def fake_flushing_analysis(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return "ok"
|
||||
|
||||
monkeypatch.setattr(module, "flushing_analysis", fake_flushing_analysis)
|
||||
client = _build_authenticated_client(module)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/flushing-analyses",
|
||||
params=[
|
||||
("network", "demo"),
|
||||
("start_time", "2025-01-02T03:04:05+08:00"),
|
||||
("valves", "V1"),
|
||||
("valves", "V2"),
|
||||
("valve_statuses", "ACTIVE"),
|
||||
("valve_statuses", "CLOSED"),
|
||||
("valve_settings", "2.5"),
|
||||
("valve_settings", ""),
|
||||
("drainage_node_ID", "N1"),
|
||||
("scheme_name", "flush_with_valve_control"),
|
||||
],
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert captured["modify_valve_opening"] is None
|
||||
assert captured["valve_control"] == {
|
||||
"V1": {"status": "ACTIVE", "setting": "2.5"},
|
||||
"V2": {"status": "CLOSED"},
|
||||
}
|
||||
|
||||
|
||||
def test_flushing_endpoint_requires_setting_for_active_valve(monkeypatch):
|
||||
module = _load_simulation_module(monkeypatch)
|
||||
client = _build_authenticated_client(module)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/flushing-analyses",
|
||||
params={
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"valves": "V1",
|
||||
"valve_statuses": "ACTIVE",
|
||||
"drainage_node_ID": "N1",
|
||||
"scheme_name": "flush_without_active_setting",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_flushing_endpoint_rejects_mixed_valve_control_modes(monkeypatch):
|
||||
module = _load_simulation_module(monkeypatch)
|
||||
client = _build_authenticated_client(module)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/flushing-analyses",
|
||||
params={
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"valves": "V1",
|
||||
"valves_k": 0.5,
|
||||
"valve_statuses": "ACTIVE",
|
||||
"valve_settings": "2.5",
|
||||
"drainage_node_ID": "N1",
|
||||
"scheme_name": "flush_with_mixed_controls",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_flushing_endpoint_rejects_settings_without_statuses(monkeypatch):
|
||||
module = _load_simulation_module(monkeypatch)
|
||||
client = _build_authenticated_client(module)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/flushing-analyses",
|
||||
params={
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"valves": "V1",
|
||||
"valves_k": 0.5,
|
||||
"valve_settings": "2.5",
|
||||
"drainage_node_ID": "N1",
|
||||
"scheme_name": "flush_with_orphan_settings",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_flushing_endpoint_requires_drainage_node(monkeypatch):
|
||||
module = _load_simulation_module(monkeypatch)
|
||||
client = _build_authenticated_client(module)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/flushing-analyses",
|
||||
params={
|
||||
"network": "demo",
|
||||
"start_time": "2025-01-02T03:04:05+08:00",
|
||||
"scheme_name": "flush_without_drainage_node",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_contaminant_endpoint_passes_current_username(monkeypatch):
|
||||
module = _load_simulation_module(monkeypatch)
|
||||
captured = {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import inspect
|
||||
import json
|
||||
from datetime import timedelta
|
||||
|
||||
@@ -7,6 +8,70 @@ from app.infra.db.timescaledb.repositories.scheme import SchemeRepository
|
||||
from app.services.time_api import parse_utc_time
|
||||
|
||||
|
||||
def test_run_simulation_exposes_explicit_valve_control():
|
||||
from app.services import simulation
|
||||
|
||||
parameters = inspect.signature(simulation.run_simulation).parameters
|
||||
|
||||
assert "valve_control" in parameters
|
||||
|
||||
|
||||
def test_apply_valve_control_matches_run_simulation_ex_semantics(monkeypatch):
|
||||
from app.services import simulation
|
||||
|
||||
updates: dict[str, dict] = {}
|
||||
|
||||
monkeypatch.setattr(
|
||||
simulation,
|
||||
"get_status",
|
||||
lambda project_name, valve_name: {
|
||||
"link": valve_name,
|
||||
"status": "OPEN",
|
||||
"setting": 1.0,
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
simulation,
|
||||
"set_status",
|
||||
lambda project_name, changeset: updates.update(
|
||||
{
|
||||
changeset.operations[0]["link"]: changeset.operations[0].copy()
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
simulation._apply_valve_control(
|
||||
"demo",
|
||||
{
|
||||
"V-status": {"status": "ACTIVE"},
|
||||
"V-setting": {"setting": 2.5},
|
||||
"V-closed": {"status": "ACTIVE", "setting": 9.0, "k": 0},
|
||||
"V-k": {"status": "ACTIVE", "setting": 9.0, "k": 0.5},
|
||||
},
|
||||
)
|
||||
|
||||
assert updates["V-status"] == {
|
||||
"link": "V-status",
|
||||
"status": "ACTIVE",
|
||||
"setting": 1.0,
|
||||
}
|
||||
assert updates["V-setting"] == {
|
||||
"link": "V-setting",
|
||||
"status": "OPEN",
|
||||
"setting": 2.5,
|
||||
}
|
||||
assert updates["V-closed"] == {
|
||||
"link": "V-closed",
|
||||
"status": "CLOSED",
|
||||
"setting": 9.0,
|
||||
}
|
||||
assert updates["V-k"] == {
|
||||
"link": "V-k",
|
||||
"status": "ACTIVE",
|
||||
"setting": 0.1036 * pow(0.5, -3.105),
|
||||
}
|
||||
|
||||
|
||||
def _node_result(periods: int) -> list[dict]:
|
||||
return [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user