<template>
|
<div class="drone-control-wrapper" v-if="modelValue">
|
<div class="drone-control-header width-100 flex-display flex-align-center flex-justify-between">
|
<span>无人机飞行控制</span>
|
<span @click="closeDrone">
|
<CloseOutlined/>
|
</span>
|
</div>
|
<div class="drone-control-box">
|
<div class="box">
|
<div class="row">
|
<div class="drone-control">
|
<Button :ghost="!flightController" size="small" @click="onClickFightControl">
|
{{ flightController ? '退出控制' : '进入控制' }}
|
</Button>
|
</div>
|
</div>
|
<div class="row">
|
<div class="drone-control-direction">
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.KEY_Q)" @onmouseup="onMouseUp">
|
<template #icon>
|
<UndoOutlined/>
|
</template>
|
<span class="word">Q</span>
|
</Button>
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.KEY_W)" @onmouseup="onMouseUp">
|
<template #icon>
|
<UpOutlined/>
|
</template>
|
<span class="word">W</span>
|
</Button>
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.KEY_E)" @onmouseup="onMouseUp">
|
<template #icon>
|
<RedoOutlined/>
|
</template>
|
<span class="word">E</span>
|
</Button>
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.ARROW_UP)" @onmouseup="onMouseUp">
|
<template #icon>
|
<ArrowUpOutlined/>
|
</template>
|
</Button>
|
<br/>
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.KEY_A)" @onmouseup="onMouseUp">
|
<template #icon>
|
<LeftOutlined/>
|
</template>
|
<span class="word">A</span>
|
</Button>
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.KEY_S)" @onmouseup="onMouseUp">
|
<template #icon>
|
<DownOutlined/>
|
</template>
|
<span class="word">S</span>
|
</Button>
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.KEY_D)" @onmouseup="onMouseUp">
|
<template #icon>
|
<RightOutlined/>
|
</template>
|
<span class="word">D</span>
|
</Button>
|
<Button size="small" ghost @mousedown="onMouseDown(KeyCode.ARROW_DOWN)" @onmouseup="onMouseUp">
|
<template #icon>
|
<ArrowDownOutlined/>
|
</template>
|
</Button>
|
</div>
|
<Button type="primary" size="small" danger ghost @click="handleEmergencyStop">
|
<template #icon>
|
<PauseCircleOutlined/>
|
</template>
|
<span>结束</span>
|
</Button>
|
</div>
|
<div class="row">
|
<DroneControlPopover
|
:visible="flyToPointPopoverData.visible"
|
:loading="flyToPointPopoverData.loading"
|
@confirm="($event) => onFlyToConfirm(true)"
|
@cancel="($event) =>onFlyToConfirm(false)"
|
>
|
<template #formContent>
|
<div class="form-content">
|
<div>
|
<span class="form-label">纬度:</span>
|
<a-input-number v-model:value="flyToPointPopoverData.latitude"/>
|
</div>
|
<div>
|
<span class="form-label">经度:</span>
|
<a-input-number v-model:value="flyToPointPopoverData.longitude"/>
|
</div>
|
<div>
|
<span class="form-label">高度(m):</span>
|
<a-input-number v-model:value="flyToPointPopoverData.height"/>
|
</div>
|
</div>
|
</template>
|
<Button size="small" ghost @click="onShowFlyToPopover">
|
<span>起飞</span>
|
</Button>
|
</DroneControlPopover>
|
<Button size="small" ghost @click="onStopFlyToPoint">
|
<span>停止起飞</span>
|
</Button>
|
<DroneControlPopover
|
:visible="takeoffToPointPopoverData.visible"
|
:loading="takeoffToPointPopoverData.loading"
|
@confirm="($event) => onTakeoffToPointConfirm(true)"
|
@cancel="($event) =>onTakeoffToPointConfirm(false)"
|
>
|
<template #formContent>
|
<div class="form-content">
|
<div>
|
<span class="form-label">纬度:</span>
|
<a-input-number v-model:value="takeoffToPointPopoverData.latitude"/>
|
</div>
|
<div>
|
<span class="form-label">经度:</span>
|
<a-input-number v-model:value="takeoffToPointPopoverData.longitude"/>
|
</div>
|
<div>
|
<span class="form-label">高度(m):</span>
|
<a-input-number v-model:value="takeoffToPointPopoverData.height"/>
|
</div>
|
<div>
|
<span class="form-label">安全起飞高度(m):</span>
|
<a-input-number v-model:value="takeoffToPointPopoverData.securityTakeoffHeight"/>
|
</div>
|
<div>
|
<span class="form-label">返回原点高度(m):</span>
|
<a-input-number v-model:value="takeoffToPointPopoverData.rthAltitude"/>
|
</div>
|
<div>
|
<span class="form-label">失控操作:</span>
|
<a-select
|
v-model:value="takeoffToPointPopoverData.rcLostAction"
|
style="width: 120px"
|
:options="LostControlActionInCommandFLightOptions"
|
></a-select>
|
</div>
|
<div>
|
<span class="form-label">线路丢失操作:</span>
|
<a-select
|
v-model:value="takeoffToPointPopoverData.exitWaylineWhenRcLost"
|
style="width: 120px"
|
:options="WaylineLostControlActionInCommandFlightOptions"
|
></a-select>
|
</div>
|
</div>
|
</template>
|
<Button size="small" ghost @click="onShowTakeoffToPointPopover">
|
<span>一键起飞</span>
|
</Button>
|
</DroneControlPopover>
|
<Button :loading="cmdItem.loading" size="small" ghost @click="sendControlCmd(cmdItem, 0)">
|
{{ cmdItem.operateText }}
|
</Button>
|
</div>
|
</div>
|
<div class="box">
|
<div class="row">
|
<Select v-model:value="payloadSelectInfo.value" style="width: 110px; marginRight: 5px"
|
:options="payloadSelectInfo.options" @change="handlePayloadChange"/>
|
<div class="drone-control">
|
<Button type="primary" size="small" @click="onAuthPayload">负载控制</Button>
|
</div>
|
</div>
|
<div class="row">
|
<DroneControlPopover
|
:visible="gimbalResetPopoverData.visible"
|
:loading="gimbalResetPopoverData.loading"
|
@confirm="($event) => onGimbalResetConfirm(true)"
|
@cancel="($event) =>onGimbalResetConfirm(false)"
|
>
|
<template #formContent>
|
<div class="form-content">
|
<div>
|
<span class="form-label">重置模式:</span>
|
<a-select
|
v-model:value="gimbalResetPopoverData.resetMode"
|
style="width: 180px"
|
:options="GimbalResetModeOptions"
|
></a-select>
|
</div>
|
</div>
|
</template>
|
<Button size="small" ghost @click="onShowGimbalResetPopover">
|
<span>复位</span>
|
</Button>
|
</DroneControlPopover>
|
<Button size="small" ghost @click="onSwitchCameraMode">
|
<span>相机模式开关</span>
|
</Button>
|
</div>
|
<div class="row">
|
<Button size="small" ghost @click="onStartCameraRecording">
|
<span>开始录制</span>
|
</Button>
|
<Button size="small" ghost @click="onStopCameraRecording">
|
<span>停止录制</span>
|
</Button>
|
</div>
|
<div class="row">
|
<Button size="small" ghost @click="onTakeCameraPhoto">
|
<span>拍照</span>
|
</Button>
|
<DroneControlPopover
|
:visible="zoomFactorPopoverData.visible"
|
:loading="zoomFactorPopoverData.loading"
|
@confirm="($event) => onZoomFactorConfirm(true)"
|
@cancel="($event) =>onZoomFactorConfirm(false)"
|
>
|
<template #formContent>
|
<div class="form-content">
|
<div>
|
<span class="form-label">相机类型:</span>
|
<a-select
|
v-model:value="zoomFactorPopoverData.cameraType"
|
style="width: 120px"
|
:options="ZoomCameraTypeOptions"
|
></a-select>
|
</div>
|
<div>
|
<span class="form-label">缩放:</span>
|
<a-input-number v-model:value="zoomFactorPopoverData.zoomFactor" :min="2"
|
:max="200"/>
|
</div>
|
</div>
|
</template>
|
<Button size="small" ghost @click="($event) => onShowZoomFactorPopover()">
|
<span class="word" @click=";">聚焦</span>
|
</Button>
|
</DroneControlPopover>
|
<DroneControlPopover
|
:visible="cameraAimPopoverData.visible"
|
:loading="cameraAimPopoverData.loading"
|
@confirm="($event) => onCameraAimConfirm(true)"
|
@cancel="($event) =>onCameraAimConfirm(false)"
|
>
|
<template #formContent>
|
<div class="form-content">
|
<div>
|
<span class="form-label">相机类型:</span>
|
<a-select
|
v-model:value="cameraAimPopoverData.cameraType"
|
style="width: 120px"
|
:options="CameraTypeOptions"
|
></a-select>
|
</div>
|
<div>
|
<span class="form-label">锁定:</span>
|
<a-switch v-model:checked="cameraAimPopoverData.locked"/>
|
</div>
|
<div>
|
<span class="form-label">x:</span>
|
<a-input-number v-model:value="cameraAimPopoverData.x" :min="0" :max="1"/>
|
</div>
|
<div>
|
<span class="form-label">y:</span>
|
<a-input-number v-model:value="cameraAimPopoverData.y" :min="0" :max="1"/>
|
</div>
|
</div>
|
</template>
|
<Button size="small" ghost @click="($event) => onShowCameraAimPopover()">
|
<span class="word" @click=";">目标</span>
|
</Button>
|
</DroneControlPopover>
|
</div>
|
</div>
|
</div>
|
<!-- 信息提示 -->
|
<!-- <DroneControlInfoPanel :message="drcInfo"></DroneControlInfoPanel> -->
|
</div>
|
</template>
|
|
<script setup lang="ts">
|
import { defineProps, reactive, ref, watch, computed, onMounted, watchEffect, defineEmits } from 'vue'
|
import { Select, message, Button } from 'ant-design-vue'
|
import { PayloadInfo, DeviceInfoType, ControlSource, DeviceOsdCamera, DrcStateEnum } from '/@/types/device'
|
import { useMyStore } from '/@/store'
|
import { postDrcEnter, postDrcExit } from '/@/api/drc'
|
import { useMqtt, DeviceTopicInfo } from './use-mqtt'
|
import {
|
DownOutlined,
|
UpOutlined,
|
LeftOutlined,
|
RightOutlined,
|
PauseCircleOutlined,
|
UndoOutlined,
|
RedoOutlined,
|
ArrowUpOutlined,
|
ArrowDownOutlined,
|
CloseOutlined
|
} from '@ant-design/icons-vue'
|
import { useManualControl, KeyCode } from './use-manual-control'
|
import { usePayloadControl } from './use-payload-control'
|
import { CameraMode, CameraType, CameraTypeOptions, ZoomCameraTypeOptions, CameraListItem } from '/@/types/live-stream'
|
import { useDroneControlWsEvent } from './use-drone-control-ws-event'
|
import { useDroneControlMqttEvent } from './use-drone-control-mqtt-event'
|
import {
|
postFlightAuth,
|
LostControlActionInCommandFLight,
|
WaylineLostControlActionInCommandFlight
|
} from '/@/api/drone-control/drone'
|
import { useDroneControl } from './use-drone-control'
|
import {
|
GimbalResetMode,
|
GimbalResetModeOptions,
|
LostControlActionInCommandFLightOptions,
|
WaylineLostControlActionInCommandFlightOptions
|
} from '/@/types/drone-control'
|
import DroneControlPopover from './DroneControlPopover.vue'
|
import DroneControlInfoPanel from './DroneControlInfoPanel.vue'
|
import { noDebugCmdList as baseCmdList, DeviceCmdItem, DeviceCmd } from '/@/types/device-cmd'
|
import { useDockControl } from './use-dock-control'
|
|
const props = defineProps<{
|
sn: string,
|
deviceInfo: DeviceInfoType,
|
payloads: null | PayloadInfo[],
|
modelValue: Boolean,
|
}>()
|
|
const store = useMyStore()
|
const clientId = computed(() => {
|
return store.state.clientId
|
})
|
|
const initCmdList = baseCmdList.find(item => item.cmdKey === DeviceCmd.ReturnHome) as DeviceCmdItem
|
const cmdItem = ref(initCmdList)
|
|
const {
|
sendDockControlCmd
|
} = useDockControl()
|
|
async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) {
|
cmdItem.loading = true
|
const result = await sendDockControlCmd({
|
sn: props.sn,
|
cmd: cmdItem.cmdKey,
|
action: cmdItem.action
|
}, false)
|
// if (result && flightController.value) {
|
// message.success('返航成功')
|
// exitFlightCOntrol()
|
// } else {
|
// message.warn('请先进入控制!')
|
// }
|
// cmdItem.loading = false
|
if (result) {
|
message.success('返航成功')
|
}
|
// 退出飞行控制
|
if (result && flightController.value) {
|
exitFlightCOntrol()
|
}
|
cmdItem.loading = false
|
}
|
|
const emits = defineEmits(['update:modelValue'])
|
// 关闭弹窗
|
const closeDrone = () => {
|
emits('update:modelValue', false)
|
}
|
const { flyToPoint, stopFlyToPoint, takeoffToPoint } = useDroneControl()
|
const MAX_SPEED = 14
|
|
const flyToPointPopoverData = reactive({
|
visible: false,
|
loading: false,
|
latitude: null as null | number,
|
longitude: null as null | number,
|
height: null as null | number,
|
maxSpeed: MAX_SPEED,
|
})
|
|
function onShowFlyToPopover () {
|
flyToPointPopoverData.visible = !flyToPointPopoverData.visible
|
flyToPointPopoverData.loading = false
|
flyToPointPopoverData.latitude = null
|
flyToPointPopoverData.longitude = null
|
flyToPointPopoverData.height = null
|
}
|
|
async function onFlyToConfirm (confirm: boolean) {
|
if (confirm) {
|
if (!flyToPointPopoverData.height || !flyToPointPopoverData.latitude || !flyToPointPopoverData.longitude) {
|
message.error('输入错误')
|
return
|
}
|
try {
|
await flyToPoint(props.sn, {
|
max_speed: flyToPointPopoverData.maxSpeed,
|
points: [
|
{
|
latitude: flyToPointPopoverData.latitude,
|
longitude: flyToPointPopoverData.longitude,
|
height: flyToPointPopoverData.height
|
}
|
]
|
})
|
} catch (error) {
|
}
|
}
|
flyToPointPopoverData.visible = false
|
}
|
|
async function onStopFlyToPoint () {
|
await stopFlyToPoint(props.sn)
|
}
|
|
const takeoffToPointPopoverData = reactive({
|
visible: false,
|
loading: false,
|
latitude: null as null | number,
|
longitude: null as null | number,
|
height: 120 as null | number,
|
securityTakeoffHeight: 100 as null | number,
|
maxSpeed: MAX_SPEED,
|
rthAltitude: 100 as null | number,
|
rcLostAction: LostControlActionInCommandFLight.RETURN_HOME,
|
exitWaylineWhenRcLost: WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION
|
})
|
|
function onShowTakeoffToPointPopover () {
|
takeoffToPointPopoverData.visible = !takeoffToPointPopoverData.visible
|
takeoffToPointPopoverData.loading = false
|
takeoffToPointPopoverData.latitude = props.deviceInfo.dock.basic_osd.latitude
|
takeoffToPointPopoverData.longitude = props.deviceInfo.dock.basic_osd.longitude
|
takeoffToPointPopoverData.securityTakeoffHeight = 100
|
takeoffToPointPopoverData.rthAltitude = 100
|
takeoffToPointPopoverData.rcLostAction = LostControlActionInCommandFLight.RETURN_HOME
|
takeoffToPointPopoverData.exitWaylineWhenRcLost = WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION
|
}
|
|
async function onTakeoffToPointConfirm (confirm: boolean) {
|
if (confirm) {
|
if (!takeoffToPointPopoverData.height ||
|
!takeoffToPointPopoverData.latitude ||
|
!takeoffToPointPopoverData.longitude ||
|
!takeoffToPointPopoverData.securityTakeoffHeight ||
|
!takeoffToPointPopoverData.rthAltitude) {
|
message.error('输入错误')
|
return
|
}
|
try {
|
await takeoffToPoint(props.sn, {
|
target_latitude: takeoffToPointPopoverData.latitude,
|
target_longitude: takeoffToPointPopoverData.longitude,
|
target_height: takeoffToPointPopoverData.height,
|
security_takeoff_height: takeoffToPointPopoverData.securityTakeoffHeight,
|
rth_altitude: takeoffToPointPopoverData.rthAltitude,
|
max_speed: takeoffToPointPopoverData.maxSpeed,
|
rc_lost_action: takeoffToPointPopoverData.rcLostAction,
|
exit_wayline_when_rc_lost: takeoffToPointPopoverData.exitWaylineWhenRcLost
|
})
|
} catch (error) {
|
}
|
}
|
takeoffToPointPopoverData.visible = false
|
}
|
|
const deviceTopicInfo: DeviceTopicInfo = reactive({
|
sn: props.sn,
|
pubTopic: '',
|
subTopic: ''
|
})
|
|
useMqtt(deviceTopicInfo)
|
|
// 飞行控制
|
// const drcState = computed(() => {
|
// return store.state.deviceState?.dockInfo[props.sn]?.link_osd?.drc_state === DrcStateEnum.CONNECTED
|
// })
|
const flightController = ref(false)
|
|
async function onClickFightControl () {
|
if (flightController.value) {
|
exitFlightCOntrol()
|
return
|
}
|
enterFlightControl()
|
}
|
|
// 进入飞行控制
|
async function enterFlightControl () {
|
if (!clientId.value) {
|
message.error('无人机不在空中,不能进入指挥飞行模式。')
|
return
|
}
|
try {
|
const { code, data } = await postDrcEnter({
|
client_id: clientId.value,
|
dock_sn: props.sn,
|
})
|
if (code === 0) {
|
flightController.value = true
|
if (data.sub && data.sub.length > 0) {
|
deviceTopicInfo.subTopic = data.sub[0]
|
}
|
if (data.pub && data.pub.length > 0) {
|
deviceTopicInfo.pubTopic = data.pub[0]
|
}
|
// 获取飞行控制权
|
if (droneControlSource.value !== ControlSource.A) {
|
await postFlightAuth(props.sn)
|
}
|
message.success('获取飞行控制成功')
|
}
|
} catch (error: any) {
|
}
|
}
|
|
// 退出飞行控制
|
async function exitFlightCOntrol () {
|
try {
|
const { code } = await postDrcExit({
|
client_id: clientId.value,
|
dock_sn: props.sn,
|
})
|
if (code === 0) {
|
flightController.value = false
|
deviceTopicInfo.subTopic = ''
|
deviceTopicInfo.pubTopic = ''
|
message.success('退出飞行控制成功')
|
}
|
} catch (error: any) {
|
}
|
}
|
|
// drc mqtt message
|
const { drcInfo, errorInfo } = useDroneControlMqttEvent(props.sn)
|
|
const {
|
handleKeyup,
|
handleEmergencyStop,
|
resetControlState,
|
} = useManualControl(deviceTopicInfo, flightController)
|
|
function onMouseDown (type: KeyCode) {
|
handleKeyup(type)
|
}
|
|
function onMouseUp () {
|
resetControlState()
|
}
|
|
// 负载控制
|
const payloadSelectInfo = {
|
value: null as any,
|
controlSource: undefined as undefined | ControlSource,
|
options: [] as any,
|
payloadIndex: '' as string,
|
camera: undefined as undefined | DeviceOsdCamera // 当前负载osd信息
|
}
|
|
const handlePayloadChange = (value: string) => {
|
const payload = props.payloads?.find(item => item.payload_sn === value)
|
if (payload) {
|
payloadSelectInfo.payloadIndex = payload.payload_index || ''
|
payloadSelectInfo.controlSource = payload.control_source
|
payloadSelectInfo.camera = undefined
|
}
|
}
|
|
// function getCurrentCamera (cameraList: CameraListItem[], cameraIndex?: string):CameraListItem | null {
|
// let camera = null
|
// cameraList.forEach(item => {
|
// if (item.camera_index === cameraIndex) {
|
// camera = item
|
// }
|
// })
|
// return camera
|
// }
|
|
// const currentCamera = computed(() => {
|
// return getCurrentCamera(props.deviceInfo.dock.basic_osd.live_capacity?.device_list[0]?.camera_list as CameraListItem[], camera_index)
|
// })
|
// 更新负载信息
|
watch(() => props.payloads, (payloads) => {
|
if (payloads && payloads.length > 0) {
|
payloadSelectInfo.value = payloads[0].payload_sn
|
payloadSelectInfo.controlSource = payloads[0].control_source || ControlSource.B
|
payloadSelectInfo.payloadIndex = payloads[0].payload_index || ''
|
payloadSelectInfo.options = payloads.map(item => ({ label: item.payload_name, value: item.payload_sn }))
|
payloadSelectInfo.camera = undefined
|
} else {
|
payloadSelectInfo.value = null
|
payloadSelectInfo.controlSource = undefined
|
payloadSelectInfo.options = []
|
payloadSelectInfo.payloadIndex = ''
|
payloadSelectInfo.camera = undefined
|
}
|
}, {
|
immediate: true,
|
deep: true
|
})
|
watch(() => props.deviceInfo.device, (droneOsd) => {
|
if (droneOsd && droneOsd.cameras) {
|
payloadSelectInfo.camera = droneOsd.cameras.find(item => item.payload_index === payloadSelectInfo.payloadIndex)
|
} else {
|
payloadSelectInfo.camera = undefined
|
}
|
}, {
|
immediate: true,
|
deep: true
|
})
|
|
// ws 消息通知
|
const { droneControlSource, payloadControlSource } = useDroneControlWsEvent(props.sn, payloadSelectInfo.value)
|
watch(() => payloadControlSource, (controlSource) => {
|
payloadSelectInfo.controlSource = controlSource.value
|
}, {
|
immediate: true,
|
deep: true
|
})
|
const {
|
checkPayloadAuth,
|
authPayload,
|
resetGimbal,
|
switchCameraMode,
|
takeCameraPhoto,
|
startCameraRecording,
|
stopCameraRecording,
|
changeCameraFocalLength,
|
cameraAim,
|
} = usePayloadControl()
|
|
async function onAuthPayload () {
|
const result = await authPayload(props.sn, payloadSelectInfo.payloadIndex)
|
if (result) {
|
payloadControlSource.value = ControlSource.A
|
}
|
}
|
|
const gimbalResetPopoverData = reactive({
|
visible: false,
|
loading: false,
|
resetMode: null as null | GimbalResetMode,
|
})
|
|
function onShowGimbalResetPopover () {
|
gimbalResetPopoverData.visible = !gimbalResetPopoverData.visible
|
gimbalResetPopoverData.loading = false
|
gimbalResetPopoverData.resetMode = null
|
}
|
|
async function onGimbalResetConfirm (confirm: boolean) {
|
if (confirm) {
|
if (gimbalResetPopoverData.resetMode === null) {
|
message.error('请选择重置模式')
|
return
|
}
|
gimbalResetPopoverData.loading = true
|
try {
|
await resetGimbal(props.sn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
reset_mode: gimbalResetPopoverData.resetMode
|
})
|
} catch (err) {
|
}
|
}
|
gimbalResetPopoverData.visible = false
|
}
|
|
async function onSwitchCameraMode () {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
const currentCameraMode = payloadSelectInfo.camera?.camera_mode
|
await switchCameraMode(props.sn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
camera_mode: currentCameraMode === CameraMode.Photo ? CameraMode.Video : CameraMode.Photo
|
})
|
}
|
|
async function onTakeCameraPhoto () {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
await takeCameraPhoto(props.sn, payloadSelectInfo.payloadIndex)
|
}
|
|
async function onStartCameraRecording () {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
await startCameraRecording(props.sn, payloadSelectInfo.payloadIndex)
|
}
|
|
async function onStopCameraRecording () {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
await stopCameraRecording(props.sn, payloadSelectInfo.payloadIndex)
|
}
|
|
const zoomFactorPopoverData = reactive({
|
visible: false,
|
loading: false,
|
cameraType: null as null | CameraType,
|
zoomFactor: null as null | number,
|
})
|
|
function onShowZoomFactorPopover () {
|
zoomFactorPopoverData.visible = !zoomFactorPopoverData.visible
|
zoomFactorPopoverData.loading = false
|
zoomFactorPopoverData.cameraType = null
|
zoomFactorPopoverData.zoomFactor = null
|
}
|
|
async function onZoomFactorConfirm (confirm: boolean) {
|
if (confirm) {
|
if (!zoomFactorPopoverData.zoomFactor || zoomFactorPopoverData.cameraType === null) {
|
message.error('请输入缩放程度')
|
return
|
}
|
zoomFactorPopoverData.loading = true
|
try {
|
await changeCameraFocalLength(props.sn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
camera_type: zoomFactorPopoverData.cameraType,
|
zoom_factor: zoomFactorPopoverData.zoomFactor
|
})
|
} catch (err) {
|
}
|
}
|
zoomFactorPopoverData.visible = false
|
}
|
|
const cameraAimPopoverData = reactive({
|
visible: false,
|
loading: false,
|
cameraType: null as null | CameraType,
|
locked: false,
|
x: null as null | number,
|
y: null as null | number,
|
})
|
|
function onShowCameraAimPopover () {
|
cameraAimPopoverData.visible = !cameraAimPopoverData.visible
|
cameraAimPopoverData.loading = false
|
cameraAimPopoverData.cameraType = null
|
cameraAimPopoverData.locked = false
|
cameraAimPopoverData.x = null
|
cameraAimPopoverData.y = null
|
}
|
|
async function onCameraAimConfirm (confirm: boolean) {
|
if (confirm) {
|
if (cameraAimPopoverData.cameraType === null || cameraAimPopoverData.x === null || cameraAimPopoverData.y === null) {
|
message.error('输入错误')
|
return
|
}
|
try {
|
await cameraAim(props.sn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
camera_type: cameraAimPopoverData.cameraType,
|
locked: cameraAimPopoverData.locked,
|
x: cameraAimPopoverData.x,
|
y: cameraAimPopoverData.y,
|
})
|
} catch (error) {
|
}
|
}
|
cameraAimPopoverData.visible = false
|
}
|
|
watch(() => errorInfo, (errorInfo) => {
|
if (errorInfo.value) {
|
message.error(errorInfo.value)
|
console.error(errorInfo.value)
|
errorInfo.value = ''
|
}
|
}, {
|
immediate: true,
|
deep: true
|
})
|
</script>
|
|
<style lang='scss' scoped>
|
.drone-control-wrapper {
|
position: absolute;
|
background: #000;
|
color: #fff;
|
left: calc(100% + 10px);
|
width: 480px;
|
top: 0;
|
|
.drone-control-header {
|
font-size: 14px;
|
font-weight: 600;
|
padding: 10px 10px 0px;
|
}
|
|
.drone-control-box {
|
display: flex;
|
flex-wrap: 1;
|
|
.box {
|
width: 50%;
|
padding: 5px;
|
border: 0.5px solid rgba(255, 255, 255, 0.3);
|
|
.row {
|
display: flex;
|
flex-wrap: wrap;
|
padding: 2px;
|
|
+ .row {
|
margin-bottom: 6px;
|
}
|
|
&::v-deep {
|
.ant-btn {
|
font-size: 12px;
|
padding: 0px 4px;
|
margin-right: 5px;
|
}
|
}
|
}
|
|
.drone-control {
|
&::v-deep {
|
|
.ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
|
padding: 0 2px;
|
}
|
}
|
}
|
|
.drone-control-direction {
|
margin-right: 10px;
|
|
.ant-btn {
|
// padding: 0px 1px;
|
margin-right: 0;
|
}
|
|
.word {
|
width: 12px;
|
margin-left: 2px;
|
font-size: 12px;
|
color: #aaa;
|
}
|
}
|
}
|
}
|
}
|
</style>
|