diff --git a/src/api/workmanagement/droneDock.ts b/src/api/workmanagement/droneDock.ts new file mode 100644 index 0000000..91e179f --- /dev/null +++ b/src/api/workmanagement/droneDock.ts @@ -0,0 +1,14 @@ +import { defHttp } from '@/utils/http/axios'; + +enum Api { + VerifyToken = '/api/DroneDock/VerifyToken', + GetDroneDockflightInfos = '/api/DroneDock/GetDroneDockflightInfos', +} + +export function getVerifyToken(token) { + return defHttp.get({ url: Api.VerifyToken + '?token=' + token }); +} + +export function getDroneDockflightInfos(id) { + return defHttp.get({ url: Api.GetDroneDockflightInfos + '?taskid=' + id }); +} diff --git a/src/plugin/video/index.ts b/src/plugin/video/index.ts new file mode 100644 index 0000000..c5db646 --- /dev/null +++ b/src/plugin/video/index.ts @@ -0,0 +1,238 @@ +import { createConnection, client, servicesTopic, destroyConnection } from './src/mqtt'; +import { airport, uav, liveInfo, buildGUID } from './src/config'; +import { GetUavPageByDocksn } from '@/api/demo/projecthome'; +import { getVerifyToken, getDroneDockflightInfos } from '@/api/workmanagement/droneDock'; +import TCPlayer from 'tcplayer.js'; + +const bid = buildGUID(); +let video_id; +let sn; +let live_url; + +class LiveStreamPlugin { + constructor() { + console.log('LiveStreamPlugin constructor'); + } + initSDK(token: String) { + /** + * 初始化SDK。token:用国土调查云ak/sk生成的token, + * 分中心进行校验通过才能使用直播插件 + */ + return getVerifyToken(token).then((res) => { + return res; + }); + } + setLiveStreamControl(divContainor: HTMLElement, deviceType: number) { + /** + * 设置视频直播画面控件,用于展示视频直播。 + * divContainor:div元素 + * deviceType: 0无人机 1 机场监控 + */ + console.log('LiveStreamPlugin setLiveStreamControl', divContainor, deviceType); + divContainor.innerHTML = + ' '; + setTimeout(() => { + const player = TCPlayer('player-container-id', { + sources: [ + { + src: live_url + '.flv', // 播放地址 + }, + ], + licenseUrl: live_url + '.flv', // license 地址,必传。参考准备工作部分,在视立方控制台申请 license 后可获得 licenseUrl + autoplay: true, // 是否自动播放 + }); + }, 1000); + } + async startLiveStreamCall(serialNum: String, deviceType: number) { + /** + * 开始视频直播 + * serialNum:无人机机场序列号 + * deviceType: 0无人机 1 机场监控 + */ + console.log('LiveStreamPlugin startLiveStreamCall', serialNum, deviceType); + sn = serialNum; + createConnection('mqtt_' + serialNum); + video_id = + deviceType == 1 + ? serialNum + '/' + airport.camera_index + '/' + airport.video_index + : serialNum + '/' + uav.camera_index + '/' + uav.video_index; + // const liveUrl = liveInfo.rtmp + serialNum; + let querys; + if (deviceType == 1) { + querys = { + bid: bid, + method: 'live_start_push', + tid: buildGUID(), + timestamp: new Date().getTime(), + data: { + url_type: 1, + url: liveInfo.rtmp + '5', + video_id: video_id, + video_quality: 3, + }, + }; + live_url = liveInfo.url + '5'; + } else { + // 根据机场sn获取无人机sn + const params = { + sn: sn, + page: 1, + limit: 99, + }; + await GetUavPageByDocksn(params).then((res) => { + console.log('GetUavPageByDocksn', res); + if (res.items.length > 0) { + querys = { + bid: bid, + method: 'live_start_push', + tid: buildGUID(), + timestamp: new Date().getTime(), + data: { + url_type: 1, + url: liveInfo.rtmp + '6', + video_id: video_id, + video_quality: 3, + }, + }; + } + }); + live_url = liveInfo.url + '5'; + } + // 发送消息 + servicesTopic(serialNum, querys); + // 订阅回执 + client.subscribe('thing/product/' + serialNum + '/services_reply', { qos: 0 }, () => {}); + // 接收消息 + client.on('message', (topic, message) => { + const rs = JSON.parse(message); + if (rs.bid == bid) { + console.log(rs); + console.log('LiveStreamPlugin liveStartPush', rs); + } + }); + } + endLiveStreamCall(deviceType: number) { + /** + * 结束视频直播 + * deviceType: 0无人机 1 机场监控 + */ + console.log('LiveStreamPlugin endLiveStreamCall', deviceType); + const querys = { + bid: bid, + method: 'live_stop_push', + tid: buildGUID(), + timestamp: new Date().getTime(), + data: { + video_id: video_id, + }, + }; + servicesTopic(sn, querys); + } + takePicture(callback) { + /** + * 拍照 + * callback:拍照完成回调方法, + * 参数如下: issuccess:状态 + * true/false; + * imgBlob:照片文件Blob; + * zpkzxx:照片扩展信息 + */ + console.log('LiveStreamPlugin takePicture', callback); + servicesTopic(sn, { + bid: bid, + method: 'camera_photo_take', + tid: buildGUID(), + timestamp: new Date().getTime(), + data: { + payload_index: uav.camera_index, + }, + }); + const data = {}; + callback(data); + } + startVideoRecording() { + /** + * 开始录像 + */ + console.log('LiveStreamPlugin startVideoRecording'); + servicesTopic(sn, { + bid: bid, + method: 'payload_authority_grab', + tid: buildGUID(), + timestamp: new Date().getTime(), + data: { + payload_index: uav.camera_index, + }, + }); + servicesTopic(sn, { + bid: bid, + method: 'camera_recording_start', + tid: buildGUID(), + timestamp: new Date().getTime(), + data: { + payload_index: uav.camera_index, + }, + }); + } + endVideoRecording(callback: Function) { + /** + * 结束录像 + * callback:录像完成回调方法, + * 参数如下: issuccess:状态 + * true/false; + */ + console.log('LiveStreamPlugin endVideoRecording', callback); + servicesTopic(sn, { + bid: bid, + method: 'camera_recording_stop', + tid: buildGUID(), + timestamp: new Date().getTime(), + data: { + payload_index: uav.camera_index, + }, + }); + const data = {}; + callback(data); + } + disposeSDK() { + /** + * 销毁SDK + */ + console.log('LiveStreamPlugin disposeSDK'); + destroyConnection(); + } + async getFlightTaskInfo(serialNum: String) { + /** + * 获取无人机任务信息 + * 参数:serialNum无人机机场序列号 + * 返回结果:FlightTaskInfo + */ + console.log('LiveStreamPlugin getFlightTaskInfo', serialNum); + + client.subscribe('thing/product/' + serialNum + '/events', { qos: 2 }, () => {}); + // 接收消息 + let id = ''; + client.on('message', async (topic, message) => { + const rs = JSON.parse(message); + if ( + rs.method == 'flighttask_progress' && + rs.data.output.status == 'in_progress' && + id == '' + ) { + console.log(rs); + id = rs.data.output.ext.flight_id; + return new Promise((resolve, reject) => { + getDroneDockflightInfos(id).then((res) => { + console.log(res); + resolve(res); + }); + }); + return await getDroneDockflightInfos(id).then((res) => { + return res; + }); + } + }); + } +} + +export default LiveStreamPlugin; diff --git a/src/plugin/video/src/config.ts b/src/plugin/video/src/config.ts new file mode 100644 index 0000000..3722a60 --- /dev/null +++ b/src/plugin/video/src/config.ts @@ -0,0 +1,31 @@ +export const airport = { + camera_index: '165-0-7', + video_index: 'normal-0', +}; +export const uav = { + camera_index: '99-0-0', + video_index: 'normal-0', +}; +export const liveInfo = { + rtmp: 'rtmp://box.wisestcity.com:1935/live/', + url: 'http://box.wisestcity.com:8081/live/', +}; +const hexList: string[] = []; +for (let i = 0; i <= 15; i++) { + hexList[i] = i.toString(16); +} +export function buildGUID(): string { + let guid = ''; + for (let i = 1; i <= 36; i++) { + if (i === 9 || i === 14 || i === 19 || i === 24) { + guid += '-'; + } else if (i === 15) { + guid += 4; + } else if (i === 20) { + guid += hexList[(Math.random() * 4) | 8]; + } else { + guid += hexList[(Math.random() * 16) | 0]; + } + } + return guid; +} diff --git a/src/plugin/video/src/mqtt.ts b/src/plugin/video/src/mqtt.ts new file mode 100644 index 0000000..f630817 --- /dev/null +++ b/src/plugin/video/src/mqtt.ts @@ -0,0 +1,70 @@ +import mqtt from 'mqtt'; + +const connection = { + protocol: 'ws', + host: '175.27.168.120', + port: 6010, + endpoint: '/mqtt', + clean: true, + connectTimeout: 30 * 1000, // ms + reconnectPeriod: 4000, // ms + clientId: '', + username: 'sdhc', + password: '', +}; + +let client: any = { + connected: false, +}; +// 创建连接函数 +const createConnection = (id, callback?) => { + try { + connection.clientId = id; + const { protocol, host, port, endpoint, ...options } = connection; + const connectUrl = `${protocol}://${host}:${port}${endpoint}`; + client = mqtt.connect(connectUrl, options); + client.on('connect', () => { + if (callback) { + callback(); + } + }); + } catch (error) { + console.log('mqtt.connect error', error); + } +}; +// 断开连接 +const destroyConnection = () => { + if (client.connected) { + try { + client.end(false, () => { + client = { + connected: false, + }; + console.log('Successfully disconnected!'); + }); + } catch (error) { + console.log('Disconnect failed', error.toString()); + } + } +}; +// 订阅事件 +const clientSubscribe = (topic: string) => { + client.subscribe(topic, { qos: 0 }, () => {}); +}; +// 发送消息 +// 定义一个函数clientPublish,用于发布消息 +const clientPublish = (topic: string, querys: any) => { + client.publish(topic, JSON.stringify(querys), { qos: 0 }, (err) => { + if (err) { + console.error('Publish error:', err); + } + }); +}; +// 发送消息 +export const servicesTopic = (id, data) => { + console.log(id, data); + // 发送消息 + clientPublish('thing/product/' + id + '/services', data); +}; + +export { createConnection, client, destroyConnection }; diff --git a/src/views/demo/workmanagement/ceshi/index.vue b/src/views/demo/workmanagement/ceshi/index.vue new file mode 100644 index 0000000..031bf64 --- /dev/null +++ b/src/views/demo/workmanagement/ceshi/index.vue @@ -0,0 +1,50 @@ + + diff --git a/src/views/demo/workmanagement/projecthome/index.vue b/src/views/demo/workmanagement/projecthome/index.vue index d913fb3..25d2717 100644 --- a/src/views/demo/workmanagement/projecthome/index.vue +++ b/src/views/demo/workmanagement/projecthome/index.vue @@ -1,7 +1,7 @@ @@ -28,6 +39,7 @@ import Result from './Result/index.vue' import { ref, onMounted, onBeforeUnmount, } from "vue" import { getClient, createConnection, clientSubscribe, destroyConnection } from '@/utils/mqtt'; import { useRouter } from 'vue-router'; +import { AirportLive, LivePreview } from '@/views/demo/workmanagement/flightoperation/index' const router = useRouter(); const airRoute = ref({ @@ -48,6 +60,9 @@ const leftComponentRef = ref(null) const rightComponentRef = ref(null) const map = ref() const connect = ref(false) +const msgData = ref({}); +const airportLiveVisible = ref(false); +const livePreviewVisible = ref(false); const clickAirPort = () => { sessionStorage.setItem('homeToFlightId', activeProject.value); @@ -94,6 +109,12 @@ onBeforeUnmount(() => { const mapOnLoad = (value) => { map.value = value } +const changeAirportLive = (type) => { + airportLiveVisible.value = type +} +const changeUAVLive = (type) => { + livePreviewVisible.value = type +}