root 2 жил өмнө
parent
commit
8c45bfc77d

+ 19 - 0
simulation-resource-video/pom.xml

@@ -29,6 +29,25 @@
         </dependency>
         <!-- minio - 结束 -->
 
+        <!-- 数据库 - 开始 -->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <!-- 数据库 - 结束 -->
+
         <!-- nacos - 开始 -->
         <dependency>
             <groupId>org.springframework.cloud</groupId>

+ 13 - 0
simulation-resource-video/src/main/java/com/css/simulation/resource/video/mapper/ConfigMapper.java

@@ -0,0 +1,13 @@
+package com.css.simulation.resource.video.mapper;
+
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+@Mapper
+public interface ConfigMapper {
+    @Select("select vehicle_id vehicleId\n" +
+            "from model_config\n" +
+            "where id = #{id}")
+    String getVehicleId(String id);
+}

+ 17 - 0
simulation-resource-video/src/main/java/com/css/simulation/resource/video/mapper/SimulationAutomaticProjectMapper.java

@@ -0,0 +1,17 @@
+package com.css.simulation.resource.video.mapper;
+
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+@Mapper
+public interface SimulationAutomaticProjectMapper {
+     @Select("select vehicle\n" +
+             "from simulation_manual_project\n" +
+             "where id = #{id}")
+     String vehicleBySdId(String id);
+     @Select("select vehicle\n" +
+             "from simulation_automatic_project\n" +
+             "where id = #{id}")
+     String vehicleByZdId(String id);
+}

+ 91 - 0
simulation-resource-video/src/main/java/com/css/simulation/resource/video/mapper/VehicleMapper.java

@@ -0,0 +1,91 @@
+package com.css.simulation.resource.video.mapper;
+
+import api.common.pojo.vo.model.VehicleVO;
+import com.css.simulation.resource.video.pojo.po.VehiclePO;
+import feign.Param;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Result;
+import org.apache.ibatis.annotations.Results;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.type.JdbcType;
+
+@Mapper
+public interface VehicleMapper {
+
+    @Results(id = "vehicle", value = {
+            @Result(column = "model_label", property = "modelLabel", jdbcType = JdbcType.VARCHAR),
+            @Result(column = "max_speed", property = "maxSpeed", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "engine_power", property = "enginePower", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "max_deceleration", property = "maxDeceleration", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "max_steering_angle", property = "maxSteeringAngle", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "mass", property = "mass", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "front_surface_effective", property = "frontSurfaceEffective", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "air_drag_coefficient", property = "airDragCoefficient", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "rolling_resistance_coefficient", property = "rollingResistanceCoefficient", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "wheel_diameter", property = "wheelDiameter", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "wheel_drive", property = "wheelDrive", jdbcType = JdbcType.VARCHAR),
+            @Result(column = "overall_efficiency", property = "overallEfficiency", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "front_distance", property = "frontDistance", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "rear_distance", property = "rearDistance", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "left_distance", property = "leftDistance", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "right_distance", property = "rightDistance", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "height_distance", property = "heightDistance", jdbcType = JdbcType.DECIMAL),
+            @Result(column = "wheelbase", property = "wheelbase", jdbcType = JdbcType.DECIMAL),
+    })
+    @Select("select model_label,\n" +
+            "       max_speed,\n" +
+            "       engine_power,\n" +
+            "       max_deceleration,\n" +
+            "       max_steering_angle,\n" +
+            "       mass,\n" +
+            "       front_surface_effective,\n" +
+            "       air_drag_coefficient,\n" +
+            "       rolling_resistance_coefficient,\n" +
+            "       wheel_diameter,\n" +
+            "       wheel_drive,\n" +
+            "       overall_efficiency,\n" +
+            "       front_distance,\n" +
+            "       rear_distance,\n" +
+            "       left_distance,\n" +
+            "       right_distance,\n" +
+            "       height_distance,\n" +
+            "       wheelbase\n" +
+            "from model_vehicle\n" +
+            "where is_deleted = '0'\n" +
+            "  and id = (\n" +
+            "    select mc.vehicle_id\n" +
+            "    from model_config mc\n" +
+            "    where mc.id = #{vehicleConfigId}\n" +
+            ")")
+    VehiclePO selectByVehicleConfigId(@Param("vehicleConfigId") String vehicleConfigId);
+
+    @Select("select id,\n" +
+            "       vehicle_code,\n" +
+            "       vehicle_name,\n" +
+            "       description,\n" +
+            "       vehicle_type,\n" +
+            "       model_label,\n" +
+            "       vehicle_front_view,\n" +
+            "       vehicle_top_view,\n" +
+            "       max_speed,\n" +
+            "       engine_power,\n" +
+            "       max_deceleration,\n" +
+            "       max_steering_angle,\n" +
+            "       mass,\n" +
+            "       front_surface_effective,\n" +
+            "       air_drag_coefficient,\n" +
+            "       rolling_resistance_coefficient,\n" +
+            "       wheel_diameter,\n" +
+            "       wheel_drive\n" +
+            "       overall_efficiency\n" +
+            "       front_distance\n" +
+            "       rear_distance\n" +
+            "       left_distance\n" +
+            "       right_distance\n" +
+            "       height_distance\n" +
+            "       wheelbase\n" +
+            "       share\n" +
+            "from model_vehicle\n" +
+            "where  id = #{vehicleId}\n")
+    VehicleVO getVehicleInfo(@Param("vehicleId") String vehicleId);
+}

