|
|
@@ -3,66 +3,78 @@
|
|
|
* 通过独立的 PTZ 后端服务调用海康威视 ISAPI 协议
|
|
|
*/
|
|
|
|
|
|
+// ==================== 类型定义 ====================
|
|
|
+
|
|
|
export interface PTZConfig {
|
|
|
- host: string // 摄像头 IP 地址
|
|
|
- username: string // 用户名
|
|
|
- password: string // 密码
|
|
|
- channel?: number // 通道号,默认 1
|
|
|
+ host: string
|
|
|
+ username: string
|
|
|
+ password: string
|
|
|
+ channel?: number
|
|
|
+}
|
|
|
+
|
|
|
+export interface PTZCommand {
|
|
|
+ pan: number
|
|
|
+ tilt: number
|
|
|
+ zoom: number
|
|
|
}
|
|
|
|
|
|
-export interface PTZDirection {
|
|
|
- pan: number // -100 ~ 100, 负左正右
|
|
|
- tilt: number // -100 ~ 100, 负下正上
|
|
|
+export interface PTZResult {
|
|
|
+ success: boolean
|
|
|
+ error?: string
|
|
|
+ data?: unknown
|
|
|
}
|
|
|
|
|
|
-// PTZ 后端服务地址
|
|
|
+// ==================== 常量 ====================
|
|
|
+
|
|
|
const PTZ_API_BASE = 'http://localhost:3002'
|
|
|
+const DEFAULT_SPEED = 50
|
|
|
|
|
|
-// 方向预设值
|
|
|
+// 方向预设值 (单位向量)
|
|
|
export const PTZ_DIRECTIONS = {
|
|
|
- UP: { pan: 0, tilt: 50 },
|
|
|
- DOWN: { pan: 0, tilt: -50 },
|
|
|
- LEFT: { pan: -50, tilt: 0 },
|
|
|
- RIGHT: { pan: 50, tilt: 0 },
|
|
|
- UP_LEFT: { pan: -50, tilt: 50 },
|
|
|
- UP_RIGHT: { pan: 50, tilt: 50 },
|
|
|
- DOWN_LEFT: { pan: -50, tilt: -50 },
|
|
|
- DOWN_RIGHT: { pan: 50, tilt: -50 },
|
|
|
+ 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 控制命令
|
|
|
- * 通过独立的 PTZ 后端服务,支持 Digest Auth
|
|
|
+ * 发送 PTZ 控制命令 (统一接口)
|
|
|
*/
|
|
|
-export async function sendPTZCommand(
|
|
|
- config: PTZConfig,
|
|
|
- direction: PTZDirection
|
|
|
-): Promise<{ success: boolean; error?: string }> {
|
|
|
+async function sendCommand(config: PTZConfig, command: PTZCommand): Promise<PTZResult> {
|
|
|
try {
|
|
|
const response = await fetch(`${PTZ_API_BASE}/ptz/control`, {
|
|
|
method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json'
|
|
|
- },
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify({
|
|
|
host: config.host,
|
|
|
username: config.username,
|
|
|
password: config.password,
|
|
|
channel: config.channel || 1,
|
|
|
- pan: direction.pan,
|
|
|
- tilt: direction.tilt,
|
|
|
- zoom: 0
|
|
|
+ pan: command.pan,
|
|
|
+ tilt: command.tilt,
|
|
|
+ zoom: command.zoom
|
|
|
})
|
|
|
})
|
|
|
|
|
|
const data = await response.json()
|
|
|
-
|
|
|
- if (data.code === 200) {
|
|
|
- return { success: true }
|
|
|
- } else {
|
|
|
- return { success: false, error: data.msg || 'Unknown error' }
|
|
|
- }
|
|
|
+ return data.code === 200 ? { success: true, data: data.data } : { success: false, error: data.msg || 'Unknown error' }
|
|
|
} catch (error) {
|
|
|
return { success: false, error: String(error) }
|
|
|
}
|
|
|
@@ -71,15 +83,11 @@ export async function sendPTZCommand(
|
|
|
/**
|
|
|
* 获取 PTZ 状态
|
|
|
*/
|
|
|
-export async function getPTZStatus(
|
|
|
- config: PTZConfig
|
|
|
-): Promise<{ success: boolean; data?: string; error?: string }> {
|
|
|
+export async function getPTZStatus(config: PTZConfig): Promise<PTZResult & { data?: string }> {
|
|
|
try {
|
|
|
const response = await fetch(`${PTZ_API_BASE}/ptz/status`, {
|
|
|
method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json'
|
|
|
- },
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify({
|
|
|
host: config.host,
|
|
|
username: config.username,
|
|
|
@@ -89,27 +97,60 @@ export async function getPTZStatus(
|
|
|
})
|
|
|
|
|
|
const data = await response.json()
|
|
|
-
|
|
|
- if (data.code === 200) {
|
|
|
- return { success: true, data: data.data?.raw }
|
|
|
- } else {
|
|
|
- return { success: false, error: data.msg || 'Unknown error' }
|
|
|
- }
|
|
|
+ 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: keyof typeof PTZ_DIRECTIONS) {
|
|
|
- return sendPTZCommand(config, PTZ_DIRECTIONS[direction])
|
|
|
+export function startPTZ(config: PTZConfig, direction: PTZDirectionKey, speed: number = DEFAULT_SPEED): Promise<PTZResult> {
|
|
|
+ const dir = PTZ_DIRECTIONS[direction]
|
|
|
+ return sendCommand(config, {
|
|
|
+ pan: dir.pan * speed,
|
|
|
+ tilt: dir.tilt * speed,
|
|
|
+ zoom: 0
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 停止 PTZ 移动
|
|
|
*/
|
|
|
-export function stopPTZ(config: PTZConfig) {
|
|
|
- return sendPTZCommand(config, PTZ_DIRECTIONS.STOP)
|
|
|
+export function stopPTZ(config: PTZConfig): Promise<PTZResult> {
|
|
|
+ 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<PTZResult> {
|
|
|
+ const zoom = PTZ_ZOOM_DIRECTIONS[direction]
|
|
|
+ return sendCommand(config, {
|
|
|
+ pan: 0,
|
|
|
+ tilt: 0,
|
|
|
+ zoom: zoom.zoom * speed
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 停止缩放
|
|
|
+ */
|
|
|
+export function stopZoom(config: PTZConfig): Promise<PTZResult> {
|
|
|
+ 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
|