From 378fc299c70e004abde6ad805274bc60e1a2db2c Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Fri, 18 Apr 2025 15:22:20 +0800
Subject: [PATCH] feat: 控制台ai直播功能

---
 src/const/drc.js                                                         |    3 
 src/api/payload.js                                                       |   17 +++++
 src/components/CurrentTaskDetails/TaskDetailsLeft.vue                    |    5 +
 src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue |   19 +++++-
 src/components/CurrentTaskDetails/TaskDetailsHead.vue                    |  105 +++++++++++++++++++---------------
 src/components/CurrentTaskDetails/CurrentTaskDetails.vue                 |   21 ++++++
 6 files changed, 116 insertions(+), 54 deletions(-)

diff --git a/src/api/payload.js b/src/api/payload.js
index 517dbf1..15be774 100644
--- a/src/api/payload.js
+++ b/src/api/payload.js
@@ -80,4 +80,21 @@
 };
 
 
+// 无人机开启ai
+export const getLiveAiLinkApi = (data) => {
+  return request({
+    url:`${API_PREFIX}/live/streams/start/ai`,
+    method: 'post',
+    data,
+  });
+};
+
+// original_stream_url
+//   :
+//   "rtmp://139.196.74.78/live/1581F6Q8D247200GGSWB-81-0-0-normal-0"
+// video_id
+//   :
+//   "1581F6Q8D247200GGSWB/81-0-0/normal-0"
+
+
 
diff --git a/src/components/CurrentTaskDetails/CurrentTaskDetails.vue b/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
index 5e95b53..181f8d5 100644
--- a/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
+++ b/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
@@ -46,6 +46,8 @@
 import { ElMessage } from 'element-plus'
 import EventBus from '@/event-bus'
 import { updateDroneQualityApi } from '@/api/drc'
+import { getLiveAiLinkApi } from '@/api/payload'
+import { CURRENT_CONFIG } from '@/utils/http/config'
 
 const isAutoControl = ref(true)
 const lineQuality = ref(1) //1流畅,2标清
@@ -65,6 +67,9 @@
 provide('dockSn', dockSn)
 provide('droneSn', droneSn)
 
+const isAiLive = ref(false)
+provide('isAiLive', isAiLive)
+
 const isShow = defineModel('show')
 const props = defineProps(['id'])
 const currentLiveUrl = ref('')
@@ -72,19 +77,27 @@
 const isMaxMap = ref(false)
 let droneWebSocket //WS实例
 
-// 机巢直播
+const video_id = ref('')
+provide('video_id', video_id)
+
+// 获取机巢直播
 const getDeviceLiveUrl = async () => {
 	const res = await liveStart(dockSn.value, 2)
 	currentLiveUrl.value = res.data.data.rtcs_url
 }
 
-const video_id = ref('')
+const getAiLiveUrl = ()=>{
+	const res = getLiveAiLinkApi({ original_stream_url: `${CURRENT_CONFIG.rtmpURL}${video_id}`, video_id })
+	currentLiveUrl.value = res.data.data.rtcs_url
+	isAiLive.value = true
+}
 
 // 获取无人机直播url
 async function getDroneLiveUrl() {
 	const res = await liveStart(droneSn.value, lineQuality.value)
 	currentLiveUrl.value = res.data.data.rtcs_url
 	video_id.value = res.data.data.video_id
+	isAiLive.value = false
 }
 
 // 无人机直播画质切换
