fix(burst-location): tolerate partial SCADA gaps

This commit is contained in:
2026-07-09 10:49:53 +08:00
parent e862e6c500
commit 6a16ea44b2
2 changed files with 146 additions and 3 deletions
+108
View File
@@ -326,6 +326,51 @@ def test_build_observed_series_from_scada_uses_chinese_error_label(monkeypatch):
assert "burst_pressure" not in message
def test_build_observed_series_from_scada_skips_missing_sensor_values(monkeypatch):
module = _load_burst_location_module()
monkeypatch.setattr(
module,
"get_all_scada_info",
lambda network: [
{"type": "pressure", "associated_element_id": "J1", "api_query_id": "q1"},
{"type": "pressure", "associated_element_id": "J2", "api_query_id": "q2"},
{"type": "pressure", "associated_element_id": "J3", "api_query_id": "q3"},
],
)
monkeypatch.setattr(
module.InternalQueries,
"query_scada_by_ids_timerange",
staticmethod(
lambda **kwargs: {
"q1": [
{"time": kwargs["start_time"], "value": 10.0},
{"time": kwargs["end_time"], "value": 12.0},
],
"q2": [],
"q3": [
{"time": kwargs["start_time"], "value": None},
{"time": kwargs["end_time"], "value": 18.0},
],
}
),
)
series, sample_count = module._build_observed_series_from_scada(
network="tjwater",
sensor_ids=["J1", "J2", "J3"],
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",
)
assert list(series.index) == ["J1", "J3"]
assert series["J1"] == pytest.approx(11.0)
assert series["J3"] == pytest.approx(18.0)
assert sample_count == 1
def test_run_burst_location_monitoring_uses_scada_for_burst_and_normal(
monkeypatch, tmp_path
):
@@ -465,3 +510,66 @@ def test_run_burst_location_monitoring_reuses_burst_window_for_normal(
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)
def test_run_burst_location_monitoring_aligns_partial_scada_data(
monkeypatch, tmp_path
):
module = _load_burst_location_module()
captured = {}
monkeypatch.setattr(
module,
"get_all_scada_info",
lambda network: [
{"type": "pressure", "associated_element_id": "J1", "api_query_id": "q1"},
{"type": "pressure", "associated_element_id": "J2", "api_query_id": "q2"},
{"type": "pressure", "associated_element_id": "J3", "api_query_id": "q3"},
],
)
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"},
)
def fake_scada_query(**kwargs):
start_hour = datetime.fromisoformat(kwargs["start_time"]).astimezone(
timezone(timedelta(hours=8))
).hour
if start_hour == 8:
return {
"q1": [{"time": kwargs["start_time"], "value": 20.0}],
"q2": [{"time": kwargs["start_time"], "value": 30.0}],
"q3": [],
}
return {
"q1": [{"time": kwargs["start_time"], "value": 10.0}],
"q2": [],
"q3": [{"time": kwargs["start_time"], "value": 12.0}],
}
monkeypatch.setattr(
module.InternalQueries,
"query_scada_by_ids_timerange",
staticmethod(fake_scada_query),
)
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, tzinfo=timezone(timedelta(hours=8))),
scada_burst_end=datetime(2025, 1, 1, 9, 0, 0, tzinfo=timezone(timedelta(hours=8))),
scada_normal_start=datetime(2025, 1, 1, 7, 0, 0, tzinfo=timezone(timedelta(hours=8))),
scada_normal_end=datetime(2025, 1, 1, 8, 0, 0, tzinfo=timezone(timedelta(hours=8))),
)
assert result["pressure_scada_ids"] == ["J1"]
assert captured["pressure_scada_ids"] == ["J1"]
assert list(captured["burst_pressure"].index) == ["J1"]
assert list(captured["normal_pressure"].index) == ["J1"]
assert captured["burst_pressure"]["J1"] == pytest.approx(20.0)
assert captured["normal_pressure"]["J1"] == pytest.approx(10.0)