fix(burst-location): use correct data sources

Simulation mode reads scheme data for both burst and normal observations. Monitoring mode reuses the burst window when no normal window is provided.
This commit is contained in:
2026-07-08 17:51:08 +08:00
parent 18943314f8
commit 87824f3b3c
3 changed files with 240 additions and 62 deletions
+117 -17
View File
@@ -190,7 +190,7 @@ def test_run_burst_location_uses_single_timerange_with_burst_source_split(monkey
use_scada_flow=True,
)
assert result["observed_source"] == "simulation_scheme_burst_realtime_normal_timerange"
assert result["observed_source"] == "simulation_scheme_timerange"
assert result["simulation_scheme"] == {
"name": "BurstSchemeA",
"type": "burst_analysis",
@@ -199,22 +199,17 @@ def test_run_burst_location_uses_single_timerange_with_burst_source_split(monkey
assert result["flow_samples"] == {"burst": 4, "normal": 4}
assert list(captured["burst_pressure"].index) == ["J1"]
assert captured["burst_pressure"]["J1"] == pytest.approx(15.0)
assert captured["normal_pressure"]["J1"] == pytest.approx(11.0)
assert captured["normal_pressure"]["J1"] == pytest.approx(15.0)
assert captured["burst_flow"]["J2"] == pytest.approx(6.0)
assert captured["burst_flow"]["P1"] == pytest.approx(8.0)
assert captured["normal_flow"]["J2"] == pytest.approx(4.0)
assert captured["normal_flow"]["P1"] == pytest.approx(5.0)
assert captured["normal_flow"]["J2"] == pytest.approx(6.0)
assert captured["normal_flow"]["P1"] == pytest.approx(8.0)
assert all(call["scheme_name"] == "BurstSchemeA" for call in scheme_calls)
assert len(scheme_calls) == 3
assert len(scheme_calls) == 6
assert any(call["element_type"] == "node" and call["field"] == "pressure" for call in scheme_calls)
assert any(call["element_type"] == "link" and call["field"] == "flow" for call in scheme_calls)
assert any(call["element_type"] == "node" and call["field"] == "actual_demand" for call in scheme_calls)
assert len(realtime_calls) == 3
assert all(datetime.fromisoformat(call["start_time"]).hour == 8 for call in realtime_calls)
assert all(datetime.fromisoformat(call["end_time"]).hour == 9 for call in realtime_calls)
assert any(call["element_type"] == "node" and call["field"] == "pressure" for call in realtime_calls)
assert any(call["element_type"] == "link" and call["field"] == "flow" for call in realtime_calls)
assert any(call["element_type"] == "node" and call["field"] == "actual_demand" for call in realtime_calls)
assert realtime_calls == []
assert result["scada_window"] == {
"burst_start": "2025-01-01T08:00:00+00:00",
"burst_end": "2025-01-01T09:00:00+00:00",
@@ -296,7 +291,42 @@ def test_build_observed_series_from_simulation_normalizes_result_ids(monkeypatch
assert series["100026"] == pytest.approx(12.0)
def test_run_burst_location_monitoring_uses_scada_for_burst_and_realtime_for_normal(
def test_build_observed_series_from_scada_uses_chinese_error_label(monkeypatch):
module = _load_burst_location_module()
monkeypatch.setattr(
module,
"get_all_scada_info",
lambda network: [
{
"type": "pressure",
"associated_element_id": "100026",
"api_query_id": "pressure-query",
}
],
)
monkeypatch.setattr(
module.InternalQueries,
"query_scada_by_ids_timerange",
staticmethod(lambda **kwargs: {"pressure-query": []}),
)
with pytest.raises(ValueError) as exc_info:
module._build_observed_series_from_scada(
network="tjwater",
sensor_ids=["100026"],
start_dt=datetime(2025, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
end_dt=datetime(2025, 1, 1, 1, 0, 0, tzinfo=timezone.utc),
data_type="pressure",
series_name="burst_pressure",
)
message = str(exc_info.value)
assert "爆管压力数据 在时间窗内无有效数据: 100026" in message
assert "burst_pressure" not in message
def test_run_burst_location_monitoring_uses_scada_for_burst_and_normal(
monkeypatch, tmp_path
):
module = _load_burst_location_module()
@@ -324,10 +354,12 @@ def test_run_burst_location_monitoring_uses_scada_for_burst_and_realtime_for_nor
def fake_scada_query(**kwargs):
scada_calls.append(kwargs)
start_hour = datetime.fromisoformat(kwargs["start_time"]).hour
values = [20.0, 22.0] if start_hour == 8 else [10.0, 12.0]
return {
"pressure-query": [
{"time": kwargs["start_time"], "value": 20.0},
{"time": kwargs["end_time"], "value": 22.0},
{"time": kwargs["start_time"], "value": values[0]},
{"time": kwargs["end_time"], "value": values[1]},
]
}
@@ -358,10 +390,78 @@ def test_run_burst_location_monitoring_uses_scada_for_burst_and_realtime_for_nor
burst_leakage=1.0,
scada_burst_start=datetime(2025, 1, 1, 8, 0, 0),
scada_burst_end=datetime(2025, 1, 1, 9, 0, 0),
scada_normal_start=datetime(2025, 1, 1, 7, 0, 0),
scada_normal_end=datetime(2025, 1, 1, 8, 0, 0),
)
assert result["observed_source"] == "scada_burst_realtime_normal_timerange"
assert len(scada_calls) == 1
assert len(realtime_calls) == 1
assert result["observed_source"] == "scada_burst_scada_normal_timerange"
assert len(scada_calls) == 2
assert len(realtime_calls) == 0
assert captured["burst_pressure"]["J1"] == pytest.approx(21.0)
assert captured["normal_pressure"]["J1"] == pytest.approx(11.0)
assert result["scada_window"] == {
"burst_start": "2025-01-01T08:00:00+00:00",
"burst_end": "2025-01-01T09:00:00+00:00",
"normal_start": "2025-01-01T07:00:00+00:00",
"normal_end": "2025-01-01T08:00:00+00:00",
}
def test_run_burst_location_monitoring_reuses_burst_window_for_normal(
monkeypatch, tmp_path
):
module = _load_burst_location_module()
captured = {}
scada_calls = []
monkeypatch.setattr(
module,
"get_all_scada_info",
lambda network: [
{
"type": "pressure",
"associated_element_id": "J1",
"api_query_id": "pressure-query",
}
],
)
monkeypatch.setattr(module, "_prepare_burst_inp", lambda network: str(tmp_path / "fake.inp"))
monkeypatch.setattr(
module,
"run_burst_location",
lambda **kwargs: captured.update(kwargs) or {"located_pipe": "Pipe-001"},
)
monkeypatch.setattr(
module.InternalQueries,
"query_scada_by_ids_timerange",
staticmethod(
lambda **kwargs: scada_calls.append(kwargs)
or {
"pressure-query": [
{"time": kwargs["start_time"], "value": 20.0},
{"time": kwargs["end_time"], "value": 22.0},
]
}
),
)
monkeypatch.setattr(
module.InternalQueries,
"query_realtime_simulation_by_ids_timerange",
staticmethod(lambda **kwargs: pytest.fail("monitoring mode must not query realtime simulation")),
)
result = module.run_burst_location_by_network(
network="tjwater",
username="testuser",
data_source="monitoring",
burst_leakage=1.0,
scada_burst_start=datetime(2025, 1, 1, 8, 0, 0),
scada_burst_end=datetime(2025, 1, 1, 9, 0, 0),
)
assert result["observed_source"] == "scada_timerange"
assert len(scada_calls) == 2
assert scada_calls[0]["start_time"] == scada_calls[1]["start_time"]
assert scada_calls[0]["end_time"] == scada_calls[1]["end_time"]
assert captured["burst_pressure"]["J1"] == pytest.approx(21.0)
assert captured["normal_pressure"]["J1"] == pytest.approx(21.0)