Browse Source

删除多余文件

chenhongyan1989 2 months ago
parent
commit
04de51a2b6
54 changed files with 0 additions and 6610 deletions
  1. 0 0
      wtoaamapi/apps/business/algorithm/__init__.py
  2. 0 11
      wtoaamapi/apps/business/algorithm/analyst.py
  3. 0 9
      wtoaamapi/apps/business/algorithm/analystNotFilter.py
  4. 0 9
      wtoaamapi/apps/business/algorithm/analystWithGoodBadLimitPoint.py
  5. 0 10
      wtoaamapi/apps/business/algorithm/analystWithGoodBadPoint.py
  6. 0 9
      wtoaamapi/apps/business/algorithm/analystWithGoodPoint.py
  7. 0 11
      wtoaamapi/apps/business/algorithm/analystWithNoCustomeFilter.py
  8. 0 49
      wtoaamapi/apps/business/algorithm/baseAnalyst.py
  9. 0 13
      wtoaamapi/apps/business/algorithm/commonBusiness.py
  10. 0 197
      wtoaamapi/apps/business/algorithm/cpAnalyst.py
  11. 0 116
      wtoaamapi/apps/business/algorithm/cpTrendAnalyst.py
  12. 0 269
      wtoaamapi/apps/business/algorithm/cpWindSpeedAnalyst.py
  13. 0 153
      wtoaamapi/apps/business/algorithm/dalAnalyst.py
  14. 0 21
      wtoaamapi/apps/business/algorithm/dataIntegrityOfMinuteAnalyst.py
  15. 0 271
      wtoaamapi/apps/business/algorithm/dataIntegrityOfSecondAnalyst.py
  16. 0 435
      wtoaamapi/apps/business/algorithm/dataMarker.py
  17. 0 540
      wtoaamapi/apps/business/algorithm/dataMarkerOfScada3.py
  18. 0 444
      wtoaamapi/apps/business/algorithm/dataProcessor.py
  19. 0 43
      wtoaamapi/apps/business/algorithm/formula_cp.py
  20. 0 92
      wtoaamapi/apps/business/algorithm/generatorSpeedPowerAnalyst.py
  21. 0 62
      wtoaamapi/apps/business/algorithm/generatorSpeedTorqueAnalyst.py
  22. 0 122
      wtoaamapi/apps/business/algorithm/minPitchAnalyst.py
  23. 0 177
      wtoaamapi/apps/business/algorithm/outputProcessor.py
  24. 0 168
      wtoaamapi/apps/business/algorithm/pitchGeneratorSpeedAnalyst.py
  25. 0 59
      wtoaamapi/apps/business/algorithm/pitchPowerAnalyst.py
  26. 0 382
      wtoaamapi/apps/business/algorithm/powerCurveAnalyst.py
  27. 0 107
      wtoaamapi/apps/business/algorithm/powerOscillationAnalyst.py
  28. 0 278
      wtoaamapi/apps/business/algorithm/ratedPowerWindSpeedAnalyst.py
  29. 0 138
      wtoaamapi/apps/business/algorithm/ratedWindSpeedAnalyst.py
  30. 0 319
      wtoaamapi/apps/business/algorithm/temperatureEnvironmentAnalyst.py
  31. 0 143
      wtoaamapi/apps/business/algorithm/temperatureLargeComponentsAnalyst.py
  32. 0 365
      wtoaamapi/apps/business/algorithm/tsrAnalyst.py
  33. 0 125
      wtoaamapi/apps/business/algorithm/tsrTrendAnalyst.py
  34. 0 379
      wtoaamapi/apps/business/algorithm/tsrWindSpeedAnalyst.py
  35. 0 0
      wtoaamapi/apps/business/algorithm/utils/__init__.py
  36. 0 57
      wtoaamapi/apps/business/algorithm/utils/csvFileUtil.py
  37. 0 89
      wtoaamapi/apps/business/algorithm/utils/directoryUtil.py
  38. 0 0
      wtoaamapi/apps/business/algorithm/utils/jsonUtil/__init__.py
  39. 0 37
      wtoaamapi/apps/business/algorithm/utils/jsonUtil/jsonUtil.py
  40. 0 21
      wtoaamapi/apps/business/algorithm/utils/test.py
  41. 0 176
      wtoaamapi/apps/business/algorithm/yawErrorAnalyst.py
  42. 0 106
      wtoaamapi/apps/business/appConfig.py
  43. 0 169
      wtoaamapi/apps/business/confBusiness.py
  44. 0 14
      wtoaamapi/apps/business/configAnalysis.py
  45. 0 3
      wtoaamapi/apps/business/const.py
  46. 0 199
      wtoaamapi/apps/business/contract.py
  47. 0 35
      wtoaamapi/apps/business/customDataContract.py
  48. 0 7
      wtoaamapi/apps/business/customFilter.py
  49. 0 7
      wtoaamapi/apps/business/dataContractType.py
  50. 0 23
      wtoaamapi/apps/business/dataFilter.py
  51. 0 6
      wtoaamapi/apps/business/dataSource.py
  52. 0 8
      wtoaamapi/apps/business/graphSet.py
  53. 0 107
      wtoaamapi/apps/business/testDataContract.py
  54. 0 20
      wtoaamapi/apps/business/turbineInfo.py

+ 0 - 0
wtoaamapi/apps/business/algorithm/__init__.py


+ 0 - 11
wtoaamapi/apps/business/algorithm/analyst.py

@@ -1,11 +0,0 @@
-from .baseAnalyst import BaseAnalyst
-import os
-import pandas as pd
-import numpy as np
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-
-class Analyst(BaseAnalyst):
-    def typeAnalyst(self):
-        pass

+ 0 - 9
wtoaamapi/apps/business/algorithm/analystNotFilter.py

@@ -1,9 +0,0 @@
-import pandas as pd
-from algorithmContract.contract import Contract
-
-from .analyst import Analyst
-
-
-class AnalystNotFilter(Analyst):
-    def filterCommon(self,dataFrame:pd.DataFrame, conf: Contract):
-        return dataFrame

+ 0 - 9
wtoaamapi/apps/business/algorithm/analystWithGoodBadLimitPoint.py

@@ -1,9 +0,0 @@
-from algorithmContract.confBusiness import *
-
-from .analyst import Analyst
-
-
-class AnalystWithGoodBadLimitPoint(Analyst):
-
-    def selectLabCondition(self,conditions: list[str]):
-        conditions.append(f"{Field_LableFlag} in (0,1,2,3,4)")

+ 0 - 10
wtoaamapi/apps/business/algorithm/analystWithGoodBadPoint.py

@@ -1,10 +0,0 @@
-from algorithmContract.confBusiness import *
-
-from .analyst import Analyst
-
-
-class AnalystWithGoodBadPoint(Analyst):
-
-    def selectLabCondition(self, conditions: list[str]):
-        conditions.append(f"{Field_LableFlag} in (0,1,2,3)")
-

+ 0 - 9
wtoaamapi/apps/business/algorithm/analystWithGoodPoint.py

@@ -1,9 +0,0 @@
-from algorithmContract.confBusiness import *
-
-from .analyst import Analyst
-
-
-class AnalystWithGoodPoint(Analyst):
-
-    def selectLabCondition(self, conditions: list[str]):
-        conditions.append(f"{Field_LableFlag} = 0")

+ 0 - 11
wtoaamapi/apps/business/algorithm/analystWithNoCustomeFilter.py

@@ -1,11 +0,0 @@
-from .baseAnalyst import BaseAnalyst
-import os
-import pandas as pd
-import numpy as np
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-
-class AnalystWithNoCustomFilter(BaseAnalyst):
-    def typeAnalyst(self):
-        pass

+ 0 - 49
wtoaamapi/apps/business/algorithm/baseAnalyst.py

@@ -1,49 +0,0 @@
-from abc import ABC, abstractmethod
-import pandas as pd
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-from .commonBusiness import CommonBusiness
-
-
-class BaseAnalyst(ABC):
-    def __init__(self, confData: ConfBusiness):
-        self.common=CommonBusiness()
-        self.confData = confData
-
-    @abstractmethod
-    def typeAnalyst(self):
-        pass
-
-    def getOutputAnalysisDir(self):
-        """
-        获取当前分析的输出目录
-        """
-        outputAnalysisDir = r"{}/{}".format(
-            self.confData.output_path, self.typeAnalyst())
-        dir.create_directory(outputAnalysisDir)
-
-        return outputAnalysisDir
-    
-    def analysisOfTurbine(self,
-                          dataFrame: pd.DataFrame,
-                          outputAnalysisDir,
-                          outputFilePath,
-                          confData: ConfBusiness,
-                          turbineName):
-        # dataFrame = self.filterCustom(dataFrame, confData)
-        self.turbineAnalysis(dataFrame, outputAnalysisDir,
-                             outputFilePath, confData, turbineName)
-
-    def turbineAnalysis(self,
-                        dataFrame: pd.DataFrame,
-                        outputAnalysisDir,
-                        outputFilePath,
-                        confData: ConfBusiness,
-                        turbineName):
-        pass
-    
-    def analysisOfTurbines(self, dataFrameMerge:pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
-        self.turbinesAnalysis(dataFrameMerge ,outputAnalysisDir,confData)
-
-    def turbinesAnalysis(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
-        pass

+ 0 - 13
wtoaamapi/apps/business/algorithm/commonBusiness.py

@@ -1,13 +0,0 @@
-import pandas as pd
-from confBusiness import Field_GeneratorTorque,ConfBusiness
-
-
-class CommonBusiness:
-    def isNone(self, value):
-        return value is None
-
-    def recalculationOfGeneratorSpeedforShow(self,dataFrame:pd.DataFrame,confData:ConfBusiness):   
-        if not self.isNone(confData.value_gen_speed_multiple) and confData.field_gen_speed in dataFrame.columns:
-            dataFrame[confData.field_gen_speed] = dataFrame[confData.field_gen_speed] * \
-                confData.value_gen_speed_multiple
-            dataFrame[Field_GeneratorTorque] = dataFrame[confData.field_gen_speed]

+ 0 - 197
wtoaamapi/apps/business/algorithm/cpAnalyst.py

@@ -1,197 +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 .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-
-class CpAnalyst(Analyst):
-    """
-    风电机组风能利用系数分析
-    """
-
-    def typeAnalyst(self):
-        return "cp"
-
-    def turbineAnalysis(self,
-                        dataFrame,
-                        outputAnalysisDir,
-                        outputFilePath,
-                        confData: ConfBusiness,
-                        turbineName):
-
-        self.cp(dataFrame, outputFilePath,
-                confData.field_wind_speed, confData.field_rotor_speed, confData.field_power, confData.field_pitch_angle1, confData.rotor_diameter, confData.density_air)
-
-    def cp(self, dataFrame, output_path, wind_speed_col, field_rotor_speed, power_col, pitch_col, rotor_diameter, air_density):
-        print('rotor_diameter={}  air_density={}'.format(
-            rotor_diameter, air_density))
-
-        dataFrame['power'] = dataFrame[power_col]  # Alias the power column
-        # Floor division by 10 and then multiply by 10
-        dataFrame['power_floor'] = (dataFrame[power_col] / 10).astype(int) * 10
-        dataFrame['wind_speed'] = dataFrame[wind_speed_col].astype(float)
-        dataFrame['rotor_speed'] = dataFrame[field_rotor_speed].astype(float)
-
-        # Power coefficient calculation
-        dataFrame['power'] = pd.to_numeric(dataFrame['power'], errors='coerce')
-        dataFrame['wind_speed'] = pd.to_numeric(
-            dataFrame['wind_speed'], errors='coerce')
-        rotor_diameter = pd.to_numeric(rotor_diameter, errors='coerce')
-        air_density = pd.to_numeric(air_density, errors='coerce')
-        print('rotor_diameter={}  air_density={}'.format(
-            rotor_diameter, air_density))
-        # Calculate cp
-        dataFrame['cp'] = dataFrame['power'] * 1000 / \
-            (0.5 * np.pi * air_density *
-             (rotor_diameter ** 2) / 4 * dataFrame['wind_speed'] ** 3)
-
-        # Group by power_floor and calculate mean, max, and min of the specified columns
-        grouped = dataFrame.groupby('power_floor').agg(
-            wind_speed=('wind_speed', 'mean'),
-            rotor_speed=('rotor_speed', 'mean'),
-            cp=('cp', 'mean'),
-            cp_max=('cp', 'max'),
-            cp_min=('cp', 'min'),
-        ).reset_index()
-
-        # grouped = dataFrame.groupby('power_floor').agg({
-        #     'wind_speed': 'mean',
-        #     'rotor_speed': 'mean',
-        #     'cp': ['mean', 'max', 'min']
-        # }).reset_index()
-
-        # Rename columns post aggregation for clarity
-        grouped.columns = ['power_floor', 'wind_speed',
-                           'rotor_speed', 'cp', 'cp_max', 'cp_min']
-
-        # Sort by power_floor
-        grouped = grouped.sort_values('power_floor')
-
-        # Write the dataframe to a CSV file
-        grouped.to_csv(output_path, index=False)
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.generate_cp_distribution(outputAnalysisDir, confData.farm_name)
-
-    def generate_cp_distribution(self, csvFileDirOfCp, farm_name, encoding='utf-8'):
-        """
-        Generates Cp distribution plots for turbines in a wind farm.
-
-        Parameters:
-        - csvFileDirOfCp: str, path to the directory containing input CSV files.
-        - farm_name: str, name of the wind farm.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """
-
-        output_path = csvFileDirOfCp
-
-        field_Name_Turbine = "turbine_name"
-        x_name = 'power_floor'
-        y_name = 'cp'
-        split_way = '_cp.csv'
-
-        sns.set_palette('deep')
-        res = pd.DataFrame()
-
-        for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):
-            for file_name in file_names:
-
-                if not file_name.endswith(".csv"):
-                    continue
-
-                file_path = os.path.join(root, file_name)
-                print(file_path)
-                frame = pd.read_csv(file_path, encoding=encoding)
-                turbine_name = file_name.split(split_way)[0]
-                frame[field_Name_Turbine] = turbine_name
-
-                res = pd.concat(
-                    [res, frame.loc[:, [field_Name_Turbine, x_name, y_name]]], axis=0)
-
-        ress = res.reset_index()
-
-        fig, ax = plt.subplots(figsize=(16, 8))
-        ax = sns.lineplot(x=x_name, y=y_name, data=ress,
-                          hue=field_Name_Turbine)
-        ax.set_title('Cp-Distribution')
-        # plt.legend(ncol=4)
-        plt.legend(title='turbine',bbox_to_anchor=(1.02, 0.5),ncol=2, loc='center left', borderaxespad=0.) 
-        plt.savefig(os.path.join(
-            output_path, "{}-Cp-Distribution.png".format(farm_name)), bbox_inches='tight', dpi=300)
-        plt.close()
-
-        grouped = ress.groupby(field_Name_Turbine)
-        for name, group in grouped:
-            color = ["lightgrey"] * len(ress[field_Name_Turbine].unique())
-            fig, ax = plt.subplots(figsize=(8, 8))
-            ax = sns.lineplot(x=x_name, y=y_name, data=ress, hue=field_Name_Turbine,
-                              palette=sns.color_palette(color), legend=False)
-            ax = sns.lineplot(x=x_name, y=y_name, data=group,
-                              color='darkblue', legend=False)
-            ax.set_title('turbine name={}'.format(name))
-            plt.savefig(os.path.join(output_path, "{}.png".format(
-                name)), bbox_inches='tight', dpi=120)
-            plt.close()
-
-    def plot_cp_distribution(self, csvFileDir,  farm_name):
-        field_Name_Turbine = "设备名"
-        x_name = 'power_floor'
-        y_name = 'cp'
-        split_way = '_cp.csv'
-        # Create the output path based on the farm name
-        output_path = csvFileDir  # output_path_template.format(farm_name)
-        # Ensure the output directory exists
-        os.makedirs(output_path, exist_ok=True)
-        print(csvFileDir)
-        # Initialize a DataFrame to store results
-        res = pd.DataFrame()
-
-        # Walk through the input directory to process each file
-        for root, _, file_names in dir.list_directory(csvFileDir):
-            for file_name in file_names:
-                full_path = os.path.join(root, file_name)
-                frame = pd.read_csv(full_path, encoding='gbk')
-                turbine_name = file_name.split(split_way)[0]
-                print("turbine_name={}".format(turbine_name))
-                frame[field_Name_Turbine] = turbine_name
-                res = pd.concat(
-                    [res, frame.loc[:, [field_Name_Turbine, x_name, y_name]]], axis=0)
-
-        # Reset index for plotting
-        ress = res.reset_index(drop=True)
-
-        # Plot combined Cp distribution for all turbines
-        fig = make_subplots(rows=1, cols=1)
-        for name, group in ress.groupby(field_Name_Turbine):
-            fig.add_trace(go.Scatter(
-                x=group[x_name], y=group[y_name], mode='lines', name=name))
-
-        fig.update_layout(title_text='{} Cp分布'.format(
-            farm_name), xaxis_title=x_name, yaxis_title=y_name)
-        fig.write_image(os.path.join(
-            output_path, "{}Cp分布.png".format(farm_name)), scale=3)
-
-        # Plot individual Cp distributions
-        unique_turbines = ress[field_Name_Turbine].unique()
-        for name in unique_turbines:
-            individual_fig = make_subplots(rows=1, cols=1)
-            # Add all turbines in grey
-            for turbine in unique_turbines:
-                group = ress[ress[field_Name_Turbine] == turbine]
-                individual_fig.add_trace(go.Scatter(
-                    x=group[x_name], y=group[y_name], mode='lines', name=turbine, line=dict(color='lightgrey')))
-
-            # Highlight the current turbine in dark blue
-            group = ress[ress[field_Name_Turbine] == name]
-            individual_fig.add_trace(go.Scatter(
-                x=group[x_name], y=group[y_name], mode='lines', name=name, line=dict(color='darkblue')))
-
-            individual_fig.update_layout(title_text='设备名={}'.format(name))
-            individual_fig.write_image(os.path.join(
-                output_path, "all-{}.png".format(name)), scale=2)

+ 0 - 116
wtoaamapi/apps/business/algorithm/cpTrendAnalyst.py

@@ -1,116 +0,0 @@
-import os
-import pandas as pd
-import numpy as np
-from plotly.subplots import make_subplots
-import plotly.graph_objects as go
-import matplotlib.pyplot as plt
-from .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-
-class CpTrendAnalyst(Analyst):
-    """
-    风电机组风能利用系数时序分析
-    """
-
-    def typeAnalyst(self):
-        return "cp_trend"
-
-    def turbineAnalysis(self,
-                        dataFrame,
-                        outputAnalysisDir,
-                        outputFilePath,
-                        confData: ConfBusiness,
-                        turbineName):
-
-        self.cp_trend(dataFrame, outputFilePath,
-                      confData.field_turbine_time, confData.field_wind_speed, confData.field_rotor_speed, confData.field_power, confData.field_pitch_angle1,
-                      confData.rotor_diameter, confData.density_air)
-
-    def cp_trend(self, dataFrame, outputFilePath,  time_col, wind_speed_col, generator_speed_col, power_col, pitch_col, rotor_diameter, density_air):
-        dataFrame['time_day'] = dataFrame[time_col].dt.date
-
-        # Assign columns and calculate 'cp'
-        dataFrame['wind_speed'] = dataFrame[wind_speed_col].astype(float)
-        dataFrame['rotor_speed'] = dataFrame[generator_speed_col].astype(float)
-        dataFrame['power'] = dataFrame[power_col]
-
-        # Power coefficient calculation
-        rotor_diameter = pd.to_numeric(rotor_diameter, errors='coerce')
-        air_density = pd.to_numeric(rotor_diameter, errors='coerce')
-        # Calculate cp
-        dataFrame['cp'] = dataFrame['power'] * 1000 / (0.5 * np.pi * air_density * (
-            rotor_diameter ** 2) / 4 * dataFrame['wind_speed'] ** 3)
-
-        # Group by day and aggregate
-        grouped = dataFrame.groupby('time_day').agg({
-            time_col: 'min',  # Assuming time_col is the datetime column for minimum time
-            'wind_speed': 'mean',
-            'rotor_speed': 'mean',
-            'cp': ['mean', 'max', 'min']
-        }).reset_index()
-
-        # Rename columns post aggregation for clarity
-        grouped.columns = ['time_day', 'time_', 'wind_speed',
-                           'rotor_speed', 'cp', 'cp_max', 'cp_min']
-
-        # Sort by day
-        grouped = grouped.sort_values('time_day')
-
-        # Write to CSV
-        grouped.to_csv(outputFilePath, index=False)
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.create_cp_trend_plots(outputAnalysisDir, confData.farm_name)
-
-    def create_cp_trend_plots(self, csvFileDirOfCp,  farm_name, encoding='utf-8'):
-        """
-        Generates and saves error bar plots for CP trend data stored in CSV files.
-
-        Parameters:
-        - csvFileDirOfCp: Path to the directory containing the input CSV files.
-        - farm_name: Name of the farm, used to format the output path.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """
-        time_day = 'time_day'
-        y_name = 'cp'
-        y_min = 'cp_min'
-        y_max = 'cp_max'
-        split_way = '_cp_trend.csv'
-
-        # Create the output directory if it does not exist
-        if not os.path.exists(csvFileDirOfCp):
-            os.makedirs(csvFileDirOfCp)
-
-        # Walk through the input directory to process each CSV file
-        for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):
-            for file_name in file_names:
-                if not file_name.endswith(".csv"):
-                    continue
-
-                # Read each CSV file
-                data = pd.read_csv(os.path.join(
-                    root, file_name), encoding=encoding)
-                data.loc[:, time_day] = pd.to_datetime(data.loc[:, time_day])
-                data[y_min] = data[y_name] - data[y_min]
-                data[y_max] = data[y_max] - data[y_name]
-                turbine_name = file_name.split(split_way)[0]
-
-                # Generate the plot
-                fig, ax = plt.subplots()
-                ax.errorbar(x=data[time_day], y=data[y_name], yerr=[data[y_min], data[y_max]],
-                            fmt='o', capsize=4, elinewidth=2, ecolor='lightgrey', mfc='dodgerblue')
-
-                ax.set_xlabel('time')
-                ax.set_ylabel('Cp')
-                # ax.set_ylim(-0.2, 8)
-                ax.set_title('{}={}'.format('turbine_name', turbine_name))
-                # 旋转x轴刻度标签
-                plt.xticks(rotation=45)
-
-
-                # Save the plot
-                plt.savefig(os.path.join(csvFileDirOfCp, "{}.png".format(
-                    turbine_name)), bbox_inches='tight', dpi=120)
-                plt.close()

+ 0 - 269
wtoaamapi/apps/business/algorithm/cpWindSpeedAnalyst.py

@@ -1,269 +0,0 @@
-import os
-
-import pandas as pd
-import plotly.graph_objects as go
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from behavior.analystWithGoodPoint import AnalystWithGoodPoint
-
-
-class CpWindSpeedAnalyst(AnalystWithGoodPoint):
-    """
-    风电机组风能利用系数分析
-    """
-
-    def typeAnalyst(self):
-        return "cp_windspeed"
-
-    def dataReprocess(self, dataFrameTurbines: pd.DataFrame) -> pd.DataFrame:
-        dataFrame = dataFrameTurbines.groupby([Field_CodeOfTurbine, Field_WindSpeedFloor]).agg(
-            cp=(Field_Cp, 'median'),
-            cp_max=(Field_Cp, 'max'),
-            cp_min=(Field_Cp, 'min'),
-        ).reset_index()
-
-        dataFrame.columns = [Field_CodeOfTurbine, Field_WindSpeedFloor,
-                             Field_Cp, Field_CpMax, Field_CpMin]
-        dataFrame = dataFrame.sort_values(
-            by=[Field_CodeOfTurbine, Field_WindSpeedFloor], ascending=[True, True])
-
-        return dataFrame
-
-    def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
-        dictionary = self.processTurbineData(turbineCodes, conf, [
-                                             Field_DeviceCode, Field_Time, Field_WindSpeed, Field_ActiverPower])
-        dataFrameOfTurbines = self.userDataFrame(
-            dictionary, conf.dataContract.configAnalysis, self)
-        # 检查所需列是否存在
-        required_columns = {Field_WindSpeedFloor, Field_Cp}
-        if not required_columns.issubset(dataFrameOfTurbines.columns):
-            raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-
-        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)]
-
-            dataFrame = self.dataReprocess(currDataFrameOfTurbines)
-
-            returnData = self.buildChart(
-                dataFrame, outputAnalysisDir, conf, currTurbineModeInfo)
-            returnDatas.append(returnData)
-
-        returnResult = pd.concat(returnDatas, ignore_index=True)
-
-        return returnResult
-
-    def buildChart(self, dataFrameOfTurbines: pd.DataFrame, outputAnalysisDir, conf: Contract, turbineModelInfo: pd.Series):
-        # Create the main Cp distribution plot using Plotly
-        fig = go.Figure()
-        # colors = px.colors.sequential.Turbo
-
-        # 创建一个列表来存储各个风电机组的数据
-        turbine_data_list = []
-
-        for turbineCode in dataFrameOfTurbines[Field_CodeOfTurbine].unique():
-            group = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine] == turbineCode]
-            currTurbineInfo = self.common.getTurbineInfo(
-                conf.dataContract.dataFilter.powerFarmID, turbineCode, self.turbineInfo)
-            fig.add_trace(go.Scatter(x=group[Field_WindSpeedFloor], y=group[Field_Cp],
-                                     mode='lines',
-                                     # line=dict(color=colors[idx % len(colors)]),
-                                     name=currTurbineInfo[Field_NameOfTurbine]))
-            # 提取数据
-            turbine_data_total = {
-                "engineName": currTurbineInfo[Field_NameOfTurbine],
-                "engineCode": turbineCode,
-                "xData": group[Field_WindSpeedFloor].tolist(),
-                "yData": group[Field_Cp].tolist(),
-                }
-            turbine_data_list.append(turbine_data_total)
-
-
-
-        fig.update_layout(title={'text': f'风能利用系数分布-{turbineModelInfo[Field_MachineTypeCode]}', 'x': 0.5},
-                          xaxis_title='风速', yaxis_title='风能利用系数',
-                          legend=dict(
-            orientation="h",  # Horizontal orientation
-            xanchor="center",  # Anchor the legend to the center
-            x=0.5,  # Position legend at the center of the x-axis
-            y=-0.2,  # Position legend below the x-axis
-            # itemsizing='constant',  # Keep the size of the legend entries constant
-            # itemwidth=50
-        ),
-            xaxis=dict(range=[0, 26], tickmode='linear',
-                       dtick=1, tickangle=-45),
-            yaxis=dict(
-            dtick=self.axisStepCp,
-            range=[self.axisLowerLimitCp,
-                   self.axisUpperLimitCp]
-        )
-        )
-
-        engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-        if isinstance(engineTypeCode, pd.Series):
-            engineTypeCode = engineTypeCode.iloc[0]
-
-        engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-        if isinstance(engineTypeName, pd.Series):
-            engineTypeName = engineTypeName.iloc[0]
-        # 构建最终的JSON对象
-        json_output = {
-            "analysisTypeCode": "风电机组风能利用系数分析",
-            "typecode": turbineModelInfo[Field_MillTypeCode],
-            "engineCode": engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "title": f'风能利用系数分布-{turbineModelInfo[Field_MachineTypeCode]}',
-            "xaixs": "风速",
-            "yaixs": "风能利用系数",
-            "data": turbine_data_list
-        }
-        output_json_path = os.path.join(outputAnalysisDir, f"{turbineModelInfo[Field_MillTypeCode]}.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-        # 保存HTML
-        # htmlFileName = f"{self.powerFarmInfo[Field_PowerFarmName].iloc[0]}-{turbineModelInfo[Field_MillTypeCode]}-Cp-Distribution.html"
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig.write_html(htmlFilePath)
-
-        result_rows = []
-        # result_rows.append({
-        #    Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #    Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #    Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #    Field_CodeOfTurbine: Const_Output_Total,
-        #    Field_Return_FilePath: htmlFilePath,
-        #    Field_Return_IsSaveDatabase: True
-        # })
-
-        # 如果需要返回DataFrame,可以包含文件路径
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: turbineModelInfo[Field_MillTypeCode],
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-
-        # Generate individual turbine plots
-        for turbineCode, group in dataFrameOfTurbines.groupby(Field_CodeOfTurbine):
-            currTurbineInfo = self.common.getTurbineInfo(
-                conf.dataContract.dataFilter.powerFarmID, turbineCode, self.turbineInfo)
-
-            # 创建一个列表来存储各个风电机组的数据
-            turbine_data_list_each = []
-
-            fig = go.Figure()
-            for turbineCode in dataFrameOfTurbines[Field_CodeOfTurbine].unique():
-                tempDataFrame = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine] == turbineCode]
-                tempTurbineInfo = self.common.getTurbineInfo(
-                    conf.dataContract.dataFilter.powerFarmID, turbineCode, self.turbineInfo)
-                fig.add_trace(go.Scatter(x=tempDataFrame[Field_WindSpeedFloor],
-                                         y=tempDataFrame[Field_Cp],
-                                         mode='lines',
-                                         line=dict(color='lightgray', width=1),
-                                         showlegend=False))
-
-                # 提取数据
-                turbine_data_other_each = {
-                    "engineName": tempTurbineInfo[Field_NameOfTurbine],
-                    "engineCode": turbineCode,
-                    "xData": tempDataFrame[Field_WindSpeedFloor].tolist(),
-                    "yData": tempDataFrame[Field_Cp].tolist(),
-                }
-                turbine_data_list_each.append(turbine_data_other_each)
-
-            fig.add_trace(go.Scatter(x=group[Field_WindSpeedFloor], y=group[Field_Cp], mode='lines', line=dict(
-                color='darkblue'), showlegend=False))
-            fig.update_layout(title=f'风机: {currTurbineInfo[Field_NameOfTurbine]}',
-                              xaxis_title='风速', yaxis_title='风能利用系数',
-                              xaxis=dict(
-                                  range=[0, 26], tickmode='linear', dtick=1, tickangle=-45),
-                              yaxis=dict(
-                                  dtick=self.axisStepCp,
-                                  range=[self.axisLowerLimitCp,
-                                         self.axisUpperLimitCp]
-                              )
-                              )
-
-            engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-            if isinstance(engineTypeCode, pd.Series):
-                engineTypeCode = engineTypeCode.iloc[0]
-
-            engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-            if isinstance(engineTypeName, pd.Series):
-                engineTypeName = engineTypeName.iloc[0]
-            # 构建最终的JSON对象
-            json_output = {
-                "analysisTypeCode": "风电机组风能利用系数分析",
-                "typecode": turbineModelInfo[Field_MillTypeCode],
-                "engineCode": engineTypeCode,
-                "engineTypeName": engineTypeName,
-                "title": f'风机: {currTurbineInfo[Field_NameOfTurbine]}',
-                "xaixs": "风速",
-                "yaixs": "风能利用系数",
-                "data": turbine_data_list_each
-
-            }
-
-            # 将JSON对象保存到文件
-            output_json_path_each = os.path.join(outputAnalysisDir,
-                                                 f"{currTurbineInfo[Field_NameOfTurbine]}.json")
-            with open(output_json_path_each, 'w', encoding='utf-8') as f:
-                import json
-                json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-            # 保存图像
-            # pngFileName = f"{currTurbineInfo[Field_NameOfTurbine]}.png"
-            # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-            # fig.write_image(pngFilePath, scale=3)
-
-            # 保存HTML
-            # htmlFileName = f"{currTurbineInfo[Field_NameOfTurbine]}.html"
-            # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-            # fig.write_html(htmlFilePath)
-
-            # result_rows.append({
-            #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-            #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            #     Field_CodeOfTurbine: currTurbineInfo[Field_CodeOfTurbine],
-            #     Field_Return_FilePath: pngFilePath,
-            #     Field_Return_IsSaveDatabase: False
-            # })
-
-            # 如果需要返回DataFrame,可以包含文件路径
-            result_rows.append({
-                Field_Return_TypeAnalyst: self.typeAnalyst(),
-                Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-                Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-                Field_CodeOfTurbine: turbineCode,
-                Field_Return_FilePath: output_json_path_each,
-                Field_Return_IsSaveDatabase: True
-            })
-
-            # result_rows.append({
-            #    Field_Return_TypeAnalyst: self.typeAnalyst(),
-            #    Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            #    Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            #    Field_CodeOfTurbine: currTurbineInfo[Field_CodeOfTurbine],
-            #    Field_Return_FilePath: htmlFilePath,
-            #    Field_Return_IsSaveDatabase: True
-            # })
-
-        result_df = pd.DataFrame(result_rows)
-
-        return result_df

