package com.njcn.advance.service.govern.voltage.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.advance.enums.AdvanceResponseEnum;
import com.njcn.advance.mapper.govern.voltage.SgEventMapper;
import com.njcn.advance.pojo.dto.govern.voltage.ProductLineComputeData;
import com.njcn.advance.pojo.dto.govern.voltage.SgEventExcel;
import com.njcn.advance.pojo.param.govern.voltage.SgEventParam;
import com.njcn.advance.pojo.po.govern.voltage.SgEvent;
import com.njcn.advance.pojo.po.govern.voltage.SgIncomingLine;
import com.njcn.advance.pojo.vo.govern.voltage.*;
import com.njcn.advance.service.govern.voltage.ISgEventService;
import com.njcn.advance.service.govern.voltage.ISgIncomingLineService;
import com.njcn.advance.service.govern.voltage.ISgProductLineService;
import com.njcn.advance.utils.NumberUtil;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.PubUtils;
import com.njcn.poi.excel.ExcelUtil;
import com.njcn.web.factory.PageFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hongawen
 * @since 2024-02-29
 */
@Service
@RequiredArgsConstructor
public class SgEventServiceImpl extends ServiceImpl<SgEventMapper, SgEvent> implements ISgEventService {

    private final ISgIncomingLineService sgIncomingLineService;

    private final ISgProductLineService productLineService;

    /**
     * 暂降幅值，不是特征幅值
     */
    private static final double[][] AMPLITUDE = {
            {80, 90},
            {70, 80},
            {60, 70},
            {50, 60},
            {40, 50},
            {10, 40}
    };

    /**
     * 暂降持续时间 ms
     */
    private static final double[][] DURATION_TIME = {
            {10, 20},
            {20, 50},
            {50, 70},
            {70, 100},
            {100, 1000}
    };

    private static final Map<double[], Integer> AMPLITUDE_KEY_MAP = new HashMap<double[], Integer>() {{
        put(AMPLITUDE[0], 85);
        put(AMPLITUDE[1], 75);
        put(AMPLITUDE[2], 65);
        put(AMPLITUDE[3], 55);
        put(AMPLITUDE[4], 45);
        put(AMPLITUDE[5], 25);
    }};

    private static final Map<double[], Integer> DURATION_TIME_KEY_MAP = new HashMap<double[], Integer>() {{
        put(DURATION_TIME[0], 15);
        put(DURATION_TIME[1], 35);
        put(DURATION_TIME[2], 60);
        put(DURATION_TIME[3], 85);
        put(DURATION_TIME[4], 550);
    }};

    /**
     * 根据查询条件返回分页的暂降数据
     *
     * @param sgEventParamQueryParam 查询条件
     */
    @Override
    public Page<SgEventVO> sgEventList(SgEventParam.SgEventParamQueryParam sgEventParamQueryParam) {
        //暂降数据实际跟进线耦合的，需要根据所传的用户获取进线id
        String userId = sgEventParamQueryParam.getUserId();
        LambdaQueryWrapper<SgIncomingLine> sgIncomingLineLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sgIncomingLineLambdaQueryWrapper.eq(SgIncomingLine::getUserId, userId)
                .eq(SgIncomingLine::getState, DataStateEnum.ENABLE.getCode());
        List<SgIncomingLine> imcomingLineList = sgIncomingLineService.list(sgIncomingLineLambdaQueryWrapper);
        //以分页的方式查询出所有的暂降数据
        if (CollectionUtil.isNotEmpty(imcomingLineList)) {
            List<String> incomingLineIdList = imcomingLineList.stream().map(SgIncomingLine::getId).collect(Collectors.toList());
            QueryWrapper<SgEventVO> sgEventVOQueryWrapper = new QueryWrapper<>();
            sgEventVOQueryWrapper.in("sg_event.incoming_line_id", incomingLineIdList)
                    .between("sg_event.start_time", LocalDateTimeUtil.parse(sgEventParamQueryParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN),
                            LocalDateTimeUtil.parse(sgEventParamQueryParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN))
                    .eq("sg_event.state", DataStateEnum.ENABLE.getCode())
                    .orderByDesc("sg_event.start_time");
            return this.baseMapper.page(new Page<>(PageFactory.getPageNum(sgEventParamQueryParam), PageFactory.getPageSize(sgEventParamQueryParam)), sgEventVOQueryWrapper);
        }
        return new Page<>();
    }

