Parcourir la source

增加覆盖率和暴露率 初次提交

ztf il y a 3 mois
Parent
commit
9dcd1ad372
23 fichiers modifiés avec 1167 ajouts et 261 suppressions
  1. 2 0
      api-common/src/main/java/api/common/pojo/constants/DictConstants.java
  2. 38 0
      api-common/src/main/java/api/common/pojo/po/scene/SceneComputerRateDTO.java
  3. 28 0
      api-common/src/main/java/api/common/pojo/vo/project/BaseSceneKeysNumsVo.java
  4. 24 0
      api-common/src/main/java/api/common/pojo/vo/project/SceneComputerECRateVo.java
  5. 22 0
      api-common/src/main/java/api/common/pojo/vo/project/SceneComputerRateBaseVo.java
  6. 28 0
      api-common/src/main/java/api/common/pojo/vo/scene/CoveragePrefixStatusVO.java
  7. 76 13
      simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/domain/service/TaskDomainService.java
  8. 41 0
      simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/db/mysql/mapper/SceneComputerRateMapper.java
  9. 2 0
      simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/db/mysql/mapper/SimulationManualProjectMapper.java
  10. 2 0
      simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/entity/ProjectEntity.java
  11. 44 2
      simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/fs/minio/MinioUtil.java
  12. 452 228
      simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/runnable/SceneEvaluationComputeRunnable.java
  13. 7 0
      simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/threadpool/ThreadPool.java
  14. 8 0
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/adapter/controller/job_manage/JobManageController.java
  15. 19 5
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/adapter/controller/scene_library/scene_reference_lib/SceneReferenceLibController.java
  16. 110 0
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/impl/SceneComputerRateServiceImpl.java
  17. 39 13
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/impl/SimulationProjectServiceImpl.java
  18. 32 0
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/service/SceneComputerRateService.java
  19. 71 0
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/service/SceneReferenceLibService.java
  20. 2 0
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/service/job_manage/SimulationProjectService.java
  21. 38 0
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/infra/db/entity/SceneComputerRateEntity.java
  22. 21 0
      simulation-resource-server/src/main/java/com/css/simulation/resource/server/infra/db/mysql/mapper/SceneComputerRateMapper.java
  23. 61 0
      simulation-resource-server/src/main/resources/mysql/mapper/SceneComputerRateMapper.xml

+ 2 - 0
api-common/src/main/java/api/common/pojo/constants/DictConstants.java

