|
@@ -1,15 +1,21 @@
|
|
|
/**
|
|
/**
|
|
|
* PTZ 云台控制 API
|
|
* PTZ 云台控制 API
|
|
|
- * 通过独立的 PTZ 后端服务调用海康威视 ISAPI 协议
|
|
|
|
|
|
|
+ * 通过独立的 PTZ 后端服务调用 ONVIF 标准协议
|
|
|
|
|
+ * 支持厂家: HIKVISION (海康威视), ANPVIZ
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
// ==================== 类型定义 ====================
|
|
// ==================== 类型定义 ====================
|
|
|
|
|
|
|
|
|
|
+// 支持的摄像头厂家
|
|
|
|
|
+export type CameraVendor = 'HIKVISION' | 'ANPVIZ' | 'DAHUA' | 'CT-IP500' | 'SVBC'
|
|
|
|
|
+
|
|
|
export interface PTZConfig {
|
|
export interface PTZConfig {
|
|
|
host: string
|
|
host: string
|
|
|
username: string
|
|
username: string
|
|
|
password: string
|
|
password: string
|
|
|
channel?: number
|
|
channel?: number
|
|
|
|
|
+ cameraId?: number
|
|
|
|
|
+ vendor?: CameraVendor
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export interface PTZCommand {
|
|
export interface PTZCommand {
|
|
@@ -52,10 +58,57 @@ export const PTZ_ZOOM_DIRECTIONS = {
|
|
|
export type PTZDirectionKey = keyof typeof PTZ_DIRECTIONS
|
|
export type PTZDirectionKey = keyof typeof PTZ_DIRECTIONS
|
|
|
export type PTZZoomKey = keyof typeof PTZ_ZOOM_DIRECTIONS
|
|
export type PTZZoomKey = keyof typeof PTZ_ZOOM_DIRECTIONS
|
|
|
|
|
|
|
|
|
|
+// PTZ 方向命令
|
|
|
|
|
+export type PTZCommandType =
|
|
|
|
|
+ | 'up'
|
|
|
|
|
+ | 'down'
|
|
|
|
|
+ | 'left'
|
|
|
|
|
+ | 'right'
|
|
|
|
|
+ | 'up_left'
|
|
|
|
|
+ | 'up_right'
|
|
|
|
|
+ | 'down_left'
|
|
|
|
|
+ | 'down_right'
|
|
|
|
|
+ | 'stop'
|
|
|
|
|
+
|
|
|
// ==================== 核心 API ====================
|
|
// ==================== 核心 API ====================
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 发送 PTZ 控制命令 (统一接口)
|
|
|
|
|
|
|
+ * 发送 PTZ 控制命令 (ONVIF 统一接口)
|
|
|
|
|
+ * 支持: HIKVISION, ANPVIZ
|
|
|
|
|
+ */
|
|
|
|
|
+async function sendPTZCommand(
|
|
|
|
|
+ config: PTZConfig,
|
|
|
|
|
+ command: PTZCommandType,
|
|
|
|
|
+ speed: number = DEFAULT_SPEED
|
|
|
|
|
+): Promise<PTZResult> {
|
|
|
|
|
+ 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,
|
|
|
|
|
+ cameraId: config.cameraId,
|
|
|
|
|
+ vendor: config.vendor || 'HIKVISION',
|
|
|
|
|
+ command,
|
|
|
|
|
+ speed
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ 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 控制命令 (旧接口 - 使用 pan/tilt/zoom)
|
|
|
|
|
+ * 注意: 缩放功能可能仅部分摄像头支持
|
|
|
*/
|
|
*/
|
|
|
async function sendCommand(config: PTZConfig, command: PTZCommand): Promise<PTZResult> {
|
|
async function sendCommand(config: PTZConfig, command: PTZCommand): Promise<PTZResult> {
|
|
|
try {
|
|
try {
|
|
@@ -67,6 +120,8 @@ async function sendCommand(config: PTZConfig, command: PTZCommand): Promise<PTZR
|
|
|
username: config.username,
|
|
username: config.username,
|
|
|
password: config.password,
|
|
password: config.password,
|
|
|
channel: config.channel || 1,
|
|
channel: config.channel || 1,
|
|
|
|
|
+ cameraId: config.cameraId,
|
|
|
|
|
+ vendor: config.vendor || 'HIKVISION',
|
|
|
pan: command.pan,
|
|
pan: command.pan,
|
|
|
tilt: command.tilt,
|
|
tilt: command.tilt,
|
|
|
zoom: command.zoom
|
|
zoom: command.zoom
|
|
@@ -74,7 +129,9 @@ async function sendCommand(config: PTZConfig, command: PTZCommand): Promise<PTZR
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const data = await response.json()
|
|
const data = await response.json()
|
|
|
- return data.code === 200 ? { success: true, data: data.data } : { 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) {
|
|
} catch (error) {
|
|
|
return { success: false, error: String(error) }
|
|
return { success: false, error: String(error) }
|
|
|
}
|
|
}
|
|
@@ -97,7 +154,9 @@ export async function getPTZStatus(config: PTZConfig): Promise<PTZResult & { dat
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const data = await response.json()
|
|
const data = await response.json()
|
|
|
- return data.code === 200 ? { success: true, data: data.data?.raw } : { 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) {
|
|
} catch (error) {
|
|
|
return { success: false, error: String(error) }
|
|
return { success: false, error: String(error) }
|
|
|
}
|
|
}
|
|
@@ -105,25 +164,39 @@ export async function getPTZStatus(config: PTZConfig): Promise<PTZResult & { dat
|
|
|
|
|
|
|
|
// ==================== 方向控制 ====================
|
|
// ==================== 方向控制 ====================
|
|
|
|
|
|
|
|
|
|
+// 方向键映射到命令
|
|
|
|
|
+const DIRECTION_TO_COMMAND: Record<PTZDirectionKey, PTZCommandType> = {
|
|
|
|
|
+ UP: 'up',
|
|
|
|
|
+ DOWN: 'down',
|
|
|
|
|
+ LEFT: 'left',
|
|
|
|
|
+ RIGHT: 'right',
|
|
|
|
|
+ UP_LEFT: 'up_left',
|
|
|
|
|
+ UP_RIGHT: 'up_right',
|
|
|
|
|
+ DOWN_LEFT: 'down_left',
|
|
|
|
|
+ DOWN_RIGHT: 'down_right',
|
|
|
|
|
+ STOP: 'stop'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 开始 PTZ 移动
|
|
* 开始 PTZ 移动
|
|
|
* @param direction 方向: UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT, STOP
|
|
* @param direction 方向: UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT, STOP
|
|
|
* @param speed 速度 1-100,默认 50
|
|
* @param speed 速度 1-100,默认 50
|
|
|
*/
|
|
*/
|
|
|
-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
|
|
|
|
|
- })
|
|
|
|
|
|
|
+export function startPTZ(
|
|
|
|
|
+ config: PTZConfig,
|
|
|
|
|
+ direction: PTZDirectionKey,
|
|
|
|
|
+ speed: number = DEFAULT_SPEED
|
|
|
|
|
+): Promise<PTZResult> {
|
|
|
|
|
+ // 使用新的 command 接口,支持所有厂家
|
|
|
|
|
+ const command = DIRECTION_TO_COMMAND[direction]
|
|
|
|
|
+ return sendPTZCommand(config, command, speed)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 停止 PTZ 移动
|
|
* 停止 PTZ 移动
|
|
|
*/
|
|
*/
|
|
|
export function stopPTZ(config: PTZConfig): Promise<PTZResult> {
|
|
export function stopPTZ(config: PTZConfig): Promise<PTZResult> {
|
|
|
- return startPTZ(config, 'STOP')
|
|
|
|
|
|
|
+ return sendPTZCommand(config, 'stop')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ==================== 缩放控制 ====================
|
|
// ==================== 缩放控制 ====================
|