package com.njcn.common.utils.wave;

import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.dto.wave.*;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * @author yxb
 * @version 1.0.0
 * @date 2022年06月02日 20:03
 * 解析comtrate文件工具类
 */
@Slf4j
public class AnalyWave {
    /******************************************
     * 调用读取comtrate文件方法
     * param strFilePath 波形.cfg文件路径
     * param iType == 0  高级算法的要求，采样率只能是32-128
     *       iType == 1  普通展示，采样率按照cfg里面最小的（大于32）
     *       iType == 2  App抽点要求，采样率抽点成32
     *       iType == 3  高级算法原始波形（大于32）
     ******************************************/
    private String strBinType;//结束读取cfg标志
    public WaveDataDTO getComtrade(String strFilePath, int iType) {
        WaveDataDTO waveDataDTO = new WaveDataDTO();
        // 首先判断文件路径是否为空
        if(StrUtil.isNotEmpty(strFilePath)){
            File file = new File(strFilePath);
            // 判断文件是否存在
            if (file.isFile() && file.exists()) {
                // 读取cfg文件
                ComtradeCfgDTO comtradeCfgDTO = getComtradeCfg(file);
                // 为空或者未找到结束符号
                if (comtradeCfgDTO == null && !strBinType.equals("BINARY"))
                    return waveDataDTO;
                /*****根据通道号计算相别**  add by yexb -----Start****
                 * 1、判断是否是3的倍数，是3的倍数则是3相
                 * 2、假如不是3的倍数 ，是1的倍数则是单相
                 ********************************************************/
                if(comtradeCfgDTO.getNAnalogNum() % 3 == 0) {
                    comtradeCfgDTO.setNPhasic(3);
                }else if(comtradeCfgDTO.getNAnalogNum() % 1 == 0) {
                    comtradeCfgDTO.setNPhasic(1);
                }else{
                    comtradeCfgDTO.setNPhasic(3);
                }
                // 给相别幅值
                waveDataDTO.setIPhasic(comtradeCfgDTO.getNPhasic());
                // 组装解析抬头
                List<String> lstWaveTitle = getWaveTitle(comtradeCfgDTO);
                // 解析.dat文件
                List<List<Float>> listWaveData = getComtradeDat(comtradeCfgDTO,strFilePath, iType);
                waveDataDTO.setComtradeCfgDTO(comtradeCfgDTO);
                waveDataDTO.setWaveTitle(lstWaveTitle);
                waveDataDTO.setListWaveData(listWaveData);

                /*****根据通道号计算相别**  add by yexb -----end****/
            }else{
                // .cfg文件不存在
                log.info("{},CFG文件不存在！",strFilePath);
            }
        }
        return waveDataDTO;
    }

    /*********************************
     * 根据波形数据算出rms值数据
     * param waveDataDTO 瞬时波形（包含了CFG配置文件）
     *  List<List<Float>>  返回RMS波形
     **********************************/
    @SuppressWarnings("unused")
    public WaveDataDTO getValidData(WaveDataDTO waveDataDTO) {
        ComtradeCfgDTO comtradeCfgDTO = waveDataDTO.getComtradeCfgDTO();//CFG 配置文件
        List<List<Float>> lstWave = waveDataDTO.getListWaveData();//瞬时波形值
        List<List<Float>> listRms = new ArrayList<>();//返回rms的值
        /*float fs = nOneWaveNum;
        int nWaveNum = (int) nAllWaveNum;*/

        int HalfTs = comtradeCfgDTO.getFinalSampleRate().intValue();// 全波有效值  (int)fs / 2;//半波有效值
        // 计算有效值算法
        /*********************************
         * modify by yexibao 2020-10-29
         * 增加多波形算法  ---------start
         ********************************/
        double iWave = 0 ;
        int nPhasic = 0;// 相别
        if(lstWave.size() > 0){
            nPhasic = comtradeCfgDTO.getNPhasic();
            //ComtradeCfg.nAnalogNum为值的个数(-1的原因是一个存的是时间)
            iWave =  Math.floor((lstWave.get(0).size() -1) / nPhasic);
            List<Float> tmpListRms;
            //每一项一项计算
            for(int j = 0; j < iWave; j ++){
                double fSumA = 0.0, fSumB= 0.0, fSumC = 0.0;
                double fValidA = 0.0, fValidB = 0.0, fValidC = 0.0;
                //循环原始数据
                for (int i = 0; i < lstWave.size(); i++) {
                    // 当第一次循环的时候实例化，其余的获取已有的List
                    List<Float> tmpListValue = lstWave.get(i); //获取每一项的值
                    if (j == 0) {
                        tmpListRms = new ArrayList<>();
                        tmpListRms.add(tmpListValue.get(0));//获取时间
                        listRms.add(tmpListRms);
                    } else
                        tmpListRms = listRms.get(i);

                    //包含了时间、电压（A、B、C）三相、电流（A、B、C）三相
                    if (tmpListValue.size() >= 2) {
                        //电压有效值算法,根据相别进行处理
                        switch(comtradeCfgDTO.getNPhasic()) {
                            case 1:
                                fSumA += Math.pow(tmpListValue.get(1 + nPhasic * j), 2);
                                if (i >= HalfTs)// 计算有效值
                                {
                                    List<Float> forwardListValue = lstWave.get(i - HalfTs);//获取前推周波的值
                                    fSumA -= Math.pow(forwardListValue.get(1 + nPhasic * j), 2);
                                }
                                fValidA = Math.sqrt(fSumA / HalfTs);

                                tmpListRms.add((float) fValidA);
                                listRms.set(i,tmpListRms);
                                break;
                            case 2:
                                fSumA += Math.pow(tmpListValue.get(1 + nPhasic * j), 2);
                                fSumB += Math.pow(tmpListValue.get(2 + nPhasic * j), 2);
                                if (i >= HalfTs)// 计算有效值
                                {
                                    List<Float> forwardListValue = lstWave.get(i - HalfTs);//获取前推周波的值
                                    fSumA -= Math.pow(forwardListValue.get(1 + nPhasic * j), 2);
                                    fSumB -= Math.pow(forwardListValue.get(2 + nPhasic * j), 2);
                                }
                                fValidA = Math.sqrt(fSumA / HalfTs);
                                fValidB = Math.sqrt(fSumB / HalfTs);

                                tmpListRms.add((float) fValidA);
                                tmpListRms.add((float) fValidB);
                                listRms.set(i,tmpListRms);
                                break;
                            case 3:
                                fSumA += Math.pow(tmpListValue.get(1 + nPhasic * j), 2);
                                fSumB += Math.pow(tmpListValue.get(2 + nPhasic * j), 2);
                                fSumC += Math.pow(tmpListValue.get(3 + nPhasic * j), 2);
                                if (i >= HalfTs)// 计算有效值
                                {
                                    List<Float> forwardListValue = lstWave.get(i - HalfTs);//获取前推周波的值
                                    fSumA -= Math.pow(forwardListValue.get(1 + nPhasic * j), 2);
                                    fSumB -= Math.pow(forwardListValue.get(2 + nPhasic * j), 2);
                                    fSumC -= Math.pow(forwardListValue.get(3 + nPhasic * j), 2);
                                }
                                fValidA = Math.sqrt(fSumA / HalfTs);
                                fValidB = Math.sqrt(fSumB / HalfTs);
                                fValidC = Math.sqrt(fSumC / HalfTs);

                                tmpListRms.add((float) fValidA);
                                tmpListRms.add((float) fValidB);
                                tmpListRms.add((float) fValidC);
                                listRms.set(i,tmpListRms);
                                break;
                        }
                    }
                }
            }

            //过滤前一个周波
            //HalfTs表示一个周波
            try {
                for (int i = 0; i < HalfTs; i++) {
                    //电压有效值算法
                    List<Float> tmpNewListRms = new ArrayList<>();//没相具体的值
                    for(int j = 0; j < iWave; j ++) {
                        if (j == 0)
                            tmpNewListRms.add(listRms.get(i).get(0));//获取时间

                        switch(nPhasic) {
                            case 1:
                                tmpNewListRms.add(listRms.get(i + HalfTs).get(1 + nPhasic * j));
                                break;
                            case 2:
                                tmpNewListRms.add(listRms.get(i + HalfTs).get(1 + nPhasic * j));
                                tmpNewListRms.add(listRms.get(i + HalfTs).get(2 + nPhasic * j));
                                break;
                            case 3:
                                tmpNewListRms.add(listRms.get(i + HalfTs).get(1 + nPhasic * j));
                                tmpNewListRms.add(listRms.get(i + HalfTs).get(2 + nPhasic * j));
                                tmpNewListRms.add(listRms.get(i + HalfTs).get(3 + nPhasic * j));
                                break;
                        }
                    }
                    listRms.set(i, tmpNewListRms);//重新赋值
                }
            } catch (Exception e) {
                // TODO 更改收个周波值出错
            }
        }
        waveDataDTO.setListRmsData(listRms);

        return waveDataDTO;
    }