+ 0 - 153
wtoaamapi/apps/business/algorithm/dalAnalyst.py

@@ -1,153 +0,0 @@
-from logging import Logger
-import pandas as pd
-from common.commonBusiness import CommonBusiness
-from algorithmContract.const import *
-from algorithmContract.confBusiness import *
-from utils.rdbmsUtil.databaseUtil import DatabaseUtil
-from sqlalchemy.orm import Session
-from sqlalchemy.sql import text
-
-
-class DALAnalyst:
-    def __init__(self, logger: Logger, dbUtil: dict[str, DatabaseUtil]) -> None:
-        self.logger = logger
-        self.dbUtil = dbUtil
-
-    def loadPowerFarmInfos(self, powerFarmID: str):
-        """
-        获取场站基础信息
-        """
-        dbUtil: DatabaseUtil = self.dbUtil[DATABASE_BusinessFoundationDb]
-
-        with dbUtil.session_scope() as session:
-            # 执行原生 SQL 查询
-            result = session.execute(text(
-                f"SELECT field_code,company_code,field_name,density,state,engine_number,rated_capacity_number,province_id,province_name,city_id,city_name,longitude,latitude,elevation_height,power_contract_url FROM wind_field where del_state=0 and field_code='{powerFarmID}'")).fetchall()
-            # 获取查询结果的列名
-            columns = ['field_code', 'company_code', 'field_name', 'density', 'state', 'engine_number', 'rated_capacity_number',
-                       'province_id', 'province_name', 'city_id', 'city_name', 'longitude', 'latitude', 'elevation_height', 'power_contract_url']
-
-            # 将查询结果转换为 DataFrame
-            dataFrame = pd.DataFrame(result, columns=columns)
-
-        return dataFrame
-
-    def loadTurbineInfos(self, powerFarmID: str):
-        """
-        获取风电机组基础信息
-        """
-        dbUtil: DatabaseUtil = self.dbUtil[DATABASE_BusinessFoundationDb]
-
-        with dbUtil.session_scope() as session:
-            # 执行原生 SQL 查询
-            result = session.execute(text(
-                f"SELECT field_code,engine_code,engine_name,mill_type_code,rated_capacity,elevation_height,hub_height,state,longitude,latitude,sightcing FROM wind_engine_group where del_state=0 and field_code='{powerFarmID}'")).fetchall()
-            # 获取查询结果的列名
-            columns = ['field_code', 'engine_code', 'engine_name', Field_MillTypeCode, 'rated_capacity', 'elevation_height', 'hub_height',
-                       'state', 'longitude', 'latitude', 'sightcing']
-
-            # 将查询结果转换为 DataFrame
-            dataFrame = pd.DataFrame(result, columns=columns)
-
-        return dataFrame
-
-    def loadDataTransfer(self, powerFarmID: str, dataBatchNum: str):
-        """
-        获取数据操作信息
-        """
-        dbUtil: DatabaseUtil = self.dbUtil[DATABASE_BusinessFoundationDb]
-
-        with dbUtil.session_scope() as session:
-            # 执行原生 SQL 查询
-            result = session.execute(text(
-                f"SELECT field_code, batch_code, engine_count, transfer_type, transfer_addr, time_granularity FROM data_transfer where field_code='{powerFarmID}' and batch_code='{dataBatchNum}'  ")).fetchall()
-            # 获取查询结果的列名
-            columns = ['field_code', 'batch_code', 'engine_count',
-                       'transfer_type', 'transfer_addr', 'time_granularity']
-
-            # 将查询结果转换为 DataFrame
-            dataFrame = pd.DataFrame(result, columns=columns)
-
-        return dataFrame
-
-    def loadTurbineModelInfos(self, turbineModels: list):
-        """
-        获取型号基础信息
-        """
-        if len(turbineModels) <= 0:
-            return pd.DataFrame()
-
-        turbineModelStr = ", ".join(
-            f"'{model}'" for model in turbineModels)  # 使用%s作为占位符,稍后可以替换为实际值
-
-        dbUtil: DatabaseUtil = self.dbUtil[DATABASE_BusinessFoundationDb]
-
-        with dbUtil.session_scope() as session:
-            # 执行原生 SQL 查询
-            result = session.execute(text(f"SELECT mill_type_code,machine_type_code,manufacturer_name,manufacturer_code,brand,tower_height,vane_long,curved_motion_type,combination,power_criterion_url,rotor_diameter,rotational_speed_ratio,rated_wind_speed,rated_cut_in_windspeed,rated_cut_out_windspeed FROM wind_engine_mill where del_state=0 and state=1 and mill_type_code in ({turbineModelStr})"
-                                          )).fetchall()
-            # 获取查询结果的列名
-            columns = [Field_MillTypeCode, Field_MachineTypeCode, Field_ManufacturerName, Field_ManufacturerCode, Field_Brand, Field_HubHeight, Field_VaneLong, Field_MotionType,
-                       Field_Combination, Field_PowerCriterionURL, Field_RotorDiameter,  Field_RSR, Field_RatedWindSpeed, Field_CutInWS, Field_CutOutWS]
-
-            # 将查询结果转换为 DataFrame
-            dataFrame = pd.DataFrame(result, columns=columns)
-
-        return dataFrame
-
-    def loadWeatherStationInfos(self, powerFarmID: str):
-        """
-        获取气象站(测风塔)基础信息
-        """
-        dbUtil: DatabaseUtil = self.dbUtil[DATABASE_BusinessFoundationDb]
-
-        with dbUtil.session_scope() as session:
-            # 执行原生 SQL 查询
-            result = session.execute(text(
-                f"select y.field_code ,x.anemometer_code ,x.anemometer_name ,x.longitude ,x.latitude  from anemometer_tower as x inner join anemometer_tower_relation as y on x.anemometer_code =y.tower_code where x.del_state=0 and x.state=1 and y.field_code='{powerFarmID}'")).fetchall()
-            # 获取查询结果的列名
-            columns = ['field_code', 'anemometer_code',
-                       'anemometer_name', 'longitude', 'latitude']
-
-            # 将查询结果转换为 DataFrame
-            dataFrame = pd.DataFrame(result, columns=columns)
-
-        return dataFrame
-
-    def processContractData(self, common: CommonBusiness, powerFarmID: str, airDensity: float, turbineModelInfo: pd.DataFrame):
-        """
-        获取合同功率曲线数据
-        """
-        dataFrameMerge = pd.DataFrame()
-        turbineModels = turbineModelInfo[Field_MillTypeCode]
-        turbineModelStr = ", ".join(f"'{model}'" for model in turbineModels)
-
-        dbUtil: DatabaseUtil = self.dbUtil[DATABASE_BusinessFoundationDb]
-
-        with dbUtil.session_scope() as session:
-            # 执行原生 SQL 查询
-            result = session.execute(text(
-                f"SELECT  field_code, mill_type_code,active_power,wind_speed as wind_velocity FROM power_word_relation_contract  where field_code='{powerFarmID}' and mill_type_code in ({turbineModelStr})")).fetchall()
-
-            # 获取查询结果的列名
-            columns = [Field_PowerFarmCode, Field_MillTypeCode,
-                       Field_ActiverPower, Field_WindSpeed]
-
-            # 将查询结果转换为 DataFrame
-            contractPowerCurves = pd.DataFrame(result, columns=columns)
-
-        grouped = contractPowerCurves.groupby(Field_MillTypeCode)
-
-        for name, group in grouped:
-            print("current turbine model :", name)
-            model = turbineModelInfo[turbineModelInfo[Field_MillTypeCode] == name]
-            if len(model) <= 0:
-                continue
-
-            dataFrame = common.calculateCp2(
-                group, airDensity, model[Field_RotorDiameter].iloc[0], Field_WindSpeed, Field_ActiverPower)
-
-            dataFrameMerge = pd.concat(
-                [dataFrameMerge, dataFrame], axis=0, sort=False)
-
-        return dataFrameMerge

+ 0 - 21
wtoaamapi/apps/business/algorithm/dataIntegrityOfMinuteAnalyst.py

@@ -1,21 +0,0 @@
-import os
-import pandas as pd
-import numpy as np
-import pandas as pd
-import matplotlib.pyplot as plt
-import seaborn as sns
-import plotly.graph_objects as go
-from plotly.subplots import make_subplots
-from geopy.distance import geodesic
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness,charset_unify
-from .dataIntegrityOfSecondAnalyst import DataIntegrityOfSecondAnalyst
-
-
-class DataIntegrityOfMinuteAnalyst(DataIntegrityOfSecondAnalyst):
-    """
-    风电机组秒级数据完整度分析
-    """
-
-    def typeAnalyst(self):
-        return "data_integrity_minute"

+ 0 - 271
wtoaamapi/apps/business/algorithm/dataIntegrityOfSecondAnalyst.py

