|
|
|
|
@ -78,6 +78,7 @@
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
|
|
|
|
|
import { useRoute } from 'vue-router'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import { Download } from '@element-plus/icons-vue'
|
|
|
|
|
import WebSocketClient from '@/utils/websocket'
|
|
|
|
|
@ -99,10 +100,16 @@ const webrtcUrl = ref('')
|
|
|
|
|
const streamRunning = ref(false)
|
|
|
|
|
const algorithmEnabled = ref(false)
|
|
|
|
|
const isLiveMode = computed(() => videoMode.value === 'live')
|
|
|
|
|
const snCode = ref('')
|
|
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
const RAW_WS_URL = `ws://${fileHttp.ptApi}/ws/raw_rtsp`
|
|
|
|
|
const DETECT_WS_URL = `ws://${fileHttp.ptApi}/ws/detect_rtsp`
|
|
|
|
|
let rawWsClient = null
|
|
|
|
|
let detectWsClient = null
|
|
|
|
|
let processWsClient = null
|
|
|
|
|
|
|
|
|
|
const PROCESS_RESULTS_WS_URL = `ws://${fileHttp.ptApi}/ws/process_results?format=json`
|
|
|
|
|
|
|
|
|
|
function destroyRawWs() {
|
|
|
|
|
if (rawWsClient) {
|
|
|
|
|
@ -340,7 +347,6 @@ function connectDetectStream() {
|
|
|
|
|
if (exportVideoUrl.value || exportReportUrl.value) {
|
|
|
|
|
canExport.value = true
|
|
|
|
|
}
|
|
|
|
|
parseAndSaveReport(exportReportUrl.value)
|
|
|
|
|
algorithmRunning.value = false
|
|
|
|
|
paused.value = false
|
|
|
|
|
destroyDetectWs()
|
|
|
|
|
@ -435,6 +441,7 @@ function toggleStream() {
|
|
|
|
|
videoMode.value = 'live'
|
|
|
|
|
webrtcUrl.value = ''
|
|
|
|
|
clearAllLiveData()
|
|
|
|
|
connectProcessWs()
|
|
|
|
|
// 如果原始流还在运行(由"切换"启动),先关闭
|
|
|
|
|
if (rawWsClient) {
|
|
|
|
|
addLog('正在关闭原始流,切换到推算流...', 'info')
|
|
|
|
|
@ -581,8 +588,10 @@ function toggleAlgorithm() {
|
|
|
|
|
|
|
|
|
|
if (videoMode.value === 'offline' && detectionFile.value) {
|
|
|
|
|
resetDetectionState()
|
|
|
|
|
snCode.value = ''
|
|
|
|
|
detectStatusText.value = '正在连接 WebSocket...'
|
|
|
|
|
addLog('开始连接算法检测服务', 'info')
|
|
|
|
|
connectProcessWs()
|
|
|
|
|
videoAutoplayTrigger.value++
|
|
|
|
|
|
|
|
|
|
wsClient = new WebSocketClient({
|
|
|
|
|
@ -683,7 +692,6 @@ function toggleAlgorithm() {
|
|
|
|
|
exportVideoUrl.value = videoUrl
|
|
|
|
|
exportReportUrl.value = msg.report_url ? (HTTP_BASE.replace(/\/$/, '') + msg.report_url) : ''
|
|
|
|
|
canExport.value = true
|
|
|
|
|
parseAndSaveReport(exportReportUrl.value)
|
|
|
|
|
addLog('检测结果已就绪,可点击导出按钮下载', 'success')
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
@ -788,7 +796,7 @@ function endDetection() {
|
|
|
|
|
if (!wsClient || wsClient.getReadyState() !== WebSocket.OPEN || ending.value) return
|
|
|
|
|
ending.value = true
|
|
|
|
|
detectStatusText.value = '已发送结束请求,等待服务端生成结果...'
|
|
|
|
|
addLog('前端请求结束检测', 'warning')
|
|
|
|
|
// addLog('前端请求结束检测', 'warning')
|
|
|
|
|
wsClient.send({ type: 'stop' })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -807,6 +815,7 @@ function addLog(message, type = 'info') {
|
|
|
|
|
// ============ 生命周期 ============
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
addLog('页面已加载,可上传离线视频或连接实时流', 'info')
|
|
|
|
|
snCode.value = route.query.sn || ''
|
|
|
|
|
try {
|
|
|
|
|
const enabled = await getAlgorithmStatusApi()
|
|
|
|
|
algorithmEnabled.value = !!enabled
|
|
|
|
|
@ -825,25 +834,44 @@ async function disableAlgorithm() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function parseAndSaveReport(reportUrl) {
|
|
|
|
|
if (!reportUrl) return
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch(reportUrl)
|
|
|
|
|
const text = await res.text()
|
|
|
|
|
|
|
|
|
|
// 按 "流程 #N:" 切片,保留分隔符
|
|
|
|
|
const sections = text.split(/(?=流程 #\d+:)/)
|
|
|
|
|
// 过滤出以 "流程 #" 开头的内容(去掉表头部分)
|
|
|
|
|
const processData = sections
|
|
|
|
|
.filter(s => /^流程 #\d+:/.test(s.trim()))
|
|
|
|
|
.map(s => s.trim())
|
|
|
|
|
|
|
|
|
|
if (processData.length > 0) {
|
|
|
|
|
await saveReportApi(processData)
|
|
|
|
|
addLog('检测报告流程数据已保存', 'success')
|
|
|
|
|
function connectProcessWs() {
|
|
|
|
|
if (processWsClient) return
|
|
|
|
|
|
|
|
|
|
const stepDetailsItem = { sn: '', message: [] }
|
|
|
|
|
|
|
|
|
|
processWsClient = new WebSocketClient({
|
|
|
|
|
url: PROCESS_RESULTS_WS_URL,
|
|
|
|
|
autoReconnect: true,
|
|
|
|
|
heartbeatInterval: 30000,
|
|
|
|
|
heartbeatTimeout: 5000,
|
|
|
|
|
debug: true
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
processWsClient.on('message', async (data) => {
|
|
|
|
|
if (data.type !== 'process_result') return
|
|
|
|
|
stepDetailsItem.message.length = 0
|
|
|
|
|
stepDetailsItem.sn = snCode.value
|
|
|
|
|
stepDetailsItem.message.push(data.message)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await saveReportApi(stepDetailsItem)
|
|
|
|
|
addLog('流程数据已实时保存', 'success')
|
|
|
|
|
} catch {
|
|
|
|
|
addLog('保存流程数据失败', 'error')
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
addLog('读取或保存报告失败: ' + (error.message || ''), 'error')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
processWsClient.on('error', () => {
|
|
|
|
|
addLog('流程结果服务连接失败', 'error')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
processWsClient.connect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function disconnectProcessWs() {
|
|
|
|
|
if (processWsClient) {
|
|
|
|
|
processWsClient.destroy()
|
|
|
|
|
processWsClient = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -858,6 +886,7 @@ onUnmounted(() => {
|
|
|
|
|
}
|
|
|
|
|
stopRawStream()
|
|
|
|
|
stopDetectStream()
|
|
|
|
|
disconnectProcessWs()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|