This commit is contained in:
2026-01-09 15:36:46 +08:00
27 changed files with 814 additions and 208 deletions

View File

@@ -15,7 +15,7 @@ CREATE TABLE `simulation_job` (
`taskName` varchar(255) DEFAULT NULL COMMENT '计算任务所属任务名称',
`runId` varchar(64) DEFAULT NULL COMMENT '计算任务所属算力ID',
`runName` varchar(255) DEFAULT NULL COMMENT '计算任务所属算力名称',
`softwareId` bigint DEFAULT NULL COMMENT '使用软件的id',
`softwareId` varchar(128) DEFAULT NULL COMMENT '使用软件的id',
`inputFileId` bigint DEFAULT NULL COMMENT '求解文件对应的文件Id',
`jobId` varchar(255) DEFAULT NULL COMMENT '计算任务Id,job new 生成',
`jobDetailId` varchar(255) DEFAULT NULL COMMENT '计算任务添加时Id,job add 生成',

94
1-sql/2026-01-09/yang.sql Normal file
View File

@@ -0,0 +1,94 @@
drop table simulation_soft_config;
drop table simulation_command_placeholder;
-- 下面的表和app注册表 simulation_app_repository 主键id关联
CREATE TABLE `spdm_baseline`.`simulation_hpc_command` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`appUuid` varchar(128) NOT NULL COMMENT 'app注册的uuId',
`softName` varchar(255) NOT NULL COMMENT '软件名称,对应的simulation_app_repository的appName名字建议格式appName_v1',
`softVersion` varchar(32) DEFAULT NULL COMMENT '软件版本号',
`functionDsc` varchar(255) DEFAULT NULL COMMENT '功能描述(如:电池仿真)',
`command` text NOT NULL COMMENT '功能对应的CMD命令',
`postFileRegular` varchar(512) DEFAULT NULL COMMENT '预留-软件执行完成后筛选回传文件正则,用于过滤回传文件',
`creatorId` bigint DEFAULT NULL COMMENT '创建者ID',
`createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updaterId` bigint DEFAULT NULL COMMENT '更新者ID',
`updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `simulation_command_soft_name` (`softName`) COMMENT '按软件名称查询',
KEY `simulation_command_appUuid` (`appUuid`) COMMENT '按appid查询'
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仿真软件hpc命令配置表';
CREATE TABLE `spdm_baseline`.`simulation_hpc_command_placeholder` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`appUuid` varchar(128)NOT NULL COMMENT 'app注册的uuId',
`keyEnName` varchar(255) NOT NULL COMMENT '占位符英文名称',
`keyCnName` varchar(255) NOT NULL COMMENT '占位符中文名称',
`valueType` varchar(50) NOT NULL COMMENT '占位符值的类型file:共享云盘文件input:用户自定义输入)',
`isDisplay` varchar(1) NOT NULL DEFAULT 'Y' COMMENT '是否展示Y:是N:否N时必须填写默认值',
`featchType` varchar(16) NOT NULL DEFAULT 'input' COMMENT '动态参数获取值的来源input用户自定义default:默认值param:从参数里取',
`defaultValue` varchar(512) DEFAULT NULL COMMENT '默认值valueType为Input且isDisplay为N时必填',
`fileRegular` varchar(512) DEFAULT NULL COMMENT '文件正则表达式valueType为file时必填用于过滤对应的求解文件',
`creatorId` bigint DEFAULT NULL COMMENT '创建者ID',
`createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updaterId` bigint DEFAULT NULL COMMENT '更新者ID',
`updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `placeholder_appUuid` (`appUuid`) COMMENT 'app表hpc求解器id查询'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仿真工具hpc命令占位符配置表';
-- 记得仿真任务执行的时候也记录 appRepositoryId
-- isDisplay 是N 假如 defaultValue是null,则在从提交对象的参数获取数据赋值
-- 回传的正则没有就是全部所有的文件
-- file类型的先收集正则把文件先上传到工作目录
-- job 表修改
drop table simulation_job;
CREATE TABLE `simulation_job` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID自增',
`jobName` varchar(255) NOT NULL COMMENT '计算任务名称',
`coreNum` int DEFAULT NULL COMMENT '计算所需要核数',
`software` varchar(100) DEFAULT NULL COMMENT '计算软件的名称',
`jobType` varchar(255) DEFAULT NULL COMMENT '计算任务类型',
`independence` tinyint(1) NOT NULL DEFAULT '0' COMMENT '计算任务是否独立存在 0非独立任务 1独立任务',
`inputFiles` text COMMENT '求解文件JSON格式存储文件UUID列表',
`masterFile` varchar(255) DEFAULT NULL COMMENT '计算主文件文件UUID',
`taskId` varchar(64) DEFAULT NULL COMMENT '计算任务所属任务ID',
`taskName` varchar(255) DEFAULT NULL COMMENT '计算任务所属任务名称',
`runId` varchar(64) DEFAULT NULL COMMENT '计算任务所属算力ID',
`runName` varchar(255) DEFAULT NULL COMMENT '计算任务所属算力名称',
`softwareId` varchar(128) DEFAULT NULL COMMENT '使用软件的uuid',
`inputFileId` bigint DEFAULT NULL COMMENT '求解文件对应的文件Id',
`jobId` varchar(255) DEFAULT NULL COMMENT '计算任务Id,job new 生成',
`jobDetailId` varchar(255) DEFAULT NULL COMMENT '计算任务添加时Id,job add 生成',
`stdoutHpcFilePath` varchar(512) DEFAULT NULL COMMENT '任务执行输出的文件在Hpc的绝对路径共享目录+jobName文件回传)+uuid下面可能有多个文件',
`stdoutSpdmMinoFilePath` varchar(1024) DEFAULT NULL COMMENT '任务执行输出的文件在 MinIO 上的路径baseDir+jobName+uuid可能包含多个文件',
`nodeName` varchar(256) DEFAULT NULL COMMENT '节点名称',
`executCommand` text COMMENT '记录最终执行的执行对应工具命令',
`startTime` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '任务开始时间',
`endTime` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '任务结束时间',
`jobStatus` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '' COMMENT '任务状态spdm,+hpc返回的其他状态定时任务维护',
`solverName` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '求解器名称',
`totalKernelTime` bigint DEFAULT NULL COMMENT '总内核态时间(单位:毫秒)',
`totalUserTime` bigint DEFAULT NULL COMMENT '总用户态时间(单位:毫秒)',
`totalElapsedTime` bigint DEFAULT NULL COMMENT '作业耗费总秒数(单位:秒)',
`uuid` varchar(64) DEFAULT NULL COMMENT '配置提交前端生成不带横线的uuid',
`fileStatus` varchar(16) DEFAULT NULL COMMENT '任务结果回传状态generating,uploading,finished',
`creatorId` bigint DEFAULT NULL COMMENT '创建者ID',
`createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updaterId` bigint DEFAULT NULL COMMENT '更新者ID',
`updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`stdoutSpdmNasFilePath` varchar(1024) DEFAULT NULL COMMENT '任务执行输出的文件在 NAS 上的路径baseDir+jobName+uuid可能包含多个文件',
`tenantId` bigint DEFAULT NULL COMMENT '租户ID',
PRIMARY KEY (`id`),
KEY `idx_task_id` (`taskId`) COMMENT '按所属任务ID查询',
KEY `idx_run_id` (`runId`) COMMENT '按所属算力ID查询',
KEY `idx_job_name` (`jobName`) COMMENT '按任务名称查询',
KEY `idx_job_status` (`jobStatus`) COMMENT '按任务状态查询',
KEY `idx_softwareId` (`softwareId`) COMMENT '按软件id查询',
KEY `idx_node_name` (`nodeName`) COMMENT '按节点名称查询'
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仿真计算任务表';
-- 家里修改
-- 修改softwareId字段类型为varchar(128)
ALTER TABLE `simulation_job` MODIFY COLUMN `softwareId` varchar(128) DEFAULT NULL COMMENT '使用软件的id';

View File

@@ -531,7 +531,7 @@ CREATE TABLE `simulation_job` (
`taskName` varchar(255) DEFAULT NULL COMMENT '计算任务所属任务名称',
`runId` varchar(64) DEFAULT NULL COMMENT '计算任务所属算力ID',
`runName` varchar(255) DEFAULT NULL COMMENT '计算任务所属算力名称',
`softwareId` bigint DEFAULT NULL COMMENT '使用软件的id',
`softwareId` varchar(128) DEFAULT NULL COMMENT '使用软件的id',
`inputFileId` bigint NOT NULL COMMENT '求解文件对应的文件Id',
`jobId` varchar(255) DEFAULT NULL COMMENT '计算任务Id,job new 生成',
`jobDetailId` varchar(255) DEFAULT NULL COMMENT '计算任务添加时Id,job add 生成',

View File

@@ -10,7 +10,10 @@ public class HPCExecuteConfig extends BaseExecuteConfig {
private String masterFileRegularStr = "^.*\\.xml$";
// 先默认写死一个,后面前端配置传递
private String inputFilesRegularStr="^.+\\.json$";
// 节点的命令
// 节点的命令 这个不用传递了,配置在后台表里的
private String nodeExeCommand;
// app注册表的uuid
private String uuid;
}

View File

@@ -5,6 +5,7 @@ import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@Data
public class SubmitHpcTaskRemoteReq implements Serializable {
@@ -19,6 +20,9 @@ public class SubmitHpcTaskRemoteReq implements Serializable {
@Schema(description = "计算软件")
public String software;
@Schema(description = "计算软件uuid")
public String softwareId;
@Schema(description = "计算任务类型")
public String jobType;
@@ -70,4 +74,7 @@ public class SubmitHpcTaskRemoteReq implements Serializable {
@Schema(description = "任务求解文件从文件路径adapter里使用,executeMode=MANUAL")
public List<String> manualInputFilePaths;
@Schema(description = "任务流用户传递的参数,用于动态替换命令")
private Map<String,Object> params;
}

View File

@@ -24,4 +24,9 @@ public class SendMsgReq {
* 是否发送cid消息
*/
private boolean sendCid = true;
// 即时通发送人工号
private String sendJobNo;
// 即时通接收人工号
private String receiveJobNo;
}

View File

@@ -1,6 +1,7 @@
package com.sdm.common.entity.resp.task;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -56,6 +57,7 @@ public class PerformanceResp {
private String tenantId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
private Long creator;

View File

@@ -0,0 +1,108 @@
package com.sdm.common.utils;
import cn.hutool.core.util.ObjectUtil;
import com.sdm.common.entity.req.pbs.SubmitHpcTaskRemoteReq;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
@Slf4j
public class CommandReplaceUtil {
/**
* 通用替换方法:将命令字符串中的 %key 占位符替换为指定值
* @param command 原始命令字符串(包含%key形式的占位符
* @param key 要替换的占位符键名(不需要带%
* @param value 替换后的值
* @return 替换完成后的命令字符串
*/
public static String replaceCommandPlaceholder(String command, String key, Object value) {
// 1. 空值校验,避免空指针异常
if (command == null) {
throw new IllegalArgumentException("命令字符串(command)不能为null");
}
if (key == null || key.trim().isEmpty()) {
throw new IllegalArgumentException("占位符键名(key)不能为null或空字符串");
}
// value允许为空空值时直接替换成空字符串
String replaceValue = ObjectUtil.isEmpty(value) ? "" : value.toString();
// 2. 构建要替换的占位符(% + key
String placeholder = "%" + key.trim();
// 3. 替换所有匹配的占位符
return command.replace(placeholder, replaceValue);
}
/**
* 根据字段名动态获取对象的字段值
* @param obj 目标对象
* @param fieldName 字段名(如 masterFileRegularStr
* @return 字段值
* @throws NoSuchFieldException 字段不存在
* @throws IllegalAccessException 访问权限不足
*/
public static Object getFieldValue(Object obj, String fieldName) {
if (obj == null || fieldName == null || fieldName.isEmpty()) {
log.warn("获取对象为空,或者字段名为空");
return null;
}
// 1. 获取对象的Class对象
Class<?> clazz = obj.getClass();
// 2. 获取字段包括public/private/protected
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
} catch (Exception e) {
log.warn("反射获取字段{}异常,{}", fieldName,e.getMessage());
return null;
}
// 3. 设置访问权限即使字段是private也能访问
field.setAccessible(true);
// 4. 获取字段值
try {
Object o = field.get(obj);
return o;
} catch (Exception e) {
log.warn("反射获取字段值{}异常,{}", fieldName,e.getMessage());
return null;
}
}
public static void main(String[] args) {
// 1. 创建测试对象
SubmitHpcTaskRemoteReq req = new SubmitHpcTaskRemoteReq();
req.setJobName("流体力学计算");
req.setCoreNum(8);
req.setMasterFileRegularStr("/data/input/file.txt");
req.setSoftwareId("softwareId11");
// 获取String类型字段
String jobName = (String) getFieldValue(req, "jobName");
System.out.println("jobName = " + jobName); // 输出:流体力学计算
// 获取int类型字段
Integer coreNum = (Integer) getFieldValue(req, "coreNum");
System.out.println("coreNum = " + coreNum); // 输出8
// 获取private字段appRepositoryId
String appId = (String) getFieldValue(req, "softwareId");
System.out.println("softwareId = " + appId); // 输出1001
}
// // 测试示例
// public static void main(String[] args) {
// // 测试用的原始命令
// String command = "\\\\CARSAFE\\share\\solver\\RLithium\\reta.exe -i %masterFilePath -t %coreNum -m %memory";
// // 替换coreNum为4
// String newCommand = replaceCommandPlaceholder(command, "coreNum", "4");
// System.out.println("替换后命令:");
// System.out.println(newCommand);
//
// // 继续替换其他占位符示例
// newCommand = replaceCommandPlaceholder(newCommand, "memory", "8G");
// System.out.println("\n继续替换memory后");
// System.out.println(newCommand);
//
//
// }
}

View File

@@ -63,7 +63,10 @@ public class HpcHandler implements ExecutionHandler<Map<String, Object>,HPCExecu
@Override
public void execute(DelegateExecution execution, Map<String, Object> params, HPCExecuteConfig config) {
CoreLogger.info("hpc process excute,params:{},config:{}",JSONObject.toJSONString(params),JSONObject.toJSONString(config));
SubmitHpcTaskRemoteReq submitHpcTaskRemoteReq = convertParamsToReq(params,config.getNodeExeCommand());
SubmitHpcTaskRemoteReq submitHpcTaskRemoteReq = convertParamsToReq(params);
submitHpcTaskRemoteReq.setParams(params);
// 设置软件id
submitHpcTaskRemoteReq.setSoftwareId(config.getUuid());
String beforeNodeId = config.getBeforeNodeId();
String currentNodeId =execution.getCurrentActivityId();
String masterFileRegularStr = config.getMasterFileRegularStr();
@@ -89,6 +92,7 @@ public class HpcHandler implements ExecutionHandler<Map<String, Object>,HPCExecu
}
// 处理hpc求解文件路径
dealHpcFile(submitHpcTaskRemoteReq,beforeNodeId,currentNodeId, processDefinitionId,processInstanceId,executeMode,params);
//
// 实现HPC处理逻辑...
// INIT(初始化)/RUNNING(执行中)/SUCCESS(执行成功)/FAIL(执行失败)
String status = AsyncTaskStatusEnum.INIT.getCode();
@@ -226,10 +230,10 @@ public class HpcHandler implements ExecutionHandler<Map<String, Object>,HPCExecu
}
/**
* 将参数Map转换为SubmitHpcTaskRemoteReq对象的工具方法
* 将参数Map转换为SubmitHpcTaskRemoteReq对象的工具方法 String command
*/
private SubmitHpcTaskRemoteReq convertParamsToReq(Map<String, Object> params,String command) {
CoreLogger.error("convertParamsToReq start command:{}",command);
private SubmitHpcTaskRemoteReq convertParamsToReq(Map<String, Object> params) {
CoreLogger.error("convertParamsToReq start ");
SubmitHpcTaskRemoteReq req = new SubmitHpcTaskRemoteReq();
if (params == null) {
return req;
@@ -258,14 +262,14 @@ public class HpcHandler implements ExecutionHandler<Map<String, Object>,HPCExecu
// String command =(params.get("command")==null||StringUtils.isBlank(params.get("command").toString()))?
// "\\\\CARSAFE\\share\\solver\\RLithium\\reta.exe -i %s" : params.get("command").toString();
// 只是测试环境用于兜底mock
if(StringUtils.isBlank(command)){
command = mockCommand;
}
if(StringUtils.isBlank(command)){
CoreLogger.error("command is empty!!!!!");
throw new RuntimeException("command is empty");
}
req.setCommand(command);
// if(StringUtils.isBlank(command)){
// command = mockCommand;
// }
// if(StringUtils.isBlank(command)){
// CoreLogger.error("command is empty!!!!!");
// throw new RuntimeException("command is empty");
// }
// req.setCommand(command);
req.setProjectname(params.get("projectname").toString());
// req.setFeatchFileType(params.get("featchFileType").toString());
// req.setBeforeNodeId(params.get("beforeNodeId").toString());

View File

@@ -12,15 +12,11 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
@@ -35,6 +31,29 @@ public class TaskAdapter implements ITaskFeignClient {
@Autowired
private TaskController taskController;
@Value("${testEnStr:}")
private String enStr;
@Value("${testEnStr2:}")
private String testEnStr2;
@Value("${pbs.task.impl}")
private String pbsImpl;
@GetMapping("/testEn")
@Operation(summary = "作业提交")
public SdmResponse<Map<String,Object>> testEn() {
Map<String, Object> map = new HashMap<>();
map.put("enStr", enStr);
map.put("pbsImpl", pbsImpl);
map.put("testEnStr2", testEnStr2);
return SdmResponse.success(map);
}
@PostMapping("/adapterSubmitHpcJob")
@Operation(summary = "作业提交")
public SdmResponse<String> adapterSubmitHpcJob(@RequestBody SubmitHpcTaskRemoteReq req) {

View File

@@ -14,7 +14,7 @@ import com.sdm.common.utils.HpcCommandExcuteUtil;
import com.sdm.pbs.model.bo.HpcJobStatusInfo;
import com.sdm.pbs.model.bo.HpcResouceInfo;
import com.sdm.pbs.model.entity.SimulationJob;
import com.sdm.pbs.model.entity.SimulationSoftConfig;
import com.sdm.pbs.model.entity.SimulationHpcCommand;
import com.sdm.pbs.model.req.JobFileCallBackReq;
import com.sdm.pbs.model.req.QueryJobReq;
import com.sdm.pbs.model.req.SubmitHpcTaskReq;
@@ -114,8 +114,8 @@ public class TaskController {
@GetMapping("/querySoftConfig")
@Operation(summary = "spdm系统查询hpc求解器指令配置")
SdmResponse<Map<String, List<SimulationSoftConfig>>> querySoftConfig (@RequestParam String softName) {
return pbsServiceDecorator.querySoftConfig(softName);
SdmResponse<SimulationHpcCommand> querySoftConfig (@RequestParam String appUuid) {
return pbsServiceDecorator.querySoftConfig(appUuid);
}

View File

@@ -1,7 +1,7 @@
package com.sdm.pbs.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sdm.pbs.model.entity.SimulationCommandPlaceholder;
import com.sdm.pbs.model.entity.SimulationHpcCommandPlaceholder;
/**
* <p>
@@ -11,6 +11,6 @@ import com.sdm.pbs.model.entity.SimulationCommandPlaceholder;
* @author author
* @since 2025-11-05
*/
public interface SimulationCommandPlaceholderMapper extends BaseMapper<SimulationCommandPlaceholder> {
public interface SimulationCommandPlaceholderMapper extends BaseMapper<SimulationHpcCommandPlaceholder> {
}

View File

@@ -1,7 +1,7 @@
package com.sdm.pbs.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sdm.pbs.model.entity.SimulationSoftConfig;
import com.sdm.pbs.model.entity.SimulationHpcCommand;
/**
* <p>
@@ -11,6 +11,6 @@ import com.sdm.pbs.model.entity.SimulationSoftConfig;
* @author author
* @since 2025-11-05
*/
public interface SimulationSoftConfigMapper extends BaseMapper<SimulationSoftConfig> {
public interface SimulationSoftConfigMapper extends BaseMapper<SimulationHpcCommand> {
}

View File

@@ -0,0 +1,11 @@
package com.sdm.pbs.model.bo;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class CommandResult {
private String command;
private String formatCommand;
}

View File

@@ -1,74 +0,0 @@
package com.sdm.pbs.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 仿真工具命令占位符配置表
* </p>
*
* @author author
* @since 2025-12-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("simulation_command_placeholder")
@Schema(description = "仿真工具命令占位符配置表")
public class SimulationCommandPlaceholder implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "自增主键")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "占位符英文名称")
@TableField("keyEnName")
private String keyEnName;
@Schema(description = "占位符中文名称")
@TableField("keyCnName")
private String keyCnName;
@Schema(description = "占位符值的类型file_exact_match:文件完全匹配file_regex_match:文件正则匹配," +
"hpc_file_selectHpc节点文件选择hpc_file_regex_matchHpc节点目录正则local_file_select本地文件选择custom_input用户自定义输入值")
@TableField("valueType")
private String valueType;
@Schema(description = "占位符的值,用户输入后赋值传递")
@TableField(exist = false)
private String inputValue="";
@Schema(description = "创建者ID")
@TableField("creatorId")
@JsonIgnore
private Long creatorId;
@Schema(description = "创建时间")
@TableField(value = "createTime", fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonIgnore
private LocalDateTime createTime;
@Schema(description = "更新者ID")
@TableField("updaterId")
@JsonIgnore
private Long updaterId;
@Schema(description = "修改时间")
@TableField(value = "updateTime", fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonIgnore
private LocalDateTime updateTime;
}

View File

@@ -2,6 +2,7 @@ package com.sdm.pbs.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -9,6 +10,7 @@ import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
@@ -22,9 +24,9 @@ import java.util.Map;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("simulation_soft_config")
@Schema(description = "仿真软件命令配置表")
public class SimulationSoftConfig implements Serializable {
@TableName("simulation_hpc_command")
@Schema(description = "仿真软件hpc命令配置表")
public class SimulationHpcCommand implements Serializable {
private static final long serialVersionUID = 1L;
@@ -32,42 +34,55 @@ public class SimulationSoftConfig implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "软件名称")
@TableField("softName")
@Schema(description = "app注册的Id")
@TableField(value = "appUuid",select = true)
private String appUuid;
@Schema(description = "软件名称,对应的simulation_app_repository的appName名字建议格式appName_v1")
@TableField(value = "softName",select = true)
private String softName;
@Schema(description = "软件版本号")
@TableField("softVersion")
@TableField(value = "softVersion",select = true)
private String softVersion;
@Schema(description = "功能描述(如:电池仿真)")
@TableField("functionDsc")
@TableField(value = "functionDsc",select = true)
private String functionDsc;
@Schema(description = "功能对应的CMD命令")
@TableField("command")
@TableField(value = "command",select = true)
private String command;
@Schema(description = "预留-软件执行完成后筛选回传文件正则,用于过滤回传文件")
@TableField(value = "postFileRegular",select = true)
@JsonIgnore
private String postFileRegular;
@Schema(description = "创建者ID")
@TableField("creatorId")
@TableField(value = "creatorId",select = true)
@JsonIgnore
private Long creatorId;
@Schema(description = "创建时间")
@TableField(value = "createTime", fill = FieldFill.INSERT)
@TableField(value = "createTime", fill = FieldFill.INSERT,select = true)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonIgnore
private LocalDateTime createTime;
@Schema(description = "更新者ID")
@TableField("updaterId")
@TableField(value = "updaterId",select = false)
@JsonIgnore
private Long updaterId;
@Schema(description = "修改时间")
@TableField(value = "updateTime", fill = FieldFill.INSERT_UPDATE)
@TableField(value = "updateTime", fill = FieldFill.INSERT_UPDATE,select = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonIgnore
private LocalDateTime updateTime;
@Schema(description= "自定义占位符,只有列表展示使用key 就是占位符")
@TableField(value = "commandExpand", insertStrategy = FieldStrategy.NEVER,select = false,updateStrategy = FieldStrategy.NEVER)
private Map<String,SimulationCommandPlaceholder> commandExpand;
@Schema(description = "命令动态扩展的数据SimulationHpcCommandPlaceholder对象")
@TableField(exist = false)
private Map<String,Object>commandExpand = new HashMap<>();
}

View File

@@ -0,0 +1,99 @@
package com.sdm.pbs.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 仿真工具命令占位符配置表
* </p>
*
* @author author
* @since 2025-12-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("simulation_hpc_command_placeholder")
@Schema(description = "仿真工具hpc命令占位符配置表")
public class SimulationHpcCommandPlaceholder implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "自增主键")
@TableId(value = "id", type = IdType.AUTO)
@JsonIgnore
private Long id;
@Schema(description = "app注册的Id")
@TableField(value = "appUuid",select = true)
private String appUuid;
@Schema(description = "占位符英文名称")
@TableField(value = "keyEnName",select = true)
private String keyEnName;
@Schema(description = "占位符中文名称")
@TableField(value = "keyCnName",select = true)
private String keyCnName;
@Schema(description = "占位符值的类型file:共享云盘文件input:用户自定义输入)")
@TableField(value = "valueType",select = true)
private String valueType;
@Schema(description = "是否展示Y:是N:否N时必须填写默认值")
@TableField(value = "isDisplay",select = true)
@JsonIgnore
private String isDisplay;
@Schema(description = "是否拼接Y:是N:否N时不用拼接到命令")
@TableField(value = "featchType",select = true)
@JsonIgnore
private String featchType;
@Schema(description = "默认值valueType为file且isDisplay为N时必填")
@TableField(value = "defaultValue",select = true)
@JsonIgnore
private String defaultValue;
@Schema(description = "文件正则表达式valueType为file时必填用于过滤对应的求解文件")
@TableField(value = "fileRegular",select = true)
@JsonIgnore
private String fileRegular;
@Schema(description = "创建者ID")
@TableField(value = "creatorId",select = false)
@JsonIgnore
private Long creatorId;
@Schema(description = "创建时间")
@TableField(value = "createTime", fill = FieldFill.INSERT,select = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonIgnore
private LocalDateTime createTime;
@Schema(description = "更新者ID")
@TableField(value = "updaterId",select = false)
@JsonIgnore
private Long updaterId;
@Schema(description = "修改时间")
@TableField(value = "updateTime", fill = FieldFill.INSERT_UPDATE,select = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonIgnore
private LocalDateTime updateTime;
@Schema(description = "占位符的值,显示给用户的字段,用户输入后赋值传递")
@TableField(exist = false)
private String inputValue = "";
}

View File

@@ -77,7 +77,7 @@ public class SimulationJob implements Serializable {
@Schema(description = "使用软件的id")
@TableField("softwareId")
private Long softwareId;
private String softwareId;
@Schema(description = "求解文件对应的文件Id")
@TableField("inputFileId")

View File

@@ -7,6 +7,8 @@ import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Data
public class SubmitHpcTaskReq {
@@ -64,7 +66,7 @@ public class SubmitHpcTaskReq {
public String projectname;
@Schema(description = "软件的id")
public Long softwareId;
public String softwareId;
@Schema(description = "计算任务回传minio的路径")
public String stdoutSpdmMinoFilePath;
@@ -78,5 +80,7 @@ public class SubmitHpcTaskReq {
@Schema(description = "求解文件本地路径,spdm工作流引擎会传递过来")
public List<String> inputFileLocalPaths = new ArrayList<>();
@Schema(description = "任务流用户传递的参数,用于动态替换命令")
private Map<String,Object> params;
}

View File

@@ -1,7 +1,7 @@
package com.sdm.pbs.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sdm.pbs.model.entity.SimulationCommandPlaceholder;
import com.sdm.pbs.model.entity.SimulationHpcCommandPlaceholder;
public interface ISimulationCommandPlaceholderService extends IService<SimulationCommandPlaceholder> {
public interface ISimulationCommandPlaceholderService extends IService<SimulationHpcCommandPlaceholder> {
}

View File

@@ -1,7 +1,7 @@
package com.sdm.pbs.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sdm.pbs.model.entity.SimulationSoftConfig;
import com.sdm.pbs.model.entity.SimulationHpcCommand;
public interface ISimulationSoftConfigService extends IService<SimulationSoftConfig> {
public interface ISimulationSoftConfigService extends IService<SimulationHpcCommand> {
}

View File

@@ -2,9 +2,9 @@ package com.sdm.pbs.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sdm.pbs.dao.SimulationCommandPlaceholderMapper;
import com.sdm.pbs.model.entity.SimulationCommandPlaceholder;
import com.sdm.pbs.model.entity.SimulationHpcCommandPlaceholder;
import org.springframework.stereotype.Service;
@Service
public class SimulationCommandPlaceholderServiceImpl extends ServiceImpl<SimulationCommandPlaceholderMapper, SimulationCommandPlaceholder> implements ISimulationCommandPlaceholderService {
public class SimulationCommandPlaceholderServiceImpl extends ServiceImpl<SimulationCommandPlaceholderMapper, SimulationHpcCommandPlaceholder> implements ISimulationCommandPlaceholderService {
}

View File

@@ -1,5 +1,6 @@
package com.sdm.pbs.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
@@ -15,17 +16,20 @@ import com.sdm.common.entity.resp.pbs.hpc.FileNodeInfo;
import com.sdm.common.feign.impl.system.MessageFeignClientImpl;
import com.sdm.common.feign.inter.flowable.IFlowableFeignClient;
import com.sdm.common.log.CoreLogger;
import com.sdm.common.utils.CommandReplaceUtil;
import com.sdm.common.utils.HpcCommandExcuteUtil;
import com.sdm.common.utils.PageUtils;
import com.sdm.pbs.model.bo.CommandResult;
import com.sdm.pbs.model.bo.HpcJobStatusInfo;
import com.sdm.pbs.model.bo.HpcResouceInfo;
import com.sdm.pbs.model.entity.SimulationCommandPlaceholder;
import com.sdm.pbs.model.entity.SimulationHpcCommand;
import com.sdm.pbs.model.entity.SimulationHpcCommandPlaceholder;
import com.sdm.pbs.model.entity.SimulationJob;
import com.sdm.pbs.model.entity.SimulationSoftConfig;
import com.sdm.pbs.model.req.JobFileCallBackReq;
import com.sdm.pbs.model.req.QueryJobReq;
import com.sdm.pbs.model.req.SubmitHpcTaskReq;
import com.sdm.pbs.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -37,11 +41,14 @@ import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import java.time.LocalDateTime;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Slf4j
@Service
@Qualifier("decoratorPbsService")
@ConditionalOnProperty(name = "pbs.task.impl", havingValue = "hpc")
@@ -89,46 +96,139 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
public SdmResponse<String> submitHpcJob(SubmitHpcTaskReq req) {
//1. 上传hpc主文件 及 其他文件
MultipartFile masterFile = req.getMasterFile();
// hpc共享机器+ subDir 这个就确定是工作目录
String subDir = req.getJobName()+"\\"+System.currentTimeMillis();
// 获取hpc工作目录
String subDir = generateHpcSubDir(req);
// webClient 调用上传这个是主文件求解算出的文件及stdout文件都指定这个文件夹下面
String masterFilePath = hpcCommandExcuteUtil.uploaHpcFile(masterFile,subDir);
dealInputFiles(req,subDir);
String masterFilePath = hpcCommandExcuteUtil.uploaHpcFile(masterFile, subDir);
req.setMasterFilePath(masterFilePath);
// 上传从文件
dealInputFiles(req, subDir);
// 任务输出的文件夹
String hpcOutPutDir = extractDirectory(masterFilePath);
req.setWorkDir(hpcOutPutDir);
// 前置处理 替换求解文件
String command="";
if(StringUtils.isNotBlank(req.getCommand())) {
command=req.getCommand();
}else {
SimulationSoftConfig simulationSoftConfig = simulationSoftConfigService.lambdaQuery().
eq(SimulationSoftConfig::getSoftName,req.getSoftware()).one();
command = simulationSoftConfig.getCommand();
// 2.处理命令拼接和参数替换
CommandResult commandResult = buildAndReplaceHpcCommand(req, masterFilePath);
if (StringUtils.isBlank(commandResult.getCommand())) {
log.error("Hpc执行失败command命令不能为空{}",JSONObject.toJSONString(req));
throw new RuntimeException("Hpc执行失败command命令不能为空");
}
if(StringUtils.isBlank(command)) {
return SdmResponse.failed("command命令不能为空软件名称:"+req.getSoftware());
}
// 处理 拼接命令 \\CARSAFE\share\solver\RLithium\reta.exe -i %s
String formatCommand = String.format(command, masterFilePath);
req.setCommand(formatCommand);
req.setMasterFilePath(masterFilePath);
req.setCommand(commandResult.getFormatCommand());
// 3. 提交
SdmResponse<String> response = pbsService.submitHpcJob(req);
String jobId="";
if(response.isSuccess()&&StringUtils.isNotEmpty(response.getData())) {
String jobId = "";
if (response.isSuccess() && StringUtils.isNotEmpty(response.getData())) {
jobId = response.getData();
}
if(StringUtils.isNotEmpty(jobId)) {
if(StringUtils.isNotEmpty(jobId)){
log.error("Hpc执行失败返回结果{}",JSONObject.toJSONString(response));
throw new RuntimeException("Hpc执行失败返回jobId空");
}
// 4. 保存任务信息到数据库
saveSimulationJobToDb(req, jobId, hpcOutPutDir, commandResult.getCommand());
return SdmResponse.success(jobId);
}
/**
* 生成HPC任务的工作子目录路径简化版
* 目录规则用户ID(非空时)\任务名称\时间戳
* @param req 提交HPC任务的请求参数
* @return 拼接好的子目录字符串
*/
private String generateHpcSubDir(SubmitHpcTaskReq req) {
Long userId = ThreadLocalContext.getUserId();
log.info("Hpc任务执行开始用户id:{}", userId);
// 拼接逻辑
String subDirPrefix = Objects.isNull(userId) ? "" : (String.valueOf(userId) + "\\");
String subDir = subDirPrefix + req.getJobName() + "\\" + System.currentTimeMillis();
return subDir;
}
/**
* 构建并替换HPC命令中的动态参数
* @param req 任务请求参数
* @param masterFilePath 主文件路径
* @return 包含原始命令和格式化命令的结果对象
*/
private CommandResult buildAndReplaceHpcCommand(SubmitHpcTaskReq req, String masterFilePath) {
String command = "";
String formatCommand = "";
// 优先使用传递的command,然后替换求解文件即可
if (StringUtils.isNotBlank(req.getCommand())) {
command = req.getCommand();
// 处理 拼接命令 \\CARSAFE\share\solver\RLithium\reta.exe -i %s
formatCommand = String.format(command, masterFilePath);
} else {
// 命令
SimulationHpcCommand simulationHpcCommand = simulationSoftConfigService.lambdaQuery()
.eq(SimulationHpcCommand::getAppUuid, req.getSoftwareId()).one();
// 动态参数
List<SimulationHpcCommandPlaceholder> placeholders = simulationCommandPlaceholderService.lambdaQuery()
.eq(SimulationHpcCommandPlaceholder::getAppUuid, req.getSoftwareId())
.list();
// 配置在表中的包含动态参数的命令
if (ObjectUtil.isNull(simulationHpcCommand) || StringUtils.isBlank(simulationHpcCommand.getCommand())) {
throw new RuntimeException("该应用没有初始化配置command");
}
command = simulationHpcCommand.getCommand();
// 命令中动态参数的处理替换
for (SimulationHpcCommandPlaceholder placeholder : placeholders) {
String keyEnName = placeholder.getKeyEnName();
Object replaceValue = null;
// 用户手动输入
if (Objects.equals(placeholder.getFeatchType(), "input")) {
replaceValue = req.getParams().get(keyEnName);
// 用户输入是空就使用配置的默认值
if (ObjectUtil.isNull(replaceValue)) { // 修复原代码的判断逻辑错误
log.warn("Hpc命令动态参数替换用户输入是空参数名{}", keyEnName);
replaceValue = placeholder.getDefaultValue();
}
}
// 入参
else if (Objects.equals(placeholder.getFeatchType(), "param")) {
replaceValue = CommandReplaceUtil.getFieldValue(req, keyEnName);
if (ObjectUtil.isNull(replaceValue)) { // 修复原代码的判断逻辑错误
log.warn("Hpc命令动态参数替换入参反射是空参数名{}", keyEnName);
replaceValue = placeholder.getDefaultValue();
}
}
// 默认兜底,比如从文件 就是null,最后命令就不拼接从文件
else if (Objects.equals(placeholder.getFeatchType(), "default")) {
replaceValue = placeholder.getDefaultValue();
if (ObjectUtil.isNull(replaceValue)) { // 修复原代码的判断逻辑错误
log.warn("Hpc命令动态参数替换默认值是空参数名{}", keyEnName);
}
}
command = CommandReplaceUtil.replaceCommandPlaceholder(command, keyEnName, replaceValue);
}
}
return new CommandResult(command, formatCommand);
}
/**
* 保存模拟任务信息到数据库
* @param req 任务请求参数
* @param jobId 任务ID
* @param hpcOutPutDir 输出目录
* @param command 执行命令
*/
private void saveSimulationJobToDb(SubmitHpcTaskReq req, String jobId, String hpcOutPutDir, String command) {
if (StringUtils.isNotEmpty(jobId)) {
// 数据入库
SimulationJob simulationJob = new SimulationJob();
// 基础字段
// simulationJob.setId(1L);
// simulationJob.setId(1L);
simulationJob.setJobName(req.getJobName());
simulationJob.setCoreNum(req.getCoreNum());
simulationJob.setSoftware(req.getSoftware());
simulationJob.setJobType(req.getJobType());
simulationJob.setIndependence(req.isIndependence());
// simulationJob.setInputFiles(JSONObject.toJSONString(req.getInputFiles()));
// simulationJob.setInputFiles(JSONObject.toJSONString(req.getInputFiles()));
// 主文件位置
simulationJob.setMasterFile(req.getMasterFilePath());
// 求解文件集合
@@ -140,20 +240,20 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
// 软件及文件关联
simulationJob.setSoftwareId(req.getSoftwareId());
// 下面的待定
// simulationJob.setInputFileId(null);
// simulationJob.setInputFileId(null);
simulationJob.setJobId(jobId);
// 没必要要
// simulationJob.setJobDetailId("");
// simulationJob.setJobDetailId("");
// 文件路径 共享目录+jobName文件回传)+uuid下面可能有多个文件
simulationJob.setStdoutHpcFilePath(hpcOutPutDir);
simulationJob.setStdoutSpdmMinoFilePath(req.getStdoutSpdmMinoFilePath());
simulationJob.setStdoutSpdmNasFilePath(req.getStdoutSpdmNasFilePath());
// 执行信息 定时任务回传的时候修改
// simulationJob.setNodeName("");
// simulationJob.setNodeName("");
simulationJob.setExecutCommand(command);
// 执行信息 定时任务回传的时候修改
// simulationJob.setStartTime("2025-11-30 10:00:00");
// simulationJob.setEndTime("2025-11-30 12:30:00");
// simulationJob.setStartTime("2025-11-30 10:00:00");
// simulationJob.setEndTime("2025-11-30 12:30:00");
simulationJob.setJobStatus("Configuring");
// 求解器名称
simulationJob.setSolverName(req.getSoftware());
@@ -162,12 +262,12 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
simulationJob.setTotalUserTime(null);
simulationJob.setTotalElapsedTime(null);
// 标识及状态
// simulationJob.setUuid(null);
// simulationJob.setUuid(null);
simulationJob.setFileStatus("generating");
// 审计字段
Long userId = ThreadLocalContext.getUserId();
Long tenantId = ThreadLocalContext.getTenantId();
CoreLogger.info("submitHpcJob save db userId:{},tenantId:{}",userId,tenantId);
CoreLogger.info("submitHpcJob save db userId:{},tenantId:{}", userId, tenantId);
simulationJob.setCreatorId(userId);
simulationJob.setTenantId(tenantId);
simulationJob.setCreateTime(LocalDateTime.now());
@@ -175,9 +275,147 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
simulationJob.setUpdateTime(LocalDateTime.now());
simulationJobService.save(simulationJob);
}
return SdmResponse.success(jobId);
}
// @Override
// public SdmResponse<String> submitHpcJob(SubmitHpcTaskReq req) {
// //1. 上传hpc主文件 及 其他文件
// MultipartFile masterFile = req.getMasterFile();
// // hpc共享机器+ subDir 这个就确定是工作目录了 todo 加上用户目录
// String subDir = req.getJobName()+"\\"+System.currentTimeMillis();
// // webClient 调用上传这个是主文件求解算出的文件及stdout文件都指定这个文件夹下面
// String masterFilePath = hpcCommandExcuteUtil.uploaHpcFile(masterFile,subDir);
// req.setMasterFilePath(masterFilePath);
// // 上传从文件
// dealInputFiles(req,subDir);
// // 任务输出的文件夹
// String hpcOutPutDir = extractDirectory(masterFilePath);
// req.setWorkDir(hpcOutPutDir);
// // 前置处理 替换求解文件
// String command="";
// String formatCommand="";
// // 优先使用传递的command,然后替换求解文件即可
// if(StringUtils.isNotBlank(req.getCommand())) {
// command=req.getCommand();
// // 处理 拼接命令 \\CARSAFE\share\solver\RLithium\reta.exe -i %s
// formatCommand = String.format(command, masterFilePath);
// }else {
// // 命令
// SimulationHpcCommand simulationHpcCommand = simulationSoftConfigService.lambdaQuery().
// eq(SimulationHpcCommand::getAppRepositoryId,req.getAppRepositoryId()).one();
// // 动态参数
// List<SimulationHpcCommandPlaceholder> placeholders = simulationCommandPlaceholderService.lambdaQuery()
// .eq(SimulationHpcCommandPlaceholder::getAppRepositoryId, req.getAppRepositoryId())
// .list();
// // 配置在表中的包含动态参数的命令
// if(ObjectUtil.isNull(simulationHpcCommand)||StringUtils.isBlank(simulationHpcCommand.getCommand())) {
// throw new RuntimeException("该应用没有初始化配置command");
// }
// command = simulationHpcCommand.getCommand();
// // 命令中动态参数的处理替换
// for(SimulationHpcCommandPlaceholder placeholder : placeholders) {
// String keyEnName = placeholder.getKeyEnName();
// // 用户手动输入
// if(Objects.equals(placeholder.getFeatchType(),"input")) {
// Object value = req.getParams().get(keyEnName);
// // 用户输入是空就使用配置的默认值
// if(ObjectUtil.isNotNull(value)) {
// log.warn("Hpc命令动态参数替换用户输入是空参数名{}",keyEnName);
// value=placeholder.getDefaultValue();
// }
// command = CommandReplaceUtil.replaceCommandPlaceholder(command, keyEnName, value);
// }
// // 入参
// if(Objects.equals(placeholder.getFeatchType(),"param")) {
// Object fieldValue = CommandReplaceUtil.getFieldValue(req, keyEnName);
// if(ObjectUtil.isNotNull(fieldValue)) {
// log.warn("Hpc命令动态参数替换入参反射是空参数名{}",keyEnName);
// fieldValue=placeholder.getDefaultValue();
// }
// command = CommandReplaceUtil.replaceCommandPlaceholder(command, keyEnName, fieldValue);
// }
// // 默认兜底,比如从文件 就是null,最后命令就不拼接从文件
// if(Objects.equals(placeholder.getFeatchType(),"default")) {
// Object defaultValue = placeholder.getDefaultValue();
// if(ObjectUtil.isNotNull(defaultValue)) {
// log.warn("Hpc命令动态参数替换默认值是空参数名{}",keyEnName);
// }
// command = CommandReplaceUtil.replaceCommandPlaceholder(command, keyEnName, defaultValue);
// }
//
// }
// }
//
// if(StringUtils.isBlank(command)) {
// return SdmResponse.failed("command命令不能为空软件名称:"+req.getSoftware());
// }
//
// req.setCommand(formatCommand);
// SdmResponse<String> response = pbsService.submitHpcJob(req);
// String jobId="";
// if(response.isSuccess()&&StringUtils.isNotEmpty(response.getData())) {
// jobId = response.getData();
// }
// if(StringUtils.isNotEmpty(jobId)) {
// // 数据入库
// SimulationJob simulationJob = new SimulationJob();
// // 基础字段
//// simulationJob.setId(1L);
// simulationJob.setJobName(req.getJobName());
// simulationJob.setCoreNum(req.getCoreNum());
// simulationJob.setSoftware(req.getSoftware());
// simulationJob.setJobType(req.getJobType());
// simulationJob.setIndependence(req.isIndependence());
//// simulationJob.setInputFiles(JSONObject.toJSONString(req.getInputFiles()));
// // 主文件位置
// simulationJob.setMasterFile(req.getMasterFilePath());
// // 求解文件集合
// simulationJob.setInputFiles(JSONObject.toJSONString(req.getInputFilePaths()));
// simulationJob.setTaskId(req.getTaskId());
// simulationJob.setTaskName(req.getTaskName());
// simulationJob.setRunId(req.getRunId());
// simulationJob.setRunName(req.getRunName());
// // 软件及文件关联
// simulationJob.setSoftwareId(req.getSoftwareId());
// // 下面的待定
//// simulationJob.setInputFileId(null);
// simulationJob.setJobId(jobId);
// // 没必要要
//// simulationJob.setJobDetailId("");
// // 文件路径 共享目录+jobName文件回传)+uuid下面可能有多个文件
// simulationJob.setStdoutHpcFilePath(hpcOutPutDir);
// simulationJob.setStdoutSpdmMinoFilePath(req.getStdoutSpdmMinoFilePath());
// simulationJob.setStdoutSpdmNasFilePath(req.getStdoutSpdmNasFilePath());
// // 执行信息 定时任务回传的时候修改
//// simulationJob.setNodeName("");
// simulationJob.setExecutCommand(command);
// // 执行信息 定时任务回传的时候修改
//// simulationJob.setStartTime("2025-11-30 10:00:00");
//// simulationJob.setEndTime("2025-11-30 12:30:00");
// simulationJob.setJobStatus("Configuring");
// // 求解器名称
// simulationJob.setSolverName(req.getSoftware());
// // 执行信息 定时任务回传的时候修改
// simulationJob.setTotalKernelTime(null);
// simulationJob.setTotalUserTime(null);
// simulationJob.setTotalElapsedTime(null);
// // 标识及状态
//// simulationJob.setUuid(null);
// simulationJob.setFileStatus("generating");
// // 审计字段
// Long userId = ThreadLocalContext.getUserId();
// Long tenantId = ThreadLocalContext.getTenantId();
// CoreLogger.info("submitHpcJob save db userId:{},tenantId:{}",userId,tenantId);
// simulationJob.setCreatorId(userId);
// simulationJob.setTenantId(tenantId);
// simulationJob.setCreateTime(LocalDateTime.now());
// simulationJob.setUpdaterId(userId);
// simulationJob.setUpdateTime(LocalDateTime.now());
// simulationJobService.save(simulationJob);
// }
// return SdmResponse.success(jobId);
// }
private void dealInputFiles(SubmitHpcTaskReq req, String subDir) {
if(req.getInputFiles()==null|| CollectionUtils.isEmpty(req.getInputFiles())) {
return;
@@ -276,35 +514,24 @@ public class PbsServiceDecorator implements IPbsServiceDecorator {
return PageUtils.getJsonObjectSdmResponse(results, page);
}
public SdmResponse<Map<String, List<SimulationSoftConfig>>> querySoftConfig(String softName) {
List<SimulationSoftConfig> configs = simulationSoftConfigService.lambdaQuery()
.eq(SimulationSoftConfig::getSoftName, softName)
.orderByDesc(SimulationSoftConfig::getCreateTime)
public SdmResponse<SimulationHpcCommand> querySoftConfig(String appUuid) {
// app对应hpc命令,一个只会有一个
SimulationHpcCommand hpcCommand = simulationSoftConfigService.lambdaQuery()
.eq(SimulationHpcCommand::getAppUuid, appUuid).one();
// 查询显示用户的动态参数
List<SimulationHpcCommandPlaceholder> placeholders = simulationCommandPlaceholderService.lambdaQuery()
.eq(SimulationHpcCommandPlaceholder::getAppUuid, appUuid)
.eq(SimulationHpcCommandPlaceholder::getValueType,"input")
.list();
List<SimulationCommandPlaceholder> placeholders = simulationCommandPlaceholderService.lambdaQuery().list();
Map<String, SimulationCommandPlaceholder> placeholderMap = placeholders.stream()
.collect(Collectors.toMap(
SimulationCommandPlaceholder::getKeyEnName, // 键keyEnName
placeholder -> placeholder,
(existing, replacement) -> existing
));
configs.forEach(config -> {
HashMap<String, SimulationCommandPlaceholder> map = new HashMap<>();
String command = config.getCommand();
List<String> placeholderKeys = extractPlaceholders(command);
placeholderKeys.stream().forEach(placeholderKey -> {
SimulationCommandPlaceholder simulationCommandPlaceholder = placeholderMap.get(placeholderKey);
if (simulationCommandPlaceholder != null) {
map.put(placeholderKey, simulationCommandPlaceholder);
}
});
config.setCommandExpand(placeholderMap);
});
// 一个softName --》一个版本--》有多个命令
Map<String, List<SimulationSoftConfig>> softConfigs = configs.stream()
.collect(Collectors.groupingBy(SimulationSoftConfig::getSoftVersion));
return SdmResponse.success(softConfigs);
if(Objects.isNull(hpcCommand)){
return SdmResponse.failed("该应用未配置hpc命令");
}
for(SimulationHpcCommandPlaceholder placeholder : placeholders){
Map<String, Object> commandExpandMap = hpcCommand.getCommandExpand();
String keyEnName = placeholder.getKeyEnName();
commandExpandMap.put(keyEnName,placeholder);
}
return SdmResponse.success(hpcCommand);
}
/**

View File

@@ -2,10 +2,10 @@ package com.sdm.pbs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sdm.pbs.dao.SimulationSoftConfigMapper;
import com.sdm.pbs.model.entity.SimulationSoftConfig;
import com.sdm.pbs.model.entity.SimulationHpcCommand;
import com.sdm.pbs.service.ISimulationSoftConfigService;
import org.springframework.stereotype.Service;
@Service
public class SimulationSoftConfigServiceImpl extends ServiceImpl<SimulationSoftConfigMapper, SimulationSoftConfig> implements ISimulationSoftConfigService {
public class SimulationSoftConfigServiceImpl extends ServiceImpl<SimulationSoftConfigMapper, SimulationHpcCommand> implements ISimulationSoftConfigService {
}

View File

@@ -0,0 +1,64 @@
import os
import re
import sys
import json
# def is_number(str):
# try:
# float(str)
# return True
# except ValueError:
# return False
def is_number(string):
""" 判断字符串是否为数字类型 """
pattern = re.compile(r'^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$')
return bool(pattern.match(string))
def get_curve_unit(string):
xUnit = ''
yUnit = ''
xPhysics = ''
yPhysics = ''
try:
unit_dict = json.loads(string)
xUnit = unit_dict.get('xUnit')
yUnit = unit_dict.get('yUnit')
xPhysics = unit_dict.get('xPhysics')
yPhysics = unit_dict.get('yPhysics')
except:
pass
return xUnit, yUnit, xPhysics, yPhysics
def get_curve_params(curvePath, decimal):
""" 获取曲线文件最大最小值 """
valueList = []
with open(curvePath, 'r+', encoding='utf-8') as fr:
lines = fr.readlines()
for line in lines:
if line.__contains__(','):
yValue = line.split(',')[1]
if(is_number(yValue)):
valueList.append(float(yValue))
maxValue = round(max(valueList), decimal)
minValue = round(min(valueList), decimal)
xUnit, yUnit, xPhysics, yPhysics = get_curve_unit(lines[0])
curve_params = {"max":maxValue, "min":minValue, "xUnit":xUnit, "yUnit":yUnit, "xPhysics":xPhysics, "yPhysics":yPhysics}
print(json.dumps(curve_params, ensure_ascii=False))
if __name__=='__main__':
# 曲线文件路径
curvePath = sys.argv[1]
# 保留小数点位数
try:
decimal = int(sys.argv[2])
except:
decimal = 3
get_curve_params(curvePath, decimal)

View File

@@ -21,7 +21,20 @@ public class CurveParamDto {
@Schema(description = "最小值")
private Double min;
@JsonProperty("unit")
@Schema(description = "单位")
private String unit;
@JsonProperty("xUnit")
@Schema(description = "x轴单位")
private String xUnit;
@JsonProperty("xPhysics")
@Schema(description = "x轴物理量")
private String xPhysics;
@JsonProperty("yUnit")
@Schema(description = "y轴单位")
private String yUnit;
@JsonProperty("yPhysics")
@Schema(description = "y轴物理量")
private String yPhysics;
}

View File

@@ -45,6 +45,21 @@ public class LyricMessageSender implements IMessageSender {
// 1、先调用基础发送
basicSender.send(req);
// 根据userId查询发送人工号
UserQueryReq userReq = new UserQueryReq();
userReq.setUserId(ThreadLocalContext.getUserId());
userReq.setTenantId(ThreadLocalContext.getTenantId());
SdmResponse<CIDUserResp> sdmResponse = sysUserFeignClient.queryUserDetail(userReq);
if (sdmResponse.getData() != null) {
req.setSendJobNo(sdmResponse.getData().getUsername());
}
// 根据userId查询接收人工号
userReq.setUserId(Long.valueOf(req.getUserId()));
SdmResponse<CIDUserResp> response = sysUserFeignClient.queryUserDetail(userReq);
if (response.getData() != null) {
req.setReceiveJobNo(response.getData().getUsername());
}
// 2、异步发送即时通消息通知
CompletableFuture.runAsync(() -> {
sendMsgToFreelink(req);
@@ -53,21 +68,11 @@ public class LyricMessageSender implements IMessageSender {
private void sendMsgToFreelink(SendMsgReq req) {
FreelinkAndDingdingInformReq param = new FreelinkAndDingdingInformReq();
// 根据userId查询工号
UserQueryReq userReq = new UserQueryReq();
userReq.setUserId(ThreadLocalContext.getUserId());
userReq.setTenantId(ThreadLocalContext.getTenantId());
SdmResponse<CIDUserResp> sdmResponse = sysUserFeignClient.queryUserDetail(userReq);
if (sdmResponse.getData() != null) {
param.setJobNo(sdmResponse.getData().getUsername());
}
param.setJobNo(req.getSendJobNo());
FreeLinkMsg freeLinkMsg = new FreeLinkMsg();
if (switchFlag.equals("true")) {
userReq.setUserId(Long.valueOf(req.getUserId()));
SdmResponse<CIDUserResp> response = sysUserFeignClient.queryUserDetail(userReq);
if (response.getData() != null) {
freeLinkMsg.setId(response.getData().getUsername());
}
freeLinkMsg.setId(req.getReceiveJobNo());
} else {
freeLinkMsg.setId(sendUserId);
}