    @Override
    public void downloadTemplate() {
        ExcelUtil.exportExcel("暂降事件模板.csv", "暂降数据", SgEventExcel.class, new ArrayList<>());
    }

    /**
     * 导出条件范围内所有的数据
     */
    @Override
    public void exportEventData(SgEventParam.SgEventParamQueryParam sgEventParamQueryParam) {
        List<SgEventVO> sgEventVOList;
        String userId = sgEventParamQueryParam.getUserId();
        LambdaQueryWrapper<SgIncomingLine> sgIncomingLineLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sgIncomingLineLambdaQueryWrapper.eq(SgIncomingLine::getUserId, userId)
                .eq(SgIncomingLine::getState, DataStateEnum.ENABLE.getCode());
        List<SgIncomingLine> imcomingLineList = sgIncomingLineService.list(sgIncomingLineLambdaQueryWrapper);
        if (CollectionUtil.isEmpty(imcomingLineList)) {
            ExcelUtil.exportExcel("暂降事件数据.xls", "暂降数据", SgEventVO.class, new ArrayList<>());
        } else {
            List<String> incomingLineIdList = imcomingLineList.stream().map(SgIncomingLine::getId).collect(Collectors.toList());
            sgEventVOList = this.baseMapper.getAllEventData(incomingLineIdList, LocalDateTimeUtil.parse(sgEventParamQueryParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN), LocalDateTimeUtil.parse(sgEventParamQueryParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN));
            ExcelUtil.exportExcel("暂降事件数据.xls", "暂降数据", SgEventVO.class, sgEventVOList);
        }
    }

    @Override
    public void importEventData(List<SgEventExcel> sgEventExcels, String incomingLineId, String productLineId) {
        //获取生产线信息
        ProductLineComputeData productLineComputeData = productLineService.assginProductLineComputeData(productLineId);
        if (CollectionUtil.isNotEmpty(sgEventExcels)) {
            List<SgEvent> sgEventList = sgEventExcels.stream().map(temp -> {
                SgEvent sgEvent = new SgEvent(incomingLineId, temp.getDuration(), temp.getFeatureAmplitude(), temp.getEventDescribe(), 0);
                String startTimeStr = temp.getStartTime().concat(StrPool.DOT).concat(dealMs(temp.getMs()));
                try {
                    sgEvent.setStartTime(LocalDateTimeUtil.parse(startTimeStr, DatePattern.NORM_DATETIME_MS_PATTERN));
                } catch (Exception exception) {
                    throw new BusinessException(AdvanceResponseEnum.EVENT_TIME_ERROR);
                }
                Double[] eventEffect = this.getEventLossAndFailureRate(productLineComputeData, sgEvent.getFeatureAmplitude(), sgEvent.getDuration());
                Double singleLoss = eventEffect[0];
                sgEvent.setEstimatedLoss(singleLoss);
                sgEvent.setProductLineId(productLineId);
                sgEvent.setFileFlag(0);
                sgEvent.setEventType("暂降");
                return sgEvent;
            }).collect(Collectors.toList());
            //先一个个的插，功能完成后来优化成批量插入，todo...
            for (SgEvent sgEvent : sgEventList) {
                this.baseMapper.insert(sgEvent);
            }
        }
    }