@@ -1,271 +0,0 @@
-import os
-import pandas as pd
-import numpy as np
-import matplotlib.pyplot as plt
-import seaborn as sns
-import plotly.graph_objects as go
-from plotly.subplots import make_subplots
-from geopy.distance import geodesic
-from behavior.analystNotFilter import AnalystNotFilter
-from utils.directoryUtil import DirectoryUtil as dir
-from algorithmContract.confBusiness import *
-import calendar
-import random
-from datetime import datetime
-from algorithmContract.contract import Contract
-
-
-class DataIntegrityOfSecondAnalyst(AnalystNotFilter):
-    """
-    风电机组秒级数据完整度分析
-    """
-
-    def typeAnalyst(self):
-        return "data_integrity_second"
-
-    def turbinesAnalysis(self,  outputAnalysisDir, conf: Contract, turbineCodes):
-        select = [Field_DeviceCode, Field_Time, Field_ActiverPower, Field_WindSpeed, Field_NacPos, Field_WindDirection, Field_RotorSpeed, Field_GeneratorSpeed, Field_GeneratorTorque, Field_AngleIncluded, Field_EnvTemp, Field_NacTemp, Field_PitchAngel1, Field_PitchAngel2, Field_PitchAngel3]
-
-        dictionary = self.processTurbineData(turbineCodes, conf, select)
-        dataFrameOfTurbines = self.userDataFrame(
-            dictionary, conf.dataContract.configAnalysis, self)
-
-        turbrineInfos = self.common.getTurbineInfos(
-            conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
-
-        currDataFrameOfTurbines = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine].isin(
-            turbineCodes)]
-        # 将 currTurbineInfos 转换为字典
-        currTurbineInfosDict = turbrineInfos.set_index(
-            Field_CodeOfTurbine)[Field_NameOfTurbine].to_dict()
-        # 使用 map 函数来填充 Field_NameOfTurbine 列
-        currDataFrameOfTurbines[Field_NameOfTurbine] = currDataFrameOfTurbines[Field_CodeOfTurbine].map(
-            currTurbineInfosDict).fillna("")
-
-        groupedDataFrame = self.dataIntegrityByMonth(
-            dataFrameOfTurbines, conf, Field_NameOfTurbine)
-        print("groupedDataFrame : \n {}".format(groupedDataFrame.head()))
-
-        return self.plotByAllMonth(groupedDataFrame, outputAnalysisDir, self.powerFarmInfo[Field_PowerFarmName].iloc[0], Field_NameOfTurbine, conf)
-
-    def fullMonthIndex(self, start_time, end_time, turbine_name, new_frame):
-        months = (end_time.year - start_time.year) * \
-            12 + end_time.month - start_time.month
-        month_range = ['%04d-%02d' % (int(start_time.year + mon//12), int(mon % 12+1))
-                       for mon in range(start_time.month-1, start_time.month+months)]
-        month_index = pd.DataFrame(month_range, columns=[Field_YearMonth])
-
-        plot_res = pd.DataFrame()
-        grouped = new_frame.groupby(turbine_name)
-        for name, group in grouped:
-            group = pd.merge(group, month_index,
-                             on=Field_YearMonth, how='outer')
-            group['数据完整度%'] = group['数据完整度%'].fillna(0)
-            group[turbine_name] = name
-            group['year'] = group[Field_YearMonth].apply(
-                lambda x: str(x).split('-')[0])
-            group['month'] = group[Field_YearMonth].apply(
-                lambda x: str(x).split('-')[1])
-            plot_res = pd.concat([plot_res, group], axis=0, sort=False)
-
-        return plot_res
-
-    def get_time_space(self,df, time_str):
-        """
-        :return: 查询时间间隔(单位:秒)
-        """
-        df1 = pd.DataFrame(df[time_str])
-        df1['chazhi'] = df1[time_str].shift(-1) - df1[time_str]
-        result = df1.sample(int(df1.shape[0] / 100))['chazhi'].value_counts().idxmax().seconds
-        del df1
-        return result
-
-    def dataIntegrityByMonth(self, dataFrameMerge: pd.DataFrame, conf: Contract, Field_NameOfTurbine):
-        grouped = dataFrameMerge.groupby([dataFrameMerge.loc[:, Field_Time].dt.year.rename('year'),
-                                          dataFrameMerge.loc[:, Field_Time].dt.month.rename(
-                                              'month'),
-                                          dataFrameMerge.loc[:, Field_NameOfTurbine]]).agg({'count'})[Field_Time].rename({'count': '长度'}, axis=1)
-
-        new_frame = grouped.reset_index('month')
-        # timeGranularity = self.dataTransfer[self.dataTransfer[Field_TransferType] == Const_TimeGranularity_Second][Field_TimeGranularity].iloc[0] if self.typeAnalyst(
-        # ) == "data_integrity_second" else self.dataTransfer[self.dataTransfer[Field_TransferType] == Const_TimeGranularity_Minute][Field_TimeGranularity].iloc[0]
-        timeGranularity=self.get_time_space(dataFrameMerge,Field_Time)
-        self.logger.info(f"{self.typeAnalyst()}   timeGranularity-->{timeGranularity}")
-
-        new_frame = new_frame.reset_index()
-
-        new_frame['数据完整度'] = (100 * new_frame['长度'] / (new_frame.apply(lambda row: calendar.monthrange(
-            row['year'], row['month'])[1] * 24 * 3600 / timeGranularity, axis=1))).round(decimals=0)
-
-        new_frame = new_frame.rename(columns={'数据完整度': '数据完整度%'})
-        new_frame['month'] = new_frame['month'].astype(
-            str).apply(lambda x: x.zfill(2))
-        new_frame[Field_YearMonth] = new_frame['year'].astype(
-            str) + '-' + new_frame['month'].astype(str)
-
-        beginTime = None
-        if not self.common.isNone(conf.dataContract.dataFilter.beginTime):
-            beginTime = conf.dataContract.dataFilter.beginTime
-        else:
-            beginTime = dataFrameMerge[Field_Time].min().strftime(
-                '%Y-%m-%d %H:%M:%S')
-        endTime = None
-        if not self.common.isNone(conf.dataContract.dataFilter.endTime):
-            endTime = conf.dataContract.dataFilter.endTime
-        else:
-            endTime = dataFrameMerge[Field_Time] .max().strftime(
-                '%Y-%m-%d %H:%M:%S')
-
-        beginTime = datetime.strptime(beginTime, '%Y-%m-%d %H:%M:%S')
-        endTime = datetime.strptime(endTime, '%Y-%m-%d %H:%M:%S')
-
-        new_frame = self.fullMonthIndex(
-            beginTime, endTime, Field_NameOfTurbine, new_frame)
-
-        return new_frame
-
-    def plotByAllMonth(self, groupedDataFrame, outputAnalysisDir, farmName, fieldTurbineName, conf: Contract):
-        title = '数据完整度检测(%)'
-        # 根据场景决定索引和列的方向
-        if len(set(groupedDataFrame[Field_YearMonth])) > len(set(groupedDataFrame[fieldTurbineName])):
-            result = groupedDataFrame.pivot(
-                values="数据完整度%", index=fieldTurbineName, columns=Field_YearMonth)
-            x_labels = result.columns.tolist()  # 月份
-            y_labels = result.index.tolist()    # 风机名
-            x_axis_title = "日期"
-            y_axis_title = "机组"
-            # 构建最终的JSON对象
-            json_output = {
-                "analysisTypeCode": "数据完整度检测(%)",
-                "engineCode": "",
-                "engineTypeName": "",
-                "xaixs": "日期",
-                "yaixs": "机组",
-                "data": [{
-                    "engineName": "",
-                    "engineCode": "",
-                    "title": f' 数据完整度%',
-                    "xData": x_labels,
-                    "yData": y_labels,
-                    "ZData": result.values.tolist(),
-                }]
-            }
-        else:
-            result = groupedDataFrame.pivot(
-                values="数据完整度%", index=Field_YearMonth, columns=fieldTurbineName)
-            x_labels = result.columns.tolist()  # 风机名
-            y_labels = result.index.tolist()    # 月份
-            x_axis_title = "机组"
-            y_axis_title = "日期"
-            # 构建最终的JSON对象
-            json_output = {
-                "analysisTypeCode": "数据完整度检测(%)",
-                "engineCode": "",
-                "engineTypeName": "",
-                "xaixs": "机组",
-                "yaixs": "日期",
-                "data": [{
-                    "engineName": "",
-                    "engineCode": "",
-                    "title": f' 数据完整度%',
-                    "xData": x_labels,
-                    "yData": y_labels,
-                    "ZData": result.values.tolist(),
-                }]
-            }
-
-        # # 创建热图
-        # 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(
-            z=result.values,
-            x=x_labels,
-            y=y_labels,
-            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,
-            texttemplate="%{text}",  # Format the text display inside cells
-        ))
-
-        # 更新图形布局
-        fig.update_layout(
-            title={'text': title, 'x': 0.5},
-            # xaxis_nticks=len(x_labels),
-            xaxis=dict(tickmode='array', tickvals=x_labels,
-                       ticktext=x_labels, tickangle=-45, title=x_axis_title),
-            yaxis=dict(tickmode='array', tickvals=y_labels,
-                       ticktext=y_labels, title=y_axis_title),
-            # xaxis=dict(tickmode='array', tickvals=list(range(len(x_labels))), ticktext=x_labels, tickangle=-45, title=x_axis_title),
-            # yaxis=dict(tickmode='array', tickvals=list(range(len(y_labels))), ticktext=y_labels, title=y_axis_title),
-            autosize=True,
-            # width=len(x_labels) * 80,  # Adjust width and height as needed
-            # height=len(y_labels) * 80,
-            margin=dict(l=50, r=50, b=100, t=100),  # 调整边距以确保标签完整显示
-            # Transparent background to show cell borders
-            plot_bgcolor='rgba(0,0,0,0)'
-        )
-
-        fig.update_traces(
-            xgap=1,
-            ygap=1
-        )
-
-        result_rows = []
-
-        # 将JSON对象保存到文件
-        output_json_path = os.path.join(outputAnalysisDir, f"Data_Integrity_Of_Second_Analyst.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-        # 保存图像
-        # pngFileName = f'{farmName}数据完整度分析.png'
-        # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-        # fig.write_image(pngFilePath, scale=3)
-
-        # 保存HTML
-        # htmlFileName = f'{farmName}数据完整度分析.html'
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig.write_html(htmlFilePath)
-
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: Const_Output_Total,
-        #     Field_Return_FilePath: pngFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: Const_Output_Total,
-            Field_MillTypeCode: 'total',
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-
-        result_df = pd.DataFrame(result_rows)
-
-        return result_df

+ 0 - 435
wtoaamapi/apps/business/algorithm/dataMarker.py

@@ -1,435 +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
-
-# 限功率识别策略(参考):1. 功率<1100,and  叶片角度>0.5 ; 2. 功率<1250 and 叶片角度>1.5 ; 3. 功率<1400 and 叶片角度>2.5 ;
-
-class DataMarker:
-    
-    #选取时间、风速、功率数据
-    def preprocessData(self,dataFrame:pd.DataFrame):
-        timeStamp = dataFrame[Field_Time]
-        activePower = dataFrame[Field_ActiverPower]
-        windSpeed = dataFrame[Field_WindSpeed]
-        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:pd.DataFrame,dataFrame:pd.DataFrame):
-        lowLimitActivePower=10.0
-        lowLimitWindSpeed=0.0
-        # 初始化标签列
-        # 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[Field_LableFlag]=0
-        dataFramePartOfSCADA = dataFramePartOfSCADA.values
-        SM = dataFramePartOfSCADA.shape #(52561,4)
-        # SM = dataFramePartOfSCADA[((dataFramePartOfSCADA[Field_ActiverPower].notna()) & (dataFramePartOfSCADA[Field_WindSpeed].notna()))].shape #(52561,4)
-        AA = SM[0] -1
-        nCounter1 = 0 
-        DzMarch809_0 = np.zeros((AA, 3)) 
-        Point_line = np.zeros(AA, dtype=int)  
-        APower = dataFrame[Field_ActiverPower].values
-        WSpeed = dataFrame[Field_WindSpeed ].values
-
-        for i in range(AA):
-            if (APower[i] > lowLimitActivePower) & (WSpeed[i] > lowLimitWindSpeed):
-                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):
-        end=binNumOfPower - TopP
-        for i in range(1, binNumOfPower - TopP):  
-            if i>=end:
-                continue
-            
-            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):   
-                if n-1>=binNumOfWindSpeed:
-                    continue
-                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,dataFrame:pd.DataFrame,ratedPower,cusInWS,cusOutWS):
-        dataFramePartOfSCADA = self.preprocessData(dataFrame)
-        powerMax = dataFrame[Field_ActiverPower].max()
-        
-        # ratedPower=dataFrame[Field_RatedPower].iloc[0]
-        # cusInWS=dataFrame[Field_CutInWS].iloc[0]
-        # cusOutWS=dataFrame[Field_CutOutWS].iloc[0]
-        
-        if pd.isna(ratedPower):
-            raise CustomError(109)
-        if pd.isna(cusInWS):
-            raise CustomError(107)
-        if pd.isna(cusOutWS):
-            raise CustomError(107)
-    
-        binNumOfPower, binNumOfWindSpeed = self.calculateIntervals(powerMax,ratedPower, cusOutWS)
-        TopP = self.calculateTopP(powerMax,ratedPower)
-        # 根据功率阈值对数据进行标签分配
-        DzMarch809,nCounter1,dataFramePartOfSCADA,Point_line,SM = self.chooseData(dataFramePartOfSCADA,dataFrame)
-        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
-        mEnd=binNumOfPower - TopP
-        for m in range(binNumOfPower - TopP, 0, -1):
-            if m>=mEnd:
-                continue
-
-            for n in range(int(np.floor(int(cusInWS) / 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,ratedPower,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=[Field_LableFlag])
-
-        dataFrame = pd.concat([dataFrame,A],axis=1) 
-        dataFrame[Field_LableFlag]=dataFrame[Field_LableFlag].fillna(0)
-        """
-        标识	说明
-        5	坏点
-        4	限功率点
-        1	好点
-        0	null
-        -1	P<=10
-        """
-        print("lab unique :",dataFrame[Field_LableFlag].unique())
-        # data=dataFrame[dataFrame[Field_LableFlag]==1]        
-        # self.plotData(data[Field_NameOfTurbine].iloc[0],data[Field_WindSpeed],data[Field_ActiverPower])
-
-        return dataFrame
-        
-
-    if __name__ == '__main__':
-        main()
-
-
-

+ 0 - 540
wtoaamapi/apps/business/algorithm/dataMarkerOfScada3.py

@@ -1,540 +0,0 @@
-import numpy as np
-import pandas as pd
-import os
-import plotly.graph_objects as go
-
-# 声明全局变量
-fieldActivePower = "功率"
-fieldWindSpeed = "风速"
-fieldPitchAngle = "叶片角度"
-PRated = 1500  # 风机额定功率
-VCutOut = 25  # 切出风速
-VCutIn = 3  # 切入风速
-VRated = 10  # 额定风速
-VNum = int(VCutOut / 0.25)  # 风速分区数量
-
-# 读取数据函数
-
-
-def read_data():
-    # 读取 CSV 文件
-    March809 = pd.read_csv(
-        "./data/DataClassificationIdentification/A01-G.csv", encoding="utf-8")
-    IdealCurve = pd.read_csv(
-        "./data/DataClassificationIdentification/A型风机设计功率曲线.csv", encoding="utf-8")
-    return March809, IdealCurve
-
-# 计算统计信息函数
-
-
-def calculate_statistics(March809, IdealCurve):
-    AA = len(March809)
-    BB = len(IdealCurve)
-    PowerMax = March809[fieldActivePower].max()
-    PowerRated = int(np.ceil(PowerMax / 100) * 100)
-    PNum = PowerRated // 25  # 功率分区数量
-
-    # 计算实际发电量
-    EPActualTotal = March809[March809[fieldActivePower]
-                             >= 0][fieldActivePower].sum() / 6
-    WindSpeedAvr = March809[fieldWindSpeed].mean()
-
-    # 计算风机可利用率
-    nShouldGP = np.sum(March809[fieldWindSpeed] >= VCutIn)
-    nRealGP = np.sum((March809[fieldWindSpeed] >= VCutIn)
-                     & (March809[fieldActivePower] > 0))
-    TurbineRunRate = (nRealGP / nShouldGP * 100) if nShouldGP > 0 else 0
-
-    # 计算理论发电量
-    EPIdealTotalAAA = 0
-    for i in range(AA):
-        nWhichBin = 0
-        for m in range(BB - 1):
-            if IdealCurve.iloc[m][fieldWindSpeed] < March809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
-                nWhichBin = m
-                break
-        if nWhichBin > 0:
-            IdealPower = (March809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
-                                                                                                            [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
-            EPIdealTotalAAA += IdealPower / 6
-
-    return AA, BB, PNum, EPActualTotal, WindSpeedAvr, TurbineRunRate, EPIdealTotalAAA
-
-# 分类数据函数
-
-
-def classify_data(March809, PNum, VNum):
-    DzMarch809 = March809[March809[fieldActivePower] > 0]
-    nCounter1 = len(DzMarch809)
-
-    XBoxNumber = np.ones((PNum, VNum), dtype=int)
-    for i in range(nCounter1):
-        nWhichP = np.digitize(
-            DzMarch809.iloc[i][fieldActivePower], np.arange(0, PNum * 25, 25)) - 1
-        nWhichV = np.digitize(
-            DzMarch809.iloc[i][fieldWindSpeed], np.arange(0.125, VNum * 0.25, 0.25))
-        if nWhichP < PNum and nWhichV < VNum:
-            XBoxNumber[nWhichP, nWhichV] += 1
-    XBoxNumber -= 1
-
-    return DzMarch809, XBoxNumber
-
-# 计算百分比函数
-
-
-def compute_percentages(XBoxNumber, PNum, VNum):
-    PBoxPercent = np.zeros((PNum, VNum))
-    PBinSum = XBoxNumber.sum(axis=1)
-    for i in range(PNum):
-        if PBinSum[i] > 0:
-            PBoxPercent[i, :] = XBoxNumber[i, :] / PBinSum[i] * 100
-
-    VBoxPercent = np.zeros((PNum, VNum))
-    VBinSum = XBoxNumber.sum(axis=0)
-    for i in range(VNum):
-        if VBinSum[i] > 0:
-            VBoxPercent[:, i] = XBoxNumber[:, i] / VBinSum[i] * 100
-
-    return PBoxPercent, VBoxPercent
-
-# 查找主带函数
-
-
-def find_main_band(PBoxPercent, PNum, VNum, XBoxNumber):
-    PBoxMaxIndex = np.argmax(PBoxPercent, axis=1)
-    PBoxMaxP = np.max(PBoxPercent, axis=1)
-
-    DotDense = np.zeros(PNum)
-    DotDenseLeftRight = np.zeros((PNum, 2), dtype=int)
-    DotValve = 90
-    for i in range(PNum - 6):
-        PDotDenseSum = PBoxMaxP[i]
-        iSpreadRight = iSpreadLeft = 1
-        while PDotDenseSum < DotValve:
-            if (PBoxMaxIndex[i] + iSpreadRight) < VNum - 1:
-                PDotDenseSum += PBoxPercent[i, PBoxMaxIndex[i] + iSpreadRight]
-                iSpreadRight += 1
-            if (PBoxMaxIndex[i] + iSpreadRight) > VNum - 1:
-                break
-            if (PBoxMaxIndex[i] - iSpreadLeft) > 0:
-                PDotDenseSum += PBoxPercent[i, PBoxMaxIndex[i] - iSpreadLeft]
-                iSpreadLeft += 1
-            if (PBoxMaxIndex[i] - iSpreadLeft) <= 0:
-                break
-        iSpreadRight -= 1
-        iSpreadLeft -= 1
-        DotDenseLeftRight[i, :] = [iSpreadLeft, iSpreadRight]
-        DotDense[i] = iSpreadLeft + iSpreadRight + 1
-
-    DotDenseWidthLeft = DotDenseLeftRight[:, 1]
-    MainBandRight = np.median(DotDenseWidthLeft)
-    PowerLimitValve = int(np.ceil(MainBandRight)) + 3
-
-    PowerLimit = np.zeros(PNum)
-    nCounterLimit = nCounter = 0
-    WidthAverage = 0
-    for i in range(PNum - 6):
-        if DotDenseLeftRight[i, 1] > PowerLimitValve and XBoxNumber[i, :].sum() > 20:
-            PowerLimit[i] = 1
-            nCounterLimit += 1
-        else:
-            WidthAverage += DotDenseLeftRight[i, 1]
-            nCounter += 1
-    WidthAverage /= nCounter
-
-    WidthVar = np.sqrt(np.mean((DotDenseLeftRight[:, 1] - WidthAverage) ** 2))
-    PowerBandWidth = WidthAverage * 2 * 0.25
-
-    return DotDense, DotDenseLeftRight, PowerLimit, WidthAverage, WidthVar, PowerBandWidth, PBoxMaxIndex
-
-# 标记坏点函数
-
-
-def mark_bad_points(DzMarch809, DotDenseLeftRight, PBoxMaxIndex, PowerLimit, PNum, VNum, XBoxNumber, PBoxPercent):
-    CurveWidthR = int(np.ceil(DotDenseLeftRight[:, 1].mean())) + 2
-    CurveWidthL = CurveWidthR
-    BBoxLimit = np.zeros((PNum, VNum))
-    for i in range(3, PNum - 6):
-        if PowerLimit[i] == 1:
-            BBoxLimit[i, PBoxMaxIndex[i] + CurveWidthR + 1: VNum] = 1
-
-    BBoxRemove = np.zeros((PNum, VNum))
-    for m in range(PNum - 6):
-        BBoxRemove[m, PBoxMaxIndex[m] + CurveWidthR: VNum] = 1
-        BBoxRemove[m, :PBoxMaxIndex[m] - CurveWidthL + 1] = 2
-
-    CurveTop = [0, 0]
-    CurveTopValve = 3
-    BTopFind = False
-    for m in range(PNum - 4, 0, -1):
-        for n in range(VNum):
-            if PBoxPercent[m, n] > CurveTopValve and XBoxNumber[m, n] >= 10:
-                CurveTop = [m, n]
-                BTopFind = True
-                break
-        if BTopFind:
-            break
-
-    IsolateValve = 3
-    for m in range(PNum - 6):
-        for n in range(PBoxMaxIndex[m] + CurveWidthR, VNum):
-            if PBoxPercent[m, n] < IsolateValve:
-                BBoxRemove[m, n] = 1
-
-    for m in range(PNum - 6, PNum):
-        BBoxRemove[m, :] = 3
-    for m in range(PNum - 5, PNum):
-        BBoxRemove[m, :CurveTop[1] - 2] = 2
-
-    DzMarch809Sel = np.zeros(len(DzMarch809), dtype=int)
-    for i in range(len(DzMarch809)):
-        nWhichP = np.digitize(
-            DzMarch809.iloc[i][fieldActivePower], np.arange(0, PNum * 25, 25)) - 1
-        nWhichV = np.digitize(
-            DzMarch809.iloc[i][fieldWindSpeed], np.arange(0.125, VNum * 0.25, 0.25))
-        
-        # if (
-        #     (DzMarch809.iloc[i][fieldActivePower] < PRated * 0.75) and
-        #     (DzMarch809.iloc[i][fieldPitchAngle] > 0.5) or
-        #     (DzMarch809.iloc[i][fieldActivePower] < PRated * 0.85) and
-        #     (DzMarch809.iloc[i][fieldPitchAngle] > 1.5) or
-        #     (DzMarch809.iloc[i][fieldActivePower] < PRated * 0.9) and
-        #     (DzMarch809.iloc[i][fieldPitchAngle] > 2.5)
-        # ):
-        #     continue
-
-        if nWhichP < PNum and nWhichV < VNum:
-            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 identify_limit_load_data(DzMarch809: pd.DataFrame, DzMarch809Sel, PRated):
-    nCounter1 = len(DzMarch809)
-    PVLimit = np.zeros((nCounter1, 2))
-    nLimitTotal = 0
-    nWindowLength = 3
-    PowerStd = 15  # 功率波动方差
-    PowerLimitUp =  PRated - 300
-    PowerLimitLow = 5  # 200kW 
-
-    nWindowNum = nCounter1 // nWindowLength
-
-    for i in range(nWindowNum):
-        LimitWindow = DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower].values
-               
-        # 检查所有数据是否在 PowerLimitLow值~PowerLimitUp值范围内
-        if not ((LimitWindow >= PowerLimitLow) & (LimitWindow <= PowerLimitUp)).all():
-            continue
-        
-        """
-        限功率识别策略(参考):
-        1. 功率<额定功率(PRated)*0.75 and  叶片角度>0.5 ; 2. 功率<额定功率*0.85 and 叶片角度>1.5 ; 3. 功率<额定功率*0.9 and 叶片角度>2.5;
-        示例:
-        1. 功率<1100,and  叶片角度>0.5 ; 2. 功率<1250 and 叶片角度>1.5 ; 3. 功率<1400 and 叶片角度>2.5 ;
-        """        
-        # 额外的限功率识别策略
-        pitch_angle_window = DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldPitchAngle].values
-
-        if (
-            (DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower] < PRated * 0.75).any() and
-            (pitch_angle_window > 0.5).any() or
-            (DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower] < PRated * 0.85).any() and
-            (pitch_angle_window > 1.5).any() or
-            (DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower] < PRated * 0.9).any() and
-            (pitch_angle_window > 2.5).any()
-        ):
-            # 标识限负荷数据
-            DzMarch809Sel[i * nWindowLength:(i + 1) * nWindowLength] = 4
-
-        UpLimit = LimitWindow[0] + PowerStd
-        LowLimit = LimitWindow[0] - PowerStd
-
-        # 检查所有数据是否在方差范围内
-        if not ((LimitWindow[1:] >= LowLimit) & (LimitWindow[1:] <= UpLimit)).all():
-            continue
-
-        # 标识限负荷数据
-        DzMarch809Sel[i * nWindowLength:(i + 1) * nWindowLength] = 4
-
-        for j in range(nWindowLength):
-            # 只提取功率和风速数据
-            PVLimit[nLimitTotal] = DzMarch809.iloc[i * nWindowLength + j][[fieldWindSpeed, fieldActivePower]]
-            nLimitTotal += 1
-
-    PVLimit = PVLimit[:nLimitTotal]  # 截取实际数据部分
-
-    return PVLimit, DzMarch809Sel
-
-# 计算能量损失函数
-def calculate_energy_loss(DzMarch809Sel, DzMarch809, IdealCurve, PNum, BB, PRated, EPIdealTotalAAA, EPActualTotal):
-    EPLostStopTotal = 0
-    nStopTotal = 0
-    for i in range(len(DzMarch809)):
-        if DzMarch809.iloc[i][fieldActivePower] <= 0:
-            nWhichBin = 0
-            for m in range(BB - 1):
-                if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
-                    nWhichBin = m
-                    break
-            if nWhichBin > 0:
-                IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
-                                                                                                                  [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
-                EPLostStopTotal += IdealPower / 6
-                nStopTotal += 1
-
-    EPLostBadTotal = 0
-    EPLost = 0
-    nBadTotal = 0
-    for i in range(len(DzMarch809)):
-        if DzMarch809Sel[i] == 1:
-            nWhichBin = 0
-            for m in range(BB - 1):
-                if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
-                    nWhichBin = m
-                    break
-            if nWhichBin > 0:
-                IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
-                                                                                                                  [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
-                EPLost += abs(IdealPower -
-                              DzMarch809.iloc[i][fieldActivePower]) / 6
-                EPLostBadTotal += EPLost
-                nBadTotal += 1
-
-    EPOverTotal = 0
-    nOverTotal = 0
-    for i in range(len(DzMarch809)):
-        if DzMarch809Sel[i] == 3:
-            EPOver = (DzMarch809.iloc[i][fieldActivePower] - PRated) / 6
-            EPOverTotal += EPOver
-            nOverTotal += 1
-
-    EPLostPerformTotal = 0
-    for i in range(len(DzMarch809)):
-        nWhichBinI = 0
-        for m in range(BB - 1):
-            if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
-                nWhichBinI = m
-                break
-        if nWhichBinI > 0:
-            IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBinI][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBinI + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBinI]
-                                                                                                               [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBinI + 1][fieldActivePower] - IdealCurve.iloc[nWhichBinI][fieldActivePower]) + IdealCurve.iloc[nWhichBinI][fieldActivePower]
-            EPLostPerformTotal += (IdealPower -
-                                   DzMarch809.iloc[i][fieldActivePower]) / 6
-
-    EPIdealTotal = EPActualTotal + EPLostStopTotal + EPLostBadTotal + \
-        EPLostPerformTotal if EPLostPerformTotal >= 0 else EPActualTotal + \
-        EPLostStopTotal + EPLostBadTotal
-
-    RemoveOverEP = 0
-    for i in range(len(DzMarch809)):
-        if DzMarch809Sel[i] == 2:
-            nWhichBin = 0
-            for m in range(BB - 1):
-                if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
-                    nWhichBin = m
-                    break
-            if nWhichBin > 0:
-                IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
-                                                                                                                  [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
-                RemoveOverEP += (DzMarch809.iloc[i]
-                                 [fieldActivePower] - IdealPower) / 6
-
-    for i in range(len(DzMarch809)):
-        if DzMarch809.iloc[i][fieldActivePower] > PRated:
-            RemoveOverEP += (DzMarch809.iloc[i][fieldActivePower] - PRated) / 6
-
-    return EPLostStopTotal, EPLostBadTotal, EPOverTotal, EPLostPerformTotal, EPIdealTotal - RemoveOverEP
-
-# 计算实测功率曲线函数
-
-
-def calculate_measured_power_curve(PVDot, VRated, PRated):
-    XBinNumber = np.ones(50)
-    PCurve = np.zeros((50, 2))
-    PCurve[:, 0] = np.arange(0.5, 25.5, 0.5)
-    XBinSum = np.zeros((50, 2))
-
-    for i in range(len(PVDot)):
-        nWhichBin = 0
-        for b in range(50):
-            if (b * 0.5 - 0.25) < PVDot.iloc[i][fieldWindSpeed] <= (b * 0.5 + 0.25):
-                nWhichBin = b
-                break
-
-        if nWhichBin > 0:
-            XBinSum[nWhichBin, 0] += PVDot.iloc[i][fieldWindSpeed]
-            XBinSum[nWhichBin, 1] += PVDot.iloc[i][fieldActivePower]
-            XBinNumber[nWhichBin] += 1
-
-    XBinNumber -= 1
-
-    for b in range(50):
-        if XBinNumber[b] > 0:
-            PCurve[b, 0] = XBinSum[b, 0] / XBinNumber[b]
-            PCurve[b, 1] = XBinSum[b, 1] / XBinNumber[b]
-
-    VRatedNum = int(VRated / 0.5)
-    for m in range(VRatedNum, 50):
-        if PCurve[m, 1] == 0:
-            PCurve[m, 1] = PRated
-
-    return PCurve
-
-# 计算标准正则功率曲线函数
-def calculate_normalized_power_curve(IdealCurve, VRated, PRated):
-    PCurveNorm = np.zeros((50, 2))
-    VRatedNum = int(VRated / 0.5)
-
-    # 15m/s以上为额定功率
-    high_wind_speeds = np.arange(15, 25.5, 0.5)
-    PCurveNorm[VRatedNum:VRatedNum+len(high_wind_speeds), 0] = high_wind_speeds
-    PCurveNorm[VRatedNum:VRatedNum+len(high_wind_speeds), 1] = PRated
-
-    # 15m/s以下正则功率曲线
-    VSpeed = np.arange(0.5, 15.5, 0.5)
-    CurveData = IdealCurve[IdealCurve[fieldWindSpeed]
-                           <= 15].to_numpy()  # 提取风速<=15的数据
-
-    for i, v in enumerate(VSpeed):
-        if i < len(CurveData) - 1:
-            # 插值计算
-            x0, y0 = CurveData[i]
-            x1, y1 = CurveData[i + 1]
-            PCurveNorm[i, 0] = v
-            PCurveNorm[i, 1] = y0 + (v - x0) * (y1 - y0) / (x1 - x0)
-        else:
-            PCurveNorm[i, 0] = v
-            PCurveNorm[i, 1] = PRated  # 防止超出范围的情况
-
-    return PCurveNorm
-
-# 保存结果函数
-
-
-def save_results(PCurve, PCurveNorm, EPKW, EPPer, outputDir='./output/A01'):
-    if not os.path.exists(outputDir):
-        os.makedirs(outputDir)
-    pd.DataFrame(PCurve, columns=[fieldWindSpeed, fieldActivePower]).to_csv(
-        os.path.join(outputDir, 'PCurve.csv'), index=False)
-    pd.DataFrame(PCurveNorm, columns=[fieldWindSpeed, fieldActivePower]).to_csv(
-        os.path.join(outputDir, 'PCurveNorm.csv'), index=False)
-    pd.DataFrame([EPKW], columns=['EPIdealTotal', 'EPActualTotal', 'EPLostStopTotal', 'EPLostBadLimitTotal', 'EPLostPerformTotal', 'EPLostBadTotal',
-                 'EPLostLimitTotal', 'EPOverTotal', 'WindSpeedAvr', 'TurbineRunRate']).to_csv(os.path.join(outputDir, 'EPKW.csv'), index=False)
-    pd.DataFrame([EPPer], columns=['Percent1', 'Percent2', 'Percent3', 'Percent4', 'Percent5', 'Percent6', 'Percent7',
-                 'Percent8', 'WindSpeedAvr', 'TurbineRunRate']).to_csv(os.path.join(outputDir, 'EPPer.csv'), index=False)
-
-# 绘制结果函数,使用 Plotly
-
-
-def plot_results(PVBad, PVLimit, PVDot, PCurve, IdealCurve):
-    fig = go.Figure()
-
-    # 添加坏点数据
-    if not PVBad.empty:
-        fig.add_trace(go.Scatter(x=PVBad[fieldWindSpeed], y=PVBad[fieldActivePower],
-                      mode='markers', name='坏点', marker=dict(color='red')))
-
-    # 添加限电点数据
-    if not PVLimit.empty:
-        fig.add_trace(go.Scatter(x=PVLimit[fieldWindSpeed], y=PVLimit[fieldActivePower],
-                      mode='markers', name='限功率', marker=dict(color='blue')))
-
-    # 添加正常点数据
-    if not PVDot.empty:
-        fig.add_trace(go.Scatter(x=PVDot[fieldWindSpeed], y=PVDot[fieldActivePower],
-                      mode='markers', name='好点', marker=dict(color='black')))
-
-    # 添加实测功率曲线
-    if PCurve.shape[0] > 0:
-        fig.add_trace(go.Scatter(
-            x=PCurve[:, 0], y=PCurve[:, 1], mode='lines+markers', name='实测功率曲线', line=dict(color='green')))
-
-    # 添加设计功率曲线
-    if IdealCurve.shape[0] > 0:
-        fig.add_trace(go.Scatter(x=IdealCurve[:, 0], y=IdealCurve[:, 1],
-                      mode='lines+markers', name='合同功率曲线', line=dict(color='yellow')))
-
-    # 更新布局
-    fig.update_layout(
-        title={'text':'风力机功率散点数据分类标识','x':0.5},
-        xaxis_title=fieldWindSpeed,
-        yaxis_title=fieldActivePower,
-        legend=dict(x=0.01, y=0.99),
-        template='plotly_white'
-    )
-
-    # fig.show()
-    outputDir = './output/A01'
-    filePath = os.path.join(outputDir, f'power_scatter.html')
-    fig.write_html(filePath)
-
-
-# 主函数
-def main():
-    # 读取数据
-    March809, IdealCurve = read_data()
-    # 计算统计信息
-    AA, BB, PNum, EPActualTotal, WindSpeedAvr, TurbineRunRate, EPIdealTotalAAA = calculate_statistics(
-        March809, IdealCurve)
-    # 分类数据
-    DzMarch809, XBoxNumber = classify_data(March809, PNum, VNum)
-    # 计算百分比
-    PBoxPercent, VBoxPercent = compute_percentages(XBoxNumber, PNum, VNum)
-    # 查找主带
-    DotDense, DotDenseLeftRight, PowerLimit, WidthAverage, WidthVar, PowerBandWidth, PBoxMaxIndex = find_main_band(
-        PBoxPercent, PNum, VNum, XBoxNumber)
-    # 标记坏点
-    DzMarch809Sel = mark_bad_points(
-        DzMarch809, DotDenseLeftRight, PBoxMaxIndex, PowerLimit, PNum, VNum, XBoxNumber, PBoxPercent)
-    # 标识限负荷数据点
-    PVLimitArray, DzMarch809Sel = identify_limit_load_data(
-        DzMarch809, DzMarch809Sel, PRated)
-
-    # 初始化存储好点和坏点的 DataFrame
-    PVBad = pd.DataFrame(columns=DzMarch809.columns)
-    PVLimit = pd.DataFrame(columns=DzMarch809.columns)
-    PVDot = pd.DataFrame(columns=DzMarch809.columns)
-
-    # 存储好点和坏点
-    for i in range(len(DzMarch809)):
-        if DzMarch809Sel[i] in [1, 2, 3]:
-            if not DzMarch809.iloc[[i]].isna().all().all():
-                PVBad = pd.concat(
-                    [PVBad, DzMarch809.iloc[[i]]], ignore_index=True)
-        elif DzMarch809Sel[i] == 4:
-            if not DzMarch809.iloc[[i]].isna().all().all():
-                PVLimit = pd.concat(
-                    [PVLimit, DzMarch809.iloc[[i]]], ignore_index=True)
-        else:
-            if not DzMarch809.iloc[[i]].isna().all().all():
-                PVDot = pd.concat(
-                    [PVDot, DzMarch809.iloc[[i]]], ignore_index=True)
-
-    # 计算能量损失
-    EPLostStopTotal, EPLostBadTotal, EPOverTotal, EPLostPerformTotal, EPIdealTotal = calculate_energy_loss(
-        DzMarch809Sel, DzMarch809, IdealCurve, PNum, BB, PRated, EPIdealTotalAAA, EPActualTotal)
-
-    EPKW = [EPIdealTotal, EPActualTotal, EPLostStopTotal, EPLostBadTotal,
-            EPLostPerformTotal, EPLostBadTotal, 0, EPOverTotal, WindSpeedAvr, TurbineRunRate]
-    EPPer = [100, EPActualTotal / EPIdealTotal * 100, EPLostStopTotal / EPIdealTotal * 100, EPLostBadTotal / EPIdealTotal * 100,
-             EPLostPerformTotal / EPIdealTotal * 100, EPLostBadTotal / EPIdealTotal * 100, 0, EPOverTotal / EPActualTotal * 100, WindSpeedAvr, TurbineRunRate]
-
-    # 计算实测功率曲线
-    PCurve = calculate_measured_power_curve(PVDot, VRated, PRated)
-    # 计算标准正则功率曲线
-    PCurveNorm = calculate_normalized_power_curve(IdealCurve, VRated, PRated)
-
-    # 保存结果
-    save_results(PCurve, PCurveNorm, EPKW, EPPer)
-    # 绘制结果
-    plot_results(PVBad, PVLimit, PVDot, PCurve, IdealCurve.to_numpy())
-
-
-if __name__ == "__main__":
-    main()

+ 0 - 444
wtoaamapi/apps/business/algorithm/dataProcessor.py

@@ -1,444 +0,0 @@
-import os
-from datetime import datetime
-import concurrent.futures
-import pandas as pd
-from confBusiness import ConfBusiness, Field_NameOfTurbine, Field_GeneratorSpeed, Field_GeneratorTorque,Field_AngleIncluded
-from .utils.directoryUtil import DirectoryUtil as dir
-from .baseAnalyst import BaseAnalyst
-from .analyst import Analyst
-from .commonBusiness import CommonBusiness
-
-class DataProcessor:
-    def __init__(self):
-        self.common=CommonBusiness()
-        self._baseAnalysts = []
-        self._noCustomFilterAnalysts = []
-        self._analysts = []
-        self.node_filter_value_state_turbine = "filter_value_state_turbine"
-        self.node_angle_pitch_min = "angle_pitch_min"
-        self.node_angle_pitch_max = "angle_pitch_max"
-        self.node_speed_wind_cut_in = "speed_wind_cut_in"
-        self.node_speed_wind_cut_out = "speed_wind_cut_out"
-        self.node_active_power_min = "active_power_min"
-        self.node_active_power_max = "active_power_max"
-
-    def attachBaseAnalyst(self, analyst: BaseAnalyst):
-        if analyst not in self._analysts:
-            self._baseAnalysts.append(analyst)
-
-    def detachBaseAnalyst(self, analyst: BaseAnalyst):
-        try:
-            self._baseAnalysts.remove(analyst)
-        except ValueError:
-            pass
-
-    def attach(self, analyst: Analyst):
-        if analyst not in self._analysts:
-            self._analysts.append(analyst)
-
-    def detach(self, analyst: Analyst):
-        try:
-            self._analysts.remove(analyst)
-        except ValueError:
-            pass
-
-    def turbineNotify(self,
-                      dataFrameOfTurbine: pd.DataFrame,
-                      confData: ConfBusiness,
-                      turbineName):
-        for analyst in self._analysts:
-            outputAnalysisDir = analyst.getOutputAnalysisDir()
-
-            outputFilePath = r"{}/{}_{}.csv".format(
-                outputAnalysisDir, turbineName, analyst.typeAnalyst())
-
-            analyst.analysisOfTurbine(
-                dataFrameOfTurbine, outputAnalysisDir, outputFilePath, confData, turbineName)
-
-    def turbinesNotify(self, dataFrameOfTurbines: pd.DataFrame,  confData: ConfBusiness):
-        for analyst in self._analysts:
-            outputAnalysisDir = analyst.getOutputAnalysisDir()
-            analyst.analysisOfTurbines(
-                dataFrameOfTurbines, outputAnalysisDir, confData)
-
-    def baseAnalystTurbineNotify(self,
-                                 dataFrameOfTurbine: pd.DataFrame,
-                                 confData: ConfBusiness,
-                                 turbineName):
-        for analyst in self._baseAnalysts:
-            outputAnalysisDir = analyst.getOutputAnalysisDir()
-
-            outputFilePath = r"{}/{}_{}.csv".format(
-                outputAnalysisDir, turbineName, analyst.typeAnalyst())
-
-            analyst.analysisOfTurbine(
-                dataFrameOfTurbine, outputAnalysisDir, outputFilePath, confData, turbineName)
-
-    def baseAnalystNotify(self, dataFrameOfTurbines: pd.DataFrame,  confData: ConfBusiness):
-        for analyst in self._baseAnalysts:
-            outputAnalysisDir = analyst.getOutputAnalysisDir()
-            analyst.analysisOfTurbines(
-                dataFrameOfTurbines, outputAnalysisDir, confData)
-
-    def filterCustom(self, dataFrame: pd.DataFrame, confData: ConfBusiness):
-        if not self.common.isNone(confData.field_wind_speed) and self.node_speed_wind_cut_in in confData.filter \
-                and not self.common.isNone(confData.filter[self.node_speed_wind_cut_in]) \
-                and not self.common.isNone(confData.field_wind_speed) and self.node_speed_wind_cut_out in confData.filter \
-                and not self.common.isNone(confData.filter[self.node_speed_wind_cut_out]):
-            windSpeedCutIn = float(
-                confData.filter[self.node_speed_wind_cut_in])
-            windSpeedCutOut = float(
-                confData.filter[self.node_speed_wind_cut_out])
-
-            dataFrame = dataFrame[~((dataFrame[confData.field_wind_speed] > windSpeedCutOut) | (
-                dataFrame[confData.field_wind_speed] < 0))]
-            dataFrame = dataFrame[~((dataFrame[confData.field_wind_speed] < windSpeedCutIn) & (
-                dataFrame[confData.field_power] < confData.rated_power*1.2))]
-
-            dataFrame = dataFrame[(
-                dataFrame[confData.field_power] <= confData.rated_power*1.2)]
-
-        # Filter rows where turbine state
-        if not self.common.isNone(confData.field_turbine_state) and self.node_filter_value_state_turbine in confData.filter and not self.common.isNone(confData.filter[self.node_filter_value_state_turbine]):
-            stateTurbine = confData.filter[self.node_filter_value_state_turbine]
-            dataFrame = dataFrame[dataFrame[confData.field_turbine_state].isin(
-                stateTurbine)]
-
-        # # Filter rows where pitch
-        # if 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]):
-        #     anglePitchMin = float(confData.filter[self.node_angle_pitch_min])
-        #     dataFrame = dataFrame[(
-        #         dataFrame[confData.field_pitch_angle1] >= anglePitchMin)]
-
-        # if not self.common.isNone(confData.field_pitch_angle1) and self.node_angle_pitch_max in confData.filter and not self.common.isNone(confData.filter[self.node_angle_pitch_max]):
-        #     anglePitchMax = float(confData.filter[self.node_angle_pitch_max])
-        #     dataFrame = dataFrame[(
-        #         dataFrame[confData.field_pitch_angle1] <= anglePitchMax)]
-
-        # # Filter rows where wind speed
-        # if not self.common.isNone(confData.field_wind_speed) and self.node_speed_wind_cut_in in confData.filter and not self.common.isNone(confData.filter[self.node_speed_wind_cut_in]):
-        #     windSpeedCutIn = float(confData.filter[self.node_speed_wind_cut_in])
-        #     dataFrame = dataFrame[(
-        #         dataFrame[confData.field_wind_speed] >= windSpeedCutIn)]
-
-        # if not self.common.isNone(confData.field_wind_speed) and self.node_speed_wind_cut_out in confData.filter and not self.common.isNone(confData.filter[self.node_speed_wind_cut_out]):
-        #     windSpeedCutOut = float(confData.filter[self.node_speed_wind_cut_out])
-        #     dataFrame = dataFrame[(
-        #         dataFrame[confData.field_wind_speed] < windSpeedCutOut)]
-
-        # # Filter rows where power
-        # if not self.common.isNone(confData.field_power) and self.node_active_power_min in confData.filter and not self.common.isNone(confData.filter[self.node_active_power_min]):
-        #     activePowerMin = float(confData.filter[self.node_active_power_min])
-        #     dataFrame = dataFrame[(
-        #         dataFrame[confData.field_power] >= activePowerMin)]
-
-        # 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]):
-        #     activePowerMax = float(confData.filter[self.node_active_power_max])
-        #     dataFrame = dataFrame[(
-        #         dataFrame[confData.field_power] < activePowerMax)]
-
-        return dataFrame
-
-    def filterData(self, dataFrame: pd.DataFrame,  confData: ConfBusiness, dataFilter, rotationalSpeedRatio):
-        # dataFrame = dataFrame.dropna(axis=0,subset=[confData.field_power,confData.field_wind_speed,confData.field_pitch_angle1])
-        dataFrame = dataFrame.dropna(
-            axis=0, subset=self.getUseColumns(confData))        
-        
-        if confData.field_wind_speed in dataFrame.columns:
-            dataFrame[confData.field_wind_speed] = dataFrame[confData.field_wind_speed].astype('float32') 
-
-        if confData.field_wind_dir in dataFrame.columns:
-            dataFrame[confData.field_wind_dir]=dataFrame[confData.field_wind_dir].astype('float32') 
-
-        if confData.field_angle_included in dataFrame.columns:
-            dataFrame[confData.field_angle_included]=dataFrame[confData.field_angle_included].astype('float32') 
-
-        if confData.field_power in dataFrame.columns:
-            dataFrame[confData.field_power]=dataFrame[confData.field_power].astype('float32') 
-        
-        if confData.field_wind_dir in dataFrame.columns:
-            dataFrame[confData.field_wind_dir]=dataFrame[confData.field_wind_dir].astype('float32') 
-
-        if confData.field_pitch_angle1 in dataFrame.columns:
-            dataFrame[confData.field_pitch_angle1]=dataFrame[confData.field_pitch_angle1].astype('float32')
-
-        if confData.field_gen_speed in dataFrame.columns: 
-            dataFrame[confData.field_gen_speed]=dataFrame[confData.field_gen_speed].astype('float32') 
-        
-        if confData.field_rotor_speed in dataFrame.columns:
-            dataFrame[confData.field_rotor_speed]=dataFrame[confData.field_rotor_speed].astype('float32') 
-
-        dataFrame = dataFrame[(dataFrame[confData.field_turbine_time] >= confData.start_time) & (
-            dataFrame[confData.field_turbine_time] < confData.end_time)]
-
-        if not self.common.isNone(confData.excludingMonths) and len(confData.excludingMonths) > 0:
-            # 给定的日期列表
-            date_strings = []
-            for month in confData.excludingMonths:
-                if not self.common.isNone(month):
-                    date_strings.append(month)
-
-            if len(date_strings) > 0:
-                mask = ~dataFrame["year-month"].isin(date_strings)
-
-                # 使用掩码过滤DataFrame,删除指定日期的行
-                dataFrame = dataFrame[mask]
-
-        dataFrame["年月"] = pd.to_datetime(
-            dataFrame[confData.field_turbine_time], format="%Y-%m")
-        dataFrame['日期'] = pd.to_datetime(
-            dataFrame[confData.field_turbine_time], format="%Y-%m")
-        dataFrame['monthIntTime'] = dataFrame['日期'].apply(
-            lambda x: x.timestamp())
-
-        dataFrame["year-month"] = dataFrame[confData.field_turbine_time].dt.strftime(
-            '%Y-%m')
-
-        return dataFrame
-
-    def calculateAngleIncluded(self, array1, array2):
-        """
-        计算两个相同长度角度数组中两两对应角度值的偏差。
-        结果限制在-90°到+90°之间,并保留两位小数。
-
-        参数:
-        array1 (list): 第一个角度数组
-        array2 (list): 第二个角度数组
-
-        返回:
-        list: 两两对应角度的偏差列表
-        """
-        deviations = []
-        for angle1, angle2 in zip(array1, array2):
-            # 计算原始偏差
-            deviation = angle1 - angle2
-
-            # 调整偏差,使其位于-180°到+180°范围内
-            if deviation == 0.0:
-                deviation = 0.0
-            else:
-                deviation = (deviation + 180) % 360 - 180
-
-            # 将偏差限制在-90°到+90°范围内
-            if deviation > 90:
-                deviation -= 180
-            elif deviation < -90:
-                deviation += 180
-
-            # 保留两位小数
-            deviations.append(round(deviation, 2))
-
-        return deviations
-
-    def recalculationOfIncludedAngle(self, dataFrame: pd.DataFrame, fieldAngleIncluded, fieldWindDirect, fieldNacellePos):
-        """
-        依据机舱位置(角度)、风向计算两者夹角
-        """
-        if fieldAngleIncluded not in dataFrame.columns and fieldWindDirect in dataFrame.columns and fieldNacellePos in dataFrame.columns:
-            dataFrame[Field_AngleIncluded] = self.calculateAngleIncluded()
-        else:
-            dataFrame[Field_AngleIncluded] = dataFrame[fieldAngleIncluded]
-
-    def recalculationOfGeneratorSpeed(self, dataFrame: pd.DataFrame, fieldRotorSpeed, fieldGeneratorSpeed, rotationalSpeedRatio):
-        """
-        风电机组发电机转速再计算,公式:转速比=发电机转速/叶轮或主轴转速
-        """
-        if fieldGeneratorSpeed in dataFrame.columns:            
-            dataFrame[Field_GeneratorSpeed] = dataFrame[fieldGeneratorSpeed]
-
-        if fieldGeneratorSpeed not in dataFrame.columns and fieldRotorSpeed in dataFrame.columns:
-            dataFrame[fieldGeneratorSpeed] = rotationalSpeedRatio * \
-                dataFrame[fieldRotorSpeed]
-
-    def recalculationOfRotorSpeed(self, dataFrame: pd.DataFrame, fieldRotorSpeed, fieldGeneratorSpeed, rotationalSpeedRatio):
-        """
-        风电机组发电机转速再计算,公式:转速比=发电机转速/叶轮或主轴转速
-        """
-        if fieldRotorSpeed not in dataFrame.columns and fieldGeneratorSpeed in dataFrame.columns:
-            dataFrame[fieldRotorSpeed] = dataFrame[fieldGeneratorSpeed] / \
-                rotationalSpeedRatio
-
-    def recalculationOfRotorTorque(self, dataFrame: pd.DataFrame, fieldGeneratorTorque, fieldActivePower, fieldGeneratorSpeed):
-        """
-        风电机组发电机转矩计算,P的单位换成KW转矩计算公式:
-        P*1000= pi/30*T*n  
-        30000/pi*P=T*n
-        30000/3.1415926*P=T*n
-        9549.297*p=T*n  
-        其中:n为发电机转速,p为有功功率,T为转矩
-        """
-        if fieldGeneratorTorque not in dataFrame.columns and fieldActivePower in dataFrame.columns and fieldGeneratorSpeed in dataFrame.columns:
-            dataFrame[fieldGeneratorTorque] = 9549.297 * \
-                dataFrame[fieldActivePower]/dataFrame[fieldGeneratorSpeed]
-
-        dataFrame[Field_GeneratorTorque] = dataFrame[fieldGeneratorTorque]
-        
-
-    def getUseColumns(self, confData: ConfBusiness):
-        useColumns = []
-
-        if not self.common.isNone(confData.field_turbine_time):
-            useColumns.append(confData.field_turbine_time)
-
-        if not self.common.isNone(confData.field_turbine_name):
-            useColumns.append(confData.field_turbine_name)
-
-        if not self.common.isNone(confData.field_wind_speed):
-            useColumns.append(confData.field_wind_speed)
-
-        if not self.common.isNone(confData.field_power):
-            useColumns.append(confData.field_power)
-
-        if not self.common.isNone(confData.field_pitch_angle1):
-            useColumns.append(confData.field_pitch_angle1)
-
-        if not self.common.isNone(confData.field_rotor_speed):
-            useColumns.append(confData.field_rotor_speed)
-
-        if not self.common.isNone(confData.field_gen_speed):
-            useColumns.append(confData.field_gen_speed)
-
-        if not self.common.isNone(confData.field_torque):
-            useColumns.append(confData.field_torque)
-
-        if not self.common.isNone(confData.field_wind_dir):
-            useColumns.append(confData.field_wind_dir)
-
-        if not self.common.isNone(confData.field_angle_included):
-            useColumns.append(confData.field_angle_included)
-
-        if not self.common.isNone(confData.field_nacelle_pos):
-            useColumns.append(confData.field_nacelle_pos)
-
-        if not self.common.isNone(confData.field_env_temp):
-            useColumns.append(confData.field_env_temp)
-
-        if not self.common.isNone(confData.field_nacelle_temp):
-            useColumns.append(confData.field_nacelle_temp)
-
-        if not self.common.isNone(confData.field_turbine_state):
-            useColumns.append(confData.field_turbine_state)
-
-        if not self.common.isNone(confData.field_temperature_large_components):
-            temperature_cols = confData.field_temperature_large_components.split(
-                ',')
-            for temperatureColumn in temperature_cols:
-                useColumns.append(temperatureColumn)
-        print(useColumns)
-        return useColumns
-
-    def loadData(self, csvFilePath, skip, confData: ConfBusiness, turbineName):
-        # Load the CSV, skipping the specified initial rows
-        dataFrame = pd.read_csv(csvFilePath, header=0,usecols=self.getUseColumns(
-            confData), skiprows=range(1, skip+1))
-
-        dataFrame[Field_NameOfTurbine] = confData.add_W_if_starts_with_digit(
-            turbineName)
-
-        # 选择所有的数值型列
-        dataFrame = dataFrame.convert_dtypes()
-        numeric_cols = dataFrame.select_dtypes(
-            include=['float64', 'float16']).columns
-
-        # 将这些列转换为float32
-        dataFrame[numeric_cols] = dataFrame[numeric_cols].astype('float32')
-
-        # 首先尝试去除字符串前后的空白
-        dataFrame[confData.field_turbine_time] = dataFrame[confData.field_turbine_time].str.strip()
-        dataFrame[confData.field_turbine_time] = pd.to_datetime(
-            dataFrame[confData.field_turbine_time],format='%Y-%m-%d %H:%M:%S',errors="coerce")
-        dataFrame[confData.field_turbine_time] = dataFrame[confData.field_turbine_time].dt.strftime(
-            '%Y-%m-%d %H:%M:%S')
-        dataFrame[confData.field_turbine_time] = pd.to_datetime(
-            dataFrame[confData.field_turbine_time])
-
-        # 对除了“时间”列之外的所有列进行自下而上的填充(先反转后填充)
-        # 注意:补植须要考虑业务合理性
-        # dataFrame = dataFrame.fillna(method='ffill')
-        # dataFrame = dataFrame.fillna(method='bfill')
-
-        return dataFrame
-
-    def execute(self, confData: ConfBusiness):
-        rotationalSpeedRatio = confData.rotational_Speed_Ratio  # 转速比
-        field_Rotor_Speed = confData.field_rotor_speed  # 字段 '主轴转速'
-        field_Generator_Speed = confData.field_gen_speed  # 字段 '发电机转速'
-        field_Torque = confData.field_torque  # 字段 "实际扭矩"
-        field_Active_Power = confData.field_power  # 字段 '有功功率'
-        dataFilter = confData.filter
-
-        # r'E:/BaiduNetdiskDownload/min_scada_TangZhen'
-        csvFileDir = confData.input_path
-        csvFileNameSplitStringForTurbine = confData.csvFileNameSplitStringForTurbine  # '.csv'
-        indexTurbine = confData.index_turbine
-        outputRootDir = confData.output_path  # r'output'
-
-        # Example usage:
-        outputDataAfterFilteringDir = r"{}/{}".format(
-            outputRootDir,  "DataAfterFiltering")
-        dir.create_directory(outputDataAfterFilteringDir)
-
-        dataFrameMergeFilter = pd.DataFrame()
-        for rootDir, subDirs, files in dir.list_directory(csvFileDir):
-            files = sorted(files)
-            for file in files:
-
-                if not file.endswith(".csv"):
-                    continue
-
-                csvFilePath = os.path.join(rootDir, file)
-                print(csvFilePath)
-
-                turbineName = confData.add_W_if_starts_with_digit(file.split(csvFileNameSplitStringForTurbine)[
-                    indexTurbine])
-
-                dataFrameFilter = self.loadData(
-                    csvFilePath, confData.skip_row_number, confData, turbineName)
-
-                dataFrameFilter = self.filterData(
-                    dataFrameFilter, confData, dataFilter, rotationalSpeedRatio)
-
-                self.baseAnalystTurbineNotify(dataFrameFilter,
-                                              confData,
-                                              turbineName)
-
-                dataFrameFilter = self.filterCustom(dataFrameFilter, confData)
-
-                dataFrameFilter = self.filterForMerge(
-                    dataFrameFilter, confData)
-
-                if len(dataFrameFilter) <= 0:
-                    print("dataFrameFilter not data.")
-                    continue
-
-                self.recalculationOfGeneratorSpeed(
-                    dataFrameFilter, field_Rotor_Speed, field_Generator_Speed, rotationalSpeedRatio)
-                self.recalculationOfRotorSpeed(
-                    dataFrameFilter, field_Rotor_Speed, field_Generator_Speed, rotationalSpeedRatio)
-                self.recalculationOfRotorTorque(
-                    dataFrameFilter, field_Torque, field_Active_Power, field_Generator_Speed)
-                self.recalculationOfIncludedAngle(
-                    dataFrameFilter, confData.field_angle_included, confData.field_wind_dir, confData.field_nacelle_pos)
-
-                dataFrameMergeFilter = pd.concat(
-                    [dataFrameMergeFilter, dataFrameFilter], axis=0, sort=False)
-
-                # dataFrameFilter.to_csv(os.path.join(
-                #     outputDataAfterFilteringDir, "{}.csv".format(turbineName)), index=False)
-                dataFrameFilter = self.turbineNotify(dataFrameFilter,
-                                                     confData,
-                                                     turbineName)
-
-        self.baseAnalystNotify(dataFrameMergeFilter,  confData)
-        self.turbinesNotify(dataFrameMergeFilter,  confData)
-
-    def filterForMerge(self, dataFrame: pd.DataFrame, confData: ConfBusiness):
-        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_pitch_angle1] > anglePitchMin))]
-
-        return dataFrame