@@ -104,6 +104,8 @@ public class DictConstants {
     public static final String EXPOSURE_RATE = "3";
     // 覆盖率
     public static final String COVERAGE = "4";
+    // 覆盖率-前置
+    public static final String COVERAGE_PREFIX = "5";
 
 
     public static final String PROJECT_WAITING = "10"; // 项目执行状态,待执行

+ 38 - 0
api-common/src/main/java/api/common/pojo/po/scene/SceneComputerRateDTO.java

@@ -0,0 +1,38 @@
+package api.common.pojo.po.scene;
+
+import lombok.*;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * 场景各种率计算PO
+ */
+@EqualsAndHashCode()
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SceneComputerRateDTO implements Serializable {
+    private String computerRateId;
+    /**
+     * 计算类型 {@link api.common.pojo.constants.DictConstants  DictConstants} 使用 EXPOSURE_RATE COVERAGE
+     */
+    private String computerType;
+    private String ruleId;
+    private String ruleName;
+    private String algorithmId;
+    private String algorithmName;
+    private String vehicleId;
+    private String vehicleName;
+    private String taskId;
+    private String computerResult;
+    private String sceneIdList;
+    private String isDeleted;
+    public Timestamp createTime;
+    /**
+     * 记录创建人(用户id)
+     */
+    public String createUserId;
+}

+ 28 - 0
api-common/src/main/java/api/common/pojo/vo/project/BaseSceneKeysNumsVo.java

@@ -0,0 +1,28 @@
+package api.common.pojo.vo.project;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author zhoutianfu
+ * @update 2025/3/2 14:03
+ * @since 2025/3/2 14:03
+ **/
+@EqualsAndHashCode(callSuper = false)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class BaseSceneKeysNumsVo extends SceneComputerRateBaseVo {
+
+    /**
+     * 关键场景个数
+     */
+    private Integer sceneNums;
+
+    public BaseSceneKeysNumsVo(String sceneBaseName, Integer sceneNums) {
+        super(sceneBaseName);
+        this.sceneNums = sceneNums;
+    }
+}

+ 24 - 0
api-common/src/main/java/api/common/pojo/vo/project/SceneComputerECRateVo.java

@@ -0,0 +1,24 @@
+package api.common.pojo.vo.project;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author zhoutianfu
+ * @update 2025/3/1 21:28
+ * @since 2025/3/1 21:28
+ **/
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SceneComputerECRateVo extends SceneComputerRateBaseVo{
+
+    /**
+     * 覆盖率
+     */
+    private Double coverageRate;
+
+    /**
+     * 暴露率
+     */
+    private Double exposureRate;
+}

+ 22 - 0
api-common/src/main/java/api/common/pojo/vo/project/SceneComputerRateBaseVo.java

@@ -0,0 +1,22 @@
+package api.common.pojo.vo.project;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author zhoutianfu
+ * @update 2025/3/2 14:01
+ * @since 2025/3/2 14:01
+ **/
+@EqualsAndHashCode
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class SceneComputerRateBaseVo {
+    /**
+     * 基础场景名称
+     */
+    private String sceneBaseName;
+}

+ 28 - 0
api-common/src/main/java/api/common/pojo/vo/scene/CoveragePrefixStatusVO.java

@@ -0,0 +1,28 @@
+package api.common.pojo.vo.scene;
+
+import api.common.pojo.vo.project.BaseSceneKeysNumsVo;
+import lombok.*;
+
+import java.util.List;
+
+/**
+ * @author zhoutianfu
+ * @update 2025/3/1 15:37
+ * @since 2025/3/1 15:37
+ **/
+@EqualsAndHashCode()
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CoveragePrefixStatusVO {
+
+    /**
+     * PROJECT_WAITING
+     */
+    private String statusCode;
+
+    private String statusMessage;
+
+    private List<BaseSceneKeysNumsVo> baseSceneKeysNumsVos;
+}

+ 76 - 13
simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/domain/service/TaskDomainService.java

@@ -17,6 +17,7 @@ import com.css.simulation.resource.scheduler.infra.runnable.SceneEvaluationCompu
 import com.css.simulation.resource.scheduler.infra.threadpool.ThreadPool;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.sun.org.apache.bcel.internal.generic.NEW;
 import io.minio.MinioClient;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
@@ -124,10 +125,19 @@ public class TaskDomainService {
         List<SceneEntity> sceneEntityList = getSceneList(projectId, packageId);
         Map<String, SceneEntity> sceneEntityMap = sceneEntityList.stream().collect(Collectors.toMap(SceneEntity::getId, Function.identity()));
         List<LeafIndexEntity> leafIndexList = new ArrayList<>();
+        // 复杂度初始
         SceneEvaluationComputeParam sceneComplexityEvaluationComputeParam = new SceneEvaluationComputeParam();
         sceneComplexityEvaluationComputeParam.setSceneEvaluationComputeSubParam(new ArrayList<>());
+        // 危险度初始
         SceneEvaluationComputeParam sceneRiskEvaluationComputeParam = new SceneEvaluationComputeParam();
         sceneRiskEvaluationComputeParam.setSceneEvaluationComputeSubParam(new ArrayList<>());
+        // 覆盖率初始
+        SceneEvaluationComputeParam sceneCoverageEvaluationComputeParam = new SceneEvaluationComputeParam();
+        sceneCoverageEvaluationComputeParam.setSceneEvaluationComputeSubParam(new ArrayList<>());
+        // 暴露率初始
+        SceneEvaluationComputeParam sceneExposureEvaluationComputeParam = new SceneEvaluationComputeParam();
+        sceneExposureEvaluationComputeParam.setSceneEvaluationComputeSubParam(new ArrayList<>());
+
         for (int i = 0; i < leafIndexTemplateList.size(); i++) {
             String scoreExplain = null; // 每个叶子指标下的任务的得分说明一样和叶子指标一致
             IndexTemplateEntity leafIndexTemplate = leafIndexTemplateList.get(i);
@@ -168,14 +178,21 @@ public class TaskDomainService {
                 TaskEntity taskEntity = simulationManualProjectTaskMapper.selectById(task2Id);
                 log.info("查询任务实体: " + taskEntity);
                 String sceneId = taskEntity.getSceneId();
+                // 复杂度
                 if (!StringUtils.isEmpty(projectEntity.getComplexityEvaluationRuleId())) {
-                    SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam = new SceneEvaluationComputeSubParam();
-                    sceneEvaluationComputeSubParam.setSceneId(sceneId);
-                    sceneEvaluationComputeSubParam.setSceneXOSCPath(sceneEntityMap.get(sceneId).getScenarioOsc());
-                    sceneEvaluationComputeSubParam.setSceneXODRPath(sceneEntityMap.get(sceneId).getScenarioOdr());
-                    sceneEvaluationComputeSubParam.setSceneType(sceneEntityMap.get(sceneId).getType());
-                    sceneComplexityEvaluationComputeParam.getSceneEvaluationComputeSubParam().add(sceneEvaluationComputeSubParam);
+                    buildSceneEvaluationParam(sceneEntityMap, sceneComplexityEvaluationComputeParam, sceneId);
+                }
+                // 不依赖标准化测试结果
+                // 暴露率
+                if (!StringUtils.isEmpty(projectEntity.getExposureRateEvaluationRuleId())) {
+                    buildSceneEvaluationParam(sceneEntityMap, sceneExposureEvaluationComputeParam, sceneId);
+                }
+
+                // 覆盖率 // 不依赖标准化测试结果
+                if (!StringUtils.isEmpty(projectEntity.getCoverageRateEvaluationRuleId())) {
+                    buildSceneEvaluationParam(sceneEntityMap, sceneCoverageEvaluationComputeParam, sceneId);
                 }
+
                 String runState = taskOfLeaf.getRunState();
                 log.debug("任务 " + task2Id + " 的运行状态为:" + runState);
                 if (DictConstants.TASK_ANALYSIS.equals(runState)) {
@@ -184,6 +201,7 @@ public class TaskDomainService {
                     final String runResultFilePath = taskOfLeaf.getRunResultFilePath();
 
                     // 计算复杂度和危险度
+                    // 危险度
                     if (!StringUtils.isEmpty(projectEntity.getRiskEvaluationRuleId())) {
                         SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam = new SceneEvaluationComputeSubParam();
                         sceneEvaluationComputeSubParam.setSceneId(sceneId);
@@ -326,6 +344,37 @@ public class TaskDomainService {
             sceneRiskEvaluationComputeParam.setAlgorithmId(projectEntity.getAlgorithm());
             computeSceneEvaluation(sceneRiskEvaluationComputeParam);
         }
+
+        // 暴露率和覆盖率的map
+        Map<String, SceneEvaluationComputeParam> secenEvalationParamMap = new HashMap<>(2);
+        if (CollectionUtil.isNotEmpty(sceneExposureEvaluationComputeParam.getSceneEvaluationComputeSubParam())) {
+            sceneExposureEvaluationComputeParam.setSceneEvaluationRuleId(projectEntity.getExposureRateEvaluationRuleId());
+            sceneExposureEvaluationComputeParam.setCreateUserId(null);
+            sceneExposureEvaluationComputeParam.setTaskId(projectId);
+            sceneExposureEvaluationComputeParam.setEvaluationId(projectId);
+            sceneExposureEvaluationComputeParam.setType(DictConstants.SIMULATION_MANUAL_PROJECT_EVALUATION_TYPE);
+            sceneExposureEvaluationComputeParam.setComputeType(DictConstants.EXPOSURE_RATE);
+            sceneExposureEvaluationComputeParam.setVehicleId(projectEntity.getVehicle());
+            sceneExposureEvaluationComputeParam.setAlgorithmId(projectEntity.getAlgorithm());
+            // 任务放到map里面,这两个都要跑全场景
+            secenEvalationParamMap.put(DictConstants.EXPOSURE_RATE, sceneExposureEvaluationComputeParam);
+        }
+        if (CollectionUtil.isNotEmpty(sceneCoverageEvaluationComputeParam.getSceneEvaluationComputeSubParam())) {
+            sceneCoverageEvaluationComputeParam.setSceneEvaluationRuleId(projectEntity.getCoverageRateEvaluationRuleId());
+            sceneCoverageEvaluationComputeParam.setCreateUserId(null);
+            sceneCoverageEvaluationComputeParam.setTaskId(projectId);
+            sceneCoverageEvaluationComputeParam.setEvaluationId(projectId);
+            sceneCoverageEvaluationComputeParam.setType(DictConstants.SIMULATION_MANUAL_PROJECT_EVALUATION_TYPE);
+            sceneCoverageEvaluationComputeParam.setComputeType(DictConstants.COVERAGE);
+            sceneCoverageEvaluationComputeParam.setVehicleId(projectEntity.getVehicle());
+            sceneCoverageEvaluationComputeParam.setAlgorithmId(projectEntity.getAlgorithm());
+            // 任务放到map里面,这两个都要跑全场景
+            secenEvalationParamMap.put(DictConstants.EXPOSURE_RATE, sceneExposureEvaluationComputeParam);
+        }
+        // 执行覆盖率和暴露率 这两个都要跑全场景
+        if (!secenEvalationParamMap.isEmpty()) {
+            computeSceneEvaluationWithMap(secenEvalationParamMap);
+        }
         // 保存叶子指标得分
         taskIndexRepository.batchInsertLeafIndex(leafIndexList);
         // 保存一级指标分数
@@ -334,23 +383,37 @@ public class TaskDomainService {
         log.info("项目 " + projectId + " 打分完成!");
     }
 
+    private void buildSceneEvaluationParam(Map<String, SceneEntity> sceneEntityMap, SceneEvaluationComputeParam sceneComplexityEvaluationComputeParam, String sceneId) {
+        SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam = new SceneEvaluationComputeSubParam();
+        sceneEvaluationComputeSubParam.setSceneId(sceneId);
+        sceneEvaluationComputeSubParam.setSceneXOSCPath(sceneEntityMap.get(sceneId).getScenarioOsc());
+        sceneEvaluationComputeSubParam.setSceneXODRPath(sceneEntityMap.get(sceneId).getScenarioOdr());
+        sceneEvaluationComputeSubParam.setSceneType(sceneEntityMap.get(sceneId).getType());
+        sceneComplexityEvaluationComputeParam.getSceneEvaluationComputeSubParam().add(sceneEvaluationComputeSubParam);
+    }
+
     public void computeSceneEvaluation(SceneEvaluationComputeParam sceneEvaluationComputeParam) {
-        sceneEvaluationComputeParam.setLinuxTempPath(linuxTempPath);
-        sceneEvaluationComputeParam.setBucketName(bucketName);
-        ThreadPool.sceneEvaluationComputePool.execute(new SceneEvaluationComputeRunnable(sceneEvaluationComputeParam));
+        Map<String, SceneEvaluationComputeParam> secenEvalationParamMap = new HashMap<>(1);
+        secenEvalationParamMap.put(sceneEvaluationComputeParam.getComputeType(), sceneEvaluationComputeParam);
+        this.computeSceneEvaluationWithMap(secenEvalationParamMap);
+
+    }
+
+    public void computeSceneEvaluationWithMap(Map<String, SceneEvaluationComputeParam> secenEvalationParamMap) {
+        ThreadPool.sceneEvaluationComputePool.execute(new SceneEvaluationComputeRunnable(linuxTempPath, bucketName, secenEvalationParamMap));
     }
 
-    public int getMultiTaskRetryTimes(String taskId){
+    public int getMultiTaskRetryTimes(String taskId) {
         String keyRetryTimes = "multi_task_retry_times_" + taskId;
         String s = stringRedisTemplate.opsForValue().get(keyRetryTimes);
-        if (StringUtil.isEmpty(s)){
+        if (StringUtil.isEmpty(s)) {
             return 0;
-        }else {
+        } else {
             return Integer.valueOf(s);
         }
     }
 
-    public void setMultiTaskRetryTimes(String taskId, int times){
+    public void setMultiTaskRetryTimes(String taskId, int times) {
         String keyRetryTimes = "multi_task_retry_times_" + taskId;
         stringRedisTemplate.opsForValue().set(keyRetryTimes, String.valueOf(times), 24 * 60 * 60, TimeUnit.SECONDS);
     }

+ 41 - 0
simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/db/mysql/mapper/SceneComputerRateMapper.java

@@ -0,0 +1,41 @@
+package com.css.simulation.resource.scheduler.infra.db.mysql.mapper;
+
+import api.common.pojo.po.scene.SceneComputerRateDTO;
+import org.apache.ibatis.annotations.*;
+import org.apache.ibatis.type.JdbcType;
+
+
+@Mapper
+public interface SceneComputerRateMapper {
+
+    @Insert("        insert into simulation.scene_computer_rate\n" +
+            "        (computer_rate_id, computer_type, rule_id, algorithm_id,vehicle_id,task_id,\n" +
+            "         computer_result, scene_id_list,\n" +
+            "         is_deleted, create_user_id, create_time\n" +
+            "        )\n" +
+            "        values (#{computerRateId,jdbcType=VARCHAR}, #{computerType,jdbcType=VARCHAR}, #{ruleId,jdbcType=VARCHAR},\n" +
+            "                #{algorithmId,jdbcType=VARCHAR},#{vehicleId,jdbcType=VARCHAR},#{taskId,jdbcType=VARCHAR},\n" +
+            "                #{computerResult,jdbcType=VARCHAR}, #{sceneIdList,jdbcType=VARCHAR},\n" +
+            "                #{isDeleted,jdbcType=VARCHAR},\n" +
+            "                #{createUserId,jdbcType=VARCHAR},\n" +
+            "                #{createTime})")
+    void saveSceneComputerRate(SceneComputerRateDTO params);
+
+    @Results(id = "sceneComputerRateResult", value = {
+            @Result(column = "computer_rate_id", property = "computerRateId", jdbcType = JdbcType.VARCHAR),
+            @Result(column = "computer_type", property = "computerType", jdbcType = JdbcType.VARCHAR),
+            @Result(column = "computer_result", property = "computerResult", jdbcType = JdbcType.VARCHAR),
+    })
+    @Select("select computer_rate_id, computer_type, computer_result " +
+            "from simulation.scene_computer_rate where computer_type = #{computerType} and is_deleted = 0")
+    SceneComputerRateDTO loadSceneComputerRate(@Param("computerType") String computerType);
+
+    @Update("update simulation.scene_computer_rate set is_deleted = 1 " +
+            "where computer_rate_id = #{computerRateId} and  computer_type = #{computerType}")
+    void deleteByIdAndType(@Param("computerRateId") String computerRateId, @Param("computerType") String computerType);
+
+    @Update("update simulation.scene_computer_rate set computer_result = #{computerResult} " +
+            "where computer_rate_id = #{computerRateId} and  computer_type = #{computerType}")
+    void updateResultByIdAndType(@Param("computerRateId") String computerRateId, @Param("computerType") String computerType,
+                                 @Param("computerResult") String computerResult);
+}

+ 2 - 0
simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/db/mysql/mapper/SimulationManualProjectMapper.java

@@ -35,6 +35,8 @@ public interface SimulationManualProjectMapper {
             "       vehicle,\n" +
             "       risk_evaluation_rule_id,\n" +
             "       complexity_evaluation_rule_id,\n" +
+            "       coverage_rate_evaluation_rule_id,\n" +
+            "       exposure_rate_evaluation_rule_id,\n" +
             "       evaluation_json_msg,\n" +
             "       '1' project_type,\n" +
             "       max_simulation_time\n" +

+ 2 - 0
simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/entity/ProjectEntity.java

@@ -22,5 +22,7 @@ public class ProjectEntity {
     private String vehicle;
     private String complexityEvaluationRuleId;
     private String riskEvaluationRuleId;
+    private String coverageRateEvaluationRuleId;
+    private String exposureRateEvaluationRuleId;
     private String evaluationJsonMsg;
 }

+ 44 - 2
simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/fs/minio/MinioUtil.java

@@ -12,6 +12,7 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Paths;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
@@ -45,14 +46,14 @@ public class MinioUtil {
     @SneakyThrows
     public static List<String> listAllFileName(MinioClient minioClient, String bucketName, String prefix) {
         Iterable<Result<Item>> list = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName)
-            .prefix(prefix).recursive(true).build());
+                .prefix(prefix).recursive(true).build());
         if (list == null || !list.iterator().hasNext()) {
             log.info(prefix + " 不存在。");
             return new ArrayList<>();
         }
         List<String> res = new ArrayList<>();
         for (Result<Item> object : list) {
-            if (object.get().isDir()){
+            if (object.get().isDir()) {
                 continue;
             }
             String name = object.get().objectName();
@@ -195,6 +196,47 @@ public class MinioUtil {
         }
     }
 
+    /**
+     * 递归下载指定目录里的所有文件
+     */
+    @SneakyThrows
+    public static void downloadDirFiles(MinioClient minioClient, String bucket, String dirPrefix, String localDir) {
+        // 列出目录下的所有对象
+        Iterable<Result<Item>> results = minioClient.listObjects(
+                ListObjectsArgs.builder()
+                        .bucket(bucket)
+                        .prefix(dirPrefix)
+                        .recursive(true) // 递归列出所有子目录和文件
+                        .build());
+
+        // 遍历并下载每个对象
+        int count = 0;
+        for (Result<Item> result : results) {
+            Item item = result.get();
+            String objectName = item.objectName();
+            String localFilePath = Paths.get(localDir, objectName.substring(dirPrefix.length())).toString();
+
+            // 创建本地目录(如果不存在)
+            File localFile = new File(localFilePath);
+            File parentDir = localFile.getParentFile();
+            if (!parentDir.exists()) {
+                parentDir.mkdirs();
+            }
+
+            // 下载对象到本地
+            minioClient.downloadObject(
+                    DownloadObjectArgs.builder()
+                            .bucket(bucket)
+                            .object(objectName)
+                            .filename(localFilePath)
+                            .build());
+
+            count++;
+        }
+        log.info("MinioUtil download files from dirPrefix={}, count={}", dirPrefix, count);
+
+    }
+
     public static void copyFile(MinioClient minioClient, String bucket, String sourceObject, String targetObject) {
         try {
             boolean objectExist = isObjectExist(minioClient, bucket, sourceObject);

+ 452 - 228
simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/runnable/SceneEvaluationComputeRunnable.java

@@ -4,6 +4,7 @@ import api.common.pojo.constants.DictConstants;
 import api.common.pojo.param.scene.SceneEvaluationComputeParam;
 import api.common.pojo.param.scene.SceneEvaluationComputeSubParam;
 import api.common.pojo.po.scene.SceneComplexityPO;
+import api.common.pojo.po.scene.SceneComputerRateDTO;
 import api.common.pojo.po.scene.SceneEvaluationRulePO;
 import api.common.pojo.po.scene.SceneRiskPO;
 import api.common.pojo.po.system.SceneEvaluationStatusPO;
@@ -17,8 +18,10 @@ import com.css.simulation.resource.scheduler.adapter.entity.impl.ApplicationCont
 import com.css.simulation.resource.scheduler.infra.db.mysql.mapper.*;
 import com.css.simulation.resource.scheduler.infra.entity.ProjectEntity;
 import com.css.simulation.resource.scheduler.infra.fs.minio.MinioUtil;
+import com.css.simulation.resource.scheduler.infra.threadpool.ThreadPool;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.sun.istack.internal.NotNull;
 import io.minio.MinioClient;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
@@ -30,10 +33,7 @@ import java.nio.file.Paths;
 import java.nio.file.attribute.PosixFilePermission;
 import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 
 /**
@@ -43,269 +43,460 @@ import java.util.Set;
 @Slf4j
 public class SceneEvaluationComputeRunnable implements Runnable {
 
-    private final SceneEvaluationComputeParam sceneEvaluationComputeParam;
+//    private final SceneEvaluationComputeParam sceneEvaluationComputeParam;
 
-    public SceneEvaluationComputeRunnable(SceneEvaluationComputeParam sceneEvaluationComputeParam) {
-        this.sceneEvaluationComputeParam = sceneEvaluationComputeParam;
+    private final String linuxTempPath;
+
+    private final String bucketName;
+
+    private final Map<String, SceneEvaluationComputeParam> sceneEvaluationComputeParamMap;
+
+    public SceneEvaluationComputeRunnable(String linuxTempPath, String bucketName,
+                                          @NotNull Map<String, SceneEvaluationComputeParam> sceneEvaluationComputeParamMap) {
+        this.linuxTempPath = linuxTempPath;
+        this.bucketName = bucketName;
+        this.sceneEvaluationComputeParamMap = sceneEvaluationComputeParamMap;
     }
 
     @Override
     public void run() {
-        // 先修改状态
-        SceneEvaluationStatusMapper sceneEvaluationStatusMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneEvaluationStatusMapper.class);
-        SceneEvaluationStatusPO sceneEvaluationStatusPO = new SceneEvaluationStatusPO();
-        sceneEvaluationStatusPO.setId(sceneEvaluationComputeParam.getEvaluationId());
-        sceneEvaluationStatusPO.setStartEvaluationTime(TimeUtil.getNowForMysql());
-        sceneEvaluationStatusPO.setEndEvaluationTime(null);
-        sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_1);
-        sceneEvaluationStatusPO.setEvaluationErrorMsg("");
-        updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
-
-        String ruleId = sceneEvaluationComputeParam.getSceneEvaluationRuleId();
-        log.info("开始执行线程:" + sceneEvaluationComputeParam);
-        String bucketName = sceneEvaluationComputeParam.getBucketName();
+        log.info("SceneEvaluationComputeRunnable begin handel.");
+        long start = System.currentTimeMillis();
+        String tempPath = StringUtil.getRandomUUID();
+        // 覆盖率和暴露率用到 两个共用目录 不多次下载了
+        Set<String> paramFilesStrSet = new HashSet<>();
+        // 先准备各种bean
         SceneEvaluationRuleMapper sceneEvaluationRuleMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneEvaluationRuleMapper.class);
         SceneComplexityMapper sceneComplexityMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneComplexityMapper.class);
         SceneRiskMapper sceneRiskMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneRiskMapper.class);
+        SceneComputerRateMapper sceneComputerRateMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneComputerRateMapper.class);
         MinioClient minioClient = ApplicationContextAwareImpl.getApplicationContext().getBean(MinioClient.class);
-        // 获取场景评价规则
-        SceneEvaluationRulePO sceneEvaluationRulePO = sceneEvaluationRuleMapper.querySceneEvaluationPyById(ruleId);
-        if (sceneEvaluationRulePO == null) {
-            log.error(ruleId + " 的场景评价规则已删除");
-            sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
-            sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
-            sceneEvaluationStatusPO.setEvaluationErrorMsg("场景评价规则已删除");
-            updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
-            return;
-        }
-        log.info("场景评价规则为:" + sceneEvaluationRulePO);
-        // 1 判断有没有用户目录,没有则复制
-        String tempPath = StringUtil.getRandomUUID();
-        log.info("项目 id :" + sceneEvaluationComputeParam.getTaskId() + " 的临时路径为:" + tempPath);
-        String evaluationDirectoryOfUser = sceneEvaluationComputeParam.getLinuxTempPath() + "scene/evaluation/" + tempPath + "/";
-        String scriptsPath = evaluationDirectoryOfUser + "scripts";
-        if (!new File(evaluationDirectoryOfUser).exists()) {
-            // 1 将场景评价规则脚本保存到 script 目录
-            FileUtil.createDirectory(scriptsPath);
-        }
 
-        // 下载场景评价脚本到脚本目录
-        if (sceneEvaluationRulePO.getScriptPath() == null) {
-            sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
-            sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
-            sceneEvaluationStatusPO.setEvaluationErrorMsg("场景评价脚本不存在");
-            updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
-            return;
-        }
-        String pyMainPath = scriptsPath + "/" + sceneEvaluationRulePO.getRuleId();
-        if (!new File(pyMainPath).exists()) {
-            try {
-                MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationRulePO.getScriptPath(), pyMainPath);
-                Path path = Paths.get(pyMainPath);
-                Set<PosixFilePermission> permissions = new HashSet<>();
-                permissions.add(PosixFilePermission.OWNER_READ);
-                permissions.add(PosixFilePermission.OWNER_WRITE);
-                permissions.add(PosixFilePermission.OWNER_EXECUTE);
-                permissions.add(PosixFilePermission.GROUP_READ);
-                permissions.add(PosixFilePermission.GROUP_WRITE);
-                permissions.add(PosixFilePermission.GROUP_EXECUTE);
-                permissions.add(PosixFilePermission.OTHERS_READ);
-                permissions.add(PosixFilePermission.OTHERS_WRITE);
-                permissions.add(PosixFilePermission.OTHERS_EXECUTE);
-                Files.setPosixFilePermissions(path, permissions);
-            } catch (IOException e) {
-                log.error("下载执行文件失败: " + sceneEvaluationRulePO.getScriptPath(), e);
+        sceneEvaluationComputeParamMap.forEach((sceneType, sceneEvaluationComputeParam) -> {
+
+            // 先修改状态
+            SceneEvaluationStatusMapper sceneEvaluationStatusMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SceneEvaluationStatusMapper.class);
+            SceneEvaluationStatusPO sceneEvaluationStatusPO = new SceneEvaluationStatusPO();
+            sceneEvaluationStatusPO.setId(sceneEvaluationComputeParam.getEvaluationId());
+            sceneEvaluationStatusPO.setStartEvaluationTime(TimeUtil.getNowForMysql());
+            sceneEvaluationStatusPO.setEndEvaluationTime(null);
+            sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_1);
+            sceneEvaluationStatusPO.setEvaluationErrorMsg("");
+            updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+
+            String ruleId = sceneEvaluationComputeParam.getSceneEvaluationRuleId();
+            log.info("开始执行线程:" + sceneEvaluationComputeParam);
+//            String bucketName = sceneEvaluationComputeParam.getBucketName();
+            // 如果是计算覆盖率,要先看覆盖率前置条件有没有跑完任务
+            SceneComputerRateDTO numsKeysSceneResult = null;
+            if (DictConstants.COVERAGE.equals(sceneEvaluationComputeParam.getComputeType())
+                    || DictConstants.COVERAGE_PREFIX.equals(sceneEvaluationComputeParam.getComputeType())) {
+                numsKeysSceneResult = sceneComputerRateMapper.loadSceneComputerRate(DictConstants.COVERAGE_PREFIX);
+                if ((DictConstants.COVERAGE.equals(sceneEvaluationComputeParam.getComputeType()))
+                        && (null == numsKeysSceneResult || StringUtil.isEmpty(numsKeysSceneResult.getComputerResult()))) {
+                    log.error("runnable run scene COVERAGE return, numsSceneKeysResultJson is null");
+                    return;
+                }
+            }
+
+            // 获取场景评价规则
+            SceneEvaluationRulePO sceneEvaluationRulePO = sceneEvaluationRuleMapper.querySceneEvaluationPyById(ruleId);
+            if (sceneEvaluationRulePO == null) {
+                log.error(ruleId + " 的场景评价规则已删除");
                 sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
                 sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
-                sceneEvaluationStatusPO.setEvaluationErrorMsg("下载场景评价脚本失败");
-                updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                sceneEvaluationStatusPO.setEvaluationErrorMsg("场景评价规则已删除");
+                updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
                 return;
             }
-        }
-        String scenePathFather = evaluationDirectoryOfUser + "compScene/";
-        String riskPathFather = evaluationDirectoryOfUser + "riskScene/";
-        for (SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam : sceneEvaluationComputeParam.getSceneEvaluationComputeSubParam()) {
-            if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
-                // 创建场景路径
-                if (StringUtil.isEmpty(sceneEvaluationComputeSubParam.getSceneXODRPath())
-                        || StringUtil.isEmpty(sceneEvaluationComputeSubParam.getSceneXOSCPath())) {
-                    continue;
-                }
-                String scenePath = scenePathFather + sceneEvaluationComputeSubParam.getSceneId();
-                if (!new File(scenePath).exists()) {
-                    log.info("创建复杂度文件夹:" + scenePath);
-                    FileUtil.createDirectory(scenePath);
-                }
-                try {
-                    // 计算复杂度,根据场景 id 获取场景信息,下载 osc odr
-                    String scenarioOsc = sceneEvaluationComputeSubParam.getSceneXOSCPath();
-                    String[] splitXosc = scenarioOsc.split("/");
-                    String xoscName = splitXosc[splitXosc.length - 1];
-                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getSceneXOSCPath(), scenePath + "/" + xoscName);
+            log.info("场景评价规则为:" + sceneEvaluationRulePO);
+            // 1 判断有没有用户目录,没有则复制
+            log.info("项目 id :" + sceneEvaluationComputeParam.getTaskId() + " 的临时路径为:" + tempPath);
+            String evaluationDirectoryOfUser = linuxTempPath + "scene/evaluation/" + tempPath + "/";
+            String scriptsPath = evaluationDirectoryOfUser + "scripts";
+            if (!new File(evaluationDirectoryOfUser).exists()) {
+                // 1 将场景评价规则脚本保存到 script 目录
+                FileUtil.createDirectory(scriptsPath);
+            }
 
-                    String scenarioOdr = sceneEvaluationComputeSubParam.getSceneXODRPath();
-                    String[] splitXodr = scenarioOdr.split("/");
-                    String xodrName = splitXodr[splitXodr.length - 1];
-                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getSceneXODRPath(), scenePath + "/" + xodrName);
-                } catch (Exception e) {
-                    log.error("文件下载失败", e);
-                    FileUtil.deleteFolder(scenePathFather);   // 删除临时文件
-                }
-            } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.RISK)) {
-                if (StringUtil.isEmpty(sceneEvaluationComputeSubParam.getEvaluationPath())) {
-                    continue;
-                }
-                // 创建场景路径
-                String scenePath = riskPathFather + sceneEvaluationComputeSubParam.getSceneId();
-                if (!new File(scenePath).exists()) {
-                    log.info("创建危险度文件夹:" + scenePath);
-                    FileUtil.createDirectory(scenePath);
-                }
-                try {
-                    // 计算危险度 从 minio path 下载 csv (ego 和 sensors)
-                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getEvaluationPath() + "/Ego.csv", scenePath + "/Ego.csv");
-                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getEvaluationPath() + "/evaluation.csv", scenePath + "/evaluation.csv");
-                } catch (Exception e) {
-                    log.error("文件下载失败", e);
-                    FileUtil.deleteFolder(riskPathFather);   // 删除临时文件
-                }
-            } else {
+            // 下载场景评价脚本到脚本目录
+            if (sceneEvaluationRulePO.getScriptPath() == null) {
                 sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
                 sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
-                sceneEvaluationStatusPO.setEvaluationErrorMsg("获取评价类型错误");
-                updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                sceneEvaluationStatusPO.setEvaluationErrorMsg("场景评价脚本不存在");
+                updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
                 return;
             }
-        }
-        String sceneEvaluationCommand;
-        if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
-            sceneEvaluationCommand = pyMainPath + " " + scenePathFather + " complexity";
-        } else {
-            sceneEvaluationCommand = pyMainPath + " " + riskPathFather + " criticality";
-        }
-        String sceneEvaluationResult;
-        log.info("开始执行场景评价命令:" + sceneEvaluationCommand);
-        Runtime r = Runtime.getRuntime();
-        Process p;
-        try {
-            p = r.exec(sceneEvaluationCommand);
-        } catch (IOException e) {
-            log.error("执行场景评价脚本失败,脚本命令为: " + sceneEvaluationCommand, e);
-            sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
-            sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
-            sceneEvaluationStatusPO.setEvaluationErrorMsg("执行场景评价命令失败");
-            updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
-            return;
-        }
-
-        StringBuilder sb = new StringBuilder();
-        try {
-            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
-            String inline;
-            while ((inline = br.readLine()) != null) {
-                sb.append(inline).append("\n");
+            String pyMainPath = scriptsPath + "/" + sceneEvaluationRulePO.getRuleId();
+            if (!new File(pyMainPath).exists()) {
+                try {
+                    MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationRulePO.getScriptPath(), pyMainPath);
+                    Path path = Paths.get(pyMainPath);
+                    Set<PosixFilePermission> permissions = new HashSet<>();
+                    permissions.add(PosixFilePermission.OWNER_READ);
+                    permissions.add(PosixFilePermission.OWNER_WRITE);
+                    permissions.add(PosixFilePermission.OWNER_EXECUTE);
+                    permissions.add(PosixFilePermission.GROUP_READ);
+                    permissions.add(PosixFilePermission.GROUP_WRITE);
+                    permissions.add(PosixFilePermission.GROUP_EXECUTE);
+                    permissions.add(PosixFilePermission.OTHERS_READ);
+                    permissions.add(PosixFilePermission.OTHERS_WRITE);
+                    permissions.add(PosixFilePermission.OTHERS_EXECUTE);
+                    Files.setPosixFilePermissions(path, permissions);
+                } catch (IOException e) {
+                    log.error("下载执行文件失败: " + sceneEvaluationRulePO.getScriptPath(), e);
+                    sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
+                    sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
+                    sceneEvaluationStatusPO.setEvaluationErrorMsg("下载场景评价脚本失败");
+                    updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                    return;
+                }
             }
-            br.close();
-        } catch (IOException e) {
-            log.error("获取场景评价脚本返回内容失败", e);
-            sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
-            sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
-            sceneEvaluationStatusPO.setEvaluationErrorMsg("获取场景评价结果失败");
-            updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
-            return;
-        }
-        sceneEvaluationResult = sb.toString();
-        log.info("场景评价结束,结果为:" + sceneEvaluationResult);
-        DecimalFormat df = new DecimalFormat("#.###");
-        for (SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam : sceneEvaluationComputeParam.getSceneEvaluationComputeSubParam()) {
-            // 读文件
-            StringBuilder result = new StringBuilder();
-            try {
-                String readPath;
+            String scenePathFather = evaluationDirectoryOfUser + "compScene/";
+            String riskPathFather = evaluationDirectoryOfUser + "riskScene/";
+            String sceneComputerPathFather = evaluationDirectoryOfUser + "sceneComputer/" + sceneEvaluationComputeParam.getTaskId();
+
+            List<String> sceneIdList = new ArrayList<>();
+            for (SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam : sceneEvaluationComputeParam.getSceneEvaluationComputeSubParam()) {
+                sceneIdList.add(sceneEvaluationComputeSubParam.getSceneId());
                 if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
-                    readPath = scenePathFather;
+                    // 创建场景路径
+                    if (StringUtil.isEmpty(sceneEvaluationComputeSubParam.getSceneXODRPath())
+                            || StringUtil.isEmpty(sceneEvaluationComputeSubParam.getSceneXOSCPath())) {
+                        continue;
+                    }
+                    String scenePath = scenePathFather + sceneEvaluationComputeSubParam.getSceneId();
+                    if (!new File(scenePath).exists()) {
+                        log.info("创建复杂度文件夹:" + scenePath);
+                        FileUtil.createDirectory(scenePath);
+                    }
+                    ThreadPool.sceneDownloadPool.execute(() -> {
+                        try {
+                            // 计算复杂度,根据场景 id 获取场景信息,下载 osc odr
+                            String scenarioOsc = sceneEvaluationComputeSubParam.getSceneXOSCPath();
+                            String[] splitXosc = scenarioOsc.split("/");
+                            String xoscName = splitXosc[splitXosc.length - 1];
+                            MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getSceneXOSCPath(), scenePath + "/" + xoscName);
+
+                            String scenarioOdr = sceneEvaluationComputeSubParam.getSceneXODRPath();
+                            String[] splitXodr = scenarioOdr.split("/");
+                            String xodrName = splitXodr[splitXodr.length - 1];
+                            MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getSceneXODRPath(), scenePath + "/" + xodrName);
+                        } catch (Exception e) {
+                            log.error("文件下载失败", e);
+                            FileUtil.deleteFolder(scenePathFather);   // 删除临时文件
+                        }
+                    });
                 } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.RISK)) {
-                    readPath = riskPathFather;
+                    if (StringUtil.isEmpty(sceneEvaluationComputeSubParam.getEvaluationPath())) {
+                        continue;
+                    }
+                    // 创建场景路径
+                    String scenePath = riskPathFather + sceneEvaluationComputeSubParam.getSceneId();
+                    if (!new File(scenePath).exists()) {
+                        log.info("创建危险度文件夹:" + scenePath);
+                        FileUtil.createDirectory(scenePath);
+                    }
+                    ThreadPool.sceneDownloadPool.execute(() -> {
+                        try {
+                            // 计算危险度 从 minio path 下载 csv (ego 和 sensors)
+                            MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getEvaluationPath() + "/Ego.csv", scenePath + "/Ego.csv");
+                            MinioUtil.downloadToFile(minioClient, bucketName, sceneEvaluationComputeSubParam.getEvaluationPath() + "/evaluation.csv", scenePath + "/evaluation.csv");
+                        } catch (Exception e) {
+                            log.error("文件下载失败", e);
+                            FileUtil.deleteFolder(riskPathFather);   // 删除临时文件
+                        }
+                    });
+                } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.EXPOSURE_RATE)
+                        || StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE)
+                        || StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE_PREFIX)) {
+                    if (StringUtil.isEmpty(sceneEvaluationComputeSubParam.getSceneXOSCPath())) {
+                        continue;
+                    }
+                    // 创建场景路径
+                    if (!new File(sceneComputerPathFather).exists()) {
+                        log.info("创建基准场景文件夹:{}", sceneComputerPathFather);
+                        FileUtil.createDirectory(sceneComputerPathFather);
+                    }
+                    // 下载目录文件
+                    String dirPrefix = subDownDirPrefix(sceneEvaluationComputeSubParam, sceneEvaluationComputeParam);
+                    if (org.apache.commons.lang3.StringUtils.isEmpty(dirPrefix)) {
+                        continue;
+                    }
+                    // 已经下载过了
+                    if (paramFilesStrSet.contains(dirPrefix)) {
+                        continue;
+                    }
+                    paramFilesStrSet.add(dirPrefix);
+                    // 开线程去下载
+                    ThreadPool.sceneDownloadPool.execute(() -> {
+                        try {
+                            MinioUtil.downloadDirFiles(minioClient, bucketName, dirPrefix, sceneComputerPathFather);
+                        } catch (Exception e) {
+                            log.error("文件目录下载失败,{}", dirPrefix);
+                        }
+                    });
                 } else {
                     sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
                     sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
                     sceneEvaluationStatusPO.setEvaluationErrorMsg("获取评价类型错误");
-                    updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                    updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
                     return;
                 }
-                FileInputStream fileInputStream = new FileInputStream(readPath + sceneEvaluationComputeSubParam.getSceneId() + "/scenario_evaluation.json");
-                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
-
-                String line;
-                while ((line = bufferedReader.readLine()) != null) {
-                    result.append(line).append("\n");
+            }
+            String sceneEvaluationCommand;
+            if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
+                sceneEvaluationCommand = pyMainPath + " " + scenePathFather + " complexity";
+            } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.RISK)) {
+                sceneEvaluationCommand = pyMainPath + " " + riskPathFather + " criticality";
+            } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE)) {
+                // 统一结果路径
+                String resultJsonPath = sceneComputerPathFather + "/coverage_evaluation.json";
+                /*
+                    覆盖率工具coverage_rate_main:
+                    输入:可执行文件+关键场景个数”结果存储路径+被测场景文件路径+“覆盖率”结果存储路径
+                 */
+                String sceneNumsJsonPath = sceneComputerPathFather + "/coverage_prefix_result_evaluation.json";
+                // 保存覆盖率前置结果到dir中
+                if (numsKeysSceneResult == null || StringUtils.isEmpty(numsKeysSceneResult.getComputerResult())) {
+                    return;
                 }
-                bufferedReader.close();
+                try (FileWriter writer = new FileWriter(sceneNumsJsonPath)) {
+                    writer.write(numsKeysSceneResult.getComputerResult()); // 写入内容
+                    log.info("内容已成功写入文件!");
+                } catch (IOException e) {
+                    log.error("写入文件时出错: {}", e.getMessage());
+                    return;
+                }
+                sceneEvaluationCommand = pyMainPath + " " + sceneNumsJsonPath + " " + sceneComputerPathFather + " " + resultJsonPath;
+            } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE_PREFIX)) {
+                /*
+                 * 覆盖率前置工具 num_key_scenes_main:
+                 * 输入:可执行文件+基准场景文件路径+“关键场景个数”结果存储路径
+                 */
+                // 统一结果路径
+                String resultJsonPath = sceneComputerPathFather + "/coverage_prefix_result_evaluation.json";
+                sceneEvaluationCommand = pyMainPath + " " + sceneComputerPathFather + " " + resultJsonPath;
+            } else {
+
+                /*
+                暴露率工具exposure_rate_main:
+                输入:可执行文件+被测场景文件路径+“暴露率”结果存储路径
+                 */
+                String resultJsonPath = sceneComputerPathFather + "/exposure_evaluation.json";
+                sceneEvaluationCommand = pyMainPath + " " + sceneComputerPathFather + " " + resultJsonPath;
+            }
+            String sceneEvaluationResult;
+            log.info("开始执行场景评价命令:" + sceneEvaluationCommand);
+            Runtime r = Runtime.getRuntime();
+            Process p;
+            try {
+                p = r.exec(sceneEvaluationCommand);
             } catch (IOException e) {
-                log.error("读取场景评价结果失败", e);
-                continue;
+                log.error("执行场景评价脚本失败,脚本命令为: " + sceneEvaluationCommand, e);
+                sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
+                sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
+                sceneEvaluationStatusPO.setEvaluationErrorMsg("执行场景评价命令失败");
+                updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                return;
             }
-            String resultStr = result.toString();
-            String replace = StringUtil.replace(resultStr, "'", "\"");
-            JsonNode rootNode;
+
+            StringBuilder sb = new StringBuilder();
             try {
-                ObjectMapper mapper = new ObjectMapper();
-                //JSON ----> JsonNode
-                rootNode = mapper.readTree(replace);
+                BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+                String inline;
+                while ((inline = br.readLine()) != null) {
+                    sb.append(inline).append("\n");
+                }
+                br.close();
+            } catch (IOException e) {
+                log.error("获取场景评价脚本返回内容失败", e);
+                sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
+                sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
+                sceneEvaluationStatusPO.setEvaluationErrorMsg("获取场景评价结果失败");
+                updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                return;
+            }
+            sceneEvaluationResult = sb.toString();
+            log.info("场景评价结束,结果为:" + sceneEvaluationResult);
+            if (DictConstants.RISK.equals(sceneEvaluationComputeParam.getComputeType())
+                    || DictConstants.COMPLEXITY.equals(sceneEvaluationComputeParam.getComputeType())) { // 这两个是单独场景跑分
+                DecimalFormat df = new DecimalFormat("#.###");
+                for (SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam : sceneEvaluationComputeParam.getSceneEvaluationComputeSubParam()) {
+                    // 读文件
+                    StringBuilder result = new StringBuilder();
+                    try {
+                        String readPath;
+                        if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
+                            readPath = scenePathFather;
+                        } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.RISK)) {
+                            readPath = riskPathFather;
+                        } else {
+                            sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
+                            sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
+                            sceneEvaluationStatusPO.setEvaluationErrorMsg("获取评价类型错误");
+                            updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                            return;
+                        }
+                        FileInputStream fileInputStream = new FileInputStream(readPath + sceneEvaluationComputeSubParam.getSceneId() + "/scenario_evaluation.json");
+                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
 
-                if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
-                    String complexity = rootNode.path("复杂度").asText();
-                    String complexityLevel = rootNode.path("复杂度等级").asText();
-                    SceneComplexityPO sceneComplexityPO = new SceneComplexityPO();
-                    double complexityD = Double.parseDouble(complexity);
-                    String formattedNumber = df.format(complexityD);
-                    sceneComplexityPO.setComplexity(formattedNumber);
-                    sceneComplexityPO.setSceneId(sceneEvaluationComputeSubParam.getSceneId());
-                    sceneComplexityPO.setComplexityId(StringUtil.getRandomUUID());
-                    sceneComplexityPO.setSceneType(sceneEvaluationComputeSubParam.getSceneType());
-                    sceneComplexityPO.setRuleId(ruleId);
-                    sceneComplexityPO.setTaskId(sceneEvaluationComputeParam.getTaskId());
-                    sceneComplexityPO.setComplexityLevel(complexityLevel);
-                    sceneComplexityPO.setIsDeleted(DictConstants.IS_NOT_DELETED);
-                    sceneComplexityPO.setCreateUserId(sceneEvaluationComputeParam.getCreateUserId());
-                    sceneComplexityPO.setCreateTime(TimeUtil.getNowForMysql());
-                    sceneComplexityMapper.saveSceneComplexity(sceneComplexityPO);
-                    if (CollectionUtil.isNotEmpty(sceneEvaluationComputeSubParam.getCopySceneId())) {
-                        copySceneComplexityResult(sceneComplexityPO.getTaskId(), sceneEvaluationComputeSubParam.getSceneId(), sceneEvaluationComputeSubParam.getCopySceneId());
+                        String line;
+                        while ((line = bufferedReader.readLine()) != null) {
+                            result.append(line).append("\n");
+                        }
+                        bufferedReader.close();
+                    } catch (IOException e) {
+                        log.error("读取场景评价结果失败", e);
+                        continue;
+                    }
+                    String resultStr = result.toString();
+                    String replace = StringUtil.replace(resultStr, "'", "\"");
+                    JsonNode rootNode;
+                    try {
+                        ObjectMapper mapper = new ObjectMapper();
+                        //JSON ----> JsonNode
+                        rootNode = mapper.readTree(replace);
+
+                        if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
+                            String complexity = rootNode.path("复杂度").asText();
+                            String complexityLevel = rootNode.path("复杂度等级").asText();
+                            SceneComplexityPO sceneComplexityPO = new SceneComplexityPO();
+                            double complexityD = Double.parseDouble(complexity);
+                            String formattedNumber = df.format(complexityD);
+                            sceneComplexityPO.setComplexity(formattedNumber);
+                            sceneComplexityPO.setSceneId(sceneEvaluationComputeSubParam.getSceneId());
+                            sceneComplexityPO.setComplexityId(StringUtil.getRandomUUID());
+                            sceneComplexityPO.setSceneType(sceneEvaluationComputeSubParam.getSceneType());
+                            sceneComplexityPO.setRuleId(ruleId);
+                            sceneComplexityPO.setTaskId(sceneEvaluationComputeParam.getTaskId());
+                            sceneComplexityPO.setComplexityLevel(complexityLevel);
+                            sceneComplexityPO.setIsDeleted(DictConstants.IS_NOT_DELETED);
+                            sceneComplexityPO.setCreateUserId(sceneEvaluationComputeParam.getCreateUserId());
+                            sceneComplexityPO.setCreateTime(TimeUtil.getNowForMysql());
+                            sceneComplexityMapper.saveSceneComplexity(sceneComplexityPO);
+                            if (CollectionUtil.isNotEmpty(sceneEvaluationComputeSubParam.getCopySceneId())) {
+                                copySceneComplexityResult(sceneComplexityPO.getTaskId(), sceneEvaluationComputeSubParam.getSceneId(), sceneEvaluationComputeSubParam.getCopySceneId());
+                            }
+                        } else {
+                            String risk = rootNode.path("危险度").asText();
+                            String riskLevel = rootNode.path("危险度等级").asText();
+                            SceneRiskPO sceneRiskPO = new SceneRiskPO();
+                            double riskD = Double.parseDouble(risk);
+                            String formattedNumber = df.format(riskD);
+                            sceneRiskPO.setRisk(formattedNumber);
+                            sceneRiskPO.setSceneId(sceneEvaluationComputeSubParam.getSceneId());
+                            sceneRiskPO.setRiskId(StringUtil.getRandomUUID());
+                            sceneRiskPO.setSceneType(sceneEvaluationComputeSubParam.getSceneType());
+                            sceneRiskPO.setRuleId(ruleId);
+                            sceneRiskPO.setTaskId(sceneEvaluationComputeParam.getTaskId());
+                            sceneRiskPO.setRiskLevel(riskLevel);
+                            sceneRiskPO.setIsDeleted(DictConstants.IS_NOT_DELETED);
+                            sceneRiskPO.setCreateUserId(sceneEvaluationComputeParam.getCreateUserId());
+                            sceneRiskPO.setAlgorithmId(sceneEvaluationComputeParam.getAlgorithmId());
+                            sceneRiskPO.setVehicleId(sceneEvaluationComputeParam.getVehicleId());
+                            sceneRiskPO.setCreateTime(TimeUtil.getNowForMysql());
+                            sceneRiskMapper.saveSceneRisk(sceneRiskPO);
+                        }
+                    } catch (Exception e) {
+                        log.error("场景" + sceneEvaluationComputeSubParam.getSceneId() + " 的场景评价失败:", e);
                     }
-                } else {
-                    String risk = rootNode.path("危险度").asText();
-                    String riskLevel = rootNode.path("危险度等级").asText();
-                    SceneRiskPO sceneRiskPO = new SceneRiskPO();
-                    double riskD = Double.parseDouble(risk);
-                    String formattedNumber = df.format(riskD);
-                    sceneRiskPO.setRisk(formattedNumber);
-                    sceneRiskPO.setSceneId(sceneEvaluationComputeSubParam.getSceneId());
-                    sceneRiskPO.setRiskId(StringUtil.getRandomUUID());
-                    sceneRiskPO.setSceneType(sceneEvaluationComputeSubParam.getSceneType());
-                    sceneRiskPO.setRuleId(ruleId);
-                    sceneRiskPO.setTaskId(sceneEvaluationComputeParam.getTaskId());
-                    sceneRiskPO.setRiskLevel(riskLevel);
-                    sceneRiskPO.setIsDeleted(DictConstants.IS_NOT_DELETED);
-                    sceneRiskPO.setCreateUserId(sceneEvaluationComputeParam.getCreateUserId());
-                    sceneRiskPO.setAlgorithmId(sceneEvaluationComputeParam.getAlgorithmId());
-                    sceneRiskPO.setVehicleId(sceneEvaluationComputeParam.getVehicleId());
-                    sceneRiskPO.setCreateTime(TimeUtil.getNowForMysql());
-                    sceneRiskMapper.saveSceneRisk(sceneRiskPO);
                 }
-            } catch (Exception e) {
-                log.error("场景" + sceneEvaluationComputeSubParam.getSceneId() + " 的场景评价失败:", e);
             }
-        }
-        sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
-        sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_2);
-        sceneEvaluationStatusPO.setEvaluationErrorMsg("");
-        updateStatus(sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+
+            if (DictConstants.EXPOSURE_RATE.equals(sceneEvaluationComputeParam.getComputeType())
+                    || DictConstants.COVERAGE.equals(sceneEvaluationComputeParam.getComputeType())
+                    || DictConstants.COVERAGE_PREFIX.equals(sceneEvaluationComputeParam.getComputeType())) { // 这3个是跑所有场景
+                // 读文件
+                StringBuilder result = new StringBuilder();
+                try {
+                    String readPath = sceneComputerPathFather;
+                    if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.EXPOSURE_RATE)) {
+                        readPath = readPath + "/exposure_evaluation.json";
+                    } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE)) {
+                        readPath = readPath + "/coverage_evaluation.json";
+                    } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE_PREFIX)) {
+                        readPath = readPath + "/coverage_prefix_result_evaluation.json";
+                    } else {
+                        sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
+                        sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_3);
+                        sceneEvaluationStatusPO.setEvaluationErrorMsg("获取评价类型错误");
+                        updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+                        return;
+                    }
+                    FileInputStream fileInputStream = new FileInputStream(readPath);
+                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
+
+                    String line;
+                    while ((line = bufferedReader.readLine()) != null) {
+                        result.append(line).append("\n");
+                    }
+                    bufferedReader.close();
+                } catch (IOException e) {
+                    log.error("读取场景评价结果失败", e);
+                }
+                String resultStr = result.toString();
+                String replace = StringUtil.replace(resultStr, "'", "\"");
+                try {
+                    // 如果是覆盖率前置条件,需要修改覆盖率前置条件
+                    if (numsKeysSceneResult != null && DictConstants.COVERAGE_PREFIX.equals(sceneEvaluationComputeParam.getComputeType())) {
+                        sceneComputerRateMapper.updateResultByIdAndType(numsKeysSceneResult.getComputerRateId(), numsKeysSceneResult.getComputerType(), replace);
+                    }else {
+                        // 保存结果
+                        SceneComputerRateDTO sceneComputerRatePO = getSceneComputerRatePO(sceneEvaluationComputeParam, replace, ruleId, sceneIdList);
+                        sceneComputerRateMapper.saveSceneComputerRate(sceneComputerRatePO);
+                    }
+
+                } catch (Exception e) {
+                    log.error("任务" + sceneEvaluationComputeParam.getTaskId() + " 的场景评价失败:", e);
+                }
+
+                sceneEvaluationStatusPO.setEndEvaluationTime(TimeUtil.getNowForMysql());
+                sceneEvaluationStatusPO.setEvaluationStatus(DictConstants.SCENE_EVALUATION_STATUS_2);
+                sceneEvaluationStatusPO.setEvaluationErrorMsg("");
+                updateStatus(sceneEvaluationComputeParam, sceneEvaluationStatusMapper, sceneEvaluationStatusPO);
+            }
+        });
         // 删除临时文件
