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. 15
      Cis.Application/AppConfig.json
  3. 10
      Cis.Application/Cb/Common/CbInfo.cs
  4. 16
      Cis.Application/Cb/Entity/CbCamera.cs
  5. 44
      Cis.Application/Cb/Entity/CbCameraParams.cs
  6. 61
      Cis.Application/Cb/Service/CbCameraParamsService.cs
  7. 19
      Cis.Application/Cb/Service/CbCameraService.cs
  8. 1
      Cis.Application/Cis.Application.csproj
  9. 472
      Cis.Application/Cis.Application.xml
  10. 2
      Cis.Application/Cm/Common/CmInfo.cs
  11. 2
      Cis.Application/Cm/Entity/CmMarkLabel.cs
  12. 18
      Cis.Application/Cm/Service/CmMarkGroupService.cs
  13. 42
      Cis.Application/Cm/Service/CmMarkLabelService.cs
  14. 80
      Cis.Application/Core/Algo/HikMarkSeacher.cs
  15. 260
      Cis.Application/Core/Algo/MarkSearcherBase.cs
  16. 9
      Cis.Application/Core/Api/IPtzApi.cs
  17. 205
      Cis.Application/Core/Api/PtzServerApi.cs
  18. 335
      Cis.Application/Core/Center/CameraDataCenter.cs
  19. 23
      Cis.Application/Core/Common/CacheInfo.cs
  20. 18
      Cis.Application/Core/Common/CoreInfo.cs
  21. 30
      Cis.Application/Core/Common/Extension.cs
  22. 36
      Cis.Application/Core/Common/Options.cs
  23. 73
      Cis.Application/Core/Component/CameraSDK/CameraSdkServer.cs
  24. 54
      Cis.Application/Core/Component/CameraSDK/ICameraSdkServer.cs
  25. 52
      Cis.Application/Core/Component/MarkSeacher/Entity/CameraCalcParams.cs
  26. 52
      Cis.Application/Core/Component/MarkSeacher/Entity/MarkLabelCalcParams.cs
  27. 48
      Cis.Application/Core/Component/MarkSeacher/Entity/MarkLabelCalcResult.cs
  28. 119
      Cis.Application/Core/Component/MarkSeacher/IMarkSearcherServer.cs
  29. 268
      Cis.Application/Core/Component/MarkSeacher/MarkSearcherServer.cs
  30. 31
      Cis.Application/Core/Component/MarkSeacher/Seacher/DaHuaMarkSearcher.cs
  31. 73
      Cis.Application/Core/Component/MarkSeacher/Seacher/HiKMarkSeacher.cs
  32. 254
      Cis.Application/Core/Component/MarkSeacher/Seacher/MarkSearcherBase.cs
  33. 29
      Cis.Application/Core/Component/MarkSeacher/Seacher/YuShiMarkSeacher.cs
  34. 181
      Cis.Application/Core/Entity/CameraCalcInfo.cs
  35. 28
      Cis.Application/Core/Service/MarkSearchService.cs
  36. 1
      Cis.Application/GlobalUsings.cs
  37. 8
      Cis.Application/Startup.cs
  38. 18
      Cis.Application/Sys/Service/SysDictDataService.cs
  39. 22
      Cis.Application/Sys/Service/SysDictTypeService.cs
  40. 22
      Cis.Application/Tb/Common/TbInfo.cs
  41. 28
      Cis.Application/Tb/Entity/TbPtzCamera.cs
  42. 57
      Cis.Application/Tb/Service/TbPtzCameraService.cs
  43. 21
      Cis.Core/Cache/CacheSetup.cs
  44. 20
      Cis.Core/Cis.Core.csproj
  45. 37
      Cis.Core/Cis.Core.xml
  46. 0
      Cis.Core/Common/Attribute/NotTableAttribute.cs
  47. 14
      Cis.Core/Common/Const/SqlSugarConst.cs
  48. 61
      Cis.Core/Common/Entity/EntityBase.cs
  49. 47
      Cis.Core/Common/Entity/Pagination.cs
  50. 128
      Cis.Core/Common/Entity/RespParamProvider.cs
  51. 0
      Cis.Core/Common/Enum/CacheTypeEnum.cs
  52. 19
      Cis.Core/Common/Enum/StatusEnum.cs
  53. 141
      Cis.Core/Common/Extension/ObjectExtension.cs
  54. 20
      Cis.Core/Common/Option/DbConnectionOptions.cs
  55. 12
      Cis.Core/Common/Option/RedisOptions.cs
  56. 0
      Cis.Core/Common/Option/SnowIdOptions.cs
  57. 14
      Cis.Core/Const/SqlSugarConst.cs
  58. 14
      Cis.Core/CoreConfig.json
  59. 61
      Cis.Core/Entity/EntityBase.cs
  60. 47
      Cis.Core/Entity/Pagination.cs
  61. 128
      Cis.Core/Entity/RespParamProvider.cs
  62. 19
      Cis.Core/Enum/StatusEnum.cs
  63. 151
      Cis.Core/Extension/ObjectExtension.cs
  64. 17
      Cis.Core/Option/CacheOptions.cs
  65. 20
      Cis.Core/Option/DbConnectionOptions.cs
  66. 2
      Cis.Web.Core/ProjectOptions.cs
  67. 12
      Cis.Web.Entry/.config/dotnet-tools.json
  68. 22
      EC.Helper/CameraSDK/Common/CameraStruct.cs
  69. 2
      EC.Helper/CameraSDK/Common/ICameraSDK.cs
  70. 11
      EC.Helper/CameraSDK/DaHua/DaHuaSDK.cs
  71. 11
      EC.Helper/CameraSDK/HiK/HiKSDK.cs
  72. 9
      EC.Helper/CameraSDK/YuShi/YuShiSDK.cs
  73. 124
      EC.Helper/Test/CameraSDKTest.cs
  74. 1
      Frontend/src/views/page/cameraCenter.vue