+ 0 - 43
wtoaamapi/apps/business/algorithm/formula_cp.py

@@ -1,43 +0,0 @@
-def calculate_area(diameter):
-    """
-    根据给定的风力涡轮机叶轮直径计算扫过面积的函数。
-
-    参数:
-    - diameter: 风力涡轮机叶轮的直径,单位:米(m)
-
-    返回:
-    - A: 风力涡轮机叶片扫过的面积,单位:平方米(m^2)
-    """
-    radius = diameter / 2
-    A = 3.141592653589793 * radius ** 2
-    return A
-
-def calculate_cp(P, A, rho, v):
-    """
-    计算风能利用系数Cp的函数。
-
-    参数:
-    - P: 风力涡轮机的输出功率,单位:瓦特(W)
-    - A: 风力涡轮机叶片扫过的面积,单位:平方米(m^2)
-    - rho: 空气密度,单位:千克每立方米(kg/m^3)
-    - v: 风速,单位:米每秒(m/s)
-
-    返回:
-    - Cp: 风能利用系数
-    """
-    Cp = P / (0.5 * rho * A * v ** 3)
-    return Cp
-
-# 示例变量
-diameter_example = 121  # 假设叶轮直径为46.2米
-P_example = 905*1000
-rho_example = 1.18
-v_example = 4.3380
-
-# 使用函数计算A
-A_example = calculate_area(diameter_example)
-
-# 使用计算得到的A值调用calculate_cp函数
-cp_value = calculate_cp(P_example, A_example, rho_example, v_example)
-
-print("Cp={}".format(cp_value))

+ 0 - 92
wtoaamapi/apps/business/algorithm/generatorSpeedPowerAnalyst.py

@@ -1,92 +0,0 @@
-import os
-import pandas as pd
-from datetime import datetime
-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
-import matplotlib.cm as cm
-from matplotlib.ticker import MultipleLocator
-from matplotlib.colors import Normalize
-from .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import Field_NameOfTurbine, ConfBusiness
-
-
-class GeneratorSpeedPowerAnalyst(Analyst):
-    """
-    风电机组发电机转速-有功功率分析
-    """
-
-    def typeAnalyst(self):
-        return "speed_power"
-
-    def turbinesAnalysis(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness):
-        self.create_and_save_plots(
-            dataFrameMerge, outputAnalysisDir, confData, confData.field_gen_speed, confData.field_power)
-
-    def create_and_save_plots(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness, field_Generator_Speed, field_Active_Power):
-        x_name = 'generator_speed'
-        y_name = 'power'
-
-        grouped = dataFrameMerge.groupby(Field_NameOfTurbine)
-        for name, group in grouped:
-            # 创建图形和坐标轴
-            fig, ax = plt.subplots()
-            cmap = cm.get_cmap('rainbow')
-
-            # 绘制散点图
-            scatter = ax.scatter(x=group[confData.field_gen_speed]*confData.value_gen_speed_multiple if not self.common.isNone(confData.value_gen_speed_multiple) else group[confData.field_gen_speed],
-                                 y=group[confData.field_power], c=group['monthIntTime'], cmap=cmap, s=5)
-
-            # g = sns.lmplot(x=field_Generator_Speed, y=field_Active_Power, data=group,
-            #                fit_reg=False, scatter_kws={"s": 5, "color": "b"}, legend=False, height=6, aspect=1.2)
-            # g = sns.lmplot(x=field_Generator_Speed, y=field_Active_Power, data=group,
-            #                fit_reg=False, scatter_kws={"s": 5}, legend=False, height=6, aspect=1.2)
-            # for ax in g.axes.flat:
-            #     # ax.xaxis.set_major_locator(MultipleLocator(100))
-            #     ax.set_xlim(confData.value_gen_speed_min, confData.value_gen_speed_max)
-            #     ax.set_xlabel(x_name)
-            #     ax.set_ylabel(y_name)
-
-            
-
-            # 设置图形标题和坐标轴标签
-            ax.set_title(f'turbine_name={name}')
-            ax.set_xlim(confData.value_gen_speed_min,
-                        confData.value_gen_speed_max)
-            # 设置x轴的刻度步长  
-            # 假设您想要每100个单位一个刻度  
-            if not self.common.isNone(confData.value_gen_speed_step):
-                loc = MultipleLocator(confData.value_gen_speed_step) # 创建每100个单位一个刻度的定位器  
-                ax.xaxis.set_major_locator(loc) # 将定位器应用到x轴上 
-            ax.set_xlabel(x_name)
-            ax.set_ylabel(y_name)
-
-            # 显示图例,并调整位置
-            ax.legend(loc='lower right')
-
-            # 设置颜色条
-            unique_months = len(group['年月'].unique())
-            ticks = np.linspace(group['monthIntTime'].min(
-            ), group['monthIntTime'].max(), min(unique_months, 6))  # 减少刻度数量
-            ticklabels = [datetime.fromtimestamp(
-                tick).strftime('%Y-%m') for tick in ticks]
-            norm = Normalize(group['monthIntTime'].min(),
-                             group['monthIntTime'].max())
-            sm = cm.ScalarMappable(norm=norm, cmap=cmap)
-
-            # 添加颜色条
-            cbar = fig.colorbar(sm, ax=ax)
-            cbar.set_ticks(ticks)
-            cbar.set_ticklabels(ticklabels)
-            # 旋转x轴刻度标签
-            plt.xticks(rotation=45)
-
-            plt.tight_layout()
-            plt.title(f'{Field_NameOfTurbine}={name}')
-            # 保存图片到指定路径
-            output_file = os.path.join(outputAnalysisDir, f"{name}.png")
-            plt.savefig(output_file, bbox_inches='tight', dpi=120)
-            plt.close()

+ 0 - 62
wtoaamapi/apps/business/algorithm/generatorSpeedTorqueAnalyst.py

@@ -1,62 +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 .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import Field_NameOfTurbine,Field_GeneratorTorque, ConfBusiness
-
-
-class GeneratorSpeedTorqueAnalyst(Analyst):
-    """
-    风电机组发电机转速-转矩分析
-    """
-
-    def typeAnalyst(self):
-        return "speed_torque"
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.create_and_save_plots(
-            dataFrameMerge, outputAnalysisDir, confData, confData.field_gen_speed, confData.field_power)
-
-    def create_and_save_plots(self, dataFrame: pd.DataFrame, outputAnalysisDir, confData: ConfBusiness, fieldGeneratorSpeed, field_Active_Power):
-        x_name = 'generator_speed'
-        y_name = 'generator_torque'
-
-        grouped = dataFrame.groupby(Field_NameOfTurbine)
-        for name, group in grouped:
-            groupNew=group.copy()
-
-            if not self.common.isNone(confData.value_gen_speed_multiple):
-                groupNew[fieldGeneratorSpeed]=group[fieldGeneratorSpeed]*confData.value_gen_speed_multiple 
-            # sns.lmplot函数参数scatter_kws: 设置为{"s": 5}时,会出现颜色丢失问题;改为={"s": 5, "color": "b"}后,则造成图形风格不统一问题;
-            g = sns.lmplot(x=fieldGeneratorSpeed, y=Field_GeneratorTorque, data=groupNew, fit_reg=False, scatter_kws={
-                           "s": 5, "color": "b"}, legend=False, height=6, aspect=1.2)
-            # g = sns.lmplot(x=fieldGeneratorSpeed, y=Field_GeneratorTorque, data=group, fit_reg=False, scatter_kws={
-            #                "s": 5}, legend=False, height=6, aspect=1.2)
-            
-            for ax in g.axes.flat:
-                # ax.xaxis.set_major_locator(MultipleLocator(100))
-                ax.set_xlim(confData.value_gen_speed_min,
-                            confData.value_gen_speed_max)
-                
-                # 设置x轴的刻度步长  
-                # 假设您想要每100个单位一个刻度  
-                if not self.common.isNone(confData.value_gen_speed_step):
-                    loc = MultipleLocator(confData.value_gen_speed_step) # 创建每100个单位一个刻度的定位器  
-                    ax.xaxis.set_major_locator(loc) # 将定位器应用到x轴上 
-
-                ax.set_xlabel(x_name)
-                ax.set_ylabel(y_name)
-
-
-            plt.tight_layout()
-            plt.title(f'{Field_NameOfTurbine}={name}')
-            # 保存图片到指定路径
-            output_file = os.path.join(outputAnalysisDir, f"{name}.png")
-            plt.savefig(output_file, bbox_inches='tight', dpi=120)
-            plt.close()

+ 0 - 122
wtoaamapi/apps/business/algorithm/minPitchAnalyst.py

@@ -1,122 +0,0 @@
-import os
-import pandas as pd
-import numpy as np
-import matplotlib.pyplot as plt
-import matplotlib.cm as cm
-import math
-from .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-
-class MinPitchAnalyst(Analyst):
-    """
-    风电机组最小桨距角分析
-    """
-
-    def typeAnalyst(self):
-        return "min_pitch"
-
-    def turbineAnalysis(self,
-                        dataFrame,
-                        outputAnalysisDir,
-                        outputFilePath,
-                        confData: ConfBusiness,
-                        turbineName):
-        self.min_pitch(dataFrame, outputFilePath,
-                       confData.field_turbine_time, confData.field_pitch_angle1, confData.field_power)
-
-    def min_pitch(self, dataFrame,  output_path, time_col, pitch_col, power_col):
-        # Convert time column to datetime and extract date
-        dataFrame['date_'] = pd.to_datetime(
-            dataFrame[time_col], format='%Y-%m-%d %H:%M:%S').dt.date
-
-        # Convert pitch to float and calculate pitch floor
-        dataFrame['pitch'] = dataFrame[pitch_col].astype(float)
-        dataFrame['pitch_floor'] = dataFrame[pitch_col].astype(int)
-
-        # Group by date and pitch floor, then count occurrences
-        grouped = dataFrame.groupby(['date_', 'pitch_floor']
-                                    ).size().reset_index(name='count')
-
-        # Sort by date and pitch floor
-        grouped.sort_values(['date_', 'pitch_floor'], inplace=True)
-
-        # Write to CSV
-        grouped.to_csv(output_path, index=False)
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.create_and_save_plot(outputAnalysisDir, confData.farm_name)
-
-    def create_and_save_plot(self, csvFileDirOfCp, farm_name, encoding='utf-8'):
-        """
-        Generates Cp distribution plots for turbines in a wind farm.
-
-        Parameters:
-        - csvFileDirOfCp: str, path to the directory containing input CSV files.
-        - farm_name: str, name of the wind farm.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """
-
-        field_Name_Turbine = "turbine_name"
-        file_time = 'date_'
-        pitch_name = 'pitch_floor'
-        count_name = 'count'
-        split_way = 'merged_min_pitch'
-        split_way = '_min_pitch.csv'
-
-        # 遍历输入路径下的所有文件
-        for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):
-            for file_name in file_names:
-
-                if not file_name.endswith(".csv"):
-                    continue
-
-                print(os.path.join(root, file_name))
-                data = pd.read_csv(os.path.join(
-                    root, file_name), encoding=encoding)
-
-                # 将日期列转换为日期类型
-                data[file_time] = pd.to_datetime(
-                    data[file_time], format='%Y-%m-%d')
-
-                # 计算count的对数
-                data['log_of_count'] = data[count_name].apply(
-                    lambda x: math.log2(x))
-
-                # 获取输出文件名(不含split_way之后的部分)
-                turbine_name = file_name.split(split_way)[0]
-                # 添加设备名作为新列
-                data[field_Name_Turbine] = turbine_name
-
-                # 创建图表
-                fig, ax = plt.subplots(figsize=(12, 8), dpi=120)
-                cmap = cm.get_cmap('Blues')
-                norm = plt.Normalize(
-                    data['log_of_count'].min(), data['log_of_count'].max())
-
-                # 绘制散点图
-                ax.scatter(x=data[file_time], y=data[pitch_name],
-                           c=data['log_of_count'], cmap=cmap, s=5)
-
-                # 设置图表属性
-                ax.set_xlabel('time')
-                ax.set_ylabel('pitch angle')
-                ax.set_ylim(-2, 60)
-                ax.margins(x=0)
-
-                # 创建颜色条
-                sm = cm.ScalarMappable(norm=norm, cmap=cmap)
-                cbar = fig.colorbar(sm, ax=ax)
-                cbar.set_label('log2(count)')
-
-                # 设置图表标题
-                ax.set_title('turbine_name={}'.format(turbine_name))
-
-                # 保存图表
-                plot_path = os.path.join(
-                    csvFileDirOfCp, "{}.png".format(turbine_name))
-                plt.savefig(plot_path, bbox_inches='tight', dpi=120)
-
-                # 关闭图表,释放资源
-                plt.close(fig)

+ 0 - 177
wtoaamapi/apps/business/algorithm/outputProcessor.py

@@ -1,177 +0,0 @@
-import os
-from datetime import datetime, timezone, timedelta
-from logging import Logger
-import pandas as pd
-from sqlalchemy.orm import Session
-from sqlalchemy.sql import text
-import shutil
-from urllib.parse import quote, unquote
-from common.commonBusiness import CommonBusiness
-from common.appConfig import GetBusinessFoundationDbUtil
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from algorithmContract.const import *
-from utils.minioUtil.threadSafeMinioClient import ThreadSafeMinioClient
-from utils.rdbmsUtil.databaseUtil import DatabaseUtil
-
-Const_FileURL = "url"
-
-
-class OutputProcessor:
-    def __init__(self, conf: Contract, logger: Logger, dbUtil: dict[str, DatabaseUtil], minioUtil: ThreadSafeMinioClient) -> None:
-        self.conf = conf
-        self.autoOrManual = 1 if self.conf.dataContract.autoOrManual == 'automatic' else 0
-        self.powerFarmID = self.conf.dataContract.dataFilter.powerFarmID
-        self.dataBatchNum = self.conf.dataContract.dataFilter.dataBatchNum
-
-        self.logger = logger
-        self.dbUtil = dbUtil
-        self.minioUtil = minioUtil
-
-        self.common = CommonBusiness()
-
-    def uploadOfMioIO(self, bucketName: str, uploadFiles):
-        """
-        上传文件到minio
-        """
-        bucketName = bucketName.lower()
-        if not self.minioUtil.bucket_exists(bucketName):
-            self.minioUtil.create_bucket(bucketName)
-            self.minioUtil.set_bucket_policy(bucketName)
-
-        # Upload files
-        upload_results = self.minioUtil.upload_files(bucketName, uploadFiles)
-
-    def removeLocalFiles(self, powerFarmID: str, dataBatchNum: str):
-        directory = f"output/{powerFarmID}/{dataBatchNum}"
-        shutil.rmtree(directory)
-
-    def analysisState(self, session: Session, batchNO: str, analysisState: int, errorState: int = ErrorState.NotErr.value, errorCode: str = None, errorInfo: str = None, analysisProgress: float = 0,analysis_finish_time: datetime=datetime.now(timezone.utc) + timedelta(hours=8)):
-        """
-        写处理状态 至主表 analysis_result
-        写总图(多机组一张图表)上传文件 至子表 analysis_general_file
-        写单机组图(单机组一张图表)上传文件 至子表 analysis_diagram_relation
-        """
-        sql = text(f"INSERT INTO analysis_result(batch_code, analysis_state, err_state, err_code, err_info,  analysis_progress, analysis_finish_time) \
-                  VALUES(:batch_code, :analysis_state, :err_state, :err_code, :err_info, :analysis_progress, :analysis_finish_time) \
-                  ON DUPLICATE KEY \
-                  UPDATE \
-                  analysis_state=VALUES(analysis_state), \
-                  err_state=VALUES(err_state), \
-                  err_code=VALUES(err_code), \
-                  err_info=VALUES(err_info), \
-                  analysis_progress=VALUES(analysis_progress), \
-                  analysis_finish_time=VALUES(analysis_finish_time);")
-
-        params = {
-            "batch_code": None if self.common.isNone(batchNO) else batchNO,
-            "analysis_state": None if self.common.isNone(analysisState) else analysisState,
-            "err_state": None if self.common.isNone(analysisState) else errorState,
-            "err_code": None if self.common.isNone(errorCode) else errorCode,
-            "err_info": None if self.common.isNone(errorInfo) else errorInfo,
-            "analysis_progress": analysisProgress,
-            "analysis_finish_time":analysis_finish_time if analysis_finish_time is not None else None
-        }
-
-        session.execute(sql, params)
-
-    def analysisResultForTurbine(self, session: Session, returnDataFrame: pd.DataFrame):
-        dataFrame = returnDataFrame[(returnDataFrame[Field_CodeOfTurbine] != 'total') & (
-            returnDataFrame[Field_Return_IsSaveDatabase])]
-
-        for index, row in dataFrame.iterrows():
-            sql = text(f"""
-                INSERT INTO analysis_diagram_relation 
-                (batch_code, field_engine_code, analysis_type_code, file_addr, auto_analysis) 
-                VALUES (:batch_code, :field_engine_code, :analysis_type_code, :file_addr, :auto_analysis)
-                ON DUPLICATE KEY UPDATE 
-                field_engine_code=VALUES(field_engine_code),
-                analysis_type_code=VALUES(analysis_type_code),
-                file_addr=VALUES(file_addr),
-                auto_analysis=VALUES(auto_analysis);
-            """)
-
-            params = {
-                "batch_code": row[Field_Return_BatchCode],
-                "field_engine_code": row[Field_CodeOfTurbine],
-                "analysis_type_code": row[Field_Return_TypeAnalyst],
-                "file_addr": row[Const_FileURL],
-                "auto_analysis": self.autoOrManual
-            }
-
-            session.execute(sql, params)
-
-    def analysisResultForTotal(self, session: Session, returnDataFrame: pd.DataFrame):
-        dataFrame = returnDataFrame[(returnDataFrame[Field_CodeOfTurbine] == 'total') & (
-            returnDataFrame[Field_Return_IsSaveDatabase])]
-
-        for index, row in dataFrame.iterrows():
-            sql = text(f"""
-                INSERT INTO analysis_general_file 
-                (batch_code, analysis_type_code, engine_type_code, file_addr, auto_analysis) 
-                VALUES (:batch_code, :analysis_type_code, :engine_type_code, :file_addr, :auto_analysis)
-                ON DUPLICATE KEY UPDATE 
-                analysis_type_code=VALUES(analysis_type_code),
-                engine_type_code=VALUES(engine_type_code),
-                file_addr=VALUES(file_addr),
-                auto_analysis=VALUES(auto_analysis);
-            """)
-
-            params = {
-                "batch_code": row[Field_Return_BatchCode],
-                "analysis_type_code": row[Field_Return_TypeAnalyst],
-                "engine_type_code": row[Field_MillTypeCode],
-                "file_addr": row[Const_FileURL],
-                "auto_analysis": self.autoOrManual
-            }
-
-            session.execute(sql, params)
-
-    def process(self, powerFarmID: str, dataBatchNum: str, returnDataFrame: pd.DataFrame, timestamp: datetime = datetime.now(timezone.utc)+timedelta(hours=8)):
-        try:
-            uploadFiles = []
-            if not returnDataFrame.empty:
-                returnDataFrame[Const_FileURL] = None
-
-            if Field_Return_IsSaveDatabase in returnDataFrame.columns:
-                returnDataFrame[Field_Return_IsSaveDatabase].fillna(
-                    True, inplace=True)
-            else:
-                returnDataFrame[Field_Return_IsSaveDatabase] = True
-
-            for index, row in returnDataFrame.iterrows():
-                directory, fileName = os.path.split(row[Field_Return_FilePath])
-                basePath = f"output/{powerFarmID}"
-                subPath = os.path.relpath(directory, basePath)
-                remoteFilePath = os.path.join(
-                    subPath, fileName).replace("\\", "/")
-                # arr=["http://",self.minioUtil.client_pool.get_ip_address(),"/",powerFarmID.lower(),"/",remoteFilePath]
-                arr = [powerFarmID.lower(), "/", remoteFilePath]
-                fileURL = "".join(arr)
-                returnDataFrame.at[index, Const_FileURL] = quote(fileURL)
-                uploadFiles.append(
-                    (remoteFilePath, row[Field_Return_FilePath]))
-
-            self.uploadOfMioIO(self.powerFarmID, uploadFiles)
-
-            foundationDB = GetBusinessFoundationDbUtil()
-
-            with foundationDB.session_scope() as session:
-                # 第一次调用,设置进度为50%
-                self.analysisState(session, self.dataBatchNum, AnalysisState.Analyzed.value,
-                                   ErrorState.NotErr.value, None, None, 50)
-                self.analysisResultForTotal(
-                    session, returnDataFrame)
-                self.analysisResultForTurbine(
-                    session, returnDataFrame)
-                  # 获取分析完成的时间
-            finish_time = datetime.now(timezone.utc) + timedelta(hours=8)
-
-            # 第二次调用:更新分析状态为已完成,进度为100%,并记录完成时间
-            self.analysisState(session, self.dataBatchNum, AnalysisState.Analyzed.value,
-                               ErrorState.NotErr.value, None, None, 100, analysis_finish_time=finish_time)
-
-            self.removeLocalFiles(powerFarmID, dataBatchNum)
-        except Exception as e:
-            self.logger.error(e)
-            raise e

+ 0 - 168
wtoaamapi/apps/business/algorithm/pitchGeneratorSpeedAnalyst.py

@@ -1,168 +0,0 @@
-import os
-from datetime import datetime
-
-import numpy as np
-import pandas as pd
-import plotly.graph_objects as go
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from behavior.analystWithGoodBadPoint import AnalystWithGoodBadPoint
-
-
-class PitchGeneratorSpeedAnalyst(AnalystWithGoodBadPoint):
-    """
-    风电机组变桨-发电机转速分析
-    """
-
-    def typeAnalyst(self):
-        return "pitch_generator_speed"
-
-    # def recalculation(self, turbineModelInfo: pd.Series, dataFrame: pd.DataFrame):
-    #     return self.recalculationOfGeneratorSpeed(
-    #         dataFrame, Field_RotorSpeed, Field_GeneratorSpeed,  self.turbineModelInfo[Field_RSR].iloc[0])
-    def selectColumns(self):
-        return [Field_DeviceCode, Field_Time,Field_WindSpeed,Field_ActiverPower,Field_PitchAngel1, Field_GeneratorSpeed]
-
-    def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
-        dictionary = self.processTurbineData(turbineCodes, conf,self.selectColumns())
-        turbineInfos = self.common.getTurbineInfos(conf.dataContract.dataFilter.powerFarmID, turbineCodes,
-                                                   self.turbineInfo)
-        dataFrame = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
-        return self.plot_speed_pitch_angle(dataFrame, turbineInfos, outputAnalysisDir, conf, )
-
-    def plot_speed_pitch_angle(self, dataFrameMerge, turbineModelInfo: pd.Series, outputAnalysisDir, conf: Contract):
-        # 按设备名分组数据
-        dataFrameMerge = dataFrameMerge[(dataFrameMerge[Field_ActiverPower] > 0)].sort_values(by=Field_YearMonth)
-        grouped = dataFrameMerge.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
-
-        # 遍历每个设备并绘制散点图
-        result_rows = []
-        for name, group in grouped:
-            groupNew = group.copy()
-
-            # 处理直驱转速与非直驱的差异
-            # if not self.common.isNone(conf.value_gen_speed_multiple):
-            #     groupNew[fieldGeneratorSpeed] = group[fieldGeneratorSpeed] * \
-            #         conf.value_gen_speed_multiple
-
-            # 创建图形
-            fig = go.Figure()
-
-            # 添加散点图
-            fig.add_trace(go.Scatter(
-                x=groupNew[Field_GeneratorSpeed],
-                y=groupNew[Field_PitchAngel1],
-                mode='markers',
-                # marker=dict(color='blue', size=3.5)
-                marker=dict(
-                    color=group[Field_UnixYearMonth],
-                    colorscale='Rainbow',
-                    size=3,
-                    opacity=0.7,
-                    colorbar=dict(
-                        tickvals=np.linspace(
-                            group[Field_UnixYearMonth].min(), group[Field_UnixYearMonth].max(), 6),
-                        ticktext=[datetime.fromtimestamp(ts).strftime('%Y-%m') for ts in np.linspace(
-                            group[Field_UnixYearMonth].min(), group[Field_UnixYearMonth].max(), 6)],
-                        thickness=18,
-                        len=1,  # 设置颜色条的长度,使其占据整个图的高度
-                        outlinecolor='rgba(255,255,255,0)'
-                    ),
-                    showscale=True
-                ),
-                showlegend=False
-            ))
-
-            # 设置图形布局
-            fig.update_layout(
-                title=f'机组: {name[0]}',
-                xaxis=dict(
-                    title='发电机转速',
-                    range=[self.axisLowerLimitGeneratorSpeed, self.axisUpperLimitGeneratorSpeed],
-                    dtick=self.axisStepGeneratorSpeed,
-                    tickangle=-45 # 设置x轴刻度值旋转角度为45度,如果需要
-                ),
-                yaxis=dict(
-                    title='桨距角',
-                    range=[self.axisLowerLimitPitchAngle, self.axisUpperLimitPitchAngle],
-                    dtick=self.axisStepPitchAngle
-                ),
-                coloraxis=dict(
-                    colorbar=dict(
-                        title="时间",
-                        ticks="outside",
-                        len=1,  # 设置颜色条的长度,使其占据整个图的高度
-                        thickness=20,  # 调整颜色条的宽度
-                        orientation='v',  # 设置颜色条为垂直方向
-                        tickmode='array',  # 确保刻度按顺序排列
-                        tickvals=dataFrameMerge[Field_YearMonth].unique(
-                        ).tolist(),  # 确保刻度为唯一的年月
-                        ticktext=dataFrameMerge[Field_YearMonth].unique(
-                        ).tolist()  # 以%Y-%m格式显示标签
-                    )
-                )
-            )
-            # 确保从 Series 中提取的是具体的值
-            engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-            if isinstance(engineTypeCode, pd.Series):
-                engineTypeCode = engineTypeCode.iloc[0]
-
-            engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-            if isinstance(engineTypeName, pd.Series):
-                engineTypeName = engineTypeName.iloc[0]
-
-            # 使用 apply() 对每个元素调用 datetime.fromtimestamp
-            group[Field_UnixYearMonth] = group[Field_UnixYearMonth].apply(lambda x: datetime.fromtimestamp(x).strftime('%Y-%m'))
-
-            # 构建最终的JSON对象
-            json_output = {
-                "analysisTypeCode": "变桨和发电机转速协调性分析",
-                "engineCode": engineTypeCode,
-                "engineTypeName": engineTypeName,
-                "xaixs": "发电机转速(r/min)",
-                "yaixs": "桨距角(°)",
-                "data": [{
-                    "engineName": name[0],
-                    "engineCode": name[1],
-                    "title": f' 机组: {name[0]}',
-                    "xData": groupNew[Field_GeneratorSpeed].tolist(),
-                    "yData": groupNew[Field_PitchAngel1].tolist(),
-                    "colorbar":group[Field_UnixYearMonth] .tolist(),
-
-                }]
-            }
-            # Save plot
-            # filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
-            # fig.write_image(filePathOfImage, scale=3)
-            # filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
-            # fig.write_html(filePathOfHtml)
-
-            # 将JSON对象保存到文件
-            output_json_path = os.path.join(outputAnalysisDir, f"pitch_GeneratorSpeed_Analyst{name[0]}.json")
-            with open(output_json_path, 'w', encoding='utf-8') as f:
-                import json
-                json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-            # 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: filePathOfImage,
-            #     Field_Return_IsSaveDatabase: 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_MillTypeCode: 'total',
-                Field_Return_FilePath: output_json_path,
-                Field_Return_IsSaveDatabase: True
-            })
-
-        result_df = pd.DataFrame(result_rows)
-
-        return result_df
-