-        FileUtil.deleteFolder(sceneEvaluationComputeParam.getLinuxTempPath() + "scene/evaluation/" + tempPath);   // 删除临时文件
+        FileUtil.deleteFolder(linuxTempPath + "scene/evaluation/" + tempPath);   // 删除临时文件
+
+        log.info("SceneEvaluationComputeRunnable handel end. cost {} ms", System.currentTimeMillis() - start);
     }
 
-    private void updateStatus(SceneEvaluationStatusMapper sceneEvaluationStatusMapper, SceneEvaluationStatusPO sceneEvaluationStatusPO) {
+    private String subDownDirPrefix(SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam,
+                                    SceneEvaluationComputeParam sceneEvaluationComputeParam) {
+        String sceneXOSCPath = sceneEvaluationComputeSubParam.getSceneXOSCPath();
+        // 覆盖率和暴露率需要下载这个目录
+        String basePath = "/Scenarios/";
+        int indexOfBasePath = sceneXOSCPath.lastIndexOf(basePath);
+        if (indexOfBasePath < 0) {
+            log.info("task={}, sceneId={}, can not find basePath={}, sceneXOSCPath={}", sceneEvaluationComputeParam.getTaskId(),
+                    sceneEvaluationComputeSubParam.getSceneId(), basePath, sceneXOSCPath);
+            return null;
+        }
+        return sceneXOSCPath.substring(0, indexOfBasePath) + basePath;
+    }
+
+    private static SceneComputerRateDTO getSceneComputerRatePO(SceneEvaluationComputeParam sceneEvaluationComputeParam,
+                                                               String replace,
+                                                               String ruleId,
+                                                               List<String> sceneIdList) {
+        SceneComputerRateDTO sceneComputerRatePO = new SceneComputerRateDTO();
+        sceneComputerRatePO.setComputerRateId(StringUtil.getRandomUUID());
+        sceneComputerRatePO.setComputerResult(replace);
+        sceneComputerRatePO.setComputerType(sceneEvaluationComputeParam.getComputeType());
+        sceneComputerRatePO.setRuleId(ruleId);
+        sceneComputerRatePO.setTaskId(sceneEvaluationComputeParam.getTaskId());
+        sceneComputerRatePO.setSceneIdList(JSONObject.toJSONString(sceneIdList));
+        sceneComputerRatePO.setIsDeleted(DictConstants.IS_NOT_DELETED);
+        sceneComputerRatePO.setCreateUserId(sceneEvaluationComputeParam.getCreateUserId());
+        sceneComputerRatePO.setAlgorithmId(sceneEvaluationComputeParam.getAlgorithmId());
+        sceneComputerRatePO.setVehicleId(sceneEvaluationComputeParam.getVehicleId());
+        sceneComputerRatePO.setCreateTime(TimeUtil.getNowForMysql());
+        return sceneComputerRatePO;
+    }
+
+    private void updateStatus(SceneEvaluationComputeParam sceneEvaluationComputeParam, SceneEvaluationStatusMapper sceneEvaluationStatusMapper, SceneEvaluationStatusPO sceneEvaluationStatusPO) {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COMPLEXITY)) {
             if (DictConstants.SCENE_IMPORT_EVALUATION_TYPE.equals(sceneEvaluationComputeParam.getType())) {
@@ -336,6 +527,39 @@ public class SceneEvaluationComputeRunnable implements Runnable {
                 evaluationJsonObj.put("riskEndEvaluationTime", sceneEvaluationStatusPO.getEndEvaluationTime() == null ? null : sdf.format(sceneEvaluationStatusPO.getEndEvaluationTime()));
                 sceneEvaluationStatusMapper.updateManualProjectEvaluationStatus(sceneEvaluationStatusPO.getId(), evaluationJsonObj.toString());
             }
+        } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE)) {
+            SimulationManualProjectMapper simulationManualProjectMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SimulationManualProjectMapper.class);
+            ProjectEntity projectEntity = simulationManualProjectMapper.selectById(sceneEvaluationComputeParam.getTaskId());
+            if (StringUtil.isNotEmpty(projectEntity.getEvaluationJsonMsg())) {
+                JSONObject evaluationJsonObj = JSONObject.parseObject(projectEntity.getEvaluationJsonMsg());
+                evaluationJsonObj.put("coverageEvaluationStatus", sceneEvaluationStatusPO.getEvaluationStatus());
+                evaluationJsonObj.put("coverageEvaluationErrorMsg", sceneEvaluationStatusPO.getEvaluationErrorMsg());
+                evaluationJsonObj.put("coverageStartEvaluationTime", sceneEvaluationStatusPO.getStartEvaluationTime() == null ? null : sdf.format(sceneEvaluationStatusPO.getStartEvaluationTime()));
+                evaluationJsonObj.put("coverageEndEvaluationTime", sceneEvaluationStatusPO.getEndEvaluationTime() == null ? null : sdf.format(sceneEvaluationStatusPO.getEndEvaluationTime()));
+                sceneEvaluationStatusMapper.updateManualProjectEvaluationStatus(sceneEvaluationStatusPO.getId(), evaluationJsonObj.toString());
+            }
+        } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.EXPOSURE_RATE)) {
+            SimulationManualProjectMapper simulationManualProjectMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SimulationManualProjectMapper.class);
+            ProjectEntity projectEntity = simulationManualProjectMapper.selectById(sceneEvaluationComputeParam.getTaskId());
+            if (StringUtil.isNotEmpty(projectEntity.getEvaluationJsonMsg())) {
+                JSONObject evaluationJsonObj = JSONObject.parseObject(projectEntity.getEvaluationJsonMsg());
+                evaluationJsonObj.put("exposureEvaluationStatus", sceneEvaluationStatusPO.getEvaluationStatus());
+                evaluationJsonObj.put("exposureEvaluationErrorMsg", sceneEvaluationStatusPO.getEvaluationErrorMsg());
+                evaluationJsonObj.put("exposureStartEvaluationTime", sceneEvaluationStatusPO.getStartEvaluationTime() == null ? null : sdf.format(sceneEvaluationStatusPO.getStartEvaluationTime()));
+                evaluationJsonObj.put("exposureEndEvaluationTime", sceneEvaluationStatusPO.getEndEvaluationTime() == null ? null : sdf.format(sceneEvaluationStatusPO.getEndEvaluationTime()));
+                sceneEvaluationStatusMapper.updateManualProjectEvaluationStatus(sceneEvaluationStatusPO.getId(), evaluationJsonObj.toString());
+            }
+        } else if (StringUtils.equals(sceneEvaluationComputeParam.getComputeType(), DictConstants.COVERAGE_PREFIX)) {
+            SimulationManualProjectMapper simulationManualProjectMapper = ApplicationContextAwareImpl.getApplicationContext().getBean(SimulationManualProjectMapper.class);
+            ProjectEntity projectEntity = simulationManualProjectMapper.selectById(sceneEvaluationComputeParam.getTaskId());
+            if (StringUtil.isNotEmpty(projectEntity.getEvaluationJsonMsg())) {
+                JSONObject evaluationJsonObj = JSONObject.parseObject(projectEntity.getEvaluationJsonMsg());
+                evaluationJsonObj.put("coveragePreEvaluationStatus", sceneEvaluationStatusPO.getEvaluationStatus());
+                evaluationJsonObj.put("coveragePreEvaluationErrorMsg", sceneEvaluationStatusPO.getEvaluationErrorMsg());
+                evaluationJsonObj.put("coveragePreStartEvaluationTime", sceneEvaluationStatusPO.getStartEvaluationTime() == null ? null : sdf.format(sceneEvaluationStatusPO.getStartEvaluationTime()));
+                evaluationJsonObj.put("coveragePreEndEvaluationTime", sceneEvaluationStatusPO.getEndEvaluationTime() == null ? null : sdf.format(sceneEvaluationStatusPO.getEndEvaluationTime()));
+                sceneEvaluationStatusMapper.updateManualProjectEvaluationStatus(sceneEvaluationStatusPO.getId(), evaluationJsonObj.toString());
+            }
         }
 
     }

