wangjiaojiao 4 місяців тому
коміт
062ec5fb21
3 змінених файлів з 911 додано та 0 видалено
  1. 2 0
      .gitignore
  2. 187 0
      api_20241201.py
  3. 722 0
      cms_class_20241201.py

+ 2 - 0
.gitignore

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

+ 187 - 0
api_20241201.py

@@ -0,0 +1,187 @@
+# coding=utf-8
+from fastapi import FastAPI,  UploadFile, HTTPException
+from fastapi.responses import JSONResponse
+from typing import List, Optional
+from pydantic import BaseModel
+import os
+import json
+import shutil
+import pandas as pd
+import uvicorn
+from cms_class_20241201 import CMSAnalyst
+from typing import List, Optional, Union
+from pydantic import BaseModel, Field, model_validator
+
+# 初始化 FastAPI 应用
+app = FastAPI()
+
+
+'''
+input:
+{
+    "ids":[12345,121212],
+    "windCode":"xxxx",
+    "analysisType":"xxxxx",
+    "fmin":int(xxxx) (None),
+    "fmax":"int(xxxx) (None),
+}
+
+
+output:
+
+{
+data:[
+    {
+    "type":"envelope_spectrum",
+    "x":list(x),
+    "y":list(y),
+    "title":title,
+    "xaxis":xaxis,
+    "yaxis":yaxis
+    },
+    {
+    "type":"bandpass_filtered",
+    "x":list(x),
+    "y":list(y),
+    "title":title,
+    "xaxis":xaxis,
+    "yaxis":yaxis
+    },
+
+
+    # 其他情况:
+    {
+    "type":"trend_analysis",
+    "Mean": mean_value,
+    "Max": max_value,
+    "Min": min_value,
+    "Xrms": Xrms,
+    "Xp": Xp,
+    "If": If,
+    "Cf": Cf,
+    "Sf": Sf,
+    "Ce": Ce,
+    "Cw": Cw,
+    "Cq": Cq
+    }
+],
+
+"code": 200,
+"message":success
+}
+
+'''
+# 请求模型定义
+class AnalysisInput(BaseModel):
+    # ids: List
+    ids: List[int]
+    windCode: str
+    analysisType: str
+    fmin: Optional[int] = None
+    fmax: Optional[int] = None
+
+    @model_validator(mode='before')
+    def convert_ids(cls, values):
+        if isinstance(values.get('ids'), int):
+            values['ids'] = [values['ids']]
+        return values
+
+# url = "http://127.0.0.1:8888/analysis/trend"
+
+
+@app.post("/analysis/{analysisType}")
+async def analyze(analysisType: str, input_data:AnalysisInput):
+
+    analysis_map = {
+        "envelope": "envelope_spectrum",#包络谱分析
+        "frequency": "frequency_domain",#频域分析
+        "time": "time_domain",#时域分析
+        "trend": "trend_analysis"#趋势分析
+    }
+
+
+    if analysisType not in analysis_map:
+        raise HTTPException(status_code=400, detail="非可用的分析类型")
+
+
+    try:
+        cms = CMSAnalyst(input_data.fmin, input_data.fmax, input_data.windCode, input_data.ids)
+
+        func = getattr(cms, analysis_map[analysisType])
+        # if callable(func):
+        #     func_res = func()
+        # func_res = json.loads(func_res)
+        # func_res['type'] = analysisType
+        # # return {
+        # #     "message": "success",
+        # #     "data": [func_res],
+        # # }
+        if callable(func):    # 用于判断一个对象是否可以被调用,是的话返回true
+            func_res = func()
+        if isinstance(func_res, str):
+            func_res = json.loads(func_res)    # 字符串转化为字典形式
+        if isinstance(func_res, dict):
+            func_res['type'] = analysisType
+        elif isinstance(func_res, list):
+            func_res = {'type': analysisType, 'data': func_res}
+        else:
+            # 处理其他情况,例如其他数据类型
+            func_res = {'type': analysisType, 'data': str(func_res)}
+        return json.dumps(func_res,ensure_ascii=False)   #对象转化为字符串形式
+        # return JSONResponse(content=func_res)#返回json格式
+    except Exception as e:
+          return {"message": "error", "detail": str(e)}
+
+# @app.post("/analysis/{analysisType}")
+# async def analyze(analysisType: str, input_data:AnalysisInput):
+#     try:
+#         analysis_map = {
+#             "envelope": ["envelope_spectrum","bandpass_filtered"],
+#             "frequency": "frequency_domain",
+#             "time": "time_domain",
+#             "trend": "trend_analysis"
+#         }
+
+
+#         if analysisType not in analysis_map:
+#             raise HTTPException(status_code=400, detail="非可用的分析类型")
+
+
+#         try:
+#             cms = CMSAnalyst(input_data.fmin, input_data.fmax, input_data.windCode, input_data.ids)
+            
+#             if analysisType == "envelope":
+#                 tmp = []
+#                 for type_func in analysis_map[analysisType]:
+#                     func = getattr(cms, type_func)
+#                     if callable(func):
+#                         func_res = func()
+#                     func_res['type'] = analysisType
+#                     tmp.append(func_res)
+
+#                 return {
+#                     "message": "success",
+#                     "data": tmp,
+#                 }
+            
+#             else:
+#                 func = getattr(cms, analysis_map[analysisType])
+#                 if callable(func):
+#                     func_res = func()
+#                 func_res['type'] = analysisType
+#                 return {
+#                     "message": "success",
+#                     "data": [func_res],
+#                 }
+#         except Exception as e:
+#             return {"message": "error", "detail": str(e)}
+#     except:
+#         raise HTTPException(status_code=400, detail="接口返回错误")
+
+
+if __name__ == "__main__":
+    uvicorn.run(
+        app,
+        host=("0.0.0.0"),
+        port=8888
+    )