2
.gitignore

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

15
Cis.Application/AppConfig.json

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

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

@ -40,5 +40,15 @@ public class CbInfo
/// </summary>
public const string CbCameraTbDesc = "相机表";
/// <summary>
/// CbCameraParams 表名
/// </summary>
public const string CbCameraParamsTbName = "cb_camera_params";
/// <summary>
/// CbCameraParams 表描述
/// </summary>
public const string CbCameraParamsTbDesc = "相机参数表";
#endregion Table Info
}

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

@ -1,7 +1,7 @@
namespace Cis.Application.Cb;
/// <summary>
/// 系统字典类型
/// 相机
/// </summary>
[SugarTable(CbInfo.CbCameraTbName, CbInfo.CbCameraTbDesc)]
[Tenant(CbInfo.DbName)]
@ -21,13 +21,6 @@ public class CbCamera : EntityBase
[Required, MaxLength(16)]
public string Ip { get; set; }
/// <summary>
/// 端口
/// </summary>
[SugarColumn(ColumnDescription = "端口", Length = 5, DefaultValue = "80")]
[Required, MaxLength(5)]
public string Port { get; set; }
/// <summary>
/// 账号
/// </summary>
@ -41,4 +34,11 @@ public class CbCamera : EntityBase
[SugarColumn(ColumnDescription = "密码", Length = 32)]
[Required, MaxLength(32)]
public string Password { get; set; }
/// <summary>
/// 相机参数 Id
/// </summary>
[SugarColumn(ColumnDescription = "相机参数Id")]
[Required]
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;
}
}

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

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

1
Cis.Application/Cis.Application.csproj

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

472
Cis.Application/Cis.Application.xml

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

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

@ -48,7 +48,7 @@ public class CmInfo
/// <summary>
/// CmMarkLabel 表描述
/// </summary>
public const string CmMarkLabelTbDesc = "标记标签表";
public const string CmMarkLabelTbDesc = "标签表";
#endregion Table Info
}

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

@ -1,7 +1,7 @@
namespace Cis.Application.Cm;
/// <summary>
/// 标记标签表
/// 标签表
/// </summary>
[SugarTable(CmInfo.CmMarkLabelTbName, CmInfo.CmMarkLabelTbDesc)]
[Tenant(CmInfo.DbName)]

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

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

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

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

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

