diff --git a/EC.Util/CameraSDK/Common/CameraException.cs b/EC.Util/CameraSDK/Common/CameraException.cs
new file mode 100644
index 0000000..a63503c
--- /dev/null
+++ b/EC.Util/CameraSDK/Common/CameraException.cs
@@ -0,0 +1,59 @@
+using System.Text;
+
+namespace EC.Util.CameraSDK;
+
+///
+/// 相机异常
+///
+public class CameraException : Exception
+{
+ public CameraException() : base()
+ {
+ }
+
+ public CameraException(string? message) : base(message)
+ {
+ }
+
+ public CameraException(string? message, Exception? innerException) : base(message, innerException)
+ {
+ }
+
+ protected class CameraExceptionObj
+ {
+ public CameraInfo? CameraInfo { get; set; }
+
+ public int Code { get; set; }
+
+ public string? Msg { get; set; }
+
+ public override string? ToString()
+ {
+ StringBuilder builder = new();
+ builder.Append($"Ip:{CameraInfo?.Ip}, Manufactor:{CameraInfo?.Manufactor}, Code:{Code}");
+ if (!string.IsNullOrEmpty(Msg)) builder.Append($", Msg:{Msg}");
+ return builder.ToString();
+ }
+ }
+
+ public static CameraException New(CameraInfo cameraInfo, int code)
+ {
+ CameraExceptionObj obj = new()
+ {
+ CameraInfo = cameraInfo,
+ Code = code
+ };
+ return new CameraException(obj.ToString());
+ }
+
+ public static CameraException New(CameraInfo cameraInfo, int code, string msg)
+ {
+ CameraExceptionObj obj = new()
+ {
+ CameraInfo = cameraInfo,
+ Code = code,
+ Msg = msg
+ };
+ return new CameraException(obj.ToString());
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/Common/CameraFactory.cs b/EC.Util/CameraSDK/Common/CameraFactory.cs
new file mode 100644
index 0000000..e54edcb
--- /dev/null
+++ b/EC.Util/CameraSDK/Common/CameraFactory.cs
@@ -0,0 +1,16 @@
+namespace EC.Util.CameraSDK;
+
+public class CameraFactory
+{
+ public static ICameraSdk BuildCameraSdk(CameraInfo info)
+ {
+ ICameraSdk sdk = (info.Manufactor) switch
+ {
+ CameraManufactor.HiK => new HiKSdk(info),
+ CameraManufactor.DaHua => new DaHuaSdk(info),
+ CameraManufactor.YuShi => new YuShiSdk(info),
+ _ => throw new NotSupportedException(),
+ };
+ return sdk;
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/Common/CameraStruct.cs b/EC.Util/CameraSDK/Common/CameraStruct.cs
new file mode 100644
index 0000000..2105df2
--- /dev/null
+++ b/EC.Util/CameraSDK/Common/CameraStruct.cs
@@ -0,0 +1,142 @@
+namespace EC.Util.CameraSDK;
+
+///
+/// 相机信息
+///
+public class CameraInfo
+{
+ #region Fields
+
+ ///
+ /// id
+ ///
+ public string Id { get; set; } = string.Empty;
+
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 相机厂商
+ ///
+ public CameraManufactor Manufactor { get; set; }
+
+ ///
+ /// ip 地址
+ ///
+ public string Ip { get; set; } = string.Empty;
+
+ ///
+ /// 端口
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// 账号
+ ///
+ public string UserName { get; set; } = string.Empty;
+
+ ///
+ /// 密码
+ ///
+ public string Password { get; set; } = string.Empty;
+
+ #endregion Fields
+
+ public static CameraInfo New(CameraManufactor manufactor, string ip, int port, string username, string password)
+ {
+ CameraInfo info = new() { Manufactor = manufactor, Ip = ip, Port = port, UserName = username, Password = password };
+ if (port <= 0) throw new Exception("Camera manufactor not support.");
+ return info;
+ }
+
+ public static CameraInfo New(CameraManufactor manufactor, string ip, string username, string password)
+ {
+ CameraInfo info = new() { Manufactor = manufactor, Ip = ip, UserName = username, Password = password };
+ int port = manufactor switch
+ {
+ CameraManufactor.HiK => (int)CameraPort.HiK,
+ CameraManufactor.DaHua => (int)CameraPort.DaHua,
+ CameraManufactor.YuShi => (int)CameraPort.YuShi,
+ _ => -1,
+ };
+ if (port <= 0) throw new Exception("Camera manufactor not support.");
+ info.Port = port;
+ return info;
+ }
+
+ public static CameraInfo New(int manufactor, string ip, int port, string username, string password)
+ {
+ CameraManufactor cm = (CameraManufactor)manufactor;
+ CameraInfo info = new() { Manufactor = cm, Ip = ip, Port = port, UserName = username, Password = password };
+ if (port <= 0) throw new Exception("Camera manufactor not support.");
+ return info;
+ }
+
+ public static CameraInfo New(int manufactor, string ip, string username, string password)
+ {
+ CameraManufactor cm = (CameraManufactor)manufactor;
+ CameraInfo info = new() { Manufactor = cm, Ip = ip, UserName = username, Password = password };
+ int port = cm switch
+ {
+ CameraManufactor.HiK => (int)CameraPort.HiK,
+ CameraManufactor.DaHua => (int)CameraPort.DaHua,
+ CameraManufactor.YuShi => (int)CameraPort.YuShi,
+ _ => -1,
+ };
+ if (port <= 0) throw new Exception("Camera manufactor not support.");
+ info.Port = port;
+ return info;
+ }
+}
+
+///
+/// 相机厂商
+///
+public enum CameraManufactor : int
+{
+ HiK = 1,
+ DaHua = 2,
+ YuShi = 3,
+}
+
+///
+/// 相机默认连接端口
+///
+public enum CameraPort : int
+{
+ HiK = 8000,
+ DaHua = 37777,
+ YuShi = 80,
+}
+
+///
+/// Ptz 信息
+///
+public class PtzInfo
+{
+ #region Fields
+
+ public double Pan { get; set; }
+
+ public double Tilt { get; set; }
+
+ public double Zoom { get; set; }
+
+ #endregion Fields
+
+ public PtzInfo(double pan, double tilt, double zoom)
+ {
+ Pan = pan;
+ Tilt = tilt;
+ Zoom = zoom;
+ }
+
+ public static PtzInfo Default
+ {
+ get { return new(0, 0, 0); }
+ }
+
+ public static PtzInfo New(double pan, double tilt, double zoom)
+ {
+ return new PtzInfo(pan, tilt, zoom);
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/Common/ICameraSdk.cs b/EC.Util/CameraSDK/Common/ICameraSdk.cs
new file mode 100644
index 0000000..a294f8d
--- /dev/null
+++ b/EC.Util/CameraSDK/Common/ICameraSdk.cs
@@ -0,0 +1,72 @@
+namespace EC.Util.CameraSDK;
+
+public abstract class ICameraSdk
+{
+ #region Fields
+
+ public CameraInfo CameraInfo { get; }
+
+ #endregion Fields
+
+ public ICameraSdk(CameraInfo cameraInfo)
+ {
+ CameraInfo = cameraInfo;
+ }
+
+ #region Base Method
+
+ ///
+ /// 初始化资源
+ ///
+ ///
+ public abstract bool Init();
+
+ ///
+ /// 释放资源
+ ///
+ ///
+ public abstract bool Destory();
+
+ ///
+ /// 连接是否成功
+ ///
+ ///
+ public abstract bool ConnectSuccess();
+
+ ///
+ /// 构建异常
+ ///
+ internal abstract void BuildException();
+
+ #endregion Base Method
+
+ #region Ptz Method
+
+ ///
+ /// 获取 ptz
+ ///
+ ///
+ public abstract PtzInfo GetPtzInfo();
+
+ ///
+ /// 获取 ptz
+ ///
+ ///
+ public abstract bool TryGetPtzInfo(out PtzInfo ptzInfo);
+
+ public abstract bool PtzMove(int cmd, int stop, int speed);
+
+ public abstract bool PtzPreset(int cmd, int presetId);
+
+ #endregion Ptz Method
+
+ #region Video Method
+
+ public abstract void StartPlay(IntPtr hwnd);
+
+ public abstract void StopPlay();
+
+ public abstract bool IsPlaying();
+
+ #endregion Video Method
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs b/EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs
new file mode 100644
index 0000000..6bedde1
--- /dev/null
+++ b/EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs
@@ -0,0 +1,1527 @@
+//#define Win32
+#define Win64
+
+//#define Linux32
+//#define Linux64
+using System.Runtime.InteropServices;
+
+namespace EC.Util.CameraSDK;
+
+public static class DaHuaOriSdk
+{
+ #region Fields
+
+#if Win32
+ public const string LibSdkPath = @"./libs/dahua/win32/dhnetsdk.dll";
+#elif Win64
+ public const string LibSdkPath = @"./libs/dahua/win64/dhnetsdk.dll";
+#elif Linux32
+ public const string LibSdkPath = @"./libs/dahua/linux32/libdhnetsdk.so";
+#elif Linux64
+ public const string LibSdkPath = @"./libs/dahua/linux64/libdhnetsdk.so";
+#endif
+
+ private const bool Debug = true;
+
+ #endregion Fields
+
+ static DaHuaOriSdk()
+ {
+ GlobalInit();
+ }
+
+ #region Global
+
+ public static bool InitSuccess { get; private set; }
+
+ public static bool GlobalInit()
+ {
+ if (InitSuccess) return true;
+ bool ret = CLIENT_InitEx(null, IntPtr.Zero, IntPtr.Zero);
+ InitSuccess = ret;
+ if (!ret) throw new Exception("DaHuaOriSDK global init failure.");
+ return ret;
+ }
+
+ public static bool GlobalDestory()
+ {
+ if (!InitSuccess) return true;
+ CLIENT_Cleanup();
+ InitSuccess = false;
+ return true;
+ }
+
+ #endregion Global
+
+ #region Sdk Const
+
+ public const int PtzSpeedMin = 1;
+ public const int PtzSpeedMax = 8;
+
+ #endregion Sdk Const
+
+ #region Sdk Enum
+
+ ///
+ /// login device mode enumeration
+ /// 登陆设备方式枚举
+ ///
+ public enum EM_LOGIN_SPAC_CAP_TYPE
+ {
+ ///
+ /// TCP login, default
+ /// TCP登陆, 默认方式
+ ///
+ TCP = 0,
+
+ ///
+ /// No criteria login
+ /// 无条件登陆
+ ///
+ ANY = 1,
+
+ ///
+ /// auto sign up login
+ /// 主动注册的登入
+ ///
+ SERVER_CONN = 2,
+
+ ///
+ /// multicast login, default
+ /// 组播登陆
+ ///
+ MULTICAST = 3,
+
+ ///
+ /// UDP method login
+ /// UDP方式下的登入
+ ///
+ UDP = 4,
+
+ ///
+ /// only main connection login
+ /// 只建主连接下的登入
+ ///
+ MAIN_CONN_ONLY = 6,
+
+ ///
+ /// SSL encryption login
+ /// SSL加密方式登陆
+ ///
+ SSL = 7,
+
+ ///
+ /// login IVS box remote device
+ /// 登录智能盒远程设备
+ ///
+ INTELLIGENT_BOX = 9,
+
+ ///
+ /// login device do not config
+ /// 登录设备后不做取配置操作
+ ///
+ NO_CONFIG = 10,
+
+ ///
+ /// USB key device login
+ /// 用U盾设备的登入
+ ///
+ U_LOGIN = 11,
+
+ ///
+ /// LDAP login
+ /// LDAP方式登录
+ ///
+ LDAP = 12,
+
+ ///
+ /// AD login
+ /// AD(ActiveDirectory)登录方式
+ ///
+ AD = 13,
+
+ ///
+ /// Radius login
+ /// Radius 登录方式
+ ///
+ RADIUS = 14,
+
+ ///
+ /// Socks5 login
+ /// Socks5登陆方式
+ ///
+ SOCKET_5 = 15,
+
+ ///
+ /// cloud login
+ /// 云登陆方式
+ ///
+ CLOUD = 16,
+
+ ///
+ /// dual authentication loin
+ /// 二次鉴权登陆方式
+ ///
+ AUTH_TWICE = 17,
+
+ ///
+ /// TS stream client login
+ /// TS码流客户端登陆方式
+ ///
+ TS = 18,
+
+ ///
+ /// web private login
+ /// 为P2P登陆方式
+ ///
+ P2P = 19,
+
+ ///
+ /// mobile client login
+ /// 手机客户端登陆
+ ///
+ MOBILE = 20,
+
+ ///
+ /// invalid login
+ /// 无效的登陆方式
+ ///
+ INVALID = 21,
+ }
+
+ ///
+ /// device type enumeration
+ /// 设备类型枚举
+ ///
+ public enum EM_NET_DEVICE_TYPE
+ {
+ ///
+ /// Unknow
+ // 未知
+ ///
+ NET_PRODUCT_NONE = 0,
+
+ ///
+ /// Non real-time MACE
+ /// 非实时MACE
+ ///
+ NET_DVR_NONREALTIME_MACE,
+
+ ///
+ /// Non real-time
+ /// 非实时
+ ///
+ NET_DVR_NONREALTIME,
+
+ ///
+ /// Network video server
+ /// 网络视频服务器
+ ///
+ NET_NVS_MPEG1,
+
+ ///
+ /// MPEG1 2-ch DVR
+ /// MPEG1二路录像机
+ ///
+ NET_DVR_MPEG1_2,
+
+ ///
+ /// MPEG1 8-ch DVR
+ /// MPEG1八路录像机
+ ///
+ NET_DVR_MPEG1_8,
+
+ ///
+ /// MPEG4 8-ch DVR
+ /// MPEG4八路录像机
+ ///
+ NET_DVR_MPEG4_8,
+
+ ///
+ /// MPEG4 16-ch DVR
+ /// MPEG4 十六路录像机
+ ///
+ NET_DVR_MPEG4_16,
+
+ ///
+ /// LB series DVR
+ /// LB系列录像机
+ ///
+ NET_DVR_MPEG4_SX2,
+
+ ///
+ /// GB series DVR
+ /// GB系列录像机
+ ///
+ NET_DVR_MEPG4_ST2,
+
+ ///
+ /// HB series DVR
+ /// HB系列录像机
+ ///
+ NET_DVR_MEPG4_SH2,
+
+ ///
+ /// GBE series DVR
+ /// GBE系列录像机
+ ///
+ NET_DVR_MPEG4_GBE,
+
+ ///
+ /// II network video server
+ /// II代网络视频服务器
+ ///
+ NET_DVR_MPEG4_NVSII,
+
+ ///
+ /// New standard configuration protocol
+ /// 新标准配置协议
+ ///
+ NET_DVR_STD_NEW,
+
+ ///
+ /// DDNS server
+ /// DDNS服务器
+ ///
+ NET_DVR_DDNS,
+
+ ///
+ /// ATM series
+ /// ATM机
+ ///
+ NET_DVR_ATM,
+
+ ///
+ /// 2nd non real-time NB series DVR
+ /// 二代非实时NB系列机器
+ ///
+ NET_NB_SERIAL,
+
+ ///
+ /// LN series
+ /// LN系列产品
+ ///
+ NET_LN_SERIAL,
+
+ ///
+ /// BAV series
+ /// BAV系列产品
+ ///
+ NET_BAV_SERIAL,
+
+ ///
+ /// SDIP series
+ /// SDIP系列产品
+ ///
+ NET_SDIP_SERIAL,
+
+ ///
+ /// IPC series
+ /// IPC系列产品
+ ///
+ NET_IPC_SERIAL,
+
+ ///
+ /// NVS B series
+ /// NVS B系列
+ ///
+ NET_NVS_B,
+
+ ///
+ /// NVS H series
+ /// NVS H系列
+ ///
+ NET_NVS_C,
+
+ ///
+ /// NVS S series
+ /// NVS S系列
+ ///
+ NET_NVS_S,
+
+ ///
+ /// NVS E series
+ /// NVS E系列
+ ///
+ NET_NVS_E,
+
+ ///
+ /// Search device type from QueryDevState. it is in string format
+ /// 从QueryDevState中查询设备类型,以字符串格式
+ ///
+ NET_DVR_NEW_PROTOCOL,
+
+ ///
+ /// NVD
+ /// 解码器
+ ///
+ NET_NVD_SERIAL,
+
+ ///
+ /// N5
+ /// N5
+ ///
+ NET_DVR_N5,
+
+ ///
+ /// HDVR
+ /// 混合DVR
+ ///
+ NET_DVR_MIX_DVR,
+
+ ///
+ /// SVR series
+ /// SVR系列
+ ///
+ NET_SVR_SERIAL,
+
+ ///
+ /// SVR-BS
+ /// SVR-BS
+ ///
+ NET_SVR_BS,
+
+ ///
+ /// NVR series
+ /// NVR系列
+ ///
+ NET_NVR_SERIAL,
+
+ ///
+ /// N51
+ /// N51
+ ///
+ NET_DVR_N51,
+
+ ///
+ /// ITSE Intelligent Analysis Box
+ /// ITSE 智能分析盒
+ ///
+ NET_ITSE_SERIAL,
+
+ ///
+ /// Intelligent traffic camera equipment
+ /// 智能交通像机设备
+ ///
+ NET_ITC_SERIAL,
+
+ ///
+ /// radar speedometer HWS
+ /// 雷达测速仪HWS
+ ///
+ NET_HWS_SERIAL,
+
+ ///
+ /// portable video record
+ /// 便携式音视频录像机
+ ///
+ NET_PVR_SERIAL,
+
+ ///
+ /// IVS(intelligent video server series)
+ /// IVS(智能视频服务器系列)
+ ///
+ NET_IVS_SERIAL,
+
+ ///
+ /// universal intelligent detect video server series
+ /// 通用智能视频侦测服务器
+ ///
+ NET_IVS_B,
+
+ ///
+ /// face recognisation server
+ /// 人脸识别服务器
+ ///
+ NET_IVS_F,
+
+ ///
+ /// video quality diagnosis server
+ /// 视频质量诊断服务器
+ ///
+ NET_IVS_V,
+
+ ///
+ /// matrix
+ /// 矩阵
+ ///
+ NET_MATRIX_SERIAL,
+
+ ///
+ /// N52
+ /// N52
+ ///
+ NET_DVR_N52,
+
+ ///
+ /// N56
+ /// N56
+ ///
+ NET_DVR_N56,
+
+ ///
+ /// ESS
+ /// ESS
+ ///
+ NET_ESS_SERIAL,
+
+ ///
+ /// 人数统计服务器
+ ///
+ NET_IVS_PC,
+
+ ///
+ /// pc-nvr
+ /// pc-nvr
+ ///
+ NET_PC_NVR,
+
+ ///
+ /// screen controller
+ /// 大屏控制器
+ ///
+ NET_DSCON,
+
+ ///
+ /// network video storage server
+ /// 网络视频存储服务器
+ ///
+ NET_EVS,
+
+ ///
+ /// an embedded intelligent video analysis system
+ /// 嵌入式智能分析视频系统
+ ///
+ NET_EIVS,
+
+ ///
+ /// DVR-N6
+ /// DVR-N6
+ ///
+ NET_DVR_N6,
+
+ ///
+ /// K-Lite Codec Pack
+ /// 万能解码器
+ ///
+ NET_UDS,
+
+ ///
+ /// Bank alarm host
+ /// 银行报警主机
+ ///
+ NET_AF6016,
+
+ ///
+ /// Video network alarm host
+ /// 视频网络报警主机
+ ///
+ NET_AS5008,
+
+ ///
+ /// Network alarm host
+ /// 网络报警主机
+ ///
+ NET_AH2008,
+
+ ///
+ /// Alarm host series
+ /// 报警主机系列
+ ///
+ NET_A_SERIAL,
+
+ ///
+ /// Access control series of products
+ /// 门禁系列产品
+ ///
+ NET_BSC_SERIAL,
+
+ ///
+ /// NVS series product
+ /// NVS系列产品
+ ///
+ NET_NVS_SERIAL,
+
+ ///
+ /// VTO series product
+ /// VTO系列产品
+ ///
+ NET_VTO_SERIAL,
+
+ ///
+ /// VTNC series product
+ /// VTNC系列产品
+ ///
+ NET_VTNC_SERIAL,
+
+ ///
+ /// TPC series product, it is the thermal device
+ /// TPC系列产品, 即热成像设备
+ ///
+ NET_TPC_SERIAL,
+
+ ///
+ /// ASM series product
+ /// 无线中继设备
+ ///
+ NET_ASM_SERIAL,
+
+ ///
+ /// VTS series product
+ /// 管理机
+ ///
+ NET_VTS_SERIAL,
+
+ ///
+ /// Alarm host-ARC2016C
+ /// 报警主机ARC2016C
+ ///
+ NET_ARC2016C,
+
+ ///
+ /// ASA Attendance machine
+ /// 考勤机
+ ///
+ NET_ASA,
+
+ ///
+ /// Industry terminal walkie-talkie
+ /// 行业对讲终端
+ ///
+ NET_VTT_SERIAL,
+
+ ///
+ /// Alarm column
+ /// 报警柱
+ ///
+ NET_VTA_SERIAL,
+
+ ///
+ /// SIP Server
+ /// SIP服务器
+ ///
+ NET_VTNS_SERIAL,
+
+ ///
+ /// Indoor unit
+ /// 室内机
+ ///
+ NET_VTH_SERIAL,
+ }
+
+ ///
+ /// 查询设备信息参数
+ ///
+ public enum EM_DEVICE_STATE
+ {
+ ///
+ /// Query device online state(return a DWORD value, 1-online, 0-offline)
+ /// 查询设备的在线状态(返回一个DWORD, 1表示在线, 0表示断线)
+ ///
+ ONLINE = 0x0035,
+
+ ///
+ /// Query ptz state(struct DH_PTZ_LOCATION_INFO)
+ /// 查询云台状态信息(对应结构体 DH_PTZ_LOCATION_INFO)
+ ///
+ PTZ_LOCATION = 0x0036,
+ }
+
+ ///
+ /// PTZ control command enumeration
+ /// 云台控制命令
+ ///
+ public enum EM_EXTPTZ_ControlType : int
+ {
+ ///
+ /// Up
+ /// 上
+ ///
+ UP = 0,
+
+ ///
+ /// Down
+ /// 下
+ ///
+ DOWN,
+
+ ///
+ /// Left
+ /// 左
+ ///
+ LEFT,
+
+ ///
+ /// Right
+ /// 右
+ ///
+ RIGHT,
+
+ ///
+ /// +Zoom in
+ /// 变倍+
+ ///
+ ZOOM_ADD,
+
+ ///
+ /// -Zoom out
+ /// 变倍-
+ ///
+ ZOOM_DEC,
+
+ ///
+ /// +Focus
+ /// 调焦+
+ ///
+ FOCUS_ADD,
+
+ ///
+ /// -Focus
+ /// 调焦-
+ ///
+ FOCUS_DEC,
+
+ ///
+ /// + Aperture
+ /// 光圈+
+ ///
+ APERTURE_ADD,
+
+ ///
+ /// -Aperture
+ /// 光圈-
+ ///
+ APERTURE_DEC,
+
+ ///
+ /// Go to preset
+ /// 转至预置点
+ ///
+ POINT_MOVE,
+
+ ///
+ /// Set
+ /// 设置
+ ///
+ POINT_SET_CONTROL,
+
+ ///
+ /// Delete
+ /// 删除
+ ///
+ POINT_DEL_CONTROL,
+
+ ///
+ /// Tour
+ /// 点间巡航
+ ///
+ POINT_LOOP_CONTROL,
+
+ ///
+ /// Light and wiper
+ /// 灯光雨刷
+ ///
+ LAMP_CONTROL,
+
+ ///
+ /// Upper left
+ /// 左上
+ ///
+ LEFTTOP = 0x20,
+
+ ///
+ /// Upper right
+ /// 右上
+ ///
+ RIGHTTOP,
+
+ ///
+ /// Down left
+ /// 左下
+ ///
+ LEFTDOWN,
+
+ ///
+ /// Down right
+ /// 右下
+ ///
+ RIGHTDOWN,
+
+ ///
+ /// Add preset to tour tour preset value
+ /// 加入预置点到巡航 巡航线路 预置点值
+ ///
+ ADDTOLOOP,
+
+ ///
+ /// Delete preset in tour tour preset value
+ /// 删除巡航中预置点 巡航线路 预置点值
+ ///
+ DELFROMLOOP,
+
+ ///
+ /// Delete tour tour
+ /// 清除巡航 巡航线路
+ ///
+ CLOSELOOP,
+
+ ///
+ /// Begin pan rotation
+ /// 开始水平旋转
+ ///
+ STARTPANCRUISE,
+
+ ///
+ /// Stop pan rotation
+ /// 停止水平旋转
+ ///
+ STOPPANCRUISE,
+
+ ///
+ /// Set left limit
+ /// 设置左边界
+ ///
+ SETLEFTBORDER,
+
+ ///
+ /// Set right limit
+ /// 设置右边界
+ ///
+ SETRIGHTBORDER,
+
+ ///
+ /// Begin scanning
+ /// 开始线扫
+ ///
+ STARTLINESCAN,
+
+ ///
+ /// Stop scanning
+ /// 停止线扫
+ ///
+ CLOSELINESCAN,
+
+ ///
+ /// Start mode mode line
+ /// 设置模式开始 模式线路
+ ///
+ SETMODESTART,
+
+ ///
+ /// Stop mode mode line
+ /// 设置模式结束 模式线路
+ ///
+ SETMODESTOP,
+
+ ///
+ /// Enable mode Mode line
+ /// 运行模式 模式线路
+ ///
+ RUNMODE,
+
+ ///
+ /// Disable mode Mode line
+ /// 停止模式 模式线路
+ ///
+ STOPMODE,
+
+ ///
+ /// Delete mode Mode line
+ /// 清除模式 模式线路
+ ///
+ DELETEMODE,
+
+ ///
+ /// Flip
+ /// 翻转命令
+ ///
+ REVERSECOMM,
+
+ ///
+ /// 3D position X address(8192) Y address(8192) zoom(4)
+ /// 快速定位 水平坐标(8192) 垂直坐标(8192) 变倍(4)
+ ///
+ FASTGOTO,
+
+ ///
+ /// auxiliary open Auxiliary point(param4 corresponding struct PTZ_CONTROL_AUXILIARY,param1、param2、param3 is invalid,dwStop set to FALSE)
+ /// 辅助开关开 辅助点(param4对应 PTZ_CONTROL_AUXILIARY,param1、param2、param3无效,dwStop设置为FALSE)
+ ///
+ AUXIOPEN,
+
+ ///
+ /// Auxiliary close Auxiliary point(param4 corresponding struct PTZ_CONTROL_AUXILIARY,param1、param2、param3 is invalid,dwStop set to FALSE)
+ /// 辅助开关关 辅助点(param4对应 PTZ_CONTROL_AUXILIARY,param1、param2、param3无效,dwStop设置为FALSE)
+ ///
+ AUXICLOSE,
+
+ ///
+ /// Open dome menu
+ /// 打开球机菜单
+ ///
+ OPENMENU = 0x36,
+
+ ///
+ /// Close menu
+ /// 关闭菜单
+ ///
+ CLOSEMENU,
+
+ ///
+ /// Confirm menu
+ /// 菜单确定
+ ///
+ MENUOK,
+
+ ///
+ /// Cancel menu
+ /// 菜单取消
+ ///
+ MENUCANCEL,
+
+ ///
+ /// menu up
+ /// 菜单上
+ ///
+ MENUUP,
+
+ ///
+ /// menu down
+ /// 菜单下
+ ///
+ MENUDOWN,
+
+ ///
+ /// menu left
+ /// 菜单左
+ ///
+ MENULEFT,
+
+ ///
+ /// Menu right
+ /// 菜单右
+ ///
+ MENURIGHT,
+
+ ///
+ /// Alarm activate PTZ parm1:Alarm input channel;parm2:Alarm activation type 1-preset 2-scan 3-tour;parm 3:activation value,such as preset value.
+ /// 报警联动云台 parm1:报警输入通道;parm2:报警联动类型1-预置点2-线扫3-巡航;parm3:联动值,如预置点号
+ ///
+ ALARMHANDLE = 0x40,
+
+ ///
+ /// Matrix switch parm1:monitor number(video output number);parm2:video input number;parm3:matrix number
+ /// 矩阵切换 parm1:监视器号(视频输出号);parm2:视频输入号;parm3:矩阵号
+ ///
+ MATRIXSWITCH = 0x41,
+
+ ///
+ /// Light controller
+ /// 灯光控制器
+ ///
+ LIGHTCONTROL,
+
+ ///
+ /// 3D accurately positioning parm1:Pan degree(0~3600); parm2: tilt coordinates(0~900); parm3:zoom(1~128)
+ /// 三维精确定位 parm1:水平角度(0~3600);parm2:垂直坐标(0~900);parm3:变倍(1~128)
+ ///
+ EXACTGOTO,
+
+ ///
+ /// Reset 3D positioning as zero
+ /// 三维定位重设零位
+ ///
+ RESETZERO,
+
+ ///
+ /// Absolute motion control command,param4 corresponding struct NET_PTZ_CONTROL_ABSOLUTELY
+ /// 绝对移动控制命令,param4对应结构 NET_PTZ_CONTROL_ABSOLUTELY
+ ///
+ MOVE_ABSOLUTELY,
+
+ ///
+ /// Continuous motion control command,param4 corresponding struct NET_PTZ_CONTROL_CONTINUOUSLY
+ /// 持续移动控制命令,param4对应结构 NET_PTZ_CONTROL_CONTINUOUSLY
+ ///
+ MOVE_CONTINUOUSLY,
+
+ ///
+ /// PTZ control command, at a certain speed to preset locu,parm4 corresponding struct NET_PTZ_CONTROL_GOTOPRESET
+ /// 云台控制命令,以一定速度转到预置位点,parm4对应结构NET_PTZ_CONTROL_GOTOPRESET
+ ///
+ GOTOPRESET,
+
+ ///
+ /// Set to horizon(param4 corresponding struct NET_PTZ_VIEW_RANGE_INFO)
+ /// 设置可视域(param4对应结构 NET_PTZ_VIEW_RANGE_INFO)
+ ///
+ SET_VIEW_RANGE = 0x49,
+
+ ///
+ /// Absolute focus(param4 corresponding struct NET_PTZ_FOCUS_ABSOLUTELY)
+ /// 绝对聚焦(param4对应结构NET_PTZ_FOCUS_ABSOLUTELY)
+ ///
+ FOCUS_ABSOLUTELY = 0x4A,
+
+ ///
+ /// Level fan sweep(param4 corresponding NET_PTZ_CONTROL_SECTORSCAN,param1,param2,param3 is invalid)
+ /// 水平扇扫(param4对应PTZ_CONTROL_SECTORSCAN,param1、param2、param3无效)
+ ///
+ HORSECTORSCAN = 0x4B,
+
+ ///
+ /// Vertical sweep fan(param4 corresponding NET_PTZ_CONTROL_SECTORSCAN,param1,param2,param3 is invalid)
+ /// 垂直扇扫(param4对应PTZ_CONTROL_SECTORSCAN,param1、param2、param3无效)
+ ///
+ VERSECTORSCAN = 0x4C,
+
+ ///
+ /// Set absolute focus, focus on value, param1 for focal length, range: [0255], param2 as the focus, scope: [0255], param3, param4 is invalid
+ /// 设定绝对焦距、聚焦值,param1为焦距,范围:[0,255],param2为聚焦,范围:[0,255],param3、param4无效
+ ///
+ SET_ABS_ZOOMFOCUS = 0x4D,
+
+ ///
+ /// Control fish eye PTZ,param4corresponding to structure NET_PTZ_CONTROL_SET_FISHEYE_EPTZ
+ /// 控制鱼眼电子云台,param4对应结构 PTZ_CONTROL_SET_FISHEYE_EPTZ
+ ///
+ SET_FISHEYE_EPTZ = 0x4E,
+
+ ///
+ /// Track start control(param4 corresponding to structure NET_PTZ_CONTROL_SET_TRACK_CONTROL,dwStop set as FALSE, param1、param2、param3 is invalid)
+ /// 轨道机开始控制(param4对应结构体为 PTZ_CONTROL_SET_TRACK_CONTROL,dwStop传FALSE, param1、param2、param3无效)
+ ///
+ SET_TRACK_START = 0x4F,
+
+ ///
+ /// Track stop control (param4 corresponding to structure NET_PTZ_CONTROL_SET_TRACK_CONTROL,dwStop set as FALSE,param1、param2、param3 is invalid)
+ /// 轨道机停止控制(param4对应结构体为 PTZ_CONTROL_SET_TRACK_CONTROL,dwStop传FALSE,param1、param2、param3无效)
+ ///
+ SET_TRACK_STOP = 0x50,
+
+ ///
+ /// Up + TELE param1=speed (1-8)
+ /// 上 + TELE param1=速度(1-8),下同
+ ///
+ UP_TELE = 0x70,
+
+ ///
+ /// Down + TELE
+ /// 下 + TELE
+ ///
+ DOWN_TELE,
+
+ ///
+ /// Left + TELE
+ /// 左 + TELE
+ ///
+ LEFT_TELE,
+
+ ///
+ /// Right + TELE
+ /// 右 + TELE
+ ///
+ RIGHT_TELE,
+
+ ///
+ /// Upper left + TELE
+ /// 左上 + TELE
+ ///
+ LEFTUP_TELE,
+
+ ///
+ /// Down left + TELE
+ /// 左下 + TELE
+ ///
+ LEFTDOWN_TELE,
+
+ ///
+ /// Upper right + TELE
+ /// 右上 + TELE
+ ///
+ TIGHTUP_TELE,
+
+ ///
+ /// Down right + TELE
+ /// 右下 + TELE
+ ///
+ RIGHTDOWN_TELE,
+
+ ///
+ /// Up + WIDE param1=speed (1-8)
+ /// 上 + WIDE param1=速度(1-8),下同
+ ///
+ UP_WIDE,
+
+ ///
+ /// Down + WIDE
+ /// 下 + WIDE
+ ///
+ DOWN_WIDE,
+
+ ///
+ /// Left + WIDE
+ /// 左 + WIDE
+ ///
+ LEFT_WIDE,
+
+ ///
+ /// Right + WIDE
+ /// 右 + WIDE
+ ///
+ RIGHT_WIDE,
+
+ ///
+ /// Upper left + WIDE
+ /// 左上 + WIDE
+ ///
+ LEFTUP_WIDE,
+
+ ///
+ /// Down left+ WIDE
+ /// 左下 + WIDE
+ ///
+ LEFTDOWN_WIDE,
+
+ ///
+ /// Upper right + WIDE
+ /// 右上 + WIDE
+ ///
+ TIGHTUP_WIDE,
+
+ ///
+ /// Down right + WIDE
+ /// 右下 + WIDE
+ ///
+ RIGHTDOWN_WIDE,
+
+ ///
+ /// Go to preset point and take a picture
+ /// 至预置点并抓图
+ ///
+ GOTOPRESETSNAP = 0x80,
+
+ ///
+ /// Calibrate the PTZ direction (two-way calibration)
+ /// 校准云台方向(双方向校准)
+ ///
+ DIRECTIONCALIBRATION = 0x82,
+
+ ///
+ /// Calibrate the PTZ direction (one-way calibration) param4 -> NET_IN_CALIBRATE_SINGLEDIRECTION
+ /// 校准云台方向(单防线校准), param4 -> NET_IN_CALIBRATE_SINGLEDIRECTION
+ ///
+ SINGLEDIRECTIONCALIBRATION = 0x83,
+
+ ///
+ /// Relative positioning of PTZ, param4 -> NET_IN_MOVERELATIVELY_INFO
+ /// 云台相对定位, param4 -> NET_IN_MOVERELATIVELY_INFO
+ ///
+ MOVE_RELATIVELY = 0x84,
+
+ ///
+ /// Set direction for PTZ, param4 -> NET_IN_SET_DIRECTION_INFO
+ /// 设置云台方向, param4 -> NET_IN_SET_DIRECTION_INFO
+ ///
+ SET_DIRECTION = 0x85,
+
+ ///
+ /// Precisely and absolutely movement control command, param4 -> NET_IN_PTZBASE_MOVEABSOLUTELY_INFO use CFG_CAP_CMD_PTZ command to get the capability of PTZ
+ /// if CFG_PTZ_PROTOCOL_CAPS_INFO -> bSupportReal equals TRUE means this device supports this feature
+ /// 精准绝对移动控制命令, param4 -> NET_IN_PTZBASE_MOVEABSOLUTELY_INFO(通过 CFG_CAP_CMD_PTZ 命令获取云台能力集( CFG_PTZ_PROTOCOL_CAPS_INFO )
+ /// 若bSupportReal为TRUE则设备支持该操作)
+ ///
+ BASE_MOVE_ABSOLUTELY = 0x86,
+
+ ///
+ /// Continuously movement control command, param4 -> NET_IN_PTZBASE_MOVECONTINUOUSLY_INFO. use CFG_CAP_CMD_PTZ command to get the capability of PTZ
+ /// if CFG_PTZ_PROTOCOL_CAPS_INFO -> stuMoveContinuously equals -> stuType.bSupportExtra equals TRUE means this device supports this feature
+ /// 云台连续移动控制命令, param4 -> NET_IN_PTZBASE_MOVECONTINUOUSLY_INFO. 通过 CFG_CAP_CMD_PTZ 命令获取云台能力集
+ /// 若 CFG_PTZ_PROTOCOL_CAPS_INFO 中 stuMoveContinuously 字段的 为 TRUE, 表示设备支持该操作
+ ///
+ BASE_MOVE_CONTINUOUSLY,
+
+ ///
+ /// Maximum command value
+ /// 最大命令值
+ ///
+ TOTAL,
+ }
+
+ ///
+ /// 预置点状态枚举
+ ///
+ public enum EM_DH_PTZ_PRESET_STATUS
+ {
+ UNKNOWN, // 未知
+ REACH, // 预置点到达
+ UNREACH, // 预置点未到达
+ }
+
+ ///
+ /// realplay type
+ /// 监视类型
+ ///
+ public enum EM_RealPlayType
+ {
+ ///
+ /// Real-time preview
+ /// 实时预览
+ ///
+ Realplay = 0,
+
+ ///
+ /// Multiple-channel preview
+ /// 多画面预览
+ ///
+ Multiplay,
+
+ ///
+ /// Real-time monitor-main stream. It is the same as EM_RealPlayType.Realplay
+ /// 实时监视-主码流,等同于EM_RealPlayType.Realplay
+ ///
+ Realplay_0,
+
+ ///
+ /// Real-time monitor -- extra stream 1
+ /// 实时监视-从码流1
+ ///
+ Realplay_1,
+
+ ///
+ /// Real-time monitor -- extra stream 2
+ /// 实时监视-从码流2
+ ///
+ Realplay_2,
+
+ ///
+ /// Real-time monitor -- extra stream 3
+ /// 实时监视-从码流3
+ ///
+ Realplay_3,
+
+ ///
+ /// Multiple-channel preview--1-window
+ /// 多画面预览-1画面
+ ///
+ Multiplay_1,
+
+ ///
+ /// Multiple-channel preview--4-window
+ /// 多画面预览-4画面
+ ///
+ Multiplay_4,
+
+ ///
+ /// Multiple-channel preview--8-window
+ /// 多画面预览-8画面
+ ///
+ Multiplay_8,
+
+ ///
+ /// Multiple-channel preview--9-window
+ /// 多画面预览-9画面
+ ///
+ Multiplay_9,
+
+ ///
+ /// Multiple-channel preview--16-window
+ /// 多画面预览-16画面
+ ///
+ Multiplay_16,
+
+ ///
+ /// Multiple-channel preview--6-window
+ /// 多画面预览-6画面
+ ///
+ Multiplay_6,
+
+ ///
+ /// Multiple-channel preview--12-window
+ /// 多画面预览-12画面
+ ///
+ Multiplay_12,
+
+ ///
+ /// Multiple-channel preview--25-window
+ /// 多画面预览-25画面
+ ///
+ Multiplay_25,
+
+ ///
+ /// Multiple-channel preview--36-window
+ /// 多画面预览-36画面
+ ///
+ Multiplay_36,
+
+ ///
+ /// test stream
+ /// 带宽测试码流
+ ///
+ Realplay_Test = 255,
+ }
+
+ #endregion Sdk Enum
+
+ #region Sdk Struct
+
+ ///
+ /// CLIENT_LoginWithHighLevelSecurity 输入参数
+ ///
+ public struct NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY
+ {
+ public uint dwSize;// 结构体大小
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string szIP; // IP
+
+ public int nPort; // 端口
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string szUserName; // 用户名
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string szPassword; // 密码
+
+ public EM_LOGIN_SPAC_CAP_TYPE emSpecCap; // 登录模式
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
+ public byte[] byReserved; // 字节对齐
+
+ public IntPtr pCapParam; // 见 CLIENT_LoginEx 接口 pCapParam 与 nSpecCap 关系
+ }
+
+ ///
+ /// device information structure
+ /// 设备信息结构体
+ ///
+ public struct NET_DEVICEINFO_Ex
+ {
+ ///
+ /// serial number
+ /// 序列号
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 48)]
+ public string sSerialNumber;
+
+ ///
+ /// count of alarm input
+ /// 报警输入个数
+ ///
+ public int nAlarmInPortNum;
+
+ ///
+ /// count of alarm output
+ /// 报警输出个数
+ ///
+ public int nAlarmOutPortNum;
+
+ ///
+ /// number of disk
+ /// 硬盘个数
+ ///
+ public int nDiskNum;
+
+ ///
+ /// device type, refer to EM_NET_DEVICE_TYPE
+ /// 设备类型,见枚举NET_DEVICE_TYPE
+ ///
+ public EM_NET_DEVICE_TYPE nDVRType;
+
+ ///
+ /// number of channel
+ /// 通道个数
+ ///
+ public int nChanNum;
+
+ ///
+ /// Online Timeout, Not Limited Access to 0, not 0 Minutes Limit Said
+ /// 在线超时时间,为0表示不限制登陆,非0表示限制的分钟数
+ ///
+ public byte byLimitLoginTime;
+
+ ///
+ /// When login failed due to password error, notice user by this parameter.This parameter is invalid when remaining login times is zero
+ /// 当登陆失败原因为密码错误时,通过此参数通知用户,剩余登陆次数,为0时表示此参数无效
+ ///
+ public byte byLeftLogTimes;
+
+ ///
+ /// keep bytes for aligned
+ /// 保留字节,字节对齐
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
+ public byte[] bReserved;
+
+ ///
+ /// when log in failed,the left time for users to unlock (seconds), -1 indicate the device haven't set the parameter
+ /// 当登陆失败,用户解锁剩余时间(秒数), -1表示设备未设置该参数
+ ///
+ public int nLockLeftTime;
+
+ ///
+ /// reserved
+ /// 保留字节
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
+ public byte[] Reserved;
+ }
+
+ ///
+ /// CLIENT_LoginWithHighLevelSecurity 输出参数
+ ///
+ public struct NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY
+ {
+ public uint dwSize;// 结构体大小
+ public NET_DEVICEINFO_Ex stuDeviceInfo; // 设备信息
+ public int nError; // 错误码,见 CLIENT_Login 接口错误码
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 132)]
+ public byte[] byReserved; // 保留字节
+ }
+
+ ///
+ /// 云台定位中非归一化坐标和变倍
+ ///
+ public struct NET_PTZSPACE_UNNORMALIZED
+ {
+ public int nPosX; // x坐标
+ public int nPosY; // y坐标
+ public int nZoom; // 放大倍率
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 52)]
+ public byte[] byReserved; // 预留字节
+ }
+
+ ///
+ /// 云台定位信息
+ ///
+ //云台定位信息
+ public struct DH_PTZ_LOCATION_INFO
+ {
+ public int nChannelID; // 通道号
+ public int nPTZPan; // 云台水平运动位置,有效范围:[0,3600]
+ public int nPTZTilt; // 云台垂直运动位置,有效范围:[-1800,1800]
+ public int nPTZZoom; // 云台光圈变动位置,有效范围:[0,128]
+ public byte bState; // 云台运动状态, 0-未知 1-运动 2-空闲
+ public byte bAction; // 云台动作,255-未知,0-预置点,1-线扫,2-巡航,3-巡迹,4-水平旋转,5-普通移动,6-巡迹录制,7-全景云台扫描,8-热度图
+
+ // 9-精确定位,10-设备校正,11-智能配置,12-云台重启
+ public byte bFocusState; // 云台聚焦状态, 0-未知, 1-运动状态, 2-空闲
+
+ public byte bEffectiveInTimeSection; // 在时间段内预置点状态是否有效
+
+ //如果当前上报的预置点是时间段内的预置点,则为1,其他情况为0
+ public int nPtzActionID; // 巡航ID号
+
+ public uint dwPresetID; // 云台所在预置点编号
+ public float fFocusPosition; // 聚焦位置
+ public byte bZoomState; // 云台ZOOM状态,0-未知,1-ZOOM,2-空闲
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public byte[] bReserved; // 对齐
+
+ public uint dwSequence; // 包序号,用于校验是否丢包
+ public uint dwUTC; // 对应的UTC(1970-1-1 00:00:00)秒数。
+ public EM_DH_PTZ_PRESET_STATUS emPresetStatus; // 预置点位置
+
+ ///
+ /// 保留字段
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 248)]
+ public int[] reserved;
+ };
+
+ #endregion Sdk Struct
+
+ #region Common Method
+
+ ///
+ /// network disconnection callback function original shape
+ /// 断线回调函数
+ ///
+ /// user LoginID:Login's returns value 登陆ID
+ /// device IP 设备IP
+ /// device prot 设备端口
+ /// user data from Init function 用户数据
+ public delegate void fDisConnectCallBack(IntPtr lLoginID, IntPtr pchDVRIP, int nDVRPort, IntPtr dwUser);
+
+ ///
+ /// network re-connection callback function original shape
+ /// 重连回调函数
+ ///
+ /// user LoginID:Login's returns value 登陆ID
+ /// device IP,string type 设备IP
+ /// device prot 设备端口
+ /// user data from SetAutoReconnect function 用户数据
+ public delegate void fHaveReConnectCallBack(IntPtr lLoginID, IntPtr pchDVRIP, int nDVRPort, IntPtr dwUser);
+
+ [DllImport(LibSdkPath)]
+ public static extern bool CLIENT_InitEx(fDisConnectCallBack? cbDisConnect, IntPtr dwUser, IntPtr lpInitParam);
+
+ [DllImport(LibSdkPath)]
+ public static extern void CLIENT_Cleanup();
+
+ [DllImport(LibSdkPath)]
+ public static extern int CLIENT_GetLastError();
+
+ [DllImport(LibSdkPath)]
+ public static extern IntPtr CLIENT_LoginWithHighLevelSecurity(ref NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam, ref NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam);
+
+ [DllImport(LibSdkPath)]
+ public static extern bool CLIENT_Logout(IntPtr lLoginID);
+
+ [DllImport(LibSdkPath)]
+ public static extern void CLIENT_SetAutoReconnect(fHaveReConnectCallBack cbAutoConnect, IntPtr dwUser);
+
+ [DllImport(LibSdkPath)]
+ public static extern bool CLIENT_QueryDevState(IntPtr lLoginID, int nType, IntPtr pBuf, int nBufLen, ref int pRetLen, int waittime);
+
+ ///
+ /// PTZ control
+ /// PTZ控制接口
+ ///
+ /// user LoginID:Login's returns value 登陆ID,Login返回值
+ /// channel number 通道号
+ /// PTZ control commands 控制命令
+ /// Parameter1 details refer to EM_EXTPTZ_ControlType 控制命令的参数1
+ /// Parameter2 details refer to EM_EXTPTZ_ControlType 控制命令的参数2
+ /// Parameter3 details refer to EM_EXTPTZ_ControlType 控制命令的参数3
+ /// stop or not, effective to PTZ eight-directions operation and lens operation. During other operation, this parameter should fill in false 是否停止
+ /// support PTZ control extensive command,support these commands: 控制命令的参数4
+ /// EM_EXTPTZ_ControlType.MOVE_ABSOLUTELY:Absolute motion control commands,param4 corresponding struct NET_PTZ_CONTROL_ABSOLUTELY
+ /// EM_EXTPTZ_ControlType.MOVE_CONTINUOUSLY:Continuous motion control commands,param4 corresponding struct NET_PTZ_CONTROL_CONTINUOUSLY
+ /// EM_EXTPTZ_ControlType.GOTOPRESET:PTZ control command, at a certain speed to preset locus,parm4 corresponding struct NET_PTZ_CONTROL_GOTOPRESET
+ /// EM_EXTPTZ_ControlType.SET_VIEW_RANGE:Set to horizon(param4 corresponding struct NET_PTZ_VIEW_RANGE_INFO
+ /// EM_EXTPTZ_ControlType.FOCUS_ABSOLUTELY:Absolute focus(param4 corresponding struct NET_PTZ_FOCUS_ABSOLUTELY
+ /// EM_EXTPTZ_ControlType.HORSECTORSCAN:Level fan sweep(param4 corresponding NET_PTZ_CONTROL_SECTORSCAN,param1、param2、param3 is invalid
+ /// EM_EXTPTZ_ControlType.VERSECTORSCAN:Vertical sweep fan(param4 corresponding NET_PTZ_CONTROL_SECTORSCAN,param1、param2、param3 is invalid
+ /// EM_EXTPTZ_ControlType.SET_FISHEYE_EPTZ:Control fish eye PTZ,param4corresponding to structure NET_PTZ_CONTROL_SET_FISHEYE_EPTZ
+ /// EM_EXTPTZ_ControlType.SET_TRACK_START/SET_TRACK_STOP:param4 corresponding to structure NET_PTZ_CONTROL_SET_TRACK_CONTROL,dwStop set as FALSE,param1、param2、param3 is invalid
+ /// failed return false, successful return true 失败返回false 成功返回true
+ [DllImport(LibSdkPath)]
+ public static extern bool CLIENT_DHPTZControlEx2(IntPtr lLoginID, int nChannelID, uint dwPTZCommand, int lParam1, int lParam2, int lParam3, bool dwStop, IntPtr param4);
+
+ ///
+ /// start real-time monitor.support 32bit and 64bit
+ /// 开始实时监视.支持32位和64位
+ ///
+ /// user LoginID:Login's returns value 登陆ID,Login返回值
+ /// real time monitor channel NO.(from 0). 通道号
+ /// display window handle. When value is 0(IntPtr.Zero), data are not decoded or displayed 显示窗口句柄
+ /// realplay type 监视类型
+ /// failed return 0, successful return the real time monitorID(real time monitor handle),as parameter of related function. 失败返回0,成功返回大于0的值
+ [DllImport(LibSdkPath)]
+ public static extern IntPtr CLIENT_RealPlayEx(IntPtr lLoginID, int nChannelID, IntPtr hWnd, EM_RealPlayType rType);
+
+ ///
+ /// stop real time monitoring
+ /// 关闭实时监视
+ ///
+ /// monitor handle StartRealPlay returns value 监视ID StartRealPlay返回值
+ /// failed return false, successful return true 失败返回false 成功返回true
+ [DllImport(LibSdkPath)]
+ public static extern bool CLIENT_StopRealPlayEx(IntPtr lRealHandle);
+
+ #endregion Common Method
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/DaHua/DaHuaSdk.cs b/EC.Util/CameraSDK/DaHua/DaHuaSdk.cs
new file mode 100644
index 0000000..607f759
--- /dev/null
+++ b/EC.Util/CameraSDK/DaHua/DaHuaSdk.cs
@@ -0,0 +1,180 @@
+using System.Runtime.InteropServices;
+
+namespace EC.Util.CameraSDK;
+
+public class DaHuaSdk : ICameraSdk
+{
+ #region Fields
+
+ private IntPtr LoginId { get; set; } = IntPtr.Zero;
+
+ private int Channel { get; } = 1;
+
+ #endregion Fields
+
+ public DaHuaSdk(CameraInfo cameraInfo) : base(cameraInfo)
+ {
+ }
+
+ #region Base Method
+
+ public override bool Init()
+ {
+ bool ret = ConnectSuccess();
+ if (ret) return true;
+ DaHuaOriSdk.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY stuInParam = new();
+ stuInParam.dwSize = (uint)Marshal.SizeOf(stuInParam);
+ stuInParam.szIP = CameraInfo.Ip;
+ stuInParam.nPort = CameraInfo.Port;
+ stuInParam.szUserName = CameraInfo.UserName;
+ stuInParam.szPassword = CameraInfo.Password;
+ stuInParam.emSpecCap = DaHuaOriSdk.EM_LOGIN_SPAC_CAP_TYPE.TCP;
+ stuInParam.pCapParam = IntPtr.Zero;
+ DaHuaOriSdk.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY stuOutParam = new();
+ stuOutParam.dwSize = (uint)Marshal.SizeOf(stuOutParam);
+ LoginId = DaHuaOriSdk.CLIENT_LoginWithHighLevelSecurity(ref stuInParam, ref stuOutParam);
+ ret = ConnectSuccess();
+ if (ret) DaHuaOriSdk.CLIENT_SetAutoReconnect(delegate (IntPtr lLoginID, IntPtr pchDVRIP, int nDVRPort, IntPtr dwUser)
+ {
+ LoginId = lLoginID;
+ }, IntPtr.Zero);
+ else BuildException();
+ return ret;
+ }
+
+ public override bool Destory()
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) return true;
+ ret = DaHuaOriSdk.CLIENT_Logout(LoginId);
+ if (ret) LoginId = IntPtr.Zero;
+ else BuildException();
+ return ret;
+ }
+
+ public override bool ConnectSuccess()
+ {
+ return LoginId != IntPtr.Zero;
+ }
+
+ internal override void BuildException()
+ {
+ uint errCode = (uint)DaHuaOriSdk.CLIENT_GetLastError();
+ if (errCode == 0) return;
+ errCode -= 0x80000000;
+ throw CameraException.New(CameraInfo, (int)errCode);
+ }
+
+ #endregion Base Method
+
+ #region Ptz Method
+
+ private static class GPIParams
+ {
+ public static int Size { get; private set; }
+ public static Type Type { get; private set; }
+
+ static GPIParams()
+ {
+ DaHuaOriSdk.DH_PTZ_LOCATION_INFO info = new();
+ Size = Marshal.SizeOf(info);
+ Type = info.GetType();
+ }
+ }
+
+ public override PtzInfo GetPtzInfo()
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) return PtzInfo.Default;
+
+ DaHuaOriSdk.DH_PTZ_LOCATION_INFO entity = new();
+ int nBufLen = GPIParams.Size;
+ int pRetLen = 0;
+ IntPtr ptrBuf = Marshal.AllocHGlobal(GPIParams.Size);
+ Marshal.StructureToPtr(entity, ptrBuf, true);
+ try
+ {
+ ret = DaHuaOriSdk.CLIENT_QueryDevState(LoginId, (int)DaHuaOriSdk.EM_DEVICE_STATE.PTZ_LOCATION, ptrBuf, nBufLen, ref pRetLen, 3000);
+ if (!ret) { BuildException(); return PtzInfo.Default; }
+ object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type);
+ if (objBuf == null) return PtzInfo.Default;
+ entity = (DaHuaOriSdk.DH_PTZ_LOCATION_INFO)objBuf;
+ return PtzInfo.New(entity.nPTZPan, entity.nPTZTilt, entity.nPTZZoom);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptrBuf);
+ }
+ }
+
+ public override bool TryGetPtzInfo(out PtzInfo ptzInfo)
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) { ptzInfo = PtzInfo.Default; return false; }
+
+ DaHuaOriSdk.DH_PTZ_LOCATION_INFO entity = new();
+ int nBufLen = GPIParams.Size;
+ int pRetLen = 0;
+ IntPtr ptrBuf = Marshal.AllocHGlobal(GPIParams.Size);
+ Marshal.StructureToPtr(entity, ptrBuf, true);
+ try
+ {
+ ret = DaHuaOriSdk.CLIENT_QueryDevState(LoginId, (int)DaHuaOriSdk.EM_DEVICE_STATE.PTZ_LOCATION, ptrBuf, nBufLen, ref pRetLen, 3000);
+ if (!ret) { BuildException(); ptzInfo = PtzInfo.Default; return false; }
+ object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type);
+ if (objBuf == null) { ptzInfo = PtzInfo.Default; return false; }
+ entity = (DaHuaOriSdk.DH_PTZ_LOCATION_INFO)objBuf;
+ ptzInfo = PtzInfo.New(entity.nPTZPan, entity.nPTZTilt, entity.nPTZZoom);
+ return true;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptrBuf);
+ }
+ }
+
+ public override bool PtzMove(int cmd, int stop, int speed)
+ {
+ if (!ConnectSuccess()) return false;
+ bool stopret = stop == 1;
+ bool ret = DaHuaOriSdk.CLIENT_DHPTZControlEx2(LoginId, Channel, (uint)cmd, speed, speed, 0, stopret, IntPtr.Zero);
+ if (!ret) BuildException();
+ return ret;
+ }
+
+ public override bool PtzPreset(int cmd, int presetId)
+ {
+ if (!ConnectSuccess()) return false;
+ bool ret = DaHuaOriSdk.CLIENT_DHPTZControlEx2(LoginId, Channel, (uint)DaHuaOriSdk.EM_EXTPTZ_ControlType.POINT_MOVE, 0, presetId, 0, false, IntPtr.Zero);
+ if (!ret) BuildException();
+ return ret;
+ }
+
+ #endregion Ptz Method
+
+ #region Video Method
+
+ private IntPtr RealplayHandle { get; set; } = IntPtr.Zero;
+
+ public override void StartPlay(IntPtr hwnd)
+ {
+ if (!ConnectSuccess() || IsPlaying()) return;
+ RealplayHandle = DaHuaOriSdk.CLIENT_RealPlayEx(LoginId, Channel, hwnd, DaHuaOriSdk.EM_RealPlayType.Realplay);
+ if (RealplayHandle == IntPtr.Zero) BuildException();
+ }
+
+ public override void StopPlay()
+ {
+ if (!IsPlaying()) return;
+ bool ret = DaHuaOriSdk.CLIENT_StopRealPlayEx(RealplayHandle);
+ RealplayHandle = IntPtr.Zero;
+ if (!ret) BuildException();
+ }
+
+ public override bool IsPlaying()
+ {
+ return RealplayHandle != IntPtr.Zero;
+ }
+
+ #endregion Video Method
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/HiK/HiKOriSdk.cs b/EC.Util/CameraSDK/HiK/HiKOriSdk.cs
new file mode 100644
index 0000000..f435066
--- /dev/null
+++ b/EC.Util/CameraSDK/HiK/HiKOriSdk.cs
@@ -0,0 +1,402 @@
+//#define Win32
+#define Win64
+
+//#define Linux32
+//#define Linux64
+using System.Runtime.InteropServices;
+
+namespace EC.Util.CameraSDK;
+
+public class HiKOriSdk
+{
+ #region Fields
+
+#if Win32
+ public const string LibSdkPath = @"./libs/hik/win32/HCNetSDK.dll";
+#elif Win64
+ public const string LibSdkPath = @"./libs/hik/win64/HCNetSDK.dll";
+#elif Linux32
+ public const string LibSdkPath = @"./libs/hik/linux32/libhcnetsdk.so";
+#elif Linux64
+ public const string LibSdkPath = @"./libs/hik/linux64/libhcnetsdk.so";
+#endif
+
+ private const bool Debug = true;
+
+ #endregion Fields
+
+ static HiKOriSdk()
+ {
+ GlobalInit();
+ }
+
+ #region Global
+
+ public static bool InitSuccess { get; private set; }
+
+ public static bool GlobalInit()
+ {
+ if (InitSuccess) return true;
+ bool ret = NET_DVR_Init();
+ InitSuccess = ret;
+ if (!ret) throw new Exception("HiKOriSdk global init failure.");
+ if (Debug) NET_DVR_SetLogToFile(3, Path.Combine("./log", "hikSdkLog"), true);
+ return ret;
+ }
+
+ public static bool GlobalDestory()
+ {
+ if (!InitSuccess) return true;
+ bool ret = NET_DVR_Cleanup();
+ if (ret) InitSuccess = false;
+ return ret;
+ }
+
+ #endregion Global
+
+ #region Sdk Const
+
+ public const int SERIALNO_LEN = 48; //序列号长度
+
+ public const int PtzSpeedMin = 1;
+ public const int PtzSpeedMax = 7;
+
+ public const int NET_DVR_DEV_ADDRESS_MAX_LEN = 129;
+ public const int NET_DVR_LOGIN_USERNAME_MAX_LEN = 64;
+ public const int NET_DVR_LOGIN_PASSWD_MAX_LEN = 64;
+
+ #region 用于 NET_DVR_SetDVRConfig 和 NET_DVR_GetDVRConfig
+
+ public const int NET_DVR_SET_PTZPOS = 292; //云台设置PTZ位置
+ public const int NET_DVR_GET_PTZPOS = 293; //云台获取PTZ位置
+ public const int NET_DVR_GET_PTZSCOPE = 294; //云台获取PTZ范围
+
+ #endregion 用于 NET_DVR_SetDVRConfig 和 NET_DVR_GetDVRConfig
+
+ /********************预览回调函数*********************/
+ public const int NET_DVR_SYSHEAD = 1;//系统头数据
+ public const int NET_DVR_STREAMDATA = 2;//视频流数据(包括复合流和音视频分开的视频流数据)
+ public const int NET_DVR_AUDIOSTREAMDATA = 3;//音频流数据
+ public const int NET_DVR_STD_VIDEODATA = 4;//标准视频流数据
+ public const int NET_DVR_STD_AUDIODATA = 5;//标准音频流数据
+
+ #region PtzMove
+
+ public const int ZOOM_IN = 11;// 焦距以速度SS变大(倍率变大)
+ public const int ZOOM_OUT = 12;// 焦距以速度SS变小(倍率变小)
+ public const int FOCUS_NEAR = 13;// 焦点以速度SS前调
+ public const int FOCUS_FAR = 14;// 焦点以速度SS后调
+ public const int IRIS_OPEN = 15;// 光圈以速度SS扩大
+ public const int IRIS_CLOSE = 16;// 光圈以速度SS缩小
+
+ public const int TILT_UP = 21; /* 云台以SS的速度上仰 */
+ public const int TILT_DOWN = 22; /* 云台以SS的速度下俯 */
+ public const int PAN_LEFT = 23; /* 云台以SS的速度左转 */
+ public const int PAN_RIGHT = 24; /* 云台以SS的速度右转 */
+ public const int UP_LEFT = 25; /* 云台以SS的速度上仰和左转 */
+ public const int UP_RIGHT = 26; /* 云台以SS的速度上仰和右转 */
+ public const int DOWN_LEFT = 27; /* 云台以SS的速度下俯和左转 */
+ public const int DOWN_RIGHT = 28; /* 云台以SS的速度下俯和右转 */
+ public const int PAN_AUTO = 29; /* 云台以SS的速度左右自动扫描 */
+ public const int GOTO_PRESET = 39; /* 快球转到预置点 */
+
+ #endregion PtzMove
+
+ #endregion Sdk Const
+
+ #region Sdk Struct
+
+ //NET_DVR_Login()参数结构
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NET_DVR_DEVICEINFO
+ {
+ [MarshalAs(
+ UnmanagedType.ByValArray,
+ SizeConst = SERIALNO_LEN,
+ ArraySubType = UnmanagedType.I1
+ )]
+ public byte[] sSerialNumber; //序列号
+
+ public byte byAlarmInPortNum; //DVR报警输入个数
+ public byte byAlarmOutPortNum; //DVR报警输出个数
+ public byte byDiskNum; //DVR硬盘个数
+ public byte byDVRType; //DVR类型, 1:DVR 2:ATM DVR 3:DVS ......
+ public byte byChanNum; //DVR 通道个数
+ public byte byStartChan; //起始通道号,例如DVS-1,DVR - 1
+ }
+
+ //NET_DVR_Login_V30()参数结构
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NET_DVR_DEVICEINFO_V30
+ {
+ [MarshalAs(
+ UnmanagedType.ByValArray,
+ SizeConst = SERIALNO_LEN,
+ ArraySubType = UnmanagedType.I1
+ )]
+ public byte[] sSerialNumber; //序列号
+
+ public byte byAlarmInPortNum; //报警输入个数
+ public byte byAlarmOutPortNum; //报警输出个数
+ public byte byDiskNum; //硬盘个数
+ public byte byDVRType; //设备类型, 1:DVR 2:ATM DVR 3:DVS ......
+ public byte byChanNum; //模拟通道个数
+ public byte byStartChan; //起始通道号,例如DVS-1,DVR - 1
+ public byte byAudioChanNum; //语音通道数
+ public byte byIPChanNum; //最大数字通道个数,低位
+ public byte byZeroChanNum; //零通道编码个数 //2010-01-16
+ public byte byMainProto; //主码流传输协议类型 0-private, 1-rtsp,2-同时支持private和rtsp
+ public byte bySubProto; //子码流传输协议类型0-private, 1-rtsp,2-同时支持private和rtsp
+ public byte bySupport; //能力,位与结果为0表示不支持,1表示支持,
+
+ //bySupport & 0x1, 表示是否支持智能搜索
+ //bySupport & 0x2, 表示是否支持备份
+ //bySupport & 0x4, 表示是否支持压缩参数能力获取
+ //bySupport & 0x8, 表示是否支持多网卡
+ //bySupport & 0x10, 表示支持远程SADP
+ //bySupport & 0x20, 表示支持Raid卡功能
+ //bySupport & 0x40, 表示支持IPSAN 目录查找
+ //bySupport & 0x80, 表示支持rtp over rtsp
+ public byte bySupport1; // 能力集扩充,位与结果为0表示不支持,1表示支持
+
+ //bySupport1 & 0x1, 表示是否支持snmp v30
+ //bySupport1 & 0x2, 支持区分回放和下载
+ //bySupport1 & 0x4, 是否支持布防优先级
+ //bySupport1 & 0x8, 智能设备是否支持布防时间段扩展
+ //bySupport1 & 0x10, 表示是否支持多磁盘数(超过33个)
+ //bySupport1 & 0x20, 表示是否支持rtsp over http
+ //bySupport1 & 0x80, 表示是否支持车牌新报警信息2012-9-28, 且还表示是否支持NET_DVR_IPPARACFG_V40结构体
+ public byte bySupport2; /*能力,位与结果为0表示不支持,非0表示支持
+
+ bySupport2 & 0x1, 表示解码器是否支持通过URL取流解码
+ bySupport2 & 0x2, 表示支持FTPV40
+ bySupport2 & 0x4, 表示支持ANR
+ bySupport2 & 0x8, 表示支持CCD的通道参数配置
+ bySupport2 & 0x10, 表示支持布防报警回传信息(仅支持抓拍机报警 新老报警结构)
+ bySupport2 & 0x20, 表示是否支持单独获取设备状态子项
+ bySupport2 & 0x40, 表示是否是码流加密设备*/
+ public ushort wDevType; //设备型号
+ public byte bySupport3; //能力集扩展,位与结果为0表示不支持,1表示支持
+
+ //bySupport3 & 0x1, 表示是否多码流
+ // bySupport3 & 0x4 表示支持按组配置, 具体包含 通道图像参数、报警输入参数、IP报警输入、输出接入参数、
+ // 用户参数、设备工作状态、JPEG抓图、定时和时间抓图、硬盘盘组管理
+ //bySupport3 & 0x8为1 表示支持使用TCP预览、UDP预览、多播预览中的"延时预览"字段来请求延时预览(后续都将使用这种方式请求延时预览)。而当bySupport3 & 0x8为0时,将使用 "私有延时预览"协议。
+ //bySupport3 & 0x10 表示支持"获取报警主机主要状态(V40)"。
+ //bySupport3 & 0x20 表示是否支持通过DDNS域名解析取流
+
+ public byte byMultiStreamProto; //是否支持多码流,按位表示,0-不支持,1-支持,bit1-码流3,bit2-码流4,bit7-主码流,bit-8子码流
+ public byte byStartDChan; //起始数字通道号,0表示无效
+ public byte byStartDTalkChan; //起始数字对讲通道号,区别于模拟对讲通道号,0表示无效
+ public byte byHighDChanNum; //数字通道个数,高位
+ public byte bySupport4;
+ public byte byLanguageType; // 支持语种能力,按位表示,每一位0-不支持,1-支持
+
+ // byLanguageType 等于0 表示 老设备
+ // byLanguageType & 0x1表示支持中文
+ // byLanguageType & 0x2表示支持英文
+ [MarshalAs(
+ UnmanagedType.ByValArray,
+ SizeConst = 9,
+ ArraySubType = UnmanagedType.I1
+ )]
+ public byte[] byRes2; //保留
+ }
+
+ public delegate void LOGINRESULTCALLBACK(int lUserID, int dwResult, IntPtr lpDeviceInfo, IntPtr pUser);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NET_DVR_USER_LOGIN_INFO
+ {
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = NET_DVR_DEV_ADDRESS_MAX_LEN, ArraySubType = UnmanagedType.I1)]
+ public byte[] sDeviceAddress;
+
+ public byte byUseTransport;
+ public ushort wPort;
+
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = NET_DVR_LOGIN_USERNAME_MAX_LEN, ArraySubType = UnmanagedType.I1)]
+ public byte[] sUserName;
+
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = NET_DVR_LOGIN_PASSWD_MAX_LEN, ArraySubType = UnmanagedType.I1)]
+ public byte[] sPassword;
+
+ public LOGINRESULTCALLBACK cbLoginResult;
+ public IntPtr pUser;
+ public bool bUseAsynLogin;
+ public byte byProxyType; //0:不使用代理,1:使用标准代理,2:使用EHome代理
+ public byte byUseUTCTime; //0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换
+ public byte byLoginMode; //0-Private, 1-ISAPI, 2-自适应
+ public byte byHttps; //0-不适用tls,1-使用tls 2-自适应
+ public int iProxyID; //代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值
+ public byte byVerifyMode; //认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效;
+
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 119, ArraySubType = UnmanagedType.I1)]
+ public byte[] byRes3;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NET_DVR_DEVICEINFO_V40
+ {
+ public NET_DVR_DEVICEINFO_V30 struDeviceV30;
+ public byte bySupportLock; //设备支持锁定功能,该字段由SDK根据设备返回值来赋值的。bySupportLock为1时,dwSurplusLockTime和byRetryLoginTime有效
+ public byte byRetryLoginTime; //剩余可尝试登陆的次数,用户名,密码错误时,此参数有效
+ public byte byPasswordLevel; //admin密码安全等级0-无效,1-默认密码,2-有效密码,3-风险较高的密码。当用户的密码为出厂默认密码(12345)或者风险较高的密码时,上层客户端需要提示用户更改密码。
+ public byte byProxyType;//代理类型,0-不使用代理, 1-使用socks5代理, 2-使用EHome代理
+ public uint dwSurplusLockTime; //剩余时间,单位秒,用户锁定时,此参数有效
+ public byte byCharEncodeType; //字符编码类型(SDK所有接口返回的字符串编码类型,透传接口除外):0- 无字符编码信息(老设备),1- GB2312(简体中文),2- GBK,3- BIG5(繁体中文),4- Shift_JIS(日文),5- EUC-KR(韩文),6- UTF-8,7- ISO8859-1,8- ISO8859-2,9- ISO8859-3,…,依次类推,21- ISO8859-15(西欧)
+ public byte bySupportDev5;//支持v50版本的设备参数获取,设备名称和设备类型名称长度扩展为64字节
+ public byte bySupport; //能力集扩展,位与结果:0- 不支持,1- 支持
+
+ // bySupport & 0x1: 保留
+ // bySupport & 0x2: 0-不支持变化上报 1-支持变化上报
+ public byte byLoginMode; //登录模式 0-Private登录 1-ISAPI登录
+
+ public int dwOEMCode;
+ public int iResidualValidity; //该用户密码剩余有效天数,单位:天,返回负值,表示密码已经超期使用,例如“-3表示密码已经超期使用3天”
+ public byte byResidualValidity; // iResidualValidity字段是否有效,0-无效,1-有效
+
+ [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 243, ArraySubType = UnmanagedType.I1)]
+ public byte[] byRes2;
+ }
+
+ //球机位置信息
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NET_DVR_PTZPOS
+ {
+ public ushort wAction; //获取时该字段无效
+ public ushort wPanPos; //水平参数
+ public ushort wTiltPos; //垂直参数
+ public ushort wZoomPos; //变倍参数
+ }
+
+ //球机范围信息
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NET_DVR_PTZSCOPE
+ {
+ public ushort wPanPosMin; //水平参数min
+ public ushort wPanPosMax; //水平参数max
+ public ushort wTiltPosMin; //垂直参数min
+ public ushort wTiltPosMax; //垂直参数max
+ public ushort wZoomPosMin; //变倍参数min
+ public ushort wZoomPosMax; //变倍参数max
+ }
+
+ //邦诺CVR
+ public const int MAX_ID_COUNT = 256;
+
+ public const int MAX_STREAM_ID_COUNT = 1024;
+ public const int STREAM_ID_LEN = 32;
+ public const int PLAN_ID_LEN = 32;
+
+ //预览V40接口
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NET_DVR_PREVIEWINFO
+ {
+ public int lChannel; //通道号
+ public uint dwStreamType; // 码流类型,0-主码流,1-子码流,2-码流3,3-码流4 等以此类推
+ public uint dwLinkMode; // 0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-RTP/RTSP,5-RSTP/HTTP
+ public IntPtr hPlayWnd; //播放窗口的句柄,为NULL表示不播放图象
+ public bool bBlocked; //0-非阻塞取流, 1-阻塞取流, 如果阻塞SDK内部connect失败将会有5s的超时才能够返回,不适合于轮询取流操作.
+ public bool bPassbackRecord; //0-不启用录像回传,1启用录像回传
+ public byte byPreviewMode; //预览模式,0-正常预览,1-延迟预览
+
+ [MarshalAs(
+ UnmanagedType.ByValArray,
+ SizeConst = STREAM_ID_LEN,
+ ArraySubType = UnmanagedType.I1
+ )]
+ public byte[] byStreamID; //流ID,lChannel为0xffffffff时启用此参数
+
+ public byte byProtoType; //应用层取流协议,0-私有协议,1-RTSP协议
+ public byte byRes1;
+ public byte byVideoCodingType; //码流数据编解码类型 0-通用编码数据 1-热成像探测器产生的原始数据(温度数据的加密信息,通过去加密运算,将原始数据算出真实的温度值)
+ public uint dwDisplayBufNum; //播放库播放缓冲区最大缓冲帧数,范围1-50,置0时默认为1
+ public byte byNPQMode; //NPQ是直连模式,还是过流媒体 0-直连 1-过流媒体
+
+ [MarshalAs(
+ UnmanagedType.ByValArray,
+ SizeConst = 215,
+ ArraySubType = UnmanagedType.I1
+ )]
+ public byte[] byRes;
+ }
+
+ #endregion Sdk Struct
+
+ #region Common Method
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_Init();
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_Cleanup();
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_SetLogToFile(int bLogEnable, string strLogDir, bool bAutoDel);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern uint NET_DVR_GetLastError();
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NET_DVR_Login_V30(string sDVRIP, int wDVRPort, string sUserName, string sPassword, ref NET_DVR_DEVICEINFO_V30 lpDeviceInfo);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NET_DVR_Login_V40(ref NET_DVR_USER_LOGIN_INFO pLoginInfo, ref NET_DVR_DEVICEINFO_V40 lpDeviceInfo);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_Logout(int iUserID);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_SetReconnect(uint dwInterval, int bEnableRecon);
+
+ //参数配置 begin
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_GetDVRConfig(int lUserID, uint dwCommand, int lChannel, IntPtr lpOutBuffer, uint dwOutBufferSize, ref uint lpBytesReturned);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_PTZControlWithSpeed_Other(int lUserID, int lChannel, uint dwPTZCommand, uint dwStop, uint dwSpeed);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_PTZPreset_Other(int lUserID, int lChannel, uint dwPTZPresetCmd, uint dwPresetIndex);
+
+ /*********************************************************
+ Function: REALDATACALLBACK
+ Desc: 预览回调
+ Input: lRealHandle 当前的预览句柄
+ dwDataType 数据类型
+ pBuffer 存放数据的缓冲区指针
+ dwBufSize 缓冲区大小
+ pUser 用户数据
+ Output:
+ Return: void
+ **********************************************************/
+
+ public delegate void RealDataCallBack(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser);
+
+ /*********************************************************
+ Function: NET_DVR_RealPlay_V40
+ Desc: 实时预览扩展接口。
+ Input: lUserID [in] NET_DVR_Login()或NET_DVR_Login_V30()的返回值
+ lpPreviewInfo [in] 预览参数
+ fRealDataCallBack_V30 [in] 码流数据回调函数
+ pUser [in] 用户数据
+ Output:
+ Return: 1表示失败,其他值作为NET_DVR_StopRealPlay等函数的句柄参数
+ **********************************************************/
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NET_DVR_RealPlay_V40(int iUserID, ref NET_DVR_PREVIEWINFO lpPreviewInfo, RealDataCallBack fRealDataCallBack_V30, IntPtr pUser);
+
+ /*********************************************************
+ Function: NET_DVR_StopRealPlay
+ Desc: 停止预览。
+ Input: lRealHandle [in] 预览句柄,NET_DVR_RealPlay或者NET_DVR_RealPlay_V30的返回值
+ Output:
+ Return:
+ **********************************************************/
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NET_DVR_StopRealPlay(int iRealHandle);
+
+ #endregion Common Method
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/HiK/HiKSdk.cs b/EC.Util/CameraSDK/HiK/HiKSdk.cs
new file mode 100644
index 0000000..a6acd39
--- /dev/null
+++ b/EC.Util/CameraSDK/HiK/HiKSdk.cs
@@ -0,0 +1,275 @@
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace EC.Util.CameraSDK;
+
+public class HiKSdk : ICameraSdk
+{
+ #region Fields
+
+ private int LoginId { get; set; } = -1;
+
+ private int Channel { get; } = 1;
+
+ #endregion Fields
+
+ public HiKSdk(CameraInfo cameraInfo) : base(cameraInfo)
+ {
+ }
+
+ #region Base Method
+
+ public override bool Init()
+ {
+ bool ret = ConnectSuccess();
+ if (ret) return true;
+ HiKOriSdk.NET_DVR_USER_LOGIN_INFO loginInfo = new()
+ {
+ bUseAsynLogin = false,
+ sDeviceAddress = new byte[129],
+ wPort = (ushort)CameraInfo.Port,
+ sUserName = new byte[64],
+ sPassword = new byte[64]
+ };
+ Encoding.Default.GetBytes(CameraInfo.Ip).CopyTo(loginInfo.sDeviceAddress, 0);
+ Encoding.Default.GetBytes(CameraInfo.UserName).CopyTo(loginInfo.sUserName, 0);
+ Encoding.Default.GetBytes(CameraInfo.Password).CopyTo(loginInfo.sPassword, 0);
+ HiKOriSdk.NET_DVR_DEVICEINFO_V40 deviceInfo = new();
+ LoginId = HiKOriSdk.NET_DVR_Login_V40(ref loginInfo, ref deviceInfo);
+ ret = ConnectSuccess();
+ if (ret) HiKOriSdk.NET_DVR_SetReconnect(10000, 1);
+ else BuildException();
+ //HiKOriSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new();
+ //LoginId = HiKOriSDK.NET_DVR_Login_V30(CameraInfo.Ip, CameraInfo.Port, CameraInfo.UserName, CameraInfo.Password, ref deviceInfo);
+ return ret;
+ }
+
+ public override bool Destory()
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) return true;
+ ret = HiKOriSdk.NET_DVR_Logout(LoginId);
+ if (ret) LoginId = -1;
+ else BuildException();
+ return ret;
+ }
+
+ public override bool ConnectSuccess()
+ {
+ return LoginId >= 0;
+ }
+
+ internal override void BuildException()
+ {
+ uint errCode = HiKOriSdk.NET_DVR_GetLastError();
+ if (errCode == 0) return;
+ throw CameraException.New(CameraInfo, (int)errCode);
+ }
+
+ private void BuildPlayCtrlException(int nPort)
+ {
+ string err = $"PlayCtrlSdk failed, error code={PlayCtrlSdk.PlayM4_GetLastError(nPort)}";
+ throw CameraException.New(CameraInfo, -1, err);
+ }
+
+ #endregion Base Method
+
+ #region Ptz Method
+
+ private static class GPIParams
+ {
+ public static int Size { get; private set; }
+ public static Type Type { get; private set; }
+
+ static GPIParams()
+ {
+ HiKOriSdk.NET_DVR_PTZPOS ptzPos = new();
+ Size = Marshal.SizeOf(ptzPos);
+ Type = ptzPos.GetType();
+ }
+ }
+
+ public override PtzInfo GetPtzInfo()
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) return PtzInfo.Default;
+
+ HiKOriSdk.NET_DVR_PTZPOS entity = new();
+ int dwSize = GPIParams.Size;
+ uint dwReturned = 0;
+ IntPtr ptrBuf = Marshal.AllocHGlobal(dwSize);
+ Marshal.StructureToPtr(entity, ptrBuf, true);
+ try
+ {
+ ret = HiKOriSdk.NET_DVR_GetDVRConfig(LoginId, HiKOriSdk.NET_DVR_GET_PTZPOS, 0, ptrBuf, (uint)dwSize, ref dwReturned);
+ if (!ret) { BuildException(); return PtzInfo.Default; }
+ object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type);
+ if (objBuf == null) return PtzInfo.Default;
+ entity = (HiKOriSdk.NET_DVR_PTZPOS)objBuf;
+ return PtzInfo.New(entity.wPanPos, entity.wTiltPos, entity.wZoomPos);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptrBuf);
+ }
+ }
+
+ public override bool TryGetPtzInfo(out PtzInfo ptzInfo)
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) { ptzInfo = PtzInfo.Default; return false; }
+
+ HiKOriSdk.NET_DVR_PTZPOS entity = new();
+ int dwSize = GPIParams.Size;
+ uint dwReturned = 0;
+ IntPtr ptrBuf = Marshal.AllocHGlobal(dwSize);
+ Marshal.StructureToPtr(entity, ptrBuf, true);
+ try
+ {
+ ret = HiKOriSdk.NET_DVR_GetDVRConfig(LoginId, HiKOriSdk.NET_DVR_GET_PTZPOS, 0, ptrBuf, (uint)dwSize, ref dwReturned);
+ if (!ret) { BuildException(); ptzInfo = PtzInfo.Default; return false; }
+ object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type);
+ if (objBuf == null) { ptzInfo = PtzInfo.Default; return false; }
+ entity = (HiKOriSdk.NET_DVR_PTZPOS)objBuf;
+ ptzInfo = PtzInfo.New(entity.wPanPos, entity.wTiltPos, entity.wZoomPos);
+ return true;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptrBuf);
+ }
+ }
+
+ public override bool PtzMove(int cmd, int stop, int speed)
+ {
+ if (!ConnectSuccess()) return false;
+ bool ret = HiKOriSdk.NET_DVR_PTZControlWithSpeed_Other(LoginId, Channel, (uint)cmd, (uint)stop, (uint)speed);
+ if (!ret) BuildException();
+ return ret;
+ }
+
+ public override bool PtzPreset(int cmd, int presetId)
+ {
+ if (!ConnectSuccess()) return false;
+ bool ret = HiKOriSdk.NET_DVR_PTZPreset_Other(LoginId, Channel, (uint)cmd, (uint)presetId);
+ if (!ret) BuildException();
+ return ret;
+ }
+
+ #endregion Ptz Method
+
+ #region Video Method
+
+ private int RealplayHandle { get; set; } = -1;
+
+ private int RealpalyPort { get; set; } = -1;
+
+ private IntPtr Hwnd { get; set; }
+
+ public override void StartPlay(IntPtr hwnd)
+ {
+ if (!ConnectSuccess() || IsPlaying()) return;
+ Hwnd = hwnd;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) StartPlayWindows();
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) StartPlayLinux();
+ else Hwnd = IntPtr.Zero;
+ }
+
+ private void StartPlayWindows()
+ {
+ HiKOriSdk.NET_DVR_PREVIEWINFO previewInfo = new()
+ {
+ hPlayWnd = Hwnd, //预览窗口
+ lChannel = 1, //预览的设备通道
+ dwStreamType = 0, //码流类型:0-主码流,1-子码流,2-码流3,3-码流4,以此类推
+ dwLinkMode = 0, //连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
+ bBlocked = true, //0- 非阻塞取流,1- 阻塞取流
+ };
+ IntPtr pUser = new(); //用户数据
+ RealplayHandle = HiKOriSdk.NET_DVR_RealPlay_V40(LoginId, ref previewInfo, null, pUser);
+ if (RealplayHandle < 0) BuildException();
+ }
+
+ private void StartPlayLinux()
+ {
+ if (RealpalyPort < 0)
+ {
+ int nPort = -1;
+ bool ret = PlayCtrlSdk.PlayM4_GetPort(ref nPort);
+ if (!ret) BuildPlayCtrlException(nPort);
+ RealpalyPort = nPort;
+ }
+ HiKOriSdk.NET_DVR_PREVIEWINFO previewInfo = new()
+ {
+ hPlayWnd = IntPtr.Zero, //预览窗口
+ lChannel = 1, //预览的设备通道
+ dwStreamType = 0, //码流类型:0-主码流,1-子码流,2-码流3,3-码流4,以此类推
+ dwLinkMode = 0, //连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
+ bBlocked = true, //0- 非阻塞取流,1- 阻塞取流
+ };
+ RealplayHandle = HiKOriSdk.NET_DVR_RealPlay_V40(LoginId, ref previewInfo, RealDataCallBack, IntPtr.Zero);
+ if (RealplayHandle < 0) BuildException();
+ }
+
+ private void RealDataCallBack(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser)
+ {
+ if (dwBufSize <= 0) return;
+ switch (dwDataType)
+ {
+ case HiKOriSdk.NET_DVR_SYSHEAD:
+ try
+ {
+ PlayCtrlSdk.PlayM4_SetStreamOpenMode(RealpalyPort, 0);
+ PlayCtrlSdk.PlayM4_OpenStream(RealpalyPort, pBuffer, dwBufSize, 2 * 1024 * 1024);
+ if (!PlayCtrlSdk.PlayM4_Play(RealpalyPort, Hwnd)) BuildPlayCtrlException(RealpalyPort);
+ }
+ catch (Exception)
+ {
+ StopPlay();
+ throw;
+ }
+ break;
+
+ case HiKOriSdk.NET_DVR_STREAMDATA:
+ PlayCtrlSdk.PlayM4_InputData(RealpalyPort, pBuffer, dwBufSize);
+ break;
+ }
+ }
+
+ public override void StopPlay()
+ {
+ if (!IsPlaying()) return;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) StopPlayWindows();
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) StopPlayLinux();
+ Hwnd = IntPtr.Zero;
+ }
+
+ public void StopPlayWindows()
+ {
+ bool ret = HiKOriSdk.NET_DVR_StopRealPlay(RealplayHandle);
+ RealplayHandle = -1;
+ if (!ret) BuildException();
+ }
+
+ public void StopPlayLinux()
+ {
+ bool ret = HiKOriSdk.NET_DVR_StopRealPlay(RealplayHandle);
+ RealplayHandle = -1;
+ if (RealpalyPort >= 0)
+ {
+ //if(!PlayCtrlSdk.PlayM4_Stop(RealpalyPort)) BuildPlayCtrlException(RealpalyPort);
+ PlayCtrlSdk.PlayM4_Stop(RealpalyPort);
+ PlayCtrlSdk.PlayM4_CloseStream(RealpalyPort);
+ PlayCtrlSdk.PlayM4_FreePort(RealpalyPort);
+ RealpalyPort = -1;
+ }
+ if (!ret) BuildException();
+ }
+
+ public override bool IsPlaying()
+ {
+ return RealplayHandle >= 0;
+ }
+
+ #endregion Video Method
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs b/EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs
new file mode 100644
index 0000000..9af7477
--- /dev/null
+++ b/EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs
@@ -0,0 +1,77 @@
+//#define Win32
+#define Win64
+
+//#define Linux32
+//#define Linux64
+using System.Runtime.InteropServices;
+
+namespace EC.Util.CameraSDK;
+
+public class PlayCtrlSdk
+{
+ #region Fields
+
+#if Win32
+ public const string LibSdkPath = @"./libs/hik/win32/PlayCtrl.dll";
+#elif Win64
+ public const string LibSdkPath = @"./libs/hik/win64/PlayCtrl.dll";
+#elif Linux32
+ public const string LibSdkPath = @"./libs/hik/linux32/libPlayCtrl.so";
+#elif Linux64
+ public const string LibSdkPath = @"./libs/hik/linux64/libPlayCtrl.so";
+#endif
+
+ #endregion Fields
+
+ static PlayCtrlSdk()
+ {
+ }
+
+ #region Sdk Struct
+
+ public struct FRAME_INFO
+ {
+ public int nWidth;
+ public int nHeight;
+ public int nStamp;
+ public int nType;
+ public int nFrameRate;
+ public uint dwFrameNum;
+ }
+
+ #endregion Sdk Struct
+
+ #region Sdk Method
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_GetPort(ref int nPort);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_FreePort(int nPort);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern uint PlayM4_GetLastError(int nPort);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_SetStreamOpenMode(int nPort, uint nMode);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_OpenStream(int nPort, IntPtr pFileHeadBuf, uint nSize, uint nBufPoolSize);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_CloseStream(int nPort);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_SetDisplayBuf(int nPort, uint nNum);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_InputData(int nPort, IntPtr pBuf, uint nSize);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_Play(int nPort, IntPtr hWnd);
+
+ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool PlayM4_Stop(int nPort);
+
+ #endregion Sdk Method
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs b/EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs
new file mode 100644
index 0000000..ea9bbcf
--- /dev/null
+++ b/EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs
@@ -0,0 +1,244 @@
+//#define Win32
+#define Win64
+
+//#define Linux32
+//#define Linux64
+using System.Runtime.InteropServices;
+
+namespace EC.Util.CameraSDK;
+
+public class YuShiOriSdk
+{
+ #region Fields
+
+#if Win32
+ public const string LibSdkPath = @"./libs/yushi/win32/NetDEVSDK.dll";
+#elif Win64
+ public const string LibSdkPath = @"./libs/yushi/win64/NetDEVSDK.dll";
+#elif Linux32
+ public const string LibSdkPath = @"./libs/yushi/linux64/libNetDEVSDK.so";
+#elif Linux64
+ public const string LibSdkPath = @"./libs/yushi/linux64/libNetDEVSDK.so";
+#endif
+
+ private const bool Debug = true;
+
+ #endregion Fields
+
+ static YuShiOriSdk()
+ {
+ GlobalInit();
+ }
+
+ #region Global
+
+ public static bool InitSuccess { get; private set; }
+
+ public static bool GlobalInit()
+ {
+ if (InitSuccess) return true;
+ bool ret = NETDEV_Init();
+ InitSuccess = ret;
+ if (!ret) throw new Exception("YuShiOriSDK global init failure.");
+ return ret;
+ }
+
+ public static bool GlobalDestory()
+ {
+ if (!InitSuccess) return true;
+ bool ret = NETDEV_Cleanup();
+ if (ret) InitSuccess = false;
+ return ret;
+ }
+
+ #endregion Global
+
+ #region Sdk Const
+
+ public const int TRUE = 1;
+ public const int FALSE = 0;
+
+ public const int PtzSpeedMin = 1;
+ public const int PtzSpeedMax = 9;
+
+ /* Common length */
+ public const int NETDEV_LEN_64 = 64;
+ public const int NETDEV_LEN_128 = 128;
+ public const int NETDEV_LEN_132 = 132;
+ public const int NETDEV_LEN_260 = 260;
+
+ /* NETDEV_PTZ_E */
+ public const int IRISCLOSE_STOP = 0x0101; /* Iris close stop */
+ public const int IRISCLOSE = 0x0102; /* Iris close */
+ public const int IRISOPEN_STOP = 0x0103; /* Iris open stop */
+ public const int IRISOPEN = 0x0104; /* Iris open */
+ public const int FOCUSNEAR_STOP = 0x0201; /* Focus near stop */
+ public const int FOCUSNEAR = 0x0202; /* Focus near */
+ public const int FOCUSFAR_STOP = 0x0203; /* Focus far stop */
+ public const int FOCUSFAR = 0x0204; /* Focus far */
+ public const int ZOOMTELE_STOP = 0x0301; /* Zoom in stop */
+ public const int ZOOMTELE = 0x0302; /* Zoom in */
+ public const int ZOOMWIDE_STOP = 0x0303; /* Zoom out stop */
+ public const int ZOOMWIDE = 0x0304; /* Zoom out */
+ public const int TILTUP = 0x0402; /* Tilt up */
+ public const int TILTDOWN = 0x0404; /* Tilt down */
+ public const int PANRIGHT = 0x0502; /* Pan right */
+ public const int PANLEFT = 0x0504; /* Pan left */
+ public const int LEFTUP = 0x0702; /* Move up left */
+ public const int LEFTDOWN = 0x0704; /* Move down left */
+ public const int RIGHTUP = 0x0802; /* Move up right */
+ public const int RIGHTDOWN = 0x0804; /* Move down right */
+ public const int ALLSTOP = 0x0901; /* All-stop command word */
+ public const int FOCUS_AND_IRIS_STOP = 0x0907; /* Focus & Iris-stop command word */
+ public const int MOVE_STOP = 0x0908; /* move stop command word */
+ public const int ZOOM_STOP = 0x0909; /* zoom stop command word */
+ /* NETDEV_PTZ_PRESETCMD_E */
+ public const int PRESET_SET = 0; /* Set preset */
+ public const int PRESET_CLE = 1; /* Clear preset */
+ public const int PRESET_GOTO = 2; /* Go to preset */
+
+ public enum NETDEV_LIVE_STREAM_INDEX_E : int
+ {
+ NETDEV_LIVE_STREAM_INDEX_MAIN = 0, /* Main stream */
+ NETDEV_LIVE_STREAM_INDEX_SUB = 1, /* Sub stream */
+ NETDEV_LIVE_STREAM_INDEX_THIRD = 2, /* Third stream */
+
+ NETDEV_LIVE_STREAM_INDEX_INVALID
+ }
+
+ public enum NETDEV_PROTOCAL_E : int
+ {
+ NETDEV_TRANSPROTOCAL_RTPUDP = 0, /* UDP */
+ NETDEV_TRANSPROTOCAL_RTPTCP = 1 /* TCP */
+ }
+
+ public enum NETDEV_PICTURE_FLUENCY_E
+ {
+ NETDEV_PICTURE_REAL = 0, /* Real-time first */
+ NETDEV_PICTURE_FLUENCY = 1, /* Fluency first */
+ NETDEV_PICTURE_BALANCE_NEW = 3, /* Balance */
+ NETDEV_PICTURE_RTMP_FLUENCY = 4, /* RTMP fluency first */
+
+ NETDEV_PICTURE_FLUENCY_INVALID = 0xff /* Invalid value */
+ }
+
+ #endregion Sdk Const
+
+ #region Sdk Struct
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NETDEV_DEVICE_LOGIN_INFO_S
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NETDEV_LEN_260)]
+ public string szIPAddr; /* IP地址/域名 */
+
+ public int dwPort; /* 端口号 */
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NETDEV_LEN_132)]
+ public string szUserName; /* 用户名 */
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NETDEV_LEN_128)]
+ public string szPassword; /* 密码 */
+
+ public int dwLoginProto; /* 登录协议, 参见NETDEV_LOGIN_PROTO_E */
+ public int dwDeviceType; /* 设备类型, 参见NETDEV_DEVICE_TYPE_E */
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
+ public byte[] byRes; /* Reserved */
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NETDEV_SELOG_INFO_S
+ {
+ public int dwSELogCount;
+ public int dwSELogTime;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
+ public byte[] byRes;
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NETDEV_VIDEO_CHL_DETAIL_INFO_S
+ {
+ public int dwChannelID;
+ public int bPtzSupported; /* Whether ptz is supported */
+ public int enStatus; /* Channel status */
+ public int dwStreamNum; /* Number of streams */
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NETDEV_LEN_64)]
+ public string szChnName; /* Device serial number */
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
+ public byte[] szReserve;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NETDEV_PTZ_STATUS_S
+ {
+ public float fPanTiltX; /* 绝对水平坐标 Absolute horizontal coordinates*/
+ public float fPanTiltY; /* 绝对竖直坐标 Absolute vertical coordinates*/
+ public float fZoomX; /* 绝对聚焦倍数 Absolute multiples*/
+ public int enPanTiltStatus;/* 云台状态 PTZ Status*/
+ public int enZoomStatus; /* 聚焦状态 Focus Status*/
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct NETDEV_PREVIEWINFO_S
+ {
+ public int dwChannelID; /* ID Channel ID */
+ public int dwStreamType; /* #NETDEV_LIVE_STREAM_INDEX_E Stream type, see enumeration #NETDEV_LIVE_STREAM_INDEX_E */
+ public int dwLinkMode; /* #NETDEV_PROTOCAL_E Transport protocol, see enumeration #NETDEV_PROTOCAL_E */
+ public IntPtr hPlayWnd; /* Play window handle */
+ public int dwFluency; /* #NETDEV_PICTURE_FLUENCY_E image play fluency*/
+ public int dwStreamMode; /* #NETDEV_STREAM_MODE_E start stream mode see #NETDEV_STREAM_MODE_E*/
+ public int dwLiveMode; /* #NETDEV_PULL_STREAM_MODE_E Rev. Flow pattern */
+ public int dwDisTributeCloud; /* #NETDEV_DISTRIBUTE_CLOUD_SRV_E distribution */
+ public int dwallowDistribution; /* allow or no distribution*/
+ public int dwTransType; /* 传输类型,参见枚举# NETDEV_TRANS_TYPE_E */
+ public int dwStreamProtocol; /* 起流协议,参见枚举# NETDEV_START_STREAM_PROT_E */
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 236)]
+ public byte[] szReserve; /* Reserved */
+ }
+
+ #endregion Sdk Struct
+
+ #region Common Method
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NETDEV_Init();
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NETDEV_Cleanup();
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NETDEV_GetLastError();
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern IntPtr NETDEV_Login_V30(ref NETDEV_DEVICE_LOGIN_INFO_S pstDevLoginInfo, ref NETDEV_SELOG_INFO_S pstSELogInfo);
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NETDEV_Logout(IntPtr lpUserID);
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NETDEV_QueryVideoChlDetailList(IntPtr lpUserID, ref int pdwChlCount, IntPtr pstVideoChlList);
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern bool NETDEV_PTZGetStatus(IntPtr lpUserID, int dwChannelID, ref NETDEV_PTZ_STATUS_S pstPTZStaus);
+
+ //public boolean NETDEV_GetDevConfig(Pointer lpUserID, int dwChannelID, int dwCommand, Pointer lpOutBuffer, int dwOutBufferSize, IntByReference pdwBytesReturned);
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NETDEV_PTZControl_Other(IntPtr lpUserID, int dwChannelID, int dwPTZCommand, int dwSpeed);
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NETDEV_PTZPreset_Other(IntPtr lpUserID, int dwChannelID, int dwPTZPresetCmd, byte[] szPresetName, int dwPresetID);
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern IntPtr NETDEV_RealPlay(IntPtr lpUserID, ref NETDEV_PREVIEWINFO_S pstPreviewInfo, IntPtr cbPlayDataCallBack, IntPtr lpUserData);
+
+ [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
+ public static extern int NETDEV_StopRealPlay(IntPtr lpRealHandle);
+
+ #endregion Common Method
+}
\ No newline at end of file
diff --git a/EC.Util/CameraSDK/YuShi/YuShiSdk.cs b/EC.Util/CameraSDK/YuShi/YuShiSdk.cs
new file mode 100644
index 0000000..eb7d3d1
--- /dev/null
+++ b/EC.Util/CameraSDK/YuShi/YuShiSdk.cs
@@ -0,0 +1,136 @@
+namespace EC.Util.CameraSDK;
+
+public class YuShiSdk : ICameraSdk
+{
+ #region Fields
+
+ private IntPtr LoginId { get; set; } = IntPtr.Zero;
+
+ private int Channel { get; } = 1;
+
+ #endregion Fields
+
+ public YuShiSdk(CameraInfo cameraInfo) : base(cameraInfo)
+ {
+ }
+
+ #region Base Method
+
+ public override bool Init()
+ {
+ bool ret = ConnectSuccess();
+ if (ret) return true;
+ YuShiOriSdk.NETDEV_DEVICE_LOGIN_INFO_S loginInfo = new();
+ loginInfo.szIPAddr = CameraInfo.Ip;
+ loginInfo.dwPort = CameraInfo.Port;
+ loginInfo.szUserName = CameraInfo.UserName;
+ loginInfo.szPassword = CameraInfo.Password;
+ YuShiOriSdk.NETDEV_SELOG_INFO_S logInfo = new();
+ LoginId = YuShiOriSdk.NETDEV_Login_V30(ref loginInfo, ref logInfo);
+ ret = ConnectSuccess();
+ if(!ret) BuildException();
+ return ret;
+ }
+
+ public override bool Destory()
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) return true;
+ ret = YuShiOriSdk.NETDEV_Logout(LoginId);
+ if (ret) LoginId = IntPtr.Zero;
+ else BuildException();
+ return ret;
+ }
+
+ public override bool ConnectSuccess()
+ {
+ return LoginId != IntPtr.Zero;
+ }
+
+ internal override void BuildException()
+ {
+ int errCode = YuShiOriSdk.NETDEV_GetLastError();
+ if (errCode == 0) return;
+ throw CameraException.New(CameraInfo, errCode);
+ }
+
+ #endregion Base Method
+
+ #region Ptz Method
+
+ public override PtzInfo GetPtzInfo()
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) return PtzInfo.Default;
+ YuShiOriSdk.NETDEV_PTZ_STATUS_S entity = new();
+ ret = YuShiOriSdk.NETDEV_PTZGetStatus(LoginId, Channel, ref entity);
+ if (!ret) { BuildException(); return PtzInfo.Default; }
+ return PtzInfo.New(entity.fPanTiltX, entity.fPanTiltY, entity.fZoomX);
+ }
+
+ public override bool TryGetPtzInfo(out PtzInfo ptzInfo)
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) { ptzInfo = PtzInfo.Default; return false; }
+ YuShiOriSdk.NETDEV_PTZ_STATUS_S entity = new();
+ ret = YuShiOriSdk.NETDEV_PTZGetStatus(LoginId, Channel, ref entity);
+ if (!ret) { BuildException(); ptzInfo = PtzInfo.Default; return false; }
+ ptzInfo = PtzInfo.New(entity.fPanTiltX, entity.fPanTiltY, entity.fZoomX);
+ return true;
+ }
+
+ public override bool PtzMove(int cmd, int stop, int speed)
+ {
+ if (!ConnectSuccess()) return false;
+ if (stop == 1) cmd = YuShiOriSdk.ALLSTOP;
+ int retNum = YuShiOriSdk.NETDEV_PTZControl_Other(LoginId, Channel, cmd, speed);
+ bool ret = retNum == YuShiOriSdk.TRUE;
+ if (!ret) BuildException();
+ return ret;
+ }
+
+ public override bool PtzPreset(int cmd, int presetId)
+ {
+ if (!ConnectSuccess()) return false;
+ int retNum = YuShiOriSdk.NETDEV_PTZPreset_Other(LoginId, Channel, cmd, Array.Empty(), presetId);
+ bool ret = retNum == YuShiOriSdk.TRUE;
+ if (!ret) BuildException();
+ return ret;
+ }
+
+ #endregion Ptz Method
+
+ #region Video Method
+
+ private IntPtr RealplayHandle { get; set; } = IntPtr.Zero;
+
+ public override void StartPlay(IntPtr hwnd)
+ {
+ if (!ConnectSuccess() || IsPlaying()) return;
+ YuShiOriSdk.NETDEV_PREVIEWINFO_S stPreviewInfo = new()
+ {
+ hPlayWnd = hwnd,
+ dwChannelID = Channel,
+ dwStreamType = 0, // YuShiOriSDK.NETDEV_LIVE_STREAM_INDEX_E
+ dwLinkMode = 1, // YuShiOriSDK.NETDEV_PROTOCAL_E
+ dwFluency = 0, // YuShiOriSDK.NETDEV_PICTURE_FLUENCY_E
+ };
+ RealplayHandle = YuShiOriSdk.NETDEV_RealPlay(LoginId, ref stPreviewInfo, IntPtr.Zero, IntPtr.Zero);
+ if (RealplayHandle == IntPtr.Zero) BuildException();
+ }
+
+ public override void StopPlay()
+ {
+ if (!IsPlaying()) return;
+ int ret = YuShiOriSdk.NETDEV_StopRealPlay(RealplayHandle);
+ RealplayHandle = IntPtr.Zero;
+ if (ret == YuShiOriSdk.FALSE) BuildException();
+ }
+
+ public override bool IsPlaying()
+ {
+ return RealplayHandle != IntPtr.Zero;
+ }
+
+ #endregion Video Method
+}
\ No newline at end of file
diff --git a/EC.Util/Common/JsonUtil.cs b/EC.Util/Common/JsonUtil.cs
new file mode 100644
index 0000000..a04587b
--- /dev/null
+++ b/EC.Util/Common/JsonUtil.cs
@@ -0,0 +1,43 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+
+namespace EC.Util.Common;
+
+public static class JsonUtil
+{
+ public static object ToJson(string Json)
+ {
+ object? obj = JsonConvert.DeserializeObject(Json);
+ return obj ?? new();
+ }
+
+ public static string ToJson(object obj)
+ {
+ IsoDateTimeConverter timeConverter = new() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
+ return JsonConvert.SerializeObject(obj, timeConverter);
+ }
+
+ public static string ToJson(object obj, string datetimeformats)
+ {
+ IsoDateTimeConverter timeConverter = new() { DateTimeFormat = datetimeformats };
+ return JsonConvert.SerializeObject(obj, timeConverter);
+ }
+
+ public static T ToObject(string Json)
+ {
+ T? obj = JsonConvert.DeserializeObject(Json);
+ return obj ?? Activator.CreateInstance();
+ }
+
+ public static List ToList(string Json)
+ {
+ List? list = JsonConvert.DeserializeObject>(Json);
+ return list ?? new List();
+ }
+
+ public static JObject ToJObject(string Json)
+ {
+ return Json != null ? JObject.Parse(Json.Replace(" ", "")) : JObject.Parse("{}");
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/Common/LogUnit.cs b/EC.Util/Common/LogUnit.cs
new file mode 100644
index 0000000..f31b657
--- /dev/null
+++ b/EC.Util/Common/LogUnit.cs
@@ -0,0 +1,40 @@
+using log4net;
+using log4net.Config;
+
+namespace EC.Util.Common;
+
+public class LogUnit
+{
+ private static readonly ILog logger;
+
+ static LogUnit()
+ {
+ string fileName = Path.Combine("config", "log4net.config");
+ if (!File.Exists(fileName)) throw new FileNotFoundException(fileName);
+ XmlConfigurator.Configure(new FileInfo(fileName));
+ logger = LogManager.GetLogger(typeof(LogUnit));
+ }
+
+ public static void Init()
+ { }
+
+ public static void Debug(string msg) => logger.Debug(msg);
+
+ public static void Info(string msg) => logger.Info(msg);
+
+ public static void Warn(string msg) => logger.Warn(msg);
+
+ public static void Fatal(string msg) => logger.Fatal(msg);
+
+ public static void Error(string msg) => logger.Error(msg);
+
+ public static void Error(Exception e) => logger.Error(e);
+
+ public static void Error(object obj, string msg) => logger.Error(obj.GetType(), new Exception(msg));
+
+ public static void Error(object obj, Exception e) => logger.Error(obj.GetType(), e);
+
+ public static void Error(Type type, string msg) => logger.Error(type, new Exception(msg));
+
+ public static void Error(Type type, Exception e) => logger.Error(type, e);
+}
\ No newline at end of file
diff --git a/EC.Util/Common/NetworkUtil.cs b/EC.Util/Common/NetworkUtil.cs
new file mode 100644
index 0000000..b0caad0
--- /dev/null
+++ b/EC.Util/Common/NetworkUtil.cs
@@ -0,0 +1,36 @@
+using System.Net.Sockets;
+
+namespace EC.Util.Common;
+
+public static class NetworkUtil
+{
+ public static string LocalAddr(this TcpClient client)
+ {
+ return client.Client.LocalEndPoint?.ToString() ?? ":";
+ }
+
+ public static string LocalIp(this TcpClient client)
+ {
+ return LocalAddr(client).Split(':')[0];
+ }
+
+ public static string LocalPort(this TcpClient client)
+ {
+ return LocalAddr(client).Split(':')[1];
+ }
+
+ public static string ClientAddr(this TcpClient client)
+ {
+ return client.Client.RemoteEndPoint?.ToString() ?? ":";
+ }
+
+ public static string ClientIp(this TcpClient client)
+ {
+ return ClientAddr(client).Split(':')[0];
+ }
+
+ public static string ClientPort(this TcpClient client)
+ {
+ return ClientAddr(client).Split(':')[1];
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/Common/TaskUtil.cs b/EC.Util/Common/TaskUtil.cs
new file mode 100644
index 0000000..c9f9ed8
--- /dev/null
+++ b/EC.Util/Common/TaskUtil.cs
@@ -0,0 +1,40 @@
+namespace EC.Util.Common;
+
+public class TaskUtil
+{
+ public static Task Run(Action action)
+ {
+ Task.Run(action);
+ return Task.Run(action);
+ }
+
+ ///
+ /// 相比于 Run,直接捕获并记录异常
+ ///
+ ///
+ ///
+ public static Task RunCatch(Action action)
+ {
+ return Task.Run(() =>
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception e)
+ {
+ LogUnit.Error(typeof(TaskUtil), e);
+ }
+ });
+ }
+
+ ///
+ /// 相比于 Run,能及时抛出并全局捕获异常
+ ///
+ ///
+ ///
+ public static Task LongRun(Action action)
+ {
+ return Task.Factory.StartNew(action, TaskCreationOptions.LongRunning);
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/Common/VerifyUtil.cs b/EC.Util/Common/VerifyUtil.cs
new file mode 100644
index 0000000..d575597
--- /dev/null
+++ b/EC.Util/Common/VerifyUtil.cs
@@ -0,0 +1,176 @@
+using System.Text.RegularExpressions;
+
+namespace EC.Util.Common;
+
+///
+/// 验证工具类
+///
+public class VerifyUtil
+{
+ ///
+ /// 验证是否为空
+ ///
+ ///
+ ///
+ public static bool IsEmpty(string str)
+ {
+ return string.IsNullOrEmpty(str);
+ }
+
+ ///
+ /// 验证是否为邮件
+ ///
+ ///
+ ///
+ public static bool IsEmail(string emailStr)
+ {
+ if (string.IsNullOrEmpty(emailStr)) return false;
+ string match = @"^([a-zA-Z0-9]+[_|\-|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\-|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$";
+ Regex r = new(match, RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ return r.IsMatch(emailStr.Trim());
+ }
+
+ ///
+ /// 验证是否为数字
+ ///
+ ///
+ ///
+ public static bool IsNumber(string numberStr)
+ {
+ if (string.IsNullOrEmpty(numberStr)) return false;
+ string match = @"^(0|[1-9][0-9]*)$";
+ Regex r = new(match, RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ return r.IsMatch(numberStr.Trim());
+ }
+
+ ///
+ /// 验证是否为浮点数
+ ///
+ ///
+ ///
+ public static bool IsDecimal(string decimalStr)
+ {
+ if (string.IsNullOrEmpty(decimalStr)) return false;
+ string match = @"^(-?\d+)(\.\d+)?$";
+ Regex r = new(match, RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ return r.IsMatch(decimalStr.Trim());
+ }
+
+ ///
+ /// 验证是否为手机号码
+ ///
+ ///
+ ///
+ public static bool IsMobile(string mobileStr)
+ {
+ if (string.IsNullOrEmpty(mobileStr)) return false;
+ string match = @"^[1]+[3,5,7,8]+\d{9}";
+ Regex r = new(match, RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ return r.IsMatch(mobileStr.Trim());
+ }
+
+ ///
+ /// 验证是否为电话号码
+ ///
+ ///
+ ///
+ public static bool IsTel(string telStr)
+ {
+ if (string.IsNullOrEmpty(telStr)) return false;
+ string match = @"^(\+86\s{1,1})?((\d{3,4}\-)\d{7,8})$";
+ Regex r = new(match, RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ return r.IsMatch(telStr.Trim());
+ }
+
+ ///
+ /// 验证是否为Ip地址
+ ///
+ ///
+ ///
+ public static bool IsIp(string ipStr)
+ {
+ if (string.IsNullOrEmpty(ipStr)) return false;
+ string match = @"^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$";
+ Regex r = new(match);
+ return r.IsMatch(ipStr.Trim());
+ }
+
+ ///
+ /// 验证是否为身份证
+ ///
+ /// 身份证号
+ ///
+ public static bool CheckIDCard(string idcardStr)
+ {
+ if (string.IsNullOrEmpty(idcardStr)) return false;
+ if (idcardStr.Length == 18) return CheckIDCard18(idcardStr);
+ else if (idcardStr.Length == 15) return CheckIDCard15(idcardStr);
+ return false;
+ }
+
+ ///
+ /// 18位身份证验证
+ ///
+ /// 身份证号
+ ///
+ private static bool CheckIDCard18(string Id)
+ {
+ long n = 0;
+ if (long.TryParse(Id.Remove(17), out n) == false || n < Math.Pow(10, 16) || long.TryParse(Id.Replace('x', '0').Replace('X', '0'), out n) == false)
+ {
+ return false;//数字验证
+ }
+ string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
+ if (address.IndexOf(Id.Remove(2)) == -1)
+ {
+ return false;//省份验证
+ }
+ string birth = Id.Substring(6, 8).Insert(6, "-").Insert(4, "-");
+ DateTime time = new DateTime();
+ if (DateTime.TryParse(birth, out time) == false)
+ {
+ return false;//生日验证
+ }
+ string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
+ string[] Wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(',');
+ char[] Ai = Id.Remove(17).ToCharArray();
+ int sum = 0;
+ for (int i = 0; i < 17; i++)
+ {
+ sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString());
+ }
+ int y = -1;
+ Math.DivRem(sum, 11, out y);
+ if (arrVarifyCode[y] != Id.Substring(17, 1).ToLower())
+ {
+ return false;//校验码验证
+ }
+ return true;//符合GB11643-1999标准
+ }
+
+ ///
+ /// 15位身份证验证
+ ///
+ /// 身份证号
+ ///
+ private static bool CheckIDCard15(string Id)
+ {
+ long n = 0;
+ if (long.TryParse(Id, out n) == false || n < Math.Pow(10, 14))
+ {
+ return false;//数字验证
+ }
+ string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
+ if (address.IndexOf(Id.Remove(2)) == -1)
+ {
+ return false;//省份验证
+ }
+ string birth = Id.Substring(6, 6).Insert(4, "-").Insert(2, "-");
+ DateTime time = new DateTime();
+ if (DateTime.TryParse(birth, out time) == false)
+ {
+ return false;//生日验证
+ }
+ return true;//符合15位身份证标准
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/EC.Util.csproj b/EC.Util/EC.Util.csproj
new file mode 100644
index 0000000..ae9e75f
--- /dev/null
+++ b/EC.Util/EC.Util.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
diff --git a/EC.Util/Port/SerialPortBuilder.cs b/EC.Util/Port/SerialPortBuilder.cs
new file mode 100644
index 0000000..db3dda6
--- /dev/null
+++ b/EC.Util/Port/SerialPortBuilder.cs
@@ -0,0 +1,60 @@
+namespace EC.Util.Port;
+
+///
+/// 串口构建类
+///
+public class SerialPortBuilder
+{
+ ///
+ ///
+ ///
+ ///
+ public static YcSerialPort CreateSerialPort(SerialPortParam param)
+ {
+ return new YcSerialPort(param);
+ }
+
+ ///
+ ///
+ ///
+ public static YcSerialPort CreateSerialPort(string comName)
+ {
+ var param = new SerialPortParam
+ {
+ ComName = comName
+ };
+ return new YcSerialPort(param);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static YcSerialPort CreateSerialPort(string comName, int baudRate)
+ {
+ var param = new SerialPortParam
+ {
+ ComName = comName,
+ BaudRate = baudRate
+ };
+ return new YcSerialPort(param);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static YcSerialPort CreateSerialPort(string comName, int baudRate, int receivedBytesThreshold)
+ {
+ var param = new SerialPortParam
+ {
+ ComName = comName,
+ BaudRate = baudRate,
+ ReceivedBytesThreshold = receivedBytesThreshold
+ };
+ return new YcSerialPort(param);
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/Port/SerialPortParam.cs b/EC.Util/Port/SerialPortParam.cs
new file mode 100644
index 0000000..d386c80
--- /dev/null
+++ b/EC.Util/Port/SerialPortParam.cs
@@ -0,0 +1,56 @@
+namespace EC.Util.Port;
+
+///
+/// 串口参数类
+///
+public class SerialPortParam
+{
+ #region Fields
+
+ ///
+ /// 串口名称
+ ///
+ public string ComName { get; set; }
+
+ ///
+ ///波特率
+ ///
+ public int BaudRate { get; set; }
+
+ ///
+ /// 奇偶校验0-4=no,odd,even,mark,space
+ ///
+ public int Parity { get; set; }
+
+ ///
+ /// 校验位对应值
+ ///
+ public int ParityValue { get; set; }
+
+ ///
+ ///数据位 默认
+ ///
+ public int DataBits { get; set; }
+
+ ///
+ /// 停止位
+ ///
+ public int StopBits { get; set; }
+
+ ///
+ /// ?Byte时,触发一次事件。设置为1时,易造成线程死锁
+ ///
+ public int ReceivedBytesThreshold { get; set; }
+
+ #endregion Fields
+
+ public SerialPortParam()
+ {
+ ComName = string.Empty;
+ BaudRate = 9600;
+ Parity = 0;
+ DataBits = 8;
+ StopBits = 1;
+ ReceivedBytesThreshold = 128;
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/Port/YcSerialPort.cs b/EC.Util/Port/YcSerialPort.cs
new file mode 100644
index 0000000..1195d59
--- /dev/null
+++ b/EC.Util/Port/YcSerialPort.cs
@@ -0,0 +1,166 @@
+using System.IO.Ports;
+using System.Text.RegularExpressions;
+
+namespace EC.Util.Port;
+
+///
+/// 串口类
+///
+public class YcSerialPort
+{
+ private SerialPort _port;//声明一个串口
+ private readonly SerialPortParam _param;
+
+ public long RecCount { get; set; }//接收计数
+
+ public long SendCount { get; set; }//发送计数
+
+ public YcSerialPort()
+ {
+ }
+
+ public YcSerialPort(SerialPortParam param)
+ {
+ _param = param;
+ }
+
+ ///
+ /// 打开串口
+ ///
+ ///
+ public bool OpenCom()
+ {
+ if (IsOpen())
+ CloseCom();
+ if (string.IsNullOrEmpty(_param.ComName))
+ throw new Exception("请插入串口设备!");
+
+ _port = new SerialPort
+ {
+ PortName = _param.ComName,//串口名
+ BaudRate = _param.BaudRate,//波特率
+ DataBits = _param.DataBits,//数据位
+ Parity = (Parity)_param.Parity,//奇偶校验0-4=no,odd,even,mark,space
+ StopBits = (StopBits)Convert.ToInt32(_param.StopBits),//停止位
+ ReadTimeout = 500,//读超时
+ WriteTimeout = 2000,//写超时
+ //ReadBufferSize = 1024,
+ //WriteBufferSize = 1024,
+ ReceivedBytesThreshold = _param.ReceivedBytesThreshold,//128Byte时触发一次事件,设置为1时易造成线程死锁。
+ RtsEnable = true,
+ DtrEnable = true,
+ };
+ _port.DataReceived += RecData;
+ //port.DataReceived += new SerialDataReceivedEventHandler(Comm_DataReceived);
+ _port.Open();
+ return IsOpen();
+ }
+
+ ///
+ /// 关闭串口
+ ///
+ public bool CloseCom()
+ {
+ if (IsOpen())
+ {
+ _port.Close();
+ _port = null;
+ }
+ return !IsOpen();
+ }
+
+ public bool IsOpen()
+ {
+ if (_port == null)
+ return false;
+ if (!_port.IsOpen)
+ return false;
+ return true;
+ }
+
+ public event EventHandler OnRecData; //定义一个委托类型的事件
+
+ private void RecData(object sender, EventArgs e)
+ {
+ var count = _port.BytesToRead;//获取缓存区字节数
+ var buf = new byte[count];//存储串口数据用
+ _port.Read(buf, 0, count);//读取缓冲数据
+ RecCount += count;//接收计数
+ OnRecData?.Invoke(this, buf);
+ }
+
+ ///
+ /// 发送 Ascii
+ ///
+ ///
+ ///
+ public void SendAscii(string text, bool newLine = false)
+ {
+ //定义一个变量,记录发送了几个字节
+ int count;
+
+ if (newLine)
+ {
+ _port.Write(text + "\r\n");
+ count = text.Length + 2;
+ }
+ else//不包含换行符
+ {
+ _port.Write(text);
+ count = text.Length;
+ }
+
+ SendCount += count;//累加发送字节数
+ }
+
+ ///
+ /// 发送 Hex
+ ///
+ ///
+ ///
+ public void SendHex(string text, bool newLine = false)
+ {
+ int count;//定义一个变量,记录发送了几个字节
+ var mc = Regex.Matches(text, @"(?i)[\da-f]{2}");//正则得到有效的十六进制数(?i)对大小写不敏感,[/da-f],{2}连续出现2次
+ var buf = new List();//填充到这个临时列表中
+ foreach (Match m in mc)
+ {
+ buf.Add(byte.Parse(m.Value, System.Globalization.NumberStyles.HexNumber));//依次添加到列表中
+ }
+ _port.Write(buf.ToArray(), 0, buf.Count);//转换列表为数组后发送
+ if (newLine)
+ {
+ _port.Write("\r\n");
+ count = buf.Count + 2;
+ }
+ else//不包含换行符
+ {
+ count = buf.Count;
+ }
+
+ SendCount += count;//累加发送字节数
+ }
+
+ ///
+ /// 发送 Hex
+ ///
+ ///
+ ///
+ public void SendHex(byte[] buf, bool newline = false)
+ {
+ int count;//定义一个变量,记录发送了几个字节
+ var bufLen = buf.Count();
+ _port.Write(buf, 0, bufLen);
+ if (newline)
+ { //记录发送的字节数
+ _port.WriteLine("\n");
+ count = bufLen + 2;
+ }
+ else
+ {
+ count = bufLen;
+ }
+
+ SendCount += count;//累加发送字节数
+ }
+}
\ No newline at end of file
diff --git a/EC.Util/config/log4net.config b/EC.Util/config/log4net.config
new file mode 100644
index 0000000..9b7fbb3
--- /dev/null
+++ b/EC.Util/config/log4net.config
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JiLinApp/JiLinApp.csproj b/JiLinApp/JiLinApp.csproj
index b924979..473b4ed 100644
--- a/JiLinApp/JiLinApp.csproj
+++ b/JiLinApp/JiLinApp.csproj
@@ -23,4 +23,9 @@
+
+
+
+
+
diff --git a/JiLinApp/Program.cs b/JiLinApp/Program.cs
index b4744d0..1b6e6c6 100644
--- a/JiLinApp/Program.cs
+++ b/JiLinApp/Program.cs
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Media;
using Avalonia.ReactiveUI;
+using EC.Util.Common;
using JiLinApp.Core.Avalonia;
using ReactiveUI;
using System;
@@ -22,10 +23,9 @@ internal class Program
AppBuilder appBuilder = BuildAvaloniaApp();
appBuilder.StartWithClassicDesktopLifetime(args);
}
- catch (Exception)
+ catch (Exception e)
{
- //The global try-catch
- throw;
+ LogUnit.Error(e);
}
}
@@ -54,18 +54,29 @@ internal class Program
private static void GlobalInit()
{
+ // Active to initialize
+ LogUnit.Init();
+
// HandleExceptions
// Exceptions from another thread
+ AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
+ {
+ if (e.ExceptionObject is Exception ex) LogUnit.Error(ex);
+ };
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
e.SetObserved();
+ LogUnit.Error(e.Exception);
};
// Exceptions from Reactive UI
- RxAppExceptionHandler.Instance.OnExceptionHandler += e =>
- {
- };
+ RxAppExceptionHandler.Instance.OnExceptionHandler += LogUnit.Error;
RxApp.DefaultExceptionHandler = RxAppExceptionHandler.Instance;
}
+ private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ throw new NotImplementedException();
+ }
+
#endregion extend
}
\ No newline at end of file
diff --git a/JiLinCpApp.sln b/JiLinCpApp.sln
index 6831c9c..ce259ec 100644
--- a/JiLinCpApp.sln
+++ b/JiLinCpApp.sln
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiLinApp", "JiLinApp\JiLinApp.csproj", "{9D1063BD-2C84-4FF3-90CF-B16159B3E803}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC.Util", "EC.Util\EC.Util.csproj", "{B5D7ED30-805C-4A5D-BD41-D2F910806340}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +17,10 @@ Global
{9D1063BD-2C84-4FF3-90CF-B16159B3E803}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D1063BD-2C84-4FF3-90CF-B16159B3E803}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D1063BD-2C84-4FF3-90CF-B16159B3E803}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B5D7ED30-805C-4A5D-BD41-D2F910806340}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5D7ED30-805C-4A5D-BD41-D2F910806340}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5D7ED30-805C-4A5D-BD41-D2F910806340}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5D7ED30-805C-4A5D-BD41-D2F910806340}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE