Selaa lähdekoodia

最新版代码

wangjiaojiao 4 kuukautta sitten
commit
96ef1594ea
100 muutettua tiedostoa jossa 541 lisäystä ja 0 poistoa
  1. 2 0
      .gitignore
  2. 282 0
      Temp_Diag.py
  3. 257 0
      api_tempdiag.py
  4. BIN
      models/WOF046400029/WOG01312/gearbox_oil_temperature.pkl
  5. BIN
      models/WOF046400029/WOG01312/generatordrive_end_bearing_temperature.pkl
  6. BIN
      models/WOF046400029/WOG01312/generatornon_drive_end_bearing_temperature.pkl
  7. BIN
      models/WOF046400029/WOG01313/gearbox_oil_temperature.pkl
  8. BIN
      models/WOF046400029/WOG01313/generatordrive_end_bearing_temperature.pkl
  9. BIN
      models/WOF046400029/WOG01313/generatornon_drive_end_bearing_temperature.pkl
  10. BIN
      models/WOF046400029/WOG01314/gearbox_oil_temperature.pkl
  11. BIN
      models/WOF046400029/WOG01314/generatordrive_end_bearing_temperature.pkl
  12. BIN
      models/WOF046400029/WOG01314/generatornon_drive_end_bearing_temperature.pkl
  13. BIN
      models/WOF046400029/WOG01315/gearbox_oil_temperature.pkl
  14. BIN
      models/WOF046400029/WOG01315/generatordrive_end_bearing_temperature.pkl
  15. BIN
      models/WOF046400029/WOG01315/generatornon_drive_end_bearing_temperature.pkl
  16. BIN
      models/WOF046400029/WOG01316/gearbox_oil_temperature.pkl
  17. BIN
      models/WOF046400029/WOG01316/generatordrive_end_bearing_temperature.pkl
  18. BIN
      models/WOF046400029/WOG01316/generatornon_drive_end_bearing_temperature.pkl
  19. BIN
      models/WOF046400029/WOG01317/gearbox_oil_temperature.pkl
  20. BIN
      models/WOF046400029/WOG01317/generatordrive_end_bearing_temperature.pkl
  21. BIN
      models/WOF046400029/WOG01317/generatornon_drive_end_bearing_temperature.pkl
  22. BIN
      models/WOF046400029/WOG01318/gearbox_oil_temperature.pkl
  23. BIN
      models/WOF046400029/WOG01318/generatordrive_end_bearing_temperature.pkl
  24. BIN
      models/WOF046400029/WOG01318/generatornon_drive_end_bearing_temperature.pkl
  25. BIN
      models/WOF046400029/WOG01319/gearbox_oil_temperature.pkl
  26. BIN
      models/WOF046400029/WOG01319/generatordrive_end_bearing_temperature.pkl
  27. BIN
      models/WOF046400029/WOG01319/generatornon_drive_end_bearing_temperature.pkl
  28. BIN
      models/WOF046400029/WOG01320/gearbox_oil_temperature.pkl
  29. BIN
      models/WOF046400029/WOG01320/generatordrive_end_bearing_temperature.pkl
  30. BIN
      models/WOF046400029/WOG01320/generatornon_drive_end_bearing_temperature.pkl
  31. BIN
      models/WOF046400029/WOG01321/gearbox_oil_temperature.pkl
  32. BIN
      models/WOF046400029/WOG01321/generatordrive_end_bearing_temperature.pkl
  33. BIN
      models/WOF046400029/WOG01321/generatornon_drive_end_bearing_temperature.pkl
  34. BIN
      models/WOF046400029/WOG01322/gearbox_oil_temperature.pkl
  35. BIN
      models/WOF046400029/WOG01322/generatordrive_end_bearing_temperature.pkl
  36. BIN
      models/WOF046400029/WOG01322/generatornon_drive_end_bearing_temperature.pkl
  37. BIN
      models/WOF046400029/WOG01323/gearbox_oil_temperature.pkl
  38. BIN
      models/WOF046400029/WOG01323/generatordrive_end_bearing_temperature.pkl
  39. BIN
      models/WOF046400029/WOG01323/generatornon_drive_end_bearing_temperature.pkl
  40. BIN
      models/WOF046400029/WOG01324/gearbox_oil_temperature.pkl
  41. BIN
      models/WOF046400029/WOG01324/generatordrive_end_bearing_temperature.pkl
  42. BIN
      models/WOF046400029/WOG01324/generatornon_drive_end_bearing_temperature.pkl
  43. BIN
      models/WOF046400029/WOG01325/gearbox_oil_temperature.pkl
  44. BIN
      models/WOF046400029/WOG01325/generatordrive_end_bearing_temperature.pkl
  45. BIN
      models/WOF046400029/WOG01325/generatornon_drive_end_bearing_temperature.pkl
  46. BIN
      models/WOF046400029/WOG01326/gearbox_oil_temperature.pkl
  47. BIN
      models/WOF046400029/WOG01326/generatordrive_end_bearing_temperature.pkl
  48. BIN
      models/WOF046400029/WOG01326/generatornon_drive_end_bearing_temperature.pkl
  49. BIN
      models/WOF046400029/WOG01327/gearbox_oil_temperature.pkl
  50. BIN
      models/WOF046400029/WOG01327/generatordrive_end_bearing_temperature.pkl
  51. BIN
      models/WOF046400029/WOG01327/generatornon_drive_end_bearing_temperature.pkl
  52. BIN
      models/WOF046400029/WOG01328/gearbox_oil_temperature.pkl
  53. BIN
      models/WOF046400029/WOG01328/generatordrive_end_bearing_temperature.pkl
  54. BIN
      models/WOF046400029/WOG01328/generatornon_drive_end_bearing_temperature.pkl
  55. BIN
      models/WOF046400029/WOG01329/gearbox_oil_temperature.pkl
  56. BIN
      models/WOF046400029/WOG01329/generatordrive_end_bearing_temperature.pkl
  57. BIN
      models/WOF046400029/WOG01329/generatornon_drive_end_bearing_temperature.pkl
  58. BIN
      models/WOF046400029/WOG01330/gearbox_oil_temperature.pkl
  59. BIN
      models/WOF046400029/WOG01330/generatordrive_end_bearing_temperature.pkl
  60. BIN
      models/WOF046400029/WOG01330/generatornon_drive_end_bearing_temperature.pkl
  61. BIN
      models/WOF046400029/WOG01331/gearbox_oil_temperature.pkl
  62. BIN
      models/WOF046400029/WOG01331/generatordrive_end_bearing_temperature.pkl
  63. BIN
      models/WOF046400029/WOG01331/generatornon_drive_end_bearing_temperature.pkl
  64. BIN
      models/WOF046400029/WOG01332/gearbox_oil_temperature.pkl
  65. BIN
      models/WOF046400029/WOG01332/generatordrive_end_bearing_temperature.pkl
  66. BIN
      models/WOF046400029/WOG01332/generatornon_drive_end_bearing_temperature.pkl
  67. BIN
      models/WOF046400029/WOG01333/gearbox_oil_temperature.pkl
  68. BIN
      models/WOF046400029/WOG01333/generatordrive_end_bearing_temperature.pkl
  69. BIN
      models/WOF046400029/WOG01333/generatornon_drive_end_bearing_temperature.pkl
  70. BIN
      models/WOF046400029/WOG01334/gearbox_oil_temperature.pkl
  71. BIN
      models/WOF046400029/WOG01334/generatordrive_end_bearing_temperature.pkl
  72. BIN
      models/WOF046400029/WOG01334/generatornon_drive_end_bearing_temperature.pkl
  73. BIN
      models/WOF046400029/WOG01335/gearbox_oil_temperature.pkl
  74. BIN
      models/WOF046400029/WOG01335/generatordrive_end_bearing_temperature.pkl
  75. BIN
      models/WOF046400029/WOG01335/generatornon_drive_end_bearing_temperature.pkl
  76. BIN
      models/WOF046400029/WOG01336/gearbox_oil_temperature.pkl
  77. BIN
      models/WOF046400029/WOG01336/generatordrive_end_bearing_temperature.pkl
  78. BIN
      models/WOF046400029/WOG01336/generatornon_drive_end_bearing_temperature.pkl
  79. BIN
      models/WOF046400029/WOG01337/gearbox_oil_temperature.pkl
  80. BIN
      models/WOF046400029/WOG01337/generatordrive_end_bearing_temperature.pkl
  81. BIN
      models/WOF046400029/WOG01337/generatornon_drive_end_bearing_temperature.pkl
  82. BIN
      models/WOF046400029/WOG01338/gearbox_oil_temperature.pkl
  83. BIN
      models/WOF046400029/WOG01338/generatordrive_end_bearing_temperature.pkl
  84. BIN
      models/WOF046400029/WOG01338/generatornon_drive_end_bearing_temperature.pkl
  85. BIN
      models/WOF046400029/WOG01339/gearbox_oil_temperature.pkl
  86. BIN
      models/WOF046400029/WOG01339/generatordrive_end_bearing_temperature.pkl
  87. BIN
      models/WOF046400029/WOG01339/generatornon_drive_end_bearing_temperature.pkl
  88. BIN
      models/WOF046400029/WOG01340/gearbox_oil_temperature.pkl
  89. BIN
      models/WOF046400029/WOG01340/generatordrive_end_bearing_temperature.pkl
  90. BIN
      models/WOF046400029/WOG01340/generatornon_drive_end_bearing_temperature.pkl
  91. BIN
      models/WOF046400029/WOG01341/gearbox_oil_temperature.pkl
  92. BIN
      models/WOF046400029/WOG01341/generatordrive_end_bearing_temperature.pkl
  93. BIN
      models/WOF046400029/WOG01341/generatornon_drive_end_bearing_temperature.pkl
  94. BIN
      models/WOF046400029/WOG01342/gearbox_oil_temperature.pkl
  95. BIN
      models/WOF046400029/WOG01342/generatordrive_end_bearing_temperature.pkl
  96. BIN
      models/WOF046400029/WOG01342/generatornon_drive_end_bearing_temperature.pkl
  97. BIN
      models/WOF046400029/WOG01343/gearbox_oil_temperature.pkl
  98. BIN
      models/WOF046400029/WOG01343/generatordrive_end_bearing_temperature.pkl
  99. BIN
      models/WOF046400029/WOG01343/generatornon_drive_end_bearing_temperature.pkl
  100. BIN
      models/WOF046400029/WOG01344/gearbox_oil_temperature.pkl

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+__pycache__/
+*.pyc

