refactor(db)!: adopt project-routed pooled databases
Reorganize WNDB by responsibility and remove legacy scheme endpoints.\n\nRoute analysis and time-series access through project pools, preserve transactional realtime replacement, and refresh GIS materialized views after writes.\n\nAdd database architecture documentation, live pooling coverage, API contract updates, and executable container verification.\n\nBREAKING CHANGE: legacy scheme APIs and flat app.native.wndb module imports are removed.
This commit is contained in:
@@ -5,15 +5,16 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from psycopg import AsyncConnection
|
||||
from uuid import UUID
|
||||
|
||||
import app.native.wndb as wndb
|
||||
from app.algorithms.cleaning.flow import clean_flow_data_df_kf
|
||||
from app.algorithms.cleaning.pressure import clean_pressure_data_df_km
|
||||
from app.algorithms.health.analyzer import PipelineHealthAnalyzer
|
||||
from app.infra.db.postgresql.scada import ScadaInfoRepository
|
||||
from app.infra.db.timescaledb.repositories.realtime import RealtimeRepository
|
||||
from app.infra.db.timescaledb.repositories.scheme import SchemeRepository
|
||||
from app.infra.db.timescaledb.repositories.analysis import AnalysisResultsRepository
|
||||
from app.infra.db.timescaledb.repositories.scada import ScadaRepository
|
||||
from app.native.wndb.model.pipes import get_pipes_by_property
|
||||
|
||||
|
||||
class CompositeQueries:
|
||||
@@ -26,7 +27,7 @@ class CompositeQueries:
|
||||
postgres_conn: AsyncConnection,
|
||||
) -> Dict[str, Dict[str, Any]]:
|
||||
scadas = await ScadaInfoRepository.get_scadas(postgres_conn)
|
||||
return {scada["id"]: scada for scada in scadas}
|
||||
return {scada["device_id"]: scada for scada in scadas}
|
||||
|
||||
@staticmethod
|
||||
async def get_scada_associated_realtime_simulation_data(
|
||||
@@ -63,8 +64,12 @@ class CompositeQueries:
|
||||
if not target_scada:
|
||||
raise ValueError(f"SCADA device {device_id} not found")
|
||||
|
||||
element_id = target_scada["associated_element_id"]
|
||||
scada_type = target_scada["type"]
|
||||
scada_type = target_scada["device_type"]
|
||||
element_id = (
|
||||
target_scada["link_id"]
|
||||
if scada_type in {"pipe_flow", "flow"}
|
||||
else target_scada["node_id"]
|
||||
)
|
||||
|
||||
if scada_type == "pipe_flow":
|
||||
# 查询 link 模拟数据
|
||||
@@ -85,17 +90,16 @@ class CompositeQueries:
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def get_scada_associated_scheme_simulation_data(
|
||||
async def get_scada_associated_analysis_simulation_data(
|
||||
timescale_conn: AsyncConnection,
|
||||
postgres_conn: AsyncConnection,
|
||||
device_ids: List[str],
|
||||
start_time: datetime,
|
||||
end_time: datetime,
|
||||
scheme_type: str,
|
||||
scheme_name: str,
|
||||
run_id: UUID,
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""
|
||||
获取 SCADA 关联的 link/node scheme 模拟值
|
||||
获取 SCADA 关联的 link/node 分析模拟值
|
||||
|
||||
根据传入的 SCADA device_ids,找到关联的 link/node,
|
||||
并根据对应的 type,查询对应的模拟数据
|
||||
@@ -121,30 +125,22 @@ class CompositeQueries:
|
||||
if not target_scada:
|
||||
raise ValueError(f"SCADA device {device_id} not found")
|
||||
|
||||
element_id = target_scada["associated_element_id"]
|
||||
scada_type = target_scada["type"]
|
||||
scada_type = target_scada["device_type"]
|
||||
element_id = (
|
||||
target_scada["link_id"]
|
||||
if scada_type in {"pipe_flow", "flow"}
|
||||
else target_scada["node_id"]
|
||||
)
|
||||
|
||||
if scada_type == "pipe_flow":
|
||||
# 查询 link 模拟数据
|
||||
res = await SchemeRepository.get_link_field_by_scheme_and_time_range(
|
||||
timescale_conn,
|
||||
scheme_type,
|
||||
scheme_name,
|
||||
start_time,
|
||||
end_time,
|
||||
element_id,
|
||||
"flow",
|
||||
res = await AnalysisResultsRepository.get_link_series(
|
||||
timescale_conn, run_id, element_id, start_time, end_time, "flow"
|
||||
)
|
||||
elif scada_type == "pressure":
|
||||
# 查询 node 模拟数据
|
||||
res = await SchemeRepository.get_node_field_by_scheme_and_time_range(
|
||||
timescale_conn,
|
||||
scheme_type,
|
||||
scheme_name,
|
||||
start_time,
|
||||
end_time,
|
||||
element_id,
|
||||
"pressure",
|
||||
res = await AnalysisResultsRepository.get_node_series(
|
||||
timescale_conn, run_id, element_id, start_time, end_time, "pressure"
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unknown SCADA type: {scada_type}")
|
||||
@@ -201,16 +197,15 @@ class CompositeQueries:
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def get_scheme_simulation_data(
|
||||
async def get_analysis_simulation_data(
|
||||
timescale_conn: AsyncConnection,
|
||||
feature_infos: List[Tuple[str, str]],
|
||||
start_time: datetime,
|
||||
end_time: datetime,
|
||||
scheme_type: str,
|
||||
scheme_name: str,
|
||||
run_id: UUID,
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""
|
||||
获取 link/node scheme 模拟值
|
||||
获取 link/node 分析模拟值
|
||||
|
||||
根据传入的 feature_infos,找到关联的 link/node,
|
||||
并根据对应的 type,查询对应的模拟数据
|
||||
@@ -220,8 +215,7 @@ class CompositeQueries:
|
||||
feature_infos: 传入的 feature 信息列表,包含 (element_id, type)
|
||||
start_time: 开始时间
|
||||
end_time: 结束时间
|
||||
scheme_type: 工况类型
|
||||
scheme_name: 工况名称
|
||||
run_id: 分析运行 ID
|
||||
|
||||
Returns:
|
||||
模拟数据字典,以 feature_id 为键,值为数据列表,每个数据包含 time, value 和 feature_id
|
||||
@@ -233,25 +227,13 @@ class CompositeQueries:
|
||||
for feature_id, feature_type in feature_infos:
|
||||
if feature_type.lower() == "pipe":
|
||||
# 查询 link 模拟数据
|
||||
res = await SchemeRepository.get_link_field_by_scheme_and_time_range(
|
||||
timescale_conn,
|
||||
scheme_type,
|
||||
scheme_name,
|
||||
start_time,
|
||||
end_time,
|
||||
feature_id,
|
||||
"flow",
|
||||
res = await AnalysisResultsRepository.get_link_series(
|
||||
timescale_conn, run_id, feature_id, start_time, end_time, "flow"
|
||||
)
|
||||
elif feature_type.lower() == "junction":
|
||||
# 查询 node 模拟数据
|
||||
res = await SchemeRepository.get_node_field_by_scheme_and_time_range(
|
||||
timescale_conn,
|
||||
scheme_type,
|
||||
scheme_name,
|
||||
start_time,
|
||||
end_time,
|
||||
feature_id,
|
||||
"pressure",
|
||||
res = await AnalysisResultsRepository.get_node_series(
|
||||
timescale_conn, run_id, feature_id, start_time, end_time, "pressure"
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unknown type: {feature_type}")
|
||||
@@ -296,7 +278,7 @@ class CompositeQueries:
|
||||
(
|
||||
scada
|
||||
for scada in scada_by_id.values()
|
||||
if scada["associated_element_id"] == element_id
|
||||
if (scada.get("node_id") or scada.get("link_id")) == element_id
|
||||
),
|
||||
None,
|
||||
)
|
||||
@@ -304,7 +286,7 @@ class CompositeQueries:
|
||||
if not associated_scada:
|
||||
return None
|
||||
|
||||
device_id = associated_scada["id"]
|
||||
device_id = associated_scada["device_id"]
|
||||
|
||||
data_field = "cleaned_value" if use_cleaned else "monitored_value"
|
||||
|
||||
@@ -358,7 +340,7 @@ class CompositeQueries:
|
||||
unsupported_ids = [
|
||||
device_id
|
||||
for device_id in device_ids
|
||||
if scada_by_id[device_id]["type"] not in supported_types
|
||||
if scada_by_id[device_id]["device_type"] not in supported_types
|
||||
]
|
||||
if unsupported_ids:
|
||||
raise ValueError(
|
||||
@@ -368,7 +350,7 @@ class CompositeQueries:
|
||||
device_ids = [
|
||||
device_id
|
||||
for device_id, info in scada_by_id.items()
|
||||
if info["type"] in supported_types
|
||||
if info["device_type"] in supported_types
|
||||
]
|
||||
|
||||
if not device_ids:
|
||||
@@ -409,12 +391,12 @@ class CompositeQueries:
|
||||
pressure_ids = [
|
||||
device_id
|
||||
for device_id in df.columns
|
||||
if scada_by_id[device_id]["type"] == "pressure"
|
||||
if scada_by_id[device_id]["device_type"] == "pressure"
|
||||
]
|
||||
flow_ids = [
|
||||
device_id
|
||||
for device_id in df.columns
|
||||
if scada_by_id[device_id]["type"] in {"pipe_flow", "flow"}
|
||||
if scada_by_id[device_id]["device_type"] in {"pipe_flow", "flow"}
|
||||
]
|
||||
|
||||
updated_rows = 0
|
||||
@@ -497,7 +479,7 @@ class CompositeQueries:
|
||||
|
||||
# 批量查询这些管道的详细信息
|
||||
fields = ["id", "diameter", "node1", "node2"]
|
||||
all_links = wndb.get_pipes_by_property(network_name, fields=fields)
|
||||
all_links = get_pipes_by_property(network_name, fields=fields)
|
||||
|
||||
# 转换为字典以快速查找
|
||||
links_dict = {link["id"]: link for link in all_links}
|
||||
|
||||
Reference in New Issue
Block a user