海康摄像头增加了云台控制等
This commit is contained in:
19
SHH.CameraSdk/Drivers/HikVision/Features/FeaturesEnums.cs
Normal file
19
SHH.CameraSdk/Drivers/HikVision/Features/FeaturesEnums.cs
Normal 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, // 雨刷
|
||||
}
|
||||
71
SHH.CameraSdk/Drivers/HikVision/Features/HikPtzProvider.cs
Normal file
71
SHH.CameraSdk/Drivers/HikVision/Features/HikPtzProvider.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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)会检测到断线,
|
||||
// 并自动开始尝试重连,直到设备重启完成上线。
|
||||
// 所以这里我们不需要手动断开连接,交给底层自愈机制即可。
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -252,6 +252,9 @@ public static partial class HikNativeMethods
|
||||
/// <summary> 命令常量:获取时间配置 </summary>
|
||||
public const uint NET_DVR_GET_TIMECFG = 118;
|
||||
|
||||
/// <summary> 命令常量:设置时间 </summary>
|
||||
public const int NET_DVR_SET_TIMECFG = 119;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- PTZ 控制相关 (PTZ Control) ---
|
||||
@@ -492,4 +495,16 @@ public static partial class HikNativeMethods
|
||||
ref uint lpBytesReturned);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
[DllImport(DllName)]
|
||||
public static extern bool NET_DVR_SetDVRConfig(int lUserID, uint dwCommand, int lChannel, System.IntPtr lpInBuffer, uint dwInBufferSize);
|
||||
|
||||
/// <summary>
|
||||
/// 设备重启
|
||||
/// </summary>
|
||||
/// <param name="lUserID"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport(DllName)]
|
||||
public static extern bool NET_DVR_RebootDVR(int lUserID);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using OpenCvSharp;
|
||||
using SHH.CameraSdk.HikFeatures;
|
||||
using System;
|
||||
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
@@ -9,7 +11,8 @@ namespace SHH.CameraSdk;
|
||||
/// 2. [Feat A] 热更新支持:实现 OnApplyOptions,支持码流/句柄不亦断线热切换。
|
||||
/// 3. [Feat B] 审计集成:全面接入 AddAuditLog,对接 Web 运维仪表盘。
|
||||
/// </summary>
|
||||
public class HikVideoSource : BaseVideoSource
|
||||
public class HikVideoSource : BaseVideoSource,
|
||||
IHikContext, ITimeSyncFeature, IRebootFeature, IPtzFeature
|
||||
{
|
||||
#region --- 静态资源 (Global Resources) ---
|
||||
|
||||
@@ -22,6 +25,33 @@ public class HikVideoSource : BaseVideoSource
|
||||
|
||||
#endregion
|
||||
|
||||
// 声明组件
|
||||
private readonly HikTimeSyncProvider _timeProvider;
|
||||
private readonly HikRebootProvider _rebootProvider;
|
||||
private readonly HikPtzProvider _ptzProvider;
|
||||
|
||||
// ==========================================
|
||||
// 实现 IHikContext (核心数据暴露)
|
||||
// ==========================================
|
||||
public int GetUserId() => _userId; // 暴露父类或私有的 _userId
|
||||
public string GetDeviceIp() => Config.IpAddress;
|
||||
|
||||
// ==========================================
|
||||
// 实现 ITimeSyncFeature (路由转发)
|
||||
// ==========================================
|
||||
// 核心逻辑:全部委托给 _timeProvider 处理,自己不写一行逻辑
|
||||
public Task<DateTime> GetTimeAsync() => _timeProvider.GetTimeAsync();
|
||||
|
||||
public Task SetTimeAsync(DateTime time) => _timeProvider.SetTimeAsync(time);
|
||||
|
||||
public Task RebootAsync() => _rebootProvider.RebootAsync();
|
||||
|
||||
public Task PtzControlAsync(PtzAction action, bool stop, int speed = 4)
|
||||
=> _ptzProvider.PtzControlAsync(action, stop, speed);
|
||||
|
||||
public Task PtzStepAsync(PtzAction action, int durationMs, int speed = 4)
|
||||
=> _ptzProvider.PtzStepAsync(action, durationMs, speed);
|
||||
|
||||
#region --- 实例成员 (Instance Members) ---
|
||||
|
||||
private int _userId = -1; // SDK 登录句柄
|
||||
@@ -53,7 +83,10 @@ public class HikVideoSource : BaseVideoSource
|
||||
|
||||
public HikVideoSource(VideoSourceConfig config) : base(config)
|
||||
{
|
||||
// 构造函数保持简洁
|
||||
// 初始化组件,将 "this" 作为上下文传进去
|
||||
_timeProvider = new HikTimeSyncProvider(this);
|
||||
_rebootProvider = new HikRebootProvider(this);
|
||||
_ptzProvider = new HikPtzProvider(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user