+ 282 - 0
Temp_Diag.py

@@ -0,0 +1,282 @@
+import numpy as np
+import pandas as pd
+from sklearn.neighbors import BallTree
+from sqlalchemy import create_engine, text
+import math, joblib, os
+from fastapi.responses import JSONResponse
+from typing import Dict
+class MSET_Temp:
+    """
+    MSET + SPRT 温度分析类:
+    - 离线训练:genDLMatrix → save_model
+    - 在线推理:load_model → predict_SPRT
+    """
+
+    def __init__(self,
+                 windCode: str,
+                 windTurbineNumberList: list[str],
+                 startTime: str,
+                 endTime: str):
+        self.windCode = windCode.strip()
+        self.windTurbineNumberList = windTurbineNumberList or []
+        self.startTime = startTime
+        self.endTime   = endTime
+
+        # 离线训练/加载后赋值
+        self.matrixD = None
+        self.healthyResidual = None
+        self.normalDataBallTree = None
+
+        # SPRT 参数(离线训练时设置)
+        self.feature_weight: np.ndarray | None = None
+        self.alpha: float = 0.1
+        self.beta:  float = 0.1
+
+    def _get_data_by_filter(self) -> pd.DataFrame:
+        """
+        在线推理专用:根据 self.windTurbineNumberList & 时间拉数据;
+        如果列表为空,则拉全场数据。
+        """
+        table = f"{self.windCode}_minute"
+        engine = create_engine(
+            #"mysql+pymysql://root:admin123456@106.120.102.238:10336/energy_data_prod"
+             "mysql+pymysql://root:admin123456@192.168.50.235:30306/energy_data_prod"
+        )
+        if self.windTurbineNumberList:
+            turbines = ",".join(f"'{t}'" for t in self.windTurbineNumberList)
+            cond = f"wind_turbine_number IN ({turbines}) AND time_stamp BETWEEN :start AND :end"
+        else:
+            cond = "time_stamp BETWEEN :start AND :end"
+
+        sql = text(f"""
+            SELECT *
+            FROM {table}
+            WHERE {cond}
+            ORDER BY time_stamp ASC
+        """)
+        return pd.read_sql(sql, engine, params={"start": self.startTime, "end": self.endTime})
+
+    def calcSimilarity(self, x: np.ndarray, y: np.ndarray, m: str = 'euc') -> float:
+        if len(x) != len(y):
+            return 0.0
+        if m == 'cbd':
+            return float(np.mean([1.0/(1.0+abs(p-q)) for p,q in zip(x,y)]))
+        diffsq = np.sum((x-y)**2)
+        return float(1.0/(1.0+math.sqrt(diffsq)))
+
+    def genDLMatrix(self, trainDataset: np.ndarray,
+                    dataSize4D=100, dataSize4L=50) -> int:
+        """
+        离线训练:构造 matrixD/matrixL/healthyResidual/BallTree
+        """
+        m, n = trainDataset.shape
+        if m < dataSize4D + dataSize4L:
+            return -1
+
+        # Step1:每维最小/最大入 D
+        D_idx, D = [], []
+        for i in range(n):
+            col = trainDataset[:, i]
+            for idx in (np.argmin(col), np.argmax(col)):
+                D.append(trainDataset[idx].tolist())
+                D_idx.append(idx)
+        # Step2:挑样本至 dataSize4D
+        while len(D_idx) < dataSize4D:
+            free = list(set(range(m)) - set(D_idx))
+            scores = [(np.mean([1-self.calcSimilarity(trainDataset[i], d) for d in D]), i)
+                      for i in free]
+            _, pick = max(scores)
+            D.append(trainDataset[pick].tolist())
+            D_idx.append(pick)
+        self.matrixD = np.array(D)
+
+        # BallTree + healthyResidual
+        self.normalDataBallTree = BallTree(
+            self.matrixD,
+            leaf_size=4,
+            metric=lambda a,b: 1.0 - self.calcSimilarity(a, b)
+        )
+        # healthyResidual
+        ests = []
+        for x in trainDataset:
+            dist, idxs = self.normalDataBallTree.query([x], k=20, return_distance=True)
+            w = 1.0/(dist[0]+1e-1)
+            w /= w.sum()
+            ests.append(np.sum([wi*self.matrixD[j] for wi,j in zip(w,idxs[0])], axis=0))
+        self.healthyResidual = np.array(ests) - trainDataset
+        return 0
+
+    def calcSPRT(self,
+                 newsStates: np.ndarray,
+                 feature_weight: np.ndarray,
+                 alpha: float = 0.1,
+                 beta: float = 0.1,
+                 decisionGroup: int = 5) -> list[float]:
+        """
+        Wald-SPRT 得分
+        """
+        # 新状态残差
+        ests = []
+        for x in newsStates:
+            dist, idxs = self.normalDataBallTree.query([x], k=20, return_distance=True)
+            w = 1.0/(dist[0]+1e-1); w/=w.sum()
+            ests.append(np.sum([wi*self.matrixD[j] for wi,j in zip(w,idxs[0])], axis=0))
+        resN = np.array(ests) - newsStates
+
+        # 加权
+        wN = [np.dot(r, feature_weight) for r in resN]
+        wH = [np.dot(r, feature_weight) for r in self.healthyResidual]
+        mu0, sigma0 = np.mean(wH), np.std(wH)
+        low = math.log(beta/(1-alpha)); high = math.log((1-beta)/alpha)
+
+        flags = []
+        for i in range(len(wN)-decisionGroup+1):
+            seg = wN[i:i+decisionGroup]; mu1=np.mean(seg)
+            si = (sum(seg)*(mu1-mu0)/sigma0**2
+                  - decisionGroup*((mu1**2-mu0**2)/(2*sigma0**2)))
+            si = max(min(si, high), low)
+            flags.append(si/high if si>0 else si/low)
+        return flags
+
+    def predict_SPRT(self,
+                     newsStates: np.ndarray,
+                     decisionGroup: int = 5) -> list[float]:
+        """
+        在线推理:用离线保存的 matrixD/healthyResidual/feature_weight/alpha/beta
+        """
+        return self.calcSPRT(
+            newsStates,
+            self.feature_weight,
+            alpha=self.alpha,
+            beta=self.beta,
+            decisionGroup=decisionGroup
+        )
+
+    def save_model(self, path: str):
+        """
+        Save matrixD, healthyResidual, feature_weight, alpha, beta
+        """
+        os.makedirs(os.path.dirname(path), exist_ok=True)
+        joblib.dump({
+            'matrixD': self.matrixD,
+            'healthyResidual': self.healthyResidual,
+            'feature_weight': self.feature_weight,
+            'alpha': self.alpha,
+            'beta': self.beta,
+        }, path)
+
+    @classmethod
+    def load_model(cls, path: str) -> 'MSET_Temp':
+        """
+        Load + rebuild BallTree
+        """
+        data = joblib.load(path)
+        inst = cls('', [], '', '')
+        inst.matrixD = data['matrixD']
+        inst.healthyResidual = data['healthyResidual']
+        inst.feature_weight = data['feature_weight']
+        inst.alpha = data['alpha']
+        inst.beta  = data['beta']
+        inst.normalDataBallTree = BallTree(
+            inst.matrixD,
+            leaf_size=4,
+            metric=lambda a,b: 1.0 - inst.calcSimilarity(a, b)
+        )
+        return inst
+
+    def query_surrounding_data(self, timestamp: str, minutes_around: int = 250) -> Dict:
+        """
+        查询指定时间点前后50个点的数据
+        参数:
+            timestamp: 中心时间点,格式为 'yyyy-mm-dd HH:MM:SS'
+            minutes_around: 查询前后多少分钟的数据
+        返回:
+            {
+                'record_count': int,
+                'records': List[Dict],
+                'columns_mapping': Dict[str, str]  # 字段中英文映射
+            }
+        """
+        # 中英文映射字典
+        cn_map = {
+            'wind_turbine_name':'风机名称',
+            'time_stamp': '时间',
+            'active_power': '有功功率(kW)',
+            'rotor_speed': '风轮转速(rpm)',
+            'generator_speed':'发电机转速(rpm)',
+            'wind_velocity': '风速(m/s)',
+            'pitch_angle_blade_1':'桨距角1(°)',
+            'pitch_angle_blade_2':'桨距角2(°)',  
+            'pitch_angle_blade_3':'桨距角3(°)',
+            'cabin_position':'机舱位置(°)',   
+            'true_wind_direction':'绝对风向(°)',
+            'yaw_error1':'对风角度(°)',     
+            'set_value_of_active_power':'有功功率设定值(kW)',
+            'gearbox_oil_temperature':'齿轮箱油温(℃)',     
+            'generatordrive_end_bearing_temperature':'发电机驱动端轴承温度(℃)',
+            'generatornon_drive_end_bearing_temperature':'发电机非驱动端轴承温度(℃)',     
+            'cabin_temperature':'机舱内温度(℃)',
+            'twisted_cable_angle':'扭缆角度(°)',     
+            'outside_cabin_temperature':'环境温度(℃)',
+            'main_bearing_temperature':'主轴承轴承温度(℃)',     
+            'main_bearing_temperature_2': '主轴承轴承温度2(℃)',            
+            'gearbox_high_speed_shaft_bearing_temperature':'齿轮箱高速轴轴承温度(℃)',
+            'gearboxmedium_speed_shaftbearing_temperature':'齿轮箱中速轴轴承温度(℃)',     
+            'gearbox_low_speed_shaft_bearing_temperature':'齿轮箱低速轴轴承温度(℃)',
+            'generator_winding1_temperature':'发电机绕组1温度(℃)',     
+            'generator_winding2_temperature':'发电机绕组2温度(℃)',
+            'generator_winding3_temperature':'发电机绕组3温度(℃)',     
+            'grid_a_phase_current':'电网A相电流(A)',     
+            'grid_b_phase_current': '电网B相电流(A)',
+            'grid_c_phase_current': '电网C相电流(A)'
+        }
+
+        table = f"{self.windCode}_minute"
+        engine = create_engine(
+            "mysql+pymysql://root:admin123456@192.168.50.235:30306/energy_data_prod"
+        )
+
+        # 查询数据
+        sql = text(f"""
+            SELECT *
+            FROM {table}
+            WHERE wind_turbine_number IN ({','.join([f"'{t}'" for t in self.windTurbineNumberList])})
+            AND time_stamp BETWEEN 
+                DATE_SUB(:timestamp, INTERVAL :minutes MINUTE) 
+                AND DATE_ADD(:timestamp, INTERVAL :minutes MINUTE)
+            ORDER BY time_stamp ASC
+        """)
+        
+        df = pd.read_sql(sql, engine, params={
+            "timestamp": timestamp,
+            "minutes": minutes_around
+        })
+
+        # 打印查询到的数据条数
+        record_count = len(df)
+        print(f"查询到 {record_count} 条数据")
+
+        if df.empty:
+            return {
+                'record_count': 0,
+                'records': [],
+                'columns_mapping': {}
+            }
+
+        # 删除空列和不需要的列
+        cols_to_drop = ['wind_turbine_number', 'reactive_power','lab', 'year', 'month','day','year_month','front_back_vibration_of_the_cabin','side_to_side_vibration_of_the_cabin',
+                        'actual_torque','given_torque','clockwise_yaw_count','counterclockwise_yaw_count','unusable','power_curve_available','required_gearbox_speed','inverter_speed_master_control',
+                        'wind_turbine_status','wind_turbine_status2','turbulence_intensity'
+                        ]
+        cols_to_drop = [col for col in cols_to_drop if col in df.columns]
+        df = df.drop(columns=cols_to_drop)
+        df = df.dropna(axis=1, how='all')
+
+        # 转换字段名和格式
+        df['time_stamp'] = df['time_stamp'].astype(str)
+        records = df.rename(columns=cn_map).to_dict('records')
+
+        return {
+            'record_count': record_count,
+            'records': records
+        }