@ -1,309 +1,166 @@
using Cis.Application.Cb;
using Cis.Application.Cm;
using Cis.Application.Tb;
using Microsoft.Extensions.Logging;
using Cis.Application.Core.Component.MarkSeacher;
using Cis.Application.Core.Component.PtzServer;
using EC.Helper.CameraSDK;
using StackExchange.Profiling.Internal;
using StackExchange.Redis;
using System.Collections.Concurrent;
namespace Cis.Application.Core;
public class CameraDataCenter
public class CameraDataCenter : ISingleton
{
#region Attr
private readonly SqlSugarRepository<CbCamera> _cbCameraRep;
private readonly SqlSugarRepository<CmMarkLabel> _cmMarkLableRep;
private readonly SqlSugarRepository<TbPtzCamera> _tbPtzCameraRep;
private readonly PtzServerApi _ptzServerApi;
private readonly CameraDataOptions options;
private readonly IDatabase _cache;
private Thread _thread { get; set; }
private readonly CameraDataOptions _options;
private readonly ICameraSdkServer _cameraSdkServer;
private readonly IMarkSearcherServer _markSearcherServer;
/// <summary>
/// (cbCameraId, cbCameraIp)
/// {cameraId, List(MarkLabelCalcResult)}
/// </summary>
private Dictionary<long, string> _cbCameraId2IpDict { get; set; } = new();
/// <summary>
/// (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();
private ConcurrentDictionary<long, List<MarkLabelCalcResult>> SearchResultListDict { get; set; } = new();
#endregion Attr
private readonly ILogger<CameraDataCenter> _logger;
public CameraDataCenter()
public CameraDataCenter(
IDatabase cache,
ICameraSdkServer cameraSdkServer,
IMarkSearcherServer markSearcherServer
)
{
_cbCameraRep = App.GetService<SqlSugarRepository<CbCamera>>();
_cmMarkLableRep = App.GetService<SqlSugarRepository<CmMarkLabel>>();
_tbPtzCameraRep = App.GetService<SqlSugarRepository<TbPtzCamera>>();
_ptzServerApi = App.GetService<PtzServerApi>();
options = App.GetOptions<CameraDataOptions>();
_logger = App.GetService<ILogger<CameraDataCenter>>();
Init();
_cache = cache;
_options = App.GetOptions<CameraDataOptions>();
_cameraSdkServer = cameraSdkServer;
_markSearcherServer = markSearcherServer;
InitThread();
}
private void Init()
{
if (!options.LazyInit)
{
List<TbPtzCamera> list = _tbPtzCameraRep.GetList();
// 根据 Ip 去重
foreach (TbPtzCamera item in list)
{
if (!_tbPtzCameraDict.ContainsKey(item.Ip))
_tbPtzCameraDict[item.Ip] = item;
}
}
#region Loop
// 初始化 thread
_thread = new Thread(WorkLoop)
{
IsBackground = true// 设置后台线程
};
_thread.Start();
}
private Thread RefreshCameraPtzThread { get; set; }
#region Loop
private Thread RefreshMarkSearcherThread { get; set; }
/// <summary>
/// 循环运行
/// 初始化线程
/// </summary>
private void WorkLoop()
private void InitThread()
{
// 初始化 CameraPtzThread
RefreshCameraPtzThread = new Thread(() =>
{
while (true)
{
RefreshPtzInfoByApi();
RefreshMarkSearcher();
Thread.Sleep(options.LoopInterval);
}
RefreshCameraPtzInfos();
Thread.Sleep(_options.CameraPtz.LoopInterval);
}
})
{
IsBackground = true// 设置后台线程
};
// 启动 CameraPtzThread
RefreshCameraPtzThread.Start();
private void RefreshPtzInfoByApi()
// 初始化 MarkSearcherThread
RefreshMarkSearcherThread = new Thread(() =>
{
foreach (TbPtzCamera item in _tbPtzCameraDict.Values)
while (true)
{
PtzInfo ptzInfo = _ptzServerApi.GetPtzInfo(item.CameraId);
_cameraPtzInfoDict[item.Ip] = ptzInfo;
RefreshMarkSearchers();
Thread.Sleep(_options.MarkSearcher.LoopInterval);
}
})
{
IsBackground = true// 设置后台线程
};
// 启动 MarkSearcherThread
RefreshMarkSearcherThread.Start();
}
private async void RefreshMarkSearcher()
/// <summary>
/// 刷新所有相机 ptz
/// 有待改善,应设置超时计数机制
/// https://blog.csdn.net/qq_28368039/article/details/118597396
/// </summary>
private async void RefreshCameraPtzInfos()
{
foreach (KeyValuePair<long, MarkSearcherBase> pair in _markSearcherDict)
HashEntry[] entries = _cache.HashGetAll(CacheInfo.CameraIpCounts);
List<Task> tasks = new();
foreach (HashEntry entry in entries)
{
long cameraId = pair.Key;
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;
string cameraIp = entry.Name;
tasks.Add(RefreshCameraPtzInfo(cameraIp));
}
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(_options.CameraPtz.Timeout));
}
#endregion Loop
#region external call
/// <summary>
/// 激活 cbCamera 进入运算
/// 刷新相机 ptz
/// </summary>
/// <param name="cameraId"></param>
/// <param name="cameraIp"></param>
/// <returns></returns>
public bool ActivateSearcher(long cameraId)
private async Task RefreshCameraPtzInfo(string cameraIp)
{
try
{
msDictLock.EnterWriteLock();
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;
bool ret = _cameraSdkServer.GetPtzInfoByIp(cameraIp, out PtzInfo ptzInfo);
if (!ret) return;
await _cache.HashSetAsync(CacheInfo.CameraPtzInfos, cameraIp, ptzInfo.ToJson());
}
// 存储 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)
/// <summary>
/// 刷新所有 markSearcher
/// </summary>
private async void RefreshMarkSearchers()
{
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
HashEntry[] entries = _cache.HashGetAll(CacheInfo.CameraId2Ip);
List<Task> tasks = new();
foreach (HashEntry entry in entries)
{
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>
/// 解除 cbCamera 进入运算
/// 刷新 markSearcher
/// </summary>
/// <param name="cameraId"></param>
/// <returns></returns>
public bool DeActivateSearcher(long cameraId)
{
try
private async Task RefreshMarkSearcher(long cameraId)
{
msDictLock.EnterWriteLock();
_markSearcherDict.Remove(cameraId);
_markLabelCalcResultListDict.Remove(cameraId);
}
finally
{
msDictLock.ExitWriteLock();
}
return true;
MarkSearcherBase markSearcher = _markSearcherServer.GetSearcher(cameraId);
if (markSearcher == null) return;
string cameraIp = _cache.HashGet(CacheInfo.CameraId2Ip, cameraId);
RedisValue value = _cache.HashGet(CacheInfo.CameraPtzInfos, cameraIp);
if (value.IsNull) return;
PtzInfo ptzInfo = value.ToString().FromJson<PtzInfo>();
markSearcher.UpdateCameraCalcParams(ptzInfo);
List<MarkLabelCalcResult> resultList = await markSearcher.SearchAsync();
SearchResultListDict[cameraId] = resultList;
}
public bool IsExistSearcher(long cameraId)
{
return _markSearcherDict.ContainsKey(cameraId);
}
public bool ActivateMarkLabel(long cameraId, long markLabelId)
{
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;
}
#endregion Loop
public bool IsExistCameraMarkLabel(long cameraId, long markLabelId)
{
bool ret = _markSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher) &&
markSearcher.ExistsMarkLabelCalcInfo(markLabelId);
return ret;
}
#region Base Method
public List<MarkLabelCalcResult> GetMarkLabelCalcResultList(long cameraId)
{
bool ret = _markLabelCalcResultListDict.TryGetValue(cameraId, out List<MarkLabelCalcResult> list);
return ret ? list : new();
}
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);
});
bool ret = SearchResultListDict.TryGetValue(cameraId, out List<MarkLabelCalcResult> resultList);
return ret ? resultList : new List<MarkLabelCalcResult>();
}
public async Task<bool> IsExistSearcherAsync(long cameraId)
{
return await Task.Run(() =>
{
return IsExistSearcher(cameraId);
});
}
#endregion Base Method
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 DectivateMarkLabel(cameraId, markLabelId);
});
}
public async Task<bool> IsExistMarkLabelAysnc(long cameraId, long markLabelId)
{
return await Task.Run(() =>
{
return IsExistCameraMarkLabel(cameraId, markLabelId);
});
}
#region Base Method Async
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
};
}
}

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

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

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
};
}
}

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

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