    /*****************************
     *获取暂降特征值，包含离线
     * param waveDataDTO 波形返回参数
     * param blType 计算方式 true：浮动门槛  false：固定门槛
     *****************************/
    public List<EigenvalueDTO> getEigenvalue(WaveDataDTO waveDataDTO, boolean blType) {
        ComtradeCfgDTO comtradeCfgDTO = waveDataDTO.getComtradeCfgDTO();//CFG 配置文件
        List<List<Float>> lstWave = waveDataDTO.getListWaveData();// 瞬时波形值
        Long finalSampleRate = comtradeCfgDTO.getFinalSampleRate();//获取最终采样率
        List<EigenvalueDTO> lstEigenvalueDTO =new ArrayList<>();// 返回值

        // 必须包含了瞬时波形
        if (lstWave.size() > 0) {
            MutationDTO mutationDTO = getMutationValue(lstWave,finalSampleRate);
            //获取突变量和RMS值
            if(mutationDTO != null){
                if (mutationDTO.getListRms_Offline().size() > 0 && mutationDTO.getListTBL_Offline().size() > 0) {
                    lstEigenvalueDTO = getEventValue(lstWave,mutationDTO,comtradeCfgDTO,blType);
                }
            }
        }
        else{
            lstEigenvalueDTO = null;
        }
        return lstEigenvalueDTO;
    }

