更换git仓库后的提交

This commit is contained in:
2025-09-25 10:57:37 +08:00
parent 9e8662efc0
commit 5c8088e808
741 changed files with 62243 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
package com.njcn.product.advance.eventSource.controller;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.product.advance.eventSource.service.EventRelevantAnalysisService;
import com.njcn.product.terminal.mysqlTerminal.pojo.param.LargeScreenCountParam;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
/**
* pqs
* 事件关联分析
*
* @author cdf
* @date 2023/6/30
*/
@Slf4j
@RestController
@RequestMapping("process")
@Api(tags = "暂降事件关联分析")
@RequiredArgsConstructor
public class EventRelevantAnalysisController extends BaseController {
private final EventRelevantAnalysisService eventRelevantAnalysisService;
@PostMapping("processEvents")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("启动关联分析")
public HttpResult<Object> processEvents(@RequestBody LargeScreenCountParam param) {
String methodDescribe = getMethodDescribe("processEvents");
List<LocalDateTime> timeVal = checkLocalDate(param.getSearchBeginTime(),param.getSearchEndTime());
eventRelevantAnalysisService.processEvents(timeVal.get(0),timeVal.get(1),param.getDeptId());
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
/**
* 校验字符串起始时间和结束时间并返回时间格式时间
* @author cdf
* @date 2023/8/10
*/
public List<LocalDateTime> checkLocalDate(String startTime,String endTime) {
List<LocalDateTime> resultList = new ArrayList<>();
if(StrUtil.isBlank(startTime) || StrUtil.isBlank(endTime)){
throw new BusinessException(CommonResponseEnum.TIME_ERROR);
}
try {
startTime = startTime+StrUtil.SPACE+"00:00:00";
endTime = endTime+StrUtil.SPACE+"23:59:59";
LocalDateTime start = LocalDateTime.parse(startTime, DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN));
LocalDateTime end = LocalDateTime.parse(endTime,DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN));
resultList.add(start);
resultList.add(end);
} catch (Exception e) {
throw new BusinessException(CommonResponseEnum.TIME_ERROR);
}
return resultList;
}
}

View File

@@ -0,0 +1,24 @@
package com.njcn.product.advance.eventSource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.njcn.product.advance.eventSource.pojo.po.PqsRelevanceLog;
import java.util.List;
/**
* pqs
*
* @author cdf
* @date 2023/6/19
*/
public interface RelevantLogMapper extends BaseMapper<PqsRelevanceLog> {
}

View File

@@ -0,0 +1,26 @@
package com.njcn.product.advance.eventSource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.njcn.product.advance.eventSource.pojo.dto.eventAggregate.EntityLogic;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.RmpEventDetailPO;
import java.util.List;
/**
* pqs
*
* @author cdf
* @date 2023/6/19
*/
public interface RmpEventAdvanceMapper extends BaseMapper<RmpEventDetailPO> {
/**
* 获取母线物理隔绝信息
* @author cdf
* @date 2023/7/21
*/
List<EntityLogic> getLogic();
}

View File

