package com.xzzn.quartz.task;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.xzzn.common.constant.RedisKeyConstants;
import com.xzzn.common.core.modbus.ModbusProcessor;
import com.xzzn.common.core.modbus.domain.DeviceConfig;
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.BusinessStatus;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.common.enums.ChargeStatus;
import com.xzzn.common.enums.DeviceCategory;
import com.xzzn.common.enums.OperatorType;
import com.xzzn.common.enums.SiteDevice;
import com.xzzn.common.enums.SocLimit;
import com.xzzn.common.enums.WorkStatus;
import com.xzzn.common.utils.DateUtils;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsAmmeterData;
import com.xzzn.ems.domain.EmsBatteryStack;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.domain.EmsPcsSetting;
import com.xzzn.ems.domain.EmsStrategyRuntimeConfig;
import com.xzzn.ems.domain.EmsStrategyLog;
import com.xzzn.ems.domain.EmsStrategyTemp;
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
import com.xzzn.ems.domain.vo.StrategyRunningVo;
import com.xzzn.ems.mapper.EmsAmmeterDataMapper;
import com.xzzn.ems.mapper.EmsBatteryStackMapper;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.mapper.EmsPcsSettingMapper;
import com.xzzn.ems.mapper.EmsStrategyLogMapper;
import com.xzzn.ems.mapper.EmsStrategyRuntimeConfigMapper;
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
import com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper;
import com.xzzn.system.domain.SysOperLog;
import com.xzzn.system.service.ISysOperLogService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("strategyPoller")
public class StrategyPoller {
    private static final Logger logger = LoggerFactory.getLogger(StrategyPoller.class);

    private static final ConcurrentHashMap<Long, Boolean> strategyLocks = new ConcurrentHashMap<>();

    // SOC 上下限值，默认为0%-100%
    private static final BigDecimal DEFAULT_SOC_DOWN = BigDecimal.ZERO;
    private static final BigDecimal DEFAULT_SOC_UP = new BigDecimal(100);
    // 逆变器功率下限值，默认为30kW
    private static final BigDecimal DEFAULT_ANTI_REVERSE_THRESHOLD = new BigDecimal(30);
    // 逆变器下限值范围，默认为20%
    private static final BigDecimal DEFAULT_ANTI_REVERSE_RANGE_PERCENT = new BigDecimal(20);
    // 逆变器功率上限值，默认为100kW
    private static final BigDecimal DEFAULT_ANTI_REVERSE_UP = new BigDecimal(100);
    // PCS功率降幅，默认为10%
    private static final BigDecimal DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal(10);
    // 电网有功功率低于20kW时，强制待机
    private static final BigDecimal DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD = new BigDecimal(20);
    // 设定功率倍率，默认10
    private static final BigDecimal DEFAULT_POWER_SET_MULTIPLIER = new BigDecimal(10);
    // 保护介入默认开启
    private static final Integer DEFAULT_PROTECT_INTERVENE_ENABLE = 1;
    // 一级保护默认降额50%
    private static final BigDecimal DEFAULT_PROTECT_L1_DERATE_PERCENT = new BigDecimal("50");
    // 保护约束失效保护时长（秒）
    private static final Integer DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS = 5;
    // 三级保护默认锁存开启
    private static final Integer DEFAULT_PROTECT_L3_LATCH_ENABLE = 1;
    // 保护冲突策略默认值
    private static final String DEFAULT_PROTECT_CONFLICT_POLICY = "MAX_LEVEL_WIN";
    // 保护约束默认功率比例
    private static final BigDecimal DEFAULT_PROTECTION_RATIO = BigDecimal.ONE;
    // 除法精度，避免BigDecimal除不尽异常
    private static final int POWER_SCALE = 4;

    @Autowired
    private EmsStrategyRunningMapper emsStrategyRunningMapper;
    @Autowired
    private EmsStrategyTempMapper emsStrategyTempMapper;
    @Autowired
    private EmsStrategyTimeConfigMapper emsStrategyTimeConfigMapper;
    @Autowired
    private EmsBatteryStackMapper emsBatteryStackMapper;
    @Autowired
    private EmsDevicesSettingMapper emsDevicesMapper;
    @Autowired
    private EmsPcsSettingMapper emsPcsSettingMapper;
    @Autowired
    private EmsAmmeterDataMapper emsAmmeterDataMapper;
    @Autowired
    private EmsStrategyLogMapper emsStrategyLogMapper;
    @Autowired
    private EmsStrategyRuntimeConfigMapper runtimeConfigMapper;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ModbusProcessor modbusProcessor;
    @Autowired
    private ISysOperLogService operLogService;