+ 0 - 59
wtoaamapi/apps/business/algorithm/pitchPowerAnalyst.py

@@ -1,59 +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 .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import *
-
-
-class PitchPowerAnalyst(Analyst):
-    """
-    风电机组变桨-功率分析
-    """
-
-    def typeAnalyst(self):
-        return "pitch_power"
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.plot_power_pitch_angle(dataFrameMerge, outputAnalysisDir, confData)
-
-    def plot_power_pitch_angle(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        x_name = 'power'
-        y_name = 'pitch_angle'
-        # 按设备名分组数据
-        grouped = dataFrameMerge.groupby(Field_NameOfTurbine)
-        print("self.ratedPower {}".format(confData.rated_power))
-        # 遍历每个设备并绘制散点图
-        for name, group in grouped:
-            # sns.lmplot函数参数scatter_kws: 设置为{"s": 5}时,会出现颜色丢失问题;
-            g = sns.lmplot(x=confData.field_power, y=confData.field_pitch_angle1, data=group,
-                           fit_reg=False, scatter_kws={"s": 5, "color": "b"}, legend=False, height=6, aspect=1.2)
-            # g = sns.lmplot(x=confData.field_power, y=confData.field_pitch_angle1, data=group,
-            #                fit_reg=False, scatter_kws={"s": 5}, legend=False, height=6, aspect=1.2)
-
-            # 设置x轴和y轴的刻度
-            for ax in g.axes.flat:
-                ax.xaxis.set_major_locator(MultipleLocator(150))
-                ax.set_xlim(-20, confData.rated_power * (1+0.2))
-
-                ax.yaxis.set_major_locator(MultipleLocator(2))
-                ax.set_ylim(-1, 18)
-
-                ax.set_xlabel(x_name)
-                ax.set_ylabel(y_name)
-
-            # 设置x轴刻度值旋转角度为45度  
-            plt.tick_params(axis='x', rotation=45)
-            # 调整布局和设置标题
-            plt.tight_layout()
-            plt.title(f'{Field_NameOfTurbine}={name}')
-
-            # 保存图像并关闭绘图窗口
-            output_file = os.path.join(outputAnalysisDir, f"{name}.png")
-            plt.savefig(output_file, bbox_inches='tight', dpi=120)
-            plt.close()

+ 0 - 382
wtoaamapi/apps/business/algorithm/powerCurveAnalyst.py

@@ -1,382 +0,0 @@
-import os
-
-import numpy as np
-import pandas as pd
-import plotly.graph_objects as go
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from behavior.analystWithGoodPoint import AnalystWithGoodPoint
-from utils.jsonUtil import JsonUtil
-
-
-class PowerCurveAnalyst(AnalystWithGoodPoint):
-    """
-    风电机组功率曲线散点分析。
-    秒级scada数据运算太慢,建议使用分钟级scada数据
-    """
-
-    def typeAnalyst(self):
-        return "power_curve"
-
-    def turbinesAnalysis(self,  outputAnalysisDir, conf: Contract, turbineCodes):
-        dictionary = self.processTurbineData(turbineCodes, conf, [
-                                             Field_DeviceCode, Field_Time, Field_WindSpeed, Field_ActiverPower])
-        dataFrameOfTurbines = self.userDataFrame(
-            dictionary, conf.dataContract.configAnalysis, self)
-        # 检查所需列是否存在
-        required_columns = {Field_WindSpeed, Field_ActiverPower}
-        if not required_columns.issubset(dataFrameOfTurbines.columns):
-            raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-
-        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)
-
-            dataFrameOfContractPowerCurve = self.dataFrameContractOfTurbine[
-                self.dataFrameContractOfTurbine[Field_MillTypeCode] == turbineModelCode]
-            currDataFrameOfTurbines = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine].isin(
-                currTurbineCodes)]
-
-            powerCurveDataOfTurbines = self.dataReprocess(
-                currDataFrameOfTurbines, self.binsWindSpeed)
-
-            # returnData = self.drawOfPowerCurve(
-            #     powerCurveDataOfTurbines, outputAnalysisDir, conf, dataFrameOfContractPowerCurve, currTurbineModeInfo)
-            # returnDatas.append(returnData)
-
-            returnJsonData= self.outputPowerCurveData(conf,outputAnalysisDir,currTurbineModeInfo,powerCurveDataOfTurbines,dataFrameOfContractPowerCurve)
-            returnDatas.append(returnJsonData)
-
-        returnResult = pd.concat(returnDatas, ignore_index=True)
-
-        return returnResult
-
-    def outputPowerCurveData(self, conf: Contract, outputAnalysisDir: str, turbineModelInfo: pd.Series, powerCurveDataOfTurbines: pd.DataFrame, dataFrameOfContractPowerCurve: pd.DataFrame) -> pd.DataFrame:
-        turbineCodes = powerCurveDataOfTurbines[Field_CodeOfTurbine].unique()
-        jsonDictionary = self.convert2Json(turbineModelInfo,turbineCodes=turbineCodes,
-                                       dataFrameOfTurbines=powerCurveDataOfTurbines, dataFrameOfContract=dataFrameOfContractPowerCurve)
-        jsonFileName = f"power_curve-{turbineModelInfo[Field_MillTypeCode]}.json"
-        jsonFilePath = os.path.join(outputAnalysisDir, jsonFileName)
-
-        JsonUtil.write_json(jsonDictionary, file_path=jsonFilePath)
-        result_rows = []
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: Const_Output_Total,
-            Field_MillTypeCode:turbineModelInfo[Field_MillTypeCode],
-            Field_Return_FilePath: jsonFilePath,
-            Field_Return_IsSaveDatabase: True
-        })
-
-        for turbineCode in turbineCodes:
-            data:pd.DataFrame=powerCurveDataOfTurbines[powerCurveDataOfTurbines[Field_CodeOfTurbine]==turbineCode]
-            jsonFileName2 = f"power_curve-{data[Field_NameOfTurbine].iloc[0]}.json"
-            jsonFilePath2 = os.path.join(outputAnalysisDir, jsonFileName2)
-            JsonUtil.write_json(jsonDictionary, file_path=jsonFilePath2)
-            result_rows.append({
-                Field_Return_TypeAnalyst: self.typeAnalyst(),
-                Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-                Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-                Field_CodeOfTurbine: turbineCode,
-                Field_Return_FilePath: jsonFilePath2,
-                Field_Return_IsSaveDatabase: True
-            })
-
-        returnDatas = pd.DataFrame(result_rows)
-        return returnDatas
-
-    def convert2Json(self, turbineModelInfo: pd.Series,turbineCodes, dataFrameOfTurbines: pd.DataFrame, dataFrameOfContract: pd.DataFrame):
-        result = {
-            "analysisTypeCode":"功率曲线分析",
-            "engineTypeCode":  turbineModelInfo[Field_MillTypeCode] ,
-            "engineTypeName": turbineModelInfo[Field_MachineTypeCode] ,
-            "data": []
-        }
-
-        # 定义要替换的空值类型
-        na_values = {pd.NA, float('nan')}
-
-        # 从对象A提取数据
-        for turbineCode in turbineCodes:
-            data:pd.DataFrame=dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine]==turbineCode]
-            engine_data = {
-                "enginName": data[Field_NameOfTurbine].iloc[0],
-                "enginCode": turbineCode,
-                "xData": data[Field_WindSpeed].replace(na_values, None).tolist(),
-                "yData": data[Field_ActiverPower].replace(na_values, None).tolist(),
-                "zData": []
-            }
-            result["data"].append(engine_data)
-
-        # 从对象B提取数据
-        contract_curve = {
-            "enginName": "合同功率曲线",
-            "xData": dataFrameOfContract[Field_WindSpeed].replace(na_values, None).tolist(),
-            "yData": dataFrameOfContract[Field_ActiverPower].replace(na_values, None).tolist(),
-            "zData": []
-        }
-        result["data"].append(contract_curve)
-
-        return result
-
-    def buildPowerCurveData(self, group: pd.DataFrame, fieldWindSpeed: str, fieldActivePower: str, bins) -> pd.DataFrame:
-        """
-        计算设备的功率曲线。
-        """
-        powerCut = group.groupby(pd.cut(group[fieldWindSpeed], bins, labels=np.arange(0, 25.5, 0.5))).agg({
-            fieldActivePower: 'median',
-            fieldWindSpeed: ['median', 'count']
-        })
-        wind_count = powerCut[fieldWindSpeed]['count'].tolist()
-        line = powerCut[fieldActivePower]['median'].round(decimals=2).tolist()
-        act_line = pd.DataFrame([powerCut.index, wind_count, line]).T
-        act_line.columns = [Field_WindSpeed,
-                            'EffectiveQuantity', Field_ActiverPower]
-        return act_line
-
-    def dataReprocess(self, dataFrameMerge: pd.DataFrame, binsWindSpeed) -> pd.DataFrame:
-        # 初始化结果DataFrame
-        dataFrames = []
-
-        # 按设备名分组数据
-        grouped = dataFrameMerge.groupby(
-            [Field_NameOfTurbine, Field_CodeOfTurbine])
-
-        # 计算每个设备的功率曲线
-        for name, group in grouped:
-            dataFramePowerCurveTurbine = self.buildPowerCurveData(
-                group, Field_WindSpeed, Field_ActiverPower, binsWindSpeed)
-            dataFramePowerCurveTurbine[Field_NameOfTurbine] = name[0]
-            dataFramePowerCurveTurbine[Field_CodeOfTurbine] = name[1]
-
-            dataFrames.append(dataFramePowerCurveTurbine)
-
-        # 绘制全场功率曲线图
-        dataFrameReprocess: pd.DataFrame = pd.concat(
-            dataFrames, ignore_index=True).reset_index(drop=True)
-
-        return dataFrameReprocess
-
-    def drawOfPowerCurve(self, powerCurveOfTurbines: pd.DataFrame, outputAnalysisDir, conf: Contract, dataFrameGuaranteePowerCurve: pd.DataFrame, turbineModelInfo: pd.Series):
-        """
-        生成功率曲线并保存为文件。
-
-        参数:
-        frames (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
-        outputAnalysisDir (str): 分析输出目录。
-        confData (ConfBusiness): 配置
-        """
-        # 绘制全场功率曲线图
-        # ress =self.dataReprocess(dataFrameMerge,self.binsWindSpeed) # all_res.reset_index(drop=True)
-
-        df1 = self.plot_power_curve(
-            powerCurveOfTurbines, outputAnalysisDir, dataFrameGuaranteePowerCurve, Field_NameOfTurbine, conf, turbineModelInfo)
-
-        # 绘制每个设备的功率曲线图
-        grouped = powerCurveOfTurbines.groupby(
-            [Field_NameOfTurbine, Field_CodeOfTurbine])
-
-        df2 = pd.DataFrame()  # 新建一个空表格,与返回的单图功率曲线合并
-        for name, group in grouped:
-            df_temp2 = self.plot_single_power_curve(
-                powerCurveOfTurbines, group, dataFrameGuaranteePowerCurve, name, outputAnalysisDir, conf)
-            df2 = pd.concat([df2, df_temp2], ignore_index=True)
-
-        # 总图与单图的表格合并
-        df = pd.concat([df1, df2], ignore_index=True)
-        return df
-
-    def plot_power_curve(self, ress, output_path, dataFrameGuaranteePowerCurve: pd.DataFrame, Field_NameOfTurbine, conf: Contract, turbineModelInfo: pd.Series):
-        """
-        绘制全场功率曲线图。
-        """
-        # colors = px.colors.sequential.Turbo
-
-        fig = go.Figure()
-
-        for turbine_num in ress[Field_NameOfTurbine].unique():
-            turbine_data = ress[ress[Field_NameOfTurbine] == turbine_num]
-            # 循环创建风速-功率折线
-            fig.add_trace(go.Scatter(
-                x=turbine_data[Field_WindSpeed],
-                y=turbine_data[Field_ActiverPower],
-                mode='lines',
-                # line=dict(color=colors[idx % len(colors)]),
-                name=f'{turbine_num}'  # 使用风电机组编号作为图例的名称
-            )
-            )
-
-        if not ress.empty and Field_CutInWS in ress.columns and ress[Field_CutInWS].notna().any():
-            cut_in_ws = ress[Field_CutInWS].min() - 1
-        else:
-            cut_in_ws = 2
-
-        fig.add_trace(go.Scatter(
-            x=dataFrameGuaranteePowerCurve[Field_WindSpeed],
-            y=dataFrameGuaranteePowerCurve[Field_ActiverPower],
-            # mode='lines',
-            # line=dict(color='red', dash='dash'),
-            mode='lines+markers',
-            line=dict(color='red'),
-            marker=dict(color='red', size=5),
-            name='合同功率曲线',
-            showlegend=True
-        )
-        )
-        # 创建布局
-        fig.update_layout(
-            title={
-                "text": f'功率曲线-{turbineModelInfo[Field_MachineTypeCode]}',
-                'x': 0.5
-            },
-            # legend_title='Turbine',
-            xaxis=dict(
-                title='风速',
-                dtick=1,
-                tickangle=-45,
-                range=[cut_in_ws, 25]
-            ),
-            yaxis=dict(
-                title='有功功率',
-                dtick=self.axisStepActivePower,
-                range=[self.axisLowerLimitActivePower,
-                       self.axisUpperLimitActivePower]
-            ),
-            legend=dict(
-                orientation="h",  # Horizontal orientation
-                xanchor="center",  # Anchor the legend to the center
-                x=0.5,  # Position legend at the center of the x-axis
-                y=-0.2,  # Position legend below the x-axis
-                # itemsizing='constant',  # Keep the size of the legend entries constant
-                # itemwidth=50
-            )
-        )
-
-        # 保存HTML
-        htmlFileName = '全场-{}-{}-功率曲线.html'.format(self.powerFarmInfo[Field_PowerFarmName].iloc[0],turbineModelInfo[Field_MillTypeCode])
-        htmlFilePath = os.path.join(output_path, htmlFileName)
-        fig.write_html(htmlFilePath)
-
-        result_rows = []
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: Const_Output_Total,
-            Field_Return_FilePath: htmlFilePath,
-            Field_Return_IsSaveDatabase: False
-        })
-
-        result_df = pd.DataFrame(result_rows)
-        return result_df
-
-    def plot_single_power_curve(self, ress, group, dataFrameGuaranteePowerCurve: pd.DataFrame, turbineName, outputAnalysisDir, conf: Contract):
-
-        fig = go.Figure()
-        for turbine_num in ress[Field_NameOfTurbine].unique():
-            turbine_data = ress[ress[Field_NameOfTurbine] == turbine_num]
-
-            # 循环创建风速-功率折线
-            fig.add_trace(go.Scatter(
-                x=turbine_data[Field_WindSpeed],
-                y=turbine_data[Field_ActiverPower],
-                mode='lines',
-                line=dict(color='lightgrey'),
-                name=f'{turbine_num}',
-                showlegend=False
-            )
-            )
-
-        if not ress.empty and Field_CutInWS in ress.columns and ress[Field_CutInWS].notna().any():
-            cut_in_ws = ress[Field_CutInWS].min() - 1
-        else:
-            cut_in_ws = 2
-
-        fig.add_trace(go.Scatter(
-            x=group[Field_WindSpeed],
-            y=group[Field_ActiverPower],
-            mode='lines',
-            line=dict(color='darkblue'),
-            name=Field_ActiverPower,
-            showlegend=False
-        )
-        )
-
-        fig.add_trace(go.Scatter(
-            x=dataFrameGuaranteePowerCurve[Field_WindSpeed],
-            y=dataFrameGuaranteePowerCurve[Field_ActiverPower],
-            mode='lines+markers',
-            line=dict(color='red'),
-            marker=dict(color='red', size=5),
-            name='合同功率曲线',
-            showlegend=True
-        )
-        )
-
-        # 创建布局
-        fig.update_layout(
-            title={
-                "text": f'机组: {turbineName[0]}'
-            },
-            legend=dict(
-                orientation="h",  # 或者 "v" 表示垂直
-                yanchor="bottom",  # 图例垂直对齐方式
-                y=0,  # 图例距离y轴下边界的距离(0到1之间)
-                xanchor="right",  # 图例水平对齐方式
-                x=1,  # 图例距离x轴右边界的距离(0到1之间)
-                bgcolor='rgba(255,255,255,0)'
-            ),
-            xaxis=dict(
-                title='风速',
-                dtick=1,
-                tickangle=-45,
-                range=[cut_in_ws, 25]
-            ),
-            yaxis=dict(
-                title='有功功率',
-                dtick=self.axisStepActivePower,
-                range=[self.axisLowerLimitActivePower,
-                       self.axisUpperLimitActivePower]
-            )
-        )
-
-        # 保存图像
-        # pngFileName = f"{turbineName[0]}.png"
-        # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-        # fig.write_image(pngFilePath, scale=3)
-
-        # # 保存HTML
-        # htmlFileName = f"{turbineName[0]}.html"
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig.write_html(htmlFilePath)
-
-        result_rows = []
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: turbineName[1],
-        #     Field_Return_FilePath: pngFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: turbineName[1],
-        #     Field_Return_FilePath: htmlFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        result_df = pd.DataFrame(result_rows)
-        return result_df

+ 0 - 107
wtoaamapi/apps/business/algorithm/powerOscillationAnalyst.py

@@ -1,107 +0,0 @@
-import os
-
-import matplotlib.pyplot as plt
-import pandas as pd
-import seaborn as sns
-from algorithmContract.confBusiness import *
-from behavior.analyst import Analyst
-from utils.directoryUtil import DirectoryUtil as dir
-
-
-class PowerOscillationAnalyst(Analyst):
-    """
-    风电机组功率震荡分析
-    """
-
-    def typeAnalyst(self):
-        return "power_diff"
-
-    def turbineAnalysis(self,
-                 dataFrame,
-                 outputAnalysisDir,
-                 outputFilePath,
-                 confData: ConfBusiness,
-                 turbineName):
-        self.power_diff(dataFrame, outputFilePath,
-                        confData.field_power,confData.field_rotor_speed)
-
-    def power_diff(self, dataFrame, outputFilePath,  field_Active_Power, field_Rotor_Speed):
-        # Floor the power column to the nearest 10
-        dataFrame['power_col_floor'] = (
-            dataFrame[field_Active_Power] // 10 * 10).astype('int32')
-
-        # Group by the floored power column
-        grouped = dataFrame.groupby('power_col_floor')
-
-        # Calculate max, min, and diff of generator speed within each group
-        agg_df = grouped[field_Rotor_Speed].agg(
-            speed_max='max',
-            speed_min='min'
-        )
-        agg_df['speed_diff'] = agg_df['speed_max'] - agg_df['speed_min']
-
-        # Sort by the floored power column
-        agg_df = agg_df.sort_index()
-
-        # Write the result to a CSV file
-        agg_df.to_csv(outputFilePath)
-
-    def turbinesAnalysis(self,dataFrameMerge,outputAnalysisDir, confData: ConfBusiness):
-        self.plot_power_oscillations(outputAnalysisDir,confData.farm_name)
-
-    def plot_power_oscillations(self, csvFileDirOfCp, farm_name, encoding='utf-8'):
-        """
-        Plot TSR trend from CSV files in a given input path and save the plots to an output path.
-
-        Parameters:
-        - csvFileDirOfCp: str, path to the directory containing input CSV files.
-        - farm_name: str, name of the wind farm.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """
-        sns.set_palette('deep')
-        field_Name_Turbine = "turbine_name"
-        x_name = 'power_col_floor'
-        y_name = 'speed_diff'
-
-        # 初始化结果DataFrame
-        res = pd.DataFrame()
-
-        # 遍历输入路径下的所有文件
-        for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):
-            for file_name in file_names:
-
-                if not file_name.endswith(CSVSuffix):
-                    continue
-
-                file_path = os.path.join(root, file_name)
-                frame = pd.read_csv(file_path, encoding=encoding)
-
-                # 获取输出文件名前缀
-                turbine_name = file_name.split(CSVSuffix)[0]
-                # 添加设备名作为新列
-                frame[field_Name_Turbine] = turbine_name
-
-                selected_data = frame.loc[:, [field_Name_Turbine, x_name, y_name]]
-                res = pd.concat([res, selected_data], axis=0)
-
-        # 重置索引
-        ress = res.reset_index(drop=True)
-
-        # 绘制所有设备的功率震荡图
-        fig, ax = plt.subplots(figsize=(16, 8))
-        ax = sns.lineplot(x=x_name, y=y_name, data=ress, hue=field_Name_Turbine)
-        ax.set_title(f'功率震荡-{self.turbineModelInfo[Field_MachineTypeCode].iloc[0]}')
-        plt.legend(ncol=4)
-        plt.savefig(csvFileDirOfCp+ r'/{}-Power-Oscillation.png'.format(farm_name), bbox_inches='tight', dpi=300)
-        plt.close()
-
-        # 分组绘制每个设备的功率震荡图
-        grouped = ress.groupby(field_Name_Turbine)
-        for name, group in grouped:
-            color = ["lightgrey"] * len(ress[field_Name_Turbine].unique())
-            fig, ax = plt.subplots(figsize=(8, 8))
-            ax = sns.lineplot(x=x_name, y=y_name, data=ress, hue=field_Name_Turbine, palette=sns.color_palette(color), legend=False)
-            ax = sns.lineplot(x=x_name, y=y_name, data=group, color='darkblue', legend=False)
-            ax.set_title('turbine_name={}'.format(name))
-            plt.savefig(csvFileDirOfCp+ r'/{}.png'.format(name), bbox_inches='tight', dpi=120)
-            plt.close()

+ 0 - 278
wtoaamapi/apps/business/algorithm/ratedPowerWindSpeedAnalyst.py

@@ -1,278 +0,0 @@
-import os
-
-import pandas as pd
-import plotly.graph_objects as go
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from behavior.analystWithGoodBadPoint import AnalystWithGoodBadPoint
-from plotly.subplots import make_subplots
-
-
-class RatedPowerWindSpeedAnalyst(AnalystWithGoodBadPoint):
-    """
-    风电机组额定功率风速分析。
-    秒级scada数据运算太慢,建议使用分钟级scada数据
-    """
-
-    def typeAnalyst(self):
-        return "rated_power_windspeed"
-
-    def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
-        dictionary=self.processTurbineData(turbineCodes,conf,[Field_DeviceCode,Field_Time,Field_EnvTemp,Field_WindSpeed,Field_ActiverPower])
-        dataFrameMerge=self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
-        turbineInfos = self.common.getTurbineInfos(conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
-        return self.draw(dataFrameMerge, outputAnalysisDir, conf,turbineInfos)
-
-    def draw(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract,turbineModelInfo: pd.Series):
-        """
-        绘制并保存额定满发风速功率分布图,根据环境温度是否大于等于25℃。
-
-        参数:
-        dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
-        outputAnalysisDir (str): 分析输出目录。
-        confData (ConfBusiness): 配置
-        """
-        # 检查所需列是否存在
-        required_columns = {Field_EnvTemp,
-                            Field_WindSpeed, Field_ActiverPower}
-        if not required_columns.issubset(dataFrameMerge.columns):
-            raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-
-        y_name = '功率'
-        upLimitOfPower = self.turbineInfo[Field_RatedPower].max() * 1.2
-        lowLimitOfPower = self.turbineInfo[Field_RatedPower].max()*0.9
-        field_RatedWindSpeed = self.turbineModelInfo[Field_RatedWindSpeed].max()
-
-        # 根据环境温度筛选数据
-        over_temp = dataFrameMerge[(dataFrameMerge[Field_EnvTemp] >= 25) & (
-            dataFrameMerge[Field_WindSpeed] >= field_RatedWindSpeed) & (dataFrameMerge[Field_ActiverPower] >= lowLimitOfPower)].sort_values(by=Field_NameOfTurbine)
-        below_temp = dataFrameMerge[(dataFrameMerge[Field_EnvTemp] < 25) & (
-            dataFrameMerge[Field_WindSpeed] >= field_RatedWindSpeed) & (dataFrameMerge[Field_ActiverPower] >= lowLimitOfPower)].sort_values(by=Field_NameOfTurbine)
-
-        # 绘制环境温度大于等于25℃的功率分布图
-        fig = make_subplots(rows=1, cols=1)
-        fig.add_trace(go.Box(y=over_temp[Field_ActiverPower], x=over_temp[Field_NameOfTurbine],
-                             # name='Ambient Temp >= 25°C',
-                             boxpoints='outliers',
-                             # box line color
-                             line=dict(color='black', width=1),
-                             # quartilemethod='exclusive',
-                             fillcolor='dodgerblue',
-                             showlegend=False,
-                             marker=dict(color='rgba(0, 0, 0, 0)', size=0.1)),
-                      row=1, col=1)
-
-        # Calculate medians and plot them as a line for visibility
-        medians = over_temp.groupby(Field_NameOfTurbine)[
-            Field_ActiverPower].median()
-        median_line = go.Scatter(
-            x=medians.index,
-            y=medians.values,
-            mode='markers',
-            marker=dict(symbol='line-ew-open', color='red', size=12),
-            showlegend=False
-        )
-        fig.add_trace(median_line)
-
-        # 更新布局
-        fig.update_yaxes(title_text=y_name, row=1, col=1, range=[
-                         lowLimitOfPower, upLimitOfPower], tickfont=dict(size=10))
-        fig.update_xaxes(title_text='机组', type='category',
-                         tickangle=-45, tickfont=dict(size=10))
-        fig.update_layout(title={
-                          'text': f'额定功率分布(环境温度>=25摄氏度)', 'x': 0.5}, boxmode='group')
-        # 确保从 Series 中提取的是具体的值
-        engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-        if isinstance(engineTypeCode, pd.Series):
-            engineTypeCode = engineTypeCode.iloc[0]
-        engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-        if isinstance(engineTypeName, pd.Series):
-            engineTypeName = engineTypeName.iloc[0]
-        # 构建最终的JSON对象
-        json_output = {
-            "analysisTypeCode": "额定功率和风速分析",
-            "engineCode":  engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "xaixs": "机组",
-            "yaixs": "功率(kW)",
-            "data": [{
-                    "title":f'额定功率分布(环境温度>=25摄氏度)',
-                    "xData": over_temp[Field_NameOfTurbine].tolist(),
-                    "yData": over_temp[Field_ActiverPower].tolist(),
-                    "linecolor":'black',
-                    "linewidth":1,
-                    "fillcolor":'dodgerblue'
-                    }]
-        }
-        result_rows = []
-        # 保存图像
-        # pngFileName = "额定满发风速功率分布(10min)(环境温度大于25度).png"
-        # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-        # fig.write_image(pngFilePath, scale=3)
-
-        # # 保存HTML
-        # htmlFileName = "额定满发风速功率分布(10min)(环境温度大于25度).html"
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig.write_html(htmlFilePath)
-        # 保存Json
-        # 将JSON对象保存到文件
-        output_json_path = os.path.join(outputAnalysisDir, "total_more_25.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-        # 如果需要返回DataFrame,可以包含文件路径
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: 'total_less_25',
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: 'total',
-        #     Field_Return_FilePath: pngFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: 'total',
-        #     Field_Return_FilePath: htmlFilePath,
-        #     Field_Return_IsSaveDatabase: True
-        # })
-
-        # 绘制环境温度小于25℃的功率分布图
-        fig = make_subplots(rows=1, cols=1)
-        fig.add_trace(go.Box(y=below_temp[Field_ActiverPower], x=below_temp[Field_NameOfTurbine],
-                             # name='Ambient Temp < 25°C',
-                             boxpoints='outliers',
-                             # box line color
-                             line=dict(color='black', width=1),
-                             # quartilemethod='exclusive',
-                             fillcolor='dodgerblue',
-                             showlegend=False,
-                             marker=dict(color='rgba(0, 0, 0, 0)', size=0.1)),
-                      row=1, col=1)
-
-        # Calculate medians and plot them as a line for visibility
-        medians = below_temp.groupby(Field_NameOfTurbine)[
-            Field_ActiverPower].median()
-        median_line = go.Scatter(
-            x=medians.index,
-            y=medians.values,
-            mode='markers',
-            marker=dict(symbol='line-ew-open', color='red', size=10),
-            showlegend=False
-        )
-        fig.add_trace(median_line)
-
-        # 更新布局
-        fig.update_yaxes(title_text=y_name, row=1, col=1, range=[
-                         lowLimitOfPower, upLimitOfPower], tickfont=dict(size=10))
-        fig.update_xaxes(title_text='机组', type='category',
-                         tickangle=-45, tickfont=dict(size=10))
-        fig.update_layout(title={
-                          'text': f'额定功率分布(环境温度<25摄氏度)', 'x': 0.5}, boxmode='group')
-        # 构建最终的JSON对象2
-        json_output2 = {
-            "analysisTypeCode": "额定功率和风速分析",
-            "engineCode":  engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "xaixs": "机组",
-            "yaixs": "功率(kw)",
-            "data": [{
-                    "title":f'额定功率分布(环境温度<25摄氏度)',
-                    "xData": below_temp[Field_NameOfTurbine].tolist(),
-                    "yData": below_temp[Field_ActiverPower].tolist(),
-                    "linecolor":'black',
-                    "linewidth":1,
-                    "fillcolor":'dodgerblue'
-                    }]
-        }
-        # 保存图像
-        # pngFileName = "额定满发风速功率分布(10min)(环境温度小于25度).png"
-        # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-        # fig.write_image(pngFilePath, scale=3)
-
-        # # 保存HTML
-        # htmlFileName = "额定满发风速功率分布(10min)(环境温度小于25度).html"
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig.write_html(htmlFilePath)
-                # 保存Json
-        # 将JSON对象保存到文件
-        output_json_path2 = os.path.join(outputAnalysisDir, "total_less_25.json")
-        with open(output_json_path2, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output2, f, ensure_ascii=False, indent=4)
-        # 如果需要返回DataFrame,可以包含文件路径
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: 'total_less_25',
-            Field_Return_FilePath: output_json_path2,
-            Field_Return_IsSaveDatabase: True
-        })
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: 'total',
-        #     Field_Return_FilePath: pngFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: 'total',
-        #     Field_Return_FilePath: htmlFilePath,
-        #     Field_Return_IsSaveDatabase: True
-        # })
-
-        result_df = pd.DataFrame(result_rows)
-
-        return result_df
-
-        """
-        # 绘制环境温度大于等于25℃的功率分布图
-        fig, ax = plt.subplots()
-        sns.boxplot(y=confData.field_power, x=Field_NameOfTurbine, data=over_temp, fliersize=0, ax=ax,
-                    medianprops={'linestyle': '-', 'color': 'red'},
-                    boxprops={'color': 'dodgerblue', 'facecolor': 'dodgerblue'})
-        ax.yaxis.set_major_locator(ticker.MultipleLocator(100))
-        ax.set_ylim(lowLimitOfPower, upLimitOfPower)
-        ax.set_ylabel(y_name)
-        ax.set_title(
-            'rated wind speed and power distribute(10min)(ambient temperature>=25℃)')
-        ax.grid(True)
-        plt.xticks(rotation=45)  # 旋转45度
-        plt.savefig(os.path.join(outputAnalysisDir,
-                    "额定满发风速功率分布(10min)(环境温度大于25度).png"), bbox_inches='tight', dpi=120)
-        plt.close()
-
-        # 绘制环境温度小于25℃的功率分布图
-        fig, ax = plt.subplots()
-        sns.boxplot(y=confData.field_power, x=Field_NameOfTurbine, data=below_temp, fliersize=0, ax=ax,
-                    medianprops={'linestyle': '-', 'color': 'red'},
-                    boxprops={'color': 'dodgerblue', 'facecolor': 'dodgerblue'})
-        ax.yaxis.set_major_locator(ticker.MultipleLocator(100))
-        ax.set_ylim(lowLimitOfPower, upLimitOfPower)
-        ax.set_ylabel(y_name)
-        ax.set_title(
-            'rated wind speed and power distribute(10min)(ambient temperature<25℃)')
-        ax.grid(True)
-        plt.xticks(rotation=45)  # 旋转45度
-        plt.savefig(os.path.join(outputAnalysisDir,
-                    "额定满发风速功率分布(10min)(环境温度小于25度).png"), bbox_inches='tight', dpi=120)
-        plt.close()
-        """