+ 7 - 0
simulation-resource-scheduler/src/main/java/com/css/simulation/resource/scheduler/infra/threadpool/ThreadPool.java

@@ -12,8 +12,15 @@ public class ThreadPool {
     private static final ThreadFactory sceneEvaluationComputeFactory = new ThreadFactoryBuilder()
             .setNameFormat("scene-evaluation-compute-pool-%d").build();
 
+    private static final ThreadFactory sceneDownloadFactory = new ThreadFactoryBuilder()
+            .setNameFormat("scene-download-pool-%d").build();
+
     public static ExecutorService sceneEvaluationComputePool = new ThreadPoolExecutor(5, 200,
             0L, TimeUnit.MILLISECONDS,
             new LinkedBlockingQueue<>(1024), sceneEvaluationComputeFactory, new ThreadPoolExecutor.AbortPolicy());
 
+    public static ExecutorService sceneDownloadPool = new ThreadPoolExecutor(5, 100,
+            10L, TimeUnit.MILLISECONDS,
+            new LinkedBlockingQueue<>(1024), sceneDownloadFactory, new ThreadPoolExecutor.AbortPolicy());
+
 }

+ 8 - 0
simulation-resource-server/src/main/java/com/css/simulation/resource/server/adapter/controller/job_manage/JobManageController.java

