Browse Source

[feat] 1.调整运算框架,优化运算流程。2.CameraMark 组中的 MarkSearch api 移动到 core 组,前端需要调整 api 命名。3.添加相机参数 CbCameraParams api。

master
fajiao 2 years ago
parent
commit
608e820cda
  1. 2
      .gitignore
  2. 17
      Cis.Application/AppConfig.json
  3. 62
      Cis.Application/Cb/Common/CbInfo.cs
  4. 62
      Cis.Application/Cb/Entity/CbCamera.cs
  5. 44
      Cis.Application/Cb/Entity/CbCameraParams.cs
  6. 61
      Cis.Application/Cb/Service/CbCameraParamsService.cs
  7. 101
      Cis.Application/Cb/Service/CbCameraService.cs
  8. 1
      Cis.Application/Cis.Application.csproj
  9. 472
      Cis.Application/Cis.Application.xml
  10. 68
      Cis.Application/Cm/Common/CmInfo.cs
  11. 32
      Cis.Application/Cm/Entity/CmMarkGroup.cs
  12. 90
      Cis.Application/Cm/Entity/CmMarkLabel.cs
  13. 100
      Cis.Application/Cm/Service/CmMarkGroupService.cs
  14. 132
      Cis.Application/Cm/Service/CmMarkLabelService.cs
  15. 80
      Cis.Application/Core/Algo/HikMarkSeacher.cs
  16. 260
      Cis.Application/Core/Algo/MarkSearcherBase.cs
  17. 9
      Cis.Application/Core/Api/IPtzApi.cs
  18. 205
      Cis.Application/Core/Api/PtzServerApi.cs
  19. 341
      Cis.Application/Core/Center/CameraDataCenter.cs
  20. 23
      Cis.Application/Core/Common/CacheInfo.cs
  21. 18
      Cis.Application/Core/Common/CoreInfo.cs
  22. 30
      Cis.Application/Core/Common/Extension.cs
  23. 44
      Cis.Application/Core/Common/Options.cs
  24. 73
      Cis.Application/Core/Component/CameraSDK/CameraSdkServer.cs
  25. 54
      Cis.Application/Core/Component/CameraSDK/ICameraSdkServer.cs
  26. 52
      Cis.Application/Core/Component/MarkSeacher/Entity/CameraCalcParams.cs
  27. 52
      Cis.Application/Core/Component/MarkSeacher/Entity/MarkLabelCalcParams.cs
  28. 48
      Cis.Application/Core/Component/MarkSeacher/Entity/MarkLabelCalcResult.cs
  29. 119
      Cis.Application/Core/Component/MarkSeacher/IMarkSearcherServer.cs
  30. 268
      Cis.Application/Core/Component/MarkSeacher/MarkSearcherServer.cs
  31. 31
      Cis.Application/Core/Component/MarkSeacher/Seacher/DaHuaMarkSearcher.cs
  32. 73
      Cis.Application/Core/Component/MarkSeacher/Seacher/HiKMarkSeacher.cs
  33. 254
      Cis.Application/Core/Component/MarkSeacher/Seacher/MarkSearcherBase.cs
  34. 29
      Cis.Application/Core/Component/MarkSeacher/Seacher/YuShiMarkSeacher.cs
  35. 181
      Cis.Application/Core/Entity/CameraCalcInfo.cs
  36. 110
      Cis.Application/Core/Service/MarkSearchService.cs
  37. 1
      Cis.Application/GlobalUsings.cs
  38. 38
      Cis.Application/Startup.cs
  39. 68
      Cis.Application/Sys/Common/SysInfo.cs
  40. 56
      Cis.Application/Sys/Entity/SysDictType.cs
  41. 100
      Cis.Application/Sys/Service/SysDictDataService.cs
  42. 104
      Cis.Application/Sys/Service/SysDictTypeService.cs
  43. 22
      Cis.Application/Tb/Common/TbInfo.cs
  44. 28
      Cis.Application/Tb/Entity/TbPtzCamera.cs
  45. 57
      Cis.Application/Tb/Service/TbPtzCameraService.cs
  46. 37
      Cis.Core/Cache/CacheSetup.cs
  47. 82
      Cis.Core/Cache/SqlSugarCache.cs
  48. 20
      Cis.Core/Cis.Core.csproj
  49. 37
      Cis.Core/Cis.Core.xml
  50. 0
      Cis.Core/Common/Attribute/NotTableAttribute.cs
  51. 14
      Cis.Core/Common/Const/SqlSugarConst.cs
  52. 61
      Cis.Core/Common/Entity/EntityBase.cs
  53. 47
      Cis.Core/Common/Entity/Pagination.cs
  54. 128
      Cis.Core/Common/Entity/RespParamProvider.cs
  55. 0
      Cis.Core/Common/Enum/CacheTypeEnum.cs
  56. 19
      Cis.Core/Common/Enum/StatusEnum.cs
  57. 141
      Cis.Core/Common/Extension/ObjectExtension.cs
  58. 20
      Cis.Core/Common/Option/DbConnectionOptions.cs
  59. 12
      Cis.Core/Common/Option/RedisOptions.cs
  60. 8
      Cis.Core/Common/Option/SnowIdOptions.cs
  61. 14
      Cis.Core/Const/SqlSugarConst.cs
  62. 14
      Cis.Core/CoreConfig.json
  63. 61
      Cis.Core/Entity/EntityBase.cs
  64. 47
      Cis.Core/Entity/Pagination.cs
  65. 128
      Cis.Core/Entity/RespParamProvider.cs
  66. 19
      Cis.Core/Enum/StatusEnum.cs
  67. 151
      Cis.Core/Extension/ObjectExtension.cs
  68. 17
      Cis.Core/Option/CacheOptions.cs
  69. 20
      Cis.Core/Option/DbConnectionOptions.cs
  70. 10
      Cis.Core/SqlSugar/IEntityFilter.cs
  71. 12
      Cis.Core/SqlSugar/ISqlSugarEntitySeedData.cs
  72. 154
      Cis.Core/SqlSugar/SqlSugarPagedList.cs
  73. 12
      Cis.Core/SqlSugar/SqlSugarRepository.cs
  74. 286
      Cis.Core/SqlSugar/SqlSugarSetup.cs
  75. 104
      Cis.Core/SqlSugar/SqlSugarUnitOfWork.cs
  76. 24
      Cis.Web.Core/ProjectOptions.cs
  77. 202
      Cis.Web.Core/Startup.cs
  78. 12
      Cis.Web.Entry/.config/dotnet-tools.json
  79. 16
      Cis.Web.Entry/Controllers/HomeController.cs
  80. 22
      EC.Helper/CameraSDK/Common/CameraStruct.cs
  81. 2
      EC.Helper/CameraSDK/Common/ICameraSDK.cs
  82. 11
      EC.Helper/CameraSDK/DaHua/DaHuaSDK.cs
  83. 11
      EC.Helper/CameraSDK/HiK/HiKSDK.cs
  84. 9
      EC.Helper/CameraSDK/YuShi/YuShiSDK.cs
  85. 124
      EC.Helper/Test/CameraSDKTest.cs
  86. 1
      Frontend/src/views/page/cameraCenter.vue

2
.gitignore