    /*********************************
     * 读取cfg方法
     * param strFilePath 文件路径
     * return  返回bool为是否解析出错
     **********************************/
    private String encoding = "GBK";
    private float nFreq;//comtrade频率  WW 2019-11-14
    private BufferedReader bufferedReader;
    private InputStreamReader read;
    private String strFileLine;// 每行读取内容
    private String[] strTempArray;// 每行读取内容按“,”分割
    //private ComtradeCfgDTO comtradeCfgDTO;//波形文件cfg总类
    private ComtradeCfgDTO getComtradeCfg(File file) {
        ComtradeCfgDTO comtradeCfgDTO = new ComtradeCfgDTO();
        try {
            // 将.cfg文件转换为管道流
            // Stream<String> lines = Files.lines(Paths.get(strFilePath));
            read = new InputStreamReader(new FileInputStream(file), encoding);// 考虑到编码格式
            bufferedReader = new BufferedReader(read);

            if (bufferedReader != null) {
                nFreq = 0f;//WW 2019-11-14
                // 第一行不关心仅仅是一些描述类的信息
                strFileLine = bufferedReader.readLine();
                // 第二行需要关心第二个(模拟量的个数)和第三个参数(开关量的个数)
                strFileLine = bufferedReader.readLine();
                // 按“,”进行分割
                strTempArray = strFileLine.split(",");
                // 按“,”进行分割
                for (int i = 0; i < strTempArray.length; i++) {
                    switch (i) {
                        case 0:// 总个数
                            comtradeCfgDTO.setNChannelNum(Integer.parseInt(strTempArray[i]));
                            break;
                        case 1:// 模拟量的个数
                        {
                            String str = strTempArray[i].substring(0, strTempArray[i].length() - 1);
                            comtradeCfgDTO.setNAnalogNum(Integer.parseInt(str));
                        }
                        break;
                        case 2:// 开关量的个数
                        {
                            String str = strTempArray[i].substring(0, strTempArray[i].length() - 1);
                            comtradeCfgDTO.setNDigitalNum(Integer.parseInt(str));
                        }
                        break;
                    }
                }

                // 从第三行到第ComtradeCfg.nChannelNum + 3行是模拟量通道和数字量通道
                List<AnalogDTO> lstAnalogDTO =new ArrayList<>();
                comtradeCfgDTO.setLstAnalogDTO(lstAnalogDTO);
                for (int i = 0; i < comtradeCfgDTO.getNChannelNum(); i++) {
                    AnalogDTO analogDTO = new AnalogDTO();
                    lstAnalogDTO.add(analogDTO);

                    strFileLine = bufferedReader.readLine();
                    strTempArray = strFileLine.split(",");
                    // 配置总共13项
                    for (int j = 0; j < strTempArray.length; j++) {
                        switch (j) {
                            case 0:// 通道序号
                                analogDTO.setNIndex(Integer.parseInt(strTempArray[j]));
                                break;
                            case 1:// 通道名称
                                analogDTO.setSzChannleName(strTempArray[j]);
                                break;
                            case 2:// 相位名称
                                analogDTO.setSzPhasicName(strTempArray[j]);
                                break;
                            case 3:// 监视的通道名称
                                analogDTO.setSzMonitoredChannleName(strTempArray[j]);
                                break;
                            case 4:// 通道的单位
                                analogDTO.setSzUnitName(strTempArray[j]);
                                break;
                            case 5:// 通道的系数
                                analogDTO.setFCoefficent(Float.parseFloat(strTempArray[j]));
                                break;
                            case 6:// 通道的偏移量
                                analogDTO.setFOffset(Float.parseFloat(strTempArray[j]));
                                break;
                            case 7:// 起始采样时间的偏移量
                                analogDTO.setFTimeOffset(Float.parseFloat(strTempArray[j]));
                                break;
                            case 8:// 采样值的最小值
                                analogDTO.setNMin(Integer.parseInt(strTempArray[j]));
                                break;
                            case 9:// 采样值的最大值
                                analogDTO.setNMax(Integer.parseInt(strTempArray[j]));
                                break;
                            case 10:// 一次变比
                                analogDTO.setFPrimary(Float.parseFloat(strTempArray[j]));
                                break;
                            case 11:// 二次变比
                                analogDTO.setFSecondary(Float.parseFloat(strTempArray[j]));
                                break;
                            case 12:// 一次值还是二次值标志
                                analogDTO.setSzValueType(strTempArray[j]);
                                break;
                        }
                    }
                }
                // 采样频率
                strFileLine = bufferedReader.readLine();
                float fFreq = Float.parseFloat(strFileLine);
                nFreq = (Float) fFreq;//WW 2019-11-14
                // 获取采样段数
                strFileLine = bufferedReader.readLine();
                int nRates = Integer.parseInt(strFileLine);
                comtradeCfgDTO.setNRates(nRates);
                // 获得每段的采样率
                List<RateDTO> lstRate =new ArrayList<>();//采样率
                long nOffset = 0;
                for (int i = 0; i < nRates; i++) {
                    strFileLine = bufferedReader.readLine();
                    strTempArray = strFileLine.split(",");
                    RateDTO rateDTO = new RateDTO();
                    lstRate.add(rateDTO);
                    for (int j = 0; j < strTempArray.length; j++) {
                        switch (j) {
                            case 0:// 单周波采样点数
                                rateDTO.setNOneSample((long) (Float.parseFloat(strTempArray[j]) / nFreq));//WW 2019-11-14
                                break;
                            case 1:// 总点数 //这里的strTemp是一个偏移量
                                nOffset = (long) (Float.parseFloat(strTempArray[j]) - nOffset);
                                rateDTO.setNSampleNum(nOffset);
                                break;
                        }
                    }
                }
                comtradeCfgDTO.setLstRate(lstRate);

                // 增加读取波形起始时间个结束时间
                SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS");
                // 波形起始时间
                strFileLine = bufferedReader.readLine();
                strFileLine = strFileLine.substring(0, strFileLine.length() - 3).replace(",", " ");
                comtradeCfgDTO.setTimeStart(sdf.parse(strFileLine));
                // 暂态触发时间
                strFileLine = bufferedReader.readLine();
                strFileLine = strFileLine.substring(0, strFileLine.length() - 3).replace(",", " ");
                comtradeCfgDTO.setTimeTrige(sdf.parse(strFileLine));
                // 获取触发时间的时间 + 毫秒
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(comtradeCfgDTO.getTimeTrige());
                comtradeCfgDTO.setFirstMs(calendar.get(Calendar.MILLISECOND));
                comtradeCfgDTO.setFirstTime(calendar.getTime());

                long a = comtradeCfgDTO.getTimeStart().getTime();
                long b = comtradeCfgDTO.getTimeTrige().getTime();
                int c = (int) (a - b);
                if (c >= 90 && c <= 110) {
                    comtradeCfgDTO.setNPush(100);
                } else if (c >= 190 && c <= 210) {
                    comtradeCfgDTO.setNPush(200);
                }
                // 赋值编码格式（二进制）
                strBinType = bufferedReader.readLine().toUpperCase();
                read.close();
            } else {
                //未读取到.cfg内容
                log.info("{},未读取到CFG文件内容！",file.getPath());
                comtradeCfgDTO = null;
            }
        } catch (Exception e) {
            // 解析.cfg文件出错
            log.info("{},解析CFG文件出错！",file.getPath());
            comtradeCfgDTO = null;
        }
        return comtradeCfgDTO;
    }