+ 59 - 0
simulation-resource-video/src/main/java/com/css/simulation/resource/video/pojo/po/VehiclePO.java

@@ -0,0 +1,59 @@
+package com.css.simulation.resource.video.pojo.po;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class VehiclePO {
+
+    /**
+     * 车辆模型
+     * 		"model": {
+     * 			"model_label": "AudiA6_10"
+     *                },
+     "dynamics": {
+     "dynamics_maxspeed": 67,
+     "dynamics_enginepower": 150000,
+     "dynamics_maxdecel": 9.5,
+     "dynamics_maxsteering": 0.48,
+     "dynamics_mass": 1700,
+     "dynamics_frontsurfaceeffective": 2.2,
+     "dynamics_airdragcoefficient": 0.31,
+     "dynamics_rollingresistance": 0,
+     "dynamics_wheeldiameter": 0.684,
+     "dynamics_wheeldrive": "wheel_drive_front",
+     "dynamics_overallefficiency": 0.75,
+     "dynamics_distfront": 3.838,
+     "dynamics_distrear": 1.086,
+     "dynamics_distleft": 0.94,
+     "dynamics_distright": 0.94,
+     "dynamics_distheight": 1.444,
+     "dynamics_wheelbase": 2.91
+     },
+     */
+    private String modelLabel;
+    private BigDecimal maxSpeed;
+    private BigDecimal enginePower;
+    private BigDecimal maxDeceleration;
+    private BigDecimal maxSteeringAngle;
+    private BigDecimal mass;
+    private BigDecimal frontSurfaceEffective;
+    private BigDecimal airDragCoefficient;
+    private BigDecimal rollingResistanceCoefficient;
+    private BigDecimal wheelDiameter;
+    private String wheelDrive;
+    private BigDecimal overallEfficiency;
+    private BigDecimal frontDistance;
+    private BigDecimal rearDistance;
+    private BigDecimal leftDistance;
+    private BigDecimal rightDistance;
+    private BigDecimal heightDistance;
+    private BigDecimal wheelbase;
+}

+ 334 - 0
simulation-resource-video/src/main/java/com/css/simulation/resource/video/service/VideoService.java

