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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +