package com.njcn.event.file.component; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.common.utils.wave.BitConverter; import com.njcn.event.file.pojo.dto.*; import com.njcn.event.file.pojo.enums.WaveFileResponseEnum; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.io.*; import java.nio.file.Files; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; /** * @author hongawen * @version 1.0.0 * @date 2023年03月03日 10:01 */ @Slf4j @Component @RequiredArgsConstructor public class WaveFileComponent { /****************************************** * 调用读取comtrate文件方法 * param strFilePath 波形.cfg文件路径 * param iType == 0 高级算法的要求,采样率只能是32-128 * iType == 1 普通展示,采样率按照cfg里面最小的(大于32) * iType == 2 App抽点要求,采样率抽点成32 * iType == 3 高级算法原始波形(大于32) ******************************************/ public WaveDataDTO getComtrade(InputStream cfgStream, InputStream datStream, int iType) { WaveDataDTO waveDataDTO = new WaveDataDTO(); // 首先判断文件路径是否为空 // 读取cfg文件 ComtradeCfgDTO comtradeCfgDTO = getComtradeCfg(cfgStream); // 为空或者未找到结束符号 if (comtradeCfgDTO == null || !"BINARY".equalsIgnoreCase(comtradeCfgDTO.getStrBinType())) { throw new BusinessException(WaveFileResponseEnum.CFG_DATA_ERROR); } /*****根据通道号计算相别** add by yexb -----Start**** * 1、判断是否是3的倍数,是3的倍数则是3相 * 2、假如不是3的倍数 ,是1的倍数则是单相 ********************************************************/ if (comtradeCfgDTO.getNAnalogNum() % 3 == 0) { comtradeCfgDTO.setNPhasic(3); } else { comtradeCfgDTO.setNPhasic(1); } // 给相别数量赋值 waveDataDTO.setIPhasic(comtradeCfgDTO.getNPhasic()); // 组装解析抬头 getWaveTitle(waveDataDTO, comtradeCfgDTO); // 解析.dat文件 List> listWaveData = getComtradeDat(comtradeCfgDTO, datStream, iType); waveDataDTO.setComtradeCfgDTO(comtradeCfgDTO); waveDataDTO.setListWaveData(listWaveData); //add by hongawen,将暂态触发起始时间记录下来 waveDataDTO.setTime(DateUtil.format(comtradeCfgDTO.getTimeTrige(), DatePattern.NORM_DATETIME_MS_PATTERN)); /*****根据通道号计算相别** add by yexb -----end****/ return waveDataDTO; } /********************************* * 根据波形数据算出rms值数据 * param waveDataDTO 瞬时波形(包含了CFG配置文件) * List> 返回RMS波形 **********************************/ @SuppressWarnings("unused") public WaveDataDTO getValidData(WaveDataDTO waveDataDTO) { //CFG 配置文件 ComtradeCfgDTO comtradeCfgDTO = waveDataDTO.getComtradeCfgDTO(); //瞬时波形值 List> lstWave = waveDataDTO.getListWaveData(); //返回rms的值 List> listRms = new ArrayList<>(); /*float fs = nOneWaveNum; int nWaveNum = (int) nAllWaveNum;*/ // 全波有效值 (int)fs / 2;//半波有效值 int halfTs = comtradeCfgDTO.getFinalSampleRate().intValue(); // 计算有效值算法 /********************************* * modify by yexibao 2020-10-29 * 增加多波形算法 ---------start ********************************/ double iWave; // 相别 int nPhasic; //存放RMS值的最小值 List> listRmsMin = new ArrayList<>(); if (lstWave.size() > 0) { nPhasic = comtradeCfgDTO.getNPhasic(); //ComtradeCfg.nAnalogNum为值的个数(-1的原因是一个存的是时间) iWave = Math.floor((lstWave.get(0).size() - 1) / (double) nPhasic); List tmpListRms; List tmpListRmsMin; //增加RMS的最小值 double fMinTime = 0.0, fMinValue = 0.0; //每一项一项计算 for (int j = 0; j < iWave; j++) { // 实例化 tmpListRmsMin = new ArrayList<>(); double fSumA = 0.0, fSumB = 0.0, fSumC = 0.0; double fValidA, fValidB, fValidC; //循环原始数据 for (int i = 0; i < lstWave.size(); i++) { // 当第一次循环的时候实例化,其余的获取已有的List//获取每一项的值 List 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 forwardListValue = lstWave.get(i - halfTs); fSumA -= Math.pow(forwardListValue.get(1 + nPhasic * j), 2); } fValidA = Math.sqrt(fSumA / halfTs); tmpListRms.add((float) (Math.round(fValidA * 100)) / 100); listRms.set(i, tmpListRms); // 最小值判断 if (i >= halfTs) { if (i == halfTs) { fMinValue = fValidA; fMinTime = tmpListValue.get(0); } else { if (fValidA < fMinValue) { fMinValue = fValidA; fMinTime = tmpListValue.get(0); } } } 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 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) (Math.round(fValidA * 100)) / 100); tmpListRms.add((float) (Math.round(fValidB * 100)) / 100); listRms.set(i, tmpListRms); // 最小值判断 if (i >= halfTs) { if (i == halfTs) { fMinValue = fValidA; fMinTime = tmpListValue.get(0); } else { if (fValidA < fMinValue) { fMinValue = fValidA; fMinTime = tmpListValue.get(0); } if (fValidB < fMinValue) { fMinValue = fValidB; fMinTime = tmpListValue.get(0); } } } 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 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) (Math.round(fValidA * 100)) / 100); tmpListRms.add((float) (Math.round(fValidB * 100)) / 100); tmpListRms.add((float) (Math.round(fValidC * 100)) / 100); listRms.set(i, tmpListRms); // 最小值判断 if (i >= halfTs) { if (i == halfTs) { fMinValue = fValidA; fMinTime = tmpListValue.get(0); } else { if (fValidA < fMinValue) { fMinValue = fValidA; fMinTime = tmpListValue.get(0); } if (fValidB < fMinValue) { fMinValue = fValidB; fMinTime = tmpListValue.get(0); } if (fValidC < fMinValue) { fMinValue = fValidC; fMinTime = tmpListValue.get(0); } } } break; default: break; } } } //增加最小值时间,最小值//获取时间 tmpListRmsMin.add((float) fMinTime); //获取时间 tmpListRmsMin.add((float) (Math.round(fMinValue * 100)) / 100); listRmsMin.add(tmpListRmsMin); } //过滤前一个周波 //HalfTs表示一个周波 try { for (int i = 0; i < halfTs; i++) { //电压有效值算法 //没相具体的值 List 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; default: break; } } //重新赋值 listRms.set(i, tmpNewListRms); } } catch (Exception e) { throw new BusinessException(WaveFileResponseEnum.RMS_DATA_ERROR); } } waveDataDTO.setListRmsData(listRms); //RMS最小值 waveDataDTO.setListRmsMinData(listRmsMin); return waveDataDTO; } /***************************** *获取暂降特征值,包含离线 * param waveDataDTO 波形返回参数 * param blType 计算方式 true:浮动门槛 false:固定门槛 *****************************/ public List getEigenvalue(WaveDataDTO waveDataDTO, boolean blType) { //CFG 配置文件 ComtradeCfgDTO comtradeCfgDTO = waveDataDTO.getComtradeCfgDTO(); // 瞬时波形值 List> lstWave = waveDataDTO.getListWaveData(); //获取最终采样率 int finalSampleRate = comtradeCfgDTO.getFinalSampleRate(); // 返回值 List lstEigenvalueDTO = new ArrayList<>(); // 必须包含了瞬时波形 if (lstWave.size() > 0) { MutationDTO mutationDTO = getMutationValue(lstWave, finalSampleRate); //获取突变量和RMS值 if (mutationDTO.getListRms_Offline().size() > 0 && mutationDTO.getListTBL_Offline().size() > 0) { lstEigenvalueDTO = getEventValue(lstWave, mutationDTO, comtradeCfgDTO, blType); } } else { lstEigenvalueDTO = null; } return lstEigenvalueDTO; } /*** * 获取波形文件流,存在则返回inputStream,不存在则返回null * 为null时,这抛出波形文件不存在异常 * @author hongawen * @date 2023/3/3 14:03 */ public InputStream getFileInputStreamByFilePath(String filePath) { File file = new File(filePath); if (file.isFile() && file.exists()) { InputStream inputStream; try { inputStream = Files.newInputStream(file.toPath()); if (inputStream.available() < 1) { throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID); } return inputStream; } catch (IOException e) { throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID); } } else { throw new BusinessException(WaveFileResponseEnum.ANALYSE_WAVE_NOT_FOUND); } } /********************************* * 读取cfg方法 * param strFilePath 文件路径 * return 返回bool为是否解析出错 **********************************/ private ComtradeCfgDTO getComtradeCfg(InputStream cfgStream) { ComtradeCfgDTO comtradeCfgDTO = new ComtradeCfgDTO(); try (InputStreamReader read = new InputStreamReader(cfgStream, CharsetUtil.CHARSET_GBK); BufferedReader bufferedReader = new BufferedReader(read);) { // 第一行不关心仅仅是一些描述类的信息 String strFileLine = bufferedReader.readLine(); // 第二行需要关心第二个(模拟量的个数)和第三个参数(开关量的个数) strFileLine = bufferedReader.readLine(); // 按“,”进行分割 String[] strTempArray = strFileLine.split(StrUtil.COMMA); // 总个数 comtradeCfgDTO.setNChannelNum(Integer.parseInt(strTempArray[0])); // 模拟量的个数 comtradeCfgDTO.setNAnalogNum(Integer.parseInt(strTempArray[1].substring(0, strTempArray[1].length() - 1))); // 开关量的个数 comtradeCfgDTO.setNDigitalNum(Integer.parseInt(strTempArray[2].substring(0, strTempArray[2].length() - 1))); // 从第三行开始的ComtradeCfg.nChannelNum行是模拟量通道和数字量通道 List 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(StrUtil.COMMA); //通道序号 analogDTO.setNIndex(Integer.parseInt(strTempArray[0])); // 通道名称 analogDTO.setSzChannleName(strTempArray[1]); // 相位名称 analogDTO.setSzPhasicName(strTempArray[2]); // 监视的通道名称 analogDTO.setSzMonitoredChannleName(strTempArray[3]); // 通道的单位 analogDTO.setSzUnitName(strTempArray[4]); // 通道的系数 analogDTO.setFCoefficent(Float.parseFloat(strTempArray[5])); // 通道的偏移量 analogDTO.setFOffset(Float.parseFloat(strTempArray[6])); // 起始采样时间的偏移量 analogDTO.setFTimeOffset(Float.parseFloat(strTempArray[7])); // 采样值的最小值 analogDTO.setNMin(Integer.parseInt(strTempArray[8])); // 采样值的最大值 analogDTO.setNMax(Integer.parseInt(strTempArray[9])); // 一次变比 analogDTO.setFPrimary(Float.parseFloat(strTempArray[10])); // 二次变比 analogDTO.setFSecondary(Float.parseFloat(strTempArray[11])); // 一次值还是二次值标志 analogDTO.setSzValueType(strTempArray[12]); } //WW 2019-11-14 // 采样频率 String freqLine = bufferedReader.readLine(); int nFreq; try { // 先尝试解析为double再四舍五入为整数,以兼容"50.00"这样的格式 nFreq = (int) Math.round(Double.parseDouble(freqLine)); } catch (NumberFormatException e) { // 如果失败则使用原来的整数解析方式 nFreq = Integer.parseInt(freqLine); } // 获取采样段数 strFileLine = bufferedReader.readLine(); int nRates = Integer.parseInt(strFileLine); comtradeCfgDTO.setNRates(nRates); // 获得每段的采样率 //采样率 List lstRate = new ArrayList<>(); int nOffset = 0; for (int i = 0; i < nRates; i++) { strFileLine = bufferedReader.readLine(); strTempArray = strFileLine.split(StrUtil.COMMA); RateDTO rateDTO = new RateDTO(); // 单周波采样点数 //WW 2019-11-14 double doubleValue = Double.parseDouble(strTempArray[0]); // 解析为 double int result = (int) (doubleValue / nFreq); // 强制转换为 int rateDTO.setNOneSample(result); // 总点数 //这里的strTemp是一个偏移量 rateDTO.setNSampleNum((Integer.parseInt(strTempArray[1]) - nOffset)); nOffset += rateDTO.getNSampleNum(); lstRate.add(rateDTO); } comtradeCfgDTO.setLstRate(lstRate); // 增加读取波形起始时间个结束时间 String timeFormat = "dd/MM/yyyy,HH:mm:ss.SSS"; // 波形起始时间 strFileLine = bufferedReader.readLine(); strFileLine = strFileLine.substring(0, strFileLine.length() - 3); comtradeCfgDTO.setTimeStart(DateUtil.parse(strFileLine, timeFormat)); // 暂态触发时间 strFileLine = bufferedReader.readLine(); strFileLine = strFileLine.substring(0, strFileLine.length() - 3); comtradeCfgDTO.setTimeTrige(DateUtil.parse(strFileLine, timeFormat)); // 获取触发时间的时间 + 毫秒 Calendar calendar = DateUtil.calendar(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) (b - a); if (c >= 90 && c <= 110) { comtradeCfgDTO.setNPush(100); } else if (c >= 190 && c <= 210) { comtradeCfgDTO.setNPush(200); } // 赋值编码格式(二进制) comtradeCfgDTO.setStrBinType(bufferedReader.readLine().toUpperCase()); } catch (Exception e) { // 解析.cfg文件出错 comtradeCfgDTO = null; } return comtradeCfgDTO; } /********************************* * 读取dat方法 * param strFilePath .dat访问路径 * param strFilePath .dat访问路径 * param iType 访问波形类型 * List> 返回波形瞬时值 **********************************/ private List> getComtradeDat(ComtradeCfgDTO comtradeCfgDTO, InputStream datStream, int iType) { //返回数据,如果仅仅做展示后期考虑换String类型,降低内存开销 List> listWaveData = new ArrayList<>(); //初始化xValue的值 float xValueAll = 0; //判断是否首次登陆 boolean blxValue = false; byte[] datArray; try { datArray = IoUtil.readBytes(datStream); if (ArrayUtil.isEmpty(datArray)) { throw new BusinessException(WaveFileResponseEnum.DAT_DATA_ERROR); } // 计算每个单独的数据块的大小 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 = (int)Math.floor(datArray.length / nBlockSize); // 获取采样率 int finalSampleRate = getFinalWaveSample(comtradeCfgDTO.getLstRate(), iType); if (finalSampleRate != -1) { //设置最终采样率 comtradeCfgDTO.setFinalSampleRate(finalSampleRate); // 计算转换后的采样率 int nnInd = 0; // 抽点后总共多少点数据 int nWaveNum; //抽点后新的的采样率 List newLstRate = new ArrayList<>(); for (int iRate = 0; iRate < comtradeCfgDTO.getNRates(); iRate++) { // 计算本段录波总共有多少波形 nWaveNum = comtradeCfgDTO.getLstRate().get(iRate).getNSampleNum() / comtradeCfgDTO.getLstRate().get(iRate).getNOneSample(); //设置总波形大小 comtradeCfgDTO.setNAllWaveNum(comtradeCfgDTO.getNAllWaveNum() + nWaveNum); // 将最低采样率替换到本段录波内 RateDTO tmpRateDTO = new RateDTO(); // 有效值标志,如果是有效值,那么就需要反向补点,而不是抽点 if (comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() >= 32) { //YXB 2025-08-27 tmpRateDTO.bRMSFlag = false; } //如果采样是全波有效值或者半波有效值,需要去补足周波点数 YXB 2025-08-27 else if (comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() <= 2) { //YXB 2025-08-27 tmpRateDTO.bRMSFlag = true; } newLstRate.add(tmpRateDTO); //iFlag =3 一定不进行抽点算法 if (iType != 3) { //true 抽点算法(当前采样率跟统一采样率不一样则是抽点,否则是未抽点) if (!Objects.equals(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); } nnInd++; } // 偏移量,采样间隔 long nOffSet = 0, nWaveSpan; //两个点之间的时间差 float fValue, dfValue; // 计算不同块的采样率 int nIndex = 0; // .CFG中采样率 RateDTO tmpRateDTO; // nBlockNum 总循环次数 for (int i = 0; i < nBlockNum; i++) { tmpRateDTO = comtradeCfgDTO.getLstRate().get(nIndex); // 判断是否进入下一段 if (i == tmpRateDTO.getNSampleNum() + nOffSet) { nOffSet += tmpRateDTO.getNSampleNum(); nIndex++; if (nIndex == nnInd) { break; } } tmpRateDTO = comtradeCfgDTO.getLstRate().get(nIndex); //YXB 2025-08-27 如果是有效值,那么需要去补点,而不是抽点 if (newLstRate.get(nIndex).bRMSFlag == true) { //计算本段补点采样间隔 nWaveSpan = newLstRate.get(nIndex).getNOneSample() / tmpRateDTO.getNOneSample(); } else { // 计算本段抽点采样间隔 nWaveSpan = tmpRateDTO.getNOneSample() / newLstRate.get(nIndex).getNOneSample(); } dfValue = (float) 20 / tmpRateDTO.getNOneSample(); // 判断是否到了需要抽的采样点 if (i % nWaveSpan == 0) { // 计算每个通道的值 //存储局部数据集合,包含了时间,A,B,C三相 List tmpWaveData = new ArrayList<>(); //YXB 2025-08-27 如果是有效值,那么需要去补点,而不是抽点 if (newLstRate.get(nIndex).bRMSFlag == true) { // 计算有多少个周波 long allWaveTemp = newLstRate.get(nIndex).getNSampleNum() / newLstRate.get(nIndex).getNOneSample(); // 本段需要补多少点 long allempSample = newLstRate.get(nIndex).getNOneSample(); int currentDataIndex = i; for (int iWaveTemp = 0; iWaveTemp < allWaveTemp; iWaveTemp++) { for (int mTempSample = 0; mTempSample < allempSample; mTempSample++) { tmpWaveData = new ArrayList<>(); for (int j = 0; j < comtradeCfgDTO.getNAnalogNum(); j++) { if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzPhasicName().equalsIgnoreCase("N")) { break; } float fCoef = comtradeCfgDTO.getLstAnalogDTO().get(j).getFCoefficent(); fValue = BitConverter.byte2ToUnsignedShort(datArray, currentDataIndex * nBlockSize + 2 * 4 + j * 2) * fCoef; //WW 2019-11-14 /************************* * 1、接口返回的默认是二次值 * 2、P是一次值 S是二次值 * 3、S(二次值)情况下: * ①、单位为"V"时候则直接等于; * ②、单位为"kV"时候需要乘以1000 * 4、P(一次值)情况下: * ①、单位为"V"时候则直接等于; * ②、单位为"kV"时候需要乘以1000 *************************/ //P是一次值 S是二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("S")) { //判断单位是V还是kV if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { fValue = fValue * 1000.0f; } else { fValue = fValue; } } //P是一次值 S是二次值 else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("P")) { //判断单位是V还是kV if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("V")) { //根据cfg内的变比,将一次值转换成二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { fValue = fValue * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); } else { fValue = fValue; } } //判断单位是V还是kV else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { //根据cfg内的变比,将一次值转换成二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { fValue = fValue * 1000.0f * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); } else { fValue = fValue; } } else //还有可能是 电流,单位是A { //根据cfg内的变比,将一次值转换成二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { fValue = fValue * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); } else { fValue = fValue; } } } //xValue前移量,假如是第一次时候则需要前移 if (!blxValue && j == 0) { xValueAll = (float) (currentDataIndex * 20) / tmpRateDTO.getNOneSample() - comtradeCfgDTO.getNPush(); blxValue = true; //只增加一个xValue的值 //增加时间值 tmpWaveData.add((float) (Math.round(xValueAll * 100)) / 100); } else if (j == 0) { xValueAll += (float) dfValue / nWaveSpan; //只增加一个xValue的值 //增加时间值 tmpWaveData.add((float) (Math.round(xValueAll * 100)) / 100); } //不同通道yValue的值都需要增加,最终成ABC三相 //每个通道的值 tmpWaveData.add((float) (Math.round(fValue * 100)) / 100); } //把每个单独的值赋予到整体里面去 listWaveData.add(tmpWaveData); } // 把每个单独的值赋予到整体里面去 if (iWaveTemp < (allWaveTemp - 1)) { currentDataIndex++; } } } else { for (int j = 0; j < comtradeCfgDTO.getNAnalogNum(); j++) { //数据只有电压ABC三相数据,不展示U0、I0等数据 YXB2020-10-09 去除相别为N相的数据 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzPhasicName().equalsIgnoreCase("N")) { break; } float fCoef = comtradeCfgDTO.getLstAnalogDTO().get(j).getFCoefficent(); fValue = BitConverter.byte2ToUnsignedShort(datArray, i * nBlockSize + 2 * 4 + j * 2) * fCoef; //WW 2019-11-14 /************************** * 1、接口返回的默认是二次值 * 2、P是一次值 S是二次值 * 3、S(二次值)情况下: * ①、单位为"V"时候则直接等于; * ②、单位为"kV"时候需要乘以1000 * 4、P(一次值)情况下: * ①、单位为"V"时候则直接等于; * ②、单位为"kV"时候需要乘以1000 **************************/ //P是一次值 S是二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("S")) { //判断单位是V还是kV if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { fValue = fValue * 1000.0f; } else { fValue = fValue; } } //P是一次值 S是二次值 else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("P")) { //判断单位是V还是kV if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("V")) { //根据cfg内的变比,将一次值转换成二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { fValue = fValue * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); } else { fValue = fValue; } } //判断单位是V还是kV else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { //根据cfg内的变比,将一次值转换成二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { fValue = fValue * 1000.0f * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); } else { fValue = fValue; } } else //还有可能是 电流,单位是A { //根据cfg内的变比,将一次值转换成二次值 if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { fValue = fValue * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); } else { fValue = fValue; } } } //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) { e.printStackTrace(); throw new BusinessException(WaveFileResponseEnum.DAT_DATA_ERROR); } return listWaveData; } // private List> getComtradeDat(ComtradeCfgDTO comtradeCfgDTO, InputStream datStream, int iType) { // //返回数据,如果仅仅做展示后期考虑换String类型,降低内存开销 // List> listWaveData = new ArrayList<>(); // //初始化xValue的值 // float xValueAll = 0; // //判断是否首次登陆 // boolean blxValue = false; // byte[] datArray; // try { // datArray = IoUtil.readBytes(datStream); // if (ArrayUtil.isEmpty(datArray)) { // throw new BusinessException(WaveFileResponseEnum.DAT_DATA_ERROR); // } // // 计算每个单独的数据块的大小 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 = (int)Math.floor(datArray.length / nBlockSize); // // // 获取采样率 // int finalSampleRate = getFinalWaveSample(comtradeCfgDTO.getLstRate(), iType); // if (finalSampleRate != -1) { // //设置最终采样率 // comtradeCfgDTO.setFinalSampleRate(finalSampleRate); // // 计算转换后的采样率 // int nnInd = 0; // // 抽点后总共多少点数据 // int nWaveNum; // //抽点后新的的采样率 // List 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(); // // 有效值标志,如果是有效值,那么就需要反向补点,而不是抽点 // if (comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() >= 32) { // //YXB 2025-08-27 // tmpRateDTO.bRMSFlag = false; // } // //如果采样是全波有效值或者半波有效值,需要去补足周波点数 YXB 2025-08-27 // else if (comtradeCfgDTO.getLstRate().get(iRate).getNOneSample() <= 2) { // //YXB 2025-08-27 // tmpRateDTO.bRMSFlag = true; // } // newLstRate.add(tmpRateDTO); // //iFlag =3 一定不进行抽点算法 // if (iType != 3) { // //true 抽点算法(当前采样率跟统一采样率不一样则是抽点,否则是未抽点) // if (!Objects.equals(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; // //两个点之间的时间差 // float fValue, dfValue; // // 计算不同块的采样率 // int nIndex = 0; // // 将最低采样率替换到本段录波内 // // .CFG中采样率 // RateDTO tmpRateDTO; // // nBlockNum 总循环次数 // for (int i = 0; i < nBlockNum; i++) { // tmpRateDTO = comtradeCfgDTO.getLstRate().get(nIndex); // // 判断是否进入下一段 // if (i == tmpRateDTO.getNSampleNum() + nOffSet) { // nOffSet += tmpRateDTO.getNSampleNum(); // nIndex++; // if (nIndex == nnInd) { // break; // } // } // tmpRateDTO = comtradeCfgDTO.getLstRate().get(nIndex); // //YXB 2025-08-27 如果是有效值,那么需要去补点,而不是抽点 // if (newLstRate.get(nIndex).bRMSFlag == true) { // //计算本段补点采样间隔 // nWaveSpan = newLstRate.get(nIndex).getNOneSample() / tmpRateDTO.getNOneSample(); // } else { // // 计算本段抽点采样间隔 // nWaveSpan = tmpRateDTO.getNOneSample() / newLstRate.get(nIndex).getNOneSample(); // } // // dfValue = (float) 20 / tmpRateDTO.getNOneSample(); // // 判断是否到了需要抽的采样点 // if (i % nWaveSpan == 0) { // // 计算每个通道的值 // //存储局部数据集合,包含了时间,A,B,C三相 // List tmpWaveData = new ArrayList<>(); // //YXB 2025-08-27 如果是有效值,那么需要去补点,而不是抽点 // if (newLstRate.get(nIndex).bRMSFlag == true) { // // 计算有多少个周波 // long allWaveTemp = newLstRate.get(nIndex).getNSampleNum() / newLstRate.get(nIndex).getNOneSample(); // // 本段需要补多少点 // long allempSample = newLstRate.get(nIndex).getNOneSample(); // //int iStartWaveTemp = i ;// 开始补点的起点 // for (int iWaveTemp = 0; iWaveTemp < allWaveTemp; iWaveTemp++) { // for (int mTempSample = 0; mTempSample < allempSample; mTempSample++) { // //最多只有半波有效值,也就是每周波是1个或者2个点,然后去补最少16个点 // if (mTempSample / nWaveSpan == 1 && mTempSample % nWaveSpan == 0) { // i++; // } // //存储局部数据集合,包含了时间,A,B,C三相 // tmpWaveData = new ArrayList<>(); // for (int j = 0; j < comtradeCfgDTO.getNAnalogNum(); j++) { // //数据只有电压ABC三相数据,不展示U0、I0等数据 YXB2020-10-09 去除相别为N相的数据 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzPhasicName().equalsIgnoreCase("N")) { // break; // } // float fCoef = comtradeCfgDTO.getLstAnalogDTO().get(j).getFCoefficent(); // // if((i * nBlockSize + 2 * 4 + j * 2) == 2437568){ // System.out.println(55); // } // fValue = BitConverter.byte2ToUnsignedShort(datArray, i * nBlockSize + 2 * 4 + j * 2) * fCoef; // //WW 2019-11-14 // /************************* // * 1、接口返回的默认是二次值 // * 2、P是一次值 S是二次值 // * 3、S(二次值)情况下: // * ①、单位为"V"时候则直接等于; // * ②、单位为"kV"时候需要乘以1000 // * 4、P(一次值)情况下: // * ①、单位为"V"时候则直接等于; // * ②、单位为"kV"时候需要乘以1000 // *************************/ // //P是一次值 S是二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("S")) { // //判断单位是V还是kV // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { // fValue = fValue * 1000.0f; // } else { // fValue = fValue; // } // } // //P是一次值 S是二次值 // else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("P")) { // //判断单位是V还是kV // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("V")) { // //根据cfg内的变比,将一次值转换成二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { // fValue = fValue * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); // } else { // fValue = fValue; // } // } // //判断单位是V还是kV // else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { // //根据cfg内的变比,将一次值转换成二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { // fValue = fValue * 1000.0f * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); // } else { // fValue = fValue; // } // } else //还有可能是 电流,单位是A // { // //根据cfg内的变比,将一次值转换成二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { // fValue = comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); // } else { // fValue = fValue; // } // } // } // // //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) dfValue / nWaveSpan; // //只增加一个xValue的值 //增加时间值 // tmpWaveData.add((float) (Math.round(xValueAll * 100)) / 100); // } // // //不同通道yValue的值都需要增加,最终成ABC三相 //每个通道的值 // tmpWaveData.add((float) (Math.round(fValue * 100)) / 100); // } // //把每个单独的值赋予到整体里面去 // listWaveData.add(tmpWaveData); // } // // 把每个单独的值赋予到整体里面去 // if (iWaveTemp < (allWaveTemp - 1)) { // i++; // } // } // } else { // for (int j = 0; j < comtradeCfgDTO.getNAnalogNum(); j++) { // //数据只有电压ABC三相数据,不展示U0、I0等数据 YXB2020-10-09 去除相别为N相的数据 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzPhasicName().equalsIgnoreCase("N")) { // break; // } // // float fCoef = comtradeCfgDTO.getLstAnalogDTO().get(j).getFCoefficent(); // fValue = BitConverter.byte2ToUnsignedShort(datArray, i * nBlockSize + 2 * 4 + j * 2) * fCoef; // // //WW 2019-11-14 // /************************** // * 1、接口返回的默认是二次值 // * 2、P是一次值 S是二次值 // * 3、S(二次值)情况下: // * ①、单位为"V"时候则直接等于; // * ②、单位为"kV"时候需要乘以1000 // * 4、P(一次值)情况下: // * ①、单位为"V"时候则直接等于; // * ②、单位为"kV"时候需要乘以1000 // **************************/ // //P是一次值 S是二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("S")) { // //判断单位是V还是kV // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { // fValue = fValue * 1000.0f; // } else { // fValue = fValue; // } // } // //P是一次值 S是二次值 // else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzValueType().equalsIgnoreCase("P")) { // //判断单位是V还是kV // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("V")) { // //根据cfg内的变比,将一次值转换成二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { // fValue = fValue * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); // } else { // fValue = fValue; // } // } // //判断单位是V还是kV // else if (comtradeCfgDTO.getLstAnalogDTO().get(j).getSzUnitName().equalsIgnoreCase("KV")) { // //根据cfg内的变比,将一次值转换成二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { // fValue = fValue * 1000.0f * comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); // } else { // fValue = fValue; // } // } else //还有可能是 电流,单位是A // { // //根据cfg内的变比,将一次值转换成二次值 // if (comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary() != 0.0f) { // fValue = comtradeCfgDTO.getLstAnalogDTO().get(j).getFSecondary() / comtradeCfgDTO.getLstAnalogDTO().get(j).getFPrimary(); // } else { // fValue = fValue; // } // } // } // //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) { // e.printStackTrace(); // throw new BusinessException(WaveFileResponseEnum.DAT_DATA_ERROR); // } // // return listWaveData; // } /********************************* * 获取最小(最终)采样率方法 * @param lstRate cfg中关于采样率参数 * @return 返回最小采样率 **********************************/ private int getFinalWaveSample(List lstRate, int iType) { // 最终返回采样率 int nFinalOneSample = -1; // 最小采样率 int nMinOneSample = -1; if (!lstRate.isEmpty()) { nMinOneSample = lstRate.get(0).getNOneSample(); int tmpOneSample; for (RateDTO rateDTO : lstRate) { tmpOneSample = rateDTO.getNOneSample(); //YXB 2025-08-27 32->16 if (tmpOneSample >= 16) { 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; } else { nFinalOneSample = nMinOneSample; } break; case 2: nFinalOneSample = 32; break; default: nFinalOneSample = nMinOneSample; break; } return nFinalOneSample; } /********************************* * 离线波形导入时获取突变量算法 * param tmpListWave 波形原始数据 * param finalSampleRate 最终采样率 **********************************/ private MutationDTO getMutationValue(List> lstWave, float finalSampleRate) { MutationDTO mutationDTO = new MutationDTO(); // 计算有效值 double fSumA = 0.0, fSumB = 0.0, fSumC = 0.0; double fValidA, fValidB, fValidC; double fValue = 0.0; // 全波有效值 ; (int)fs / 2;//半波有效值 int HalfTs = (int) finalSampleRate; //瞬时波形数据_瞬时---前推周波的值_瞬时----周波的值_突变量----前推周波的值_突变量 List 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 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 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> 返回暂降数据 **********************************/ private List getEventValue(List> lstWave, MutationDTO mutationDTO, ComtradeCfgDTO comtradeCfgDTO, boolean blType) { List> tblWave = mutationDTO.getListTBL_Offline(); List> rmstWave = mutationDTO.getListRms_Offline(); //额定电压 float fBase = 57.74f; //假如所选的是380V,那么PT变比是1:1,因此额定电压要选220 //模拟量通道记录 List lstAnalogDTO = comtradeCfgDTO.getLstAnalogDTO(); if (!lstAnalogDTO.isEmpty()) { if (lstAnalogDTO.get(0).getFPrimary() / lstAnalogDTO.get(0).getFSecondary() <= 1) { fBase = 220f; } } //采样率 int nSJ = comtradeCfgDTO.getFinalSampleRate().intValue(); /********************************* * 0是特征幅值(残余电压百分比) * 1是特征幅值(残余电压) * 2额定定压(动态电压) * 3是持续时间 **********************************/ //ABC三相分析结果 List lstEigenvalueDTO = new ArrayList<>(); for (int i = 0; i < 3; i++) { //低电压和郭电压标识值 int iDDY = 0, iGDY; //某一项分析结果 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; default: 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 (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; /*** * 暂降幅值90% */ private float Un09 = (0.90f * 57.74f); /*** * 暂降幅值2% */ private float Un002 = (0.02f * 57.74f); /*** * 暂降幅值110% */ private float Un110 = (1.10f * 57.74f); /************************************ *低电压的判据 包含了暂降和中断 * @param realWave 原始波形数据 * @param tblWave 突变量波形数据 * @param rmstWave RMS波形数据 * @param nCirclePoint 采样率 * @param nType 类别 0:A相,1:B相,2:C相 * @param blFlag 浮动门槛和固定门槛 *************************************/ private int App_Disturb_DDY1(List> realWave, List> tblWave, List> 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 = (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> realWave, List> tblWave, List> 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; } /********************************* * 获取波形标题的方法 * param tmpComtradeCfgDTO 文件路径 * return 返回List返回数据格式说明 **********************************/ private void getWaveTitle(WaveDataDTO waveDataDTO, ComtradeCfgDTO comtradeCfgDTO) { //编辑数据标题 YXB2020-10-09 去除相别为N相的数据//存储数据标题 List tmpWaveTitle = new ArrayList<>(); List channelName = new ArrayList<>(); // 模拟量通道记录类 AnalogDTO analogDTO; tmpWaveTitle.add("Time"); channelName.add("/"); String strUnit; for (int j = 0; j < comtradeCfgDTO.getNAnalogNum(); j++) { analogDTO = comtradeCfgDTO.getLstAnalogDTO().get(j); // 假如为N相则跳过 if (!StrUtil.equals(analogDTO.getSzPhasicName().toUpperCase(), "N")) { if ("A".equalsIgnoreCase(analogDTO.getSzUnitName())) { strUnit = "I"; } else { strUnit = "U"; } tmpWaveTitle.add(strUnit + analogDTO.getSzPhasicName().toUpperCase() + "相"); channelName.add(analogDTO.getSzChannleName()); } } waveDataDTO.setWaveTitle(tmpWaveTitle); waveDataDTO.setChannelNames(channelName); } /********************************* * 由.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; default: break; } //把.cfg换成.dat strDatFilePath = strFilePath.replace(strOriginally, strReplace); return strDatFilePath; } public static void main(String[] args) { /******************************************************** * iFlag == 0 高级算法的要求,采样率只能是32-128 * iFlag == 1 普通展示,采样率按照cfg里面最小的(大于32) * iFlag == 2 App抽点要求,采样率抽点成32 * iFlag == 3 高级算法原始波形(大于32) ********************************************************/ /** 输出格式: 2014-5-05 00:00:00 大写H为24小时制 */ DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String s; Date d = new Date(); s = sdf.format(d); System.out.println(s); WaveFileComponent waveFileComponent = new WaveFileComponent(); InputStream cfgStream = waveFileComponent.getFileInputStreamByFilePath("F:\\PQ_PQLD3_9_20250821_081038_640.cfg"); InputStream datStream = waveFileComponent.getFileInputStreamByFilePath("F:\\PQ_PQLD3_9_20250821_081038_640.dat"); // 获取瞬时波形 //获取原始波形值 WaveDataDTO waveDataDTO = waveFileComponent.getComtrade(cfgStream, datStream, 1); d = new Date(); s = sdf.format(d); System.out.println(s); // 获取RMS波形 WaveDataDTO waveDataDTO1 = waveFileComponent.getValidData(waveDataDTO); d = new Date(); s = sdf.format(d); System.out.println(s); // 获取特征值 List lstEigenvalueDTO = waveFileComponent.getEigenvalue(waveDataDTO, true); System.out.println(1); } }