8 files modified
1 files renamed
| | |
| | | // 拍照和录像 |
| | | export async function callPhotoAndVideoCmd(sn, type) { |
| | | return await request({ |
| | | url:`/drone-device-core/droneAirport/liveStreamApi/${sn}/payload/photoAndVideoCmd/${type}`, |
| | | url:`${API_PREFIX}/devices/${sn}/payload/photoAndVideoCmd/${type}`, |
| | | method:'get', |
| | | }) |
| | | } |
| File was renamed from src/components/CurrentTaskDetails/ControlComPass/ControlComPass.vue |
| | |
| | | <div class="left-img" :data-text="`${attitude_pitch}°`"> |
| | | <div class="scaleImg"> |
| | | <p class="scale" :style="{ top: 45 + ScaleTop + 'px' }"></p> |
| | | <img src="../../../assets/images/rightmapidentification.png" /> |
| | | <img src="../../../../assets/images/rightmapidentification.png" /> |
| | | </div> |
| | | </div> |
| | | <div class="instrument-center"> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="center-show"> |
| | | <img src="../../../assets/images/mapidentification.png" /> |
| | | <img src="../../../../assets/images/mapidentification.png" /> |
| | | </div> |
| | | <div class="rotat-btn"></div> |
| | | </div> |
| | | <div class="right-img" :data-text="`${height}m`"> |
| | | <div class="ident-arrow"> |
| | | <img src="../../../assets/images/leftmapidentification.png" /> |
| | | <img src="../../../../assets/images/leftmapidentification.png" /> |
| | | <div class="arrow-box" :style="{ bottom: realHeight }"> |
| | | <div class="arrow"></div> |
| | | </div> |
| | |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | | import ControlComPass from '../ControlComPass/ControlComPass.vue' |
| | | import ControlComPass from '@/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue' |
| | | import { KeyCode, useManualControl } from '@/hooks/controlDrone/useManualControl' |
| | | import { droneController, exitController, postDrc, returnHome, returnHomeCancel } from '@/api/drc' |
| | | import { ElMessage } from 'element-plus' |
| | |
| | | RefreshLeft, |
| | | RefreshRight, |
| | | } from '@element-plus/icons-vue' |
| | | |
| | | import _ from 'lodash' |
| | | import BaseControl from '@/components/CurrentTaskDetails/ControlPanel/BaseControl.vue' |
| | | import EventBus from '@/event-bus' |
| | | import { getPayloadControlApi, ptzControlApi } from '@/api/payload' |
| | | |
| | | const deviceOsdInfo = inject('deviceOsdInfo') |
| | | const host = computed(() => deviceOsdInfo?.value?.data?.host || {}) |
| | | const taskDetails = inject('taskDetails') |
| | | const dockSn = inject('dockSn') |
| | | const droneSn = inject('droneSn') |
| | |
| | | { name: '左', key: KeyCode.ARROW_LEFT, operate: 'left', style: { left: '-70%' } }, |
| | | ] |
| | | |
| | | const list4 = [ |
| | | [ |
| | | { name: '焦距倍数', value: '0' }, |
| | | { name: '俯仰角度', value: '0.0°' }, |
| | | { name: '横向角度', value: '0.0°' }, |
| | | ], |
| | | [ |
| | | { name: '储存', value: '64.5G' }, |
| | | { name: '方向', value: '正北' }, |
| | | { name: '方向', value: '正北' }, |
| | | ], |
| | | ] |
| | | const list4 = computed(() => { |
| | | const { longitude, latitude, height, payloads } = host?.value || {} |
| | | const { gimbal_pitch } = payloads?.[0] || {} //俯仰角度 |
| | | return [ |
| | | [ |
| | | { name: '焦距倍数', value: '0' }, |
| | | { name: '俯仰角度', value: pitchAngle.value.angle }, |
| | | { name: '横向角度', value: yawAngle.value.angle }, |
| | | ], |
| | | [ |
| | | { name: '储存', value: '64.5G' }, |
| | | { name: '方向', value: pitchAngle.value.direction }, |
| | | { name: '方向', value: yawAngle.value.direction }, |
| | | ], |
| | | ] |
| | | }) |
| | | const pitchAngle = computed(() => { |
| | | const { longitude, latitude, height, payloads } = host?.value || {} |
| | | const gimbal_pitch = payloads?.[0]?.gimbal_pitch || 0 |
| | | let direction = '' |
| | | if (gimbal_pitch > -2 && gimbal_pitch < 2) { |
| | | direction = '正前' |
| | | } else if (gimbal_pitch >= 2 && gimbal_pitch < 90) { |
| | | direction = '斜上' |
| | | } else if (gimbal_pitch === 90) { |
| | | direction = '正上' |
| | | } else if (gimbal_pitch <= -2 && gimbal_pitch > -90) { |
| | | direction = '斜下' |
| | | } else if (gimbal_pitch === -90 || gimbal_pitch < -90) { |
| | | direction = '正下' |
| | | } |
| | | return { |
| | | angle: _.round(gimbal_pitch || 0, 1) + '°', |
| | | direction, |
| | | } |
| | | }) |
| | | |
| | | const yawAngle = computed(() => { |
| | | let { longitude, latitude, height, payloads, attitude_head } = host?.value || {} |
| | | const gimbal_pitch = payloads?.[0]?.gimbal_pitch || 0 |
| | | const gimbal_yaw = payloads?.[0]?.gimbal_yaw || 0 |
| | | attitude_head = attitude_head || 0 |
| | | let yaw = '' |
| | | if (gimbal_yaw > 180) { |
| | | yaw = gimbal_yaw |
| | | } else { |
| | | yaw = gimbal_yaw - attitude_head |
| | | } |
| | | let result = 0 |
| | | if (yaw < 0) { |
| | | result = yaw + 360 |
| | | } |
| | | if (yaw > 0) { |
| | | result = yaw |
| | | } |
| | | if ((yaw > -2 && yaw < 2) || parseInt(attitude_head) === parseInt(gimbal_yaw)) { |
| | | result = attitude_head < 0 ? 180 + (180 + attitude_head) : attitude_head |
| | | } |
| | | let direction = '' |
| | | const roundResult = Math.round(result) |
| | | if (roundResult === 0) { |
| | | direction = '正北' |
| | | } else if (roundResult > 0 && roundResult < 45) { |
| | | direction = '北偏东' |
| | | } else if (roundResult === 45) { |
| | | direction = '东北' |
| | | } else if (roundResult > 45 && roundResult < 90) { |
| | | direction = '北偏东' |
| | | } else if (roundResult === 90) { |
| | | direction = '正东' |
| | | } else if (roundResult > 90 && roundResult < 135) { |
| | | direction = '东偏南' |
| | | } else if (roundResult === 135) { |
| | | direction = '东南' |
| | | } else if (roundResult > 135 && roundResult < 180) { |
| | | direction = '南偏东' |
| | | } else if (roundResult === 180) { |
| | | direction = '正南' |
| | | } else if (roundResult > 180 && roundResult < 225) { |
| | | direction = '南偏西' |
| | | } else if (roundResult === 225) { |
| | | direction = '西南' |
| | | } else if (roundResult > 225 && roundResult < 270) { |
| | | direction = '西偏南' |
| | | } else if (roundResult === 270) { |
| | | direction = '正西' |
| | | } else if (roundResult > 270 && roundResult < 315) { |
| | | direction = '西偏北' |
| | | } else if (roundResult === 315) { |
| | | direction = '西北' |
| | | } else if (roundResult > 315 && roundResult < 360) { |
| | | direction = '北偏西' |
| | | } else if (roundResult === 360) { |
| | | direction = '正北' |
| | | } |
| | | return { |
| | | angle: _.round(result, 1) + '°', |
| | | direction, |
| | | } |
| | | }) |
| | | |
| | | const deviceTopicInfo = ref({ |
| | | pubTopic: '', |
| | |
| | | provide('isAutoControl', isAutoControl) |
| | | provide('lineQuality', lineQuality) |
| | | |
| | | const taskDetailsViewer = ref(null) |
| | | provide('taskDetailsViewer', taskDetailsViewer) |
| | | |
| | | let taskDetails = ref({}) |
| | | const deviceOsdInfo = ref({}) |
| | | provide('taskDetails', taskDetails) |
| | |
| | | case EBizCode.DeviceOsd: { |
| | | deviceOsdInfo.value = payload |
| | | setCurrentLiveUrl() |
| | | console.log(deviceOsdInfo.value, 'device_osd信息') |
| | | console.log(payload, 'DeviceOsd--信息') |
| | | break |
| | | } |
| | | case EBizCode.GatewayOsd: { |
| | | console.log(payload, 'GatewayOsd--信息') |
| | | break |
| | | } |
| | | case EBizCode.DockOsd: { |
| | | console.log(payload, 'GatewayOsd--信息') |
| | | break |
| | | } |
| | | default: |
| | |
| | | tilingScheme: new AmapMercatorTilingScheme(), |
| | | credit: 'amap_SL', |
| | | }) |
| | | let viewer = null |
| | | |
| | | const taskDetailsViewer = inject('taskDetailsViewer') |
| | | |
| | | const taskDetails = inject('taskDetails') |
| | | const deviceOsdInfo = inject('deviceOsdInfo') |
| | | |
| | | const initMap = () => { |
| | | viewer = new Viewer('currentTaskMap', { |
| | | taskDetailsViewer.value = new Viewer('currentTaskMap', { |
| | | terrain: Terrain.fromWorldTerrain(), |
| | | infoBox: false, // 禁用沙箱,解决控制台报错 |
| | | animation: false, // 左下角的动画仪表盘 |
| | |
| | | baseLayer: false, |
| | | fullscreenButton: false, |
| | | }) |
| | | const gdLayer = viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL) |
| | | const gdLayer = taskDetailsViewer?.value.imageryLayers.addImageryProvider(imageryProvider_ammapSL) |
| | | const options = { |
| | | bInvertColor: true, |
| | | bFilterColor: true, |
| | | filterColor: '#4e70a6', |
| | | } |
| | | // 添加蓝色滤镜 |
| | | addBlueFilter(options, viewer, gdLayer) |
| | | viewer.scene.morphTo2D(0) |
| | | addBlueFilter(options, taskDetailsViewer?.value, gdLayer) |
| | | taskDetailsViewer?.value.scene.morphTo2D(0) |
| | | //设置默认点 |
| | | viewer.camera.setView({ |
| | | taskDetailsViewer?.value.camera.setView({ |
| | | destination: Cartesian3.fromDegrees(115.763819, 28.787374, 5000), |
| | | }) |
| | | } |
| | |
| | | return Cartesian3.fromDegrees(Number(lon), Number(lat)) |
| | | }) |
| | | // 起点 |
| | | viewer.entities.add({ |
| | | taskDetailsViewer?.value.entities.add({ |
| | | position: positions[0], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(rwqfdImg), |
| | |
| | | }, |
| | | }) |
| | | // 终点 |
| | | viewer.entities.add({ |
| | | taskDetailsViewer?.value.entities.add({ |
| | | position: positions[positions.length - 1], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(endPointImg), |
| | |
| | | }, |
| | | }) |
| | | // 路径线 |
| | | viewer.entities.add({ |
| | | taskDetailsViewer?.value.entities.add({ |
| | | polyline: { |
| | | width: 4, |
| | | positions: positions, |
| | |
| | | const waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder) |
| | | if (!waylinesXMLObj.Placemark.length) return |
| | | const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(',')) |
| | | flyVisual(allPoint, viewer) |
| | | flyVisual(allPoint, taskDetailsViewer?.value) |
| | | drawWayline(waylinesXMLObj) |
| | | } |
| | | |
| | | const removeMap = () => { |
| | | viewer.entities.removeAll() |
| | | viewer.destroy() |
| | | taskDetailsViewer?.value.entities.removeAll() |
| | | taskDetailsViewer?.value.destroy() |
| | | } |
| | | |
| | | let viewInfoFrustum |
| | | // 设置视椎 |
| | | const setCreateFrustum = () => { |
| | | const deviceInfo = deviceOsdInfo.value?.data?.host |
| | | if (!deviceInfo) return |
| | | const host = deviceOsdInfo.value?.data?.host |
| | | if (!host) return |
| | | viewInfoFrustum?.clear() |
| | | if ([14, 0].includes(deviceInfo.mode_code)) return |
| | | const attitude_head = 180 + deviceInfo.attitude_head |
| | | const gimbal_pitch = 90 - Number(deviceInfo?.payloads[0]?.gimbal_pitch) || 0 |
| | | viewInfoFrustum = new CreateFrustum(viewer, { |
| | | if ([14, 0].includes(host.mode_code)) return |
| | | const attitude_head = 180 + host.attitude_head |
| | | const gimbal_pitch = 90 - Number(host?.payloads[0]?.gimbal_pitch) || 0 |
| | | viewInfoFrustum = new CreateFrustum(taskDetailsViewer?.value, { |
| | | position: { |
| | | longitude: deviceInfo.longitude, |
| | | latitude: deviceInfo.latitude, |
| | | altitude: deviceInfo.height, |
| | | longitude: host.longitude, |
| | | latitude: host.latitude, |
| | | altitude: host.height, |
| | | }, |
| | | width: 30, |
| | | height: 30, |
| | |
| | | </div> |
| | | </div> |
| | | <div class="controlBtn"> |
| | | <el-icon class="refresh"><Refresh /></el-icon> |
| | | <el-icon class="refresh"> |
| | | <Refresh /> |
| | | </el-icon> |
| | | <div class="switchBtn" @click="switchBtn"> |
| | | <div :class="{ open: open }">NO</div> |
| | | <div :class="{ open: !open }">OFF</div> |
| | |
| | | |
| | | <script setup> |
| | | import { Refresh } from '@element-plus/icons-vue' |
| | | import { getFlightStatistics } from '@/api/home/machineNest' |
| | | import { throttle } from 'lodash' |
| | | import { getLnglatAltitude } from '@/utils/cesium/mapUtil' |
| | | import _ from 'lodash' |
| | | |
| | | const taskDetailsViewer = inject('taskDetailsViewer') |
| | | const deviceOsdInfo = inject('deviceOsdInfo') |
| | | const dockSn = inject('dockSn') |
| | | const open = ref(true) |
| | | const singleTotal = ref({}) |
| | | const host = computed(() => deviceOsdInfo?.value?.data?.host || {}) |
| | | |
| | | const infoList = computed(() => { |
| | | const { longitude, latitude, height, payloads } = host?.value || {} |
| | | return [ |
| | | { title: '实时真高', value: '0' }, |
| | | { title: '绝对高度', value: _.round(height || 0, 1) + 'm' }, |
| | | { title: '水平速度', value: '0' }, |
| | | { title: '垂直速度', value: '0' }, |
| | | { title: '经度', value: _.round(longitude || 0, 2) }, |
| | | { title: '纬度', value: _.round(latitude || 0, 2) }, |
| | | { title: '4G信号', value: '0' }, |
| | | { title: 'SDR信号', value: '0' }, |
| | | { title: 'GPS搜星数', value: '0' }, |
| | | { title: 'RTK搜星数', value: '0' }, |
| | | { title: '距离机场', value: '0' }, |
| | | { title: '飞行时长', value: (singleTotal.value?.hour_count || 0) + '小时' }, |
| | | { title: '电池电量', value: (host?.value?.battery?.capacity_percent || 0) + '%' }, |
| | | ] |
| | | }) |
| | | |
| | | const switchBtn = () => { |
| | | open.value = !open.value |
| | | } |
| | | const infoList = [ |
| | | { title: '实时真高', value: '0' }, |
| | | { title: '绝对高度', value: '0' }, |
| | | { title: '水平速度', value: '0' }, |
| | | { title: '垂直速度', value: '0' }, |
| | | { title: '经度', value: '0' }, |
| | | { title: '纬度', value: '0' }, |
| | | { title: '4G信号', value: '0' }, |
| | | { title: 'SDR信号', value: '0' }, |
| | | { title: 'GPS搜星数', value: '0' }, |
| | | { title: 'RTK搜星数', value: '0' }, |
| | | { title: '距离机场', value: '0' }, |
| | | { title: '飞行时长', value: '0' }, |
| | | { title: '电池电量', value: '0' }, |
| | | ] |
| | | |
| | | function getFlightStatisticsFun() { |
| | | if (!dockSn.value) return |
| | | getFlightStatistics(dockSn.value).then(res => { |
| | | singleTotal.value = res.data.data |
| | | }) |
| | | } |
| | | |
| | | function getRealTimeReallyHigh() { |
| | | if (!taskDetailsViewer?.value) return |
| | | const { latitude, longitude, height } = host?.value || {} |
| | | if (!latitude) return |
| | | getLnglatAltitude(longitude, latitude, taskDetailsViewer.value).then(res => { |
| | | const findIndex = infoList.value.findIndex(item => item.title === '实时真高') |
| | | infoList.value[findIndex].value = _.round(height - res?.height, 1) + 'm' |
| | | }) |
| | | } |
| | | |
| | | const getRealTimeReallyHighThrottle = throttle(getRealTimeReallyHigh, 500, { leading: true, trailing: true }) |
| | | watch( |
| | | deviceOsdInfo, |
| | | () => { |
| | | getRealTimeReallyHighThrottle() |
| | | }, |
| | | { immediate: true } |
| | | ) |
| | | |
| | | watch( |
| | | dockSn, |
| | | () => { |
| | | getFlightStatisticsFun() |
| | | }, |
| | | { immediate: true } |
| | | ) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | |
| | | z-index: 5; |
| | | width: 100%; |
| | | height: 68px; |
| | | background: rgb(0,0,0,.4); /* 半透明背景 */ |
| | | background: rgb(0, 0, 0, 0.4); /* 半透明背景 */ |
| | | backdrop-filter: blur(5px); |
| | | padding: 0 31px; |
| | | display: flex; |
| | |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | |
| | | .refresh{ |
| | | .refresh { |
| | | color: white; |
| | | font-size: 30px; |
| | | cursor: pointer; |
| | |
| | | <div @click="recordFun">{{ isRecording ? '录像中...' : '录像' }}</div> |
| | | </div> |
| | | <div class="multiCol"> |
| | | <div>喊话</div> |
| | | <div>广播</div> |
| | | <div @click="ElMessage.warning('加急开发中...')">喊话</div> |
| | | <div @click="ElMessage.warning('加急开发中...')">广播</div> |
| | | </div> |
| | | |
| | | <div class="cameraZoom"> |
| | |
| | | const msg = isRecording.value ? '停止录像' : '开始录像' |
| | | const emitType = isRecording.value ? 'controlPanel-timeStop' : 'controlPanel-timeStart' |
| | | |
| | | callPhotoAndVideoCmd(droneSn.value, type).then(res => { |
| | | callPhotoAndVideoCmd(dockSn.value, type).then(res => { |
| | | ElMessage.success(msg) |
| | | EventBus.emit(emitType) |
| | | isRecording.value = !isRecording.value |
| | |
| | | // 拍照 |
| | | function takePictures() { |
| | | // photo拍照,video_start开始录像,video_stop结束录像 |
| | | callPhotoAndVideoCmd(droneSn.value, 'photo').then(res => { |
| | | callPhotoAndVideoCmd(dockSn.value, 'photo').then(res => { |
| | | ElMessage.success('拍照成功') |
| | | }) |
| | | } |
| | |
| | | export function useManualControl(mqttState,deviceTopicInfo, isCurrentFlightController,paramsRef) { |
| | | let activeCodeKey = null |
| | | let genPortOne = true //是一代机场 |
| | | const mqttHooks = useMqtt(mqttState,deviceTopicInfo) |
| | | let mqttHooks = useMqtt(mqttState,deviceTopicInfo) |
| | | let seq = 0 |
| | | const keysPressed = [ |
| | | {key: 'KeyQ', value: false}, |
| | |
| | | { immediate: true } |
| | | ) |
| | | |
| | | onBeforeUnmount(()=>{ |
| | | mqttHooks?.d |
| | | }) |
| | | |
| | | onUnmounted(() => { |
| | | deviceTopicInfo.subTopic = '' |
| | | deviceTopicInfo.pubTopic = '' |
| | | mqttHooks = null |
| | | closeKeyboardManualControl() |
| | | }) |
| | | |
| | |
| | | // 2.发心跳 |
| | | publishDrcPing(deviceTopicInfo.sn) |
| | | } else { |
| | | console.log('清除pingInterval') |
| | | clearInterval(state.heartState.get(deviceTopicInfo.sn)?.pingInterval) |
| | | state.heartState.delete(deviceTopicInfo.sn) |
| | | heartBeatSeq.value = 0 |