123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 |
- <template>
- <div style="display: flex; justify-content: left;">
- <div class="control-mic">
- <microphone :channel-id="channelId" :device-id="deviceId" :enable-debug="enableDebug"
- :http-hook="httpHook"
- :https-hook="httpsHook"
- :push-key="pushKey"
- ></microphone>
- </div>
- <div class="control-wrapper">
- <div class="control-btn control-top" @mousedown="ptzControlHandleDown('up')" @mouseup="ptzControlHandleUp('up')">
- <i class="el-icon-caret-top"></i>
- <div class="control-inner-btn control-inner"></div>
- </div>
- <div class="control-btn control-left" @mousedown="ptzControlHandleDown('left')" @mouseup="ptzControlHandleUp('left')">
- <i class="el-icon-caret-left"></i>
- <div class="control-inner-btn control-inner"></div>
- </div>
- <div class="control-btn control-bottom" @mousedown="ptzControlHandleDown('down')" @mouseup="ptzControlHandleUp('down')">
- <i class="el-icon-caret-bottom"></i>
- <div class="control-inner-btn control-inner"></div>
- </div>
- <div class="control-btn control-right" @mousedown="ptzControlHandleDown('right')" @mouseup="ptzControlHandleUp('right')">
- <i class="el-icon-caret-right"></i>
- <div class="control-inner-btn control-inner"></div>
- </div>
- <div class="control-round">
- <div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
- </div>
- <!-- 放大 -->
- <div style="position: absolute; left: 7.25rem; top: -1.1rem" @mousedown="ptzCamera('zoomin')"
- @mouseup="ptzCamera('stop')">
- <i class="el-icon-zoom-in control-zoom-btn" style="font-size: 1.875rem;"></i>
- </div>
- <!-- 聚焦 -->
- <div style="position: absolute; left: 7.25rem; top: 1.25rem"
- @click="ptzCameraFocus('focus')" >
- <i class="el-icon-aim control-zoom-btn" style="font-size: 1.875rem;"></i>
- </div>
- <!-- 缩小 -->
- <div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;" @mousedown="ptzCamera('zoomout')" @mouseup="ptzCamera('stop')"><i class="el-icon-zoom-out control-zoom-btn"></i></div>
- <div class="contro-speed" style="position: absolute; left: 4px; top: 7rem; width: 9rem;">
- <el-slider v-model="controSpeed" :max="255"></el-slider>
- </div>
- </div>
- <div class="control-panel">
- <!-- 预置位 -->
- <el-tabs tab-position="left" style="height: 210px;width:100%">
- <el-tab-pane :label="$t('device.preset.title')" >
- <!-- 预置位查询 -->
- <el-table
- v-loading="presetLoading"
- :data="presetList"
- height="260px"
- width="100%"
- :row-style="{height: '20px',fontSize: '12px'}"
- style="width: 100%;font-size: 16px">
- <el-table-column
- prop="ind"
- label="No"
- width="90">
- </el-table-column>
- <el-table-column
- prop="remark"
- :label="$t('comment')"
- width="100">
- </el-table-column>
- <el-table-column
- prop="operation"
- >
- <template slot-scope="scope">
- <el-button class="ml-2" type="primary" size="mini" @click="presetPosition(129, scope.row.ind)">
- {{ $t('setting') }}
- </el-button>
- <el-button v-if="scope.row.load" class="ml-2" type="primary" size="mini" @click="presetPosition(130, scope.row.ind)">
- {{ $t('run') }}
- </el-button>
- <el-button v-if="scope.row.load" class="ml-2" type="danger" size="mini" @click="presetPosition(131, scope.row.ind)">
- {{ $t('delete') }}
- </el-button>
- </template>
- <template slot="header" slot-scope="scope">
- <div class="w-full flex">
- <!-- input number -->
- <el-popover
- placement="bottom"
- :title="$t('device.preset.fastPreset')"
- width="400"
- trigger="click">
- <div class="w-full flex justify-center items-center">
- <el-input-number size="mini" :min="1" :max="255" v-model="presetPos"></el-input-number>
- <el-button class="ml-2" type="primary" size="mini" @click="presetPosition(129, presetPos)">
- {{ $t('setting') }}
- </el-button>
- <el-button class="ml-2" type="primary" size="mini" @click="presetPosition(130, presetPos)">
- {{ $t('run') }}
- </el-button>
- <el-button class="ml-2" type="danger" size="mini" @click="presetPosition(131, presetPos)">
- {{ $t('delete') }}
- </el-button>
- </div>
- <el-button slot="reference">
- {{ $t('device.preset.fastPreset') }}
- </el-button>
- </el-popover>
- <el-button class="ml-2" type="primary" size="mini" icon="el-icon-refresh" @click="queryPresetPos"></el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
- <!-- <el-tab-pane label="巡航" >-->
- <!-- <el-button-group>-->
- <!-- <el-tag size="medium">巡航速度</el-tag>-->
- <!-- <el-input-number size="mini" v-model="cruisingSpeed" :precision="0" :min="1" :max="4095"></el-input-number>-->
- <!-- <el-button size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置</el-button>-->
- <!-- <br/>-->
- <!-- <hr/>-->
- <!-- <el-tag size="medium">停留时间</el-tag>-->
- <!-- <el-input-number size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>-->
- <!-- <el-button size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置</el-button>-->
- <!-- <br/>-->
- <!-- <hr/>-->
- <!-- <el-tag size="medium">巡航组编号</el-tag>-->
- <!-- <el-input-number size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number>-->
- <!-- <el-button size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点</el-button>-->
- <!-- <el-button size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点</el-button>-->
- <!-- <el-button size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组</el-button>-->
- <!-- <el-button size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航</el-button>-->
- <!-- <br/>-->
- <!-- <hr/>-->
- <!-- <el-tag size="medium">扫描速度</el-tag>-->
- <!-- <el-input-number size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>-->
- <!-- <el-button size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置</el-button>-->
- <!-- <br/>-->
- <!-- <hr/>-->
- <!-- <el-tag size="medium">扫描组编号</el-tag>-->
- <!-- <el-input-number size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number>-->
- <!-- <el-button size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界</el-button>-->
- <!-- <el-button size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界</el-button>-->
- <!-- <el-button size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(137, scanGroup, 0)">扫描</el-button>-->
- <!-- <el-button size="mini" type="danger" icon="el-icon-switch-button" @click="ptzCamera('stop')">停止</el-button>-->
- <!-- </el-button-group>-->
- <!-- </el-tab-pane>-->
- <!-- <el-tab-pane label="扫描">角色管理</el-tab-pane>-->
- </el-tabs>
- </div>
- </div>
- </template>
- <script>
- import handle from "@/until/handle";
- import Microphone from "@/components/common/microphone"
- let queryTimer = null;
- let isZoom = null;
- let sendStopTimer = null;
- let changToLongDownStateTimer = null;
- // 是否长按
- let isLongDown = null;
- // 长按与连续短按的时间
- let pressDuration = 1000;
- // 从第一次按下按钮到执行命令时的重复时间
- let clickDuration = 700;
- // 连点持续时间
- let conClickDuration = 350;
- // 计时器
- let clickTimer = null;
- let conClickTimer = null;
- let pressTimer = null;
- let sendEndTimer = null;
- /**
- * @description 云台控制组件
- * @param deviceId 设备id
- * @param channelId 通道id
- */
- export default {
- name: "ptzControl",
- components: {Microphone},
- props:{
- deviceId:{require:true},
- channelId:{require:true}
- },
- data(){
- return {
- enableDebug: true,
- isLoading: false,
- tabActiveName: 'control',
- controSpeed: 30,
- zoomSpeed: 30,
- presetPos: 1,
- cruisingSpeed: 100,
- cruisingTime: 5,
- cruisingGroup: 0,
- scanSpeed: 100,
- scanGroup: 0,
- direction: '',// 当前按下的按钮的方向
- step:0,//步长
- stepValue:5,//
- clickCount: 0,// 连续点击数量
- isClick: true,// 是否为点击
- isLongClick: false,// 是否为连续点击
- isPress: false,// 是否为长按
- isSendAutoMove: false,// 是否为长按命令倒计时中
- pushKey: "",
- httpsHook: "",
- httpHook: "",
- presetList: [],
- presetLoading: false,
- presetInd: 1,// 选择的预置位id
- }
- },
- beforeMount() {
- // this.queryPushParam();
- },
- mounted() {
- this.queryPresetPos();
- },
- methods:{
- timeSendFocus(){
- queryTimer = setTimeout(async ()=>{
- await this.ptzCameraFocus();
- queryTimer = null;
- },700)
- },
- timeSendStop(){
- console.log('短按自动跟发stop');
- this.ptzCamera('stop');
- },
- testHandle(key, item){
- console.log('testHandle')
- console.log(key)
- console.log(item)
- },
- async ptzCamera(command){
- console.log('云台控制:' + command);
- let isSendFocus = false;
- let that = this;
- let isAutoSendStop = false;
- if(command === 'zoomin' || command === 'zoomout'){
- isZoom = true;
- if (queryTimer!=null){
- // 中止
- clearTimeout(queryTimer);
- }
- }else if(command === 'stop' && isZoom){
- // 发送待定值
- isSendFocus = true;
- }else{
- isZoom = false;
- // down 发送特定指令值.
- }
- clearTimeout(sendStopTimer);
- sendStopTimer = null;
- // 不连续发送指令
- if(command !== 'stop'){
- isLongDown = false;
- // 非停止指令
- changToLongDownStateTimer = setTimeout(()=>{
- isLongDown = true;
- },1000)
- }else{
- // 确保不是自动对焦
- if(!isLongDown && !isSendFocus){
- // 短按阻止立即发送end,等待900ms发送end
- sendStopTimer = setTimeout(()=>{
- this.timeSendStop();
- },1200);
- console.log('进行短按操作');
- return;
- }
- // 清除定时器
- clearTimeout(changToLongDownStateTimer);
- changToLongDownStateTimer = null;
- }
- let [err,res] = await handle(this.$axios.axios({
- method: 'post',
- url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.zoomSpeed
- }));
- if(err){
- console.error(err)}
- if(isSendFocus){
- // TODO 在变焦后自动跟发聚焦指令
- // this.timeSendFocus();
- }
- },
- async ptzCameraFocus(){
- // todo 发送聚焦http指令
- console.log("摄像头聚焦");
- let url = `/api/ptz/focus/`
- url+=`${this.deviceId}/`;
- url+=`${this.channelId}/`;
- let [err,res] = await handle(this.$axios.axios({
- method: 'post',
- url: url
- }));
- if(err){
- console.error(err)}
- },
- presetPosition: function (cmdCode, presetPos) {
- console.log('预置位控制:' + this.presetPos + ' : 0x' + cmdCode.toString(16));
- let that = this;
- this.$axios.axios({
- method: 'post',
- url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '¶meter1=0¶meter2=' + presetPos + '&combindCode2=0'
- }).then(function (res) {
- if (cmdCode === 129 || cmdCode === 131){
- this.queryPresetPos();
- }
- });
- },
- setSpeedOrTime: function (cmdCode, groupNum, parameter) {
- let that = this;
- let parameter2 = parameter % 256;
- let combindCode2 = Math.floor(parameter / 256) * 16;
- console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter2.toString(16) + ' 0x' + combindCode2.toString(16));
- this.$axios.axios({
- method: 'post',
- url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '¶meter1=' + groupNum + '¶meter2=' + parameter2 + '&combindCode2=' + combindCode2
- }).then(function (res) {});
- },
- setCommand: function (cmdCode, groupNum, parameter) {
- let that = this;
- console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter.toString(16) + ' 0x0');
- this.$axios.axios({
- method: 'post',
- url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '¶meter1=' + groupNum + '¶meter2=' + parameter + '&combindCode2=0'
- }).then(function (res) {});
- },
- /**
- * 按下云台控制按钮逻辑
- * @param direction 方向
- */
- ptzControlHandleDown(direction){
- if(sendStopTimer){
- clearTimeout(sendStopTimer);
- }
- console.log('--------------按下')
- this.direction = direction;
- this.isPress = false;
- this.isClick = true;
- // 1200
- pressTimer = setTimeout(()=>{
- console.log('长按')
- // 长按
- this.isPress = true;
- this.isClick=false;
- this.clickCount = 0;
- this.isSendAutoMove = false;
- // 发送云台移动命令,步长为0,视作连续移动
- this.sendCommand(this.direction,0);
- // 等待850毫秒,如果850毫秒内的抬起了鼠标,则发送停止命令
- setTimeout(()=>{
- console.log(`按下的延迟isSendAutoMove: ${this.isSendAutoMove}`)
- if(this.isSendAutoMove){
- console.log('长按抬起延迟结束');
- this.sendCommand('stop',0);
- }
- this.isSendAutoMove = true;
- },600);
- },pressDuration);
- // 处于连点状态,刷新连点计时器
- clearTimeout(conClickTimer);
- conClickTimer = null;
- },
- /**
- * 松开按钮逻辑
- * @param command
- */
- ptzControlHandleUp(command){
- if(this.isPress){
- // 如果没有抬起
- console.log(111111111)
- let _isSendAutoMove = this.isSendAutoMove;
- if(!_isSendAutoMove){
- console.log('长按')
- this.isSendAutoMove = true;
- return;
- }
- console.log('长按抬起结束')
- this.sendCommand('stop',0)
- }else{
- clearTimeout(pressTimer);
- this.clickCount ++;
- conClickTimer = setTimeout(()=>{
- // 结束连点,合并命令.
- this.isClick = false;
- conClickTimer=null;
- clickTimer=null;
- // 发送
- console.log(`快速点按${this.clickCount}`)
- clearTimeout(clickTimer);
- this.sendCommand(this.direction,this.clickCount);
- },conClickDuration);
- if(!clickTimer){
- clickTimer = setTimeout(()=>{
- // 连点超时
- if(this.isClick){
- clearTimeout(conClickTimer);
- conClickTimer=null;
- clickTimer=null;
- // 强制发送当前的
- console.log(`连按缓存${this.clickCount}`);
- this.sendCommand(this.direction,this.clickCount);
- }
- },clickDuration)
- }
- }
- },
- /**
- * 发送命令至服务器
- * @param command
- * @param step
- * @returns {Promise<void>}
- */
- async sendCommand(command,step=0)
- {
- console.log(`[send] ${command} - ${step}`);
- // step = step * 5
- let url = `/api/ptz/c/${this.deviceId}/${this.channelId}/?c=${command}&step=${step*this.stepValue}`
- console.log(url);
- let [err,res] = await handle(this.$axios.axios({
- method: 'post',
- url: url
- }));
- this.clickCount = 0;
- if(err){console.error(err)}
- },
- async queryPushParam()
- {
- let url = `/api/server/pushConfig`
- let [err,res] = await handle(this.$axios.axios({
- method: 'get',
- url: url
- }));
- if(err){
- console.error(err)
- return this.$message.error(err.message);
- }
- console.log(res);
- let response = res.data;
- if(response.code === 0){
- this.httpsHook = response.data["httpsHook"];
- this.httpHook = response.data["httpHook"];
- this.pushKey = response.data["pushKey"];
- }else{
- this.$message.warning(response.msg)
- }
- },
- async queryPresetPos(){
- console.log('请求预置位');
- this.presetLoading = true;
- let n_presetLength = 255;
- let presetTitle = this.$t('device.preset.title');
- let presetList = new Array(n_presetLength).fill({}).map((item,i)=>{
- return {
- ind: i+1,
- remark: `${presetTitle}${i+1}`,
- load: false
- }
- })
- console.log( presetList);
- let queryUrl = `/api/ptz/preset/query/${this.deviceId}/${this.channelId}`
- // 加载预置位
- let [err,res] = await handle(this.$axios.axios({
- method: 'get',
- url: queryUrl
- }));
- this.presetLoading = false;
- if (err){
- if(err){
- console.error(err)
- this.presetList = presetList;
- return this.$message.error(err.message);
- }
- }
- console.log(res);
- /**
- * res = {
- * "code": 0,
- * "msg": "success",
- * "data": [
- * {
- * "presetId": 1,
- * "presetName": "预置位1",
- * }
- * ]
- */
- let response = res.data;
- if (response.code === 0){
- response.data.forEach(item=>{
- console.log(item.presetId)
- console.log(presetList[item.presetId-1])
- presetList[item.presetId-1] = {
- ind: item.presetId,
- remark: item.presetName,
- load: true,
- }
- });
- }else{
- this.$message.warning(response.msg)
- }
- console.log(presetList);
- this.presetList = presetList;
- }
- },
- }
- </script>
- <style scoped>
- .control-mic{
- width: 4rem;
- /*height: 100%;*/
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .control-wrapper {
- position: relative;
- width: 6.25rem;
- height: 6.25rem;
- max-width: 6.25rem;
- max-height: 6.25rem;
- border-radius: 100%;
- margin-top: 1.5rem;
- margin-left: 0.5rem;
- flex-shrink: 0;
- float: left;
- }
- .control-panel {
- position: relative;
- width: 100%;
- top: 0;
- left: 5rem;
- height: 11rem;
- max-height: 11rem;
- }
- .control-btn {
- display: flex;
- justify-content: center;
- position: absolute;
- width: 44%;
- height: 44%;
- border-radius: 5px;
- border: 1px solid #78aee4;
- box-sizing: border-box;
- transition: all 0.3s linear;
- }
- .control-btn:hover {
- cursor:pointer
- }
- .control-btn i {
- font-size: 20px;
- color: #78aee4;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .control-btn i:hover {
- cursor:pointer
- }
- .control-zoom-btn:hover {
- cursor:pointer
- }
- .control-round {
- position: absolute;
- top: 21%;
- left: 21%;
- width: 58%;
- height: 58%;
- background: #fff;
- border-radius: 100%;
- }
- .control-round-inner {
- position: absolute;
- left: 13%;
- top: 13%;
- display: flex;
- justify-content: center;
- align-items: center;
- width: 70%;
- height: 70%;
- font-size: 40px;
- color: #78aee4;
- border: 1px solid #78aee4;
- border-radius: 100%;
- transition: all 0.3s linear;
- }
- .control-inner-btn {
- position: absolute;
- width: 60%;
- height: 60%;
- background: #fafafa;
- }
- .control-top {
- top: -8%;
- left: 27%;
- transform: rotate(-45deg);
- border-radius: 5px 100% 5px 0;
- }
- .control-top i {
- transform: rotate(45deg);
- border-radius: 5px 100% 5px 0;
- }
- .control-top .control-inner {
- left: -1px;
- bottom: 0;
- border-top: 1px solid #78aee4;
- border-right: 1px solid #78aee4;
- border-radius: 0 100% 0 0;
- }
- .control-top .fa {
- transform: rotate(45deg) translateY(-7px);
- }
- .control-left {
- top: 27%;
- left: -8%;
- transform: rotate(45deg);
- border-radius: 5px 0 5px 100%;
- }
- .control-left i {
- transform: rotate(-45deg);
- }
- .control-left .control-inner {
- right: -1px;
- top: -1px;
- border-bottom: 1px solid #78aee4;
- border-left: 1px solid #78aee4;
- border-radius: 0 0 0 100%;
- }
- .control-left .fa {
- transform: rotate(-45deg) translateX(-7px);
- }
- .control-right {
- top: 27%;
- right: -8%;
- transform: rotate(45deg);
- border-radius: 5px 100% 5px 0;
- }
- .control-right i {
- transform: rotate(-45deg);
- }
- .control-right .control-inner {
- left: -1px;
- bottom: -1px;
- border-top: 1px solid #78aee4;
- border-right: 1px solid #78aee4;
- border-radius: 0 100% 0 0;
- }
- .control-right .fa {
- transform: rotate(-45deg) translateX(7px);
- }
- .control-bottom {
- left: 27%;
- bottom: -8%;
- transform: rotate(45deg);
- border-radius: 0 5px 100% 5px;
- }
- .control-bottom i {
- transform: rotate(-45deg);
- }
- .control-bottom .control-inner {
- top: -1px;
- left: -1px;
- border-bottom: 1px solid #78aee4;
- border-right: 1px solid #78aee4;
- border-radius: 0 0 100% 0;
- }
- .control-bottom .fa {
- transform: rotate(-45deg) translateY(7px);
- }
- .trank {
- width: 80%;
- height: 180px;
- text-align: left;
- padding: 0 10%;
- overflow: auto;
- }
- .trankInfo {
- width: 80%;
- padding: 0 10%;
- }
- </style>
|