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
{
///
/// 报警服务
///
[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;
}
///
/// 新增报警信息
///
///
///
public async Task AddAlarm(RevData input)
{
await SetDeviceStataus(input);
var key = RedisKeyList.DeviceInfo(input.product_ad_id);
var deviceinfo = await _redisService.GetAsync(key);
if (deviceinfo == null)
{
deviceinfo = await _db.CopyNew().Queryable().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>();
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();
}
///
/// 防区中心距离
///
///
///
///
///
private async Task GetCenterDistance(double lat, double lon, long positioonId)
{
try
{
var key = RedisKeyList.PositioinRegion(positioonId);
var pontion = await _redisService.GetAsync(key);
if (pontion == null)
{
pontion = await _db.CopyNew().Queryable()
.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;
}
///
/// 判断 距离是否在范围内
///
///
///
///
///
///
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;
}
///
/// 判断是否在白名单中
///
///
///
///
///
private async Task 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(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(geoJson);
// 创建点
var point = new Point(longitude, latitude);
// 判断点是否在几何图形内
return geometry.Contains(point);
}
///
/// 计算入侵级别
///
///
///
///
///
private async Task 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(key);
if (geodata == null)
{
geodata = await _db.CopyNew().Queryable()
.Where(s => s.Id == positionId)
.FirstAsync();
await _redisService.SetAsync(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;
}
///
/// 推送消息
///
///
///
public async Task PushMessage(object message)
{
//推送前端无人机数据
await _pushService.SendMessageToAll(message);
}
///
/// 设置设备在线状态,并更新数据
///
///
///
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(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(deviceinfo).UpdateColumns(it => new { it.Lat, it.Lon }).ExecuteCommandAsync();
}
}
private async Task GetBatId(string droneId)
{
var timeSpan = Convert.ToDouble(_config["BatchId"]);
var key = RedisKeyList.BatchIdBysn(droneId);
//从redis取出batchid,如果没有,就新加一个,每次访问都重置一下过期时间来模拟滑动过期
var batchId = await _redisService.GetAsync(key);
if (batchId == 0)
{
batchId = SnowFlakeSingle.Instance.NextId();
await _redisService.DeleteAsync(RedisKeyList.index_data());
}
await _redisService.SetAsync(key, batchId, TimeSpan.FromSeconds(timeSpan));
return batchId;
}
///
/// //根据batchId获取报警信息
///
///
///
public async Task GetByBatchId(long batchId)
{
var items = await _db.Queryable().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 };
}
///
/// 首页统计
///
///
public async Task> IndexCount()
{
Dictionary result = new Dictionary();
// 获取当前日期和时间
DateTime currentDate = DateTime.Now;
// 获取当天的开始时间
DateTime startDate = currentDate.Date;
// 获取当天的结束时间(23:59:59)
DateTime endDate = currentDate.Date.AddDays(1).AddTicks(-1);
//计算当天
var todaywaring = await _db.Queryable().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()
// .Where(s => s.BatchId > 0)
// .Where(s => s.CreateTime >= startDate && s.CreateTime <= endDate);
//计算总数
var totalcount = _db.Queryable().SplitTable()
.Where(s => s.alarmLevel > 0 && s.IsWhitelist == false)
.GroupBy(s => s.BatchId)
.Select(s => s.BatchId);
//计算处理总数
//var totalhandle = _db.Queryable()
// .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;
}
///
/// 分页
///
///
///
public async Task GetPage(AlarmReq input)
{
var result = await CreatePage(input);
return new ApiResult()
{
code = 0,
data = new
{
total = result.Item1,
items = result.Item2
}
};
}
///
/// 热力图
///
///
public async Task hotmap()
{
var query = await _db.Queryable().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 };
}
///
/// 列表
///
///
///
public async Task>> CreatePage(AlarmReq input)
{
RefAsync total = 0;
var items = await _db.Queryable().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 CreateHistoryPage(AlarmReq input)
{
var tables = _db.SplitHelper().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(string.Format(coutsql, tablesql) + string.Format(page, tablesql, (input.pageNum - 1) * input.pageSize, input.pageSize));
var query = await _db.Queryable()
.Where(s => pageitem.Item2.Contains(s.BatchId)).SplitTable()
.GroupBy(s => new { s.BatchId, s.serial_number, s.device_type, s.positionId, s.PostionName, s.freq, s.alarmLevel })
.Select(st => new AlarmRepDto
{
batchId = st.BatchId.ToString(),
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 == 0 ? 1 : (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds,
model = st.device_type,
IsWhitelist = SqlFunc.AggregateMax(st.IsWhitelist),
alarmLevel = SqlFunc.AggregateMax(st.alarmLevel)
}).OrderByDescending(s => s.batchId).ToListAsync();
return new ApiResult()
{
code = 0,
data = new
{
total = pageitem.Item1.First(),
items = query
}
};
}
///
/// 报表统计
///
///
public async Task 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().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()
})
}
};
}
///
/// 热力图,取经纬度4位数
///
///
///
private async Task