1
Cis.Application/GlobalUsings.cs

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

8
Cis.Application/Startup.cs

@ -1,4 +1,7 @@
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.Hosting;
using Microsoft.Extensions.DependencyInjection;
@ -15,9 +18,10 @@ public class Startup : AppStartup
public void ConfigureServices(IServiceCollection services)
{
services.AddConfigurableOptions<CameraDataOptions>();
services.AddConfigurableOptions<PtzServerOptions>();
services.AddSingleton(new CameraDataCenter());
services.AddSingleton<ICameraSdkServer, CameraSdkServer>();
services.AddSingleton<IMarkSearcherServer, MarkSearcherServer>();
services.AddSingleton(typeof(CameraDataCenter));
}
/// <summary>

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

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

22
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;
@ -9,35 +8,35 @@ namespace Cis.Application.Sys;
[ApiDescriptionSettings(SysInfo.GroupName, Order = SysInfo.GroupOrder)]
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]
public async Task<bool> Add([FromForm] SysDictType entity)
{
return await _sysDictTypeRep.InsertAsync(entity);
return await _baseRep.InsertAsync(entity);
}
[HttpPost]
public async Task<bool> Update([FromForm] SysDictType entity)
{
return await _sysDictTypeRep.UpdateAsync(entity);
return await _baseRep.UpdateAsync(entity);
}
[HttpPost]
public async Task<bool> Delete([FromForm] SysDictType entity)
{
return await _sysDictTypeRep.DeleteAsync(entity);
return await _baseRep.DeleteAsync(entity);
}
[HttpGet]
public async Task<SysDictType> Get(long id)
{
SysDictType entity = await _sysDictTypeRep.GetByIdAsync(id);
SysDictType entity = await _baseRep.GetByIdAsync(id);
return entity;
}
@ -45,7 +44,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
public async Task<List<SysDictType>> GetList(string queryJson)
{
JObject queryObj = queryJson.ToJObject();
List<SysDictType> list = await _sysDictTypeRep.AsQueryable()
List<SysDictType> list = await _baseRep.AsQueryable()
.ToListAsync();
return list;
}
@ -55,9 +54,8 @@ public class SysDictTypeService : IDynamicApiController, ITransient
{
Pagination pageObj = pagination.ToObject<Pagination>();
JObject queryObj = queryJson.ToJObject();
List<SysDictType> list = await _sysDictTypeRep.AsQueryable()
List<SysDictType> list = await _baseRep.AsQueryable()
.ToPageListAsync(pageObj.Index, pageObj.Size);
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;
}
}

