<template>
|
<div class="pointControl">
|
<div class="manualControl"></div>
|
|
<div class="direction">
|
<div class="boxTitle">
|
飞
|
<br />
|
行
|
<br />
|
控
|
<br />
|
制
|
<br />
|
器
|
</div>
|
<div class="btnGroup">
|
<div class="btnGroupT">
|
<div class="btnItem" v-for="item in list1">
|
<el-icon class="btnIcon">
|
<component :is="item.icon" />
|
</el-icon>
|
<div class="btn" @mousedown="onMouseDown(item.key)" @mouseup="onMouseUp">{{ item.text }}</div>
|
</div>
|
</div>
|
<div class="btnGroupB">
|
<div class="btnItem" v-for="item in list2">
|
<div class="btn" @mousedown="onMouseDown(item.key)" @mouseup="onMouseUp">{{ item.text }}</div>
|
<el-icon class="btnIcon">
|
<component :is="item.icon" />
|
</el-icon>
|
</div>
|
</div>
|
</div>
|
|
<div class="speed">
|
<el-icon class="btnIcon">
|
<Plus />
|
</el-icon>
|
<div>5<br>m/s</div>
|
<el-icon class="btnIcon">
|
<Minus />
|
</el-icon>
|
</div>
|
|
|
<div class="upAndDown">
|
<div class="btnGroupT">
|
<div class="btnItem">
|
<el-icon class="btnIcon">
|
<Top />
|
</el-icon>
|
<div class="btn" @mousedown="onMouseDown(KeyCode.ARROW_UP)" @mouseup="onMouseUp">C</div>
|
</div>
|
</div>
|
<div class="btnGroupT">
|
<div class="btnItem">
|
<div class="btn" @mousedown="onMouseDown(KeyCode.ARROW_DOWN)" @mouseup="onMouseUp">Z</div>
|
<el-icon class="btnIcon">
|
<Bottom />
|
</el-icon>
|
</div>
|
</div>
|
</div>
|
|
</div>
|
|
<div class="compass">
|
<ControlComPass />
|
</div>
|
|
<div class="ptzControl">
|
<div>
|
云
|
<br />
|
台
|
<br />
|
控
|
<br />
|
制
|
</div>
|
<div></div>
|
</div>
|
</div>
|
</template>
|
<script setup>
|
import ControlComPass from './ControlComPass/ControlComPass.vue'
|
import { KeyCode, useManualControl } from '@/hooks/controlDrone/useManualControl'
|
import { droneController, exitController, postDrc, postDrcExit } from '@/api/drc'
|
import { ElMessage } from 'element-plus'
|
import { useStore } from 'vuex'
|
import { UranusMqtt } from '@/mqtt'
|
import {
|
ArrowDown,
|
ArrowLeft,
|
ArrowRight,
|
ArrowUp,
|
Bottom, Minus,
|
Plus,
|
RefreshLeft,
|
RefreshRight,
|
} from '@element-plus/icons-vue'
|
|
const deviceOsdInfo = inject('deviceOsdInfo')
|
const taskDetails = inject('taskDetails')
|
const store = useStore()
|
const workspace_id = computed(() => taskDetails.value.way_lines[0].workspace_id)
|
const dock_sn = computed(() => taskDetails.value.device_sns[0])
|
const list1 = [
|
{ key: KeyCode.KEY_Q, text: 'Q', icon: RefreshLeft },
|
{ key: KeyCode.KEY_W, text: 'W', icon: ArrowUp },
|
{ key: KeyCode.KEY_E, text: 'E', icon: RefreshRight },
|
]
|
const list2 = [
|
{ key: KeyCode.KEY_A, text: 'A', icon: ArrowLeft },
|
{ key: KeyCode.KEY_S, text: 'S', icon: ArrowDown },
|
{ key: KeyCode.KEY_D, text: 'D', icon: ArrowRight },
|
]
|
|
let mqttState = null
|
const client_id = ref('')
|
|
const deviceTopicInfo = ref({
|
sn: deviceOsdInfo.value?.data?.sn,
|
pubTopic: '',
|
subTopic: '',
|
})
|
const flightController = ref(false)
|
console.log('控制面板')
|
|
// 控制对象
|
let manualControl = {}
|
|
function onMouseDown(type) {
|
manualControl?.handleKeyup(type)
|
}
|
|
function onMouseUp() {
|
console.log('弹起')
|
manualControl?.resetControlState()
|
}
|
|
// 取消手动控制
|
function cancelControl() {
|
exitController({ client_id: client_id.value, dock_sn: dock_sn.value })
|
.then(res => {
|
flightController.value = false
|
deviceTopicInfo.value.subTopic = ''
|
deviceTopicInfo.value.pubTopic = ''
|
ElMessage.success('退出飞行控制成功')
|
})
|
.catch(e => {})
|
}
|
|
// 手动控制
|
function control() {
|
if (!client_id.value) return ElMessage.error('无人机不在空中,不能进入指挥飞行模式。')
|
if (!dock_sn.value) return ElMessage.error('系统错误,未获取到dock_sn')
|
droneController({ client_id: client_id.value, dock_sn: dock_sn.value }).then(res => {
|
flightController.value = true
|
const { data } = res.data
|
if (data.sub && data.sub?.length > 0) {
|
deviceTopicInfo.value.subTopic = data.sub[0]
|
}
|
if (data.pub && data.pub?.length > 0) {
|
deviceTopicInfo.value.pubTopic = data.pub[0]
|
}
|
ElMessage.success('控制成功')
|
})
|
}
|
|
// 创建连接
|
const createConnect = async () => {
|
const result = await postDrc({}, workspace_id.value)
|
if (result?.code === 0) {
|
const { address, client_id: clientId, username, password, expire_time } = result.data
|
mqttState = new UranusMqtt(address, { clientId, username, password })
|
mqttState?.initMqtt()
|
client_id.value = clientId
|
}
|
}
|
|
// 销毁连接
|
const destroyConnect = () => {
|
if (mqttState) {
|
mqttState?.destroyed()
|
mqttState = null
|
client_id.value = ''
|
}
|
}
|
|
onMounted(async () => {
|
await createConnect()
|
// 使用控制
|
manualControl = useManualControl(mqttState, deviceTopicInfo.value, flightController)
|
})
|
|
onBeforeUnmount(() => {
|
destroyConnect()
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.f-c {
|
display: flex;
|
align-items: center;
|
}
|
|
|
.pointControl {
|
position: absolute;
|
bottom: 0;
|
right: 0;
|
width: 1540px;
|
height: 217px;
|
background: rgb(0, 0, 0, 0.4); /* 半透明背景 */
|
backdrop-filter: blur(5px);
|
border-radius: 40px 0px 40px 40px;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
color: white;
|
gap: 0 10px;
|
pointer-events: all;
|
|
.direction {
|
width: 476px;
|
height: 188px;
|
background: rgb(0, 0, 0, 0.4); /* 半透明背景 */
|
border-radius: 40px 40px 40px 40px;
|
display: flex;
|
align-items: center;
|
justify-content: space-around;
|
.boxTitle{
|
font-family: Segoe UI, Segoe UI;
|
font-weight: 400;
|
font-size: 14px;
|
color: #D2E8FA;
|
}
|
|
.btnGroup {
|
display: flex;
|
flex-direction: column;
|
gap: 10px 0;
|
.btnGroupT,
|
.btnGroupB {
|
width: 238px;
|
height: 73px;
|
}
|
}
|
.upAndDown{
|
display: flex;
|
flex-direction: column;
|
gap: 10px 0;
|
.btnGroupT,
|
.btnGroupB {
|
width: 58px;
|
height: 73px;
|
}
|
}
|
.speed{
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
align-items: center;
|
width: 58px;
|
height: 155px;
|
background: #37393F;
|
box-shadow: 2px 4px 6px 0px rgba(0,13,26,0.42);
|
border-radius: 8px 8px 8px 8px;
|
text-align: center;
|
padding: 5px 0;
|
.btnIcon{
|
font-size: 20px;
|
}
|
}
|
|
}
|
|
.manualControl {
|
width: 188px;
|
height: 188px;
|
background: rgb(0, 0, 0, 0.4); /* 半透明背景 */
|
border-radius: 40px 40px 40px 40px;
|
}
|
|
.ptzControl {
|
width: 406px;
|
height: 188px;
|
background: rgb(0, 0, 0, 0.4); /* 半透明背景 */
|
border-radius: 40px 40px 40px 40px;
|
}
|
|
.compass {
|
width: 356px;
|
height: 188px;
|
background: rgba(157, 173, 189, 0.11);
|
background: rgb(0, 0, 0, 0.4); /* 半透明背景 */
|
border-radius: 40px 40px 40px 40px;
|
}
|
|
|
.btnGroupT,
|
.btnGroupB {
|
background: #37393f;
|
box-shadow: 2px 4px 6px 0px rgba(0, 13, 26, 0.42);
|
border-radius: 8px 8px 8px 8px;
|
display: flex;
|
align-items: center;
|
text-align: center;
|
justify-content: center;
|
gap: 0 45px;
|
|
.btnItem {
|
.btnIcon {
|
font-size: 20px;
|
|
&:first-child {
|
margin-bottom: 5px;
|
}
|
}
|
|
.btn {
|
width: 35px;
|
height: 35px;
|
background: #222324;
|
line-height: 35px;
|
border-radius: 5px;
|
cursor: pointer;
|
|
&:first-child {
|
margin-bottom: 5px;
|
}
|
}
|
}
|
}
|
}
|
</style>
|