chenhongyan1989 11 månader sedan
förälder
incheckning
038f3682bd

+ 0 - 1
.gitignore

@@ -43,7 +43,6 @@ codegen/
 **/build/
 **/build/
 **/dist/
 **/dist/
 **/log/
 **/log/
-./log/
 
 
 **/*.egg-info/
 **/*.egg-info/
 **/__pycache__/
 **/__pycache__/

+ 0 - 40
appService/README.MD

@@ -1,40 +0,0 @@
-# 服务安装
-
-## Windows服务安装(Windows操作系统)
-    1. 创建应用程序,执行命令:  pyinstaller --onefile app.py
-    2. 安装服务,在PowerShell中,执行命令:New-Service -Name MyService -BinaryPathName "E:\WorkSpace\SourceCode\WTOAAM\appService\dist\app.exe"
-   
-## Deamon服务安装(Linux操作系统)
-    1. 创建应用程序,执行命令:  pyinstaller --onefile app.py
-    2. 编写 Systemd 服务单元文件:创建一个以 .service 为后缀的 Systemd 服务单元文件,该文件包含了关于你的服务的配置信息。通常这些文件存放在 /etc/systemd/system/ 目录下。例如,创建一个名为 mydaemon.service 的服务单元文件,内容类似于:
-    [Unit]
-    Description=My Daemon Service
-    After=network.target
-
-    [Service]
-    Type=simple
-    ExecStart=/path/to/your/daemon/executable
-    Restart=always
-
-    [Install]
-    WantedBy=multi-user.target
-
-    其中:
-    Description:描述服务的简短说明。
-    After:指定服务应该在哪些其他服务之后启动。
-    Type:指定服务的类型,可以是 simple、forking、oneshot、dbus 等。
-    ExecStart:指定服务启动时执行的命令或可执行文件的路径。
-    Restart:指定服务在失败或意外终止后是否应该自动重启。
-    WantedBy:指定服务应该在何时启动。常见的是 multi-user.target,表示在系统引导时启动。
-
-    3. 启用和启动服务:通过执行以下命令启用和启动服务:
-    sudo systemctl enable mydaemon.service
-    sudo systemctl start mydaemon.service
-
-    4. 停止和重启服务:你可以使用 systemctl 命令停止和重启服务:
-    sudo systemctl stop mydaemon.service   # 停止服务
-    sudo systemctl restart mydaemon.service   # 重启服务
-
-    5. 查看服务状态:你可以使用 systemctl status 命令来查看服务的状态和相关信息:
-    systemctl status mydaemon.service
-

+ 0 - 4
appService/__init__.py

@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from . import service
-__all__=['service']

+ 0 - 60
appService/app.py

@@ -1,60 +0,0 @@
-import sys
-import time
-import threading
-import servicemanager
-from argparse import ArgumentParser
-from appService.logUtil import LogUtil
-
-logUtil=LogUtil()
-logger=logUtil.getLogger()
-
-def parse_args():
-    logger.debug("test2")
-    parser = ArgumentParser(description="Run as a service.")
-    parser.add_argument("action", choices=["start", "stop", "restart", "status"],default="start")
-    parser.add_argument("--type", choices=["daemon", "service"], default="service")
-    logger.debug("test3")
-    return parser.parse_args()
-
-def main():               
-    if sys.platform != "win32" :
-        from appService.service.daemonService import DaemonService
-        args = parse_args()
-
-        daemon_service = DaemonService()
-        if args.action == "start":
-            daemon_service.start()
-        elif args.action == "stop":
-            daemon_service.stop()
-        elif args.action == "status":
-            daemon_service.status()
-
-    if sys.platform == "win32":
-        from appService.service.winService import WinService,CommandLine
-
-        servicemanager.Initialize()
-        servicemanager.PrepareToHostSingle(WinService)
-        servicemanager.StartServiceCtrlDispatcher()
-
-    # if args.type == "service":
-    #     from appService.service.winService import WinService,CommandLine
-        
-    #     if len(sys.argv) == 1:
-    #         servicemanager.Initialize()
-    #         servicemanager.PrepareToHostSingle(WinService)
-    #         servicemanager.StartServiceCtrlDispatcher()
-    #     else:
-    #         CommandLine(WinService)
-    # elif args.type == "daemon":
-    #     from appService.service.daemonService import DaemonService
-
-    #     daemon_service = DaemonService()
-    #     if args.action == "start":
-    #         daemon_service.start()
-    #     elif args.action == "stop":
-    #         daemon_service.stop()
-    #     elif args.action == "status":
-    #         daemon_service.status()
-
-if __name__ == "__main__":
-    main()

+ 0 - 37
appService/app.spec

@@ -1,37 +0,0 @@
-# -*- mode: python ; coding: utf-8 -*-
-
-
-a = Analysis(
-    ['app.py'],
-    pathex=[],
-    binaries=[],
-    datas=[],
-    hiddenimports=[],
-    hookspath=[],
-    hooksconfig={},
-    runtime_hooks=[],
-    excludes=[],
-    noarchive=False,
-)
-pyz = PYZ(a.pure)
-
-exe = EXE(
-    pyz,
-    a.scripts,
-    a.binaries,
-    a.datas,
-    [],
-    name='app',
-    debug=False,
-    bootloader_ignore_signals=False,
-    strip=False,
-    upx=True,
-    upx_exclude=[],
-    runtime_tmpdir=None,
-    console=True,
-    disable_windowed_traceback=False,
-    argv_emulation=False,
-    target_arch=None,
-    codesign_identity=None,
-    entitlements_file=None,
-)

+ 0 - 0
appService/create


+ 0 - 20
appService/logUtil.py

@@ -1,20 +0,0 @@
-import logging
-from logging.handlers import RotatingFileHandler
-
-class LogUtil:
-    def __init__(self):
-        # 配置logging
-        logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-
-        # 创建logger对象
-        self.logger = logging.getLogger('example_logger')
-
-        # 创建一个FileHandler来输出所有级别的日志到指定文件
-        log_file = r'./example.log'
-        file_handler = logging.FileHandler(log_file)
-        file_handler.setLevel(logging.DEBUG)  # 设置FileHandler的日志级别为DEBUG,以确保记录所有级别的日志
-        file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
-        self.logger.addHandler(file_handler)
-
-    def getLogger(self):
-        return self.logger

+ 0 - 0
appService/service/__init__.py


+ 0 - 28
appService/service/daemonService.py

@@ -1,28 +0,0 @@
-import sys
-import time
-import threading
-import pwd
-import signal
-
-class DaemonService:
-    def __init__(self):
-        self.stop_event = threading.Event()
-
-    def run(self):
-        while not self.stop_event.is_set():
-            print("Daemon is running...")
-            time.sleep(5)
-
-    def start(self):
-        self.stop_event.clear()
-        thread = threading.Thread(target=self.run)
-        thread.start()
-
-    def stop(self):
-        self.stop_event.set()
-
-    def status(self):
-        if self.stop_event.is_set():
-            print("Daemon is not running.")
-        else:
-            print("Daemon is running.")

+ 0 - 58
appService/service/winService.py

@@ -1,58 +0,0 @@
-import sys
-import time
-import threading
-import win32event
-import win32service
-import win32serviceutil
-from appService.logUtil import LogUtil
-
-logUtil=LogUtil()
-logger=logUtil.getLogger()
-
-def CommandLine(classService):
-    win32serviceutil.HandleCommandLine(classService)
-
-class WinService(win32serviceutil.ServiceFramework):
-    _svc_name_ = "MyService"
-    _svc_display_name_ = "My Service"
-    _svc_description_ = "This is a sample service."
-
-    def __init__(self, args):
-        logger.info("execute init")
-        try:
-            win32serviceutil.ServiceFramework.__init__(self, args)
-            self.stop_event = win32event.CreateEvent(None, 0, 0, None)
-        except Exception as ex:
-            logger.error(ex)
-
-    def SvcStop(self):
-        self.stop()
-
-    def SvcDoRun(self):
-        self.start()
-
-    def main(self):
-        while self.is_running:
-            logger.info("Service is running...")
-            time.sleep(10)
-
-    def start(self):
-        logger.info("execute start")
-        try:
-            self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
-            self.is_running = True
-            self.ReportServiceStatus(win32service.SERVICE_RUNNING)
-            self.main()
-        except Exception as ex:
-            logger.error(ex)
-
-    def stop(self):
-        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-        self.is_running = False
-        win32event.SetEvent(self.stop_event)
-
-    def status(self):
-        if self.is_running:
-            print("Service is running.")
-        else:
-            print("Service is not running.")

+ 0 - 100
conf_template/conf_powerfarm_template.json

@@ -1,100 +0,0 @@
-{
-    "name_PowerFarm": "长清风电场",
-    "rated_Power_Turbine_Unit_kW": 2000,
-    "rated_WindSpeed": 11,
-    "rotor_diameter": 105.0,
-    "rated_cut_in_windspeed":3, 
-    "rated_cut_out_windspeed":25,
-    "rotational_Speed_Ratio": 117.91,
-    "name_Output": "second level",
-    "time_Period_Unit_Second": 600,
-    "turbineInfoFilePathCSV": "F:/大生科技/数据/高本山10分钟数据/机组信息_风电场_gbs.csv",
-    "turbineGuaranteedPowerCurveFilePathCSV": "F:/大生科技/数据/高本山10分钟数据/curv_power_hetong_gbs.csv",
-    "inputFileDirectoryByCSV": "F:/大生科技/数据/高本山10分钟数据/gaobenshan-10min/",
-    "csvFileNameSplitStringForTurbine": ".csv",
-    "index_turbine": 0,
-    "filter": {
-        "filter_value_state_turbine": null,
-        "speed_wind_cut_in": 3,
-        "speed_wind_cut_out": 25,
-        "angle_pitch_min": 2,
-        "angle_pitch_max": null,
-        "active_power_min": 39,
-        "active_power_max": 2000,
-        "speed_generator_min": null,
-        "speed_generator_max": null,
-        "activePowerAvailable": [
-            1
-        ]
-    },
-    "graphSets": {
-        "generatorSpeed": {
-            "step": 200,
-            "min": 1000,
-            "max": 2000
-        },
-        "generatorTorque": {
-            "step": 2000,
-            "min": 0,
-            "max": 12000
-        },
-        "cp": {
-            "step": 0.5,
-            "min": 0,
-            "max": 2
-        },
-        "tsr": {
-            "step": 5,
-            "min": 0,
-            "max": 30
-        },
-        "pitchAngle": {
-            "step": 2,
-            "min": -1,
-            "max": 30
-        },
-        "activePower": {
-            "step": 250,
-            "min": 0,
-            "max": 2000
-        },
-        "generatorTemperature": {
-            "step": 10,
-            "min": -40,
-            "max": 100
-        }
-    },
-    "outputFileDirectory": "output",
-    "skip_row_number": 0,
-    "density_air": 1.222,
-    "name_Type_For_Analysis": "数据完整度",
-    "date_Begin": "2023-02-01 00:00:00",
-    "date_End": "2023-12-31 23:59:59",
-    "excludingMonths": null,
-    "turbine_Time": "时间",
-    "turbine_Name": null,
-    "speed_Wind": "30秒平均风速",
-    "power_Active": "有功功率",
-    "pitch_Angle1": "桨距角1",
-    "pitch_Angle2": "桨距角2",
-    "pitch_Angle3": "桨距角3",
-    "state_Turbine": null,
-    "speed_Generator": "发电机转速",
-    "speed_Rotor": "风轮转速",
-    "torque": null,
-    "direction_Wind": "60秒平均风向角",
-    "angle_included": "风向角",
-    "nacelle_Pos": "机舱位置",
-    "temperature_Env": "舱外温度",
-    "temperature_Nacelle": "舱内温度",
-    "Cabin_Vibrate_X": "机舱横向(左右)振动值(RMS)",
-    "Cabin_Vibrate_Y": "机舱前后(俯仰)振动值(RMS)",
-    "activePowerSet": "有功功率设定值",
-    "activePowerAvailable": "功率曲线可用",
-    "temperature_large_components": "低速轴承温度,高速轴承温度,齿轮箱入口油温,驱动前轴承温度,自由后轴承温度,发电机定子U温度",
-    "temperature_Generator": {
-        "yAxisDE": "低速轴承温度",
-        "yAxisNDE": "高速轴承温度",
-        "diffTemperature": "舱内温度"
-    }
-}

