|
|
|
using Cis.Application.Cb;
|
|
|
|
using Cis.Application.Cm;
|
|
|
|
using Cis.Application.Tb;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
|
|
|
namespace Cis.Application.Core;
|
|
|
|
|
|
|
|
public class CameraDataCenter
|
|
|
|
{
|
|
|
|
#region Attr
|
|
|
|
|
|
|
|
private readonly SqlSugarRepository<CbCamera> _cbCameraRep;
|
|
|
|
private readonly SqlSugarRepository<CmMarkLabel> _cmMarkLableRep;
|
|
|
|
private readonly SqlSugarRepository<TbPtzCamera> _tbPtzCameraRep;
|
|
|
|
private readonly PtzServerApi _ptzServerApi;
|
|
|
|
private readonly CameraDataOptions options;
|
|
|
|
|
|
|
|
private Thread _thread { get; set; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// (cbCameraId, cbCameraIp)
|
|
|
|
/// </summary>
|
|
|
|
private Dictionary<long, string> _cbCameraId2IpDict { get; set; } = new();
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// (cameraIp, TbPtzCamera)
|
|
|
|
/// </summary>
|
|
|
|
private ConcurrentDictionary<string, TbPtzCamera> _tbPtzCameraDict { get; set; } = new();
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// (cameraIp, PtzInfo)
|
|
|
|
/// </summary>
|
|
|
|
private ConcurrentDictionary<string, PtzInfo> _cameraPtzInfoDict { get; set; } = new();
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// _markSearcherDict 锁对象,写锁
|
|
|
|
/// </summary>
|
|
|
|
private static ReaderWriterLockSlim msDictLock { get; } = new();
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// (cbCameraId, MarkSearcherBase)
|
|
|
|
/// </summary>
|
|
|
|
private ConcurrentDictionary<long, MarkSearcherBase> _markSearcherDict { get; set; } = new();
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// (cameraId, List(MarkLabelCalcResult))
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
private ConcurrentDictionary<long, List<MarkLabelCalcResult>> _markLabelCalcResultListDict { get; set; } = new();
|
|
|
|
|
|
|
|
#endregion Attr
|
|
|
|
|
|
|
|
private readonly ILogger<CameraDataCenter> _logger;
|
|
|
|
|
|
|
|
public CameraDataCenter()
|
|
|
|
{
|
|
|
|
_cbCameraRep = App.GetService<SqlSugarRepository<CbCamera>>();
|
|
|
|
_cmMarkLableRep = App.GetService<SqlSugarRepository<CmMarkLabel>>();
|
|
|
|
_tbPtzCameraRep = App.GetService<SqlSugarRepository<TbPtzCamera>>();
|
|
|
|
_ptzServerApi = App.GetService<PtzServerApi>();
|
|
|
|
options = App.GetOptions<CameraDataOptions>();
|
|
|
|
_logger = App.GetService<ILogger<CameraDataCenter>>();
|
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Init()
|
|
|
|
{
|
|
|
|
if (!options.LazyInit)
|
|
|
|
{
|
|
|
|
List<TbPtzCamera> list = _tbPtzCameraRep.GetList();
|
|
|
|
// 根据 Ip 去重
|
|
|
|
foreach (TbPtzCamera item in list)
|
|
|
|
{
|
|
|
|
if (!_tbPtzCameraDict.ContainsKey(item.Ip))
|
|
|
|
_tbPtzCameraDict[item.Ip] = item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 初始化 thread
|
|
|
|
_thread = new Thread(WorkLoop)
|
|
|
|
{
|
|
|
|
IsBackground = true// 设置后台线程
|
|
|
|
};
|
|
|
|
_thread.Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
#region Loop
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 循环运行
|
|
|
|
/// </summary>
|
|
|
|
private void WorkLoop()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
RefreshPtzInfoByApi();
|
|
|
|
RefreshMarkSearcher();
|
|
|
|
Thread.Sleep(options.LoopInterval);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void RefreshPtzInfoByApi()
|
|
|
|
{
|
|
|
|
foreach (TbPtzCamera item in _tbPtzCameraDict.Values)
|
|
|
|
{
|
|
|
|
PtzInfo ptzInfo = _ptzServerApi.GetPtzInfo(item.CameraId);
|
|
|
|
_cameraPtzInfoDict[item.Ip] = ptzInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private async void RefreshMarkSearcher()
|
|
|
|
{
|
|
|
|
foreach (KeyValuePair<long, MarkSearcherBase> pair in _markSearcherDict)
|
|
|
|
{
|
|
|
|
long cameraId = pair.Key;
|
|
|
|
MarkSearcherBase markSearcher = pair.Value;
|
|
|
|
bool ret = _cbCameraId2IpDict.TryGetValue(cameraId, out string cameraIp);
|
|
|
|
if (!ret) continue;
|
|
|
|
ret = _cameraPtzInfoDict.TryGetValue(cameraIp, out PtzInfo ptzInfo);
|
|
|
|
if (!ret) continue;
|
|
|
|
markSearcher.UpdateCameraCalcInfo(ptzInfo);
|
|
|
|
List<MarkLabelCalcResult> resultList = await markSearcher.CalcAsync();
|
|
|
|
_markLabelCalcResultListDict[cameraId] = resultList;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion Loop
|
|
|
|
|
|
|
|
#region external call
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 激活 cbCamera 进入运算
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="cameraId"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public bool ActivateSearcher(long cameraId)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
msDictLock.EnterWriteLock();
|
|
|
|
if (_markSearcherDict.ContainsKey(cameraId))
|
|
|
|
return false;
|
|
|
|
// 获取 cbCamera
|
|
|
|
CbCamera cbCamera = _cbCameraRep.GetById(cameraId);
|
|
|
|
if (cbCamera == null) return false;
|
|
|
|
// 获取 tbPtzCamera
|
|
|
|
string cameraIp = cbCamera.Ip;
|
|
|
|
bool ret = _tbPtzCameraDict.TryGetValue(cameraIp, out TbPtzCamera tbPtzCamera);
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
tbPtzCamera = _tbPtzCameraRep.GetFirst(u => u.Ip == cameraIp);
|
|
|
|
if (tbPtzCamera == null) return false;
|
|
|
|
_tbPtzCameraDict[cameraIp] = tbPtzCamera;
|
|
|
|
}
|
|
|
|
// 存储 cbCamera id 对应 ip 关系
|
|
|
|
_cbCameraId2IpDict[cbCamera.Id] = cameraIp;
|
|
|
|
// 创建 cameraCalcInfo
|
|
|
|
CameraCalcInfo cameraCalcInfo = CameraCalcInfo.New(cameraId, _ptzServerApi.GetPtzInfo(tbPtzCamera.Id));
|
|
|
|
// 创建 markSeacher
|
|
|
|
HikMarkSeacher markSeacher = new(cameraCalcInfo);
|
|
|
|
// 获取 cmMarkLabel 列表
|
|
|
|
List<CmMarkLabel> cmMarkLabelList = _cmMarkLableRep.GetList(u => u.CbCameraId == cameraId);
|
|
|
|
// 将 cmMarkLabel 一一添加到 markSeacher
|
|
|
|
foreach (CmMarkLabel item in cmMarkLabelList)
|
|
|
|
{
|
|
|
|
MarkLabelCalcInfo markLabelCalcInfo = MarkLabelCalcInfo.New(
|
|
|
|
item.Id,
|
|
|
|
PtzInfo.New(item.PanPosition, item.TiltPosition, item.ZoomPosition),
|
|
|
|
item.VideoWidth,
|
|
|
|
item.VideoHeight,
|
|
|
|
item.CanvasLeftRatio,
|
|
|
|
item.CanvasTopRatio
|
|
|
|
);
|
|
|
|
markSeacher.AddMarkLabelCalcInfo(markLabelCalcInfo);
|
|
|
|
}
|
|
|
|
// 将 markSeacher 放入字典
|
|
|
|
_markSearcherDict[cameraId] = markSeacher;
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
msDictLock.ExitWriteLock();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 解除 cbCamera 进入运算
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="cameraId"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public bool DeActivateSearcher(long cameraId)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
msDictLock.EnterWriteLock();
|
|
|
|
_markSearcherDict.Remove(cameraId);
|
|
|
|
_markLabelCalcResultListDict.Remove(cameraId);
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
msDictLock.ExitWriteLock();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsExistSearcher(long cameraId)
|
|
|
|
{
|
|
|
|
return _markSearcherDict.ContainsKey(cameraId);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool ActivateMarkLabel(long cameraId, long markLabelId)
|
|
|
|
{
|
|
|
|
bool ret = _markSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher);
|
|
|
|
if (!ret)
|
|
|
|
return false;
|
|
|
|
if (markSearcher.ExistsMarkLabelCalcInfo(markLabelId))
|
|
|
|
return false;
|
|
|
|
CmMarkLabel label = _cmMarkLableRep.GetById(markLabelId);
|
|
|
|
if (label == null)
|
|
|
|
return false;
|
|
|
|
MarkLabelCalcInfo markLabelCalcInfo = MarkLabelCalcInfo.New(
|
|
|
|
label.Id,
|
|
|
|
PtzInfo.New(label.PanPosition, label.TiltPosition, label.ZoomPosition),
|
|
|
|
label.VideoWidth,
|
|
|
|
label.VideoHeight,
|
|
|
|
label.CanvasLeftRatio,
|
|
|
|
label.CanvasTopRatio
|
|
|
|
);
|
|
|
|
return markSearcher.AddMarkLabelCalcInfo(markLabelCalcInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool DectivateMarkLabel(long cameraId, long markLabelId)
|
|
|
|
{
|
|
|
|
bool ret = _markSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher);
|
|
|
|
return ret ? markSearcher.DeleteMarkLabelCalcInfo(markLabelId) : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsExistCameraMarkLabel(long cameraId, long markLabelId)
|
|
|
|
{
|
|
|
|
bool ret = _markSearcherDict.TryGetValue(cameraId, out MarkSearcherBase markSearcher) &&
|
|
|
|
markSearcher.ExistsMarkLabelCalcInfo(markLabelId);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<MarkLabelCalcResult> GetMarkLabelCalcResultList(long cameraId)
|
|
|
|
{
|
|
|
|
bool ret = _markLabelCalcResultListDict.TryGetValue(cameraId, out List<MarkLabelCalcResult> list);
|
|
|
|
return ret ? list : new();
|
|
|
|
}
|
|
|
|
|
|
|
|
public PtzInfo GetCameraPtzInfo(long cameraId)
|
|
|
|
{
|
|
|
|
PtzInfo ptzInfo = null;
|
|
|
|
bool ret = _cbCameraId2IpDict.TryGetValue(cameraId, out string cameraIp) &&
|
|
|
|
_cameraPtzInfoDict.TryGetValue(cameraIp, out ptzInfo);
|
|
|
|
return ret ? ptzInfo : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<bool> ActivateSearcherAsync(long cameraId)
|
|
|
|
{
|
|
|
|
return await Task.Run(() =>
|
|
|
|
{
|
|
|
|
return ActivateSearcher(cameraId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<bool> DeActivateSearcherAsync(long cameraId)
|
|
|
|
{
|
|
|
|
return await Task.Run(() =>
|
|
|
|
{
|
|
|
|
return DeActivateSearcher(cameraId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<bool> IsExistSearcherAsync(long cameraId)
|
|
|
|
{
|
|
|
|
return await Task.Run(() =>
|
|
|
|
{
|
|
|
|
return IsExistSearcher(cameraId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<bool> ActivateMarkLabelAsync(long cameraId, long markLabelId)
|
|
|
|
{
|
|
|
|
return await Task.Run(() =>
|
|
|
|
{
|
|
|
|
return ActivateMarkLabel(cameraId, markLabelId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<bool> DeactivateMarkLabelAsync(long cameraId, long markLabelId)
|
|
|
|
{
|
|
|
|
return await Task.Run(() =>
|
|
|
|
{
|
|
|
|
return DectivateMarkLabel(cameraId, markLabelId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<bool> IsExistMarkLabelAysnc(long cameraId, long markLabelId)
|
|
|
|
{
|
|
|
|
return await Task.Run(() =>
|
|
|
|
{
|
|
|
|
return IsExistCameraMarkLabel(cameraId, markLabelId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<List<MarkLabelCalcResult>> GetMarkLabelCalcResultListAsync(long cameraId)
|
|
|
|
{
|
|
|
|
return await Task.Run(() =>
|
|
|
|
{
|
|
|
|
return GetMarkLabelCalcResultList(cameraId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion external call
|
|
|
|
}
|