import pandas as pd import os from algorithmContract.confBusiness import * from algorithmContract.contract import Contract from behavior.analystNotFilter import AnalystNotFilter class FaultAnalyst(AnalystNotFilter): """ 风电机组故障分析 新逻辑:基于首发故障结束时间划分窗口, 以首发故障的结束时间为界,后续故障的开始时间如果 ≤ 这个结束时间,则视为连带故障;否则作为新的首发故障。 风场范围的故障分析 统计风场范围内 首发故障中各故障发生的次数和累计时长 """ def typeAnalyst(self): return "fault" def selectColumns(self): # 直接返回数据库表中的真实列名 return ["wind_turbine_name", "begin_time", "end_time", "fault_detail"] #风机原始名称、故障开始时间、故障结束时间、故障描述 def getTimeGranularitys(self, conf: Contract): return ["fault"] def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes): dictionary = self.processTurbineData(turbineCodes, conf, self.selectColumns()) dataFrameMerge = dictionary.get("fault", pd.DataFrame()) if dataFrameMerge.empty: return pd.DataFrame() return self.get_result(dataFrameMerge, outputAnalysisDir, conf) def get_result(self, dataFrame: pd.DataFrame, outputAnalysisDir: str, conf: Contract): if dataFrame.empty: return pd.DataFrame() df = dataFrame.copy() # 转换时间列 df["begin_time"] = pd.to_datetime(df["begin_time"]) df["end_time"] = pd.to_datetime(df["end_time"]) df = df.dropna(subset=["begin_time", "end_time"]) if df.empty: return pd.DataFrame() # 存储中间结果 turbine_events = [] fault_events = [] # 按风机分组处理 for turbine, group in df.groupby("wind_turbine_name"): group = group.sort_values("begin_time").reset_index(drop=True) i = 0 n = len(group) while i < n: primary = group.iloc[i] primary_start = primary["begin_time"] primary_end = primary["end_time"] duration_sec = (primary_end - primary_start).total_seconds() turbine_events.append({ "turbine": turbine, "duration_sec": duration_sec }) fault_events.append({ "fault_detail": primary["fault_detail"], "duration_sec": duration_sec }) # 跳过连带故障 j = i + 1 while j < n and group.iloc[j]["begin_time"] <= primary_end: j += 1 i = j # 聚合风机维度 if turbine_events: turbine_df = pd.DataFrame(turbine_events) turbine_summary = turbine_df.groupby("turbine").agg( count=("duration_sec", "size"), fault_time=("duration_sec", "sum") ).reset_index() turbine_summary = turbine_summary.rename(columns={"turbine": "wind_turbine_name"}) turbine_file = os.path.join(outputAnalysisDir, f"turbine_fault_result{CSVSuffix}") turbine_summary.to_csv(turbine_file, index=False, encoding='utf-8-sig') else: turbine_summary = pd.DataFrame() # 聚合故障类型维度 if fault_events: fault_df = pd.DataFrame(fault_events) fault_summary = fault_df.groupby("fault_detail").agg( count=("duration_sec", "size"), fault_time_sum=("duration_sec", "sum") ).reset_index() fault_file = os.path.join(outputAnalysisDir, f"total_fault_result{CSVSuffix}") fault_summary.to_csv(fault_file, index=False, encoding='utf-8-sig') else: fault_summary = pd.DataFrame() # 返回结果 result_rows = [] if not turbine_summary.empty: 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: "turbine_fault_result", Field_Return_FilePath: turbine_file, Field_Return_IsSaveDatabase: True }) if not fault_summary.empty: 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_fault_result", Field_Return_FilePath: fault_file, Field_Return_IsSaveDatabase: True }) return pd.DataFrame(result_rows)