import os from typing import Any from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, Body from pydantic import BaseModel, ConfigDict, Field from starlette.concurrency import run_in_threadpool from app.auth.keycloak_dependencies import get_current_keycloak_username from app.services.dma_leakage_estimation import ( run_leakage_identification, ) router = APIRouter() DEFAULT_N_WORKERS = max(1, min((os.cpu_count() or 1) - 1, 4)) MAX_POPULATION_SIZE = 1_000 MAX_GENERATIONS = 1_000 MAX_DURATION_HOURS = 168 class LeakageIdentifyRequest(BaseModel): """漏损识别请求模型""" model_config = ConfigDict(extra="forbid") network: str = Field(..., description="管网名称(或数据库名称)") observed_pressure_data: dict[str, list[Any]] | list[dict[str, Any]] | None = ( Field(None, description="观测的压力数据;文件路径不属于公共 API 输入") ) start_time: float = Field(0, ge=0, description="起始时间(小时)") duration: float = Field( 24, gt=0, le=MAX_DURATION_HOURS, description="持续时间(小时)" ) timestep: float = Field(5, gt=0, le=1440, description="时间步长(分钟)") q_sum: float = Field(0.2, ge=0, description="总流量(m3/s)") q_sum_unit: str = Field("m3/s", description="流量单位") pop_size: int = Field( 50, ge=2, le=MAX_POPULATION_SIZE, description="种群大小" ) max_gen: int = Field(100, ge=1, le=MAX_GENERATIONS, description="最大代数") n_workers: int = Field( DEFAULT_N_WORKERS, ge=1, le=DEFAULT_N_WORKERS, description="工作进程数", ) output_flow_unit: str = Field("m3/s", description="输出流量单位") dma_count: int | None = Field(None, ge=1, description="DMA区域数量") scada_start: datetime | None = Field(None, description="SCADA数据起始时间") scada_end: datetime | None = Field(None, description="SCADA数据结束时间") sensor_nodes: list[str] | None = Field(None, description="传感器节点列表") scheme_name: str | None = Field(None, description="方案名称") @router.post( "/leakage-identifications", summary="执行漏损识别", description="基于压力观测数据和遗传算法识别管网中的漏损位置和大小" ) async def identify_leakage( data: LeakageIdentifyRequest = Body(..., description="漏损识别请求数据"), username: str = Depends(get_current_keycloak_username), ) -> dict[str, Any]: """ 执行漏损识别分析。 使用遗传算法对比模型计算和实测压力数据, 识别管网中的漏损节点和漏水量。 Args: data: 包含管网名称(或数据库名称)、压力数据及优化参数的请求体 username: 当前认证用户名 Returns: 包含识别结果的字典 Raises: HTTPException: 当处理过程中发生错误时 """ try: return await run_in_threadpool( run_leakage_identification, **data.model_dump(), username=username, ) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc))