Files
FATrace/FATrace.HKNetLib/Wrapper/HkCamera.cs
2025-12-03 15:58:17 +08:00

785 lines
26 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using FATrace.HKNetLib.Common;
using FATrace.HKNetLib.Hardware;
using FATrace.Model;
using System.Reflection;
using System.Runtime.InteropServices;
using System.IO;
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();
/// <summary>
/// 初始化海康sdk
/// </summary>
/// <exception cref="BadImageFormatException"></exception>
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);
/// <summary>
/// 获取最新的错误信息
/// </summary>
/// <returns></returns>
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信息
/// <summary>
/// NVR IP
/// </summary>
public string NVR_IP { get; set; }
/// <summary>
/// NVR Port
/// </summary>
public ushort NVR_Port { get; set; }
/// <summary>
/// NVR UserName
/// </summary>
public string NVR_UserName { get; set; }
/// <summary>
/// NVR Pw
/// </summary>
public string NVR_Pw { get; set; }
/// <summary>
/// NVR VideoSavePath 保存的地址信息
/// </summary>
public string NVRVideoSavePath { get; set; }
#endregion
#region
/// <summary>
/// 事件发布的消息
/// </summary>
public event EventHandler<VideoAction> VideoActionEventHandler;
/// <summary>
/// 下发视频进度过程的Handler
/// </summary>
public event EventHandler<short> NVRLoadVideoProcessEventHandler;
/// <summary>
/// 下发视频完成的Handler
/// </summary>
public event EventHandler<string> NVRLoadVideoCompleteEventHandler;
/// <summary>
/// 当前NVR登录信息
/// </summary>
public CameraLoginInfo NVRLoginInfo { get; set; } = new CameraLoginInfo();
/// <summary>
/// NVR登录状态
/// </summary>
public bool NVRLoginState { get; set; }
#endregion
#region NVR开发
/// <summary>
/// NVRCamera 集合数据
/// </summary>
public List<NVRCamera> ListNVRCamera { get; set; } = new List<NVRCamera>();
/// <summary>
/// NVR设备信息 DVR_DEVICEINFO_V30
/// </summary>
public NET_DVR_DEVICEINFO_V30 DVRDeviceInfoV30;
/// <summary>
/// DVR_IPPARACFG_V40
/// </summary>
public CHCNetSDK.NET_DVR_IPPARACFG_V40 DVR_IPPARACFG_V40 { get; set; }
/// <summary>
/// 模拟通道个数
/// </summary>
private uint dwAChanTotalNum { get; set; } = 0;
/// <summary>
/// 数字通道个数
/// </summary>
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; }
/// <summary>
/// 最新的错误信息
/// </summary>
public string LastMsgErr { get; set; }
/// <summary>
/// 登录的用户Id
/// </summary>
public Int32 m_lUserID { get; set; } = -1;
/// <summary>
/// 选择的通道号
/// 一般是 一个通道对应一个相机
/// </summary>
public long iSelIndex = 0;
/// <summary>
/// 通道集合
/// </summary>
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 96, ArraySubType = UnmanagedType.U4)]
public int[] iChannelNum = new int[96];
/// <summary>
/// 下载的Handle
/// </summary>
public Int32 m_lDownHandle { get; set; } = -1;
/// <summary>
/// 用户登录
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 获取通道信息
/// </summary>
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);
}
/// <summary>
/// 根据时间获取DVR 视频数据信息
/// </summary>
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;
/// <summary>
/// 启动下载进度轮询,定期发布 NVRLoadVideoProcessEventHandler 事件
/// </summary>
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();
try { NVRLoadVideoCompleteEventHandler?.Invoke(this, "下载失败"); } catch { }
break;
}
try { await Task.Delay(_downloadPollIntervalMs, token); } catch { /* ignore */ }
}
}
catch (Exception ex)
{
// 轮询线程异常吞掉,避免影响上层逻辑
try { NVRLoadVideoCompleteEventHandler?.Invoke(this, $"下载失败:{ex.Message}"); } catch { }
}
}, token);
}
/// <summary>
/// 停止下载进度轮询
/// </summary>
private void StopDownloadProgressMonitor()
{
try
{
if (_downloadCts != null)
{
if (!_downloadCts.IsCancellationRequested)
{
_downloadCts.Cancel();
}
_downloadCts.Dispose();
_downloadCts = null;
}
}
catch
{
// 忽略取消中的异常
}
}
/// <summary>
/// 停止当前下载并清理资源
/// </summary>
/// <returns></returns>
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);
}
/// <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;
}
}
/// <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;
}
}
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;
}
/// <summary>
/// 获取对焦距模式
/// </summary>
/// <returns></returns>
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
}
}