+ 0 - 152
conf_template/conf_template.json

@@ -1,152 +0,0 @@
-[
-    {
-        "configFilePath": "conf/conf_powerfarm_template.json",
-        "configAnalysis": [
-            {
-                "package": "algorithm.cabinVibrateAnalyst",
-                "className": "CabinVibrateAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.windSpeedFrequencyAnalyst",
-                "className": "WindSpeedFrequencyAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.windDirectionFrequencyAnalyst",
-                "className": "WindDirectionFrequencyAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.windRoseOfTurbine",
-                "className": "WinRoseOfTurbineAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.temperatureLargeComponentsAnalyst",
-                "className": "TemperatureLargeComponentsAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.temperatureEnvironmentAnalyst",
-                "className": "TemperatureEnvironmentAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.generatorSpeedPowerAnalyst",
-                "className": "GeneratorSpeedPowerAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.generatorSpeedTorqueAnalyst",
-                "className": "GeneratorSpeedTorqueAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.pitchPowerAnalyst",
-                "className": "PitchPowerAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.pitchGeneratorSpeedAnalyst",
-                "className": "PitchGeneratorSpeedAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.dataIntegrityOfMinuteAnalyst",
-                "className": "DataIntegrityOfMinuteAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.powerCurveAnalyst",
-                "className": "PowerCurveAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.ratedPowerWindSpeedAnalyst",
-                "className": "RatedPowerWindSpeedAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.ratedWindSpeedAnalyst",
-                "className": "RatedWindSpeedAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.powerScatter2DAnalyst",
-                "className": "PowerScatter2DAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.powerScatterAnalyst",
-                "className": "PowerScatterAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.windSpeedAnalyst",
-                "className": "WindSpeedAnalyst",
-                "methodName": "executeAnalysis"
-            },            
-            {
-                "package": "algorithm.pitchPowerWindSpeedAnalyst",
-                "className": "PitchPowerWindSpeedAnalyst",
-                "methodName": "executeAnalysis"
-            },            
-            {
-                "package": "algorithm.dataIntegrityOfSecondAnalyst",
-                "className": "DataIntegrityOfSecondAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.yawErrorAnalyst",
-                "className": "YawErrorAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.minPitchAnalyst",
-                "className": "MinPitchAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.cpAnalyst",
-                "className": "CpAnalyst",
-                "methodName": "executeAnalysis"
-            },
-			{
-				"package": "algorithm.cpWindSpeedAnalyst",
-				"className": "CpWindSpeedAnalyst",
-				"methodName": "executeAnalysis"
-			},
-            {
-                "package": "algorithm.cpTrendAnalyst",
-                "className": "CpTrendAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.tsrAnalyst",
-                "className": "TSRAnalyst",
-                "methodName": "executeAnalysis"
-            },
-			{
-				"package": "algorithm.tsrWindSpeedAnalyst",
-				"className": "TSRWindSpeedAnalyst",
-				"methodName": "executeAnalysis"
-			},
-            {
-                "package": "algorithm.tsrTrendAnalyst",
-                "className": "TSRTrendAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.powerOscillationAnalyst",
-                "className": "PowerOscillationAnalyst",
-                "methodName": "executeAnalysis"
-            },
-            {
-                "package": "algorithm.pitchTSRCpAnalyst",
-                "className": "PitchTSRCpAnalyst",
-                "methodName": "executeAnalysis"
-            }
-        ]
-    }
-]

+ 0 - 21
dataAnalysisBehavior/behavior/analystExcludeRatedPower.py

@@ -1,21 +0,0 @@
-from .analyst import Analyst
-import os
-import pandas as pd
-import numpy as np
-from utils.directoryUtil import DirectoryUtil as dir
-from algorithmContract.confBusiness import *
-
-
-class AnalystExcludeRatedPower(Analyst):
-    def filterCommon(self,dataFrame:pd.DataFrame, confData:ConfBusiness):
-        dataFrame=super().filterCommon(dataFrame,confData)
-
-        if not self.common.isNone(confData.field_power) and self.node_active_power_max in confData.filter and not self.common.isNone(confData.filter[self.node_active_power_max]) \
-                and not self.common.isNone(confData.field_pitch_angle1) and self.node_angle_pitch_min in confData.filter and not self.common.isNone(confData.filter[self.node_angle_pitch_min]):
-            activePowerMax = float(confData.filter[self.node_active_power_max])
-            anglePitchMin = float(confData.filter[self.node_angle_pitch_min])
-
-            dataFrame = dataFrame[~((dataFrame[confData.field_power] >= activePowerMax*0.9) & (
-                dataFrame[confData.field_power] <= activePowerMax*1.2))]
-        
-        return dataFrame

+ 23 - 4
dataAnalysisBusiness/algorithm/dataIntegrityOfSecondAnalyst.py

@@ -130,17 +130,36 @@ class DataIntegrityOfSecondAnalyst(AnalystNotFilter):
             x_axis_title = "机组"
             x_axis_title = "机组"
             y_axis_title = "日期"
             y_axis_title = "日期"
 
 
+        # # 创建热图
+        # fig = go.Figure(data=go.Heatmap(
+        #     z=result.values,
+        #     x=x_labels,
+        #     y=y_labels,
+        #     colorscale='Viridis',
+        #     # colorbar=dict(title='数据完整度%'),
+        #     showscale=False,  # 显示颜色条
+        #     text=result.values,
+        #     texttemplate="%{text}",  # Format the text display inside cells
+        #     # hoverinfo='text'
+        # ))
         # 创建热图
         # 创建热图
         fig = go.Figure(data=go.Heatmap(
         fig = go.Figure(data=go.Heatmap(
             z=result.values,
             z=result.values,
             x=x_labels,
             x=x_labels,
             y=y_labels,
             y=y_labels,
-            colorscale='Viridis',
-            # colorbar=dict(title='数据完整度%'),
-            showscale=False,  # 显示颜色条
+            colorscale=[
+                [0.0, 'rgb(255, 102, 102)'],  # 柔和的红色
+                [0.5, 'rgb(255, 102, 102)'],
+                [0.5, 'rgb(255, 255, 153)'],  # 柔和的黄色
+                [0.85, 'rgb(255, 255, 153)'],
+                [0.85, 'rgb(153, 255, 153)'],  # 柔和的绿色
+                [1.0, 'rgb(153, 255, 153)']
+            ],
+            zmin=0,  # 设置颜色范围的最小值
+            zmax=100,  # 设置颜色范围的最大值
+            showscale=True,  # 显示颜色条
             text=result.values,
             text=result.values,
             texttemplate="%{text}",  # Format the text display inside cells
             texttemplate="%{text}",  # Format the text display inside cells
-            # hoverinfo='text'
         ))
         ))
 
 
         # 更新图形布局
         # 更新图形布局

+ 0 - 411
dataAnalysisBusiness/algorithm/dataMarker.py

