forked from drone/command-center-dashboard

罗广辉
2025-04-15 2abd8e08f2148331e14bf1332e05f5d6302f82de
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/ControlPanel.vue
@@ -1,32 +1,124 @@
<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,
@@ -36,29 +128,21 @@
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 = ''
@@ -68,11 +152,11 @@
      .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) {
@@ -85,10 +169,35 @@
   })
}
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">
@@ -97,19 +206,133 @@
   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>