Browse Source

[feat] 整体完成

todolist:
1.实时视频窗口展示问题(标题栏按钮和任务栏)
2.大华宇视 linux 上的视频对接
3.对接服务的收发消息展示
master
fajiao 1 year ago
parent
commit
417546c793
  1. 47
      EC.Util/CameraSDK/Common/CameraFactory.cs
  2. 111
      EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs
  3. 40
      EC.Util/CameraSDK/HiK/HiKOriSdk.cs
  4. 78
      EC.Util/CameraSDK/HiK/HiKSdk.cs
  5. 37
      EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs
  6. 26
      EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs
  7. 23
      EC.Util/CameraSDK/YuShi/YuShiSdk.cs
  8. 8
      EC.Util/Common/LogUnit.cs
  9. 12
      EC.Util/Common/SystemUtil.cs
  10. 7
      EC.Util/EC.Util.csproj
  11. BIN
      EC.Util/libs/cameraSdks.zip
  12. 16
      JiLinApp.Biz/TransmitAlarm/Service/AlarmMqttService.cs
  13. 9
      JiLinApp.Docking/Alarm/AlarmCodeHelper.cs
  14. 2
      JiLinApp.Docking/JiLinApp.Docking.csproj
  15. 9
      JiLinApp.Docking/Ptz/PtzCmd.cs
  16. 3
      JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs
  17. 2
      JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs
  18. 0
      JiLinApp.Docking/config/alarmcodes.json
  19. 18
      JiLinApp/App.axaml
  20. 156
      JiLinApp/Components/CameraRealPlay.axaml
  21. 180
      JiLinApp/Components/CameraRealPlay.axaml.cs
  22. 5
      JiLinApp/Components/NativeHost.cs
  23. 22
      JiLinApp/Core/App/Config.cs
  24. 65
      JiLinApp/Core/App/Global.cs
  25. 13
      JiLinApp/Core/App/MessageEvent.cs
  26. 150
      JiLinApp/Core/Avalonia/ControlsUtil.cs
  27. 30
      JiLinApp/JiLinApp.csproj
  28. 54
      JiLinApp/Pages/FenceServer/Fence.axaml
  29. 174
      JiLinApp/Pages/FenceServer/Fence.axaml.cs
  30. 10
      JiLinApp/Pages/FenceServer/FenceViewModel.cs
  31. 89
      JiLinApp/Pages/Main/MainWindow.axaml
  32. 3
      JiLinApp/Pages/Main/MainWindow.axaml.cs
  33. 13
      JiLinApp/Pages/Main/MainWindowViewModel.cs
  34. 261
      JiLinApp/Pages/PtzServer/Ptz.axaml
  35. 405
      JiLinApp/Pages/PtzServer/Ptz.axaml.cs
  36. 10
      JiLinApp/Pages/PtzServer/PtzViewModel.cs
  37. 54
      JiLinApp/Pages/VibrateServer/Vibrate.axaml
  38. 168
      JiLinApp/Pages/VibrateServer/Vibrate.axaml.cs
  39. 10
      JiLinApp/Pages/VibrateServer/VibrateViewModel.cs
  40. 12
      JiLinApp/Program.cs
  41. 50
      JiLinApp/config/appconfig.json

47
EC.Util/CameraSDK/Common/CameraFactory.cs

@ -1,7 +1,52 @@
namespace EC.Util.CameraSDK;
using System.IO.Compression;
using System.Runtime.InteropServices;
namespace EC.Util.CameraSDK;
public class CameraFactory
{
static CameraFactory()
{
string zipPath = Path.Combine("libs", "cameraSdks.zip");
if (!File.Exists(zipPath)) throw new FileNotFoundException(zipPath);
using ZipArchive archive = ZipFile.OpenRead(zipPath);
bool isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows), is64 = Environment.Is64BitProcess;
string sysEnv = string.Format("{0}{1}", isWin ? "win" : "linux", is64 ? "64" : "32");
string hkOrDir = $"cameraSdks/hik/{sysEnv}/";
string dhOrDir = $"cameraSdks/dahua/{sysEnv}/";
string ysOrDir = $"cameraSdks/yushi/{sysEnv}/";
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Length == 0) continue;
string fullName = entry.FullName, fileExtPath = string.Empty;
if (fullName.StartsWith(hkOrDir))
fileExtPath = Path.Join("libs", "hik", fullName[(hkOrDir.Length - 1)..]);
else if (fullName.StartsWith(dhOrDir))
fileExtPath = Path.Join("libs", "dahua", fullName[(dhOrDir.Length - 1)..]);
else if (fullName.StartsWith(ysOrDir))
fileExtPath = Path.Join("libs", "yushi", fullName[(ysOrDir.Length - 1)..]);
if (string.IsNullOrEmpty(fileExtPath)) continue;
FileInfo fi = new(fileExtPath);
if (fi.Directory != null && !fi.Directory.Exists) fi.Directory.Create();
if (!fi.Exists) entry.ExtractToFile(fileExtPath);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
string[] hikLibsName = new string[] { "libAudioRender.so", "libSuperRender.so" };
foreach (string name in hikLibsName)
{
if (File.Exists(Path.Join(".", name))) continue;
string path = Path.Join("libs", "hik", name);
FileInfo fi = new(path);
if (!fi.Exists) throw new FileNotFoundException(path);
fi.CopyTo(Path.Join(".", name), false);
}
}
}
public static void VirtualInit()
{ }
public static ICameraSdk BuildCameraSdk(CameraInfo info)
{
ICameraSdk sdk = (info.Manufactor) switch

111
EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs

@ -1,8 +1,5 @@
//#define Win32
#define Win64
//#define Win
//#define Linux32
//#define Linux64
using System.Runtime.InteropServices;
namespace EC.Util.CameraSDK;
@ -11,14 +8,10 @@ public static class DaHuaOriSdk
{
#region Fields
#if Win32
public const string LibSdkPath = @"./libs/dahua/win32/dhnetsdk.dll";
#elif Win64
public const string LibSdkPath = @"./libs/dahua/win64/dhnetsdk.dll";
#elif Linux32
public const string LibSdkPath = @"./libs/dahua/linux32/libdhnetsdk.so";
#elif Linux64
public const string LibSdkPath = @"./libs/dahua/linux64/libdhnetsdk.so";
#if Win
public const string LibSdkPath = @"./libs/dahua/dhnetsdk.dll";
#else
public const string LibSdkPath = @"./libs/dahua/libdhnetsdk.so";
#endif
private const bool Debug = true;
@ -40,6 +33,17 @@ public static class DaHuaOriSdk
bool ret = CLIENT_InitEx(null, IntPtr.Zero, IntPtr.Zero);
InitSuccess = ret;
if (!ret) throw new Exception("DaHuaOriSdk global init failure.");
//打开日志
if (Debug)
{
NET_LOG_SET_PRINT_INFO logInfo = new()
{
dwSize = (uint)Marshal.SizeOf(typeof(NET_LOG_SET_PRINT_INFO)),
bSetFilePath = 1,
szLogFilePath = Path.Combine("./log", "dahuaSdkLog", "sdk_log.log")
};
CLIENT_LogOpen(ref logInfo);
}
return ret;
}
@ -1265,6 +1269,86 @@ public static class DaHuaOriSdk
#region Sdk Struct
// SDK日志回调
public delegate int fSDKLogCallBack(IntPtr lUploadFileHandle, uint nLogSize, IntPtr dwUser);
/// <summary>
/// SDK全局日志打印信息
/// SDK global log print
/// </summary>
public struct NET_LOG_SET_PRINT_INFO
{
public uint dwSize;
/// <summary>
/// 是否重设日志路径
/// reset log path
/// </summary>
public int bSetFilePath;
/// <summary>
/// 日志路径(默认"./sdk_log/sdk_log.log")
/// log path(default"./sdk_log/sdk_log.log")
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szLogFilePath;
/// <summary>
/// 是否重设日志文件大小
/// reset log size
/// </summary>
public int bSetFileSize;
/// <summary>
/// 每个日志文件的大小(默认大小10240), 单位:比特
/// each log file size(default size 10240), unit:bit
/// </summary>
public uint nFileSize;
/// <summary>
/// 是否重设日志文件个数
/// reset log file number
/// </summary>
public int bSetFileNum;
/// <summary>
/// 绕接日志文件个数(默认大小10)
/// log file quantity(default size 10)
/// </summary>
public uint nFileNum;
/// <summary>
/// 是否重设日志打印输出策略
/// reset log print strategy
/// </summary>
public int bSetPrintStrategy;
/// <summary>
/// 日志输出策略, 0:输出到文件(默认); 1:输出到窗口
/// log out strategy, 0: output to file(defualt); 1:output to window
/// </summary>
public uint nPrintStrategy;
/// <summary>
/// 字节对齐
/// Byte alignment
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] byReserved;
/// <summary>
/// 日志回调,需要将sdk日志回调出来时设置,默认为NULL
/// log callback, (default NULL)
/// </summary>
public fSDKLogCallBack cbSDKLogCallBack;
/// <summary>
/// 用户数据
/// UserData
/// </summary>
public IntPtr dwUser;
}
/// <summary>
/// CLIENT_LoginWithHighLevelSecurity 输入参数
/// </summary>
@ -1462,6 +1546,9 @@ public static class DaHuaOriSdk
[DllImport(LibSdkPath)]
public static extern void CLIENT_Cleanup();
[DllImport(LibSdkPath)]
public static extern bool CLIENT_LogOpen(ref NET_LOG_SET_PRINT_INFO pstLogPrintInfo);
[DllImport(LibSdkPath)]
public static extern int CLIENT_GetLastError();

40
EC.Util/CameraSDK/HiK/HiKOriSdk.cs

