fix:1.添加jeecg-model-consumer模块,修改源项重建、输运模拟、天气预报计算功能,使任务与业务逻辑分开,适应多服务器部署
This commit is contained in:
parent
b780ad3127
commit
5b83cccf7f
|
|
@ -8,19 +8,28 @@ public enum SourceRebuildTaskStatusEnum {
|
|||
/**
|
||||
* 执行失败
|
||||
*/
|
||||
ERROR(-1),
|
||||
FAILURE(-1),
|
||||
/**
|
||||
* 未开始
|
||||
*/
|
||||
NOT_STARTED(0),
|
||||
/**
|
||||
* 等待中
|
||||
*/
|
||||
WAITING(1),
|
||||
/**
|
||||
* 执行中
|
||||
*/
|
||||
IN_OPERATION(1),
|
||||
IN_OPERATION(2),
|
||||
/**
|
||||
* 已完成
|
||||
*/
|
||||
COMPLETED(2);
|
||||
COMPLETED(3),
|
||||
|
||||
/**
|
||||
* 检查未通过
|
||||
*/
|
||||
INSPECTION_FAILED(4);
|
||||
|
||||
private Integer value;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,21 +14,31 @@ public enum WeatherTaskStatusEnum {
|
|||
*/
|
||||
NOT_STARTED(0),
|
||||
/**
|
||||
* 进行中
|
||||
* 等待中
|
||||
*/
|
||||
IN_OPERATION(1),
|
||||
WAITING(1),
|
||||
/**
|
||||
* 执行中
|
||||
*/
|
||||
IN_OPERATION(2),
|
||||
/**
|
||||
* 已完成
|
||||
*/
|
||||
COMPLETED(2);
|
||||
COMPLETED(3),
|
||||
|
||||
private Integer key;
|
||||
/**
|
||||
* 检查未通过
|
||||
*/
|
||||
INSPECTION_FAILED(4);
|
||||
;
|
||||
|
||||
WeatherTaskStatusEnum(Integer key) {
|
||||
this.key = key;
|
||||
private Integer value;
|
||||
|
||||
WeatherTaskStatusEnum(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Integer getKey(){
|
||||
return this.key;
|
||||
public Integer getValue(){
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package org.jeecg.properties;
|
||||
package org.jeecg.common.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
|
@ -9,11 +9,6 @@ import org.springframework.stereotype.Component;
|
|||
@ConfigurationProperties(prefix = "source-rebuild")
|
||||
public class SourceRebuildProperties {
|
||||
|
||||
/**
|
||||
* Rserve 服务地址
|
||||
*/
|
||||
private String serverAddr;
|
||||
|
||||
/**
|
||||
* Rserve端口
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,43 +6,38 @@ import org.springframework.stereotype.Component;
|
|||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "filesystem")
|
||||
@ConfigurationProperties(prefix = "weather")
|
||||
public class SystemStorageProperties {
|
||||
|
||||
/**
|
||||
* 系统存储根路径
|
||||
*/
|
||||
private String rootPath;
|
||||
|
||||
/**
|
||||
* 盘古模型预测数据存储路径
|
||||
*/
|
||||
private String pangu;
|
||||
private String panguDataPath;
|
||||
|
||||
/**
|
||||
* CRA40数据存储路径
|
||||
*/
|
||||
private String cra40;
|
||||
private String cra40DataPath;
|
||||
|
||||
/**
|
||||
* NCEP CFSv2数据存储路径
|
||||
*/
|
||||
private String ncep;
|
||||
private String ncepDataPath;
|
||||
|
||||
/**
|
||||
* graphcast模型预测数据存储路径
|
||||
*/
|
||||
private String graphcast;
|
||||
private String graphcastDataPath;
|
||||
|
||||
/**
|
||||
* t1h数据存储路径
|
||||
*/
|
||||
private String t1h;
|
||||
private String t1hDataPath;
|
||||
|
||||
/**
|
||||
* fnl数据存储路径
|
||||
*/
|
||||
private String fnl;
|
||||
private String fnlDataPath;
|
||||
|
||||
/**
|
||||
* WRF数据存储路径
|
||||
|
|
@ -55,14 +50,9 @@ public class SystemStorageProperties {
|
|||
private String cmaqPath;
|
||||
|
||||
/**
|
||||
* grib文件格式化脚本使用的python环境
|
||||
* python环境路径
|
||||
*/
|
||||
private String formatScriptPythonEnv;
|
||||
|
||||
/**
|
||||
* pangu的ai-models 安装地址
|
||||
*/
|
||||
private String panguEnvPath;
|
||||
private String pythonEnvPath;
|
||||
|
||||
/**
|
||||
* graphcast的ai-models 安装地址
|
||||
|
|
@ -83,4 +73,24 @@ public class SystemStorageProperties {
|
|||
* 盘古数据格式化脚本路径(格式化成flexpart能识别的)
|
||||
*/
|
||||
private String panguFormatScriptPath;
|
||||
|
||||
/**
|
||||
* graphcast数据格式化脚本路径(格式化成flexpart能识别的)
|
||||
*/
|
||||
private String graphcastFormatScriptPath;
|
||||
|
||||
/**
|
||||
* 预测模式为本地时,预测文件的临时存储路径
|
||||
*/
|
||||
private String forecastFileTmpPath;
|
||||
|
||||
/**
|
||||
* 天气预测docker容器http请求 baseurl
|
||||
*/
|
||||
private String weatherForecastBaseUrl;
|
||||
|
||||
/**
|
||||
* 天气预测docker容器http请求 url
|
||||
*/
|
||||
private String weatherForecastUri;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ import org.springframework.stereotype.Component;
|
|||
@ConfigurationProperties(prefix = "t1h-download")
|
||||
public class T1hDownloadProperties {
|
||||
|
||||
/**
|
||||
* 是否下载t1h数据开关
|
||||
*/
|
||||
private boolean downloadSwitch;
|
||||
|
||||
/**
|
||||
* 定时器cron
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
package org.jeecg.common.util;
|
||||
|
||||
import ucar.ma2.Array;
|
||||
import ucar.nc2.Attribute;
|
||||
import ucar.nc2.NetcdfFile;
|
||||
import ucar.nc2.NetcdfFiles;
|
||||
import ucar.nc2.Variable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Grib2TimeReader {
|
||||
|
||||
public static LocalDateTime readValidTime(String gribFile) throws Exception {
|
||||
try (NetcdfFile nc = NetcdfFiles.open(gribFile)) {
|
||||
Variable timeVar = nc.findVariable("time");
|
||||
if (timeVar == null) {
|
||||
throw new RuntimeException("未找到 time 变量,请先用 ncdump/ToolsUI 查看 GRIB 映射后的变量名");
|
||||
}
|
||||
|
||||
Attribute unitsAttr = timeVar.findAttribute("units");
|
||||
if (unitsAttr == null) {
|
||||
throw new RuntimeException("time 变量没有 units 属性");
|
||||
}
|
||||
|
||||
String units = unitsAttr.getStringValue();
|
||||
Array data = timeVar.read();
|
||||
|
||||
double offset = data.getDouble(data.getIndex());
|
||||
return parseTimeUnits(units, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static LocalDateTime parseTimeUnits(String units, double offset) {
|
||||
Pattern pattern = Pattern.compile("(?i)(\\w+)\\s+since\\s+(.+)");
|
||||
Matcher matcher = pattern.matcher(units.trim());
|
||||
|
||||
if (!matcher.matches()) {
|
||||
throw new RuntimeException("无法解析 time units: " + units);
|
||||
}
|
||||
|
||||
String unit = matcher.group(1).toLowerCase();
|
||||
String baseText = matcher.group(2).trim();
|
||||
|
||||
LocalDateTime baseTime;
|
||||
if (baseText.endsWith("Z")) {
|
||||
baseTime = OffsetDateTime.parse(baseText).toLocalDateTime();
|
||||
} else {
|
||||
baseText = baseText.replace(" ", "T");
|
||||
baseTime = LocalDateTime.parse(baseText);
|
||||
}
|
||||
|
||||
long seconds;
|
||||
if (unit.startsWith("hour")) {
|
||||
seconds = Math.round(offset * 3600);
|
||||
} else if (unit.startsWith("minute")) {
|
||||
seconds = Math.round(offset * 60);
|
||||
} else if (unit.startsWith("second")) {
|
||||
seconds = Math.round(offset);
|
||||
} else if (unit.startsWith("day")) {
|
||||
seconds = Math.round(offset * 86400);
|
||||
} else {
|
||||
throw new RuntimeException("不支持的时间单位: " + unit);
|
||||
}
|
||||
|
||||
return baseTime.plusSeconds(seconds);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
LocalDateTime validTime = readValidTime("C:\\Users\\cnndc\\Desktop\\pangu_20260605_12_00.grib2");
|
||||
|
||||
String result = validTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
System.out.println(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package org.jeecg.common.util;
|
||||
|
||||
import lombok.val;
|
||||
import ucar.ma2.*;
|
||||
import ucar.nc2.Attribute;
|
||||
import ucar.nc2.NetcdfFile;
|
||||
|
|
|
|||
|
|
@ -58,13 +58,17 @@ public class MybatisInterceptor implements Interceptor {
|
|||
}
|
||||
}
|
||||
// 注入创建时间
|
||||
if ("createTime".equals(field.getName()) && Date.class.equals(field.getType())) {
|
||||
if ("createTime".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object localCreateDate = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (localCreateDate == null || "".equals(localCreateDate)) {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, new Date());
|
||||
if (Date.class.equals(field.getType())){
|
||||
field.set(parameter, new Date());
|
||||
}else if (LocalDateTime.class.equals(field.getType())){
|
||||
field.set(parameter, LocalDateTime.now());
|
||||
}
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -109,9 +113,13 @@ public class MybatisInterceptor implements Interceptor {
|
|||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
if ("updateTime".equals(field.getName()) && Date.class.equals(field.getType())) {
|
||||
if ("updateTime".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, new Date());
|
||||
if (Date.class.equals(field.getType())){
|
||||
field.set(parameter, new Date());
|
||||
}else if (LocalDateTime.class.equals(field.getType())){
|
||||
field.set(parameter, LocalDateTime.now());
|
||||
}
|
||||
field.setAccessible(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import org.jeecg.common.validgroup.UpdateGroup;
|
|||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
|
|
@ -57,7 +58,7 @@ public class SourceRebuildMonitoringData implements Serializable {
|
|||
@NotNull(message = "测量停止时间不能为空", groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@TableField(value = "collect_stop")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date collectStop;
|
||||
private LocalDateTime collectStop;
|
||||
|
||||
/**
|
||||
* 活度浓度
|
||||
|
|
@ -89,5 +90,5 @@ public class SourceRebuildMonitoringData implements Serializable {
|
|||
@TableField(value = "create_time")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ import org.jeecg.common.validgroup.InsertGroup;
|
|||
import org.jeecg.common.validgroup.UpdateGroup;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +40,7 @@ public class SourceRebuildTask implements Serializable {
|
|||
private String taskName;
|
||||
|
||||
/**
|
||||
* 任务状态-1执行错误,0-未开始,1进行中,2已完成
|
||||
* 任务状态-1执行错误,0-未开始,1等待中,2进行中,3已完成,4检查未通过
|
||||
*/
|
||||
@Null(message = "任务状态必须为空",groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@TableField(value = "task_status")
|
||||
|
|
@ -85,7 +87,7 @@ public class SourceRebuildTask implements Serializable {
|
|||
@NotNull(message = "srs时间周期-开始日期不能为空",groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@TableField(value = "srs_start_time")
|
||||
private Date srsStartTime;
|
||||
private LocalDate srsStartTime;
|
||||
|
||||
/**
|
||||
* srs时间周期-结束日期
|
||||
|
|
@ -93,7 +95,7 @@ public class SourceRebuildTask implements Serializable {
|
|||
@NotNull(message = "srs时间周期-结束日期不能为空",groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@TableField(value = "srs_end_time")
|
||||
private Date srsEndTime;
|
||||
private LocalDate srsEndTime;
|
||||
|
||||
/**
|
||||
* 释放类型,1-持续释放,2-均匀释放,3-非均匀释放
|
||||
|
|
@ -126,14 +128,14 @@ public class SourceRebuildTask implements Serializable {
|
|||
*/
|
||||
@TableField(value = "release_start_time")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
private Date releaseStartTime;
|
||||
private LocalDateTime releaseStartTime;
|
||||
|
||||
/**
|
||||
* 释放源结束释放时间
|
||||
*/
|
||||
@TableField(value = "release_end_time")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
private Date releaseEndTime;
|
||||
private LocalDateTime releaseEndTime;
|
||||
|
||||
/**
|
||||
* 源强
|
||||
|
|
@ -175,25 +177,13 @@ public class SourceRebuildTask implements Serializable {
|
|||
@TableField(value = "result_address")
|
||||
private String resultAddress;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(value = "create_by")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(value = "create_time")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@TableField(value = "update_by")
|
||||
private String updateBy;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
|
|
@ -201,5 +191,11 @@ public class SourceRebuildTask implements Serializable {
|
|||
@TableField(value = "update_time")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 任务置顶(0未置顶,1置顶)
|
||||
*/
|
||||
@TableField(value = "top_task")
|
||||
private Integer topTask;
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import lombok.Data;
|
|||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
|
|
@ -36,7 +37,7 @@ public class SourceRebuildTaskLog implements Serializable {
|
|||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 日志内容
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ public class TransportTask{
|
|||
private Integer releaseDataSource;
|
||||
|
||||
/**
|
||||
* 任务置顶
|
||||
* 任务置顶(0未置顶,1置顶)
|
||||
*/
|
||||
@TableField(value = "top_task")
|
||||
private Integer topTask;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class TransportTaskBackwardChild {
|
|||
@TableField(value = "create_time")
|
||||
@JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 样品id
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
|||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -53,7 +55,7 @@ public class TransportTaskForwardChild {
|
|||
@TableField(value = "create_time")
|
||||
@JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 正演子表信息
|
||||
|
|
|
|||
|
|
@ -63,6 +63,6 @@ public class TransportTaskForwardRelease {
|
|||
@TableField(value = "create_time")
|
||||
@JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
|||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
|
|
@ -32,6 +34,6 @@ public class TransportTaskForwardSpecies {
|
|||
@TableField(value = "create_time")
|
||||
@JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
|||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
|
|
@ -40,5 +41,5 @@ public class TransportTaskLog implements Serializable {
|
|||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
|
|
@ -5,12 +5,9 @@ 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.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 气象数据文件表
|
||||
|
|
@ -22,9 +19,14 @@ public class WeatherData implements Serializable {
|
|||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "ID")
|
||||
private String id;
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 预测任务产生的数据有taskId
|
||||
*/
|
||||
@TableField(value = "task_id")
|
||||
private Integer taskId;
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
|
|
@ -32,12 +34,6 @@ public class WeatherData implements Serializable {
|
|||
@TableField(value = "file_name")
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
@TableField(value = "file_size")
|
||||
private Double fileSize;
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
|
|
@ -62,30 +58,18 @@ public class WeatherData implements Serializable {
|
|||
@TableField(value = "file_path")
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 对于盘古模型,graphcast模型预测结果文件,格式化后的数据存储路径
|
||||
*/
|
||||
@TableField(value = "format_file_path")
|
||||
private String formatFilePath;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@TableField(value = "create_time")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 文件MD5值
|
||||
*/
|
||||
@TableField(value = "md5_value")
|
||||
private String md5Value;
|
||||
|
||||
/**
|
||||
* 总分片数
|
||||
*/
|
||||
@TableField(value = "share_total")
|
||||
private Integer shareTotal;
|
||||
|
||||
/**
|
||||
* 分片索引
|
||||
*/
|
||||
@TableField(value = "share_index")
|
||||
private Integer shareIndex;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 日期批次
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package org.jeecg.modules.base.entity;
|
||||
|
||||
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.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 关联气象数据文件日志表
|
||||
*/
|
||||
@Data
|
||||
@TableName("stas_weather_linked_data_log")
|
||||
public class WeatherLinkedDataLog implements Serializable {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
@Schema(description = "ID")
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 数据类型(3-cra40,4-ncep,5-fnl)
|
||||
*/
|
||||
@TableField(value = "data_type")
|
||||
private Integer dataType;
|
||||
|
||||
/**
|
||||
* 任务运行过程日志
|
||||
*/
|
||||
@TableField(value = "log_content")
|
||||
private String logContent;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ import org.jeecg.common.validgroup.UpdateGroup;
|
|||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Date;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 天气预测任务表
|
||||
|
|
@ -29,9 +29,9 @@ public class WeatherTask{
|
|||
*/
|
||||
@Null(message = "ID必须为空",groups = { InsertGroup.class})
|
||||
@NotNull(message = "ID不能为空",groups = { UpdateGroup.class})
|
||||
@TableId(type = IdType.INPUT)
|
||||
@TableId(type = IdType.AUTO)
|
||||
@Schema(description = "ID")
|
||||
private java.lang.String id;
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
|
|
@ -41,7 +41,7 @@ public class WeatherTask{
|
|||
private String taskName;
|
||||
|
||||
/**
|
||||
* 任务状态(-1失败,0-未开始,1-运行中,2-已完成)
|
||||
* 任务状态(-1失败,0-未开始,1-等待中,2-执行中,3-已完成)
|
||||
*/
|
||||
@Null(message = "任务状态必须为空",groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@TableField(value = "task_status")
|
||||
|
|
@ -113,7 +113,7 @@ public class WeatherTask{
|
|||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
|
|
@ -125,6 +125,12 @@ public class WeatherTask{
|
|||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 任务置顶(0未置顶,1置顶)
|
||||
*/
|
||||
@TableField(value = "top_task")
|
||||
private Integer topTask;
|
||||
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
|||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
|
|
@ -20,15 +21,15 @@ public class WeatherTaskLog implements Serializable {
|
|||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@TableId(type = IdType.AUTO)
|
||||
@Schema(description = "ID")
|
||||
private String id;
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 任务表主键
|
||||
*/
|
||||
@TableField(value = "task_id")
|
||||
private String taskId;
|
||||
private Integer taskId;
|
||||
|
||||
/**
|
||||
* 任务运行过程日志
|
||||
|
|
@ -40,6 +41,6 @@ public class WeatherTaskLog implements Serializable {
|
|||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package org.jeecg.modules.base.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.base.entity.WeatherLinkedDataLog;
|
||||
|
||||
public interface WeatherLinkedDataLogMapper extends BaseMapper<WeatherLinkedDataLog> {
|
||||
}
|
||||
|
|
@ -15,6 +15,8 @@
|
|||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<rEngine.version>2.1.0</rEngine.version>
|
||||
<rserve.version>1.8.1</rserve.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
@ -23,5 +25,19 @@
|
|||
<artifactId>jeecg-boot-base-core</artifactId>
|
||||
<version>${jeecgboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.rosuda.REngine</groupId>
|
||||
<artifactId>REngine</artifactId>
|
||||
<version>${rEngine.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.rosuda.REngine</groupId>
|
||||
<artifactId>Rserve</artifactId>
|
||||
<version>${rserve.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
package org.jeecg.jsch;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.jcraft.jsch.*;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* ssh远程操作工具
|
||||
*/
|
||||
@Slf4j
|
||||
public class JSchRemoteRunner {
|
||||
|
||||
private final JSch jsch = new JSch();
|
||||
|
||||
@Getter
|
||||
private Session session = null;
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
* @param host
|
||||
* @param port
|
||||
* @param username
|
||||
* @param password
|
||||
* @throws JSchException
|
||||
*/
|
||||
public void login(String host, int port, String username, String password) throws JSchException {
|
||||
Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking","no");
|
||||
config.put("PreferredAuthentications", "password");//只用秘密登录
|
||||
session = jsch.getSession(username, host, port);
|
||||
session.setUserInfo(new UserInfo() {
|
||||
@Override
|
||||
public String getPassphrase() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
@Override
|
||||
public boolean promptPassword(String s) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean promptPassphrase(String s) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean promptYesNo(String s) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public void showMessage(String message) {
|
||||
log.info("SSH Message: {}", message);
|
||||
}
|
||||
});
|
||||
session.setConfig(config);
|
||||
session.setServerAliveInterval(30);
|
||||
session.setServerAliveCountMax(3);
|
||||
session.setTimeout(0);
|
||||
session.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入文件内容,如果文件不存在则创建
|
||||
* @param path
|
||||
* @param content
|
||||
*/
|
||||
public void writeFile(String path, String content) {
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect();
|
||||
sftp.put(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)),path,ChannelSftp.OVERWRITE);
|
||||
} catch (JSchException | SftpException e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (Objects.nonNull(sftp)) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空目录下所有文件
|
||||
* @param path
|
||||
*/
|
||||
public void clearDir(String path){
|
||||
if (StrUtil.isBlank(path) || StrUtil.equals("/", path)){
|
||||
throw new RuntimeException("需要清空的目录路径不能为空");
|
||||
}
|
||||
ChannelExec channel = null;
|
||||
try{
|
||||
//打开一个执行通道
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
String fullCommand = "rm -f "+path+"/*";
|
||||
channel.setCommand(fullCommand);
|
||||
// 连接通道
|
||||
channel.connect();
|
||||
}catch(JSchException e){
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (Objects.nonNull(channel)) {
|
||||
channel.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断目录是否存在
|
||||
* @param dirPath
|
||||
* @return
|
||||
*/
|
||||
public boolean dirExist(String dirPath){
|
||||
if (StrUtil.isBlank(dirPath)){
|
||||
return false;
|
||||
}
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect();
|
||||
SftpATTRS stat = sftp.stat(dirPath);
|
||||
return stat != null && stat.isDir();
|
||||
} catch (SftpException e) {
|
||||
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
|
||||
return false;
|
||||
}
|
||||
throw new RuntimeException("SFTP检查目录失败:",e);
|
||||
} catch (JSchException e) {
|
||||
throw new RuntimeException("ssh连接异常:",e);
|
||||
} finally {
|
||||
if (Objects.nonNull(sftp)) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断目录是否存在
|
||||
* @param dirPaths
|
||||
* @return
|
||||
*/
|
||||
public List<String> getNotExistDirs(List<String> dirPaths){
|
||||
if (CollUtil.isEmpty(dirPaths)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
List<String> notExistDirs = new ArrayList<>();
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect();
|
||||
for (String dirPath : dirPaths) {
|
||||
try {
|
||||
SftpATTRS stat = sftp.stat(dirPath);
|
||||
if (stat == null || !stat.isDir()){
|
||||
notExistDirs.add(dirPath);
|
||||
}
|
||||
} catch (SftpException e) {
|
||||
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
|
||||
log.warn("{}文件不存在",dirPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return notExistDirs;
|
||||
} catch (JSchException e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (Objects.nonNull(sftp)) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取远程主机指定目录下的所有匹配规则的文件
|
||||
* @param path
|
||||
* @param matchingSuffix
|
||||
* @return
|
||||
*/
|
||||
public List<File> listFiles(String path,String matchingSuffix){
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect();
|
||||
Vector<ChannelSftp.LsEntry> fileList = sftp.ls(path);
|
||||
List<File> matchedFiles = new ArrayList<>();
|
||||
for (ChannelSftp.LsEntry entry : fileList) {
|
||||
String fileName = entry.getFilename();
|
||||
if (fileName.endsWith(matchingSuffix)) {
|
||||
matchedFiles.add(new File(path+File.separator+fileName));
|
||||
}
|
||||
}
|
||||
return CollUtil.emptyIfNull(matchedFiles);
|
||||
} catch (JSchException | SftpException e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (Objects.nonNull(sftp)) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取远程主机指定目录下的所有匹配规则的文件
|
||||
* @param paths
|
||||
* @param matchingSuffix
|
||||
* @return
|
||||
*/
|
||||
public List<String> listFiles(List<String> paths,String matchingSuffix){
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect();
|
||||
List<String> matchedFiles = new ArrayList<>();
|
||||
for (String path : paths) {
|
||||
Vector<ChannelSftp.LsEntry> fileList = sftp.ls(path);
|
||||
for (ChannelSftp.LsEntry entry : fileList) {
|
||||
String fileName = entry.getFilename();
|
||||
if (fileName.endsWith(matchingSuffix)) {
|
||||
matchedFiles.add(path+File.separator+fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CollUtil.emptyIfNull(matchedFiles);
|
||||
} catch (JSchException | SftpException e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (Objects.nonNull(sftp)) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录,可多层创建
|
||||
* @param path
|
||||
*/
|
||||
public void mkdir(String path){
|
||||
ChannelExec channelExec = null;
|
||||
try {
|
||||
channelExec = (ChannelExec) session.openChannel("exec");
|
||||
channelExec.setCommand("mkdir -p "+path);
|
||||
channelExec.connect();
|
||||
while (!channelExec.isClosed()) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (Objects.nonNull(channelExec)){
|
||||
channelExec.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件
|
||||
* @param srcPath
|
||||
* @param destPath
|
||||
*/
|
||||
public void moveFiles(String srcPath,String destPath){
|
||||
if (StrUtil.isBlank(srcPath) || StrUtil.equals("/", srcPath) || StrUtil.isBlank(destPath) || StrUtil.equals("/", destPath)){
|
||||
throw new RuntimeException("需要移动的目录路径不能为空");
|
||||
}
|
||||
ChannelExec channelExec = null;
|
||||
try {
|
||||
channelExec = (ChannelExec) session.openChannel("exec");
|
||||
channelExec.setCommand("mv "+srcPath+" "+destPath);
|
||||
channelExec.connect();
|
||||
while (!channelExec.isClosed()) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (Objects.nonNull(channelExec)){
|
||||
channelExec.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭会话
|
||||
*/
|
||||
public void close(){
|
||||
if (Objects.nonNull(session)) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package org.jeecg.rebuild.consumer;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.SourceRebuildTaskStatusEnum;
|
||||
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SourceRebuildProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
import org.jeecg.modules.base.mapper.*;
|
||||
import org.jeecg.rebuild.consumer.china.AbstractTaskMsgHandler;
|
||||
import org.jeecg.rebuild.consumer.china.Server11TaskHandler;
|
||||
import org.jeecg.rebuild.service.SourceRebuildMonitoringDataService;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskLogService;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 源项重建任务控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class RebuildTaskConsumerHandler {
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
private final SourceRebuildTaskLogService sourceRebuildTaskLogService;
|
||||
private final SourceRebuildProperties sourceRebuildProperties;
|
||||
private final SourceRebuildTaskService sourceRebuildTaskService;
|
||||
private final ServerProperties serverProperties;
|
||||
private final SourceRebuildMonitoringDataService monitoringDataService;
|
||||
|
||||
public void startConsumerThread(){
|
||||
MessageConsumerThread messageConsumerThread = new MessageConsumerThread();
|
||||
messageConsumerThread.setName("rebuild-task-thread");
|
||||
messageConsumerThread.start();
|
||||
log.info("启动源项重建任务消费线程----------------------");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理源项重建任务
|
||||
*/
|
||||
private void handlerTask(SourceRebuildTask message){
|
||||
AbstractTaskMsgHandler taskMsgHandler = new Server11TaskHandler();
|
||||
taskMsgHandler.init(
|
||||
sourceRebuildTaskLogService,
|
||||
sourceRebuildProperties,
|
||||
sourceRebuildTaskService,
|
||||
monitoringDataService,
|
||||
serverProperties,
|
||||
redisUtil);
|
||||
taskMsgHandler.handler(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取等待中的任务
|
||||
* @return
|
||||
*/
|
||||
private SourceRebuildTask getTopMessage(){
|
||||
LambdaQueryWrapper<SourceRebuildTask> topQueryWrapper = new LambdaQueryWrapper<>();
|
||||
topQueryWrapper.eq(SourceRebuildTask::getTaskStatus, SourceRebuildTaskStatusEnum.WAITING.getValue());
|
||||
topQueryWrapper.orderByDesc(SourceRebuildTask::getTopTask);
|
||||
topQueryWrapper.orderByDesc(SourceRebuildTask::getUpdateTime);
|
||||
topQueryWrapper.last("LIMIT 1");
|
||||
SourceRebuildTask rebuildTask = sourceRebuildTaskService.getOne(topQueryWrapper);
|
||||
//如果没有要置顶执行的任务,则按照普通创建时间排序查询
|
||||
if(Objects.isNull(rebuildTask)){
|
||||
LambdaQueryWrapper<SourceRebuildTask> timeQueryWrapper = new LambdaQueryWrapper<>();
|
||||
timeQueryWrapper.eq(SourceRebuildTask::getTaskStatus, SourceRebuildTaskStatusEnum.WAITING.getValue());
|
||||
timeQueryWrapper.orderByAsc(SourceRebuildTask::getCreateTime);
|
||||
timeQueryWrapper.last("LIMIT 1");
|
||||
rebuildTask = sourceRebuildTaskService.getOne(timeQueryWrapper);
|
||||
}
|
||||
return rebuildTask;
|
||||
}
|
||||
|
||||
private class MessageConsumerThread extends Thread{
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
//获取本机项数据,如果为空,表示本机没有任务在运行
|
||||
boolean flag = redisUtil.hHasKey(CommonConstant.HOST_TASK_STATE,CommonConstant.BUILD_TASK_STATE_PRE+serverProperties.getHost());
|
||||
if (!flag) {
|
||||
SourceRebuildTask topMessage = getTopMessage();
|
||||
if (Objects.nonNull(topMessage)) {
|
||||
handlerTask(topMessage);
|
||||
}
|
||||
}
|
||||
TimeUnit.SECONDS.sleep(60);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("执行源项重建任务数据异常,原因为:",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.jeecg.rebuild.consumer.china;
|
||||
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 执行链路
|
||||
*/
|
||||
public abstract class AbstractChain {
|
||||
|
||||
/**
|
||||
* 上一任处理链
|
||||
*/
|
||||
@Setter
|
||||
protected AbstractTaskMsgHandler previous;
|
||||
|
||||
/**
|
||||
* 下一任处理链
|
||||
*/
|
||||
@Setter
|
||||
protected AbstractTaskMsgHandler next;
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
protected abstract void setChina();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package org.jeecg.rebuild.consumer.china;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SourceRebuildProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
import org.jeecg.rebuild.service.SourceRebuildMonitoringDataService;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskLogService;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskService;
|
||||
import org.jeecg.rebuild.task.SourceRebuildTaskExec;
|
||||
|
||||
/**
|
||||
* 处理任务策略父类
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractTaskMsgHandler extends AbstractChain {
|
||||
|
||||
protected RedisUtil redisUtil;
|
||||
protected SourceRebuildTaskLogService sourceRebuildTaskLogService;
|
||||
protected SourceRebuildProperties sourceRebuildProperties;
|
||||
protected SourceRebuildTaskService sourceRebuildTaskService;
|
||||
protected SourceRebuildMonitoringDataService monitoringDataService;
|
||||
protected ServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* 初始化bean引用
|
||||
*/
|
||||
public void init(SourceRebuildTaskLogService sourceRebuildTaskLogService,
|
||||
SourceRebuildProperties sourceRebuildProperties,
|
||||
SourceRebuildTaskService sourceRebuildTaskService,
|
||||
SourceRebuildMonitoringDataService monitoringDataService,
|
||||
ServerProperties serverProperties,
|
||||
RedisUtil redisUtil) {
|
||||
this.sourceRebuildTaskLogService = sourceRebuildTaskLogService;
|
||||
this.sourceRebuildProperties = sourceRebuildProperties;
|
||||
this.sourceRebuildTaskService = sourceRebuildTaskService;
|
||||
this.monitoringDataService = monitoringDataService;
|
||||
this.serverProperties = serverProperties;
|
||||
this.redisUtil = redisUtil;
|
||||
this.setChina();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化下个节点
|
||||
* @param serverProperties
|
||||
*/
|
||||
protected void initNext(ServerProperties serverProperties){
|
||||
this.serverProperties = serverProperties;
|
||||
this.setChina();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
public abstract void handler(SourceRebuildTask message);
|
||||
|
||||
/**
|
||||
* 运行任务
|
||||
* @param rebuildTask
|
||||
*/
|
||||
protected void runTask(SourceRebuildTask rebuildTask,String ip){
|
||||
try {
|
||||
redisUtil.hset(CommonConstant.HOST_TASK_STATE,CommonConstant.BUILD_TASK_STATE_PRE+serverProperties.getHost(),rebuildTask.getId());
|
||||
boolean flag = false;
|
||||
SourceRebuildTaskExec taskExec = new SourceRebuildTaskExec();
|
||||
taskExec.init(
|
||||
rebuildTask,
|
||||
sourceRebuildTaskLogService,
|
||||
sourceRebuildProperties,
|
||||
sourceRebuildTaskService,
|
||||
serverProperties,
|
||||
monitoringDataService);
|
||||
taskExec.setName("source-rebuild-task");
|
||||
flag = taskExec.checkTask();
|
||||
if (flag){
|
||||
log.info("任务:{},检查通过,当前在主机:{}上执行。", rebuildTask.getTaskName(),ip);
|
||||
taskExec.start();
|
||||
}else {
|
||||
sourceRebuildTaskService.setInpectionFailed(rebuildTask);
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("检查任务或开启任务执行线程出现错误",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package org.jeecg.rebuild.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
|
||||
/**
|
||||
* 处理重建任务
|
||||
*/
|
||||
public class Server11TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
AbstractTaskMsgHandler taskHandler = new Server12TaskHandler();
|
||||
taskHandler.initNext(super.serverProperties);
|
||||
taskHandler.setPrevious(this);
|
||||
super.setNext(taskHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(SourceRebuildTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp11())){
|
||||
super.runTask(message,serverProperties.getIp11());
|
||||
}else {
|
||||
super.next.handler(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.jeecg.rebuild.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
|
||||
/**
|
||||
* 处理重建任务
|
||||
*/
|
||||
public class Server12TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
AbstractTaskMsgHandler taskHandler = new Server13TaskHandler();
|
||||
taskHandler.initNext(super.serverProperties);
|
||||
taskHandler.setPrevious(this);
|
||||
super.setNext(taskHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(SourceRebuildTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp12())){
|
||||
super.runTask(message,serverProperties.getIp12());
|
||||
}else {
|
||||
super.next.handler(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.jeecg.rebuild.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
|
||||
/**
|
||||
* 处理重建任务
|
||||
*/
|
||||
public class Server13TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
AbstractTaskMsgHandler taskHandler = new Server14TaskHandler();
|
||||
taskHandler.initNext(super.serverProperties);
|
||||
taskHandler.setPrevious(this);
|
||||
super.setNext(taskHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(SourceRebuildTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp13())){
|
||||
super.runTask(message,serverProperties.getIp13());
|
||||
}else {
|
||||
super.next.handler(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.jeecg.rebuild.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
|
||||
/**
|
||||
* 处理重建任务
|
||||
*/
|
||||
public class Server14TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(SourceRebuildTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp14())){
|
||||
super.runTask(message,serverProperties.getIp14());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.jeecg.rebuild.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildMonitoringData;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 源项重建任务监测数据
|
||||
*/
|
||||
public interface SourceRebuildMonitoringDataService extends IService<SourceRebuildMonitoringData> {
|
||||
|
||||
/**
|
||||
* 获取任务所属监测数据
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
List<SourceRebuildMonitoringData> getTaskMonitoringData(Integer taskId);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package org.jeecg.rebuild.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTaskLog;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 源项重建任务日志数据
|
||||
*/
|
||||
public interface SourceRebuildTaskLogService extends IService<SourceRebuildTaskLog> {
|
||||
|
||||
/**
|
||||
* 保存源项重建任务运行过程日志
|
||||
* @param sourceRebuildTaskLog
|
||||
*/
|
||||
void cteate(SourceRebuildTaskLog sourceRebuildTaskLog);
|
||||
|
||||
/**
|
||||
* 删除任务运行过程日志
|
||||
* @param taskId
|
||||
*/
|
||||
void delete(Integer taskId);
|
||||
|
||||
/**
|
||||
* 批量保存日志
|
||||
* @param logs
|
||||
*/
|
||||
void batchCteate(List<SourceRebuildTaskLog> logs);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.jeecg.rebuild.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
|
||||
/**
|
||||
* 源项重建任务
|
||||
*/
|
||||
public interface SourceRebuildTaskService extends IService<SourceRebuildTask> {
|
||||
|
||||
/**
|
||||
* 修改任务状态
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskStatus(Integer taskId, Integer taskStatus);
|
||||
|
||||
/**
|
||||
* 任务耗时
|
||||
* @param taskId
|
||||
* @param minute
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskTimeConsuming(Integer taskId, Double minute,Integer taskStatus);
|
||||
|
||||
/**
|
||||
* 设置检查失败
|
||||
* @param task
|
||||
*/
|
||||
void setInpectionFailed(SourceRebuildTask task);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.jeecg.rebuild.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildMonitoringData;
|
||||
import org.jeecg.modules.base.mapper.SourceRebuildMonitoringDataMapper;
|
||||
import org.jeecg.rebuild.service.SourceRebuildMonitoringDataService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 源项重建任务监测数据
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SourceRebuildMonitoringDataServiceImpl extends ServiceImpl<SourceRebuildMonitoringDataMapper, SourceRebuildMonitoringData> implements SourceRebuildMonitoringDataService {
|
||||
|
||||
/**
|
||||
* 获取任务所属监测数据
|
||||
*
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<SourceRebuildMonitoringData> getTaskMonitoringData(Integer taskId) {
|
||||
LambdaQueryWrapper<SourceRebuildMonitoringData> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SourceRebuildMonitoringData::getTaskId, taskId);
|
||||
queryWrapper.orderByAsc(SourceRebuildMonitoringData::getCollectStop);
|
||||
return this.baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.jeecg.rebuild.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTaskLog;
|
||||
import org.jeecg.modules.base.mapper.SourceRebuildTaskLogMapper;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskLogService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class SourceRebuildTaskLogServiceImpl extends ServiceImpl<SourceRebuildTaskLogMapper, SourceRebuildTaskLog> implements SourceRebuildTaskLogService {
|
||||
|
||||
/**
|
||||
* 保存源项重建任务运行过程日志
|
||||
*
|
||||
* @param sourceRebuildTaskLog
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void cteate(SourceRebuildTaskLog sourceRebuildTaskLog) {
|
||||
this.baseMapper.insert(sourceRebuildTaskLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务运行过程日志
|
||||
*
|
||||
* @param taskId
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void delete(Integer taskId) {
|
||||
LambdaQueryWrapper<SourceRebuildTaskLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SourceRebuildTaskLog::getTaskId, taskId);
|
||||
this.baseMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存日志
|
||||
*
|
||||
* @param logs
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void batchCteate(List<SourceRebuildTaskLog> logs) {
|
||||
this.saveBatch(logs);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package org.jeecg.rebuild.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.SourceRebuildTaskStatusEnum;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
import org.jeecg.modules.base.mapper.SourceRebuildTaskMapper;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 源项重建任务
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SourceRebuildTaskServiceImpl extends ServiceImpl<SourceRebuildTaskMapper, SourceRebuildTask> implements SourceRebuildTaskService {
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* 修改任务状态
|
||||
*
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatus(Integer taskId, Integer taskStatus) {
|
||||
SourceRebuildTask sourceRebuildTask = this.baseMapper.selectById(taskId);
|
||||
sourceRebuildTask.setTaskStatus(taskStatus);
|
||||
this.updateById(sourceRebuildTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务耗时
|
||||
*
|
||||
* @param taskId
|
||||
* @param minute
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskTimeConsuming(Integer taskId, Double minute,Integer taskStatus) {
|
||||
SourceRebuildTask sourceRebuildTask = this.baseMapper.selectById(taskId);
|
||||
sourceRebuildTask.setTimeConsuming(minute);
|
||||
sourceRebuildTask.setTaskStatus(taskStatus);
|
||||
this.updateById(sourceRebuildTask);
|
||||
redisUtil.hdel(CommonConstant.HOST_TASK_STATE,CommonConstant.BUILD_TASK_STATE_PRE+serverProperties.getHost());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置检查失败
|
||||
* @param task
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void setInpectionFailed(SourceRebuildTask task) {
|
||||
task.setTaskStatus(SourceRebuildTaskStatusEnum.INSPECTION_FAILED.getValue());
|
||||
this.baseMapper.updateById(task);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,26 @@
|
|||
package org.jeecg.task;
|
||||
package org.jeecg.rebuild.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.jeecg.common.constant.enums.SourceRebuildReleaseSourceEnum;
|
||||
import org.jeecg.common.constant.enums.SourceRebuildTaskStatusEnum;
|
||||
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SourceRebuildProperties;
|
||||
import org.jeecg.jsch.JSchRemoteRunner;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildMonitoringData;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTaskLog;
|
||||
import org.jeecg.service.SourceRebuildTaskLogService;
|
||||
import org.jeecg.service.SourceRebuildTaskService;
|
||||
import org.jeecg.rebuild.service.SourceRebuildMonitoringDataService;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskLogService;
|
||||
import org.jeecg.rebuild.service.SourceRebuildTaskService;
|
||||
import org.jeecg.transport.flexparttask.ProgressEvent;
|
||||
import org.jeecg.transport.flexparttask.ProgressQueue;
|
||||
import org.rosuda.REngine.REXP;
|
||||
import org.rosuda.REngine.REXPDouble;
|
||||
import org.rosuda.REngine.REXPInteger;
|
||||
|
|
@ -25,32 +34,45 @@ import java.math.RoundingMode;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
public class SourceRebuildTaskExec extends Thread{
|
||||
|
||||
private List<SourceRebuildMonitoringData> taskMonitoringDatas;
|
||||
private SourceRebuildTaskLogService sourceRebuildTaskLogService;
|
||||
private SourceRebuildTask sourceRebuildTask;
|
||||
private SourceRebuildProperties sourceRebuildProperties;
|
||||
private SourceRebuildTaskService sourceRebuildTaskService;
|
||||
private ServerProperties serverProperties;
|
||||
private SourceRebuildMonitoringDataService monitoringDataService;
|
||||
private List<SourceRebuildMonitoringData> taskMonitoringDatas;
|
||||
private List<String> srsFilesPath;
|
||||
private JSchRemoteRunner jschRemoteRunner = null;
|
||||
private boolean taskRunError = false;
|
||||
private String errorFlag = "__R_ERROR__";
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public void init(List<SourceRebuildMonitoringData> taskMonitoringDatas,
|
||||
SourceRebuildTask sourceRebuildTask,
|
||||
public void init(SourceRebuildTask sourceRebuildTask,
|
||||
SourceRebuildTaskLogService sourceRebuildTaskLogService,
|
||||
SourceRebuildProperties sourceRebuildProperties,
|
||||
SourceRebuildTaskService sourceRebuildTaskService){
|
||||
this.taskMonitoringDatas = taskMonitoringDatas;
|
||||
SourceRebuildTaskService sourceRebuildTaskService,
|
||||
ServerProperties serverProperties,
|
||||
SourceRebuildMonitoringDataService monitoringDataService){
|
||||
this.sourceRebuildTask = sourceRebuildTask;
|
||||
this.sourceRebuildTaskLogService = sourceRebuildTaskLogService;
|
||||
this.sourceRebuildProperties = sourceRebuildProperties;
|
||||
this.sourceRebuildTaskService = sourceRebuildTaskService;
|
||||
this.serverProperties = serverProperties;
|
||||
this.monitoringDataService = monitoringDataService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -58,6 +80,92 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
this.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验任务
|
||||
* @return
|
||||
*/
|
||||
public boolean checkTask(){
|
||||
boolean flag = true;
|
||||
try {
|
||||
List<String> msgList = new ArrayList<>();
|
||||
//校验监测数据
|
||||
List<SourceRebuildMonitoringData> taskMonitoringDatas = this.monitoringDataService.getTaskMonitoringData(sourceRebuildTask.getId());
|
||||
if(CollectionUtils.isEmpty(taskMonitoringDatas)){
|
||||
flag = false;
|
||||
msgList.add("监测数据为空,请补充监测数据");
|
||||
}else {
|
||||
this.taskMonitoringDatas = taskMonitoringDatas;
|
||||
jschRemoteRunner = new JSchRemoteRunner();
|
||||
//登录ssh
|
||||
jschRemoteRunner.login(this.serverProperties.getHost(),this.serverProperties.getPort(),
|
||||
this.serverProperties.getUsername(),this.serverProperties.getPassword());
|
||||
//校验SRS目录是否存在
|
||||
List<String> srsDirsPath = new ArrayList<>();
|
||||
for (SourceRebuildMonitoringData monitoringData : taskMonitoringDatas) {
|
||||
StringBuilder srsPath = new StringBuilder();
|
||||
srsPath.append(sourceRebuildProperties.getSrsFilePath());
|
||||
// srsPath.append(File.separator);
|
||||
srsPath.append("/");
|
||||
srsPath.append(DateUtil.format(monitoringData.getCollectStop(),"yyyyMMdd"));
|
||||
// srsPath.append(File.separator);
|
||||
srsPath.append("/");
|
||||
srsPath.append(sourceRebuildProperties.getXeGZFileDirSign());
|
||||
srsDirsPath.add(srsPath.toString());
|
||||
}
|
||||
//获取不存在的srs目录
|
||||
List<String> notExistDirs = jschRemoteRunner.getNotExistDirs(srsDirsPath);
|
||||
if (CollUtil.isNotEmpty(notExistDirs)){
|
||||
flag = false;
|
||||
String[] notExistDirArr = new String[notExistDirs.size()];
|
||||
for(int i=0;i<notExistDirs.size();i++){
|
||||
notExistDirArr[i] = "SRS目录不存在:"+notExistDirs.get(i);
|
||||
}
|
||||
this.batchGenerateLog(notExistDirArr);
|
||||
}else {
|
||||
//如果SRS目录都存在找到对应的srs文件,时间相差6小时内都可以
|
||||
List<String> notExistFiles = new ArrayList<>();
|
||||
List<String> srsFilesPath = new ArrayList<>();
|
||||
List<String> filesPath = jschRemoteRunner.listFiles(srsDirsPath, ".srm.gz");
|
||||
taskMonitoringDatas.forEach(data->{
|
||||
Map<Long,String> srsFilePathMap = new HashMap<>();
|
||||
for (String filePath : filesPath){
|
||||
String fileName = filePath.substring(filePath.lastIndexOf("/")+1);
|
||||
String[] fileNameSplit = fileName.split("\\.");
|
||||
LocalDateTime date = LocalDateTime.parse(fileNameSplit[2], DateTimeFormatter.ofPattern("yyyyMMddHH"));
|
||||
if(data.getStation().equals(fileNameSplit[0])){
|
||||
Duration duration = Duration.between(data.getCollectStop(), date).abs();
|
||||
long hours = duration.toHours();
|
||||
if (hours <= 6){
|
||||
String relativePath = filePath.substring(sourceRebuildProperties.getSrsFilePath().length()+1);
|
||||
srsFilePathMap.put(hours,relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(srsFilePathMap)){
|
||||
Long min = CollUtil.min(srsFilePathMap.keySet());
|
||||
srsFilesPath.add(srsFilePathMap.get(min)+"\n");
|
||||
}else {
|
||||
String collectStopStr = DateUtil.format(data.getCollectStop(),"yyyy-MM-dd HH:mm:ss");
|
||||
notExistFiles.add(collectStopStr+"样品对应的SRS文件不存在");
|
||||
}
|
||||
});
|
||||
//如果notExistFiles为空说明样品对应的srs文件都找到了
|
||||
if (CollUtil.isEmpty(notExistFiles)){
|
||||
this.srsFilesPath = srsFilesPath;
|
||||
}else {
|
||||
flag = false;
|
||||
this.batchGenerateLog(notExistFiles.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (Exception e){
|
||||
this.sourceRebuildTaskService.updateTaskStatus(this.sourceRebuildTask.getId(), SourceRebuildTaskStatusEnum.INSPECTION_FAILED.getValue());
|
||||
log.error("源项重建任务检查出现错误,任务名称:{}",this.sourceRebuildTask.getTaskName(),e);
|
||||
jschRemoteRunner.close();
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*/
|
||||
|
|
@ -72,30 +180,30 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
String startRunLog = "----------------------------------------开始执行任务----------------------------------------";
|
||||
this.generateLog(startRunLog);
|
||||
//生成重建需要的监测数据
|
||||
String monitorDataLog = "----------------------------------------生成监测数据输入文件----------------------------------------";
|
||||
String titleLog = "Entity,Metric,Date,Value,Uncertainty,MDC Value";
|
||||
this.generateLog(monitorDataLog);
|
||||
this.generateLog(titleLog);
|
||||
this.generateMonitoringDataFile();
|
||||
//生成SRS关系文件
|
||||
String srsDataLog = "----------------------------------------生成SRS数据输入文件----------------------------------------";
|
||||
this.generateLog(srsDataLog);
|
||||
this.generateSRSDataFile();
|
||||
String sourceProcessDataLog = "----------------------------------------配置源项分析参数并执行源项重建----------------------------------------";
|
||||
this.generateLog(sourceProcessDataLog);
|
||||
this.execSourceRebuild();
|
||||
String genStopLog = "----------------------------------------任务执行结束----------------------------------------";
|
||||
this.generateLog(genStopLog);
|
||||
}catch (Exception e){
|
||||
String taskErrorLog = "任务执行失败,原因:"+e.getMessage();
|
||||
this.generateLog(taskErrorLog);
|
||||
throw e;
|
||||
taskRunError = true;
|
||||
this.generateLog("任务执行失败,原因:"+e.getMessage());
|
||||
log.error("任务执行失败,原因为:",e);
|
||||
}finally {
|
||||
jschRemoteRunner.close();
|
||||
//添加任务耗时
|
||||
stopWatch.stop();
|
||||
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
|
||||
Double min = seconds/60D;
|
||||
this.sourceRebuildTaskService.updateTaskTimeConsuming(this.sourceRebuildTask.getId(),min);
|
||||
Integer taskState;
|
||||
if (taskRunError){
|
||||
//如果taskRunError等于true,说明运行过程出错,这里把任务状态改为失败
|
||||
taskState = SourceRebuildTaskStatusEnum.FAILURE.getValue();
|
||||
}else{
|
||||
taskState = SourceRebuildTaskStatusEnum.COMPLETED.getValue();
|
||||
}
|
||||
this.sourceRebuildTaskService.updateTaskTimeConsuming(this.sourceRebuildTask.getId(),min,taskState);
|
||||
String taskCompletedLog = "任务执行结束,耗时:"+min+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.sourceRebuildTask.getId(),taskCompletedLog));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,17 +211,16 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
* 生成重建需要的监测数据
|
||||
*/
|
||||
private void generateMonitoringDataFile(){
|
||||
String monitorDataLog = "----------------------------------------生成监测数据输入文件----------------------------------------";
|
||||
String title = "Entity,Metric,Date,Value,Uncertainty,MDC Value\n";
|
||||
this.generateLog(monitorDataLog);
|
||||
this.generateLog(title);
|
||||
//根据监测数据生成文件(input_subexp1.dat)
|
||||
String inputPath = this.getInputPath();
|
||||
File inputSubexp1 = new File(inputPath +File.separator+ "input_subexp1.dat");
|
||||
if(!FileUtil.exist(inputSubexp1)){
|
||||
FileUtil.touch(inputSubexp1);
|
||||
}
|
||||
// String inputPath = sourceRebuildProperties.getRInput()+File.separator+ "input_subexp1.dat";
|
||||
String inputPath = sourceRebuildProperties.getRInput()+"/"+ "input_subexp1.dat";
|
||||
//格式化监测数据
|
||||
String title = "Entity,Metric,Date,Value,Uncertainty,MDC Value";
|
||||
List<String> lines = new ArrayList<>();
|
||||
lines.add(title);
|
||||
|
||||
for(SourceRebuildMonitoringData taskMonitoringData : taskMonitoringDatas){
|
||||
String station = taskMonitoringData.getStation();
|
||||
String nuclide = taskMonitoringData.getNuclide();
|
||||
|
|
@ -122,93 +229,35 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
String uncertainty = this.formatSCI(taskMonitoringData.getUncertainty());
|
||||
String mdc = this.formatSCI(taskMonitoringData.getMdc());
|
||||
|
||||
String formattedLine = String.format("%s, %s, %s, %s, %s, %s",station,nuclide,date,activity,uncertainty,mdc);
|
||||
String formattedLine = String.format("%s, %s, %s, %s, %s, %s\n",station,nuclide,date,activity,uncertainty,mdc);
|
||||
lines.add(formattedLine);
|
||||
//把监测数据详情保存到日志
|
||||
this.generateLog(formattedLine);
|
||||
}
|
||||
FileUtil.writeLines(lines,inputSubexp1,"UTF-8");
|
||||
jschRemoteRunner.writeFile(inputPath,String.join("",lines));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成SRS关系数据文件
|
||||
*/
|
||||
private void generateSRSDataFile(){
|
||||
//以日期为key,以文件相对路径为value
|
||||
Map<Date,String> allSRSFileMap = new HashMap<>();
|
||||
Map<Date,String> finalSRSFileMap = new TreeMap<>();
|
||||
//根据监测数据校验SRS文件,如果缺少SRS文件也报错
|
||||
String srsFilePath = this.getSRSFilePath();
|
||||
Path dirpath = Paths.get(srsFilePath);
|
||||
try(Stream<Path> walk = Files.walk(dirpath)) {
|
||||
walk.filter(Files::isRegularFile)
|
||||
.filter(path -> path.toAbsolutePath().toString().contains(sourceRebuildProperties.getXeGZFileDirSign()))
|
||||
.forEach(filePath -> {
|
||||
//USX77.fp.2024112115.f9.srm
|
||||
String[] nameArr = filePath.getFileName().toString().split("\\.");
|
||||
Date date = DateUtil.parse(nameArr[2],"yyyyMMddHH");
|
||||
String relativePath = filePath.toAbsolutePath().toString().substring(filePath.toAbsolutePath().toString().indexOf("srs"));
|
||||
allSRSFileMap.put(date,relativePath);
|
||||
});
|
||||
//如果遍历指定目录没有找到SRS数据文件则结束
|
||||
if(CollectionUtils.isEmpty(allSRSFileMap)){
|
||||
String srsNotExistLog = "SRS 文件存储目录中没有SRS数据文件";
|
||||
throw new RuntimeException(srsNotExistLog);
|
||||
}
|
||||
//根据监测数据多线程找对应的SRS数据文件,每一个没找到都写入日志
|
||||
int srsNotExistCount = 0;
|
||||
for(SourceRebuildMonitoringData monitoringData : this.taskMonitoringDatas){
|
||||
boolean flag = false;
|
||||
String station = monitoringData.getStation();
|
||||
Date collectStop = monitoringData.getCollectStop();
|
||||
for (Map.Entry<Date, String> entry : allSRSFileMap.entrySet()) {
|
||||
Date date = entry.getKey();
|
||||
String srsFile = entry.getValue();
|
||||
if(srsFile.contains(station)){
|
||||
boolean withinSixHours = this.isWithinSixHours(collectStop, date);
|
||||
if (withinSixHours) {
|
||||
finalSRSFileMap.put(collectStop, srsFile);
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!flag){
|
||||
srsNotExistCount ++;
|
||||
String targetSRSNotExist = "监测数据:"+DateUtil.format(collectStop,"yyyyMMddHH")+"所对应的SRS文件不存在";
|
||||
this.generateLog(targetSRSNotExist);
|
||||
}
|
||||
}
|
||||
//当监测数据都寻找完对应的SRS数据文件后,未寻找到的结果如果大于0,则结束流程,并写入日志
|
||||
if(srsNotExistCount > 0){
|
||||
String countSRSNotExist = "共有:"+srsNotExistCount+"条监测数据,找不到对应的SRS数据文件";
|
||||
this.generateLog(countSRSNotExist);
|
||||
throw new RuntimeException(countSRSNotExist);
|
||||
}
|
||||
//每条监测数据都找到SRS数据文件后生成文件(srsfilelist_subexp1.dat)
|
||||
String inputPath = getInputPath();
|
||||
File srsSubexp1 = new File(inputPath +File.separator+ "srsfilelist_subexp1.dat");
|
||||
if(!FileUtil.exist(srsSubexp1)){
|
||||
FileUtil.touch(srsSubexp1);
|
||||
}
|
||||
//去重并格式化监测数据
|
||||
List<String> lines = new ArrayList<>(finalSRSFileMap.values());
|
||||
//把SRS数据详情保存到日志
|
||||
lines.forEach(this::generateLog);
|
||||
//写入到文件
|
||||
FileUtil.writeLines(lines,srsSubexp1,"UTF-8");
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
String srsDataLog = "----------------------------------------生成SRS数据输入文件----------------------------------------";
|
||||
this.generateLog(srsDataLog);
|
||||
this.srsFilesPath.forEach(this::generateLog);
|
||||
// String inputPath = sourceRebuildProperties.getRInput()+File.separator+ "srsfilelist_subexp1.dat";
|
||||
String inputPath = sourceRebuildProperties.getRInput()+"/"+ "srsfilelist_subexp1.dat";
|
||||
jschRemoteRunner.writeFile(inputPath,String.join("",this.srsFilesPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行源项重建
|
||||
*/
|
||||
private void execSourceRebuild(){
|
||||
private void execSourceRebuild() throws Exception {
|
||||
String sourceProcessDataLog = "----------------------------------------配置源项分析参数并执行源项重建----------------------------------------";
|
||||
this.generateLog(sourceProcessDataLog);
|
||||
RConnection conn = null;
|
||||
try {
|
||||
conn = new RConnection(this.sourceRebuildProperties.getServerAddr(), this.sourceRebuildProperties.getPort());
|
||||
conn = new RConnection(this.serverProperties.getHost(), this.sourceRebuildProperties.getPort());
|
||||
conn.login(this.sourceRebuildProperties.getUsername(), this.sourceRebuildProperties.getPassword());
|
||||
if(conn.isConnected()){
|
||||
String log = "Rserve 连接成功,设置运行参数";
|
||||
|
|
@ -216,8 +265,8 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
//重建任务名称
|
||||
conn.assign("experiment", "FREAR_syntheticTestCase");
|
||||
//重建任务输入数据目录
|
||||
conn.assign("expdir", this.getInputPath());
|
||||
conn.assign("datadir", this.getSRSFilePath());
|
||||
conn.assign("expdir", this.sourceRebuildProperties.getRInput());
|
||||
conn.assign("datadir", this.sourceRebuildProperties.getSrsFilePath());
|
||||
//重建任务输出数据目录
|
||||
conn.assign("outbasedir", this.getOutputPath());
|
||||
//数据库相关
|
||||
|
|
@ -243,8 +292,8 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
String domain = String.format("domain <- list(lonmin=%s,latmin=%s,lonmax=%s,latmax=%s,dx=%s,dy=%s)",lonmin,latmin,lonmax,latmax,resolution,resolution);
|
||||
conn.eval(domain);
|
||||
//设置时间范围
|
||||
String srsStartTime = DateUtil.format(this.sourceRebuildTask.getSrsStartTime(), "yyyy-MM-dd");
|
||||
String srsEndTime = DateUtil.format(this.sourceRebuildTask.getSrsEndTime(), "yyyy-MM-dd");
|
||||
String srsStartTime = this.sourceRebuildTask.getSrsStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
String srsEndTime = this.sourceRebuildTask.getSrsEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
String times = String.format("times <- expression(seq(as.POSIXct('%s'),as.POSIXct('%s'),by=3*3600))",srsStartTime,srsEndTime);
|
||||
conn.eval(times);
|
||||
//如果已知排放源位置
|
||||
|
|
@ -269,19 +318,28 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
conn.eval("setwd(\""+this.sourceRebuildProperties.getWorkSpace()+"\")");
|
||||
//执行脚本
|
||||
conn.eval("source(\"R_scripts/set_up.R\")");
|
||||
REXP outputREXP = conn.eval("capture.output({source(\"R_scripts/main.R\")})");
|
||||
String rCmd =
|
||||
"local({" +
|
||||
" tryCatch({" +
|
||||
" out <- capture.output(source('R_scripts/main.R', echo=TRUE, print.eval=TRUE));" +
|
||||
" c('__R_OK__', out)" +
|
||||
" }, error=function(e) {" +
|
||||
" c('__R_ERROR__'," +
|
||||
" paste('message:', conditionMessage(e))," +
|
||||
" paste('call:', paste(deparse(conditionCall(e)), collapse=' '))" +
|
||||
" )" +
|
||||
" })" +
|
||||
"})";
|
||||
// REXP outputREXP = conn.eval("capture.output({source(\"R_scripts/main.R\")})");
|
||||
REXP outputREXP = conn.eval(rCmd);
|
||||
// 将输出作为字符串数组获取
|
||||
String[] outputLines = outputREXP.asStrings();
|
||||
if(ArrayUtil.isNotEmpty(outputLines)){
|
||||
this.batchGenerateLog(outputLines);
|
||||
}
|
||||
}
|
||||
} catch (RserveException e) {
|
||||
String log = "Rserve 执行过程报错,请检查配置参数";
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(log);
|
||||
} catch (REXPMismatchException e) {
|
||||
throw new RuntimeException(e);
|
||||
}catch (Exception e) {
|
||||
throw new Exception(e);
|
||||
}finally {
|
||||
if(Objects.nonNull(conn) && conn.isConnected()){
|
||||
conn.close();
|
||||
|
|
@ -289,18 +347,6 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个时间误差是否在2小时内
|
||||
* @param date1
|
||||
* @param date2
|
||||
* @return
|
||||
*/
|
||||
private boolean isWithinSixHours(Date date1, Date date2){
|
||||
long diffInMillis = Math.abs(date2.getTime() - date1.getTime());// 毫秒差(绝对值)
|
||||
long sixHoursInMillis = 6 * 60 * 60 * 1000; // 6小时
|
||||
return diffInMillis <= sixHoursInMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化为科学计数法
|
||||
* @param value
|
||||
|
|
@ -317,33 +363,6 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
return df.format(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取R项目运行输入监测数据及监测数据和srs对应关系文件地址
|
||||
* @return
|
||||
*/
|
||||
private String getInputPath(){
|
||||
StringBuilder inputPath = new StringBuilder();
|
||||
inputPath.append(sourceRebuildProperties.getRInput());
|
||||
if(!FileUtil.exist(inputPath.toString())){
|
||||
FileUtil.mkdir(inputPath.toString());
|
||||
}
|
||||
return inputPath.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SRS 文件存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getSRSFilePath(){
|
||||
StringBuilder srsFilePath = new StringBuilder();
|
||||
srsFilePath.append(sourceRebuildProperties.getSrsFilePath());
|
||||
if(!FileUtil.exist(srsFilePath.toString())){
|
||||
String srsNotExistLog = "SRS 文件存储目录不存在";
|
||||
throw new RuntimeException(srsNotExistLog);
|
||||
}
|
||||
return srsFilePath.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取R项目运行结果输出地址
|
||||
* @return
|
||||
|
|
@ -351,11 +370,11 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
private String getOutputPath(){
|
||||
StringBuilder outputPath = new StringBuilder();
|
||||
outputPath.append(sourceRebuildProperties.getROutput());
|
||||
outputPath.append(File.separator);
|
||||
outputPath.append("/");
|
||||
// outputPath.append(File.separator);
|
||||
outputPath.append(sourceRebuildTask.getTaskName());
|
||||
if(!FileUtil.exist(outputPath.toString())){
|
||||
FileUtil.mkdir(outputPath.toString());
|
||||
}
|
||||
//创建任务输出目录
|
||||
this.jschRemoteRunner.mkdir(outputPath.toString());
|
||||
return outputPath.toString();
|
||||
}
|
||||
|
||||
|
|
@ -364,6 +383,9 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
* @param logContent
|
||||
*/
|
||||
private void generateLog(String logContent){
|
||||
if(logContent.contains(errorFlag)){
|
||||
taskRunError = true;
|
||||
}
|
||||
sourceRebuildTaskLogService.cteate(new SourceRebuildTaskLog(sourceRebuildTask.getId(),logContent));
|
||||
}
|
||||
|
||||
|
|
@ -374,6 +396,9 @@ public class SourceRebuildTaskExec extends Thread{
|
|||
private void batchGenerateLog(String[] logContents){
|
||||
List<SourceRebuildTaskLog> logs = new ArrayList<>();
|
||||
for(String logContent : logContents){
|
||||
if(logContent.contains(errorFlag)){
|
||||
taskRunError = true;
|
||||
}
|
||||
logs.add(new SourceRebuildTaskLog(sourceRebuildTask.getId(),logContent));
|
||||
}
|
||||
sourceRebuildTaskLogService.batchCteate(logs);
|
||||
|
|
@ -13,13 +13,13 @@ import org.jeecg.common.constant.RocketMQTopConstant;
|
|||
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
|
||||
import org.jeecg.common.constant.enums.TransportTaskTopEnum;
|
||||
import org.jeecg.common.properties.DataFusionProperties;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.properties.TransportSimulationProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.dto.TransportTaskDTO;
|
||||
import org.jeecg.modules.base.entity.TransportTask;
|
||||
import org.jeecg.modules.base.mapper.*;
|
||||
import org.jeecg.properties.ServerProperties;
|
||||
import org.jeecg.transport.consumer.china.AbstractTaskMsgHandler;
|
||||
import org.jeecg.transport.consumer.china.Server11TaskHandler;
|
||||
import org.jeecg.transport.service.StationDataService;
|
||||
|
|
@ -38,7 +38,7 @@ import java.util.concurrent.TimeUnit;
|
|||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TranTaskMessageConsumerHandler{
|
||||
public class TranTaskConsumerHandler{
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
private final TransportTaskMapper transportTaskMapper;
|
||||
|
|
@ -106,19 +106,14 @@ public class TranTaskMessageConsumerHandler{
|
|||
//获取本机项数据,如果为空,表示本机没有任务在运行
|
||||
boolean flag = redisUtil.hHasKey(CommonConstant.HOST_TASK_STATE,CommonConstant.TRAN_TASK_STATE_PRE+serverProperties.getHost());
|
||||
if (!flag) {
|
||||
try {
|
||||
TransportTask topMessage = getTopMessage();
|
||||
if (Objects.nonNull(topMessage)) {
|
||||
handlerTask(topMessage);
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("消费输运任务数据异常,原因为:{}",e.getMessage(),e);
|
||||
TransportTask topMessage = getTopMessage();
|
||||
if (Objects.nonNull(topMessage)) {
|
||||
handlerTask(topMessage);
|
||||
}
|
||||
}
|
||||
log.info("60秒钟处理一次");
|
||||
TimeUnit.SECONDS.sleep(30);
|
||||
TimeUnit.SECONDS.sleep(60);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
log.error("执行输运任务数据异常,原因为:",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package org.jeecg.transport.consumer.china;
|
|||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 能谱执行链路
|
||||
* 执行链路
|
||||
*/
|
||||
public abstract class AbstractChain {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,20 @@
|
|||
package org.jeecg.transport.consumer.china;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.TransportTaskModeEnum;
|
||||
import org.jeecg.common.properties.DataFusionProperties;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.properties.TransportSimulationProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.dto.TransportTaskDTO;
|
||||
import org.jeecg.modules.base.entity.TransportTask;
|
||||
import org.jeecg.modules.base.mapper.*;
|
||||
import org.jeecg.properties.ServerProperties;
|
||||
import org.jeecg.transport.flexparttask.AbstractTaskExec;
|
||||
import org.jeecg.transport.flexparttask.BackwardTaskExec;
|
||||
import org.jeecg.transport.flexparttask.ForwardTaskExec;
|
||||
import org.jeecg.transport.service.StationDataService;
|
||||
import org.jeecg.transport.service.StationsModValService;
|
||||
import org.jeecg.transport.service.TransportTaskService;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 处理任务策略父类
|
||||
|
|
@ -120,7 +117,7 @@ public abstract class AbstractTaskMsgHandler extends AbstractChain{
|
|||
}
|
||||
}
|
||||
if (flag){
|
||||
log.info("任务:{},匹配成功,当前在主机:{}上执行。", transportTask.getTaskName(),ip);
|
||||
log.info("任务:{},检查通过,当前在主机:{}上执行。", transportTask.getTaskName(),ip);
|
||||
}else {
|
||||
transportTaskService.setInpectionFailed(transportTask);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class Server13TaskHandler extends AbstractTaskMsgHandler {
|
|||
@Override
|
||||
public void handler(TransportTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp13())){
|
||||
super.runTask(message,serverProperties.getIp12());
|
||||
super.runTask(message,serverProperties.getIp13());
|
||||
}else {
|
||||
super.next.handler(message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public class Server14TaskHandler extends AbstractTaskMsgHandler {
|
|||
@Override
|
||||
public void handler(TransportTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp14())){
|
||||
super.runTask(message,serverProperties.getIp14());
|
||||
//这里需要再加个判断,14服务器处理反演和永久任务
|
||||
// if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(message.getTaskMode())){
|
||||
// super.runTask(message,serverProperties.getIp11());
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import org.apache.logging.log4j.util.Strings;
|
|||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.common.properties.DataFusionProperties;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.properties.TransportSimulationProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.TransportTask;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.mapper.*;
|
||||
import org.jeecg.properties.ServerProperties;
|
||||
import org.jeecg.transport.service.StationDataService;
|
||||
import org.jeecg.transport.service.StationsModValService;
|
||||
import org.jeecg.transport.service.TransportTaskService;
|
||||
|
|
@ -147,20 +147,18 @@ public abstract class AbstractTaskExec extends Thread{
|
|||
*/
|
||||
protected String getMetDataPath(Integer dataSource){
|
||||
StringBuilder path = new StringBuilder();
|
||||
path.append(systemStorageProperties.getRootPath());
|
||||
path.append(File.separator);
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)){
|
||||
path.append(systemStorageProperties.getPangu());
|
||||
path.append(systemStorageProperties.getPanguDataPath()+"/format");
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)){
|
||||
path.append(systemStorageProperties.getGraphcast());
|
||||
path.append(systemStorageProperties.getGraphcastDataPath()+"/format");
|
||||
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)){
|
||||
path.append(systemStorageProperties.getCra40());
|
||||
path.append(systemStorageProperties.getCra40DataPath());
|
||||
}else if(WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)){
|
||||
path.append(systemStorageProperties.getNcep());
|
||||
path.append(systemStorageProperties.getNcepDataPath());
|
||||
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataSource)){
|
||||
path.append(systemStorageProperties.getFnl());
|
||||
path.append(systemStorageProperties.getFnlDataPath());
|
||||
}else if(WeatherDataSourceEnum.T1H.getKey().equals(dataSource)){
|
||||
path.append(systemStorageProperties.getT1h());
|
||||
path.append(systemStorageProperties.getT1hDataPath());
|
||||
}
|
||||
return path.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import org.jeecg.common.constant.enums.FlexpartSpeciesType;
|
|||
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.modules.base.entity.*;
|
||||
import org.jeecg.transport.util.JSchRemoteRunner;
|
||||
import org.jeecg.jsch.JSchRemoteRunner;
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
|
@ -119,7 +119,7 @@ public class BackwardTaskExec extends AbstractTaskExec {
|
|||
taskState = TransportTaskStatusEnum.COMPLETED.getValue();
|
||||
}
|
||||
super.transportTaskService.updateTaskStatusToCompleted(super.transportTask.getId(),result.doubleValue(),taskState);
|
||||
String taskCompletedLog = "任务执行完成,耗时:"+min+"分钟";
|
||||
String taskCompletedLog = "任务执行结束,耗时:"+min+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),taskCompletedLog));
|
||||
}
|
||||
}
|
||||
|
|
@ -150,7 +150,8 @@ public class BackwardTaskExec extends AbstractTaskExec {
|
|||
paramContent.append(super.transportTask.getZ1()).append("\n");
|
||||
paramContent.append(super.transportTask.getZ2()).append("\n");
|
||||
paramContent.append(metDataPath).append("\n");
|
||||
paramContent.append(super.simulationProperties.getOutputPath()+ File.separator+super.transportTask.getTaskName()).append("\n");
|
||||
paramContent.append(super.simulationProperties.getOutputPath()+ "/" +super.transportTask.getTaskName()).append("\n");
|
||||
// paramContent.append(super.simulationProperties.getOutputPath()+ File.separator+super.transportTask.getTaskName()).append("\n");
|
||||
paramContent.append(FlexpartSpeciesType.NOT_SPECIES.getId()).append("\n");//反演固定61,不显示具体核素,只显示Xe
|
||||
jschRemoteRunner.writeFile(paramConfigPath,paramContent.toString());
|
||||
//处理台站数据文件
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import org.apache.logging.log4j.util.Strings;
|
|||
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.modules.base.entity.*;
|
||||
import org.jeecg.transport.util.JSchRemoteRunner;
|
||||
import org.jeecg.jsch.JSchRemoteRunner;
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
|
@ -136,7 +136,7 @@ public class ForwardTaskExec extends AbstractTaskExec {
|
|||
taskState = TransportTaskStatusEnum.COMPLETED.getValue();
|
||||
}
|
||||
super.transportTaskService.updateTaskStatusToCompleted(super.transportTask.getId(),result.doubleValue(),taskState);
|
||||
String taskCompletedLog = "任务执行完成,耗时:"+min+"分钟";
|
||||
String taskCompletedLog = "任务执行结束,耗时:"+min+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),taskCompletedLog));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package org.jeecg.transport.flexparttask;
|
|||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
|
||||
import org.jeecg.modules.base.entity.TransportTaskLog;
|
||||
import org.jeecg.transport.service.TransportTaskService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -14,7 +13,7 @@ import java.util.Objects;
|
|||
* @author 86187
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@Component("transportProgressMonitor")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ProgressMonitor{
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.*;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.*;
|
||||
import org.jeecg.modules.base.mapper.*;
|
||||
import org.jeecg.properties.ServerProperties;
|
||||
import org.jeecg.transport.service.TransportTaskService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
|
@ -126,7 +126,6 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
@Override
|
||||
public void setInpectionFailed(TransportTask transportTask) {
|
||||
transportTask.setTaskStatus(TransportTaskStatusEnum.INSPECTION_FAILED.getValue());
|
||||
transportTask.setUpdateTime(LocalDateTime.now());
|
||||
this.baseMapper.updateById(transportTask);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
package org.jeecg.transport.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.jcraft.jsch.*;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.transport.flexparttask.ProgressEvent;
|
||||
import org.jeecg.transport.flexparttask.ProgressQueue;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* ssh远程操作工具
|
||||
*/
|
||||
@Slf4j
|
||||
public class JSchRemoteRunner {
|
||||
|
||||
private final JSch jsch = new JSch();
|
||||
|
||||
@Getter
|
||||
private Session session = null;
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
* @param host
|
||||
* @param port
|
||||
* @param username
|
||||
* @param password
|
||||
* @throws JSchException
|
||||
*/
|
||||
public void login(String host, int port, String username, String password) throws JSchException {
|
||||
Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking","no");
|
||||
config.put("PreferredAuthentications", "password");//只用秘密登录
|
||||
session = jsch.getSession(username, host, port);
|
||||
session.setUserInfo(new UserInfo() {
|
||||
@Override
|
||||
public String getPassphrase() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
@Override
|
||||
public boolean promptPassword(String s) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean promptPassphrase(String s) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean promptYesNo(String s) {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public void showMessage(String message) {
|
||||
log.info("SSH Message: {}", message);
|
||||
}
|
||||
});
|
||||
session.setConfig(config);
|
||||
session.setServerAliveInterval(30);
|
||||
session.setServerAliveCountMax(3);
|
||||
session.setTimeout(0);
|
||||
session.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入文件内容,如果文件不存在则创建
|
||||
* @param path
|
||||
* @param content
|
||||
*/
|
||||
public void writeFile(String path, String content) {
|
||||
ChannelSftp sftp = null;
|
||||
try {
|
||||
sftp = (ChannelSftp)session.openChannel("sftp");
|
||||
sftp.connect();
|
||||
sftp.put(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)),path,ChannelSftp.OVERWRITE);
|
||||
} catch (JSchException | SftpException e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (sftp != null) {
|
||||
sftp.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空目录下所有文件
|
||||
* @param path
|
||||
*/
|
||||
public void clearDir(String path){
|
||||
if (StrUtil.isBlank(path) || StrUtil.equals("/", path)){
|
||||
throw new RuntimeException("需要清空的目录路径不能为空");
|
||||
}
|
||||
ChannelExec channel = null;
|
||||
try{
|
||||
//打开一个执行通道
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
String fullCommand = "rm -f "+path+"/*";
|
||||
channel.setCommand(fullCommand);
|
||||
// 连接通道
|
||||
channel.connect();
|
||||
}catch(JSchException e){
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (channel != null) {
|
||||
channel.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭会话
|
||||
*/
|
||||
public void close(){
|
||||
if (session != null) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package org.jeecg.weather.consumer;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.SourceRebuildTaskStatusEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.weather.consumer.china.AbstractTaskMsgHandler;
|
||||
import org.jeecg.weather.consumer.china.Server11TaskHandler;
|
||||
import org.jeecg.weather.service.WeatherTaskService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 天气预报任务控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WeatherTaskConsumerHandler {
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
private final WeatherTaskService weatherTaskService;
|
||||
private final SystemStorageProperties systemStorageProperties;
|
||||
private final WeatherDataMapper weatherDataMapper;
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
public void startConsumerThread(){
|
||||
MessageConsumerThread messageConsumerThread = new MessageConsumerThread();
|
||||
messageConsumerThread.setName("weather-task-thread");
|
||||
messageConsumerThread.start();
|
||||
log.info("启动天气预报任务消费线程----------------------");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理天气预报任务
|
||||
*/
|
||||
private void handlerTask(WeatherTask message){
|
||||
AbstractTaskMsgHandler taskMsgHandler = new Server11TaskHandler();
|
||||
taskMsgHandler.init(
|
||||
redisUtil,
|
||||
weatherTaskService,
|
||||
systemStorageProperties,
|
||||
weatherDataMapper,
|
||||
serverProperties
|
||||
);
|
||||
taskMsgHandler.handler(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取等待中的任务
|
||||
* @return
|
||||
*/
|
||||
private WeatherTask getTopMessage(){
|
||||
LambdaQueryWrapper<WeatherTask> topQueryWrapper = new LambdaQueryWrapper<>();
|
||||
topQueryWrapper.eq(WeatherTask::getTaskStatus, WeatherTaskStatusEnum.WAITING.getValue());
|
||||
topQueryWrapper.orderByDesc(WeatherTask::getTopTask);
|
||||
topQueryWrapper.orderByDesc(WeatherTask::getUpdateTime);
|
||||
topQueryWrapper.last("LIMIT 1");
|
||||
WeatherTask weatherTask = this.weatherTaskService.getOne(topQueryWrapper);
|
||||
//如果没有要置顶执行的任务,则按照普通创建时间排序查询
|
||||
if(Objects.isNull(weatherTask)){
|
||||
LambdaQueryWrapper<WeatherTask> timeQueryWrapper = new LambdaQueryWrapper<>();
|
||||
timeQueryWrapper.eq(WeatherTask::getTaskStatus, SourceRebuildTaskStatusEnum.WAITING.getValue());
|
||||
timeQueryWrapper.orderByAsc(WeatherTask::getCreateTime);
|
||||
timeQueryWrapper.last("LIMIT 1");
|
||||
weatherTask = this.weatherTaskService.getOne(timeQueryWrapper);
|
||||
}
|
||||
return weatherTask;
|
||||
}
|
||||
|
||||
private class MessageConsumerThread extends Thread{
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
//获取本机项数据,如果为空,表示本机没有任务在运行
|
||||
boolean flag = redisUtil.hHasKey(CommonConstant.HOST_TASK_STATE,CommonConstant.WEATHER_TASK_STATE_PRE+serverProperties.getHost());
|
||||
if (!flag) {
|
||||
WeatherTask topMessage = getTopMessage();
|
||||
if (Objects.nonNull(topMessage)) {
|
||||
handlerTask(topMessage);
|
||||
}
|
||||
}
|
||||
TimeUnit.SECONDS.sleep(30);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("执行源项重建任务数据异常,原因为:",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.jeecg.weather.consumer.china;
|
||||
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 执行链路
|
||||
*/
|
||||
public abstract class AbstractChain {
|
||||
|
||||
/**
|
||||
* 上一任处理链
|
||||
*/
|
||||
@Setter
|
||||
protected AbstractTaskMsgHandler previous;
|
||||
|
||||
/**
|
||||
* 下一任处理链
|
||||
*/
|
||||
@Setter
|
||||
protected AbstractTaskMsgHandler next;
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
protected abstract void setChina();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package org.jeecg.weather.consumer.china;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.weather.service.WeatherTaskService;
|
||||
import org.jeecg.weather.task.AbstractWeatherTask;
|
||||
import org.jeecg.weather.task.WeatherForecastTaskExec;
|
||||
|
||||
/**
|
||||
* 处理任务策略父类
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractTaskMsgHandler extends AbstractChain {
|
||||
|
||||
protected RedisUtil redisUtil;
|
||||
protected WeatherTaskService weatherTaskService;
|
||||
protected SystemStorageProperties systemStorageProperties;
|
||||
protected WeatherDataMapper weatherDataMapper;
|
||||
protected ServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* 初始化bean引用
|
||||
*/
|
||||
public void init(RedisUtil redisUtil,
|
||||
WeatherTaskService weatherTaskService,
|
||||
SystemStorageProperties systemStorageProperties,
|
||||
WeatherDataMapper weatherDataMapper,
|
||||
ServerProperties serverProperties) {
|
||||
this.redisUtil = redisUtil;
|
||||
this.weatherTaskService = weatherTaskService;
|
||||
this.systemStorageProperties = systemStorageProperties;
|
||||
this.weatherDataMapper = weatherDataMapper;
|
||||
this.serverProperties = serverProperties;
|
||||
|
||||
this.setChina();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化下个节点
|
||||
* @param serverProperties
|
||||
*/
|
||||
protected void initNext(ServerProperties serverProperties){
|
||||
this.serverProperties = serverProperties;
|
||||
this.setChina();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
public abstract void handler(WeatherTask message);
|
||||
|
||||
/**
|
||||
* 运行任务
|
||||
* @param weatherTask
|
||||
*/
|
||||
protected void runTask(WeatherTask weatherTask,String ip){
|
||||
try {
|
||||
boolean flag;
|
||||
AbstractWeatherTask taskExec = new WeatherForecastTaskExec();
|
||||
taskExec.init(
|
||||
redisUtil,
|
||||
weatherTask,
|
||||
weatherTaskService,
|
||||
systemStorageProperties,
|
||||
weatherDataMapper,
|
||||
serverProperties);
|
||||
taskExec.setName("weather-forecast-task");
|
||||
flag = taskExec.checkTask(weatherTask);
|
||||
if (flag){
|
||||
log.info("任务:{},检查通过,当前在主机:{}上执行。", weatherTask.getTaskName(),ip);
|
||||
taskExec.start();
|
||||
}else {
|
||||
weatherTaskService.setInpectionFailed(weatherTask);
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("检查任务或开启任务执行线程出现错误",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package org.jeecg.weather.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
|
||||
/**
|
||||
* 处理天气预报任务
|
||||
*/
|
||||
public class Server11TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
AbstractTaskMsgHandler taskHandler = new Server12TaskHandler();
|
||||
taskHandler.initNext(super.serverProperties);
|
||||
taskHandler.setPrevious(this);
|
||||
super.setNext(taskHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(WeatherTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp11())){
|
||||
super.runTask(message,serverProperties.getIp11());
|
||||
}else {
|
||||
super.next.handler(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.jeecg.weather.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
|
||||
/**
|
||||
* 处理天气预报任务
|
||||
*/
|
||||
public class Server12TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
AbstractTaskMsgHandler taskHandler = new Server13TaskHandler();
|
||||
taskHandler.initNext(super.serverProperties);
|
||||
taskHandler.setPrevious(this);
|
||||
super.setNext(taskHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(WeatherTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp12())){
|
||||
super.runTask(message,serverProperties.getIp12());
|
||||
}else {
|
||||
super.next.handler(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.jeecg.weather.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
|
||||
/**
|
||||
* 处理天气预报任务
|
||||
*/
|
||||
public class Server13TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
AbstractTaskMsgHandler taskHandler = new Server14TaskHandler();
|
||||
taskHandler.initNext(super.serverProperties);
|
||||
taskHandler.setPrevious(this);
|
||||
super.setNext(taskHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(WeatherTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp13())){
|
||||
super.runTask(message,serverProperties.getIp13());
|
||||
}else {
|
||||
super.next.handler(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.jeecg.weather.consumer.china;
|
||||
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
|
||||
/**
|
||||
* 处理天气预报任务
|
||||
*/
|
||||
public class Server14TaskHandler extends AbstractTaskMsgHandler {
|
||||
|
||||
/**
|
||||
* 设置过滤器链
|
||||
*/
|
||||
@Override
|
||||
protected void setChina() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@Override
|
||||
public void handler(WeatherTask message) {
|
||||
if(serverProperties.getHost().equals(serverProperties.getIp14())){
|
||||
super.runTask(message,serverProperties.getIp14());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.jeecg.weather.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.entity.WeatherTaskLog;
|
||||
|
||||
/**
|
||||
* 天气预报预测任务管理
|
||||
*/
|
||||
public interface WeatherTaskService extends IService<WeatherTask> {
|
||||
|
||||
/**
|
||||
* 保存任务过程日志
|
||||
* @param log
|
||||
*/
|
||||
void saveLog(WeatherTaskLog log);
|
||||
|
||||
/**
|
||||
* 修改任务运行状态
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskStatus(Integer taskId,Integer taskStatus);
|
||||
|
||||
/**
|
||||
* 根据任务id删除任务日志
|
||||
* @param taskId
|
||||
*/
|
||||
void deleteTaskLog(Integer taskId);
|
||||
|
||||
/**
|
||||
* 修改任务状态为结束(完成或异常停止)
|
||||
* @param taskId
|
||||
* @param timeConsuming
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskStatusToCompleted(Integer taskId, Double timeConsuming,Integer taskStatus);
|
||||
|
||||
/**
|
||||
* 设置检查失败
|
||||
* @param weatherTask
|
||||
*/
|
||||
void setInpectionFailed(WeatherTask weatherTask);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package org.jeecg.weather.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.entity.WeatherTaskLog;
|
||||
import org.jeecg.modules.base.mapper.WeatherTaskLogMapper;
|
||||
import org.jeecg.modules.base.mapper.WeatherTaskMapper;
|
||||
import org.jeecg.weather.service.WeatherTaskService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 天气预报预测任务管理
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, WeatherTask> implements WeatherTaskService {
|
||||
|
||||
private final WeatherTaskLogMapper weatherTaskLogMapper;
|
||||
private final RedisUtil redisUtil;
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* 保存任务过程日志
|
||||
*
|
||||
* @param log
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void saveLog(WeatherTaskLog log) {
|
||||
this.weatherTaskLogMapper.insert(log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务运行状态
|
||||
*
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatus(Integer taskId, Integer taskStatus) {
|
||||
WeatherTask weatherTask = this.baseMapper.selectById(taskId);
|
||||
weatherTask.setTaskStatus(taskStatus);
|
||||
this.updateById(weatherTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id删除任务日志
|
||||
*
|
||||
* @param taskId
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void deleteTaskLog(Integer taskId) {
|
||||
LambdaQueryWrapper<WeatherTaskLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WeatherTaskLog::getTaskId,taskId);
|
||||
this.weatherTaskLogMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务状态为结束(完成或异常停止)
|
||||
* @param taskId
|
||||
* @param timeConsuming
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatusToCompleted(Integer taskId, Double timeConsuming,Integer taskStatus) {
|
||||
WeatherTask weatherTask = this.baseMapper.selectById(taskId);
|
||||
weatherTask.setTaskStatus(taskStatus);
|
||||
weatherTask.setTimeConsuming(timeConsuming);
|
||||
this.updateById(weatherTask);
|
||||
redisUtil.hdel(CommonConstant.HOST_TASK_STATE,CommonConstant.WEATHER_TASK_STATE_PRE+serverProperties.getHost(),taskId.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置检查失败
|
||||
*
|
||||
* @param weatherTask
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void setInpectionFailed(WeatherTask weatherTask) {
|
||||
weatherTask.setTaskStatus(WeatherTaskStatusEnum.INSPECTION_FAILED.getValue());
|
||||
this.updateById(weatherTask);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package org.jeecg.weather.task;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.WeatherForecastDatasourceEnum;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.weather.service.WeatherTaskService;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 天气预报模型执行父类
|
||||
*/
|
||||
public abstract class AbstractWeatherTask extends Thread{
|
||||
|
||||
protected RedisUtil redisUtil;
|
||||
protected WeatherTask weatherTask;
|
||||
protected WeatherTaskService weatherTaskService;
|
||||
protected SystemStorageProperties systemStorageProperties;
|
||||
protected WeatherDataMapper weatherDataMapper;
|
||||
protected ServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public void init(RedisUtil redisUtil,
|
||||
WeatherTask weatherTask,
|
||||
WeatherTaskService weatherTaskService,
|
||||
SystemStorageProperties systemStorageProperties,
|
||||
WeatherDataMapper weatherDataMapper,
|
||||
ServerProperties serverProperties){
|
||||
this.redisUtil = redisUtil;
|
||||
this.weatherTask = weatherTask;
|
||||
this.weatherTaskService = weatherTaskService;
|
||||
this.systemStorageProperties = systemStorageProperties;
|
||||
this.weatherDataMapper = weatherDataMapper;
|
||||
this.serverProperties = serverProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置检查任务
|
||||
* @param weatherTask
|
||||
* @return
|
||||
*/
|
||||
public boolean checkTask(WeatherTask weatherTask){
|
||||
if(Objects.isNull(weatherTask)){
|
||||
return false;
|
||||
}
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){
|
||||
if(StrUtil.isBlank(weatherTask.getInputFile())){
|
||||
return false;
|
||||
}
|
||||
if(!FileUtil.exist(weatherTask.getInputFile())){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置redis标记
|
||||
*/
|
||||
protected void setRedisFlag(){
|
||||
redisUtil.hset(CommonConstant.HOST_TASK_STATE,CommonConstant.WEATHER_TASK_STATE_PRE+serverProperties.getHost(),weatherTask.getId().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 持续读取日志
|
||||
*/
|
||||
protected void execCommand(String command, Session session){
|
||||
ChannelExec channel = null;
|
||||
try{
|
||||
//打开一个执行通道
|
||||
channel = (ChannelExec) session.openChannel("exec");
|
||||
String fullCommand = command + " 2>&1";
|
||||
channel.setCommand(fullCommand);
|
||||
// 获取脚本的标准输出流,包含错误输出流
|
||||
InputStream in = channel.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
// 连接通道
|
||||
channel.connect();
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if(StrUtil.isNotBlank(line)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(weatherTask.getId(),line));
|
||||
}
|
||||
}
|
||||
}catch(JSchException | IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
if (channel != null) {
|
||||
channel.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package org.jeecg.task;
|
||||
package org.jeecg.weather.task;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
|
|
@ -14,14 +13,14 @@ public class ProgressEvent implements Serializable{
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String taskId;
|
||||
private Integer taskId;
|
||||
|
||||
/**
|
||||
* 过程日志
|
||||
*/
|
||||
private String content;
|
||||
|
||||
public ProgressEvent(String taskId, String content) {
|
||||
public ProgressEvent(Integer taskId, String content) {
|
||||
this.taskId = taskId;
|
||||
this.content = content;
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package org.jeecg.task;
|
||||
package org.jeecg.weather.task;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.base.entity.WeatherTaskLog;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
import org.jeecg.weather.service.WeatherTaskService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.Objects;
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ import java.util.Objects;
|
|||
* @author 86187
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@Component("weatherProgressMonitor")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ProgressMonitor{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.jeecg.task;
|
||||
package org.jeecg.weather.task;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
package org.jeecg.weather.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherForecastDatasourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.util.Grib2TimeReader;
|
||||
import org.jeecg.jsch.JSchRemoteRunner;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 气象预测任务执行线程
|
||||
*/
|
||||
@Slf4j
|
||||
public class WeatherForecastTaskExec extends AbstractWeatherTask {
|
||||
|
||||
/**
|
||||
* 切片目录(临时)
|
||||
*/
|
||||
private final static String GRIB_COPY_DIR = "grib_copy_split";
|
||||
/**
|
||||
* 预测单个大文件输出目录(临时)
|
||||
*/
|
||||
private final static String OUTPUT_DIR = "output";
|
||||
/**
|
||||
* 适配flexpart格式化存储路径(临时)
|
||||
*/
|
||||
private final static String FLEXPART_FORMAT_DIR = "flexpart_format";
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*/
|
||||
public void execute() {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
try{
|
||||
super.setRedisFlag();
|
||||
//修改任务状态为执行中
|
||||
this.weatherTaskService.updateTaskStatus(this.weatherTask.getId(), WeatherTaskStatusEnum.IN_OPERATION.getValue());
|
||||
//如果此任务已存在历史日志,先清除
|
||||
this.weatherTaskService.deleteTaskLog(this.weatherTask.getId());
|
||||
//执行模拟
|
||||
this.execSimulation();
|
||||
//执行完成
|
||||
this.execComplete(stopWatch,WeatherTaskStatusEnum.COMPLETED.getValue(), "");
|
||||
}catch (Exception e){
|
||||
String taskErrorLog = "任务执行失败,原因:"+e.getMessage();
|
||||
//执行失败
|
||||
this.execComplete(stopWatch,WeatherTaskStatusEnum.FAILURE.getValue(), taskErrorLog);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行完成
|
||||
* @param stopWatch
|
||||
* @param taskStatus
|
||||
* @param taskErrorLog
|
||||
*/
|
||||
private void execComplete(StopWatch stopWatch,Integer taskStatus,String taskErrorLog){
|
||||
//添加任务耗时
|
||||
stopWatch.stop();
|
||||
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
|
||||
double min = seconds/60D;
|
||||
BigDecimal bgMin = new BigDecimal(min);
|
||||
BigDecimal result = bgMin.setScale(2, RoundingMode.HALF_UP);
|
||||
this.weatherTaskService.updateTaskStatusToCompleted(this.weatherTask.getId(),result.doubleValue(),taskStatus);
|
||||
if (WeatherTaskStatusEnum.COMPLETED.getValue().equals(taskStatus)){
|
||||
String taskCompletedLog = "任务执行完成,耗时:"+result+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskCompletedLog));
|
||||
}else if (WeatherTaskStatusEnum.FAILURE.getValue().equals(taskStatus)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskErrorLog));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行模拟
|
||||
*/
|
||||
private void execSimulation(){
|
||||
JSchRemoteRunner jschRemoteRunner = new JSchRemoteRunner();
|
||||
try {
|
||||
//登录ssh
|
||||
jschRemoteRunner.login(super.serverProperties.getHost(),super.serverProperties.getPort(),
|
||||
super.serverProperties.getUsername(),super.serverProperties.getPassword());
|
||||
//预测气象数据
|
||||
this.forecast();
|
||||
//适配flexpart
|
||||
this.convertGrib(jschRemoteRunner);
|
||||
//检查预测结果数据并存储到数据库
|
||||
this.checkDataAndSaveToDB();
|
||||
//删除以任务id命名的目录
|
||||
String delPath = "";
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
delPath = this.systemStorageProperties.getPanguModelExecPath()+File.separator+this.weatherTask.getId();
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
delPath = this.systemStorageProperties.getGraphcastModelExecPath()+File.separator+this.weatherTask.getId();
|
||||
}
|
||||
File outputDir = new File(delPath);
|
||||
if (outputDir.exists() && outputDir.isDirectory()){
|
||||
FileUtils.deleteDirectory(new File(delPath));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("盘古模型执行预测任务出现异常",e);
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
jschRemoteRunner.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 天气预测
|
||||
*/
|
||||
private void forecast(){
|
||||
try {
|
||||
//处理运行命令
|
||||
Map<String,Object> commandParam = null;
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
commandParam = this.getPanguRequestJson();
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
commandParam = this.getGraphcastRequestJson();
|
||||
}
|
||||
if (Objects.nonNull(commandParam)){
|
||||
WebClient webClient = WebClient.builder()
|
||||
.baseUrl(super.systemStorageProperties.getWeatherForecastBaseUrl())
|
||||
.build();
|
||||
webClient.post()
|
||||
.uri(super.systemStorageProperties.getWeatherForecastUri())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.TEXT_PLAIN)
|
||||
.bodyValue(commandParam)
|
||||
.retrieve()
|
||||
.bodyToFlux(String.class)
|
||||
.doOnNext(log->{
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),log));
|
||||
})
|
||||
.doOnError(e->{
|
||||
throw new RuntimeException(e);
|
||||
})
|
||||
.blockLast();
|
||||
}
|
||||
}catch (RuntimeException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换grib
|
||||
* @param jschRemoteRunner
|
||||
*/
|
||||
private void convertGrib(JSchRemoteRunner jschRemoteRunner){
|
||||
try {
|
||||
String panguFormatScriptPath = systemStorageProperties.getPanguFormatScriptPath();
|
||||
String graphcastFormatScriptPath = systemStorageProperties.getGraphcastFormatScriptPath();
|
||||
String flexpartFormatPath = this.getFlexpartFormatPath();
|
||||
String pythonEnvPath = systemStorageProperties.getPythonEnvPath();
|
||||
String gribCopyPath = this.getGribCopyPath();
|
||||
boolean exist = jschRemoteRunner.dirExist(flexpartFormatPath);
|
||||
if (!exist){
|
||||
jschRemoteRunner.mkdir(flexpartFormatPath);
|
||||
}
|
||||
//构建转换命令
|
||||
String startDate = this.weatherTask.getStartDate().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
String endDate = this.weatherTask.getStartDate().atTime(this.weatherTask.getStartTime(), 0, 0).plusHours(this.weatherTask.getLeadTime()).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
StringBuilder convertCommand = new StringBuilder();
|
||||
convertCommand.append("cd ");
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
convertCommand.append(panguFormatScriptPath);
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
convertCommand.append(graphcastFormatScriptPath);
|
||||
}
|
||||
convertCommand.append(" && ");
|
||||
convertCommand.append(pythonEnvPath);
|
||||
convertCommand.append(File.separator);
|
||||
convertCommand.append("python3 -u ");
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
convertCommand.append("pangu_reformat.py ");
|
||||
convertCommand.append("--ini_dir ");
|
||||
convertCommand.append(panguFormatScriptPath);
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
convertCommand.append(" graphcast_reformat.py ");
|
||||
convertCommand.append("--ini_dir ");
|
||||
convertCommand.append(graphcastFormatScriptPath);
|
||||
}
|
||||
convertCommand.append(" --output_dir ");
|
||||
convertCommand.append(flexpartFormatPath);
|
||||
convertCommand.append(" --input_dir ");
|
||||
convertCommand.append(gribCopyPath);
|
||||
convertCommand.append(" --start_date ");
|
||||
convertCommand.append(startDate);
|
||||
convertCommand.append(" --end_date ");
|
||||
convertCommand.append(endDate);
|
||||
super.execCommand(convertCommand.toString(),jschRemoteRunner.getSession());
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("适配flexpart,转换grib异常",e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查预测结果数据并存储到数据库
|
||||
*/
|
||||
private void checkDataAndSaveToDB(){
|
||||
String flexpartFormatPath = this.getFlexpartFormatPath();
|
||||
String gribCopyPath = this.getGribCopyPath();
|
||||
List<File> sourceFiles = FileUtil.loopFiles(gribCopyPath);
|
||||
List<File> formatFiles = FileUtil.loopFiles(flexpartFormatPath);
|
||||
if(CollUtil.isEmpty(sourceFiles)){
|
||||
String log = "天气预测任务可能出现问题,未找到预测结果切片";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),log));
|
||||
return;
|
||||
}
|
||||
if(CollUtil.isEmpty(formatFiles)){
|
||||
String log = "天气预测任务可能出现问题,适配flexpart切片目录为空";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),log));
|
||||
return;
|
||||
}
|
||||
int gribNum = this.weatherTask.getLeadTime()/6;
|
||||
if(sourceFiles.size() != gribNum || formatFiles.size() != gribNum){
|
||||
String log = "天气预测任务可能出现问题,预测结果文件数量和预测时间不一致";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),log));
|
||||
return;
|
||||
}
|
||||
//把预测好的及格式化后的气象文件移动到最终目录
|
||||
String formatFilesFinalPath = this.getFormatFilesFinalStoragePath();
|
||||
String sourceFilesFinalPath = this.getSourceFilesFinalStoragePath();
|
||||
FileUtil.move(new File(gribCopyPath),new File(sourceFilesFinalPath),true);
|
||||
FileUtil.move(new File(flexpartFormatPath),new File(formatFilesFinalPath),true);
|
||||
//处理文件入库
|
||||
List<WeatherData> dataList = new ArrayList<>();
|
||||
for(File sourceFile : sourceFiles){
|
||||
try{
|
||||
//构建文件信息
|
||||
WeatherData weatherData = new WeatherData();
|
||||
weatherData.setFileName(sourceFile.getName());
|
||||
weatherData.setFileExt(sourceFile.getName().substring(sourceFile.getName().lastIndexOf(".")+1));
|
||||
weatherData.setDataStartTime(Grib2TimeReader.readValidTime(sourceFilesFinalPath+File.separator+sourceFile.getName()));
|
||||
weatherData.setDataSource(weatherTask.getPredictionModel());
|
||||
weatherData.setFilePath(sourceFilesFinalPath+File.separator+sourceFile.getName());
|
||||
weatherData.setFormatFilePath(formatFilesFinalPath+File.separator+sourceFile.getName());
|
||||
weatherData.setTaskId(this.weatherTask.getId());
|
||||
dataList.add(weatherData);
|
||||
}catch (Exception e){
|
||||
String logContent = "读取"+sourceFilesFinalPath+File.separator+sourceFile.getName()+"文件时间参数出现错误";
|
||||
log.error(logContent,e);
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),logContent));
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(dataList)){
|
||||
weatherDataMapper.insert(dataList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取盘古模型请求命令
|
||||
* @return
|
||||
*/
|
||||
private Map<String,Object> getPanguRequestJson(){
|
||||
Map<String,Object> map = new HashMap<>();
|
||||
map.put("model","pangu");
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
map.put("input_model","cds");
|
||||
}else if (this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){
|
||||
map.put("input_model","file");
|
||||
map.put("file",this.weatherTask.getInputFile());
|
||||
}
|
||||
map.put("date",this.weatherTask.getStartDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
||||
map.put("time",this.weatherTask.getStartTime());
|
||||
map.put("lead_time",this.weatherTask.getLeadTime());
|
||||
map.put("assets","assets-panguweather");
|
||||
map.put("workdir",systemStorageProperties.getPanguModelExecPath());
|
||||
map.put("split_dir",getGribCopyPath());
|
||||
map.put("path",buildOutputFilePath());
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Graphcast模型请求命令
|
||||
* @return
|
||||
*/
|
||||
private Map<String,Object> getGraphcastRequestJson(){
|
||||
Map<String,Object> map = new HashMap<>();
|
||||
map.put("model","graphcast");
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
map.put("input_model","cds");
|
||||
}else if (this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){
|
||||
map.put("input_model","file");
|
||||
map.put("file",this.weatherTask.getInputFile());
|
||||
}
|
||||
map.put("date",this.weatherTask.getStartDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
||||
map.put("time",this.weatherTask.getStartTime());
|
||||
map.put("lead_time",this.weatherTask.getLeadTime());
|
||||
map.put("class","od");
|
||||
map.put("assets","assets-graphcast");
|
||||
map.put("workdir",systemStorageProperties.getPanguModelExecPath());
|
||||
map.put("split_dir",getGribCopyPath());
|
||||
map.put("path",buildOutputFilePath());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建输出文件地址
|
||||
* @return
|
||||
*/
|
||||
private String buildOutputFilePath(){
|
||||
StringBuilder path = new StringBuilder();
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append(this.systemStorageProperties.getPanguModelExecPath());
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append(this.systemStorageProperties.getGraphcastModelExecPath());
|
||||
}
|
||||
path.append(File.separator);
|
||||
path.append(this.weatherTask.getId());
|
||||
path.append(File.separator);
|
||||
path.append(OUTPUT_DIR);
|
||||
path.append(File.separator);
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append("panguweather_output.grib");
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append("graphcast_output.grib");
|
||||
}
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Pangu/Graphcast数据切割存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getGribCopyPath(){
|
||||
StringBuilder path = new StringBuilder();
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append(systemStorageProperties.getPanguModelExecPath());
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append(systemStorageProperties.getPanguModelExecPath());
|
||||
}
|
||||
path.append(File.separator);
|
||||
path.append(this.weatherTask.getId());
|
||||
path.append(File.separator);
|
||||
path.append(GRIB_COPY_DIR);
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取盘古数据存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getFlexpartFormatPath(){
|
||||
StringBuilder path = new StringBuilder();
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append(systemStorageProperties.getPanguModelExecPath());
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
path.append(systemStorageProperties.getGraphcastModelExecPath());
|
||||
}
|
||||
path.append(File.separator);
|
||||
path.append(this.weatherTask.getId());
|
||||
path.append(File.separator);
|
||||
path.append(FLEXPART_FORMAT_DIR);
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预测原文件最终存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getSourceFilesFinalStoragePath(){
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
return systemStorageProperties.getPanguModelExecPath()+"/source";
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
return systemStorageProperties.getGraphcastModelExecPath()+"/source";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预测并格式化后的文件最终存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getFormatFilesFinalStoragePath(){
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
return systemStorageProperties.getPanguModelExecPath()+"/format";
|
||||
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())){
|
||||
return systemStorageProperties.getGraphcastModelExecPath()+"/format";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -2,35 +2,16 @@ package org.jeecg.service;
|
|||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTaskLog;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 源项重建任务日志数据
|
||||
*/
|
||||
public interface SourceRebuildTaskLogService extends IService<SourceRebuildTaskLog> {
|
||||
|
||||
/**
|
||||
* 保存源项重建任务运行过程日志
|
||||
* @param sourceRebuildTaskLog
|
||||
*/
|
||||
void cteate(SourceRebuildTaskLog sourceRebuildTaskLog);
|
||||
|
||||
/**
|
||||
* 删除任务运行过程日志
|
||||
* @param taskId
|
||||
*/
|
||||
void delete(Integer taskId);
|
||||
|
||||
/**
|
||||
* 获取任务运行过程日志
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
List<SourceRebuildTaskLog> getTaskLogs(Integer taskId);
|
||||
|
||||
/**
|
||||
* 批量保存日志
|
||||
* @param logs
|
||||
*/
|
||||
void batchCteate(List<SourceRebuildTaskLog> logs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,18 +62,4 @@ public interface SourceRebuildTaskService extends IService<SourceRebuildTask> {
|
|||
* @return
|
||||
*/
|
||||
List<SourceRebuildTaskLog> getTaskLog(Integer taskId);
|
||||
|
||||
/**
|
||||
* 修改任务状态
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskStatus(Integer taskId, Integer taskStatus);
|
||||
|
||||
/**
|
||||
* 任务耗时
|
||||
* @param taskId
|
||||
* @param minute
|
||||
*/
|
||||
void updateTaskTimeConsuming(Integer taskId, Double minute);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,17 +13,6 @@ import java.util.List;
|
|||
@Service
|
||||
public class SourceRebuildTaskLogServiceImpl extends ServiceImpl<SourceRebuildTaskLogMapper, SourceRebuildTaskLog> implements SourceRebuildTaskLogService {
|
||||
|
||||
/**
|
||||
* 保存源项重建任务运行过程日志
|
||||
*
|
||||
* @param sourceRebuildTaskLog
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void cteate(SourceRebuildTaskLog sourceRebuildTaskLog) {
|
||||
this.baseMapper.insert(sourceRebuildTaskLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务运行过程日志
|
||||
*
|
||||
|
|
@ -36,28 +25,4 @@ public class SourceRebuildTaskLogServiceImpl extends ServiceImpl<SourceRebuildTa
|
|||
queryWrapper.eq(SourceRebuildTaskLog::getTaskId, taskId);
|
||||
this.baseMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务运行过程日志
|
||||
*
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<SourceRebuildTaskLog> getTaskLogs(Integer taskId) {
|
||||
LambdaQueryWrapper<SourceRebuildTaskLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SourceRebuildTaskLog::getTaskId, taskId);
|
||||
return this.baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存日志
|
||||
*
|
||||
* @param logs
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void batchCteate(List<SourceRebuildTaskLog> logs) {
|
||||
this.saveBatch(logs);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import lombok.RequiredArgsConstructor;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.constant.enums.SourceRebuildTaskStatusEnum;
|
||||
import org.jeecg.common.properties.SourceRebuildProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.system.query.PageRequest;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildMonitoringData;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
|
|
@ -19,11 +18,9 @@ import org.jeecg.modules.base.mapper.SourceRebuildTaskMapper;
|
|||
import org.jeecg.service.SourceRebuildMonitoringDataService;
|
||||
import org.jeecg.service.SourceRebuildTaskLogService;
|
||||
import org.jeecg.service.SourceRebuildTaskService;
|
||||
import org.jeecg.task.SourceRebuildTaskExec;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
|
@ -40,7 +37,6 @@ public class SourceRebuildTaskServiceImpl extends ServiceImpl<SourceRebuildTaskM
|
|||
private final SourceRebuildTaskLogMapper sourceRebuildTaskLogMapper;
|
||||
private final SourceRebuildMonitoringDataService monitoringDataService;
|
||||
private final SourceRebuildTaskLogService sourceRebuildTaskLogService;
|
||||
private final SystemStorageProperties systemStorageProperties;
|
||||
private final SourceRebuildProperties sourceRebuildProperties;
|
||||
|
||||
/**
|
||||
|
|
@ -81,6 +77,8 @@ public class SourceRebuildTaskServiceImpl extends ServiceImpl<SourceRebuildTaskM
|
|||
if (Objects.nonNull(checkNameResult)) {
|
||||
throw new RuntimeException("此任务已存在");
|
||||
}
|
||||
sourceRebuildTask.setCreateTime(LocalDateTime.now());
|
||||
sourceRebuildTask.setUpdateTime(LocalDateTime.now());
|
||||
sourceRebuildTask.setTaskStatus(SourceRebuildTaskStatusEnum.NOT_STARTED.getValue());
|
||||
this.baseMapper.insert(sourceRebuildTask);
|
||||
}
|
||||
|
|
@ -134,6 +132,7 @@ public class SourceRebuildTaskServiceImpl extends ServiceImpl<SourceRebuildTaskM
|
|||
checkIdResult.setHalflife(sourceRebuildTask.getHalflife());
|
||||
checkIdResult.setQmin(sourceRebuildTask.getQmin());
|
||||
checkIdResult.setQmax(sourceRebuildTask.getQmax());
|
||||
checkIdResult.setUpdateTime(LocalDateTime.now());
|
||||
this.updateById(checkIdResult);
|
||||
}
|
||||
|
||||
|
|
@ -172,22 +171,13 @@ public class SourceRebuildTaskServiceImpl extends ServiceImpl<SourceRebuildTaskM
|
|||
if(Objects.isNull(sourceRebuildTask)){
|
||||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
|
||||
//校验监测数据
|
||||
List<SourceRebuildMonitoringData> taskMonitoringDatas = this.monitoringDataService.getTaskMonitoringData(id);
|
||||
if(CollectionUtils.isEmpty(taskMonitoringDatas)){
|
||||
throw new RuntimeException("监测数据为空,请补充监测数据");
|
||||
}
|
||||
|
||||
SourceRebuildTaskExec sourceRebuildTaskExec = new SourceRebuildTaskExec();
|
||||
sourceRebuildTaskExec.init(
|
||||
taskMonitoringDatas,
|
||||
sourceRebuildTask,
|
||||
sourceRebuildTaskLogService,
|
||||
sourceRebuildProperties,
|
||||
this);
|
||||
sourceRebuildTaskExec.setName(sourceRebuildTask.getId()+"_任务执行线程");
|
||||
sourceRebuildTaskExec.start();
|
||||
sourceRebuildTask.setTaskStatus(SourceRebuildTaskStatusEnum.WAITING.getValue());
|
||||
this.updateById(sourceRebuildTask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -202,33 +192,4 @@ public class SourceRebuildTaskServiceImpl extends ServiceImpl<SourceRebuildTaskM
|
|||
queryWrapper.select(SourceRebuildTaskLog::getCreateTime,SourceRebuildTaskLog::getLogContent);
|
||||
return sourceRebuildTaskLogMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务状态
|
||||
*
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatus(Integer taskId, Integer taskStatus) {
|
||||
SourceRebuildTask sourceRebuildTask = this.baseMapper.selectById(taskId);
|
||||
sourceRebuildTask.setTaskStatus(taskStatus);
|
||||
this.updateById(sourceRebuildTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务耗时
|
||||
*
|
||||
* @param taskId
|
||||
* @param minute
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskTimeConsuming(Integer taskId, Double minute) {
|
||||
SourceRebuildTask sourceRebuildTask = this.baseMapper.selectById(taskId);
|
||||
sourceRebuildTask.setTimeConsuming(minute);
|
||||
sourceRebuildTask.setTaskStatus(SourceRebuildTaskStatusEnum.COMPLETED.getValue());
|
||||
this.updateById(sourceRebuildTask);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.jeecg.service.impl;
|
|||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.properties.ServerProperties;
|
||||
import org.jeecg.common.properties.SourceRebuildProperties;
|
||||
import org.jeecg.common.util.DownloadUtils;
|
||||
import org.jeecg.modules.base.entity.SourceRebuildTask;
|
||||
|
|
@ -27,6 +28,7 @@ public class TaskResultDataServiceImpl implements TaskResultDataService {
|
|||
|
||||
private final SourceRebuildTaskService sourceRebuildTaskService;
|
||||
private final SourceRebuildProperties sourceRebuildProperties;
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
/**
|
||||
* 获取Rserve连接
|
||||
|
|
@ -35,7 +37,7 @@ public class TaskResultDataServiceImpl implements TaskResultDataService {
|
|||
private RConnection getRConnection(){
|
||||
RConnection conn = null;
|
||||
try {
|
||||
conn = new RConnection(sourceRebuildProperties.getServerAddr(),sourceRebuildProperties.getPort());
|
||||
conn = new RConnection(serverProperties.getHost(),sourceRebuildProperties.getPort());
|
||||
conn.login(sourceRebuildProperties.getUsername(), sourceRebuildProperties.getPassword());
|
||||
} catch (RserveException e) {
|
||||
throw new RuntimeException("Rserve连接失败");
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
package org.jeecg.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "server-prop")
|
||||
public class ServerProperties {
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
}
|
||||
|
|
@ -108,8 +108,6 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
}
|
||||
transportTask.setTimeConsuming(0D);
|
||||
transportTask.setTopTask(TransportTaskTopEnum.NOT_TOP.getValue());
|
||||
transportTask.setCreateTime(LocalDateTime.now());
|
||||
transportTask.setUpdateTime(LocalDateTime.now());
|
||||
this.baseMapper.insert(transportTask);
|
||||
if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode()) &&
|
||||
CollUtil.isNotEmpty(transportTask.getBackwardChild())) {
|
||||
|
|
@ -210,7 +208,6 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
checkIdResult.setZ2(transportTask.getZ2());
|
||||
checkIdResult.setParticleCount(transportTask.getParticleCount());
|
||||
checkIdResult.setReleaseDataSource(transportTask.getReleaseDataSource());
|
||||
checkIdResult.setUpdateTime(LocalDateTime.now());
|
||||
this.baseMapper.updateById(checkIdResult);
|
||||
//先删除再保存
|
||||
if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode())) {
|
||||
|
|
@ -315,7 +312,6 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
transportTask.setTopTask(TransportTaskTopEnum.TOP.getValue());
|
||||
transportTask.setUpdateTime(LocalDateTime.now());
|
||||
this.baseMapper.updateById(transportTask);
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +372,6 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
}
|
||||
//修改状态为等待中
|
||||
checkIdResult.setTaskStatus(TransportTaskStatusEnum.WAITING.getValue());
|
||||
checkIdResult.setUpdateTime(LocalDateTime.now());
|
||||
this.baseMapper.updateById(checkIdResult);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,20 +4,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherFileSuffixEnum;
|
||||
import org.jeecg.common.system.query.PageRequest;
|
||||
import org.jeecg.job.DownloadT1hJob;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.entity.WeatherLinkedDataLog;
|
||||
import org.jeecg.service.WeatherDataService;
|
||||
import org.jeecg.vo.FileExistVo;
|
||||
import org.jeecg.vo.FileUploadResultVo;
|
||||
import org.jeecg.vo.FileVo;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
|
@ -52,25 +48,6 @@ public class WeatherDataController {
|
|||
return Result.OK(rspData);
|
||||
}
|
||||
|
||||
@AutoLog(value = "验证文件是否存在")
|
||||
@Operation(summary = "验证文件是否存在")
|
||||
@GetMapping("verifyFileExist")
|
||||
public Result<?> verifyFileExist(@NotBlank(message = "文件MD5值不能为空") String fileMD5Value) {
|
||||
FileExistVo fileExist = weatherDataService.verifyFileExist(fileMD5Value);
|
||||
return Result.ok(fileExist);
|
||||
}
|
||||
|
||||
@AutoLog(value = "上传文件")
|
||||
@Operation(summary = "上传文件")
|
||||
@PostMapping("uploadFile")
|
||||
public Result<?> uploadFile(FileVo fileVo){
|
||||
if (!fileVo.getFileExt().equals(WeatherFileSuffixEnum.GRIB.getValue()) && !fileVo.getFileExt().equals(WeatherFileSuffixEnum.GRIB2.getValue())){
|
||||
throw new RuntimeException("不支持当前上传的文件类型!");
|
||||
}else{
|
||||
FileUploadResultVo resultVo = weatherDataService.uploadFile(fileVo);
|
||||
return Result.ok(resultVo);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 查询批次列表
|
||||
* @return
|
||||
|
|
@ -112,7 +89,7 @@ public class WeatherDataController {
|
|||
@AutoLog(value = "气象预测-气象预览")
|
||||
@Operation(summary = "气象预测-气象预览")
|
||||
@GetMapping(value = "getWeatherDataPreview")
|
||||
public Result<?> getWeatherDataPreview(String weatherId,Integer weatherType) {
|
||||
public Result<?> getWeatherDataPreview(Integer weatherId,Integer weatherType) {
|
||||
return Result.OK(weatherDataService.getWeatherDataPreview(weatherId, weatherType));
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +128,7 @@ public class WeatherDataController {
|
|||
@AutoLog(value = "删除气象数据")
|
||||
@Operation(summary = "删除气象数据")
|
||||
@DeleteMapping("delete")
|
||||
public Result<?> delete(@RequestBody List<String> ids){
|
||||
public Result<?> delete(@RequestBody List<Integer> ids){
|
||||
weatherDataService.delete(ids);
|
||||
return Result.OK();
|
||||
}
|
||||
|
|
@ -166,4 +143,22 @@ public class WeatherDataController {
|
|||
weatherDataService.handleStaticDataToDB(path,dataSource);
|
||||
return Result.OK();
|
||||
}
|
||||
|
||||
@AutoLog(value = "关联气象数据")
|
||||
@Operation(summary = "关联气象数据")
|
||||
@PutMapping("linkedData")
|
||||
public Result<?> linkedData(Integer dataType,
|
||||
@DateTimeFormat(iso=DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
@DateTimeFormat(iso=DateTimeFormat.ISO.DATE)LocalDate endDate){
|
||||
weatherDataService.linkedData(dataType,startDate,endDate);
|
||||
return Result.OK();
|
||||
}
|
||||
|
||||
@AutoLog(value = "查询关联气象数据日志")
|
||||
@Operation(summary = "查询关联气象数据日志")
|
||||
@GetMapping("getLinkedDataLog")
|
||||
public Result<?> getLinkedDataLog(){
|
||||
List<WeatherLinkedDataLog> result = weatherDataService.getLinkedDataLog();
|
||||
return Result.OK(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import org.jeecg.common.validgroup.InsertGroup;
|
|||
import org.jeecg.common.validgroup.UpdateGroup;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
import org.jeecg.vo.ForecastFileVO;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
|
@ -53,7 +52,7 @@ public class WeatherTaskController {
|
|||
@AutoLog(value = "获取单条天气预测任务记录")
|
||||
@Operation(summary = "获取单条天气预测任务记录")
|
||||
@GetMapping("getById")
|
||||
public Result<?> getById(@NotNull(message = "ID不能为空") String id){
|
||||
public Result<?> getById(@NotNull(message = "ID不能为空") Integer id){
|
||||
return Result.OK(weatherTaskService.getById(id));
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +67,7 @@ public class WeatherTaskController {
|
|||
@AutoLog(value = "删除天气预测任务")
|
||||
@Operation(summary = "删除天气预测任务")
|
||||
@DeleteMapping("delete")
|
||||
public Result<?> delete(@RequestBody List<String> ids){
|
||||
public Result<?> delete(@RequestBody List<Integer> ids){
|
||||
weatherTaskService.delete(ids);
|
||||
return Result.OK();
|
||||
}
|
||||
|
|
@ -76,7 +75,7 @@ public class WeatherTaskController {
|
|||
@AutoLog(value = "启动任务")
|
||||
@Operation(summary = "启动任务")
|
||||
@PutMapping("runTask")
|
||||
public Result<?> runTask(@NotBlank(message = "任务ID不能为空") String taskId){
|
||||
public Result<?> runTask(@NotBlank(message = "任务ID不能为空") Integer taskId){
|
||||
weatherTaskService.runTask(taskId);
|
||||
return Result.OK();
|
||||
}
|
||||
|
|
@ -84,7 +83,7 @@ public class WeatherTaskController {
|
|||
@AutoLog(value = "获取天气预测任务过程日志")
|
||||
@Operation(summary = "获取天气预测任务过程日志")
|
||||
@GetMapping("getTaskLog")
|
||||
public Result<?> getTaskLog(@NotBlank(message = "预测任务ID不能为空") String taskId){
|
||||
public Result<?> getTaskLog(@NotBlank(message = "预测任务ID不能为空") Integer taskId){
|
||||
return Result.OK(weatherTaskService.getTaskLog(taskId));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,29 +42,31 @@ public class DownloadT1hJob {
|
|||
@Scheduled(cron = "#{@t1hDownloadProperties.cron}")
|
||||
// @Scheduled(cron = "0 0 1 * * ?")
|
||||
public void downloadT1HFile() {
|
||||
log.info("开始执行T1H文件下载任务");
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
try {
|
||||
String baseTime = getBaseTime();
|
||||
// 第一阶段:下载文件
|
||||
downloadAllT1hFiles(baseTime);
|
||||
// 第二阶段:合并为NC文件
|
||||
mergeT1hNCFiles(baseTime);
|
||||
// 第三阶段:解析为Grib2文件
|
||||
reformat(baseTime);
|
||||
// 合并后删除原始文件
|
||||
Arrays.stream(new File(getFullPath(t1hDownloadProperties.getT1hPath(), baseTime)).listFiles()).filter(File::isFile).forEach(File::delete);
|
||||
Arrays.stream(new File(getFullPath(t1hDownloadProperties.getT1hNcPath(), baseTime)).listFiles()).filter(File::isFile).forEach(File::delete);
|
||||
// 更新气象文件信息
|
||||
saveWeatherData(baseTime);
|
||||
log.info("T1H文件下载任务执行完成");
|
||||
} catch (Exception e) {
|
||||
log.error("T1H文件下载任务执行失败", e);
|
||||
throw new RuntimeException("T1H文件下载任务失败", e);
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
log.info("T1H文件下载任务执行耗时: {} 毫秒", stopWatch.getTotalTimeMillis());
|
||||
if (t1hDownloadProperties.isDownloadSwitch()){
|
||||
log.info("开始执行T1H文件下载任务");
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
try {
|
||||
String baseTime = getBaseTime();
|
||||
// 第一阶段:下载文件
|
||||
downloadAllT1hFiles(baseTime);
|
||||
// 第二阶段:合并为NC文件
|
||||
mergeT1hNCFiles(baseTime);
|
||||
// 第三阶段:解析为Grib2文件
|
||||
reformat(baseTime);
|
||||
// 合并后删除原始文件
|
||||
Arrays.stream(new File(getFullPath(t1hDownloadProperties.getT1hPath(), baseTime)).listFiles()).filter(File::isFile).forEach(File::delete);
|
||||
Arrays.stream(new File(getFullPath(t1hDownloadProperties.getT1hNcPath(), baseTime)).listFiles()).filter(File::isFile).forEach(File::delete);
|
||||
// 更新气象文件信息
|
||||
saveWeatherData(baseTime);
|
||||
log.info("T1H文件下载任务执行完成");
|
||||
} catch (Exception e) {
|
||||
log.error("T1H文件下载任务执行失败", e);
|
||||
throw new RuntimeException("T1H文件下载任务失败", e);
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
log.info("T1H文件下载任务执行耗时: {} 毫秒", stopWatch.getTotalTimeMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +92,7 @@ public class DownloadT1hJob {
|
|||
List<String> forecastTimes = generateForecastTimes();
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||
t1hDownloadProperties.getPythonPath(),
|
||||
getPythonScriptPath(t1hDownloadProperties.getMergeT1hPy()),
|
||||
t1hDownloadProperties.getMergeT1hPy(),
|
||||
"--indir", getFullPath(t1hDownloadProperties.getT1hPath(), baseTime),
|
||||
"--output_dir", getFullPath(t1hDownloadProperties.getT1hNcPath(), baseTime),
|
||||
"--variables", String.join(",", variables),
|
||||
|
|
@ -108,13 +110,13 @@ public class DownloadT1hJob {
|
|||
private void reformat(String baseTime) {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||
t1hDownloadProperties.getPythonPath(),
|
||||
getPythonScriptPath(t1hDownloadProperties.getReformatPy()),
|
||||
"--ini_dir", getPythonScriptPath(t1hDownloadProperties.getScriptPath()),
|
||||
"--output_dir", getFullPath(systemStorageProperties.getT1h(), baseTime),
|
||||
t1hDownloadProperties.getReformatPy(),
|
||||
"--ini_dir", t1hDownloadProperties.getScriptPath(),
|
||||
"--output_dir", getFullPath(systemStorageProperties.getT1hDataPath(), baseTime),
|
||||
"--cra_dir", getFullPath(t1hDownloadProperties.getT1hNcPath(), baseTime),
|
||||
"--start_date", getStartTime(),
|
||||
"--end_date", getEndTime(),
|
||||
"--var_table", getPythonScriptPath(t1hDownloadProperties.getLookupFileName())
|
||||
"--var_table",t1hDownloadProperties.getLookupFileName()
|
||||
);
|
||||
|
||||
ExecutePyUtils.executePythonProcess(processBuilder, "文件合并");
|
||||
|
|
@ -159,7 +161,7 @@ public class DownloadT1hJob {
|
|||
private String[] buildPythonCommand(String element, String baseTime, String forecastHours) {
|
||||
return new String[]{
|
||||
t1hDownloadProperties.getPythonPath(),
|
||||
getPythonScriptPath(t1hDownloadProperties.getDownloadT1hPy()),
|
||||
t1hDownloadProperties.getDownloadT1hPy(),
|
||||
"--base-time", baseTime,
|
||||
"--element", element,
|
||||
"--output-dir", getFullPath(t1hDownloadProperties.getT1hPath(), baseTime),
|
||||
|
|
@ -187,7 +189,7 @@ public class DownloadT1hJob {
|
|||
}
|
||||
|
||||
public List<WeatherData> readFolderFiles(String baseTime) {
|
||||
String folderPath = getFullPath(systemStorageProperties.getT1h(), baseTime);
|
||||
String folderPath = getFullPath(systemStorageProperties.getT1hDataPath(), baseTime);
|
||||
try (Stream<Path> paths = Files.list(Paths.get(folderPath))) {
|
||||
return paths.filter(Files::isRegularFile)
|
||||
.filter(Files::isRegularFile)
|
||||
|
|
@ -203,16 +205,11 @@ public class DownloadT1hJob {
|
|||
private WeatherData extractFileInfo(File file, String baseTime) {
|
||||
WeatherData data = new WeatherData();
|
||||
data.setFileName(file.getName());
|
||||
data.setFileSize((double) file.length() / 1024 / 1024); // MB
|
||||
data.setFileExt(getFileExtension(file.getName()));
|
||||
data.setFilePath(file.getAbsolutePath());
|
||||
data.setDataSource(WeatherDataSourceEnum.T1H.getKey());
|
||||
data.setDataStartTime(parseStartTimeFromFileName(file.getName()));
|
||||
data.setCreateTime(new Date());
|
||||
data.setMd5Value(calculateMD5(file.getAbsolutePath()));
|
||||
data.setShareTotal(1);
|
||||
data.setTimeBatch(baseTime);
|
||||
data.setCreateTime(new Date());
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
@ -246,14 +243,7 @@ public class DownloadT1hJob {
|
|||
* 获取完整路径
|
||||
*/
|
||||
private String getFullPath(String relativePath, String baseTime) {
|
||||
return systemStorageProperties.getRootPath() + File.separator + relativePath + File.separator + baseTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Python脚本路径
|
||||
*/
|
||||
private String getPythonScriptPath(String scriptName) {
|
||||
return systemStorageProperties.getRootPath() + File.separator + scriptName;
|
||||
return relativePath + File.separator + baseTime;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.common.system.query.PageRequest;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.entity.WeatherLinkedDataLog;
|
||||
import org.jeecg.vo.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
|
@ -12,7 +13,7 @@ import java.util.List;
|
|||
public interface WeatherDataService extends IService<WeatherData> {
|
||||
|
||||
WeatherResultVO getWeatherData(Integer dataType, Integer weatherType, String timeBatch, LocalDateTime startTime, int hour);
|
||||
WeatherResultVO getWeatherDataPreview(String weatherId, Integer weatherType);
|
||||
WeatherResultVO getWeatherDataPreview(Integer weatherId, Integer weatherType);
|
||||
WindDataLineVO getDataLine(Integer dataType, String timeBatch, LocalDateTime startTime, LocalDateTime endTime, double longitude, double latitude);
|
||||
List<WindRoseData> getWindRose(Integer dataType, String timeBatch, LocalDateTime startTime, LocalDateTime endTime,double longitude, double latitude);
|
||||
|
||||
|
|
@ -27,24 +28,11 @@ public interface WeatherDataService extends IService<WeatherData> {
|
|||
*/
|
||||
IPage<WeatherData> page(PageRequest pageRequest,String fileName, String fileExt, String dataSource, LocalDate startDate, LocalDate endDate);
|
||||
|
||||
/**
|
||||
* 验证文件是否存在
|
||||
* @param fileMD5Value 文件唯一MD5值
|
||||
* @return
|
||||
*/
|
||||
FileExistVo verifyFileExist(String fileMD5Value);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param fileVo
|
||||
*/
|
||||
FileUploadResultVo uploadFile(FileVo fileVo);
|
||||
|
||||
/**
|
||||
* 删除气象数据
|
||||
* @param ids
|
||||
*/
|
||||
void delete(List<String> ids);
|
||||
void delete(List<Integer> ids);
|
||||
|
||||
/**
|
||||
* 处理静态气象数据入库接口,比上传快
|
||||
|
|
@ -52,8 +40,16 @@ public interface WeatherDataService extends IService<WeatherData> {
|
|||
void handleStaticDataToDB(String path,Integer dataSource);
|
||||
|
||||
/**
|
||||
* 保存模型预测好的文件信息到WeatherData
|
||||
* @param weatherData
|
||||
* 查询关联气象数据日志
|
||||
* @return
|
||||
*/
|
||||
void saveFileInfo(WeatherData weatherData);
|
||||
List<WeatherLinkedDataLog> getLinkedDataLog();
|
||||
|
||||
/**
|
||||
* 关联气象数据
|
||||
* @param dataType
|
||||
* @param startDate
|
||||
* @param endDate
|
||||
*/
|
||||
void linkedData(Integer dataType,LocalDate startDate, LocalDate endDate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public interface WeatherTaskService extends IService<WeatherTask> {
|
|||
* @param id
|
||||
* @return
|
||||
*/
|
||||
WeatherTask getById(String id);
|
||||
WeatherTask getById(Integer id);
|
||||
|
||||
/**
|
||||
* 修改天气预报预测任务
|
||||
|
|
@ -47,46 +47,18 @@ public interface WeatherTaskService extends IService<WeatherTask> {
|
|||
* 删除预测任务
|
||||
* @param ids
|
||||
*/
|
||||
void delete(List<String> ids);
|
||||
void delete(List<Integer> ids);
|
||||
|
||||
/**
|
||||
* 运行任务
|
||||
* @param id
|
||||
*/
|
||||
void runTask(String id);
|
||||
void runTask(Integer id);
|
||||
|
||||
/**
|
||||
* 获取任务运行日志
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
List<WeatherTaskLog> getTaskLog(String taskId);
|
||||
|
||||
/**
|
||||
* 保存任务过程日志
|
||||
* @param log
|
||||
*/
|
||||
void saveLog(WeatherTaskLog log);
|
||||
|
||||
/**
|
||||
* 修改任务运行状态
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskStatus(String taskId,Integer taskStatus);
|
||||
|
||||
/**
|
||||
* 根据任务id删除任务日志
|
||||
* @param taskId
|
||||
*/
|
||||
void deleteTaskLog(String taskId);
|
||||
|
||||
/**
|
||||
* 修改任务状态为结束(完成或异常停止)
|
||||
* @param taskId
|
||||
* @param timeConsuming
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskStatusToCompleted(String taskId, Double timeConsuming,Integer taskStatus);
|
||||
|
||||
List<WeatherTaskLog> getTaskLog(Integer taskId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,39 +5,30 @@ import cn.hutool.core.io.FileUtil;
|
|||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.constant.WeatherPrefixConstants;
|
||||
import org.jeecg.common.constant.WeatherStepConstants;
|
||||
import org.jeecg.common.constant.WeatherSuffixConstants;
|
||||
import org.jeecg.common.constant.enums.*;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.exception.JeecgFileUploadException;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.system.query.PageRequest;
|
||||
import org.jeecg.common.util.Grib2TimeReader;
|
||||
import org.jeecg.common.util.NcUtil;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.entity.WeatherLinkedDataLog;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.modules.base.mapper.WeatherLinkedDataLogMapper;
|
||||
import org.jeecg.service.WeatherDataService;
|
||||
import org.jeecg.utils.WindRoseDataGenerator;
|
||||
import org.jeecg.vo.*;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ucar.nc2.NetcdfFile;
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
|
@ -49,7 +40,6 @@ import java.time.ZoneId;
|
|||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jeecg.common.constant.LatLonSizeConstants.*;
|
||||
|
||||
@Slf4j
|
||||
|
|
@ -57,10 +47,9 @@ import static org.jeecg.common.constant.LatLonSizeConstants.*;
|
|||
@RequiredArgsConstructor
|
||||
public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, WeatherData> implements WeatherDataService {
|
||||
|
||||
private final SystemStorageProperties systemStorageProperties;
|
||||
private final DataSourceTransactionManager transactionManager;
|
||||
private final TransactionDefinition transactionDefinition;
|
||||
private final WeatherDataMapper weatherDataMapper;
|
||||
private final WeatherLinkedDataLogMapper weatherLinkedDataLogMapper;
|
||||
private final SystemStorageProperties systemStorageProperties;
|
||||
|
||||
/**
|
||||
* 根据类型和小时数获取天气数据
|
||||
|
|
@ -104,7 +93,7 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
* @return 天气数据列表
|
||||
*/
|
||||
@Override
|
||||
public WeatherResultVO getWeatherDataPreview(String weatherId, Integer weatherType) {
|
||||
public WeatherResultVO getWeatherDataPreview(Integer weatherId, Integer weatherType) {
|
||||
Objects.requireNonNull(weatherId, "天气数据ID不能为空");
|
||||
WeatherData weatherData = this.baseMapper.selectById(weatherId);
|
||||
Integer dataType = weatherData.getDataSource();
|
||||
|
|
@ -321,164 +310,12 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
queryWrapper.between((Objects.nonNull(startTime) && Objects.nonNull(endTime)),WeatherData::getDataStartTime,startTime,endTime);
|
||||
queryWrapper.eq(StringUtils.isNotBlank(fileExt),WeatherData::getFileExt, fileExt);
|
||||
queryWrapper.like(StringUtils.isNotBlank(fileName),WeatherData::getFileName, fileName);
|
||||
queryWrapper.select(WeatherData::getId,WeatherData::getFileName,WeatherData::getFileSize,WeatherData::getDataSource,
|
||||
queryWrapper.select(WeatherData::getId,WeatherData::getFileName,WeatherData::getDataSource,
|
||||
WeatherData::getFileExt,WeatherData::getDataStartTime,WeatherData::getFilePath,WeatherData::getTimeBatch);
|
||||
IPage<WeatherData> iPage = new Page<>(pageRequest.getPageNum(),pageRequest.getPageSize());
|
||||
return this.baseMapper.selectPage(iPage, queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证文件是否存在
|
||||
*
|
||||
* @param fileMD5Value 文件唯一MD5值
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public FileExistVo verifyFileExist(String fileMD5Value) {
|
||||
LambdaQueryWrapper<WeatherData> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(WeatherData::getMd5Value, fileMD5Value);
|
||||
|
||||
FileExistVo fileExist = new FileExistVo();
|
||||
WeatherData weatherData = this.baseMapper.selectOne(wrapper);
|
||||
if(Objects.nonNull(weatherData)) {
|
||||
fileExist.setExist(true);
|
||||
fileExist.setFileId(weatherData.getId());
|
||||
fileExist.setShareTotal(weatherData.getShareTotal());
|
||||
fileExist.setShareIndex(weatherData.getShareIndex());
|
||||
}
|
||||
return fileExist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param fileVo
|
||||
*/
|
||||
@Override
|
||||
public FileUploadResultVo uploadFile(FileVo fileVo) {
|
||||
final TransactionStatus transactionStatus = this.transactionManager.getTransaction(transactionDefinition);
|
||||
//文件保存路径
|
||||
String storagePath = null;
|
||||
//数据ID
|
||||
String id= null;
|
||||
//上传成功总体标记
|
||||
boolean flag = true;
|
||||
try{
|
||||
MultipartFile file = fileVo.getFile();
|
||||
//文件保存路径
|
||||
storagePath = this.getFileStoragePath(fileVo.getDataSource(), fileVo.getFileName());
|
||||
fileVo.setFilePath(storagePath);
|
||||
//如果上传策略是分片则加分片后缀
|
||||
if(fileVo.isFileShare()) {
|
||||
storagePath += StringPool.DOT;
|
||||
storagePath += fileVo.getShareIndex();
|
||||
}
|
||||
//保存文件
|
||||
File shareFile = new File(storagePath.toString());
|
||||
if(!shareFile.getParentFile().exists()) {
|
||||
shareFile.getParentFile().setWritable(true);
|
||||
shareFile.getParentFile().mkdirs();
|
||||
}
|
||||
file.transferTo(shareFile);
|
||||
|
||||
//保存文件信息入库
|
||||
FileExistVo fileExist = this.verifyFileExist(fileVo.getMd5Value());
|
||||
if(fileExist.isExist()) {
|
||||
WeatherData queryResult = this.baseMapper.selectById(fileExist.getFileId());
|
||||
queryResult.setShareIndex(fileVo.getShareIndex());
|
||||
this.baseMapper.updateById(queryResult);
|
||||
id = queryResult.getId();
|
||||
}else {
|
||||
WeatherData weatherData = new WeatherData();
|
||||
BeanUtils.copyProperties(fileVo, weatherData);
|
||||
this.baseMapper.insert(weatherData);
|
||||
id = weatherData.getId();
|
||||
}
|
||||
this.transactionManager.commit(transactionStatus);
|
||||
}catch (Exception e) {
|
||||
flag = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if(fileVo.isFileShare()) {
|
||||
if(fileVo.getShareIndex() == (fileVo.getShareTotal()-1)) {
|
||||
flag = this.merge(fileVo);
|
||||
}else {
|
||||
//未合并之前需先是false,不然后面就开始处理气象文件数据开始时间和计算文件大小
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(flag) {
|
||||
final TransactionStatus updateTransactionStatus = this.transactionManager.getTransaction(transactionDefinition);
|
||||
try{
|
||||
//处理气象文件数据开始时间和计算文件大小
|
||||
WeatherData queryResult = this.baseMapper.selectById(id);
|
||||
File dataFile = new File(storagePath);
|
||||
if(dataFile.exists() && dataFile.length()>0){
|
||||
//获取文件数据开始日期
|
||||
String reftime = NcUtil.getReftime(dataFile.getAbsolutePath());
|
||||
if(StringUtils.isBlank(reftime)) {
|
||||
throw new JeecgFileUploadException("解析气象文件起始时间数据异常,此文件可能损坏");
|
||||
}
|
||||
Instant instant = Instant.parse(reftime);
|
||||
LocalDateTime utcDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
|
||||
queryResult.setDataStartTime(utcDateTime);
|
||||
//计算文件大小M
|
||||
BigDecimal divideVal = new BigDecimal("1024");
|
||||
BigDecimal bg = new BigDecimal(dataFile.length());
|
||||
BigDecimal fileSize = bg.divide(divideVal).divide(divideVal).setScale(2, RoundingMode.HALF_UP);
|
||||
queryResult.setFileSize(fileSize.doubleValue());
|
||||
//把文件移入新路径
|
||||
String newFileDirPath = dataFile.getParentFile().getParent()+File.separator+File.separator;
|
||||
String newFilePath = newFileDirPath+File.separator+dataFile.getName();
|
||||
FileUtil.mkdir(newFileDirPath);
|
||||
File parentDir = dataFile.getParentFile();
|
||||
File[] files = parentDir.listFiles();
|
||||
if(ArrayUtils.isNotEmpty(files)) {
|
||||
for(File file : files) {
|
||||
File targetFile = new File(newFileDirPath + File.separator + file.getName());
|
||||
FileUtil.move(file,targetFile,true);
|
||||
}
|
||||
}
|
||||
//删除临时目录
|
||||
parentDir.delete();
|
||||
//给原路径变量重新赋值
|
||||
storagePath = newFileDirPath;
|
||||
//修改文件新路径存到数据库
|
||||
queryResult.setFilePath(newFilePath);
|
||||
this.baseMapper.updateById(queryResult);
|
||||
this.transactionManager.commit(updateTransactionStatus);
|
||||
}
|
||||
}catch (Exception e){
|
||||
flag = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
//如果单文件上传失败或者分片上传失败则删除数据
|
||||
if((!flag && !fileVo.isFileShare()) ||
|
||||
(!flag && fileVo.isFileShare() && fileVo.getShareIndex() == (fileVo.getShareTotal()-1))) {
|
||||
String delDirPath = null;
|
||||
File file = new File(storagePath);
|
||||
if(file.exists() && file.isFile()){
|
||||
delDirPath = file.getParent();
|
||||
}else if(file.isDirectory()){
|
||||
delDirPath = file.getAbsolutePath();
|
||||
}
|
||||
FileUtil.del(delDirPath);
|
||||
final TransactionStatus delTransactionStatus = this.transactionManager.getTransaction(transactionDefinition);
|
||||
this.baseMapper.deleteById(id);
|
||||
this.transactionManager.commit(delTransactionStatus);
|
||||
log.error("{}文件上传失败",fileVo.getFileName());
|
||||
throw new RuntimeException(fileVo.getFileName()+"文件上传失败");
|
||||
}
|
||||
|
||||
FileUploadResultVo result = new FileUploadResultVo();
|
||||
result.setCompleted(flag);
|
||||
result.setId(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除气象数据
|
||||
*
|
||||
|
|
@ -486,28 +323,40 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void delete(List<String> ids) {
|
||||
public void delete(List<Integer> ids) {
|
||||
List<WeatherData> weatherDatas = this.baseMapper.selectByIds(ids);
|
||||
if (CollUtil.isNotEmpty(weatherDatas)) {
|
||||
for(WeatherData weatherData : weatherDatas) {
|
||||
//删除气象文件和生成的.gbx9、.ncx2文件
|
||||
File dataFile = new File(weatherData.getFilePath());
|
||||
if(dataFile.exists()) {
|
||||
dataFile.delete();
|
||||
}
|
||||
File gbx9File = new File(weatherData.getFilePath()+".gbx9");
|
||||
if(gbx9File.exists()) {
|
||||
gbx9File.delete();
|
||||
}
|
||||
File ncx4File = new File(weatherData.getFilePath()+".ncx4");
|
||||
if(ncx4File.exists()) {
|
||||
ncx4File.delete();
|
||||
this.delWeatherDataFile(weatherData.getFilePath());
|
||||
if(WeatherDataSourceEnum.PANGU.getKey().equals(weatherData.getDataSource()) ||
|
||||
WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherData.getDataSource())) {
|
||||
this.delWeatherDataFile(weatherData.getFormatFilePath());
|
||||
}
|
||||
}
|
||||
this.baseMapper.deleteByIds(ids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除气象文件
|
||||
* @param filePath
|
||||
*/
|
||||
private void delWeatherDataFile(String filePath) {
|
||||
File dataFile = new File(filePath);
|
||||
if(dataFile.exists()) {
|
||||
dataFile.delete();
|
||||
}
|
||||
File gbx9File = new File(filePath+".gbx9");
|
||||
if(gbx9File.exists()) {
|
||||
gbx9File.delete();
|
||||
}
|
||||
File ncx4File = new File(filePath+".ncx4");
|
||||
if(ncx4File.exists()) {
|
||||
ncx4File.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理静态气象数据入库接口,比上传快
|
||||
*/
|
||||
|
|
@ -525,21 +374,15 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
weatherData.setFileExt(file.getName().substring(file.getName().lastIndexOf(".")+1));
|
||||
weatherData.setDataSource(dataSource);
|
||||
weatherData.setFilePath(file.getAbsolutePath());
|
||||
weatherData.setMd5Value(DigestUtils.md5Hex(is));
|
||||
weatherData.setShareTotal(1);
|
||||
//获取文件数据开始日期
|
||||
String reftime = NcUtil.getReftime(file.getAbsolutePath());
|
||||
Grib2TimeReader.readValidTime(file.getAbsolutePath());
|
||||
if(StringUtils.isBlank(reftime)) {
|
||||
throw new JeecgFileUploadException("解析气象文件起始时间数据异常,此文件可能损坏");
|
||||
}
|
||||
Instant instant = Instant.parse(reftime);
|
||||
LocalDateTime utcDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
|
||||
weatherData.setDataStartTime(utcDateTime);
|
||||
//计算文件大小M
|
||||
BigDecimal divideVal = new BigDecimal("1024");
|
||||
BigDecimal bg = new BigDecimal(file.length());
|
||||
BigDecimal fileSize = bg.divide(divideVal).divide(divideVal).setScale(2, RoundingMode.HALF_UP);
|
||||
weatherData.setFileSize(fileSize.doubleValue());
|
||||
this.baseMapper.insert(weatherData);
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e.getMessage());
|
||||
|
|
@ -557,94 +400,107 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
}
|
||||
|
||||
/**
|
||||
* 保存模型预测好的文件信息到WeatherData
|
||||
* 查询关联气象数据日志
|
||||
*
|
||||
* @param weatherData
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void saveFileInfo(WeatherData weatherData) {
|
||||
this.save(weatherData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件合并
|
||||
* @param fileVo
|
||||
* @throws Exception
|
||||
*/
|
||||
private boolean merge(FileVo fileVo) {
|
||||
boolean mergeFlag = true;
|
||||
|
||||
String storagePath = this.getFileStoragePath(fileVo.getDataSource(), fileVo.getFileName());
|
||||
BufferedOutputStream bos = null;
|
||||
BufferedInputStream bis = null;
|
||||
try {
|
||||
bos = new BufferedOutputStream(new FileOutputStream(storagePath,true));
|
||||
byte[] byt = new byte[10*1024];
|
||||
int len;
|
||||
for(int i=0;i<fileVo.getShareTotal();i++) {
|
||||
bis = new BufferedInputStream(new FileInputStream(storagePath+"."+i));
|
||||
while((len = bis.read(byt))!=-1) {
|
||||
bos.write(byt, 0, len);
|
||||
}
|
||||
if(null != bis) {
|
||||
bis.close();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
mergeFlag = false;
|
||||
log.error(fileVo.getFileName()+"文件分片上传异常");
|
||||
}finally {
|
||||
try {
|
||||
if(null != bos) {
|
||||
bos.flush();
|
||||
bos.close();
|
||||
}
|
||||
if(null != bis) {
|
||||
bis.close();
|
||||
}
|
||||
}catch (Exception e){
|
||||
mergeFlag = false;
|
||||
}
|
||||
}
|
||||
//合并成功或失败都删除旧得分片
|
||||
for(int i=0;i<fileVo.getShareTotal();i++) {
|
||||
File file = new File(storagePath+"."+i);
|
||||
if(file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
return mergeFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接文件存储路径
|
||||
* @param dataSource
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
private String getFileStoragePath(Integer dataSource,String fileName) {
|
||||
StringBuilder storagePath = new StringBuilder();
|
||||
storagePath.append(this.systemStorageProperties.getRootPath());
|
||||
storagePath.append(File.separator);
|
||||
if (WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)) {
|
||||
storagePath.append(this.systemStorageProperties.getPangu());
|
||||
} else if (WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)) {
|
||||
storagePath.append(this.systemStorageProperties.getGraphcast());
|
||||
} else if (WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)) {
|
||||
storagePath.append(this.systemStorageProperties.getCra40());
|
||||
} else if (WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)) {
|
||||
storagePath.append(this.systemStorageProperties.getNcep());
|
||||
} else if (WeatherDataSourceEnum.FNL.getKey().equals(dataSource)) {
|
||||
storagePath.append(this.systemStorageProperties.getFnl());
|
||||
} else if (WeatherDataSourceEnum.T1H.getKey().equals(dataSource)) {
|
||||
storagePath.append(this.systemStorageProperties.getT1h());
|
||||
@Override
|
||||
public List<WeatherLinkedDataLog> getLinkedDataLog() {
|
||||
LambdaQueryWrapper<WeatherLinkedDataLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.select(WeatherLinkedDataLog::getCreateTime,WeatherLinkedDataLog::getLogContent);
|
||||
return weatherLinkedDataLogMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联气象数据
|
||||
*
|
||||
* @param dataType
|
||||
* @param startDate
|
||||
* @param endDate
|
||||
*/
|
||||
@Override
|
||||
public void linkedData(Integer dataType, LocalDate startDate, LocalDate endDate) {
|
||||
//先清空表
|
||||
weatherLinkedDataLogMapper.delete(null);
|
||||
String linkedAddr = "";
|
||||
if(WeatherDataSourceEnum.NCEP.getKey().equals(dataType)) {
|
||||
linkedAddr = systemStorageProperties.getNcepDataPath();
|
||||
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataType)) {
|
||||
linkedAddr = systemStorageProperties.getFnlDataPath();
|
||||
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataType)) {
|
||||
linkedAddr = systemStorageProperties.getCra40DataPath();
|
||||
}
|
||||
storagePath.append(File.separator);
|
||||
storagePath.append(fileName.substring(0,fileName.lastIndexOf(StringPool.DOT)));
|
||||
storagePath.append(File.separator);
|
||||
storagePath.append(fileName);
|
||||
return storagePath.toString();
|
||||
File dir = new File(linkedAddr);
|
||||
if (!dir.exists()) {
|
||||
FileUtil.mkdir(dir);
|
||||
}
|
||||
List<File> fileList = FileUtil.loopFiles(dir, new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
if(StrUtil.endWith(file.getName(), WeatherFileSuffixEnum.GRIB.getValue())
|
||||
|| StrUtil.endWith(file.getName(), WeatherFileSuffixEnum.GRB2.getValue())
|
||||
|| StrUtil.endWith(file.getName(), WeatherFileSuffixEnum.GRIB2.getValue())){
|
||||
try{
|
||||
LocalDate localDate = null;
|
||||
if(WeatherDataSourceEnum.NCEP.getKey().equals(dataType)) {
|
||||
String dateTimeStr = file.getName().substring(0, file.getName().indexOf("."));
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, formatter);
|
||||
localDate = dateTime.toLocalDate();
|
||||
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataType)){
|
||||
String dateTimeStr = file.getName().split("_")[1];
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
localDate = LocalDate.parse(dateTimeStr, formatter);
|
||||
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataType)){
|
||||
String dateTimeStr = file.getName().split("_")[1];
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
localDate = LocalDate.parse(dateTimeStr, formatter);
|
||||
}
|
||||
if(Objects.nonNull(localDate) && !localDate.isBefore(startDate) && !localDate.isAfter(endDate)) {
|
||||
return true;
|
||||
}
|
||||
}catch(Exception e){
|
||||
log.error("{}文件命名格式错误,需确认",file.getAbsolutePath());
|
||||
WeatherLinkedDataLog linkedLog = new WeatherLinkedDataLog();
|
||||
linkedLog.setDataType(dataType);
|
||||
linkedLog.setLogContent(file.getAbsolutePath()+"文件命名格式错误,需确认");
|
||||
weatherLinkedDataLogMapper.insert(linkedLog);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (CollUtil.isNotEmpty(fileList)) {
|
||||
for (File file : fileList) {
|
||||
try {
|
||||
WeatherData weatherData = new WeatherData();
|
||||
weatherData.setFileName(file.getName());
|
||||
weatherData.setFileExt(file.getName().substring(file.getName().lastIndexOf(".")+1));
|
||||
weatherData.setDataSource(dataType);
|
||||
weatherData.setFilePath(file.getAbsolutePath());
|
||||
//获取文件数据开始日期
|
||||
LocalDateTime localDateTime = Grib2TimeReader.readValidTime(file.getAbsolutePath());
|
||||
weatherData.setDataStartTime(localDateTime);
|
||||
this.baseMapper.insert(weatherData);
|
||||
|
||||
WeatherLinkedDataLog linkedLog = new WeatherLinkedDataLog();
|
||||
linkedLog.setDataType(dataType);
|
||||
linkedLog.setLogContent(file.getAbsolutePath()+"关联成功");
|
||||
weatherLinkedDataLogMapper.insert(linkedLog);
|
||||
}catch (Exception e){
|
||||
log.error("关联{}气象数据文件出现错误,原因为:",file.getAbsolutePath(),e);
|
||||
WeatherLinkedDataLog linkedLog = new WeatherLinkedDataLog();
|
||||
linkedLog.setDataType(dataType);
|
||||
linkedLog.setLogContent(file.getAbsolutePath()+"关联失败,原因为:"+e.getMessage());
|
||||
weatherLinkedDataLogMapper.insert(linkedLog);
|
||||
}
|
||||
}
|
||||
WeatherLinkedDataLog linkedLog = new WeatherLinkedDataLog();
|
||||
linkedLog.setDataType(dataType);
|
||||
linkedLog.setLogContent("关联任务执行完毕!");
|
||||
weatherLinkedDataLogMapper.insert(linkedLog);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -831,16 +687,6 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
throw new RuntimeException("未找到气象数据:" + currentTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减六小时取yyyyMMddHH
|
||||
* @param targetTime
|
||||
* @return
|
||||
*/
|
||||
public String minus6HoursAndFormat(LocalDateTime targetTime) {
|
||||
LocalDateTime minus6Hours = targetTime.minusHours(6);
|
||||
return minus6Hours.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理结果数据
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ import cn.hutool.core.io.FileUtil;
|
|||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherFileSuffixEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherForecastDatasourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
|
|
@ -23,8 +20,6 @@ import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
|||
import org.jeecg.modules.base.mapper.WeatherTaskLogMapper;
|
||||
import org.jeecg.modules.base.mapper.WeatherTaskMapper;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
import org.jeecg.task.GraphcastWeatherTaskExec;
|
||||
import org.jeecg.task.PanGuWeatherTaskExec;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
|
@ -95,40 +90,34 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
throw new RuntimeException("此当前预测时间为参数的任务已存在");
|
||||
}
|
||||
//手动获取id
|
||||
String id = IdWorker.getIdStr();
|
||||
weatherTask.setId(id);
|
||||
weatherTask.setTaskStatus(WeatherTaskStatusEnum.NOT_STARTED.getKey());
|
||||
weatherTask.setTaskStatus(WeatherTaskStatusEnum.NOT_STARTED.getValue());
|
||||
this.save(weatherTask);
|
||||
if (WeatherForecastDatasourceEnum.LOCATION_FILE.getKey().equals(weatherTask.getDataSources())){
|
||||
try {
|
||||
MultipartFile file = weatherTask.getFile();
|
||||
//构造文件名称
|
||||
StringBuilder fileName = new StringBuilder();
|
||||
fileName.append(file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf(".")));
|
||||
fileName.append("_"+id+".");
|
||||
fileName.append(WeatherFileSuffixEnum.GRIB.getValue());
|
||||
fileName.append("_"+weatherTask.getId());
|
||||
fileName.append(file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")));
|
||||
|
||||
//文件保存目录
|
||||
StringBuilder filePath = new StringBuilder();
|
||||
filePath.append(this.systemStorageProperties.getPanguModelExecPath());
|
||||
filePath.append(File.separator);
|
||||
filePath.append(id);
|
||||
//文件地址
|
||||
File storageFile = new File(filePath+File.separator+fileName);
|
||||
//文件保存地址
|
||||
File storageFile = new File(this.systemStorageProperties.getForecastFileTmpPath()+File.separator+fileName);
|
||||
//如果不存在则创建
|
||||
if(!FileUtil.exist(filePath.toString())){
|
||||
FileUtil.mkdir(filePath.toString());
|
||||
if(!FileUtil.exist(storageFile)){
|
||||
FileUtil.mkdir(storageFile.getParent());
|
||||
}else{
|
||||
if (storageFile.exists()){
|
||||
storageFile.delete();
|
||||
}
|
||||
}
|
||||
file.transferTo(storageFile);
|
||||
weatherTask.setInputFile(fileName.toString());
|
||||
weatherTask.setInputFile(storageFile.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
this.save(weatherTask);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -138,7 +127,7 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
* @return
|
||||
*/
|
||||
@Override
|
||||
public WeatherTask getById(String id) {
|
||||
public WeatherTask getById(Integer id) {
|
||||
return this.baseMapper.selectById(id);
|
||||
}
|
||||
|
||||
|
|
@ -183,21 +172,21 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
//构造文件名称
|
||||
StringBuilder fileName = new StringBuilder();
|
||||
fileName.append(file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf(".")));
|
||||
fileName.append("_"+queryResult.getId()+".");
|
||||
fileName.append(WeatherFileSuffixEnum.GRIB.getValue());
|
||||
fileName.append("_"+weatherTask.getId());
|
||||
fileName.append(file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")));
|
||||
|
||||
//文件保存路径
|
||||
StringBuilder filePath = new StringBuilder();
|
||||
filePath.append(this.systemStorageProperties.getPanguModelExecPath());
|
||||
filePath.append(File.separator);
|
||||
filePath.append(fileName);
|
||||
|
||||
File storageFile = new File(filePath.toString());
|
||||
if (storageFile.exists()){
|
||||
storageFile.delete();
|
||||
//文件保存地址
|
||||
File storageFile = new File(this.systemStorageProperties.getForecastFileTmpPath()+File.separator+fileName);
|
||||
//如果不存在则创建
|
||||
if(!FileUtil.exist(storageFile)){
|
||||
FileUtil.mkdir(storageFile.getParent());
|
||||
}else{
|
||||
if (storageFile.exists()){
|
||||
storageFile.delete();
|
||||
}
|
||||
}
|
||||
file.transferTo(storageFile);
|
||||
queryResult.setInputFile(fileName.toString());
|
||||
queryResult.setInputFile(storageFile.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -214,18 +203,12 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void delete(List<String> ids) {
|
||||
public void delete(List<Integer> ids) {
|
||||
List<WeatherTask> weatherTasks = this.baseMapper.selectByIds(ids);
|
||||
if(CollUtil.isNotEmpty(weatherTasks)){
|
||||
for (WeatherTask weatherTask : weatherTasks) {
|
||||
if (Objects.nonNull(weatherTask) && StrUtil.isNotBlank(weatherTask.getInputFile())){
|
||||
String inputFileParent = "";
|
||||
if (WeatherDataSourceEnum.PANGU.getKey().equals(weatherTask.getPredictionModel())){
|
||||
inputFileParent = this.systemStorageProperties.getPanguModelExecPath();
|
||||
}else if (WeatherDataSourceEnum.GRAPHCAST.getKey().equals(weatherTask.getPredictionModel())) {
|
||||
inputFileParent = this.systemStorageProperties.getGraphcastModelExecPath();
|
||||
}
|
||||
File inputFile = new File(inputFileParent+File.separator+weatherTask.getInputFile());
|
||||
File inputFile = new File(weatherTask.getInputFile());
|
||||
if (inputFile.exists()){
|
||||
inputFile.delete();
|
||||
}
|
||||
|
|
@ -246,22 +229,18 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
* @param id
|
||||
*/
|
||||
@Override
|
||||
public void runTask(String id) {
|
||||
public void runTask(Integer id) {
|
||||
WeatherTask task = this.baseMapper.selectById(id);
|
||||
if(Objects.isNull(task)){
|
||||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
if (WeatherDataSourceEnum.PANGU.getKey().equals(task.getPredictionModel())){
|
||||
PanGuWeatherTaskExec exec = new PanGuWeatherTaskExec();
|
||||
exec.init(task,this,systemStorageProperties,weatherDataMapper);
|
||||
exec.setName("天气预测任务线程");
|
||||
exec.start();
|
||||
} else if (WeatherDataSourceEnum.GRAPHCAST.getKey().equals(task.getPredictionModel())) {
|
||||
GraphcastWeatherTaskExec exec = new GraphcastWeatherTaskExec();
|
||||
exec.init(task,this,systemStorageProperties,weatherDataMapper);
|
||||
exec.setName("天气预测任务线程");
|
||||
exec.start();
|
||||
if(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey().equals(task.getDataSources())){
|
||||
if(!FileUtil.exist(task.getInputFile())){
|
||||
throw new RuntimeException("此任务缺少模型预测需要的输入数据");
|
||||
}
|
||||
}
|
||||
task.setTaskStatus(WeatherTaskStatusEnum.WAITING.getValue());
|
||||
this.updateById(task);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -271,63 +250,11 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<WeatherTaskLog> getTaskLog(String taskId) {
|
||||
public List<WeatherTaskLog> getTaskLog(Integer taskId) {
|
||||
LambdaQueryWrapper<WeatherTaskLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WeatherTaskLog::getTaskId,taskId);
|
||||
queryWrapper.select(WeatherTaskLog::getCreateTime,WeatherTaskLog::getLogContent);
|
||||
queryWrapper.orderByAsc(WeatherTaskLog::getCreateTime);
|
||||
return this.weatherTaskLogMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存任务过程日志
|
||||
*
|
||||
* @param log
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void saveLog(WeatherTaskLog log) {
|
||||
this.weatherTaskLogMapper.insert(log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务运行状态
|
||||
*
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatus(String taskId, Integer taskStatus) {
|
||||
WeatherTask weatherTask = this.baseMapper.selectById(taskId);
|
||||
weatherTask.setTaskStatus(taskStatus);
|
||||
this.updateById(weatherTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id删除任务日志
|
||||
*
|
||||
* @param taskId
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void deleteTaskLog(String taskId) {
|
||||
LambdaQueryWrapper<WeatherTaskLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WeatherTaskLog::getTaskId,taskId);
|
||||
this.weatherTaskLogMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务状态为结束(完成或异常停止)
|
||||
* @param taskId
|
||||
* @param timeConsuming
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatusToCompleted(String taskId, Double timeConsuming,Integer taskStatus) {
|
||||
WeatherTask weatherTask = this.baseMapper.selectById(taskId);
|
||||
weatherTask.setTaskStatus(taskStatus);
|
||||
weatherTask.setTimeConsuming(timeConsuming);
|
||||
this.updateById(weatherTask);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,410 +0,0 @@
|
|||
package org.jeecg.task;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherForecastDatasourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.exception.JeecgFileUploadException;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.util.NcUtil;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 气象预测任务执行线程
|
||||
*/
|
||||
public class GraphcastWeatherTaskExec extends Thread{
|
||||
|
||||
private WeatherTask weatherTask;
|
||||
private WeatherTaskService weatherTaskService;
|
||||
private SystemStorageProperties systemStorageProperties;
|
||||
private WeatherDataMapper weatherDataMapper;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public void init(WeatherTask weatherTask,
|
||||
WeatherTaskService weatherTaskService,
|
||||
SystemStorageProperties systemStorageProperties,
|
||||
WeatherDataMapper weatherDataMapper){
|
||||
this.weatherTask = weatherTask;
|
||||
this.weatherTaskService = weatherTaskService;
|
||||
this.systemStorageProperties = systemStorageProperties;
|
||||
this.weatherDataMapper = weatherDataMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*/
|
||||
public void execute() {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
try{
|
||||
//修改任务状态为执行中
|
||||
this.weatherTaskService.updateTaskStatus(this.weatherTask.getId(), WeatherTaskStatusEnum.IN_OPERATION.getKey());
|
||||
//如果此任务已存在历史日志,先清除
|
||||
this.weatherTaskService.deleteTaskLog(this.weatherTask.getId());
|
||||
//执行模拟
|
||||
this.execSimulation();
|
||||
//执行完成
|
||||
this.execComplete(stopWatch,WeatherTaskStatusEnum.COMPLETED.getKey(), "");
|
||||
}catch (Exception e){
|
||||
String taskErrorLog = "任务执行失败,原因:"+e.getMessage();
|
||||
//执行失败
|
||||
this.execComplete(stopWatch,WeatherTaskStatusEnum.FAILURE.getKey(), taskErrorLog);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行完成
|
||||
* @param stopWatch
|
||||
* @param taskStatus
|
||||
* @param taskErrorLog
|
||||
*/
|
||||
private void execComplete(StopWatch stopWatch,Integer taskStatus,String taskErrorLog){
|
||||
//添加任务耗时
|
||||
stopWatch.stop();
|
||||
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
|
||||
double min = seconds/60D;
|
||||
BigDecimal bgMin = new BigDecimal(min);
|
||||
BigDecimal result = bgMin.setScale(2, RoundingMode.HALF_UP);
|
||||
this.weatherTaskService.updateTaskStatusToCompleted(this.weatherTask.getId(),result.doubleValue(),taskStatus);
|
||||
if (WeatherTaskStatusEnum.COMPLETED.getKey().equals(taskStatus)){
|
||||
String taskCompletedLog = "任务执行完成,耗时:"+result+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskCompletedLog));
|
||||
}else if (WeatherTaskStatusEnum.FAILURE.getKey().equals(taskStatus)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskErrorLog));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行模拟
|
||||
*/
|
||||
private void execSimulation(){
|
||||
try {
|
||||
//输入文件
|
||||
String inputFileName = this.weatherTask.getInputFile();
|
||||
//预测文件
|
||||
String outputFileName = "graphcast_output_"+this.weatherTask.getId()+".grib";
|
||||
//定义名称后缀,执行grib_set命令时去除,否则命名会冲突
|
||||
String gribFileSuffix = "_not_grib_set";
|
||||
//临时目录grib_copy,grib_set,python转换都在这个目录里,最后删除
|
||||
String tempDir = systemStorageProperties.getGraphcastModelExecPath()+File.separator+this.weatherTask.getId();
|
||||
if(!FileUtil.exist(tempDir)){
|
||||
FileUtil.mkdir(tempDir);
|
||||
}
|
||||
//预测气象数据
|
||||
this.forecast(inputFileName,outputFileName);
|
||||
//切割文件,6小时一个
|
||||
this.splitGrib(outputFileName,gribFileSuffix,tempDir);
|
||||
//适配flexpart
|
||||
// this.convertGrib(gribFileSuffix,tempDir);
|
||||
//保存文件数据入库
|
||||
this.saveGribInfoToDB(tempDir,this.getPanguWeatherPath(),gribFileSuffix);
|
||||
//删除预测文件
|
||||
// File inputFile = new File(systemStorageProperties.getGraphcastModelExecPath()+File.separator+inputFileName);
|
||||
// if (inputFile.exists()){
|
||||
// inputFile.delete();
|
||||
// }
|
||||
// //删除输出的总文件
|
||||
// File outputFile = new File(systemStorageProperties.getGraphcastModelExecPath()+File.separator+outputFileName);
|
||||
// if (outputFile.exists()){
|
||||
// outputFile.delete();
|
||||
// }
|
||||
// //适配flexpart结束,删除目录
|
||||
// if(FileUtil.exist(tempDir)){
|
||||
// FileUtil.del(tempDir);
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 盘古预测
|
||||
* @param inputFileAddr
|
||||
* @param outputFileAddr
|
||||
*/
|
||||
private void forecast(String inputFileAddr,String outputFileAddr){
|
||||
try {
|
||||
//处理开始日志
|
||||
String startLogFormat = "";
|
||||
String startTimeFormat = LocalDateTimeUtil.format(this.weatherTask.getStartDate(),"yyyy-MM-dd HH:mm:ss");
|
||||
String forecastTime = this.weatherTask.getLeadTime().toString();
|
||||
String forecastModel = WeatherDataSourceEnum.GRAPHCAST.getValue();
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
startLogFormat = "任务开始,本次使用预测模型为:%s,预测开始时间为:%s,预测时长为:%s,前置数据采用CDS在线数据";
|
||||
}else {
|
||||
startLogFormat = "任务开始,本次使用预测模型为:%s,预测开始时间为:%s,预测时长为:%s,前置数据采用离线数据:"+this.weatherTask.getInputFile();
|
||||
}
|
||||
String startLog = String.format(startLogFormat,forecastModel,startTimeFormat,forecastTime);
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),startLog));
|
||||
//处理运行命令
|
||||
List<String> command = new ArrayList<>();
|
||||
String aiModelsPath = systemStorageProperties.getGraphcastEnvPath()+File.separator+"ai-models";
|
||||
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
command.add(aiModelsPath);
|
||||
command.add("--input");
|
||||
command.add("cds");
|
||||
command.add("--date");
|
||||
command.add(LocalDateTimeUtil.format(this.weatherTask.getStartDate(),"yyyyMMdd"));
|
||||
command.add("--time");
|
||||
command.add(this.weatherTask.getStartTime().toString());
|
||||
command.add("--lead-time");
|
||||
command.add(this.weatherTask.getLeadTime().toString());
|
||||
command.add("--path");
|
||||
command.add(outputFileAddr);
|
||||
command.add("--assets");
|
||||
command.add("assets-graphcast");
|
||||
command.add("--class od graphcast");
|
||||
}else if (this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){
|
||||
//离线文件还需处理
|
||||
command.add(aiModelsPath);
|
||||
command.add("--file");
|
||||
command.add(inputFileAddr);
|
||||
command.add("--lead-time");
|
||||
command.add(this.weatherTask.getLeadTime().toString());
|
||||
command.add("--path");
|
||||
command.add(outputFileAddr);
|
||||
command.add("--assets");
|
||||
command.add("assets-graphcast");
|
||||
command.add("--class od graphcast");
|
||||
}
|
||||
String execLog = "执行任务命令,开始模拟,命令为:"+command;
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),execLog));
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.directory(new File(systemStorageProperties.getGraphcastModelExecPath()));
|
||||
processBuilder.redirectErrorStream(true);
|
||||
Process process = processBuilder.start();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
|
||||
//读取输出日志
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if(StrUtil.isNotBlank(line)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),line));
|
||||
}
|
||||
}
|
||||
//等待进程结束
|
||||
process.waitFor();
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("盘古模型预测异常",e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分割grib文件
|
||||
* @param outputFileAddr
|
||||
* @param gribFileSuffix
|
||||
* @param gribCopyTargetDir
|
||||
*/
|
||||
private void splitGrib(String outputFileAddr,String gribFileSuffix,String gribCopyTargetDir){
|
||||
try {
|
||||
String handleGribLog = "预测结束,开始处理grib文件,切割为6小时一个文件";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),handleGribLog));
|
||||
LocalDateTime startTime = this.weatherTask.getStartDate().atTime(this.weatherTask.getStartTime(), 0, 0);
|
||||
//grib_copy命令
|
||||
String gribCopyCommandPath = systemStorageProperties.getGraphcastEnvPath()+File.separator+"grib_copy";
|
||||
//grib_set命令
|
||||
String gribSetCommandPath = systemStorageProperties.getGraphcastEnvPath()+File.separator+"grib_set";
|
||||
//公用gribProcessBuilder
|
||||
ProcessBuilder gribProcessBuilder = new ProcessBuilder();
|
||||
gribProcessBuilder.directory(new File(systemStorageProperties.getGraphcastModelExecPath()));
|
||||
//把grib文件切割成每6小时一份
|
||||
int step = 6;
|
||||
int i=this.weatherTask.getStartTime();
|
||||
while(i <= this.weatherTask.getLeadTime()){
|
||||
String gribCopyFileAddr = gribCopyTargetDir+File.separator+"graphcast_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+gribFileSuffix+".grib";
|
||||
//切割grib文件命令
|
||||
List<String> gribCopyCommand = new ArrayList<>();
|
||||
gribCopyCommand.add(gribCopyCommandPath);
|
||||
gribCopyCommand.add("-w");
|
||||
gribCopyCommand.add("step="+i);
|
||||
gribCopyCommand.add(outputFileAddr);
|
||||
gribCopyCommand.add(gribCopyFileAddr);
|
||||
System.out.println("gribCopyCommand:"+gribCopyCommand);
|
||||
gribProcessBuilder.command(gribCopyCommand);
|
||||
Process gribCopyProcess = gribProcessBuilder.start();
|
||||
gribCopyProcess.waitFor();
|
||||
|
||||
//重新设置reftime信息
|
||||
String gribSetFileAddr = gribCopyTargetDir+File.separator+"graphcast_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+".grib";
|
||||
String date = LocalDateTimeUtil.format(startTime,"yyyyMMdd");
|
||||
String time = LocalDateTimeUtil.format(startTime,"HHmm");
|
||||
List<String> gribSetCommand = new ArrayList<>();
|
||||
gribSetCommand.add(gribSetCommandPath);
|
||||
gribSetCommand.add("-s");
|
||||
gribSetCommand.add("dataDate="+date+",dataTime="+time+",endStep="+0);
|
||||
gribSetCommand.add(gribCopyFileAddr);
|
||||
gribSetCommand.add(gribSetFileAddr);
|
||||
System.out.println("gribSetCommand:"+gribSetCommand);
|
||||
gribProcessBuilder.command(gribSetCommand);
|
||||
Process gribSetProcess = gribProcessBuilder.start();
|
||||
gribSetProcess.waitFor();
|
||||
i+=step;
|
||||
startTime = startTime.plusHours(step);
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("分割grib文件异常",e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换grib
|
||||
* @param gribFileSuffix
|
||||
* @param gribFilePath
|
||||
*/
|
||||
private void convertGrib(String gribFileSuffix,String gribFilePath){
|
||||
try {
|
||||
// python3 pangu_reformat.py --ini_dir /export/xe_bk_data/weather/pangu_format_script --output_dir /export/xe_bk_data/weather/pangu2 --input_dir /export/xe_bk_data/weather/pangu2 --start_date 20251120 --end_date 20251120
|
||||
//调用python脚本转换flexpart能识别的气象数据文件
|
||||
//删除带有_not_grib_set名称的文件,此文件还未设置reftime属性
|
||||
List<File> files = FileUtil.loopFiles(gribFilePath, file -> file.getName().contains(gribFileSuffix));
|
||||
if(!files.isEmpty()){
|
||||
for(File file:files){
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
String adapterGribLog = "文件切割结束,开始调用python脚本适配flexpart模型";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),adapterGribLog));
|
||||
//构建转换命令
|
||||
String convertScriptPath = systemStorageProperties.getPanguFormatScriptPath();
|
||||
String inputPath = systemStorageProperties.getPanguModelExecPath()+File.separator+this.weatherTask.getId();
|
||||
String outPutPath = inputPath;
|
||||
String startDate = this.weatherTask.getStartDate().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
String endDate = this.weatherTask.getStartDate().atTime(this.weatherTask.getStartTime(), 0, 0).plusHours(this.weatherTask.getLeadTime()).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
List<String> convertCommand = new ArrayList<>();
|
||||
convertCommand.add(systemStorageProperties.getFormatScriptPythonEnv()+File.separator+"python3");
|
||||
convertCommand.add("pangu_reformat.py");
|
||||
convertCommand.add("--ini_dir");
|
||||
convertCommand.add(convertScriptPath);
|
||||
convertCommand.add("--output_dir");
|
||||
convertCommand.add(outPutPath);
|
||||
convertCommand.add("--input_dir");
|
||||
convertCommand.add(inputPath);
|
||||
convertCommand.add("--start_date");
|
||||
convertCommand.add(startDate);
|
||||
convertCommand.add("--end_date");
|
||||
convertCommand.add(endDate);
|
||||
System.out.println("convertCommand:"+convertCommand);
|
||||
ProcessBuilder adapterGribProcessBuilder = new ProcessBuilder();
|
||||
adapterGribProcessBuilder.directory(new File(convertScriptPath));
|
||||
adapterGribProcessBuilder.command(convertCommand);
|
||||
Process adapterGribProcess = adapterGribProcessBuilder.start();
|
||||
adapterGribProcess.waitFor();
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("适配flexpart,转换grib异常",e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存生成的气象数据存储到数据库
|
||||
* @param inputPath
|
||||
* @param targetPath
|
||||
*/
|
||||
private void saveGribInfoToDB(String inputPath,String targetPath,String gribFileSuffix){
|
||||
String handleGribLog = "格式转换结束,处理grib文件数据入库";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),handleGribLog));
|
||||
|
||||
List<File> files = FileUtil.loopFiles(inputPath,file -> file.getName().startsWith("graphcast_") && !file.getName().contains(gribFileSuffix));
|
||||
List<File> targetFiles = new ArrayList<>();
|
||||
if(!files.isEmpty()){
|
||||
for(File file:files){
|
||||
File targetFile = new File(targetPath+File.separator+file.getName());
|
||||
FileUtil.move(file,targetFile,true);
|
||||
if(targetFile.exists()){
|
||||
targetFiles.add(targetFile);
|
||||
}
|
||||
}
|
||||
for(File targetFile:targetFiles){
|
||||
//获取文件数据开始日期
|
||||
String reftime = NcUtil.getReftime(targetFile.getAbsolutePath());
|
||||
if(StringUtils.isBlank(reftime)) {
|
||||
throw new JeecgFileUploadException("解析气象文件起始时间数据异常,此文件可能损坏");
|
||||
}
|
||||
//计算文件大小M
|
||||
BigDecimal divideVal = new BigDecimal("1024");
|
||||
BigDecimal bg = new BigDecimal(targetFile.length());
|
||||
BigDecimal fileSize = bg.divide(divideVal).divide(divideVal).setScale(2, RoundingMode.HALF_UP);
|
||||
//处理文件数据开始时间
|
||||
Instant instant = Instant.parse(reftime);
|
||||
LocalDateTime utcDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
|
||||
//计算文件MD5值
|
||||
String md5Val = "";
|
||||
try (InputStream is = new FileInputStream(targetFile.getAbsolutePath())) {
|
||||
md5Val = DigestUtils.md5Hex(is);
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(targetFile.getName()+"MD5值计算失败");
|
||||
}
|
||||
//校验文件是否存在,存在删除从新新增
|
||||
LambdaQueryWrapper<WeatherData> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WeatherData::getFileName,targetFile.getName());
|
||||
WeatherData queryResult = weatherDataMapper.selectOne(queryWrapper);
|
||||
if(Objects.nonNull(queryResult)){
|
||||
weatherDataMapper.deleteById(queryResult.getId());
|
||||
}
|
||||
//构建文件信息
|
||||
WeatherData weatherData = new WeatherData();
|
||||
weatherData.setFileName(targetFile.getName());
|
||||
weatherData.setFileSize(fileSize.doubleValue());
|
||||
weatherData.setFileExt(targetFile.getName().substring(targetFile.getName().lastIndexOf(".")+1));
|
||||
weatherData.setDataStartTime(utcDateTime);
|
||||
weatherData.setDataSource(weatherTask.getPredictionModel());
|
||||
weatherData.setFilePath(targetFile.getAbsolutePath());
|
||||
weatherData.setMd5Value(md5Val);
|
||||
weatherData.setShareTotal(1);
|
||||
weatherDataMapper.insert(weatherData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取盘古数据存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getPanguWeatherPath(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.systemStorageProperties.getRootPath());
|
||||
sb.append(File.separator);
|
||||
sb.append(this.systemStorageProperties.getPangu());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Graphcast数据存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getGraphcastWeatherPath(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.systemStorageProperties.getRootPath());
|
||||
sb.append(File.separator);
|
||||
sb.append(this.systemStorageProperties.getGraphcast());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,438 +0,0 @@
|
|||
package org.jeecg.task;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherForecastDatasourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.exception.JeecgFileUploadException;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.util.NcUtil;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 气象预测任务执行线程
|
||||
*/
|
||||
@Slf4j
|
||||
public class PanGuWeatherTaskExec extends Thread{
|
||||
|
||||
private WeatherTask weatherTask;
|
||||
private WeatherTaskService weatherTaskService;
|
||||
private SystemStorageProperties systemStorageProperties;
|
||||
private WeatherDataMapper weatherDataMapper;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public void init(WeatherTask weatherTask,
|
||||
WeatherTaskService weatherTaskService,
|
||||
SystemStorageProperties systemStorageProperties,
|
||||
WeatherDataMapper weatherDataMapper){
|
||||
this.weatherTask = weatherTask;
|
||||
this.weatherTaskService = weatherTaskService;
|
||||
this.systemStorageProperties = systemStorageProperties;
|
||||
this.weatherDataMapper = weatherDataMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*/
|
||||
public void execute() {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
try{
|
||||
//修改任务状态为执行中
|
||||
this.weatherTaskService.updateTaskStatus(this.weatherTask.getId(), WeatherTaskStatusEnum.IN_OPERATION.getKey());
|
||||
//如果此任务已存在历史日志,先清除
|
||||
this.weatherTaskService.deleteTaskLog(this.weatherTask.getId());
|
||||
//执行模拟
|
||||
this.execSimulation();
|
||||
//执行完成
|
||||
this.execComplete(stopWatch,WeatherTaskStatusEnum.COMPLETED.getKey(), "");
|
||||
}catch (Exception e){
|
||||
String taskErrorLog = "任务执行失败,原因:"+e.getMessage();
|
||||
//执行失败
|
||||
this.execComplete(stopWatch,WeatherTaskStatusEnum.FAILURE.getKey(), taskErrorLog);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行完成
|
||||
* @param stopWatch
|
||||
* @param taskStatus
|
||||
* @param taskErrorLog
|
||||
*/
|
||||
private void execComplete(StopWatch stopWatch,Integer taskStatus,String taskErrorLog){
|
||||
//添加任务耗时
|
||||
stopWatch.stop();
|
||||
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
|
||||
double min = seconds/60D;
|
||||
BigDecimal bgMin = new BigDecimal(min);
|
||||
BigDecimal result = bgMin.setScale(2, RoundingMode.HALF_UP);
|
||||
this.weatherTaskService.updateTaskStatusToCompleted(this.weatherTask.getId(),result.doubleValue(),taskStatus);
|
||||
if (WeatherTaskStatusEnum.COMPLETED.getKey().equals(taskStatus)){
|
||||
String taskCompletedLog = "任务执行完成,耗时:"+result+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskCompletedLog));
|
||||
}else if (WeatherTaskStatusEnum.FAILURE.getKey().equals(taskStatus)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskErrorLog));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行模拟
|
||||
*/
|
||||
private void execSimulation(){
|
||||
try {
|
||||
//输入文件
|
||||
String inputFileName = this.weatherTask.getInputFile();
|
||||
//预测文件
|
||||
String outputFileName = "panguweather_output_"+this.weatherTask.getId()+".grib";
|
||||
//定义名称后缀,执行grib_set命令时去除,否则命名会冲突
|
||||
String gribFileSuffix = "_not_grib_set";
|
||||
|
||||
//临时目录grib_copy,grib_set,python转换的输入都在这个目录里,最后删除
|
||||
String inputPath = systemStorageProperties.getPanguModelExecPath()+File.separator+this.weatherTask.getId();
|
||||
if(!FileUtil.exist(inputPath)){
|
||||
FileUtil.mkdir(inputPath);
|
||||
}
|
||||
//python转换的输出在这里,最后删除
|
||||
String outputPath = inputPath+File.separator+"formatResult";
|
||||
if(!FileUtil.exist(outputPath)){
|
||||
FileUtil.mkdir(outputPath);
|
||||
}
|
||||
//预测气象数据
|
||||
this.forecast(inputFileName,outputFileName);
|
||||
//切割文件,6小时一个
|
||||
this.splitGrib(outputFileName,gribFileSuffix,inputPath);
|
||||
//适配flexpart
|
||||
this.convertGrib(gribFileSuffix,inputPath,outputPath);
|
||||
//保存文件数据入库
|
||||
this.saveGribInfoToDB(inputPath,this.getPanguWeatherPath());
|
||||
//删除预测文件
|
||||
File inputFile = new File(systemStorageProperties.getPanguModelExecPath()+File.separator+inputFileName);
|
||||
if (inputFile.exists()){
|
||||
inputFile.delete();
|
||||
}
|
||||
// 删除输出的总文件
|
||||
File outputFile = new File(systemStorageProperties.getPanguModelExecPath()+File.separator+outputFileName);
|
||||
if (outputFile.exists()){
|
||||
outputFile.delete();
|
||||
}
|
||||
//适配flexpart结束,删除目录
|
||||
if(FileUtil.exist(inputPath)){
|
||||
FileUtil.del(inputPath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 盘古预测
|
||||
* @param inputFileAddr
|
||||
* @param outputFileAddr
|
||||
*/
|
||||
private void forecast(String inputFileAddr,String outputFileAddr){
|
||||
try {
|
||||
//处理开始日志
|
||||
String startLogFormat = "";
|
||||
String startTimeFormat = LocalDateTimeUtil.format(this.weatherTask.getStartDate(),"yyyy-MM-dd HH:mm:ss");
|
||||
String forecastTime = this.weatherTask.getLeadTime().toString();
|
||||
String forecastModel = WeatherDataSourceEnum.PANGU.getValue();
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
startLogFormat = "任务开始,本次使用预测模型为:%s,预测开始时间为:%s,预测时长为:%s,前置数据采用CDS在线数据";
|
||||
}else {
|
||||
startLogFormat = "任务开始,本次使用预测模型为:%s,预测开始时间为:%s,预测时长为:%s,前置数据采用离线数据:"+this.weatherTask.getInputFile();
|
||||
}
|
||||
String startLog = String.format(startLogFormat,forecastModel,startTimeFormat,forecastTime);
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),startLog));
|
||||
//处理运行命令
|
||||
List<String> command = new ArrayList<>();
|
||||
String aiModelsPath = systemStorageProperties.getPanguEnvPath()+File.separator+"ai-models";
|
||||
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
command.add(aiModelsPath);
|
||||
command.add("--input");
|
||||
command.add("cds");
|
||||
command.add("--date");
|
||||
command.add(LocalDateTimeUtil.format(this.weatherTask.getStartDate(),"yyyyMMdd"));
|
||||
command.add("--time");
|
||||
command.add(this.weatherTask.getStartTime().toString());
|
||||
command.add("--lead-time");
|
||||
command.add(this.weatherTask.getLeadTime().toString());
|
||||
command.add("--path");
|
||||
command.add(outputFileAddr);
|
||||
command.add("--assets");
|
||||
command.add("assets-panguweather");
|
||||
command.add("panguweather");
|
||||
}else if (this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){
|
||||
//离线文件还需处理
|
||||
command.add(aiModelsPath);
|
||||
command.add("--file");
|
||||
command.add(inputFileAddr);
|
||||
command.add("--lead-time");
|
||||
command.add(this.weatherTask.getLeadTime().toString());
|
||||
command.add("--path");
|
||||
command.add(outputFileAddr);
|
||||
command.add("--assets");
|
||||
command.add("assets-panguweather");
|
||||
command.add("panguweather");
|
||||
}
|
||||
String execLog = "执行任务命令,开始模拟,命令为:"+command;
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),execLog));
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.directory(new File(systemStorageProperties.getPanguModelExecPath()));
|
||||
processBuilder.redirectErrorStream(true);
|
||||
Process process = processBuilder.start();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
|
||||
//读取输出日志
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if(StrUtil.isNotBlank(line)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),line));
|
||||
}
|
||||
}
|
||||
//等待进程结束
|
||||
process.waitFor();
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("盘古模型预测异常",e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分割grib文件
|
||||
* @param outputFileAddr
|
||||
* @param gribFileSuffix
|
||||
* @param gribCopyTargetDir
|
||||
*/
|
||||
private void splitGrib(String outputFileAddr,String gribFileSuffix,String gribCopyTargetDir){
|
||||
try {
|
||||
String handleGribLog = "预测结束,开始处理grib文件,切割为6小时一个文件";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),handleGribLog));
|
||||
LocalDateTime startTime = this.weatherTask.getStartDate().atTime(this.weatherTask.getStartTime(), 0, 0);
|
||||
//grib_copy命令
|
||||
String gribCopyCommandPath = systemStorageProperties.getPanguEnvPath()+File.separator+"grib_copy";
|
||||
//grib_set命令
|
||||
String gribSetCommandPath = systemStorageProperties.getPanguEnvPath()+File.separator+"grib_set";
|
||||
//公用gribProcessBuilder
|
||||
ProcessBuilder gribProcessBuilder = new ProcessBuilder();
|
||||
gribProcessBuilder.directory(new File(systemStorageProperties.getPanguModelExecPath()));
|
||||
//把grib文件切割成每6小时一份
|
||||
int step = 6;
|
||||
int i=this.weatherTask.getStartTime();
|
||||
while(i <= this.weatherTask.getLeadTime()){
|
||||
String gribCopyFileAddr = gribCopyTargetDir+File.separator+"panguweather_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+gribFileSuffix+".grib";
|
||||
//切割grib文件命令
|
||||
List<String> gribCopyCommand = new ArrayList<>();
|
||||
gribCopyCommand.add(gribCopyCommandPath);
|
||||
gribCopyCommand.add("-w");
|
||||
gribCopyCommand.add("step="+i);
|
||||
gribCopyCommand.add(outputFileAddr);
|
||||
gribCopyCommand.add(gribCopyFileAddr);
|
||||
gribProcessBuilder.command(gribCopyCommand);
|
||||
log.info("gribCopyCommand:{}", gribCopyCommand);
|
||||
Process gribCopyProcess = gribProcessBuilder.start();
|
||||
gribCopyProcess.waitFor();
|
||||
|
||||
//重新设置reftime信息
|
||||
String gribSetFileAddr = gribCopyTargetDir+File.separator+"panguweather_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+".grib";
|
||||
String date = LocalDateTimeUtil.format(startTime,"yyyyMMdd");
|
||||
String time = LocalDateTimeUtil.format(startTime,"HHmm");
|
||||
List<String> gribSetCommand = new ArrayList<>();
|
||||
gribSetCommand.add(gribSetCommandPath);
|
||||
gribSetCommand.add("-s");
|
||||
gribSetCommand.add("dataDate="+date+",dataTime="+time+",endStep="+0);
|
||||
gribSetCommand.add(gribCopyFileAddr);
|
||||
gribSetCommand.add(gribSetFileAddr);
|
||||
gribProcessBuilder.command(gribSetCommand);
|
||||
log.info("gribSetCommand:{}", gribSetCommand);
|
||||
Process gribSetProcess = gribProcessBuilder.start();
|
||||
gribSetProcess.waitFor();
|
||||
i+=step;
|
||||
startTime = startTime.plusHours(step);
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("分割grib文件异常",e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换grib
|
||||
* @param gribFileSuffix
|
||||
* @param inputPath
|
||||
* @param outputPath
|
||||
*/
|
||||
private void convertGrib(String gribFileSuffix,String inputPath,String outputPath){
|
||||
try {
|
||||
// python3 pangu_reformat.py --ini_dir /export/xe_bk_data/weather/pangu_format_script --output_dir /export/xe_bk_data/weather/pangu2 --input_dir /export/xe_bk_data/weather/pangu2 --start_date 20251120 --end_date 20251120
|
||||
//调用python脚本转换flexpart能识别的气象数据文件
|
||||
//删除带有_not_grib_set名称的文件,此文件还未设置reftime属性
|
||||
List<File> files = FileUtil.loopFiles(inputPath, file -> file.getName().contains(gribFileSuffix));
|
||||
if(!files.isEmpty()){
|
||||
for(File file:files){
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
//构建转换命令
|
||||
String convertScriptPath = systemStorageProperties.getPanguFormatScriptPath();
|
||||
String startDate = this.weatherTask.getStartDate().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
String endDate = this.weatherTask.getStartDate().atTime(this.weatherTask.getStartTime(), 0, 0).plusHours(this.weatherTask.getLeadTime()).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
List<String> convertCommand = new ArrayList<>();
|
||||
convertCommand.add(systemStorageProperties.getFormatScriptPythonEnv()+File.separator+"python3");
|
||||
convertCommand.add("pangu_reformat.py");
|
||||
convertCommand.add("--ini_dir");
|
||||
convertCommand.add(convertScriptPath);
|
||||
convertCommand.add("--output_dir");
|
||||
convertCommand.add(outputPath);
|
||||
convertCommand.add("--input_dir");
|
||||
convertCommand.add(inputPath);
|
||||
convertCommand.add("--start_date");
|
||||
convertCommand.add(startDate);
|
||||
convertCommand.add("--end_date");
|
||||
convertCommand.add(endDate);
|
||||
|
||||
String adapterGribLog = "文件切割结束,开始调用python脚本适配flexpart模型,执行命令为:"+convertCommand;
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),adapterGribLog));
|
||||
|
||||
ProcessBuilder adapterGribProcessBuilder = new ProcessBuilder();
|
||||
adapterGribProcessBuilder.directory(new File(convertScriptPath));
|
||||
adapterGribProcessBuilder.command(convertCommand);
|
||||
Process adapterGribProcess = adapterGribProcessBuilder.start();
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(adapterGribProcess.getInputStream(), "UTF-8"));
|
||||
//读取输出日志
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if(StrUtil.isNotBlank(line)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),line));
|
||||
}
|
||||
}
|
||||
boolean finished = adapterGribProcess.waitFor((5*3600),TimeUnit.SECONDS);
|
||||
if (!finished) {
|
||||
// 如果在指定时间内进程没有结束,则强制销毁它
|
||||
adapterGribProcess.destroyForcibly(); // 强制终止进程
|
||||
String destroyForciblyLog = "python脚本适配flexpart模型进程超时,终止进程";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),destroyForciblyLog));
|
||||
} else {
|
||||
int exitCode = adapterGribProcess.exitValue();
|
||||
String progressFinishedLog = "python脚本适配flexpart模型进程结束,exitCode:"+exitCode;
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),progressFinishedLog));
|
||||
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("适配flexpart,转换grib异常",e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存生成的气象数据存储到数据库
|
||||
* @param inputPath
|
||||
* @param targetPath
|
||||
*/
|
||||
private void saveGribInfoToDB(String inputPath,String targetPath){
|
||||
String handleGribLog = "格式转换结束,处理grib文件数据入库";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),handleGribLog));
|
||||
|
||||
List<File> files = FileUtil.loopFiles(inputPath,file -> file.getName().startsWith("pangu_"));
|
||||
List<File> targetFiles = new ArrayList<>();
|
||||
if(!files.isEmpty()){
|
||||
for(File file:files){
|
||||
File targetFile = new File(targetPath+File.separator+file.getName());
|
||||
FileUtil.move(file,targetFile,true);
|
||||
if(targetFile.exists()){
|
||||
targetFiles.add(targetFile);
|
||||
}
|
||||
}
|
||||
for(File targetFile:targetFiles){
|
||||
//获取文件数据开始日期
|
||||
String reftime = NcUtil.getReftime(targetFile.getAbsolutePath());
|
||||
if(StringUtils.isBlank(reftime)) {
|
||||
throw new JeecgFileUploadException("解析气象文件起始时间数据异常,此文件可能损坏");
|
||||
}
|
||||
//计算文件大小M
|
||||
BigDecimal divideVal = new BigDecimal("1024");
|
||||
BigDecimal bg = new BigDecimal(targetFile.length());
|
||||
BigDecimal fileSize = bg.divide(divideVal).divide(divideVal).setScale(2, RoundingMode.HALF_UP);
|
||||
//处理文件数据开始时间
|
||||
Instant instant = Instant.parse(reftime);
|
||||
LocalDateTime utcDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
|
||||
//计算文件MD5值
|
||||
String md5Val = "";
|
||||
try (InputStream is = new FileInputStream(targetFile.getAbsolutePath())) {
|
||||
md5Val = DigestUtils.md5Hex(is);
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(targetFile.getName()+"MD5值计算失败");
|
||||
}
|
||||
//校验文件是否存在,存在删除从新新增
|
||||
LambdaQueryWrapper<WeatherData> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WeatherData::getFileName,targetFile.getName());
|
||||
WeatherData queryResult = weatherDataMapper.selectOne(queryWrapper);
|
||||
if(Objects.nonNull(queryResult)){
|
||||
weatherDataMapper.deleteById(queryResult.getId());
|
||||
}
|
||||
//构建文件信息
|
||||
WeatherData weatherData = new WeatherData();
|
||||
weatherData.setFileName(targetFile.getName());
|
||||
weatherData.setFileSize(fileSize.doubleValue());
|
||||
weatherData.setFileExt(targetFile.getName().substring(targetFile.getName().lastIndexOf(".")+1));
|
||||
weatherData.setDataStartTime(utcDateTime);
|
||||
weatherData.setDataSource(weatherTask.getPredictionModel());
|
||||
weatherData.setFilePath(targetFile.getAbsolutePath());
|
||||
weatherData.setMd5Value(md5Val);
|
||||
weatherData.setShareTotal(1);
|
||||
weatherDataMapper.insert(weatherData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取盘古数据存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getPanguWeatherPath(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.systemStorageProperties.getRootPath());
|
||||
sb.append(File.separator);
|
||||
sb.append(this.systemStorageProperties.getPangu());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Graphcast数据存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getGraphcastWeatherPath(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.systemStorageProperties.getRootPath());
|
||||
sb.append(File.separator);
|
||||
sb.append(this.systemStorageProperties.getGraphcast());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.jeecg.common.base.BaseMap;
|
||||
import org.jeecg.common.constant.GlobalConstants;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.transport.consumer.TranTaskMessageConsumerHandler;
|
||||
import org.jeecg.rebuild.consumer.RebuildTaskConsumerHandler;
|
||||
import org.jeecg.transport.consumer.TranTaskConsumerHandler;
|
||||
import org.jeecg.weather.consumer.WeatherTaskConsumerHandler;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
|
@ -23,7 +25,9 @@ import java.net.UnknownHostException;
|
|||
public class JeecgConsumerCloudApplication extends SpringBootServletInitializer implements CommandLineRunner {
|
||||
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final TranTaskMessageConsumerHandler tranTaskMsgConsumerHandler;
|
||||
private final TranTaskConsumerHandler tranTaskConsumerHandler;
|
||||
private final RebuildTaskConsumerHandler rebuildTaskConsumerHandler;
|
||||
private final WeatherTaskConsumerHandler weatherTaskConsumerHandler;
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||
|
|
@ -51,6 +55,8 @@ public class JeecgConsumerCloudApplication extends SpringBootServletInitializer
|
|||
params.put(GlobalConstants.HANDLER_NAME, GlobalConstants.LODER_ROUDER_HANDLER);
|
||||
//刷新网关
|
||||
redisTemplate.convertAndSend(GlobalConstants.REDIS_TOPIC_NAME, params);
|
||||
tranTaskMsgConsumerHandler.startConsumerThread();
|
||||
tranTaskConsumerHandler.startConsumerThread();
|
||||
rebuildTaskConsumerHandler.startConsumerThread();
|
||||
weatherTaskConsumerHandler.startConsumerThread();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user