1.完成输运任务基础接口2.修改气象数据部分接口

This commit is contained in:
panbaolin 2025-11-03 11:50:09 +08:00
parent 20625bdcad
commit f52789064d
26 changed files with 1037 additions and 84 deletions

View File

@ -189,9 +189,6 @@ public interface CommonConstant {
*/
String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr";
/** 系统通告消息状态1=已发布 */
String ANNOUNCEMENT_SEND_STATUS_1 = "1";
/**POST请求*/
String HTTP_POST = "POST";
@ -210,9 +207,6 @@ public interface CommonConstant {
/**String 类型的空值*/
String STRING_NULL = "null";
/**前端vue3版本Header参数名*/
String VERSION="X-Version";
/**存储在线程变量里的动态表名*/
String DYNAMIC_TABLE_NAME="DYNAMIC_TABLE_NAME";
@ -276,4 +270,8 @@ public interface CommonConstant {
* 缓存用户最后一次收到消息通知的时间 KEY
*/
String CACHE_KEY_USER_LAST_ANNOUNT_TIME_1HOUR = "sys:cache:userinfo:user_last_annount_time::%s";
String ALL_STATIONS = "stations";
String ALL_NUCLEARFACILITY = "nuclearfacility";
}

View File

@ -6,7 +6,7 @@ package org.jeecg.common.constant.enums;
public enum SourceRebuildTaskStatusEnum {
/**
* 未开始
* 执行失败
*/
ERROR(-1),
/**

View File

@ -0,0 +1,26 @@
package org.jeecg.common.constant.enums;
/**
* 输运任务模式说明枚举
*/
public enum TransportTaskModeEnum {
/**
* 正向
*/
FORWARD(-1),
/**
* 反向
*/
BACK_FORWARD(1);
private Integer key;
TransportTaskModeEnum(Integer key) {
this.key = key;
}
public Integer getKey(){
return this.key;
}
}

View File

@ -1,34 +1,34 @@
package org.jeecg.common.constant.enums;
/**
* 输运任务状态说明枚举
* 输运模拟任务状态说明枚举
*/
public enum TransportTaskStatusEnum {
/**
* 失败
* 执行失败
*/
FAILURE(-1),
ERROR(-1),
/**
* 未开始
*/
NOT_START(1),
NOT_STARTED(0),
/**
* 行中
* 行中
*/
RUNNING(2),
IN_OPERATION(1),
/**
* 已完成
*/
COMPLETED(3);
COMPLETED(2);
private Integer key;
private Integer value;
TransportTaskStatusEnum(Integer key) {
this.key = key;
TransportTaskStatusEnum(Integer value) {
this.value = value;
}
public Integer getKey(){
return this.key;
public Integer getValue(){
return this.value;
}
}

View File

@ -26,8 +26,12 @@ public enum WeatherDataSourceEnum {
/**
* T1H
*/
T1H(5,"T1H");
FNL(5,"FNL"),
/**
* T1H
*/
T1H(6,"T1H");
private Integer key;
private String value;

View File

@ -30,14 +30,19 @@ public class SystemStorageProperties {
private String ncep;
/**
* T1H数据存储路径
* graphcast模型预测数据存储路径
*/
private String graphcast;
/**
* t1h数据存储路径
*/
private String t1h;
/**
* graphcast模型预测数据存储路径
* fnl数据存储路径
*/
private String graphcast;
private String fnl;
/**
* WRF数据存储路径

View File

@ -0,0 +1,117 @@
package org.jeecg.common.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "transport-simulation")
public class TransportSimulationProperties {
/**
* 模型输出路径
*/
private String outputPath;
/**
* 参数配置文件路径和fnl气象数据有关系
*/
private String fnlParamConfigPath;
/**
* 参数配置文件路径和pangu气象数据有关系
*/
private String panguParamConfigPath;
/**
* 参数配置文件路径和graphcast气象数据有关系
*/
private String graphcastParamConfigPath;
/**
* 参数配置文件路径和cra40气象数据有关系
*/
private String cra40ParamConfigPath;
/**
* 参数配置文件路径和ncep气象数据有关系
*/
private String ncepParamConfigPath;
/**
* 参数配置文件路径和T1H气象数据有关系
*/
private String t1hParamConfigPath;
/**
* 台站数据配置文件路径和fnl气象数据有关系
*/
private String fnlStationsConfigPath;
/**
* 台站数据配置文件路径和pangu气象数据有关系
*/
private String panguStationsConfigPath;
/**
* 台站数据配置文件路径和graphcast气象数据有关系
*/
private String graphcastStationsConfigPath;
/**
* 台站数据配置文件路径和cra40气象数据有关系
*/
private String cra40StationsConfigPath;
/**
* 台站数据配置文件路径和ncep气象数据有关系
*/
private String ncepStationsConfigPath;
/**
* 台站数据配置文件路径和T1H气象数据有关系
*/
private String t1hStationsConfigPath;
/**
* 正演脚本路径和fnl气象数据有关系
*/
private String fnlForwardScriptPath;
/**
* 正演脚本路径和pangu气象数据有关系
*/
private String panguForwardScriptPath;
/**
* 正演脚本路径和graphcast气象数据有关系
*/
private String graphcastForwardScriptPath;
/**
* 正演脚本路径和cra40气象数据有关系
*/
private String cra40ForwardScriptPath;
/**
* 正演脚本路径和ncep气象数据有关系
*/
private String ncepForwardScriptPath;
/**
* 正演脚本路径和T1H气象数据有关系
*/
private String t1hForwardScriptPath;
/**
* 反演脚本路径和fnl气象数据有关系
*/
private String fnlBackwardScriptPath;
/**
* 反演脚本路径和pangu气象数据有关系
*/
private String panguBackwardScriptPath;
/**
* 反演脚本路径和graphcast气象数据有关系
*/
private String graphcastBackwardScriptPath;
/**
* 反演脚本路径和cra40气象数据有关系
*/
private String cra40BackwardScriptPath;
/**
* 反演脚本路径和ncep气象数据有关系
*/
private String ncepBackwardScriptPath;
/**
* 反演脚本路径和T1H气象数据有关系
*/
private String t1hBackwardScriptPath;
}

View File

@ -146,7 +146,7 @@ public class SourceRebuildTask implements Serializable {
*/
@Null(message = "耗时必须为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "time_consuming")
private Integer timeConsuming;
private Double timeConsuming;
/**
* 半衰期

View File

@ -60,12 +60,26 @@ public class TransportTask{
private Integer taskPprogress;
/**
* 任务状态-1执行失败1未开始2运行中3已完成
* 任务状态-1执行失败0未开始1运行中2已完成
*/
@Null(message = "任务状态必须为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "task_status")
private Integer taskStatus;
/**
* 气象数据类型1-盘古模型2-graphcast3-cra404-ncep
*/
@NotNull(message = "气象数据类型不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "use_met_type")
private Integer useMetType;
/**
* 耗时分钟
*/
@Null(message = "耗时必须为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "time_consuming")
private Double timeConsuming;
/**
* 创建人
*/
@ -94,6 +108,41 @@ public class TransportTask{
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 模拟开始时间
*/
@NotNull(message = "模拟开始时间不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "start_time")
private Date startTime;
/**
* 模拟结束时间
*/
@NotNull(message = "模拟结束时间不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "end_time")
private Date endTime;
/**
* 释放下部高度
*/
@NotNull(message = "释放下部高度不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "z1")
private Double z1;
/**
* 释放上部高度
*/
@NotNull(message = "释放上部高度不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "z2")
private Double z2;
/**
* 粒子数量
*/
@NotNull(message = "粒子数量不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "particle_count")
private Integer particleCount;
@Valid
@TableField(exist = false)
List<TransportTaskChild> childList;

View File

@ -5,6 +5,7 @@ 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 jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
@ -35,46 +36,32 @@ public class TransportTaskChild{
private Integer taskId;
/**
* 模拟开始时间
* 台站编目
*/
@NotNull(message = "模拟开始时间不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "start_time")
private Date startTime;
@NotBlank(message = "释放量不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "station_code")
private String stationCode;
/**
* 模拟结束时间
* 经度1
*/
@NotNull(message = "模拟结束时间不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "end_time")
private Date endTime;
@NotNull(message = "经度不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "lon")
private Double lon;
/**
* 释放下部高度
* 纬度1
*/
@NotNull(message = "释放下部高度不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "z1")
private Double z1;
@NotNull(message = "度不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "lat")
private Double lat;
/**
* 释放上部高度
* 释放
*/
@NotNull(message = "释放上部高度不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "z2")
private Double z2;
/**
* 粒子数量
*/
@NotNull(message = "粒子数量不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "particle_count")
private Integer particleCount;
/**
* 物种编号
*/
@NotNull(message = "物种编号不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "specnum_rel")
private Integer specnumRel;
@NotNull(message = "释放量不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "release_amount")
private String releaseAmount;
/**
* 创建人

View File

@ -8,7 +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 +20,15 @@ public class TransportTaskLog implements Serializable {
/**
* ID
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "ID")
private String id;
@TableId(type = IdType.AUTO)
@Schema(description = "id")
private Integer id;
/**
* 任务表主键
*/
@TableField(value = "task_id")
private String taskId;
private Integer taskId;
/**
* 任务运行过程日志
@ -40,6 +40,5 @@ public class TransportTaskLog implements Serializable {
* 创建时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
private Date createTime;
}

View File

@ -51,7 +51,7 @@ public class WeatherData implements Serializable {
private LocalDateTime dataStartTime;
/**
* 数据来源1-盘古模型2-graphcast3-cra404-ncep
* 数据来源1-盘古模型2-graphcast3-cra404-ncep,5-t1h,6-fnl
*/
@TableField(value = "data_source")
private Integer dataSource;

View File

@ -0,0 +1,137 @@
package org.jeecg.modules.base.entity.configuration;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("CONFIGURATION.GARDS_NUCLEARFACILITY")
public class GardsNuclearfacility implements Serializable {
/**
* 核设施id
*/
@TableField(value = "FACILITY_ID")
private Integer facilityId;
/**
* 核设施名称
*/
@TableField(value = "FACILITY_NAME")
private String facilityName;
/**
* 核设施类型
*/
@TableField(value = "TYPE")
private String type;
/**
* 地点
*/
@TableField(value = "LOCATION")
private String location;
/**
* 经度
*/
@TableField(value = "LONGITUDE")
private String longitude;
/**
* 纬度
*/
@TableField(value = "LATITUDE")
private String latitude;
/**
* 状态
*/
@TableField(value = "STATUS")
private String status;
/**
* 修建时间
*/
@TableField(value = "BUILDDATE")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date buildDate;
/**
* 临界时间
*/
@TableField(value = "CRITICALITYDATE")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date criticalityDate;
/**
* 退休时间
*/
@TableField(value = "RETIREDATE")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date retireDate;
/**
* 网格工程日期
*/
@TableField(value = "GRIDCONEETIONDATE")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date gridconeetionDate;
/**
* 销售公司
*/
@TableField(value = "VENDOR")
private String vendor;
/**
* 拥有者
*/
@TableField(value = "OWNER")
private String owner;
/**
* 操作人员
*/
@TableField(value = "OPERARTOR")
private String operartor;
/**
* 容量
*/
@TableField(value = "CAPACITYGROSS")
private Integer capacitygross;
/**
* 容量集
*/
@TableField(value = "CAPACITYNET")
private Integer capacitynet;
/**
* 热容量
*/
@TableField(value = "CAPACITYTHERMAL")
private Integer capacitythermal;
/**
* 活动时间
*/
@TableField(value = "ACTIVITY_DAY")
private Integer activityDay;
/**
* 活动年份
*/
@TableField(value = "ACTIVITY_YEAR")
private Integer activityYear;
}

View File

@ -0,0 +1,103 @@
package org.jeecg.modules.base.entity.configuration;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName(value = "CONFIGURATION.GARDS_STATIONS")
public class GardsStations implements Serializable {
/**
* 台站id
*/
@TableId(type = IdType.INPUT)
private Integer stationId;
/**
* 台站编码
*/
@TableField(value = "STATION_CODE")
private String stationCode;
/**
* 城市编码
*/
@TableField(value = "COUNTRY_CODE")
private String countryCode;
/**
* 台站类型
*/
@TableField(value = "TYPE")
private String type;
/**
* 经度
*/
@TableField(value = "LON")
private Double lon;
/**
* 纬度
*/
@TableField(value = "LAT")
private Double lat;
/**
* 海拔
*/
@TableField(value = "ELEVATION")
private Double elevation;
/**
* 描述
*/
@TableField(value = "DESCRIPTION")
private String description;
/**
* 开始运行日期
*/
@TableField(value = "DATE_BEGIN")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date dateBegin;
/**
* 运行终止日期
*/
@TableField(value = "DATE_END")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date dateEnd;
/**
* 运行状态
*/
@TableField(value = "STATUS")
private String status;
/**
* 操作时间
*/
@TableField(value = "MODDATE")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date moddate;
@TableField(value = "CATEGORY")
private Integer category;
/**
* 有效率计算类型
*/
@TableField(value = "EFFIC_CALCUL_TYPE")
private String efficCalculType;
}

View File

@ -0,0 +1,7 @@
package org.jeecg.modules.base.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.base.entity.configuration.GardsNuclearfacility;
public interface GardsNuclearfacilityMapper extends BaseMapper<GardsNuclearfacility> {
}

View File

@ -0,0 +1,7 @@
package org.jeecg.modules.base.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.base.entity.configuration.GardsStations;
public interface GardsStationsMapper extends BaseMapper<GardsStations> {
}

View File

@ -73,7 +73,7 @@ public interface SourceRebuildTaskService extends IService<SourceRebuildTask> {
/**
* 任务耗时
* @param taskId
* @param taskTimeConsuming
* @param minute
*/
void updateTaskTimeConsuming(Integer taskId, Integer taskTimeConsuming);
void updateTaskTimeConsuming(Integer taskId, Double minute);
}

View File

@ -221,13 +221,13 @@ public class SourceRebuildTaskServiceImpl extends ServiceImpl<SourceRebuildTaskM
* 任务耗时
*
* @param taskId
* @param taskTimeConsuming
* @param minute
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void updateTaskTimeConsuming(Integer taskId, Integer taskTimeConsuming) {
public void updateTaskTimeConsuming(Integer taskId, Double minute) {
SourceRebuildTask sourceRebuildTask = this.baseMapper.selectById(taskId);
sourceRebuildTask.setTimeConsuming(taskTimeConsuming);
sourceRebuildTask.setTimeConsuming(minute);
this.updateById(sourceRebuildTask);
}
}

View File

@ -93,8 +93,9 @@ public class SourceRebuildTaskExec extends Thread{
}finally {
//添加任务耗时
stopWatch.stop();
Integer time = Long.valueOf(stopWatch.getTime(TimeUnit.MINUTES)).intValue();
this.sourceRebuildTaskService.updateTaskTimeConsuming(this.sourceRebuildTask.getId(),time);
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
Double min = seconds/60D;
this.sourceRebuildTaskService.updateTaskTimeConsuming(this.sourceRebuildTask.getId(),min);
}
}

View File

@ -1,10 +0,0 @@
package org.jeecg.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.base.entity.TransportTask;
/**
* 传输任务结果数据
*/
public interface TransportTaskResultService extends IService<TransportTask> {
}

View File

@ -2,6 +2,8 @@ package org.jeecg.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import org.jeecg.common.system.query.PageRequest;
import org.jeecg.modules.base.entity.TransportTask;
import org.jeecg.modules.base.entity.TransportTaskLog;
@ -62,4 +64,30 @@ public interface TransportTaskService extends IService<TransportTask> {
* @return
*/
List<TransportTaskLog> getTaskLog(Integer taskId);
/**
* 保存任务日志
* @param transportTaskLog
*/
void saveLog(TransportTaskLog transportTaskLog);
/**
* 修改任务耗时
* @param taskId
* @param minute
*/
void updateTaskTimeConsuming(Integer taskId, Double minute);
/**
* 删除任务日志
* @param taskId
*/
void deleteTaskLog(Integer taskId);
/**
* 修改任务状态
* @param taskId
* @param status
*/
void updateTaskStatus(Integer taskId, Integer status);
}

View File

@ -1,4 +1,4 @@
package org.jeecg.service;
package org.jeecg.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -9,6 +9,8 @@ import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
import org.jeecg.common.constant.enums.TransportTaskTypeEnum;
import org.jeecg.common.properties.SystemStorageProperties;
import org.jeecg.common.properties.TransportSimulationProperties;
import org.jeecg.common.system.query.PageRequest;
import org.jeecg.modules.base.entity.TransportTask;
import org.jeecg.modules.base.entity.TransportTaskChild;
@ -16,6 +18,9 @@ import org.jeecg.modules.base.entity.TransportTaskLog;
import org.jeecg.modules.base.mapper.TransportTaskChildMapper;
import org.jeecg.modules.base.mapper.TransportTaskLogMapper;
import org.jeecg.modules.base.mapper.TransportTaskMapper;
import org.jeecg.modules.base.mapper.WeatherDataMapper;
import org.jeecg.service.TransportTaskService;
import org.jeecg.task.TransportTaskExec;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
@ -28,10 +33,13 @@ import java.util.Objects;
*/
@RequiredArgsConstructor
@Service
public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,TransportTask> implements TransportTaskService{
public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,TransportTask> implements TransportTaskService {
private final TransportTaskChildMapper transportTaskChildMapper;
private final TransportTaskLogMapper transportTaskLogMapper;
private final WeatherDataMapper weatherDataMapper;
private final TransportSimulationProperties simulationProperties;
private final SystemStorageProperties systemStorageProperties;
/**
* 分页查询任务列表
@ -76,8 +84,9 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
throw new RuntimeException("此任务已存在,请核对任务名称信息");
}
transportTask.setTaskPprogress(0);
transportTask.setTaskStatus(TransportTaskStatusEnum.NOT_START.getKey());
transportTask.setTaskStatus(TransportTaskStatusEnum.NOT_STARTED.getValue());
transportTask.setTaskType(TransportTaskTypeEnum.MANUALLY.getKey());
transportTask.setTimeConsuming(0D);
this.baseMapper.insert(transportTask);
if(CollUtil.isNotEmpty(transportTask.getChildList())){
transportTask.getChildList().forEach(transportTaskChild -> {
@ -128,6 +137,12 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
}
checkIdResult.setTaskName(transportTask.getTaskName());
checkIdResult.setTaskMode(transportTask.getTaskMode());
checkIdResult.setUseMetType(transportTask.getUseMetType());
checkIdResult.setStartTime(transportTask.getStartTime());
checkIdResult.setEndTime(transportTask.getEndTime());
checkIdResult.setZ1(transportTask.getZ1());
checkIdResult.setZ2(transportTask.getZ2());
checkIdResult.setParticleCount(transportTask.getParticleCount());
this.baseMapper.updateById(checkIdResult);
//先删除再保存
LambdaQueryWrapper<TransportTaskChild> delQueryWrapper = new LambdaQueryWrapper<>();
@ -163,7 +178,23 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void runTask(Integer id) {
//校验任务
TransportTask checkIdResult = this.baseMapper.selectById(id);
if(Objects.isNull(checkIdResult)){
throw new RuntimeException("此任务不存在");
}
LambdaQueryWrapper<TransportTaskChild> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TransportTaskChild::getTaskId,id);
List<TransportTaskChild> transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper);
if(Objects.isNull(transportTaskChildren)){
throw new RuntimeException("此任务对应的flexpart模型配置信息不存在请确认");
}
TransportTaskExec taskExec = new TransportTaskExec();
taskExec.init(weatherDataMapper,this,
checkIdResult,transportTaskChildren,
simulationProperties,systemStorageProperties);
taskExec.setName("输运模拟执行线程");
taskExec.start();
}
/**
@ -178,4 +209,58 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
queryWrapper.eq(TransportTaskLog::getTaskId,taskId);
return transportTaskLogMapper.selectList(queryWrapper);
}
/**
* 保存任务日志
*
* @param transportTaskLog
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void saveLog(TransportTaskLog transportTaskLog) {
transportTaskLogMapper.insert(transportTaskLog);
}
/**
* 修改任务耗时
*
* @param taskId
* @param minute
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void updateTaskTimeConsuming(Integer taskId, Double minute) {
TransportTask transportTask = this.baseMapper.selectById(taskId);
transportTask.setTimeConsuming(minute);
}
/**
* 删除任务日志
*
* @param taskId
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void deleteTaskLog(Integer taskId) {
LambdaQueryWrapper<TransportTaskLog> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TransportTaskLog::getTaskId,taskId);
transportTaskLogMapper.delete(queryWrapper);
}
/**
* 修改任务状态
* @param taskId
* @param status
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void updateTaskStatus(Integer taskId, Integer status) {
TransportTask transportTask = this.baseMapper.selectById(taskId);
if(Objects.isNull(transportTask)){
throw new RuntimeException("此任务不存在");
}
transportTask.setTaskStatus(status);
this.baseMapper.updateById(transportTask);
}
}

View File

@ -0,0 +1,27 @@
package org.jeecg.task;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 日志事件
*/
@Data
@NoArgsConstructor
public class ProgressEvent implements Serializable{
private static final long serialVersionUID = 1L;
private Integer taskId;
/**
* 过程日志
*/
private String content;
public ProgressEvent(Integer taskId, String content) {
this.taskId = taskId;
this.content = content;
}
}

View File

@ -0,0 +1,50 @@
package org.jeecg.task;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.base.entity.TransportTaskLog;
import org.jeecg.service.TransportTaskService;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 日志监测线程
* @author 86187
*
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class ProgressMonitor{
private final TransportTaskService transportTaskService;
@PostConstruct
public void start() {
ProgressMonitorThread monitor = new ProgressMonitorThread();
monitor.setName("输运模拟日志监测线程");
monitor.start();
}
private class ProgressMonitorThread extends Thread{
@Override
public void run() {
for(;;) {
try {
ProgressEvent event = ProgressQueue.getInstance().take();
if(Objects.nonNull(event)) {
TransportTaskLog log = new TransportTaskLog();
log.setTaskId(event.getTaskId());
log.setLogContent(event.getContent());
transportTaskService.saveLog(log);
}
}catch (Exception e){
log.error("输运模拟日志存储线程异常,日志存储失败,原因为:{}",e.getMessage());
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,39 @@
package org.jeecg.task;
import java.util.LinkedList;
/**
* 日志队列
*/
public class ProgressQueue {
private final LinkedList<ProgressEvent> queue = new LinkedList<>();
private static ProgressQueue progressQueue = new ProgressQueue();
public static ProgressQueue getInstance() {
return progressQueue;
}
public void offer(ProgressEvent event) {
synchronized (queue) {
queue.addLast(event);
queue.notify();
}
}
public ProgressEvent take(){
synchronized (queue) {
if(queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ProgressEvent event = queue.removeFirst();
return event;
}
}
}

View File

@ -0,0 +1,294 @@
package org.jeecg.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
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.lang3.time.StopWatch;
import org.apache.logging.log4j.util.Strings;
import org.jeecg.common.constant.enums.TransportTaskModeEnum;
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
import org.jeecg.common.properties.SystemStorageProperties;
import org.jeecg.common.properties.TransportSimulationProperties;
import org.jeecg.modules.base.entity.*;
import org.jeecg.modules.base.mapper.WeatherDataMapper;
import org.jeecg.service.TransportTaskService;
import java.io.*;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class TransportTaskExec extends Thread{
private WeatherDataMapper weatherDataMapper;
private TransportTaskService transportTaskService;
private TransportTask transportTask;
private List<TransportTaskChild> transportTaskChildren;
private TransportSimulationProperties simulationProperties;
private SystemStorageProperties systemStorageProperties;
/**
* 初始化
*/
public void init(WeatherDataMapper weatherDataMapper,
TransportTaskService transportTaskService,
TransportTask transportTask,
List<TransportTaskChild> transportTaskChildren,
TransportSimulationProperties simulationProperties,
SystemStorageProperties systemStorageProperties){
this.weatherDataMapper = weatherDataMapper;
this.transportTaskService = transportTaskService;
this.transportTask = transportTask;
this.transportTaskChildren = transportTaskChildren;
this.simulationProperties = simulationProperties;
this.systemStorageProperties = systemStorageProperties;
}
@Override
public void run() {
this.execute();
}
/**
* 执行任务
*/
public void execute() {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try{
//修改任务状态为执行中
this.transportTaskService.updateTaskStatus(this.transportTask.getId(), TransportTaskStatusEnum.IN_OPERATION.getValue());
//如果此任务已存在历史日志先清除
this.transportTaskService.deleteTaskLog(this.transportTask.getId());
//检查气象数据
this.checkMetData();
//执行模拟
this.execSimulation();
}catch (Exception e){
String taskErrorLog = "任务执行失败,原因:"+e.getMessage();
ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),taskErrorLog));
e.printStackTrace();
throw e;
}finally {
//添加任务耗时
stopWatch.stop();
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
Double min = seconds/60D;
this.transportTaskService.updateTaskTimeConsuming(this.transportTask.getId(),min);
}
}
/**
* 检查气象数据
*/
private void checkMetData(){
String msg = "检查气象数据";
ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),msg));
LocalDateTime startTime = this.transportTask.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime endTime = this.transportTask.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
startTime = startTime.minusDays(3);
endTime = endTime.plusDays(3);
LambdaQueryWrapper<WeatherData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(WeatherData::getDataSource,this.transportTask.getUseMetType());
queryWrapper.between(WeatherData::getDataStartTime,startTime,endTime);
List<WeatherData> dataList = weatherDataMapper.selectList(queryWrapper);
long days = ChronoUnit.DAYS.between(startTime, endTime);
//一天至少4个气象数据至少是00,06,12,18
// if(CollUtil.isEmpty(dataList) || dataList.size() < (days*4)){
// String exceptionMsg = "此任务时间范围(%s - %s)参数所涉及的气象数据不完整,请核对气象数据";
// String formatMsg = String.format(exceptionMsg, LocalDateTimeUtil.format(startTime, "yyyy-MM-dd hh:mm:ss"), LocalDateTimeUtil.format(endTime, "yyyy-MM-dd hh:mm:ss"));
// ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),formatMsg));
// throw new RuntimeException(formatMsg);
// }
String finalMsg = "检查气象数据完毕";
ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),finalMsg));
}
/**
* 执行模拟
*/
private void execSimulation(){
Process process = null;
try {
String paramMsg = "生成flexpart所需参数文件param.config,stations.config";
ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),paramMsg));
//处理参数配置文件
String paramConfigPath = this.getParamConfigPath(this.transportTask.getUseMetType());
String metDataPath = this.getMetDataPath(this.transportTask.getUseMetType());
if(!FileUtil.exist(paramConfigPath)){
FileUtil.touch(paramConfigPath);
}
StringBuilder paramContent = new StringBuilder();
paramContent.append(DateUtil.format(this.transportTask.getStartTime(),"yyyyMMdd")).append("\n");
paramContent.append(DateUtil.format(this.transportTask.getEndTime(),"yyyyMMdd")).append("\n");
paramContent.append(DateUtil.format(this.transportTask.getStartTime(),"HHmmss")).append("\n");
paramContent.append(DateUtil.format(this.transportTask.getEndTime(),"HHmmss")).append("\n");
paramContent.append(this.transportTask.getZ1()).append("\n");
paramContent.append(this.transportTask.getZ2()).append("\n");
paramContent.append(metDataPath).append("\n");
FileUtil.writeString(paramContent.toString(),paramConfigPath,"UTF-8");
//处理台站数据文件
List<String> stationConfigInfo = new ArrayList<>();
String stationsConfigPath = this.getStationsConfigPath(this.transportTask.getUseMetType());
if(!FileUtil.exist(stationsConfigPath)){
FileUtil.touch(stationsConfigPath);
}
this.transportTaskChildren.forEach(taskChild -> {
String format = "%s,%f,%f,%s";
String row = String.format(format,taskChild.getStationCode(),taskChild.getLat(),taskChild.getLon(),taskChild.getReleaseAmount());
stationConfigInfo.add(row);
});
FileUtil.writeLines(stationConfigInfo,stationsConfigPath,"UTF-8");
//获取脚本路径
String scriptPath = null;
if(TransportTaskModeEnum.FORWARD.getKey().equals(this.transportTask.getTaskMode())){
scriptPath = this.getForwardScriptPath(this.transportTask.getUseMetType());
} else if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(this.transportTask.getTaskMode())) {
scriptPath = this.getBackForwardScriptPath(this.transportTask.getUseMetType());
}
String execScriptMsg = "执行任务脚本,开始模拟,路径为:"+scriptPath;
ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),execScriptMsg));
ProcessBuilder processBuilder = new ProcessBuilder(scriptPath);
processBuilder.directory(new File(new File(scriptPath).getParent()));
processBuilder.redirectErrorStream(true);
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.transportTask.getId(),line));
}
}
//等待进程结束
process.waitFor();
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
if(Objects.nonNull(process)){
process.destroy();
}
}
}
/**
* 获取气象数据路径
* @param dataSource
* @return
*/
private 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());
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)){
path.append(systemStorageProperties.getGraphcast());
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)){
path.append(systemStorageProperties.getCra40());
}else if(WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)){
path.append(systemStorageProperties.getNcep());
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataSource)){
path.append(systemStorageProperties.getFnl());
}else if(WeatherDataSourceEnum.T1H.getKey().equals(dataSource)){
path.append(systemStorageProperties.getT1h());
}
return path.toString();
}
/**
* 获取参数配置文件路径
* @param dataSource
* @return
*/
private String getParamConfigPath(Integer dataSource){
if(WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)){
return simulationProperties.getPanguParamConfigPath();
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)){
return simulationProperties.getGraphcastParamConfigPath();
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)){
return simulationProperties.getCra40ParamConfigPath();
}else if(WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)){
return simulationProperties.getNcepParamConfigPath();
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataSource)){
return simulationProperties.getFnlParamConfigPath();
}else if(WeatherDataSourceEnum.T1H.getKey().equals(dataSource)){
return simulationProperties.getT1hParamConfigPath();
}
return Strings.EMPTY;
}
/**
* 获取台站参数配置文件路径
* @param dataSource
* @return
*/
private String getStationsConfigPath(Integer dataSource){
if(WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)){
return simulationProperties.getPanguStationsConfigPath();
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)){
return simulationProperties.getCra40StationsConfigPath();
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)){
return simulationProperties.getCra40StationsConfigPath();
}else if(WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)){
return simulationProperties.getNcepStationsConfigPath();
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataSource)){
return simulationProperties.getFnlStationsConfigPath();
}else if(WeatherDataSourceEnum.T1H.getKey().equals(dataSource)){
return simulationProperties.getT1hStationsConfigPath();
}
return Strings.EMPTY;
}
/**
* 获取正演脚本路径
* @param dataSource
* @return
*/
private String getForwardScriptPath(Integer dataSource){
if(WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)){
return simulationProperties.getPanguForwardScriptPath();
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)){
return simulationProperties.getGraphcastForwardScriptPath();
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)){
return simulationProperties.getCra40ForwardScriptPath();
}else if(WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)){
return simulationProperties.getNcepForwardScriptPath();
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataSource)){
return simulationProperties.getFnlForwardScriptPath();
}else if(WeatherDataSourceEnum.T1H.getKey().equals(dataSource)){
return simulationProperties.getT1hForwardScriptPath();
}
return Strings.EMPTY;
}
/**
* 获取正演脚本路径
* @param dataSource
* @return
*/
private String getBackForwardScriptPath(Integer dataSource){
if(WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)){
return simulationProperties.getPanguBackwardScriptPath();
}else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)){
return simulationProperties.getGraphcastBackwardScriptPath();
}else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)){
return simulationProperties.getCra40BackwardScriptPath();
}else if(WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)){
return simulationProperties.getNcepBackwardScriptPath();
}else if(WeatherDataSourceEnum.FNL.getKey().equals(dataSource)){
return simulationProperties.getFnlBackwardScriptPath();
}else if(WeatherDataSourceEnum.T1H.getKey().equals(dataSource)){
return simulationProperties.getT1hBackwardScriptPath();
}
return Strings.EMPTY;
}
}