2026-03-02 13:57:10 +08:00
|
|
|
|
using Serilog;
|
|
|
|
|
|
|
|
|
|
|
|
namespace SHH.CameraSdk.HikFeatures;
|
2025-12-28 13:14:40 +08:00
|
|
|
|
|
|
|
|
|
|
public class HikPtzProvider : IPtzFeature
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly IHikContext _context;
|
|
|
|
|
|
|
|
|
|
|
|
public HikPtzProvider(IHikContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
_context = context;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 13:57:10 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 判断该动作是否支持速度参数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static bool IsSpeedSupported(PtzAction action)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Optimized: 方向和缩放支持速度,其余(雨刷/聚焦/光圈)为开关量或标准调节
|
|
|
|
|
|
return action <= PtzAction.ZoomOut;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-28 13:14:40 +08:00
|
|
|
|
public async Task PtzControlAsync(PtzAction action, bool stop, int speed)
|
|
|
|
|
|
{
|
|
|
|
|
|
int userId = _context.GetUserId();
|
|
|
|
|
|
if (userId < 0) throw new InvalidOperationException("设备离线");
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 映射指令
|
|
|
|
|
|
uint hikCommand = action switch
|
|
|
|
|
|
{
|
|
|
|
|
|
PtzAction.Left => HikNativeMethods.PAN_LEFT,
|
2026-03-02 13:57:10 +08:00
|
|
|
|
PtzAction.Up => HikNativeMethods.TILT_UP,
|
2025-12-28 13:14:40 +08:00
|
|
|
|
PtzAction.Right => HikNativeMethods.PAN_RIGHT,
|
2026-03-02 13:57:10 +08:00
|
|
|
|
PtzAction.Down => HikNativeMethods.TILT_DOWN,
|
|
|
|
|
|
|
|
|
|
|
|
PtzAction.LeftUp => HikNativeMethods.UP_LEFT, // 海康 SDK: 左上
|
|
|
|
|
|
PtzAction.LeftDown => HikNativeMethods.DOWN_LEFT, // 海康 SDK: 左下
|
|
|
|
|
|
PtzAction.RightUp => HikNativeMethods.UP_RIGHT, // 海康 SDK: 右上
|
|
|
|
|
|
PtzAction.RightDown => HikNativeMethods.DOWN_RIGHT, // 海康 SDK: 右下
|
|
|
|
|
|
|
|
|
|
|
|
PtzAction.Auto => HikNativeMethods.PAN_AUTO,
|
|
|
|
|
|
|
2025-12-28 13:14:40 +08:00
|
|
|
|
PtzAction.ZoomIn => HikNativeMethods.ZOOM_IN,
|
|
|
|
|
|
PtzAction.ZoomOut => HikNativeMethods.ZOOM_OUT,
|
|
|
|
|
|
PtzAction.FocusNear => HikNativeMethods.FOCUS_NEAR,
|
|
|
|
|
|
PtzAction.FocusFar => HikNativeMethods.FOCUS_FAR,
|
|
|
|
|
|
PtzAction.IrisOpen => HikNativeMethods.IRIS_OPEN,
|
|
|
|
|
|
PtzAction.IrisClose => HikNativeMethods.IRIS_CLOSE,
|
|
|
|
|
|
PtzAction.Wiper => HikNativeMethods.WIPER_PWRON,
|
|
|
|
|
|
_ => 0
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (hikCommand == 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 转换停止标志 (海康: 0=开始, 1=停止)
|
|
|
|
|
|
uint dwStop = stop ? 1u : 0u;
|
|
|
|
|
|
|
2026-03-02 13:57:10 +08:00
|
|
|
|
// 3. 调用 SDK
|
2025-12-28 13:14:40 +08:00
|
|
|
|
await Task.Run(() =>
|
|
|
|
|
|
{
|
2026-03-02 13:57:10 +08:00
|
|
|
|
bool result;
|
|
|
|
|
|
// Optimized: 接口分流逻辑,确保不同类型的动作调用正确的 SDK 接口
|
|
|
|
|
|
if (IsSpeedSupported(action))
|
|
|
|
|
|
{
|
|
|
|
|
|
uint dwSpeed = (uint)Math.Clamp(speed, 1, 7);
|
|
|
|
|
|
result = HikNativeMethods.NET_DVR_PTZControlWithSpeed_Other(userId, 1, hikCommand, dwStop, dwSpeed);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 对于雨刷、聚焦、光圈,使用不带速度的接口
|
|
|
|
|
|
result = HikNativeMethods.NET_DVR_PTZControl_Other(userId, 1, hikCommand, dwStop);
|
|
|
|
|
|
}
|
2025-12-28 13:14:40 +08:00
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
|
{
|
2026-03-02 13:57:10 +08:00
|
|
|
|
// Modified: 统一记录到 D:\Logs
|
|
|
|
|
|
uint errorCode = HikNativeMethods.NET_DVR_GetLastError();
|
|
|
|
|
|
Log.Warning("Ayay.AiVideo: PTZ {Action} failed. Stop: {Stop}, ErrorCode: {ErrorCode}",
|
|
|
|
|
|
action, stop, errorCode);
|
2025-12-28 13:14:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task PtzStepAsync(PtzAction action, int durationMs, int speed)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. 开始转动 (调用已有的逻辑,stop=false)
|
|
|
|
|
|
await PtzControlAsync(action, false, speed);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 等待指定时间 (非阻塞等待)
|
|
|
|
|
|
// 注意:这里使用 Task.Delay,精度对云台控制来说足够了
|
|
|
|
|
|
if (durationMs > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(durationMs);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 停止转动 (调用已有的逻辑,stop=true)
|
|
|
|
|
|
await PtzControlAsync(action, true, speed);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|