Files
FATrace/FATrace.HKNetLib/Wrapper/HkCamera.cs
2025-09-11 20:29:17 +08:00

578 lines
20 KiB
C#
Raw 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static FATrace.HKNetLib.Hardware.CHCNetSDK;
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设备信息 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; }
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; }
/// <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);
return false;
}
else
{
//登录成功
//MessageBox.Show("Login Success!");
//btnLogin.Text = "Logout";
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));
//列出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)
{
//MessageBox.Show("Downloading, please stop firstly!");//正在下载,请先停止下载
return (Result: false, Msg: "正在下载,请先停止下载");
}
NET_DVR_PLAYCOND struDownPara = new NET_DVR_PLAYCOND();
struDownPara.dwChannel = (uint)iChannelNum[(int)iSelIndex]; //通道号 Channel number
//设置下载的开始时间 Set the starting time
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;
//设置下载的结束时间 Set the stopping time
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;
//string sVideoFileName; //录像文件保存路径和文件名 the path and file name to save
//sVideoFileName = "D:\\Downtest_Channel" + struDownPara.dwChannel + ".mp4";
//按时间下载 Download by time
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才能实现下载。
//V5.0.3.2或以后版本通过该接口保存录像保存的录像文件数据超过文件最大限制字节数默认为1024MBSDK会自动切片即新建文件进行保存文件名命名规则为“在接口传入的文件名基础上增加数字标识(例如:*_1.mp4、*_2.mp4)”。
//可以调用NET_DVR_GetSDKLocalCfg、NET_DVR_SetSDKLocalCfg(配置类型NET_DVR_LOCAL_CFG_TYPE_GENERAL)获取和设置切片模式和文件最大限制字节数。
if (!CHCNetSDK.NET_DVR_PlayBackControl_V40(m_lDownHandle, CHCNetSDK.NET_DVR_PLAYSTART, IntPtr.Zero, 0, IntPtr.Zero, ref iOutValue))
{
LastMsgErr = GetLastError();
return (Result: false, Msg: $"[NET_DVR_PlayBackControl_V40] 执行下载控制失败:{LastMsgErr}");
}
return (Result: true, Msg: "下载成功!");
}
/// <summary>
/// 获取下载的进度
/// </summary>
/// <param name="downHandle"></param>
/// <returns></returns>
public int Sdk_NET_DVR_GetDownloadPos(Int32 downHandle)
{
return CHCNetSDK.NET_DVR_GetDownloadPos(downHandle);
}
#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
}
}