    @Resource(name = "modbusExecutor")
    private ExecutorService modbusExecutor;

    public void pollAllDevices() {
        logger.info("开始执行运行策略数据轮询...");
        List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getPendingPollerStrategy(null);
        strategyRunningVoList.forEach(strategyVo -> {
            Long strategyId = strategyVo.getId();
            if (strategyLocks.putIfAbsent(strategyId, true) == null) {
                // 使用共享的modbusExecutor串行执行，避免与ModbusPoller并发访问导致通讯故障
                modbusExecutor.submit(() -> {
                    try {
                        processData(strategyVo);
                    } catch (Exception e) {
                        logger.error("运行策略{}轮询异常", strategyVo.getId(), e);
                    } finally {
                        logger.info("运行策略{}轮询任务执行完成，释放锁", strategyVo.getId());
                        strategyLocks.remove(strategyId);
                    }
                });
            } else {
                logger.info("策略{}已在处理中，跳过重复执行", strategyId);
            }
        });
    }
    // 处理获取到的运行策略数据，modbus发送指定的命令控制设备
    private void processData(StrategyRunningVo strategyVo) {
        logger.info("运行策略数据处理开始");
        // 根据运行策略获取主副策略的模板数据
        Long mainStrategyId = strategyVo.getMainStrategyId();
        Long auxStrategyId = strategyVo.getAuxStrategyId();
        String siteId = strategyVo.getSiteId();
        // 处理主策略数据
        if (mainStrategyId != null && StringUtils.isNotBlank(siteId)) {
            dealStrategyCurveData(mainStrategyId, siteId);
        }
        // 处理副策略数据
        if (auxStrategyId != null && StringUtils.isNotBlank(siteId)) {
            dealStrategyCurveData(auxStrategyId, siteId);
        }

        logger.info("运行策略轮询处理结束");
    }