21
Cis.Core/Cache/CacheSetup.cs

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

20
Cis.Core/Cis.Core.csproj

@ -6,6 +6,12 @@
<DocumentationFile>Cis.Core.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Component\**" />
<EmbeddedResource Remove="Component\**" />
<None Remove="Component\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Cis.Core.xml" />
<None Remove="CoreConfig.json" />
@ -18,20 +24,16 @@
</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.Pure" Version="4.4.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" />
<PackageReference Include="NewLife.Core" Version="10.0.2022.1001" />
<PackageReference Include="NewLife.Redis" Version="5.0.2022.901" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.11" />
<PackageReference Include="NewLife.Core" Version="10.0.2022.1101" />
<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" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>
<ProjectExtensions><VisualStudio><UserProperties coreconfig_1json__JsonSchema="https://download.stackhawk.com/hawk/jsonschema/hawkconfig.json" /></VisualStudio></ProjectExtensions>
</Project>

37
Cis.Core/Cis.Core.xml

@ -4,14 +4,9 @@
<name>Cis.Core</name>
</assembly>
<members>
<member name="T:Cis.Core.NotTableAttribute">
<summary>
非实体表特性
</summary>
</member>
<member name="M:Cis.Core.CacheSetup.AddCache(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
缓存注册(新生命Redis组件)
Redis 缓存注册
</summary>
<param name="services"></param>
</member>
@ -20,6 +15,11 @@
SqlSugar二级缓存(必须是内存缓存)
</summary>
</member>
<member name="T:Cis.Core.NotTableAttribute">
<summary>
非实体表特性
</summary>
</member>
<member name="F:Cis.Core.SqlSugarConst.DefaultConfigId">
<summary>
默认数据库标识
@ -289,21 +289,6 @@
<param name="pi"></param>
<returns></returns>
</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">
<summary>
数据库配置选项
@ -319,6 +304,16 @@
启用库表初始化
</summary>
</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">
<summary>
雪花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; }
}

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

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": {
"ConnectionConfigs": [
{
@ -10,19 +9,12 @@
//"ConnectionString": "DataSource=./cis.db",
//"DbType": "PostgreSQL",
//"ConnectionString": "HOST=127.0.0.1;PORT=5432;USER ID=pgsql;PASSWORD=123456;DATABASE=cis;",
"EnableInitDb": false //
},
{
"ConfigId": "serverdb",
"DbType": "MySql",
"ConnectionString": "Data Source=127.0.0.1;port=3306;User ID=root;Password=123456;Database=serverdb;pooling=true;sslmode=none;CharSet=utf8;",
"EnableInitDb": false //
"EnableInitDb": true //
}
]
},
"Cache": {
"CacheType": "Memory", // MemoryRedis
"RedisConnectionString": "127.0.0.1:6379;password=123456;db=2"
"Redis": {
"ConnectionString": "127.0.0.1:6379,password=123456,defaultDatabase=2"
},
"AppSettings": {
"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; }
}

2
Cis.Web.Core/ProjectOptions.cs

@ -13,7 +13,7 @@ public static class ProjectOptions
public static IServiceCollection AddProjectOptions(this IServiceCollection services)
{
services.AddConfigurableOptions<DbConnectionOptions>();
services.AddConfigurableOptions<CacheOptions>();
services.AddConfigurableOptions<RedisOptions>();
services.AddConfigurableOptions<SnowIdOptions>();
return services;

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"
]
}
}
}

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 };
int port = (CameraType)type switch
{
CameraType.HiK => CameraPort.HiK,
CameraType.DaHua => CameraPort.DaHua,
CameraType.YuShi => CameraPort.YuShi,
CameraType.HiK => (int)CameraPort.HiK,
CameraType.DaHua => (int)CameraPort.DaHua,
CameraType.YuShi => (int)CameraPort.YuShi,
_ => -1,
};
info.Port = port;
@ -62,21 +62,21 @@ public class CameraInfo
/// <summary>
/// 相机类型
/// </summary>
public enum CameraType
public enum CameraType : int
{
HiK = 0,
DaHua = 1,
YuShi = 2,
HiK = 1,
DaHua,
YuShi,
}
/// <summary>
/// 相机默认连接端口
/// </summary>
public class CameraPort
public enum CameraPort : int
{
public const int HiK = 8000;
public const int DaHua = 37777;
public const int YuShi = 8800;
HiK = 8000,
DaHua = 37777,
YuShi = 8800,
}
/// <summary>

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