@@ -0,0 +1,334 @@
+package com.css.simulation.resource.video.service;
+
+import api.common.pojo.po.scene.VehicleTypePO;
+import api.common.pojo.vo.model.VehicleVO;
+import api.common.util.FileUtil;
+import api.common.util.LinuxUtil;
+import com.css.simulation.resource.video.mapper.ConfigMapper;
+import com.css.simulation.resource.video.mapper.SimulationAutomaticProjectMapper;
+import com.css.simulation.resource.video.mapper.VehicleMapper;
+import com.css.simulation.resource.video.util.MinioUtil;
+import io.minio.MinioClient;
+import lombok.SneakyThrows;
+import org.dom4j.Attribute;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.SAXReader;
+import org.dom4j.io.XMLWriter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Iterator;
+
+@Service
+public class VideoService {
+    @Resource
+    VehicleMapper vehicleMapper;
+    @Resource
+    ConfigMapper configMapper;
+    @Resource
+    SimulationAutomaticProjectMapper simulationAutomaticProjectMapper;
+    @Resource
+    MinioClient minioClient;
+
+
+    //* -------------------------------- Comment --------------------------------
+
+    public static final String oldXoscName = "simulation_my0.xosc";
+    public static final String newXoscName = "simulation_my05.xosc";
+    public static final String oldXoscRelativePath = "xosc/simulation_my0.xosc";
+    public static final String[] csvNameArray = {"Ego.csv", "evaluation.csv"};
+
+    //* -------------------------------- Comment --------------------------------
+    @Value("${scheduler.linux-path.temp}")
+    String linuxTempPath;
+    @Value("${scheduler.minio-path.project-result}")
+    String projectResultPathOfMinio;
+    @Value("${minio.bucket-name}")
+    String bucketName;
+
+
+    /**
+     * 生成视频
+     */
+    @SneakyThrows
+    public void generateVideo(String projectId, String projectType, String taskId) {
+        //1 获取两个 csv 的目录,和 xodr 和 osgb 三个路径
+        String csvDirectoryPath = linuxTempPath + "video/" + projectId + "/" + taskId + "/";
+        String xodrPath = FileUtil.listAbsolutePathByFiletype(csvDirectoryPath, ".xodr").get(0);    // xodr 文件在创建项目时已经下载好了
+        String osgbPath = FileUtil.listAbsolutePathByFiletype(csvDirectoryPath, ".osgb").get(0);    // osgb 文件在创建项目时已经下载好了
+        //2 下载 csv 文件
+        for (String csvName : csvNameArray) {
+            MinioUtil.downloadToFile(minioClient, bucketName, projectResultPathOfMinio + projectId + "/" + taskId + "/" + csvName, csvDirectoryPath + "/" + csvName);
+        }
+        //3 生成 xosc 文件
+        String xoscPath = generateXosc(csvDirectoryPath, xodrPath, osgbPath, projectId, projectType);
+        //4 生成图片
+        String pictureDirectoryPath = csvDirectoryPath + "picture";
+        FileUtil.createDirectory(pictureDirectoryPath);
+        String esminiCommand =  "/root/disk1/simulation-cloud/esmini/build/EnvironmentSimulator/code-examples/image-capture/image-capture "
+                + xoscPath + " " + pictureDirectoryPath + "/screenshot";
+        String esminiResult = LinuxUtil.execute(esminiCommand);
+        //5 生成视频
+        String videoName = "simulation_output.mp4";
+        String videoTargetPathOfLinux = csvDirectoryPath + "video";
+        FileUtil.createDirectory(videoTargetPathOfLinux);
+        String videoTargetPathOfMinio = projectResultPathOfMinio + projectId + "/" + taskId + "/" + videoName;
+
+        String execute = LinuxUtil.execute("ffmpeg"
+                + " -f image2 -framerate 30 "
+                + " -i " + pictureDirectoryPath + "/screenshot_%05d.tga"
+                + " -c:v libx264 -vf format=yuv420p -crf 20 "
+                + videoTargetPathOfLinux + "/" + videoName
+        );
+        //6 将视频上传到 minio
+        MinioUtil.uploadFromFile(minioClient, videoTargetPathOfLinux + videoName, bucketName, videoTargetPathOfMinio);
+    }
+
+    /**
+     * Xosc
+     */
+    @SneakyThrows
+    private String generateXosc(String csvDirectoryPath, String xodrPath, String osgbPath, String projectId, String projectType) {
+        //1 生成 xosc 文件
+        String command = "D:\\Users\\anaconda3\\envs\\flaskProject\\python.exe C:/Users/CSS/Desktop/simulation/gqSw/simulation_my.py " + csvDirectoryPath;
+        String execute = LinuxUtil.execute(command);    // 执行命令后生成 xosc 到目录下
+        String oldXoscPath = csvDirectoryPath + oldXoscRelativePath;  // 生成文件的路径是固定的
+        //2 调用修改xosc
+        return modifyXosc(oldXoscPath, xodrPath, osgbPath, projectId, projectType);  // 返回最新的xosc文件全路径
+    }
+
+
+    /**
+     * 修改 xosc 文件
+     */
+    @SneakyThrows
+    private String modifyXosc(String oldXoscPath, String xodrPath, String osgbPath, String projectId, String projectType) {
+        String newXoscPath = oldXoscPath.replace(oldXoscName, newXoscName);
+        //获取三维自车对象
+        VehicleTypePO po = vehicleById(projectId, projectType);
+
+        //1 创建Reader对象
+        SAXReader reader = new SAXReader();
+        //2 加载xml
+        // Document document = reader.read(new File("D:\\simulation_my0.xosc"));
+        Document document = reader.read(new File(oldXoscPath));
+        //3 获取根节点
+        Element root = document.getRootElement();
+        Iterator<Element> iterator0 = root.elementIterator();
+        while (iterator0.hasNext()) {   // 第一层遍历
+            Element node1 = iterator0.next();
+            if ("Entities".equals(node1.getName())) { // 找到第一层节点后进入第二层遍历
+                Iterator<Element> iterator1 = node1.elementIterator();
+                while (iterator1.hasNext()) {
+                    Element node2 = iterator1.next();
+                    // 添加节点
+                    if ("ScenarioObject".equals(node2.getName())) {
+                        String scenarioObjectName = node2.attribute("name").getValue();
+                        if (scenarioObjectName.equals("Ego")) {
+                            Iterator<Element> iterator2 = node2.elementIterator();
+                            while (iterator2.hasNext()) {
+                                Element node3 = iterator2.next();
+                                if ("Vehicle".equals(node3.getName())) {
+                                    Attribute name = node3.attribute("name");
+                                    name.setText("Audi_A3_2009_red");
+                                    Attribute vehicleCategory = node3.attribute("vehicleCategory");
+                                    vehicleCategory.setText(po.getVehicleCategory());
+                                    node3.addAttribute("model3d",po.getModel3d());//自车渲染
+                                    Iterator<Element> iterator4 = node3.elementIterator();
+                                    while (iterator4.hasNext()) {
+                                        Element node4 = iterator4.next();
+                                        if ("Performance".equals(node4.getName())) {
+                                            Attribute maxSpeed = node4.attribute("maxSpeed");
+                                            maxSpeed.setText(po.getPerformanceMaxSpeed() + "");
+                                            Attribute maxDeceleration = node4.attribute("maxDeceleration");
+                                            maxDeceleration.setText(po.getPerformanceMaxDeceleration() + "");
+                                            Attribute maxAcceleration = node4.attribute("maxAcceleration");
+                                            maxAcceleration.setText(po.getPerformanceMaxAcceleration());
+                                        } else if ("BoundingBox".equals(node4.getName())) {
+                                            Iterator<Element> iterator5 = node4.elementIterator();
+                                            while (iterator5.hasNext()) {
+                                                Element node5 = iterator5.next();
+                                                if ("Center".equals(node5.getName())) {
+                                                    Attribute x = node5.attribute("x");
+                                                    x.setText(po.getCenterX() + "");
+                                                    Attribute y = node5.attribute("y");
+                                                    y.setText(po.getCenterY() + "");
+                                                    Attribute z = node5.attribute("z");
+                                                    z.setText(po.getCenterZ() + "");
+                                                }
+                                                if ("Dimensions".equals(node5.getName())) {
+                                                    Attribute width = node5.attribute("width");
+                                                    width.setText(po.getDimensionsWidth() + "");
+                                                    Attribute length = node5.attribute("length");
+                                                    length.setText(po.getDimensionsHeight() + "");
+                                                    Attribute height = node5.attribute("height");
+                                                    height.setText(po.getDimensionsHeight() + "");
+                                                }
+                                            }
+                                        } else if ("Axles".equals(node4.getName())) {
+                                            Iterator<Element> iterator5 = node4.elementIterator();
+                                            while (iterator5.hasNext()) {
+                                                Element node5 = iterator5.next();
+                                                if ("FrontAxle".equals(node5.getName())) {
+                                                    Attribute maxSteering = node5.attribute("maxSteering");
+                                                    maxSteering.setText(po.getFrontAxleMaxSteering() + "");
+                                                    Attribute wheelDiameter = node5.attribute("wheelDiameter");
+                                                    wheelDiameter.setText(po.getFrontAxleWheelDiameter() + "");
+                                                    Attribute trackWidth = node5.attribute("trackWidth");
+                                                    trackWidth.setText(po.getFrontAxleTrackWidth() + "");
+                                                    Attribute positionX = node5.attribute("positionX");
+                                                    positionX.setText(po.getFrontAxlePositionX() + "");
+                                                    Attribute positionZ = node5.attribute("positionZ");
+                                                    positionZ.setText(po.getFrontAxlePositionZ() + "");
+                                                }
+                                                if ("RearAxle".equals(node5.getName())) {
+                                                    Attribute maxSteering = node5.attribute("maxSteering");
+                                                    maxSteering.setText(po.getRearAxleMaxSteering());
+                                                    Attribute wheelDiameter = node5.attribute("wheelDiameter");
+                                                    wheelDiameter.setText(po.getRearAxleWheelDiameter() + "");
+                                                    Attribute trackWidth = node5.attribute("trackWidth");
+                                                    trackWidth.setText(po.getRearAxleTrackWidth() + "");
+                                                    Attribute positionX = node5.attribute("positionX");
+                                                    positionX.setText(po.getRearAxlePositionX() + "");
+                                                    Attribute positionZ = node5.attribute("positionZ");
+                                                    positionZ.setText(po.getRearAxlePositionZ() + "");
+                                                }
+                                            }
+                                        }
+
+                                    }
+                                }
+
+                            }
+
+                            // 保存文件
+                            // Writer osWrite = new OutputStreamWriter(Files.newOutputStream(Paths.get("D:\\simulation_my0000.xosc")));// 创建输出流
+
+                        }
+                    }
+                }
+            }
+
+            if ("RoadNetwork".equals(node1.getName())) { // 找到第一层节点后进入第二层遍历
+                Iterator<Element> iterator1 = node1.elementIterator();
+                while (iterator1.hasNext()) {
+                    Element node2 = iterator1.next();
+                    // 添加节点
+                    if ("LogicFile".equals(node2.getName())) {
+                        Attribute name = node2.attribute("filepath");
+                        name.setText(xodrPath);
+                    }
+                    if ("SceneGraphFile".equals(node2.getName())) {
+                        Attribute name = node2.attribute("filepath");
+                        name.setText(osgbPath);
+                    }
+                }
+            }
+
+            Writer osWrite = new OutputStreamWriter(Files.newOutputStream(Paths.get(newXoscPath)));
+            OutputFormat format = OutputFormat.createPrettyPrint(); // 获取输出的指定格式
+            format.setEncoding("UTF-8");// 设置编码 ,确保解析的xml为UTF-8格式
+            XMLWriter writer = new XMLWriter(osWrite, format);// XMLWriter
+            writer.write(document);// 把document写入xmlFile指定的文件(可以为被解析的文件或者新创建的文件)
+            writer.flush();
+            writer.close();
+        }
+        return newXoscPath;
+    }
+
+
+    //获取三维自车对象
+    @SneakyThrows
+    private VehicleTypePO vehicleById(String projectId, String projectType) {
+        VehicleTypePO po = new VehicleTypePO();
+        //项目类型 1.手动;2.自动
+        String vehicle = "";
+        if (projectType.equals("1")) {
+            vehicle = simulationAutomaticProjectMapper.vehicleBySdId(projectId);
+        } else if (projectType.equals("2")) {
+            vehicle = simulationAutomaticProjectMapper.vehicleByZdId(projectId);
+        }
+        String vehicleId = configMapper.getVehicleId(vehicle);
+
+        VehicleVO vehicleInfo = vehicleMapper.getVehicleInfo(vehicleId);
+
+        //自车模型
+        String car = vehicleInfo.getVehicleTypeStr().substring(0, vehicleInfo.getVehicleTypeStr().indexOf(","));
+        car = car.substring(0, 1).toLowerCase() + car.substring(1);
+        if (car.contains("truck")) {
+            po.setVehicleCategory("truck");
+        } else if (car.contains("trailer")) {
+            po.setVehicleCategory("trailer");
+        } else if (car.contains("van")) {
+            po.setVehicleCategory("van");
+        } else if (car.contains("semitrailer")) {
+            po.setVehicleCategory("semitrailer");
+        } else if (car.contains("bus")) {
+            po.setVehicleCategory("bus");
+        } else if (car.contains("motorbike")) {
+            po.setVehicleCategory("motorbike");
+        } else if (car.contains("bicycle")) {
+            po.setVehicleCategory("bicycle");
+        } else if (car.contains("bicycle")) {
+            po.setVehicleCategory("bicycle");
+        } else if (car.contains("train")) {
+            po.setVehicleCategory("train");
+        } else if (car.contains("tram")) {
+            po.setVehicleCategory("tram");
+        } else {
+            po.setVehicleCategory("car");
+        }
+
+        //车前距
+        BigDecimal frontDistance = vehicleInfo.getFrontDistance();
+        //车后距
+        BigDecimal mo2 = new BigDecimal("2");
+        BigDecimal rearDistance = vehicleInfo.getRearDistance();
+        if (frontDistance.compareTo(rearDistance) > 0) {
+            BigDecimal CenterX = (frontDistance.add(rearDistance)).divide(mo2).subtract(rearDistance);
+            po.setCenterX(CenterX);
+        } else {
+            BigDecimal CenterX = (frontDistance.add(rearDistance)).divide(mo2).subtract(frontDistance);
+            po.setCenterX(CenterX);
+        }
+        //车高
+        BigDecimal mo3 = new BigDecimal("3");
+        po.setCenterZ(vehicleInfo.getHeightDistance().divide(mo3));
+        //车左距  车右距
+        BigDecimal LeftDistance = vehicleInfo.getLeftDistance();
+        BigDecimal RightDistance = vehicleInfo.getRightDistance();
+        po.setDimensionsWidth(LeftDistance.add(RightDistance));
+        po.setDimensionsHeight(vehicleInfo.getHeightDistance());
+        po.setDimensionsLength(frontDistance.add(rearDistance));
+
+        po.setPerformanceMaxSpeed(vehicleInfo.getMaxSpeed());
+        po.setPerformanceMaxDeceleration(vehicleInfo.getMaxDeceleration());
+        BigDecimal moπ = new BigDecimal("3.141516");
+        BigDecimal mo180 = new BigDecimal("180");
+        po.setFrontAxleMaxSteering(vehicleInfo.getMaxSteeringAngle().multiply(moπ).divide(mo180));
+        po.setFrontAxleWheelDiameter(vehicleInfo.getWheelDiameter());
+        BigDecimal mo4 = new BigDecimal("4");
+        BigDecimal mo5 = new BigDecimal("5");
+        po.setFrontAxleTrackWidth(LeftDistance.add(RightDistance).multiply(mo4).divide(mo5));
+        po.setFrontAxlePositionX(po.getCenterX());
+        po.setFrontAxlePositionZ(po.getCenterZ());
+
+        po.setRearAxleWheelDiameter(vehicleInfo.getWheelDiameter());
+        po.setRearAxleTrackWidth(po.getFrontAxleTrackWidth());
+        po.setRearAxlePositionX(po.getFrontAxlePositionX());
+        po.setRearAxlePositionZ(po.getFrontAxlePositionZ());
+        return po;
+
+    }
+
+
+}