    /*********************************
     * 读取dat方法
     * param strFilePath  .dat访问路径
     * param strFilePath  .dat访问路径
     * param iType  访问波形类型
     *  List<List<Float>>  返回波形瞬时值
     **********************************/
    private List<List<Float>> getComtradeDat(ComtradeCfgDTO comtradeCfgDTO,String strFilePath, int iType) {
        List<List<Float>> listWaveData = new ArrayList<>();//返回数据
        // 波形文件路径由 .cfg 换成 .dat
        String strDatFilePath = getDatFilePath(strFilePath);
        float xValueAll = 0;//初始化xValue的值
        boolean blxValue = false;//判断是否首次登陆
        byte[] Array;
        try {
            //读取本地文件
            File file = new File(strDatFilePath);
            // 判断文件是否存在
            if (file.isFile() && file.exists()) {
                Array = new byte[(int) file.length()];
                FileInputStream readFile = null;
                try {
                    readFile = new FileInputStream(file);
                    readFile.read(Array, 0, Array.length);
                } catch (IOException e) {
                    // TODO 读取DAT文件失败
                }
                finally {
                    if (readFile != null) {
                        //PubUtils.safeClose(readFile,"安全关闭读取CFG文件失败，异常为：");
                    }
                }
            } else {
                // TODO 找不到DAT文件
                return listWaveData;
            }

            // 计算每个单独的数据块的大小 4字节的序号 4字节的时间 2字节的值
            // 示例中的排布是 4字节的序号 4字节的时间 UA(2字节) UB(2字节) UC(2字节) IA(2字节) IB(2字节) IC(2字节)
            int nDigSize = (comtradeCfgDTO.getNDigitalNum() % 16) > 0 ? (comtradeCfgDTO.getNDigitalNum() / 16 + 1) * 2: comtradeCfgDTO.getNDigitalNum() / 16 * 2;
            int nBlockSize = 2 * Integer.SIZE / 8 + comtradeCfgDTO.getNAnalogNum() * 2 + nDigSize;
            int nBlockNum = Array.length / nBlockSize;// 总长度除以每个快的大小
            long finalSampleRate = getFinalWaveSample(comtradeCfgDTO.getLstRate(),iType);// 获取采样率
            if (finalSampleRate != -1){
                comtradeCfgDTO.setFinalSampleRate(finalSampleRate);//设置最终采样率

                int nnInd = 0;// 计算转换后的采样率
                long nWaveNum = 0;// 抽点后总共多少点数据
                List<RateDTO> newLstRate = new ArrayList<>();//抽点后新的的采样率
                for (int iRate = 0; iRate < comtradeCfgDTO.getNRates(); iRate++) {
                    if (comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() >= 32) {
                        // 计算本段录波总共有多少波形
                        nWaveNum = comtradeCfgDTO.getLstRate().get(iRate).getNSampleNum() / comtradeCfgDTO.getLstRate().get(iRate).getNOneSample();
                        comtradeCfgDTO.setNAllWaveNum(comtradeCfgDTO.getNAllWaveNum() + nWaveNum);//设置总波形大小
                        // 将最低采样率替换到本段录波内
                        RateDTO tmpRateDTO = new RateDTO();
                        newLstRate.add(tmpRateDTO);
                        //iFlag =3 一定不进行抽点算法
                        if(iType !=3) {
                            //true 抽点算法（当前采样率跟统一采样率不一样则是抽点，否则是未抽点）
                            if (comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() != comtradeCfgDTO.getFinalSampleRate()) {
                                newLstRate.get(nnInd).setNOneSample(comtradeCfgDTO.getFinalSampleRate());
                                // 计算本段录波按照最低采样点应该有多少录波
                                newLstRate.get(nnInd).setNSampleNum(comtradeCfgDTO.getFinalSampleRate() * nWaveNum);
                            } else {
                                newLstRate.get(nnInd).setNOneSample(comtradeCfgDTO.getLstRate().get(iRate).getNOneSample());
                                // 计算本段录波按照最低采样点应该有多少录波
                                newLstRate.get(nnInd).setNSampleNum(comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() * nWaveNum);
                            }
                        } else{
                            newLstRate.get(nnInd).setNOneSample(comtradeCfgDTO.getLstRate().get(iRate).getNOneSample());
                            // 计算本段录波按照最低采样点应该有多少录波
                            newLstRate.get(nnInd).setNSampleNum(comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() * nWaveNum);
                        }

                        // 正常的配置中采样率
                        comtradeCfgDTO.getLstRate().get(nnInd).setNOneSample(comtradeCfgDTO.getLstRate().get(iRate).getNOneSample());
                        comtradeCfgDTO.getLstRate().get(nnInd).setNSampleNum(comtradeCfgDTO.getLstRate().get(iRate).getNSampleNum());
                        nnInd++;
                    }
                }

                long nOffSet = 0,nWaveSpan = 0;// 偏移量,采样间隔
                float fValue = 0f,dfValue = 0f;//两个点之间的时间差
                int nIndex = 0;// 计算不同块的采样率
                // 将最低采样率替换到本段录波内
                RateDTO tmpRateDTO = new RateDTO();// .CFG中采样率

                // nBlockNum 总循环次数
                for (int i = 0; i < nBlockNum; i++) {
                    tmpRateDTO = comtradeCfgDTO.getLstRate().get(nIndex);
                    // 计算本段抽点采样间隔
                    nWaveSpan = tmpRateDTO.getNOneSample() / newLstRate.get(nIndex).getNOneSample();
                    // 判断是否进入下一段
                    if (i == tmpRateDTO.getNSampleNum() + nOffSet) {
                        nOffSet += tmpRateDTO.getNSampleNum();
                        nIndex++;
                        if (nIndex == nnInd)
                            break;
                    }
                    dfValue = (float) 20 / tmpRateDTO.getNOneSample();
                    // 判断是否到了需要抽的采样点
                    if (i % nWaveSpan == 0) {
                        // 计算每个通道的值
                        List<Float> tmpWaveData = new ArrayList<Float>();//存储局部数据集合，包含了时间，A，B，C三相
                        AnalogDTO tmpAnalogDTO =new AnalogDTO();
                        for (int j = 0; j < comtradeCfgDTO.getNAnalogNum(); j++) {
                            tmpAnalogDTO = comtradeCfgDTO.getLstAnalogDTO().get(j);

                            //数据只有电压ABC三相数据，不展示U0、I0等数据 YXB2020-10-09 去除相别为N相的数据
                            if (tmpAnalogDTO.getSzPhasicName().toUpperCase().equals("N"))
                                break;

                            float fCoef = tmpAnalogDTO.getFCoefficent();
                            fValue = BitConverter.byte2ToUnsignedShort(Array, i * nBlockSize + 2 * 4 + j * 2) * fCoef;

                            //WW 2019-11-14
                            if (tmpAnalogDTO.getSzValueType().toUpperCase().equals("S"))//P是一次值 S是二次值
                            {
                                if (tmpAnalogDTO.getSzUnitName().toUpperCase().equals("KV"))//判断单位是V还是kV
                                    fValue = fValue*1000.0f;
                                else
                                    fValue = fValue;
                            }
                            else if (tmpAnalogDTO.getSzValueType().toUpperCase().equals("P"))//P是一次值 S是二次值
                            {
                                if (tmpAnalogDTO.getSzUnitName().toUpperCase().equals("V"))//判断单位是V还是kV
                                {
                                    if (tmpAnalogDTO.getFPrimary() != 0.0f)//根据cfg内的变比，将一次值转换成二次值
                                        fValue = fValue * tmpAnalogDTO.getFSecondary() / tmpAnalogDTO.getFPrimary();
                                    else
                                        fValue = fValue;
                                }
                                else if(tmpAnalogDTO.getSzUnitName().toUpperCase().equals("KV"))//判断单位是V还是kV
                                {
                                    if (tmpAnalogDTO.getFPrimary() != 0.0f)//根据cfg内的变比，将一次值转换成二次值
                                        fValue = fValue * 1000.0f * tmpAnalogDTO.getFSecondary() / tmpAnalogDTO.getFPrimary();
                                    else
                                        fValue = fValue;
                                }
                                else //还有可能是电流，单位是A
                                {
                                    if (tmpAnalogDTO.getFPrimary() != 0.0f)//根据cfg内的变比，将一次值转换成二次值
                                        fValue = tmpAnalogDTO.getFSecondary() / tmpAnalogDTO.getFPrimary();
                                    else
                                        fValue= fValue;
                                }
                            }

                            //ComtradeCfg.OneChannleCfg.get(j)

                            //xValue前移量，假如是第一次时候则需要前移
                            if (!blxValue && j == 0) {
                                xValueAll = (float) (i * 20) / tmpRateDTO.getNOneSample() - comtradeCfgDTO.getNPush();
                                blxValue = true;
                                //只增加一个xValue的值
                                tmpWaveData.add((float) (Math.round(xValueAll * 100)) / 100);//增加时间值
                            } else if (j == 0) {
                                xValueAll += (float) nWaveSpan * dfValue;
                                //只增加一个xValue的值
                                tmpWaveData.add((float) (Math.round(xValueAll * 100)) / 100);//增加时间值
                            }

                            //不同通道yValue的值都需要增加，最终成ABC三相
                            tmpWaveData.add((float) (Math.round(fValue * 100)) / 100);//每个通道的值
                        }
                        listWaveData.add(tmpWaveData);//把每个单独的值赋予到整体里面去
                    }
                }
            }
        } catch (Exception e) {
            // TODO 读取文件出错
            return listWaveData;
        }

        return listWaveData;
    }

