Browse Source

添加SPRT趋势分析,阈值分析添加分页逻辑

Xmia 1 month ago
parent
commit
0f946621cf
2 changed files with 259 additions and 85 deletions
  1. 152 44
      Temp_Diag.PY
  2. 107 41
      api_tempdiag.py

+ 152 - 44
Temp_Diag.PY

@@ -55,7 +55,7 @@ class MSET_Temp:
         """
         table_name = f"{self.windCode}_minute"
         engine = create_engine(
-          #  "mysql+pymysql://root:admin123456@106.120.102.238:10336/energy_data_prod"
+            # "mysql+pymysql://root:admin123456@106.120.102.238:10336/energy_data_prod"
             "mysql+pymysql://root:admin123456@192.168.50.235:30306/energy_data_prod"
         )
 
@@ -289,74 +289,182 @@ class MSET_Temp:
         df_long['temp_channel'] = df_long['temp_channel'].map(temp_channel_mapping)
 
         return df_long
+    
 
     def get_trend(self) -> dict:
         """
-        趋势分析
-        获取温度趋势:将温度数据按时间返回。
-        返回格式:{
-            "timestamps": [ISO8601 字符串列表],
-            "channels": [
-                {"temp_channel": "main_bearing_temperature", "values": [浮点列表]},
-                {"temp_channel": "gearbox_oil_temperature", "values": [...]},
-                ...
-            ],
-            "unit": "°C"
-        }
+        趋势分析(SPRT_score)
         """
+        # 1) 按风机编号 + 时间范围查询原始数据
         df = self._get_data_by_filter()
-
+        # 如果没有任何数据,则四通道均为空
         if df.empty:
-            return {"timestamps": [], "channels": [], "unit": "°C"}
+            return {
+                "data": {
+                    "main_bearing": {},
+                    "gearbox_oil": {},
+                    "generator_drive_end": {},
+                    "generator_nondrive_end": {}
+                },
+                "code": 200,
+                "message": "success"
+            }
 
-        # 定义所有需要检查的温度列
+        # 2) 筛选存在的温度列
         temp_cols_all = [
             'main_bearing_temperature',
             'gearbox_oil_temperature',
             'generatordrive_end_bearing_temperature',
             'generatornon_drive_end_bearing_temperature'
         ]
-        # 选择实际存在的列
         temp_cols = [c for c in temp_cols_all if c in df.columns]
-        
-        # 如果没有温度数据列,返回空数据
         if not temp_cols:
-            return {"timestamps": [], "channels": [], "unit": "°C"}
+            return {
+                "data": {
+                    "main_bearing": {},
+                    "gearbox_oil": {},
+                    "generator_drive_end": {},
+                    "generator_nondrive_end": {}
+                },
+                "code": 200,
+                "message": "success"
+            }
 
-        # 转数值,并删除 NaN
+        # 3) 转数值 & 删除 NaN
         df[temp_cols] = df[temp_cols].apply(pd.to_numeric, errors='coerce')
         df = df.dropna(subset=temp_cols + ['time_stamp'])
-
-        # 转时间戳为 `YYYY-MM-DD HH:MM:SS` 格式
-        df['time_stamp'] = pd.to_datetime(df['time_stamp']).dt.strftime("%Y-%m-%d %H:%M:%S")
+        if df.empty:
+            return {
+                "data": {
+                    "main_bearing": {},
+                    "gearbox_oil": {},
+                    "generator_drive_end": {},
+                    "generator_nondrive_end": {}
+                },
+                "code": 200,
+                "message": "success"
+            }
+
+        # 4) time_stamp 转 datetime 并排序
+        df['time_stamp'] = pd.to_datetime(df['time_stamp'])
         df = df.sort_values('time_stamp').reset_index(drop=True)
 
-        # 时间戳格式化为 ISO 8601 字符串
-        timestamps = df['time_stamp'].tolist()
+        # 5) 准备后半段时间戳列表
+        all_timestamps = df['time_stamp'].dt.strftime("%Y-%m-%d %H:%M:%S").tolist()
+        half = len(all_timestamps) // 2
+        trend_timestamps = all_timestamps[half:]
+
+        # 6) 计算每个通道的 SPRT_score
+        # 中文映射(如果需要保留中文名称可在这儿用,但输出不用)
+        # temp_channel_mapping = { ... }
+
+        # 初始化四通道输出结构,默认空对象
+        result_data = {
+            "main_bearing": {},
+            "gearbox_oil": {},
+            "generator_drive_end": {},
+            "generator_nondrive_end": {}
+        }
+        # key 映射:DataFrame 列名 -> 输出字段名
+        key_map = {
+            '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'
+        }
 
-        # 对每个通道,收集它在相应行的数值
-        channels_data = []
         for col in temp_cols:
-            channels_data.append({
-                "temp_channel": col,
-                "values": df[col].tolist()
-            })
-            
-        # 将 temp_channel 列的英文名称改为中文
-        temp_channel_mapping = {
-            'main_bearing_temperature': '主轴承温度',
-            'gearbox_oil_temperature': '齿轮箱油温',
-            'generatordrive_end_bearing_temperature': '发电机驱动端轴承温度',
-            'generatornon_drive_end_bearing_temperature': '发电机非驱动端轴承温度'
-        }
+            series = df[col].values
+            train = series[:half].reshape(-1, 1)
+            test  = series[half:].reshape(-1, 1)
 
-        for channel in channels_data:
-            channel['temp_channel'] = temp_channel_mapping.get(channel['temp_channel'], channel['temp_channel'])
+            # 生成 DL 矩阵并计算 SPRT
+            if self.genDLMatrix(train, dataSize4D=60, dataSize4L=5) != 0:
+                flags = [None] * len(test)
+            else:
+                flags = self.calcSPRT(test, np.array([1.0]), decisionGroup=1)
 
+            # 填充对应 key
+            key = key_map[col]
+            result_data[key] = {
+                "timestamps": trend_timestamps,
+                "values": flags
+            }
 
         return {
-            "timestamps": timestamps,
-            "channels": channels_data,
-            "unit": "°C"
+            "data": result_data,
+            "code": 200,
+            "message": "success"
         }
 
+
+    # def get_trend(self) -> dict:
+    #     """
+    #     趋势分析
+    #     获取温度趋势:将温度数据按时间返回。
+    #     返回格式:{
+    #         "timestamps": [ISO8601 字符串列表],
+    #         "channels": [
+    #             {"temp_channel": "main_bearing_temperature", "values": [浮点列表]},
+    #             {"temp_channel": "gearbox_oil_temperature", "values": [...]},
+    #             ...
+    #         ],
+    #         "unit": "°C"
+    #     }
+    #     """
+    #     df = self._get_data_by_filter()
+
+    #     if df.empty:
+    #         return {"timestamps": [], "channels": [], "unit": "°C"}
+
+    #     # 定义所有需要检查的温度列
+    #     temp_cols_all = [
+    #         'main_bearing_temperature',
+    #         'gearbox_oil_temperature',
+    #         'generatordrive_end_bearing_temperature',
+    #         'generatornon_drive_end_bearing_temperature'
+    #     ]
+    #     # 选择实际存在的列
+    #     temp_cols = [c for c in temp_cols_all if c in df.columns]
+        
+    #     # 如果没有温度数据列,返回空数据
+    #     if not temp_cols:
+    #         return {"timestamps": [], "channels": [], "unit": "°C"}
+
+    #     # 转数值,并删除 NaN
+    #     df[temp_cols] = df[temp_cols].apply(pd.to_numeric, errors='coerce')
+    #     df = df.dropna(subset=temp_cols + ['time_stamp'])
+
+    #     # 转时间戳为 `YYYY-MM-DD HH:MM:SS` 格式
+    #     df['time_stamp'] = pd.to_datetime(df['time_stamp']).dt.strftime("%Y-%m-%d %H:%M:%S")
+    #     df = df.sort_values('time_stamp').reset_index(drop=True)
+
+    #     # 时间戳格式化为 ISO 8601 字符串
+    #     timestamps = df['time_stamp'].tolist()
+
+    #     # 对每个通道,收集它在相应行的数值
+    #     channels_data = []
+    #     for col in temp_cols:
+    #         channels_data.append({
+    #             "temp_channel": col,
+    #             "values": df[col].tolist()
+    #         })
+            
+    #     # 将 temp_channel 列的英文名称改为中文
+    #     temp_channel_mapping = {
+    #         'main_bearing_temperature': '主轴承温度',
+    #         'gearbox_oil_temperature': '齿轮箱油温',
+    #         'generatordrive_end_bearing_temperature': '发电机驱动端轴承温度',
+    #         'generatornon_drive_end_bearing_temperature': '发电机非驱动端轴承温度'
+    #     }
+
+    #     for channel in channels_data:
+    #         channel['temp_channel'] = temp_channel_mapping.get(channel['temp_channel'], channel['temp_channel'])
+
+
+    #     return {
+    #         "timestamps": timestamps,
+    #         "channels": channels_data,
+    #         "unit": "°C"
+    #     }
+

