# 艾默生OIF平台运行主程序
import os.path
import traceback
import polars as pl
from yangke.performance.iapws97 import H_PT, T_P
from yangke.base import interpolate_value_simple
import joblib
import numpy as np
import time
import collections
import csv

# 尝试导入OIF库，如果失败则定义模拟类
try:
    # noinspection all
    from oif.pisystem_python import SystemLib
    # noinspection all
    from oif.pipoint_python import PointLib

    OIF_AVAILABLE = True
    print("OIF库导入成功")
except ImportError:
    print("OIF库导入失败，使用自定义SystemLib和PointLib")
    OIF_AVAILABLE = False


    # 定义模拟的SystemLib
    class SystemLib:
        @staticmethod
        def WriteAdvisory(message):
            print(f"{message}")


    # 定义模拟的PointLib
    class PointLib:
        @staticmethod
        def GetFloatPoint(kks):
            # 模拟从PI系统获取数据，返回默认值或随机值
            return None

        @staticmethod
        def GetFloatPointList(kks_list):
            # 模拟批量获取数据
            return [PointLib.GetFloatPoint(kks) for kks in kks_list]

        @staticmethod
        def SetPoint(tag_name, value):
            return True

folder = r"E:\TPRI"
if not os.path.exists(folder):
    folder = r"C:\wps同步\高安屯冷端历史"
heat_para = "汽机抽汽供热量"  # "总供热量", "汽机抽汽供热量"


class StabilityMonitor:
    def __init__(self, window_size=20, stability_thresholds=None):
        """
        初始化稳定性监控器

        Args:
            window_size: 窗口大小（10分钟/30秒 = 20个数据点）
            stability_thresholds: 各参数的稳定性阈值字典
        """
        self.window_size = window_size
        # 使用deque自动维护固定大小的窗口
        self.data_buffer = collections.deque(maxlen=window_size)

        # 默认稳定性阈值（需要根据实际业务调整）
        self.stability_thresholds = stability_thresholds or {
            '汽轮发电机功率': 5.0,  # 汽轮发电机功率变化阈值(MW)
            '热负荷': 10.0,  # 汽机抽汽供热量变化阈值(MW)
            '环境温度': 2.0,  # 环境温度变化阈值(°C)
            '循泵运行数字编码': 0,  # 循泵运行数字编码变化阈值
            '风机运行台数': 0  # 风机运行台数变化阈值
        }

    def add_data_point(self, data_point):
        """
        添加新的数据点

        Args:
            data_point: 包含所有参数的字典
        """
        timestamp = time.time()
        data_with_time = {
            'timestamp': timestamp,
            '汽轮发电机功率': data_point.get('汽轮发电机功率', 0),
            '热负荷': data_point.get('热负荷', 0),
            '环境温度': data_point.get('环境温度', 0),
            '循泵运行数字编码': data_point.get('循泵运行数字编码', 0),
            '风机运行台数': data_point.get('风机运行台数', 0)
        }
        self.data_buffer.append(data_with_time)

    def is_stable(self):
        """
        判断当前工况是否稳定

        Returns:
            bool: True表示稳定，False表示不稳定
        """
        # 数据点不足时不判断稳定性
        if len(self.data_buffer) < self.window_size:
            return False

        # 获取窗口内的数据
        values = list(self.data_buffer)

        # 检查每个参数的稳定性
        parameters = [
            ('汽轮发电机功率', '汽轮发电机功率'),
            ('热负荷', '热负荷'),
            ('环境温度', '环境温度'),
            ('循泵运行数字编码', '循泵运行数字编码'),
            ('风机运行台数', '风机运行台数')
        ]

        for param_key, dict_key in parameters:
            values_list = [item[dict_key] for item in values]
            max_val = max(values_list)
            min_val = min(values_list)
            variation = abs(max_val - min_val)

            # 如果任一参数变化超过阈值，则认为不稳定
            if variation > self.stability_thresholds[param_key]:
                return False

        return True

    def get_current_status(self):
        """
        获取当前状态信息

        Returns:
            dict: 包含稳定性状态和相关数据的字典
        """
        return {
            'is_stable': self.is_stable(),
            'data_points_count': len(self.data_buffer),
            'window_size': self.window_size
        }


