| | |
| | | <template> |
| | | <div class="pointControl"> |
| | | <div class="manualControl"></div> |
| | | |
| | | <div class="direction"> |
| | | <div class="blackBg directionUp"> |
| | | <el-button type="primary" @click="control">控制</el-button> |
| | | <el-button type="primary" @click="cancelControl">取消控制</el-button> |
| | | <el-button type="primary" ghost @mousedown="onMouseDown(KeyCode.KEY_Q)" @mouseup="onMouseUp">q</el-button> |
| | | <div class="boxTitle"> |
| | | 飞 |
| | | <br /> |
| | | 行 |
| | | <br /> |
| | | 控 |
| | | <br /> |
| | | 制 |
| | | <br /> |
| | | 器 |
| | | </div> |
| | | <div class="blackBg directionDown"></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> |
| | | |
| | | <ControlComPass /> |
| | | <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 { useMqtt } from '@/hooks/controlDrone/useMqtt' |
| | | import { useConnectDrone } from '@/hooks/controlDrone/useConnectDrone' |
| | | import { droneController, exitController, postDrcExit } from '@/api/drc' |
| | | 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, |
| | |
| | | const flightController = ref(false) |
| | | console.log('控制面板') |
| | | |
| | | // 连接无人机mqtt 成功获得有效控制 |
| | | useConnectDrone() |
| | | |
| | | // 订阅消息 |
| | | useMqtt(deviceTopicInfo.value) |
| | | |
| | | // 使用手动控制 |
| | | const { handleKeyup, handleEmergencyStop, resetControlState } = useManualControl( |
| | | deviceTopicInfo.value, |
| | | flightController |
| | | ) |
| | | // 控制对象 |
| | | let manualControl = {} |
| | | |
| | | function onMouseDown(type) { |
| | | console.log('anxia') |
| | | handleKeyup(type) |
| | | manualControl?.handleKeyup(type) |
| | | } |
| | | |
| | | const store = useStore() |
| | | const clientId = computed(() => store.state.common.clientId) |
| | | const dock_sn = computed(() => taskDetails.value.device_sns[0]) |
| | | function onMouseUp() { |
| | | console.log('弹起') |
| | | manualControl?.resetControlState() |
| | | } |
| | | |
| | | // 取消手动控制 |
| | | function cancelControl() { |
| | | exitController({ client_id: clientId.value, dock_sn:dock_sn.value }) |
| | | exitController({ client_id: client_id.value, dock_sn: dock_sn.value }) |
| | | .then(res => { |
| | | flightController.value = false |
| | | deviceTopicInfo.value.subTopic = '' |
| | |
| | | .catch(e => {}) |
| | | } |
| | | |
| | | // 控制 |
| | | // 手动控制 |
| | | function control() { |
| | | if (!clientId.value) return ElMessage.error('无人机不在空中,不能进入指挥飞行模式。') |
| | | if (!client_id.value) return ElMessage.error('无人机不在空中,不能进入指挥飞行模式。') |
| | | if (!dock_sn.value) return ElMessage.error('系统错误,未获取到dock_sn') |
| | | droneController({ client_id: clientId.value, dock_sn:dock_sn.value }).then(res => { |
| | | 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) { |
| | |
| | | }) |
| | | } |
| | | |
| | | function onMouseUp() { |
| | | console.log('弹起') |
| | | resetControlState() |
| | | // 创建连接 |
| | | 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"> |
| | |
| | | align-items: center; |
| | | } |
| | | |
| | | |
| | | .pointControl { |
| | | position: absolute; |
| | | bottom: 0; |
| | | right: 0; |
| | | width: 1540px; |
| | | height: 217px; |
| | | background: rgba(255, 255, 255, 0.3); |
| | | background: rgb(0, 0, 0, 0.4); /* 半透明背景 */ |
| | | backdrop-filter: blur(5px); |
| | | border-radius: 40px 0px 40px 40px; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: flex-end; |
| | | 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> |