+ 107 - 41
api_tempdiag.py

@@ -25,35 +25,42 @@ class TemperatureInput(BaseModel):
             values['windTurbineNumberList'] = [raw]
         return values
 
+class TemperatureThresholdInput(TemperatureInput):
+    pageNo:   int  # 必填
+    pageSize: int  # 必填
+
 # 阈值分析
 @app.post("/temperature/threshold")
-async def route_threshold(input_data: TemperatureInput):
+async def route_threshold(input_data: TemperatureThresholdInput):
     """
-    阈值分析接口(阈值固定 0.99):
-      - 输入:
-        {
-          "windCode": "WOF01000010",
-          "windTurbineNumberList": ["WOG00542"],
-          "startTime": "2023-01-01 00:00",
-          "endTime": "2023-01-05 12:00"
-        }
-      - 返回:
-        {
-          "data": {
-            "type": "temperature_threshold",
-            "records": [
-              {
-                "time_stamp": "2024-06-08 00:05:00",
-                "temp_channel": "main_bearing_temperature",
-                "SPRT_score": 0.123,
-                "status": "正常"
-              },
-              ...
-            ]
-          },
-          "code": 200,
-          "message": "success"
-        }
+    阈值分析接口(带分页)  
+    - 输入:
+      {
+        "windCode": "WOF01000010",
+        "windTurbineNumberList": ["WOG00542"],
+        "startTime": "2023-01-01 00:00",
+        "endTime": "2023-01-05 12:00",
+        "pageNo": 2,
+        "pageSize": 20
+      }
+
+    - 返回:
+      {
+        "data": {
+          "type": "temperature_threshold",
+          "records": [
+            {
+              "time_stamp": "2024-06-08 00:05:00",
+              "temp_channel": "主轴承温度",
+              "SPRT_score": 0.123,
+              "status": "正常"
+            },
+            ...
+          ]
+        },
+        "code": 200,
+        "message": "success"
+      }
     """
     try:
         analyzer = MSET_Temp(
@@ -62,12 +69,16 @@ async def route_threshold(input_data: TemperatureInput):
             startTime=input_data.startTime,
             endTime=input_data.endTime
         )
