Преглед изворни кода

feat: 添加仿真测试记录

HeWang пре 8 месеци
родитељ
комит
ea40f6ca84
4 измењених фајлова са 412 додато и 280 уклоњено
  1. 205 167
      electron/main.js
  2. 17 29
      electron/preload.js
  3. 143 55
      src/views/AboutView.vue
  4. 47 29
      src/views/ReportView.vue

+ 205 - 167
electron/main.js

@@ -38,25 +38,10 @@ app.on('window-all-closed', () => {
     }
 });
 
-app.on('ready', () => {
-    // 异步
-    ipcMain.on('run-simulation', (event, {obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
-        const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + obstacle_flag + ' ' + default_start_flag + ' ' + default_end_flag +
-            ' ' + start_point + ' ' + end_point;
-        console.log('command:', command);
-        exec(command, (error, stdout, stderr) => {
-            if (error) {
-                console.error(`exec error: ${error}`);
-                event.reply('run-simulation-response', { success: false, message: error.message });
-                return;
-            } else {
-                console.log(`stdout: ${stdout}`);
-                console.error(`stderr: ${stderr}`);
-                event.reply('run-simulation-response', { success: true, message: 'Run simulation successfully' });
-            }
-        });
-    });
-})
+// app.on('ready', () => {
+//     // 异步
+//
+// })
 
 app.on('activate', () => {
     if (BrowserWindow.getAllWindows().length === 0) {
@@ -66,63 +51,144 @@ app.on('activate', () => {
 
 
 // ------------- 进程通信 -------------
-ipcMain.on('open-gazebo', (event, arg) => {
-    console.log('Received open-gazebo event');
-    exec('gazebo', (error, stdout, stderr) => {
-        if (error) {
-            console.error(`exec error: ${error}`);
-            return;
-        }
-        console.log(`stdout: ${stdout}`);
-        console.error(`stderr: ${stderr}`);
-    });
+ipcMain.handle('dialog:open', async (event, options = {}) => {
+    const result = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0], options);
+    return result.canceled ? null : result.filePaths;
 });
 
 
-ipcMain.on('open-rviz', (event, arg) => {
-    console.log('Received open-rviz event');
-    exec('rviz', (error, stdout, stderr) => {
-        if (error) {
-            console.error(`exec error: ${error}`);
-            return;
+// 下载文件
+ipcMain.handle('download-file', async (event, { url, fileName, savePath, overwriteFlag }) => {
+    try {
+        // console.log("url", url)
+        // console.log("response", response)
+
+        const filePath = path.join(savePath, fileName);
+
+        // 查找文件是否存在
+        const fileExists = fs.existsSync(filePath)
+        if (fileExists && !overwriteFlag) { // 已存在且不覆盖
+            return {success: true, filePath}
+        } else { // 不存在
+            const response = await axios.get(url, {responseType: 'stream'});
+            // 写入文件
+            const writer = fs.createWriteStream(filePath);
+
+            return new Promise((resolve, reject) => {
+                response.data.pipe(writer);
+
+                let error = null;
+                writer.on('error', err => {
+                    error = err;
+                    writer.close();
+                    reject(err);
+                });
+
+                writer.on('close', () => {
+                    if (!error) {
+                        resolve({success: true, filePath}); // 返回成功信息
+                    } else {
+                        reject({success: false, error: error.message}); // 返回错误信息
+                    }
+                });
+            });
         }
-        console.log(`stdout: ${stdout}`);
-        console.error(`stderr: ${stderr}`);
-    });
+    } catch (error) {
+        // console.error('下载文件出错:', error);
+        throw error;
+    }
 });
 
-ipcMain.on('close-gazebo', (event, arg) => {
-    console.log('Received close-gazebo event');
-    exec('pkill -f gzserver & pkill -f gzclient', (error, stdout, stderr) => {
-        if (error) {
-            console.error(`exec error: ${error}`);
-            return;
-        }
-        console.log(`stdout: ${stdout}`);
-        console.error(`stderr: ${stderr}`);
-    });
+// 删除文件
+ipcMain.on('delete-file', (event, {fileName, savePath}) => {
+    const filePath = path.join(savePath, fileName);
+    // 检查文件是否存在
+    if (fs.existsSync(filePath)) {
+        // 文件存在,尝试删除
+        fs.unlink(filePath, (err) => {
+            if (err) {
+                event.reply('delete-file-response', { success: false, message: err.message });
+            } else {
+                event.reply('delete-file-response', { success: true, message: 'File deleted successfully' });
+            }
+        });
+    } else {
+        // 文件不存在
+        event.reply('delete-file-response', { success: false, message: 'File does not exist' });
+    }
 });
 
+// 上传文件
+ipcMain.on('upload-file', async (event, {filePath, uploadUrl}) => {
+    if (!fs.existsSync(filePath)) {
+        console.log("File does not exist");
+        event.reply('upload-file-response', {success: false, message: 'File does not exist'});
+        return;
+    }
+    // 读取文件
+    // fs.readFile(filePath, async (err, data) => {
+    //     if(err) {
+    //         console.log("Error reading file:", err);
+    //         event.reply('upload-file-response', { success: false, message: err.message });
+    //         return;
+    //     }
+    //
+    //     // 创建formData对象
+    //     const formData = new FormData();
+    //     formData.append('file', new Blob([data]), path.basename(filePath));
+    //
+    //     try {
+    //         // 使用axios上传文件
+    //         const response = await axios.post(uploadUrl, formData, {
+    //             headers: {
+    //                 'Content-Type': 'multipart/form-data'
+    //             }
+    //         })
+    //         console.log('response', response)
+    //         event.reply('upload-file-response', { success: true, message: 'File uploaded successfully' });
+    //     } catch (err) {
+    //         console.log("Error uploading file:", err);
+    //         event.reply('upload-file-response', { success: false, message: err.message });
+    //     }
+    // })
 
-ipcMain.on('close-rviz', (event, arg) => {
-    console.log('Received close-rviz event');
-    exec('pkill -f rviz', (error, stdout, stderr) => {
-        if (error) {
-            console.error(`exec error: ${error}`);
-            return;
+    try {
+        const result = await uploadFile(filePath, uploadUrl);
+        console.log("result", result);
+        if (result.status) {
+            event.reply('upload-file-response', { success: true, message: 'File uploaded successfully', details: result.details });
+        } else {
+            event.reply('upload-file-response', { success: false, message: 'Error uploading file', details: '' });
         }
-        console.log(`stdout: ${stdout}`);
-        console.error(`stderr: ${stderr}`);
-    });
+    } catch (err) {
+        event.reply('upload-file-response', { success: false, message: err.message, details: '' });
+    }
 });
 
+async function uploadFile(filePath, uploadUrl) {
 
-ipcMain.handle('dialog:open', async (event, options = {}) => {
-    const result = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0], options);
-    return result.canceled ? null : result.filePaths;
-});
+    const data = fs.readFileSync(filePath)
+    // 创建formData对象
+    const formData = new FormData();
+    formData.append('file', new Blob([data]), path.basename(filePath));
+
+    try {
+        // 使用axios上传文件
+        const response = await axios.post(uploadUrl, formData, {
+            headers: {
+                'Content-Type': 'multipart/form-data'
+            }
+        })
+        // console.log('response', response.data)
 
+        return response.data
+    } catch (err) {
+        console.log("Error uploading file:", err);
+        return { status: false, code: '', message: '上传stl文件失败', details: '' };
+    }
+}
 
+// 导入算法镜像
 ipcMain.on('docker-import', (event, filePath, tag) => {
     const command = 'bash /home/cicv/work/pji_desktop/docker_import/run_docker_import.sh ' + filePath + ' pji_nav ' + tag
     console.log('Docker import command:', command);
@@ -139,7 +205,7 @@ ipcMain.on('docker-import', (event, filePath, tag) => {
     });
 });
 
-
+// 生成world
 ipcMain.on('generate-world', (event, {rosbag_path}) => {
     const command = 'bash /home/cicv/work/pji_desktop/simulation/generate_world.sh ' + rosbag_path
     console.log('World generation command:', command);
@@ -155,7 +221,7 @@ ipcMain.on('generate-world', (event, {rosbag_path}) => {
     });
 });
 
-
+// 启动仿真环境
 ipcMain.on('start-container', (event, {zip_file_path, image_name, container_name}) => {
 
     // 使用 spawn 启动脚本
@@ -203,115 +269,87 @@ ipcMain.on('start-container', (event, {zip_file_path, image_name, container_name
     }
 });
 
-// 同步
-// ipcMain.handle('run-simulation', (event, {obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
-//     const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + obstacle_flag + ' ' + default_start_flag + ' ' + default_end_flag +
-//         ' ' + start_point + ' ' + end_point;
-//     console.log('command:', command);
-//
-//     // 同步
-//     try {
-//         const  output = execSync(command)
-//         console.log("output", output.toString());
-//         return { success: false, message: error.message }
-//     } catch (error) {
-//         console.error(`exec error: ${error}`);
-//         return { success: true, message: 'Run simulation successfully' }
-//     }
-// });
-
-ipcMain.handle('download-file', async (event, { url, fileName, savePath, overwriteFlag }) => {
-    try {
-        // console.log("url", url)
-        // console.log("response", response)
-
-        const filePath = path.join(savePath, fileName);
-
-        // 查找文件是否存在
-        const fileExists = fs.existsSync(filePath)
-        if (fileExists && !overwriteFlag) { // 已存在且不覆盖
-            return {success: true, filePath}
-        } else { // 不存在
-            const response = await axios.get(url, {responseType: 'stream'});
-            // 写入文件
-            const writer = fs.createWriteStream(filePath);
-
-            return new Promise((resolve, reject) => {
-                response.data.pipe(writer);
-
-                let error = null;
-                writer.on('error', err => {
-                    error = err;
-                    writer.close();
-                    reject(err);
-                });
-
-                writer.on('close', () => {
-                    if (!error) {
-                        resolve({success: true, filePath}); // 返回成功信息
-                    } else {
-                        reject({success: false, error: error.message}); // 返回错误信息
-                    }
-                });
-            });
-        }
-    } catch (error) {
-        // console.error('下载文件出错:', error);
-        throw error;
-    }
-});
-
-
-// 删除文件
-ipcMain.on('delete-file', (event, {fileName, savePath}) => {
-    const filePath = path.join(savePath, fileName);
-    // 检查文件是否存在
-    if (fs.existsSync(filePath)) {
-        // 文件存在,尝试删除
-        fs.unlink(filePath, (err) => {
-            if (err) {
-                event.reply('delete-file-response', { success: false, message: err.message });
+// 执行仿真
+ipcMain.on('run-simulation', (event, {random_flag, count, obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
+    const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + random_flag + ' ' + count + ' ' + obstacle_flag + ' ' +
+        default_start_flag + ' ' + default_end_flag + ' ' + start_point + ' ' + end_point;
+    console.log('command:', command);
+    exec(command, (error, stdout, stderr) => {
+        if (error) {
+            console.error(`exec error: ${error}`);
+            event.reply('run-simulation-response', { success: false, message: error.message });
+        } else {
+            console.log(`stdout: ${stdout}`);
+            console.error(`stderr: ${stderr}`);
+            if (stdout.toString().includes('Evaluation finished')) { // 判断结束标志
+                event.reply('run-simulation-response', { success: true, message: 'Run simulation successfully' });
             } else {
-                event.reply('delete-file-response', { success: true, message: 'File deleted successfully' });
+                event.reply('run-simulation-response', { success: false, message: 'Error running simulation' });
             }
-        });
-    } else {
-        // 文件不存在
-        event.reply('delete-file-response', { success: false, message: 'File does not exist' });
-    }
-});
-
-// 上传文件
-ipcMain.on('upload-file', (event, {filePath, uploadUrl}) => {
-    if (!fs.existsSync(filePath)) {
-        console.log("File does not exist");
-        event.reply('upload-file-response', { success: false, message: 'File does not exist' });
-        return;
-    }
-    // 读取文件
-    fs.readFile(filePath, async (err, data) => {
-        if(err) {
-            console.log("Error reading file:", err);
-            event.reply('upload-file-response', { success: false, message: err.message });
-            return;
         }
+    });
+});
 
-        // 创建formData对象
-        const formData = new FormData();
-        formData.append('file', new Blob([data]), path.basename(filePath));
+// 读取并上传算法评价结果
+ipcMain.handle('process-evaluation-files', async (event, {N, equipmentNo, sceneNo}) => {
+    const result = []
+    const evalDir = "/home/cicv/work/pji_desktop/simulation/data/evaluation";
+    const bagDir = "/home/cicv/work/pji_desktop/simulation/data/record_bag";
+    console.log('N', N)
+    // 记录时间戳
+    const timeStamp = Date.now()
+    console.log("timeStamp", timeStamp);
+
+    // 遍历评价结果文件夹,读取并上传相关数据
+    for (let i = 1; i <= N; i++) {
+        const subDir = path.join(evalDir, `${i}/result`);
+        console.log("subDir", subDir);
+        // 文件路径 - report.json
+        const jsonFilePath = path.join(subDir, "report.json");
+        console.log("jsonFilePath", jsonFilePath);
+        // 文件路径 - report.pdf
+        const pdfFilePath = path.join(subDir, "report.pdf");
+        console.log("pdfFilePath", pdfFilePath);
+        // 文件路径 - test-${i}.bag
+        const bagFilePath = path.join(bagDir, `test-${i}.bag`);
+        console.log("bagFilePath", bagFilePath);
+
+        const uploadPdfUrl = "http://127.0.0.1:8888/simulation/upload/pdf?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo +
+            "&timeStamp=" + timeStamp + "&round=" + i;
+
+        const uploadBagUrl = "http://127.0.0.1:8888/simulation/upload/bag?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo +
+            "&timeStamp=" + timeStamp + "&round=" + i;
 
         try {
-            // 使用axios上传文件
-            const response = await axios.post(uploadUrl, formData, {
-                headers: {
-                    'Content-Type': 'multipart/form-data'
-                }
+            // 读取json文件
+            const jsonData = fs.readFileSync(jsonFilePath, "utf-8");
+            const parsedData = JSON.parse(jsonData);
+            console.log("parsedData", parsedData);
+            // 上传pdf文件
+            const uploadPdfResult = await uploadFile(pdfFilePath, uploadPdfUrl);
+            console.log("uploadPdfResult", uploadPdfResult);
+            if (!uploadPdfResult.status) { break}
+            // 上传bag文件
+            const uploadBagResult = await uploadFile(bagFilePath, uploadBagUrl);
+            console.log("uploadBagUrl", uploadBagUrl);
+            if (!uploadBagResult.status) { break}
+            // 整合结果
+            result.push({
+                "round": i,
+                "jsonData": parsedData,
+                "uploadPdfKey": uploadPdfResult.details,
+                "uploadBagKey": uploadBagResult.details,
+                "timeStamp": timeStamp
             })
-            console.log('response', response)
-            event.reply('upload-file-response', { success: true, message: 'File uploaded successfully' });
-        } catch (err) {
-            console.log("Error uploading file:", err);
-            event.reply('upload-file-response', { success: false, message: err.message });
+        } catch (error) {
+            console.log("error", error);
+            return { success: false, message: "Error uploading evaluation results", data: [] }
         }
-    })
-});
+    }
+    // 判断数据上传是否完整
+    if (result.length === N) {
+        return { success: true, message: "Evaluation results uploaded successfully", data: result }
+    } else {
+        return { success: false, message: "Error uploading evaluation results", data: [] }
+    }
+})

+ 17 - 29
electron/preload.js

@@ -2,22 +2,7 @@ console.log('Preload script loaded');
 const {contextBridge, ipcRenderer} = require('electron');
 
 contextBridge.exposeInMainWorld('electronAPI', {
-    openGazebo: () => {
-        console.log('打开 gazebo');
-        ipcRenderer.send('open-gazebo');
-    },
-    openRviz: () => {
-        console.log('打开 rviz');
-        ipcRenderer.send('open-rviz');
-    },
-    closeGazebo: () => {
-        console.log('关闭 gazebo');
-        ipcRenderer.send('close-gazebo');
-    },
-    closeRviz: () => {
-        console.log('关闭 rviz');
-        ipcRenderer.send('close-rviz');
-    },
+
     openFileManager: async () => {
         try {
             const result = await ipcRenderer.invoke('dialog:open');
@@ -27,6 +12,18 @@ contextBridge.exposeInMainWorld('electronAPI', {
             throw error;
         }
     },
+    downloadFile: async (url, fileName, savePath, overwriteFlag) => {
+        try {
+            return await ipcRenderer.invoke('download-file', { url, fileName, savePath, overwriteFlag });
+        } catch (error) {
+            console.error('下载文件时出错:', error);
+            return { success: false, error: error.message };
+        }
+    },
+    deleteFile: (fileName, savePath) => ipcRenderer.send('delete-file', {fileName, savePath}),
+    onDeleteFileResponse: (callback) => ipcRenderer.once('delete-file-response', callback),
+    uploadFile: async (filePath, uploadUrl) => ipcRenderer.send('upload-file', {filePath, uploadUrl}),
+    onUploadFileResponse: (callback) => ipcRenderer.once('upload-file-response', callback),
     // dockerImport 函数现在接受两个参数:filePath 和 tag
     dockerImport: (filePath, tag) => {
         console.log('docker导入算法镜像文件:', filePath, '本地镜像版本:', tag);
@@ -34,9 +31,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
         ipcRenderer.send('docker-import', filePath, tag);
     },
     onDockerImportResponse: (callback) => ipcRenderer.once('docker-import-response', callback),
-    runSimulation: async (obstacle_flag, default_start_flag, default_end_flag, start_point, end_point) => {
+    runSimulation: async (random_flag, count, obstacle_flag, default_start_flag, default_end_flag, start_point, end_point) => {
         // 异步
-        ipcRenderer.send('run-simulation', {obstacle_flag, default_start_flag, default_end_flag, start_point, end_point});
+        ipcRenderer.send('run-simulation', {random_flag, count, obstacle_flag, default_start_flag, default_end_flag, start_point, end_point});
 
         // 同步
         // try {
@@ -58,18 +55,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
         ipcRenderer.send('start-container', {zip_file_path, image_name, container_name});
     },
     onStartContainerResponse: (callback) =>  ipcRenderer.once('start-container-response', callback),
-    downloadFile: async (url, fileName, savePath, overwriteFlag) => {
-        try {
-            return await ipcRenderer.invoke('download-file', { url, fileName, savePath, overwriteFlag });
-        } catch (error) {
-            console.error('下载文件时出错:', error);
-            return { success: false, error: error.message };
-        }
+    processEvaluationFiles: async (N, equipmentNo, sceneNo) => {
+        return await ipcRenderer.invoke('process-evaluation-files', {N, equipmentNo, sceneNo});
     },
-    deleteFile: (fileName, savePath) => ipcRenderer.send('delete-file', {fileName, savePath}),
-    onDeleteFileResponse: (callback) => ipcRenderer.once('delete-file-response', callback),
-    uploadFile: (filePath, uploadUrl) => ipcRenderer.send('upload-file', {filePath, uploadUrl}),
-    onUploadFileResponse: (callback) => ipcRenderer.once('upload-file-response', callback),
 });
 
 

+ 143 - 55
src/views/AboutView.vue

@@ -4,6 +4,44 @@
     <!--      <el-button type="primary" @click="onSubmit">默认终点</el-button>-->
     <!--      <el-button type="primary" @click="onSubmit">自定义终点</el-button>-->
     <!--    </el-form-item>-->
+    <el-dialog v-model="evalTableVisible" title="算法评价结果" width="70%">
+      <div class="topbar">
+        <el-form style=" background-color: rgba(0,0,0,0%);" :inline="true" :model="queryLine"
+                 class="demo-form-inline">
+
+          <el-form-item label="算法评价等级">
+            <el-input v-model="queryLine.algorithmLevel" placeholder="请输入算法评价等级" clearable/>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="danger" @click="onSubmit">查询</el-button>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="danger" @click="onSubmit">重置</el-button>
+          </el-form-item>
+          <div style="float: right">
+            <el-form-item>
+              <el-button type="danger" @click="onSubmit">下载测试bag</el-button>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="danger" @click="onSubmit">下载算法评价报告</el-button>
+            </el-form-item>
+          </div>
+
+        </el-form>
+
+      </div>
+      <el-table stripe style="background-color: rgba(255,0,0,99%);width: 100%" border :data="evalData"
+                fixed ref="multipleTableRef" :cell-style="{ textAlign: 'center'}" :header-cell-style="{ textAlign: 'center'}">
+        <el-table-column type="selection" width="55"/>
+        <el-table-column prop="testTime" label="测试时间"/>
+        <el-table-column prop="testBag" label="测试bag"/>
+        <el-table-column prop="testDuration" label="测试时长"/>
+        <el-table-column prop="testDistance" label="测试里程"/>
+        <el-table-column prop="algorithmLevel" label="算法评价等级"/>
+        <el-table-column prop="algorithmReport" label="算法评价报告"/>
+      </el-table>
+
+    </el-dialog>
     <el-form-item label="随机起终点     :">
       <el-switch
           v-model="form.isRandom"
@@ -40,6 +78,7 @@
         <el-radio :value="false">否</el-radio>
       </el-radio-group>
     </el-form-item>
+
     <el-form-item label="            ">
       <!--      <el-button type="primary" @click="goToMain">开始执行</el-button>-->
       <el-button type="primary" @click="runSimulation">开始执行</el-button>
@@ -112,11 +151,11 @@
       </el-dialog>
 
 <!--      <el-button style="margin-left: 10px;" type="primary" @click="goToMain">更换设置并执行</el-button>-->
-<!--      <el-button style="margin-left: 10px;" type="primary" @click="goToAlgorithmEval">算法评价</el-button>-->
-    </el-form-item>
-    <el-form-item label="            ">
-      <el-button type="primary" @click="goToAlgorithmEval">算法评价</el-button>
+      <el-button style="margin-left: 10px;" type="primary" @click="evalTableVisible = true" >算法评价结果</el-button>
     </el-form-item>
+<!--    <el-form-item label="            ">-->
+<!--      <el-button type="primary" @click="goToAlgorithmEval">算法评价结果</el-button>-->
+<!--    </el-form-item>-->
     <el-form-item label="            ">
       <el-button type="primary" @click="goToMain">返回</el-button>
     </el-form-item>
@@ -125,16 +164,44 @@
 </template>
 
 <script lang="ts" setup>
-import {useRouter} from 'vue-router'; // 导入 Vue Router 的 useRouter 钩子
+import {useRoute, useRouter} from 'vue-router'; // 导入 Vue Router 的 useRouter 钩子
 import {reactive} from 'vue'
 import {ref} from 'vue'
 import {ElMessageBox} from 'element-plus'
 import {ElTable, ElLoading, ElMessage} from "element-plus";
 import {getCurrentInstance} from "vue";
+import axios from "axios";
 
 const startDialogVisible = ref(false)
 const endDialogVisible = ref(false)
 const {proxy} = getCurrentInstance();
+const evalResult = ref([])
+const evalTableVisible = ref(true)
+const evalData = ref([])
+const queryLine = reactive({
+  algorithmLevel: ''
+})
+const onSubmit = () => {
+  page()
+}
+
+const router = useRouter();
+const route = useRoute();
+console.log("route.query", route.query)
+const sceneId = route.query.scene_id;
+const deviceType = route.query.device_type;
+const deviceId = route.query.device_id;
+const deviceNo = route.query.device_no;
+const deviceName = route.query.device_name;
+const algoImageName = route.query.algo_image_name;
+const worldPath = route.query.world_path;
+console.log("sceneId", sceneId)
+console.log("deviceType", deviceType)
+console.log("deviceId", deviceId)
+console.log("deviceNo", deviceNo)
+console.log("deviceName", deviceName)
+console.log("algoImageName", algoImageName)
+console.log("worldPath", worldPath)
 
 const handleClose = (done: () => void) => {
   done()
@@ -150,7 +217,7 @@ const form = reactive({
   type: [],
   resource: true,
   desc: '',
-  isRandom: false,
+  isRandom: true,
   origin: true,
   destination: true,
   randomCount: ref(1),
@@ -174,26 +241,6 @@ const endForm = reactive({
   H: 0,
 })
 
-function validateInput(value) {
-
-  let temp = value.toString();
-  temp = temp.replace(/。/g, ".");
-  temp = temp.replace(/[^\d.]/g, "");
-  temp = temp.replace(/^\./g, "");
-  temp = temp.replace(/\.{2,}/g, "");
-  temp = temp.replace(".", "$#$").replace(/\./g, "").replace("$#$", ".");
-  temp = temp.replace(/^(\-)*(\d+)\.(\d\d).*$/, "$1$2.$3");
-  inputForm.value.perfSum = temp
-}
-
-const formatToTwoDecimalPlaces = (value) => {
-  return parseFloat(value).toFixed(2); // 格式化为两位小数
-};
-
-const parseToNumber = (value) => {
-  return parseFloat(value); // 将输入值解析为数字
-};
-
 const originChange = (value: string) => {
   if (!value) {
     startDialogVisible.value = true
@@ -206,46 +253,89 @@ const destinationChange = (value: string) => {
   }
 }
 
-const onSubmit = () => {
-  console.log('submit!')
-}
-const router = useRouter();
-
 const runSimulation = () => {
   // 是否加载默认障碍物
+  let random_flag = form.isRandom
+  let count = form.randomCount
   let obstacle_flag = form.resource
   let default_start_flag = form.origin
   let default_end_flag = form.destination
   let start_point = form.origin ? '"0 0 0 0 0 0"' : '"' +  startForm.X + ' ' + startForm.Y  + ' ' + startForm.Z + ' ' + startForm.R + ' ' + startForm.P + ' ' + startForm.H + '"'
   let end_point = form.destination ? '"0 0 0 0 0 0"' : '"' + endForm.X + ' ' + endForm.Y  + ' ' + endForm.Z + ' ' + endForm.R + ' ' + endForm.P + ' ' + endForm.H + '"'
+  let N = random_flag ? count : 1
 
   console.log("end_point=" + end_point)
-  // 开启仿真测试
-  // 同步
-  // const result = await window.electronAPI.runSimulation(obstacle_flag, default_start_flag, default_end_flag, start_point, end_point)
-  // if (result.success) { // 脚本执行成功
-  //   console.log('Script execution completed successfully.')
-  //   // ElMessage.success("仿真测试成功!");
-  //   ElMessage.closeAll()
-  //   proxy.$message.success("仿真测试成功!");
-  // } else { // 脚本执行过程中发生错误
-  //   console.error('Script execution failed.');
-  //   proxy.$message.error("仿真测试错误!");
-  // }
+  console.log("count", count)
 
   // 异步
-  window.electronAPI.runSimulation(obstacle_flag, default_start_flag, default_end_flag, start_point, end_point)
+  window.electronAPI.runSimulation(random_flag, count, obstacle_flag, default_start_flag, default_end_flag, start_point, end_point)
+  evalResult.value = []
   // 监听脚本执行状态
-  window.electronAPI.onRunSimulationResponse( (event, result) => {
-    if (result.success) { // 脚本执行成功
-      console.log('Script execution completed successfully.')
-      // ElMessage.success("仿真测试成功!");
-      // 清除历史窗口
-      ElMessage.closeAll()
-      proxy.$message.success("仿真测试成功!");
-    } else { // 脚本执行过程中发生错误
+  window.electronAPI.onRunSimulationResponse( async (event, result) => {
+    if (!result.success) { // 脚本执行过程中发生错误
       console.error('Script execution failed.');
-      proxy.$message.error("仿真测试错误!");
+      proxy.$message.error("仿真测试发生错误!");
+    } else { // 脚本执行成功
+      console.log('Script execution completed successfully.')
+
+      // 上传并收集算法评价结果
+      const response = await window.electronAPI.processEvaluationFiles(N, deviceNo, sceneId)
+      if (!response.success) { // 算法评价结果收集失败
+        console.log("Error collecting evaluation results")
+        return
+      }
+      console.log("response.data", response.data)
+      evalResult.value = response.data
+      // 上传pgm文件并获取存储url
+      // 执行脚本 - 上传文件
+      const filePath = "/home/cicv/work/pji_desktop/simulation/data/mapBuf/map.pgm"
+      const pgmUploadUrl = "http://127.0.0.1:8888/simulation/upload/pgm?equipmentNo=" + deviceNo + "&sceneNo=" + sceneId
+      window.electronAPI.uploadFile(filePath, pgmUploadUrl);
+      // 监听脚本执行状态
+      window.electronAPI.onUploadFileResponse( async (event, result) => {
+        if (result.success) { // 脚本执行成功
+          console.log('File uploaded successfully.')
+          // 上传路径 - map.pgm
+          const uploadPgmKey = result.details
+          console.log('uploadPgmKey', uploadPgmKey)
+          let simulation_data = []
+          evalResult.value.forEach((d, i) => {
+            simulation_data.push({
+              'device_id': deviceId,
+              'device_name': deviceName,
+              'device_type': deviceType,
+              'algo_image_name': algoImageName,
+              'pgm_path': uploadPgmKey,
+              'world_path': worldPath,
+              'test_bag_path': d["uploadBagKey"],
+              'test_duration': d["jsonData"]["testDuration"],
+              'test_distance': d["jsonData"]["testMileage"],
+              'algo_evaluation_level': d["jsonData"]["algorithmLevel"],
+              'algo_evaluation_report': d["uploadPdfKey"],
+              'test_time': String(d["timeStamp"]),
+              'round': d["round"],
+              'scene_id': sceneId,
+              'device_no': deviceNo,
+            })
+          })
+          console.log("simulation_data", simulation_data)
+          // 上传数据
+          // 检查world文件是否存在
+          let response = await axios.post('/local/simulation/add/record', simulation_data)
+
+          if (!response.data.status){ // 不存在
+            proxy.$message.error("仿真测试发生错误!");
+            return
+          }
+          // ElMessage.success("仿真测试成功!");
+          // 清除历史窗口
+          ElMessage.closeAll()
+          proxy.$message.success("仿真测试执行成功!");
+        } else { // 脚本执行失败
+          console.error('Uploading file failed.');
+          proxy.$message.error("仿真测试发生错误!");
+        }
+      })
     }
   })
 }
@@ -258,6 +348,4 @@ const goToMain = () => {
 const goToAlgorithmEval = () => {
   router.push('/algorithm_eval')
 }
-
-
 </script>

+ 47 - 29
src/views/ReportView.vue

@@ -262,9 +262,7 @@ const update_active = ref(0)
 const world_active = ref(0)
 const simulation_active = ref(0)
 const router = useRouter();
-const algorithmContainerState = ref(false)
-const gazeboState = ref(false)
-const rvizState = ref(false)
+
 const multipleTableRef = ref<InstanceType<typeof ElTable>>
 
 const algorithmDialogVisible = ref(false)
@@ -279,6 +277,39 @@ const stlUploadUrl = ref<string>()
 
 const selectImageName = ref("")
 const imageNames = ref([])
+const currentRow = ref()
+const worldUrl = ref("")
+
+const goToMapUpdate = () => {
+  router.push('/map_update')
+}
+
+const goToUpdateStat = () => {
+  router.push('/update_stat')
+}
+
+const goToDetail = () => {
+  router.push({
+    name: 'about',
+    query: {
+      "scene_id": currentRow.value.id,
+      "device_type": currentRow.value.equipmentTypeName,
+      "device_id": currentRow.value.equipmentId,
+      "device_no":  currentRow.value.equipmentNo,
+      "device_name": currentRow.value.equipmentName,
+      "world_path": worldUrl.value,
+      "algo_image_name": selectImageName.value
+    }
+  })
+}
+
+const goToMapRescan = () => {
+  router.push('/map_rescan')
+}
+
+const goToTestRecord = () => {
+  router.push('/test_record')
+}
 
 const handleClose = (done: () => void) => {
   done()
@@ -325,7 +356,7 @@ const errorMessage = (response) => {
 }
 
 //上传文件成功
-const uploadSuccess = (response, file, fileList) => {
+const uploadSuccess = (response, file, fileList) => { // world 文件上传成功
   // 继续上传map.stl文件
   uploadStlFile(() => {
     // 更新步骤条状态
@@ -348,6 +379,7 @@ const uploadStlFile = (callback) => {
     window.electronAPI.onUploadFileResponse( (event, result) => {
       if (result.success) { // 脚本执行成功
         console.log('Script execution completed successfully.')
+        // 执行回调 - 状态变更及提醒
         callback()
       } else { // 脚本执行失败
         console.error('Script execution failed.');
@@ -385,7 +417,6 @@ const algorithmForm = reactive({
 const mapRescanForm = reactive({
   name: ''
 })
-const currentTag = ref('')
 
 const algorithmImport = async () => {
   algorithmDialogVisible.value = false
@@ -409,25 +440,7 @@ const algorithmImport = async () => {
   }
 };
 
-const goToDetail = () => {
-  router.push('/about')
-}
-
-const goToMapUpdate = () => {
-  router.push('/map_update')
-}
-
-const goToUpdateStat = () => {
-  router.push('/update_stat')
-}
-
-const goToMapRescan = () => {
-  router.push('/map_rescan')
-}
 
-const goToTestRecord = () => {
-  router.push('/test_record')
-}
 
 // 数据容量单位从B转成MB
 const dataSizeFormatter = (row, column, cellValue, index) => {
@@ -483,7 +496,7 @@ const updateMap = async () => {
 
   // 根据id下载地图更新需要的数据(压缩包)
   const id = ids[0]
-  const url = "http://localhost:8888/map/download/zipfile?id=" + id // 此处url不受配置的代理规则影响,应传递后端完整url地址
+  const url = "http://localhost:8888/map/download/zip?id=" + id // 此处url不受配置的代理规则影响,应传递后端完整url地址
   const fileName = "data-" + id + ".zip"
   const savePath = "/home/cicv/work/pji_desktop/tmp_download/map_zip"
 
@@ -504,7 +517,7 @@ const updateMap = async () => {
 
 const checkMapbufConsistency = async (ids) => {
  try {
-   const response =  await axios.post('/local/map/check/mapbufconsistency', ids)
+   const response =  await axios.post('/local/map/check/mapbuf/consistency', ids)
    console.log(response.data)
    return response.data.status
  } catch (error) {
@@ -529,17 +542,17 @@ const generateWorld = async (row) => {
   const id = row.id
   const equipmentNo = row.equipmentNo
   const sceneNo = id
-  const url = "http://127.0.0.1:8888/map/download/mapbagfile?id=" + id
+  const url = "http://127.0.0.1:8888/map/download/mapbag?id=" + id
   const fileName = "map-" + id + ".bag"
   const savePath = "/home/cicv/work/pji_desktop/tmp_download/map_bag"
   console.log("id", id)
 
   // 拼接world文件上传url
-  worldUploadUrl.value = "http://127.0.0.1:8888/world/upload/worldfile?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo
+  worldUploadUrl.value = "http://127.0.0.1:8888/world/upload/world?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo
   console.log("worldUploadUrl.value=" + worldUploadUrl.value)
 
   // 拼接map.stl文件上传url
-  stlUploadUrl.value = "http://127.0.0.1:8888/world/upload/stlfile?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo
+  stlUploadUrl.value = "http://127.0.0.1:8888/world/upload/stl?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo
   console.log("stlUploadUrl.value=" + stlUploadUrl.value)
 
   console.log("Downloading file: map.bag...")
@@ -586,6 +599,9 @@ const generateWorld = async (row) => {
 
 // 仿真测试环境准备
 const startSimulation = async (row) => {
+  // 保存row
+  currentRow.value = row
+  // console.log("currentRow.value", currentRow.value)
   // 检查是否已选择算法镜像
   let imageName = selectImageName.value
   let containerName = imageName.replace(':', '_')
@@ -603,10 +619,12 @@ const startSimulation = async (row) => {
 
   // 检查world文件是否存在
   response = await axios.get('/local/world/check/file/world/status?id=' + row.id)
+
   if (!response.data.status){ // 不存在
     ElMessage.error("world文件不存在,请先生成并上传world文件!")
     return
   }
+  worldUrl.value = response.data.details
 
   // 开启对话框
   simulationDialogVisible.value = true
@@ -614,7 +632,7 @@ const startSimulation = async (row) => {
 
   // 根据id下载仿真测试需要的数据(压缩包)
   const id = row.id
-  const url = "http://localhost:8888/simulation/download/zipfile?id=" + id // 此处url不受配置的代理规则影响,应传递后端完整url地址
+  const url = "http://localhost:8888/simulation/download/zip?id=" + id // 此处url不受配置的代理规则影响,应传递后端完整url地址
   const fileName = "data-" + id + ".zip"
   const savePath = "/home/cicv/work/pji_desktop/tmp_download/simulation_zip"