+ 171 - 0
simulation-resource-video/src/main/java/com/css/simulation/resource/video/util/MinioUtil.java

@@ -0,0 +1,171 @@
+package com.css.simulation.resource.video.util;
+
+import io.minio.*;
+import io.minio.errors.*;
+import io.minio.http.Method;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+public class MinioUtil {
+
+    /**
+     * 判断 bucket 是否存在
+     */
+    public static boolean isBucketExist(MinioClient minioClient, String bucketName) throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException, io.minio.errors.InternalException {
+        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
+    }
+
+    /**
+     * 判断 object 是否存在
+     */
+    public static boolean isObjectExist(MinioClient minioClient, String bucket, String object) {
+        try {
+            StatObjectResponse objectStat = minioClient.statObject(StatObjectArgs.builder().bucket(bucket).object(object).build());
+            return objectStat != null;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+    /**
+     * 创建 bucket
+     */
+    public static void createBucket(MinioClient minioClient, String bucketName) throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException, io.minio.errors.InternalException {
+        if (!isBucketExist(minioClient, bucketName)) {
+            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
+        }
+    }
+
+    /**
+     * 获取预览路径
+     *
+     * @return 预览路径
+     */
+    public static String getPreviewUrl(MinioClient minioClient, Method method, String bucket, String object) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, io.minio.errors.InternalException {
+        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
+                .method(method)
+                .bucket(bucket)
+                .object(object)
+                .build());
+    }
+
+    /**
+     * 通过文件路径上传文件上传文件
+     */
+    public static void uploadFromFile(
+            MinioClient minioClient,
+            String sourceFilePath,
+            String bucketName,
+            String objectName
+    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException, io.minio.errors.InternalException {
+        minioClient.uploadObject(UploadObjectArgs.builder()
+                .filename(sourceFilePath)
+                .bucket(bucketName)
+                .object(objectName)
+                .build());
+    }
+
+    /**
+     * 通过文件路径上传文件上传文件
+     *
+     * @param partSize 分片最小 5MB
+     */
+    public static void uploadFromStream(
+            MinioClient minioClient,
+            InputStream inputStream,
+            long objectSize,
+            long partSize,
+            String bucketName,
+            String objectName
+    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException, io.minio.errors.InternalException {
+        minioClient.putObject(PutObjectArgs.builder()
+                .stream(inputStream, objectSize, partSize)
+                .bucket(bucketName)
+                .object(objectName)
+                .build());
+    }
+
+    /**
+     * 通过文件路径上传文件上传文件
+     */
+    public static void uploadFromMultipartFile(
+            MinioClient minioClient,
+            MultipartFile multipartFile,
+            String bucketName,
+            String objectName
+    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException, io.minio.errors.InternalException {
+        InputStream inputStream = multipartFile.getInputStream();
+        long objectSize = multipartFile.getSize();
+//        long partSize = 5 * 1024 * 1024L; // 分片最小 5M
+        long partSize = -1; // 不分片
+        minioClient.putObject(PutObjectArgs.builder()
+                .stream(inputStream, objectSize, partSize)
+                .bucket(bucketName)
+                .object(objectName)
+                .build());
+    }
+
+
+
+    /**
+     * 下载文件
+     */
+    public static void downloadToFile(
+            MinioClient minioClient,
+            String bucketName,
+            String objectName,
+            String targetFilePath
+    ) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, io.minio.errors.InternalException {
+
+        File file = new File(targetFilePath);
+        if (!file.getParentFile().exists()) {
+            boolean mkdir = file.getParentFile().mkdirs();
+        }
+        minioClient.downloadObject(DownloadObjectArgs.builder()
+                .bucket(bucketName)
+                .object(objectName)
+                .filename(targetFilePath)
+                .build());
+    }
+    /**
+     * 下载文件
+     */
+    public static InputStream downloadToStream(
+            MinioClient minioClient,
+            String bucketName,
+            String objectName
+    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException, io.minio.errors.InternalException {
+        return minioClient.getObject(GetObjectArgs.builder()
+                .bucket(bucketName)
+                .object(objectName)
+                .build());
+    }
+
+    /**
+     * 读取文件内容
+     */
+    public static String downloadToString(
+            MinioClient minioClient,
+            String bucketName,
+            String objectName
+    ) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, io.minio.errors.InternalException {
+        GetObjectResponse response = minioClient.getObject(GetObjectArgs.builder()
+                .bucket(bucketName)
+                .object(objectName)
+                .build());
+        StringBuilder result = new StringBuilder();
+        byte[] buf = new byte[4096];//创建字节数组,存储临时读取的数据
+        int len;//记录数据读取的长度
+        //循环读取数据
+        while ((len = response.read(buf)) != -1) { //长度为-1则读取完毕
+            result.append(new String(buf, 0, len)).append("\n");
+        }
+        response.close();
+        return result.toString();
+    }
+}

+ 10 - 0
simulation-resource-video/src/main/resources/bootstrap-aliyun.yaml

@@ -0,0 +1,10 @@
+spring:
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 47.94.105.148:8848
+        namespace: 3698bfc2-a612-487a-b2a2-aaad16cd9d9d
+      config:
+        server-addr: 47.94.105.148:8848
+        namespace: 3698bfc2-a612-487a-b2a2-aaad16cd9d9d
+        file-extension: yaml

+ 10 - 0
simulation-resource-video/src/main/resources/bootstrap-dev.yaml

@@ -0,0 +1,10 @@
+spring:
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 47.93.135.21:8848
+        namespace: 3698bfc2-a612-487a-b2a2-aaad16cd9d9d
+      config:
+        server-addr: 47.93.135.21:8848
+        namespace: 3698bfc2-a612-487a-b2a2-aaad16cd9d9d
+        file-extension: yaml

+ 10 - 0
simulation-resource-video/src/main/resources/bootstrap-test.yaml

@@ -0,0 +1,10 @@
+spring:
+  cloud:
+    nacos:
+      discovery:
+        server-addr: 47.93.135.21:8848
+        namespace: 3698bfc2-a612-487a-b2a2-aaad16cd9d9d
+      config:
+        server-addr: 47.93.135.21:8848
+        namespace: 3698bfc2-a612-487a-b2a2-aaad16cd9d9d
+        file-extension: yaml

+ 9 - 0
simulation-resource-video/src/main/resources/bootstrap.yaml

@@ -0,0 +1,9 @@
+spring:
+  servlet:
+    multipart:
+      max-file-size: 10GB
+      max-request-size: 10GB
+  application:
+    name: simulation-resource-video
+  profiles:
+    active: dev

+ 119 - 0
simulation-resource-video/src/main/resources/logback-spring.xml

@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="true">
+    <!-- 项目名称 -->
+    <property name="PROJECT_NAME" value="changjingyun"/>
+
+    <!--定义不同环境的日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
+    <springProfile name="aliyun">
+        <property name="LOG_HOME" value="/opt/simulation-cloud/simulation-resource-video/log"/>
+    </springProfile>
+
+    <!--输出到控制台-->
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>debug</level>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+    </appender>
+
+    <!--输出到debug-->
+    <appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!--日志文件输出的文件名-->
+            <FileNamePattern>${LOG_HOME}/debug.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <!--日志文件保留天数-->
+            <MaxHistory>15</MaxHistory>
+            <!--日志文件最大的大小-->
+            <MaxFileSize>10MB</MaxFileSize>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印DEBUG日志 -->
+            <level>DEBUG</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--输出到info-->
+    <appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!--日志文件输出的文件名-->
+            <FileNamePattern>${LOG_HOME}/info.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <!--日志文件保留天数-->
+            <MaxHistory>15</MaxHistory>
+            <!--日志文件最大的大小-->
+            <MaxFileSize>10MB</MaxFileSize>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印INFO日志 -->
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--输出到error-->
+    <appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!--日志文件输出的文件名-->
+            <FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <!--日志文件保留天数-->
+            <MaxHistory>15</MaxHistory>
+            <!--日志文件最大的大小-->
+            <MaxFileSize>10MB</MaxFileSize>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印ERROR日志 -->
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--输出到warn-->
+    <appender name="warn" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!--日志文件输出的文件名-->
+            <FileNamePattern>${LOG_HOME}/warn.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <!--日志文件保留天数-->
+            <MaxHistory>15</MaxHistory>
+            <!--日志文件最大的大小-->
+            <MaxFileSize>10MB</MaxFileSize>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印WARN日志 -->
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--分别设置对应的日志输出节点 -->
+    <root level="info">
+        <appender-ref ref="console"/>
+        <appender-ref ref="debug"/>
+        <appender-ref ref="info"/>
+        <appender-ref ref="error"/>
+        <appender-ref ref="warn"/>
+    </root>
+</configuration>