@@ -1,411 +0,0 @@
-import os
-import re
-import pandas as pd
-import numpy as np
-import matplotlib.pyplot as plt
-from matplotlib.pyplot import MultipleLocator
-import math
-import pdb
-from algorithmContract.confBusiness import *  #将这个包里的全部加载
-
-intervalPower = 25
-intervalWindspeed = 0.25
-
-class DataMarker:
-    
-    #选取时间、风速、功率数据
-    def preprocessData(self,dataFrame:pd.DataFrame,confData:ConfBusiness):
-        timeStamp = dataFrame[confData.field_turbine_time]
-        activePower = dataFrame[confData.field_power]
-        windSpeed = dataFrame[confData.field_wind_speed]
-        dataFramePartOfSCADA = pd.concat([timeStamp,activePower,windSpeed], axis=1)
-        return dataFramePartOfSCADA
-    
-    #计算分仓数目
-    def calculateIntervals(self,activePowerMax, ratedPower, windSpeedCutOut):
-        binNumOfPower = math.floor((activePowerMax) / intervalPower) + 1 if (activePowerMax) >= ratedPower else math.floor(ratedPower / intervalPower)
-        binNumOfWindSpeed = math.ceil(windSpeedCutOut / intervalWindspeed)
-        return binNumOfPower, binNumOfWindSpeed
-
-    def calculateTopP(self,activePowerMax,ratedPower):
-        
-        TopP = 0   
-        if activePowerMax >= ratedPower: 
-            TopP = math.floor((activePowerMax - ratedPower) / intervalPower) + 1  
-        else:  
-            TopP = 0
-        return TopP
-
-    def chooseData(self,dataFramePartOfSCADA,dataFrame:pd.DataFrame,confData:ConfBusiness):
-        
-        # 初始化标签列
-        SM1 = dataFramePartOfSCADA.shape 
-        AA1 = SM1[0]  
-        lab = [[0] for _ in range(AA1)]
-        lab = pd.DataFrame(lab,columns=['lab'])
-        dataFramePartOfSCADA = pd.concat([dataFramePartOfSCADA,lab],axis=1)  #在tpv后加一列标签列
-        dataFramePartOfSCADA = dataFramePartOfSCADA.values
-        SM = dataFramePartOfSCADA.shape #(52561,4)
-        AA = SM[0] 
-        nCounter1 = 0 
-        DzMarch809_0 = np.zeros((AA, 3)) 
-        Point_line = np.zeros(AA, dtype=int)  
-        APower = dataFrame[confData.field_power].values
-        WSpeed = dataFrame[confData.field_wind_speed].values
-
-        for i in range(AA):
-            if (APower[i] > 10.0) & (WSpeed[i] > 0.0):
-                nCounter1 += 1  
-                DzMarch809_0[nCounter1-1, 0] = WSpeed[i]  
-                DzMarch809_0[nCounter1-1, 1] = APower[i] 
-                Point_line[nCounter1-1] = i+1  
-            if APower[i] <= 10: 
-                dataFramePartOfSCADA[i,SM[1]-1] = -1
-                
-            DzMarch809 = DzMarch809_0[:nCounter1, :] 
-            
-        return DzMarch809,nCounter1,dataFramePartOfSCADA,Point_line,SM
-
-    def gridCount(self,binNumOfWindSpeed,binNumOfPower,nCounter1,DzMarch809):  
-        # 遍历有效数据
-        XBoxNumber = np.ones((binNumOfPower, binNumOfWindSpeed),dtype=int) 
-        for i in range(nCounter1):             
-            for m in range(1, binNumOfPower + 1):  
-                if (DzMarch809[i,1] > (m - 1) * intervalPower) and (DzMarch809[i,1] <= m * intervalPower):  
-                    nWhichP = m  
-                    break  
-            for n in range(1, binNumOfWindSpeed + 1):  
-                if (DzMarch809[i, 0] > (n - 1) * intervalWindspeed) and (DzMarch809[i, 0] <= n * intervalWindspeed):  
-                    nWhichV = n  
-                    break  
-            if (nWhichP > 0) and (nWhichV > 0):  
-                XBoxNumber[nWhichP - 1][nWhichV - 1] += 1
-        for m in range(1,binNumOfPower+1):
-            for n in range(1,binNumOfWindSpeed+1):
-                XBoxNumber[m-1,n-1] = XBoxNumber[m-1,n-1] - 1
-        
-        return XBoxNumber
-
-    def percentageDots(self,XBoxNumber, binNumOfPower, binNumOfWindSpeed,axis):
-        
-        BoxPercent = np.zeros((binNumOfPower, binNumOfWindSpeed), dtype=float)     
-        BinSum = np.zeros((binNumOfPower if axis == 'power' else binNumOfWindSpeed, 1), dtype=int)
-        for i in range(1,1+(binNumOfPower if axis == 'power' else binNumOfWindSpeed)):
-            for m in range(1,(binNumOfWindSpeed if axis == 'power' else binNumOfPower)+1):  
-                BinSum[i-1] = BinSum[i-1] + (XBoxNumber[i-1,m-1] if axis == 'power' else XBoxNumber[m-1,i-1])
-            for m in range(1,(binNumOfWindSpeed if axis == 'power' else binNumOfPower)+1):  
-                if BinSum[i-1]>0:
-                    if axis == 'power':
-                        BoxPercent[i-1,m-1] = (XBoxNumber[i-1,m-1] / BinSum[i-1])*100
-                    else:
-                        BoxPercent[m-1,i-1] = (XBoxNumber[m-1,i-1] / BinSum[i-1])*100
-                        
-        return BoxPercent,BinSum
-
-    def maxBoxPercentage(self,BoxPercent, binNumOfPower, binNumOfWindSpeed, axis):
-        
-        BoxMaxIndex = np.zeros((binNumOfPower if axis == 'power' else binNumOfWindSpeed,1),dtype = int) 
-        BoxMax = np.zeros((binNumOfPower if axis == 'power' else binNumOfWindSpeed,1),dtype = float)  
-        for m in range(1,(binNumOfPower if axis == 'power' else binNumOfWindSpeed)+1):
-            BoxMaxIndex[m-1] = (np.argmax(BoxPercent[m-1, :])) if axis == 'power' else (np.argmax(BoxPercent[:, m-1]))
-            BoxMax[m-1] = (np.max(BoxPercent[m-1, :]))if axis == 'power' else (np.max(BoxPercent[:, m-1]))
-
-        return BoxMaxIndex, BoxMax
-
-    def extendBoxPercent(self,m, BoxMax,TopP,BoxMaxIndex,BoxPercent,binNumOfPower,binNumOfWindSpeed):
-        
-        DotDense = np.zeros(binNumOfPower)  
-        DotDenseLeftRight = np.zeros((binNumOfPower,2))
-        DotValve = m 
-        PDotDenseSum = 0
-        for i in range(binNumOfPower - TopP):
-            PDotDenseSum = BoxMax[i] 
-            iSpreadRight = 1  
-            iSpreadLeft = 1         
-            while PDotDenseSum < DotValve:  
-                if (BoxMaxIndex[i] + iSpreadRight) < binNumOfWindSpeed-1-1:  
-                    PDotDenseSum += BoxPercent[i, BoxMaxIndex[i] + iSpreadRight] 
-                    iSpreadRight += 1  
-                else:
-                    break             
-                if (BoxMaxIndex[i] - iSpreadLeft) > 0:  
-                    PDotDenseSum += BoxPercent[i, BoxMaxIndex[i] - iSpreadLeft] 
-                    iSpreadLeft += 1  
-                else:  
-                    break  
-            iSpreadRight = iSpreadRight-1
-            iSpreadLeft = iSpreadLeft-1
-        
-            DotDenseLeftRight[i, 0] = iSpreadLeft 
-            DotDenseLeftRight[i, 1] = iSpreadRight 
-            DotDense[i] = iSpreadLeft + iSpreadRight + 1    
-
-        return DotDenseLeftRight
-
-    def calculatePWidth(self,binNumOfPower,TopP,DotDenseLeftRight,PBinSum):
-        
-
-        PowerLimit = np.zeros(binNumOfPower, dtype=int)  
-        WidthAverage = 0    
-        WidthAverage_L = 0 
-        nCounter = 0  
-        PowerLimitValve = 6    
-        N_Pcount = 20  
-        for i in range(binNumOfPower - TopP):   
-            if (DotDenseLeftRight[i, 1] > PowerLimitValve) and (PBinSum[i] > N_Pcount):  
-                PowerLimit[i] = 1  
-            
-            if DotDenseLeftRight[i, 1] <= PowerLimitValve:  
-                WidthAverage += DotDenseLeftRight[i, 1]
-                WidthAverage_L += DotDenseLeftRight[i,1] 
-                nCounter += 1  
-        WidthAverage /= nCounter if nCounter > 0 else 1  
-        WidthAverage_L /= nCounter if nCounter > 0 else 1   
-
-        return WidthAverage, WidthAverage_L,PowerLimit
-
-    def amendMaxBox(self,binNumOfPower,TopP,PowerLimit,BoxMaxIndex):
-        
-
-        for i in range(1, binNumOfPower - TopP+1):  
-            if (PowerLimit[i] == 1) and (abs(BoxMaxIndex[i] - BoxMaxIndex[i - 1]) > 5):  
-                BoxMaxIndex[i] = BoxMaxIndex[i - 1] + 1  
-
-        return BoxMaxIndex
-
-    def markBoxLimit(self,binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,CurveWidthL,BoxMaxIndex):
-        
-        BBoxRemove = np.zeros((binNumOfPower, binNumOfWindSpeed), dtype=int)  
-        for m in range(binNumOfPower - TopP): 
-            for n in range(int(BoxMaxIndex[m]) + int(CurveWidthR), binNumOfWindSpeed):
-                BBoxRemove[m, n] = 1  
-            for n in range(int(BoxMaxIndex[m]) - int(CurveWidthL)+1, 0, -1):   
-                BBoxRemove[m, n-1] = 2 
-        return BBoxRemove
-
-    def markBoxPLimit(self,binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,PowerLimit,BoxPercent,BoxMaxIndex,mm_value:int,BBoxRemove,nn_value:int):
-        
-        BBoxLimit = np.zeros((binNumOfPower, binNumOfWindSpeed), dtype=int)  
-        for i in range(2, binNumOfPower - TopP):  
-            if PowerLimit[i] == 1:
-                BBoxLimit[i, int(BoxMaxIndex[i] + CurveWidthR + 1):binNumOfWindSpeed] = 1
-        IsolateValve = 3
-        for m in range(binNumOfPower - TopP):    
-            for n in range(int(BoxMaxIndex[m]) + int(CurveWidthR), binNumOfWindSpeed):    
-                if BoxPercent[m, n] < IsolateValve:   
-                    BBoxRemove[m, n] = 1
-
-        for m in range(binNumOfPower - TopP, binNumOfPower):   
-            for n in range(binNumOfWindSpeed):  
-                BBoxRemove[m, n] = 3
-        
-        # 标记功率主带拐点左侧的欠发网格  
-        for m in range(mm_value - 1, binNumOfPower - TopP): 
-            for n in range(int(nn_value) - 2):
-                BBoxRemove[m, n] = 2
-        
-        return BBoxLimit
-        
-    def markData(self,binNumOfPower, binNumOfWindSpeed,DzMarch809,BBoxRemove,nCounter1):
-        
-        DzMarch809Sel = np.zeros(nCounter1, dtype=int)
-        nWhichP = 0  
-        nWhichV = 0  
-        for i in range(nCounter1):   
-            for m in range( binNumOfPower ):   
-                if ((DzMarch809[i,1])> m * intervalPower) and ((DzMarch809[i,1]) <= (m+1) * intervalPower):  
-                    nWhichP = m  #m记录的是index
-                    break  
-            for n in range( binNumOfWindSpeed ):    
-                if DzMarch809[i,0] > ((n+1) * intervalWindspeed - intervalWindspeed/2) and DzMarch809[i,0] <= ((n+1) * intervalWindspeed + intervalWindspeed / 2):  
-                    nWhichV = n 
-                    break  
-            if nWhichP >= 0 and nWhichV >= 0:  
-                if BBoxRemove[nWhichP, nWhichV] == 1:   
-                    DzMarch809Sel[i] = 1  
-                elif BBoxRemove[nWhichP, nWhichV] == 2:  
-                    DzMarch809Sel[i] = 2  
-                elif BBoxRemove[nWhichP , nWhichV] == 3:  
-                    DzMarch809Sel[i] = 0  
-
-        return DzMarch809Sel
-        
-
-    def windowFilter(self,nCounter1,ratedPower,DzMarch809,DzMarch809Sel,Point_line):
-        
-
-        PVLimit = np.zeros((nCounter1, 3)) 
-        nLimitTotal = 0  
-        nWindowLength = 6  
-        LimitWindow = np.zeros(nWindowLength)
-        UpLimit = 0   
-        LowLimit = 0  
-        PowerStd = 30  
-        nWindowNum = np.floor(nCounter1/nWindowLength)
-        PowerLimitUp = ratedPower - 100  
-        PowerLimitLow = 100  
-
-        # 循环遍历每个窗口  
-        for i in range(int(nWindowNum)):  
-            start_idx = i * nWindowLength  
-            end_idx = start_idx + nWindowLength  
-            LimitWindow = DzMarch809[start_idx:end_idx, 1]  
-            
-            bAllInAreas = np.all(LimitWindow >= PowerLimitLow) and np.all(LimitWindow <= PowerLimitUp)  
-            if not bAllInAreas:  
-                continue  
-            
-            UpLimit = LimitWindow[0] + PowerStd  
-            LowLimit = LimitWindow[0] - PowerStd  
-            
-            bAllInUpLow = np.all(LimitWindow >= LowLimit) and np.all(LimitWindow <= UpLimit)  
-            if bAllInUpLow: 
-                DzMarch809Sel[start_idx:end_idx] = 4  
-    
-                for j in range(nWindowLength):  
-                    PVLimit[nLimitTotal, :2] = DzMarch809[start_idx + j, :2]  
-                    PVLimit[nLimitTotal, 2] = Point_line[start_idx + j]  # 对数据进行标识  
-                    nLimitTotal += 1  
-        return PVLimit,nLimitTotal
-
-    def store_points(self,DzMarch809, DzMarch809Sel,Point_line, nCounter1):  
-          
-        PVDot = np.zeros((nCounter1, 3))
-        PVBad = np.zeros((nCounter1, 3))  
-
-        nCounterPV = 0  
-        nCounterBad = 0 
-        for i in range(nCounter1):
-            if DzMarch809Sel[i] == 0:   
-                nCounterPV += 1 
-                PVDot[nCounterPV-1, :2] = DzMarch809[i, :2]
-                PVDot[nCounterPV-1, 2] = Point_line[i]  
-            elif DzMarch809Sel[i] in [1, 2, 3]:  
-                nCounterBad += 1  
-                PVBad[nCounterBad-1, :2] = DzMarch809[i, :2]  
-                PVBad[nCounterBad-1, 2] = Point_line[i]
-                    
-        return PVDot, nCounterPV,PVBad,nCounterBad  
-
-    def markAllData(self,nCounterPV,nCounterBad,dataFramePartOfSCADA,PVDot,PVBad,SM,nLimitTotal,PVLimit):
-
-        for i in range(nCounterPV):
-            dataFramePartOfSCADA[int(PVDot[i, 2] - 1), (SM[1]-1)] = 1   
-        #坏点  
-        for i in range(nCounterBad):  
-            dataFramePartOfSCADA[int(PVBad[i, 2] - 1),(SM[1]-1)] = 5  # 坏点标识  
-
-        # 对所有数据中的限电点进行标注   
-        for i in range(nLimitTotal):  
-            dataFramePartOfSCADA[int(PVLimit[i, 2] - 1),(SM[1]-1)] = 4  # 限电点标识  
-
-        return dataFramePartOfSCADA
-    
-    # 4. 数据可视化
-    def plotData(self,turbineName:str,ws:list, ap:list):
-        fig = plt.figure()
-        plt.scatter(ws, ap, s=1, c='black', marker='.')
-        ax = plt.gca()
-        ax.xaxis.set_major_locator(MultipleLocator(5))
-        ax.yaxis.set_major_locator(MultipleLocator(500))
-        plt.title(turbineName)
-        plt.xlim((0, 30))
-        plt.ylim((0, 2200))
-        plt.tick_params(labelsize=8)
-        plt.xlabel("V/(m$·$s$^{-1}$)", fontsize=8)
-        plt.ylabel("P/kW", fontsize=8)
-        plt.show()
-    
-    
-    def main(self,confData: ConfBusiness,dataFrame:pd.DataFrame):
-        dataFramePartOfSCADA = self.preprocessData(dataFrame, confData)
-        powerMax = dataFrame[confData.field_power].max()
-
-        binNumOfPower, binNumOfWindSpeed = self.calculateIntervals(powerMax,confData.rated_power,confData.rated_cut_out_windspeed)
-        TopP = self.calculateTopP(powerMax,confData.rated_power)
-        # 根据功率阈值对数据进行标签分配
-        DzMarch809,nCounter1,dataFramePartOfSCADA,Point_line,SM = self.chooseData(dataFramePartOfSCADA,dataFrame,confData)
-        XBoxNumber = self.gridCount(binNumOfWindSpeed,binNumOfPower,nCounter1,DzMarch809)
-        PBoxPercent,PBinSum = self.percentageDots(XBoxNumber, binNumOfPower, binNumOfWindSpeed, 'power')
-        VBoxPercent,VBinSum = self.percentageDots(XBoxNumber, binNumOfPower, binNumOfWindSpeed, 'speed')
-
-        PBoxMaxIndex, PBoxMaxP = self.maxBoxPercentage(PBoxPercent, binNumOfPower, binNumOfWindSpeed, 'power')
-        VBoxMaxIndex, VBoxMaxV = self.maxBoxPercentage(VBoxPercent, binNumOfPower, binNumOfWindSpeed, 'speed')
-        if PBoxMaxIndex[0] > 14: PBoxMaxIndex[0] = 9
-        DotDenseLeftRight = self.extendBoxPercent(90, PBoxMaxP,TopP,PBoxMaxIndex,PBoxPercent,binNumOfPower,binNumOfWindSpeed)
-        WidthAverage, WidthAverage_L,PowerLimit = self.calculatePWidth(binNumOfPower,TopP,DotDenseLeftRight,PBinSum)
-        PBoxMaxIndex = self.amendMaxBox(binNumOfPower,TopP,PowerLimit,PBoxMaxIndex)
-        # 计算功率主带的左右边界  
-        CurveWidthR = np.ceil(WidthAverage) + 2  
-        CurveWidthL = np.ceil(WidthAverage_L) + 2 
-        #确定功率主带的左上拐点,即额定风速位置的网格索引
-        CurveTop = np.zeros((2, 1), dtype=int)  
-        BTopFind = 0  
-        mm_value = None
-        nn_value = None
-        for m in range(binNumOfPower - TopP, 0, -1):
-            for n in range(int(np.floor(int(confData.rated_cut_in_windspeed) / intervalWindspeed)), binNumOfWindSpeed - 1):   
-                if (VBoxPercent[m, n - 1] < VBoxPercent[m, n]) and (VBoxPercent[m, n] <= VBoxPercent[m, n + 1]) and (XBoxNumber[m, n] >= 3):   
-                    CurveTop[0] = m  
-                    CurveTop[1] = n  #[第80个,第40个]
-                    BTopFind = 1
-                    mm_value = m
-                    nn_value = n
-                    break 
-            if BTopFind == 1:  
-                break 
-        #标记网格
-        BBoxRemove = self.markBoxLimit(binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,CurveWidthL,PBoxMaxIndex)
-        if mm_value is not None and nn_value is not None:
-            BBoxLimit = self.markBoxPLimit(binNumOfPower,binNumOfWindSpeed,TopP,CurveWidthR,PowerLimit,PBoxPercent,PBoxMaxIndex,mm_value,BBoxRemove,nn_value)
-        DzMarch809Sel = self.markData(binNumOfPower, binNumOfWindSpeed,DzMarch809,BBoxRemove,nCounter1)
-        PVLimit,nLimitTotal = self.windowFilter(nCounter1,confData.rated_power,DzMarch809,DzMarch809Sel,Point_line)
-        #将功率滑动窗口主带平滑化
-        nSmooth = 0   
-        for i in range(binNumOfPower - TopP - 1):  
-            PVLeftDown = np.zeros(2)  
-            PVRightUp = np.zeros(2)   
-            if PBoxMaxIndex[i + 1] - PBoxMaxIndex[i] >= 1:  
-                # 计算左下和右上顶点的坐标  
-                PVLeftDown[0] = (PBoxMaxIndex[i]+1 + CurveWidthR) * 0.25 - 0.125  
-                PVLeftDown[1] = (i) * 25  
-                PVRightUp[0] = (PBoxMaxIndex[i+1]+1 + CurveWidthR) * 0.25 - 0.125  
-                PVRightUp[1] = (i+1) * 25  
-                    
-                for m in range(nCounter1):  
-                    # 检查当前点是否在锯齿区域内  
-                    if (DzMarch809[m, 0] > PVLeftDown[0]) and (DzMarch809[m, 0] < PVRightUp[0]) and (DzMarch809[m, 1] > PVLeftDown[1]) and (DzMarch809[m, 1] < PVRightUp[1]):
-                        # 检查斜率是否大于对角连线  
-                        if ((DzMarch809[m, 1] - PVLeftDown[1]) / (DzMarch809[m, 0] - PVLeftDown[0])) > ((PVRightUp[1] - PVLeftDown[1]) / (PVRightUp[0] - PVLeftDown[0])):
-                            # 如果在锯齿左上三角形中,则选中并增加锯齿平滑计数器  
-                            DzMarch809Sel[m] = 0  
-                            nSmooth += 1  
-        # DzMarch809Sel 数组现在包含了锯齿平滑的选择结果,nSmooth 是选中的点数
-        PVDot, nCounterPV,PVBad,nCounterBad = self.store_points(DzMarch809, DzMarch809Sel,Point_line, nCounter1)
-        #标注   
-        dataFramePartOfSCADA = self.markAllData(nCounterPV,nCounterBad,dataFramePartOfSCADA,PVDot,PVBad,SM,nLimitTotal,PVLimit)
-        A = dataFramePartOfSCADA[:,-1]
-        A=pd.DataFrame(A,columns=['lab'])
-
-        dataFrame = pd.concat([dataFrame,A],axis=1) 
-
-        """
-        标识	说明
-        5	坏点
-        4	限功率点
-        1	好点
-        0	null
-        -1	P<=10
-        """
-        print("lab unique :",dataFrame['lab'].unique())
-        # data=dataFrame[dataFrame['lab']==1]        
-        # self.plotData(data[Field_NameOfTurbine].iloc[0],data[confData.field_wind_speed],data[confData.field_power])
-
-        return dataFrame
-        
-
-    if __name__ == '__main__':
-        main()
-
-
-