+ 257 - 0
api_tempdiag.py

@@ -0,0 +1,257 @@
+# 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)

BIN
models/WOF046400029/WOG01312/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01312/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01312/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01313/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01313/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01313/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01314/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01314/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01314/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01315/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01315/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01315/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01316/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01316/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01316/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01317/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01317/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01317/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01318/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01318/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01318/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01319/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01319/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01319/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01320/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01320/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01320/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01321/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01321/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01321/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01322/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01322/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01322/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01323/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01323/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01323/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01324/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01324/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01324/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01325/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01325/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01325/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01326/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01326/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01326/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01327/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01327/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01327/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01328/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01328/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01328/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01329/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01329/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01329/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01330/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01330/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01330/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01331/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01331/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01331/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01332/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01332/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01332/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01333/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01333/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01333/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01334/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01334/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01334/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01335/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01335/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01335/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01336/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01336/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01336/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01337/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01337/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01337/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01338/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01338/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01338/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01339/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01339/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01339/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01340/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01340/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01340/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01341/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01341/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01341/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01342/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01342/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01342/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01343/gearbox_oil_temperature.pkl


BIN
models/WOF046400029/WOG01343/generatordrive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01343/generatornon_drive_end_bearing_temperature.pkl


BIN
models/WOF046400029/WOG01344/gearbox_oil_temperature.pkl


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä