新能源专项分析算法迁移

This commit is contained in:
wr
2025-04-03 16:10:00 +08:00
parent 3b62feefd6
commit af59a427f1
28 changed files with 1542 additions and 18 deletions

View File

@@ -18,6 +18,7 @@ import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.device.biz.commApi.CommTerminalGeneralClient;
import com.njcn.device.biz.pojo.dto.*;
import com.njcn.device.biz.pojo.param.DeptGetLineParam;
import com.njcn.device.pq.api.DeptLineFeignClient;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.web.controller.BaseController;
@@ -26,7 +27,6 @@ import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.entity.CmpStep;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
@@ -62,7 +62,8 @@ public class ExecutionCenter extends BaseController {
private DeptFeignClient deptFeignClient;
@Resource
private FlowExecutor flowExecutor;
@Resource
private DeptLineFeignClient deptLineFeignClient;
/***
* 1、校验非全链执行时tagNames节点标签集合必须为非空否则提示---无可执行节点
@@ -125,8 +126,6 @@ public class ExecutionCenter extends BaseController {
CalculatedParam calculatedParam = judgeExecuteParam(baseParam);
// 测点索引
if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
// calculatedParam.setIdList(Arrays.asList("c5f4925dbe333230810fe1a6afc51dfb","293178e56cd207e6e33586090f5034be","2df0cb6a87ef523b726c0a9ff6cfd288","43a1391b503bcd7f1b9a2f8f90e53e0f"
// ));
calculatedParam.setIdList(commTerminalGeneralClient.getRunMonitorIds().getData());
}
LiteflowResponse liteflowResponse;
@@ -151,6 +150,40 @@ public class ExecutionCenter extends BaseController {
}
}
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("新能源专项分析算法执行链")
@PostMapping("/specialAnalysis")
@Async("asyncExecutor")
public void specialAnalysisExecutor(@RequestBody BaseParam baseParam) {
String methodDescribe = getMethodDescribe("specialAnalysisExecutor");
//手动判断参数是否合法,
CalculatedParam calculatedParam = judgeExecuteParam(baseParam);
// 测点索引
if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
calculatedParam.setIdList(deptLineFeignClient.getLineByDeptIdAndNewStation(null).getData());
}
LiteflowResponse liteflowResponse;
if (baseParam.isRepair()) {
//补招时,起始日期、截止日期必填
DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
//递增日期执行算法链
for (int i = 0; i < betweenDay; i++) {
if (i != 0) {
startDate = DateUtil.offsetDay(startDate, 1);
}
calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
liteflowResponse = flowExecutor.execute2Resp("special_analysis", calculatedParam);
dealResponse(calculatedParam, liteflowResponse, methodDescribe);
}
} else {
//非补招
liteflowResponse = flowExecutor.execute2Resp("special_analysis", calculatedParam);
dealResponse(calculatedParam, liteflowResponse, methodDescribe);
}
}
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("监测点算法执行链(按小时执行的算法)")
@PostMapping("/measurementPointExecutorByHour")
@@ -188,7 +221,7 @@ public class ExecutionCenter extends BaseController {
}
} else {
//非补招
calculatedParam.setDataDate(DateUtil.format(DateUtil.offsetHour( DateUtil.parse(baseParam.getDataDate(), DatePattern.NORM_DATETIME_FORMAT),-1), DatePattern.NORM_DATETIME_FORMATTER));
calculatedParam.setDataDate(DateUtil.format(DateUtil.offsetHour(DateUtil.parse(baseParam.getDataDate(), DatePattern.NORM_DATETIME_FORMAT), -1), DatePattern.NORM_DATETIME_FORMATTER));
liteflowResponse = flowExecutor.execute2Resp("measurement_point_hour", calculatedParam);
dealResponse(calculatedParam, liteflowResponse, methodDescribe);
}
@@ -272,7 +305,8 @@ public class ExecutionCenter extends BaseController {
}
}
//
//
// @OperateInfo(info = LogEnum.BUSINESS_COMMON)
// @ApiOperation("pms国网上送单位层级算法执行链")
// @PostMapping("/uploadOrgExecutor")

View File

@@ -21,7 +21,7 @@ import javax.annotation.Resource;
public class DeviceExecutor extends BaseExecutor {
@Resource
private IDataOnlineRateService onlineRateService;
private IDataOnlineRateService onlineRateService;
/**
@@ -34,6 +34,7 @@ public class DeviceExecutor extends BaseExecutor {
public boolean deviceOnlineRateAccess(NodeComponent bindCmp) {
return isAccess(bindCmp);
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "deviceOnlineRate", nodeType = NodeTypeEnum.COMMON)
public void deviceOnlineRateProcess(NodeComponent bindCmp) {
onlineRateService.dataOnlineRate(bindCmp.getRequestData());
@@ -43,6 +44,7 @@ public class DeviceExecutor extends BaseExecutor {
public boolean dataOnlineRateMonthAccess(NodeComponent bindCmp) {
return isAccess(bindCmp);
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "deviceOnlineRateMonth", nodeType = NodeTypeEnum.COMMON)
public void deviceOnlineRateMonthProcess(NodeComponent bindCmp) {
onlineRateService.dataOnlineRateMonth(bindCmp.getRequestData());

View File

@@ -0,0 +1,58 @@
package com.njcn.algorithm.executor;
import com.njcn.algorithm.service.line.ISpecialAnalysisService;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import lombok.RequiredArgsConstructor;
import javax.annotation.Resource;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年11月06日 15:59
*/
@LiteflowComponent
@RequiredArgsConstructor
public class SpecialAnalysisExecutor extends BaseExecutor {
@Resource
private ISpecialAnalysisService specialAnalysisService;
/**
* 算法名: 高低压穿越算法(sp_through)
*
* @param bindCmp
* @return
*/
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS, nodeId = "spThrough", nodeType = NodeTypeEnum.COMMON)
public boolean spThroughAccess(NodeComponent bindCmp) {
return isAccess(bindCmp);
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "spThrough", nodeType = NodeTypeEnum.COMMON)
public void spThroughProcess(NodeComponent bindCmp) {
specialAnalysisService.dataDaySpThroughHandle(bindCmp.getRequestData());
}
/**
* 算法名: 有功功率趋势算法(r_active_power_range)
*
* @param bindCmp
* @return
*/
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS, nodeId = "activePower", nodeType = NodeTypeEnum.COMMON)
public boolean activePowerAccess(NodeComponent bindCmp) {
return isAccess(bindCmp);
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "activePower", nodeType = NodeTypeEnum.COMMON)
public void activePowerProcess(NodeComponent bindCmp) {
specialAnalysisService.dataDayActivePowerHandle(bindCmp.getRequestData());
}
}

View File

@@ -0,0 +1,31 @@
package com.njcn.algorithm.service.line;
import com.njcn.algorithm.pojo.bo.CalculatedParam;
/**
*@Author: wr
* @Description: 高低电压穿越记录
* @Author: wr
* @Date: 2025/4/1 15:22
*/
public interface ISpecialAnalysisService {
/**
* 高低电压穿越记录
* @param calculatedParam
* @Author: wr
* @Date: 2025/4/1 15:21
*/
void dataDaySpThroughHandle(CalculatedParam<String> calculatedParam);
/**
* 有功功率趋势
* @param calculatedParam
* @Author: wr
* @Date: 2025/4/1 18:44
*/
void dataDayActivePowerHandle(CalculatedParam<String> calculatedParam);
}

View File

@@ -0,0 +1,477 @@
package com.njcn.algorithm.serviceimpl.line;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.njcn.algorithm.pojo.bo.CalculatedParam;
import com.njcn.algorithm.service.line.ISpecialAnalysisService;
import com.njcn.dataProcess.api.DataHarmpowerPFeignClient;
import com.njcn.dataProcess.api.DataLimitRateDetailFeignClient;
import com.njcn.dataProcess.api.SpThroughFeignClient;
import com.njcn.dataProcess.param.LineCountEvaluateParam;
import com.njcn.dataProcess.pojo.dto.*;
import com.njcn.dataProcess.util.TimeUtils;
import com.njcn.device.biz.commApi.CommLineClient;
import com.njcn.device.biz.pojo.dto.LineDTO;
import com.njcn.device.pms.pojo.param.MonitorTerminalParam;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.vo.LineDetailDataVO;
import com.njcn.event.api.EventDetailFeignClient;
import com.njcn.event.api.TransientFeignClient;
import com.njcn.event.file.pojo.dto.WaveDataDTO;
import com.njcn.event.pojo.param.EventCountParam;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.supervision.api.UserLedgerFeignClient;
import com.njcn.supervision.pojo.vo.user.NewUserReportVO;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.api.DictTreeFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.enums.DicTreeEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.system.pojo.vo.DictTreeVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 高低电压穿越 服务类
*
* @author guofeihu
* @since 2024-08-22
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class SpecialAnalysisServiceImpl implements ISpecialAnalysisService {
@Value("${line.num}")
private Integer NUM = 100;
@Resource
private EventDetailFeignClient eventDetailFeignClient;
@Resource
private TransientFeignClient transientFeignClient;
@Resource
private LineFeignClient lineFeignClient;
@Resource
private DictTreeFeignClient dictTreeFeignClient;
@Resource
private DicDataFeignClient dicDataFeignClient;
@Resource
private SpThroughFeignClient spThroughFeignClient;
@Resource
private DataHarmpowerPFeignClient dataHarmpowerPFeignClient;
@Resource
private CommLineClient commLineClient;
@Resource
private UserLedgerFeignClient userLedgerFeignClient;
@Resource
private DataLimitRateDetailFeignClient dataLimitRateDetailFeignClient;
@Override
public void dataDaySpThroughHandle(CalculatedParam<String> calculatedParam) {
List<SpThroughDto> info = new ArrayList<>();
List<String> lineList = calculatedParam.getIdList();
if (CollUtil.isNotEmpty(lineList)) {
DictData dip = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_DIP.getCode()).getData();
DictData rise = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_RISE.getCode()).getData();
DictTreeVO windFarms = dictTreeFeignClient.queryByCode(DicTreeEnum.Wind_Farms.getCode()).getData();
DictTreeVO powerStation = dictTreeFeignClient.queryByCode(DicTreeEnum.Power_Station.getCode()).getData();
LocalDateTime currentTime = LocalDateTime.now();
List<List<String>> pendingIds = ListUtils.partition(lineList, NUM);
MonitorTerminalParam monitorTerminalParam = new MonitorTerminalParam();
monitorTerminalParam.setSystemType(0);
monitorTerminalParam.setType(0);
for (List<String> pendingId : pendingIds) {
List<LineDetailDataVO> lineDetailDataVOS = lineFeignClient.getLineDetailList(pendingId).getData();
if (CollUtil.isNotEmpty(lineDetailDataVOS)) {
Map<String, LineDetailDataVO> lineDetailDataMap = lineDetailDataVOS.stream().collect(Collectors.toMap(LineDetailDataVO::getLineId, Function.identity()));
EventCountParam param = new EventCountParam();
param.setIds(new ArrayList<>(lineDetailDataMap.keySet()));
param.setStartTime(TimeUtils.getBeginOfDay(calculatedParam.getDataDate()));
param.setEndTime(currentTime.format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
List<RmpEventDetailPO> evenStDetailPOS = eventDetailFeignClient.getNewEventDetailByTime(param).getData();
for (RmpEventDetailPO rmpEventDetailPO : evenStDetailPOS) {
if (lineDetailDataMap.containsKey(rmpEventDetailPO.getMeasurementPointId())) {
LineDetailDataVO line = lineDetailDataMap.get(rmpEventDetailPO.getMeasurementPointId());
//准备高低电压穿越实体bean
SpThroughDto spThroughPO = new SpThroughDto();
spThroughPO.setEventId(rmpEventDetailPO.getEventId());
spThroughPO.setEventType(rmpEventDetailPO.getEventType());
spThroughPO.setStationType(line.getBigObjType());
//默认该事件没有穿越
spThroughPO.setIsOrNot(0);
//设置波形查询的条件:事件ID
monitorTerminalParam.setId(rmpEventDetailPO.getEventId());
//获取RMS波形数据(由于WaveDataDTO为复杂对象所以转成byte数组在进行反序列化)
byte[] bytes = transientFeignClient.getTransientAnalyseWaveToByteArray(monitorTerminalParam).getData();
WaveDataDTO waveDataDTO = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
//强转成WaveDataDTO
waveDataDTO = (WaveDataDTO) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
//标称电压
if (line.getScale() != null) {
BigDecimal voltageLevel = BigDecimal.valueOf(Float.parseFloat(line.getScale()))
.divide(BigDecimal.valueOf(1.732), 2, RoundingMode.HALF_UP);
//格式化数据源
List<List<String>> newListRmsData = listRmsTimeDataFormat(waveDataDTO);
//暂升事件
if (rmpEventDetailPO.getEventType().equals(rise.getId())) {
if (BooleanUtil.or(isRiseThrough(125, 130, 500, voltageLevel, newListRmsData),
isRiseThrough(120, 125, 1000, voltageLevel, newListRmsData),
isRiseThrough(110, 120, 10000, voltageLevel, newListRmsData))) {
spThroughPO.setIsOrNot(1);
}
}
//暂降事件
if (rmpEventDetailPO.getEventType().equals(dip.getId())) {
//风电场
if (line.getBigObjType().equals(windFarms.getId())) {
if (BooleanUtil.or(isDipThrough(20, 625, 1, voltageLevel, newListRmsData),
isDipThrough(20, 2000, 2, voltageLevel, newListRmsData))
) {
spThroughPO.setIsOrNot(1);
}
}
//光伏电站
if (line.getBigObjType().equals(powerStation.getId())) {
if (BooleanUtil.or(isDipThrough(0, 150, 1, voltageLevel, newListRmsData),
isDipThrough(20, 625, 1, voltageLevel, newListRmsData),
isDipThrough(20, 90, null, voltageLevel, newListRmsData))) {
spThroughPO.setIsOrNot(1);
}
}
}
info.add(spThroughPO);
}
}
}
}
}
}
if (CollUtil.isNotEmpty(info)) {
spThroughFeignClient.batchInsertionThrough(info);
}
}
@Override
public void dataDayActivePowerHandle(CalculatedParam<String> calculatedParam) {
List<RActivePowerRangeDto> info = new ArrayList<>();
DictTreeVO windFarms = dictTreeFeignClient.queryByCode(DicTreeEnum.Wind_Farms.getCode()).getData();
List<String> lineList = calculatedParam.getIdList();
if (CollUtil.isNotEmpty(lineList)) {
List<LineDTO> data = commLineClient.getLineDetailBatch(lineList).getData();
List<LineDTO> windLine = data.stream()
.filter(x -> StrUtil.isNotBlank(x.getBigObjType()))
.filter(x -> x.getBigObjType().equals(windFarms.getId())).collect(Collectors.toList());
LineCountEvaluateParam lineParam = new LineCountEvaluateParam();
lineParam.setStartTime(TimeUtils.getBeginOfDay(calculatedParam.getDataDate()));
lineParam.setEndTime(TimeUtils.getEndOfDay(calculatedParam.getDataDate()));
LocalDate localDate = LocalDateTimeUtil.parseDate(calculatedParam.getDataDate(), DatePattern.NORM_DATE_PATTERN);
if (CollUtil.isNotEmpty(windLine)) {
List<List<LineDTO>> pendingIds = ListUtils.partition(windLine, NUM);
for (List<LineDTO> pendingId : pendingIds) {
//获取分钟有功功率数据
Map<String, String> objIdMap = pendingId.stream().collect(Collectors.toMap(LineDTO::getLineId, LineDTO::getObjId));
lineParam.setLineId(new ArrayList<>(objIdMap.keySet()));
List<DataPowerPDto> dataPowerPList = dataHarmpowerPFeignClient.getRawData(lineParam).getData();
Map<String, List<DataPowerPDto>> lineP = dataPowerPList.stream().collect(Collectors.groupingBy(DataPowerPDto::getLineId));
//获取用户信息
List<NewUserReportVO> userPower = userLedgerFeignClient.getUserReportByIds(new ArrayList<>(objIdMap.values())).getData();
Map<String, Double> userPowerMap = userPower.stream().collect(Collectors.toMap(NewUserReportVO::getId, NewUserReportVO::getRatePower));
lineP.forEach((lineId, dtoList) -> {
if (objIdMap.containsKey(lineId)) {
//根据监测点找到用户,再根据用户找到额定有功功率
String s = objIdMap.get(lineId);
if (userPowerMap.containsKey(s)) {
RActivePowerRangeDto dto = new RActivePowerRangeDto();
dto.setLineId(lineId);
dto.setTimeId(localDate);
Double ratePower = userPowerMap.get(s);
//默认初始10个空集合
List<String>[] min = new List[10];
for (int i = 0; i < 10; i++) {
min[i] = new ArrayList<>();
}
for (DataPowerPDto dataPowerPDto : dtoList) {
String minTime = dataPowerPDto.getMinTime().substring(11, dataPowerPDto.getMinTime().length());
double temp = BigDecimal.valueOf(ratePower)
.divide(BigDecimal.valueOf(dataPowerPDto.getP()), 4, RoundingMode.HALF_UP).doubleValue();
Integer i = getInteger(temp);
if (ObjectUtil.isNotNull(i)) {
min[i].add(minTime);
}
}
for (int i = 0; i < 10; i++) {
try {
Field minTime = RActivePowerRangeDto.class.getDeclaredField("minsTime" + i);
Field minNum = RActivePowerRangeDto.class.getDeclaredField("minsNum" + i);
Field isOrNot = RActivePowerRangeDto.class.getDeclaredField("isOrNot" + i);
minTime.setAccessible(true);
minNum.setAccessible(true);
isOrNot.setAccessible(true);
if (CollUtil.isEmpty(min[i])) {
minTime.set(dto, "[]");
} else {
List<String> mins = min[i].stream().distinct().collect(Collectors.toList());
minTime.set(dto, JSON.toJSONString(mins));
List<String> limitTime = dataLimitRateDetailFeignClient.getLimitRateDetailTime(lineId, calculatedParam.getDataDate()).getData();
if (CollUtil.isNotEmpty(limitTime)) {
List<String> collect = mins.stream().filter(x -> limitTime.contains(x)).collect(Collectors.toList());
if (CollUtil.isNotEmpty(collect)) {
isOrNot.set(dto, 1);
}
}
}
minNum.set(dto, min[i].size());
} catch (Exception e) {
e.printStackTrace();
}
}
info.add(dto);
}
}
});
}
}
}
if (CollUtil.isNotEmpty(info)) {
spThroughFeignClient.batchInsertionPower(info);
}
}
/**
* @param waveDataDTO
* @Description: 格式化RMS数据:将listRmsData中的电压数据取出在根据pt变比组成新的RMS数据
* @return: java.util.List<java.util.List < java.lang.Float>>
* @Author: wr
* @Date: 2025/3/26 19:49
*/
private List<List<String>> listRmsTimeDataFormat(WaveDataDTO waveDataDTO) {
//重新便利数据信息
List<List<String>> info = new ArrayList<>();
List<String> a = new ArrayList<>();
List<String> b = new ArrayList<>();
List<String> c = new ArrayList<>();
List<Integer> indexs = new ArrayList<>();
//根据channelNames取出电压对应listRmsData数据中的数据索引
for (int i = 0; i < waveDataDTO.getChannelNames().size(); i++) {
if (waveDataDTO.getChannelNames().get(i).indexOf("U") != -1) {
indexs.add(i);
}
}
//前端展示的波形图数据来源就是这个所以这边也跟前端保持一致
for (int i = 0; i < waveDataDTO.getListRmsData().size(); i++) {
List<Float> cur = waveDataDTO.getListRmsData().get(i);
//数组第一个为时间
Float time = cur.get(0);
if (time >= 0) {
//便利电压数据的索引
for (Integer index : indexs) {
//根据PT变比算出新的数据(前端取的一次值,也是根据pt变比算出的,所以这边和前端保持一致)新增到新的数组中
if (1 == index) {
a.add(time + "_" + cur.get(index));
}
if (2 == index) {
b.add(time + "_" + cur.get(index));
}
if (3 == index) {
c.add(time + "_" + cur.get(index));
}
}
}
}
info.add(a);
info.add(b);
info.add(c);
return info;
}
/**
* 暂降
* 风电场
* a风电场并网点电压跌至标称电压的20%时,风电场内的风电机组应保证不脱网连续运行625 ms。
* b风电场并网点电压在发生跌落后2s内能够恢复到标称电压的90%时,风电场内的风电机组应保证不脱网连续运行。
* 光伏电站
* a光伏发电站并网点电压跌至0时,光伏发电站内的光伏逆变器和无功补偿装置应能够不脱网连续运行150 ms;
* b光伏发电站并网点电压跌至标称电压的20%时,光伏发电站内的光伏逆变器和无功补偿装置n)应能够不脱网连续运行625ms;
* c光伏发电站并网点电压跌至标称电压的20%以上至90%时,光伏发电站内的光伏逆变器和无功补偿装置应能在阴影区域内不脱网连续运行
*
* @return
*/
private boolean isDipThrough(int start, int ms, Integer option, BigDecimal voltageLevel, List<List<String>> newListRmsData) {
if (ObjectUtil.isNotNull(option)) {
for (List<String> newListRmsDatum : newListRmsData) {
List<String> temp = new ArrayList<>();
Double anchorPoint = -1.0;
for (String num : newListRmsDatum) {
String[] split = num.split("_");
Double time = Double.valueOf(split[0]);
Double v = Double.valueOf(split[1]);
//获得数据占比 (56.62/(220/1.732))*100
BigDecimal curValue = BigDecimal.valueOf(v).multiply(BigDecimal.valueOf(100))
.divide(voltageLevel, 2, RoundingMode.HALF_UP);
if (1 == option) {
//方案一判断是否有跌落到20%的情况
if (start == curValue.doubleValue()) {
temp.add(num);
} else {
if (CollUtil.isNotEmpty(temp)) {
Integer startRms = Integer.valueOf(temp.get(0).split("_")[0]);
Integer endRms = Integer.valueOf(temp.get(temp.size()).split("_")[0]);
if (endRms - startRms > ms) {
return true;
}
temp = new ArrayList<>();
}
}
}
if (2 == option) {
//方案二判断是否有跌落到90%的情况
if (voltageLevel.doubleValue() > v && anchorPoint != -1.0) {
anchorPoint = time + ms;
} else if (anchorPoint.equals(time)) {
if (v < voltageLevel.doubleValue() * 0.9) {
return true;
}
anchorPoint = -1.0;
}
}
if (3 == option) {
//方案三20%以上至90%时,需要判断上升信息
// if (voltageLevel > v && anchorPoint != -1.0) {
// anchorPoint = time + ms;
// } else if (anchorPoint.equals(time)) {
// if (v < voltageLevel*0.9) {
// return true;
// }
// anchorPoint = -1.0;
// }
}
}
}
}
return false;
}
/**
* 暂升
* 风电场
* a风电场并网点电压升高至标称电压的 125%~130%之间时,风电场内的风电机组应保证不脱网连续运行 500 ms;
* b风电场并网点电压升高至标称电压的 120%~125%之间时,风电场内的风电机组应保证不脱网连续运行1s;
* c风电场并网点电压升高至标称电压的 110%~120%之间时,风电场内的风电机组应保证不脱网连续运行 10 s。
* 光伏电站
* a光伏发电站并网点电压升高至标称电压的125%以上至130%时,光伏发电站内的光伏逆变器和无功补偿装置应能够不脱网连续运行500ms;
* b光伏发电站并网点电压升高至标称电压的120%以上至125%时,光伏发电站内的光伏逆变器和无功补偿装置应能够不脱网连续运行1s;
* c光伏发电站并网点电压升高至标称电压的110%以上至120%时,光伏发电站内的光伏逆变器和其他无功补偿装置应能够不脱网连续运行10s。
*
* @Author: wr
* @Date: 2025/4/3 11:10
*/
private boolean isRiseThrough(int start, int end, int ms, BigDecimal voltageLevel, List<List<String>> newListRmsData) {
//分别有abc三相电压值 [0]是毫秒 [1]A相值 [2]B相值 [3]C相值
for (List<String> newListRmsDatum : newListRmsData) {
List<String> temp = new ArrayList<>();
for (String num : newListRmsDatum) {
String[] split = num.split("_");
Double v = Double.valueOf(split[1]);
//获得数据占比 (56.62/(220/1.732))*100
BigDecimal curValue = BigDecimal.valueOf(v).multiply(BigDecimal.valueOf(100))
.divide(voltageLevel
.divide(BigDecimal.valueOf(1.732), 2, RoundingMode.HALF_UP), 2, RoundingMode.HALF_UP);
if (NumberUtil.isIn(curValue, BigDecimal.valueOf(start), BigDecimal.valueOf(end))) {
temp.add(num);
} else {
if (CollUtil.isNotEmpty(temp)) {
int maxIndex = getMaxIndex(temp);
Integer startRms = Integer.valueOf(temp.get(maxIndex).split("_")[0]);
Integer endRms = Integer.valueOf(temp.get(temp.size()).split("_")[0]);
if (endRms - startRms > ms) {
return true;
}
temp = new ArrayList<>();
}
}
}
}
return false;
}
private int getMaxIndex(List<String> temp) {
double maxValue = Double.MIN_VALUE;
int maxIndex = -1;
for (int i = 0; i < temp.size(); i++) {
String[] parts = temp.get(i).split("_");
if (parts.length != 2) {
continue; // 跳过格式错误的元素
}
double value = Double.parseDouble(parts[1]);
if (value > maxValue) {
maxValue = value;
maxIndex = i;
}
}
return maxIndex;
}
private Integer getInteger(double temp) {
Integer i = null;
if (temp <= 0.1) {
i = 0;
} else if (0.1 < temp && temp <= 0.2) {
i = 1;
} else if (0.2 < temp && temp <= 0.3) {
i = 2;
} else if (0.3 < temp && temp <= 0.4) {
i = 3;
} else if (0.4 < temp && temp <= 0.5) {
i = 4;
} else if (0.5 < temp && temp <= 0.6) {
i = 5;
} else if (0.6 < temp && temp <= 0.7) {
i = 6;
} else if (0.7 < temp && temp <= 0.8) {
i = 7;
} else if (0.8 < temp && temp <= 0.9) {
i = 8;
} else if (temp > 0.9) {
i = 9;
}
return i;
}
}