diff --git a/EC.Util/CameraSDK/Common/CameraException.cs b/EC.Util/CameraSDK/Common/CameraException.cs
new file mode 100644
index 0000000..b65cfec
--- /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 CameraManufactor Manufactor { get; set; }
+
+ public int Code { get; set; }
+
+ public string? Msg { get; set; }
+
+ public override string? ToString()
+ {
+ StringBuilder builder = new();
+ builder.Append($"Manufactor:{Manufactor}, Code:{Code}");
+ if (!string.IsNullOrEmpty(Msg)) builder.Append($", Msg:{Msg}");
+ return builder.ToString();
+ }
+ }
+
+ public static CameraException New(CameraManufactor manufactor, int code)
+ {
+ CameraExceptionObj obj = new()
+ {
+ Manufactor = manufactor,
+ Code = code
+ };
+ return new CameraException(obj.ToString());
+ }
+
+ public static CameraException New(CameraManufactor manufactor, int code, string msg)
+ {
+ CameraExceptionObj obj = new()
+ {
+ Manufactor = manufactor,
+ Code = code,
+ Msg = msg
+ };
+ return new CameraException(obj.ToString());
+ }
+}
\ 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..40a9ce9
--- /dev/null
+++ b/EC.Util/CameraSDK/Common/CameraStruct.cs
@@ -0,0 +1,111 @@
+namespace EC.Util.CameraSDK;
+
+///
+/// 相机信息
+///
+public class CameraInfo
+{
+ #region Attr
+
+ ///
+ /// 相机厂商
+ ///
+ public int 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 Attr
+
+ public static CameraInfo New(int 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(int manufactor, string ip, string userName, string password)
+ {
+ CameraInfo info = new() { Manufactor = manufactor, Ip = ip, UserName = userName, Password = password };
+ int port = (CameraManufactor)manufactor switch
+ {
+ CameraManufactor.HiK => (int)CameraPort.HiK,
+ CameraManufactor.DaHua => (int)CameraPort.DaHua,
+ CameraManufactor.YuShi => (int)CameraPort.YuShi,
+ _ => -1,
+ };
+ info.Port = port;
+ if (port <= 0)
+ throw new Exception("Camera manufactor not support.");
+ return info;
+ }
+}
+
+///
+/// 相机厂商
+///
+public enum CameraManufactor : int
+{
+ HiK = 1,
+ DaHua,
+ YuShi,
+}
+
+///
+/// 相机默认连接端口
+///
+public enum CameraPort : int
+{
+ HiK = 8000,
+ DaHua = 37777,
+ YuShi = 8800,
+}
+
+///
+/// Ptz 信息
+///
+public class PtzInfo
+{
+ #region Attr
+
+ public double Pan { get; set; }
+ public double Tilt { get; set; }
+ public double Zoom { get; set; }
+
+ #endregion Attr
+
+ 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);
+ }
+}
diff --git a/EC.Util/CameraSDK/Common/ICameraSDK.cs b/EC.Util/CameraSDK/Common/ICameraSDK.cs
new file mode 100644
index 0000000..90f2370
--- /dev/null
+++ b/EC.Util/CameraSDK/Common/ICameraSDK.cs
@@ -0,0 +1,58 @@
+namespace EC.Util.CameraSDK;
+
+public abstract class ICameraSDK
+{
+ #region Attr
+
+ protected CameraInfo CameraInfo { get; set; }
+
+ #endregion Attr
+
+ public ICameraSDK(CameraInfo cameraInfo)
+ {
+ CameraInfo = cameraInfo;
+ }
+
+ #region Base Method
+
+ ///
+ /// 初始化资源
+ ///
+ ///
+ public abstract bool Init();
+
+ ///
+ /// 释放资源
+ ///
+ ///
+ public abstract bool Destory();
+
+ ///
+ /// 连接是否成功
+ ///
+ ///
+ public abstract bool ConnectSuccess();
+
+ ///
+ /// 构建异常
+ ///
+ public abstract void BuildException();
+
+ #endregion Base Method
+
+ #region Main Method
+
+ ///
+ /// 获取 ptz
+ ///
+ ///
+ public abstract PtzInfo GetPtzInfo();
+
+ ///
+ /// 获取 ptz
+ ///
+ ///
+ public abstract bool TryGetPtzInfo(out PtzInfo ptzInfo);
+
+ #endregion Main 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..47dbb2f
--- /dev/null
+++ b/EC.Util/CameraSDK/HiK/HiKOriSDK.cs
@@ -0,0 +1,245 @@
+using System.Runtime.InteropServices;
+
+namespace EC.Util.CameraSDK;
+
+public static class HiKOriSDK
+{
+ #region Lib Attr
+
+ public const string LibHcNetSDK = @"./libs/hik-win64/HCNetSDK.dll";
+
+ #endregion Lib Attr
+
+ 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.");
+ 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; //序列号长度
+
+ #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
+
+ #region PtzMove
+
+ 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()参数结构
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ public struct NET_DVR_DEVICEINFO
+ {
+ [MarshalAsAttribute(
+ 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()参数结构
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ public struct NET_DVR_DEVICEINFO_V30
+ {
+ [MarshalAsAttribute(
+ 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表示支持英文
+ [MarshalAsAttribute(
+ UnmanagedType.ByValArray,
+ SizeConst = 9,
+ ArraySubType = UnmanagedType.I1
+ )]
+ public byte[] byRes2; //保留
+ }
+
+ //球机位置信息
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ public struct NET_DVR_PTZPOS
+ {
+ public ushort wAction; //获取时该字段无效
+ public ushort wPanPos; //水平参数
+ public ushort wTiltPos; //垂直参数
+ public ushort wZoomPos; //变倍参数
+ }
+
+ //球机范围信息
+ [StructLayoutAttribute(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
+ }
+
+ #endregion SDK Struct
+
+ #region Common Method
+
+ [DllImport(LibHcNetSDK)]
+ public static extern bool NET_DVR_Init();
+
+ [DllImport(LibHcNetSDK)]
+ public static extern bool NET_DVR_Cleanup();
+
+ [DllImport(LibHcNetSDK)]
+ public static extern uint NET_DVR_GetLastError();
+
+ [DllImport(LibHcNetSDK)]
+ public static extern int NET_DVR_Login_V30(
+ string sDVRIP,
+ int wDVRPort,
+ string sUserName,
+ string sPassword,
+ ref NET_DVR_DEVICEINFO_V30 lpDeviceInfo
+ );
+
+ [DllImport(LibHcNetSDK)]
+ public static extern bool NET_DVR_Logout(int iUserID);
+
+ //参数配置 begin
+ [DllImport(LibHcNetSDK)]
+ public static extern bool NET_DVR_GetDVRConfig(
+ int lUserID,
+ uint dwCommand,
+ int lChannel,
+ IntPtr lpOutBuffer,
+ uint dwOutBufferSize,
+ ref uint lpBytesReturned
+ );
+
+ [DllImport(LibHcNetSDK)]
+ public static extern bool NET_DVR_PTZControlWithSpeed_Other(
+ int lUserID,
+ int lChannel,
+ uint dwPTZCommand,
+ uint dwStop,
+ uint dwSpeed
+ );
+
+ [DllImport(LibHcNetSDK)]
+ public static extern bool NET_DVR_PTZPreset_Other(
+ int lUserID,
+ int lChannel,
+ uint dwPTZPresetCmd,
+ uint dwPresetIndex
+ );
+
+ #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..a90a01a
--- /dev/null
+++ b/EC.Util/CameraSDK/HiK/HiKSDK.cs
@@ -0,0 +1,143 @@
+using System.Runtime.InteropServices;
+
+namespace EC.Util.CameraSDK;
+
+public class HiKSDK : ICameraSDK
+{
+ #region Attr
+
+ private int LoginId { get; set; } = -1;
+
+ #endregion Attr
+
+ public HiKSDK(CameraInfo cameraInfo) : base(cameraInfo)
+ {
+ }
+
+ #region Base Method
+
+ public override bool Init()
+ {
+ bool ret = ConnectSuccess();
+ if (ret) return true;
+
+ HiKOriSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new();
+ LoginId = HiKOriSDK.NET_DVR_Login_V30(CameraInfo.Ip, CameraInfo.Port, CameraInfo.UserName, CameraInfo.Password, ref deviceInfo);
+ ret = ConnectSuccess();
+
+ return ret;
+ }
+
+ public override bool Destory()
+ {
+ bool ret = ConnectSuccess();
+ if (!ret) return true;
+
+ ret = HiKOriSDK.NET_DVR_Logout(LoginId);
+ if (ret) LoginId = -1;
+
+ return ret;
+ }
+
+ public override bool ConnectSuccess()
+ {
+ return LoginId >= 0;
+ }
+
+ public override void BuildException()
+ {
+ uint errCode = HiKOriSDK.NET_DVR_GetLastError();
+ if (errCode == 0) return;
+ throw CameraException.New(CameraManufactor.HiK, (int)errCode);
+ }
+
+ #endregion Base Method
+
+ #region Main 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);
+ }
+ }
+
+ #endregion Main Method
+
+ #region Ptz Method
+
+ public void PtzMove(uint[] args)
+ {
+ bool ret = ConnectSuccess();
+ if (!ret || args == null || args.Length < 3) return;
+ int lChannel = 1;
+ HiKOriSDK.NET_DVR_PTZControlWithSpeed_Other(LoginId, lChannel, args[0], args[1], args[2]);
+ }
+
+ public void PtzPreset(uint[] args)
+ {
+ bool ret = ConnectSuccess();
+ if (!ret || args == null || args.Length < 2) return;
+ int lChannel = 1;
+ HiKOriSDK.NET_DVR_PTZPreset_Other(LoginId, lChannel, args[0], args[1]);
+ }
+
+ #endregion Ptz 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/SystemUtil.cs b/EC.Util/Common/SystemUtil.cs
new file mode 100644
index 0000000..900b312
--- /dev/null
+++ b/EC.Util/Common/SystemUtil.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace EC.Util.Common;
+
+public static class SystemUtil
+{
+ [DllImport("Kernel32")]
+ public static extern void AllocConsole();
+
+ [DllImport("Kernel32")]
+ public static extern void FreeConsole();
+}
\ 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..50886e3
--- /dev/null
+++ b/EC.Util/EC.Util.csproj
@@ -0,0 +1,45 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ 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..8f129f9
--- /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/Zmq/ZmqUtil.cs b/EC.Util/Zmq/ZmqUtil.cs
new file mode 100644
index 0000000..2335201
--- /dev/null
+++ b/EC.Util/Zmq/ZmqUtil.cs
@@ -0,0 +1,11 @@
+namespace EC.Util.Zmq;
+
+internal class ZmqUtil
+{
+ //#region fields
+
+ //private SubscriberSocket SubSocket { get; set; }
+ //private PublisherSocket PubSocket { get; set; }
+
+ //#endregion fields
+}
\ No newline at end of file
diff --git a/EC.Util/libs/hik-win64/HCCore.dll b/EC.Util/libs/hik-win64/HCCore.dll
new file mode 100644
index 0000000..266afa3
Binary files /dev/null and b/EC.Util/libs/hik-win64/HCCore.dll differ
diff --git a/EC.Util/libs/hik-win64/HCNetSDK.dll b/EC.Util/libs/hik-win64/HCNetSDK.dll
new file mode 100644
index 0000000..0c4ef60
Binary files /dev/null and b/EC.Util/libs/hik-win64/HCNetSDK.dll differ
diff --git a/EC.Util/libs/hik-win64/HCNetSDKCom/HCCoreDevCfg.dll b/EC.Util/libs/hik-win64/HCNetSDKCom/HCCoreDevCfg.dll
new file mode 100644
index 0000000..63a8836
Binary files /dev/null and b/EC.Util/libs/hik-win64/HCNetSDKCom/HCCoreDevCfg.dll differ
diff --git a/EC.Util/libs/hik-win64/HCNetSDKCom/HCPreview.dll b/EC.Util/libs/hik-win64/HCNetSDKCom/HCPreview.dll
new file mode 100644
index 0000000..92a3cf9
Binary files /dev/null and b/EC.Util/libs/hik-win64/HCNetSDKCom/HCPreview.dll differ
diff --git a/EC.Util/libs/hik-win64/libcrypto-1_1-x64.dll b/EC.Util/libs/hik-win64/libcrypto-1_1-x64.dll
new file mode 100644
index 0000000..6731338
Binary files /dev/null and b/EC.Util/libs/hik-win64/libcrypto-1_1-x64.dll differ
diff --git a/EC.Util/libs/hik-win64/libssl-1_1-x64.dll b/EC.Util/libs/hik-win64/libssl-1_1-x64.dll
new file mode 100644
index 0000000..ac5e8fd
Binary files /dev/null and b/EC.Util/libs/hik-win64/libssl-1_1-x64.dll differ
diff --git a/JiLinApp.sln b/JiLinApp.sln
new file mode 100644
index 0000000..82cd743
--- /dev/null
+++ b/JiLinApp.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33205.214
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiLinApp", "JiLinApp\JiLinApp.csproj", "{273E34C9-ED30-460E-BA9A-66A8F27CC5FE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EC.Util", "EC.Util\EC.Util.csproj", "{A41E47C9-1930-4FF0-955E-B91EC859D262}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {273E34C9-ED30-460E-BA9A-66A8F27CC5FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {273E34C9-ED30-460E-BA9A-66A8F27CC5FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {273E34C9-ED30-460E-BA9A-66A8F27CC5FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {273E34C9-ED30-460E-BA9A-66A8F27CC5FE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A41E47C9-1930-4FF0-955E-B91EC859D262}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A41E47C9-1930-4FF0-955E-B91EC859D262}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A41E47C9-1930-4FF0-955E-B91EC859D262}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A41E47C9-1930-4FF0-955E-B91EC859D262}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {18952E5F-487C-4B42-92A5-5EBBE87AFF1F}
+ EndGlobalSection
+EndGlobal
diff --git a/JiLinApp/App.xaml b/JiLinApp/App.xaml
new file mode 100644
index 0000000..5f4c959
--- /dev/null
+++ b/JiLinApp/App.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JiLinApp/App.xaml.cs b/JiLinApp/App.xaml.cs
new file mode 100644
index 0000000..fd8b25c
--- /dev/null
+++ b/JiLinApp/App.xaml.cs
@@ -0,0 +1,79 @@
+using JiLinApp.Core;
+using JiLinApp.Pages.Main;
+using NewLife.Log;
+using Prism.Ioc;
+using Prism.Modularity;
+using System;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Threading;
+
+namespace JiLinApp;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App
+{
+ public App() : base()
+ {
+ RegisterEvents();
+ Global.Init();
+ }
+
+ protected override void OnStartup(StartupEventArgs e)
+ {
+ base.OnStartup(e);
+ }
+
+ protected override Window CreateShell()
+ {
+ return Container.Resolve();
+ }
+
+ protected override void RegisterTypes(IContainerRegistry containerRegistry)
+ {
+ //containerRegistry.RegisterSingleton();
+ }
+
+ protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
+ {
+ //moduleCatalog.AddModule();
+ }
+
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+ }
+
+ #region Bind
+
+ private void RegisterEvents()
+ {
+ //UI线程未捕获异常处理事件(UI主线程)
+ DispatcherUnhandledException += App_DispatcherUnhandledException;
+
+ //Task线程内未捕获异常处理事件
+ TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
+
+ //非UI线程未捕获异常处理事件(例如自己创建的一个子线程)
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+ }
+
+ private static void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
+ {
+ XTrace.Log.Error("{0}", e.Exception.Message);
+ }
+
+ private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
+ {
+ XTrace.Log.Error("{0}", e.Exception.Message);
+ }
+
+ private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ XTrace.Log.Error("{0}", ((Exception)e.ExceptionObject).Message);
+ }
+
+ #endregion Bind
+}
\ No newline at end of file
diff --git a/JiLinApp/Core/Config.cs b/JiLinApp/Core/Config.cs
new file mode 100644
index 0000000..693fe4e
--- /dev/null
+++ b/JiLinApp/Core/Config.cs
@@ -0,0 +1,17 @@
+using EC.Util.CameraSDK;
+using System.Collections.Generic;
+
+namespace JiLinApp.Core;
+
+public class AppConfig
+{
+ public ZmqConfig Zmq { get; set; }
+
+ public List CameraList { get; set; }
+}
+
+public class ZmqConfig
+{
+ public string AlarmPubAddr { get; set; }
+ public string AlarmPubTopic { get; set; }
+}
\ No newline at end of file
diff --git a/JiLinApp/Core/Global.cs b/JiLinApp/Core/Global.cs
new file mode 100644
index 0000000..9b3cf63
--- /dev/null
+++ b/JiLinApp/Core/Global.cs
@@ -0,0 +1,42 @@
+using NetMQ.Sockets;
+using NewLife.Configuration;
+using System;
+using System.Windows;
+
+namespace JiLinApp.Core;
+
+public static class Global
+{
+ #region fields
+
+ public static readonly JsonConfigProvider ConfigProvider;
+ public static readonly AppConfig AppConfig;
+
+ public static readonly PublisherSocket AlarmPubSocket;
+
+ #endregion fields
+
+ static Global()
+ {
+ try
+ {
+ // Config
+ ConfigProvider = new() { FileName = "config.json" };
+ AppConfig = new AppConfig();
+ ConfigProvider.Bind(AppConfig);
+
+ // Zmq
+ AlarmPubSocket = new();
+ AlarmPubSocket.Connect(AppConfig.Zmq.AlarmPubAddr);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.ToString());
+ throw;
+ }
+ }
+
+ public static void Init()
+ {
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Core/TaskUtil.cs b/JiLinApp/Core/TaskUtil.cs
new file mode 100644
index 0000000..273e263
--- /dev/null
+++ b/JiLinApp/Core/TaskUtil.cs
@@ -0,0 +1,23 @@
+using NewLife.Log;
+using System;
+using System.Threading.Tasks;
+
+namespace JiLinApp.Core;
+
+public static class TaskUtil
+{
+ public static Task Run(Action action)
+ {
+ return Task.Run(() =>
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception e)
+ {
+ XTrace.Log.Error("{0}", e.Message);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Docking/AlarmMessage.cs b/JiLinApp/Docking/AlarmMessage.cs
new file mode 100644
index 0000000..5e4ffd1
--- /dev/null
+++ b/JiLinApp/Docking/AlarmMessage.cs
@@ -0,0 +1,64 @@
+using JiLinApp.Pages.FenceServerManage;
+using JiLinApp.Pages.ShakeServerManage;
+using System;
+
+namespace JiLinApp.Docking;
+
+public class AlarmMessage
+{
+ /** (必填)传感器设备编码*/
+ public string labelCode;
+ /** (必填)报警类型((1-视频报警,2-雷达报警;3-微振动警报,4-电子围网警报,9-其他报警))*/
+ public int warnType;
+ /** (必填)报警级别(1-5)*/
+ public int warnLevel;
+ /** (必填)报警内容*/
+ public string warnContent;
+ /** 处置方式*/
+ public int dealWay;
+ /** (必填)处置状态(1-未处理,2-已处理)*/
+ public int dealStatus;
+ /** (必填)发生地点*/
+ public string cameraLoc;
+ /** (必填)发生时间*/
+ public DateTime createTime;
+ /** 负责人*/
+ public string director;
+ /** (必填)类型(1-预警,2-报警)*/
+ public int kind;
+ /** 现场照片*/
+ public string imgUrl;
+}
+
+public static class AlarmMessageHelper
+{
+ public static AlarmMessage ToAlarmMessage(this TcpAlarmHostMessage msg)
+ {
+ AlarmMessage obj = new()
+ {
+ labelCode = Convert.ToString(msg.Id),
+ warnType = 3,
+ warnLevel = 1,
+ warnContent = msg.AlarmId,
+ dealStatus = 1,
+ createTime = Convert.ToDateTime(msg.AlarmTime),
+ kind = 1,
+ };
+ return obj;
+ }
+
+ public static AlarmMessage ToAlarmMessage(this UdpAlarmHostMessage msg)
+ {
+ AlarmMessage obj = new()
+ {
+ labelCode = Convert.ToString(msg.Id),
+ warnType = 4,
+ warnLevel = 1,
+ warnContent = msg.CID,
+ dealStatus = 1,
+ createTime = Convert.ToDateTime(msg.AlarmTime),
+ kind = 1,
+ };
+ return obj;
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/JiLinApp.csproj b/JiLinApp/JiLinApp.csproj
new file mode 100644
index 0000000..16d40e4
--- /dev/null
+++ b/JiLinApp/JiLinApp.csproj
@@ -0,0 +1,24 @@
+
+
+ WinExe
+ net6.0-windows
+ zh-Hans
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
\ No newline at end of file
diff --git a/JiLinApp/Mvvm/RegionViewModelBase.cs b/JiLinApp/Mvvm/RegionViewModelBase.cs
new file mode 100644
index 0000000..4dc8b0b
--- /dev/null
+++ b/JiLinApp/Mvvm/RegionViewModelBase.cs
@@ -0,0 +1,30 @@
+using Prism.Events;
+using Prism.Regions;
+using System;
+
+namespace JiLinApp.Mvvm;
+
+public class RegionViewModelBase : ViewModelBase, INavigationAware, IConfirmNavigationRequest
+{
+ public RegionViewModelBase(IRegionManager region, IEventAggregator ea) : base(region, ea)
+ {
+ }
+
+ public virtual void ConfirmNavigationRequest(NavigationContext navigationContext, Action continuationCallback)
+ {
+ continuationCallback(true);
+ }
+
+ public virtual bool IsNavigationTarget(NavigationContext navigationContext)
+ {
+ return true;
+ }
+
+ public virtual void OnNavigatedFrom(NavigationContext navigationContext)
+ {
+ }
+
+ public virtual void OnNavigatedTo(NavigationContext navigationContext)
+ {
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Mvvm/ViewModelBase.cs b/JiLinApp/Mvvm/ViewModelBase.cs
new file mode 100644
index 0000000..9769206
--- /dev/null
+++ b/JiLinApp/Mvvm/ViewModelBase.cs
@@ -0,0 +1,34 @@
+using Prism.Events;
+using Prism.Mvvm;
+using Prism.Regions;
+
+namespace JiLinApp.Mvvm;
+
+public abstract class AbstractViewModelBase : BindableBase
+{
+ #region fields
+
+ protected IRegionManager _region { get; set; }
+ protected IEventAggregator _ea { get; set; }
+
+ #endregion fields
+
+ public AbstractViewModelBase(IRegionManager region, IEventAggregator ea)
+ {
+ _region = region;
+ _ea = ea;
+ }
+
+ public abstract void SetView(object view);
+}
+
+public class ViewModelBase : AbstractViewModelBase
+{
+ public ViewModelBase(IRegionManager region, IEventAggregator ea) : base(region, ea)
+ {
+ }
+
+ public override void SetView(object view)
+ {
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/AlarmEncode.cs b/JiLinApp/Pages/FenceServerManage/Core/AlarmEncode.cs
new file mode 100644
index 0000000..437f286
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/AlarmEncode.cs
@@ -0,0 +1,141 @@
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class AlarmEncode
+{
+ private static byte[] TEA_key = new byte[16] {
+ 0x08,0x01,0x08,0x06,0x07,0x08,0x07,0x08,
+ 0x08,0x90,0xC5,0x04,0x0D,0x0E,0x0F,0x10
+ };
+
+ public static byte[] encodeMessage(byte[] msg)
+ {
+ if (!checkMessage(msg)) return null;
+ return getMessage(msg[0], btea_encrypt(getContent(msg, msg[0] - 1)));
+ }
+
+ public static byte[] decodeMessage(byte[] msg)
+ {
+ if (!checkMessage(msg)) return null;
+ return getMessage(msg[0], btea_decrpyt(getContent(msg, msg[0] - 1)));
+ }
+
+ public static byte[] CRC16(byte[] data)
+ {
+ int len = data.Length;
+ if (len > 0)
+ {
+ ushort crc = 0xFFFF;
+
+ for (int i = 0; i < len; i++)
+ {
+ crc = (ushort)(crc ^ (data[i]));
+ for (int j = 0; j < 8; j++)
+ {
+ crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
+ }
+ }
+ byte hi = (byte)((crc & 0xFF00) >> 8); //高位置
+ byte lo = (byte)(crc & 0x00FF); //低位置
+
+ return new byte[] { hi, lo };
+ }
+ return new byte[] { 0, 0 };
+ }
+
+ private static byte version = 0x12;
+ private static byte[] head = new byte[] { 0xF0, 0xFA };
+ private static byte end = 0x0D;
+
+ public static byte[] getSendMessage(byte command, byte[] data)
+ {
+ byte[] msg = null;
+ if (data == null)
+ {
+ msg = new byte[8];
+ }
+ else
+ {
+ msg = new byte[data.Length + 8];
+ }
+ msg[0] = (byte)msg.Length;
+ msg[1] = version;
+ msg[2] = head[0];
+ msg[3] = head[1];
+ msg[4] = command;
+ if (data != null)
+ {
+ for (int i = 0; i < data.Length; i++)
+ {
+ msg[i + 5] = data[i];
+ }
+ }
+ byte[] subMsg = null;
+ if (data == null)
+ {
+ subMsg = new byte[4];
+ }
+ else
+ {
+ subMsg = new byte[4 + data.Length];
+ }
+ for (int i = 0; i < subMsg.Length; i++)
+ {
+ subMsg[i] = msg[i + 1];
+ }
+ byte[] crc = CRC16(subMsg);
+ msg[msg.Length - 3] = crc[1];
+ msg[msg.Length - 2] = crc[0];
+ msg[msg.Length - 1] = end;
+ return msg;
+ }
+
+ private static bool checkMessage(byte[] msg)
+ {
+ if (msg == null) return false;
+ if (msg[0] > msg.Length) return false;
+ return true;
+ }
+
+ private static byte[] getContent(byte[] msg, int len)
+ {
+ byte[] bytes = new byte[len];
+ for (int i = 0; i < len; i++)
+ {
+ bytes[i] = msg[i + 1];
+ }
+ return bytes;
+ }
+
+ private static byte[] getMessage(byte msgLen, byte[] msg)
+ {
+ byte[] out_msg = new byte[msg.Length + 1];
+ out_msg[0] = msgLen;
+ for (int i = 0; i < msg.Length; i++)
+ {
+ out_msg[i + 1] = msg[i];
+ }
+ return out_msg;
+ }
+
+ private static byte[] btea_encrypt(byte[] bytes)
+ {
+ byte[] output = new byte[bytes.Length];
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ byte abyte = (byte)(bytes[i] ^ TEA_key[i % 16]);
+ output[i] = (byte)(((byte)(abyte >> 3)) | ((byte)(abyte << 5)));
+ }
+ return output;
+ }
+
+ private static byte[] btea_decrpyt(byte[] bytes)
+ {
+ byte[] output = new byte[bytes.Length];
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ byte abyte = (byte)(((byte)(bytes[i] << 3)) | ((byte)(bytes[i] >> 5)));
+ output[i] = (byte)(abyte ^ TEA_key[i % 16]);
+ }
+ return output;
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/Entity/DeviceStateMessage.cs b/JiLinApp/Pages/FenceServerManage/Core/Entity/DeviceStateMessage.cs
new file mode 100644
index 0000000..c5052f2
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/Entity/DeviceStateMessage.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class DeviceStateMessage
+{
+ public int deviceID { get; set; }//设备唯一ID
+ public string StatusTime { get; set; }//时间
+ public int channel { get; set; }//防区
+ public int StatusType { get; set; }//状态
+
+ public DeviceStateMessage(int id, DateTime time, int Channel, int statusType)
+ {
+ this.deviceID = id;
+ this.StatusTime = time.ToString("yyyy-MM-dd HH:mm:ss");
+ this.channel = Channel;
+ this.StatusType = statusType;
+ }
+
+ public DeviceStateMessage()
+ {
+ this.deviceID = -1;
+ this.StatusTime = "";
+ this.channel = -1;
+ this.StatusType = -1;
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/Entity/SectorState.cs b/JiLinApp/Pages/FenceServerManage/Core/Entity/SectorState.cs
new file mode 100644
index 0000000..bc38f7a
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/Entity/SectorState.cs
@@ -0,0 +1,67 @@
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class SectorState
+{
+ public int id { get; set; }//防区号
+ /*0:防区未使用
+ 1:防区撤防
+ 2:防区布防
+ 3:防区旁路
+ 4:防区报警
+ 5:无线防区欠压
+ 6:防区掉线(与主线总线脱离)
+ */
+ public int state { get; set; }//防区状态
+
+ public string stateStr
+ {
+ get
+ {
+ switch (state)
+ {
+ case 0: return "防区未使用";
+ case 1: return "防区撤防";
+ case 2: return "防区布防";
+ case 3: return "防区旁路";
+ case 4: return "防区报警";
+ case 5: return "无线防区欠压";
+ case 6: return "防区掉线";
+ default: return "未知状态:" + state;
+ }
+ }
+ }
+
+ /*
+ 0:普通防区,无特殊参数
+ 1:张力防区需要单独查询因为这个防区显示张力线值每条线状态
+ 2:脉冲围栏
+ 3:振动光纤
+ 4:泄漏电缆
+ 5:网络或总线多子防区模块
+ */
+ public int type { get; set; }//防区类型,特殊参数需单独查询
+
+ public string typeStr
+ {
+ get
+ {
+ switch (type)
+ {
+ case 0: return "普通防区";
+ case 1: return "张力防区";
+ case 2: return "脉冲围栏";
+ case 3: return "振动光纤";
+ case 4: return "泄漏电缆";
+ case 5: return "网络或总线多子防区模块";
+ default: return "未知类型:" + type;
+ }
+ }
+ }
+
+ public SectorState(int id, byte data)
+ {
+ this.id = id;
+ this.state = data & 0x0F;
+ this.type = data >> 4;
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpAlarmHostDevice.cs b/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpAlarmHostDevice.cs
new file mode 100644
index 0000000..21be3db
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpAlarmHostDevice.cs
@@ -0,0 +1,172 @@
+using System.Collections.Generic;
+
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class UdpAlarmHostDevice
+{
+ public int deviceID { get; set; }//设备唯一ID
+
+ public string IP { get; set; }//设备IP
+
+ public int groupID { get; set; }//分组号,报警主机可划入不同组
+
+ public int userID { get; set; }//用户ID,指主机名称,有多台主机可以通过此名称区分
+ /*0x00: 撤防状态
+ 0x04: 在家布防(留守布防有些防区可能是在旁路状态)
+ 0x02:即时布防(所有防区没有延时)
+ 0x08 :即时留守布防(有些防区可能旁路,但是没有旁路防区没有延时)
+ 0x01:外出布防(普通布防最常用)
+ 0x09:部分防区布防部分防区撤防
+ */
+ public int deviceState { get; set; }//主机布撤状态,
+
+ public string deviceStateStr
+ {
+ get
+ {
+ switch (deviceState)
+ {
+ case 0x00:
+ return "撤防状态";
+
+ case 0x01:
+ return "外出布防";
+
+ case 0x02:
+ return "即时布防";
+
+ case 0x04:
+ return "在家布防";
+
+ case 0x08:
+ return "即时留守布防";
+
+ case 0x09:
+ return "部分防区布防";
+
+ default:
+ return "未知状态号:" + deviceState;
+ }
+ }
+ }
+
+ /*0x00:此设备处于常规工作状态,正常报警
+ 0x01:工作在设置模式,不会处理报警,用户这进行设置,设置完一般很快退出
+ 0x02:用户正在查询当前报警信息
+ 0x03:用户正在设置时钟
+ 0x04:工作在用户密码修改模式下
+ */
+ public int deviceWorkState { get; set; }//设备工作状态
+
+ public string deviceWorkStateStr
+ {
+ get
+ {
+ switch (deviceState)
+ {
+ case 0x00:
+ return "常规工作状态";
+
+ case 0x01:
+ return "在设置模式";
+
+ case 0x02:
+ return "用户正在查询报警";
+
+ case 0x03:
+ return "用户正在设置时钟";
+
+ case 0x04:
+ return "用户密码修改模式";
+
+ default:
+ return "未知状态号:" + deviceWorkState;
+ }
+ }
+ }
+
+ public int cellState { get; set; }//电池状态 1=电池故障 0=电池正常
+ public int electricityState { get; set; }//交流状态 1=交流掉电 0=交流正常
+ public int fuseState { get; set; }//警保险丝状态 1=外接警号断了0=正常
+ public int sectorState { get; set; }//有线防区触发了 1=其中有线防区触发了 0=所有有线防区准备好
+ public int cellTestState { get; set; }//正在电池载能测试 1=主机正在进行电池载能测试 0=无(主机会降低电压到电池12V以下,来测试电池负载特性需要2分钟)
+ public int deviceElectricityState { get; set; }//设备交流电状态1=交流掉电,目前电池供电0=正常 这是即时状态
+ public int soundState { get; set; }//设备声音输出状态(=0关闭声音) =1 输出声音) 用于布防和报警延时
+ public int testState { get; set; }//主机本地测试状态1=主机在本地测试(用于测试探测好坏是没有报警记录的)0=无
+
+ public int criticalAlarmState { get; set; }//软防区紧急报警状态1=有紧急报警(用户通过手动按下主机自带装置报警) 0=无
+ public int fireAlarmState { get; set; }//软防区火警报警状态1=有火警报警(用户通过手动按下主机自带装置报警) 0=无
+ public int stealAlarmState { get; set; }//软防区盗警报警状态1=有盗警报警(用户通过手动按下主机自带装置报警) 0=无
+ public int deviceFireAlarmState { get; set; }//设备中有火警报警状态 1=其中有火警防区触发了 0=无 整体状态
+ public int deviceAlarmState { get; set; }//设备有报警发生 1=指设备有报警状态,任一一种0=无报警
+ public int deviceBywayState { get; set; }//设备有旁路防区1=指设备某些防区有旁路了(这时软件可以要求上传具体旁路的防区) 2=无防区旁路
+ public int busOfflineState { get; set; }//有总线防区掉线状态1=指某些总线防区掉线了0=无
+ public int netOfflineState { get; set; }//有网络防区掉线状态1=指某些网络防区掉线了0=无
+
+ /*00:通用设备兼容没有设置或未知设备
+ 01:EH508报警主机
+ 02:EH508NP网络模块
+ 03: NETLINKBOARD32路网络模块
+ 04: EH508NETCILENT网络终端
+ 05: EH800 8路光纤主机
+ 06: EH508NP_UDP 私有服务器网络模块
+ 07: EH508CILENT_UDP 私有服务器接警终端
+ 08: EH508SEVER_UDP 私有服务器主机
+ 09: EH508MAP_UDP 私有服务器电子地图
+ 0A: EH508_UDP 私有服务器用EH508主机
+ 0x32: H768_IP系列网络模块
+ 0x33: D238C_IP网络模块
+ 0x34: H778S+ 系列报警主机
+ 0x35: H778S系列报警主机
+ 0x36:N201 N202网络模块
+ */
+ public int devideType { get; set; }//设备类型:区分不同的设备类型 增强可控,防止混乱,具体的设备类型见附加说明表格
+
+ public int signalIntensity { get; set; }//31:信号最强,如果设备类型不是这种不需要显示 指主要带有GPRS模块设备跟据设备类型来的
+
+ public int channelNum { get; set; }//分区数量
+
+ public List channelState { get; set; }//分区布防撤防状态,0为撤防,1为布防
+ public List channelAlarmState { get; set; }//分区报警状态,0为报警中,1为未报警
+
+ //-------------------------------防区信息-----------------------------//
+ public int maxSectorNum { get; set; }//最大防区号
+
+ public List sectorList { get; set; }
+
+ public int deviceOnlineState { get; set; }//设备在线状态
+
+ public int keep_live { get; set; }//设备在线状态
+
+ public UdpAlarmHostDevice()
+ {
+ deviceID = 0;
+ groupID = 0;
+ userID = 0;
+ deviceState = -1;
+ deviceWorkState = 0;
+ cellState = 0;
+ electricityState = 0;
+ fuseState = 0;
+ sectorState = 0;
+ cellTestState = 0;
+ deviceElectricityState = 0;
+ soundState = 0;
+ testState = 0;
+ criticalAlarmState = 0;
+ fireAlarmState = 0;
+ stealAlarmState = 0;
+ deviceFireAlarmState = 0;
+ deviceAlarmState = 0;
+ deviceBywayState = 0;
+ busOfflineState = 0;
+ netOfflineState = 0;
+ devideType = 0;
+ signalIntensity = 0;
+ channelNum = 0;
+ channelState = null;
+ channelAlarmState = null;
+ sectorList = null;
+ deviceOnlineState = 0;
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpAlarmHostMessage.cs b/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpAlarmHostMessage.cs
new file mode 100644
index 0000000..dcd7a56
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpAlarmHostMessage.cs
@@ -0,0 +1,30 @@
+using Newtonsoft.Json.Linq;
+
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class UdpAlarmHostMessage
+{
+ public int Id { get; set; }
+ public string AlarmTime { get; set; }//报警时间
+ public string CID { get; set; }//CID代码
+
+ /*设备信息*/
+ public int DeviceID { get; set; }//设备唯一ID
+ public int ChannelNum { get; set; }//防区号
+ public int SubChannelNum { get; set; }//子防区号
+ public string Ip { get; set; }//设备IP
+ public int GroupID { get; set; }//分组号,报警主机可划入不同组
+ public int UserID { get; set; }//用户ID,指主机名称,有多台主机可以通过此名称区分
+ public int LinkOut { get; set; }//联动输出
+ public string ExtendArgs { get; set; }//扩展参数
+
+ /*CID信息*/
+ public string AlarmLevel { get; set; }//报警级别
+ public string AlarmContent { get; set; }//报警内容
+ public string AlarmRemarks { get; set; }//报警备注
+ public string AlarmType { get; set; }//报警类型
+
+ /*联动信息*/
+ public bool IsLinked { get; set; }//是否有联动信息
+ public JArray Linklist { get; set; }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpManagerConfig.cs b/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpManagerConfig.cs
new file mode 100644
index 0000000..9c2df9d
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/Entity/UdpManagerConfig.cs
@@ -0,0 +1,12 @@
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class UdpManagerConfig
+{
+ public string ServerIp { get; set; }
+
+ public int ServerPort { get; set; }
+
+ public int DevicePort { get; set; }
+
+ public int DeviceHeartKeep { get; set; }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/Udp.cs b/JiLinApp/Pages/FenceServerManage/Core/Udp.cs
new file mode 100644
index 0000000..7778cb2
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/Udp.cs
@@ -0,0 +1,71 @@
+using System.ComponentModel;
+using System.Net;
+using System.Net.Sockets;
+
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class Udp
+{
+ private BackgroundWorker back = null;
+
+ ///
+ /// 用于UDP发送的网络服务类
+ ///
+ private UdpClient udpcSend = null;
+
+ ///
+ /// 用于UDP接收的网络服务类
+ ///
+ private UdpClient udpcRecv;
+
+ public delegate void UDPReceive(string IP, byte[] str);//
+
+ public event UDPReceive myUDPReceive;//
+
+ public Udp(string ip, int port)
+ {
+ IPEndPoint localIpep = new IPEndPoint(
+ IPAddress.Parse(ip), port); // 本机IP和监听端口号
+
+ udpcRecv = new UdpClient(localIpep);
+ udpcSend = new UdpClient();
+ back = new BackgroundWorker();
+ back.WorkerSupportsCancellation = true;
+ back.DoWork += back_DoWork;
+ back.RunWorkerAsync();
+ }
+
+ ~Udp()
+ {
+ if (back != null)
+ back.CancelAsync();
+ if (udpcRecv != null)
+ udpcRecv.Close();
+ if (udpcSend != null)
+ udpcSend.Close();
+ }
+
+ public bool SendMessage(byte[] sendbytes, string IP, int port)
+ {
+ IPEndPoint remoteIpep = new IPEndPoint(
+ IPAddress.Parse(IP), port); // 发送到的IP地址和端口号
+ int result = udpcSend.Send(sendbytes, sendbytes.Length, remoteIpep);
+ return result >= sendbytes.Length;
+ }
+
+ private void back_DoWork(object sender, DoWorkEventArgs e)
+ {
+ IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);
+ while (!back.CancellationPending)
+ {
+ try
+ {
+ byte[] bytRecv = udpcRecv.Receive(ref remoteIpep);
+ myUDPReceive(remoteIpep.Address.ToString(), bytRecv);
+ }
+ catch
+ {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Core/UdpManager.cs b/JiLinApp/Pages/FenceServerManage/Core/UdpManager.cs
new file mode 100644
index 0000000..c19de15
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Core/UdpManager.cs
@@ -0,0 +1,885 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Timers;
+using System.Windows;
+
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class UdpManager
+{
+ #region fields
+
+ private Udp myUdp = null;
+ private int udp_sendPort = -1;
+ private int udp_ReceivePort = -1;
+ private int keep_alive = -1;
+ private Timer PtzCheckTimer = new();
+
+ private List deviceList = new();
+
+ #endregion fields
+
+ public UdpManager()
+ {
+ PtzCheckTimer.Interval = 1000;
+ PtzCheckTimer.Elapsed += PTZCheckTimer_Elapsed;
+ }
+
+ //计时判断设备是否在线,若超过规定时间没有新消息发来,则设备离线
+ private void PTZCheckTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ foreach (UdpAlarmHostDevice p1 in deviceList)
+ {
+ if (p1.keep_live >= 0)
+ {
+ p1.keep_live--;
+ }
+ if (p1.keep_live < 0)
+ {
+ p1.deviceOnlineState = 0;
+ if (OnUdpAlarmDeviceState != null)
+ {
+ OnUdpAlarmDeviceState(p1.deviceID, "设备离线");
+ }
+ }
+ }
+ }
+
+ public List getDeviceAll()
+ {
+ return deviceList;
+ }
+
+ public UdpAlarmHostDevice getDevice(int deviceID)
+ {
+ foreach (UdpAlarmHostDevice p1 in deviceList)
+ {
+ if (p1.deviceID == deviceID) return p1;
+ }
+ return null;
+ }
+
+ public List getDeviceChannelAll(int deviceID)
+ {
+ foreach (UdpAlarmHostDevice p1 in deviceList)
+ {
+ if (p1.deviceID == deviceID) return p1.sectorList;
+ }
+ return null;
+ }
+
+ public SectorState getDeviceChannel(int deviceID, int channel)
+ {
+ UdpAlarmHostDevice state = getDevice(deviceID);
+ if (state == null || state.sectorList == null) return null;
+ foreach (SectorState p1 in state.sectorList)
+ {
+ if (p1.id == channel) return p1;
+ }
+ return null;
+ }
+
+ #region BaseMethod
+
+ public bool StartServer(UdpManagerConfig config)
+ {
+ string s_IP = config.ServerIp;
+ int s_Port = config.ServerPort;
+ int d_Port = config.DevicePort;
+ int d_Keep = config.DeviceHeartKeep;
+
+ return AlarmHostLogin(s_IP, s_Port, d_Port, d_Keep);
+ }
+
+ public bool StopServer()
+ {
+ return AlarmHostLoginOut();
+ }
+
+ private bool AlarmHostLogin(string IP, int Port, int device_port, int keep_alive)
+ {
+ if (myUdp != null) AlarmHostLoginOut();
+
+ this.udp_sendPort = device_port;
+ this.udp_ReceivePort = Port;
+ this.keep_alive = keep_alive;
+ try
+ {
+ //启动UDP协议
+ myUdp = new Udp(IP, udp_ReceivePort);
+ myUdp.myUDPReceive += myUDPReceive;
+ }
+ catch (Exception ex)
+ {
+ myUdp = null;
+ MessageBox.Show(ex.ToString());
+ return false;
+ }
+ if (keep_alive > 0)
+ {
+ PtzCheckTimer.Enabled = true;
+ }
+ return true;
+ }
+
+ private bool AlarmHostLoginOut()
+ {
+ PtzCheckTimer.Enabled = false;
+ if (myUdp != null)
+ {
+ myUdp.myUDPReceive -= myUDPReceive;
+ myUdp = null;
+ }
+ return true;
+ }
+
+ #endregion BaseMethod
+
+ public delegate void UDPAlarmDeviceStateEvent(int deviceId, string state);
+
+ public event UDPAlarmDeviceStateEvent OnUdpAlarmDeviceState;
+
+ public delegate void UDPAlarmSectionStateEvent(int deviceId, int channel, string state);
+
+ public event UDPAlarmSectionStateEvent OnUdpAlarmSectionState;
+
+ public delegate void UDPAlarmEvent(UdpAlarmHostMessage msg);
+
+ public event UDPAlarmEvent OnUdpAlarmEvent;
+
+ public delegate void UDPAlarmCancelEvent(int deviceId, int channel);
+
+ public event UDPAlarmCancelEvent OnUdpAlarmCancel;
+
+ private void myUDPReceive(string Ip, byte[] str)
+ {
+ //解码
+ byte[] msg = AlarmEncode.decodeMessage(str);
+ //解析
+ switch (msg[4])
+ {
+ case 0x01: //心跳数据
+ analysisHeartMessage(Ip, msg);
+ break;
+
+ case 0x02://报警信息
+ AnalysisAlarmMessage(Ip, msg);
+ break;
+
+ case 0x03://防区信息
+ analysisAllSectorMessage(Ip, msg);
+ break;
+
+ case 0x04://张力防区信息
+ analysisSectorMessage(Ip, msg);
+ break;
+
+ case 0x05://报警主机最大防区信息
+ analysisMaxSectorMessage(Ip, msg);
+ break;
+
+ case 0x08://返回报警主机设置参数回服务器,无需解析
+ break;
+
+ default:
+ break;
+ }
+ Console.WriteLine("消息类型:" + msg[4]);
+ sendOK(Ip);
+ }
+
+ private void sendOK(string IP)
+ {
+ if (myUdp == null) return;
+ //byte[] bytes = new byte[] { 0x08, 0x12, 0xF0, 0xFA, 0x8F, 0x7F, 0x2E, 0x0D };
+ byte[] bytes = new byte[] { 0x08, 0x12, 0xF0, 0xFA, 0x8F, 0x06, 0x6B, 0x0D };
+ myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), IP, udp_sendPort);
+ }
+
+ private void analysisHeartMessage(string IP, byte[] msg)
+ {
+ int deviceID = ByteToInt(msg, 5);
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (device == null)
+ {
+ device = new UdpAlarmHostDevice();
+ device.deviceID = deviceID;
+ device.userID = ByteToInt(msg, 13);
+ device.IP = IP;
+ device.deviceOnlineState = 1;
+ device.keep_live = keep_alive;
+ deviceList.Add(device);
+ }
+ device.deviceID = deviceID;
+ device.IP = IP;
+ device.groupID = ByteToInt(msg, 9);
+ device.userID = ByteToInt(msg, 13);
+ if (device.deviceState != msg[17])
+ {
+ if (OnUdpAlarmDeviceState != null)
+ {
+ switch (msg[17])
+ {
+ case 0x00://撤防状态
+ OnUdpAlarmDeviceState(device.deviceID, "撤防状态");
+ break;
+
+ case 0x01://外出布防
+ OnUdpAlarmDeviceState(device.deviceID, "外出布防(普通布防最常用)");
+ break;
+
+ case 0x02://即时布防(所有防区没有延时)
+ OnUdpAlarmDeviceState(device.deviceID, "即时布防(所有防区没有延时)");
+ break;
+
+ case 0x04://在家布防(留守布防有些防区可能是在旁路状态)
+ OnUdpAlarmDeviceState(device.deviceID, "在家布防(留守布防有些防区可能是在旁路状态)");
+ break;
+
+ case 0x08://即时留守布防(有些防区可能旁路,但是没有旁路防区没有延时)
+ OnUdpAlarmDeviceState(device.deviceID, "即时留守布防(有些防区可能旁路,但是没有旁路防区没有延时)");
+ break;
+
+ case 0x09://部分防区布防部分防区撤防
+ OnUdpAlarmDeviceState(device.deviceID, "部分防区布防部分防区撤防");
+ break;
+ }
+ }
+ }
+ device.deviceState = msg[17];
+ device.deviceWorkState = msg[18];
+ //设备状态1
+ device.cellState = getBit(msg[19], 0);
+ device.electricityState = getBit(msg[19], 1);
+ device.fuseState = getBit(msg[19], 2);
+ device.sectorState = getBit(msg[19], 3);
+ device.cellTestState = getBit(msg[19], 4);
+ device.deviceElectricityState = getBit(msg[19], 5);
+ device.soundState = getBit(msg[19], 6);
+ device.testState = getBit(msg[19], 7);
+ //设备状态2
+ device.criticalAlarmState = getBit(msg[20], 0);
+ device.fireAlarmState = getBit(msg[20], 1);
+ device.stealAlarmState = getBit(msg[20], 2);
+ device.deviceFireAlarmState = getBit(msg[20], 3);
+ device.deviceAlarmState = getBit(msg[20], 4);
+ device.deviceBywayState = getBit(msg[20], 5);
+ device.busOfflineState = getBit(msg[20], 6);
+ device.netOfflineState = getBit(msg[20], 7);
+ //设备状态3,4暂不使用
+
+ device.devideType = msg[23];
+ device.signalIntensity = msg[24];
+ device.channelNum = msg[25];
+ if (device.channelNum > 0)
+ {
+ device.channelState = new List();
+ device.channelAlarmState = new List();
+ for (int i = 0; i < device.channelNum; i++)
+ {
+ device.channelState.Add(getBit(msg[26], i));
+ device.channelAlarmState.Add(getBit(msg[27], i));
+ }
+ }
+ //在线状态
+ device.keep_live = keep_alive;
+ if (device.deviceOnlineState == 0)
+ {
+ if (OnUdpAlarmDeviceState != null)
+ {
+ OnUdpAlarmDeviceState(device.deviceID, "设备上线");
+ }
+ }
+ device.deviceOnlineState = 1;
+ }
+
+ private int getBit(byte bytes, int index)
+ {
+ switch (index)
+ {
+ case 0:
+ return bytes & 0x01;
+
+ case 1:
+ return (bytes & 0x02) >> 1;
+
+ case 2:
+ return (bytes & 0x04) >> 2;
+
+ case 3:
+ return (bytes & 0x08) >> 3;
+
+ case 4:
+ return (bytes & 0x10) >> 4;
+
+ case 5:
+ return (bytes & 0x20) >> 5;
+
+ case 6:
+ return (bytes & 0x40) >> 6;
+
+ case 7:
+ return (bytes & 0x80) >> 7;
+
+ default:
+ return 0;
+ }
+ }
+
+ private int ByteToInt(byte[] msg, int start)
+ {
+ byte[] bytes = new byte[] { msg[start + 3], msg[start + 2], msg[start + 1], msg[start] };
+ return BitConverter.ToInt32(bytes, 0);
+ }
+
+ private byte[] IntToByte(int num)
+ {
+ byte[] bytes = BitConverter.GetBytes(num);
+ return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
+ }
+
+ public void testAlarm(string devideId, string channel)
+ {
+ byte[] bytes = BitConverter.GetBytes(int.Parse(devideId));
+ byte[] bytes2 = BitConverter.GetBytes(int.Parse(channel));
+ byte[] msg = new byte[36]{
+ 0x24,0x12,0xF0,0xFA,
+ 0x02,bytes[3],bytes[2],bytes[1],
+ bytes[0],0x00,0x00,0x00,
+ 0x00,bytes[3],bytes[2],bytes[1],
+ bytes[0],0x22,0x12,0x23,
+ 0x10,0x10,0x10,0x01,
+ 0x01,0x03,0x01,0x00,
+ 0x02,bytes2[0],bytes2[1],0x00,
+ 0x00,0x04,0x03,0x01
+ };
+ AnalysisAlarmMessage("127.0.0.1", msg);
+ }
+
+ private void AnalysisAlarmMessage(string IP, byte[] msg)
+ {
+ string CID = getCID(msg[23]) + getCID(msg[24]) + getCID(msg[25]) + getCID(msg[26]);
+ int deviceID = ByteToInt(msg, 5);
+ int channelNum = msg[29] + msg[30] * 256;
+ string alarmTime = "20" + getBCD(msg[17]) + "-" + getBCD(msg[18]) + "-" + getBCD(msg[19])
+ + " " + getBCD(msg[20]) + ":" + getBCD(msg[21]) + ":" + getBCD(msg[22]);
+ if (CID.StartsWith("3"))
+ {
+ DataTable arrlist2 = new();
+ //DataTable arrlist2 = DBCenter.center.SearchByCondition("table_SectorAlarmMessage", "CID2='" + CID + "'");
+ if (arrlist2 == null || arrlist2.Rows.Count == 0)
+ {
+ //不是想要的消除警报信息
+ Console.WriteLine("不是想要的消除警报信息,设备号:" + deviceID + ",CID:" + CID);
+ return;
+ }
+ else
+ {
+ if (OnUdpAlarmCancel != null)
+ {
+ OnUdpAlarmCancel(deviceID, channelNum);
+ }
+ return;
+ }
+ }
+ //查询CID
+ DataTable arrlist = new();
+ //DataTable arrlist = DBCenter.center.SearchByCondition("table_SectorAlarmMessage", "CID='" + CID + "'");
+ if (arrlist == null || arrlist.Rows.Count == 0)
+ {
+ //查询不到的CID码的不需要处理
+ Console.WriteLine("不是想要的警报信息,设备号:" + deviceID + ",CID:" + CID);
+ return;
+ }
+
+ UdpAlarmHostMessage alarm = new()
+ {
+ DeviceID = deviceID,
+ Ip = IP,
+ GroupID = ByteToInt(msg, 9),
+ UserID = ByteToInt(msg, 13),
+ AlarmTime = alarmTime,
+ //CID暂定
+ CID = CID,
+ LinkOut = msg[27] + msg[28] * 256,
+ ChannelNum = channelNum,
+ SubChannelNum = msg[31] + msg[32] * 256,
+ ExtendArgs = msg[33].ToString("X2") + " " + msg[34].ToString("X2") + " " + msg[35].ToString("X2"),
+
+ AlarmLevel = arrlist.Rows[0]["level"].ToString(),
+ AlarmContent = arrlist.Rows[0]["content"].ToString(),
+ AlarmRemarks = arrlist.Rows[0]["remarks"].ToString(),
+ AlarmType = arrlist.Rows[0]["type"].ToString(),
+
+ Linklist = new()
+ };
+ //string error = "";
+ //alarm.linklist = DBCenter.center.SearchByConditionJArray("table_UDPLink", "deviceID=" + alarm.deviceID + "/channel='" + alarm.channelNum + "'", ref error);
+ if (alarm.Linklist == null || alarm.Linklist.Count == 0)
+ {
+ alarm.IsLinked = false;
+ }
+ else
+ {
+ alarm.IsLinked = true;
+ }
+
+ ProcessAlarm(alarm);
+ }
+
+ //处理报警事件
+ private void ProcessAlarm(UdpAlarmHostMessage msg)
+ {
+ OnUdpAlarmEvent(msg);
+
+ ////报警信息放入数据库
+ //JObject obj = new()
+ //{
+ // { "alarmTime", msg.AlarmTime },
+ // { "CID", msg.CID },
+
+ // { "deviceID", msg.DeviceID },
+ // { "channelNum", msg.ChannelNum },
+ // { "subChannelNum", msg.SubChannelNum },
+
+ // { "IP", msg.Ip },
+ // { "groupID", msg.GroupID },
+ // { "userID", msg.UserID },
+ // { "linkOut", msg.LinkOut },
+ // { "extendArgs", msg.ExtendArgs },
+
+ // { "alarm_level", msg.AlarmLevel },
+ // { "alarm_content", msg.AlarmContent },
+ // { "alarm_remarks", msg.AlarmRemarks },
+ // { "alarm_type", msg.AlarmType },
+
+ // { "IsLinked", msg.IsLinked },
+ // { "linklist", msg.Linklist }
+ //};
+ //string error = "";
+ //DBCenter.center.InsertResult("table_UDPAlarmMessage", obj, ref error);
+ ////打开预览窗口
+ //if (msg.IsLinked)
+ //{
+ // if (OnUdpAlarmEvent != null)
+ // {
+ // OnUdpAlarmEvent(msg);
+ // }
+ // //联动预置位
+ // for (int i = 0; i < msg.Linklist.Count; i++)
+ // {
+ // if (msg.Linklist[i]["needPreset"].ToString() == "true")
+ // {
+ // //int camID = int.Parse(msg.linklist[i]["camID"].ToString());
+ // //string camType = msg.linklist[i]["camType"].ToString();
+ // //CamType device_type = CamType.Table;
+ // //if (camType == "摄像机") device_type = CamType.Camera;
+ // //int presetId = int.Parse(msg.linklist[i]["presetID"].ToString());
+ // //RequestToService.center.NET_SDK_PTZPreset_Other(camID, device_type, (int)WMPTZPresetCommand.WMPTZPreset_GOTO, presetId, ref error);
+ // }
+ // }
+ //}
+ }
+
+ private string getCID(byte bytes)
+ {
+ switch (bytes)
+ {
+ case 0x00:
+ return "0";
+
+ case 0x01:
+ return "1";
+
+ case 0x02:
+ return "2";
+
+ case 0x03:
+ return "3";
+
+ case 0x04:
+ return "4";
+
+ case 0x05:
+ return "5";
+
+ case 0x06:
+ return "6";
+
+ case 0x07:
+ return "7";
+
+ case 0x08:
+ return "8";
+
+ case 0x09:
+ return "9";
+
+ case 0x0A:
+ return "A";
+
+ case 0x0B:
+ return "B";
+
+ case 0x0C:
+ return "C";
+
+ case 0x0D:
+ return "D";
+
+ case 0x0E:
+ return "E";
+
+ case 0x0F:
+ return "F";
+
+ default:
+ return "0";
+ }
+ }
+
+ private string getBCD(byte bytes)
+ {
+ int num = (bytes >> 4) * 10 + (bytes & 0x0F);
+ return num.ToString();
+ }
+
+ private byte getBCDByte(int num)
+ {
+ if (num >= 100) num = num % 100;
+ int hex = num / 10;
+ int lex = num % 10;
+ return (byte)(hex * 16 + lex);
+ }
+
+ private byte[] getBCDTime(DateTime time)
+ {
+ return new byte[] { getBCDByte(time.Year),getBCDByte((int)time.DayOfWeek),getBCDByte(time.Month),getBCDByte(time.Day),
+
+ getBCDByte(time.Hour),getBCDByte(time.Minute),getBCDByte(time.Second) };
+ }
+
+ private string getBCD2(byte bytes)
+ {
+ int num1 = bytes / 16;
+ int num2 = bytes % 16;
+ return (num1 * 10 + num2).ToString();
+ }
+
+ private void analysisAllSectorMessage(string IP, byte[] msg)
+ {
+ int deviceID = ByteToInt(msg, 5);
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (device == null)
+ {
+ device = new UdpAlarmHostDevice();
+ device.deviceID = deviceID;
+ device.IP = IP;
+ device.deviceOnlineState = 1;
+ device.keep_live = keep_alive;
+ deviceList.Add(device);
+ }
+ device.IP = IP;
+ device.groupID = ByteToInt(msg, 9);
+ device.userID = ByteToInt(msg, 13);
+
+ if (device.sectorList == null)
+ {
+ device.sectorList = new List();
+ }
+ int count = msg[19];
+ for (int i = 0; i < count; i++)
+ {
+ int index = i + msg[20];//防区序号
+ int pos = 21 + i;//防区信息所在byte数组未知
+ SectorState state = new SectorState(index, msg[pos]);
+ updateSector(state, device.sectorList, device.deviceID);
+ }
+ }
+
+ private void updateSector(SectorState state, List list, int deviceId)
+ {
+ for (int i = 0; i < list.Count; i++)
+ {
+ if (list[i].id == state.id)
+ {
+ //设备离线
+ if (list[i].state != state.state)
+ {
+ reportSectorState(deviceId, state.id, state.state);
+ }
+ list[i] = state; //防区存在,更新
+ return;
+ }
+ }
+ list.Add(state);//防区不存在,添加
+ reportSectorState(deviceId, state.id, state.state);
+ }
+
+ private void reportSectorState(int deviceId, int channel, int state)
+ {
+ if (OnUdpAlarmSectionState != null)
+ {
+ switch (state)
+ {
+ case 0:
+ OnUdpAlarmSectionState(deviceId, channel, "防区未使用");
+ break;
+
+ case 1://撤防
+ OnUdpAlarmSectionState(deviceId, channel, "防区撤防");
+ break;
+
+ case 2://布防
+ OnUdpAlarmSectionState(deviceId, channel, "防区布防");
+ break;
+
+ case 3://旁路
+ OnUdpAlarmSectionState(deviceId, channel, "防区旁路");
+ break;
+
+ case 4://报警
+ OnUdpAlarmSectionState(deviceId, channel, "防区报警");
+ break;
+
+ case 5://无线防区欠压
+ OnUdpAlarmSectionState(deviceId, channel, "无线防区欠压");
+ break;
+
+ case 6://防区掉线
+ OnUdpAlarmSectionState(deviceId, channel, "防区掉线(与主线总线脱离)");
+ break;
+
+ case 7://未准备就绪
+ OnUdpAlarmSectionState(deviceId, channel, "未准备就绪");
+ break;
+ }
+ }
+ }
+
+ private void analysisSectorMessage(string IP, byte[] msg)
+ {
+ //东北没有张力防区,暂不解析
+ }
+
+ private void analysisMaxSectorMessage(string IP, byte[] msg)
+ {
+ int deviceID = ByteToInt(msg, 5);
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (device == null)
+ {
+ device = new UdpAlarmHostDevice();
+ device.deviceID = deviceID;
+ device.IP = IP;
+ device.deviceOnlineState = 1;
+ device.keep_live = keep_alive;
+ deviceList.Add(device);
+ }
+ device.IP = IP;
+ device.groupID = ByteToInt(msg, 9);
+ device.userID = ByteToInt(msg, 13);
+ device.maxSectorNum = msg[17] * 256 + msg[18];
+ }
+
+ private bool checkDevice(UdpAlarmHostDevice device, ref string error)
+ {
+ if (device == null)
+ {
+ error = "没有此报警主机的记录";
+ return false;
+ }
+ if (device.deviceOnlineState == 0)
+ {
+ error = "此报警主机离线";
+ return false;
+ }
+ if (myUdp == null)
+ {
+ error = "UDP故障";
+ return false;
+ }
+ return true;
+ }
+
+ //外出布防或者布防
+ public bool setDeviceDefence(int deviceID, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0x80, new byte[] { 0x60 });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //立即布防
+ public bool setDeviceDefenceImmediately(int deviceID, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0x80, new byte[] { 0x62 });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //在家布防留守布防
+ public bool setDeviceDefenceHome(int deviceID, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0x80, new byte[] { 0x63 });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //即时留守布防
+ public bool setDeviceDefenceHomeImmediately(int deviceID, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0x80, new byte[] { 0x64 });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //撤防
+ public bool withdrawDeviceDefence(int deviceID, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0x80, new byte[] { 0x61 });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //清除报警记忆(复位)
+ public bool clearDeviceDefence(int deviceID, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0x80, new byte[] { 0x65 });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //校对系统时间
+ public bool setDeviceTime(int deviceID, DateTime time, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] content = getBCDTime(time);
+ byte[] bytes = AlarmEncode.getSendMessage(0x8D, content);
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //设备重启
+ public bool rebootDevice(int deviceID, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0xA0, null);
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //单防区布防
+ public bool setDeviceChannelDefence(int deviceID, int channel, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0xC0, new byte[] { (byte)(channel / 256), (byte)(channel % 256) });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //单防区撤防
+ public bool withdrawDeviceChannelDefence(int deviceID, int channel, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0xC1, new byte[] { (byte)(channel / 256), (byte)(channel % 256) });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //单防区旁路
+ public bool setDeviceChannelByway(int deviceID, int channel, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0xC2, new byte[] { (byte)(channel / 256), (byte)(channel % 256) });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //单防区旁路恢复
+ public bool withdrawDeviceChannelByway(int deviceID, int channel, ref string error)
+ {
+ UdpAlarmHostDevice device = getDevice(deviceID);
+ if (checkDevice(device, ref error))
+ {
+ byte[] bytes = AlarmEncode.getSendMessage(0xC3, new byte[] { (byte)(channel / 256), (byte)(channel % 256) });
+ return myUdp.SendMessage(AlarmEncode.encodeMessage(bytes), device.IP, udp_sendPort);
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Main.xaml b/JiLinApp/Pages/FenceServerManage/Main.xaml
new file mode 100644
index 0000000..1319fc8
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Main.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/Main.xaml.cs b/JiLinApp/Pages/FenceServerManage/Main.xaml.cs
new file mode 100644
index 0000000..3848a6f
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/Main.xaml.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace JiLinApp.Pages.FenceServerManage;
+
+///
+/// Main.xaml 的交互逻辑
+///
+public partial class Main : UserControl
+{
+ #region Fields
+
+ private UdpManager Manager { get; } = new();
+
+ #endregion Fields
+
+ public Main()
+ {
+ InitializeComponent();
+ Init();
+ }
+
+ #region Init
+
+ private void Init()
+ {
+ if (DataContext != null)
+ {
+ MainViewModel vm = DataContext as MainViewModel;
+ vm.SetView(this);
+
+ Manager.OnUdpAlarmDeviceState += vm.OnUdpAlarmDeviceState;
+ Manager.OnUdpAlarmSectionState += vm.OnUdpAlarmSectionState;
+ Manager.OnUdpAlarmEvent += vm.OnUdpAlarmEvent;
+ }
+ }
+
+ private void ChangeState(bool openState)
+ {
+ if (openState)
+ {
+ OpenBtn.IsEnabled = false;
+ StopBtn.IsEnabled = true;
+ }
+ else
+ {
+ OpenBtn.IsEnabled = true;
+ StopBtn.IsEnabled = false;
+ }
+ }
+
+ #endregion Init
+
+ #region ElementEvent
+
+ private void OpenBtn_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ UdpManagerConfig config = new()
+ {
+ ServerIp = ServerIp.Text,
+ ServerPort = int.Parse(ServerPort.Text),
+ DevicePort = int.Parse(DevicePort.Text),
+ DeviceHeartKeep = int.Parse(DeviceHeartKeep.Text)
+ };
+ Manager.StartServer(config);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.ToString());
+ return;
+ }
+ ChangeState(true);
+ }
+
+ private void StopBtn_Click(object sender, RoutedEventArgs e)
+ {
+ Manager.StopServer();
+ ChangeState(false);
+ }
+
+ #endregion ElementEvent
+
+ #region InvokeEvent
+
+ public void Test()
+ {
+ }
+
+ #endregion InvokeEvent
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/FenceServerManage/MainViewModel.cs b/JiLinApp/Pages/FenceServerManage/MainViewModel.cs
new file mode 100644
index 0000000..79d094c
--- /dev/null
+++ b/JiLinApp/Pages/FenceServerManage/MainViewModel.cs
@@ -0,0 +1,42 @@
+using JiLinApp.Mvvm;
+using JiLinApp.Pages.Main;
+using Prism.Events;
+using Prism.Regions;
+
+namespace JiLinApp.Pages.FenceServerManage;
+
+public class MainViewModel : ViewModelBase
+{
+ #region fields
+
+ private Main View { get; set; }
+
+ #endregion fields
+
+ public MainViewModel(IRegionManager region, IEventAggregator ea) : base(region, ea)
+ {
+ }
+
+ public override void SetView(object view)
+ {
+ View = (Main)view;
+ View.Test();
+ }
+
+ #region ConnEvent
+
+ internal void OnUdpAlarmDeviceState(int deviceId, string state)
+ {
+ }
+
+ internal void OnUdpAlarmSectionState(int deviceId, int channel, string state)
+ {
+ }
+
+ internal void OnUdpAlarmEvent(UdpAlarmHostMessage msg)
+ {
+ _ea.GetEvent().Publish(msg);
+ }
+
+ #endregion ConnEvent
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/Main/Core/MessageEvent.cs b/JiLinApp/Pages/Main/Core/MessageEvent.cs
new file mode 100644
index 0000000..9562302
--- /dev/null
+++ b/JiLinApp/Pages/Main/Core/MessageEvent.cs
@@ -0,0 +1,17 @@
+using JiLinApp.Pages.FenceServerManage;
+using JiLinApp.Pages.ShakeServerManage;
+using Prism.Events;
+
+namespace JiLinApp.Pages.Main;
+
+public class MessageSentEvent : PubSubEvent
+{
+}
+
+public class ShakeTcpAlarmSendEvent : PubSubEvent
+{
+}
+
+public class FenceUdpAlarmSendEvent : PubSubEvent
+{
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/Main/Core/PageNames.cs b/JiLinApp/Pages/Main/Core/PageNames.cs
new file mode 100644
index 0000000..5b4afae
--- /dev/null
+++ b/JiLinApp/Pages/Main/Core/PageNames.cs
@@ -0,0 +1,5 @@
+namespace JiLinApp.Pages.Main;
+
+public static class PageNames
+{
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/Main/Core/RegionNames.cs b/JiLinApp/Pages/Main/Core/RegionNames.cs
new file mode 100644
index 0000000..9511ddb
--- /dev/null
+++ b/JiLinApp/Pages/Main/Core/RegionNames.cs
@@ -0,0 +1,6 @@
+namespace JiLinApp.Pages.Main;
+
+public static class RegionNames
+{
+ public static string ContentRegion { get; } = "ContentRegion";
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/Main/MainWindow.xaml b/JiLinApp/Pages/Main/MainWindow.xaml
new file mode 100644
index 0000000..cce736d
--- /dev/null
+++ b/JiLinApp/Pages/Main/MainWindow.xaml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JiLinApp/Pages/Main/MainWindow.xaml.cs b/JiLinApp/Pages/Main/MainWindow.xaml.cs
new file mode 100644
index 0000000..6c1508e
--- /dev/null
+++ b/JiLinApp/Pages/Main/MainWindow.xaml.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Controls.Primitives;
+
+namespace JiLinApp.Pages.Main;
+
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ #region ElementEvent
+
+ private void ToggleButton_Checked(object sender, RoutedEventArgs e)
+ {
+ ToggleButton btn = (ToggleButton)sender;
+ bool isChecked = btn.IsChecked ?? false;
+ if (isChecked) { AllocConsole(); AttachConsole(-1); }
+ else FreeConsole();
+ }
+
+ #endregion ElementEvent
+
+ #region
+
+ [DllImport("Kernel32")]
+ private static extern void AllocConsole();
+
+ [DllImport("Kernel32")]
+ private static extern void FreeConsole();
+
+ [DllImport("kernel32")]
+ private static extern bool AttachConsole(int dwProcessId);
+
+ #endregion
+
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ Console.WriteLine("test");
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/PtzManage/Core/PelcoD.cs b/JiLinApp/Pages/PtzManage/Core/PelcoD.cs
new file mode 100644
index 0000000..e332e7f
--- /dev/null
+++ b/JiLinApp/Pages/PtzManage/Core/PelcoD.cs
@@ -0,0 +1,242 @@
+using System;
+
+namespace JiLinApp.Pages.PtzManage;
+
+public class PelcoD
+{
+ private string watchdir = ""; //监控方向
+ private static readonly byte STX = 0xFF; //同步字节
+
+ #region 监控方向和定时监控实体
+
+ public string WatchDir
+ {
+ get { return watchdir; }
+ set { watchdir = value; }
+ }
+
+ #endregion 监控方向和定时监控实体
+
+ #region 基本指令定义
+
+ #region 指令码1
+
+ private const byte FocusNear = 0x01; //增加聚焦
+ private const byte IrisOpen = 0x02; //减小光圈
+ private const byte IrisClose = 0x04; //增加光圈
+ private const byte CameraOnOff = 0x08; //摄像机打开和关闭
+ private const byte AutoManualScan = 0x10; //自动和手动扫描
+ private const byte Sense = 0x80; //Sence码
+
+ #endregion 指令码1
+
+ #region 指令码2
+
+ private const byte PanRight = 0x02; //右
+ private const byte PanLeft = 0x04; //左
+
+ private const byte TiltUp = 0x08; //上
+ private const byte TiltDown = 0x10; //下
+ private const byte ZoomTele = 0x20; //增加对焦
+ private const byte ZoomWide = 0x40; //减小对焦
+ private const byte FocusFar = 0x80; //减小聚焦
+
+ #endregion 指令码2
+
+ #region 镜头左右平移的速度
+
+ public static byte PanSpeedMin = 0x00; //停止
+ public static byte PanSpeedMax = 0x3F; //最高速
+
+ #endregion 镜头左右平移的速度
+
+ #region 镜头上下移动的速度
+
+ public static byte TiltSpeedMin = 0x00; //停止
+ public static byte TiltSpeedMax = 0x3F; //最高速
+
+ #endregion 镜头上下移动的速度
+
+ #endregion 基本指令定义
+
+ private const byte PanRightUp = 0xa; //右上
+ private const byte PanLeftUp = 0x0c; //左上
+
+ private const byte PanRightDown = 0x12; //右下
+ private const byte PanLeftDown = 0x14; //左下
+
+ #region 云台控制枚举
+
+ public enum Switch
+ {
+ On = 0x01,
+ Off = 0x02
+ } //雨刷控制
+
+ public enum Focus
+ {
+ Near = FocusNear,
+ Far = FocusFar
+ } //聚焦控制
+
+ public enum Zoom
+ {
+ Wide = ZoomWide,
+ Tele = ZoomTele
+ } //对焦控制
+
+ public enum Tilt
+ {
+ Up = TiltUp,
+ Down = TiltDown
+ } //上下控制
+
+ public enum Pan
+ {
+ Left = PanLeft,
+ Right = PanRight,
+ LeftUp = PanLeftUp,
+ LeftDown = PanLeftDown,
+ RightUp = PanRightUp,
+ RightDown = PanRightDown
+ } //左右控制
+
+ public enum Scan
+ {
+ Auto,
+ Manual
+ } //自动和手动控制
+
+ public enum Iris
+ {
+ Open = IrisOpen,
+ Close = IrisClose
+ } //光圈控制
+
+ public enum PresetAction
+ {
+ Set = 0x03,
+ Clear = 0x05,
+ Goto = 0x07
+ }
+
+ #endregion 云台控制枚举
+
+ #region 云台控制方法
+
+ //雨刷控制
+ public static byte[] CameraSwitch(uint deviceAddress, Switch action)
+ {
+ byte m_action = CameraOnOff;
+ if (action == Switch.On)
+ m_action = CameraOnOff + Sense;
+ return Message.GetMessage(deviceAddress, m_action, 0x00, 0x00, 0x00);
+ }
+
+ //光圈控制
+ public static byte[] CameraIrisSwitch(uint deviceAddress, Iris action)
+ {
+ return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
+ }
+
+ //聚焦控制
+ public static byte[] CameraFocus(uint deviceAddress, Focus action)
+ {
+ if (action == Focus.Near)
+ return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
+ else
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, 0x00);
+ }
+
+ //对焦控制
+ public static byte[] CameraZoom(uint deviceAddress, Zoom action)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, 0x00);
+ }
+
+ ///
+ /// 上下控制
+ ///
+ ///
+ ///
+ ///
+ ///
+
+ public static byte[] CameraTilt(uint deviceAddress, Tilt action, uint speed)
+ {
+ if (speed < TiltSpeedMin)
+ speed = TiltSpeedMin;
+ if (speed > TiltSpeedMax)
+ speed = TiltSpeedMax;
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, (byte)speed);
+ }
+
+ ///
+ /// 左右控制
+ ///
+ ///
+ ///
+ ///
+ ///
+
+ public static byte[] CameraPan(uint deviceAddress, Pan action, uint speed)
+ {
+ if (speed < PanSpeedMin)
+ speed = PanSpeedMin;
+ if (speed > PanSpeedMax)
+ speed = PanSpeedMax;
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, (byte)speed, 0x00);
+ }
+
+ //停止云台的移动
+ public static byte[] CameraStop(uint deviceAddress)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x00, 0x00, 0x00);
+ }
+
+ //自动和手动控制
+ public static byte[] CameraScan(uint deviceAddress, Scan scan)
+ {
+ byte m_byte = AutoManualScan;
+ if (scan == Scan.Auto)
+ m_byte = AutoManualScan + Sense;
+ return Message.GetMessage(deviceAddress, m_byte, 0x00, 0x00, 0x00);
+ }
+
+ public static byte[] Preset(uint deviceAddress, PresetAction action, byte presetId)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, presetId);
+ }
+
+ #endregion 云台控制方法
+
+ public struct Message
+ {
+ public static byte Address;
+ public static byte CheckSum;
+
+ public static byte Command1,
+ Command2,
+ Data1,
+ Data2;
+
+ public static byte[] GetMessage(
+ uint address,
+ byte command1,
+ byte command2,
+ byte data1,
+ byte data2
+ )
+ {
+ if (address < 1 & address > 256)
+ throw new Exception("Pelco D协议只支持256设备");
+ Address = byte.Parse(address.ToString());
+ Command1 = command1;
+ Command2 = command2;
+ Data1 = data1;
+ Data2 = data2;
+ CheckSum = (byte)(Address + Command1 + Command2 + Data1 + Data2);
+ return new byte[] { STX, Address, Command1, Command2, Data1, Data2, CheckSum };
+ }
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/PtzManage/Core/PelcoP.cs b/JiLinApp/Pages/PtzManage/Core/PelcoP.cs
new file mode 100644
index 0000000..0a2bfab
--- /dev/null
+++ b/JiLinApp/Pages/PtzManage/Core/PelcoP.cs
@@ -0,0 +1,364 @@
+using System;
+
+namespace JiLinApp.Pages.PtzManage;
+
+///
+/// dot.NET Implementation of Pelco P Protocol
+///
+public class PelcoP
+{
+ private const byte STX = 0xA0;
+ private const byte ETX = 0xAF;
+
+ #region Pan and Tilt Commands
+
+ #region Data1
+
+ private const byte FocusFar = 0x01;
+ private const byte FocusNear = 0x02;
+ private const byte IrisOpen = 0x04;
+ private const byte IrisClose = 0x08;
+ private const byte CameraOnOff = 0x10;
+ private const byte AutoscanOn = 0x20;
+ private const byte CameraOn = 0x40;
+
+ #endregion Data1
+
+ #region Data2
+
+ private const byte PanRight = 0x02;
+ private const byte PanLeft = 0x04;
+ private const byte TiltUp = 0x08;
+ private const byte TiltDown = 0x10;
+ private const byte ZoomTele = 0x20;
+ private const byte ZoomWide = 0x40;
+
+ #endregion Data2
+
+ #region Data3
+
+ public static byte PanSpeedMin = 0x00;
+ public static byte PanSpeedMax = 0x3F;
+
+ #endregion Data3
+
+ #region Data4
+
+ public static byte TiltSpeedMin = 0x00;
+ public static byte TiltSpeedMax = 0x3F;
+
+ #endregion Data4
+
+ #endregion Pan and Tilt Commands
+
+ #region Enums
+
+ public enum PatternAction
+ {
+ Start,
+ Stop,
+ Run
+ }
+
+ public enum Action
+ {
+ Start,
+ Stop
+ }
+
+ public enum LensSpeed
+ {
+ Low = 0x00,
+ Medium = 0x01,
+ High = 0x02,
+ Turbo = 0x03
+ }
+
+ public enum Pan
+ {
+ Left = PanLeft,
+ Right = PanRight
+ }
+
+ public enum Tilt
+ {
+ Up = TiltUp,
+ Down = TiltDown
+ }
+
+ public enum Iris
+ {
+ Open = IrisOpen,
+ Close = IrisClose
+ }
+
+ public enum Zoom
+ {
+ Wide = ZoomWide,
+ Tele = ZoomTele
+ }
+
+ public enum Switch
+ {
+ On,
+ Off
+ }
+
+ public enum Focus
+ {
+ Near = FocusNear,
+ Far = FocusFar
+ }
+
+ public enum PresetAction
+ {
+ Set = 0x03,
+ Clear = 0x05,
+ Goto = 0x07
+ }
+
+ #endregion Enums
+
+ #region Extended Command Set
+
+ public static byte[] Preset(uint deviceAddress, PresetAction action, byte presetId)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, presetId);
+ }
+
+ public static byte[] Flip(uint deviceAddress)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x07, 0x00, 0x21);
+ }
+
+ public static byte[] ZeroPanPosition(uint deviceAddress)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x07, 0x00, 0x22);
+ }
+
+ public static byte[] AutoScan(uint deviceAddress, Action action)
+ {
+ byte m_action;
+ if (action == Action.Start)
+ m_action = 0x09;
+ else
+ m_action = 0x0B;
+ return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, 0x00);
+ }
+
+ public static byte[] RemoteReset(uint deviceAddress)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x0F, 0x00, 0x00);
+ }
+
+ public static byte[] Zone(uint deviceAddress, byte zone, Action action)
+ {
+ if (zone < 0x01 & zone > 0x08)
+ throw new Exception("Zone value should be between 0x01 and 0x08 include");
+ byte m_action;
+ if (action == Action.Start)
+ m_action = 0x11;
+ else
+ m_action = 0x13;
+
+ return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, zone);
+ }
+
+ public static byte[] WriteToScreen(uint deviceAddress, string text)
+ {
+ if (text.Length > 40)
+ text = text.Remove(40, text.Length - 40);
+ System.Text.Encoding encoding = System.Text.Encoding.ASCII;
+ byte[] m_bytes = new byte[encoding.GetByteCount(text) * 8];
+ int i = 0;
+ byte m_scrPosition;
+ byte m_ASCIIchr;
+
+ foreach (char ch in text)
+ {
+ m_scrPosition = Convert.ToByte(i / 8);
+ m_ASCIIchr = Convert.ToByte(ch);
+ Array.Copy(
+ Message.GetMessage(deviceAddress, 0x00, 0x15, m_scrPosition, m_ASCIIchr),
+ 0,
+ m_bytes,
+ i,
+ 8
+ );
+ i = i + 8;
+ }
+
+ return m_bytes;
+ }
+
+ public static byte[] ClearScreen(uint deviceAddress)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x17, 0x00, 0x00);
+ }
+
+ public static byte[] AlarmAcknowledge(uint deviceAddress, uint alarmID)
+ {
+ if (alarmID < 1 & alarmID > 8)
+ throw new Exception("Only 8 alarms allowed for Pelco P implementation");
+ return Message.GetMessage(deviceAddress, 0x00, 0x19, 0x00, Convert.ToByte(alarmID));
+ }
+
+ public static byte[] ZoneScan(uint deviceAddress, Action action)
+ {
+ byte m_action;
+ if (action == Action.Start)
+ m_action = 0x1B;
+ else
+ m_action = 0x1D;
+ return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, 0x00);
+ }
+
+ public static byte[] Pattern(uint deviceAddress, PatternAction action)
+ {
+ byte m_action;
+ switch (action)
+ {
+ case PatternAction.Start:
+ m_action = 0x1F;
+ break;
+
+ case PatternAction.Stop:
+ m_action = 0x21;
+ break;
+
+ case PatternAction.Run:
+ m_action = 0x23;
+ break;
+
+ default:
+ m_action = 0x23;
+ break;
+ }
+ return Message.GetMessage(deviceAddress, 0x00, m_action, 0x00, 0x00);
+ }
+
+ public static byte[] SetZoomLensSpeed(uint deviceAddress, LensSpeed speed)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x25, 0x00, (byte)speed);
+ }
+
+ public static byte[] SetFocusLensSpeed(uint deviceAddress, LensSpeed speed)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x27, 0x00, (byte)speed);
+ }
+
+ #endregion Extended Command Set
+
+ #region Base Command Set
+
+ public static byte[] CameraSwitch(uint deviceAddress, Switch action)
+ {
+ byte m_action = CameraOnOff;
+ if (action == Switch.On)
+ m_action += CameraOnOff; //Maybe wrong !!!
+ return Message.GetMessage(deviceAddress, m_action, 0x00, 0x00, 0x00);
+ }
+
+ public static byte[] CameraIrisSwitch(uint deviceAddress, Iris action)
+ {
+ return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
+ }
+
+ public static byte[] CameraFocus(uint deviceAddress, Focus action)
+ {
+ return Message.GetMessage(deviceAddress, (byte)action, 0x00, 0x00, 0x00);
+ }
+
+ public static byte[] CameraZoom(uint deviceAddress, Zoom action)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, 0x00);
+ }
+
+ public static byte[] CameraTilt(uint deviceAddress, Tilt action, uint speed)
+ {
+ if (speed < TiltSpeedMin)
+ speed = TiltSpeedMin;
+ if (speed > TiltSpeedMax)
+ speed = TiltSpeedMax;
+
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, 0x00, (byte)speed);
+ }
+
+ public static byte[] CameraPan(uint deviceAddress, Pan action, uint speed)
+ {
+ if (speed < PanSpeedMin)
+ speed = PanSpeedMin;
+ if (speed > PanSpeedMax)
+ speed = PanSpeedMax;
+
+ return Message.GetMessage(deviceAddress, 0x00, (byte)action, (byte)speed, 0x00);
+ }
+
+ public static byte[] CameraPanTilt(
+ uint deviceAddress,
+ Pan panAction,
+ uint panSpeed,
+ Tilt tiltAction,
+ uint tiltSpeed
+ )
+ {
+ byte[] m_bytes = new byte[8];
+ byte[] m_tiltMessage = CameraTilt(deviceAddress, tiltAction, tiltSpeed);
+ byte[] m_panMessage = CameraPan(deviceAddress, panAction, panSpeed);
+ //m_tiltMessage.CopyTo(m_bytes,0);
+ //m_panMessage.CopyTo(m_bytes,9);
+ /*m_bytes[0] = m_tiltMessage[0];
+ m_bytes[1] = m_tiltMessage[1];
+ m_bytes[2] = m_tiltMessage[2];
+ m_bytes[3] = (byte)(m_tiltMessage[3]+m_panMessage[3]);
+ m_bytes[4] = (byte)(+m_panMessage[4]);
+ m_bytes[5] = (byte)(+m_panMessage[5]);
+ m_bytes[6] = m_tiltMessage[6];
+ m_bytes[7] = m_tiltMessage[7];*/
+ m_bytes = Message.GetMessage(
+ deviceAddress,
+ 0x00,
+ (byte)(m_tiltMessage[3] + m_panMessage[3]),
+ m_panMessage[4],
+ m_tiltMessage[5]
+ );
+ return m_bytes;
+ }
+
+ public static byte[] CameraStop(uint deviceAddress)
+ {
+ return Message.GetMessage(deviceAddress, 0x00, 0x00, 0x00, 0x00);
+ }
+
+ #endregion Base Command Set
+
+ public struct Message
+ {
+ public static byte Address;
+ public static byte CheckSum;
+
+ public static byte Data1,
+ Data2,
+ Data3,
+ Data4;
+
+ public static byte[] GetMessage(
+ uint address,
+ byte data1,
+ byte data2,
+ byte data3,
+ byte data4
+ )
+ {
+ if (address < 0 & address > 32)
+ throw new Exception("Protocol Pelco P support 32 devices only");
+ Address = byte.Parse((address - 1).ToString());
+ Data1 = data1;
+ Data2 = data2;
+ Data3 = data3;
+ Data4 = data4;
+ CheckSum = (byte)(STX ^ Address ^ Data1 ^ Data2 ^ Data3 ^ Data4 ^ ETX);
+ return new byte[] { STX, Address, Data1, Data2, Data3, Data4, ETX, CheckSum };
+ }
+ }
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/PtzManage/Core/PtzCmd.cs b/JiLinApp/Pages/PtzManage/Core/PtzCmd.cs
new file mode 100644
index 0000000..5d108a9
--- /dev/null
+++ b/JiLinApp/Pages/PtzManage/Core/PtzCmd.cs
@@ -0,0 +1,191 @@
+using EC.Util.CameraSDK;
+using System;
+
+namespace JiLinApp.Pages.PtzManage;
+
+public class PtzCmd
+{
+ public static PtzControlType GetControlType(string ctrlStr)
+ {
+ return ctrlStr switch
+ {
+ "PelcoD" => PtzControlType.PelcoD,
+ "PelcoP" => PtzControlType.PelcoP,
+ "HikSdk" => PtzControlType.HikSdk,
+ _ => PtzControlType.None
+ };
+ }
+
+ public static PtzCmdType GetCmdType(string cmdStr)
+ {
+ return cmdStr switch
+ {
+ "Top" => PtzCmdType.Top,
+ "Down" => PtzCmdType.Down,
+ "Left" => PtzCmdType.Left,
+ "Right" => PtzCmdType.Right,
+ "TopLeft" => PtzCmdType.TopLeft,
+ "TopRight" => PtzCmdType.TopRight,
+ "DownLeft" => PtzCmdType.DownLeft,
+ "DownRight" => PtzCmdType.DownRight,
+ "Stop" => PtzCmdType.Stop,
+ "AutoStart" => PtzCmdType.AutoStart,
+ "AutoStop" => PtzCmdType.AutoStop,
+ "PresetSet" => PtzCmdType.PresetSet,
+ "PresetClear" => PtzCmdType.PresetClear,
+ "PresetGoto" => PtzCmdType.PresetGoto,
+ _ => PtzCmdType.None
+ };
+ }
+
+ public static byte[] GetCmd(PtzControlType ctrlType, PtzCmdType cmdType, byte[] args = null)
+ {
+ args ??= new byte[] { 1, 1, 1 };
+ byte[] cmd = ctrlType switch
+ {
+ PtzControlType.PelcoD => GetPelcoDCmd(cmdType, args),
+ PtzControlType.PelcoP => GetPelcoPCmd(cmdType, args),
+ _ => Array.Empty(),
+ };
+ return cmd;
+ }
+
+ public static byte[] GetPelcoDCmd(PtzCmdType cmdType, byte[] args)
+ {
+ uint addr = args[0];
+ uint panSpeed = 0x2f;
+ uint tiltSpeed = (uint)(PelcoD.TiltSpeedMax + PelcoD.TiltSpeedMin) / 2;
+ byte[] cmd = cmdType switch
+ {
+ PtzCmdType.Top => PelcoD.CameraTilt(addr, PelcoD.Tilt.Up, tiltSpeed),
+ PtzCmdType.Down => PelcoD.CameraTilt(addr, PelcoD.Tilt.Down, tiltSpeed),
+ PtzCmdType.Left => PelcoD.CameraPan(addr, PelcoD.Pan.Left, panSpeed),
+ PtzCmdType.Right => PelcoD.CameraPan(addr, PelcoD.Pan.Right, panSpeed),
+ PtzCmdType.TopLeft => PelcoD.CameraPan(addr, PelcoD.Pan.LeftUp, panSpeed),
+ PtzCmdType.TopRight => PelcoD.CameraPan(addr, PelcoD.Pan.RightUp, panSpeed),
+ PtzCmdType.DownLeft => PelcoD.CameraPan(addr, PelcoD.Pan.LeftDown, panSpeed),
+ PtzCmdType.DownRight => PelcoD.CameraPan(addr, PelcoD.Pan.RightDown, panSpeed),
+ PtzCmdType.Stop => PelcoD.CameraStop(addr),
+ PtzCmdType.AutoStart => PelcoD.CameraScan(addr, PelcoD.Scan.Auto),
+ PtzCmdType.AutoStop => PelcoD.CameraScan(addr, PelcoD.Scan.Manual),
+ PtzCmdType.PresetGoto => PelcoD.Preset(addr, PelcoD.PresetAction.Goto, args[1]),
+ PtzCmdType.None => Array.Empty(),
+ _ => Array.Empty(),
+ };
+ return cmd;
+ }
+
+ public static byte[] GetPelcoPCmd(PtzCmdType cmdType, byte[] args)
+ {
+ uint addr = args[0];
+ uint panSpeed = (uint)(PelcoP.PanSpeedMax + PelcoP.PanSpeedMin) / 2;
+ uint tiltSpeed = (uint)(PelcoP.TiltSpeedMax + PelcoP.TiltSpeedMin) / 2;
+ byte[] cmd = cmdType switch
+ {
+ PtzCmdType.Top => PelcoP.CameraTilt(addr, PelcoP.Tilt.Up, tiltSpeed),
+ PtzCmdType.Down => PelcoP.CameraTilt(addr, PelcoP.Tilt.Down, tiltSpeed),
+ PtzCmdType.Left => PelcoP.CameraPan(addr, PelcoP.Pan.Left, panSpeed),
+ PtzCmdType.Right => PelcoP.CameraPan(addr, PelcoP.Pan.Right, panSpeed),
+ PtzCmdType.TopLeft => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Left, panSpeed, PelcoP.Tilt.Up, tiltSpeed),
+ PtzCmdType.TopRight => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Right, panSpeed, PelcoP.Tilt.Up, tiltSpeed),
+ PtzCmdType.DownLeft => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Left, panSpeed, PelcoP.Tilt.Down, tiltSpeed),
+ PtzCmdType.DownRight => PelcoP.CameraPanTilt(addr, PelcoP.Pan.Right, panSpeed, PelcoP.Tilt.Down, tiltSpeed),
+ PtzCmdType.Stop => PelcoP.CameraStop(addr),
+ PtzCmdType.AutoStart => PelcoP.AutoScan(addr, PelcoP.Action.Start),
+ PtzCmdType.AutoStop => PelcoP.AutoScan(addr, PelcoP.Action.Stop),
+ PtzCmdType.PresetGoto => PelcoP.Preset(addr, PelcoP.PresetAction.Goto, args[1]),
+ PtzCmdType.None => Array.Empty(),
+ _ => Array.Empty(),
+ };
+ return cmd;
+ }
+
+ public static void HikPtzMove(ref HiKSDK sdk, PtzCmdType cmdType, uint[] args)
+ {
+ if (sdk == null || args == null) return;
+ uint[] arr = new uint[args.Length + 1];
+ args.CopyTo(arr, 1);
+ switch (cmdType)
+ {
+ case PtzCmdType.Top:
+ arr[0] = HiKOriSDK.TILT_UP;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.Down:
+ arr[0] = HiKOriSDK.TILT_DOWN;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.Left:
+ arr[0] = HiKOriSDK.PAN_LEFT;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.Right:
+ arr[0] = HiKOriSDK.PAN_RIGHT;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.TopLeft:
+ arr[0] = HiKOriSDK.UP_LEFT;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.TopRight:
+ arr[0] = HiKOriSDK.UP_RIGHT;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.DownLeft:
+ arr[0] = HiKOriSDK.DOWN_LEFT;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.DownRight:
+ arr[0] = HiKOriSDK.DOWN_RIGHT;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.AutoStart:
+ arr[0] = HiKOriSDK.PAN_AUTO;
+ sdk.PtzMove(arr);
+ break;
+
+ case PtzCmdType.PresetGoto:
+ arr[0] = HiKOriSDK.GOTO_PRESET;
+ sdk.PtzPreset(arr);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+public enum PtzControlType
+{
+ PelcoD,
+ PelcoP,
+ HikSdk,
+ None
+}
+
+public enum PtzCmdType
+{
+ Top,
+ Down,
+ Left,
+ Right,
+ TopLeft,
+ TopRight,
+ DownLeft,
+ DownRight,
+ Stop,
+ AutoStart,
+ AutoStop,
+ PresetSet,
+ PresetClear,
+ PresetGoto,
+ None
+}
\ No newline at end of file
diff --git a/JiLinApp/Pages/PtzManage/Main.xaml b/JiLinApp/Pages/PtzManage/Main.xaml
new file mode 100644
index 0000000..0cfdb7e
--- /dev/null
+++ b/JiLinApp/Pages/PtzManage/Main.xaml
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JiLinApp/Pages/PtzManage/Main.xaml.cs b/JiLinApp/Pages/PtzManage/Main.xaml.cs
new file mode 100644
index 0000000..2d3ec4a
--- /dev/null
+++ b/JiLinApp/Pages/PtzManage/Main.xaml.cs
@@ -0,0 +1,315 @@
+using EC.Util.CameraSDK;
+using EC.Util.Port;
+using ImTools;
+using JiLinApp.Core;
+using NewLife.Reflection;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+namespace JiLinApp.Pages.PtzManage;
+
+///
+/// Main.xaml 的交互逻辑
+///
+public partial class Main : UserControl
+{
+ #region fields
+
+ private YcSerialPort Port { get; set; }
+
+ private ConcurrentDictionary HikMap { get; } = new();
+
+ #endregion fields
+
+ public Main()
+ {
+ InitializeComponent();
+ Init();
+ }
+
+ #region Init
+
+ private void Init()
+ {
+ // 初始化串口
+ string[] ports = SerialPort.GetPortNames();
+ ports = ports.OrderBy(s => int.Parse(Regex.Match(s, @"\d+").Value)).ToArray();
+ foreach (string port in ports)
+ {
+ ComName.Items.Add(new ComboBoxItem { Content = port });
+ }
+
+ // 初始化下拉框 Id
+ for (int i = 1; i <= 10; i++)
+ {
+ CameraId.Items.Add(new ComboBoxItem { Content = i });
+ PresetId.Items.Add(new ComboBoxItem { Content = i });
+ }
+
+ // 绑定云台响应事件
+ List