海康摄像头增加了云台控制等

This commit is contained in:
2025-12-28 13:14:40 +08:00
parent 2ee25a4f7c
commit 231247c80f
12 changed files with 774 additions and 107 deletions

View File

@@ -0,0 +1,19 @@
namespace SHH.CameraSdk.HikFeatures;
/// <summary>
/// 云台动作枚举
/// </summary>
public enum PtzAction
{
Up,
Down,
Left,
Right,
ZoomIn, // 放大
ZoomOut, // 缩小
FocusNear, // 聚焦近
FocusFar, // 聚焦远
IrisOpen, // 光圈大
IrisClose, // 光圈小
Wiper, // 雨刷
}

View File

@@ -0,0 +1,71 @@
namespace SHH.CameraSdk.HikFeatures;
public class HikPtzProvider : IPtzFeature
{
private readonly IHikContext _context;
public HikPtzProvider(IHikContext context)
{
_context = context;
}
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.Up => HikNativeMethods.TILT_UP,
PtzAction.Down => HikNativeMethods.TILT_DOWN,
PtzAction.Left => HikNativeMethods.PAN_LEFT,
PtzAction.Right => HikNativeMethods.PAN_RIGHT,
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;
// 3. 限制速度范围 (1-7)
uint dwSpeed = (uint)Math.Clamp(speed, 1, 7);
// 4. 调用 SDK
await Task.Run(() =>
{
// Channel 默认为 1
bool result = HikNativeMethods.NET_DVR_PTZControlWithSpeed_Other(userId, 1, hikCommand, dwStop, dwSpeed);
if (!result)
{
// 这里通常不抛异常,因为云台繁忙或到头是常态,记录日志即可
// Console.WriteLine($"PTZ Error: {HikNativeMethods.NET_DVR_GetLastError()}");
}
});
}
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);
}
}

View File

@@ -0,0 +1,36 @@
namespace SHH.CameraSdk.HikFeatures;
public class HikRebootProvider : IRebootFeature
{
private readonly IHikContext _context;
public HikRebootProvider(IHikContext context)
{
_context = context;
}
public async Task RebootAsync()
{
// 1. 检查登录状态
int userId = _context.GetUserId();
if (userId < 0) throw new InvalidOperationException("设备未登录或离线,无法发送重启指令");
// 2. 执行 SDK 调用
await Task.Run(() =>
{
bool result = HikNativeMethods.NET_DVR_RebootDVR(userId);
if (!result)
{
uint err = HikNativeMethods.NET_DVR_GetLastError();
throw new Exception($"重启指令发送失败,错误码: {err}");
}
});
// 3. 注意:
// 重启指令发送成功后,设备会断开网络。
// 宿主类(HikVideoSource)的保活机制(KeepAlive)会检测到断线,
// 并自动开始尝试重连,直到设备重启完成上线。
// 所以这里我们不需要手动断开连接,交给底层自愈机制即可。
}
}

View File

@@ -0,0 +1,98 @@
namespace SHH.CameraSdk.HikFeatures;
/// <summary>
/// 海康时间同步组件
/// </summary>
public class HikTimeSyncProvider : ITimeSyncFeature
{
private readonly IHikContext _context;
public HikTimeSyncProvider(IHikContext context)
{
_context = context;
}
public async Task<DateTime> GetTimeAsync()
{
// 1. 获取句柄
int userId = _context.GetUserId();
if (userId < 0) throw new InvalidOperationException("设备未登录");
return await Task.Run(() =>
{
uint returned = 0;
uint size = (uint)Marshal.SizeOf(typeof(HikNativeMethods.NET_DVR_TIME));
IntPtr ptr = Marshal.AllocHGlobal((int)size);
try
{
// 调用 SDK: NET_DVR_GET_TIME (命令号 118)
bool result = HikNativeMethods.NET_DVR_GetDVRConfig(
userId,
HikNativeMethods.NET_DVR_GET_TIMECFG,
0,
ptr,
size,
ref returned);
if (!result)
{
var err = HikNativeMethods.NET_DVR_GetLastError();
throw new Exception($"获取时间失败, 错误码: {err}");
}
var t = Marshal.PtrToStructure<HikNativeMethods.NET_DVR_TIME>(ptr);
return new DateTime((int)t.dwYear, (int)t.dwMonth, (int)t.dwDay, (int)t.dwHour, (int)t.dwMinute, (int)t.dwSecond);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
});
}
public async Task SetTimeAsync(DateTime time)
{
int userId = _context.GetUserId();
if (userId < 0) throw new InvalidOperationException("设备未登录");
await Task.Run(() =>
{
var t = new HikNativeMethods.NET_DVR_TIME
{
dwYear = (uint)time.Year,
dwMonth = (uint)time.Month,
dwDay = (uint)time.Day,
dwHour = (uint)time.Hour,
dwMinute = (uint)time.Minute,
dwSecond = (uint)time.Second
};
uint size = (uint)Marshal.SizeOf(t);
IntPtr ptr = Marshal.AllocHGlobal((int)size);
try
{
Marshal.StructureToPtr(t, ptr, false);
// 调用 SDK: NET_DVR_SET_TIME (命令号 119)
bool result = HikNativeMethods.NET_DVR_SetDVRConfig(
userId,
HikNativeMethods.NET_DVR_SET_TIMECFG,
0,
ptr,
size);
if (!result)
{
var err = HikNativeMethods.NET_DVR_GetLastError();
throw new Exception($"设置时间失败, 错误码: {err}");
}
}
finally
{
Marshal.FreeHGlobal(ptr);
}
});
}
}