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:
@@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Body
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -43,8 +44,7 @@ class BurstDetectionRequest(BaseModel):
|
||||
sensor_nodes: list[str] | None = Field(None, description="传感器节点列表")
|
||||
scheme_name: str | None = Field(None, description="方案名称")
|
||||
data_source: str = Field("monitoring", description="数据来源:monitoring(监测)或simulation(模拟)")
|
||||
simulation_scheme_name: str | None = Field(None, description="模拟方案名称")
|
||||
simulation_scheme_type: str | None = Field(None, description="模拟方案类型")
|
||||
simulation_run_id: UUID | None = Field(None, description="分析模拟运行 ID")
|
||||
|
||||
|
||||
@router.post(
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Any
|
||||
from datetime import datetime
|
||||
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Body
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -32,9 +33,8 @@ class BurstLocationRequest(BaseModel):
|
||||
scada_normal_start: datetime | None = Field(None, description="监测数据正常工况开始时间")
|
||||
scada_normal_end: datetime | None = Field(None, description="监测数据正常工况结束时间")
|
||||
use_scada_flow: bool = Field(False, description="是否使用SCADA流量数据")
|
||||
scheme_name: str | None = Field(None, description="方案名称")
|
||||
simulation_scheme_name: str | None = Field(None, description="模拟方案名称")
|
||||
simulation_scheme_type: str | None = Field(None, description="模拟方案类型")
|
||||
scheme_name: str | None = Field(None, description="爆管定位运行名称")
|
||||
simulation_run_id: UUID | None = Field(None, description="分析模拟运行 ID")
|
||||
|
||||
|
||||
@router.post(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
get_control,
|
||||
get_control_schema,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_curve,
|
||||
delete_curve,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
get_energy,
|
||||
get_energy_schema,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_pattern,
|
||||
delete_pattern,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_mixing,
|
||||
add_source,
|
||||
api,
|
||||
delete_mixing,
|
||||
delete_source,
|
||||
get_emitter,
|
||||
@@ -23,6 +21,7 @@ from app.services.tjnetwork import (
|
||||
get_tank_reaction,
|
||||
get_tank_reaction_schema,
|
||||
set_emitter,
|
||||
set_mixing,
|
||||
set_pipe_reaction,
|
||||
set_quality,
|
||||
set_reaction,
|
||||
@@ -270,7 +269,7 @@ async def fastapi_set_mixing(
|
||||
更新指定水池的混合属性值。
|
||||
"""
|
||||
props = await req.json()
|
||||
return api.set_mixing(network, ChangeSet(props))
|
||||
return set_mixing(network, ChangeSet(props))
|
||||
|
||||
@router.post("/mixing-configurations", response_model=None, summary="添加混合", description="在网络中添加一个新的混合")
|
||||
async def fastapi_add_mixing(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body, Response
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_label,
|
||||
add_vertex,
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
from typing import List, Any
|
||||
from fastapi import APIRouter, Request, HTTPException, Query, Body
|
||||
from app.services.tjnetwork import (
|
||||
ChangeSet,
|
||||
get_all_extension_data_keys,
|
||||
get_all_extension_data,
|
||||
get_extension_data,
|
||||
set_extension_data
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get(
|
||||
"/all-extension-data-keys",
|
||||
summary="获取所有扩展数据键",
|
||||
description="获取指定网络的所有扩展数据的键列表"
|
||||
)
|
||||
async def get_all_extension_data_keys_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[str]:
|
||||
"""
|
||||
获取所有扩展数据键。
|
||||
|
||||
返回指定网络中所有可用的扩展数据键。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
扩展数据键列表
|
||||
"""
|
||||
return get_all_extension_data_keys(network)
|
||||
|
||||
@router.get(
|
||||
"/all-extension-datas",
|
||||
summary="获取所有扩展数据",
|
||||
description="获取指定网络的所有扩展数据"
|
||||
)
|
||||
async def get_all_extension_data_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取所有扩展数据。
|
||||
|
||||
返回指定网络的所有扩展数据及其值。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
扩展数据字典
|
||||
"""
|
||||
return get_all_extension_data(network)
|
||||
|
||||
@router.get(
|
||||
"/extension-datas",
|
||||
summary="获取指定扩展数据",
|
||||
description="获取指定网络中指定键的扩展数据值"
|
||||
)
|
||||
async def get_extension_data_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
key: str = Query(..., description="扩展数据键")
|
||||
) -> str | None:
|
||||
"""
|
||||
获取指定扩展数据。
|
||||
|
||||
返回指定网络中指定键对应的扩展数据值。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
key: 扩展数据键
|
||||
|
||||
Returns:
|
||||
扩展数据值,如果不存在返回None
|
||||
"""
|
||||
return get_extension_data(network, key)
|
||||
|
||||
@router.patch(
|
||||
"/extension-datas",
|
||||
response_model=None,
|
||||
summary="设置扩展数据",
|
||||
description="设置指定网络中的扩展数据"
|
||||
)
|
||||
async def set_extension_data_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
设置扩展数据。
|
||||
|
||||
在指定网络中设置扩展数据,并返回变更集信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 包含扩展数据的请求体
|
||||
|
||||
Returns:
|
||||
变更集信息
|
||||
"""
|
||||
props = await req.json()
|
||||
print(props)
|
||||
cs = set_extension_data(network, ChangeSet(props))
|
||||
print(cs.operations[0])
|
||||
return cs
|
||||
@@ -1,62 +0,0 @@
|
||||
from typing import Any
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi import status
|
||||
from pydantic import BaseModel
|
||||
from app.services.tjnetwork import (
|
||||
get_all_sensor_placements,
|
||||
get_all_burst_locate_results,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
async def fastapi_get_json():
|
||||
"""
|
||||
获取JSON示例
|
||||
|
||||
返回示例JSON格式的响应
|
||||
"""
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
content={
|
||||
"code": 400,
|
||||
"message": "this is message",
|
||||
"data": 123,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/sensor-placement-schemes", summary="获取所有传感器位置", description="获取网络中所有传感器的放置位置信息")
|
||||
async def fastapi_get_all_sensor_placements(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
||||
"""
|
||||
获取所有传感器位置
|
||||
|
||||
返回网络中所有传感器的放置位置及其配置信息
|
||||
"""
|
||||
return get_all_sensor_placements(network)
|
||||
|
||||
|
||||
@router.get("/burst-locations", summary="获取所有爆管定位结果", description="获取网络中所有爆管定位的分析结果")
|
||||
async def fastapi_get_all_burst_locate_results(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
||||
"""
|
||||
获取所有爆管定位结果
|
||||
|
||||
返回网络中所有的爆管定位分析结果
|
||||
"""
|
||||
return get_all_burst_locate_results(network)
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
"""测试数据模型"""
|
||||
str_info: str
|
||||
|
||||
|
||||
async def fastapi_test_dict(data: Item) -> dict[str, str]:
|
||||
"""
|
||||
测试字典处理
|
||||
|
||||
接收Item模型,返回其字典格式
|
||||
"""
|
||||
item = data.dict()
|
||||
return item
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
calculate_demand_to_network,
|
||||
calculate_demand_to_nodes,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
delete_junction,
|
||||
delete_pipe,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_junction,
|
||||
delete_junction,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
PIPE_STATUS_OPEN,
|
||||
add_pipe,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_pump,
|
||||
delete_pump,
|
||||
|
||||
@@ -1,534 +1,73 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Query, Request
|
||||
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_district_metering_area,
|
||||
add_region,
|
||||
add_service_area,
|
||||
add_virtual_district,
|
||||
calculate_district_metering_area_for_network,
|
||||
calculate_district_metering_area_for_nodes,
|
||||
calculate_district_metering_area_for_region,
|
||||
calculate_service_area,
|
||||
calculate_virtual_district,
|
||||
delete_district_metering_area,
|
||||
delete_region,
|
||||
delete_service_area,
|
||||
delete_virtual_district,
|
||||
generate_district_metering_area,
|
||||
generate_service_area,
|
||||
generate_sub_district_metering_area,
|
||||
generate_virtual_district,
|
||||
get_all_district_metering_area_ids,
|
||||
get_all_district_metering_areas,
|
||||
get_all_service_areas,
|
||||
get_all_virtual_districts,
|
||||
get_district_metering_area,
|
||||
get_district_metering_area_schema,
|
||||
get_nodes_in_region,
|
||||
get_region,
|
||||
get_region_schema,
|
||||
get_service_area,
|
||||
get_service_area_schema,
|
||||
get_virtual_district,
|
||||
get_virtual_district_schema,
|
||||
set_district_metering_area,
|
||||
get_regions,
|
||||
set_region,
|
||||
set_service_area,
|
||||
set_virtual_district,
|
||||
)
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
############################################################
|
||||
# region 32
|
||||
############################################################
|
||||
|
||||
@router.get(
|
||||
"/network-schemas/region",
|
||||
summary="获取区域属性架构",
|
||||
description="获取指定水网的区域属性架构定义"
|
||||
)
|
||||
async def fastapi_get_region_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
@router.get("/network-schemas/region", summary="获取区域属性架构")
|
||||
async def get_region_schema_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""获取区域的属性架构。"""
|
||||
return get_region_schema(network)
|
||||
|
||||
@router.get(
|
||||
"/regions/detail",
|
||||
summary="获取区域信息",
|
||||
description="获取指定ID的区域详细信息"
|
||||
)
|
||||
async def fastapi_get_region(
|
||||
|
||||
@router.get("/regions", summary="获取区域列表")
|
||||
async def get_regions_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="区域ID")
|
||||
) -> list[dict[str, Any]]:
|
||||
return [get_region(network, region_id) for region_id in get_regions(network)]
|
||||
|
||||
|
||||
@router.get("/regions/detail", summary="获取区域信息")
|
||||
async def get_region_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="区域 ID"),
|
||||
) -> dict[str, Any]:
|
||||
"""获取区域的详细信息。"""
|
||||
return get_region(network, id)
|
||||
|
||||
@router.patch(
|
||||
"/regions",
|
||||
response_model=None,
|
||||
summary="设置区域属性",
|
||||
description="修改指定区域的属性信息"
|
||||
)
|
||||
async def fastapi_set_region(
|
||||
|
||||
@router.get("/regions/nodes", summary="获取区域节点")
|
||||
async def get_region_nodes_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""设置区域属性。"""
|
||||
props = await req.json()
|
||||
return set_region(network, ChangeSet(props))
|
||||
|
||||
@router.post(
|
||||
"/regions",
|
||||
response_model=None,
|
||||
summary="添加新区域",
|
||||
description="向水网添加一个新的区域"
|
||||
)
|
||||
async def fastapi_add_region(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""添加新的区域。"""
|
||||
props = await req.json()
|
||||
return add_region(network, ChangeSet(props))
|
||||
|
||||
@router.delete(
|
||||
"/regions",
|
||||
response_model=None,
|
||||
summary="删除区域",
|
||||
description="删除指定的区域"
|
||||
)
|
||||
async def fastapi_delete_region(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""删除区域。"""
|
||||
props = await req.json()
|
||||
return delete_region(network, ChangeSet(props))
|
||||
|
||||
|
||||
############################################################
|
||||
# district_metering_area 33
|
||||
############################################################
|
||||
|
||||
@router.post(
|
||||
"/district-metering-areas/for-region",
|
||||
summary="计算区域内DMA分区",
|
||||
description="为指定区域计算区域计量(DMA)分区方案"
|
||||
)
|
||||
async def fastapi_calculate_district_metering_area_for_region(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> list[list[str]]:
|
||||
"""
|
||||
计算区域内DMA分区。
|
||||
|
||||
请求体格式:
|
||||
{
|
||||
"region": 区域ID(str),
|
||||
"part_count": 分区数量(int),
|
||||
"part_type": 分区类型(int)
|
||||
}
|
||||
"""
|
||||
props = await req.json()
|
||||
region = props["region"]
|
||||
part_count = props["part_count"]
|
||||
part_type = props["part_type"]
|
||||
return calculate_district_metering_area_for_region(
|
||||
network, region, part_count, part_type
|
||||
)
|
||||
|
||||
@router.post(
|
||||
"/district-metering-areas/for-network",
|
||||
summary="计算整网DMA分区",
|
||||
description="为整个水网计算区域计量(DMA)分区方案"
|
||||
)
|
||||
async def fastapi_calculate_district_metering_area_for_network(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> list[list[str]]:
|
||||
"""
|
||||
计算整网DMA分区。
|
||||
|
||||
请求体格式:
|
||||
{
|
||||
"part_count": 分区数量(int),
|
||||
"part_type": 分区类型(int)
|
||||
}
|
||||
"""
|
||||
props = await req.json()
|
||||
part_count = props["part_count"]
|
||||
part_type = props["part_type"]
|
||||
return calculate_district_metering_area_for_network(network, part_count, part_type)
|
||||
|
||||
@router.get(
|
||||
"/network-schemas/district-metering-area",
|
||||
summary="获取DMA属性架构",
|
||||
description="获取指定水网的区域计量(DMA)属性架构定义"
|
||||
)
|
||||
async def fastapi_get_district_metering_area_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""获取DMA的属性架构。"""
|
||||
return get_district_metering_area_schema(network)
|
||||
|
||||
@router.get(
|
||||
"/district-metering-areas/detail",
|
||||
summary="获取DMA信息",
|
||||
description="获取指定ID的区域计量(DMA)详细信息"
|
||||
)
|
||||
async def fastapi_get_district_metering_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="DMA ID")
|
||||
) -> dict[str, Any]:
|
||||
"""获取DMA的详细信息。"""
|
||||
return get_district_metering_area(network, id)
|
||||
|
||||
@router.patch(
|
||||
"/district-metering-areas",
|
||||
response_model=None,
|
||||
summary="设置DMA属性",
|
||||
description="修改指定DMA的属性信息"
|
||||
)
|
||||
async def fastapi_set_district_metering_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""设置DMA属性。"""
|
||||
props = await req.json()
|
||||
return set_district_metering_area(network, ChangeSet(props))
|
||||
|
||||
@router.post(
|
||||
"/district-metering-areas",
|
||||
response_model=None,
|
||||
summary="添加新DMA",
|
||||
description="向水网添加一个新的区域计量(DMA)"
|
||||
)
|
||||
async def fastapi_add_district_metering_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""添加新的DMA。"""
|
||||
props = await req.json()
|
||||
# boundary should be [(x,y), (x,y)]
|
||||
boundary = props.get("boundary", [])
|
||||
newBoundary = []
|
||||
for pt in boundary:
|
||||
if len(pt) >= 2:
|
||||
newBoundary.append((pt[0], pt[1]))
|
||||
props["boundary"] = newBoundary
|
||||
return add_district_metering_area(network, ChangeSet(props))
|
||||
|
||||
@router.delete(
|
||||
"/district-metering-areas",
|
||||
response_model=None,
|
||||
summary="删除DMA",
|
||||
description="删除指定的区域计量(DMA)"
|
||||
)
|
||||
async def fastapi_delete_district_metering_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""删除DMA。"""
|
||||
props = await req.json()
|
||||
return delete_district_metering_area(network, ChangeSet(props))
|
||||
|
||||
@router.get(
|
||||
"/district-metering-areas/ids",
|
||||
summary="获取所有DMA ID",
|
||||
description="获取指定水网中所有DMA的ID列表"
|
||||
)
|
||||
async def fastapi_get_all_district_metering_area_ids(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
id: str = Query(..., description="区域 ID"),
|
||||
) -> list[str]:
|
||||
"""获取所有DMA的ID列表。"""
|
||||
return get_all_district_metering_area_ids(network)
|
||||
return get_nodes_in_region(network, id)
|
||||
|
||||
@router.get(
|
||||
"/district-metering-areas",
|
||||
summary="获取所有DMA",
|
||||
description="获取指定水网中所有DMA的详细信息"
|
||||
)
|
||||
async def getalldistrictmeteringareas(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[dict[str, Any]]:
|
||||
"""获取所有DMA的详细信息列表。"""
|
||||
return get_all_district_metering_areas(network)
|
||||
|
||||
@router.post(
|
||||
"/district-metering-area-generation-runs",
|
||||
response_model=None,
|
||||
summary="生成DMA分区",
|
||||
description="根据参数自动生成水网的DMA分区方案"
|
||||
)
|
||||
async def fastapi_generate_district_metering_area(
|
||||
@router.patch("/regions", summary="修改区域", response_model=None)
|
||||
async def set_region_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
part_count: int = Query(..., description="分区数量", gt=0),
|
||||
part_type: int = Query(..., description="分区类型"),
|
||||
inflate_delta: float = Query(..., description="膨胀参数")
|
||||
request: Request = None,
|
||||
) -> ChangeSet:
|
||||
"""生成DMA分区。"""
|
||||
return generate_district_metering_area(
|
||||
network, part_count, part_type, inflate_delta
|
||||
)
|
||||
return set_region(network, ChangeSet(await request.json()))
|
||||
|
||||
@router.post(
|
||||
"/sub-district-metering-areas",
|
||||
response_model=None,
|
||||
summary="生成DMA子分区",
|
||||
description="为指定DMA生成子DMA分区"
|
||||
)
|
||||
async def fastapi_generate_sub_district_metering_area(
|
||||
|
||||
@router.post("/regions", summary="添加区域", response_model=None)
|
||||
async def add_region_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
dma: str = Query(..., description="DMA ID"),
|
||||
part_count: int = Query(..., description="分区数量", gt=0),
|
||||
part_type: int = Query(..., description="分区类型"),
|
||||
inflate_delta: float = Query(..., description="膨胀参数")
|
||||
request: Request = None,
|
||||
) -> ChangeSet:
|
||||
"""生成DMA子分区。"""
|
||||
return generate_sub_district_metering_area(
|
||||
network, dma, part_count, part_type, inflate_delta
|
||||
)
|
||||
payload = await request.json()
|
||||
payload["boundary"] = [tuple(point[:2]) for point in payload.get("boundary", [])]
|
||||
return add_region(network, ChangeSet(payload))
|
||||
|
||||
|
||||
############################################################
|
||||
# service_area 34
|
||||
############################################################
|
||||
|
||||
@router.post(
|
||||
"/service-area-calculations",
|
||||
summary="计算服务区",
|
||||
description="计算指定水网的服务区分区,返回全部时间步结果"
|
||||
)
|
||||
async def fastapi_calculate_service_area(
|
||||
@router.delete("/regions", summary="删除区域", response_model=None)
|
||||
async def delete_region_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> list[dict[str, list[str]]]:
|
||||
"""计算服务区分区,返回全部时间步结果。"""
|
||||
return calculate_service_area(network)
|
||||
|
||||
@router.get(
|
||||
"/network-schemas/service-area",
|
||||
summary="获取服务区属性架构",
|
||||
description="获取指定水网的服务区属性架构定义"
|
||||
)
|
||||
async def fastapi_get_service_area_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""获取服务区的属性架构。"""
|
||||
return get_service_area_schema(network)
|
||||
|
||||
@router.get(
|
||||
"/service-areas/detail",
|
||||
summary="获取服务区信息",
|
||||
description="获取指定ID的服务区详细信息"
|
||||
)
|
||||
async def fastapi_get_service_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="服务区ID")
|
||||
) -> dict[str, Any]:
|
||||
"""获取服务区的详细信息。"""
|
||||
return get_service_area(network, id)
|
||||
|
||||
@router.patch(
|
||||
"/service-areas",
|
||||
response_model=None,
|
||||
summary="设置服务区属性",
|
||||
description="修改指定服务区的属性信息"
|
||||
)
|
||||
async def fastapi_set_service_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
request: Request = None,
|
||||
) -> ChangeSet:
|
||||
"""设置服务区属性。"""
|
||||
props = await req.json()
|
||||
return set_service_area(network, ChangeSet(props))
|
||||
|
||||
@router.post(
|
||||
"/service-areas",
|
||||
response_model=None,
|
||||
summary="添加新服务区",
|
||||
description="向水网添加一个新的服务区"
|
||||
)
|
||||
async def fastapi_add_service_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""添加新的服务区。"""
|
||||
props = await req.json()
|
||||
return add_service_area(network, ChangeSet(props))
|
||||
|
||||
@router.delete(
|
||||
"/service-areas",
|
||||
response_model=None,
|
||||
summary="删除服务区",
|
||||
description="删除指定的服务区"
|
||||
)
|
||||
async def fastapi_delete_service_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""删除服务区。"""
|
||||
props = await req.json()
|
||||
return delete_service_area(network, ChangeSet(props))
|
||||
|
||||
@router.get(
|
||||
"/service-areas",
|
||||
summary="获取所有服务区",
|
||||
description="获取指定水网中的所有服务区信息"
|
||||
)
|
||||
async def fastapi_get_all_service_areas(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[dict[str, Any]]:
|
||||
"""获取所有服务区的信息列表。"""
|
||||
return get_all_service_areas(network)
|
||||
|
||||
@router.post(
|
||||
"/service-area-generation-runs",
|
||||
response_model=None,
|
||||
summary="生成服务区分区",
|
||||
description="根据参数自动生成水网的服务区分区"
|
||||
)
|
||||
async def fastapi_generate_service_area(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
inflate_delta: float = Query(..., description="膨胀参数")
|
||||
) -> ChangeSet:
|
||||
"""生成服务区分区。"""
|
||||
return generate_service_area(network, inflate_delta)
|
||||
|
||||
|
||||
############################################################
|
||||
# virtual_district 35
|
||||
############################################################
|
||||
|
||||
@router.post(
|
||||
"/virtual-district-calculations",
|
||||
summary="计算虚拟分区",
|
||||
description="根据指定的压力监测节点作为中心节点计算虚拟分区方案"
|
||||
)
|
||||
async def fastapi_calculate_virtual_district(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
centers: list[str] = Query(..., description="压力监测节点ID列表")
|
||||
) -> dict[str, list[Any]]:
|
||||
"""计算虚拟分区。"""
|
||||
return calculate_virtual_district(network, centers)
|
||||
|
||||
@router.get(
|
||||
"/network-schemas/virtual-district",
|
||||
summary="获取虚拟分区属性架构",
|
||||
description="获取指定水网的虚拟分区属性架构定义"
|
||||
)
|
||||
async def fastapi_get_virtual_district_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""获取虚拟分区的属性架构。"""
|
||||
return get_virtual_district_schema(network)
|
||||
|
||||
@router.get(
|
||||
"/virtual-districts/detail",
|
||||
summary="获取虚拟分区信息",
|
||||
description="获取指定ID的虚拟分区详细信息"
|
||||
)
|
||||
async def fastapi_get_virtual_district(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="虚拟分区ID")
|
||||
) -> dict[str, Any]:
|
||||
"""获取虚拟分区的详细信息。"""
|
||||
return get_virtual_district(network, id)
|
||||
|
||||
@router.patch(
|
||||
"/virtual-districts",
|
||||
response_model=None,
|
||||
summary="设置虚拟分区属性",
|
||||
description="修改指定虚拟分区的属性信息"
|
||||
)
|
||||
async def fastapi_set_virtual_district(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""设置虚拟分区属性。"""
|
||||
props = await req.json()
|
||||
return set_virtual_district(network, ChangeSet(props))
|
||||
|
||||
@router.post(
|
||||
"/virtual-districts",
|
||||
response_model=None,
|
||||
summary="添加新虚拟分区",
|
||||
description="向水网添加一个新的虚拟分区"
|
||||
)
|
||||
async def fastapi_add_virtual_district(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""添加新的虚拟分区。"""
|
||||
props = await req.json()
|
||||
return add_virtual_district(network, ChangeSet(props))
|
||||
|
||||
@router.delete(
|
||||
"/virtual-districts",
|
||||
response_model=None,
|
||||
summary="删除虚拟分区",
|
||||
description="删除指定的虚拟分区"
|
||||
)
|
||||
async def fastapi_delete_virtual_district(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""删除虚拟分区。"""
|
||||
props = await req.json()
|
||||
return delete_virtual_district(network, ChangeSet(props))
|
||||
|
||||
@router.get(
|
||||
"/virtual-districts",
|
||||
summary="获取所有虚拟分区",
|
||||
description="获取指定水网中的所有虚拟分区信息"
|
||||
)
|
||||
async def fastapi_get_all_virtual_district(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[dict[str, Any]]:
|
||||
"""获取所有虚拟分区的信息列表。"""
|
||||
return get_all_virtual_districts(network)
|
||||
|
||||
@router.post(
|
||||
"/virtual-district-generation-runs",
|
||||
response_model=None,
|
||||
summary="生成虚拟分区",
|
||||
description="根据参数自动生成虚拟分区方案"
|
||||
)
|
||||
async def fastapi_generate_virtual_district(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
inflate_delta: float = Query(..., description="膨胀参数"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""生成虚拟分区。"""
|
||||
props = await req.json()
|
||||
return generate_virtual_district(network, props["centers"], inflate_delta)
|
||||
|
||||
@router.post(
|
||||
"/district-metering-areas/for-nodes",
|
||||
summary="计算节点DMA分区",
|
||||
description="为指定节点集计算区域计量(DMA)分区方案"
|
||||
)
|
||||
async def fastapi_calculate_district_metering_area_for_nodes(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> list[list[str]]:
|
||||
"""
|
||||
计算节点DMA分区。
|
||||
|
||||
请求体格式:
|
||||
{
|
||||
"nodes": 节点ID列表(list[str]),
|
||||
"part_count": 分区数量(int),
|
||||
"part_type": 分区类型(int)
|
||||
}
|
||||
"""
|
||||
props = await req.json()
|
||||
nodes = props["nodes"]
|
||||
part_count = props["part_count"]
|
||||
part_type = props["part_type"]
|
||||
return calculate_district_metering_area_for_nodes(
|
||||
network, nodes, part_count, part_type
|
||||
)
|
||||
return delete_region(network, ChangeSet(await request.json()))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_reservoir,
|
||||
delete_reservoir,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
get_tag,
|
||||
get_tag_schema,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
add_tank,
|
||||
delete_tank,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from fastapi import APIRouter, Request, Query, Path, Body
|
||||
from typing import Any, List, Dict, Union
|
||||
from app.services.tjnetwork import (
|
||||
Any,
|
||||
ChangeSet,
|
||||
VALVES_TYPE_PRV,
|
||||
add_valve,
|
||||
|
||||
@@ -10,8 +10,6 @@ from app.auth.permissions import (
|
||||
)
|
||||
from app.domain.schemas.metadata import ProjectMetaResponse
|
||||
import app.services.project_info as project_info
|
||||
from app.infra.db.postgresql.database import get_database_instance as get_pg_db
|
||||
from app.infra.db.timescaledb.database import get_database_instance as get_ts_db
|
||||
from app.services.tjnetwork import (
|
||||
ChangeSet,
|
||||
list_project,
|
||||
@@ -26,11 +24,7 @@ from app.services.tjnetwork import (
|
||||
read_inp,
|
||||
dump_inp,
|
||||
get_all_vertices,
|
||||
get_all_scada_elements,
|
||||
get_all_district_metering_areas,
|
||||
get_all_service_areas,
|
||||
get_all_virtual_districts,
|
||||
get_extension_data,
|
||||
get_all_scada_info,
|
||||
convert_inp_v3_to_v2,
|
||||
)
|
||||
|
||||
@@ -137,27 +131,6 @@ async def open_project_endpoint(
|
||||
"""
|
||||
open_project(network)
|
||||
|
||||
# 尝试连接指定数据库
|
||||
try:
|
||||
# 初始化 PostgreSQL 连接池
|
||||
pg_instance = await get_pg_db(network)
|
||||
async with pg_instance.get_connection() as conn:
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute("SELECT 1")
|
||||
|
||||
# 初始化 TimescaleDB 连接池
|
||||
ts_instance = await get_ts_db(network)
|
||||
async with ts_instance.get_connection() as conn:
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute("SELECT 1")
|
||||
|
||||
except Exception as e:
|
||||
# 记录错误但不阻断项目打开,或者根据需求决定是否阻断
|
||||
# 这里选择打印错误,因为 open_project 原本只负责原生部分
|
||||
print(f"Failed to connect to databases for {network}: {str(e)}")
|
||||
# 如果数据库连接是必须的,可以抛出异常:
|
||||
# raise HTTPException(status_code=500, detail=f"Database connection failed: {str(e)}")
|
||||
|
||||
return network
|
||||
|
||||
@router.delete("/projects/current", summary="关闭项目", description="将指定项目从内存中卸载,释放资源。")
|
||||
@@ -202,24 +175,11 @@ async def export_inp_endpoint(
|
||||
op = cs.operations[0]
|
||||
open_project(network)
|
||||
op["vertex"] = json.dumps(get_all_vertices(network))
|
||||
op["scada"] = json.dumps(get_all_scada_elements(network))
|
||||
op["dma"] = json.dumps(get_all_district_metering_areas(network))
|
||||
op["sa"] = json.dumps(get_all_service_areas(network))
|
||||
op["vd"] = json.dumps(get_all_virtual_districts(network))
|
||||
op["legend"] = get_extension_data(network, "legend")
|
||||
|
||||
db = get_extension_data(network, "scada_db")
|
||||
print(db)
|
||||
scada_db = ""
|
||||
if db:
|
||||
scada_db = db
|
||||
print(scada_db)
|
||||
op["scada_db"] = scada_db
|
||||
op["scada"] = json.dumps(get_all_scada_info(network))
|
||||
|
||||
close_project(network)
|
||||
|
||||
return cs
|
||||
|
||||
@router.post("/projects/current/imports", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。")
|
||||
async def read_inp_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
@@ -353,19 +313,7 @@ async def fastapi_convert_v3_to_v2(
|
||||
op = cs.operations[0]
|
||||
open_project(network)
|
||||
op["vertex"] = json.dumps(get_all_vertices(network))
|
||||
op["scada"] = json.dumps(get_all_scada_elements(network))
|
||||
op["dma"] = json.dumps(get_all_district_metering_areas(network))
|
||||
op["sa"] = json.dumps(get_all_service_areas(network))
|
||||
op["vd"] = json.dumps(get_all_virtual_districts(network))
|
||||
op["legend"] = get_extension_data(network, "legend")
|
||||
|
||||
db = get_extension_data(network, "scada_db")
|
||||
print(db)
|
||||
scada_db = ""
|
||||
if db:
|
||||
scada_db = db
|
||||
print(scada_db)
|
||||
op["scada_db"] = scada_db
|
||||
op["scada"] = json.dumps(get_all_scada_info(network))
|
||||
|
||||
close_project(network)
|
||||
|
||||
@@ -496,19 +444,7 @@ async def fastapi_convert_v3_to_v2(
|
||||
op = cs.operations[0]
|
||||
open_project(network)
|
||||
op["vertex"] = json.dumps(get_all_vertices(network))
|
||||
op["scada"] = json.dumps(get_all_scada_elements(network))
|
||||
op["dma"] = json.dumps(get_all_district_metering_areas(network))
|
||||
op["sa"] = json.dumps(get_all_service_areas(network))
|
||||
op["vd"] = json.dumps(get_all_virtual_districts(network))
|
||||
op["legend"] = get_extension_data(network, "legend")
|
||||
|
||||
db = get_extension_data(network, "scada_db")
|
||||
print(db)
|
||||
scada_db = ""
|
||||
if db:
|
||||
scada_db = db
|
||||
print(scada_db)
|
||||
op["scada_db"] = scada_db
|
||||
op["scada"] = json.dumps(get_all_scada_info(network))
|
||||
|
||||
close_project(network)
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Path, Query
|
||||
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.scheme import SchemeRepository
|
||||
from app.infra.db.postgresql.analysis import AnalysisRepository
|
||||
from app.auth.project_dependencies import get_project_pg_connection
|
||||
|
||||
router = APIRouter()
|
||||
@@ -33,8 +35,8 @@ async def get_scada_info_with_connection(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/schemes/list-with-connection", summary="获取方案列表", description="使用连接池查询所有方案信息")
|
||||
async def get_scheme_list_with_connection(
|
||||
@router.get("/analysis/runs", summary="获取分析运行列表")
|
||||
async def get_analysis_runs(
|
||||
conn: AsyncConnection = Depends(get_database_connection),
|
||||
):
|
||||
"""
|
||||
@@ -43,14 +45,15 @@ async def get_scheme_list_with_connection(
|
||||
返回项目中所有方案的详细信息
|
||||
"""
|
||||
try:
|
||||
scheme_data = await SchemeRepository.get_schemes(conn)
|
||||
return {"success": True, "data": scheme_data, "count": len(scheme_data)}
|
||||
runs = await AnalysisRepository.list_runs(conn)
|
||||
return {"success": True, "data": runs, "count": len(runs)}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"查询方案信息时发生错误: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"查询分析运行时发生错误: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/burst-locations/database-view", summary="获取爆管定位结果", description="使用连接池查询所有爆管定位结果")
|
||||
async def get_burst_locate_result_with_connection(
|
||||
@router.get("/analysis/runs/{run_id}", summary="获取分析运行")
|
||||
async def get_analysis_run(
|
||||
run_id: UUID,
|
||||
conn: AsyncConnection = Depends(get_database_connection),
|
||||
):
|
||||
"""
|
||||
@@ -59,17 +62,22 @@ async def get_burst_locate_result_with_connection(
|
||||
返回项目中所有的爆管定位分析结果
|
||||
"""
|
||||
try:
|
||||
burst_data = await SchemeRepository.get_burst_locate_results(conn)
|
||||
return {"success": True, "data": burst_data, "count": len(burst_data)}
|
||||
run = await AnalysisRepository.get_run(conn, run_id)
|
||||
if run is None:
|
||||
raise HTTPException(status_code=404, detail="分析运行不存在")
|
||||
return run
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"查询爆管定位结果时发生错误: {str(e)}"
|
||||
status_code=500, detail=f"查询分析运行时发生错误: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/burst-locations/{burst_incident}", summary="按事件查询爆管定位结果", description="根据爆管事件ID查询对应的爆管定位结果")
|
||||
async def get_burst_locate_result_by_incident(
|
||||
burst_incident: str = Path(..., description="爆管事件ID"),
|
||||
@router.get("/analysis/runs/{run_id}/results", summary="获取分析结果")
|
||||
async def get_analysis_results(
|
||||
run_id: UUID,
|
||||
result_type: str | None = Query(default=None, description="结果类型"),
|
||||
conn: AsyncConnection = Depends(get_database_connection),
|
||||
):
|
||||
"""
|
||||
@@ -79,11 +87,9 @@ async def get_burst_locate_result_by_incident(
|
||||
burst_incident: 爆管事件的唯一标识符
|
||||
"""
|
||||
try:
|
||||
return await SchemeRepository.get_burst_locate_result_by_incident(
|
||||
conn, burst_incident
|
||||
)
|
||||
return await AnalysisRepository.list_results(conn, run_id, result_type)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"根据 burst_incident 查询爆管定位结果时发生错误: {str(e)}",
|
||||
detail=f"查询分析结果时发生错误: {str(e)}",
|
||||
)
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
from typing import Any, List, Dict
|
||||
from fastapi import APIRouter, Query, Path
|
||||
from app.services.tjnetwork import (
|
||||
get_pipe_risk_probability_now,
|
||||
get_pipe_risk_probability,
|
||||
get_pipes_risk_probability,
|
||||
get_network_pipe_risk_probability_now,
|
||||
get_pipe_risk_probability_geometries,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get(
|
||||
"/pipes/risk-probability-now",
|
||||
summary="获取管道当前风险概率",
|
||||
description="获取指定管道当前时刻的风险概率值"
|
||||
)
|
||||
async def fastapi_get_pipe_risk_probability_now(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
pipe_id: str = Query(..., description="管道ID")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取管道当前风险概率。
|
||||
|
||||
查询指定管道在当前时刻的风险概率值。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
pipe_id: 管道ID
|
||||
|
||||
Returns:
|
||||
包含风险概率信息的字典
|
||||
"""
|
||||
return get_pipe_risk_probability_now(network, pipe_id)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/pipes/risk-probability",
|
||||
summary="获取管道风险概率历史",
|
||||
description="获取指定管道的风险概率历史数据"
|
||||
)
|
||||
async def fastapi_get_pipe_risk_probability(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
pipe_id: str = Query(..., description="管道ID")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取管道风险概率历史。
|
||||
|
||||
查询指定管道的历史风险概率数据。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
pipe_id: 管道ID
|
||||
|
||||
Returns:
|
||||
包含风险概率历史的字典
|
||||
"""
|
||||
return get_pipe_risk_probability(network, pipe_id)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/pipes-risk-probabilities",
|
||||
summary="批量获取多条管道风险概率",
|
||||
description="批量获取多条管道的风险概率值"
|
||||
)
|
||||
async def fastapi_get_pipes_risk_probability(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
pipe_ids: str = Query(..., description="逗号分隔的管道ID列表")
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
批量获取多条管道风险概率。
|
||||
|
||||
查询多条指定管道的风险概率值。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
pipe_ids: 逗号分隔的管道ID列表(例如:pipe1,pipe2,pipe3)
|
||||
|
||||
Returns:
|
||||
包含多条管道风险概率的列表
|
||||
"""
|
||||
pipeids = pipe_ids.split(",")
|
||||
return get_pipes_risk_probability(network, pipeids)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/network-pipe-risk-probability-nows",
|
||||
summary="获取整个网络的管道风险概率",
|
||||
description="获取指定网络中所有管道的当前风险概率值"
|
||||
)
|
||||
async def fastapi_get_network_pipe_risk_probability_now(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取整个网络的管道风险概率。
|
||||
|
||||
查询指定网络中所有管道在当前时刻的风险概率值。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
包含网络内所有管道风险概率的列表
|
||||
"""
|
||||
return get_network_pipe_risk_probability_now(network)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/pipes/risk-probability-geometries",
|
||||
summary="获取管道风险几何信息",
|
||||
description="获取指定网络中管道的风险相关几何数据"
|
||||
)
|
||||
async def fastapi_get_pipe_risk_probability_geometries(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取管道风险几何信息。
|
||||
|
||||
查询指定网络中管道的地理和风险相关的几何数据。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
包含几何信息和风险数据的字典
|
||||
"""
|
||||
return get_pipe_risk_probability_geometries(network)
|
||||
+18
-509
@@ -1,525 +1,34 @@
|
||||
from typing import Any
|
||||
from fastapi import APIRouter, Request, Query
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.services.tjnetwork import (
|
||||
ChangeSet,
|
||||
get_scada_info,
|
||||
get_all_scada_info,
|
||||
get_scada_device_schema,
|
||||
get_scada_device,
|
||||
set_scada_device,
|
||||
add_scada_device,
|
||||
delete_scada_device,
|
||||
clean_scada_device,
|
||||
get_all_scada_device_ids,
|
||||
get_all_scada_devices,
|
||||
get_scada_device_data_schema,
|
||||
get_scada_device_data,
|
||||
set_scada_device_data,
|
||||
add_scada_device_data,
|
||||
delete_scada_device_data,
|
||||
clean_scada_device_data,
|
||||
get_scada_element_schema,
|
||||
get_scada_element,
|
||||
set_scada_element,
|
||||
add_scada_element,
|
||||
delete_scada_element,
|
||||
clean_scada_element,
|
||||
get_all_scada_elements,
|
||||
get_scada_element_schema,
|
||||
get_scada_info,
|
||||
get_scada_info_schema,
|
||||
)
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
async def fast_get_scada_properties(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
scada: str = Query(..., description="SCADA设备ID")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取单个SCADA设备的属性信息
|
||||
|
||||
根据管网名称和SCADA设备ID获取该设备的完整属性。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
scada: SCADA设备ID
|
||||
|
||||
Returns:
|
||||
SCADA设备的属性字典
|
||||
"""
|
||||
return get_scada_info(network, scada)
|
||||
|
||||
async def fast_get_all_scada_properties(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取指定管网所有SCADA设备的属性信息
|
||||
|
||||
查询该管网下所有已配置的SCADA设备的属性列表。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA设备属性列表
|
||||
"""
|
||||
return get_all_scada_info(network)
|
||||
|
||||
|
||||
############################################################
|
||||
# scada_device 设备管理
|
||||
############################################################
|
||||
|
||||
@router.get("/network-schemas/scada-device", summary="获取SCADA设备架构", tags=["SCADA设备"])
|
||||
async def fastapi_get_scada_device_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""
|
||||
获取SCADA设备的数据架构
|
||||
|
||||
返回SCADA设备表的字段定义和类型信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA设备的字段架构信息
|
||||
"""
|
||||
return get_scada_device_schema(network)
|
||||
|
||||
@router.get("/scada-devices/detail", summary="获取SCADA设备", tags=["SCADA设备"])
|
||||
async def fastapi_get_scada_device(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="SCADA设备ID")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取单个SCADA设备的信息
|
||||
|
||||
根据设备ID查询该设备的详细信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
id: SCADA设备ID
|
||||
|
||||
Returns:
|
||||
SCADA设备信息
|
||||
"""
|
||||
return get_scada_device(network, id)
|
||||
|
||||
@router.patch("/scada-devices", response_model=None, summary="更新SCADA设备", tags=["SCADA设备"])
|
||||
async def fastapi_set_scada_device(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
更新SCADA设备信息
|
||||
|
||||
修改指定SCADA设备的属性。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含要更新的设备属性
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return set_scada_device(network, ChangeSet(props))
|
||||
|
||||
@router.post("/scada-devices", response_model=None, summary="添加SCADA设备", tags=["SCADA设备"])
|
||||
async def fastapi_add_scada_device(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
添加新的SCADA设备
|
||||
|
||||
在指定管网中添加一个新的SCADA设备。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含新设备的属性
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return add_scada_device(network, ChangeSet(props))
|
||||
|
||||
@router.delete("/scada-devices", response_model=None, summary="删除SCADA设备", tags=["SCADA设备"])
|
||||
async def fastapi_delete_scada_device(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
删除SCADA设备
|
||||
|
||||
从指定管网中删除一个SCADA设备。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含要删除的设备ID
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return delete_scada_device(network, ChangeSet(props))
|
||||
|
||||
@router.post("/scada-device-cleaning-runs", response_model=None, summary="清空SCADA设备表", tags=["SCADA设备"])
|
||||
async def fastapi_clean_scada_device(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
清空SCADA设备表
|
||||
|
||||
删除指定管网中所有的SCADA设备。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
return clean_scada_device(network)
|
||||
|
||||
@router.get("/scada-devices/ids", summary="获取所有SCADA设备ID", tags=["SCADA设备"])
|
||||
async def fastapi_get_all_scada_device_ids(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[str]:
|
||||
"""
|
||||
获取指定管网所有SCADA设备的ID列表
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA设备ID列表
|
||||
"""
|
||||
return get_all_scada_device_ids(network)
|
||||
|
||||
@router.get("/scada-devices", summary="获取所有SCADA设备", tags=["SCADA设备"])
|
||||
async def fastapi_get_all_scada_devices(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取指定管网所有SCADA设备的完整信息
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA设备信息列表
|
||||
"""
|
||||
return get_all_scada_devices(network)
|
||||
|
||||
|
||||
############################################################
|
||||
# scada_device_data 设备数据管理
|
||||
############################################################
|
||||
|
||||
@router.get("/network-schemas/scada-device-data", summary="获取SCADA设备数据架构", tags=["SCADA设备数据"])
|
||||
async def fastapi_get_scada_device_data_schema(
|
||||
@router.get("/network-schemas/scada-device", summary="获取 SCADA 设备结构")
|
||||
async def get_scada_device_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""
|
||||
获取SCADA设备数据的表结构
|
||||
|
||||
返回SCADA设备数据表的字段定义和类型信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA设备数据的字段架构信息
|
||||
"""
|
||||
return get_scada_device_data_schema(network)
|
||||
|
||||
@router.get("/scada-device-datas/detail", summary="获取SCADA设备数据", tags=["SCADA设备数据"])
|
||||
async def fastapi_get_scada_device_data(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
device_id: str = Query(..., description="SCADA设备ID")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取单个SCADA设备的数据
|
||||
|
||||
查询指定设备的监测数据或配置数据。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
device_id: SCADA设备ID
|
||||
|
||||
Returns:
|
||||
SCADA设备数据
|
||||
"""
|
||||
return get_scada_device_data(network, device_id)
|
||||
|
||||
@router.patch("/scada-device-datas", response_model=None, summary="更新SCADA设备数据", tags=["SCADA设备数据"])
|
||||
async def fastapi_set_scada_device_data(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
更新SCADA设备数据
|
||||
|
||||
修改指定SCADA设备的数据。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含要更新的数据
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return set_scada_device_data(network, ChangeSet(props))
|
||||
|
||||
@router.post("/scada-device-datas", response_model=None, summary="添加SCADA设备数据", tags=["SCADA设备数据"])
|
||||
async def fastapi_add_scada_device_data(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
添加新的SCADA设备数据
|
||||
|
||||
为指定SCADA设备添加新的数据记录。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含新数据的内容
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return add_scada_device_data(network, ChangeSet(props))
|
||||
|
||||
@router.delete("/scada-device-datas", response_model=None, summary="删除SCADA设备数据", tags=["SCADA设备数据"])
|
||||
async def fastapi_delete_scada_device_data(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
删除SCADA设备数据
|
||||
|
||||
删除指定SCADA设备的数据记录。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含要删除的数据ID
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return delete_scada_device_data(network, ChangeSet(props))
|
||||
|
||||
@router.post("/scada-device-data-cleaning-runs", response_model=None, summary="清空SCADA设备数据表", tags=["SCADA设备数据"])
|
||||
async def fastapi_clean_scada_device_data(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
清空SCADA设备数据表
|
||||
|
||||
删除指定管网中所有SCADA设备的数据。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
return clean_scada_device_data(network)
|
||||
|
||||
|
||||
############################################################
|
||||
# scada_element SCADA元素映射
|
||||
############################################################
|
||||
|
||||
@router.get("/network-schemas/scada-element", summary="获取SCADA元素架构", tags=["SCADA元素映射"])
|
||||
async def fastapi_get_scada_element_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""
|
||||
获取SCADA元素映射的表结构
|
||||
|
||||
返回SCADA元素映射表的字段定义和类型信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA元素映射的字段架构信息
|
||||
"""
|
||||
return get_scada_element_schema(network)
|
||||
|
||||
@router.get("/scada-elements", summary="获取所有SCADA元素映射", tags=["SCADA元素映射"])
|
||||
async def fastapi_get_scada_elements(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取指定管网所有SCADA元素映射
|
||||
|
||||
查询所有SCADA设备与管网元素(节点/管道)的映射关系。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA元素映射列表
|
||||
"""
|
||||
return get_all_scada_elements(network)
|
||||
|
||||
@router.get("/scada-elements/detail", summary="获取单个SCADA元素映射", tags=["SCADA元素映射"])
|
||||
async def fastapi_get_scada_element(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="SCADA元素映射ID")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取单个SCADA元素映射的信息
|
||||
|
||||
根据ID查询特定的SCADA设备与管网元素的映射关系。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
id: SCADA元素映射ID
|
||||
|
||||
Returns:
|
||||
SCADA元素映射信息
|
||||
"""
|
||||
return get_scada_element(network, id)
|
||||
|
||||
@router.patch("/scada-elements", response_model=None, summary="更新SCADA元素映射", tags=["SCADA元素映射"])
|
||||
async def fastapi_set_scada_element(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
更新SCADA元素映射
|
||||
|
||||
修改SCADA设备与管网元素的映射关系。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含要更新的映射信息
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return set_scada_element(network, ChangeSet(props))
|
||||
|
||||
@router.post("/scada-elements", response_model=None, summary="添加SCADA元素映射", tags=["SCADA元素映射"])
|
||||
async def fastapi_add_scada_element(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
添加新的SCADA元素映射
|
||||
|
||||
创建SCADA设备与管网元素的新映射关系。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含新映射的信息
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return add_scada_element(network, ChangeSet(props))
|
||||
|
||||
@router.delete("/scada-elements", response_model=None, summary="删除SCADA元素映射", tags=["SCADA元素映射"])
|
||||
async def fastapi_delete_scada_element(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
删除SCADA元素映射
|
||||
|
||||
移除SCADA设备与管网元素的映射关系。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
req: 请求体,包含要删除的映射ID
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
props = await req.json()
|
||||
return delete_scada_element(network, ChangeSet(props))
|
||||
|
||||
@router.post("/scada-element-cleaning-runs", response_model=None, summary="清空SCADA元素映射表", tags=["SCADA元素映射"])
|
||||
async def fastapi_clean_scada_element(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
清空SCADA元素映射表
|
||||
|
||||
删除指定管网中所有的SCADA元素映射。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
变更集合信息
|
||||
"""
|
||||
return clean_scada_element(network)
|
||||
|
||||
|
||||
############################################################
|
||||
# scada_info SCADA信息
|
||||
############################################################
|
||||
|
||||
@router.get("/scada-info-schemas", summary="获取SCADA信息架构", tags=["SCADA信息"])
|
||||
async def fastapi_get_scada_info_schema(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
) -> dict[str, dict[str, Any]]:
|
||||
"""
|
||||
获取SCADA信息表的结构
|
||||
|
||||
返回SCADA信息表的字段定义和类型信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA信息的字段架构信息
|
||||
"""
|
||||
return get_scada_info_schema(network)
|
||||
|
||||
@router.get("/scada-info/detail", summary="获取SCADA信息", tags=["SCADA信息"])
|
||||
async def fastapi_get_scada_info(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
id: str = Query(..., description="SCADA信息ID")
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
获取单个SCADA信息
|
||||
|
||||
根据ID查询SCADA的详细配置信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
id: SCADA信息ID
|
||||
|
||||
Returns:
|
||||
SCADA信息详情
|
||||
"""
|
||||
return get_scada_info(network, id)
|
||||
|
||||
@router.get("/scada-info", summary="获取所有SCADA信息", tags=["SCADA信息"])
|
||||
async def fastapi_get_all_scada_info(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||
@router.get("/scada-devices", summary="获取 SCADA 设备列表")
|
||||
async def get_scada_devices(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
获取指定管网所有SCADA的信息
|
||||
|
||||
查询该管网下所有已配置的SCADA的完整信息。
|
||||
|
||||
Args:
|
||||
network: 管网名称(或数据库名称)
|
||||
|
||||
Returns:
|
||||
SCADA信息列表
|
||||
"""
|
||||
return get_all_scada_info(network)
|
||||
|
||||
|
||||
@router.get("/scada-devices/detail", summary="获取 SCADA 设备")
|
||||
async def get_scada_device(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
device_id: str = Query(..., description="SCADA 设备 ID"),
|
||||
) -> dict[str, Any]:
|
||||
return get_scada_info(network, device_id)
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, HTTPException, Path, Query
|
||||
from typing import Any
|
||||
from app.services.tjnetwork import get_scheme_schema, get_scheme, get_all_schemes
|
||||
from app.services.scheme_management import query_scheme_detail
|
||||
from app.services.time_api import extract_date
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/network-schemas/scheme", summary="获取方案模式", description="获取指定网络的方案模式定义")
|
||||
async def fastapi_get_scheme_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[Any, Any]]:
|
||||
"""
|
||||
获取方案模式定义
|
||||
|
||||
返回指定网络的方案模式结构定义
|
||||
"""
|
||||
return get_scheme_schema(network)
|
||||
|
||||
@router.get("/schemes/detail", summary="获取单个方案", description="根据名称获取指定的方案信息")
|
||||
async def fastapi_get_scheme(network: str = Query(..., description="管网名称(或数据库名称)"), schema_name: str = Query(..., description="方案名称")) -> dict[Any, Any]:
|
||||
"""
|
||||
获取单个方案详情
|
||||
|
||||
返回指定网络中指定名称的方案详细信息
|
||||
"""
|
||||
return get_scheme(network, schema_name)
|
||||
|
||||
@router.get("/schemes", summary="获取所有方案", description="获取指定网络的所有方案信息")
|
||||
async def fastapi_get_all_schemes(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
scheme_type: str | None = Query(None, description="方案类型;为空时返回全部类型"),
|
||||
query_date: datetime | None = Query(None, description="查询日期(可选)"),
|
||||
) -> list[dict[Any, Any]]:
|
||||
"""
|
||||
获取所有方案列表
|
||||
|
||||
返回指定网络中所有可用的方案
|
||||
"""
|
||||
parsed_date = (
|
||||
extract_date(query_date, field_name="query_date")
|
||||
if query_date is not None
|
||||
else None
|
||||
)
|
||||
return get_all_schemes(network, scheme_type=scheme_type, query_date=parsed_date)
|
||||
|
||||
|
||||
@router.get("/schemes/{scheme_name}", summary="获取方案详情", description="按方案类型获取指定方案详情")
|
||||
async def fastapi_get_scheme_detail(
|
||||
scheme_name: str = Path(..., description="方案名称"),
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
scheme_type: str | None = Query(None, description="方案类型;为空时返回通用方案详情"),
|
||||
) -> dict[Any, Any]:
|
||||
result = query_scheme_detail(
|
||||
name=network,
|
||||
scheme_name=scheme_name,
|
||||
scheme_type=scheme_type,
|
||||
)
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail=f"Scheme {scheme_name} not found")
|
||||
return result
|
||||
@@ -1,6 +1,7 @@
|
||||
import logging
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Path, Query, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
@@ -26,8 +27,9 @@ from app.services.sensor_placement import (
|
||||
build_sensor_placement_workbook,
|
||||
can_edit_sensor_placement,
|
||||
get_sensor_placement_candidate,
|
||||
get_sensor_placement_scheme,
|
||||
update_sensor_placement_scheme,
|
||||
get_sensor_placement_run,
|
||||
list_sensor_placement_runs,
|
||||
update_sensor_placement_run,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
@@ -74,19 +76,19 @@ def _service_http_error(exc: Exception) -> HTTPException:
|
||||
)
|
||||
|
||||
|
||||
def _get_scheme_response(
|
||||
def _get_run_response(
|
||||
network: str,
|
||||
scheme_id: int,
|
||||
run_id: UUID,
|
||||
current_user: Any,
|
||||
project_context: ProjectContext,
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
scheme = get_sensor_placement_scheme(network, scheme_id)
|
||||
run = get_sensor_placement_run(network, run_id)
|
||||
return {
|
||||
**scheme,
|
||||
**run,
|
||||
"can_edit": (
|
||||
_can_modify_project(project_context)
|
||||
and can_edit_sensor_placement(current_user, scheme)
|
||||
and can_edit_sensor_placement(current_user, run)
|
||||
),
|
||||
}
|
||||
except (
|
||||
@@ -116,7 +118,7 @@ async def get_sensor_placement_candidate_detail(
|
||||
|
||||
|
||||
@router.post(
|
||||
"/sensor-placement-optimization-runs",
|
||||
"/sensor-placement-runs",
|
||||
response_model=SensorPlacementSchemeResponse,
|
||||
summary="创建并返回监测点优化方案",
|
||||
)
|
||||
@@ -136,13 +138,13 @@ async def optimize_sensor_placement_scheme(
|
||||
created = await run_in_threadpool(
|
||||
optimizer,
|
||||
name=network,
|
||||
scheme_name=payload.scheme_name,
|
||||
scheme_name=payload.run_name,
|
||||
sensor_number=payload.sensor_count,
|
||||
min_diameter=payload.min_diameter,
|
||||
username=current_user.username,
|
||||
)
|
||||
scheme = get_sensor_placement_scheme(network, int(created["id"]))
|
||||
return {**scheme, "can_edit": True}
|
||||
run = get_sensor_placement_run(network, created["run_id"])
|
||||
return {**run, "can_edit": True}
|
||||
except (
|
||||
SensorPlacementConflictError,
|
||||
SensorPlacementValidationError,
|
||||
@@ -158,31 +160,45 @@ async def optimize_sensor_placement_scheme(
|
||||
|
||||
|
||||
@router.get(
|
||||
"/sensor-placement-schemes/{scheme_id}",
|
||||
"/sensor-placement-runs",
|
||||
response_model=list[SensorPlacementSchemeResponse],
|
||||
summary="获取监测点优化运行",
|
||||
)
|
||||
async def get_sensor_placement_runs(
|
||||
project_context: ProjectContext = Depends(get_project_context),
|
||||
) -> list[dict[str, Any]]:
|
||||
return await run_in_threadpool(
|
||||
list_sensor_placement_runs,
|
||||
project_context.project_code,
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/sensor-placement-runs/{run_id}",
|
||||
response_model=SensorPlacementSchemeResponse,
|
||||
summary="获取监测点方案详情",
|
||||
)
|
||||
async def get_sensor_placement_scheme_detail(
|
||||
scheme_id: int,
|
||||
async def get_sensor_placement_run_detail(
|
||||
run_id: UUID,
|
||||
network: str = Query(..., min_length=1),
|
||||
project_context: ProjectContext = Depends(get_project_context),
|
||||
current_user=Depends(get_current_metadata_user),
|
||||
) -> dict[str, Any]:
|
||||
return _get_scheme_response(
|
||||
return _get_run_response(
|
||||
_project_network(network, project_context),
|
||||
scheme_id,
|
||||
run_id,
|
||||
current_user,
|
||||
project_context,
|
||||
)
|
||||
|
||||
|
||||
@router.put(
|
||||
"/sensor-placement-schemes/{scheme_id}",
|
||||
"/sensor-placement-runs/{run_id}",
|
||||
response_model=SensorPlacementSchemeResponse,
|
||||
summary="覆盖保存监测点方案",
|
||||
)
|
||||
async def overwrite_sensor_placement_scheme(
|
||||
scheme_id: int,
|
||||
async def overwrite_sensor_placement_run(
|
||||
run_id: UUID,
|
||||
payload: SensorPlacementUpdateRequest,
|
||||
network: str = Query(..., min_length=1),
|
||||
project_context: ProjectContext = Depends(get_project_context),
|
||||
@@ -190,21 +206,21 @@ async def overwrite_sensor_placement_scheme(
|
||||
) -> dict[str, Any]:
|
||||
network = _project_network(network, project_context)
|
||||
_require_project_write(project_context)
|
||||
scheme = _get_scheme_response(
|
||||
run = _get_run_response(
|
||||
network,
|
||||
scheme_id,
|
||||
run_id,
|
||||
current_user,
|
||||
project_context,
|
||||
)
|
||||
if not scheme["can_edit"]:
|
||||
raise HTTPException(status_code=403, detail="无权修改该监测点方案")
|
||||
if not run["can_edit"]:
|
||||
raise HTTPException(status_code=403, detail="无权修改该监测点优化运行")
|
||||
|
||||
try:
|
||||
updated = update_sensor_placement_scheme(
|
||||
updated = update_sensor_placement_run(
|
||||
network,
|
||||
scheme_id,
|
||||
expected_sensor_location=payload.expected_sensor_location,
|
||||
sensor_location=payload.sensor_location,
|
||||
run_id,
|
||||
expected_sensor_locations=payload.expected_sensor_locations,
|
||||
sensor_locations=payload.sensor_locations,
|
||||
)
|
||||
return {**updated, "can_edit": True}
|
||||
except (
|
||||
@@ -216,26 +232,26 @@ async def overwrite_sensor_placement_scheme(
|
||||
|
||||
|
||||
@router.post(
|
||||
"/sensor-placement-schemes/{scheme_id}/exports/excel",
|
||||
"/sensor-placement-runs/{run_id}/exports/excel",
|
||||
summary="导出监测点工程清单",
|
||||
)
|
||||
async def export_sensor_placement_excel(
|
||||
scheme_id: int,
|
||||
run_id: UUID,
|
||||
payload: SensorPlacementExportRequest,
|
||||
network: str = Query(..., min_length=1),
|
||||
project_context: ProjectContext = Depends(get_project_context),
|
||||
current_user=Depends(get_current_metadata_user),
|
||||
) -> StreamingResponse:
|
||||
network = _project_network(network, project_context)
|
||||
scheme = _get_scheme_response(
|
||||
run = _get_run_response(
|
||||
network,
|
||||
scheme_id,
|
||||
run_id,
|
||||
current_user,
|
||||
project_context,
|
||||
)
|
||||
if (
|
||||
payload.sensor_location != scheme["sensor_location"]
|
||||
and not scheme["can_edit"]
|
||||
payload.sensor_locations != run["sensor_locations"]
|
||||
and not run["can_edit"]
|
||||
):
|
||||
raise HTTPException(status_code=403, detail="无权导出该方案的未保存草稿")
|
||||
|
||||
@@ -243,14 +259,14 @@ async def export_sensor_placement_excel(
|
||||
workbook = await run_in_threadpool(
|
||||
build_sensor_placement_workbook,
|
||||
network=network,
|
||||
scheme=scheme,
|
||||
sensor_location=payload.sensor_location,
|
||||
scheme=run,
|
||||
sensor_location=payload.sensor_locations,
|
||||
adjustment_status=payload.adjustment_status,
|
||||
)
|
||||
except SensorPlacementValidationError as exc:
|
||||
raise _service_http_error(exc) from exc
|
||||
|
||||
filename = f"{scheme['scheme_name']}_监测点清单.xlsx"
|
||||
filename = f"{run['name']}_监测点清单.xlsx"
|
||||
encoded_filename = quote(filename)
|
||||
return StreamingResponse(
|
||||
workbook,
|
||||
|
||||
@@ -22,11 +22,6 @@ from app.algorithms.simulation.scenarios import (
|
||||
# scheduling_analysis,
|
||||
pressure_regulation,
|
||||
)
|
||||
from app.algorithms.sensor import (
|
||||
pressure_sensor_placement_sensitivity,
|
||||
pressure_sensor_placement_kmeans,
|
||||
)
|
||||
|
||||
from app.services.simulation_ops import (
|
||||
project_management,
|
||||
scheduling_simulation,
|
||||
@@ -108,14 +103,6 @@ class PumpFailureState(BaseModel):
|
||||
pump_status: dict = Field(..., description="泵状态字典")
|
||||
|
||||
|
||||
class PressureSensorPlacement(BaseModel):
|
||||
name: str = Field(..., description="管网名称(或数据库名称)")
|
||||
scheme_name: str = Field(..., description="方案名称")
|
||||
sensor_number: int = Field(..., description="传感器数量")
|
||||
min_diameter: int = Field(0, description="最小管径限制")
|
||||
username: str = Field(..., description="用户名")
|
||||
|
||||
|
||||
def run_simulation_manually_by_date(
|
||||
network_name: str, start_time: datetime, duration: int
|
||||
) -> None:
|
||||
@@ -663,154 +650,6 @@ async def fastapi_pump_failure(data: PumpFailureState = Body(..., description="
|
||||
return json.dumps("SUCCESS")
|
||||
|
||||
|
||||
@router.post("/pressure-sensor-placement-sensitivity-calculations", summary="压力传感器放置-灵敏度分析(基础)", description="基于灵敏度分析方法,为指定管网项目确定最优的压力传感器放置位置。此为基础版本。")
|
||||
async def pressure_sensor_placement_sensitivity_endpoint(
|
||||
name: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
scheme_name: str = Query(..., description="放置方案名称"),
|
||||
sensor_number: int = Query(..., description="传感器数量"),
|
||||
min_diameter: int = Query(..., description="最小管径限制(毫米)"),
|
||||
username: str = Query(..., description="用户名"),
|
||||
):
|
||||
"""
|
||||
压力传感器放置-灵敏度分析(基础版本)
|
||||
|
||||
- **name**: 管网名称(或数据库名称)
|
||||
- **scheme_name**: 放置方案名称
|
||||
- **sensor_number**: 传感器数量
|
||||
- **min_diameter**: 最小管径限制(毫米)
|
||||
- **username**: 用户名
|
||||
|
||||
基于灵敏度分析方法确定传感器放置位置。
|
||||
"""
|
||||
return pressure_sensor_placement_sensitivity(
|
||||
name, scheme_name, sensor_number, min_diameter, username
|
||||
)
|
||||
|
||||
|
||||
@router.post("/pressure-sensor-placement-sensitivities", summary="压力传感器放置-灵敏度分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于灵敏度分析方法确定最优放置位置。")
|
||||
async def fastapi_pressure_sensor_placement_sensitivity(
|
||||
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
|
||||
) -> None:
|
||||
"""
|
||||
压力传感器放置-灵敏度分析(高级版本)
|
||||
|
||||
请求体参数:
|
||||
- **name**: 管网名称(或数据库名称)
|
||||
- **scheme_name**: 放置方案名称
|
||||
- **sensor_number**: 传感器数量
|
||||
- **min_diameter**: 最小管径限制(毫米)
|
||||
- **username**: 用户名
|
||||
|
||||
基于灵敏度分析方法确定压力传感器的最优放置位置。
|
||||
"""
|
||||
item = data.dict()
|
||||
pressure_sensor_placement_sensitivity(
|
||||
name=item["name"],
|
||||
scheme_name=item["scheme_name"],
|
||||
sensor_number=item["sensor_number"],
|
||||
min_diameter=item["min_diameter"],
|
||||
username=item["username"],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/pressure-sensor-placement-kmeans-calculations", summary="压力传感器放置-KMeans聚类分析(基础)", description="基于KMeans聚类算法,为指定管网项目确定压力传感器的最优放置位置。此为基础版本。")
|
||||
async def pressure_sensor_placement_kmeans_endpoint(
|
||||
name: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
scheme_name: str = Query(..., description="放置方案名称"),
|
||||
sensor_number: int = Query(..., description="传感器数量"),
|
||||
min_diameter: int = Query(..., description="最小管径限制(毫米)"),
|
||||
username: str = Query(..., description="用户名"),
|
||||
):
|
||||
"""
|
||||
压力传感器放置-KMeans聚类分析(基础版本)
|
||||
|
||||
- **name**: 管网名称(或数据库名称)
|
||||
- **scheme_name**: 放置方案名称
|
||||
- **sensor_number**: 传感器数量
|
||||
- **min_diameter**: 最小管径限制(毫米)
|
||||
- **username**: 用户名
|
||||
|
||||
基于KMeans聚类算法确定传感器放置位置。
|
||||
"""
|
||||
return pressure_sensor_placement_kmeans(
|
||||
name, scheme_name, sensor_number, min_diameter, username
|
||||
)
|
||||
|
||||
|
||||
@router.post("/pressure-sensor-placement-kmeans", summary="压力传感器放置-KMeans聚类分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于KMeans聚类算法确定最优放置位置。")
|
||||
async def fastapi_pressure_sensor_placement_kmeans(
|
||||
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
|
||||
) -> None:
|
||||
"""
|
||||
压力传感器放置-KMeans聚类分析(高级版本)
|
||||
|
||||
请求体参数:
|
||||
- **name**: 管网名称(或数据库名称)
|
||||
- **scheme_name**: 放置方案名称
|
||||
- **sensor_number**: 传感器数量
|
||||
- **min_diameter**: 最小管径限制(毫米)
|
||||
- **username**: 用户名
|
||||
|
||||
基于KMeans聚类算法确定压力传感器的最优放置位置。
|
||||
"""
|
||||
item = data.dict()
|
||||
pressure_sensor_placement_kmeans(
|
||||
name=item["name"],
|
||||
scheme_name=item["scheme_name"],
|
||||
sensor_number=item["sensor_number"],
|
||||
min_diameter=item["min_diameter"],
|
||||
username=item["username"],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/sensor-placement-schemes", summary="传感器放置方案创建", description="创建新的传感器放置方案,支持灵敏度分析和KMeans聚类两种方法。根据指定的方法自动计算最优的传感器放置位置。")
|
||||
async def fastapi_pressure_sensor_placement(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
scheme_name: str = Query(..., description="放置方案名称"),
|
||||
sensor_type: str = Query(..., description="传感器类型"),
|
||||
method: str = Query(..., description="放置方法('sensitivity'或'kmeans')"),
|
||||
sensor_count: int = Query(..., description="传感器数量"),
|
||||
min_diameter: int = Query(0, description="最小管径限制(毫米),默认0"),
|
||||
user_name: str = Query(..., description="用户名"),
|
||||
) -> str:
|
||||
"""
|
||||
传感器放置方案创建
|
||||
|
||||
- **network**: 管网名称(或数据库名称)
|
||||
- **scheme_name**: 放置方案名称
|
||||
- **sensor_type**: 传感器类型
|
||||
- **method**: 放置方法('sensitivity'或'kmeans')
|
||||
- **sensor_count**: 传感器数量
|
||||
- **min_diameter**: 最小管径限制(毫米,默认0)
|
||||
- **user_name**: 用户名
|
||||
|
||||
支持两种放置方法:
|
||||
- sensitivity: 基于灵敏度分析
|
||||
- kmeans: 基于KMeans聚类
|
||||
"""
|
||||
if method not in ["sensitivity", "kmeans"]:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Invalid method. Must be 'sensitivity' or 'kmeans'"
|
||||
)
|
||||
if method == "sensitivity":
|
||||
pressure_sensor_placement_sensitivity(
|
||||
name=network,
|
||||
scheme_name=scheme_name,
|
||||
sensor_number=sensor_count,
|
||||
min_diameter=min_diameter,
|
||||
username=user_name,
|
||||
)
|
||||
elif method == "kmeans":
|
||||
pressure_sensor_placement_kmeans(
|
||||
name=network,
|
||||
scheme_name=scheme_name,
|
||||
sensor_number=sensor_count,
|
||||
min_diameter=min_diameter,
|
||||
username=user_name,
|
||||
)
|
||||
return "success"
|
||||
|
||||
|
||||
@router.post("/simulation-runs", summary="手动运行日期指定模拟", description="根据指定的开始时间和持续时间,手动运行水力模拟。开始时间必须是显式带时区的 ISO 8601 / RFC3339 时间。")
|
||||
async def fastapi_run_simulation_manually_by_date(
|
||||
data: RunSimulationManuallyByDate = Body(..., description="模拟运行参数"),
|
||||
@@ -829,30 +668,6 @@ async def fastapi_run_simulation_manually_by_date(
|
||||
item = data.model_dump()
|
||||
try:
|
||||
simulation.query_corresponding_element_id_and_query_id(item["name"])
|
||||
simulation.query_corresponding_pattern_id_and_query_id(item["name"])
|
||||
region_result = simulation.query_non_realtime_region(item["name"])
|
||||
globals.source_outflow_region_id = simulation.get_source_outflow_region_id(
|
||||
item["name"], region_result
|
||||
)
|
||||
globals.realtime_region_pipe_flow_and_demand_id = (
|
||||
simulation.query_realtime_region_pipe_flow_and_demand_id(
|
||||
item["name"], region_result
|
||||
)
|
||||
)
|
||||
globals.pipe_flow_region_patterns = simulation.query_pipe_flow_region_patterns(
|
||||
item["name"]
|
||||
)
|
||||
globals.non_realtime_region_patterns = (
|
||||
simulation.query_non_realtime_region_patterns(item["name"], region_result)
|
||||
)
|
||||
(
|
||||
globals.source_outflow_region_patterns,
|
||||
globals.realtime_region_pipe_flow_and_demand_patterns,
|
||||
) = simulation.get_realtime_region_patterns(
|
||||
item["name"],
|
||||
globals.source_outflow_region_id,
|
||||
globals.realtime_region_pipe_flow_and_demand_id,
|
||||
)
|
||||
start_time = parse_utc_time(item["start_time"], field_name="start_time")
|
||||
run_simulation_manually_by_date(
|
||||
item["name"], start_time, item["duration"]
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
from fastapi import APIRouter, Depends, Request, Query
|
||||
from app.auth.permissions import SIMULATION_RUN, require_permission
|
||||
from app.services.tjnetwork import (
|
||||
ChangeSet,
|
||||
get_current_operation,
|
||||
execute_undo,
|
||||
execute_redo,
|
||||
list_snapshot,
|
||||
have_snapshot,
|
||||
have_snapshot_for_operation,
|
||||
have_snapshot_for_current_operation,
|
||||
take_snapshot_for_operation,
|
||||
take_snapshot_for_current_operation,
|
||||
take_snapshot,
|
||||
pick_snapshot,
|
||||
pick_operation,
|
||||
sync_with_server,
|
||||
execute_batch_commands,
|
||||
execute_batch_command,
|
||||
get_restore_operation,
|
||||
set_restore_operation,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/current-operation-ids", summary="获取当前操作ID", description="获取网络当前的操作ID")
|
||||
async def get_current_operation_id_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int:
|
||||
"""
|
||||
获取当前操作ID
|
||||
|
||||
返回网络当前正在执行的操作ID
|
||||
"""
|
||||
return get_current_operation(network)
|
||||
|
||||
@router.post("/undos", summary="撤销操作", description="撤销网络上最后的一个操作")
|
||||
async def undo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")):
|
||||
"""
|
||||
撤销操作
|
||||
|
||||
撤销网络上最近执行的一个操作
|
||||
"""
|
||||
return execute_undo(network)
|
||||
|
||||
@router.post("/redos", summary="重做操作", description="重做网络上被撤销的操作")
|
||||
async def redo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")):
|
||||
"""
|
||||
重做操作
|
||||
|
||||
重做网络上被撤销的操作
|
||||
"""
|
||||
return execute_redo(network)
|
||||
|
||||
@router.get("/snapshots", summary="获取快照列表", description="获取网络中的所有快照")
|
||||
async def list_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[tuple[int, str]]:
|
||||
"""
|
||||
获取快照列表
|
||||
|
||||
返回网络中所有可用的快照及其信息
|
||||
"""
|
||||
return list_snapshot(network)
|
||||
|
||||
@router.get("/snapshots/existence", summary="检查快照是否存在", description="检查指定标签的快照是否存在")
|
||||
async def have_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> bool:
|
||||
"""
|
||||
检查快照是否存在
|
||||
|
||||
返回指定标签的快照是否存在
|
||||
"""
|
||||
return have_snapshot(network, tag)
|
||||
|
||||
@router.get("/snapshot-for-operations", summary="检查操作快照是否存在", description="检查指定操作ID的快照是否存在")
|
||||
async def have_snapshot_for_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> bool:
|
||||
"""
|
||||
检查操作快照是否存在
|
||||
|
||||
返回指定操作ID的快照是否存在
|
||||
"""
|
||||
return have_snapshot_for_operation(network, operation)
|
||||
|
||||
@router.get("/snapshot-for-current-operations", summary="检查当前操作快照是否存在", description="检查当前操作的快照是否存在")
|
||||
async def have_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> bool:
|
||||
"""
|
||||
检查当前操作快照是否存在
|
||||
|
||||
返回当前操作的快照是否存在
|
||||
"""
|
||||
return have_snapshot_for_current_operation(network)
|
||||
|
||||
@router.post("/snapshot-for-operations", summary="为操作创建快照", description="为指定的操作创建快照")
|
||||
async def take_snapshot_for_operation_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
operation: int = Query(..., description="操作ID"),
|
||||
tag: str = Query(..., description="快照标签")
|
||||
) -> None:
|
||||
"""
|
||||
为操作创建快照
|
||||
|
||||
为指定操作创建一个带标签的快照
|
||||
"""
|
||||
return take_snapshot_for_operation(network, operation, tag)
|
||||
|
||||
@router.post("/snapshot-for-current-operations", summary="为当前操作创建快照", description="为当前操作创建快照")
|
||||
async def take_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None:
|
||||
"""
|
||||
为当前操作创建快照
|
||||
|
||||
为网络当前操作创建一个快照
|
||||
"""
|
||||
return take_snapshot_for_current_operation(network, tag)
|
||||
|
||||
@router.post("/snapshots", summary="创建快照", description="为网络创建一个快照")
|
||||
async def take_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None:
|
||||
"""
|
||||
创建快照
|
||||
|
||||
为网络创建一个带标签的快照
|
||||
"""
|
||||
return take_snapshot(network, tag)
|
||||
|
||||
@router.patch("/snapshots", summary="选择快照", description="选择并恢复到指定的快照", response_model=None)
|
||||
async def pick_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签"), discard: bool = Query(False, description="是否丢弃当前更改")) -> ChangeSet:
|
||||
"""
|
||||
选择快照
|
||||
|
||||
选择并恢复到指定的快照
|
||||
"""
|
||||
return pick_snapshot(network, tag, discard)
|
||||
|
||||
@router.patch("/operations", summary="选择操作", description="选择并恢复到指定的操作", response_model=None)
|
||||
async def pick_operation_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
operation: int = Query(..., description="操作ID"),
|
||||
discard: bool = Query(False, description="是否丢弃当前更改")
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
选择操作
|
||||
|
||||
选择并恢复到指定的操作
|
||||
"""
|
||||
return pick_operation(network, operation, discard)
|
||||
|
||||
@router.post("/with-servers", summary="与服务器同步", description="将网络与服务器同步到指定操作", response_model=None)
|
||||
async def sync_with_server_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
operation: int = Query(..., description="目标操作ID"),
|
||||
_=Depends(require_permission(SIMULATION_RUN)),
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
与服务器同步
|
||||
|
||||
将网络与服务器同步到指定的操作
|
||||
"""
|
||||
return sync_with_server(network, operation)
|
||||
|
||||
@router.post("/network-command-batches", summary="执行批量命令", description="执行多个网络操作命令", response_model=None)
|
||||
async def execute_batch_commands_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), req: Request = None) -> ChangeSet:
|
||||
"""
|
||||
执行批量命令
|
||||
|
||||
在网络上执行多个操作命令
|
||||
"""
|
||||
jo_root = await req.json()
|
||||
cs: ChangeSet = ChangeSet()
|
||||
cs.operations = jo_root["operations"]
|
||||
rcs = execute_batch_commands(network, cs)
|
||||
return rcs
|
||||
|
||||
@router.post("/network-command-batches/compressed", summary="执行压缩批量命令", description="执行压缩的批量命令", response_model=None)
|
||||
async def execute_compressed_batch_commands_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
req: Request = None
|
||||
) -> ChangeSet:
|
||||
"""
|
||||
执行压缩批量命令
|
||||
|
||||
执行压缩格式的批量命令
|
||||
"""
|
||||
jo_root = await req.json()
|
||||
cs: ChangeSet = ChangeSet()
|
||||
cs.operations = jo_root["operations"]
|
||||
return execute_batch_command(network, cs)
|
||||
|
||||
@router.get("/restore-operations", summary="获取恢复操作ID", description="获取网络的恢复操作ID")
|
||||
async def get_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int:
|
||||
"""
|
||||
获取恢复操作ID
|
||||
|
||||
返回网络的恢复操作ID
|
||||
"""
|
||||
return get_restore_operation(network)
|
||||
|
||||
@router.patch("/restore-operations", summary="设置恢复操作ID", description="设置网络的恢复操作ID")
|
||||
async def set_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> None:
|
||||
"""
|
||||
设置恢复操作ID
|
||||
|
||||
设置网络的恢复操作ID
|
||||
"""
|
||||
return set_restore_operation(network, operation)
|
||||
@@ -0,0 +1,82 @@
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query
|
||||
from psycopg import AsyncConnection
|
||||
|
||||
from app.infra.db.timescaledb.repositories.analysis import AnalysisResultsRepository
|
||||
|
||||
from .dependencies import get_timescale_connection
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/timeseries/analysis/runs/{run_id}/results", status_code=201)
|
||||
async def store_analysis_results(
|
||||
run_id: UUID = Path(..., description="分析运行 ID"),
|
||||
payload: dict = Body(...),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
try:
|
||||
node_rows = payload.get("node_results", [])
|
||||
link_rows = payload.get("link_results", [])
|
||||
await AnalysisResultsRepository.store_results(
|
||||
conn, run_id, node_rows, link_rows
|
||||
)
|
||||
return {
|
||||
"run_id": run_id,
|
||||
"node_count": len(node_rows),
|
||||
"link_count": len(link_rows),
|
||||
}
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.get("/timeseries/analysis/runs/{run_id}/nodes/{node_id}")
|
||||
async def get_analysis_node_series(
|
||||
run_id: UUID,
|
||||
node_id: str,
|
||||
start_time: datetime = Query(...),
|
||||
end_time: datetime = Query(...),
|
||||
field: str = Query(...),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
try:
|
||||
return await AnalysisResultsRepository.get_node_series(
|
||||
conn, run_id, node_id, start_time, end_time, field
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.get("/timeseries/analysis/runs/{run_id}/links/{link_id}")
|
||||
async def get_analysis_link_series(
|
||||
run_id: UUID,
|
||||
link_id: str,
|
||||
start_time: datetime = Query(...),
|
||||
end_time: datetime = Query(...),
|
||||
field: str = Query(...),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
try:
|
||||
return await AnalysisResultsRepository.get_link_series(
|
||||
conn, run_id, link_id, start_time, end_time, field
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.get("/timeseries/analysis/runs/{run_id}/values")
|
||||
async def get_analysis_values_at_time(
|
||||
run_id: UUID,
|
||||
result_time: datetime = Query(...),
|
||||
element_type: str = Query(..., pattern="^(node|link)$"),
|
||||
field: str = Query(...),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
try:
|
||||
return await AnalysisResultsRepository.get_values_at_time(
|
||||
conn, run_id, element_type, result_time, field
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
@@ -1,6 +1,7 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from datetime import datetime
|
||||
from psycopg import AsyncConnection
|
||||
from uuid import UUID
|
||||
|
||||
from app.infra.db.timescaledb.composite_queries import CompositeQueries
|
||||
from .dependencies import get_timescale_connection, get_postgres_connection
|
||||
@@ -13,8 +14,7 @@ async def get_scada_associated_simulation_data(
|
||||
start_time: datetime = Query(..., description="查询开始时间"),
|
||||
end_time: datetime = Query(..., description="查询结束时间"),
|
||||
device_ids: str = Query(..., description="SCADA设备ID列表,逗号分隔"),
|
||||
scheme_type: str = Query(None, description="方案类型,若为空则查询实时数据"),
|
||||
scheme_name: str = Query(None, description="方案名称,若为空则查询实时数据"),
|
||||
run_id: UUID | None = Query(None, description="分析运行 ID;为空时查询实时数据"),
|
||||
timescale_conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
postgres_conn: AsyncConnection = Depends(get_postgres_connection),
|
||||
):
|
||||
@@ -22,14 +22,13 @@ async def get_scada_associated_simulation_data(
|
||||
获取SCADA关联的link/node模拟值
|
||||
|
||||
根据传入的SCADA device_ids,找到关联的link/node,
|
||||
并根据对应的type,查询对应的模拟数据。支持查询实时或方案数据。
|
||||
并根据对应的type,查询对应的模拟数据。支持查询实时或分析运行数据。
|
||||
|
||||
Args:
|
||||
start_time: 查询开始时间
|
||||
end_time: 查询结束时间
|
||||
device_ids: SCADA设备ID列表,用逗号分隔
|
||||
scheme_type: 方案类型,若为空则查询实时数据
|
||||
scheme_name: 方案名称,若为空则查询实时数据
|
||||
run_id: 分析运行 ID,若为空则查询实时数据
|
||||
timescale_conn: TimescaleDB连接
|
||||
postgres_conn: PostgreSQL连接
|
||||
|
||||
@@ -46,15 +45,14 @@ async def get_scada_associated_simulation_data(
|
||||
else []
|
||||
)
|
||||
|
||||
if scheme_type and scheme_name:
|
||||
result = await CompositeQueries.get_scada_associated_scheme_simulation_data(
|
||||
if run_id is not None:
|
||||
result = await CompositeQueries.get_scada_associated_analysis_simulation_data(
|
||||
timescale_conn,
|
||||
postgres_conn,
|
||||
device_ids_list,
|
||||
start_time,
|
||||
end_time,
|
||||
scheme_type,
|
||||
scheme_name,
|
||||
run_id,
|
||||
)
|
||||
else:
|
||||
result = (
|
||||
@@ -80,23 +78,21 @@ async def get_feature_simulation_data(
|
||||
feature_infos: str = Query(
|
||||
..., description="特征信息,格式: id1:type1,id2:type2,type为pipe(管道)或junction(节点)"
|
||||
),
|
||||
scheme_type: str = Query(None, description="方案类型,若为空则查询实时数据"),
|
||||
scheme_name: str = Query(None, description="方案名称,若为空则查询实时数据"),
|
||||
run_id: UUID | None = Query(None, description="分析运行 ID;为空时查询实时数据"),
|
||||
timescale_conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
获取link/node模拟值
|
||||
|
||||
根据传入的featureInfos,找到关联的link/node,
|
||||
并根据对应的type,查询对应的模拟数据。支持查询实时或方案数据。
|
||||
并根据对应的type,查询对应的模拟数据。支持查询实时或分析运行数据。
|
||||
|
||||
Args:
|
||||
start_time: 查询开始时间
|
||||
end_time: 查询结束时间
|
||||
feature_infos: 格式为 "element_id1:type1,element_id2:type2"
|
||||
例如: "P1:pipe,J1:junction"
|
||||
scheme_type: 方案类型,若为空则查询实时数据
|
||||
scheme_name: 方案名称,若为空则查询实时数据
|
||||
run_id: 分析运行 ID,若为空则查询实时数据
|
||||
timescale_conn: TimescaleDB连接
|
||||
|
||||
Returns:
|
||||
@@ -119,14 +115,13 @@ async def get_feature_simulation_data(
|
||||
if not feature_infos_list:
|
||||
raise HTTPException(status_code=400, detail="feature_infos cannot be empty")
|
||||
|
||||
if scheme_type and scheme_name:
|
||||
result = await CompositeQueries.get_scheme_simulation_data(
|
||||
if run_id is not None:
|
||||
result = await CompositeQueries.get_analysis_simulation_data(
|
||||
timescale_conn,
|
||||
feature_infos_list,
|
||||
start_time,
|
||||
end_time,
|
||||
scheme_type,
|
||||
scheme_name,
|
||||
run_id,
|
||||
)
|
||||
else:
|
||||
result = await CompositeQueries.get_realtime_simulation_data(
|
||||
|
||||
@@ -1,391 +0,0 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
from psycopg import AsyncConnection
|
||||
|
||||
from app.infra.db.timescaledb.repositories.scheme import SchemeRepository
|
||||
from .dependencies import get_timescale_connection
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/timeseries/schemes/links/batches", status_code=201, summary="批量插入方案管道数据")
|
||||
async def insert_scheme_links(
|
||||
data: List[dict] = Body(..., description="方案管道数据列表"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
批量插入方案管道数据
|
||||
|
||||
将特定方案的管道模拟数据批量插入时间序列数据库。
|
||||
|
||||
Args:
|
||||
data: 方案管道数据列表
|
||||
|
||||
Returns:
|
||||
插入成功的记录数
|
||||
"""
|
||||
await SchemeRepository.insert_links_batch(conn, data)
|
||||
return {"message": f"Inserted {len(data)} records"}
|
||||
|
||||
|
||||
@router.get("/timeseries/schemes/links", summary="查询方案管道数据")
|
||||
async def get_scheme_links(
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
start_time: datetime = Query(..., description="查询开始时间"),
|
||||
end_time: datetime = Query(..., description="查询结束时间"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
查询指定方案和时间范围内的管道数据
|
||||
|
||||
根据方案和时间范围查询管道的模拟值。
|
||||
|
||||
Args:
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
start_time: 查询开始时间
|
||||
end_time: 查询结束时间
|
||||
|
||||
Returns:
|
||||
方案管道数据列表
|
||||
"""
|
||||
return await SchemeRepository.get_links_by_scheme_and_time_range(
|
||||
conn, scheme_type, scheme_name, start_time, end_time
|
||||
)
|
||||
|
||||
|
||||
@router.get("/timeseries/schemes/links/{link_id}/field", summary="查询方案管道字段数据")
|
||||
async def get_scheme_link_field(
|
||||
link_id: str = Path(..., description="管道ID"),
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
start_time: datetime = Query(..., description="查询开始时间"),
|
||||
end_time: datetime = Query(..., description="查询结束时间"),
|
||||
field: str = Query(..., description="要查询的字段名称"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
查询指定方案管道的特定字段数据
|
||||
|
||||
查询特定方案中指定管道在时间范围内的特定字段值。
|
||||
|
||||
Args:
|
||||
link_id: 管道ID
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
start_time: 查询开始时间
|
||||
end_time: 查询结束时间
|
||||
field: 字段名称
|
||||
|
||||
Returns:
|
||||
字段数据列表
|
||||
|
||||
Raises:
|
||||
HTTPException: 当查询参数无效时返回400错误
|
||||
"""
|
||||
try:
|
||||
return await SchemeRepository.get_link_field_by_scheme_and_time_range(
|
||||
conn, scheme_type, scheme_name, start_time, end_time, link_id, field
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.patch("/timeseries/schemes/links/{link_id}/field", summary="更新方案管道字段")
|
||||
async def update_scheme_link_field(
|
||||
link_id: str = Path(..., description="管道ID"),
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
time: datetime = Query(..., description="更新数据的时间戳"),
|
||||
field: str = Query(..., description="要更新的字段名称"),
|
||||
value: float = Query(..., description="更新的字段值"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
更新指定方案管道的字段值
|
||||
|
||||
更新特定方案中指定管道在某个时间的字段数据。
|
||||
|
||||
Args:
|
||||
link_id: 管道ID
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
time: 数据时间戳
|
||||
field: 字段名称
|
||||
value: 字段新值
|
||||
|
||||
Returns:
|
||||
更新结果信息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当字段不存在或更新失败时返回400错误
|
||||
"""
|
||||
try:
|
||||
await SchemeRepository.update_link_field(
|
||||
conn, time, scheme_type, scheme_name, link_id, field, value
|
||||
)
|
||||
return {"message": "Updated successfully"}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/timeseries/schemes/links", summary="删除方案管道数据")
|
||||
async def delete_scheme_links(
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
start_time: datetime = Query(..., description="删除开始时间"),
|
||||
end_time: datetime = Query(..., description="删除结束时间"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
删除指定方案和时间范围内的管道数据
|
||||
|
||||
删除在指定方案和时间范围内的所有管道模拟数据。
|
||||
|
||||
Args:
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
start_time: 删除开始时间
|
||||
end_time: 删除结束时间
|
||||
|
||||
Returns:
|
||||
删除结果信息
|
||||
"""
|
||||
await SchemeRepository.delete_links_by_scheme_and_time_range(
|
||||
conn, scheme_type, scheme_name, start_time, end_time
|
||||
)
|
||||
return {"message": "Deleted successfully"}
|
||||
|
||||
|
||||
@router.post("/timeseries/schemes/nodes/batches", status_code=201, summary="批量插入方案节点数据")
|
||||
async def insert_scheme_nodes(
|
||||
data: List[dict] = Body(..., description="方案节点数据列表"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
批量插入方案节点数据
|
||||
|
||||
将特定方案的节点模拟数据批量插入时间序列数据库。
|
||||
|
||||
Args:
|
||||
data: 方案节点数据列表
|
||||
|
||||
Returns:
|
||||
插入成功的记录数
|
||||
"""
|
||||
await SchemeRepository.insert_nodes_batch(conn, data)
|
||||
return {"message": f"Inserted {len(data)} records"}
|
||||
|
||||
|
||||
@router.get("/timeseries/schemes/nodes/{node_id}/field", summary="查询方案节点字段数据")
|
||||
async def get_scheme_node_field(
|
||||
node_id: str = Path(..., description="节点ID"),
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
start_time: datetime = Query(..., description="查询开始时间"),
|
||||
end_time: datetime = Query(..., description="查询结束时间"),
|
||||
field: str = Query(..., description="要查询的字段名称"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
查询指定方案节点的特定字段数据
|
||||
|
||||
查询特定方案中指定节点在时间范围内的特定字段值。
|
||||
|
||||
Args:
|
||||
node_id: 节点ID
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
start_time: 查询开始时间
|
||||
end_time: 查询结束时间
|
||||
field: 字段名称
|
||||
|
||||
Returns:
|
||||
字段数据列表
|
||||
|
||||
Raises:
|
||||
HTTPException: 当查询参数无效时返回400错误
|
||||
"""
|
||||
try:
|
||||
return await SchemeRepository.get_node_field_by_scheme_and_time_range(
|
||||
conn, scheme_type, scheme_name, start_time, end_time, node_id, field
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.patch("/timeseries/schemes/nodes/{node_id}/field", summary="更新方案节点字段")
|
||||
async def update_scheme_node_field(
|
||||
node_id: str = Path(..., description="节点ID"),
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
time: datetime = Query(..., description="更新数据的时间戳"),
|
||||
field: str = Query(..., description="要更新的字段名称"),
|
||||
value: float = Query(..., description="更新的字段值"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
更新指定方案节点的字段值
|
||||
|
||||
更新特定方案中指定节点在某个时间的字段数据。
|
||||
|
||||
Args:
|
||||
node_id: 节点ID
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
time: 数据时间戳
|
||||
field: 字段名称
|
||||
value: 字段新值
|
||||
|
||||
Returns:
|
||||
更新结果信息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当字段不存在或更新失败时返回400错误
|
||||
"""
|
||||
try:
|
||||
await SchemeRepository.update_node_field(
|
||||
conn, time, scheme_type, scheme_name, node_id, field, value
|
||||
)
|
||||
return {"message": "Updated successfully"}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/timeseries/schemes/nodes", summary="删除方案节点数据")
|
||||
async def delete_scheme_nodes(
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
start_time: datetime = Query(..., description="删除开始时间"),
|
||||
end_time: datetime = Query(..., description="删除结束时间"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
删除指定方案和时间范围内的节点数据
|
||||
|
||||
删除在指定方案和时间范围内的所有节点模拟数据。
|
||||
|
||||
Args:
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
start_time: 删除开始时间
|
||||
end_time: 删除结束时间
|
||||
|
||||
Returns:
|
||||
删除结果信息
|
||||
"""
|
||||
await SchemeRepository.delete_nodes_by_scheme_and_time_range(
|
||||
conn, scheme_type, scheme_name, start_time, end_time
|
||||
)
|
||||
return {"message": "Deleted successfully"}
|
||||
|
||||
|
||||
@router.post("/timeseries/schemes/simulation-results", status_code=201, summary="存储方案模拟结果")
|
||||
async def store_scheme_simulation_result(
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
node_result_list: List[dict] = Body(..., description="节点模拟结果列表"),
|
||||
link_result_list: List[dict] = Body(..., description="管道模拟结果列表"),
|
||||
result_start_time: str = Query(..., description="模拟结果开始时间"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
存储方案模拟结果到时间序列数据库
|
||||
|
||||
将特定方案的节点和管道模拟计算结果批量存储到TimescaleDB数据库。
|
||||
|
||||
Args:
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
node_result_list: 节点模拟结果列表
|
||||
link_result_list: 管道模拟结果列表
|
||||
result_start_time: 模拟结果对应的起始时间
|
||||
|
||||
Returns:
|
||||
存储结果信息
|
||||
"""
|
||||
await SchemeRepository.store_scheme_simulation_result(
|
||||
conn,
|
||||
scheme_type,
|
||||
scheme_name,
|
||||
node_result_list,
|
||||
link_result_list,
|
||||
result_start_time,
|
||||
)
|
||||
return {"message": "Scheme simulation results stored successfully"}
|
||||
|
||||
|
||||
@router.get(
|
||||
"/timeseries/schemes/records", summary="按方案、时间和属性查询数据"
|
||||
)
|
||||
async def query_scheme_records_by_scheme_time_property(
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
query_time: str = Query(..., description="查询时间"),
|
||||
type: str = Query(..., description="元素类型,pipe(管道)或 junction(节点)"),
|
||||
property: str = Query(..., description="要查询的属性名称"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
按指定方案、时间和属性查询所有方案数据
|
||||
|
||||
查询在特定方案和时间点,所有指定类型元素的特定属性值。
|
||||
|
||||
Args:
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
query_time: 查询时间
|
||||
type: 元素类型(pipe或junction)
|
||||
property: 属性名称
|
||||
|
||||
Returns:
|
||||
查询结果列表
|
||||
|
||||
Raises:
|
||||
HTTPException: 当查询参数无效时返回400错误
|
||||
"""
|
||||
try:
|
||||
results = await SchemeRepository.query_all_record_by_scheme_time_property(
|
||||
conn, scheme_type, scheme_name, query_time, type, property
|
||||
)
|
||||
return {"results": results}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/timeseries/schemes/simulation-results", summary="按ID和时间查询方案模拟数据")
|
||||
async def query_scheme_simulation_by_id_time(
|
||||
scheme_type: str = Query(..., description="方案类型"),
|
||||
scheme_name: str = Query(..., description="方案名称"),
|
||||
id: str = Query(..., description="元素ID(管道ID或节点ID)"),
|
||||
type: str = Query(..., description="元素类型,pipe(管道)或 junction(节点)"),
|
||||
query_time: str = Query(..., description="查询时间"),
|
||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||
):
|
||||
"""
|
||||
按指定ID和时间查询方案模拟结果
|
||||
|
||||
查询特定方案中的元素在某一时间点的模拟数据。
|
||||
|
||||
Args:
|
||||
scheme_type: 方案类型
|
||||
scheme_name: 方案名称
|
||||
id: 元素ID
|
||||
type: 元素类型(pipe或junction)
|
||||
query_time: 查询时间
|
||||
|
||||
Returns:
|
||||
模拟结果数据
|
||||
|
||||
Raises:
|
||||
HTTPException: 当查询参数无效时返回400错误
|
||||
"""
|
||||
try:
|
||||
result = await SchemeRepository.query_scheme_simulation_result_by_id_time(
|
||||
conn, scheme_type, scheme_name, id, type, query_time
|
||||
)
|
||||
return {"result": result}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
+7
-35
@@ -7,20 +7,15 @@ from app.api.v1.endpoints import (
|
||||
audit,
|
||||
burst_detection,
|
||||
burst_location,
|
||||
extension,
|
||||
geocoding,
|
||||
leakage,
|
||||
meta,
|
||||
misc,
|
||||
model_import,
|
||||
project,
|
||||
project_data,
|
||||
risk,
|
||||
scada,
|
||||
schemes,
|
||||
sensor_placement,
|
||||
simulation,
|
||||
snapshots,
|
||||
web_search,
|
||||
)
|
||||
from app.api.v1.endpoints.components import (
|
||||
@@ -45,15 +40,14 @@ from app.api.v1.endpoints.network import (
|
||||
valves,
|
||||
)
|
||||
from app.api.v1.endpoints.timeseries import (
|
||||
analysis as ts_analysis,
|
||||
composite as ts_composite,
|
||||
realtime as ts_realtime,
|
||||
scada as ts_scada,
|
||||
scheme as ts_scheme,
|
||||
)
|
||||
from app.auth.permissions import (
|
||||
BURST_RUN,
|
||||
OPTIMIZATION_RUN,
|
||||
RISK_RUN,
|
||||
SCADA_CLEAN,
|
||||
SCADA_VIEW,
|
||||
SIMULATION_RUN,
|
||||
@@ -88,7 +82,6 @@ simulation_access = Depends(
|
||||
webgis_view_access = Depends(require_permission(WEBGIS_VIEW))
|
||||
simulation_run_access = Depends(require_permission(SIMULATION_RUN))
|
||||
burst_run_access = Depends(require_permission(BURST_RUN))
|
||||
risk_run_access = Depends(require_permission(RISK_RUN))
|
||||
optimization_run_access = Depends(require_permission(OPTIMIZATION_RUN))
|
||||
|
||||
# Core services
|
||||
@@ -139,32 +132,16 @@ api_router.include_router(
|
||||
tags=["Simulation Control"],
|
||||
dependencies=[simulation_run_access],
|
||||
)
|
||||
api_router.include_router(scada.router, dependencies=[scada_access])
|
||||
api_router.include_router(
|
||||
scada.router,
|
||||
tags=["SCADA Metadata"],
|
||||
dependencies=[scada_access],
|
||||
)
|
||||
api_router.include_router(
|
||||
sensor_placement.router,
|
||||
tags=["Sensor Placement"],
|
||||
dependencies=[optimization_run_access],
|
||||
)
|
||||
api_router.include_router(
|
||||
snapshots.router,
|
||||
tags=["Snapshots"],
|
||||
dependencies=[simulation_access],
|
||||
)
|
||||
api_router.include_router(
|
||||
schemes.router,
|
||||
tags=["Schemes"],
|
||||
dependencies=[simulation_access],
|
||||
)
|
||||
api_router.include_router(
|
||||
misc.router,
|
||||
tags=["Misc"],
|
||||
dependencies=[webgis_view_access],
|
||||
)
|
||||
api_router.include_router(
|
||||
risk.router,
|
||||
tags=["Risk"],
|
||||
dependencies=[risk_run_access],
|
||||
)
|
||||
api_router.include_router(
|
||||
web_search.router,
|
||||
tags=["Web Search"],
|
||||
@@ -194,7 +171,7 @@ api_router.include_router(
|
||||
# TimescaleDB data
|
||||
for endpoint_router, tag in (
|
||||
(ts_realtime.router, "TimescaleDB - Realtime"),
|
||||
(ts_scheme.router, "TimescaleDB - Scheme"),
|
||||
(ts_analysis.router, "TimescaleDB - Analysis"),
|
||||
):
|
||||
api_router.include_router(
|
||||
endpoint_router,
|
||||
@@ -217,8 +194,3 @@ api_router.include_router(
|
||||
tags=["Project Data"],
|
||||
dependencies=[webgis_view_access],
|
||||
)
|
||||
api_router.include_router(
|
||||
extension.router,
|
||||
tags=["Extension"],
|
||||
dependencies=[webgis_access],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user