@@ -116,6 +116,14 @@ public class JobManageController {
     return simulationProjectService.selectProjectDetailsById(param);
   }
 
+  /**
+   * 获取项目的覆盖率和暴露率
+   */
+  @RequestMapping("/selectProjectComputerRates")
+  public ResponseBodyVO<List<SceneComputerECRateVo>> selectProjectComputerRates(@RequestBody SimulationManualProjectParam param) {
+    return simulationProjectService.selectProjectComputerRates(param);
+  }
+
   /**
    * 获取工作详情
    */

+ 19 - 5
simulation-resource-server/src/main/java/com/css/simulation/resource/server/adapter/controller/scene_library/scene_reference_lib/SceneReferenceLibController.java

@@ -7,6 +7,7 @@ import api.common.pojo.param.scene.SceneReferenceLibParam;
 import api.common.pojo.param.scene.SceneReferenceLibSelectParam;
 import api.common.pojo.po.scene.*;
 import api.common.pojo.po.system.SceneImportPO;
+import api.common.pojo.vo.scene.CoveragePrefixStatusVO;
 import api.common.pojo.vo.scene.SceneReferenceLibVO;
 import api.common.util.CollectionUtil;
 import api.common.util.ObjectUtil;
@@ -17,10 +18,7 @@ import com.css.simulation.resource.server.infra.util.PageUtil;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import org.springframework.beans.BeanUtils;
-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.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import java.util.ArrayList;
@@ -112,11 +110,27 @@ public class SceneReferenceLibController {
         if (!DictConstants.ROLE_CODE_ADMIN.equals(roleCode) && !DictConstants.ROLE_CODE_SYSADMIN.equals(roleCode)) {
             return new ResponseBodyVO<>(ResponseBodyVO.Response.CLIENT_FAILURE, "非管理员禁止删除");
         }
-        if(CollectionUtil.isEmpty(params.getSceneNames())){
+        if (CollectionUtil.isEmpty(params.getSceneNames())) {
             return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS);
         }
         sceneReferenceLibService.batchDeleteSceneReference(params.getSceneNames());
         return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS);
     }
 
