# main.py import os, glob import pandas as pd from fastapi import FastAPI, HTTPException from fastapi.responses import JSONResponse from pydantic import BaseModel, model_validator from typing import List,Dict from sqlalchemy import create_engine, text from Temp_Diag import MSET_Temp app = FastAPI(root_path="/api/diag",title="Temperature Diagnosis API") # 全局:{ windCode: { turbine: { channel: model, … }, … }, … } MODEL_STORE: dict[str, dict[str, dict[str, MSET_Temp]]] = {} # 英文→中文映射 cn_map = { 'main_bearing_temperature': '主轴承温度', 'gearbox_oil_temperature': '齿轮箱油温', 'generatordrive_end_bearing_temperature': '发电机驱动端轴承温度', 'generatornon_drive_end_bearing_temperature': '发电机非驱动端轴承温度' } class TemperatureInput(BaseModel): windCode: str windTurbineNumberList: List[str] startTime: str endTime: str @model_validator(mode='before') def ensure_list(cls, v): raw = v.get('windTurbineNumberList') if isinstance(raw, str): v['windTurbineNumberList'] = [raw] return v class TemperatureThresholdInput(TemperatureInput): pageNo: int pageSize: int class TemperatureDataQueryInput(BaseModel): windCode: str windTurbineNumber: str timestamp: str @app.on_event("startup") def load_all_models(): for f in glob.glob("models/*/*/*.pkl"): _, wc, turbine, fname = f.split(os.sep) ch = os.path.splitext(fname)[0] MODEL_STORE.setdefault(wc, {}).setdefault(turbine, {})[ch] = MSET_Temp.load_model(f) print("模型加载完成:", {k: list(v.keys()) for k,v in MODEL_STORE.items()}) @app.post("/temperature/threshold") async def route_threshold(inp: TemperatureThresholdInput): """ 输入: { "windCode": "WOF091200030", "windTurbineNumberList": ["WOG01355"], "startTime": "2024-06-01 00:00", "endTime": "2024-06-05 01:00", "pageNo": 1, "pageSize": 10 } 输出: { "data": { "type": "temperature_threshold", "records": [ { "wind_turbine_number": "WOG01355", "time_stamp": "2025-06-01 00:05:00", "temp_channel": "主轴承温度", "SPRT_score": 0.12, "status": "正常" }, ... ], "totalSize": 42 }, "code": 200, "message": "success" } """ # 1) 校验模型是否存在 if inp.windCode not in MODEL_STORE: raise HTTPException(404, f"无模型:{inp.windCode}") # 2) 为每台待分析风机,拉数据并推理 records = [] for turbine in inp.windTurbineNumberList: if turbine not in MODEL_STORE[inp.windCode]: continue analyzer = MSET_Temp(inp.windCode, [turbine], inp.startTime, inp.endTime) df = analyzer._get_data_by_filter() if df.empty: continue df['time_stamp'] = pd.to_datetime(df['time_stamp']) for eng, cn in cn_map.items(): if eng not in df.columns: continue sub = df[['time_stamp', eng]].dropna() arr = sub[eng].values.reshape(-1,1) ts = sub['time_stamp'].dt.strftime("%Y-%m-%d %H:%M:%S").tolist() model = MODEL_STORE[inp.windCode][turbine].get(eng) if not model: continue flags = model.predict_SPRT(arr, decisionGroup=1) for i, sc in enumerate(flags): records.append({ "wind_turbine_number": turbine, "time_stamp": ts[i], "temp_channel": cn, "SPRT_score": sc, "status": "危险" if sc>0.99 else "正常" }) # 分页返回 total = len(records) start = (inp.pageNo-1)*inp.pageSize end = start+inp.pageSize return { "data": { "type": "temperature_threshold", "records": records[start:end], "totalSize": total }, "code": 200, "message": "success" } @app.post("/SPRT/trend") async def route_trend(inp: TemperatureInput): """ 输入: { "windCode": "WOF091200030", "windTurbineNumberList": ["WOG01355"], "startTime": "2024-06-01 00:00", "endTime": "2024-06-05 01:00" } 输出: { "data": { "type": "SPRT_trend", "main_bearing": {"timestamps": [...], "values": [...]}, "gearbox_oil": {"timestamps": [...], "values": [...]}, "generator_drive_end": {"timestamps": [...], "values": [...]}, "generator_nondrive_end": {"timestamps": [...], "values": [...]} }, "code": 200, "message": "success" } """ if inp.windCode not in MODEL_STORE: raise HTTPException(404, f"无模型:{inp.windCode}") turbines_out = [] for turbine in inp.windTurbineNumberList: if turbine not in MODEL_STORE[inp.windCode]: continue analyzer = MSET_Temp(inp.windCode, [turbine], inp.startTime, inp.endTime) df = analyzer._get_data_by_filter() if df.empty: continue df['time_stamp'] = pd.to_datetime(df['time_stamp']) ch_data = {} for eng, key in { 'main_bearing_temperature':'main_bearing', 'gearbox_oil_temperature':'gearbox_oil', 'generatordrive_end_bearing_temperature':'generator_drive_end', 'generatornon_drive_end_bearing_temperature':'generator_nondrive_end' }.items(): if eng not in df.columns: ch_data[key] = {"timestamps": [], "values": []} continue sub = df[['time_stamp', eng]].dropna() ts = sub['time_stamp'].dt.strftime("%Y-%m-%d %H:%M:%S").tolist() arr = sub[eng].values.reshape(-1,1) model = MODEL_STORE[inp.windCode][turbine].get(eng) vals = model.predict_SPRT(arr, decisionGroup=1) if model else [] ch_data[key] = {"timestamps": ts, "values": vals} # turbines_out.append({ # "wind_turbine_number": turbine, # **ch_data # }) return { "data": { "type": "SPRT_trend", **ch_data }, "code": 200, "message": "success" } @app.post("/temperature/dataquery") async def query_data(inp: TemperatureDataQueryInput): """ 查询指定风机在特定时间点前后各50个时间点的数据 输入: { "windCode": "WOF091200030", "windTurbineNumber": "WOG01355", "timestamp": "2024-06-01 00:00:00" } 输出: { "data": { "wind_turbine_number": "WOG01355", "record_count": 101, "records": [ {"时间戳": "2024-05-31 23:10:00", "主轴承温度": 65.2, ...}, {"时间戳": "2024-05-31 23:15:00", "主轴承温度": 65.5, ...}, ... {"时间戳": "2024-06-01 00:50:00", "主轴承温度": 66.1, ...} ] }, "code": 200, "message": "success" } """ try: analyzer = MSET_Temp(inp.windCode, [inp.windTurbineNumber], "", "") result = analyzer.query_surrounding_data(inp.timestamp,minutes_around = 250) if result['record_count'] == 0: return JSONResponse( content={"code": 405, "message": "未找到数据"}, status_code=200 ) return { "data": { "wind_turbine_number": inp.windTurbineNumber, "records": result['records'] }, "code": 200, "message": "success" } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)