fajiao
2 years ago
87 changed files with 3257 additions and 2926 deletions
@ -1,12 +1,13 @@ |
|||||
{ |
{ |
||||
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json", |
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json", |
||||
"CameraDataOptions": { |
"CameraData": { |
||||
"LazyInit": false, |
"CameraPtz": { |
||||
"LoopInterval": "1000" |
"LoopInterval": "1000", |
||||
}, |
"Timeout": "2000" |
||||
"PTZServer": { |
}, |
||||
"Type": "", |
"MarkSearcher": { |
||||
"Ip": "127.0.0.1", |
"LoopInterval": "1000", |
||||
"Port": "7022" |
"Timeout": "2000" |
||||
|
} |
||||
} |
} |
||||
} |
} |
@ -1,44 +1,44 @@ |
|||||
namespace Cis.Application.Cb; |
namespace Cis.Application.Cb; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 系统字典类型表
|
/// 相机表
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarTable(CbInfo.CbCameraTbName, CbInfo.CbCameraTbDesc)] |
[SugarTable(CbInfo.CbCameraTbName, CbInfo.CbCameraTbDesc)] |
||||
[Tenant(CbInfo.DbName)] |
[Tenant(CbInfo.DbName)] |
||||
public class CbCamera : EntityBase |
public class CbCamera : EntityBase |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// 名称
|
/// 名称
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "名称", Length = 64)] |
[SugarColumn(ColumnDescription = "名称", Length = 64)] |
||||
[Required, MaxLength(64)] |
[Required, MaxLength(64)] |
||||
public string Name { get; set; } |
public string Name { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// ip 地址
|
/// ip 地址
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "ip地址", Length = 16)] |
[SugarColumn(ColumnDescription = "ip地址", Length = 16)] |
||||
[Required, MaxLength(16)] |
[Required, MaxLength(16)] |
||||
public string Ip { get; set; } |
public string Ip { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 端口
|
/// 账号
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "端口", Length = 5, DefaultValue = "80")] |
[SugarColumn(ColumnDescription = "账号", Length = 32)] |
||||
[Required, MaxLength(5)] |
[Required, MaxLength(32)] |
||||
public string Port { get; set; } |
public string UserName { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 账号
|
/// 密码
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "账号", Length = 32)] |
[SugarColumn(ColumnDescription = "密码", Length = 32)] |
||||
[Required, MaxLength(32)] |
[Required, MaxLength(32)] |
||||
public string UserName { get; set; } |
public string Password { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 密码
|
/// 相机参数 Id
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "密码", Length = 32)] |
[SugarColumn(ColumnDescription = "相机参数Id")] |
||||
[Required, MaxLength(32)] |
[Required] |
||||
public string Password { get; set; } |
public long CbCameraParamsId { get; set; } |
||||
} |
} |
@ -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; } |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -1,64 +1,64 @@ |
|||||
namespace Cis.Application.Cm; |
namespace Cis.Application.Cm; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 标记标签表
|
/// 标签表
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarTable(CmInfo.CmMarkLabelTbName, CmInfo.CmMarkLabelTbDesc)] |
[SugarTable(CmInfo.CmMarkLabelTbName, CmInfo.CmMarkLabelTbDesc)] |
||||
[Tenant(CmInfo.DbName)] |
[Tenant(CmInfo.DbName)] |
||||
public class CmMarkLabel : EntityBase |
public class CmMarkLabel : EntityBase |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// 相机 Id
|
/// 相机 Id
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "相机Id")] |
[SugarColumn(ColumnDescription = "相机Id")] |
||||
public long CbCameraId { get; set; } |
public long CbCameraId { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 标记组 Id
|
/// 标记组 Id
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "标记组Id")] |
[SugarColumn(ColumnDescription = "标记组Id")] |
||||
public long CmMarkGroupId { get; set; } |
public long CmMarkGroupId { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 名称
|
/// 名称
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "名称", Length = 64)] |
[SugarColumn(ColumnDescription = "名称", Length = 64)] |
||||
public string Name { get; set; } |
public string Name { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Pan 坐标
|
/// Pan 坐标
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "Pan坐标")] |
[SugarColumn(ColumnDescription = "Pan坐标")] |
||||
public double PanPosition { get; set; } |
public double PanPosition { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Tilt 坐标
|
/// Tilt 坐标
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "Tilt坐标")] |
[SugarColumn(ColumnDescription = "Tilt坐标")] |
||||
public double TiltPosition { get; set; } |
public double TiltPosition { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Zoom 坐标
|
/// Zoom 坐标
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "Zoom坐标")] |
[SugarColumn(ColumnDescription = "Zoom坐标")] |
||||
public double ZoomPosition { get; set; } |
public double ZoomPosition { get; set; } |
||||
|
|
||||
[SugarColumn(ColumnDescription = "视频宽度")] |
[SugarColumn(ColumnDescription = "视频宽度")] |
||||
public double VideoWidth { get; set; } |
public double VideoWidth { get; set; } |
||||
|
|
||||
[SugarColumn(ColumnDescription = "视频高度")] |
[SugarColumn(ColumnDescription = "视频高度")] |
||||
public double VideoHeight { get; set; } |
public double VideoHeight { get; set; } |
||||
|
|
||||
[SugarColumn(ColumnDescription = "画布 left 距离比例")] |
[SugarColumn(ColumnDescription = "画布 left 距离比例")] |
||||
public double CanvasLeftRatio { get; set; } |
public double CanvasLeftRatio { get; set; } |
||||
|
|
||||
[SugarColumn(ColumnDescription = "画布 top 距离比例")] |
[SugarColumn(ColumnDescription = "画布 top 距离比例")] |
||||
public double CanvasTopRatio { get; set; } |
public double CanvasTopRatio { get; set; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 备注
|
/// 备注
|
||||
/// </summary>
|
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "备注", Length = 256)] |
[SugarColumn(ColumnDescription = "备注", Length = 256)] |
||||
[MaxLength(256)] |
[MaxLength(256)] |
||||
public string Remark { get; set; } |
public string Remark { get; set; } |
||||
} |
} |
@ -1,85 +1,87 @@ |
|||||
using Cis.Application.Core; |
using Cis.Application.Core; |
||||
|
using Cis.Application.Core.Component.CameraSDK; |
||||
|
using EC.Helper.CameraSDK; |
||||
using Newtonsoft.Json.Linq; |
using Newtonsoft.Json.Linq; |
||||
|
|
||||
namespace Cis.Application.Cm; |
namespace Cis.Application.Cm; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 标记标签服务
|
/// 标签服务
|
||||
/// </summary>
|
/// </summary>
|
||||
[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)] |
[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)] |
||||
public class CmMarkLabelService : IDynamicApiController, ITransient |
public class CmMarkLabelService : IDynamicApiController, ITransient |
||||
{ |
{ |
||||
private readonly SqlSugarRepository<CmMarkLabel> _cmMarkLabelRep; |
private readonly SqlSugarRepository<CmMarkLabel> _baseRep; |
||||
|
|
||||
private CameraDataCenter _cameraDataCenter { get; set; } |
private CameraSdkServer _cameraSdk { get; set; } |
||||
|
|
||||
public CmMarkLabelService(CameraDataCenter cameraDataCenter, |
public CmMarkLabelService( |
||||
SqlSugarRepository<CmMarkLabel> cmMarkLabelRep) |
SqlSugarRepository<CmMarkLabel> baseRep, |
||||
{ |
CameraSdkServer cameraSdk |
||||
_cameraDataCenter = cameraDataCenter; |
) |
||||
_cmMarkLabelRep = cmMarkLabelRep; |
{ |
||||
} |
_baseRep = baseRep; |
||||
|
_cameraSdk = cameraSdk; |
||||
|
} |
||||
|
|
||||
[HttpPost] |
[HttpPost] |
||||
public async Task<bool> Add([FromForm] CmMarkLabel entity) |
public async Task<bool> Add([FromForm] CmMarkLabel entity) |
||||
{ |
{ |
||||
PtzInfo ptzInfo = _cameraDataCenter.GetCameraPtzInfo(entity.CbCameraId); |
bool ret = _cameraSdk.GetPtzInfoById(entity.CbCameraId.ToString(), out PtzInfo ptzInfo); |
||||
if (ptzInfo == null) |
if (!ret) return false; |
||||
return false; |
entity.PanPosition = ptzInfo.Pan; |
||||
entity.PanPosition = ptzInfo.Pan; |
entity.TiltPosition = ptzInfo.Tilt; |
||||
entity.TiltPosition = ptzInfo.Tilt; |
entity.ZoomPosition = ptzInfo.Zoom; |
||||
entity.ZoomPosition = ptzInfo.Zoom; |
return await _baseRep.InsertAsync(entity); |
||||
return await _cmMarkLabelRep.InsertAsync(entity); |
} |
||||
} |
|
||||
|
|
||||
[HttpPost] |
[HttpPost] |
||||
public async Task<long> AddReturnId([FromForm] CmMarkLabel entity) |
public async Task<long> AddReturnId([FromForm] CmMarkLabel entity) |
||||
{ |
{ |
||||
PtzInfo ptzInfo = _cameraDataCenter.GetCameraPtzInfo(entity.CbCameraId); |
bool ret = _cameraSdk.GetPtzInfoById(entity.CbCameraId.ToString(), out PtzInfo ptzInfo); |
||||
if (ptzInfo == null) |
if (!ret) return 0; |
||||
return 0; |
entity.PanPosition = ptzInfo.Pan; |
||||
entity.PanPosition = ptzInfo.Pan; |
entity.TiltPosition = ptzInfo.Tilt; |
||||
entity.TiltPosition = ptzInfo.Tilt; |
entity.ZoomPosition = ptzInfo.Zoom; |
||||
entity.ZoomPosition = ptzInfo.Zoom; |
await _baseRep.InsertAsync(entity); |
||||
await _cmMarkLabelRep.InsertAsync(entity); |
return entity.Id; |
||||
return entity.Id; |
} |
||||
} |
|
||||
|
|
||||
[HttpPost] |
[HttpPost] |
||||
public async Task<bool> Update([FromForm] CmMarkLabel entity) |
public async Task<bool> Update([FromForm] CmMarkLabel entity) |
||||
{ |
{ |
||||
return await _cmMarkLabelRep.UpdateAsync(entity); |
return await _baseRep.UpdateAsync(entity); |
||||
} |
} |
||||
|
|
||||
[HttpPost] |
[HttpPost] |
||||
public async Task<bool> Delete([FromForm] CmMarkLabel entity) |
public async Task<bool> Delete([FromForm] CmMarkLabel entity) |
||||
{ |
{ |
||||
return await _cmMarkLabelRep.DeleteAsync(entity); |
return await _baseRep.DeleteAsync(entity); |
||||
} |
} |
||||
|
|
||||
[HttpGet] |
[HttpGet] |
||||
public async Task<CmMarkLabel> Get(long id) |
public async Task<CmMarkLabel> Get(long id) |
||||
{ |
{ |
||||
CmMarkLabel entity = await _cmMarkLabelRep.GetByIdAsync(id); |
CmMarkLabel entity = await _baseRep.GetByIdAsync(id); |
||||
return entity; |
return entity; |
||||
} |
} |
||||
|
|
||||
[HttpGet] |
[HttpGet] |
||||
public async Task<List<CmMarkLabel>> GetList(string queryJson = "") |
public async Task<List<CmMarkLabel>> GetList(string queryJson = "") |
||||
{ |
{ |
||||
JObject queryObj = queryJson.ToJObject(); |
JObject queryObj = queryJson.ToJObject(); |
||||
List<CmMarkLabel> list = await _cmMarkLabelRep.AsQueryable() |
List<CmMarkLabel> list = await _baseRep.AsQueryable() |
||||
.ToListAsync(); |
.ToListAsync(); |
||||
return list; |
return list; |
||||
} |
} |
||||
|
|
||||
[HttpGet] |
[HttpGet] |
||||
public async Task<List<CmMarkLabel>> GetPageList(string queryJson, string pagination) |
public async Task<List<CmMarkLabel>> GetPageList(string queryJson, string pagination) |
||||
{ |
{ |
||||
Pagination pageObj = pagination.ToObject<Pagination>(); |
Pagination pageObj = pagination.ToObject<Pagination>(); |
||||
JObject queryObj = queryJson.ToJObject(); |
JObject queryObj = queryJson.ToJObject(); |
||||
List<CmMarkLabel> list = await _cmMarkLabelRep.AsQueryable() |
List<CmMarkLabel> list = await _baseRep.AsQueryable() |
||||
.ToPageListAsync(pageObj.Index, pageObj.Size); |
.ToPageListAsync(pageObj.Index, pageObj.Size); |
||||
return list; |
return list; |
||||
} |
} |
||||
} |
} |
@ -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
|
|
||||
} |
|
@ -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
|
|
||||
} |
|
@ -1,9 +0,0 @@ |
|||||
namespace Cis.Application.Core; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Ptz Api 接口
|
|
||||
/// </summary>
|
|
||||
public interface IPtzApi |
|
||||
{ |
|
||||
|
|
||||
} |
|
@ -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服务交互使用结构体
|
|
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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 |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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, |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -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 |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -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 |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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
|
||||
|
} |
@ -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 |
|
||||
}; |
|
||||
} |
|
||||
} |
|
@ -1,60 +1,62 @@ |
|||||
using Cis.Application.Cm; |
using Cis.Application.Core.Component.MarkSeacher; |
||||
|
|
||||
namespace Cis.Application.Core; |
namespace Cis.Application.Core; |
||||
|
|
||||
[ApiDescriptionSettings(CmInfo.GroupName, Order = CmInfo.GroupOrder)] |
[ApiDescriptionSettings(CoreInfo.GroupName, Order = CoreInfo.GroupOrder)] |
||||
public class MarkSearchService : IDynamicApiController, ITransient |
public class MarkSearchService : IDynamicApiController, ITransient |
||||
{ |
{ |
||||
#region Attr
|
#region Attr
|
||||
|
|
||||
private CameraDataCenter _cameraDataCenter { get; set; } |
private readonly CameraDataCenter _cameraDataCenter; |
||||
|
private readonly IMarkSearcherServer _markSearcherServer; |
||||
#endregion Attr
|
|
||||
|
#endregion Attr
|
||||
public MarkSearchService(CameraDataCenter cameraDataCenter) |
|
||||
{ |
public MarkSearchService(CameraDataCenter cameraDataCenter, IMarkSearcherServer markSearcherServer) |
||||
_cameraDataCenter = cameraDataCenter; |
{ |
||||
} |
_cameraDataCenter = cameraDataCenter; |
||||
|
_markSearcherServer = markSearcherServer; |
||||
[HttpPost] |
} |
||||
public async Task<bool> ActivateSearcher([FromForm] long cameraId) |
|
||||
{ |
[HttpPost] |
||||
return await _cameraDataCenter.ActivateSearcherAsync(cameraId); |
public async Task<bool> ActivateSearcher([FromForm] long cameraId) |
||||
} |
{ |
||||
|
return await _markSearcherServer.ActivateSearcherAsync(cameraId); |
||||
[HttpPost] |
} |
||||
public async Task<bool> DeActivateSearcher([FromForm] long cameraId) |
|
||||
{ |
[HttpPost] |
||||
return await _cameraDataCenter.DeActivateSearcherAsync(cameraId); |
public async Task<bool> DeactivateSearcher([FromForm] long cameraId) |
||||
} |
{ |
||||
|
return await _markSearcherServer.DeactivateSearcherAsync(cameraId); |
||||
[HttpGet] |
} |
||||
public async Task<bool> IsExistSearcher(long cameraId) |
|
||||
{ |
[HttpGet] |
||||
return await _cameraDataCenter.IsExistSearcherAsync(cameraId); |
public async Task<bool> IsExistsSearcher(long cameraId) |
||||
} |
{ |
||||
|
return await _markSearcherServer.IsExistsSearcherAsync(cameraId); |
||||
[HttpPost] |
} |
||||
public async Task<bool> ActivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId) |
|
||||
{ |
[HttpPost] |
||||
return await _cameraDataCenter.ActivateMarkLabelAsync(cameraId, markLabelId); |
public async Task<bool> ActivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId) |
||||
} |
{ |
||||
|
return await _markSearcherServer.ActivateMarkLabelAsync(cameraId, markLabelId); |
||||
[HttpPost] |
} |
||||
public async Task<bool> DeactivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId) |
|
||||
{ |
[HttpPost] |
||||
return await _cameraDataCenter.DeactivateMarkLabelAsync(cameraId, markLabelId); |
public async Task<bool> DeactivateMarkLabel([FromForm] long cameraId, [FromForm] long markLabelId) |
||||
} |
{ |
||||
|
return await _markSearcherServer.DeactivateMarkLabelAsync(cameraId, markLabelId); |
||||
[HttpGet] |
} |
||||
public async Task<bool> IsExistMarkLabel(long cameraId,long markLabelId) |
|
||||
{ |
[HttpGet] |
||||
return await _cameraDataCenter.IsExistMarkLabelAysnc(cameraId, markLabelId); |
public async Task<bool> IsExistsMarkLabel(long cameraId, long markLabelId) |
||||
} |
{ |
||||
|
return await _markSearcherServer.IsExistsMarkLabelAsync(cameraId, markLabelId); |
||||
[HttpGet] |
} |
||||
public async Task<List<MarkLabelCalcResult>> GetMarkLabelCalcResultList(long cameraId) |
|
||||
{ |
[HttpGet] |
||||
return await _cameraDataCenter.GetMarkLabelCalcResultListAsync(cameraId); |
public async Task<List<MarkLabelCalcResult>> GetMarkLabelCalcResultList(long cameraId) |
||||
} |
{ |
||||
|
return await _cameraDataCenter.GetMarkLabelCalcResultListAsync(cameraId); |
||||
|
} |
||||
} |
} |
@ -1,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
|
|
||||
} |
|
@ -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; } |
|
||||
} |
|
@ -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; |
|
||||
} |
|
||||
} |
|
@ -1,28 +1,21 @@ |
|||||
using NewLife.Caching; |
using StackExchange.Redis; |
||||
|
|
||||
namespace Cis.Core; |
namespace Cis.Core; |
||||
|
|
||||
public static class CacheSetup |
public static class CacheSetup |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// 缓存注册(新生命Redis组件)
|
/// Redis 缓存注册
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="services"></param>
|
/// <param name="services"></param>
|
||||
public static void AddCache(this IServiceCollection services) |
public static void AddCache(this IServiceCollection services) |
||||
{ |
{ |
||||
services.AddSingleton<ICache>(options => |
services.AddSingleton<IDatabase>(options => |
||||
{ |
{ |
||||
var cacheOptions = App.GetOptions<CacheOptions>(); |
var redisOptions = App.GetOptions<RedisOptions>(); |
||||
if (cacheOptions.CacheType == CacheTypeEnum.Redis.ToString()) |
ConnectionMultiplexer multiplexer = ConnectionMultiplexer.Connect(redisOptions.ConnectionString); |
||||
{ |
IDatabase cache = multiplexer.GetDatabase(); |
||||
var redis = new Redis(); |
return cache; |
||||
redis.Init(cacheOptions.RedisConnectionString); |
}); |
||||
return redis; |
} |
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return Cache.Default; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
} |
@ -0,0 +1,14 @@ |
|||||
|
namespace Cis.Core; |
||||
|
|
||||
|
public class SqlSugarConst |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 默认数据库标识
|
||||
|
/// </summary>
|
||||
|
public const string DefaultConfigId = "Cis"; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 默认表主键
|
||||
|
/// </summary>
|
||||
|
public const string DefaultPrimaryKey = "Id"; |
||||
|
} |
@ -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; } |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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,0 +1,19 @@ |
|||||
|
namespace Cis.Core; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 通用状态枚举
|
||||
|
/// </summary>
|
||||
|
public enum StatusEnum |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 停用
|
||||
|
/// </summary>
|
||||
|
[Description("停用")] |
||||
|
Disable = 0, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 启用
|
||||
|
/// </summary>
|
||||
|
[Description("启用")] |
||||
|
Enable = 1, |
||||
|
} |
@ -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(" ", "")); |
||||
|
} |
||||
|
|
||||
|
/// <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; |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
namespace Cis.Core; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Redis 配置选项
|
||||
|
/// </summary>
|
||||
|
public sealed class RedisOptions : IConfigurableOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Redis 连接字符串
|
||||
|
/// </summary>
|
||||
|
public string ConnectionString { get; set; } |
||||
|
} |
@ -1,14 +0,0 @@ |
|||||
namespace Cis.Core; |
|
||||
|
|
||||
public class SqlSugarConst |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 默认数据库标识
|
|
||||
/// </summary>
|
|
||||
public const string DefaultConfigId = "Cis"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 默认表主键
|
|
||||
/// </summary>
|
|
||||
public const string DefaultPrimaryKey = "Id"; |
|
||||
} |
|
@ -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; } |
|
||||
} |
|
@ -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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -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; } |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
namespace Cis.Core; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 通用状态枚举
|
|
||||
/// </summary>
|
|
||||
public enum StatusEnum |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 停用
|
|
||||
/// </summary>
|
|
||||
[Description("停用")] |
|
||||
Disable = 0, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 启用
|
|
||||
/// </summary>
|
|
||||
[Description("启用")] |
|
||||
Enable = 1, |
|
||||
} |
|
@ -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(" ", "")); |
|
||||
} |
|
||||
|
|
||||
/// <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; |
|
||||
} |
|
||||
} |
|
@ -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; } |
|
||||
} |
|
@ -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; } |
|
||||
} |
|
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"version": 1, |
||||
|
"isRoot": true, |
||||
|
"tools": { |
||||
|
"dotnet-ef": { |
||||
|
"version": "7.0.0", |
||||
|
"commands": [ |
||||
|
"dotnet-ef" |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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("================================="); |
|
||||
} |
|
||||
} |
|
Loading…
Reference in new issue