    private void dealStrategyCurveData(Long strategyId, String siteId) {
        EmsStrategyRuntimeConfig runtimeConfig = getRuntimeConfig(siteId);
        // 1.获取当前策略的所有模板
        List<Map<String, String>> temps = emsStrategyTempMapper.getTempNameList(strategyId, siteId);
        if (CollectionUtils.isEmpty(temps)) {
            logger.info("当前站点: {}, 策略: {} 没有模板数据", siteId, strategyId);
            return;
        }
        for (Map<String, String> temp : temps) {
            // 2.查询当月配置的运行策略
            String tempId = temp.get("templateId");
            int month = LocalDateTime.now().getMonthValue();
            List<EmsStrategyTimeConfig> timeConfigs = emsStrategyTimeConfigMapper.getTimeConfigByTempIdAndMonth(tempId, month);
            if (CollectionUtils.isEmpty(timeConfigs)) {
                continue;
            }
            logger.info("当前站点: {}, 策略: {}, {}月配置模版：{}", siteId, strategyId, month, tempId);
            // 3.查询当月配置的运行策略时间阶段数据
            List<EmsStrategyTemp> powerConfig = emsStrategyTempMapper.selectStrategyTempByTempId(tempId);
            if (CollectionUtils.isEmpty(powerConfig)) {
                logger.info("当前站点: {}, 策略: {}, 模版：{} 未配置数据", siteId, strategyId, tempId);
                continue;
            }
            // 4.遍历时间段数据，判断当前时间是否在时间段内，在时间段内的进行处理
            for (EmsStrategyTemp emsStrategyTemp : powerConfig) {
                if (emsStrategyTemp.getStartTime() == null || emsStrategyTemp.getEndTime() == null) {
                    logger.info("当前站点: {}, 策略: {}, 模版：{} 未配置时间阶段数据", siteId, strategyId, tempId);
                    continue;
                }
                // 判断当前时间是否在时间段内
                if (!isTimeInRange(LocalTime.now(), emsStrategyTemp.getStartTime(), emsStrategyTemp.getEndTime())) {
                   logger.info("当前站点: {}, 策略: {}, 时间段：{} 不在时间段内", siteId, strategyId, emsStrategyTemp.getStartTime() + "-" + emsStrategyTemp.getEndTime());
                    continue;
                }
                // 查询PCS设备信息
                EmsDevicesSetting queryDevices = new EmsDevicesSetting();
                queryDevices.setSiteId(siteId);
                queryDevices.setDeviceCategory(DeviceCategory.PCS.getCode());
                List<EmsDevicesSetting> pcsDeviceList = emsDevicesMapper.selectEmsDevicesSettingList(queryDevices);
                if (CollectionUtils.isEmpty(pcsDeviceList)) {
                    logger.info("当前站点: {} 未配置PCS设备", siteId);
                    continue;
                }
                // 判断SOC上下限
                if (isSocInRange(emsStrategyTemp, runtimeConfig)) {
                    ProtectionConstraintVo protectionConstraint = getProtectionConstraint(siteId);
                    Map<Long, EmsPcsSetting> pcsSettingCache = new HashMap<>();
                    BigDecimal avgChargeDischargePower = emsStrategyTemp.getChargeDischargePower()
                            .divide(new BigDecimal(pcsDeviceList.size()), POWER_SCALE, RoundingMode.HALF_UP);
                    BigDecimal totalActivePower = null;
                    if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
                        // 同一站点同一轮执行只读取一次电网电表，降低重复查库和数据抖动
                        EmsAmmeterData emsAmmeterData = emsAmmeterDataMapper.getLastData(emsStrategyTemp.getSiteId(), SiteDevice.LOAD.name());
                        if (emsAmmeterData != null) {
                            totalActivePower = emsAmmeterData.getTotalActivePower();
                        }
                    }
                    for (EmsDevicesSetting pcsDevice : pcsDeviceList) {
                        EmsPcsSetting pcsSetting = pcsSettingCache.computeIfAbsent(
                                pcsDevice.getId(),
                                id -> emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(id)
                        );
                        if (pcsSetting == null || pcsSetting.getClusterNum() < 1) {
                            logger.info("当前站点: {}, PCS设备: {} 未获取电池簇数量", siteId, pcsDevice.getDeviceId());
                            continue;
                        }
                        // 平均功率值根据倍率放大后，再按电池簇数量平均分配
                        BigDecimal strategyPower = avgChargeDischargePower.multiply(runtimeConfig.getPowerSetMultiplier())
                                .divide(new BigDecimal(pcsSetting.getClusterNum()), POWER_SCALE, RoundingMode.HALF_UP);
                        // 根据充电状态，处理数据
                        if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
                            StrategyCommandDecision decision = applyProtectionConstraint(
                                    strategyPower,
                                    ChargeStatus.CHARGING,
                                    runtimeConfig,
                                    protectionConstraint
                            );
                            // 发送Modbus命令控制设备-充电
                            sendModbusCommand(
                                    Collections.singletonList(pcsDevice),
                                    pcsSetting,
                                    decision.getChargeStatus(),
                                    decision.getPower(),
                                    emsStrategyTemp,
                                    false,
                                    null
                            );
                        } else if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
                            boolean needAntiReverseFlow = false;
                            Integer powerDownType = null;
                            BigDecimal chargeDischargePower = strategyPower;
                            // 查询策略运行日志
                            EmsStrategyLog lastStrategyLog = getLastStrategyLog(pcsDevice.getDeviceId(), emsStrategyTemp);
                            if (lastStrategyLog != null) {
                                // 如果当前时间段已经进入待机状态，则不处理
                                if (ChargeStatus.STANDBY.getCode().equals(lastStrategyLog.getChargeStatus())) {
                                    continue;
                                }
                                chargeDischargePower = lastStrategyLog.getChargeDischargePower();
                                powerDownType = lastStrategyLog.getPowerDownType();
                            }

                            // 查询电网电表的正向有功功率,36kW-50kW范围内，稳定运行，低于36kW，降功率，高于50kW，增加功率
                            if (totalActivePower == null) {
                                logger.warn("当前站点: {}, 未获取到最新电表数据，执行保守策略并切换待机", emsStrategyTemp.getSiteId());
                                sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, true, 0);
                                continue;
                            } else {
                                // 电网功率过低，直接待机，不再放电
                                if (totalActivePower.compareTo(runtimeConfig.getAntiReverseHardStopThreshold()) < 0) {
                                    sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, true, 0);
                                    continue;
                                }
                                // 放电开始先按差值限幅：差值=电网功率-防逆流阈值
                                BigDecimal diffPower = totalActivePower.subtract(runtimeConfig.getAntiReverseThreshold());
                                BigDecimal targetPower = diffPower.compareTo(BigDecimal.ZERO) > 0 ? diffPower : BigDecimal.ZERO;
                                if (targetPower.compareTo(strategyPower) > 0) {
                                    targetPower = strategyPower;
                                }
                                if (chargeDischargePower.compareTo(targetPower) > 0) {
                                    chargeDischargePower = targetPower;
                                }
                                // 判断是否需要防逆流
                                needAntiReverseFlow = isNeedAntiReverseFlow(totalActivePower, runtimeConfig);
                                BigDecimal power = strategyPower.multiply(runtimeConfig.getAntiReversePowerDownPercent())
                                        .divide(new BigDecimal(100), POWER_SCALE, RoundingMode.HALF_UP);
                                if (needAntiReverseFlow) {
                                    // 降功率
                                    chargeDischargePower = chargeDischargePower.subtract(power);
                                    powerDownType = 0;
                                } else {
                                    // 判断是否需要增加功率,
                                    if (powerDownType != null && totalActivePower.compareTo(runtimeConfig.getAntiReverseUp()) > 0) {
                                        if (chargeDischargePower.compareTo(targetPower) >= 0) {
                                            // 功率增加到限幅值则停止
                                            continue;
                                        }
                                        // 增加功率
                                        chargeDischargePower = chargeDischargePower.add(power);
                                        if (chargeDischargePower.compareTo(targetPower) > 0) {
                                            chargeDischargePower = targetPower;
                                        }
                                        powerDownType = 1;
                                        needAntiReverseFlow = true;
                                    }
                                }
                            }

                            if (chargeDischargePower.compareTo(BigDecimal.ZERO) < 0) {
                                chargeDischargePower = BigDecimal.ZERO;
                            }
                            StrategyCommandDecision decision = applyProtectionConstraint(
                                    chargeDischargePower,
                                    ChargeStatus.DISCHARGING,
                                    runtimeConfig,
                                    protectionConstraint
                            );
                            ChargeStatus finalStatus = decision.getChargeStatus();
                            BigDecimal finalPower = decision.getPower();
                            if (ChargeStatus.STANDBY.equals(finalStatus) || BigDecimal.ZERO.compareTo(finalPower) == 0) {
                                // 如果已经降功率到0，则设备直接待机
                                // 发送Modbus命令控制设备-待机
                                sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, needAntiReverseFlow, powerDownType);
                            } else {
                                // 发送Modbus命令控制设备-放电
                                sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, finalStatus, finalPower, emsStrategyTemp, needAntiReverseFlow, powerDownType);
                            }
                        } else {
                            // 发送Modbus命令控制设备-待机
                            sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, false, null);
                        }
                    }
                } else {
                    // 发送Modbus命令控制设备-待机
                    sendModbusCommand(pcsDeviceList, null, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, false, null);
                }
            }

        }
    }


    private void saveStrategyLog(String deviceId, BigDecimal chargeDischargePower, String chargeStatus,
                                 EmsStrategyTemp strategyTemp, boolean needAntiReverseFlow, Integer powerDownType) {
        EmsStrategyLog log = new EmsStrategyLog();
        log.setStrategyId(strategyTemp.getStrategyId());
        log.setTemplateId(strategyTemp.getTemplateId());
        log.setSiteId(strategyTemp.getSiteId());
        log.setDeviceId(deviceId);
        log.setStartTime(strategyTemp.getStartTime());
        log.setEndTime(strategyTemp.getEndTime());
        log.setChargeDischargePower(chargeDischargePower);
        log.setChargeStatus(chargeStatus);
        log.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
        log.setAntiReverse(needAntiReverseFlow ? 1 : 0);
        log.setPowerDownType(powerDownType);
        emsStrategyLogMapper.insertEmsStrategyLog(log);
    }
    
    private List<EmsStrategyLog> getStrategyLog(String deviceId, String chargeStatus,
                                                EmsStrategyTemp strategyTemp, boolean needAntiReverseFlow) {
        EmsStrategyLog query = new EmsStrategyLog();
        query.setStrategyId(strategyTemp.getStrategyId());
        query.setTemplateId(strategyTemp.getTemplateId());
        query.setSiteId(strategyTemp.getSiteId());
        query.setDeviceId(deviceId);
        query.setStartTime(strategyTemp.getStartTime());
        query.setEndTime(strategyTemp.getEndTime());
        query.setChargeStatus(chargeStatus);
        query.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
        query.setAntiReverse(needAntiReverseFlow ? 1 : 0);
        return emsStrategyLogMapper.selectEmsStrategyLogList(query);
    }

    private EmsStrategyLog getLastStrategyLog(String deviceId, EmsStrategyTemp strategyTemp) {
        EmsStrategyLog query = new EmsStrategyLog();
        query.setStrategyId(strategyTemp.getStrategyId());
        query.setTemplateId(strategyTemp.getTemplateId());
        query.setSiteId(strategyTemp.getSiteId());
        query.setDeviceId(deviceId);
        query.setStartTime(strategyTemp.getStartTime());
        query.setEndTime(strategyTemp.getEndTime());
        query.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
        return emsStrategyLogMapper.getLastStrategyLog(query);
    }

    private boolean isNeedAntiReverseFlow(BigDecimal totalActivePower, EmsStrategyRuntimeConfig runtimeConfig) {
        // 获取当前设定的防逆流阈值(30kW)
        BigDecimal threshold = runtimeConfig.getAntiReverseThreshold();
        // 计算20%范围的上限(36kW)
        BigDecimal upperLimit = threshold.multiply(runtimeConfig.getAntiReverseRangePercent()).divide(new BigDecimal(100)).add(threshold);

        // 判断电网电表正向有功功率是否小于36kW(接近30kW的20%范围)
        return totalActivePower.compareTo(upperLimit) < 0;
    }

    public List<WriteTagConfig> getSwitchDeviceWriteTags(EmsPcsSetting pcsSetting, String workStatus) {
        List<WriteTagConfig> writeTags = new ArrayList<>();
        BigDecimal power;

        if (WorkStatus.NORMAL.getCode().equals(workStatus)) {
            // 开机先发送开机指令再发送有功功率给定值
            WriteTagConfig writeTag = new WriteTagConfig();
            writeTag.setAddress(pcsSetting.getPointAddress());
            writeTag.setValue(pcsSetting.getStartCommand());
            writeTags.add(writeTag);

            power = pcsSetting.getStartPower();
        } else {
            // 关机
            power = pcsSetting.getStopPower();
        }

        JSONArray array = JSON.parseArray(pcsSetting.getClusterPointAddress());
        for (int i = 0; i < pcsSetting.getClusterNum(); i++) {
            Object clusterPointAddress = array.get(i);
            WriteTagConfig clusterWriteTag = new WriteTagConfig();
            clusterWriteTag.setAddress(String.valueOf(clusterPointAddress));
            // 电池簇PCS有功功率给定默认置0
            if (power == null) {
                power = BigDecimal.ZERO;
            }
            clusterWriteTag.setValue(power);
            writeTags.add(clusterWriteTag);
        }
        if (WorkStatus.STOP.getCode().equals(workStatus)) {
            // 关机先发送有功功率给定值再发送关机指令
            WriteTagConfig writeTag = new WriteTagConfig();
            writeTag.setAddress(pcsSetting.getPointAddress());
            writeTag.setValue(pcsSetting.getStopCommand());
            writeTags.add(writeTag);
        }
        return writeTags;
    }

    public List<WriteTagConfig> getWriteTags(EmsPcsSetting pcsSetting, BigDecimal chargeDischargePower) {
        List<WriteTagConfig> writeTags = new ArrayList<>();
        JSONArray array = JSON.parseArray(pcsSetting.getClusterPointAddress());
        for (int i = 0; i < pcsSetting.getClusterNum(); i++) {
            Object clusterPointAddress = array.get(i);
            WriteTagConfig clusterWriteTag = new WriteTagConfig();
            clusterWriteTag.setAddress(String.valueOf(clusterPointAddress));
            clusterWriteTag.setValue(chargeDischargePower);
            writeTags.add(clusterWriteTag);
        }
        return writeTags;
    }

    public DeviceConfig getDeviceConfig(String siteId, String deviceId, EmsDevicesSetting device, EmsPcsSetting pcsSetting, BigDecimal chargeDischargePower, int writeType) {
        if (Objects.isNull(pcsSetting)) {
            pcsSetting = emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(device.getId());
            if (pcsSetting == null) {
                logger.info("当前站点: {}, PCS设备: {} 未找到对应PCS配置", siteId, deviceId);
                return null;
            }
        }
        if (device.getIpPort() == null || device.getSlaveId() == null) {
            logger.info("当前站点: {}, PCS设备: {} 未配置IP端口或从站号", siteId, deviceId);
            return null;
        }
        DeviceConfig deviceConfig = new DeviceConfig();
        deviceConfig.setDeviceNumber(device.getDeviceId());
        deviceConfig.setDeviceName(device.getDeviceName());
        deviceConfig.setSlaveId(device.getSlaveId().intValue());
        deviceConfig.setHost(device.getIpAddress());
        deviceConfig.setPort(device.getIpPort().intValue());
        deviceConfig.setWriteTags(writeType == 0 ? getWriteTags(pcsSetting, chargeDischargePower) : getSwitchDeviceWriteTags(pcsSetting, device.getWorkStatus()));
        return deviceConfig;
    }

    private void sendModbusCommand(List<EmsDevicesSetting> pcsDeviceList, EmsPcsSetting pcsSetting, ChargeStatus chargeStatus, BigDecimal chargeDischargePower,
                                   EmsStrategyTemp emsStrategyTemp, boolean needAntiReverseFlow, Integer powerDownType) {
        for (EmsDevicesSetting pcsDevice : pcsDeviceList) {
            String siteId = pcsDevice.getSiteId();
            String deviceId = pcsDevice.getDeviceId();
            List<EmsStrategyLog> strategyLogList = getStrategyLog(deviceId, chargeStatus.getCode(), emsStrategyTemp, needAntiReverseFlow);
            if (CollectionUtils.isNotEmpty(strategyLogList)) {
                boolean isExist = true;
                if (ChargeStatus.DISCHARGING.equals(chargeStatus) && needAntiReverseFlow) {
                    isExist = false;
                }
                if (isExist) {
                    logger.info("当前站点: {}, PCS设备: {} 当前时间段已存在策略执行记录，不再重复执行", siteId, deviceId);
                    continue;
                }
            }

            // 每次操作先判断设备工作状态
            if (StringUtils.isEmpty(pcsDevice.getWorkStatus()) || WorkStatus.ABNORMAL.getCode().equals(pcsDevice.getWorkStatus())) {
                // 设备故障，不发送指令
                continue;
            } else if (WorkStatus.STOP.getCode().equals(pcsDevice.getWorkStatus())) {
                // 设备停机
                if (ChargeStatus.STANDBY.equals(chargeStatus)) {
                    // 待机,则不写入功率值
                    continue;
                } else {
                    // 充、放电,则先开机设备
                    if (!switchDevice(pcsDevice, pcsSetting, WorkStatus.NORMAL)) {
                        continue;
                    }
                }
            }

            DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting, chargeDischargePower, 0);
            if (deviceConfig == null) {
                continue;
            }
            boolean result = modbusProcessor.writeDataToDeviceWithRetry(deviceConfig);
            if (!result) {
                logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, chargeStatus.getInfo());
                recordDeviceOperationLog(siteId, deviceId, "写功率", chargeDischargePower, false, chargeStatus.getInfo() + "功率下发失败");
                continue;
            } else {
                recordDeviceOperationLog(siteId, deviceId, "写功率", chargeDischargePower, true, null);
                if (ChargeStatus.STANDBY.equals(chargeStatus)) {
                    // 待机，先写功率值，再关机
                    if (!switchDevice(pcsDevice, pcsSetting, WorkStatus.STOP)) {
                        logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, WorkStatus.STOP.getInfo());
                        continue;
                    }
                }
            }
            // 记录策略执行日志
            saveStrategyLog(deviceId, chargeDischargePower, chargeStatus.getCode(), emsStrategyTemp, needAntiReverseFlow, powerDownType);
        }
    }

    //设备开关机
    private boolean switchDevice(EmsDevicesSetting pcsDevice, EmsPcsSetting pcsSetting, WorkStatus workStatus) {
        String siteId = pcsDevice.getSiteId();
        String deviceId = pcsDevice.getDeviceId();
        String originalWorkStatus = pcsDevice.getWorkStatus();
        pcsDevice.setWorkStatus(workStatus.getCode());
        DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting , null, 1);
        if (deviceConfig == null) {
            pcsDevice.setWorkStatus(originalWorkStatus);
            return false;
        }
        boolean result = modbusProcessor.writeDataToDeviceWithRetry(deviceConfig);
        if (!result) {
            pcsDevice.setWorkStatus(originalWorkStatus);
            logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, workStatus.getInfo());
            recordDeviceOperationLog(siteId, deviceId, "开关机", workStatus.getInfo(), false, workStatus.getInfo() + "指令发送失败");
        } else {
            recordDeviceOperationLog(siteId, deviceId, "开关机", workStatus.getInfo(), true, null);
        }
        return result;
    }

    private void recordDeviceOperationLog(String siteId, String deviceId, String action, Object param, boolean success, String errorMsg) {
        try {
            SysOperLog operLog = new SysOperLog();
            operLog.setTitle("策略设备控制-" + action);
            operLog.setBusinessType(BusinessType.UPDATE.ordinal());
            operLog.setMethod(this.getClass().getName() + "." + action);
            operLog.setRequestMethod("SCHEDULE");
            operLog.setOperatorType(OperatorType.OTHER.ordinal());
            operLog.setOperName("system");
            operLog.setOperIp("127.0.0.1");
            operLog.setOperUrl("/quartz/strategyPoller");
            operLog.setOperTime(DateUtils.getNowDate());
            Map<String, Object> operParam = new HashMap<>();
            operParam.put("siteId", siteId);
            operParam.put("deviceId", deviceId);
            operParam.put("action", action);
            operParam.put("param", param);
            operLog.setOperParam(StringUtils.substring(JSON.toJSONString(operParam), 0, 2000));
            operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(Collections.singletonMap("success", success)), 0, 2000));
            operLog.setStatus(success ? BusinessStatus.SUCCESS.ordinal() : BusinessStatus.FAIL.ordinal());
            if (!success) {
                operLog.setErrorMsg(StringUtils.substring(errorMsg, 0, 2000));
            }
            operLogService.insertOperlog(operLog);
        } catch (Exception e) {
            logger.error("记录sys_oper_log失败, siteId={}, deviceId={}, action={}", siteId, deviceId, action, e);
        }
    }

    private ProtectionConstraintVo getProtectionConstraint(String siteId) {
        ProtectionConstraintVo constraint = redisCache.getCacheObject(RedisKeyConstants.PROTECTION_CONSTRAINT + siteId);
        if (constraint == null) {
            return ProtectionConstraintVo.empty();
        }
        if (constraint.getPowerLimitRatio() == null) {
            constraint.setPowerLimitRatio(DEFAULT_PROTECTION_RATIO);
        }
        if (constraint.getAllowCharge() == null) {
            constraint.setAllowCharge(true);
        }
        if (constraint.getAllowDischarge() == null) {
            constraint.setAllowDischarge(true);
        }
        if (constraint.getForceStandby() == null) {
            constraint.setForceStandby(false);
        }
        if (constraint.getForceStop() == null) {
            constraint.setForceStop(false);
        }
        if (constraint.getLevel() == null) {
            constraint.setLevel(0);
        }
        return constraint;
    }

    private StrategyCommandDecision applyProtectionConstraint(BigDecimal targetPower,
                                                              ChargeStatus targetStatus,
                                                              EmsStrategyRuntimeConfig runtimeConfig,
                                                              ProtectionConstraintVo constraint) {
        if (constraint == null || nullSafeInt(constraint.getLevel()) <= 0) {
            return new StrategyCommandDecision(targetStatus, safePower(targetPower));
        }
        if (Boolean.TRUE.equals(constraint.getForceStop()) || Boolean.TRUE.equals(constraint.getForceStandby())) {
            return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
        }
        if (ChargeStatus.CHARGING.equals(targetStatus) && Boolean.FALSE.equals(constraint.getAllowCharge())) {
            return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
        }
        if (ChargeStatus.DISCHARGING.equals(targetStatus) && Boolean.FALSE.equals(constraint.getAllowDischarge())) {
            return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
        }
        BigDecimal ratio = getPowerLimitRatio(constraint, runtimeConfig);
        BigDecimal finalPower = safePower(targetPower).multiply(ratio).setScale(POWER_SCALE, RoundingMode.HALF_UP);
        if (finalPower.compareTo(BigDecimal.ZERO) <= 0) {
            return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
        }
        return new StrategyCommandDecision(targetStatus, finalPower);
    }

    private BigDecimal getPowerLimitRatio(ProtectionConstraintVo constraint, EmsStrategyRuntimeConfig runtimeConfig) {
        BigDecimal ratio = constraint.getPowerLimitRatio();
        if (ratio == null || ratio.compareTo(BigDecimal.ZERO) < 0 || ratio.compareTo(BigDecimal.ONE) > 0) {
            ratio = DEFAULT_PROTECTION_RATIO;
        }
        if (nullSafeInt(constraint.getLevel()) == 1 && DEFAULT_PROTECTION_RATIO.compareTo(ratio) == 0) {
            BigDecimal deratePercent = runtimeConfig.getProtectL1DeratePercent();
            if (deratePercent == null || deratePercent.compareTo(BigDecimal.ZERO) < 0 || deratePercent.compareTo(new BigDecimal("100")) > 0) {
                deratePercent = DEFAULT_PROTECT_L1_DERATE_PERCENT;
            }
            ratio = deratePercent.divide(new BigDecimal("100"), POWER_SCALE, RoundingMode.HALF_UP);
        }
        return ratio;
    }

    private BigDecimal safePower(BigDecimal power) {
        if (power == null || power.compareTo(BigDecimal.ZERO) < 0) {
            return BigDecimal.ZERO;
        }
        return power;
    }

    private int nullSafeInt(Integer value) {
        return value == null ? 0 : value;
    }

    private static class StrategyCommandDecision {
        private final ChargeStatus chargeStatus;
        private final BigDecimal power;

        private StrategyCommandDecision(ChargeStatus chargeStatus, BigDecimal power) {
            this.chargeStatus = chargeStatus;
            this.power = power;
        }

        public ChargeStatus getChargeStatus() {
            return chargeStatus;
        }

        public BigDecimal getPower() {
            return power;
        }
    }

    // 判断当前时间是否在时间范围内
    private static boolean isTimeInRange(LocalTime now, Date startTime, Date endTime) {
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");
        LocalTime startLocalTime = startTime.toInstant()
                .atZone(zoneId)
                .toLocalTime();
        LocalTime endLocalTime = endTime.toInstant()
                .atZone(zoneId)
                .toLocalTime();
        // 支持跨天时段，如23:00-01:00；边界采用闭区间
        if (!startLocalTime.isAfter(endLocalTime)) {
            return !now.isBefore(startLocalTime) && !now.isAfter(endLocalTime);
        }
        return !now.isBefore(startLocalTime) || !now.isAfter(endLocalTime);
    }

    // 判断SOC上限和下限
    private boolean isSocInRange(EmsStrategyTemp emsStrategyTemp, EmsStrategyRuntimeConfig runtimeConfig) {
        BigDecimal socDown = runtimeConfig.getSocDown();
        BigDecimal socUp = runtimeConfig.getSocUp();
        if (SocLimit.ON.getCode().equals(emsStrategyTemp.getSdcLimit())) {
            socDown = emsStrategyTemp.getSdcDown();
            socUp = emsStrategyTemp.getSdcUp();
        }
        // 查询电池堆(BMSD) SOC
        EmsBatteryStack emsBatteryStack = emsBatteryStackMapper.getSiteSumStackInfo(emsStrategyTemp.getSiteId());
        if (emsBatteryStack == null || emsBatteryStack.getStackSoc() == null) {
            return true;
        }

        // 充电阶段判断SOC上限值
        if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus()) && emsBatteryStack.getStackSoc().compareTo(socUp) >= 0) {
            return false;
        }

        // 放电阶段判断SOC下限值
        if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus()) && emsBatteryStack.getStackSoc().compareTo(socDown) <= 0) {
            return false;
        }

        return true;
    }

    private EmsStrategyRuntimeConfig getRuntimeConfig(String siteId) {
        EmsStrategyRuntimeConfig config = runtimeConfigMapper.selectBySiteId(siteId);
        if (config == null) {
            config = new EmsStrategyRuntimeConfig();
            config.setSiteId(siteId);
        }
        if (config.getSocDown() == null) {
            config.setSocDown(DEFAULT_SOC_DOWN);
        }
        if (config.getSocUp() == null) {
            config.setSocUp(DEFAULT_SOC_UP);
        }
        if (config.getAntiReverseThreshold() == null) {
            config.setAntiReverseThreshold(DEFAULT_ANTI_REVERSE_THRESHOLD);
        }
        if (config.getAntiReverseRangePercent() == null) {
            config.setAntiReverseRangePercent(DEFAULT_ANTI_REVERSE_RANGE_PERCENT);
        }
        if (config.getAntiReverseUp() == null) {
            config.setAntiReverseUp(DEFAULT_ANTI_REVERSE_UP);
        }
        if (config.getAntiReversePowerDownPercent() == null) {
            config.setAntiReversePowerDownPercent(DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT);
        }
        if (config.getAntiReverseHardStopThreshold() == null) {
            config.setAntiReverseHardStopThreshold(DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD);
        }
        if (config.getPowerSetMultiplier() == null || config.getPowerSetMultiplier().compareTo(BigDecimal.ZERO) <= 0) {
            config.setPowerSetMultiplier(DEFAULT_POWER_SET_MULTIPLIER);
        }
        if (config.getProtectInterveneEnable() == null) {
            config.setProtectInterveneEnable(DEFAULT_PROTECT_INTERVENE_ENABLE);
        }
        if (config.getProtectL1DeratePercent() == null
                || config.getProtectL1DeratePercent().compareTo(BigDecimal.ZERO) < 0
                || config.getProtectL1DeratePercent().compareTo(new BigDecimal("100")) > 0) {
            config.setProtectL1DeratePercent(DEFAULT_PROTECT_L1_DERATE_PERCENT);
        }
        if (config.getProtectRecoveryStableSeconds() == null || config.getProtectRecoveryStableSeconds() < 0) {
            config.setProtectRecoveryStableSeconds(DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS);
        }
        if (config.getProtectL3LatchEnable() == null) {
            config.setProtectL3LatchEnable(DEFAULT_PROTECT_L3_LATCH_ENABLE);
        }
        if (StringUtils.isEmpty(config.getProtectConflictPolicy())) {
            config.setProtectConflictPolicy(DEFAULT_PROTECT_CONFLICT_POLICY);
        }
        return config;
    }

}