+ 0 - 138
wtoaamapi/apps/business/algorithm/ratedWindSpeedAnalyst.py

@@ -1,138 +0,0 @@
-import os
-
-import pandas as pd
-import plotly.graph_objects as go
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from behavior.analystWithGoodBadPoint import AnalystWithGoodBadPoint
-
-
-class RatedWindSpeedAnalyst(AnalystWithGoodBadPoint):
-    """
-    风电机组额定风速分析。
-    秒级scada数据运算太慢,建议使用分钟级scada数据
-    """
-
-    def typeAnalyst(self):
-        return "rated_windspeed"
-
-    def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
-        # dictionary=self.processTurbineData(turbineCodes,conf,[Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower])
-        # dataFrameMerge=self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
-        dictionary = self.processTurbineData(turbineCodes, conf,
-                                             [Field_DeviceCode, Field_Time, Field_WindSpeed, Field_ActiverPower])
-        turbineInfos = self.common.getTurbineInfos(conf.dataContract.dataFilter.powerFarmID, turbineCodes,
-                                                   self.turbineInfo)
-        dataFrameMerge = self.userDataFrame(dictionary, conf.dataContract.configAnalysis, self)
-        return self.draw(dataFrameMerge, turbineInfos, outputAnalysisDir, conf)
-
-
-    def draw(self, dataFrameMerge: pd.DataFrame, turbineModelInfo: pd.Series, outputAnalysisDir, conf: Contract):
-        """
-        绘制并保存满发风速区间数据计数图。
-
-        参数:
-        dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
-        outputAnalysisDir (str): 分析输出目录。
-        confData (ConfBusiness): 配置
-        """
-
-        # 初始化结果列表
-        res = []
-
-        # 按设备名分组并计算统计数据
-        grouped = dataFrameMerge.groupby(Field_NameOfTurbine)
-        for name, group in grouped:
-            group = group[group[Field_WindSpeed] >= 11]
-            res.append([name, group[Field_ActiverPower].min(), group[Field_ActiverPower].max(
-            ), group[Field_ActiverPower].median(), group.shape[0]])
-
-        # 创建结果DataFrame
-        data = pd.DataFrame(res, columns=[
-                            Field_NameOfTurbine, 'power-min', 'power-max', 'power-median', 'count'])
-
-        fig = go.Figure(data=[go.Bar(
-                        x=data[Field_NameOfTurbine],
-                        y=data['count'],
-                        marker_color='dodgerblue'
-                        )
-        ]
-        )
-
-        fig.update_layout(
-            title={
-                "text": f'额定风速间隔数据计数',
-                        'x': 0.5
-            },
-            xaxis=dict(
-                title='机组',
-                tickangle=-45
-            ),
-            yaxis=dict(
-                title='总数'
-            )
-        )
-
-        # 确保从 Series 中提取的是具体的值
-
-        engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-        if isinstance(engineTypeCode, pd.Series):
-            engineTypeCode = engineTypeCode.iloc[0]
-
-        engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-        if isinstance(engineTypeName, pd.Series):
-            engineTypeName = engineTypeName.iloc[0]
-        # 构建最终的JSON对象
-        json_output = {
-            "analysisTypeCode": "额定风速分析",
-            "engineCode": engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "xaixs": "机组",
-            "yaixs": "总数",
-            "data": [{
-                "engineName": "",
-                "engineCode": "",
-                "title": f' 额定风速间隔数据计数',
-                "xData": data[Field_NameOfTurbine].tolist(),
-                "yData": data['count'].tolist(),
-            }]
-        }
-
-        result_rows = []
-        # 保存图像
-        # pngFileName = '风速区间数据计数.png'
-        # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-        # fig.write_image(pngFilePath, scale=3)
-
-        # 保存HTML
-        # htmlFileName = '风速区间数据计数.html'
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig.write_html(htmlFilePath)
-        # 将JSON对象保存到文件
-        output_json_path = os.path.join(outputAnalysisDir, f"rated_WindSpeed.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: 'total',
-        #     Field_Return_FilePath: pngFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: 'total',
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-
-        result_df = pd.DataFrame(result_rows)
-
-        return result_df

+ 0 - 319
wtoaamapi/apps/business/algorithm/temperatureEnvironmentAnalyst.py

@@ -1,319 +0,0 @@
-import os
-
-import numpy as np
-import pandas as pd
-import plotly.graph_objects as go
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from behavior.analystWithGoodBadLimitPoint import AnalystWithGoodBadLimitPoint
-from geopy.distance import geodesic
-from plotly.subplots import make_subplots
-
-
-class TemperatureEnvironmentAnalyst(AnalystWithGoodBadLimitPoint):
-    """
-    风电机组环境温度传感器分析
-    """
-
-    def typeAnalyst(self):
-        return "temperature_environment"
-
-    def turbinesAnalysis(self,  outputAnalysisDir, conf: Contract, turbineCodes):
-        dictionary = self.processTurbineData(turbineCodes, conf, [
-                                             Field_DeviceCode, Field_Time,Field_EnvTemp, Field_WindSpeed, Field_ActiverPower])
-        dataFrameOfTurbines = self.userDataFrame(
-            dictionary, conf.dataContract.configAnalysis, self)
-        # 检查所需列是否存在
-        required_columns = {Field_CodeOfTurbine,Field_EnvTemp}
-        if not required_columns.issubset(dataFrameOfTurbines.columns):
-            raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-
-        #  环境温度分析
-        turbineEnvTempData = dataFrameOfTurbines.groupby(Field_CodeOfTurbine).agg(
-            {Field_EnvTemp: 'median'})
-        turbineEnvTempData = turbineEnvTempData.reset_index()
-        mergeData = self.mergeData(self.turbineInfo, turbineEnvTempData)
-        # 分机型
-        turbrineInfos = self.common.getTurbineInfos(
-            conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
-
-        returnResult= self.draw(mergeData, outputAnalysisDir, conf,turbrineInfos)
-
-        return returnResult
-        # return self.draw(mergeData, outputAnalysisDir, conf)
-
-    def mergeData(self,  turbineInfos: pd.DataFrame, turbineEnvTempData):
-        """
-        将每台机组的环境温度均值数据与机组信息,按机组合并
-
-        参数:
-        turbineInfos (pandas.DataFrame): 机组信息数据
-        turbineEnvTempData (pandas.DataFrame): 每台机组的环境温度均值数据
-
-        返回:
-        pandas.DataFrame: 每台机组的环境温度均值数据与机组信息合并数据
-        """
-
-        """
-        合并类型how的选项包括:
-
-        'inner': 内连接,只保留两个DataFrame中都有的键的行。
-        'outer': 外连接,保留两个DataFrame中任一或两者都有的键的行。
-        'left': 左连接,保留左边DataFrame的所有键,以及右边DataFrame中匹配的键的行。
-        'right': 右连接,保留右边DataFrame的所有键,以及左边DataFrame中匹配的键的行。
-        """
-        # turbineInfos[fieldTurbineName]=turbineInfos[fieldTurbineName].astype(str).apply(confData.add_W_if_starts_with_digit)
-        # turbineEnvTempData[Field_NameOfTurbine] = turbineEnvTempData[Field_NameOfTurbine].astype(
-        #     str)
-        tempDataFrame = pd.merge(turbineInfos, turbineEnvTempData, on=[
-                              Field_CodeOfTurbine], how='inner')
-
-        # 保留指定字段,例如 'Key' 和 'Value1'
-        mergeDataFrame = tempDataFrame[[Field_CodeOfTurbine, Field_NameOfTurbine, Field_Latitude,Field_Longitude, Field_EnvTemp]]
-
-        return mergeDataFrame
-
-    # 定义查找给定半径内点的函数
-    def find_points_within_radius(self, data, center, field_temperature_env, radius):
-        points_within_radius = []
-        for index, row in data.iterrows():
-            distance = geodesic(
-                (center[2], center[1]), (row[Field_Latitude], row[Field_Longitude])).meters
-            if distance <= radius:
-                points_within_radius.append(
-                    (row[Field_NameOfTurbine], row[field_temperature_env]))
-        return points_within_radius
-
-    fieldTemperatureDiff = "temperature_diff"
-
-    # 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,
-                                             Field_Longitude, Field_Latitude, Field_EnvTemp]].apply(tuple, axis=1)
-        coordinates = dataFrame['new'].tolist()
-        # df = pd.DataFrame(coordinates, columns=[Field_NameOfTurbine, Field_Longitude, Field_Latitude, confData.field_env_temp])
-
-        # 查找半径内的点
-        points_within_radius = {coord: self.find_points_within_radius(
-            dataFrame, coord, Field_EnvTemp, self.turbineModelInfo[Field_RotorDiameter].iloc[0]*10) for coord in coordinates}
-        res = []
-        for center, nearby_points in points_within_radius.items():
-            current_temp = dataFrame[dataFrame[Field_NameOfTurbine]
-                                     == center[0]][Field_EnvTemp].iloc[0]
-            target_tuple = (center[0], current_temp)
-            if target_tuple in nearby_points:
-                nearby_points.remove(target_tuple)
-            median_temp = np.median(
-                [i[1] for i in nearby_points]) if nearby_points else current_temp
-            res.append((center[0], nearby_points, median_temp, current_temp))
-        res = pd.DataFrame(
-            res, columns=[Field_NameOfTurbine, '周边机组', '周边机组温度', '当前机组温度'])
-        res[self.fieldTemperatureDiff] = res['当前机组温度'] - res['周边机组温度']
-
-        # 使用plotly进行数据可视化
-        fig1 = make_subplots(rows=1, cols=1)
-
-        # 温度差异条形图
-        fig1.add_trace(
-            go.Bar(x=res[Field_NameOfTurbine],
-                   y=res[self.fieldTemperatureDiff], marker_color='dodgerblue'),
-            row=1, col=1
-        )
-        fig1.update_layout(
-            title={'text': f'温度偏差', 'x': 0.5},
-            xaxis_title='机组名称',
-            yaxis_title='温度偏差',
-            shapes=[
-                {'type': 'line', 'x0': 0, 'x1': 1, 'xref': 'paper', 'y0': 5,
-                    'y1': 5, 'line': {'color': 'red', 'dash': 'dot'}},
-                {'type': 'line', 'x0': 0, 'x1': 1, 'xref': 'paper', 'y0': -
-                    5, 'y1': -5, 'line': {'color': 'red', 'dash': 'dot'}}
-            ],
-            xaxis=dict(tickangle=-45)  # 设置x轴刻度旋转角度为45度
-        )
-
-        # 确保从 Series 中提取的是具体的值
-        engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-        if isinstance(engineTypeCode, pd.Series):
-            engineTypeCode = engineTypeCode.iloc[0]
-
-        engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-        if isinstance(engineTypeName, pd.Series):
-            engineTypeName = engineTypeName.iloc[0]
-        # 构建最终的JSON对象
-        json_output = {
-            "analysisTypeCode": "风电机组环境温度传感器分析",
-            "engineCode": engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "xaixs": "机组名称",
-            "yaixs": "温度偏差(℃)",
-            "data": [{
-                "engineName": "",  # Field_NameOfTurbine
-                "engineCode": "",  # Field_CodeOfTurbine
-                "title": f'温度偏差',
-                "xData": res[Field_NameOfTurbine].tolist(),
-                "yData": res[self.fieldTemperatureDiff].tolist(),
-            }]
-        }
-
-        result_rows = []
-        # 保存图像
-        # pngFileName = '{}环境温差Bias.png'.format(
-        #     self.powerFarmInfo[Field_PowerFarmName].iloc[0])
-        # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-        # fig1.write_image(pngFilePath, scale=3)
-
-        # 保存HTML
-        # htmlFileName = '{}环境温差Bias.html'.format(
-        #    self.powerFarmInfo[Field_PowerFarmName].iloc[0])
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig1.write_html(htmlFilePath)
-
-        # 将JSON对象保存到文件
-        output_json_path = os.path.join(outputAnalysisDir, f"total_Bias.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-        # 如果需要返回DataFrame,可以包含文件路径
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: 'total_Bias',
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: Const_Output_Total,
-        #     Field_Return_FilePath: pngFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        # result_rows.append({
-        #    Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #    Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #    Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #    Field_CodeOfTurbine: Const_Output_Total,
-        #    Field_Return_FilePath: htmlFilePath,
-        #    Field_Return_IsSaveDatabase: True
-        # })
-
-        # 环境温度中位数条形图
-        fig2 = make_subplots(rows=1, cols=1)
-        fig2.add_trace(
-            go.Bar(x=res[Field_NameOfTurbine],
-                   y=res['当前机组温度'], marker_color='dodgerblue'),
-            row=1, col=1
-        )
-        fig2.update_layout(
-            title={'text': f'平均温度', 'x': 0.5},
-            xaxis_title='机组名称',
-            yaxis_title=' 温度',
-            xaxis=dict(tickangle=-45)  # 为x轴也设置旋转角度
-        )
-
-        # 确保从 Series 中提取的是具体的值
-        engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-        if isinstance(engineTypeCode, pd.Series):
-            engineTypeCode = engineTypeCode.iloc[0]
-
-        engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-        if isinstance(engineTypeName, pd.Series):
-            engineTypeName = engineTypeName.iloc[0]
-        # 构建最终的JSON对象
-        json_output = {
-            "analysisTypeCode": "风电机组环境温度传感器分析",
-            "engineCode": engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "xaixs": "机组名称",
-            "yaixs": "温度(℃)",
-            "data": [{
-                "engineName": "",  # Field_NameOfTurbine
-                "engineCode": "",  # Field_CodeOfTurbine
-                "title": f'平均温度',
-                "xData": res[Field_NameOfTurbine].tolist(),
-                "yData": res['当前机组温度'].tolist(),
-            }]
-        }
-
-
-        # 保存图像
-        # pngFileName = '{}环境温度中位数.png'.format(
-        #     self.powerFarmInfo[Field_PowerFarmName].iloc[0])
-        # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-        # fig2.write_image(pngFilePath, scale=3)
-
-        # 保存HTML
-        # htmlFileName = '{}环境温度中位数.html'.format(
-        #     self.powerFarmInfo[Field_PowerFarmName].iloc[0])
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig2.write_html(htmlFilePath)
-
-        # 将JSON对象保存到文件
-        output_json_path = os.path.join(outputAnalysisDir, f"total_Mid.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-        # 如果需要返回DataFrame,可以包含文件路径
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: 'total_Mid',
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-
-        # result_rows.append({
-        #     Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #     Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #     Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #     Field_CodeOfTurbine: Const_Output_Total,
-        #     Field_Return_FilePath: pngFilePath,
-        #     Field_Return_IsSaveDatabase: False
-        # })
-
-        # result_rows.append({
-        #    Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #    Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #    Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #    Field_CodeOfTurbine: Const_Output_Total,
-        #    Field_Return_FilePath: htmlFilePath,
-        #    Field_Return_IsSaveDatabase: True
-        # })
-
-        result_df = pd.DataFrame(result_rows)
-
-        return result_df
-        """
-        fig, ax = plt.subplots(figsize=(16,8),dpi=96)
-       
-        # 设置x轴刻度值旋转角度为45度  
-        plt.tick_params(axis='x', rotation=45)
-
-        sns.barplot(x=Field_NameOfTurbine,y=self.fieldTemperatureDiff,data=res,ax=ax,color='dodgerblue')
-        plt.axhline(y=5,ls=":",c="red")#添加水平直线
-        plt.axhline(y=-5,ls=":",c="red")#添加水平直线
-        ax.set_ylabel('temperature_difference')
-        ax.set_title('temperature Bias')
-        plt.savefig(outputAnalysisDir +'//'+ "{}环境温差Bias.png".format(confData.farm_name),bbox_inches='tight',dpi=120)
-
-
-        fig2, ax2 = plt.subplots(figsize=(16,8),dpi=96)
-        # 设置x轴刻度值旋转角度为45度  
-        plt.tick_params(axis='x', rotation=45)
-        
-        sns.barplot(x=Field_NameOfTurbine ,y='当前机组温度',data=res,ax=ax2,color='dodgerblue')
-        ax2.set_ylabel('temperature')
-        ax2.set_title('temperature median')
-        plt.savefig(outputAnalysisDir +'//'+ "{}环境温度均值.png".format(confData.farm_name),bbox_inches='tight',dpi=120)
-        """

+ 0 - 143
wtoaamapi/apps/business/algorithm/temperatureLargeComponentsAnalyst.py

@@ -1,143 +0,0 @@
-import os
-import pandas as pd
-import numpy as np
-import pandas as pd  
-import matplotlib.pyplot as plt  
-import seaborn as sns  
-from .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-class TemperatureLargeComponentsAnalyst(Analyst):
-    """
-    风电机组大部件温升分析
-    """
-
-    def typeAnalyst(self):
-        return "temperature_large_components"
-
-    def turbineAnalysis(self,
-                 dataFrame,
-                 outputAnalysisDir,
-                 outputFilePath,
-                 confData: ConfBusiness,
-                 turbineName):
-
-        self.temp_power(dataFrame, outputFilePath,
-                        confData.field_turbine_time,confData.field_power,confData.field_temperature_large_components)
-        
-    def getNoneEmptyFields(self,dataFrame,temperatureFields):
-        # 检查指定列中非全为空的列
-        non_empty_columns = dataFrame[temperatureFields].apply(
-            lambda x: x.notnull().any(), axis=0)
-        # 获取非全为空的列名
-        noneEmptyFields = non_empty_columns[non_empty_columns].index.tolist()
-        return noneEmptyFields
-
-    def temp_power(self, dataFrame, output_path,  field_time, field_power_active, field_temperature_large_componts):
-        # Convert the string list of temperature columns into a list
-        print("field_temperature_large_componts is {}".format(field_temperature_large_componts))
-        temperature_cols = field_temperature_large_componts.split(',')
-        
-        useCols = []
-        useCols.append(field_time)
-        useCols.append(field_power_active)
-        useCols.extend(temperature_cols)
-
-        # 获取非全为空的列名
-        non_empty_cols =self.getNoneEmptyFields(dataFrame,temperature_cols)
-        
-        # 清洗数据
-        dataFrame=dataFrame[useCols]
-        dataFrame = dataFrame.dropna(axis=1, how='all')
-        dataFrame = dataFrame.dropna(axis=0)
-        
-        # Calculate 'power_floor'
-        dataFrame['power_floor'] = (dataFrame[field_power_active] / 10).astype(int) * 10
-
-        # Initialize an empty DataFrame for aggregation
-        agg_dict = {col: 'mean' for col in non_empty_cols}
-
-        # Group by 'power_floor' and aggregate
-        grouped = dataFrame.groupby('power_floor').agg(agg_dict).reset_index()
-
-        # Sort by 'power_floor'
-        grouped.sort_values('power_floor', inplace=True)
-
-        # Write to CSV
-        grouped.to_csv(output_path, index=False)
-    
-    def turbinesAnalysis(self, dataFrameMerge,outputAnalysisDir, confData: ConfBusiness):
-        self.plot_temperature_distribution(outputAnalysisDir,confData,confData.field_temperature_large_components)
-  
-    def plot_temperature_distribution(self,csvFileDirOfCp, confData: ConfBusiness, field_temperature_large_componts,encoding='utf-8'):
-        """
-        Generates Cp distribution plots for turbines in a wind farm.
-
-        Parameters:
-        - csvFileDirOfCp: str, path to the directory containing input CSV files.
-        - farm_name: str, name of the wind farm.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """ 
-        field_Name_Turbine= "turbine_name"
-        x_name = 'power_floor'  
-        y_name = 'temperature'  
-        split_way = '_temperature_large_components.csv' 
-        
-        columns = field_temperature_large_componts.split(',')
-        # Create output directories if they don't exist  
-        for column in columns:  
-            type_name = '{}'.format(column)  
-            output_path = os.path.join(csvFileDirOfCp, type_name)  
-            os.makedirs(output_path, exist_ok=True)  
-            print("current column {}".format(column))
-
-            sns.set_palette('deep')
-            # Initialize DataFrame to store concatenated data  
-            res = pd.DataFrame()  
-    
-            # Iterate over files in the input path  
-            for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):  
-                for file_name in file_names:                
-
-                    if not file_name.endswith(".csv"):
-                        continue
-
-                    print(os.path.join(root, file_name))  
-                    frame = pd.read_csv(os.path.join(root, file_name), encoding=encoding)  
-
-                    if column not in frame.columns:
-                        continue
-                    
-                    # 获取输出文件名(不含split_way之后的部分)  
-                    turbineName = file_name.split(split_way)[0]  
-                    # 添加设备名作为新列  
-                    frame[field_Name_Turbine] = confData.add_W_if_starts_with_digit(turbineName)
-
-                    res = pd.concat([res, frame.loc[:, [field_Name_Turbine, x_name, column]]], axis=0)  
-    
-            # Reset index and plot  
-            ress = res.reset_index()  
-            fig, ax2 = plt.subplots()  
-            ax2 = sns.lineplot(x=x_name, y=column, data=ress, hue=field_Name_Turbine)  
-            # ax2.set_xlim(-150, 2100)  
-            ax2.set_xlabel(x_name)
-            ax2.set_ylabel(y_name)
-            ax2.set_title('Temperature-Distribute')  
-            plt.legend(bbox_to_anchor=(1.02, 0.5), loc='center left',ncol=2, borderaxespad=0.)  
-            plt.savefig(os.path.join(output_path, "{}.png".format(column)), bbox_inches='tight', dpi=120)  
-            plt.close()  
-    
-            # Plot individual device lines  
-            grouped = ress.groupby(field_Name_Turbine)  
-            for name, group in grouped:  
-                color = ["lightgrey"] * len(ress[field_Name_Turbine].unique())  
-                fig, ax = plt.subplots()  
-                ax = sns.lineplot(x=x_name, y=column, data=ress, hue=field_Name_Turbine, palette=sns.set_palette(color), legend=False)  
-                ax = sns.lineplot(x=x_name, y=column, data=group, color='darkblue', legend=False)   
-                ax.set_xlabel(x_name)
-                ax.set_ylabel(y_name)
-                ax.set_title('turbine_name={}'.format(name))  
-                # ax.set_xlim(-150, 2100)  
-                plt.savefig(os.path.join(output_path, "{}.png".format(name)), bbox_inches='tight', dpi=120)  
-                plt.close()  

+ 0 - 365
wtoaamapi/apps/business/algorithm/tsrAnalyst.py

@@ -1,365 +0,0 @@
-import os
-import pandas as pd
-import math
-import numpy as np
-from plotly.subplots import make_subplots
-import plotly.express as px
-import pandas as pd
-import plotly.graph_objects as go
-import seaborn as sns
-from matplotlib.ticker import MultipleLocator
-from behavior.analystWithGoodPoint import AnalystWithGoodPoint
-from utils.directoryUtil import DirectoryUtil as dir
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-
-
-class TSRAnalyst(AnalystWithGoodPoint):
-    """
-    风电机组叶尖速比分析
-    """
-
-    def typeAnalyst(self):
-        return "tsr"
-
-    def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
-        dictionary = self.processTurbineData(turbineCodes, conf, [
-                                             Field_DeviceCode, Field_Time, Field_WindSpeed, Field_ActiverPower,Field_RotorSpeed,Field_GeneratorSpeed])
-        dataFrameOfTurbines = self.userDataFrame(
-            dictionary, conf.dataContract.configAnalysis, self)
-
-        # 检查所需列是否存在
-        required_columns = {Field_WindSpeed, Field_RotorSpeed,Field_PowerFloor,Field_GeneratorSpeed}
-        if not required_columns.issubset(dataFrameOfTurbines.columns):
-            raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-
-        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)]
-
-            #创建一个与currDataFrameOfTurbines相同的dataFrameMerge
-            dataFrameMerge=currDataFrameOfTurbines.copy()
-            # return self.plot_tsr_distribution(self.tsr(dataFrameMerge), outputAnalysisDir, conf)
-            dataFrameMerge[Field_PowerFarmName] = self.currPowerFarmInfo.loc[Field_PowerFarmName]
-            # Calculate 'power_floor'
-            dataFrameMerge[Field_PowerFloor] = (
-                dataFrameMerge[Field_ActiverPower] / 10).astype(int) * 10
-
-            # Ensure the necessary columns are of float type
-            dataFrameMerge[Field_WindSpeed] = dataFrameMerge[Field_WindSpeed].astype(float)
-            dataFrameMerge[Field_RotorSpeed] = dataFrameMerge[Field_RotorSpeed].astype(float)
-            dataFrameMerge[Field_GeneratorSpeed] = dataFrameMerge[Field_GeneratorSpeed].astype(float)
-
-            # Group by 'power_floor' and calculate median, max, and min of TSR
-            grouped = dataFrameMerge.groupby([Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine]).agg({
-                Field_WindSpeed: 'mean',
-                Field_RotorSpeed: 'median',
-                Field_GeneratorSpeed: 'median',
-                Field_TSR: ['mean', 'max', 'min'],
-                Field_PowerFarmName: 'max'
-            }).reset_index()
-
-            # Rename columns for clarity post aggregation
-            grouped.columns = [Field_PowerFloor,  Field_CodeOfTurbine, Field_NameOfTurbine, Field_WindSpeed,
-                               Field_RotorSpeed, Field_GeneratorSpeed, Field_TSR, Field_TSRMax, Field_TSRMin, Field_PowerFarmName]
-
-            # Sort by 'power_floor'
-            grouped = grouped.sort_values(by=[Field_CodeOfTurbine, Field_PowerFloor])
-
-            returnData = self.plot_tsr_distribution(
-                grouped, outputAnalysisDir, conf, currTurbineModeInfo)
-            returnDatas.append(returnData)
-
-        returnResult = pd.concat(returnDatas, ignore_index=True)
-
-        return returnResult
-
-      #------------------------------------------
-
-        # dictionary = self.processTurbineData(turbineCodes,conf,[Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_RotorSpeed,Field_GeneratorSpeed])
-        # dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
-        # # return self.plot_tsr_distribution(self.tsr(dataFrameMerge), outputAnalysisDir, conf)
-        # dataFrameMerge[Field_PowerFarmName] = self.currPowerFarmInfo.loc[Field_PowerFarmName]
-        # # Calculate 'power_floor'
-        # dataFrameMerge[Field_PowerFloor] = (
-        #     dataFrameMerge[Field_ActiverPower] / 10).astype(int) * 10
-
-        # # Ensure the necessary columns are of float type
-        # dataFrameMerge[Field_WindSpeed] = dataFrameMerge[Field_WindSpeed].astype(float)
-        # dataFrameMerge[Field_RotorSpeed] = dataFrameMerge[Field_RotorSpeed].astype(float)
-        # dataFrameMerge[Field_GeneratorSpeed] = dataFrameMerge[Field_GeneratorSpeed].astype(float)
-
-        # # Group by 'power_floor' and calculate median, max, and min of TSR
-        # grouped = dataFrameMerge.groupby([Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine]).agg({
-        #     Field_WindSpeed: 'median',
-        #     Field_RotorSpeed: 'median',
-        #     Field_GeneratorSpeed: 'median',
-        #     Field_TSR: ['median', 'max', 'min'],
-        #     Field_PowerFarmName: 'max'
-        # }).reset_index()
-
-        # # Rename columns for clarity post aggregation
-        # grouped.columns = [Field_PowerFloor,  Field_CodeOfTurbine, Field_NameOfTurbine, Field_WindSpeed,
-        #                    Field_RotorSpeed, Field_GeneratorSpeed, Field_TSR, Field_TSRMax, Field_TSRMin, Field_PowerFarmName]
-
-        # # Sort by 'power_floor'
-        # grouped = grouped.sort_values(by=[Field_CodeOfTurbine, Field_PowerFloor])
-
-        # return self.plot_tsr_distribution(grouped, outputAnalysisDir, conf)
-
-    def plot_tsr_distribution(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract, turbineModelInfo: pd.Series):
-        """
-        Generates tsr distribution plots for turbines in a wind farm.
-
-        Parameters:
-        - csvFileDirOfCp: str, path to the directory containing input CSV files.
-        - farm_name: str, name of the wind farm.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """
-        x_name = Field_PowerFloor
-        y_name = Field_TSR
-
-        upLimitOfTSR = 20
-
-        # 创建一个列表来存储各个风电机组的数据
-        turbine_data_list = []
-
-        # 绘制全场TSR分布图
-        fig = go.Figure()
-        # colors = px.colors.sequential.Turbo
-        # 遍历不同的turbine来添加线条
-        for turbine in dataFrameMerge[Field_NameOfTurbine].unique():
-            turbine_data = dataFrameMerge[dataFrameMerge[Field_NameOfTurbine] == turbine]
-            fig.add_trace(go.Scatter(x=turbine_data[x_name], y=turbine_data[y_name],
-                                     mode='lines',
-                                     # line=dict(color=colors[idx % len(colors)]),
-                                     name=turbine))
-            # 提取数据
-            turbine_data_total = {
-                "engineName": turbine,
-                "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
-                "xData": turbine_data[x_name].tolist(),
-                "yData": turbine_data[y_name].tolist(),
-                }
-            turbine_data_list.append(turbine_data_total)
-
-
-        fig.update_layout(
-            title={
-                "text": f'叶尖速比分布-{turbineModelInfo[Field_MachineTypeCode]}',
-                'x': 0.5
-            },
-
-            xaxis=dict(
-                title='最小功率',
-                dtick=200,
-                tickangle=-45,
-                range=[0, 1800]),
-            yaxis=dict(
-                title='叶尖速比',
-                dtick=self.axisStepTSR,
-                range=[self.axisLowerLimitTSR,
-                       self.axisUpperLimitTSR]
-            ),
-            legend=dict(
-                orientation="h",  # Horizontal orientation
-                xanchor="center",  # Anchor the legend to the center
-                x=0.5,  # Position legend at the center of the x-axis
-                y=-0.2,  # Position legend below the x-axis
-                # itemsizing='constant',  # Keep the size of the legend entries constant
-                # itemwidth=50
-            )
-        )
-
-        # 设置x轴标签旋转
-        fig.update_xaxes(tickangle=-45)
-
-        engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-        if isinstance(engineTypeCode, pd.Series):
-            engineTypeCode = engineTypeCode.iloc[0]
-
-        engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-        if isinstance(engineTypeName, pd.Series):
-            engineTypeName = engineTypeName.iloc[0]
-        # 构建最终的JSON对象
-        json_output = {
-            "analysisTypeCode": "风电机组叶尖速比分析",
-            "typecode": turbineModelInfo[Field_MillTypeCode],
-            "engineCode": engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "title": f'叶尖速比分布-{turbineModelInfo[Field_MachineTypeCode]}',
-            "xaixs": "最小功率(kW)",
-            "yaixs": "叶尖速比",
-            "data": turbine_data_list
-
-        }
-
-        # 保存图形
-        # fig.write_image(csvFileDirOfCp + r"/{}-TSR-Distibute.png".format(confData.farm_name),format='png',width=800, height=500,scale=3)
-        # fig.show()
-
-        # 保存HTML
-        # htmlFileName = f"{dataFrameMerge[Field_PowerFarmName].iloc[0]}-TSR-Distribution-{turbineModelInfo[Field_MillTypeCode]}.html"
-        #htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        #fig.write_html(htmlFilePath)
-
-        result_rows = []
-        # result_rows.append({
-        #    Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #    Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #    Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #    Field_CodeOfTurbine: 'total',
-        #    Field_Return_FilePath: htmlFilePath,
-        #        Field_Return_IsSaveDatabase: True
-        # })
-
-        # 将JSON对象保存到文件
-        output_json_path = os.path.join(outputAnalysisDir, f"{turbineModelInfo[Field_MillTypeCode]}.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-        # 如果需要返回DataFrame,可以包含文件路径
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: turbineModelInfo[Field_MillTypeCode],
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-
-
-        # 绘制每个设备的TSR分布图
-        for name, group in dataFrameMerge.groupby([Field_NameOfTurbine, Field_CodeOfTurbine]):
-            fig = go.Figure()
-
-            # 创建一个列表来存储各个风电机组的数据
-            turbine_data_list_each = []
-
-            # 循环绘制turbine的线条
-            for turbine in dataFrameMerge[Field_NameOfTurbine].unique():
-                turbine_data = dataFrameMerge[dataFrameMerge[Field_NameOfTurbine] == turbine]
-                fig.add_trace(go.Scatter(x=turbine_data[x_name],
-                                         y=turbine_data[y_name],
-                                         mode='lines',
-                                         line=dict(color='lightgrey'),
-                                         showlegend=False))
-                # 提取数据
-                turbine_data_each = {
-                    "engineName": turbine,
-                    "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
-                    "xData": turbine_data[x_name].tolist(),
-                    "yData": turbine_data[y_name].tolist(),
-                }
-                turbine_data_list_each.append(turbine_data_each)
-
-            fig.add_trace(go.Scatter(x=group[x_name],
-                                     y=group[y_name],
-                                     mode='lines',
-                                     line=dict(color='darkblue'),
-                                     showlegend=False))
-
-            fig.update_layout(
-                title={"text": '机组: {}'.format(name[0])},
-                # margin=dict(
-                # t=35,  # 顶部 margin,减小这个值可以使标题更靠近图形
-                # l=60,  # 左侧 margin
-                # r=60,  # 右侧 margin
-                # b=40,  # 底部 margin
-                # ),
-                xaxis=dict(
-                    title='功率',
-                    dtick=200,
-                    tickangle=-45,
-                    range=[0, 1800]),
-                yaxis=dict(
-                    title='叶尖速比',
-                    dtick=self.axisStepTSR,
-                    range=[self.axisLowerLimitTSR,
-                           self.axisUpperLimitTSR]
-                )
-            )
-            fig.update_xaxes(tickangle=-45)
-
-
-            engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-            if isinstance(engineTypeCode, pd.Series):
-                engineTypeCode = engineTypeCode.iloc[0]
-
-            engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-            if isinstance(engineTypeName, pd.Series):
-                engineTypeName = engineTypeName.iloc[0]
-            # 构建最终的JSON对象
-            json_output = {
-                "analysisTypeCode": "风电机组叶尖速比分析",
-                "typecode": turbineModelInfo[Field_MillTypeCode],
-                "engineCode": engineTypeCode,
-                "engineTypeName": engineTypeName,
-                "title": f'机组:{format(name[0])}',
-                "xaixs": "功率(kW)",
-                "yaixs": "叶尖速比",
-                "data": turbine_data_list_each
-
-            }
-
-
-            # 保存图像
-            # pngFileName = f"{name[0]}.png"
-            # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-            # fig.write_image(pngFilePath, scale=3)
-
-            # 保存HTML
-            # htmlFileName = f"{name[0]}.html"
-            # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-            # fig.write_html(htmlFilePath)
-
-            # 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: pngFilePath,
-            #     Field_Return_IsSaveDatabase: False
-            # })
-
-            # 将JSON对象保存到文件
-            output_json_path_each = os.path.join(outputAnalysisDir, f"{name[0]}.json")
-            with open(output_json_path_each, 'w', encoding='utf-8') as f:
-                import json
-                json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-            # 如果需要返回DataFrame,可以包含文件路径
-            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: output_json_path_each,
-                Field_Return_IsSaveDatabase: True
-            })
-
-
-            # 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: htmlFilePath,
-            #    Field_Return_IsSaveDatabase: True
-            # })
-
-        result_df = pd.DataFrame(result_rows)
-        return result_df
-

