import { getPtzControlPower, ptzControl } from '@/api/video-controller.js' export class PtzController { constructor() { // 使用Map存储每个设备的controlToken和过期时间 this.tokenMap = new Map() // 格式: { deviceId: { token, expireTime } } this.ptzSpeed = 4 } /** * 获取云台控制权 * @param {Object} stream - 视频流对象,包含deviceId和channelId * @returns {Promise} 返回控制令牌 */ getPtzToken(stream) { return new Promise((resolve, reject) => { const deviceId = stream.deviceId // 检查是否有该设备的有效token const tokenInfo = this.tokenMap.get(deviceId) if (tokenInfo && tokenInfo.expireTime > Date.now()) { resolve(tokenInfo.token) return } // 获取新的控制令牌 getPtzControlPower({ device_id: deviceId, channel_id: stream.channelId }).then(response => { if (response?.control_token) { const token = response.control_token // 设置令牌过期时间,默认为2分钟 const expireTime = Date.now() + 2 * 60 * 1000 // 存储设备特定的token信息 this.tokenMap.set(deviceId, { token, expireTime, channelId: stream.channelId }) resolve(token) } else { reject(new Error('获取控制令牌失败:无效的响应数据')) } }).catch(error => { console.error('获取云台控制权失败:', error.response.data.error_msg) if( error.response?.data?.error_code === "IVM.20000029") { reject(new Error('设备已被别人占用')) } else { reject(error) } }) }) } /** * 开始云台控制 * @param {Object} params - 控制参数 * @param {string} params.direction - 控制方向 * @param {Object} params.stream - 视频流对象 * @param {Function} params.onSuccess - 成功回调 * @param {Function} params.onError - 错误回调 */ startPtzControl({ direction, stream, onSuccess, onError }) { if (!stream) { onError && onError(new Error('请先选择设备')) return } const deviceId = stream.deviceId console.log('startPtzControl', direction, deviceId) // 先获取云台控制权 this.getPtzToken(stream).then(controlToken => { // 验证controlToken是否是当前设备的controlToken const tokenInfo = this.tokenMap.get(deviceId) if (!tokenInfo || tokenInfo.token !== controlToken) { onError && onError(new Error('控制令牌不匹配,正在重新获取...')) // 清除可能过期的token信息 this.tokenMap.delete(deviceId) // 重新尝试控制 setTimeout(() => { this.startPtzControl({ direction, stream, onSuccess, onError }) }, 500) return } const params = { mode: "NON_POINT", direction: direction, speed: this.ptzSpeed, control_lock: controlToken } // 调用云台控制API ptzControl({ user_id: process.env.VUE_APP_HUAWEI_USER_ID, device_id: deviceId, channel_id: stream.channelId, direction: params, speed: this.ptzSpeed }).then(response => { console.log('云台控制成功:', direction) onSuccess && onSuccess(response) }).catch(error => { console.error('云台控制失败:', error) // 如果是控制权失效,尝试重新获取控制权 if (error.response?.data?.error_code === 'IVM.00000010' || error.response?.data?.error_msg?.includes('control_lock')) { onError && onError(new Error('控制权已过期,正在重新获取...')) // 清除已过期的token this.tokenMap.delete(deviceId) // 重新尝试控制 setTimeout(() => { this.startPtzControl({ direction, stream, onSuccess, onError }) }, 500) } else { onError && onError(new Error(`云台${direction}控制失败: ${error.message || '未知错误'}`)) } }) }).catch(error => { onError && onError(new Error(`获取云台控制权失败: ${error.message || '未知错误'}`)) }) } /** * 停止云台控制 * @param {Object} stream - 视频流对象 * @param {Function} onError - 错误回调 */ ptzStop(stream, onError) { if (!stream) return const deviceId = stream.deviceId const tokenInfo = this.tokenMap.get(deviceId) if (!tokenInfo || !tokenInfo.token) { console.log('没有找到设备的控制令牌,无需停止云台') return } ptzControl({ user_id: process.env.VUE_APP_HUAWEI_USER_ID, device_id: deviceId, channel_id: stream.channelId, direction: { mode: "NON_POINT", direction: "STOP", speed: this.ptzSpeed, control_lock: tokenInfo.token } }).then(() => { console.log('云台停止成功') // 清理该设备的token信息 this.tokenMap.delete(deviceId) }).catch(error => { console.error('云台停止失败:', error) // 即使失败也清理token信息,因为可能是token已过期 this.tokenMap.delete(deviceId) onError && onError(new Error(`云台停止失败: ${error.message || '未知错误'}`)) }) } /** * 设置云台速度 * @param {number} speed - 速度值(1-7) */ setPtzSpeed(speed) { this.ptzSpeed = Math.max(1, Math.min(7, speed)) } /** * 获取当前云台速度 * @returns {number} 当前速度值 */ getPtzSpeed() { return this.ptzSpeed } } // 导出单例实例 export const ptzController = new PtzController()