package com.njcn.csharmonic.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.advance.api.EventCauseFeignClient;
import com.njcn.advance.pojo.dto.EventAnalysisDTO;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.csdevice.api.CsLedgerFeignClient;
import com.njcn.csdevice.api.CsLineFeignClient;
import com.njcn.csdevice.enums.AlgorithmResponseEnum;
import com.njcn.csdevice.pojo.dto.CsLineDTO;
import com.njcn.csdevice.pojo.dto.DevDetailDTO;
import com.njcn.csdevice.pojo.po.CsLinePO;
import com.njcn.csdevice.pojo.vo.DataGroupEventVO;
import com.njcn.csharmonic.enums.CsEventEnum;
import com.njcn.csharmonic.enums.CsTransientEnum;
import com.njcn.csharmonic.mapper.CsEventPOMapper;
import com.njcn.csharmonic.param.*;
import com.njcn.csharmonic.pojo.param.EventStatisticParam;
import com.njcn.csharmonic.pojo.po.CsEventPO;
import com.njcn.csharmonic.pojo.vo.*;
import com.njcn.csharmonic.service.CsEventPOService;
import com.njcn.csharmonic.service.CsEventUserPOService;
import com.njcn.csharmonic.utils.DataChangeUtil;
import com.njcn.cssystem.api.MsgSendFeignClient;
import com.njcn.cssystem.pojo.param.MsgSendParam;
import com.njcn.event.common.mapper.WlRmpEventDetailMapper;
import com.njcn.event.common.service.EventAnalysisService;
import com.njcn.event.file.component.WaveFileComponent;
import com.njcn.event.file.component.WavePicComponent;
import com.njcn.event.file.pojo.bo.WaveDataDetail;
import com.njcn.event.file.pojo.dto.WaveDataDTO;
import com.njcn.event.file.pojo.enums.WaveFileResponseEnum;
import com.njcn.event.file.utils.WaveUtil;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.influx.pojo.constant.InfluxDBTableConstant;
import com.njcn.influx.pojo.dto.EventDataSetDTO;
import com.njcn.influx.service.EvtDataService;
import com.njcn.influx.utils.InfluxDbUtils;
import com.njcn.minioss.config.MinIossProperties;
import com.njcn.minioss.util.MinIoUtils;
import com.njcn.oss.constant.GeneralConstant;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.api.EleEvtFeignClient;
import com.njcn.system.api.EpdFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.system.pojo.po.EleEpdPqd;
import com.njcn.system.pojo.po.EleEvtParm;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.influxdb.InfluxDB;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.njcn.csharmonic.enums.CsTransientEnum.*;