@@ -169,6 +182,8 @@
 onMounted(() => {
 	getTaskDetails()
 	EventBus.on('CurrentTaskDetails-timeStop', changeLineQuality)
+	EventBus.on('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl)
+	EventBus.on('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl)
 })
 
 onBeforeUnmount(() => {
@@ -176,6 +191,8 @@
 	deviceOsdInfo.value = {}
 	droneWebSocket = null
 	EventBus.off('CurrentTaskDetails-timeStop', changeLineQuality)
+	EventBus.off('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl)
+	EventBus.off('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl)
 })
 </script>
 
diff --git a/src/components/CurrentTaskDetails/TaskDetailsHead.vue b/src/components/CurrentTaskDetails/TaskDetailsHead.vue
index a27d1c1..c6164b2 100644
--- a/src/components/CurrentTaskDetails/TaskDetailsHead.vue
+++ b/src/components/CurrentTaskDetails/TaskDetailsHead.vue
@@ -1,19 +1,19 @@
 <template>
 	<div class="detailsHead">
-		<div class="droneName" :title="taskDetails.name">{{taskDetails.name}}</div>
+		<div class="droneName" :title="taskDetails.name">{{ taskDetails.name }}</div>
 		<div class="infoListBox">
 			<div v-for="item in infoList">
-				<div class="infoValue">{{ item.value }}</div>
+				<div class="infoValue">{{ item.value }}{{ item.unit }}</div>
 				<div class="infoTitle">{{ item.title }}</div>
 			</div>
 		</div>
 		<div class="controlBtn">
-			<el-icon class="refresh" @click="ElMessage.warning('加急开发中...')">
+			<el-icon class="refresh" @click="refreshLive">
 				<Refresh />
 			</el-icon>
 			<div class="switchBtn" @click="switchBtn">
-				<div :class="{ open: open }">NO</div>
-				<div :class="{ open: !open }">OFF</div>
+				<div :class="{ open: !isAiLive }">OFF</div>
+				<div :class="{ open: isAiLive }">ON</div>
 			</div>
 		</div>
 	</div>
@@ -24,37 +24,45 @@
 import { getFlightStatistics } from '@/api/home/machineNest'
 import _, { throttle } from 'lodash'
 import { getLnglatAltitude, getLnglatDist } from '@/utils/cesium/mapUtil'
-import { networkQuality } from '@/const/drc'
+import { fourGQuality, SDRQuality } from '@/const/drc'
 import { ElMessage } from 'element-plus'
+import EventBus from '@/event-bus'
 
 const taskDetailsViewer = inject('taskDetailsViewer')
 const wsInfo = inject('wsInfo')
 const dockSn = inject('dockSn')
 const taskDetails = inject('taskDetails')
-const open = ref(true)
+const isAiLive = inject('isAiLive')
 const singleTotal = ref({})
 
 // 不要随意更换顺序,有联动
 const infoList = ref([
-	{ title: '实时真高', value: 0 },
-	{ title: '绝对高度', value: 0 },
-	{ title: '水平速度', value: 0 },
-	{ title: '垂直速度', value: 0 },
-	{ title: '经度', value: 0 },
-	{ title: '纬度', value: 0 },
-	{ title: '风速', value: 0 },
-	{ title: '4G信号', value: 0 },
-	{ title: 'SDR信号', value: 0 },
-	{ title: 'GPS搜星数', value: 0 },
-	{ title: 'RTK搜星数', value: 0 },
-	{ title: '距离机场', value: 0 },
-	{ title: '飞行时长', value: 0 },
-	{ title: '电池电量', value: 0 },
+	{ index: 0, title: '实时真高', value: 0, unit: 'M' },
+	{ index: 1, title: '绝对高度', value: 0, unit: 'M' },
+	{ index: 2, title: '水平速度', value: 0, unit: 'M/s' },
+	{ index: 3, title: '垂直速度', value: 0, unit: 'M/s' },
+	{ index: 4, title: '经度', value: 0, unit: '°' },
+	{ index: 5, title: '纬度', value: 0, unit: '°' },
+	{ index: 6, title: '风速', value: 0, unit: 'M/s' },
+	{ index: 7, title: '4G信号', value: 0, unit: '' },
+	{ index: 8, title: 'SDR信号', value: 0, unit: '' },
+	{ index: 9, title: 'GPS搜星数', value: 0, unit: '' },
+	{ index: 10, title: 'RTK搜星数', value: 0, unit: '' },
+	{ index: 11, title: '距离机场', value: 0, unit: 'M' },
+	{ index: 12, title: '飞行时长', value: 0, unit: '小时' },
+	{ index: 13, title: '电池电量', value: 0, unit: '%' },
 ])
 
 const switchBtn = () => {
-	ElMessage.warning('加急开发中...')
-	open.value = !open.value
+	if (isAiLive.value) {
+		EventBus.emit('CurrentTaskDetails-getDroneLiveUrl')
+	} else {
+		EventBus.emit('CurrentTaskDetails-getAiLiveUrl')
+	}
+}
+
+function refreshLive(){
+	EventBus.emit('CurrentTaskDetails-getAiLiveUrl')
 }
 
 function getFlightStatisticsFun() {
@@ -70,8 +78,8 @@
 	const { latitude, longitude, height } = device_osd_host
 	if (!latitude) return
 	getLnglatAltitude(longitude, latitude, taskDetailsViewer.value).then(res => {
-		const last =  height - res?.height
-		infoList.value[0].value = last ? infoList.value[0].value : _.round(height - res?.height, 1) + 'm'
+		const last = height - res?.height
+		infoList.value[0].value = last ? infoList.value[0].value : _.round(height - res?.height, 1)
 	})
 }
 
@@ -79,29 +87,31 @@
 	const device_osd_host = wsInfo?.value?.device_osd?.data?.host || {}
 	const dock_osd_host = wsInfo?.value?.dock_osd?.data?.host || {}
 	const { longitude, latitude, height, horizontal_speed, vertical_speed, wind_speed, battery } = device_osd_host
-	const { longitude: dockLon, latitude: dockLat } = dock_osd_host
+	const { longitude: dockLon, latitude: dockLat, wireless_link } = dock_osd_host
+
 	let dist = infoList.value[11].value
 	if (longitude && latitude && dockLon && dockLat) {
-		dist = _.round(getLnglatDist(longitude, latitude, dockLon, dockLat), 0) + 'M'
+		dist = _.round(getLnglatDist(longitude, latitude, dockLon, dockLat), 0)
 	}
-	const GPSNum = dock_osd_host?.position_state?.gps_number || infoList.value[9].value
-	const RTKNum = dock_osd_host?.position_state?.rtk_number || infoList.value[10].value
-	infoList.value = [
-		{ title: '实时真高', value: '0' },
-		{ title: '绝对高度', value: _.round(height || 0, 1) + 'M' },
-		{ title: '水平速度', value: _.round(horizontal_speed || 0, 0) + 'M/s' },
-		{ title: '垂直速度', value: _.round(vertical_speed || 0, 0) + 'M/s' },
-		{ title: '经度', value: _.round(longitude || 0, 2) },
-		{ title: '纬度', value: _.round(latitude || 0, 2) },
-		{ title: '风速', value: _.round(wind_speed || 0, 0) + 'M/s' },
-		{ title: '4G信号', value: networkQuality?.[dock_osd_host?.quality] || '-' },
-		{ title: 'SDR信号', value: '0' },
-		{ title: 'GPS搜星数', value: GPSNum },
-		{ title: 'RTK搜星数', value: RTKNum },
-		{ title: '距离机场', value: dist },
-		{ title: '飞行时长', value: _.round((singleTotal.value?.hour_count || 0),1) + '小时' },
-		{ title: '电池电量', value: (battery?.capacity_percent || 0) + '%' },
-	]
+
+	const newGPSNum = dock_osd_host?.position_state?.gps_number
+	const newRTKNum = dock_osd_host?.position_state?.rtk_number
+	const newFourG = wireless_link?.['4g_quality']
+	const newSdr = wireless_link?.['sdr_quality']
+
+	infoList.value[1].value = _.round(height || 0, 1)
+	infoList.value[2].value = _.round(horizontal_speed || 0, 0)
+	infoList.value[3].value = _.round(vertical_speed || 0, 0)
+	infoList.value[4].value = _.round(longitude || 0, 2)
+	infoList.value[5].value = _.round(latitude || 0, 2)
+	infoList.value[6].value = _.round(wind_speed || 0, 0)
+	if (newFourG !== undefined) infoList.value[7].value = newFourG
+	if (newSdr !== undefined) infoList.value[8].value = newSdr
+	if (newGPSNum !== undefined) infoList.value[9].value = newGPSNum
+	if (newRTKNum !== undefined) infoList.value[10].value = newRTKNum
+	infoList.value[11].value = dist
+	infoList.value[12].value = _.round(singleTotal.value?.hour_count || 0, 1)
+	infoList.value[13].value = battery?.capacity_percent || 0
 	getRealTimeReallyHigh()
 }
 
@@ -150,7 +160,7 @@
 		color: #ededed;
 		text-align: center;
 		line-height: 42px;
-		white-space:nowrap;
+		white-space: nowrap;
 		overflow: hidden;
 		text-overflow: ellipsis;
 		padding: 0 10px;
@@ -166,8 +176,9 @@
 		font-weight: 400;
 		margin-left: 15px;
 
-		>div{
+		> div {
 			width: 90px;
+
 			.infoValue {
 				font-size: 20px;
 				color: #ffffff;
diff --git a/src/components/CurrentTaskDetails/TaskDetailsLeft.vue b/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
index 89b9174..fe76edb 100644
--- a/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
+++ b/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
@@ -83,7 +83,7 @@
 
 // 修改相机参数
 function cameraParamsChange() {
-	const { camera_type, zoom_factor} = cameraParams.value
+	const { camera_type, zoom_factor } = cameraParams.value
 	cameraParamsChangeApi({
 		sn: dockSn.value,
 		...cameraParams.value,
@@ -125,6 +125,9 @@
 
 // 拍照
 function takePictures() {
+	if (isRecording.value) {
+		return ElMessage.warning('请先结束录像')
+	}
 	// photo拍照,video_start开始录像,video_stop结束录像
 	callPhotoAndVideoCmd(dockSn.value, 'photo').then(res => {
 		ElMessage.success('拍照成功')
diff --git a/src/const/drc.js b/src/const/drc.js
index 6877433..c4a4ad1 100644
--- a/src/const/drc.js
+++ b/src/const/drc.js
@@ -8,4 +8,5 @@
 	DELAY_TIME_INFO_PUSH: 'delay_info_push', // 图传链路延时信息上报
 }
 
-export const networkQuality = { 0: '无信号', 1: '差', 2: '较差', 3: '一般', 4: '较好', 5: '好' }
+export const fourGQuality = { 0: '无信号', 1: '差', 2: '较差', 3: '一般', 4: '较好', 5: '好' }
+export const SDRQuality = { 0: '无信号', 1: '差', 2: '较差', 3: '一般', 4: '较好', 5: '好' }
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue b/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
index 935f067..e875c03 100644
--- a/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -59,7 +59,15 @@
   <!-- 添加任务 -->
   <AddTask v-model:show="isShowAddTask" @refresh="searchClick"/>
   <!-- 当前任务详情 -->
-  <CurrentTaskDetails v-if="isShowCurrentTaskDetails"  v-model:show="isShowCurrentTaskDetails" :id="rowData.id"/>
+  <CurrentTaskDetails
+		v-if="isShowCurrentTaskDetails"
+		v-model:show="isShowCurrentTaskDetails"
+		:id="rowData.id"/>
+	<!-- 历史人物详情 -->
+  <DeviceJobDetails
+		v-if="isShowDeviceJobDetails"
+		v-model:show="isShowDeviceJobDetails"
+		:wayLineJodInfoId="wayLineJodInfoId"/>
 </template>
 
 <script setup>
@@ -68,6 +76,7 @@
 import CurrentTaskDetails from '@/components/CurrentTaskDetails/CurrentTaskDetails.vue';
 import { jobList } from '@/api/home/task';
 import { ElMessage } from 'element-plus'
+import DeviceJobDetails from '@/components/DeviceJobDetails/DeviceJobDetails.vue'
 
 const jobListParams = reactive({
   current: 1,
@@ -76,6 +85,9 @@
 });
 const jobListData = ref([]);
 const total = ref(0);
+let wayLineJodInfoId = ref('')
+let isShowDeviceJobDetails = ref(false);
+let isShowCurrentTaskDetails = ref(false);
 
 // 获取任务列表
 const getJobList = () => {
@@ -98,15 +110,16 @@
 };
 
 // 查看当前任务详情 如果是一台机则显示详情 如果是多台机则进入集群调度(暂未开发)
-let isShowCurrentTaskDetails = ref(false);
 let rowData = ref({});
 const handleDetail = (row) => {
 	if (row.device_sns.length === 1){
+		console.log(row)
 		if (row.status === 2 || row.status === 1){
 			rowData.value = row? row : {};
 			isShowCurrentTaskDetails.value = true;
 		} else{
-			ElMessage.warning('todo 跳历史任务详情')
+			wayLineJodInfoId.value = row.id
+			isShowDeviceJobDetails.value = true
 		}
 	}else{
 		ElMessage.warning('即将跳转到集群调度');

--
Gitblit v1.9.3