+ 0 - 125
wtoaamapi/apps/business/algorithm/tsrTrendAnalyst.py

@@ -1,125 +0,0 @@
-import os
-import pandas as pd
-import numpy as np
-import matplotlib.pyplot as plt
-import seaborn as sns
-from .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-
-class TSRTrendAnalyst(Analyst):
-    """
-    风电机组叶尖速比时序分析
-    """
-
-    def typeAnalyst(self):
-        return "tsr_trend"
-
-    def turbineAnalysis(self,
-                        dataFrame,
-                        outputAnalysisDir,
-                        outputFilePath,
-                        confData: ConfBusiness,
-                        turbineName):
-
-        self.tsr_trend(dataFrame, outputFilePath,
-                       confData.field_turbine_time, confData.field_wind_speed, confData.field_rotor_speed, confData.field_power, confData.field_pitch_angle1,
-                       confData.rotor_diameter)
-
-    def tsr_trend(self, dataFrame, output_path, field_time, field_wind_speed, field_rotor_speed, field_power_active, field_angle_pitch, rotor_diameter):
-        # Convert time column to datetime and extract date
-        dataFrame[field_time] = pd.to_datetime(
-            dataFrame[field_time], format='%Y-%m-%d %H:%M:%S')
-        dataFrame['time_day'] = dataFrame[field_time].dt.date
-
-        # Calculate 'tsr'
-        dataFrame['wind_speed'] = dataFrame[field_wind_speed].astype(float)
-        dataFrame['rotor_speed'] = dataFrame[field_rotor_speed].astype(float)
-        rotor_diameter = pd.to_numeric(rotor_diameter, errors='coerce')
-        dataFrame['tsr'] = (dataFrame['rotor_speed'] * 0.104667 *
-                            (rotor_diameter / 2)) / dataFrame['wind_speed']
-
-        # Group by day and aggregate
-        grouped = dataFrame.groupby('time_day').agg({
-            field_time: 'min',
-            'wind_speed': 'mean',
-            'rotor_speed': 'mean',
-            'tsr': ['mean', 'max', 'min']
-        }).reset_index()
-
-        # Rename columns post-aggregation
-        grouped.columns = ['time_day', 'time_', 'wind_speed',
-                           'rotor_speed', 'tsr', 'tsr_max', 'tsr_min']
-
-        # Sort by day
-        grouped.sort_values('time_day', inplace=True)
-
-        # Write to CSV
-        grouped.to_csv(output_path, index=False)
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.plot_tsr_trend(outputAnalysisDir, confData.farm_name)
-
-    def plot_tsr_trend(self, csvFileDirOfCp, farm_name, encoding='utf-8'):
-        """  
-        Plot TSR trend from CSV files in a given input path and save the plots to an output path.  
-
-        Parameters:  
-        - csvFileDirOfCp: str, path to the directory containing input CSV files.
-        - farm_name: str, name of the wind farm.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """
-        field_Name_Turbine = "turbine_name"
-        file_time = 'time_day'
-        y_name = 'tsr'
-        y_min = 'tsr_min'
-        y_max = 'tsr_max'
-        split_way = '_tsr_trend.csv'
-
-        # Set seaborn palette
-        sns.set_palette('deep')
-
-        # Iterate over the files in the input directory
-        for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):
-            for file_name in file_names:
-
-                if not file_name.endswith(".csv"):
-                    continue
-
-                try:
-                    print(os.path.join(root, file_name))
-                    data = pd.read_csv(os.path.join(
-                        root, file_name), encoding=encoding)
-
-                    # Convert the time column to datetime
-                    data.loc[:, file_time] = pd.to_datetime(
-                        data.loc[:, file_time])
-
-                    # Calculate min and max TSR values
-                    data[y_min] = data[y_name] - data[y_min]
-                    data[y_max] = data[y_max] - data[y_name]
-
-                    # Split the file name to get the output name
-                    turbine_name = file_name.split(split_way)[0]
-                    # 添加设备名作为新列
-                    data[field_Name_Turbine] = turbine_name
-
-                    # Plot the TSR trend with error bars
-                    fig, ax = plt.subplots()
-                    ax.errorbar(x=data[file_time], y=data[y_name], yerr=[data[y_min], data[y_max]],
-                                fmt='o', capsize=4, elinewidth=2, ecolor='lightgrey', mfc='dodgerblue')
-
-                    # Set axis labels, limits, and title
-                    ax.set_xlabel('time')
-                    ax.set_ylabel('TSR')
-                    # ax.set_ylim(0, 16)
-                    ax.set_title('turbine_name={}'.format(turbine_name))
-
-                    # Save the plot to the output path
-                    plt.savefig(os.path.join(csvFileDirOfCp, "{}.png".format(
-                        turbine_name)), bbox_inches='tight', dpi=120)
-                    plt.close(fig)
-
-                except Exception as e:
-                    print(f"An error occurred while processing file {file_name}: {e}")

+ 0 - 379
wtoaamapi/apps/business/algorithm/tsrWindSpeedAnalyst.py

@@ -1,379 +0,0 @@
-import os
-import pandas as pd
-import plotly.graph_objects as go
-import seaborn as sns
-from algorithmContract.confBusiness import *
-from algorithmContract.contract import Contract
-from behavior.analystWithGoodPoint import AnalystWithGoodPoint
-
-
-class TSRWindSpeedAnalyst(AnalystWithGoodPoint):
-    """
-    风电机组叶尖速比分析
-    """
-
-    def typeAnalyst(self):
-        return "tsr_windspeed"
-
-    def selectColumns(self):
-        return [Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_RotorSpeed,Field_GeneratorSpeed]
-
-    # def turbineAnalysis(self, dataFrame, outputAnalysisDir, outputFilePath, conf: Contract, Field_NameOfTurbine):
-    #
-    #     self.tsr(dataFrame, outputFilePath, Field_WindSpeed, Field_RotorSpeed,
-    #              Field_ActiverPower)
-
-    def tsr(self, dataFrame : pd.DataFrame):
-
-        #Add column field_name
-        dataFrame[Field_PowerFarmName] = self.currPowerFarmInfo.loc[Field_PowerFarmName]
-        # Alias the power column
-        # dataFrame[Field_Power] = dataFrame[Field_ActiverPower]
-        # Calculate 'wind_speed_floor'
-        # dataFrame[Field_WindSpeedFloor] = (dataFrame[Field_WindSpeed] / 1).astype(int) + 0.5
-
-        # Ensure the necessary columns are of float type
-        # dataFrame['wind_speed'] = dataFrame[Field_WindSpeed].astype(float)
-        dataFrame[Field_RotorSpeed] = dataFrame[Field_RotorSpeed].astype(float)
-        dataFrame[Field_GeneratorSpeed] = dataFrame[Field_GeneratorSpeed].astype(float)
-
-        # rotor_diameter = pd.to_numeric(rotor_diameter, errors='coerce')
-        # # Calculate TSR
-        # dataFrame['tsr'] = (dataFrame['rotor_speed'] * 0.104667 *
-        #                     (rotor_diameter / 2)) / dataFrame['wind_speed']
-
-        # Group by 'wind_speed_floor' and calculate median, max, and min of TSR
-        grouped = dataFrame.groupby([Field_WindSpeedFloor,Field_CodeOfTurbine,Field_NameOfTurbine]).agg({
-            Field_ActiverPower: 'median',
-            Field_PowerFloor: 'median',
-            Field_RotorSpeed : 'median',
-            Field_GeneratorSpeed: 'median',
-            Field_TSR : ['median', 'max', 'min'],
-            Field_PowerFarmName: 'max'
-        }).reset_index()
-
-        # Rename columns for clarity post aggregation
-        grouped.columns = [Field_WindSpeedFloor,  Field_CodeOfTurbine, Field_NameOfTurbine,Field_Power,Field_PowerFloor,
-                           Field_RotorSpeed, Field_GeneratorSpeed, Field_TSR, Field_TSRMax, Field_TSRMin, Field_PowerFarmName]
-
-        # Sort by 'wind_speed_floor'
-        grouped.sort_values(by=[Field_NameOfTurbine, Field_PowerFloor])
-
-        return grouped
-
-    def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
-        dictionary = self.processTurbineData(turbineCodes,conf,self.selectColumns())
-        dataFrameOfTurbines = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
-        # 检查所需列是否存在
-        required_columns = {Field_WindSpeed, Field_RotorSpeed,Field_PowerFloor,Field_GeneratorSpeed,Field_TSR}
-        if not required_columns.issubset(dataFrameOfTurbines.columns):
-            raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
-
-        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)]
-            #创建一个与dataFrameOfTurbines相同的dataFrame
-            dataFrame=currDataFrameOfTurbines.copy()
-            returnData = self.plot_tsr_distribution(
-                    self.tsr(dataFrame), outputAnalysisDir, conf, currTurbineModeInfo)
-            returnDatas.append(returnData)
-
-        returnResult = pd.concat(returnDatas, ignore_index=True)
-
-
-        return returnResult
-
-    def plot_tsr_distribution(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract,turbineModelInfo: pd.Series):
-        """
-        Generates tsr distribution plots for turbines in a wind farm.
-
-        Parameters:
-        - csvFileDirOfCp: str, path to the directory containing input CSV files.
-        - farm_name: str, name of the wind farm.
-        - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
-        """
-        x_name = 'wind_speed_floor'
-        y_name = 'tsr'
-
-        # upLimitOfTSR = 20
-
-        # 设置绘图样式
-        sns.set_palette('deep')
-
-        # 初始化结果DataFrame
-        # res = pd.DataFrame()
-        #
-        # # 遍历输入目录中的所有文件
-        # for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):
-        #     for file_name in file_names:
-        #
-        #         if not file_name.endswith(CSVSuffix):
-        #             continue
-        #
-        #         file_path = os.path.join(root, file_name)
-        #
-        #         # 读取CSV文件
-        #         frame = pd.read_csv(file_path, encoding=encoding)
-        #         frame = frame[(frame[x_name] > 0)]
-        #
-        #         # 选择需要的列并合并到结果DataFrame中
-        #         res = pd.concat([res, frame], axis=0)
-        #
-        # # 重置索引
-        # ress = res.reset_index()
-        # dataFrame[Field_NameOfTurbine] = dataFrame[Field_NameOfTurbine].astype(str)
-        # dataFrame = dataFrame.sort_values(by=[Field_NameOfTurbine, x_name])
-        # 绘制全场TSR分布图
-        fig = go.Figure()
-        # colors = px.colors.sequential.Turbo
-
-        # 创建一个列表来存储各个风电机组的数据
-        turbine_data_list = []
-
-        # 遍历不同的turbine来添加线条
-        for turbine in dataFrame[Field_NameOfTurbine].unique():
-            turbine_data = dataFrame[dataFrame[Field_NameOfTurbine] == turbine]
-            fig.add_trace(go.Scatter(x=turbine_data[x_name], y=turbine_data[y_name],
-                                     mode='lines',
-                                     # line=dict(color=colors[idx % len(colors)]),
-                                     name=turbine))
-            # 提取数据
-            turbine_data_total = {
-                "engineName": turbine,
-                "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
-                "xData": turbine_data[x_name].tolist(),
-                "yData": turbine_data[y_name].tolist(),
-                }
-            turbine_data_list.append(turbine_data_total)
-
-
-        fig.update_layout(
-            title={
-                "text": f'叶尖速比分布图-{turbineModelInfo[Field_MachineTypeCode]}',
-                'x': 0.5
-            },
-            # margin=dict(
-            # t=35,  # 顶部 margin,减小这个值可以使标题更靠近图形
-            # l=60,  # 左侧 margin
-            # r=60,  # 右侧 margin
-            # b=40,  # 底部 margin
-            # ),
-
-            # legend=dict(title='Turbine',
-            #             x=1.02,
-            #             y=0.5,
-            #             orientation='v',
-            #             traceorder='normal',
-            #             font=dict(size=12),
-            #             bgcolor='rgba(255, 255, 255, 0)',
-            #             bordercolor='rgba(255, 255, 255, 0)'),
-            legend=dict(
-                orientation="h",  # Horizontal orientation
-                xanchor="center",  # Anchor the legend to the center
-                x=0.5,  # Position legend at the center of the x-axis
-                y=-0.2,  # Position legend below the x-axis
-                # itemsizing='constant',  # Keep the size of the legend entries constant
-                # itemwidth=50
-            ),
-            xaxis=dict(
-                title='风速',
-                dtick=1,
-                tickangle=-45,
-                range=[0, 26]),
-            yaxis=dict(
-                title='叶尖风速比',
-                dtick=self.axisStepTSR,
-                range=[self.axisLowerLimitTSR,
-                       self.axisUpperLimitTSR]
-            )
-        )
-        # 设置x轴标签旋转
-        fig.update_xaxes(tickangle=-45)
-        # 保存图形
-        # fig.write_image(csvFileDirOfCp + r"/{}-TSR-Distibute.png".format(confData.farm_name),format='png',width=800, height=500,scale=3)
-        # fig.show()
-
-
-        engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-        if isinstance(engineTypeCode, pd.Series):
-            engineTypeCode = engineTypeCode.iloc[0]
-
-        engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-        if isinstance(engineTypeName, pd.Series):
-            engineTypeName = engineTypeName.iloc[0]
-        # 构建最终的JSON对象
-        json_output = {
-            "analysisTypeCode": "风电机组叶尖速比和风速分析",
-            "typecode": turbineModelInfo[Field_MillTypeCode],
-            "engineCode": engineTypeCode,
-            "engineTypeName": engineTypeName,
-            "title": f'叶尖速比分布图-{turbineModelInfo[Field_MachineTypeCode]}',
-            "xaixs": "风速",
-            "yaixs": "叶尖风速比",
-            "data": turbine_data_list
-
-        }
-
-
-        # 保存HTML
-        # htmlFileName = f"{dataFrame[Field_PowerFarmName].iloc[0]}-TSR-Distribution-{turbineModelInfo[Field_MillTypeCode]}.html"
-        # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-        # fig.write_html(htmlFilePath)
-
-        result_rows = []
-        # result_rows.append({
-        #    Field_Return_TypeAnalyst: self.typeAnalyst(),
-        #    Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-        #    Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-        #    Field_CodeOfTurbine: 'total',
-        #    Field_Return_FilePath: htmlFilePath,
-        #    Field_Return_IsSaveDatabase: True
-        # })
-
-        # 将JSON对象保存到文件
-        output_json_path = os.path.join(outputAnalysisDir, f"{turbineModelInfo[Field_MillTypeCode]}.json")
-        with open(output_json_path, 'w', encoding='utf-8') as f:
-            import json
-            json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-        # 如果需要返回DataFrame,可以包含文件路径
-        result_rows.append({
-            Field_Return_TypeAnalyst: self.typeAnalyst(),
-            Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
-            Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
-            Field_CodeOfTurbine: 'total',
-            Field_MillTypeCode: turbineModelInfo[Field_MillTypeCode],
-            Field_Return_FilePath: output_json_path,
-            Field_Return_IsSaveDatabase: True
-        })
-
-
-
-        # 绘制每个设备的TSR分布图
-        for name, group in dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine]):
-            fig = go.Figure()
-
-            # 创建一个列表来存储各个风电机组的数据
-            turbine_data_list_each = []
-
-            # 循环绘制turbine的线条
-            for turbine in dataFrame[Field_NameOfTurbine].unique():
-                turbine_data = dataFrame[dataFrame[Field_NameOfTurbine] == turbine]
-                fig.add_trace(go.Scatter(x=turbine_data[x_name],
-                                         y=turbine_data[y_name],
-                                         mode='lines',
-                                         line=dict(color='lightgrey'),
-                                         showlegend=False))
-                # 提取数据
-                turbine_data_each = {
-                    "engineName": turbine,
-                    "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
-                    "xData": turbine_data[x_name].tolist(),
-                    "yData": turbine_data[y_name].tolist(),
-                }
-                turbine_data_list_each.append(turbine_data_each)
-
-            fig.add_trace(go.Scatter(x=group[x_name],
-                                     y=group[y_name],
-                                     mode='lines',
-                                     line=dict(color='darkblue'),
-                                     showlegend=False))
-
-            fig.update_layout(
-                title={"text": '机组: {}'.format(name[0])},
-                # margin=dict(
-                #         t=35,  # 顶部 margin,减小这个值可以使标题更靠近图形
-                #         l=60,  # 左侧 margin
-                #         r=60,  # 右侧 margin
-                #         b=40,  # 底部 margin
-                # ),
-                xaxis=dict(
-                    title='风速最低阈值 ',
-                    dtick=1,
-                    tickangle=-45,
-                    range=[0, 26]),
-                yaxis=dict(
-                    title='叶尖速比',
-                    dtick=self.axisStepTSR,
-                    range=[self.axisLowerLimitTSR,
-                           self.axisUpperLimitTSR]
-                )
-            )
-            fig.update_xaxes(tickangle=-45)
-
-
-            engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
-            if isinstance(engineTypeCode, pd.Series):
-                engineTypeCode = engineTypeCode.iloc[0]
-
-            engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
-            if isinstance(engineTypeName, pd.Series):
-                engineTypeName = engineTypeName.iloc[0]
-            # 构建最终的JSON对象
-            json_output = {
-                "analysisTypeCode": "风电机组叶尖速比和风速分析",
-                "typecode": turbineModelInfo[Field_MillTypeCode],
-                "engineCode": engineTypeCode,
-                "engineTypeName": engineTypeName,
-                "title": f'机组:{format(name[0])}',
-                "xaixs": "功率(kW)",
-                "yaixs": "叶尖速比",
-                "data": turbine_data_list_each
-
-            }
-            # 将JSON对象保存到文件
-            output_json_path_each = os.path.join(outputAnalysisDir, f"{name[0]}.json")
-            with open(output_json_path_each, 'w', encoding='utf-8') as f:
-                import json
-                json.dump(json_output, f, ensure_ascii=False, indent=4)
-
-            # 如果需要返回DataFrame,可以包含文件路径
-            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: output_json_path_each,
-                Field_Return_IsSaveDatabase: True
-            })
-
-            # 保存图像
-            # pngFileName = f"{name[0]}.png"
-            # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
-            # fig.write_image(pngFilePath, scale=3)
-
-            # 保存HTML
-            # htmlFileName = f"{name[0]}.html"
-            # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
-            # fig.write_html(htmlFilePath)
-
-            # 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: pngFilePath,
-            #     Field_Return_IsSaveDatabase: 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: htmlFilePath,
-            #    Field_Return_IsSaveDatabase: True
-            # })
-
-        result_df = pd.DataFrame(result_rows)
-        return result_df

+ 0 - 0
wtoaamapi/apps/business/algorithm/utils/__init__.py


+ 0 - 57
wtoaamapi/apps/business/algorithm/utils/csvFileUtil.py

@@ -1,57 +0,0 @@
-import pandas as pd
-
-class CSVFileUtil:
-    def __init__(self, filepath):
-        """
-        初始化CSV工具类
-        :param filepath: CSV文件的路径
-        """
-        self.filepath = filepath
-
-    def read_csv(self):
-        """
-        读取CSV文件
-        :return: 返回DataFrame对象
-        """
-        return pd.read_csv(self.filepath)
-    
-    def read_csv_columns(self,useColumns):
-        """
-        读取CSV文件
-        :return: 返回DataFrame对象
-        """
-        return pd.read_csv(self.filepath,usecols=useColumns)
-
-    def write_csv(self, data, dest_path):
-        """
-        将数据写入CSV文件
-        :param data: DataFrame对象或可转换为DataFrame的数据
-        :param dest_path: 保存CSV文件的路径
-        """
-        if not isinstance(data, pd.DataFrame):
-            data = pd.DataFrame(data)
-        data.to_csv(dest_path, index=False)
-
-    def select_and_save_columns(self, columns, dest_path):
-        """
-        选取指定的列并保存为新的CSV文件
-        :param columns: 要选取的列名列表
-        :param dest_path: 保存新CSV文件的路径
-        """
-        df = self.read_csv_columns(columns)
-        self.write_csv(df, dest_path)
-
-# 使用示例
-if __name__ == "__main__":
-    filepath = '/path/to/your/original.csv'  # 替换为你的CSV文件路径
-    dest_path = '/path/to/your/selected_columns.csv'  # 替换为你想保存选取列后的CSV文件路径
-    columns = ['时间','iTempOutdoor_1sec','iTempNacelle_1sec','iGenPower','iRotorSpeedPDM','iGenSpeed','iWindSpeed_real',
-               'iPitchAngle1','iPitchAngle2','iPitchAngle3','iNacellePositionTotal','iwindDirection','iVaneDiiection',
-               'iActivePoweiSetPointValue','iReactivePower','iKWhThisDay_h','iVibrationY','iVibrationZ','iTemp1GearOil_1sec',
-               'iTempRotorBearA_1sec','iTempGearBearNDE_1sec','iTempGearBearDE_1sec','iTempGenBearDE_1sec','iTempGenBearNDE_1sec',
-               'iTempGenStatorU_1sec','iTempHub_1sec','iTempCntr_1sec','WT_Runcode','WT_Faultcode','iTempRotorBearA_1sec']  # 替换为你想选取的列名
-
-    tool = CSVFileUtil(filepath)
-    tool.select_and_save_columns(columns, dest_path)
-
-

+ 0 - 89
wtoaamapi/apps/business/algorithm/utils/directoryUtil.py

@@ -1,89 +0,0 @@
-import os
-import shutil
-
-class DirectoryUtil:
-    @staticmethod
-    def create_directory(path):
-        """
-        创建一个新目录。如果目录已存在,则不执行任何操作。
-        :param path: 要创建的目录路径
-        """
-        try:
-            os.makedirs(path, exist_ok=True)
-            print(f"Directory '{path}' created successfully.")
-        except OSError as error:
-            print(f"Creating directory '{path}' failed. Error: {error}")
-
-    @staticmethod
-    def delete_directory(path):
-        """
-        删除一个目录及其所有内容。
-        :param path: 要删除的目录路径
-        """
-        try:
-            shutil.rmtree(path)
-            print(f"Directory '{path}' deleted successfully.")
-        except OSError as error:
-            print(f"Deleting directory '{path}' failed. Error: {error}")
-
-    @staticmethod
-    def list_directory_contents(path):
-        """
-        列出目录中的所有文件和子目录。
-        :param path: 目录路径
-        :return: 目录内容的列表
-        """
-        try:
-            contents = os.listdir(path)
-            print(f"Contents of directory '{path}': {contents}")
-            return contents
-        except OSError as error:
-            print(f"Listing contents of directory '{path}' failed. Error: {error}")
-            return []
-        
-    @staticmethod
-    def list_directory(path):
-        """
-        列出目录中的所有文件和子目录。
-        :param path: 目录路径
-        :return: 指定目录、子目录列表、文件列表
-        """
-        try:
-            return os.walk(path)
-        except OSError as error:
-            print(f"Listing contents of directory '{path}' failed. Error: {error}")
-            return path,None,None
-
-    @staticmethod
-    def check_directory_exists(path):
-        """
-        检查一个目录是否存在。
-        :param path: 目录路径
-        :return: 如果目录存在则返回True,否则返回False
-        """
-        return os.path.exists(path) and os.path.isdir(path)
-
-# 使用示例
-if __name__ == "__main__":
-    path = 'E:\\BaiduNetdiskDownload\\merge1'  # 替换为你的目录路径
-
-    # 创建目录
-    DirectoryUtil.create_directory(path)
-
-    # 检查目录是否存在
-    if DirectoryUtil.check_directory_exists(path):
-        print(f"Directory '{path}' exists.")
-
-    # 列出目录内容
-    DirectoryUtil.list_directory_contents(path)
-
-    results=DirectoryUtil.list_directory(path)
-
-    for root,dirs,files in results:
-        print(root)
-        print(dirs)
-        print(files)
-
-    # 删除目录
-    # 注意:这将删除目录及其所有内容,请谨慎操作!
-    # DirectoryTool.delete_directory(path)

+ 0 - 0
wtoaamapi/apps/business/algorithm/utils/jsonUtil/__init__.py


+ 0 - 37
wtoaamapi/apps/business/algorithm/utils/jsonUtil/jsonUtil.py

@@ -1,37 +0,0 @@
-import json
-
-class JsonUtil:
-    @staticmethod
-    def read_json(file_path, encoding='utf-8'):
-        """读取JSON文件"""
-        try:
-            with open(file_path, 'r', encoding=encoding) as f:
-                return json.load(f)
-        except FileNotFoundError:
-            print("文件不存在")
-            return None
-
-    @staticmethod
-    def write_json(data, file_path, encoding='utf-8'):
-        """写入JSON到文件"""
-        with open(file_path, 'w', encoding=encoding) as f:
-            json.dump(data, f, ensure_ascii=False, indent=4)
-
-    @staticmethod
-    def update_json(file_path, updates, encoding='utf-8'):
-        """更新JSON文件"""
-        data = JsonUtil.read_json(file_path, encoding)
-        if data is not None:
-            data.update(updates)
-            JsonUtil.write_json(data, file_path, encoding)
-
-    @staticmethod
-    def delete_key(file_path, key, encoding='utf-8'):
-        """从JSON文件删除特定键"""
-        data = JsonUtil.read_json(file_path, encoding)
-        if data is not None:
-            if key in data:
-                del data[key]
-                JsonUtil.write_json(data, file_path, encoding)
-            else:
-                print(f"键 '{key}' 不存在。")

+ 0 - 21
wtoaamapi/apps/business/algorithm/utils/test.py

@@ -1,21 +0,0 @@
-import pandas as pd
-import numpy as np
-
-# 创建一个示例DataFrame
-df = pd.DataFrame({
-    'A': [1, np.nan, 3],
-    'B': [np.nan, np.nan, np.nan],
-    'C': [2, 2, np.nan],
-    'D': [np.nan, 3, 3]
-})
-
-# 指定你想要检查的列
-columns_to_check = ['A', 'B', 'C']
-
-# 检查指定列中非全为空的列
-non_empty_columns = df[columns_to_check].apply(lambda x: x.notnull().any(), axis=0)
-
-# 获取非全为空的列名
-non_empty_column_names = non_empty_columns[non_empty_columns].index.tolist()
-
-print("非全为空的列名:", non_empty_column_names)

+ 0 - 176
wtoaamapi/apps/business/algorithm/yawErrorAnalyst.py

