api_tempdiag.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. # main.py
  2. import os, glob
  3. import pandas as pd
  4. from fastapi import FastAPI, HTTPException
  5. from fastapi.responses import JSONResponse
  6. from pydantic import BaseModel, model_validator
  7. from typing import List,Dict
  8. from sqlalchemy import create_engine, text
  9. from Temp_Diag import MSET_Temp
  10. app = FastAPI(root_path="/api/diag",title="Temperature Diagnosis API")
  11. # 全局:{ windCode: { turbine: { channel: model, … }, … }, … }
  12. MODEL_STORE: dict[str, dict[str, dict[str, MSET_Temp]]] = {}
  13. # 英文→中文映射
  14. cn_map = {
  15. 'main_bearing_temperature': '主轴承温度',
  16. 'gearbox_oil_temperature': '齿轮箱油温',
  17. 'generatordrive_end_bearing_temperature': '发电机驱动端轴承温度',
  18. 'generatornon_drive_end_bearing_temperature': '发电机非驱动端轴承温度'
  19. }
  20. class TemperatureInput(BaseModel):
  21. windCode: str
  22. windTurbineNumberList: List[str]
  23. startTime: str
  24. endTime: str
  25. @model_validator(mode='before')
  26. def ensure_list(cls, v):
  27. raw = v.get('windTurbineNumberList')
  28. if isinstance(raw, str):
  29. v['windTurbineNumberList'] = [raw]
  30. return v
  31. class TemperatureThresholdInput(TemperatureInput):
  32. pageNo: int
  33. pageSize: int
  34. class TemperatureDataQueryInput(BaseModel):
  35. windCode: str
  36. windTurbineNumber: str
  37. timestamp: str
  38. @app.on_event("startup")
  39. def load_all_models():
  40. for f in glob.glob("models/*/*/*.pkl"):
  41. _, wc, turbine, fname = f.split(os.sep)
  42. ch = os.path.splitext(fname)[0]
  43. MODEL_STORE.setdefault(wc, {}).setdefault(turbine, {})[ch] = MSET_Temp.load_model(f)
  44. print("模型加载完成:", {k: list(v.keys()) for k,v in MODEL_STORE.items()})
  45. @app.post("/temperature/threshold")
  46. async def route_threshold(inp: TemperatureThresholdInput):
  47. """
  48. 输入:
  49. {
  50. "windCode": "WOF091200030",
  51. "windTurbineNumberList": ["WOG01355"],
  52. "startTime": "2024-06-01 00:00",
  53. "endTime": "2024-06-05 01:00",
  54. "pageNo": 1,
  55. "pageSize": 10
  56. }
  57. 输出:
  58. {
  59. "data": {
  60. "type": "temperature_threshold",
  61. "records": [
  62. {
  63. "wind_turbine_number": "WOG01355",
  64. "time_stamp": "2025-06-01 00:05:00",
  65. "temp_channel": "主轴承温度",
  66. "SPRT_score": 0.12,
  67. "status": "正常"
  68. },
  69. ...
  70. ],
  71. "totalSize": 42
  72. },
  73. "code": 200,
  74. "message": "success"
  75. }
  76. """
  77. # 1) 校验模型是否存在
  78. if inp.windCode not in MODEL_STORE:
  79. raise HTTPException(404, f"无模型:{inp.windCode}")
  80. # 2) 为每台待分析风机,拉数据并推理
  81. records = []
  82. for turbine in inp.windTurbineNumberList:
  83. if turbine not in MODEL_STORE[inp.windCode]:
  84. continue
  85. analyzer = MSET_Temp(inp.windCode, [turbine], inp.startTime, inp.endTime)
  86. df = analyzer._get_data_by_filter()
  87. if df.empty:
  88. continue
  89. df['time_stamp'] = pd.to_datetime(df['time_stamp'])
  90. for eng, cn in cn_map.items():
  91. if eng not in df.columns:
  92. continue
  93. sub = df[['time_stamp', eng]].dropna()
  94. arr = sub[eng].values.reshape(-1,1)
  95. ts = sub['time_stamp'].dt.strftime("%Y-%m-%d %H:%M:%S").tolist()
  96. model = MODEL_STORE[inp.windCode][turbine].get(eng)
  97. if not model:
  98. continue
  99. flags = model.predict_SPRT(arr, decisionGroup=1)
  100. for i, sc in enumerate(flags):
  101. records.append({
  102. "wind_turbine_number": turbine,
  103. "time_stamp": ts[i],
  104. "temp_channel": cn,
  105. "SPRT_score": sc,
  106. "status": "危险" if sc>0.99 else "正常"
  107. })
  108. # 分页返回
  109. total = len(records)
  110. start = (inp.pageNo-1)*inp.pageSize
  111. end = start+inp.pageSize
  112. return {
  113. "data": {
  114. "type": "temperature_threshold",
  115. "records": records[start:end],
  116. "totalSize": total
  117. },
  118. "code": 200,
  119. "message": "success"
  120. }
  121. @app.post("/SPRT/trend")
  122. async def route_trend(inp: TemperatureInput):
  123. """
  124. 输入:
  125. {
  126. "windCode": "WOF091200030",
  127. "windTurbineNumberList": ["WOG01355"],
  128. "startTime": "2024-06-01 00:00",
  129. "endTime": "2024-06-05 01:00"
  130. }
  131. 输出:
  132. {
  133. "data": {
  134. "type": "SPRT_trend",
  135. "main_bearing": {"timestamps": [...], "values": [...]},
  136. "gearbox_oil": {"timestamps": [...], "values": [...]},
  137. "generator_drive_end": {"timestamps": [...], "values": [...]},
  138. "generator_nondrive_end": {"timestamps": [...], "values": [...]}
  139. },
  140. "code": 200,
  141. "message": "success"
  142. }
  143. """
  144. if inp.windCode not in MODEL_STORE:
  145. raise HTTPException(404, f"无模型:{inp.windCode}")
  146. turbines_out = []
  147. for turbine in inp.windTurbineNumberList:
  148. if turbine not in MODEL_STORE[inp.windCode]:
  149. continue
  150. analyzer = MSET_Temp(inp.windCode, [turbine], inp.startTime, inp.endTime)
  151. df = analyzer._get_data_by_filter()
  152. if df.empty:
  153. continue
  154. df['time_stamp'] = pd.to_datetime(df['time_stamp'])
  155. ch_data = {}
  156. for eng, key in {
  157. 'main_bearing_temperature':'main_bearing',
  158. 'gearbox_oil_temperature':'gearbox_oil',
  159. 'generatordrive_end_bearing_temperature':'generator_drive_end',
  160. 'generatornon_drive_end_bearing_temperature':'generator_nondrive_end'
  161. }.items():
  162. if eng not in df.columns:
  163. ch_data[key] = {"timestamps": [], "values": []}
  164. continue
  165. sub = df[['time_stamp', eng]].dropna()
  166. ts = sub['time_stamp'].dt.strftime("%Y-%m-%d %H:%M:%S").tolist()
  167. arr = sub[eng].values.reshape(-1,1)
  168. model = MODEL_STORE[inp.windCode][turbine].get(eng)
  169. vals = model.predict_SPRT(arr, decisionGroup=1) if model else []
  170. ch_data[key] = {"timestamps": ts, "values": vals}
  171. # turbines_out.append({
  172. # "wind_turbine_number": turbine,
  173. # **ch_data
  174. # })
  175. return {
  176. "data": {
  177. "type": "SPRT_trend",
  178. **ch_data
  179. },
  180. "code": 200,
  181. "message": "success"
  182. }
  183. @app.post("/temperature/dataquery")
  184. async def query_data(inp: TemperatureDataQueryInput):
  185. """
  186. 查询指定风机在特定时间点前后各50个时间点的数据
  187. 输入:
  188. {
  189. "windCode": "WOF091200030",
  190. "windTurbineNumber": "WOG01355",
  191. "timestamp": "2024-06-01 00:00:00"
  192. }
  193. 输出:
  194. {
  195. "data": {
  196. "wind_turbine_number": "WOG01355",
  197. "record_count": 101,
  198. "records": [
  199. {"时间戳": "2024-05-31 23:10:00", "主轴承温度": 65.2, ...},
  200. {"时间戳": "2024-05-31 23:15:00", "主轴承温度": 65.5, ...},
  201. ...
  202. {"时间戳": "2024-06-01 00:50:00", "主轴承温度": 66.1, ...}
  203. ]
  204. },
  205. "code": 200,
  206. "message": "success"
  207. }
  208. """
  209. try:
  210. analyzer = MSET_Temp(inp.windCode, [inp.windTurbineNumber], "", "")
  211. result = analyzer.query_surrounding_data(inp.timestamp,minutes_around = 250)
  212. if result['record_count'] == 0:
  213. return JSONResponse(
  214. content={"code": 405, "message": "未找到数据"},
  215. status_code=200
  216. )
  217. return {
  218. "data": {
  219. "wind_turbine_number": inp.windTurbineNumber,
  220. "records": result['records']
  221. },
  222. "code": 200,
  223. "message": "success"
  224. }
  225. except Exception as e:
  226. raise HTTPException(status_code=500, detail=str(e))
  227. if __name__ == "__main__":
  228. import uvicorn
  229. uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)