<template>
|
<div class="ptz-control">
|
<div class="btn-group">
|
<div class="btn-item" :class="isPtz?'actived-blue':''" @touchstart="ptzClick">
|
<img src="@/assets/images/yt.png" />
|
</div>
|
<div class="btn-item" @touchstart="onTakeCameraPhoto">
|
<img src="@/assets/images/photo.png" />
|
</div>
|
<div class="btn-item" v-if="!isCameraRecording" @touchstart="onStartCameraRecording">
|
<img src="@/assets/images/sx-open.png" />
|
</div>
|
<div class="btn-item" v-else @touchstart="onStopCameraRecording">
|
<img src="@/assets/images/sx-close.png"/>
|
</div>
|
<div class="btn-item" @touchstart="onZoom(true)">
|
<img src="@/assets/images/fangda.png" />
|
</div>
|
<div class="btn-item" @touchstart="onZoom(false)">
|
<img src="@/assets/images/suoxiao.png" />
|
</div>
|
<!-- <div class="btn-item">
|
<img v-if="!isBigVideo" src="@/assets/images/fang.png" @touchstart="changeModelVideo(true)" />
|
<img v-else src="@/assets/images/suo.png" @touchstart="changeModelVideo(false)" />
|
</div> -->
|
</div>
|
<div class="circle-box" v-show="isPtz">
|
<div class="top" @click="ytClick('up')"></div>
|
<div class="left" @click="ytClick('left')"></div>
|
<div class="bottom" @click="ytClick('down')"></div>
|
<div class="right" @click="ytClick('right')"></div>
|
<div class="reset-center" @click="onResetGimbal(0)">
|
<el-icon><Refresh /></el-icon>
|
</div>
|
<div class="blue-bgc"></div>
|
</div>
|
</div>
|
</template>
|
|
<script lang="ts" setup>
|
import { ElMessage } from 'element-plus'
|
import { Refresh } from '@element-plus/icons-vue';
|
import { ControlSource, EModeCode } from '@/types/device'
|
import { CameraMode } from '@/types/live-stream'
|
import { ref, onMounted, onBeforeUnmount, nextTick, reactive, computed, watch } from 'vue';
|
import { postPayloadCommands, postPayloadAuth } from '@/api/drone/payload'
|
import { switchLivestream } from '@/api/drone'
|
import { usePayloadControl } from './use-payload-control'
|
import { useDroneControlWsEvent } from '@/utils/websocket/drone-ws-control'
|
import cesiumOperation from '@/utils/cesium-tsa.js';
|
import { useMyStore } from '@/store'
|
import { fa } from 'element-plus/es/locale';
|
|
const { loadLAYER } = cesiumOperation()
|
|
const {
|
checkPayloadAuth,
|
authPayload,
|
resetGimbal,
|
switchCameraMode,
|
takeCameraPhoto,
|
startCameraRecording,
|
stopCameraRecording,
|
changeCameraFocalLength,
|
} = usePayloadControl()
|
|
const props = defineProps({
|
sn: {
|
type: String,
|
required: true,
|
},
|
osdVisible: {
|
type: Object,
|
required: true,
|
},
|
cesiumViewe: {
|
type: Object,
|
required: true,
|
}
|
});
|
const emitEvents = defineEmits(['changeModelVideo'])
|
|
// 放大和缩小
|
// let isBigVideo = ref<Boolean>(false)
|
|
// 录像
|
let isCameraRecording = ref<Boolean>(false)
|
//
|
let isActionComplete = ref<Boolean>(false)
|
|
let payloadCameraMode = ref<String>('wide')
|
|
// 缩放监听更新值
|
let zoom_factor_watch = ref(0)
|
// 缩放初始值
|
let zoom_factor_start = ref(0)
|
// 缩放变化值
|
let zoom_factor = ref(0)
|
// is_First 记录第一次
|
let is_First = ref<Boolean>(false)
|
|
// 是否显示云台图标
|
let isPtz = ref(false)
|
|
// 显示高德地图矢量还是影像(2D/3D)
|
// let is2d = ref(true)
|
|
let yaw_speed = ref((payloadCameraMode.value == 'wide'? 1 : Math.ceil(zoom_factor.value))*10) // 云台横向角度
|
let pitch_speed = ref((payloadCameraMode.value == 'wide'? 1 : Math.ceil(zoom_factor.value))*10) // 云台纵向角度
|
|
let resultProps = reactive<any>(props.osdVisible.children.payloads_list[0])
|
// console.log(resultProps, '现在勒')
|
const store = useMyStore()
|
// 负载控制
|
const payloadSelectInfo = reactive<any>({
|
value: resultProps.payload_sn,
|
controlSource: resultProps.control_source,
|
options: [],
|
payloadIndex: resultProps.payload_index,
|
camera: '', // 当前负载osd信息
|
})
|
|
watch(() => store.state.wsMessage, (newValue, oldValue) => {
|
if (newValue && newValue.cameras) {
|
// console.log('监控值', newValue.cameras)
|
zoom_factor_watch.value = Math.ceil(newValue.cameras[0].zoom_factor)
|
if (!is_First.value) {
|
zoom_factor.value = zoom_factor_watch.value
|
zoom_factor_start.value = zoom_factor_watch.value
|
is_First.value = true
|
}
|
|
payloadSelectInfo.camera = newValue.cameras.find(
|
(item:any) =>
|
item.payload_index === payloadSelectInfo.payloadIndex,
|
)
|
} else {
|
payloadSelectInfo.camera = undefined
|
}
|
},
|
{
|
deep: true,
|
})
|
// ws 消息通知
|
const { droneControlSource, payloadControlSource } = useDroneControlWsEvent(props.sn, payloadSelectInfo.value)
|
watch(() => payloadControlSource, (controlSource) => {
|
payloadSelectInfo.controlSource = 'A'// controlSource.value
|
}, {
|
immediate: true,
|
deep: true
|
})
|
|
|
// 切换是否出现云台上下左右图
|
const ptzClick = () => {
|
isPtz.value = !isPtz.value
|
}
|
|
// 拍照
|
const onTakeCameraPhoto = () => {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
|
if (isActionComplete.value) {
|
ElMessage.warning('当前相机动作暂未完成,请勿点击!!')
|
return
|
}
|
if (payloadSelectInfo.camera?.recording_state) {
|
ElMessage.warning('当前摄像头正在录制视频,请先结束视频录制!!')
|
return
|
}
|
|
if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Photo) {
|
const res = switchCamera(CameraMode.Photo)
|
res.then((response:any) => {
|
if (!response) return
|
|
if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Photo) {
|
curTaskCameraPhoto(2000)
|
} else {
|
curTaskCameraPhoto(0)
|
}
|
})
|
|
return
|
}
|
|
curTaskCameraPhoto(0)
|
}
|
const curTaskCameraPhoto = (time:any) => {
|
isActionComplete.value = true
|
|
ElMessage({
|
message: '请稍后,正在拍摄中。。。',
|
duration: 2000,
|
})
|
setTimeout(() => {
|
let cameraType = ref('wide')
|
if (payloadCameraMode.value === 'zoom') {
|
cameraType.value = payloadCameraMode
|
}
|
console.log('1111',cameraType.value)
|
takeCameraPhoto(
|
props.osdVisible.dockSn,
|
payloadSelectInfo.payloadIndex,
|
cameraType.value
|
).then((photoRes:any) => {
|
if (!photoRes) {
|
return
|
}
|
|
isActionComplete.value = false
|
})
|
}, time)
|
}
|
const switchCamera = (camera_mode:any) => {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
return switchCameraMode(props.osdVisible.dockSn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
camera_mode: camera_mode,
|
video_type: payloadCameraMode.value,
|
})
|
}
|
|
// 相机操作
|
const onAuthPayload = () => {
|
//负载控制
|
authPayload(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex).then(
|
(payloadRes:any) => {
|
if (payloadRes.code !== 0)
|
return ElMessage.error('获得有效载荷控制失败,请稍后重试!!')
|
payloadControlSource.value = ControlSource.A
|
ElMessage.success('成功获得有效载荷控制')
|
},
|
)
|
}
|
// 开始录像
|
const onStartCameraRecording = () => {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
if (isActionComplete.value) {
|
ElMessage.warning('当前相机动作暂未完成,请勿点击!!')
|
return
|
}
|
|
if (payloadSelectInfo.camera?.recording_state) {
|
return ElMessage.warning(
|
'当前摄像头正在录制视频,请勿重复点击!!',
|
)
|
}
|
|
if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Video) {
|
const res = switchCamera(CameraMode.Video)
|
res.then((response) => {
|
if (!response) return
|
if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Video) {
|
curStartCameraRecording(2000)
|
} else {
|
curStartCameraRecording(0)
|
}
|
})
|
|
return
|
}
|
|
curStartCameraRecording(0)
|
}
|
|
const curStartCameraRecording = (time:any) => {
|
isActionComplete.value = true
|
|
ElMessage({
|
message: '请稍后,正在开启视频录制。。。',
|
duration: 2000,
|
})
|
|
setTimeout(() => {
|
startCameraRecording(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex)
|
.then((res) => {
|
if (res.code !== 0)
|
return ElMessage.warning('开始录制失败,请稍后重试!!')
|
ElMessage.success('开始录制成功')
|
// 开始录像
|
isCameraRecording.value = true
|
isActionComplete.value = false
|
}).catch((e) => {
|
isActionComplete.value = false
|
})
|
}, time)
|
}
|
|
// 停止录像
|
const onStopCameraRecording = () => {
|
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
|
return
|
}
|
if (isActionComplete.value) {
|
ElMessage.warning('当前相机动作暂未完成,请勿点击!!')
|
return
|
}
|
isActionComplete.value = true
|
stopCameraRecording(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex)
|
.then((res) => {
|
if (res.code !== 0)
|
return ElMessage.warning('停止录制失败,请稍后重试!!')
|
ElMessage.success('停止录制成功')
|
// 停止录像
|
isCameraRecording.value = false
|
isActionComplete.value = false
|
})
|
.catch((e) => {
|
isActionComplete.value = false
|
})
|
}
|
|
|
// 缩放
|
const onZoom = (value:any) => {
|
if (value) {
|
zoom_factor.value += 1
|
if (zoom_factor.value >= 200) {
|
zoom_factor.value = 200
|
}
|
} else {
|
zoom_factor.value -= 1
|
if (zoom_factor.value <= 2) {
|
zoom_factor.value = 2
|
}
|
console.log('9999', zoom_factor.value)
|
}
|
switchLivestream({
|
video_id: `${props.osdVisible.dockSn}/${payloadSelectInfo.payloadIndex}/normal-0`,
|
video_type: 'zoom',
|
}).then((res:any) => {
|
if (res.data.code != 0) return
|
// 只考虑 广角:wide模式
|
changeCameraFocalLength(props.osdVisible.dockSn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
camera_type: 'zoom',
|
zoom_factor: zoom_factor.value,
|
}, value)
|
})
|
}
|
|
// const onChangeD = () => {
|
// is2d.value = !is2d.value
|
// store.commit('SET_MAP_SETTING_MODE', is2d.value?2:3);
|
// loadLAYER();
|
// }
|
|
// 重置云台角度
|
const onResetGimbal = async (resetMode:any) => {
|
try {
|
await resetGimbal(props.osdVisible.dockSn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
reset_mode: resetMode,
|
})
|
await changeCameraFocalLength(props.osdVisible.dockSn, {
|
payload_index: payloadSelectInfo.payloadIndex,
|
camera_type: 'zoom',
|
zoom_factor: zoom_factor_start.value,
|
})
|
// this.zoomMultiple = 8 // 设置步长
|
} catch (err) {
|
console.log(err)
|
}
|
}
|
// 记得加loading
|
// 云台上 下 左 右
|
const ytClick = async (value:String) => {
|
if (payloadCameraMode.value == 'zoom' && zoom_factor.value > 35 && (value == 'up' || value == 'right')) { pitch_speed.value = 350 }
|
if (payloadCameraMode.value == 'zoom' && zoom_factor.value > 35 && (value == 'left' || value == 'down')) { pitch_speed.value = -350 }
|
if (value == 'up' || value == 'down') {
|
postPayloadCommands(props.osdVisible.dockSn, {cmd: 'camera_screen_drag',
|
data: {
|
locked: false,
|
payload_index: payloadSelectInfo.payloadIndex,
|
pitch_speed: value == 'up' ? pitch_speed.value : -pitch_speed.value,
|
yaw_speed: 0,
|
}})
|
} else if ((value == 'left' || value == 'right')) {
|
postPayloadCommands(props.osdVisible.dockSn, {cmd: 'camera_screen_drag',
|
data: {
|
locked: false,
|
payload_index: payloadSelectInfo.payloadIndex,
|
pitch_speed: 0,
|
yaw_speed: value == 'right' ? pitch_speed.value : -pitch_speed.value,
|
}})
|
}
|
}
|
|
// 视频大小的缩放
|
// const changeModelVideo = (value:Boolean) => {
|
// isBigVideo.value = value
|
// emitEvents('changeModelVideo', value)
|
// }
|
|
</script>
|
|
<style lang="scss" scoped>
|
.ptz-control {
|
.btn-group {
|
position: absolute;
|
right: 0.2rem;
|
bottom: 0rem;
|
.btn-item {
|
width: 2rem;
|
height: 2rem;
|
border-radius: 3px;
|
background-color: rgba(0, 0, 0, 0.5);
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
overflow: hidden;
|
cursor: pointer;
|
margin-bottom: 0.5rem;
|
pointer-events: all;
|
img {
|
width: 1.6rem;
|
height: 1.6rem;
|
}
|
}
|
.actived-blue {
|
background-image: none;
|
background-color: rgba(23, 124, 198, 0.7);
|
}
|
}
|
.circle-box {
|
width: 100px;
|
height: 100px;
|
border-radius: 50%;
|
overflow: hidden;
|
background-color: rgba(0, 0, 0, 0.5);
|
position: fixed;
|
top: 20%;
|
left: 1%;
|
|
.top,
|
.left,
|
.bottom,
|
.right {
|
width: 10px;
|
height: 10px;
|
position: absolute;
|
cursor: pointer;
|
z-index: 2;
|
}
|
|
.top {
|
top: -2px;
|
left: 50%;
|
transform: translateX(-50%);
|
border-top: 10px solid transparent;
|
border-right: 10px solid transparent;
|
border-left: 10px solid transparent;
|
border-bottom: 10px solid #fff;
|
|
&:hover {
|
&~.blue-bgc {
|
border-top: 50px solid #409eff;
|
}
|
}
|
}
|
|
.left {
|
top: 50%;
|
left: -2px;
|
transform: translateY(-50%);
|
border-top: 10px solid transparent;
|
border-right: 10px solid #fff;
|
border-left: 10px solid transparent;
|
border-bottom: 10px solid transparent;
|
|
&:hover {
|
&~.blue-bgc {
|
border-left: 50px solid #409eff;
|
}
|
}
|
}
|
|
.bottom {
|
bottom: -2px;
|
left: 50%;
|
transform: translateX(-50%);
|
border-top: 10px solid #fff;
|
border-right: 10px solid transparent;
|
border-left: 10px solid transparent;
|
border-bottom: 10px solid transparent;
|
|
&:hover {
|
&~.blue-bgc {
|
border-bottom: 50px solid #409eff;
|
}
|
}
|
}
|
|
.right {
|
top: 50%;
|
right: -2px;
|
transform: translateY(-50%);
|
border-top: 10px solid transparent;
|
border-right: 10px solid transparent;
|
border-left: 10px solid #fff;
|
border-bottom: 10px solid transparent;
|
|
&:hover {
|
&~.blue-bgc {
|
border-right: 50px solid #409eff;
|
}
|
}
|
}
|
|
.reset-center {
|
position: absolute;
|
width: 40%;
|
height: 40%;
|
left: 50%;
|
top: 50%;
|
transform: translate(-50%, -50%);
|
border-radius: 50%;
|
background-color: rgb(63, 66, 68);
|
z-index: 2;
|
cursor: pointer;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
|
i {
|
color: #fff;
|
font-size: 20px;
|
font-weight: bolder;
|
}
|
|
&:hover {
|
background-color: #409eff;
|
}
|
}
|
|
.blue-bgc {
|
width: 100px;
|
height: 100px;
|
position: absolute;
|
top: 0;
|
left: 0;
|
z-index: 1;
|
border-top: 50px solid transparent;
|
border-right: 50px solid transparent;
|
border-left: 50px solid transparent;
|
border-bottom: 50px solid transparent;
|
}
|
}
|
}
|
</style>
|