@ -1,8 +1,6 @@
//#define Win32
#define Win64
//#define Win
#define Linux64
//#define Linux32
//#define Linux64
using System.Runtime.InteropServices;
namespace EC.Util.CameraSDK;
@ -11,17 +9,13 @@ public class HiKOriSdk
{
#region Fields
#if Win32
public const string LibSdkPath = @"./libs/hik/win32/HCNetSDK.dll";
#elif Win64
public const string LibSdkPath = @"./libs/hik/win64/HCNetSDK.dll";
#elif Linux32
public const string LibSdkPath = @"./libs/hik/linux32/libhcnetsdk.so";
#elif Linux64
public const string LibSdkPath = @"./libs/hik/linux64/libhcnetsdk.so";
#if Win
public const string LibSdkPath = @"./libs/hik/HCNetSDK.dll";
#else
public const string LibSdkPath = @"./libs/hik/libhcnetsdk.so";
#endif
private const bool Debug = true;
private const bool Debug = false;
#endregion Fields
@ -293,8 +287,16 @@ public class HiKOriSdk
public struct NET_DVR_PREVIEWINFO
{
public int lChannel; //通道号
#if !Linux64
public uint dwStreamType; // 码流类型,0-主码流,1-子码流,2-码流3,3-码流4 等以此类推
#else
public ulong dwStreamType; // 码流类型,0-主码流,1-子码流,2-码流3,3-码流4 等以此类推
#endif
#if !Linux64
public uint dwLinkMode; // 0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-RTP/RTSP,5-RSTP/HTTP
#else
public ulong dwLinkMode; // 0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-RTP/RTSP,5-RSTP/HTTP
#endif
public IntPtr hPlayWnd; //播放窗口的句柄,为NULL表示不播放图象
public bool bBlocked; //0-非阻塞取流, 1-阻塞取流, 如果阻塞SDK内部connect失败将会有5s的超时才能够返回,不适合于轮询取流操作.
public bool bPassbackRecord; //0-不启用录像回传,1启用录像回传
@ -387,6 +389,18 @@ public class HiKOriSdk
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
public static extern int NET_DVR_RealPlay_V40(int iUserID, ref NET_DVR_PREVIEWINFO lpPreviewInfo, RealDataCallBack fRealDataCallBack_V30, IntPtr pUser);
/*********************************************************
Function: REALDATACALLBACK
Desc: (ص)
Input:
Output:
Return:
**********************************************************/
//public delegate void SETREALDATACALLBACK(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, uint dwUser);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
public static extern bool NET_DVR_SetRealDataCallBack(int lRealHandle, RealDataCallBack fRealDataCallBack, IntPtr pUser);
/*********************************************************
Function: NET_DVR_StopRealPlay
Desc:

78
EC.Util/CameraSDK/HiK/HiKSdk.cs

@ -66,7 +66,7 @@ public class HiKSdk : ICameraSdk
throw CameraException.New(CameraInfo, (int)errCode);
}
private void BuildPlayCtrlException(int nPort)
public void BuildPlayCtrlException(int nPort)
{
string err = $"PlayCtrlSdk failed, error code={PlayCtrlSdk.PlayM4_GetLastError(nPort)}";
throw CameraException.New(CameraInfo, -1, err);
@ -101,7 +101,7 @@ public class HiKSdk : ICameraSdk
Marshal.StructureToPtr(entity, ptrBuf, true);
try
{
ret = HiKOriSdk.NET_DVR_GetDVRConfig(LoginId, HiKOriSdk.NET_DVR_GET_PTZPOS, 0, ptrBuf, (uint)dwSize, ref dwReturned);
ret = HiKOriSdk.NET_DVR_GetDVRConfig(LoginId, HiKOriSdk.NET_DVR_GET_PTZPOS, Channel, ptrBuf, (uint)dwSize, ref dwReturned);
if (!ret) { BuildException(); return PtzInfo.Default; }
object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type);
if (objBuf == null) return PtzInfo.Default;
@ -126,7 +126,7 @@ public class HiKSdk : ICameraSdk
Marshal.StructureToPtr(entity, ptrBuf, true);
try
{
ret = HiKOriSdk.NET_DVR_GetDVRConfig(LoginId, HiKOriSdk.NET_DVR_GET_PTZPOS, 0, ptrBuf, (uint)dwSize, ref dwReturned);
ret = HiKOriSdk.NET_DVR_GetDVRConfig(LoginId, HiKOriSdk.NET_DVR_GET_PTZPOS, Channel, 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; }
@ -162,7 +162,7 @@ public class HiKSdk : ICameraSdk
private int RealplayHandle { get; set; } = -1;
private int RealpalyPort { get; set; } = -1;
private int RealplayPort { get; set; } = -1;
private IntPtr Hwnd { get; set; }
@ -180,7 +180,7 @@ public class HiKSdk : ICameraSdk
HiKOriSdk.NET_DVR_PREVIEWINFO previewInfo = new()
{
hPlayWnd = Hwnd, //预览窗口
lChannel = 1, //预览的设备通道
lChannel = Channel, //预览的设备通道
dwStreamType = 0, //码流类型:0-主码流,1-子码流,2-码流3,3-码流4,以此类推
dwLinkMode = 0, //连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
bBlocked = true, //0- 非阻塞取流,1- 阻塞取流
@ -192,50 +192,26 @@ public class HiKSdk : ICameraSdk
private void StartPlayLinux()
{
if (RealpalyPort < 0)
if (RealplayPort < 0 && RealDataCallBack == null)
{
int nPort = -1;
bool ret = PlayCtrlSdk.PlayM4_GetPort(ref nPort);
if (!ret) BuildPlayCtrlException(nPort);
RealpalyPort = nPort;
RealplayPort = nPort;
}
HiKOriSdk.NET_DVR_PREVIEWINFO previewInfo = new()
{
hPlayWnd = IntPtr.Zero, //预览窗口
lChannel = 1, //预览的设备通道
lChannel = Channel, //预览的设备通道
dwStreamType = 0, //码流类型:0-主码流,1-子码流,2-码流3,3-码流4,以此类推
dwLinkMode = 0, //连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
bBlocked = true, //0- 非阻塞取流,1- 阻塞取流
};
RealDataCallBack ??= DefaultRealDataCallBack;
RealplayHandle = HiKOriSdk.NET_DVR_RealPlay_V40(LoginId, ref previewInfo, RealDataCallBack, IntPtr.Zero);
if (RealplayHandle < 0) BuildException();
}
private void RealDataCallBack(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser)
{
if (dwBufSize <= 0) return;
switch (dwDataType)
{
case HiKOriSdk.NET_DVR_SYSHEAD:
try
{
PlayCtrlSdk.PlayM4_SetStreamOpenMode(RealpalyPort, 0);
PlayCtrlSdk.PlayM4_OpenStream(RealpalyPort, pBuffer, dwBufSize, 2 * 1024 * 1024);
if (!PlayCtrlSdk.PlayM4_Play(RealpalyPort, Hwnd)) BuildPlayCtrlException(RealpalyPort);
}
catch (Exception)
{
StopPlay();
throw;
}
break;
case HiKOriSdk.NET_DVR_STREAMDATA:
PlayCtrlSdk.PlayM4_InputData(RealpalyPort, pBuffer, dwBufSize);
break;
}
}
public override void StopPlay()
{
if (!IsPlaying()) return;
@ -244,28 +220,48 @@ public class HiKSdk : ICameraSdk
Hwnd = IntPtr.Zero;
}
public void StopPlayWindows()
private void StopPlayWindows()
{
bool ret = HiKOriSdk.NET_DVR_StopRealPlay(RealplayHandle);
RealplayHandle = -1;
if (!ret) BuildException();
}
public void StopPlayLinux()
private void StopPlayLinux()
{
bool ret = HiKOriSdk.NET_DVR_StopRealPlay(RealplayHandle);
RealplayHandle = -1;
if (RealpalyPort >= 0)
RealDataCallBack = null;
if (RealplayPort >= 0)
{
//if(!PlayCtrlSdk.PlayM4_Stop(RealpalyPort)) BuildPlayCtrlException(RealpalyPort);
PlayCtrlSdk.PlayM4_Stop(RealpalyPort);
PlayCtrlSdk.PlayM4_CloseStream(RealpalyPort);
PlayCtrlSdk.PlayM4_FreePort(RealpalyPort);
RealpalyPort = -1;
PlayCtrlSdk.PlayM4_Stop(RealplayPort);
PlayCtrlSdk.PlayM4_CloseStream(RealplayPort);
PlayCtrlSdk.PlayM4_FreePort(RealplayPort);
RealplayPort = -1;
}
if (!ret) BuildException();
}
public HiKOriSdk.RealDataCallBack? RealDataCallBack { get; set; }
private void DefaultRealDataCallBack(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser)
{
if (RealplayPort < 0 || dwBufSize <= 0) return;
switch (dwDataType)
{
case HiKOriSdk.NET_DVR_SYSHEAD:
PlayCtrlSdk.PlayM4_SetStreamOpenMode(RealplayPort, 0);
PlayCtrlSdk.PlayM4_OpenStream(RealplayPort, pBuffer, dwBufSize, 2 * 1024 * 1024);
PlayCtrlSdk.PlayM4_SetDisplayBuf(RealplayPort, 5);
if (!PlayCtrlSdk.PlayM4_Play(RealplayPort, Hwnd)) BuildPlayCtrlException(RealplayPort);
break;
case HiKOriSdk.NET_DVR_STREAMDATA:
PlayCtrlSdk.PlayM4_InputData(RealplayPort, pBuffer, dwBufSize);
break;
}
}
public override bool IsPlaying()
{
return RealplayHandle >= 0;

37
EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs

@ -1,8 +1,5 @@
//#define Win32
#define Win64
//#define Win
//#define Linux32
//#define Linux64
using System.Runtime.InteropServices;
namespace EC.Util.CameraSDK;
@ -11,14 +8,10 @@ public class PlayCtrlSdk
{
#region Fields
#if Win32
public const string LibSdkPath = @"./libs/hik/win32/PlayCtrl.dll";
#elif Win64
public const string LibSdkPath = @"./libs/hik/win64/PlayCtrl.dll";
#elif Linux32
public const string LibSdkPath = @"./libs/hik/linux32/libPlayCtrl.so";
#elif Linux64
public const string LibSdkPath = @"./libs/hik/linux64/libPlayCtrl.so";
#if Win
public const string LibSdkPath = @"./libs/hik/PlayCtrl.dll";
#else
public const string LibSdkPath = @"./libs/hik/libPlayCtrl.so";
#endif
#endregion Fields
@ -43,34 +36,34 @@ public class PlayCtrlSdk
#region Sdk Method
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_GetPort(ref int nPort);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_FreePort(int nPort);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern uint PlayM4_GetLastError(int nPort);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_SetStreamOpenMode(int nPort, uint nMode);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_OpenStream(int nPort, IntPtr pFileHeadBuf, uint nSize, uint nBufPoolSize);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_CloseStream(int nPort);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_SetDisplayBuf(int nPort, uint nNum);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_InputData(int nPort, IntPtr pBuf, uint nSize);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_Play(int nPort, IntPtr hWnd);
[DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)]
[DllImport(LibSdkPath)]
public static extern bool PlayM4_Stop(int nPort);
#endregion Sdk Method

26
EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs

@ -1,8 +1,5 @@
//#define Win32
#define Win64
//#define Win
//#define Linux32
//#define Linux64
using System.Runtime.InteropServices;
namespace EC.Util.CameraSDK;
@ -11,14 +8,10 @@ public class YuShiOriSdk
{
#region Fields
#if Win32
public const string LibSdkPath = @"./libs/yushi/win32/NetDEVSDK.dll";
#elif Win64
public const string LibSdkPath = @"./libs/yushi/win64/NetDEVSDK.dll";
#elif Linux32
public const string LibSdkPath = @"./libs/yushi/linux64/libNetDEVSDK.so";
#elif Linux64
public const string LibSdkPath = @"./libs/yushi/linux64/libNetDEVSDK.so";
#if Win
public const string LibSdkPath = @"./libs/yushi/NetDEVSDK.dll";
#else
public const string LibSdkPath = @"./libs/yushi/libNetDEVSDK.so";
#endif
private const bool Debug = true;
@ -40,6 +33,12 @@ public class YuShiOriSdk
bool ret = NETDEV_Init();
InitSuccess = ret;
if (!ret) throw new Exception("YuShiOriSdk global init failure.");
if (Debug)
{
string logPath = Path.Combine("./log", "yushiSdkLog");
Directory.CreateDirectory(logPath);
NETDEV_SetLogPath(logPath);
}
return ret;
}
@ -211,6 +210,9 @@ public class YuShiOriSdk
[DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern bool NETDEV_Cleanup();
[DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int NETDEV_SetLogPath(string strLogPath);
[DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int NETDEV_GetLastError();

23
EC.Util/CameraSDK/YuShi/YuShiSdk.cs

@ -1,4 +1,7 @@
namespace EC.Util.CameraSDK;
using System.Runtime.InteropServices;
using static EC.Util.CameraSDK.YuShiOriSdk;
namespace EC.Util.CameraSDK;
public class YuShiSdk : ICameraSdk
{
@ -20,15 +23,21 @@ public class YuShiSdk : ICameraSdk
{
bool ret = ConnectSuccess();
if (ret) return true;
YuShiOriSdk.NETDEV_DEVICE_LOGIN_INFO_S loginInfo = new();
loginInfo.szIPAddr = CameraInfo.Ip;
loginInfo.dwPort = CameraInfo.Port;
loginInfo.szUserName = CameraInfo.UserName;
loginInfo.szPassword = CameraInfo.Password;
YuShiOriSdk.NETDEV_DEVICE_LOGIN_INFO_S loginInfo = new()
{
szIPAddr = CameraInfo.Ip,
dwPort = CameraInfo.Port,
szUserName = CameraInfo.UserName,
szPassword = CameraInfo.Password
};
YuShiOriSdk.NETDEV_SELOG_INFO_S logInfo = new();
LoginId = YuShiOriSdk.NETDEV_Login_V30(ref loginInfo, ref logInfo);
ret = ConnectSuccess();
if(!ret) BuildException();
if (!ret) BuildException();
//别删 NETDEV_QueryVideoChlDetailList,否则 NETDEV_PTZPreset_Other 教做人
int pdwChlCount = 16;
IntPtr pstVideoChlList = Marshal.AllocHGlobal(pdwChlCount * Marshal.SizeOf(typeof(NETDEV_VIDEO_CHL_DETAIL_INFO_S)));
YuShiOriSdk.NETDEV_QueryVideoChlDetailList(LoginId, ref pdwChlCount, pstVideoChlList);
return ret;
}

8
EC.Util/Common/LogUnit.cs

@ -9,13 +9,13 @@ public class LogUnit
static LogUnit()
{
string fileName = Path.Combine("config", "log4net.config");
if (!File.Exists(fileName)) throw new FileNotFoundException(fileName);
XmlConfigurator.Configure(new FileInfo(fileName));
string filePath = Path.Combine("config", "log4net.config");
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
XmlConfigurator.Configure(new FileInfo(filePath));
logger = LogManager.GetLogger(typeof(LogUnit));
}
public static void Init()
public static void VirtualInit()
{ }
public static void Debug(string msg) => logger.Debug(msg);

12
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();
}

7
EC.Util/EC.Util.csproj

@ -16,10 +16,9 @@
<None Update="config\log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="libs\" />
<None Update="libs\cameraSdks.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

BIN
EC.Util/libs/cameraSdks.zip

Binary file not shown.

16
JiLinApp.Biz/TransmitAlarm/Service/AlarmMqttService.cs

@ -39,7 +39,6 @@ public class AlarmMqttService : IAlarmService
public AlarmMqttService(MqttConfig config)
{
MqttFactory factory = new();
ClientOptions = factory.CreateClientOptionsBuilder()
.WithTcpServer(config.Ip, config.Port)
.WithClientId(config.ClientId)
@ -50,21 +49,6 @@ public class AlarmMqttService : IAlarmService
SubscribeOptions = factory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(f => f.WithTopic(config.SubCmdTopic))
.Build();
//if (config.Local)
//{
// ServerOptions = factory.CreateServerOptionsBuilder()
// .WithDefaultEndpoint()
// .WithDefaultEndpointPort(config.Port)
// .Build();
// MqttServer server = factory.CreateMqttServer(ServerOptions);
// server.ValidatingConnectionAsync += Server_ValidatingConnectionAsync;
// server.ClientConnectedAsync += Server_ClientConnectedAsync;
// server.ClientDisconnectedAsync += Server_ClientDisconnectedAsync;
// server.ClientAcknowledgedPublishPacketAsync += Server_ClientAcknowledgedPublishPacketAsync;
// Server = server;
//}
IMqttClient client = factory.CreateMqttClient();
client.ApplicationMessageReceivedAsync += Client_ApplicationMessageReceivedAsync;

9
JiLinApp.Docking/Alarm/AlarmCodeHelper.cs

@ -14,7 +14,9 @@ public class AlarmCodeHelper
static AlarmCodeHelper()
{
using StreamReader r = new(Path.Combine("config", "alarmcode.json"));
string filePath = Path.Combine("config", "alarmcodes.json");
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
using StreamReader r = new(filePath);
string jsonStr = r.ReadToEnd();
List<AlarmCode> list = JsonConvert.DeserializeObject<List<AlarmCode>>(jsonStr) ?? new();
AlarmCodeDict = list.ToDictionary(item => item.Id, item => item);
@ -22,9 +24,8 @@ public class AlarmCodeHelper
TypeList = list.GroupBy(item => item.Type).Select(it => it.First().Type).ToList();
}
public static void Init()
{
}
public static void VirtualInit()
{ }
public static AlarmCode Get(string id)
{

2
JiLinApp.Docking/JiLinApp.Docking.csproj

@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<None Update="config\alarmcode.json">
<None Update="config\alarmcodes.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

9
JiLinApp.Docking/Ptz/PtzCmd.cs

@ -162,7 +162,8 @@ public class PtzCameraCmd
public static void HikPtzMove(ICameraSdk sdk, PtzCmdType cmdType, int[] args)
{
int stop = args[0], presetId = args[0];
int speed = (HiKOriSdk.PtzSpeedMin + HiKOriSdk.PtzSpeedMax) / 2;
int speed = HiKOriSdk.PtzSpeedMax;
//int speed = (HiKOriSdk.PtzSpeedMin + HiKOriSdk.PtzSpeedMax) / 2;
switch (cmdType)
{
case PtzCmdType.Left:
@ -237,7 +238,8 @@ public class PtzCameraCmd
private static void DaHuaPtzMove(ICameraSdk sdk, PtzCmdType cmdType, int[] args)
{
int stop = args[0], presetId = args[0];
int speed = (DaHuaOriSdk.PtzSpeedMin + DaHuaOriSdk.PtzSpeedMax) / 2;
int speed = DaHuaOriSdk.PtzSpeedMax;
//int speed = (DaHuaOriSdk.PtzSpeedMin + DaHuaOriSdk.PtzSpeedMax) / 2;
switch (cmdType)
{
case PtzCmdType.Left:
@ -312,7 +314,8 @@ public class PtzCameraCmd
public static void YuShiPtzMove(ICameraSdk sdk, PtzCmdType cmdType, int[] args)
{
int stop = args[0], presetId = args[0];
int speed = (YuShiOriSdk.PtzSpeedMax + YuShiOriSdk.PtzSpeedMin) / 2;
int speed = YuShiOriSdk.PtzSpeedMax;
//int speed = (YuShiOriSdk.PtzSpeedMax + YuShiOriSdk.PtzSpeedMin) / 2;
switch (cmdType)
{
case PtzCmdType.Left:

3
JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs

@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
namespace JiLinApp.Docking.VibrateAlarm;
@ -67,7 +68,7 @@ public class AsyncTcpServer : IDisposable
Encoding = Encoding.Default;
Listener = new(address, port);
Clients = new();
Listener.AllowNatTraversal(true);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) Listener.AllowNatTraversal(true);
}
~AsyncTcpServer()

2
JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs

@ -676,7 +676,7 @@ public class TcpManager
if (send) break;
Thread.Sleep(SendTryInterval);
}
Console.WriteLine("Send to {0}:{1} => {2}, send:{3}", clientMsg.ClientIp, clientMsg.ClientPort, cmd, send);
Console.WriteLine("Send to {0}:{1} => {2}, {3}", clientMsg.ClientIp, clientMsg.ClientPort, cmd, send);
return send;
}

0
JiLinApp.Docking/config/alarmcode.json → JiLinApp.Docking/config/alarmcodes.json

18
JiLinApp/App.axaml

@ -1,10 +1,18 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:icons="using:Material.Icons.Avalonia"
xmlns:styles="using:Material.Styles.Themes"
x:Class="JiLinApp.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<icons:MaterialIconStyles />
<styles:MaterialTheme BaseTheme="Light" PrimaryColor="Blue" SecondaryColor="Lime" />
<Style Selector="ComboBox">
<Setter Property="MaxDropDownHeight" Value="350" />
</Style>
<Style Selector="ComboBox:disabled Border[Name=PART_Border]">
<Setter Property="BorderBrush" Value="#a6a6a6" />
</Style>
</Application.Styles>
</Application>

156
JiLinApp/Components/CameraRealPlay.axaml

@ -0,0 +1,156 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:components="using:JiLinApp.Components"
xmlns:controls="using:Material.Styles.Controls"
xmlns:assists="using:Material.Styles.Assists"
xmlns:icons="using:Material.Icons.Avalonia"
x:Class="JiLinApp.Components.CameraRealPlay"
Icon="/Assets/avalonia-logo.ico"
Title="RealPlay" Width="769" Height="397">
<Window.Styles>
<!--PtzCtrlPanel-->
<Style Selector="#RealPlayGrid StackPanel[Name=PtzCtrlPanel]">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel Button">
<Setter Property="Width" Value="32" />
<Setter Property="Height" Value="32" />
<Setter Property="Padding" Value="0" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel[(Grid.Column)=1] TextBlock">
<Setter Property="Foreground" Value="#2b1f19" />
<Setter Property="FontSize" Value="13" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel:nth-child(n+10) Button">
<Setter Property="Width" Value="27" />
<Setter Property="Height" Value="27" />
<Setter Property="Padding" Value="0" />
</Style>
<Style Selector="#PtzCtrlPanel ComboBox[Name=PresetIdCbx] > StackPanel">
<Setter Property="Width" Value="100" />
</Style>
<Style Selector="#PtzCtrlPanel Button[Name=GotoBtn]">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="30" />
</Style>
<Style Selector="Window Panel#PART_MinimiseButton">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="Window Panel#PART_RestoreButton">
<Setter Property="IsVisible" Value="False" />
</Style>
</Window.Styles>
<Grid Name="RealPlayGrid" ColumnDefinitions="640*,114*">
<DockPanel Grid.Column="0">
<components:NativeHost Name="player" />
</DockPanel>
<StackPanel Name="PtzCtrlPanel" Grid.Column="1">
<Grid RowDefinitions="35,35,35,5,30,30,30,5,60,35" ColumnDefinitions="35,35,35">
<StackPanel Grid.Row="0" Grid.Column="0">
<Button Name="LeftTopBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowTopLeft" />
</Button>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1">
<Button Name="TopBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowTop" />
</Button>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="2">
<Button Name="RightTopBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowTopRight" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Name="LeftBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowLeft" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1">
<Button Name="AutoMoveBtn" Classes="Outline">
<icons:MaterialIcon Kind="Circle" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2">
<Button Name="RightBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowRight" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0">
<Button Name="LeftDownBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowBottomLeft" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="1">
<Button Name="DownBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowBottom" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="2">
<Button Name="RightDownBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowBottomRight" />
</Button>
</StackPanel>
<StackPanel Grid.Row="4" Grid.Column="0">
<Button Name="ZoomInBtn" Classes="Outline">
<icons:MaterialIcon Kind="PlusBold" />
</Button>
</StackPanel>
<StackPanel Grid.Row="4" Grid.Column="1">
<TextBlock Text="调焦" />
</StackPanel>
<StackPanel Grid.Row="4" Grid.Column="2">
<Button Name="ZoomOutBtn" Classes="Outline">
<icons:MaterialIcon Kind="MinusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="5" Grid.Column="0">
<Button Name="FocusNearBtn" Classes="Outline">
<icons:MaterialIcon Kind="PlusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="5" Grid.Column="1">
<TextBlock Text="聚焦" />
</StackPanel>
<StackPanel Grid.Row="5" Grid.Column="2">
<Button Name="FocusFarBtn" Classes="Outline">
<icons:MaterialIcon Kind="MinusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="6" Grid.Column="0">
<Button Name="IrisOpenBtn" Classes="Outline">
<icons:MaterialIcon Kind="PlusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="6" Grid.Column="1">
<TextBlock Text="光焦" />
</StackPanel>
<StackPanel Grid.Row="6" Grid.Column="2">
<Button Name="IrisCloseBtn" Classes="Outline">
<icons:MaterialIcon Kind="MinusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="8" Grid.ColumnSpan="3">
<ComboBox Name="PresetIdCbx" Classes="Outline" ContextFlyout="{x:Null}" assists:ComboBoxAssist.Label="预置点" />
</StackPanel>
<StackPanel Grid.Row="9" Grid.ColumnSpan="3">
<Button Name="GotoBtn" Classes="Light" Content="前往预置点" />
</StackPanel>
</Grid>
</StackPanel>
</Grid>
</Window>

180
JiLinApp/Components/CameraRealPlay.axaml.cs

@ -0,0 +1,180 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Threading;
using EC.Util.CameraSDK;
using JiLinApp.Core.Avalonia;
using JiLinApp.Docking.Ptz;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace JiLinApp.Components;
public partial class CameraRealPlay : Window
{
#region
private ICameraSdk? CameraSdk { get; set; }
#endregion
public CameraRealPlay()
{
InitializeComponent();
//绑定云台响应事件
List<Button> btnList = new();
ControlsUtil.FindChild(PtzCtrlPanel, ref btnList);
foreach (var btn in btnList)
{
if ((btn.Name ?? string.Empty).StartsWith("goto", StringComparison.OrdinalIgnoreCase)) btn.Click += GotoBtn_Click;
else
{
btn.AddHandler(PointerPressedEvent, PtzBtn_MouseDown, RoutingStrategies.Tunnel);
btn.AddHandler(PointerReleasedEvent, PtzBtn_MouseUp, RoutingStrategies.Tunnel);
}
}
// 初始化预置点 id 下拉框
for (int i = 1; i <= 10; i++) PresetIdCbx.Items.Add(new ComboBoxItem { Content = i });
PresetIdCbx.SelectedIndex = 0;
}
public CameraRealPlay(ICameraSdk cameraSdk) : this()
{
SetCameraSdk(cameraSdk);
}
#region Inner
public void SetCameraSdk(ICameraSdk cameraSdk)
{
CameraSdk = cameraSdk;
}
public IntPtr GetHwnd()
{
return player.Handle;
}
public bool IsClosed { get; private set; }
protected override void OnClosing(WindowClosingEventArgs e)
{
base.OnClosing(e);
IsClosed = true;
StopPlay();
}
#endregion Inner
#region Play
public void StartPlay()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
}
if (CameraSdk is HiKSdk hikSdk)
{
if (RealplayPort < 0)
{
int nPort = -1;
bool ret = PlayCtrlSdk.PlayM4_GetPort(ref nPort);
if (!ret) hikSdk.BuildPlayCtrlException(nPort);
RealplayPort = nPort;
}
hikSdk.RealDataCallBack = HikRealDataCallBack;
}
else if (CameraSdk is DaHuaSdk dhSdk)
{
}
else if (CameraSdk is YuShiSdk ysSdk)
{
}
CameraSdk?.StartPlay(player.Handle);
}
public void StopPlay()
{
Dispatcher.UIThread.InvokeAsync(() => CameraSdk?.StopPlay());
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
}
if (CameraSdk is HiKSdk hikSdk)
{
if (RealplayPort >= 0)
{
PlayCtrlSdk.PlayM4_Stop(RealplayPort);
PlayCtrlSdk.PlayM4_CloseStream(RealplayPort);
PlayCtrlSdk.PlayM4_FreePort(RealplayPort);
RealplayPort = -1;
}
hikSdk.RealDataCallBack = null;
}
else if (CameraSdk is DaHuaSdk dhSdk)
{
}
else if (CameraSdk is YuShiSdk ysSdk)
{
}
}
public bool IsPlaying()
{
return CameraSdk?.IsPlaying() ?? false;
}
private int RealplayPort { get; set; } = -1;
private void HikRealDataCallBack(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser)
{
if (RealplayPort < 0 || dwBufSize <= 0) return;
Dispatcher.UIThread.Invoke(() =>
{
switch (dwDataType)
{
case HiKOriSdk.NET_DVR_SYSHEAD:
PlayCtrlSdk.PlayM4_SetStreamOpenMode(RealplayPort, 0);
PlayCtrlSdk.PlayM4_OpenStream(RealplayPort, pBuffer, dwBufSize, 2 * 1024 * 1024);
PlayCtrlSdk.PlayM4_SetDisplayBuf(RealplayPort, 5);
PlayCtrlSdk.PlayM4_Play(RealplayPort, GetHwnd());
break;
case HiKOriSdk.NET_DVR_STREAMDATA:
PlayCtrlSdk.PlayM4_InputData(RealplayPort, pBuffer, dwBufSize);
break;
}
});
}
#endregion Play
#region ElementEvent
private void PtzBtn_MouseDown(object? sender, PointerPressedEventArgs e)
{
if (CameraSdk == null || sender is not Button btn) return;
PtzCmdType cmdType = PtzCmd.GetCmdType((btn.Name ?? string.Empty).Replace("Btn", ""));
int stop = 0;
PtzCameraCmd.PtzMove(CameraSdk, cmdType, new int[] { stop });
}
private void PtzBtn_MouseUp(object? sender, PointerReleasedEventArgs e)
{
if (CameraSdk == null || sender is not Button btn) return;
PtzCmdType cmdType = PtzCmd.GetCmdType((btn.Name ?? string.Empty).Replace("Btn", ""));
int stop = 1;
PtzCameraCmd.PtzMove(CameraSdk, cmdType, new int[] { stop });
}
private void GotoBtn_Click(object? sender, RoutedEventArgs e)
{
if (CameraSdk == null) return;
PtzCmdType cmdType = PtzCmdType.PresetGoto;
int presetId = int.Parse(PresetIdCbx.GetText());
PtzCameraCmd.PtzMove(CameraSdk, cmdType, new int[] { presetId });
}
#endregion ElementEvent
}

5
JiLinApp/Core/Avalonia/NativeHost.cs → JiLinApp/Components/NativeHost.cs

@ -2,12 +2,15 @@
using Avalonia.Platform;
using System;
namespace JiLinApp.Core.Avalonia;
namespace JiLinApp.Components;
public class NativeHost : NativeControlHost
{
public IntPtr Handle { get; set; }
public NativeHost() : base()
{ }
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
{
IPlatformHandle? pfHandle = base.CreateNativeControlCore(parent);

22
JiLinApp/Core/App/Config.cs

@ -0,0 +1,22 @@
using JiLinApp.Biz.TransmitAlarm;
using JiLinApp.Docking.Military;
using JiLinApp.Docking.Ptz;
using System.Collections.Generic;
namespace JiLinApp.Core;
public class AppConfig
{
public BaseConfig Base { get; set; }
public MilitaryConfig Military { get; set; }
public AlarmPlatformConfig AlarmPlatform { get; set; }
}
public class BaseConfig
{
public bool Console { get; set; }
public List<PtzControlTypeConfig> PtzCtrlTypes { get; set; }
}

65
JiLinApp/Core/App/Global.cs

@ -0,0 +1,65 @@
using EC.Util.CameraSDK;
using EC.Util.Common;
using JiLinApp.Biz.TransmitAlarm;
using JiLinApp.Docking.Alarm;
using JiLinApp.Docking.Military;
using JiLinApp.Docking.Ptz;
using NewLife.Configuration;
using System;
using System.IO;
namespace JiLinApp.Core;
public static class Global
{
#region Fields
private static JsonConfigProvider ConfigProvider { get; }
public static AppConfig AppConfig { get; }
public static MilitaryService MilitaryService { get; }
public static IAlarmService AlarmService { get; }
#endregion Fields
static Global()
{
try
{
// Config
string filePath = Path.Combine("config", "appconfig.json");
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
ConfigProvider = new() { FileName = filePath };
AppConfig = new AppConfig();
ConfigProvider.Bind(AppConfig);
// BaseConfig
BaseConfig baseConfig = AppConfig.Base;
// ptzCtrlTypes
PtzControlTypeConfigHelper.Init(baseConfig.PtzCtrlTypes);
// MilitaryConfig
MilitaryConfig militaryConfig = AppConfig.Military;
MilitaryService = new MilitaryService(militaryConfig);
// AlarmPlatformConfig
AlarmPlatformConfig alarmPlatformConfig = AppConfig.AlarmPlatform;
AlarmService = AlarmServiceFactory.CreateService(alarmPlatformConfig);
AlarmService.Start();
}
catch (Exception)
{
throw;
}
}
public static void Init()
{
// 静态类在调用之后才会初始化静态类构造方法
CameraFactory.VirtualInit();
LogUnit.VirtualInit();
AlarmCodeHelper.VirtualInit();
}
}

13
JiLinApp/Core/App/MessageEvent.cs

@ -0,0 +1,13 @@
using JiLinApp.Docking.FenceAlarm;
using JiLinApp.Docking.VibrateAlarm;
using ReactiveUI;
using System;
namespace JiLinApp.Core.App;
public class MessageEvent
{
public static IObservable<TcpAlarmHostMessage> VibrateAlarm { get; } = MessageBus.Current.Listen<TcpAlarmHostMessage>("VibrateAlarm");
public static IObservable<UdpAlarmHostMessage> FenceAlarm { get; } = MessageBus.Current.Listen<UdpAlarmHostMessage>("FenceAlarm");
}

150
JiLinApp/Core/Avalonia/ControlsUtil.cs

@ -0,0 +1,150 @@
using Avalonia.Controls;
using Material.Dialog;
using Material.Dialog.Icons;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
namespace JiLinApp.Core.Avalonia;
public static class ControlsUtil
{
/// <summary>
/// 查找子控件
/// </summary>
/// <typeparam name="T">控件类型</typeparam>
/// <param name="parent">父控件依赖对象</param>
/// <param name="list">子控件列表</param>
public static void FindChild<T>(Control parent, ref List<T> list) where T : Control
{
if (parent == null) return;
if (parent is Panel pParent)
{
foreach (Control item in pParent.Children)
{
if (item is Panel panel) FindChild(panel, ref list);
else if (item is T child) list.Add(child);
}
}
else if (parent is Decorator dParent)
{
if (dParent.Child != null) FindChild(dParent.Child, ref list);
}
}
public static Window? GetWindow(Control control)
{
Control? temp = control;
while (temp?.Parent != null)
{
if (temp.Parent is Window w) return w;
else temp = temp.Parent is Control c ? c : null;
}
return null;
}
public static bool TryGetWindow(Control control, out Window? window)
{
Control? temp = control;
window = null;
while (temp?.Parent != null)
{
if (temp.Parent is Window w) { window = w; break; }
else temp = temp.Parent is Control c ? c : null;
}
return window != null;
}
public static string GetText(this TextBox tb)
{
return tb.Text ?? string.Empty;
}
public static bool GetText(this TextBox tb, out string? text)
{
text = tb.Text;
return text != null;
}
public static string GetText(this ComboBox cbx)
{
string? val = string.Empty;
var item = cbx.SelectedItem;
if (item == null) return val;
if (item is Control)
{
if (TryGetValue(item, "Content", out object? content)) val = content?.ToString();
else if (TryGetValue(item, "Text", out string? text)) val = text;
}
else val = item.ToString();
return val ?? string.Empty;
}
public static bool GetText(this ComboBox cbx, out string? val)
{
var item = cbx.SelectedItem;
if (item is Control)
{
if (TryGetValue(item, "Content", out object? content)) val = content?.ToString();
else if (TryGetValue(item, "Text", out string? text)) val = text;
else val = null;
}
else val = item?.ToString();
return val != null;
}
/// <summary>
/// 利用反射来判断对象是否包含某个属性
/// </summary>
/// <param name="instance">object</param>
/// <param name="propertyName">需要判断的属性</param>
/// <returns>是否包含</returns>
private static bool ContainProperty(object instance, string propertyName)
{
if (instance == null || string.IsNullOrEmpty(propertyName)) return false;
PropertyInfo? property = instance.GetType()?.GetProperty(propertyName);
return property != null;
}
private static bool TryGetValue<T>(object instance, string propertyName, out T? val)
{
if (instance == null || string.IsNullOrEmpty(propertyName))
{
val = default;
return false;
}
PropertyInfo? property = instance.GetType()?.GetProperty(propertyName);
val = (T?)property?.GetValue(instance, null);
return val != null;
}
public static async Task<DialogResult> ShowDialog(Window? window, string title = "Info", string message = "")
{
if (window == null) return DialogResult.NoResult;
var dialog = DialogHelper.CreateAlertDialog(new AlertDialogBuilderParams
{
Borderless = true,
Width = 300,
DialogHeaderIcon = DialogIconKind.Info,
ContentHeader = title,
SupportingText = message,
StartupLocation = WindowStartupLocation.CenterOwner,
});
return await dialog.ShowDialog(window);
}
public static async Task<DialogResult> ShowWarnDialog(Window? window, string title = "Warning", string message = "")
{
if (window == null) return DialogResult.NoResult;
var dialog = DialogHelper.CreateAlertDialog(new AlertDialogBuilderParams
{
Borderless = true,
Width = 300,
DialogHeaderIcon = DialogIconKind.Warning,
ContentHeader = title,
SupportingText = message,
StartupLocation = WindowStartupLocation.CenterOwner,
});
return await dialog.ShowDialog(window);
}
}

30
JiLinApp/JiLinApp.csproj

@ -15,17 +15,35 @@
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia" Version="11.0.0-rc1.1" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-rc1.1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-rc1.1" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.0-rc1.1" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview8" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview8" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-rc1.1" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-rc1.1" />
<PackageReference Include="Material.Avalonia" Version="3.0.0-avalonia11-preview4.179-nightly" />
<PackageReference Include="Material.Icons.Avalonia" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EC.Util\EC.Util.csproj" />
<ProjectReference Include="..\JiLinApp.Biz\JiLinApp.Biz.csproj" />
<ProjectReference Include="..\JiLinApp.Docking\JiLinApp.Docking.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Pages\FenceServer\Fence.axaml.cs">
<DependentUpon>Fence.axaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="config\appconfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

54
JiLinApp/Pages/FenceServer/Fence.axaml

@ -0,0 +1,54 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Material.Styles.Controls"
xmlns:assists="using:Material.Styles.Assists"
x:Class="JiLinApp.Pages.FenceServer.Fence"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450">
<UserControl.Styles>
<Style Selector="#FencePanel TextBox">
<Setter Property="Width" Value="200" />
<Setter Property="UseFloatingWatermark" Value="True"/>
</Style>
<Style Selector="#FencePanel TextBlock[Name=PART_LabelText]">
<Setter Property="Foreground" Value="#272727"/>
</Style>
<Style Selector="#FencePanel TextBox:focus TextBlock[Name=PART_LabelText]">
<Setter Property="Foreground" Value="#2196f3"/>
</Style>
<Style Selector="#FencePanel StackPanel">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#FencePanel WrapPanel">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#FencePanel WrapPanel Button">
<Setter Property="Width" Value="100" />
<Setter Property="Margin" Value="5" />
</Style>
</UserControl.Styles>
<Grid Name="FencePanel" RowDefinitions="250,1*">
<Border Grid.Row="0" BorderBrush="#2196f3" BorderThickness="1">
<Grid RowDefinitions="30,1*">
<Border Grid.Row="0" Height="30" Background="#2196f3" Padding="8 0 0 0">
<TextBlock Text="优周围网服务器设置(UDP)" FontSize="14" VerticalAlignment="Center" MaxLines="1" Foreground="#F0F0F0" />
</Border>
<Grid Grid.Row="1" RowDefinitions="1*,1*,1*" ColumnDefinitions="1*,1*,1*">
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBox Name="ServerPortTb" Classes="outline" Text="23039" assists:TextFieldAssist.Label="本地端口" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBox Name="HeartKeepTb" Classes="outline" Text="10" assists:TextFieldAssist.Label="心跳间隔" />
</StackPanel>
<WrapPanel Grid.Row="2" Grid.Column="0">
<Button Name="OpenBtn" Classes="Light" Content="开启服务" Width="100" Margin="5" />
<Button Name="CloseBtn" Classes="Light" Content="关闭服务" Width="100" Margin="5" IsEnabled="False" />
</WrapPanel>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>

174
JiLinApp/Pages/FenceServer/Fence.axaml.cs

@ -0,0 +1,174 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using EC.Util.Common;
using JiLinApp.Biz.TransmitAlarm;
using JiLinApp.Core;
using JiLinApp.Core.App;
using JiLinApp.Core.Avalonia;
using JiLinApp.Docking.FenceAlarm;
using Material.Dialog;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace JiLinApp.Pages.FenceServer;
public partial class Fence : UserControl
{
#region Fields
private UdpManager Manager { get; } = new();
private IAlarmService AlarmService { get; set; }
private Window? _win;
private Window? Wdw
{
get
{
_win ??= ControlsUtil.GetWindow(this);
return _win;
}
}
#endregion Fields
public Fence()
{
InitializeComponent();
AlarmService = Global.AlarmService;
OpenBtn.Click += OpenBtn_Click;
CloseBtn.Click += CloseBtn_Click;
Manager.OnFenceUdpDeviceState += Manager_OnFenceUdpDeviceState;
Manager.OnFenceUdpSectorState += Manager_OnFenceUdpSensorState;
Manager.OnFenceUdpAlarm += Manager_OnFenceUdpAlarm;
AlarmService.OnFenceUdpSendDevices += AlarmService_OnFenceUdpSendDevices;
AlarmService.OnFenceUdpSendSensors += AlarmService_OnFenceUdpSendSensors;
}
#region ElementEvent
private void OpenBtn_Click(object? sender, RoutedEventArgs e)
{
if (Manager.IsRunning()) return;
try
{
UdpManagerConfig config = new()
{
ServerPort = int.Parse(ServerPortTb.GetText()),
DeviceHeartKeep = int.Parse(HeartKeepTb.GetText())
};
Manager.Start(config);
ChangeServerBtnState(true);
}
catch (Exception ex)
{
_ = ControlsUtil.ShowWarnDialog(Wdw, message: ex.ToString());
LogUnit.Error(ex);
}
}
private void CloseBtn_Click(object? sender, RoutedEventArgs e)
{
if (!Manager.IsRunning()) return;
try
{
Manager.Stop();
ChangeServerBtnState(false);
}
catch (Exception ex)
{
_ = ControlsUtil.ShowWarnDialog(Wdw, message: ex.ToString());
LogUnit.Error(ex);
}
}
private void ChangeServerBtnState(bool openState)
{
OpenBtn.IsEnabled = !openState;
CloseBtn.IsEnabled = openState;
}
#endregion ElementEvent
#region Manager Event
private void Manager_OnFenceUdpDeviceState(UdpAlarmHost device)
{
object deviceObj = new
{
id = device.DeviceId,
onlineState = device.OnlineState,
defenceState = device.DefenceState
};
AlarmService.SendDeviceState(DeviceType.Fence, deviceObj);
}
private void Manager_OnFenceUdpSensorState(SectorState sector)
{
object sensorObj = new
{
id = sector.Id,
deviceId = sector.DeviceId,
state = sector.State,
type = sector.Type,
};
AlarmService.SendSensorState(DeviceType.Fence, sensorObj);
}
private void Manager_OnFenceUdpAlarm(UdpAlarmHostMessage msg)
{
AlarmMessage alarm = msg.ToAlarmMessage();
AlarmService.SendAlarm(alarm);
MessageEvent.FenceAlarm.Publish(msg);
}
#endregion Manager Event
#region SubCmd Event
private void AlarmService_OnFenceUdpSendDevices(JObject reqObj)
{
List<object> respDeviceList = new();
List<UdpAlarmHost> deviceList = Manager.GetDeviceList();
foreach (var item in deviceList)
{
if (item.DeviceId <= 0) continue;
respDeviceList.Add(new
{
id = item.DeviceId,
//groupId = item.GroupId,
//userId = item.UserId,
onlineState = item.OnlineState,
defenceState = item.DefenceState
});
}
AlarmService.SendDevices(DeviceType.Fence, respDeviceList);
}
private void AlarmService_OnFenceUdpSendSensors(JObject reqObj)
{
int deviceId = reqObj.GetValue("deviceId", StringComparison.OrdinalIgnoreCase).ToInt();
List<SectorState> sectorList = Manager.TryGetDevice(deviceId, out UdpAlarmHost device) ? device.SectorDict.Values.ToList() : new();
List<object> respSensorList = new();
foreach (var item in sectorList)
{
respSensorList.Add(new
{
id = item.Id,
deviceId = item.DeviceId,
state = item.State,
type = item.Type,
});
}
AlarmService.SendSensors(DeviceType.Fence, deviceId, respSensorList);
}
#endregion SubCmd Event
}

10
JiLinApp/Pages/FenceServer/FenceViewModel.cs

@ -0,0 +1,10 @@
using JiLinApp.Core.Avalonia;
namespace JiLinApp.Pages.FenceServer;
public class FenceViewModel : ViewModelBase
{
public FenceViewModel()
{
}
}

89
JiLinApp/Pages/Main/MainWindow.axaml

@ -1,20 +1,91 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Material.Styles.Controls"
xmlns:icons="using:Material.Icons.Avalonia"
xmlns:vm="using:JiLinApp.Pages.Main"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:fenceServer="using:JiLinApp.Pages.FenceServer"
xmlns:ptzServer="using:JiLinApp.Pages.PtzServer"
xmlns:vibrateServer="using:JiLinApp.Pages.VibrateServer"
x:Class="JiLinApp.Pages.Main.MainWindow"
x:DataType="vm:MainWindowViewModel"
Width="900" Height="560" MinWidth="775" MinHeight="560"
Icon="/Assets/avalonia-logo.ico"
Title="JiLinApp">
<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<Window.Styles>
<Style Selector="TabControl">
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="Background" Value="#2196f3" />
</Style>
<Style Selector="TabControl /template/ ContentPresenter">
<Setter Property="Background" Value="#F0F0F0" />
<Setter Property="Padding" Value="4" />
</Style>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Style Selector="TabItem">
<Setter Property="FontSize" Value="12" />
<Setter Property="Width" Value="84" />
<Setter Property="Height" Value="63" />
<Setter Property="Background" Value="#2196f3" />
<Setter Property="Foreground" Value="#F0F0F0" />
</Style>
<Style Selector="TabItem TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
<Style Selector="TabItem /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="56" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="2 5 2 1" />
</Style>
<Style Selector="TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="#2B579A" />
<Setter Property="Background" Value="#F0F0F0" />
</Style>
<Style Selector="TabItem:focus /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="#2B579A" />
<Setter Property="Background" Value="#F0F0F0" />
</Style>
<Style Selector="TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="#2B579A" />
<Setter Property="Background" Value="#F0F0F0" />
</Style>
</Window.Styles>
</Window>
<TabControl>
<TabItem>
<TabItem.Header>
<StackPanel>
<icons:MaterialIcon Kind="CogOutline" Width="30" Height="30" />
<TextBlock Text="Ptz服务" />
</StackPanel>
</TabItem.Header>
<ptzServer:Ptz DataContext="{Binding PtzViewModel}" />
</TabItem>
<TabItem>
<TabItem.Header>
<StackPanel>
<icons:MaterialIcon Kind="Server" Width="30" Height="30" />
<TextBlock Text="震动报警服务" />
</StackPanel>
</TabItem.Header>
<vibrateServer:Vibrate DataContext="{Binding VibrateViewModel}" />
</TabItem>
<TabItem>
<TabItem.Header>
<StackPanel>
<icons:MaterialIcon Kind="Server" Width="30" Height="30" />
<TextBlock Text="优周围网服务" />
</StackPanel>
</TabItem.Header>
<fenceServer:Fence DataContext="{Binding FenceViewModel}"/>
</TabItem>
</TabControl>
</Window>

3
JiLinApp/Pages/Main/MainWindow.axaml.cs

@ -1,4 +1,7 @@
using Avalonia.Controls;
using JiLinApp.Pages.FenceServer;
using JiLinApp.Pages.PtzServer;
using JiLinApp.Pages.VibrateServer;
namespace JiLinApp.Pages.Main;

13
JiLinApp/Pages/Main/MainWindowViewModel.cs

@ -1,8 +1,19 @@
using JiLinApp.Core.Avalonia;
using JiLinApp.Pages.FenceServer;
using JiLinApp.Pages.PtzServer;
using JiLinApp.Pages.VibrateServer;
namespace JiLinApp.Pages.Main;
public class MainWindowViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia!";
#region Fields
internal PtzViewModel PtzViewModel { get; } = new();
internal VibrateViewModel VibrateViewModel { get; } = new();
internal FenceViewModel FenceViewModel { get; } = new();
#endregion Fields
}

261
JiLinApp/Pages/PtzServer/Ptz.axaml

@ -0,0 +1,261 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Material.Styles.Controls"
xmlns:assists="using:Material.Styles.Assists"
xmlns:icons="using:Material.Icons.Avalonia"
x:Class="JiLinApp.Pages.PtzServer.Ptz"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="550">
<UserControl.Styles>
<Style Selector="StackPanel">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<!--ComSetPanel-->
<Style Selector="#ComSetPanel ComboBox">
<Setter Property="Width" Value="200" />
</Style>
<Style Selector="#ComSetPanel WrapPanel">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#ComSetPanel WrapPanel Button">
<Setter Property="Width" Value="100" />
<Setter Property="Margin" Value="5" />
</Style>
<!--DeviceSetPanel-->
<Style Selector="#DeviceSetPanel ComboBox">
<Setter Property="Width" Value="200" />
</Style>
<Style Selector="#DeviceSetPanel Button">
<Setter Property="Width" Value="100" />
<Setter Property="Margin" Value="5" />
</Style>
<!--PtzSetPanel-->
<Style Selector="#PtzSetPanel ComboBox">
<Setter Property="Width" Value="160" />
</Style>
<!--PtzSelectPanel-->
<!--PtzCtrlPanel-->
<Style Selector="#PtzSetPanel Grid[Name=PtzCtrlPanel]">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel Button">
<Setter Property="Width" Value="45" />
<Setter Property="Height" Value="45" />
<Setter Property="Padding" Value="0" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel[(Grid.Column)=4] Button">
<Setter Property="Width" Value="35" />
<Setter Property="Height" Value="35" />
<Setter Property="Padding" Value="0" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel[(Grid.Column)=5] TextBlock">
<Setter Property="Foreground" Value="#2b1f19" />
</Style>
<Style Selector="#PtzCtrlPanel StackPanel[(Grid.Column)=6] Button">
<Setter Property="Width" Value="35" />
<Setter Property="Height" Value="35" />
<Setter Property="Padding" Value="0" />
</Style>
<!--PtzPresetPanel-->
<Style Selector="#PtzPresetPanel Button">
<Setter Property="Width" Value="105" />
<Setter Property="Margin" Value="5" />
</Style>
</UserControl.Styles>
<Grid RowDefinitions="300,250,auto" ColumnDefinitions="2*,1*">
<Border Name="ComSetPanel" Grid.Row="0" Grid.Column="0" BorderBrush="#2196f3" BorderThickness="1" Margin="0 0 4 0">
<Grid RowDefinitions="30,1*">
<Border Grid.Row="0" Height="30" Background="#2196f3" Padding="8 0 0 0">
<TextBlock Text="串口设置" FontSize="14" VerticalAlignment="Center" MaxLines="1" Foreground="#F0F0F0" />
</Border>
<Grid Grid.Row="1" RowDefinitions="1*,1*,1*,1*" ColumnDefinitions="1*,1*">
<StackPanel Grid.Row="0" Grid.Column="0">
<ComboBox Name="ComNameCbx" Classes="Outline" ContextFlyout="{x:Null}" assists:ComboBoxAssist.Label="本地端口" />
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1">
<ComboBox Name="BaudRateCbx" Classes="Outline" ContextFlyout="{x:Null}" SelectedIndex="3" assists:ComboBoxAssist.Label="波特率">
<ComboBoxItem Content="1200" />
<ComboBoxItem Content="2400" />
<ComboBoxItem Content="4800" />
<ComboBoxItem Content="9600" />
<ComboBoxItem Content="19200" />
</ComboBox>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<ComboBox Name="ParityCbx" Classes="Outline" ContextFlyout="{x:Null}" SelectedIndex="0" assists:ComboBoxAssist.Label="校验位">
<ComboBoxItem Content="无" />
</ComboBox>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1">
<ComboBox Name="DataBitsCbx" Classes="Outline" ContextFlyout="{x:Null}" SelectedIndex="4" assists:ComboBoxAssist.Label="数据位">
<ComboBoxItem Content="4" />
<ComboBoxItem Content="5" />
<ComboBoxItem Content="6" />
<ComboBoxItem Content="7" />
<ComboBoxItem Content="8" />
</ComboBox>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0">
<ComboBox Name="StopBitsCbx" Classes="Outline" ContextFlyout="{x:Null}" SelectedIndex="0" assists:ComboBoxAssist.Label="停止位">
<ComboBoxItem Content="1" />
<ComboBoxItem Content="1.5" />
<ComboBoxItem Content="2" />
</ComboBox>
</StackPanel>
<WrapPanel Grid.Row="2" Grid.Column="1">
<Button Name="OpenComBtn" Classes="Light" Content="连接" />
<Button Name="CloseComBtn" Classes="Light" Content="关闭" IsEnabled="False" />
</WrapPanel>
</Grid>
</Grid>
</Border>
<Border Name="DeviceSetPanel" Grid.Row="0" Grid.Column="1" BorderBrush="#2196f3" BorderThickness="1">
<Grid RowDefinitions="30,1*">
<Border Grid.Row="0" Height="30" Background="#2196f3" Padding="8 0 0 0">
<TextBlock Text="设备设置" FontSize="14" VerticalAlignment="Center" MaxLines="1" Foreground="#F0F0F0" />
</Border>
<Grid Grid.Row="1" RowDefinitions="1*,1*,1*,1*">
<StackPanel Grid.Row="0" Grid.Column="0">
<ComboBox Name="CameraIpCbx" Classes="Outline" ContextFlyout="{x:Null}" assists:ComboBoxAssist.Label="相机Ip" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Name="ShowLiveVideoBtn" Classes="Light" Content="实时视频" />
</StackPanel>
</Grid>
</Grid>
</Border>
<Border Name="PtzSetPanel" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" BorderBrush="#2196f3" BorderThickness="1" Margin="0 4 0 0">
<Grid RowDefinitions="30,1*">
<Border Grid.Row="0" Height="30" Background="#2196f3" Padding="8 0 0 0">
<TextBlock Text="云台控制" FontSize="14" VerticalAlignment="Center" MaxLines="1" Foreground="#F0F0F0" />
</Border>
<Grid Grid.Row="1" ColumnDefinitions="1*,2*,1*">
<Grid Name="PtzSelectPanel" Grid.Column="0" RowDefinitions="60,60,1*">
<StackPanel Grid.Row="0">
<ComboBox Name="ControlTypeNameCbx" Classes="Outline" ContextFlyout="{x:Null}" assists:ComboBoxAssist.Label="控制类型" />
</StackPanel>
<StackPanel Grid.Row="1">
<ComboBox Name="CameraIdCbx" Classes="Outline" ContextFlyout="{x:Null}" assists:ComboBoxAssist.Label="相机Id" />
</StackPanel>
</Grid>
<Grid Name="PtzCtrlPanel" Grid.Column="1" RowDefinitions="50,50,50,1*" ColumnDefinitions="50,50,50,15,40,40,40">
<StackPanel Grid.Row="0" Grid.Column="0">
<Button Name="LeftTopBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowTopLeft" />
</Button>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1">
<Button Name="TopBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowTop" />
</Button>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="2">
<Button Name="RightTopBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowTopRight" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Name="LeftBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowLeft" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1">
<Button Name="AutoMoveBtn" Classes="Outline">
<icons:MaterialIcon Kind="Circle" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2">
<Button Name="RightBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowRight" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0">
<Button Name="LeftDownBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowBottomLeft" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="1">
<Button Name="DownBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowBottom" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="2">
<Button Name="RightDownBtn" Classes="Outline">
<icons:MaterialIcon Kind="ArrowBottomRight" />
</Button>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="4">
<Button Name="ZoomInBtn" Classes="Outline">
<icons:MaterialIcon Kind="PlusBold" />
</Button>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="5">
<TextBlock Text="调焦" />
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="6">
<Button Name="ZoomOutBtn" Classes="Outline">
<icons:MaterialIcon Kind="MinusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="4">
<Button Name="FocusNearBtn" Classes="Outline">
<icons:MaterialIcon Kind="PlusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="5">
<TextBlock Text="聚焦" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="6">
<Button Name="FocusFarBtn" Classes="Outline">
<icons:MaterialIcon Kind="MinusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="4">
<Button Name="IrisOpenBtn" Classes="Outline">
<icons:MaterialIcon Kind="PlusThick" />
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="5">
<TextBlock Text="光焦" />
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="6">
<Button Name="IrisCloseBtn" Classes="Outline">
<icons:MaterialIcon Kind="MinusThick" />
</Button>
</StackPanel>
</Grid>
<Grid Name="PtzPresetPanel" Grid.Column="2" RowDefinitions="60,60,1*">
<StackPanel Grid.Row="0">
<ComboBox Name="PresetIdCbx" Classes="Outline" ContextFlyout="{x:Null}" assists:ComboBoxAssist.Label="预置点" />
</StackPanel>
<StackPanel Grid.Row="1">
<Button Name="GotoBtn" Classes="Light" Content="前往预置点" />
</StackPanel>
</Grid>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>

405
JiLinApp/Pages/PtzServer/Ptz.axaml.cs

@ -0,0 +1,405 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Threading;
using EC.Util.CameraSDK;
using EC.Util.Common;
using EC.Util.Port;
using JiLinApp.Biz.TransmitAlarm;
using JiLinApp.Components;
using JiLinApp.Core;
using JiLinApp.Core.App;
using JiLinApp.Core.Avalonia;
using JiLinApp.Docking.FenceAlarm;
using JiLinApp.Docking.Military;
using JiLinApp.Docking.Ptz;
using JiLinApp.Docking.VibrateAlarm;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
namespace JiLinApp.Pages.PtzServer;
public partial class Ptz : UserControl
{
#region Fields
private AppConfig AppConfig { get; } = Global.AppConfig;
private MilitaryService MilitaryService { get; } = Global.MilitaryService;
private List<CameraLinkageInfo> CameraLinkageList { get; set; } = new();
private ConcurrentDictionary<string, ICameraSdk> CameraSdkDict { get; } = new();
private ConcurrentDictionary<string, CameraRealPlay> RealPlayDict { get; } = new();
private Window? _win;
private Window? Wdw
{
get
{
_win ??= ControlsUtil.GetWindow(this);
return _win;
}
}
#endregion Fields
public Ptz()
{
InitializeComponent();
//绑定串口服务开关响应事件
OpenComBtn.Click += OpenComBtn_Click;
CloseComBtn.Click += CloseComBtn_Click;
//绑定实时视频响应事件
ShowLiveVideoBtn.Click += ShowLiveVideoBtn_Click;
//绑定云台响应事件
List<Button> btnList = new();
ControlsUtil.FindChild(PtzCtrlPanel, ref btnList);
foreach (var btn in btnList)
{
btn.AddHandler(PointerPressedEvent, PtzBtn_MouseDown, RoutingStrategies.Tunnel);
btn.AddHandler(PointerReleasedEvent, PtzBtn_MouseUp, RoutingStrategies.Tunnel);
}
GotoBtn.Click += GotoBtn_Click;
//绑定云台控制类型更改事件
ControlTypeNameCbx.SelectionChanged += ControlTypeName_SelectionChanged;
MessageEvent.VibrateAlarm.Subscribe(HandleVibrateAlarm);
MessageEvent.FenceAlarm.Subscribe(HandleFenceAlarm);
// 初始化串口下拉框
string[] ports = SerialPort.GetPortNames().OrderBy(s => int.Parse(Regex.Match(s, @"\d+").Value)).ToArray();
foreach (string port in ports) ComNameCbx.Items.Add(new ComboBoxItem { Content = port });
ComNameCbx.SelectedIndex = 0;
// 初始化相机 sdk 和相机 ip 下拉框
CameraLinkageList = MilitaryService.GetCameraLinkageList();
TaskUtil.RunCatch(() => Dispatcher.UIThread.Invoke(() => LoadCameraSdkDict(MilitaryService.GetCameraList())));
// 初始化云台控制类型下拉框
foreach (var cfg in AppConfig.Base.PtzCtrlTypes) ControlTypeNameCbx.Items.Add(new ComboBoxItem { Content = cfg.Name });
ControlTypeNameCbx.SelectedIndex = 0;
// 初始化相机 id 下拉框
for (int i = 1; i <= 5; i++) CameraIdCbx.Items.Add(new ComboBoxItem { Content = i });
CameraIdCbx.SelectedIndex = 0;
// 初始化预置点 id 下拉框
for (int i = 1; i <= 10; i++) PresetIdCbx.Items.Add(new ComboBoxItem { Content = i });
PresetIdCbx.SelectedIndex = 0;
}
#region Com
private YcSerialPort? Port { get; set; }
private bool IsComConnect()
{
return Port != null && Port.IsOpen();
}
private void ChangeState(bool openState)
{
OpenComBtn.IsEnabled = !openState;
CloseComBtn.IsEnabled = openState;
}
#endregion Com
#region Element Event
private void OpenComBtn_Click(object? sender, RoutedEventArgs e)
{
if (IsComConnect()) return;
try
{
SerialPortParam param = new()
{
ComName = ComNameCbx.GetText(),
BaudRate = int.Parse(BaudRateCbx.GetText()),
Parity = 0,
DataBits = int.Parse(DataBitsCbx.GetText()),
StopBits = int.Parse(StopBitsCbx.GetText()),
};
Port = new YcSerialPort(param);
Port.OpenCom();
ChangeState(true);
}
catch (Exception ex)
{
Port = null;
_ = ControlsUtil.ShowWarnDialog(Wdw, message: ex.ToString());
LogUnit.Error(ex);
}
}
private void CloseComBtn_Click(object? sender, RoutedEventArgs e)
{
if (!IsComConnect()) return;
try
{
Port?.CloseCom();
Port = null;
ChangeState(false);
}
catch (Exception ex)
{
_ = ControlsUtil.ShowWarnDialog(Wdw, message: ex.ToString());
LogUnit.Error(ex);
}
}
private void PtzBtn_MouseDown(object? sender, PointerPressedEventArgs e)
{
if (sender is not Button btn) return;
PtzControlType ctrlType = PtzControlTypeConfigHelper.GetControlType(ControlTypeNameCbx.GetText());
PtzCmdType cmdType = PtzCmd.GetCmdType((btn.Name ?? string.Empty).Replace("Btn", ""));
switch (ctrlType)
{
case PtzControlType.PelcoD:
case PtzControlType.PelcoP:
if (!IsComConnect()) break;
byte cameraId = byte.Parse(CameraIdCbx.GetText());
byte[] cmd = PtzComCmd.GetCmd(ctrlType, cmdType, new object[] { cameraId });
if (cmd != null && cmd.Length > 0) Port?.SendHex(cmd);
break;
case PtzControlType.DCamera:
if (!IsComConnect()) break;
cmd = PtzComCmd.GetCmd(ctrlType, cmdType);
if (cmd != null && cmd.Length > 0) Port?.SendHex(cmd);
break;
case PtzControlType.CameraSdk:
string cameraIp = CameraIpCbx.GetText();
CameraSdkDict.TryGetValue(cameraIp, out ICameraSdk? cameraSdk);
if (cameraSdk == null) break;
int stop = 0;
PtzCameraCmd.PtzMove(cameraSdk, cmdType, new int[] { stop });
break;
default: break;
}
}
private void PtzBtn_MouseUp(object? sender, PointerReleasedEventArgs e)
{
if (sender is not Button btn) return;
PtzControlType ctrlType = PtzControlTypeConfigHelper.GetControlType(ControlTypeNameCbx.GetText());
PtzCmdType cmdType = PtzCmdType.Stop;
switch (ctrlType)
{
case PtzControlType.PelcoD:
case PtzControlType.PelcoP:
if (!IsComConnect()) break;
byte cameraId = byte.Parse(CameraIdCbx.GetText());
byte[] cmd = PtzComCmd.GetCmd(ctrlType, cmdType, new object[] { cameraId });
if (cmd != null && cmd.Length > 0) Port?.SendHex(cmd);
break;
case PtzControlType.DCamera:
if (!IsComConnect()) break;
cmd = PtzComCmd.GetCmd(ctrlType, cmdType);
if (cmd != null && cmd.Length > 0) Port?.SendHex(cmd);
break;
case PtzControlType.CameraSdk:
string cameraIp = CameraIpCbx.GetText();
CameraSdkDict.TryGetValue(cameraIp, out ICameraSdk? cameraSdk);
if (cameraSdk == null) break;
cmdType = PtzCmd.GetCmdType((btn.Name ?? string.Empty).Replace("Btn", ""));
int stop = 1;
PtzCameraCmd.PtzMove(cameraSdk, cmdType, new int[] { stop });
break;
default: break;
}
}
private void GotoBtn_Click(object? sender, RoutedEventArgs e)
{
PtzControlType ctrlType = PtzControlTypeConfigHelper.GetControlType(ControlTypeNameCbx.GetText());
PtzCmdType cmdType = PtzCmdType.PresetGoto;
byte presetId = byte.Parse(PresetIdCbx.GetText());
switch (ctrlType)
{
case PtzControlType.PelcoD:
case PtzControlType.PelcoP:
if (!IsComConnect()) break;
byte cameraId = byte.Parse(CameraIdCbx.GetText());
byte[] cmd = PtzComCmd.GetCmd(ctrlType, cmdType, new object[] { cameraId, presetId });
if (cmd != null && cmd.Length > 0) Port?.SendHex(cmd);
break;
case PtzControlType.CameraSdk:
string cameraIp = CameraIpCbx.GetText();
CameraSdkDict.TryGetValue(cameraIp, out ICameraSdk? cameraSdk);
if (cameraSdk == null) break;
PtzCameraCmd.PtzMove(cameraSdk, cmdType, new int[] { presetId });
break;
default: break;
}
}
private void ShowLiveVideoBtn_Click(object? sender, RoutedEventArgs e)
{
string cameraIp = CameraIpCbx.GetText();
if (!CameraSdkDict.TryGetValue(cameraIp, out ICameraSdk? sdk)) return;
Dispatcher.UIThread.Invoke(() => ShowLiveVideo(sdk));
}
private void ControlTypeName_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (e.AddedItems[0] is not ComboBoxItem cbxItem) return;
string ctrlTypeName = cbxItem?.Content?.ToString() ?? string.Empty;
PtzControlType ctrlType = PtzControlTypeConfigHelper.GetControlType(ctrlTypeName);
switch (ctrlType)
{
case PtzControlType.PelcoD:
case PtzControlType.PelcoP:
case PtzControlType.DCamera:
CameraIdCbx.IsEnabled = true;
CameraIpCbx.IsEnabled = false;
ShowLiveVideoBtn.IsEnabled = false;
break;
case PtzControlType.CameraSdk:
CameraIdCbx.IsEnabled = false;
CameraIpCbx.IsEnabled = true;
ShowLiveVideoBtn.IsEnabled = true;
break;
default:
CameraIdCbx.IsEnabled = false;
CameraIpCbx.IsEnabled = false;
ShowLiveVideoBtn.IsEnabled = false;
break;
}
}
#endregion Element Event
#region Invoke Event
private void ShowLiveVideo(ICameraSdk sdk)
{
if (!sdk.ConnectSuccess()) { LogUnit.Error(this, $"CameraSdk(cameraIp:{sdk.CameraInfo.Ip}) connect failure."); return; }
if (!ControlsUtil.TryGetWindow(this, out Window? w)) return;
string cameraIp = sdk.CameraInfo.Ip;
RealPlayDict.TryGetValue(cameraIp, out CameraRealPlay? realPlay);
if (realPlay == null || realPlay.IsClosed)
{
realPlay = new(sdk)
{
Title = $"RealPlay({sdk.CameraInfo.Name})",
CanResize = false,
ShowInTaskbar = true
};
RealPlayDict[cameraIp] = realPlay;
}
realPlay.Show(w);
realPlay.WindowState = WindowState.Normal;
realPlay.StartPlay();
}
private void HandleVibrateAlarm(TcpAlarmHostMessage msg)
{
AlarmMessage alarm = msg.ToAlarmMessage();
TaskUtil.RunCatch(() => HandleAlarmInvoke(alarm));
}
private void HandleFenceAlarm(UdpAlarmHostMessage msg)
{
AlarmMessage alarm = msg.ToAlarmMessage();
TaskUtil.RunCatch(() => HandleAlarmInvoke(alarm));
}
private void HandleAlarmInvoke(AlarmMessage alarm)
{
if (alarm == null) return;
bool realPlay = AppConfig.AlarmPlatform.RealPlay;
string deviceId = alarm.LabelCode;
string sensorId = alarm.ChannelId;
List<CameraLinkageInfo> cameraLinkages = GetCameraLinkageList(sensorId);
foreach (var item in cameraLinkages)
{
if (item == null) { LogUnit.Error(this, $"CameraLinkageInfo(sensorId:{sensorId}) not found."); return; }
string cameraId = item.CameraId;
ICameraSdk? cameraSdk = GetCameraSdk(cameraId);
if (cameraSdk == null) { LogUnit.Error(this, $"CameraSdk(cameraId:{cameraId}) not found."); return; }
if (realPlay) Dispatcher.UIThread.Invoke(() => ShowLiveVideo(cameraSdk));
// TODO: 设计 PriorityQueue
int len = item.PresetIds.Length;
for (int i = 0; i < len; i++)
{
int presetId = item.PresetIds[i];
PtzCameraCmd.PtzMove(cameraSdk, PtzCmdType.PresetGoto, new int[] { presetId });
if (i != len) Thread.Sleep(5000);
}
}
}
#endregion Invoke Event
#region Military
private void LoadCameraSdkDict(List<CameraInfo> cameraList)
{
int tryTime = 5, tryInterval = 500;
for (int i = 0; i < cameraList.Count; i++)
{
CameraInfo info = cameraList[i];
string ip = info.Ip;
if (CameraSdkDict.ContainsKey(ip)) continue;
ICameraSdk sdk = CameraFactory.BuildCameraSdk(info);
CameraSdkDict[ip] = sdk;
CameraIpCbx.Items.Add(new ComboBoxItem { Content = ip });
TaskUtil.RunCatch(() =>
{
bool flag = false;
for (int i = 0; i < tryTime; i++)
{
flag = sdk.Init();
if (flag) break;
Thread.Sleep(tryInterval);
}
});
}
}
private CameraLinkageInfo? GetCameraLinkage(string sensorId)
{
foreach (var item in CameraLinkageList)
{
if (sensorId.Equals(item.SensorId)) return item;
}
return null;
}
private List<CameraLinkageInfo> GetCameraLinkageList(string sensorId)
{
List<CameraLinkageInfo> list = new();
foreach (var item in CameraLinkageList)
{
if (sensorId.Equals(item.SensorId)) list.Add(item);
}
return list;
}
private ICameraSdk? GetCameraSdk(string cameraId)
{
foreach (var item in CameraSdkDict.Values)
{
if (item == null) continue;
if (cameraId.Equals(item.CameraInfo.Id)) return item;
}
return null;
}
#endregion Military
}

10
JiLinApp/Pages/PtzServer/PtzViewModel.cs

@ -0,0 +1,10 @@
using JiLinApp.Core.Avalonia;
namespace JiLinApp.Pages.PtzServer;
public class PtzViewModel : ViewModelBase
{
public PtzViewModel()
{
}
}

54
JiLinApp/Pages/VibrateServer/Vibrate.axaml

@ -0,0 +1,54 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Material.Styles.Controls"
xmlns:assists="using:Material.Styles.Assists"
x:Class="JiLinApp.Pages.VibrateServer.Vibrate"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450">
<UserControl.Styles>
<Style Selector="#VibratePanel TextBox">
<Setter Property="Width" Value="200" />
<Setter Property="UseFloatingWatermark" Value="True"/>
</Style>
<Style Selector="#VibratePanel TextBlock[Name=PART_LabelText]">
<Setter Property="Foreground" Value="#272727"/>
</Style>
<Style Selector="#VibratePanel TextBox:focus TextBlock[Name=PART_LabelText]">
<Setter Property="Foreground" Value="#2196f3"/>
</Style>
<Style Selector="#VibratePanel StackPanel">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#VibratePanel WrapPanel">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="#VibratePanel WrapPanel Button">
<Setter Property="Width" Value="100" />
<Setter Property="Margin" Value="5" />
</Style>
</UserControl.Styles>
<Grid RowDefinitions="250,1*">
<Border Name="VibratePanel" Grid.Row="0" BorderBrush="#2196f3" BorderThickness="1">
<Grid RowDefinitions="30,1*">
<Border Grid.Row="0" Height="30" Background="#2196f3" Padding="8 0 0 0">
<TextBlock Text="震动报警服务器设置(TCP)" FontSize="14" VerticalAlignment="Center" MaxLines="1" Foreground="#F0F0F0" />
</Border>
<Grid Grid.Row="1" RowDefinitions="1*,1*,1*" ColumnDefinitions="1*,1*,1*">
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBox Name="ServerPortTb" Classes="outline" Text="9000" assists:TextFieldAssist.Label="本地端口" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBox Name="HeartKeepTb" Classes="outline" Text="10" assists:TextFieldAssist.Label="心跳间隔" />
</StackPanel>
<WrapPanel Grid.Row="2" Grid.Column="0">
<Button Name="OpenBtn" Classes="Light" Content="开启服务" Width="100" Margin="5" />
<Button Name="CloseBtn" Classes="Light" Content="关闭服务" Width="100" Margin="5" IsEnabled="False" />
</WrapPanel>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>

168
JiLinApp/Pages/VibrateServer/Vibrate.axaml.cs

@ -0,0 +1,168 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using EC.Util.Common;
using JiLinApp.Biz.TransmitAlarm;
using JiLinApp.Core;
using JiLinApp.Core.App;
using JiLinApp.Core.Avalonia;
using JiLinApp.Docking.VibrateAlarm;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace JiLinApp.Pages.VibrateServer;
public partial class Vibrate : UserControl
{
#region Fields
private TcpManager Manager { get; } = new();
private IAlarmService AlarmService { get; set; }
private Window? _win;
private Window? Wdw
{
get
{
_win ??= ControlsUtil.GetWindow(this);
return _win;
}
}
#endregion Fields
public Vibrate()
{
InitializeComponent();
AlarmService = Global.AlarmService;
OpenBtn.Click += OpenBtn_Click;
CloseBtn.Click += CloseBtn_Click;
Manager.OnVibrateTcpDeviceState += Manager_OnVibrateTcpDeviceState;
Manager.OnVibrateTcpSensorState += Manager_OnVibrateTcpSensorSate;
Manager.OnVibrateTcpAlarm += Manager_OnVibrateTcpAlarm;
AlarmService.OnVibrateTcpSendDevices += AlarmService_OnVibrateTcpSendDevices;
AlarmService.OnVibrateTcpSendSensors += AlarmService_OnVibrateTcpSendSensors;
}
#region ElementEvent
private void OpenBtn_Click(object? sender, RoutedEventArgs e)
{
if (Manager.IsRunning()) return;
try
{
TcpManagerConfig config = new()
{
ServerPort = int.Parse(ServerPortTb.GetText()),
DeviceHeartKeep = int.Parse(HeartKeepTb.GetText())
};
Manager.Start(config);
ChangeServerBtnState(true);
}
catch (Exception ex)
{
_ = ControlsUtil.ShowWarnDialog(Wdw, message: ex.ToString());
LogUnit.Error(ex);
}
}
private void CloseBtn_Click(object? sender, RoutedEventArgs e)
{
if (!Manager.IsRunning()) return;
try
{
Manager.Stop();
ChangeServerBtnState(false);
}
catch (Exception ex)
{
_ = ControlsUtil.ShowWarnDialog(Wdw, message: ex.ToString());
LogUnit.Error(ex);
}
}
private void ChangeServerBtnState(bool openState)
{
OpenBtn.IsEnabled = !openState;
CloseBtn.IsEnabled = openState;
}
#endregion ElementEvent
#region Manager Event
private void Manager_OnVibrateTcpDeviceState(ClientMessage device)
{
object deviceObj = new
{
id = device.Host.Id,
onlineState = device.OnlineState
};
AlarmService.SendDeviceState(DeviceType.Vibrate, deviceObj);
}
private void Manager_OnVibrateTcpSensorSate(SensorState sensor)
{
object sensorObj = new
{
id = sensor.Addr,
deviceId = sensor.DeviceId,
onlineState = sensor.OnlineState,
alarmState = sensor.AlarmState,
};
AlarmService.SendSensorState(DeviceType.Vibrate, sensorObj);
}
private void Manager_OnVibrateTcpAlarm(TcpAlarmHostMessage msg)
{
AlarmMessage alarm = msg.ToAlarmMessage();
AlarmService.SendAlarm(alarm);
MessageEvent.VibrateAlarm.Publish(msg);
}
#endregion Manager Event
#region SubCmd Event
private void AlarmService_OnVibrateTcpSendDevices(JObject reqObj)
{
List<object> respDeviceList = new();
List<ClientMessage> deviceList = Manager.GetDeviceList();
foreach (var item in deviceList)
{
if (item.Host.Id <= 0) continue;
respDeviceList.Add(new
{
id = item.Host.Id,
onlineState = item.OnlineState
});
}
AlarmService.SendDevices(DeviceType.Vibrate, respDeviceList);
}
private void AlarmService_OnVibrateTcpSendSensors(JObject reqObj)
{
int deviceId = reqObj.GetValue("deviceId", StringComparison.OrdinalIgnoreCase).ToInt();
List<SensorState> sensorList = Manager.TryGetDevice(deviceId, out ClientMessage device) ? device.SensorDict.Values.ToList() : new();
List<object> respSensorList = new();
foreach (var item in sensorList)
{
respSensorList.Add(new
{
id = item.Addr,
deviceId = item.DeviceId,
onlineState = item.OnlineState,
alarmState = item.AlarmState,
});
}
AlarmService.SendSensors(DeviceType.Vibrate, deviceId, respSensorList);
}
#endregion SubCmd Event
}

10
JiLinApp/Pages/VibrateServer/VibrateViewModel.cs

@ -0,0 +1,10 @@
using JiLinApp.Core.Avalonia;
namespace JiLinApp.Pages.VibrateServer;
public class VibrateViewModel : ViewModelBase
{
public VibrateViewModel()
{
}
}

12
JiLinApp/Program.cs

@ -2,6 +2,7 @@
using Avalonia.Media;
using Avalonia.ReactiveUI;
using EC.Util.Common;
using JiLinApp.Core;
using JiLinApp.Core.Avalonia;
using ReactiveUI;
using System;
@ -17,7 +18,7 @@ internal class Program
[STAThread]
public static void Main(string[] args)
{
GlobalInit();
Init();
try
{
AppBuilder appBuilder = BuildAvaloniaApp();
@ -52,10 +53,10 @@ internal class Program
#region extend
private static void GlobalInit()
private static void Init()
{
// Active to initialize
LogUnit.Init();
Global.Init();
// HandleExceptions
// Exceptions from another thread
@ -73,10 +74,5 @@ internal class Program
RxApp.DefaultExceptionHandler = RxAppExceptionHandler.Instance;
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
throw new NotImplementedException();
}
#endregion extend
}

50
JiLinApp/config/appconfig.json

@ -0,0 +1,50 @@
{
"base": {
"console": true,
"ptzCtrlTypes": [
{
"type": "CameraSdk",
"Name": "协议HDY"
},
{
"type": "PelcoD",
"Name": "协议Pd"
},
{
"type": "PelcoP",
"Name": "协议Pp"
},
{
"type": "DCamera",
"Name": "协议D508"
}
]
},
"military": {
"url": "http://192.168.1.119:8080/military",
"username": "admin",
"password": "123456",
"requestTryTime": "5",
"requestTryInterval": "200"
},
"alarmPlatform": {
"realPlay": true,
"type": "mqtt",
"mqtt": {
"ip": "192.168.1.200",
"port": "1883",
"username": "admin",
"password": "public",
"clientId": "alarm-platform1",
"retryTime": "5",
"retryInterval": "200",
"pubDevicesTopic": "PPS/Devices",
"pubSensorsTopic": "PPS/Sensors",
"pubDeviceStateTopic": "PPS/DeviceState",
"pubSensorStateTopic": "PPS/SensorState",
"pubAlarmTopic": "PPS/AlarmMessage/Fence",
"subCmdTopic": "PPS/Cmd"
}
}
}
Loading…
Cancel
Save