feat(scada): serve project devices through pooled API

This commit is contained in:
2026-09-08 18:18:30 +08:00
parent 5966d039de
commit 8942541759
9 changed files with 269 additions and 151 deletions
-19
View File
@@ -3,7 +3,6 @@ from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query
from psycopg import AsyncConnection
from app.infra.db.postgresql.scada import ScadaInfoRepository
from app.infra.db.postgresql.analysis import AnalysisRepository
from app.auth.project_dependencies import get_project_pg_connection
@@ -17,24 +16,6 @@ async def get_database_connection(
yield conn
@router.get("/scada-info/database-view", summary="获取SCADA信息", description="使用连接池查询所有SCADA信息")
async def get_scada_info_with_connection(
conn: AsyncConnection = Depends(get_database_connection),
):
"""
获取所有SCADA信息
返回项目中所有的SCADA设备信息
"""
try:
scada_data = await ScadaInfoRepository.get_scadas(conn)
return {"success": True, "data": scada_data, "count": len(scada_data)}
except Exception as e:
raise HTTPException(
status_code=500, detail=f"查询SCADA信息时发生错误: {str(e)}"
)
@router.get("/analysis/runs", summary="获取分析运行列表")
async def get_analysis_runs(
conn: AsyncConnection = Depends(get_database_connection),
+27 -19
View File
@@ -1,34 +1,42 @@
from typing import Any
from fastapi import APIRouter, Query
from fastapi import APIRouter, Depends, HTTPException
from psycopg import AsyncConnection
from app.services.tjnetwork import (
get_all_scada_info,
get_scada_info,
get_scada_info_schema,
)
from app.auth.project_dependencies import get_project_pg_connection
from app.domain.schemas.scada import ScadaDeviceResponse
from app.infra.db.postgresql.scada import ScadaInfoRepository, get_scada_info_schema
router = APIRouter()
@router.get("/network-schemas/scada-device", summary="获取 SCADA 设备结构")
def get_scada_device_schema(
network: str = Query(..., description="管网名称(或数据库名称)"),
) -> dict[str, dict[str, Any]]:
return get_scada_info_schema(network)
def get_scada_device_schema() -> dict[str, dict[str, Any]]:
return get_scada_info_schema("")
@router.get("/scada-devices", summary="获取 SCADA 设备列表")
def get_scada_devices(
network: str = Query(..., description="管网名称(或数据库名称)"),
@router.get(
"/scada-devices",
summary="获取 SCADA 设备列表",
response_model=list[ScadaDeviceResponse],
)
async def get_scada_devices(
conn: AsyncConnection = Depends(get_project_pg_connection),
) -> list[dict[str, Any]]:
return get_all_scada_info(network)
return await ScadaInfoRepository.get_scadas(conn)
@router.get("/scada-devices/detail", summary="获取 SCADA 设备")
def get_scada_device(
network: str = Query(..., description="管网名称(或数据库名称)"),
device_id: str = Query(..., description="SCADA 设备 ID"),
@router.get(
"/scada-devices/{device_id}",
summary="获取 SCADA 设备",
response_model=ScadaDeviceResponse,
)
async def get_scada_device(
device_id: str,
conn: AsyncConnection = Depends(get_project_pg_connection),
) -> dict[str, Any]:
return get_scada_info(network, device_id)
device = await ScadaInfoRepository.get_scada(conn, device_id)
if device is None:
raise HTTPException(status_code=404, detail="SCADA 设备不存在")
return device
+18
View File
@@ -0,0 +1,18 @@
from pydantic import BaseModel
class ScadaDeviceResponse(BaseModel):
"""Project SCADA metadata keyed by the canonical device identifier."""
device_id: str
device_type: str
node_id: str | None = None
link_id: str | None = None
api_query_id: str | None = None
transmission_mode: str
transmission_frequency: str
reliability: int | None = None
x: float | None = None
y: float | None = None
longitude: float | None = None
latitude: float | None = None
+19 -1
View File
@@ -9,7 +9,9 @@ from app.native.wndb.core.database import read_all, try_read
_SCADA_VIEW_SELECT = """
SELECT id AS device_id, device_type, node_id, link_id, api_query_id,
transmission_mode, transmission_frequency, reliability, x, y
transmission_mode, transmission_frequency, reliability, x, y,
ST_X(ST_Transform(geom, 4326)) AS longitude,
ST_Y(ST_Transform(geom, 4326)) AS latitude
FROM gis.scada_devices
"""
@@ -60,6 +62,8 @@ def _device(record: dict[str, Any]) -> dict[str, Any]:
"reliability": _optional_int(record["reliability"]),
"x": _optional_float(record["x"]),
"y": _optional_float(record["y"]),
"longitude": _optional_float(record["longitude"]),
"latitude": _optional_float(record["latitude"]),
}
@@ -76,6 +80,18 @@ class ScadaInfoRepository:
return [_device(record) for record in records]
@staticmethod
async def get_scada(
conn: AsyncConnection, device_id: str
) -> dict[str, Any] | None:
async with conn.cursor() as cur:
await cur.execute(
_SCADA_VIEW_SELECT + " WHERE id = %s",
(device_id,),
)
record = await cur.fetchone()
return _device(record) if record else None
@staticmethod
async def get_existing_device_ids(
conn: AsyncConnection, device_ids: list[str]
@@ -102,6 +118,8 @@ def get_scada_info_schema(name: str) -> dict[str, dict[str, Any]]:
"reliability": {"type": "int", "optional": False, "readonly": True},
"x": {"type": "float", "optional": True, "readonly": True},
"y": {"type": "float", "optional": True, "readonly": True},
"longitude": {"type": "float", "optional": True, "readonly": True},
"latitude": {"type": "float", "optional": True, "readonly": True},
}