+    /**
+     * 获取覆盖率前置任务状态
+     */
+    @GetMapping(value = "/loadConvergePrefixStatus", name = "获取覆盖率前置任务状态")
+    public ResponseBodyVO<CoveragePrefixStatusVO> loadConvergePrefixStatus() {
+        return sceneReferenceLibService.loadConvergePrefixStatus();
+    }
+
+    /**
+     * 执行覆盖率前置任务
+     */
+    @PostMapping(value = "/handleConvergePrefix", name = "执行覆盖率前置任务")
+    public ResponseBodyVO<String> handleConvergePrefix() {
+        return sceneReferenceLibService.handleConvergePrefix();
+    }
+
 }

+ 110 - 0
simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/impl/SceneComputerRateServiceImpl.java

@@ -0,0 +1,110 @@
+package com.css.simulation.resource.server.app.impl;
+
+import api.common.pojo.constants.DictConstants;
+import api.common.pojo.vo.project.BaseSceneKeysNumsVo;
+import api.common.pojo.vo.project.SceneComputerECRateVo;
+import api.common.pojo.vo.scene.CoveragePrefixStatusVO;
+import api.common.util.CollectionUtil;
+import api.common.util.StringUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.css.simulation.resource.server.app.service.SceneComputerRateService;
+import com.css.simulation.resource.server.infra.db.entity.SceneComputerRateEntity;
+import com.css.simulation.resource.server.infra.db.mysql.mapper.SceneComputerRateMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * @author zhoutianfu
+ * @update 2025/3/2 13:23
+ * @since 2025/3/2 13:23
+ **/
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class SceneComputerRateServiceImpl implements SceneComputerRateService {
+
+    private final SceneComputerRateMapper sceneComputerRateMapper;
+
+    @Override
+    public void saveSceneComputerRate(SceneComputerRateEntity params) {
+        sceneComputerRateMapper.saveSceneComputerRate(params);
+    }
+
+    @Override
+    public void deleteByIdAndType(String computerRateId, String computerType) {
+        sceneComputerRateMapper.deleteByIdAndType(computerRateId, computerType);
+    }
+
+    @Override
+    public List<SceneComputerECRateVo> selectProjectComputerRates(String projectId, List<String> computerTypeList) {
+
+        List<SceneComputerRateEntity> sceneComputerRateEntities = sceneComputerRateMapper.loadSceneComputerRateByTaskIdAndType(projectId, computerTypeList);
+        Map<String, SceneComputerECRateVo> sceneComputerECRateVoMap = new HashMap<>();
+        if (!CollectionUtil.isEmpty(sceneComputerRateEntities)) {
+            for (SceneComputerRateEntity sceneComputerRateEntity : sceneComputerRateEntities) {
+                String computerResult = sceneComputerRateEntity.getComputerResult();
+                if (StringUtil.isEmpty(computerResult)) {
+                    continue;
+                }
+                List<JSONObject> jsonObjects = JSONObject.parseArray(computerResult, JSONObject.class);
+                for (JSONObject jsonObject : jsonObjects) {
+                    String sceneName = (String) jsonObject.get("场景库名称");
+                    SceneComputerECRateVo orDefault = sceneComputerECRateVoMap.getOrDefault(sceneName, new SceneComputerECRateVo());
+                    orDefault.setSceneBaseName(sceneName);
+                    if (DictConstants.COVERAGE.equals(sceneComputerRateEntity.getComputerType())) {
+                        Object coverageRate = jsonObject.getOrDefault("覆盖率", 0.0);
+                        BigDecimal bigDecimal = new BigDecimal(coverageRate.toString());
+                        orDefault.setCoverageRate(bigDecimal.doubleValue());
+                    } else if (DictConstants.EXPOSURE_RATE.equals(sceneComputerRateEntity.getComputerType())) {
+                        Object exposureRate = jsonObject.getOrDefault("暴露率", 0.0);
+                        BigDecimal bigDecimal = new BigDecimal(exposureRate.toString());
+                        orDefault.setExposureRate(bigDecimal.doubleValue());
+                    } else {
+                        continue;
+                    }
+                    sceneComputerECRateVoMap.put(sceneName, orDefault);
+                }
+            }
+        }
+        return new ArrayList<>(sceneComputerECRateVoMap.values());
+    }
+
+    @Override
+    public SceneComputerRateEntity loadSceneCoveragePrefixKeyNums() {
+        return sceneComputerRateMapper.loadSceneCoveragePrefixKeyNums(DictConstants.COVERAGE_PREFIX);
+    }
+
+    @Override
+    public CoveragePrefixStatusVO loadConvergePrefixStatus() {
+        SceneComputerRateEntity sceneComputerRateEntity = this.loadSceneCoveragePrefixKeyNums();
+        CoveragePrefixStatusVO coveragePrefixStatusVO = new CoveragePrefixStatusVO();
+        if (sceneComputerRateEntity != null) {
+            if (!StringUtil.isEmpty(sceneComputerRateEntity.getComputerResult())) {
+                coveragePrefixStatusVO.setStatusCode(DictConstants.PROJECT_COMPLETED);
+                coveragePrefixStatusVO.setStatusMessage("执行完成");
+                String computerResult = sceneComputerRateEntity.getComputerResult();
+                log.info("ConvergePrefix computerResult={}", computerResult);
+                List<JSONObject> jsonObjects = JSONObject.parseArray(computerResult, JSONObject.class);
+                List<BaseSceneKeysNumsVo> baseSceneKeysNumsVos = new ArrayList<>(jsonObjects.size());
+                for (JSONObject jsonObject : jsonObjects) {
+                    String sceneName = (String) jsonObject.get("场景库名称");
+                    Integer keyNums = (Integer) jsonObject.get("关键场景个数");
+                    BaseSceneKeysNumsVo baseSceneKeysNumsVo = new BaseSceneKeysNumsVo(sceneName, keyNums);
+                    baseSceneKeysNumsVos.add(baseSceneKeysNumsVo);
+                }
+                coveragePrefixStatusVO.setBaseSceneKeysNumsVos(baseSceneKeysNumsVos);
+            } else {
+                coveragePrefixStatusVO.setStatusCode(DictConstants.PROJECT_RUNNING);
+                coveragePrefixStatusVO.setStatusMessage(DictConstants.PROJECT_RUNNING_NAME);
+            }
+        } else {
+            coveragePrefixStatusVO.setStatusCode(DictConstants.PROJECT_WAITING);
+            coveragePrefixStatusVO.setStatusMessage("未执行");
+        }
+        return coveragePrefixStatusVO;
+    }
+}