@@ -1,176 +0,0 @@
-import os
-import numpy as np
-from plotly.subplots import make_subplots
-import plotly.graph_objects as go
-from scipy.optimize import curve_fit
-import matplotlib.pyplot as plt
-import pandas as pd
-from .analyst import Analyst
-from .utils.directoryUtil import DirectoryUtil as dir
-from confBusiness import ConfBusiness
-
-
-class YawErrorAnalyst(Analyst):
-    """
-    风电机组静态偏航误差分析
-    """
-
-    def typeAnalyst(self):
-        return "yaw_error"
-
-    def turbineAnalysis(self,
-                        dataFrame,
-                        outputAnalysisDir,
-                        outputFilePath,
-                        confData: ConfBusiness,
-                        turbineName):
-
-        self.yaw_error(dataFrame, outputFilePath,
-                       confData.field_turbine_time, confData.field_angle_included, confData.field_power, confData.field_pitch_angle1)
-
-    def yaw_error(self, dataFrame, output_path,  field_time, field_angle_included_wind_dir, field_power_active, pitch_col):
-        # Calculate floor values and other transformations
-        dataFrame['wind_dir_floor'] = np.floor(dataFrame[field_angle_included_wind_dir]).astype(int)
-        # dataFrame['wind_dir_floor'] = dataFrame[field_angle_included_wind_dir].round().astype(int)
-
-        dataFrame['power'] = dataFrame[field_power_active].astype(float)
-        dataFrame['time'] = pd.to_datetime(dataFrame[field_time])
-
-        # Calculate aggregated metrics for power
-        grouped = dataFrame.groupby('wind_dir_floor').agg({
-            'power': ['mean', 'max', 'min', 'median', lambda x: (x > 0).sum(), lambda x: (x > x.median()).sum()]
-        }).reset_index()
-
-        # Rename columns for clarity
-        grouped.columns = ['wind_dir_floor', 'mean_power', 'max_power',
-                           'min_power', 'median_power', 'power_gt_0', 'power_gt_80p']
-
-        # Calculate total sums for conditions
-        power_gt_0_sum = grouped['power_gt_0'].sum()
-        power_gt_80p_sum = grouped['power_gt_80p'].sum()
-
-        # Calculate ratios
-        grouped['ratio_0'] = grouped['power_gt_0'] / power_gt_0_sum
-        grouped['ratio_80p'] = grouped['power_gt_80p'] / power_gt_80p_sum
-
-        # Filter out zero ratios and calculate slope
-        grouped = grouped[grouped['ratio_0'] > 0]
-        grouped['slop'] = grouped['ratio_80p'] / grouped['ratio_0']
-
-        # Sort by wind direction floor
-        grouped.sort_values('wind_dir_floor', inplace=True)
-
-        # Write to CSV
-        grouped.to_csv(output_path, index=False)
-
-    def turbinesAnalysis(self, dataFrameMerge, outputAnalysisDir, confData: ConfBusiness):
-        self.yaw_result(outputAnalysisDir)
-
-    def poly_func(self, x, a, b, c, d, e):
-        return a * x**4 + b * x ** 3 + c * x ** 2 + d * x + e
-
-    def yaw_result(self, csvFileDir):
-        files = os.listdir(csvFileDir)
-        for file in files:
-            old_name = os.path.join(csvFileDir, file)
-            print(file)
-            # if os.path.isdir(old_name):
-            #    data_stat(old_name)
-            #    continue
-            # print(old_name)
-            if not file.endswith(".csv"):
-                continue
-            data_path = old_name
-            # limit_path = data_path+".limit"
-
-            df = pd.read_csv(data_path)
-            
-            # 不具备普适性,视静态偏航误差输出数据结果调整,经2024-3-13验证输出结果不可信
-            # df = df[df["wind_dir_floor"] > -12]
-            # df = df[df["wind_dir_floor"] < 12]
-
-            df.dropna(inplace=True)
-            # drop extreme value, the 3 largest and 3 smallest
-
-            df_ = df[df["ratio_80p"] > 0.000]
-
-            xdata = df_['wind_dir_floor']
-            ydata = df_['slop']
-
-            # make ydata smooth
-            ydata = ydata.rolling(7).median()[6:]
-            xdata = xdata[3:-3]
-            
-            # 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()
-
-            # subplots
-            plt.figure(figsize=(10, 6))
-            fig = plt.subplot(211)
-            plt.title(file)
-            plt.scatter(x=df["wind_dir_floor"], y=df["slop"],
-                        s=5, label="energy gain")
-            plt.plot(xdata, fitted_ydata, 'r-', label="fit")
-            plt.scatter(xdata[max_pos], fitted_ydata[max_pos], s=20,
-                        label="max pos:"+str(df["wind_dir_floor"][max_pos]))
-            plt.legend()
-            plt.grid(True)
-            fig = plt.subplot(212)
-            plt.scatter(x=df["wind_dir_floor"],
-                        y=df["ratio_0"], s=5, label="base energy")
-            plt.scatter(x=df["wind_dir_floor"],
-                        y=df["ratio_80p"], s=5, label="slope energy")
-            plt.legend()
-            plt.grid(True)
-            plt.savefig(data_path+".png")
-            plt.close()
-            # calc squear error of fitted_ydata and ydata
-
-            print(file, df["wind_dir_floor"][max_pos])
-
-    # def yaw_result(self, csvFileDir):
-    #     files = os.listdir(csvFileDir)
-    #     for file in files:
-    #         if not file.endswith(".csv"):
-    #             continue
-
-    #         data_path = os.path.join(csvFileDir, file)
-    #         df = pd.read_csv(data_path)
-    #         df.dropna(inplace=True)
-
-    #         # Filter and smooth data
-    #         df_ = df[df["ratio_80p"] > 0.000]
-    #         xdata = df_['wind_dir_floor']
-    #         ydata = df_['slop'].rolling(7).median()[6:]
-    #         xdata = xdata[3:-3].reset_index(drop=True)
-    #         ydata = ydata.reset_index(drop=True)
-
-    #         # Curve fitting
-    #         popt, _ = curve_fit(self.poly_func, xdata, ydata)
-    #         fitted_ydata = self.poly_func(xdata, *popt)
-    #         max_pos = fitted_ydata.argmax()
-
-    #         # Plotting
-    #         fig = make_subplots(rows=2, cols=1)
-
-    #         # Scatter plot of energy gain
-    #         fig.add_trace(go.Scatter(x=df["wind_dir_floor"], y=df["slop"], mode='markers', name="energy gain", marker=dict(size=5)), row=1, col=1)
-    #         # Line plot of fit
-    #         fig.add_trace(go.Scatter(x=xdata, y=fitted_ydata, mode='lines', name="fit", line=dict(color='red')), row=1, col=1)
-    #         # Marker for max position
-    #         fig.add_trace(go.Scatter(x=[xdata[max_pos]], y=[fitted_ydata[max_pos]], mode='markers', name=f"max pos: {xdata[max_pos]}", marker=dict(size=10, color='orange')), row=1, col=1)
-
-    #         # Scatter plot of base and slope energy
-    #         fig.add_trace(go.Scatter(x=df["wind_dir_floor"], y=df["ratio_0"], mode='markers', name="base energy", marker=dict(size=5)), row=2, col=1)
-    #         fig.add_trace(go.Scatter(x=df["wind_dir_floor"], y=df["ratio_80p"], mode='markers', name="slope energy", marker=dict(size=5)), row=2, col=1)
-
-    #         fig.update_layout(height=600, width=800, title_text=file)
-    #         fig.write_image(data_path+".png")
-
-    #         print(file, xdata[max_pos])

+ 0 - 106
wtoaamapi/apps/business/appConfig.py

@@ -1,106 +0,0 @@
-import os
-from utils.jsonUtil import JsonUtil
-from dataclasses import dataclass
-from utils.logUtil import LogUtil
-from utils.rdbmsUtil.databaseUtil import DatabaseUtil
-from utils.minioUtil.minioClientPool import MinioClientPool
-from utils.minioUtil.threadSafeMinioClient import ThreadSafeMinioClient
-from algorithmContract.const import *
-
-
-@dataclass
-class DatabaseConfig:
-    type: str
-    url: str
-    timeout: int  # Timeout in seconds
-    poolSize: int
-    maxPoolSize: int
-    minPoolSize: int
-    maxIdleTime: int  # Max idle time in seconds
-
-
-@dataclass
-class MinioConfig:
-    endpoint: str
-    accessKey: str
-    secretKey: str
-    secure: bool
-    poolSize: int
-    timeout: int  # Timeout in seconds
-
-
-@dataclass
-class LoggingConfig:
-    logFilePath: str
-    maxFileSize: int
-    maxTotalSize: int
-    backupCount: int
-    logFormat: str
-    level: str
-
-
-@dataclass
-class AppConfig:
-    databaseConfig: dict
-    minioConfig: MinioConfig
-    loggingConfig: LoggingConfig
-
-    @staticmethod
-    def loadFromFile(filePath: str):
-        configJson = JsonUtil.read_json(filePath)
-        businessFoundationDb = DatabaseConfig(
-            **configJson['databaseConfig'][DATABASE_BusinessFoundationDb])
-        businessDb = DatabaseConfig(
-            **configJson['databaseConfig'][DATABASE_businessDb])
-        minio = MinioConfig(**configJson['minioConfig'])
-        logging = LoggingConfig(**configJson['loggingConfig'])
-        return AppConfig({
-            "businessFoundationDb": businessFoundationDb,
-            "businessDb": businessDb
-        }, minio, logging)
-
-
-# Example usage
-configFilePath = os.environ.get('APP_CONFIG_FILE', r"conf/appConfig.json")
-config = AppConfig.loadFromFile(configFilePath)
-# config = AppConfig.loadFromFile(r'conf/appConfig.json')
-
-
-def GetLogger():
-    return LogUtil(config.loggingConfig.logFilePath, config.loggingConfig.logFormat,
-                   config.loggingConfig.maxTotalSize, config.loggingConfig.backupCount).getLogger()
-
-
-def GetBusinessFoundationDbUtil():
-    return DatabaseUtil(url=config.databaseConfig[DATABASE_BusinessFoundationDb].url,
-                        pool_size=config.databaseConfig[DATABASE_BusinessFoundationDb].poolSize,
-                        max_overflow=config.databaseConfig[DATABASE_BusinessFoundationDb].maxPoolSize,
-                        connect_timeout=config.databaseConfig[DATABASE_BusinessFoundationDb].timeout)
-
-
-def GetBusinessDbUtil():
-    return DatabaseUtil(url=config.databaseConfig[DATABASE_businessDb].url,
-                        pool_size=config.databaseConfig[DATABASE_businessDb].poolSize,
-                        max_overflow=config.databaseConfig[DATABASE_businessDb].maxPoolSize,
-                        connect_timeout=config.databaseConfig[DATABASE_businessDb].timeout)
-
-
-def GetDbUtil():
-    dbInstance = {}
-    return {
-        DATABASE_BusinessFoundationDb: DatabaseUtil(url=config.databaseConfig[DATABASE_BusinessFoundationDb].url,
-                                                    pool_size=config.databaseConfig[DATABASE_BusinessFoundationDb].poolSize,
-                                                    max_overflow=config.databaseConfig[
-                                                        DATABASE_BusinessFoundationDb].maxPoolSize,
-                                                    connect_timeout=config.databaseConfig[DATABASE_BusinessFoundationDb].timeout),
-        DATABASE_businessDb: DatabaseUtil(url=config.databaseConfig[DATABASE_businessDb].url,
-                                          pool_size=config.databaseConfig[DATABASE_businessDb].poolSize,
-                                          max_overflow=config.databaseConfig[DATABASE_businessDb].maxPoolSize,
-                                          connect_timeout=config.databaseConfig[DATABASE_businessDb].timeout)
-    }
-
-
-def GetMinIOUtil():
-    pool = MinioClientPool(config.minioConfig.endpoint, config.minioConfig.accessKey,
-                           config.minioConfig.secretKey, config.minioConfig.poolSize)
-    return ThreadSafeMinioClient(pool)

+ 0 - 169
wtoaamapi/apps/business/confBusiness.py

@@ -1,169 +0,0 @@
-from enum import Enum
-
-# 全局变量
-charset_unify = 'utf-8'
-CSVSuffix = '.csv'
-Const_Output_Total='total'
-Const_TimeGranularity_Second="second"
-Const_TimeGranularity_Minute="minute"
-
-### SCADA ###
-Field_DeviceCode="wind_turbine_number" #风机编号
-Field_ActiverPower="active_power"       #有功功率
-Field_GeneratorTorque = "actual_torque" #实际扭矩
-Field_GeneratorSpeed = "generator_speed" #发电机转速
-Field_RotorSpeed = "rotor_speed"       #风轮转速
-Field_WindSpeed = "wind_velocity"     #风速
-Field_WindDirection="true_wind_direction"  # 绝对风向
-Field_AngleIncluded = "yaw_error1"  # 对风角度
-Field_PitchAngel1="pitch_angle_blade_1"  # 桨距角1
-Field_PitchAngel2="pitch_angle_blade_2"  # 桨距角2
-Field_PitchAngel3="pitch_angle_blade_3"  # 桨距角3
-Field_Time="time_stamp"  # .strftime('%Y-%m-%d %H:%M:%S')
-Field_EnvTemp="outside_cabin_temperature"  # 环境温度
-Field_NacTemp="cabin_temperature"  # 机舱内温度
-Field_NacPos="cabin_position"  # 机舱位置
-Field_GeneratorDE="generatordrive_end_bearing_temperature"  # 发电机驱动端轴承温度/发电机低速轴温度
-Field_GeneratorNDE="generatornon_drive_end_bearing_temperature"  # 发电机非驱动端轴承温度/发电机高速轴温度
-Field_MainBearTemp="main_bearing_temperature"  # 主轴承轴承温度
-Field_GbHsBearTemp="gearbox_high_speed_shaft_bearing_temperature"  # 齿轮箱高速轴轴承温度
-Field_GbMsBearTemp="gearboxmedium_speed_shaftbearing_temperature"  # 齿轮箱中速轴轴承温度
-Field_GbLsBearTemp="gearbox_low_speed_shaft_bearing_temperature"  # 齿轮箱低速轴轴承温度
-Field_GenWiTemp1="generator_winding1_temperature"  # 发电机绕组1温度
-Field_GenWiTemp2="generator_winding2_temperature"  # 发电机绕组2温度
-Field_GenWiTemp3="generator_winding3_temperature"  # 发电机绕组3温度
-Field_GbOilTemp="gearbox_oil_temperature"  # 齿轮箱油温
-Field_PCA="power_curve_available"  # 功率曲线可用
-Field_APSet="set_value_of_active_power"  # 有功功率设定值
-Field_NacFbVib="front_back_vibration_of_the_cabin"  # 机舱前后振动
-Field_NacLrVib="side_to_side_vibration_of_the_cabin"  # 机舱左右振动
-Field_StatusOfTurbine="wind_turbine_status"  # 风机状态1
-
-# 故障/告警数据
-Field_DeviceCode="wind_turbine_number" #风机编号
-Field_DeviceName="wind_turbine_name"#风机名称
-Field_FaultTime="time_diff"#故障时长
-Field_FaultDetail="fault_detail"#故障类型
-Field_BeginTime="begin_time"#故障开始时间
-Field_EndTime="end_time"#故障开始时间
-
-
-### 二次计算 ###
-Field_Cp = "cp"
-Field_CpMedian = "cp_median"
-Field_CpMax = "cp_max"
-Field_CpMin = "cp_min"
-Field_TSR = "tsr"
-Field_TSRModified = "tsr_modified"
-Field_TSRMax = "tsr_max"
-Field_TSRMin = "tsr_min"
-Field_TSRMedian = "tsr_median"
-Field_Year="year"
-Field_Month="month"
-Field_UnixYearMonth="monthIntTime"
-Field_YearMonth = "year-month"
-Field_YearMonthDay = "year-month-day"
-Field_PowerFloor= "power_floor"
-Field_Power="power"
-Field_WindSpeedFloor= "wind_speed_floor"
-Field_YawError="yaw_error1"
-Field_LableFlag="lab"
-
-### 风场信息表 	wind_field ###
-Field_PowerFarmCode="field_code"
-Field_PowerFarmName="field_name"
-Field_ProvinceID="province_id"
-Field_ProvinceName="province_name"
-Field_CityID="city_id"
-Field_CityName="city_name"
-Field_CompanyCode="company_code"  # 企业编号
-Field_CreateBy="create_by"  # 创建人
-Field_CreateTime="create_time"  # 创建时间
-Field_AirDensity="density"  # 空气密度-合同功率曲线
-Field_NumberOfTurbine="engine_number"  # 风机数量
-Field_PowerContractURL="power_contract_url"  # 合同功率曲线地址
-Field_RatedPowerSUM="rated_capacity_number"  # 总额定容量-sum机组
-
-### 风机机组信息表 	wind_engine_group ###
-Field_NameOfTurbine="engine_name"
-Field_CodeOfTurbine="engine_code"
-Field_RatedPower="rated_capacity"  # 额定功率
-Field_RatedWindSpeed="rated_wind_speed" # 额定风速
-Field_Elevation="elevation_height"
-Field_HubHeight="hub_height"  # 轮毂高度
-Field_Latitude="latitude"  # wind_engine_mill 也有
-Field_Longitude="longitude"  # wind_engine_mill 也有
-Field_Sightcing="sightcing"  # 是否标杆风机
-
-### 风机机型信息表	wind_engine_mill ###
-Field_RotorDiameter="rotor_diameter" # 叶轮直径
-Field_Brand="brand"  # 品牌名称(风机)
-Field_Combination="combination"  # 组合字段
-Field_MotionType="curved_motion_type"  # 驱动方式
-Field_DelState="del_state"  # 删除方式
-Field_MachineTypeCode="machine_type_code"  # 机型型号
-Field_MillTypeCode="mill_type_code"  # 编号
-Field_ManufacturerCode="manufacturer_code"  # 厂商编号
-Field_ManufacturerName="manufacturer_name"  # 厂商名称
-Field_PowerCriterionURL="power_criterion_url"  # 标准功率曲线地址
-Field_TowerHeight="tower_height"
-Field_VaneLong="vane_long"  # 叶片长度
-Field_RSR="rotational_speed_ratio"  # 传动比-转速比
-Field_CutInWS="rated_cut_in_windspeed"  # 切入风速
-Field_CutOutWS="rated_cut_out_windspeed" # 切出风速
-
-### 测风塔信息 	anemometer_tower -> anemometer_tower_relation  ###
-Field_AnemometerCode="anemometer_code"
-Field_AnemometerName="anemometer_name"
-
-### 数据转换
-Field_TransferType="transfer_type"  # 转换类型 (枚举值:second、minute)
-Field_TimeGranularity="time_granularity"  # 时间粒度(秒)
-
-Field_State="state"
-Field_UpdateTime="update_time"
-Field_UpdateBy="update_by"
-
-Field_Return_TypeAnalyst="typeAnalyst"
-Field_Return_BatchCode="batch_code"
-Field_Return_FilePath="localFilePath"
-Field_Return_IsSaveDatabase:bool=True
-
-class ErrorState(Enum):
-    NotErr=0
-    Err=1     # 异常
-
-class AnalysisState(Enum):
-    NotAnalyzed=-1 # 未分析
-    RequstQueue=10 # 请求队列中
-    Analyzing=20    # 分析中
-    Analyzed=30     # 已分析
-
-class CustomError(Exception):
-    ERROR_CODES = {
-        -1:"未知异常,请联系技术人员排查问题",
-        100: "未获得业务基础数据,或基础数据缺失",
-        101: "未获得业务数据,或业务数据与基础数据不匹配",
-        102: "缺少风电机组运行数据",
-        103: "算法模型未输出结果",
-        104: "缺失场站基础信息",
-        105: "缺失机组基础信息",
-        106: "缺失场站的数据批次信息",
-        107: "场站存在未配置机型信息(额定风速、切入风速、切出风速)的机组",
-        108: "缺失机组的合同功率曲线信息",
-        109: "场站存在未配置额定功率的机组",
-        110: "场站未配置空气密度",
-        111: "场站所属风电机组存在未配置叶轮直径",
-        112: "SCADA数据不包含风速或其全无值",
-        113: "SCADA数据不包含有功功率或其全无值",
-        114: "机组未配置机型信息",
-        115: "机组未配置基础信息",
-    }
-
-    def __init__(self, code,msg=""):
-        self.code = code
-        self.message =f"{msg} {self.ERROR_CODES.get(code, CustomError.ERROR_CODES.get(-1))}"
-        super().__init__(self.message)
-
-    def __str__(self):
-        return f"[Error Code: {self.code}] {self.message}"

+ 0 - 14
wtoaamapi/apps/business/configAnalysis.py

@@ -1,14 +0,0 @@
-class ConfigAnalysis:
-    def __init__(self, package: str, className: str, methodName: str,scada:str):
-        self.package = package
-        self.className = className
-        self.methodName = methodName
-        self.scada=scada
-
-    def to_dict(self):
-        return {
-            "package": self.package,
-            "className": self.className,
-            "methodName": self.methodName,
-            "scada":self.scada
-        }

+ 0 - 3
wtoaamapi/apps/business/const.py

@@ -1,3 +0,0 @@
-
-DATABASE_BusinessFoundationDb="businessFoundationDb"
-DATABASE_businessDb="businessDb"

+ 0 - 199
wtoaamapi/apps/business/contract.py

@@ -1,199 +0,0 @@
-import traceback
-import json
-from algorithmContract.dataContractType import DataContractType
-from algorithmContract.customDataContract import DataContract
-from algorithmContract.dataSource import DataSource
-from algorithmContract.dataFilter import DataFilter
-from algorithmContract.customFilter import CustomFilter
-from algorithmContract.configAnalysis import ConfigAnalysis
-from algorithmContract.graphSet import GraphSet
-
-
-class Contract:
-    def __init__(self, contractType: DataContractType, dataContract: DataContract):
-        self.contractType = contractType
-        self.dataContract = dataContract
-
-    def to_dict(self):
-        return {
-            "dataContractType": self.contractType.to_dict(),
-            "dataContract": self.dataContract.to_dict()
-        }
-
-
-def LoadAnalysisInput(jsonString: str):
-    # Load the JSON string into a Python dictionary
-    try:
-        jsonData = json.loads(jsonString)
-
-        jsonDataType = jsonData["dataContractType"]
-        contractType = DataContractType(
-            jsonDataType["type"], jsonDataType["version"])
-
-        jsonDataContract = jsonData["dataContract"]
-
-        autoOrManual = jsonDataContract["autoOrManual"]
-        # Initialize the entity classes using the JSON data
-        # data_source = DataSource(jsonDataContract["dataSource"]["scada"])
-
-        custom_filters = {}
-        for key, value in jsonDataContract["dataFilter"]["customFilter"].items():
-            custom_filter = CustomFilter(value.get("min"), value.get("max"))
-            custom_filters[key] = custom_filter
-            
-        if (not jsonDataContract["dataFilter"]["turbines"] is None) and (not isinstance(jsonDataContract["dataFilter"]["turbines"],list)):
-              raise Exception("输入参数中机组数据类型不合法.")  
-
-        data_filter = DataFilter(
-            jsonDataContract["dataFilter"]["powerFarmID"],
-            jsonDataContract["dataFilter"]["turbines"],
-            jsonDataContract["dataFilter"]["dataBatchNum"],
-            jsonDataContract["dataFilter"]["beginTime"],
-            jsonDataContract["dataFilter"]["endTime"],
-            jsonDataContract["dataFilter"]["excludingMonths"],
-            custom_filters
-        )
-
-        config_analysis = []
-        for item in jsonDataContract["configAnalysis"]:
-            analysis = ConfigAnalysis(
-                item["package"], item["className"], item["methodName"], item["scada"])
-            config_analysis.append(analysis)
-
-        graph_sets = {}
-        for key, value in jsonDataContract["graphSets"].items():
-            graph_set = GraphSet(value["step"], value["min"], value["max"])
-            graph_sets[key] = graph_set
-
-        data_contract = DataContract(autoOrManual,
-                                     data_filter, config_analysis, graph_sets)
-
-        contract = Contract(contractType, data_contract)
-    except Exception as e:
-        print("exception: {}".format(e))
-        traceback.print_exc()  # 打印异常的堆栈跟踪
-        raise e
-
-    return contract
-
-
-# JSON string representing the data contract
-jsonString = '''
-{
-	"dataContractType": {
-		"type": "analysisExecuteOrder",
-		"version": "1.2.0"
-	},
-	"dataContract": {
-		"autoOrManual":"automatic",
-		"dataSource": {
-			"scada": "second"
-		},
-		"dataFilter": {
-			"powerFarmID": "010-00001",
-			"turbines": [
-				"010-00001-0001",
-				"010-00001-0002"
-			],
-			"dataBatchNum": "B2024042211-0",
-			"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.powerCurveAnalyst",
-				"className": "PowerCurveAnalyst",
-				"methodName": "executeAnalysis"
-			},
-			{
-				"package": "algorithm.powerScatter2DAnalyst",
-				"className": "PowerScatter2DAnayst",
-				"methodName": "executeAnalysis"
-			},
-			{
-				"package": "algorithm.powerScatterAnalyst",
-				"className": "PowerScatterAnalyst",
-				"methodName": "executeAnalysis"
-			},
-			{
-				"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
-			},
-			"activePower": {
-				"step": 250,
-				"min": 0,
-				"max": 2000
-			}
-		}
-	}
-}
-'''
-
-
-def load():
-    return LoadAnalysisInput(jsonString)
-
-
-def Analysis(contract: Contract):
-    jsonString = ""
-    try:
-        contractDict = contract.to_dict()  # 先转换为字典
-        jsonString = json.dumps(contractDict)
-    except:
-        traceback.print_exc()
-    return jsonString

+ 0 - 35
wtoaamapi/apps/business/customDataContract.py

@@ -1,35 +0,0 @@
-import types
-from algorithmContract.dataSource import DataSource
-from algorithmContract.dataFilter import DataFilter
-from algorithmContract.customFilter import CustomFilter
-from algorithmContract.configAnalysis import ConfigAnalysis
-from algorithmContract.graphSet import GraphSet
-
-
-class DataContract:
-    def __init__(self,autoOrManual:str , dataFilter: DataFilter, configAnalysis: list[ConfigAnalysis], graphSets: dict[str,GraphSet]):
-        self.autoOrManual=autoOrManual
-        # self.dataSource = dataSource
-        self.dataFilter = dataFilter
-        self.configAnalysis = configAnalysis
-        self.graphSets = graphSets
-
-    def _graph_sets_to_dict(self, graph_sets):
-        result = {}
-        for key, value in graph_sets.items():
-            if isinstance(value, GraphSet):
-                result[key] = value.to_dict()
-            elif isinstance(value, dict):
-                result[key] = self._graph_sets_to_dict(value)
-            else:
-                raise TypeError(f"Unsupported type for graph set value: {type(value)}")
-        return result
-
-    def to_dict(self):
-        return {
-            "autoOrManual":self.autoOrManual,
-            # "dataSource": self.dataSource.to_dict(),
-            "dataFilter": self.dataFilter.to_dict(),            
-            "configAnalysis": [analysis.to_dict() for analysis in self.configAnalysis],  # 修改这一行
-            "graphSets": {key: graph.to_dict() for key, graph in self.graphSets.items()}  # 假设 graphSets 是一个字典 # self._graph_sets_to_dict(self.graphSets) # 
-        }

+ 0 - 7
wtoaamapi/apps/business/customFilter.py

@@ -1,7 +0,0 @@
-class CustomFilter:
-    def __init__(self, min_val:float, max_val:float):
-        self.min = min_val
-        self.max = max_val
-
-    def to_dict(self):
-        return {"min": self.min,"max":self.max}

+ 0 - 7
wtoaamapi/apps/business/dataContractType.py

@@ -1,7 +0,0 @@
-class DataContractType:
-    def __init__(self, type:str, version:str):
-        self.type = type
-        self.version = version
-    
-    def to_dict(self):
-        return {"type": self.type,"version":self.version}

+ 0 - 23
wtoaamapi/apps/business/dataFilter.py

@@ -1,23 +0,0 @@
-from algorithmContract.customFilter import CustomFilter
-
-
-class DataFilter:
-    def __init__(self, powerFarmID: str, turbines: list, dataBatchNum: str, beginTime: str, endTime: str, excludingMonths: list, customFilter: dict[str,CustomFilter]):
-        self.powerFarmID = powerFarmID
-        self.turbines = turbines
-        self.dataBatchNum = dataBatchNum
-        self.beginTime = beginTime
-        self.endTime = endTime
-        self.excludingMonths = excludingMonths
-        self.customFilter = customFilter
-
-    def to_dict(self):
-        return {
-            "powerFarmID": self.powerFarmID,
-            "turbines": self.turbines,
-            "dataBatchNum": self.dataBatchNum,
-            "beginTime": self.beginTime,
-            "endTime": self.endTime,
-            "excludingMonths": self.excludingMonths,
-            "customFilter": {key: filter.to_dict() for key, filter in self.customFilter.items()}
-        }

+ 0 - 6
wtoaamapi/apps/business/dataSource.py

@@ -1,6 +0,0 @@
-class DataSource:
-    def __init__(self, scada:str):
-        self.scada = scada
-        
-    def to_dict(self):
-        return {"scada": self.scada}

+ 0 - 8
wtoaamapi/apps/business/graphSet.py

@@ -1,8 +0,0 @@
-class GraphSet:
-    def __init__(self, step:float, min_val:float, max_val:float):
-        self.step = step
-        self.min = min_val
-        self.max = max_val
-
-    def to_dict(self):
-        return {"step": self.step,"min":self.min,"max":self.max}

+ 0 - 107
wtoaamapi/apps/business/testDataContract.py

@@ -1,107 +0,0 @@
-import json
-from algorithmContract.contract import LoadAnalysisInput,Analysis
-
-
-
-# JSON string representing the data contract
-jsonString = '''
-{
-	"dataContractType": {
-		"type": "analysisExecuteOrder",
-		"version": "1.2.0"
-	},
-	"dataContract": {
-		"autoOrManual": "automatic",
-		"dataFilter": {
-			"powerFarmID": "WOF01000002",
-			"turbines": null,
-			"dataBatchNum": "zhaoyuan_20240528",
-			"beginTime": "2023-01-01 00:00:00",
-			"endTime": "2024-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.temperatureLargeComponentsAnalyst",
-				"className": "TemperatureLargeComponentsAnalyst",
-				"methodName": "executeAnalysis",
-				"scada": "minute"
-			}
-		],
-		"graphSets": {
-			"directDrive": {
-				"generatorSpeed": {
-					"step": 5,
-					"min": 0,
-					"max": 30
-				},
-				"generatorTorque": {
-					"step": 10000,
-					"min": 0,
-					"max": 100000
-				}
-			},
-			"indirectDrive": {
-				"generatorSpeed": {
-					"step": 200,
-					"min": 1000,
-					"max": 2000
-				},
-				"generatorTorque": {
-					"step": 2000,
-					"min": 0,
-					"max": 12000
-				}
-			},
-			"tsr": {
-				"step": 5,
-				"min": 0,
-				"max": 30
-			},
-			"pitchAngle": {
-				"step": 1,
-				"min": -1,
-				"max": 20
-			},
-			"activePower": {
-				"step": 250,
-				"min": 0,
-				"max": 2000
-			}
-		}
-	}
-}
-'''
-
-
-data=LoadAnalysisInput(jsonString)
-
-print(data.dataContract.graphSets["directDrive"]["generatorSpeed"].step)
-print(data.dataContract.graphSets["directDrive"]["generatorTorque"].step)
-
-string=Analysis(data)
-print(string)
-
-
-

+ 0 - 20
wtoaamapi/apps/business/turbineInfo.py

@@ -1,20 +0,0 @@
-from algorithmContract.confBusiness import *
-import pandas as pd
-
-
-def loadTurbineInfo(turbineInfoFilePathCSV,charset=charset_unify):
-    """
-    通过机组信息csv文件,载入机组信息
-
-    参数:
-    turbineInfoFilePathCSV (str): 机组信息csv文件路径
-    charset (str): 读取文件编码字符集,默认为utf-8
-
-    返回:
-    pandas.DataFrame : 机组信息
-    """
-    dataFrameOfTurbine= pd.read_csv(turbineInfoFilePathCSV,encoding=charset)
-
-    return dataFrameOfTurbine
-    
-