123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- <template>
- <el-form :model="form" label-width="auto" style="max-width: 700px">
- <!-- <el-form-item label="发布终点 :">-->
- <!-- <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-select
- v-model="selectLevel"
- placeholder="请选择"
- size="default"
- style="width: 180px; "
- clearable
- >
- <el-option
- v-for="item in algoLevels"
- :key="item"
- :label="item"
- :value="item"
- />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="danger" @click="queryRecordByCondition">查询</el-button>
- </el-form-item>
- <el-form-item>
- <el-button type="danger" @click="resetRecord">重置</el-button>
- </el-form-item>
- <div style="float: right">
- <el-form-item>
- <el-button type="danger" @click="downloadFile('bag')" :disabled="multipleSelection.length == 0">下载测试bag</el-button>
- </el-form-item>
- <el-form-item>
- <el-button type="danger" @click="downloadFile('report')" :disabled="multipleSelection.length == 0">下载算法评价报告</el-button>
- </el-form-item>
- </div>
- </el-form>
- </div>
- <el-table stripe style="background-color: rgba(255,0,0,99%);width: 100%" border :data="evalTableData"
- @selection-change="handleSelectionChange"
- fixed ref="multipleTableRef" :cell-style="{ textAlign: 'center'}" :header-cell-style="{ textAlign: 'center'}">
- <el-table-column type="selection" width="55"/>
- <el-table-column prop="test_time" :formatter="formatDate" label="测试时间"/>
- <el-table-column prop="round" label="轮次"/>
- <el-table-column prop="test_bag_path" :formatter="(row) => formatPath(row, 'test_bag_path')" label="测试bag"/>
- <el-table-column prop="test_duration" label="测试时长"/>
- <el-table-column prop="test_distance" label="测试里程"/>
- <el-table-column prop="algo_evaluation_level" label="算法评价等级"/>
- <el-table-column prop="algo_evaluation_report" :formatter="(row) => formatPath(row, 'algo_evaluation_report')" label="算法评价报告"/>
- </el-table>
- </el-dialog>
- <el-form-item label="随机起终点 :">
- <el-switch
- v-model="form.isRandom"
- inline-prompt
- active-text="是"
- inactive-text="否"
- />
- <div v-if="form.isRandom">
- <el-input-number class="mx-4" v-model="form.randomCount" :min="1" :max="5" style="margin-left: 15px; width: 100px;"/>
- <span style="margin-left: 10px;">次</span>
- </div>
- </el-form-item>
- <el-form-item label="起点设置 :" v-if="!form.isRandom">
- <el-radio-group v-model="form.origin" @change="originChange">
- <el-radio :value="true">默认起点</el-radio>
- <el-radio :value="false">自定义起点</el-radio>
- </el-radio-group>
- <span v-show="form.origin==false" @click="startDialogVisible=true" style="margin-left: 20px; font-size: 14px; color: gray; cursor: pointer">
- 当前值:X: {{startForm.X}} Y: {{startForm.Y}} Z: {{startForm.Z}} R: {{startForm.R}} P: {{startForm.P}} H: {{startForm.H}}
- </span>
- </el-form-item>
- <el-form-item label="终点设置 :" v-if="!form.isRandom">
- <el-radio-group v-model="form.destination" @change="destinationChange">
- <el-radio :value="true">默认终点</el-radio>
- <el-radio :value="false">自定义终点</el-radio>
- </el-radio-group>
- <span v-show="form.destination==false" @click="endDialogVisible=true" style="margin-left: 20px; font-size: 14px; color: gray; cursor: pointer">
- 当前值:X: {{endForm.X}} Y: {{endForm.Y}} Z: {{endForm.Z}} R: {{endForm.R}} P: {{endForm.P}} H: {{endForm.H}}
- </span>
- </el-form-item>
- <el-form-item label="加载默认障碍物:">
- <el-radio-group v-model="form.resource">
- <el-radio :value="true">是</el-radio>
- <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>
- <el-dialog
- v-model="startDialogVisible"
- title="请输入自定义起点参数"
- width="300"
- :before-close="handleClose"
- draggable
- >
- <template #footer>
- <div class="dialog-footer">
- <el-form :model="startForm" label-width="auto">
- <el-form-item style="margin-bottom: 10px" label="位置 X :" required >
- <el-input-number style="width: 100%;" v-model="startForm.X" :controls="false" :precision="2" />
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="位置 Y :" required>
- <el-input-number style="width: 100%;" v-model="startForm.Y" :controls="false" :precision="2" />
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="位置 Z :">
- <el-input-number style="width: 100%;" v-model="startForm.Z" :controls="false" :precision="2" disabled/>
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="角度 R :">
- <el-input-number style="width: 100%;" v-model="startForm.R" :controls="false" :precision="2" disabled/>
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="角度 P :">
- <el-input-number style="width: 100%;" v-model="startForm.P" :controls="false" :precision="2" disabled/>
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="角度 H :" required>
- <el-input-number style="width: 100%;" v-model="startForm.H" :controls="false" :precision="2" />
- </el-form-item>
- </el-form>
- <el-button type="primary" @click="startDialogVisible = false">确认</el-button>
- </div>
- </template>
- </el-dialog>
- <el-dialog
- v-model="endDialogVisible"
- title="请输入自定义终点参数"
- width="300"
- :before-close="handleClose"
- draggable
- >
- <template #footer>
- <div class="dialog-footer">
- <el-form :model="endForm" label-width="auto">
- <el-form-item style="margin-bottom: 10px" label="位置 X :">
- <el-input-number style="width: 100%;" v-model="endForm.X" :controls="false" :precision="2" />
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="位置 Y :">
- <el-input-number style="width: 100%;" v-model="endForm.Y" :controls="false" :precision="2"/>
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="位置 Z :">
- <el-input-number style="width: 100%;" v-model="endForm.Z" :controls="false" :precision="2" disabled/>
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="角度 R :">
- <el-input-number style="width: 100%;" v-model="endForm.R" :controls="false" :precision="2" disabled/>
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="角度 P :">
- <el-input-number style="width: 100%;" v-model="endForm.P" :controls="false" :precision="2" disabled/>
- </el-form-item>
- <el-form-item style="margin-bottom: 10px" label="角度 H :">
- <el-input-number style="width: 100%;" v-model="endForm.H" :controls="false" :precision="2"/>
- </el-form-item>
- </el-form>
- <el-button type="primary" @click="endDialogVisible = false">确认</el-button>
- </div>
- </template>
- </el-dialog>
- <!-- <el-button style="margin-left: 10px;" type="primary" @click="goToMain">更换设置并执行</el-button> -->
- <el-button style="margin-left: 10px;" type="primary" @click="showEvalResult" :disabled="evalResult.length === 0">算法评价结果</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>
- </el-form>
- </template>
- <script lang="ts" setup>
- import {useRoute, useRouter} from 'vue-router'; // 导入 Vue Router 的 useRouter 钩子
- import {nextTick, reactive} from 'vue'
- import {ref} from 'vue'
- import {ElTable, ElLoading, ElMessage} from "element-plus";
- import {getCurrentInstance} from "vue";
- import axios from "axios";
- import path from 'path-browserify';
- import moment from "moment";
- const startDialogVisible = ref(false)
- const endDialogVisible = ref(false)
- const {proxy} = getCurrentInstance();
- const evalResult = ref([])
- const evalTableVisible = ref(false)
- const evalTableData = ref([])
- const queryLine = reactive({
- algorithmLevel: ''
- })
- const multipleSelection = ref<[]>([])
- 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;
- const PJI_SCRIPT_PATH_PREFIX = import.meta.env.VITE_PJI_SCRIPT_PATH_PREFIX
- 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 selectLevel = ref("")
- const algoLevels = ['较差', '一般', '良好', '优秀']
- // const testTime = ref("1726113464184")
- const testTime = ref("")
- const resetEvalTableData = ref([])
- const handleClose = (done: () => void) => {
- done()
- }
- // do not use same name with ref
- const form = reactive({
- name: '',
- region: '',
- date1: '',
- date2: '',
- delivery: false,
- type: [],
- resource: true,
- desc: '',
- isRandom: false,
- origin: true,
- destination: true,
- randomCount: ref(1),
- })
- const startForm = reactive({
- X: 0,
- Y: 0,
- Z: 0,
- R: 0,
- P: 0,
- H: 0,
- })
- const endForm = reactive({
- X: 0,
- Y: 0,
- Z: 0,
- R: 0,
- P: 0,
- H: 0,
- })
- const originChange = (value: string) => {
- if (!value) {
- startDialogVisible.value = true
- }
- }
- const destinationChange = (value: string) => {
- if (!value) {
- endDialogVisible.value = true
- }
- }
- const handleSelectionChange = (rows: []) => {
- multipleSelection.value = rows
- console.log("multipleSelection", multipleSelection.value)
- }
- const downloadFile = async (typeName) => {
- // object key
- let keys
- // 文件名(区分1个文件/多个文件)
- let fileName
- if (typeName === "bag") {
- keys = multipleSelection.value.map(item => item["test_bag_path"])
- fileName = multipleSelection.value.length > 1 ? sceneId + "_" + typeName + ".zip" : "test.bag"
- } else if(typeName === "report") {
- keys = multipleSelection.value.map(item => item["algo_evaluation_report"])
- fileName = multipleSelection.value.length > 1 ? sceneId + "_" + typeName + ".zip" : "report.pdf"
- }
- console.log("keys", keys)
- console.log("typeName", typeName)
- console.log("fileName", fileName)
- // 下载记录
- const url = "http://127.0.0.1:8888/simulation/download/oss/key?sceneId=" + sceneId + "&typeName=" + typeName
- const result = await window.electronAPI.downloadFile(url, fileName, "", true, true, "post", keys);
- if (!result.success) { // 下载失败
- console.error('File download failed:', result.error);
- ElMessage.error("文件下载失败!");
- } else { // 下载成功
- console.log('File downloaded successfully:', result.filePath);
- ElMessage.success("文件下载成功!");
- }
- }
- // 表格 - 格式化路径
- const formatPath = (row, name) => {
- return path.basename(row[name])
- }
- const formatDate = (row) => {
- return moment(+row.test_time).format("YYYY-MM-DD HH:mm:ss")
- }
- // 弹窗显示算法评价记录
- const showEvalResult = async (value: string) => {
- console.log("testTime", testTime.value)
- console.log("sceneId", sceneId)
- if (testTime.value != "") {
- // 查询算法评价记录
- let response = await axios.post('/local/simulation/query/test/record', {
- 'scene_id': sceneId,
- 'test_time': testTime.value,
- })
- if (!response.data.status){ // 不存在
- proxy.$message.error("算法评价结果查询失败!");
- return
- }
- // 解析结果
- evalTableData.value = JSON.parse(response.data.data)
- resetEvalTableData.value = evalTableData.value // for reset purpose
- await nextTick() // 避免vue warning: onMounted is called when there is no active component instance...
- // 开启弹窗
- evalTableVisible.value = true
- console.log("evalTableData.value", evalTableData.value)
- }else {
- proxy.$message.error("暂无算法评价结果!");
- }
- }
- // 根据条件筛选结果
- // 正常情况下包含分页需要发送请求以根据条件查询数据,但本页面不包含分页且查询条件较少,因此直接通过filter筛选数据以节省网络资源
- const queryRecordByCondition = async () => {
- // 含分页 - 发送请求
- // let response = await axios.post('/local/simulation/query/test/record', {
- // 'scene_id': sceneId,
- // 'test_time': testTime.value,
- // "algo_evaluation_level": selectLevel.value
- // })
- //
- // if (!response.data.status){ // 不存在
- // proxy.$message.error("算法评价结果查询失败!");
- // return
- // }
- // // 解析结果
- // evalTableData.value = JSON.parse(response.data.details)
- console.log("selectLevel.value", selectLevel.value)
- // 不含分页 - filter
- if (selectLevel.value !== "" && selectLevel.value != undefined) { // 查询条件不为空
- evalTableData.value = resetEvalTableData.value.filter(d => d["algo_evaluation_level"] === selectLevel.value)
- } else { // 查询条件为空,重置结果
- resetRecord()
- }
- }
- // 重置结果
- const resetRecord = () => {
- evalTableData.value = resetEvalTableData.value
- }
- 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)
- console.log("count", count)
- // 异步
- window.electronAPI.runSimulation(random_flag, count, obstacle_flag, default_start_flag, default_end_flag, start_point, end_point)
- evalResult.value = []
- // 监听脚本执行状态
- window.electronAPI.onRunSimulationResponse( async (event, result) => {
- if (!result.success) { // 脚本执行过程中发生错误
- console.error('Script execution failed.');
- proxy.$message.error("仿真测试发生错误!");
- console.log()
- } 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")
- proxy.$message.error("仿真测试发生错误!");
- return
- }
- console.log("response.data", response.data)
- evalResult.value = response.data
- // 上传pgm文件并获取存储url
- // 执行脚本 - 上传文件
- const filePath = PJI_SCRIPT_PATH_PREFIX + "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) => {
- if (i === 0) testTime.value = String(d["timeStamp"])
- 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)
- // 上传数据
- 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("仿真测试发生错误!");
- }
- })
- }
- })
- }
- const goToMain = () => {
- // router.push('/')
- router.back()
- }
- const goToAlgorithmEval = () => {
- router.push('/algorithm_eval')
- }
- </script>
|