-        df_result = analyzer.check_threshold()  # DataFrame 长格式
-        records = df_result.to_dict(orient="records")
+        records = analyzer.check_threshold().to_dict(orient="records")
+
+        start = (input_data.pageNo - 1) * input_data.pageSize
+        end   = start + input_data.pageSize
+        paginated = records[start:end]
+
         return {
             "data": {
                 "type": "temperature_threshold",
-                "records": records
+                "records": paginated
             },
             "code": 200,
             "message": "success"
@@ -82,12 +93,12 @@ async def route_threshold(input_data: TemperatureInput):
             }
         )
 
-# 趋势分析(暂未调用)
-@app.post("/temperature/trend")
+# SPRT趋势分析
+@app.post("/SPRT/trend")
 async def route_trend(input_data: TemperatureInput):
     """
     趋势分析接口:
-      - 输入
+      - 输入:
         {
           "windCode": "WOF01000010",
           "windTurbineNumberList": ["WOG00542"],
@@ -97,13 +108,11 @@ async def route_trend(input_data: TemperatureInput):
       - 返回:
         {
           "data": {
-            "type": "temperature_trend",
-            "timestamps": [ "2024-06-08 00:00:00", ... ],
-            "channels": [
-               { "temp_channel": "main_bearing_temperature", "values": [24.5, 24.7, ...] },
-               ...
-            ],
-            "unit": "°C"
+            "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"
@@ -116,11 +125,14 @@ async def route_trend(input_data: TemperatureInput):
             startTime=input_data.startTime,
             endTime=input_data.endTime
         )
+        # get_trend() 已经返回形如 {"data": { … 四个 key … }} 的字典
         result = analyzer.get_trend()
+
+        # 组装最终响应
         return {
             "data": {
-                "type": "temperature_trend",
-                **result
+                "type": "SPRT_trend",
+                **result.get("data", {})    # 四个通道的数据或空对象
             },
             "code": 200,
             "message": "success"
@@ -136,6 +148,60 @@ async def route_trend(input_data: TemperatureInput):
         )
 
 
+# # 温度趋势分析(暂未调用)
+# @app.post("/temperature/trend")
+# async def route_trend(input_data: TemperatureInput):
+#     """
+#     趋势分析接口:
+#       - 输入:
+#         {
+#           "windCode": "WOF01000010",
+#           "windTurbineNumberList": ["WOG00542"],
+#           "startTime": "2023-01-01 00:00",
+#           "endTime": "2023-01-05 12:00"
+#         }
+#       - 返回:
+#         {
+#           "data": {
+#             "type": "temperature_trend",
+#             "timestamps": [ "2024-06-08 00:00:00", ... ],
+#             "channels": [
+#                { "temp_channel": "main_bearing_temperature", "values": [24.5, 24.7, ...] },
+#                ...
+#             ],
+#             "unit": "°C"
+#           },
+#           "code": 200,
+#           "message": "success"
+#         }
+#     """
+#     try:
+#         analyzer = MSET_Temp(
+#             windCode=input_data.windCode,
+#             windTurbineNumberList=input_data.windTurbineNumberList,
+#             startTime=input_data.startTime,
+#             endTime=input_data.endTime
+#         )
+#         result = analyzer.get_trend()
+#         return {
+#             "data": {
+#                 "type": "temperature_trend",
+#                 **result
+#             },
+#             "code": 200,
+#             "message": "success"
+#         }
+#     except Exception as e:
+#         return JSONResponse(
+#             status_code=500,
+#             content={
+#                 "code": 500,
+#                 "message": "analysis failed",
+#                 "detail": str(e)
+#             }
+#         )
+
+
 
 if __name__ == "__main__":
     uvicorn.run(