    /*********************************
     * 获取波形标题的方法
     * param tmpComtradeCfgDTO 文件路径
     * return  返回List<String>返回数据格式说明
     **********************************/
    private List<String> getWaveTitle(ComtradeCfgDTO comtradeCfgDTO){
        //编辑数据标题 YXB2020-10-09 去除相别为N相的数据//存储数据标题
        List<String> tmpWaveTitle = new ArrayList<>();
        // 模拟量通道记录类
        AnalogDTO analogDTO = new AnalogDTO();
        tmpWaveTitle.add("Time");
        String  strUnit = "U";
        for (int j = 0; j <comtradeCfgDTO.getNAnalogNum(); j++) {
            analogDTO = comtradeCfgDTO.getLstAnalogDTO().get(j);
            // 假如为N相则跳过
            if (StrUtil.equals(analogDTO.getSzPhasicName().toUpperCase(),"N") ) {
                continue;
            } else {
                switch (analogDTO.getSzUnitName().toUpperCase()){
                    case "KV":
                        strUnit = "U";
                        break;
                    case "V":
                        strUnit = "U";
                        break;
                    case "A":
                        strUnit = "I";
                        break;
                    default:
                        strUnit = "U";
                        break;
                }
                tmpWaveTitle.add(strUnit + analogDTO.getSzPhasicName().toUpperCase()+"相");
            }
        }
        return tmpWaveTitle;
    }

    /*********************************
     * 由.cfg 路径更换成 .dat
     * param strFilePath 文件路径
     * return  String返回.dat文件的路径
     **********************************/
    private String getDatFilePath(String strFilePath){
        String strDatFilePath;
        String strOriginally=".cfg";//替换前的
        String strReplace=".dat";//替换后的
        String strIntercept = strFilePath.substring(strFilePath.lastIndexOf(".") + 1);//截取.之后字符串
        switch(strIntercept)
        {
            case "cfg":
                strOriginally=".cfg";
                strReplace=".dat";
                break;
            case "CFG":
                strOriginally=".CFG";
                strReplace=".DAT";
                break;
            case "Cfg":
                strOriginally=".Cfg";
                strReplace=".Dat";
                break;
            case "CFg":
                strOriginally=".CFg";
                strReplace=".DAt";
                break;
            case "cFg":
                strOriginally=".cFg";
                strReplace=".dAt";
                break;
            case "cFG":
                strOriginally=".cFG";
                strReplace=".dAT";
                break;
        }
        strDatFilePath = strFilePath.replace(strOriginally, strReplace);//把.cfg换成.dat
        return strDatFilePath;
    }

    /*********************************
     * 获取最小(最终)采样率方法
     * @param lstRate  cfg中关于采样率参数
     * @return 返回最小采样率
     **********************************/
    private long getFinalWaveSample(List<RateDTO> lstRate,int iType) {
        long nFinalOneSample = -1;// 最终返回采样率
        long nMinOneSample = -1;// 最小采样率
        if(lstRate.size() > 0){
            nMinOneSample =  lstRate.get(0).getNOneSample();
            long tmpOneSample = 0;
            for (int i = 0; i < lstRate.size(); i++) {
                tmpOneSample =  lstRate.get(i).getNOneSample();
                if (tmpOneSample >= 32) {
                    if (nMinOneSample > tmpOneSample)
                        nMinOneSample = tmpOneSample;
                }
            }
        }
        /********************************************************
         * iFlag == 0  高级算法的要求，采样率只能是32-128
         * iFlag == 1  普通展示，采样率按照cfg里面最小的（大于32）
         * iFlag == 2  App抽点要求，采样率抽点成32
         * iFlag == 3  高级算法原始波形（大于32）
         ********************************************************/
        switch (iType){
            case 0:
                if (nMinOneSample < 32) {
                    nFinalOneSample = 32;
                } else if (nMinOneSample > 128) {
                    nFinalOneSample = 128;
                }
                break;
            case 1:
                nFinalOneSample = nMinOneSample;
                break;
            case 2:
                nFinalOneSample = 32;
                break;
            case 3:
                nFinalOneSample = nMinOneSample;
                break;
            default:
                nFinalOneSample = nMinOneSample;
                break;

        }
        return nFinalOneSample;
    }

