using FATrace.HKNetLib.Common;
using FATrace.HKNetLib.Hardware;
using FATrace.Model;
using System.Reflection;
using System.Runtime.InteropServices;
using static FATrace.HKNetLib.Hardware.CHCNetSDK;
using System.Threading;
using System.Threading.Tasks;
namespace FATrace.HKNetLib.Wrapper
{
public class HkCamera : ICamera
{
private bool _disposedValue;
private bool _sdkInit;
private int _userId;
private string _serailNumber;
private CameraLoginInfo _loginInfo = new CameraLoginInfo();
///
/// 初始化海康sdk
///
///
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);
///
/// 获取最新的错误信息
///
///
public string GetLastError()
{
var error = CHCNetSDK.NET_DVR_GetLastError();
if (error == 0)
{
return string.Empty;
}
if (Enum.TryParse(error.ToString(), out HKErrorCode result))
{
return result.GetDesc();
}
return $"错误码: {error}";
}
public string GetSerialNumber() { return _serailNumber; }
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;
}
}
#region NVR信息
///
/// NVR IP
///
public string NVR_IP { get; set; }
///
/// NVR Port
///
public ushort NVR_Port { get; set; }
///
/// NVR UserName
///
public string NVR_UserName { get; set; }
///
/// NVR Pw
///
public string NVR_Pw { get; set; }
///
/// NVR VideoSavePath 保存的地址信息
///
public string NVRVideoSavePath { get; set; }
#endregion
#region 公共信息
///
/// 事件发布的消息
///
public event EventHandler VideoActionEventHandler;
///
/// 下发视频进度过程的Handler
///
public event EventHandler NVRLoadVideoProcessEventHandler;
///
/// 下发视频完成的Handler
///
public event EventHandler NVRLoadVideoCompleteEventHandler;
///
/// 当前NVR登录信息
///
public CameraLoginInfo NVRLoginInfo { get; set; } = new CameraLoginInfo();
///
/// NVR登录状态
///
public bool NVRLoginState { get; set; }
#endregion
#region NVR开发
///
/// NVRCamera 集合数据
///
public List ListNVRCamera { get; set; } = new List();
///
/// NVR设备信息 DVR_DEVICEINFO_V30
///
public NET_DVR_DEVICEINFO_V30 DVRDeviceInfoV30;
///
/// DVR_IPPARACFG_V40
///
public CHCNetSDK.NET_DVR_IPPARACFG_V40 DVR_IPPARACFG_V40 { get; set; }
///
/// 模拟通道个数
///
private uint dwAChanTotalNum { get; set; } = 0;
///
/// 数字通道个数
///
private uint dwDChanTotalNum { get; set; } = 0;
public NET_DVR_GET_STREAM_UNION DVR_GET_STREAM_UNION { get; set; }
public CHCNetSDK.NET_DVR_IPCHANINFO DVR_IPCHANINFO { get; set; }
///
/// 最新的错误信息
///
public string LastMsgErr { get; set; }
///
/// 登录的用户Id
///
public Int32 m_lUserID { get; set; } = -1;
///
/// 选择的通道号
/// 一般是 一个通道对应一个相机
///
public long iSelIndex = 0;
///
/// 通道集合
///
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 96, ArraySubType = UnmanagedType.U4)]
public int[] iChannelNum = new int[96];
///
/// 下载的Handle
///
public Int32 m_lDownHandle { get; set; } = -1;
///
/// 用户登录
///
///
public bool Sdk_NET_DVR_Login_V30(string DVRIPAddress, int DVRPortNumber, string DVRUserName, string DVRPassword)
{
//登录设备 Login the device
m_lUserID = CHCNetSDK.NET_DVR_Login_V30(DVRIPAddress, DVRPortNumber, DVRUserName, DVRPassword, ref DVRDeviceInfoV30);
if (m_lUserID < 0)
{
LastMsgErr = GetLastError();
//MessageBox.Show(str1);
NVRLoginState = false;
return false;
}
else
{
//登录成功
//MessageBox.Show("Login Success!");
//btnLogin.Text = "Logout";
NVRLoginInfo.IP = DVRIPAddress;
NVRLoginInfo.Port = (ushort)DVRPortNumber;
NVRLoginInfo.Password = DVRPassword;
NVRLoginInfo.UserName = DVRUserName;
NVRLoginState = true;
dwAChanTotalNum = (uint)DVRDeviceInfoV30.byChanNum;
dwDChanTotalNum = (uint)DVRDeviceInfoV30.byIPChanNum + 256 * (uint)DVRDeviceInfoV30.byHighDChanNum;
if (dwDChanTotalNum > 0)
{
InfoIPChannel();
}
else
{
for (int i = 0; i < dwAChanTotalNum; i++)
{
//ListAnalogChannel(i + 1, 1);
iChannelNum[i] = i + (int)DVRDeviceInfoV30.byStartChan;
}
// MessageBox.Show("This device has no IP channel!");
}
}
return true;
}
///
/// 获取通道信息
///
private void InfoIPChannel()
{
uint dwSize = (uint)Marshal.SizeOf(DVR_IPPARACFG_V40);
IntPtr ptrIpParaCfgV40 = Marshal.AllocHGlobal((Int32)dwSize);
Marshal.StructureToPtr(DVR_IPPARACFG_V40, ptrIpParaCfgV40, false);
uint dwReturn = 0;
int iGroupNo = 0; //该Demo仅获取第一组64个通道,如果设备IP通道大于64路,需要按组号0~i多次调用NET_DVR_GET_IPPARACFG_V40获取
if (!CHCNetSDK.NET_DVR_GetDVRConfig(m_lUserID, CHCNetSDK.NET_DVR_GET_IPPARACFG_V40, iGroupNo, ptrIpParaCfgV40, dwSize, ref dwReturn))
{
LastMsgErr = GetLastError();
/*str1 = "NET_DVR_GET_IPPARACFG_V40 failed, error code= " + iLastErr; //*/
//获取IP资源配置信息失败,输出错误号
//MessageBox.Show(str1);
}
else
{
// succ
DVR_IPPARACFG_V40 = (CHCNetSDK.NET_DVR_IPPARACFG_V40)Marshal.PtrToStructure(ptrIpParaCfgV40, typeof(CHCNetSDK.NET_DVR_IPPARACFG_V40));
for (int i = 0; i < dwAChanTotalNum; i++)
{
//ListAnalogChannel(i + 1, DVR_IPPARACFG_V40.byAnalogChanEnable[i]);
iChannelNum[i] = i + (int)DVRDeviceInfoV30.byStartChan;
}
byte byStreamType;
uint iDChanNum = 64;
if (dwDChanTotalNum < 64)
{
iDChanNum = dwDChanTotalNum; //如果设备IP通道小于64路,按实际路数获取
}
for (int i = 0; i < iDChanNum; i++)
{
iChannelNum[i + dwAChanTotalNum] = i + (int)DVR_IPPARACFG_V40.dwStartDChan;
byStreamType = DVR_IPPARACFG_V40.struStreamMode[i].byGetStreamType;
DVR_GET_STREAM_UNION = DVR_IPPARACFG_V40.struStreamMode[i].uGetStream;
switch (byStreamType)
{
//目前NVR仅支持0- 直接从设备取流一种方式
case 0:
dwSize = (uint)Marshal.SizeOf(DVR_GET_STREAM_UNION);
IntPtr ptrChanInfo = Marshal.AllocHGlobal((Int32)dwSize);
Marshal.StructureToPtr(DVR_GET_STREAM_UNION, ptrChanInfo, false);
DVR_IPCHANINFO = (CHCNetSDK.NET_DVR_IPCHANINFO)Marshal.PtrToStructure(ptrChanInfo, typeof(CHCNetSDK.NET_DVR_IPCHANINFO));
ListNVRCamera.Add(new NVRCamera()
{
//ChNo = DVR_IPCHANINFO.byChannel,
ChNo = (i + 1),
IPID = DVR_IPCHANINFO.byIPID,
IsOnline = DVR_IPCHANINFO.byEnable
});
//列出IP通道
//ListIPChannel(i + 1, DVR_IPCHANINFO.byEnable, DVR_IPCHANINFO.byIPID);
Marshal.FreeHGlobal(ptrChanInfo);
break;
default:
break;
}
}
}
Marshal.FreeHGlobal(ptrIpParaCfgV40);
}
///
/// 根据时间获取DVR 视频数据信息
///
public (bool Result, string Msg) Sdk_NET_DVR_GetFileByTime_V40(DateTime startTime, DateTime endTime, string SaveVideFilePath)
{
if (m_lDownHandle >= 0)
{
return (Result: false, Msg: "正在下载,请先停止下载");
}
NET_DVR_PLAYCOND struDownPara = new NET_DVR_PLAYCOND();
struDownPara.dwChannel = (uint)iChannelNum[(int)iSelIndex];
// 设置下载的开始时间
struDownPara.struStartTime.dwYear = startTime.Year;
struDownPara.struStartTime.dwMonth = startTime.Month;
struDownPara.struStartTime.dwDay = startTime.Day;
struDownPara.struStartTime.dwHour = startTime.Hour;
struDownPara.struStartTime.dwMinute = startTime.Minute;
struDownPara.struStartTime.dwSecond = startTime.Second;
// 设置下载的结束时间
struDownPara.struStopTime.dwYear = endTime.Year;
struDownPara.struStopTime.dwMonth = endTime.Month;
struDownPara.struStopTime.dwDay = endTime.Day;
struDownPara.struStopTime.dwHour = endTime.Hour;
struDownPara.struStopTime.dwMinute = endTime.Minute;
struDownPara.struStopTime.dwSecond = endTime.Second;
// 按时间下载
m_lDownHandle = CHCNetSDK.NET_DVR_GetFileByTime_V40(m_lUserID, SaveVideFilePath, ref struDownPara);
if (m_lDownHandle < 0)
{
LastMsgErr = GetLastError();
return (Result: false, Msg: $"[NET_DVR_GetFileByTime_V40] 执行下载失败:{LastMsgErr}");
}
uint iOutValue = 0;
// 该接口指定了当前要下载的录像文件,调用成功后,还需要调用 NET_DVR_PlayBackControl_V40 接口的开始播放控制命令(NET_DVR_PLAYSTART)才能实现下载。
if (!CHCNetSDK.NET_DVR_PlayBackControl_V40(m_lDownHandle, CHCNetSDK.NET_DVR_PLAYSTART, IntPtr.Zero, 0, IntPtr.Zero, ref iOutValue))
{
LastMsgErr = GetLastError();
try { CHCNetSDK.NET_DVR_StopGetFile(m_lDownHandle); } catch { }
m_lDownHandle = -1;
return (Result: false, Msg: $"[NET_DVR_PlayBackControl_V40] 执行下载控制失败:{LastMsgErr}");
}
StartDownloadProgressMonitor();
return (Result: true, Msg: "下载OK!");
}
#endregion
#region 跟踪下载
private CancellationTokenSource _downloadCts;
private const int _downloadPollIntervalMs = 500;
///
/// 启动下载进度轮询,定期发布 NVRLoadVideoProcessEventHandler 事件
///
public void StartDownloadProgressMonitor()
{
// 先确保没有旧的轮询任务
StopDownloadProgressMonitor();
_downloadCts = new CancellationTokenSource();
var token = _downloadCts.Token;
Task.Run(async () =>
{
try
{
while (!token.IsCancellationRequested && m_lDownHandle >= 0)
{
int progress = -1;
try
{
progress = CHCNetSDK.NET_DVR_GetDownloadPos(m_lDownHandle);
}
catch
{
progress = -1;
}
if (progress >= 0)
{
short percent = (short)Math.Min(100, progress);
try
{
NVRLoadVideoProcessEventHandler?.Invoke(this, percent);
}
catch
{
// 事件订阅者异常不应中断轮询
}
if (progress >= 100)
{
// 完成:停止下载,退出轮询
Sdk_NET_DVR_StopGetFile();
NVRLoadVideoCompleteEventHandler?.Invoke(this, "下载完成");
break;
}
}
else
{
// 发生错误,尝试停止下载并退出
Sdk_NET_DVR_StopGetFile();
break;
}
try { await Task.Delay(_downloadPollIntervalMs, token); } catch { /* ignore */ }
}
}
catch
{
// 轮询线程异常吞掉,避免影响上层逻辑
}
}, token);
}
///
/// 停止下载进度轮询
///
private void StopDownloadProgressMonitor()
{
try
{
if (_downloadCts != null)
{
if (!_downloadCts.IsCancellationRequested)
{
_downloadCts.Cancel();
}
_downloadCts.Dispose();
_downloadCts = null;
}
}
catch
{
// 忽略取消中的异常
}
}
///
/// 停止当前下载并清理资源
///
///
public (bool Result, string Msg) Sdk_NET_DVR_StopGetFile()
{
try
{
// 停止轮询
StopDownloadProgressMonitor();
if (m_lDownHandle >= 0)
{
bool stopOk = CHCNetSDK.NET_DVR_StopGetFile(m_lDownHandle);
m_lDownHandle = -1;
if (!stopOk)
{
LastMsgErr = GetLastError();
return (false, $"[NET_DVR_StopGetFile] 停止下载失败:{LastMsgErr}");
}
}
return (true, "停止下载成功");
}
catch (Exception)
{
return (false, "停止下载异常");
}
}
#endregion
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);
}
///
/// 控制云台,设备接收到控制命令后直接返回成功。不关心云台是否进行相应的动作
///
///
/// 取值范围[1,7]
///
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;
}
}
///
/// 停止云台
///
///
///
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;
}
}
///
/// 抓图
///
///
///
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;
}
}
public bool SetFocusMode(FocusModeType focusModeType)
{
bool res = false;
var destMode = focusModeType;
CHCNetSDK.NET_DVR_FOCUSMODE_CFG focusmode_cfg = new CHCNetSDK.NET_DVR_FOCUSMODE_CFG();
focusmode_cfg.byRes = new byte[48];
focusmode_cfg.byRes1 = new byte[2];
int nSize = Marshal.SizeOf(focusmode_cfg);
focusmode_cfg.dwSize = (uint)nSize;
IntPtr ptrDeviceCfg = Marshal.AllocHGlobal(nSize);
try
{
// 获取当前模式
Marshal.StructureToPtr(focusmode_cfg, ptrDeviceCfg, false);
uint rSize = (uint)nSize;
if (!CHCNetSDK.NET_DVR_GetDVRConfig(_userId, CHCNetSDK.NET_DVR_GET_FOCUSMODECFG, 1, ptrDeviceCfg, (uint)nSize, ref rSize))
{
var errorStr = CHCNetSDK.NET_DVR_GetLastError();
return res;
}
// 如果当前模式与请求模式相同,则直接返回true
CHCNetSDK.NET_DVR_FOCUSMODE_CFG getObj = (CHCNetSDK.NET_DVR_FOCUSMODE_CFG)Marshal.PtrToStructure(ptrDeviceCfg, typeof(CHCNetSDK.NET_DVR_FOCUSMODE_CFG));
var curMode = (FocusModeType)getObj.byFocusMode;
if (curMode == destMode)
{
res = true;
return res;
}
// 否则调用设置方法
getObj.fOpticalZoomLevel = 0;
getObj.byOpticalZoom = 32;
getObj.byFocusMode = (byte)destMode;
IntPtr ptrDeviceCfg1 = Marshal.AllocHGlobal((int)getObj.dwSize);
Marshal.StructureToPtr(getObj, ptrDeviceCfg1, false);
res = CHCNetSDK.NET_DVR_SetDVRConfig(_userId, CHCNetSDK.NET_DVR_SET_FOCUSMODECFG, 1, ptrDeviceCfg1, focusmode_cfg.dwSize);
if (!res)
{
var errorStr = CHCNetSDK.NET_DVR_GetLastError();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
Marshal.FreeHGlobal(ptrDeviceCfg);
}
return res;
}
///
/// 获取对焦距模式
///
///
public FocusModeType GetFocusMode()
{
FocusModeType retValue = FocusModeType.None;
CHCNetSDK.NET_DVR_FOCUSMODE_CFG focusmode_cfg = new CHCNetSDK.NET_DVR_FOCUSMODE_CFG
{
byRes = new byte[48],
byRes1 = new byte[2]
};
int nSize = Marshal.SizeOf(focusmode_cfg);
focusmode_cfg.dwSize = (uint)nSize;
IntPtr ptrDeviceCfg = Marshal.AllocHGlobal(nSize);
try
{
Marshal.StructureToPtr(focusmode_cfg, ptrDeviceCfg, false);
uint rSize = (uint)nSize;
if (CHCNetSDK.NET_DVR_GetDVRConfig(this._userId, CHCNetSDK.NET_DVR_GET_FOCUSMODECFG, 1, ptrDeviceCfg, (uint)nSize, ref rSize))
{
CHCNetSDK.NET_DVR_FOCUSMODE_CFG getObj = (CHCNetSDK.NET_DVR_FOCUSMODE_CFG)Marshal.PtrToStructure(ptrDeviceCfg, typeof(CHCNetSDK.NET_DVR_FOCUSMODE_CFG));
retValue = (FocusModeType)getObj.byFocusMode;
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
Marshal.FreeHGlobal(ptrDeviceCfg);
}
return retValue;
}
public bool Shutdown()
{
try
{
if (!CheckLogin()) return false;
return CHCNetSDK.NET_DVR_ShutDownDVR(_userId);
}
catch
{
return false;
}
}
public bool Reboot()
{
try
{
if (!CheckLogin()) return false;
return CHCNetSDK.NET_DVR_RebootDVR(_userId);
}
catch
{
return false;
}
}
#region 释放接口
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 void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
}