From b9c2ff0398a463b3ced114466ea171a2a6f7e912 Mon Sep 17 00:00:00 2001
From: fajiao <1519100073@qq.com>
Date: Wed, 12 Oct 2022 14:46:46 +0800
Subject: [PATCH] feat: commit some changes
---
Cis.Application/Cb/Common/CbInfo.cs | 2 +-
Cis.Application/Cb/Service/CbCameraService.cs | 3 +-
Cis.Application/Cis.Application.xml | 121 ++++++++++-
Cis.Application/Cm/Common/CmInfo.cs | 12 +-
Cis.Application/Cm/Entity/CmMarkLabel.cs | 52 +++++
.../Cm/Service/CmMarkGroupService.cs | 42 +++-
.../Cm/Service/CmMarkLabelService.cs | 55 +++++
Cis.Application/Core/Algo/MarkSearcherBase.cs | 6 +
Cis.Application/Core/Api/IPtzApi.cs | 9 +
Cis.Application/Core/Api/PtzServerApi.cs | 198 ++++++++++++++++++
.../Core/Center/CameraDataCenter.cs | 113 ++++++++++
Cis.Application/Core/Common/Options.cs | 22 ++
Cis.Application/Core/Entity/CameraCalcInfo.cs | 32 +++
.../Core/Service/MarkSearchService.cs | 22 ++
Cis.Application/GlobalUsings.cs | 1 +
Cis.Application/Startup.cs | 14 +-
Cis.Application/Sys/Common/SysInfo.cs | 2 +-
.../Sys/Service/SysDictDataService.cs | 41 +++-
.../Sys/Service/SysDictTypeService.cs | 43 +++-
Cis.Application/Tb/Common/TbInfo.cs | 29 +++
Cis.Application/Tb/Entity/TbPtzCamera.cs | 28 +++
.../Tb/Service/TbPtzCameraService.cs | 51 +++++
Cis.Core/Cis.Core.csproj | 5 +
Cis.Core/Cis.Core.xml | 168 +++++++--------
Cis.Core/CoreConfig.json | 49 ++++-
.../{Util => Entity}/RespParamProvider.cs | 0
Cis.Web.Core/Startup.cs | 22 ++
Cis.Web.Entry/Cis.Web.Entry.csproj | 4 -
Cis.Web.Entry/wwwroot/images/logo.png | Bin 0 -> 25808 bytes
29 files changed, 1034 insertions(+), 112 deletions(-)
create mode 100644 Cis.Application/Cm/Entity/CmMarkLabel.cs
create mode 100644 Cis.Application/Cm/Service/CmMarkLabelService.cs
create mode 100644 Cis.Application/Core/Algo/MarkSearcherBase.cs
create mode 100644 Cis.Application/Core/Api/IPtzApi.cs
create mode 100644 Cis.Application/Core/Api/PtzServerApi.cs
create mode 100644 Cis.Application/Core/Center/CameraDataCenter.cs
create mode 100644 Cis.Application/Core/Common/Options.cs
create mode 100644 Cis.Application/Core/Entity/CameraCalcInfo.cs
create mode 100644 Cis.Application/Core/Service/MarkSearchService.cs
create mode 100644 Cis.Application/Tb/Common/TbInfo.cs
create mode 100644 Cis.Application/Tb/Entity/TbPtzCamera.cs
create mode 100644 Cis.Application/Tb/Service/TbPtzCameraService.cs
rename Cis.Core/{Util => Entity}/RespParamProvider.cs (100%)
create mode 100644 Cis.Web.Entry/wwwroot/images/logo.png
diff --git a/Cis.Application/Cb/Common/CbInfo.cs b/Cis.Application/Cb/Common/CbInfo.cs
index f64368f..b612a81 100644
--- a/Cis.Application/Cb/Common/CbInfo.cs
+++ b/Cis.Application/Cb/Common/CbInfo.cs
@@ -22,7 +22,7 @@ public class CbInfo
#region Database Info
///
- /// 数据库名
+ /// 数据库标识
///
public const string DbName = SqlSugarConst.DefaultConfigId;
diff --git a/Cis.Application/Cb/Service/CbCameraService.cs b/Cis.Application/Cb/Service/CbCameraService.cs
index 165cffb..156e370 100644
--- a/Cis.Application/Cb/Service/CbCameraService.cs
+++ b/Cis.Application/Cb/Service/CbCameraService.cs
@@ -1,5 +1,4 @@
using Newtonsoft.Json.Linq;
-using System.Runtime.InteropServices;
namespace Cis.Application.Cb;
@@ -22,7 +21,7 @@ public class CbCameraService : IDynamicApiController, ITransient
return entity;
}
- public async Task> GetList(string queryJson)
+ public async Task> GetList(string queryJson = "")
{
JObject queryObj = queryJson.ToJObject();
List list = await _cbCameraRep.AsQueryable()
diff --git a/Cis.Application/Cis.Application.xml b/Cis.Application/Cis.Application.xml
index 4107b98..45840b3 100644
--- a/Cis.Application/Cis.Application.xml
+++ b/Cis.Application/Cis.Application.xml
@@ -21,7 +21,7 @@
- 数据库名
+ 数据库标识
@@ -86,7 +86,7 @@
- 数据库名
+ 数据库标识
@@ -99,6 +99,16 @@
CmMarkGroup 表描述
+
+
+ CmMarkLabel 表名
+
+
+
+
+ CmMarkLabel 表描述
+
+
标记分组表
@@ -119,11 +129,106 @@
备注
+
+
+ 标记标签表
+
+
+
+
+ 名称
+
+
+
+
+ Pan 位置
+
+
+
+
+ Tilt 位置
+
+
+
+
+ Zoom 位置
+
+
+
+
+ 备注
+
+
+
+
+ 相机 Id
+
+
+
+
+ 标记组 Id
+
+
标记分组服务
+
+
+ 标记标签服务
+
+
+
+
+ Ptz 信息
+
+
+
+
+ Pan
+
+
+
+
+ Tilt
+
+
+
+
+ Zoom
+
+
+
+
+ Ptz Api 接口
+
+
+
+
+ Ptz Api
+
+
+
+
+ PtzServer选项
+
+
+
+
+ 服务类别
+
+
+
+
+ 服务IP
+
+
+
+
+ 服务端口
+
+
配置应用所需服务,在该方法中可以添加应用所需要的功能或服务
@@ -154,7 +259,7 @@
- 数据库名
+ 数据库标识
@@ -257,5 +362,15 @@
系统字典类型服务
+
+
+ 数据库标识
+
+
+
+
+ TbPtzCamera 表名
+
+
diff --git a/Cis.Application/Cm/Common/CmInfo.cs b/Cis.Application/Cm/Common/CmInfo.cs
index f82d8e1..abfda16 100644
--- a/Cis.Application/Cm/Common/CmInfo.cs
+++ b/Cis.Application/Cm/Common/CmInfo.cs
@@ -22,7 +22,7 @@ public class CmInfo
#region Database Info
///
- /// 数据库名
+ /// 数据库标识
///
public const string DbName = SqlSugarConst.DefaultConfigId;
@@ -40,5 +40,15 @@ public class CmInfo
///
public const string CmMarkGroupTbDesc = "标记分组表";
+ ///
+ /// CmMarkLabel 表名
+ ///
+ public const string CmMarkLabelTbName = "cm_mark_label";
+
+ ///
+ /// CmMarkLabel 表描述
+ ///
+ public const string CmMarkLabelTbDesc = "标记标签表";
+
#endregion Table Info
}
\ No newline at end of file
diff --git a/Cis.Application/Cm/Entity/CmMarkLabel.cs b/Cis.Application/Cm/Entity/CmMarkLabel.cs
new file mode 100644
index 0000000..bb0429a
--- /dev/null
+++ b/Cis.Application/Cm/Entity/CmMarkLabel.cs
@@ -0,0 +1,52 @@
+namespace Cis.Application.Cm;
+
+///
+/// 标记标签表
+///
+[SugarTable(CmInfo.CmMarkLabelTbName, CmInfo.CmMarkLabelTbDesc)]
+[Tenant(CmInfo.DbName)]
+public class CmMarkLabel : EntityBase
+{
+ ///
+ /// 名称
+ ///
+ [SugarColumn(ColumnDescription = "名称", Length = 64)]
+ public string Name { get; set; }
+
+ ///
+ /// Pan 位置
+ ///
+ [SugarColumn(ColumnDescription = "Pan位置")]
+ public double PanPosition { get; set; }
+
+ ///
+ /// Tilt 位置
+ ///
+ [SugarColumn(ColumnDescription = "Tilt位置")]
+ public double TiltPosition { get; set; }
+
+ ///
+ /// Zoom 位置
+ ///
+ [SugarColumn(ColumnDescription = "Zoom位置")]
+ public double ZoomPosition { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [SugarColumn(ColumnDescription = "备注", Length = 256)]
+ [MaxLength(256)]
+ public string Remark { get; set; }
+
+ ///
+ /// 相机 Id
+ ///
+ [SugarColumn(ColumnDescription = "相机Id")]
+ public long CbCameraId { get; set; }
+
+ ///
+ /// 标记组 Id
+ ///
+ [SugarColumn(ColumnDescription = "标记组Id")]
+ public long CmMarkGroupId { get; set; }
+}
\ No newline at end of file
diff --git a/Cis.Application/Cm/Service/CmMarkGroupService.cs b/Cis.Application/Cm/Service/CmMarkGroupService.cs
index faabdb0..2cf1302 100644
--- a/Cis.Application/Cm/Service/CmMarkGroupService.cs
+++ b/Cis.Application/Cm/Service/CmMarkGroupService.cs
@@ -1,4 +1,6 @@
-namespace Cis.Application.Cm;
+using Newtonsoft.Json.Linq;
+
+namespace Cis.Application.Cm;
///
/// 标记分组服务
@@ -12,4 +14,42 @@ public class CmMarkGroupService : IDynamicApiController, ITransient
{
_cmMarkGroupRep = cmMarkGroupRep;
}
+
+ public async Task Get(long id)
+ {
+ CmMarkGroup entity = await _cmMarkGroupRep.GetByIdAsync(id);
+ return entity;
+ }
+
+ public async Task> GetList(string queryJson)
+ {
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _cmMarkGroupRep.AsQueryable()
+ .ToListAsync();
+ return list;
+ }
+
+ public async Task> GetPageList(string queryJson, string pagination)
+ {
+ Pagination pageObj = pagination.ToObject();
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _cmMarkGroupRep.AsQueryable()
+ .ToPageListAsync(pageObj.Index, pageObj.Size);
+ return list;
+ }
+
+ public async Task Add(CmMarkGroup entity)
+ {
+ await _cmMarkGroupRep.InsertAsync(entity);
+ }
+
+ public async Task Update(CmMarkGroup entity)
+ {
+ await _cmMarkGroupRep.UpdateAsync(entity);
+ }
+
+ public async Task Delete(CmMarkGroup entity)
+ {
+ await _cmMarkGroupRep.DeleteAsync(entity);
+ }
}
\ No newline at end of file
diff --git a/Cis.Application/Cm/Service/CmMarkLabelService.cs b/Cis.Application/Cm/Service/CmMarkLabelService.cs
new file mode 100644
index 0000000..3df7e79
--- /dev/null
+++ b/Cis.Application/Cm/Service/CmMarkLabelService.cs
@@ -0,0 +1,55 @@
+using Newtonsoft.Json.Linq;
+
+namespace Cis.Application.Cm;
+
+///
+/// 标记标签服务
+///
+[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)]
+public class CmMarkLabelService : IDynamicApiController, ITransient
+{
+ private readonly SqlSugarRepository _cmMarkLabelRep;
+
+ public CmMarkLabelService(SqlSugarRepository cmMarkLabelRep)
+ {
+ _cmMarkLabelRep = cmMarkLabelRep;
+ }
+
+ public async Task Get(long id)
+ {
+ CmMarkLabel entity = await _cmMarkLabelRep.GetByIdAsync(id);
+ return entity;
+ }
+
+ public async Task> GetList(string queryJson = "")
+ {
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _cmMarkLabelRep.AsQueryable()
+ .ToListAsync();
+ return list;
+ }
+
+ public async Task> GetPageList(string queryJson, string pagination)
+ {
+ Pagination pageObj = pagination.ToObject();
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _cmMarkLabelRep.AsQueryable()
+ .ToPageListAsync(pageObj.Index, pageObj.Size);
+ return list;
+ }
+
+ public async Task Add(CmMarkLabel entity)
+ {
+ await _cmMarkLabelRep.InsertAsync(entity);
+ }
+
+ public async Task Update(CmMarkLabel entity)
+ {
+ await _cmMarkLabelRep.UpdateAsync(entity);
+ }
+
+ public async Task Delete(CmMarkLabel entity)
+ {
+ await _cmMarkLabelRep.DeleteAsync(entity);
+ }
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Algo/MarkSearcherBase.cs b/Cis.Application/Core/Algo/MarkSearcherBase.cs
new file mode 100644
index 0000000..033a34b
--- /dev/null
+++ b/Cis.Application/Core/Algo/MarkSearcherBase.cs
@@ -0,0 +1,6 @@
+namespace Cis.Application.Core;
+
+public class MarkSearcherBase
+{
+
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Api/IPtzApi.cs b/Cis.Application/Core/Api/IPtzApi.cs
new file mode 100644
index 0000000..e3461e5
--- /dev/null
+++ b/Cis.Application/Core/Api/IPtzApi.cs
@@ -0,0 +1,9 @@
+namespace Cis.Application.Core;
+
+///
+/// Ptz Api 接口
+///
+public interface IPtzApi
+{
+
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Api/PtzServerApi.cs b/Cis.Application/Core/Api/PtzServerApi.cs
new file mode 100644
index 0000000..4f65872
--- /dev/null
+++ b/Cis.Application/Core/Api/PtzServerApi.cs
@@ -0,0 +1,198 @@
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace Cis.Application.Core;
+
+///
+/// Ptz Api
+///
+public class PtzServerApi : IPtzApi, ISingleton
+{
+ #region Attr
+
+ private TcpClient _tcpClient { get; set; }
+
+ private NetworkStream _stream { get; set; }
+
+ #endregion Attr
+
+ public PtzServerApi()
+ {
+ PtzServerOptions options = App.GetOptions();
+ _tcpClient = new TcpClient(options.Ip, options.Port);
+ //创建一个 networkstream 用来写入和读取数据
+ _stream = _tcpClient.GetStream();
+ }
+
+ public RequestRealControl GetPtzInfo(int cameraId)
+ {
+ RequestRealControl realControl = new();
+ try
+ {
+ string recieve_string = string.Empty;
+ realControl.token = 666;
+
+ realControl.CameraInfo.cameraid = cameraId;
+ realControl.Status = true;
+ realControl.realControlType = RealControlType.PTZINFO_GET_;
+ byte[] data = StructToByte(realControl);
+ _stream.Write(data, 0, data.Length);
+
+ byte[] recieve_byte = new byte[_tcpClient.ReceiveBufferSize];
+ _stream.Read(recieve_byte, 0, (int)_tcpClient.ReceiveBufferSize);
+ //stream.BeginRead(recieve_byte, 0, (int)tcp_client.ReceiveBufferSize,EndRead, stream);
+ realControl = (RequestRealControl)BytetoStruct(recieve_byte, typeof(RequestRealControl));
+ if (realControl.PTZPositionInfo.FT < 0)
+ {
+ realControl.PTZPositionInfo.FT = 3600 + realControl.PTZPositionInfo.FT;
+ }
+ }
+ catch (Exception)
+ {
+ realControl = default;
+ }
+ return realControl;
+ }
+
+ public static byte[] StructToByte(object structObj)
+ {
+ //获取结构体大小
+ int size = Marshal.SizeOf(structObj);
+
+ byte[] data = new byte[size];
+
+ //分配内存空间
+ IntPtr structPtr = Marshal.AllocHGlobal(size);
+ // 将结构体数据复制到内存空间
+ Marshal.StructureToPtr(structObj, structPtr, false);
+ // 将内存空间的数据拷贝到byte数组
+ Marshal.Copy(structPtr, data, 0, size);
+ //释放内存
+ Marshal.FreeHGlobal(structPtr);
+ return data;
+ }
+
+ public static object BytetoStruct(byte[] bytes, Type type)
+ {
+ object obj = new object();
+ try
+ {
+ byte[] temp = bytes;
+ // 获取结构体大小
+ int size = Marshal.SizeOf(type);
+ if (size > bytes.Length)
+ return null;
+ // 分配结构体内存空间
+ IntPtr structPtr = Marshal.AllocHGlobal(size);
+ // 将byte数组内容拷贝到内存中
+ Marshal.Copy(temp, 0, structPtr, size);
+
+ // 将内存空间转化为目标结构体
+ obj = Marshal.PtrToStructure(structPtr, type);
+ //释放内存
+ Marshal.FreeHGlobal(structPtr);
+ }
+ catch (Exception)
+ {
+ }
+ return obj;
+ }
+
+}
+
+#region 与ptz服务交互使用结构体
+
+//----------------------------------------------------------------------------------------
+// 与ptz服务交互使用结构体
+//----------------------------------------------------------------------------------------
+//注意这个属性不能少
+[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+public struct RequestRealControl
+{
+ public int token;
+
+ public CameraInfo CameraInfo;
+
+ public RealControlType realControlType;
+
+ //请求时 状态 true:开始;false:结束 答复时:true:成功;其他:失败
+ public bool Status;
+
+ public PresentInfo PresentInfo;
+
+ public PTZPosInfo PTZPositionInfo;
+ //int数组,SizeConst表示数组的个数,在转换成
+ //byte数组前必须先初始化数组,再使用,初始化
+ //的数组长度必须和SizeConst一致,例test = new int[6];
+ //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
+ //public int[] test;
+}
+
+[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
+public struct CameraInfo
+{
+ public int cameraid;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
+ public char[] ip;
+
+ public ushort port;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
+ private char[] user;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
+ private char[] password;
+}
+
+[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
+public struct PresentInfo
+{
+ public int presentNo;//预置位编号
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
+ private char[] PresentName; // 预置位名称
+}
+
+[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
+public struct PTZPosInfo
+{
+ public int FP;//p信息
+ public int FT;//T信息
+ public int FZ;//Z信息
+ public int TitlePosMin;//描述点垂直参数min
+ public int ZoomPosMin;//描述点变倍参数min
+
+ public float FHorWidth;//水平宽度 精确到小数点后两位 *10000
+ public float FVerWidth;//垂直宽度 精确到小数点后两位 *10000
+ public float FFold;//zoom=1没变时的焦距 精确到小数点后两位 *100
+
+ public int CameraType;
+}
+
+public enum RealControlType
+{
+ MOVE_UP_ = 1,
+ MOVE_DOWN_,
+ MOVE_LEFT_,
+ MOVE_RIGHT_,
+ MOVE_LEFTUP_,
+ MOVE_LEFTDOWN_,
+ MOVE_RIGHTUP_,
+ MOVE_RIGHTDOWN_,
+
+ ZOOM_IN_,
+ ZOOM_OUT_,
+ FOCUS_NEAR_,
+ FOCUS_FAR_,
+ IRIS_OPEN_,
+ IRIS_CLOSE_,
+
+ PRESET_SET_,
+ PRESET_CALL_,
+
+ PTZINFO_GET_,
+ PTZINFO_SET_
+}
+
+#endregion 与ptz服务交互使用结构体
\ No newline at end of file
diff --git a/Cis.Application/Core/Center/CameraDataCenter.cs b/Cis.Application/Core/Center/CameraDataCenter.cs
new file mode 100644
index 0000000..5dc864b
--- /dev/null
+++ b/Cis.Application/Core/Center/CameraDataCenter.cs
@@ -0,0 +1,113 @@
+using Cis.Application.Cb;
+using Cis.Application.Cm;
+using Cis.Application.Tb;
+using System.Collections.Concurrent;
+
+namespace Cis.Application.Core;
+
+public class CameraDataCenter
+{
+ #region Attr
+
+ private Thread _thread { get; set; }
+ private List _tbPtzCameraList { get; set; }
+ private ConcurrentDictionary _cameraPtzInfoDict { get; set; }
+
+ private readonly SqlSugarRepository _cbCameraRep;
+ private readonly SqlSugarRepository _cmMarkLableRep;
+ private readonly SqlSugarRepository _tbPtzCameraRep;
+ private readonly PtzServerApi _ptzServerApi;
+
+ #endregion Attr
+
+ public CameraDataCenter()
+ {
+ _cbCameraRep = App.GetService>();
+ _cmMarkLableRep = App.GetService>();
+ _tbPtzCameraRep = App.GetService>();
+ _ptzServerApi = App.GetService();
+ Init();
+ }
+
+ private void Init()
+ {
+ // 初始化 tbPtzCameraList
+ _tbPtzCameraList = _tbPtzCameraRep.GetList();
+ // 根据 Ip 去重
+ _tbPtzCameraList = _tbPtzCameraList.Where((a, i) => _tbPtzCameraList.FindIndex(b => b.Ip == a.Ip) == i).ToList();
+
+ // 初始化 ptzInfoDict
+ _cameraPtzInfoDict = new ConcurrentDictionary();
+
+ // 初始化 thread
+ _thread = new Thread(WorkLoop)
+ {
+ IsBackground = true// 设置后台线程
+ };
+ _thread.Start();
+ }
+
+ private void LazyInit()
+ {
+ // 初始化 tbPtzCameraList
+ _tbPtzCameraList = new();
+
+ // 初始化 ptzInfoDict
+ _cameraPtzInfoDict = new ConcurrentDictionary();
+
+ // 初始化 thread
+ _thread = new Thread(WorkLoop)
+ {
+ IsBackground = true// 设置后台线程
+ };
+ _thread.Start();
+ }
+
+ #region Loop
+
+ private void WorkLoop()
+ {
+ while (true)
+ {
+ GetPtzInfoByApi();
+ Thread.Sleep(10000);
+ }
+ }
+
+ private void GetPtzInfoByApi()
+ {
+ foreach (TbPtzCamera item in _tbPtzCameraList)
+ {
+ RequestRealControl rrc = _ptzServerApi.GetPtzInfo(item.CameraId);
+ CameraCalcInfo ptzInfo = new()
+ {
+ Pan = rrc.PTZPositionInfo.FP,
+ Tilt = rrc.PTZPositionInfo.FT,
+ Zoom = rrc.PTZPositionInfo.FZ
+ };
+ _cameraPtzInfoDict[item.Ip] = ptzInfo;
+ }
+ }
+
+ private void Calc()
+ {
+ }
+
+ #endregion Loop
+
+ #region external call
+
+ public bool GetCamera(string ip)
+ {
+ return false;
+ }
+
+ public bool ActiveCamera(string ip)
+ {
+
+
+ return false;
+ }
+
+ #endregion external call
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Common/Options.cs b/Cis.Application/Core/Common/Options.cs
new file mode 100644
index 0000000..c121c2a
--- /dev/null
+++ b/Cis.Application/Core/Common/Options.cs
@@ -0,0 +1,22 @@
+namespace Cis.Application.Core;
+
+///
+/// PtzServer选项
+///
+public class PtzServerOptions : IConfigurableOptions
+{
+ ///
+ /// 服务类别
+ ///
+ public string Type { get; set; }
+
+ ///
+ /// 服务IP
+ ///
+ public string Ip { get; set; }
+
+ ///
+ /// 服务端口
+ ///
+ public int Port { get; set; }
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Entity/CameraCalcInfo.cs b/Cis.Application/Core/Entity/CameraCalcInfo.cs
new file mode 100644
index 0000000..e34c9b6
--- /dev/null
+++ b/Cis.Application/Core/Entity/CameraCalcInfo.cs
@@ -0,0 +1,32 @@
+namespace Cis.Application.Core;
+
+///
+/// 相机 Ptz 信息
+///
+public class CameraCalcInfo
+{
+ ///
+ /// 图像的宽度
+ ///
+ public int ImageWidth { get; set; } = 1920;
+
+ ///
+ /// 图像的宽度
+ ///
+ public int ImageHeight { get; set; } = 1080;
+
+ ///
+ /// Pan
+ ///
+ public double Pan { get; set; }
+
+ ///
+ /// Tilt
+ ///
+ public double Tilt { get; set; }
+
+ ///
+ /// Zoom
+ ///
+ public double Zoom { get; set; }
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Service/MarkSearchService.cs b/Cis.Application/Core/Service/MarkSearchService.cs
new file mode 100644
index 0000000..0432a26
--- /dev/null
+++ b/Cis.Application/Core/Service/MarkSearchService.cs
@@ -0,0 +1,22 @@
+using Cis.Application.Cm;
+
+namespace Cis.Application.Core;
+
+[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)]
+public class MarkSearchService : IDynamicApiController, ITransient
+{
+ #region Attr
+
+ private CameraDataCenter _cameraDataCenter { get; set; }
+
+ #endregion Attr
+
+ public MarkSearchService(CameraDataCenter cameraDataCenter)
+ {
+ _cameraDataCenter = cameraDataCenter;
+ }
+
+ public void ActivateCamera()
+ {
+ }
+}
\ No newline at end of file
diff --git a/Cis.Application/GlobalUsings.cs b/Cis.Application/GlobalUsings.cs
index a7568c4..76b15eb 100644
--- a/Cis.Application/GlobalUsings.cs
+++ b/Cis.Application/GlobalUsings.cs
@@ -1,5 +1,6 @@
global using Cis.Core;
global using Furion;
+global using Furion.ConfigurableOptions;
global using Furion.DependencyInjection;
global using Furion.DynamicApiController;
global using Microsoft.AspNetCore.Mvc;
diff --git a/Cis.Application/Startup.cs b/Cis.Application/Startup.cs
index d88de9c..19a7348 100644
--- a/Cis.Application/Startup.cs
+++ b/Cis.Application/Startup.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Builder;
+using Cis.Application.Core;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
@@ -12,8 +13,11 @@ public class Startup : AppStartup
///
///
public void ConfigureServices(IServiceCollection services)
- {
- }
+ {
+ services.AddConfigurableOptions();
+
+ services.AddSingleton(new CameraDataCenter());
+ }
///
/// 配置应用请求处理管道
@@ -21,6 +25,6 @@ public class Startup : AppStartup
///
///
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- }
+ {
+ }
}
\ No newline at end of file
diff --git a/Cis.Application/Sys/Common/SysInfo.cs b/Cis.Application/Sys/Common/SysInfo.cs
index 3ffb68d..dbeea07 100644
--- a/Cis.Application/Sys/Common/SysInfo.cs
+++ b/Cis.Application/Sys/Common/SysInfo.cs
@@ -22,7 +22,7 @@ public class SysInfo
#region Database Info
///
- /// 数据库名
+ /// 数据库标识
///
public const string DbName = SqlSugarConst.DefaultConfigId;
diff --git a/Cis.Application/Sys/Service/SysDictDataService.cs b/Cis.Application/Sys/Service/SysDictDataService.cs
index 0ac8c68..6042140 100644
--- a/Cis.Application/Sys/Service/SysDictDataService.cs
+++ b/Cis.Application/Sys/Service/SysDictDataService.cs
@@ -1,4 +1,6 @@
-namespace Cis.Application.Sys;
+using Newtonsoft.Json.Linq;
+
+namespace Cis.Application.Sys;
///
/// 系统字典值服务
@@ -13,4 +15,41 @@ public class SysDictDataService : IDynamicApiController, ITransient
_sysDictDataRep = sysDictDataRep;
}
+ public async Task Get(long id)
+ {
+ SysDictData entity = await _sysDictDataRep.GetByIdAsync(id);
+ return entity;
+ }
+
+ public async Task> GetList(string queryJson)
+ {
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _sysDictDataRep.AsQueryable()
+ .ToListAsync();
+ return list;
+ }
+
+ public async Task> GetPageList(string queryJson, string pagination)
+ {
+ Pagination pageObj = pagination.ToObject();
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _sysDictDataRep.AsQueryable()
+ .ToPageListAsync(pageObj.Index, pageObj.Size);
+ return list;
+ }
+
+ public async Task Add(SysDictData entity)
+ {
+ await _sysDictDataRep.InsertAsync(entity);
+ }
+
+ public async Task Update(SysDictData entity)
+ {
+ await _sysDictDataRep.UpdateAsync(entity);
+ }
+
+ public async Task Delete(SysDictData entity)
+ {
+ await _sysDictDataRep.DeleteAsync(entity);
+ }
}
\ No newline at end of file
diff --git a/Cis.Application/Sys/Service/SysDictTypeService.cs b/Cis.Application/Sys/Service/SysDictTypeService.cs
index 74c1b17..4c9763b 100644
--- a/Cis.Application/Sys/Service/SysDictTypeService.cs
+++ b/Cis.Application/Sys/Service/SysDictTypeService.cs
@@ -1,4 +1,7 @@
-namespace Cis.Application.Sys;
+using Cis.Application.Cm;
+using Newtonsoft.Json.Linq;
+
+namespace Cis.Application.Sys;
///
/// 系统字典类型服务
@@ -12,4 +15,42 @@ public class SysDictTypeService : IDynamicApiController, ITransient
{
_sysDictTypeRep = sysDictTypeRep;
}
+
+ public async Task Get(long id)
+ {
+ SysDictType entity = await _sysDictTypeRep.GetByIdAsync(id);
+ return entity;
+ }
+
+ public async Task> GetList(string queryJson)
+ {
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _sysDictTypeRep.AsQueryable()
+ .ToListAsync();
+ return list;
+ }
+
+ public async Task> GetPageList(string queryJson, string pagination)
+ {
+ Pagination pageObj = pagination.ToObject();
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _sysDictTypeRep.AsQueryable()
+ .ToPageListAsync(pageObj.Index, pageObj.Size);
+ return list;
+ }
+
+ public async Task Add(SysDictType entity)
+ {
+ await _sysDictTypeRep.InsertAsync(entity);
+ }
+
+ public async Task Update(SysDictType entity)
+ {
+ await _sysDictTypeRep.UpdateAsync(entity);
+ }
+
+ public async Task Delete(SysDictType entity)
+ {
+ await _sysDictTypeRep.DeleteAsync(entity);
+ }
}
\ No newline at end of file
diff --git a/Cis.Application/Tb/Common/TbInfo.cs b/Cis.Application/Tb/Common/TbInfo.cs
new file mode 100644
index 0000000..135f94d
--- /dev/null
+++ b/Cis.Application/Tb/Common/TbInfo.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Cis.Application.Tb;
+
+public class TbInfo
+{
+ #region Database Info
+
+ ///
+ /// 数据库标识
+ ///
+ public const string DbName = "serverdb";
+
+ #endregion Database Info
+
+ #region Table Info
+
+ ///
+ /// TbPtzCamera 表名
+ ///
+ public const string TbPtzCameraTbName = "tb_ptzcamera";
+
+ #endregion Table Info
+}
+
diff --git a/Cis.Application/Tb/Entity/TbPtzCamera.cs b/Cis.Application/Tb/Entity/TbPtzCamera.cs
new file mode 100644
index 0000000..6f75ea8
--- /dev/null
+++ b/Cis.Application/Tb/Entity/TbPtzCamera.cs
@@ -0,0 +1,28 @@
+namespace Cis.Application.Tb;
+
+[SugarTable(TbInfo.TbPtzCameraTbName)]
+[Tenant(TbInfo.DbName)]
+public class TbPtzCamera
+{
+ public int Id { get; set; }
+
+ public int Type { get; set; }
+
+ public string Ip { get; set; }
+
+ public int Port { get; set; }
+
+ public string User { get; set; }
+
+ public string Pass { get; set; }
+
+ public int CameraId { get; set; }
+
+ public double PanPosition { get; set; }
+
+ public double TitlePosition { get; set; }
+
+ public double ZoomPosition { get; set; }
+
+ public byte Request { get; set; }
+}
\ No newline at end of file
diff --git a/Cis.Application/Tb/Service/TbPtzCameraService.cs b/Cis.Application/Tb/Service/TbPtzCameraService.cs
new file mode 100644
index 0000000..03cb5bf
--- /dev/null
+++ b/Cis.Application/Tb/Service/TbPtzCameraService.cs
@@ -0,0 +1,51 @@
+using Newtonsoft.Json.Linq;
+
+namespace Cis.Application.Tb;
+
+public class TbPtzCameraService : ITransient
+{
+ private readonly SqlSugarRepository _tbPtzCameraRep;
+
+ public TbPtzCameraService(SqlSugarRepository tbPtzCameraRep)
+ {
+ _tbPtzCameraRep = tbPtzCameraRep;
+ }
+
+ public async Task Get(int id)
+ {
+ TbPtzCamera entity = await _tbPtzCameraRep.GetByIdAsync(id);
+ return entity;
+ }
+
+ public async Task> GetList(string queryJson = "")
+ {
+ JObject queryObj = !string.IsNullOrEmpty(queryJson) ? queryJson.ToJObject():default;
+ List list = await _tbPtzCameraRep.AsQueryable()
+ .ToListAsync();
+ return list;
+ }
+
+ public async Task> GetPageList(string pagination, string queryJson = "")
+ {
+ Pagination pageObj = pagination.ToObject();
+ JObject queryObj = queryJson.ToJObject();
+ List list = await _tbPtzCameraRep.AsQueryable()
+ .ToPageListAsync(pageObj.Index, pageObj.Size);
+ return list;
+ }
+
+ public async Task Add(TbPtzCamera entity)
+ {
+ await _tbPtzCameraRep.InsertAsync(entity);
+ }
+
+ public async Task Update(TbPtzCamera entity)
+ {
+ await _tbPtzCameraRep.UpdateAsync(entity);
+ }
+
+ public async Task Delete(TbPtzCamera entity)
+ {
+ await _tbPtzCameraRep.DeleteAsync(entity);
+ }
+}
\ No newline at end of file
diff --git a/Cis.Core/Cis.Core.csproj b/Cis.Core/Cis.Core.csproj
index 6530dd8..27ffbae 100644
--- a/Cis.Core/Cis.Core.csproj
+++ b/Cis.Core/Cis.Core.csproj
@@ -22,11 +22,16 @@
+
+
+
+
+
diff --git a/Cis.Core/Cis.Core.xml b/Cis.Core/Cis.Core.xml
index 310f220..30c9dce 100644
--- a/Cis.Core/Cis.Core.xml
+++ b/Cis.Core/Cis.Core.xml
@@ -110,6 +110,90 @@
页码数
+
+
+ 全局规范化结果
+
+
+
+
+ 异常返回值
+
+
+
+
+
+
+
+ 成功返回值
+
+
+
+
+
+
+
+ 验证失败返回值
+
+
+
+
+
+
+
+ 特定状态码返回值
+
+
+
+
+
+
+
+
+ 返回 RESTful 风格结果集
+
+
+
+
+
+
+
+
+
+ 全局返回结果
+
+
+
+
+
+ 状态码
+
+
+
+
+ 类型success、warning、error
+
+
+
+
+ 错误信息
+
+
+
+
+ 数据
+
+
+
+
+ 附加数据
+
+
+
+
+ 时间戳
+
+
缓存类型枚举
@@ -397,89 +481,5 @@
-
-
- 全局规范化结果
-
-
-
-
- 异常返回值
-
-
-
-
-
-
-
- 成功返回值
-
-
-
-
-
-
-
- 验证失败返回值
-
-
-
-
-
-
-
- 特定状态码返回值
-
-
-
-
-
-
-
-
- 返回 RESTful 风格结果集
-
-
-
-
-
-
-
-
-
- 全局返回结果
-
-
-
-
-
- 状态码
-
-
-
-
- 类型success、warning、error
-
-
-
-
- 错误信息
-
-
-
-
- 数据
-
-
-
-
- 附加数据
-
-
-
-
- 时间戳
-
-
diff --git a/Cis.Core/CoreConfig.json b/Cis.Core/CoreConfig.json
index b6deb2e..471e3b9 100644
--- a/Cis.Core/CoreConfig.json
+++ b/Cis.Core/CoreConfig.json
@@ -10,7 +10,13 @@
//"ConnectionString": "DataSource=./cis.db",
//"DbType": "PostgreSQL",
//"ConnectionString": "HOST=127.0.0.1;PORT=5432;USER ID=pgsql;PASSWORD=123456;DATABASE=cis;",
- "EnableInitDb": false, // 启用库表初始化
+ "EnableInitDb": false // 启用库表初始化
+ },
+ {
+ "ConfigId": "serverdb",
+ "DbType": "MySql",
+ "ConnectionString": "Data Source=127.0.0.1;port=3306;User ID=root;Password=123456;Database=serverdb;pooling=true;sslmode=none;CharSet=utf8;",
+ "EnableInitDb": false // 启用库表初始化
}
]
},
@@ -18,12 +24,17 @@
"CacheType": "Redis", // Memory、Redis
"RedisConnectionString": "127.0.0.1:6379;password=123456;db=2"
},
- "SnowId": {
- "WorkerId": 5 // 取值范围0~63,默认1
+ "PTZServer": {
+ "Type": "",
+ "Ip": "127.0.0.1",
+ "Port": "7022"
+ },
+ "AppSettings": {
+ "InjectSpecificationDocument": true // 生产环境是否开启Swagger
},
"SpecificationDocumentSettings": {
- "DocumentTitle": "Swagger",//默认标题
- "DefaultGroupName": "Default"//默认分组名称
+ "DocumentTitle": "Swagger", //默认标题
+ "DefaultGroupName": "Default" //默认分组名称
},
"DynamicApiControllerSettings": {
"DefaultRoutePrefix": "api", //默认路由前缀
@@ -33,8 +44,30 @@
"AsLowerCamelCase": true, //启用小驼峰命名(首字母小写)
"UrlParameterization": true // 方法参数
},
- "AppSettings": {
- "InjectSpecificationDocument": true // 生产环境是否开启Swagger
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ },
+ "File": {
+ "Enabled": true, // 启用文件日志
+ "FileName": "logs/{0:yyyyMMdd}_{1}.log", // 日志文件
+ "Append": true, // 追加覆盖
+ // "MinimumLevel": "Information", // 日志级别
+ "FileSizeLimitBytes": 10485760, // 10M=10*1024*1024
+ "MaxRollingFiles": 30 // 只保留30个文件
+ },
+ "Monitor": {
+ "GlobalEnabled": true, // 启用全局拦截日志
+ "IncludeOfMethods": [], // 拦截特定方法,当GlobalEnabled=false有效
+ "ExcludeOfMethods": [], // 排除特定方法,当GlobalEnabled=true有效
+ "BahLogLevel": "Information", // Oops.Oh 和 Oops.Bah 业务日志输出级别
+ "WithReturnValue": true, // 配置是否包含返回值,默认true
+ "ReturnValueThreshold": 0 // 配置返回值字符串阈值,默认0全量输出
+ }
+ },
+ "SnowId": {
+ "WorkerId": 5 // 取值范围0~63,默认1
},
"CorsAccessorSettings": {
"WithExposedHeaders": [
@@ -43,4 +76,4 @@
"environment"
]
}
-}
+}
\ No newline at end of file
diff --git a/Cis.Core/Util/RespParamProvider.cs b/Cis.Core/Entity/RespParamProvider.cs
similarity index 100%
rename from Cis.Core/Util/RespParamProvider.cs
rename to Cis.Core/Entity/RespParamProvider.cs
diff --git a/Cis.Web.Core/Startup.cs b/Cis.Web.Core/Startup.cs
index 6f1206e..5248263 100644
--- a/Cis.Web.Core/Startup.cs
+++ b/Cis.Web.Core/Startup.cs
@@ -4,12 +4,16 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
+using System;
+using System.IO;
using Yitter.IdGenerator;
namespace Cis.Web.Core;
+[AppStartup(1000)]
public class Startup : AppStartup
{
///
@@ -39,12 +43,30 @@ public class Startup : AppStartup
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // 首字母小写(驼峰样式)
+ options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;// 设置本地时区
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; // 时间格式化
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // 忽略循环引用
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; // 忽略空值
})
.AddInjectWithUnifyResult();
+ // 日志记录
+ if (App.GetConfig("Logging:File:Enabled")) // 日志写入文件
+ {
+ Array.ForEach(new[] { LogLevel.Information, LogLevel.Warning, LogLevel.Error }, logLevel =>
+ {
+ services.AddFileLogging(options =>
+ {
+ options.FileNameRule = fileName => string.Format(fileName, DateTime.Now, logLevel.ToString()); // 每天创建一个文件
+ options.WriteFilter = logMsg => logMsg.LogLevel == logLevel; // 日志级别
+ options.HandleWriteError = (writeError) => // 写入失败时启用备用文件
+ {
+ writeError.UseRollbackFileName(Path.GetFileNameWithoutExtension(writeError.CurrentFileName) + "-oops" + Path.GetExtension(writeError.CurrentFileName));
+ };
+ });
+ });
+ }
+
// 配置雪花Id算法机器码
YitIdHelper.SetIdGenerator(new IdGeneratorOptions
{
diff --git a/Cis.Web.Entry/Cis.Web.Entry.csproj b/Cis.Web.Entry/Cis.Web.Entry.csproj
index 2ddaf1d..6392984 100644
--- a/Cis.Web.Entry/Cis.Web.Entry.csproj
+++ b/Cis.Web.Entry/Cis.Web.Entry.csproj
@@ -10,8 +10,4 @@
-
-
-
-
diff --git a/Cis.Web.Entry/wwwroot/images/logo.png b/Cis.Web.Entry/wwwroot/images/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..eff5511ffb1210f5e940d040354df5a4e0834cf0
GIT binary patch
literal 25808
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