ly/Service/AlarmService.cs

537 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using LY.App.Common;
using LY.App.Common.Redis;
using LY.App.Common.WebSocket;
using LY.App.Extensions.DI;
using LY.App.Model;
using Mapster;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO;
using SqlSugar;
using StackExchange.Redis;
using System.Text;
namespace LY.App.Service
{
/// <summary>
/// 报警服务
/// </summary>
[ServiceInjection(InjectionType.Transient)]
public class AlarmService
{
private readonly SqlSugarClient _db;
private readonly IConfiguration _config;
private readonly RedisService _redisService;
private readonly PushService _pushService;
public AlarmService(SqlSugarClient db, IConfiguration config, RedisService redisService, PushService pushService)
{
_db = db;
_config = config;
_redisService = redisService;
_pushService = pushService;
}
/// <summary>
/// 新增报警信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ApiResult> AddAlarm(RevData input)
{
await SetDeviceStataus(input);
var key = RedisKeyList.DeviceInfo(input.product_ad_id);
var deviceinfo = await _redisService.GetAsync<DeviceEntity>(key);
if (deviceinfo == null)
{
deviceinfo = await _db.CopyNew().Queryable<DeviceEntity>().Where(s => s.DeviceSN == input.product_ad_id).FirstAsync();
if (deviceinfo == null)
{
return new ApiResult() { code = 1, msg = "设备不存在" };
}
await _redisService.SetAsync(key, deviceinfo, TimeSpan.FromDays(1));
}
var entity = input.data.Adapt<List<Alarm>>();
if (entity.Any())
{
// 排除掉不在范围内的报警信息
var O4entity = entity.Where(s => s.device_type == "O4" || s.serial_number == "O4" || s.device_type == "UAV" || !string.IsNullOrEmpty(s.device_type));
entity = entity.Where(s => checkDistance(s.drone_lat, s.drone_lon, deviceinfo.Lat, deviceinfo.Lon) == true).ToList();
if (O4entity.Any())
{
entity.AddRange(O4entity);
}
if (entity.Any())
{
foreach (var item in entity)
{
item.BatchId = await GetBatId(item.serial_number);
item.DeviceId = deviceinfo.Id;
item.DeviceName = deviceinfo.Name;
item.positionId = deviceinfo.PositionId;
item.PostionName = deviceinfo.PositionName;
item.Time = input.time;
item.distance = GisHelper.HaversineDistance(item.drone_lat, item.drone_lon, item.app_lat, item.app_lon);
item.IsWhitelist = await Iswhitlist(item.serial_number, item.drone_lat, item.drone_lon);
item.alarmLevel = item.IsWhitelist == true ? 0 : await GetAlarmLevel(deviceinfo.PositionId, item.drone_lon, item.drone_lat);
item.centerdistance = await GetCenterDistance(item.drone_lat, item.drone_lon, item.positionId);
}
await _db.CopyNew().Insertable(entity).SplitTable().ExecuteReturnSnowflakeIdListAsync();
//推送报警信息
await _pushService.SendMessageToAll(new { msgType = "event", data = entity });
await _redisService.DeleteAsync(RedisKeyList.index_data());
}
}
return new ApiResult();
}
/// <summary>
/// 防区中心距离
/// </summary>
/// <param name="lat"></param>
/// <param name="lon"></param>
/// <param name="positioonId"></param>
/// <returns></returns>
private async Task<double> GetCenterDistance(double lat, double lon, long positioonId)
{
try
{
var key = RedisKeyList.PositioinRegion(positioonId);
var pontion = await _redisService.GetAsync<PositionInfo>(key);
if (pontion == null)
{
pontion = await _db.CopyNew().Queryable<PositionInfo>()
.Where(s => s.Id == positioonId)
.FirstAsync();
await _redisService.SetAsync(key, pontion);
}
var distance = GisHelper.HaversineDistance(lat, lon, pontion.lat, pontion.lon);
return distance;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return 0;
}
/// <summary>
/// 判断 距离是否在范围内
/// </summary>
/// <param name="lat"></param>
/// <param name="lon"></param>
/// <param name="d_lat"></param>
/// <param name="d_lon"></param>
/// <returns></returns>
private bool checkDistance(double lat, double lon, double d_lat, double d_lon)
{
if (lat == 0 || lon == 0) return false;
var maxDistance = Convert.ToDouble(_config["MaxDistance"]);
var distance = GisHelper.HaversineDistance(lat, lon, d_lat, d_lon);
return distance < maxDistance;
}
/// <summary>
/// 判断是否在白名单中
/// </summary>
/// <param name="serial_number"></param>
/// <param name="lat"></param>
/// <param name="lon"></param>
/// <returns></returns>
private async Task<bool> Iswhitlist(string serial_number, double lat, double lon)
{
string key = RedisKeyList.white_list(serial_number);
if (await _redisService.ExistsAsync(key))
{
var entity = await _redisService.GetAsync<Whitelist>(key);
//判断时间是否在区在
if (entity.allDay)
{
return true;
}
else
{
return entity.startTime <= DateTime.Now && DateTime.Now <= entity.endTime;
}
}
return false;
}
static bool IsPointInGeoJson(double latitude, double longitude, string geoJson)
{
// 使用 NetTopologySuite 解析 GeoJSON
var reader = new GeoJsonReader();
Geometry geometry = reader.Read<Geometry>(geoJson);
// 创建点
var point = new Point(longitude, latitude);
// 判断点是否在几何图形内
return geometry.Contains(point);
}
/// <summary>
/// 计算入侵级别
/// </summary>
/// <param name="positionId"></param>
/// <param name="lon"></param>
/// <param name="lat"></param>
/// <returns></returns>
private async Task<int> GetAlarmLevel(long positionId, double lon, double lat)
{
int result = 0;
if (positionId > 0 && lon > 0 && lat > 0)
{
var key = RedisKeyList.PositioinRegion(positionId);
var geodata = await _redisService.GetAsync<PositionInfo>(key);
if (geodata == null)
{
geodata = await _db.CopyNew().Queryable<PositionInfo>()
.Where(s => s.Id == positionId)
.FirstAsync();
await _redisService.SetAsync<PositionInfo>(key, geodata);
}
WKTReader reader = new WKTReader();
Geometry point = reader.Read($"POINT ({lon} {lat})");
Geometry multipolygon = reader.Read(geodata.Region);
if (multipolygon.Contains(point))
{
return 1;
}
}
return result;
}
/// <summary>
/// 推送消息
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task PushMessage(object message)
{
//推送前端无人机数据
await _pushService.SendMessageToAll(message);
}
/// <summary>
/// 设置设备在线状态,并更新数据
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private async Task SetDeviceStataus(RevData input)
{
await _redisService.SetAsync(RedisKeyList.DeviceStatus(input.product_ad_id), true, TimeSpan.FromSeconds(15));
//更新 设备缓存
var key = RedisKeyList.DeviceInfo(input.product_ad_id);
var deviceinfo = await _redisService.GetAsync<DeviceEntity>(key);
if (deviceinfo != null && deviceinfo.isMove)
{
deviceinfo.Lat = input.product_lat;
deviceinfo.Lon = input.product_lon;
await _redisService.SetAsync(key, deviceinfo);
//更新位置
// await _db.Updateable(deviceinfo).ExecuteCommandAsync();
await _db.CopyNew().Updateable<DeviceEntity>(deviceinfo).UpdateColumns(it => new { it.Lat, it.Lon }).ExecuteCommandAsync();
}
}
private async Task<long> GetBatId(string droneId)
{
var timeSpan = Convert.ToDouble(_config["BatchId"]);
var key = RedisKeyList.BatchIdBysn(droneId);
//从redis取出batchid如果没有就新加一个每次访问都重置一下过期时间来模拟滑动过期
var batchId = await _redisService.GetAsync<long>(key);
if (batchId == 0)
{
batchId = SnowFlakeSingle.Instance.NextId();
await _redisService.DeleteAsync(RedisKeyList.index_data());
}
await _redisService.SetAsync(key, batchId, TimeSpan.FromSeconds(timeSpan));
return batchId;
}
/// <summary>
/// //根据batchId获取报警信息
/// </summary>
/// <param name="batchId"></param>
/// <returns></returns>
public async Task<ApiResult> GetByBatchId(long batchId)
{
var items = await _db.Queryable<Alarm>().SplitTable()
.Where(s => s.BatchId == batchId)
.OrderBy(s => s.Id).Select(s => new
{
Lon = s.drone_lon,
Lat = s.drone_lat,
Alt = s.height,
s.app_lat,
s.app_lon,
s.CreateTime,
AlarmLevel = s.alarmLevel,
}).ToListAsync();
return new ApiResult() { data = items };
}
/// <summary>
/// 首页统计
/// </summary>
/// <returns></returns>
public async Task<Dictionary<string, int>> IndexCount()
{
Dictionary<string, int> result = new Dictionary<string, int>();
// 获取当前日期和时间
DateTime currentDate = DateTime.Now;
// 获取当天的开始时间
DateTime startDate = currentDate.Date;
// 获取当天的结束时间23:59:59
DateTime endDate = currentDate.Date.AddDays(1).AddTicks(-1);
//计算当天
var todaywaring = await _db.Queryable<Alarm>().SplitTable()
.Where(s => s.alarmLevel > 0 && s.IsWhitelist == false)
.Where(s => s.CreateTime >= startDate && s.CreateTime <= endDate)
.GroupBy(s => s.BatchId)
.Select(s => s.BatchId).CountAsync();
//计算当天处理次数
//var todayhandle = _db.Queryable<AttackEntity>()
// .Where(s => s.BatchId > 0)
// .Where(s => s.CreateTime >= startDate && s.CreateTime <= endDate);
//计算总数
var totalcount = _db.Queryable<Alarm>().SplitTable()
.Where(s => s.alarmLevel > 0 && s.IsWhitelist == false)
.GroupBy(s => s.BatchId)
.Select(s => s.BatchId);
//计算处理总数
//var totalhandle = _db.Queryable<AttackEntity>()
// .Where(x => x.BatchId > 0);
//组合查询结果
//return new
//{
// todaywaring = await todaywaring.CountAsync(),
// todayhandle =0,
// totalcount = await totalcount.CountAsync(),
// totalhandle = 0
//};
result.Add("todaywaring", todaywaring);
result.Add("todayhandle", 0);
result.Add("totalcount", await totalcount.CountAsync());
result.Add("totalhandle", 0);
return result;
}
/// <summary>
/// 分页
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ApiResult> GetPage(AlarmReq input)
{
var result = await CreatePage(input);
return new ApiResult()
{
code = 0,
data = new
{
total = result.Item1,
items = result.Item2
}
};
}
/// <summary>
/// 热力图
/// </summary>
/// <returns></returns>
public async Task<ApiResult> hotmap()
{
var query = await _db.Queryable<Alarm>().SplitTable()
.GroupBy(x => new { Lon = SqlFunc.Round(x.drone_lon, 4), Lat = SqlFunc.Round(x.drone_lat, 4) }) // 按经纬度分组
.Select(x => new
{
Lon = SqlFunc.Round(x.drone_lon, 4),
Lat = SqlFunc.Round(x.drone_lat, 4),
Count = SqlFunc.AggregateCount(x.BatchId), // 统计去重后的BatchId
})
.ToListAsync();
return new ApiResult() { data = query };
}
/// <summary>
/// 列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<Tuple<int, List<AlarmRepDto>>> CreatePage(AlarmReq input)
{
RefAsync<int> total = 0;
var items = await _db.Queryable<Alarm>().SplitTable()
.WhereIF(input.Frequency.HasValue, st => st.freq == input.Frequency.Value)
.WhereIF(!string.IsNullOrEmpty(input.sn), s => s.serial_number.Contains(input.sn))
.WhereIF(!string.IsNullOrEmpty(input.model), st => st.device_type == input.model)
.WhereIF(input.strartDate.HasValue, st => st.CreateTime >= input.strartDate.Value)
.WhereIF(input.endDate.HasValue, st => st.CreateTime <= input.endDate.Value.AddDays(1))
.OrderBy(s => s.BatchId, OrderByType.Desc)
.GroupBy(s => new { s.BatchId, s.serial_number, s.device_type, s.positionId, s.PostionName, s.freq })
.Select(st => new AlarmRepDto
{
batchId = st.BatchId.ToString(),
startTime = SqlFunc.AggregateMin(st.CreateTime),
endTime = SqlFunc.AggregateMax(st.CreateTime),
sn = st.serial_number,
Frequency = st.freq,
duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds,
model = st.device_type,
positionId = st.positionId,
positionName = st.PostionName
}).MergeTable()//合并查询
.ToPageListAsync(input.pageNum, input.pageSize, total);
return Tuple.Create(total.Value, items);
}
public async Task<ApiResult> CreateHistoryPage(AlarmReq input)
{
var tables = _db.SplitHelper<Alarm>().GetTables();
string coutsql = @" SELECT COUNT(*)
FROM (
SELECT batch_id
FROM (
{0}
) AS unionTable
GROUP BY batch_id
) AS CountTable;";
string page = @" SELECT *
FROM (
SELECT batch_id
FROM (
{0}
) AS unionTable
GROUP BY batch_id ORDER BY batch_id desc LIMIT {1},{2}
) AS CountTable ";
string tablesql = string.Join(" UNION ALL ", tables.Select(item => $"SELECT batch_id FROM {item.TableName} GROUP BY batch_id "));
if (!string.IsNullOrEmpty(input.sn))
{
tablesql = string.Join(" UNION ALL ", tables.Select(item => $"SELECT batch_id FROM {item.TableName} where serial_number like '%{input.sn}%' GROUP BY batch_id"));
}
var pageitem = await _db.Ado.SqlQueryAsync<int, long>(string.Format(coutsql, tablesql) + string.Format(page, tablesql, (input.pageNum - 1) * input.pageSize, input.pageSize));
var temp = await _db.Queryable<Alarm>()
.Where(s => pageitem.Item2.Contains(s.BatchId)).SplitTable().Select(s => new { s.BatchId, s.freq, s.Id, s.positionId, s.PostionName, s.alarmLevel }).ToListAsync();
var query = await _db.Queryable<Alarm>()
.Where(s => pageitem.Item2.Contains(s.BatchId)).SplitTable()
.GroupBy(s => new { s.BatchId, s.serial_number, s.device_type })
.Select(st => new AlarmRepDto
{
batchId = st.BatchId.ToString(),
startTime = SqlFunc.AggregateMin(st.CreateTime),
endTime = SqlFunc.AggregateMax(st.CreateTime),
sn = st.serial_number,
duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds,
model = st.device_type,
IsWhitelist = SqlFunc.AggregateMax(st.IsWhitelist),
}).OrderByDescending(s => s.batchId).ToListAsync();
query.ForEach(s =>
{
s.duration = s.duration == 0 ? 1 : s.duration;
s.Frequency = temp.Where(m => m.BatchId == long.Parse(s.batchId)).OrderByDescending(o => o.Id).FirstOrDefault().freq;
s.positionId = temp.Where(m => m.BatchId == long.Parse(s.batchId)).OrderByDescending(o => o.Id).FirstOrDefault().positionId;
s.positionName = temp.Where(m => m.BatchId == long.Parse(s.batchId)).OrderByDescending(o => o.Id).FirstOrDefault().PostionName;
s.alarmLevel = temp.Where(m => m.BatchId == long.Parse(s.batchId)).Any(o => o.alarmLevel == 1) ? 1 : 0;
}); return new ApiResult()
{
code = 0,
data = new
{
total = pageitem.Item1.First(),
items = query
}
};
}
/// <summary>
/// 报表统计
/// </summary>
/// <returns></returns>
public async Task<ApiResult> GetReport(DateTime? start, DateTime? end)
{
start = start.HasValue ? start.Value : DateTime.Now.AddMonths(-1);
end = end.HasValue ? end.Value.AddDays(1) : DateTime.Now.Date.AddDays(1);
var query = await _db.Queryable<Alarm>().SplitTable()
.Where(s => s.alarmLevel > 0)
.WhereIF(start.HasValue, st => st.CreateTime >= start.Value)
.WhereIF(end.HasValue, st => st.CreateTime <= end.Value.AddDays(1))
.GroupBy(s => new { s.BatchId, s.serial_number, s.device_type, s.positionId, s.PostionName, s.DeviceId, s.DeviceName })
.Select(st => new
{
batchId = st.BatchId,
startTime = SqlFunc.AggregateMin(st.CreateTime),
endTime = SqlFunc.AggregateMax(st.CreateTime),
sn = st.serial_number,
Frequency = SqlFunc.AggregateMax(st.freq),
duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds,
model = st.device_type,
positionName = st.PostionName,
deviceId = st.DeviceId,
deviceName = st.DeviceName
}).MergeTable()//合并查询
.ToListAsync();
return new ApiResult()
{
data = new
{
freq = query.GroupBy(s => new { freq = FreqConvert.CoverFreq(s.Frequency) })
.Select(b => new
{
b.Key.freq,
count = b.Count()
}),
model = query.GroupBy(s => new { s.model })
.Select(b => new
{
b.Key.model,
count = b.Count()
}),
position = query.GroupBy(s => new { s.positionName })
.Select(s => new
{
s.Key.positionName,
count = s.Count()
}),
time = query.GroupBy(s => s.startTime.ToString("HH"))
.Select(it => new
{
it.Key,
count = it.Count()
}),
date = query.GroupBy(s => s.startTime.ToString("yyyy-MM-dd"))
.Select(it => new
{
it.Key,
count = it.Count()
}),
device = query.GroupBy(s => new { s.deviceName })
.Select(b => new
{
b.Key.deviceName,
count = b.Count()
})
}
};
}
/// <summary>
/// 热力图取经纬度4位数
/// </summary>
/// <param name="batchIds"></param>
/// <returns></returns>
private async Task<object> GenerateHotMap(List<long> batchIds)
{
return await _db.Queryable<Alarm>().SplitTable()
.Where(s => s.drone_lat > 0 && s.drone_lon > 0 && batchIds.Contains(s.BatchId))
.Select(s => new
{
drone_lon = SqlFunc.Round(s.drone_lon, 4),
drone_lat = SqlFunc.Round(s.drone_lat, 4),
})
.GroupBy(s => new { s.drone_lat, s.drone_lon })
.Select(s => new
{
Key = new { Lon = SqlFunc.Round(s.drone_lat, 4), Lat = SqlFunc.Round(s.drone_lon, 4) },
val = SqlFunc.AggregateCount(s)
})
.ToListAsync();
}
}
}