    /*********************************
     * 离线波形导入时获取突变量算法
     * param tmpListWave 波形原始数据
     * param finalSampleRate 最终采样率
     **********************************/
    private MutationDTO getMutationValue(List<List<Float>> lstWave,float finalSampleRate) {
        MutationDTO mutationDTO =new MutationDTO();
        // 计算有效值
        double fSumA = 0.0, fSumB = 0.0, fSumC = 0.0;
        double fValidA = 0.0, fValidB = 0.0, fValidC = 0.0;
        double fValue = 0.0;
        int HalfTs = (int) finalSampleRate;// 全波有效值 ； (int)fs / 2;//半波有效值
        //瞬时波形数据_瞬时---前推周波的值_瞬时----周波的值_突变量----前推周波的值_突变量
        List<Float> tmpRealValue,forwardRealValue,tblValue,forwardTblValue;
        // 计算有效值算法
        for (int i = 0; i < lstWave.size(); i++) {
            //获取每一项的值
            tmpRealValue = lstWave.get(i);
            //包含了时间、A、B、C三相
            if (tmpRealValue.size() >= 4) {
                fSumA += Math.pow(tmpRealValue.get(1), 2);
                fSumB += Math.pow(tmpRealValue.get(2), 2);
                fSumC += Math.pow(tmpRealValue.get(3), 2);
                // 计算有效值
                List<Float> tmpRmsValue = new ArrayList<>();
                if (i >= finalSampleRate) {
                    forwardRealValue = lstWave.get(i - HalfTs);
                    fSumA -= Math.pow(forwardRealValue.get(1), 2);
                    fSumB -= Math.pow(forwardRealValue.get(2), 2);
                    fSumC -= Math.pow(forwardRealValue.get(3), 2);
                }
                // 计算突变量值
                List<Float> tmpTblValue = new ArrayList<>();
                tmpTblValue.add(tmpRealValue.get(0));//获取时间
                if (i >= HalfTs) {
                    tblValue = lstWave.get(i);//获取前推周波的值
                    forwardTblValue = lstWave.get(i - HalfTs);//获取前推周波的值
                    tmpTblValue.add(tblValue.get(1) - forwardTblValue.get(1));
                    tmpTblValue.add(tblValue.get(2) - forwardTblValue.get(2));
                    tmpTblValue.add(tblValue.get(3) - forwardTblValue.get(3));
                } else {
                    tmpTblValue.add(0f);
                    tmpTblValue.add(0f);
                    tmpTblValue.add(0f);
                }
                fValidA = Math.sqrt(fSumA / HalfTs);
                fValidB = Math.sqrt(fSumB / HalfTs);
                fValidC = Math.sqrt(fSumC / HalfTs);

                if (i >= finalSampleRate) {
                    if (fValidA < mutationDTO.getFMinMagA())
                        mutationDTO.setFMinMagA(fValidA);
                    if (fValidB < mutationDTO.getFMinMagB())
                        mutationDTO.setFMinMagB(fValidB);
                    if (fValidC < mutationDTO.getFMinMagC())
                        mutationDTO.setFMinMagC(fValidC);
                }
                //RMS获取
                tmpRmsValue.add(tmpRealValue.get(0));//获取时间
                tmpRmsValue.add((float) fValidA);
                tmpRmsValue.add((float) fValidB);
                tmpRmsValue.add((float) fValidC);
                mutationDTO.getListRms_Offline().add(tmpRmsValue);

                //RMS获取
                mutationDTO.getListTBL_Offline().add(tmpTblValue);
            }
        }
        return mutationDTO;
    }