/**
 * Description:
 * Date: 2023/9/4  15:15【需求编号】
 *
 * @author clam
 * @version V1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CsEventPOServiceImpl extends ServiceImpl<CsEventPOMapper, CsEventPO> implements CsEventPOService {

    private final EvtDataService evtDataService;
    private final FileStorageUtil fileStorageUtil;
    private final WaveFileComponent waveFileComponent;
    private final CsLineFeignClient csLineFeignClient;
    private final CsLedgerFeignClient csLedgerFeignClient;
    private final EpdFeignClient epdFeignClient;
    private final CsEventUserPOService csEventUserPOService;
    private final EleEvtFeignClient eleEvtFeignClient;
    private final WavePicComponent wavePicComponent;
    private final MinIossProperties minIossProperties;
    private final InfluxDbUtils influxDbUtils;
    private final MinIoUtils minIoUtils;
    private final DicDataFeignClient dicDataFeignClient;
    private final WlRmpEventDetailMapper wlRmpEventDetailMapper;
    private final CsLedgerFeignClient csLedgerFeignclient;
    private final EventAnalysisService eventAnalysisService;
    private final EventCauseFeignClient eventCauseFeignClient;
    private final MsgSendFeignClient msgSendFeignClient;
    private final CsEventPOMapper csEventPOMapper;
    /** 两段数据间隔区域补null点数量，可通过Nacos配置wave.gap.null.points调整 */
    @Value("${wave.gap.null.points:5}")
    private int waveGapNullPoints;

    @Override
    public List<EventDetailVO> queryEventList(CsEventUserQueryParam csEventUserQueryParam) {

        List<EventDetailVO> list = csEventUserPOService.queryUserEventList(csEventUserQueryParam);
        list.forEach(temp -> {
            DevDetailDTO devDetail = csLedgerFeignClient.queryDevDetail(temp.getDeviceId()).getData();
            temp.setEquipmentName(devDetail.getEquipmentName());
            temp.setProjectId(devDetail.getProjectId());
            temp.setProjectName(devDetail.getProjectName());
            temp.setEngineeringid(devDetail.getEngineeringid());
            temp.setEngineeringName(devDetail.getEngineeringName());
            EleEpdPqd data = epdFeignClient.findByName(temp.getTag()).getData();
            List<EleEvtParm> data1 = eleEvtFeignClient.queryByPid(data.getId()).getData();
            List<EventDataSetDTO> eventDataSetDTOS = new ArrayList<>();
            for (EleEvtParm eleEvtParm : data1) {
                EventDataSetDTO eventDataSetDTO = new EventDataSetDTO();
                BeanUtils.copyProperties(eleEvtParm, eventDataSetDTO);
                if (Objects.equals(eventDataSetDTO.getName(),"Evt_Param_Position")) {
                    continue;
//                        eventDataSetDTO.setValue(Objects.equals(temp.getLocation(),"grid")?"电网侧":"负载侧");
                }
                EventDataSetDTO evtData = evtDataService.getEventDataSet("evt_data", temp.getId(), eleEvtParm.getName());
                if (evtData == null) {
                    eventDataSetDTO.setValue("3.1415926");
                } else {
                    eventDataSetDTO.setValue(Optional.ofNullable(evtData.getValue()).orElse("3.1415926"));
                }
                eventDataSetDTOS.add(eventDataSetDTO);
            }
            temp.setDataSet(eventDataSetDTOS);

        });

        return list;
    }

    @Override
    public Page<DataGroupEventVO> pageQueryByLineId(CsEventUserQueryPage csEventUserQueryPage) {
        Page<DataGroupEventVO> result = new Page<>(csEventUserQueryPage.getPageNum(),csEventUserQueryPage.getPageSize());
        //治理，无线混用会导致csEventUserQueryPage.getStartTime()为空，这里坐下特殊处理，当时间为空的时候默认查当天的数据
        DateTime begin = DateUtil.beginOfDay(new Date());
        DateTime end = DateUtil.endOfDay(new Date());
        if(StringUtils.isNotBlank(csEventUserQueryPage.getStartTime())){
             begin = DateUtil.parse(csEventUserQueryPage.getStartTime(), DatePattern.NORM_DATE_PATTERN);
        }
        if(StringUtils.isNotBlank(csEventUserQueryPage.getEndTime())){
            end = DateUtil.endOfDay(DateUtil.parse(csEventUserQueryPage.getEndTime(), DatePattern.NORM_DATE_PATTERN));
        }

        LambdaQueryWrapper<CsEventPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (!Objects.isNull(csEventUserQueryPage.getEvtParamTmMin())) {
            lambdaQueryWrapper.gt(CsEventPO::getPersistTime,csEventUserQueryPage.getEvtParamTmMin());
        }
        if (!Objects.isNull(csEventUserQueryPage.getEvtParamTmMax())) {
            lambdaQueryWrapper.lt(CsEventPO::getPersistTime,csEventUserQueryPage.getEvtParamTmMax());
        }
        if (!Objects.isNull(csEventUserQueryPage.getFeatureAmplitudeMin())) {
            lambdaQueryWrapper.gt(CsEventPO::getAmplitude,csEventUserQueryPage.getFeatureAmplitudeMin());
        }
        if (!Objects.isNull(csEventUserQueryPage.getFeatureAmplitudeMax())) {
            lambdaQueryWrapper.lt(CsEventPO::getAmplitude,csEventUserQueryPage.getFeatureAmplitudeMax());
        }
        if (!Objects.isNull(csEventUserQueryPage.getSeverityMin())) {
            lambdaQueryWrapper.gt(CsEventPO::getSeverity,csEventUserQueryPage.getSeverityMin());
        }
        if (!Objects.isNull(csEventUserQueryPage.getSeverityMax())) {
            lambdaQueryWrapper.lt(CsEventPO::getSeverity,csEventUserQueryPage.getSeverityMax());
        }
        if (CollUtil.isNotEmpty(csEventUserQueryPage.getTarget())) {
            List<String> tag = DataChangeUtil.getTag(csEventUserQueryPage.getTarget());
            lambdaQueryWrapper.in(CsEventPO::getTag,tag);
        }
        if (!Objects.isNull(csEventUserQueryPage.getFileFlag()) && csEventUserQueryPage.getFileFlag() == 0) {
            lambdaQueryWrapper.isNull(CsEventPO::getWavePath);
        } else if (!Objects.isNull(csEventUserQueryPage.getFileFlag()) && csEventUserQueryPage.getFileFlag() == 1) {
            lambdaQueryWrapper.isNotNull(CsEventPO::getWavePath);
        }

        lambdaQueryWrapper.eq(CsEventPO::getLineId,csEventUserQueryPage.getLineId())
                .eq(CsEventPO::getType,0)
                .between(CsEventPO::getStartTime,begin,end)
                .orderByDesc(CsEventPO::getStartTime);
        Page<CsEventPO> page = this.page(new Page<>(csEventUserQueryPage.getPageNum(),csEventUserQueryPage.getPageSize()),lambdaQueryWrapper);
        if(CollUtil.isNotEmpty(page.getRecords())){
            List<DataGroupEventVO> dataGroupEventVOList = BeanUtil.copyToList(page.getRecords(),DataGroupEventVO.class);
            result.setRecords(dataGroupEventVOList);
            result.setTotal(page.getTotal());
        }
        return result;
    }

    @Override
    public Page<DataGroupEventVO> pageEvent(EventStatisticParam baseParam) {
        Page<DataGroupEventVO> result = new Page<>(PageFactory.getPageNum(baseParam),PageFactory.getPageSize(baseParam));
        DateTime begin = DateUtil.beginOfDay(DateUtil.parse(baseParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN));
        DateTime end = DateUtil.endOfDay(DateUtil.parse(baseParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN));
        //暂态类型
        final List<String> EVENT_TAGS;
        if (Objects.isNull(baseParam.getEventType())) {
            EVENT_TAGS = Arrays.asList(
                    EVT_SYS_DIPSTR.getCode(),
                    EVT_SYS_INTRSTR.getCode(),
                    EVT_SYS_SWLSTR.getCode()
            );
        } else {
            switch (baseParam.getEventType()) {
                case 1:
                    EVENT_TAGS = Collections.singletonList(EVT_SYS_DIPSTR.getCode());
                    break;
                case 2:
                    EVENT_TAGS = Collections.singletonList(EVT_SYS_INTRSTR.getCode());
                    break;
                case 3:
                    EVENT_TAGS = Collections.singletonList(EVT_SYS_SWLSTR.getCode());
                    break;
                default:
                    EVENT_TAGS = Arrays.asList(
                            EVT_SYS_DIPSTR.getCode(),
                            EVT_SYS_INTRSTR.getCode(),
                            EVT_SYS_SWLSTR.getCode()
                    );
            }
        }

        //监测点
        List<CsLinePO> csLinePOList = csLineFeignClient.getSimpleLine().getData();
        if (CollUtil.isEmpty(csLinePOList)) {
            return result;
        }
        List<String> lineIds = csLinePOList.stream().map(CsLinePO::getLineId).distinct().collect(Collectors.toList());

        LambdaQueryWrapper<CsEventPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(StrUtil.isNotBlank(baseParam.getLineId()),CsEventPO::getLineId,baseParam.getLineId())
                .eq(CsEventPO::getType, CsEventEnum.EVENT_TYPE.getCode())
                .in(CsEventPO::getTag, EVENT_TAGS)
                .in(CsEventPO::getLineId,lineIds)
                .between(CsEventPO::getStartTime,begin,end)
                .orderByDesc(CsEventPO::getStartTime);
        Page<CsEventPO> page = this.page(new Page<>(PageFactory.getPageNum(baseParam),PageFactory.getPageSize(baseParam)),lambdaQueryWrapper);
        if(CollUtil.isNotEmpty(page.getRecords())){
            Map<String,String> linePOMap = csLinePOList.stream().collect(Collectors.toMap(CsLinePO::getLineId, CsLinePO::getName));

            List<DataGroupEventVO> dataGroupEventVOList = BeanUtil.copyToList(page.getRecords(),DataGroupEventVO.class);
            dataGroupEventVOList.forEach(item->{
                item.setLineName(linePOMap.getOrDefault(item.getLineId(),"/"));
                if(CsTransientEnum.EVT_SYS_SWLSTR.getCode().equals(item.getTag())){
                    item.setSeverity(null);
                }else {
                    item.setSeverity(Double.valueOf(getYzd(item.getPersistTime()*1000,item.getAmplitude()/100.0)));
                }
                item.setTag(CsTransientEnum.getNameByCode(item.getTag()));
                item.setPersistTime(BigDecimal.valueOf(item.getPersistTime()).setScale(3, RoundingMode.HALF_UP).doubleValue());
            });
            result.setRecords(dataGroupEventVOList);
            result.setTotal(page.getTotal());
        }
        return result;
    }

    /**
     * 获取该事件的严重度
     *
     * @param persistTime 持续时间 ms单位
     * @param eventValue  暂降、暂升幅值
     */
    public String getYzd(Double persistTime, Double eventValue) {
        Double yzd;
        // 格式化小数
        DecimalFormat df = new DecimalFormat("0.000");
        if (persistTime <= 20) {
            yzd = 1.0 - eventValue;
        } else if (persistTime > 20 && persistTime <= 200) {
            yzd = 2.0 * (1 - eventValue);
        } else if (persistTime > 200 && persistTime <= 500) {
            yzd = 3.3 * (1 - eventValue);
        } else if (persistTime > 500 && persistTime <= 10000) {
            yzd = 5.0 * (1 - eventValue);
        } else {
            yzd = 10.0 * (1 - eventValue);
        }
        return df.format(yzd);
    }

    @Override
    @Transactional
    public void saveBatchEventList(List<CsEventPO> csEventPOS) {
        this.saveBatch(csEventPOS);
    }

    @Override
    public void getFileZip(String eventId, HttpServletResponse response) {
        CsEventPO eventDetail = this.baseMapper.selectById(eventId);
        String waveName = eventDetail.getWavePath();
        if (StrUtil.isBlank(waveName)) {
            throw new BusinessException(WaveFileResponseEnum.ANALYSE_WAVE_NOT_FOUND);
        }
        int i = 0;
        String baseName="";
        try {
            List<MultipartFile> excelreport = fileStorageUtil.getAllFile(minIossProperties.getBucket(), waveName);
            //被压缩文件流集合
            InputStream[] srcFiles = new InputStream[excelreport.size()];
            //被压缩文件名称
            String[] srcFileNames = new String[excelreport.size()];
            for (MultipartFile entity : excelreport) {
                //以下代码为获取图片inputStream
                InputStream ins = entity.getInputStream();
                if (ins == null) {
                    continue;
                }
                //塞入流数组中
                srcFiles[i] = ins;
                int lastIndex = entity.getOriginalFilename().lastIndexOf('/');
                String fileName = entity.getOriginalFilename().substring(lastIndex + 1);
                baseName = FilenameUtils.getBaseName(fileName);
                srcFileNames[i] = fileName;
                i++;
            }
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(baseName+".zip", "UTF-8"));
            //多个文件压缩成压缩包返回
            ZipUtil.zip(response.getOutputStream(), srcFileNames, srcFiles);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public List<DataGroupEventVO> queryEventList(ItemParam param, LocalDateTime startDate, LocalDateTime endDate, String lineId) {
        List<DataGroupEventVO> dataGroupEventVOList = new ArrayList<>();
        LambdaQueryWrapper<CsEventPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        if (!Objects.isNull(param.getEvtParamTmMin())) {
            lambdaQueryWrapper.gt(CsEventPO::getPersistTime,param.getEvtParamTmMin());
        }
        if (!Objects.isNull(param.getEvtParamTmMax())) {
            lambdaQueryWrapper.lt(CsEventPO::getPersistTime,param.getEvtParamTmMax());
        }
        if (!Objects.isNull(param.getFeatureAmplitudeMin())) {
            lambdaQueryWrapper.gt(CsEventPO::getAmplitude,param.getFeatureAmplitudeMin());
        }
        if (!Objects.isNull(param.getFeatureAmplitudeMax())) {
            lambdaQueryWrapper.lt(CsEventPO::getAmplitude,param.getFeatureAmplitudeMax());
        }
        if (!Objects.isNull(param.getSeverityMin())) {
            lambdaQueryWrapper.gt(CsEventPO::getSeverity,param.getSeverityMin());
        }
        if (!Objects.isNull(param.getSeverityMax())) {
            lambdaQueryWrapper.lt(CsEventPO::getSeverity,param.getSeverityMax());
        }
        if (CollUtil.isNotEmpty(param.getTarget())) {
            List<String> tag = DataChangeUtil.getTag(param.getTarget());
            lambdaQueryWrapper.in(CsEventPO::getTag,tag);
        }
        if (!Objects.isNull(param.getFileFlag()) && param.getFileFlag() == 0) {
            lambdaQueryWrapper.isNull(CsEventPO::getWavePath);
        } else if (!Objects.isNull(param.getFileFlag()) && param.getFileFlag() == 1) {
            lambdaQueryWrapper.isNotNull(CsEventPO::getWavePath);
        }

        lambdaQueryWrapper.eq(CsEventPO::getLineId,lineId)
                .eq(CsEventPO::getType,0).between(CsEventPO::getStartTime,startDate,endDate)
                .orderByDesc(CsEventPO::getStartTime);
        List<CsEventPO> pos = this.baseMapper.selectList(lambdaQueryWrapper);
        if(CollUtil.isNotEmpty(pos)){
            dataGroupEventVOList = BeanUtil.copyToList(pos,DataGroupEventVO.class);
        }
        return dataGroupEventVOList;
    }

    @Override
    public List<CsEventPO> queryByModelId(DataParam param) {
        List<CsEventPO> pos = new ArrayList<>();
        LambdaQueryWrapper<CsEventPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(CsEventPO::getLineId,param.getLineId())
                .eq(CsEventPO::getClDid,param.getClDid())
                .between(CsEventPO::getStartTime,param.getStartTime()+" 00:00:00",param.getEndTime()+" 23:59:59")
                .orderByDesc(CsEventPO::getStartTime);
        //判断当前用户，管理员查看所有告警；普通用户可以查看二级、三级告警(一级看不了)
        String userRole = RequestUtil.getUserRole();
        String trimmedString = userRole.substring(1, userRole.length() - 1).replace("\"", "");
        List<String> list = Arrays.asList(trimmedString.split(","));
        if (!list.contains("tourist")) {
            pos = this.baseMapper.selectList(lambdaQueryWrapper);
            if (list.contains("app_vip_user")) {
                pos = pos.stream()
                        .filter(entity -> entity.getLevel() != 1)
                        .collect(Collectors.toList());
            }
        }
        pos = list.contains("tourist") ? new ArrayList<>() : pos;
        return pos;
    }

    @Override
    @DSTransactional
    public void addCldEvent(CldEventParam param) {
        //校验是新增时间还是更新文件信息
        LambdaQueryWrapper<CsEventPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(CsEventPO::getLineId,param.getMonitorId()).eq(CsEventPO::getStartTime,param.getStartTime());
        CsEventPO event = this.getOne(lambdaQueryWrapper);
        if (Objects.isNull(event)) {
            //首次录入
            //根据监测点获取装置信息
            CsLinePO po = csLineFeignClient.getById(param.getMonitorId()).getData();
            CsEventPO eventPo = new CsEventPO();
            String uuid = IdUtil.simpleUUID();
            eventPo.setId(uuid);
            eventPo.setLineId(param.getMonitorId());
            eventPo.setDeviceId(po.getDeviceId());
            LocalDateTime time = LocalDateTime.parse(param.getStartTime(), DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_MS_PATTERN));
            eventPo.setStartTime(time);
            eventPo.setTag(getTag(param.getEventType()));
            eventPo.setType(0);
            eventPo.setClDid(po.getClDid());
            eventPo.setLevel(2);
            eventPo.setProcess(4);
            eventPo.setPersistTime(param.getDuration());
            eventPo.setAmplitude(param.getAmplitude() * 100);
            eventPo.setPhase(param.getPhase());
            String dropZone = eventAnalysisService.determineDropZone(String.valueOf(param.getAmplitude() * 100),String.valueOf(param.getDuration()));
            eventPo.setLandPoint(dropZone);
            if (param.getEventType() == 1) {
                if (!Objects.isNull(param.getDuration()) && !Objects.isNull(param.getAmplitude())) {
                    eventPo.setSeverity(Double.valueOf(getYzd(param.getDuration()*1000,param.getAmplitude())));
                }
            }

            this.baseMapper.insert(eventPo);
            //influxDB数据录入
            List<String> records = new ArrayList<String>();
            Map<String, String> tags = new HashMap<>();
            tags.put(InfluxDBTableConstant.UUID,uuid);
            Map<String,Object> fields = new HashMap<>();
            fields.put("Evt_Param_Phase",param.getPhase());
            fields.put("Evt_Param_Tm",param.getDuration());
            fields.put("Evt_Param_VVaDepth",param.getAmplitude() * 100);
            Point point = influxDbUtils.pointBuilder("evt_data", time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()/1000, TimeUnit.SECONDS, tags, fields);
            BatchPoints batchPoints = BatchPoints.database(influxDbUtils.getDbName()).retentionPolicy("").consistency(InfluxDB.ConsistencyLevel.ALL).build();
            batchPoints.point(point);
            records.add(batchPoints.lineProtocol());
            if (CollectionUtil.isNotEmpty(records)) {
                influxDbUtils.batchInsert(influxDbUtils.getDbName(), "", InfluxDB.ConsistencyLevel.ALL, TimeUnit.MILLISECONDS, records);
            }
            //同步数据到 r_mp_event_detail
            insertEvent(uuid, param, time, eventPo.getSeverity());

            //获取台账信息
            DevDetailDTO devDetailDto = csLedgerFeignclient.queryDevDetail(po.getDeviceId()).getData();
            LocalDateTime eventTime = LocalDateTime.parse(param.getStartTime(), DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_MS_PATTERN));

            Double amplitudePercent = BigDecimal.valueOf(param.getAmplitude() * 100).setScale(2, RoundingMode.HALF_UP).doubleValue();
            Double durationSeconds = BigDecimal.valueOf(param.getDuration()).setScale(2, RoundingMode.HALF_UP).doubleValue();

            //异步处理事件用户关系和App消息推送
            try {
                MsgSendParam msgSendParam = new MsgSendParam();
                msgSendParam.setEventType(1);
                msgSendParam.setType("2");
                msgSendParam.setDevId(po.getDeviceId());
                msgSendParam.setEventName(getTag(param.getEventType()));
                msgSendParam.setEventTime(eventTime);
                msgSendParam.setId(uuid);
                msgSendParam.setNDid(devDetailDto.getNDid());
                msgSendParam.setAmplitude(amplitudePercent);
                msgSendParam.setPersistTime(durationSeconds);
                msgSendParam.setDropZone(dropZone);
                msgSendFeignClient.appMsgSend(msgSendParam);
            } catch (Exception e) {
                log.error("异步保存事件用户和通知失败，事件ID: {}", uuid, e);
            }

            //如果是暂降事件，异步发送短信通知
            if (param.getEventType() == 1) {
                try {
                    MsgSendParam msgSendParam = new MsgSendParam();
                    msgSendParam.setDevId(po.getDeviceId());
                    msgSendParam.setEventTime(eventTime);
                    msgSendParam.setAmplitude(amplitudePercent);
                    msgSendParam.setPersistTime(durationSeconds);
                    msgSendParam.setDropZone(dropZone);
                    msgSendFeignClient.smsMsgSend(msgSendParam);
                } catch (Exception e) {
                    log.error("异步发送短信通知失败，事件ID: {}", uuid, e);
                }
            }
        } else {
            if (StrUtil.isNotBlank(param.getWavePath())) {
                EventAnalysisDTO var1 = new EventAnalysisDTO();
                var1.setWlFilePath(param.getWavePath());
                EventAnalysisDTO dto = eventCauseFeignClient.analysisCauseAndType(var1).getData();
                List<DictData> list1 = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.EVENT_REASON.getCode()).getData();
                String id1 = list1.stream()
                        .filter(item -> Objects.equals(item.getAlgoDescribe(), dto.getCause()))
                        .map(DictData::getId)
                        .findFirst()
                        .orElse(null);
                List<DictData> list2 = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.EVENT_TYPE.getCode()).getData();
                String id2 = list2.stream()
                        .filter(item -> Objects.equals(item.getAlgoDescribe(), dto.getType()))
                        .map(DictData::getId)
                        .findFirst()
                        .orElse(null);
                //更新文件信息
                //先校验两份文件的名称是否一致
                this.lambdaUpdate()
                        .eq(CsEventPO::getLineId,param.getMonitorId())
                        .eq(CsEventPO::getStartTime,param.getStartTime())
                        .set(CsEventPO::getWavePath,param.getWavePath())
                        .set(CsEventPO::getAdvanceReason,id1)
                        .set(CsEventPO::getAdvanceType,id2)
                        .update();
                //更新文件信息
                updateEvent(param,id1,id2);
                //生成移动端图片
                this.getWavePics(event.getId(),2);
            }
        }
    }

    @Override
    public Boolean updateEventById(String id) {
        boolean result = false;
        CsEventPO po = this.baseMapper.selectById(id);
        if (po != null) {
            LambdaQueryWrapper<RmpEventDetailPO> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(RmpEventDetailPO::getEventId,id);
            RmpEventDetailPO po2 = wlRmpEventDetailMapper.selectOne(wrapper);
            po2.setWavePath(po.getWavePath());
            po2.setAdvanceReason(po.getAdvanceReason());
            po2.setAdvanceType(po.getAdvanceType());
            po2.setFileFlag(1);
            if (!Objects.isNull(po.getAdvanceReason()) && !Objects.isNull(po.getAdvanceType())) {
                po2.setDealFlag(1);
            }
            int row = wlRmpEventDetailMapper.updateById(po2);
            if (row > 0) {
                result = true;
            }
        }
        return result;
    }

    public void insertEvent(String uuid, CldEventParam param, LocalDateTime time, Double severity) {
        RmpEventDetailPO rmpEventDetailPO = new RmpEventDetailPO();
        rmpEventDetailPO.setEventId(uuid);
        rmpEventDetailPO.setMeasurementPointId(param.getMonitorId());
        rmpEventDetailPO.setStartTime(time);
        rmpEventDetailPO.setEventType(getEventType(param.getEventType()));
        rmpEventDetailPO.setFeatureAmplitude(param.getAmplitude() * 100);
        rmpEventDetailPO.setDuration(param.getDuration());
        rmpEventDetailPO.setEventDescribe(getTag(param.getEventType()));
        rmpEventDetailPO.setPhase(param.getPhase());
        rmpEventDetailPO.setDealFlag(0);
        rmpEventDetailPO.setFileFlag(0);
        rmpEventDetailPO.setSeverity(severity);
        wlRmpEventDetailMapper.insert(rmpEventDetailPO);
    }

    public void updateEvent(CldEventParam param,String advanceReason, String advanceType) {
        LambdaQueryWrapper<RmpEventDetailPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RmpEventDetailPO::getMeasurementPointId,param.getMonitorId()).eq(RmpEventDetailPO::getStartTime,param.getStartTime());
        RmpEventDetailPO po = wlRmpEventDetailMapper.selectOne(wrapper);
        po.setWavePath(param.getWavePath());
        po.setAdvanceReason(advanceReason);
        po.setAdvanceType(advanceType);
        po.setFileFlag(1);
        if (!Objects.isNull(advanceReason) && !Objects.isNull(advanceType)) {
            po.setDealFlag(1);
        }
        wlRmpEventDetailMapper.updateById(po);
    }


    @Override
    public List<CsEventPO> getEventByTime(List<String> lineList, String startTime, String endTime) {
        LambdaQueryWrapper<CsEventPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.between(CsEventPO::getStartTime,startTime,endTime)
                .eq(CsEventPO::getType,0)
                .isNull(CsEventPO::getWavePath);
        if (CollUtil.isNotEmpty(lineList)) {
            lambdaQueryWrapper.in(CsEventPO::getLineId,lineList);
        }
        return this.list(lambdaQueryWrapper);
    }

    @Override
    public List<CsWarnDescVO> getEventDesc(List<String> lineIdList) {
        List<CsWarnDescVO> csWarnDescVOList = new ArrayList<>();
        if(CollUtil.isEmpty(lineIdList)){
            return csWarnDescVOList;
        }
        /**
         * todo.. 目前且先查最近的60条
         */
        csWarnDescVOList = this.baseMapper.getEventDesc(lineIdList,60);
        // 处理下描述
        for (CsWarnDescVO csWarnDescVO : csWarnDescVOList) {
            String desc;
            if(csWarnDescVO.getAmplitude() <= 90) {
                desc = "降至";
            } else if (csWarnDescVO.getAmplitude() >= 110) {
                desc = "升至";
            } else {
                desc = "维持";
            }
            csWarnDescVO.setEventDesc("电压暂态事件，持续时间"+csWarnDescVO.getPersistTime()+"秒，电压"+desc+csWarnDescVO.getAmplitude()+"%。");
        }
        return csWarnDescVOList;
    }

    @Override
    public List<CsEventPO> getEvents(List<String> idList) {
        LambdaQueryWrapper<CsEventPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CsEventPO::getId,idList);
        return this.list(wrapper);
    }

    @Override
    public List<CsEventPO> getDevAlarmList(CsEventUserQueryParam param) {
        LambdaQueryWrapper<CsEventPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(CsEventPO::getStartTime,param.getStartTime(),param.getEndTime())
                .eq(CsEventPO::getType,3)
                .in(CsEventPO::getLevel,Arrays.asList(1,2,3,6,7))
                .in(CsEventPO::getDeviceId,param.getTarget());
        return this.list(wrapper);
    }

    @Override
    public IPage<CsEventLogsVo> getFrontDebugLogs(EventStatisticParam param) {
        IPage<CsEventLogsVo> result = new Page<>(param.getPageNum(), param.getPageSize());
        List<CsEventLogsVo> voRecords = new ArrayList<>();
        IPage<CsEventPO> page = new Page<>(param.getPageNum(), param.getPageSize());
        //获取所有监测点信息
        List<CsLineDTO> lineList = csLineFeignClient.getAllLineDetail().getData();
        if (CollectionUtil.isEmpty(lineList)) {
            return result;
        }
        Map<String,List<CsLineDTO>> devMap = lineList.stream().collect(Collectors.groupingBy(CsLineDTO::getDeviceId));
        Map<String, CsLineDTO> lineMap = lineList.stream().collect(Collectors.toMap(CsLineDTO::getLineId, Function.identity()));

        List<String> lineIds = new ArrayList<>();
        List<String> devIds = new ArrayList<>();

        //根据关键字筛选（用户可能输入监测点名称或设备名称）
        if (StringUtil.isNotBlank(param.getSearchValue())) {
            String keyword = param.getSearchValue();
            lineIds = lineList.stream()
                    .filter(item -> item.getName().contains(keyword))
                    .map(CsLineDTO::getLineId)
                    .distinct()
                    .collect(Collectors.toList());
            devIds = lineList.stream()
                    .filter(item -> item.getDeviceName().contains(keyword))
                    .map(CsLineDTO::getDeviceId)
                    .distinct()
                    .collect(Collectors.toList());
            //关键字筛选后没有任何匹配，返回空页
            if (CollectionUtil.isEmpty(lineIds) && CollectionUtil.isEmpty(devIds)) {
                return result;
            }
        }

        LambdaQueryWrapper<CsEventPO> queryWrapper = new LambdaQueryWrapper<>();
        //时间范围和类型是通用条件
        queryWrapper
                .between(CsEventPO::getStartTime,
                        DateUtil.beginOfDay(DateUtil.parse(param.getSearchBeginTime())).toString(),
                        DateUtil.endOfDay(DateUtil.parse(param.getSearchEndTime())).toString())
                .in(CsEventPO::getLevel, Arrays.asList(4, 5, 6, 7))
                .eq(CsEventPO::getType, 3)
                .orderByDesc(CsEventPO::getStartTime);

        //关键字筛选：监测点名称 OR 设备名称
        if (StringUtil.isNotBlank(param.getSearchValue())) {
            List<String> finalLineIds = lineIds;
            List<String> finalDevIds = devIds;
            queryWrapper.and(i -> {
                if (CollUtil.isNotEmpty(finalLineIds)) {
                    i.in(CsEventPO::getLineId, finalLineIds);
                }
                if (CollUtil.isNotEmpty(finalDevIds)) {
                    i.or().in(CsEventPO::getDeviceId, finalDevIds);
                }
            });
        }

        //告警等级筛选
        if (StringUtil.isNotBlank(param.getLevel())) {
            List<Integer> levelList = Arrays.stream(param.getLevel().split(","))
                    .map(String::trim)
                    .filter(s -> !s.isEmpty())
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(levelList)) {
                queryWrapper.in(CsEventPO::getLevel, levelList);
            }
        }
        page = csEventPOMapper.selectPage(page, queryWrapper);
        List<CsEventPO> records = page.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            records.forEach(item->{
                CsEventLogsVo vo = new CsEventLogsVo();
                BeanUtils.copyProperties(item,vo);
                if (!Objects.isNull(item.getLineId())) {
                    vo.setLineName(lineMap.get(item.getLineId()).getName());
                }
                if (!Objects.isNull(item.getDeviceId())) {
                    vo.setDeviceName(devMap.get(item.getDeviceId()).get(0).getDeviceName());
                }
                vo.setEvent(item.getTag());
                voRecords.add(vo);
            });
        }
        result.setRecords(voRecords);
        result.setTotal(page.getTotal());
        result.setSize(page.getSize());
        result.setCurrent(page.getCurrent());
        result.setPages(page.getPages());
        return result;
    }

    @Override
    public CsEventReasonAndTypeVo eventReasonAndTypeStatistics(EventStatisticParam param) {
        CsEventReasonAndTypeVo vo = new CsEventReasonAndTypeVo();
        String begin = DateUtil.beginOfDay(DateUtil.parse(param.getSearchBeginTime())).toString();
        String end = DateUtil.endOfDay(DateUtil.parse(param.getSearchEndTime())).toString();
        DictData data1 = dicDataFeignClient.getDicDataByCode(DicDataEnum.RESON_REST.getCode()).getData();
        DictData data2 = dicDataFeignClient.getDicDataByCode(DicDataEnum.TYPE_REST.getCode()).getData();
        vo.setEventReason(csEventPOMapper.getReasonStatistics(begin, end, data1.getId()));
        vo.setEventType(csEventPOMapper.getTypeStatistics(begin, end, data2.getId()));
        return vo;
    }

    public String getTag(Integer type) {
        String tag;
        switch (type) {
            case 1:
                tag = "Evt_Sys_DipStr";
                break;
            case 2:
                tag = "Evt_Sys_SwlStr";
                break;
            case 3:
                tag = "Evt_Sys_IntrStr";
                break;
            case 4:
                tag = "Transient";
                break;
            default:
                tag = "Un_Know";
                break;
        }
        return tag;
    }

    public String getEventType(Integer type) {
        String tag;
        switch (type) {
            case 1:
                DictData dip = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_DIP.getCode()).getData();
                tag = dip.getId();
                break;
            case 2:
                DictData rise = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_RISE.getCode()).getData();
                tag = rise.getId();
                break;
            case 3:
                DictData interruptions = dicDataFeignClient.getDicDataByCode(DicDataEnum.SHORT_INTERRUPTIONS.getCode()).getData();
                tag = interruptions.getId();
                break;
            default:
                tag = "Un_Know";
                break;
        }
        return tag;
    }


    /**
     * @return WaveDataDTO
     * @date 2023/9/21 15:59
     * param iType == 0  高级算法的要求，采样率只能是32-128
     * iType == 1  普通展示，采样率按照cfg里面最小的（大于32）
     * iType == 2  App抽点要求，采样率抽点成32
     * iType == 3  高级算法原始波形（大于32）
     */
    @Override
    public WaveDataDTO analyseWave(String eventId, int iType, Boolean isApp) {
        WaveDataDTO waveDataDTO;
        //获取暂降事件
        CsEventPO eventDetail = this.baseMapper.selectById(eventId);
        String waveName = eventDetail.getWavePath();
        if (StrUtil.isBlank(waveName)) {
            throw new BusinessException(WaveFileResponseEnum.ANALYSE_WAVE_NOT_FOUND);
        }
        String cfgPath = waveName.concat(GeneralConstant.CFG), datPath = waveName.concat(GeneralConstant.DAT);
        //不同设备，波形文件后缀大小写不一致，针对大小写判断文件是否存在
        boolean result1 = minIoUtils.checkFileIsExist(minIossProperties.getBucket(), cfgPath);
        if (!result1) {
            cfgPath = waveName.concat(GeneralConstant.CFG_LOWER);
        }
        boolean result2 = minIoUtils.checkFileIsExist(minIossProperties.getBucket(), datPath);
        if (!result2) {
            datPath = waveName.concat(GeneralConstant.DAT_LOWER);
        }

        System.out.println("波形路径-------------------" + cfgPath);
        try (
                InputStream cfgStream = fileStorageUtil.getFileStream(cfgPath);
                InputStream datStream = fileStorageUtil.getFileStream(datPath)
        ) {
            if (Objects.isNull(cfgStream) || Objects.isNull(datStream)) {
                throw new BusinessException(WaveFileResponseEnum.ANALYSE_WAVE_NOT_FOUND);
            }
            //持续时间超过30S时，iType调整为2，按32采样率采集数据
            if (Objects.nonNull(eventDetail.getPersistTime()) && eventDetail.getPersistTime() > 30) {
                iType = 2;
            }
            waveDataDTO = waveFileComponent.getComtrade(cfgStream, datStream, iType);
        } catch (Exception e) {
            throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
        }
        waveDataDTO = waveFileComponent.getValidData(waveDataDTO);
        if (!Objects.isNull(isApp) && isApp) {
            //根据持续时间裁剪波形数据，只保留开始段和结束段的关键数据
            filterWaveDataByPersistTime(waveDataDTO, eventDetail.getPersistTime());
        }
        List<CsLinePO> csLinePOList = csLineFeignClient.queryLineById(Stream.of(eventDetail.getLineId()).collect(Collectors.toList())).getData();
        if (CollectionUtil.isEmpty(csLinePOList)) {
            throw new BusinessException(AlgorithmResponseEnum.LINE_DATA_MISS);
        }
        waveDataDTO.setPtType(csLinePOList.get(0).getConType());
        CsLinePO firstLine = csLinePOList.get(0);
        double pt2Ratio = Optional.ofNullable(firstLine.getPt2Ratio()).orElse(1.0);
        waveDataDTO.setPt(firstLine.getPtRatio() / pt2Ratio);
        double ct2Ratio = Optional.ofNullable(firstLine.getCt2Ratio()).orElse(1.0);
        waveDataDTO.setCt(firstLine.getCtRatio() / ct2Ratio);
        waveDataDTO.setMonitorName(csLinePOList.get(0).getName());
        return waveDataDTO;
    }

    @Override
    public CsEventVO getWavePics(String eventId, int iType) {
        CsEventPO eventDetail = this.baseMapper.selectById(eventId);
        if (StrUtil.isBlank(eventDetail.getInstantPics())) {
            //获取波形数据。然后绘图
            WaveDataDTO waveDataDTO = this.analyseWave(eventId, iType ,true);
            Integer nPush = Objects.isNull(waveDataDTO.getComtradeCfgDTO().getNPush())? 100: waveDataDTO.getComtradeCfgDTO().getNPush();
            //数据筛选,如果是双路电压的话，会存在2个波形数据
            List<WaveDataDetail> waveDataDetails = WaveUtil.filterWaveData(waveDataDTO);
            //单通道处理
            if (ObjectUtils.isNotEmpty(waveDataDetails) && waveDataDetails.size() == 2) {
                String instantPath = wavePicComponent.generateImageShun(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
                eventDetail.setInstantPics(instantPath);
                if (StrUtil.isBlank(eventDetail.getRmsPics())) {
                    String rmsPath = wavePicComponent.generateImageRms(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
                    eventDetail.setRmsPics(rmsPath);
                }
            }
            //双通道处理
            else if (ObjectUtils.isNotEmpty(waveDataDetails) && waveDataDetails.size() == 4) {
                String instantPath = wavePicComponent.generateInstantImageZl(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
                eventDetail.setInstantPics(instantPath);
                if (StrUtil.isBlank(eventDetail.getRmsPics())) {
                    String rmsPath = wavePicComponent.generateRmsImageZl(nPush,waveDataDetails,eventDetail.getPersistTime()*1000);
                    eventDetail.setRmsPics(rmsPath);
                }
            }
            this.updateById(eventDetail);
        }
        CsEventVO csEventVO = new CsEventVO();
        BeanUtil.copyProperties(eventDetail, csEventVO);
        return csEventVO;
    }

    private void filterWaveDataByPersistTime(WaveDataDTO waveDataDTO, Double persistTime) {
        if (persistTime == null || persistTime <= 0) {
            return;
        }
        Integer nPush = waveDataDTO.getComtradeCfgDTO().getNPush();
        if (Objects.isNull(nPush)) {
            nPush = 100;
        }

        double persistTimeMs = persistTime * 1000;

        // 开始段：波形起始前后
        double startSegBegin = -nPush;
        double startSegEnd = nPush;

        // 结束段：恢复点前后
        double endSegBegin = persistTimeMs - nPush;
        double endSegEnd = persistTimeMs + nPush;

        List<List<Float>> originalWaveData = waveDataDTO.getListWaveData();
        List<List<Float>> originalRmsData = waveDataDTO.getListRmsData();

        if (originalWaveData.isEmpty()) {
            return;
        }

        int waveColumnCount = originalWaveData.get(0).size();
        int rmsColumnCount = originalRmsData.get(0).size();
        boolean isMerged = endSegBegin <= startSegEnd;

        List<List<Float>> filteredWaveData;
        List<List<Float>> filteredRmsData;

        if (isMerged) {
            // 两段重叠或相邻，合并为一段连续数据，不做抽点
            double mergedEnd = Math.max(startSegEnd, endSegEnd);
            filteredWaveData = filterByTimeRange(originalWaveData, startSegBegin, mergedEnd);
            filteredRmsData = filterByTimeRange(originalRmsData, startSegBegin, mergedEnd);
        } else {
            // 两段分离，各自按时间范围裁剪，不做抽点
            List<List<Float>> seg1Wave = filterByTimeRange(originalWaveData, startSegBegin, startSegEnd);
            List<List<Float>> seg2Wave = filterByTimeRange(originalWaveData, endSegBegin, endSegEnd);
            List<List<Float>> seg1Rms = filterByTimeRange(originalRmsData, startSegBegin, startSegEnd);
            List<List<Float>> seg2Rms = filterByTimeRange(originalRmsData, endSegBegin, endSegEnd);

            filteredWaveData = new ArrayList<>();
            filteredRmsData = new ArrayList<>();

            // 段1非空才加入
            if (!seg1Wave.isEmpty()) {
                filteredWaveData.addAll(seg1Wave);
            }
            if (!seg1Rms.isEmpty()) {
                filteredRmsData.addAll(seg1Rms);
            }
            // 两段都非空时才补null间隔
            boolean waveGapNeeded = !seg1Wave.isEmpty() && !seg2Wave.isEmpty();
            boolean rmsGapNeeded = !seg1Rms.isEmpty() && !seg2Rms.isEmpty();

            if (waveGapNeeded) {
                double waveGapStart = getTimeValue(seg1Wave.get(seg1Wave.size() - 1));
                double waveGapEnd = getTimeValue(seg2Wave.get(0));
                List<List<Float>> gapWaveData = createNullGapData(waveGapStart, waveGapEnd, waveGapNullPoints, waveColumnCount);
                filteredWaveData.addAll(gapWaveData);
            }
            if (rmsGapNeeded) {
                double rmsGapStart = getTimeValue(seg1Rms.get(seg1Rms.size() - 1));
                double rmsGapEnd = getTimeValue(seg2Rms.get(0));
                List<List<Float>> gapRmsData = createNullGapData(rmsGapStart, rmsGapEnd, waveGapNullPoints, rmsColumnCount);
                filteredRmsData.addAll(gapRmsData);
            }

            // 段2非空才加入
            if (!seg2Wave.isEmpty()) {
                filteredWaveData.addAll(seg2Wave);
            }
            if (!seg2Rms.isEmpty()) {
                filteredRmsData.addAll(seg2Rms);
            }
        }
        waveDataDTO.setListWaveData(filteredWaveData);
        waveDataDTO.setListRmsData(filteredRmsData);
    }

    private double getTimeValue(List<Float> data) {
        return data.get(0).doubleValue();
    }

    /**
     * 在两段数据的间隔区域补固定数量的null数据点
     * null点的时间在gapStart和gapEnd之间均匀分布
     * 每个null点：[时间, null, null, ...]，echarts遇到null值会渲染为断线
     *
     * @param gapStart      段1最后一个点时间(ms)
     * @param gapEnd        段2第一个点时间(ms)
     * @param nullPointCount 补null点的数量
     * @param columnCount    每行数据列数（含时间列）
     */
    private List<List<Float>> createNullGapData(double gapStart, double gapEnd, int nullPointCount, int columnCount) {
        List<List<Float>> gapData = new ArrayList<>();
        if (gapEnd <= gapStart || nullPointCount <= 0) {
            return gapData;
        }
        // 将gapStart到gapEnd之间均匀分成 nullPointCount+1 段
        // null点放在中间的 nullPointCount 个位置，两端留给段1末点和段2首点
        double step = (gapEnd - gapStart) / (nullPointCount + 1);
        for (int i = 1; i <= nullPointCount; i++) {
            double time = gapStart + step * i;
            List<Float> nullPoint = new ArrayList<>();
            nullPoint.add((float) (Math.round(time * 100.0) / 100.0));
            for (int j = 1; j < columnCount; j++) {
                nullPoint.add(null);
            }
            gapData.add(nullPoint);
        }
        return gapData;
    }
    /**
     * 按时间范围筛选数据列表
     *
     * @param dataList  原始数据列表，每行第一个元素为时间坐标(ms)
     * @param startTime 起始时间(ms)
     * @param endTime   结束时间(ms)
     * @return 范围内的数据列表
     */
    private List<List<Float>> filterByTimeRange(List<List<Float>> dataList, double startTime, double endTime) {
        return dataList.stream()
                .filter(data -> {
                    double time = ((Number) data.get(0)).doubleValue();
                    return time >= startTime && time <= endTime;
                })
                .collect(Collectors.toList());
    }

}