class main_OIF:  # 该类名必须和文件名相同
    class Calculation:
        def __init__(self):
            SystemLib.WriteAdvisory('---------------------------------开始优化-------------------------------')
            self.warning = 0  # 0表示无报警，1表示背压过高，且循泵和风机均已调到最大，需要运行人员接管
            self.stability_monitor = StabilityMonitor(
                window_size=20, stability_thresholds={
                    '汽轮发电机功率': 5.0,  # 汽轮发电机功率变化阈值(MW)
                    '热负荷': 10.0,  # 汽机抽汽供热量变化阈值(MW)
                    '环境温度': 2.0,  # 环境温度变化阈值(°C)
                    '循泵运行数字编码': 0,  # 循泵运行数字编码变化阈值
                    '风机运行台数': 0  # 风机运行台数变化阈值
                })
            # 准备返回指定列的数据
            self.循泵运行方式映射 = {
                "000": 0,  # 0 kW # 0
                "001": 1,  # 836.7 kW # 1台低速小泵 15257 t/h
                "010": 2,  # 1381.3 kW # 1台高速小泵 18693 t/h，最小值12877，最大值22514
                "100": 3,  # 1904 kW # 1台大泵 # 30884 t/h
                "020": 4,  # 2762.6 kW # 2台高速小泵 # 36052 t/h
                "110": 5,  # 3450.5 kW # 一大一小高速 # 平均流量44912t/h
                "200": 6,  # 5174.7 kW # 2台大泵 # 平均流量52523t/h
                # "120": 7,  # 4303.5 kW # 一大两小高速（不常用） # 平均流量37442t/h # 这两种方式不常用，都不考虑
                # "210": 8,  # 5989.7 kW # 两大一小高速（不常用）
            }
            self.pump_power = {
                0: 0,
                1: 836.7,
                2: 1381.3,
                3: 1904,
                4: 2762.6,
                5: 3450.5,
                6: 5174.7,
            }
            self.fan_power = {
                0: 0,
                1: 223.6,
                2: 447.0,
                3: 670.7,
                4: 890.8,
                5: 1115.8,
                6: 1324.7,
                7: 1525.1,
                8: 1730.1,
            }
            self.tags = {
                "1号锅炉高压主汽流量": {"KKS": "11LBA90CF101-SEL.UNIT1@NET0.OVATION", "default": 296.644},
                "2号锅炉高压主汽流量": {"KKS": "12LBA90CF101-SEL.UNIT1@NET0.OVATION", "default": 295.641},
                "高压缸进汽压力": {"KKS": "13LBA40CP101.UNIT1@NET0.OVATION", "default": 11.835},
                "高压缸进汽温度": {"KKS": "13LBA40CT601.UNIT1@NET0.OVATION", "default": 537.059},
                "高排压力": {"KKS": "13LBC01CP101.UNIT1@NET0.OVATION", "default": 3.352},
                "高排温度": {"KKS": "13LBC01CT601.UNIT1@NET0.OVATION", "default": 359.035},
                "高排至1号炉流量": {"KKS": "11LBC80CF101-SEL.UNIT1@NET0.OVATION", "default": 308.141},
                "高排至2号炉流量": {"KKS": "12LBC80CF101-SEL.UNIT1@NET0.OVATION", "default": 333.668},
                "1号炉高旁减温水流量": {"KKS": "11LAE93CF101-CAL.UNIT1@NET0.OVATION", "default": 0.000},
                "2号炉高旁减温水流量": {"KKS": "12LAE93CF101-CAL.UNIT1@NET0.OVATION", "default": 2.906},
                "再热蒸汽母管压力": {"KKS": "13LBB40CP101.UNIT1@NET0.OVATION", "default": 3.084},
                "再热蒸汽母管温度": {"KKS": "13LBB40CT601.UNIT1@NET0.OVATION", "default": 566.345},
                "1号炉再热蒸汽流量": {"KKS": "11LBB80CF101-CAL.UNIT1@NET0.OVATION", "default": 384.809},
                "2号炉再热蒸汽流量": {"KKS": "12LBX80CF101-SEL.UNIT1@NET0.OVATION", "default": 409.371},
                "低压主蒸汽压力(中排混合前)": {"KKS": "13LBA60CP101.UNIT1@NET0.OVATION", "default": 0.555},
                "低压主蒸汽温度(中排混合前)": {"KKS": "13LBA60CT601.UNIT1@NET0.OVATION", "default": 247.790},
                "低压主汽至热网抽汽压力(中排混合后)": {"KKS": "10LBD92CP101.UNIT1@NET0.OVATION", "default": 0.464},
                "低压主汽至热网抽汽温度(中排混合后)": {"KKS": "10LBD92CT601.UNIT1@NET0.OVATION", "default": 314.482},
                "低压主汽至热网调门反馈(分支管)": {"KKS": "10LBA61AA102XB12.UNIT1@NET0.OVATION", "default": 5.000},
                "低压主汽母管至热网调门": {"KKS": "13RCAOSE2502.UNIT1@NET0.OVATION", "default": 4.719},
                "1号炉再热蒸汽减温水调门反馈": {"KKS": "11LAF80AA101XB12.UNIT1@NET0.OVATION", "default": 0.093},
                "1号炉过热蒸汽减温水调门": {"KKS": "11LAE90AA101XB12.UNIT1@NET0.OVATION", "default": 38.202},
                "1号机高压汽包连排调门": {"KKS": "11HAD95AA101XB12.UNIT1@NET0.OVATION", "default": 3.050},
                "1号机中压汽包连排调门": {"KKS": "11HAD85AA101XB12.UNIT1@NET0.OVATION", "default": 3.000},
                "1号机低压汽包连排调门": {"KKS": "11HAD75AA101XB12.UNIT1@NET0.OVATION", "default": 3.080},
                "1号炉过热蒸汽减温水流量": {"KKS": "11LAE90CF101-COMP.UNIT1@NET0.OVATION", "default": 5.594},
                "1号炉再热蒸汽减温水流量": {"KKS": "11LAF80CF101-COMP.UNIT1@NET0.OVATION", "default": 0.000},
                "2号炉再热蒸汽减温水调门反馈": {"KKS": "12LAF80AA101XB12.UNIT1@NET0.OVATION", "default": -0.090},
                "2号炉过热蒸汽减温水调门": {"KKS": "12LAE90AA101XB12.UNIT1@NET0.OVATION", "default": 51.942},
                "2号机高压汽包连排调门": {"KKS": "12HAD95AA101XB12.UNIT1@NET0.OVATION", "default": 3.040},
                "2号机中压汽包连排调门": {"KKS": "12HAD85AA101XB12.UNIT1@NET0.OVATION", "default": 3.200},
                "2号机低压汽包连排调门": {"KKS": "12HAD75AA101XB12.UNIT1@NET0.OVATION", "default": 3.100},
                "2号炉过热蒸汽减温水流量": {"KKS": "12LAE90CF101-COMP.UNIT1@NET0.OVATION", "default": 4.254},
                "2号炉再热蒸汽减温水流量": {"KKS": "12LAF80CF101-COMP.UNIT1@NET0.OVATION", "default": 0.000},
                "瞬时热网供水热量": {"KKS": "RWJL1-07.UNIT1@NET0.OVATION", "default": 1207.560},
                "瞬时热网回水热量": {"KKS": "RWJL1-08.UNIT1@NET0.OVATION", "default": 912.556},
                "热网瞬时供水流量": {"KKS": "RWJL1-09.UNIT1@NET0.OVATION", "default": 3604.886},
                "热网瞬时回水流量": {"KKS": "RWJL1-10.UNIT1@NET0.OVATION", "default": 3600.650},
                "热网供水压力": {"KKS": "10NDA60CP101.UNIT1@NET0.OVATION", "default": 0.642},
                "热网回水压力": {"KKS": "10NDB10CP101.UNIT1@NET0.OVATION", "default": 0.259},
                "热网供水温度": {"KKS": "RWJL1-11.UNIT1@NET0.OVATION", "default": 79.755},
                "热网回水温度": {"KKS": "RWJL1-12.UNIT1@NET0.OVATION", "default": 60.647},
                "1号炉烟气供热器供热量": {"KKS": "11LCC71CQ101.UNIT1@NET0.OVATION", "default": 68.988},
                "2号炉烟气供热器供热量": {"KKS": "12LCC71CQ101.UNIT1@NET0.OVATION", "default": 60.147},
                "凝汽器真空选择值": {"KKS": "10MAG10CP101-SEL.UNIT1@NET0.OVATION", "default": 7.436},
                "凝汽器真空1": {"KKS": "10MAG10CP101.UNIT1@NET0.OVATION", "default": 7.501},
                "凝汽器真空2": {"KKS": "10MAG10CP102.UNIT1@NET0.OVATION", "default": 7.364},
                "汽轮发电机功率": {"KKS": "10CJA07FU101.UNIT1@NET0.OVATION", "default": 279.990},
                "燃机功率1": {"KKS": "11GT-MW.UNIT1@NET0.OVATION", "default": 305.151},
                "燃机功率2": {"KKS": "12GT-MW.UNIT1@NET0.OVATION", "default": 303.749},
                "汽机循环水供水管道流量1": {"KKS": "10PAC11CF101.UNIT1@NET0.OVATION", "default": 17362.748},
                "汽机循环水供水管道流量2": {"KKS": "10PAC11CF102.UNIT1@NET0.OVATION", "default": 14625.702},
                "1号大循泵电流（额定298A）": {"KKS": "10PAC11AP001XQ01.UNIT1@NET0.OVATION", "default": 235.388},
                "2号大循泵电流（额定298A）": {"KKS": "10PAC12AP001XQ01.UNIT1@NET0.OVATION", "default": -0.787},
                "3号小循泵电流（高速）": {"KKS": "10PAC13AP001XQ01.UNIT1@NET0.OVATION", "default": -0.499},
                "3号小循泵电流（低速）": {"KKS": "10PAC13AP001XQ02.UNIT1@NET0.OVATION", "default": -0.181},
                "4号小循泵电流（高速）": {"KKS": "10PAC14AP001XQ01.UNIT1@NET0.OVATION", "default": -0.224},
                "4号小循泵电流（低速）": {"KKS": "10PAC14AP001XQ02.UNIT1@NET0.OVATION", "default": 0.002},
                "凝汽器进水压力1": {"KKS": "13MAG01CP103.UNIT1@NET0.OVATION", "default": 0.158},
                "凝汽器进水压力2": {"KKS": "13MAG01CP105.UNIT1@NET0.OVATION", "default": 0.158},
                "凝汽器进水温度1": {"KKS": "13MAG01CT303.UNIT1@NET0.OVATION", "default": 26.286},
                "凝汽器进水温度2": {"KKS": "13MAG01CT305.UNIT1@NET0.OVATION", "default": 26.097},
                "凝汽器回水温度1": {"KKS": "13MAG01CT304.UNIT1@NET0.OVATION", "default": 40.115},
                "凝汽器回水温度2": {"KKS": "13MAG01CT306.UNIT1@NET0.OVATION", "default": 40.052},
                "机力塔风机1电流": {"KKS": "10PAG11AN001XQ01.UNIT1@NET0.OVATION", "default": 22.942},
                "机力塔风机2电流": {"KKS": "10PAG11AN002XQ01.UNIT1@NET0.OVATION", "default": -0.072},
                "机力塔风机3电流": {"KKS": "10PAG11AN003XQ01.UNIT1@NET0.OVATION", "default": 23.232},
                "机力塔风机4电流": {"KKS": "10PAG11AN004XQ01.UNIT1@NET0.OVATION", "default": 23.816},
                "机力塔风机5电流": {"KKS": "10PAG11AN005XQ01.UNIT1@NET0.OVATION", "default": 23.377},
                "机力塔风机6电流": {"KKS": "10PAG11AN006XQ01.UNIT1@NET0.OVATION", "default": 23.103},
                "机力塔风机7电流": {"KKS": "10PAG11AN007XQ01.UNIT1@NET0.OVATION", "default": 23.679},
                "机力塔风机8电流": {"KKS": "10PAG11AN008XQ01.UNIT1@NET0.OVATION", "default": 22.019},
                "空气湿球温度": {"KKS": "10PAC11CT111.UNIT1@NET0.OVATION", "default": 15.352},
                "风速": {"KKS": "10AC10FS101-X.UNIT1@NET0.OVATION", "default": 3.166},
                "大气压力1": {"KKS": "11RCAOGC00702.UNIT1@NET0.OVATION", "default": 1019.309},
                "大气压力2": {"KKS": "12RCAOGC00702.UNIT1@NET0.OVATION", "default": 1019.717},
                "#1燃机环境温度": {"KKS": "11MBL01CT101.UNIT1@NET0.OVATION", "default": 17.790},
                "#2燃机环境温度": {"KKS": "12MBL01CT101.UNIT1@NET0.OVATION", "default": 17.377},
                "#1燃机进气湿度": {"KKS": "11MBL01CM101A.UNIT1@NET0.OVATION", "default": 78.851},
                "#2燃机进气湿度": {"KKS": "12MBL01CM101A.UNIT1@NET0.OVATION", "default": 71.840},
                "1号炉TCA入口温度": {"KKS": "11RCAOMD60502.UNIT1@NET0.OVATION", "default": 164.378},
                "1号炉TCA入口流量": {"KKS": "11RCAOMD60501.UNIT1@NET0.OVATION", "default": 99.127},
                "1号炉TCA出口温度": {"KKS": "11RCAOMD60503.UNIT1@NET0.OVATION", "default": 270.086},
                "1号炉TCA入口压力": {"KKS": "11LAB90CP101-SEL.UNIT1@NET0.OVATION", "default": 14.470},
                "1号炉高压给水流量(TCA分流后)": {"KKS": "11LAB90CF101-SEL.UNIT1@NET0.OVATION", "default": 185.922},
                "1号炉高压给水温度": {"KKS": "11LAB90CT301-SEL.UNIT1@NET0.OVATION", "default": 165.975},
                "1号炉中压给水流量": {"KKS": "11LAB80CF101-SEL.UNIT1@NET0.OVATION", "default": 119.225},
                "1号炉中压给水压力": {"KKS": "11LAB81CP103-SEL.UNIT1@NET0.OVATION", "default": 5.496},
                "1号炉中压给水温度": {"KKS": "11LAB80CT301-SEL.UNIT1@NET0.OVATION", "default": 160.461},
                "1号FGH入口流量": {"KKS": "11RCAOME60410.UNIT1@NET0.OVATION", "default": 37.362},
                "1号FGH入口温度": {"KKS": "11RCAOME60408.UNIT1@NET0.OVATION", "default": 239.760},
                "1号FGH入口压力": {"KKS": "11LAB83CP101.UNIT1@NET0.OVATION", "default": 5.140},
                "1号FGH出口温度": {"KKS": "11RCAOME60409.UNIT1@NET0.OVATION", "default": 85.915},
                "1号炉凝结水流量(含FGH回水)": {"KKS": "11LAB70CF101-SEL.UNIT1@NET0.OVATION", "default": 447.744},
                "1号炉凝结水压力(FGH混合后)": {"KKS": "11LCA70CP102.UNIT1@NET0.OVATION", "default": 2.539},
                "1号炉凝结水温度(FGH混合后)": {"KKS": "11LCA70CT302.UNIT1@NET0.OVATION", "default": 57.819},
                "2号炉TCA入口温度": {"KKS": "12RCAOMD60502.UNIT1@NET0.OVATION", "default": 162.634},
                "2号炉TCA入口流量": {"KKS": "12RCAOMD60501.UNIT1@NET0.OVATION", "default": 80.298},
                "2号炉TCA出口温度": {"KKS": "12RCAOMD60503.UNIT1@NET0.OVATION", "default": 287.206},
                "2号炉TCA入口压力": {"KKS": "12LAB90CP101-SEL.UNIT1@NET0.OVATION", "default": 14.593},
                "2号炉高压给水流量(TCA分流后)": {"KKS": "12LAB90CF101-SEL.UNIT1@NET0.OVATION", "default": 202.594},
                "2号炉高压给水温度": {"KKS": "12LAB90CT301-SEL.UNIT1@NET0.OVATION", "default": 165.581},
                "2号炉中压给水流量": {"KKS": "12LAB80CF101-SEL.UNIT1@NET0.OVATION", "default": 116.211},
                "2号炉中压给水压力": {"KKS": "12LAB81CP103-SEL.UNIT1@NET0.OVATION", "default": 5.595},
                "2号炉中压给水温度": {"KKS": "12LAB80CT301-SEL.UNIT1@NET0.OVATION", "default": 164.071},
                "2号炉FGH入口流量": {"KKS": "12RCAOME60410.UNIT1@NET0.OVATION", "default": 37.726},
                "2号炉FGH入口温度": {"KKS": "12RCAOME60408.UNIT1@NET0.OVATION", "default": 240.540},
                "2号炉FGH入口压力": {"KKS": "12LAB83CP101.UNIT1@NET0.OVATION", "default": 5.350},
                "2号炉FGH出口温度": {"KKS": "12RCAOME60409.UNIT1@NET0.OVATION", "default": 87.798},
                "2号炉凝结水流量(含FGH回水)": {"KKS": "12LAB70CF101-SEL.UNIT1@NET0.OVATION", "default": 450.884},
                "2号炉凝结水压力(FGH混合后)": {"KKS": "12LCA70CP102.UNIT1@NET0.OVATION", "default": 2.534},
                "2号炉凝结水温度(FGH混合后)": {"KKS": "12LCA70CT302.UNIT1@NET0.OVATION", "default": 58.391},
                "凝泵出口凝结水流量": {"KKS": "13LCA20CF101-SEL.UNIT1@NET0.OVATION", "default": 728.051},
                "凝结水补水流量": {"KKS": "10GHA10CF101-SEL.UNIT1@NET0.OVATION", "default": 0.000},
                "凝结水补水压力": {"KKS": "10GHA10CP101-CAL.UNIT1@NET0.OVATION", "default": 0.399},
                "凝结水补水温度": {"KKS": "10GHA10CT301.UNIT1@NET0.OVATION", "default": 23.389},
                "1号炉低过出口蒸汽流量": {"KKS": "11LBA70CF101-SEL.UNIT1@NET0.OVATION", "default": 40.144},
                "2号炉低过出口蒸汽流量": {"KKS": "12LBA70CF101-SEL.UNIT1@NET0.OVATION", "default": 37.835},
                "1号炉中压过热器出口流量": {"KKS": "11LBA80CF101-SEL.UNIT1@NET0.OVATION", "default": -1},
                "1号炉凝结水再循环流量": {"KKS": "11LCC71CF101-COMP.UNIT1@NET0.OVATION", "default": 202.291},
                "1号炉烟气加热器入口凝结水温度": {"KKS": "11LCC71CT301.UNIT1@NET0.OVATION", "default": 170.774},
                "1号炉烟气加热器出口凝结水温度": {"KKS": "11LCC71CT302.UNIT1@NET0.OVATION", "default": 81.122},
                "2号炉凝结水再循环流量": {"KKS": "12LCC71CF101-COMP.UNIT1@NET0.OVATION", "default": 266.144},
                "2号炉烟气加热器入口凝结水温度": {"KKS": "12LCC71CT301.UNIT1@NET0.OVATION", "default": 135.305},
                "2号炉烟气加热器出口凝结水温度": {"KKS": "12LCC71CT302.UNIT1@NET0.OVATION", "default": 75.361},
                "2号炉中压过热器出口流量": {"KKS": "12LBA80CF101-SEL.UNIT1@NET0.OVATION", "default": -1.000},
                "凝泵出口凝结水压力": {"KKS": "13LDB10CP101.UNIT1@NET0.OVATION", "default": 0.300},
                "凝泵出口凝结水温度": {"KKS": "13LCA11CT601.UNIT1@NET0.OVATION", "default": 43.408},
                "低压缸入口调门开度": {"KKS": "13RCAOSE2501.UNIT1@NET0.OVATION", "default": 99.033},
                "纯凝模式": {"KKS": "10CJA03FU010.UNIT1@NET0.OVATION", "default": 0},
                "背压模式": {"KKS": "10CJA03FU009.UNIT1@NET0.OVATION", "default": 0},
                "12号热网加热器疏水": {"KKS": "10LCP40CF101.UNIT1@NET0.OVATION", "default": 0.000},
                "34号热网加热器疏水": {"KKS": "10LCP80CF101.UNIT1@NET0.OVATION", "default": 0.000},
                "燃机1天然气流量": {"KKS": "11RCAOGB01801.UNIT1@NET0.OVATION", "default": 0.000},
                "燃机2天然气流量": {"KKS": "12RCAOGB01801.UNIT1@NET0.OVATION", "default": 0.000},
                "1号冷却塔进水蝶阀": {"KKS": "10PAG11AA001XB01.UNIT1@NET0.OVATION", "default": 1.000},
                "2号冷却塔进水蝶阀": {"KKS": "10PAG11AA002XB01.UNIT1@NET0.OVATION", "default": 1},
                "3号冷却塔进水蝶阀": {"KKS": "10PAG11AA003XB01.UNIT1@NET0.OVATION", "default": 1.000},
                "4号冷却塔进水蝶阀": {"KKS": "10PAG11AA004XB01.UNIT1@NET0.OVATION", "default": 1.000},
                "5号冷却塔进水蝶阀": {"KKS": "10PAG11AA005XB01.UNIT1@NET0.OVATION", "default": 1.000},
                "6号冷却塔进水蝶阀": {"KKS": "10PAG11AA006XB01.UNIT1@NET0.OVATION", "default": 1.000},
                "7号冷却塔进水蝶阀": {"KKS": "10PAG11AA007XB01.UNIT1@NET0.OVATION", "default": 1.000},
                "8号冷却塔进水蝶阀": {"KKS": "10PAG11AA008XB01.UNIT1@NET0.OVATION", "default": 1.000},
                "凝汽器循环水回水温度1": {"KKS": "13MAG01CT304.UNIT1@NET0.OVATION", "default": 0},
                "凝汽器循环水回水温度2": {"KKS": "13MAG01CT306.UNIT1@NET0.OVATION", "default": 0}
            }

            self.tags_res = {
                "pump_big_opt": "TP-DABENG-NUM-S.UNIT1@NET0.OVATION",
                "pump_high_opt": "TP-XIAOBENGGAOSU-NUM-S.UNIT1@NET0.OVATION",
                "pump_low_opt": "TP-XIAOBENGDISU-NUM-S.UNIT1@NET0.OVATION",
                "fan_opt": "TP-FAN-NUM-S.UNIT1@NET0.OVATION",
                "循泵实际功耗": "TP-TOTAL-POWER-PUMP-ACT.UNIT1@NET0.OVATION",
                "循泵建议功耗": "TP-TOTAL-POWER-PUMP-S.UNIT1@NET0.OVATION",
                "风机实际功耗": "TP-TOTAL-POWER-FAN-ACT.UNIT1@NET0.OVATION",
                "风机建议功耗": "TP-TOTAL-POWER-FAN-S.UNIT1@NET0.OVATION",
                "凝汽器热负荷": "TP-Q-CON-ACT.UNIT1@NET0.OVATION",
                "凝汽器端差": "TP-COND-DUANCHA.UNIT1@NET0.OVATION",
                "凝汽器汽阻": "TP-COND-QIZU.UNIT1@NET0.OVATION",
                "凝汽器过冷度": "TP-COND-GUOLENGDU.UNIT1@NET0.OVATION",
                "循环水温升": "TP-COLD-BK1.UNIT1@NET0.OVATION",
                "power_cold": "TP-TOTAL-POWER-COLD-ACT.UNIT1@NET0.OVATION",
                "power_cold_opt": "TP-TOTAL-POWER-COLD-S.UNIT1@NET0.OVATION",
                "power_cold_delta": "TP-TOTAL-POWER-COLD-DLT.UNIT1@NET0.OVATION",
                "power_inc": "TP-NET-POWER-INC.UNIT1@NET0.OVATION",  # 净功率增加量
                "gas_consume": "TP-GAS-ASS-ACT.UNIT1@NET0.OVATION",
                "gas_consume_opt": "TP-GAS-ASS-S.UNIT1@NET0.OVATION",
                "gas_dec": "TP-GAS-ASS-DLT.UNIT1@NET0.OVATION",
                "bp": "TP-BP-ACT.UNIT1@NET0.OVATION",
                "bp_opt": "TP-BP-S.UNIT1@NET0.OVATION",
                "power_st_opt": "TP-POWER-OPT.UNIT1@NET0.OVATION",  # 优化后的汽轮发电机功率
                "power_st_delta": "TP-POWER-ST-DELTA.UNIT1@NET0.OVATION"
            }
            # try:
            #    self.model = DataFitterNet.load_yk(os.path.join(folder, "背压预测模型10.torch"))
            #    self.input_columns = self.model.x_title
            # except:
            #    self.input_columns = None
            #    SystemLib.WriteAdvisory(f"未找到模型文件:{os.path.join(folder, '背压预测模型10.torch')}")
            self.input_columns = None
            try:
                self.water_flow_model = joblib.load('circulating_water_flow_model.pkl')  # 循环水流量计算模型
            except FileNotFoundError:
                SystemLib.WriteAdvisory(
                    f'未找到循环水流量计算模型:{os.path.abspath("circulating_water_flow_model.pkl")}')

            SystemLib.WriteAdvisory("导入历史数据")
            try:
                self.dataset_chu = pl.read_csv(os.path.join(folder, "纯凝.csv"))
                self.dataset_cho = pl.read_csv(os.path.join(folder, "抽凝.csv"))
                SystemLib.WriteAdvisory("历史数据加载完成")
                if self.input_columns is None:
                    self.input_columns: list = self.dataset_chu.columns
                    self.input_columns.remove('DateTime')
                    SystemLib.WriteAdvisory(f"使用历史数据集中的列标题作为输入参数：{self.input_columns}")
                # 分析背压偏差的原因

            except:
                SystemLib.WriteAdvisory(f'未找到数据文件:{os.path.join(folder, "纯凝.csv")}')
            self.snapshot: dict = self.get_snapshot()
            self.last_optimization_params = None
            self.last_optimization_result = None

            self.bp_history = []  # 用于存储历史背压值
            self.bp_history_length = 19  # 历史背压值的数量
            self.sample_intervals = {'汽轮发电机功率': 2, heat_para: 20,
                                     '环境温度': 1, '凝结水补水流量': 1,
                                     '循泵运行方式数字编码': 0, '风机运行台数': 0,
                                     '冷却塔进水蝶阀开启数量': 0,
                                     '大气湿度': 5}  # [1, 10, 1, 1, 1, 1, 1, 5]  # 指定每个参数的采样间隔
            self.current_index = 0
            self.refer_cols = ['汽轮发电机功率', heat_para, '环境温度', '大气湿度', '循泵运行方式数字编码',
                               '风机运行台数', '冷却塔进水蝶阀开启数量']
            self.results_list = []  # 处理历史数据时保存处理结果，正常优化时不需要该参数

            self.results_file = '纯凝.csv'  # 定义结果文件名
            # 写入CSV文件的头部
            with open(self.results_file, 'w', encoding='utf-8') as f:
                # 准备头部信息，包含所有可能的列
                import csv
                # 先写入头部
                header = self.refer_cols.copy()  # 参数列
                # 为了确定所有可能的列名，我们先读取一个数据集获取所有列名
                header.extend([col for col in self.dataset_chu.columns if col not in header])
                writer = csv.writer(f)
                writer.writerow(header)
            self.snapshot['纯凝模式'] = 1
            self.analysis_bp_deviation(self.dataset_chu)

            self.results_file = '抽凝.csv'  # 定义结果文件名
            # 写入CSV文件的头部
            with open(self.results_file, 'w', encoding='utf-8') as f:
                # 准备头部信息，包含所有可能的列
                import csv
                # 先写入头部
                header = self.refer_cols.copy()  # 参数列
                header.extend([col for col in self.dataset_cho.columns if col not in header])
                writer = csv.writer(f)
                writer.writerow(header)
            self.snapshot['纯凝模式'] = 0
            self.analysis_bp_deviation(self.dataset_cho)

        def iterate_all_combinations(self, param_ranges, total_num):
            """
            递归遍历所有参数组合
            param_ranges = [
                ('汽轮发电机功率', [80, 82, 84, 86, 88, 90, 92, 292]),
                ('汽机抽汽供热量', [-107, -87, -67, -47, -27, -7, 13, 33, 53, 73, 93, 113, 133, 153, 173, 193, 213, 233, 253]),
                ('环境温度', [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]),
                ('大气湿度', [3, 13, 23, 33, 43, 53, 63, 73, 83, 93]),
                ('循泵运行方式数字编码', [2, 3, 4, 5, 6]),
                ('风机运行台数', [2, 3, 4, 5, 6, 7, 8]),
                ('冷却塔进水蝶阀开启数量', [3, 4, 5, 6, 7, 8])
            ]
            """
            x_num = len(param_ranges)  # 表示有x_num各需要遍历的参数
            lvl = 0
            mean = None
            if x_num == 7:
                # 写x_num个嵌套的for循环
                for x1 in param_ranges[0][1]:
                    for x2 in param_ranges[1][1]:
                        for x3 in param_ranges[2][1]:
                            for x4 in param_ranges[3][1]:
                                for x5 in param_ranges[4][1]:
                                    for x6 in param_ranges[5][1]:
                                        for x7 in param_ranges[6][1]:
                                            current_combo = {
                                                param_ranges[0][0]: x1,
                                                param_ranges[1][0]: x2,
                                                param_ranges[2][0]: x3,
                                                param_ranges[3][0]: x4,
                                                param_ranges[4][0]: x5,
                                                param_ranges[5][0]: x6,
                                                param_ranges[6][0]: x7
                                            }
                                            mean, lvl = self.get_his_bp(current_combo, 1)
                                            if mean is not None:
                                                SystemLib.WriteAdvisory(f"{mean=}")
                                                # 将结果添加到列表中
                                                result_dict = current_combo.copy()
                                                # 添加mean中的各项数据
                                                for col in mean.columns:
                                                    result_dict[col] = mean[col][0] if mean[col].shape[0] > 0 else None

                                                # 实时写入CSV文件
                                                with open(self.results_file, 'a', encoding='utf-8', newline='') as f:
                                                    writer = csv.writer(f)
                                                    # 获取CSV头部（所有列名）
                                                    header = list(result_dict.keys())
                                                    # 获取值，保持与头部的顺序一致
                                                    row = [result_dict.get(col) for col in header]
                                                    writer.writerow(row)
                                            elif lvl < 7:
                                                # 根据lvl的数值跳出到第lvl个循环，如lvl=2，则x2的值调到下一个，x2之后的从头开始循环
                                                break
                                            else:
                                                continue
                                        if lvl < 6 and mean is None:
                                            break
                                    if lvl < 5 and mean is None:
                                        break
                                if lvl < 4 and mean is None:
                                    break
                            if lvl < 3 and mean is None:
                                break
                    if lvl < 2 and mean is None:
                        break

        # if index == len(param_ranges):
        #     self.current_index += 1
        #
        #     # 所有参数都已设置，处理当前组合
        #     SystemLib.WriteAdvisory(f"{self.current_index}/{total_num}参数组合: {current_combo.values()}")
        #     mean, lvl = self.get_his_bp(current_combo, 1)
        #     if mean is not None:
        #         SystemLib.WriteAdvisory(f"{mean=}")
        #         # 将结果添加到列表中
        #         result_dict = current_combo.copy()
        #         # 添加mean中的各项数据
        #         for col in mean.columns:
        #             result_dict[col] = mean[col][0] if mean[col].shape[0] > 0 else None
        #
        #         # 实时写入CSV文件
        #
        #         with open(self.results_file, 'a', encoding='utf-8', newline='') as f:
        #             writer = csv.writer(f)
        #             # 获取CSV头部（所有列名）
        #             header = list(result_dict.keys())
        #             # 获取值，保持与头部的顺序一致
        #             row = [result_dict.get(col) for col in header]
        #             writer.writerow(row)
        # else:
        #     col_name, values = param_ranges[index]
        #     for value in values:
        #         current_combo[col_name] = value
        #
        #         # 检查当前参数组合是否可能匹配
        #         mean, lvl = self.get_his_bp(current_combo, 1)
        #
        #         # 如果mean不为None，说明找到了匹配数据，继续遍历
        #         if mean is not None:
        #             self.iterate_all_combinations(param_ranges, index + 1, current_combo.copy(), total_num)
        #         else:
        #             # mean为None，lvl表示从第几个参数开始没有符合条件的工况
        #             # 如果lvl等于当前参数在refer_cols中的位置+1，说明当前参数的这个值导致无法匹配
        #             current_param_position = None
        #             for i, refer_col in enumerate(self.refer_cols):
        #                 if refer_col == col_name:
        #                     current_param_position = i
        #                     break
        #
        #             # 如果当前参数在refer_cols中的位置等于lvl-1（因为lvl是从1开始计数的），说明当前参数就是导致无法匹配的原因
        #             if current_param_position is not None and current_param_position + 1 == lvl:
        #                 SystemLib.WriteAdvisory(f"参数{col_name}={value}导致无法匹配，跳过该值后续的遍历")
        #                 # 跳过当前参数的这个值，继续下一个值
        #                 continue
        #             else:
        #                 # 继续遍历，因为可能在其他参数上能找到匹配
        #                 self.iterate_all_combinations(param_ranges, index + 1, current_combo.copy(), total_num)

        def analysis_bp_deviation(self, df):
            """
            self.dataset_chu和self.dataset_cho中都包含['汽轮发电机功率', heat_para, '环境温度', '凝结水补水流量',
            '循泵运行方式数字编码', '风机运行台数', '冷却塔进水蝶阀开启数量', '大气湿度']这些列，代表联合循环机组运行在
            这些参数表征的特定工况下，同时，self.dataset_chu和self.dataset_cho中还包含背压列，表征每种运行工况下的背压。

            """

            # 获取dataset_chu中特定列的最小值和最大值

            # 确保这些列在数据集中存在
            existing_columns = [col for col in self.refer_cols if col in df.columns]
            if len(existing_columns) != len(self.refer_cols):
                raise ValueError("数据集缺少必要的列")

            # 指定每个参数的采样间隔
            intervals = [self.sample_intervals.get(col) for col in self.refer_cols]  # 根据您的要求指定间隔

            # 生成各列的采样值用于遍历
            param_ranges = []
            total_num = 1
            for i, col_name in enumerate(self.refer_cols):
                min_val = round(df.select(pl.col(col_name).min())[col_name][0])
                max_val = round(df.select(pl.col(col_name).max())[col_name][0])

                # 获取对应的间隔值，如果超出范围则使用最后一个值
                interval = intervals[i]

                # 根据数据类型和指定间隔生成采样值，所有参数都按整数处理
                if interval == 0:
                    sample_values = list(range(min_val, max_val + 1, 1))
                else:
                    sample_values = list(range(min_val, max_val + interval, interval * 2))
                param_ranges.append((col_name, sample_values))
                total_num *= len(sample_values)
                SystemLib.WriteAdvisory(f"列 {col_name}: 间隔={interval}, 采样值 {sample_values}")

            SystemLib.WriteAdvisory(f"参数组合总数为: {total_num}")
            # 使用递归或迭代方式实现多参数遍历
            if param_ranges:
                self.iterate_all_combinations(param_ranges, total_num)

        # _ = self.find_same_cols(self.dataset_chu,
        #                             ['汽轮发电机功率', heat_para, '环境温度', '凝结水补水流量',
        #                              '循泵运行方式数字编码',
        #                              '风机运行台数', '冷却塔进水蝶阀开启数量', '大气湿度'],
        #                             [1, 10, 1, 1, 1, 1, 1, 5])
        #
        # _ = self.find_same_cols(self.dataset_cho,
        #                         ['汽轮发电机功率', heat_para, '环境温度', '凝结水补水流量',
        #                          '循泵运行方式数字编码',
        #                          '风机运行台数', '冷却塔进水蝶阀开启数量', '大气湿度'],
        #                         [1, 10, 1, 1, 1, 1, 1, 5]
        #                         )
        # return _

        def predict_flowrate(self, pump_mode, valve_count):
            """
            根据循泵运行方式和机力塔上水蝶阀开启数量计算循环水流量

            Args:
                pump_mode: 循泵运行方式数字编码
                valve_count: 冷却塔进水蝶阀开启数量

            Returns:
                predicted_flow: 预测的循环水流量
            """
            _X = np.array([[pump_mode, valve_count]])
            predicted_flow = self.water_flow_model.predict(_X)
            return predicted_flow[0]

        # def get_his_bp(self, power, heat, temperature, rh, valves, pump, fan) -> (float, int):
        #     """
        #     根据汽轮发电机功率、供热量、环境温度、相对湿度、蝶阀数量、循泵和风机数量预测机组背压
        #
        #     返回(选中范围的行的平均值, 匹配的偏离程度)
        #     """
        #
        #     # 根据不同的运行模式选择不同的数据集
        #     if self.snapshot['纯凝模式'] > 0.5:  # 纯凝模式
        #         dataset = self.dataset_chu
        #     else:  # 抽凝模式
        #         dataset = self.dataset_cho
        #
        #     for level in range(1, 20):
        #         power_range = level * 1
        #         heat_range = level * 10
        #         rh_range = level * 5
        #         temp_range = level * 1
        #         # 蝶阀数量严格匹配
        #         # 循泵和风机运行方式严格匹配
        #
        #         result = dataset.filter(
        #             (pl.col("汽轮发电机功率") >= power - power_range) &
        #             (pl.col("汽轮发电机功率") <= power + power_range) &
        #             (pl.col(heat_para) >= heat - heat_range) &
        #             (pl.col(heat_para) <= heat + heat_range) &
        #             (pl.col("环境温度") >= temperature - temp_range) &
        #             (pl.col("环境温度") <= temperature + temp_range) &
        #             (pl.col("冷却塔进水蝶阀开启数量") == valves) &
        #             (pl.col("循泵运行方式数字编码") == pump) &
        #             (pl.col("风机运行台数") == fan)
        #         )
        #
        #         if result.height > 0:
        #             return result.mean(), level
        #
        #     # 如果仍然没有找到匹配的数据，则返回默认值
        #     SystemLib.WriteAdvisory(
        #         f"未找到匹配的历史数据，功率={power}, {heat_para}={heat}, 温度={temperature}, 循泵={pump}, 风机={fan}")
        #     return None, 6
        def get_his_bp(self, param_values: dict, max_lvl=20):
            """
            根据参数字典预测机组背压

            Args:
                param_values: 参数字典，键为参数名，值为参数值
                max_lvl: 允许的最大偏差级别

            Returns:
                tuple: (选中范围的行的平均值, 匹配的偏离程度)
            """
            # 根据不同的运行模式选择不同的数据集
            if self.snapshot['纯凝模式'] > 0.5:  # 纯凝模式
                dataset = self.dataset_chu
            else:  # 抽凝模式
                dataset = self.dataset_cho

            for level in range(0, max_lvl):
                # 构建过滤条件
                conditions = []

                for col_name in self.refer_cols:
                    if col_name in param_values:
                        current_val = param_values[col_name]
                        interval = self.sample_intervals.get(col_name, 1)  # 默认间隔为1
                        range_val = (level + 1) * interval
                        if range_val == 0:
                            # 严格相等
                            conditions.append(pl.col(col_name) == current_val)
                        else:
                            conditions.append(
                                (pl.col(col_name) >= current_val - range_val) &
                                (pl.col(col_name) <= current_val + range_val)
                            )

                # 应用所有条件
                query = dataset
                i = 0
                for condition in conditions:
                    i += 1
                    query = query.filter(condition)
                    if query.height == 0:
                        SystemLib.WriteAdvisory(f"第{i}个条件未匹配，参数值={param_values}")
                        return None, i

                if query.height > 0:
                    SystemLib.WriteAdvisory(f"找到匹配的历史数据{query.height=}，参数值={param_values}")
                    return query.mean(), level + 1

            # 如果仍然没有找到匹配的数据，则返回默认值
            SystemLib.WriteAdvisory(
                f"未找到匹配的历史数据，参数值={param_values}")
            return None, 20

        def get_best_his_run_type(self) -> pl.DataFrame | None:
            """
            获取历史上当前['汽轮发电机功率', '总供热量', '环境温度', '冷却塔进水蝶阀开启数量', '大气湿度']下的
            行，并查找这些工况下都有哪些'循泵运行方式数字编码'和'风机运行台数'，按照'循泵运行方式数字编码'和'风机运行台数'的不同组合
            分组。查询方式类似于self.get_his_bp()方法，其中['汽轮发电机功率', '总供热量', '环境温度', '冷却塔进水蝶阀开启数量',
            '大气湿度']参数可以从self.snapshot中获取。
            """
            # 获取当前工况的关键参数
            current_power = self.snapshot['汽轮发电机功率']
            current_heat = self.snapshot[heat_para]
            current_temperature = self.snapshot['环境温度']
            current_humidity = self.snapshot['大气湿度']
            current_valve_count = self.snapshot['冷却塔进水蝶阀开启数量']

            # 根据不同的运行模式选择不同的数据集
            if self.snapshot['纯凝模式'] > 0.5:
                dataset = self.dataset_chu
            else:
                dataset = self.dataset_cho

            # 如果没有找到精确匹配，则逐步扩大搜索范围
            for level in range(1, 20):
                power_range = level * 1
                heat_range = level * 10
                temp_range = level * 1
                humidity_range = level * 10

                result = dataset.filter(
                    (pl.col("汽轮发电机功率") >= current_power - power_range) &
                    (pl.col("汽轮发电机功率") <= current_power + power_range) &
                    (pl.col(heat_para) >= current_heat - heat_range) &
                    (pl.col(heat_para) <= current_heat + heat_range) &
                    (pl.col("环境温度") >= current_temperature - temp_range) &
                    (pl.col("环境温度") <= current_temperature + temp_range) &
                    (pl.col("大气湿度") >= current_humidity - humidity_range) &
                    (pl.col("大气湿度") <= current_humidity + humidity_range) &
                    (pl.col("冷却塔进水蝶阀开启数量") == current_valve_count)
                )

                if result.height > 0:
                    # 按照'循泵运行方式数字编码'和'风机运行台数'的不同组合分组
                    SystemLib.WriteAdvisory(f"{result.columns=}")
                    SystemLib.WriteAdvisory(f"{result.height=}")
                    grouped_result = result.group_by(["循泵运行方式数字编码", "风机运行台数"]).mean().sort("气耗率",
                                                                                                           descending=True)
                    SystemLib.WriteAdvisory(f"{grouped_result=}")
                    return grouped_result

            # 如果仍未找到匹配项，返回None
            SystemLib.WriteAdvisory(f"未找到匹配的历史运行数据，"
                                    f"功率={current_power}, {heat_para}={current_heat}, "
                                    f"温度={current_temperature}, 湿度={current_humidity}, "
                                    f"蝶阀={current_valve_count}")
            return None

        def 安全边界(self, pump_opt, fan_opt):
            """
            确保优化结果的最佳冷端运行方式在安全边界之内
            """
            # 如果当前背压>8.78，pump_opt取6，如果当前背压>7.94，fan_opt取8
            # 如果当前背压<3.91，pump_opt取1，fan_opt取0，防止背压过低，防冻
            self.warning = 0
            if self.snapshot['背压'] > 9.3:
                pump_opt = 6
                fan_opt = 8
                SystemLib.WriteAdvisory(f"背压过高，且循泵风机均已开到最大，请检查系统运行方式")
                self.warning = 1
            if self.snapshot['背压'] > 8.78:
                SystemLib.WriteAdvisory(f"背压较高，将循泵和风机开到最大")
                pump_opt = 6
                fan_opt = 8
            elif self.snapshot['背压'] > 7.94:
                SystemLib.WriteAdvisory(f"背压较高，将循泵和风机开到较大")
                fan_opt = 8
                if pump_opt < 5:
                    pump_opt = 5
            elif self.snapshot['背压'] < 3.91:  # 背压低于3.91是因为当前冷端设备运行的较大导致的，不能简单地将pump_opt=1, fan_opt=0，否则背压会升高很多
                if pump_opt < self.snapshot["循泵运行方式数字编码"]:
                    pump_opt = self.snapshot["循泵运行方式数字编码"]
                if fan_opt < self.snapshot["风机运行台数"]:
                    fan_opt = self.snapshot["风机运行台数"]
            return pump_opt, fan_opt

        def prevent_frequent_fluctuation(self, recommended_pump, recommended_fan):
            """
            防止在某些临界点上，运行条件稍微变化一点导致最佳运行方式反复波动的问题
            通过判断关键参数是否波动超过一定值来决定是否更新优化结果

            Args:
                recommended_pump: 推荐的循泵运行方式数字编码
                recommended_fan: 推荐的风机运行台数

            Returns:
                tuple: (最终确定的循泵运行方式, 最终确定的风机运行台数)
            """
            # 定义各参数的波动阈值
            thresholds = {
                '汽轮发电机功率': 5.0,  # 功率波动阈值，单位MW
                heat_para: 50.0,  # 热负荷波动阈值
                '环境温度': 2.0,  # 环境温度波动阈值，单位℃
                '冷却塔进水蝶阀开启数量': 1.0  # 蝶阀开启数量波动阈值
            }

            # 检查是否存在上次的优化结果记录
            if self.last_optimization_params is None:
                # 第一次运行，直接保存当前参数并返回推荐值
                self.last_optimization_params = {
                    '汽轮发电机功率': self.snapshot['汽轮发电机功率'],
                    heat_para: self.snapshot[heat_para],
                    '环境温度': self.snapshot['环境温度'],
                    '冷却塔进水蝶阀开启数量': self.snapshot['冷却塔进水蝶阀开启数量']
                }
                self.last_optimization_result = (recommended_pump, recommended_fan)
                return recommended_pump, recommended_fan

            # 检查各关键参数的波动情况
            params_stable = True
            current_params = {
                '汽轮发电机功率': self.snapshot['汽轮发电机功率'],
                heat_para: self.snapshot[heat_para],
                '环境温度': self.snapshot['环境温度'],
                '冷却塔进水蝶阀开启数量': self.snapshot['冷却塔进水蝶阀开启数量']
            }

            for param, threshold in thresholds.items():
                last_value = self.last_optimization_params[param]
                current_value = current_params[param]
                fluctuation = abs(current_value - last_value)

                if fluctuation > threshold:
                    params_stable = False
                    SystemLib.WriteAdvisory(f"参数 {param} 波动过大: 上次={last_value:.2f}, 当前={current_value:.2f}, "
                                            f"波动={fluctuation:.2f}, 阈值={threshold:.2f}")

            # 更新参数记录
            self.last_optimization_params = current_params

            # 如果参数波动不大，则保持上次的优化结果
            if params_stable:
                SystemLib.WriteAdvisory("关键参数波动较小，保持上次优化结果")
                return self.last_optimization_result
            else:
                SystemLib.WriteAdvisory("关键参数波动较大，更新为当前优化结果")
                self.last_optimization_result = (recommended_pump, recommended_fan)
                return recommended_pump, recommended_fan

        def Execute(self):
            # Input rule code here
            # Py_Action_1.Action.Execute(Py_Action_1)
            try:
                self.snapshot = self.get_snapshot()
                self.cal_heat_load_condense()  # 背压模式下，只需要该函数计算的气耗率的数据
                if self.snapshot['背压模式'] == 1:
                    SystemLib.WriteAdvisory(f"背压模型运行")
                    self.write_result(1, 0, 0)
                    return True

                # 先计算当前运行方式对应的背压
                _input = [self.snapshot[col] for col in self.input_columns]
                his_run_type = self.get_best_his_run_type()
                if his_run_type is None:  # 说明以当前工况没有对应的历史数据，尽量不优化，保持运行方式不变，如果背压过高则增大风机循泵数量
                    pump_opt, fan_opt = self.安全边界(self.snapshot['循泵运行方式数字编码'],
                                                      self.snapshot['风机运行台数'])
                    SystemLib.WriteAdvisory(
                        f"当前循泵运行方式 = {self.snapshot['循泵运行方式数字编码']}，当前风机台数 = {self.snapshot['风机运行台数']}, {pump_opt=}, {fan_opt=}")
                    # 根据最近的汽轮发电机功率对背压进行插值，这里先直接去当前运行背压
                elif his_run_type.height == 1:
                    pump_opt_ = his_run_type.select(pl.col("循泵运行方式数字编码")).item()
                    fan_opt_ = his_run_type.select(pl.col("风机运行台数")).item()
                    pump_opt, fan_opt = self.安全边界(pump_opt_, fan_opt_)
                    SystemLib.WriteAdvisory(f"{pump_opt_=}, {fan_opt_=}, {pump_opt=}, {fan_opt=}")
                else:  # 有多种组合方式
                    _1 = his_run_type.select(pl.col("循泵运行方式数字编码")).to_series()
                    _2 = his_run_type.select(pl.col("风机运行台数")).to_series()
                    SystemLib.WriteAdvisory(f"循泵运行方式列表 = {_1}")
                    SystemLib.WriteAdvisory(f"风机运行台数列表 = {_2}")
                    pump_opt_ = _1.item(0)
                    fan_opt_ = _1.item(0)
                    pump_opt, fan_opt = self.安全边界(pump_opt_, fan_opt_)
                    SystemLib.WriteAdvisory(f"{pump_opt_=}, {fan_opt_=}, {pump_opt=}, {fan_opt=}")

                pump_opt, fan_opt = self.prevent_frequent_fluctuation(pump_opt, fan_opt)

                param_values = {
                    '汽轮发电机功率': self.snapshot['汽轮发电机功率'],
                    heat_para: self.snapshot[heat_para],
                    '环境温度': self.snapshot['环境温度'],
                    '大气湿度': self.snapshot['大气湿度'],
                    '冷却塔进水蝶阀开启数量': self.snapshot['冷却塔进水蝶阀开启数量'],
                    '循泵运行方式数字编码': pump_opt,
                    '风机运行台数': fan_opt
                }
                row, index = self.get_his_bp(param_values)  # 计算最佳工况背压
                if row is not None:
                    bp_ = row.select(pl.col("背压")).item()
                    gas_consume_opt = row.select(pl.col("气耗率")).item()
                else:
                    SystemLib.WriteAdvisory(f"未找到当前工况下最佳运行方式的背压和气耗率数据，请检查数据")
                    bp_ = self.snapshot['背压']
                    gas_consume_opt = self.snapshot['气耗率']
                净功率增加 = 0

                param_values = {
                    '汽轮发电机功率': self.snapshot['汽轮发电机功率'],
                    heat_para: self.snapshot[heat_para],
                    '环境温度': self.snapshot['环境温度'],
                    '大气湿度': self.snapshot['大气湿度'],
                    '冷却塔进水蝶阀开启数量': self.snapshot['冷却塔进水蝶阀开启数量'],
                    '循泵运行方式数字编码': self.snapshot['循泵运行方式数字编码'],
                    '风机运行台数': self.snapshot['风机运行台数']
                }
                row, index = self.get_his_bp(param_values)  # 计算当前背压
                if row is not None:
                    self.snapshot['当前理论背压'] = row.select(pl.col("背压")).item()
                else:
                    SystemLib.WriteAdvisory(f"未找到当前工况下，实际冷端设备运行方式的气耗率数据，请检查数据")
                    self.snapshot['当前理论背压'] = self.snapshot['背压']

                self.write_result(pump_opt, fan_opt, bp_)
                return True
            except:
                SystemLib.WriteAdvisory(f"{traceback.format_exc()}")
                return True

        def find_same_cols(self, df, cols, precisions):
            """
            查找df中是否存在指定列cols相同的行，判断相同的精度为precisions。
            precisions中的值表示比较的精度，例如：
            - 1 表示精确到个位数
            - 10 表示精确到十位数
            - 0.1 表示精确到小数点后一位

            Args:
                df: polars DataFrame对象
                cols: 需要比较的列名列表
                precisions: 对应每列的比较精度列表

            Returns:
                list: 按重复次数降序排列的DataFrame列表，每个DataFrame包含一组重复的行
            """
            if len(cols) != len(precisions):
                raise ValueError("列数与精度数不匹配")

            # 创建表达式列表用于添加舍入后的列
            rounded_exprs = []
            for col, precision in zip(cols, precisions):
                if precision >= 1:
                    # 对于大于等于1的精度，使用四舍五入到最近的倍数
                    rounded_exprs.append((pl.col(col) / precision).round() * precision)
                else:
                    # 对于小数精度，使用round函数
                    decimals = abs(int(np.log10(precision))) if precision > 0 else 0
                    rounded_exprs.append(pl.col(col).round(decimals))

            # 为原始DataFrame添加舍入后的列
            df_with_rounded = df.clone()
            for i, col in enumerate(cols):
                df_with_rounded = df_with_rounded.with_columns(
                    rounded_exprs[i].alias(f"_rounded_{col}")
                )

            # 构建用于分组的列名列表
            rounded_col_names = [f"_rounded_{col}" for col in cols]

            # 按照舍入后的值进行分组并统计每组的行数
            try:
                # 尝试使用 pl.len() (新版本)
                grouped_df = df_with_rounded.group_by(rounded_col_names).agg(
                    pl.len().alias("_count")
                )
            except AttributeError:
                # 如果 pl.len() 不存在，则使用 pl.count() (旧版本)
                grouped_df = df_with_rounded.group_by(rounded_col_names).agg(
                    pl.count().alias("_count")
                )

            # 找出有重复的组（计数大于1的组），并按重复次数降序排序
            duplicate_groups = grouped_df.filter(pl.col("_count") > 1).sort("_count", descending=True)

            # 如果没有重复的组，返回空列表
            if duplicate_groups.height == 0:
                return []

            # 为每个重复组创建DataFrame
            result_dfs = []
            for i in range(duplicate_groups.height):
                # 获取当前组的条件
                group_conditions = []
                group_values = duplicate_groups.row(i)
                for j, col_name in enumerate(rounded_col_names):
                    group_conditions.append(pl.col(col_name) == group_values[j])

                # 构建过滤条件
                filter_condition = group_conditions[0]
                for condition in group_conditions[1:]:
                    filter_condition = filter_condition & condition

                # 从原始数据中筛选出属于当前组的所有行
                matched_rows = df_with_rounded.filter(filter_condition)

                # 移除临时添加的舍入列
                result_cols = [col for col in matched_rows.columns if
                               not col.startswith("_rounded_") and col != "_count"]
                final_df = matched_rows.select(result_cols)

                result_dfs.append(final_df)

                # 检查同组中背压差别是否超过0.6kPa
                if "背压" in final_df.columns:
                    max_bp = final_df.select(pl.max("背压")).item()
                    min_bp = final_df.select(pl.min("背压")).item()
                    if max_bp - min_bp > 0.6:
                        SystemLib.WriteAdvisory(f"发现背压差异超过0.6kPa的组，第{i}组: 差异值={max_bp - min_bp:.3f}kPa")
                        # 可以在这里打印更多关于这组数据的信息
                        SystemLib.WriteAdvisory(f"该组数据条数: {final_df.height}")

            return result_dfs

        def get_snapshot(self) -> dict:
            try:
                SystemLib.WriteAdvisory("读取实时数据")
                # SystemLib.WriteAdvisory("read data from dcs")
                # try:
                #     kks_list = [obj.get('KKS') for obj in self.tags.values()]
                #     des_list = list(self.tags.keys())
                #     SystemLib.WriteAdvisory('1')
                #     tag_values = PointLib.GetFloatPointList(kks_list)
                #     SystemLib.WriteAdvisory('2')
                #     SystemLib.WriteAdvisory(f"tag_values = {tag_values}")
                # except:
                #     SystemLib.WriteAdvisory(f"{traceback.format_exc()}")

                kv = {}
                i = 0
                for des, obj in self.tags.items():
                    i = i + 1
                    # SystemLib.WriteAdvisory(f"{i=}")
                    kks = obj.get('KKS')
                    default_val = obj.get('default')
                    # SystemLib.WriteAdvisory(f"读取第{i}个测点，{kks=}, {default_val=}")
                    try:
                        tag_value = PointLib.GetFloatPoint(kks)
                    #    tag_value = default_val
                    except:
                        SystemLib.WriteAdvisory(f"{traceback.format_exc()}")
                    # SystemLib.WriteAdvisory(f"读取的实时数据为{tag_value}")
                    if tag_value is None:
                        SystemLib.WriteAdvisory(f"读取测点{des}的值失败，编码为{kks}，将使用默认值{default_val}")
                        kv[des] = default_val
                    else:
                        kv[des] = tag_value
                        SystemLib.WriteAdvisory(f"{des} = {kv[des]}")
                SystemLib.WriteAdvisory("实时数据读取完成")

            except:
                SystemLib.WriteAdvisory(f"{traceback.format_exc()}")
            return kv

        def download_history(self) -> dict:
            try:
                SystemLib.WriteAdvisory("读取实时数据")
                kv = {}
                i = 0
                for des, obj in self.tags.items():
                    i = i + 1
                    # SystemLib.WriteAdvisory(f"{i=}")
                    kks = obj.get('KKS')
                    default_val = obj.get('default')
                    # SystemLib.WriteAdvisory(f"读取第{i}个测点，{kks=}, {default_val=}")
                    try:
                        tag_value = PointLib
                    #    tag_value = default_val
                    except:
                        SystemLib.WriteAdvisory(f"{traceback.format_exc()}")
                    # SystemLib.WriteAdvisory(f"读取的实时数据为{tag_value}")
                    if tag_value is None:
                        SystemLib.WriteAdvisory(f"读取测点{des}的值失败，编码为{kks}，将使用默认值{default_val}")
                        kv[des] = default_val
                    else:
                        kv[des] = tag_value
                        SystemLib.WriteAdvisory(f"{des} = {kv[des]}")
                SystemLib.WriteAdvisory("实时数据读取完成")
            except:
                SystemLib.WriteAdvisory(f"{traceback.format_exc()}")
            return kv

        def write_result(self, pump_opt, fan_opt, bp_opt):
            """
            写计算结果
                "pump_big_opt": "",
                "pump_high_opt": "",
                "pump_low_opt": "",
                "fan_opt": "",
                "power_cold": "",
                "power_cold_opt": "",
                "power_inc": "",
                "gas_consume": "",
                "gas_consume_opt": "",
                "gas_dec": ""

            @param pump: 循泵运行方式
            @param fan: 风机运行数量

            """
            if isinstance(pump_opt, int):
                # 根据数字在self.循泵运行方式映射中查询对应的字符串
                for key, value in self.循泵运行方式映射.items():
                    if value == pump_opt:
                        pump_str = key
                        break
                else:
                    print('未找到循泵运行方式数字编码对应的运行方式，退出')
                    return
            else:
                pump_str = pump_opt

            # 计算循泵运行方式数字编码
            循泵状态 = {
                '1号大循泵': self.snapshot['1号大循泵电流（额定298A）'] > 1,
                '2号大循泵': self.snapshot['2号大循泵电流（额定298A）'] > 1,
                '3号小循泵高速': self.snapshot['3号小循泵电流（高速）'] > 1,
                '3号小循泵低速': self.snapshot['3号小循泵电流（低速）'] > 1,
                '4号小循泵高速': self.snapshot['4号小循泵电流（高速）'] > 1,
                '4号小循泵低速': self.snapshot['4号小循泵电流（低速）'] > 1
            }

            # 根据循泵运行状态确定运行方式编码
            # 构造运行方式字符串（格式如"001"表示1台低速小泵）
            运行方式 = ""
            # 大泵状态（最多2台）
            运行方式 += str(sum([循泵状态['1号大循泵'], 循泵状态['2号大循泵']]))
            # 高速小泵状态（最多2台）
            运行方式 += str(sum([循泵状态['3号小循泵高速'], 循泵状态['4号小循泵高速']]))
            # 低速小泵状态（最多2台）
            运行方式 += str(sum([循泵状态['3号小循泵低速'], 循泵状态['4号小循泵低速']]))

            try:
                pump_big_opt = int(pump_str[0])
                pump_high_opt = int(pump_str[1])
                pump_low_opt = int(pump_str[2])
                fan_opt = fan_opt
                power_cold, pump_power, fan_power = self.cal_power_cold(运行方式, self.snapshot['风机运行台数'])
                power_cold_opt, pump_power_opt, fan_power_opt = self.cal_power_cold(pump_str, fan_opt)

                bp_opt = self.smooth_bp(bp_opt, pump_opt, fan_opt)

                delta_power = self.cal_delta_power(bp_opt)
                delta_power_cold = power_cold_opt - power_cold

                power_inc = delta_power - delta_power_cold  # 净功率增加值
                power_inc = -power_inc if power_inc < 0 else power_inc
                # GJ/h / MJ/Nm3 = Nm3/h
                供热天然气流量 = self.snapshot["总供热量"] * 1000 / 35.5
                SystemLib.WriteAdvisory(f"{供热天然气流量 = } Nm3/h")
                实际气耗率 = (self.snapshot['燃机1天然气流量'] + self.snapshot['燃机2天然气流量'] - 供热天然气流量) / (
                        self.snapshot['燃机功率1'] + self.snapshot['燃机功率2'] + self.snapshot['汽轮发电机功率'])
                优化后气耗率 = (self.snapshot['燃机1天然气流量'] + self.snapshot[
                    '燃机2天然气流量'] - 供热天然气流量) / (
                                       self.snapshot['燃机功率1'] + self.snapshot['燃机功率2'] + self.snapshot[
                                   '汽轮发电机功率'] + power_inc / 1000)
                gas_dec = 实际气耗率 - 优化后气耗率

                PointLib.SetPoint(self.tags_res.get("pump_big_opt"), pump_big_opt)
                PointLib.SetPoint(self.tags_res.get("pump_high_opt"), pump_high_opt)
                PointLib.SetPoint(self.tags_res.get("pump_low_opt"), pump_low_opt)
                PointLib.SetPoint(self.tags_res.get("fan_opt"), fan_opt)
                PointLib.SetPoint(self.tags_res.get("power_cold"), power_cold)
                PointLib.SetPoint(self.tags_res.get("power_cold_opt"), power_cold_opt)
                PointLib.SetPoint(self.tags_res.get("power_cold_delta"), power_cold - power_cold_opt)
                PointLib.SetPoint(self.tags_res.get("gas_consume"), 实际气耗率)
                PointLib.SetPoint(self.tags_res.get("gas_consume_opt"), 优化后气耗率)
                PointLib.SetPoint(self.tags_res.get("gas_dec"), gas_dec)
                PointLib.SetPoint(self.tags_res.get("bp"), self.snapshot['当前理论背压'])
                PointLib.SetPoint(self.tags_res.get("bp_opt"), bp_opt)
                net_power = self.snapshot["汽轮发电机功率"] * 1000 - power_cold
                net_power_opt = net_power + power_inc
                power_st_opt = net_power_opt + power_cold_opt
                PointLib.SetPoint(self.tags_res.get("power_st_opt"), power_st_opt)
                PointLib.SetPoint(self.tags_res.get("power_st_delta"),
                                  power_st_opt - self.snapshot["汽轮发电机功率"] * 1000)
                PointLib.SetPoint(self.tags_res.get("power_inc"), power_inc)
                PointLib.SetPoint(self.tags_res.get("循泵实际功耗"), pump_power)
                PointLib.SetPoint(self.tags_res.get("风机实际功耗"), fan_power)
                PointLib.SetPoint(self.tags_res.get("循泵建议功耗"), pump_power_opt)
                PointLib.SetPoint(self.tags_res.get("风机建议功耗"), fan_power_opt)
                PointLib.SetPoint(self.tags_res.get("凝汽器热负荷"), self.snapshot['凝汽器热负荷(循环水计算)'] / 3600)
                t_sat = T_P(self.snapshot["背压"] / 1000)
                SystemLib.WriteAdvisory(f"{t_sat=}, {self.snapshot['凝泵出口凝结水温度']=}")
                PointLib.SetPoint(self.tags_res.get("凝汽器端差"), t_sat - (
                        self.snapshot['凝汽器循环水回水温度1'] + self.snapshot["凝汽器循环水回水温度2"]) / 2)
                PointLib.SetPoint(self.tags_res.get("凝汽器汽阻"), self.snapshot['凝汽器热负荷(循环水计算)'])
                PointLib.SetPoint(self.tags_res.get("循环水温升"), self.snapshot["循环水温升"])

                PointLib.SetPoint(self.tags_res.get("凝汽器过冷度"), t_sat - self.snapshot['凝泵出口凝结水温度'])
                mode = "抽凝模式"
                if self.snapshot["背压模式"] == 1:
                    mode = "背压模式"
                elif self.snapshot["纯凝模式"] == 1:
                    mode = "纯凝模式"
                else:
                    mode = "抽凝模式"
                SystemLib.WriteAdvisory(
                    f"{pump_big_opt=}, {pump_high_opt=}, {pump_low_opt=}, {fan_opt=}, bp={self.snapshot['背压']}"
                    f", {bp_opt=}, 工况是否稳定：{self.stability_monitor.is_stable()}, {mode}")

            except:
                SystemLib.WriteAdvisory(f"{traceback.format_exc()}")

        def cal_delta_power(self, bp):
            """
            计算背压对应的功率变化
            """
            bp_lilun = self.snapshot['当前理论背压']  # 当前运行方式的理论背压
            bp_delta = bp - bp_lilun  # 新运行方式下背压相对当前理论背压的变化值
            q_condense = self.snapshot['凝汽器热负荷'] / 3600  # 凝汽器热负荷，单位转换为MW
            if q_condense > 455:  # 根据当前的热负荷，计算背压变化对功率变化量的影响，参考冷端优化性能试验报告图3-图8
                q1 = 455
                delta_power1 = -0.025847 * bp_delta * bp_delta - 0.714627 * bp_delta - 0.017927
                q2 = 517
                delta_power2 = -0.033305 * bp_delta * bp_delta - 0.540825 * bp_delta
            elif q_condense > 410:
                q1 = 410
                delta_power1 = -0.039658 * bp_delta * bp_delta - 0.588991 * bp_delta - 0.014335
                q2 = 455
                delta_power2 = -0.025847 * bp_delta * bp_delta - 0.714627 * bp_delta - 0.017927
            elif q_condense > 371:
                q1 = 371
                delta_power1 = -0.056129 * bp_delta * bp_delta - 0.708775 * bp_delta - 0.000155
                q2 = 410
                delta_power2 = -0.039658 * bp_delta * bp_delta - 0.588991 * bp_delta - 0.014335
            elif q_condense > 257:
                q1 = 257
                delta_power1 = -0.064834 * bp_delta * bp_delta - 1.260514 * bp_delta - 0.010347
                q2 = 371
                delta_power2 = -0.056129 * bp_delta * bp_delta - 0.708775 * bp_delta - 0.000155
            else:
                q1 = 198
                delta_power1 = -0.014188 * bp_delta * bp_delta - 1.285405 * bp_delta + 0.012156
                q2 = 257
                delta_power2 = -0.064834 * bp_delta * bp_delta - 1.260514 * bp_delta - 0.010347

            delta_power_percent = interpolate_value_simple(q_condense, q1, delta_power1, q2, delta_power2)
            delta_power = delta_power_percent * self.snapshot['汽轮发电机功率']
            SystemLib.WriteAdvisory(f"背压变化：{bp_delta}，导致汽轮发电机功率变化：{delta_power}")

            return delta_power

        def cal_power_cold(self, pump_type, fan):
            """
            根据self.pump_power和self.fan_power中定义的数据，计算冷端设备功耗，包括风机和循泵功耗

            @param pump_type: 循泵类型编号（整数）或运行方式字符串（如"001"）
            @param fan: 风机运行数量（整数）
            @return: 冷端设备总功耗（千瓦）
            """
            # 获取循泵功耗
            if pump_type in self.pump_power:
                pump_power = self.pump_power[pump_type]
            else:
                # 如果pump_type是int，先转换为字符串
                if isinstance(pump_type, int):
                    # 在循泵运行方式映射中查找对应的字符串
                    pump_type_str = None
                    for key, value in self.循泵运行方式映射.items():
                        if value == pump_type:
                            pump_type_str = key
                            break

                    # 如果找到了对应的字符串，则使用该字符串
                    if pump_type_str is not None:
                        pump_type = pump_type_str
                    else:
                        # 如果没有找到，使用默认值
                        pump_power = 0
                else:
                    # pump_type已经是字符串，直接使用
                    pass

                # 如果pump_type是运行方式字符串，如"001"表示0台大泵、0台高速小泵、1台低速小泵
                if isinstance(pump_type, str) and len(pump_type) == 3:
                    big_pump_count = int(pump_type[0])  # 大泵数量
                    high_speed_pump_count = int(pump_type[1])  # 高速小泵数量
                    low_speed_pump_count = int(pump_type[2])  # 低速小泵数量

                    # 根据不同类型泵的数量和功率计算总功率
                    # 大泵功率为1904kW，高速泵功率为1380kW，低速泵功率为836.7kW
                    big_pump_power = big_pump_count * 1904
                    high_speed_pump_power = high_speed_pump_count * 1380
                    low_speed_pump_power = low_speed_pump_count * 836.7

                    pump_power = big_pump_power + high_speed_pump_power + low_speed_pump_power
                else:
                    pump_power = 0

            # 获取风机功耗
            fan_power = self.fan_power.get(fan, fan * 223)

            # 计算冷端设备总功耗
            power_cold = pump_power + fan_power

            return power_cold, pump_power, fan_power

        def smooth_bp(self, dest_bp, pump_opt, fan_opt):
            """
            dest_bp: 当前优化的目标背压值，为了防止最佳背压跳变，增加平滑函数
            """
            if self.stability_monitor.is_stable():  # 未定状态下，目标背压需要调整到和当前相匹配
                # 目标背压还会经过平滑出力，因此目标背压跳变不会影响背压曲线的平滑变化
                if self.snapshot['循泵运行方式数字编码'] == pump_opt and self.snapshot['风机运行台数'] == fan_opt:
                    SystemLib.WriteAdvisory(f"当前工况稳定，且建议运行方式等于实际运行方式")
                    if dest_bp - self.snapshot['背压'] > 0.4:
                        dest_bp = self.snapshot['背压'] + 0.4
                    elif dest_bp - self.snapshot['背压'] < -0.4:
                        dest_bp = self.snapshot['背压'] - 0.4
            self.bp_history.append(dest_bp)
            if len(self.bp_history) > self.bp_history_length:
                self.bp_history.pop(0)

            result = sum(self.bp_history) / len(self.bp_history)
            return result

        def cal_heat_load_condense(self):
            """
            基于单个时刻的snapshot数据计算凝汽器热负荷
            """
            # 计算平均大气压力（单位转换：kPa -> MPa）
            self.snapshot['平均大气压力'] = (self.snapshot['大气压力1'] + self.snapshot['大气压力2']) / 2000

            # 计算环境温度和大气湿度平均值
            self.snapshot['环境温度'] = (self.snapshot['#1燃机环境温度'] + self.snapshot['#2燃机环境温度']) / 2
            self.snapshot['大气湿度'] = (self.snapshot['#1燃机进气湿度'] + self.snapshot['#2燃机进气湿度']) / 2

            # 计算高压缸进汽压力绝压
            self.snapshot['高压缸进汽压力绝压'] = self.snapshot['高压缸进汽压力'] + self.snapshot['平均大气压力']

            # 计算高压缸进汽总流量
            self.snapshot['高压缸进汽总流量'] = self.snapshot['1号锅炉高压主汽流量'] + self.snapshot[
                '2号锅炉高压主汽流量']

            # 计算高压缸进汽焓值
            self.snapshot['高压缸进汽焓'] = H_PT(self.snapshot['高压缸进汽压力绝压'], self.snapshot['高压缸进汽温度'])

            # 计算高压缸进汽能量
            self.snapshot['高压缸进汽能量'] = self.snapshot['高压缸进汽总流量'] * self.snapshot['高压缸进汽焓']

            # 计算平均背压
            self.snapshot['背压'] = (self.snapshot['凝汽器真空1'] + self.snapshot['凝汽器真空2']) / 2
            if (self.snapshot['凝汽器真空1'] - self.snapshot['凝汽器真空2']) > 0.5:
                SystemLib.WriteAdvisory(f"两个凝汽器真空测点偏差过大，请检查测点是否正常！")

            # 计算凝结水绝对压力
            self.snapshot['凝结水绝对压力'] = self.snapshot['凝泵出口凝结水压力'] + self.snapshot['平均大气压力']

            # 计算平均凝结水焓
            self.snapshot['平均凝结水焓'] = H_PT(self.snapshot['凝结水绝对压力'], self.snapshot['凝泵出口凝结水温度'])

            # 计算再热蒸汽相关参数
            self.snapshot['再热蒸汽母管绝对压力'] = self.snapshot['再热蒸汽母管压力'] + self.snapshot['平均大气压力']
            self.snapshot['高排绝对压力'] = self.snapshot['高排压力'] + self.snapshot['平均大气压力']

            # 计算焓值
            self.snapshot['热再热蒸汽焓'] = H_PT(self.snapshot['再热蒸汽母管绝对压力'],
                                                 self.snapshot['再热蒸汽母管温度'])
            self.snapshot['高排焓'] = H_PT(self.snapshot['高排绝对压力'], self.snapshot['高排温度'])

            # 计算冷再热蒸汽流量
            self.snapshot['F_crh'] = self.snapshot['高排至1号炉流量'] + self.snapshot['高排至2号炉流量']

            # 计算冷再热蒸汽吸热量
            self.snapshot['冷再热蒸汽吸热量'] = self.snapshot['F_crh'] * (
                    self.snapshot['热再热蒸汽焓'] - self.snapshot['高排焓'])

            # 计算中压给水部分吸热量相关参数
            self.snapshot['F_is'] = (self.snapshot['1号炉再热蒸汽流量'] + self.snapshot['1号炉再热蒸汽流量'] -
                                     self.snapshot['F_crh'])
            self.snapshot['F_is*H_hrh'] = self.snapshot['F_is'] * self.snapshot['热再热蒸汽焓']

            # 计算低压部分吸热量
            self.snapshot['低压主蒸汽绝对压力混合前'] = (self.snapshot['低压主蒸汽压力(中排混合前)'] +
                                                         self.snapshot['平均大气压力'])
            self.snapshot['H_ls'] = H_PT(self.snapshot['低压主蒸汽绝对压力混合前'],
                                         self.snapshot['低压主蒸汽温度(中排混合前)'])
            # 更新F_ls计算方法 - 根据历史数据处理脚本中的新逻辑
            # 注意：由于实时系统中可能没有"纯凝模式"、"12号热网加热器疏水"和"34号热网加热器疏水"这些标签，
            # 所以如果获取不到这些点值，就使用原始计算方法作为后备方案
            纯凝模式 = self.snapshot["纯凝模式"]
            热网加热器1疏水 = self.snapshot["12号热网加热器疏水"]
            热网加热器2疏水 = self.snapshot["34号热网加热器疏水"]

            if 纯凝模式 is not None and 热网加热器1疏水 is not None and 热网加热器2疏水 is not None:
                if 纯凝模式 > 0.5:  # 大于0.5表示纯凝模式
                    self.snapshot['F_ls'] = self.snapshot['1号炉低过出口蒸汽流量'] + self.snapshot[
                        '2号炉低过出口蒸汽流量'] - 热网加热器1疏水 - 热网加热器2疏水
                else:
                    self.snapshot['F_ls'] = self.snapshot['1号炉低过出口蒸汽流量'] + self.snapshot[
                        '2号炉低过出口蒸汽流量']

            else:
                # 后备方案 - 使用原来的计算方法
                self.snapshot['F_ls'] = self.snapshot['1号炉低过出口蒸汽流量'] + self.snapshot['2号炉低过出口蒸汽流量']
                SystemLib.WriteAdvisory("无法获取热网相关测点，使用后备F_ls计算方法")

            self.snapshot['低压蒸汽能量'] = self.snapshot['F_ls'] * self.snapshot['H_ls']

            # 计算汽机轴功率当量热量
            self.snapshot['汽机轴功率当量热量'] = 3600 * (self.snapshot['汽轮发电机功率'] / 0.99 + 0.607)  # 单位为MJ/h
            self.snapshot['总供热量'] = self.snapshot['瞬时热网供水热量'] - self.snapshot['瞬时热网回水热量']
            self.snapshot['烟气供热器供热量'] = self.snapshot['1号炉烟气供热器供热量'] + self.snapshot[
                '2号炉烟气供热器供热量']
            self.snapshot['汽机抽汽供热量'] = self.snapshot['总供热量'] - self.snapshot['烟气供热器供热量']
            # 计算低压缸排汽能量
            if 纯凝模式 is not None:
                if 纯凝模式 > 0.5:  # 纯凝模式
                    self.snapshot['低压缸排汽能量'] = (
                            self.snapshot['高压缸进汽能量'] + self.snapshot['冷再热蒸汽吸热量'] +
                            self.snapshot['F_is*H_hrh'] + self.snapshot['低压蒸汽能量'] -
                            self.snapshot['汽机轴功率当量热量'])
                else:  # 抽凝模式
                    self.snapshot['低压缸排汽能量'] = (
                            self.snapshot['高压缸进汽能量'] + self.snapshot['冷再热蒸汽吸热量'] +
                            self.snapshot['F_is*H_hrh'] + self.snapshot['低压蒸汽能量'] -
                            self.snapshot['汽机抽汽供热量'] * 1000 - self.snapshot[
                                '汽机轴功率当量热量'])
            else:
                # 后备方案 - 使用原来的计算方法（默认减去汽机抽汽供热量）
                self.snapshot['低压缸排汽能量'] = (self.snapshot['高压缸进汽能量'] + self.snapshot['冷再热蒸汽吸热量'] +
                                                   self.snapshot['F_is*H_hrh'] + self.snapshot['低压蒸汽能量'] -
                                                   self.snapshot['汽机抽汽供热量'] * 1000 - self.snapshot[
                                                       '汽机轴功率当量热量'])
                SystemLib.WriteAdvisory("无法获取纯凝模式状态，使用后备低压缸排汽能量计算方法")

            # 计算补水焓
            self.snapshot['补水焓'] = H_PT(self.snapshot['凝结水补水压力'], self.snapshot['凝结水补水温度'])

            # 计算总凝结水流量
            self.snapshot['F_con'] = (self.snapshot['高压缸进汽总流量'] + self.snapshot['F_is'] +
                                      self.snapshot['F_ls'] + self.snapshot['凝结水补水流量'])

            # 最终计算凝汽器热负荷
            self.snapshot['凝汽器热负荷'] = (self.snapshot['低压缸排汽能量'] +
                                             self.snapshot['补水焓'] * self.snapshot['凝结水补水流量'] -
                                             self.snapshot['F_con'] * self.snapshot['平均凝结水焓'])
            self.snapshot["循环水温升"] = (self.snapshot["凝汽器回水温度1"] - self.snapshot["凝汽器进水温度1"] +
                                           self.snapshot["凝汽器回水温度2"] - self.snapshot["凝汽器进水温度2"]) / 2
            self.snapshot['循环水吸热量1'] = 4.2 * self.snapshot["汽机循环水供水管道流量1"] * (
                    self.snapshot["凝汽器回水温度1"] - self.snapshot["凝汽器进水温度1"])
            self.snapshot['循环水吸热量2'] = 4.2 * self.snapshot["汽机循环水供水管道流量2"] * (
                    self.snapshot["凝汽器回水温度2"] - self.snapshot["凝汽器进水温度2"])
            self.snapshot['凝汽器热负荷(循环水计算)'] = self.snapshot["循环水吸热量1"] + self.snapshot["循环水吸热量2"]
            # 计算风机运行台数（根据机力塔风机电流判断，假设电流>1A为运行）
            风机电流列 = [
                '机力塔风机1电流', '机力塔风机2电流', '机力塔风机3电流', '机力塔风机4电流',
                '机力塔风机5电流', '机力塔风机6电流', '机力塔风机7电流', '机力塔风机8电流'
            ]
            self.snapshot['风机运行台数'] = sum(1 for col in 风机电流列 if self.snapshot[col] > 1)

            # 计算循泵运行方式数字编码
            循泵状态 = {
                '1号大循泵': self.snapshot['1号大循泵电流（额定298A）'] > 1,
                '2号大循泵': self.snapshot['2号大循泵电流（额定298A）'] > 1,
                '3号小循泵高速': self.snapshot['3号小循泵电流（高速）'] > 1,
                '3号小循泵低速': self.snapshot['3号小循泵电流（低速）'] > 1,
                '4号小循泵高速': self.snapshot['4号小循泵电流（高速）'] > 1,
                '4号小循泵低速': self.snapshot['4号小循泵电流（低速）'] > 1
            }

            # 根据循泵运行状态确定运行方式编码
            # 构造运行方式字符串（格式如"001"表示1台低速小泵）
            运行方式 = ""
            # 大泵状态（最多2台）
            运行方式 += str(sum([循泵状态['1号大循泵'], 循泵状态['2号大循泵']]))
            # 高速小泵状态（最多2台）
            运行方式 += str(sum([循泵状态['3号小循泵高速'], 循泵状态['4号小循泵高速']]))
            # 低速小泵状态（最多2台）
            运行方式 += str(sum([循泵状态['3号小循泵低速'], 循泵状态['4号小循泵低速']]))
            # 根据运行方式字符串查找对应的数字编码
            self.snapshot['循泵运行方式数字编码'] = self.循泵运行方式映射.get(运行方式, 0)

            self.snapshot['冷却塔进水蝶阀开启数量'] = (
                    self.snapshot['1号冷却塔进水蝶阀'] + self.snapshot['2号冷却塔进水蝶阀']
                    + self.snapshot['3号冷却塔进水蝶阀'] + self.snapshot['4号冷却塔进水蝶阀']
                    + self.snapshot['4号冷却塔进水蝶阀'] + self.snapshot['6号冷却塔进水蝶阀']
                    + self.snapshot['7号冷却塔进水蝶阀'] + self.snapshot['8号冷却塔进水蝶阀']
            )

            self.snapshot['循环水流量'] = self.snapshot['汽机循环水供水管道流量1'] + self.snapshot[
                '汽机循环水供水管道流量2']

            self.snapshot['气耗率'] = (self.snapshot['燃机1天然气流量'] + self.snapshot['燃机2天然气流量']) / (
                    self.snapshot['燃机功率1'] + self.snapshot['燃机功率2'] + self.snapshot['汽轮发电机功率'])
            # 返回计算结果

            self.stability_monitor.add_data_point(
                {
                    '汽轮发电机功率': self.snapshot['汽轮发电机功率'],
                    '热负荷': self.snapshot[heat_para],
                    '环境温度': self.snapshot['环境温度'],
                    '循泵运行方式数字编码': self.snapshot['循泵运行方式数字编码'],
                    '风机运行台数': self.snapshot['风机运行台数']
                }
            )
            return [self.snapshot[col] for col in self.input_columns]


if __name__ == "__main__":
    cal = main_OIF.Calculation()
    cal.Execute()