    /*********************************
     * 离线波形导入时获取暂降特征值的算法
     * param lstWave 波形原始数据
     * param tblWave 突变量波形数据
     * param rmstWave RMS数据
     * param comtradeCfgDTO 波形CFG配置参数
     * param blType 计算方式
     * List<List<Float>>  返回暂降数据
     **********************************/
    private List<EigenvalueDTO> getEventValue(List<List<Float>> lstWave,MutationDTO mutationDTO,ComtradeCfgDTO comtradeCfgDTO, boolean blType) {
        List<List<Float>> tblWave = mutationDTO.getListTBL_Offline();
        List<List<Float>> rmstWave = mutationDTO.getListRms_Offline();
        //额定电压
        float fBase = 57.74f;
        //假如所选的是380V，那么PT变比是1:1，因此额定电压要选220
        List<AnalogDTO> lstAnalogDTO = comtradeCfgDTO.getLstAnalogDTO();//模拟量通道记录
        if (lstAnalogDTO.size() > 0) {
            if (lstAnalogDTO.get(0).getFPrimary() / lstAnalogDTO.get(0).getFSecondary() <= 1) {
                fBase = 220f;
            }
        }
        //采样率
        int nSJ = comtradeCfgDTO.getFinalSampleRate().intValue();
        /*********************************
         * 0是特征幅值（残余电压百分比）
         * 1是特征幅值（残余电压）
         * 2额定定压（动态电压）
         * 3是持续时间
         **********************************/
        List<EigenvalueDTO> lstEigenvalueDTO = new ArrayList<>();//ABC三相分析结果
        for (int i = 0; i < 3; i++) {
            int iDDY = 0, iGDY = 0;//低电压和郭电压标识值
            EigenvalueDTO eigenvalueDTO = new EigenvalueDTO();//某一项分析结果
            iDDY = App_Disturb_DDY1(lstWave, tblWave, rmstWave, nSJ, i, blType);
            if (Disturb_Val == 0 && Disturb_SJ == 0) {
                //判断A相的暂态事件类型是否为短时中断或电压暂降
                iGDY = App_Disturb_GDY1(lstWave, tblWave, rmstWave, nSJ, i, blType);
                if (iGDY != 0) {
                    if (Disturb_Val != 0) {
                        if (blType) {
                            eigenvalueDTO.setAmplitude(Disturb_Val / rmstWave.get(nSJ + 2).get(i + 1));//征幅值（残余电压百分比）
                            eigenvalueDTO.setResidualVoltage(Disturb_Val);//特征幅值（残余电压）
                            eigenvalueDTO.setRatedVoltage(rmstWave.get(nSJ + 2).get(i + 1));//额定定压（动态电压）
                        } else {
                            eigenvalueDTO.setAmplitude(Disturb_Val / 57.74f);//征幅值（残余电压百分比）
                            eigenvalueDTO.setResidualVoltage(Disturb_Val);//特征幅值（残余电压）
                            eigenvalueDTO.setRatedVoltage(57.74f);//额定定压（动态电压）
                        }
                    }
                } else//如果都没有找到，那么需要从曲线里面找出比较小的值来计算
                {
                    double rate = 0f;
                    double residualVoltage = 0.f;//残余电压
                    switch (i) {
                        case 0:
                            residualVoltage = mutationDTO.getFMinMagA();
                            break;
                        case 1:
                            residualVoltage = mutationDTO.getFMinMagB();
                            break;
                        case 2:
                            residualVoltage = mutationDTO.getFMinMagC();
                            break;
                    }

                    if (residualVoltage != -1) {
                        rate = residualVoltage / fBase > 1 ? 1.0f : residualVoltage / fBase;
                    }
                    eigenvalueDTO.setAmplitude((float) rate);//征幅值（残余电压百分比）
                    eigenvalueDTO.setResidualVoltage((float) residualVoltage);//特征幅值（残余电压）
                    eigenvalueDTO.setRatedVoltage(fBase);//额定定压（动态电压）
                }
            } else {
                if (Disturb_Val != 0) {
                    if (Disturb_Val != 0) {
                        if (blType) {
                            eigenvalueDTO.setAmplitude(Disturb_Val / rmstWave.get(nSJ + 2).get(i + 1));//征幅值（残余电压百分比）
                            eigenvalueDTO.setResidualVoltage(Disturb_Val);//特征幅值（残余电压）
                            eigenvalueDTO.setRatedVoltage(rmstWave.get(nSJ + 2).get(i + 1));//额定定压（动态电压）
                        } else {
                            eigenvalueDTO.setAmplitude(Disturb_Val / 57.74f);//征幅值（残余电压百分比）
                            eigenvalueDTO.setResidualVoltage(Disturb_Val);//特征幅值（残余电压）
                            eigenvalueDTO.setRatedVoltage(57.74f);//额定定压（动态电压）
                        }
                    }
                }
            }
            eigenvalueDTO.setDurationTime(Disturb_SJ / nSJ * 20.0f);//持续时间
            lstEigenvalueDTO.add(eigenvalueDTO);
        }
        return lstEigenvalueDTO;
    }

    private float Disturb_Val = 0;//暂降幅值
    private double Disturb_Time = 0;//持续时间
    private float Disturb_SJ = 0;//暂态启动点号

    private float Un09 = (0.90f * 57.74f);//暂降幅值90%
    private float Un002 = (0.02f * 57.74f);//暂降幅值2%
    private float Un110 = (1.10f * 57.74f);//暂降幅值110%

    /************************************
     *低电压的判据 包含了暂降和中断
     * @param realWave  原始波形数据
     * @param tblWave   突变量波形数据
     * @param rmstWave  RMS波形数据
     * @param nCirclePoint 采样率
     * @param nType  类别 0：A相，1：B相，2：C相
     * @param blFlag 浮动门槛和固定门槛
     * @return
     *************************************/
    private int App_Disturb_DDY1(List<List<Float>> realWave, List<List<Float>> tblWave, List<List<Float>> rmstWave, int nCirclePoint, int nType, boolean blFlag) {
        Disturb_Val = 0;
        Disturb_Time = 0;
        Disturb_SJ = 0;

        /****************************
         * ADC 临时的突变量判据
         * Disturb_JS_Val 事件启动点时刻的有效值
         ****************************/
        double temp, ADC, Disturb_JS_Val = 0;
        int iTbl = 0;
        long Disturb_QD = 0;
        //nSJ就是全波的采样率,nHalfSJ是半波的采样率
        int nSJ = nCirclePoint, nHalfSJ = nCirclePoint / 2;
        //定义90%和20%额定电压
        float fUN09 = Un09, fUN002 = Un002;
        //增加浮动门槛判断
        if (blFlag) {
            /****************************
             * 计算值去掉一个前一个周波后
             * 取第二个周波的第二个有效值
             * 第一个值是时间、A,B,C三相
             ****************************/
            if (rmstWave.size() > nSJ + 2) {
                fUN09 = rmstWave.get(nSJ + 2).get(nType + 1) * 0.9f;
                fUN002 = rmstWave.get(nSJ + 2).get(nType + 1) * 0.02f;
            }
        }
        /****************************
         * 从第二个周波开始
         ****************************/
        for (int i = nSJ; i < realWave.size(); i++) {
            //获取RMS有效值
            float rmsValue = rmstWave.get(i).get(nType + 1);
            //电压扰动启动判别
            if (Disturb_QD == 0) {
                //有效值小于90%
                if (rmsValue < fUN09) {
                    Disturb_QD = 0xff;
                    Disturb_Val = rmsValue;
                    Disturb_JS_Val = rmsValue;
                    //寻找突变点
                    for (int j = 0; j < nHalfSJ; j++) {
                        //临时的突变量
                        ADC = tblWave.get(i - nHalfSJ + j).get(nType + 1);
                        //临时的突变量小于0时候取绝对值
                        if (ADC < 0) {
                            ADC = 0 - ADC;
                        }
                        if (ADC > fUN002) {
                            Disturb_SJ += (nHalfSJ - j);
                            iTbl = (int) (i - nHalfSJ + j);
                            break;
                        }
                    }
                }
            }
            //电压扰动返回判别
            else {
                if (rmsValue < (fUN09 + fUN002)) {
                    Disturb_SJ++;
                    if (Disturb_Val > rmsValue) {
                        Disturb_Val = rmsValue;
                    }
                    Disturb_JS_Val = rmsValue;
                } else {
                    if (Disturb_SJ >= (nSJ + nHalfSJ)) {
                        //20%突变量标志
                        int iFlag = 0;
                        for (int j = 0; j < nHalfSJ; j++) {
                            iFlag = j;
                            //临时的突变量
                            ADC = tblWave.get(i - nHalfSJ + j).get(nType + 1);
                            //临时的突变量小于0时候取绝对值
                            if (ADC < 0) {
                                ADC = 0 - ADC;
                            }
                            if (ADC > fUN002)
                                break;
                        }
                        Disturb_SJ -= (nHalfSJ - iFlag);
                        Disturb_Time = ((double) Disturb_SJ) * 20 / nSJ;
                        return iTbl;
                    } else {
                        Disturb_SJ++;
                        ADC = realWave.get(i).get(nType + 1);
                        if (ADC < 0) ADC = 0 - ADC;
                        temp = rmsValue - Disturb_JS_Val;
                        if (temp < 0) temp = 0 - temp;
                        if ((ADC > 100) && (temp < 0.1)) {
                            Disturb_SJ -= (nHalfSJ + 1);
                            Disturb_Time = ((double) Disturb_SJ) * 20 / nSJ + 1;
                            return iTbl;
                        }
                    }
                    Disturb_JS_Val = rmsValue;
                }
            }
        }
        return iTbl;
    }