@@ -0,0 +1,21 @@
package com.njcn.product.advance.eventSource.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.njcn.product.advance.eventSource.pojo.dto.eventAggregate.EventAssObj;
import com.njcn.product.advance.eventSource.pojo.po.RmpEventDetailAssPO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* pqs
*
* @author cdf
* @date 2023/8/9
*/
public interface RmpEventDetailAssMapper extends BaseMapper<RmpEventDetailAssPO> {
int insertEventAssData(@Param("list") List<EventAssObj> list);
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.product.advance.eventSource.mapper.RmpEventAdvanceMapper">
<select id="queryLineIdbyPointId" resultType="string" parameterType="string">
select pid from pq_line where id = #{pointId}
</select>
<select id="queryCodebyGuid" resultType="java.lang.Integer" parameterType="java.lang.String">
select triphase from sys_dict_data where id = #{guid}
</select>
<select id="getLogic" resultType="EntityLogic">
SELECT
a.tp_index tPIndex,
b.logic_before nodeBefore,
b.logic_next nodeNext,
d.Algo_Describe type
FROM
pqs_tflgployass a,
pqs_tflgass b,
pqs_transformer c,
sys_dict_data d
WHERE
a.tf_index = b.tf_index
AND b.tf_index = c.tf_index
and c.Wiring = d.id
</select>
</mapper>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.product.advance.eventSource.mapper.RmpEventDetailAssMapper">
<!-- pqs_eventass批量插入数据 -->
<insert id="insertEventAssData" parameterType="java.util.List">
insert into r_mp_event_detail_ass values
<foreach collection="list" item="eventAssData" index="index"
separator=",">
(
#{eventAssData.indexEventAss},#{eventAssData.time},#{eventAssData.describe},
#{eventAssData.bRange},#{eventAssData.indexUser},#{eventAssData.indexUser},#{eventAssData.updateTime},#{eventAssData.updateTime}
)
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,10 @@
package com.njcn.product.advance.eventSource.pojo.constant;
/**
* @author xy
* @date 2021/12/29 15:10
*/
public interface HarmonicValidMessage {
String DATA_NOT_BLANK = "参数不能为空";
}

View File

@@ -0,0 +1,43 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
import lombok.Data;
/**
* pqs
*
* @author cdf
* @date 2023/7/20
*/
@Data
public class EntityGroupData {
private int idx[];
private int all_evt_num;
private int evt_in_num;
private int evt_out_num;
private int evt_res_num;
private int Matrixcata[][];
private EntityGroupEvtData in_buf[];
private EntityGroupEvtData out_buf[];
private EntityGroupEvtData res_buf[];
private EntityGroupEvtData grp_buf[][];
private int grp_num[];
private int grp_all_num;
private EntityGroupEvtData grp_cata_buf[][][];
private int grp_cata_num[][];
public EntityGroupData() {
idx = new int[FinalData.MAX_EVT_NUM];
Matrixcata = new int[FinalData.MAX_CATA_NUM][FinalData.MAX_EVT_NUM];
in_buf = new EntityGroupEvtData[FinalData.MAX_EVT_NUM];
out_buf = new EntityGroupEvtData[FinalData.MAX_EVT_NUM];
res_buf = new EntityGroupEvtData[FinalData.MAX_EVT_NUM];
grp_buf = new EntityGroupEvtData[FinalData.MAX_GROUP_NUM][FinalData.MAX_EVT_NUM];
grp_num = new int[FinalData.MAX_GROUP_NUM];
grp_cata_buf = new EntityGroupEvtData[FinalData.MAX_GROUP_NUM][FinalData.MAX_CATA_NUM
+ 2][FinalData.MAX_EVT_NUM];
grp_cata_num = new int[FinalData.MAX_GROUP_NUM][FinalData.MAX_CATA_NUM + 2];
}
}

View File

@@ -0,0 +1,120 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
public class EntityGroupEvtData implements Cloneable,Comparable<EntityGroupEvtData> {
//逻辑节点序号
private int node;
//事件开始时间时标
private int start_time;
//类别
private int cata;
//标注类别
private int cata2;
//物理节点
private String nodePhysics;
private SagEvent sagEvent;
private String sagReason;
public EntityGroupEvtData(String nodePhysics, int start_time, int cata, int cata2,SagEvent sagEvent,String sagReason) {
this.nodePhysics = nodePhysics;
this.start_time = start_time;
this.cata = cata;
this.cata2 = cata2;
this.sagEvent = sagEvent;
this.sagReason = sagReason;
}
public SagEvent getSagEvent() {
return sagEvent;
}
public void setSagEvent(SagEvent sagEvent) {
this.sagEvent = sagEvent;
}
public String getNodePhysics() {
return nodePhysics;
}
public void setNodePhysics(String nodePhysics) {
this.nodePhysics = nodePhysics;
}
public int getNode() {
return node;
}
public void setNode(int node) {
this.node = node;
}
public int getStart_time() {
return start_time;
}
public void setStart_time(int start_time) {
this.start_time = start_time;
}
public int getCata() {
return cata;
}
public void setCata(int cata) {
this.cata = cata;
}
public int getCata2() {
return cata2;
}
public void setCata2(int cata2) {
this.cata2 = cata2;
}
public String getSagReason() {
return sagReason;
}
public void setSagReason(String sagReason) {
this.sagReason = sagReason;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Object objClone() {
try {
return clone();
} catch (CloneNotSupportedException e) {
return new EntityGroupEvtData("-1", -1, -1, -1,null,null);
}
}
@Override
public String toString() {
return "EntityGroupEvtData{" +
"node=" + node +
", start_time=" + start_time +
", cata=" + cata +
", cata2=" + cata2 +
'}';
}
@Override
public int compareTo(EntityGroupEvtData obj) {
if(this.getStart_time() < obj.getStart_time()){
return -1;
}else if(this.getStart_time() > obj.getStart_time()){
return 1;
}
return 0;
}
}

View File

@@ -0,0 +1,21 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
import lombok.Data;
@Data
public class EntityLogic {
//物理隔绝变压器策略GUID
private String tPIndex;
//变压器逻辑上节点
private Integer node_h;
//变压器逻辑下节点
private Integer node_l;
// 变压器连接方式
private Integer type;
//变压器物理上节点
private String nodeBefore;
//变压器物理下节点
private String nodeNext;
}

View File

@@ -0,0 +1,70 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
import java.io.Serializable;
import java.util.Arrays;
public class EntityMtrans implements Serializable {
private static final long serialVersionUID = 1L;
private int Matrixcata0[][];
private int Matrixcata1[][];
private int Mtrans[][];
private int possiable_path[][];
private int path_num;
public EntityMtrans() {
super();
Mtrans = new int[FinalData.NODE_NUM][FinalData.NODE_NUM];
Matrixcata0 = new int[FinalData.EVT_TYPE_NUM][FinalData.NODE_NUM];
Matrixcata1 = new int[FinalData.EVT_TYPE_NUM][FinalData.NODE_NUM];
possiable_path = new int[FinalData.MAX_PATH_NUM][FinalData.NODE_NUM + 1];
path_num = 0;
}
public int[][] getMatrixcata0() {
return Matrixcata0;
}
public void setMatrixcata0(int[][] matrixcata0) {
Matrixcata0 = matrixcata0;
}
public int[][] getMatrixcata1() {
return Matrixcata1;
}
public void setMatrixcata1(int[][] matrixcata1) {
Matrixcata1 = matrixcata1;
}
public int[][] getMtrans() {
return Mtrans;
}
public void setMtrans(int[][] mtrans) {
Mtrans = mtrans;
}
public int[][] getPossiable_path() {
return possiable_path;
}
public void setPossiable_path(int[][] possiable_path) {
this.possiable_path = possiable_path;
}
public int getPath_num() {
return path_num;
}
public void setPath_num(int path_num) {
this.path_num = path_num;
}
@Override
public String toString() {
return "EntityMtrans [Matrixcata0=" + Arrays.toString(Matrixcata0) + ", Matrixcata1="
+ Arrays.toString(Matrixcata1) + ", Mtrans=" + Arrays.toString(Mtrans) + ", possiable_path="
+ Arrays.toString(possiable_path) + ", path_num=" + path_num + "]";
}
}

View File

@@ -0,0 +1,122 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
/*
*一个归一化事件包含多个事件(一对多)
*indexEventAss:事件关联分析表Guid
*time:归一化中第一个时间
*describe:关联事件描述
*bRange:是否进行范围分析
*indexUser:用户表Guid
*updateTime:更新时间
*state:数据状态
*name:关联事件名称
*list:属于该归一化事件的暂降事件
*strTime:字符串时间
*/
@Data
public class EventAssObj implements Serializable {
private String indexEventAss;
private LocalDateTime time;
private String describe;
private int bRange;
private String indexUser;
private LocalDateTime updateTime = LocalDateTime.now();
private int state;
private String name;
private String strTime;
private List<SagEvent> list;
public String getStrTime() {
return strTime;
}
public void setStrTime(String strTime) {
this.strTime = strTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIndexEventAss() {
return indexEventAss;
}
public void setIndexEventAss(String indexEventAss) {
this.indexEventAss = indexEventAss;
}
public LocalDateTime getTime() {
return time;
}
public void setTime(LocalDateTime time) {
this.time = time;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public int getbRange() {
return bRange;
}
public void setbRange(int bRange) {
this.bRange = bRange;
}
public String getIndexUser() {
return indexUser;
}
public void setIndexUser(String indexUser) {
this.indexUser = indexUser;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public List<SagEvent> getList() {
return list;
}
public void setList(List<SagEvent> list) {
this.list = list;
}
@Override
public String toString() {
return "EventAssObj [indexEventAss=" + indexEventAss + ", time=" + time + ", describe=" + describe + ", bRange="
+ bRange + ", indexUser=" + indexUser + ", updateTime=" + updateTime + ", state=" + state + ", name="
+ name + ", strTime=" + strTime + ", list=" + list + "]";
}
}

View File

@@ -0,0 +1,25 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
public class FinalData {
public static final int TIME_THRESHOLD = 10;//暂降事件按开始时间归集门槛10秒
public static final int MAX_GROUP_NUM = 1000;//分组的最大组数
public static final int MAX_CATA_NUM = 7;//类别数
public static final int MAX_EVT_NUM = 1000;//最大事件个数
public static final int QVVR_TYPE_THREE = 9; //三相故障
public static final int QVVR_TYPE_UNKNOWN = 10; //故障类型未知
public static final int QVVR_TYPE_OUTOFRANGE = -1; //节点不在网络拓扑中
public static final int DATA_INF = -1;
public static final int EVT_TYPE_NUM = 6;//故障类型数
public static final int MAX_PATH_NUM = 50;//最大路径数
public static int NODE_NUM;//输入节点数
// 暂降综合评估算法
public static final int CLUSER_NUM = 4; // 系统中各监测点分类后的代表节点
public static final int MAX_LINE_NUM = 1000; // 监测点最多个数
public static final int MAX_STA_NUM = 120; // 支持的子系统个数
static {
NODE_NUM = -1;
}
}

View File

@@ -0,0 +1,32 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
import lombok.Data;
import java.io.Serializable;
/**
* pqs
*终端监测点名称信息
* nameGD:供电公司名称
* nameBD:变电站名称
* nameSubV:母线名称
* namePoint:监测点名称
* indexPoint:监测点的唯一标识
*
* 新增add
* xuyang
* 2021.05.11
* 监测点电压等级:monitorVoltageLevel
* 监测点干扰源类型终:monitorLoadType
*/
@Data
public class PlantInfo implements Serializable {
private String indexPoint;
private String nameGD;
private String nameBD;
private String nameSubV;
private String namePoint;
private String monitorVoltageLevel;
private String monitorLoadType;
private String objName;
}

View File

@@ -0,0 +1,420 @@
package com.njcn.product.advance.eventSource.pojo.dto.eventAggregate;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* pqs
*
* @author cdf
* @date 2023/7/21
*/
public class SagEvent implements Comparable<SagEvent>, Serializable {
// 事件的唯一标识
private String indexEventDetail;
private Integer waveType;
// 暂降事件发生时间
private LocalDateTime sagTime;
// 暂降事件发生时间毫秒
private Integer msec;
// 事件描述
private String events;
// 持续时间
private Float persistTime;
// 发生暂降事件的监测点层级信息
private PlantInfo plantInfo;
// 拼接sagTime和msec
private String strTime;
// 事件发生时刻的毫秒表示
private Long time;
// 监测点的唯一标识
private String indexPoint;
// 归一化事件的GUID
private String indexEventAss;
// 特征幅值
private Float eventValue;
// 暂降原因
private String sagReason;
// 暂降类型
private String sagType;
// 暂降类型描述
private String sagTypeDes;
public String getSagTypeDes() {
return sagTypeDes;
}
public void setSagTypeDes(String sagTypeDes) {
this.sagTypeDes = sagTypeDes;
}
// 暂降深度
private String strEventValue;
private String strPersist;
// 事件是否经过高级算法处理(0-未处理1-已处理默认为0)
private Integer dealFlag;
// 事件是否经过高级算法处理中文描述(已处理、未处理)
private String dealFlagDescription;
// 录波文件是否存在(0-不存在1-存在默认为0)
private Integer fileFlag;
// 录波文件是否存在中文描述(存在、不存在)
private String fileFlagDescription;
// 高级算法返回dq持续时间
private Float dqTime;
// 高级算法处理事件个数记录
private Integer number;
// 归一化处理更新时间
private LocalDateTime dealTime;
// 高级算法的对应关系
private int cata;
// 第一次事件的触发时间
private LocalDateTime firstTime;
// 第一次事件的暂降类型
private String firstType;
// 第一次事件的触发时间毫秒
private Integer firstMs;
// 第一次事件触发时间date->毫秒
private Long firstTimeMills;
// 暂降严重度
private Float severity;
// 排序方式
private int sortType = 0; // 初始化默认为0-按照时间排序 新增1-按暂降严重度排序 2-暂降发生时刻排序 3-先根据电压等级排序,如果相等再按照暂降幅值排序
//电压等级
private Double voltage;
//监测点对象名称
private String objName;
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
public Integer getWaveType() {
return waveType;
}
public void setWaveType(Integer waveType) {
this.waveType = waveType;
}
private String strVoltage;
public Double getVoltage() {
return voltage;
}
public void setVoltage(Double voltage) {
this.voltage = voltage;
}
public String getStrVoltage() {
return strVoltage;
}
public void setStrVoltage(String strVoltage) {
//转为double
strVoltage = strVoltage.toUpperCase();
String str = strVoltage.substring(0, strVoltage.indexOf("KV"));
this.voltage = Double.parseDouble(str);
}
public int getSortType() {
return sortType;
}
public void setSortType(int sortType) {
this.sortType = sortType;
}
public Float getSeverity() {
return severity;
}
public void setSeverity(Float severity) {
this.severity = severity;
}
public Long getFirstTimeMills() {
return firstTimeMills;
}
public void setFirstTimeMills(Long firstTimeMills) {
this.firstTimeMills = firstTimeMills;
}
public Integer getFirstMs() {
return firstMs;
}
public void setFirstMs(Integer firstMs) {
this.firstMs = firstMs;
}
public LocalDateTime getFirstTime() {
return firstTime;
}
public void setFirstTime(LocalDateTime firstTime) {
this.firstTime = firstTime;
}
public String getFirstType() {
return firstType;
}
public void setFirstType(String firstType) {
this.firstType = firstType;
}
public Integer getFileFlag() {
return fileFlag;
}
public void setFileFlag(Integer fileFlag) {
this.fileFlag = fileFlag;
}
public String getFileFlagDescription() {
return fileFlagDescription;
}
public void setFileFlagDescription(String fileFlagDescription) {
this.fileFlagDescription = fileFlagDescription;
}
public int getCata() {
return cata;
}
public void setCata(int cata) {
this.cata = cata;
}
public LocalDateTime getDealTime() {
return dealTime;
}
public void setDealTime(LocalDateTime dealTime) {
this.dealTime = dealTime;
}
public Float getDqTime() {
return dqTime;
}
public void setDqTime(Float dqTime) {
this.dqTime = dqTime;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public void setDealFlagDescription(String dealFlagDescription) {
this.dealFlagDescription = dealFlagDescription;
}
public String getDealFlagDescription() {
return dealFlagDescription;
}
public Integer getDealFlag() {
return dealFlag;
}
public void setDealFlag(Integer dealFlag) {
this.dealFlag = dealFlag;
}
public String getIndexEventDetail() {
return indexEventDetail;
}
public void setIndexEventDetail(String indexEventDetail) {
this.indexEventDetail = indexEventDetail;
}
public LocalDateTime getSagTime() {
return sagTime;
}
public void setSagTime(LocalDateTime sagTime) {
this.sagTime = sagTime;
}
public Integer getMsec() {
return msec;
}
public void setMsec(Integer msec) {
this.msec = msec;
}
public String getEvents() {
return events;
}
public void setEvents(String events) {
this.events = events;
}
public Float getPersistTime() {
return persistTime;
}
public void setPersistTime(Float persistTime) {
if (persistTime == null) {
this.persistTime = 0f;
return;
}
float f1 = (float) (Math.round(persistTime.floatValue() * 1000)) / 1000;
this.persistTime = new Float(f1);
}
public PlantInfo getPlantInfo() {
return plantInfo;
}
public void setPlantInfo(PlantInfo plantInfo) {
this.plantInfo = plantInfo;
}
public String getStrTime() {
return strTime;
}
public void setStrTime(String strTime) {
this.strTime = strTime;
}
public Long getTime() {
return time;
}
public void setTime(Long time) {
this.time = time;
}
public String getIndexPoint() {
return indexPoint;
}
public void setIndexPoint(String indexPoint) {
this.indexPoint = indexPoint;
}
public String getIndexEventAss() {
return indexEventAss;
}
public void setIndexEventAss(String indexEventAss) {
this.indexEventAss = indexEventAss;
}
public Float getEventValue() {
return eventValue;
}
public void setEventValue(Float eventValue) {
if (eventValue == null) {
this.eventValue = 0f;
return;
}
this.eventValue = eventValue;
}
public String getSagReason() {
return sagReason;
}
public void setSagReason(String sagReason) {
this.sagReason = sagReason;
}
public String getSagType() {
return sagType;
}
public void setSagType(String sagType) {
this.sagType = sagType;
}
public String getStrEventValue() {
return strEventValue;
}
public void setStrEventValue(String strEventValue) {
this.strEventValue = strEventValue;
}
public String getStrPersist() {
return strPersist;
}
public void setStrPersist(String strPersist) {
this.strPersist = strPersist;
}
// 根据设定规则进行排序
@Override
public int compareTo(SagEvent obj) {
switch (this.getSortType()) {
case 1:
return obj.getSeverity().compareTo(this.getSeverity());
case 2:
return this.getTime().compareTo(obj.getTime());
case 3: {
if (obj.getVoltage().compareTo(this.getVoltage()) != 0) {
return obj.getVoltage().compareTo(this.getVoltage());
}
else {
return this.getEventValue().compareTo(obj.getEventValue());
}
}
default:
break;
}
return this.getFirstTimeMills().compareTo(obj.getFirstTimeMills());
}
}

View File

@@ -0,0 +1,107 @@
package com.njcn.product.advance.eventSource.pojo.enums;
import lombok.Getter;
/**
* @author hongawen
* @version 1.0.0
* @date 2021年04月13日 10:50
*/
@Getter
public enum AdvanceResponseEnum {
ANALYSIS_USER_DATA_ERROR("A0101","解析用采数据内容失败"),
INTERNAL_ERROR("A0101","系统内部异常"),
USER_DATA_EMPTY("A0101","用采数据内容为空"),
USER_DATA_NOT_FOUND("A0101","未找到用采数据"),
RESP_DATA_NOT_FOUND("A0101","未找到责任划分数据"),
WIN_TIME_ERROR("A0101","限值时间小于窗口"),
CALCULATE_INTERVAL_ERROR("A0101","对齐计算间隔值非法"),
RESP_RESULT_DATA_NOT_FOUND("A0101","未找到责任划分缓存数据"),
USER_DATA_P_NODE_PARAMETER_ERROR("A0101","无用采用户或所有用户的完整性均不满足条件"),
RESPONSIBILITY_PARAMETER_ERROR("A0101","调用接口程序计算失败,参数非法"),
EVENT_EMPTY("A0102","没有查询到未分析事件"),
USER_NAME_EXIST("A0103","用户名已存在"),
DATA_NOT_FOUND("A0104","数据缺失,请根据模版上传近两周数据"),
DATA_UNDERRUN("A0104","数据量不足,请根据模版上传充足近两周数据"),
DOCUMENT_FORMAT_ERROR("A0105","数据缺失,导入失败!请检查导入文档的格式是否正确"),
DEVICE_LOST("A0104","用户下缺少设备"),
USER_LOST("A0106","干扰源用户缺失"),
UNCOMPLETE_STRATEGY("A0106","配置安全III级预警,II级预警,I级预警4条完整策略"),
EXISTENCE_EVALUATION_RESULT("A0104","存在评结果结果,如要评估,请删除后评估"),
SG_USER_NAME_REPEAT("A0102","业务用户名重复"),
SG_PRODUCT_LINE_NAME_REPEAT("A0102","生产线名重复"),
SG_USER_ID_MISS("A0102","业务用户id缺失"),
SG_PRODUCT_LINE_ID_MISS("A0102","生产线id缺失"),
SG_MACHINE_ID_MISS("A0102","设备id缺失"),
IMPORT_EVENT_DATA_FAIL("A0102","请检查导入数据的准确性"),
PRODUCT_LINE_DATA_MISS("A0102","生产线数据缺失"),
MACHINE_DATA_MISS("A0102","设备数据缺失"),
INCOMING_LINE_DATA_MISS("A0102","进线数据缺失"),
EVENT_DATA_MISS("A0102","没有可供参考的暂降数据"),
WIN_DATA_ERROR("A0102","算法校验窗宽超限"),
DATA_ERROR("A0102","算法校验数据长度超限"),
INIT_DATA_ERROR("A0102","算法初始化数据失败"),
USER_HAS_PRODUCT("A0102","当前用户存在生产线"),
PRODUCT_HAS_MACHINE("A0102","当前生产线存在设备"),
MACHINE_HAS_UNIT("A0102","当前设备存在元器件"),
EVENT_TIME_ERROR("A0102","暂降事件时间格式有误,请检查"),
INVALID_FILE_TYPE("A0102","请选择CSV文件"),
INSUFFICIENCY_OF_INTEGRITY("A00561","时间范围内谐波数据完整性不足"),
INTERVAL_ERROR("A0102","监测点时间间隔错误"),
;
private final String code;
private final String message;
AdvanceResponseEnum(String code, String message) {
this.code = code;
this.message = message;
}
public static String getCodeByMsg(String msg){
for (AdvanceResponseEnum userCodeEnum : AdvanceResponseEnum.values()) {
if (userCodeEnum.message.equalsIgnoreCase(msg)) {
return userCodeEnum.code;
}
}
return "";
}
}

View File

@@ -0,0 +1,39 @@
package com.njcn.product.advance.eventSource.pojo.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.njcn.db.mybatisplus.bo.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("pqs_relevancy_log")
public class PqsRelevanceLog extends BaseEntity {
@TableId("id")
private String id;
/**
* 归一化算法时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timeId;
/**
* 归一化算法描述
*/
private String contentDes;
private Integer state;
@TableField(exist = false)
private String createName;
}

View File

@@ -0,0 +1,52 @@
package com.njcn.product.advance.eventSource.pojo.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.njcn.db.mybatisplus.bo.BaseEntity;
import com.njcn.product.advance.eventSource.pojo.dto.eventAggregate.SagEvent;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
import java.util.List;
/**
* pqs
*
* @author cdf
* @date 2023/8/9
*/
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("r_mp_event_detail_ass")
public class RmpEventDetailAssPO extends BaseEntity {
/**
*事件关联分析表uuid
*/
@TableId("Event_Ass_Id")
private String eventAssId;
/**
*发生时间(归一化中第一个时间)
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
private LocalDateTime timeId;
/**
*关联事件描述
*/
private String contentDes;
/**
*是否进行范围分析0分析1未分析
*/
private Integer analyseFlag;
@TableField(exist = false)
private List<SagEvent> list;
}

View File

@@ -0,0 +1,35 @@
package com.njcn.product.advance.eventSource.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.RmpEventDetailPO;
import com.njcn.web.pojo.param.BaseParam;
import java.time.LocalDateTime;
import java.util.List;
/**
* pqs
*
* @author cdf
* @date 2023/6/30
*/
public interface EventRelevantAnalysisService extends IService<RmpEventDetailPO> {
/**
*
* @author cdf
* @date 2023/6/30
*/
void processEvents(LocalDateTime startTime,LocalDateTime endTime,String deptId);
}

View File

@@ -0,0 +1,25 @@
package com.njcn.product.advance.eventSource.service;
import com.njcn.influx.pojo.dto.HarmHistoryDataDTO;
import com.njcn.influx.pojo.po.DataHarmPowerP;
import com.njcn.product.advance.responsility.pojo.bo.UserDataExcel;
import com.njcn.product.advance.responsility.pojo.param.HistoryHarmParam;
import com.njcn.product.advance.responsility.pojo.param.PHistoryHarmParam;
import java.util.List;
public interface HistoryHarmonicService {
/***
* 按次、监测点获取指定历史谐波数据
* @author hongawen
* @date 2023/7/19 9:56
* @param historyHarmParam 请求历史谐波数据参数
* @return HarmHistoryDataDTO
*/
HarmHistoryDataDTO getHistoryHarmData(HistoryHarmParam historyHarmParam);
List<DataHarmPowerP> getHarmonicPData(PHistoryHarmParam param);
}

View File

@@ -0,0 +1,7 @@
package com.njcn.product.advance.eventSource.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.product.advance.eventSource.pojo.po.RmpEventDetailAssPO;
public interface RmpEventDetailAssService extends IService<RmpEventDetailAssPO> {
}

View File

@@ -0,0 +1,565 @@
package com.njcn.product.advance.eventSource.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.product.advance.eventSource.mapper.RelevantLogMapper;
import com.njcn.product.advance.eventSource.mapper.RmpEventAdvanceMapper;
import com.njcn.product.advance.eventSource.pojo.dto.eventAggregate.*;
import com.njcn.product.advance.eventSource.pojo.enums.AdvanceResponseEnum;
import com.njcn.product.advance.eventSource.pojo.po.PqsRelevanceLog;
import com.njcn.product.advance.eventSource.pojo.po.RmpEventDetailAssPO;
import com.njcn.product.advance.eventSource.service.EventRelevantAnalysisService;
import com.njcn.product.advance.eventSource.service.RmpEventDetailAssService;
import com.njcn.product.advance.eventSource.utils.UtilNormalization;
import com.njcn.product.system.dict.mapper.DictDataMapper;
import com.njcn.product.system.dict.pojo.enums.DicDataEnum;
import com.njcn.product.system.dict.pojo.enums.DicDataTypeEnum;
import com.njcn.product.system.dict.pojo.po.DictData;
import com.njcn.product.terminal.mysqlTerminal.mapper.LedgerScaleMapper;
import com.njcn.product.terminal.mysqlTerminal.pojo.dto.LedgerBaseInfo;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.PqsTflgployass;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.RmpEventDetailPO;
import com.njcn.product.terminal.mysqlTerminal.pojo.vo.AdvanceEventDetailVO;
import com.njcn.product.terminal.mysqlTerminal.service.CommGeneralService;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* pqs
*
* @author cdf
* @date 2023/6/30
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class EventRelevantAnalysisServiceImpl extends ServiceImpl<RmpEventAdvanceMapper, RmpEventDetailPO> implements EventRelevantAnalysisService {
private final DictDataMapper dictDataMapper;
private final RmpEventAdvanceMapper rmpEventAdvanceMapper;
private final RelevantLogMapper relevantLogMapper;
private final CommGeneralService commGeneralService;
private final RmpEventAdvanceMapper eventAdvanceMapper;
private final LedgerScaleMapper ledgerScaleMapper;
private final RmpEventDetailAssService rmpEventDetailAssService;
@Override
@Transactional(rollbackFor = Exception.class)
public void processEvents(LocalDateTime startTime, LocalDateTime endTime, String deptId) {
TimeInterval timeInterval = new TimeInterval();
//获取节点和变压器配置信息
Map<String, Map<String, Integer>> nodeMap1 = getBeforeNodeInfo();
Set<Map.Entry<String, Map<String, Integer>>> nodeSort1 = nodeMap1.entrySet();
System.out.println(nodeSort1);
Map<String, EntityMtrans> entityMtransMap1 = getNodeInfo();
Set<Map.Entry<String, EntityMtrans>> setMtrans1 = entityMtransMap1.entrySet();
System.out.println(setMtrans1);
LocalDateTime date = LocalDateTime.now();
HandleEvent handleEvent = new HandleEvent();
// 获取并验证基础数据
List<EntityGroupEvtData> baseList = handleEvent.getData(startTime, endTime, deptId);
if (CollectionUtil.isEmpty(baseList)) {
throw new BusinessException("当前时间段暂无可分析事件");
}
List<EntityGroupEvtData> otherEventList = new ArrayList<>(baseList);
Map<String, EntityMtrans> entityMtransMap = getNodeInfo();
Set<Map.Entry<String, EntityMtrans>> setMtrans = entityMtransMap.entrySet();
System.out.println(setMtrans);
//获取节点和变压器配置信息
Map<String, Map<String, Integer>> nodeMap = getBeforeNodeInfo();
Set<Map.Entry<String, Map<String, Integer>>> nodeSort = nodeMap.entrySet();
System.out.println(nodeSort);
//初始化结果容器
List<SagEvent> listSagEvent = new ArrayList<>();
List<RmpEventDetailAssPO> rmpEventDetailAssPoList = new ArrayList<>();
//获取短路故障字典
DictData dictData = dictDataMapper.getDicDataByNameAndTypeName(DicDataTypeEnum.EVENT_REASON.getName(), DicDataEnum.SHORT_TROUBLE.getName());
for (Map.Entry<String, Map<String, Integer>> m : nodeSort) {
List<EntityGroupEvtData> list = new ArrayList<>();
Set<Map.Entry<String, Integer>> mapValue = m.getValue().entrySet();
FinalData.NODE_NUM = m.getValue().size();
for (Map.Entry<String, Integer> mm : mapValue) {
for (EntityGroupEvtData groupEvtData : baseList) {
if (groupEvtData.getNodePhysics().equals(mm.getKey()) && dictData.getId().equals(groupEvtData.getSagReason())) {
groupEvtData.setNode(mm.getValue());
list.add(groupEvtData);
}
}
// 筛选不在矩阵中的事件id
otherEventList.removeIf(entityGroupEvtData -> entityGroupEvtData.getNodePhysics().equals(mm.getKey()) && dictData.getId().equals(entityGroupEvtData.getSagReason()));
}
EntityGroupEvtData[] entityGroupEvtData = new EntityGroupEvtData[list.size()];
Collections.sort(list);
list.toArray(entityGroupEvtData);
for (Map.Entry<String, EntityMtrans> mEntry : setMtrans) {
if (mEntry.getKey().equals(m.getKey())) {
//算法最多处理1000条数据超过限制需分批处理 先将数据根据某种方式进行升序/降序排序,然后分段处理 加入循环处理
int circulation = entityGroupEvtData.length % FinalData.MAX_EVT_NUM == 0
? entityGroupEvtData.length / FinalData.MAX_EVT_NUM
: entityGroupEvtData.length / FinalData.MAX_EVT_NUM + 1;
for (int i = 0; i < circulation; i++) {
int to = 0;
if (i == circulation - 1) {
to = entityGroupEvtData.length % FinalData.MAX_EVT_NUM > 0
? entityGroupEvtData.length
: (i + 1) * FinalData.MAX_EVT_NUM - 1;
} else {
to = (i + 1) * FinalData.MAX_EVT_NUM - 1;
}
EntityGroupEvtData[] arrayObj = Arrays.copyOfRange(entityGroupEvtData,
i * FinalData.MAX_EVT_NUM, to);
EntityMtrans entityMtrans = mEntry.getValue();
EntityGroupData entityGroupData = handleEvent.translate(arrayObj, entityMtrans);
// 处理分析结果
handleEvent.show_group_info(entityGroupData, listSagEvent, rmpEventDetailAssPoList, date);
}
}
}
}
// 处理非标准数据
disposeNonStandardData(handleEvent, otherEventList, rmpEventDetailAssPoList, listSagEvent, date);
rmpEventDetailAssService.saveBatch(rmpEventDetailAssPoList);
List<RmpEventDetailPO> eventUpdateList = new ArrayList<>();
for (int i = 0; i < listSagEvent.size(); i++) {
RmpEventDetailPO rmp = new RmpEventDetailPO();
rmp.setEventId(listSagEvent.get(i).getIndexEventDetail());
rmp.setEventassIndex(listSagEvent.get(i).getIndexEventAss());
rmp.setDealTime(listSagEvent.get(i).getDealTime());
eventUpdateList.add(rmp);
if ((i + 1) % 1000 == 0) {
this.updateBatchById(eventUpdateList);
eventUpdateList.clear();
} else if (i == listSagEvent.size() - 1) {
this.updateBatchById(eventUpdateList);
}
}
// 增加策略记录
String describe = "用户" + RequestUtil.getLoginName() + "进行了关联分析";
PqsRelevanceLog entityPqsRelevance = new PqsRelevanceLog();
entityPqsRelevance.setContentDes(describe);
entityPqsRelevance.setState(DataStateEnum.ENABLE.getCode());
entityPqsRelevance.setTimeId(date);
relevantLogMapper.insert(entityPqsRelevance);
log.info("事件关联分析用时:" + timeInterval.interval() / 1000 + "");
}
/**********************************************************************
* 归集结果与非矩阵事件进行比对
**********************************************************************/
public void disposeNonStandardData(HandleEvent handleEvent, List<EntityGroupEvtData> noDealList, List<RmpEventDetailAssPO> assPoList, List<SagEvent> list2, LocalDateTime date) {
Iterator<EntityGroupEvtData> iterator = noDealList.iterator();
while (iterator.hasNext()) {
EntityGroupEvtData entityGroupEvtData = iterator.next();
for (RmpEventDetailAssPO eventAssObj : assPoList) {
long sRange = Math.abs(Duration.between(eventAssObj.getTimeId(), entityGroupEvtData.getSagEvent().getSagTime()).getSeconds());
if (sRange < 10) {
int b = 0;
int a = 0;
for (SagEvent sagEvent : eventAssObj.getList()) {
if (sagEvent.getCata() == 9) {
b++;
} else if (sagEvent.getCata() != 10) {
a++;
}
}
if (b > 0) {
if (entityGroupEvtData.getCata() < 9) {
break;
}
} else if (a > 0) {
if (entityGroupEvtData.getCata() == 9) {
break;
}
}
iterator.remove();
entityGroupEvtData.getSagEvent().setIndexEventAss(eventAssObj.getEventAssId());
entityGroupEvtData.getSagEvent().setDealTime(date);
eventAssObj.getList().add(entityGroupEvtData.getSagEvent());
String describe = "事件关联分析编号" + eventAssObj.getTimeId() + "共包含" + eventAssObj.getList().size() + "个事件";
eventAssObj.setContentDes(describe);
list2.add(entityGroupEvtData.getSagEvent());
break;
}
}
}
// 如果还有未归集的数据则单独拎为单一事件处理
for (EntityGroupEvtData entityGroupEvtData : noDealList) {
String strUUID = IdUtil.simpleUUID();
entityGroupEvtData.getSagEvent().setIndexEventAss(strUUID);
entityGroupEvtData.getSagEvent().setDealTime(date);
List<SagEvent> dealList = new ArrayList<>();
dealList.add(entityGroupEvtData.getSagEvent());
handleEvent.processing(dealList, assPoList, date);
list2.add(entityGroupEvtData.getSagEvent());
}
}
class HandleEvent {
public EntityGroupData translate(EntityGroupEvtData[] entityGroupEvtData, EntityMtrans entityMtrans) {
// 获取测试数据的数组长度
int testLogNum = entityGroupEvtData.length;
// 实例化EntityGroupData给其中的数组分配空间
EntityGroupData groupBuf = new EntityGroupData();
// 填入日志
setMatrixcata(groupBuf, entityMtrans);
create_evt_buf(entityGroupEvtData, groupBuf, testLogNum);
UtilNormalization.sort_Tstart(groupBuf); // 根据时标进行划分
// 根据暂降类型进行划分
for (int i = 0; i < groupBuf.getGrp_all_num(); i++) {
UtilNormalization.sort_cata(groupBuf, i);
}
return groupBuf;
}
//获取原始暂降数据
public List<EntityGroupEvtData> getData(LocalDateTime startTime, LocalDateTime endTime, String deptId) {
List<EntityGroupEvtData> entityGroupEvtDataList = new ArrayList<>();
List<DictData> advanceType = dictDataMapper.getDicDataByTypeCode(DicDataTypeEnum.EVENT_TYPE.getCode());
Map<String, DictData> advanceMap = advanceType.stream().collect(Collectors.toMap(DictData::getId, Function.identity()));
//获取时间范围内的事件
List<AdvanceEventDetailVO> advanceEventDetailVOList = querySagEventsAll(startTime, endTime, deptId);
for (AdvanceEventDetailVO advanceEventDetailVO : advanceEventDetailVOList) { // 获取监测点线路序号
//母线id
String nodePhysics = advanceEventDetailVO.getBusBarId();
// 根据暂降类型获取高级算法对应的编号
int cata;
int startTimeTemp;
if (Objects.isNull(advanceEventDetailVO.getFirstType())) {
cata = advanceMap.get(advanceEventDetailVO.getAdvanceType()).getAlgoDescribe();
long timestampMillis = advanceEventDetailVO.getStartTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
startTimeTemp = (int) (timestampMillis / 1000);
} else {
cata = advanceMap.get(advanceEventDetailVO.getAdvanceType()).getAlgoDescribe(); // 获取类型
long timestampMillis = advanceEventDetailVO.getFirstTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
startTimeTemp = (int) (timestampMillis / 1000);
}
// 填充SagEvent对象数据
SagEvent sagEvent = new SagEvent();
sagEvent.setIndexEventDetail(advanceEventDetailVO.getEventId());
sagEvent.setSagTime(advanceEventDetailVO.getStartTime());
sagEvent.setFirstTime(advanceEventDetailVO.getFirstTime());// 必须增加,否则序列化出错
sagEvent.setTime(Timestamp.valueOf(advanceEventDetailVO.getStartTime()).getTime());
sagEvent.setFirstTimeMills((long) startTimeTemp);
sagEvent.setMsec(advanceEventDetailVO.getDuration());
PlantInfo plantInfo = new PlantInfo();
plantInfo.setNameBD(advanceEventDetailVO.getSubName());
plantInfo.setNameGD(advanceEventDetailVO.getGdName());
plantInfo.setNamePoint(advanceEventDetailVO.getLineId());
sagEvent.setPlantInfo(plantInfo);
sagEvent.setIndexPoint(advanceEventDetailVO.getLineId());
sagEvent.setCata(cata);
EntityGroupEvtData entityGroupEvtData = new EntityGroupEvtData(nodePhysics, startTimeTemp, cata, -1, sagEvent, advanceEventDetailVO.getAdvanceReason());
entityGroupEvtDataList.add(entityGroupEvtData);
}
return entityGroupEvtDataList;
}
public void create_evt_buf(EntityGroupEvtData[] arr, EntityGroupData obj, int len) {
System.arraycopy(arr, 0, obj.getIn_buf(), 0, arr.length);
obj.setEvt_in_num(len);
}
public void create_matrixcata(List<EntityLogic> list, EntityMtrans entityMtrans) {
EntityLogic[] node_data = new EntityLogic[list.size()];
for (int i = 0; i < list.size(); i++) {
node_data[i] = list.get(i);
}
int len = node_data.length;
UtilNormalization.matrixcata_pro(node_data, entityMtrans, len);
}
public void setMatrixcata(EntityGroupData obj, EntityMtrans entityMtrans) {
int i, j;
for (i = 0; i < (FinalData.MAX_CATA_NUM - 1); i++) {
for (j = 0; j < FinalData.NODE_NUM; j++) {
obj.getMatrixcata()[i][j] = entityMtrans.getMatrixcata1()[i][j];
}
}
}
public void show_group_info(EntityGroupData obj, List<SagEvent> list, List<RmpEventDetailAssPO> assEvent, LocalDateTime date) {
int i, j, k;
for (i = 0; i < obj.getGrp_all_num(); i++) {
String strUUID = IdUtil.simpleUUID();
List<SagEvent> listTem = new ArrayList<>();
for (j = 0; j < FinalData.MAX_CATA_NUM + 2; j++) {
if (obj.getGrp_cata_num()[i][j] != 0) {
for (k = 0; k < obj.getGrp_cata_num()[i][j]; k++) {
obj.getGrp_cata_buf()[i][j][k].getSagEvent().setIndexEventAss(strUUID);
obj.getGrp_cata_buf()[i][j][k].getSagEvent().setDealTime(date);
listTem.add(obj.getGrp_cata_buf()[i][j][k].getSagEvent());
list.add(obj.getGrp_cata_buf()[i][j][k].getSagEvent());
}
}
}
if (!listTem.isEmpty()) {
processing(listTem, assEvent, date);
}
}
}
public void processing(List<SagEvent> list, List<RmpEventDetailAssPO> lists, LocalDateTime date) {
// 根据暂降事件发生时间进行排序
Collections.sort(list);
RmpEventDetailAssPO eventAssObj = new RmpEventDetailAssPO();
String strUUID = list.get(0).getIndexEventAss();
// 归一化处理数据填充
eventAssObj.setEventAssId(strUUID);
// 事件发生时间
eventAssObj.setTimeId(list.get(0).getSagTime());
// 获取当前用户GUID
eventAssObj.setCreateBy(RequestUtil.getUserId());
// 是否进行范围分析 默认未分析
eventAssObj.setAnalyseFlag(1);
// 更新时间
eventAssObj.setUpdateTime(date);
// 暂降事件描述
String codeName = LocalDateTimeUtil.format(eventAssObj.getTimeId(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
String describe = "事件关联分析编号" + codeName + "共包含" + list.size() + "个事件";
eventAssObj.setContentDes(describe);
eventAssObj.setList(list);
lists.add(eventAssObj);
}
}
Map<String, Map<String, Integer>> getBeforeNodeInfo() {
Map<String, Map<String, Integer>> setNodeSort = new HashMap<>();
List<EntityLogic> list = rmpEventAdvanceMapper.getLogic();
if (CollectionUtil.isNotEmpty(list)) {
Map<String, List<String>> map = getLogicInfo(list);
setNodeSort = nodeSort(map);
}
return setNodeSort;
}
/*************************************************************************************
* 获取变压器信息并生成矩阵
*************************************************************************************/
public Map<String, EntityMtrans> getNodeInfo() {
Map<String, EntityMtrans> entityMtranMap = new HashMap<>(32);
List<EntityLogic> list = rmpEventAdvanceMapper.getLogic();
if (CollectionUtil.isNotEmpty(list)) {
Map<String, List<String>> map = getLogicInfo(list);
Map<String, Map<String, Integer>> setNodeSort = nodeSort(map);
setNodeSort.forEach((key, val) -> {
FinalData.NODE_NUM = val.size();
List<EntityLogic> listNew = new ArrayList<>();
for (EntityLogic entityLogic : list) {
if (entityLogic.getTPIndex().equals(key)) {
entityLogic.setNode_h(val.get(entityLogic.getNodeBefore()));
entityLogic.setNode_l(val.get(entityLogic.getNodeNext()));
listNew.add(entityLogic);
}
}
EntityMtrans entityMtrans = new EntityMtrans();
HandleEvent handleEvent = new HandleEvent();
handleEvent.create_matrixcata(listNew, entityMtrans);
entityMtranMap.put(key, entityMtrans);
});
}
return entityMtranMap;
}
/*******************************************
* 增加排序功能并缓存进redis
*******************************************/
public Map<String, Map<String, Integer>> nodeSort(Map<String, List<String>> mapList) {
Set<Map.Entry<String, List<String>>> sets = mapList.entrySet();
Map<String, Map<String, Integer>> map = new HashMap<>();
for (Map.Entry<String, List<String>> m : sets) {
int index = 1;
Map<String, Integer> map2 = new HashMap<>();
for (String item : m.getValue()) {
map2.put(item, index++);
}
map.put(m.getKey(), map2);
}
return map;
}
/**
* 抽取物理隔绝信息与母线的关系并放入map集合中
* 与getTflgPloyInfo()方法功能类似
*/
public Map<String, List<String>> getLogicInfo(List<EntityLogic> list) {
if (list.size() > 0) {
Iterator<String> iterator = getAreaInfo(list).iterator();
Map<String, List<String>> map = new HashMap<>();
while (iterator.hasNext()) {
List<String> listLogic = new ArrayList<>();
String areaString = iterator.next();
for (EntityLogic entityLogic : list) {
if (entityLogic.getTPIndex().equals(areaString)) {
listLogic.add(entityLogic.getNodeBefore());
listLogic.add(entityLogic.getNodeNext());
}
}
//去除list中重复数据
Set<String> set = new TreeSet<>(listLogic);
map.put(areaString, new ArrayList<>(set));
}
return map;
}
return null;
}
/**
* 获取物理隔绝编码信息
* 供getInfo()、getLogicInfo()方法使用
* 先从list数组中去重然后获取物理隔绝编码
*/
public <T> Set<String> getAreaInfo(List<T> list) {
Set<T> set = new HashSet<T>(list);
Iterator<T> iterator = set.iterator();
Set<String> setReturn = new HashSet<String>();
while (iterator.hasNext()) {
Object object = iterator.next();
if (object instanceof PqsTflgployass) {
setReturn.add(((PqsTflgployass) object).getTpIndex());
continue;
}
setReturn.add(((EntityLogic) object).getTPIndex());
}
return setReturn;
}
public List<AdvanceEventDetailVO> querySagEventsAll(LocalDateTime startTime, LocalDateTime endTime, String deptId) {
List<AdvanceEventDetailVO> result = new ArrayList<>();
List<String> lineIds = commGeneralService.getRunLineIdsByDept(deptId);
if (CollUtil.isNotEmpty(lineIds)) {
LambdaQueryWrapper<RmpEventDetailPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.and(i -> i.isNull(RmpEventDetailPO::getEventassIndex).or().eq(RmpEventDetailPO::getEventassIndex, ""))
.between(RmpEventDetailPO::getStartTime, startTime, endTime).in(RmpEventDetailPO::getMeasurementPointId,lineIds);
List<RmpEventDetailPO> rmpEventDetailPOList = eventAdvanceMapper.selectList(lambdaQueryWrapper);
if (CollectionUtil.isEmpty(rmpEventDetailPOList)) {
throw new BusinessException(AdvanceResponseEnum.EVENT_EMPTY);
}
List<String> tempLineIds = rmpEventDetailPOList.stream().map(RmpEventDetailPO::getLineId).distinct().collect(Collectors.toList());
List<LedgerBaseInfo> temLine = ledgerScaleMapper.getLedgerBaseInfo(tempLineIds);
Map<String, LedgerBaseInfo> map = temLine.stream().collect(Collectors.toMap(LedgerBaseInfo::getLineId, Function.identity()));
result = BeanUtil.copyToList(rmpEventDetailPOList, AdvanceEventDetailVO.class);
result.forEach(item -> {
if (map.containsKey(item.getLineId())) {
LedgerBaseInfo areaLineInfoVO = map.get(item.getLineId());
item.setGdName(areaLineInfoVO.getGdName());
item.setSubName(areaLineInfoVO.getStationName());
item.setNum(areaLineInfoVO.getNum());
item.setVoltageId(areaLineInfoVO.getVoltageLevel());
item.setBusBarId(areaLineInfoVO.getBusBarId());
}
});
}
result = result.stream().filter(it -> StrUtil.isNotBlank(it.getBusBarId())).collect(Collectors.toList());
return result;
}
}

View File

@@ -0,0 +1,344 @@
package com.njcn.product.advance.eventSource.service.impl;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.PubUtils;
import com.njcn.influx.imapper.DataHarmPowerPMapper;
import com.njcn.influx.imapper.DataHarmRateVMapper;
import com.njcn.influx.imapper.DataIMapper;
import com.njcn.influx.pojo.constant.InfluxDBTableConstant;
import com.njcn.influx.pojo.dto.HarmData;
import com.njcn.influx.pojo.dto.HarmHistoryDataDTO;
import com.njcn.influx.pojo.po.DataHarmPowerP;
import com.njcn.influx.pojo.po.DataHarmRateV;
import com.njcn.influx.pojo.po.DataI;
import com.njcn.influx.query.InfluxQueryWrapper;
import com.njcn.product.advance.eventSource.pojo.enums.AdvanceResponseEnum;
import com.njcn.product.advance.responsility.imapper.DataHarmP;
import com.njcn.product.advance.responsility.pojo.bo.UserDataExcel;
import com.njcn.product.advance.responsility.pojo.param.HistoryHarmParam;
import com.njcn.product.advance.eventSource.service.HistoryHarmonicService;
import com.njcn.product.advance.responsility.pojo.param.PHistoryHarmParam;
import com.njcn.product.terminal.mysqlTerminal.mapper.LineMapper;
import com.njcn.product.terminal.mysqlTerminal.mapper.OverlimitMapper;
import com.njcn.product.terminal.mysqlTerminal.pojo.dto.LineDevGetDTO;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.Overlimit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Author: cdf
* @CreateTime: 2025-09-08
* @Description:
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class HistoryHarmonicServiceImpl implements HistoryHarmonicService {
private final OverlimitMapper overlimitMapper;
private final LineMapper lineMapper;
private final DataIMapper dataIMapper;
private final DataHarmRateVMapper dataHarmRateVMapper;
private final DataHarmPowerPMapper dataHarmPowerPMapper;
@Override
public HarmHistoryDataDTO getHistoryHarmData(HistoryHarmParam historyHarmParam) {
List<HarmData> historyData;
float overLimit;
Overlimit overlimit = overlimitMapper.selectById(historyHarmParam.getLineId());
//判断是电流还是电压谐波
if (historyHarmParam.getType() == 0) {
historyData = getIHistoryData(historyHarmParam);
overLimit = PubUtils.getValueByMethod(overlimit, "getIharm", historyHarmParam.getTime());
} else {
historyData = getVHistoryData(historyHarmParam);
overLimit = PubUtils.getValueByMethod(overlimit, "getUharm", historyHarmParam.getTime());
}
return new HarmHistoryDataDTO(historyData, overLimit);
}
@Override
public List<DataHarmPowerP> getHarmonicPData(PHistoryHarmParam historyHarmParam) {
InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(DataHarmPowerP.class);
influxQueryWrapper
.select(DataHarmPowerP::getP,DataHarmPowerP::getTime,DataHarmPowerP::getLineId)
.eq(DataHarmPowerP::getPhaseType,InfluxDBTableConstant.PHASE_TYPE_T)
.eq(DataHarmPowerP::getValueType,InfluxDBTableConstant.CP95)
.regular(DataHarmPowerP::getLineId,historyHarmParam.getLineIds())
.between(DataHarmPowerP::getTime,historyHarmParam.getSearchBeginTime().concat(InfluxDBTableConstant.START_TIME), historyHarmParam.getSearchEndTime().concat(InfluxDBTableConstant.END_TIME));
List<DataHarmPowerP> dataHarmPowerPList = dataHarmPowerPMapper.selectByQueryWrapper(influxQueryWrapper);
return dataHarmPowerPList;
}
/***
* 获取指定次数 监测点的历史谐波电流数据
* @author hongawen
* @date 2023/7/19 10:03
*/
private List<HarmData> getIHistoryData(HistoryHarmParam historyHarmParam) {
LineDevGetDTO lineDetailData = lineMapper.getMonitorDetail(historyHarmParam.getLineId());
List<HarmData> historyData;
InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(DataI.class, HarmData.class);
influxQueryWrapper
.select(DataI::getTime)
.max("i_" + historyHarmParam.getTime(), "value")
.between(DataI::getTime, historyHarmParam.getSearchBeginTime().concat(" 00:00:00"), historyHarmParam.getSearchEndTime().concat(" 23:59:59"))
.eq(DataI::getLineId, historyHarmParam.getLineId())
.or(DataI::getPhaseType, Stream.of(InfluxDBTableConstant.PHASE_TYPE_A, InfluxDBTableConstant.PHASE_TYPE_B, InfluxDBTableConstant.PHASE_TYPE_C).collect(Collectors.toList()))
//以时间分组时,需要加上时间间隔,比如此处需要加上监测点的采样间隔
.groupBy("time(" + lineDetailData.getInterval() + "m)")
.timeAsc();
String string = influxQueryWrapper.generateSql();
historyData = dataIMapper.getIHistoryData(string);
if (CollectionUtils.isEmpty(historyData)) {
//如果数据为空,则提示给用户暂无数据
throw new BusinessException(CommonResponseEnum.NO_DATA);
}
//最新两条数据的间隔与监测点查出的间隔做对比,返回一个合理的间隔
historyData = historyData.stream().filter(Objects::nonNull).collect(Collectors.toList());
int lineInterval = getInterval(lineDetailData.getInterval(), PubUtils.instantToDate(historyData.get(historyData.size() - 1).getTime()), PubUtils.instantToDate(historyData.get(historyData.size() - 2).getTime()));
historyData = dealHistoryData(historyData, lineInterval);
if (CollectionUtils.isEmpty(historyData)) {
//如果数据为空,则提示给用户暂无数据
throw new BusinessException(CommonResponseEnum.NO_DATA);
}
//根据时间天数,获取理论上多少次用采数据
List<String> dateStr = PubUtils.getTimes(DateUtil.beginOfDay(DateUtil.parse(historyHarmParam.getSearchBeginTime())), DateUtil.endOfDay(DateUtil.parse(historyHarmParam.getSearchEndTime())));
int dueTimes = dateStr.size() * 1440 / lineInterval;
int realTimes = historyData.size();
if (dueTimes != realTimes) {
//期待值与实际值不等,则提示用户时间范围内谐波数据完整性不足
throw new BusinessException(AdvanceResponseEnum.INSUFFICIENCY_OF_INTEGRITY);
}
return historyData.stream().sorted(Comparator.comparing(HarmData::getTime)).collect(Collectors.toList());
}
/**
* 获取谐波电压的数据
* <p>
* 因历史谐波表data_harmrate_v
*/
private List<HarmData> getVHistoryData(HistoryHarmParam historyHarmParam) {
LineDevGetDTO lineDetailData = lineMapper.getMonitorDetail(historyHarmParam.getLineId());
List<HarmData> historyData;
InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(DataHarmRateV.class, HarmData.class);
influxQueryWrapper
.select(DataHarmRateV::getTime)
.max("v_" + historyHarmParam.getTime(), "value")
.between(DataHarmRateV::getTime, historyHarmParam.getSearchBeginTime().concat(" 00:00:00"), historyHarmParam.getSearchEndTime().concat(" 23:59:59"))
.eq(DataHarmRateV::getLineId, historyHarmParam.getLineId())
.or(DataHarmRateV::getPhaseType, Stream.of(InfluxDBTableConstant.PHASE_TYPE_A, InfluxDBTableConstant.PHASE_TYPE_B, InfluxDBTableConstant.PHASE_TYPE_C).collect(Collectors.toList()))
.groupBy("time(" + lineDetailData.getInterval() + "m)")
.timeAsc();
historyData = dataHarmRateVMapper.getHarmRateVHistoryData(influxQueryWrapper);
if (CollectionUtils.isEmpty(historyData)) {
//如果数据为空,则提示给用户暂无数据
throw new BusinessException(CommonResponseEnum.NO_DATA);
}
historyData = historyData.stream().filter(Objects::nonNull).collect(Collectors.toList());
int lineInterval = getInterval(lineDetailData.getInterval(), PubUtils.instantToDate(historyData.get(historyData.size() - 1).getTime()), PubUtils.instantToDate(historyData.get(historyData.size() - 2).getTime()));
//最新两条数据的间隔与监测点查出的间隔做对比,返回一个合理的间隔
historyData = dealHistoryData(historyData, lineInterval);
if (CollectionUtils.isEmpty(historyData)) {
//如果数据为空,则提示给用户暂无数据
throw new BusinessException(CommonResponseEnum.NO_DATA);
}
//根据时间天数,获取理论上多少次用采数据
List<String> dateStr = PubUtils.getTimes(DateUtil.beginOfDay(DateUtil.parse(historyHarmParam.getSearchBeginTime())), DateUtil.endOfDay(DateUtil.parse(historyHarmParam.getSearchEndTime())));
int dueTimes = dateStr.size() * 1440 / lineInterval;
int realTimes = historyData.size();
if (dueTimes != realTimes) {
//期待值与实际值不等,则提示用户时间范围内谐波数据完整性不足
throw new BusinessException(AdvanceResponseEnum.INSUFFICIENCY_OF_INTEGRITY);
}
return historyData.stream().sorted(Comparator.comparing(HarmData::getTime)).collect(Collectors.toList());
}
/**
* 获取合理的测量间隔
*/
private int getInterval(int lineInterval, Date lastOne, Date lastTwo) {
int interval = 0;
Calendar one = Calendar.getInstance();
one.setTime(lastOne);
Calendar two = Calendar.getInstance();
two.setTime(lastTwo);
long oneTime = lastOne.getTime();
long twoTime = lastTwo.getTime();
long intvalTime = oneTime - twoTime;
long databaseInterval = lineInterval * 60 * 1000;
if (oneTime < twoTime || intvalTime >= databaseInterval) {
interval = lineInterval;
}
if (intvalTime < databaseInterval) {
interval = (int) (intvalTime / (1000 * 60));
}
return interval;
}
/**
* 根据库中查询的数据,进行数据补齐操作
*
* @param beforeDeal 库中实际的历史谐波数据
*/
private List<HarmData> dealHistoryData(List<HarmData> beforeDeal, int lineInterval) {
List<HarmData> result = new ArrayList<>();
try {
if (CollectionUtils.isEmpty(beforeDeal)) {
return result;
} else {
//先将查询数据按日进行收集
Map<String/*yyyy-MM-dd的时间格式*/, Map<Date, HarmData>/*当前天的所有谐波数据*/> dayHistoryDatas = new HashMap<>();
for (HarmData harmData : beforeDeal) {
Date time = PubUtils.instantToDate(harmData.getTime());
String date = DateUtil.format(time, DatePattern.NORM_DATE_PATTERN);
if (dayHistoryDatas.containsKey(date)) {
Map<Date, HarmData> harmDataMap = dayHistoryDatas.get(date);
harmDataMap.put(PubUtils.getSecondsAsZero(PubUtils.instantToDate(harmData.getTime())), harmData);
dayHistoryDatas.put(date, harmDataMap);
} else {
Map<Date, HarmData> harmDataMap = new HashMap<>();
harmDataMap.put(PubUtils.getSecondsAsZero(PubUtils.instantToDate(harmData.getTime())), harmData);
dayHistoryDatas.put(date, harmDataMap);
}
}
//将数据按日期处理后,开始进行完整性判断,满足完整性则进行补齐,否则返回空数据
Set<String> days = dayHistoryDatas.keySet();
for (String day : days) {
//获取出当天的历史谐波数据
Map<Date, HarmData> harmDataMap = dayHistoryDatas.get(day);
if (CollectionUtils.isEmpty(harmDataMap)) {
continue;
}
int dueTimes = 1440 / lineInterval;
int realTimes = harmDataMap.size();
double integrity = (double) realTimes / (double) dueTimes;
if (integrity < 0.9 || integrity >= 1.0) {
//完整性不足,则返回原数据
Set<Date> dates = harmDataMap.keySet();
for (Date time : dates) {
result.add(harmDataMap.get(time));
}
} else if (integrity < 1.0) {
//进行数据补齐,数据补齐需要根据监测点测量间隔,最好是MAP格式 map的key是yyyy-MM-dd HH:mm
List<HarmData> afterDeal = new ArrayList<>();
String timeTemp = day + " 00:00:00";
Date date = DateUtil.parse(timeTemp, DatePattern.NORM_DATETIME_PATTERN);
for (int i = 0; i < dueTimes; i++) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, lineInterval * i);
HarmData temp = harmDataMap.get(calendar.getTime());
if (temp != null && temp.getValue() != null) {
afterDeal.add(temp);
} else {
//递归找到前面的值
Float preValue = getPreHarmValue(date, calendar.getTime(), harmDataMap, lineInterval);
//递归找到后面的值
Float appendValue = getAppendHarmValue(date, calendar.getTime(), harmDataMap, lineInterval);
HarmData harmData = new HarmData();
harmData.setTime(PubUtils.dateToInstant(calendar.getTime()));
//还需要判断前值和后值为空的情况
if (null == preValue && null == appendValue) {
harmData.setValue(0.0f);
} else if (null == preValue) {
harmData.setValue(appendValue);
} else if (null == appendValue) {
harmData.setValue(preValue);
} else {
harmData.setValue((preValue + appendValue) / 2);
}
afterDeal.add(harmData);
}
}
result.addAll(afterDeal);
}
}
}
} catch (Exception e) {
log.error("开始处理历史电压谐波数据失败,失败原因:{}", e.toString());
throw new BusinessException(AdvanceResponseEnum.INSUFFICIENCY_OF_INTEGRITY);
}
return result;
}
/**
* 递归找前值 谐波数据
*
* @param date 起始时间
* @param time 当前事件
* @param beforeDeal 处理前的数据
*/
private Float getPreHarmValue(Date date, Date time, Map<Date, HarmData> beforeDeal, int interval) {
Float result;
if (date.getTime() >= time.getTime()) {
return null;
} else {
Calendar calendar = Calendar.getInstance();
calendar.setTime(time);
interval = -interval;
calendar.add(Calendar.MINUTE, interval);
HarmData temp = beforeDeal.get(calendar.getTime());
if (temp == null || temp.getValue() == null) {
result = getPreHarmValue(date, calendar.getTime(), beforeDeal, Math.abs(interval));
} else {
result = temp.getValue();
}
}
return result;
}
/**
* 递归找后置 谐波数据
*
* @param date 起始时间
* @param time 截止时间
*/
private Float getAppendHarmValue(Date date, Date time, Map<Date, HarmData> beforeDeal, int interval) {
Float result;
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, 1);
calendar.add(Calendar.MINUTE, -interval);
if (calendar.getTimeInMillis() <= time.getTime()) {
return null;
} else {
Calendar calendar1 = Calendar.getInstance();
calendar1.setTime(time);
calendar1.add(Calendar.MINUTE, interval);
HarmData temp = beforeDeal.get(calendar1.getTime());
if (temp == null || temp.getValue() == null) {
result = getAppendHarmValue(date, calendar1.getTime(), beforeDeal, interval);
} else {
result = temp.getValue();
}
}
return result;
}
}

View File

@@ -0,0 +1,18 @@
package com.njcn.product.advance.eventSource.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.product.advance.eventSource.mapper.RmpEventDetailAssMapper;
import com.njcn.product.advance.eventSource.pojo.po.RmpEventDetailAssPO;
import com.njcn.product.advance.eventSource.service.RmpEventDetailAssService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @Author: cdf
* @CreateTime: 2025-09-04
* @Description:
*/
@Service
@RequiredArgsConstructor
public class RmpEventDetailAssServiceImpl extends ServiceImpl<RmpEventDetailAssMapper,RmpEventDetailAssPO> implements RmpEventDetailAssService {
}

View File

@@ -0,0 +1,347 @@
package com.njcn.product.advance.eventSource.utils;
import com.njcn.product.advance.eventSource.pojo.dto.eventAggregate.*;
public class UtilNormalization {
public static void matrixcata_pro(EntityLogic[] transformer, EntityMtrans entityMtrans, int len) {
int i, j, k;
int node1, node2, con;
int src_node[] = new int[] { 0 };
// 连接方式转化为矩阵形式,行、列表示所有节点
// inf表示两个节点不相连0表示与自身相连其他数值表示变压器连接类型
// 将初始矩阵的元素设为inf,对角线元素设为0
for (i = 0; i < FinalData.NODE_NUM; i++) {
for (j = 0; j < FinalData.NODE_NUM; j++) {
entityMtrans.getMtrans()[i][j] = FinalData.DATA_INF;
}
entityMtrans.getMtrans()[i][i] = 0;
}
// 根据transformer设置元素
for (i = 0; i < len; i++) {
node1 = transformer[i].getNode_h();
node2 = transformer[i].getNode_l();
con = transformer[i].getType();
entityMtrans.getMtrans()[node1 - 1][node2 - 1] = con;
entityMtrans.getMtrans()[node2 - 1][node1 - 1] = con;
}
StringBuilder str = new StringBuilder();
for (i = 0; i < FinalData.NODE_NUM; i++) {
for (j = 0; j < FinalData.NODE_NUM; j++) {
str.append(entityMtrans.getMtrans()[i][j]).append(" ");
if (j == (FinalData.NODE_NUM - 1))
str.append("\r\n");
}
}
// 类型匹配矩阵Matrixcata
// Matrixcata模式匹配矩阵列为节点数行为总类别数元素为第一个节点分别是1-6类别情况下其他节点类别情况。
// 元素1,2,34,5,6 分别对应 Dc,Cb,Da,Cc,Db,Ca
// 设置矩阵第一行元素
for (i = 0; i < FinalData.NODE_NUM; i++)
entityMtrans.getMatrixcata0()[0][i] = 0;
for (i = 1; i < FinalData.NODE_NUM; i++) {
// 路径缓存清空
for (j = 0; j < FinalData.MAX_PATH_NUM; j++) {
for (k = 0; k < (FinalData.NODE_NUM + 1); k++)
entityMtrans.getPossiable_path()[j][k] = 0;
}
entityMtrans.setPath_num(0);
// 寻找路径
src_node[0] = 0;
findPath(entityMtrans, src_node, i, 0, 1, FinalData.NODE_NUM);
if (entityMtrans.getPath_num() != 0)
entityMtrans.getMatrixcata0()[0][i] = entityMtrans.getPossiable_path()[0][FinalData.NODE_NUM]; // 采用第一条路径
else
entityMtrans.getMatrixcata0()[0][i] = FinalData.DATA_INF; // 找不到路径填大值表示不通
}
// 构造矩阵其他行元素
for (i = 1; i < FinalData.EVT_TYPE_NUM; i++) {
for (j = 0; j < FinalData.NODE_NUM; j++)
// EntityGroupData.Matrixcata0[i][j] =
// EntityGroupData.Matrixcata0[0][j] + i;
if (entityMtrans.getMatrixcata0()[0][j] == FinalData.DATA_INF) {
entityMtrans.getMatrixcata0()[i][j] = FinalData.DATA_INF;
} else {
entityMtrans.getMatrixcata0()[i][j] = entityMtrans.getMatrixcata0()[0][j] + i;
}
}
// 将数据归类到0-5
for (i = 0; i < FinalData.EVT_TYPE_NUM; i++) {
for (j = 0; j < FinalData.NODE_NUM; j++)
entityMtrans.getMatrixcata1()[i][j] = entityMtrans.getMatrixcata0()[i][j] % 6;
}
// 0换成6将数据归类到1-6
for (i = 0; i < FinalData.EVT_TYPE_NUM; i++) {
for (j = 0; j < FinalData.NODE_NUM; j++) {
if (entityMtrans.getMatrixcata1()[i][j] == 0)
entityMtrans.getMatrixcata1()[i][j] = 6;
}
}
str.delete(0, str.length());
for (i = 0; i < FinalData.EVT_TYPE_NUM; i++) {
for (j = 0; j < FinalData.NODE_NUM; j++) {
str.append(entityMtrans.getMatrixcata1()[i][j]).append(" ");
if (j == (FinalData.NODE_NUM - 1))
str.append("\r\n");
}
}
}
public static int findPath(EntityMtrans entityMtrans, int[] OriginalNode, int destination, int Weight, int src_num, int node_num) // 深度优先搜索
{
int i, j;
int last_node;
int nextNodes[] = new int[FinalData.NODE_NUM];
int nextNode_num = 0;
int nextNodes0[] = new int[FinalData.NODE_NUM];
int nextNode_num0 = 0;
int tmpPath[] = new int[FinalData.NODE_NUM + 1];
int tmpPath_num;
if (src_num < 1) // 源节点个数不对
return 1;
last_node = OriginalNode[src_num - 1];
if (last_node > node_num) // 判断最后一个节点号是否在范围内
return 1;
for (i = 0; i < node_num; i++) {
// if((Mtrans[last_node][i]>0)&&(Mtrans[last_node][i]<DATA_INF))
// //寻找相同的节点
if (entityMtrans.getMtrans()[last_node][i] > 0) // 寻找相同的节点
{
nextNodes[nextNode_num] = i;
nextNode_num++;
}
}
// 如果一条路的最后一个节点就是目标节点说明此路径是所有路径中的一条可以直接return
if (last_node == destination) {
if (entityMtrans.getPath_num() >= FinalData.MAX_PATH_NUM)
return 1;
for (i = 0; i < src_num; i++)
entityMtrans.getPossiable_path()[entityMtrans.getPath_num()][i] = OriginalNode[i];
entityMtrans.getPossiable_path()[entityMtrans.getPath_num()][FinalData.NODE_NUM] = Weight; // 最后一个节点填入变压器连接
entityMtrans.setPath_num(entityMtrans.getPath_num() + 1);
} else {
for (i = 0; i < src_num; i++) {
if (destination == OriginalNode[i])
return 1;
}
}
// 判断下一个节点有没有目的节点
for (i = 0; i < nextNode_num; i++) {
if (nextNodes[i] == destination) {
// 先清零;
for (j = 0; j < (FinalData.NODE_NUM + 1); j++)
tmpPath[j] = 0;
// 填入源节点
for (j = 0; j < src_num; j++)
tmpPath[j] = OriginalNode[j];
tmpPath[src_num] = destination; // 目的节点加在后面
tmpPath[FinalData.NODE_NUM] = Weight + entityMtrans.getMtrans()[last_node][destination]; // 最后一个点填入变压器累计
tmpPath_num = src_num + 1;
if (entityMtrans.getPath_num() >= FinalData.MAX_PATH_NUM)
return 1;
for (j = 0; j < (FinalData.NODE_NUM + 1); j++)
entityMtrans.getPossiable_path()[entityMtrans.getPath_num()][j] = tmpPath[j]; // tmpPath为路径的路阻
entityMtrans.setPath_num(entityMtrans.getPath_num() + 1);
nextNodes[i] = 0;
if (nextNode_num != 0) // if(nextNode_num)
nextNode_num--;
} else {
// 判断如果源节点中有下一个节点,不再寻找处理
for (j = 0; j < src_num; j++) {
if (nextNodes[i] == OriginalNode[j]) {
nextNodes[i] = 0;
}
}
}
}
// 不是目的节点的下一节点继续寻找
for (i = 0; i < nextNode_num; i++) {
if (nextNodes[i] != 0) {
nextNodes0[nextNode_num0] = nextNodes[i];
nextNode_num0++;
}
}
for (i = 0; i < nextNode_num0; i++) {
// 填入源节点
for (j = 0; j < src_num; j++)
tmpPath[j] = OriginalNode[j];
tmpPath[src_num] = nextNodes0[i]; // 下一个节点加在后面
tmpPath_num = src_num + 1;
findPath(entityMtrans, tmpPath, destination, (Weight + entityMtrans.getMtrans()[last_node][nextNodes0[i]]), tmpPath_num,
node_num);
}
return 0;
}
public static int sort_Tstart(EntityGroupData buf) {
int res_num, out_num;
int idx = 0;
if ((buf == null) || (buf.getEvt_in_num() == 0))
return 0;
res_num = buf.getEvt_in_num();
while (res_num > 0) { // while(res_num)
out_num = sort_Tstart_single(buf);
// 输出缓冲填入归集缓冲
// buf.getGrp_buf()[idx] = buf.getOut_buf();
System.arraycopy(buf.getOut_buf(), 0, buf.getGrp_buf()[idx], 0, buf.getOut_buf().length);
buf.getGrp_num()[idx] = out_num;
// 未归集填入输入缓冲
// buf.setIn_buf(buf.getRes_buf());
System.arraycopy(buf.getRes_buf(), 0, buf.getIn_buf(), 0, buf.getRes_buf().length);
buf.setEvt_in_num(buf.getEvt_res_num());
idx++;
if (idx >= FinalData.MAX_GROUP_NUM) // 分组超限
break;
if (out_num <= res_num)
res_num = res_num - out_num;
else
break; // 分组数目超限
}
buf.setGrp_all_num(idx);
return 1;
}
public static int sort_Tstart_single(EntityGroupData buf) {
int i;
int start_time;
int thd_time1, thd_time2;
if ((buf == null) || (buf.getEvt_in_num() == 0))
return 0;
buf.setEvt_out_num(0);
buf.setEvt_res_num(0);
// 如果只有一个事件直接赋值返回
if (buf.getEvt_in_num() == 1) {
buf.setEvt_out_num(1);
// buf.getOut_buf()[0] = buf.getIn_buf()[0];
// System.arraycopy(buf.getIn_buf()[0], 0, buf.getOut_buf()[0], 0,
// 1);
buf.getOut_buf()[0] = (EntityGroupEvtData) buf.getIn_buf()[0].objClone();
return buf.getEvt_out_num();
}
start_time = buf.getIn_buf()[0].getStart_time();
thd_time1 = start_time - FinalData.TIME_THRESHOLD;
thd_time2 = start_time + FinalData.TIME_THRESHOLD;
// 判断时标阀值门槛归集
for (i = 0; i < buf.getEvt_in_num(); i++) {
start_time = buf.getIn_buf()[i].getStart_time();
// 在阈值范围内
if ((start_time >= thd_time1) && (start_time <= thd_time2)) {
// buf.getOut_buf()[buf.getEvt_out_num()] = buf.getIn_buf()[i];
// System.arraycopy(buf.getIn_buf()[i], 0,
// buf.getOut_buf()[buf.getEvt_out_num()], 0, 1);
buf.getOut_buf()[buf.getEvt_out_num()] = (EntityGroupEvtData) buf.getIn_buf()[i].objClone();
buf.setEvt_out_num(buf.getEvt_out_num() + 1);
} else {
// buf.getRes_buf()[buf.getEvt_res_num()] = buf.getIn_buf()[i];
// System.arraycopy(buf.getIn_buf()[i], 0,
// buf.getRes_buf()[buf.getEvt_res_num()], 0, 1);
buf.getRes_buf()[buf.getEvt_res_num()] = (EntityGroupEvtData) buf.getIn_buf()[i].objClone();
buf.setEvt_res_num(buf.getEvt_res_num() + 1);
}
}
return buf.getEvt_out_num();
}
public static int sort_cata(EntityGroupData buf, int idx) {
int i, j;
int cata, node;
int odrer[] = new int[FinalData.MAX_CATA_NUM + 2];
// 针对类别是1-6的数据进行模式匹配并标注属于哪一个模式
for (i = 0; i < (FinalData.MAX_CATA_NUM + 2); i++)
odrer[i] = 0;
// 暂降类型转换
// 将类型7,8,9转换为6,2,4
// 其中789分别对应BC两相接地,AC两相接地,AB两相接地,1,2,34,56分别对应Dc,Cb,Da,Cc,Db,Ca
/*
* for (i = 0; i < buf.getGrp_num()[idx]; i++) { if
* (buf.getGrp_buf()[idx][i].getCata() == 7)
* buf.getGrp_buf()[idx][i].setCata(6); if
* (buf.getGrp_buf()[idx][i].getCata() == 8)
* buf.getGrp_buf()[idx][i].setCata(2); if
* (buf.getGrp_buf()[idx][i].getCata() == 9)
* buf.getGrp_buf()[idx][i].setCata(4); }
*/
for (i = 0; i < buf.getGrp_num()[idx]; i++) {
/*
* if (buf.getGrp_buf()[idx][i].getCata() == 10) //事件类型未知
* buf.getGrp_buf()[idx][i].setCata(11); if
* (buf.getGrp_buf()[idx][i].getCata() == 9) //三相
* buf.getGrp_buf()[idx][i].setCata(10);
*/
if (buf.getGrp_buf()[idx][i].getCata() == 0)
buf.getGrp_buf()[idx][i].setCata(6);
if (buf.getGrp_buf()[idx][i].getCata() == 6)
buf.getGrp_buf()[idx][i].setCata(6);
if (buf.getGrp_buf()[idx][i].getCata() == 7)
buf.getGrp_buf()[idx][i].setCata(2);
if (buf.getGrp_buf()[idx][i].getCata() == 8)
buf.getGrp_buf()[idx][i].setCata(4);
}
// 将数据进行模式匹配,并标注属于哪一个模式
for (i = 0; i < buf.getGrp_num()[idx]; i++) {
cata = buf.getGrp_buf()[idx][i].getCata();
node = buf.getGrp_buf()[idx][i].getNode();
if ((node > FinalData.NODE_NUM) || (buf.getMatrixcata()[0][node - 1] == FinalData.DATA_INF)) {
buf.getGrp_buf()[idx][i].setCata2(FinalData.QVVR_TYPE_OUTOFRANGE);
// buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM +
// 1][odrer[FinalData.MAX_CATA_NUM + 1]] =
// buf.getGrp_buf()[idx][i];
// System.arraycopy(buf.getGrp_buf()[idx][i], 0,
// buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM +
// 1][odrer[FinalData.MAX_CATA_NUM + 1]], 0, 1);
buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM + 1][odrer[FinalData.MAX_CATA_NUM
+ 1]] = (EntityGroupEvtData) buf.getGrp_buf()[idx][i].objClone();
odrer[FinalData.MAX_CATA_NUM + 1]++;
} else if (cata == FinalData.QVVR_TYPE_UNKNOWN) {
buf.getGrp_buf()[idx][i].setCata2(FinalData.QVVR_TYPE_UNKNOWN);
// buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM][odrer[FinalData.MAX_CATA_NUM]]
// = buf.getGrp_buf()[idx][i];
// System.arraycopy(buf.getGrp_buf()[idx][i], 0,
// buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM][odrer[FinalData.MAX_CATA_NUM]],
// 0, 1);
buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM][odrer[FinalData.MAX_CATA_NUM]] = (EntityGroupEvtData) buf
.getGrp_buf()[idx][i].objClone();
odrer[FinalData.MAX_CATA_NUM]++;
} else if (cata == FinalData.QVVR_TYPE_THREE) // ÈıÏàÔݽµ¹éÀà
{
buf.getGrp_buf()[idx][i].setCata2(FinalData.QVVR_TYPE_THREE);
// buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM -
// 1][odrer[FinalData.MAX_CATA_NUM - 1]] =
// buf.getGrp_buf()[idx][i];
// System.arraycopy(buf.getGrp_buf()[idx][i], 0,
// buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM -
// 1][odrer[FinalData.MAX_CATA_NUM - 1]], 0, 1);
buf.getGrp_cata_buf()[idx][FinalData.MAX_CATA_NUM - 1][odrer[FinalData.MAX_CATA_NUM
- 1]] = (EntityGroupEvtData) buf.getGrp_buf()[idx][i].objClone();
odrer[FinalData.MAX_CATA_NUM - 1]++;
} else // 1-6类暂降归类
{
for (j = 0; j < FinalData.MAX_CATA_NUM; j++) {
if (cata == buf.getMatrixcata()[j][node - 1])// 判断数据类别属于第几行
{
buf.getGrp_buf()[idx][i].setCata2(j + 1);
// 进行归类
// buf.getGrp_cata_buf()[idx][j][odrer[j]] =
// buf.getGrp_buf()[idx][i];
// System.arraycopy(buf.getGrp_buf()[idx][i], 0,
// buf.getGrp_cata_buf()[idx][j][odrer[j]], 0, 1);
buf.getGrp_cata_buf()[idx][j][odrer[j]] = (EntityGroupEvtData) buf.getGrp_buf()[idx][i]
.objClone();
odrer[j]++;
}
}
}
}
for (i = 0; i < FinalData.MAX_CATA_NUM + 2; i++)
buf.getGrp_cata_num()[idx][i] = odrer[i];
return 0;
}
}

View File

@@ -0,0 +1,91 @@
package com.njcn.product.advance.harmonicUp.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.product.advance.harmonicUp.pojo.po.UpHarmonicDetail;
import com.njcn.product.advance.harmonicUp.pojo.vo.UpTableInfo;
import com.njcn.product.advance.harmonicUp.service.HarmonicUpService;
import com.njcn.product.terminal.mysqlTerminal.pojo.param.LargeScreenCountParam;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @Author: cdf
* @CreateTime: 2025-09-12
* @Description:
*/
@Slf4j
@RestController
@RequestMapping("harmonicUp")
@Api(tags = "谐波放大")
@RequiredArgsConstructor
public class HarmonicUpController extends BaseController {
private final HarmonicUpService harmonicUpService;
@GetMapping("analyzePreData")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("谐波放大算法预处理")
public HttpResult<Object> analyzePreData(@RequestParam("date")String date) {
String methodDescribe = getMethodDescribe("analyzePreData");
harmonicUpService.analyzePreData(date);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
@PostMapping("getDetail")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("获取监测点谐波放大详情")
public HttpResult<Page<UpHarmonicDetail>> getDetail(@RequestBody LargeScreenCountParam param) {
String methodDescribe = getMethodDescribe("getDetail");
checkParam(param);
Page<UpHarmonicDetail> result = harmonicUpService.getDetail(param);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
}
@PostMapping("getInfoList")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("谐波放大实时数据列表")
public HttpResult<Page<UpHarmonicDetail>> getInfoList(@RequestBody LargeScreenCountParam param) {
String methodDescribe = getMethodDescribe("getInfoList");
Page<UpHarmonicDetail> result = harmonicUpService.getInfoList(param);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
}
@PostMapping("tableInfo")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("谐波放大热力图")
public HttpResult<UpTableInfo> tableInfo(@RequestBody LargeScreenCountParam param) {
String methodDescribe = getMethodDescribe("tableInfo");
UpTableInfo result = harmonicUpService.tableInfo(param);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
}
private void checkParam(LargeScreenCountParam param){
if(StrUtil.isBlank(param.getSearchBeginTime())){
throw new BusinessException(CommonResponseEnum.FAIL,"时间不可为空!");
}
if(StrUtil.isBlank(param.getLineId())){
throw new BusinessException(CommonResponseEnum.FAIL,"监测点id不可为空!");
}
/* if(StrUtil.isBlank(param.getDeptId())){
throw new BusinessException(CommonResponseEnum.FAIL,"部门id不可为空!");
}*/
}
}

View File

@@ -0,0 +1,16 @@
package com.njcn.product.advance.harmonicUp.imapper;
import com.njcn.influx.base.InfluxDbBaseMapper;
import com.njcn.product.advance.harmonicUp.pojo.po.DataIUp;
/**
* @author hongawen
* @version 1.0
* @data 2024/11/7 18:49
*/
public interface DataIUpToMapper extends InfluxDbBaseMapper<DataIUp> {
}

View File

@@ -0,0 +1,18 @@
package com.njcn.product.advance.harmonicUp.imapper;
import com.njcn.influx.base.InfluxDbBaseMapper;
import com.njcn.product.advance.harmonicUp.pojo.po.DataVUp;
/**
* @author hongawen
* @version 1.0
* @data 2024/11/7 18:49
*/
public interface DataVUpToMapper extends InfluxDbBaseMapper<DataVUp> {
}

View File

@@ -0,0 +1,22 @@
package com.njcn.product.advance.harmonicUp.mapper;
/**
* @Author: cdf
* @CreateTime: 2025-09-12
* @Description:
*/
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.njcn.product.advance.harmonicUp.pojo.po.UpHarmonicDetail;
/**
* <p>
* 谐波放大详情表 Mapper 接口
* </p>
*
* @author
* @since 2025-09-12
*/
public interface UpHarmonicDetailMapper extends BaseMapper<UpHarmonicDetail> {
}

View File

@@ -0,0 +1,45 @@
package com.njcn.product.advance.harmonicUp.pojo.param;
import com.njcn.common.pojo.constant.PatternRegex;
import com.njcn.product.advance.eventSource.pojo.constant.HarmonicValidMessage;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* @author denghuajun
* @date 2022/3/11
*
*/
@Data
public class HistoryParam {
@ApiModelProperty("开始时间")
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
private String searchBeginTime;
@ApiModelProperty("结束时间")
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
private String searchEndTime;
@ApiModelProperty("监测点id集合")
private String[] lineId;
@ApiModelProperty("指标集合")
private String[] condition;
@ApiModelProperty("谐波次数")
private Integer harmonic;
@ApiModelProperty("间谐波次数")
private Integer inHarmonic;
@ApiModelProperty("类型(1-平均值2-最小值3-最大值4-CP95值)")
private Integer valueType;
@ApiModelProperty("接线方式")
@NotNull(message = "接线方式不可为空")
private Integer ptType;
}

View File

@@ -0,0 +1,194 @@
package com.njcn.product.advance.harmonicUp.pojo.po;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.njcn.influx.utils.InstantDateSerializer;
import lombok.Data;
import org.influxdb.annotation.Column;
import org.influxdb.annotation.Measurement;
import org.influxdb.annotation.TimeColumn;
import java.time.Instant;
/**
* 类的介绍:
*
*/
@Data
@Measurement(name = "data_i_up")
public class DataIUp {
@Column(name = "time",tag =true)
@JsonSerialize(using = InstantDateSerializer.class)
@TimeColumn
private Instant time;
@Column(name = "line_id",tag = true)
private String lineId;
@Column(name = "phasic_type",tag = true)
private String phasicType;
@Column(name = "quality_flag",tag = true)
private String qualityFlag="0";
@Column(name = "value_type",tag = true)
private String valueType;
//是否是异常指标数据0否1是
@Column(name = "abnormal_flag")
private Integer abnormalFlag;
@Column(name = "i_1")
private Double i1;
@Column(name = "i_2")
private Double i2;
@Column(name = "i_3")
private Double i3;
@Column(name = "i_4")
private Double i4;
@Column(name = "i_5")
private Double i5;
@Column(name = "i_6")
private Double i6;
@Column(name = "i_7")
private Double i7;
@Column(name = "i_8")
private Double i8;
@Column(name = "i_9")
private Double i9;
@Column(name = "i_10")
private Double i10;
@Column(name = "i_11")
private Double i11;
@Column(name = "i_12")
private Double i12;
@Column(name = "i_13")
private Double i13;
@Column(name = "i_14")
private Double i14;
@Column(name = "i_15")
private Double i15;
@Column(name = "i_16")
private Double i16;
@Column(name = "i_17")
private Double i17;
@Column(name = "i_18")
private Double i18;
@Column(name = "i_19")
private Double i19;
@Column(name = "i_20")
private Double i20;
@Column(name = "i_21")
private Double i21;
@Column(name = "i_22")
private Double i22;
@Column(name = "i_23")
private Double i23;
@Column(name = "i_24")
private Double i24;
@Column(name = "i_25")
private Double i25;
@Column(name = "i_26")
private Double i26;
@Column(name = "i_27")
private Double i27;
@Column(name = "i_28")
private Double i28;
@Column(name = "i_29")
private Double i29;
@Column(name = "i_30")
private Double i30;
@Column(name = "i_31")
private Double i31;
@Column(name = "i_32")
private Double i32;
@Column(name = "i_33")
private Double i33;
@Column(name = "i_34")
private Double i34;
@Column(name = "i_35")
private Double i35;
@Column(name = "i_36")
private Double i36;
@Column(name = "i_37")
private Double i37;
@Column(name = "i_38")
private Double i38;
@Column(name = "i_39")
private Double i39;
@Column(name = "i_40")
private Double i40;
@Column(name = "i_41")
private Double i41;
@Column(name = "i_42")
private Double i42;
@Column(name = "i_43")
private Double i43;
@Column(name = "i_44")
private Double i44;
@Column(name = "i_45")
private Double i45;
@Column(name = "i_46")
private Double i46;
@Column(name = "i_47")
private Double i47;
@Column(name = "i_48")
private Double i48;
@Column(name = "i_49")
private Double i49;
@Column(name = "i_50")
private Double i50;
}

View File

@@ -0,0 +1,200 @@
package com.njcn.product.advance.harmonicUp.pojo.po;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.njcn.influx.utils.InstantDateSerializer;
import lombok.Data;
import org.influxdb.annotation.Column;
import org.influxdb.annotation.Measurement;
import org.influxdb.annotation.TimeColumn;
import org.springframework.beans.BeanUtils;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 类的介绍:
*
*/
@Data
@Measurement(name = "data_v_up")
public class DataVUp {
@TimeColumn
@Column(name = "time", tag = true)
@JsonSerialize(using = InstantDateSerializer.class)
private Instant time;
@Column(name = "line_id", tag = true)
private String lineId;
@Column(name = "phasic_type", tag = true)
private String phasicType;
@Column(name = "value_type", tag = true)
private String valueType;
@Column(name = "quality_flag", tag = true)
private String qualityFlag="0";
//是否是异常指标数据0否1是
@Column(name = "abnormal_flag")
private Integer abnormalFlag;
@Column(name = "v_1")
private Double v1;
@Column(name = "v_2")
private Double v2;
@Column(name = "v_3")
private Double v3;
@Column(name = "v_4")
private Double v4;
@Column(name = "v_5")
private Double v5;
@Column(name = "v_6")
private Double v6;
@Column(name = "v_7")
private Double v7;
@Column(name = "v_8")
private Double v8;
@Column(name = "v_9")
private Double v9;
@Column(name = "v_10")
private Double v10;
@Column(name = "v_11")
private Double v11;
@Column(name = "v_12")
private Double v12;
@Column(name = "v_13")
private Double v13;
@Column(name = "v_14")
private Double v14;
@Column(name = "v_15")
private Double v15;
@Column(name = "v_16")
private Double v16;
@Column(name = "v_17")
private Double v17;
@Column(name = "v_18")
private Double v18;
@Column(name = "v_19")
private Double v19;
@Column(name = "v_20")
private Double v20;
@Column(name = "v_21")
private Double v21;
@Column(name = "v_22")
private Double v22;
@Column(name = "v_23")
private Double v23;
@Column(name = "v_24")
private Double v24;
@Column(name = "v_25")
private Double v25;
@Column(name = "v_26")
private Double v26;
@Column(name = "v_27")
private Double v27;
@Column(name = "v_28")
private Double v28;
@Column(name = "v_29")
private Double v29;
@Column(name = "v_30")
private Double v30;
@Column(name = "v_31")
private Double v31;
@Column(name = "v_32")
private Double v32;
@Column(name = "v_33")
private Double v33;
@Column(name = "v_34")
private Double v34;
@Column(name = "v_35")
private Double v35;
@Column(name = "v_36")
private Double v36;
@Column(name = "v_37")
private Double v37;
@Column(name = "v_38")
private Double v38;
@Column(name = "v_39")
private Double v39;
@Column(name = "v_40")
private Double v40;
@Column(name = "v_41")
private Double v41;
@Column(name = "v_42")
private Double v42;
@Column(name = "v_43")
private Double v43;
@Column(name = "v_44")
private Double v44;
@Column(name = "v_45")
private Double v45;
@Column(name = "v_46")
private Double v46;
@Column(name = "v_47")
private Double v47;
@Column(name = "v_48")
private Double v48;
@Column(name = "v_49")
private Double v49;
@Column(name = "v_50")
private Double v50;
}

View File

@@ -0,0 +1,110 @@
package com.njcn.product.advance.harmonicUp.pojo.po;
/**
* @Author: cdf
* @CreateTime: 2025-09-12
* @Description:
*/
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 谐波放大详情表
* </p>
*
* @author
* @since 2025-09-12
*/
@Data
@TableName("up_harmonic_detail")
public class UpHarmonicDetail implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 事件id
*/
@TableField(value = "id")
private String id;
/**
* 监测点id
*/
@TableField("monitor_id")
private String monitorId;
/**
* 开始时间
*/
@TableField("start_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;
/**
* 结束时间
*/
@TableField("end_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime;
/**
* 谐波放大持续时间s
*/
@TableField("duration")
private Double duration;
/**
* 谐波次数
*/
@TableField("harmonic_count")
private Integer harmonicCount;
/**
* 相别
*/
@TableField("phase")
private String phase;
/**
* 电压标准值
*/
@TableField("v_Avg_Value")
private Double vAvgValue;
/**
* 电流标准值
*/
@TableField("i_Avg_Value")
private Double iAvgValue;
@TableField("up_scheme")
private String upScheme;
/**
* 创建时间
*/
@TableField("create_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@TableField(exist = false)
private String stationName;
@TableField(exist = false)
private String monitorName;
@TableField(exist = false)
private String objName;
}

View File

@@ -0,0 +1,55 @@
package com.njcn.product.advance.harmonicUp.pojo.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author denghuajun
* @date 2022/3/11
*
*/
@Data
public class HistoryDataResultVO implements Serializable {
@ApiModelProperty("监测点名称")
private String lineName;
@ApiModelProperty("指标名称")
private String targetName;
@ApiModelProperty("相别")
private List<String> phaiscType;
@ApiModelProperty("单位")
private List<String> unit;
@ApiModelProperty("谐波次数")
private Integer harmNum;
@ApiModelProperty("数值")
private List<List<Object>> value;
@ApiModelProperty("最小值")
private Float minValue;
@ApiModelProperty("最大值")
private Float maxValue;
@ApiModelProperty("上限")
private Float topLimit;
@ApiModelProperty("下限")
private Float lowerLimit;
@ApiModelProperty("接线方式 0.星型 1.星三角 2.三角")
private String wiringMethod;
/* @ApiModelProperty("暂降事件详情")
private List<EventDetailVO> eventDetail;*/
}

View File

@@ -0,0 +1,30 @@
package com.njcn.product.advance.harmonicUp.pojo.vo;
import com.njcn.influx.pojo.bo.HarmonicHistoryData;
import lombok.Data;
import org.influxdb.dto.QueryResult;
import java.io.Serializable;
import java.util.List;
/**
* @author denghuajun
* @date 2022/3/15
* 存值
*/
@Data
public class QueryResultLimitVO implements Serializable {
private QueryResult queryResult;
private List<HarmonicHistoryData> harmonicHistoryDataList;
private Float topLimit;
private Float lowerLimit;
private String lineName;
private String targetName;
private List<String> phaiscType;
private List<String> unit;
private Integer harmNum;
/**
* 接线方式 0.星型 1.星三角 2.三角
*/
private String wiringMethod;
}

View File

@@ -0,0 +1,32 @@
package com.njcn.product.advance.harmonicUp.pojo.vo;
import lombok.Data;
import java.util.List;
/**
* @Author: cdf
* @CreateTime: 2025-09-15
* @Description:
*/
@Data
public class UpTableInfo {
private List<String> date;
private List<String> monitorList;
private List<Inner> inner;
@Data
public static class Inner{
private String date;
private String lineId;
private String monitorName;
private Integer count = 0;
}
}

View File

@@ -0,0 +1,28 @@
package com.njcn.product.advance.harmonicUp.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.product.advance.harmonicUp.pojo.po.UpHarmonicDetail;
import com.njcn.product.advance.harmonicUp.pojo.vo.UpTableInfo;
import com.njcn.product.terminal.mysqlTerminal.pojo.param.LargeScreenCountParam;
import java.util.List;
/**
* @Author: cdf
* @CreateTime: 2025-09-11
* @Description: 谐波放大
*/
public interface HarmonicUpService extends IService<UpHarmonicDetail> {
void analyzePreData(String date);
Page<UpHarmonicDetail> getDetail(LargeScreenCountParam param);
Page<UpHarmonicDetail> getInfoList(LargeScreenCountParam param);
UpTableInfo tableInfo(LargeScreenCountParam param);
}

View File

@@ -0,0 +1,27 @@
package com.njcn.product.advance.harmonicUp.service;
import com.njcn.common.pojo.param.StatisticsBizBaseParam;
import com.njcn.influx.pojo.dto.HarmHistoryDataDTO;
import com.njcn.product.advance.harmonicUp.pojo.param.HistoryParam;
import com.njcn.product.advance.harmonicUp.pojo.vo.HistoryDataResultVO;
import java.util.List;
/**
* 稳态数据
* @author denghuajun
* @date 2022/3/14
*
*/
public interface HistoryResultService {
/**
* 稳态数据分析
* @param historyParam 参数
* @return 结果
*/
List<HistoryDataResultVO> getHistoryResult(HistoryParam historyParam);
}

View File

@@ -0,0 +1,719 @@
package com.njcn.product.advance.harmonicUp.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.*;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.influx.imapper.DataIMapper;
import com.njcn.influx.imapper.DataVMapper;
import com.njcn.influx.pojo.constant.InfluxDBTableConstant;
import com.njcn.influx.pojo.po.DataI;
import com.njcn.influx.pojo.po.DataV;
import com.njcn.influx.query.InfluxQueryWrapper;
import com.njcn.product.advance.harmonicUp.imapper.DataIUpToMapper;
import com.njcn.product.advance.harmonicUp.imapper.DataVUpToMapper;
import com.njcn.product.advance.harmonicUp.mapper.UpHarmonicDetailMapper;
import com.njcn.product.advance.harmonicUp.pojo.po.DataIUp;
import com.njcn.product.advance.harmonicUp.pojo.po.DataVUp;
import com.njcn.product.advance.harmonicUp.pojo.po.UpHarmonicDetail;
import com.njcn.product.advance.harmonicUp.pojo.vo.UpTableInfo;
import com.njcn.product.advance.harmonicUp.service.HarmonicUpService;
import com.njcn.product.terminal.mysqlTerminal.mapper.LedgerScaleMapper;
import com.njcn.product.terminal.mysqlTerminal.pojo.dto.LedgerBaseInfo;
import com.njcn.product.terminal.mysqlTerminal.pojo.param.LargeScreenCountParam;
import com.njcn.product.terminal.mysqlTerminal.service.CommGeneralService;
import com.njcn.web.factory.PageFactory;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Author: cdf
* @CreateTime: 2025-09-11
* @Description:
*/
/**
* @Author: cdf
* @CreateTime: 2025-09-11
* @Description: 谐波分析服务实现类仅使用up_harmonic_detail表存储事件信息
*/
@Service
@EnableScheduling
@RequiredArgsConstructor
@Slf4j
public class HarmonicUpServiceImpl extends ServiceImpl<UpHarmonicDetailMapper,UpHarmonicDetail> implements HarmonicUpService {
private final DataVMapper dataVMapper;
private final DataIMapper dataIMapper;
private final DataVUpToMapper dataVUpToMapper;
private final DataIUpToMapper dataIUpToMapper;
private final CommGeneralService commGeneralService;
private final LedgerScaleMapper ledgerScaleMapper;
// 常量定义
final String DAY_FORMAT = DatePattern.NORM_DATE_PATTERN;
final String MONTH_FORMAT = DatePattern.NORM_MONTH_PATTERN;
// 配置参数
@Value("${harmonic.voltage.change.rate.threshold:0.20}")
private Double voltageChangeRateThreshold;
//电流
@Value("${harmonic.current.change.rate.threshold:0.10}")
private Double currentChangeRateThreshold;
@Value("${harmonic.continuous.anomaly.count:10}")
private Integer continuousAnomalyCount;
// 需要分析的特定谐波次数,可根据需求扩展
private List<Integer> specificHarmonicOrders = Arrays.asList(3, 5, 7, 11, 13);
/**
* 每日凌晨2点执行谐波分析分析前一天的数据
*/
@Scheduled(cron = "0 0 2 * * ?")
public void dailyHarmonicAnalysis() {
analyzePreData("");
}
@Override
public void analyzePreData(String date) {
log.info("开始执行谐波分析,日期: {}", date);
Map<String,Map<String, Double>> avgVMap = new HashMap<>();
Map<String,Map<String, Double>> avgIMap = new HashMap<>();
// 计算并保存电压和电流变化率数据
List<DataVUp> dataVUpList = calculateAndSaveDataVUp(date,avgVMap);
List<DataIUp> dataIUpList = calculateAndSaveDataIUp(date,avgIMap);
// 分析谐波放大事件并直接保存到up_harmonic_detail表
analyzeAndSaveHarmonicEvents(dataVUpList, dataIUpList,avgVMap,avgIMap);
log.info("谐波分析执行完成,日期: {}", date);
}
@Override
public Page<UpHarmonicDetail> getDetail(LargeScreenCountParam param) {
Page<UpHarmonicDetail> resultPage = new Page<>(PageFactory.getPageNum(param),PageFactory.getPageSize(param));
DateTime date;
LambdaQueryWrapper<UpHarmonicDetail> lambdaQueryWrapper = new LambdaQueryWrapper<>();
try {
date = DateUtil.parse(param.getSearchBeginTime(),DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));
lambdaQueryWrapper.between(UpHarmonicDetail::getStartTime,DateUtil.beginOfDay(date),DateUtil.endOfDay(date));
}catch (Exception e){
date = DateUtil.parse(param.getSearchBeginTime(),DateTimeFormatter.ofPattern(DatePattern.NORM_MONTH_PATTERN));
lambdaQueryWrapper.between(UpHarmonicDetail::getStartTime,DateUtil.beginOfMonth(date),DateUtil.endOfMonth(date));
}
lambdaQueryWrapper.eq(UpHarmonicDetail::getMonitorId,param.getLineId()).orderByAsc(UpHarmonicDetail::getHarmonicCount,UpHarmonicDetail::getPhase,UpHarmonicDetail::getStartTime);
Page<UpHarmonicDetail> result = this.page(new Page<>(PageFactory.getPageNum(param),PageFactory.getPageSize(param)),lambdaQueryWrapper);
if(CollUtil.isEmpty(result.getRecords())){
return resultPage;
}
List<LedgerBaseInfo> ledgerBaseInfoList = ledgerScaleMapper.getLedgerBaseInfo(Stream.of(param.getLineId()).collect(Collectors.toList()));
if(CollUtil.isEmpty(ledgerBaseInfoList)){
throw new BusinessException(CommonResponseEnum.FAIL,"查询台账为空");
}
LedgerBaseInfo ledgerBaseInfo = ledgerBaseInfoList.get(0);
result.getRecords().forEach(it->{
it.setMonitorName(ledgerBaseInfo.getLineName());
it.setStationName(ledgerBaseInfo.getStationName());
it.setObjName(ledgerBaseInfo.getObjName());
it.setVAvgValue(BigDecimal.valueOf(it.getVAvgValue()*1000).setScale(2,RoundingMode.HALF_UP).doubleValue());
});
return result;
}
@Override
public Page<UpHarmonicDetail> getInfoList(LargeScreenCountParam param) {
Page<UpHarmonicDetail> result = new Page<>(PageFactory.getPageNum(param),PageFactory.getPageSize(param));
DateTime start = DateUtil.beginOfDay(DateUtil.parse(param.getSearchBeginTime()));
DateTime end = DateUtil.endOfDay(DateUtil.parse(param.getSearchEndTime()));
List<String> lineIds = commGeneralService.getRunLineIdsByDept(param.getDeptId());
if(CollUtil.isEmpty(lineIds)){
return result;
}
LambdaQueryWrapper<UpHarmonicDetail> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.between(UpHarmonicDetail::getStartTime,start,end)
.in(UpHarmonicDetail::getMonitorId,lineIds).orderByDesc(UpHarmonicDetail::getStartTime);
result = this.page(new Page<>(PageFactory.getPageNum(param),PageFactory.getPageSize(param)),lambdaQueryWrapper);
if(CollUtil.isEmpty(result.getRecords())){
return result;
}
List<String> ids = result.getRecords().stream().map(UpHarmonicDetail::getMonitorId).distinct().collect(Collectors.toList());
List<LedgerBaseInfo> ledgerBaseInfoList = ledgerScaleMapper.getLedgerBaseInfo(ids);
if(CollUtil.isEmpty(ledgerBaseInfoList)){
throw new BusinessException(CommonResponseEnum.FAIL,"查询台账为空");
}
Map<String,LedgerBaseInfo> ledgerBaseInfoMap = ledgerBaseInfoList.stream().collect(Collectors.toMap(LedgerBaseInfo::getLineId,line->line));
result.getRecords().forEach(it->{
LedgerBaseInfo ledgerBaseInfo = ledgerBaseInfoMap.get(it.getMonitorId());
it.setMonitorName(ledgerBaseInfo.getLineName());
it.setStationName(ledgerBaseInfo.getStationName());
it.setObjName(ledgerBaseInfo.getObjName());
it.setVAvgValue(BigDecimal.valueOf(it.getVAvgValue()*1000).setScale(2,RoundingMode.HALF_UP).doubleValue());
});
return result;
}
@Override
public UpTableInfo tableInfo(LargeScreenCountParam param) {
// 初始化结果对象
UpTableInfo result = new UpTableInfo();
List<String> dateList = new ArrayList<>();
List<UpTableInfo.Inner> innerList = new ArrayList<>();
// 1. 获取线路ID和台账基础信息
List<String> lineIds = commGeneralService.getRunLineIdsByDept(param.getDeptId());
List<LedgerBaseInfo> ledgerBaseInfoList = ledgerScaleMapper.getLedgerBaseInfo(lineIds);
if (CollUtil.isEmpty(ledgerBaseInfoList)) {
throw new BusinessException(CommonResponseEnum.FAIL, "查询台账为空");
}
// 2. 构建线路ID到名称的映射 & 提取线路名称列表
Map<String, LedgerBaseInfo> ledgerBaseInfoMap = ledgerBaseInfoList.stream()
.collect(Collectors.toMap(LedgerBaseInfo::getLineId, Function.identity()));
List<String> ledgerList = ledgerBaseInfoList.stream().map(it-> {
if(StrUtil.isBlank(it.getObjName())){
return it.getLineName();
}
return strTranslate(it.getObjName());
}).collect(Collectors.toList());
// 3. 处理日期范围
DateTime start = DateUtil.beginOfDay(DateUtil.parse(param.getSearchBeginTime()));
DateTime end = DateUtil.endOfDay(DateUtil.parse(param.getSearchEndTime()));
long dayDiff = DateUtil.betweenDay(start, end, false);
// 确定日期范围和格式
DateRange dateRange;
String dateFormat;
if (dayDiff <= 31) {
dateRange = DateUtil.range(start, end, DateField.DAY_OF_MONTH);
dateFormat = DAY_FORMAT;
} else {
dateRange = DateUtil.range(start, end, DateField.MONTH);
dateFormat = MONTH_FORMAT;
}
// 4. 查询数据
LambdaQueryWrapper<UpHarmonicDetail> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.between(UpHarmonicDetail::getStartTime, start, end)
.in(UpHarmonicDetail::getMonitorId, lineIds)
.orderByDesc(UpHarmonicDetail::getStartTime);
List<UpHarmonicDetail> data = this.list(queryWrapper);
// 5. 处理数据(分空数据和非空数据情况)
if (CollUtil.isEmpty(data)) {
// 无数据时填充默认值
fillDefaultData(dateRange, dateFormat, ledgerBaseInfoList, dateList, innerList);
} else {
// 有数据时按日期和线路分组统计
processExistingData(dateRange, dateFormat, ledgerBaseInfoMap, data, dateList, innerList);
}
// 6. 设置结果
result.setDate(dateList);
result.setMonitorList(ledgerList);
result.setInner(innerList);
return result;
}
/**
* 填充默认数据(当查询结果为空时)
*/
private void fillDefaultData(DateRange dateRange, String dateFormat,
List<LedgerBaseInfo> ledgerList, List<String> dateList,
List<UpTableInfo.Inner> innerList) {
for (DateTime range : dateRange) {
String time = DateUtil.format(range, dateFormat);
dateList.add(time);
ledgerList.forEach(line -> {
UpTableInfo.Inner inner = new UpTableInfo.Inner();
inner.setDate(time);
inner.setMonitorName(StrUtil.isNotBlank(line.getObjName())?strTranslate(line.getObjName()):line.getLineName());
inner.setLineId(line.getLineId());
inner.setCount(0);
innerList.add(inner);
});
}
}
/**
* 处理查询到的数据
*/
private void processExistingData(DateRange dateRange, String dateFormat,
Map<String, LedgerBaseInfo> ledgerBaseInfoMap,
List<UpHarmonicDetail> data,
List<String> dateList,
List<UpTableInfo.Inner> innerList) {
// 按监控ID分组
Map<String, List<UpHarmonicDetail>> upMonitorMap = data.stream()
.collect(Collectors.groupingBy(UpHarmonicDetail::getMonitorId));
Map<String, LedgerBaseInfo> temMap = ObjectUtil.cloneByStream(ledgerBaseInfoMap);
for (DateTime range : dateRange) {
String time = DateUtil.format(range, dateFormat);
dateList.add(time);
upMonitorMap.forEach((lineKey, details) -> {
temMap.remove(lineKey);
UpTableInfo.Inner inner = new UpTableInfo.Inner();
inner.setDate(time);
if(ledgerBaseInfoMap.containsKey(lineKey)){
LedgerBaseInfo ledgerBaseInfo = ledgerBaseInfoMap.get(lineKey);
inner.setMonitorName(StrUtil.isNotBlank(ledgerBaseInfo.getObjName())?strTranslate(ledgerBaseInfo.getObjName()):ledgerBaseInfo.getLineName());
inner.setLineId(ledgerBaseInfo.getLineId());
}
// 统计当前日期下的记录数
long count = details.stream()
.filter(detail -> DateUtil.format(detail.getStartTime(),dateFormat).equals(time))
.count();
inner.setCount((int) count);
innerList.add(inner);
});
//针对未发生谐波放大的测点
temMap.forEach((k,v)->{
UpTableInfo.Inner inner = new UpTableInfo.Inner();
inner.setDate(time);
inner.setMonitorName(StrUtil.isNotBlank(v.getObjName())?strTranslate(v.getObjName()):v.getLineName());
inner.setLineId(v.getLineId());
inner.setCount(0);
innerList.add(inner);
});
}
}
private String strTranslate(String str){
return str.replace("无锡市", "").replace("无锡", "");
}
/**
* 计算并保存电压变化率数据
*/
private List<DataVUp> calculateAndSaveDataVUp(String date,Map<String,Map<String, Double>> avgVMap) {
InfluxQueryWrapper influxQueryWrapperV = new InfluxQueryWrapper(DataV.class);
influxQueryWrapperV.between(DataV::getTime, date.concat(InfluxDBTableConstant.START_TIME), date.concat(InfluxDBTableConstant.END_TIME))
.eq(DataV::getValueType, InfluxDBTableConstant.CP95)
.ne(DataV::getPhaseType, InfluxDBTableConstant.PHASE_TYPE_T);
List<DataV> dataVList = dataVMapper.selectByQueryWrapper(influxQueryWrapperV);
if(CollUtil.isEmpty(dataVList)){
log.error("data_v谐波放大算法查询原始数据为空");
}
Map<String, List<DataV>> lineVMap = dataVList.stream()
.collect(Collectors.groupingBy(DataV::getLineId));
List<DataVUp> dataVUpList = new ArrayList<>();
lineVMap.forEach((lineId, list) -> {
// 计算所有字段的平均值V1-V50
Map<String, Double> avgMap = calculateFieldAveragesV(list, 50);
avgVMap.put(lineId,avgMap);
// 按相别分组处理
Map<String, List<DataV>> phaseList = list.stream()
.collect(Collectors.groupingBy(DataV::getPhaseType));
phaseList.forEach((phase, pList) -> {
pList.forEach(dataV -> {
DataVUp dataVUp = new DataVUp();
dataVUp.setPhasicType(phase);
dataVUp.setTime(dataV.getTime());
dataVUp.setLineId(dataV.getLineId());
dataVUp.setValueType(dataV.getValueType());
// 动态设置 V1-V50 的相对变化率
avgMap.forEach((field, avg) -> {
try {
// 获取 DataV 的当前字段值
Method getter = DataV.class.getMethod("get" + field);
double value = (double) getter.invoke(dataV);
// 计算相对变化率
double relativeChange = (avg == 0) ? 0 :
BigDecimal.valueOf((value - avg) / avg)
.setScale(6, RoundingMode.HALF_UP)
.doubleValue();
// 设置到 DataVUp
Method setter = DataVUp.class.getMethod("set" + field, Double.class);
setter.invoke(dataVUp, relativeChange);
// 判断是否为异常值
if (specificHarmonicOrders.contains(getHarmonicOrderFromField(field))
&& relativeChange > voltageChangeRateThreshold) {
dataVUp.setAbnormalFlag(1);
} else if (dataVUp.getAbnormalFlag() == null) {
dataVUp.setAbnormalFlag(0);
}
} catch (NoSuchMethodException e) {
log.error("字段 {} 的 getter/setter 方法不存在", field, e);
} catch (Exception e) {
log.error("反射调用字段 {} 失败: {}", field, e.getMessage(), e);
}
});
dataVUpList.add(dataVUp);
});
});
});
if(CollUtil.isNotEmpty(dataVUpList)) {
List<DataVUp> result = dataVUpList.stream()
.sorted(Comparator.comparing(DataVUp::getTime).thenComparing(DataVUp::getPhasicType))
.collect(Collectors.toList());
dataVUpToMapper.insertBatch(result);
}
return dataVUpList;
}
/**
* 计算并保存电流变化率数据
*/
private List<DataIUp> calculateAndSaveDataIUp(String date,Map<String,Map<String, Double>> avgIMap) {
InfluxQueryWrapper influxQueryWrapperI = new InfluxQueryWrapper(DataI.class);
influxQueryWrapperI.between(DataI::getTime, date.concat(InfluxDBTableConstant.START_TIME), date.concat(InfluxDBTableConstant.END_TIME))
.eq(DataI::getValueType, InfluxDBTableConstant.CP95)
.ne(DataI::getPhaseType, InfluxDBTableConstant.PHASE_TYPE_T);
List<DataI> dataIList = dataIMapper.selectByQueryWrapper(influxQueryWrapperI);
if(CollUtil.isEmpty(dataIList)){
log.error("data_i谐波放大算法查询原始数据为空");
}
Map<String, List<DataI>> lineIMap = dataIList.stream()
.collect(Collectors.groupingBy(DataI::getLineId));
List<DataIUp> dataIUpList = new ArrayList<>();
lineIMap.forEach((lineId, list) -> {
// 计算所有字段的平均值I1-I50
Map<String, Double> avgMap = calculateFieldAveragesI(list, 50);
avgIMap.put(lineId,avgMap);
// 按相别分组处理
Map<String, List<DataI>> phaseList = list.stream()
.collect(Collectors.groupingBy(DataI::getPhaseType));
phaseList.forEach((phase, pList) -> {
pList.forEach(dataI -> {
DataIUp dataIUp = new DataIUp();
dataIUp.setPhasicType(phase);
dataIUp.setTime(dataI.getTime());
dataIUp.setLineId(dataI.getLineId());
dataIUp.setValueType(dataI.getValueType());
// 动态设置 I1-I50 的相对变化率
avgMap.forEach((field, avg) -> {
try {
// 获取 DataI 的当前字段值
Method getter = DataI.class.getMethod("get" + field);
double value = (double) getter.invoke(dataI);
double relativeChange = 3.1415926;
if(value>0.01){
// 计算相对变化率
relativeChange = (avg == 0) ? 0 :
BigDecimal.valueOf(Math.abs(value - avg) / avg)
.setScale(6, RoundingMode.HALF_UP)
.doubleValue();
}
// 设置到 DataIUp
Method setter = DataIUp.class.getMethod("set" + field, Double.class);
setter.invoke(dataIUp, relativeChange);
} catch (NoSuchMethodException e) {
log.error("字段 {} 的 getter/setter 方法不存在", field, e);
} catch (Exception e) {
log.error("反射调用字段 {} 失败: {}", field, e.getMessage(), e);
}
});
dataIUpList.add(dataIUp);
});
});
});
if(CollUtil.isNotEmpty(dataIUpList)) {
List<DataIUp> result = dataIUpList.stream()
.sorted(Comparator.comparing(DataIUp::getTime).thenComparing(DataIUp::getPhasicType))
.collect(Collectors.toList());
dataIUpToMapper.insertBatch(result);
}
return dataIUpList;
}
/**
* 分析谐波放大事件并保存到up_harmonic_detail表
*/
private void analyzeAndSaveHarmonicEvents(List<DataVUp> dataVUpList, List<DataIUp> dataIUpList,Map<String,Map<String, Double>> avgVMap,Map<String,Map<String, Double>> avgIMap) {
// 按线路、时间、相别分组电流数据,便于查询
Map<String, DataIUp> iUpMap = dataIUpList.stream()
.collect(Collectors.toMap(
item -> item.getLineId() + StrUtil.C_UNDERLINE + item.getTime() + StrUtil.C_UNDERLINE + item.getPhasicType(),
item -> item
));
// 存储所有异常点信息
Map<String, List<AnomalyInfo>> anomalyMap = new HashMap<>();
// 遍历电压数据,检查异常点
Set<String> lineSet = new HashSet<>();
for (DataVUp vUp : dataVUpList) {
lineSet.add(vUp.getLineId());
String key = vUp.getLineId() + StrUtil.C_UNDERLINE + vUp.getTime() + StrUtil.C_UNDERLINE + vUp.getPhasicType();
// 检查是否有对应的电流数据
if (!iUpMap.containsKey(key)) {
continue;
}
DataIUp iUp = iUpMap.get(key);
// 检查每个特定谐波次数
for (int order : specificHarmonicOrders) {
try {
// 获取电压变化率
String vField = "v" + order;
Method vGetter = DataVUp.class.getMethod("get" + Character.toUpperCase(vField.charAt(0)) + vField.substring(1));
double vChangeRate = (double) vGetter.invoke(vUp);
// 获取电流变化率
String iField = "i" + order;
Method iGetter = DataIUp.class.getMethod("get" + Character.toUpperCase(iField.charAt(0)) + iField.substring(1));
double iChangeRate = (double) iGetter.invoke(iUp);
// 检查是否满足异常条件
if (vChangeRate > voltageChangeRateThreshold && iChangeRate < currentChangeRateThreshold && iChangeRate!=3.1415926) {
String anomalyKey = vUp.getLineId() + StrUtil.C_UNDERLINE + vUp.getPhasicType() + StrUtil.C_UNDERLINE + order;
AnomalyInfo info = new AnomalyInfo();
info.setLineId(vUp.getLineId());
info.setPhase(vUp.getPhasicType());
info.setHarmonicOrder(order);
info.setTime(vUp.getTime());
info.setVoltageChangeRate(vChangeRate);
info.setCurrentChangeRate(iChangeRate);
if (!anomalyMap.containsKey(anomalyKey)) {
anomalyMap.put(anomalyKey, new ArrayList<>());
}
anomalyMap.get(anomalyKey).add(info);
}
} catch (Exception e) {
log.error("分析谐波次数 {} 时发生错误", order, e);
}
}
}
List<LedgerBaseInfo> ledgerBaseInfoList = ledgerScaleMapper.getLedgerBaseInfo(new ArrayList<>(lineSet));
Map<String,LedgerBaseInfo> ledgerBaseInfoMap = ledgerBaseInfoList.stream().collect(Collectors.toMap(LedgerBaseInfo::getLineId,Function.identity()));
// 处理异常点,生成谐波放大事件
List<UpHarmonicDetail> harmonicDetails = new ArrayList<>();
for (List<AnomalyInfo> anomalies : anomalyMap.values()) {
if (anomalies.size() < continuousAnomalyCount) {
continue; // 连续异常点数量不足,不生成事件
}
// 按时间排序
List<AnomalyInfo> sortedAnomalies = anomalies.stream()
.sorted(Comparator.comparing(AnomalyInfo::getTime))
.collect(Collectors.toList());
if(sortedAnomalies.get(0).getLineId().equals("9686e66738bab8516ff2c2e9fedc0518")){
System.out.println(555);
}
// 检查是否连续
List<List<AnomalyInfo>> infoList =findAllContinuousAnomalies(sortedAnomalies,ledgerBaseInfoMap);
if (!infoList.isEmpty()) {
for (List<AnomalyInfo> temList : infoList) {
AnomalyInfo first = temList.get(0);
AnomalyInfo last = temList.get(temList.size() - 1);
// 创建谐波放大事件记录
UpHarmonicDetail detail = new UpHarmonicDetail();
detail.setId(IdWorker.get32UUID());
detail.setMonitorId(first.getLineId());
detail.setPhase(first.getPhase());
detail.setHarmonicCount(first.getHarmonicOrder());
// 转换时间格式
detail.setStartTime(LocalDateTime.ofInstant(first.getTime(), ZoneId.systemDefault()));
detail.setEndTime(LocalDateTime.ofInstant(last.getTime(), ZoneId.systemDefault()));
// 计算持续时间(秒)
long duration = ChronoUnit.SECONDS.between(
detail.getStartTime(),
detail.getEndTime()
);
detail.setDuration((double) duration);
detail.setVAvgValue(avgVMap.get(first.lineId).get("V"+first.getHarmonicOrder()));
detail.setIAvgValue(avgIMap.get(first.lineId).get("I"+first.getHarmonicOrder()));
detail.setCreateTime(LocalDateTime.now());
if(first.getHarmonicOrder()>5){
detail.setUpScheme("选取5% - 6%电抗率");
}else {
detail.setUpScheme("选取12%的电抗率或选取5% - 6%与12%两种电抗率混装方式)");
}
harmonicDetails.add(detail);
log.info("发现谐波放大事件: 监测点={}, 谐波次数={}, 持续时间={}秒, 开始时间={}",
first.getLineId(), first.getHarmonicOrder(), duration, detail.getStartTime());
}
}
}
// 批量保存事件
if (CollUtil.isNotEmpty(harmonicDetails)) {
this.saveBatch(harmonicDetails);
}
}
/**
* 检查数据是否连续(每分钟一条记录)
*/
/**
* 查找所有连续10条记录每条间隔50~70秒的事件
*/
private List<List<AnomalyInfo>> findAllContinuousAnomalies(List<AnomalyInfo> anomalies,Map<String,LedgerBaseInfo> ledgerBaseInfoMap) {
List<List<AnomalyInfo>> groups = new ArrayList<>();
List<AnomalyInfo> currentGroup = new ArrayList<>();
int timeInterval = ledgerBaseInfoMap.get(anomalies.get(0).getLineId()).getTimeInterval();
for (AnomalyInfo current : anomalies) {
if (currentGroup.isEmpty()) {
currentGroup.add(current);
} else {
AnomalyInfo lastInGroup = currentGroup.get(currentGroup.size() - 1);
long minutesDiff = ChronoUnit.MINUTES.between(lastInGroup.getTime(), current.getTime());
if (minutesDiff == timeInterval) {
currentGroup.add(current);
} else {
// 不连续,检查当前组是否满足 >=10
if (currentGroup.size() >= continuousAnomalyCount) {
groups.add(new ArrayList<>(currentGroup));
}
// 重置当前组
currentGroup.clear();
currentGroup.add(current);
}
}
}
return groups;
}
/**
* 从字段名中提取谐波次数
*/
private int getHarmonicOrderFromField(String field) {
try {
return Integer.parseInt(field.substring(1));
} catch (Exception e) {
return -1;
}
}
/**
* 计算 DataV 对象中 V1-Vmax 的平均值
*/
private Map<String, Double> calculateFieldAveragesV(List<DataV> list, Integer maxField) {
Map<String, Double> avgMap = new HashMap<>();
for (int i = 1; i <= maxField; i++) {
String field = "V" + i;
double avg = list.stream()
.mapToDouble(dataV -> {
try {
Method getter = DataV.class.getMethod("get" + field);
return (double) getter.invoke(dataV);
} catch (Exception e) {
return 0d;
}
})
.average()
.orElse(0);
avgMap.put(field, avg);
}
return avgMap;
}
/**
* 计算 DataI 对象中 I1-Imax 的平均值
*/
private Map<String, Double> calculateFieldAveragesI(List<DataI> list, Integer maxField) {
Map<String, Double> avgMap = new HashMap<>();
for (int i = 1; i <= maxField; i++) {
String field = "I" + i;
double avg = list.stream()
.mapToDouble(dataI -> {
try {
Method getter = DataI.class.getMethod("get" + field);
return (double) getter.invoke(dataI);
} catch (Exception e) {
return 0d;
}
})
.average()
.orElse(0);
avgMap.put(field, avg);
}
return avgMap;
}
/**
* 内部类:用于临时存储异常点信息
*/
@Data
private static class AnomalyInfo {
private String lineId;
private String phase;
private int harmonicOrder;
private Instant time;
private double voltageChangeRate;
private double currentChangeRate;
}
}

View File

@@ -0,0 +1,777 @@
package com.njcn.product.advance.harmonicUp.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.njcn.common.pojo.constant.BizParamConstant;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.param.StatisticsBizBaseParam;
import com.njcn.common.utils.PubUtils;
import com.njcn.influx.imapper.CommonMapper;
import com.njcn.influx.imapper.DataHarmRateVMapper;
import com.njcn.influx.imapper.DataIMapper;
import com.njcn.influx.pojo.bo.HarmonicHistoryData;
import com.njcn.influx.pojo.constant.InfluxDBTableConstant;
import com.njcn.product.advance.harmonicUp.pojo.param.HistoryParam;
import com.njcn.product.advance.harmonicUp.pojo.vo.HistoryDataResultVO;
import com.njcn.product.advance.harmonicUp.pojo.vo.QueryResultLimitVO;
import com.njcn.product.advance.harmonicUp.service.HistoryResultService;
import com.njcn.product.system.dict.mapper.DictDataMapper;
import com.njcn.product.terminal.mysqlTerminal.mapper.LineMapper;
import com.njcn.product.terminal.mysqlTerminal.mapper.OverlimitMapper;
import com.njcn.product.terminal.mysqlTerminal.pojo.dto.LineDevGetDTO;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.Overlimit;
import com.njcn.product.terminal.mysqlTerminal.pojo.po.PqsDeviceUnit;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author denghuajun
* @date 2022/3/14
*/
@Slf4j
@Service
@AllArgsConstructor
public class HistoryResultServiceImpl implements HistoryResultService {
private final CommonMapper commonMapper;
private final DataIMapper dataIMapper;
private final DataHarmRateVMapper dataHarmRateVMapper;
private final LineMapper lineMapper;
private final OverlimitMapper overlimitMapper;
private final DictDataMapper dictDataMapper;
@Override
public List<HistoryDataResultVO> getHistoryResult(HistoryParam historyParam) {
List<HistoryDataResultVO> historyDataResultVOList = new ArrayList<>();
//获取监测点
String[] points = historyParam.getLineId();
Integer number = 0;
for (int i = 0; i < points.length; i++) {
HistoryDataResultVO historyDataResultVO;
//获取指标
String[] contions = historyParam.getCondition();
for (int j = 0; j < contions.length; j++) {
if ("40".equals(contions[j]) || "41".equals(contions[j]) || "42".equals(contions[j]) || "43".equals(contions[j])
|| "44".equals(contions[j]) || "45".equals(contions[j]) || "50".equals(contions[j]) || "51".equals(contions[j])
|| "52".equals(contions[j])) {
number = historyParam.getHarmonic();
}
if ("46".equals(contions[j]) || "47".equals(contions[j]) || "48".equals(contions[j]) || "49".equals(contions[j])) {
number = historyParam.getInHarmonic();
}
historyDataResultVO = getCondition(historyParam.getSearchBeginTime(), historyParam.getSearchEndTime(), points[i], contions[j], number, historyParam.getValueType(), historyParam.getPtType());
historyDataResultVOList.add(historyDataResultVO);
}
}
return historyDataResultVOList;
}
/**
* influxDB相关操作
* 查询稳态数据分析
*/
@SneakyThrows
private HistoryDataResultVO getCondition(String startTime, String endTime, String lineId, String contion, Integer number, Integer valueType, Integer ptType) {
HistoryDataResultVO historyDataResultVO = new HistoryDataResultVO();
QueryResultLimitVO queryResultLimitVO = getQueryResult(startTime, endTime, lineId, contion, number, valueType, ptType);
List<HarmonicHistoryData> harmonicHistoryDataList = queryResultLimitVO.getHarmonicHistoryDataList();
BeanUtil.copyProperties(queryResultLimitVO, historyDataResultVO);
//时间轴
List<Date> time = new ArrayList<>();
//A相值
List<Float> aValue;
//B相值
List<Float> bValue = new ArrayList<>();
//C相值
List<Float> cValue = new ArrayList<>();
//针对统计相别为T时存放的数据
List<Float> fValue = new ArrayList<>();
List<List<Object>> objectListData = new ArrayList<>();
if (CollectionUtil.isNotEmpty(harmonicHistoryDataList)) {
//相别统计为T时业务数据处理
if (StrUtil.isBlank(harmonicHistoryDataList.get(0).getPhasicType()) || harmonicHistoryDataList.get(0).getPhasicType().equalsIgnoreCase("t")) {
for (HarmonicHistoryData harmonicHistoryData : harmonicHistoryDataList) {
time.add(PubUtils.instantToDate(harmonicHistoryData.getTime()));
fValue.add(BigDecimal.valueOf(harmonicHistoryData.getAValue()).setScale(4, RoundingMode.HALF_UP).floatValue());
//返回结果有多个值,需要额外处理下
if (Integer.parseInt(contion) == 14) {
bValue.add(BigDecimal.valueOf(harmonicHistoryData.getBValue()).setScale(4, RoundingMode.HALF_UP).floatValue());
cValue.add(BigDecimal.valueOf(harmonicHistoryData.getCValue()).setScale(4, RoundingMode.HALF_UP).floatValue());
}
}
//组装二维数组
for (int i = 0; i < time.size(); i++) {
List<Object> objects = new ArrayList<>();
objects.add(time.get(i));
objects.add(fValue.get(i));
if (Integer.parseInt(contion) == 14) {
objects.add(bValue.get(i));
objects.add(cValue.get(i));
}
objectListData.add(objects);
}
historyDataResultVO.setTopLimit(queryResultLimitVO.getTopLimit());
historyDataResultVO.setLowerLimit(queryResultLimitVO.getLowerLimit());
historyDataResultVO.setMinValue(Collections.min(fValue));
historyDataResultVO.setMaxValue(Collections.max(fValue));
historyDataResultVO.setValue(objectListData);
} else {
//按时间分组
Map<Instant, List<HarmonicHistoryData>> map = harmonicHistoryDataList.stream().collect(Collectors.groupingBy(HarmonicHistoryData::getTime, TreeMap::new, Collectors.toList()));
Float maxI = null;
Float minI = null;
for (Map.Entry<Instant, List<HarmonicHistoryData>> entry : map.entrySet()) {
List<HarmonicHistoryData> val = entry.getValue();
Object[] objects = {PubUtils.instantToDate(entry.getKey()), 0, 0, 0};
//需要保证val的长度为3
if (val.size() != 3) {
for (int i = 0; i < 3 - val.size(); i++) {
HarmonicHistoryData tem = new HarmonicHistoryData();
tem.setAValue(0f);
val.add(tem);
}
}
for (HarmonicHistoryData harmonicHistoryData : val) {
if (InfluxDBTableConstant.PHASE_TYPE_A.equalsIgnoreCase(harmonicHistoryData.getPhasicType())) {
BigDecimal a = BigDecimal.valueOf(harmonicHistoryData.getAValue()).setScale(4, RoundingMode.HALF_UP);
objects[1] = a;
maxI = max(maxI, a.floatValue());
minI = min(minI, a.floatValue());
} else if (InfluxDBTableConstant.PHASE_TYPE_B.equalsIgnoreCase(harmonicHistoryData.getPhasicType())) {
BigDecimal b = BigDecimal.valueOf(harmonicHistoryData.getAValue()).setScale(4, RoundingMode.HALF_UP);
objects[2] = b;
maxI = max(maxI, b.floatValue());
minI = min(minI, b.floatValue());
} else if (InfluxDBTableConstant.PHASE_TYPE_C.equalsIgnoreCase(harmonicHistoryData.getPhasicType())) {
BigDecimal c = BigDecimal.valueOf(harmonicHistoryData.getAValue()).setScale(4, RoundingMode.HALF_UP);
objects[3] = c;
maxI = max(maxI, c.floatValue());
minI = min(minI, c.floatValue());
}
}
List<Object> list = new ArrayList<>(Arrays.asList(objects));
objectListData.add(list);
}
historyDataResultVO.setMaxValue(maxI);
historyDataResultVO.setMinValue(minI);
historyDataResultVO.setTopLimit(queryResultLimitVO.getTopLimit());
historyDataResultVO.setLowerLimit(queryResultLimitVO.getLowerLimit());
historyDataResultVO.setValue(objectListData);
}
} else {
return historyDataResultVO;
}
return historyDataResultVO;
}
private Float max(Float ding, Float a) {
if (Objects.isNull(ding)) {
ding = a;
}
if (a > ding) {
ding = a;
}
return ding;
}
private Float min(Float ding, Float a) {
if (Objects.isNull(ding)) {
ding = a;
}
if (a < ding) {
ding = a;
}
return ding;
}
private QueryResultLimitVO getQueryResult(String startTime, String endTime, String lineId, String contion, Integer number, Integer valueType, Integer ptType) {
PqsDeviceUnit pqsDeviceUnit = new PqsDeviceUnit();
QueryResultLimitVO queryResultLimitVO = new QueryResultLimitVO();
if (!lineId.isEmpty()) {
Float topLimit = 0f;
Float lowerLimit = 0f;
//获取监测点信息
LineDevGetDTO lineDetailData = lineMapper.getMonitorDetail(lineId);
//获取限值
Overlimit overlimit = overlimitMapper.selectById(lineId);
//组装sql语句
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(InfluxDBTableConstant.TIME + " >= '").append(startTime).append("' and ").append(InfluxDBTableConstant.TIME).append(" <= '").append(endTime).append("' and (");
//sql语句
stringBuilder.append(InfluxDBTableConstant.LINE_ID + "='").append(lineId).append("')");
String valueTypeName = "";
switch (valueType) {
case 1:
valueTypeName = "AVG";
break;
case 2:
valueTypeName = "MIN";
break;
case 3:
valueTypeName = "MAX";
break;
case 4:
valueTypeName = "CP95";
break;
default:
break;
}
if (!Integer.valueOf(contion).equals(60) && !Integer.valueOf(contion).equals(61) && !Integer.valueOf(contion).equals(62)) {
stringBuilder.append(" and ").append(InfluxDBTableConstant.VALUE_TYPE + "='").append(valueTypeName).append("'");
}
String sql = "";
List<String> phasicType = new ArrayList<>();
List<String> unit = new ArrayList<>();
String targetName = "";
switch (Integer.parseInt(contion)) {
case 10:
//相电压有效值
sql = "SELECT time as time, rms as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add(pqsDeviceUnit.getPhaseVoltage());
targetName = "相电压有效值";
break;
case 11:
//线电压有效值
sql = "SELECT time as time, rms_lvr as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C')order by time asc tz('Asia/Shanghai');";
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
unit.add(pqsDeviceUnit.getLineVoltage());
targetName = "线电压有效值";
break;
case 12:
//电压偏差
sql = "SELECT time as time, vu_dev as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
topLimit = overlimit.getVoltageDev();
lowerLimit = overlimit.getUvoltageDev();
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
unit.add("%");
targetName = "电压偏差";
break;
case 13:
//三相电压不平衡度
sql = "SELECT time as time, v_unbalance as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
topLimit = overlimit.getUbalance();
phasicType.add("三相电压不平衡度");
unit.add("%");
targetName = "三相电压不平衡度";
break;
case 14:
//电压不平衡
sql = "SELECT time as time, v_zero as aValue, v_pos as bValue, v_neg as cValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='T') order by time asc tz('Asia/Shanghai');";
phasicType.add("零序电压");
phasicType.add("正序电压");
phasicType.add("负序电压");
unit.add(pqsDeviceUnit.getNoPositiveV());
unit.add(pqsDeviceUnit.getPositiveV());
unit.add(pqsDeviceUnit.getNoPositiveV());
targetName = "电压不平衡";
break;
case 15:
//电压总谐波畸变率
sql = "SELECT time as time, v_thd as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
topLimit = overlimit.getUaberrance();
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
unit.add("%");
targetName = "电压总谐波畸变率";
break;
case 20:
//电流有效值
sql = "SELECT time as time, rms as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add("A");
targetName = "电流有效值";
break;
case 21:
//电流总畸变率
sql = "SELECT time as time, i_thd as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add("%");
targetName = "电流总谐波畸变率";
break;
case 22:
//负序电流
sql = "SELECT time as time, i_neg as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_i WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
topLimit = overlimit.getINeg();
phasicType.add("负序电流");
unit.add("A");
targetName = "负序电流";
break;
case 30:
//频率 V9暂时代表Freq
sql = "SELECT time as time, freq as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
topLimit = 50 + overlimit.getFreqDev();
lowerLimit = 50 - overlimit.getFreqDev();
phasicType.add("频率");
unit.add("Hz");
targetName = "频率";
break;
case 40:
//谐波电压含有率
if (number == 1) {
targetName = "基波电压幅值";
//修改幅值表
sql = "SELECT time as time, v_1 as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
unit.add(pqsDeviceUnit.getPhaseVoltage());
} else {
targetName = "谐波电压含有率";
sql = "SELECT time as time, v_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmrate_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
if (number < 26) {
topLimit = PubUtils.getValueByMethod(overlimit, "getUharm", number);
}
unit.add("%");
}
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
break;
case 41:
//谐波电流含有率
if (number == 1) {
targetName = "谐波电流幅值";
sql = "SELECT time as time, i_1 as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmrate_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
unit.add("A");
} else {
targetName = "谐波电流含有率";
sql = "SELECT time as time, i_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmrate_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
unit.add("%");
}
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
break;
case 42:
//谐波电压幅值
if (number == 1) {
sql = "SELECT time as time, v_1 as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
} else {
sql = "SELECT time as time, v_" + number + "*1000 as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
}
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
if (number == 1) {
unit.add(pqsDeviceUnit.getVfundEffective());
targetName = "基波电压幅值";
} else {
unit.add("V");
targetName = "谐波电压幅值";
}
break;
case 43:
//谐波电流幅值
if (number == 1) {
sql = "SELECT time as time, i_1 as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
targetName = "基波电流幅值";
} else {
sql = "SELECT time as time, i_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
if (number < 26) {
topLimit = PubUtils.getValueByMethod(overlimit, "getIharm", number);
}
targetName = "谐波电流幅值";
}
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add("A");
break;
case 44:
//谐波电压相角
if (number == 1) {
targetName = "基波电压相角";
sql = "SELECT time as time, v_1 as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmphasic_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
} else {
targetName = "谐波电压相角";
sql = "SELECT time as time, v_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmphasic_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
}
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
unit.add("°");
break;
case 45:
//谐波电流相角
if (number == 1) {
sql = "SELECT time as time, i_1 as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmphasic_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
targetName = "基波电流相角";
} else {
sql = "SELECT time as time, i_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmphasic_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
targetName = "谐波电流相角";
}
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add("°");
break;
case 46:
//间谐波电压含有率
sql = "SELECT time as time, v_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_inharm_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
if(number<17){
topLimit = PubUtils.getValueByMethod(overlimit, "getInuharm", number);
}else {
topLimit = 0.0f;
}
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
unit.add("%");
targetName = "间谐波电压含有率";
break;
case 47:
//间谐波电流含有率
sql = "SELECT time as time, i_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_inharm_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add("%");
targetName = "间谐波电流含有率";
break;
case 48:
//间谐波电压幅值
sql = "SELECT time as time, v_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_inharm_v WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
targetName = "间谐波电压幅值";
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
unit.add(pqsDeviceUnit.getPhaseVoltage());
break;
case 49:
//间谐波电流幅值
sql = "SELECT time as time, i_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_inharm_i WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add("A");
targetName = "间谐波电流幅值";
break;
case 50:
//谐波有功功率
sql = "SELECT time as time, p_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_p WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
if (number == 1) {
unit.add(pqsDeviceUnit.getFundActiveP());
} else {
unit.add("W");
}
targetName = "谐波有功功率";
break;
case 51:
//谐波无功功率
sql = "SELECT time as time, q_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_q WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
if (number == 1) {
unit.add(pqsDeviceUnit.getTotalNoP());
} else {
unit.add("Var");
}
targetName = "谐波无功功率";
break;
case 52:
//谐波视在功率
sql = "SELECT time as time, s_" + number + " as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_s WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
if (number == 1) {
unit.add(pqsDeviceUnit.getTotalViewP());
} else {
unit.add("VA");
}
targetName = "谐波视在功率";
break;
case 53:
//三相有功功率
sql = "SELECT time as time, p as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_p WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add(pqsDeviceUnit.getTotalActiveP());
targetName = "三相有功功率";
break;
case 54:
//三相无功功率
sql = "SELECT time as time, q as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_q WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add(pqsDeviceUnit.getTotalNoP());
targetName = "三相无功功率";
break;
case 55:
//三相视在功率
sql = "SELECT time as time, s as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_s WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
unit.add(pqsDeviceUnit.getTotalViewP());
targetName = "三相视在功率";
break;
case 56:
//三相总有功功率
sql = "SELECT time as time, p as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_p WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
phasicType.add("三相总有功功率");
unit.add(pqsDeviceUnit.getTotalActiveP());
targetName = "三相总有功功率";
break;
case 57:
//三相总无功功率
sql = "SELECT time as time, q as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_q WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
phasicType.add("三相总无功功率");
unit.add(pqsDeviceUnit.getTotalNoP());
targetName = "三相总无功功率";
break;
case 58:
//三相总视在功率
sql = "SELECT time as time, s as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_s WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
phasicType.add("三相总视在功率");
unit.add(pqsDeviceUnit.getTotalViewP());
targetName = "三相总视在功率";
break;
case 59:
//视在功率因数
sql = "SELECT time as time, pf as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_p WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') group by phasic_type order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
targetName = "视在功率因数";
break;
case 591:
//位移功率因数
sql = "SELECT time as time, df as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_p WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
targetName = "位移功率因数";
break;
case 592:
//总视在功率因数
sql = "SELECT time as time, pf as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_p WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
phasicType.add("总视在功率因数");
targetName = "总视在功率因数";
break;
case 593:
//总位移功率因数
sql = "SELECT time as time, df as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_harmpower_p WHERE " + stringBuilder +
" and phasic_type ='T' order by time asc tz('Asia/Shanghai');";
phasicType.add("总位移功率因数");
targetName = "总位移功率因数";
break;
case 61:
//长时闪变
sql = "SELECT time as time, plt as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_plt WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
topLimit = overlimit.getFlicker();
targetName = "长时闪变";
break;
case 60:
//短时闪变
sql = "SELECT time as time, pst as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_flicker WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
topLimit = overlimit.getFlicker();
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
targetName = "短时闪变";
break;
case 62:
//电压波动
sql = "SELECT time as time, fluc as aValue ," + InfluxDBTableConstant.PHASIC_TYPE + " FROM data_fluc WHERE " + stringBuilder +
" and (phasic_type ='A' or phasic_type ='B' or phasic_type ='C') order by time asc tz('Asia/Shanghai');";
if (ptType == 0) {
phasicType.add("A相");
phasicType.add("B相");
phasicType.add("C相");
} else {
phasicType.add("AB相");
phasicType.add("BC相");
phasicType.add("CA相");
}
targetName = "电压波动";
unit.add("%");
break;
default:
break;
}
//大致有3种类型
//1、一次查询返回3条记录分别为A/B/C三相的结果
//2、一次查询返回一条记录以T相为条件返回某3个指标值
//3、一次查询返回一条记录以T相为条件返回某1个指标值
List<HarmonicHistoryData> harmonicHistoryData = commonMapper.getHistoryResult(sql);
queryResultLimitVO.setHarmonicHistoryDataList(harmonicHistoryData);
queryResultLimitVO.setTopLimit(topLimit);
queryResultLimitVO.setLowerLimit(lowerLimit);
queryResultLimitVO.setPhaiscType(phasicType);
queryResultLimitVO.setUnit(unit);
queryResultLimitVO.setLineName(lineDetailData.getPointName());
queryResultLimitVO.setHarmNum(number);
queryResultLimitVO.setTargetName(targetName);
queryResultLimitVO.setWiringMethod(lineDetailData.getWiringMethod());
} else {
return queryResultLimitVO;
}
return queryResultLimitVO;
}
}

View File

@@ -0,0 +1,382 @@
package com.njcn.product.advance.responsility.analysis;
import com.njcn.product.advance.responsility.pojo.constant.HarmonicConstants;
import com.njcn.product.advance.responsility.utils.MathUtils;
import org.apache.commons.math3.linear.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 典则相关分析类
* 实现典则相关系数的计算
*
* @author hongawen
* @version 1.0
*/
public class CanonicalCorrelationAnalysis {
private static final Logger logger = LoggerFactory.getLogger(CanonicalCorrelationAnalysis.class);
/**
* 计算典则相关系数
* 对应C代码中的TransCancor函数
*
* @param powerData 功率数据矩阵 [时间][节点]
* @param harmonicData 谐波数据向量
* @param windowSize 窗口大小
* @param nodeCount 节点数量
* @return 典则相关系数
*/
public static float computeCanonicalCorrelation(float[][] powerData, float[] harmonicData,
int windowSize, int nodeCount) {
logger.info("===== 开始典型相关分析 =====");
logger.info("输入参数: windowSize={}, nodeCount={}", windowSize, nodeCount);
try {
// 提取窗口数据
double[][] x = new double[windowSize][nodeCount];
double[] y = new double[windowSize];
// ===== 数据质量统计(只统计,不影响计算) =====
int nanCountPower = 0, infiniteCountPower = 0, zeroCountPower = 0;
int nanCountHarmonic = 0, infiniteCountHarmonic = 0, zeroCountHarmonic = 0;
double powerSum = 0, harmonicSum = 0;
double powerMin = Double.MAX_VALUE, powerMax = -Double.MAX_VALUE;
double harmonicMin = Double.MAX_VALUE, harmonicMax = -Double.MAX_VALUE;
for (int i = 0; i < windowSize; i++) {
for (int j = 0; j < nodeCount; j++) {
float val = powerData[i][j];
x[i][j] = val;
// 仅统计,不改变原逻辑
if (Float.isNaN(val)) {
nanCountPower++;
} else if (Float.isInfinite(val)) {
infiniteCountPower++;
} else if (val == 0.0f) {
zeroCountPower++;
} else {
powerSum += val;
powerMin = Math.min(powerMin, val);
powerMax = Math.max(powerMax, val);
}
}
float harmonicVal = harmonicData[i];
y[i] = harmonicVal;
// 仅统计,不改变原逻辑
if (Float.isNaN(harmonicVal)) {
nanCountHarmonic++;
} else if (Float.isInfinite(harmonicVal)) {
infiniteCountHarmonic++;
} else if (harmonicVal == 0.0f) {
zeroCountHarmonic++;
} else {
harmonicSum += harmonicVal;
harmonicMin = Math.min(harmonicMin, harmonicVal);
harmonicMax = Math.max(harmonicMax, harmonicVal);
}
}
// ===== 数据质量报告(只记录日志) =====
int totalPowerCount = windowSize * nodeCount;
int totalHarmonicCount = windowSize;
logger.info("功率数据质量分析:");
logger.info(" 总数据点: {}", totalPowerCount);
logger.info(" NaN数量: {} ({:.2f}%)", nanCountPower, nanCountPower * 100.0 / totalPowerCount);
logger.info(" 无穷大数量: {} ({:.2f}%)", infiniteCountPower, infiniteCountPower * 100.0 / totalPowerCount);
logger.info(" 零值数量: {} ({:.2f}%)", zeroCountPower, zeroCountPower * 100.0 / totalPowerCount);
logger.info(" 有效数据范围: [{}, {}]", powerMin == Double.MAX_VALUE ? "N/A" : powerMin,
powerMax == -Double.MAX_VALUE ? "N/A" : powerMax);
logger.info("谐波数据质量分析:");
logger.info(" 总数据点: {}", totalHarmonicCount);
logger.info(" NaN数量: {} ({:.2f}%)", nanCountHarmonic, nanCountHarmonic * 100.0 / totalHarmonicCount);
logger.info(" 无穷大数量: {} ({:.2f}%)", infiniteCountHarmonic, infiniteCountHarmonic * 100.0 / totalHarmonicCount);
logger.info(" 零值数量: {} ({:.2f}%)", zeroCountHarmonic, zeroCountHarmonic * 100.0 / totalHarmonicCount);
logger.info(" 有效数据范围: [{}, {}]", harmonicMin == Double.MAX_VALUE ? "N/A" : harmonicMin,
harmonicMax == -Double.MAX_VALUE ? "N/A" : harmonicMax);
// 只记录警告,不停止计算
if (nanCountPower > 0 || infiniteCountPower > 0) {
logger.warn("功率数据包含异常值NaN: {}, Infinite: {}", nanCountPower, infiniteCountPower);
}
if (nanCountHarmonic > 0 || infiniteCountHarmonic > 0) {
logger.warn("谐波数据包含异常值NaN: {}, Infinite: {}", nanCountHarmonic, infiniteCountHarmonic);
}
// 计算协方差矩阵 SXX
logger.info("===== 开始协方差计算 =====");
double[][] sxxMatrix = MathUtils.covarianceMatrix(x, windowSize, nodeCount);
// ===== SXX矩阵诊断只记录日志=====
double sxxTrace = 0;
double sxxFrobeniusNorm = 0;
boolean sxxHasNaN = false, sxxHasInfinite = false;
for (int i = 0; i < nodeCount; i++) {
sxxTrace += sxxMatrix[i][i];
for (int j = 0; j < nodeCount; j++) {
double val = sxxMatrix[i][j];
if (Double.isNaN(val)) {
sxxHasNaN = true;
}
if (Double.isInfinite(val)) {
sxxHasInfinite = true;
}
sxxFrobeniusNorm += val * val;
}
}
sxxFrobeniusNorm = Math.sqrt(sxxFrobeniusNorm);
logger.info("SXX矩阵诊断:");
logger.info(" 维度: {}x{}", nodeCount, nodeCount);
logger.info(" 迹(trace): {}", sxxTrace);
logger.info(" Frobenius范数: {}", sxxFrobeniusNorm);
logger.info(" 包含NaN: {}", sxxHasNaN);
logger.info(" 包含无穷大: {}", sxxHasInfinite);
logger.info(" 对角线元素: {}", java.util.Arrays.toString(
java.util.stream.IntStream.range(0, nodeCount)
.mapToDouble(i -> sxxMatrix[i][i])
.toArray()));
// 计算协方差 SYY
double syyMatrix = MathUtils.covariance(y, y, windowSize);
logger.info("SYY协方差: {}", syyMatrix);
if (Math.abs(syyMatrix) < HarmonicConstants.MIN_COVARIANCE) {
logger.warn("SYY过小 ({}), 调整为最小值: {}", syyMatrix, HarmonicConstants.MIN_COVARIANCE);
syyMatrix = HarmonicConstants.MIN_COVARIANCE;
}
// 计算协方差向量 SXY
double[] sxyVector = MathUtils.covarianceVector(x, y, windowSize, nodeCount);
// ===== SXY向量诊断只记录日志=====
double sxyNorm = 0;
boolean sxyHasNaN = false, sxyHasInfinite = false;
for (double val : sxyVector) {
if (Double.isNaN(val)) {
sxyHasNaN = true;
}
if (Double.isInfinite(val)) {
sxyHasInfinite = true;
}
sxyNorm += val * val;
}
sxyNorm = Math.sqrt(sxyNorm);
logger.info("SXY向量诊断:");
logger.info(" 长度: {}", sxyVector.length);
logger.info(" L2范数: {}", sxyNorm);
logger.info(" 包含NaN: {}", sxyHasNaN);
logger.info(" 包含无穷大: {}", sxyHasInfinite);
logger.info(" 向量值: {}", java.util.Arrays.toString(sxyVector));
// 使用Apache Commons Math进行矩阵运算
logger.info("===== 开始矩阵分解 =====");
RealMatrix sxx = new Array2DRowRealMatrix(sxxMatrix);
RealVector sxy = new ArrayRealVector(sxyVector);
// 计算 SXX^(-1)
logger.info("准备计算SXX逆矩阵...");
DecompositionSolver solver = new LUDecomposition(sxx).getSolver();
RealMatrix invSxx;
// 检查矩阵奇异性
double sxxDet = new LUDecomposition(sxx).getDeterminant();
logger.info("SXX矩阵行列式: {}", sxxDet);
if (Math.abs(sxxDet) < 1e-15) {
logger.warn("SXX矩阵几乎奇异 (det={})", sxxDet);
}
if (!solver.isNonSingular()) {
// 如果矩阵奇异,使用伪逆
logger.warn("SXX matrix is singular, using pseudo-inverse");
try {
SingularValueDecomposition svd = new SingularValueDecomposition(sxx);
invSxx = svd.getSolver().getInverse();
} catch (Exception svdException) {
logger.error("SVD pseudo-inverse failed, using regularized inverse", svdException);
// 添加正则化项
RealMatrix identity = MatrixUtils.createRealIdentityMatrix(sxx.getRowDimension());
RealMatrix regularized = sxx.add(identity.scalarMultiply(1e-10));
invSxx = new LUDecomposition(regularized).getSolver().getInverse();
}
} else {
invSxx = solver.getInverse();
}
// 计算 U = SXX^(-1) * SXY * (1/SYY) * SXY'
RealVector temp = invSxx.operate(sxy);
double scale = 1.0 / syyMatrix;
RealMatrix uMatrix = temp.outerProduct(sxy).scalarMultiply(scale);
// 计算特征值 - 添加数值稳定性处理
double maxEigenvalue = 0.0;
try {
// 首先检查矩阵是否有效
double[][] uMatrixData = uMatrix.getData();
boolean hasNaN = false;
boolean hasInfinite = false;
for (int i = 0; i < uMatrixData.length; i++) {
for (int j = 0; j < uMatrixData[i].length; j++) {
if (Double.isNaN(uMatrixData[i][j])) {
hasNaN = true;
}
if (Double.isInfinite(uMatrixData[i][j])) {
hasInfinite = true;
}
}
}
if (hasNaN || hasInfinite) {
logger.warn("U matrix contains NaN or Infinite values, returning 0");
return 0.0f;
}
// 检查矩阵条件数
SingularValueDecomposition svdCheck = new SingularValueDecomposition(uMatrix);
double conditionNumber = svdCheck.getConditionNumber();
if (conditionNumber > 1e12) {
logger.warn("U matrix is ill-conditioned (condition number: {}), using SVD approach", conditionNumber);
// 使用SVD方法获取最大奇异值的平方作为最大特征值
double[] singularValues = svdCheck.getSingularValues();
if (singularValues.length > 0) {
maxEigenvalue = singularValues[0] * singularValues[0];
}
} else {
// 正常的特征值分解
EigenDecomposition eigenDecomposition = new EigenDecomposition(uMatrix);
double[] eigenvalues = eigenDecomposition.getRealEigenvalues();
// 找最大特征值
for (double eigenvalue : eigenvalues) {
maxEigenvalue = Math.max(maxEigenvalue, Math.abs(eigenvalue));
}
}
} catch (Exception eigenException) {
logger.warn("EigenDecomposition failed, trying alternative approach: {}", eigenException.getMessage());
// 备用方案使用SVD方法
try {
SingularValueDecomposition svd = new SingularValueDecomposition(uMatrix);
double[] singularValues = svd.getSingularValues();
if (singularValues.length > 0) {
maxEigenvalue = singularValues[0] * singularValues[0];
}
} catch (Exception svdException) {
logger.error("Both EigenDecomposition and SVD failed, returning 0", svdException);
return 0.0f;
}
}
// 典则相关系数是最大特征值的平方根
double canonicalCorr = Math.sqrt(Math.abs(maxEigenvalue));
// 限制在[0,1]范围内
if (canonicalCorr > 1.0) {
canonicalCorr = 1.0;
}
logger.info("===== 典型相关分析计算完成 =====");
logger.info("最大特征值: {}", maxEigenvalue);
logger.info("典型相关系数: {}", canonicalCorr);
logger.info("是否被截断到1.0: {}", canonicalCorr >= 1.0);
return (float) canonicalCorr;
} catch (Exception e) {
logger.error("Error computing canonical correlation", e);
logger.error("异常详情: {}", e.getMessage());
logger.error("异常类型: {}", e.getClass().getSimpleName());
return 0.0f;
}
}
/**
* 滑动窗口计算典则相关系数序列
* 对应C代码中的SlideCanCor函数
*
* @param powerData 功率数据矩阵 [时间][节点]
* @param harmonicData 谐波数据向量
* @param windowSize 窗口大小
* @param nodeCount 节点数量
* @param dataLength 数据总长度
* @return 典则相关系数序列
*/
public static float[] slidingCanonicalCorrelation(float[][] powerData, float[] harmonicData,
int windowSize, int nodeCount, int dataLength) {
int slideLength = dataLength - windowSize;
if (slideLength <= 0) {
throw new IllegalArgumentException("Data length must be greater than window size");
}
float[] slideCanCor = new float[slideLength];
logger.info("Starting sliding canonical correlation analysis, slide length: {}", slideLength);
for (int i = 0; i < slideLength; i++) {
// 提取窗口数据
float[][] windowPower = new float[windowSize][nodeCount];
float[] windowHarmonic = new float[windowSize];
for (int j = 0; j < windowSize; j++) {
System.arraycopy(powerData[i + j], 0, windowPower[j], 0, nodeCount);
windowHarmonic[j] = harmonicData[i + j];
}
// 计算当前窗口的典则相关系数
slideCanCor[i] = computeCanonicalCorrelation(windowPower, windowHarmonic,
windowSize, nodeCount);
if (i % 10 == 0) {
logger.debug("Processed window {}/{}", i, slideLength);
}
}
logger.info("Sliding canonical correlation analysis completed");
return slideCanCor;
}
/**
* 计算包含/不包含背景的动态相关系数
* 对应C代码中的SlideCor函数
*
* @param powerData 功率数据(单个节点)
* @param harmonicData 谐波数据
* @param slideCanCor 滑动典则相关系数
* @param windowSize 窗口大小
* @return 动态相关系数序列
*/
public static float[] slidingCorrelation(float[] powerData, float[] harmonicData,
float[] slideCanCor, int windowSize) {
int slideLength = slideCanCor.length;
float[] slideCor = new float[slideLength];
for (int i = 0; i < slideLength; i++) {
float[] tempPower = new float[windowSize];
float[] tempHarmonic = new float[windowSize];
for (int j = 0; j < windowSize; j++) {
tempPower[j] = powerData[i + j];
tempHarmonic[j] = harmonicData[i + j] * slideCanCor[i];
}
slideCor[i] = MathUtils.pearsonCorrelation(tempHarmonic, tempPower, windowSize);
}
return slideCor;
}
}

View File

@@ -0,0 +1,425 @@
package com.njcn.product.advance.responsility.calculator;
import com.njcn.product.advance.responsility.analysis.CanonicalCorrelationAnalysis;
import com.njcn.product.advance.responsility.model.HarmonicData;
import com.njcn.product.advance.responsility.pojo.constant.CalculationMode;
import com.njcn.product.advance.responsility.pojo.constant.CalculationStatus;
import com.njcn.product.advance.responsility.pojo.constant.HarmonicConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 谐波责任计算主引擎
* 严格对应C代码中的harm_res系列函数
*
* @author hongawen
* @version 2.0 - 修复版本严格对照C代码实现
*/
public class HarmonicCalculationEngine {
private static final Logger logger = LoggerFactory.getLogger(HarmonicCalculationEngine.class);
// 对应C代码中的全局变量
private int P; // 节点数 p_node
private int TL; // 功率数据长度 p_num
private int LL; // 谐波数据长度 harm_num
private int JIANGE; // 数据间隔比例
private int width; // 窗口大小
private float XIANE; // 谐波门槛
/**
* 主计算入口
* 对应C代码中的harm_res函数
*
* @param data 谐波数据对象
* @return 计算是否成功
*/
public boolean calculate(HarmonicData data) {
logger.info("Starting harmonic calculation, mode: {}", data.getCalculationMode());
try {
if (data.getCalculationMode() == CalculationMode.FULL_CALCULATION) {
return fullCalculation(data);
} else {
return partialCalculation(data);
}
} catch (Exception e) {
logger.error("Calculation failed with exception: " + e.getMessage(), e);
e.printStackTrace();
data.setCalculationStatus(CalculationStatus.FAILED);
return false;
}
}
/**
* 完整计算模式
* 严格对应C代码中的harm_res_all函数
*
* @param data 谐波数据对象
* @return 计算是否成功
*/
private boolean fullCalculation(HarmonicData data) {
logger.info("Executing full calculation mode");
// 1. 数据初始化 - 对应 data_init_all()
if (!initializeFullCalculationData(data)) {
logger.error("Data initialization failed");
data.setCalculationStatus(CalculationStatus.FAILED);
return false;
}
// 2. 创建工作数组 - 对应C代码行536-540
float[][] a = new float[TL][P]; // 功率数据副本
float[] b = new float[LL]; // 谐波数据副本
float[] u = new float[TL]; // 对齐后的谐波数据
// 3. 复制数据 - 对应C代码行542-552
for (int i = 0; i < TL; i++) {
for (int j = 0; j < P; j++) {
a[i][j] = data.getPowerData()[i][j];
}
}
for (int i = 0; i < LL; i++) {
b[i] = data.getHarmonicData()[i];
}
// 4. 数据对齐处理 - 严格对应C代码行554-562
// 注意C代码是原地修改数组b
for (int i = 0; i < LL; i += JIANGE) {
float tempt = 0.0f;
for (int j = 0; j < JIANGE; j++) {
tempt += b[i + j];
}
b[i] = tempt / JIANGE; // 覆盖原位置
}
// 5. 构建Udata - 严格对应C代码行570-580
// 注意:使用 i*JIANGE 索引
for (int i = 0; i < TL; i++) {
u[i] = b[i * JIANGE]; // 关键:使用 i*JIANGE 索引
}
int slcorlength = TL - width;
// 6. 计算滑动典则相关系数 - 对应C代码行584
logger.info("Computing sliding canonical correlation");
float[] cancorrelation = CanonicalCorrelationAnalysis.slidingCanonicalCorrelation(
a, u, width, P, TL
);
// 7. 保存典则相关系数 - 对应C代码行592-601
float[] Core = new float[slcorlength];
float[] BjCore = new float[slcorlength];
for (int i = 0; i < slcorlength; i++) {
Core[i] = cancorrelation[i];
BjCore[i] = 1 - cancorrelation[i];
}
data.setCanonicalCorrelation(Core);
data.setBackgroundCanonicalCorr(BjCore);
// 8. 计算动态相关系数矩阵 - 对应C代码行605-635
logger.info("Computing correlation matrix");
float[][] simCor = new float[slcorlength][P];
// 对应C代码行618-632对每个节点计算动态相关系数
for (int i = 0; i < P; i++) {
// 提取第i个节点的功率数据
float[] xe = new float[TL];
for (int m = 0; m < TL; m++) {
xe[m] = a[m][i]; // 对应 Pdata.block(0, i, TL, 1)
}
// 计算该节点的滑动相关系数
float[] slidecor = CanonicalCorrelationAnalysis.slidingCorrelation(
xe, u, cancorrelation, width
);
// 存储结果
for (int j = 0; j < slcorlength; j++) {
simCor[j][i] = slidecor[j];
}
}
data.setCorrelationData(simCor);
// 9. 计算EK值 - 对应C代码行642-654
logger.info("Computing EK values");
float[][] EKdata = ResponsibilityCalculator.computeEK(
simCor, a, width, P, TL
);
// 10. 计算FK值 - 对应C代码行660-673
logger.info("Computing FK values");
float[][] FKdata = ResponsibilityCalculator.computeFK(
EKdata, width, P, TL
);
data.setFkData(FKdata);
// 11. 计算HK值 - 对应C代码行678-691
logger.info("Computing HK values");
float[][] HKdata = ResponsibilityCalculator.computeHK(
BjCore, EKdata, width, P, TL
);
data.setHkData(HKdata);
// 12. 设置结果数量 - 对应C代码行693
data.setResponsibilityDataCount(slcorlength);
// 13. 统计超限时段的责任 - 对应C代码行696-724
logger.info("Computing responsibility sums");
// 重要修正C代码的SumHK函数中虽然Udata长度是TL但是循环只遍历前slg(=TL-width)个元素
// 所以我们需要传入完整的u数组长度TL让sumResponsibility内部处理
// 对应C代码VectorXd Udata(TL); 以及 SumHK函数调用
// 统计HK责任包含背景- 对应C代码行698-710
// 注意传入完整的u数组TL长度而不是截取的数组
float[] sumHK = ResponsibilityCalculator.sumResponsibility(
HKdata, u, XIANE, width, P + 1, TL
);
data.setSumHKData(sumHK);
// 统计FK责任不包含背景- 对应C代码行712-724
// 同样传入完整的u数组和TL参数
float[] sumFK = ResponsibilityCalculator.sumResponsibility(
FKdata, u, XIANE, width, P, TL
);
data.setSumFKData(sumFK);
// 14. 标记计算成功 - 对应C代码行739
data.setCalculationStatus(CalculationStatus.CALCULATED);
logger.info("Full calculation completed successfully");
return true;
}
/**
* 初始化完整计算数据
* 对应C代码中的data_init_all函数
*/
private boolean initializeFullCalculationData(HarmonicData data) {
// 设置全局变量 - 对应C代码行478-483
P = data.getPowerNodeCount();
TL = data.getPowerCount();
LL = data.getHarmonicCount();
// 对应C代码第481行JIANGE = pq_buf.harm_num/pq_buf.p_num;
// 重要修正JIANGE应该是 谐波数量/功率点数,不是谐波数量/节点数
JIANGE = LL / TL; // 这个是正确的harm_num / p_num (其中p_num是功率点数)
width = data.getWindowSize();
XIANE = data.getHarmonicThreshold();
// 验证数据 - 对应C代码行485-504
if (JIANGE * TL != LL || JIANGE < 1) {
logger.error("Data length mismatch: JIANGE({}) * TL({}) != LL({})",
JIANGE, TL, LL);
return false;
}
if (width < HarmonicConstants.MIN_WIN_LEN || width > HarmonicConstants.MAX_WIN_LEN) {
logger.error("Invalid window size: {}", width);
return false;
}
if (TL < 2 * width) {
logger.error("Power data length {} is too short for window size {}", TL, width);
return false;
}
if (P > HarmonicConstants.MAX_P_NODE || TL > HarmonicConstants.MAX_P_NUM ||
LL > HarmonicConstants.MAX_HARM_NUM) {
logger.error("Data size exceeds limits");
return false;
}
return true;
}
/**
* 部分重算模式
* 对应C代码中的harm_res_part函数
*
* @param data 谐波数据对象
* @return 计算是否成功
*/
private boolean partialCalculation(HarmonicData data) {
logger.info("Executing partial calculation mode");
// 1. 数据初始化 - 对应 data_init_part()
if (!initializePartialCalculationData(data)) {
logger.error("Data initialization failed for partial calculation");
data.setCalculationStatus(CalculationStatus.FAILED);
return false;
}
// 2. 准备Udata - 对应C代码行816-818
// C代码VectorXd Udata(TL); 并从pq_buf.harm_data复制TL个元素
int res_num = data.getResponsibilityDataCount();
// 验证责任数据行数
if (res_num != TL - width) {
logger.warn("责任数据行数({})与期望值(TL-width={})不匹配", res_num, TL - width);
res_num = TL - width; // 使用正确的值
}
// 重要修正与C代码保持一致Udata长度应该是TL而不是res_num
// C代码VectorXd Udata(TL);
float[] Udata = new float[TL];
// 从harm_data复制TL个元素到Udata
// C代码for (int j = 0; j < TL; j++) Udata[j] = pq_buf.harm_data[j];
if (data.getHarmonicData().length < TL) {
logger.warn("谐波数据长度({})小于TL({}), 将补零",
data.getHarmonicData().length, TL);
System.arraycopy(data.getHarmonicData(), 0, Udata, 0, data.getHarmonicData().length);
// 剩余部分自动补零
} else {
System.arraycopy(data.getHarmonicData(), 0, Udata, 0, TL);
}
logger.debug("准备Udata完成: 长度={} (对应C代码TL), 责任数据行数={}", Udata.length, res_num);
// 3. 统计HK责任 - 对应C代码行806-830
logger.info("Recalculating HK responsibility sums");
// 对应C代码第808-814行创建新的HKdata矩阵只包含RES_NUM行
// C代码MatrixXd HKdata(RES_NUM, (P + 1));
// 添加数据验证
if (data.getHkData() == null || data.getHkData().length == 0) {
logger.error("HK数据为空或长度为0");
data.setCalculationStatus(CalculationStatus.FAILED);
return false;
}
// 重要C代码创建了新的RES_NUM行的HKdata从原始数据复制前RES_NUM行
// 对应C代码第808-814行
float[][] HKdataForCalc = new float[res_num][P + 1];
int copyRows = Math.min(res_num, data.getHkData().length);
logger.debug("创建用于计算的HK数据矩阵: {}x{}, 从原始数据复制{}行",
res_num, P + 1, copyRows);
for (int i = 0; i < copyRows; i++) {
for (int j = 0; j < P + 1; j++) {
if (j < data.getHkData()[i].length) {
HKdataForCalc[i][j] = data.getHkData()[i][j];
}
}
}
logger.debug("调用HK sumResponsibility参数: HKdata[{}x{}], Udata[{}], TL={}, width={}",
HKdataForCalc.length, HKdataForCalc.length > 0 ? HKdataForCalc[0].length : 0,
Udata.length, TL, width);
try {
// 对应C代码第819行arrHKsum = SumHK(HKdata, Udata, wdith, colK, TL);
float[] sumHK = ResponsibilityCalculator.sumResponsibility(
HKdataForCalc, // 使用新创建的RES_NUM行的HK数据
Udata, // 长度为TL的数组
XIANE,
width,
P + 1,
TL // 传入TL参数
);
data.setSumHKData(sumHK);
logger.debug("HK责任计算完成结果长度: {}", sumHK != null ? sumHK.length : "null");
} catch (Exception e) {
logger.error("HK责任计算失败: " + e.getMessage(), e);
throw e;
}
// 4. 统计FK责任 - 对应C代码行839-851
logger.info("Recalculating FK responsibility sums");
// 对应C代码虽然没有显式创建新的FKdata但逻辑相同
// 添加数据验证
if (data.getFkData() == null || data.getFkData().length == 0) {
logger.error("FK数据为空或长度为0");
data.setCalculationStatus(CalculationStatus.FAILED);
return false;
}
// 创建用于计算的FK数据矩阵RES_NUM行
float[][] FKdataForCalc = new float[res_num][P];
int copyRowsFK = Math.min(res_num, data.getFkData().length);
logger.debug("创建用于计算的FK数据矩阵: {}x{}, 从原始数据复制{}行",
res_num, P, copyRowsFK);
for (int i = 0; i < copyRowsFK; i++) {
for (int j = 0; j < P; j++) {
if (j < data.getFkData()[i].length) {
FKdataForCalc[i][j] = data.getFkData()[i][j];
}
}
}
logger.debug("调用FK sumResponsibility参数: FKdata[{}x{}], Udata[{}], TL={}, width={}",
FKdataForCalc.length, FKdataForCalc.length > 0 ? FKdataForCalc[0].length : 0,
Udata.length, TL, width);
try {
// 对应C代码第840行arrHKsum = SumHK(FKdata, Udata, wdith, colK, TL);
float[] sumFK = ResponsibilityCalculator.sumResponsibility(
FKdataForCalc, // 使用新创建的RES_NUM行的FK数据
Udata, // 使用相同的Udata(长度TL)
XIANE,
width,
P,
TL // 传入TL参数
);
data.setSumFKData(sumFK);
logger.debug("FK责任计算完成结果长度: {}", sumFK != null ? sumFK.length : "null");
} catch (Exception e) {
logger.error("FK责任计算失败: " + e.getMessage(), e);
throw e;
}
// 5. 标记计算成功 - 对应C代码行858
data.setCalculationStatus(CalculationStatus.CALCULATED);
logger.info("Partial calculation completed successfully");
return true;
}
/**
* 初始化部分计算数据
* 对应C代码中的data_init_part函数
*/
private boolean initializePartialCalculationData(HarmonicData data) {
// 设置变量 - 对应C代码行762-766
int RES_NUM = data.getResponsibilityDataCount();
P = data.getPowerNodeCount();
TL = data.getWindowSize() + RES_NUM;
width = data.getWindowSize();
XIANE = data.getHarmonicThreshold();
// 验证数据 - 对应C代码行756-778
if ((RES_NUM + width) != data.getHarmonicCount()) {
logger.error("Data length mismatch: res_num({}) + win({}) != harm_num({})",
RES_NUM, width, data.getHarmonicCount());
return false;
}
if (width < HarmonicConstants.MIN_WIN_LEN || width > HarmonicConstants.MAX_WIN_LEN) {
logger.error("Invalid window size: {}", width);
return false;
}
if (P > HarmonicConstants.MAX_P_NODE || TL > HarmonicConstants.MAX_P_NUM) {
logger.error("Data size exceeds limits");
return false;
}
// 验证FK和HK数据存在
if (data.getFkData() == null || data.getHkData() == null) {
logger.error("FK or HK data is null");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,429 @@
package com.njcn.product.advance.responsility.calculator;
import com.njcn.product.advance.responsility.analysis.CanonicalCorrelationAnalysis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 责任指标计算类
* 计算谐波责任的各项指标
* 严格对应C代码实现
*
* @author hongawen
* @version 2.0 - 修复版本严格对照C代码实现
*/
public class ResponsibilityCalculator {
private static final Logger logger = LoggerFactory.getLogger(ResponsibilityCalculator.class);
/**
* 计算EK值动态责任指标
* 严格对应C代码中的DyEKCom函数行300-357
*
* @param correlationData 动态相关系数矩阵 [时间][节点]
* @param powerData 功率数据矩阵 [时间][节点]
* @param windowSize 窗口大小
* @param nodeCount 节点数量
* @param dataLength 数据长度
* @return EK值矩阵
*/
public static float[][] computeEK(float[][] correlationData, float[][] powerData,
int windowSize, int nodeCount, int dataLength) {
int slideLength = dataLength - windowSize;
float[][] ekData = new float[slideLength][nodeCount];
float[][] akData = new float[slideLength][nodeCount];
logger.info("Computing EK values, slide length: {}", slideLength);
// 计算AK值 - 对应C代码行307-319
for (int i = 0; i < slideLength; i++) {
float sumPower = 0;
// 计算功率总和 - 对应C代码行309-313
for (int j = 0; j < nodeCount; j++) {
sumPower += powerData[i][j]; // 注意这里用的是powerData[i][j]
}
// 计算AK值 - 对应C代码行314-318
for (int j = 0; j < nodeCount; j++) {
if (sumPower > 0) {
akData[i][j] = correlationData[i][j] * (powerData[i][j] / sumPower);
} else {
akData[i][j] = 0;
}
}
}
// 归一化处理得到EK值 - 对应C代码行320-342
for (int i = 0; i < slideLength; i++) {
// 重要C代码初始化为0而不是Float.MIN_VALUE/MAX_VALUE
// 对应C代码行322-323
float maxValue = 0;
float minValue = 0;
// 找最大最小值 - 对应C代码行322-334
for (int j = 0; j < nodeCount; j++) {
if (akData[i][j] > maxValue) {
maxValue = akData[i][j];
}
if (akData[i][j] < minValue) {
minValue = akData[i][j];
}
}
float range = maxValue - minValue;
// 归一化 - 对应C代码行338-341
for (int j = 0; j < nodeCount; j++) {
if (Math.abs(range) > 1e-10) {
ekData[i][j] = (akData[i][j] - minValue) / range;
} else {
ekData[i][j] = 0;
}
}
}
logger.info("EK computation completed");
return ekData;
}
/**
* 计算FK值不包含背景的责任指标
* 严格对应C代码中的DyFKCom函数行358-389
*
* @param ekData EK值矩阵
* @param windowSize 窗口大小
* @param nodeCount 节点数量
* @param dataLength 数据长度
* @return FK值矩阵
*/
public static float[][] computeFK(float[][] ekData, int windowSize,
int nodeCount, int dataLength) {
int slideLength = dataLength - windowSize;
float[][] fkData = new float[slideLength][nodeCount];
logger.info("Computing FK values");
// 对应C代码行364-376
for (int i = 0; i < slideLength; i++) {
float sumEK = 0;
// 计算EK总和 - 对应C代码行366-370
for (int j = 0; j < nodeCount; j++) {
sumEK += ekData[i][j];
}
// 计算FK值归一化- 对应C代码行372-375
for (int j = 0; j < nodeCount; j++) {
if (sumEK > 0) {
fkData[i][j] = ekData[i][j] / sumEK;
} else {
fkData[i][j] = 0;
}
}
}
logger.info("FK computation completed");
return fkData;
}
/**
* 计算HK值包含背景的责任指标
* 严格对应C代码中的DyHKCom函数行390-429
*
* @param backgroundCanCor 背景典则相关系数1-典则相关系数)
* @param ekData EK值矩阵
* @param windowSize 窗口大小
* @param nodeCount 节点数量
* @param dataLength 数据长度
* @return HK值矩阵
*/
public static float[][] computeHK(float[] backgroundCanCor, float[][] ekData,
int windowSize, int nodeCount, int dataLength) {
int slideLength = dataLength - windowSize;
float[][] hkData = new float[slideLength][nodeCount + 1];
float[][] newEK = new float[slideLength][nodeCount + 1];
logger.info("Computing HK values");
// 构建包含背景的EK矩阵 - 对应C代码行396-403
for (int i = 0; i < slideLength; i++) {
// 复制原有EK值
for (int j = 0; j < nodeCount; j++) {
newEK[i][j] = ekData[i][j];
}
// 添加背景值
newEK[i][nodeCount] = backgroundCanCor[i];
}
// 计算HK值 - 对应C代码行405-416
for (int i = 0; i < slideLength; i++) {
float sumEK = 0;
// 计算总和 - 对应C代码行407-411
for (int j = 0; j < nodeCount + 1; j++) {
sumEK += newEK[i][j];
}
// 归一化得到HK值 - 对应C代码行412-415
for (int j = 0; j < nodeCount + 1; j++) {
if (sumEK > 0) {
hkData[i][j] = newEK[i][j] / sumEK;
} else {
hkData[i][j] = 0;
}
}
}
logger.info("HK computation completed");
return hkData;
}
/**
* 计算超限时段的责任总和
* 严格对应C代码中的SumHK函数行431-461
*
* @param responsibilityData 责任数据矩阵FK或HK[时间][节点]
* @param harmonicData 谐波数据Udata - 长度为TL
* @param threshold 谐波门槛XIANE
* @param windowSize 窗口大小
* @param columnCount 列数(节点数或节点数+1
* @param tl_num 对应C代码的TL参数总数据点数
* @return 各节点的责任总和百分比
*/
public static float[] sumResponsibility(float[][] responsibilityData, float[] harmonicData,
float threshold, int windowSize, int columnCount, int tl_num) {
// 对应C代码int slg = tl_num - width;
int slideLength = tl_num - windowSize; // 使用传入的tl_num计算而不是从responsibilityData.length推断
float[] sumData = new float[columnCount]; // 对应C代码中的 arrHKsum
double[] HKSum = new double[columnCount]; // 对应C代码中的 VectorXd HKSum - 使用double精度
int exceedCount = 0; // 对应C代码中的 coutt
// ===== 添加详细调试日志 =====
logger.info("======= 开始 sumResponsibility 计算 =======");
logger.info("输入参数:");
logger.info(" threshold(阈值): {}", threshold);
logger.info(" windowSize(窗口大小): {}", windowSize);
logger.info(" columnCount(列数): {}", columnCount);
logger.info(" tl_num(总数据长度): {}", tl_num);
logger.info(" slideLength(滑动长度): {}", slideLength);
logger.info(" responsibilityData维度: {}x{}",
responsibilityData != null ? responsibilityData.length : "null",
responsibilityData != null && responsibilityData.length > 0 ? responsibilityData[0].length : "null");
logger.info(" harmonicData长度: {}", harmonicData != null ? harmonicData.length : "null");
// 数据验证
if (harmonicData == null) {
logger.error("错误: harmonicData为null!");
throw new NullPointerException("harmonicData不能为null");
}
if (responsibilityData == null) {
logger.error("错误: responsibilityData为null!");
throw new NullPointerException("responsibilityData不能为null");
}
// 关键验证:检查数组长度是否充足
// C代码中Udata长度是TL循环遍历slg=TL-width个元素
logger.info("数据验证: slideLength={}, harmonicData.length={}, responsibilityData.length={}",
slideLength, harmonicData.length, responsibilityData.length);
if (harmonicData.length < slideLength) {
logger.error("!!!谐波数据长度不足!!!");
logger.error("需要访问harmonicData[0]到harmonicData[{}], 但数组长度只有{}",
slideLength - 1, harmonicData.length);
throw new IllegalArgumentException(
String.format("谐波数据长度不足: 需要%d, 实际%d", slideLength, harmonicData.length));
}
if (responsibilityData.length < slideLength) {
logger.error("!!!责任数据行数不足!!!");
logger.error("需要访问responsibilityData[0]到responsibilityData[{}], 但数组长度只有{}",
slideLength - 1, responsibilityData.length);
throw new IllegalArgumentException(
String.format("责任数据行数不足: 需要%d, 实际%d", slideLength, responsibilityData.length));
}
// ===== 添加数据分布统计 =====
logger.info("谐波数据分析:");
float harmonicMin = Float.MAX_VALUE, harmonicMax = Float.MIN_VALUE;
double harmonicSum = 0;
int preliminaryExceedCount = 0;
for (int i = 0; i < Math.min(slideLength, harmonicData.length); i++) {
float val = harmonicData[i];
harmonicMin = Math.min(harmonicMin, val);
harmonicMax = Math.max(harmonicMax, val);
harmonicSum += val;
if (val > threshold) {
preliminaryExceedCount++;
}
}
double harmonicAvg = harmonicSum / Math.min(slideLength, harmonicData.length);
logger.info(" 谐波数据范围: [{}, {}]", harmonicMin, harmonicMax);
logger.info(" 谐波数据平均值: {}", harmonicAvg);
logger.info(" 设定阈值: {}", threshold);
logger.info(" 初步统计超限个数: {}/{} ({:.2f}%)",
preliminaryExceedCount, Math.min(slideLength, harmonicData.length),
preliminaryExceedCount * 100.0 / Math.min(slideLength, harmonicData.length));
// ===== 责任数据分析 =====
logger.info("责任数据分析(检查前5行的归一化情况):");
for (int i = 0; i < Math.min(5, responsibilityData.length); i++) {
float rowSum = 0;
for (int j = 0; j < responsibilityData[i].length; j++) {
rowSum += responsibilityData[i][j];
}
logger.info(" 第{}行和: {} (期望值: 1.0, 偏差: {})", i, rowSum, Math.abs(rowSum - 1.0f));
}
// 统计超限时段的责任 - 对应C代码行437-449
// 重要C代码中有一个设计缺陷coutt在每个j循环中被重置
// 但最后计算百分比时使用的是最后一次j循环的coutt值
// 为了严格保持一致,我们也要复现这个逻辑
logger.info("===== 开始循环计算每列的累加值 =====");
int[] exceedCountPerColumn = new int[columnCount]; // 记录每列的超限次数,用于调试
for (int j = 0; j < columnCount; j++) {
HKSum[j] = 0;
exceedCount = 0; // 对应C代码行440: coutt = 0;
logger.info("开始计算第{}列 (共{}列)", j, columnCount);
double columnSum = 0; // 用于调试
int columnExceedCount = 0; // 用于调试
for (int i = 0; i < slideLength; i++) {
// 添加越界检查
if (i >= harmonicData.length) {
logger.error("!!!数组越界!!! 尝试访问harmonicData[{}], 但数组长度只有{}",
i, harmonicData.length);
logger.error("发生在: j={}, i={}", j, i);
throw new ArrayIndexOutOfBoundsException(
String.format("访问harmonicData[%d]越界, 数组长度=%d", i, harmonicData.length));
}
// 对应C代码行443-447
if (harmonicData[i] > threshold) {
double currentResponsibility = responsibilityData[i][j];
HKSum[j] += currentResponsibility; // 对应C代码行445
exceedCount++; // 对应C代码行446
columnSum += currentResponsibility;
columnExceedCount++;
// 只打印前几个超限情况的详细信息
if (columnExceedCount <= 3) {
logger.info(" 时刻i={}: 谐波值={} > 阈值={}, 责任值={}, 累加到{}",
i, harmonicData[i], threshold, currentResponsibility, HKSum[j]);
}
}
}
exceedCountPerColumn[j] = columnExceedCount;
logger.info("第{}列计算完成: 累加值={}, 超限次数={}", j, HKSum[j], columnExceedCount);
}
// 注意这里exceedCount保留的是最后一列j=columnCount-1的超限次数
// 这与C代码的行为一致
logger.info("===== 循环计算完成 =====");
logger.info("最终exceedCount={} (来自最后一列的计算)", exceedCount);
logger.info("各列超限次数对比: {}", java.util.Arrays.toString(exceedCountPerColumn));
logger.info("各列累加值: {}", java.util.Arrays.toString(HKSum));
// 计算平均责任百分比 - 对应C代码行453-459
logger.info("===== 开始计算最终百分比 =====");
for (int i = 0; i < columnCount; i++) {
sumData[i] = 0; // 对应C代码行454
}
double totalPercentage = 0; // 用于统计总和
for (int i = 0; i < columnCount; i++) {
if (exceedCount > 0) {
// 对应C代码行458: arrHKsum[i] = 100 * (HKSum(i)/coutt);
// 使用double进行计算然后转换为float
double percentage = 100.0 * (HKSum[i] / (double)exceedCount);
sumData[i] = (float)percentage;
totalPercentage += percentage;
logger.info("节点{}: 累加值={}, 除以超限次数={}, 百分比={}%",
i, HKSum[i], exceedCount, percentage);
} else {
logger.warn("节点{}: 超限次数为0百分比设为0", i);
}
}
logger.info("===== 计算结果汇总 =====");
logger.info("使用的超限次数(分母): {}", exceedCount);
logger.info("各节点百分比: {}", java.util.Arrays.toString(sumData));
logger.info("百分比总和: {}% (期望100%)", totalPercentage);
logger.info("偏差: {}%", Math.abs(totalPercentage - 100.0));
if (Math.abs(totalPercentage - 100.0) > 1.0) {
logger.warn("!!!注意!!! 百分比总和偏离100%超过1%,可能存在问题");
}
logger.info("======= sumResponsibility 计算完成 =======");
return sumData;
}
/**
* 计算超限时段的责任总和(兼容版本)
* 为了向后兼容保留不带tl_num参数的版本
*
* @param responsibilityData 责任数据矩阵FK或HK[时间][节点]
* @param harmonicData 谐波数据Udata
* @param threshold 谐波门槛XIANE
* @param windowSize 窗口大小
* @param columnCount 列数(节点数或节点数+1
* @return 各节点的责任总和百分比
*/
public static float[] sumResponsibility(float[][] responsibilityData, float[] harmonicData,
float threshold, int windowSize, int columnCount) {
// 如果没有提供tl_num从数据推断
int tl_num = responsibilityData.length + windowSize;
return sumResponsibility(responsibilityData, harmonicData, threshold, windowSize, columnCount, tl_num);
}
/**
* 计算所有节点的动态相关系数矩阵
* 这个函数在主引擎中已经内联实现,这里保留作为辅助方法
*
* @param powerData 功率数据矩阵
* @param harmonicData 谐波数据
* @param canonicalCorr 典则相关系数序列
* @param windowSize 窗口大小
* @param nodeCount 节点数量
* @return 动态相关系数矩阵
*/
public static float[][] computeCorrelationMatrix(float[][] powerData, float[] harmonicData,
float[] canonicalCorr, int windowSize,
int nodeCount) {
int slideLength = canonicalCorr.length;
float[][] correlationMatrix = new float[slideLength][nodeCount];
logger.info("Computing correlation matrix for all nodes");
for (int nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) {
// 提取单个节点的功率数据
float[] nodePower = new float[powerData.length];
for (int i = 0; i < powerData.length; i++) {
nodePower[i] = powerData[i][nodeIdx];
}
// 计算该节点的动态相关系数
float[] nodeCorr = CanonicalCorrelationAnalysis
.slidingCorrelation(nodePower, harmonicData, canonicalCorr, windowSize);
// 存储结果
for (int i = 0; i < slideLength; i++) {
correlationMatrix[i][nodeIdx] = nodeCorr[i];
}
}
logger.info("Correlation matrix computation completed");
return correlationMatrix;
}
}

View File

@@ -0,0 +1,68 @@
package com.njcn.product.advance.responsility.controller;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.influx.pojo.dto.HarmHistoryDataDTO;
import com.njcn.product.advance.harmonicUp.pojo.param.HistoryParam;
import com.njcn.product.advance.harmonicUp.pojo.vo.HistoryDataResultVO;
import com.njcn.product.advance.harmonicUp.service.HistoryResultService;
import com.njcn.product.advance.responsility.pojo.param.HistoryHarmParam;
import com.njcn.product.advance.eventSource.service.HistoryHarmonicService;
import com.njcn.web.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Author: cdf
* @CreateTime: 2025-09-08
* @Description:
*/
@Validated
@Slf4j
@RestController
@RequestMapping("/harmonic")
@Api(tags = "稳态数据分析")
@RequiredArgsConstructor
public class HistoryHarmonicController extends BaseController {
private final HistoryHarmonicService historyHarmonicService;
private final HistoryResultService historyResultService;
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@PostMapping("/getHistoryHarmData")
@ApiOperation("获取谐波历史数据")
@ApiImplicitParam(name = "historyHarmParam", value = "谐波历史数据请求参数", required = true)
public HttpResult<HarmHistoryDataDTO> getHistoryHarmData(@RequestBody @Validated HistoryHarmParam historyHarmParam) {
String methodDescribe = getMethodDescribe("getHistoryHarmData");
HarmHistoryDataDTO harmHistoryDataDTO = historyHarmonicService.getHistoryHarmData(historyHarmParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, harmHistoryDataDTO, methodDescribe);
}
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@PostMapping("/getHistoryResult")
@ApiOperation("稳态数据分析")
@ApiImplicitParam(name = "historyParam", value = "稳态数据分析参数", required = true)
public HttpResult<List<HistoryDataResultVO>> getHistoryResult(@RequestBody @Validated HistoryParam historyParam) {
String methodDescribe = getMethodDescribe("getHistoryResult");
List<HistoryDataResultVO> list = historyResultService.getHistoryResult(historyParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
}
}

View File

@@ -0,0 +1,114 @@
package com.njcn.product.advance.responsility.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.product.advance.responsility.pojo.dto.RespDataDTO;
import com.njcn.product.advance.responsility.pojo.dto.ResponsibilityResult;
import com.njcn.product.advance.responsility.pojo.param.RespBaseParam;
import com.njcn.product.advance.responsility.pojo.param.ResponsibilityCalculateParam;
import com.njcn.product.advance.responsility.pojo.param.ResponsibilitySecondCalParam;
import com.njcn.product.advance.responsility.service.IRespDataResultService;
import com.njcn.product.advance.responsility.service.IRespDataService;
import com.njcn.web.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月21日 10:06
*/
@RestController
@RequestMapping("responsibility")
@Api(tags = "谐波责任划分-谐波责任数据处理")
@RequiredArgsConstructor
public class ResponsibilityController extends BaseController {
private final IRespDataService respDataService;
private final IRespDataResultService respDataResultService;
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@PostMapping("/responsibilityList")
@ApiOperation("查询责任划分列表分页")
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
public HttpResult<Page<RespDataDTO>> responsibilityList(@RequestBody @Validated RespBaseParam queryParam) {
String methodDescribe = getMethodDescribe("responsibilityList");
Page<RespDataDTO> list = respDataService.responsibilityList(queryParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
}
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@PostMapping("/deleteByIds")
@ApiOperation("删除责任划分数据")
@ApiImplicitParam(name = "ids", value = "待删除的责任id集合", required = true)
public HttpResult<Page<RespDataDTO>> deleteByIds(@RequestBody List<String> ids) {
String methodDescribe = getMethodDescribe("deleteByIds");
respDataService.deleteByIds(ids);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
@PostMapping("getDynamicData")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("动态谐波责任划分")
@ApiImplicitParam(name = "responsibilityCalculateParam", value = "谐波责任动态划分参数", required = true)
public HttpResult<ResponsibilityResult> getDynamicData(@RequestBody @Validated ResponsibilityCalculateParam responsibilityCalculateParam) {
String methodDescribe = getMethodDescribe("getDynamicData");
ResponsibilityResult datas = respDataService.getDynamicData(responsibilityCalculateParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, datas, methodDescribe);
}
@PostMapping("getResponsibilityData")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("二次计算责任划分")
@ApiImplicitParam(name = "responsibilitySecondCalParam", value = "二次计算责任划分参数", required = true)
public HttpResult<ResponsibilityResult> getResponsibilityData(@RequestBody @Validated ResponsibilitySecondCalParam responsibilitySecondCalParam) {
String methodDescribe = getMethodDescribe("getResponsibilityData");
ResponsibilityResult datas = respDataService.getResponsibilityData(responsibilitySecondCalParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, datas, methodDescribe);
}
@GetMapping("displayHistoryData")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("回显历史责任划分结果")
@ApiImplicitParam(name = "id", value = "责任数据id", required = true)
public HttpResult<List<ResponsibilityResult>> displayHistoryData(String id,Integer time) {
String methodDescribe = getMethodDescribe("displayHistoryData");
List<ResponsibilityResult> datas = respDataResultService.displayHistoryData(id,time);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, datas, methodDescribe);
}
@PostMapping("systemDynamicData")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("动态谐波责任划分")
@ApiImplicitParam(name = "responsibilityCalculateParam", value = "谐波责任动态划分参数", required = true)
public HttpResult<ResponsibilityResult> systemDynamicData(@RequestBody @Validated ResponsibilityCalculateParam responsibilityCalculateParam) {
String methodDescribe = getMethodDescribe("getDynamicData");
ResponsibilityResult datas = respDataService.getDynamicData(responsibilityCalculateParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, datas, methodDescribe);
}
}

View File

@@ -0,0 +1,114 @@
package com.njcn.product.advance.responsility.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.dto.SelectOption;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.product.advance.responsility.pojo.dto.RespDataDTO;
import com.njcn.product.advance.responsility.pojo.param.UserDataIntegrityParam;
import com.njcn.product.advance.responsility.pojo.po.RespUserData;
import com.njcn.product.advance.responsility.pojo.po.RespUserDataIntegrity;
import com.njcn.product.advance.responsility.service.IRespUserDataIntegrityService;
import com.njcn.product.advance.responsility.service.IRespUserDataService;
import com.njcn.web.controller.BaseController;
import com.njcn.web.pojo.param.BaseParam;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月13日 14:11
*/
@RestController
@RequestMapping("responsibility")
@Api(tags = "谐波责任划分-用采数据处理")
@RequiredArgsConstructor
public class UserDataController extends BaseController {
private final IRespUserDataService respUserDataService;
private final IRespUserDataIntegrityService respUserDataIntegrityService;
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@PostMapping("/userDataList")
@ApiOperation("查询用户列表分页")
@ApiImplicitParam(name = "queryParam", value = "查询参数", required = true)
public HttpResult<Page<RespUserData>> userDataList(@RequestBody @Validated BaseParam queryParam) {
String methodDescribe = getMethodDescribe("userDataList");
Page<RespUserData> list = respUserDataService.userDataList(queryParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
}
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@PostMapping("/userDataIntegrityList")
@ApiOperation("用采完整性不足列表")
@ApiImplicitParam(name = "userDataIntegrityParam", value = "查询参数", required = true)
public HttpResult<Page<RespUserDataIntegrity>> userDataIntegrityList(@RequestBody @Validated UserDataIntegrityParam userDataIntegrityParam) {
String methodDescribe = getMethodDescribe("userDataIntegrityList");
Page<RespUserDataIntegrity> list = respUserDataIntegrityService.userDataIntegrityList(userDataIntegrityParam);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
}
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@PostMapping("/deleteUserDataByIds")
@ApiOperation("删除用采数据")
@ApiImplicitParam(name = "ids", value = "待删除用采数据id集合", required = true)
public HttpResult<Page<RespDataDTO>> deleteUserDataByIds(@RequestBody List<String> ids) {
String methodDescribe = getMethodDescribe("deleteUserDataByIds");
respUserDataService.deleteUserDataByIds(ids);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@GetMapping("/userDataSelect")
@ApiOperation("用采数据下拉")
public HttpResult<List<SelectOption>> userDataSelect() {
String methodDescribe = getMethodDescribe("userDataSelect");
List<SelectOption> listOption = respUserDataService.userDataSelect();
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, listOption, methodDescribe);
}
/**
* 上传用采数据,并对用采数据进行数据分析并缓存
*
* @param file 上传的表格
*/
@PostMapping("uploadUserData")
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
@ApiOperation("上传用采数据")
public HttpResult<Object> uploadUserData(@ApiParam(value = "文件", required = true) @RequestPart("file") MultipartFile file, HttpServletResponse response) {
String methodDescribe = getMethodDescribe("uploadUserData");
String fileName = file.getOriginalFilename();
long fileSize = file.getSize() / 1024;
//判断文件大小
if (fileSize > 3072) {
throw new BusinessException(CommonResponseEnum.FILE_SIZE_ERROR);
}
assert fileName != null;
if (!fileName.matches("^.+\\.(?i)(xlsx)$") && !fileName.matches("^.+\\.(?i)(xls)$")) {
throw new BusinessException(CommonResponseEnum.FILE_XLSX_ERROR);
}
respUserDataService.uploadUserData(file, response);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
}

View File

@@ -0,0 +1,30 @@
package com.njcn.product.advance.responsility.imapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.njcn.influx.ano.IgnoreData;
import com.njcn.influx.utils.InstantDateDeserializer;
import com.njcn.influx.utils.InstantDateSerializer;
import lombok.Data;
import org.influxdb.annotation.Column;
import java.time.Instant;
/**
* @Author: cdf
* @CreateTime: 2025-09-18
* @Description:
*/
@Data
public class DataHarmP {
@Column(name = "time")
@JsonSerialize(using = InstantDateSerializer.class)
@JsonDeserialize(using = InstantDateDeserializer.class)
private Instant time;
@Column(name = "line_id")
private String lineId;
@IgnoreData(true)
private Float value;
}

View File

@@ -0,0 +1,26 @@
package com.njcn.product.advance.responsility.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.product.advance.responsility.pojo.dto.RespDataDTO;
import com.njcn.product.advance.responsility.pojo.po.RespData;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author hongawen
* @since 2023-07-21
*/
public interface RespDataMapper extends BaseMapper<RespData> {
Page<RespDataDTO> page(@Param("page") Page<Object> objectPage, @Param("ew")QueryWrapper<RespDataDTO> queryWrapper);
void deleteByIds(@Param("ids") List<String> ids);
}

View File

@@ -0,0 +1,16 @@
package com.njcn.product.advance.responsility.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.njcn.product.advance.responsility.pojo.po.RespDataResult;
/**
* <p>
* Mapper 接口
* </p>
*
* @author hongawen
* @since 2023-07-24
*/
public interface RespDataResultMapper extends BaseMapper<RespDataResult> {
}

View File

@@ -0,0 +1,20 @@
package com.njcn.product.advance.responsility.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.product.advance.responsility.pojo.po.RespUserDataIntegrity;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* Mapper 接口
* </p>
*
* @author hongawen
* @since 2023-07-13
*/
public interface RespUserDataIntegrityMapper extends BaseMapper<RespUserDataIntegrity> {
Page<RespUserDataIntegrity> page(@Param("page") Page<Object> objectPage, @Param("ew") QueryWrapper<RespUserDataIntegrity> lambdaQueryWrapper);
}

View File

@@ -0,0 +1,24 @@
package com.njcn.product.advance.responsility.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.product.advance.responsility.pojo.po.RespUserData;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author hongawen
* @since 2023-07-13
*/
public interface RespUserDataMapper extends BaseMapper<RespUserData> {
Page<RespUserData> page(@Param("page")Page<Object> objectPage, @Param("ew")QueryWrapper<RespUserData> respUserDataQueryWrapper);
void deleteUserDataByIds(@Param("ids") List<String> ids);
}

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.product.advance.responsility.mapper.RespDataMapper">
<select id="page" resultType="RespDataDTO">
SELECT pqs_resp_data.*,
pqs_resp_user_data.name userDataName
FROM pqs_resp_data pqs_resp_data
left join pqs_resp_user_data pqs_resp_user_data on pqs_resp_data.user_data_id = pqs_resp_user_data.id
WHERE
${ew.sqlSegment}
</select>
<update id="deleteByIds" >
update
pqs_resp_data
set state = 0
where
id
in
<foreach collection="ids" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</update>
</mapper>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.product.advance.responsility.mapper.RespDataResultMapper">
</mapper>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.product.advance.responsility.mapper.RespUserDataIntegrityMapper">
<select id="page" resultType="RespUserDataIntegrity">
SELECT pqs_resp_user_data_integrity.*
FROM pqs_resp_user_data_integrity pqs_resp_user_data_integrity
WHERE ${ew.sqlSegment}
</select>
</mapper>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.product.advance.responsility.mapper.RespUserDataMapper">
<select id="page" resultType="RespUserData">
SELECT pqs_resp_user_data.*
FROM pqs_resp_user_data pqs_resp_user_data
WHERE ${ew.sqlSegment}
</select>
<update id="deleteUserDataByIds" >
update
pqs_resp_user_data
set state = 0
where
id
in
<foreach collection="ids" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</update>
</mapper>

View File

@@ -0,0 +1,47 @@
package com.njcn.product.advance.responsility.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 当根据动态责任数据获取用户责任量化结果时,将需要的一些参数进行缓存
* 比如 harmNum,pNode,HKData,FKData,HarmData,监测点的测量间隔,win窗口最小公倍数
* 以及FKData每个时间点的p对应的用户List<name>
*
* @author hongawen
* @Date: 2019/4/29 16:06
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CacheQvvrData implements Serializable {
private int pNode;
private int harmNum;
private float[] harmData;
private float[][] fKData;
private float[][] hKData;
private List<String> names;
private int lineInterval;
private int win;
//最小公倍数
private int minMultiple;
//横轴时间
private List<Long> times;
}

View File

@@ -0,0 +1,33 @@
package com.njcn.product.advance.responsility.model;
import com.sun.jna.Structure;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
/**
* @author: tw
* @date: 2022/1/12 10:57
*/
public class HIKSDKStructure extends Structure {
protected List<String> getFieldOrder(){
List<String> fieldOrderList = new ArrayList<String>();
for (Class<?> cls = getClass();
!cls.equals(HIKSDKStructure.class);
cls = cls.getSuperclass()) {
Field[] fields = cls.getDeclaredFields();
int modifiers;
for (Field field : fields) {
modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
continue;
}
fieldOrderList.add(field.getName());
}
}
return fieldOrderList;
}
}

View File

@@ -0,0 +1,37 @@
package com.njcn.product.advance.responsility.model;
import com.sun.jna.Structure;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
public class HKDataStruct extends Structure implements Serializable {
public float hk[] = new float[QvvrStruct.MAX_P_NODE + 1];
public HKDataStruct() {
}
@Override
protected List getFieldOrder() {
return Collections.singletonList("hk");
}
public HKDataStruct(double[] hk) {
for (int i = 0; i < hk.length; i++) {
this.hk[i] = (float) hk[i];
}
}
public static class ByReference extends HKDataStruct implements Structure.ByReference {
public ByReference(double[] p) {
super(p);
}
}
public static class ByValue extends HKDataStruct implements Structure.ByValue {
public ByValue(double[] p) {
super(p);
}
}
}

View File

@@ -0,0 +1,287 @@
package com.njcn.product.advance.responsility.model;
import com.njcn.product.advance.responsility.pojo.constant.CalculationMode;
import com.njcn.product.advance.responsility.pojo.constant.CalculationStatus;
import com.njcn.product.advance.responsility.pojo.constant.HarmonicConstants;
/**
* 谐波数据结构类
* 对应C语言中的harm_data_struct结构体
*
* @author hongawen
* @version 1.0
*/
public class HarmonicData {
// 输入参数
private CalculationMode calculationMode; // 计算标志
private int harmonicCount; // 谐波数据个数
private int powerCount; // 功率数据个数
private int powerNodeCount; // 功率负荷节点数
private int windowSize; // 数据窗大小
private int responsibilityDataCount; // 代入的责任数据个数
private float harmonicThreshold; // 谐波电压门槛
// 数据数组
private float[] harmonicData; // 谐波数据序列
private float[][] powerData; // 功率数据序列
// 输入输出数据
private float[][] correlationData; // 动态相关系数数据序列
private float[][] fkData; // 不包含背景动态谐波责任数据序列
private float[][] hkData; // 包含背景动态谐波责任数据序列
private float[] canonicalCorrelation; // 典则相关系数
private float[] backgroundCanonicalCorr; // 包含背景典则相关系数
// 输出结果
private CalculationStatus calculationStatus; // 计算状态
private float[] sumFKData; // 不包含背景谐波责任
private float[] sumHKData; // 包含背景谐波责任
/**
* 默认构造函数
*/
public HarmonicData() {
this.calculationMode = CalculationMode.FULL_CALCULATION;
this.calculationStatus = CalculationStatus.NOT_CALCULATED;
this.windowSize = HarmonicConstants.DEFAULT_WINDOW_SIZE;
}
/**
* Builder模式构造器
*/
public static class Builder {
private HarmonicData data = new HarmonicData();
public Builder calculationMode(CalculationMode mode) {
data.calculationMode = mode;
return this;
}
public Builder harmonicCount(int count) {
data.harmonicCount = count;
return this;
}
public Builder powerCount(int count) {
data.powerCount = count;
return this;
}
public Builder powerNodeCount(int count) {
data.powerNodeCount = count;
return this;
}
public Builder windowSize(int size) {
data.windowSize = size;
return this;
}
public Builder harmonicThreshold(float threshold) {
data.harmonicThreshold = threshold;
return this;
}
public Builder harmonicData(float[] data) {
this.data.harmonicData = data;
return this;
}
public Builder powerData(float[][] data) {
this.data.powerData = data;
return this;
}
public HarmonicData build() {
// 验证数据
validateData();
// 初始化数组
initializeArrays();
return data;
}
private void validateData() {
if (data.harmonicCount <= 0 || data.harmonicCount > HarmonicConstants.MAX_HARM_NUM) {
throw new IllegalArgumentException("Invalid harmonic count: " + data.harmonicCount);
}
if (data.powerCount <= 0 || data.powerCount > HarmonicConstants.MAX_P_NUM) {
throw new IllegalArgumentException("Invalid power count: " + data.powerCount);
}
if (data.powerNodeCount <= 0 || data.powerNodeCount > HarmonicConstants.MAX_P_NODE) {
throw new IllegalArgumentException("Invalid power node count: " + data.powerNodeCount);
}
if (data.windowSize < HarmonicConstants.MIN_WIN_LEN ||
data.windowSize > HarmonicConstants.MAX_WIN_LEN) {
throw new IllegalArgumentException("Invalid window size: " + data.windowSize);
}
// 验证数据对齐
if (data.calculationMode == CalculationMode.FULL_CALCULATION) {
int ratio = data.harmonicCount / data.powerCount;
if (ratio * data.powerCount != data.harmonicCount || ratio < 1) {
throw new IllegalArgumentException("Harmonic data count must be integer multiple of power data count");
}
}
}
private void initializeArrays() {
if (data.harmonicData == null) {
data.harmonicData = new float[data.harmonicCount];
}
if (data.powerData == null) {
data.powerData = new float[data.powerCount][data.powerNodeCount];
}
int resultCount = data.powerCount - data.windowSize;
if (resultCount > 0) {
data.correlationData = new float[resultCount][data.powerNodeCount];
data.fkData = new float[resultCount][data.powerNodeCount];
data.hkData = new float[resultCount][data.powerNodeCount + 1];
data.canonicalCorrelation = new float[resultCount];
data.backgroundCanonicalCorr = new float[resultCount];
}
data.sumFKData = new float[data.powerNodeCount];
data.sumHKData = new float[data.powerNodeCount + 1];
}
}
// Getters and Setters
public CalculationMode getCalculationMode() {
return calculationMode;
}
public void setCalculationMode(CalculationMode calculationMode) {
this.calculationMode = calculationMode;
}
public int getHarmonicCount() {
return harmonicCount;
}
public void setHarmonicCount(int harmonicCount) {
this.harmonicCount = harmonicCount;
}
public int getPowerCount() {
return powerCount;
}
public void setPowerCount(int powerCount) {
this.powerCount = powerCount;
}
public int getPowerNodeCount() {
return powerNodeCount;
}
public void setPowerNodeCount(int powerNodeCount) {
this.powerNodeCount = powerNodeCount;
}
public int getWindowSize() {
return windowSize;
}
public void setWindowSize(int windowSize) {
this.windowSize = windowSize;
}
public int getResponsibilityDataCount() {
return responsibilityDataCount;
}
public void setResponsibilityDataCount(int responsibilityDataCount) {
this.responsibilityDataCount = responsibilityDataCount;
}
public float getHarmonicThreshold() {
return harmonicThreshold;
}
public void setHarmonicThreshold(float harmonicThreshold) {
this.harmonicThreshold = harmonicThreshold;
}
public float[] getHarmonicData() {
return harmonicData;
}
public void setHarmonicData(float[] harmonicData) {
this.harmonicData = harmonicData;
}
public float[][] getPowerData() {
return powerData;
}
public void setPowerData(float[][] powerData) {
this.powerData = powerData;
}
public float[][] getCorrelationData() {
return correlationData;
}
public void setCorrelationData(float[][] correlationData) {
this.correlationData = correlationData;
}
public float[][] getFkData() {
return fkData;
}
public void setFkData(float[][] fkData) {
this.fkData = fkData;
}
public float[][] getHkData() {
return hkData;
}
public void setHkData(float[][] hkData) {
this.hkData = hkData;
}
public float[] getCanonicalCorrelation() {
return canonicalCorrelation;
}
public void setCanonicalCorrelation(float[] canonicalCorrelation) {
this.canonicalCorrelation = canonicalCorrelation;
}
public float[] getBackgroundCanonicalCorr() {
return backgroundCanonicalCorr;
}
public void setBackgroundCanonicalCorr(float[] backgroundCanonicalCorr) {
this.backgroundCanonicalCorr = backgroundCanonicalCorr;
}
public CalculationStatus getCalculationStatus() {
return calculationStatus;
}
public void setCalculationStatus(CalculationStatus calculationStatus) {
this.calculationStatus = calculationStatus;
}
public float[] getSumFKData() {
return sumFKData;
}
public void setSumFKData(float[] sumFKData) {
this.sumFKData = sumFKData;
}
public float[] getSumHKData() {
return sumHKData;
}
public void setSumHKData(float[] sumHKData) {
this.sumHKData = sumHKData;
}
}

View File

@@ -0,0 +1,55 @@
package com.njcn.product.advance.responsility.model;
import com.sun.jna.Structure;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class PDataStruct extends Structure implements Serializable {
public float p[] = new float[QvvrStruct.MAX_P_NODE];
public PDataStruct() {
}
@Override
protected List getFieldOrder() {
// return null;
return Collections.singletonList("p");
}
public PDataStruct(double[] p) {
for (int i = 0; i < p.length; i++) {
this.p[i] = (float) p[i];
}
}
public static class ByReference extends PDataStruct implements Structure.ByReference {
public ByReference(double[] p) {
super(p);
}
}
public static class ByValue extends PDataStruct implements Structure.ByValue {
public ByValue(double[] p) {
super(p);
}
}
public float[] getP() {
return p;
}
public void setP(float[] p) {
this.p = p;
}
@Override
public String toString() {
return "PDataStruct{" +
"p=" + Arrays.toString(p) +
'}';
}
}

View File

@@ -0,0 +1,54 @@
package com.njcn.product.advance.responsility.model;
import lombok.Data;
import java.io.Serializable;
@Data
public class QvvrDataEntity implements Serializable {
public static final int MAX_P_NODE= 200; //功率节点个数限制按200个限制
public static final int MAX_P_NUM= 96 * 100; //功率数据按15分钟间隔100天处理
public static final int MAX_HARM_NUM= 1440 * 100; //谐波数据按一分钟间隔100天处理
public static final int MAX_WIN_LEN=96 * 10; //按15分钟算10天
public static final int MIN_WIN_LEN = 4; //按15分钟算1小时
//输入参数
public int calFlag; //计算标志0默认用电压和功率数据计算相关系数和责任1用代入的动态相关系数计算责任
public int harmNum; //谐波数据个数
public int pNum; //功率数据个数
public int pNode; //功率负荷节点数
public int win; //数据窗大小
public int resNum; //代入的责任数据个数
public float harmMk; //谐波电压门槛
public float harmData[]; //谐波数据序列
public float [][] pData; //功率数据序列
public float [][] simData; //动态相关系数数据序列,可作为输入或者输出
public float [][] fKData; //不包含背景动态谐波责任数据序列,可作为输入或者输出
public float [][] hKData; //包含背景动态谐波责任数据序列,可作为输入或者输出
public float [] core; //典则相关系数
public float [] bjCore; //包含背景典则相关系数
//输出结果
public int calOk; //是否计算正确标志置位0表示未计算置位1表示计算完成
public float [] sumFKdata;//不包含背景谐波责任
public float [] sumHKdata;//包含背景谐波责任
public QvvrDataEntity() {
calFlag = 0;
harmData = new float[MAX_HARM_NUM];
pData = new float[MAX_P_NUM][MAX_P_NODE];
simData = new float[MAX_P_NUM][MAX_P_NODE];
fKData = new float[MAX_P_NUM][MAX_P_NODE];
hKData = new float[MAX_P_NUM][MAX_P_NODE+1];
core = new float[MAX_P_NUM];
bjCore = new float[MAX_P_NUM];
sumFKdata = new float[MAX_P_NODE];
sumHKdata = new float[MAX_P_NODE + 1];
}
}

View File

@@ -0,0 +1,205 @@
package com.njcn.product.advance.responsility.model;
import com.sun.jna.Structure;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
public class QvvrStruct extends HIKSDKStructure implements Serializable {
public static final int MAX_P_NODE = 200; //功率节点个数限制按200个限制
public static final int MAX_P_NUM = 96 * 100; //功率数据按15分钟间隔100天处理
public static final int MAX_HARM_NUM = 1440 * 100; //谐波数据按一分钟间隔100天处理
public static final int MAX_WIN_LEN = 96 * 10; //按15分钟算10天
public static final int MIN_WIN_LEN = 4; //按15分钟算1小时
//输入参数
public int cal_flag; //计算标志0默认用电压和功率数据计算相关系数和责任1用代入的动态相关系数计算责任
public int harm_num; //谐波数据个数
public int p_num; //功率数据个数
public int p_node; //功率负荷节点数
public int win; //数据窗大小
public int res_num; //代入的责任数据个数
public float harm_mk; //谐波电压门槛
public float harm_data[]; //谐波数据序列
public PDataStruct p_data[]; //功率数据序列
public PDataStruct sim_data[]; //动态相关系数数据序列,可作为输入或者输出
public PDataStruct FKdata[]; //不包含背景动态谐波责任数据序列,可作为输入或者输出
public HKDataStruct HKdata[]; //包含背景动态谐波责任数据序列,可作为输入或者输出
public float Core[]; //典则相关系数
public float BjCore[]; //包含背景典则相关系数
//输出结果
public int cal_ok; //是否计算正确标志置位0表示未计算置位1表示计算完成
public float sumFKdata[];//不包含背景谐波责任
public float sumHKdata[];//包含背景谐波责任
public QvvrStruct() {
cal_flag = 0;
harm_data = new float[MAX_HARM_NUM];
p_data = new PDataStruct[MAX_P_NUM];
sim_data = new PDataStruct[MAX_P_NUM];
FKdata = new PDataStruct[MAX_P_NUM];
HKdata = new HKDataStruct[MAX_P_NUM];
Core = new float[MAX_P_NUM];
BjCore = new float[MAX_P_NUM];
sumFKdata = new float[MAX_P_NODE];
sumHKdata = new float[MAX_P_NODE + 1];
}
public static class ByReference extends QvvrStruct implements Structure.ByReference {
}
public static class ByValue extends QvvrStruct implements Structure.ByValue {
}
public PDataStruct[] getFKdata() {
return FKdata;
}
public void setFKdata(PDataStruct[] FKdata) {
this.FKdata = FKdata;
}
public HKDataStruct[] getHKdata() {
return HKdata;
}
public void setHKdata(HKDataStruct[] HKdata) {
this.HKdata = HKdata;
}
public float[] getSumFKdata() {
return sumFKdata;
}
public void setSumFKdata(float[] sumFKdata) {
this.sumFKdata = sumFKdata;
}
public float[] getSumHKdata() {
return sumHKdata;
}
public void setSumHKdata(float[] sumHKdata) {
this.sumHKdata = sumHKdata;
}
public int getCal_flag() {
return cal_flag;
}
public void setCal_flag(int cal_flag) {
this.cal_flag = cal_flag;
}
public int getHarm_num() {
return harm_num;
}
public void setHarm_num(int harm_num) {
this.harm_num = harm_num;
}
public float getHarm_mk() {
return harm_mk;
}
public void setHarm_mk(float harm_mk) {
this.harm_mk = harm_mk;
}
public float[] getHarm_data() {
return harm_data;
}
public void setHarm_data(float[] harm_data) {
this.harm_data = harm_data;
}
public float[] getCore() {
return Core;
}
public void setCore(float[] core) {
Core = core;
}
public float[] getBjCore() {
return BjCore;
}
public void setBjCore(float[] bjCore) {
BjCore = bjCore;
}
public int getCal_ok() {
return cal_ok;
}
public void setCal_ok(int cal_ok) {
this.cal_ok = cal_ok;
}
public int getP_num() {
return p_num;
}
public void setP_num(int p_num) {
this.p_num = p_num;
}
public int getP_node() {
return p_node;
}
public void setP_node(int p_node) {
this.p_node = p_node;
}
public int getWin() {
return win;
}
public void setWin(int win) {
this.win = win;
}
public int getRes_num() {
return res_num;
}
public void setRes_num(int res_num) {
this.res_num = res_num;
}
public PDataStruct[] getP_data() {
return p_data;
}
public void setP_data(PDataStruct[] p_data) {
this.p_data = p_data;
}
public PDataStruct[] getSim_data() {
return sim_data;
}
public void setSim_data(PDataStruct[] sim_data) {
this.sim_data = sim_data;
}
@Override
protected List getFieldOrder() {
return Arrays.asList(
"cal_flag", "harm_num", "p_num", "p_node", "win",
"res_num", "harm_mk", "harm_data", "p_data", "sim_data",
"FKdata", "HKdata", "Core", "BjCore", "cal_ok",
"sumFKdata",
"sumHKdata");
}
}

View File

@@ -0,0 +1,37 @@
package com.njcn.product.advance.responsility.pojo.bo;
import lombok.Data;
import java.io.Serializable;
import java.util.*;
/**
* 处理用采原始数据得到的一个结果
*
* @author hongawen
* @Date: 2019/4/26 15:57
*/
@Data
public class DealDataResult implements Serializable {
/***
* String 户号@监测点号@户名
* String yyyy-MM-dd
* Date 数据的详细日期
* UserDataExcel 数据详细信息
* 先以测量局号分组,再以该测量局号下每个日期分组
*/
private Map<String, Map<String, Map<Date, UserDataExcel>>> totalData = new HashMap<>();
private List<String> dates = new ArrayList<>();
/***
* String 户号@监测点号@户名
* String yyyy-MM-dd
* UserDataExcel 数据详细信息
* 先以测量局号分组,再以该测量局号下每个日期分组
*/
private Map<String, Map<String, List<UserDataExcel>>> totalListData = new HashMap<>();
}

View File

@@ -0,0 +1,31 @@
package com.njcn.product.advance.responsility.pojo.bo;
import com.njcn.product.advance.responsility.pojo.po.RespUserDataIntegrity;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 针对天处理用采数据的结果实体
* @author hongawen
* @Date: 2019/4/19 14:38
*/
@Data
public class DealUserDataResult implements Serializable {
//处理好的数据
private List<UserDataExcel> completed = new ArrayList<>();
//因当日完整性不足90没有处理直接返回
private List<UserDataExcel> lack = new ArrayList<>();
//完整性不足时,用户信息描述
private String detail;
//完整性不足的具体信息
private RespUserDataIntegrity respUserDataIntegrity;
}

View File

@@ -0,0 +1,25 @@
package com.njcn.product.advance.responsility.pojo.bo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月28日 11:32
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespCommon implements Serializable {
private int pNum;
private int userIntervalTime;
private int lineInterval;
}

View File

@@ -0,0 +1,25 @@
package com.njcn.product.advance.responsility.pojo.bo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月28日 11:38
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespHarmData implements Serializable {
private float[] harmData;
private List<Long> harmTime;
private float overLimit;
}

View File

@@ -0,0 +1,46 @@
package com.njcn.product.advance.responsility.pojo.bo;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 提取用采数据或者将用采数据写进excel的实体类
*
* @author hongawen
* @date 2019/4/11 10:43
*/
@Data
public class UserDataExcel implements Serializable, Comparable<UserDataExcel> {
@Excel(name = "时间")
private String time;
@Excel(name = "瞬时功率")
private BigDecimal work;
@Excel(name = "户号")
private String userId;
@Excel(name = "测量点局号")
private String line;
@Excel(name = "户名")
private String userName;
@Override
public int compareTo(UserDataExcel o) {
if (DateUtil.parse(this.time, DatePattern.NORM_DATETIME_PATTERN).getTime() > DateUtil.parse(o.getTime(), DatePattern.NORM_DATETIME_PATTERN).getTime()) {
return 1;
} else if (DateUtil.parse(this.time, DatePattern.NORM_DATETIME_PATTERN).getTime() == DateUtil.parse(o.getTime(), DatePattern.NORM_DATETIME_PATTERN).getTime()) {
return 0;
}
return -1;
}
}

View File

@@ -0,0 +1,46 @@
package com.njcn.product.advance.responsility.pojo.constant;
/**
* 计算模式枚举
*
* @author hongawen
* @version 1.0
*/
public enum CalculationMode {
/**
* 完整计算模式
* 使用电压和功率数据计算相关系数和责任
*/
FULL_CALCULATION(0, "完整计算模式"),
/**
* 部分重算模式
* 使用已有的动态相关系数计算责任
*/
PARTIAL_RECALCULATION(1, "部分重算模式");
private final int code;
private final String description;
CalculationMode(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
public static CalculationMode fromCode(int code) {
for (CalculationMode mode : values()) {
if (mode.code == code) {
return mode;
}
}
throw new IllegalArgumentException("Invalid calculation mode code: " + code);
}
}

View File

@@ -0,0 +1,49 @@
package com.njcn.product.advance.responsility.pojo.constant;
/**
* 计算状态枚举
*
* @author hongawen
* @version 1.0
*/
public enum CalculationStatus {
/**
* 未计算
*/
NOT_CALCULATED(0, "未计算"),
/**
* 计算完成
*/
CALCULATED(1, "计算完成"),
/**
* 计算失败
*/
FAILED(-1, "计算失败");
private final int code;
private final String description;
CalculationStatus(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
public static CalculationStatus fromCode(int code) {
for (CalculationStatus status : values()) {
if (status.code == code) {
return status;
}
}
return NOT_CALCULATED;
}
}

View File

@@ -0,0 +1,60 @@
package com.njcn.product.advance.responsility.pojo.constant;
/**
* 谐波责任量化系统常量定义
*
* @author hongawen
* @version 1.0
*/
public final class HarmonicConstants {
private HarmonicConstants() {
// 防止实例化
}
/**
* 最大谐波数据个数 (1440*100)
* 按一分钟间隔100天处理
*/
public static final int MAX_HARM_NUM = 144000;
/**
* 最大功率数据个数 (96*100)
* 按15分钟间隔100天处理
*/
public static final int MAX_P_NUM = 9600;
/**
* 最大功率节点个数
* 按200个限制
*/
public static final int MAX_P_NODE = 200;
/**
* 最大数据窗长度 (96*10)
* 按15分钟算10天
*/
public static final int MAX_WIN_LEN = 960;
/**
* 最小数据窗长度
* 按15分钟算一小时
*/
public static final int MIN_WIN_LEN = 4;
/**
* 默认数据窗大小
* 一天的数据量15分钟间隔
*/
public static final int DEFAULT_WINDOW_SIZE = 96;
/**
* 数值计算精度阈值
*/
public static final double EPSILON = 1e-10;
/**
* 协方差计算最小值(避免除零)
*/
public static final double MIN_COVARIANCE = 1e-5;
}

View File

@@ -0,0 +1,27 @@
package com.njcn.product.advance.responsility.pojo.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author hongawen
* @Date: 2019/4/3 13:34
*/
@Data
public class CustomerData implements Serializable {
/***
* 用户名称
*/
private String customerName;
/***
* 每时刻的数据
*/
private List<Float> valueDatas=new ArrayList<>();
}

View File

@@ -0,0 +1,34 @@
package com.njcn.product.advance.responsility.pojo.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author hongawen
* @Date: 2019/4/3 13:35
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerResponsibility implements Serializable {
/***
* 用户名
*/
private String customerName;
/***
* 责任值
*/
private float responsibilityData;
/***
* 监测点id
*/
private String monitorId;
}

View File

@@ -0,0 +1,29 @@
package com.njcn.product.advance.responsility.pojo.dto;
import com.njcn.product.advance.responsility.pojo.po.RespData;
import lombok.Data;
import java.io.Serializable;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月24日 17:49
*/
@Data
public class RespDataDTO extends RespData implements Serializable {
private String userDataName;
private String gdName;
private String subName;
private String devName;
private String ip;
private String lineName;
}

View File

@@ -0,0 +1,53 @@
package com.njcn.product.advance.responsility.pojo.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 谐波责任量化最终结果,包括动态数据和责任量化结果
*
* @author hongawen
* @Date: 2019/4/3 15:00
*/
@Data
public class ResponsibilityResult implements Serializable {
/***
* 限值
*/
private String limitValue;
/***
* 指定起始时间
*/
private String limitSTime;
/***
* 指定结束时间
*/
private String limitETime;
/***
* 责任划分结果存库数据
*/
private String responsibilityDataIndex;
/***
* 每个用户的详细时刻的责任数据
*/
private List<CustomerData> datas;
/***
* 时间轴
*/
private List<Long> timeDatas=new ArrayList<>();
/***
* 用户责任的表格数据
*/
private List<CustomerResponsibility> responsibilities;
}

View File

@@ -0,0 +1,56 @@
package com.njcn.product.advance.responsility.pojo.param;
import com.njcn.common.pojo.constant.PatternRegex;
import com.njcn.product.advance.eventSource.pojo.constant.HarmonicValidMessage;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.util.List;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月19日 09:23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HistoryHarmParam implements Serializable {
@ApiModelProperty("开始时间")
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
@Pattern(regexp = PatternRegex.TIME_FORMAT, message = "时间格式错误")
private String searchBeginTime;
@ApiModelProperty("结束时间")
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
@Pattern(regexp = PatternRegex.TIME_FORMAT, message = "时间格式错误")
private String searchEndTime;
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
@ApiModelProperty("监测点索引")
private String lineId;
@Max(1)
@Min(0)
@ApiModelProperty("0-电流 1-电压")
private int type;
@Max(50)
@Min(2)
@ApiModelProperty("谐波次数")
private Integer time;
}

View File

@@ -0,0 +1,34 @@
package com.njcn.product.advance.responsility.pojo.param;
import com.njcn.common.pojo.constant.PatternRegex;
import com.njcn.product.advance.eventSource.pojo.constant.HarmonicValidMessage;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.List;
/**
* @Author: cdf
* @CreateTime: 2025-09-18
* @Description:
*/
@Data
public class PHistoryHarmParam {
@ApiModelProperty("开始时间")
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
@Pattern(regexp = PatternRegex.TIME_FORMAT, message = "时间格式错误")
private String searchBeginTime;
@ApiModelProperty("结束时间")
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
@Pattern(regexp = PatternRegex.TIME_FORMAT, message = "时间格式错误")
private String searchEndTime;
@NotBlank(message = HarmonicValidMessage.DATA_NOT_BLANK)
@ApiModelProperty("监测点索引")
private List<String> lineIds;
}

View File

@@ -0,0 +1,18 @@
package com.njcn.product.advance.responsility.pojo.param;
import com.njcn.web.pojo.param.BaseParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @Author: cdf
* @CreateTime: 2025-09-09
* @Description:
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RespBaseParam extends BaseParam {
private String deptId;
}

View File

@@ -0,0 +1,57 @@
package com.njcn.product.advance.responsility.pojo.param;
import com.njcn.common.pojo.constant.PatternRegex;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.util.List;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月21日 10:20
*/
@Data
public class ResponsibilityCalculateParam implements Serializable {
@ApiModelProperty("开始时间")
@NotBlank(message = "参数不能为空")
@Pattern(regexp = PatternRegex.TIME_FORMAT, message = "时间格式错误")
private String searchBeginTime;
@ApiModelProperty("结束时间")
@NotBlank(message = "参数不能为空")
@Pattern(regexp = PatternRegex.TIME_FORMAT, message = "时间格式错误")
private String searchEndTime;
@NotBlank(message = "参数不能为空")
@ApiModelProperty("监测点索引")
private String lineId;
@NotBlank(message = "参数不能为空")
@ApiModelProperty("用采数据索引")
private String userDataId;
@Min(0)
@Max(1)
@ApiModelProperty("0-电流 1-电压")
private int type;
@Min(2)
@Max(50)
@ApiModelProperty("谐波次数")
private Integer time;
@ApiModelProperty("背景用户的下级")
private List<String> userList;
@ApiModelProperty("0或者null:配网环境,1.系统环境")
private Integer systemType;
}

View File

@@ -0,0 +1,44 @@
package com.njcn.product.advance.responsility.pojo.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月24日 15:47
*/
@Data
public class ResponsibilitySecondCalParam implements Serializable {
@NotBlank(message = "参数不能为空")
@ApiModelProperty("责任数据索引")
private String resDataId;
@Min(2)
@Max(50)
@ApiModelProperty("谐波次数")
private Integer time;
@Min(0)
@Max(1)
@ApiModelProperty("0-电流 1-电压")
private int type;
@ApiModelProperty("限值")
private float limitValue;
@ApiModelProperty("开始时间(yyyy-MM-dd HH:mm:ss)")
@NotBlank(message = "参数不能为空")
private String limitStartTime;
@ApiModelProperty("结束时间(yyyy-MM-dd HH:mm:ss)")
@NotBlank(message = "参数不能为空")
private String limitEndTime;
}

View File

@@ -0,0 +1,19 @@
package com.njcn.product.advance.responsility.pojo.param;
import com.njcn.web.pojo.param.BaseParam;
import lombok.Data;
import java.io.Serializable;
/**
* @author hongawen
* @version 1.0.0
* @date 2023年07月25日 14:14
*/
@Data
public class UserDataIntegrityParam extends BaseParam implements Serializable {
private String userDataId;
}

View File

@@ -0,0 +1,55 @@
package com.njcn.product.advance.responsility.pojo.po;
import com.baomidou.mybatisplus.annotation.TableName;
import com.njcn.db.mybatisplus.bo.BaseEntity;
import lombok.Getter;
import lombok.Setter;
/**
*
* @author hongawen
* @since 2023-07-21
*/
@Getter
@Setter
@TableName("pqs_resp_data")
public class RespData extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 责任量化数据结果
*/
private String id;
/**
* 监测点索引
*/
private String lineId;
/**
* 用采数据索引
*/
private String userDataId;
/**
* 谐波类型(谐波电压、谐波电流)
*/
private String dataType;
/**
* 谐波次数
*/
private String dataTimes;
/**
* 计算的时间窗口
*/
private String timeWindow;
/**
* 状态0 删除 1正常
*/
private Integer state;
}

View File

@@ -0,0 +1,80 @@
package com.njcn.product.advance.responsility.pojo.po;
import com.baomidou.mybatisplus.annotation.TableName;
import com.njcn.db.mybatisplus.bo.BaseEntity;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author hongawen
* @since 2023-07-24
*/
@Getter
@Setter
@TableName("pqs_resp_data_result")
public class RespDataResult extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 责任划分结果数据文件保存记录表
*/
private String id;
/**
* 责任划分结果表id
*/
private String resDataId;
/**
* 限值
*/
private Float limitValue;
/***
* 起始时间
*/
private Date startTime;
/***
* 结束时间
*/
private Date endTime;
/**
* 谐波次数
*/
private Integer time;
/**
* 用户责任数据地址
*/
private String userDetailData;
/**
* 用户责任时间数据地址
*/
private String timeData;
/**
* 前10用户的每刻对应的责任数据地址
*/
private String userResponsibility;
/**
* 调用高级算法后的数据结果地址,提供二次计算
*/
private String qvvrData;
/**
* 状态0 删除 1正常
*/
private Integer state;
}

View File

@@ -0,0 +1,57 @@
package com.njcn.product.advance.responsility.pojo.po;
import com.baomidou.mybatisplus.annotation.TableName;
import com.njcn.db.mybatisplus.bo.BaseEntity;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
/**
*
* @author hongawen
* @since 2023-07-13
*/
@Getter
@Setter
@TableName("pqs_resp_user_data")
public class RespUserData extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 用采数据表id
*/
private String id;
/**
* 用采数据名称
*/
private String name;
/**
* 起始日期
*/
private LocalDate startTime;
/**
* 截止日期
*/
private LocalDate endTime;
/**
* 0 存在数据不完整的1 存在数据完整
*/
private Integer integrity = 1;
/**
* 用采数据存放地址
*/
private String dataPath;
/**
* 状态0 删除 1正常
*/
private Integer state;
}

View File

@@ -0,0 +1,63 @@
package com.njcn.product.advance.responsility.pojo.po;
import com.baomidou.mybatisplus.annotation.TableName;
import com.njcn.db.mybatisplus.bo.BaseEntity;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
*
* @author hongawen
* @since 2023-07-13
*/
@Getter
@Setter
@TableName("pqs_resp_user_data_integrity")
public class RespUserDataIntegrity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 用采数据完整不足表Id
*/
private String id;
/**
* 用采数据表id
*/
private String userDataId;
/**
* 用户名称
*/
private String userName;
/**
* 用户户号
*/
private String userNo;
/**
* 测量点局号
*/
private String lineNo;
/**
* 数据不完整的日期
*/
private LocalDate lackDate;
/**
* 完整率低于90%会记录)
*/
private BigDecimal integrity;
/**
* 状态0 删除 1正常
*/
private Integer state;
}

View File

@@ -0,0 +1,62 @@
package com.njcn.product.advance.responsility.service;
import com.njcn.product.advance.responsility.model.HarmonicData;
/**
* 谐波责任计算服务接口
*
* @author hongawen
* @version 1.0
*/
public interface IHarmonicResponsibilityService {
/**
* 执行谐波责任计算
*
* @param data 输入的谐波数据
* @return 计算是否成功
*/
boolean calculate(HarmonicData data);
/**
* 执行完整计算
*
* @param harmonicData 谐波数据数组
* @param powerData 功率数据矩阵
* @param harmonicCount 谐波数据个数
* @param powerCount 功率数据个数
* @param nodeCount 节点数量
* @param windowSize 窗口大小
* @param threshold 谐波门槛
* @return 计算结果
*/
HarmonicData fullCalculation(float[] harmonicData, float[][] powerData,
int harmonicCount, int powerCount, int nodeCount,
int windowSize, float threshold);
/**
* 执行部分重算
*
* @param harmonicData 谐波数据数组
* @param fkData FK数据矩阵
* @param hkData HK数据矩阵
* @param harmonicCount 谐波数据个数
* @param nodeCount 节点数量
* @param windowSize 窗口大小
* @param responsibilityCount 责任数据个数
* @param threshold 谐波门槛
* @return 计算结果
*/
HarmonicData partialCalculation(float[] harmonicData, float[][] fkData, float[][] hkData,
int harmonicCount, int nodeCount, int windowSize,
int responsibilityCount, float threshold);
/**
* 验证输入数据的有效性
*
* @param data 待验证的数据
* @return 验证结果消息null表示验证通过
*/
String validateData(HarmonicData data);
}

View File

@@ -0,0 +1,20 @@
package com.njcn.product.advance.responsility.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.product.advance.responsility.pojo.dto.ResponsibilityResult;
import com.njcn.product.advance.responsility.pojo.po.RespDataResult;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author hongawen
* @since 2023-07-24
*/
public interface IRespDataResultService extends IService<RespDataResult> {
List<ResponsibilityResult> displayHistoryData(String id, Integer time);
}

View File

@@ -0,0 +1,38 @@
package com.njcn.product.advance.responsility.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.product.advance.responsility.pojo.dto.RespDataDTO;
import com.njcn.product.advance.responsility.pojo.dto.ResponsibilityResult;
import com.njcn.product.advance.responsility.pojo.param.RespBaseParam;
import com.njcn.product.advance.responsility.pojo.param.ResponsibilityCalculateParam;
import com.njcn.product.advance.responsility.pojo.param.ResponsibilitySecondCalParam;
import com.njcn.product.advance.responsility.pojo.po.RespData;
import com.njcn.web.pojo.param.BaseParam;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author hongawen
* @since 2023-07-21
*/
public interface IRespDataService extends IService<RespData> {
ResponsibilityResult getDynamicDataOld(ResponsibilityCalculateParam responsibilityCalculateParam);
ResponsibilityResult getDynamicData(ResponsibilityCalculateParam responsibilityCalculateParam);
ResponsibilityResult getResponsibilityDataOld(ResponsibilitySecondCalParam responsibilitySecondCalParam);
ResponsibilityResult getResponsibilityData(ResponsibilitySecondCalParam responsibilitySecondCalParam);
Page<RespDataDTO> responsibilityList(RespBaseParam queryParam);
void deleteByIds(List<String> ids);
}

View File

@@ -0,0 +1,20 @@
package com.njcn.product.advance.responsility.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.product.advance.responsility.pojo.param.UserDataIntegrityParam;
import com.njcn.product.advance.responsility.pojo.po.RespUserDataIntegrity;
/**
* <p>
* 服务类
* </p>
*
* @author hongawen
* @since 2023-07-13
*/
public interface IRespUserDataIntegrityService extends IService<RespUserDataIntegrity> {
Page<RespUserDataIntegrity> userDataIntegrityList(UserDataIntegrityParam userDataIntegrityParam);
}

View File

@@ -0,0 +1,40 @@
package com.njcn.product.advance.responsility.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njcn.common.pojo.dto.SelectOption;
import com.njcn.product.advance.responsility.pojo.bo.UserDataExcel;
import com.njcn.product.advance.responsility.pojo.po.RespUserData;
import com.njcn.web.pojo.param.BaseParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author hongawen
* @since 2023-07-13
*/
public interface IRespUserDataService extends IService<RespUserData> {
/**
* 解析用采数据并保存
* @author hongawen
* @date 2023/7/13 19:48
* @param file 用采数据
*/
void uploadUserData(MultipartFile file, HttpServletResponse response);
Page<RespUserData> userDataList(BaseParam queryParam);
List<SelectOption> userDataSelect();
void deleteUserDataByIds(List<String> ids);
List<UserDataExcel> getUserDataExcelList(String userDataId);
}

View File

@@ -0,0 +1,163 @@
package com.njcn.product.advance.responsility.service.impl;
import com.njcn.product.advance.responsility.calculator.HarmonicCalculationEngine;
import com.njcn.product.advance.responsility.model.HarmonicData;
import com.njcn.product.advance.responsility.pojo.constant.CalculationMode;
import com.njcn.product.advance.responsility.pojo.constant.HarmonicConstants;
import com.njcn.product.advance.responsility.service.IHarmonicResponsibilityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* 谐波责任计算服务实现类
*
* @author hongawen
* @version 1.0
*/
@Service
public class HarmonicResponsibilityServiceImpl implements IHarmonicResponsibilityService {
private static final Logger logger = LoggerFactory.getLogger(HarmonicResponsibilityServiceImpl.class);
private final HarmonicCalculationEngine engine;
public HarmonicResponsibilityServiceImpl() {
this.engine = new HarmonicCalculationEngine();
}
@Override
public boolean calculate(HarmonicData data) {
if (data == null) {
logger.error("Input data is null");
return false;
}
String validationError = validateData(data);
if (validationError != null) {
logger.error("Data validation failed: {}", validationError);
return false;
}
long startTime = System.currentTimeMillis();
boolean result = engine.calculate(data);
long endTime = System.currentTimeMillis();
logger.info("Calculation completed in {} ms, result: {}", (endTime - startTime), result);
return result;
}
@Override
public HarmonicData fullCalculation(float[] harmonicData, float[][] powerData,
int harmonicCount, int powerCount, int nodeCount,
int windowSize, float threshold) {
logger.info("Starting full calculation with harmonicCount={}, powerCount={}, nodeCount={}, windowSize={}",
harmonicCount, powerCount, nodeCount, windowSize);
HarmonicData data = new HarmonicData.Builder()
.calculationMode(CalculationMode.FULL_CALCULATION)
.harmonicCount(harmonicCount)
.powerCount(powerCount)
.powerNodeCount(nodeCount)
.windowSize(windowSize)
.harmonicThreshold(threshold)
.harmonicData(harmonicData)
.powerData(powerData)
.build();
calculate(data);
return data;
}
@Override
public HarmonicData partialCalculation(float[] harmonicData, float[][] fkData, float[][] hkData,
int harmonicCount, int nodeCount, int windowSize,
int responsibilityCount, float threshold) {
logger.info("Starting partial calculation with harmonicCount={}, nodeCount={}, windowSize={}, responsibilityCount={}",
harmonicCount, nodeCount, windowSize, responsibilityCount);
HarmonicData data = new HarmonicData();
data.setCalculationMode(CalculationMode.PARTIAL_RECALCULATION);
data.setHarmonicCount(harmonicCount);
data.setPowerNodeCount(nodeCount);
data.setWindowSize(windowSize);
data.setResponsibilityDataCount(responsibilityCount);
data.setHarmonicThreshold(threshold);
data.setHarmonicData(harmonicData);
data.setFkData(fkData);
data.setHkData(hkData);
// 初始化输出数组
data.setSumFKData(new float[nodeCount]);
data.setSumHKData(new float[nodeCount + 1]);
calculate(data);
return data;
}
@Override
public String validateData(HarmonicData data) {
if (data == null) {
return "Data object is null";
}
// 验证基本参数
if (data.getHarmonicCount() <= 0 || data.getHarmonicCount() > HarmonicConstants.MAX_HARM_NUM) {
return String.format("Invalid harmonic count: %d (should be 1-%d)",
data.getHarmonicCount(), HarmonicConstants.MAX_HARM_NUM);
}
if (data.getCalculationMode() == CalculationMode.FULL_CALCULATION) {
// 完整计算模式验证
if (data.getPowerCount() <= 0 || data.getPowerCount() > HarmonicConstants.MAX_P_NUM) {
return String.format("Invalid power count: %d (should be 1-%d)",
data.getPowerCount(), HarmonicConstants.MAX_P_NUM);
}
if (data.getPowerNodeCount() <= 0 || data.getPowerNodeCount() > HarmonicConstants.MAX_P_NODE) {
return String.format("Invalid power node count: %d (should be 1-%d)",
data.getPowerNodeCount(), HarmonicConstants.MAX_P_NODE);
}
// 验证数据对齐
int ratio = data.getHarmonicCount() / data.getPowerCount();
if (ratio * data.getPowerCount() != data.getHarmonicCount()) {
return String.format("Harmonic count %d is not aligned with power count %d",
data.getHarmonicCount(), data.getPowerCount());
}
// 验证数据数组
if (data.getHarmonicData() == null || data.getHarmonicData().length < data.getHarmonicCount()) {
return "Harmonic data array is null or insufficient";
}
if (data.getPowerData() == null || data.getPowerData().length < data.getPowerCount()) {
return "Power data array is null or insufficient";
}
} else if (data.getCalculationMode() == CalculationMode.PARTIAL_RECALCULATION) {
// 部分计算模式验证
if (data.getResponsibilityDataCount() + data.getWindowSize() != data.getHarmonicCount()) {
return String.format("Data length mismatch: resNum(%d) + winSize(%d) != harmCount(%d)",
data.getResponsibilityDataCount(), data.getWindowSize(), data.getHarmonicCount());
}
if (data.getFkData() == null || data.getHkData() == null) {
return "FK or HK data is null for partial calculation";
}
}
// 验证窗口大小
if (data.getWindowSize() < HarmonicConstants.MIN_WIN_LEN ||
data.getWindowSize() > HarmonicConstants.MAX_WIN_LEN) {
return String.format("Invalid window size: %d (should be %d-%d)",
data.getWindowSize(), HarmonicConstants.MIN_WIN_LEN, HarmonicConstants.MAX_WIN_LEN);
}
return null; // 验证通过
}
}

View File

@@ -0,0 +1,88 @@
package com.njcn.product.advance.responsility.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.product.advance.responsility.mapper.RespDataResultMapper;
import com.njcn.product.advance.responsility.pojo.dto.CustomerData;
import com.njcn.product.advance.responsility.pojo.dto.CustomerResponsibility;
import com.njcn.product.advance.responsility.pojo.dto.ResponsibilityResult;
import com.njcn.product.advance.responsility.pojo.po.RespData;
import com.njcn.product.advance.responsility.pojo.po.RespDataResult;
import com.njcn.product.advance.responsility.service.IRespDataResultService;
import com.njcn.product.advance.responsility.service.IRespDataService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* <p>
* 服务实现类
* </p>
*
* @author hongawen
* @since 2023-07-24
*/
@Service
public class RespDataResultServiceImpl extends ServiceImpl<RespDataResultMapper, RespDataResult> implements IRespDataResultService {
@Resource
private FileStorageUtil fileStorageUtil;
@Lazy
@Resource
private IRespDataService respDataService;
@Override
public List<ResponsibilityResult> displayHistoryData(String id, Integer time) {
List<ResponsibilityResult> responsibilityResults = new ArrayList<>();
if (Objects.isNull(time)) {
RespData respData = respDataService.getById(id);
String[] split = respData.getDataTimes().split(StrPool.COMMA);
time = Integer.parseInt(split[0]);
}
LambdaQueryWrapper<RespDataResult> respDataResultLambdaQueryWrapper = new LambdaQueryWrapper<>();
respDataResultLambdaQueryWrapper.eq(RespDataResult::getResDataId, id)
.eq(RespDataResult::getTime, time);
List<RespDataResult> respDataResults = this.baseMapper.selectList(respDataResultLambdaQueryWrapper);
if (CollectionUtil.isNotEmpty(respDataResults)) {
ResponsibilityResult responsibilityResult;
for (RespDataResult respDataResult : respDataResults) {
responsibilityResult = new ResponsibilityResult();
responsibilityResult.setLimitValue(String.valueOf(respDataResult.getLimitValue()));
responsibilityResult.setLimitSTime(DateUtil.format(respDataResult.getStartTime(), DatePattern.NORM_DATETIME_PATTERN));
responsibilityResult.setLimitETime(DateUtil.format(respDataResult.getEndTime(), DatePattern.NORM_DATETIME_PATTERN));
responsibilityResult.setResponsibilityDataIndex(respDataResult.getResDataId());
//处理时间轴数据
InputStream timeDataStream = fileStorageUtil.getFileStream(respDataResult.getTimeData());
String timeDataStr = IoUtil.readUtf8(timeDataStream);
List<Long> timeData = JSONArray.parseArray(timeDataStr, Long.class);
responsibilityResult.setTimeDatas(timeData);
//处理用户详细数据
InputStream userDetailStream = fileStorageUtil.getFileStream(respDataResult.getUserDetailData());
String userDetailStr = IoUtil.readUtf8(userDetailStream);
List<CustomerData> customerData = JSONArray.parseArray(userDetailStr, CustomerData.class);
responsibilityResult.setDatas(customerData);
//处理排名前10数据
InputStream respStream = fileStorageUtil.getFileStream(respDataResult.getUserResponsibility());
String respStr = IoUtil.readUtf8(respStream);
List<CustomerResponsibility> respData = JSONArray.parseArray(respStr, CustomerResponsibility.class);
responsibilityResult.setResponsibilities(respData);
responsibilityResults.add(responsibilityResult);
}
}
return responsibilityResults;
}
}

View File

@@ -0,0 +1,33 @@
package com.njcn.product.advance.responsility.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.product.advance.responsility.mapper.RespUserDataIntegrityMapper;
import com.njcn.product.advance.responsility.pojo.param.UserDataIntegrityParam;
import com.njcn.product.advance.responsility.pojo.po.RespUserDataIntegrity;
import com.njcn.product.advance.responsility.service.IRespUserDataIntegrityService;
import com.njcn.web.factory.PageFactory;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author hongawen
* @since 2023-07-13
*/
@Service
public class RespUserDataIntegrityServiceImpl extends ServiceImpl<RespUserDataIntegrityMapper, RespUserDataIntegrity> implements IRespUserDataIntegrityService {
@Override
public Page<RespUserDataIntegrity> userDataIntegrityList(UserDataIntegrityParam userDataIntegrityParam) {
QueryWrapper<RespUserDataIntegrity> lambdaQueryWrapper = new QueryWrapper<>();
lambdaQueryWrapper.eq("pqs_resp_user_data_integrity.user_data_id", userDataIntegrityParam.getUserDataId())
.orderByDesc("pqs_resp_user_data_integrity.create_time").like("pqs_resp_user_data_integrity.user_name",userDataIntegrityParam.getSearchValue());
return this.baseMapper.page(new Page<>(PageFactory.getPageNum(userDataIntegrityParam), PageFactory.getPageSize(userDataIntegrityParam)), lambdaQueryWrapper);
}
}

View File

@@ -0,0 +1,486 @@
package com.njcn.product.advance.responsility.service.impl;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.handler.inter.IReadHandler;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.dto.SelectOption;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.FileUtil;
import com.njcn.common.utils.PubUtils;
import com.njcn.db.mybatisplus.constant.DbConstant;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.product.advance.responsility.mapper.RespUserDataMapper;
import com.njcn.product.advance.responsility.pojo.bo.DealDataResult;
import com.njcn.product.advance.responsility.pojo.bo.DealUserDataResult;
import com.njcn.product.advance.responsility.pojo.bo.UserDataExcel;
import com.njcn.product.advance.eventSource.pojo.enums.AdvanceResponseEnum;
import com.njcn.product.advance.responsility.pojo.po.RespUserData;
import com.njcn.product.advance.responsility.pojo.po.RespUserDataIntegrity;
import com.njcn.product.advance.responsility.service.IRespUserDataIntegrityService;
import com.njcn.product.advance.responsility.service.IRespUserDataService;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.pojo.param.BaseParam;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*
* @author hongawen
* @since 2023-07-13
*/
@Service
@RequiredArgsConstructor
public class RespUserDataServiceImpl extends ServiceImpl<RespUserDataMapper, RespUserData> implements IRespUserDataService {
private final FileStorageUtil fileStorageUtil;
private final IRespUserDataIntegrityService respUserDataIntegrityService;
@Override
public void uploadUserData(MultipartFile file, HttpServletResponse response) {
ImportParams params = new ImportParams();
List<UserDataExcel> userDataExcels = new ArrayList<>();
try {
// 显式设置SAX解析器如果添加依赖后仍有问题
System.setProperty("org.xml.sax.driver", "org.apache.xerces.parsers.SAXParser");
ExcelImportUtil.importExcelBySax(file.getInputStream(), UserDataExcel.class, params, new IReadHandler<UserDataExcel>() {
@Override
public void handler(UserDataExcel o) {
userDataExcels.add(o);
}
@Override
public void doAfterAll() {
}
});
//处理用户上传的用采数据内容
analysisUserData(userDataExcels, file.getOriginalFilename());
} catch (Exception e) {
throw new BusinessException(AdvanceResponseEnum.ANALYSIS_USER_DATA_ERROR);
}
}
@Override
public Page<RespUserData> userDataList(BaseParam queryParam) {
QueryWrapper<RespUserData> respUserDataQueryWrapper = new QueryWrapper<>();
if (ObjectUtil.isNotNull(queryParam)) {
//查询参数不为空,进行条件填充
if (StrUtil.isNotBlank(queryParam.getSearchValue())) {
//仅提供用采名称
respUserDataQueryWrapper.and(param -> param.like("pqs_resp_user_data.name", queryParam.getSearchValue()));
}
//排序
if (ObjectUtil.isAllNotEmpty(queryParam.getSortBy(), queryParam.getOrderBy())) {
respUserDataQueryWrapper.orderBy(true, queryParam.getOrderBy().equals(DbConstant.ASC), StrUtil.toUnderlineCase(queryParam.getSortBy()));
} else {
//没有排序参数默认根据sort字段排序没有排序字段的根据updateTime更新时间排序
respUserDataQueryWrapper.orderBy(true, false, "pqs_resp_user_data.update_time");
}
} else {
respUserDataQueryWrapper.orderBy(true, false, "pqs_resp_user_data.update_time");
}
respUserDataQueryWrapper.eq("pqs_resp_user_data.state", DataStateEnum.ENABLE.getCode());
return this.baseMapper.page(new Page<>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)), respUserDataQueryWrapper);
}
@Override
public List<SelectOption> userDataSelect() {
List<SelectOption> selectOptions = new ArrayList<>();
LambdaQueryWrapper<RespUserData> respUserDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
respUserDataLambdaQueryWrapper.eq(RespUserData::getState, DataStateEnum.ENABLE.getCode())
.orderByDesc(RespUserData::getUpdateTime);
List<RespUserData> respUserData = this.baseMapper.selectList(respUserDataLambdaQueryWrapper);
if (CollectionUtil.isNotEmpty(respUserData)) {
selectOptions = respUserData.stream().map(temp -> new SelectOption(temp.getName(), temp.getId())).collect(Collectors.toList());
}
return selectOptions;
}
@Override
public void deleteUserDataByIds(List<String> ids) {
this.baseMapper.deleteUserDataByIds(ids);
}
@Override
public List<UserDataExcel> getUserDataExcelList(String userDataId) {
LambdaQueryWrapper<RespUserData> userDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
userDataLambdaQueryWrapper.eq(RespUserData::getId, userDataId).eq(RespUserData::getState, DataStateEnum.ENABLE.getCode());
RespUserData respUserData = this.getOne(userDataLambdaQueryWrapper);
if (Objects.isNull(respUserData)) {
throw new BusinessException(AdvanceResponseEnum.USER_DATA_NOT_FOUND);
}
InputStream fileStream = fileStorageUtil.getFileStream(respUserData.getDataPath());
String excelDataStr = IoUtil.read(fileStream, CharsetUtil.UTF_8);
//将文件流转为list集合
List<UserDataExcel> userDataExcels = JSONArray.parseArray(excelDataStr, UserDataExcel.class);
if (CollectionUtils.isEmpty(userDataExcels)) {
throw new BusinessException(AdvanceResponseEnum.USER_DATA_NOT_FOUND);
}
return userDataExcels;
}
/**
* 根据流获取出用采有功功率数据
*/
private void analysisUserData(List<UserDataExcel> userDataExcelList, String fileName) {
List<UserDataExcel> exportExcelList = new ArrayList<>();
RespUserData respUserData;
//判断数据提取情况
if (CollectionUtils.isEmpty(userDataExcelList)) {
throw new BusinessException(AdvanceResponseEnum.USER_DATA_EMPTY);
}
DealDataResult dealDataResult = getStanderData(userDataExcelList, 0);
Map<String/*户号@监测点号@户名*/, Map<String/*yyyy-MM-dd天日期*/, Map<Date/*yyyy-MM-dd HH:mm:ss日期格式*/, UserDataExcel>>> totalData = dealDataResult.getTotalData();
//收集所有的日期,以便获取起始日期和截止日期
List<String> dates = dealDataResult.getDates();
//将前面获取出来的日期进行排序,提供入库
List<LocalDate> resultDates = getSortDate(dates);
LocalDate endTime = resultDates.get(resultDates.size() - 1);
LocalDate startTime = resultDates.get(0);
//针对每个用户的数据进行完整度的判断 todo 暂且认为所有用户的时间跨度是一样的比如都是15天或者都是30天不存在有的用户5天数据有的用户10天数据
Map<String, List<UserDataExcel>> tempResult = new HashMap<>();
List<RespUserDataIntegrity> respUserDataIntegrities = new ArrayList<>();
Set<String> userNames = totalData.keySet();
for (String name : userNames) {
Map<String, Map<Date, UserDataExcel>> userDataTemp = totalData.get(name);
//现在数据拿到了但是因为是hashkey所以日期顺序是乱的-->怎么变成有序的呢
Set<String> times = userDataTemp.keySet();
//循环日期处理数据
for (String time : times) {
DealUserDataResult dealtData = dealUserData(name, userDataTemp.get(time), time, 15);
List<UserDataExcel> UserDataExcelTemp = dealtData.getCompleted();
List<UserDataExcel> UserDataExcel;
if (CollectionUtils.isEmpty(UserDataExcelTemp) && Objects.nonNull(dealtData.getRespUserDataIntegrity())) {
//为空,说明补齐操作没有进行,选择填充缺失数据即可
respUserDataIntegrities.add(dealtData.getRespUserDataIntegrity());
UserDataExcel = dealtData.getLack();
} else {
//填充补齐完整性后的数据
UserDataExcel = UserDataExcelTemp;
}
List<UserDataExcel> userDatas = tempResult.get(name);
if (CollectionUtil.isNotEmpty(UserDataExcel)) {
if (CollectionUtils.isEmpty(userDatas)) {
userDatas = new ArrayList<>(UserDataExcel);
} else {
userDatas.addAll(UserDataExcel);
}
}
tempResult.put(name, userDatas);
}
}
//完成后,开始将数据按公司排序,然后输出到指定表格中,方便下次使用
for (String name : userNames) {
List<UserDataExcel> tempUserData = tempResult.get(name);
//按时间排序
Collections.sort(tempUserData);
exportExcelList.addAll(tempUserData);
}
//输出到报表中
String fileNameWithOutSuffix = fileName.substring(0, fileName.indexOf('.'));
fileNameWithOutSuffix = fileNameWithOutSuffix.concat(LocalDateTimeUtil.format(startTime, DatePattern.PURE_DATE_PATTERN)).concat(StrPool.DASHED).concat(LocalDateTimeUtil.format(endTime, DatePattern.PURE_DATE_PATTERN));
//处理完后的用采数据生成json文件流到oss服务器
JSONArray finalUserData = JSONArray.parseArray(JSON.toJSONString(exportExcelList));
InputStream reportStream = IoUtil.toStream(finalUserData.toString(), CharsetUtil.UTF_8);
String ossPath = fileStorageUtil.uploadStream(reportStream, OssPath.RESPONSIBILITY_USER_DATA, FileUtil.generateFileName("json"));
//入库前进行查询操作,存在则更新,不存在则插入
LambdaQueryWrapper<RespUserData> respUserDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
respUserDataLambdaQueryWrapper.eq(RespUserData::getName, fileNameWithOutSuffix)
.eq(RespUserData::getStartTime, startTime)
.eq(RespUserData::getEndTime, endTime)
.eq(RespUserData::getState, DataStateEnum.ENABLE.getCode());
respUserData = this.baseMapper.selectOne(respUserDataLambdaQueryWrapper);
//不存在则插入
if (Objects.isNull(respUserData)) {
respUserData = new RespUserData();
respUserData.setEndTime(endTime);
respUserData.setStartTime(startTime);
respUserData.setName(fileNameWithOutSuffix);
respUserData.setDataPath(ossPath);
respUserData.setState(DataStateEnum.ENABLE.getCode());
this.baseMapper.insert(respUserData);
if (CollectionUtil.isNotEmpty(respUserDataIntegrities)) {
//关联插入数据 户号,监测点号,户名,时间,完整性
for (RespUserDataIntegrity respUserDataIntegrity : respUserDataIntegrities) {
respUserDataIntegrity.setUserDataId(respUserData.getId());
}
//插入操作
respUserDataIntegrityService.saveBatch(respUserDataIntegrities);
respUserData.setIntegrity(1);
} else {
respUserData.setIntegrity(0);
}
this.baseMapper.updateById(respUserData);
} else {
//存在则更新,需要删除之前的oss文件
fileStorageUtil.deleteFile(respUserData.getDataPath());
if (CollectionUtil.isNotEmpty(respUserDataIntegrities)) {
LambdaQueryWrapper<RespUserDataIntegrity> respUserDataIntegrityLambdaQueryWrapper = new LambdaQueryWrapper<>();
respUserDataIntegrityLambdaQueryWrapper.eq(RespUserDataIntegrity::getUserDataId, respUserData.getId());
respUserDataIntegrityService.remove(respUserDataIntegrityLambdaQueryWrapper);
for (RespUserDataIntegrity respUserDataIntegrity : respUserDataIntegrities) {
respUserDataIntegrity.setUserDataId(respUserData.getId());
}
//插入操作
respUserDataIntegrityService.saveBatch(respUserDataIntegrities);
respUserData.setIntegrity(1);
} else {
respUserData.setIntegrity(0);
}
respUserData.setDataPath(ossPath);
this.baseMapper.updateById(respUserData);
}
}
/**
* 解析用采数据为一个标准格式
*/
public static DealDataResult getStanderData(List<UserDataExcel> userDataExcelBodies, int flag) {
DealDataResult result = new DealDataResult();
//收集所有的日期,以便获取起始日期和截止日期
List<String> dates = new ArrayList<>();
Map<String, Map<String, Map<Date, UserDataExcel>>> totalData = new HashMap<>();
Map<String, Map<String, List<UserDataExcel>>> totalListData = new HashMap<>();
for (UserDataExcel UserDataExcel : userDataExcelBodies) {
//第一个key
String key = UserDataExcel.getUserId() + "@" + UserDataExcel.getLine() + "@" + UserDataExcel.getUserName();
String time = UserDataExcel.getTime().substring(0, 10);
if (!dates.contains(time)) {
dates.add(time);
}
if (!totalData.containsKey(key)) {
if (flag == 0) {
//Map形式避免后面补齐数据嵌套循环
Map<Date, UserDataExcel> userDatas = new HashMap<>();
userDatas.put(PubUtils.getSecondsAsZero(DateUtil.parse(UserDataExcel.getTime(), DatePattern.NORM_DATETIME_PATTERN)), UserDataExcel);
Map<String, Map<Date, UserDataExcel>> dataToUserDatas = new HashMap<>();
dataToUserDatas.put(time, userDatas);
totalData.put(key, dataToUserDatas);
} else if (flag == 1) {
//List形式,避免后面责任数据提取嵌套循环
List<UserDataExcel> userListDatas = new ArrayList<>();
userListDatas.add(UserDataExcel);
Map<String, List<UserDataExcel>> dataToUserListDatas = new HashMap<>();
dataToUserListDatas.put(time, userListDatas);
totalData.put(key, new HashMap<>());
totalListData.put(key, dataToUserListDatas);
}
} else {
if (flag == 0) {
//Map形式避免后面补齐数据嵌套循环
Map<String, Map<Date, UserDataExcel>> dataToUserDatas = totalData.get(key);
Map<Date, UserDataExcel> userDatas = dataToUserDatas.get(time);
//某日凌晨,还没存放该日的数据
if (CollectionUtils.isEmpty(userDatas)) {
userDatas = new HashMap<>();
userDatas.put(PubUtils.getSecondsAsZero(DateUtil.parse(UserDataExcel.getTime(), DatePattern.NORM_DATETIME_PATTERN)), UserDataExcel);
dataToUserDatas.put(time, userDatas);
} else {
//累加该日的数据
userDatas.put(PubUtils.getSecondsAsZero(DateUtil.parse(UserDataExcel.getTime(), DatePattern.NORM_DATETIME_PATTERN)), UserDataExcel);
dataToUserDatas.put(time, userDatas);
}
totalData.put(key, dataToUserDatas);
} else if (flag == 1) {
//List形式,避免后面责任数据提取嵌套循环
Map<String, List<UserDataExcel>> dataToUserListDatas = totalListData.get(key);
List<UserDataExcel> userListDatas = dataToUserListDatas.get(time);
if (CollectionUtils.isEmpty(userListDatas)) {
userListDatas = new ArrayList<>();
userListDatas.add(UserDataExcel);
dataToUserListDatas.put(time, userListDatas);
} else {
userListDatas.add(UserDataExcel);
dataToUserListDatas.put(time, userListDatas);
}
totalListData.put(key, dataToUserListDatas);
}
}
}
result.setDates(dates);
result.setTotalData(totalData);
result.setTotalListData(totalListData);
return result;
}
/**
* 将日期排序后返回
*/
private List<LocalDate> getSortDate(List<String> dates) {
List<LocalDate> result = new ArrayList<>();
for (String date : dates) {
LocalDate temp = LocalDateTimeUtil.parseDate(date, DatePattern.NORM_DATE_PATTERN);
result.add(temp);
}
if (!CollectionUtils.isEmpty(result)) {
Collections.sort(result);
}
return result;
}
/**
* 处理用户每日数据
*
* @param name 用户名
* @param beforeDeal 处理前的用户数据
*/
private DealUserDataResult dealUserData(String name, Map<Date, UserDataExcel> beforeDeal, String time, int step) {
DealUserDataResult result = new DealUserDataResult();
String[] userFlag = name.split("@");
//每天的最开是的数据是从00:00:00开始的所以起始时间为time + 00:00:00
List<UserDataExcel> completed = new ArrayList<>();
List<UserDataExcel> lack = new ArrayList<>();
if (CollectionUtils.isEmpty(beforeDeal)) {
return result;
} else {
String timeTemp = time + " 00:00:00";
Date date = DateUtil.parse(timeTemp, DatePattern.NORM_DATETIME_PATTERN);
int count = 24 * 60 / 15;
if ((float) beforeDeal.size() / (float) count < 0.9) {
Set<Date> dates = beforeDeal.keySet();
for (Date tempDate : dates) {
UserDataExcel UserDataExcel = beforeDeal.get(tempDate);
if (UserDataExcel.getWork() != null) {
lack.add(UserDataExcel);
}
}
RespUserDataIntegrity respUserDataIntegrity = new RespUserDataIntegrity();
respUserDataIntegrity.setIntegrity(BigDecimal.valueOf((double) lack.size() / 96.0));
respUserDataIntegrity.setLackDate(LocalDateTimeUtil.parseDate(time, DatePattern.NORM_DATE_PATTERN));
respUserDataIntegrity.setUserName(userFlag[2]);
respUserDataIntegrity.setLineNo(userFlag[1]);
respUserDataIntegrity.setUserNo(userFlag[0]);
result.setLack(lack);
result.setRespUserDataIntegrity(respUserDataIntegrity);
return result;
} else {
for (int i = 0; i < count; i++) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, step * i);
UserDataExcel UserDataExcel = beforeDeal.get(calendar.getTime());
if (UserDataExcel != null && UserDataExcel.getWork() != null) {
completed.add(UserDataExcel);
} else {
//找到前一个时间点值
Float perValue = getPreValue(date, calendar.getTime(), beforeDeal);
//找到后一个时间点值
Float appendValue = getAppendValue(date, count, step, calendar.getTime(), beforeDeal);
UserDataExcel temp = new UserDataExcel();
SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
temp.setTime(sdf.format(calendar.getTime()));
temp.setUserId(userFlag[0]);
temp.setLine(userFlag[1]);
temp.setUserName(userFlag[2]);
//还需要判断前值和后值为空的情况
if (null == perValue && null == appendValue) {
temp.setWork(new BigDecimal("0.0"));
} else if (null == perValue) {
temp.setWork(new BigDecimal(appendValue));
} else if (null == appendValue) {
temp.setWork(new BigDecimal(perValue));
} else {
temp.setWork(BigDecimal.valueOf((perValue + appendValue) / 2));
}
completed.add(temp);
}
}
}
}
result.setCompleted(completed);
return result;
}
/**
* 递归找前值
*
* @param date 起始时间
* @param time 当前时间
* @param beforeDeal 处理前的数据
*/
private Float getPreValue(Date date, Date time, Map<Date, UserDataExcel> beforeDeal) {
Float result;
if (date.equals(time)) {
return null;
} else {
Calendar calendar = Calendar.getInstance();
calendar.setTime(time);
calendar.add(Calendar.MINUTE, -15);
UserDataExcel temp = beforeDeal.get(calendar.getTime());
if (temp == null || temp.getWork() == null) {
result = getPreValue(date, calendar.getTime(), beforeDeal);
} else {
result = temp.getWork().floatValue();
}
}
return result;
}
/**
* 递归找后置
*
* @param date 起始时间
* @param count 一天时间的总计数
* @param step 间隔分钟
* @param time 截止时间
*/
private Float getAppendValue(Date date, int count, int step, Date time, Map<Date, UserDataExcel> beforeDeal) {
Float result;
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MINUTE, (count - 1) * step);
if (time.equals(calendar.getTime())) {
return null;
} else {
Calendar calendar1 = Calendar.getInstance();
calendar1.setTime(time);
calendar1.add(Calendar.MINUTE, 15);
UserDataExcel temp = beforeDeal.get(calendar1.getTime());
if (temp == null || temp.getWork() == null) {
result = getAppendValue(date, count, step, calendar1.getTime(), beforeDeal);
} else {
result = temp.getWork().floatValue();
}
}
return result;
}
}

View File

@@ -0,0 +1,314 @@
package com.njcn.product.advance.responsility.utils;
import com.njcn.product.advance.responsility.pojo.constant.HarmonicConstants;
import org.apache.commons.math3.linear.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 数学工具类
* 提供基础数学计算功能
*
* @author hongawen
* @version 1.0
*/
public class MathUtils {
private static final Logger logger = LoggerFactory.getLogger(MathUtils.class);
/**
* 计算协方差
*
* @param x 数据序列1
* @param y 数据序列2
* @param width 数据窗宽度
* @return 协方差值
*/
public static double covariance(double[] x, double[] y, int width) {
if (x == null || y == null || width <= 0) {
throw new IllegalArgumentException("Invalid input parameters for covariance calculation");
}
if (x.length < width || y.length < width) {
throw new IllegalArgumentException("Data length is less than window width");
}
double meanX = 0.0;
double meanY = 0.0;
// 计算均值
for (int i = 0; i < width; i++) {
meanX += x[i];
meanY += y[i];
}
meanX /= width;
meanY /= width;
// 计算协方差
double cov = 0.0;
for (int i = 0; i < width; i++) {
cov += (x[i] - meanX) * (y[i] - meanY);
}
return cov / (width - 1);
}
/**
* 计算协方差float版本
*/
public static float covariance(float[] x, float[] y, int width) {
double[] dx = new double[x.length];
double[] dy = new double[y.length];
for (int i = 0; i < x.length; i++) dx[i] = x[i];
for (int i = 0; i < y.length; i++) dy[i] = y[i];
return (float) covariance(dx, dy, width);
}
/**
* 计算Pearson相关系数
*
* @param x 数据序列1
* @param y 数据序列2
* @param count 数据长度
* @return Pearson相关系数
*/
public static double pearsonCorrelation(double[] x, double[] y, int count) {
if (x == null || y == null || count <= 0) {
throw new IllegalArgumentException("Invalid input parameters for Pearson correlation");
}
double meanX = 0.0;
double meanY = 0.0;
// 计算均值
for (int i = 0; i < count; i++) {
meanX += x[i];
meanY += y[i];
}
meanX /= count;
meanY /= count;
// 计算相关系数的各个部分
double numerator = 0.0;
double denomX = 0.0;
double denomY = 0.0;
for (int i = 0; i < count; i++) {
double dx = x[i] - meanX;
double dy = y[i] - meanY;
numerator += dx * dy;
denomX += dx * dx;
denomY += dy * dy;
}
double denominator = Math.sqrt(denomX * denomY);
if (Math.abs(denominator) < HarmonicConstants.EPSILON) {
logger.warn("Denominator is too small in Pearson correlation calculation");
return 0.0;
}
return numerator / denominator;
}
/**
* 计算Pearson相关系数float版本
*/
public static float pearsonCorrelation(float[] x, float[] y, int count) {
double[] dx = new double[count];
double[] dy = new double[count];
for (int i = 0; i < count; i++) {
dx[i] = x[i];
dy[i] = y[i];
}
return (float) pearsonCorrelation(dx, dy, count);
}
/**
* 计算协方差矩阵SXX
*
* @param data 数据矩阵 [时间][节点]
* @param width 窗口宽度
* @param nodeCount 节点数
* @return 协方差矩阵
*/
public static double[][] covarianceMatrix(double[][] data, int width, int nodeCount) {
double[][] covMatrix = new double[nodeCount][nodeCount];
for (int i = 0; i < nodeCount; i++) {
for (int j = 0; j < nodeCount; j++) {
double[] col1 = new double[width];
double[] col2 = new double[width];
for (int k = 0; k < width; k++) {
col1[k] = data[k][i];
col2[k] = data[k][j];
}
covMatrix[i][j] = covariance(col1, col2, width);
}
}
return covMatrix;
}
/**
* 计算协方差矩阵float版本
*/
public static float[][] covarianceMatrix(float[][] data, int width, int nodeCount) {
float[][] covMatrix = new float[nodeCount][nodeCount];
for (int i = 0; i < nodeCount; i++) {
for (int j = 0; j < nodeCount; j++) {
float[] col1 = new float[width];
float[] col2 = new float[width];
for (int k = 0; k < width; k++) {
col1[k] = data[k][i];
col2[k] = data[k][j];
}
covMatrix[i][j] = covariance(col1, col2, width);
}
}
return covMatrix;
}
/**
* 计算协方差向量SXY
*
* @param data 数据矩阵 [时间][节点]
* @param y 目标向量
* @param width 窗口宽度
* @param nodeCount 节点数
* @return 协方差向量
*/
public static double[] covarianceVector(double[][] data, double[] y, int width, int nodeCount) {
double[] covVector = new double[nodeCount];
for (int i = 0; i < nodeCount; i++) {
double[] col = new double[width];
for (int k = 0; k < width; k++) {
col[k] = data[k][i];
}
covVector[i] = covariance(col, y, width);
}
return covVector;
}
/**
* 计算协方差向量float版本
*/
public static float[] covarianceVector(float[][] data, float[] y, int width, int nodeCount) {
float[] covVector = new float[nodeCount];
for (int i = 0; i < nodeCount; i++) {
float[] col = new float[width];
for (int k = 0; k < width; k++) {
col[k] = data[k][i];
}
covVector[i] = covariance(col, y, width);
}
return covVector;
}
/**
* 矩阵求逆
* 使用Apache Commons Math库
*
* @param matrix 输入矩阵
* @return 逆矩阵
*/
public static double[][] matrixInverse(double[][] matrix) {
RealMatrix realMatrix = new Array2DRowRealMatrix(matrix);
try {
// 使用LU分解求逆
DecompositionSolver solver = new LUDecomposition(realMatrix).getSolver();
RealMatrix inverseMatrix = solver.getInverse();
return inverseMatrix.getData();
} catch (SingularMatrixException e) {
logger.error("Matrix is singular, cannot compute inverse", e);
throw new RuntimeException("Matrix inversion failed: singular matrix");
}
}
/**
* 计算矩阵的特征值
*
* @param matrix 输入矩阵
* @return 特征值数组
*/
public static double[] eigenvalues(double[][] matrix) {
RealMatrix realMatrix = new Array2DRowRealMatrix(matrix);
EigenDecomposition eigenDecomposition = new EigenDecomposition(realMatrix);
return eigenDecomposition.getRealEigenvalues();
}
/**
* 归一化处理
* 将数据归一化到[0,1]区间
*
* @param data 输入数据
* @return 归一化后的数据
*/
public static double[] normalize(double[] data) {
if (data == null || data.length == 0) {
return data;
}
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
// 找最大最小值
for (double value : data) {
min = Math.min(min, value);
max = Math.max(max, value);
}
double range = max - min;
if (Math.abs(range) < HarmonicConstants.EPSILON) {
return new double[data.length]; // 返回全0数组
}
double[] normalized = new double[data.length];
for (int i = 0; i < data.length; i++) {
normalized[i] = (data[i] - min) / range;
}
return normalized;
}
/**
* 数据对齐处理
* 将不同采样间隔的数据对齐到相同的时间间隔
*
* @param data 原始数据
* @param originalInterval 原始采样间隔
* @param targetInterval 目标采样间隔
* @return 对齐后的数据
*/
public static float[] alignData(float[] data, int originalInterval, int targetInterval) {
if (targetInterval % originalInterval != 0) {
throw new IllegalArgumentException(
"Target interval must be multiple of original interval");
}
int ratio = targetInterval / originalInterval;
int newLength = data.length / ratio;
float[] alignedData = new float[newLength];
for (int i = 0; i < newLength; i++) {
float sum = 0;
for (int j = 0; j < ratio; j++) {
sum += data[i * ratio + j];
}
alignedData[i] = sum / ratio;
}
return alignedData;
}
}

View File

@@ -0,0 +1,766 @@
package com.njcn.product.advance.responsility.utils;
import cn.hutool.core.bean.BeanUtil;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.product.advance.eventSource.pojo.enums.AdvanceResponseEnum;
import com.njcn.product.advance.responsility.model.QvvrDataEntity;
import org.apache.commons.math3.linear.*;
import org.ejml.data.DMatrixRMaj;
import org.ejml.dense.row.CommonOps_DDRM;
import org.ejml.dense.row.factory.DecompositionFactory_DDRM;
import org.ejml.interfaces.decomposition.EigenDecomposition_F64;
import org.ejml.simple.SimpleMatrix;
import java.util.Arrays;
/**
* 责任量化的算法细节:
* 此代码用以计算刻度不等两序列之间的动态相关系数与动态谐波责任指标;
* 随机数生成器生成原始谐波U数据一维数组长度为LL生成原始负荷PE数据二维数组长度为TL宽度为P表示P个负荷公司
* 只能识别 U数据长度 为PE数据长度的 JIANGE倍也即无法自动判别时间间隔需人为定义
* Width用以控制动态相关系数的计算窗宽
* 使用<Eigen>库中函数进行矩阵构造与计算;
* 最终的到结果为向量Core为动态典则相关系数
* 矩阵simCor为剥离背景后的动态相关系数其中每列为每一用户负荷
* 矩阵HKdata为使用simCor计算的动态谐波责任指标其中每列为每一用户负荷
* 向量sumHKdata为超限额的谐波时不同用户的长时谐波责任指标
* 函数说明cov(a,b)计算协方差;
* SXXa,width计算长度为width序列的方差矩阵
* SXYa,b,width计算长度为width两序列的协方差矩阵
* TransCancor(Ma,Vb,width)计算长度为width的矩阵a与向量b的典则相关系数
* SlideCanCor(a,b,width)计算a与b的在 窗宽 width下 的动态典则相关系数;
* SlideCor(a,b,slidecancor,width)计算a,b,在窗宽 width 下 典则相关剥离背景后的动态相关系数。
*
* 算法很多代码没有注释这些代码翻译的友谊的C代码不清楚实际逻辑
*/
public class ResponsibilityAlgorithm {
static int P = 21;
static int TL = 671;
static int LL = 3355;
static int JIANGE = 5;
static int wdith = 96;
static float XIANE = 0;
static int RES_NUM = 0;
static QvvrDataEntity qvvrResult;
public QvvrDataEntity getResponsibilityResult(QvvrDataEntity qvvrParam) {
int i, j;
if (qvvrParam != null) {
// 计算责任
harm_res(qvvrParam.calFlag, qvvrParam);
// 输出结果
qvvrParam.calOk = qvvrResult.calOk;
if (qvvrParam.calOk == 1) {
// 长时越限谐波责任
for (i = 0; i < qvvrParam.pNode; i++)
qvvrParam.sumFKdata[i] = qvvrResult.sumFKdata[i];
for (i = 0; i < (qvvrParam.pNode + 1); i++)
qvvrParam.sumHKdata[i] = qvvrResult.sumHKdata[i];
// 如果是原始功率数据和谐波数据代入计算,将动态相关系数和动态谐波责任输出
if (qvvrParam.calFlag == 0) {
qvvrParam.resNum = qvvrResult.resNum;
for (i = 0; i < qvvrResult.resNum; i++) {
qvvrParam.core[i] = qvvrResult.core[i];
qvvrParam.bjCore[i] = qvvrResult.bjCore[i];
for (j = 0; j < qvvrParam.pNode; j++) {
qvvrParam.fKData[i][j] = qvvrResult.fKData[i][j];
qvvrParam.simData[i][j] = qvvrResult.simData[i][j];
}
for (j = 0; j < (qvvrParam.pNode + 1); j++)
qvvrParam.hKData[i][j] = qvvrResult.hKData[i][j];
}
}
}
}
return qvvrParam;
}
public static int harm_res(int calFlag, QvvrDataEntity qvvrParam) {
if (calFlag == 0) {
harm_res_all(qvvrParam);
} else if (calFlag == 1) {
harm_res_part(qvvrParam);
}
return 0;
}
static int harm_res_part(QvvrDataEntity qvvrParam) {
int ret = 0;
//缓冲大小初始化
ret = data_init_part(qvvrParam);
if (ret != 0) {
qvvrResult.calOk = 0;
// System.out.printf("data init err,exit\r\n");
throw new BusinessException(AdvanceResponseEnum.DATA_ERROR);
}
int colK = P + 1;
RealMatrix HKdata = MatrixUtils.createRealMatrix(RES_NUM, colK);
for (int i = 0; i < P + 1; i++) {
for (int j = 0; j < RES_NUM; j++) {
HKdata.setEntry(j, i, qvvrResult.hKData[j][i]);
}
}
RealVector Udata = new ArrayRealVector(TL);
for (int j = 0; j < TL; j++) {
Udata.setEntry(j, qvvrResult.harmData[j]);
}
float[] arrHKsum = SumHK(HKdata, Udata, wdith, colK, TL);
RealVector sumHKdata = new ArrayRealVector(colK);
for (int i = 0; i < colK; i++) {
sumHKdata.setEntry(i, 0);
}
float sum_hk = 0;
for (int i = 0; i < colK; i++) {
sumHKdata.setEntry(i, arrHKsum[i]);
sum_hk += sumHKdata.getEntry(i);
qvvrResult.sumHKdata[i] = (float) sumHKdata.getEntry(i);
}
RealMatrix FKdata = MatrixUtils.createRealMatrix(RES_NUM, P);
for (int i = 0; i < P; i++) {
for (int j = 0; j < RES_NUM; j++) {
FKdata.setEntry(j, i, qvvrResult.fKData[j][i]);
}
}
colK = P;
arrHKsum = SumHK(FKdata, Udata, wdith, colK, TL);
RealVector sumFKdata = new ArrayRealVector(colK);
for (int i = 0; i < colK; i++) {
sumFKdata.setEntry(i, 0);
}
float sum_fk = 0;
for (int i = 0; i < colK; i++) {
sumFKdata.setEntry(i, arrHKsum[i]);
sum_fk += sumFKdata.getEntry(i);
qvvrResult.sumFKdata[i] = (float) sumFKdata.getEntry(i);
}
qvvrResult.calOk = 1;
return 0;
}
static int data_init_part(QvvrDataEntity qvvrParam) {
qvvrResult = new QvvrDataEntity();
//输入数据处理
BeanUtil.copyProperties(qvvrParam, qvvrResult);
// if ((qvvrResult.resNum + qvvrResult.win) != qvvrResult.harmNum) {
// System.out.printf("数据未对齐...\r\n");
// return -1;
// }
RES_NUM = qvvrResult.resNum;
P = qvvrResult.pNode;
TL = qvvrResult.win + qvvrResult.resNum;
wdith = qvvrResult.win;
XIANE = qvvrResult.harmMk;
if ((wdith < QvvrDataEntity.MIN_WIN_LEN) || (wdith > QvvrDataEntity.MAX_WIN_LEN)) {
System.out.printf("窗宽超限...\r\n");
throw new BusinessException(AdvanceResponseEnum.WIN_DATA_ERROR);
}
if ((P > QvvrDataEntity.MAX_P_NODE) || (TL > QvvrDataEntity.MAX_P_NUM)) {
System.out.printf("数据长度超限...\r\n");
throw new BusinessException(AdvanceResponseEnum.DATA_ERROR);
}
for (int i = 0; i < RES_NUM; i++) {
for (int j = 0; j < P; j++) {
qvvrResult.fKData[i][j] = qvvrParam.fKData[i][j];
}
for (int j = 0; j < P + 1; j++) {
qvvrResult.hKData[i][j] = qvvrParam.hKData[i][j];
}
}
// 复制 qvvrParam
for (int i = 0; i < TL; i++) {
qvvrResult.harmData[i] = qvvrParam.harmData[i];
}
return 0;
}
static int harm_res_all(QvvrDataEntity qvvrParam) {
int ret = 0;
//缓冲大小初始化
ret = data_init_all(qvvrParam);
if (ret != 0) {
qvvrResult.calOk = 0;
System.out.printf("data init err,exit\r\n");
throw new BusinessException(AdvanceResponseEnum.INIT_DATA_ERROR);
}
//测试数据申请空间
float[][] a = new float[TL][];
for (int i = 0; i < TL; i++) {
a[i] = new float[P];
}
float[] b = new float[LL];
float[] u = new float[TL];
for (int i = 0; i < TL; i++) {
for (int j = 0; j < P; j++) {
a[i][j] = qvvrResult.pData[i][j];
}
}
for (int i = 0; i < LL; i++) {
b[i] = qvvrResult.harmData[i];
}
for (int i = 0; i < LL; i += JIANGE) {
float tempt = 0.0f;
for (int j = 0; j < JIANGE; j++) {
tempt += b[i + j];
}
b[i] = tempt / JIANGE;
}
int width = wdith;
int slcorlength = TL - width;
//剥离背景谐波后的动态相关系数计算
//数据格式转换
// 创建矩阵Pdata并复制数据
double[][] PdataArray = new double[TL][P];
for (int i = 0; i < TL; i++) {
for (int j = 0; j < P; j++) {
PdataArray[i][j] = a[i][j];
}
}
RealMatrix Pdata = new Array2DRowRealMatrix(PdataArray);
// 创建向量Udata并复制数据
double[] UdataArray = new double[TL];
for (int i = 0; i < TL; i++) {
UdataArray[i] = b[i * JIANGE];
}
RealVector Udata = new ArrayRealVector(UdataArray);
for (int i = 0; i < TL; i++) {
u[i] = (float) UdataArray[i];
}
//动态典则相关系数数组获得 并转化为向量
float[] cancorrelation = SlideCanCor(a, u, width, P, TL);
RealVector Core = new ArrayRealVector(slcorlength);
RealVector bjCore = new ArrayRealVector(slcorlength);
for (int i = 0; i < slcorlength; i++) {
Core.setEntry(i, cancorrelation[i]);
qvvrResult.core[i] = (float) Core.getEntry(i);
}
for (int i = 0; i < slcorlength; i++) {
bjCore.setEntry(i, 1 - cancorrelation[i]);
qvvrResult.bjCore[i] = (float) bjCore.getEntry(i);
}
float[] y = new float[TL];
float[] xe = new float[TL];
float[] slidecor;
RealVector temptPe = new ArrayRealVector(TL);
RealVector temptU = new ArrayRealVector(TL);
RealMatrix simCor = new Array2DRowRealMatrix(slcorlength, P);
// 格式转换
temptU = Udata.getSubVector(0, TL);
for (int m = 0; m < TL; m++) {
y[m] = (float) temptU.getEntry(m);
}
// 格式转换、计算系数、格式转换
for (int i = 0; i < P; i++) {
temptPe = Pdata.getColumnVector(i);
for (int m = 0; m < TL; m++) {
xe[m] = (float) temptPe.getEntry(m);
}
slidecor = slideCor(xe, y, cancorrelation, width, TL); // 计算每个用户负荷与用户谐波的动态相关系数
for (int j = 0; j < slcorlength; j++) {
simCor.setEntry(j, i, slidecor[j]);
qvvrResult.simData[j][i] = (float) simCor.getEntry(j, i);
}
}
//动态谐波责任指标计算
//EK计算用于后续计算FK不含背景的用户责任指标、HK包含背景的用户责任指标
//float **EKarr = (float **)malloc(TL * sizeof(float *));//先申请P个指针型字节的空间
//for (int i = 0; i < TL; i++)
//EKarr[i] = (float *)malloc(TL * Float.SIZE / Byte.SIZE);
float[][] EKarr;
EKarr = dyEKCom(simCor, Pdata, width, P, TL);
RealMatrix EKdata = MatrixUtils.createRealMatrix(slcorlength, P);
for (int i = 0; i < slcorlength; i++) {
for (int j = 0; j < P; j++) {
EKdata.setEntry(i, j, EKarr[i][j]);
}
}
//不含背景的用户谐波责任指标
//float **FKarr = (float **)malloc(TL * sizeof(float *));//先申请P个指针型字节的空间
//for (int i = 0; i < TL; i++)
//FKarr[i] = (float *)malloc(TL * Float.SIZE / Byte.SIZE);
float[][] FKarr;
FKarr = DyFKCom(EKdata, width, P, TL);
RealMatrix FKdata = MatrixUtils.createRealMatrix(slcorlength, P);
for (int i = 0; i < slcorlength; i++) {
for (int j = 0; j < P; j++) {
FKdata.setEntry(i, j, FKarr[i][j]);
}
}
qvvrResult.fKData=FKarr;
//包含背景的谐波责任指标
//float **HKarr = (float **)malloc(TL * sizeof(float *));//先申请P个指针型字节的空间
//for (int i = 0; i < TL; i++)
//HKarr[i] = (float *)malloc(TL * Float.SIZE / Byte.SIZE);
float[][] HKarr;
HKarr = DyHKCom(bjCore, EKdata, width, P, TL);
RealMatrix HKdata = MatrixUtils.createRealMatrix(slcorlength, (P + 1));
for (int i = 0; i < slcorlength; i++) {
for (int j = 0; j < (P + 1); j++) {
HKdata.setEntry(i, j, HKarr[i][j]);
qvvrResult.hKData[i][j] = (float) HKdata.getEntry(i, j);
}
}
qvvrResult.resNum = slcorlength;
//超限额长时谐波责任指标计算
float[] arrHKsum = new float[TL];
int colK = P + 1;//FKdata时为P
arrHKsum = SumHK(HKdata, Udata, width, colK, TL);//可更改FKdata表示为不包含背景时的长时责任划分
RealVector sumHKdata = new ArrayRealVector(colK);
for (int i = 0; i < colK; i++) {
sumHKdata.setEntry(i, 0);
}
float sum_hk = 0;
for (int i = 0; i < colK; i++) {
sumHKdata.setEntry(i, arrHKsum[i]);
sum_hk += sumHKdata.getEntry(i);
qvvrResult.sumHKdata[i] = (float) sumHKdata.getEntry(i);
}
colK = P;//FKdata时为P
arrHKsum = SumHK(FKdata, Udata, width, colK, TL);//可更改FKdata表示为不包含背景时的长时责任划分
RealVector sumFKdata = new ArrayRealVector(colK);
for (int i = 0; i < colK; i++) {
sumFKdata.setEntry(i, 0);
}
float sum_fk = 0;
for (int i = 0; i < colK; i++) {
sumFKdata.setEntry(i, arrHKsum[i]);
sum_fk += sumFKdata.getEntry(i);
qvvrResult.sumFKdata[i] = (float) sumHKdata.getEntry(i);
}
//结果输出
qvvrResult.calOk = 1;
return 0;
}
static int data_init_all(QvvrDataEntity qvvrParam) {
qvvrResult = new QvvrDataEntity();
//输入数据处理
BeanUtil.copyProperties(qvvrParam, qvvrResult);
P = qvvrResult.pNode;
TL = qvvrResult.pNum;
LL = qvvrResult.harmNum;
JIANGE = qvvrResult.harmNum / qvvrResult.pNum;
wdith = qvvrResult.win;
XIANE = qvvrResult.harmMk;
if ((JIANGE * TL != LL) || (JIANGE < 1)) {
return -1;
}
if ((wdith < QvvrDataEntity.MIN_WIN_LEN) || (wdith > QvvrDataEntity.MAX_WIN_LEN)) {
// System.out.printf("窗宽超限...\r\n");
throw new BusinessException(AdvanceResponseEnum.EVENT_DATA_MISS);
}
if (TL < (2 * wdith)) {
System.out.printf("窗宽和数据长度不匹配...\r\n");
return -1;
}
if ((P > QvvrDataEntity.MAX_P_NODE) || (TL > QvvrDataEntity.MAX_P_NUM) || (LL > QvvrDataEntity.MAX_HARM_NUM)) {
System.out.printf("数据长度超限...\r\n");
throw new BusinessException(AdvanceResponseEnum.EVENT_DATA_MISS);
}
float[][] clone = new float[qvvrParam.getPData().length][];
for (int i = 0; i < qvvrParam.getPData().length; i++) {
clone[i] = Arrays.copyOf(qvvrParam.getPData()[i], qvvrParam.getPData()[i].length);
}
qvvrResult.setPData(clone);
for (int i = 0; i < LL; i++) {
qvvrResult.getHarmData()[i] = qvvrParam.getHarmData()[i];
}
//System.out.printf("win = %d\r\n",wdith);
return 0;
}
public static float[] SlideCanCor(float[][] x, float[] y, int width, int p_num, int tl_num) {
int slcorlength = tl_num - width;
float[][] a = new float[width][p_num];
for (int i = 0; i < width; i++) {
a[i] = new float[p_num];
}
float[] b = new float[width];
float[][] sxxmatrix = new float[p_num][p_num];
for (int i = 0; i < p_num; i++) {
sxxmatrix[i] = new float[p_num];
}
float[] sxymatrix = new float[tl_num];
float[] x1 = new float[tl_num];
float[] x2 = new float[tl_num];
float[] x3 = new float[tl_num];
float[][] xx = new float[width][p_num];
for (int i = 0; i < width; i++) {
xx[i] = new float[p_num];
}
float[] yy = new float[width];
RealMatrix Pdata = new Array2DRowRealMatrix(tl_num, p_num);
RealVector Udata = new ArrayRealVector(tl_num);
for (int i = 0; i < tl_num; i++) {
for (int j = 0; j < p_num; j++) {
Pdata.setEntry(i, j, x[i][j]);
}
Udata.setEntry(i, y[i]);
}
RealMatrix temptP = new Array2DRowRealMatrix(width, p_num);
RealVector temptU = new ArrayRealVector(width);
float[] slideCancor = new float[tl_num];
for (int i = 0; i < slcorlength; i++) {
temptU = Udata.getSubVector(i, width);
temptP = Pdata.getSubMatrix(i, i + width - 1, 0, p_num - 1);
slideCancor[i] = TransCancor(temptP, temptU, width, p_num, tl_num, sxxmatrix, sxymatrix, x1, x2, x3, xx, yy);
}
return slideCancor;
}
public static float TransCancor(RealMatrix a, RealVector b, int width, int p_num, int tl_num,
float[][] sxxMatrix, float[] sxyMatrix, float[] x1, float[] x2,
float[] x3, float[][] x, float[] y) {
float syymatrix;
for (int i = 0; i < width; i++) {
for (int j = 0; j < p_num; j++) {
x[i][j] = (float) a.getEntry(i, j);
}
y[i] = (float) b.getEntry(i);
}
// 假设的SXX函数需要根据实际实现来调整
sxxMatrix = SXX(x, width, p_num, tl_num, sxxMatrix, x1, x2);
syymatrix = cov(y, y, width); // 假设的COV函数需要根据实际实现来调整
if (syymatrix == 0) {
syymatrix = 0.00001F;
}
// 假设的SXY函数需要根据实际实现来调整
sxyMatrix = SXY(x, y, width, p_num, tl_num, sxyMatrix, x3);
//
// RealMatrix A = MatrixUtils.createRealMatrix(p_num, p_num);
// RealMatrix invSXX = MatrixUtils.createRealMatrix(p_num, p_num);
// RealMatrix I = MatrixUtils.createRealIdentityMatrix(p_num); // I is an identity matrix
//
// // 二维数组转为矩阵
// for (int i = 0; i < p_num; i++) {
// for (int j = 0; j < p_num; j++) {
// A.setEntry(i, j, sxxMatrix[i][j]);
// }
// }
//
// // 使用 LU 分解方法计算矩阵 invSXX
// invSXX = new LUDecomposition(A).getSolver().solve(I);
// 创建 p_num × p_num 的矩阵 A
SimpleMatrix A = new SimpleMatrix(p_num, p_num);
// 创建 p_num × p_num 的逆矩阵 invSXX
SimpleMatrix invSXX = new SimpleMatrix(p_num, p_num);
// 创建 p_num × p_num 的单位矩阵 I
SimpleMatrix I = SimpleMatrix.identity(p_num);
// 二维数组转为矩阵 A
for (int i = 0; i < p_num; i++) {
for (int j = 0; j < p_num; j++) {
A.set(i, j, sxxMatrix[i][j]);
}
}
// 使用 LU 分解方法计算矩阵 invSXX
//invSXX = A.invert().mult(I);
// 创建长度为 p_num 的向量 sXYMa_Eigen
DMatrixRMaj sXYMa_Eigen = new DMatrixRMaj(p_num, 1);
for (int i = 0; i < p_num; i++) {
sXYMa_Eigen.set(i, 0, sxyMatrix[i]);
}
// 计算 Umatrix
DMatrixRMaj Umatrix = new DMatrixRMaj(p_num, p_num);
CommonOps_DDRM.multOuter(sXYMa_Eigen, Umatrix); // 外积
CommonOps_DDRM.divide(Umatrix, syymatrix); // 矩阵按标量除法
CommonOps_DDRM.divide(Umatrix, syymatrix); // 再次按标量除法
// 计算特征值
EigenDecomposition_F64<DMatrixRMaj> eigenDecomposition = DecompositionFactory_DDRM.eig(p_num, false);
eigenDecomposition.decompose(Umatrix);
float corrMax = 0;
for (int i = 0; i < p_num; i++) {
float absCorr = (float) Math.abs(eigenDecomposition.getEigenvalue(i).getReal());
if (absCorr > corrMax) {
corrMax = absCorr;
}
}
float cancor = (float) Math.sqrt(corrMax);
if (cancor >= 1) {
cancor = 1;
}
return cancor;
}
public static float[][] SXX(float[][] x, int width, int p_num, int tl_num, float[][] sxxmatrix, float[] x1, float[] x2) {
int i, j, m;
for (i = 0; i < p_num; i++) {
for (j = 0; j < p_num; j++) {
for (m = 0; m < width; m++) {
x1[m] = x[m][i];
x2[m] = x[m][j];
}
sxxmatrix[i][j] = cov(x1, x2, width);
}
}
return sxxmatrix;
}
public static float[] SXY(float[][] x, float[] y, int width, int p_num, int tl_num, float[] sxymatrix, float[] x1) {
int i, m;
for (i = 0; i < p_num; i++) {
for (m = 0; m < width; m++) {
x1[m] = x[m][i];
}
sxymatrix[i] = cov(x1, y, width);
}
return sxymatrix;
}
public static float cov(float[] x, float[] y, int width) {
float d1, d2, d3, d4;
float mx, my;
float cov;
int i;
int xlength = width;
int ylength = width;
d1 = d2 = d3 = d4 = mx = my = 0.0f;
for (i = 0; i < xlength; i++) {
mx += x[i];
my += y[i];
}
mx = mx / xlength;
my = my / ylength;
for (i = 0; i < xlength; i++) {
d1 += (x[i] - mx) * (y[i] - my);
}
cov = d1 / (xlength - 1);
return cov;
}
public static float[] slideCor(float[] x, float[] y, float[] slidecor, int width, int tlNum) {
int slcorLength = tlNum - width;
float[] slcor = new float[tlNum]; // 注意调整数组大小根据实际需要
// 动态相关系数
for (int i = 0; i < slcorLength; i++) {
float[] temptp = new float[width];
float[] temptq = new float[width];
for (int j = 0; j < width; j++) {
temptp[j] = x[i + j];
temptq[j] = y[i + j] * slidecor[i];
}
slcor[i] = pearCor(temptq, temptp, width);
}
return slcor;
}
public static float pearCor(float[] x, float[] y, int count) {
float d1 = 0, d2 = 0, d3 = 0, d4 = 0;
float mx = 0, my = 0;
float result = 0;
// 计算x和y的平均值
for (int i = 0; i < count; i++) {
mx += x[i];
my += y[i];
}
mx /= count;
my /= count;
// 计算相关系数的数据组成部分
for (int i = 0; i < count; i++) {
d1 += (x[i] - mx) * (y[i] - my);
d2 += (x[i] - mx) * (x[i] - mx);
d3 += (y[i] - my) * (y[i] - my);
}
d4 = (float) Math.sqrt(d2 * d3);
if (d4 == 0) {
// 除数为0时相关系数为0
result = 0;
} else {
result = d1 / d4;
}
return result;
}
private static float[][] dyEKCom(RealMatrix Dydata, RealMatrix Pdata, int width, int p_num, int tl_num) {
int slg = tl_num - width;
RealMatrix AKdata = MatrixUtils.createRealMatrix(slg, p_num);
RealMatrix SumP = MatrixUtils.createRealMatrix(slg, 1);
RealMatrix EKdata = MatrixUtils.createRealMatrix(slg, p_num);
for (int i = 0; i < slg; i++) {
SumP.setEntry(i, 0, 0);
for (int j = 0; j < p_num; j++) {
float sumPValue = (float) (SumP.getEntry(i, 0) + Pdata.getEntry(i, j));
SumP.setEntry(i, 0, sumPValue);
}
for (int j = 0; j < p_num; j++) {
float AKdataValue = (float) (Dydata.getEntry(i, j) * (Pdata.getEntry(i, j) / SumP.getEntry(i, 0)));
AKdata.setEntry(i, j, AKdataValue);
}
}
for (int i = 0; i < slg; i++) {
float maxdata = Float.MIN_VALUE;
float mindata = Float.MAX_VALUE;
for (int j = 0; j < p_num; j++) {
maxdata = Math.max(maxdata, (float) AKdata.getEntry(i, j));
mindata = Math.min(mindata, (float) AKdata.getEntry(i, j));
}
for (int j = 0; j < p_num; j++) {
EKdata.setEntry(i, j, (AKdata.getEntry(i, j) - mindata) / (maxdata - mindata));
}
}
float[][] arrEK = new float[slg][p_num];
for (int i = 0; i < slg; i++) {
for (int j = 0; j < p_num; j++) {
arrEK[i][j] = (float) EKdata.getEntry(i, j);
}
}
return arrEK;
}
private static float[][] DyFKCom(RealMatrix EKdata, int width, int p_num, int tl_num) {
int slg = tl_num - width;
RealMatrix FKdata = MatrixUtils.createRealMatrix(slg, p_num);
ArrayRealVector SumEK = new ArrayRealVector(slg);
for (int i = 0; i < slg; i++) {
SumEK.setEntry(i, 0);
for (int j = 0; j < p_num; j++) {
float sumEKValue = (float) (SumEK.getEntry(i) + EKdata.getEntry(i, j));
SumEK.setEntry(i, sumEKValue);
}
for (int j = 0; j < p_num; j++) {
float FKdataValue = (float) (EKdata.getEntry(i, j) / SumEK.getEntry(i));
FKdata.setEntry(i, j, FKdataValue);
}
}
float[][] arrFK = new float[tl_num][p_num];
for (int i = 0; i < slg; i++) {
for (int j = 0; j < p_num; j++) {
arrFK[i][j] = (float) FKdata.getEntry(i, j);
}
}
return arrFK;
}
private static float[][] DyHKCom(RealVector cancordata, RealMatrix EKdata, int width, int p_num, int tl_num) {
int slg = tl_num - width;
RealMatrix HKdata = MatrixUtils.createRealMatrix(slg, p_num + 1);
RealMatrix newEK = MatrixUtils.createRealMatrix(slg, p_num + 1);
ArrayRealVector SumEK = new ArrayRealVector(slg);
for (int i = 0; i < slg; i++) {
for (int j = 0; j < p_num; j++) {
newEK.setEntry(i, j, EKdata.getEntry(i, j));
}
newEK.setEntry(i, p_num, cancordata.getEntry(i));
}
for (int i = 0; i < slg; i++) {
SumEK.setEntry(i, 0);
for (int j = 0; j < p_num + 1; j++) {
float sumEKValue = (float) (SumEK.getEntry(i) + newEK.getEntry(i, j));
SumEK.setEntry(i, sumEKValue);
}
for (int j = 0; j < p_num + 1; j++) {
float HKdataValue = (float) (newEK.getEntry(i, j) / SumEK.getEntry(i));
HKdata.setEntry(i, j, HKdataValue);
}
}
float[][] arrHK = new float[tl_num][p_num + 1];
for (int i = 0; i < slg; i++) {
for (int j = 0; j < p_num + 1; j++) {
arrHK[i][j] = (float) HKdata.getEntry(i, j);
}
}
return arrHK;
}
private static float[] SumHK(RealMatrix HKdata, RealVector Udata, int width, int colK, int tl_num) {
int P1 = colK;
RealVector HKSum = new ArrayRealVector(P1);
int slg = tl_num - width;
int coutt = 0;
for (int j = 0; j < P1; j++) {
HKSum.setEntry(j, 0);
coutt = 0;
for (int i = 0; i < slg; i++) {
if (Udata.getEntry(i) > XIANE) {
float HKdataEntry = (float) HKdata.getEntry(i, j);
HKSum.setEntry(j, HKSum.getEntry(j) + HKdataEntry);
coutt++;
}
}
}
float[] arrHKsum = new float[P1];
for (int i = 0; i < P1; i++) {
arrHKsum[i] = 0;
}
for (int i = 0; i < P1; i++) {
if (coutt > 0) {
arrHKsum[i] = (float) (100 * (HKSum.getEntry(i) / coutt));
}
}
return arrHKsum;
}
}

View File

@@ -0,0 +1,13 @@
package com.njcn.product.advance;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CnAdvanceApplicationTests {
@Test
void contextLoads() {
}
}