diff --git a/README.md b/README.md index 3d91c07..9e5f5f8 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,6 @@ -JeecgBoot 低代码开发平台 -=============== - -当前最新版本: 3.8.0(发布日期:2025-05-16) - - -[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE) -[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://jeecg.com/aboutusIndex) -[![](https://img.shields.io/badge/version-3.8.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot) -[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot) -[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot) - - - -项目介绍 ------------------------------------ - -

Java Low Code Platform for Enterprise web applications

- -JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端分离架构 SpringBoot2.x和3.x,SpringCloud,Ant Design Vue3,Mybatis-plus,Shiro,JWT,支持微服务。强大的代码生成器让前后端代码一键生成,实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE), 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性! - - -#### 项目说明 - -| 项目名 | 说明 | -|--------------------|------------------------| -| `jeecg-boot` | 后端源码JAVA(SpringBoot微服务架构) | -| `jeecgboot-vue3` | 前端源码VUE3(vue3+vite5+ts最新技术栈) | - - - -技术文档 ------------------------------------ - -- 官方网站: [http://www.jeecg.com](http://www.jeecg.com) -- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) -- QQ交流群 : ⑩716488839、⑨808791225、其他(满) -- 在线演示 : [在线演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex) -> 演示系统的登录账号密码,请点击 [获取账号密码](http://jeecg.com/doc/demo) 获取 - - - -启动项目 ------------------------------------ - -- [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup) -- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick) - - -微服务启动 ------------------------------------ -- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer) -- [Docker启动微服务后台](https://help.jeecg.com/java/docker/springcloud) - - - -技术架构: ------------------------------------ - -#### 后端 - -- IDE建议: IDEA (必须安装lombok插件 ) -- 语言:Java 8+ (支持17) -- 依赖管理:Maven -- 基础框架:Spring Boot 2.7.18 -- 微服务框架: Spring Cloud Alibaba 2021.0.1.0 -- 持久层框架:MybatisPlus 3.5.3.2 -- 报表工具: JimuReport 1.9.4 -- 安全框架:Apache Shiro 1.12.0,Jwt 3.11.0 -- 微服务技术栈:Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking -- 数据库连接池:阿里巴巴Druid 1.1.24 -- 日志打印:logback -- 缓存:Redis -- 其他:autopoi, fastjson,poi,Swagger-ui,quartz, lombok(简化代码)等。 -- 默认数据库脚本:MySQL5.7+ -- [其他数据库,需要自己转](https://my.oschina.net/jeecg/blog/4905722) - - -#### 前端 - -- 前端IDE建议:WebStorm、Vscode -- 采用 Vue3.0+TypeScript+Vite+Ant-Design-Vue等新技术方案,包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能 -- 最新技术栈:Vue3.0 + TypeScript + Vite5 + ant-design-vue4 + pinia + echarts + unocss + vxe-table + qiankun + es6 -- 依赖管理:node、npm、pnpm - - - -#### 支持库 - -| 数据库 | 支持 | -| --- | --- | -| MySQL | √ | -| Oracle11g | √ | -| Sqlserver2017 | √ | -| PostgreSQL | √ | -| MariaDB | √ | -| 达梦 | √ | -| 人大金仓 | √ | -| TiDB | √ | - - - - -## 微服务解决方案 - - -- 1、服务注册和发现 Nacos √ -- 2、统一配置中心 Nacos √ -- 3、路由网关 gateway(三种加载方式) √ -- 4、分布式 http feign √ -- 5、熔断降级限流 Sentinel √ -- 6、分布式文件 Minio、阿里OSS √ -- 7、统一权限控制 JWT + Shiro √ -- 8、服务监控 SpringBootAdmin√ -- 9、链路跟踪 Skywalking [参考文档](https://help.jeecg.com/java/springcloud/super/skywarking) -- 10、消息中间件 RabbitMQ √ -- 11、分布式任务 xxl-job √ -- 12、分布式事务 Seata -- 13、轻量分布式日志 Loki+grafana套件 -- 14、支持 docker-compose、k8s、jenkins -- 15、CAS 单点登录 √ -- 16、路由限流 √ - - - -后台目录结构 ------------------------------------ -``` -项目结构 -├─jeecg-boot-parent(父POM: 项目依赖、modules组织) -│ ├─jeecg-boot-base-core(共通模块: 工具类、config、权限、查询过滤器、注解等) -│ ├─jeecg-module-demo 示例代码 -│ ├─jeecg-module-system System系统管理目录 -│ │ ├─jeecg-system-biz System系统管理权限等功能 -│ │ ├─jeecg-system-start System单体启动项目(8080) -│ │ ├─jeecg-system-api System系统管理模块对外api -│ │ │ ├─jeecg-system-cloud-api System模块对外提供的微服务接口 -│ │ │ ├─jeecg-system-local-api System模块对外提供的单体接口 -│ ├─jeecg-server-cloud --微服务模块 - ├─jeecg-cloud-gateway --微服务网关模块(9999) - ├─jeecg-cloud-nacos --Nacos服务模块(8848) - ├─jeecg-system-cloud-start --System微服务启动项目(7001) - ├─jeecg-demo-cloud-start --Demo微服务启动项目(7002) - ├─jeecg-visual - ├─jeecg-cloud-monitor --微服务监控模块 (9111) - ├─jeecg-cloud-xxljob --微服务xxljob定时任务服务端 (9080) - ├─jeecg-cloud-sentinel --sentinel服务端 (9000) - ├─jeecg-cloud-test -- 微服务测试示例(各种例子) - ├─jeecg-cloud-test-more -- 微服务测试示例(feign、熔断降级、xxljob、分布式锁) - ├─jeecg-cloud-test-rabbitmq -- 微服务测试示例(rabbitmq) - ├─jeecg-cloud-test-seata -- 微服务测试示例(seata分布式事务) - ├─jeecg-cloud-test-shardingsphere -- 微服务测试示例(分库分表) -``` - - - - -#### 微服务架构图 -![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot_springcloud2022.png "在这里输入图片标题") - + + diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/FlexpartSpeciesType.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/FlexpartSpeciesType.java new file mode 100644 index 0000000..674b2fd --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/FlexpartSpeciesType.java @@ -0,0 +1,23 @@ +package org.jeecg.common.constant.enums; + +/** + * 物种类型 + */ +public enum FlexpartSpeciesType { + + XE_131m(51), + XE_133(52), + XE_133m(53), + XE_135(54), + NOT_SPECIES(61); + + private Integer value; + + FlexpartSpeciesType(int value) { + this.value = value; + } + + public Integer getValue() { + return value; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/TransportReleaseDataSource.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/TransportReleaseDataSource.java new file mode 100644 index 0000000..ac4e343 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/TransportReleaseDataSource.java @@ -0,0 +1,23 @@ +package org.jeecg.common.constant.enums; + +public enum TransportReleaseDataSource { + + /** + * 自动填写 + */ + AUTO_SELECT(1), + /** + * 手动输入 + */ + MANUAL_ENTRY(2); + + private Integer key; + + TransportReleaseDataSource(Integer key) { + this.key = key; + } + + public Integer getKey(){ + return this.key; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/TransportSampleTypeEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/TransportSampleTypeEnum.java new file mode 100644 index 0000000..5519a78 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/TransportSampleTypeEnum.java @@ -0,0 +1,16 @@ +package org.jeecg.common.constant.enums; + +/** + * 输运任务样品类型说明枚举 + */ +public enum TransportSampleTypeEnum { + + /** + * 正向 + */ + B, + /** + * 反向 + */ + P; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/DataFusionProperties.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/DataFusionProperties.java index af944b2..cb587e7 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/DataFusionProperties.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/DataFusionProperties.java @@ -20,7 +20,11 @@ public class DataFusionProperties { /** * srs文件的上级目录有可能是flexpart.x.ecmwf.l1或flexpart.x.ncep.l1 */ - private String srmParentDir; + private String idcSrmParentDir; + /** + * srs文件的上级目录有可能是flexpart.x.ecmwf.l1或flexpart.x.ncep.l1 + */ + private String ndcSrmParentDir; /** * 浓度值过滤条件 */ diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/ServerProperties.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/ServerProperties.java new file mode 100644 index 0000000..1f3cc93 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/ServerProperties.java @@ -0,0 +1,19 @@ +package org.jeecg.common.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; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java index 907a213..61d16a9 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java @@ -60,10 +60,15 @@ public class SystemStorageProperties { private String formatScriptPythonEnv; /** - * ai-models 安装地址 + * pangu的ai-models 安装地址 */ private String panguEnvPath; + /** + * graphcast的ai-models 安装地址 + */ + private String graphcastEnvPath; + /** * 盘古模型执行路径 */ diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/TransportSimulationProperties.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/TransportSimulationProperties.java index 62e43d3..880ee52 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/TransportSimulationProperties.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/TransportSimulationProperties.java @@ -9,11 +9,26 @@ import org.springframework.stereotype.Component; @ConfigurationProperties(prefix = "transport-simulation") public class TransportSimulationProperties { + /** + * 颗粒物样品大气输运级别 + */ + private Integer transportLevel_p; + + /** + * 惰性气体样品输运状态 + */ + private Integer transportLevel_x; + /** * 模型输出路径 */ private String outputPath; + /** + * 正演各站点排放数据存储目录 + */ + private String inputSiteHourPath; + /** * 参数配置文件路径(和fnl气象数据有关系) */ diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTask.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTask.java index 343838f..555a483 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTask.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTask.java @@ -5,7 +5,6 @@ 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.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Null; @@ -13,6 +12,7 @@ import lombok.Data; import org.jeecg.common.validgroup.InsertGroup; import org.jeecg.common.validgroup.UpdateGroup; import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; import java.util.Date; import java.util.List; @@ -90,7 +90,7 @@ public class TransportTask{ * 创建时间 */ @TableField(value = "create_time") - @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; @@ -104,7 +104,7 @@ public class TransportTask{ * 更新时间 */ @TableField(value = "update_time") - @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date updateTime; @@ -112,15 +112,19 @@ public class TransportTask{ * 模拟开始时间 */ @NotNull(message = "模拟开始时间不能为空",groups = {InsertGroup.class, UpdateGroup.class}) + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @TableField(value = "start_time") - private Date startTime; + private LocalDateTime startTime; /** * 模拟结束时间 */ @NotNull(message = "模拟结束时间不能为空",groups = {InsertGroup.class, UpdateGroup.class}) + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @TableField(value = "end_time") - private Date endTime; + private LocalDateTime endTime; /** * 释放下部高度 @@ -143,7 +147,27 @@ public class TransportTask{ @TableField(value = "particle_count") private Integer particleCount; - @Valid + /** + * 正演释放数据来源 1自动填写,2手动输入 + */ + @TableField(value = "release_data_source") + private Integer releaseDataSource; + + /** + * 反演子表信息 + */ @TableField(exist = false) - List childList; + private List backwardChild; + + /** + * 正演子表信息 + */ + @TableField(exist = false) + private List forwardChild; + + /** + * 正演物种信息 + */ + @TableField(exist = false) + private List species; } diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskChild.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskBackwardChild.java similarity index 51% rename from jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskChild.java rename to jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskBackwardChild.java index 6b7dabb..1f3ace2 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskChild.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskBackwardChild.java @@ -5,75 +5,87 @@ 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; -import org.jeecg.common.validgroup.InsertGroup; -import org.jeecg.common.validgroup.UpdateGroup; import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; import java.util.Date; /** - * 输运模拟任务子表 + * 输运模拟反演任务配置子表 */ @Data -@TableName("stas_transport_task_child") -public class TransportTaskChild{ +@TableName("stas_transport_task_backward_child") +public class TransportTaskBackwardChild { /** * ID */ - @Null(message = "ID必须为空",groups = { InsertGroup.class}) @TableId(type = IdType.AUTO) private Integer id; /** * 任务ID */ - @Null(message = "任务id必须为空",groups = {InsertGroup.class, UpdateGroup.class}) @TableField(value = "task_id") private Integer taskId; /** * 台站编目 */ - @NotBlank(message = "台站编码不能为空",groups = {InsertGroup.class, UpdateGroup.class}) @TableField(value = "station_code") private String stationCode; /** - * 经度1 + * 经度 */ - @NotNull(message = "经度不能为空",groups = {InsertGroup.class, UpdateGroup.class}) @TableField(value = "lon") private Double lon; /** - * 纬度1 + * 纬度 */ - @NotNull(message = "经度不能为空",groups = {InsertGroup.class, UpdateGroup.class}) @TableField(value = "lat") private Double lat; /** * 释放量 */ - @NotNull(message = "释放量不能为空",groups = {InsertGroup.class, UpdateGroup.class}) @TableField(value = "release_amount") private String releaseAmount; - /** - * 创建人 - */ - @TableField(value = "create_by") - private String createBy; - /** * 创建时间 */ @TableField(value = "create_time") - @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; + + /** + * 样品id + */ + @TableField(value = "sample_id") + private Integer sampleId; + + /** + * 样品类型 + */ + @TableField(value = "sample_type") + private String sampleType; + + /** + * 开始测量时间 + */ + @TableField(value = "acq_start_time") + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime acqStartTime; + + /** + * 结束测量时间 + */ + @TableField(value = "acq_end_time") + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime acqEndTime; } diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardChild.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardChild.java new file mode 100644 index 0000000..74602bf --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardChild.java @@ -0,0 +1,64 @@ +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 lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import java.util.Date; +import java.util.List; + +/** + * 输运模拟正演任务配置子表 + */ +@Data +@TableName("stas_transport_task_forward_child") +public class TransportTaskForwardChild { + + /** + * ID + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 任务ID + */ + @TableField(value = "task_id") + private Integer taskId; + + /** + * 台站编目 + */ + @TableField(value = "station_code") + private String stationCode; + + /** + * 经度 + */ + @TableField(value = "lon") + private Double lon; + + /** + * 纬度 + */ + @TableField(value = "lat") + private Double lat; + + /** + * 创建时间 + */ + @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; + + /** + * 正演子表信息 + */ + @TableField(exist = false) + private List forwardReleaseChild; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardRelease.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardRelease.java new file mode 100644 index 0000000..8418805 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardRelease.java @@ -0,0 +1,68 @@ +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 lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import java.util.Date; + +/** + * 输运模拟正演任务配置子表 + */ +@Data +@TableName("stas_transport_task_forward_release") +public class TransportTaskForwardRelease { + + /** + * ID + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 任务ID + */ + @TableField(value = "task_id") + private Integer taskId; + + /** + * 任务ID + */ + @TableField(value = "forward_child_id") + private Integer forwardChildId; + + /** + * 开始时间 + */ + @TableField(value = "start_time") + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime startTime; + + /** + * 结束时间 + */ + @TableField(value = "end_time") + @JsonFormat(timezone = "Asia/Shanghai", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime endTime; + + /** + * 释放量 + */ + @TableField(value = "release_amount") + private String releaseAmount; + + /** + * 创建时间 + */ + @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; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardSpecies.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardSpecies.java new file mode 100644 index 0000000..bf3f9e3 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/TransportTaskForwardSpecies.java @@ -0,0 +1,37 @@ +package org.jeecg.modules.base.entity; + +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.util.Date; + +/** + * 输运模拟正演任务配置子表 + */ +@Data +@TableName("stas_transport_task_forward_species") +public class TransportTaskForwardSpecies { + + /** + * 任务ID + */ + @TableField(value = "task_id") + private Integer taskId; + + /** + * 物种id + */ + @TableField(value = "species_id") + private String speciesId; + + /** + * 创建时间 + */ + @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; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskBackwardChildMapper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskBackwardChildMapper.java new file mode 100644 index 0000000..cd9097d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskBackwardChildMapper.java @@ -0,0 +1,7 @@ +package org.jeecg.modules.base.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.base.entity.TransportTaskBackwardChild; + +public interface TransportTaskBackwardChildMapper extends BaseMapper { +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskChildMapper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskChildMapper.java deleted file mode 100644 index 6ed0e2d..0000000 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskChildMapper.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.jeecg.modules.base.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.jeecg.modules.base.entity.TransportTaskChild; - -public interface TransportTaskChildMapper extends BaseMapper { -} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardChildMapper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardChildMapper.java new file mode 100644 index 0000000..7b9d0d6 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardChildMapper.java @@ -0,0 +1,7 @@ +package org.jeecg.modules.base.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.base.entity.TransportTaskForwardChild; + +public interface TransportTaskForwardChildMapper extends BaseMapper { +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardReleaseMapper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardReleaseMapper.java new file mode 100644 index 0000000..3e3bbac --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardReleaseMapper.java @@ -0,0 +1,7 @@ +package org.jeecg.modules.base.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.base.entity.TransportTaskForwardRelease; + +public interface TransportTaskForwardReleaseMapper extends BaseMapper { +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardSpeciesMapper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardSpeciesMapper.java new file mode 100644 index 0000000..9774a6a --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/mapper/TransportTaskForwardSpeciesMapper.java @@ -0,0 +1,8 @@ +package org.jeecg.modules.base.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.base.entity.TransportTaskForwardSpecies; + +public interface TransportTaskForwardSpeciesMapper extends BaseMapper { + +} diff --git a/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/IMSSampleAnalysesMapper.java b/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/IMSSampleAnalysesMapper.java index c6b66ac..72ccbcf 100644 --- a/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/IMSSampleAnalysesMapper.java +++ b/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/IMSSampleAnalysesMapper.java @@ -1,6 +1,7 @@ package org.jeecg.sample.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; @@ -23,5 +24,6 @@ public interface IMSSampleAnalysesMapper extends BaseMapper { * 查询待输运的样品列表 * @return */ - List> getSamplesTransportList(); + List> getSamplesTransportList(@Param("transportLevel_p") Integer transportLevel_p, + @Param("transportLevel_x") Integer transportLevel_x); } diff --git a/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/xml/IMSSampleAnalysesMapper.xml b/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/xml/IMSSampleAnalysesMapper.xml index 9c597e9..b85748d 100644 --- a/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/xml/IMSSampleAnalysesMapper.xml +++ b/jeecg-module-large-screen/src/main/java/org/jeecg/sample/mapper/xml/IMSSampleAnalysesMapper.xml @@ -40,9 +40,9 @@ INNER JOIN CONFIGURATION.GARDS_STATIONS gst on gsd.STATION_ID = gst.STATION_ID WHERE CLOSE_STATUS = 0 and ( - (gsd.SAMPLE_TYPE = 'P' AND ga.CATEGORY IN (4, 5)) + (gsd.SAMPLE_TYPE = 'P' AND ga.CATEGORY > #{transportLevel_p}) or - (gsd.SAMPLE_TYPE = 'B' AND ga.CATEGORY IN (3)) + (gsd.SAMPLE_TYPE = 'B' AND ga.CATEGORY > #{transportLevel_x}) ) ORDER BY gts.MODDATE desc diff --git a/jeecg-module-large-screen/src/main/java/org/jeecg/sample/service/impl/IMSSampleAnalysesServiceImpl.java b/jeecg-module-large-screen/src/main/java/org/jeecg/sample/service/impl/IMSSampleAnalysesServiceImpl.java index 3c106d0..2b7821e 100644 --- a/jeecg-module-large-screen/src/main/java/org/jeecg/sample/service/impl/IMSSampleAnalysesServiceImpl.java +++ b/jeecg-module-large-screen/src/main/java/org/jeecg/sample/service/impl/IMSSampleAnalysesServiceImpl.java @@ -3,6 +3,7 @@ package org.jeecg.sample.service.impl; import com.baomidou.dynamic.datasource.annotation.DS; import lombok.RequiredArgsConstructor; import org.jeecg.common.constant.enums.TransportTaskCloseStatusEnum; +import org.jeecg.common.properties.TransportSimulationProperties; import org.jeecg.modules.base.entity.rnauto.GardsTransportStatus; import org.jeecg.modules.base.mapper.GardsTransportStatusMapper; import org.jeecg.sample.mapper.IMSSampleAnalysesMapper; @@ -18,6 +19,7 @@ public class IMSSampleAnalysesServiceImpl implements IMSSampleAnalysesService { private final IMSSampleAnalysesMapper sampleAnalysesMapper; private final GardsTransportStatusMapper transportStatusMapper; + private final TransportSimulationProperties transportSimulationProperties; /** * 统计气溶胶样品数量 @@ -46,7 +48,7 @@ public class IMSSampleAnalysesServiceImpl implements IMSSampleAnalysesService { @DS("ora") @Override public List> getSamplesTransportList() { - return sampleAnalysesMapper.getSamplesTransportList(); + return sampleAnalysesMapper.getSamplesTransportList(transportSimulationProperties.getTransportLevel_p(),transportSimulationProperties.getTransportLevel_x()); } /** diff --git a/jeecg-module-transport/src/main/java/org/jeecg/controller/TransportResultDataController.java b/jeecg-module-transport/src/main/java/org/jeecg/controller/TransportResultDataController.java index 0666c17..a0d2803 100644 --- a/jeecg-module-transport/src/main/java/org/jeecg/controller/TransportResultDataController.java +++ b/jeecg-module-transport/src/main/java/org/jeecg/controller/TransportResultDataController.java @@ -20,8 +20,8 @@ public class TransportResultDataController { private final TransportResultDataService transportResultDataService; - @AutoLog(value = "分页查询输运任务数据") - @Operation(summary = "分页查询输运任务数据") + @AutoLog(value = "获取扩散数据") + @Operation(summary = "获取扩散数据") @GetMapping("getDiffusionData") public Result getDiffusionData(@Validated(value= QueryGroup.class) QueryDiffusionVO queryDiffusionVO) { return Result.OK(transportResultDataService.getDiffusionData(queryDiffusionVO)); diff --git a/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportResultDataServiceImpl.java b/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportResultDataServiceImpl.java index e589f17..c3bebd4 100644 --- a/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportResultDataServiceImpl.java +++ b/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportResultDataServiceImpl.java @@ -2,28 +2,25 @@ package org.jeecg.service.impl; 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 lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.enums.TransportTaskModeEnum; -import org.jeecg.common.constant.enums.TransportTimingAnalysisEnum; import org.jeecg.common.properties.TransportSimulationProperties; import org.jeecg.common.util.NcUtil; import org.jeecg.common.util.RedisUtil; import org.jeecg.modules.base.entity.TransportTask; -import org.jeecg.modules.base.entity.TransportTaskChild; -import org.jeecg.modules.base.entity.configuration.GardsNuclearReactors; +import org.jeecg.modules.base.entity.TransportTaskBackwardChild; +import org.jeecg.modules.base.entity.TransportTaskForwardChild; import org.jeecg.modules.base.entity.configuration.GardsStations; -import org.jeecg.modules.base.mapper.TransportTaskChildMapper; +import org.jeecg.modules.base.mapper.TransportTaskBackwardChildMapper; +import org.jeecg.modules.base.mapper.TransportTaskForwardChildMapper; import org.jeecg.modules.base.mapper.TransportTaskMapper; import org.jeecg.service.StationDataService; import org.jeecg.service.TransportResultDataService; import org.jeecg.util.BilinearInterpolatorWithMath; -import org.jeecg.vo.ConcModValVo; import org.jeecg.vo.ContributionAnalysisVO; import org.jeecg.vo.QueryDiffusionVO; import org.jeecg.vo.TaskStationsVO; @@ -33,15 +30,10 @@ import ucar.ma2.DataType; import ucar.ma2.Index; import ucar.ma2.InvalidRangeException; import ucar.nc2.Attribute; -import ucar.nc2.Dimension; import ucar.nc2.NetcdfFile; import ucar.nc2.Variable; import java.io.File; import java.io.IOException; -import java.math.BigDecimal; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; @@ -52,7 +44,8 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic private final TransportTaskMapper transportTaskMapper; private final TransportSimulationProperties simulationProperties; - private final TransportTaskChildMapper transportTaskChildMapper; + private final TransportTaskBackwardChildMapper backwardChildMapper; + private final TransportTaskForwardChildMapper forwardChildMapper; private final StationDataService stationDataService; private final RedisUtil redisUtil; private final static String FORWARD="forward"; @@ -123,18 +116,18 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic */ private List> getBackwardData(TransportTask transportTask,QueryDiffusionVO queryDiffusionVO){ //验证任务子信息 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,queryDiffusionVO.getTaskId()); - queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode); - queryWrapper.orderByAsc(TransportTaskChild::getId); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskBackwardChild::getTaskId,queryDiffusionVO.getTaskId()); + queryWrapper.select(TransportTaskBackwardChild::getId, TransportTaskBackwardChild::getStationCode); + queryWrapper.orderByAsc(TransportTaskBackwardChild::getId); + List transportTaskChildren = backwardChildMapper.selectList(queryWrapper); if(CollUtil.isEmpty(transportTaskChildren)){ throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息"); } List> result = new ArrayList<>(); for(Integer pointNum : queryDiffusionVO.getPointspecs()){ //获取nc文件路径 - TransportTaskChild transportTaskChild = transportTaskChildren.get(pointNum); + TransportTaskBackwardChild transportTaskChild = transportTaskChildren.get(pointNum); String path = this.getBackForwardTaskNCPath(transportTask,transportTaskChild.getStationCode()); try (NetcdfFile ncFile = NetcdfFile.open(path.toString())) { List lonData = NcUtil.getNCList(ncFile, "longitude"); @@ -175,28 +168,22 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic if(Objects.isNull(transportTask)){ throw new RuntimeException("此任务不存在"); } - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,taskId); - queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode); - queryWrapper.orderByAsc(TransportTaskChild::getId); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); - if(CollUtil.isEmpty(transportTaskChildren)){ - throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息"); - } + + List stationCodes = this.getStationCodes(transportTask.getId(),transportTask.getTaskMode()); //获取nc文件路径 String path = ""; if(TransportTaskModeEnum.FORWARD.getKey().equals(transportTask.getTaskMode())){ path = this.getForwardTaskNCPath(transportTask); }else if(TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode())){ - path = this.getBackForwardTaskNCPath(transportTask,transportTaskChildren.get(0).getStationCode()); + path = this.getBackForwardTaskNCPath(transportTask,stationCodes.get(0)); } if(!FileUtil.exist(path)){ throw new RuntimeException("此任务模拟结果不存在,请确认任务运行状态"); } //本任务模拟的台站数据 Map stationNumMap = new LinkedHashMap<>(); - for (int i = 0; i getStationCodes(Integer taskId,Integer taskMode) { + List stationCodes = new ArrayList<>(); + if(TransportTaskModeEnum.FORWARD.getKey().equals(taskMode)){ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskForwardChild::getTaskId,taskId); + queryWrapper.select(TransportTaskForwardChild::getId, TransportTaskForwardChild::getStationCode); + queryWrapper.orderByAsc(TransportTaskForwardChild::getId); + List transportTaskChildren = forwardChildMapper.selectList(queryWrapper); + if(CollUtil.isEmpty(transportTaskChildren)){ + throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息"); + } + transportTaskChildren.forEach(taskChild -> stationCodes.add(taskChild.getStationCode())); + }else if(TransportTaskModeEnum.BACK_FORWARD.getKey().equals(taskMode)){ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskBackwardChild::getTaskId,taskId); + queryWrapper.select(TransportTaskBackwardChild::getId, TransportTaskBackwardChild::getStationCode); + queryWrapper.orderByAsc(TransportTaskBackwardChild::getId); + List transportTaskChildren = backwardChildMapper.selectList(queryWrapper); + if(CollUtil.isEmpty(transportTaskChildren)){ + throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息"); + } + transportTaskChildren.forEach(taskChild -> stationCodes.add(taskChild.getStationCode())); + } + return stationCodes; + } + /** * 查询符合度分析数据 * @param taskId @@ -241,99 +260,100 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic */ @Override public List> getConformityAnalysis(Integer taskId,Integer stationId,String nuclideName,String facilityName) { - TransportTask transportTask = transportTaskMapper.selectById(taskId); - if(Objects.isNull(transportTask)){ - throw new RuntimeException("此任务不存在"); - } - - List> xeResults = stationDataService.getXeResults(stationId, transportTask.getStartTime(), transportTask.getEndTime(), nuclideName); - //获取nc文件路径 - String path = this.getForwardTaskNCPath(transportTask); - try (NetcdfFile ncFile = NetcdfFile.open(path.toString())) { - int pointNum = -1; - int maxPointNum = -1; - if (StrUtil.isNotBlank(facilityName)) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,taskId); - queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode); - queryWrapper.orderByAsc(TransportTaskChild::getId); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); - for(int i=0;i lonData = NcUtil.getNCList(ncFile, "longitude"); - List latData = NcUtil.getNCList(ncFile, "latitude"); - List timeData = NcUtil.getNCList(ncFile, "time"); - GardsStations station = this.stationDataService.getStationById(stationId); - Map lonRecentValMap = this.getRecentValue(lonData, station.getLon()); - Map latRecentValMap = this.getRecentValue(latData, station.getLat()); - Integer lonBestIndex = Integer.parseInt(lonRecentValMap.get("bestIndex").toString()); - Integer latBestIndex = Integer.parseInt(latRecentValMap.get("bestIndex").toString()); - - Variable spec001Mr = ncFile.findVariable("spec001_mr"); - List modValList = new ArrayList<>(); - for(int i=pointNum;i<=maxPointNum;i++){ - for(int k=0;k 0){ - Instant instant = transportTask.getStartTime().toInstant(); - LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - localDateTime = localDateTime.plusSeconds(timeData.get(k).intValue()); - long second = localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond(); - ConcModValVo concModValVo = new ConcModValVo(); - concModValVo.setSecond(second); - concModValVo.setPointDataVal(pointDataVal); - modValList.add(concModValVo); - }else { - ConcModValVo concModValVo = new ConcModValVo(); - concModValVo.setSecond(0L); - concModValVo.setPointDataVal(pointDataVal); - modValList.add(concModValVo); - } - } - } - if(CollUtil.isNotEmpty(modValList)){ - xeResults.parallelStream().forEach(resultMap -> { - Date acqStart = (Date)resultMap.get("acquisitionStart"); - Date acqEnd = (Date)resultMap.get("acquisitionStop"); - int acqStartSecond = (int)(acqStart.getTime()/1000); - int acqStopSecond = (int)(acqEnd.getTime()/1000); - int count = 0; - Double sumVodValue = 0D; - for (ConcModValVo modVal : modValList){ - if (modVal.getSecond() >= acqStartSecond && modVal.getSecond() <= acqStopSecond) { - sumVodValue += modVal.getPointDataVal(); - count +=1; - } - } - if (sumVodValue != 0){ - //纳克转换为毫克需除以1000000 - BigDecimal vodValue = new BigDecimal(sumVodValue); - BigDecimal finalVodValue = vodValue.divide(new BigDecimal(count)).divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP); - resultMap.put("modValue", finalVodValue); - }else { - resultMap.put("modValue", sumVodValue); - } - }); - } - } catch (IOException | InvalidRangeException e) { - throw new RuntimeException(e); - } - return xeResults; +// TransportTask transportTask = transportTaskMapper.selectById(taskId); +// if(Objects.isNull(transportTask)){ +// throw new RuntimeException("此任务不存在"); +// } +// +// List> xeResults = stationDataService.getXeResults(stationId, transportTask.getStartTime(), transportTask.getEndTime(), nuclideName); +// //获取nc文件路径 +// String path = this.getForwardTaskNCPath(transportTask); +// try (NetcdfFile ncFile = NetcdfFile.open(path.toString())) { +// int pointNum = -1; +// int maxPointNum = -1; +// if (StrUtil.isNotBlank(facilityName)) { +// LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); +// queryWrapper.eq(TransportTaskBackwardChild::getTaskId,taskId); +// queryWrapper.select(TransportTaskBackwardChild::getId, TransportTaskBackwardChild::getStationCode); +// queryWrapper.orderByAsc(TransportTaskBackwardChild::getId); +// List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); +// for(int i=0;i lonData = NcUtil.getNCList(ncFile, "longitude"); +// List latData = NcUtil.getNCList(ncFile, "latitude"); +// List timeData = NcUtil.getNCList(ncFile, "time"); +// GardsStations station = this.stationDataService.getStationById(stationId); +// Map lonRecentValMap = this.getRecentValue(lonData, station.getLon()); +// Map latRecentValMap = this.getRecentValue(latData, station.getLat()); +// Integer lonBestIndex = Integer.parseInt(lonRecentValMap.get("bestIndex").toString()); +// Integer latBestIndex = Integer.parseInt(latRecentValMap.get("bestIndex").toString()); +// +// Variable spec001Mr = ncFile.findVariable("spec001_mr"); +// List modValList = new ArrayList<>(); +// for(int i=pointNum;i<=maxPointNum;i++){ +// for(int k=0;k 0){ +// Instant instant = transportTask.getStartTime().toInstant(); +// LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); +// localDateTime = localDateTime.plusSeconds(timeData.get(k).intValue()); +// long second = localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond(); +// ConcModValVo concModValVo = new ConcModValVo(); +// concModValVo.setSecond(second); +// concModValVo.setPointDataVal(pointDataVal); +// modValList.add(concModValVo); +// }else { +// ConcModValVo concModValVo = new ConcModValVo(); +// concModValVo.setSecond(0L); +// concModValVo.setPointDataVal(pointDataVal); +// modValList.add(concModValVo); +// } +// } +// } +// if(CollUtil.isNotEmpty(modValList)){ +// xeResults.parallelStream().forEach(resultMap -> { +// Date acqStart = (Date)resultMap.get("acquisitionStart"); +// Date acqEnd = (Date)resultMap.get("acquisitionStop"); +// int acqStartSecond = (int)(acqStart.getTime()/1000); +// int acqStopSecond = (int)(acqEnd.getTime()/1000); +// int count = 0; +// Double sumVodValue = 0D; +// for (ConcModValVo modVal : modValList){ +// if (modVal.getSecond() >= acqStartSecond && modVal.getSecond() <= acqStopSecond) { +// sumVodValue += modVal.getPointDataVal(); +// count +=1; +// } +// } +// if (sumVodValue != 0){ +// //纳克转换为毫克需除以1000000 +// BigDecimal vodValue = new BigDecimal(sumVodValue); +// BigDecimal finalVodValue = vodValue.divide(new BigDecimal(count)).divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP); +// resultMap.put("modValue", finalVodValue); +// }else { +// resultMap.put("modValue", sumVodValue); +// } +// }); +// } +// } catch (IOException | InvalidRangeException e) { +// throw new RuntimeException(e); +// } +// return xeResults; + return null; } /** @@ -344,12 +364,12 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic */ @Override public List getTaskFacility(Integer taskId) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,taskId); - queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode); - queryWrapper.orderByAsc(TransportTaskChild::getId); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); - return transportTaskChildren.stream().map(TransportTaskChild::getStationCode).collect(Collectors.toList()); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskForwardChild::getTaskId,taskId); + queryWrapper.select(TransportTaskForwardChild::getId, TransportTaskForwardChild::getStationCode); + queryWrapper.orderByAsc(TransportTaskForwardChild::getId); + List transportTaskChildren = forwardChildMapper.selectList(queryWrapper); + return transportTaskChildren.stream().map(TransportTaskForwardChild::getStationCode).collect(Collectors.toList()); } /** @@ -378,104 +398,104 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic */ @Override public void handleContributionAnalysis(Integer taskId) { - //查询任务数据 - TransportTask transportTask = transportTaskMapper.selectById(taskId); - //查询需要处理贡献分析数据的台站 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,taskId); - List stationInfos = this.transportTaskChildMapper.selectList(queryWrapper); - - //所以核设施数据 - List facilitys = (List) redisUtil.get(CommonConstant.ALL_NUCLEARFACILITY); - - NetcdfFile ncFile = null; - try { - for(TransportTaskChild stationInfo :stationInfos) { - //每个台站的结果数据 - ContributionAnalysisVO contributionAnalysisVO = new ContributionAnalysisVO(); - //存储台站每天的浓度值数据 - Map stationEveryDayConcDatas = new LinkedHashMap<>(); - //存储核设施每天的浓度值数据 - Map> facilityEveryDayConcDatas = new HashMap<>(); - //饼图数据 - Map pipeChartData = new HashMap<>(); - //总浓度值 - Double totalConc = 0D; - //获取nc文件路径 - String path = this.getBackForwardTaskNCPath(transportTask,stationInfo.getStationCode()); - ncFile = NetcdfFile.open(path.toString()); - List lonData = NcUtil.getNCList(ncFile, "longitude"); - List latData = NcUtil.getNCList(ncFile, "latitude"); - List timeData = NcUtil.getNCList(ncFile, "time"); - Variable spec001Mr = ncFile.findVariable("spec001_mr"); - for(int k=0;k0){ - if (!facilityEveryDayConcDatas.containsKey(dayStr)) { - Map facilityConcMap = new HashMap<>(); - facilityConcMap.put(facility.getUnitName(),facilityConc); - facilityEveryDayConcDatas.put(dayStr,facilityConcMap); - }else { - Map facilityConcMap = facilityEveryDayConcDatas.get(dayStr); - if (!facilityConcMap.containsKey(facility.getUnitName())) { - facilityConcMap.put(facility.getUnitName(),facilityConc); - }else { - facilityConcMap.put(facility.getUnitName(),facilityConcMap.get(facility.getUnitName())+facilityConc); - } - } - } - } - } - //计算本模拟时间内总浓度数据 - totalConc = stationEveryDayConcDatas.values().stream().mapToDouble(Double::doubleValue).sum(); - //处理饼图数据 - if(CollUtil.isNotEmpty(facilityEveryDayConcDatas)){ - for (Map facilityConcMap : facilityEveryDayConcDatas.values()) { - Set> entries = facilityConcMap.entrySet(); - for (Map.Entry entry : entries) { - if (!pipeChartData.containsKey(entry.getKey())) { - pipeChartData.put(entry.getKey(),entry.getValue()); - }else { - pipeChartData.put(entry.getKey(),pipeChartData.get(entry.getKey())+entry.getValue()); - } - } - } - } - //处理返回值 - contributionAnalysisVO.setTotalConc(totalConc); - contributionAnalysisVO.setStationEveryDayConcDatas(stationEveryDayConcDatas); - contributionAnalysisVO.setFacilityEveryDayConcDatas(facilityEveryDayConcDatas); - contributionAnalysisVO.setNuclideNames(new ArrayList<>(pipeChartData.keySet())); - contributionAnalysisVO.setPipeChartData(pipeChartData); - redisUtil.set(CommonConstant.TRANSPORT_CONTRIBUTION_ANALYSIS+transportTask.getId()+":"+stationInfo.getStationCode(), contributionAnalysisVO); - ncFile.close(); - } - }catch (IOException | InvalidRangeException e) { - throw new RuntimeException(e); - }finally { - try { - if(ncFile !=null){ - ncFile.close(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } +// //查询任务数据 +// TransportTask transportTask = transportTaskMapper.selectById(taskId); +// //查询需要处理贡献分析数据的台站 +// LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); +// queryWrapper.eq(TransportTaskBackwardChild::getTaskId,taskId); +// List stationInfos = this.transportTaskChildMapper.selectList(queryWrapper); +// +// //所以核设施数据 +// List facilitys = (List) redisUtil.get(CommonConstant.ALL_NUCLEARFACILITY); +// +// NetcdfFile ncFile = null; +// try { +// for(TransportTaskBackwardChild stationInfo :stationInfos) { +// //每个台站的结果数据 +// ContributionAnalysisVO contributionAnalysisVO = new ContributionAnalysisVO(); +// //存储台站每天的浓度值数据 +// Map stationEveryDayConcDatas = new LinkedHashMap<>(); +// //存储核设施每天的浓度值数据 +// Map> facilityEveryDayConcDatas = new HashMap<>(); +// //饼图数据 +// Map pipeChartData = new HashMap<>(); +// //总浓度值 +// Double totalConc = 0D; +// //获取nc文件路径 +// String path = this.getBackForwardTaskNCPath(transportTask,stationInfo.getStationCode()); +// ncFile = NetcdfFile.open(path.toString()); +// List lonData = NcUtil.getNCList(ncFile, "longitude"); +// List latData = NcUtil.getNCList(ncFile, "latitude"); +// List timeData = NcUtil.getNCList(ncFile, "time"); +// Variable spec001Mr = ncFile.findVariable("spec001_mr"); +// for(int k=0;k0){ +// if (!facilityEveryDayConcDatas.containsKey(dayStr)) { +// Map facilityConcMap = new HashMap<>(); +// facilityConcMap.put(facility.getUnitName(),facilityConc); +// facilityEveryDayConcDatas.put(dayStr,facilityConcMap); +// }else { +// Map facilityConcMap = facilityEveryDayConcDatas.get(dayStr); +// if (!facilityConcMap.containsKey(facility.getUnitName())) { +// facilityConcMap.put(facility.getUnitName(),facilityConc); +// }else { +// facilityConcMap.put(facility.getUnitName(),facilityConcMap.get(facility.getUnitName())+facilityConc); +// } +// } +// } +// } +// } +// //计算本模拟时间内总浓度数据 +// totalConc = stationEveryDayConcDatas.values().stream().mapToDouble(Double::doubleValue).sum(); +// //处理饼图数据 +// if(CollUtil.isNotEmpty(facilityEveryDayConcDatas)){ +// for (Map facilityConcMap : facilityEveryDayConcDatas.values()) { +// Set> entries = facilityConcMap.entrySet(); +// for (Map.Entry entry : entries) { +// if (!pipeChartData.containsKey(entry.getKey())) { +// pipeChartData.put(entry.getKey(),entry.getValue()); +// }else { +// pipeChartData.put(entry.getKey(),pipeChartData.get(entry.getKey())+entry.getValue()); +// } +// } +// } +// } +// //处理返回值 +// contributionAnalysisVO.setTotalConc(totalConc); +// contributionAnalysisVO.setStationEveryDayConcDatas(stationEveryDayConcDatas); +// contributionAnalysisVO.setFacilityEveryDayConcDatas(facilityEveryDayConcDatas); +// contributionAnalysisVO.setNuclideNames(new ArrayList<>(pipeChartData.keySet())); +// contributionAnalysisVO.setPipeChartData(pipeChartData); +// redisUtil.set(CommonConstant.TRANSPORT_CONTRIBUTION_ANALYSIS+transportTask.getId()+":"+stationInfo.getStationCode(), contributionAnalysisVO); +// ncFile.close(); +// } +// }catch (IOException | InvalidRangeException e) { +// throw new RuntimeException(e); +// }finally { +// try { +// if(ncFile !=null){ +// ncFile.close(); +// } +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// } } /** @@ -489,11 +509,11 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic if(Objects.isNull(transportTask)){ throw new RuntimeException("此任务不存在"); } - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,taskId); - queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode); - queryWrapper.orderByAsc(TransportTaskChild::getId); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskBackwardChild::getTaskId,taskId); + queryWrapper.select(TransportTaskBackwardChild::getId, TransportTaskBackwardChild::getStationCode); + queryWrapper.orderByAsc(TransportTaskBackwardChild::getId); + List transportTaskChildren = backwardChildMapper.selectList(queryWrapper); if(CollUtil.isEmpty(transportTaskChildren)){ throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息"); } @@ -527,19 +547,19 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic if(Objects.isNull(transportTask)){ throw new RuntimeException("此任务不存在"); } - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,taskId); - queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode); - queryWrapper.orderByAsc(TransportTaskChild::getId); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); - if(CollUtil.isEmpty(transportTaskChildren)){ - throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息"); - } //获取nc文件路径 String path = ""; if(TransportTaskModeEnum.FORWARD.getKey().equals(transportTask.getTaskMode())){ path = this.getForwardTaskNCPath(transportTask); }else if(TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode())){ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskBackwardChild::getTaskId,taskId); + queryWrapper.select(TransportTaskBackwardChild::getId, TransportTaskBackwardChild::getStationCode); + queryWrapper.orderByAsc(TransportTaskBackwardChild::getId); + List transportTaskChildren = backwardChildMapper.selectList(queryWrapper); + if(CollUtil.isEmpty(transportTaskChildren)){ + throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息"); + } path = this.getBackForwardTaskNCPath(transportTask,transportTaskChildren.get(0).getStationCode()); } if(!FileUtil.exist(path)){ @@ -569,46 +589,46 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic */ @Override public Map getTimingAnalysis(Integer taskId,String stationCode,String facilityName,Integer timeNum) { - TransportTask transportTask = transportTaskMapper.selectById(taskId); - if(Objects.isNull(transportTask)){ - throw new RuntimeException("此任务不存在"); - } - //包含从缓存中拿 - String key = CommonConstant.TRANSPORT_TIMING_ANALYSIS+transportTask.getId()+":"+stationCode+":"+facilityName; - if(redisUtil.hasKey(key)){ - Map concValMap = (LinkedHashMap)redisUtil.get(key); - //如果是3小时纬度,直接范围,因为最小是3小时 - if (TransportTimingAnalysisEnum.THREE_HOURS.getKey().equals(timeNum)) { - concValMap.forEach((k,v)->{ - //纳克转换为毫克需除以1000000 - BigDecimal concValue = new BigDecimal(v); - BigDecimal finalValue = concValue.divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP); - concValMap.put(k,finalValue.doubleValue()); - }); - return concValMap; - }else{ - Map resultMap = new LinkedHashMap<>(); - Instant endTimeInstant = transportTask.getEndTime().toInstant(); - LocalDateTime endTime = LocalDateTime.ofInstant(endTimeInstant, ZoneId.systemDefault()); - - Instant startTimeInstant = transportTask.getStartTime().toInstant(); - LocalDateTime startTime = LocalDateTime.ofInstant(startTimeInstant, ZoneId.systemDefault()); - - boolean flag = true; - while(flag){ - String dayTimeStr = LocalDateTimeUtil.format(startTime, "yyyy-MM-dd HH:mm:ss"); - //纳克转换为毫克需除以1000000 - BigDecimal concValue = new BigDecimal(concValMap.get(dayTimeStr)); - BigDecimal finalValue = concValue.divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP); - resultMap.put(dayTimeStr,finalValue.doubleValue()); - startTime = startTime.plusHours(timeNum); - if(startTime.isEqual(endTime)){ - flag = false; - } - } - return resultMap; - } - } +// TransportTask transportTask = transportTaskMapper.selectById(taskId); +// if(Objects.isNull(transportTask)){ +// throw new RuntimeException("此任务不存在"); +// } +// //包含从缓存中拿 +// String key = CommonConstant.TRANSPORT_TIMING_ANALYSIS+transportTask.getId()+":"+stationCode+":"+facilityName; +// if(redisUtil.hasKey(key)){ +// Map concValMap = (LinkedHashMap)redisUtil.get(key); +// //如果是3小时纬度,直接范围,因为最小是3小时 +// if (TransportTimingAnalysisEnum.THREE_HOURS.getKey().equals(timeNum)) { +// concValMap.forEach((k,v)->{ +// //纳克转换为毫克需除以1000000 +// BigDecimal concValue = new BigDecimal(v); +// BigDecimal finalValue = concValue.divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP); +// concValMap.put(k,finalValue.doubleValue()); +// }); +// return concValMap; +// }else{ +// Map resultMap = new LinkedHashMap<>(); +// Instant endTimeInstant = transportTask.getEndTime().toInstant(); +// LocalDateTime endTime = LocalDateTime.ofInstant(endTimeInstant, ZoneId.systemDefault()); +// +// Instant startTimeInstant = transportTask.getStartTime().toInstant(); +// LocalDateTime startTime = LocalDateTime.ofInstant(startTimeInstant, ZoneId.systemDefault()); +// +// boolean flag = true; +// while(flag){ +// String dayTimeStr = LocalDateTimeUtil.format(startTime, "yyyy-MM-dd HH:mm:ss"); +// //纳克转换为毫克需除以1000000 +// BigDecimal concValue = new BigDecimal(concValMap.get(dayTimeStr)); +// BigDecimal finalValue = concValue.divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP); +// resultMap.put(dayTimeStr,finalValue.doubleValue()); +// startTime = startTime.plusHours(timeNum); +// if(startTime.isEqual(endTime)){ +// flag = false; +// } +// } +// return resultMap; +// } +// } return Map.of(); } @@ -619,62 +639,62 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic */ @Override public void handleTimingAnalysis(Integer taskId) { - TransportTask transportTask = this.transportTaskMapper.selectById(taskId); - - //查询需要处理数据的台站 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,taskId); - List stationInfos = this.transportTaskChildMapper.selectList(queryWrapper); - - //所以核设施数据 - List facilitys = (List) redisUtil.get(CommonConstant.ALL_NUCLEARFACILITY); - - NetcdfFile ncFile = null; - try { - for(TransportTaskChild stationInfo :stationInfos) { - log.info("处理"+stationInfo.getStationCode()+"台站数据"); - Map> everyFacilityConcDatas = new HashMap<>(); - //获取nc文件路径 - String path = this.getBackForwardTaskNCPath(transportTask,stationInfo.getStationCode()); - ncFile = NetcdfFile.open(path.toString()); - List lonData = NcUtil.getNCList(ncFile, "longitude"); - List latData = NcUtil.getNCList(ncFile, "latitude"); - List timeData = NcUtil.getNCList(ncFile, "time"); - Variable spec001Mr = ncFile.findVariable("spec001_mr"); - for (GardsNuclearReactors facility : facilitys){ - //存储台站每步的浓度值数据 - Map everyStepConcDatas = new LinkedHashMap<>(); - for(int k=0;k{ - String key = CommonConstant.TRANSPORT_TIMING_ANALYSIS+transportTask.getId()+":"+stationInfo.getStationCode()+":"+facilityName; - redisUtil.set(key, facilityConcData); - }); - } - ncFile.close(); - } - }catch (IOException | InvalidRangeException e) { - throw new RuntimeException(e); - }finally { - try { - if(ncFile !=null){ - ncFile.close(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } +// TransportTask transportTask = this.transportTaskMapper.selectById(taskId); +// +// //查询需要处理数据的台站 +// LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); +// queryWrapper.eq(TransportTaskBackwardChild::getTaskId,taskId); +// List stationInfos = this.transportTaskChildMapper.selectList(queryWrapper); +// +// //所以核设施数据 +// List facilitys = (List) redisUtil.get(CommonConstant.ALL_NUCLEARFACILITY); +// +// NetcdfFile ncFile = null; +// try { +// for(TransportTaskBackwardChild stationInfo :stationInfos) { +// log.info("处理"+stationInfo.getStationCode()+"台站数据"); +// Map> everyFacilityConcDatas = new HashMap<>(); +// //获取nc文件路径 +// String path = this.getBackForwardTaskNCPath(transportTask,stationInfo.getStationCode()); +// ncFile = NetcdfFile.open(path.toString()); +// List lonData = NcUtil.getNCList(ncFile, "longitude"); +// List latData = NcUtil.getNCList(ncFile, "latitude"); +// List timeData = NcUtil.getNCList(ncFile, "time"); +// Variable spec001Mr = ncFile.findVariable("spec001_mr"); +// for (GardsNuclearReactors facility : facilitys){ +// //存储台站每步的浓度值数据 +// Map everyStepConcDatas = new LinkedHashMap<>(); +// for(int k=0;k{ +// String key = CommonConstant.TRANSPORT_TIMING_ANALYSIS+transportTask.getId()+":"+stationInfo.getStationCode()+":"+facilityName; +// redisUtil.set(key, facilityConcData); +// }); +// } +// ncFile.close(); +// } +// }catch (IOException | InvalidRangeException e) { +// throw new RuntimeException(e); +// }finally { +// try { +// if(ncFile !=null){ +// ncFile.close(); +// } +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// } } /** diff --git a/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportTaskServiceImpl.java b/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportTaskServiceImpl.java index afed0e1..e7e78d3 100644 --- a/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportTaskServiceImpl.java +++ b/jeecg-module-transport/src/main/java/org/jeecg/service/impl/TransportTaskServiceImpl.java @@ -7,22 +7,22 @@ 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.jeecg.common.constant.CommonConstant; +import org.jeecg.common.constant.enums.TransportReleaseDataSource; +import org.jeecg.common.constant.enums.TransportTaskModeEnum; import org.jeecg.common.constant.enums.TransportTaskStatusEnum; import org.jeecg.common.constant.enums.TransportTaskTypeEnum; +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.system.query.PageRequest; import org.jeecg.common.util.RedisUtil; -import org.jeecg.modules.base.entity.TransportTask; -import org.jeecg.modules.base.entity.TransportTaskChild; -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.modules.base.entity.*; +import org.jeecg.modules.base.mapper.*; import org.jeecg.service.TransportTaskService; -import org.jeecg.task.flexparttask.TransportTaskExec; +import org.jeecg.task.flexparttask.AbstractTaskExec; +import org.jeecg.task.flexparttask.BackwardTaskExec; +import org.jeecg.task.flexparttask.ForwardTaskExec; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; @@ -37,12 +37,17 @@ import java.util.Objects; @Service public class TransportTaskServiceImpl extends ServiceImpl implements TransportTaskService { - private final TransportTaskChildMapper transportTaskChildMapper; + private final TransportTaskBackwardChildMapper taskBackwardChildMapper; private final TransportTaskLogMapper transportTaskLogMapper; private final WeatherDataMapper weatherDataMapper; private final TransportSimulationProperties simulationProperties; private final SystemStorageProperties systemStorageProperties; private final RedisUtil redisUtil; + private final DataFusionProperties dataFusionProperties; + private final TransportTaskForwardReleaseMapper taskForwardReleaseMapper; + private final TransportTaskForwardChildMapper taskForwardChildMapper; + private final TransportTaskForwardSpeciesMapper taskForwardSpeciesMapper; + private final ServerProperties serverProperties; /** * 分页查询任务列表 @@ -92,11 +97,37 @@ public class TransportTaskServiceImpl extends ServiceImpl { + if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode()) && + CollUtil.isNotEmpty(transportTask.getBackwardChild())) { + transportTask.getBackwardChild().forEach(transportTaskChild -> { transportTaskChild.setTaskId(transportTask.getId()); }); - transportTaskChildMapper.insert(transportTask.getChildList()); + taskBackwardChildMapper.insert(transportTask.getBackwardChild()); + }else if (TransportTaskModeEnum.FORWARD.getKey().equals(transportTask.getTaskMode())) { + if(CollUtil.isNotEmpty(transportTask.getForwardChild())){ + if(TransportReleaseDataSource.MANUAL_ENTRY.getKey().equals(transportTask.getReleaseDataSource())){ + for (TransportTaskForwardChild child : transportTask.getForwardChild()) { + child.setTaskId(transportTask.getId()); + taskForwardChildMapper.insert(child); + child.getForwardReleaseChild().forEach(forwardReleaseChild -> { + forwardReleaseChild.setForwardChildId(child.getId()); + forwardReleaseChild.setTaskId(transportTask.getId()); + }); + taskForwardReleaseMapper.insert(child.getForwardReleaseChild()); + } + }else { + transportTask.getForwardChild().forEach(transportTaskChild -> { + transportTaskChild.setTaskId(transportTask.getId()); + }); + taskForwardChildMapper.insert(transportTask.getForwardChild()); + } + } + if(CollUtil.isNotEmpty(transportTask.getSpecies())){ + transportTask.getSpecies().forEach(transportTaskSpecies -> { + transportTaskSpecies.setTaskId(transportTask.getId()); + }); + taskForwardSpeciesMapper.insert(transportTask.getSpecies()); + } } } @@ -112,11 +143,37 @@ public class TransportTaskServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,transportTask.getId()); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); - if(CollUtil.isNotEmpty(transportTaskChildren)){ - transportTask.setChildList(transportTaskChildren); + if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode())) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskBackwardChild::getTaskId,transportTask.getId()); + List transportTaskChildren = taskBackwardChildMapper.selectList(queryWrapper); + if(CollUtil.isNotEmpty(transportTaskChildren)){ + transportTask.setBackwardChild(transportTaskChildren); + } + }else if (TransportTaskModeEnum.FORWARD.getKey().equals(transportTask.getTaskMode())) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskForwardChild::getTaskId,transportTask.getId()); + List taskChildren = taskForwardChildMapper.selectList(queryWrapper); + if(CollUtil.isNotEmpty(taskChildren)){ + transportTask.setForwardChild(taskChildren); + if(TransportReleaseDataSource.MANUAL_ENTRY.getKey().equals(transportTask.getReleaseDataSource())){ + taskChildren.forEach(taskChild -> { + LambdaQueryWrapper releaseQueryWrapper = new LambdaQueryWrapper<>(); + releaseQueryWrapper.eq(TransportTaskForwardRelease::getForwardChildId,taskChild.getId()); + releaseQueryWrapper.orderByAsc(TransportTaskForwardRelease::getStartTime); + List forwardReleases = taskForwardReleaseMapper.selectList(releaseQueryWrapper); + if(CollUtil.isNotEmpty(forwardReleases)){ + taskChild.setForwardReleaseChild(forwardReleases); + } + }); + } + } + LambdaQueryWrapper speciesQueryWrapper = new LambdaQueryWrapper<>(); + speciesQueryWrapper.eq(TransportTaskForwardSpecies::getTaskId,transportTask.getId()); + List species = taskForwardSpeciesMapper.selectList(speciesQueryWrapper); + if(CollUtil.isNotEmpty(species)){ + transportTask.setSpecies(species); + } } return transportTask; } @@ -147,16 +204,55 @@ public class TransportTaskServiceImpl extends ServiceImpl delQueryWrapper = new LambdaQueryWrapper<>(); - delQueryWrapper.eq(TransportTaskChild::getTaskId,checkIdResult.getId()); - transportTaskChildMapper.delete(delQueryWrapper); - if(CollUtil.isNotEmpty(transportTask.getChildList())){ - transportTask.getChildList().forEach(transportTaskChild -> { - transportTaskChild.setTaskId(transportTask.getId()); - }); - transportTaskChildMapper.insert(transportTask.getChildList()); + if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode())) { + LambdaQueryWrapper delQueryWrapper = new LambdaQueryWrapper<>(); + delQueryWrapper.eq(TransportTaskBackwardChild::getTaskId,checkIdResult.getId()); + taskBackwardChildMapper.delete(delQueryWrapper); + if(CollUtil.isNotEmpty(transportTask.getBackwardChild())){ + transportTask.getBackwardChild().forEach(transportTaskChild -> { + transportTaskChild.setTaskId(transportTask.getId()); + }); + taskBackwardChildMapper.insert(transportTask.getBackwardChild()); + } + }else if (TransportTaskModeEnum.FORWARD.getKey().equals(transportTask.getTaskMode())) { + //先删除再新增 + LambdaQueryWrapper delForwardChildQueryWrapper = new LambdaQueryWrapper<>(); + delForwardChildQueryWrapper.eq(TransportTaskForwardChild::getTaskId,checkIdResult.getId()); + taskForwardChildMapper.delete(delForwardChildQueryWrapper); + LambdaQueryWrapper delForwardReleaseQueryWrapper = new LambdaQueryWrapper<>(); + delForwardReleaseQueryWrapper.eq(TransportTaskForwardRelease::getTaskId,checkIdResult.getId()); + taskForwardReleaseMapper.delete(delForwardReleaseQueryWrapper); + LambdaQueryWrapper delForwardSpeciesQueryWrapper = new LambdaQueryWrapper<>(); + delForwardSpeciesQueryWrapper.eq(TransportTaskForwardSpecies::getTaskId,checkIdResult.getId()); + taskForwardSpeciesMapper.delete(delForwardSpeciesQueryWrapper); + //重新新增 + if(CollUtil.isNotEmpty(transportTask.getForwardChild())){ + if(TransportReleaseDataSource.MANUAL_ENTRY.getKey().equals(transportTask.getReleaseDataSource())){ + for (TransportTaskForwardChild child : transportTask.getForwardChild()) { + child.setTaskId(transportTask.getId()); + taskForwardChildMapper.insert(child); + child.getForwardReleaseChild().forEach(forwardReleaseChild -> { + forwardReleaseChild.setForwardChildId(child.getId()); + forwardReleaseChild.setTaskId(transportTask.getId()); + }); + taskForwardReleaseMapper.insert(child.getForwardReleaseChild()); + } + }else { + transportTask.getForwardChild().forEach(transportTaskChild -> { + transportTaskChild.setTaskId(transportTask.getId()); + }); + taskForwardChildMapper.insert(transportTask.getForwardChild()); + } + } + if(CollUtil.isNotEmpty(transportTask.getSpecies())){ + transportTask.getSpecies().forEach(transportTaskSpecies -> { + transportTaskSpecies.setTaskId(transportTask.getId()); + }); + taskForwardSpeciesMapper.insert(transportTask.getSpecies()); + } } } @@ -168,13 +264,15 @@ public class TransportTaskServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,id); - transportTaskChildMapper.delete(queryWrapper); - this.baseMapper.deleteById(id); - //删除存储的贡献分析数据和时序分析数据 - redisUtil.del(CommonConstant.TRANSPORT_CONTRIBUTION_ANALYSIS+id); - redisUtil.del(CommonConstant.TRANSPORT_TIMING_ANALYSIS+id); + TransportTask transportTask = this.baseMapper.selectById(id); + if(Objects.nonNull(transportTask)){ + if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode())) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskBackwardChild::getTaskId,id); + taskBackwardChildMapper.delete(queryWrapper); + } + this.baseMapper.deleteById(id); + } } /** @@ -190,19 +288,51 @@ public class TransportTaskServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(TransportTaskChild::getTaskId,id); - queryWrapper.orderByAsc(TransportTaskChild::getId); - List transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper); - if(Objects.isNull(transportTaskChildren)){ - throw new RuntimeException("此任务对应的flexpart模型配置信息不存在,请确认"); + if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(checkIdResult.getTaskMode())){ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskBackwardChild::getTaskId,id); + queryWrapper.orderByAsc(TransportTaskBackwardChild::getId); + List transportTaskChildren = taskBackwardChildMapper.selectList(queryWrapper); + if(Objects.isNull(transportTaskChildren)){ + throw new RuntimeException("此任务对应的flexpart反演配置信息不存在,请确认"); + } + AbstractTaskExec taskExec = new BackwardTaskExec(); + taskExec.init(weatherDataMapper,this, + checkIdResult,transportTaskChildren, + simulationProperties,systemStorageProperties, + dataFusionProperties,serverProperties); + taskExec.setName("大气输运反演任务执行线程"); + taskExec.start(); + }else if (TransportTaskModeEnum.FORWARD.getKey().equals(checkIdResult.getTaskMode())){ + //查询物种 + LambdaQueryWrapper speciesQueryWrapper = new LambdaQueryWrapper<>(); + speciesQueryWrapper.eq(TransportTaskForwardSpecies::getTaskId,id); + List species = this.taskForwardSpeciesMapper.selectList(speciesQueryWrapper); + if (CollUtil.isNotEmpty(species)) { + checkIdResult.setSpecies(species); + } + //查询台站信息 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TransportTaskForwardChild::getTaskId,id); + List stationInfo = taskForwardChildMapper.selectList(queryWrapper); + if (CollUtil.isNotEmpty(stationInfo)) { + stationInfo.forEach(station -> { + LambdaQueryWrapper releaseQueryWrapper = new LambdaQueryWrapper<>(); + releaseQueryWrapper.eq(TransportTaskForwardRelease::getForwardChildId,station.getId()); + releaseQueryWrapper.orderByAsc(TransportTaskForwardRelease::getStartTime); + List releaseList = taskForwardReleaseMapper.selectList(releaseQueryWrapper); + if (CollUtil.isNotEmpty(releaseList)) { + station.setForwardReleaseChild(releaseList); + } + }); + checkIdResult.setForwardChild(stationInfo); + } + AbstractTaskExec taskExec = new ForwardTaskExec(); + taskExec.init(weatherDataMapper,this,checkIdResult, + simulationProperties,systemStorageProperties,serverProperties); + taskExec.setName("大气输运反演任务执行线程"); + taskExec.start(); } - TransportTaskExec taskExec = new TransportTaskExec(); - taskExec.init(weatherDataMapper,this, - checkIdResult,transportTaskChildren, - simulationProperties,systemStorageProperties); - taskExec.setName("输运模拟执行线程"); - taskExec.start(); } /** diff --git a/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/AbstractTaskExec.java b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/AbstractTaskExec.java new file mode 100644 index 0000000..5581739 --- /dev/null +++ b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/AbstractTaskExec.java @@ -0,0 +1,219 @@ +package org.jeecg.task.flexparttask; + +import cn.hutool.core.util.StrUtil; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import lombok.Setter; +import org.apache.logging.log4j.util.Strings; +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.modules.base.entity.TransportTask; +import org.jeecg.modules.base.entity.TransportTaskBackwardChild; +import org.jeecg.modules.base.mapper.WeatherDataMapper; +import org.jeecg.service.TransportTaskService; + +import java.io.*; +import java.util.List; +import java.util.Properties; + +public abstract class AbstractTaskExec extends Thread{ + + protected WeatherDataMapper weatherDataMapper; + protected TransportTaskService transportTaskService; + protected TransportTask transportTask; + protected List transportTaskChildren; + protected TransportSimulationProperties simulationProperties; + protected SystemStorageProperties systemStorageProperties; + protected DataFusionProperties dataFusionProperties; + protected ServerProperties serverProperties; + + /** + * 初始化 + */ + public void init(WeatherDataMapper weatherDataMapper, + TransportTaskService transportTaskService, + TransportTask transportTask, + TransportSimulationProperties simulationProperties, + SystemStorageProperties systemStorageProperties, + ServerProperties serverProperties){ + this.weatherDataMapper = weatherDataMapper; + this.transportTaskService = transportTaskService; + this.transportTask = transportTask; + this.simulationProperties = simulationProperties; + this.systemStorageProperties = systemStorageProperties; + this.serverProperties = serverProperties; + } + + /** + * 初始化 + */ + public void init(WeatherDataMapper weatherDataMapper, + TransportTaskService transportTaskService, + TransportTask transportTask, + List transportTaskChildren, + TransportSimulationProperties simulationProperties, + SystemStorageProperties systemStorageProperties, + DataFusionProperties dataFusionProperties, + ServerProperties serverProperties){ + this.weatherDataMapper = weatherDataMapper; + this.transportTaskService = transportTaskService; + this.transportTask = transportTask; + this.simulationProperties = simulationProperties; + this.systemStorageProperties = systemStorageProperties; + this.transportTaskChildren = transportTaskChildren; + this.dataFusionProperties = dataFusionProperties; + this.serverProperties = serverProperties; + } + + public abstract void execute(); + + protected abstract void checkMetData(); + + protected abstract void execSimulation(); + + /** + * 获取气象数据路径 + * @param dataSource + * @return + */ + 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()); + }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 + */ + protected 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 + */ + protected String getStationsConfigPath(Integer dataSource){ + StringBuilder path = new StringBuilder(); + if(WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)){ + path.append(simulationProperties.getPanguStationsConfigPath()); + }else if(WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)){ + path.append(simulationProperties.getCra40StationsConfigPath()); + }else if(WeatherDataSourceEnum.CRA40.getKey().equals(dataSource)){ + path.append(simulationProperties.getCra40StationsConfigPath()); + }else if(WeatherDataSourceEnum.NCEP.getKey().equals(dataSource)){ + path.append(simulationProperties.getNcepStationsConfigPath()); + }else if(WeatherDataSourceEnum.FNL.getKey().equals(dataSource)){ + path.append(simulationProperties.getFnlStationsConfigPath()); + }else if(WeatherDataSourceEnum.T1H.getKey().equals(dataSource)){ + path.append(simulationProperties.getT1hStationsConfigPath()); + } + return path.toString(); + } + + + protected class JSchRemoteRunner{ + + private JSch jsch = new JSch(); + + private Session session = null; + + private ChannelExec channel = null; + + @Setter + private String command; + + /** + * 登录 + * @param host + * @param port + * @param username + * @param password + * @return + * @throws JSchException + */ + protected Session login(String host,int port,String username,String password) throws JSchException { + Properties config = new Properties(); + config.put("StrictHostKeyChecking","no"); + + session = jsch.getSession(username, host, port); + session.setPassword(password); + session.setConfig(config); + session.connect(); + return session; + } + + /** + * 持续读取日志 + */ + protected void execCommand() throws JSchException, IOException { + 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(transportTask.getId(),line)); + } + } +// // 等待脚本执行完成 +// while (channel.isConnected()) { +// Thread.sleep(1000); +// } + }catch(JSchException |IOException e){ + throw new RuntimeException(e); + }finally { + // 关闭资源 + if (channel != null) { + channel.disconnect(); + } + if (session != null) { + session.disconnect(); + } + } + } + } +} diff --git a/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/BackwardTaskExec.java b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/BackwardTaskExec.java new file mode 100644 index 0000000..89b0db8 --- /dev/null +++ b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/BackwardTaskExec.java @@ -0,0 +1,187 @@ +package org.jeecg.task.flexparttask; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.io.FileUtil; +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.FlexpartSpeciesType; +import org.jeecg.common.constant.enums.TransportTaskStatusEnum; +import org.jeecg.common.constant.enums.WeatherDataSourceEnum; +import org.jeecg.modules.base.entity.*; +import java.io.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class BackwardTaskExec extends AbstractTaskExec{ + + @Override + public void run() { + this.execute(); + } + + /** + * 执行任务 + */ + public void execute() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + try{ + //修改任务状态为执行中 + super.transportTaskService.updateTaskStatus(super.transportTask.getId(), TransportTaskStatusEnum.IN_OPERATION.getValue()); + //如果此任务已存在历史日志,先清除 + super.transportTaskService.deleteTaskLog(super.transportTask.getId()); + //检查气象数据 + this.checkMetData(); + //执行模拟 + this.execSimulation(); + //生成SRS文件 +// this.generateSRSFile(); + }catch (Exception e){ + String taskErrorLog = "任务执行失败,原因:"+e.getMessage(); + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),taskErrorLog)); + super.transportTaskService.updateTaskStatus(super.transportTask.getId(),TransportTaskStatusEnum.FAILURE.getValue()); + throw e; + }finally { + //添加任务耗时 + 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); + super.transportTaskService.updateTaskStatusToCompleted(super.transportTask.getId(),result.doubleValue()); + String taskCompletedLog = "任务执行完成,耗时:"+min+"分钟"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),taskCompletedLog)); + } + } + + /** + * 检查气象数据 + */ + protected void checkMetData(){ + String msg = "检查气象数据"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),msg)); + + LocalDateTime startTime = super.transportTask.getStartTime(); + LocalDateTime endTime = super.transportTask.getEndTime(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(WeatherData::getDataSource,super.transportTask.getUseMetType()); + queryWrapper.between(WeatherData::getDataStartTime,startTime,endTime); + List dataList = super.weatherDataMapper.selectList(queryWrapper); + long hours = ChronoUnit.HOURS.between(startTime, endTime)/6; + //一天至少4个气象数据,至少是00,06,12,18 + if(CollUtil.isEmpty(dataList) || dataList.size() < hours){ + 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(super.transportTask.getId(),formatMsg)); + throw new RuntimeException(formatMsg); + } + String finalMsg = "检查气象数据完毕"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),finalMsg)); + } + + /** + * 执行模拟 + */ + protected void execSimulation(){ +// Process process = null; + try { + String paramMsg = "生成flexpart所需参数文件:param.config,stations.config"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),paramMsg)); + //处理参数配置文件 + String paramConfigPath = super.getParamConfigPath(super.transportTask.getUseMetType()); + String metDataPath = super.getMetDataPath(super.transportTask.getUseMetType()); + if(!FileUtil.exist(paramConfigPath)){ + FileUtil.touch(paramConfigPath); + } + StringBuilder paramContent = new StringBuilder(); + paramContent.append(super.transportTask.getStartTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"))).append("\n"); + paramContent.append(super.transportTask.getEndTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"))).append("\n"); + paramContent.append(super.transportTask.getStartTime().format(DateTimeFormatter.ofPattern("HHmmss"))).append("\n"); + paramContent.append(super.transportTask.getEndTime().format(DateTimeFormatter.ofPattern("HHmmss"))).append("\n"); + paramContent.append(super.transportTask.getParticleCount()).append("\n"); + 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(FlexpartSpeciesType.NOT_SPECIES.getValue()).append("\n");//反演固定61,不显示具体核素,只显示Xe + FileUtil.writeString(paramContent.toString(),paramConfigPath,"UTF-8"); + //处理台站数据文件 + List stationConfigInfo = new ArrayList<>(); + String stationsConfigPath = super.getStationsConfigPath(super.transportTask.getUseMetType())+".backward"; + if(!FileUtil.exist(stationsConfigPath)){ + FileUtil.touch(stationsConfigPath); + } + super.transportTaskChildren.forEach(taskChild -> { + String format = "%s,%f,%f,%s,%s,%s,%s,%s"; + BigDecimal srcReleaseAmount = new BigDecimal(taskChild.getReleaseAmount()); + BigDecimal constant = new BigDecimal("1000"); + DecimalFormat scientificFormat = new DecimalFormat("0.00E0"); + double value = srcReleaseAmount.divide(constant).doubleValue(); + String releaseAmount = scientificFormat.format(value); + String row = String.format(format,taskChild.getStationCode(),taskChild.getLat(),taskChild.getLon(),releaseAmount, + taskChild.getAcqStartTime().format(DateTimeFormatter.ofPattern("yyyyMMdd")), + taskChild.getAcqEndTime().format(DateTimeFormatter.ofPattern("yyyyMMdd")), + taskChild.getAcqStartTime().format(DateTimeFormatter.ofPattern("HHmmss")), + taskChild.getAcqEndTime().format(DateTimeFormatter.ofPattern("HHmmss"))); + stationConfigInfo.add(row); + }); + //最后一行需要换行,否则启动flexpart报错 + stationConfigInfo.add("\n"); + FileUtil.writeLines(stationConfigInfo,stationsConfigPath,"UTF-8"); + //获取反演脚本路径 + String scriptPath = this.getBackForwardScriptPath(super.transportTask.getUseMetType()); + String execScriptMsg = "执行任务脚本,开始模拟,路径为:"+scriptPath; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),execScriptMsg)); + //ssh连接宿主机调用flexpart + JSchRemoteRunner jschRemoteRunner = new JSchRemoteRunner(); + jschRemoteRunner.setCommand(scriptPath); + jschRemoteRunner.login(super.serverProperties.getHost(),super.serverProperties.getPort(), + super.serverProperties.getUsername(),super.serverProperties.getPassword()); + jschRemoteRunner.execCommand(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 获取反演脚本路径 + * @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; + } + + /** + * 生成SRS文件 + */ + private void generateSRSFile(){ + for (TransportTaskBackwardChild transportTaskChild : this.transportTaskChildren){ + BuildNcToSrsFile ncToSrsFile = new BuildNcToSrsFile(); + ncToSrsFile.init(dataFusionProperties,simulationProperties,transportTask,transportTaskChild,transportTaskChild.getReleaseAmount()); + ncToSrsFile.execute(); + } + } +} diff --git a/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/BuildNcToSrsFile.java b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/BuildNcToSrsFile.java new file mode 100644 index 0000000..9d8cd82 --- /dev/null +++ b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/BuildNcToSrsFile.java @@ -0,0 +1,272 @@ +package org.jeecg.task.flexparttask; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; +import org.jeecg.common.constant.enums.TransportSampleTypeEnum; +import org.jeecg.common.constant.enums.WeatherDataSourceEnum; +import org.jeecg.common.properties.DataFusionProperties; +import org.jeecg.common.properties.TransportSimulationProperties; +import org.jeecg.modules.base.entity.TransportTask; +import org.jeecg.modules.base.entity.TransportTaskBackwardChild; +import ucar.ma2.Array; +import ucar.ma2.DataType; +import ucar.ma2.InvalidRangeException; +import ucar.nc2.Attribute; +import ucar.nc2.NetcdfFile; +import ucar.nc2.Variable; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.DecimalFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.GZIPOutputStream; + +/** + * 根据NC文件构建SRS文件 + */ +@Slf4j +public class BuildNcToSrsFile { + + private final static String BACK_FORWARD="backward"; + private DataFusionProperties dataFusionProperties; + private TransportSimulationProperties simulationProperties; + private TransportTask transportTask; + private TransportTaskBackwardChild transportTaskChild; + private final int radius=6371*1000; + private final double pi=3.14; + private double factorMul; + + public void init(DataFusionProperties dataFusionProperties, + TransportSimulationProperties simulationProperties, + TransportTask transportTask, + TransportTaskBackwardChild transportTaskChild, + String releaseAmount) { + this.dataFusionProperties = dataFusionProperties; + this.simulationProperties = simulationProperties; + this.transportTask = transportTask; + this.transportTaskChild = transportTaskChild; + this.factorMul = Double.parseDouble(releaseAmount); + } + + /** + * 执行转换任务 + */ + public void execute(){ + String ncFilePath = this.getBackForwardTaskNCPath(); + if(!FileUtil.exist(ncFilePath)){ + throw new RuntimeException("此任务模拟结果不存在,请确认任务运行状态"); + } + try (NetcdfFile ncFile = NetcdfFile.open(ncFilePath.toString())) { + String header = this.buildHeader(ncFile); + List body = buildBody(ncFile); + body.add(0,header); + File file = new File(this.getSrsStoragePath()); + if(!file.exists()){ + if(!file.getParentFile().exists()){ + file.getParentFile().mkdirs(); + } + file.createNewFile(); + } + Path outputPath = Paths.get(file.getParent(), file.getName()); + this.compressContentToFile(body,outputPath); + log.info("SRS文件生成完毕,路径为:{}", file.getAbsolutePath()); + } catch (IOException | InvalidRangeException e) { + String errLog = "SRS文件生成失败,原因:"+e.getMessage(); + log.error(errLog,e); + throw new RuntimeException(e); + } + } + + private String buildHeader(NetcdfFile ncFile){ + //处理台站经纬度 + BigDecimal lonBigDecimal = new BigDecimal(this.transportTaskChild.getLon()); + BigDecimal latBigDecimal = new BigDecimal(this.transportTaskChild.getLat()); + Double lon = lonBigDecimal.setScale(2, RoundingMode.HALF_UP).doubleValue(); + Double lat = latBigDecimal.setScale(2, RoundingMode.HALF_UP).doubleValue(); + //处理测量开始和结束时间参数 + LocalDateTime acqStartTime = transportTaskChild.getAcqStartTime(); + LocalDateTime acqEndTime = transportTaskChild.getAcqEndTime(); + String acqStartTimeStr = acqStartTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + String acqEndTimeStr = acqEndTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + //处理模拟总时长 + LocalDateTime taskStartTime = transportTask.getStartTime(); + LocalDateTime taskEndTime = transportTask.getEndTime(); + long startHour = taskStartTime.atZone(ZoneId.of("Asia/Shanghai")).getHour(); + long endHour = taskEndTime.atZone(ZoneId.of("Asia/Shanghai")).getHour(); + long totalHour = endHour - startHour; + //处理输出频率[小时] 和 平均时间[小时]参数 + Attribute loutstep = ncFile.findGlobalAttribute("loutstep"); + Attribute loutaver = ncFile.findGlobalAttribute("loutaver"); + int outputFreHour = Math.abs(loutstep.getNumericValue().intValue())/3600; + int averageTime = Math.abs(loutaver.getNumericValue().intValue())/3600; + //获取网格精度 + Attribute dxoutVar = ncFile.findGlobalAttribute("dxout"); + Attribute dyoutVar = ncFile.findGlobalAttribute("dyout"); + String header = String.format(" %s %s %s %s %s %s %s %s %s %s %s %s %s", + lon,lat,acqStartTimeStr,acqStartTime.getHour(),acqEndTimeStr,acqEndTime.getHour(), + this.transportTaskChild.getReleaseAmount(),totalHour,outputFreHour,averageTime, + dxoutVar.getNumericValue().doubleValue(),dyoutVar.getNumericValue().doubleValue(), + this.transportTaskChild.getStationCode()); + return header; + } + + /** + * 计算body数据 + * @param ncFile + * @return + * @throws InvalidRangeException + * @throws IOException + */ + private List buildBody(NetcdfFile ncFile) throws InvalidRangeException, IOException { + //获取时间序号 + Variable time = ncFile.findVariable("time"); + int timeLen = time.getShape()[0]; + //获取经纬度网格数量 + Variable longitude = ncFile.findVariable("longitude"); + Variable latitude = ncFile.findVariable("latitude"); + int lonLen = longitude.getShape()[0]; + int latLen = latitude.getShape()[0]; + Array latArray = latitude.read(); + Array lonArray = longitude.read(); + double[] lats = (double[]) latArray.get1DJavaArray(DataType.DOUBLE); + double[] lons = (double[]) lonArray.get1DJavaArray(DataType.DOUBLE); + //获取网格精度 + Attribute dxoutVar = ncFile.findGlobalAttribute("dxout"); + Attribute dyoutVar = ncFile.findGlobalAttribute("dyout"); + double dxout = dxoutVar.getNumericValue().doubleValue(); + double dyout = dyoutVar.getNumericValue().doubleValue(); + //处理输出频率[小时] + Attribute loutstep = ncFile.findGlobalAttribute("loutstep"); + double outputFreHour = loutstep.getNumericValue().doubleValue(); + + List body = new ArrayList<>(); + Variable spec001Mr = ncFile.findVariable("spec001_mr"); + for (int t=0;t0){ + double currentLat = lats[y]; + double currentLon = lons[x]; + double dx_1d=cal_dx_1d(currentLat); + double dy_1d = radius * pi / 180; + double area=dyout * dxout * dy_1d * dx_1d; + double volume=area*100;//height = 100 米(固定的高度层厚度) + double factor = pointVal / (outputFreHour * 3600) / volume * factorMul; + DecimalFormat scientificFormat = new DecimalFormat("0.0000000E0"); + String result = scientificFormat.format(factor); + String line = String.format(" %-7s %8s %-4s %s",currentLat,currentLon,t+1,result); + body.add(line); + } + } + } + } + return body; + } + + /** + * 获取反演NC结果文件路径 + * @return + */ + private String getBackForwardTaskNCPath(){ + //拼接nc文件路径 + StringBuilder path = new StringBuilder(); + path.append(simulationProperties.getOutputPath()); + path.append(File.separator); + path.append(this.transportTask.getTaskName()); + path.append(File.separator); + path.append(BACK_FORWARD); + path.append(File.separator); + path.append(this.transportTaskChild.getStationCode()); + path.append(File.separator); + path.append("grid_time_").append(DateUtil.format(transportTask.getEndTime(), "yyyyMMddHHmmss")).append(".nc"); + return path.toString(); + } + + /** + * 获取SRS文件存储路径 + * @return + */ + private String getSrsStoragePath(){ + StringBuilder path = new StringBuilder(); + path.append(dataFusionProperties.getNdcSRSPath()); + path.append(File.separator); + path.append(transportTaskChild.getAcqEndTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); + path.append(File.separator); + if(TransportSampleTypeEnum.B.name().equals(transportTaskChild.getSampleType())){ + path.append("flexpart.x."+this.getMetType()+".l1"); + }else if(TransportSampleTypeEnum.P.name().equals(transportTaskChild.getSampleType())){ + path.append("flexpart."+this.getMetType()+".l1"); + } + path.append(File.separator); + path.append(transportTaskChild.getStationCode()+".fp."); + path.append(transportTaskChild.getAcqEndTime().format(DateTimeFormatter.ofPattern("yyyyMMddHH"))); + path.append(".f9.srm.gz"); + return path.toString(); + } + + /** + * 获取气象类型 + * @return + */ + private String getMetType(){ + if (WeatherDataSourceEnum.PANGU.getKey().equals(transportTask.getUseMetType())){ + return WeatherDataSourceEnum.PANGU.name().toLowerCase(); + }else if (WeatherDataSourceEnum.GRAPHCAST.getKey().equals(transportTask.getUseMetType())){ + return WeatherDataSourceEnum.GRAPHCAST.name().toLowerCase(); + }else if (WeatherDataSourceEnum.CRA40.getKey().equals(transportTask.getUseMetType())){ + return WeatherDataSourceEnum.CRA40.name().toLowerCase(); + }else if (WeatherDataSourceEnum.NCEP.getKey().equals(transportTask.getUseMetType())){ + return WeatherDataSourceEnum.NCEP.name().toLowerCase(); + }else if (WeatherDataSourceEnum.FNL.getKey().equals(transportTask.getUseMetType())){ + return WeatherDataSourceEnum.FNL.name().toLowerCase(); + }else if (WeatherDataSourceEnum.T1H.getKey().equals(transportTask.getUseMetType())){ + return WeatherDataSourceEnum.T1H.name().toLowerCase(); + } + return Strings.EMPTY; + } + + /** + * 计算经度方向的实际距离(米) + * 考虑地球曲率效应,经度1°的距离随纬度变化 + * @param lat_input + * @return + */ + private Double cal_dx_1d(Double lat_input){ + return radius*2*pi*Math.cos(lat_input/180*pi)/360; + } + + private void compressContentToFile(List contents, Path outputFile) throws IOException { + try (FileOutputStream fileOut = new FileOutputStream(outputFile.toFile()); + GZIPOutputStream gzipOut = new GZIPOutputStream(fileOut)) { + + for (int i = 0; i < contents.size(); i++) { + String str = contents.get(i); + + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + gzipOut.write(bytes); + + if (i < contents.size() - 1) { + gzipOut.write('\n'); + } + } + } + } +} diff --git a/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/ForwardTaskExec.java b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/ForwardTaskExec.java new file mode 100644 index 0000000..960517d --- /dev/null +++ b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/ForwardTaskExec.java @@ -0,0 +1,222 @@ +package org.jeecg.task.flexparttask; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.io.FileUtil; +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.TransportTaskStatusEnum; +import org.jeecg.common.constant.enums.WeatherDataSourceEnum; +import org.jeecg.modules.base.entity.*; +import java.io.File; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class ForwardTaskExec extends AbstractTaskExec{ + + @Override + public void run() { + this.execute(); + } + + /** + * 执行任务 + */ + public void execute() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + try{ + //修改任务状态为执行中 + super.transportTaskService.updateTaskStatus(super.transportTask.getId(), TransportTaskStatusEnum.IN_OPERATION.getValue()); + //如果此任务已存在历史日志,先清除 + super.transportTaskService.deleteTaskLog(super.transportTask.getId()); + //检查气象数据 + this.checkMetData(); + //执行模拟 + this.execSimulation(); + }catch (Exception e){ + String taskErrorLog = "任务执行失败,原因:"+e.getMessage(); + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),taskErrorLog)); + this.transportTaskService.updateTaskStatus(super.transportTask.getId(),TransportTaskStatusEnum.FAILURE.getValue()); + throw e; + }finally { + //添加任务耗时 + 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); + super.transportTaskService.updateTaskStatusToCompleted(super.transportTask.getId(),result.doubleValue()); + String taskCompletedLog = "任务执行完成,耗时:"+min+"分钟"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),taskCompletedLog)); + } + } + + /** + * 检查气象数据 + */ + protected void checkMetData(){ + String msg = "检查气象数据"; + ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),msg)); + + LocalDateTime startTime = super.transportTask.getStartTime(); + LocalDateTime endTime = super.transportTask.getEndTime(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(WeatherData::getDataSource,super.transportTask.getUseMetType()); + queryWrapper.between(WeatherData::getDataStartTime,startTime,endTime); + List dataList = super.weatherDataMapper.selectList(queryWrapper); + long hours = ChronoUnit.HOURS.between(startTime, endTime)/6; + //一天至少4个气象数据,至少是00,06,12,18 + if(CollUtil.isEmpty(dataList) || dataList.size() < hours){ + 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(super.transportTask.getId(),formatMsg)); + throw new RuntimeException(formatMsg); + } + String finalMsg = "检查气象数据完毕"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),finalMsg)); + } + + /** + * 执行模拟 + */ + protected void execSimulation(){ + Process process = null; + try { + String paramMsg = "生成flexpart所需配置文件:param.config,stations.config,input_site_hour"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),paramMsg)); + //处理参数配置文件-param.config + String paramConfigPath = super.getParamConfigPath(super.transportTask.getUseMetType()); + String metDataPath = super.getMetDataPath(super.transportTask.getUseMetType()); + if(!FileUtil.exist(paramConfigPath)){ + FileUtil.touch(paramConfigPath); + } + StringBuilder paramContent = new StringBuilder(); + paramContent.append(super.transportTask.getStartTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"))).append("\n"); + paramContent.append(super.transportTask.getEndTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"))).append("\n"); + paramContent.append(super.transportTask.getStartTime().format(DateTimeFormatter.ofPattern("HHmmss"))).append("\n"); + paramContent.append(super.transportTask.getEndTime().format(DateTimeFormatter.ofPattern("HHmmss"))).append("\n"); + paramContent.append(super.transportTask.getParticleCount()).append("\n"); + 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"); + + //配置物种信息 + List species = super.transportTask.getSpecies(); + if(CollUtil.isEmpty(species)){ + String speciesMissingLog = super.transportTask.getTaskName()+"任务物种信息不存在,任务停止"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),speciesMissingLog)); + super.transportTaskService.updateTaskStatus(super.transportTask.getId(),TransportTaskStatusEnum.FAILURE.getValue()); + return; + } + for(int i=0;i stationConfigInfo = new ArrayList<>(); + String stationsConfigPath = super.getStationsConfigPath(super.transportTask.getUseMetType()); + if(!FileUtil.exist(stationsConfigPath)){ + FileUtil.touch(stationsConfigPath); + } + super.transportTask.getForwardChild().forEach(station ->{ + String format = "%s,%f,%f,%s"; + //最后0是以前版本的总释放量,改版后无用,给个默认值 + String row = String.format(format,station.getStationCode(),station.getLat(),station.getLon(),"0"); + stationConfigInfo.add(row); + }); + //最后一行需要换行,否则启动flexpart报错 + stationConfigInfo.add("\n"); + FileUtil.writeLines(stationConfigInfo,stationsConfigPath,"UTF-8"); + + //配置各站点排放数据 + //如果input_site_hour目录不存在先创建 + if(!FileUtil.exist(super.simulationProperties.getInputSiteHourPath())){ + FileUtil.mkdir(super.simulationProperties.getInputSiteHourPath()); + }else { + FileUtil.clean(super.simulationProperties.getInputSiteHourPath()); + } + Map> releaseInfoMap = new HashMap<>(); + for (TransportTaskForwardChild station : super.transportTask.getForwardChild()){ + if (CollUtil.isEmpty(station.getForwardReleaseChild())){ + String releaseMissingLog = station.getStationCode()+"站点释放信息缺失,任务停止"; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),releaseMissingLog)); + super.transportTaskService.updateTaskStatus(super.transportTask.getId(),TransportTaskStatusEnum.FAILURE.getValue()); + return; + } + releaseInfoMap.put(station.getStationCode(),station.getForwardReleaseChild()); + } + releaseInfoMap .forEach((stationCode,releaseInfoList)->{ + String stationReleaseFile = super.simulationProperties.getInputSiteHourPath() + File.separator + stationCode + ".txt"; + FileUtil.touch(stationReleaseFile); + List stationReleaseInfo = new ArrayList<>(); + releaseInfoList.forEach(releaseInfo ->{ + String format = "%s,%s,%s"; + String startTime = releaseInfo.getStartTime().format(DateTimeFormatter.ofPattern("yyyyMMddHH")); + String endTime = releaseInfo.getEndTime().format(DateTimeFormatter.ofPattern("yyyyMMddHH")); + BigDecimal releaseAmount = new BigDecimal(releaseInfo.getReleaseAmount()); + BigDecimal constant = new BigDecimal("1000"); + DecimalFormat scientificFormat = new DecimalFormat("0.00E0"); + double value = releaseAmount.divide(constant).doubleValue(); + String releaseAmountFormat = scientificFormat.format(value); + String row = String.format(format, startTime, endTime, releaseAmountFormat); + stationReleaseInfo.add(row); + }); + FileUtil.writeLines(stationReleaseInfo,stationReleaseFile,"UTF-8"); + }); + //获取正演脚本路径 + String scriptPath = this.getForwardScriptPath(super.transportTask.getUseMetType()); + String execScriptMsg = "执行任务脚本,开始模拟,路径为:"+scriptPath; + ProgressQueue.getInstance().offer(new ProgressEvent(super.transportTask.getId(),execScriptMsg)); + //ssh连接宿主机调用flexpart + JSchRemoteRunner jschRemoteRunner = new JSchRemoteRunner(); + jschRemoteRunner.setCommand(scriptPath); + jschRemoteRunner.login(super.serverProperties.getHost(),super.serverProperties.getPort(), + super.serverProperties.getUsername(),super.serverProperties.getPassword()); + jschRemoteRunner.execCommand(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + /** + * 获取正演脚本路径 + * @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; + } +} diff --git a/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/TransportTaskExec.java b/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/TransportTaskExec.java deleted file mode 100644 index 3eec0d1..0000000 --- a/jeecg-module-transport/src/main/java/org/jeecg/task/flexparttask/TransportTaskExec.java +++ /dev/null @@ -1,307 +0,0 @@ -package org.jeecg.task.flexparttask; - -import cn.hutool.core.date.DateUtil; -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.math.BigDecimal; -import java.math.RoundingMode; -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 transportTaskChildren; - private TransportSimulationProperties simulationProperties; - private SystemStorageProperties systemStorageProperties; - - /** - * 初始化 - */ - public void init(WeatherDataMapper weatherDataMapper, - TransportTaskService transportTaskService, - TransportTask transportTask, - List 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)); - this.transportTaskService.updateTaskStatus(this.transportTask.getId(),TransportTaskStatusEnum.FAILURE.getValue()); - throw e; - }finally { - //添加任务耗时 - 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.transportTaskService.updateTaskStatusToCompleted(this.transportTask.getId(),result.doubleValue()); - String taskCompletedLog = "任务执行完成,耗时:"+min+"分钟"; - ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),taskCompletedLog)); - } - } - - /** - * 检查气象数据 - */ - 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 queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(WeatherData::getDataSource,this.transportTask.getUseMetType()); - queryWrapper.between(WeatherData::getDataStartTime,startTime,endTime); - List 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.getParticleCount()).append("\n"); - paramContent.append(this.transportTask.getZ1()).append("\n"); - paramContent.append(this.transportTask.getZ2()).append("\n"); - paramContent.append(metDataPath).append("\n"); - paramContent.append(this.simulationProperties.getOutputPath()+File.separator+this.transportTask.getTaskName()).append("\n"); - if(TransportTaskModeEnum.FORWARD.getKey().equals(this.transportTask.getTaskMode())){ - paramContent.append(54).append("\n");//物种先固定写54对应XE135 - } else if (TransportTaskModeEnum.BACK_FORWARD.getKey().equals(this.transportTask.getTaskMode())) { - paramContent.append(64).append("\n");//物种先固定对应XE135 - } - FileUtil.writeString(paramContent.toString(),paramConfigPath,"UTF-8"); - //处理台站数据文件 - List 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); - }); - //最后一行需要换行,否则启动flexpart报错 - stationConfigInfo.add("\n"); - 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; - } -} diff --git a/jeecg-module-transport/src/main/java/org/jeecg/task/nuclearfacilitiestask/AssociatedNuclearFacilitiesTaskExec.java b/jeecg-module-transport/src/main/java/org/jeecg/task/nuclearfacilitiestask/AssociatedNuclearFacilitiesTaskExec.java new file mode 100644 index 0000000..83f9838 --- /dev/null +++ b/jeecg-module-transport/src/main/java/org/jeecg/task/nuclearfacilitiestask/AssociatedNuclearFacilitiesTaskExec.java @@ -0,0 +1,460 @@ +package org.jeecg.task.nuclearfacilitiestask; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; +import org.apache.logging.log4j.util.Strings; +import org.jeecg.common.constant.enums.AssociatedWaveformTaskEnum; +import org.jeecg.common.constant.enums.WaveformEventResultTypeEnum; +import org.jeecg.common.properties.DataFusionProperties; +import org.jeecg.config.datasource.DataSourceSwitcher; +import org.jeecg.modules.base.entity.copsdb.Origin; +import org.jeecg.modules.base.entity.rnauto.GardsWaveformEvent; +import org.jeecg.modules.base.entity.rnauto.GardsWaveformEventResult; +import org.jeecg.modules.base.mapper.GardsWaveformEventMapper; +import org.jeecg.modules.base.mapper.GardsWaveformEventResultMapper; +import org.jeecg.modules.base.mapper.OriginMapper; +import org.jeecg.vo.SRSRecord; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; + +@Slf4j +public class AssociatedNuclearFacilitiesTaskExec extends Thread{ + + private DataSourceTransactionManager transactionManager; + private TransactionDefinition transactionDefinition; + private DataFusionProperties dataFusionProperties; + private GardsWaveformEventMapper waveformEventMapper; + private GardsWaveformEventResultMapper waveformEventResultMapper; + private OriginMapper originMapper; + private Integer sampleId; + private Date acqEndTime; + private String stationCode; + private File idcSrsFile = null; + private File ndcSrsFile = null; + private List idcSrsContents; + private List ndcSrsContents; + + /** + * 初始化 + */ + public void init( + DataSourceTransactionManager transactionManager, + TransactionDefinition transactionDefinition, + DataFusionProperties dataFusionProperties, + GardsWaveformEventMapper waveformEventMapper, + OriginMapper originMapper, + GardsWaveformEventResultMapper waveformEventResultMapper, + Integer sampleId, + Date acqEndTime, + String stationCode){ + this.transactionManager = transactionManager; + this.transactionDefinition = transactionDefinition; + this.dataFusionProperties = dataFusionProperties; + this.waveformEventMapper = waveformEventMapper; + this.originMapper = originMapper; + this.waveformEventResultMapper = waveformEventResultMapper; + this.sampleId = sampleId; + this.acqEndTime = acqEndTime; + this.stationCode = stationCode; + } + + @Override + public void run() { + this.execute(); + } + + /** + * 执行任务 + */ + public void execute() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + try{ + //清除历史关联结果 + this.cleanHistoryResult(this.sampleId); + //修改任务状态为执行中 + this.updateTaskStatus(this.sampleId, AssociatedWaveformTaskEnum.IN_PROCESS.getValue()); + //检查srs文件是否存在 + this.checkSRSExist(); + //解析SRS文件数据 + this.parseSrsFile(); + //关联波形数据 + this.associationWaveform(); + this.execComplete(stopWatch,AssociatedWaveformTaskEnum.COMPLETE.getValue(),null); + }catch (Exception e){ + String taskErrorLog = "任务执行失败,原因:"+e.getMessage(); + this.execComplete(stopWatch,AssociatedWaveformTaskEnum.UNSUCCESSFUL.getValue(),taskErrorLog); + throw e; + } + } + + /** + * 检查IDC和NDC SRS文件是否存在 + */ + private void checkSRSExist(){ + String acqEndDate = DateUtil.format(this.acqEndTime,"yyyyMMdd"); + this.checkIdcSrsExist(acqEndDate); + this.checkNdcSrsExist(acqEndDate); + } + + /** + * 解析SRS文件 + */ + private void parseSrsFile(){ + if(Objects.nonNull(this.idcSrsFile)){ + this.idcSrsContents = this.readSrmFromGzStreaming(this.idcSrsFile); + } + if(Objects.nonNull(this.ndcSrsFile)){ + this.ndcSrsContents = this.readSrmFromGzStreaming(this.ndcSrsFile); + } + } + + /** + * 关联波形数据 + */ + private void associationWaveform(){ + Set idcEvents = this.associationSrs(this.idcSrsContents); + Set ndcEvents = this.associationSrs(this.ndcSrsContents); + DataSourceSwitcher.switchToOracle(); + final TransactionStatus transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); + try { + //保存关联结果 + if (CollUtil.isNotEmpty(idcEvents)){ + this.saveWaveformEventResult(idcEvents,WaveformEventResultTypeEnum.IDC.getKey()); + } + if (CollUtil.isNotEmpty(ndcEvents)){ + this.saveWaveformEventResult(ndcEvents,WaveformEventResultTypeEnum.NDC.getKey()); + } + //修改关联结果统计 + GardsWaveformEvent waveformEvent = this.waveformEventMapper.selectById(sampleId); + waveformEvent.setIdcEvents(idcEvents.size()); + waveformEvent.setNdcEvents(ndcEvents.size()); + waveformEvent.setDescription(Strings.EMPTY); + waveformEvent.setModdate(new Date()); + waveformEvent.setStatus(AssociatedWaveformTaskEnum.COMPLETE.getValue()); + this.waveformEventMapper.updateById(waveformEvent); + this.transactionManager.commit(transactionStatus); + }finally { + DataSourceSwitcher.clearDataSource(); + } + } + + /** + * 关联idc srs文件 + */ + private Set associationSrs(List srsContents){ + // 台站经度 台站纬度 开始测量日期 小时 结束测量时间 小时 系数 总共模拟时长 小时数 网格大小 台站编码 + // 139.08 36.30 20241203 12 20241203 18 0.1300000E+16 336 1 1 0.50 0.50 "JPX38" + String[] firstLine = srsContents.get(0).split("\\s+"); + Integer totalHour = Integer.parseInt(firstLine[8]); + Integer hourlyCoefficient = Integer.parseInt(firstLine[9]); + Double gridLonResolution = Double.parseDouble(firstLine[11]); + Double gridLatResolution = Double.parseDouble(firstLine[12]); + //根据测量结束时间,生成时间范围,用于查询范围内的波形事件 + LocalDateTime endTime = LocalDateTime.ofInstant(this.acqEndTime.toInstant(), ZoneId.of("Asia/Shanghai")); + endTime = endTime.withMinute(0).withSecond(0); + LocalDateTime startTime = endTime.minusHours(totalHour).minusHours(dataFusionProperties.getTraceabilityTime()*24); + System.out.println(startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + System.out.println(endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + //查询范围内波形事件 + List origins = this.selectOriginByTime(startTime,endTime); + //处理srs文件中的坐标记录格式化成对象 + List srsRecords = buildSRSRecord(srsContents, hourlyCoefficient,endTime); + //怎么对srsRecords进行按经纬度分组,分组后,对每组经纬度只求一个网格边界,然后再根据时间做origins查询,统计关联数 + Map, List> srsRecordsGroup = srsRecords.stream() + .collect(Collectors.groupingBy(record -> + new AbstractMap.SimpleImmutableEntry<>(record.getLon(), record.getLat()) + )); + return this.queryWaveform(origins, srsRecordsGroup, gridLonResolution,gridLatResolution); + } + + /** + * 检查IDC SRS文件是否存在 + */ + private void checkIdcSrsExist(String acqEndDate){ + //构建srs文件路径 + StringBuilder srsFilePath = new StringBuilder(); + srsFilePath.append(dataFusionProperties.getIdcSRSPath()); + srsFilePath.append(File.separator); + srsFilePath.append(acqEndDate); + srsFilePath.append(File.separator); + srsFilePath.append(dataFusionProperties.getIdcSrmParentDir()); + + //构建srm.gz文件名称 + StringBuilder srmFileName = new StringBuilder(); + srmFileName.append(stationCode); + srmFileName.append(".fp."); + srmFileName.append(DateUtil.format(this.acqEndTime,"yyyyMMddHH")); + srmFileName.append(".f9.srm.gz"); + + String fullFilePath = srsFilePath + File.separator + srmFileName; + File idcSrsFile = new File(fullFilePath); + if (idcSrsFile.exists()){ + this.idcSrsFile = idcSrsFile; + }else { + this.updateTaskNotSuccessful("IDC SRS文件不存在"); + } + } + + /** + * 检查NDC SRS文件是否存在 + */ + private void checkNdcSrsExist(String acqEndDate){ + //构建srs文件路径 + StringBuilder srsFilePath = new StringBuilder(); + srsFilePath.append(dataFusionProperties.getNdcSRSPath()); + srsFilePath.append(File.separator); + srsFilePath.append(acqEndDate); + srsFilePath.append(File.separator); + srsFilePath.append(dataFusionProperties.getNdcSrmParentDir()); + + //构建srm.gz文件名称 + StringBuilder srmFileName = new StringBuilder(); + srmFileName.append(stationCode); + srmFileName.append(".fp."); + srmFileName.append(DateUtil.format(this.acqEndTime,"yyyyMMddHH")); + srmFileName.append(".f9.srm.gz"); + + String fullFilePath = srsFilePath + File.separator + srmFileName; + File ndcSrsFile = new File(fullFilePath); + if (ndcSrsFile.exists()){ + this.ndcSrsFile = idcSrsFile; + }else { + this.updateTaskNotSuccessful("NDC SRS文件不存在"); + } + } + + /** + * 处理srs文件中的坐标记录格式化成对象 + * @param contents + * @param hourlyCoefficient + * @param acqEndTime 测量结束时间 + * @return + */ + private List buildSRSRecord(List contents,Integer hourlyCoefficient,LocalDateTime acqEndTime){ + List list = contents.parallelStream().skip(1).map(content->{ + String[] line = content.split("\\s+"); + BigDecimal val1 = new BigDecimal(line[4]); + BigDecimal val2 = new BigDecimal(dataFusionProperties.getFilterConditions()); + //过滤掉比较小的值 + if(val1.compareTo(val2) > 0){ + SRSRecord srsRecord = new SRSRecord(); + srsRecord.setLat(Double.parseDouble(line[1])); + srsRecord.setLon(Double.parseDouble(line[2])); + srsRecord.setHour(Integer.parseInt(line[3])*hourlyCoefficient); + srsRecord.setConc(line[4]); + LocalDateTime endTime = acqEndTime.minusHours(srsRecord.getHour()); + LocalDateTime startTime = acqEndTime.minusHours(srsRecord.getHour()+dataFusionProperties.getTraceabilityTime()*24); + long startSecond = startTime.atZone(ZoneId.of("Asia/Shanghai")).toEpochSecond(); + long endSecond = endTime.atZone(ZoneId.of("Asia/Shanghai")).toEpochSecond(); + srsRecord.setStartSecond(startSecond); + srsRecord.setEndSecond(endSecond); + return srsRecord; + } + return null; + }).filter(Objects::nonNull) + .toList(); + return list; + } + + /** + * 根据经纬度及时间查询范围内的波形数据,并进行计数,最后的计数就是关联的波形结果 + * @param srsRecordsGroup + */ + private Set queryWaveform(List origins,Map, List> srsRecordsGroup,Double gridLonResolution,Double gridLatResolution){ + Set result = ConcurrentHashMap.newKeySet(); + if (CollUtil.isNotEmpty(srsRecordsGroup) && CollUtil.isNotEmpty(origins)){ + srsRecordsGroup.forEach((key,srsRecords)->{ + double leftLon = Math.floor(key.getKey() / gridLonResolution) * gridLonResolution; + double bottomLat = Math.floor(key.getValue() / gridLatResolution) * gridLatResolution; + double rightLon = leftLon + gridLonResolution; + double topLat = bottomLat + gridLatResolution; + //统计srs中每条记录内关联的波形数据 + srsRecords.parallelStream().forEach(record->{ + Set orids = origins.stream() + .filter(origin ->origin.getLon() >= leftLon && origin.getLon() < rightLon && + origin.getLat() >= bottomLat && origin.getLat() < topLat && + origin.getTime() >= record.getStartSecond() && origin.getTime() <= record.getEndSecond()) + .map(Origin::getOrid).collect(Collectors.toSet()); + if (CollUtil.isNotEmpty(orids)){ + result.addAll(orids); + } + }); + }); + return result; + } + return result; + } + + /** + * 修改任务状态 + * @param sampleId + * @param status + */ + private void updateTaskStatus(Integer sampleId, Integer status) { + DataSourceSwitcher.switchToOracle(); + final TransactionStatus transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); + try { + GardsWaveformEvent waveformEvent = this.waveformEventMapper.selectById(sampleId); + waveformEvent.setStatus(status); + waveformEvent.setModdate(new Date()); + this.waveformEventMapper.updateById(waveformEvent); + this.transactionManager.commit(transactionStatus); + }catch (Exception e){ + this.transactionManager.rollback(transactionStatus); + log.error(e.getMessage(),e); + }finally { + DataSourceSwitcher.clearDataSource(); + } + } + + /** + * 修改任务关联未成功 + * @param description + */ + private void updateTaskNotSuccessful(String description) { + DataSourceSwitcher.switchToOracle(); + final TransactionStatus transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); + try { + GardsWaveformEvent waveformEvent = this.waveformEventMapper.selectById(this.sampleId); + waveformEvent.setStatus(AssociatedWaveformTaskEnum.UNSUCCESSFUL.getValue()); + waveformEvent.setDescription(waveformEvent.getDescription()+"\n"+description); + waveformEvent.setModdate(new Date()); + this.waveformEventMapper.updateById(waveformEvent); + this.transactionManager.commit(transactionStatus); + }catch (Exception e){ + this.transactionManager.rollback(transactionStatus); + log.error(e.getMessage(),e); + }finally { + DataSourceSwitcher.clearDataSource(); + } + } + + /** + * 清除历史关联结果 + * @param sampleId + */ + private void cleanHistoryResult(Integer sampleId) { + DataSourceSwitcher.switchToOracle(); + final TransactionStatus transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(GardsWaveformEventResult::getSampleId, sampleId); + this.waveformEventResultMapper.delete(queryWrapper); + this.transactionManager.commit(transactionStatus); + }catch (Exception e){ + this.transactionManager.rollback(transactionStatus); + log.error(e.getMessage(),e); + }finally { + DataSourceSwitcher.clearDataSource(); + } + } + + /** + * 获取时间范围内的波形数据 + * @param startTime + * @param endTime + * @return + */ + private List selectOriginByTime(LocalDateTime startTime,LocalDateTime endTime){ + DataSourceSwitcher.switchToOracle(); + try { + long startSecond = startTime.atZone(ZoneId.of("Asia/Shanghai")).toEpochSecond(); + long endSecond = endTime.atZone(ZoneId.of("Asia/Shanghai")).toEpochSecond(); + return originMapper.selectOriginByTime(startSecond,endSecond); + }finally { + DataSourceSwitcher.clearDataSource(); + } + } + + /** + * 保存关联结果 + * @param orids + */ + private void saveWaveformEventResult(Set orids,Integer type){ + DataSourceSwitcher.switchToOracle(); + final TransactionStatus transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); + try { + if(CollUtil.isNotEmpty(orids)){ + List waveformEventResults = new ArrayList<>(); + orids.forEach(orid->{ + GardsWaveformEventResult waveformEventResult = new GardsWaveformEventResult(); + waveformEventResult.setSampleId(this.sampleId); + waveformEventResult.setOrid(orid); + waveformEventResult.setType(type); + waveformEventResult.setModdate(new Date()); + waveformEventResults.add(waveformEventResult); + }); + this.waveformEventResultMapper.insert(waveformEventResults); + this.transactionManager.commit(transactionStatus); + } + }finally { + DataSourceSwitcher.clearDataSource(); + } + } + + /** + * 读取srm文件 + * @param gzFile + * @return + */ + public List readSrmFromGzStreaming(File gzFile){ + try { + List contents = new ArrayList<>(); + try (FileInputStream fis = new FileInputStream(gzFile); + GZIPInputStream gis = new GZIPInputStream(fis); + BufferedReader reader = new BufferedReader(new InputStreamReader(gis, StandardCharsets.UTF_8))) { + + String line; + while ((line = reader.readLine()) != null) { + contents.add(line); + } + } + return contents; + }catch (Exception e){ + log.error("SRS文件解析异常,路径为:{},原因为:{}",gzFile.getAbsolutePath(),e.getMessage()); + return List.of(); + } + } + + /** + * 任务执行完成 + * @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); + if (AssociatedWaveformTaskEnum.COMPLETE.getValue().equals(taskStatus)){ + log.info("任务执行完成,耗时{}分钟",result); + + }else if (AssociatedWaveformTaskEnum.UNSUCCESSFUL.getValue().equals(taskStatus)){ + log.error(taskErrorLog); + } + this.updateTaskStatus(this.sampleId,taskStatus); + } +} diff --git a/jeecg-module-transport/src/main/java/org/jeecg/task/waveformtask/AssociatedWaveformTaskExec.java b/jeecg-module-transport/src/main/java/org/jeecg/task/waveformtask/AssociatedWaveformTaskExec.java index 4c945d4..611985b 100644 --- a/jeecg-module-transport/src/main/java/org/jeecg/task/waveformtask/AssociatedWaveformTaskExec.java +++ b/jeecg-module-transport/src/main/java/org/jeecg/task/waveformtask/AssociatedWaveformTaskExec.java @@ -164,7 +164,8 @@ public class AssociatedWaveformTaskExec extends Thread{ String[] firstLine = srsContents.get(0).split("\\s+"); Integer totalHour = Integer.parseInt(firstLine[8]); Integer hourlyCoefficient = Integer.parseInt(firstLine[9]); - Double gridSize = Double.parseDouble(firstLine[11]); + Double gridLonResolution = Double.parseDouble(firstLine[11]); + Double gridLatResolution = Double.parseDouble(firstLine[12]); //根据测量结束时间,生成时间范围,用于查询范围内的波形事件 LocalDateTime endTime = LocalDateTime.ofInstant(this.acqEndTime.toInstant(), ZoneId.of("Asia/Shanghai")); endTime = endTime.withMinute(0).withSecond(0); @@ -180,7 +181,7 @@ public class AssociatedWaveformTaskExec extends Thread{ .collect(Collectors.groupingBy(record -> new AbstractMap.SimpleImmutableEntry<>(record.getLon(), record.getLat()) )); - return this.queryWaveform(origins, srsRecordsGroup, gridSize); + return this.queryWaveform(origins, srsRecordsGroup, gridLonResolution,gridLatResolution); } /** @@ -193,7 +194,7 @@ public class AssociatedWaveformTaskExec extends Thread{ srsFilePath.append(File.separator); srsFilePath.append(acqEndDate); srsFilePath.append(File.separator); - srsFilePath.append(dataFusionProperties.getSrmParentDir()); + srsFilePath.append(dataFusionProperties.getIdcSrmParentDir()); //构建srm.gz文件名称 StringBuilder srmFileName = new StringBuilder(); @@ -221,7 +222,7 @@ public class AssociatedWaveformTaskExec extends Thread{ srsFilePath.append(File.separator); srsFilePath.append(acqEndDate); srsFilePath.append(File.separator); - srsFilePath.append(dataFusionProperties.getSrmParentDir()); + srsFilePath.append(dataFusionProperties.getNdcSrmParentDir()); //构建srm.gz文件名称 StringBuilder srmFileName = new StringBuilder(); @@ -276,14 +277,14 @@ public class AssociatedWaveformTaskExec extends Thread{ * 根据经纬度及时间查询范围内的波形数据,并进行计数,最后的计数就是关联的波形结果 * @param srsRecordsGroup */ - private Set queryWaveform(List origins,Map, List> srsRecordsGroup,Double gridSize){ + private Set queryWaveform(List origins,Map, List> srsRecordsGroup,Double gridLonResolution,Double gridLatResolution){ Set result = ConcurrentHashMap.newKeySet(); if (CollUtil.isNotEmpty(srsRecordsGroup) && CollUtil.isNotEmpty(origins)){ srsRecordsGroup.forEach((key,srsRecords)->{ - double leftLon = Math.floor(key.getKey() / gridSize) * gridSize; - double bottomLat = Math.floor(key.getValue() / gridSize) * gridSize; - double rightLon = leftLon + gridSize; - double topLat = bottomLat + gridSize; + double leftLon = Math.floor(key.getKey() / gridLonResolution) * gridLonResolution; + double bottomLat = Math.floor(key.getValue() / gridLatResolution) * gridLatResolution; + double rightLon = leftLon + gridLonResolution; + double topLat = bottomLat + gridLatResolution; //统计srs中每条记录内关联的波形数据 srsRecords.parallelStream().forEach(record->{ Set orids = origins.stream() @@ -453,17 +454,4 @@ public class AssociatedWaveformTaskExec extends Thread{ } this.updateTaskStatus(this.sampleId,taskStatus); } - - public static void main(String[] args) { - LocalDateTime endTime = LocalDateTime.of(2024,12, 3,18, 0, 0); - LocalDateTime startTime = endTime.minusDays(74); - - long startSecond = startTime.atZone(ZoneId.of("Asia/Shanghai")).toEpochSecond(); - long endSecond = endTime.atZone(ZoneId.of("Asia/Shanghai")).toEpochSecond(); - - System.out.println(startSecond); - System.out.println(endSecond); - - } - } diff --git a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java index 302310c..c218409 100644 --- a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java +++ b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java @@ -321,7 +321,8 @@ public class WeatherDataServiceImpl extends ServiceImpl iPage = new Page<>(pageRequest.getPageNum(),pageRequest.getPageSize()); return this.baseMapper.selectPage(iPage, queryWrapper); } diff --git a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherTaskServiceImpl.java b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherTaskServiceImpl.java index 9f86058..1727b6f 100644 --- a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherTaskServiceImpl.java +++ b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherTaskServiceImpl.java @@ -9,6 +9,7 @@ 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; @@ -20,7 +21,8 @@ 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.WeatherTaskExec; +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; @@ -225,10 +227,17 @@ public class WeatherTaskServiceImpl extends ServiceImpl command = new ArrayList<>(); - String aiModelsPath = systemStorageProperties.getPanguEnvPath()+File.separator+"ai-models"; + String aiModelsPath = systemStorageProperties.getGraphcastEnvPath()+File.separator+"ai-models"; if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){ command.add(aiModelsPath); @@ -192,8 +182,8 @@ public class WeatherTaskExec extends Thread{ command.add("--path"); command.add(outputFileAddr); command.add("--assets"); - command.add("assets-panguweather"); - command.add("panguweather"); + command.add("assets-graphcast"); + command.add("--class od graphcast"); }else if (this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){ //离线文件还需处理 command.add(aiModelsPath); @@ -204,13 +194,13 @@ public class WeatherTaskExec extends Thread{ command.add("--path"); command.add(outputFileAddr); command.add("--assets"); - command.add("assets-panguweather"); - command.add("panguweather"); + 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.getPanguModelExecPath())); + processBuilder.directory(new File(systemStorageProperties.getGraphcastModelExecPath())); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); @@ -240,17 +230,17 @@ public class WeatherTaskExec extends Thread{ 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"; + String gribCopyCommandPath = systemStorageProperties.getGraphcastEnvPath()+File.separator+"grib_copy"; //grib_set命令 - String gribSetCommandPath = systemStorageProperties.getPanguEnvPath()+File.separator+"grib_set"; + String gribSetCommandPath = systemStorageProperties.getGraphcastEnvPath()+File.separator+"grib_set"; //公用gribProcessBuilder ProcessBuilder gribProcessBuilder = new ProcessBuilder(); - gribProcessBuilder.directory(new File(systemStorageProperties.getPanguModelExecPath())); + 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+"panguweather_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+gribFileSuffix+".grib"; + String gribCopyFileAddr = gribCopyTargetDir+File.separator+"graphcast_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+gribFileSuffix+".grib"; //切割grib文件命令 List gribCopyCommand = new ArrayList<>(); gribCopyCommand.add(gribCopyCommandPath); @@ -264,7 +254,7 @@ public class WeatherTaskExec extends Thread{ gribCopyProcess.waitFor(); //重新设置reftime信息 - String gribSetFileAddr = gribCopyTargetDir+File.separator+"panguweather_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+".grib"; + String gribSetFileAddr = gribCopyTargetDir+File.separator+"graphcast_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+".grib"; String date = LocalDateTimeUtil.format(startTime,"yyyyMMdd"); String time = LocalDateTimeUtil.format(startTime,"HHmm"); List gribSetCommand = new ArrayList<>(); @@ -338,11 +328,11 @@ public class WeatherTaskExec extends Thread{ * @param inputPath * @param targetPath */ - private void saveGribInfoToDB(String inputPath,String targetPath){ + private void saveGribInfoToDB(String inputPath,String targetPath,String gribFileSuffix){ String handleGribLog = "格式转换结束,处理grib文件数据入库"; ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),handleGribLog)); - List files = FileUtil.loopFiles(inputPath,file -> file.getName().startsWith("pangu_")); + List files = FileUtil.loopFiles(inputPath,file -> file.getName().startsWith("graphcast_") && !file.getName().contains(gribFileSuffix)); List targetFiles = new ArrayList<>(); if(!files.isEmpty()){ for(File file:files){ diff --git a/jeecg-module-weather/src/main/java/org/jeecg/task/PanGuWeatherTaskExec.java b/jeecg-module-weather/src/main/java/org/jeecg/task/PanGuWeatherTaskExec.java new file mode 100644 index 0000000..ca02843 --- /dev/null +++ b/jeecg-module-weather/src/main/java/org/jeecg/task/PanGuWeatherTaskExec.java @@ -0,0 +1,438 @@ +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 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 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 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 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 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 files = FileUtil.loopFiles(inputPath,file -> file.getName().startsWith("pangu_")); + List 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 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(); + } +}