temperature.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import glob
  2. import os
  3. from pathlib import Path
  4. import pandas as pd
  5. from fastapi import APIRouter, HTTPException
  6. from fastapi.responses import JSONResponse
  7. from app.logger import logger
  8. from app.models.AutoDiagModel import AutoDiagInput
  9. from app.models.TemperatureInput import TemperatureInput
  10. from app.models.TemperatureThresholdInput import TemperatureThresholdInput
  11. from app.models.TemperatureDataQueryInput import TemperatureDataQueryInput
  12. from app.services.Auto_diag import Auto_diag
  13. from app.services.MSET_Temp import MSET_Temp
  14. router = APIRouter()
  15. # 全局:{ windCode: { turbine: { channel: model, … }, … }, … }
  16. MODEL_STORE: dict[str, dict[str, dict[str, MSET_Temp]]] = {}
  17. # 英文→中文映射
  18. cn_map = {
  19. 'main_bearing_temperature': '主轴承温度',
  20. 'gearbox_oil_temperature': '齿轮箱油温',
  21. 'generatordrive_end_bearing_temperature': '发电机驱动端轴承温度',
  22. 'generatornon_drive_end_bearing_temperature': '发电机非驱动端轴承温度'
  23. }
  24. @router.on_event("startup")
  25. def load_all_models():
  26. for f in glob.glob("./app/temperature-models/*/*/*.pkl"):
  27. path = Path(f)
  28. ch = os.path.splitext(path.name)[0]
  29. MODEL_STORE.setdefault(path.parent.parent.name, {}).setdefault(path.parent.name, {})[ch] = MSET_Temp.load_model(f)
  30. logger.info(f"模型加载完成: { {k: list(v.keys()) for k, v in MODEL_STORE.items()} }")
  31. @router.post("/temperature/threshold")
  32. async def route_threshold(inp: TemperatureThresholdInput):
  33. """
  34. 输入:
  35. {
  36. "windCode": "WOF091200030",
  37. "windTurbineNumberList": ["WOG01355"],
  38. "startTime": "2024-06-01 00:00",
  39. "endTime": "2024-06-05 01:00",
  40. "pageNo": 1,
  41. "pageSize": 10
  42. }
  43. 输出:
  44. {
  45. "data": {
  46. "type": "temperature_threshold",
  47. "records": [
  48. {
  49. "wind_turbine_number": "WOG01355",
  50. "time_stamp": "2025-06-01 00:05:00",
  51. "temp_channel": "主轴承温度",
  52. "SPRT_score": 0.12,
  53. "status": "正常"
  54. },
  55. ...
  56. ],
  57. "totalSize": 42
  58. },
  59. "code": 200,
  60. "message": "success"
  61. }
  62. """
  63. # 1) 校验模型是否存在
  64. if inp.windCode not in MODEL_STORE:
  65. raise HTTPException(404, f"无模型:{inp.windCode}")
  66. # 2) 为每台待分析风机,拉数据并推理
  67. records = []
  68. for turbine in inp.windTurbineNumberList:
  69. if turbine not in MODEL_STORE[inp.windCode]:
  70. continue
  71. analyzer = MSET_Temp(inp.windCode, [turbine], inp.startTime, inp.endTime)
  72. df = analyzer._get_data_by_filter()
  73. if df.empty:
  74. continue
  75. df['time_stamp'] = pd.to_datetime(df['time_stamp'])
  76. for eng, cn in cn_map.items():
  77. if eng not in df.columns:
  78. continue
  79. sub = df[['time_stamp', eng]].dropna()
  80. arr = sub[eng].values.reshape(-1, 1)
  81. ts = sub['time_stamp'].dt.strftime("%Y-%m-%d %H:%M:%S").tolist()
  82. model = MODEL_STORE[inp.windCode][turbine].get(eng)
  83. if not model:
  84. continue
  85. flags = model.predict_SPRT(arr, decisionGroup=1)
  86. for i, sc in enumerate(flags):
  87. records.append({
  88. "wind_turbine_number": turbine,
  89. "time_stamp": ts[i],
  90. "temp_channel": cn,
  91. "SPRT_score": sc,
  92. "status": "危险" if sc > 0.99 else "正常"
  93. })
  94. # 分页返回
  95. total = len(records)
  96. start = (inp.pageNo - 1) * inp.pageSize
  97. end = start + inp.pageSize
  98. return {
  99. "data": {
  100. "type": "temperature_threshold",
  101. "records": records[start:end],
  102. "totalSize": total
  103. },
  104. "code": 200,
  105. "message": "success"
  106. }
  107. @router.post("/SPRT/trend")
  108. async def route_trend(inp: TemperatureInput):
  109. """
  110. 输入:
  111. {
  112. "windCode": "WOF091200030",
  113. "windTurbineNumberList": ["WOG01355"],
  114. "startTime": "2024-06-01 00:00",
  115. "endTime": "2024-06-05 01:00"
  116. }
  117. 输出:
  118. {
  119. "data": {
  120. "type": "SPRT_trend",
  121. "main_bearing": {"timestamps": [...], "values": [...]},
  122. "gearbox_oil": {"timestamps": [...], "values": [...]},
  123. "generator_drive_end": {"timestamps": [...], "values": [...]},
  124. "generator_nondrive_end": {"timestamps": [...], "values": [...]}
  125. },
  126. "code": 200,
  127. "message": "success"
  128. }
  129. """
  130. if inp.windCode not in MODEL_STORE:
  131. raise HTTPException(404, f"无模型:{inp.windCode}")
  132. turbines_out = []
  133. for turbine in inp.windTurbineNumberList:
  134. if turbine not in MODEL_STORE[inp.windCode]:
  135. continue
  136. analyzer = MSET_Temp(inp.windCode, [turbine], inp.startTime, inp.endTime)
  137. df = analyzer._get_data_by_filter()
  138. if df.empty:
  139. continue
  140. df['time_stamp'] = pd.to_datetime(df['time_stamp'])
  141. ch_data = {}
  142. for eng, key in {
  143. 'main_bearing_temperature': 'main_bearing',
  144. 'gearbox_oil_temperature': 'gearbox_oil',
  145. 'generatordrive_end_bearing_temperature': 'generator_drive_end',
  146. 'generatornon_drive_end_bearing_temperature': 'generator_nondrive_end'
  147. }.items():
  148. if eng not in df.columns:
  149. ch_data[key] = {"timestamps": [], "values": []}
  150. continue
  151. sub = df[['time_stamp', eng]].dropna()
  152. ts = sub['time_stamp'].dt.strftime("%Y-%m-%d %H:%M:%S").tolist()
  153. arr = sub[eng].values.reshape(-1, 1)
  154. model = MODEL_STORE[inp.windCode][turbine].get(eng)
  155. vals = model.predict_SPRT(arr, decisionGroup=1) if model else []
  156. ch_data[key] = {"timestamps": ts, "values": vals}
  157. # turbines_out.append({
  158. # "wind_turbine_number": turbine,
  159. # **ch_data
  160. # })
  161. return {
  162. "data": {
  163. "type": "SPRT_trend",
  164. **ch_data
  165. },
  166. "code": 200,
  167. "message": "success"
  168. }
  169. if __name__ == "__main__":
  170. import uvicorn
  171. uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
  172. @router.post("/temperature/dataquery")
  173. async def query_data(inp: TemperatureDataQueryInput):
  174. """
  175. 查询指定风机在特定时间点前后各50个时间点的数据
  176. 输入:
  177. {
  178. "windCode": "WOF091200030",
  179. "windTurbineNumber": "WOG01355",
  180. "timestamp": "2024-06-01 00:00:00"
  181. }
  182. 输出:
  183. {
  184. "data": {
  185. "wind_turbine_number": "WOG01355",
  186. "record_count": 101,
  187. "records": [
  188. {"时间戳": "2024-05-31 23:10:00", "主轴承温度": 65.2, ...},
  189. {"时间戳": "2024-05-31 23:15:00", "主轴承温度": 65.5, ...},
  190. ...
  191. {"时间戳": "2024-06-01 00:50:00", "主轴承温度": 66.1, ...}
  192. ]
  193. },
  194. "code": 200,
  195. "message": "success"
  196. }
  197. """
  198. try:
  199. analyzer = MSET_Temp(inp.windCode, [inp.windTurbineNumber], "", "")
  200. result = analyzer.query_surrounding_data(inp.timestamp,minutes_around = 250)
  201. if result['record_count'] == 0:
  202. return JSONResponse(
  203. content={"code": 405, "message": "未找到数据"},
  204. status_code=200
  205. )
  206. return {
  207. "data": {
  208. "wind_turbine_number": inp.windTurbineNumber,
  209. "records": result['records']
  210. },
  211. "code": 200,
  212. "message": "success"
  213. }
  214. except Exception as e:
  215. raise HTTPException(status_code=500, detail=str(e))
  216. @router.post("/autodiag/{autodiagType}")
  217. async def perform_diagnosis(autodiagType: str, input_data: AutoDiagInput):
  218. """
  219. 执行自动诊断分析
  220. 参数:
  221. autodiagType: 诊断类型
  222. input_data: 包含ids, windCode, engine_code的输入数据
  223. 返回:
  224. 诊断结果,包含状态码列表和统计信息
  225. """
  226. autodiag_map = {
  227. "Unbalance": "Unbalance_diag", # 不平衡诊断
  228. "Misalignment": "Misalignment_diag", # 不对中诊断
  229. "Looseness": "Looseness_diag", # 松动诊断
  230. "Bearing": "Bearing_diag", # 轴承诊断
  231. "Gear": "Gear_diag" # 齿轮诊断
  232. }
  233. if autodiagType not in autodiag_map:
  234. raise HTTPException(status_code=400, detail="非可用的诊断类型")
  235. try:
  236. # 初始化诊断类
  237. autodiag = Auto_diag(input_data.ids, input_data.windCode, input_data.engine_code)
  238. # 获取诊断方法
  239. func = getattr(autodiag, autodiag_map[autodiagType])
  240. # 执行诊断
  241. if callable(func):
  242. result = func() # 直接返回格式化后的结果
  243. return JSONResponse(content=result)
  244. except ValueError as e:
  245. # 专门捕获齿轮诊断的错误
  246. if "Can not perform gearbox diagnosis" in str(e):
  247. return JSONResponse(
  248. status_code=200,
  249. content={
  250. "code": 400,
  251. "message": str(e)
  252. }
  253. )
  254. elif "当前采集频率不适合进行诊断分析" in str(e):
  255. return JSONResponse(
  256. status_code=200,
  257. content={
  258. "code": 405,
  259. "message": str(e)
  260. }
  261. )
  262. # 其他ValueError
  263. raise HTTPException(status_code=400, detail=str(e))
  264. except Exception as e:
  265. raise HTTPException(status_code=500, detail=str(e))