@ -46,7 +46,7 @@ public abstract class ICameraSDK
/// 获取 ptz
/// </summary>
/// <returns></returns>
public abstract PtzInfo GetPtzInfo();
public abstract bool GetPtzInfo(out PtzInfo ptzInfo);
#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();
if (!ret) return PtzInfo.Default;
if (!ret) { ptzInfo = PtzInfo.Default; return false; }
DaHuaOriSDK.DH_PTZ_LOCATION_INFO entity = new();
int nBufLen = GPIParams.Size;
@ -95,11 +95,12 @@ public class DaHuaSDK : ICameraSDK
try
{
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);
if (objBuf == null) return PtzInfo.Default;
if (objBuf == null) { ptzInfo = PtzInfo.Default; return false; }
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
{

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();
if (!ret) return PtzInfo.Default;
if (!ret) { ptzInfo = PtzInfo.Default; return false; }
HiKOriSDK.NET_DVR_PTZPOS entity = new();
int dwSize = GPIParams.Size;
@ -81,11 +81,12 @@ public class HiKSDK : ICameraSDK
try
{
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);
if (objBuf == null) return PtzInfo.Default;
if (objBuf == null) { ptzInfo = PtzInfo.Default; return false; }
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
{

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

@ -58,16 +58,17 @@ public class YuShiSDK : ICameraSDK
#region Main Method
public override PtzInfo GetPtzInfo()
public override bool GetPtzInfo(out PtzInfo ptzInfo)
{
bool ret = ConnectSuccess();
if (!ret) return PtzInfo.Default;
if (!ret) { ptzInfo = PtzInfo.Default; return false; }
YuShiOriSDK.NETDEV_PTZ_STATUS_S entity = new();
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

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() {
cameraApi.GetList().then((res) => {
let list: Array<any> = res.data.data;
console.log(res);
for (let item of list) {
this.cameraMap.set(item.id, item);
this.treeData!.push({title: item.ip, key: item.id});

Loading…
Cancel
Save