+ 722 - 0
cms_class_20241201.py

@@ -0,0 +1,722 @@
+import numpy as np
+from scipy.signal import hilbert
+from scipy.fft import ifft
+import plotly.graph_objs as go
+import pandas as pd
+from sqlalchemy import create_engine, text
+import sqlalchemy
+from typing import Dict,Any
+import json
+import ast
+import math
+
+'''
+# 输入:
+{
+    "ids":[12345,121212],
+    "windCode":"xxxx",
+    "analysisType":"xxxxx",
+    "fmin":int(xxxx) (None),
+    "fmax":"int(xxxx) (None),
+}
+
+[{id:xxxx,"time":xxx},{}]
+
+id[123456]
+
+# 通过id,读取mysql,获取数据
+engine = create_engine('mysql+pymysql://root:admin123456@192.168.50.235:30306/energy_data')
+
+def get_by_id(table_name,id):
+    lastday_df_sql = f"SELECT * FROM {table_name} where id = {id} "
+    # print(lastday_df_sql)
+    df = pd.read_sql(lastday_df_sql, engine)
+    return df
+
+select distinct id, timeStamp from table_name group by ids
+
+ids  time 
+1   xxx
+2   xxxx
+
+df_data = []    
+# for id in ids:
+# sql_data = get_by_id('SKF001_wave',id)
+# df_data.append(sql_data)
+# print(sql_data)
+
+[df1,df2]
+'''
+
+
+'''
+数据库字段:
+"samplingFrequency"
+"timeStamp"
+"mesureData"
+'''
+# %%
+
+# %%
+
+# 主要的类
+class CMSAnalyst:
+    def __init__(self, fmin, fmax, table_name, ids):
+        self.table_name =table_name
+        self.ids = ids
+        # envelope_spectrum_analysis
+        # datas是[df1,df2,.....]
+        #从数据库获取原始数据
+        self.datas = self._get_by_id(table_name,ids)
+        self.datas = [
+            df[['id', 'mesure_data', 'time_stamp', 'sampling_frequency', 
+                'wind_turbine_number', 'rotational_speed', 'mesure_point_name']]
+            for df in self.datas
+        ]       
+
+        # 只输入一个id,返回一个[df],所以拿到self.data[0]
+        self.data_filter = self.datas[0]
+        # print("mesure_data sample:", self.data_filter['mesure_data'].iloc[0])  # 打印第一条数据
+        self.data = np.array(ast.literal_eval(self.data_filter['mesure_data'].iloc[0]))        
+        # print(self.data_filter)
+        # 取数据列
+        self.data = np.array(ast.literal_eval(self.data_filter['mesure_data'][0]))
+        self.envelope_spectrum_m = self.data.shape[0]
+        self.envelope_spectrum_n = 1
+        #设置分析参数
+        self.fs = int(self.data_filter['sampling_frequency'].iloc[0])
+        self.envelope_spectrum_t = np.arange(self.envelope_spectrum_m) / self.fs
+        self.fmin = fmin if fmin is not None else 0 
+        self.fmax = fmax if fmax is not None else float('inf') 
+        self.envelope_spectrum_y = self._bandpass_filter(self.data)
+        self.f, self.HP = self._calculate_envelope_spectrum(self.envelope_spectrum_y)
+        #设备信息
+        self.wind_code = self.data_filter['wind_turbine_number'].iloc[0]
+        self.rpm_Gen = self.data_filter['rotational_speed'].iloc[0]
+        self.mesure_point_name = self.data_filter['mesure_point_name'].iloc[0]
+        self.fn_Gen = round(self.rpm_Gen/60,2)
+
+        self.CF = self.Characteristic_Frequency()
+        print(self.CF)
+        self.CF = pd.DataFrame(self.CF,index=[0])
+        print(self.CF)
+        print(self.rpm_Gen)
+        #if self.CF['type'].iloc[0] == 'bearing':
+        n_rolls_m = self.CF['n_rolls'].iloc[0]
+        d_rolls_m = self.CF['d_rolls'].iloc[0]
+        D_diameter_m = self.CF['D_diameter'].iloc[0]
+        theta_deg_m = self.CF['theta_deg'].iloc[0]
+        print(n_rolls_m)
+        print(d_rolls_m)
+        print(D_diameter_m)
+        print(theta_deg_m)
+        self.bearing_frequencies = self.calculate_bearing_frequencies(n_rolls_m, d_rolls_m, D_diameter_m, theta_deg_m, self.rpm_Gen)
+        print(self.bearing_frequencies)
+        self.bearing_frequencies = pd.DataFrame(self.bearing_frequencies,index=[0])
+        print(self.bearing_frequencies)
+        # frequency_domain_analysis
+        (
+            self.frequency_domain_analysis_t,
+            self.frequency_domain_analysis_f,
+            self.frequency_domain_analysis_m,
+            self.frequency_domain_analysis_mag,
+            self.frequency_domain_analysis_Xrms,
+        ) = self._calculate_spectrum(self.data)
+
+        # time_domain_analysis
+        self.time_domain_analysis_t = np.arange(self.data.shape[0]) / self.fs
+        
+    # def _get_by_id(self, windcode, ids):
+    #     df_res = []
+    #     #engine = create_engine('mysql+pymysql://root:admin123456@192.168.50.235:30306/energy_data_prod')
+    #     engine = create_engine('mysql+pymysql://root:admin123456@106.120.102.238:10336/energy_data_prod')
+    #     for id in ids:
+    #         table_name=windcode+'_wave'
+    #         lastday_df_sql = f"SELECT * FROM {table_name} where id = {id} "
+    #         # print(lastday_df_sql)
+    #         df = pd.read_sql(lastday_df_sql, engine)
+    #         df_res.append(df)
+    #     return df_res
+    
+    # def _get_by_id(self, windcode, ids):
+    #     #engine = create_engine('mysql+pymysql://root:admin123456@106.120.102.238:10336/energy_data_prod')
+    #     engine = create_engine('mysql+pymysql://root:admin123456@192.168.50.235:30306/energy_data_prod')
+    #     table_name = windcode + '_wave'
+    #     ids_str = ','.join(map(str, ids))
+    #     sql = f"SELECT * FROM {table_name} WHERE id IN ({ids_str}) ORDER BY time_stamp"
+    #     df = pd.read_sql(sql, engine)
+        
+    #     # 按ID分组返回
+    #     grouped = [group for _, group in df.groupby('id')]
+    #     return grouped        
+    def _get_by_id(self, windcode, ids):
+        engine = create_engine('mysql+pymysql://root:admin123456@192.168.50.235:30306/energy_data_prod')
+        table_name = windcode + '_wave'
+        ids_str = ','.join(map(str, ids))
+        sql = f"SELECT * FROM {table_name} WHERE id IN ({ids_str}) ORDER BY time_stamp"
+        print("Executing SQL:", sql)  # 打印 SQL
+        df = pd.read_sql(sql, engine)
+        print("Returned DataFrame shape:", df.shape)  # 检查返回的数据量
+        grouped = [group for _, group in df.groupby('id')]
+        return grouped
+
+    # envelope_spectrum_analysis 包络谱分析
+    def _bandpass_filter(self, data):
+        """带通滤波"""
+        
+        m= data.shape[0]
+        ni = round(self.fmin * self.envelope_spectrum_m / self.fs + 1)
+        # na = round(self.fmax * self.envelope_spectrum_m / self.fs + 1)
+        if self.fmax == float('inf'):
+            na = m
+        else:
+            na = round(self.fmax * m / self.fs + 1)
+        col = 1
+        y = np.zeros((self.envelope_spectrum_m, col))
+        # for p in range(col):
+        #     print(data.shape,p)
+        z = np.fft.fft(data)
+        a = np.zeros(self.envelope_spectrum_m, dtype=complex)
+        a[ni:na] = z[ni:na]
+        a[self.envelope_spectrum_m - na + 1 : self.envelope_spectrum_m - ni + 1] = z[
+            self.envelope_spectrum_m - na + 1 : self.envelope_spectrum_m - ni + 1
+        ]
+        z = np.fft.ifft(a)
+        y[:, 0] = np.real(z)
+
+        return y
+
+    def _calculate_envelope_spectrum(self, y):
+        """计算包络谱"""
+        m, n = y.shape
+        HP = np.zeros((m, n))
+        col = 1
+        for p in range(col):
+            H = np.abs(hilbert(y[:, p] - np.mean(y[:, p])))
+            HP[:, p] = np.abs(np.fft.fft(H - np.mean(H))) * 2 / m
+        f = np.fft.fftfreq(m, d=1 / self.fs)
+        return f, HP
+
+
+    def envelope_spectrum(self):
+        """绘制包络谱"""
+        # 只取正频率部分
+        positive_frequencies = self.f[: self.envelope_spectrum_m // 2]
+        positive_HP = self.HP[: self.envelope_spectrum_m // 2, 0]
+
+        x = positive_frequencies
+        y = positive_HP
+        title = "包络谱"
+        xaxis = "频率(Hz)"
+        yaxis = "加速度(m/s^2)"
+        Xrms = np.sqrt(np.mean(y**2))  # 加速度均方根值(有效值)
+        rpm_Gen = round(self.rpm_Gen, 2)
+        BPFI_1X = round(self.bearing_frequencies['BPFI'].iloc[0], 2)
+        BPFO_1X = round(self.bearing_frequencies['BPFO'].iloc[0], 2)
+        BSF_1X = round(self.bearing_frequencies['BSF'].iloc[0], 2)
+        FTF_1X = round(self.bearing_frequencies['FTF'].iloc[0], 2)
+        fn_Gen = round(self.fn_Gen, 2)
+        _3P_1X = round(self.fn_Gen, 2) * 3
+
+        # if self.CF['type'].iloc[0] == 'bearing':
+        result = {
+            "fs":self.fs,
+            "Xrms":round(Xrms, 2),
+            "x":list(x),
+            "y":list(y),
+            "title":title,
+            "xaxis":xaxis,
+            "yaxis":yaxis,
+            "rpm_Gen": round(rpm_Gen, 2),  # 转速r/min
+            "BPFI":  [{"Xaxis": BPFI_1X ,"val": "1BPFI"},{"Xaxis": BPFI_1X*2 ,"val": "2BPFI"},
+                        {"Xaxis": BPFI_1X*3, "val": "3BPFI"}, {"Xaxis": BPFI_1X*4, "val": "4BPFI"},
+                        {"Xaxis": BPFI_1X*5, "val": "5BPFI"}, {"Xaxis": BPFI_1X*6, "val": "6BPFI"}],
+            "BPFO": [{"Xaxis": BPFO_1X ,"val": "1BPFO"},{"Xaxis": BPFO_1X*2 ,"val": "2BPFO"},
+                        {"Xaxis": BPFO_1X*3, "val": "3BPFO"}, {"Xaxis": BPFO_1X*4, "val": "4BPFO"},
+                        {"Xaxis": BPFO_1X*5, "val": "5BPFO"}, {"Xaxis": BPFO_1X*6, "val": "6BPFO"}],
+            "BSF": [{"Xaxis": BSF_1X ,"val": "1BSF"},{"Xaxis": BSF_1X*2 ,"val": "2BSF"},
+                        {"Xaxis": BSF_1X*3, "val": "3BSF"}, {"Xaxis": BSF_1X*4, "val": "4BSF"},
+                        {"Xaxis": BSF_1X*5, "val": "5BSF"}, {"Xaxis": BSF_1X*6, "val": "6BSF"}],
+            "FTF": [{"Xaxis": FTF_1X ,"val": "1FTF"},{"Xaxis": FTF_1X*2 ,"val": "2FTF"},
+                        {"Xaxis": FTF_1X*3, "val": "3FTF"}, {"Xaxis": FTF_1X*4, "val": "4FTF"},
+                        {"Xaxis": FTF_1X*5, "val": "5FTF"}, {"Xaxis": FTF_1X*6, "val": "6FTF"}],
+            "fn_Gen":[{"Xaxis": fn_Gen ,"val": "1X"},{"Xaxis": fn_Gen*2 ,"val": "2X"},
+                        {"Xaxis": fn_Gen*3, "val": "3X"}, {"Xaxis": fn_Gen*4, "val": "4X"},
+                        {"Xaxis": fn_Gen*5, "val": "5X"}, {"Xaxis": fn_Gen*6, "val": "6X"}],
+            "B3P":_3P_1X,
+        }
+        # result = json.dumps(result, ensure_ascii=False)
+        result = self.replace_nan(result)
+
+        return result
+
+
+    # frequency_domain_analysis 频谱分析
+
+    def _calculate_spectrum(self, data):
+        """计算频谱"""
+        m = data.shape[0]
+        n = 1
+        t = np.arange(m) / self.fs
+        mag = np.zeros((m, n))
+        Xrms = np.sqrt(np.mean(data**2))  # 加速度均方根值(有效值)
+        # col=1
+        # for p in range(col):
+        mag = np.abs(np.fft.fft(data - np.mean(data))) * 2 / m
+        f = np.fft.fftfreq(m, d=1 / self.fs)
+        return t, f, m, mag, Xrms
+
+    def frequency_domain(self):
+        """绘制频域波形参数"""
+        # 只取正频率部分
+        positive_frequencies = self.frequency_domain_analysis_f[
+            : self.frequency_domain_analysis_m // 2
+        ]
+        positive_mag = self.frequency_domain_analysis_mag[
+            : self.frequency_domain_analysis_m // 2
+        ]
+
+        x = positive_frequencies
+        y = positive_mag
+        title = "频域信号"
+        xaxis = "频率(Hz)"
+        yaxis = "加速度(m/s^2)"
+        Xrms = self.frequency_domain_analysis_Xrms
+
+        rpm_Gen = round(self.rpm_Gen, 2)
+        BPFI_1X = round(self.bearing_frequencies['BPFI'].iloc[0], 2)
+        BPFO_1X =  round(self.bearing_frequencies['BPFO'].iloc[0], 2)
+        BSF_1X =  round(self.bearing_frequencies['BSF'].iloc[0], 2)
+        FTF_1X = round(self.bearing_frequencies['FTF'].iloc[0], 2)
+        fn_Gen = round(self.fn_Gen, 2)
+        _3P_1X = round(self.fn_Gen, 2) * 3
+
+        # if self.CF['type'].iloc[0] == 'bearing':
+        result = {
+            "fs":self.fs,
+            "Xrms":round(Xrms, 2),
+            "x":list(x),
+            "y":list(y),
+            "title":title,
+            "xaxis":xaxis,
+            "yaxis":yaxis,
+            "rpm_Gen": round(rpm_Gen, 2),  # 转速r/min
+            "BPFI":  [{"Xaxis": BPFI_1X ,"val": "1BPFI"},{"Xaxis": BPFI_1X*2 ,"val": "2BPFI"},
+                        {"Xaxis": BPFI_1X*3, "val": "3BPFI"}, {"Xaxis": BPFI_1X*4, "val": "4BPFI"},
+                        {"Xaxis": BPFI_1X*5, "val": "5BPFI"}, {"Xaxis": BPFI_1X*6, "val": "6BPFI"}],
+            "BPFO": [{"Xaxis": BPFO_1X ,"val": "1BPFO"},{"Xaxis": BPFO_1X*2 ,"val": "2BPFO"},
+                        {"Xaxis": BPFO_1X*3, "val": "3BPFO"}, {"Xaxis": BPFO_1X*4, "val": "4BPFO"},
+                        {"Xaxis": BPFO_1X*5, "val": "5BPFO"}, {"Xaxis": BPFO_1X*6, "val": "6BPFO"}],
+            "BSF": [{"Xaxis": BSF_1X ,"val": "1BSF"},{"Xaxis": BSF_1X*2 ,"val": "2BSF"},
+                        {"Xaxis": BSF_1X*3, "val": "3BSF"}, {"Xaxis": BSF_1X*4, "val": "4BSF"},
+                        {"Xaxis": BSF_1X*5, "val": "5BSF"}, {"Xaxis": BSF_1X*6, "val": "6BSF"}],
+            "FTF": [{"Xaxis": FTF_1X ,"val": "1FTF"},{"Xaxis": FTF_1X*2 ,"val": "2FTF"},
+                        {"Xaxis": FTF_1X*3, "val": "3FTF"}, {"Xaxis": FTF_1X*4, "val": "4FTF"},
+                        {"Xaxis": FTF_1X*5, "val": "5FTF"}, {"Xaxis": FTF_1X*6, "val": "6FTF"}],
+            "fn_Gen":[{"Xaxis": fn_Gen ,"val": "1X"},{"Xaxis": fn_Gen*2 ,"val": "2X"},
+                        {"Xaxis": fn_Gen*3, "val": "3X"}, {"Xaxis": fn_Gen*4, "val": "4X"},
+                        {"Xaxis": fn_Gen*5, "val": "5X"}, {"Xaxis": fn_Gen*6, "val": "6X"}],
+            "B3P":_3P_1X,
+        }
+        result = self.replace_nan(result)       
+        result = json.dumps(result, ensure_ascii=False)
+        return result
+
+    # time_domain_analysis 时域分析
+    def time_domain(self):
+        """绘制时域波形参数"""        
+        x = self.time_domain_analysis_t
+        y = self.data
+        rpm_Gen =self.rpm_Gen
+        title = "时间域信号"
+        xaxis = "时间(s)"
+        yaxis = "加速度(m/s^2)"
+        # 图片右侧统计量
+        mean_value = np.mean(y)#平均值
+        max_value = np.max(y)#最大值
+        min_value = np.min(y)#最小值
+        Xrms = np.sqrt(np.mean(y**2))  # 加速度均方根值(有效值)
+        Xp = (max_value - min_value) / 2  # 峰值(单峰最大值) # 峰值
+        Xpp=max_value-min_value#峰峰值
+        Cf = Xp / Xrms  # 峰值指标
+        Sf = Xrms / mean_value  # 波形指标
+        If = Xp / np.mean(np.abs(y))  # 脉冲指标
+        Xr = np.mean(np.sqrt(np.abs(y))) ** 2  # 方根幅值
+        Ce = Xp / Xr  # 裕度指标
+        # 计算每个数据点的绝对值减去均值后的三次方,并求和
+        sum_abs_diff_cubed_3 = np.mean((np.abs(y) - mean_value) ** 3)
+        # 计算偏度指标
+        Cw = sum_abs_diff_cubed_3 / (Xrms**3)
+        # 计算每个数据点的绝对值减去均值后的四次方,并求和
+        sum_abs_diff_cubed_4 = np.mean((np.abs(y) - mean_value) ** 4)
+        # 计算峭度指标
+        Cq = sum_abs_diff_cubed_4 / (Xrms**4)
+        result = {
+                 
+            "x":list(x),
+            "y":list(y),
+            "title":title,
+            "xaxis":xaxis,
+            "yaxis":yaxis,
+            "fs":self.fs,
+            "Xrms":round(Xrms, 2),#有效值
+            "mean_value":round(mean_value, 2),# 均值 
+            "max_value":round(max_value, 2),# 最大值 
+            "min_value":round(min_value, 2),  # 最小值          
+            "Xp":round(Xp, 2),# 峰值
+            "Xpp":round(Xpp, 2),# 峰峰值
+            "Cf":round(Cf, 2),# 峰值指标
+            "Sf":round(Sf, 2),# 波形因子
+            "If":round(If, 2),# 脉冲指标
+            "Ce":round(Ce, 2),# 裕度指标
+            "Cw":round(Cw, 2) ,# 偏度指标
+            "Cq":round(Cq, 2) ,# 峭度指标
+            "rpm_Gen": round(rpm_Gen, 2),  # 转速r/min
+
+        }
+        result = self.replace_nan(result)
+        result = json.dumps(result, ensure_ascii=False)
+
+        return result
+
+    def trend_analysis(self) -> str:
+        """
+        优化后的趋势分析方法(向量化计算统计指标)
+        返回 JSON 字符串,包含所有时间点的统计结果。
+        """
+        for df in self.datas:
+            df['parsed_data'] = df['mesure_data'].apply(json.loads)            
+        # 1. 合并所有数据并解析 mesure_data
+        combined_df = pd.concat(self.datas)
+        
+
+        combined_df['parsed_data'] = combined_df['mesure_data'].apply(json.loads)  # 批量解析 JSON
+        
+        # 2. 向量化计算统计指标(避免逐行循环)
+        def calculate_stats(group: pd.DataFrame) -> Dict[str, Any]:
+            data = np.array(group['parsed_data'].iloc[0])  # 提取振动数据数组
+            fs = int(group['sampling_frequency'].iloc[0])  # 采样频率
+            dt = 1 / fs  # 时间间隔
+
+            # 计算时域指标(向量化操作)
+            mean = np.mean(data)
+            max_val = np.max(data)
+            min_val = np.min(data)
+            Xrms = np.sqrt(np.mean(data**2))
+            Xp = (max_val - min_val) / 2
+            Cf = Xp / Xrms
+            Sf = Xrms / mean if mean != 0 else 0
+            If = Xp / np.mean(np.abs(data))
+            Xr = np.mean(np.sqrt(np.abs(data))) ** 2
+            Ce = Xp / Xr
+            
+            # 计算偏度和峭度
+
+ 
+            # 计算速度有效值
+            velocity = np.cumsum(data) * dt
+            velocity_rms = np.sqrt(np.mean(velocity**2))
+            Cw = np.mean((data - mean) ** 3) / (Xrms ** 3) if Xrms != 0 else 0
+            Cq = np.mean((data - mean) ** 4) / (Xrms ** 4) if Xrms != 0 else 0                     
+            return {
+                "fs": fs,
+                "Mean": round(mean, 2),
+                "Max": round(max_val, 2),
+                "Min": round(min_val, 2),
+                "Xrms": round(Xrms, 2),
+                "Xp": round(Xp, 2),
+                "If": round(If, 2),
+                "Cf": round(Cf, 2),
+                "Sf": round(Sf, 2),
+                "Ce": round(Ce, 2),
+                "Cw": round(Cw, 2),
+                "Cq": round(Cq, 2),
+                "velocity_rms": round(velocity_rms, 2),
+                "time_stamp": str(group['time_stamp'].iloc[0])
+            }
+        
+ 
+        # 3. 按 ID 分组并应用统计计算
+        stats = combined_df.groupby('id').apply(calculate_stats).tolist()
+
+        # 4. 返回 JSON 格式结果
+        return json.dumps(stats, ensure_ascii=False) 
+
+
+    def Characteristic_Frequency(self):
+        """提取轴承、齿轮等参数"""
+        str1 = self.mesure_point_name
+        print(str1)
+        # 2、连接233的数据库'energy_show',从表'wind_engine_group'查找风机编号'engine_code'对应的机型编号'mill_type_code'
+        engine_code = self.wind_code
+        print(engine_code)
+        Engine = create_engine('mysql+pymysql://admin:admin123456@192.168.50.233:3306/energy_show')
+        #Engine = create_engine('mysql+pymysql://admin:admin123456@106.120.102.238:16306/energy_show')        
+        # df_sql2 = f"SELECT * FROM {'wind_engine_group'} where engine_code = {'engine_code'} "
+        df_sql2 = f"SELECT * FROM wind_engine_group WHERE engine_code = '{engine_code}'"
+        df2 = pd.read_sql(df_sql2, Engine)
+        mill_type_code = df2['mill_type_code'].iloc[0]
+        print(mill_type_code)
+
+        # # 3、从表'unit_bearings'中通过机型编号'mill_type_code'查找部件'brand'、'model'的参数信息
+        # 3、从相关的表中通过机型编号'mill_type_code'或者齿轮箱编号gearbox_code查找部件'brand'、'model'的参数信息
+        #unit_bearings主轴承参数表 关键词"main_bearing"
+        if 'main_bearing' in str1:
+            print("main_bearing")
+            df_sql3 = f"SELECT * FROM unit_bearings WHERE mill_type_code = '{mill_type_code}' "            
+            df3 = pd.read_sql(df_sql3, Engine)
+            if df3.empty:
+                print("警告: 没有找到有效的机型信息")   
+            if 'front' in str1:
+                brand = 'front_bearing' + '_brand'  
+                model = 'front_bearing' + '_model'  
+                front_has_value = not pd.isna(df3[brand].iloc[0]) and not pd.isna(df3[model].iloc[0]) 
+                if not  front_has_value:
+                    print("警告: 没有找到有效的品牌信息")
+            elif 'rear' in str1:
+                brand = 'rear_bearing' + '_brand'  
+                model = 'rear_bearing' + '_model'  
+                end_has_value = not pd.isna(df3[brand].iloc[0]) and not pd.isna(df3[model].iloc[0])       
+                if not  end_has_value:
+                    print("警告: 没有找到有效的品牌信息")                
+            else:
+                # 当没有指定 front 或 end 时,自动选择有值的轴承信息
+                front_brand_col = 'front_bearing_brand'
+                front_model_col = 'front_bearing_model'
+                rear_brand_col = 'rear_bearing_brand'
+                rear_model_col = 'rear_bearing_model'
+                # 检查 front_bearing 是否有值
+                front_has_value = not pd.isna(df3[front_brand_col].iloc[0]) and not pd.isna(df3[front_model_col].iloc[0])                
+                # 检查 end_bearing 是否有值
+                end_has_value = not pd.isna(df3[rear_brand_col].iloc[0]) and not pd.isna(df3[rear_model_col].iloc[0])               
+                # 根据检查结果选择合适的列
+                if front_has_value and end_has_value:
+                    # 如果两者都有值,默认选择 front
+                    brand = front_brand_col
+                    model = front_model_col
+                elif front_has_value:
+                    brand = front_brand_col
+                    model = front_model_col
+                elif end_has_value:
+                    brand = rear_brand_col
+                    model = rear_model_col
+                else:
+                    # 如果两者都没有有效值,设置默认值或抛出异常
+                    print("警告: 没有找到有效的轴承信息")
+                    brand = front_brand_col  # 默认使用 front
+                    model = front_model_col  # 默认使用 front
+            print(brand)
+            _brand = df3[brand].iloc[0]
+            _model = df3[model].iloc[0]
+            print(_brand)
+            print(_model)     
+        #unit_dynamo 发电机参数表 关键词generator stator
+        elif 'generator'in str1 or 'stator' in str1:
+            print("generator or 'stator'")
+            # df_sql3 = f"SELECT * FROM {'unit_dynamo'} where mill_type_code = {'mill_type_code'} "
+            df_sql3 = f"SELECT * FROM unit_dynamo WHERE mill_type_code = '{mill_type_code}' "
+            df3 = pd.read_sql(df_sql3, Engine)
+            if 'non' in str1:
+                brand = 'non_drive_end_bearing' + '_brand'  
+                model = 'non_drive_end_bearing' + '_model'  
+            else:
+                brand = 'drive_end_bearing' + '_brand'  
+                model = 'drive_end_bearing' + '_model'              
+            print(brand)
+            _brand = df3[brand].iloc[0]
+            _model = df3[model].iloc[0]
+            print(_brand)
+            print(_model)       
+
+         #齿轮箱区分行星轮/平行轮 和 轴承两个表
+        elif 'gearbox' in str1:
+            print("gearbox")
+            #根据mill_type_code从unit_gearbox表中获得gearbox_code
+            df_sql3 = f"SELECT * FROM unit_gearbox WHERE mill_type_code = '{mill_type_code}' "
+            df3 = pd.read_sql(df_sql3, Engine)
+            gearbox_code =df3['code'].iloc[0]
+            print(gearbox_code)         
+            #Engine33 = create_engine('mysql+pymysql://admin:admin123456@106.120.102.238:16306/energy_show')
+             #如果是行星轮/平行轮 则从unit_gearbox_structure 表中取数据
+            if 'planet'in str1 or 'sun' in str1:
+                print("'planet' or 'sun' ")
+                gearbox_structure =1 if 'planet'in str1 else 2
+                planetary_gear_grade =1
+                if 'first' in str1:
+                    planetary_gear_grade =1
+                elif 'second'in str1:
+                    planetary_gear_grade =2
+                elif 'third'in str1:
+                    planetary_gear_grade =3
+                # df_sql33 = f"SELECT * FROM unit_gearbox_structure WHERE gearbox_code = '{gearbox_code}' "
+                df_sql33 = f"""
+                SELECT bearing_brand, bearing_model 
+                FROM unit_gearbox_structure 
+                    WHERE gearbox_code = '{gearbox_code}' 
+                    AND gearbox_structure = '{gearbox_structure}'
+                    AND planetary_gear_grade = '{planetary_gear_grade}'
+                    """
+                df33 = pd.read_sql(df_sql33, Engine)
+                if df33.empty:
+                    print("unit_gearbox_structure没有该测点的参数")
+                else:
+                    brand = 'bearing' + '_brand'  
+                    model = 'bearing' + '_model'  
+                    print(brand)
+                    _brand = df33[brand].iloc[0]
+                    _model = df33[model].iloc[0]
+                    has_value = not pd.isna(df33[brand].iloc[0]) and not pd.isna(df33[model].iloc[0])
+                    if has_value:   
+                        print(_brand)
+                        print(_model)   
+                    else:
+                        print("警告: 没有找到有效的轴承信息")
+             #如果是齿轮箱轴承 则从unit_gearbox_bearings 表中取数据
+            elif 'shaft' in str1 or'input' in str1:
+                print("'shaft'or'input'")
+                # df_sql33 = f"SELECT * FROM unit_gearbox_bearings WHERE gearbox_code = '{gearbox_code}' "
+                # df33 = pd.read_sql(df_sql33, Engine33)
+                #高速轴 低速中间轴 取bearing_rs/gs均可
+                parallel_wheel_grade=1
+                if 'low_speed' in str1:
+                    parallel_wheel_grade =1                
+                elif 'low_speed_intermediate' in str1:
+                    parallel_wheel_grade =2
+                elif 'high_speed' in str1:
+                    parallel_wheel_grade =3    
+                # df_sql33 = f"SELECT * FROM unit_gearbox_bearings WHERE gearbox_code = '{gearbox_code}' "
+                df_sql33 = f"""
+                SELECT bearing_rs_brand, bearing_rs_model, bearing_gs_brand, bearing_gs_model 
+                FROM unit_gearbox_bearings 
+                WHERE gearbox_code = '{gearbox_code}'
+                AND parallel_wheel_grade = '{parallel_wheel_grade}'
+                """
+                df33 = pd.read_sql(df_sql33, Engine)
+                if not df33.empty:
+                    if 'high_speed' in str1 or 'low_speed_intermediate' in str1:
+                        rs_brand = 'bearing_rs' + '_brand'  
+                        rs_model = 'bearing_rs' + '_model'  
+                        gs_brand = 'bearing_gs' + '_brand'  
+                        gs_model = 'bearing_gs' + '_model'  
+                        rs_has_value = not pd.isna(df33[rs_brand].iloc[0]) and not pd.isna(df33[rs_model].iloc[0])     
+                        gs_has_value = not pd.isna(df33[gs_brand].iloc[0]) and not pd.isna(df33[gs_model].iloc[0])    
+                        if rs_has_value and gs_has_value:
+                            brand = rs_brand
+                            model = rs_model
+                        elif rs_has_value:
+                            brand = rs_brand
+                            model = rs_model
+                        elif gs_has_value:
+                            brand = gs_brand
+                            model = gs_model
+                        else:
+                            print("警告: 没有找到有效的品牌信息")
+                            brand = rs_brand 
+                            model = rs_model 
+                    #低速轴 取bearing_model          
+                    elif 'low_speed'in str1:
+                        brand = 'bearing' + '_brand'  
+                        model = 'bearing' + '_model'
+                else:
+                    print("警告: 没有找到有效的轴承信息")     
+                print(brand)
+                _brand = df33[brand].iloc[0]
+                _model = df33[model].iloc[0]
+                print(_brand)
+                print(_model)   
+
+
+        # 4、从表'unit_dict_brand_model'中通过'_brand'、'_model'查找部件的参数信息
+
+        df_sql4 = f"SELECT * FROM unit_dict_brand_model where manufacture = %s AND model_number = %s"
+        params = [(_brand, _model)]
+        df4 = pd.read_sql(df_sql4, Engine, params=params)
+        n_rolls = df4['rolls_number'].iloc[0]
+        d_rolls = df4['rolls_diameter'].iloc[0]
+        D_diameter = df4['circle_diameter'].iloc[0]
+        theta_deg = df4['theta_deg'].iloc[0]
+        result = {
+            "type":'bearing',
+            "n_rolls":round(n_rolls, 2),
+            "d_rolls":round(d_rolls, 2),
+            "D_diameter":round(D_diameter, 2),
+            "theta_deg":round(theta_deg, 2),
+        }
+    # result = json.dumps(result, ensure_ascii=False)
+        return result
+    def calculate_bearing_frequencies(self, n, d, D, theta_deg, rpm):
+        """
+        计算轴承各部件特征频率
+
+        参数:
+        n (int): 滚动体数量
+        d (float): 滚动体直径(单位:mm)
+        D (float): 轴承节圆直径(滚动体中心圆直径,单位:mm)
+        theta_deg (float): 接触角(单位:度)
+        rpm (float): 转速(转/分钟)
+
+        返回:
+        dict: 包含各特征频率的字典(单位:Hz)
+        """
+        # 转换角度为弧度
+        theta = math.radians(theta_deg)
+
+        # 转换直径单位为米(保持单位一致性,实际计算中比值抵消单位影响)
+        # 注意:由于公式中使用的是比值,单位可以保持mm不需要转换
+        ratio = d / D
+
+        # 基础频率计算(转/秒)
+        f_r = rpm / 60.0
+
+        # 计算各特征频率
+        BPFI = n / 2 * (1 + ratio * math.cos(theta)) * f_r  # 内圈故障频率
+        BPFO = n / 2 * (1 - ratio * math.cos(theta)) * f_r  # 外圈故障频率
+        BSF = (D / (2 * d)) * (1 - (ratio ** 2) * (math.cos(theta) ** 2)) * f_r  # 滚动体故障频率
+        FTF = 0.5 * (1 - ratio * math.cos(theta)) * f_r  # 保持架故障频率
+
+        return {
+            "BPFI": round(BPFI, 2),
+            "BPFO": round(BPFO, 2),
+            "BSF": round(BSF, 2),
+            "FTF": round(FTF, 2),
+
+        }
+    
+    #检查返回结果是否有nan 若有,则替换成none
+    def replace_nan(self, obj):
+        if isinstance(obj, dict):
+            return {k: self.replace_nan(v) for k, v in obj.items()}
+        elif isinstance(obj, list):
+            return [self.replace_nan(item) for item in obj]
+        elif isinstance(obj, float) and math.isnan(obj):
+            return None
+        return obj
+
+if __name__ == "__main__":
+    # table_name = "SKF001_wave"
+    # ids = [67803,67804]
+    # fmin, fmax = None, None
+
+    cms = CMSAnalyst(fmin, fmax,table_name,ids)
+    time_domain = cms.time_domain()
+    # print(time_domain)
+
+'''
+    trace = go.Scatter(
+        x=time_domain['x'],
+        y=time_domain['y'],
+        mode="lines",
+        name=time_domain['title'],
+    )
+    layout = go.Layout(
+        title= time_domain['title'],
+        xaxis=dict(title=time_domain["xaxis"]),
+        yaxis=dict(title=time_domain["yaxis"]),
+    )
+    fig = go.Figure(data=[trace], layout=layout)
+    fig.show()
+'''
+
+    # data_path_lsit = ["test1.csv", "test2.csv"]
+    # trend_analysis_test = cms.trend_analysis(data_path_lsit, fmin, fmax)
+    # print(trend_analysis_test)