    /************************************
     *过电压的判据
     * @param realWave  原始波形数据
     * @param tblWave   突变量波形数据
     * @param rmstWave  RMS波形数据
     * @param nCirclePoint 采样率
     * @param nType  类别 0：A相，1：B相，2：C相
     * @param blFlag 浮动门槛和固定门槛
     * @return
     *************************************/
    private int App_Disturb_GDY1(List<List<Float>> realWave, List<List<Float>> tblWave, List<List<Float>> rmstWave, int nCirclePoint, int nType, boolean blFlag) {
        Disturb_Val = 0;
        Disturb_Time = 0;
        Disturb_SJ = 0;

        /****************************
         * ADC 临时的突变量判据
         * Disturb_JS_Val 事件启动点时刻的有效值
         ****************************/
        double temp, ADC, Disturb_JS_Val = 0;
        int iTbl = 0;
        long Disturb_QD = 0;
        //nSJ就是全波的采样率,nHalfSJ是半波的采样率
        int nSJ = nCirclePoint, nHalfSJ = nCirclePoint / 2;
        //定义110%和20%额定电压
        float fUN110 = Un110, fUN002 = Un002;
        //增加浮动门槛判断
        if (blFlag) {
            /****************************
             * 计算值去掉一个前一个周波后
             * 取第二个周波的第二个有效值
             * 第一个值是时间、A,B,C三相
             ****************************/
            if (rmstWave.size() > nSJ + 2) {
                fUN110 = rmstWave.get(nSJ + 2).get(nType + 1) * 1.1f;
                fUN002 = rmstWave.get(nSJ + 2).get(nType + 1) * 0.02f;
            }
        }
        /****************************
         * 从第二个周波开始
         ****************************/
        for (int i = nSJ; i < realWave.size(); i++) {
            //获取RMS有效值
            float rmsValue = rmstWave.get(i).get(nType + 1);
            //电压扰动启动判别
            if (Disturb_QD == 0) {
                if (rmsValue > fUN110) {
                    Disturb_QD = 0xff;
                    Disturb_Val = rmsValue;
                    Disturb_JS_Val = rmsValue;
                    //寻找突变点
                    for (int j = 0; j < nHalfSJ; j++) {
                        //临时的突变量
                        ADC = tblWave.get(i - nHalfSJ + j).get(nType + 1);
                        //临时的突变量小于0时候取绝对值
                        if (ADC < 0) {
                            ADC = 0 - ADC;
                        }
                        if (ADC > fUN002) {
                            Disturb_SJ += (nHalfSJ - j);
                            iTbl = (int) (i - nHalfSJ + j);
                            break;
                        }
                    }
                }
            }
            //电压扰动返回判别
            else {
                if (rmsValue > (fUN110 - fUN002)) {
                    Disturb_SJ++;
                    if (Disturb_Val < rmsValue)
                        Disturb_Val = rmsValue;
                    Disturb_JS_Val = rmsValue;
                } else {
                    if (Disturb_SJ >= (nSJ + nHalfSJ)) {
                        int iFlag = 0;
                        for (int j = 0; j < nHalfSJ; j++) {
                            iFlag = j;
                            ADC = tblWave.get(i - nHalfSJ + j).get(nType + 1);
                            if (ADC < 0) ADC = 0 - ADC;
                            if (ADC > fUN002)
                                break;
                        }
                        Disturb_SJ -= (nHalfSJ - iFlag);
                        Disturb_Time = (double) Disturb_SJ * 20 / nSJ;
                        return iTbl;
                    } else {
                        Disturb_SJ++;
                        ADC = realWave.get(i).get(nType + 1);
                        if (ADC < 0) ADC = 0 - ADC;
                        temp = rmsValue - Disturb_JS_Val;
                        if (temp < 0) temp = 0 - temp;
                        if ((ADC > 100) && (temp < 0.1)) {
                            Disturb_SJ -= (nHalfSJ + 1);
                            Disturb_Time = Disturb_SJ * 20 / nSJ + 1;
                            return iTbl;
                        }
                    }
                    Disturb_JS_Val = rmsValue;
                }
            }
        }
        return iTbl;
    }


    public static void main(String[] args) {
        /********************************************************
         * iFlag == 0  高级算法的要求，采样率只能是32-128
         * iFlag == 1  普通展示，采样率按照cfg里面最小的（大于32）
         * iFlag == 2  App抽点要求，采样率抽点成32
         * iFlag == 3  高级算法原始波形（大于32）
         ********************************************************/
        AnalyWave analyWave = new AnalyWave();
        // 获取瞬时波形
        WaveDataDTO waveDataDTO = analyWave.getComtrade("C:\\Users\\陈超\\Desktop\\Comtrade\\192.168.0.58\\222.CFG", 1);//获取原始波形值
        // 获取RMS波形
        WaveDataDTO waveDataDTO1 = analyWave.getValidData(waveDataDTO);
        // 获取特征值
        List<EigenvalueDTO> lstEigenvalueDTO = analyWave.getEigenvalue(waveDataDTO,true);
        String str = "";
    }
}
