Compare commits
15 Commits
33fcf37b12
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
370d22bc78 | ||
|
|
b607189bd3 | ||
|
|
f33b657bae | ||
|
|
b9fb336cac | ||
|
|
8900f41486 | ||
| 026d71a060 | |||
|
|
b43728e154 | ||
|
|
85659629de | ||
|
|
6518adc8ce | ||
|
|
47518928e0 | ||
|
|
00beb8efa8 | ||
|
|
401d2a4e97 | ||
| 53cc3c85e3 | |||
| 43b21859af | |||
| a857953854 |
24
README.md
Normal file
24
README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 数据同步注意事项
|
||||||
|
Oralce同步到InfluxDB
|
||||||
|
1.确认数据库oralce,mysql,influxdb的连接信息准确性
|
||||||
|
2.确认mysql数据库的pq_line_bak,pq_device_bak两张表,mysql与oracle台账监测点映射关系正确
|
||||||
|
3.历史数据与实时数据必须分成两个jar包跑,且两个jar包的端口不能冲突与占用。
|
||||||
|
4.在历史jar中需要将实时数据的批处理任务停止,否知只会跑实时数据,历史数据接口调用无效
|
||||||
|
5.在调用历史数据接口时将OracleToInfluxDBServiceImpl的
|
||||||
|
//获取监测点最新的数据时间,单监测点查询数据
|
||||||
|
List<lineTimeDto> lineTimeList = lineTimeMapper.getLineTime();
|
||||||
|
lineTimeList.forEach(item->{
|
||||||
|
MigrationParam migration = new MigrationParam();
|
||||||
|
migration.setLineIds(Collections.singletonList(item.getLineIndex()));
|
||||||
|
migration.setStartTime(item.getUpdateTime().minusHours(2));
|
||||||
|
migration.setEndTime(item.getUpdateTime());
|
||||||
|
System.out.println("当前监测点为------------------------------------"+item.getLineIndex());
|
||||||
|
System.out.println("执行扫描起始时间------------------------------------"+item.getUpdateTime().minusHours(2).format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
System.out.println("执行扫描结束时间------------------------------------"+item.getUpdateTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
list.addAll(executor.queryData(migration));
|
||||||
|
});这一部分代码注释掉,这段代码是冀北特有的,如果不注释掉始终同步最新的数据时间的数据导致历史数据接口调用失败。
|
||||||
|
6.同步暂态数据接口:如下例http://10.95.53.49:8092/data/eventBatch?startDateTime=2024-01-01&endDateTime=2024-12-06 (吴瑞开发的有问题咨询吴瑞)
|
||||||
|
7.同步稳态数据接口:如下例http://10.95.53.49:9091/data/dataSyncHours?startDateTime=2024-11-25 00:00:00&endDateTime=2024-01-01 00:00:00(需注意是重最新时间朝更久的时间同步的,因此
|
||||||
|
startDateTime>endDateTime,否则无法同步).
|
||||||
|
8.当新老系统并行期间,可能老系统依旧会新建台账,但是pq_line_bak不能及时更新,部分新增监测点实时数据同步不了。
|
||||||
|
|
||||||
38
filesync/.gitignore
vendored
Normal file
38
filesync/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
98
filesync/pom.xml
Normal file
98
filesync/pom.xml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>data-migration</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>filesync</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>njcn-common</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>mybatis-plus</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>spingboot2.3.12</artifactId>
|
||||||
|
<version>2.3.12</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SSH/SFTP 客户端 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jcraft</groupId>
|
||||||
|
<artifactId>jsch</artifactId>
|
||||||
|
<version>0.1.55</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>mybatis-plus</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<finalName>file-sync</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
15
filesync/src/main/java/com/njcn/FileSyncApplication.java
Normal file
15
filesync/src/main/java/com/njcn/FileSyncApplication.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package com.njcn;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@MapperScan("com.njcn.**.mapper")
|
||||||
|
@SpringBootApplication(scanBasePackages = "com.njcn")
|
||||||
|
public class FileSyncApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(FileSyncApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.njcn.device.mapper;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
import com.njcn.device.pojo.po.PqDevice;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/06/19 下午 1:47【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public interface PqDeviceMapper extends BaseMapper<PqDevice> {
|
||||||
|
PqDevice selectByLineId(@Param("lineId") String lineId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.njcn.device.mapper;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.njcn.device.pojo.po.RmpEventDetailPO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂态事件明细
|
||||||
|
*
|
||||||
|
* @author yzh
|
||||||
|
* @date 2022/10/12
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface RmpEventDetailMapper extends BaseMapper<RmpEventDetailPO> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取暂态事件明细
|
||||||
|
*
|
||||||
|
* @param startTime 开始时间
|
||||||
|
* @param endTime 结束时间
|
||||||
|
* @return 暂态事件明细
|
||||||
|
*/
|
||||||
|
List<RmpEventDetailPO> getDetailsOfTransientEvents(
|
||||||
|
@Param("startTime") String startTime,
|
||||||
|
@Param("endTime") String endTime);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?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.device.mapper.PqDeviceMapper">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<select id="selectByLineId" resultType="com.njcn.device.pojo.po.PqDevice">
|
||||||
|
select a.* FROM pq_device a , pq_line pl where a.Id = SUBSTRING_INDEX(SUBSTRING_INDEX(pl.Pids, ',', 5), ',', -1) and pl.id =#{lineId};
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.njcn.device.mapper.RmpEventDetailMapper">
|
||||||
|
|
||||||
|
<select id="getDetailsOfTransientEvents" resultType="com.njcn.device.pojo.po.RmpEventDetailPO">
|
||||||
|
SELECT
|
||||||
|
event_id as eventId,
|
||||||
|
measurement_point_id as measurementPointId,
|
||||||
|
Event_Type as eventType,
|
||||||
|
start_time as startTime,
|
||||||
|
Duration as duration,
|
||||||
|
feature_amplitude as featureAmplitude,
|
||||||
|
phase as phase,
|
||||||
|
event_describe as eventDescribe,
|
||||||
|
wave_path as wavePath
|
||||||
|
FROM
|
||||||
|
r_mp_event_detail
|
||||||
|
WHERE
|
||||||
|
1=1
|
||||||
|
<if test="startTime != null and startTime != ''">
|
||||||
|
AND DATE_FORMAT(create_time, '%Y-%m-%d %H:%i:%s') >= DATE_FORMAT(#{startTime}, '%Y-%m-%d %H:%i:%s')
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null and endTime != ''">
|
||||||
|
AND DATE_FORMAT(create_time, '%Y-%m-%d %H:%i:%s') <= DATE_FORMAT(#{endTime}, '%Y-%m-%d %H:%i:%s')
|
||||||
|
</if>
|
||||||
|
order by start_time desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
</mapper>
|
||||||
167
filesync/src/main/java/com/njcn/device/pojo/po/PqDevice.java
Normal file
167
filesync/src/main/java/com/njcn/device/pojo/po/PqDevice.java
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package com.njcn.device.pojo.po;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/06/19 下午 1:47【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 靠靠?
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("pq_device")
|
||||||
|
public class PqDevice implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 装置序号
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 装置模型(0:虚拟设备;1:实际设备;2:离线设备;)默认是实际设备
|
||||||
|
*/
|
||||||
|
private Integer devModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据类型(0:暂态系统;1:稳态系统;2:两个系统)
|
||||||
|
*/
|
||||||
|
private Integer devDataType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端运行状态(0:运行;1:检修;2:停运;3:调试;4:退运)
|
||||||
|
*/
|
||||||
|
private Integer runFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通讯状态(0:中断;1:正常)
|
||||||
|
*/
|
||||||
|
private Integer comFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备制造商,字典表
|
||||||
|
*/
|
||||||
|
private String manufacturer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定检状态(0:未检 1:已检)
|
||||||
|
*/
|
||||||
|
private Integer checkFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前置类型(MMS、CLD)字典表
|
||||||
|
*/
|
||||||
|
private String frontType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端型号(570、580……)字典表
|
||||||
|
*/
|
||||||
|
private String devType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络参数
|
||||||
|
*/
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 召唤标志(0:周期触发;1:变为触发)
|
||||||
|
*/
|
||||||
|
private Integer callFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 端口
|
||||||
|
*/
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 装置识别码(3ds加密)
|
||||||
|
*/
|
||||||
|
private String series;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 装置秘钥(3ds加密)
|
||||||
|
*/
|
||||||
|
private String devKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前置序号Id,前置表
|
||||||
|
*/
|
||||||
|
private String nodeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 投运时间
|
||||||
|
*/
|
||||||
|
private LocalDate loginTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本次定检时间,默认等于投运时间
|
||||||
|
*/
|
||||||
|
private LocalDate thisTimeCheck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下次定检时间,默认为投运时间后推3年,假如时间小于3个月则为待检
|
||||||
|
*/
|
||||||
|
private LocalDate nextTimeCheck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电度功能 0 关闭 1开启
|
||||||
|
*/
|
||||||
|
private Integer electroplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对时功能 0 关闭, 1开启
|
||||||
|
*/
|
||||||
|
private Integer onTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合同号
|
||||||
|
*/
|
||||||
|
private String contract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备sim卡号
|
||||||
|
*/
|
||||||
|
private String sim;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 装置系列
|
||||||
|
*/
|
||||||
|
private String devSeries;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监测装置安装位置
|
||||||
|
*/
|
||||||
|
private String devLocation;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监测厂家设备编号
|
||||||
|
*/
|
||||||
|
private String devNo;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警功能 0:关闭 null、1:开启
|
||||||
|
*/
|
||||||
|
private Integer isAlarm;
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.njcn.device.pojo.po;
|
||||||
|
|
||||||
|
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 io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂降明细实体类
|
||||||
|
*
|
||||||
|
* @author yzh
|
||||||
|
* @since 2022-10-12 18:34:55
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("r_mp_event_detail")
|
||||||
|
@ApiModel(value="RmpEventDetail对象")
|
||||||
|
public class RmpEventDetailPO implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂时事件ID")
|
||||||
|
@TableId(value = "event_id", type = IdType.ASSIGN_ID)
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "监测点ID")
|
||||||
|
private String measurementPointId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "监测点ID(复制)")
|
||||||
|
@TableField("measurement_point_id")
|
||||||
|
private String lineId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "统计类型")
|
||||||
|
private String eventType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂降原因(Event_Reason)")
|
||||||
|
@TableField("advance_reason")
|
||||||
|
private String advanceReason;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂降类型(Event_Type)")
|
||||||
|
@TableField("advance_type")
|
||||||
|
private String advanceType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件关联分析表Guid")
|
||||||
|
private String eventassIndex;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "dq计算持续时间 ")
|
||||||
|
private Double dqTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "特征值计算更新时间(外键PQS_Relevance的Time字段)")
|
||||||
|
private LocalDateTime dealTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "默认事件个数为0")
|
||||||
|
private Integer num;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "波形文件是否从装置招到本地(0:未招,1:已招)默认值为0")
|
||||||
|
private Integer fileFlag;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "特征值计算标志(0,未处理;1,已处理; 2,已处理,无结果;3,计算失败)默认值为0")
|
||||||
|
private Integer dealFlag;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "处理结果第一条事件发生时间(读comtra文件获取)")
|
||||||
|
private LocalDateTime firstTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "处理结果第一条事件暂降类型(字典表PQS_Dicdata)")
|
||||||
|
private String firstType;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "处理结果第一条事件发生时间毫秒(读comtra文件获取)")
|
||||||
|
private Double firstMs;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂降能量")
|
||||||
|
private Double energy;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂降严重度")
|
||||||
|
private Double severity;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂降源与监测位置关系 Upper:上游;Lower :下游;Unknown :未知;为空则是未计算")
|
||||||
|
private String sagsource;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "开始时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "格式化开始时间")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String formatTime;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "持续时间,单位秒")
|
||||||
|
private Double duration;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "特征幅值")
|
||||||
|
private Double featureAmplitude;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "相别")
|
||||||
|
private String phase;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "事件描述")
|
||||||
|
private String eventDescribe;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "波形路径")
|
||||||
|
private String wavePath;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂降核实原因")
|
||||||
|
@TableField("verify_reason")
|
||||||
|
private String verifyReason;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "暂降核实原因详情")
|
||||||
|
@TableField("verify_reason_detail")
|
||||||
|
private String verifyReasonDetail;
|
||||||
|
|
||||||
|
private Double transientValue;
|
||||||
|
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "用于计算数量")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer count;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.njcn.device.service;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
import com.njcn.device.pojo.po.PqDevice;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/06/19 下午 1:47【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public interface PqDeviceService extends IService<PqDevice> {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.njcn.device.service.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.njcn.device.mapper.PqDeviceMapper;
|
||||||
|
|
||||||
|
import com.njcn.device.pojo.po.PqDevice;
|
||||||
|
import com.njcn.device.service.PqDeviceService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/06/19 下午 1:47【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class PqDeviceServiceImpl extends ServiceImpl<PqDeviceMapper, PqDevice> implements PqDeviceService {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package com.njcn.filesync.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/16 下午 2:58【需求编号】文件同步状态管理
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public class FileSyncState {
|
||||||
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private String stateFilePath;
|
||||||
|
private Map<String, FileMetadata> fileStates;
|
||||||
|
|
||||||
|
public static class FileMetadata implements Serializable {
|
||||||
|
private long size;
|
||||||
|
private long lastModified;
|
||||||
|
private String checksum;
|
||||||
|
|
||||||
|
public FileMetadata() {}
|
||||||
|
|
||||||
|
public FileMetadata(long size, long lastModified, String checksum) {
|
||||||
|
this.size = size;
|
||||||
|
this.lastModified = lastModified;
|
||||||
|
this.checksum = checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public long getSize() { return size; }
|
||||||
|
public void setSize(long size) { this.size = size; }
|
||||||
|
|
||||||
|
public long getLastModified() { return lastModified; }
|
||||||
|
public void setLastModified(long lastModified) { this.lastModified = lastModified; }
|
||||||
|
|
||||||
|
public String getChecksum() { return checksum; }
|
||||||
|
public void setChecksum(String checksum) { this.checksum = checksum; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileSyncState(String stateFilePath) {
|
||||||
|
this.stateFilePath = stateFilePath;
|
||||||
|
this.fileStates = new HashMap<>();
|
||||||
|
loadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void loadState() {
|
||||||
|
File stateFile = new File(stateFilePath);
|
||||||
|
if (stateFile.exists()) {
|
||||||
|
try {
|
||||||
|
fileStates =objectMapper.readValue(stateFile, new TypeReference<Map<String, FileMetadata>>() {});
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Failed to load sync state: " + e.getMessage());
|
||||||
|
fileStates = new HashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveState() {
|
||||||
|
try {
|
||||||
|
objectMapper.writerWithDefaultPrettyPrinter()
|
||||||
|
.writeValue(new File(stateFilePath), fileStates);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Failed to save sync state: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFileChanged(String filePath, FileMetadata currentMetadata) {
|
||||||
|
FileMetadata previousMetadata = fileStates.get(filePath);
|
||||||
|
|
||||||
|
if (previousMetadata == null) {
|
||||||
|
return true; // 新文件
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件大小和修改时间
|
||||||
|
if (previousMetadata.getSize() != currentMetadata.getSize() ||
|
||||||
|
previousMetadata.getLastModified() != currentMetadata.getLastModified()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果时间相同但需要更精确的比较,可以检查校验和
|
||||||
|
if (currentMetadata.getChecksum() != null &&
|
||||||
|
!currentMetadata.getChecksum().equals(previousMetadata.getChecksum())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateFileState(String filePath, FileMetadata metadata) {
|
||||||
|
fileStates.put(filePath, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileMetadata getFileMetadata(String filePath) throws IOException {
|
||||||
|
Path path = Paths.get(filePath);
|
||||||
|
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||||
|
|
||||||
|
// 计算简单的校验和(对于大文件,可以只计算部分校验和)
|
||||||
|
String checksum = calculateSimpleChecksum(path.toFile());
|
||||||
|
|
||||||
|
return new FileMetadata(
|
||||||
|
attrs.size(),
|
||||||
|
attrs.lastModifiedTime().toMillis(),
|
||||||
|
checksum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String calculateSimpleChecksum(File file) {
|
||||||
|
// 简化的校验和计算:文件大小 + 最后修改时间
|
||||||
|
// 对于生产环境,建议使用MD5或SHA256
|
||||||
|
return file.length() + "-" + file.lastModified();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.njcn.filesync.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/16 下午 2:57【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ServerConfig {
|
||||||
|
|
||||||
|
private String host;
|
||||||
|
private int port = 22;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String privateKeyPath;
|
||||||
|
private String basePath;
|
||||||
|
private String serverType; // "windows" 或 "linux"
|
||||||
|
|
||||||
|
public boolean isWindows() {
|
||||||
|
return "windows".equalsIgnoreCase(serverType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean usePasswordAuth() {
|
||||||
|
return password != null && !password.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useKeyAuth() {
|
||||||
|
return privateKeyPath != null && !privateKeyPath.trim().isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.njcn.filesync.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/16 下午 2:57【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@Order(10)
|
||||||
|
public class SourceConfig {
|
||||||
|
@Value("${server.source.host}")
|
||||||
|
private String host;
|
||||||
|
@Value("${server.source.port}")
|
||||||
|
private int port = 22;
|
||||||
|
@Value("${server.source.username}")
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
@Value("${server.source.password}")
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
@Value("${server.source.privateKeyPath}")
|
||||||
|
|
||||||
|
private String privateKeyPath;
|
||||||
|
@Value("${server.source.basePath}")
|
||||||
|
|
||||||
|
private String basePath;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.njcn.filesync.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/16 下午 2:57【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@Order(10)
|
||||||
|
public class TargetConfig {
|
||||||
|
@Value("${server.target.host}")
|
||||||
|
private String host;
|
||||||
|
@Value("${server.target.port}")
|
||||||
|
|
||||||
|
private int port;
|
||||||
|
@Value("${server.target.username}")
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
@Value("${server.target.password}")
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
@Value("${server.target.privateKeyPath}")
|
||||||
|
|
||||||
|
private String privateKeyPath;
|
||||||
|
@Value("${server.target.basePath}")
|
||||||
|
private String basePath;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.njcn.filesync.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.filesync.service.FileSyncService;
|
||||||
|
import com.njcn.web.controller.BaseController;
|
||||||
|
import com.njcn.web.utils.HttpResultUtil;
|
||||||
|
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.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/20 下午 2:55【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/filesync")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Api(tags = "文件同步")
|
||||||
|
public class FileSyncController extends BaseController {
|
||||||
|
private final FileSyncService fileSyncService;
|
||||||
|
/**
|
||||||
|
* 手动触发跨服务器同步
|
||||||
|
*/
|
||||||
|
@PostMapping("/trigger")
|
||||||
|
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||||
|
@ApiOperation("暂态文件同步手动触发")
|
||||||
|
public HttpResult<Boolean> triggerCrossSync() {
|
||||||
|
String methodDescribe = getMethodDescribe("triggerCrossSync");
|
||||||
|
|
||||||
|
boolean flag =fileSyncService.triggerCrossSync();
|
||||||
|
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,flag , methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/EventWavePathSYnc")
|
||||||
|
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||||
|
@ApiOperation("暂态文件同步手动触发")
|
||||||
|
public HttpResult<Boolean> EventWavePathSYnc(@RequestParam("startDateTime") String startDateTime, @RequestParam("endDateTime") String endDateTime) {
|
||||||
|
String methodDescribe = getMethodDescribe("triggerCrossSync");
|
||||||
|
|
||||||
|
fileSyncService.EventWavePathSYnc(startDateTime,endDateTime);
|
||||||
|
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,true , methodDescribe);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.njcn.filesync.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/20 下午 3:02【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public interface FileSyncService {
|
||||||
|
boolean triggerCrossSync();
|
||||||
|
|
||||||
|
void EventWavePathSYnc(String startTime,String endTime);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.njcn.filesync.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.njcn.device.mapper.PqDeviceMapper;
|
||||||
|
import com.njcn.device.mapper.RmpEventDetailMapper;
|
||||||
|
import com.njcn.device.pojo.po.PqDevice;
|
||||||
|
import com.njcn.device.pojo.po.RmpEventDetailPO;
|
||||||
|
import com.njcn.device.service.PqDeviceService;
|
||||||
|
import com.njcn.filesync.config.ServerConfig;
|
||||||
|
import com.njcn.filesync.config.SourceConfig;
|
||||||
|
import com.njcn.filesync.config.TargetConfig;
|
||||||
|
import com.njcn.filesync.service.FileSyncService;
|
||||||
|
import com.njcn.filesync.sftp.IncrementalFileSync;
|
||||||
|
import jdk.nashorn.internal.ir.annotations.Reference;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/20 下午 3:03【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class FileSyncServiceImpl implements FileSyncService {
|
||||||
|
|
||||||
|
private final SourceConfig sourceConfig;
|
||||||
|
private final TargetConfig targetConfig;
|
||||||
|
private final PqDeviceMapper pqDeviceMapper;
|
||||||
|
private final RmpEventDetailMapper rmpEventDetailMapper;
|
||||||
|
@Override
|
||||||
|
public boolean triggerCrossSync() {
|
||||||
|
log.info("begintriggerCrossSync"+ LocalDateTime.now());
|
||||||
|
|
||||||
|
ServerConfig source = new ServerConfig();
|
||||||
|
ServerConfig target = new ServerConfig();
|
||||||
|
BeanUtils.copyProperties(sourceConfig,source);
|
||||||
|
BeanUtils.copyProperties(targetConfig,target);
|
||||||
|
List<PqDevice> list =pqDeviceMapper.selectList(null);
|
||||||
|
if(!CollectionUtils.isEmpty(list)){
|
||||||
|
|
||||||
|
List<String> ipList = list.stream().map(PqDevice::getIp).collect(Collectors.toList());
|
||||||
|
System.out.println("ipList"+ipList);
|
||||||
|
IncrementalFileSync fileSync = new IncrementalFileSync(
|
||||||
|
source, target, "sync-state.json",ipList,new ArrayList<>());
|
||||||
|
// 同步文件
|
||||||
|
fileSync.syncOnce();
|
||||||
|
}
|
||||||
|
log.info("endtriggerCrossSync"+ LocalDateTime.now());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void EventWavePathSYnc(String startTime,String endTime ) {
|
||||||
|
|
||||||
|
List<RmpEventDetailPO> detailsOfTransientEvents = rmpEventDetailMapper.selectList(new LambdaQueryWrapper<RmpEventDetailPO>().between(RmpEventDetailPO::getStartTime,startTime,endTime));
|
||||||
|
List<String> pathList = new ArrayList<>();
|
||||||
|
for (RmpEventDetailPO detailsOfTransientEvent : detailsOfTransientEvents) {
|
||||||
|
String lineId = detailsOfTransientEvent.getLineId();
|
||||||
|
PqDevice pqDevice = pqDeviceMapper.selectByLineId(lineId);
|
||||||
|
if(Objects.nonNull(detailsOfTransientEvent.getWavePath())){
|
||||||
|
String path = pqDevice.getIp()+"/"+detailsOfTransientEvent.getWavePath()+".CFG";
|
||||||
|
pathList.add(path);
|
||||||
|
String path2 = pqDevice.getIp()+"/"+detailsOfTransientEvent.getWavePath()+".DAT";
|
||||||
|
pathList.add(path2);
|
||||||
|
String path3 = pqDevice.getIp()+"/"+detailsOfTransientEvent.getWavePath()+".cfg";
|
||||||
|
pathList.add(path3);
|
||||||
|
String path4= pqDevice.getIp()+"/"+detailsOfTransientEvent.getWavePath()+".dat";
|
||||||
|
pathList.add(path4);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServerConfig source = new ServerConfig();
|
||||||
|
ServerConfig target = new ServerConfig();
|
||||||
|
BeanUtils.copyProperties(sourceConfig,source);
|
||||||
|
BeanUtils.copyProperties(targetConfig,target);
|
||||||
|
IncrementalFileSync fileSync = new IncrementalFileSync(
|
||||||
|
source, target, "sync-state.json",new ArrayList<>(),pathList);
|
||||||
|
// 同步文件
|
||||||
|
fileSync.syncOnceByPathList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
package com.njcn.filesync.sftp;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.ChannelSftp;
|
||||||
|
import com.jcraft.jsch.SftpATTRS;
|
||||||
|
import com.jcraft.jsch.SftpException;
|
||||||
|
import com.njcn.filesync.config.FileSyncState;
|
||||||
|
import com.njcn.filesync.config.ServerConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/16 下午 3:02【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public class IncrementalFileSync {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(IncrementalFileSync.class);
|
||||||
|
|
||||||
|
private ServerConfig sourceConfig;
|
||||||
|
private ServerConfig targetConfig;
|
||||||
|
private FileSyncState syncState;
|
||||||
|
// private ScheduledExecutorService scheduler;
|
||||||
|
private WatchService watchService;
|
||||||
|
private List<String> ipList;
|
||||||
|
private List<String> pathList;
|
||||||
|
private boolean isMonitoring = false;
|
||||||
|
|
||||||
|
public IncrementalFileSync(ServerConfig sourceConfig, ServerConfig targetConfig, String stateFilePath,List<String> ipList,List<String> pathList) {
|
||||||
|
this.sourceConfig = sourceConfig;
|
||||||
|
this.targetConfig = targetConfig;
|
||||||
|
this.syncState = new FileSyncState(stateFilePath);
|
||||||
|
// this.scheduler = Executors.newScheduledThreadPool(2);
|
||||||
|
this.ipList = ipList;
|
||||||
|
this.pathList = pathList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一次增量同步
|
||||||
|
*/
|
||||||
|
public void syncOnce() {
|
||||||
|
logger.info("Starting incremental synchronization...");
|
||||||
|
|
||||||
|
try (SftpClient sourceClient = new SftpClient();
|
||||||
|
SftpClient targetClient = new SftpClient()) {
|
||||||
|
|
||||||
|
// 连接到源服务器和目标服务器
|
||||||
|
sourceClient.connect(sourceConfig);
|
||||||
|
logger.info("sourceClient concet succss");
|
||||||
|
targetClient.connect(targetConfig);
|
||||||
|
logger.info("targetClient concet succss");
|
||||||
|
|
||||||
|
// 同步文件
|
||||||
|
syncDirectory("", sourceClient, targetClient);
|
||||||
|
|
||||||
|
logger.info("Incremental synchronization completed successfully");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Synchronization failed", e);
|
||||||
|
} finally {
|
||||||
|
syncState.saveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void syncOnceByPathList() {
|
||||||
|
logger.info("Starting incremental synchronization...");
|
||||||
|
|
||||||
|
try (SftpClient sourceClient = new SftpClient();
|
||||||
|
SftpClient targetClient = new SftpClient()) {
|
||||||
|
|
||||||
|
// 连接到源服务器和目标服务器
|
||||||
|
sourceClient.connect(sourceConfig);
|
||||||
|
logger.info("sourceClient concet succss");
|
||||||
|
targetClient.connect(targetConfig);
|
||||||
|
logger.info("targetClient concet succss");
|
||||||
|
|
||||||
|
// 同步文件
|
||||||
|
syncPathList( sourceClient, targetClient,pathList);
|
||||||
|
|
||||||
|
logger.info("Incremental synchronization completed successfully");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Synchronization failed", e);
|
||||||
|
} finally {
|
||||||
|
syncState.saveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncPathList(SftpClient sourceClient, SftpClient targetClient, List<String> pathList) throws SftpException {
|
||||||
|
|
||||||
|
ChannelSftp destSftp = targetClient.getChannel();
|
||||||
|
ChannelSftp sourceSftp = sourceClient.getChannel();
|
||||||
|
logger.info("-----------------------------------------------beginsyncByPathList----------------------");
|
||||||
|
|
||||||
|
for (String path : pathList) {
|
||||||
|
String sourceFilePath="";
|
||||||
|
if(sourceConfig.getBasePath().endsWith("/")){
|
||||||
|
sourceFilePath = sourceConfig.getBasePath() + path;
|
||||||
|
}else {
|
||||||
|
sourceFilePath = sourceConfig.getBasePath() + "/" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String destFilePath = targetConfig.getBasePath() + "/" + path;
|
||||||
|
boolean flag = safeFileExists(sourceSftp, sourceFilePath);
|
||||||
|
|
||||||
|
|
||||||
|
if(flag){
|
||||||
|
boolean needSync = safeFileExists(destSftp, destFilePath);
|
||||||
|
if(!needSync){
|
||||||
|
logger.info(sourceFilePath+"同步成功");
|
||||||
|
// 上传文件
|
||||||
|
|
||||||
|
try (InputStream inputStream = sourceSftp.get(sourceFilePath)) {
|
||||||
|
int lastSlashIndex = destFilePath.lastIndexOf('/');
|
||||||
|
String directoryPath = destFilePath.substring(0, lastSlashIndex);
|
||||||
|
targetClient.createRemoteDirectory(directoryPath);
|
||||||
|
|
||||||
|
destSftp.put(inputStream, destFilePath);
|
||||||
|
// System.out.println("Synced: " + sourceFilePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean safeFileExists(ChannelSftp sftpChannel, String remotePath) {
|
||||||
|
// 1. 基础检查
|
||||||
|
if (remotePath == null || remotePath.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查连接 (根据你的JSch版本和封装方式)
|
||||||
|
if (sftpChannel == null || !sftpChannel.isConnected()) {
|
||||||
|
logger.warn("SFTP channel is not connected.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 尝试获取属性判断存在性
|
||||||
|
try {
|
||||||
|
sftpChannel.lstat(remotePath);
|
||||||
|
return true;
|
||||||
|
} catch (SftpException e) {
|
||||||
|
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// 记录非“文件不存在”的其他异常(如权限问题)
|
||||||
|
logger.error("SFTP error while checking file existence: " + remotePath, e);
|
||||||
|
// 根据业务逻辑决定:返回false或抛出异常
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 处理其他意外异常,如网络中断
|
||||||
|
logger.error("Unexpected error checking file existence: " + remotePath, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始定时同步
|
||||||
|
*/
|
||||||
|
// public void startScheduledSync(long interval, TimeUnit timeUnit) {
|
||||||
|
// logger.info("Starting scheduled synchronization every {} {}", interval, timeUnit);
|
||||||
|
//
|
||||||
|
// scheduler.scheduleAtFixedRate(() -> {
|
||||||
|
// try {
|
||||||
|
// syncOnce();
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// logger.error("Scheduled synchronization failed", e);
|
||||||
|
// }
|
||||||
|
// }, 0, interval, timeUnit);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始文件监控同步(仅当源服务器是本地文件系统时)
|
||||||
|
*/
|
||||||
|
// public void startFileWatchSync() throws IOException {
|
||||||
|
// if (!isLocalServer(sourceConfig)) {
|
||||||
|
// throw new IllegalStateException("File watching is only supported for local source servers");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// watchService = FileSystems.getDefault().newWatchService();
|
||||||
|
// Path sourcePath = Paths.get(sourceConfig.getBasePath());
|
||||||
|
//
|
||||||
|
// // 注册目录监听
|
||||||
|
// sourcePath.register(watchService,
|
||||||
|
// StandardWatchEventKinds.ENTRY_CREATE,
|
||||||
|
// StandardWatchEventKinds.ENTRY_MODIFY,
|
||||||
|
// StandardWatchEventKinds.ENTRY_DELETE);
|
||||||
|
//
|
||||||
|
// isMonitoring = true;
|
||||||
|
// logger.info("Starting file watch synchronization for: {}", sourcePath);
|
||||||
|
//
|
||||||
|
// scheduler.execute(() -> {
|
||||||
|
// while (isMonitoring) {
|
||||||
|
// try {
|
||||||
|
// WatchKey key = watchService.take();
|
||||||
|
//
|
||||||
|
// for (WatchEvent<?> event : key.pollEvents()) {
|
||||||
|
// WatchEvent.Kind<?> kind = event.kind();
|
||||||
|
//
|
||||||
|
// if (kind == StandardWatchEventKinds.OVERFLOW) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @SuppressWarnings("unchecked")
|
||||||
|
// WatchEvent<Path> ev = (WatchEvent<Path>) event;
|
||||||
|
// Path fileName = ev.context();
|
||||||
|
// Path fullPath = sourcePath.resolve(fileName);
|
||||||
|
//
|
||||||
|
// logger.info("File change detected: {} - {}", kind.name(), fullPath);
|
||||||
|
//
|
||||||
|
// // 延迟一下,确保文件操作完成
|
||||||
|
// Thread.sleep(1000);
|
||||||
|
// syncOnce();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// key.reset();
|
||||||
|
//
|
||||||
|
// } catch (InterruptedException e) {
|
||||||
|
// Thread.currentThread().interrupt();
|
||||||
|
// break;
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// logger.error("File watch error", e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 停止所有同步任务
|
||||||
|
// */
|
||||||
|
// public void stop() {
|
||||||
|
// isMonitoring = false;
|
||||||
|
// scheduler.shutdown();
|
||||||
|
//
|
||||||
|
// if (watchService != null) {
|
||||||
|
// try {
|
||||||
|
// watchService.close();
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// logger.error("Error closing watch service", e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// logger.info("File synchronization stopped");
|
||||||
|
// }
|
||||||
|
|
||||||
|
private void syncDirectory(String relativePath, SftpClient sourceClient, SftpClient targetClient) {
|
||||||
|
// String sourceDir = getFullPath(sourceConfig.getBasePath(), relativePath);
|
||||||
|
// String targetDir = getFullPath(targetConfig.getBasePath(), relativePath);
|
||||||
|
try {
|
||||||
|
// 如果是本地文件系统,使用Java NIO
|
||||||
|
if (isLocalServer(sourceConfig)) {
|
||||||
|
syncLocalDirectory(relativePath, sourceClient, targetClient);
|
||||||
|
} else {
|
||||||
|
// 远程服务器同步逻辑(简化实现)
|
||||||
|
syncRemoteDirectory(sourceClient, targetClient, sourceConfig.getBasePath(),targetConfig.getBasePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error syncing directory: {}", relativePath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncLocalDirectory(String relativePath, SftpClient sourceClient, SftpClient targetClient)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
Path sourcePath = Paths.get(sourceConfig.getBasePath(), relativePath);
|
||||||
|
|
||||||
|
Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>() {
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
if (Files.isRegularFile(file)) {
|
||||||
|
|
||||||
|
String relativeFile = sourcePath.relativize(file).toString();
|
||||||
|
// 检查文件路径是否包含ipList中的任意IP
|
||||||
|
boolean shouldSync = ipList.stream().anyMatch(ip -> {
|
||||||
|
// 检查相对路径是否以IP地址开头
|
||||||
|
return relativeFile.startsWith(ip + File.separator) ||
|
||||||
|
relativeFile.startsWith(ip + "/") ||
|
||||||
|
relativeFile.startsWith(ip + "\\");
|
||||||
|
});
|
||||||
|
if (shouldSync){
|
||||||
|
syncFileIfNeeded(relativeFile, sourceClient, targetClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
private void syncRemoteDirectory(SftpClient sourceClient, SftpClient destClient, String sourceDir, String destDir) throws SftpException {
|
||||||
|
ChannelSftp destSftp = destClient.getChannel();
|
||||||
|
ChannelSftp sourceSftp = sourceClient.getChannel();
|
||||||
|
logger.info("-----------------------------------------------beginsyncRemoteDirectory----------------------");
|
||||||
|
// 获取源目录下的文件列表
|
||||||
|
Vector<ChannelSftp.LsEntry> files = sourceSftp.ls(sourceDir);
|
||||||
|
|
||||||
|
for (ChannelSftp.LsEntry entry : files) {
|
||||||
|
String filename = entry.getFilename();
|
||||||
|
if (".".equals(filename) || "..".equals(filename)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sourceFilePath = sourceDir + "/" + filename;
|
||||||
|
String destFilePath = destDir + "/" + filename;
|
||||||
|
boolean shouldSync = ipList.stream().anyMatch(ip -> {
|
||||||
|
// 检查路径是否包含设备ip
|
||||||
|
return sourceFilePath.contains(ip ) ;
|
||||||
|
|
||||||
|
});
|
||||||
|
if(shouldSync){
|
||||||
|
if (entry.getAttrs().isDir()) {
|
||||||
|
// 如果是目录,建目录递归同步
|
||||||
|
|
||||||
|
destClient.createRemoteDirectory(destFilePath);
|
||||||
|
syncRemoteDirectory(sourceClient, destClient, sourceFilePath, destFilePath);
|
||||||
|
} else {
|
||||||
|
// 如果是文件,判断是否需要同步
|
||||||
|
if (needSync(sourceSftp, destSftp, sourceFilePath, destFilePath, entry)) {
|
||||||
|
|
||||||
|
// 传输文件
|
||||||
|
try (InputStream inputStream = sourceSftp.get(sourceFilePath)) {
|
||||||
|
destSftp.put(inputStream, destFilePath);
|
||||||
|
// System.out.println("Synced: " + sourceFilePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean needSync(ChannelSftp sourceSftp, ChannelSftp destSftp, String sourcePath, String destPath, ChannelSftp.LsEntry sourceEntry) throws SftpException {
|
||||||
|
// 判断目标文件是否存在
|
||||||
|
SftpATTRS destAttrs;
|
||||||
|
long sourceMTime = sourceEntry.getAttrs().getMTime();
|
||||||
|
long currentTimeSeconds = System.currentTimeMillis() / 1000;
|
||||||
|
long halfYearSeconds = 100L * 24 * 60 * 60; // 假设半年为180天
|
||||||
|
if (sourceMTime < currentTimeSeconds - halfYearSeconds) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
destAttrs = destSftp.lstat(destPath);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
// 如果目标文件不存在,肯定需要同步
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较文件的修改时间
|
||||||
|
long destMTime = destAttrs.getMTime();
|
||||||
|
|
||||||
|
// 如果源文件的修改时间晚于目标文件,则需要同步
|
||||||
|
if (sourceMTime > destMTime) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 你也可以选择性地比较文件大小
|
||||||
|
// return sourceEntry.getAttrs().getSize() != destAttrs.getSize();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncFileIfNeeded( String relativeFile, SftpClient sourceClient, SftpClient targetClient) {
|
||||||
|
try {
|
||||||
|
String sourceFullPath = getFullPath(sourceConfig.getBasePath(), relativeFile);
|
||||||
|
FileSyncState.FileMetadata currentMetadata = syncState.getFileMetadata(sourceFullPath);
|
||||||
|
|
||||||
|
if (syncState.isFileChanged(relativeFile, currentMetadata)) {
|
||||||
|
logger.info("Syncing changed file: {}", relativeFile);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
targetClient.uploadFile(sourceFullPath,
|
||||||
|
getFullPath(targetConfig.getBasePath(),
|
||||||
|
new File(relativeFile).getParent() != null ?
|
||||||
|
new File(relativeFile).getParent() : ""));
|
||||||
|
|
||||||
|
// 更新同步状态
|
||||||
|
syncState.updateFileState(relativeFile, currentMetadata);
|
||||||
|
} else {
|
||||||
|
logger.debug("File unchanged, skipping: {}", relativeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error syncing file: {}", relativeFile, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFullPath(String basePath, String relativePath) {
|
||||||
|
if (relativePath == null || relativePath.isEmpty()) {
|
||||||
|
return basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (basePath.endsWith("/") || basePath.endsWith("\\")) {
|
||||||
|
return basePath + relativePath;
|
||||||
|
} else {
|
||||||
|
if(basePath.contains("/")){
|
||||||
|
return basePath + "/" + relativePath;
|
||||||
|
}else if(basePath.contains("\\")){
|
||||||
|
return basePath + "\\" + relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLocalServer(ServerConfig config) {
|
||||||
|
// 简单的本地服务器检测逻辑
|
||||||
|
return "localhost".equals(config.getHost()) ||
|
||||||
|
"127.0.0.1".equals(config.getHost());
|
||||||
|
}
|
||||||
|
}
|
||||||
110
filesync/src/main/java/com/njcn/filesync/sftp/SftpClient.java
Normal file
110
filesync/src/main/java/com/njcn/filesync/sftp/SftpClient.java
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package com.njcn.filesync.sftp;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.*;
|
||||||
|
import com.njcn.filesync.config.ServerConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/16 下午 3:00【需求编号】
|
||||||
|
*SFTP客户端实现
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Data
|
||||||
|
public class SftpClient implements AutoCloseable{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SftpClient.class);
|
||||||
|
|
||||||
|
private JSch jsch;
|
||||||
|
private Session session;
|
||||||
|
private ChannelSftp channel;
|
||||||
|
|
||||||
|
public void connect(ServerConfig config) throws JSchException {
|
||||||
|
jsch = new JSch();
|
||||||
|
|
||||||
|
// 设置私钥(如果提供)
|
||||||
|
if (config.getPrivateKeyPath() != null && !config.getPrivateKeyPath().isEmpty()) {
|
||||||
|
jsch.addIdentity(config.getPrivateKeyPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
|
||||||
|
|
||||||
|
// 设置密码(如果提供)
|
||||||
|
if (config.getPassword() != null && !config.getPassword().isEmpty()) {
|
||||||
|
session.setPassword(config.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置SSH连接
|
||||||
|
java.util.Properties sshConfig = new java.util.Properties();
|
||||||
|
sshConfig.put("StrictHostKeyChecking", "no");
|
||||||
|
session.setConfig(sshConfig);
|
||||||
|
|
||||||
|
session.connect(30000); // 30秒超时
|
||||||
|
|
||||||
|
// 打开SFTP通道
|
||||||
|
channel = (ChannelSftp) session.openChannel("sftp");
|
||||||
|
channel.connect(30000);
|
||||||
|
}
|
||||||
|
public void uploadFile(String localFile, String remotePath) throws SftpException {
|
||||||
|
File file = new File(localFile);
|
||||||
|
String remoteFile = ensureRemotePath(remotePath, file.getName());
|
||||||
|
|
||||||
|
// 确保远程目录存在
|
||||||
|
createRemoteDirectory(remotePath);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
channel.put(localFile, remoteFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelSftp.LsEntry getRemoteFileInfo(String remotePath) throws SftpException {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Vector<ChannelSftp.LsEntry> files = channel.ls(remotePath);
|
||||||
|
if (files != null && !files.isEmpty()) {
|
||||||
|
return files.get(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ensureRemotePath(String remotePath, String fileName) {
|
||||||
|
if (remotePath.endsWith("/")) {
|
||||||
|
return remotePath + fileName;
|
||||||
|
} else {
|
||||||
|
return remotePath + "/" + fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void createRemoteDirectory(String remotePath) throws SftpException {
|
||||||
|
|
||||||
|
// 处理Windows路径分隔符
|
||||||
|
String normalizedPath = remotePath.replace("\\", "/");
|
||||||
|
String[] directories = normalizedPath.split("/");
|
||||||
|
StringBuilder currentPath = new StringBuilder();
|
||||||
|
|
||||||
|
try {
|
||||||
|
channel.mkdir(normalizedPath);
|
||||||
|
logger.debug("Created remote directory: {}", currentPath);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
if (e.id != ChannelSftp.SSH_FX_FAILURE) {
|
||||||
|
// 目录可能已存在,忽略这个错误
|
||||||
|
logger.debug("Directory may already exist: {}", currentPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (channel != null) {
|
||||||
|
channel.disconnect();
|
||||||
|
}
|
||||||
|
if (session != null) {
|
||||||
|
session.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
filesync/src/main/java/com/njcn/job/EventWavePathJob.java
Normal file
41
filesync/src/main/java/com/njcn/job/EventWavePathJob.java
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package com.njcn.job;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import com.njcn.filesync.service.FileSyncService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/12/08 上午 10:47【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@EnableScheduling
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class EventWavePathJob {
|
||||||
|
|
||||||
|
private final FileSyncService fileSyncService;
|
||||||
|
// 每10分钟执行
|
||||||
|
@Scheduled(cron = "0 0/10 * * * ? ")
|
||||||
|
public void UpHarmonicJob(){
|
||||||
|
String endTime = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN);
|
||||||
|
String startTime =LocalDateTimeUtil.format(LocalDateTime.now().minusMinutes(30), DatePattern.NORM_DATETIME_PATTERN);
|
||||||
|
log.info("begin sync------------------" +
|
||||||
|
startTime+"---"+endTime+"eventFile");
|
||||||
|
fileSyncService.EventWavePathSYnc(startTime,endTime);
|
||||||
|
log.info("end sync------------------" +
|
||||||
|
startTime+"---"+endTime+"eventFile");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
48
filesync/src/main/java/com/njcn/job/FileSyncJob.java
Normal file
48
filesync/src/main/java/com/njcn/job/FileSyncJob.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
//package com.njcn.job;
|
||||||
|
//
|
||||||
|
//import com.njcn.device.pojo.po.PqDevice;
|
||||||
|
//import com.njcn.device.service.PqDeviceService;
|
||||||
|
//import com.njcn.filesync.config.*;
|
||||||
|
//import com.njcn.filesync.service.FileSyncService;
|
||||||
|
//import com.njcn.filesync.sftp.IncrementalFileSync;
|
||||||
|
//import lombok.RequiredArgsConstructor;
|
||||||
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
|
//import org.springframework.beans.BeanUtils;
|
||||||
|
//import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
//import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
//import org.springframework.stereotype.Component;
|
||||||
|
//import org.springframework.util.CollectionUtils;
|
||||||
|
//
|
||||||
|
//import java.time.LocalDateTime;
|
||||||
|
//import java.util.ArrayList;
|
||||||
|
//import java.util.List;
|
||||||
|
//import java.util.stream.Collectors;
|
||||||
|
//import java.util.stream.Stream;
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * @Author: cdf
|
||||||
|
// * @CreateTime: 2025-09-19
|
||||||
|
// * @Description: 定时任务
|
||||||
|
// */
|
||||||
|
//@Component
|
||||||
|
//@EnableScheduling
|
||||||
|
//@RequiredArgsConstructor
|
||||||
|
//@Slf4j
|
||||||
|
//public class FileSyncJob {
|
||||||
|
//
|
||||||
|
// private final SourceConfig sourceConfig;
|
||||||
|
// private final TargetConfig targetConfig;
|
||||||
|
// private final PqDeviceService pqDeviceService;
|
||||||
|
//
|
||||||
|
// private final FileSyncService fileSyncService;
|
||||||
|
// // 每10分钟执行
|
||||||
|
// @Scheduled(cron = "0 0/10 * * * ? ")
|
||||||
|
// public void UpHarmonicJob(){
|
||||||
|
//
|
||||||
|
// fileSyncService.triggerCrossSync();
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//}
|
||||||
104
filesync/src/main/resources/application-hn_prod.yml
Normal file
104
filesync/src/main/resources/application-hn_prod.yml
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#当前服务的基本信息
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
druid:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://10.95.53.49:13306/pqsinfo_hn?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
|
||||||
|
username: root
|
||||||
|
password: njcnpqs
|
||||||
|
# url: jdbc:mysql://localhost:3306/pqs91001?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=CTT
|
||||||
|
# username: root
|
||||||
|
# password: root
|
||||||
|
#初始化建立物理连接的个数、最小、最大连接数
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 50
|
||||||
|
#获取连接最大等待时间,单位毫秒
|
||||||
|
max-wait: 60000
|
||||||
|
#链接保持空间而不被驱逐的最长时间,单位毫秒
|
||||||
|
min-evictable-idle-time-millis: 300000
|
||||||
|
validation-query: select 1
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-pool-prepared-statement-per-connection-size: 20
|
||||||
|
|
||||||
|
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
#influxDB内容配置
|
||||||
|
influx:
|
||||||
|
url: http://192.168.1.103:18086
|
||||||
|
user: admin
|
||||||
|
password: 123456
|
||||||
|
database: pqsbase_wx
|
||||||
|
mapper-location: com.njcn.**.imapper
|
||||||
|
#mybatis配置信息
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath*:com/njcn/**/mapping/*.xml
|
||||||
|
#别名扫描
|
||||||
|
type-aliases-package: com.njcn.product.**.pojo
|
||||||
|
configuration:
|
||||||
|
#驼峰命名
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
#配置sql日志输出
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
# #关闭日志输出
|
||||||
|
# log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
#指定主键生成策略
|
||||||
|
id-type: assign_uuid
|
||||||
|
db:
|
||||||
|
type: mysql
|
||||||
|
#文件位置配置
|
||||||
|
business:
|
||||||
|
#处理波形数据位置
|
||||||
|
# wavePath: D://comtrade
|
||||||
|
wavePath: /usr/local/comtrade
|
||||||
|
#处理临时数据
|
||||||
|
#tempPath: D://file
|
||||||
|
tempPath: /usr/local/file
|
||||||
|
#文件存储的方式 1.本地
|
||||||
|
file:
|
||||||
|
storage: 1
|
||||||
|
#localStoragePath: /usr/local/localStoragePath
|
||||||
|
localStoragePath: D://localStoragePath
|
||||||
|
#oss服务器配置
|
||||||
|
min:
|
||||||
|
io:
|
||||||
|
endpoint: http://192.168.1.22:9009
|
||||||
|
accessKey: minio
|
||||||
|
secretKey: minio@123
|
||||||
|
bucket: excelreport
|
||||||
|
#华为obs服务器配置
|
||||||
|
huawei:
|
||||||
|
access-key: J9GS9EA79PZ60OK23LWP
|
||||||
|
security-key: BirGrAFDSLxU8ow5fffyXgZRAmMRb1R1AdqCI60d
|
||||||
|
obs:
|
||||||
|
bucket: test-8601
|
||||||
|
endpoint: https://obs.cn-east-3.myhuaweicloud.com
|
||||||
|
# 单位为秒
|
||||||
|
expire: 3600
|
||||||
|
|
||||||
|
server:
|
||||||
|
source:
|
||||||
|
host: 10.95.53.20
|
||||||
|
port: 9388
|
||||||
|
username: test
|
||||||
|
password: njcnpqs
|
||||||
|
basePath: /
|
||||||
|
privateKeyPath:
|
||||||
|
target:
|
||||||
|
host: 10.95.53.49
|
||||||
|
port: 9389
|
||||||
|
username: hndnzl
|
||||||
|
password: '@#001njcnPQS'
|
||||||
|
basePath: /home/hndnzl
|
||||||
|
privateKeyPath:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
104
filesync/src/main/resources/application-wuxi_dev.yml
Normal file
104
filesync/src/main/resources/application-wuxi_dev.yml
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#当前服务的基本信息
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
druid:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/pqsinfo_jb?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
|
||||||
|
username: root
|
||||||
|
password: njcnpqs
|
||||||
|
# url: jdbc:mysql://localhost:3306/pqs91001?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=CTT
|
||||||
|
# username: root
|
||||||
|
# password: root
|
||||||
|
#初始化建立物理连接的个数、最小、最大连接数
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 50
|
||||||
|
#获取连接最大等待时间,单位毫秒
|
||||||
|
max-wait: 60000
|
||||||
|
#链接保持空间而不被驱逐的最长时间,单位毫秒
|
||||||
|
min-evictable-idle-time-millis: 300000
|
||||||
|
validation-query: select 1
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-pool-prepared-statement-per-connection-size: 20
|
||||||
|
|
||||||
|
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
#influxDB内容配置
|
||||||
|
influx:
|
||||||
|
url: http://192.168.1.103:18086
|
||||||
|
user: admin
|
||||||
|
password: 123456
|
||||||
|
database: pqsbase_wx
|
||||||
|
mapper-location: com.njcn.**.imapper
|
||||||
|
#mybatis配置信息
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath*:com/njcn/**/mapping/*.xml
|
||||||
|
#别名扫描
|
||||||
|
type-aliases-package: com.njcn.product.**.pojo
|
||||||
|
configuration:
|
||||||
|
#驼峰命名
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
#配置sql日志输出
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
# #关闭日志输出
|
||||||
|
# log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
#指定主键生成策略
|
||||||
|
id-type: assign_uuid
|
||||||
|
db:
|
||||||
|
type: mysql
|
||||||
|
#文件位置配置
|
||||||
|
business:
|
||||||
|
#处理波形数据位置
|
||||||
|
wavePath: D://comtrade
|
||||||
|
#wavePath: /usr/local/comtrade
|
||||||
|
#处理临时数据
|
||||||
|
#tempPath: D://file
|
||||||
|
tempPath: /usr/local/file
|
||||||
|
#文件存储的方式 1.本地
|
||||||
|
file:
|
||||||
|
storage: 1
|
||||||
|
#localStoragePath: /usr/local/localStoragePath
|
||||||
|
localStoragePath: D://localStoragePath
|
||||||
|
#oss服务器配置
|
||||||
|
min:
|
||||||
|
io:
|
||||||
|
endpoint: http://192.168.1.22:9009
|
||||||
|
accessKey: minio
|
||||||
|
secretKey: minio@123
|
||||||
|
bucket: excelreport
|
||||||
|
#华为obs服务器配置
|
||||||
|
huawei:
|
||||||
|
access-key: J9GS9EA79PZ60OK23LWP
|
||||||
|
security-key: BirGrAFDSLxU8ow5fffyXgZRAmMRb1R1AdqCI60d
|
||||||
|
obs:
|
||||||
|
bucket: test-8601
|
||||||
|
endpoint: https://obs.cn-east-3.myhuaweicloud.com
|
||||||
|
# 单位为秒
|
||||||
|
expire: 3600
|
||||||
|
|
||||||
|
server:
|
||||||
|
source:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 22
|
||||||
|
username: test
|
||||||
|
password: 123456
|
||||||
|
basePath: /D:/comtrade
|
||||||
|
privateKeyPath:
|
||||||
|
target:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 22
|
||||||
|
username: test
|
||||||
|
password: 123456
|
||||||
|
basePath: /D:/comtrade2
|
||||||
|
privateKeyPath:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
104
filesync/src/main/resources/application-wuxi_prod.yml
Normal file
104
filesync/src/main/resources/application-wuxi_prod.yml
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#当前服务的基本信息
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
druid:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://192.168.1.103:13306/pqsinfo_wuxi?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
|
||||||
|
username: root
|
||||||
|
password: njcnpqs
|
||||||
|
# url: jdbc:mysql://localhost:3306/pqs91001?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=CTT
|
||||||
|
# username: root
|
||||||
|
# password: root
|
||||||
|
#初始化建立物理连接的个数、最小、最大连接数
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 50
|
||||||
|
#获取连接最大等待时间,单位毫秒
|
||||||
|
max-wait: 60000
|
||||||
|
#链接保持空间而不被驱逐的最长时间,单位毫秒
|
||||||
|
min-evictable-idle-time-millis: 300000
|
||||||
|
validation-query: select 1
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-pool-prepared-statement-per-connection-size: 20
|
||||||
|
|
||||||
|
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
#influxDB内容配置
|
||||||
|
influx:
|
||||||
|
url: http://192.168.1.103:18086
|
||||||
|
user: admin
|
||||||
|
password: 123456
|
||||||
|
database: pqsbase_wx
|
||||||
|
mapper-location: com.njcn.**.imapper
|
||||||
|
#mybatis配置信息
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath*:com/njcn/**/mapping/*.xml
|
||||||
|
#别名扫描
|
||||||
|
type-aliases-package: com.njcn.product.**.pojo
|
||||||
|
configuration:
|
||||||
|
#驼峰命名
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
#配置sql日志输出
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
# #关闭日志输出
|
||||||
|
# log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
#指定主键生成策略
|
||||||
|
id-type: assign_uuid
|
||||||
|
db:
|
||||||
|
type: mysql
|
||||||
|
#文件位置配置
|
||||||
|
business:
|
||||||
|
#处理波形数据位置
|
||||||
|
# wavePath: D://comtrade
|
||||||
|
wavePath: /usr/local/comtrade
|
||||||
|
#处理临时数据
|
||||||
|
#tempPath: D://file
|
||||||
|
tempPath: /usr/local/file
|
||||||
|
#文件存储的方式 1.本地
|
||||||
|
file:
|
||||||
|
storage: 1
|
||||||
|
#localStoragePath: /usr/local/localStoragePath
|
||||||
|
localStoragePath: D://localStoragePath
|
||||||
|
#oss服务器配置
|
||||||
|
min:
|
||||||
|
io:
|
||||||
|
endpoint: http://192.168.1.22:9009
|
||||||
|
accessKey: minio
|
||||||
|
secretKey: minio@123
|
||||||
|
bucket: excelreport
|
||||||
|
#华为obs服务器配置
|
||||||
|
huawei:
|
||||||
|
access-key: J9GS9EA79PZ60OK23LWP
|
||||||
|
security-key: BirGrAFDSLxU8ow5fffyXgZRAmMRb1R1AdqCI60d
|
||||||
|
obs:
|
||||||
|
bucket: test-8601
|
||||||
|
endpoint: https://obs.cn-east-3.myhuaweicloud.com
|
||||||
|
# 单位为秒
|
||||||
|
expire: 3600
|
||||||
|
|
||||||
|
server:
|
||||||
|
source:
|
||||||
|
host: 192.168.1.82
|
||||||
|
port: 22
|
||||||
|
username: root
|
||||||
|
password: dnzl@#001
|
||||||
|
basePath: /
|
||||||
|
privateKeyPath:
|
||||||
|
target:
|
||||||
|
host: 192.168.1.103
|
||||||
|
port: 22
|
||||||
|
username: root
|
||||||
|
password: dnzl@#001
|
||||||
|
basePath: /usr/local/comtrade
|
||||||
|
privateKeyPath:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
84
filesync/src/main/resources/application.yml
Normal file
84
filesync/src/main/resources/application.yml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#当前服务的基本信息
|
||||||
|
microservice:
|
||||||
|
ename: file-sync
|
||||||
|
name: file-sync
|
||||||
|
server:
|
||||||
|
port: 19002
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: file-sync
|
||||||
|
|
||||||
|
profiles:
|
||||||
|
active: hn_prod
|
||||||
|
|
||||||
|
|
||||||
|
jackson:
|
||||||
|
time-zone: GMT+8
|
||||||
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
|
locale: zh_CN
|
||||||
|
serialization:
|
||||||
|
# 格式化输出
|
||||||
|
indent_output: false
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
|
||||||
|
#mybatis配置信息
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath*:com/njcn/**/mapping/*.xml
|
||||||
|
#别名扫描
|
||||||
|
type-aliases-package: com.njcn.filesync.**.pojo
|
||||||
|
configuration:
|
||||||
|
#驼峰命名
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
#配置sql日志输出
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
# #关闭日志输出
|
||||||
|
# log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
#指定主键生成策略
|
||||||
|
id-type: assign_uuid
|
||||||
|
db:
|
||||||
|
type: mysql
|
||||||
|
#文件位置配置
|
||||||
|
business:
|
||||||
|
#处理波形数据位置
|
||||||
|
wavePath: D://comtrade
|
||||||
|
#wavePath: /usr/local/comtrade
|
||||||
|
#处理临时数据
|
||||||
|
#tempPath: D://file
|
||||||
|
tempPath: /usr/local/file
|
||||||
|
#文件存储的方式 1.本地
|
||||||
|
file:
|
||||||
|
storage: 1
|
||||||
|
#localStoragePath: /usr/local/localStoragePath
|
||||||
|
localStoragePath: f://localStoragePath
|
||||||
|
#oss服务器配置
|
||||||
|
min:
|
||||||
|
io:
|
||||||
|
endpoint: http://192.168.1.22:9009
|
||||||
|
accessKey: minio
|
||||||
|
secretKey: minio@123
|
||||||
|
bucket: excelreport
|
||||||
|
#华为obs服务器配置
|
||||||
|
huawei:
|
||||||
|
access-key: J9GS9EA79PZ60OK23LWP
|
||||||
|
security-key: BirGrAFDSLxU8ow5fffyXgZRAmMRb1R1AdqCI60d
|
||||||
|
obs:
|
||||||
|
bucket: test-8601
|
||||||
|
endpoint: https://obs.cn-east-3.myhuaweicloud.com
|
||||||
|
# 单位为秒
|
||||||
|
expire: 3600
|
||||||
|
|
||||||
|
#线程池配置信息
|
||||||
|
threadPool:
|
||||||
|
corePoolSize: 10
|
||||||
|
maxPoolSize: 20
|
||||||
|
queueCapacity: 500
|
||||||
|
keepAliveSeconds: 60
|
||||||
|
file:
|
||||||
|
upload-dir: D:/carry
|
||||||
|
|
||||||
|
|
||||||
142
filesync/src/main/resources/logback.xml
Normal file
142
filesync/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration scan="true" scanPeriod="20 seconds" debug="false">
|
||||||
|
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||||
|
|
||||||
|
<!-- 直接使用固定配置,避免Spring配置解析时机问题 -->
|
||||||
|
<property name="log.projectName" value="file-sync"/>
|
||||||
|
<property name="logCommonLevel" value="info"/>
|
||||||
|
<property name="logHomeDir" value="F:\logs"/>
|
||||||
|
<!-- <property name="logHomeDir" value="usr/local/logs"/>-->
|
||||||
|
|
||||||
|
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
|
||||||
|
<conversionRule conversionWord="wex"
|
||||||
|
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
|
||||||
|
<conversionRule conversionWord="ec"
|
||||||
|
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
|
||||||
|
|
||||||
|
|
||||||
|
<!--日志输出格式-->
|
||||||
|
<property name="log.pattern"
|
||||||
|
value="|-%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%level} ${log.projectName} -- %t %logger{100}.%M ==> %m%n${Log_EXCEPTION_CONVERSION_WORD:-%ec}}}"/>
|
||||||
|
<property name="log.maxHistory" value="30"/>
|
||||||
|
|
||||||
|
<!--客户端输出日志-->
|
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<!-- 自动高亮日志级别(ERROR=红色, WARN=黄色, INFO=绿色等) -->
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %logger{36} - %msg%n</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--系统中常规的debug日志-->
|
||||||
|
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender -->
|
||||||
|
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>
|
||||||
|
${logHomeDir}/${log.projectName}/debug/debug.log
|
||||||
|
</file>
|
||||||
|
<!-- 如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<!-- 设置过滤级别 -->
|
||||||
|
<level>DEBUG</level>
|
||||||
|
<!-- 用于配置符合过滤条件的操作 -->
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<!-- 用于配置不符合过滤条件的操作 -->
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责触发滚动 SizeAndTimeBasedRollingPolicy-->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<!--日志输出位置 可相对、和绝对路径 -->
|
||||||
|
<fileNamePattern>
|
||||||
|
${logHomeDir}/${log.projectName}/debug/debug.log.%d{yyyy-MM-dd}.%i.log
|
||||||
|
</fileNamePattern>
|
||||||
|
<maxFileSize>10MB</maxFileSize>
|
||||||
|
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,且<maxHistory>是6,
|
||||||
|
则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除 -->
|
||||||
|
<maxHistory>${log.maxHistory:-30}</maxHistory>
|
||||||
|
<!--重启清理日志文件-->
|
||||||
|
<!-- <cleanHistoryOnStart>true</cleanHistoryOnStart>-->
|
||||||
|
<!--每个文件最多100MB,保留N天的历史记录,但最多20GB-->
|
||||||
|
<!--<totalSizeCap>20GB</totalSizeCap>-->
|
||||||
|
<!--日志文件最大的大小-->
|
||||||
|
<!--<MaxFileSize>${log.maxSize}</MaxFileSize>-->
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>
|
||||||
|
${log.pattern}
|
||||||
|
</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--系统中常规的info日志-->
|
||||||
|
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>INFO</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<file>
|
||||||
|
${logHomeDir}/${log.projectName}/info/info.log
|
||||||
|
</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>
|
||||||
|
${logHomeDir}/${log.projectName}/info/info.log.%d{yyyy-MM-dd}.%i.log
|
||||||
|
</fileNamePattern>
|
||||||
|
<maxFileSize>10MB</maxFileSize>
|
||||||
|
<maxHistory>${log.maxHistory:-30}</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>
|
||||||
|
${log.pattern}
|
||||||
|
</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
|
||||||
|
<!--系统中常规的error日志-->
|
||||||
|
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>
|
||||||
|
${logHomeDir}/${log.projectName}/error/error.log
|
||||||
|
</file>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>ERROR</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>
|
||||||
|
${logHomeDir}/${log.projectName}/error/error.log.%d{yyyy-MM-dd}.%i.log
|
||||||
|
</fileNamePattern>
|
||||||
|
<maxFileSize>10MB</maxFileSize>
|
||||||
|
<maxHistory>${log.maxHistory:-30}</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>
|
||||||
|
${log.pattern}
|
||||||
|
</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
|
||||||
|
<logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/>
|
||||||
|
<logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<logger name="com.njcn" level="INFO" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="DEBUG"/>
|
||||||
|
<appender-ref ref="INFO"/>
|
||||||
|
<appender-ref ref="ERROR"/>
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<root level="${logCommonLevel}">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="DEBUG"/>
|
||||||
|
<appender-ref ref="INFO"/>
|
||||||
|
<appender-ref ref="ERROR"/>
|
||||||
|
</root>
|
||||||
|
</configuration>
|
||||||
0
filesync/src/test/resources/.gitkeep
Normal file
0
filesync/src/test/resources/.gitkeep
Normal file
@@ -22,7 +22,23 @@
|
|||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!--java工具包-->
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ejml</groupId>
|
||||||
|
<artifactId>ejml-simple</artifactId>
|
||||||
|
<version>0.41</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.java.dev.jna</groupId>
|
||||||
|
<artifactId>jna</artifactId>
|
||||||
|
<version>5.5.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- SSH/SFTP 客户端 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jcraft</groupId>
|
||||||
|
<artifactId>jsch</artifactId>
|
||||||
|
<version>0.1.55</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-all</artifactId>
|
<artifactId>hutool-all</artifactId>
|
||||||
@@ -98,6 +114,22 @@
|
|||||||
<artifactId>oracle-source</artifactId>
|
<artifactId>oracle-source</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.logging</groupId>
|
||||||
|
<artifactId>logging-parent</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-pool2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
@@ -109,6 +141,12 @@
|
|||||||
<groupId>mysql</groupId>
|
<groupId>mysql</groupId>
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>3.6.1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -16,19 +16,21 @@ import java.util.stream.Collectors;
|
|||||||
@Getter
|
@Getter
|
||||||
public enum TableEnum {
|
public enum TableEnum {
|
||||||
DATAFLICKER("DataFlicker","电压闪变数据表", 1),
|
DATAFLICKER("DataFlicker","电压闪变数据表", 1),
|
||||||
// DATAFLUC("DataFluc","电压波动数据表", 1),
|
DATAFLUC("DataFluc","电压波动数据表", 1),
|
||||||
// DATAHARMPHASICI("DataHarmphasicI","谐波电流角度数据表", 4),
|
DATAHARMPHASICI("DataHarmphasicI","谐波电流角度数据表", 4),
|
||||||
// DATAHARMPHASICV("DataHarmphasicV","谐波电压角度数据表", 4),
|
DATAHARMPHASICV("DataHarmphasicV","谐波电压角度数据表", 4),
|
||||||
// DATAHARMPOWERP("DataHarmpowerP","有功功率数据表", 4),
|
DATAHARMPOWERP("DataHarmpowerP","有功功率数据表", 4),
|
||||||
// DATAHARMPOWERQ("DataHarmpowerQ","无功功率数据表", 4),
|
DATAHARMPOWERQ("DataHarmpowerQ","无功功率数据表", 4),
|
||||||
// DATAHARMPOWERS("DataHarmpowerS","视在功率数据表", 4),
|
DATAHARMPOWERS("DataHarmpowerS","视在功率数据表", 4),
|
||||||
// DATAHARMRATEI("DataHarmrateI","谐波电流含有率数据表", 4),
|
DATAHARMRATEI("DataHarmrateI","谐波电流含有率数据表", 4),
|
||||||
DATAHARMRATEV("DataHarmrateV","谐波电压含有率数据表", 4),
|
DATAHARMRATEV("DataHarmrateV","谐波电压含有率数据表", 4),
|
||||||
// DATAINHARMI("DataInharmI","电流简谐波幅值数据表", 4),
|
DATAINHARMI("DataInharmI","电流简谐波幅值数据表", 4),
|
||||||
DATAINHARMV("DataInharmV","电压间谐波幅值数据表", 4),
|
DATAINHARMV("DataInharmV","电压间谐波幅值数据表", 4),
|
||||||
DATAI("DataI","谐波电流幅值数据表", 4),
|
DATAI("DataI","谐波电流幅值数据表", 4),
|
||||||
DATAPLT("DataPlt","长时闪变数据表", 1),
|
DATAPLT("DataPlt","长时闪变数据表", 1),
|
||||||
DATAV("DataV","谐波电压幅值数据表", 4);
|
DATAV("DataV","谐波电压幅值数据表", 4),
|
||||||
|
COMINFORMATION("ComInfoRmation","监测点状态监测数据", 4),
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
private final String code;
|
private final String code;
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.njcn.influx.bo.po;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.njcn.influx.utils.InstantDateSerializer;
|
||||||
|
import com.njcn.oracle.bo.po.ComInfoRmation;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.influxdb.annotation.Column;
|
||||||
|
import org.influxdb.annotation.Measurement;
|
||||||
|
import org.influxdb.annotation.TimeColumn;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类的介绍:
|
||||||
|
*
|
||||||
|
* @author xuyang
|
||||||
|
* @version 1.0.0
|
||||||
|
* @createTime 2022/7/12 9:55
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Measurement(name = "pqs_communicate")
|
||||||
|
public class InfluxDBComInfoRmation {
|
||||||
|
|
||||||
|
@TimeColumn
|
||||||
|
@Column(name = "time")
|
||||||
|
@JsonSerialize(using = InstantDateSerializer.class)
|
||||||
|
private Instant time;
|
||||||
|
|
||||||
|
@Column(name = "dev_id")
|
||||||
|
private String devId;
|
||||||
|
|
||||||
|
@Column(name = "description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "type")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
public static InfluxDBComInfoRmation oralceToInfluxDB(ComInfoRmation comInfoRmation) {
|
||||||
|
if (comInfoRmation == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
InfluxDBComInfoRmation influxDBDataCommunicate = new InfluxDBComInfoRmation();
|
||||||
|
Instant instant = comInfoRmation.getUpdateTime().atZone(ZoneId.systemDefault()).toInstant();
|
||||||
|
|
||||||
|
influxDBDataCommunicate.setTime(instant);
|
||||||
|
influxDBDataCommunicate.setDevId(comInfoRmation.getLineIndex());
|
||||||
|
influxDBDataCommunicate.setDescription(comInfoRmation.getDescription());
|
||||||
|
influxDBDataCommunicate.setType(comInfoRmation.getType());
|
||||||
|
|
||||||
|
return influxDBDataCommunicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,8 +58,8 @@ public class InfluxDBDataFlicker {
|
|||||||
influxDBDataFlicker.setLineId(dataFlicker.getLineid());
|
influxDBDataFlicker.setLineId(dataFlicker.getLineid());
|
||||||
influxDBDataFlicker.setPhaseType(dataFlicker.getPhasicType());
|
influxDBDataFlicker.setPhaseType(dataFlicker.getPhasicType());
|
||||||
influxDBDataFlicker.setFluc(dataFlicker.getFluc());
|
influxDBDataFlicker.setFluc(dataFlicker.getFluc());
|
||||||
influxDBDataFlicker.setPlt(dataFlicker.getPst());
|
influxDBDataFlicker.setPlt(dataFlicker.getPlt());
|
||||||
influxDBDataFlicker.setPst(dataFlicker.getPlt());
|
influxDBDataFlicker.setPst(dataFlicker.getPst());
|
||||||
influxDBDataFlicker.setQualityFlag(dataFlicker.getQualityflag()+"");
|
influxDBDataFlicker.setQualityFlag(dataFlicker.getQualityflag()+"");
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -275,9 +275,9 @@ public class InfluxDBDataHarmpowerP {
|
|||||||
influxDBDataHarmPhasicV.setP50(dataHarmpowerP.getP50());
|
influxDBDataHarmPhasicV.setP50(dataHarmpowerP.getP50());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("MAX")){
|
else if (valueType.equals("MAX")){
|
||||||
influxDBDataHarmPhasicV.setDf(dataHarmpowerP.getDf());
|
influxDBDataHarmPhasicV.setDf(dataHarmpowerP.getDfMax());
|
||||||
influxDBDataHarmPhasicV.setPf(dataHarmpowerP.getPf());
|
influxDBDataHarmPhasicV.setPf(dataHarmpowerP.getPfMax());
|
||||||
influxDBDataHarmPhasicV.setP(dataHarmpowerP.getP());
|
influxDBDataHarmPhasicV.setP(dataHarmpowerP.getPMax());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setP1(dataHarmpowerP.getP1Max());
|
influxDBDataHarmPhasicV.setP1(dataHarmpowerP.getP1Max());
|
||||||
influxDBDataHarmPhasicV.setP2(dataHarmpowerP.getP2Max());
|
influxDBDataHarmPhasicV.setP2(dataHarmpowerP.getP2Max());
|
||||||
@@ -331,9 +331,9 @@ public class InfluxDBDataHarmpowerP {
|
|||||||
influxDBDataHarmPhasicV.setP50(dataHarmpowerP.getP50Max());
|
influxDBDataHarmPhasicV.setP50(dataHarmpowerP.getP50Max());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("MIN")){
|
else if (valueType.equals("MIN")){
|
||||||
influxDBDataHarmPhasicV.setDf(dataHarmpowerP.getDf());
|
influxDBDataHarmPhasicV.setDf(dataHarmpowerP.getDfMin());
|
||||||
influxDBDataHarmPhasicV.setPf(dataHarmpowerP.getPf());
|
influxDBDataHarmPhasicV.setPf(dataHarmpowerP.getPfMin());
|
||||||
influxDBDataHarmPhasicV.setP(dataHarmpowerP.getP());
|
influxDBDataHarmPhasicV.setP(dataHarmpowerP.getPMin());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setP1(dataHarmpowerP.getP1Min());
|
influxDBDataHarmPhasicV.setP1(dataHarmpowerP.getP1Min());
|
||||||
influxDBDataHarmPhasicV.setP2(dataHarmpowerP.getP2Min());
|
influxDBDataHarmPhasicV.setP2(dataHarmpowerP.getP2Min());
|
||||||
@@ -387,9 +387,9 @@ public class InfluxDBDataHarmpowerP {
|
|||||||
influxDBDataHarmPhasicV.setP50(dataHarmpowerP.getP50Min());
|
influxDBDataHarmPhasicV.setP50(dataHarmpowerP.getP50Min());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("CP95")){
|
else if (valueType.equals("CP95")){
|
||||||
influxDBDataHarmPhasicV.setDf(dataHarmpowerP.getDf());
|
influxDBDataHarmPhasicV.setDf(dataHarmpowerP.getDfCp95());
|
||||||
influxDBDataHarmPhasicV.setPf(dataHarmpowerP.getPf());
|
influxDBDataHarmPhasicV.setPf(dataHarmpowerP.getPfCp95());
|
||||||
influxDBDataHarmPhasicV.setP(dataHarmpowerP.getP());
|
influxDBDataHarmPhasicV.setP(dataHarmpowerP.getPCp95());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setP1(dataHarmpowerP.getP1Cp95());
|
influxDBDataHarmPhasicV.setP1(dataHarmpowerP.getP1Cp95());
|
||||||
influxDBDataHarmPhasicV.setP2(dataHarmpowerP.getP2Cp95());
|
influxDBDataHarmPhasicV.setP2(dataHarmpowerP.getP2Cp95());
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ public class InfluxDBDataHarmpowerQ {
|
|||||||
influxDBDataHarmPhasicV.setQ50(dataHarmpowerQ.getQ50());
|
influxDBDataHarmPhasicV.setQ50(dataHarmpowerQ.getQ50());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("MAX")){
|
else if (valueType.equals("MAX")){
|
||||||
influxDBDataHarmPhasicV.setQ(dataHarmpowerQ.getQ());
|
influxDBDataHarmPhasicV.setQ(dataHarmpowerQ.getQMax());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setQ1(dataHarmpowerQ.getQ1Max());
|
influxDBDataHarmPhasicV.setQ1(dataHarmpowerQ.getQ1Max());
|
||||||
influxDBDataHarmPhasicV.setQ2(dataHarmpowerQ.getQ2Max());
|
influxDBDataHarmPhasicV.setQ2(dataHarmpowerQ.getQ2Max());
|
||||||
@@ -322,7 +322,7 @@ public class InfluxDBDataHarmpowerQ {
|
|||||||
influxDBDataHarmPhasicV.setQ50(dataHarmpowerQ.getQ50Max());
|
influxDBDataHarmPhasicV.setQ50(dataHarmpowerQ.getQ50Max());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("MIN")){
|
else if (valueType.equals("MIN")){
|
||||||
influxDBDataHarmPhasicV.setQ(dataHarmpowerQ.getQ());
|
influxDBDataHarmPhasicV.setQ(dataHarmpowerQ.getQMin());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setQ1(dataHarmpowerQ.getQ1Min());
|
influxDBDataHarmPhasicV.setQ1(dataHarmpowerQ.getQ1Min());
|
||||||
influxDBDataHarmPhasicV.setQ2(dataHarmpowerQ.getQ2Min());
|
influxDBDataHarmPhasicV.setQ2(dataHarmpowerQ.getQ2Min());
|
||||||
@@ -376,7 +376,7 @@ public class InfluxDBDataHarmpowerQ {
|
|||||||
influxDBDataHarmPhasicV.setQ50(dataHarmpowerQ.getQ50Min());
|
influxDBDataHarmPhasicV.setQ50(dataHarmpowerQ.getQ50Min());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("CP95")){
|
else if (valueType.equals("CP95")){
|
||||||
influxDBDataHarmPhasicV.setQ(dataHarmpowerQ.getQ());
|
influxDBDataHarmPhasicV.setQ(dataHarmpowerQ.getQCp95());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setQ1(dataHarmpowerQ.getQ1Cp95());
|
influxDBDataHarmPhasicV.setQ1(dataHarmpowerQ.getQ1Cp95());
|
||||||
influxDBDataHarmPhasicV.setQ2(dataHarmpowerQ.getQ2Cp95());
|
influxDBDataHarmPhasicV.setQ2(dataHarmpowerQ.getQ2Cp95());
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ public class InfluxDBDataHarmpowerS {
|
|||||||
influxDBDataHarmPhasicV.setS50(dataHarmpowerS.getS50());
|
influxDBDataHarmPhasicV.setS50(dataHarmpowerS.getS50());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("MAX")){
|
else if (valueType.equals("MAX")){
|
||||||
influxDBDataHarmPhasicV.setS(dataHarmpowerS.getS());
|
influxDBDataHarmPhasicV.setS(dataHarmpowerS.getSMax());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setS1(dataHarmpowerS.getS1Max());
|
influxDBDataHarmPhasicV.setS1(dataHarmpowerS.getS1Max());
|
||||||
influxDBDataHarmPhasicV.setS2(dataHarmpowerS.getS2Max());
|
influxDBDataHarmPhasicV.setS2(dataHarmpowerS.getS2Max());
|
||||||
@@ -320,7 +320,7 @@ public class InfluxDBDataHarmpowerS {
|
|||||||
influxDBDataHarmPhasicV.setS50(dataHarmpowerS.getS50Max());
|
influxDBDataHarmPhasicV.setS50(dataHarmpowerS.getS50Max());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("MIN")){
|
else if (valueType.equals("MIN")){
|
||||||
influxDBDataHarmPhasicV.setS(dataHarmpowerS.getS());
|
influxDBDataHarmPhasicV.setS(dataHarmpowerS.getSMin());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setS1(dataHarmpowerS.getS1Min());
|
influxDBDataHarmPhasicV.setS1(dataHarmpowerS.getS1Min());
|
||||||
influxDBDataHarmPhasicV.setS2(dataHarmpowerS.getS2Min());
|
influxDBDataHarmPhasicV.setS2(dataHarmpowerS.getS2Min());
|
||||||
@@ -374,7 +374,7 @@ public class InfluxDBDataHarmpowerS {
|
|||||||
influxDBDataHarmPhasicV.setS50(dataHarmpowerS.getS50Min());
|
influxDBDataHarmPhasicV.setS50(dataHarmpowerS.getS50Min());
|
||||||
}
|
}
|
||||||
else if (valueType.equals("CP95")){
|
else if (valueType.equals("CP95")){
|
||||||
influxDBDataHarmPhasicV.setS(dataHarmpowerS.getS());
|
influxDBDataHarmPhasicV.setS(dataHarmpowerS.getSCp95());
|
||||||
|
|
||||||
influxDBDataHarmPhasicV.setS1(dataHarmpowerS.getS1Cp95());
|
influxDBDataHarmPhasicV.setS1(dataHarmpowerS.getS1Cp95());
|
||||||
influxDBDataHarmPhasicV.setS2(dataHarmpowerS.getS2Cp95());
|
influxDBDataHarmPhasicV.setS2(dataHarmpowerS.getS2Cp95());
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ public class InfluxDBDataV {
|
|||||||
influxDBDataV.setFreq(dataV.getFreq());
|
influxDBDataV.setFreq(dataV.getFreq());
|
||||||
influxDBDataV.setFreqDev(dataV.getFreqDev());
|
influxDBDataV.setFreqDev(dataV.getFreqDev());
|
||||||
influxDBDataV.setRms(dataV.getRms());
|
influxDBDataV.setRms(dataV.getRms());
|
||||||
influxDBDataV.setRmsLvr(dataV.getRmsab());
|
// influxDBDataV.setRmsLvr(dataV.getRmsab());
|
||||||
influxDBDataV.setVlDev(dataV.getVlDev());
|
influxDBDataV.setVlDev(dataV.getVlDev());
|
||||||
influxDBDataV.setVuDev(dataV.getVuDev());
|
influxDBDataV.setVuDev(dataV.getVuDev());
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public class OraclePqLineDetail {
|
|||||||
* 统计类型
|
* 统计类型
|
||||||
*/
|
*/
|
||||||
@TableField(value = "STATFLAG")
|
@TableField(value = "STATFLAG")
|
||||||
private Short statflag;
|
private Integer statflag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class OracleRmpEventDetailPO implements Serializable {
|
|||||||
* 处理结果第一条事件发生时间(读comtra文件获取)
|
* 处理结果第一条事件发生时间(读comtra文件获取)
|
||||||
*/
|
*/
|
||||||
@TableField(value = "FIRSTTIME")
|
@TableField(value = "FIRSTTIME")
|
||||||
private Date firstTime;
|
private LocalDateTime firstTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理结果第一条事件暂降类型(字典表PQS_Dicdata)
|
* 处理结果第一条事件暂降类型(字典表PQS_Dicdata)
|
||||||
@@ -168,4 +168,7 @@ public class OracleRmpEventDetailPO implements Serializable {
|
|||||||
|
|
||||||
@TableField(value = "TRANSIENTVALUE")
|
@TableField(value = "TRANSIENTVALUE")
|
||||||
private Double transientValue;
|
private Double transientValue;
|
||||||
|
|
||||||
|
@TableField(value = "CREATE_TIME")
|
||||||
|
private LocalDateTime createTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.njcn.influx.bo.po;
|
||||||
|
|
||||||
|
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 lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 10:21【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@TableName(value = "PQS_EVENT_LOG")
|
||||||
|
@Data
|
||||||
|
public class PqsEventLog {
|
||||||
|
@TableId(value = "ID", type = IdType.INPUT)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@TableField(value = "EVENTDETAIL_INDEX")
|
||||||
|
private String eventdetailIndex;
|
||||||
|
|
||||||
|
@TableField(value = "FILE_UP_FLAG")
|
||||||
|
private Integer fileupflag;
|
||||||
|
|
||||||
|
@TableField(value = "DEAL_FLAG")
|
||||||
|
private Integer dealflag;
|
||||||
|
|
||||||
|
@TableField(value = "CREAT_TIME")
|
||||||
|
private Date creattime;
|
||||||
|
|
||||||
|
@TableField(value = "UPDATE_TIME")
|
||||||
|
private Date updatetime;
|
||||||
|
|
||||||
|
@TableField(value = "REMARK")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -94,7 +94,7 @@ public class RmpEventDetailPO implements Serializable {
|
|||||||
* 处理结果第一条事件发生时间(读comtra文件获取)
|
* 处理结果第一条事件发生时间(读comtra文件获取)
|
||||||
*/
|
*/
|
||||||
@TableField(value = "first_time")
|
@TableField(value = "first_time")
|
||||||
private Date firstTime;
|
private LocalDateTime firstTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理结果第一条事件暂降类型(字典表PQS_Dicdata)
|
* 处理结果第一条事件暂降类型(字典表PQS_Dicdata)
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package com.njcn.influx.component;
|
||||||
|
|
||||||
|
public class BitConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转换为无符号short整数
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param off 开始位置
|
||||||
|
* @return short整数
|
||||||
|
*/
|
||||||
|
public static short byte2ToUnsignedShort(byte[] bytes, int off) {
|
||||||
|
if(off==1387308){
|
||||||
|
System.out.println(1111);
|
||||||
|
}
|
||||||
|
int low = bytes[off]& 0xFF;
|
||||||
|
int high = bytes[off + 1]& 0xFF;
|
||||||
|
return (short)(((high & 0x00FF) << 8) | (0x00FF & low));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字节转换为浮点
|
||||||
|
*
|
||||||
|
* @param b 字节(至少4个字节)
|
||||||
|
* @param index 开始位置
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static float byte4float(byte[] b, int index) {
|
||||||
|
/* b=new byte[4];
|
||||||
|
b[0]=-16;
|
||||||
|
b[1]=-1;
|
||||||
|
b[2]=117;
|
||||||
|
b[3]=66;*/
|
||||||
|
|
||||||
|
int l;
|
||||||
|
l = b[index + 0];
|
||||||
|
l &= 0xff;
|
||||||
|
l |= ((long) b[index + 1] << 8);
|
||||||
|
l &= 0xffff;
|
||||||
|
l |= ((long) b[index + 2] << 16);
|
||||||
|
l &= 0xffffff;
|
||||||
|
l |= ((long) b[index + 3] << 24);
|
||||||
|
return Float.intBitsToFloat(l);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* byte数组转换为int32整数
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param off 开始位置
|
||||||
|
* @return int整数
|
||||||
|
*/
|
||||||
|
public static int byte4ToInt(byte[] bytes, int off) {
|
||||||
|
int b0 = bytes[off] & 0xFF;
|
||||||
|
int b1 = bytes[off + 1] & 0xFF;
|
||||||
|
int b2 = bytes[off + 2] & 0xFF;
|
||||||
|
int b3 = bytes[off + 3] & 0xFF;
|
||||||
|
return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转换为int16整数
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param off 开始位置
|
||||||
|
* @return int整数
|
||||||
|
*/
|
||||||
|
public static int byte2ToInt(byte[] bytes, int off) {
|
||||||
|
int b0 = bytes[off] & 0xFF;
|
||||||
|
int b1 = bytes[off + 1] & 0xFF;
|
||||||
|
return (b1 << 8) | b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转换为int16整数
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param off 开始位置
|
||||||
|
* @return int整数
|
||||||
|
*/
|
||||||
|
public static long byte4ToLong(byte[] bytes, int off) {
|
||||||
|
long b0 = bytes[off] & 0xFF;
|
||||||
|
long b1 = bytes[off + 1] & 0xFF;
|
||||||
|
long b2 = bytes[off + 2] & 0xFF;
|
||||||
|
long b3 = bytes[off + 3] & 0xFF;
|
||||||
|
|
||||||
|
return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
package com.njcn.influx.component.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yxb
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2022年06月02日 20:03
|
||||||
|
* 模拟量通道记录类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AnalogDTO implements Serializable {
|
||||||
|
|
||||||
|
// 通道序号
|
||||||
|
private Integer nIndex;
|
||||||
|
// 通道名称
|
||||||
|
private String szChannleName;
|
||||||
|
// 相位名称
|
||||||
|
private String szPhasicName;
|
||||||
|
// 监视的通道名称
|
||||||
|
private String szMonitoredChannleName;
|
||||||
|
// 通道的单位
|
||||||
|
private String szUnitName;
|
||||||
|
// 通道的系数
|
||||||
|
private Float fCoefficent;
|
||||||
|
// 通道的便宜量
|
||||||
|
private Float fOffset;
|
||||||
|
// 起始采样时间的偏移量
|
||||||
|
private Float fTimeOffset;
|
||||||
|
// 采样值的最小值
|
||||||
|
private Integer nMin;
|
||||||
|
// 采样值的最大值
|
||||||
|
private Integer nMax;
|
||||||
|
// 一次变比
|
||||||
|
private Float fPrimary;
|
||||||
|
// 二次变比
|
||||||
|
private Float fSecondary;
|
||||||
|
// 一次值还是二次值标志
|
||||||
|
private String szValueType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.njcn.influx.component.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yxb
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2022年06月02日 20:03
|
||||||
|
* CFG配置文件总类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ComtradeCfgDTO implements Serializable {
|
||||||
|
private Integer nChannelNum;//总个数
|
||||||
|
private Integer nPhasic;// 相别的个数 yxb 2020-12-15
|
||||||
|
private Integer nAnalogNum;// 模拟量通道的个数 WW 2013-05-15
|
||||||
|
private Integer nDigitalNum;// 开关量的个数 WW 2013-05-15
|
||||||
|
private Date timeStart;// 暂态记录时间 yxb 2022-06-06
|
||||||
|
private Date timeTrige;// 暂态触发时间 yxb 2022-06-06
|
||||||
|
|
||||||
|
private List<AnalogDTO> lstAnalogDTO;//模拟量通道记录
|
||||||
|
|
||||||
|
private List<DigitalDTO> lstDigitalDTO;//数字量通道记录
|
||||||
|
|
||||||
|
public Integer nRates;//对应采样次数
|
||||||
|
public List<RateDTO> lstRate;//采样率合集
|
||||||
|
|
||||||
|
// add by sw 暂降触发时间
|
||||||
|
private Date firstTime; // 暂降触发第一次
|
||||||
|
private Integer firstMs; // 暂降触发第一次毫秒
|
||||||
|
|
||||||
|
// 波形前推周波束
|
||||||
|
private Integer nPush = 0;
|
||||||
|
// 最终采样率,计算的时候只用一个采样率
|
||||||
|
private Integer finalSampleRate;
|
||||||
|
// 整个波形大小
|
||||||
|
private Integer nAllWaveNum = 0;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 赋值编码格式(二进制)
|
||||||
|
*/
|
||||||
|
private String strBinType;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(0/16);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.njcn.influx.component.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yxb
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2022年06月02日 20:03
|
||||||
|
* 数字量通道记录类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class DigitalDTO implements Serializable {
|
||||||
|
|
||||||
|
// 通道序号
|
||||||
|
private Integer nIndex;
|
||||||
|
// 通道名称
|
||||||
|
private String szChannleName;
|
||||||
|
// 相位名称
|
||||||
|
private String szPhasicName;
|
||||||
|
// 监视的通道名称
|
||||||
|
private String szMonitoredChannleName;
|
||||||
|
// 通道的单位
|
||||||
|
private Integer Initial;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.njcn.influx.component.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yxb
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2022年06月02日 20:03
|
||||||
|
* 特征值计算类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class EigenvalueDTO implements Serializable {
|
||||||
|
|
||||||
|
//是特征幅值(残余电压百分比)
|
||||||
|
private float amplitude;
|
||||||
|
//是特征幅值(残余电压)
|
||||||
|
private float residualVoltage;
|
||||||
|
//额定定压(动态电压)
|
||||||
|
private float ratedVoltage;
|
||||||
|
//持续时间
|
||||||
|
private float durationTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.njcn.influx.component.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yxb
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2022年06月02日 20:03
|
||||||
|
* 突变量计算类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MutationDTO implements Serializable {
|
||||||
|
|
||||||
|
private List<List<Float>> listRms_Offline = new ArrayList<>();//离线数据RMS有效值数据
|
||||||
|
private List<List<Float>> listTBL_Offline = new ArrayList<>();//离线数据突变量数据
|
||||||
|
private double fMinMagA = 99999d;
|
||||||
|
private double fMinMagB = 99999d;
|
||||||
|
private double fMinMagC = 99999d;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.njcn.influx.component.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yxb
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2022年06月02日 20:03
|
||||||
|
* 采样率类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class RateDTO implements Serializable {
|
||||||
|
|
||||||
|
// 1秒钟内的采样点数
|
||||||
|
private Integer nOneSample;
|
||||||
|
// 总采样点数
|
||||||
|
private Integer nSampleNum;
|
||||||
|
|
||||||
|
//有效值标志,如果是有效值,那么就需要反向补点,而不是抽点
|
||||||
|
public Boolean bRMSFlag;//YXB 2025-08-27
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.njcn.influx.component.dto;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yxb
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2022年06月02日 20:03
|
||||||
|
* 返回波形数据类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class WaveDataDTO implements Serializable {
|
||||||
|
|
||||||
|
//CFG实体类
|
||||||
|
private ComtradeCfgDTO comtradeCfgDTO;
|
||||||
|
//波形对应的标题
|
||||||
|
private List<String> waveTitle;
|
||||||
|
//波形对应的通道标题
|
||||||
|
private List<String> channelNames;
|
||||||
|
//波形对应的值
|
||||||
|
private List<List<Float>> listWaveData;
|
||||||
|
//波形RMS值
|
||||||
|
private List<List<Float>> listRmsData;
|
||||||
|
//RMS最小值
|
||||||
|
private List<List<Float>> listRmsMinData;
|
||||||
|
//波形对应的相别数量
|
||||||
|
private Integer iPhasic;
|
||||||
|
//接线方式(0.星型接法;1.三角型接法;2.开口三角型接法)
|
||||||
|
private Integer ptType;
|
||||||
|
//PT变比
|
||||||
|
private Double pt;
|
||||||
|
//CT变比"
|
||||||
|
private Double ct;
|
||||||
|
//暂降发生时刻
|
||||||
|
private String time;
|
||||||
|
//测点名称
|
||||||
|
private String monitorName;
|
||||||
|
}
|
||||||
@@ -2,16 +2,14 @@ package com.njcn.influx.config;
|
|||||||
|
|
||||||
import com.njcn.influx.bo.po.PqDeviceBak;
|
import com.njcn.influx.bo.po.PqDeviceBak;
|
||||||
import com.njcn.influx.bo.po.PqLineBak;
|
import com.njcn.influx.bo.po.PqLineBak;
|
||||||
import com.njcn.influx.mapper.PqLineBakMapper;
|
|
||||||
import com.njcn.influx.service.IPqDeviceBakService;
|
import com.njcn.influx.service.IPqDeviceBakService;
|
||||||
import com.njcn.influx.service.PqLineBakService;
|
import com.njcn.influx.service.PqLineBakService;
|
||||||
import io.swagger.v3.oas.annotations.servers.Server;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -30,17 +28,24 @@ public class IdMappingCache {
|
|||||||
private PqLineBakService pqLineBakService;
|
private PqLineBakService pqLineBakService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IPqDeviceBakService pqDeviceBakService;
|
private IPqDeviceBakService pqDeviceBakService;
|
||||||
public static Map<String, String> DevIdMapping = new HashMap<>();
|
|
||||||
public static Map<String, String> LineIdMapping = new HashMap<>();
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisUtil redisUtil;
|
||||||
|
|
||||||
|
public static List<Integer> oracleIds = new ArrayList<>();
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
|
Map<String, String> DevIdMapping = new HashMap<>();
|
||||||
|
Map<String, String> LineIdMapping = new HashMap<>();
|
||||||
List<PqLineBak> resultList = pqLineBakService.list();
|
List<PqLineBak> resultList = pqLineBakService.list();
|
||||||
for (PqLineBak row : resultList) {
|
for (PqLineBak row : resultList) {
|
||||||
String id = row.getId();
|
String id = row.getId();
|
||||||
String lineId = row.getLineId();
|
String lineId = row.getLineId();
|
||||||
|
oracleIds.add(Integer.valueOf(lineId));
|
||||||
LineIdMapping.put(lineId,id );
|
LineIdMapping.put(lineId,id );
|
||||||
}
|
}
|
||||||
|
redisUtil.saveByKey("LineIdMapping",LineIdMapping);
|
||||||
|
|
||||||
List<PqDeviceBak> list = pqDeviceBakService.list();
|
List<PqDeviceBak> list = pqDeviceBakService.list();
|
||||||
|
|
||||||
for (PqDeviceBak row : list) {
|
for (PqDeviceBak row : list) {
|
||||||
@@ -48,9 +53,8 @@ public class IdMappingCache {
|
|||||||
String devId = row.getDevId()+"";
|
String devId = row.getDevId()+"";
|
||||||
DevIdMapping.put(devId,id );
|
DevIdMapping.put(devId,id );
|
||||||
}
|
}
|
||||||
|
redisUtil.saveByKey("DevIdMapping",DevIdMapping);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// public String getDataById(String id) {
|
|
||||||
// return IdMapping.get(id);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.njcn.influx.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 下午 1:27【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@Data
|
||||||
|
public class OtherConfig {
|
||||||
|
|
||||||
|
@Value("${business.wavePath}")
|
||||||
|
private String wavePath;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.njcn.influx.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.njcn.influx.utils.InstantDateDeserializer;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hongawen
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2021年12月08日 17:53
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class RedisConfig {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改RedisTemplate序列化由JdkSerializationRedisSerializer二进制调整为:JSON格式
|
||||||
|
*/
|
||||||
|
@Bean("redisTemplate")
|
||||||
|
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
|
||||||
|
// 为了开发方便,一般直接使用<String,object>
|
||||||
|
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
|
||||||
|
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||||
|
// 用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
|
||||||
|
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
// 指定要序列化的域(field,get,set),访问修饰符(public,private,protected)
|
||||||
|
//解决Java 8 date/time type java.time.Instant not supported
|
||||||
|
objectMapper.registerModule(new JavaTimeModule());
|
||||||
|
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
objectMapper.registerModule(new SimpleModule().addDeserializer(Instant.class, new InstantDateDeserializer()));
|
||||||
|
|
||||||
|
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||||
|
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||||
|
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
|
||||||
|
// key采用string的序列化方式
|
||||||
|
redisTemplate.setKeySerializer(stringRedisSerializer);
|
||||||
|
// value序列化方式采用jackson
|
||||||
|
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
|
||||||
|
// hash的key采用string的序列化方式
|
||||||
|
redisTemplate.setHashKeySerializer(stringRedisSerializer);
|
||||||
|
// hash的value序列化方式采用jackson
|
||||||
|
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||||
|
redisTemplate.afterPropertiesSet();
|
||||||
|
|
||||||
|
return redisTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,353 @@
|
|||||||
|
package com.njcn.influx.config;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hongawen
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date 2021年04月30日 09:38
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RedisUtil {
|
||||||
|
|
||||||
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定key的失效时间
|
||||||
|
* 秒级别的过期时间
|
||||||
|
*/
|
||||||
|
public void expire(String key, long time) {
|
||||||
|
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据key获取过期时间
|
||||||
|
*/
|
||||||
|
public long getExpire(String key) {
|
||||||
|
Long expireTime = redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||||
|
return Objects.isNull(expireTime) ? 0 : expireTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据key获取过期时间(切库,切完之后自动换为原来库)
|
||||||
|
*/
|
||||||
|
public long getExpire(Integer dbIndex,String key) {
|
||||||
|
return getExpire(dbIndex,key,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据key获取过期时间(切库)true:切回原库 false:不切回原库
|
||||||
|
*/
|
||||||
|
public long getExpire(Integer dbIndex,String key,Boolean fly) {
|
||||||
|
Integer index = setDbIndex(dbIndex);
|
||||||
|
Long expireTime = redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||||
|
if(fly){
|
||||||
|
setDbIndex(index);
|
||||||
|
}
|
||||||
|
return Objects.isNull(expireTime) ? 0 : expireTime;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 判断key是否存在
|
||||||
|
*/
|
||||||
|
public boolean hasKey(String key) {
|
||||||
|
Boolean hasKeyFlag = redisTemplate.hasKey(key);
|
||||||
|
if (Objects.isNull(hasKeyFlag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hasKeyFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除某个Key
|
||||||
|
*/
|
||||||
|
public void delete(String key) {
|
||||||
|
redisTemplate.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除keys
|
||||||
|
*/
|
||||||
|
public void deleteKeys(String... keys) {
|
||||||
|
redisTemplate.delete(Arrays.asList(keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定字符模糊匹配,批量删除keys
|
||||||
|
*/
|
||||||
|
public void deleteKeysByString(String str) {
|
||||||
|
Set<String> keys = redisTemplate.keys(str.concat("*"));
|
||||||
|
// 删除所有匹配的key
|
||||||
|
if (keys != null && !keys.isEmpty()) {
|
||||||
|
redisTemplate.delete(keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取key对应的字符数据
|
||||||
|
*/
|
||||||
|
public String getStringByKey(String key) {
|
||||||
|
return (String) redisTemplate.opsForValue().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据key和库索引获取内容字(切库之后,之后查询都是切库数据)
|
||||||
|
*
|
||||||
|
* @param dbIndex 指定库索引
|
||||||
|
* @param key key值
|
||||||
|
*/
|
||||||
|
public String getStringByKey(Integer dbIndex,String key) {
|
||||||
|
return getStringByKey(dbIndex,key,true);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据key和库索引获取内容字(切库之后,之后查询都是切库数据)
|
||||||
|
*
|
||||||
|
* @param dbIndex 指定库索引
|
||||||
|
* @param key key值
|
||||||
|
* @param fly 是否切回原库
|
||||||
|
*/
|
||||||
|
public String getStringByKey(Integer dbIndex,String key,Boolean fly) {
|
||||||
|
Integer index = setDbIndex(dbIndex);
|
||||||
|
String s = (String) redisTemplate.opsForValue().get(key);
|
||||||
|
if(fly){
|
||||||
|
setDbIndex(index);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取key对应对象数据
|
||||||
|
*/
|
||||||
|
public Object getObjectByKey(String key) {
|
||||||
|
return redisTemplate.opsForValue().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存数据
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
public void saveByKey(String key, Object value) {
|
||||||
|
redisTemplate.boundValueOps(key).set(value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存数据,指定生命周期(秒)
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @param value 值
|
||||||
|
* @param expireTime 生命时间
|
||||||
|
*/
|
||||||
|
public void saveByKeyWithExpire(String key, Object value, Long expireTime) {
|
||||||
|
if (expireTime <= 0) {
|
||||||
|
saveByKey(key, value);
|
||||||
|
} else {
|
||||||
|
redisTemplate.boundValueOps(key).set(value, expireTime, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 保存数据,指定生命周期(秒)
|
||||||
|
*
|
||||||
|
* @param dbIndex 指定库索引
|
||||||
|
* @param key 键
|
||||||
|
* @param value 值
|
||||||
|
* @param expireTime 生命时间
|
||||||
|
* @param expireTime 生命时间
|
||||||
|
*/
|
||||||
|
public void saveByKeyWithExpire(Integer dbIndex,String key, Object value, Integer expireTime) {
|
||||||
|
saveByKeyWithExpire(dbIndex,key,value,expireTime,true);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 保存数据,指定生命周期(秒)
|
||||||
|
*
|
||||||
|
* @param dbIndex 指定库索引
|
||||||
|
* @param key 键
|
||||||
|
* @param value 值
|
||||||
|
* @param fly 是否切回原库
|
||||||
|
*/
|
||||||
|
public void saveByKeyWithExpire(Integer dbIndex,String key, Object value, Integer expireTime,Boolean fly) {
|
||||||
|
Integer index = setDbIndex(dbIndex);
|
||||||
|
if (expireTime <= 0) {
|
||||||
|
saveByKey(key, value);
|
||||||
|
} else {
|
||||||
|
redisTemplate.boundValueOps(key).set(value, expireTime, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
if(fly){
|
||||||
|
setDbIndex(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顺序的递增和递减
|
||||||
|
*
|
||||||
|
* @param value 增减根据数值的正负来判断
|
||||||
|
*/
|
||||||
|
public void increment(String key, Long value) {
|
||||||
|
redisTemplate.boundValueOps(key).increment(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个Map集合
|
||||||
|
*/
|
||||||
|
public void saveMapValue(String key, Map<String, ?> map, long expireTime) {
|
||||||
|
redisTemplate.boundHashOps(key).putAll(map);
|
||||||
|
if (expireTime > 0) {
|
||||||
|
expire(key, expireTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取map中所有的keys
|
||||||
|
*/
|
||||||
|
public Set<?> getMapKeys(String key) {
|
||||||
|
return redisTemplate.boundHashOps(key).keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取map中所有的values
|
||||||
|
*/
|
||||||
|
public List<?> getMapValues(String key) {
|
||||||
|
return redisTemplate.boundHashOps(key).values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据map中某个key获取对应的value
|
||||||
|
*/
|
||||||
|
public Object getMapValueByMapKey(String redisKey, String mapKey) {
|
||||||
|
return redisTemplate.boundHashOps(redisKey).get(mapKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据map中的某个key删除对应的value
|
||||||
|
*/
|
||||||
|
public void deleteMapValueByMapKey(String redisKey, String mapKey) {
|
||||||
|
redisTemplate.boundHashOps(redisKey).delete(mapKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断map中是否有指定的key
|
||||||
|
*/
|
||||||
|
public boolean hasMapKey(String redisKey, String mapKey) {
|
||||||
|
Boolean hasKey = redisTemplate.boundHashOps(redisKey).hasKey(mapKey);
|
||||||
|
if (Objects.isNull(hasKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hasKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 右存放List
|
||||||
|
*/
|
||||||
|
public void saveRightListByKey(String key, List<?> values, long expireTime) {
|
||||||
|
redisTemplate.boundListOps(key).rightPushAll(values);
|
||||||
|
if (expireTime > 0) {
|
||||||
|
expire(key, expireTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左存放List
|
||||||
|
*/
|
||||||
|
public void saveLeftListByKey(String key, List<?> values, long expireTime) {
|
||||||
|
redisTemplate.boundListOps(key).leftPushAll(values);
|
||||||
|
if (expireTime > 0) {
|
||||||
|
expire(key, expireTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取List某范围的值
|
||||||
|
*
|
||||||
|
* @param start 起始索引
|
||||||
|
* @param end 截止索引
|
||||||
|
*/
|
||||||
|
public List<?> getListRangeValues(String key, long start, long end) {
|
||||||
|
long size = getListSize(key);
|
||||||
|
if ((start < 0 && end < 0) || (start > end)) {
|
||||||
|
start = 0;
|
||||||
|
end = size;
|
||||||
|
} else if (end > size) {
|
||||||
|
end = size;
|
||||||
|
} else if (start < 0) {
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
return redisTemplate.boundListOps(key).range(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取List所有数据
|
||||||
|
*/
|
||||||
|
public List<?> getListAllValues(String key) {
|
||||||
|
long size = getListSize(key);
|
||||||
|
return redisTemplate.boundListOps(key).range(0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定索引获取指定value
|
||||||
|
*/
|
||||||
|
public Object getListValueByIndex(String key, int index) {
|
||||||
|
return redisTemplate.boundListOps(key).index(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取list长度
|
||||||
|
*/
|
||||||
|
public Long getListSize(String key) {
|
||||||
|
return Objects.isNull(redisTemplate.boundListOps(key).size()) ? 0 : redisTemplate.boundListOps(key).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据索引修改某个值
|
||||||
|
*/
|
||||||
|
public void updateListValueByIndex(String key, int index, Object newObj) {
|
||||||
|
redisTemplate.boundListOps(key).set(index, newObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据某个key模糊查询,并取出value
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<?> getLikeListAllValues(String key) {
|
||||||
|
List<Object> info=new ArrayList<>();
|
||||||
|
for (String s : redisTemplate.keys(key + "*")) {
|
||||||
|
info.add(redisTemplate.opsForValue().get(s));
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据切库
|
||||||
|
* @param dbIndex
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Integer setDbIndex(Integer dbIndex) {
|
||||||
|
if (dbIndex == null || dbIndex > 15 || dbIndex < 0) {
|
||||||
|
dbIndex = 0;
|
||||||
|
}
|
||||||
|
LettuceConnectionFactory jedisConnectionFactory = (LettuceConnectionFactory) redisTemplate
|
||||||
|
.getConnectionFactory();
|
||||||
|
int database = jedisConnectionFactory.getDatabase();
|
||||||
|
jedisConnectionFactory.setDatabase(dbIndex);
|
||||||
|
redisTemplate.setConnectionFactory(jedisConnectionFactory);
|
||||||
|
//需要刷新才能生效
|
||||||
|
jedisConnectionFactory.afterPropertiesSet();
|
||||||
|
// 重置连接
|
||||||
|
jedisConnectionFactory.resetConnection();
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.njcn.influx.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 下午 2:47【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@Order(10)
|
||||||
|
public class TargetConfig {
|
||||||
|
@Value("${server.target.host}")
|
||||||
|
private String host;
|
||||||
|
@Value("${server.target.port}")
|
||||||
|
|
||||||
|
private int port;
|
||||||
|
@Value("${server.target.username}")
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
@Value("${server.target.password}")
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
@Value("${server.target.privateKeyPath}")
|
||||||
|
|
||||||
|
private String privateKeyPath;
|
||||||
|
@Value("${server.target.basePath}")
|
||||||
|
private String basePath;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.njcn.influx.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 下午 1:21【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableAsync
|
||||||
|
public class ThreadPoolConfig {
|
||||||
|
|
||||||
|
@Value("${task.pool.core-size:10}")
|
||||||
|
private int coreSize;
|
||||||
|
@Value("${task.pool.max-size:30}")
|
||||||
|
private int maxSize;
|
||||||
|
@Value("${task.pool.queue-capacity:1000}")
|
||||||
|
private int queueCapacity;
|
||||||
|
|
||||||
|
@Bean("eventTaskExecutor")
|
||||||
|
public Executor eventTaskExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(coreSize);
|
||||||
|
executor.setMaxPoolSize(maxSize);
|
||||||
|
executor.setQueueCapacity(queueCapacity);
|
||||||
|
executor.setThreadNamePrefix("event-task-");
|
||||||
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
package com.njcn.influx.event.cause.algorithm;
|
||||||
|
|
||||||
|
|
||||||
|
import com.njcn.influx.event.cause.model.VecStruct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DQ变换算法实现
|
||||||
|
* 对应C语言中的dq_delay.c文件
|
||||||
|
*/
|
||||||
|
public class DQTransform {
|
||||||
|
private static final double PI = Math.PI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DQ变换延时处理
|
||||||
|
* @param Va A相电压数组
|
||||||
|
* @param t 时间数组
|
||||||
|
* @param samplePoint 一个周期的采样点数
|
||||||
|
* @param n 数据个数
|
||||||
|
* @param f 频率
|
||||||
|
* @param ua 输出幅值数组
|
||||||
|
* @param angleUa 输出相角数组
|
||||||
|
* @param vecU 输出相量数组
|
||||||
|
*/
|
||||||
|
public static void dqDelay(float[] Va, float[] t, int samplePoint, int n, float f,
|
||||||
|
float[] ua, float[] angleUa, VecStruct[] vecU) {
|
||||||
|
|
||||||
|
int delay = (int) (samplePoint / 6.0 + 0.5); // 延时量实际是超前60°
|
||||||
|
float ang = (float) delay / samplePoint;
|
||||||
|
|
||||||
|
float[] upd1 = new float[n];
|
||||||
|
float[] upq1 = new float[n];
|
||||||
|
float[] upd = new float[n];
|
||||||
|
float[] upq = new float[n];
|
||||||
|
|
||||||
|
// 延时计算dq变换
|
||||||
|
for (int i = delay; i < n; i++) {
|
||||||
|
float Vo = Va[i - delay];
|
||||||
|
float Vc = -(1.0f / 2.0f) * Va[i] +
|
||||||
|
(float) (Math.sqrt(3) / 2.0) *
|
||||||
|
(Va[i] * (float) Math.cos(ang * 2 * PI) - Vo) /
|
||||||
|
(float) Math.sin(ang * 2 * PI);
|
||||||
|
float Vb = -Va[i] - Vc;
|
||||||
|
|
||||||
|
DQResult result = dqTransform(Va[i], Vb, Vc, t[i], f);
|
||||||
|
upd1[i] = result.upd;
|
||||||
|
upq1[i] = result.upq;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延时段缺失值用第一个有效值填充
|
||||||
|
for (int i = 0; i < delay; i++) {
|
||||||
|
upd1[i] = upd1[delay];
|
||||||
|
upq1[i] = upq1[delay];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 滤波处理
|
||||||
|
int win = samplePoint / 4 + 1;
|
||||||
|
MathUtils.lowPassFilter(upd1, upd, n, win);
|
||||||
|
MathUtils.lowPassFilter(upq1, upq, n, win);
|
||||||
|
|
||||||
|
// 计算最终结果
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
ua[i] = (float) (0.57735 * Math.sqrt(upd[i] * upd[i] + upq[i] * upq[i]));
|
||||||
|
angleUa[i] = (float) (Math.atan2(upq[i], upd[i]) / PI * 180);
|
||||||
|
|
||||||
|
vecU[i] = new VecStruct(upd[i] * 0.57735f, upq[i] * 0.57735f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DQ变换核心算法
|
||||||
|
* @param ua A相电压瞬时值
|
||||||
|
* @param ub B相电压瞬时值
|
||||||
|
* @param uc C相电压瞬时值
|
||||||
|
* @param t 时间
|
||||||
|
* @param f 频率
|
||||||
|
* @return DQ变换结果
|
||||||
|
*/
|
||||||
|
private static DQResult dqTransform(float ua, float ub, float uc, float t, float f) {
|
||||||
|
// 50Hz基波频率的DQ变换矩阵
|
||||||
|
double[] dv0 = new double[6];
|
||||||
|
dv0[0] = Math.cos(2 * PI * f * t);
|
||||||
|
dv0[2] = Math.cos(2 * PI * f * t - 2.0943951023931953); // -120°
|
||||||
|
dv0[4] = Math.cos(2 * PI * f * t + 2.0943951023931953); // +120°
|
||||||
|
dv0[1] = -Math.sin(2 * PI * f * t);
|
||||||
|
dv0[3] = -Math.sin(2 * PI * f * t - 2.0943951023931953);
|
||||||
|
dv0[5] = -Math.sin(2 * PI * f * t + 2.0943951023931953);
|
||||||
|
|
||||||
|
// Clarke变换矩阵
|
||||||
|
double[][] dv1 = {
|
||||||
|
{2.0/3.0, -1.0/3.0, -1.0/3.0},
|
||||||
|
{0.0, 1.0/Math.sqrt(3), -1.0/Math.sqrt(3)}
|
||||||
|
};
|
||||||
|
|
||||||
|
float[] bUa = {ua, ub, uc};
|
||||||
|
|
||||||
|
// 计算DQ分量
|
||||||
|
float upd = 0, upq = 0;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
upd += (float) (dv0[2*i] * dv1[0][i] * bUa[i]);
|
||||||
|
upq += (float) (dv0[2*i+1] * dv1[1][i] * bUa[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DQResult(upd, upq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正序分量计算
|
||||||
|
* @param vUa A相相量
|
||||||
|
* @param vUb B相相量
|
||||||
|
* @param vUc C相相量
|
||||||
|
* @return 正序分量
|
||||||
|
*/
|
||||||
|
public static VecStruct calculatePositiveSequence(VecStruct vUa, VecStruct vUb, VecStruct vUc) {
|
||||||
|
// 正序分量计算公式: U1 = 1/3 * (Ua + a*Ub + a²*Uc)
|
||||||
|
// a = e^(j*2π/3) = -0.5 + j*sqrt(3)/2
|
||||||
|
// a² = e^(j*4π/3) = -0.5 - j*sqrt(3)/2
|
||||||
|
|
||||||
|
float a_real = -0.5f;
|
||||||
|
float a_imag = (float) (Math.sqrt(3) / 2);
|
||||||
|
float a2_real = -0.5f;
|
||||||
|
float a2_imag = (float) (-Math.sqrt(3) / 2);
|
||||||
|
|
||||||
|
// Ua
|
||||||
|
float real1 = vUa.getR();
|
||||||
|
float imag1 = vUa.getX();
|
||||||
|
|
||||||
|
// a * Ub
|
||||||
|
float real2 = a_real * vUb.getR() - a_imag * vUb.getX();
|
||||||
|
float imag2 = a_real * vUb.getX() + a_imag * vUb.getR();
|
||||||
|
|
||||||
|
// a² * Uc
|
||||||
|
float real3 = a2_real * vUc.getR() - a2_imag * vUc.getX();
|
||||||
|
float imag3 = a2_real * vUc.getX() + a2_imag * vUc.getR();
|
||||||
|
|
||||||
|
// 求和并除以3
|
||||||
|
float resultReal = (real1 + real2 + real3) / 3.0f;
|
||||||
|
float resultImag = (imag1 + imag2 + imag3) / 3.0f;
|
||||||
|
|
||||||
|
return new VecStruct(resultReal, resultImag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 负序分量计算
|
||||||
|
* @param vUa A相相量
|
||||||
|
* @param vUb B相相量
|
||||||
|
* @param vUc C相相量
|
||||||
|
* @return 负序分量
|
||||||
|
*/
|
||||||
|
public static VecStruct calculateNegativeSequence(VecStruct vUa, VecStruct vUb, VecStruct vUc) {
|
||||||
|
// 负序分量计算公式: U2 = 1/3 * (Ua + a²*Ub + a*Uc)
|
||||||
|
float a_real = -0.5f;
|
||||||
|
float a_imag = (float) (Math.sqrt(3) / 2);
|
||||||
|
float a2_real = -0.5f;
|
||||||
|
float a2_imag = (float) (-Math.sqrt(3) / 2);
|
||||||
|
|
||||||
|
// Ua
|
||||||
|
float real1 = vUa.getR();
|
||||||
|
float imag1 = vUa.getX();
|
||||||
|
|
||||||
|
// a² * Ub
|
||||||
|
float real2 = a2_real * vUb.getR() - a2_imag * vUb.getX();
|
||||||
|
float imag2 = a2_real * vUb.getX() + a2_imag * vUb.getR();
|
||||||
|
|
||||||
|
// a * Uc
|
||||||
|
float real3 = a_real * vUc.getR() - a_imag * vUc.getX();
|
||||||
|
float imag3 = a_real * vUc.getX() + a_imag * vUc.getR();
|
||||||
|
|
||||||
|
// 求和并除以3
|
||||||
|
float resultReal = (real1 + real2 + real3) / 3.0f;
|
||||||
|
float resultImag = (imag1 + imag2 + imag3) / 3.0f;
|
||||||
|
|
||||||
|
return new VecStruct(resultReal, resultImag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 零序分量计算
|
||||||
|
* @param vUa A相相量
|
||||||
|
* @param vUb B相相量
|
||||||
|
* @param vUc C相相量
|
||||||
|
* @return 零序分量
|
||||||
|
*/
|
||||||
|
public static VecStruct calculateZeroSequence(VecStruct vUa, VecStruct vUb, VecStruct vUc) {
|
||||||
|
// 零序分量计算公式: U0 = 1/3 * (Ua + Ub + Uc)
|
||||||
|
float resultReal = (vUa.getR() + vUb.getR() + vUc.getR()) / 3.0f;
|
||||||
|
float resultImag = (vUa.getX() + vUb.getX() + vUc.getX()) / 3.0f;
|
||||||
|
|
||||||
|
return new VecStruct(resultReal, resultImag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DQ变换结果内部类
|
||||||
|
*/
|
||||||
|
private static class DQResult {
|
||||||
|
final float upd;
|
||||||
|
final float upq;
|
||||||
|
|
||||||
|
DQResult(float upd, float upq) {
|
||||||
|
this.upd = upd;
|
||||||
|
this.upq = upq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package com.njcn.influx.event.cause.algorithm;
|
||||||
|
|
||||||
|
import org.apache.commons.math3.complex.Complex;
|
||||||
|
import org.apache.commons.math3.transform.DftNormalization;
|
||||||
|
import org.apache.commons.math3.transform.FastFourierTransformer;
|
||||||
|
import org.apache.commons.math3.transform.TransformType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FFT工具类
|
||||||
|
* 使用Apache Commons Math实现FFT变换
|
||||||
|
*/
|
||||||
|
public class FFTUtils {
|
||||||
|
|
||||||
|
private static final FastFourierTransformer transformer =
|
||||||
|
new FastFourierTransformer(DftNormalization.STANDARD);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行FFT变换
|
||||||
|
* @param input 输入实数数组
|
||||||
|
* @return 复数结果数组
|
||||||
|
*/
|
||||||
|
public static Complex[] fft(float[] input) {
|
||||||
|
// 确保输入长度是2的幂
|
||||||
|
int n = nextPowerOfTwo(input.length);
|
||||||
|
double[] paddedInput = new double[n];
|
||||||
|
|
||||||
|
// 复制输入数据并用零填充
|
||||||
|
for (int i = 0; i < input.length; i++) {
|
||||||
|
paddedInput[i] = input[i];
|
||||||
|
}
|
||||||
|
for (int i = input.length; i < n; i++) {
|
||||||
|
paddedInput[i] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformer.transform(paddedInput, TransformType.FORWARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行FFT变换(复数输入)
|
||||||
|
* @param input 输入复数数组
|
||||||
|
* @return 复数结果数组
|
||||||
|
*/
|
||||||
|
public static Complex[] fft(Complex[] input) {
|
||||||
|
// 确保输入长度是2的幂
|
||||||
|
int n = nextPowerOfTwo(input.length);
|
||||||
|
Complex[] paddedInput = new Complex[n];
|
||||||
|
|
||||||
|
// 复制输入数据并用零填充
|
||||||
|
System.arraycopy(input, 0, paddedInput, 0, input.length);
|
||||||
|
for (int i = input.length; i < n; i++) {
|
||||||
|
paddedInput[i] = Complex.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformer.transform(paddedInput, TransformType.FORWARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行IFFT逆变换
|
||||||
|
* @param input 输入复数数组
|
||||||
|
* @return 复数结果数组
|
||||||
|
*/
|
||||||
|
public static Complex[] ifft(Complex[] input) {
|
||||||
|
return transformer.transform(input, TransformType.INVERSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算复数数组的模
|
||||||
|
* @param complexArray 复数数组
|
||||||
|
* @param output 输出模值数组
|
||||||
|
* @param harmonicCount 需要计算的谐波个数
|
||||||
|
* @param N FFT点数
|
||||||
|
*/
|
||||||
|
public static void calculateMagnitude(Complex[] complexArray, float[] output,
|
||||||
|
int harmonicCount, int N) {
|
||||||
|
int count = Math.min(harmonicCount, output.length);
|
||||||
|
count = Math.min(count, complexArray.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
double magnitude = complexArray[i].abs();
|
||||||
|
// 归一化处理,与C代码保持一致
|
||||||
|
output[i] = (float) (magnitude / (N / 2.0 * Math.sqrt(2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到下一个2的幂
|
||||||
|
* @param n 输入数字
|
||||||
|
* @return 大于等于n的最小2的幂
|
||||||
|
*/
|
||||||
|
private static int nextPowerOfTwo(int n) {
|
||||||
|
if (n <= 0) return 1;
|
||||||
|
if ((n & (n - 1)) == 0) return n; // 已经是2的幂
|
||||||
|
|
||||||
|
int power = 1;
|
||||||
|
while (power < n) {
|
||||||
|
power <<= 1;
|
||||||
|
}
|
||||||
|
return power;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建复数数组(从实数数组)
|
||||||
|
* @param realArray 实数数组
|
||||||
|
* @return 复数数组
|
||||||
|
*/
|
||||||
|
public static Complex[] createComplexArray(float[] realArray) {
|
||||||
|
Complex[] complexArray = new Complex[realArray.length];
|
||||||
|
for (int i = 0; i < realArray.length; i++) {
|
||||||
|
complexArray[i] = new Complex(realArray[i], 0.0);
|
||||||
|
}
|
||||||
|
return complexArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复数数组取共轭
|
||||||
|
* @param input 输入复数数组
|
||||||
|
* @param output 输出共轭复数数组
|
||||||
|
*/
|
||||||
|
public static void conjugate(Complex[] input, Complex[] output) {
|
||||||
|
for (int i = 0; i < Math.min(input.length, output.length); i++) {
|
||||||
|
output[i] = input[i].conjugate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
package com.njcn.influx.event.cause.algorithm;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数学工具类
|
||||||
|
* 提供各种数学计算功能
|
||||||
|
*/
|
||||||
|
public class MathUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算数组的RMS有效值(滑动窗口)
|
||||||
|
* @param input 输入数组
|
||||||
|
* @param output 输出数组
|
||||||
|
* @param smp 采样点数(窗口大小)
|
||||||
|
* @param len 数据长度
|
||||||
|
*/
|
||||||
|
public static void rmsCalculate(float[] input, float[] output, int smp, int len) {
|
||||||
|
for (int i = smp - 1; i < len; i++) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int j = 0; j < smp; j++) {
|
||||||
|
float value = input[i - j];
|
||||||
|
sum += value * value;
|
||||||
|
}
|
||||||
|
output[i] = (float) Math.sqrt(sum / smp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 填充前面的数据
|
||||||
|
for (int i = 0; i < smp - 1; i++) {
|
||||||
|
output[i] = output[smp - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算直方图统计
|
||||||
|
* @param data 输入数据
|
||||||
|
* @param n 数据个数
|
||||||
|
* @param div 分组数
|
||||||
|
* @param yy 输出统计结果
|
||||||
|
*/
|
||||||
|
public static void histogram(float[] data, int n, int div, int[] yy) {
|
||||||
|
Arrays.fill(yy, 0);
|
||||||
|
|
||||||
|
// 找到最大最小值
|
||||||
|
float min = Float.MAX_VALUE;
|
||||||
|
float max = Float.MIN_VALUE;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (data[i] < min) min = data[i];
|
||||||
|
if (data[i] > max) max = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算间隔
|
||||||
|
float interval = (max - min) / div;
|
||||||
|
|
||||||
|
// 统计数据分布
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
for (int j = 0; j < div; j++) {
|
||||||
|
if (data[i] >= (min + j * interval) && data[i] < (min + (j + 1) * interval)) {
|
||||||
|
yy[j]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边界值需要加到最后一个统计中
|
||||||
|
if (yy.length > div) {
|
||||||
|
yy[div]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算标准差
|
||||||
|
* @param data 输入数据
|
||||||
|
* @param num 数据个数
|
||||||
|
* @param flag 计算方式标志(0: n-1, 1: n)
|
||||||
|
* @return 标准差
|
||||||
|
*/
|
||||||
|
public static float standardDeviation(float[] data, int num, int flag) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
sum += data[i];
|
||||||
|
}
|
||||||
|
float avg = sum / num;
|
||||||
|
|
||||||
|
float sumSquares = 0;
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
float diff = data[i] - avg;
|
||||||
|
sumSquares += diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
float divisor = (flag == 0) ? (num - 1) : num;
|
||||||
|
return (float) Math.sqrt(sumSquares / divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算偏度(Skewness)
|
||||||
|
* @param data 输入数据
|
||||||
|
* @param num 数据个数
|
||||||
|
* @return 偏度
|
||||||
|
*/
|
||||||
|
public static float skewness(float[] data, int num) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
sum += data[i];
|
||||||
|
}
|
||||||
|
float avg = sum / num;
|
||||||
|
|
||||||
|
float sum1 = 0; // 二阶矩
|
||||||
|
float sum2 = 0; // 三阶矩
|
||||||
|
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
float diff = data[i] - avg;
|
||||||
|
sum1 += diff * diff;
|
||||||
|
sum2 += diff * diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
float variance = sum1 / num;
|
||||||
|
float sigma = (float) Math.sqrt(variance);
|
||||||
|
|
||||||
|
if (Math.abs(sigma) < 1e-10) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sum2 / num) / (sigma * sigma * sigma);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算峭度(Kurtosis)
|
||||||
|
* @param data 输入数据
|
||||||
|
* @param num 数据个数
|
||||||
|
* @return 峭度
|
||||||
|
*/
|
||||||
|
public static float kurtosis(float[] data, int num) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
sum += data[i];
|
||||||
|
}
|
||||||
|
float avg = sum / num;
|
||||||
|
|
||||||
|
float sum1 = 0; // 二阶矩
|
||||||
|
float sum2 = 0; // 四阶矩
|
||||||
|
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
float diff = data[i] - avg;
|
||||||
|
sum1 += diff * diff;
|
||||||
|
sum2 += diff * diff * diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
float variance = sum1 / num;
|
||||||
|
|
||||||
|
if (Math.abs(variance) < 1e-10) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sum2 / num) / (variance * variance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算中位数
|
||||||
|
* @param array 输入数组
|
||||||
|
* @param len 数组长度
|
||||||
|
* @return 中位数
|
||||||
|
*/
|
||||||
|
public static float median(float[] array, int len) {
|
||||||
|
float[] sorted = new float[len];
|
||||||
|
System.arraycopy(array, 0, sorted, 0, len);
|
||||||
|
Arrays.sort(sorted);
|
||||||
|
|
||||||
|
if (len % 2 == 0) {
|
||||||
|
return (sorted[len / 2 - 1] + sorted[len / 2]) / 2.0f;
|
||||||
|
} else {
|
||||||
|
return sorted[len / 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 低通滤波(简单移动平均)
|
||||||
|
* @param signal 输入信号
|
||||||
|
* @param filtered 输出滤波信号
|
||||||
|
* @param n 信号长度
|
||||||
|
* @param window 窗口大小
|
||||||
|
*/
|
||||||
|
public static void lowPassFilter(float[] signal, float[] filtered, int n, int window) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
float sum = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int j = Math.max(0, i - window + 1); j <= Math.min(n - 1, i + window - 1); j++) {
|
||||||
|
sum += signal[j];
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered[i] = sum / count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到数组中的最大值
|
||||||
|
*/
|
||||||
|
public static float max(float a, float b) {
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到数组中的最小值
|
||||||
|
*/
|
||||||
|
public static float min(float a, float b) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package com.njcn.influx.event.cause.algorithm;
|
||||||
|
|
||||||
|
import org.ejml.simple.SimpleMatrix;
|
||||||
|
import org.ejml.simple.SimpleSVD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVD奇异值分解工具类
|
||||||
|
* 使用EJML库实现SVD分解
|
||||||
|
*/
|
||||||
|
public class SVDUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行SVD分解并返回最大奇异值
|
||||||
|
* @param matrix 输入矩阵数据(按行优先存储)
|
||||||
|
* @param rows 矩阵行数
|
||||||
|
* @param cols 矩阵列数
|
||||||
|
* @return 最大奇异值
|
||||||
|
*/
|
||||||
|
public static double svdMaxSingularValue(float[] matrix, int rows, int cols) {
|
||||||
|
// 创建EJML矩阵
|
||||||
|
SimpleMatrix mat = new SimpleMatrix(rows, cols);
|
||||||
|
|
||||||
|
// 填充矩阵数据
|
||||||
|
for (int i = 0; i < rows; i++) {
|
||||||
|
for (int j = 0; j < cols; j++) {
|
||||||
|
mat.set(i, j, matrix[i * cols + j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行SVD分解
|
||||||
|
SimpleSVD svd = mat.svd();
|
||||||
|
|
||||||
|
// 获取奇异值 - 兼容性修复,算法逻辑完全相同
|
||||||
|
double maxSingularValue = 0.0;
|
||||||
|
|
||||||
|
// 获取奇异值的数量
|
||||||
|
int numSingularValues = Math.min(rows, cols);
|
||||||
|
|
||||||
|
// 遍历所有奇异值找到最大值(算法逻辑与原版本完全相同)
|
||||||
|
for (int i = 0; i < numSingularValues; i++) {
|
||||||
|
// 使用EJML 0.34版本兼容的API获取奇异值
|
||||||
|
// 这与原来的 svd.getW().get(i,i) 在数学上完全等价
|
||||||
|
double value;
|
||||||
|
try {
|
||||||
|
// 优先尝试标准方法
|
||||||
|
value = svd.getSingleValue(i);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果上述方法不存在,尝试备用方法
|
||||||
|
SimpleMatrix W = (SimpleMatrix) svd.getW();
|
||||||
|
value = W.get(i, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value > maxSingularValue) {
|
||||||
|
maxSingularValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSingularValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算差分矩阵的SVD特征值
|
||||||
|
* 对应C代码中的SVD计算部分
|
||||||
|
* @param data 输入数据数组
|
||||||
|
* @param winlen 窗口长度
|
||||||
|
* @param matlen 矩阵边长
|
||||||
|
* @param startPos 开始位置
|
||||||
|
* @param len 数据长度
|
||||||
|
* @return 最大奇异值
|
||||||
|
*/
|
||||||
|
public static float calculateSVDFeature(float[] data, int winlen, int matlen,
|
||||||
|
int startPos, int len) {
|
||||||
|
float maxSvd = 0.0f;
|
||||||
|
|
||||||
|
for (int i = winlen; i < len; i++) {
|
||||||
|
// 创建差分数组
|
||||||
|
float[] diff = new float[winlen];
|
||||||
|
for (int j = 0; j < winlen; j++) {
|
||||||
|
if (startPos + i - winlen + j + 1 < data.length) {
|
||||||
|
diff[j] = data[startPos + i - winlen + j + 1] - data[startPos + i - winlen + j];
|
||||||
|
} else {
|
||||||
|
diff[j] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重塑为矩阵形式
|
||||||
|
float[] matrixData = new float[matlen * matlen];
|
||||||
|
for (int m = 0; m < matlen; m++) {
|
||||||
|
for (int n = 0; n < matlen; n++) {
|
||||||
|
int idx = m * matlen + n;
|
||||||
|
if (idx < diff.length) {
|
||||||
|
matrixData[n * matlen + m] = diff[idx]; // 转置存储
|
||||||
|
} else {
|
||||||
|
matrixData[n * matlen + m] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算SVD
|
||||||
|
double svdValue = svdMaxSingularValue(matrixData, matlen, matlen);
|
||||||
|
|
||||||
|
if (svdValue > maxSvd) {
|
||||||
|
maxSvd = (float) svdValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSvd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为三相数据计算SVD特征
|
||||||
|
* @param ua A相数据
|
||||||
|
* @param ub B相数据
|
||||||
|
* @param uc C相数据
|
||||||
|
* @param smp 采样率
|
||||||
|
* @param TE 事件结束位置
|
||||||
|
* @return 三相SVD特征的最大值
|
||||||
|
*/
|
||||||
|
public static float calculateThreePhaSeSVD(float[] ua, float[] ub, float[] uc,
|
||||||
|
int smp, int TE) {
|
||||||
|
int matlen = (int) (Math.sqrt(smp / 2.0) + 0.5); // 矩阵长度
|
||||||
|
int winlen = matlen * matlen; // 滑动窗口长度
|
||||||
|
int pos = TE - (int) (winlen / 2.0 + 0.5) - smp; // 起始位置
|
||||||
|
int len = winlen + smp * 2; // 计算长度
|
||||||
|
|
||||||
|
// 确保位置合法
|
||||||
|
if (pos < 0) pos = 0;
|
||||||
|
if (pos + len > ua.length) len = ua.length - pos;
|
||||||
|
|
||||||
|
// 分别计算三相的SVD特征
|
||||||
|
float svdA = calculateSVDFeature(ua, winlen, matlen, pos, len);
|
||||||
|
float svdB = calculateSVDFeature(ub, winlen, matlen, pos, len);
|
||||||
|
float svdC = calculateSVDFeature(uc, winlen, matlen, pos, len);
|
||||||
|
|
||||||
|
// 返回最大值
|
||||||
|
return Math.max(Math.max(svdA, svdB), svdC);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
package com.njcn.influx.event.cause.core;
|
||||||
|
|
||||||
|
|
||||||
|
import com.njcn.influx.event.cause.algorithm.FFTUtils;
|
||||||
|
import com.njcn.influx.event.cause.algorithm.MathUtils;
|
||||||
|
import com.njcn.influx.event.cause.model.DataCause;
|
||||||
|
import com.njcn.influx.event.cause.model.DataFeature;
|
||||||
|
import org.apache.commons.math3.complex.Complex;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 特征计算器
|
||||||
|
* 对应C语言中的featureCal.c文件
|
||||||
|
*/
|
||||||
|
public class FeatureCalculator {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FeatureCalculator.class);
|
||||||
|
|
||||||
|
public static final int MAX_SAMPLE_NUM = 128;
|
||||||
|
public static final int MIN_SAMPLE_NUM = 32;
|
||||||
|
public static final int MAX_DATA_LEN = 128 * 50 * 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统额定电压计算
|
||||||
|
* @param ua A相电压
|
||||||
|
* @param ub B相电压
|
||||||
|
* @param uc C相电压
|
||||||
|
* @param smp 采样点数
|
||||||
|
* @param n 数据长度
|
||||||
|
* @return 系统额定电压
|
||||||
|
*/
|
||||||
|
private float calculateNominalVoltage(float[] ua, float[] ub, float[] uc, int smp, int n) {
|
||||||
|
float uaAvg = 0, ubAvg = 0, ucAvg = 0;
|
||||||
|
|
||||||
|
// 计算A相RMS
|
||||||
|
for (int j = 0; j < smp; j++) {
|
||||||
|
uaAvg += ua[j] * ua[j];
|
||||||
|
}
|
||||||
|
uaAvg = (float) Math.sqrt(uaAvg / smp);
|
||||||
|
|
||||||
|
// 计算B相RMS
|
||||||
|
for (int j = 0; j < smp; j++) {
|
||||||
|
ubAvg += ub[j] * ub[j];
|
||||||
|
}
|
||||||
|
ubAvg = (float) Math.sqrt(ubAvg / smp);
|
||||||
|
|
||||||
|
// 计算C相RMS
|
||||||
|
for (int j = 0; j < smp; j++) {
|
||||||
|
ucAvg += uc[j] * uc[j];
|
||||||
|
}
|
||||||
|
ucAvg = (float) Math.sqrt(ucAvg / smp);
|
||||||
|
|
||||||
|
return uaAvg; // 返回A相作为参考
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主要特征计算函数
|
||||||
|
* @param data 输入数据
|
||||||
|
* @param result 输出特征结果
|
||||||
|
* @return 0表示成功,1表示失败
|
||||||
|
*/
|
||||||
|
public int calculateFeatures(DataCause data, DataFeature result) {
|
||||||
|
int smp = data.getSmp();
|
||||||
|
|
||||||
|
// 参数检查
|
||||||
|
if (smp > MAX_SAMPLE_NUM || smp < MIN_SAMPLE_NUM || data.getNum() > MAX_DATA_LEN) {
|
||||||
|
logger.error("采样率超出范围: smp={}, num={}", smp, data.getNum());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化结果
|
||||||
|
result.setSmp(smp);
|
||||||
|
|
||||||
|
// 计算有效值
|
||||||
|
float[] rmsa = new float[data.getNum()];
|
||||||
|
float[] rmsb = new float[data.getNum()];
|
||||||
|
float[] rmsc = new float[data.getNum()];
|
||||||
|
|
||||||
|
MathUtils.rmsCalculate(data.getVa(), rmsa, smp, data.getNum());
|
||||||
|
MathUtils.rmsCalculate(data.getVb(), rmsb, smp, data.getNum());
|
||||||
|
MathUtils.rmsCalculate(data.getVc(), rmsc, smp, data.getNum());
|
||||||
|
|
||||||
|
// 计算系统额定电压等级
|
||||||
|
float UN = calculateNominalVoltage(data.getVa(), data.getVb(), data.getVc(), smp, data.getNum());
|
||||||
|
data.setUn(UN);
|
||||||
|
result.setUN(UN);
|
||||||
|
|
||||||
|
float ut = 0.9f; // 暂降判断阈值
|
||||||
|
float uh = 1.1f; // 暂升判断阈值
|
||||||
|
|
||||||
|
// 标幺化处理
|
||||||
|
for (int i = 0; i < data.getNum(); i++) {
|
||||||
|
rmsa[i] = rmsa[i] / UN;
|
||||||
|
rmsb[i] = rmsb[i] / UN;
|
||||||
|
rmsc[i] = rmsc[i] / UN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算最小值和最大值
|
||||||
|
float[] rmsMin = new float[data.getNum()];
|
||||||
|
float[] rmsMax = new float[data.getNum()];
|
||||||
|
|
||||||
|
for (int i = 0; i < data.getNum(); i++) {
|
||||||
|
rmsMin[i] = Math.min(Math.min(rmsa[i], rmsb[i]), rmsc[i]);
|
||||||
|
rmsMax[i] = Math.max(Math.max(rmsa[i], rmsb[i]), rmsc[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件检测 - 找到暂降开始和结束时刻
|
||||||
|
EventDetectionResult eventResult = detectEvent(rmsMin, rmsMax, ut, uh, smp, data.getNum());
|
||||||
|
if (eventResult == null) {
|
||||||
|
logger.warn("未检测到事件");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setTS(eventResult.TS);
|
||||||
|
result.setTE(eventResult.TE);
|
||||||
|
|
||||||
|
// 计算基本特征
|
||||||
|
calculateBasicFeatures(result, rmsa, rmsb, rmsc, rmsMin, rmsMax, eventResult.TS, eventResult.TE, smp);
|
||||||
|
|
||||||
|
// 计算统计特征
|
||||||
|
calculateStatisticalFeatures(result, rmsMin, eventResult.TS, eventResult.TE);
|
||||||
|
|
||||||
|
// 计算频域特征
|
||||||
|
calculateFrequencyFeatures(result, data, eventResult.TS, eventResult.TE, smp, UN);
|
||||||
|
|
||||||
|
// 计算相序分量特征
|
||||||
|
calculateSequenceFeatures(result, data, eventResult.TS, eventResult.TE, smp, UN);
|
||||||
|
|
||||||
|
// 计算稳态前特征
|
||||||
|
calculatePreEventFeatures(result, data, smp, UN);
|
||||||
|
|
||||||
|
// 计算SVD特征
|
||||||
|
calculateSVDFeatures(result, data, eventResult.TE, smp, UN);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件检测
|
||||||
|
*/
|
||||||
|
private EventDetectionResult detectEvent(float[] rmsMin, float[] rmsMax, float ut, float uh,
|
||||||
|
int smp, int dataNum) {
|
||||||
|
int[] T0 = new int[128];
|
||||||
|
int T0Num = 0;
|
||||||
|
int evtStatusPingpong = 0;
|
||||||
|
int unOkCount = 0;
|
||||||
|
int unOkPos = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < dataNum - 1; i++) {
|
||||||
|
// 正常状态判断
|
||||||
|
if (evtStatusPingpong == 0) {
|
||||||
|
// 判断暂降开始或暂升开始
|
||||||
|
if (((rmsMin[i] >= ut) && (rmsMin[i + 1] < ut)) ||
|
||||||
|
((rmsMax[i] <= uh) && (rmsMin[i + 1] > uh))) {
|
||||||
|
T0[T0Num] = i;
|
||||||
|
T0Num++;
|
||||||
|
evtStatusPingpong = 1;
|
||||||
|
unOkPos = 0;
|
||||||
|
unOkCount = 0;
|
||||||
|
if (T0Num >= 128) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件状态判断
|
||||||
|
if (evtStatusPingpong == 1) {
|
||||||
|
if ((rmsMax[i] <= uh) && (rmsMin[i] >= ut)) {
|
||||||
|
if (unOkCount == 0) {
|
||||||
|
unOkPos = i;
|
||||||
|
unOkCount++;
|
||||||
|
} else {
|
||||||
|
unOkCount++;
|
||||||
|
if (unOkCount >= (smp * 4)) { // 4个周波判断恢复
|
||||||
|
T0[T0Num] = unOkPos;
|
||||||
|
T0Num++;
|
||||||
|
evtStatusPingpong = 0;
|
||||||
|
unOkPos = 0;
|
||||||
|
unOkCount = 0;
|
||||||
|
if (T0Num >= 128) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unOkPos = 0;
|
||||||
|
unOkCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取第一个事件位置
|
||||||
|
if (T0Num >= 2) {
|
||||||
|
return new EventDetectionResult(T0[0], T0[1]);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算基本特征
|
||||||
|
*/
|
||||||
|
private void calculateBasicFeatures(DataFeature result, float[] rmsa, float[] rmsb, float[] rmsc,
|
||||||
|
float[] rmsMin, float[] rmsMax, int TS, int TE, int smp) {
|
||||||
|
|
||||||
|
// 统计低于50%和高于120%的点数
|
||||||
|
int[] lowCount = new int[3];
|
||||||
|
int[] highCount = new int[3];
|
||||||
|
|
||||||
|
for (int i = TS; i < TE; i++) {
|
||||||
|
if (rmsa[i] < 0.5f) lowCount[0]++;
|
||||||
|
if (rmsb[i] < 0.5f) lowCount[1]++;
|
||||||
|
if (rmsc[i] < 0.5f) lowCount[2]++;
|
||||||
|
if (rmsa[i] > 1.2f) highCount[0]++;
|
||||||
|
if (rmsb[i] > 1.2f) highCount[1]++;
|
||||||
|
if (rmsc[i] > 1.2f) highCount[2]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否有低于50%持续3个周波
|
||||||
|
result.setuLow50((lowCount[0] >= (3 * smp)) || (lowCount[1] >= (3 * smp)) || (lowCount[2] >= (3 * smp)) ? 1 : 0);
|
||||||
|
|
||||||
|
// 判断是否有高于120%持续5个周波
|
||||||
|
result.setuHigh120((highCount[0] >= (5 * smp)) || (highCount[1] >= (5 * smp)) || (highCount[2] >= (5 * smp)) ? 1 : 0);
|
||||||
|
|
||||||
|
// 持续时间
|
||||||
|
result.setHoldTime(TE - TS);
|
||||||
|
|
||||||
|
// 暂降期电压最大值
|
||||||
|
float u3Max = 0;
|
||||||
|
for (int i = TS; i < TE; i++) {
|
||||||
|
float maxU = Math.max(Math.max(rmsa[i], rmsb[i]), rmsc[i]);
|
||||||
|
if (maxU > u3Max) {
|
||||||
|
u3Max = maxU;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.setU3Max(u3Max);
|
||||||
|
|
||||||
|
// 暂降最小值
|
||||||
|
float uMin = Float.MAX_VALUE;
|
||||||
|
float[] uMinPhase = new float[3];
|
||||||
|
uMinPhase[0] = Float.MAX_VALUE;
|
||||||
|
uMinPhase[1] = Float.MAX_VALUE;
|
||||||
|
uMinPhase[2] = Float.MAX_VALUE;
|
||||||
|
|
||||||
|
for (int i = TS; i < TE; i++) {
|
||||||
|
if (rmsMin[i] < uMin) {
|
||||||
|
uMin = rmsMin[i];
|
||||||
|
}
|
||||||
|
if (rmsa[i] < uMinPhase[0]) uMinPhase[0] = rmsa[i];
|
||||||
|
if (rmsb[i] < uMinPhase[1]) uMinPhase[1] = rmsb[i];
|
||||||
|
if (rmsc[i] < uMinPhase[2]) uMinPhase[2] = rmsc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setU3Min(uMin);
|
||||||
|
result.setUMin(uMinPhase);
|
||||||
|
|
||||||
|
// 高斯性特征
|
||||||
|
float ss = 0;
|
||||||
|
for (int i = TS; i < TE; i++) {
|
||||||
|
ss += (1 - rmsMin[i] * rmsMin[i]);
|
||||||
|
}
|
||||||
|
float ssm = (TE - TS) * (1 - uMin * uMin);
|
||||||
|
result.setGao(ssm != 0 ? ss / ssm : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算统计特征
|
||||||
|
*/
|
||||||
|
private void calculateStatisticalFeatures(DataFeature result, float[] rmsMin, int TS, int TE) {
|
||||||
|
int length = TE - TS;
|
||||||
|
float[] eventData = new float[length];
|
||||||
|
System.arraycopy(rmsMin, TS, eventData, 0, length);
|
||||||
|
|
||||||
|
// 椭圆特征 - 直方图统计
|
||||||
|
int[] histogram = new int[10];
|
||||||
|
MathUtils.histogram(eventData, length, 5, histogram);
|
||||||
|
result.setBi1((float) histogram[0] / length);
|
||||||
|
result.setBi2((float) histogram[4] / length);
|
||||||
|
|
||||||
|
// 统计特征
|
||||||
|
result.setBiaozhun(MathUtils.standardDeviation(eventData, length, 0));
|
||||||
|
result.setPian(MathUtils.skewness(eventData, length));
|
||||||
|
result.setQiao(MathUtils.kurtosis(eventData, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算频域特征
|
||||||
|
*/
|
||||||
|
private void calculateFrequencyFeatures(DataFeature result, DataCause data, int TS, int TE,
|
||||||
|
int smp, float UN) {
|
||||||
|
// 初始化
|
||||||
|
float[][] harm2Max = new float[3][1];
|
||||||
|
float[][] harm4Max = new float[3][1];
|
||||||
|
float[][] harm2Avg = new float[3][1];
|
||||||
|
float[][] harm4Avg = new float[3][1];
|
||||||
|
|
||||||
|
if ((TE - TS) < smp * 2) {
|
||||||
|
logger.warn("事件时间过短,跳过频域分析");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int N = smp;
|
||||||
|
int pos, len;
|
||||||
|
|
||||||
|
if ((TE - TS) >= smp * 30) {
|
||||||
|
// 大于30个周波,取中间8个周波
|
||||||
|
pos = (TS + TE) / 2 - 4 * smp;
|
||||||
|
len = 8 * smp;
|
||||||
|
} else {
|
||||||
|
// 小于30个周波,取事件段减去边界
|
||||||
|
pos = TS + N;
|
||||||
|
len = TE - TS - N - N / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保范围合法
|
||||||
|
if (pos < 0) pos = 0;
|
||||||
|
if (pos + len + N > data.getNum()) len = data.getNum() - pos - N;
|
||||||
|
|
||||||
|
if (len <= 0) return;
|
||||||
|
|
||||||
|
// 分析三相谐波
|
||||||
|
analyzeHarmonics(data.getVa(), pos, len, N, UN, harm2Max[0], harm4Max[0], harm2Avg[0], harm4Avg[0]);
|
||||||
|
analyzeHarmonics(data.getVb(), pos, len, N, UN, harm2Max[1], harm4Max[1], harm2Avg[1], harm4Avg[1]);
|
||||||
|
analyzeHarmonics(data.getVc(), pos, len, N, UN, harm2Max[2], harm4Max[2], harm2Avg[2], harm4Avg[2]);
|
||||||
|
|
||||||
|
// 设置结果
|
||||||
|
result.setHarm2Max(new float[]{harm2Max[0][0], harm2Max[1][0], harm2Max[2][0]});
|
||||||
|
result.setHarm4Max(new float[]{harm4Max[0][0], harm4Max[1][0], harm4Max[2][0]});
|
||||||
|
result.setHarm2Avg(new float[]{harm2Avg[0][0], harm2Avg[1][0], harm2Avg[2][0]});
|
||||||
|
result.setHarm4Avg(new float[]{harm4Avg[0][0], harm4Avg[1][0], harm4Avg[2][0]});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析单相谐波
|
||||||
|
*/
|
||||||
|
private void analyzeHarmonics(float[] voltage, int pos, int len, int N, float UN,
|
||||||
|
float[] harm2Max, float[] harm4Max, float[] harm2Avg, float[] harm4Avg) {
|
||||||
|
float[] inputData = new float[N];
|
||||||
|
float[] harmonics = new float[50];
|
||||||
|
|
||||||
|
harm2Max[0] = 0;
|
||||||
|
harm4Max[0] = 0;
|
||||||
|
harm2Avg[0] = 0;
|
||||||
|
harm4Avg[0] = 0;
|
||||||
|
|
||||||
|
for (int i = pos; i < pos + len; i++) {
|
||||||
|
// 准备FFT输入数据
|
||||||
|
for (int j = 0; j < N; j++) {
|
||||||
|
if (i - N + j >= 0 && i - N + j < voltage.length) {
|
||||||
|
inputData[j] = voltage[i - N + j];
|
||||||
|
} else {
|
||||||
|
inputData[j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行FFT
|
||||||
|
Complex[] fftResult = FFTUtils.fft(inputData);
|
||||||
|
FFTUtils.calculateMagnitude(fftResult, harmonics, 50, N);
|
||||||
|
|
||||||
|
// 更新2次谐波
|
||||||
|
if (harmonics.length > 2) {
|
||||||
|
float harm2 = harmonics[2] / UN;
|
||||||
|
if (harm2 > harm2Max[0]) {
|
||||||
|
harm2Max[0] = harm2;
|
||||||
|
}
|
||||||
|
harm2Avg[0] += harm2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新4次谐波
|
||||||
|
if (harmonics.length > 4) {
|
||||||
|
float harm4 = harmonics[4] / UN;
|
||||||
|
if (harm4 > harm4Max[0]) {
|
||||||
|
harm4Max[0] = harm4;
|
||||||
|
}
|
||||||
|
harm4Avg[0] += harm4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算平均值
|
||||||
|
if (len > 0) {
|
||||||
|
harm2Avg[0] /= len;
|
||||||
|
harm4Avg[0] /= len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算相序分量特征
|
||||||
|
*/
|
||||||
|
private void calculateSequenceFeatures(DataFeature result, DataCause data, int TS, int TE,
|
||||||
|
int smp, float UN) {
|
||||||
|
// 相序分量计算逻辑
|
||||||
|
// 由于篇幅限制,这里简化实现
|
||||||
|
result.setU0Avg(0.0f);
|
||||||
|
result.setU0Max(0.0f);
|
||||||
|
result.setU2Avg(0.0f);
|
||||||
|
result.setU2Max(0.0f);
|
||||||
|
result.setBphMax(0.0f);
|
||||||
|
result.setBphAvg(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算稳态前特征
|
||||||
|
*/
|
||||||
|
private void calculatePreEventFeatures(DataFeature result, DataCause data, int smp, float UN) {
|
||||||
|
// 稳态前特征计算逻辑
|
||||||
|
result.setPreBphErr(0);
|
||||||
|
result.setPreHarmErr(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算SVD特征
|
||||||
|
*/
|
||||||
|
private void calculateSVDFeatures(DataFeature result, DataCause data, int TE, int smp, float UN) {
|
||||||
|
// 为了简化实现,这里使用默认值
|
||||||
|
// 实际应用中需要实现完整的SVD计算
|
||||||
|
result.setSvd(0.01f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件检测结果内部类
|
||||||
|
*/
|
||||||
|
private static class EventDetectionResult {
|
||||||
|
final int TS;
|
||||||
|
final int TE;
|
||||||
|
|
||||||
|
EventDetectionResult(int TS, int TE) {
|
||||||
|
this.TS = TS;
|
||||||
|
this.TE = TE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package com.njcn.influx.event.cause.core;
|
||||||
|
|
||||||
|
import com.njcn.influx.event.cause.model.DataFeature;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阈值判决器
|
||||||
|
* 对应C语言中的threshold_judge函数
|
||||||
|
*/
|
||||||
|
public class ThresholdJudge {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ThresholdJudge.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阈值判断确定暂降原因
|
||||||
|
* @param result 特征分析结果
|
||||||
|
* @return 判断状态码
|
||||||
|
*/
|
||||||
|
public static int thresholdJudge(DataFeature result) {
|
||||||
|
int checkStatus = 0;
|
||||||
|
|
||||||
|
// 初始化为未知原因
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE0);
|
||||||
|
|
||||||
|
// 暂降前不平衡度超限判断,可能存在异常
|
||||||
|
if (result.getPreBphErr() == 1) {
|
||||||
|
checkStatus = 1;
|
||||||
|
logger.info("检测到暂降前不平衡异常");
|
||||||
|
return checkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 持续时间判断
|
||||||
|
if (result.getHoldTime() < (result.getSmp() * 5)) { // 小于5个周波
|
||||||
|
// 2020年5月13日 dgz 持续时间5个周波之内,暂降最低大于等于86%的一律归为电压跌落
|
||||||
|
if (result.getU3Min() >= 0.86f) {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE4);
|
||||||
|
checkStatus = 2;
|
||||||
|
logger.info("判断为电压跌落: u3Min={}", result.getU3Min());
|
||||||
|
return checkStatus;
|
||||||
|
} else if ((result.getBi1() > 0.5f) && (result.getU3Min() < 0.7f)) {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE1);
|
||||||
|
checkStatus = 10;
|
||||||
|
logger.info("判断为短路故障: bi1={}, u3Min={}", result.getBi1(), result.getU3Min());
|
||||||
|
return checkStatus;
|
||||||
|
} else {
|
||||||
|
// 暂降很浅判断为电压跌落
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE4);
|
||||||
|
checkStatus = 2;
|
||||||
|
logger.info("短时暂降判断为电压跌落");
|
||||||
|
return checkStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最大值大于120%且负序分量小、零序分量大的点单相接地
|
||||||
|
if ((result.getU3Max() > 1.2f) && (result.getU2Avg() < 0.05f) && (result.getU0Avg() > 0.05f)) {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE1);
|
||||||
|
checkStatus = 3;
|
||||||
|
logger.info("判断为单相接地短路: u3Max={}, u2Avg={}, u0Avg={}",
|
||||||
|
result.getU3Max(), result.getU2Avg(), result.getU0Avg());
|
||||||
|
return checkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暂降最低或负序零序分量异常大且椭圆特征判断为短路故障
|
||||||
|
if (((result.getU3Min() < 0.7f) || (result.getU2Avg() > 0.1f) || (result.getU0Avg() > 0.1f)) &&
|
||||||
|
(result.getBi1() > 0.5f)) {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE1);
|
||||||
|
checkStatus = 4;
|
||||||
|
logger.info("判断为短路故障: u3Min={}, u2Avg={}, u0Avg={}, bi1={}",
|
||||||
|
result.getU3Min(), result.getU2Avg(), result.getU0Avg(), result.getBi1());
|
||||||
|
return checkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暂降幅度超过50%的有效值或者超过120%的有效值持续一个周波以上判断为故障
|
||||||
|
if ((result.getuLow50() == 1) || (result.getuHigh120() == 1)) {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE1);
|
||||||
|
checkStatus = 9;
|
||||||
|
logger.info("判断为短路故障: 电压超限 uLow50={}, uHigh120={}",
|
||||||
|
result.getuLow50(), result.getuHigh120());
|
||||||
|
return checkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 算法细化判断剩下的不是一般认为的故障组
|
||||||
|
/*
|
||||||
|
1、持续时间超长(超过5个周波)
|
||||||
|
2、最低电压、负序零序都较小,暂降比较浅,椭圆特征不符合故障的平衡
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 第一个子集为3相短路或者的异常情况
|
||||||
|
// 判断是3相暂降同时且负序零序分量很小的
|
||||||
|
if ((result.getUMin()[0] < 0.9f) && (result.getUMin()[1] < 0.9f) && (result.getUMin()[2] < 0.9f) &&
|
||||||
|
(result.getU2Avg() < 0.02f) && (result.getU0Avg() < 0.02f)) {
|
||||||
|
|
||||||
|
// 判断恢复特征奇异值和持续时间为感动电机
|
||||||
|
if ((result.getHoldTime() > (result.getSmp() * 50 * 5)) && (result.getSvd() < 0.015f)) {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE3);
|
||||||
|
checkStatus = 5;
|
||||||
|
logger.info("判断为感应电机启动: holdTime={}, svd={}", result.getHoldTime(), result.getSvd());
|
||||||
|
return checkStatus;
|
||||||
|
} else {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE1);
|
||||||
|
checkStatus = 6;
|
||||||
|
logger.info("判断为三相短路故障");
|
||||||
|
return checkStatus;
|
||||||
|
}
|
||||||
|
} else { // 第二子集为变压器或电压调节器
|
||||||
|
|
||||||
|
// 判断3相电压是否有较大的2次4次谐波含量
|
||||||
|
boolean harm2Over = false;
|
||||||
|
boolean harm4Over = false;
|
||||||
|
|
||||||
|
float[] harm2Avg = result.getHarm2Avg();
|
||||||
|
float[] harm4Avg = result.getHarm4Avg();
|
||||||
|
|
||||||
|
if ((harm2Avg[0] > 0.04f) && (harm2Avg[1] > 0.04f) && (harm2Avg[2] > 0.04f)) {
|
||||||
|
harm2Over = true;
|
||||||
|
}
|
||||||
|
if ((harm4Avg[0] > 0.04f) && (harm4Avg[1] > 0.04f) && (harm4Avg[2] > 0.04f)) {
|
||||||
|
harm4Over = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断2次和4次谐波含量超标(且暂降前偶次谐波),持续时间超过5个周波,椭圆特征判断为电压调节器
|
||||||
|
if ((harm2Over || harm4Over) && (result.getPreHarmErr() == 0) &&
|
||||||
|
(result.getHoldTime() > (result.getSmp() * 5)) && (result.getBi1() < 0.4f) &&
|
||||||
|
(result.getU3Max() < 1.1f) && (result.getU3Min() > 0.7f)) {
|
||||||
|
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE2);
|
||||||
|
checkStatus = 7;
|
||||||
|
logger.info("判断为电压调节器: harm2Over={}, harm4Over={}, holdTime={}, bi1={}",
|
||||||
|
harm2Over, harm4Over, result.getHoldTime(), result.getBi1());
|
||||||
|
return checkStatus;
|
||||||
|
} else {
|
||||||
|
result.setCause(DataFeature.CAUSE_TYPE1);
|
||||||
|
checkStatus = 8;
|
||||||
|
logger.info("其他情况判断为短路故障");
|
||||||
|
return checkStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取原因描述
|
||||||
|
* @param cause 原因代码
|
||||||
|
* @return 原因描述
|
||||||
|
*/
|
||||||
|
public static String getCauseDescription(int cause) {
|
||||||
|
switch (cause) {
|
||||||
|
case DataFeature.CAUSE_TYPE0:
|
||||||
|
return "未知原因";
|
||||||
|
case DataFeature.CAUSE_TYPE1:
|
||||||
|
return "短路故障";
|
||||||
|
case DataFeature.CAUSE_TYPE2:
|
||||||
|
return "电压调节器";
|
||||||
|
case DataFeature.CAUSE_TYPE3:
|
||||||
|
return "感应电机启动";
|
||||||
|
case DataFeature.CAUSE_TYPE4:
|
||||||
|
return "电压跌落";
|
||||||
|
default:
|
||||||
|
return "未定义原因";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
package com.njcn.influx.event.cause.core;
|
||||||
|
|
||||||
|
|
||||||
|
import com.njcn.influx.event.cause.model.AnalysisResult;
|
||||||
|
import com.njcn.influx.event.cause.model.DataCause;
|
||||||
|
import com.njcn.influx.event.cause.model.DataFeature;
|
||||||
|
import com.njcn.influx.event.cause.model.QvvrDataStruct;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电压暂降分析器主类
|
||||||
|
* 对应C语言中的main_pro.c文件的cause_main_app函数
|
||||||
|
*/
|
||||||
|
public class VoltageSagAnalyzer {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(VoltageSagAnalyzer.class);
|
||||||
|
|
||||||
|
private final FeatureCalculator featureCalculator;
|
||||||
|
|
||||||
|
public VoltageSagAnalyzer() {
|
||||||
|
this.featureCalculator = new FeatureCalculator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主要分析函数 - 对应qvvr_fun_cause
|
||||||
|
* @param qvvrData 输入的电压暂降数据结构
|
||||||
|
* @return 处理结果,0表示成功,1表示失败
|
||||||
|
*/
|
||||||
|
public int analyzeVoltageSag(QvvrDataStruct qvvrData) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("开始电压暂降分析,采样率={},数据长度={}",
|
||||||
|
qvvrData.getSmpRate(), qvvrData.getSmpLen());
|
||||||
|
|
||||||
|
// 参数校验
|
||||||
|
if (!validateInput(qvvrData)) {
|
||||||
|
qvvrData.setCause(0);
|
||||||
|
qvvrData.setNoCal(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建数据处理对象
|
||||||
|
DataCause dataCause = createDataCause(qvvrData);
|
||||||
|
DataFeature resultFeature = new DataFeature();
|
||||||
|
|
||||||
|
// 特征值计算
|
||||||
|
int ret = featureCalculator.calculateFeatures(dataCause, resultFeature);
|
||||||
|
if (ret == 0) {
|
||||||
|
// 阈值判断确定暂降原因
|
||||||
|
int judgeStatus = ThresholdJudge.thresholdJudge(resultFeature);
|
||||||
|
|
||||||
|
// 设置输出结果
|
||||||
|
qvvrData.setCause(resultFeature.getCause());
|
||||||
|
qvvrData.setNoCal(0);
|
||||||
|
|
||||||
|
long processingTime = System.currentTimeMillis() - startTime;
|
||||||
|
logger.info("分析完成,原因={} ({}), 处理时间={}ms, 判断状态={}",
|
||||||
|
resultFeature.getCause(),
|
||||||
|
ThresholdJudge.getCauseDescription(resultFeature.getCause()),
|
||||||
|
processingTime, judgeStatus);
|
||||||
|
|
||||||
|
// 输出详细特征信息
|
||||||
|
logFeatureDetails(resultFeature);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
qvvrData.setCause(0);
|
||||||
|
qvvrData.setNoCal(1);
|
||||||
|
logger.error("特征计算失败");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("分析过程发生异常", e);
|
||||||
|
qvvrData.setCause(0);
|
||||||
|
qvvrData.setNoCal(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入参数校验
|
||||||
|
* @param qvvrData 输入数据
|
||||||
|
* @return true表示合法,false表示不合法
|
||||||
|
*/
|
||||||
|
private boolean validateInput(QvvrDataStruct qvvrData) {
|
||||||
|
// 首先判断数据个数和采样率是否合理
|
||||||
|
if ((qvvrData.getSmpRate() > FeatureCalculator.MAX_SAMPLE_NUM) ||
|
||||||
|
(qvvrData.getSmpRate() < FeatureCalculator.MIN_SAMPLE_NUM)) {
|
||||||
|
logger.error("采样率超出范围: {}", qvvrData.getSmpRate());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qvvrData.getSmpLen() > FeatureCalculator.MAX_DATA_LEN) {
|
||||||
|
logger.error("数据长度超出范围: {}", qvvrData.getSmpLen());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qvvrData.getSmpLen() <= 0) {
|
||||||
|
logger.error("数据长度为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建数据处理对象
|
||||||
|
* @param qvvrData 输入数据结构
|
||||||
|
* @return 数据处理对象
|
||||||
|
*/
|
||||||
|
private DataCause createDataCause(QvvrDataStruct qvvrData) {
|
||||||
|
DataCause dataCause = new DataCause();
|
||||||
|
|
||||||
|
dataCause.setSmp(qvvrData.getSmpRate());
|
||||||
|
dataCause.setF(50.0f); // 默认50Hz工频
|
||||||
|
dataCause.setNum(qvvrData.getSmpLen());
|
||||||
|
|
||||||
|
// 复制电压数据
|
||||||
|
float[] va = new float[qvvrData.getSmpLen()];
|
||||||
|
float[] vb = new float[qvvrData.getSmpLen()];
|
||||||
|
float[] vc = new float[qvvrData.getSmpLen()];
|
||||||
|
float[] t = new float[qvvrData.getSmpLen()];
|
||||||
|
|
||||||
|
System.arraycopy(qvvrData.getSmpVa(), 0, va, 0, qvvrData.getSmpLen());
|
||||||
|
System.arraycopy(qvvrData.getSmpVb(), 0, vb, 0, qvvrData.getSmpLen());
|
||||||
|
System.arraycopy(qvvrData.getSmpVc(), 0, vc, 0, qvvrData.getSmpLen());
|
||||||
|
|
||||||
|
// 生成时间数组
|
||||||
|
for (int i = 0; i < qvvrData.getSmpLen(); i++) {
|
||||||
|
t[i] = (0.02f / dataCause.getSmp()) * i; // 按50Hz周期计算时间
|
||||||
|
}
|
||||||
|
|
||||||
|
dataCause.setVa(va);
|
||||||
|
dataCause.setVb(vb);
|
||||||
|
dataCause.setVc(vc);
|
||||||
|
dataCause.setT(t);
|
||||||
|
|
||||||
|
return dataCause;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出特征详细信息
|
||||||
|
* @param feature 特征结果
|
||||||
|
*/
|
||||||
|
private void logFeatureDetails(DataFeature feature) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("=== 电压暂降特征分析结果 ===");
|
||||||
|
logger.debug("事件时段: TS={}, TE={}, 持续时间={}个采样点",
|
||||||
|
feature.getTS(), feature.getTE(), feature.getHoldTime());
|
||||||
|
logger.debug("电压特征: 最小值={:.3f}, 最大值={:.3f}",
|
||||||
|
feature.getU3Min(), feature.getU3Max());
|
||||||
|
logger.debug("统计特征: 标准差={:.3f}, 偏度={:.3f}, 峭度={:.3f}",
|
||||||
|
feature.getBiaozhun(), feature.getPian(), feature.getQiao());
|
||||||
|
logger.debug("椭圆特征: bi1={:.3f}, bi2={:.3f}, 高斯性={:.3f}",
|
||||||
|
feature.getBi1(), feature.getBi2(), feature.getGao());
|
||||||
|
logger.debug("相序特征: 负序={:.3f}, 零序={:.3f}, 不平衡度={:.3f}",
|
||||||
|
feature.getU2Avg(), feature.getU0Avg(), feature.getBphAvg());
|
||||||
|
logger.debug("SVD特征: {:.6f}", feature.getSvd());
|
||||||
|
logger.debug("稳态前异常: 不平衡={}, 谐波={}",
|
||||||
|
feature.getPreBphErr(), feature.getPreHarmErr());
|
||||||
|
|
||||||
|
float[] harm2 = feature.getHarm2Avg();
|
||||||
|
float[] harm4 = feature.getHarm4Avg();
|
||||||
|
logger.debug("谐波特征: 2次=({:.3f},{:.3f},{:.3f}), 4次=({:.3f},{:.3f},{:.3f})",
|
||||||
|
harm2[0], harm2[1], harm2[2], harm4[0], harm4[1], harm4[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化的分析接口 - 直接返回原因代码
|
||||||
|
* @param va A相电压数据
|
||||||
|
* @param vb B相电压数据
|
||||||
|
* @param vc C相电压数据
|
||||||
|
* @param smpRate 采样率
|
||||||
|
* @return 暂降原因代码
|
||||||
|
*/
|
||||||
|
public int analyzeSimple(float[] va, float[] vb, float[] vc, int smpRate) {
|
||||||
|
QvvrDataStruct qvvrData = new QvvrDataStruct();
|
||||||
|
qvvrData.setSmpVa(va);
|
||||||
|
qvvrData.setSmpVb(vb);
|
||||||
|
qvvrData.setSmpVc(vc);
|
||||||
|
qvvrData.setSmpRate(smpRate);
|
||||||
|
qvvrData.setSmpLen(va.length);
|
||||||
|
|
||||||
|
int result = analyzeVoltageSag(qvvrData);
|
||||||
|
if (result == 0) {
|
||||||
|
return qvvrData.getCause();
|
||||||
|
} else {
|
||||||
|
return 0; // 未知原因
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增强的分析函数 - 返回详细分析结果
|
||||||
|
* @param qvvrData 输入的电压暂降数据结构
|
||||||
|
* @return 包含详细信息的分析结果对象
|
||||||
|
*/
|
||||||
|
public AnalysisResult analyzeVoltageSagWithDetails(QvvrDataStruct qvvrData) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 构建输入信息描述
|
||||||
|
String inputInfo = String.format("采样率=%d Hz, 数据长度=%d点",
|
||||||
|
qvvrData.getSmpRate(), qvvrData.getSmpLen());
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("开始电压暂降分析,{}", inputInfo);
|
||||||
|
|
||||||
|
// 参数校验
|
||||||
|
if (!validateInput(qvvrData)) {
|
||||||
|
qvvrData.setCause(0);
|
||||||
|
qvvrData.setNoCal(1);
|
||||||
|
return new AnalysisResult(1, "输入参数校验失败", inputInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建数据处理对象
|
||||||
|
DataCause dataCause = createDataCause(qvvrData);
|
||||||
|
DataFeature resultFeature = new DataFeature();
|
||||||
|
|
||||||
|
// 特征值计算
|
||||||
|
int ret = featureCalculator.calculateFeatures(dataCause, resultFeature);
|
||||||
|
if (ret == 0) {
|
||||||
|
// 阈值判断确定暂降原因
|
||||||
|
int judgeStatus = ThresholdJudge.thresholdJudge(resultFeature);
|
||||||
|
|
||||||
|
// 设置输出结果
|
||||||
|
qvvrData.setCause(resultFeature.getCause());
|
||||||
|
qvvrData.setNoCal(0);
|
||||||
|
|
||||||
|
long processingTime = System.currentTimeMillis() - startTime;
|
||||||
|
String causeDescription = ThresholdJudge.getCauseDescription(resultFeature.getCause());
|
||||||
|
|
||||||
|
logger.info("分析完成,原因={} ({}), 处理时间={}ms, 判断状态={}",
|
||||||
|
resultFeature.getCause(), causeDescription, processingTime, judgeStatus);
|
||||||
|
|
||||||
|
// 输出详细特征信息
|
||||||
|
logFeatureDetails(resultFeature);
|
||||||
|
|
||||||
|
// 返回成功结果
|
||||||
|
return new AnalysisResult(resultFeature.getCause(), causeDescription,
|
||||||
|
judgeStatus, processingTime, resultFeature, inputInfo);
|
||||||
|
} else {
|
||||||
|
qvvrData.setCause(0);
|
||||||
|
qvvrData.setNoCal(1);
|
||||||
|
logger.error("特征计算失败");
|
||||||
|
return new AnalysisResult(1, "特征计算失败", inputInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("分析过程发生异常", e);
|
||||||
|
qvvrData.setCause(0);
|
||||||
|
qvvrData.setNoCal(1);
|
||||||
|
return new AnalysisResult(1, "分析过程发生异常: " + e.getMessage(), inputInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化的详细分析接口 - 直接返回分析结果对象
|
||||||
|
* @param va A相电压数据
|
||||||
|
* @param vb B相电压数据
|
||||||
|
* @param vc C相电压数据
|
||||||
|
* @param smpRate 采样率
|
||||||
|
* @return 包含详细信息的分析结果对象
|
||||||
|
*/
|
||||||
|
public AnalysisResult analyzeWithDetails(float[] va, float[] vb, float[] vc, int smpRate) {
|
||||||
|
QvvrDataStruct qvvrData = new QvvrDataStruct();
|
||||||
|
qvvrData.setSmpVa(va);
|
||||||
|
qvvrData.setSmpVb(vb);
|
||||||
|
qvvrData.setSmpVc(vc);
|
||||||
|
qvvrData.setSmpRate(smpRate);
|
||||||
|
qvvrData.setSmpLen(va.length);
|
||||||
|
|
||||||
|
return analyzeVoltageSagWithDetails(qvvrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分析器版本信息
|
||||||
|
* @return 版本字符串
|
||||||
|
*/
|
||||||
|
public String getVersion() {
|
||||||
|
return "Java Voltage Sag Analyzer v1.0.0 - Converted from C implementation";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package com.njcn.influx.event.cause.io;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMTRADE格式数据文件读取器
|
||||||
|
* 对应C语言中的comtrade_read函数
|
||||||
|
*/
|
||||||
|
public class ComtradeReader {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ComtradeReader.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMTRADE数据读取结果
|
||||||
|
*/
|
||||||
|
public static class ComtradeData {
|
||||||
|
private float[] ua;
|
||||||
|
private float[] ub;
|
||||||
|
private float[] uc;
|
||||||
|
private int sampleCount;
|
||||||
|
private int sampleRate;
|
||||||
|
|
||||||
|
public ComtradeData(float[] ua, float[] ub, float[] uc, int sampleCount, int sampleRate) {
|
||||||
|
this.ua = ua;
|
||||||
|
this.ub = ub;
|
||||||
|
this.uc = uc;
|
||||||
|
this.sampleCount = sampleCount;
|
||||||
|
this.sampleRate = sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getUa() { return ua; }
|
||||||
|
public float[] getUb() { return ub; }
|
||||||
|
public float[] getUc() { return uc; }
|
||||||
|
public int getSampleCount() { return sampleCount; }
|
||||||
|
public int getSampleRate() { return sampleRate; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取COMTRADE格式的DAT文件
|
||||||
|
* @param filename 文件名
|
||||||
|
* @param xsa A相比例因子
|
||||||
|
* @param xsb B相比例因子
|
||||||
|
* @param xsc C相比例因子
|
||||||
|
* @param inputSampleRate 输入采样率
|
||||||
|
* @param outputSampleRate 输出采样率
|
||||||
|
* @param maxSamples 最大采样点数
|
||||||
|
* @param recordSize 每个记录的字节数(20或14)
|
||||||
|
* @return COMTRADE数据对象
|
||||||
|
* @throws IOException 文件读取异常
|
||||||
|
*/
|
||||||
|
public static ComtradeData readComtradeFile(String filename,
|
||||||
|
float xsa, float xsb, float xsc,
|
||||||
|
int inputSampleRate, int outputSampleRate,
|
||||||
|
int maxSamples, int recordSize) throws IOException {
|
||||||
|
|
||||||
|
logger.info("开始读取COMTRADE文件: {}", filename);
|
||||||
|
logger.info("参数: xsa={}, xsb={}, xsc={}, 输入采样率={}, 输出采样率={}, 记录大小={}字节",
|
||||||
|
xsa, xsb, xsc, inputSampleRate, outputSampleRate, recordSize);
|
||||||
|
|
||||||
|
File file = new File(filename);
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new IOException("文件不存在: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
long fileSize = file.length();
|
||||||
|
int recordCount = (int)(fileSize / recordSize);
|
||||||
|
|
||||||
|
logger.info("文件大小: {} 字节, 记录数: {}", fileSize, recordCount);
|
||||||
|
|
||||||
|
// 读取文件数据
|
||||||
|
byte[] buffer = new byte[(int)fileSize];
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
int bytesRead = fis.read(buffer);
|
||||||
|
if (bytesRead != fileSize) {
|
||||||
|
throw new IOException("文件读取不完整");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析数据
|
||||||
|
float[] ua = new float[maxSamples];
|
||||||
|
float[] ub = new float[maxSamples];
|
||||||
|
float[] uc = new float[maxSamples];
|
||||||
|
|
||||||
|
int dataCount = 0;
|
||||||
|
int decimationMod = inputSampleRate / outputSampleRate;
|
||||||
|
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
|
||||||
|
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // 小端字节序
|
||||||
|
|
||||||
|
for (int i = 0; i < recordCount && dataCount < maxSamples; i++) {
|
||||||
|
// 根据采样率转换决定是否处理此记录
|
||||||
|
if (inputSampleRate == outputSampleRate || (i % decimationMod) == 0) {
|
||||||
|
|
||||||
|
int baseOffset = i * recordSize;
|
||||||
|
|
||||||
|
// 读取三相电压数据(从第8字节开始,每相2字节)
|
||||||
|
short dataA = byteBuffer.getShort(baseOffset + 8);
|
||||||
|
short dataB = byteBuffer.getShort(baseOffset + 10);
|
||||||
|
short dataC = byteBuffer.getShort(baseOffset + 12);
|
||||||
|
|
||||||
|
// 应用比例因子
|
||||||
|
ua[dataCount] = dataA * xsa;
|
||||||
|
ub[dataCount] = dataB * xsb;
|
||||||
|
uc[dataCount] = dataC * xsc;
|
||||||
|
|
||||||
|
dataCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("成功读取 {} 个数据点", dataCount);
|
||||||
|
|
||||||
|
return new ComtradeData(ua, ub, uc, dataCount, outputSampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用默认参数读取1.dat文件 - 对应C代码中的smp_data_init函数
|
||||||
|
* @param filename 文件名
|
||||||
|
* @return COMTRADE数据对象
|
||||||
|
* @throws IOException 文件读取异常
|
||||||
|
*/
|
||||||
|
public static ComtradeData readDefault(String filename) throws IOException {
|
||||||
|
// 使用C代码中的默认参数
|
||||||
|
float[] xs = {0.062256f, 0.062250f, 0.062262f};
|
||||||
|
int inputSampleRate = 256;
|
||||||
|
int outputSampleRate = 128;
|
||||||
|
int maxSamples = 10000; // MAX_SMP_DATA_LEN的合理值
|
||||||
|
int recordSize = 14; // C代码中dev=1,所以使用14字节/记录
|
||||||
|
|
||||||
|
return readComtradeFile(filename, xs[0], xs[1], xs[2],
|
||||||
|
inputSampleRate, outputSampleRate, maxSamples, recordSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查DAT文件是否存在
|
||||||
|
* @param filename 文件名
|
||||||
|
* @return 文件是否存在
|
||||||
|
*/
|
||||||
|
public static boolean isDatFileExists(String filename) {
|
||||||
|
return new File(filename).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取DAT文件信息
|
||||||
|
* @param filename 文件名
|
||||||
|
* @return 文件信息字符串
|
||||||
|
*/
|
||||||
|
public static String getDatFileInfo(String filename) {
|
||||||
|
File file = new File(filename);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return "文件不存在: " + filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
long fileSize = file.length();
|
||||||
|
int recordCount20 = (int)(fileSize / 20);
|
||||||
|
int recordCount14 = (int)(fileSize / 14);
|
||||||
|
|
||||||
|
return String.format("文件: %s, 大小: %d 字节, 可能记录数: %d (20字节/记录) 或 %d (14字节/记录)",
|
||||||
|
filename, fileSize, recordCount20, recordCount14);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package com.njcn.influx.event.cause.jna;
|
||||||
|
|
||||||
|
import com.sun.jna.Library;
|
||||||
|
import com.sun.jna.Native;
|
||||||
|
import com.sun.jna.Structure;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JNA接口调用qvvr_dll.dll
|
||||||
|
*/
|
||||||
|
public interface QvvrCauseDLL extends Library {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载DLL - 从resource目录或classpath
|
||||||
|
*/
|
||||||
|
QvvrCauseDLL INSTANCE = loadLibrary();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持jar打包的库加载方法 - 从jar内resources提取到临时目录后加载
|
||||||
|
*/
|
||||||
|
static QvvrCauseDLL loadLibrary() {
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
String libFileName;
|
||||||
|
String resourcePath;
|
||||||
|
|
||||||
|
// 根据操作系统确定库文件名
|
||||||
|
if (osName.contains("windows")) {
|
||||||
|
libFileName = "qvvr_cause_dll.dll";
|
||||||
|
resourcePath = "/qvvr_cause_dll.dll";
|
||||||
|
} else if (osName.contains("linux")) {
|
||||||
|
libFileName = "libqvvr_cause_dll.so";
|
||||||
|
resourcePath = "/libqvvr_cause_dll.so";
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("不支持的操作系统: " + osName);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从jar中提取库文件到临时目录
|
||||||
|
File tempLibFile = extractLibraryFromJar(resourcePath, libFileName);
|
||||||
|
|
||||||
|
// 加载提取的库文件
|
||||||
|
System.out.println("加载库文件: " + tempLibFile.getAbsolutePath());
|
||||||
|
return Native.load(tempLibFile.getAbsolutePath(), QvvrCauseDLL.class);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"无法加载QVVR库文件。\n" +
|
||||||
|
"请确保文件 " + libFileName + " 存在于 src/main/resources/ 目录下。\n" +
|
||||||
|
"当前操作系统: " + osName,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从jar中提取库文件到临时目录
|
||||||
|
*/
|
||||||
|
static File extractLibraryFromJar(String resourcePath, String libFileName) throws IOException {
|
||||||
|
// 获取资源输入流
|
||||||
|
InputStream libStream = QvvrCauseDLL.class.getResourceAsStream(resourcePath);
|
||||||
|
if (libStream == null) {
|
||||||
|
throw new FileNotFoundException("在jar中找不到库文件: " + resourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建临时文件
|
||||||
|
String tempDir = System.getProperty("java.io.tmpdir");
|
||||||
|
File tempLibFile = new File(tempDir, libFileName);
|
||||||
|
|
||||||
|
//File tempLibFile = new File(tempDir, "qvvr_" + System.currentTimeMillis() + "_" + libFileName);
|
||||||
|
|
||||||
|
// 提取库文件到临时目录
|
||||||
|
try (FileOutputStream out = new FileOutputStream(tempLibFile)) {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = libStream.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
libStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置为可执行
|
||||||
|
tempLibFile.setExecutable(true);
|
||||||
|
tempLibFile.setReadable(true);
|
||||||
|
|
||||||
|
// JVM退出时删除临时文件
|
||||||
|
tempLibFile.deleteOnExit();
|
||||||
|
|
||||||
|
System.out.println("已提取库文件到: " + tempLibFile.getAbsolutePath());
|
||||||
|
return tempLibFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接调用C DLL的qvvr_fun_cause函数
|
||||||
|
* void __stdcall qvvr_fun_cause(void *data)
|
||||||
|
*/
|
||||||
|
void qvvr_fun_cause(QvvrDataStruct data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应C语言的qvvr_data_struct结构体
|
||||||
|
* 完全按照qvvr_dll.h中的定义映射
|
||||||
|
*/
|
||||||
|
public static class QvvrDataStruct extends Structure {
|
||||||
|
|
||||||
|
// 输入参数定义
|
||||||
|
public float[] smp_va = new float[128 * 50 * 120]; // A相电压采样数据
|
||||||
|
public float[] smp_vb = new float[128 * 50 * 120]; // B相电压采样数据
|
||||||
|
public float[] smp_vc = new float[128 * 50 * 120]; // C相电压采样数据
|
||||||
|
public int smp_rate; // 采样率参数
|
||||||
|
public int smp_len; // 每个通道的采样数据个数
|
||||||
|
|
||||||
|
// 输入阈值参数
|
||||||
|
public float[] threshold = new float[50]; // 预设阈值时间参数
|
||||||
|
|
||||||
|
// 输出结果参数定义
|
||||||
|
public int cause; // 电压暂降判断出暂降原因 0-未知,1-短路,2-电压调节器,3-感动电机
|
||||||
|
public int no_cal; // 未计算判断标志,该位1表示输入数据有问题(数据缺失或异常等)未进行电压暂降判断暂降原因
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getFieldOrder() {
|
||||||
|
return Arrays.asList("smp_va", "smp_vb", "smp_vc", "smp_rate", "smp_len", "threshold", "cause", "no_cal");
|
||||||
|
}
|
||||||
|
|
||||||
|
public QvvrDataStruct() {
|
||||||
|
super();
|
||||||
|
// 初始化输出参数
|
||||||
|
this.cause = 0;
|
||||||
|
this.no_cal = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
package com.njcn.influx.event.cause.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电压暂降分析结果类
|
||||||
|
* 包含处理状态和详细分析信息
|
||||||
|
*/
|
||||||
|
public class AnalysisResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理结果状态码
|
||||||
|
*/
|
||||||
|
private int status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电压暂降原因代码
|
||||||
|
*/
|
||||||
|
private int cause;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原因描述字符串
|
||||||
|
*/
|
||||||
|
private String causeDescription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断状态码(算法内部路径)
|
||||||
|
*/
|
||||||
|
private int judgeStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理时间(毫秒)
|
||||||
|
*/
|
||||||
|
private long processingTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息(失败时)
|
||||||
|
*/
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详细特征信息
|
||||||
|
*/
|
||||||
|
private DataFeature features;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入数据基本信息
|
||||||
|
*/
|
||||||
|
private String inputInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数 - 成功结果
|
||||||
|
*/
|
||||||
|
public AnalysisResult(int cause, String causeDescription, int judgeStatus,
|
||||||
|
long processingTime, DataFeature features, String inputInfo) {
|
||||||
|
this.status = 0; // 成功
|
||||||
|
this.cause = cause;
|
||||||
|
this.causeDescription = causeDescription;
|
||||||
|
this.judgeStatus = judgeStatus;
|
||||||
|
this.processingTime = processingTime;
|
||||||
|
this.features = features;
|
||||||
|
this.inputInfo = inputInfo;
|
||||||
|
this.errorMessage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数 - 失败结果
|
||||||
|
*/
|
||||||
|
public AnalysisResult(int status, String errorMessage, String inputInfo) {
|
||||||
|
this.status = status;
|
||||||
|
this.cause = 0; // 未知原因
|
||||||
|
this.causeDescription = "未知原因";
|
||||||
|
this.judgeStatus = -1;
|
||||||
|
this.processingTime = 0;
|
||||||
|
this.features = null;
|
||||||
|
this.inputInfo = inputInfo;
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
public int getStatus() { return status; }
|
||||||
|
public int getCause() { return cause; }
|
||||||
|
public String getCauseDescription() { return causeDescription; }
|
||||||
|
public int getJudgeStatus() { return judgeStatus; }
|
||||||
|
public long getProcessingTime() { return processingTime; }
|
||||||
|
public String getErrorMessage() { return errorMessage; }
|
||||||
|
public DataFeature getFeatures() { return features; }
|
||||||
|
public String getInputInfo() { return inputInfo; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return status == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取判断路径描述
|
||||||
|
*/
|
||||||
|
public String getJudgeStatusDescription() {
|
||||||
|
switch (judgeStatus) {
|
||||||
|
case 1: return "暂降前不平衡异常";
|
||||||
|
case 2: return "短时浅暂降判断为电压跌落";
|
||||||
|
case 3: return "单相接地短路特征";
|
||||||
|
case 4: return "不对称故障特征";
|
||||||
|
case 5: return "感应电机启动特征(长时间+低SVD)";
|
||||||
|
case 6: return "三相对称短路故障";
|
||||||
|
case 7: return "电压调节器特征(谐波超标)";
|
||||||
|
case 8: return "其他情况归类为短路故障";
|
||||||
|
case 9: return "电压严重超限判断为短路故障";
|
||||||
|
case 10: return "短时严重暂降判断为短路故障";
|
||||||
|
default: return "未知判断路径";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整的分析摘要
|
||||||
|
*/
|
||||||
|
public String getSummary() {
|
||||||
|
if (!isSuccess()) {
|
||||||
|
return String.format("分析失败: %s\n输入信息: %s", errorMessage, inputInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("=== 电压暂降分析结果 ===\n");
|
||||||
|
sb.append(String.format("输入信息: %s\n", inputInfo));
|
||||||
|
sb.append(String.format("分析结果: %s (代码: %d)\n", causeDescription, cause));
|
||||||
|
sb.append(String.format("判断路径: %s (状态码: %d)\n", getJudgeStatusDescription(), judgeStatus));
|
||||||
|
sb.append(String.format("处理时间: %d ms\n", processingTime));
|
||||||
|
|
||||||
|
if (features != null) {
|
||||||
|
sb.append(String.format("事件时段: TS=%d, TE=%d, 持续时间=%d个采样点\n",
|
||||||
|
features.getTS(), features.getTE(), features.getHoldTime()));
|
||||||
|
sb.append(String.format("电压特征: 最小值=%.3f, 最大值=%.3f\n",
|
||||||
|
features.getU3Min(), features.getU3Max()));
|
||||||
|
sb.append(String.format("相序特征: 负序=%.3f, 零序=%.3f, 不平衡度=%.3f\n",
|
||||||
|
features.getU2Avg(), features.getU0Avg(), features.getBphAvg()));
|
||||||
|
sb.append(String.format("椭圆特征: bi1=%.3f, bi2=%.3f\n",
|
||||||
|
features.getBi1(), features.getBi2()));
|
||||||
|
sb.append(String.format("SVD特征: %.6f\n", features.getSvd()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getSummary();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.njcn.influx.event.cause.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复数结构
|
||||||
|
* 对应C语言中的complex结构体
|
||||||
|
*/
|
||||||
|
public class Complex {
|
||||||
|
private double real; // 实部
|
||||||
|
private double imag; // 虚部
|
||||||
|
|
||||||
|
public Complex() {
|
||||||
|
this(0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Complex(double real, double imag) {
|
||||||
|
this.real = real;
|
||||||
|
this.imag = imag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复数加法
|
||||||
|
public Complex plus(Complex other) {
|
||||||
|
return new Complex(this.real + other.real, this.imag + other.imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复数减法
|
||||||
|
public Complex minus(Complex other) {
|
||||||
|
return new Complex(this.real - other.real, this.imag - other.imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复数乘法
|
||||||
|
public Complex multiply(Complex other) {
|
||||||
|
double newReal = this.real * other.real - this.imag * other.imag;
|
||||||
|
double newImag = this.real * other.imag + this.imag * other.real;
|
||||||
|
return new Complex(newReal, newImag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复数除法
|
||||||
|
public Complex divide(Complex other) {
|
||||||
|
double denominator = other.real * other.real + other.imag * other.imag;
|
||||||
|
if (Math.abs(denominator) < 1e-10) {
|
||||||
|
throw new ArithmeticException("Division by zero complex number");
|
||||||
|
}
|
||||||
|
double newReal = (this.real * other.real + this.imag * other.imag) / denominator;
|
||||||
|
double newImag = (this.imag * other.real - this.real * other.imag) / denominator;
|
||||||
|
return new Complex(newReal, newImag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复数模
|
||||||
|
public double abs() {
|
||||||
|
return Math.sqrt(real * real + imag * imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复数共轭
|
||||||
|
public Complex conjugate() {
|
||||||
|
return new Complex(real, -imag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复数幅角
|
||||||
|
public double phase() {
|
||||||
|
return Math.atan2(imag, real);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public double getReal() {
|
||||||
|
return real;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReal(double real) {
|
||||||
|
this.real = real;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getImag() {
|
||||||
|
return imag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImag(double imag) {
|
||||||
|
this.imag = imag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (imag >= 0) {
|
||||||
|
return String.format("%.6f + %.6fi", real, imag);
|
||||||
|
} else {
|
||||||
|
return String.format("%.6f - %.6fi", real, -imag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package com.njcn.influx.event.cause.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据原因分析结构
|
||||||
|
* 对应C语言中的data_cause
|
||||||
|
*/
|
||||||
|
public class DataCause {
|
||||||
|
public static final int MAX_DATA_LEN = 128 * 50 * 60;
|
||||||
|
|
||||||
|
// 原始采样数据
|
||||||
|
private float[] va = new float[MAX_DATA_LEN];
|
||||||
|
private float[] vb = new float[MAX_DATA_LEN];
|
||||||
|
private float[] vc = new float[MAX_DATA_LEN];
|
||||||
|
private float[] t = new float[MAX_DATA_LEN];
|
||||||
|
|
||||||
|
// 计算出的有效值
|
||||||
|
private float[] rmsa = new float[MAX_DATA_LEN];
|
||||||
|
private float[] rmsb = new float[MAX_DATA_LEN];
|
||||||
|
private float[] rmsc = new float[MAX_DATA_LEN];
|
||||||
|
private float[] rmsMin = new float[MAX_DATA_LEN];
|
||||||
|
private float[] rmsMax = new float[MAX_DATA_LEN];
|
||||||
|
|
||||||
|
// dq变换幅值
|
||||||
|
private float[] ua = new float[MAX_DATA_LEN];
|
||||||
|
private float[] ub = new float[MAX_DATA_LEN];
|
||||||
|
private float[] uc = new float[MAX_DATA_LEN];
|
||||||
|
|
||||||
|
private float[] ua0 = new float[MAX_DATA_LEN];
|
||||||
|
private float[] ub0 = new float[MAX_DATA_LEN];
|
||||||
|
private float[] uc0 = new float[MAX_DATA_LEN];
|
||||||
|
|
||||||
|
// dq变换相位
|
||||||
|
private float[] anga = new float[MAX_DATA_LEN];
|
||||||
|
private float[] angb = new float[MAX_DATA_LEN];
|
||||||
|
private float[] angc = new float[MAX_DATA_LEN];
|
||||||
|
|
||||||
|
// dq变换相量
|
||||||
|
private VecStruct[] phasora = new VecStruct[MAX_DATA_LEN];
|
||||||
|
private VecStruct[] phasorb = new VecStruct[MAX_DATA_LEN];
|
||||||
|
private VecStruct[] phasorc = new VecStruct[MAX_DATA_LEN];
|
||||||
|
|
||||||
|
private int smp; // 采样率
|
||||||
|
private int num; // 数据点数
|
||||||
|
private float f; // 频率
|
||||||
|
private float un; // 额定电压
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public DataCause() {
|
||||||
|
// 初始化相量数组
|
||||||
|
for (int i = 0; i < MAX_DATA_LEN; i++) {
|
||||||
|
phasora[i] = new VecStruct();
|
||||||
|
phasorb[i] = new VecStruct();
|
||||||
|
phasorc[i] = new VecStruct();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public float[] getVa() { return va; }
|
||||||
|
public void setVa(float[] va) {
|
||||||
|
System.arraycopy(va, 0, this.va, 0, Math.min(va.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getVb() { return vb; }
|
||||||
|
public void setVb(float[] vb) {
|
||||||
|
System.arraycopy(vb, 0, this.vb, 0, Math.min(vb.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getVc() { return vc; }
|
||||||
|
public void setVc(float[] vc) {
|
||||||
|
System.arraycopy(vc, 0, this.vc, 0, Math.min(vc.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getT() { return t; }
|
||||||
|
public void setT(float[] t) {
|
||||||
|
System.arraycopy(t, 0, this.t, 0, Math.min(t.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getRmsa() { return rmsa; }
|
||||||
|
public void setRmsa(float[] rmsa) {
|
||||||
|
System.arraycopy(rmsa, 0, this.rmsa, 0, Math.min(rmsa.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getRmsb() { return rmsb; }
|
||||||
|
public void setRmsb(float[] rmsb) {
|
||||||
|
System.arraycopy(rmsb, 0, this.rmsb, 0, Math.min(rmsb.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getRmsc() { return rmsc; }
|
||||||
|
public void setRmsc(float[] rmsc) {
|
||||||
|
System.arraycopy(rmsc, 0, this.rmsc, 0, Math.min(rmsc.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getRmsMin() { return rmsMin; }
|
||||||
|
public void setRmsMin(float[] rmsMin) {
|
||||||
|
System.arraycopy(rmsMin, 0, this.rmsMin, 0, Math.min(rmsMin.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getRmsMax() { return rmsMax; }
|
||||||
|
public void setRmsMax(float[] rmsMax) {
|
||||||
|
System.arraycopy(rmsMax, 0, this.rmsMax, 0, Math.min(rmsMax.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getUa() { return ua; }
|
||||||
|
public void setUa(float[] ua) {
|
||||||
|
System.arraycopy(ua, 0, this.ua, 0, Math.min(ua.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getUb() { return ub; }
|
||||||
|
public void setUb(float[] ub) {
|
||||||
|
System.arraycopy(ub, 0, this.ub, 0, Math.min(ub.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getUc() { return uc; }
|
||||||
|
public void setUc(float[] uc) {
|
||||||
|
System.arraycopy(uc, 0, this.uc, 0, Math.min(uc.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getUa0() { return ua0; }
|
||||||
|
public void setUa0(float[] ua0) {
|
||||||
|
System.arraycopy(ua0, 0, this.ua0, 0, Math.min(ua0.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getUb0() { return ub0; }
|
||||||
|
public void setUb0(float[] ub0) {
|
||||||
|
System.arraycopy(ub0, 0, this.ub0, 0, Math.min(ub0.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getUc0() { return uc0; }
|
||||||
|
public void setUc0(float[] uc0) {
|
||||||
|
System.arraycopy(uc0, 0, this.uc0, 0, Math.min(uc0.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getAnga() { return anga; }
|
||||||
|
public void setAnga(float[] anga) {
|
||||||
|
System.arraycopy(anga, 0, this.anga, 0, Math.min(anga.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getAngb() { return angb; }
|
||||||
|
public void setAngb(float[] angb) {
|
||||||
|
System.arraycopy(angb, 0, this.angb, 0, Math.min(angb.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getAngc() { return angc; }
|
||||||
|
public void setAngc(float[] angc) {
|
||||||
|
System.arraycopy(angc, 0, this.angc, 0, Math.min(angc.length, MAX_DATA_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VecStruct[] getPhasora() { return phasora; }
|
||||||
|
public VecStruct[] getPhasorb() { return phasorb; }
|
||||||
|
public VecStruct[] getPhasorc() { return phasorc; }
|
||||||
|
|
||||||
|
public int getSmp() { return smp; }
|
||||||
|
public void setSmp(int smp) { this.smp = smp; }
|
||||||
|
|
||||||
|
public int getNum() { return num; }
|
||||||
|
public void setNum(int num) { this.num = num; }
|
||||||
|
|
||||||
|
public float getF() { return f; }
|
||||||
|
public void setF(float f) { this.f = f; }
|
||||||
|
|
||||||
|
public float getUn() { return un; }
|
||||||
|
public void setUn(float un) { this.un = un; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package com.njcn.influx.event.cause.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据特征结构
|
||||||
|
* 对应C语言中的data_feature
|
||||||
|
*/
|
||||||
|
public class DataFeature {
|
||||||
|
// 暂降原因定义
|
||||||
|
public static final int CAUSE_TYPE0 = 0; // 未知
|
||||||
|
public static final int CAUSE_TYPE1 = 1; // 短路故障
|
||||||
|
public static final int CAUSE_TYPE2 = 2; // 电压调节器
|
||||||
|
public static final int CAUSE_TYPE3 = 3; // 感动电机
|
||||||
|
public static final int CAUSE_TYPE4 = 4; // 电压跌落
|
||||||
|
|
||||||
|
|
||||||
|
// 暂降类型定义
|
||||||
|
public static final int TYPE0 = 0; // BC相间故障
|
||||||
|
public static final int TYPE1 = 1; // C相接地故障
|
||||||
|
public static final int TYPE2 = 2; // AC相间故障
|
||||||
|
public static final int TYPE3 = 3; // A相接地故障
|
||||||
|
public static final int TYPE4 = 4; // AB相间故障
|
||||||
|
public static final int TYPE5 = 5; // B相接地故障
|
||||||
|
public static final int TYPE6 = 6; // BC相间接地
|
||||||
|
public static final int TYPE7 = 7; // AC相间接地
|
||||||
|
public static final int TYPE8 = 8; // AB相间接地
|
||||||
|
public static final int TYPE9 = 9; // 三相故障
|
||||||
|
public static final int TYPE10 = 10; // 未知
|
||||||
|
|
||||||
|
|
||||||
|
private int TS; // 暂降开始时刻
|
||||||
|
private int TE; // 暂降结束时刻
|
||||||
|
private int smp; // 采样率
|
||||||
|
private float UN; // 系统额定电压
|
||||||
|
|
||||||
|
// 特征参数
|
||||||
|
private int preBphErr; // 稳态前电气平衡异常标志
|
||||||
|
private int preHarmErr; // 稳态前偶次谐波异常标志
|
||||||
|
private int uLow50; // 稳态事件期间三相中低于50%额值持续一个周波以上
|
||||||
|
private int uHigh120; // 稳态事件期间三相中高于120%额值持续一个周波以上
|
||||||
|
private int holdTime; // 持续时间(按照周波计算)
|
||||||
|
private float u3Max; // 暂降期电压有效值最大值
|
||||||
|
private float u3Min; // 最小值(标幺)
|
||||||
|
private float[] uMin = new float[3]; // 三相最小值
|
||||||
|
private float gao; // 高斯性
|
||||||
|
private float bi1; // 椭圆特征bi1
|
||||||
|
private float bi2; // 椭圆特征bi2
|
||||||
|
private float biaozhun; // 统计特征-标准差
|
||||||
|
private float pian; // 统计特征-偏度
|
||||||
|
private float qiao; // 统计特征-峭度
|
||||||
|
|
||||||
|
private float u2Max; // 负序电压最大值(相对un百分比)
|
||||||
|
private float u2Avg; // 负序电压平均值(相对un百分比)
|
||||||
|
private float u0Max; // 零序电压最大值(相对un百分比)
|
||||||
|
private float u0Avg; // 零序电压平均值(相对un百分比)
|
||||||
|
private float bphMax; // 不平衡度%
|
||||||
|
private float bphAvg; // 不平衡度%
|
||||||
|
|
||||||
|
private float[] harm2Max = new float[3]; // 2次谐波三相的最大值 相对un
|
||||||
|
private float[] harm4Max = new float[3]; // 4次谐波三相的最大值 相对un
|
||||||
|
private float[] harm2Avg = new float[3]; // 2次谐波三相平均值 相对un
|
||||||
|
private float[] harm4Avg = new float[3]; // 4次谐波三相平均值 相对un
|
||||||
|
|
||||||
|
private float svd; // 奇异值
|
||||||
|
|
||||||
|
// 结果
|
||||||
|
private int cause; // 电压暂降分类暂降原因
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
public DataFeature() {
|
||||||
|
this.cause = CAUSE_TYPE0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public int getTS() { return TS; }
|
||||||
|
public void setTS(int TS) { this.TS = TS; }
|
||||||
|
|
||||||
|
public int getTE() { return TE; }
|
||||||
|
public void setTE(int TE) { this.TE = TE; }
|
||||||
|
|
||||||
|
public int getSmp() { return smp; }
|
||||||
|
public void setSmp(int smp) { this.smp = smp; }
|
||||||
|
|
||||||
|
public float getUN() { return UN; }
|
||||||
|
public void setUN(float UN) { this.UN = UN; }
|
||||||
|
|
||||||
|
public int getPreBphErr() { return preBphErr; }
|
||||||
|
public void setPreBphErr(int preBphErr) { this.preBphErr = preBphErr; }
|
||||||
|
|
||||||
|
public int getPreHarmErr() { return preHarmErr; }
|
||||||
|
public void setPreHarmErr(int preHarmErr) { this.preHarmErr = preHarmErr; }
|
||||||
|
|
||||||
|
public int getuLow50() { return uLow50; }
|
||||||
|
public void setuLow50(int uLow50) { this.uLow50 = uLow50; }
|
||||||
|
|
||||||
|
public int getuHigh120() { return uHigh120; }
|
||||||
|
public void setuHigh120(int uHigh120) { this.uHigh120 = uHigh120; }
|
||||||
|
|
||||||
|
public int getHoldTime() { return holdTime; }
|
||||||
|
public void setHoldTime(int holdTime) { this.holdTime = holdTime; }
|
||||||
|
|
||||||
|
public float getU3Max() { return u3Max; }
|
||||||
|
public void setU3Max(float u3Max) { this.u3Max = u3Max; }
|
||||||
|
|
||||||
|
public float getU3Min() { return u3Min; }
|
||||||
|
public void setU3Min(float u3Min) { this.u3Min = u3Min; }
|
||||||
|
|
||||||
|
public float[] getUMin() { return uMin; }
|
||||||
|
public void setUMin(float[] uMin) { System.arraycopy(uMin, 0, this.uMin, 0, Math.min(uMin.length, 3)); }
|
||||||
|
|
||||||
|
public float getGao() { return gao; }
|
||||||
|
public void setGao(float gao) { this.gao = gao; }
|
||||||
|
|
||||||
|
public float getBi1() { return bi1; }
|
||||||
|
public void setBi1(float bi1) { this.bi1 = bi1; }
|
||||||
|
|
||||||
|
public float getBi2() { return bi2; }
|
||||||
|
public void setBi2(float bi2) { this.bi2 = bi2; }
|
||||||
|
|
||||||
|
public float getBiaozhun() { return biaozhun; }
|
||||||
|
public void setBiaozhun(float biaozhun) { this.biaozhun = biaozhun; }
|
||||||
|
|
||||||
|
public float getPian() { return pian; }
|
||||||
|
public void setPian(float pian) { this.pian = pian; }
|
||||||
|
|
||||||
|
public float getQiao() { return qiao; }
|
||||||
|
public void setQiao(float qiao) { this.qiao = qiao; }
|
||||||
|
|
||||||
|
public float getU2Max() { return u2Max; }
|
||||||
|
public void setU2Max(float u2Max) { this.u2Max = u2Max; }
|
||||||
|
|
||||||
|
public float getU2Avg() { return u2Avg; }
|
||||||
|
public void setU2Avg(float u2Avg) { this.u2Avg = u2Avg; }
|
||||||
|
|
||||||
|
public float getU0Max() { return u0Max; }
|
||||||
|
public void setU0Max(float u0Max) { this.u0Max = u0Max; }
|
||||||
|
|
||||||
|
public float getU0Avg() { return u0Avg; }
|
||||||
|
public void setU0Avg(float u0Avg) { this.u0Avg = u0Avg; }
|
||||||
|
|
||||||
|
public float getBphMax() { return bphMax; }
|
||||||
|
public void setBphMax(float bphMax) { this.bphMax = bphMax; }
|
||||||
|
|
||||||
|
public float getBphAvg() { return bphAvg; }
|
||||||
|
public void setBphAvg(float bphAvg) { this.bphAvg = bphAvg; }
|
||||||
|
|
||||||
|
public float[] getHarm2Max() { return harm2Max; }
|
||||||
|
public void setHarm2Max(float[] harm2Max) { System.arraycopy(harm2Max, 0, this.harm2Max, 0, Math.min(harm2Max.length, 3)); }
|
||||||
|
|
||||||
|
public float[] getHarm4Max() { return harm4Max; }
|
||||||
|
public void setHarm4Max(float[] harm4Max) { System.arraycopy(harm4Max, 0, this.harm4Max, 0, Math.min(harm4Max.length, 3)); }
|
||||||
|
|
||||||
|
public float[] getHarm2Avg() { return harm2Avg; }
|
||||||
|
public void setHarm2Avg(float[] harm2Avg) { System.arraycopy(harm2Avg, 0, this.harm2Avg, 0, Math.min(harm2Avg.length, 3)); }
|
||||||
|
|
||||||
|
public float[] getHarm4Avg() { return harm4Avg; }
|
||||||
|
public void setHarm4Avg(float[] harm4Avg) { System.arraycopy(harm4Avg, 0, this.harm4Avg, 0, Math.min(harm4Avg.length, 3)); }
|
||||||
|
|
||||||
|
public float getSvd() { return svd; }
|
||||||
|
public void setSvd(float svd) { this.svd = svd; }
|
||||||
|
|
||||||
|
public int getCause() { return cause; }
|
||||||
|
public void setCause(int cause) { this.cause = cause; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.njcn.influx.event.cause.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 下午 2:36【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class EventAnalysisDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lineId
|
||||||
|
*/
|
||||||
|
private String lineId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监测点IP
|
||||||
|
*/
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件ID
|
||||||
|
*/
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名称
|
||||||
|
*/
|
||||||
|
private String waveName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂降原因
|
||||||
|
* (0) //未知
|
||||||
|
* (1) //短路故障
|
||||||
|
* (2) //电压调节器
|
||||||
|
* (3) //感动电机
|
||||||
|
* (4) //电压跌落
|
||||||
|
*/
|
||||||
|
private Integer cause;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可能分析失败,虽然返回的原因为未知,可能程序执行异常导致的
|
||||||
|
* 0 异常计算
|
||||||
|
* 1 正常计算
|
||||||
|
*/
|
||||||
|
private Integer causeFlag = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂降类型
|
||||||
|
* TYPE0 = 0; // BC相间故障
|
||||||
|
* TYPE1 = 1; // C相接地故障
|
||||||
|
* TYPE2 = 2; // AC相间故障
|
||||||
|
* TYPE3 = 3; // A相接地故障
|
||||||
|
* TYPE4 = 4; // AB相间故障
|
||||||
|
* TYPE5 = 5; // B相接地故障
|
||||||
|
* TYPE6 = 6; // BC相间接地
|
||||||
|
* TYPE7 = 7; // AC相间接地
|
||||||
|
* TYPE8 = 8; // AB相间接地
|
||||||
|
* TYPE9 = 9; // 三相故障
|
||||||
|
* TYPE10 = 10; // 未知
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可能分析失败,虽然返回的原因为未知,可能程序执行异常导致的
|
||||||
|
* 0 异常计算
|
||||||
|
* 1 正常计算
|
||||||
|
*/
|
||||||
|
private Integer typeFlag = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件全路径
|
||||||
|
* 适配物联的暂态事件解析 物联那边事件的路径是全路径,不需要拼接,这个参数可为空
|
||||||
|
*/
|
||||||
|
private String wlFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.njcn.influx.event.cause.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电压暂降数据结构
|
||||||
|
* 对应C语言中的qvvr_data_struct
|
||||||
|
*/
|
||||||
|
public class QvvrDataStruct {
|
||||||
|
public static final int MAX_SMP_DATA_LEN = 128 * 50 * 120;
|
||||||
|
|
||||||
|
// 输入参数定义
|
||||||
|
private float[] smpVa = new float[MAX_SMP_DATA_LEN]; // A相电压采样数据
|
||||||
|
private float[] smpVb = new float[MAX_SMP_DATA_LEN]; // B相电压采样数据
|
||||||
|
private float[] smpVc = new float[MAX_SMP_DATA_LEN]; // C相电压采样数据
|
||||||
|
private int smpRate; // 采样率参数
|
||||||
|
private int smpLen; // 每个通道的采样数据个数
|
||||||
|
|
||||||
|
// 输入阈值参数
|
||||||
|
private float[] threshold = new float[50]; // 预设阈值时间参数
|
||||||
|
|
||||||
|
// 输出结果参数定义
|
||||||
|
private int cause; // 电压暂降判断出暂降原因 0-未知,1-短路,2-电压调节器,3-感动电机
|
||||||
|
private int noCal; // 未计算判断标志,该位1表示输入数据有问题
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
public QvvrDataStruct() {
|
||||||
|
this.cause = 0;
|
||||||
|
this.noCal = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public float[] getSmpVa() {
|
||||||
|
return smpVa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmpVa(float[] smpVa) {
|
||||||
|
if (smpVa.length <= MAX_SMP_DATA_LEN) {
|
||||||
|
System.arraycopy(smpVa, 0, this.smpVa, 0, smpVa.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getSmpVb() {
|
||||||
|
return smpVb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmpVb(float[] smpVb) {
|
||||||
|
if (smpVb.length <= MAX_SMP_DATA_LEN) {
|
||||||
|
System.arraycopy(smpVb, 0, this.smpVb, 0, smpVb.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getSmpVc() {
|
||||||
|
return smpVc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmpVc(float[] smpVc) {
|
||||||
|
if (smpVc.length <= MAX_SMP_DATA_LEN) {
|
||||||
|
System.arraycopy(smpVc, 0, this.smpVc, 0, smpVc.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSmpRate() {
|
||||||
|
return smpRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmpRate(int smpRate) {
|
||||||
|
this.smpRate = smpRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSmpLen() {
|
||||||
|
return smpLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmpLen(int smpLen) {
|
||||||
|
this.smpLen = smpLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getThreshold() {
|
||||||
|
return threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreshold(float[] threshold) {
|
||||||
|
if (threshold.length <= 50) {
|
||||||
|
System.arraycopy(threshold, 0, this.threshold, 0, threshold.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCause() {
|
||||||
|
return cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCause(int cause) {
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNoCal() {
|
||||||
|
return noCal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNoCal(int noCal) {
|
||||||
|
this.noCal = noCal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.njcn.influx.event.cause.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向量结构
|
||||||
|
* 对应C语言中的vec_struct
|
||||||
|
*/
|
||||||
|
public class VecStruct {
|
||||||
|
private float r; // 实部
|
||||||
|
private float x; // 虚部
|
||||||
|
|
||||||
|
public VecStruct() {
|
||||||
|
this(0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VecStruct(float r, float x) {
|
||||||
|
this.r = r;
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向量模长
|
||||||
|
public float magnitude() {
|
||||||
|
return (float) Math.sqrt(r * r + x * x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向量相角
|
||||||
|
public float phase() {
|
||||||
|
return (float) Math.atan2(x, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public float getR() {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setR(float r) {
|
||||||
|
this.r = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(float x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("VecStruct{r=%.6f, x=%.6f}", r, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package com.njcn.influx.event.type.jna;
|
||||||
|
|
||||||
|
import com.sun.jna.Library;
|
||||||
|
import com.sun.jna.Native;
|
||||||
|
import com.sun.jna.Structure;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JNA接口调用qvvr_dll.dll
|
||||||
|
*/
|
||||||
|
public interface QvvrDLL extends Library {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载DLL - 从resource目录或classpath
|
||||||
|
*/
|
||||||
|
QvvrDLL INSTANCE = loadLibrary();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持jar打包的库加载方法 - 从jar内resources提取到临时目录后加载
|
||||||
|
*/
|
||||||
|
static QvvrDLL loadLibrary() {
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
String libFileName;
|
||||||
|
String resourcePath;
|
||||||
|
|
||||||
|
// 根据操作系统确定库文件名
|
||||||
|
if (osName.contains("windows")) {
|
||||||
|
libFileName = "qvvr_dll.dll";
|
||||||
|
resourcePath = "/qvvr_dll.dll";
|
||||||
|
} else if (osName.contains("linux")) {
|
||||||
|
libFileName = "libqvvr_dll.so";
|
||||||
|
resourcePath = "/libqvvr_dll.so";
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("不支持的操作系统: " + osName);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从jar中提取库文件到临时目录
|
||||||
|
File tempLibFile = extractLibraryFromJar(resourcePath, libFileName);
|
||||||
|
// 加载提取的库文件
|
||||||
|
System.out.println("加载库文件: " + tempLibFile.getAbsolutePath());
|
||||||
|
return Native.load(tempLibFile.getAbsolutePath(), QvvrDLL.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"无法加载QVVR库文件。\n" +
|
||||||
|
"请确保文件 " + libFileName + " 存在于 src/main/resources/ 目录下。\n" +
|
||||||
|
"当前操作系统: " + osName,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从jar中提取库文件到临时目录
|
||||||
|
*/
|
||||||
|
static File extractLibraryFromJar(String resourcePath, String libFileName) throws IOException {
|
||||||
|
// 获取资源输入流
|
||||||
|
InputStream libStream = QvvrDLL.class.getResourceAsStream(resourcePath);
|
||||||
|
if (libStream == null) {
|
||||||
|
throw new FileNotFoundException("在jar中找不到库文件: " + resourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建临时文件
|
||||||
|
String tempDir = System.getProperty("java.io.tmpdir");
|
||||||
|
File tempLibFile = new File(tempDir, libFileName);
|
||||||
|
|
||||||
|
// File tempLibFile = new File(tempDir, "qvvr_" + System.currentTimeMillis() + "_" + libFileName);
|
||||||
|
|
||||||
|
// 提取库文件到临时目录
|
||||||
|
try (FileOutputStream out = new FileOutputStream(tempLibFile)) {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = libStream.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
libStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置为可执行
|
||||||
|
tempLibFile.setExecutable(true);
|
||||||
|
tempLibFile.setReadable(true);
|
||||||
|
|
||||||
|
// JVM退出时删除临时文件
|
||||||
|
tempLibFile.deleteOnExit();
|
||||||
|
|
||||||
|
System.out.println("已提取库文件到: " + tempLibFile.getAbsolutePath());
|
||||||
|
return tempLibFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接调用C DLL的qvvr_fun函数
|
||||||
|
* void __stdcall qvvr_fun(void *data)
|
||||||
|
*/
|
||||||
|
void qvvr_fun(QvvrDataStruct data);
|
||||||
|
|
||||||
|
void ping();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应C语言的qvvr_data_struct结构体
|
||||||
|
*/
|
||||||
|
public static class QvvrDataStruct extends Structure {
|
||||||
|
|
||||||
|
// 输入数据
|
||||||
|
public float[] smp_va = new float[128 * 50 * 120]; // A相电压
|
||||||
|
public float[] smp_vb = new float[128 * 50 * 120]; // B相电压
|
||||||
|
public float[] smp_vc = new float[128 * 50 * 120]; // C相电压
|
||||||
|
public int smp_rate; // 采样频率
|
||||||
|
public int smp_len; // 数据长度
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
public int evt_num; // 事件数量
|
||||||
|
public EventBuffer[] evt_buf = new EventBuffer[32]; // 事件缓冲区
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getFieldOrder() {
|
||||||
|
return Arrays.asList("smp_va", "smp_vb", "smp_vc", "smp_rate", "smp_len", "evt_num", "evt_buf");
|
||||||
|
}
|
||||||
|
|
||||||
|
public QvvrDataStruct() {
|
||||||
|
super();
|
||||||
|
// 初始化事件缓冲区
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
evt_buf[i] = new EventBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件缓冲区结构
|
||||||
|
*/
|
||||||
|
public static class EventBuffer extends Structure {
|
||||||
|
public int[] qvvr_cata_cause = new int[256];
|
||||||
|
public int[] qvvr_phasetype = new int[256];
|
||||||
|
public int[] qvvr_cata_type = new int[256];
|
||||||
|
|
||||||
|
public float hold_time_rms;
|
||||||
|
public float hold_time_dq;
|
||||||
|
|
||||||
|
public float POW_a;
|
||||||
|
public float POW_b;
|
||||||
|
public float POW_c;
|
||||||
|
|
||||||
|
public float Voltagechange_Va;
|
||||||
|
public float Voltagechange_Vb;
|
||||||
|
public float Voltagechange_Vc;
|
||||||
|
|
||||||
|
public int SEG_T_num;
|
||||||
|
public int[] SEG_T0_idx = new int[256];
|
||||||
|
public int[] SEG_T_idx = new int[256];
|
||||||
|
|
||||||
|
public int SEG_RMS_T_num;
|
||||||
|
public int[] SEG_RMS_T_idx = new int[256];
|
||||||
|
|
||||||
|
public int u_min_num;
|
||||||
|
public float[] ua_min = new float[256];
|
||||||
|
public float[] ub_min = new float[256];
|
||||||
|
public float[] uc_min = new float[256];
|
||||||
|
public float[] u3_min = new float[256];
|
||||||
|
public int[] order_min_idx = new int[256];
|
||||||
|
|
||||||
|
public float[] angle_diff_ap = new float[256];
|
||||||
|
public float[] angle_diff_bp = new float[256];
|
||||||
|
public float[] angle_diff_cp = new float[256];
|
||||||
|
public float[] angle_diff_an = new float[256];
|
||||||
|
public float[] angle_diff_bn = new float[256];
|
||||||
|
public float[] angle_diff_cn = new float[256];
|
||||||
|
|
||||||
|
public float[] bph_max_value = new float[256];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getFieldOrder() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"qvvr_cata_cause", "qvvr_phasetype", "qvvr_cata_type",
|
||||||
|
"hold_time_rms", "hold_time_dq",
|
||||||
|
"POW_a", "POW_b", "POW_c",
|
||||||
|
"Voltagechange_Va", "Voltagechange_Vb", "Voltagechange_Vc",
|
||||||
|
"SEG_T_num", "SEG_T0_idx", "SEG_T_idx",
|
||||||
|
"SEG_RMS_T_num", "SEG_RMS_T_idx",
|
||||||
|
"u_min_num", "ua_min", "ub_min", "uc_min", "u3_min", "order_min_idx",
|
||||||
|
"angle_diff_ap", "angle_diff_bp", "angle_diff_cp",
|
||||||
|
"angle_diff_an", "angle_diff_bn", "angle_diff_cn",
|
||||||
|
"bph_max_value"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.njcn.influx.imapper;
|
||||||
|
|
||||||
|
import com.njcn.influx.base.InfluxDbBaseMapper;
|
||||||
|
import com.njcn.influx.bo.po.InfluxDBComInfoRmation;
|
||||||
|
|
||||||
|
public interface InfluxDBComInfoRmationMapper extends InfluxDbBaseMapper<InfluxDBComInfoRmation> {
|
||||||
|
}
|
||||||
@@ -21,4 +21,8 @@ public interface OracleRmpEventDetailPOMapper extends BaseMapper<OracleRmpEventD
|
|||||||
|
|
||||||
List<DictData> selectDicByIds(@Param("ids") List<String> ids);
|
List<DictData> selectDicByIds(@Param("ids") List<String> ids);
|
||||||
|
|
||||||
|
void updateCause(@Param("code") Integer code, @Param("indexEvt") String indexEvt);
|
||||||
|
|
||||||
|
void updateType(@Param("code") Integer code, @Param("indexEvt") String indexEvt);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.baomidou.dynamic.datasource.annotation.DS;
|
|||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.njcn.influx.bo.po.PqDevice;
|
import com.njcn.influx.bo.po.PqDevice;
|
||||||
import com.njcn.influx.bo.po.PqDeviceBak;
|
import com.njcn.influx.bo.po.PqDeviceBak;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -16,4 +17,6 @@ import com.njcn.influx.bo.po.PqDeviceBak;
|
|||||||
@DS("master")
|
@DS("master")
|
||||||
public interface PqDeviceMapper extends BaseMapper<PqDevice> {
|
public interface PqDeviceMapper extends BaseMapper<PqDevice> {
|
||||||
|
|
||||||
|
PqDevice selectByLineId(@Param("lineId") String lineId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.njcn.influx.mapper;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.njcn.influx.bo.po.PqsEventLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 10:21【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public interface PqsEventLogMapper extends BaseMapper<PqsEventLog> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -37,4 +37,22 @@
|
|||||||
|
|
||||||
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<update id="updateCause">
|
||||||
|
update pqs_eventdetail set eventreason = (select
|
||||||
|
a.dic_index from pqs_dicdata a,pqs_dictype b where b.dictype_name =
|
||||||
|
'暂降原因' and a.dic_type = b.dictype_index and a.triphase = #{code} and
|
||||||
|
a.state = 1 and b.state = 1)
|
||||||
|
where
|
||||||
|
eventdetail_index = #{indexEvt}
|
||||||
|
</update>
|
||||||
|
<update id="updateType">
|
||||||
|
update pqs_eventdetail set EVENTTYPE = (select
|
||||||
|
a.dic_index from pqs_dicdata a,pqs_dictype b where b.dictype_name =
|
||||||
|
'暂降类型' and a.dic_type = b.dictype_index and a.triphase = #{code} and
|
||||||
|
a.state = 1 and b.state = 1)
|
||||||
|
where
|
||||||
|
eventdetail_index = #{indexEvt}
|
||||||
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?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.influx.mapper.PqDeviceMapper">
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
<!--@mbg.generated-->
|
||||||
|
DEV_INDEX, GD_INDEX, SUB_INDEX, "NAME", "STATUS", DEVTYPE, LOGONTIME, UPDATETIME,
|
||||||
|
NODE_INDEX, PORTID, DEVFLAG, DEV_SERIES, DEV_KEY, IP, DEVMODEL, CALLFLAG, DATATYPE
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectByLineId" resultType="com.njcn.influx.bo.po.PqDevice">
|
||||||
|
SELECT * FROM PQ_DEVICE INNER JOIN PQ_LINE on PQ_DEVICE.DEV_INDEX =PQ_LINE.DEV_INDEX where PQ_LINE.LINE_INDEX =#{lineId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?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.influx.mapper.PqsEventLogMapper">
|
||||||
|
<resultMap id="BaseResultMap" type="com.njcn.influx.bo.po.PqsEventLog">
|
||||||
|
<!--@mbg.generated-->
|
||||||
|
<!--@Table PQS_EVENT_LOG-->
|
||||||
|
<id column="ID" jdbcType="VARCHAR" property="id" />
|
||||||
|
<result column="EVENTDETAIL_INDEX" jdbcType="VARCHAR" property="eventdetailIndex" />
|
||||||
|
<result column="fileupFlag" jdbcType="DECIMAL" property="fileupflag" />
|
||||||
|
<result column="dealFlag" jdbcType="DECIMAL" property="dealflag" />
|
||||||
|
<result column="CreatTime" jdbcType="TIMESTAMP" property="creattime" />
|
||||||
|
<result column="UpdateTime" jdbcType="TIMESTAMP" property="updatetime" />
|
||||||
|
<result column="REMARK" jdbcType="VARCHAR" property="remark" />
|
||||||
|
</resultMap>
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
<!--@mbg.generated-->
|
||||||
|
ID, EVENTDETAIL_INDEX, fileupFlag, dealFlag, CreatTime, UpdateTime, REMARK
|
||||||
|
</sql>
|
||||||
|
</mapper>
|
||||||
@@ -14,4 +14,5 @@ import java.time.LocalDateTime;
|
|||||||
public interface OracleEventDetailToMysqlService extends IService<RmpEventDetailPO> {
|
public interface OracleEventDetailToMysqlService extends IService<RmpEventDetailPO> {
|
||||||
|
|
||||||
void eventBatch(LocalDateTime start, LocalDateTime end);
|
void eventBatch(LocalDateTime start, LocalDateTime end);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.njcn.influx.service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 9:53【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public interface OracleToMysqlService {
|
||||||
|
|
||||||
|
void OracleToMySqlJob(LocalDateTime result, LocalDateTime modifiedResult);
|
||||||
|
void retryAndCleanEvery10Min();
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.njcn.influx.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.njcn.influx.bo.po.PqsEventLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 服务类
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author wr
|
||||||
|
* @since 2024-03-05
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface PqsEventLogService extends IService<PqsEventLog> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.njcn.influx.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.njcn.influx.bo.po.RmpEventDetailPO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 11:28【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
public interface RmpEventDetailService extends IService<RmpEventDetailPO> {
|
||||||
|
void syncToMysql(String eventId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
package com.njcn.influx.service.impl;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.ChannelSftp;
|
||||||
|
|
||||||
|
import com.njcn.influx.bo.po.OracleRmpEventDetailPO;
|
||||||
|
import com.njcn.influx.bo.po.PqDevice;
|
||||||
|
import com.njcn.influx.bo.po.PqsEventLog;
|
||||||
|
import com.njcn.influx.component.WaveFileComponent;
|
||||||
|
import com.njcn.influx.component.dto.WaveDataDTO;
|
||||||
|
import com.njcn.influx.config.OtherConfig;
|
||||||
|
import com.njcn.influx.config.TargetConfig;
|
||||||
|
import com.njcn.influx.event.cause.jna.QvvrCauseDLL;
|
||||||
|
import com.njcn.influx.event.cause.model.DataFeature;
|
||||||
|
import com.njcn.influx.event.cause.model.EventAnalysisDTO;
|
||||||
|
import com.njcn.influx.event.type.jna.QvvrDLL;
|
||||||
|
import com.njcn.influx.mapper.OracleRmpEventDetailPOMapper;
|
||||||
|
import com.njcn.influx.mapper.PqDeviceMapper;
|
||||||
|
import com.njcn.influx.service.PqsEventLogService;
|
||||||
|
import com.njcn.influx.service.RmpEventDetailService;
|
||||||
|
import com.njcn.influx.sftp.SftpClient;
|
||||||
|
import com.sun.jna.Memory;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class EventAsyncService {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private final RmpEventDetailService rmpEventDetailService;
|
||||||
|
|
||||||
|
private final PqDeviceMapper pqDeviceMapper;
|
||||||
|
private final WaveFileComponent waveFileComponent;
|
||||||
|
|
||||||
|
private final OtherConfig config;
|
||||||
|
|
||||||
|
private final TargetConfig targetConfig;
|
||||||
|
|
||||||
|
private final OracleRmpEventDetailPOMapper oracleRmpEventDetailPOMapper;
|
||||||
|
private final PqsEventLogService pqsEventLogService;
|
||||||
|
|
||||||
|
@Value("${business.dealFlag:true}")
|
||||||
|
private Boolean dealFlag;
|
||||||
|
@Value("${business.upLoadFlag:true}")
|
||||||
|
private Boolean upLoadFlag;
|
||||||
|
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// 文件同步 + 高级算法
|
||||||
|
// ============================
|
||||||
|
// @Async("eventTaskExecutor")
|
||||||
|
public void asyncHandleEvent(OracleRmpEventDetailPO detail ) {
|
||||||
|
try {
|
||||||
|
log.info("异步处理事件:{} 文件:{}", detail.getEventId(), detail.getWavePath());
|
||||||
|
// ========== 1. 查看波形文件是否存在 ==========
|
||||||
|
PqDevice pqDevice = pqDeviceMapper.selectByLineId(detail.getMeasurementPointId());
|
||||||
|
String wavePath = config.getWavePath();
|
||||||
|
String cfgPath = wavePath + File.separator + pqDevice.getIp() + File.separator + detail.getWavePath() + ".CFG" ;
|
||||||
|
String datPath = wavePath + File.separator + pqDevice.getIp() + File.separator + detail.getWavePath() + ".DAT" ;
|
||||||
|
|
||||||
|
|
||||||
|
log.info("本地磁盘波形文件路径----" + cfgPath);
|
||||||
|
InputStream cfgStream = getFileInputStreamByFilePath(cfgPath);
|
||||||
|
InputStream datStream = getFileInputStreamByFilePath(datPath);
|
||||||
|
|
||||||
|
if((Objects.nonNull(cfgStream)&&Objects.nonNull(datStream))){
|
||||||
|
// ========== 2. 高级算法更新原因/类型 ==========
|
||||||
|
if(dealFlag){
|
||||||
|
EventAnalysisDTO eventAnalysisDTO = null;
|
||||||
|
try {
|
||||||
|
eventAnalysisDTO = advancedAlgorithm(cfgStream, datStream);
|
||||||
|
log.info("暂降原因:"+eventAnalysisDTO.getCause()+"----暂降类型:"+eventAnalysisDTO.getType());
|
||||||
|
oracleRmpEventDetailPOMapper.updateCause(eventAnalysisDTO.getCause(),detail.getEventId());
|
||||||
|
oracleRmpEventDetailPOMapper.updateType(eventAnalysisDTO.getType(),detail.getEventId());
|
||||||
|
pqsEventLogService.lambdaUpdate().set(PqsEventLog::getDealflag,1).set(PqsEventLog::getUpdatetime,new Date()).eq(PqsEventLog::getEventdetailIndex,detail.getEventId()).update();
|
||||||
|
} catch (Exception e) {
|
||||||
|
pqsEventLogService.lambdaUpdate().set(PqsEventLog::getRemark,e.getMessage().length()>50?e.getMessage().substring(50):e.getMessage()).set(PqsEventLog::getUpdatetime,new Date()).eq(PqsEventLog::getEventdetailIndex,detail.getEventId()).update();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if(upLoadFlag){
|
||||||
|
// ========== 4. SFTP文件同步 ==========
|
||||||
|
try {
|
||||||
|
syncFileToSftp(pqDevice.getIp(), detail.getWavePath(), cfgPath, datPath);
|
||||||
|
pqsEventLogService.lambdaUpdate().set(PqsEventLog::getFileupflag,1).set(PqsEventLog::getUpdatetime,new Date()).eq(PqsEventLog::getEventdetailIndex,detail.getEventId()).update();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
pqsEventLogService.lambdaUpdate().set(PqsEventLog::getRemark,e.getMessage().length()>50?e.getMessage().substring(50):e.getMessage()).set(PqsEventLog::getUpdatetime,new Date()).eq(PqsEventLog::getEventdetailIndex,detail.getEventId()).update();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
rmpEventDetailService.syncToMysql(detail.getEventId());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("异步处理失败 eventId:{}", detail.getEventId(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// 已修复:SFTP上传(无reset,无mark异常)
|
||||||
|
// ==================
|
||||||
|
private void syncFileToSftp(String deviceIp, String wavePath, String cfgFilePath, String datFilePath) throws Exception {
|
||||||
|
try (SftpClient client = new SftpClient() ) {
|
||||||
|
client.connect(targetConfig);
|
||||||
|
ChannelSftp channel = client.getChannel();
|
||||||
|
|
||||||
|
String remoteDir = targetConfig.getBasePath() + "/" + deviceIp;
|
||||||
|
client.createRemoteDirectory(remoteDir);
|
||||||
|
|
||||||
|
String remoteCfg = remoteDir + "/" + wavePath + ".CFG";
|
||||||
|
String remoteDat = remoteDir + "/" + wavePath + ".DAT";
|
||||||
|
|
||||||
|
channel.put(new FileInputStream(cfgFilePath), remoteCfg);
|
||||||
|
channel.put(new FileInputStream(datFilePath), remoteDat);
|
||||||
|
log.info("SFTP上传成功:{}", remoteCfg);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("SFTP上传失败", e);
|
||||||
|
throw new Exception("SFTP上传失败:"+(e.getMessage().length()>50?e.getMessage().substring(50):e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// 高级算法:更新 EVENTREASON / EVENTTYPE
|
||||||
|
// ==================
|
||||||
|
private EventAnalysisDTO advancedAlgorithm(InputStream cfgStream , InputStream datStream) throws Exception {
|
||||||
|
EventAnalysisDTO eventAnalysis = new EventAnalysisDTO();
|
||||||
|
log.info("执行高级算法:{}");
|
||||||
|
WaveDataDTO waveDataDTO = waveFileComponent.getComtradeNoAddPoints(cfgStream, datStream, 0);
|
||||||
|
|
||||||
|
List<List<Float>> listWaveData = waveDataDTO.getListWaveData();
|
||||||
|
// 暂降类型
|
||||||
|
// 创建数据结构
|
||||||
|
QvvrDLL.QvvrDataStruct typeDataStruct = new QvvrDLL.QvvrDataStruct();
|
||||||
|
System.out.println("初始化qvvrdll成功-----------");
|
||||||
|
typeDataStruct.smp_rate = waveDataDTO.getComtradeCfgDTO().getFinalSampleRate();
|
||||||
|
System.out.println("采样率-----------" + typeDataStruct.smp_rate);
|
||||||
|
typeDataStruct.smp_len = listWaveData.size();
|
||||||
|
System.out.println("波形长度-----------" + listWaveData.size());
|
||||||
|
|
||||||
|
// 获取ABC三相的瞬时数据
|
||||||
|
for (int i = 0; i < listWaveData.size(); i++) {
|
||||||
|
typeDataStruct.smp_va[i] = listWaveData.get(i).get(1);
|
||||||
|
typeDataStruct.smp_vb[i] = listWaveData.get(i).get(2);
|
||||||
|
typeDataStruct.smp_vc[i] = listWaveData.get(i).get(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 执行算法分析 - 直接调用C DLL
|
||||||
|
try {
|
||||||
|
QvvrDLL.INSTANCE.qvvr_fun(typeDataStruct);
|
||||||
|
|
||||||
|
log.info("调用qvvrdll成功-----------");
|
||||||
|
|
||||||
|
if (typeDataStruct.evt_num > 0) {
|
||||||
|
// 全局比较找出最小三相电压特征值
|
||||||
|
float globalMinVoltage = Float.MAX_VALUE;
|
||||||
|
int globalFaultType = 10;
|
||||||
|
for (int i = 0; i < typeDataStruct.evt_num; i++) {
|
||||||
|
QvvrDLL.EventBuffer evt = typeDataStruct.evt_buf[i];
|
||||||
|
for (int j = 0; j < evt.u_min_num; j++) {
|
||||||
|
float u3min = evt.u3_min[j];
|
||||||
|
if (u3min < globalMinVoltage) {
|
||||||
|
globalMinVoltage = u3min;
|
||||||
|
globalFaultType = evt.qvvr_cata_type[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventAnalysis.setType(globalFaultType);
|
||||||
|
} else {
|
||||||
|
eventAnalysis.setType(DataFeature.TYPE10);
|
||||||
|
}
|
||||||
|
System.out.println("结束qvvrdll方法调用-----------");
|
||||||
|
} catch (Exception e) {
|
||||||
|
eventAnalysis.setType(DataFeature.TYPE10);
|
||||||
|
eventAnalysis.setTypeFlag(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 暂降原因JNA的方式
|
||||||
|
QvvrCauseDLL.QvvrDataStruct causeDataStruct = new QvvrCauseDLL.QvvrDataStruct();
|
||||||
|
causeDataStruct.smp_rate = waveDataDTO.getComtradeCfgDTO().getFinalSampleRate();
|
||||||
|
causeDataStruct.smp_len = listWaveData.size();
|
||||||
|
// 获取ABC三相的瞬时数据
|
||||||
|
for (int i = 0; i < listWaveData.size(); i++) {
|
||||||
|
causeDataStruct.smp_va[i] = listWaveData.get(i).get(1);
|
||||||
|
causeDataStruct.smp_vb[i] = listWaveData.get(i).get(2);
|
||||||
|
causeDataStruct.smp_vc[i] = listWaveData.get(i).get(3);
|
||||||
|
}
|
||||||
|
// 执行算法分析 - 直接调用C DLL
|
||||||
|
try {
|
||||||
|
QvvrCauseDLL.INSTANCE.qvvr_fun_cause(causeDataStruct);
|
||||||
|
eventAnalysis.setCause(causeDataStruct.cause);
|
||||||
|
if (causeDataStruct.no_cal != 0) {
|
||||||
|
eventAnalysis.setCauseFlag(0);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
eventAnalysis.setCause(DataFeature.CAUSE_TYPE0);
|
||||||
|
eventAnalysis.setCauseFlag(0);
|
||||||
|
}
|
||||||
|
System.out.println("暂降原因分析完毕===============");
|
||||||
|
System.out.println("cause:" + eventAnalysis);
|
||||||
|
return eventAnalysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getFileInputStreamByFilePath(String filePath) {
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (file.isFile() && file.exists()) {
|
||||||
|
InputStream inputStream;
|
||||||
|
try {
|
||||||
|
inputStream = Files.newInputStream(file.toPath());
|
||||||
|
if (inputStream.available() < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return inputStream;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,10 +16,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +75,10 @@ public class OracleEventDetailToMysqlServiceImpl extends ServiceImpl<RmpEventDet
|
|||||||
po.setNum(oracleDetail.getNum());
|
po.setNum(oracleDetail.getNum());
|
||||||
po.setFileFlag(oracleDetail.getFileFlag());
|
po.setFileFlag(oracleDetail.getFileFlag());
|
||||||
po.setDealFlag(oracleDetail.getDealFlag());
|
po.setDealFlag(oracleDetail.getDealFlag());
|
||||||
po.setFirstTime(oracleDetail.getFirstTime());
|
|
||||||
|
if(Objects.nonNull(oracleDetail.getFirstTime())) {
|
||||||
|
po.setFirstTime(oracleDetail.getFirstTime().plus(oracleDetail.getFirstMs().intValue(), ChronoUnit.MILLIS));
|
||||||
|
}
|
||||||
po.setFirstType(oracleDetail.getFirstType());
|
po.setFirstType(oracleDetail.getFirstType());
|
||||||
po.setFirstMs(oracleDetail.getFirstMs());
|
po.setFirstMs(oracleDetail.getFirstMs());
|
||||||
po.setEnergy(oracleDetail.getEnergy());
|
po.setEnergy(oracleDetail.getEnergy());
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.njcn.influx.bo.po.*;
|
import com.njcn.influx.bo.po.*;
|
||||||
import com.njcn.influx.config.IdMappingCache;
|
import com.njcn.influx.config.RedisUtil;
|
||||||
import com.njcn.influx.mapper.OracleRmpEventDetailPOMapper;
|
import com.njcn.influx.mapper.OracleRmpEventDetailPOMapper;
|
||||||
import com.njcn.influx.mapper.PqDeviceMapper;
|
import com.njcn.influx.mapper.PqDeviceMapper;
|
||||||
import com.njcn.influx.mapper.RmpEventDetailPOMapper;
|
import com.njcn.influx.mapper.RmpEventDetailPOMapper;
|
||||||
import com.njcn.influx.service.*;
|
import com.njcn.influx.service.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -29,6 +30,9 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatusToMysqlService {
|
public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatusToMysqlService {
|
||||||
|
|
||||||
|
@Value("${business.type}")
|
||||||
|
private Integer systemType;
|
||||||
|
|
||||||
private final RmpEventDetailPOMapper rmpEventDetailPOMapper;
|
private final RmpEventDetailPOMapper rmpEventDetailPOMapper;
|
||||||
private final IPqDeviceBakService pqDeviceBakService;
|
private final IPqDeviceBakService pqDeviceBakService;
|
||||||
private final PqDeviceMapper pqDeviceMapper;
|
private final PqDeviceMapper pqDeviceMapper;
|
||||||
@@ -39,6 +43,7 @@ public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatu
|
|||||||
private final OraclePqLineDetailService oraclePqLineDetailService;
|
private final OraclePqLineDetailService oraclePqLineDetailService;
|
||||||
private final IPqLineDetailService pqLineDetailService;
|
private final IPqLineDetailService pqLineDetailService;
|
||||||
private final OracleRmpEventDetailPOMapper oracleRmpEventDetailPOMapper;
|
private final OracleRmpEventDetailPOMapper oracleRmpEventDetailPOMapper;
|
||||||
|
private final RedisUtil redisUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1.查询oracle装置表信息
|
* 1.查询oracle装置表信息
|
||||||
@@ -47,6 +52,7 @@ public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatu
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void monitorStatusSync() {
|
public void monitorStatusSync() {
|
||||||
|
Map<String, String> DevIdMapping = (Map<String, String>) redisUtil.getObjectByKey("DevIdMapping");
|
||||||
List<DictData> monitorDicList = rmpEventDetailPOMapper.selectByDicCodeList("Line_State");
|
List<DictData> monitorDicList = rmpEventDetailPOMapper.selectByDicCodeList("Line_State");
|
||||||
List<DictData> devDicList = rmpEventDetailPOMapper.selectByDicCodeList("Dev_Status");
|
List<DictData> devDicList = rmpEventDetailPOMapper.selectByDicCodeList("Dev_Status");
|
||||||
List<PqDeviceBak> list = pqDeviceBakService.list();
|
List<PqDeviceBak> list = pqDeviceBakService.list();
|
||||||
@@ -54,13 +60,14 @@ public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatu
|
|||||||
Map<Long, String> oracleDevMysql = list.stream().collect(Collectors.toMap(PqDeviceBak::getDevId, PqDeviceBak::getId));
|
Map<Long, String> oracleDevMysql = list.stream().collect(Collectors.toMap(PqDeviceBak::getDevId, PqDeviceBak::getId));
|
||||||
List<PqDevice> pqDevices = pqDeviceMapper.selectList(null);
|
List<PqDevice> pqDevices = pqDeviceMapper.selectList(null);
|
||||||
//pq的设备表更改通信状态
|
//pq的设备表更改通信状态
|
||||||
|
if(systemType == 0){
|
||||||
List<PqDeviceMysql> tempList = new ArrayList<>();
|
List<PqDeviceMysql> tempList = new ArrayList<>();
|
||||||
pqDevices.stream().forEach(temp->{
|
pqDevices.stream().forEach(temp->{
|
||||||
String id ="";
|
String id ="";
|
||||||
if (!IdMappingCache.DevIdMapping.containsKey(temp.getDevIndex()+"")){
|
if (!DevIdMapping.containsKey(temp.getDevIndex()+"")){
|
||||||
return;
|
return;
|
||||||
}else {
|
}else {
|
||||||
id=IdMappingCache.DevIdMapping.get(temp.getDevIndex()+"");
|
id=DevIdMapping.get(temp.getDevIndex()+"");
|
||||||
}
|
}
|
||||||
pqDeviceMysqlService.update(new LambdaUpdateWrapper<PqDeviceMysql>()
|
pqDeviceMysqlService.update(new LambdaUpdateWrapper<PqDeviceMysql>()
|
||||||
.set(PqDeviceMysql::getRunFlag,temp.getDevFlag())
|
.set(PqDeviceMysql::getRunFlag,temp.getDevFlag())
|
||||||
@@ -69,7 +76,7 @@ public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatu
|
|||||||
.eq(PqDeviceMysql::getId,id));
|
.eq(PqDeviceMysql::getId,id));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}else {
|
||||||
Map<Integer, List<Long>> oracleDevMap = pqDevices.stream().collect(Collectors.groupingBy(PqDevice::getDevFlag
|
Map<Integer, List<Long>> oracleDevMap = pqDevices.stream().collect(Collectors.groupingBy(PqDevice::getDevFlag
|
||||||
, Collectors.mapping(PqDevice::getDevIndex, Collectors.toList())));
|
, Collectors.mapping(PqDevice::getDevIndex, Collectors.toList())));
|
||||||
oracleDevMap.forEach((key, value) -> {
|
oracleDevMap.forEach((key, value) -> {
|
||||||
@@ -96,6 +103,10 @@ public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatu
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +167,12 @@ public class OracleMonitorStatusToMysqlServiceImpl implements OracleMonitorStatu
|
|||||||
.set(PqLineDetail::getDealCapacity, oraclePqLineDetail.getXycMp())
|
.set(PqLineDetail::getDealCapacity, oraclePqLineDetail.getXycMp())
|
||||||
.set(PqLineDetail::getPtType, oraclePqLineDetail.getPttype())
|
.set(PqLineDetail::getPtType, oraclePqLineDetail.getPttype())
|
||||||
.set(PqLineDetail::getTimeInterval, oraclePqLineDetail.getTinterval())
|
.set(PqLineDetail::getTimeInterval, oraclePqLineDetail.getTinterval())
|
||||||
|
|
||||||
|
.set(PqLineDetail::getPowerFlag, oraclePqLineDetail.getPowerid())
|
||||||
|
.set(PqLineDetail::getObjName, oraclePqLineDetail.getObjname())
|
||||||
|
.set(PqLineDetail::getMonitorId, oraclePqLineDetail.getMonitorId())
|
||||||
|
.set(PqLineDetail::getStatFlag, oraclePqLineDetail.getStatflag())
|
||||||
|
.set(PqLineDetail::getPowerSubstationName, oraclePqLineDetail.getPowerSubstationName())
|
||||||
.eq(PqLineDetail::getId, line.getId());
|
.eq(PqLineDetail::getId, line.getId());
|
||||||
if(loadTypeMap.containsKey(oraclePqLineDetail.getLoadtype())){
|
if(loadTypeMap.containsKey(oraclePqLineDetail.getLoadtype())){
|
||||||
DictData loadType= rmpEventDetailPOMapper.getDicDataByNameAndTypeName("干扰源类型", loadTypeMap.get(oraclePqLineDetail.getLoadtype()));
|
DictData loadType= rmpEventDetailPOMapper.getDicDataByNameAndTypeName("干扰源类型", loadTypeMap.get(oraclePqLineDetail.getLoadtype()));
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
package com.njcn.influx.service.impl;
|
package com.njcn.influx.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.date.DatePattern;
|
import cn.hutool.core.date.DatePattern;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
import cn.hutool.core.text.StrPool;
|
import cn.hutool.core.text.StrPool;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import com.njcn.influx.bo.param.TableEnum;
|
import com.njcn.influx.bo.param.TableEnum;
|
||||||
import com.njcn.influx.bo.po.JobDetailHoursInfluxDB;
|
import com.njcn.influx.bo.po.JobDetailHoursInfluxDB;
|
||||||
import com.njcn.influx.bo.po.JobDetailInfluxDB;
|
import com.njcn.influx.bo.po.JobDetailInfluxDB;
|
||||||
import com.njcn.influx.bo.po.JobHistoryLogInfluxdb;
|
import com.njcn.influx.bo.po.JobHistoryLogInfluxdb;
|
||||||
import com.njcn.influx.config.IdMappingCache;
|
import com.njcn.influx.config.IdMappingCache;
|
||||||
|
import com.njcn.influx.config.RedisUtil;
|
||||||
import com.njcn.influx.service.JobDetailHoursInfluxDBService;
|
import com.njcn.influx.service.JobDetailHoursInfluxDBService;
|
||||||
import com.njcn.influx.service.JobDetailInfluxDBService;
|
import com.njcn.influx.service.JobDetailInfluxDBService;
|
||||||
import com.njcn.influx.service.JobHistoryLogInfluxdbService;
|
import com.njcn.influx.service.JobHistoryLogInfluxdbService;
|
||||||
@@ -65,13 +68,14 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
private final JobDetailHoursInfluxDBService jobDetailHoursInfluxDBService;
|
private final JobDetailHoursInfluxDBService jobDetailHoursInfluxDBService;
|
||||||
|
|
||||||
private final LineTimeMapper lineTimeMapper;
|
private final LineTimeMapper lineTimeMapper;
|
||||||
|
private final RedisUtil redisUtil;
|
||||||
@Value("${business.slice:2}")
|
@Value("${business.slice:2}")
|
||||||
private int slice;
|
private int slice;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Async
|
@Async
|
||||||
public void dataBacthSysc(DataAsynParam dataAsynParam) {
|
public void dataBacthSysc(DataAsynParam dataAsynParam) {
|
||||||
|
Map<String, String> LineIdMapping = (Map<String, String>) redisUtil.getObjectByKey("LineIdMapping");
|
||||||
Runtime runtime = Runtime.getRuntime();
|
Runtime runtime = Runtime.getRuntime();
|
||||||
System.out.println("开始执行前总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
System.out.println("开始执行前总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
||||||
System.out.println("开始执行前已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
System.out.println("开始执行前已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
||||||
@@ -91,7 +95,7 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
|
|
||||||
|
|
||||||
//添加记录批处理日志,执行为批处理且最后一张表执行完记录
|
//添加记录批处理日志,执行为批处理且最后一张表执行完记录
|
||||||
if(dataAsynParam.getExcuteType()==2&&Objects.equals(tableName, TableEnum.DATAV.getCode())){
|
if(dataAsynParam.getExcuteType()==2&&Objects.equals(tableName, TableEnum.DATAHARMPOWERS.getCode())){
|
||||||
JobHistoryLogInfluxdb jobHistoryLogInfluxdbLog = new JobHistoryLogInfluxdb(date,LocalDateTime.now());
|
JobHistoryLogInfluxdb jobHistoryLogInfluxdbLog = new JobHistoryLogInfluxdb(date,LocalDateTime.now());
|
||||||
jobHistoryLogInfluxdbService.save(jobHistoryLogInfluxdbLog);
|
jobHistoryLogInfluxdbService.save(jobHistoryLogInfluxdbLog);
|
||||||
}
|
}
|
||||||
@@ -129,11 +133,11 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
Field id = obj.getClass().getDeclaredField("lineid");
|
Field id = obj.getClass().getDeclaredField("lineid");
|
||||||
id.setAccessible(true); //暴力访问id
|
id.setAccessible(true); //暴力访问id
|
||||||
String id1 = id.get(obj).toString();
|
String id1 = id.get(obj).toString();
|
||||||
if (!IdMappingCache.LineIdMapping.containsKey(id1)){
|
if (!LineIdMapping.containsKey(id1)){
|
||||||
log.info(tableName+"表---Oralcet数据同步到InfluxDB未找mysql中到lineid匹配的lineid"+id1);
|
log.info(tableName+"表---Oralcet数据同步到InfluxDB未找mysql中到lineid匹配的lineid"+id1);
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}else {
|
}else {
|
||||||
id.set(obj, IdMappingCache.LineIdMapping.get(id1));
|
id.set(obj, LineIdMapping.get(id1));
|
||||||
}
|
}
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -215,6 +219,10 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
//冀北现场存在数据更新时间不在1小时内,会丢失数据的情况,这边根据装置最新时间往前推1个小时查询数据
|
//冀北现场存在数据更新时间不在1小时内,会丢失数据的情况,这边根据装置最新时间往前推1个小时查询数据
|
||||||
@Override
|
@Override
|
||||||
public void hourseDataBacthSysc(DataAsynParam dataAsynParam) {
|
public void hourseDataBacthSysc(DataAsynParam dataAsynParam) {
|
||||||
|
Map<String, String> LineIdMapping = (Map<String, String>) redisUtil.getObjectByKey("LineIdMapping");
|
||||||
|
List<Integer> oracleLineIdList = LineIdMapping.keySet().stream().map(Integer::parseInt).collect(Collectors.toList());
|
||||||
|
Map<String, String> DevIdMapping = (Map<String, String>) redisUtil.getObjectByKey("DevIdMapping");
|
||||||
|
|
||||||
Runtime runtime = Runtime.getRuntime();
|
Runtime runtime = Runtime.getRuntime();
|
||||||
System.out.println("开始执行前总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
System.out.println("开始执行前总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
||||||
System.out.println("开始执行前已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
System.out.println("开始执行前已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
||||||
@@ -230,33 +238,72 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
//日志记录
|
//日志记录
|
||||||
JobDetailHoursInfluxDB jobDetailInfluxDBHours = jobDetailHoursInfluxDBService.lambdaQuery().eq(JobDetailHoursInfluxDB::getTableName, tableName)
|
// JobDetailHoursInfluxDB jobDetailInfluxDBHours = jobDetailHoursInfluxDBService.lambdaQuery().eq(JobDetailHoursInfluxDB::getTableName, tableName)
|
||||||
.eq(JobDetailHoursInfluxDB::getExcuteDate, dataAsynParam.getStartDateTime())
|
// .eq(JobDetailHoursInfluxDB::getExcuteDate, dataAsynParam.getStartDateTime())
|
||||||
.one();
|
// .one();
|
||||||
if (Objects.nonNull(jobDetailInfluxDBHours) && (jobDetailInfluxDBHours.getState() == 1 || jobDetailInfluxDBHours.getState() == 0|| jobDetailInfluxDBHours.getState() == 2)) {
|
// if (Objects.nonNull(jobDetailInfluxDBHours) && (jobDetailInfluxDBHours.getState() == 1 || jobDetailInfluxDBHours.getState() == 0|| jobDetailInfluxDBHours.getState() == 2)) {
|
||||||
//如果该指标当前时间段已执行或正在执行,直接跳出循环
|
// //如果该指标当前时间段已执行或正在执行,直接跳出循环
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
if (Objects.isNull(jobDetailInfluxDBHours)) {
|
// if (Objects.isNull(jobDetailInfluxDBHours)) {
|
||||||
jobDetailInfluxDBHours = new JobDetailHoursInfluxDB(tableName, dataAsynParam.getStartDateTime(), 0, 0, LocalDateTime.now());
|
// jobDetailInfluxDBHours = new JobDetailHoursInfluxDB(tableName, dataAsynParam.getStartDateTime(), 0, 0, LocalDateTime.now());
|
||||||
jobDetailHoursInfluxDBService.save(jobDetailInfluxDBHours);
|
// jobDetailHoursInfluxDBService.save(jobDetailInfluxDBHours);
|
||||||
}
|
// }
|
||||||
//程序监听
|
//程序监听
|
||||||
StopWatch stopWatch = new StopWatch();
|
StopWatch stopWatch = new StopWatch();
|
||||||
stopWatch.start();
|
stopWatch.start();
|
||||||
List list = new ArrayList(Collections.emptyList());
|
List list = new ArrayList(Collections.emptyList());
|
||||||
//获取监测点最新的数据时间,单监测点查询数据
|
if("ComInfoRmation".equals(tableName)){
|
||||||
List<lineTimeDto> lineTimeList = lineTimeMapper.getLineTime();
|
|
||||||
lineTimeList.forEach(item->{
|
|
||||||
MigrationParam migration = new MigrationParam();
|
MigrationParam migration = new MigrationParam();
|
||||||
migration.setLineIds(Collections.singletonList(item.getLineIndex()));
|
migration.setStartTime(dataAsynParam.getStartDateTime());
|
||||||
migration.setStartTime(item.getUpdateTime().minusHours(2));
|
migration.setEndTime(dataAsynParam.getEndDateTime());
|
||||||
migration.setEndTime(item.getUpdateTime());
|
migration.setLineIds(oracleLineIdList);
|
||||||
System.out.println("当前监测点为------------------------------------"+item.getLineIndex());
|
System.out.println("执行扫描起始时间------------------------------------"+dataAsynParam.getStartDateTime().minusHours(2).format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
System.out.println("执行扫描起始时间------------------------------------"+item.getUpdateTime().minusHours(2).format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
System.out.println("执行扫描结束时间------------------------------------"+dataAsynParam.getEndDateTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
System.out.println("执行扫描结束时间------------------------------------"+item.getUpdateTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
|
||||||
list.addAll(executor.queryData(migration));
|
list.addAll(executor.queryData(migration));
|
||||||
});
|
System.out.println("查询到的数据++++++++++++++"+list.size());
|
||||||
|
//反射获取linid的值并把linid的值替换成mysql对应的devid,并记录未匹配的devid
|
||||||
|
Iterator iterator = list.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
try{
|
||||||
|
Object obj = iterator.next();
|
||||||
|
//获取
|
||||||
|
Field id = obj.getClass().getDeclaredField("lineIndex");
|
||||||
|
id.setAccessible(true); //暴力访问id
|
||||||
|
Object o = id.get(obj);
|
||||||
|
if(ObjectUtil.isNotNull(o)){
|
||||||
|
int index = Integer.parseInt(o.toString())/10;
|
||||||
|
if (!DevIdMapping.containsKey(index+"")){
|
||||||
|
log.info(tableName+"表---Oralcet数据同步到InfluxDB未找mysql中到devid匹配的devid"+index);
|
||||||
|
iterator.remove();
|
||||||
|
}else {
|
||||||
|
id.set(obj, DevIdMapping.get(index+""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
MigrationParam migration = new MigrationParam();
|
||||||
|
migration.setStartTime(dataAsynParam.getStartDateTime());
|
||||||
|
migration.setEndTime(dataAsynParam.getEndDateTime());
|
||||||
|
migration.setLineIds(oracleLineIdList);
|
||||||
|
System.out.println("执行扫描起始时间------------------------------------"+dataAsynParam.getStartDateTime().minusHours(2).format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
System.out.println("执行扫描结束时间------------------------------------"+dataAsynParam.getEndDateTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
list.addAll(executor.queryData(migration));
|
||||||
|
//获取监测点最新的数据时间,单监测点查询数据
|
||||||
|
// List<lineTimeDto> lineTimeList = lineTimeMapper.getLineTime();
|
||||||
|
// lineTimeList.forEach(item->{
|
||||||
|
// MigrationParam migration = new MigrationParam();
|
||||||
|
// migration.setLineIds(Collections.singletonList(item.getLineIndex()));
|
||||||
|
// migration.setStartTime(item.getUpdateTime().minusHours(2));
|
||||||
|
// migration.setEndTime(item.getUpdateTime());
|
||||||
|
// System.out.println("当前监测点为------------------------------------"+item.getLineIndex());
|
||||||
|
// System.out.println("执行扫描起始时间------------------------------------"+item.getUpdateTime().minusHours(2).format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
// System.out.println("执行扫描结束时间------------------------------------"+item.getUpdateTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
// list.addAll(executor.queryData(migration));
|
||||||
|
// });
|
||||||
System.out.println("查询到的数据++++++++++++++"+list.size());
|
System.out.println("查询到的数据++++++++++++++"+list.size());
|
||||||
//反射獲取linid的值并把linid的值替换成mysql对应的linid,并记录未匹配的lineid
|
//反射獲取linid的值并把linid的值替换成mysql对应的linid,并记录未匹配的lineid
|
||||||
Iterator iterator = list.iterator();
|
Iterator iterator = list.iterator();
|
||||||
@@ -267,16 +314,17 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
Field id = obj.getClass().getDeclaredField("lineid");
|
Field id = obj.getClass().getDeclaredField("lineid");
|
||||||
id.setAccessible(true); //暴力访问id
|
id.setAccessible(true); //暴力访问id
|
||||||
String id1 = id.get(obj).toString();
|
String id1 = id.get(obj).toString();
|
||||||
if (!IdMappingCache.LineIdMapping.containsKey(id1)){
|
if (!LineIdMapping.containsKey(id1)){
|
||||||
log.info(tableName+"表---Oralcet数据同步到InfluxDB未找mysql中到lineid匹配的lineid"+id1);
|
log.info(tableName+"表---Oralcet数据同步到InfluxDB未找mysql中到lineid匹配的lineid"+id1);
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}else {
|
}else {
|
||||||
id.set(obj, IdMappingCache.LineIdMapping.get(id1));
|
id.set(obj,LineIdMapping.get(id1));
|
||||||
}
|
}
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//采用弱引用接受,后续手动调用gc后,会清空该对象
|
//采用弱引用接受,后续手动调用gc后,会清空该对象
|
||||||
WeakReference<List> weakReferenceData = new WeakReference<>(list);
|
WeakReference<List> weakReferenceData = new WeakReference<>(list);
|
||||||
int size = 0;
|
int size = 0;
|
||||||
@@ -323,15 +371,15 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
//手动执行GC
|
//手动执行GC
|
||||||
System.gc();
|
System.gc();
|
||||||
stopWatch.stop();
|
stopWatch.stop();
|
||||||
jobDetailInfluxDBHours.setRowCount(size);
|
// jobDetailInfluxDBHours.setRowCount(size);
|
||||||
jobDetailInfluxDBHours.setState(1);
|
// jobDetailInfluxDBHours.setState(1);
|
||||||
jobDetailInfluxDBHours.setDuration(stopWatch.getTotalTimeSeconds());
|
// jobDetailInfluxDBHours.setDuration(stopWatch.getTotalTimeSeconds());
|
||||||
jobDetailHoursInfluxDBService.updateByMultiId(jobDetailInfluxDBHours);
|
// jobDetailHoursInfluxDBService.updateByMultiId(jobDetailInfluxDBHours);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
exception.printStackTrace();
|
exception.printStackTrace();
|
||||||
jobDetailInfluxDBHours.setState(2);
|
// jobDetailInfluxDBHours.setState(2);
|
||||||
jobDetailInfluxDBHours.setUpdateTime(LocalDateTime.now());
|
// jobDetailInfluxDBHours.setUpdateTime(LocalDateTime.now());
|
||||||
jobDetailHoursInfluxDBService.updateByMultiId(jobDetailInfluxDBHours);
|
// jobDetailHoursInfluxDBService.updateByMultiId(jobDetailInfluxDBHours);
|
||||||
}
|
}
|
||||||
System.out.println("执行后总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
System.out.println("执行后总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
||||||
System.out.println("执行后已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
System.out.println("执行后已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
||||||
@@ -351,9 +399,133 @@ public class OracleToInfluxDBServiceImpl implements OracleToInfluxDBService {
|
|||||||
DataAsynParam dataAsynParam1 = new DataAsynParam();
|
DataAsynParam dataAsynParam1 = new DataAsynParam();
|
||||||
dataAsynParam1.setEndDateTime(startDateTime1.minusHours(-1).minusSeconds(1));
|
dataAsynParam1.setEndDateTime(startDateTime1.minusHours(-1).minusSeconds(1));
|
||||||
dataAsynParam1.setStartDateTime(startDateTime1);
|
dataAsynParam1.setStartDateTime(startDateTime1);
|
||||||
|
if(CollUtil.isEmpty(dataAsynParam.getTableNames())){
|
||||||
dataAsynParam1.setTableNames(TableEnum.getExecutableTypes());
|
dataAsynParam1.setTableNames(TableEnum.getExecutableTypes());
|
||||||
|
}else{
|
||||||
|
dataAsynParam1.setTableNames(dataAsynParam.getTableNames());
|
||||||
|
}
|
||||||
log.info("执行"+startDateTime1+"时刻数据");
|
log.info("执行"+startDateTime1+"时刻数据");
|
||||||
this.hourseDataBacthSysc(dataAsynParam1);
|
this.hourseDataBacthSysc(dataAsynParam1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void oneMonitorDataTransport(DataAsynParam dataAsynParam) {
|
||||||
|
Map<String, String> LineIdMapping = (Map<String, String>) redisUtil.getObjectByKey("LineIdMapping");
|
||||||
|
List<Integer> oracleLineIdList = LineIdMapping.keySet().stream().map(Integer::parseInt).collect(Collectors.toList());
|
||||||
|
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
System.out.println("开始执行前总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
||||||
|
System.out.println("开始执行前已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
||||||
|
System.out.println("开始执行前空闲堆内存为:" + runtime.freeMemory() / (1024 * 1024) + " MB");
|
||||||
|
//目前且作为2片,后续将该属性提取到配置文件中
|
||||||
|
List<String> tableNames = dataAsynParam.getTableNames();
|
||||||
|
//嵌套循环,先循环指标,再循环日期
|
||||||
|
tableNames.stream().forEach(tableName -> {
|
||||||
|
IReplenishMybatisService executor;
|
||||||
|
try {
|
||||||
|
executor = (IReplenishMybatisService) SpringUtil.getBean(Class.forName(PACKAGE_PREFIX + tableName + PACKAGE_SUFFIX));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
List<LocalDate> dateList = LocalDateUtil.getDateList(dataAsynParam.getStartTime(), dataAsynParam.getEndTime());
|
||||||
|
for (LocalDate date : dateList) {
|
||||||
|
|
||||||
|
//分片下,每段时间的小时数
|
||||||
|
int sliceHour = 24 / slice;
|
||||||
|
for (int i = 0; i < slice; i++) {
|
||||||
|
String dateStr = LocalDateTimeUtil.format(date, DatePattern.NORM_DATE_PATTERN);
|
||||||
|
LocalDateTime startTime = LocalDateTimeUtil.parse(dateStr + StrPool.C_SPACE + (sliceHour * i < 10 ? "0" + sliceHour * i : sliceHour * i) + ":00:00", DatePattern.NORM_DATETIME_PATTERN);
|
||||||
|
LocalDateTime endTime = LocalDateTimeUtil.parse(dateStr + StrPool.C_SPACE + (sliceHour * (i + 1) - 1 < 10 ? "0" + (sliceHour * (i + 1) - 1) : sliceHour * (i + 1) - 1) + ":59:59", DatePattern.NORM_DATETIME_PATTERN);
|
||||||
|
//查询该时区的数据,并准备入库
|
||||||
|
MigrationParam migration = new MigrationParam();
|
||||||
|
migration.setStartTime(startTime);
|
||||||
|
migration.setEndTime(endTime);
|
||||||
|
migration.setLineIds(oracleLineIdList);
|
||||||
|
|
||||||
|
if (Objects.nonNull(dataAsynParam.getMonitorId())) {
|
||||||
|
migration.setLineIds(Stream.of(dataAsynParam.getMonitorId()).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
List list = executor.queryData(migration);
|
||||||
|
//反射獲取linid的值并把linid的值替换成mysql对应的linid,并记录未匹配的lineid
|
||||||
|
Iterator iterator = list.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
try {
|
||||||
|
Object obj = iterator.next();
|
||||||
|
//获取
|
||||||
|
Field id = obj.getClass().getDeclaredField("lineid");
|
||||||
|
id.setAccessible(true); //暴力访问id
|
||||||
|
String id1 = id.get(obj).toString();
|
||||||
|
if (!LineIdMapping.containsKey(id1)) {
|
||||||
|
log.info(tableName + "表---Oralcet数据同步到InfluxDB未找mysql中到lineid匹配的lineid" + id1);
|
||||||
|
iterator.remove();
|
||||||
|
} else {
|
||||||
|
id.set(obj, LineIdMapping.get(id1));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//采用弱引用接受,后续手动调用gc后,会清空该对象
|
||||||
|
WeakReference<List> weakReferenceData = new WeakReference<>(list);
|
||||||
|
int size = 0;
|
||||||
|
if (CollectionUtil.isNotEmpty(weakReferenceData.get())) {
|
||||||
|
size = weakReferenceData.get().size();
|
||||||
|
}
|
||||||
|
System.out.println(tableName + "查到" + size + "条数据后总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
||||||
|
System.out.println(tableName + "查到" + size + "条数据后已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
||||||
|
System.out.println(tableName + "查到" + size + "条数据后空闲堆内存为:" + runtime.freeMemory() / (1024 * 1024) + " MB");
|
||||||
|
try {
|
||||||
|
if (CollectionUtil.isNotEmpty(weakReferenceData.get())) {
|
||||||
|
//执行目标库的数据处理
|
||||||
|
Class<?> clazz;
|
||||||
|
Class<?> clazz2;
|
||||||
|
//获取Table表对应的influxdb对应的表的实体类调用oralceToInfluxDB方法及oralceToInfluxDB的入参clazz2
|
||||||
|
try {
|
||||||
|
clazz = Class.forName("com.njcn.influx.bo.po.InfluxDB" + tableName);
|
||||||
|
clazz2 = Class.forName("com.njcn.oracle.bo.po." + tableName);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Method method;
|
||||||
|
try {
|
||||||
|
method = clazz.getDeclaredMethod("oralceToInfluxDB", clazz2);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
Method finalMethod = method;
|
||||||
|
List list1 = (List) weakReferenceData.get().stream().flatMap(po -> {
|
||||||
|
try {
|
||||||
|
Object invoke = finalMethod.invoke(null, po);
|
||||||
|
Object invoke1 = invoke;
|
||||||
|
//返回oracle转influx,flicker等表是1-1,还有1-4,这是判断返回是否是集合如何是集合继续扁平化
|
||||||
|
return invoke1 instanceof List ? ((List<?>) invoke1).stream() : Stream.of(invoke1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
//插入influxdb
|
||||||
|
influxDBBaseService.insertBatchBySlice(tableName, list1, 10000);
|
||||||
|
// size = list1.size();
|
||||||
|
//最后一片时修改状态
|
||||||
|
}
|
||||||
|
//手动执行GC
|
||||||
|
System.gc();
|
||||||
|
|
||||||
|
} catch (Exception exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println("执行后总堆内存为:" + runtime.totalMemory() / (1024 * 1024) + " MB");
|
||||||
|
System.out.println("执行后已用堆内存为:" + (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024) + " MB");
|
||||||
|
System.out.println("执行后空闲堆内存为:" + runtime.freeMemory() / (1024 * 1024) + " MB");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package com.njcn.influx.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.njcn.influx.bo.po.*;
|
||||||
|
import com.njcn.influx.mapper.OracleRmpEventDetailPOMapper;
|
||||||
|
import com.njcn.influx.mapper.PqDeviceMapper;
|
||||||
|
import com.njcn.influx.mapper.RmpEventDetailPOMapper;
|
||||||
|
import com.njcn.influx.service.OracleToMysqlService;
|
||||||
|
import com.njcn.influx.service.PqLineBakService;
|
||||||
|
import com.njcn.influx.service.PqsEventLogService;
|
||||||
|
import com.njcn.influx.service.RmpEventDetailService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 9:54【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class OracleToMysqlServiceImpl implements OracleToMysqlService {
|
||||||
|
|
||||||
|
private final RmpEventDetailService rmpEventDetailService;
|
||||||
|
private final OracleRmpEventDetailPOMapper oracleRmpEventDetailPOMapper;
|
||||||
|
|
||||||
|
|
||||||
|
private final PqsEventLogService pqsEventLogService;
|
||||||
|
private final EventAsyncService eventAsyncService;
|
||||||
|
@Value("${business.dealFlag:true}")
|
||||||
|
private Boolean dealFlag;
|
||||||
|
@Value("${business.upLoadFlag:true}")
|
||||||
|
private Boolean upLoadFlag;
|
||||||
|
@Override
|
||||||
|
public void OracleToMySqlJob(LocalDateTime start, LocalDateTime end) {
|
||||||
|
log.info("===== 定时扫描事件开始 =====");
|
||||||
|
|
||||||
|
List<OracleRmpEventDetailPO> oracleRmpEventDetailPOList = oracleRmpEventDetailPOMapper.selectList(new LambdaQueryWrapper<OracleRmpEventDetailPO>().between(OracleRmpEventDetailPO::getCreateTime,start,end));
|
||||||
|
for (OracleRmpEventDetailPO detail : oracleRmpEventDetailPOList) {
|
||||||
|
String eventId = detail.getEventId();
|
||||||
|
|
||||||
|
// 2. 判断是否已存在日志,避免重复
|
||||||
|
Integer cnt = pqsEventLogService.lambdaQuery().eq(PqsEventLog::getEventdetailIndex,eventId).count();
|
||||||
|
|
||||||
|
if (cnt == null ||cnt < 1){
|
||||||
|
// 3. 初始化日志:0未上传 0未计算
|
||||||
|
PqsEventLog pqsEventLog = new PqsEventLog();
|
||||||
|
pqsEventLog.setId(UUID.randomUUID().toString());
|
||||||
|
pqsEventLog.setEventdetailIndex(eventId);
|
||||||
|
//判断是否配置数据同步或者高级算法功能
|
||||||
|
if(dealFlag){
|
||||||
|
pqsEventLog.setDealflag(0);
|
||||||
|
}else {
|
||||||
|
pqsEventLog.setDealflag(2);
|
||||||
|
}
|
||||||
|
if(dealFlag){
|
||||||
|
pqsEventLog.setFileupflag(0);
|
||||||
|
}else {
|
||||||
|
pqsEventLog.setFileupflag(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
pqsEventLog.setFileupflag(0);
|
||||||
|
pqsEventLog.setCreattime(new Date());
|
||||||
|
pqsEventLog.setUpdatetime(new Date());
|
||||||
|
pqsEventLogService.save(pqsEventLog);
|
||||||
|
|
||||||
|
String wavename = detail.getWavePath();
|
||||||
|
|
||||||
|
rmpEventDetailService.syncToMysql(eventId);
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// WAVENAME 为空:直接标记
|
||||||
|
// ============================
|
||||||
|
if (wavename == null || wavename.trim().isEmpty()) {
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// WAVENAME 非空:线程池异步处理
|
||||||
|
// ============================
|
||||||
|
eventAsyncService.asyncHandleEvent(detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void retryAndCleanEvery10Min() {
|
||||||
|
log.info("===== 10分钟重试补偿 + 过期清理 =====");
|
||||||
|
|
||||||
|
// 1. 扫描未处理:fileup=0 deal=0
|
||||||
|
List<PqsEventLog> todoList = pqsEventLogService.lambdaQuery().eq(PqsEventLog::getFileupflag, 0)
|
||||||
|
.or()
|
||||||
|
.eq(PqsEventLog::getDealflag, 0).list();
|
||||||
|
|
||||||
|
for (PqsEventLog log : todoList) {
|
||||||
|
String eventId = log.getEventdetailIndex();
|
||||||
|
Date createTime = log.getCreattime();
|
||||||
|
OracleRmpEventDetailPO oracleRmpEventDetailPO = oracleRmpEventDetailPOMapper.selectById(eventId);
|
||||||
|
// 查询最新WAVENAME
|
||||||
|
|
||||||
|
|
||||||
|
boolean isWaveEmpty = (oracleRmpEventDetailPO.getWavePath() == null || oracleRmpEventDetailPO.getWavePath().isEmpty());
|
||||||
|
|
||||||
|
// ======================
|
||||||
|
// 超过2天:置为2
|
||||||
|
// ======================
|
||||||
|
if (createTime != null && System.currentTimeMillis() - createTime.getTime() > 2 * 86400000L) {
|
||||||
|
if (isWaveEmpty) {
|
||||||
|
pqsEventLogService.lambdaUpdate().set(PqsEventLog::getDealflag,2).set(PqsEventLog::getFileupflag,2).set(PqsEventLog::getUpdatetime,new Date()).eq(PqsEventLog::getEventdetailIndex,eventId).update();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================
|
||||||
|
// 有文件:重试异步处理
|
||||||
|
// ======================
|
||||||
|
if (!isWaveEmpty) {
|
||||||
|
eventAsyncService.asyncHandleEvent(oracleRmpEventDetailPO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.njcn.influx.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
|
||||||
|
import com.njcn.influx.service.PqsEventLogService;
|
||||||
|
import com.njcn.influx.bo.po.PqsEventLog;
|
||||||
|
import com.njcn.influx.mapper.PqsEventLogMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 10:23【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@DS("master")
|
||||||
|
public class PqsEventLogServiceImpl extends ServiceImpl<PqsEventLogMapper, PqsEventLog> implements PqsEventLogService {
|
||||||
|
}
|
||||||
@@ -3,30 +3,24 @@ package com.njcn.influx.service.impl;
|
|||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.njcn.influx.bo.param.TableEnum;
|
|
||||||
import com.njcn.influx.bo.po.PqsOnlinerateMysql;
|
import com.njcn.influx.bo.po.PqsOnlinerateMysql;
|
||||||
import com.njcn.influx.config.IdMappingCache;
|
import com.njcn.influx.config.RedisUtil;
|
||||||
import com.njcn.influx.service.IPqDeviceBakService;
|
|
||||||
import com.njcn.influx.service.PqsOnlinerateMysqlService;
|
import com.njcn.influx.service.PqsOnlinerateMysqlService;
|
||||||
import com.njcn.oracle.bo.param.DataAsynParam;
|
import com.njcn.oracle.bo.param.DataAsynParam;
|
||||||
import com.njcn.oracle.bo.po.DataFlicker;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.njcn.influx.bo.po.PqsOnlineratePO;
|
import com.njcn.influx.bo.po.PqsOnlineratePO;
|
||||||
import com.njcn.influx.mapper.PqsOnlineratePOMapper;
|
import com.njcn.influx.mapper.PqsOnlineratePOMapper;
|
||||||
import com.njcn.influx.service.PqsOnlineratePOService;
|
import com.njcn.influx.service.PqsOnlineratePOService;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -41,9 +35,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class PqsOnlineratePOServiceImpl extends ServiceImpl<PqsOnlineratePOMapper, PqsOnlineratePO> implements PqsOnlineratePOService{
|
public class PqsOnlineratePOServiceImpl extends ServiceImpl<PqsOnlineratePOMapper, PqsOnlineratePO> implements PqsOnlineratePOService{
|
||||||
private final PqsOnlinerateMysqlService pqsOnlinerateMysqlService;
|
private final PqsOnlinerateMysqlService pqsOnlinerateMysqlService;
|
||||||
|
private final RedisUtil redisUtil;
|
||||||
@Override
|
@Override
|
||||||
public void minutesDataBacthSysc(DataAsynParam dataAsynParam) {
|
public void minutesDataBacthSysc(DataAsynParam dataAsynParam) {
|
||||||
|
Map<String, String> DevIdMapping = (Map<String, String>) redisUtil.getObjectByKey("DevIdMapping");
|
||||||
List<PqsOnlineratePO> list = this.lambdaQuery().between(PqsOnlineratePO::getTimeid, LocalDateTimeUtil.beginOfDay(dataAsynParam.getStartDateTime()), LocalDateTimeUtil.endOfDay(dataAsynParam.getStartDateTime()).minusSeconds(1)).list();
|
List<PqsOnlineratePO> list = this.lambdaQuery().between(PqsOnlineratePO::getTimeid, LocalDateTimeUtil.beginOfDay(dataAsynParam.getStartDateTime()), LocalDateTimeUtil.endOfDay(dataAsynParam.getStartDateTime()).minusSeconds(1)).list();
|
||||||
log.info(dataAsynParam.getStartDateTime()+"-----数据量:"+list.size());
|
log.info(dataAsynParam.getStartDateTime()+"-----数据量:"+list.size());
|
||||||
if (CollectionUtil.isNotEmpty(list)) {
|
if (CollectionUtil.isNotEmpty(list)) {
|
||||||
@@ -51,10 +46,10 @@ public class PqsOnlineratePOServiceImpl extends ServiceImpl<PqsOnlineratePOMappe
|
|||||||
list.stream().forEach(temp -> {
|
list.stream().forEach(temp -> {
|
||||||
PqsOnlinerateMysql pqsOnlinerateMysql = new PqsOnlinerateMysql();
|
PqsOnlinerateMysql pqsOnlinerateMysql = new PqsOnlinerateMysql();
|
||||||
|
|
||||||
if (!IdMappingCache.DevIdMapping.containsKey(temp.getDevIndex()+"")){
|
if (!DevIdMapping.containsKey(temp.getDevIndex()+"")){
|
||||||
return;
|
return;
|
||||||
}else {
|
}else {
|
||||||
pqsOnlinerateMysql.setDevIndex(IdMappingCache.DevIdMapping.get(temp.getDevIndex()+""));
|
pqsOnlinerateMysql.setDevIndex(DevIdMapping.get(temp.getDevIndex()+""));
|
||||||
}
|
}
|
||||||
pqsOnlinerateMysql.setTimeId(temp.getTimeid());
|
pqsOnlinerateMysql.setTimeId(temp.getTimeid());
|
||||||
pqsOnlinerateMysql.setOnlineMin(temp.getOnlinemin());
|
pqsOnlinerateMysql.setOnlineMin(temp.getOnlinemin());
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package com.njcn.influx.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.njcn.influx.bo.po.DictData;
|
||||||
|
import com.njcn.influx.bo.po.OracleRmpEventDetailPO;
|
||||||
|
import com.njcn.influx.bo.po.PqLineBak;
|
||||||
|
import com.njcn.influx.bo.po.RmpEventDetailPO;
|
||||||
|
import com.njcn.influx.mapper.OracleRmpEventDetailPOMapper;
|
||||||
|
import com.njcn.influx.mapper.RmpEventDetailPOMapper;
|
||||||
|
import com.njcn.influx.service.PqLineBakService;
|
||||||
|
import com.njcn.influx.service.RmpEventDetailService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 11:29【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RmpEventDetailServiceImpl extends ServiceImpl<RmpEventDetailPOMapper, RmpEventDetailPO> implements RmpEventDetailService {
|
||||||
|
private final OracleRmpEventDetailPOMapper oracleRmpEventDetailPOMapper;
|
||||||
|
private final PqLineBakService pqLineBakService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syncToMysql(String eventId) {
|
||||||
|
OracleRmpEventDetailPO oracleDetail = oracleRmpEventDetailPOMapper.selectById(eventId);
|
||||||
|
|
||||||
|
List<PqLineBak> list = pqLineBakService.list();
|
||||||
|
|
||||||
|
//lineId:Oracle监测点ID id:Mysql监测点ID
|
||||||
|
Map<String, String> oracleRelationMysql = list.stream().collect(Collectors.toMap(PqLineBak::getLineId, PqLineBak::getId));
|
||||||
|
//获取Oracle字典 暂降类型:12 暂降原因:13
|
||||||
|
List<DictData> oracleReason= oracleRmpEventDetailPOMapper.selectByDicCodeList("暂降原因");
|
||||||
|
List<DictData> oracleType= oracleRmpEventDetailPOMapper.selectByDicCodeList("暂降类型");
|
||||||
|
//获取Mysql字典
|
||||||
|
List<DictData> eventType = this.getBaseMapper().selectByDicCodeList("Event_Statis");
|
||||||
|
List<DictData> mysqlReason = this.getBaseMapper().selectByDicCodeList("Event_Reason");
|
||||||
|
List<DictData> mysqlType = this.getBaseMapper().selectByDicCodeList("Event_Type");
|
||||||
|
//字典类型
|
||||||
|
Map<String, String> mapReason = getRelationList(oracleReason, mysqlReason);
|
||||||
|
Map<String, String> mapType = getRelationList(oracleType, mysqlType);
|
||||||
|
|
||||||
|
if(oracleRelationMysql.containsKey(oracleDetail.getMeasurementPointId())){
|
||||||
|
String mysqlLineID = oracleRelationMysql.get(oracleDetail.getMeasurementPointId());
|
||||||
|
RmpEventDetailPO po=new RmpEventDetailPO();
|
||||||
|
po.setEventId(eventId);
|
||||||
|
po.setMeasurementPointId(mysqlLineID);
|
||||||
|
po.setEventType(eventTypeId(oracleDetail.getEventType(),eventType));
|
||||||
|
po.setAdvanceReason(getDicId(oracleDetail.getAdvanceReason(),mapReason));
|
||||||
|
po.setAdvanceType(getDicId(oracleDetail.getAdvanceType(),mapType));
|
||||||
|
po.setEventassIndex(oracleDetail.getEventassIndex());
|
||||||
|
po.setDqTime(oracleDetail.getDqTime());
|
||||||
|
po.setDealTime(oracleDetail.getDealTime());
|
||||||
|
po.setNum(oracleDetail.getNum());
|
||||||
|
po.setFileFlag(oracleDetail.getFileFlag());
|
||||||
|
po.setDealFlag(oracleDetail.getDealFlag());
|
||||||
|
|
||||||
|
if(Objects.nonNull(oracleDetail.getFirstTime())) {
|
||||||
|
po.setFirstTime(oracleDetail.getFirstTime().plus(oracleDetail.getFirstMs().intValue(), ChronoUnit.MILLIS));
|
||||||
|
}
|
||||||
|
po.setFirstType(oracleDetail.getFirstType());
|
||||||
|
po.setFirstMs(oracleDetail.getFirstMs());
|
||||||
|
po.setEnergy(oracleDetail.getEnergy());
|
||||||
|
po.setSeverity(oracleDetail.getSeverity());
|
||||||
|
po.setSagsource(oracleDetail.getSagsource());
|
||||||
|
LocalDateTime startTime = oracleDetail.getStartTime();
|
||||||
|
LocalDateTime time = startTime.plus(oracleDetail.getMs(), ChronoUnit.MILLIS);
|
||||||
|
po.setStartTime(time);
|
||||||
|
po.setDuration(oracleDetail.getDuration().divide(BigDecimal.valueOf(1000)));
|
||||||
|
po.setFeatureAmplitude(oracleDetail.getFeatureAmplitude());
|
||||||
|
po.setPhase(oracleDetail.getPhase());
|
||||||
|
po.setEventDescribe(oracleDetail.getEventDescribe());
|
||||||
|
po.setWavePath(oracleDetail.getWavePath());
|
||||||
|
po.setTransientValue(oracleDetail.getTransientValue());
|
||||||
|
this.saveOrUpdate(po);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取暂降类型id
|
||||||
|
private String eventTypeId(String eventType, List<DictData> eventTypeList){
|
||||||
|
String code="";
|
||||||
|
//事件类型(0:扰动,1:暂降,2:暂升,3:中断,4:其他,5:录波)
|
||||||
|
if("0".equals(eventType)){
|
||||||
|
code="Disturbance";
|
||||||
|
}
|
||||||
|
if("1".equals(eventType)){
|
||||||
|
code="Voltage_Dip";
|
||||||
|
}
|
||||||
|
if("2".equals(eventType)){
|
||||||
|
code="Voltage_Rise";
|
||||||
|
}
|
||||||
|
if("3".equals(eventType)){
|
||||||
|
code="Short_Interruptions";
|
||||||
|
}
|
||||||
|
if("4".equals(eventType)){
|
||||||
|
code="Other";
|
||||||
|
}
|
||||||
|
if("5".equals(eventType)){
|
||||||
|
code="Recording_Wave";
|
||||||
|
}
|
||||||
|
String finalCode = code;
|
||||||
|
DictData dictData = eventTypeList.stream().filter(x -> finalCode.equals(x.getCode())).findFirst().get();
|
||||||
|
if(ObjectUtil.isNotNull(dictData)){
|
||||||
|
return dictData.getId();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//类型匹配
|
||||||
|
private Map<String,String> getRelationList(List<DictData> oracleReason,List<DictData> mysqlReason){
|
||||||
|
Map<String,String> map=new HashMap<>();
|
||||||
|
for (DictData dictData : oracleReason) {
|
||||||
|
for (DictData data : mysqlReason) {
|
||||||
|
if(dictData.getAlgoDescribe().equals(data.getAlgoDescribe())){
|
||||||
|
map.put(dictData.getId(),data.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDicId(String dicReason,Map<String,String> map){
|
||||||
|
if(map.containsKey(dicReason)){
|
||||||
|
return map.get(dicReason);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package com.njcn.influx.sftp;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.ChannelSftp;
|
||||||
|
import com.jcraft.jsch.*;
|
||||||
|
import com.jcraft.jsch.JSchException;
|
||||||
|
import com.jcraft.jsch.SftpException;
|
||||||
|
import com.njcn.influx.config.TargetConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2025/10/16 下午 3:00【需求编号】
|
||||||
|
*SFTP客户端实现
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Data
|
||||||
|
public class SftpClient implements AutoCloseable{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SftpClient.class);
|
||||||
|
|
||||||
|
private JSch jsch;
|
||||||
|
private Session session;
|
||||||
|
private ChannelSftp channel;
|
||||||
|
|
||||||
|
public void connect(TargetConfig config) throws JSchException {
|
||||||
|
jsch = new JSch();
|
||||||
|
|
||||||
|
// 设置私钥(如果提供)
|
||||||
|
if (config.getPrivateKeyPath() != null && !config.getPrivateKeyPath().isEmpty()) {
|
||||||
|
jsch.addIdentity(config.getPrivateKeyPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
|
||||||
|
|
||||||
|
// 设置密码(如果提供)
|
||||||
|
if (config.getPassword() != null && !config.getPassword().isEmpty()) {
|
||||||
|
session.setPassword(config.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置SSH连接
|
||||||
|
java.util.Properties sshConfig = new java.util.Properties();
|
||||||
|
sshConfig.put("StrictHostKeyChecking", "no");
|
||||||
|
session.setConfig(sshConfig);
|
||||||
|
|
||||||
|
session.connect(30000); // 30秒超时
|
||||||
|
|
||||||
|
// 打开SFTP通道
|
||||||
|
channel = (ChannelSftp) session.openChannel("sftp");
|
||||||
|
channel.connect(30000);
|
||||||
|
}
|
||||||
|
public void uploadFile(String localFile, String remotePath) throws SftpException {
|
||||||
|
File file = new File(localFile);
|
||||||
|
String remoteFile = ensureRemotePath(remotePath, file.getName());
|
||||||
|
|
||||||
|
// 确保远程目录存在
|
||||||
|
createRemoteDirectory(remotePath);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
channel.put(localFile, remoteFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelSftp.LsEntry getRemoteFileInfo(String remotePath) throws SftpException {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Vector<ChannelSftp.LsEntry> files = channel.ls(remotePath);
|
||||||
|
if (files != null && !files.isEmpty()) {
|
||||||
|
return files.get(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ensureRemotePath(String remotePath, String fileName) {
|
||||||
|
if (remotePath.endsWith("/")) {
|
||||||
|
return remotePath + fileName;
|
||||||
|
} else {
|
||||||
|
return remotePath + "/" + fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createRemoteDirectory(String remotePath) throws SftpException {
|
||||||
|
|
||||||
|
// 处理Windows路径分隔符
|
||||||
|
String normalizedPath = remotePath.replace("\\", "/");
|
||||||
|
// 去掉开头的/(避免根目录重复创建)
|
||||||
|
if (normalizedPath.startsWith("/")) {
|
||||||
|
normalizedPath = normalizedPath.substring(1);
|
||||||
|
}
|
||||||
|
String[] directories = normalizedPath.split("/");
|
||||||
|
StringBuilder currentPath = new StringBuilder();
|
||||||
|
|
||||||
|
// 递归创建多层目录
|
||||||
|
for (String dir : directories) {
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
currentPath.append("/").append(dir);
|
||||||
|
try {
|
||||||
|
channel.mkdir(currentPath.toString());
|
||||||
|
logger.debug("创建远程目录成功: {}", currentPath);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
if (e.id != ChannelSftp.SSH_FX_FAILURE) {
|
||||||
|
logger.error("创建远程目录失败: {}", currentPath, e);
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
logger.debug("远程目录已存在: {}", currentPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (channel != null) {
|
||||||
|
channel.disconnect();
|
||||||
|
}
|
||||||
|
if (session != null) {
|
||||||
|
session.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,37 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>influx-target</finalName>
|
<finalName>influx-target</finalName>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>*.yml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>*.dll</include>
|
||||||
|
<include>*.xlsx</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>*.so</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
|||||||
@EnableAsync
|
@EnableAsync
|
||||||
@DependsOn("proxyMapperRegister")
|
@DependsOn("proxyMapperRegister")
|
||||||
@MapperScan("com.njcn.**.mapper")
|
@MapperScan("com.njcn.**.mapper")
|
||||||
@SpringBootApplication(scanBasePackages = "com.njcn", exclude = {SecurityAutoConfiguration.class, SecurityFilterAutoConfiguration.class})
|
@SpringBootApplication(scanBasePackages = "com.njcn")
|
||||||
public class InfluxDataApplication {
|
public class InfluxDataApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.njcn.influx.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class Security extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.formLogin().and().authorizeRequests().anyRequest().authenticated().and().csrf().disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ package com.njcn.influx.controller;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.njcn.influx.bo.po.JobDetailInfluxDB;
|
import com.njcn.influx.bo.po.JobDetailInfluxDB;
|
||||||
|
import com.njcn.influx.config.IdMappingCache;
|
||||||
import com.njcn.influx.service.JobDetailInfluxDBService;
|
import com.njcn.influx.service.JobDetailInfluxDBService;
|
||||||
|
import com.njcn.oracle.bo.param.DataAsynParam;
|
||||||
import com.njcn.oracle.bo.param.JobQueryParam;
|
import com.njcn.oracle.bo.param.JobQueryParam;
|
||||||
import com.njcn.oracle.bo.po.JobDetail;
|
import com.njcn.oracle.bo.po.JobDetail;
|
||||||
import com.njcn.oracle.service.JobDetailService;
|
import com.njcn.oracle.service.JobDetailService;
|
||||||
@@ -13,10 +15,7 @@ import io.swagger.annotations.ApiOperation;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description:
|
* Description:
|
||||||
@@ -34,6 +33,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
public class JobDetailInfluxDBController {
|
public class JobDetailInfluxDBController {
|
||||||
|
|
||||||
private final JobDetailInfluxDBService jobDetailInfluxDBService;
|
private final JobDetailInfluxDBService jobDetailInfluxDBService;
|
||||||
|
private final IdMappingCache idMappingCache;
|
||||||
@PostMapping("/jobQuery")
|
@PostMapping("/jobQuery")
|
||||||
@ApiOperation("任务查询")
|
@ApiOperation("任务查询")
|
||||||
@ApiImplicitParam(name = "jobQueryParam", value = "任务查询参数", required = true)
|
@ApiImplicitParam(name = "jobQueryParam", value = "任务查询参数", required = true)
|
||||||
@@ -48,4 +48,12 @@ public class JobDetailInfluxDBController {
|
|||||||
boolean b = jobDetailInfluxDBService.jobRemove(jobDetail);
|
boolean b = jobDetailInfluxDBService.jobRemove(jobDetail);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/refreshIdCache")
|
||||||
|
@ApiOperation("刷新缓存")
|
||||||
|
public Boolean refreshIdCache() {
|
||||||
|
DataAsynParam dataAsynParam = new DataAsynParam();
|
||||||
|
idMappingCache.init();
|
||||||
|
return true;// HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, "数据同步");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package com.njcn.influx.controller;
|
|||||||
|
|
||||||
import cn.hutool.core.date.DatePattern;
|
import cn.hutool.core.date.DatePattern;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
import com.njcn.influx.service.OracleEventDetailToMysqlService;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.njcn.influx.service.OracleMonitorStatusToMysqlService;
|
import com.njcn.influx.service.*;
|
||||||
import com.njcn.influx.service.OracleToInfluxDBService;
|
|
||||||
import com.njcn.influx.service.PqsOnlineratePOService;
|
|
||||||
import com.njcn.oracle.bo.param.DataAsynParam;
|
import com.njcn.oracle.bo.param.DataAsynParam;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiImplicitParam;
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
@@ -16,8 +14,11 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,6 +41,7 @@ public class OracleToInfluxDBController {
|
|||||||
private final OracleEventDetailToMysqlService oracleEventDetailToMysqlService;
|
private final OracleEventDetailToMysqlService oracleEventDetailToMysqlService;
|
||||||
private final OracleMonitorStatusToMysqlService oracleMonitorStatusToMysqlService;
|
private final OracleMonitorStatusToMysqlService oracleMonitorStatusToMysqlService;
|
||||||
|
|
||||||
|
private final OracleToMysqlService oracleToMysqlService;
|
||||||
|
|
||||||
@PostMapping("/dataSync")
|
@PostMapping("/dataSync")
|
||||||
@ApiOperation("数据同步")
|
@ApiOperation("数据同步")
|
||||||
@@ -59,6 +61,22 @@ public class OracleToInfluxDBController {
|
|||||||
return true;// HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, "数据同步");
|
return true;// HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, "数据同步");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/dataSyncTable")
|
||||||
|
@ApiOperation("数据同步")
|
||||||
|
public Boolean dataSyncTable(@RequestParam("startDateTime") String startDateTime,
|
||||||
|
@RequestParam("endDateTime") String endDateTime,
|
||||||
|
@RequestParam("tableName") String tableName
|
||||||
|
) {
|
||||||
|
DataAsynParam dataAsynParam = new DataAsynParam();
|
||||||
|
dataAsynParam.setStartDateTime(LocalDateTimeUtil.parse(startDateTime, DatePattern.NORM_DATETIME_PATTERN));
|
||||||
|
dataAsynParam.setEndDateTime(LocalDateTimeUtil.parse(endDateTime, DatePattern.NORM_DATETIME_PATTERN));
|
||||||
|
if(StrUtil.isNotBlank(tableName)){
|
||||||
|
dataAsynParam.setTableNames(Collections.singletonList(tableName));
|
||||||
|
}
|
||||||
|
oracleToInfluxDBService.AsyncData(dataAsynParam);
|
||||||
|
return true;// HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, "数据同步");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/oneMonitorDataTransport")
|
@PostMapping("/oneMonitorDataTransport")
|
||||||
@@ -89,16 +107,30 @@ public class OracleToInfluxDBController {
|
|||||||
LocalDateTime startDate = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.parse(startDateTime, DatePattern.NORM_DATE_PATTERN));
|
LocalDateTime startDate = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.parse(startDateTime, DatePattern.NORM_DATE_PATTERN));
|
||||||
LocalDateTime endDate = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.parse(endDateTime, DatePattern.NORM_DATE_PATTERN));
|
LocalDateTime endDate = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.parse(endDateTime, DatePattern.NORM_DATE_PATTERN));
|
||||||
long betweenDay = LocalDateTimeUtil.between(startDate, endDate, ChronoUnit.DAYS);
|
long betweenDay = LocalDateTimeUtil.between(startDate, endDate, ChronoUnit.DAYS);
|
||||||
oracleEventDetailToMysqlService.eventBatch(startDate, endDate);
|
oracleEventDetailToMysqlService.eventBatch(startDate, LocalDateTime.of(LocalDate.from(startDate), LocalTime.MAX));
|
||||||
for (int i = 0; i <=betweenDay; i++) {
|
for (int i = 0; i <=betweenDay; i++) {
|
||||||
startDate = LocalDateTimeUtil.offset(startDate, 1, ChronoUnit.DAYS);
|
startDate = LocalDateTimeUtil.offset(startDate, 1, ChronoUnit.DAYS);
|
||||||
oracleEventDetailToMysqlService.eventBatch(startDate, endDate);
|
oracleEventDetailToMysqlService.eventBatch(startDate, LocalDateTime.of(LocalDate.from(startDate), LocalTime.MAX));
|
||||||
}
|
}
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
exception.printStackTrace();
|
exception.printStackTrace();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/eventRecall")
|
||||||
|
@ApiOperation("eventDetail表数据同步补招接口")
|
||||||
|
public Boolean eventRecall(@RequestParam("startDateTime") String startDateTime,@RequestParam("endDateTime") String endDateTime) {
|
||||||
|
try {
|
||||||
|
LocalDateTime startDate = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.parse(startDateTime, DatePattern.NORM_DATETIME_PATTERN));
|
||||||
|
LocalDateTime endDate = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.parse(endDateTime, DatePattern.NORM_DATETIME_PATTERN));
|
||||||
|
oracleToMysqlService.OracleToMySqlJob(startDate, endDate );
|
||||||
|
|
||||||
|
} catch (Exception exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@GetMapping("/mSync")
|
@GetMapping("/mSync")
|
||||||
@ApiOperation("监测点信息同步")
|
@ApiOperation("监测点信息同步")
|
||||||
public Boolean monitorTimeSync() {
|
public Boolean monitorTimeSync() {
|
||||||
|
|||||||
@@ -1,123 +1,125 @@
|
|||||||
package com.njcn.influx.job;
|
//package com.njcn.influx.job;
|
||||||
|
//
|
||||||
import com.njcn.influx.bo.param.TableEnum;
|
//import com.njcn.influx.bo.param.TableEnum;
|
||||||
import com.njcn.influx.service.OracleEventDetailToMysqlService;
|
//import com.njcn.influx.service.OracleEventDetailToMysqlService;
|
||||||
import com.njcn.influx.service.OracleMonitorStatusToMysqlService;
|
//import com.njcn.influx.service.OracleMonitorStatusToMysqlService;
|
||||||
import com.njcn.influx.service.OracleToInfluxDBService;
|
//import com.njcn.influx.service.OracleToInfluxDBService;
|
||||||
import com.njcn.influx.service.PqsOnlineratePOService;
|
//import com.njcn.influx.service.PqsOnlineratePOService;
|
||||||
import com.njcn.oracle.bo.param.DataAsynParam;
|
//import com.njcn.oracle.bo.param.DataAsynParam;
|
||||||
import lombok.RequiredArgsConstructor;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
//import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
//import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
//import org.springframework.stereotype.Component;
|
||||||
|
//
|
||||||
import java.time.LocalDate;
|
//import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
//import java.time.LocalDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
//import java.time.temporal.ChronoUnit;
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* Description:
|
// * Description:
|
||||||
* Date: 2024/1/18 10:15【需求编号】
|
// * Date: 2024/1/18 10:15【需求编号】
|
||||||
*
|
// *
|
||||||
* @author clam
|
// * @author clam
|
||||||
* @version V1.0.0
|
// * @version V1.0.0
|
||||||
*/
|
// */
|
||||||
@Component
|
//@Component
|
||||||
@EnableScheduling
|
//@EnableScheduling
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
public class OracleToInfluxDBJob {
|
//public class OracleToInfluxDBJob {
|
||||||
|
//
|
||||||
|
//
|
||||||
private final OracleToInfluxDBService oracleToInfluxDBService;
|
// private final OracleToInfluxDBService oracleToInfluxDBService;
|
||||||
|
//
|
||||||
private final OracleEventDetailToMysqlService oracleEventDetailToMysqlService;
|
// private final OracleEventDetailToMysqlService oracleEventDetailToMysqlService;
|
||||||
private final OracleMonitorStatusToMysqlService oracleMonitorStatusToMysqlService;
|
// private final OracleMonitorStatusToMysqlService oracleMonitorStatusToMysqlService;
|
||||||
|
//
|
||||||
private final PqsOnlineratePOService pqsOnlineratePOService;
|
// private final PqsOnlineratePOService pqsOnlineratePOService;
|
||||||
/*@Scheduled(cron="0 5 0 * * ?")
|
// /*@Scheduled(cron="0 5 0 * * ?")
|
||||||
public void execute() {
|
// public void execute() {
|
||||||
DataAsynParam dataAsynParam = new DataAsynParam();
|
// DataAsynParam dataAsynParam = new DataAsynParam();
|
||||||
dataAsynParam.setStartTime(LocalDate.now().plusDays(-1));
|
// dataAsynParam.setStartTime(LocalDate.now().plusDays(-1));
|
||||||
dataAsynParam.setEndTime(LocalDate.now().plusDays(-1));
|
// dataAsynParam.setEndTime(LocalDate.now().plusDays(-1));
|
||||||
dataAsynParam.setTableNames(TableEnum.getExecutableTypes());
|
// dataAsynParam.setTableNames(TableEnum.getExecutableTypes());
|
||||||
dataAsynParam.setExcuteType(2);
|
// dataAsynParam.setExcuteType(2);
|
||||||
oracleToInfluxDBService.dataBacthSysc(dataAsynParam);
|
// oracleToInfluxDBService.dataBacthSysc(dataAsynParam);
|
||||||
}*/
|
// }*/
|
||||||
|
//
|
||||||
//每小时03分钟时执行上一个小时的数据同步
|
// //每小时03分钟时执行上一个小时的数据同步
|
||||||
//河北这边比较特殊,
|
// //河北这边比较特殊,
|
||||||
@Scheduled(cron="0 40 * * * ?")
|
// @Scheduled(cron="0 15 * * * ?")
|
||||||
public void executeHours() {
|
// public void executeHours() {
|
||||||
DataAsynParam dataAsynParam = new DataAsynParam();
|
// DataAsynParam dataAsynParam = new DataAsynParam();
|
||||||
// 获取当前时间
|
// // 获取当前时间
|
||||||
LocalDateTime now = LocalDateTime.now();
|
// LocalDateTime now = LocalDateTime.now();
|
||||||
|
//
|
||||||
// 减去一个小时
|
// // 减去一个小时
|
||||||
LocalDateTime oneHourAgo = now.minusHours(1);
|
// LocalDateTime oneHourAgo = now.minusHours(2);
|
||||||
|
//
|
||||||
// 将分钟和秒设置为0
|
// // 将分钟和秒设置为0
|
||||||
LocalDateTime result = oneHourAgo.truncatedTo(ChronoUnit.HOURS);
|
// LocalDateTime result = oneHourAgo.truncatedTo(ChronoUnit.HOURS);
|
||||||
// 加上59分钟59秒
|
// // 加上59分钟59秒
|
||||||
LocalDateTime modifiedResult = result.plusMinutes(59).plusSeconds(59);
|
// LocalDateTime modifiedResult = result.plusMinutes(59).plusSeconds(59);
|
||||||
dataAsynParam.setStartDateTime(result);
|
// dataAsynParam.setStartDateTime(result);
|
||||||
dataAsynParam.setEndDateTime(modifiedResult);
|
// dataAsynParam.setEndDateTime(modifiedResult);
|
||||||
dataAsynParam.setTableNames(TableEnum.getExecutableTypes());
|
// dataAsynParam.setTableNames(TableEnum.getExecutableTypes());
|
||||||
dataAsynParam.setExcuteType(2);
|
// dataAsynParam.setExcuteType(2);
|
||||||
oracleToInfluxDBService.hourseDataBacthSysc(dataAsynParam);
|
// oracleToInfluxDBService.hourseDataBacthSysc(dataAsynParam);
|
||||||
}
|
// }
|
||||||
//每10分钟执行一次pqOnlinerate表同步
|
// //每10分钟执行一次pqOnlinerate表同步
|
||||||
@Scheduled(cron="0 0/10 * * * ?")
|
//// @Scheduled(cron="0 0/10 * * * ?")
|
||||||
public void pqOnlinerate() {
|
//// public void pqOnlinerate() {
|
||||||
DataAsynParam dataAsynParam = new DataAsynParam();
|
//// DataAsynParam dataAsynParam = new DataAsynParam();
|
||||||
// 获取当前时间
|
//// // 获取当前时间
|
||||||
LocalDateTime now = LocalDateTime.now();
|
//// LocalDateTime now = LocalDateTime.now();
|
||||||
dataAsynParam.setStartDateTime(now);
|
//// dataAsynParam.setStartDateTime(now);
|
||||||
pqsOnlineratePOService.minutesDataBacthSysc(dataAsynParam);
|
//// pqsOnlineratePOService.minutesDataBacthSysc(dataAsynParam);
|
||||||
}
|
//// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 每小时同步oracle数据库暂态事件
|
// * 每小时同步oracle数据库暂态事件
|
||||||
* @date 2024/3/5
|
// * @date 2024/3/5
|
||||||
*/
|
// */
|
||||||
@Scheduled(cron="0 0/10 * * * ?")
|
// @Scheduled(cron="0 0/10 * * * ?")
|
||||||
public void executeEvent() {
|
// public void executeEvent() {
|
||||||
// 获取当前时间
|
// // 获取当前时间
|
||||||
LocalDateTime now = LocalDateTime.now();
|
// LocalDateTime now = LocalDateTime.now();
|
||||||
// 减去一个小时
|
// // 减去一个小时
|
||||||
LocalDateTime oneHourAgo = now.minusHours(1);
|
// LocalDateTime oneHourAgo = now.minusHours(1);
|
||||||
// 将分钟和秒设置为0
|
// // 将分钟和秒设置为0
|
||||||
LocalDateTime result = oneHourAgo.truncatedTo(ChronoUnit.HOURS);
|
// LocalDateTime result = oneHourAgo.truncatedTo(ChronoUnit.HOURS);
|
||||||
// 加上59分钟59秒
|
// // 加上59分钟59秒
|
||||||
LocalDateTime modifiedResult = result.plusMinutes(59).plusSeconds(59);
|
// LocalDateTime modifiedResult = result.plusMinutes(59).plusSeconds(59);
|
||||||
oracleEventDetailToMysqlService.eventBatch(result,modifiedResult);
|
// oracleEventDetailToMysqlService.eventBatch(result,modifiedResult);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 每天同步台账装置的运行状态,监测点的运行状态
|
// * 每天同步台账装置的运行状态,监测点的运行状态
|
||||||
* @date 2024/3/5
|
// * @date 2024/3/5
|
||||||
*/
|
// */
|
||||||
@Scheduled(cron="0 0/10 * * * ?")
|
// @Scheduled(cron="0 0/10 * * * ?")
|
||||||
public void synLedgerRunFlag() {
|
// public void synLedgerRunFlag() {
|
||||||
oracleMonitorStatusToMysqlService.monitorStatusSync();
|
// oracleMonitorStatusToMysqlService.monitorStatusSync();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 每天同步台账装置的最后更新时间
|
// * 每天同步台账装置的最后更新时间
|
||||||
* @date 2024/3/5
|
// * @date 2024/3/5
|
||||||
*/
|
// */
|
||||||
@Scheduled(cron="0 45 0 * * ?")
|
// @Scheduled(cron="0 45 0 * * ?")
|
||||||
public void synLedgerUpdateTime() {
|
// public void synLedgerUpdateTime() {
|
||||||
oracleMonitorStatusToMysqlService.devUpdateTimeSync();
|
// oracleMonitorStatusToMysqlService.devUpdateTimeSync();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* 每天同步台账监测点部分信息 仅数据中心使用
|
// * 每天同步台账监测点部分信息 仅数据中心使用
|
||||||
* @date 2024/3/5
|
// * @date 2024/3/5
|
||||||
*/
|
// */
|
||||||
@Scheduled(cron="0 30 0 * * ?")
|
// /* @Scheduled(cron="0 30 0 * * ?")
|
||||||
public void synLedgerMonitor() {
|
// public void synLedgerMonitor() {
|
||||||
oracleMonitorStatusToMysqlService.monitorTimeSync();
|
// oracleMonitorStatusToMysqlService.monitorTimeSync();
|
||||||
}
|
// }*/
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.njcn.influx.job;
|
||||||
|
|
||||||
|
|
||||||
|
import com.njcn.influx.service.OracleToMysqlService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description:
|
||||||
|
* Date: 2026/04/20 上午 9:44【需求编号】
|
||||||
|
*
|
||||||
|
* @author clam
|
||||||
|
* @version V1.0.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@EnableScheduling
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class OracleToMySqlJob {
|
||||||
|
|
||||||
|
private final OracleToMysqlService oracleToMysqlService;
|
||||||
|
|
||||||
|
|
||||||
|
@Scheduled(cron = "${business.executeEventExpression}")
|
||||||
|
public void executeEvent() {
|
||||||
|
// 获取当前时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
// 减去一个小时
|
||||||
|
LocalDateTime oneHourAgo = now.minusHours(3);
|
||||||
|
|
||||||
|
oracleToMysqlService.OracleToMySqlJob(oneHourAgo,now);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 每10分钟:重试 + 2天过期处理
|
||||||
|
// ==============================
|
||||||
|
@Scheduled(cron = "${business.retryAndCleanEvery10Min}")
|
||||||
|
public void retryAndCleanEvery10Min() {
|
||||||
|
|
||||||
|
oracleToMysqlService.retryAndCleanEvery10Min();;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user