石超 2025-06-22 09:02:14 +08:00
commit 9f3bdd049d
15 changed files with 919 additions and 154 deletions

11
src/api/demo/device.ts Normal file
View File

@ -0,0 +1,11 @@
import { defHttp } from '@/utils/http/axios';
enum Api {
GetDataList = '/api/Manage/GetDataList',
}
export function GetDataList(params) {
return defHttp.get({
url: Api.GetDataList,
params
});
}

View File

@ -1,5 +1,15 @@
import { clientReizePublish, clientReizeSubscribe } from '@/utils/mqtt';
export const return_home_status = {
canceled: '取消或终止',
failed: '失败',
in_progress: '执行中',
ok: '执行成功',
paused: '暂停',
rejected: '拒绝',
sent: '已下发',
timeout: '超时',
};
export const eventsTopic = (data) => {
// 发送消息
clientReizePublish('thing/product/8UUXN5400A079H/events', data);

View File

@ -29,3 +29,12 @@ export const services_replyTopic = () => {
// 订阅消息
clientSubscribe('thing/product/8UUXN5400A079H/services_reply');
};
// 无人机
export const servicesUAVTopic = (data) => {
// 发送消息
clientPublish('thing/product/1581F8HGX254V00A0BUY/services', data);
};
export const services_replyUAVTopic = () => {
// 订阅消息
clientSubscribe('thing/product/1581F8HGX254V00A0BUY/services_reply');
};

View File

@ -13,7 +13,7 @@ const connection = {
connectTimeout: 30 * 1000, // ms
reconnectPeriod: 4000, // ms
// clientId: 'mqttx_' + Math.random().toString(16).substring(2, 8),
clientId: 'mqtt_client_1581F8HGX254V00A0BUY',
clientId: `mqtt_${Math.random().toString(16).slice(2)}`,
// auth
username: 'sdhc',
password: '',
@ -43,11 +43,23 @@ const handleOnReConnect = () => {
}
};
// 创建连接函数
const createConnection = () => {
const createConnection = (callback?) => {
try {
const { protocol, host, port, endpoint, ...options } = connection;
const connectUrl = `${protocol}://${host}:${port}${endpoint}`;
client = mqtt.connect(connectUrl, options);
client.on('connect', () => {
console.log('✅ 已连接');
if(callback){
callback()
}
});
client.on('close', () => {
console.log('❌ 连接已关闭');
});
client.on('error', (err) => {
console.error('❌ 出错了:', err);
});
if (client.on) {
}
} catch (error) {
@ -95,6 +107,14 @@ const clientPublish = (topic: string, querys: any) => {
}
});
};
// 获取连接状态
const getConnectStatus = () => {
console.log('client',client)
if (!client.connected) {
return false
}
return true
}
// on 事件
// connect 连接
// reconnect 重新连接
@ -162,4 +182,5 @@ export {
getReizeClient,
clientReizeSubscribe,
clientReizePublish,
getConnectStatus,
};

View File

@ -40,3 +40,32 @@ export function buildShortUUID(prefix = ''): string {
unique++;
return prefix + '_' + random + unique + String(time);
}
export function uuid(len, radix) {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let uuid = [],
i;
radix = radix || chars.length;
if (len) {
// Compact form
for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
} else {
// rfc4122, version 4 form
let r;
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | (Math.random() * 16);
uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}

View File

@ -1,16 +1,29 @@
<template>
<BasicTable class="w-4/4 xl:w-5/5" @register="registerTable">
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="openFeedbackDrawer"></a-button>
<a-button type="primary" @click="openDeviceBindingModal"></a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key == 'workSpaceId'">
{{ props.projectList.find(item => item.value == record.workSpaceId)?.label }}
</template>
<template v-if="column.key == 'first_power_on'">
{{ record.first_power_on? dayjs(record.first_power_on).format('YYYY-MM-DD HH:mm:ss'): '' }}
</template>
<template v-if="column.key == 'compatible_status'">
{{ record.compatible_status? record.compatible_status == 0? "不需要一致性升级": "需要一致性升级": "" }}
</template>
<template v-if="column.key == 'flighttask_step_code'">
{{ workStatus(record)}}
</template>
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: 'ant-design:file-search-outlined',
onClick: () => {
console.log('deviceInfoList',deviceInfoList)
},
},
{
@ -26,24 +39,70 @@
<a-drawer class="feedback-drawer" v-model:open="feedbackDrawer" width="80%" :closable="false">
<FeedbackDrawer />
</a-drawer>
<a-drawer class="feedback-drawer" v-model:open="feedbackDrawer" width="45%" :closable="false">
<FeedbackDrawer />
</a-drawer>
<a-modal v-model:open="deviceBindingModal" title="设备绑定码" @ok="handleOk">
<DeviceBindModal />
</a-modal>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { ref, defineProps, onMounted, watch, nextTick } from "vue"
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { columns, dataSource, searchFormSchema } from './utils'
import { columns, searchFormSchema } from './utils'
import FeedbackDrawer from './FeedbackDrawer/index.vue'
import DeviceBindModal from './DeviceBindModal/index.vue'
import { GetDataList } from '@/api/demo/device'
import { getClient, clientSubscribe } from '@/utils/mqtt'
import dayjs from "dayjs";
const props = defineProps(['projectList','connected'])
watch(() => props.projectList, () => {
const targetField = searchFormSchema.find(item => item.field === 'project');
if (targetField) {
targetField.componentProps = {
...(targetField.componentProps || {}),
options: props.projectList,
};
}
})
watch(() => props.connected, () => {
getClient().on('message', (topic, message) => {
const rs = JSON.parse(message)
console.log('rs',rs)
console.log('getDataSource',getDataSource())
let list = getDataSource()
let sn = rs.gateway
for(let i = 0; i < list.length; i++){
if(list[i].sn == sn){
list[i] = {...list[i],...rs.data}
}
}
setTableData(list)
});
})
const feedbackDrawer = ref(false)
const deviceBindingModal = ref(false)
const [registerTable, { reload, expandAll, getForm}] = useTable({
const afterFetch = ref(false)
const deviceControl = ref(false)
watch(() => [afterFetch.value, props.connected], ([newAfterFetch, newConnected], [oldAfterFetch, oldConnected]) => {
console.log(newAfterFetch,newConnected)
console.log(getDataSource())
if(newConnected && newAfterFetch){
nextTick(() => {
console.log(getDataSource())
getDataSource().forEach(item => {
let topicUrl = `thing/product/${item.sn}/osd`;
clientSubscribe(topicUrl);
})
})
}
})
const [registerTable, { reload, expandAll, getForm,getDataSource,setTableData }] = useTable({
title: '机场',
// api: (params) => LoadCaseInfoLists(type,params),
dataSource,
api: GetDataList,
columns,
rowKey: 'id',
formConfig: {
@ -57,16 +116,15 @@ const [registerTable, { reload, expandAll, getForm}] = useTable({
useSearchForm: true,
//
showTableSetting: true,
bordered: true,
bordered: false,
beforeFetch(data) {
let params = {...data}
if(!params.nowStatus){
params.nowStatus = '复提待审核'
}
return params
return data
},
afterFetch(data) {
console.log('afterFetch', data);
afterFetch.value = true
return data.map(item => {
return {...item, children: item.uavList}
})
},
actionColumn: {
width: 100,
@ -83,6 +141,30 @@ const openFeedbackDrawer = () => {
const openDeviceBindingModal = () => {
deviceBindingModal.value = true
}
const workStatus = (record) => {
if(record.flighttask_step_code){
switch(record.flighttask_step_code){
case 0:
return '作业准备中'
case 1:
return '飞行作业中'
case 2:
return '作业后状态恢复'
case 3:
return '自定义飞行区更新中'
case 4:
return '地形障碍物更新中'
case 5:
return '任务空闲'
case 255:
return '飞行器异常'
case 256:
return '未知状态'
}
}else{
return ''
}
}
</script>
<style lang="scss" scoped>

View File

@ -2,7 +2,7 @@ import { BasicColumn, FormSchema } from '@/components/Table';
export const columns = [
{
title: '设备型号',
dataIndex: 'model',
dataIndex: 'typeId',
},
{
title: '设备SN',
@ -18,7 +18,7 @@ export const columns = [
},
{
title: '固件升级',
dataIndex: 'upgrade',
dataIndex: 'compatible_status',
},
{
title: '飞行安全数据库',
@ -26,11 +26,11 @@ export const columns = [
},
{
title: '工作状态',
dataIndex: 'status',
dataIndex: 'flighttask_step_code',
},
{
title: '所属项目',
dataIndex: 'project',
dataIndex: 'workSpaceId',
},
{
title: '加入组织时间',
@ -38,26 +38,10 @@ export const columns = [
},
{
title: '在线时间',
dataIndex: 'online_time',
dataIndex: 'first_power_on',
},
];
export const dataSource = [
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
{ model: 111, sn:222, name: 333, version: 444, upgrade:'1.0.0', database:'test', status: 555, project: 666, join_time: 777, online_time: 888 },
]
export const searchFormSchema: FormSchema[] = [
{
field: 'upgrade_status',

View File

@ -2,20 +2,51 @@
<div class="device-page">
<a-tabs v-model:activeKey="activeKey" class="device-tabs">
<a-tab-pane key="airport" tab="机场">
<Airport />
<Airport :connected="connected" :projectList="projectList"/>
</a-tab-pane>
<a-tab-pane key="aerocraft" tab="飞行器">
<Aerocraft />
<Aerocraft :projectList="projectList"/>
</a-tab-pane>
</a-tabs>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { ref, onMounted, onBeforeUnmount } from "vue"
import Airport from './Airport/index.vue'
import Aerocraft from './Aerocraft/index.vue'
import { GetWorkspaceList } from '@/api/demo/projecthome'
import { getConnectStatus,createConnection,destroyConnection } from '@/utils/mqtt'
onMounted(() => {
if(!getConnectStatus()){
console.log("需要重连")
createConnection(changeConnect)
connectMQTT.value = true
}else{
changeConnect()
}
GetWorkspaceList().then(res => {
projectList.value = res.map(item => {
return {
label: item.WorkspaceName,
value: item.Id,
}
})
})
})
onBeforeUnmount(() => {
if(connectMQTT.value){
destroyConnection()
}
})
const changeConnect = () => {
connected.value = true
}
const activeKey = ref('airport')
const projectList = ref([])
const connectMQTT = ref(false)
const connected = ref(false)
</script>

View File

@ -6,4 +6,5 @@ export { default as LivePreview } from './src/LivePreview.vue';
export { default as RemoteDebugging } from './src/RemoteDebugging.vue';
export { default as LoadControl } from './src/LoadControl.vue';
export { default as FlightControl } from './src/FlightControl.vue';
export { default as TakeOffForm } from './src/TakeOffForm.vue';
export { default as Map } from '../workplan/components/map.vue';

View File

@ -24,10 +24,14 @@
</div>
<!-- 飞行控制 -->
<div v-if="flightControlVisible">
<FlightControl @changeFlightControl="changeFlightControl" />
<FlightControl @changeFlightControl="changeFlightControl" @clickTakeOff="clickTakeOff" />
</div>
<!-- 一键起飞表单 -->
<div v-if="takeOffFormVisible">
<TakeOffForm @changeTakeOffForm="changeTakeOffForm" />
</div>
<div class="AirportLive" v-if="airportLiveVisible" v-drag>
<AirportLive />
<AirportLive :msgData="msgData" />
</div>
<div
class="LivePreview"
@ -35,7 +39,7 @@
:style="{ bottom: airportLiveVisible ? '300px' : '20px' }"
v-drag
>
<LivePreview />
<LivePreview :msgData="msgData" />
</div>
</div>
</template>
@ -51,6 +55,7 @@
RemoteDebugging,
LoadControl,
FlightControl,
TakeOffForm,
} from './index';
import { useMessage } from '@/hooks/web/useMessage';
import { getClient, createConnection, clientSubscribe } from '@/utils/mqtt';
@ -81,6 +86,8 @@
const loadControlVisible = ref(false);
//
const flightControlVisible = ref(false);
//
const takeOffFormVisible = ref(false);
const changeAirportLive = () => {
airportLiveVisible.value = !airportLiveVisible.value;
};
@ -93,6 +100,13 @@
const changeFlightControl = () => {
flightControlVisible.value = !flightControlVisible.value;
};
const clickTakeOff = () => {
console.log('clickTakeOff');
takeOffFormVisible.value = true;
};
const changeTakeOffForm = () => {
takeOffFormVisible.value = false;
};
const msgData = ref();
const changeSelect = async (value?: any) => {
console.log(value);

View File

@ -4,8 +4,8 @@
<div class="title">机场直播</div>
<div class="title-icon">
<RedoOutlined @click="startLiveFun" />
<a-divider type="vertical" style="border-color: #4e5778" />
<ExpandOutlined />
<!-- <a-divider type="vertical" style="border-color: #4e5778" /> -->
<!-- <ExpandOutlined /> -->
<a-divider type="vertical" style="border-color: #4e5778" />
<PoweroffOutlined @click="closeLive" />
</div>
@ -25,44 +25,116 @@
</template>
<script setup lang="ts">
import { RedoOutlined, ExpandOutlined, PoweroffOutlined } from '@ant-design/icons-vue';
import { onMounted, onBeforeUnmount } from 'vue';
import { onMounted, onBeforeUnmount, watch } from 'vue';
import { startLive, endLive } from '@/api/workmanagement/airportMaintenance';
import TCPlayer from 'tcplayer.js';
import 'tcplayer.js/dist/tcplayer.min.css'; //
import { servicesTopic, services_replyTopic } from '@/utils/debugging/remote';
import { useMessage } from '@/hooks/web/useMessage';
import { buildGUID } from '@/utils/uuid';
const { createMessage } = useMessage();
const props = defineProps({
msgData: Object,
});
let player;
watch(
() => props.msgData,
(val) => {
if (val.topic == 'thing/product/8UUXN5400A079H/services_reply') {
if (val.message.method == 'live_start_push') {
console.log(val);
if (val.message.data.result == 0) {
createMessage.success('开始直播成功');
player.src('http://175.27.168.120:6012/live/2.m3u8');
} else if (val.message.data.result == 513003) {
createMessage.success('直播已开启');
player.src('http://175.27.168.120:6012/live/2.m3u8');
} else {
createMessage.error('开始直播失败');
}
} else if (val.message.method == 'live_stop_push') {
if (val.message.data.result == 0) {
createMessage.success('停止直播成功');
if (player) {
// player.dispose();
}
} else {
createMessage.error('停止直播失败');
}
}
}
},
);
const startLiveFun = () => {
const querys = {
urlType: 1, // 0 = RTMP 1GB28181 3WebRTC 4
url: 'rtmp://221.2.83.254:1935/live/2',
//video_id = "1581F8HGX254V00A0BUY/0-100-1/normal-0",
videoId: '8UUXN5400A079H/165-0-7/normal-0',
quality: 1, // 0=1=2=3=4=
bid: buildGUID(),
method: 'live_start_push',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
url_type: 1, // 0 = RTMP 1GB28181 3WebRTC 4
url: 'rtmp://175.27.168.120:6019/live/2',
//video_id = "1581F8HGX254V00A0BUY/0-100-1/normal-0",
video_id: '8UUXN5400A079H/165-0-7/normal-0',
video_quality: 1, // 0=1=2=3=4=
},
};
startLive(querys).then((res) => {
console.log(res);
servicesTopic(querys);
services_replyTopic();
// const querys = {
// urlType: 1, // 0 = RTMP 1GB28181 3WebRTC 4
// url: 'rtmp://175.27.168.120:6019/live/2',
// //video_id = "1581F8HGX254V00A0BUY/0-100-1/normal-0",
// videoId: '8UUXN5400A079H/165-0-7/normal-0',
// quality: 1, // 0=1=2=3=4=
// };
// startLive(querys).then((res) => {
// console.log(res);
// player = TCPlayer('player-container-id', {
// width: 360,
// height: 200,
// sources: [
// {
// src: 'http://175.27.168.120:6012/live/2.m3u8', //
// },
// ],
// licenseUrl: 'http://175.27.168.120:6012/live/2.m3u8', // license license licenseUrl
// });
// });
};
const closeLive = () => {
const querys = {
bid: buildGUID(),
method: 'live_stop_push',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
video_id: '8UUXN5400A079H/165-0-7/normal-0',
},
};
servicesTopic(querys);
// endLive({
// videoId: '8UUXN5400A079H/165-0-7/normal-0',
// }).then((res) => {
// console.log(res);
// player.dispose();
// });
};
onMounted(() => {
setTimeout(() => {
startLiveFun();
player = TCPlayer('player-container-id', {
width: 360,
height: 200,
sources: [
{
src: 'http://221.2.83.254:7005/live/2.m3u8', //
src: 'http://175.27.168.120:6012/live/2.m3u8', //
},
],
licenseUrl: 'http://221.2.83.254:7005/live/2.m3u8', // license license licenseUrl
licenseUrl: 'http://175.27.168.120:6012/live/2.m3u8', // license license licenseUrl
});
});
};
const closeLive = () => {
endLive({
videoId: '8UUXN5400A079H/165-0-7/normal-0',
}).then((res) => {
console.log(res);
player.dispose();
});
};
onMounted(() => {
// startLiveFun();
}, 1000);
});
//
onBeforeUnmount(() => {
@ -107,5 +179,11 @@
border: 1px solid #3a57e8;
}
}
.player {
height: 200px;
#player-container-id-live {
height: 100%;
}
}
}
</style>

View File

@ -8,38 +8,49 @@
</div>
<div class="content">
<div class="content-button">
<a-button>获取飞行器控制器</a-button>
<a-button>一键起飞</a-button>
<a-button @click="obtain"></a-button>
<a-button @click="emits('clickTakeOff')"></a-button>
<a-button>指点飞行</a-button>
<a-button>智能环绕</a-button>
<a-button>一键返航</a-button>
<!-- <a-button>智能环绕</a-button> -->
<a-button @click="returnVoyage"></a-button>
</div>
<div class="content-info">
<div class="info-item">
<img src="@/assets/images/flightoperation/flight_control.png" alt="" />
<div class="info-item-top"></div>
<div class="info-item-right"></div>
<div class="info-item-bottom"></div>
<div class="info-item-left"></div>
<div class="info-item-top" title="上升" @click="changeDRC('throttle', 'up')"></div>
<div class="info-item-right" title="右旋转" @click="changeDRC('yaw', 'up')"></div>
<div class="info-item-bottom" title="下降" @click="changeDRC('throttle', 'down')"></div>
<div class="info-item-left" title="左旋转" @click="changeDRC('yaw', 'down')"></div>
</div>
<div class="info-item">
<img src="@/assets/images/flightoperation/flight_control.png" alt="" />
<div class="info-item-top"></div>
<div class="info-item-right"></div>
<div class="info-item-bottom"></div>
<div class="info-item-left"></div>
<div class="info-item-top" title="前进" @click="changeDRC('pitch', 'up')"></div>
<div class="info-item-right" title="右移" @click="changeDRC('roll', 'up')"></div>
<div class="info-item-bottom" title="后退" @click="changeDRC('pitch', 'down')"></div>
<div class="info-item-left" title="左移" @click="changeDRC('roll', 'down')"></div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { onMounted, ref, watch, reactive } from 'vue';
import { getClient, createConnection } from '@/utils/mqtt';
import { CloseOutlined } from '@ant-design/icons-vue';
import { vDrag } from '@/utils/drag';
import { getReizeClient, createSeizeConnection, clientReizePublish } from '@/utils/mqtt';
import {
eventsTopic,
events_replyTopic,
drcDownTopic,
servicesTopic,
return_home_status,
} from '@/utils/debugging/events';
import { buildGUID } from '@/utils/uuid';
import { useMessage } from '@/hooks/web/useMessage';
const emits = defineEmits(['changeFlightControl']);
const { createMessage } = useMessage();
const emits = defineEmits(['changeFlightControl', 'clickTakeOff']);
const props = defineProps({
airportAllVal: Object,
});
@ -58,6 +69,13 @@
capacity_percent: 0,
},
});
const data = reactive({
roll: 1024,
pitch: 1024,
throttle: 1024,
yaw: 1024,
gimbal_pitch: 1024,
});
watch(
() => props.airportAllVal,
(val) => {
@ -65,6 +83,65 @@
airportVal.value = val.data;
},
);
//
const obtain = () => {
if (!getReizeClient()) {
createSeizeConnection();
}
setTimeout(() => {
//
eventsTopic({
bid: buildGUID(),
method: 'flight_authority_grab',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {},
});
//
eventsTopic({
bid: buildGUID(),
method: 'payload_authority_grab',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
payload_index: '99-0-0',
},
});
events_replyTopic();
// //
getReizeClient().on('message', (topic, message) => {
const rs = JSON.parse(message);
console.log(rs);
//
if (rs.method === 'return_home') {
createMessage.info(return_home_status[rs.data.output.status]);
}
});
}, 1000);
};
//
const returnVoyage = () => {
servicesTopic({
bid: buildGUID(),
method: 'return_home',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {},
});
};
const changeDRC = (type, value) => {
if (value == 'up') {
data[type] = data[type] + 20;
} else {
data[type] = data[type] - 20;
}
const querys = {
seq: 1,
method: 'stick_control',
data: data,
};
drcDownTopic(querys);
};
onMounted(() => {});
</script>
<style lang="less" scoped>

View File

@ -3,6 +3,7 @@
<div class="preview-title">
<div class="title-select">
<div class="title-select-item">
<span>镜头:</span>
<a-select
ref="select"
v-model:value="selectVal.camera"
@ -23,9 +24,9 @@
</div>
</div>
<div class="title-icon">
<RedoOutlined />
<a-divider type="vertical" style="border-color: #4e5778" />
<ExpandOutlined />
<RedoOutlined @click="startLiveFun" />
<!-- <a-divider type="vertical" style="border-color: #4e5778" /> -->
<!-- <ExpandOutlined /> -->
<a-divider type="vertical" style="border-color: #4e5778" />
<PoweroffOutlined @click="closeLive" />
</div>
@ -46,7 +47,7 @@
</template>
<script setup lang="ts">
import { RedoOutlined, ExpandOutlined, PoweroffOutlined } from '@ant-design/icons-vue';
import { reactive, onMounted, ref } from 'vue';
import { reactive, onMounted, ref, watch } from 'vue';
import { buildGUID } from '@/utils/uuid';
import { getClient, createConnection } from '@/utils/mqtt';
import {
@ -57,21 +58,79 @@
} from '@/api/workmanagement/airportMaintenance';
import TCPlayer from 'tcplayer.js';
import 'tcplayer.js/dist/tcplayer.min.css'; //
import { servicesUAVTopic, services_replyUAVTopic } from '@/utils/debugging/remote';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage } = useMessage();
const props = defineProps({
msgData: Object,
});
let player;
watch(
() => props.msgData,
(val) => {
if (val.topic == 'thing/product/1581F8HGX254V00A0BUY/services_reply') {
if (val.message.method == 'live_start_push') {
console.log(val);
if (val.message.data.result == 0) {
createMessage.success('开始直播成功');
player.src('http://175.27.168.120:6012/live/3.m3u8');
} else if (val.message.data.result == 513003) {
createMessage.success('直播已开启');
player.src('http://175.27.168.120:6012/live/3.m3u8');
} else {
createMessage.error('开始直播失败');
}
} else if (val.message.method == 'live_stop_push') {
if (val.message.data.result == 0) {
createMessage.success('停止直播成功');
if (player) {
// player.dispose();
}
} else {
createMessage.error('停止直播失败');
}
} else if (val.message.method == 'live_set_quality') {
if (val.message.data.result == 0) {
createMessage.success('设置清晰度成功');
player.src('http://175.27.168.120:6012/live/3.m3u8');
} else {
createMessage.error('设置清晰度失败');
}
} else if (val.message.method == 'live_lens_change') {
console.log(val);
if (val.message.data.result == 0) {
createMessage.success('设置直播镜头成功');
player.src('http://175.27.168.120:6012/live/3.m3u8');
} else {
createMessage.error('设置直播镜头失败');
}
}
}
},
);
const selectVal = reactive({
camera: 1,
camera: 'normal',
resolution: 1,
});
const optionsArr = reactive({
cameraOptions: [
{
label: '相机',
value: 0,
label: '红外',
value: 'ir',
},
{
label: '直播',
value: 1,
label: '默认',
value: 'normal',
},
{
label: '广角',
value: 'wide',
},
{
label: '变焦',
value: 'zoom',
},
],
resolutionOptions: [
@ -99,38 +158,77 @@
});
const startLiveFun = () => {
const querys = {
urlType: 1, // 0 = RTMP 1GB28181 3WebRTC 4
url: 'rtmp://221.2.83.254:1935/live/3',
//video_id = "1581F8HGX254V00A0BUY/0-100-1/normal-0",
videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
quality: 1, // 0=1=2=3=4=
bid: buildGUID(),
method: 'live_start_push',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
url_type: 1, // 0 = RTMP 1GB28181 3WebRTC 4
url: 'rtmp://175.27.168.120:6019/live/3',
//video_id = "1581F8HGX254V00A0BUY/0-100-1/normal-0",
video_id: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
video_quality: 1, // 0=1=2=3=4=
},
};
startLive(querys).then((res) => {
console.log(res);
playVideo();
});
servicesUAVTopic(querys);
services_replyUAVTopic();
// const querys = {
// urlType: 1, // 0 = RTMP 1GB28181 3WebRTC 4
// // url: 'rtmp://221.2.83.254:1935/live/3',
// url: 'rtmp://175.27.168.120:6019/live/3',
// //video_id = "1581F8HGX254V00A0BUY/0-100-1/normal-0",
// videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
// quality: 1, // 0=1=2=3=4=
// };
// startLive(querys).then((res) => {
// console.log(res);
// playVideo();
// });
};
const resolutionChange = (val: any) => {
const querys = {
videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
videoQuality: val, // 0=1=2=3=4=
bid: buildGUID(),
method: 'live_set_quality',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
video_id: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
video_quality: val, // 0=1=2=3=4=
},
};
setCameraVideo(querys).then((res) => {
console.log(res);
playVideo();
});
servicesUAVTopic(querys);
// const querys = {
// videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
// videoQuality: val, // 0=1=2=3=4=
// };
// setCameraVideo(querys).then((res) => {
// console.log(res);
// playVideo();
// });
};
const cameraChange = (val: any) => {
console.log(val);
const requestData = {
videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
position: val, // 0=1=2=3=4=
camera: '99-0-0',
const querys = {
bid: buildGUID(),
method: 'live_lens_change',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
video_id: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
video_type: val, // 0=1=2=3=4=
},
};
exchangeCamera(requestData).then((res) => {
console.log(res);
playVideo();
});
console.log(querys);
servicesUAVTopic(querys);
// const requestData = {
// videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
// position: val, // 0=1=2=3=4=
// camera: '99-0-0',
// };
// exchangeCamera(requestData).then((res) => {
// console.log(res);
// playVideo();
// });
};
const data = reactive({
playedTime: 0,
@ -143,21 +241,34 @@
height: 180,
sources: [
{
src: 'http://221.2.83.254:7005/live/3.m3u8', //
src: 'http://175.27.168.120:6012/live/3.m3u8', //
},
],
licenseUrl: 'http://221.2.83.254:7005/live/3.m3u8', // license license licenseUrl
licenseUrl: 'http://175.27.168.120:6012/live/3.m3u8', // license license licenseUrl
});
};
const closeLive = () => {
endLive({
videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
}).then((res) => {
console.log(res);
});
const querys = {
bid: buildGUID(),
method: 'live_stop_push',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
video_id: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
},
};
servicesUAVTopic(querys);
// endLive({
// videoId: '1581F8HGX254V00A0BUY/165-0-7/normal-0',
// }).then((res) => {
// console.log(res);
// });
};
onMounted(() => {
// startLiveFun();
setTimeout(() => {
startLiveFun();
playVideo();
}, 1000);
// resolutionChange(1);
});
</script>
@ -241,5 +352,11 @@
top: 100px;
z-index: 3;
}
.player {
height: 180px;
#player-container-id-live {
height: 100%;
}
}
}
</style>

View File

@ -21,10 +21,22 @@
</div>
<div class="direction-controller">
<img src="@/assets/images/flightoperation/direction_controller.png" alt="" />
<div class="direction-controller-top" @click="changeDrc('top')"></div>
<div class="direction-controller-right" @click="changeDrc('right')"></div>
<div class="direction-controller-bottom" @click="changeDrc('bottom')"></div>
<div class="direction-controller-left" @click="changeDrc('left')"></div>
<div class="direction-controller-top" title="前进" @click="changeDrc('pitch', 'top')"></div>
<div
class="direction-controller-right"
title="右移"
@click="changeDrc('roll', 'right')"
></div>
<div
class="direction-controller-bottom"
title="后退"
@click="changeDrc('pitch', 'bottom')"
></div>
<div
class="direction-controller-left"
title="左移"
@click="changeDrc('roll', 'left')"
></div>
</div>
</div>
</div>
@ -73,25 +85,30 @@
);
const obtain = () => {
//
eventsTopic({
bid: buildGUID(),
method: 'flight_authority_grab',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {},
});
//
eventsTopic({
bid: buildGUID(),
method: 'payload_authority_grab',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
payload_index: '99-0-0',
},
});
events_replyTopic();
if (!getReizeClient()) {
createSeizeConnection();
}
setTimeout(() => {
//
eventsTopic({
bid: buildGUID(),
method: 'flight_authority_grab',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {},
});
//
eventsTopic({
bid: buildGUID(),
method: 'payload_authority_grab',
tid: buildGUID(),
timestamp: new Date().getTime(),
data: {
payload_index: '99-0-0',
},
});
events_replyTopic();
}, 1000);
};
//
const singleShot = () => {
@ -107,15 +124,11 @@
});
};
//
const changeDrc = (val) => {
if (val == 'top') {
drcVal.pitch = drcVal.pitch + 20;
} else if (val == 'bottom') {
drcVal.pitch = drcVal.pitch - 20;
} else if (val == 'left') {
drcVal.roll = drcVal.roll - 20;
} else if (val == 'right') {
drcVal.roll = drcVal.roll + 20;
const changeDrc = (type, val) => {
if (val == 'up') {
drcVal[type] = drcVal[type] + 20;
} else {
drcVal[type] = drcVal[type] - 20;
}
const querys = {
seq: 1,
@ -125,7 +138,6 @@
drcDownTopic(querys);
};
onMounted(() => {
// createSeizeConnection();
// //
// getReizeClient().on('message', (topic, message) => {
// const rs = JSON.parse(message);

View File

@ -0,0 +1,289 @@
<template>
<div class="takeoff-information">
<div class="title"
>一键起飞
<div @click="emits('changeTakeOffForm')">
<CloseOutlined />
</div>
</div>
<div class="content">
<div class="content-edit">
目标点纬度
<div>
<a-input v-model:value="data.target_latitude" readonly />
</div>
</div>
<div class="content-edit">
目标点经度
<div>
<a-input v-model:value="data.target_longitude" readonly />
</div>
</div>
<div class="content-edit">
目标点高度
<div>
<a-input v-model:value="data.target_height" />
</div>
</div>
<div class="content-edit">
安全起飞高度
<div>
<a-input v-model:value="data.security_takeoff_height" />
</div>
</div>
<div class="content-edit">
返航模式
<div>
<a-select v-model:value="data.rth_mode" :options="rth_modeOptions" />
</div>
</div>
<div class="content-edit">
返航高度
<div>
<a-input v-model:value="data.rth_altitude" />
</div>
</div>
<div class="content-edit">
遥控器失控动作
<div>
<a-select v-model:value="data.rc_lost_action" :options="rc_lost_actionOptions" />
</div>
</div>
<div class="content-edit">
指点飞行失控动作
<div>
<a-select
v-model:value="data.commander_mode_lost_action"
:options="commander_mode_lost_actionOptions"
/>
</div>
</div>
<div class="content-edit">
指点飞行模式
<div>
<a-select
v-model:value="data.commander_flight_mode"
:options="commander_flight_modeOptions"
/>
</div>
</div>
<div class="content-edit">
指点飞行高度
<div>
<a-input v-model:value="data.commander_flight_height" />
</div>
</div>
<div class="content-button">
<a-button
type="primary"
style="background: #0a99eb; width: 40%"
@click="emits('changeTakeOffForm')"
>关闭</a-button
>
<a-button type="primary" style="background: #3a57e8; width: 40%" @click="takeOff"
>起飞</a-button
>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, watch } from 'vue';
import { CopyOutlined, EditOutlined } from '@ant-design/icons-vue';
import { timestampToFormattedDate } from '@/utils/index';
import { buildGUID, uuid } from '@/utils/uuid';
import { servicesTopic, services_replyTopic } from '@/utils/debugging/events';
import { vDrag } from '@/utils/drag';
import { CloseOutlined } from '@ant-design/icons-vue';
const emits = defineEmits(['changeTakeOffForm']);
const props = defineProps({
msgData: Object,
});
const data = reactive({
flight_id: uuid(14, 14),
target_latitude: 35.134615,
target_longitude: 118.296676,
target_height: 115,
security_takeoff_height: 100,
rth_altitude: 115,
rth_mode: 1,
max_speed: 10,
commander_flight_mode: 1,
rc_lost_action: 2,
commander_mode_lost_action: 1,
commander_flight_height: 115.0,
flight_safety_advance_check: 1,
simulate_mission: {
is_enable: 1, // {"0":"","1":""}
latitude: 35.143567, //
longitude: 118.305623, //
},
});
const uavInformation = ref({
sub_device: {
//
device_online_status: 0,
},
drone_battery_maintenance_info: {
//
capacity_percent: 0,
},
wireless_link: {
// Dongle
dongle_number: 0,
sdr_quality: 0,
'4g_quality': 0,
'4g_uav_quality': 0,
'4g_gnd_quality': 0,
remain_upload: 0,
sdr_link_state: 0,
sdr_freq_band: 0,
},
});
const time = ref(timestampToFormattedDate(new Date().getTime()));
const rth_modeOptions = ref([
{ label: '智能高度', value: 0 },
{ label: '设定高度', value: 1 },
]);
const rc_lost_actionOptions = ref([
{ label: '悬停', value: 0 },
{ label: '着陆(降落)', value: 1 },
{ label: '返航', value: 2 },
]);
const commander_mode_lost_actionOptions = ref([
{ label: '继续执行指点飞行任务', value: 0 },
{ label: '退出指点飞行任务,执行普通失控行为', value: 1 },
]);
const commander_flight_modeOptions = ref([
{ label: '智能高度飞行', value: 0 },
{ label: '设定高度飞行', value: 1 },
]);
watch(
() => props.msgData,
(val) => {
if (val.topic == 'thing/product/8UUXN5400A079H/osd') {
if (
val.message.data.sub_device ||
val.message.data.drone_battery_maintenance_info ||
val.message.data.wireless_link
) {
// console.log(val);
uavInformation.value = val.message.data;
time.value = timestampToFormattedDate(val.message.timestamp);
}
}
},
);
const takeOff = () => {
const querys = {
bid: buildGUID(),
data: data,
tid: buildGUID(),
timestamp: new Date().getTime(),
method: 'takeoff_to_point',
};
console.log(querys);
// servicesTopic(querys);
setTimeout(() => {
emits('changeTakeOffForm');
}, 1000);
};
</script>
<style lang="less" scoped>
.takeoff-information {
position: absolute;
top: 120px;
left: 300px;
z-index: 999;
width: 460px;
padding: 10px;
background: #0d0e15;
margin: 10px 0 0 10px;
box-shadow:
0px 10px 30px 0px rgba(0, 0, 6, 0.15),
inset 0px 0px 36px 0px rgba(58, 87, 232, 0.73);
border-radius: 6px;
backdrop-filter: blur(3px);
color: #fff;
.title {
width: 100%;
padding: 10px 0;
box-shadow: 0px 10px 30px 0px rgba(0, 0, 6, 0.15);
border-bottom: 1px solid #4e5778;
display: flex;
justify-content: space-between;
align-items: center;
span {
cursor: pointer;
}
}
.content-title {
font-size: 14px;
padding: 10px 0;
span {
font-size: 12px;
}
}
.content-item {
display: flex;
align-items: center;
border-bottom: 1px solid #4e5778;
padding: 10px 0;
.item-div {
width: 49%;
img {
width: 20px;
}
}
}
.content-button {
margin-top: 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
.content-edit {
display: flex;
align-items: center;
justify-content: space-between;
// border: 1px solid #4e5778;
margin-top: 10px;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
input {
background: none;
width: 260px;
text-align: right;
color: #fff;
font-size: 12px;
}
::v-deep .ant-input-number,
.ant-input-number-group-wrapper {
background: none;
width: 260px !important;
text-align: right;
color: #fff;
font-size: 12px;
}
::v-deep .ant-input-number-group-wrapper {
input {
background: none;
width: 260px !important;
text-align: right;
color: #fff;
font-size: 12px;
}
}
::v-deep .ant-select-selector {
background: none;
width: 260px;
text-align: right;
color: #fff;
font-size: 12px;
}
}
}
</style>