    /**
     * 处理毫秒补零操作
     *
     * @param ms 1位就补2位0
     */
    private String dealMs(String ms) {
        if (ms.length() >= 3) {
            return ms.substring(0, 3);
        } else if (ms.length() == 2) {
            return "0".concat(ms);
        } else {
            return "00".concat(ms);
        }
    }

    /**
     * 根据进线、时间获取范围内发生的暂降事件
     *
     * @param id        进线id
     * @param startTime 起始时间
     * @param endTime   截止时间
     */
    @Override
    public List<SgEvent> getEventData(String id, String startTime, String endTime) {
        LambdaQueryWrapper<SgEvent> sgEventLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sgEventLambdaQueryWrapper.eq(SgEvent::getIncomingLineId, id)
                .between(SgEvent::getStartTime,
                        LocalDateTimeUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN),
                        LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN)
                )
                .eq(SgEvent::getState, DataStateEnum.ENABLE.getCode())
                .orderByDesc(SgEvent::getStartTime);
        return this.list(sgEventLambdaQueryWrapper);
    }

    /**
     * 根据进线、时间获取范围内电网接入水平
     *
     * @param id        进线id
     * @param startTime 起始时间
     * @param endTime   截止时间
     */
    @Override
    public Map<Integer[], Integer> getEventMap(String id, String startTime, String endTime) {
        Map<Integer[], Integer> eventMap = new HashMap<>(16);
        LambdaQueryWrapper<SgEvent> lambdaQueryWrapper;
        for (double[] amplitudeRange : AMPLITUDE) {
            for (double[] durationTimeRange : DURATION_TIME) {
                lambdaQueryWrapper = new LambdaQueryWrapper<>();
                lambdaQueryWrapper.eq(SgEvent::getIncomingLineId, id)
                        .ge(SgEvent::getFeatureAmplitude, amplitudeRange[0] * 0.01)
                        .lt(SgEvent::getFeatureAmplitude, amplitudeRange[1] * 0.01)
                        .ge(SgEvent::getDuration, durationTimeRange[0])
                        .lt(SgEvent::getDuration, durationTimeRange[1])
                        .eq(SgEvent::getState, DataStateEnum.ENABLE.getCode())
                        .between(SgEvent::getStartTime,
                                LocalDateTimeUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN),
                                LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN)
                        );
                int count = this.count(lambdaQueryWrapper);
                if (count > 0) {
                    Integer[] key = new Integer[2];
                    key[0] = (int) ((amplitudeRange[0] + amplitudeRange[1]) / 2);
                    key[1] = (int) ((durationTimeRange[0] + durationTimeRange[1]) / 2);
                    eventMap.put(key, count);
                }
            }
        }
        return eventMap;
    }

    /**
     * 根据备用路线查询暂降事件与当前进线做对比，是否能做治理优化
     *
     * @param id             当前进线id
     * @param incomingLineId 备用进线id
     * @param startTime      起始时间
     * @param endTime        截止时间
     * @param actionTime     动作时间
     */
    @Override
    public Map<Integer[], Integer> countEventMap4Switch(Map<Integer[], Integer> eventMap, String id, String incomingLineId, String startTime, String endTime, Double actionTime) {
        LambdaQueryWrapper<SgEvent> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SgEvent::getIncomingLineId, id)
                .between(SgEvent::getStartTime,
                        LocalDateTimeUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN),
                        LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN)
                );
        List<SgEvent> mainList = this.list(lambdaQueryWrapper);
        List<SgEvent> subList = new ArrayList<>();
        int beforeCount = mainList.size();
        if (beforeCount > 0) {
            // 主线有暂降在算辅线
            LambdaQueryWrapper<SgEvent> lambdaQueryWrapperBackup = new LambdaQueryWrapper<>();
            lambdaQueryWrapperBackup.eq(SgEvent::getIncomingLineId, incomingLineId)
                    .between(SgEvent::getStartTime,
                            LocalDateTimeUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN),
                            LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN)
                    ).eq(SgEvent::getState, DataStateEnum.ENABLE.getCode());
            subList = this.list(lambdaQueryWrapperBackup);
            subList = intersectionList(mainList, subList);
        }
        int afterCount = subList.size();
        if (beforeCount > 0 && afterCount > 0) {
            //两条进线的暂降发生时间可重复度超过50%，不输出快切开关治理
            if ((double) afterCount / (double) beforeCount > 0.5) {
                return eventMap;
            }
        }

        // 按响应时间对暂降数据进行过滤,并合并重复暂降数据
        mainList = processTimeResponse(mainList, subList, actionTime.intValue());
        return fillMap(mainList);
    }

    @Override
    public List<SgEvent> countEvent4Switch(List<SgEvent> sgEventList, String id, String incomingLineId, String startTime, String endTime, Double actionTime) {
        LambdaQueryWrapper<SgEvent> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SgEvent::getIncomingLineId, id)
                .between(SgEvent::getStartTime,
                        LocalDateTimeUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN),
                        LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN)
                );
        List<SgEvent> mainList = this.list(lambdaQueryWrapper);
        List<SgEvent> subList = new ArrayList<>();
        int beforeCount = mainList.size();
        if (beforeCount > 0) {
            // 主线有暂降在算辅线
            LambdaQueryWrapper<SgEvent> lambdaQueryWrapperBackup = new LambdaQueryWrapper<>();
            lambdaQueryWrapperBackup.eq(SgEvent::getIncomingLineId, incomingLineId)
                    .between(SgEvent::getStartTime,
                            LocalDateTimeUtil.parse(startTime, DatePattern.NORM_DATE_PATTERN),
                            LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATE_PATTERN)
                    ).eq(SgEvent::getState, DataStateEnum.ENABLE.getCode());
            subList = this.list(lambdaQueryWrapperBackup);
            subList = intersectionList(mainList, subList);
        }
        int afterCount = subList.size();
        if (beforeCount > 0 && afterCount > 0) {
            //两条进线的暂降发生时间可重复度超过50%，不输出快切开关治理
            if ((double) afterCount / (double) beforeCount > 0.5) {
                return sgEventList;
            }
        }

        // 按响应时间对暂降数据进行过滤,并合并重复暂降数据
        mainList = processTimeResponse(mainList, subList, actionTime.intValue());
        return mainList;

    }

    @Override
    public boolean deleteEventData(List<String> ids) {
        return this.removeByIds(ids);
    }

    @Override
    public TransientEventCountVO threeDList(SgEventParam.SgEventParamQueryParam sgEventParamQueryParam) {
        TransientEventCountVO params = new TransientEventCountVO();
        String beginDate = sgEventParamQueryParam.getSearchBeginTime();
        String endDate = sgEventParamQueryParam.getSearchEndTime();
        String userId = sgEventParamQueryParam.getUserId();
        LambdaQueryWrapper<SgIncomingLine> sgIncomingLineLambdaQueryWrapper = new LambdaQueryWrapper<>();
        sgIncomingLineLambdaQueryWrapper.eq(SgIncomingLine::getUserId, userId)
                .eq(SgIncomingLine::getState, DataStateEnum.ENABLE.getCode());
        List<SgIncomingLine> imcomingLineList = sgIncomingLineService.list(sgIncomingLineLambdaQueryWrapper);
        //根据用户id获取所有的进线id
        if (CollectionUtil.isEmpty(imcomingLineList)) {
            return null;
        }
        List<String> incomingLineIDList = imcomingLineList.stream().map(SgIncomingLine::getId).collect(Collectors.toList());
        List<List<Object>> list = countEventTimes(incomingLineIDList, beginDate, endDate);
        // 转换
        ArrayList<String> amplitudes = new ArrayList<>();
        ArrayList<String> durations = new ArrayList<>();
        ArrayList<ArrayList<String>> value = new ArrayList<>();
        List<TransientEventCount> tbList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            List<Object> list1 = list.get(i);
            for (int j = 0; j < list1.size(); j++) {
                if (i == 0 && j == 0) {
                    continue;
                }
                Object v = list1.get(j);
                //第一行为持续时间
                if (i == 0) {
                    durations.add(String.valueOf(v));
                } else {
                    //第一列为幅值
                    if (j == 0) {
                        amplitudes.add(String.valueOf(v));
                    } else {
                        ArrayList<String> vv = new ArrayList<>();
                        String amplitude = amplitudes.get(i - 1);
                        vv.add(amplitude);

                        String duration = durations.get(j - 1);
                        vv.add(duration);

                        vv.add(String.valueOf(v));
                        value.add(vv);
                    }
                }
            }
        }
        String[][] strTwo = new String[value.size()][];
        for (int i = 0; i < value.size(); i++) {
            ArrayList<String> v = value.get(i);
            String[] vv = v.toArray(new String[v.size()]);
            strTwo[i] = vv;
        }
        params.setValues(strTwo);

        list.remove(0);
        for (List<Object> l : list) {
            TransientEventCount count = new TransientEventCount();
            count.setAmplitude(String.valueOf(l.get(0)));
            count.setD001_002s(String.valueOf(l.get(1)));
            count.setD002_005s(String.valueOf(l.get(2)));
            count.setD005_007s(String.valueOf(l.get(3)));
            count.setD007_010s(String.valueOf(l.get(4)));
            count.setD010_100s(String.valueOf(l.get(5)));
            tbList.add(count);
        }
        return params;
    }

    @Override
    public void calcLoss(String id) {
        SgEvent sgEvent = this.getById(id);
        //获取生产线信息
        ProductLineComputeData productLineComputeData = productLineService.assginProductLineComputeData(sgEvent.getProductLineId());
        //根据暂降信息计算预估损失
        Double[] eventEffect = this.getEventLossAndFailureRate(productLineComputeData, sgEvent.getFeatureAmplitude(), sgEvent.getDuration());
        Double singleLoss = eventEffect[0];
        sgEvent.setEstimatedLoss(singleLoss);
        this.baseMapper.updateById(sgEvent);
    }


    /**
     * 需要计算出生产线故障率和生产线此次的损失
     *
     * @param productLineComputeData 生产线数据
     * @param amplitude              特征幅值
     * @param durationTime           持续时间
     */
    @Override
    public Double[] getEventLossAndFailureRate(ProductLineComputeData productLineComputeData, double amplitude, double durationTime) {
        //总损失
        double totalLoss = 0.0;
        //生产线故障率
        Double productFailureRate = 0.0;
        //如果是模式二，需要独立计算下设备损失+原料损失
        List<SgMachineVO> machineVOList = productLineComputeData.getMachineVOList();
        if (CollectionUtil.isNotEmpty(machineVOList)) {
            for (int i = 0; i < machineVOList.size(); i++) {
                List<SgSensitiveUnitVO> sgSensitiveUnitList = machineVOList.get(i).getSgSensitiveUnitList();
                //计算故障率
                double devFailureRate = getDevFailureRate(sgSensitiveUnitList, amplitude, durationTime);
                if (productLineComputeData.getCalcMode() == 1) {
                    //根据该故障率以及设备损失、原料损失计算出该设备单次的损失
                    double machineLoss = (machineVOList.get(i).getMachineLoss() + machineVOList.get(i).getMaterialLoss()) * devFailureRate;
                    totalLoss = totalLoss + machineLoss;
                }
                //顺便计算出生产线的故障率
                if (i == 0) {
                    productFailureRate = devFailureRate;
                } else {
                    // 默认设备是串联的
                    productFailureRate = 1 - (1 - productFailureRate) * (1 - devFailureRate);
                }
            }
            //计算完各个设备的损失，最终计算下生产线在该暂降幅值、持续时间条件下的单独的经济损失
            totalLoss = totalLoss + productLineComputeData.getCapacityLoss() * productFailureRate;
        }
        totalLoss = PubUtils.doubleRound(2, totalLoss);
        return new Double[]{totalLoss, productFailureRate};
    }

    @Override
    public List<SgEventVO> getEventDataByProductLineId(SgEventParam.SgEventParamQueryParam sgEventParamQueryParam) {
        return this.baseMapper.getEventDataByProductLineId(sgEventParamQueryParam.getProductId(), LocalDateTimeUtil.parse(sgEventParamQueryParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN),
                LocalDateTimeUtil.parse(sgEventParamQueryParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN));
    }

    /**
     * 计算生产线下设备的故障率
     *
     * @param sgSensitiveUnitList 敏感元件数据
     * @param amplitude           特征幅值
     * @param durationTime        持续时间 ms
     */
    private double getDevFailureRate(List<SgSensitiveUnitVO> sgSensitiveUnitList, double amplitude, double durationTime) {
        if (CollectionUtil.isEmpty(sgSensitiveUnitList)) {
            return 0;
        }
        double rate = 0;
        boolean first = true;
        for (SgSensitiveUnitVO sgSensitiveUnitVO : sgSensitiveUnitList) {
            double unitRate = 0;
            Double uMax = sgSensitiveUnitVO.getVtcAmpUpper();
            Double uMin = sgSensitiveUnitVO.getVtcAmpLower();
            Double timeMax = sgSensitiveUnitVO.getVtcTimeLower();
            Double timeMin = sgSensitiveUnitVO.getVtcTimeUpper();
            if (amplitude > uMax || durationTime < timeMin) {
                // 暂降幅值大于最大值,或者持续时间小于最小值,属于正常状态(故障率0)
                unitRate = 0;
            } else if (amplitude < uMin && durationTime > timeMax) {
                // 暂降幅值小于最小值和持续时间大于最大值,有一个故障就返回故障(故障率1)
                unitRate = 1;
            } else {
                // 根据灵敏度得到FtFu
                double fu, ft;
                if (sgSensitiveUnitVO.getSensitivity() == 3) {
                    // 低灵敏度
                    // 残余电压取指数分布,公式1-6
                    double tt1 = amplitude - uMin;
                    tt1 = -100 * tt1;
                    tt1 = Math.exp(tt1);
                    fu = 100 * tt1;
                    // 持续时间取反指数分布,公式1-7
                    double tt2 = durationTime - timeMin;
                    tt2 = 100 * tt2;
                    tt2 = Math.exp(tt2);
                    ft = 100 * tt2;
                } else if (sgSensitiveUnitVO.getSensitivity() == 2) {
                    // 中灵敏度，残余电压和持续时间均取正态分布，公式1-8
                    double tt1 = (double) 1 / (0.17 * Math.sqrt(2 * 3.14));
                    double tt2 = Math.pow(durationTime - (uMax + uMin) / 2, 2);
                    double tt3 = Math.pow(amplitude - (timeMin + timeMax) / 2, 2);
                    double tt4 = (double) 2 * Math.pow(0.17, 2);
                    // 残余电压
                    double ttt1 = tt2 / tt4;
                    ttt1 = 0 - ttt1;
                    ttt1 = Math.exp(ttt1);
                    fu = tt1 * ttt1;
                    // 持续时间
                    double ttt2 = tt3 / tt4;
                    ttt2 = 0 - ttt2;
                    ttt2 = Math.exp(ttt2);
                    ft = tt1 * ttt2;
                } else if (sgSensitiveUnitVO.getSensitivity() == 1) {
                    // 高灵敏度
                    // 残余电压取反指数分布，公式1-7
                    double tt1 = amplitude - uMax;
                    tt1 = 100 * tt1;
                    tt1 = Math.exp(tt1);
                    fu = 100 * tt1;
                    // 持续时间取指数分布,公式1-6
                    double tt2 = durationTime - timeMin;
                    tt2 = -100 * tt2;
                    tt2 = Math.exp(tt2);
                    ft = 100 * tt2;
                } else {
                    // 默认灵敏度，均匀分布，公式1-5
                    fu = (float) 1 / (uMax - uMin);
                    ft = (float) 1 / (timeMax - timeMin);
                }

                //判断在ABC哪个区域
                if (durationTime >= timeMin && durationTime <= timeMax && amplitude >= uMin && amplitude <= uMax) {
                    // A区,公式1-1
                    if (Double.isNaN(fu) || Double.isNaN(ft)) {
                        unitRate = 0;
                    } else if (Double.isInfinite(fu) || Double.isInfinite(ft)) {
                        unitRate = 1;
                    } else {
                        unitRate = (uMax - amplitude) * fu * (durationTime - timeMin) * ft;
                    }
                }
                if (durationTime > timeMax && amplitude >= uMin && amplitude <= uMax) {
                    // B区,公式1-2
                    unitRate = (uMax - amplitude) * fu;
                }
                if (durationTime > timeMin && durationTime <= timeMax && amplitude < uMin) {
                    // C区,公式1-3
                    if (Double.isNaN(ft)) {
                        unitRate = 0;
                    } else if (Double.isInfinite(ft)) {
                        unitRate = 1;
                    } else {
                        unitRate = (durationTime - timeMin) * ft;
                    }
                }
            }
            if (first) {
                rate = unitRate;
                first = false;
            } else {
                // 元器件串联
                rate = 1 - (1 - rate) * (1 - unitRate);
            }
        }
        return rate;
    }


    /**
     * 统计暂降事件发生次数， 限定条件降幅范围和持续时间范围
     */
    public List<List<Object>> countEventTimes(List<String> lineIds, String beginDate, String endDate) {
        List<List<Object>> rList = new ArrayList<>();
        List<Object> firstRow = new ArrayList<>();
        firstRow.add("幅值\\持续时间");
        rList.add(firstRow);
        boolean b = true;
        for (double[] amplitudeRange : AMPLITUDE) {
            ArrayList<Object> row = getOneRowEventTimesData(b ? firstRow : null, amplitudeRange, lineIds, beginDate, endDate);
            rList.add(row);
            b = false;
        }
        return rList;
    }

    /**
     * 获取一行事件次数统计数据
     */
    private ArrayList<Object> getOneRowEventTimesData(List<Object> firstRow, double[] amplitudeRange, List<String> lineIds, String beginDate, String endDate) {
        ArrayList<Object> rList = new ArrayList<>();
        rList.add(NumberUtil.format(amplitudeRange[0] * 0.01, "0.0") + "~" + NumberUtil.format(amplitudeRange[1] * 0.01, "0.0"));
        for (double[] durationTimeRange : SgEventServiceImpl.DURATION_TIME) {
            if (firstRow != null) {
                firstRow.add(NumberUtil.format(durationTimeRange[0] * 0.001, "0.00") + "~" + NumberUtil.format(durationTimeRange[1] * 0.001, "0.00"));
            }
            LambdaQueryWrapper<SgEvent> sgEventLambdaQueryWrapper = new LambdaQueryWrapper<>();
            sgEventLambdaQueryWrapper.eq(SgEvent::getState, DataStateEnum.ENABLE.getCode())
                    .between(SgEvent::getStartTime,
                            LocalDateTimeUtil.parse(beginDate, DatePattern.NORM_DATE_PATTERN),
                            LocalDateTimeUtil.parse(endDate, DatePattern.NORM_DATE_PATTERN)
                    )
                    .in(SgEvent::getIncomingLineId, lineIds)
                    .between(SgEvent::getDuration, durationTimeRange[0], durationTimeRange[1])
                    .between(SgEvent::getFeatureAmplitude, amplitudeRange[0] * 0.01, amplitudeRange[1] * 0.01);
            Integer count = this.count(sgEventLambdaQueryWrapper);
            rList.add(count);
        }
        return rList;
    }


    /**
     * 计算两个List的交集
     *
     * @param aList 主进线的暂降事件
     * @param bList 备用进线的暂降事件
     */
    private List<SgEvent> intersectionList(List<SgEvent> aList, List<SgEvent> bList) {
        List<SgEvent> rList = new ArrayList<>();
        for (SgEvent a : aList) {
            boolean bol = false;
            // 如果响应时间小于持续时间,再看辅线是否暂降
            for (SgEvent b : bList) {
                bol = comparisonEvent(a, b);
                if (bol) {
                    break;
                }
            }
            if (bol) {
                rList.add(a);
            }
        }
        return rList;
    }

    /**
     * 用于判断两个事件持续的时间是否有重合，重合即不可修复。
     *
     * @param mainEvent   主进线事件
     * @param backUpEvent 备用进线事件
     */
    private boolean comparisonEvent(SgEvent mainEvent, SgEvent backUpEvent) {
        LocalDateTime begin = mainEvent.getStartTime();
        LocalDateTime end = LocalDateTimeUtil.offset(begin, mainEvent.getDuration().longValue(), ChronoUnit.MILLIS);
        LocalDateTime target = backUpEvent.getStartTime();
        return !target.isBefore(begin) && !target.isAfter(end);
    }

    /**
     * 拼接不能治理的暂降数据和重复暂降数据
     *
     * @param mainEvents   原始暂降数据
     * @param backupEvents 重复的暂降数据
     * @param timeResponse 快切开关响应时间
     */
    private List<SgEvent> processTimeResponse(List<SgEvent> mainEvents, List<SgEvent> backupEvents, int timeResponse) {
        List<SgEvent> rList = new ArrayList<>();
        for (SgEvent a : mainEvents) {
            boolean bol = timeResponse > a.getDuration();
            // 考虑响应时间
            if (bol) {
                rList.add(a);
            }
        }

        // 将重复的暂降数据bList加入到rList中
        for (SgEvent b : backupEvents) {
            boolean bol = false;
            for (SgEvent c : rList) {
                // 重复检测
                if (Objects.equals(b.getId(), c.getId())) {
                    bol = true;
                    break;
                }
            }

            if (!bol) {
                rList.add(b);
            }
        }

        return rList;
    }


    /**
     * 通过循环List累加的方式，统计频次
     *
     * @param eventList 暂降事件集合
     */
    private Map<Integer[], Integer> fillMap(List<SgEvent> eventList) {
        Map<Integer[], Integer> rMap = new HashMap<>(16);
        for (SgEvent event : eventList) {
            Integer[] key = getKey(event.getDuration(), event.getFeatureAmplitude());
            if (rMap.containsKey(key)) {
                rMap.put(key, rMap.get(key) + 1);
            } else {
                rMap.put(key, 1);
            }
        }
        return rMap;
    }

    /**
     * 通过持续时间和幅值，获取频次表的key
     *
     * @param eventDurationTime 持续时间
     * @param eventAmplitude    特征幅值
     */
    private Integer[] getKey(double eventDurationTime, double eventAmplitude) {
        Integer[] key = {0, 0};
        double eAmpl = eventAmplitude * 100;
        for (double[] a : AMPLITUDE) {
            if (eAmpl > a[0] && eAmpl <= a[1]) {
                Integer t2 = AMPLITUDE_KEY_MAP.get(a);
                key[0] = t2;
            }
        }
        for (double[] a : DURATION_TIME) {
            if (eventDurationTime > a[0] && eventDurationTime <= a[1]) {
                Integer t1 = DURATION_TIME_KEY_MAP.get(a);
                key[1] = t1;
            }
        }
        return key;
    }


}