+ 39 - 13
simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/impl/SimulationProjectServiceImpl.java

@@ -23,18 +23,20 @@ import api.common.pojo.vo.project.*;
 import api.common.pojo.vo.scene.ScenePackageNewVO;
 import api.common.util.*;
 import com.alibaba.fastjson.JSONObject;
+import com.css.simulation.resource.server.app.constant.ProjectRunStateEnum;
+import com.css.simulation.resource.server.app.constant.SceneTypeEnum;
+import com.css.simulation.resource.server.app.service.AlgorithmService;
+import com.css.simulation.resource.server.app.service.DictService;
+import com.css.simulation.resource.server.app.service.SceneComputerRateService;
 import com.css.simulation.resource.server.app.service.SceneEvaluationRuleService;
+import com.css.simulation.resource.server.app.service.job_manage.SimulationProjectService;
 import com.css.simulation.resource.server.domain.service.UserDomainService;
+import com.css.simulation.resource.server.infra.db.entity.SceneComputerRateEntity;
 import com.css.simulation.resource.server.infra.db.mysql.mapper.*;
-import com.css.simulation.resource.server.app.service.AlgorithmService;
-import com.css.simulation.resource.server.infra.util.*;
 import com.css.simulation.resource.server.infra.feign.service.AlgoPlatformService;
 import com.css.simulation.resource.server.infra.feign.service.FileDownService;
 import com.css.simulation.resource.server.infra.feign.service.MonitorService;
-import com.css.simulation.resource.server.app.constant.ProjectRunStateEnum;
-import com.css.simulation.resource.server.app.constant.SceneTypeEnum;
-import com.css.simulation.resource.server.app.service.job_manage.SimulationProjectService;
-import com.css.simulation.resource.server.app.service.DictService;
+import com.css.simulation.resource.server.infra.util.*;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.google.common.base.Joiner;
@@ -152,6 +154,9 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
     @Resource
     private SimulationProjectService simulationProjectService;
 
+    @Resource
+    private SceneComputerRateService sceneComputerRateService;
+
     // * -------------------------------- Comment --------------------------------
 
     private final String[] dateFmtArr = new String[]{"yyyyMMdd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd"};
@@ -335,6 +340,14 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
             simulationManualProjectMapper.updateDetailsAndNowRunStateById(oldProjectId, projectDetailsVOJson, DictConstants.PROJECT_TERMINATING);
             projectStopToKafka(oldProjectPO);
         } else if (DictConstants.PROJECT_RUNNING.equals(newState)) {
+
+            // 覆盖率需要依赖前置条件,所以在这判断一下,前置条件有没有好
+            if (!StringUtil.isEmpty(oldProjectPO.getCoverageRateEvaluationRuleId())) {
+                SceneComputerRateEntity sceneComputerRateEntity = sceneComputerRateService.loadSceneCoveragePrefixKeyNums();
+                if (sceneComputerRateEntity == null || StringUtil.isEmpty(sceneComputerRateEntity.getComputerResult())) {
+                    return new ResponseBodyVO<>(ResponseBodyVO.Response.CLIENT_FAILURE, "覆盖率前置条件任务没有结果,不能执行覆盖率计算");
+                }
+            }
             projectDetailsVO.setNowRunState(DictConstants.PROJECT_RUNNING);
             projectDetailsVO.setNowRunStateName(DictConstants.PROJECT_RUNNING_NAME);
             //1 校验项目的信息是否可用
@@ -5139,7 +5152,7 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
                             }
                             in.close();
                             evaluation_csv_done = true;
-                        }else if ("output_driverCtrl.csv".equals(fileName)) {
+                        } else if ("output_driverCtrl.csv".equals(fileName)) {
                             if (output_driverCtrl_csv_done) {
                                 continue;
                             }
@@ -5164,7 +5177,7 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
                             }
                             in.close();
                             output_driverCtrl_csv_done = true;
-                        }else if ("output_objectState_sensor.csv".equals(fileName)) {
+                        } else if ("output_objectState_sensor.csv".equals(fileName)) {
                             if (output_objectState_sensor_csv_done) {
                                 continue;
                             }
@@ -5189,7 +5202,7 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
                             }
                             in.close();
                             output_objectState_sensor_csv_done = true;
-                        }else if ("output_objectState.csv".equals(fileName)) {
+                        } else if ("output_objectState.csv".equals(fileName)) {
                             if (output_objectState_csv_done) {
                                 continue;
                             }
@@ -5214,7 +5227,7 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
                             }
                             in.close();
                             output_objectState_csv_done = true;
-                        }else if ("output_roadMark.csv".equals(fileName)) {
+                        } else if ("output_roadMark.csv".equals(fileName)) {
                             if (output_roadMark_csv_done) {
                                 continue;
                             }
@@ -5239,7 +5252,7 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
                             }
                             in.close();
                             output_roadMark_csv_done = true;
-                        }else if ("output_roadPos.csv".equals(fileName)) {
+                        } else if ("output_roadPos.csv".equals(fileName)) {
                             if (output_roadPos_csv_done) {
                                 continue;
                             }
@@ -5264,7 +5277,7 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
                             }
                             in.close();
                             output_roadPos_csv_done = true;
-                        }else if ("output_trafficLight.csv".equals(fileName)) {
+                        } else if ("output_trafficLight.csv".equals(fileName)) {
                             if (output_trafficLight_csv_done) {
                                 continue;
                             }
@@ -5289,7 +5302,7 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
                             }
                             in.close();
                             output_trafficLight_csv_done = true;
-                        }else if ("output_trafficSignal.csv".equals(fileName)) {
+                        } else if ("output_trafficSignal.csv".equals(fileName)) {
                             if (output_trafficSignal_csv_done) {
                                 continue;
                             }
@@ -6261,6 +6274,19 @@ public class SimulationProjectServiceImpl implements SimulationProjectService {
         return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, pageInfo);
     }
 