@ -475,3 +475,5 @@ FodyWeavers.xsd
# Local History for Visual Studio Code # Local History for Visual Studio Code
.history/ .history/
# Custom
!/EC.Helper/libs/**

17
Cis.Application/AppConfig.json

@ -1,12 +1,13 @@
{ {
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json", "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
"CameraDataOptions": { "CameraData": {
"LazyInit": false, "CameraPtz": {
"LoopInterval": "1000" "LoopInterval": "1000",
}, "Timeout": "2000"
"PTZServer": { },
"Type": "", "MarkSearcher": {
"Ip": "127.0.0.1", "LoopInterval": "1000",
"Port": "7022" "Timeout": "2000"
}
} }
} }

62
Cis.Application/Cb/Common/CbInfo.cs

@ -5,40 +5,50 @@
/// </summary> /// </summary>
public class CbInfo public class CbInfo
{ {
#region Api Info #region Api Info
/// <summary> /// <summary>
/// Api 分组名 /// Api 分组名
/// </summary> /// </summary>
public const string GroupName = "CameraBase"; public const string GroupName = "CameraBase";
/// <summary> /// <summary>
/// Api 分组排序 /// Api 分组排序
/// </summary> /// </summary>
public const int GroupOrder = 100; public const int GroupOrder = 100;
#endregion Api Info #endregion Api Info
#region Database Info #region Database Info
/// <summary> /// <summary>
/// 数据库标识 /// 数据库标识
/// </summary> /// </summary>
public const string DbName = SqlSugarConst.DefaultConfigId; public const string DbName = SqlSugarConst.DefaultConfigId;
#endregion Database Info #endregion Database Info
#region Table Info #region Table Info
/// <summary> /// <summary>
/// CbCamera 表名 /// CbCamera 表名
/// </summary> /// </summary>
public const string CbCameraTbName = "cb_camera"; public const string CbCameraTbName = "cb_camera";
/// <summary> /// <summary>
/// CbCamera 表描述 /// CbCamera 表描述
/// </summary> /// </summary>
public const string CbCameraTbDesc = "相机表"; public const string CbCameraTbDesc = "相机表";
#endregion Table Info /// <summary>
/// CbCameraParams 表名
/// </summary>
public const string CbCameraParamsTbName = "cb_camera_params";
/// <summary>
/// CbCameraParams 表描述
/// </summary>
public const string CbCameraParamsTbDesc = "相机参数表";
#endregion Table Info
} }

62
Cis.Application/Cb/Entity/CbCamera.cs

@ -1,44 +1,44 @@
namespace Cis.Application.Cb; namespace Cis.Application.Cb;
/// <summary> /// <summary>
/// 系统字典类型 /// 相机
/// </summary> /// </summary>
[SugarTable(CbInfo.CbCameraTbName, CbInfo.CbCameraTbDesc)] [SugarTable(CbInfo.CbCameraTbName, CbInfo.CbCameraTbDesc)]
[Tenant(CbInfo.DbName)] [Tenant(CbInfo.DbName)]
public class CbCamera : EntityBase public class CbCamera : EntityBase
{ {
/// <summary> /// <summary>
/// 名称 /// 名称
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "名称", Length = 64)] [SugarColumn(ColumnDescription = "名称", Length = 64)]
[Required, MaxLength(64)] [Required, MaxLength(64)]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// ip 地址 /// ip 地址
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "ip地址", Length = 16)] [SugarColumn(ColumnDescription = "ip地址", Length = 16)]
[Required, MaxLength(16)] [Required, MaxLength(16)]
public string Ip { get; set; } public string Ip { get; set; }
/// <summary> /// <summary>
/// 端口 /// 账号
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "端口", Length = 5, DefaultValue = "80")] [SugarColumn(ColumnDescription = "账号", Length = 32)]
[Required, MaxLength(5)] [Required, MaxLength(32)]
public string Port { get; set; } public string UserName { get; set; }
/// <summary> /// <summary>
/// 账号 /// 密码
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "账号", Length = 32)] [SugarColumn(ColumnDescription = "密码", Length = 32)]
[Required, MaxLength(32)] [Required, MaxLength(32)]
public string UserName { get; set; } public string Password { get; set; }
/// <summary> /// <summary>
/// 密码 /// 相机参数 Id
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "密码", Length = 32)] [SugarColumn(ColumnDescription = "相机参数Id")]
[Required, MaxLength(32)] [Required]
public string Password { get; set; } public long CbCameraParamsId { get; set; }
} }

44
Cis.Application/Cb/Entity/CbCameraParams.cs

@ -0,0 +1,44 @@
namespace Cis.Application.Cb;
/// <summary>
/// 相机参数表
/// </summary>
[SugarTable(CbInfo.CbCameraParamsTbName, CbInfo.CbCameraParamsTbDesc)]
[Tenant(CbInfo.DbName)]
public class CbCameraParams : EntityBase
{
/// <summary>
/// 名称
/// </summary>
[SugarColumn(ColumnDescription = "名称", Length = 64)]
[Required, MaxLength(64)]
public string Name { get; set; }
/// <summary>
/// 相机类型
/// </summary>
[SugarColumn(ColumnDescription = "相机类型")]
[Required]
public int CameraType { get; set; }
/// <summary>
/// sdk 端口
/// </summary>
[SugarColumn(ColumnDescription = "sdk端口")]
[Required]
public int SdkPort { get; set; }
/// <summary>
/// FocusX
/// </summary>
[SugarColumn(ColumnDescription = "FocusX")]
[Required]
public double FocusX { get; set; }
/// <summary>
/// FocusY
/// </summary>
[SugarColumn(ColumnDescription = "FocusY")]
[Required]
public double FocusY { get; set; }
}

61
Cis.Application/Cb/Service/CbCameraParamsService.cs

@ -0,0 +1,61 @@
using Newtonsoft.Json.Linq;
namespace Cis.Application.Cb;
/// <summary>
/// 相机参数服务
/// </summary>
[ApiDescriptionSettings(CbInfo.GroupName, Order = CbInfo.GroupOrder)]
public class CbCameraParamsService : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<CbCameraParams> _baseRep;
public CbCameraParamsService(SqlSugarRepository<CbCameraParams> baseRep)
{
_baseRep = baseRep;
}
[HttpPost]
public async Task<bool> Add([FromForm] CbCameraParams entity)
{
return await _baseRep.InsertAsync(entity);
}
[HttpPost]
public async Task<bool> Update([FromForm] CbCameraParams entity)
{
return await _baseRep.UpdateAsync(entity);
}
[HttpPost]
public async Task<bool> Delete([FromForm] CbCameraParams entity)
{
return await _baseRep.DeleteAsync(entity);
}
[HttpGet]
public async Task<CbCameraParams> Get(long id)
{
CbCameraParams entity = await _baseRep.GetByIdAsync(id);
return entity;
}
[HttpGet]
public async Task<List<CbCameraParams>> GetList(string queryJson = "")
{
JObject queryObj = queryJson.ToJObject();
List<CbCameraParams> list = await _baseRep.AsQueryable()
.ToListAsync();
return list;
}
[HttpGet]
public async Task<List<CbCameraParams>> GetPageList(string queryJson, string pagination)
{
Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject();
List<CbCameraParams> list = await _baseRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size);
return list;
}
}

101
Cis.Application/Cb/Service/CbCameraService.cs

@ -8,55 +8,54 @@ namespace Cis.Application.Cb;
[ApiDescriptionSettings(CbInfo.GroupName, Order = CbInfo.GroupOrder)] [ApiDescriptionSettings(CbInfo.GroupName, Order = CbInfo.GroupOrder)]
public class CbCameraService : IDynamicApiController, ITransient public class CbCameraService : IDynamicApiController, ITransient
{ {
private readonly SqlSugarRepository<CbCamera> _cbCameraRep; private readonly SqlSugarRepository<CbCamera> _baseRep;
public CbCameraService(SqlSugarRepository<CbCamera> cbCameraRep) public CbCameraService(SqlSugarRepository<CbCamera> baseRep)
{ {
_cbCameraRep = cbCameraRep; _baseRep = baseRep;
} }
[HttpPost] [HttpPost]
public async Task<bool> Add([FromForm] CbCamera entity) public async Task<bool> Add([FromForm] CbCamera entity)
{ {
return await _cbCameraRep.InsertAsync(entity); return await _baseRep.InsertAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Update([FromForm] CbCamera entity) public async Task<bool> Update([FromForm] CbCamera entity)
{ {
return await _cbCameraRep.UpdateAsync(entity); return await _baseRep.UpdateAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Delete([FromForm] CbCamera entity) public async Task<bool> Delete([FromForm] CbCamera entity)
{ {
return await _cbCameraRep.DeleteAsync(entity); return await _baseRep.DeleteAsync(entity);
} }
[HttpGet] [HttpGet]
public async Task<CbCamera> Get(long id) public async Task<CbCamera> Get(long id)
{ {
CbCamera entity = await _cbCameraRep.GetByIdAsync(id); CbCamera entity = await _baseRep.GetByIdAsync(id);
return entity; return entity;
} }
[HttpGet] [HttpGet]
public async Task<List<CbCamera>> GetList(string queryJson = "") public async Task<List<CbCamera>> GetList(string queryJson = "")
{ {
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<CbCamera> list = await _cbCameraRep.AsQueryable() List<CbCamera> list = await _baseRep.AsQueryable()
.ToListAsync(); .ToListAsync();
return list; return list;
} }
[HttpGet] [HttpGet]
public async Task<List<CbCamera>> GetPageList(string queryJson, string pagination) public async Task<List<CbCamera>> GetPageList(string queryJson, string pagination)
{ {
Pagination pageObj = pagination.ToObject<Pagination>(); Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<CbCamera> list = await _cbCameraRep.AsQueryable() List<CbCamera> list = await _baseRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size); .ToPageListAsync(pageObj.Index, pageObj.Size);
return list; return list;
} }
} }

1
Cis.Application/Cis.Application.csproj

@ -24,6 +24,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Cis.Core\Cis.Core.csproj" /> <ProjectReference Include="..\Cis.Core\Cis.Core.csproj" />
<ProjectReference Include="..\EC.Helper\EC.Helper.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

472
Cis.Application/Cis.Application.xml

@ -34,9 +34,19 @@
CbCamera 表描述 CbCamera 表描述
</summary> </summary>
</member> </member>
<member name="F:Cis.Application.Cb.CbInfo.CbCameraParamsTbName">
<summary>
CbCameraParams 表名
</summary>
</member>
<member name="F:Cis.Application.Cb.CbInfo.CbCameraParamsTbDesc">
<summary>
CbCameraParams 表描述
</summary>
</member>
<member name="T:Cis.Application.Cb.CbCamera"> <member name="T:Cis.Application.Cb.CbCamera">
<summary> <summary>
系统字典类型表 相机
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Cb.CbCamera.Name"> <member name="P:Cis.Application.Cb.CbCamera.Name">
@ -49,11 +59,6 @@
ip 地址 ip 地址
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Cb.CbCamera.Port">
<summary>
端口
</summary>
</member>
<member name="P:Cis.Application.Cb.CbCamera.UserName"> <member name="P:Cis.Application.Cb.CbCamera.UserName">
<summary> <summary>
账号 账号
@ -64,6 +69,46 @@
密码 密码
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Cb.CbCamera.CbCameraParamsId">
<summary>
相机参数 Id
</summary>
</member>
<member name="T:Cis.Application.Cb.CbCameraParams">
<summary>
相机参数表
</summary>
</member>
<member name="P:Cis.Application.Cb.CbCameraParams.Name">
<summary>
名称
</summary>
</member>
<member name="P:Cis.Application.Cb.CbCameraParams.CameraType">
<summary>
相机类型
</summary>
</member>
<member name="P:Cis.Application.Cb.CbCameraParams.SdkPort">
<summary>
sdk 端口
</summary>
</member>
<member name="P:Cis.Application.Cb.CbCameraParams.FocusX">
<summary>
FocusX
</summary>
</member>
<member name="P:Cis.Application.Cb.CbCameraParams.FocusY">
<summary>
FocusY
</summary>
</member>
<member name="T:Cis.Application.Cb.CbCameraParamsService">
<summary>
相机参数服务
</summary>
</member>
<member name="T:Cis.Application.Cb.CbCameraService"> <member name="T:Cis.Application.Cb.CbCameraService">
<summary> <summary>
相机服务 相机服务
@ -131,7 +176,7 @@
</member> </member>
<member name="T:Cis.Application.Cm.CmMarkLabel"> <member name="T:Cis.Application.Cm.CmMarkLabel">
<summary> <summary>
记标签表 标签表
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Cm.CmMarkLabel.CbCameraId"> <member name="P:Cis.Application.Cm.CmMarkLabel.CbCameraId">
@ -176,281 +221,460 @@
</member> </member>
<member name="T:Cis.Application.Cm.CmMarkLabelService"> <member name="T:Cis.Application.Cm.CmMarkLabelService">
<summary> <summary>
记标签服务 标签服务
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkSearcherBase._cameraCalcInfo"> <member name="P:Cis.Application.Core.CameraDataCenter.SearchResultListDict">
<summary> <summary>
当前相机参数信息 {cameraId, List(MarkLabelCalcResult)}
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkSearcherBase.mliListLock"> <member name="M:Cis.Application.Core.CameraDataCenter.InitThread">
<summary> <summary>
_markLabelInfoList 锁对象,写锁 初始化线程
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkSearcherBase._markLabelInfoDict"> <member name="M:Cis.Application.Core.CameraDataCenter.RefreshCameraPtzInfos">
<summary> <summary>
需要计算的 markLabelInfo 列表 刷新所有相机 ptz
有待改善,应设置超时计数机制
https://blog.csdn.net/qq_28368039/article/details/118597396
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkSearcherBase.World2CameraMatrix"> <member name="M:Cis.Application.Core.CameraDataCenter.RefreshCameraPtzInfo(System.String)">
<summary> <summary>
世界坐标转化为相机坐标矩阵 刷新相机 ptz
</summary> </summary>
<param name="cameraIp"></param>
<returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.Calc"> <member name="M:Cis.Application.Core.CameraDataCenter.RefreshMarkSearchers">
<summary> <summary>
计算标签位置过程 刷新所有 markSearcher
</summary> </summary>
<returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.IsCameraRotate(Cis.Application.Core.PtzInfo)"> <member name="M:Cis.Application.Core.CameraDataCenter.RefreshMarkSearcher(System.Int64)">
<summary> <summary>
判断相机是否进行了转动,转动了则需要重新计算世界坐标到相机坐标的转换矩阵 刷新 markSearcher
</summary> </summary>
<param name="newInfo"></param> <param name="cameraId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.ConvertWorldToCamera(Cis.Application.Core.CameraCalcInfo)"> <member name="P:Cis.Application.Core.CacheInfo.CameraId2Ip">
<summary> <summary>
获取将世界坐标系中的点转化为相机坐标系中的点的转换矩阵 CameraId2Ip 字典
</summary> </summary>
<param name="cameraCalcInfo"></param>
<returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.ConvertCameraToWorld(Cis.Application.Core.MarkLabelCalcInfo)"> <member name="P:Cis.Application.Core.CacheInfo.CameraIpCounts">
<summary> <summary>
获取将相机坐标系中的点转化为世界坐标系中的点的转换矩阵 CameraIpCounts 字典
</summary> </summary>
<param name="labelCalcInfo"></param> </member>
<member name="P:Cis.Application.Core.CacheInfo.CameraPtzInfos">
<summary>
CameraPtzInfos 字典
</summary>
</member>
<member name="F:Cis.Application.Core.CoreInfo.GroupName">
<summary>
Api 分组名
</summary>
</member>
<member name="F:Cis.Application.Core.CoreInfo.GroupOrder">
<summary>
Api 分组排序
</summary>
</member>
<member name="P:Cis.Application.Core.CameraDataBase.LoopInterval">
<summary>
循环间隔,单位毫秒
</summary>
</member>
<member name="P:Cis.Application.Core.CameraDataBase.Timeout">
<summary>
超时时间,单位毫秒
</summary>
</member>
<member name="T:Cis.Application.Core.Component.CameraSDK.CameraSdkServer">
<summary>
相机 sdk 服务类
</summary>
</member>
<member name="P:Cis.Application.Core.Component.CameraSDK.CameraSdkServer.CameraSdkDict">
<summary>
{ip, ICameraSDK}
</summary>
</member>
<member name="T:Cis.Application.Core.Component.PtzServer.ICameraSdkServer">
<summary>
相机 sdk 服务接口
</summary>
</member>
<member name="M:Cis.Application.Core.Component.PtzServer.ICameraSdkServer.RegisterCamera(EC.Helper.CameraSDK.CameraInfo)">
<summary>
注册相机
</summary>
<param name="cameraInfo"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.CalcSensor"> <member name="M:Cis.Application.Core.Component.PtzServer.ICameraSdkServer.DeleteCamera(System.String)">
<summary> <summary>
此方法计算在球机zoom值最小的情况下成像矩阵中的 f 本质为获取像元大小 注销相机
尝试方案1:通过计算的方式来获取
尝试方案2:通过张友定相机标定的方法来生成成像矩阵中的 f
</summary> </summary>
<param name="ip"></param>
<returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.ConvertPanPosToAngle(System.Double)"> <member name="M:Cis.Application.Core.Component.PtzServer.ICameraSdkServer.IsExistsCamera(System.String)">
<summary> <summary>
将Pan值转化为角度 是否存在相机
</summary> </summary>
<param name="ip"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.ConvertTiltPosToAngle(System.Double,System.Double)"> <member name="M:Cis.Application.Core.Component.PtzServer.ICameraSdkServer.GetPtzInfoById(System.String,EC.Helper.CameraSDK.PtzInfo@)">
<summary> <summary>
将Tilt转化为角度 获取 ptz
</summary> </summary>
<param name="cameraId"></param>
<param name="ptzInfo"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.MarkSearcherBase.GetFOfMatrixByZoomPos(System.Double)"> <member name="M:Cis.Application.Core.Component.PtzServer.ICameraSdkServer.GetPtzInfoByIp(System.String,EC.Helper.CameraSDK.PtzInfo@)">
<summary> <summary>
根据当前zoom值获取相机矩阵参数 获取 ptz
</summary> </summary>
<param name="zoomPos"></param> <param name="ip"></param>
<param name="ptzInfo"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="T:Cis.Application.Core.IPtzApi"> <member name="T:Cis.Application.Core.Component.MarkSeacher.CameraCalcParams">
<summary>
相机计算参数
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.CameraCalcParams.Id">
<summary>
Camera Id
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.CameraCalcParams.PtzInfo">
<summary>
Ptz 信息
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.CameraCalcParams.FocusX">
<summary>
FocusX
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.CameraCalcParams.FocusY">
<summary>
FocusY
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.CameraCalcParams.VideoWidth">
<summary>
视频宽度
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.CameraCalcParams.VideoHeight">
<summary>
视频高度
</summary>
</member>
<member name="T:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams">
<summary>
标签计算参数
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams.Id">
<summary> <summary>
Ptz Api 接口 MarkLabel Id
</summary> </summary>
</member> </member>
<member name="T:Cis.Application.Core.PtzServerApi"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams.PtzInfo">
<summary> <summary>
Ptz Api Ptz 信息
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataCenter._cbCameraId2IpDict"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams.VideoWidth">
<summary> <summary>
(cbCameraId, cbCameraIp) 视频宽度
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataCenter._tbPtzCameraDict"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams.VideoHeight">
<summary> <summary>
(cameraIp, TbPtzCamera) 视频高度
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataCenter._cameraPtzInfoDict"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams.CanvasLeftRatio">
<summary> <summary>
(cameraIp, PtzInfo) 画布 left 距离比例
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataCenter.msDictLock"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams.CanvasTopRatio">
<summary> <summary>
_markSearcherDict 锁对象,写锁 画布 top 距离比例
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataCenter._markSearcherDict"> <member name="T:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcResult">
<summary> <summary>
(cbCameraId, MarkSearcherBase) 标签计算结果
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataCenter._markLabelCalcResultListDict"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcResult.Id">
<summary> <summary>
(cameraId, List(MarkLabelCalcResult)) MarkLabel Id
</summary> </summary>
</member> </member>
<member name="M:Cis.Application.Core.CameraDataCenter.WorkLoop"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcResult.InFlag">
<summary> <summary>
循环运行 true 显示(在当前视频画面里面)
false 不显示(不在当前视频画面里面)
</summary> </summary>
</member> </member>
<member name="M:Cis.Application.Core.CameraDataCenter.ActivateSearcher(System.Int64)"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcResult.CanvasLeftRatio">
<summary> <summary>
激活 cbCamera 进入运算 画布 left 距离比例
</summary>
</member>
<member name="P:Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcResult.CanvasTopRatio">
<summary>
画布 top 距离比例
</summary>
</member>
<member name="T:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer">
<summary>
追踪标签服务接口
</summary>
</member>
<member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.ActivateSearcher(System.Int64)">
<summary>
激活相机进入运算
</summary> </summary>
<param name="cameraId"></param> <param name="cameraId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Cis.Application.Core.CameraDataCenter.DeActivateSearcher(System.Int64)"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.DeactivateSearcher(System.Int64)">
<summary> <summary>
解除 cbCamera 进入运算 解除相机进入运算
</summary> </summary>
<param name="cameraId"></param> <param name="cameraId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataOptions.LazyInit"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.IsExistsSearcher(System.Int64)">
<summary> <summary>
是否懒加载 是否存在相机进入运算
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.CameraDataOptions.LoopInterval"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.GetSearcher(System.Int64)">
<summary> <summary>
循环间隔,单位毫秒 获取 Searcher
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="T:Cis.Application.Core.PtzServerOptions"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.ActivateMarkLabel(System.Int64,System.Int64)">
<summary> <summary>
PtzServer选项 激活标签进入运算
</summary> </summary>
<param name="cameraId"></param>
<param name="markLabelId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.PtzServerOptions.Type"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.DeactivateMarkLabel(System.Int64,System.Int64)">
<summary> <summary>
服务类别 解除标签进入运算
</summary> </summary>
<param name="cameraId"></param>
<param name="markLabelId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.PtzServerOptions.Ip"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.IsExistsMarkLabel(System.Int64,System.Int64)">
<summary> <summary>
服务IP 是否存在标签进入运算
</summary> </summary>
<param name="cameraId"></param>
<param name="markLabelId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.PtzServerOptions.Port"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.ActivateSearcherAsync(System.Int64)">
<summary> <summary>
服务端口 激活相机进入运算
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="T:Cis.Application.Core.CameraCalcInfo"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.DeactivateSearcherAsync(System.Int64)">
<summary> <summary>
相机计算信息 解除相机进入运算
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.CameraCalcInfo.Id"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.IsExistsSearcherAsync(System.Int64)">
<summary> <summary>
Camera Id 是否存在相机进入运算
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.CameraCalcInfo.PtzInfo"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.GetSearcherAsync(System.Int64)">
<summary> <summary>
Ptz 信息 获取 Searcher
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.CameraCalcInfo.VideoWidth"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.ActivateMarkLabelAsync(System.Int64,System.Int64)">
<summary> <summary>
视频的宽度 激活标签进入运算
</summary> </summary>
<param name="cameraId"></param>
<param name="markLabelId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.CameraCalcInfo.VideoHeight"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.DeactivateMarkLabelAsync(System.Int64,System.Int64)">
<summary> <summary>
视频的高度 解除标签进入运算
</summary> </summary>
<param name="cameraId"></param>
<param name="markLabelId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.CameraCalcInfo.MinFocusX"> <member name="M:Cis.Application.Core.Component.MarkSeacher.IMarkSearcherServer.IsExistsMarkLabelAsync(System.Int64,System.Int64)">
<summary> <summary>
最小焦距 是否存在标签进入运算
</summary> </summary>
<param name="cameraId"></param>
<param name="markLabelId"></param>
<returns></returns>
</member> </member>
<member name="T:Cis.Application.Core.PtzInfo"> <member name="T:Cis.Application.Core.Component.MarkSeacher.MarkSearcherServer">
<summary> <summary>
Ptz 信息 追踪标签服务类
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.PtzInfo.Pan"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkSearcherServer.MarkSearcherDict">
<summary> <summary>
Pan 坐标 {cbCameraId, MarkSearcherBase}
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.PtzInfo.Tilt"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkSearcherServer.MsDictRWLock">
<summary> <summary>
Tilt 坐标 MarkSearcherDict 原子操作锁
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.PtzInfo.Zoom"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherServer.ActivateSearcherAtom(System.Int64)">
<summary> <summary>
Zoom 坐标 ActivateSearcher 原子操作
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="T:Cis.Application.Core.MarkLabelCalcInfo"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherServer.DeactivateSearcherAtom(System.Int64)">
<summary> <summary>
标记标签计算信息 DeactivateSearcher 原子操作
</summary> </summary>
<param name="cameraId"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcInfo.Id"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.CameraCalcParams">
<summary> <summary>
MarkLabel Id 当前相机计算参数
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcInfo.PtzInfo"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.World2CameraMatrix">
<summary> <summary>
Ptz 信息 相机当前位置的世界坐标转化为相机坐标矩阵
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcInfo.VideoWidth"> <member name="P:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.MarkLabelCalcParamsDict">
<summary> <summary>
视频宽度 {cameraId, MarkLabelCalcParams}
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcInfo.VideoHeight"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.IsCameraRotate(EC.Helper.CameraSDK.PtzInfo)">
<summary> <summary>
视频高度 判断相机是否进行了转动,转动了则需要重新计算世界坐标到相机坐标的转换矩阵
</summary> </summary>
<param name="newInfo"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcInfo.CanvasLeftRatio"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.CalcSensor">
<summary> <summary>
画布 left 距离比例 此方法计算在球机zoom值最小的情况下成像矩阵中的 f 本质为获取像元大小
尝试方案1:通过计算的方式来获取
尝试方案2:通过张正友相机标定的方法来生成成像矩阵中的 f
</summary> </summary>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcInfo.CanvasTopRatio"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.ConvertWorldToCamera(Cis.Application.Core.Component.MarkSeacher.CameraCalcParams)">
<summary> <summary>
画布 top 距离比例 获取将世界坐标系中的点转化为相机坐标系中的点的转换矩阵
</summary> </summary>
<param name="cameraCalcParams"></param>
<returns></returns>
</member> </member>
<member name="T:Cis.Application.Core.MarkLabelCalcResult"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.ConvertCameraToWorld(Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams)">
<summary> <summary>
标记标签计算结果 获取将相机坐标系中的点转化为世界坐标系中的点的转换矩阵
</summary> </summary>
<param name="labelCalcParams"></param>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcResult.Id"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.Search">
<summary> <summary>
MarkLabel Id 计算标签位置过程
</summary> </summary>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcResult.InFlag"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.ConvertPanPosToAngle(System.Double)">
<summary> <summary>
true 显示(在当前视频画面里面) 将Pan值转化为角度
false 不显示(不在当前视频画面里面)
</summary> </summary>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcResult.CanvasLeftRatio"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.ConvertTiltPosToAngle(System.Double,System.Double)">
<summary> <summary>
画布 left 距离比例 将Tilt转化为角度
</summary> </summary>
<returns></returns>
</member> </member>
<member name="P:Cis.Application.Core.MarkLabelCalcResult.CanvasTopRatio"> <member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.GetFOfMatrixByZoomPos(System.Double)">
<summary> <summary>
画布 top 距离比例 根据当前zoom值获取相机矩阵参数
</summary>
<param name="zoomPos"></param>
<returns></returns>
</member>
<member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.UpdateCameraCalcParams(EC.Helper.CameraSDK.PtzInfo)">
<summary>
更新相机计算参数
</summary>
<param name="ptzInfo"></param>
</member>
<member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.AddMarkLabelCalcParams(Cis.Application.Core.Component.MarkSeacher.MarkLabelCalcParams)">
<summary>
添加标签计算参数
</summary>
<param name="labelCalcParams"></param>
<returns></returns>
</member>
<member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.DeleteMarkLabelCalcParams(System.Int64)">
<summary>
删除标签计算参数
</summary> </summary>
<param name="markLabelId"></param>
<returns></returns>
</member>
<member name="M:Cis.Application.Core.Component.MarkSeacher.MarkSearcherBase.IsExistsMarkLabelCalcParams(System.Int64)">
<summary>
是否存在标签计算参数
</summary>
<param name="markLabelId"></param>
<returns></returns>
</member> </member>
<member name="M:Cis.Application.Startup.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection)"> <member name="M:Cis.Application.Startup.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary> <summary>
@ -585,15 +809,5 @@
系统字典类型服务 系统字典类型服务
</summary> </summary>
</member> </member>
<member name="F:Cis.Application.Tb.TbInfo.DbName">
<summary>
数据库标识
</summary>
</member>
<member name="F:Cis.Application.Tb.TbInfo.TbPtzCameraTbName">
<summary>
TbPtzCamera 表名
</summary>
</member>
</members> </members>
</doc> </doc>

68
Cis.Application/Cm/Common/CmInfo.cs

@ -5,50 +5,50 @@
/// </summary> /// </summary>
public class CmInfo public class CmInfo
{ {
#region Api Info #region Api Info
/// <summary> /// <summary>
/// Api 分组名 /// Api 分组名
/// </summary> /// </summary>
public const string GroupName = "CameraMark"; public const string GroupName = "CameraMark";
/// <summary> /// <summary>
/// Api 分组排序 /// Api 分组排序
/// </summary> /// </summary>
public const int GroupOrder = 100; public const int GroupOrder = 100;
#endregion Api Info #endregion Api Info
#region Database Info #region Database Info
/// <summary> /// <summary>
/// 数据库标识 /// 数据库标识
/// </summary> /// </summary>
public const string DbName = SqlSugarConst.DefaultConfigId; public const string DbName = SqlSugarConst.DefaultConfigId;
#endregion Database Info #endregion Database Info
#region Table Info #region Table Info
/// <summary> /// <summary>
/// CmMarkGroup 表名 /// CmMarkGroup 表名
/// </summary> /// </summary>
public const string CmMarkGroupTbName = "cm_mark_group"; public const string CmMarkGroupTbName = "cm_mark_group";
/// <summary> /// <summary>
/// CmMarkGroup 表描述 /// CmMarkGroup 表描述
/// </summary> /// </summary>
public const string CmMarkGroupTbDesc = "标记分组表"; public const string CmMarkGroupTbDesc = "标记分组表";
/// <summary> /// <summary>
/// CmMarkLabel 表名 /// CmMarkLabel 表名
/// </summary> /// </summary>
public const string CmMarkLabelTbName = "cm_mark_label"; public const string CmMarkLabelTbName = "cm_mark_label";
/// <summary> /// <summary>
/// CmMarkLabel 表描述 /// CmMarkLabel 表描述
/// </summary> /// </summary>
public const string CmMarkLabelTbDesc = "标记标签表"; public const string CmMarkLabelTbDesc = "标签表";
#endregion Table Info #endregion Table Info
} }

32
Cis.Application/Cm/Entity/CmMarkGroup.cs

@ -7,22 +7,22 @@
[Tenant(CmInfo.DbName)] [Tenant(CmInfo.DbName)]
public class CmMarkGroup : EntityBase public class CmMarkGroup : EntityBase
{ {
/// <summary> /// <summary>
/// 名称 /// 名称
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "名称", Length = 64)] [SugarColumn(ColumnDescription = "名称", Length = 64)]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// 排序 /// 排序
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "排序")] [SugarColumn(ColumnDescription = "排序")]
public int Order { get; set; } public int Order { get; set; }
/// <summary> /// <summary>
/// 备注 /// 备注
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "备注", Length = 256)] [SugarColumn(ColumnDescription = "备注", Length = 256)]
[MaxLength(256)] [MaxLength(256)]
public string Remark { get; set; } public string Remark { get; set; }
} }

90
Cis.Application/Cm/Entity/CmMarkLabel.cs

@ -1,64 +1,64 @@
namespace Cis.Application.Cm; namespace Cis.Application.Cm;
/// <summary> /// <summary>
/// 标记标签表 /// 标签表
/// </summary> /// </summary>
[SugarTable(CmInfo.CmMarkLabelTbName, CmInfo.CmMarkLabelTbDesc)] [SugarTable(CmInfo.CmMarkLabelTbName, CmInfo.CmMarkLabelTbDesc)]
[Tenant(CmInfo.DbName)] [Tenant(CmInfo.DbName)]
public class CmMarkLabel : EntityBase public class CmMarkLabel : EntityBase
{ {
/// <summary> /// <summary>
/// 相机 Id /// 相机 Id
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "相机Id")] [SugarColumn(ColumnDescription = "相机Id")]
public long CbCameraId { get; set; } public long CbCameraId { get; set; }
/// <summary> /// <summary>
/// 标记组 Id /// 标记组 Id
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "标记组Id")] [SugarColumn(ColumnDescription = "标记组Id")]
public long CmMarkGroupId { get; set; } public long CmMarkGroupId { get; set; }
/// <summary> /// <summary>
/// 名称 /// 名称
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "名称", Length = 64)] [SugarColumn(ColumnDescription = "名称", Length = 64)]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// Pan 坐标 /// Pan 坐标
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "Pan坐标")] [SugarColumn(ColumnDescription = "Pan坐标")]
public double PanPosition { get; set; } public double PanPosition { get; set; }
/// <summary> /// <summary>
/// Tilt 坐标 /// Tilt 坐标
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "Tilt坐标")] [SugarColumn(ColumnDescription = "Tilt坐标")]
public double TiltPosition { get; set; } public double TiltPosition { get; set; }
/// <summary> /// <summary>
/// Zoom 坐标 /// Zoom 坐标
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "Zoom坐标")] [SugarColumn(ColumnDescription = "Zoom坐标")]
public double ZoomPosition { get; set; } public double ZoomPosition { get; set; }
[SugarColumn(ColumnDescription = "视频宽度")] [SugarColumn(ColumnDescription = "视频宽度")]
public double VideoWidth { get; set; } public double VideoWidth { get; set; }
[SugarColumn(ColumnDescription = "视频高度")] [SugarColumn(ColumnDescription = "视频高度")]
public double VideoHeight { get; set; } public double VideoHeight { get; set; }
[SugarColumn(ColumnDescription = "画布 left 距离比例")] [SugarColumn(ColumnDescription = "画布 left 距离比例")]
public double CanvasLeftRatio { get; set; } public double CanvasLeftRatio { get; set; }
[SugarColumn(ColumnDescription = "画布 top 距离比例")] [SugarColumn(ColumnDescription = "画布 top 距离比例")]
public double CanvasTopRatio { get; set; } public double CanvasTopRatio { get; set; }
/// <summary> /// <summary>
/// 备注 /// 备注
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "备注", Length = 256)] [SugarColumn(ColumnDescription = "备注", Length = 256)]
[MaxLength(256)] [MaxLength(256)]
public string Remark { get; set; } public string Remark { get; set; }
} }

100
Cis.Application/Cm/Service/CmMarkGroupService.cs

@ -8,54 +8,54 @@ namespace Cis.Application.Cm;
[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)] [ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)]
public class CmMarkGroupService : IDynamicApiController, ITransient public class CmMarkGroupService : IDynamicApiController, ITransient
{ {
private readonly SqlSugarRepository<CmMarkGroup> _cmMarkGroupRep; private readonly SqlSugarRepository<CmMarkGroup> _baseRep;
public CmMarkGroupService(SqlSugarRepository<CmMarkGroup> cmMarkGroupRep) public CmMarkGroupService(SqlSugarRepository<CmMarkGroup> baseRep)
{ {
_cmMarkGroupRep = cmMarkGroupRep; _baseRep = baseRep;
} }
[HttpPost] [HttpPost]
public async Task<bool> Add([FromForm] CmMarkGroup entity) public async Task<bool> Add([FromForm] CmMarkGroup entity)
{ {
return await _cmMarkGroupRep.InsertAsync(entity); return await _baseRep.InsertAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Update([FromForm] CmMarkGroup entity) public async Task<bool> Update([FromForm] CmMarkGroup entity)
{ {
return await _cmMarkGroupRep.UpdateAsync(entity); return await _baseRep.UpdateAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Delete([FromForm] CmMarkGroup entity) public async Task<bool> Delete([FromForm] CmMarkGroup entity)
{ {
return await _cmMarkGroupRep.DeleteAsync(entity); return await _baseRep.DeleteAsync(entity);
} }
[HttpGet] [HttpGet]
public async Task<CmMarkGroup> Get(long id) public async Task<CmMarkGroup> Get(long id)
{ {
CmMarkGroup entity = await _cmMarkGroupRep.GetByIdAsync(id); CmMarkGroup entity = await _baseRep.GetByIdAsync(id);
return entity; return entity;
} }
[HttpGet] [HttpGet]
public async Task<List<CmMarkGroup>> GetList(string queryJson) public async Task<List<CmMarkGroup>> GetList(string queryJson)
{ {
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<CmMarkGroup> list = await _cmMarkGroupRep.AsQueryable() List<CmMarkGroup> list = await _baseRep.AsQueryable()
.ToListAsync(); .ToListAsync();
return list; return list;
} }
[HttpGet] [HttpGet]
public async Task<List<CmMarkGroup>> GetPageList(string queryJson, string pagination) public async Task<List<CmMarkGroup>> GetPageList(string queryJson, string pagination)
{ {
Pagination pageObj = pagination.ToObject<Pagination>(); Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<CmMarkGroup> list = await _cmMarkGroupRep.AsQueryable() List<CmMarkGroup> list = await _baseRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size); .ToPageListAsync(pageObj.Index, pageObj.Size);
return list; return list;
} }
} }

132
Cis.Application/Cm/Service/CmMarkLabelService.cs

@ -1,85 +1,87 @@
using Cis.Application.Core; using Cis.Application.Core;
using Cis.Application.Core.Component.CameraSDK;
using EC.Helper.CameraSDK;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace Cis.Application.Cm; namespace Cis.Application.Cm;
/// <summary> /// <summary>
/// 标记标签服务 /// 标签服务
/// </summary> /// </summary>
[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)] [ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)]
public class CmMarkLabelService : IDynamicApiController, ITransient public class CmMarkLabelService : IDynamicApiController, ITransient
{ {
private readonly SqlSugarRepository<CmMarkLabel> _cmMarkLabelRep; private readonly SqlSugarRepository<CmMarkLabel> _baseRep;
private CameraDataCenter _cameraDataCenter { get; set; } private CameraSdkServer _cameraSdk { get; set; }
public CmMarkLabelService(CameraDataCenter cameraDataCenter, public CmMarkLabelService(
SqlSugarRepository<CmMarkLabel> cmMarkLabelRep) SqlSugarRepository<CmMarkLabel> baseRep,
{ CameraSdkServer cameraSdk
_cameraDataCenter = cameraDataCenter; )
_cmMarkLabelRep = cmMarkLabelRep; {
} _baseRep = baseRep;
_cameraSdk = cameraSdk;
}
[HttpPost] [HttpPost]
public async Task<bool> Add([FromForm] CmMarkLabel entity) public async Task<bool> Add([FromForm] CmMarkLabel entity)
{ {
PtzInfo ptzInfo = _cameraDataCenter.GetCameraPtzInfo(entity.CbCameraId); bool ret = _cameraSdk.GetPtzInfoById(entity.CbCameraId.ToString(), out PtzInfo ptzInfo);
if (ptzInfo == null) if (!ret) return false;
return false; entity.PanPosition = ptzInfo.Pan;
entity.PanPosition = ptzInfo.Pan; entity.TiltPosition = ptzInfo.Tilt;
entity.TiltPosition = ptzInfo.Tilt; entity.ZoomPosition = ptzInfo.Zoom;
entity.ZoomPosition = ptzInfo.Zoom; return await _baseRep.InsertAsync(entity);
return await _cmMarkLabelRep.InsertAsync(entity); }
}
[HttpPost] [HttpPost]
public async Task<long> AddReturnId([FromForm] CmMarkLabel entity) public async Task<long> AddReturnId([FromForm] CmMarkLabel entity)
{ {
PtzInfo ptzInfo = _cameraDataCenter.GetCameraPtzInfo(entity.CbCameraId); bool ret = _cameraSdk.GetPtzInfoById(entity.CbCameraId.ToString(), out PtzInfo ptzInfo);
if (ptzInfo == null) if (!ret) return 0;
return 0; entity.PanPosition = ptzInfo.Pan;
entity.PanPosition = ptzInfo.Pan; entity.TiltPosition = ptzInfo.Tilt;
entity.TiltPosition = ptzInfo.Tilt; entity.ZoomPosition = ptzInfo.Zoom;
entity.ZoomPosition = ptzInfo.Zoom; await _baseRep.InsertAsync(entity);
await _cmMarkLabelRep.InsertAsync(entity); return entity.Id;
return entity.Id; }
}
[HttpPost] [HttpPost]
public async Task<bool> Update([FromForm] CmMarkLabel entity) public async Task<bool> Update([FromForm] CmMarkLabel entity)
{ {
return await _cmMarkLabelRep.UpdateAsync(entity); return await _baseRep.UpdateAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Delete([FromForm] CmMarkLabel entity) public async Task<bool> Delete([FromForm] CmMarkLabel entity)
{ {
return await _cmMarkLabelRep.DeleteAsync(entity); return await _baseRep.DeleteAsync(entity);
} }
[HttpGet] [HttpGet]
public async Task<CmMarkLabel> Get(long id) public async Task<CmMarkLabel> Get(long id)
{ {
CmMarkLabel entity = await _cmMarkLabelRep.GetByIdAsync(id); CmMarkLabel entity = await _baseRep.GetByIdAsync(id);
return entity; return entity;
} }
[HttpGet] [HttpGet]
public async Task<List<CmMarkLabel>> GetList(string queryJson = "") public async Task<List<CmMarkLabel>> GetList(string queryJson = "")
{ {
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<CmMarkLabel> list = await _cmMarkLabelRep.AsQueryable() List<CmMarkLabel> list = await _baseRep.AsQueryable()
.ToListAsync(); .ToListAsync();
return list; return list;
} }
[HttpGet] [HttpGet]
public async Task<List<CmMarkLabel>> GetPageList(string queryJson, string pagination) public async Task<List<CmMarkLabel>> GetPageList(string queryJson, string pagination)
{ {
Pagination pageObj = pagination.ToObject<Pagination>(); Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<CmMarkLabel> list = await _cmMarkLabelRep.AsQueryable() List<CmMarkLabel> list = await _baseRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size); .ToPageListAsync(pageObj.Index, pageObj.Size);
return list; return list;
} }
} }

80
Cis.Application/Core/Algo/HikMarkSeacher.cs

@ -1,80 +0,0 @@
using System.Drawing;
namespace Cis.Application.Core;
public class HikMarkSeacher : MarkSearcherBase
{
public HikMarkSeacher(CameraCalcInfo cameraCalcInfo) : base(cameraCalcInfo)
{
}
#region Implement
protected override double ConvertPanPosToAngle(double panPos)
{
double ret = 0.1 * HexToDecMa(panPos) / 180 * Math.PI;
ret = (ret >= 0) ? ret : (2 * Math.PI + ret);
return ret;
}
protected override double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0)
{
double ndiff;
if (tiltPos > tiltMinPos)
ndiff = HexToDecMa(tiltPos) - HexToDecMa(tiltMinPos);
else
ndiff = HexToDecMa(tiltPos) + HexToDecMa(13824) - HexToDecMa(tiltMinPos);
double ret = 0.1 * ndiff / 180 * Math.PI;
return ret;
}
protected override PointF GetFOfMatrixByZoomPos(double zoomPos)
{
PointF pointF = new()
{
X = (float)GetFx(zoomPos),
Y = (float)GetFy(zoomPos)
};
return pointF;
}
protected override void CalcSensor()
{
_cameraCalcInfo.MinFocusX = 1783.6 / _cameraCalcInfo.VideoWidth;
_cameraCalcInfo.MinFocusY = 1781.4 / _cameraCalcInfo.VideoHeight;
}
#endregion Implement
#region Util
protected virtual double GetFx(double zoomPos)
{
CameraCalcInfo calcInfo = _cameraCalcInfo;
return calcInfo.MinFocusX * GetZoomTag(zoomPos);
}
protected virtual double GetFy(double zoomPos)
{
CameraCalcInfo calcInfo = _cameraCalcInfo;
return calcInfo.MinFocusY * GetZoomTag(zoomPos);
}
protected virtual double GetZoomTag(double zoomPos)
{
double ret = HexToDecMa(zoomPos) * 0.1;
ret = (ret - 1) * 0.65 + 1;
return ret;
}
protected int HexToDecMa(double wHex)
{
int wHexInt = wHex.ToInt();
int ret = (wHexInt / 4096) * 1000 + ((wHexInt % 4096) / 256) * 100 + ((wHexInt % 256) / 16) * 10 + (wHexInt % 16);
return ret;
}
#endregion Util
}

260
Cis.Application/Core/Algo/MarkSearcherBase.cs

@ -1,260 +0,0 @@
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using System.Collections.Concurrent;
using System.Drawing;
namespace Cis.Application.Core;
public abstract class MarkSearcherBase
{
#region Attr
/// <summary>
/// 当前相机参数信息
/// </summary>
protected CameraCalcInfo _cameraCalcInfo { get; set; }
/// <summary>
/// _markLabelInfoList 锁对象,写锁
/// </summary>
private static object mliListLock { get; } = new();
/// <summary>
/// 需要计算的 markLabelInfo 列表
/// </summary>
protected ConcurrentDictionary<long, MarkLabelCalcInfo> _markLabelInfoDict { get; set; } = new();
//protected MatrixBuilder<double> MBuilder { get; set; } = Matrix<double>.Build;
/// <summary>
/// 世界坐标转化为相机坐标矩阵
/// </summary>
protected Matrix<double> World2CameraMatrix { get; set; }
#endregion Attr
public MarkSearcherBase(CameraCalcInfo cameraCalcInfo)
{
UpdateCameraCalcInfoRelated(cameraCalcInfo);
}
#region Calc
/// <summary>
/// 计算标签位置过程
/// </summary>
/// <returns></returns>
public List<MarkLabelCalcResult> Calc()
{
List<MarkLabelCalcResult> resultList = new();
if (World2CameraMatrix == null || _markLabelInfoDict.IsEmpty)
return resultList;
foreach (MarkLabelCalcInfo item in _markLabelInfoDict.Values)
{
Matrix<double> labelC2WMatrix = ConvertCameraToWorld(item);
Matrix<double> labelPointMatrix = new DenseMatrix(3, 1, new double[]
{
(item.CanvasLeftRatio * item.VideoWidth) / _cameraCalcInfo.VideoWidth - 0.5 ,
(item.CanvasTopRatio * item.VideoHeight) / _cameraCalcInfo.VideoHeight - 0.5 ,
1
});
Matrix<double> lResult = labelC2WMatrix.Multiply(labelPointMatrix);
Matrix<double> pResult = World2CameraMatrix.Multiply(lResult);
double x = pResult[0, 0] / pResult[2, 0] + 0.5;
double y = pResult[1, 0] / pResult[2, 0] + 0.5;
MarkLabelCalcResult labelCalcResult;
if (x > 0.99 || x < 0.01 || y > 0.99 || y < 0.01 || pResult[2, 0] < 0)
labelCalcResult = MarkLabelCalcResult.New(item.Id, false);
else
labelCalcResult = MarkLabelCalcResult.New(item.Id, true, x, y);
resultList.Add(labelCalcResult);
}
return resultList;
}
public async Task<List<MarkLabelCalcResult>> CalcAsync()
{
return await Task.Run(Calc);
}
/// <summary>
/// 判断相机是否进行了转动,转动了则需要重新计算世界坐标到相机坐标的转换矩阵
/// </summary>
/// <param name="newInfo"></param>
/// <returns></returns>
protected bool IsCameraRotate(PtzInfo newInfo)
{
bool ret = _cameraCalcInfo.PtzInfo.Pan != newInfo.Pan ||
_cameraCalcInfo.PtzInfo.Tilt != newInfo.Tilt ||
_cameraCalcInfo.PtzInfo.Zoom != newInfo.Zoom;
return ret;
}
/// <summary>
/// 获取将世界坐标系中的点转化为相机坐标系中的点的转换矩阵
/// </summary>
/// <param name="cameraCalcInfo"></param>
/// <returns></returns>
protected Matrix<double> ConvertWorldToCamera(CameraCalcInfo cameraCalcInfo)
{
double panAngle = ConvertPanPosToAngle(cameraCalcInfo.PtzInfo.Pan);
double tiltAngle = ConvertTiltPosToAngle(cameraCalcInfo.PtzInfo.Tilt);
PointF pointF = GetFOfMatrixByZoomPos(cameraCalcInfo.PtzInfo.Zoom);
double sinPan = Math.Sin(panAngle);
double cosPan = Math.Cos(panAngle);
double sinTilt = Math.Sin(tiltAngle);
double cosTilt = Math.Cos(tiltAngle);
Matrix<double> fMatrix = new DenseMatrix(3, 3, new double[]
{
pointF.X, 0, 0 ,
0, pointF.Y, 0 ,
0, 0, 1 ,
});
Matrix<double> rotateTiltMatrix = new DenseMatrix(3, 3, new double[]
{
1, 0, 0 ,
0, cosTilt, sinTilt ,
0, -sinTilt, cosTilt ,
});
Matrix<double> midResult = fMatrix.Multiply(rotateTiltMatrix);
Matrix<double> rotatePanMatrix = new DenseMatrix(3, 3, new double[]
{
cosPan, 0, sinPan ,
0, 1, 0 ,
-sinPan, 0, cosPan ,
});
var last = midResult.Multiply(rotatePanMatrix);
return last;
}
/// <summary>
/// 获取将相机坐标系中的点转化为世界坐标系中的点的转换矩阵
/// </summary>
/// <param name="labelCalcInfo"></param>
/// <returns></returns>
protected Matrix<double> ConvertCameraToWorld(MarkLabelCalcInfo labelCalcInfo)
{
double panAngle = ConvertPanPosToAngle(labelCalcInfo.PtzInfo.Pan);
double tiltAngle = ConvertTiltPosToAngle(labelCalcInfo.PtzInfo.Tilt);
PointF pointF = GetFOfMatrixByZoomPos(labelCalcInfo.PtzInfo.Zoom);
double sinPan = Math.Sin(panAngle);
double cosPan = Math.Cos(panAngle);
double sinTilt = Math.Sin(tiltAngle);
double cosTilt = Math.Cos(tiltAngle);
Matrix<double> rotatePanMatrix = new DenseMatrix(3, 3, new double[]
{
cosPan, 0, -sinPan ,
0, 1, 0 ,
sinPan, 0, cosPan
});
Matrix<double> rotateTiltMatrix = new DenseMatrix(3, 3, new double[]
{
1, 0, 0 ,
0, cosTilt, -sinTilt ,
0, sinTilt, cosTilt
});
Matrix<double> midResult = rotatePanMatrix.Multiply(rotateTiltMatrix);
Matrix<double> fMatrix = new DenseMatrix(3, 3, new double[]
{
(1 / pointF.X), 0, 0 ,
0, (1 / pointF.Y), 0 ,
0, 0, 1
});
var last = midResult.Multiply(fMatrix);
return last;
}
/// <summary>
/// 此方法计算在球机zoom值最小的情况下成像矩阵中的 f 本质为获取像元大小
/// 尝试方案1:通过计算的方式来获取
/// 尝试方案2:通过张友定相机标定的方法来生成成像矩阵中的 f
/// </summary>
protected virtual void CalcSensor()
{
}
#endregion Calc
#region Util
/// <summary>
/// 将Pan值转化为角度
/// </summary>
/// <returns></returns>
protected abstract double ConvertPanPosToAngle(double panPos);
/// <summary>
/// 将Tilt转化为角度
/// </summary>
/// <returns></returns>
protected abstract double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0);
/// <summary>
/// 根据当前zoom值获取相机矩阵参数
/// </summary>
/// <param name="zoomPos"></param>
/// <returns></returns>
protected abstract PointF GetFOfMatrixByZoomPos(double zoomPos);
#endregion Util
#region Operate Attr
protected void UpdateCameraCalcInfoRelated(CameraCalcInfo cameraCalcInfo)
{
_cameraCalcInfo = cameraCalcInfo;
CalcSensor();
World2CameraMatrix = ConvertWorldToCamera(cameraCalcInfo);
}
public void UpdateCameraCalcInfo(PtzInfo ptzInfo)
{
if (IsCameraRotate(ptzInfo))
{
_cameraCalcInfo.PtzInfo = ptzInfo;
World2CameraMatrix = ConvertWorldToCamera(_cameraCalcInfo);
}
}
public bool AddMarkLabelCalcInfo(MarkLabelCalcInfo info)
{
lock (mliListLock)
{
if (_markLabelInfoDict.ContainsKey(info.Id))
return false;
return _markLabelInfoDict.TryAdd(info.Id, info);
}
}
public bool DeleteMarkLabelCalcInfo(long id)
{
lock (mliListLock)
{
if (!_markLabelInfoDict.ContainsKey(id))
return true;
return _markLabelInfoDict.Remove(id);
}
}
public bool ExistsMarkLabelCalcInfo(long id)
{
if (_markLabelInfoDict.ContainsKey(id))
return true;
return false;
}
#endregion Operate Attr
}

9
Cis.Application/Core/Api/IPtzApi.cs

@ -1,9 +0,0 @@
namespace Cis.Application.Core;
/// <summary>
/// Ptz Api 接口
/// </summary>
public interface IPtzApi
{
}

205
Cis.Application/Core/Api/PtzServerApi.cs

@ -1,205 +0,0 @@
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Cis.Application.Core;
/// <summary>
/// Ptz Api
/// </summary>
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<PtzServerOptions>();
_tcpClient = new TcpClient(options.Ip, options.Port);
//创建一个 networkstream 用来写入和读取数据
_stream = _tcpClient.GetStream();
}
public RequestRealControl GetPtzRrc(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 PtzInfo GetPtzInfo(int cameraId)
{
RequestRealControl rrc = GetPtzRrc(cameraId);
PtzInfo ptzInfo = PtzInfo.New(rrc.PTZPositionInfo.FP, rrc.PTZPositionInfo.FT, rrc.PTZPositionInfo.FZ);
return ptzInfo;
}
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服务交互使用结构体

341
Cis.Application/Core/Center/CameraDataCenter.cs

@ -1,309 +1,166 @@
using Cis.Application.Cb; using Cis.Application.Cb;
using Cis.Application.Cm; using Cis.Application.Cm;
using Cis.Application.Tb; using Cis.Application.Core.Component.MarkSeacher;
using Microsoft.Extensions.Logging; using Cis.Application.Core.Component.PtzServer;
using EC.Helper.CameraSDK;
using StackExchange.Profiling.Internal;
using StackExchange.Redis;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace Cis.Application.Core; namespace Cis.Application.Core;
public class CameraDataCenter public class CameraDataCenter : ISingleton
{ {
#region Attr #region Attr
private readonly SqlSugarRepository<CbCamera> _cbCameraRep; private readonly SqlSugarRepository<CbCamera> _cbCameraRep;
private readonly SqlSugarRepository<CmMarkLabel> _cmMarkLableRep; private readonly SqlSugarRepository<CmMarkLabel> _cmMarkLableRep;
private readonly SqlSugarRepository<TbPtzCamera> _tbPtzCameraRep; private readonly IDatabase _cache;
private readonly PtzServerApi _ptzServerApi;
private readonly CameraDataOptions options;
private Thread _thread { get; set; } private readonly CameraDataOptions _options;
private readonly ICameraSdkServer _cameraSdkServer;
private readonly IMarkSearcherServer _markSearcherServer;
/// <summary> /// <summary>
/// (cbCameraId, cbCameraIp) /// {cameraId, List(MarkLabelCalcResult)}
/// </summary> /// </summary>
private Dictionary<long, string> _cbCameraId2IpDict { get; set; } = new();
/// <summary> private ConcurrentDictionary<long, List<MarkLabelCalcResult>> SearchResultListDict { get; set; } = new();
/// (cameraIp, TbPtzCamera)
/// </summary>
private ConcurrentDictionary<string, TbPtzCamera> _tbPtzCameraDict { get; set; } = new();
/// <summary>
/// (cameraIp, PtzInfo)
/// </summary>
private ConcurrentDictionary<string, PtzInfo> _cameraPtzInfoDict { get; set; } = new();
/// <summary>
/// _markSearcherDict 锁对象,写锁
/// </summary>
private static ReaderWriterLockSlim msDictLock { get; } = new();
/// <summary>
/// (cbCameraId, MarkSearcherBase)
/// </summary>
private ConcurrentDictionary<long, MarkSearcherBase> _markSearcherDict { get; set; } = new();
/// <summary>
/// (cameraId, List(MarkLabelCalcResult))
/// </summary>
private ConcurrentDictionary<long, List<MarkLabelCalcResult>> _markLabelCalcResultListDict { get; set; } = new();
#endregion Attr #endregion Attr
private readonly ILogger<CameraDataCenter> _logger; public CameraDataCenter(
IDatabase cache,
public CameraDataCenter() ICameraSdkServer cameraSdkServer,
IMarkSearcherServer markSearcherServer
)
{ {
_cbCameraRep = App.GetService<SqlSugarRepository<CbCamera>>(); _cbCameraRep = App.GetService<SqlSugarRepository<CbCamera>>();
_cmMarkLableRep = App.GetService<SqlSugarRepository<CmMarkLabel>>(); _cmMarkLableRep = App.GetService<SqlSugarRepository<CmMarkLabel>>();
_tbPtzCameraRep = App.GetService<SqlSugarRepository<TbPtzCamera>>(); _cache = cache;
_ptzServerApi = App.GetService<PtzServerApi>(); _options = App.GetOptions<CameraDataOptions>();
options = App.GetOptions<CameraDataOptions>(); _cameraSdkServer = cameraSdkServer;
_logger = App.GetService<ILogger<CameraDataCenter>>(); _markSearcherServer = markSearcherServer;
Init(); InitThread();
} }
private void Init() #region Loop
private Thread RefreshCameraPtzThread { get; set; }
private Thread RefreshMarkSearcherThread { get; set; }
/// <summary>
/// 初始化线程
/// </summary>
private void InitThread()
{ {
if (!options.LazyInit) // 初始化 CameraPtzThread
RefreshCameraPtzThread = new Thread(() =>
{ {
List<TbPtzCamera> list = _tbPtzCameraRep.GetList(); while (true)
// 根据 Ip 去重
foreach (TbPtzCamera item in list)
{ {
if (!_tbPtzCameraDict.ContainsKey(item.Ip)) RefreshCameraPtzInfos();
_tbPtzCameraDict[item.Ip] = item; Thread.Sleep(_options.CameraPtz.LoopInterval);
} }
} })
{
IsBackground = true// 设置后台线程
};
// 启动 CameraPtzThread
RefreshCameraPtzThread.Start();
// 初始化 thread // 初始化 MarkSearcherThread
_thread = new Thread(WorkLoop) RefreshMarkSearcherThread = new Thread(() =>
{
while (true)
{
RefreshMarkSearchers();
Thread.Sleep(_options.MarkSearcher.LoopInterval);
}
})
{ {
IsBackground = true// 设置后台线程 IsBackground = true// 设置后台线程
}; };
_thread.Start(); // 启动 MarkSearcherThread
RefreshMarkSearcherThread.Start();
} }
#region Loop
/// <summary> /// <summary>
/// 循环运行 /// 刷新所有相机 ptz
/// 有待改善,应设置超时计数机制
/// https://blog.csdn.net/qq_28368039/article/details/118597396
/// </summary> /// </summary>
private void WorkLoop() private async void RefreshCameraPtzInfos()
{ {
while (true) HashEntry[] entries = _cache.HashGetAll(CacheInfo.CameraIpCounts);
List<Task> tasks = new();
foreach (HashEntry entry in entries)
{ {
RefreshPtzInfoByApi(); string cameraIp = entry.Name;
RefreshMarkSearcher(); tasks.Add(RefreshCameraPtzInfo(cameraIp));
Thread.Sleep(options.LoopInterval);
} }
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(_options.CameraPtz.Timeout));
} }
private void RefreshPtzInfoByApi() /// <summary>
{ /// 刷新相机 ptz
foreach (TbPtzCamera item in _tbPtzCameraDict.Values) /// </summary>
{ /// <param name="cameraIp"></param>
PtzInfo ptzInfo = _ptzServerApi.GetPtzInfo(item.CameraId); /// <returns></returns>
_cameraPtzInfoDict[item.Ip] = ptzInfo; private async Task RefreshCameraPtzInfo(string cameraIp)
}
}
private async void RefreshMarkSearcher()
{ {
foreach (KeyValuePair<long, MarkSearcherBase> pair in _markSearcherDict) bool ret = _cameraSdkServer.GetPtzInfoByIp(cameraIp, out PtzInfo ptzInfo);
{ if (!ret) return;
long cameraId = pair.Key; await _cache.HashSetAsync(CacheInfo.CameraPtzInfos, cameraIp, ptzInfo.ToJson());
MarkSearcherBase markSearcher = pair.Value;
bool ret = _cbCameraId2IpDict.TryGetValue(cameraId, out string cameraIp);
if (!ret) continue;
ret = _cameraPtzInfoDict.TryGetValue(cameraIp, out PtzInfo ptzInfo);
if (!ret) continue;
markSearcher.UpdateCameraCalcInfo(ptzInfo);
List<MarkLabelCalcResult> resultList = await markSearcher.CalcAsync();
_markLabelCalcResultListDict[cameraId] = resultList;
}
} }
#endregion Loop
#region external call
/// <summary> /// <summary>
/// 激活 cbCamera 进入运算 /// 刷新所有 markSearcher
/// </summary> /// </summary>
/// <param name="cameraId"></param> private async void RefreshMarkSearchers()
/// <returns></returns>
public bool ActivateSearcher(long cameraId)
{ {
try HashEntry[] entries = _cache.HashGetAll(CacheInfo.CameraId2Ip);
{ List<Task> tasks = new();
msDictLock.EnterWriteLock(); foreach (HashEntry entry in entries)
if (_markSearcherDict.ContainsKey(cameraId))
return false;
// 获取 cbCamera
CbCamera cbCamera = _cbCameraRep.GetById(cameraId);
if (cbCamera == null) return false;
// 获取 tbPtzCamera
string cameraIp = cbCamera.Ip;
bool ret = _tbPtzCameraDict.TryGetValue(cameraIp, out TbPtzCamera tbPtzCamera);
if (!ret)
{
tbPtzCamera = _tbPtzCameraRep.GetFirst(u => u.Ip == cameraIp);
if (tbPtzCamera == null) return false;
_tbPtzCameraDict[cameraIp] = tbPtzCamera;
}
// 存储 cbCamera id 对应 ip 关系
_cbCameraId2IpDict[cbCamera.Id] = cameraIp;
// 创建 cameraCalcInfo
CameraCalcInfo cameraCalcInfo = CameraCalcInfo.New(cameraId, _ptzServerApi.GetPtzInfo(tbPtzCamera.Id));
// 创建 markSeacher
HikMarkSeacher markSeacher = new(cameraCalcInfo);
// 获取 cmMarkLabel 列表
List<CmMarkLabel> cmMarkLabelList = _cmMarkLableRep.GetList(u => u.CbCameraId == cameraId);
// 将 cmMarkLabel 一一添加到 markSeacher
foreach (CmMarkLabel item in cmMarkLabelList)
{
MarkLabelCalcInfo markLabelCalcInfo = MarkLabelCalcInfo.New(
item.Id,
PtzInfo.New(item.PanPosition, item.TiltPosition, item.ZoomPosition),
item.VideoWidth,
item.VideoHeight,
item.CanvasLeftRatio,
item.CanvasTopRatio
);
markSeacher.AddMarkLabelCalcInfo(markLabelCalcInfo);
}
// 将 markSeacher 放入字典
_markSearcherDict[cameraId] = markSeacher;
}
finally
{ {
msDictLock.ExitWriteLock(); string cameraId = entry.Name;
tasks.Add(RefreshMarkSearcher(cameraId.ToLong()));
} }
return true; await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(_options.CameraPtz.Timeout));
} }
/// <summary> /// <summary>
/// 解除 cbCamera 进入运算 /// 刷新 markSearcher
/// </summary> /// </summary>
/// <param name="cameraId"></param> /// <param name="cameraId"></param>
/// <returns></returns> /// <returns></returns>
public bool DeActivateSearcher(long cameraId) private async Task RefreshMarkSearcher(long cameraId)
{ {
try MarkSearcherBase markSearcher = _markSearcherServer.GetSearcher(cameraId);
{ if (markSearcher == null) return;
msDictLock.EnterWriteLock(); string cameraIp = _cache.HashGet(CacheInfo.CameraId2Ip, cameraId);
_markSearcherDict.Remove(cameraId); RedisValue value = _cache.HashGet(CacheInfo.CameraPtzInfos, cameraIp);
_markLabelCalcResultListDict.Remove(cameraId); if (value.IsNull) return;
} PtzInfo ptzInfo = value.ToString().FromJson<PtzInfo>();
finally markSearcher.UpdateCameraCalcParams(ptzInfo);
{ List<MarkLabelCalcResult> resultList = await markSearcher.SearchAsync();
msDictLock.ExitWriteLock(); SearchResultListDict[cameraId] = resultList;
}
return true;
}
public bool IsExistSearcher(long cameraId)
{
return _markSearcherDict.ContainsKey(cameraId);
} }
public bool ActivateMarkLabel(long cameraId, long markLabelId) #endregion Loop
{
bool ret = _markSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher);
if (!ret)
return false;
if (markSearcher.ExistsMarkLabelCalcInfo(markLabelId))
return false;
CmMarkLabel label = _cmMarkLableRep.GetById(markLabelId);
if (label == null)
return false;
MarkLabelCalcInfo markLabelCalcInfo = MarkLabelCalcInfo.New(
label.Id,
PtzInfo.New(label.PanPosition, label.TiltPosition, label.ZoomPosition),
label.VideoWidth,
label.VideoHeight,
label.CanvasLeftRatio,
label.CanvasTopRatio
);
return markSearcher.AddMarkLabelCalcInfo(markLabelCalcInfo);
}
public bool DectivateMarkLabel(long cameraId, long markLabelId)
{
bool ret = _markSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher);
return ret ? markSearcher.DeleteMarkLabelCalcInfo(markLabelId) : false;
}
public bool IsExistCameraMarkLabel(long cameraId, long markLabelId) #region Base Method
{
bool ret = _markSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher) &&
markSearcher.ExistsMarkLabelCalcInfo(markLabelId);
return ret;
}
public List<MarkLabelCalcResult> GetMarkLabelCalcResultList(long cameraId) public List<MarkLabelCalcResult> GetMarkLabelCalcResultList(long cameraId)
{ {
bool ret = _markLabelCalcResultListDict.TryGetValue(cameraId, out List<MarkLabelCalcResult> list); bool ret = SearchResultListDict.TryGetValue(cameraId, out List<MarkLabelCalcResult> resultList);
return ret ? list : new(); return ret ? resultList : new List<MarkLabelCalcResult>();
}
public PtzInfo GetCameraPtzInfo(long cameraId)
{
PtzInfo ptzInfo = null;
bool ret = _cbCameraId2IpDict.TryGetValue(cameraId, out string cameraIp) &&
_cameraPtzInfoDict.TryGetValue(cameraIp, out ptzInfo);
return ret ? ptzInfo : null;
}
public async Task<bool> ActivateSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return ActivateSearcher(cameraId);
});
}
public async Task<bool> DeActivateSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return DeActivateSearcher(cameraId);
});
}
public async Task<bool> IsExistSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return IsExistSearcher(cameraId);
});
}
public async Task<bool> ActivateMarkLabelAsync(long cameraId, long markLabelId)
{
return await Task.Run(() =>
{
return ActivateMarkLabel(cameraId, markLabelId);
});
} }
public async Task<bool> DeactivateMarkLabelAsync(long cameraId, long markLabelId) #endregion Base Method
{
return await Task.Run(() =>
{
return DectivateMarkLabel(cameraId, markLabelId);
});
}
public async Task<bool> IsExistMarkLabelAysnc(long cameraId, long markLabelId) #region Base Method Async
{
return await Task.Run(() =>
{
return IsExistCameraMarkLabel(cameraId, markLabelId);
});
}
public async Task<List<MarkLabelCalcResult>> GetMarkLabelCalcResultListAsync(long cameraId) public async Task<List<MarkLabelCalcResult>> GetMarkLabelCalcResultListAsync(long cameraId)
{ {
@ -313,5 +170,5 @@ public class CameraDataCenter
}); });
} }
#endregion external call #endregion Base Method Async
} }

23
Cis.Application/Core/Common/CacheInfo.cs

@ -0,0 +1,23 @@
namespace Cis.Application.Core;
public class CacheInfo
{
#region Cache Name
/// <summary>
/// CameraId2Ip 字典
/// </summary>
public static string CameraId2Ip { get; set; } = "CameraId2Ip";
/// <summary>
/// CameraIpCounts 字典
/// </summary>
public static string CameraIpCounts { get; set; } = "CameraIpCounts";
/// <summary>
/// CameraPtzInfos 字典
/// </summary>
public static string CameraPtzInfos { get; set; } = "CameraPtzInfos";
#endregion Cache Name
}

18
Cis.Application/Core/Common/CoreInfo.cs

@ -0,0 +1,18 @@
namespace Cis.Application.Core;
public class CoreInfo
{
#region Api Info
/// <summary>
/// Api 分组名
/// </summary>
public const string GroupName = "Core";
/// <summary>
/// Api 分组排序
/// </summary>
public const int GroupOrder = 100;
#endregion Api Info
}

30
Cis.Application/Core/Common/Extension.cs

@ -0,0 +1,30 @@
using Cis.Application.Core.Component.MarkSeacher;
using EC.Helper.CameraSDK;
namespace Cis.Application.Core;
[SuppressSniffer]
public static class Extension
{
public static ICameraSDK CreateCameraSDK(this CameraType cameraType, CameraInfo cameraInfo)
{
return cameraType switch
{
CameraType.HiK => new HiKSDK(cameraInfo),
CameraType.DaHua => new DaHuaSDK(cameraInfo),
CameraType.YuShi => new YuShiSDK(cameraInfo),
_ => null,
};
}
public static MarkSearcherBase CreateMarkSearcher(this CameraType cameraType, CameraCalcParams cameraCalcParams)
{
return cameraType switch
{
CameraType.HiK => new HiKMarkSearcher(cameraCalcParams),
CameraType.DaHua => new DaHuaMarkSearcher(cameraCalcParams),
CameraType.YuShi => new YuShiMarkSearcher(cameraCalcParams),
_ => null
};
}
}

44
Cis.Application/Core/Common/Options.cs

@ -2,34 +2,30 @@
public class CameraDataOptions : IConfigurableOptions public class CameraDataOptions : IConfigurableOptions
{ {
/// <summary> public string Test { get; set; }
/// 是否懒加载
/// </summary>
public bool LazyInit { get; set; }
/// <summary> public CameraPtz CameraPtz { get; set; }
/// 循环间隔,单位毫秒
/// </summary> public MarkSearcher MarkSearcher { get; set; }
public int LoopInterval { get; set; }
} }
/// <summary> public class CameraDataBase
/// PtzServer选项
/// </summary>
public class PtzServerOptions : IConfigurableOptions
{ {
/// <summary> /// <summary>
/// 服务类别 /// 循环间隔,单位毫秒
/// </summary> /// </summary>
public string Type { get; set; } public int LoopInterval { get; set; }
/// <summary>
/// 超时时间,单位毫秒
/// </summary>
public int Timeout { get; set; }
}
/// <summary> public class CameraPtz : CameraDataBase
/// 服务IP {
/// </summary> }
public string Ip { get; set; }
/// <summary> public class MarkSearcher : CameraDataBase
/// 服务端口 {
/// </summary>
public int Port { get; set; }
} }

73
Cis.Application/Core/Component/CameraSDK/CameraSdkServer.cs

@ -0,0 +1,73 @@
using Cis.Application.Core.Component.PtzServer;
using EC.Helper.CameraSDK;
using StackExchange.Redis;
using System.Collections.Concurrent;
namespace Cis.Application.Core.Component.CameraSDK;
/// <summary>
/// 相机 sdk 服务类
/// </summary>
public class CameraSdkServer : ICameraSdkServer, ISingleton
{
#region Attr
private readonly IDatabase _cache;
/// <summary>
/// {ip, ICameraSDK}
/// </summary>
private ConcurrentDictionary<string, ICameraSDK> CameraSdkDict { get; set; } = new();
#endregion Attr
public CameraSdkServer(IDatabase cache)
{
_cache = cache;
}
#region Base Method
public bool RegisterCamera(CameraInfo cameraInfo)
{
bool ret = CameraSdkDict.ContainsKey(cameraInfo.Ip);
if (ret) return false;
ICameraSDK cameraSDK = ((CameraType)cameraInfo.Type).CreateCameraSDK(cameraInfo);
if (cameraSDK == null) return false;
ret = cameraSDK.Init();
if (!ret) return false;
ret = CameraSdkDict.TryAdd(cameraInfo.Ip, cameraSDK);
return ret;
}
public bool DeleteCamera(string ip)
{
return CameraSdkDict.TryRemove(ip, out _);
}
public bool IsExistsCamera(string ip)
{
return CameraSdkDict.ContainsKey(ip);
}
#endregion Base Method
#region Main Method
public bool GetPtzInfoById(string cameraId, out PtzInfo ptzInfo)
{
RedisValue value = _cache.HashGet(CacheInfo.CameraId2Ip, cameraId);
if (value.IsNull) { ptzInfo = PtzInfo.Default; return false; }
string ip = value.ToString();
return GetPtzInfoByIp(ip, out ptzInfo);
}
public bool GetPtzInfoByIp(string ip, out PtzInfo ptzInfo)
{
bool ret = CameraSdkDict.TryGetValue(ip, out ICameraSDK cameraSDK);
if (!ret) { ptzInfo = PtzInfo.Default; return false; }
return cameraSDK.GetPtzInfo(out ptzInfo);
}
#endregion Main Method
}

54
Cis.Application/Core/Component/CameraSDK/ICameraSdkServer.cs

@ -0,0 +1,54 @@
using EC.Helper.CameraSDK;
namespace Cis.Application.Core.Component.PtzServer;
/// <summary>
/// 相机 sdk 服务接口
/// </summary>
public interface ICameraSdkServer
{
#region Base Method
/// <summary>
/// 注册相机
/// </summary>
/// <param name="cameraInfo"></param>
/// <returns></returns>
public bool RegisterCamera(CameraInfo cameraInfo);
/// <summary>
/// 注销相机
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public bool DeleteCamera(string ip);
/// <summary>
/// 是否存在相机
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public bool IsExistsCamera(string ip);
#endregion Base Method
#region Main Method
/// <summary>
/// 获取 ptz
/// </summary>
/// <param name="cameraId"></param>
/// <param name="ptzInfo"></param>
/// <returns></returns>
public bool GetPtzInfoById(string cameraId, out PtzInfo ptzInfo);
/// <summary>
/// 获取 ptz
/// </summary>
/// <param name="ip"></param>
/// <param name="ptzInfo"></param>
/// <returns></returns>
public bool GetPtzInfoByIp(string ip, out PtzInfo ptzInfo);
#endregion Main Method
}

52
Cis.Application/Core/Component/MarkSeacher/Entity/CameraCalcParams.cs

@ -0,0 +1,52 @@
using EC.Helper.CameraSDK;
namespace Cis.Application.Core.Component.MarkSeacher;
/// <summary>
/// 相机计算参数
/// </summary>
public class CameraCalcParams
{
/// <summary>
/// Camera Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// Ptz 信息
/// </summary>
public PtzInfo PtzInfo { get; set; }
/// <summary>
/// FocusX
/// </summary>
public double FocusX { get; set; }
/// <summary>
/// FocusY
/// </summary>
public double FocusY { get; set; }
/// <summary>
/// 视频宽度
/// </summary>
public int VideoWidth { get; set; }
/// <summary>
/// 视频高度
/// </summary>
public int VideoHeight { get; set; }
public static CameraCalcParams New(long id, PtzInfo ptzInfo, double focusX, double focusY, int videoWidth = 1920, int videoHeight = 1080)
{
return new()
{
Id = id,
PtzInfo = ptzInfo,
FocusX = focusX,
FocusY = focusY,
VideoWidth = videoWidth,
VideoHeight = videoHeight,
};
}
}

52
Cis.Application/Core/Component/MarkSeacher/Entity/MarkLabelCalcParams.cs

@ -0,0 +1,52 @@
using EC.Helper.CameraSDK;
namespace Cis.Application.Core.Component.MarkSeacher;
/// <summary>
/// 标签计算参数
/// </summary>
public class MarkLabelCalcParams
{
/// <summary>
/// MarkLabel Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// Ptz 信息
/// </summary>
public PtzInfo PtzInfo { get; set; }
/// <summary>
/// 视频宽度
/// </summary>
public double VideoWidth { get; set; }
/// <summary>
/// 视频高度
/// </summary>
public double VideoHeight { get; set; }
/// <summary>
/// 画布 left 距离比例
/// </summary>
public double CanvasLeftRatio { get; set; }
/// <summary>
/// 画布 top 距离比例
/// </summary>
public double CanvasTopRatio { get; set; }
public static MarkLabelCalcParams New(long id, PtzInfo ptzInfo, double videoWidth, double videoHeight, double canvasLeftRatio, double canvasTopRatio)
{
return new()
{
Id = id,
PtzInfo = ptzInfo,
VideoWidth = videoWidth,
VideoHeight = videoHeight,
CanvasLeftRatio = canvasLeftRatio,
CanvasTopRatio = canvasTopRatio
};
}
}

48
Cis.Application/Core/Component/MarkSeacher/Entity/MarkLabelCalcResult.cs

@ -0,0 +1,48 @@
namespace Cis.Application.Core.Component.MarkSeacher;
/// <summary>
/// 标签计算结果
/// </summary>
public class MarkLabelCalcResult
{
/// <summary>
/// MarkLabel Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// true 显示(在当前视频画面里面)
/// false 不显示(不在当前视频画面里面)
/// </summary>
public bool InFlag { get; set; }
/// <summary>
/// 画布 left 距离比例
/// </summary>
public double CanvasLeftRatio { get; set; }
/// <summary>
/// 画布 top 距离比例
/// </summary>
public double CanvasTopRatio { get; set; }
public static MarkLabelCalcResult New(long id, bool inFlag)
{
return new()
{
Id = id,
InFlag = inFlag
};
}
public static MarkLabelCalcResult New(long id, bool inFlag, double canvasLeftRatio, double canvasTopRatio)
{
return new()
{
Id = id,
InFlag = inFlag,
CanvasLeftRatio = canvasLeftRatio,
CanvasTopRatio = canvasTopRatio
};
}
}

119
Cis.Application/Core/Component/MarkSeacher/IMarkSearcherServer.cs

@ -0,0 +1,119 @@
namespace Cis.Application.Core.Component.MarkSeacher;
/// <summary>
/// 追踪标签服务接口
/// </summary>
public interface IMarkSearcherServer
{
#region Base Method
/// <summary>
/// 激活相机进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public bool ActivateSearcher(long cameraId);
/// <summary>
/// 解除相机进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public bool DeactivateSearcher(long cameraId);
/// <summary>
/// 是否存在相机进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public bool IsExistsSearcher(long cameraId);
/// <summary>
/// 获取 Searcher
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public MarkSearcherBase GetSearcher(long cameraId);
/// <summary>
/// 激活标签进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <param name="markLabelId"></param>
/// <returns></returns>
public bool ActivateMarkLabel(long cameraId, long markLabelId);
/// <summary>
/// 解除标签进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <param name="markLabelId"></param>
/// <returns></returns>
public bool DeactivateMarkLabel(long cameraId, long markLabelId);
/// <summary>
/// 是否存在标签进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <param name="markLabelId"></param>
/// <returns></returns>
public bool IsExistsMarkLabel(long cameraId, long markLabelId);
#endregion Base Method
#region Base Method
/// <summary>
/// 激活相机进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public Task<bool> ActivateSearcherAsync(long cameraId);
/// <summary>
/// 解除相机进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public Task<bool> DeactivateSearcherAsync(long cameraId);
/// <summary>
/// 是否存在相机进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public Task<bool> IsExistsSearcherAsync(long cameraId);
/// <summary>
/// 获取 Searcher
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public Task<MarkSearcherBase> GetSearcherAsync(long cameraId);
/// <summary>
/// 激活标签进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <param name="markLabelId"></param>
/// <returns></returns>
public Task<bool> ActivateMarkLabelAsync(long cameraId, long markLabelId);
/// <summary>
/// 解除标签进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <param name="markLabelId"></param>
/// <returns></returns>
public Task<bool> DeactivateMarkLabelAsync(long cameraId, long markLabelId);
/// <summary>
/// 是否存在标签进入运算
/// </summary>
/// <param name="cameraId"></param>
/// <param name="markLabelId"></param>
/// <returns></returns>
public Task<bool> IsExistsMarkLabelAsync(long cameraId, long markLabelId);
#endregion Base Method
}

268
Cis.Application/Core/Component/MarkSeacher/MarkSearcherServer.cs

@ -0,0 +1,268 @@
using Cis.Application.Cb;
using Cis.Application.Cm;
using Cis.Application.Core.Component.PtzServer;
using EC.Helper.CameraSDK;
using StackExchange.Redis;
using System.Collections.Concurrent;
namespace Cis.Application.Core.Component.MarkSeacher;
/// <summary>
/// 追踪标签服务类
/// </summary>
public class MarkSearcherServer : IMarkSearcherServer, ISingleton
{
#region Attr
private readonly SqlSugarRepository<CbCamera> _cbCameraRep;
private readonly SqlSugarRepository<CbCameraParams> _cbCameraParamsRep;
private readonly SqlSugarRepository<CmMarkLabel> _cmMarkLableRep;
private readonly IDatabase _cache;
private readonly ICameraSdkServer _cameraSdkServer;
/// <summary>
/// {cbCameraId, MarkSearcherBase}
/// </summary>
private ConcurrentDictionary<long, MarkSearcherBase> MarkSearcherDict { get; set; } = new();
/// <summary>
/// MarkSearcherDict 原子操作锁
/// </summary>
private ReaderWriterLockSlim MsDictRWLock { get; } = new();
#endregion Attr
public MarkSearcherServer(
IDatabase cache,
ICameraSdkServer cameraSdkServer
)
{
_cbCameraRep = App.GetService<SqlSugarRepository<CbCamera>>();
_cbCameraParamsRep = App.GetService<SqlSugarRepository<CbCameraParams>>();
_cmMarkLableRep = App.GetService<SqlSugarRepository<CmMarkLabel>>();
_cache = cache;
_cameraSdkServer = cameraSdkServer;
}
#region Base Method
public bool ActivateSearcher(long cameraId)
{
try
{
MsDictRWLock.EnterWriteLock();
return ActivateSearcherAtom(cameraId);
}
finally
{
MsDictRWLock.ExitWriteLock();
}
}
/// <summary>
/// ActivateSearcher 原子操作
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
private bool ActivateSearcherAtom(long cameraId)
{
bool ret = MarkSearcherDict.ContainsKey(cameraId);
if (ret) return false;
// 获取相机
CbCamera cbCamera = _cbCameraRep.GetById(cameraId);
if (cbCamera == null) return false;
// 获取相机参数
CbCameraParams cbCameraParams = _cbCameraParamsRep.GetById(cbCamera.CbCameraParamsId);
if (cbCameraParams == null) return false;
// 注册并获取 ptz
ret = _cameraSdkServer.IsExistsCamera(cbCamera.Ip);
if (!ret)
{
CameraInfo cameraInfo = CameraInfo.New(cbCameraParams.CameraType, cbCamera.Ip, cbCamera.UserName, cbCamera.Password);
ret = _cameraSdkServer.RegisterCamera(cameraInfo);
if (!ret)
{
// double check
ret = _cameraSdkServer.IsExistsCamera(cbCamera.Ip);
if (!ret) return false;
}
}
ret = _cameraSdkServer.GetPtzInfoByIp(cbCamera.Ip, out PtzInfo ptzInfo);
if (!ret) return false;
// 创建相机计算参数
CameraCalcParams cameraCalcParams = CameraCalcParams.New(cbCamera.Id, ptzInfo, cbCameraParams.FocusX, cbCameraParams.FocusY);
// 创建 markSearcher
MarkSearcherBase markSearcher = ((CameraType)cbCameraParams.CameraType).CreateMarkSearcher(cameraCalcParams);
if (markSearcher == null) return false;
// 获取标签列表
List<CmMarkLabel> cmMarkLabelList = _cmMarkLableRep.GetList(u => u.CbCameraId == cameraId);
// 将标签添加到 markSearcher
foreach (CmMarkLabel item in cmMarkLabelList)
{
// 创建标签计算参数
MarkLabelCalcParams labelCalcParams = MarkLabelCalcParams.New(
item.Id,
PtzInfo.New(item.PanPosition, item.TiltPosition, item.ZoomPosition),
item.VideoWidth,
item.VideoHeight,
item.CanvasLeftRatio,
item.CanvasTopRatio
);
markSearcher.AddMarkLabelCalcParams(labelCalcParams);
}
// 存放 markSearcher
ret = MarkSearcherDict.TryAdd(cameraId, markSearcher);
if (!ret) return false;
// 记录缓存
_cache.HashSet(CacheInfo.CameraId2Ip, cbCamera.Id, cbCamera.Ip);
_cache.HashIncrement(CacheInfo.CameraIpCounts, cbCamera.Ip);
return true;
}
public bool DeactivateSearcher(long cameraId)
{
try
{
MsDictRWLock.EnterWriteLock();
return DeactivateSearcherAtom(cameraId);
}
finally
{
MsDictRWLock.ExitWriteLock();
}
}
/// <summary>
/// DeactivateSearcher 原子操作
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
private bool DeactivateSearcherAtom(long cameraId)
{
bool ret = MarkSearcherDict.TryRemove(cameraId, out _);
if (!ret) return false;
RedisValue value = _cache.HashGet(CacheInfo.CameraId2Ip, cameraId);
if (value.IsNull) return false;
string cameraIp = (string)value;
_cache.HashDelete(CacheInfo.CameraId2Ip, cameraId);
_cache.HashDecrement(CacheInfo.CameraIpCounts, cameraIp);
//int ipCount = (int)_cache.HashGet(CacheInfo.CameraIpCounts, cameraIp);
//if (ipCount <= 0)
//{
// // 直接删除 or 设置过期
// _cache.HashDelete(CacheInfo.CameraIpCounts, cameraIp);
// _cameraSdkServer.DeleteCamera(cameraIp);
//}
return true;
}
public bool IsExistsSearcher(long cameraId)
{
return MarkSearcherDict.ContainsKey(cameraId);
}
public MarkSearcherBase GetSearcher(long cameraId)
{
MarkSearcherDict.TryGetValue(cameraId, out MarkSearcherBase searcher);
return searcher;
}
public bool ActivateMarkLabel(long cameraId, long markLabelId)
{
bool ret = MarkSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher);
if (!ret) return false;
ret = markSearcher.IsExistsMarkLabelCalcParams(markLabelId);
if (ret) return false;
CmMarkLabel label = _cmMarkLableRep.GetById(markLabelId);
if (label == null) return false;
MarkLabelCalcParams labelCalcParams = MarkLabelCalcParams.New(
label.Id,
PtzInfo.New(label.PanPosition, label.TiltPosition, label.ZoomPosition),
label.VideoWidth,
label.VideoHeight,
label.CanvasLeftRatio,
label.CanvasTopRatio
);
ret = markSearcher.AddMarkLabelCalcParams(labelCalcParams);
return ret;
}
public bool DeactivateMarkLabel(long cameraId, long markLabelId)
{
bool ret = MarkSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher);
if (!ret) return false;
ret = markSearcher.DeleteMarkLabelCalcParams(markLabelId);
return ret;
}
public bool IsExistsMarkLabel(long cameraId, long markLabelId)
{
return MarkSearcherDict.TryGetValue(cameraId, out MarkSearcherBase searcher)
&& searcher.IsExistsMarkLabelCalcParams(markLabelId);
}
#endregion Base Method
#region Base Method Async
public async Task<bool> ActivateSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return ActivateSearcher(cameraId);
});
}
public async Task<bool> DeactivateSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return DeactivateSearcher(cameraId);
});
}
public async Task<bool> IsExistsSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return IsExistsSearcher(cameraId);
});
}
public async Task<MarkSearcherBase> GetSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return GetSearcher(cameraId);
});
}
public async Task<bool> ActivateMarkLabelAsync(long cameraId, long markLabelId)
{
return await Task.Run(() =>
{
return ActivateMarkLabel(cameraId, markLabelId);
});
}
public async Task<bool> DeactivateMarkLabelAsync(long cameraId, long markLabelId)
{
return await Task.Run(() =>
{
return DeactivateMarkLabel(cameraId, markLabelId);
});
}
public async Task<bool> IsExistsMarkLabelAsync(long cameraId, long markLabelId)
{
return await Task.Run(() =>
{
return IsExistsMarkLabel(cameraId, markLabelId);
});
}
#endregion Base Method Async
}

31
Cis.Application/Core/Component/MarkSeacher/Seacher/DaHuaMarkSearcher.cs

@ -0,0 +1,31 @@
using System.Drawing;
namespace Cis.Application.Core.Component.MarkSeacher;
public class DaHuaMarkSearcher : MarkSearcherBase
{
public DaHuaMarkSearcher(CameraCalcParams cameraCalcParams) : base(cameraCalcParams)
{
}
#region Implement
protected override double ConvertPanPosToAngle(double panPos)
{
double ret = (-0.1 * panPos) / 180 * Math.PI;
return ret;
}
protected override double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0)
{
double ret = (0.1 * tiltPos) / 180 * Math.PI;
return ret;
}
protected override PointF GetFOfMatrixByZoomPos(double zoomPos)
{
throw new NotImplementedException();
}
#endregion Implement
}

73
Cis.Application/Core/Component/MarkSeacher/Seacher/HiKMarkSeacher.cs

@ -0,0 +1,73 @@
using System.Drawing;
namespace Cis.Application.Core.Component.MarkSeacher;
public class HiKMarkSearcher : MarkSearcherBase
{
public HiKMarkSearcher(CameraCalcParams cameraCalcParams) : base(cameraCalcParams)
{
}
#region Implement
protected override double ConvertPanPosToAngle(double panPos)
{
double ret = 0.1 * HexToDecMa(panPos) / 180 * Math.PI;
ret = (ret >= 0) ? ret : (2 * Math.PI + ret);
return ret;
}
protected override double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0)
{
double ndiff;
if (tiltPos > tiltMinPos)
ndiff = HexToDecMa(tiltPos) - HexToDecMa(tiltMinPos);
else
ndiff = HexToDecMa(tiltPos) + HexToDecMa(13824) - HexToDecMa(tiltMinPos);
double ret = 0.1 * ndiff / 180 * Math.PI;
return ret;
}
protected override PointF GetFOfMatrixByZoomPos(double zoomPos)
{
PointF pointF = new()
{
X = (float)GetFx(zoomPos),
Y = (float)GetFy(zoomPos)
};
return pointF;
}
#endregion Implement
#region Util
protected virtual double GetFx(double zoomPos)
{
CameraCalcParams calcParams = CameraCalcParams;
return calcParams.FocusX * GetZoomTag(zoomPos);
}
protected virtual double GetFy(double zoomPos)
{
CameraCalcParams calcParams = CameraCalcParams;
return calcParams.FocusY * GetZoomTag(zoomPos);
}
protected virtual double GetZoomTag(double zoomPos)
{
double ret = HexToDecMa(zoomPos) * 0.1;
ret = (ret - 1) * 0.65 + 1;
return ret;
}
protected int HexToDecMa(double wHex)
{
int wHexInt = wHex.ToInt();
int ret = (wHexInt / 4096) * 1000 + ((wHexInt % 4096) / 256) * 100 + ((wHexInt % 256) / 16) * 10 + (wHexInt % 16);
return ret;
}
#endregion Util
}

254
Cis.Application/Core/Component/MarkSeacher/Seacher/MarkSearcherBase.cs

@ -0,0 +1,254 @@
using EC.Helper.CameraSDK;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using System.Collections.Concurrent;
using System.Drawing;
namespace Cis.Application.Core.Component.MarkSeacher;
public abstract class MarkSearcherBase
{
#region Attr
/// <summary>
/// 当前相机计算参数
/// </summary>
protected CameraCalcParams CameraCalcParams { get; set; }
/// <summary>
/// 相机当前位置的世界坐标转化为相机坐标矩阵
/// </summary>
protected Matrix<double> World2CameraMatrix { get; set; }
/// <summary>
/// {cameraId, MarkLabelCalcParams}
/// </summary>
private ConcurrentDictionary<long, MarkLabelCalcParams> MarkLabelCalcParamsDict { get; set; } = new();
#endregion Attr
public MarkSearcherBase(CameraCalcParams cameraCalcParams)
{
CameraCalcParams = cameraCalcParams;
CalcSensor();
World2CameraMatrix = ConvertWorldToCamera(cameraCalcParams);
}
#region Calc
/// <summary>
/// 判断相机是否进行了转动,转动了则需要重新计算世界坐标到相机坐标的转换矩阵
/// </summary>
/// <param name="newInfo"></param>
/// <returns></returns>
protected bool IsCameraRotate(PtzInfo newInfo)
{
bool ret = CameraCalcParams.PtzInfo.Pan != newInfo.Pan ||
CameraCalcParams.PtzInfo.Tilt != newInfo.Tilt ||
CameraCalcParams.PtzInfo.Zoom != newInfo.Zoom;
return ret;
}
/// <summary>
/// 此方法计算在球机zoom值最小的情况下成像矩阵中的 f 本质为获取像元大小
/// 尝试方案1:通过计算的方式来获取
/// 尝试方案2:通过张正友相机标定的方法来生成成像矩阵中的 f
/// </summary>
protected void CalcSensor()
{
CameraCalcParams.FocusX /= CameraCalcParams.VideoWidth;
CameraCalcParams.FocusY /= CameraCalcParams.VideoHeight;
}
/// <summary>
/// 获取将世界坐标系中的点转化为相机坐标系中的点的转换矩阵
/// </summary>
/// <param name="cameraCalcParams"></param>
/// <returns></returns>
protected Matrix<double> ConvertWorldToCamera(CameraCalcParams cameraCalcParams)
{
double panAngle = ConvertPanPosToAngle(cameraCalcParams.PtzInfo.Pan);
double tiltAngle = ConvertTiltPosToAngle(cameraCalcParams.PtzInfo.Tilt);
PointF pointF = GetFOfMatrixByZoomPos(cameraCalcParams.PtzInfo.Zoom);
double sinPan = Math.Sin(panAngle);
double cosPan = Math.Cos(panAngle);
double sinTilt = Math.Sin(tiltAngle);
double cosTilt = Math.Cos(tiltAngle);
Matrix<double> fMatrix = new DenseMatrix(3, 3, new double[]
{
pointF.X, 0, 0,
0, pointF.Y, 0,
0, 0, 1,
});
Matrix<double> rotateTiltMatrix = new DenseMatrix(3, 3, new double[]
{
1, 0, 0,
0, cosTilt, sinTilt,
0, -sinTilt, cosTilt,
});
Matrix<double> rotatePanMatrix = new DenseMatrix(3, 3, new double[]
{
cosPan, 0, sinPan,
0, 1, 0,
-sinPan, 0, cosPan,
});
Matrix<double> resultMatrix = fMatrix.Multiply(rotateTiltMatrix).Multiply(rotatePanMatrix);
return resultMatrix;
}
/// <summary>
/// 获取将相机坐标系中的点转化为世界坐标系中的点的转换矩阵
/// </summary>
/// <param name="labelCalcParams"></param>
/// <returns></returns>
protected Matrix<double> ConvertCameraToWorld(MarkLabelCalcParams labelCalcParams)
{
double panAngle = ConvertPanPosToAngle(labelCalcParams.PtzInfo.Pan);
double tiltAngle = ConvertTiltPosToAngle(labelCalcParams.PtzInfo.Tilt);
PointF pointF = GetFOfMatrixByZoomPos(labelCalcParams.PtzInfo.Zoom);
double sinPan = Math.Sin(panAngle);
double cosPan = Math.Cos(panAngle);
double sinTilt = Math.Sin(tiltAngle);
double cosTilt = Math.Cos(tiltAngle);
Matrix<double> rotatePanMatrix = new DenseMatrix(3, 3, new double[]
{
cosPan, 0, -sinPan,
0, 1, 0,
sinPan, 0, cosPan,
});
Matrix<double> rotateTiltMatrix = new DenseMatrix(3, 3, new double[]
{
1, 0, 0,
0, cosTilt, -sinTilt,
0, sinTilt, cosTilt,
});
Matrix<double> fMatrix = new DenseMatrix(3, 3, new double[]
{
(1 / pointF.X), 0, 0,
0, (1 / pointF.Y), 0,
0, 0, 1,
});
Matrix<double> resultMatrix = rotatePanMatrix.Multiply(rotateTiltMatrix).Multiply(fMatrix);
return resultMatrix;
}
/// <summary>
/// 计算标签位置过程
/// </summary>
/// <returns></returns>
public List<MarkLabelCalcResult> Search()
{
List<MarkLabelCalcResult> resultList = new();
if (World2CameraMatrix == null || MarkLabelCalcParamsDict.IsEmpty)
return resultList;
foreach (MarkLabelCalcParams item in MarkLabelCalcParamsDict.Values)
{
Matrix<double> labelC2WMatrix = ConvertCameraToWorld(item);
Matrix<double> labelPointMatrix = new DenseMatrix(3, 1, new double[]
{
(item.CanvasLeftRatio-0.5)*item.VideoWidth / CameraCalcParams.VideoWidth,
(item.CanvasTopRatio-0.5)*item.VideoHeight / CameraCalcParams.VideoHeight,
1
});
Matrix<double> lResult = labelC2WMatrix.Multiply(labelPointMatrix);
Matrix<double> pResult = World2CameraMatrix.Multiply(lResult);
double x = pResult[0, 0] / pResult[2, 0] + 0.5;
double y = pResult[1, 0] / pResult[2, 0] + 0.5;
MarkLabelCalcResult labelCalcResult;
if (x > 0.99 || x < 0.01 || y > 0.99 || y < 0.01 || pResult[2, 0] < 0)
labelCalcResult = MarkLabelCalcResult.New(item.Id, false);
else
labelCalcResult = MarkLabelCalcResult.New(item.Id, true, x, y);
resultList.Add(labelCalcResult);
}
return resultList;
}
public async Task<List<MarkLabelCalcResult>> SearchAsync()
{
return await Task.Run(Search);
}
#endregion Calc
#region Util
/// <summary>
/// 将Pan值转化为角度
/// </summary>
/// <returns></returns>
protected abstract double ConvertPanPosToAngle(double panPos);
/// <summary>
/// 将Tilt转化为角度
/// </summary>
/// <returns></returns>
protected abstract double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0);
/// <summary>
/// 根据当前zoom值获取相机矩阵参数
/// </summary>
/// <param name="zoomPos"></param>
/// <returns></returns>
protected abstract PointF GetFOfMatrixByZoomPos(double zoomPos);
#endregion Util
#region Base Method
/// <summary>
/// 更新相机计算参数
/// </summary>
/// <param name="ptzInfo"></param>
public void UpdateCameraCalcParams(PtzInfo ptzInfo)
{
if (IsCameraRotate(ptzInfo))
{
CameraCalcParams.PtzInfo = ptzInfo;
World2CameraMatrix = ConvertWorldToCamera(CameraCalcParams);
}
}
/// <summary>
/// 添加标签计算参数
/// </summary>
/// <param name="labelCalcParams"></param>
/// <returns></returns>
public bool AddMarkLabelCalcParams(MarkLabelCalcParams labelCalcParams)
{
return MarkLabelCalcParamsDict.TryAdd(labelCalcParams.Id, labelCalcParams);
}
/// <summary>
/// 删除标签计算参数
/// </summary>
/// <param name="markLabelId"></param>
/// <returns></returns>
public bool DeleteMarkLabelCalcParams(long markLabelId)
{
return MarkLabelCalcParamsDict.TryRemove(markLabelId, out _);
}
/// <summary>
/// 是否存在标签计算参数
/// </summary>
/// <param name="markLabelId"></param>
/// <returns></returns>
public bool IsExistsMarkLabelCalcParams(long markLabelId)
{
return MarkLabelCalcParamsDict.ContainsKey(markLabelId);
}
#endregion Base Method
}

29
Cis.Application/Core/Component/MarkSeacher/Seacher/YuShiMarkSeacher.cs

@ -0,0 +1,29 @@
using System.Drawing;
namespace Cis.Application.Core.Component.MarkSeacher;
public class YuShiMarkSearcher : MarkSearcherBase
{
public YuShiMarkSearcher(CameraCalcParams cameraCalcParams) : base(cameraCalcParams)
{
}
#region Implement
protected override double ConvertPanPosToAngle(double panPos)
{
throw new NotImplementedException();
}
protected override double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0)
{
throw new NotImplementedException();
}
protected override PointF GetFOfMatrixByZoomPos(double zoomPos)
{
throw new NotImplementedException();
}
#endregion Implement
}

181
Cis.Application/Core/Entity/CameraCalcInfo.cs

@ -1,181 +0,0 @@
namespace Cis.Application.Core;
/// <summary>
/// 相机计算信息
/// </summary>
public class CameraCalcInfo
{
/// <summary>
/// Camera Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// Ptz 信息
/// </summary>
public PtzInfo PtzInfo { get; set; }
/// <summary>
/// 视频的宽度
/// </summary>
public int VideoWidth { get; set; } = 1920;
/// <summary>
/// 视频的高度
/// </summary>
public int VideoHeight { get; set; } = 1080;
/// <summary>
/// 最小焦距
/// </summary>
public double MinFocusX { get; set; } = 0f;
public double MinFocusY { get; set; } = 0f;
public static CameraCalcInfo New(long id, PtzInfo ptzInfo)
{
return new()
{
Id = id,
PtzInfo = ptzInfo
};
}
public static CameraCalcInfo New(long id, PtzInfo ptzInfo, int videoWidth, int videoHeight)
{
return new()
{
Id = id,
PtzInfo = ptzInfo,
VideoWidth = videoWidth,
VideoHeight = videoHeight
};
}
}
/// <summary>
/// Ptz 信息
/// </summary>
public class PtzInfo
{
/// <summary>
/// Pan 坐标
/// </summary>
public double Pan { get; set; }
/// <summary>
/// Tilt 坐标
/// </summary>
public double Tilt { get; set; }
/// <summary>
/// Zoom 坐标
/// </summary>
public double Zoom { get; set; }
public static PtzInfo New(double pan, double tilt, double zoom)
{
return new()
{
Pan = pan,
Tilt = tilt,
Zoom = zoom
};
}
}
/// <summary>
/// 标记标签计算信息
/// </summary>
public class MarkLabelCalcInfo
{
/// <summary>
/// MarkLabel Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// Ptz 信息
/// </summary>
public PtzInfo PtzInfo { get; set; }
/// <summary>
/// 视频宽度
/// </summary>
public double VideoWidth { get; set; }
/// <summary>
/// 视频高度
/// </summary>
public double VideoHeight { get; set; }
/// <summary>
/// 画布 left 距离比例
/// </summary>
public double CanvasLeftRatio { get; set; }
/// <summary>
/// 画布 top 距离比例
/// </summary>
public double CanvasTopRatio { get; set; }
public static MarkLabelCalcInfo New(long id, PtzInfo ptzInfo, double videoWidth, double videoHeight, double canvasLeftRatio, double canvasTopRatio)
{
return new()
{
Id = id,
PtzInfo = ptzInfo,
VideoWidth = videoWidth,
VideoHeight = videoHeight,
CanvasLeftRatio = canvasLeftRatio,
CanvasTopRatio = canvasTopRatio
};
}
}
/// <summary>
/// 标记标签计算结果
/// </summary>
public class MarkLabelCalcResult
{
/// <summary>
/// MarkLabel Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// true 显示(在当前视频画面里面)
/// false 不显示(不在当前视频画面里面)
/// </summary>
public bool InFlag { get; set; }
/// <summary>
/// 画布 left 距离比例
/// </summary>
public double CanvasLeftRatio { get; set; }
/// <summary>
/// 画布 top 距离比例
/// </summary>
public double CanvasTopRatio { get; set; }
public static MarkLabelCalcResult New(long id, bool inFlag)
{
return new()
{
Id = id,
InFlag = inFlag
};
}
public static MarkLabelCalcResult New(long id, bool inFlag, double canvasLeftRatio, double canvasTopRatio)
{
return new()
{
Id = id,
InFlag = inFlag,
CanvasLeftRatio = canvasLeftRatio,
CanvasTopRatio = canvasTopRatio
};
}
}

110
Cis.Application/Core/Service/MarkSearchService.cs

@ -1,60 +1,62 @@
using Cis.Application.Cm; using Cis.Application.Core.Component.MarkSeacher;
namespace Cis.Application.Core; namespace Cis.Application.Core;
[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)] [ApiDescriptionSettings(CoreInfo.GroupName, Order = CoreInfo.GroupOrder)]
public class MarkSearchService : IDynamicApiController, ITransient public class MarkSearchService : IDynamicApiController, ITransient
{ {
#region Attr #region Attr
private CameraDataCenter _cameraDataCenter { get; set; } private readonly CameraDataCenter _cameraDataCenter;
private readonly IMarkSearcherServer _markSearcherServer;
#endregion Attr
#endregion Attr
public MarkSearchService(CameraDataCenter cameraDataCenter)
{ public MarkSearchService(CameraDataCenter cameraDataCenter, IMarkSearcherServer markSearcherServer)
_cameraDataCenter = cameraDataCenter; {
} _cameraDataCenter = cameraDataCenter;
_markSearcherServer = markSearcherServer;
[HttpPost] }
public async Task<bool> ActivateSearcher([FromForm] long cameraId)
{ [HttpPost]
return await _cameraDataCenter.ActivateSearcherAsync(cameraId); public async Task<bool> ActivateSearcher([FromForm] long cameraId)
} {
return await _markSearcherServer.ActivateSearcherAsync(cameraId);
[HttpPost] }
public async Task<bool> DeActivateSearcher([FromForm] long cameraId)
{ [HttpPost]
return await _cameraDataCenter.DeActivateSearcherAsync(cameraId); public async Task<bool> DeactivateSearcher([FromForm] long cameraId)
} {
return await _markSearcherServer.DeactivateSearcherAsync(cameraId);
[HttpGet] }
public async Task<bool> IsExistSearcher(long cameraId)
{ [HttpGet]
return await _cameraDataCenter.IsExistSearcherAsync(cameraId); public async Task<bool> IsExistsSearcher(long cameraId)
} {
return await _markSearcherServer.IsExistsSearcherAsync(cameraId);
[HttpPost] }
public async Task<bool> ActivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId)
{ [HttpPost]
return await _cameraDataCenter.ActivateMarkLabelAsync(cameraId, markLabelId); public async Task<bool> ActivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId)
} {
return await _markSearcherServer.ActivateMarkLabelAsync(cameraId, markLabelId);
[HttpPost] }
public async Task<bool> DeactivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId)
{ [HttpPost]
return await _cameraDataCenter.DeactivateMarkLabelAsync(cameraId, markLabelId); public async Task<bool> DeactivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId)
} {
return await _markSearcherServer.DeactivateMarkLabelAsync(cameraId, markLabelId);
[HttpGet] }
public async Task<bool> IsExistMarkLabel(long cameraId,long markLabelId)
{ [HttpGet]
return await _cameraDataCenter.IsExistMarkLabelAysnc(cameraId, markLabelId); public async Task<bool> IsExistsMarkLabel(long cameraId, long markLabelId)
} {
return await _markSearcherServer.IsExistsMarkLabelAsync(cameraId, markLabelId);
[HttpGet] }
public async Task<List<MarkLabelCalcResult>> GetMarkLabelCalcResultList(long cameraId)
{ [HttpGet]
return await _cameraDataCenter.GetMarkLabelCalcResultListAsync(cameraId); public async Task<List<MarkLabelCalcResult>> GetMarkLabelCalcResultList(long cameraId)
} {
return await _cameraDataCenter.GetMarkLabelCalcResultListAsync(cameraId);
}
} }

1
Cis.Application/GlobalUsings.cs

@ -3,7 +3,6 @@ global using Furion;
global using Furion.ConfigurableOptions; global using Furion.ConfigurableOptions;
global using Furion.DependencyInjection; global using Furion.DependencyInjection;
global using Furion.DynamicApiController; global using Furion.DynamicApiController;
global using Furion.FriendlyException;
global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc;
global using SqlSugar; global using SqlSugar;
global using System.ComponentModel.DataAnnotations; global using System.ComponentModel.DataAnnotations;

38
Cis.Application/Startup.cs

@ -1,4 +1,7 @@
using Cis.Application.Core; using Cis.Application.Core;
using Cis.Application.Core.Component.CameraSDK;
using Cis.Application.Core.Component.MarkSeacher;
using Cis.Application.Core.Component.PtzServer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -8,24 +11,25 @@ namespace Cis.Application;
[AppStartup(100)] [AppStartup(100)]
public class Startup : AppStartup public class Startup : AppStartup
{ {
/// <summary> /// <summary>
/// 配置应用所需服务,在该方法中可以添加应用所需要的功能或服务 /// 配置应用所需服务,在该方法中可以添加应用所需要的功能或服务
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddConfigurableOptions<CameraDataOptions>(); services.AddConfigurableOptions<CameraDataOptions>();
services.AddConfigurableOptions<PtzServerOptions>();
services.AddSingleton(new CameraDataCenter()); services.AddSingleton<ICameraSdkServer, CameraSdkServer>();
services.AddSingleton<IMarkSearcherServer, MarkSearcherServer>();
services.AddSingleton(typeof(CameraDataCenter));
} }
/// <summary> /// <summary>
/// 配置应用请求处理管道 /// 配置应用请求处理管道
/// </summary> /// </summary>
/// <param name="app"></param> /// <param name="app"></param>
/// <param name="env"></param> /// <param name="env"></param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ {
} }
} }

68
Cis.Application/Sys/Common/SysInfo.cs

@ -5,50 +5,50 @@
/// </summary> /// </summary>
public class SysInfo public class SysInfo
{ {
#region Api Info #region Api Info
/// <summary> /// <summary>
/// Api 分组名 /// Api 分组名
/// </summary> /// </summary>
public const string GroupName = "System"; public const string GroupName = "System";
/// <summary> /// <summary>
/// Api 分组排序 /// Api 分组排序
/// </summary> /// </summary>
public const int GroupOrder = 100; public const int GroupOrder = 100;
#endregion Api Info #endregion Api Info
#region Database Info #region Database Info
/// <summary> /// <summary>
/// 数据库标识 /// 数据库标识
/// </summary> /// </summary>
public const string DbName = SqlSugarConst.DefaultConfigId; public const string DbName = SqlSugarConst.DefaultConfigId;
#endregion Database Info #endregion Database Info
#region Table Info #region Table Info
/// <summary> /// <summary>
/// SysDictType 表名 /// SysDictType 表名
/// </summary> /// </summary>
public const string SysDictTypeTbName = "sys_dict_type"; public const string SysDictTypeTbName = "sys_dict_type";
/// <summary> /// <summary>
/// SysDictType 表描述 /// SysDictType 表描述
/// </summary> /// </summary>
public const string SysDictTypeTbDesc = "系统字典类型表"; public const string SysDictTypeTbDesc = "系统字典类型表";
/// <summary> /// <summary>
/// SysDataType 表名 /// SysDataType 表名
/// </summary> /// </summary>
public const string SysDictDataTbName = "sys_data_data"; public const string SysDictDataTbName = "sys_data_data";
/// <summary> /// <summary>
/// SysDataType 表描述 /// SysDataType 表描述
/// </summary> /// </summary>
public const string SysDictDataTbDesc = "系统字典值表"; public const string SysDictDataTbDesc = "系统字典值表";
#endregion Table Info #endregion Table Info
} }

56
Cis.Application/Sys/Entity/SysDictType.cs

@ -6,36 +6,36 @@
[SugarTable(SysInfo.SysDictTypeTbName, SysInfo.SysDictTypeTbDesc)] [SugarTable(SysInfo.SysDictTypeTbName, SysInfo.SysDictTypeTbDesc)]
public class SysDictType : EntityBase public class SysDictType : EntityBase
{ {
/// <summary> /// <summary>
/// 名称 /// 名称
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "名称", Length = 64)] [SugarColumn(ColumnDescription = "名称", Length = 64)]
[Required, MaxLength(64)] [Required, MaxLength(64)]
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// 编码 /// 编码
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "编码", Length = 64)] [SugarColumn(ColumnDescription = "编码", Length = 64)]
[Required, MaxLength(64)] [Required, MaxLength(64)]
public string Code { get; set; } public string Code { get; set; }
/// <summary> /// <summary>
/// 排序 /// 排序
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "排序")] [SugarColumn(ColumnDescription = "排序")]
public int Order { get; set; } public int Order { get; set; }
/// <summary> /// <summary>
/// 备注 /// 备注
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "备注", Length = 256)] [SugarColumn(ColumnDescription = "备注", Length = 256)]
[MaxLength(256)] [MaxLength(256)]
public string Remark { get; set; } public string Remark { get; set; }
/// <summary> /// <summary>
/// 状态 /// 状态
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "状态")] [SugarColumn(ColumnDescription = "状态")]
public StatusEnum Status { get; set; } = StatusEnum.Enable; public StatusEnum Status { get; set; } = StatusEnum.Enable;
} }

100
Cis.Application/Sys/Service/SysDictDataService.cs

@ -8,54 +8,54 @@ namespace Cis.Application.Sys;
[ApiDescriptionSettings(SysInfo.GroupName, Order = SysInfo.GroupOrder)] [ApiDescriptionSettings(SysInfo.GroupName, Order = SysInfo.GroupOrder)]
public class SysDictDataService : IDynamicApiController, ITransient public class SysDictDataService : IDynamicApiController, ITransient
{ {
private readonly SqlSugarRepository<SysDictData> _sysDictDataRep; private readonly SqlSugarRepository<SysDictData> _baseRep;
public SysDictDataService(SqlSugarRepository<SysDictData> sysDictDataRep) public SysDictDataService(SqlSugarRepository<SysDictData> baseRep)
{ {
_sysDictDataRep = sysDictDataRep; _baseRep = baseRep;
} }
[HttpPost] [HttpPost]
public async Task<bool> Add([FromForm] SysDictData entity) public async Task<bool> Add([FromForm] SysDictData entity)
{ {
return await _sysDictDataRep.InsertAsync(entity); return await _baseRep.InsertAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Update([FromForm] SysDictData entity) public async Task<bool> Update([FromForm] SysDictData entity)
{ {
return await _sysDictDataRep.UpdateAsync(entity); return await _baseRep.UpdateAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Delete([FromForm] SysDictData entity) public async Task<bool> Delete([FromForm] SysDictData entity)
{ {
return await _sysDictDataRep.DeleteAsync(entity); return await _baseRep.DeleteAsync(entity);
} }
[HttpGet] [HttpGet]
public async Task<SysDictData> Get(long id) public async Task<SysDictData> Get(long id)
{ {
SysDictData entity = await _sysDictDataRep.GetByIdAsync(id); SysDictData entity = await _baseRep.GetByIdAsync(id);
return entity; return entity;
} }
[HttpGet] [HttpGet]
public async Task<List<SysDictData>> GetList(string queryJson) public async Task<List<SysDictData>> GetList(string queryJson)
{ {
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<SysDictData> list = await _sysDictDataRep.AsQueryable() List<SysDictData> list = await _baseRep.AsQueryable()
.ToListAsync(); .ToListAsync();
return list; return list;
} }
[HttpGet] [HttpGet]
public async Task<List<SysDictData>> GetPageList(string queryJson, string pagination) public async Task<List<SysDictData>> GetPageList(string queryJson, string pagination)
{ {
Pagination pageObj = pagination.ToObject<Pagination>(); Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<SysDictData> list = await _sysDictDataRep.AsQueryable() List<SysDictData> list = await _baseRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size); .ToPageListAsync(pageObj.Index, pageObj.Size);
return list; return list;
} }
} }

104
Cis.Application/Sys/Service/SysDictTypeService.cs

@ -1,5 +1,4 @@
using Cis.Application.Cm; using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
namespace Cis.Application.Sys; namespace Cis.Application.Sys;
@ -9,55 +8,54 @@ namespace Cis.Application.Sys;
[ApiDescriptionSettings(SysInfo.GroupName, Order = SysInfo.GroupOrder)] [ApiDescriptionSettings(SysInfo.GroupName, Order = SysInfo.GroupOrder)]
public class SysDictTypeService : IDynamicApiController, ITransient public class SysDictTypeService : IDynamicApiController, ITransient
{ {
private readonly SqlSugarRepository<SysDictType> _sysDictTypeRep; private readonly SqlSugarRepository<SysDictType> _baseRep;
public SysDictTypeService(SqlSugarRepository<SysDictType> sysDictTypeRep) public SysDictTypeService(SqlSugarRepository<SysDictType> baseRep)
{ {
_sysDictTypeRep = sysDictTypeRep; _baseRep = baseRep;
} }
[HttpPost] [HttpPost]
public async Task<bool> Add([FromForm] SysDictType entity) public async Task<bool> Add([FromForm] SysDictType entity)
{ {
return await _sysDictTypeRep.InsertAsync(entity); return await _baseRep.InsertAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Update([FromForm] SysDictType entity) public async Task<bool> Update([FromForm] SysDictType entity)
{ {
return await _sysDictTypeRep.UpdateAsync(entity); return await _baseRep.UpdateAsync(entity);
} }
[HttpPost] [HttpPost]
public async Task<bool> Delete([FromForm] SysDictType entity) public async Task<bool> Delete([FromForm] SysDictType entity)
{ {
return await _sysDictTypeRep.DeleteAsync(entity); return await _baseRep.DeleteAsync(entity);
} }
[HttpGet] [HttpGet]
public async Task<SysDictType> Get(long id) public async Task<SysDictType> Get(long id)
{ {
SysDictType entity = await _sysDictTypeRep.GetByIdAsync(id); SysDictType entity = await _baseRep.GetByIdAsync(id);
return entity; return entity;
} }
[HttpGet] [HttpGet]
public async Task<List<SysDictType>> GetList(string queryJson) public async Task<List<SysDictType>> GetList(string queryJson)
{ {
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<SysDictType> list = await _sysDictTypeRep.AsQueryable() List<SysDictType> list = await _baseRep.AsQueryable()
.ToListAsync(); .ToListAsync();
return list; return list;
} }
[HttpGet] [HttpGet]
public async Task<List<SysDictType>> GetPageList(string queryJson, string pagination) public async Task<List<SysDictType>> GetPageList(string queryJson, string pagination)
{ {
Pagination pageObj = pagination.ToObject<Pagination>(); Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject(); JObject queryObj = queryJson.ToJObject();
List<SysDictType> list = await _sysDictTypeRep.AsQueryable() List<SysDictType> list = await _baseRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size); .ToPageListAsync(pageObj.Index, pageObj.Size);
return list; return list;
} }
} }

22
Cis.Application/Tb/Common/TbInfo.cs

@ -1,22 +0,0 @@
namespace Cis.Application.Tb;
public class TbInfo
{
#region Database Info
/// <summary>
/// 数据库标识
/// </summary>
public const string DbName = "serverdb";
#endregion Database Info
#region Table Info
/// <summary>
/// TbPtzCamera 表名
/// </summary>
public const string TbPtzCameraTbName = "tb_ptzcamera";
#endregion Table Info
}

28
Cis.Application/Tb/Entity/TbPtzCamera.cs

@ -1,28 +0,0 @@
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; }
}

57
Cis.Application/Tb/Service/TbPtzCameraService.cs

@ -1,57 +0,0 @@
using Newtonsoft.Json.Linq;
namespace Cis.Application.Tb;
public class TbPtzCameraService : ITransient
{
private readonly SqlSugarRepository<TbPtzCamera> _tbPtzCameraRep;
public TbPtzCameraService(SqlSugarRepository<TbPtzCamera> tbPtzCameraRep)
{
_tbPtzCameraRep = tbPtzCameraRep;
}
[HttpPost]
public async Task<bool> Add([FromForm] TbPtzCamera entity)
{
return await _tbPtzCameraRep.InsertAsync(entity);
}
[HttpPost]
public async Task<bool> Update([FromForm] TbPtzCamera entity)
{
return await _tbPtzCameraRep.UpdateAsync(entity);
}
[HttpPost]
public async Task<bool> Delete([FromForm] TbPtzCamera entity)
{
return await _tbPtzCameraRep.DeleteAsync(entity);
}
[HttpGet]
public async Task<TbPtzCamera> Get(int id)
{
TbPtzCamera entity = await _tbPtzCameraRep.GetByIdAsync(id);
return entity;
}
[HttpGet]
public async Task<List<TbPtzCamera>> GetList(string queryJson = "")
{
JObject queryObj = !string.IsNullOrEmpty(queryJson) ? queryJson.ToJObject() : default;
List<TbPtzCamera> list = await _tbPtzCameraRep.AsQueryable()
.ToListAsync();
return list;
}
[HttpGet]
public async Task<List<TbPtzCamera>> GetPageList(string pagination, string queryJson = "")
{
Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject();
List<TbPtzCamera> list = await _tbPtzCameraRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size);
return list;
}
}

37
Cis.Core/Cache/CacheSetup.cs

@ -1,28 +1,21 @@
using NewLife.Caching; using StackExchange.Redis;
namespace Cis.Core; namespace Cis.Core;
public static class CacheSetup public static class CacheSetup
{ {
/// <summary> /// <summary>
/// 缓存注册(新生命Redis组件) /// Redis 缓存注册
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
public static void AddCache(this IServiceCollection services) public static void AddCache(this IServiceCollection services)
{ {
services.AddSingleton<ICache>(options => services.AddSingleton<IDatabase>(options =>
{ {
var cacheOptions = App.GetOptions<CacheOptions>(); var redisOptions = App.GetOptions<RedisOptions>();
if (cacheOptions.CacheType == CacheTypeEnum.Redis.ToString()) ConnectionMultiplexer multiplexer = ConnectionMultiplexer.Connect(redisOptions.ConnectionString);
{ IDatabase cache = multiplexer.GetDatabase();
var redis = new Redis(); return cache;
redis.Init(cacheOptions.RedisConnectionString); });
return redis; }
}
else
{
return Cache.Default;
}
});
}
} }

82
Cis.Core/Cache/SqlSugarCache.cs

@ -7,45 +7,45 @@ namespace Cis.Core;
/// </summary> /// </summary>
public class SqlSugarCache : ICacheService, ISingleton public class SqlSugarCache : ICacheService, ISingleton
{ {
private static readonly ICache _cache = NewLife.Caching.Cache.Default; private static readonly ICache _cache = NewLife.Caching.Cache.Default;
public void Add<V>(string key, V value) public void Add<V>(string key, V value)
{ {
_cache.Set(key, value); _cache.Set(key, value);
} }
public void Add<V>(string key, V value, int cacheDurationInSeconds) public void Add<V>(string key, V value, int cacheDurationInSeconds)
{ {
_cache.Set(key, value, cacheDurationInSeconds); _cache.Set(key, value, cacheDurationInSeconds);
} }
public bool ContainsKey<V>(string key) public bool ContainsKey<V>(string key)
{ {
return _cache.ContainsKey(key); return _cache.ContainsKey(key);
} }
public V Get<V>(string key) public V Get<V>(string key)
{ {
return _cache.Get<V>(key); return _cache.Get<V>(key);
} }
public IEnumerable<string> GetAllKey<V>() public IEnumerable<string> GetAllKey<V>()
{ {
return _cache.Keys; return _cache.Keys;
} }
public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue) public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue)
{ {
if (!_cache.TryGetValue<V>(cacheKey, out V value)) if (!_cache.TryGetValue<V>(cacheKey, out V value))
{ {
value = create(); value = create();
_cache.Set(cacheKey, value, cacheDurationInSeconds); _cache.Set(cacheKey, value, cacheDurationInSeconds);
} }
return value; return value;
} }
public void Remove<V>(string key) public void Remove<V>(string key)
{ {
_cache.Remove(key); _cache.Remove(key);
} }
} }

20
Cis.Core/Cis.Core.csproj

@ -6,6 +6,12 @@
<DocumentationFile>Cis.Core.xml</DocumentationFile> <DocumentationFile>Cis.Core.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="Component\**" />
<EmbeddedResource Remove="Component\**" />
<None Remove="Component\**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Cis.Core.xml" /> <None Remove="Cis.Core.xml" />
<None Remove="CoreConfig.json" /> <None Remove="CoreConfig.json" />
@ -18,20 +24,16 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.4.9" /> <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.2.2" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.4.9" /> <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.4.9" />
<PackageReference Include="Furion.Pure" Version="4.4.9" /> <PackageReference Include="Furion.Pure" Version="4.4.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.11" />
<PackageReference Include="NewLife.Core" Version="10.0.2022.1001" /> <PackageReference Include="NewLife.Core" Version="10.0.2022.1101" />
<PackageReference Include="NewLife.Redis" Version="5.0.2022.901" />
<PackageReference Include="SqlSugarCore" Version="5.1.3.1" /> <PackageReference Include="SqlSugarCore" Version="5.1.3.1" />
<PackageReference Include="StackExchange.Redis" Version="2.6.66" /> <PackageReference Include="StackExchange.Redis" Version="2.6.80" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ProjectExtensions><VisualStudio><UserProperties coreconfig_1json__JsonSchema="https://download.stackhawk.com/hawk/jsonschema/hawkconfig.json" /></VisualStudio></ProjectExtensions>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>
</Project> </Project>

37
Cis.Core/Cis.Core.xml

@ -4,14 +4,9 @@
<name>Cis.Core</name> <name>Cis.Core</name>
</assembly> </assembly>
<members> <members>
<member name="T:Cis.Core.NotTableAttribute">
<summary>
非实体表特性
</summary>
</member>
<member name="M:Cis.Core.CacheSetup.AddCache(Microsoft.Extensions.DependencyInjection.IServiceCollection)"> <member name="M:Cis.Core.CacheSetup.AddCache(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary> <summary>
缓存注册(新生命Redis组件) Redis 缓存注册
</summary> </summary>
<param name="services"></param> <param name="services"></param>
</member> </member>
@ -20,6 +15,11 @@
SqlSugar二级缓存(必须是内存缓存) SqlSugar二级缓存(必须是内存缓存)
</summary> </summary>
</member> </member>
<member name="T:Cis.Core.NotTableAttribute">
<summary>
非实体表特性
</summary>
</member>
<member name="F:Cis.Core.SqlSugarConst.DefaultConfigId"> <member name="F:Cis.Core.SqlSugarConst.DefaultConfigId">
<summary> <summary>
默认数据库标识 默认数据库标识
@ -289,21 +289,6 @@
<param name="pi"></param> <param name="pi"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="T:Cis.Core.CacheOptions">
<summary>
缓存配置选项
</summary>
</member>
<member name="P:Cis.Core.CacheOptions.CacheType">
<summary>
缓存类型
</summary>
</member>
<member name="P:Cis.Core.CacheOptions.RedisConnectionString">
<summary>
Redis连接字符串
</summary>
</member>
<member name="T:Cis.Core.DbConnectionOptions"> <member name="T:Cis.Core.DbConnectionOptions">
<summary> <summary>
数据库配置选项 数据库配置选项
@ -319,6 +304,16 @@
启用库表初始化 启用库表初始化
</summary> </summary>
</member> </member>
<member name="T:Cis.Core.RedisOptions">
<summary>
Redis 配置选项
</summary>
</member>
<member name="P:Cis.Core.RedisOptions.ConnectionString">
<summary>
Redis 连接字符串
</summary>
</member>
<member name="T:Cis.Core.SnowIdOptions"> <member name="T:Cis.Core.SnowIdOptions">
<summary> <summary>
雪花Id配置选项 雪花Id配置选项

0
Cis.Core/Attribute/NotTableAttribute.cs → Cis.Core/Common/Attribute/NotTableAttribute.cs

14
Cis.Core/Common/Const/SqlSugarConst.cs

@ -0,0 +1,14 @@
namespace Cis.Core;
public class SqlSugarConst
{
/// <summary>
/// 默认数据库标识
/// </summary>
public const string DefaultConfigId = "Cis";
/// <summary>
/// 默认表主键
/// </summary>
public const string DefaultPrimaryKey = "Id";
}

61
Cis.Core/Common/Entity/EntityBase.cs

@ -0,0 +1,61 @@
namespace Cis.Core;
/// <summary>
/// 框架实体基类Id
/// </summary>
public abstract class EntityBaseId
{
/// <summary>
/// 雪花Id
/// </summary>
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true, IsIdentity = false)]
public virtual long Id { get; set; }
}
/// <summary>
/// 框架实体基类
/// </summary>
public abstract class EntityBase : EntityBaseId
{
/// <summary>
/// 创建时间
/// </summary>
[SugarColumn(ColumnDescription = "创建时间")]
public virtual DateTime? CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[SugarColumn(ColumnDescription = "更新时间")]
public virtual DateTime? UpdateTime { get; set; }
/// <summary>
/// 创建者Id
/// </summary>
[SugarColumn(ColumnDescription = "创建者Id")]
public virtual long? CreateUserId { get; set; }
/// <summary>
/// 修改者Id
/// </summary>
[SugarColumn(ColumnDescription = "修改者Id")]
public virtual long? UpdateUserId { get; set; }
/// <summary>
/// 软删除
/// </summary>
[SugarColumn(ColumnDescription = "软删除")]
public virtual bool IsDelete { get; set; } = false;
}
/// <summary>
/// 业务数据实体基类(数据权限)
/// </summary>
public abstract class DataEntityBase : EntityBase
{
/// <summary>
/// 创建者部门Id
/// </summary>
[SugarColumn(ColumnDescription = "创建者部门Id")]
public virtual long? CreateOrgId { get; set; }
}

47
Cis.Core/Common/Entity/Pagination.cs

@ -0,0 +1,47 @@
namespace Cis.Core;
public class Pagination
{
/// <summary>
/// 当前页
/// </summary>
public virtual int Index { get; set; }
/// <summary>
/// 页码容量
/// </summary>
public virtual int Size { get; set; }
/// <summary>
/// 排序字段
/// </summary>
public virtual string Field { get; set; }
/// <summary>
/// 排序方向
/// </summary>
public virtual string Order { get; set; }
/// <summary>
/// 总记录数
/// </summary>
public virtual int Total { get; set; }
/// <summary>
/// 页码数
/// </summary>
public virtual int Number
{
get
{
if (Size > 0)
{
return ((Size % Total) == 0) ? (Size / Total) : ((Size / Total) + 1);
}
else
{
return 0;
}
}
}
}

128
Cis.Core/Common/Entity/RespParamProvider.cs

@ -0,0 +1,128 @@
namespace Cis.Core;
/// <summary>
/// 全局规范化结果
/// </summary>
[UnifyModel(typeof(RespParam<>))]
public class RespParamProvider : IUnifyResultProvider
{
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
}
/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
{
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
}
/// <summary>
/// 验证失败返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult));
}
/// <summary>
/// 特定状态码返回值
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
// 设置响应状态码
UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings);
switch (statusCode)
{
// 处理 401 状态码
case StatusCodes.Status401Unauthorized:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 登录已过期,请重新登录"),
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 禁止访问,没有权限"),
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
default: break;
}
}
/// <summary>
/// 返回 RESTful 风格结果集
/// </summary>
/// <param name="statusCode"></param>
/// <param name="succeeded"></param>
/// <param name="data"></param>
/// <param name="errors"></param>
/// <returns></returns>
private static RespParam<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
{
return new RespParam<object>
{
Code = statusCode,
Message = errors is string str ? str : JSON.Serialize(errors),
Data = data,
Type = succeeded ? "success" : "error",
Extras = UnifyContext.Take(),
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
}
}
/// <summary>
/// 全局返回结果
/// </summary>
/// <typeparam name="T"></typeparam>
public class RespParam<T>
{
/// <summary>
/// 状态码
/// </summary>
public int Code { get; set; }
/// <summary>
/// 类型success、warning、error
/// </summary>
public string Type { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 数据
/// </summary>
public T Data { get; set; }
/// <summary>
/// 附加数据
/// </summary>
public object Extras { get; set; }
/// <summary>
/// 时间戳
/// </summary>
public long Timestamp { get; set; }
}

0
Cis.Core/Enum/CacheTypeEnum.cs → Cis.Core/Common/Enum/CacheTypeEnum.cs

19
Cis.Core/Common/Enum/StatusEnum.cs

@ -0,0 +1,19 @@
namespace Cis.Core;
/// <summary>
/// 通用状态枚举
/// </summary>
public enum StatusEnum
{
/// <summary>
/// 停用
/// </summary>
[Description("停用")]
Disable = 0,
/// <summary>
/// 启用
/// </summary>
[Description("启用")]
Enable = 1,
}

141
Cis.Core/Common/Extension/ObjectExtension.cs

@ -0,0 +1,141 @@
using Newtonsoft.Json.Linq;
using System.Text;
namespace Cis.Core;
/// <summary>
/// 对象拓展
/// </summary>
[SuppressSniffer]
public static class ObjectExtension
{
/// <summary>
/// 判断类型是否实现某个泛型
/// </summary>
/// <param name="type">类型</param>
/// <param name="generic">泛型类型</param>
/// <returns>bool</returns>
public static bool HasImplementedRawGeneric(this Type type, Type generic)
{
// 检查接口类型
var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
if (isTheRawGenericType) return true;
// 检查类型
while (type != null && type != typeof(object))
{
isTheRawGenericType = IsTheRawGenericType(type);
if (isTheRawGenericType) return true;
type = type.BaseType;
}
return false;
// 判断逻辑
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
}
/// <summary>
/// 将字典转化为QueryString格式
/// </summary>
/// <param name="dict"></param>
/// <param name="urlEncode"></param>
/// <returns></returns>
public static string ToQueryString(this Dictionary<string, string> dict, bool urlEncode = true)
{
return string.Join("&", dict.Select(p => $"{(urlEncode ? p.Key?.UrlEncode() : "")}={(urlEncode ? p.Value?.UrlEncode() : "")}"));
}
/// <summary>
/// 将字符串URL编码
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string UrlEncode(this string str)
{
return string.IsNullOrEmpty(str) ? "" : System.Web.HttpUtility.UrlEncode(str, Encoding.UTF8);
}
/// <summary>
/// List转DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public static DataTable ToDataTable<T>(this List<T> list)
{
DataTable result = new();
if (list.Count > 0)
{
// result.TableName = list[0].GetType().Name; // 表名赋值
PropertyInfo[] propertys = list[0].GetType().GetProperties();
foreach (PropertyInfo pi in propertys)
{
Type colType = pi.PropertyType;
if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
colType = colType.GetGenericArguments()[0];
}
if (IsIgnoreColumn(pi))
continue;
result.Columns.Add(pi.Name, colType);
}
for (int i = 0; i < list.Count; i++)
{
ArrayList tempList = new();
foreach (PropertyInfo pi in propertys)
{
if (IsIgnoreColumn(pi))
continue;
object obj = pi.GetValue(list[i], null);
tempList.Add(obj);
}
object[] array = tempList.ToArray();
result.LoadDataRow(array, true);
}
}
return result;
}
/// <summary>
/// 对象序列化成Json字符串
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToJson(object obj)
{
return JSON.GetJsonSerializer().Serialize(obj);
}
/// <summary>
/// Json字符串反序列化成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <returns></returns>
public static T ToObject<T>(this string json)
{
return JSON.GetJsonSerializer().Deserialize<T>(json);
}
/// <summary>
/// 字串反序列化成linq对象
/// </summary>
/// <param name="Json">字串</param>
/// <returns></returns>
public static JObject ToJObject(this string Json)
{
return string.IsNullOrEmpty(Json) ? JObject.Parse("{}") : JObject.Parse(Json.Replace("&nbsp;", ""));
}
/// <summary>
/// 排除SqlSugar忽略的列
/// </summary>
/// <param name="pi"></param>
/// <returns></returns>
private static bool IsIgnoreColumn(PropertyInfo pi)
{
var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsIgnore == true);
return sc != null;
}
}

20
Cis.Core/Common/Option/DbConnectionOptions.cs

@ -0,0 +1,20 @@
namespace Cis.Core;
/// <summary>
/// 数据库配置选项
/// </summary>
public sealed class DbConnectionOptions : IConfigurableOptions
{
/// <summary>
/// 数据库集合
/// </summary>
public List<DbConnectionConfig> ConnectionConfigs { get; set; }
}
public sealed class DbConnectionConfig : ConnectionConfig
{
/// <summary>
/// 启用库表初始化
/// </summary>
public bool EnableInitDb { get; set; }
}

12
Cis.Core/Common/Option/RedisOptions.cs

@ -0,0 +1,12 @@
namespace Cis.Core;
/// <summary>
/// Redis 配置选项
/// </summary>
public sealed class RedisOptions : IConfigurableOptions
{
/// <summary>
/// Redis 连接字符串
/// </summary>
public string ConnectionString { get; set; }
}

8
Cis.Core/Option/SnowIdOptions.cs → Cis.Core/Common/Option/SnowIdOptions.cs

@ -5,8 +5,8 @@
/// </summary> /// </summary>
public sealed class SnowIdOptions : IConfigurableOptions public sealed class SnowIdOptions : IConfigurableOptions
{ {
/// <summary> /// <summary>
/// 机器码 /// 机器码
/// </summary> /// </summary>
public ushort WorkerId { get; set; } public ushort WorkerId { get; set; }
} }

14
Cis.Core/Const/SqlSugarConst.cs

@ -1,14 +0,0 @@
namespace Cis.Core;
public class SqlSugarConst
{
/// <summary>
/// 默认数据库标识
/// </summary>
public const string DefaultConfigId = "Cis";
/// <summary>
/// 默认表主键
/// </summary>
public const string DefaultPrimaryKey = "Id";
}

14
Cis.Core/CoreConfig.json

@ -1,5 +1,4 @@
{ {
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
"DbConnection": { "DbConnection": {
"ConnectionConfigs": [ "ConnectionConfigs": [
{ {
@ -10,19 +9,12 @@
//"ConnectionString": "DataSource=./cis.db", //"ConnectionString": "DataSource=./cis.db",
//"DbType": "PostgreSQL", //"DbType": "PostgreSQL",
//"ConnectionString": "HOST=127.0.0.1;PORT=5432;USER ID=pgsql;PASSWORD=123456;DATABASE=cis;", //"ConnectionString": "HOST=127.0.0.1;PORT=5432;USER ID=pgsql;PASSWORD=123456;DATABASE=cis;",
"EnableInitDb": false // "EnableInitDb": true //
},
{
"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 //
} }
] ]
}, },
"Cache": { "Redis": {
"CacheType": "Memory", // MemoryRedis "ConnectionString": "127.0.0.1:6379,password=123456,defaultDatabase=2"
"RedisConnectionString": "127.0.0.1:6379;password=123456;db=2"
}, },
"AppSettings": { "AppSettings": {
"InjectSpecificationDocument": true // Swagger "InjectSpecificationDocument": true // Swagger

61
Cis.Core/Entity/EntityBase.cs

@ -1,61 +0,0 @@
namespace Cis.Core;
/// <summary>
/// 框架实体基类Id
/// </summary>
public abstract class EntityBaseId
{
/// <summary>
/// 雪花Id
/// </summary>
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true, IsIdentity = false)]
public virtual long Id { get; set; }
}
/// <summary>
/// 框架实体基类
/// </summary>
public abstract class EntityBase : EntityBaseId
{
/// <summary>
/// 创建时间
/// </summary>
[SugarColumn(ColumnDescription = "创建时间")]
public virtual DateTime? CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[SugarColumn(ColumnDescription = "更新时间")]
public virtual DateTime? UpdateTime { get; set; }
/// <summary>
/// 创建者Id
/// </summary>
[SugarColumn(ColumnDescription = "创建者Id")]
public virtual long? CreateUserId { get; set; }
/// <summary>
/// 修改者Id
/// </summary>
[SugarColumn(ColumnDescription = "修改者Id")]
public virtual long? UpdateUserId { get; set; }
/// <summary>
/// 软删除
/// </summary>
[SugarColumn(ColumnDescription = "软删除")]
public virtual bool IsDelete { get; set; } = false;
}
/// <summary>
/// 业务数据实体基类(数据权限)
/// </summary>
public abstract class DataEntityBase : EntityBase
{
/// <summary>
/// 创建者部门Id
/// </summary>
[SugarColumn(ColumnDescription = "创建者部门Id")]
public virtual long? CreateOrgId { get; set; }
}

47
Cis.Core/Entity/Pagination.cs

@ -1,47 +0,0 @@
namespace Cis.Core;
public class Pagination
{
/// <summary>
/// 当前页
/// </summary>
public virtual int Index { get; set; }
/// <summary>
/// 页码容量
/// </summary>
public virtual int Size { get; set; }
/// <summary>
/// 排序字段
/// </summary>
public virtual string Field { get; set; }
/// <summary>
/// 排序方向
/// </summary>
public virtual string Order { get; set; }
/// <summary>
/// 总记录数
/// </summary>
public virtual int Total { get; set; }
/// <summary>
/// 页码数
/// </summary>
public virtual int Number
{
get
{
if (Size > 0)
{
return ((Size % Total) == 0) ? (Size / Total) : ((Size / Total) + 1);
}
else
{
return 0;
}
}
}
}

128
Cis.Core/Entity/RespParamProvider.cs

@ -1,128 +0,0 @@
namespace Cis.Core;
/// <summary>
/// 全局规范化结果
/// </summary>
[UnifyModel(typeof(RespParam<>))]
public class RespParamProvider : IUnifyResultProvider
{
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors));
}
/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
{
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
}
/// <summary>
/// 验证失败返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult));
}
/// <summary>
/// 特定状态码返回值
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
// 设置响应状态码
UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings);
switch (statusCode)
{
// 处理 401 状态码
case StatusCodes.Status401Unauthorized:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 登录已过期,请重新登录"),
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 禁止访问,没有权限"),
App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
default: break;
}
}
/// <summary>
/// 返回 RESTful 风格结果集
/// </summary>
/// <param name="statusCode"></param>
/// <param name="succeeded"></param>
/// <param name="data"></param>
/// <param name="errors"></param>
/// <returns></returns>
private static RespParam<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
{
return new RespParam<object>
{
Code = statusCode,
Message = errors is string str ? str : JSON.Serialize(errors),
Data = data,
Type = succeeded ? "success" : "error",
Extras = UnifyContext.Take(),
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
}
}
/// <summary>
/// 全局返回结果
/// </summary>
/// <typeparam name="T"></typeparam>
public class RespParam<T>
{
/// <summary>
/// 状态码
/// </summary>
public int Code { get; set; }
/// <summary>
/// 类型success、warning、error
/// </summary>
public string Type { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 数据
/// </summary>
public T Data { get; set; }
/// <summary>
/// 附加数据
/// </summary>
public object Extras { get; set; }
/// <summary>
/// 时间戳
/// </summary>
public long Timestamp { get; set; }
}

19
Cis.Core/Enum/StatusEnum.cs

@ -1,19 +0,0 @@
namespace Cis.Core;
/// <summary>
/// 通用状态枚举
/// </summary>
public enum StatusEnum
{
/// <summary>
/// 停用
/// </summary>
[Description("停用")]
Disable = 0,
/// <summary>
/// 启用
/// </summary>
[Description("启用")]
Enable = 1,
}

151
Cis.Core/Extension/ObjectExtension.cs

@ -1,151 +0,0 @@
using Newtonsoft.Json.Linq;
using System.Text;
namespace Cis.Core;
/// <summary>
/// 对象拓展
/// </summary>
[SuppressSniffer]
public static class ObjectExtension
{
/// <summary>
/// 判断类型是否实现某个泛型
/// </summary>
/// <param name="type">类型</param>
/// <param name="generic">泛型类型</param>
/// <returns>bool</returns>
public static bool HasImplementedRawGeneric(this Type type, Type generic)
{
// 检查接口类型
var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
if (isTheRawGenericType) return true;
// 检查类型
while (type != null && type != typeof(object))
{
isTheRawGenericType = IsTheRawGenericType(type);
if (isTheRawGenericType) return true;
type = type.BaseType;
}
return false;
// 判断逻辑
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
}
/// <summary>
/// 将字典转化为QueryString格式
/// </summary>
/// <param name="dict"></param>
/// <param name="urlEncode"></param>
/// <returns></returns>
public static string ToQueryString(this Dictionary<string, string> dict, bool urlEncode = true)
{
return string.Join("&", dict.Select(p => $"{(urlEncode ? p.Key?.UrlEncode() : "")}={(urlEncode ? p.Value?.UrlEncode() : "")}"));
}
/// <summary>
/// 将字符串URL编码
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string UrlEncode(this string str)
{
return string.IsNullOrEmpty(str) ? "" : System.Web.HttpUtility.UrlEncode(str, Encoding.UTF8);
}
/// <summary>
/// List转DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public static DataTable ToDataTable<T>(this List<T> list)
{
DataTable result = new();
if (list.Count > 0)
{
// result.TableName = list[0].GetType().Name; // 表名赋值
PropertyInfo[] propertys = list[0].GetType().GetProperties();
foreach (PropertyInfo pi in propertys)
{
Type colType = pi.PropertyType;
if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
colType = colType.GetGenericArguments()[0];
}
if (IsIgnoreColumn(pi))
continue;
result.Columns.Add(pi.Name, colType);
}
for (int i = 0; i < list.Count; i++)
{
ArrayList tempList = new();
foreach (PropertyInfo pi in propertys)
{
if (IsIgnoreColumn(pi))
continue;
object obj = pi.GetValue(list[i], null);
tempList.Add(obj);
}
object[] array = tempList.ToArray();
result.LoadDataRow(array, true);
}
}
return result;
}
/// <summary>
/// 对象序列化成Json字符串
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToJson(this object obj)
{
return JSON.GetJsonSerializer().Serialize(obj);
}
/// <summary>
/// Json字符串反序列化成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <returns></returns>
public static T ToObject<T>(this string json)
{
return JSON.GetJsonSerializer().Deserialize<T>(json);
}
/// <summary>
/// 字串反序列化成linq对象
/// </summary>
/// <param name="Json">字串</param>
/// <returns></returns>
public static JObject ToJObject(this string Json)
{
return string.IsNullOrEmpty(Json) ? JObject.Parse("{}") : JObject.Parse(Json.Replace("&nbsp;", ""));
}
/// <summary>
/// 排除SqlSugar忽略的列
/// </summary>
/// <param name="pi"></param>
/// <returns></returns>
private static bool IsIgnoreColumn(PropertyInfo pi)
{
var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsIgnore == true);
return sc != null;
}
public static bool ExistsKey(this IDictionary dict, object key)
{
foreach (object item in dict.Keys)
{
if (Equals(key, item))
return true;
}
return false;
}
}

17
Cis.Core/Option/CacheOptions.cs

@ -1,17 +0,0 @@
namespace Cis.Core;
/// <summary>
/// 缓存配置选项
/// </summary>
public sealed class CacheOptions : IConfigurableOptions
{
/// <summary>
/// 缓存类型
/// </summary>
public string CacheType { get; set; }
/// <summary>
/// Redis连接字符串
/// </summary>
public string RedisConnectionString { get; set; }
}

20
Cis.Core/Option/DbConnectionOptions.cs

@ -1,20 +0,0 @@
namespace Cis.Core;
/// <summary>
/// 数据库配置选项
/// </summary>
public sealed class DbConnectionOptions : IConfigurableOptions
{
/// <summary>
/// 数据库集合
/// </summary>
public List<DbConnectionConfig> ConnectionConfigs { get; set; }
}
public sealed class DbConnectionConfig : ConnectionConfig
{
/// <summary>
/// 启用库表初始化
/// </summary>
public bool EnableInitDb { get; set; }
}

10
Cis.Core/SqlSugar/IEntityFilter.cs

@ -5,9 +5,9 @@
/// </summary> /// </summary>
public interface IEntityFilter public interface IEntityFilter
{ {
/// <summary> /// <summary>
/// 实体过滤器 /// 实体过滤器
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
IEnumerable<TableFilterItem<object>> AddEntityFilter(); IEnumerable<TableFilterItem<object>> AddEntityFilter();
} }

12
Cis.Core/SqlSugar/ISqlSugarEntitySeedData.cs

@ -5,11 +5,11 @@
/// </summary> /// </summary>
/// <typeparam name="TEntity"></typeparam> /// <typeparam name="TEntity"></typeparam>
public interface ISqlSugarEntitySeedData<TEntity> public interface ISqlSugarEntitySeedData<TEntity>
where TEntity : class, new() where TEntity : class, new()
{ {
/// <summary> /// <summary>
/// 种子数据 /// 种子数据
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
IEnumerable<TEntity> HasData(); IEnumerable<TEntity> HasData();
} }

154
Cis.Core/SqlSugar/SqlSugarPagedList.cs

@ -5,42 +5,42 @@
/// </summary> /// </summary>
/// <typeparam name="TEntity"></typeparam> /// <typeparam name="TEntity"></typeparam>
public class SqlSugarPagedList<TEntity> public class SqlSugarPagedList<TEntity>
where TEntity : new() where TEntity : new()
{ {
/// <summary> /// <summary>
/// 页码 /// 页码
/// </summary> /// </summary>
public int Page { get; set; } public int Page { get; set; }
/// <summary> /// <summary>
/// 页容量 /// 页容量
/// </summary> /// </summary>
public int PageSize { get; set; } public int PageSize { get; set; }
/// <summary> /// <summary>
/// 总条数 /// 总条数
/// </summary> /// </summary>
public int Total { get; set; } public int Total { get; set; }
/// <summary> /// <summary>
/// 总页数 /// 总页数
/// </summary> /// </summary>
public int TotalPages { get; set; } public int TotalPages { get; set; }
/// <summary> /// <summary>
/// 当前页集合 /// 当前页集合
/// </summary> /// </summary>
public IEnumerable<TEntity> Items { get; set; } public IEnumerable<TEntity> Items { get; set; }
/// <summary> /// <summary>
/// 是否有上一页 /// 是否有上一页
/// </summary> /// </summary>
public bool HasPrevPage { get; set; } public bool HasPrevPage { get; set; }
/// <summary> /// <summary>
/// 是否有下一页 /// 是否有下一页
/// </summary> /// </summary>
public bool HasNextPage { get; set; } public bool HasNextPage { get; set; }
} }
/// <summary> /// <summary>
@ -48,53 +48,53 @@ public class SqlSugarPagedList<TEntity>
/// </summary> /// </summary>
public static class SqlSugarPagedExtensions public static class SqlSugarPagedExtensions
{ {
/// <summary> /// <summary>
/// 分页拓展 /// 分页拓展
/// </summary> /// </summary>
/// <param name="entity"></param> /// <param name="entity"></param>
/// <param name="pageIndex"></param> /// <param name="pageIndex"></param>
/// <param name="pageSize"></param> /// <param name="pageSize"></param>
/// <returns></returns> /// <returns></returns>
public static SqlSugarPagedList<TEntity> ToPagedList<TEntity>(this ISugarQueryable<TEntity> entity, int pageIndex, int pageSize) public static SqlSugarPagedList<TEntity> ToPagedList<TEntity>(this ISugarQueryable<TEntity> entity, int pageIndex, int pageSize)
where TEntity : new() where TEntity : new()
{ {
var total = 0; var total = 0;
var items = entity.ToPageList(pageIndex, pageSize, ref total); var items = entity.ToPageList(pageIndex, pageSize, ref total);
var totalPages = (int)Math.Ceiling(total / (double)pageSize); var totalPages = (int)Math.Ceiling(total / (double)pageSize);
return new SqlSugarPagedList<TEntity> return new SqlSugarPagedList<TEntity>
{ {
Page = pageIndex, Page = pageIndex,
PageSize = pageSize, PageSize = pageSize,
Items = items, Items = items,
Total = total, Total = total,
TotalPages = totalPages, TotalPages = totalPages,
HasNextPage = pageIndex < totalPages, HasNextPage = pageIndex < totalPages,
HasPrevPage = pageIndex - 1 > 0 HasPrevPage = pageIndex - 1 > 0
}; };
} }
/// <summary> /// <summary>
/// 分页拓展 /// 分页拓展
/// </summary> /// </summary>
/// <param name="entity"></param> /// <param name="entity"></param>
/// <param name="pageIndex"></param> /// <param name="pageIndex"></param>
/// <param name="pageSize"></param> /// <param name="pageSize"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<SqlSugarPagedList<TEntity>> ToPagedListAsync<TEntity>(this ISugarQueryable<TEntity> entity, int pageIndex, int pageSize) public static async Task<SqlSugarPagedList<TEntity>> ToPagedListAsync<TEntity>(this ISugarQueryable<TEntity> entity, int pageIndex, int pageSize)
where TEntity : new() where TEntity : new()
{ {
RefAsync<int> total = 0; RefAsync<int> total = 0;
var items = await entity.ToPageListAsync(pageIndex, pageSize, total); var items = await entity.ToPageListAsync(pageIndex, pageSize, total);
var totalPages = (int)Math.Ceiling(total / (double)pageSize); var totalPages = (int)Math.Ceiling(total / (double)pageSize);
return new SqlSugarPagedList<TEntity> return new SqlSugarPagedList<TEntity>
{ {
Page = pageIndex, Page = pageIndex,
PageSize = pageSize, PageSize = pageSize,
Items = items, Items = items,
Total = total, Total = total,
TotalPages = totalPages, TotalPages = totalPages,
HasNextPage = pageIndex < totalPages, HasNextPage = pageIndex < totalPages,
HasPrevPage = pageIndex - 1 > 0 HasPrevPage = pageIndex - 1 > 0
}; };
} }
} }

12
Cis.Core/SqlSugar/SqlSugarRepository.cs

@ -6,11 +6,11 @@
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class SqlSugarRepository<T> : SimpleClient<T> where T : class, new() public class SqlSugarRepository<T> : SimpleClient<T> where T : class, new()
{ {
protected ITenant iTenant = null; // 多租户事务 protected ITenant iTenant = null; // 多租户事务
public SqlSugarRepository(ISqlSugarClient context = null) : base(context) // 默认值等于null不能少 public SqlSugarRepository(ISqlSugarClient context = null) : base(context) // 默认值等于null不能少
{ {
iTenant = App.GetService<ISqlSugarClient>().AsTenant(); iTenant = App.GetService<ISqlSugarClient>().AsTenant();
base.Context = iTenant.GetConnectionWithAttr<T>(); base.Context = iTenant.GetConnectionWithAttr<T>();
} }
} }

286
Cis.Core/SqlSugar/SqlSugarSetup.cs

@ -2,158 +2,158 @@
public static class SqlSugarSetup public static class SqlSugarSetup
{ {
/// <summary> /// <summary>
/// Sqlsugar 上下文初始化 /// Sqlsugar 上下文初始化
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
public static void AddSqlSugar(this IServiceCollection services) public static void AddSqlSugar(this IServiceCollection services)
{ {
var dbOptions = App.GetOptions<DbConnectionOptions>(); var dbOptions = App.GetOptions<DbConnectionOptions>();
var configureExternalServices = new ConfigureExternalServices var configureExternalServices = new ConfigureExternalServices
{ {
EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required
{ {
if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|| (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null)) || (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null))
column.IsNullable = true; column.IsNullable = true;
}, },
DataInfoCacheService = new SqlSugarCache(), DataInfoCacheService = new SqlSugarCache(),
}; };
dbOptions.ConnectionConfigs.ForEach(config => dbOptions.ConnectionConfigs.ForEach(config =>
{ {
config.ConfigureExternalServices = configureExternalServices; config.ConfigureExternalServices = configureExternalServices;
config.InitKeyType = InitKeyType.Attribute; config.InitKeyType = InitKeyType.Attribute;
config.IsAutoCloseConnection = true; config.IsAutoCloseConnection = true;
config.MoreSettings = new ConnMoreSettings config.MoreSettings = new ConnMoreSettings
{ {
IsAutoRemoveDataCache = true IsAutoRemoveDataCache = true
}; };
}); });
SqlSugarScope sqlSugar = new(dbOptions.ConnectionConfigs.Adapt<List<ConnectionConfig>>(), client => SqlSugarScope sqlSugar = new(dbOptions.ConnectionConfigs.Adapt<List<ConnectionConfig>>(), client =>
{ {
dbOptions.ConnectionConfigs.ForEach(config => dbOptions.ConnectionConfigs.ForEach(config =>
{ {
var db = client.GetConnectionScope((string)config.ConfigId); var db = client.GetConnectionScope((string)config.ConfigId);
// 设置超时时间 // 设置超时时间
db.Ado.CommandTimeOut = 30; db.Ado.CommandTimeOut = 30;
// 打印SQL语句 // 打印SQL语句
db.Aop.OnLogExecuting = (sql, pars) => db.Aop.OnLogExecuting = (sql, pars) =>
{ {
if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.Green; Console.ForegroundColor = ConsoleColor.Green;
if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase)) if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase)) if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.Blue; Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, sql, pars) + "\r\n"); Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, sql, pars) + "\r\n");
App.PrintToMiniProfiler("SqlSugar", "Info", sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); App.PrintToMiniProfiler("SqlSugar", "Info", sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
}; };
db.Aop.OnError = (ex) => db.Aop.OnError = (ex) =>
{ {
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
Console.WriteLine("【" + DateTime.Now + "——错误SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n"); Console.WriteLine("【" + DateTime.Now + "——错误SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n");
App.PrintToMiniProfiler("SqlSugar", "Error", $"{ex.Message}{Environment.NewLine}{ex.Sql}{pars}{Environment.NewLine}"); App.PrintToMiniProfiler("SqlSugar", "Error", $"{ex.Message}{Environment.NewLine}{ex.Sql}{pars}{Environment.NewLine}");
}; };
// 数据审计 // 数据审计
db.Aop.DataExecuting = (oldValue, entityInfo) => db.Aop.DataExecuting = (oldValue, entityInfo) =>
{ {
// 新增操作 // 新增操作
if (entityInfo.OperationType == DataFilterType.InsertByObject) if (entityInfo.OperationType == DataFilterType.InsertByObject)
{ {
// 主键(long类型)且没有值的---赋值雪花Id // 主键(long类型)且没有值的---赋值雪花Id
if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long)) if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
{ {
var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue); var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);
if (id == null || (long)id == 0) if (id == null || (long)id == 0)
entityInfo.SetValue(Yitter.IdGenerator.YitIdHelper.NextId()); entityInfo.SetValue(Yitter.IdGenerator.YitIdHelper.NextId());
} }
if (entityInfo.PropertyName == "CreateTime") if (entityInfo.PropertyName == "CreateTime")
entityInfo.SetValue(DateTime.Now); entityInfo.SetValue(DateTime.Now);
} }
// 更新操作 // 更新操作
if (entityInfo.OperationType == DataFilterType.UpdateByObject) if (entityInfo.OperationType == DataFilterType.UpdateByObject)
{ {
if (entityInfo.PropertyName == "UpdateTime") if (entityInfo.PropertyName == "UpdateTime")
entityInfo.SetValue(DateTime.Now); entityInfo.SetValue(DateTime.Now);
} }
}; };
}); });
}); });
// 初始化数据库表结构及种子数据 // 初始化数据库表结构及种子数据
InitDataBase(sqlSugar, dbOptions); InitDataBase(sqlSugar, dbOptions);
services.AddSingleton<ISqlSugarClient>(sqlSugar); // 单例注册 services.AddSingleton<ISqlSugarClient>(sqlSugar); // 单例注册
services.AddScoped(typeof(SqlSugarRepository<>)); // 注册仓储 services.AddScoped(typeof(SqlSugarRepository<>)); // 注册仓储
services.AddUnitOfWork<SqlSugarUnitOfWork>(); // 注册事务与工作单元 services.AddUnitOfWork<SqlSugarUnitOfWork>(); // 注册事务与工作单元
} }
/// <summary> /// <summary>
/// 初始化数据库结构 /// 初始化数据库结构
/// </summary> /// </summary>
private static void InitDataBase(SqlSugarScope db, DbConnectionOptions dbOptions) private static void InitDataBase(SqlSugarScope db, DbConnectionOptions dbOptions)
{ {
// 创建数据库 // 创建数据库
dbOptions.ConnectionConfigs.ForEach(config => dbOptions.ConnectionConfigs.ForEach(config =>
{ {
if (!config.EnableInitDb || config.DbType == SqlSugar.DbType.Oracle) return; if (!config.EnableInitDb || config.DbType == SqlSugar.DbType.Oracle) return;
db.GetConnectionScope(config.ConfigId).DbMaintenance.CreateDatabase(); db.GetConnectionScope(config.ConfigId).DbMaintenance.CreateDatabase();
}); });
// 获取所有实体表-初始化表结构 // 获取所有实体表-初始化表结构
var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
&& u.IsDefined(typeof(SugarTable), false) && !u.IsDefined(typeof(NotTableAttribute), false)); && u.IsDefined(typeof(SugarTable), false) && !u.IsDefined(typeof(NotTableAttribute), false));
if (!entityTypes.Any()) return; if (!entityTypes.Any()) return;
foreach (var entityType in entityTypes) foreach (var entityType in entityTypes)
{ {
var tAtt = entityType.GetCustomAttribute<TenantAttribute>(); // 多数据库 var tAtt = entityType.GetCustomAttribute<TenantAttribute>(); // 多数据库
var configId = tAtt == null ? SqlSugarConst.DefaultConfigId : tAtt.configId.ToString(); var configId = tAtt == null ? SqlSugarConst.DefaultConfigId : tAtt.configId.ToString();
if (!dbOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId == configId).EnableInitDb) if (!dbOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId == configId).EnableInitDb)
continue; continue;
var db2 = db.GetConnectionScope(configId); var db2 = db.GetConnectionScope(configId);
var splitTable = entityType.GetCustomAttribute<SplitTableAttribute>(); // 分表 var splitTable = entityType.GetCustomAttribute<SplitTableAttribute>(); // 分表
if (splitTable == null) if (splitTable == null)
db2.CodeFirst.InitTables(entityType); db2.CodeFirst.InitTables(entityType);
else else
db2.CodeFirst.SplitTables().InitTables(entityType); db2.CodeFirst.SplitTables().InitTables(entityType);
} }
// 获取所有种子配置-初始化数据 // 获取所有种子配置-初始化数据
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>)))); && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))));
if (!seedDataTypes.Any()) return; if (!seedDataTypes.Any()) return;
foreach (var seedType in seedDataTypes) foreach (var seedType in seedDataTypes)
{ {
var instance = Activator.CreateInstance(seedType); var instance = Activator.CreateInstance(seedType);
var hasDataMethod = seedType.GetMethod("HasData"); var hasDataMethod = seedType.GetMethod("HasData");
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>(); var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
if (seedData == null) continue; if (seedData == null) continue;
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
var tAtt = entityType.GetCustomAttribute<TenantAttribute>(); var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
var configId = tAtt == null ? SqlSugarConst.DefaultConfigId : tAtt.configId.ToString(); var configId = tAtt == null ? SqlSugarConst.DefaultConfigId : tAtt.configId.ToString();
if (!dbOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId == configId).EnableInitDb) if (!dbOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId == configId).EnableInitDb)
continue; continue;
var db2 = db.GetConnectionScope(configId); var db2 = db.GetConnectionScope(configId);
var seedDataTable = seedData.ToList().ToDataTable(); var seedDataTable = seedData.ToList().ToDataTable();
seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName; seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName;
if (seedDataTable.Columns.Contains(SqlSugarConst.DefaultPrimaryKey)) if (seedDataTable.Columns.Contains(SqlSugarConst.DefaultPrimaryKey))
{ {
var storage = db2.Storageable(seedDataTable).WhereColumns(SqlSugarConst.DefaultPrimaryKey).ToStorage(); var storage = db2.Storageable(seedDataTable).WhereColumns(SqlSugarConst.DefaultPrimaryKey).ToStorage();
storage.AsInsertable.ExecuteCommand(); storage.AsInsertable.ExecuteCommand();
storage.AsUpdateable.ExecuteCommand(); storage.AsUpdateable.ExecuteCommand();
} }
else // 没有主键或者不是预定义的主键(没主键有重复的可能) else // 没有主键或者不是预定义的主键(没主键有重复的可能)
{ {
var storage = db2.Storageable(seedDataTable).ToStorage(); var storage = db2.Storageable(seedDataTable).ToStorage();
storage.AsInsertable.ExecuteCommand(); storage.AsInsertable.ExecuteCommand();
} }
} }
} }
} }

104
Cis.Core/SqlSugar/SqlSugarUnitOfWork.cs

@ -5,61 +5,61 @@
/// </summary> /// </summary>
public sealed class SqlSugarUnitOfWork : IUnitOfWork public sealed class SqlSugarUnitOfWork : IUnitOfWork
{ {
/// <summary> /// <summary>
/// SqlSugar 对象 /// SqlSugar 对象
/// </summary> /// </summary>
private readonly ISqlSugarClient _sqlSugarClient; private readonly ISqlSugarClient _sqlSugarClient;
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
/// </summary> /// </summary>
/// <param name="sqlSugarClient"></param> /// <param name="sqlSugarClient"></param>
public SqlSugarUnitOfWork(ISqlSugarClient sqlSugarClient) public SqlSugarUnitOfWork(ISqlSugarClient sqlSugarClient)
{ {
_sqlSugarClient = sqlSugarClient; _sqlSugarClient = sqlSugarClient;
} }
/// <summary> /// <summary>
/// 开启工作单元处理 /// 开启工作单元处理
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context"></param>
/// <param name="unitOfWork"></param> /// <param name="unitOfWork"></param>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
public void BeginTransaction(FilterContext context, UnitOfWorkAttribute unitOfWork) public void BeginTransaction(FilterContext context, UnitOfWorkAttribute unitOfWork)
{ {
_sqlSugarClient.AsTenant().BeginTran(); _sqlSugarClient.AsTenant().BeginTran();
} }
/// <summary> /// <summary>
/// 提交工作单元处理 /// 提交工作单元处理
/// </summary> /// </summary>
/// <param name="resultContext"></param> /// <param name="resultContext"></param>
/// <param name="unitOfWork"></param> /// <param name="unitOfWork"></param>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
public void CommitTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork) public void CommitTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork)
{ {
_sqlSugarClient.AsTenant().CommitTran(); _sqlSugarClient.AsTenant().CommitTran();
} }
/// <summary> /// <summary>
/// 回滚工作单元处理 /// 回滚工作单元处理
/// </summary> /// </summary>
/// <param name="resultContext"></param> /// <param name="resultContext"></param>
/// <param name="unitOfWork"></param> /// <param name="unitOfWork"></param>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
public void RollbackTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork) public void RollbackTransaction(FilterContext resultContext, UnitOfWorkAttribute unitOfWork)
{ {
_sqlSugarClient.AsTenant().RollbackTran(); _sqlSugarClient.AsTenant().RollbackTran();
} }
/// <summary> /// <summary>
/// 执行完毕(无论成功失败) /// 执行完毕(无论成功失败)
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context"></param>
/// <param name="resultContext"></param> /// <param name="resultContext"></param>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
public void OnCompleted(FilterContext context, FilterContext resultContext) public void OnCompleted(FilterContext context, FilterContext resultContext)
{ {
_sqlSugarClient.Dispose(); _sqlSugarClient.Dispose();
} }
} }

24
Cis.Web.Core/ProjectOptions.cs

@ -5,17 +5,17 @@ namespace Cis.Web.Core;
public static class ProjectOptions public static class ProjectOptions
{ {
/// <summary> /// <summary>
/// 注册项目配置选项 /// 注册项目配置选项
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
/// <returns></returns> /// <returns></returns>
public static IServiceCollection AddProjectOptions(this IServiceCollection services) public static IServiceCollection AddProjectOptions(this IServiceCollection services)
{ {
services.AddConfigurableOptions<DbConnectionOptions>(); services.AddConfigurableOptions<DbConnectionOptions>();
services.AddConfigurableOptions<CacheOptions>(); services.AddConfigurableOptions<RedisOptions>();
services.AddConfigurableOptions<SnowIdOptions>(); services.AddConfigurableOptions<SnowIdOptions>();
return services; return services;
} }
} }

202
Cis.Web.Core/Startup.cs

@ -16,115 +16,115 @@ namespace Cis.Web.Core;
[AppStartup(1000)] [AppStartup(1000)]
public class Startup : AppStartup public class Startup : AppStartup
{ {
/// <summary> /// <summary>
/// 配置应用所需服务,在该方法中可以添加应用所需要的功能或服务 /// 配置应用所需服务,在该方法中可以添加应用所需要的功能或服务
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
// 配置选项 // 配置选项
services.AddProjectOptions(); services.AddProjectOptions();
// 缓存注册 // 缓存注册
services.AddCache(); services.AddCache();
// SqlSugar // SqlSugar
services.AddSqlSugar(); services.AddSqlSugar();
// JWT // JWT
services.AddJwt<JwtHandler>(); services.AddJwt<JwtHandler>();
// 允许跨域 // 允许跨域
services.AddCorsAccessor(); services.AddCorsAccessor();
// 添加 https 跨域时请执行以下方法(关键方法 SetIsOriginAllowed),发现 http 和 https 仅能存在一个 跨域 // 添加 https 跨域时请执行以下方法(关键方法 SetIsOriginAllowed),发现 http 和 https 仅能存在一个 跨域
// 同时启用 http 和 https,猜测需要通过设置 WithOrigins // 同时启用 http 和 https,猜测需要通过设置 WithOrigins
// 参考 https://furion.baiqian.ltd/docs/cors?_highlight=corsaccessorsettings#165-corsaccessorsettings-%E9%85%8D%E7%BD%AE // 参考 https://furion.baiqian.ltd/docs/cors?_highlight=corsaccessorsettings#165-corsaccessorsettings-%E9%85%8D%E7%BD%AE
// 以及 http://www.manongjc.com/detail/26-gmogyzvnnewpkml.html // 以及 http://www.manongjc.com/detail/26-gmogyzvnnewpkml.html
//services.AddCors(options => //services.AddCors(options =>
//{ //{
// options.AddPolicy("CorsPolicy", opt => opt // options.AddPolicy("CorsPolicy", opt => opt
// .SetIsOriginAllowed(_ => true) // .SetIsOriginAllowed(_ => true)
// .AllowAnyHeader() // .AllowAnyHeader()
// .AllowAnyMethod() // .AllowAnyMethod()
// .AllowCredentials() // .AllowCredentials()
// ); // );
//}); //});
// 远程请求 // 远程请求
services.AddRemoteRequest(); services.AddRemoteRequest();
// 任务调度 // 任务调度
services.AddTaskScheduler(); services.AddTaskScheduler();
// 日志监听 // 日志监听
services.AddMonitorLogging(); services.AddMonitorLogging();
services.AddControllersWithViews() services.AddControllersWithViews()
.AddNewtonsoftJson(options => .AddNewtonsoftJson(options =>
{ {
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // 首字母小写(驼峰样式) options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // 首字母小写(驼峰样式)
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;// 设置本地时区 options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;// 设置本地时区
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; // 时间格式化 options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; // 时间格式化
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // 忽略循环引用 options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // 忽略循环引用
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; // 忽略空值 options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; // 忽略空值
}) })
.AddInjectWithUnifyResult<RespParamProvider>(); .AddInjectWithUnifyResult<RespParamProvider>();
// 日志记录 // 日志记录
if (App.GetConfig<bool>("Logging:File:Enabled")) // 日志写入文件 if (App.GetConfig<bool>("Logging:File:Enabled")) // 日志写入文件
{ {
Array.ForEach(new[] { LogLevel.Information, LogLevel.Warning, LogLevel.Error }, logLevel => Array.ForEach(new[] { LogLevel.Information, LogLevel.Warning, LogLevel.Error }, logLevel =>
{ {
services.AddFileLogging(options => services.AddFileLogging(options =>
{ {
options.FileNameRule = fileName => string.Format(fileName, DateTime.Now, logLevel.ToString()); // 每天创建一个文件 options.FileNameRule = fileName => string.Format(fileName, DateTime.Now, logLevel.ToString()); // 每天创建一个文件
options.WriteFilter = logMsg => logMsg.LogLevel == logLevel; // 日志级别 options.WriteFilter = logMsg => logMsg.LogLevel == logLevel; // 日志级别
options.HandleWriteError = (writeError) => // 写入失败时启用备用文件 options.HandleWriteError = (writeError) => // 写入失败时启用备用文件
{ {
writeError.UseRollbackFileName(Path.GetFileNameWithoutExtension(writeError.CurrentFileName) + "-oops" + Path.GetExtension(writeError.CurrentFileName)); writeError.UseRollbackFileName(Path.GetFileNameWithoutExtension(writeError.CurrentFileName) + "-oops" + Path.GetExtension(writeError.CurrentFileName));
}; };
}); });
}); });
} }
// 配置雪花Id算法机器码 // 配置雪花Id算法机器码
YitIdHelper.SetIdGenerator(new IdGeneratorOptions YitIdHelper.SetIdGenerator(new IdGeneratorOptions
{ {
WorkerId = App.GetOptions<SnowIdOptions>().WorkerId WorkerId = App.GetOptions<SnowIdOptions>().WorkerId
}); });
} }
/// <summary> /// <summary>
/// 配置应用请求处理管道 /// 配置应用请求处理管道
/// </summary> /// </summary>
/// <param name="app"></param> /// <param name="app"></param>
/// <param name="env"></param> /// <param name="env"></param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ {
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
app.UseForwardedHeaders(); app.UseForwardedHeaders();
} }
else else
{ {
app.UseExceptionHandler("/Home/Error"); app.UseExceptionHandler("/Home/Error");
app.UseForwardedHeaders(); app.UseForwardedHeaders();
app.UseHsts(); app.UseHsts();
} }
// 启用HTTPS // 启用HTTPS
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseRouting(); app.UseRouting();
app.UseCorsAccessor(); app.UseCorsAccessor();
//app.UseCors("CorsPolicy"); //app.UseCors("CorsPolicy");
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseInject(); app.UseInject();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllerRoute( endpoints.MapControllerRoute(
name: "default", name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"); pattern: "{controller=Home}/{action=Index}/{id?}");
}); });
} }
} }

12
Cis.Web.Entry/.config/dotnet-tools.json

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.0",
"commands": [
"dotnet-ef"
]
}
}
}

16
Cis.Web.Entry/Controllers/HomeController.cs

@ -6,14 +6,14 @@ namespace Cis.Web.Entry.Controllers;
[AllowAnonymous] [AllowAnonymous]
public class HomeController : Controller public class HomeController : Controller
{ {
public HomeController() public HomeController()
{ {
} }
public IActionResult Index() public IActionResult Index()
{ {
ViewBag.Description = "Hello, dotnet."; ViewBag.Description = "Hello, dotnet.";
return View(); return View();
} }
} }

22
EC.Helper/CameraSDK/Common/CameraStruct.cs

@ -47,9 +47,9 @@ public class CameraInfo
CameraInfo info = new() { Type = type, Ip = ip, UserName = userName, Password = password }; CameraInfo info = new() { Type = type, Ip = ip, UserName = userName, Password = password };
int port = (CameraType)type switch int port = (CameraType)type switch
{ {
CameraType.HiK => CameraPort.HiK, CameraType.HiK => (int)CameraPort.HiK,
CameraType.DaHua => CameraPort.DaHua, CameraType.DaHua => (int)CameraPort.DaHua,
CameraType.YuShi => CameraPort.YuShi, CameraType.YuShi => (int)CameraPort.YuShi,
_ => -1, _ => -1,
}; };
info.Port = port; info.Port = port;
@ -62,21 +62,21 @@ public class CameraInfo
/// <summary> /// <summary>
/// 相机类型 /// 相机类型
/// </summary> /// </summary>
public enum CameraType public enum CameraType : int
{ {
HiK = 0, HiK = 1,
DaHua = 1, DaHua,
YuShi = 2, YuShi,
} }
/// <summary> /// <summary>
/// 相机默认连接端口 /// 相机默认连接端口
/// </summary> /// </summary>
public class CameraPort public enum CameraPort : int
{ {
public const int HiK = 8000; HiK = 8000,
public const int DaHua = 37777; DaHua = 37777,
public const int YuShi = 8800; YuShi = 8800,
} }
/// <summary> /// <summary>

2
EC.Helper/CameraSDK/Common/ICameraSDK.cs

@ -46,7 +46,7 @@ public abstract class ICameraSDK
/// 获取 ptz /// 获取 ptz
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public abstract PtzInfo GetPtzInfo(); public abstract bool GetPtzInfo(out PtzInfo ptzInfo);
#endregion Main Method #endregion Main Method
} }

11
EC.Helper/CameraSDK/DaHua/DaHuaSDK.cs

@ -82,10 +82,10 @@ public class DaHuaSDK : ICameraSDK
} }
} }
public override PtzInfo GetPtzInfo() public override bool GetPtzInfo(out PtzInfo ptzInfo)
{ {
bool ret = ConnectSuccess(); bool ret = ConnectSuccess();
if (!ret) return PtzInfo.Default; if (!ret) { ptzInfo = PtzInfo.Default; return false; }
DaHuaOriSDK.DH_PTZ_LOCATION_INFO entity = new(); DaHuaOriSDK.DH_PTZ_LOCATION_INFO entity = new();
int nBufLen = GPIParams.Size; int nBufLen = GPIParams.Size;
@ -95,11 +95,12 @@ public class DaHuaSDK : ICameraSDK
try try
{ {
ret = DaHuaOriSDK.CLIENT_QueryDevState(LoginId, (int)DaHuaOriSDK.EM_DEVICE_STATE.PTZ_LOCATION, ptrBuf, nBufLen, ref pRetLen, 3000); ret = DaHuaOriSDK.CLIENT_QueryDevState(LoginId, (int)DaHuaOriSDK.EM_DEVICE_STATE.PTZ_LOCATION, ptrBuf, nBufLen, ref pRetLen, 3000);
if (!ret) { BuildException(); return PtzInfo.Default; } if (!ret) { BuildException(); ptzInfo = PtzInfo.Default; return false; }
object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type); object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type);
if (objBuf == null) return PtzInfo.Default; if (objBuf == null) { ptzInfo = PtzInfo.Default; return false; }
entity = (DaHuaOriSDK.DH_PTZ_LOCATION_INFO)objBuf; entity = (DaHuaOriSDK.DH_PTZ_LOCATION_INFO)objBuf;
return PtzInfo.New(entity.nPTZPan, entity.nPTZTilt, entity.nPTZZoom); ptzInfo = PtzInfo.New(entity.nPTZPan, entity.nPTZTilt, entity.nPTZZoom);
return true;
} }
finally finally
{ {

11
EC.Helper/CameraSDK/HiK/HiKSDK.cs

@ -68,10 +68,10 @@ public class HiKSDK : ICameraSDK
} }
} }
public override PtzInfo GetPtzInfo() public override bool GetPtzInfo(out PtzInfo ptzInfo)
{ {
bool ret = ConnectSuccess(); bool ret = ConnectSuccess();
if (!ret) return PtzInfo.Default; if (!ret) { ptzInfo = PtzInfo.Default; return false; }
HiKOriSDK.NET_DVR_PTZPOS entity = new(); HiKOriSDK.NET_DVR_PTZPOS entity = new();
int dwSize = GPIParams.Size; int dwSize = GPIParams.Size;
@ -81,11 +81,12 @@ public class HiKSDK : ICameraSDK
try 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, 0, ptrBuf, (uint)dwSize, ref dwReturned);
if (!ret) { BuildException(); return PtzInfo.Default; } if (!ret) { BuildException(); ptzInfo = PtzInfo.Default; return false; }
object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type); object? objBuf = Marshal.PtrToStructure(ptrBuf, GPIParams.Type);
if (objBuf == null) return PtzInfo.Default; if (objBuf == null) { ptzInfo = PtzInfo.Default; return false; }
entity = (HiKOriSDK.NET_DVR_PTZPOS)objBuf; entity = (HiKOriSDK.NET_DVR_PTZPOS)objBuf;
return PtzInfo.New(entity.wPanPos, entity.wTiltPos, entity.wZoomPos); ptzInfo = PtzInfo.New(entity.wPanPos, entity.wTiltPos, entity.wZoomPos);
return true;
} }
finally finally
{ {

9
EC.Helper/CameraSDK/YuShi/YuShiSDK.cs

@ -58,16 +58,17 @@ public class YuShiSDK : ICameraSDK
#region Main Method #region Main Method
public override PtzInfo GetPtzInfo() public override bool GetPtzInfo(out PtzInfo ptzInfo)
{ {
bool ret = ConnectSuccess(); bool ret = ConnectSuccess();
if (!ret) return PtzInfo.Default; if (!ret) { ptzInfo = PtzInfo.Default; return false; }
YuShiOriSDK.NETDEV_PTZ_STATUS_S entity = new(); YuShiOriSDK.NETDEV_PTZ_STATUS_S entity = new();
ret = YuShiOriSDK.NETDEV_PTZGetStatus(LoginId, 1, ref entity); ret = YuShiOriSDK.NETDEV_PTZGetStatus(LoginId, 1, ref entity);
if (!ret) { BuildException(); return PtzInfo.Default; } if (!ret) { BuildException(); ptzInfo = PtzInfo.Default; return false; }
return PtzInfo.New(entity.fPanTiltX, entity.fPanTiltY, entity.fZoomX); ptzInfo = PtzInfo.New(entity.fPanTiltX, entity.fPanTiltY, entity.fZoomX);
return true;
} }
#endregion Main Method #endregion Main Method

124
EC.Helper/Test/CameraSDKTest.cs

@ -1,124 +0,0 @@
using EC.Helper.CameraSDK;
namespace EC.Helper.Test;
public class CameraSDKTest
{
public static void Test()
{
//HiKTest();
//DaHuaTest();
//YuShiTest();
}
public static void HiKTest()
{
Console.WriteLine("====HiK==========================");
try
{
string ip = "192.168.1.65";
string username = "admin";
string password = "hk123456";
int type = (int)CameraType.HiK;
CameraInfo cameraInfo = CameraInfo.New(type, ip, username, password);
ICameraSDK sdk = new HiKSDK(cameraInfo);
ICameraSDK sdk2 = new HiKSDK(cameraInfo);
sdk.Init();
sdk2.Init();
PtzInfo ptzInfo = sdk.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
sdk.Destory();
Thread.Sleep(1000);
ptzInfo = sdk.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
ptzInfo = sdk2.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
throw;
}
Console.WriteLine("=================================");
}
public static void DaHuaTest()
{
Console.WriteLine("====DaHua========================");
try
{
string ip = "192.168.1.71";
string username = "admin";
string password = "hk123456";
int type = (int)CameraType.DaHua;
CameraInfo cameraInfo = CameraInfo.New(type, ip, username, password);
ICameraSDK sdk = new DaHuaSDK(cameraInfo);
ICameraSDK sdk2 = new DaHuaSDK(cameraInfo);
sdk.Init();
sdk2.Init();
PtzInfo ptzInfo = sdk.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
sdk.Destory();
Thread.Sleep(1000);
ptzInfo = sdk.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
ptzInfo = sdk2.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
throw;
}
Console.WriteLine("=================================");
}
public static void YuShiTest()
{
Console.WriteLine("====YuShi========================");
try
{
string ip = "192.168.1.109";
string username = "admin";
string password = "hk123456";
int type = (int)CameraType.YuShi;
CameraInfo cameraInfo = CameraInfo.New(type, ip, username, password);
ICameraSDK sdk = new YuShiSDK(cameraInfo);
ICameraSDK sdk2 = new YuShiSDK(cameraInfo);
sdk.Init();
sdk2.Init();
PtzInfo ptzInfo = sdk.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
sdk.Destory();
Thread.Sleep(1000);
ptzInfo = sdk.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
ptzInfo = sdk2.GetPtzInfo();
Console.WriteLine($"{ptzInfo.Pan}, {ptzInfo.Tilt}, {ptzInfo.Zoom}");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
throw;
}
Console.WriteLine("=================================");
}
}

1
Frontend/src/views/page/cameraCenter.vue

@ -76,6 +76,7 @@ export default defineComponent({
loadTreeData() { loadTreeData() {
cameraApi.GetList().then((res) => { cameraApi.GetList().then((res) => {
let list: Array<any> = res.data.data; let list: Array<any> = res.data.data;
console.log(res);
for (let item of list) { for (let item of list) {
this.cameraMap.set(item.id, item); this.cameraMap.set(item.id, item);
this.treeData!.push({title: item.ip, key: item.id}); this.treeData!.push({title: item.ip, key: item.id});

Loading…
Cancel
Save