/** * PTZ 云台控制 API * 通过独立的 PTZ 后端服务调用海康威视 ISAPI 协议 */ // ==================== 类型定义 ==================== export interface PTZConfig { host: string username: string password: string channel?: number } export interface PTZCommand { pan: number tilt: number zoom: number } export interface PTZResult { success: boolean error?: string data?: unknown } // ==================== 常量 ==================== const PTZ_API_BASE = 'http://localhost:3002' const DEFAULT_SPEED = 50 // 方向预设值 (单位向量) export const PTZ_DIRECTIONS = { UP: { pan: 0, tilt: 1 }, DOWN: { pan: 0, tilt: -1 }, LEFT: { pan: -1, tilt: 0 }, RIGHT: { pan: 1, tilt: 0 }, UP_LEFT: { pan: -1, tilt: 1 }, UP_RIGHT: { pan: 1, tilt: 1 }, DOWN_LEFT: { pan: -1, tilt: -1 }, DOWN_RIGHT: { pan: 1, tilt: -1 }, STOP: { pan: 0, tilt: 0 } } as const // 缩放预设值 (单位向量) export const PTZ_ZOOM_DIRECTIONS = { IN: { zoom: 1 }, OUT: { zoom: -1 }, STOP: { zoom: 0 } } as const export type PTZDirectionKey = keyof typeof PTZ_DIRECTIONS export type PTZZoomKey = keyof typeof PTZ_ZOOM_DIRECTIONS // ==================== 核心 API ==================== /** * 发送 PTZ 控制命令 (统一接口) */ async function sendCommand(config: PTZConfig, command: PTZCommand): Promise { try { const response = await fetch(`${PTZ_API_BASE}/ptz/control`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ host: config.host, username: config.username, password: config.password, channel: config.channel || 1, pan: command.pan, tilt: command.tilt, zoom: command.zoom }) }) const data = await response.json() return data.code === 200 ? { success: true, data: data.data } : { success: false, error: data.msg || 'Unknown error' } } catch (error) { return { success: false, error: String(error) } } } /** * 获取 PTZ 状态 */ export async function getPTZStatus(config: PTZConfig): Promise { try { const response = await fetch(`${PTZ_API_BASE}/ptz/status`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ host: config.host, username: config.username, password: config.password, channel: config.channel || 1 }) }) const data = await response.json() return data.code === 200 ? { success: true, data: data.data?.raw } : { success: false, error: data.msg || 'Unknown error' } } catch (error) { return { success: false, error: String(error) } } } // ==================== 方向控制 ==================== /** * 开始 PTZ 移动 * @param direction 方向: UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT, STOP * @param speed 速度 1-100,默认 50 */ export function startPTZ(config: PTZConfig, direction: PTZDirectionKey, speed: number = DEFAULT_SPEED): Promise { const dir = PTZ_DIRECTIONS[direction] return sendCommand(config, { pan: dir.pan * speed, tilt: dir.tilt * speed, zoom: 0 }) } /** * 停止 PTZ 移动 */ export function stopPTZ(config: PTZConfig): Promise { return startPTZ(config, 'STOP') } // ==================== 缩放控制 ==================== /** * 开始缩放 * @param direction 缩放方向: IN, OUT, STOP * @param speed 速度 1-100,默认 50 */ export function startZoom(config: PTZConfig, direction: PTZZoomKey, speed: number = DEFAULT_SPEED): Promise { const zoom = PTZ_ZOOM_DIRECTIONS[direction] return sendCommand(config, { pan: 0, tilt: 0, zoom: zoom.zoom * speed }) } /** * 停止缩放 */ export function stopZoom(config: PTZConfig): Promise { return startZoom(config, 'STOP') } // ==================== 便捷方法 ==================== export const zoomIn = (config: PTZConfig, speed?: number) => startZoom(config, 'IN', speed) export const zoomOut = (config: PTZConfig, speed?: number) => startZoom(config, 'OUT', speed) export const zoomStop = stopZoom