+    @Override
+    public ResponseBodyVO<List<SceneComputerECRateVo>> selectProjectComputerRates(SimulationManualProjectParam param) {
+
+        String projectId = param.getProjectId();
+        if (StringUtil.isEmpty(projectId)) {
+            return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, new ArrayList<>());
+        }
+        List<String> computerTypeList = new ArrayList<>();
+        computerTypeList.add(DictConstants.COVERAGE);
+        computerTypeList.add(DictConstants.EXPOSURE_RATE);
+        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, sceneComputerRateService.selectProjectComputerRates(projectId, computerTypeList));
+    }
+
     private void convertPoToVo(SimulationAutomaticProjectPO po, SimulationManualProjectSingleVo vo) {
         vo.setId(po.getId());
         vo.setProjectName(po.getProjectName());

+ 32 - 0
simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/service/SceneComputerRateService.java

@@ -0,0 +1,32 @@
+package com.css.simulation.resource.server.app.service;
+
+import api.common.pojo.vo.project.SceneComputerECRateVo;
+import api.common.pojo.vo.scene.CoveragePrefixStatusVO;
+import com.css.simulation.resource.server.infra.db.entity.SceneComputerRateEntity;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author zhoutianfu
+ * @update 2025/3/2 13:20
+ * @since 2025/3/2 13:20
+ **/
+public interface SceneComputerRateService {
+
+    void saveSceneComputerRate(SceneComputerRateEntity params);
+
+    void deleteByIdAndType(@Param("computerRateId") String computerRateId, @Param("computerType") String computerType);
+
+    /**
+     * 根据项目id和场景计算类型查询覆盖率,暴露率等数据
+     * @param projectId projectId
+     * @param computerTypeList computerTypeList
+     * @return List<SceneComputerECRateVo>
+     */
+    List<SceneComputerECRateVo>  selectProjectComputerRates(String projectId, List<String> computerTypeList);
+
+    SceneComputerRateEntity loadSceneCoveragePrefixKeyNums();
+
+    CoveragePrefixStatusVO loadConvergePrefixStatus();
+}

+ 71 - 0
simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/service/SceneReferenceLibService.java

@@ -1,5 +1,6 @@
 package com.css.simulation.resource.server.app.service;
 
+import api.common.pojo.common.ResponseBodyVO;
 import api.common.pojo.constants.DictConstants;
 import api.common.pojo.constants.LogConstants;
 import api.common.pojo.enums.SceneEvaluationLevel;
@@ -7,8 +8,11 @@ import api.common.pojo.param.MinioParameter;
 import api.common.pojo.param.scene.*;
 import api.common.pojo.po.scene.*;
 import api.common.pojo.po.system.SceneImportPO;
+import api.common.pojo.vo.scene.CoveragePrefixStatusVO;
 import api.common.util.*;
 import com.alibaba.druid.util.StringUtils;
+import com.alibaba.fastjson.JSONObject;
+import com.css.simulation.resource.server.infra.db.entity.SceneComputerRateEntity;
 import com.css.simulation.resource.server.infra.db.mysql.mapper.*;
 import com.css.simulation.resource.server.infra.feign.service.FileDownService;
 import com.css.simulation.resource.server.infra.util.AuthUtil;
@@ -51,6 +55,9 @@ public class SceneReferenceLibService {
     @Resource
     private SystemScenePackageSublistMapper systemScenePackageSublistMapper;
 
+    @Resource
+    private SceneComputerRateService sceneComputerRateService;
+
     public SceneEvaluationOperatePO getSceneEvaluationFirst(SceneReferenceLibSelectParam params, List<String> sceneIdList) {
         // 添加复杂度 危险度数据
         SceneEvaluationForListParam sceneEvaluationForListParam = new SceneEvaluationForListParam();
@@ -439,5 +446,69 @@ public class SceneReferenceLibService {
         }
     }
 
+    public ResponseBodyVO<CoveragePrefixStatusVO> loadConvergePrefixStatus() {
+        // 查询执行结果
+        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, sceneComputerRateService.loadConvergePrefixStatus());
+
+    }
+
+
+    public ResponseBodyVO<String> handleConvergePrefix() {
+        // 查询覆盖率前置的脚本
+        SceneEvaluationScriptParam sceneEvaluationScriptParam = new SceneEvaluationScriptParam();
+        sceneEvaluationScriptParam.setSceneEvaluationType(Collections.singletonList(DictConstants.COVERAGE_PREFIX));
+        List<SceneEvaluationRulePO> list = sceneEvaluationRuleService.queryEvaluationScript(sceneEvaluationScriptParam);
+
+        if (CollectionUtil.isEmpty(list)) {
+            return new ResponseBodyVO<>(ResponseBodyVO.Response.CLIENT_FAILURE, "覆盖率前置脚本未上传");
+        }
+        SceneEvaluationRulePO sceneEvaluationRulePO = list.get(0);
+        // 查询 所有场景包
+        SceneReferenceLibSelectParam params = new SceneReferenceLibSelectParam();
+        List<SceneReferenceLibPO> sceneReferenceLibPOS = sceneReferenceLibMapper.querySceneReferenceLibList(params);
+
+        SceneEvaluationComputeParam sceneComplexityEvaluationComputeParam = new SceneEvaluationComputeParam();
+        sceneComplexityEvaluationComputeParam.setSceneEvaluationComputeSubParam(new ArrayList<>());
+        Set<String> sceneIds = new HashSet<>(sceneReferenceLibPOS.size());
+        for (SceneReferenceLibPO sceneReferenceLibPO : sceneReferenceLibPOS) {
+            SceneEvaluationComputeSubParam sceneEvaluationComputeSubParam = new SceneEvaluationComputeSubParam();
+            sceneEvaluationComputeSubParam.setSceneId(sceneReferenceLibPO.getSceneId());
+            sceneEvaluationComputeSubParam.setSceneXOSCPath(sceneReferenceLibPO.getXmlAddress());
+            sceneEvaluationComputeSubParam.setSceneXODRPath(sceneReferenceLibPO.getXodrAddress());
+            sceneEvaluationComputeSubParam.setSceneType(DictConstants.SCENE_REFERENCE_LIB);
+            sceneComplexityEvaluationComputeParam.getSceneEvaluationComputeSubParam().add(sceneEvaluationComputeSubParam);
+            sceneIds.add(sceneReferenceLibPO.getSceneId());
+        }
+
 
+        // 执行
+        String taskId = StringUtil.getRandomUUID();
+        sceneComplexityEvaluationComputeParam.setTaskId(taskId);
+        sceneComplexityEvaluationComputeParam.setEvaluationId(taskId);
+        sceneComplexityEvaluationComputeParam.setType(DictConstants.SCENE_IMPORT_EVALUATION_TYPE);
+        sceneComplexityEvaluationComputeParam.setSceneEvaluationRuleId(sceneEvaluationRulePO.getRuleId());
+        sceneComplexityEvaluationComputeParam.setComputeType(DictConstants.COVERAGE_PREFIX);
+        sceneComplexityEvaluationComputeParam.setCreateUserId(AuthUtil.getCurrentUserId());
+        // 删除旧的前置结果
+        SceneComputerRateEntity sceneComputerRateEntity1 = sceneComputerRateService.loadSceneCoveragePrefixKeyNums();
+        if (sceneComputerRateEntity1 != null) {
+            if (StringUtil.isEmpty(sceneComputerRateEntity1.getComputerResult())) {
+                return new ResponseBodyVO<>(ResponseBodyVO.Response.CLIENT_FAILURE, "覆盖率前置脚本还在运行中,请稍等再重跑");
+            }
+            sceneComputerRateService.deleteByIdAndType(sceneComputerRateEntity1.getComputerRateId(), DictConstants.COVERAGE_PREFIX);
+        }
+        SceneComputerRateEntity sceneComputerRateEntity = new SceneComputerRateEntity();
+        sceneComputerRateEntity.setTaskId(taskId);
+        sceneComputerRateEntity.setComputerRateId(taskId);
+        sceneComputerRateEntity.setComputerType(DictConstants.COVERAGE_PREFIX);
+        sceneComputerRateEntity.setSceneIdList(JSONObject.toJSONString(new ArrayList<>(sceneIds)));
+        sceneComputerRateEntity.setRuleId(sceneEvaluationRulePO.getRuleId());
+        sceneComputerRateEntity.setCreateUserId(AuthUtil.getCurrentUserId());
+        sceneComputerRateEntity.setCreateTime(TimeUtil.getNowForMysql());
+        // 新增一个空前置结果,好处理状态
+        sceneComputerRateService.saveSceneComputerRate(sceneComputerRateEntity);
+        // 执行任务计算覆盖率前置条件
+        sceneEvaluationRuleService.computeSceneEvaluation(sceneComplexityEvaluationComputeParam);
+        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS);
+    }
 }

+ 2 - 0
simulation-resource-server/src/main/java/com/css/simulation/resource/server/app/service/job_manage/SimulationProjectService.java

@@ -90,4 +90,6 @@ public interface SimulationProjectService {
   ResponseBodyVO<SimulationManualProjectEvaluationRuleVo> getEvaluationRuleDetail(SimulationManualProjectParam param);
 
   ResponseBodyVO<PageInfo<ManualProjectTaskVo>> querySceneEvaluationDetail(SimulationManualProjectEvaluationDetailParam param);
+
+  ResponseBodyVO<List<SceneComputerECRateVo>> selectProjectComputerRates(SimulationManualProjectParam param);
 }

+ 38 - 0
simulation-resource-server/src/main/java/com/css/simulation/resource/server/infra/db/entity/SceneComputerRateEntity.java

@@ -0,0 +1,38 @@
+package com.css.simulation.resource.server.infra.db.entity;
+
+import lombok.*;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * 场景各种率计算PO
+ */
+@EqualsAndHashCode()
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SceneComputerRateEntity implements Serializable {
+    private String computerRateId;
+    /**
+     * 计算类型 {@link api.common.pojo.constants.DictConstants  DictConstants} 使用 EXPOSURE_RATE COVERAGE
+     */
+    private String computerType;
+    private String ruleId;
+    private String ruleName;
+    private String algorithmId;
+    private String algorithmName;
+    private String vehicleId;
+    private String vehicleName;
+    private String taskId;
+    private String computerResult;
+    private String sceneIdList;
+    private String isDeleted;
+    public Timestamp createTime;
+    /**
+     * 记录创建人(用户id)
+     */
+    public String createUserId;
+}

+ 21 - 0
simulation-resource-server/src/main/java/com/css/simulation/resource/server/infra/db/mysql/mapper/SceneComputerRateMapper.java

@@ -0,0 +1,21 @@
+package com.css.simulation.resource.server.infra.db.mysql.mapper;
+
+import com.css.simulation.resource.server.infra.db.entity.SceneComputerRateEntity;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+
+@Repository
+@Mapper
+public interface SceneComputerRateMapper {
+
+    void saveSceneComputerRate(SceneComputerRateEntity params);
+
+    SceneComputerRateEntity loadSceneCoveragePrefixKeyNums(@Param("computerType") String computerType);
+
+    void deleteByIdAndType(@Param("computerRateId") String computerRateId, @Param("computerType") String computerType);
+
+    List<SceneComputerRateEntity> loadSceneComputerRateByTaskIdAndType(@Param("taskId") String taskId, @Param("computerTypeList") List<String> computerTypeList);
+}

+ 61 - 0
simulation-resource-server/src/main/resources/mysql/mapper/SceneComputerRateMapper.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.css.simulation.resource.server.infra.db.mysql.mapper.SceneComputerRateMapper" >
+
+    <resultMap id="resultMap" type="com.css.simulation.resource.server.infra.db.entity.SceneComputerRateEntity">
+        <id column="computer_rate_id" property="computerRateId" jdbcType="VARCHAR"/>
+        <result column="computer_type" property="computerType" jdbcType="VARCHAR"/>
+        <result column="rule_id" property="ruleId" jdbcType="VARCHAR"/>
+        <result column="algorithm_id" property="algorithmId" jdbcType="VARCHAR"/>
+        <result column="vehicle_id" property="vehicleId" jdbcType="DECIMAL"/>
+        <result column="task_id" property="taskId" jdbcType="DECIMAL"/>
+        <result column="computer_result" property="computerResult" jdbcType="DECIMAL"/>
+        <result column="scene_id_list" property="sceneIdList" jdbcType="DECIMAL"/>
+        <result column="is_deleted" property="isDeleted" jdbcType="VARCHAR"/>
+        <result column="create_user_id" property="createUserId" jdbcType="DECIMAL"/>
+        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <insert id="saveSceneComputerRate"
+            parameterType="com.css.simulation.resource.server.infra.db.entity.SceneComputerRateEntity">
+
+      insert into simulation.scene_computer_rate
+            (computer_rate_id, computer_type, rule_id, algorithm_id,vehicle_id,task_id,
+             computer_result, scene_id_list,
+             is_deleted, create_user_id, create_time
+            )
+            values (#{computerRateId,jdbcType=VARCHAR}, #{computerType,jdbcType=VARCHAR}, #{ruleId,jdbcType=VARCHAR},
+                    #{algorithmId,jdbcType=VARCHAR},#{vehicleId,jdbcType=VARCHAR},#{taskId,jdbcType=VARCHAR},
+                    #{computerResult,jdbcType=VARCHAR}, #{sceneIdList,jdbcType=VARCHAR},
+                    #{isDeleted,jdbcType=VARCHAR},
+                    #{createUserId,jdbcType=VARCHAR},
+                    #{createTime,jdbcType=TIMESTAMP})
+    </insert>
+    <update id="deleteByIdAndType">
+        update simulation.scene_computer_rate set is_deleted = 1
+            where computer_rate_id = #{computerRateId} and  computer_type = #{computerType}
+    </update>
+
+    <select id="loadSceneCoveragePrefixKeyNums" resultMap="resultMap"
+            parameterType="java.lang.String">
+        select computer_rate_id, computer_type, computer_result
+        from simulation.scene_computer_rate where computer_type = #{computerType} and is_deleted = '0'
+    </select>
+    <select id="loadSceneComputerRateByTaskIdAndType" resultMap="resultMap">
+        select computer_rate_id, task_id, computer_type, computer_result
+        from simulation.scene_computer_rate
+        <where>
+            <if test="taskId != null and taskId != ''">
+                task_id = #{taskId}
+            </if>
+            <if test="computerTypeList != null and computerTypeList.size()>0">
+                AND computer_type IN
+                <foreach collection="computerTypeList" item="item" index="index"
+                         separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </if>
+            and is_deleted = 0
+        </where>
+    </select>
+</mapper>