序言
上篇海康SDK使用以及常见的坑受到了许多网友的喜爱,这也说明了在工控领域内,使用.net开发还是非常便捷省事的。 结合硬件的开发经验来谈语言,也是非常切合实用主义的,这里继续上篇未完成的事情,对海康威视的SDK进行进一步封装,已解决其在x86和x64系统执行时的疑难杂症,并且对海康的SDK进行进一步封装,第一版代码发在github上,供大家测试和使用。
声明下,海康威视没有给赞助费,希望厂家能够看到,给点打赏,哈哈~~~
1. 支持x86和x64的运行时自动加载
做过开发的同学都知道,我们目前的操作系统分为x86和x64两个版本,海康SDK是基于C++编写,在编译时已经定义了系统的操作版本,因此需要我们在开发时根据自己的运行环境载入不同的SDK版本。
这是不是有些复杂,一不小心就掉入了不能加载正确的dll镜像的陷阱中?有没有更方便的办法呢?
当然是有的,基于.net Framework版本的应用可以采用一种巧妙的方式绕开[DllImport] 属性的载入限制, 当然如果是.net core 3以后的版本,还有更精妙的办法来适应linux平台的方式,这里按下不表,暂时聚焦在.net Framework 4运行环境下的载入方式。
大家都知道,DllImportAttribute
是编译时确定路径的,因此无法在运行时进行dll路径的设置,所以我们利用windows系统的SetDllDirectory
函数,来解决加载时自动搜索的路径,这里巧妙的设置加载路径,便绕开了这个限制。
public HkCamera()
{
_sdkInit = CHCNetSDK.NET_DVR_Init();
}
static HkCamera()
{
//设置搜索路径,兼容x86 和 x64
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
path = Path.Combine(path,"Lib", IntPtr.Size == 8 ? "x64" : "x86");
bool ok = SetDllDirectory(path);
if (!ok) throw new System.ComponentModel.Win32Exception();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string path);
在海康原始的SDK文件中,我们需要检查引入类库部分,让它们都采用相对路径,如下:
[DllImportAttribute(@".\HCNetSDK.dll")]
public static extern int NET_DVR_SendWithRecvRemoteConfig(...
这里运行时的环境,简单的使用IntPtr.Size == 8 ? "x64" : "x86"
进行检测,简单使用便捷!
2. 海康设备的在线检测
经过仔细阅读海康的SDK帮助手册,海康在新的SDK中引入了在线检测函数,这里提供新版海康SDK的在线检测方案,以替换上篇文章中阐述的方法。
当然如果是使用老版本的SDK,可以保留之前的方式,效果是一样的。
CHCNetSDK.NET_DVR_RemoteControl(_userId, CHCNetSDK.NET_DVR_CHECK_USER_STATUS, IntPtr.Zero, 0);
使用NET_DVR_CHECK_USER_STATUS
来检测设备是否在线,经测试,断联后检测的时间会稍微长点。
3. 封装登录和登出
海康SDK在使用时,需要对SDK进行初始化,在使用完毕后,需要释放资源,这里可以继承IDisposable
接口,实现释放接口,以便销毁该类时,自动卸载这个非托管资源,避免资源泄漏。
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// TODO: 释放托管状态(托管对象)
}
if (_sdkInit)
{
if (_userId >= 0)
{
Logout();
}
try
{
CHCNetSDK.NET_DVR_Cleanup();
}
catch { }
}
_disposedValue = true;
}
}
登录和登出是使用海康视频,录像机等设备必须做的操作,这里也进行了统一封装,方便上层调用。
public bool Login(CameraLoginInfo cameraLoginInfo)
{
try
{
if (_userId >= 0)
{
Logout();
}
_loginInfo = cameraLoginInfo;
var loginInfo = new CHCNetSDK.NET_DVR_USER_LOGIN_INFO();
loginInfo.sDeviceAddress = cameraLoginInfo.IP;
loginInfo.wPort = cameraLoginInfo.Port;
loginInfo.sUserName = cameraLoginInfo.UserName;
loginInfo.sPassword = cameraLoginInfo.Password;
//是否异步登录:0- 否,1- 是
loginInfo.bUseAsynLogin = false;
var _deviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V40();
_userId = CHCNetSDK.NET_DVR_Login_V40(ref loginInfo, ref _deviceInfo);
_serailNumber = _deviceInfo.struDeviceV30.sSerialNumber;
return _userId >= 0;
}
catch
{
return false;
}
}
public bool IsOnline()
{
try
{
return CHCNetSDK.NET_DVR_RemoteControl(_userId, CHCNetSDK.NET_DVR_CHECK_USER_STATUS, IntPtr.Zero, 0);
}
catch { return false; }
}
public void Logout()
{
try
{
if (_userId >= 0)
{
CHCNetSDK.NET_DVR_Logout(_userId);
_userId = -1;
}
}
catch { }
}
private bool CheckLogin()
{
if (IsOnline()) return true;
return Login(_loginInfo);
}
这里定义了登录的接口类,限定了登录需要的资源,包含IP和端口,用户名和密码,以及默认的视频通道号。
端口默认为8000,因此初始化时可以不用配置。
/// <summary>
/// 海康DVR登录信息
/// </summary>
public class CameraLoginInfo
{
/// <summary>
/// 摄像头的IP地址
/// </summary>
public string IP { get; set; }
/// <summary>
/// 登录端口号
/// </summary>
public ushort Port { get; set; } = 8000;
/// <summary>
/// 登录用户名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 登录密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// 默认通道
/// </summary>
public int ChannelNo { get; set; } = 1;
}
4.云台控制
海康监控相机最常用到的应该是云台控制和抓取图片了,这里也进行了封装,当然这里还缺少对硬盘录像机,红外测温等的封装,后续将一一完善。
云台的控制基本是一样的,都是配对操作,一个开始,一个停止。
/// <summary>
/// 控制云台,设备接收到控制命令后直接返回成功。不关心云台是否进行相应的动作
/// </summary>
/// <param name="cmd"></param>
/// <param name="speed">取值范围[1,7] </param>
/// <returns></returns>
public bool StartPTZControl(PtzCommand cmd, Int32 speed = 4)
{
try
{
if (!CheckLogin()) return false;
var result = CHCNetSDK.NET_DVR_PTZControlWithSpeed_Other(_userId,_loginInfo.ChannelNo, (uint)cmd, 0, (uint)speed);
return result;
}
catch
{
return false;
}
}
/// <summary>
/// 停止云台
/// </summary>
/// <param name="cmd"></param>
/// <returns></returns>
public bool StopPTZControl(PtzCommand cmd)
{
try
{
if (!CheckLogin()) return false;
var result = CHCNetSDK.NET_DVR_PTZControlWithSpeed_Other(_userId, _loginInfo.ChannelNo, (uint)cmd, 1, 4);
return result;
}
catch
{
return false;
}
}
注意,所有操作没有抛出异常,因此外部使用的时候,不需要进行try/catch,如果需要提示错误信息,那么需要调用 GetLastError去获取即可。
这里还是有优化空间的,标准的做法是抛出异常方式,定义一堆异常类,不知道大家能接受那种方式呢?
5.抓取图片
最后一步,对抓取图片进行封装,这里需要你提供合适的文件路径和名称。
/// <summary>
/// 抓图
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public bool CapturePicture(string fileName)
{
if (!CheckLogin()) return false;
try
{
FileInfo fi = new FileInfo(fileName);
if (fi.Exists)
{
File.Delete(fi.FullName);
}
if (!Directory.Exists(fi.DirectoryName))
{
Directory.CreateDirectory(fi.DirectoryName);
}
CHCNetSDK.NET_DVR_JPEGPARA lpJpegPara = new CHCNetSDK.NET_DVR_JPEGPARA
{
wPicQuality = 0,
wPicSize = 0xff
};
return CHCNetSDK.NET_DVR_CaptureJPEGPicture(_userId, _loginInfo.ChannelNo, ref lpJpegPara, fileName);
}
catch
{
return false;
}
}
总结
海康SDK的封装还是比较繁琐的,主要在如下几个方面:
- 兼容x86和x64
- 错误的处理方式包装
- 原始的SDK,需要人性化包装改造
- 需要硬件进行全面测试
- 大量的接口,只能先根据使用场景进行裁剪,后续的改善跟进。
你学废了吗?
👓都看到这了,还在乎点个赞吗?
👓都点赞了,还在乎一个收藏吗?
👓都收藏了,还在乎一个评论吗?