+ 105 - 3
dataAnalysisBusiness/algorithm/pitchPowerAnalyst.py

@@ -35,7 +35,35 @@ class PitchPowerAnalyst(AnalystWithGoodBadPoint):
 
 
         dataFrameMerge = dataFrame[(dataFrame[Field_ActiverPower] > 0)].sort_values(by=Field_YearMonth)
         dataFrameMerge = dataFrame[(dataFrame[Field_ActiverPower] > 0)].sort_values(by=Field_YearMonth)
         grouped = dataFrameMerge.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
         grouped = dataFrameMerge.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
-
+                # 定义固定的颜色映射列表
+        fixed_colors = [
+                "#3E409C",
+                "#476CB9",
+                "#3586BF",
+                "#4FA4B5",
+                "#52A3AE",
+                "#60C5A3",
+                "#85D0AE",
+                "#A8DCA2",
+                "#CFEE9E",
+                "#E4F39E",
+                "#EEF9A7",
+                "#FBFFBE",
+                "#FDF1A9",
+                "#FFE286",
+                "#FFC475",
+                "#FCB06C",
+                "#F78F4F",
+                "#F96F4A",
+                "#E4574C",
+                "#CA3756",
+                "#AF254F"
+        ]
+
+        # 将 fixed_colors 转换为 Plotly 的 colorscale 格式
+        fixed_colorscale = [
+            [i / (len(fixed_colors) - 1), color] for i, color in enumerate(fixed_colors)
+        ]
         # 遍历每个设备并绘制散点图
         # 遍历每个设备并绘制散点图
         result_rows1 = []
         result_rows1 = []
         for name, group in grouped:
         for name, group in grouped:
@@ -50,7 +78,7 @@ class PitchPowerAnalyst(AnalystWithGoodBadPoint):
                 # marker=dict(color='blue', size=3.5)
                 # marker=dict(color='blue', size=3.5)
                 marker=dict(
                 marker=dict(
                     color=group[Field_UnixYearMonth],
                     color=group[Field_UnixYearMonth],
-                    colorscale='Rainbow',
+                    colorscale=fixed_colorscale,
                     size=3,
                     size=3,
                     opacity=0.7,
                     opacity=0.7,
                     colorbar=dict(
                     colorbar=dict(
@@ -133,7 +161,7 @@ class PitchPowerAnalyst(AnalystWithGoodBadPoint):
             by=Field_YearMonth)
             by=Field_YearMonth)
 
 
         grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
         grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
-
+        '''
         # 遍历每个设备的数据
         # 遍历每个设备的数据
         result_rows2 = []
         result_rows2 = []
         for name, group in grouped:
         for name, group in grouped:
@@ -190,6 +218,80 @@ class PitchPowerAnalyst(AnalystWithGoodBadPoint):
                         itemwidth=80  # Set the width of legend items to 50 pixels
                         itemwidth=80  # Set the width of legend items to 50 pixels
                     )
                     )
                 )
                 )
+        '''
+        # 假设 colorsList 已经在代码的其他部分定义
+        colorsList = [  
+            "#3E409C",
+            "#3586BF",
+            "#52A3AE",
+            "#85D0AE",
+            "#A8DCA2",
+            "#FBFFBE",
+            "#FDF1A9",
+            "#FFE286",
+            "#FCB06C",
+            "#F96F4A",
+            "#E4574C",
+            "#AF254F"
+        ]
+        
+        # 遍历每个设备的数据
+        result_rows2 = []
+        for name, group in grouped:
+            if len(group[Field_YearMonth].unique()) > 1:
+                fig = px.scatter_3d(
+                    group,
+                    x=Field_PitchAngel1,
+                    y=Field_YearMonth,
+                    z=Field_ActiverPower,
+                    color=Field_YearMonth,
+                    color_discrete_sequence=colorsList,  # 使用 colorsList 作为颜色映射
+                    labels={
+                        Field_PitchAngel1: '桨距角',
+                        Field_YearMonth: '时间',
+                        Field_ActiverPower: '功率'
+                    },
+                )
+        
+                # 设置固定散点大小
+                fig.update_traces(marker=dict(size=1.5))
+        
+                # 更新图形的布局
+                fig.update_layout(
+                    title={
+                        "text": f'月度桨距角功率3D散点图: {name[0]}',
+                        "x": 0.5
+                    },
+                    scene=dict(
+                        xaxis=dict(
+                            title='桨距角',
+                            dtick=self.axisStepPitchAngle,
+                            range=[self.axisLowerLimitPitchAngle, self.axisUpperLimitPitchAngle],
+                        ),
+                        yaxis=dict(
+                            title='时间',
+                            tickformat='%Y-%m',  # 日期格式,
+                            dtick='M1',  # 每月一个刻度
+                            showgrid=True,  # 显示网格线
+                        ),
+                        zaxis=dict(
+                            title='功率',
+                            dtick=self.axisStepActivePower,
+                            range=[self.axisLowerLimitActivePower, self.axisUpperLimitActivePower],
+                            showgrid=True,  # 显示网格线
+                        )
+                    ),
+                    scene_camera=dict(
+                        up=dict(x=0, y=0, z=1),  # 保持相机向上方向不变
+                        center=dict(x=0, y=0, z=0),  # 保持相机中心位置不变
+                        eye=dict(x=-1.8, y=-1.8, z=1.2)  # 调整eye属性以实现水平旋转180°
+                    ),
+                    legend=dict(
+                        orientation="h",
+                        itemsizing="constant",  # Use constant size for legend items
+                        itemwidth=80  # Set the width of legend items to 50 pixels
+                    )
+                )
 
 
                 # 保存图像
                 # 保存图像
                 filePathOfHtml = os.path.join(
                 filePathOfHtml = os.path.join(

+ 97 - 1
dataAnalysisBusiness/algorithm/powerScatter2DAnalyst.py

@@ -59,7 +59,7 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
         #     cut_in_ws = dataFrame[Field_CutInWS].min() - 1
         #     cut_in_ws = dataFrame[Field_CutInWS].min() - 1
         # else:
         # else:
         #     cut_in_ws = 2
         #     cut_in_ws = 2
-
+        '''
         # 按设备名分组数据
         # 按设备名分组数据
         grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
         grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
         result_rows = []
         result_rows = []
@@ -122,6 +122,102 @@ class PowerScatter2DAnalyst(AnalystWithGoodBadPoint):
                                          name=f'{datetime.fromtimestamp(month_data["monthIntTime"].iloc[0]).strftime("%Y-%m")}',
                                          name=f'{datetime.fromtimestamp(month_data["monthIntTime"].iloc[0]).strftime("%Y-%m")}',
                                          showlegend=True)
                                          showlegend=True)
                     fig.add_trace(scatter)
                     fig.add_trace(scatter)
+        '''
+        
+        # 按设备名分组数据
+        grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
+        result_rows = []
+
+        # 定义固定的颜色映射列表
+        fixed_colors = [
+                "#3E409C",
+                "#476CB9",
+                "#3586BF",
+                "#4FA4B5",
+                "#52A3AE",
+                "#60C5A3",
+                "#85D0AE",
+                "#A8DCA2",
+                "#CFEE9E",
+                "#E4F39E",
+                "#EEF9A7",
+                "#FBFFBE",
+                "#FDF1A9",
+                "#FFE286",
+                "#FFC475",
+                "#FCB06C",
+                "#F78F4F",
+                "#F96F4A",
+                "#E4574C",
+                "#CA3756",
+                "#AF254F"
+        ]
+
+        # 将 fixed_colors 转换为 Plotly 的 colorscale 格式
+        fixed_colorscale = [
+            [i / (len(fixed_colors) - 1), color] for i, color in enumerate(fixed_colors)
+        ]
+        fixed_colors_points = [
+            "#F96F4A",
+            "#FFC475",
+            "#FBFFBE",
+            "#85D0AE",
+            "#3586BF",
+            "#3E409C"
+
+        ]
+        # 遍历每个设备的数据
+        for name, group in grouped:
+            fig = make_subplots()
+
+            # 提取月份
+            group['month'] = group['monthIntTime'].apply(lambda x: datetime.fromtimestamp(x).month)
+            unique_months = group['month'].unique()
+
+            # 计算时间跨度
+            time_span_months = len(unique_months)
+
+            if time_span_months >= 6:
+                # 绘制散点图(时间跨度大于等于6个月)
+                scatter = go.Scatter(x=group[Field_WindSpeed],
+                                     y=group[Field_ActiverPower],
+                                     mode='markers',
+                                     marker=dict(
+                                         color=group['monthIntTime'],
+                                         colorscale=fixed_colorscale,  # 使用自定义的 colorscale
+                                         size=3,
+                                         opacity=0.7,
+                                         colorbar=dict(
+                                             tickvals=np.linspace(
+                                                 group['monthIntTime'].min(), group['monthIntTime'].max(), 6),
+                                             ticktext=[datetime.fromtimestamp(ts).strftime(
+                                                 '%Y-%m') for ts in np.linspace(group['monthIntTime'].min(), group['monthIntTime'].max(), 6)],
+                                             thickness=18,
+                                             len=1,  # 设置颜色条的长度,使其占据整个图的高度
+                                             outlinecolor='rgba(255,255,255,0)'
+                                         ),
+                                         showscale=True
+                                     ),
+                                     showlegend=False)  # 不显示散点图的 legend,用 colorbar 代替
+                fig.add_trace(scatter)
+            else:
+                # 绘制散点图(时间跨度小于6个月)
+                for i, month in enumerate(unique_months):
+                    month_data = group[group['month'] == month]
+                    # 使用固定的颜色列表
+                    color = fixed_colors_points[i % len(fixed_colors_points)]
+                    scatter = go.Scatter(x=month_data[Field_WindSpeed],
+                                         y=month_data[Field_ActiverPower],
+                                         mode='markers',
+                                         marker=dict(
+                                             color=color,
+                                             size=3,
+                                             opacity=0.7
+                                         ),
+                                         name=f'{datetime.fromtimestamp(month_data["monthIntTime"].iloc[0]).strftime("%Y-%m")}',
+                                         showlegend=True)
+                    fig.add_trace(scatter)
+
            # 绘制合同功率曲线
            # 绘制合同功率曲线
             line = go.Scatter(x=dataFrameGuaranteePowerCurve[Field_WindSpeed],
             line = go.Scatter(x=dataFrameGuaranteePowerCurve[Field_WindSpeed],
                               y=dataFrameGuaranteePowerCurve[Field_ActiverPower],
                               y=dataFrameGuaranteePowerCurve[Field_ActiverPower],

+ 16 - 3
dataAnalysisBusiness/algorithm/powerScatterAnalyst.py

@@ -29,7 +29,7 @@ class PowerScatterAnalyst(AnalystWithGoodBadPoint):
             print("After screening for blade pitch angle less than the configured value, plot power curve scatter points without data")
             print("After screening for blade pitch angle less than the configured value, plot power curve scatter points without data")
             return
             return
 
 
-        # # dataFrameGuaranteePowerCurve=self.powerFarmInfo[Field_PowerContractURL]
+        # dataFrameGuaranteePowerCurve=self.powerFarmInfo[Field_PowerContractURL]
         # grouped=self.dataFrameContractOfTurbine.groupby([Field_PowerFarmCode, Field_MillTypeCode])
         # grouped=self.dataFrameContractOfTurbine.groupby([Field_PowerFarmCode, Field_MillTypeCode])
 
 
         # for groupByKey,contractPowerCurveOfMillType in grouped:
         # for groupByKey,contractPowerCurveOfMillType in grouped:
@@ -55,8 +55,21 @@ class PowerScatterAnalyst(AnalystWithGoodBadPoint):
         conf (ConfBusiness): 配置
         conf (ConfBusiness): 配置
         """
         """
         # 按设备名分组数据
         # 按设备名分组数据
-        colorsList = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
-                      '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#aec7e8', '#ffbb78']
+        # colorsList = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
+        #               '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#aec7e8', '#ffbb78']
+        colorsList = [  
+                        "#3E409C",
+                        "#3586BF",
+                        "#52A3AE",
+                        "#85D0AE",
+                        "#A8DCA2",
+                        "#FBFFBE",
+                        "#FDF1A9",
+                        "#FFE286",
+                        "#FCB06C",
+                        "#F96F4A",
+                        "#E4574C",
+                        "#AF254F"]
         cutInWsField = self.turbineModelInfo[Field_CutInWS]
         cutInWsField = self.turbineModelInfo[Field_CutInWS]
         cut_in_ws = cutInWsField.min() - 1 if cutInWsField.notna().any() else 2
         cut_in_ws = cutInWsField.min() - 1 if cutInWsField.notna().any() else 2
         # if not dataFrame.empty and Field_CutInWS in  dataFrame.columns and  dataFrame[Field_CutInWS].notna().any():
         # if not dataFrame.empty and Field_CutInWS in  dataFrame.columns and  dataFrame[Field_CutInWS].notna().any():

+ 8 - 6
dataAnalysisBusiness/algorithm/productionIndicatorAnalyst.py

@@ -83,7 +83,8 @@ class ProductionIndicatorAnalyst(AnalystNotFilter):
         # 平均风速
         # 平均风速
         WindSpeedAvr = df[Field_WindSpeed].mean()
         WindSpeedAvr = df[Field_WindSpeed].mean()
         # 切入风速
         # 切入风速
-        cut_in_ws = self.turbineModelInfo[Field_CutInWS]
+        CutInWS = self.turbineModelInfo[Field_CutInWS]
+        cut_in_ws=CutInWS.iloc[0]
         # 风机可利用率
         # 风机可利用率
         nShouldGP = (df[Field_WindSpeed] >= cut_in_ws).sum()
         nShouldGP = (df[Field_WindSpeed] >= cut_in_ws).sum()
         nRealGP = ((df[Field_WindSpeed] >= cut_in_ws) & (df[Field_ActiverPower] > 0)).sum()
         nRealGP = ((df[Field_WindSpeed] >= cut_in_ws) & (df[Field_ActiverPower] > 0)).sum()
@@ -229,7 +230,8 @@ class ProductionIndicatorAnalyst(AnalystNotFilter):
         '''
         '''
         # 确定新的风速范围
         # 确定新的风速范围
         # 下限:切入风速减1m/s
         # 下限:切入风速减1m/s
-        cut_in_ws = self.turbineModelInfo[Field_CutInWS]
+        cut_in_ws = self.turbineModelInfo[Field_CutInWS].iloc[0]
+
         lower_limit = cut_in_ws - 1.0
         lower_limit = cut_in_ws - 1.0
         # 找到对应85%额定功率的风速
         # 找到对应85%额定功率的风速
         df_ideal_sorted = dataFrameGuaranteePowerCurve.sort_values(by=Field_WindSpeed)
         df_ideal_sorted = dataFrameGuaranteePowerCurve.sort_values(by=Field_WindSpeed)
@@ -309,10 +311,10 @@ class ProductionIndicatorAnalyst(AnalystNotFilter):
         Qp, Thc, Qdr,Rdr = self.Production_indicators(dataFrameresults)
         Qp, Thc, Qdr,Rdr = self.Production_indicators(dataFrameresults)
         
         
         # 将Qp, Thc, Rdr添加到results_df中
         # 将Qp, Thc, Rdr添加到results_df中
-        dataFrameResult_total['Qp'] = [Qp]
-        dataFrameResult_total['Thc'] = [Thc]
-        dataFrameResult_total['Rdr'] = [Rdr]
-        dataFrameResult_total['Qdr'] = [Qdr]
+        dataFrameResult_total['Qp'] = [Qp]#风场总发电量
+        dataFrameResult_total['Thc'] = [Thc]#风场等效利用小时
+        dataFrameResult_total['Rdr'] = [Rdr]#风场弃风率
+        dataFrameResult_total['Qdr'] = [Qdr]#风场弃风电量
                 #保存为csv文件
                 #保存为csv文件
 
 
         print("dataFrameResult_total:",dataFrameResult_total)
         print("dataFrameResult_total:",dataFrameResult_total)

+ 24 - 6
dataAnalysisBusiness/algorithm/temperatureEnvironmentAnalyst.py

@@ -12,7 +12,7 @@ from plotly.subplots import make_subplots
 
 
 class TemperatureEnvironmentAnalyst(AnalystWithGoodBadLimitPoint):
 class TemperatureEnvironmentAnalyst(AnalystWithGoodBadLimitPoint):
     """
     """
-    风电机组大部件温升分析
+    风电机组环境温度传感器分析
     """
     """
 
 
     def typeAnalyst(self):
     def typeAnalyst(self):
@@ -33,8 +33,25 @@ class TemperatureEnvironmentAnalyst(AnalystWithGoodBadLimitPoint):
             {Field_EnvTemp: 'median'})
             {Field_EnvTemp: 'median'})
         turbineEnvTempData = turbineEnvTempData.reset_index()
         turbineEnvTempData = turbineEnvTempData.reset_index()
         mergeData = self.mergeData(self.turbineInfo, turbineEnvTempData)
         mergeData = self.mergeData(self.turbineInfo, turbineEnvTempData)
-
-        return self.draw(mergeData, outputAnalysisDir, conf)
+        # 分机型
+        turbrineInfos = self.common.getTurbineInfos(
+            conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
+
+        groupedOfTurbineModel = turbrineInfos.groupby(Field_MillTypeCode)
+
+        returnDatas = []
+        for turbineModelCode, group in groupedOfTurbineModel:
+            currTurbineCodes = group[Field_CodeOfTurbine].unique().tolist()
+            currTurbineModeInfo = self.common.getTurbineModelByCode(
+                turbineModelCode, self.turbineModelInfo)
+            currDataFrameOfTurbines = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine].isin(
+                currTurbineCodes)]
+            returnData= self.draw(mergeData, outputAnalysisDir, conf,currTurbineModeInfo)
+            returnDatas.append(returnData)
+
+        returnResult = pd.concat(returnDatas, ignore_index=True) 
+        return returnResult
+        # return self.draw(mergeData, outputAnalysisDir, conf)
 
 
     def mergeData(self,  turbineInfos: pd.DataFrame, turbineEnvTempData):
     def mergeData(self,  turbineInfos: pd.DataFrame, turbineEnvTempData):
         """
         """
@@ -80,7 +97,8 @@ class TemperatureEnvironmentAnalyst(AnalystWithGoodBadLimitPoint):
 
 
     fieldTemperatureDiff = "temperature_diff"
     fieldTemperatureDiff = "temperature_diff"
 
 
-    def draw(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract, charset=charset_unify):
+    # def draw(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract, charset=charset_unify):
+    def draw(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract, turbineModelInfo: pd.Series):
         # 处理数据
         # 处理数据
         dataFrame['new'] = dataFrame.loc[:, [Field_NameOfTurbine,
         dataFrame['new'] = dataFrame.loc[:, [Field_NameOfTurbine,
                                              Field_Longitude, Field_Latitude, Field_EnvTemp]].apply(tuple, axis=1)
                                              Field_Longitude, Field_Latitude, Field_EnvTemp]].apply(tuple, axis=1)
@@ -114,7 +132,7 @@ class TemperatureEnvironmentAnalyst(AnalystWithGoodBadLimitPoint):
             row=1, col=1
             row=1, col=1
         )
         )
         fig1.update_layout(
         fig1.update_layout(
-            title={'text': f'温度偏差-{self.turbineModelInfo[Field_MachineTypeCode].iloc[0]}', 'x': 0.5},
+            title={'text': f'温度偏差-{turbineModelInfo[Field_MachineTypeCode]}', 'x': 0.5},
             xaxis_title='机组名称',
             xaxis_title='机组名称',
             yaxis_title='温度偏差',
             yaxis_title='温度偏差',
             shapes=[
             shapes=[
@@ -165,7 +183,7 @@ class TemperatureEnvironmentAnalyst(AnalystWithGoodBadLimitPoint):
             row=1, col=1
             row=1, col=1
         )
         )
         fig2.update_layout(
         fig2.update_layout(
-            title={'text': f'平均温度-{self.turbineModelInfo[Field_MachineTypeCode].iloc[0]}', 'x': 0.5},
+            title={'text': f'平均温度-{turbineModelInfo[Field_MachineTypeCode]}', 'x': 0.5},
             xaxis_title='机组名称',
             xaxis_title='机组名称',
             yaxis_title=' 温度',
             yaxis_title=' 温度',
             xaxis=dict(tickangle=-45)  # 为x轴也设置旋转角度
             xaxis=dict(tickangle=-45)  # 为x轴也设置旋转角度

+ 0 - 81
dataAnalysisBusiness/algorithm/windRoseOfTurbine.py

@@ -1,81 +0,0 @@
-import os
-import pandas as pd
-import numpy as np
-import plotly.graph_objects as go
-from plotly.subplots import make_subplots
-import seaborn as sns
-import matplotlib.pyplot as plt
-from matplotlib.ticker import MultipleLocator
-from windrose import WindroseAxes
-from behavior.analyst import Analyst
-from utils.directoryUtil import DirectoryUtil as dir
-from algorithmContract.confBusiness import *
-from matplotlib.cm import get_cmap 
-from matplotlib.colors import ListedColormap   
-
-
-class WinRoseOfTurbineAnalyst(Analyst):
-    """
-    风电机组变桨-功率分析
-    """
-
-    def typeAnalyst(self):
-        return "wind_rose_turbine"
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.windRoseAnalysis(dataFrameMerge, outputAnalysisDir, confData)
-
-    def windRoseAnalysis(self, dataFrameMerge:pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
-        # 检查所需列是否存在
-        required_columns = {confData.field_wind_dir,confData.field_wind_speed}
-        if not required_columns.issubset(dataFrameMerge.columns):
-            raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-        
-        # 风速区间  
-        bins = [0, 3, 6, 9, np.inf]  
-        speed_labels = ['[0,3)', '[3,6)', '[6,9)', '>=9']  
-        # 准备颜色映射  
-        colors = plt.cm.Blues(np.linspace(0, 1, len(speed_labels)))  
-        cmap = ListedColormap(colors)  
-        # 将风向按照22.5度一个间隔进行分组  
-        wind_directions = np.arange(0, 360, 22.5)  
-
-        # 按设备名分组数据
-        grouped = dataFrameMerge.groupby(Field_NameOfTurbine)
-        print("self.ratedPower {}".format(confData.rated_power))
-        # 遍历每个设备并绘制图
-        for name, group in grouped:            
-            # 对风速进行分箱处理,但不添加到DataFrame中  
-            speed_bins = pd.cut(group[confData.field_wind_speed], bins=bins, labels=speed_labels)  
-            
-            # 将风向按照22.5度一个间隔进行分组  
-            wind_directions = np.arange(0, 360, 22.5)  
-            group['风向分组'] = pd.cut(group[confData.field_wind_dir], bins=wind_directions, labels=wind_directions[:-1])  
-            
-            # 绘制风玫瑰图  
-            fig, ax = plt.subplots(figsize=(8, 8), subplot_kw={'polar': True})  
-            
-            # 为每个风速区间绘制风向的条形图,并添加图例  
-            for i, (label, color) in enumerate(zip(speed_labels, colors)):  
-                # 筛选出当前风速区间的数据  
-                subset = group[speed_bins == label]  
-                # 计算每个风向分组的频数  
-                counts = subset['风向分组'].value_counts().reindex(wind_directions[:-1], fill_value=0)  
-                # 绘制条形图,并添加标签用于图例  
-                bar = ax.bar(counts.index * np.pi / 180, counts.values, color=cmap(i), alpha=0.75, width=(22.5 * np.pi / 180), label=label)  
-            
-            # 设置标题和标签  
-            ax.set_title(f"Wind Rose {name}", va='top')  
-            ax.set_theta_zero_location('N')  # 设置0度位置为北  
-            ax.set_theta_direction(-1)  # 设置角度方向为顺时针  
-            ax.set_yticklabels([])  # 不显示y轴刻度标签  
-            ax.set_xticks(wind_directions * np.pi / 180)  # 设置x轴刻度  
-            ax.set_xticklabels(['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'])  # 设置x轴刻度标签为方向  
-            
-            # 添加图例,并设置位置以避免与图形重叠  
-            ax.legend(title='Wind Speed', bbox_to_anchor=(1.1, 1), loc='center left', borderaxespad=0.)   
-
-            # 保存图像并关闭绘图窗口
-            output_file = os.path.join(outputAnalysisDir, f"{name}.png")
-            plt.savefig(output_file, bbox_inches='tight', dpi=120)
-            plt.close()

+ 120 - 202
dataAnalysisBusiness/algorithm/yawErrorAnalyst.py

@@ -9,7 +9,6 @@ from plotly.subplots import make_subplots
 from scipy.optimize import curve_fit
 from scipy.optimize import curve_fit
 
 
 
 
-
 class YawErrorAnalyst(AnalystWithGoodPoint):
 class YawErrorAnalyst(AnalystWithGoodPoint):
     """
     """
     风电机组静态偏航误差分析
     风电机组静态偏航误差分析
@@ -20,230 +19,167 @@ class YawErrorAnalyst(AnalystWithGoodPoint):
     fieldPowerMax = 'max_power'
     fieldPowerMax = 'max_power'
     fieldPowerMin = 'min_power'
     fieldPowerMin = 'min_power'
     fieldPowerMedian = 'median_power'
     fieldPowerMedian = 'median_power'
-    fieldYawError = 'yaw_error'
-    fieldStep = 'wsstep'
-    fieldK = 'k' 
-    fieldWindSpeed = 'mean_WindSpeed'
-    fieldcount = 'point_num'
+    fieldPowerGT0p = 'power_gt_0'
+    fieldPowerGT80p = 'power_gt_80p'
+    fieldPowerRatio0p = 'ratio_0'
+    fieldPowerRatio80p = 'ratio_80p'
+    fieldSlop = 'slop'
 
 
     def typeAnalyst(self):
     def typeAnalyst(self):
         return "yaw_error"
         return "yaw_error"
 
 
     def selectColumns(self):
     def selectColumns(self):
-        return [Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_AngleIncluded,Field_PitchAngel1,Field_RotorSpeed, Field_GeneratorSpeed]
-
-    def filterCommon(self, dataFrame: pd.DataFrame,conf:Contract):
-        #-------------------1.物理筛选--------
-        # 使用 loc 方法获取 Field_RatedPower 列的值
-        fullpower  = self.turbineInfo[Field_RatedPower].iloc[0]
-        # rated_power  = self.turbineInfo.loc[self.turbineInfo[Field_CodeOfTurbine].isin(dataFrame[Field_CodeOfTurbine]), Field_RatedPower]
-        # fullpower=rated_power.iloc[0]
-        # rated_power=self.turbineInfo[Field_RatedPower]
-
-        # 删除小于0的有功功率
-        dataFrame = dataFrame[~(dataFrame[Field_ActiverPower] < 0)]
-        # 删除小于2.5的风速
-        dataFrame = dataFrame[~(dataFrame[Field_WindSpeed] < 2.5)]
-        # 删除有功小于额定功率-100,变桨角度大于0.2的数据
-        dataFrame = dataFrame[~((dataFrame[Field_ActiverPower] < fullpower - 100) & (dataFrame[Field_PitchAngel1] > 3))]
-        # 删除有功小于额定功率-40,变桨角度大于1的数据
-        dataFrame = dataFrame[~((dataFrame[Field_ActiverPower] < fullpower - 40) & (dataFrame[Field_PitchAngel1] > 5))]
-        # 删除对风角度小于-30,大于30的数据
-        dataFrame = dataFrame[~((dataFrame[Field_AngleIncluded].abs() > 30))]
-        #-------------------2.数学筛选--------
-        wsstep = 0.5
-        stdup = 6
-        stddown = 2.2
-        for wscol in np.arange(3, 15.25, wsstep/2):
-            dataFrame_lv = dataFrame[(dataFrame[Field_WindSpeed] >= (wscol-wsstep/4)) & (dataFrame[Field_WindSpeed] < (wscol+wsstep/4))]
-            meanpowr = np.mean(dataFrame_lv[Field_ActiverPower])
-            stddataws = np.std(dataFrame_lv[Field_ActiverPower])
-            index1 = dataFrame[(dataFrame[Field_WindSpeed] >= (wscol-wsstep/4)) & (dataFrame[Field_WindSpeed] < (wscol+wsstep/4))
-                             & ((dataFrame_lv[Field_ActiverPower] - meanpowr) > stdup * stddataws) | ((meanpowr - dataFrame_lv
-            [Field_ActiverPower]) > stddown * stddataws)].index
-            dataFrame.drop(index1, inplace=True)
+        return [Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_AngleIncluded]
+
+    def filterCommon(self, dataFrame: pd.DataFrame, conf: Contract):
+        dataFrame = dataFrame[~((dataFrame[Field_AngleIncluded].abs() >= 15))]
 
 
         return dataFrame
         return dataFrame
 
 
-    def calculateYawError(self, dataFrame: pd.DataFrame, fieldAngleInclude,fieldActivePower, fieldWindSpeed):
-        
+    def calculateYawError(self, dataFrame: pd.DataFrame, fieldAngleInclude, fieldActivePower):
         dataFrame = dataFrame.dropna(
         dataFrame = dataFrame.dropna(
-            subset=[Field_NameOfTurbine, fieldAngleInclude, fieldActivePower,fieldWindSpeed])
-        df_math_yaw = dataFrame.copy()
-        # 偏航误差角度计算
-        degree_length = 20  # 对风角度最大值
-        yaw_data_out = np.empty((0, 4), float)
-        wsstep = 0.5
-        # 初始化一个字典来存储每 k 次循环的平均功率结果
-        power_dict = {}
-        for k in np.arange(3, 15.5, wsstep):
-            yaw_data_value_co = np.empty((0, 4), float)
-            yaw_data = df_math_yaw[(df_math_yaw[fieldWindSpeed] >= (k - 0.25)) & (df_math_yaw[fieldWindSpeed] < (k + 0.25))]
-            # print(yaw_data)
-            for m in np.arange((0 - degree_length), degree_length + 1, 0.5):
-                wd = yaw_data[(yaw_data[fieldAngleInclude] >= (m - 0.5)) & (yaw_data[fieldAngleInclude] < (m + 0.5))]
-                wd_row = wd.shape[0]
-                if wd_row < 10:
-                    continue
-                if wd_row >= 10:
-                    max_value_row = wd[fieldActivePower].idxmax()
-                    min_value_row = wd[fieldActivePower].idxmin()
-                    wd = wd.drop(max_value_row)
-                    wd = wd.drop(min_value_row)
-                wd_row = wd.shape[0]
-                mean_wd = wd[fieldAngleInclude].mean()
-                mean_power = wd[fieldActivePower].mean()
-                mean_ws = wd[fieldWindSpeed].mean()
-                yaw_data_value = [mean_power, mean_wd, mean_ws, wd_row]
-                yaw_data_value_co = np.vstack((yaw_data_value_co, yaw_data_value))
-                # 存储当前 k 和 m 对应的 mean_power
-                if k not in power_dict:
-                    power_dict[k] = {}
-                power_dict[k][m] = mean_power
-            # 检查 yaw_data_value_co 是否为空
-            if yaw_data_value_co.size == 0:
-                print(f"yaw_data_value_co k={k}是空的,无法计算argmax")
-                continue
-            else:
-                max_row = np.argmax(yaw_data_value_co[:, 0])
-                yaw_data_out = np.vstack((yaw_data_out, yaw_data_value_co[max_row, :]))
+            subset=[Field_NameOfTurbine, fieldAngleInclude, fieldActivePower])
+        # Calculate floor values and other transformations
+        dataFrame[self.fieldWindDirFloor] = np.floor(
+            dataFrame[fieldAngleInclude]).astype(int)
+        dataFrame[self.fieldPower] = dataFrame[fieldActivePower].astype(float)
+
+        # Calculate aggregated metrics for power
+        grouped = dataFrame.groupby(self.fieldWindDirFloor).agg({
+            Field_NameOfTurbine: ['min'],
+            self.fieldPower: ['mean', 'max', 'min', 'median', lambda x: (
+                x > 0).sum(), lambda x: (x > x.median()).sum()]
+        }).reset_index()
+
+        # Rename columns for clarity
+        grouped.columns = [self.fieldWindDirFloor, Field_NameOfTurbine, self.fieldPowerMean, self.fieldPowerMax,
+                           self.fieldPowerMin, self.fieldPowerMedian, self.fieldPowerGT0p, self.fieldPowerGT80p]
 
 
-        # 将结果转换为 DataFrame
-        result_df = pd.DataFrame(yaw_data_out, columns=[self.fieldPowerMean, self.fieldYawError,self.fieldWindSpeed,self.fieldcount])
-        # result_df.to_csv('D:\\dashengkeji\\project\\WTOAAM\\debug_tset_20241008\\result_df_JHS_.csv',index=False)
-        # 构建最终画图的 DataFrame
-        final_df = pd.DataFrame(columns=[self.fieldWindDirFloor] + [f'{k}-{k+wsstep}' for k in np.arange(4, 9, wsstep)])
-        rows = []
-        for m in np.arange((0 - degree_length), degree_length + 1, 1):
-            row = {self.fieldWindDirFloor: m}
-            for k in np.arange(4, 9, wsstep):
-                if k in power_dict and power_dict[k]:  # 检查 power_dict[k] 是否存在且不为空
-                    row[f'{k}-{k+wsstep}'] = power_dict[k].get(m, np.nan)
-                else:
-                    row[f'{k}-{k+wsstep}'] = np.nan  # 如果 power_dict[k] 为空,则填充 np.nan
-            rows.append(row)
+        # Calculate total sums for conditions
+        power_gt_0_sum = grouped[self.fieldPowerGT0p].sum()
+        power_gt_80p_sum = grouped[self.fieldPowerGT80p].sum()
 
 
-        final_df = pd.concat([final_df, pd.DataFrame(rows)], ignore_index=True)
+        # Calculate ratios
+        grouped[self.fieldPowerRatio0p] = grouped[self.fieldPowerGT0p] / \
+            power_gt_0_sum
+        grouped[self.fieldPowerRatio80p] = grouped[self.fieldPowerGT80p] / \
+            power_gt_80p_sum
 
 
+        # Filter out zero ratios and calculate slope
+        grouped = grouped[grouped[self.fieldPowerRatio0p] > 0]
+        grouped[self.fieldSlop] = grouped[self.fieldPowerRatio80p] / \
+            grouped[self.fieldPowerRatio0p]
 
 
-        return result_df,final_df
+        # Sort by wind direction floor
+        grouped.sort_values(self.fieldWindDirFloor, inplace=True)
 
 
+        # # Write to CSV
+        # grouped.to_csv(output_path, index=False)
+
+        return grouped
+
+    def poly_func(self, x, a, b, c, d, e):
+        return a * x**4 + b * x ** 3 + c * x ** 2 + d * x + e
 
 
     def turbinesAnalysis(self,  outputAnalysisDir, conf: Contract, turbineCodes):
     def turbinesAnalysis(self,  outputAnalysisDir, conf: Contract, turbineCodes):
         dictionary = self.processTurbineData(turbineCodes,conf,self.selectColumns())
         dictionary = self.processTurbineData(turbineCodes,conf,self.selectColumns())
         dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
         dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
-        # dataFrameMerge.to_csv('D:\dashengkeji\project\WTOAAM\debug_tset_20241008\dataFrameJHS_WOG00935.csv',index=False)
         return self.yawErrorAnalysis(dataFrameMerge, outputAnalysisDir, conf)
         return self.yawErrorAnalysis(dataFrameMerge, outputAnalysisDir, conf)
 
 
     def yawErrorAnalysis(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract):
     def yawErrorAnalysis(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract):
         # 检查所需列是否存在
         # 检查所需列是否存在
-        required_columns = {Field_ActiverPower, Field_YawError,Field_WindSpeed,Field_PitchAngel1,Field_Cp,Field_TSR}
+        required_columns = {Field_ActiverPower, Field_YawError}
         if not required_columns.issubset(dataFrameMerge.columns):
         if not required_columns.issubset(dataFrameMerge.columns):
             raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
             raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-        
-        
 
 
         results = []
         results = []
         result_rows = []
         result_rows = []
         grouped = dataFrameMerge.groupby(
         grouped = dataFrameMerge.groupby(
             [Field_NameOfTurbine, Field_CodeOfTurbine])
             [Field_NameOfTurbine, Field_CodeOfTurbine])
 
 
-          
         for name, group in grouped:
         for name, group in grouped:
-            group = self.filterCommon(group,conf)
-            df,final_df = self.calculateYawError(
-                group, Field_YawError, Field_ActiverPower,Field_WindSpeed)
-            
+            df = self.calculateYawError(
+                group, Field_YawError, Field_ActiverPower)
+
             df.dropna(inplace=True)
             df.dropna(inplace=True)
-            
-            # final_df.dropna(inplace=True)
-            """
-            自动化选择tsr平稳段的风速段作为风速最后的筛选区间
-            """
-            # 滑动窗口size
-            window_size=5
-            # 差分阈值
-            threshold=2
-            diff = group[Field_TSR].diff().abs()
-            stable_mask = diff.rolling(window=window_size).mean() < threshold
-            # print("diff的索引:", diff.index)
-            # print("stable_mask的索引:", stable_mask.index)
-            # 找到最长的平稳段
-            longest_stable_segment = None
-            max_length = 0
-            current_segment = None
-
-            for i in range(len(stable_mask)):
-                if i in stable_mask.index:
-                    if stable_mask[i]:
-                        if current_segment is None:
-                            current_segment = [i]
-                        else:
-                            current_segment.append(i)
-                    else:
-                        if current_segment is not None:
-                            if len(current_segment) > max_length:
-                                longest_stable_segment = current_segment
-                                max_length = len(current_segment)
-                            current_segment = None
-
-            if current_segment is not None and len(current_segment) > max_length:
-                longest_stable_segment = current_segment
-
-            if longest_stable_segment is not None:
-                start_idx = longest_stable_segment[0]
-                end_idx = longest_stable_segment[-1]
-                # 获取tsr平稳区间对应的风俗区间
-                if start_idx is not None and end_idx is not None:
-                    # 提取对应的windspeed区间
-                    stable_windspeed_segment = group.loc[start_idx:end_idx, Field_WindSpeed]
-                    # print(f"平稳段的起始索引: {start_idx}, 结束索引: {end_idx}")
-                    # print("对应的windspeed区间:")
-                    # print(stable_windspeed_segment)
-                    # 计算风速平稳段的最大值和最小值
-                    max_windspeed = stable_windspeed_segment.max()
-                    min_windspeed = stable_windspeed_segment.min()
-                    # print(f"风速平稳段的最大值: {max_windspeed}")
-                    # print(f"风速平稳段的最小值: {min_windspeed}")
-                else:
-                    print("未找到平稳段")
-                    
-            # 筛选出 self.fieldWindSpeed 列的数值处于 4.5 到 8 区间内的行
-            filtered_df = df[(df[self.fieldWindSpeed] >= 4.5) & (df[self.fieldWindSpeed] <= 8)]
-            # 查找 df 中  self.fieldYawError列的平均值,并向下取整作为偏航误差
-            max_yaw_error = filtered_df[self.fieldYawError].mean()
-            max_yaw_error_floor = np.floor(max_yaw_error)
-
-            # 存储结果
-            results.append({
-                Field_NameOfTurbine: name[0],
-                Field_YawError: max_yaw_error_floor
-            })
+            # drop extreme value, the 3 largest and 3 smallest
 
 
-            #对 final_df 中的数据进行可视化
-            fig = go.Figure()
-            for col in final_df.columns[1:]:  # 跳过第一列 self.fieldWindDirFloor
-                fig.add_trace(go.Scatter(
-                    x=final_df[self.fieldWindDirFloor],
-                    y=final_df[col],
-                    mode='lines+markers',
-                    name=col
-                ))
-            
+            df_ = df[df[self.fieldPowerRatio80p] > 0.000]
+
+            xdata = df_[self.fieldWindDirFloor]
+            ydata = df_[self.fieldSlop]
+
+            # make ydata smooth
+            ydata = ydata.rolling(7).median()[6:]
+            xdata = xdata[3:-3]
+
+            if len(xdata) <= 0 and len(ydata) <= 0:
+                continue
+
+            # Curve fitting
+            popt, pcov = curve_fit(self.poly_func, xdata, ydata)
+            # popt contains the optimized parameters a, b, and c
+            # Generate fitted y-dataFrame using the optimized parameters
+            fitted_ydata = self.poly_func(xdata, *popt)
+
+            # get the max value of fitted_ydata and its index
+            max_pos = fitted_ydata.idxmax()
+
+            # Create a subplot with two rows
+            fig = make_subplots(rows=2, cols=1)
+
+            # First subplot
+            fig.add_trace(
+                go.Scatter(x=df[self.fieldWindDirFloor], y=df[self.fieldSlop],
+                           mode='markers', name="Energy Gain", marker=dict(size=5)),
+                row=1, col=1
+            )
+            fig.add_trace(
+                go.Scatter(x=xdata, y=fitted_ydata, mode='lines',
+                           name="Fit", line=dict(color='red')),
+                row=1, col=1
+            )
+            fig.add_trace(
+                go.Scatter(x=[df[self.fieldWindDirFloor][max_pos]], y=[fitted_ydata[max_pos]],
+                           mode='markers', name="Max Pos:"+str(df[self.fieldWindDirFloor][max_pos]),
+                           marker=dict(size=20)),
+                row=1, col=1
+            )
+
+            # Second subplot
+            fig.add_trace(
+                go.Scatter(x=df[self.fieldWindDirFloor], y=df[self.fieldPowerRatio0p],
+                           mode='markers', name="Base Energy", marker=dict(size=5)),
+                row=2, col=1
+            )
+            fig.add_trace(
+                go.Scatter(x=df[self.fieldWindDirFloor], y=df[self.fieldPowerRatio80p],
+                           mode='markers', name="Slope Energy", marker=dict(size=5)),
+                row=2, col=1
+            )
+
+            # Update layout
             fig.update_layout(
             fig.update_layout(
-                title=f'偏航误差分析 {name[0]} ',
-                xaxis_title='偏航角度',
-                yaxis_title='平均功率',
-                legend_title='风速区间',
-                showlegend=True
+                title={
+                    "text": f"Yaw Error Analysis: {name[0]}",
+                    "x": 0.5
+                },
+                showlegend=True,
+                margin=dict(t=50, b=10)  # t为顶部(top)间距,b为底部(bottom)间距
             )
             )
-            
+
             # Save to file
             # Save to file
             filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
             filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
             fig.write_image(filePathOfImage, scale=3)
             fig.write_image(filePathOfImage, scale=3)
             filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
             filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
             fig.write_html(filePathOfHtml)
             fig.write_html(filePathOfHtml)
 
 
+            # calc squear error of fitted_ydata and ydata
+            print(name[0], "\t", df[self.fieldWindDirFloor][max_pos])
+
+            resultOfTurbine = [name[0], df[self.fieldWindDirFloor][max_pos]]
+            results.append(resultOfTurbine)
+
             result_rows.append({
             result_rows.append({
                 Field_Return_TypeAnalyst: self.typeAnalyst(),
                 Field_Return_TypeAnalyst: self.typeAnalyst(),
                 Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
                 Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
@@ -260,24 +196,6 @@ class YawErrorAnalyst(AnalystWithGoodPoint):
                 Field_CodeOfTurbine: name[1],
                 Field_CodeOfTurbine: name[1],
                 Field_Return_FilePath: filePathOfHtml,
                 Field_Return_FilePath: filePathOfHtml,
                 Field_Return_IsSaveDatabase: False
                 Field_Return_IsSaveDatabase: False
-            
-            
-            })
-            # 初始化一个空的DataFrame,指定列名
-            columns = [Field_NameOfTurbine, Field_YawError]
-            dataFrameResult = pd.DataFrame(results, columns=columns)
-
-            filePathOfYawErrorResult = os.path.join(
-                outputAnalysisDir, f"{name[0]}yaw_error_result{CSVSuffix}")
-            df.to_csv(filePathOfYawErrorResult, index=False)
-
-            result_rows.append({
-                Field_Return_TypeAnalyst: self.typeAnalyst(),
-                Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-                Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-                Field_CodeOfTurbine: name[1],
-                Field_Return_FilePath: filePathOfYawErrorResult,
-                        Field_Return_IsSaveDatabase: True
             })
             })
 
 
         # 初始化一个空的DataFrame,指定列名
         # 初始化一个空的DataFrame,指定列名

+ 24 - 21
dataAnalysisBusiness/algorithm/yawErrorDensityAnalyst.py

@@ -24,13 +24,13 @@ class YawErrorDensityAnalyst(AnalystWithGoodPoint):
         dataFrame = dataFrame.dropna(
         dataFrame = dataFrame.dropna(
             subset=[Field_NameOfTurbine, Field_YawError, Field_ActiverPower,Field_WindSpeed])
             subset=[Field_NameOfTurbine, Field_YawError, Field_ActiverPower,Field_WindSpeed])
 
 
-        filtered_dataFrame = dataFrame[dataFrame[Field_YawError].abs() <= 30]
+        filtered_dataFrame = dataFrame[(dataFrame[Field_YawError].abs() <= 30)&(dataFrame[Field_WindSpeed] >= 0)&(dataFrame[Field_WindSpeed] <= 25)]
         x=filtered_dataFrame[Field_YawError]
         x=filtered_dataFrame[Field_YawError]
         y=filtered_dataFrame[Field_WindSpeed]
         y=filtered_dataFrame[Field_WindSpeed]
         # data = np.column_stack((x, y))  # 合并为两列数组
         # data = np.column_stack((x, y))  # 合并为两列数组
         # 使用 binned_statistic_2d 来计算散点的密度分布
         # 使用 binned_statistic_2d 来计算散点的密度分布
-        binSize_x = 40
-        binSize_y = 40
+        binSize_x = 60
+        binSize_y = 50
         counts, x_edges, y_edges, binnumber = binned_statistic_2d(x, y, values=None, statistic='count', bins=[binSize_x, binSize_y])
         counts, x_edges, y_edges, binnumber = binned_statistic_2d(x, y, values=None, statistic='count', bins=[binSize_x, binSize_y])
 
 
         # 将数据密度转化为颜色值
         # 将数据密度转化为颜色值
@@ -130,23 +130,26 @@ class YawErrorDensityAnalyst(AnalystWithGoodPoint):
             print(f'{name[0]}指标:',result)
             print(f'{name[0]}指标:',result)
             # 用密度作为颜色绘制散点图,并限制横坐标范围为 -20 到 20
             # 用密度作为颜色绘制散点图,并限制横坐标范围为 -20 到 20
             fig = go.Figure()
             fig = go.Figure()
-            fig.add_trace(go.Scatter(
-                                    x=df["x"],
-                                    y=df["y"],
-                                    mode='markers', 
-                                    marker=dict(size=3,
-                                                opacity=0.7, 
-                                                color=df["density"], 
-                                                colorscale=fixed_colorscale, 
-                                                showscale=True,
-                                                )
-                                        ))
-            fig.update_layout(xaxis_title='对风角度',
-                               yaxis_title='风速', 
-                               title=f' 动态偏航误差分析-{name[0]}',
-                               xaxis=dict(range=[-30, 30]),
-                               yaxis=dict(range=[0, 25]))
-
+            fig.add_trace(go.Scattergl(
+                x=df["x"],
+                y=df["y"],
+                mode='markers', 
+                marker=dict(
+                    size=3,
+                    opacity=0.7, 
+                    color=df["density"], 
+                    colorscale=fixed_colorscale, 
+                    showscale=True,
+                )
+            ))
+
+            fig.update_layout(
+                xaxis_title='对风角度',
+                yaxis_title='风速', 
+                title=f'动态偏航误差分析-{name[0]}',
+                xaxis=dict(range=[-20, 20]),  # 限制横坐标范围为 -20 到 20
+                yaxis=dict(range=[0, 25])
+            )
             # Save to file
             # Save to file
             filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
             filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
             fig.write_image(filePathOfImage, scale=3)
             fig.write_image(filePathOfImage, scale=3)
@@ -168,7 +171,7 @@ class YawErrorDensityAnalyst(AnalystWithGoodPoint):
                 Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
                 Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
                 Field_CodeOfTurbine: name[1],
                 Field_CodeOfTurbine: name[1],
                 Field_Return_FilePath: filePathOfHtml,
                 Field_Return_FilePath: filePathOfHtml,
-                Field_Return_IsSaveDatabase: False
+                Field_Return_IsSaveDatabase: True
             
             
             
             
             })
             })

+ 0 - 4
dataContract/__init__.py

@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from . import algorithmContract
-__all__=['algorithmContract']

+ 0 - 88
dataContract/algorithmContract/dataContract.json

@@ -1,88 +0,0 @@
-{
-	"dataContractType": {
-		"dataContractType": "analysisExecuteOrder",
-		"version": "1.0.0"
-	},
-	"dataContract": {
-		"dataSource": {
-			"scada": "minute"
-		},
-		"dataFilter": {
-			"powerFarmID": "",
-			"turbines": [
-				{
-					"dataBatchNum": "B2024042211-0"
-				},
-				{
-					"dataBatchNum": "B2024042211-0"
-				},
-				{
-					"dataBatchNum": "B2024042211-1"
-				}
-			],
-			"beginTime": "2023-01-01 00:00:00",
-			"endTime": "2023-12-31 23:59:59",
-			"excludingMonths": [
-				"2023-12",
-				"2023-09"
-			],
-			"customFilter": {
-				"valueWindSpeed": {
-					"min": 3.0,
-					"max": 25.0
-				},
-				"valuePitchAngle": {
-					"min": 2,
-					"max": null
-				},
-				"valueActivePower": {
-					"min": 10,
-					"max": 2500
-				},
-                "valueGeneratorSpeed": {
-                    "min": 10,
-                    "max": 2500
-                }
-			}
-		},
-		"configAnalysis": [
-            {
-                "package": "algorithm.windSpeedFrequencyAnalyst",
-                "className": "WindSpeedFrequencyAnalyst",
-                "methodName": "executeAnalysis"
-            },
-			{
-				"package": "algorithm.generatorSpeedPowerAnalyst",
-				"className": "GeneratorSpeedPowerAnalyst",
-				"methodName": "executeAnalysis"
-			}
-		],
-		"graphSets": {
-			"generatorSpeed": {
-				"step": 200,
-				"min": 1000,
-				"max": 2000
-			},
-			"generatorTorque": {
-				"step": 2000,
-				"min": 0,
-				"max": 12000
-			},
-			"cp": {
-				"step": 0.5,
-				"min": 0,
-				"max": 2
-			},
-			"tsr": {
-				"step": 5,
-				"min": 0,
-				"max": 30
-			},
-			"pitchAngle": {
-				"step": 1,
-				"min": -1,
-				"max": 20
-			}
-		}
-	}
-}

+ 0 - 0
mydatabase.db


+ 0 - 91
wtoaamapi/apps/business/main.py

@@ -1,91 +0,0 @@
-import pandas as pd
-import importlib
-import concurrent.futures
-from utils.jsonUtil import JsonUtil
-from algorithmContract.confBusiness import ConfBusiness
-from algorithm.dataProcessor import DataProcessor
-from behavior.baseAnalyst import BaseAnalyst
-from behavior.analyst import Analyst
-import seaborn as sns
-sns.set_style("darkgrid") 
-
-def buildDynamicInstance(module_path, class_name, confData: ConfBusiness):
-    # 动态导入模块
-    module = importlib.import_module(module_path)
-    # 获取类
-    cls = getattr(module, class_name)
-    # 创建类实例
-    instance = cls(confData)
-
-    return instance
-
-def dynamic_instance_and_call(module_path, class_name, method_name, *args, **kwargs):
-    # 动态导入模块
-    module = importlib.import_module(module_path)
-    # 获取类
-    cls = getattr(module, class_name)
-    # 创建类实例
-    instance = cls()
-    # 获取方法
-    method = getattr(instance, method_name)
-    # 调用方法
-    method(*args, **kwargs)
-
-
-def loadJson(filePath):
-    # 打开并读取JSON文件
-    with open(filePath, 'r') as f:
-        data = JsonUtil.read_json(filePath)
-
-    return data
-
-def executeAnalysis(config):
-    configBusinessFilePath=config["configFilePath"]
-    print(configBusinessFilePath)
-    businessConfig=ConfBusiness()
-    configBusiness = businessConfig.loadConfig(configBusinessFilePath)
-
-    baseAnalysts=[]
-    noCustomFilterAnalysts = []
-    analysts = []
-
-    for dynamicAnalyst in config["configAnalysis"]:
-        package = dynamicAnalyst["package"]
-        className = dynamicAnalyst["className"]
-        methodName = dynamicAnalyst["methodName"]
-        analyst = buildDynamicInstance(package, className, configBusiness)
-
-        analysts.append(analyst)
-                   
-    process = DataProcessor()
-
-    for analyst in analysts:
-        process.attach(analyst)
-    process.execute(configBusiness)
-
-    for analyst in analysts:
-        process.detach(analyst)
-
-
-if __name__ == "__main__":
-    configs = loadJson(r"conf/conf.json")
-
-    for config in configs:
-        executeAnalysis(config)
-
-    # 使用多线程时,matplotlib绘图报错
-    # 在使用Python语言的matplotlib包时,如果尝试在多线程环境中创建图形界面,你可能会遇到“QWidget-: Must construct a QApplication before a QWidget”的错误。
-    # 这个错误通常是因为matplotlib的图形后端(backend)依赖于Qt框架,而Qt框架要求在一个QApplication实例被创建之后再创建任何QWidget对象。
-    # 在多线程环境中,每个线程都应该有一个它自己的事件循环。然而,QApplication实例通常是全局的,并且应该只在主线程中创建一次。
-    # 如果你尝试在一个非主线程中创建QApplication或者QWidget,就会遇到这个问题。
-    
-    # with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
-    #     futures=[executor.submit(executeAnalysis, config) for config in configs]        
-
-    #     for future in concurrent.futures.as_completed(futures):
-    #         try:
-    #             result = future.result()
-    #         except Exception as exc:
-    #             print(f'生成异常: {exc}')
-    #         else:
-    #             print(f'任务返回结果: {result}')

+ 0 - 41
wtoaamapi/testListen.py

@@ -1,41 +0,0 @@
-import socket  
-  
-def start_server(port):  
-    # 创建一个socket对象  
-    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
-      
-    # 绑定到指定的IP地址和端口  
-    # 如果你想监听所有可用的网络接口,可以使用空字符串('')作为IP地址  
-    server_address = ('', port)  
-    print(f'Starting up on {server_address[1]}')  
-    server_socket.bind(server_address)  
-      
-    # 开始监听连接  
-    server_socket.listen(1)  
-      
-    while True:  
-        # 等待客户端连接  
-        print('Waiting for a connection')  
-        connection, client_address = server_socket.accept()  
-          
-        try:  
-            print(f'Connection from {client_address}')  
-              
-            # 接收数据,最多1024字节  
-            while True:  
-                data = connection.recv(1024)  
-                print(f'Received {len(data)} bytes from {client_address}')  
-                  
-                if not data:  
-                    break  
-                  
-                # 向客户端发送数据  
-                connection.sendall(data)  
-                  
-        finally:  
-            # 清理连接  
-            connection.close()  
-  
-if __name__ == '__main__':  
-    port = 8123  # 你可以更改为你想要的端口号  
-    start_server(port)