|
- using Microsoft.Extensions.Options;
- using Newtonsoft.Json;
- using System.Text;
- using TelpoDataService.Util.Clients;
- using TelpoDataService.Util;
- using TelpoDataService.Util.Entities.GpsCard;
- using TelpoDataService.Util.Entities.GpsLocationHistory;
- using TelpoDataService.Util.Models;
- using TelpoDataService.Util.QueryObjects;
- using TelpoPush.Fence.Worker.Common;
- using TelpoPush.Fence.Worker.Models.CacheTemplates;
- using TelpoPush.Fence.Worker.Models.Config;
- using TelpoPush.Fence.Worker.Models.Enum;
- using TelpoPush.Fence.Worker.Models.Fence;
- using TelpoPush.Fence.Worker.Models.MqTemplates;
- using TelpoPush.Fence.Worker.Models.PushTemplates;
- using TelpoPush.Fence.Worker.Service.Cache;
- using TelpoPush.Fence.Worker.Service.Mq;
-
- namespace TelpoPush.Fence.Worker.Handlers
- {
- public class FenceProcess
- {
- private readonly static object _GeofenceStatusLock = new object();
- private readonly static object _GeofenceDataLock = new object();
- private readonly GpsCardAccessorClient<GpsGeofenceStatus> _fenceStatusApiClient;
- private readonly GpsLocationHistoryAccessorClient<HisGpsAlarm> _alarmApiClient;
- private readonly IHostEnvironment _env;
- private readonly ILogger<FenceProcess> _logger;
- private readonly HttpHelperAsync _httpHelper;
- private readonly RedisUtil _redis;
- private readonly MqProcessMessage _serviceMqProcess;
- private readonly FenceConfig _positionConfig;
-
- public FenceProcess(
- IHostEnvironment env,
- ILogger<FenceProcess> logger,
- HttpHelperAsync httpHelper,
- RedisUtil redis,
- GpsCardAccessorClient<GpsGeofenceStatus> fenceStatusApiClient,
- GpsLocationHistoryAccessorClient<HisGpsAlarm> alarmApiClient,
- MqProcessMessage serviceMqProcess,
- IOptions<FenceConfig> positionConfig
- )
- {
- _env = env;
- _logger = logger;
- _httpHelper = httpHelper;
- _redis = redis;
- _positionConfig = positionConfig.Value;
- _serviceMqProcess = serviceMqProcess;
- _fenceStatusApiClient = fenceStatusApiClient;
- _alarmApiClient = alarmApiClient;
-
- }
-
- public async Task SendFence(string message)
- {
-
- _redis.ClearGpsDeviceFence();
-
- BaseModel model = JsonConvert.DeserializeObject<BaseModel>(message);
- var loc = model?.data;
- _logger.LogInformation($"设备{loc.imei},获取kafka数据并解析<{JsonConvert.SerializeObject(model)}>");
- if ((int)loc.locationType == 2) // 限制条件:locationType=2 LBS定位不推送
- {
- _logger.LogInformation($"设备{loc.imei},定位类型为LBS:<locationType={(int)loc.locationType}>不做处理");
- return;
- }
- DateTime time = DateTime.Parse(model.time); //=LastUpdate
- DateTime UtcDate = DateTime.Parse(loc.UtcDate);
- bool isPush = await _redis.GetIsDateStatus(new DeviceTime
- {
- imei = loc.imei,
- dt = time
- });
-
- isPush = true;
- if (!isPush)
- _logger.LogInformation($"数据未处理(历史数据):{loc.imei},{model.time}");
-
-
- var fenceObj = await _redis.GetGpsDeviceFence(loc.imei);
- if (fenceObj == null)
- _logger.LogInformation($"设备<{loc.imei}>还未设置电子围栏!");
-
- int radius = loc.Radius;
- GpsFencePoint location = new GpsFencePoint()
- {
- Longitude = Convert.ToDouble(loc.gaodeLongitude),
- Latitude = Convert.ToDouble(loc.gaodeLatitude)
- };
- var lpt = new GpsPoint(loc.originalLatitude, loc.originalLongitude);
- bool isInside = false; //{false=外面,true=里面}
- bool isInside_old = false; //{false=外面,true=里面}
- try
- {
- foreach (var fence in fenceObj.fenceList)
- {
- if (!Convert.ToBoolean(fence.isActive)) //围栏开关 0是关 1是开
- {
- _logger.LogInformation($"围栏<{fence.geofenceId}:{fence.geofenceName}>未生效!");
- continue;
- }
- //当前gaode坐标为中心点,精度(Radius)为半径画圆
- double _lat, _lng;
- GeoConvert.G2Gps((double)loc.gaodeLatitude, (double)loc.gaodeLongitude, out _lat, out _lng);
- var _cpt = new GpsPoint(Convert.ToDecimal(_lat), Convert.ToDecimal(_lng));
- var _circle = new Circle(_cpt, loc.Radius);
- switch (fence.fenceType) // 1 为圆形,2 多边形
- {
- case 1: // 圆形
- {
- foreach (var item in fence.pointList)
- {
- //圆形围栏坐标+半径
- double lat, lng;
- GeoConvert.G2Gps((double)item.latitude, (double)item.longitude, out lat, out lng);
- var cpt = new GpsPoint(Convert.ToDecimal(lat), Convert.ToDecimal(lng));
- var circle = new Circle(cpt, item.radius);
- //是否在围栏范围内
- isInside = GeoUtils.IsPointInCircle(lpt, circle);
- isInside_old = isInside;
- if (!isInside)
- {
- isInside_old = isInside;
- //圆和圆是否有交点
- isInside = GeoUtils.IsCircle2Circle(_circle, circle);
- }
- }
- }
- break;
- case 2: //多边形
- List<GpsFencePoint> pointList = new List<GpsFencePoint>();
- foreach (var item in fence.pointList)
- {
- pointList.Add(new GpsFencePoint
- {
- Longitude = (double)item.longitude,
- Latitude = (double)item.latitude
- });
- }
- isInside = GeofenceHelper.IsPolygonContainsPoint(pointList, new GpsFencePoint { Longitude = (double)loc.gaodeLongitude, Latitude = (double)loc.gaodeLatitude });
- isInside_old = isInside;
- if (!isInside)
- {
- //圆和多边形围栏否有交点
- var degree = GeofenceHelper.CalcDisgreeFromRadius(radius);
- isInside = CirclePolygonAlgorithm.IsIntersect(location, degree, pointList);
- isInside = GeofenceHelper.IsPolygonIntersectCircle(pointList, location, radius);
- }
- break;
- default:
- break;
- }
-
- //围栏进处理逻辑----------------------
- // [上次(历史缓存)状态]
- //lastStatus.isInside => false:在里面、false:在外面;
- //[当前状态]
- //sInside => false:外面、true:里面
- //=============================================
- //进入围栏:isInside = true
- // status = true
- //离开围栏:isInside = false
- // status = false
- //进入围栏:isInside = true
- // status(没有历史记录)
-
-
- var lastStatus = await GetLastGeofenceStatus(loc.imei, fence.geofenceId, fence.geofenceName);
- if (lastStatus != null)
- {
- if (isInside == lastStatus.isInside)
- await DataSaveAsync(model, fence, isInside, isInside_old, lastStatus.isInside, true).ConfigureAwait(false);
- }
- else
- {
- if (isInside)
- await DataSaveAsync(model, fence, isInside, true, true, false).ConfigureAwait(false);
- }
- }
- }
- catch (Exception ex)
- {
- _logger.LogError($"设备<{loc.imei}>进出围栏报警处理逻辑[异常] {ex.Message}\n{ex.StackTrace}\n{ex.HResult}");
- }
- }
-
-
- /// <summary>
- /// 进出记录处理
- /// </summary>
- /// <param name="model">当前定位数据</param>
- /// <param name="fence">当前围栏信息</param>
- /// <param name="isInside">当前围栏状态[面到面]:(sInside => false:外面、true:里面)</param>
- /// <param name="isInside_old">当前围栏状态[点到面]:(isInside_old => false:外面、true:里面)</param>
- /// <param name="status">最近(缓存)围栏状态[数据\Redis]:(false:在里面、false:在外面)</param>
- /// <param name="isRecord">是否第一次触发围栏状态(进入)</param>
- /// <returns></returns>
- public async Task DataSaveAsync(BaseModel model, DeviceFenceList fence, bool isInside, bool isInside_old, bool status, bool isRecord)
- {
- lock (_GeofenceDataLock)
- {
- var loc = model.data;
- string key = loc.imei + "_" + fence.geofenceId;
- DateTime UtcDate = DateTime.Parse(loc.UtcDate);
- bool? saveStatus = null;
- bool isExt = false; // 是否保存警告数据,并推送进/出围栏报警
- int isReuslt = 0; //最终进出结果(3=进,4=出)
- string deviceName = "";//设备昵称
- string alarmTypeTilte = ""; //进出描述
- string alarmTypeTilteLog = ""; //日志描述
- string fenceTypeStr = fence.fenceType == 1 ? "圆形围栏" : "多边形围栏"; //围栏类型描述
- _logger.LogInformation($"{fenceTypeStr}:{fence.geofenceName},Id:{fence.geofenceId},当前状态:({isInside}){(isInside ? "在里面" : "在外面")},上次状态:({status}){(status ? "在外面" : "在里面")}");
- //进出逻辑判断
- #region 进围栏报警
- if (isInside && status) // 当前在里面,上次记录为在外面,则触发进围栏报警
- {
- isReuslt = Convert.ToInt32(AlarmType.Entry);
- alarmTypeTilte = Enum.GetName(typeof(AlarmTypeChinese), AlarmType.Entry);
- saveStatus = false;
- #region 保存警告数据,并推送 进围栏报警
- //进围栏是否报警条件
- if (fence.alarmType == Convert.ToUInt32(GeofenceType.Entry) || fence.alarmType == Convert.ToUInt32(GeofenceType.Both))
- isExt = true;
- #endregion
- }
- #endregion
- #region 出围栏报警
- else if (!isInside && !status) // 当前在外面,上次记录为在里面,则触发出围栏报警
- {
- isReuslt = Convert.ToInt32(AlarmType.Exit);
- alarmTypeTilte = Enum.GetName(typeof(AlarmTypeChinese), AlarmType.Exit);
- saveStatus = true;
- #region 保存警告数据,并推送 出围栏报警
- //出围栏是否报警条件
- if (fence.alarmType == Convert.ToUInt32(GeofenceType.Exit) || fence.alarmType == Convert.ToUInt32(GeofenceType.Both))
- isExt = true;
- #endregion
- }
- #endregion
- //进出数据处理
- if (isReuslt > 0)
- {
- #region 保存状态,更新缓存
- //更新最后状态到数据库
- var dfstatus = new GpsGeofenceStatus
- {
- IsGeofenceStatus = saveStatus.Value,
- Serialno = loc.imei,
- GeofenceId = fence.geofenceId,
- };
- if (!isRecord)
- {
- dfstatus.Id = Guid.NewGuid().ToString();
- alarmTypeTilteLog = "第一次" + alarmTypeTilte;
- }
- else
- alarmTypeTilteLog = alarmTypeTilte;
- var saveGeofenceStatus = SaveGeofenceStatus(dfstatus);
- if (!saveGeofenceStatus.result)
- _logger.LogError(saveGeofenceStatus.message);
- else
- //更新缓存
- _redis.SetFenceLastStatus(key, new LastStatusInfo()
- {
- imei = loc.imei,
- fenceId = fence.geofenceId,
- fenceInfo = fence.geofenceName,
- typeId = isReuslt,
- typeInfo = Enum.GetName(typeof(AlarmTypeChinese), isReuslt),
- isInside = saveStatus.Value,
- address = loc.address,
- dt = model.time
- }).Wait();
- #endregion
- if (isExt)
- {
- #region 保存警告数据,并推送 进/出围栏报警
- string GeofenceResult = "";
- StringBuilder sb_old = new StringBuilder();
- StringBuilder sb = new StringBuilder();
- string url = "https://id.ssjlai.com/antpayweb/#/gaode-point-in-ring?";
- if (fence.fenceType == 1)
- {
- foreach (var item in fence.pointList)
- {
- sb.Append($"新算法【出围栏({isInside})】:{url}center={loc.gaodeLongitude},{loc.gaodeLatitude},{loc.Radius}&polygon={item.longitude},{item.latitude},{fence.geofenceId},{item.radius}");
- sb_old.Append($"旧算法【出围栏({isInside_old})】:{url}center={loc.gaodeLongitude},{loc.gaodeLatitude},0&polygon={item.longitude},{item.latitude},{fence.geofenceId},{item.radius}");
- }
- }
- else
- {
- string pointListStr = "";
- foreach (var item in fence.pointList)
- {
- pointListStr += $"{item.longitude}-{item.latitude},";
- }
- sb.Append($"新算法【出围栏({isInside})】:{url}center={loc.gaodeLongitude},{loc.gaodeLatitude},{loc.Radius}&polygon={pointListStr.Trim(',')},{fence.geofenceId},0");
- sb_old.Append($"旧算法【出围栏({isInside_old})】:{url}center={loc.gaodeLongitude},{loc.gaodeLatitude},0&polygon={pointListStr.Trim(',')},{fence.geofenceId},0");
- }
- GeofenceResult = sb.ToString() + ";" + sb_old.ToString();
- //设备昵称
- var deviceInfo = _redis.GetGpsDevice(loc.imei).Result;
- if (deviceInfo != null)
- deviceName = deviceInfo.deviceName;
- //警告保存到数据库
- var alarm = new HisGpsAlarm
- {
- DeviceId = loc.DeviceId,
- BaiduLat = loc.baiduLatitude,
- BaiduLng = loc.baiduLongitude,
- CreateTime = DateTime.Now,
- DeviceUtcTime = UtcDate,
- GeofenceId = fence.geofenceId,
- Glat = loc.gaodeLatitude,
- Glng = loc.gaodeLongitude,
- MessageId = Guid.NewGuid().ToString("D"),
- Olat = loc.originalLatitude,
- Olng = loc.originalLongitude,
- Serialno = loc.imei,
- DeviceName = deviceName,
- TypeId = isReuslt,
- Address = loc.address,
- Remarks = alarmTypeTilte + " " + fence.geofenceName,
- GeofenceResult = GeofenceResult
- };
- _alarmApiClient.Add(alarm);
-
- #region 微信推送(启用)
- PushWxTemplate wxModel = new PushWxTemplate
- {
- deviceId = loc.DeviceId,
- imei = loc.imei,
- alarmTypeId = alarm.TypeId,
- alarmDeviceName = alarm.DeviceName,
- alarmRemarks = alarmTypeTilte + " " + fence.geofenceName,
- address = loc.address,
- };
- _serviceMqProcess.ProcessWxAlarm(wxModel, fence.geofenceId, model.time).Wait();
- #endregion
- #region 第三方推送(停用)
- // 进围栏报警
- PushXiaoAnPushTemplate statusModel = new PushXiaoAnPushTemplate
- {
- Address = fence.geofenceName,
- DateTime = model.time,
- NickName = loc.imei,
- Title = alarmTypeTilte + " 报警",
- TextContent = alarmTypeTilte + " " + fence.geofenceName,
- };
- //PushStatusXiaoAn(loc.imei, statusModel);
- //_serviceMqProcess.ProcessThirdAlarm(statusModel, MqAlarmType.EnterFenceAlarm, model.time);
- // _serviceMqProcess.ProcessThirdAlarm(alarm, MqAlarmType.EnterFenceAlarm, model.time);
- #endregion
- #region 数据服务(java)部分推送
- if (fence.appType == 2)
- {
- var push = new
- {
- BaiduLatitude = loc.baiduLatitude,
- BaiduLongitude = loc.baiduLongitude,
- GaodeLatitude = loc.gaodeLatitude,
- GaodeLongitude = loc.gaodeLongitude,
- OriginalLatitude = loc.originalLatitude,
- OriginalLongitude = loc.originalLongitude,
- FenceId = fence.geofenceId,
- FenceName = fence.geofenceName,
- Address = loc.address,
- Info = alarmTypeTilte + " " + fence.geofenceName,
- };
-
- var commonInfo = new
- {
- Data = push,
- Imei = loc.imei,
- Time = model.time,
- AlarmType = isReuslt
- };
- _serviceMqProcess.ProcessPushService(commonInfo, loc.imei).Wait();
- }
- #endregion
- _logger.LogError($"设备{loc.imei}{alarmTypeTilteLog},loc.UtcDate: {UtcDate.ToString("yyyy-MM-dd HH:mm:ss")},loc.LastUpdate: {model.time},DateTime.Now: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
- #endregion
- }
- }
- }
- }
-
- //获取最近一次围栏进出状态
- public async Task<LastStatusInfo> GetLastGeofenceStatus(string imei, string fenceId, string fenceName)
- {
- LastStatusInfo model = null;
- string key = imei + "_" + fenceId;
- var lastStatus = await _redis.GetFenceLastStatus(key);
- if (lastStatus != null)
- return lastStatus;
- else
- {
- // 数据服务调用
- var param = new GeneralParam
- {
- Filters = new List<QueryFilterCondition>
- {
- new QueryFilterCondition
- {
- Key=nameof(GpsGeofenceStatus.GeofenceId),
- Value=fenceId,
- ValueType=QueryValueTypeEnum.String,
- Operator=QueryOperatorEnum.Equal
- }
- }
- };
- var gfs = _fenceStatusApiClient.GetFirst(param);
- if (gfs != null)
- {
- model = new LastStatusInfo()
- {
- imei = gfs.Serialno,
- fenceId = gfs.GeofenceId,
- fenceInfo = fenceName,
- typeId = gfs.IsGeofenceStatus ? Convert.ToInt32(AlarmType.Exit) : Convert.ToInt32(AlarmType.Entry),
- typeInfo = gfs.IsGeofenceStatus ? Enum.GetName(typeof(AlarmTypeChinese), AlarmType.Exit) : Enum.GetName(typeof(AlarmTypeChinese), AlarmType.Entry),
- isInside = gfs.IsGeofenceStatus,
- address = "",
- dt = gfs.CreateTime.Value.ToString("yyyy-MM-dd HH:mm:ss")
- };
- await _redis.SetFenceLastStatus(key, model);
- }
- }
- return model;
- }
-
- //保存围栏状态
- public (bool result, string message) SaveGeofenceStatus(GpsGeofenceStatus gfs)
- {
- lock (_GeofenceStatusLock)
- {
- bool result = false;
- string message = String.Empty;
- try
- {
- var param = new GeneralParam
- {
- Filters = new List<QueryFilterCondition>
- {
- new QueryFilterCondition
- {
- Key=nameof(GpsGeofenceStatus.GeofenceId),
- Value=gfs.GeofenceId,
- ValueType=QueryValueTypeEnum.String,
- Operator=QueryOperatorEnum.Equal
- }
- }
- };
-
- List<GpsGeofenceStatus> list = _fenceStatusApiClient.GetList(param).ToList();
- if (list.Count > 0)
- {
- if (list.Count == 1)
- {
- var gfStatus = list.FirstOrDefault();
- gfStatus.IsGeofenceStatus = gfs.IsGeofenceStatus;
- gfStatus.CreateTime = DateTime.Now;
- _fenceStatusApiClient.Update(gfStatus);
- }
- else
- {
- //只保留一条记录,多余的删除(历史遗留数据)
- int loop = 0;
- foreach (var item in list)
- {
- loop++;
- if (loop == 1)
- {
- var gfStatus = list.FirstOrDefault();
- gfStatus.IsGeofenceStatus = gfs.IsGeofenceStatus;
- gfStatus.CreateTime = DateTime.Now;
- _fenceStatusApiClient.Update(gfStatus);
- }
- else
- {
- _fenceStatusApiClient.Delete(item);
- }
- }
- }
- }
- else
- {
- gfs.CreateTime = DateTime.Now;
- _fenceStatusApiClient.Add(gfs);
- _logger.LogInformation($"增加围栏状态:<{JsonConvert.SerializeObject(gfs)}>");
- }
-
- #region 旧代码
- // GeofenceStatus gfStatus = client.GetFirst(param);
- //if (gfStatus != null)
- //{
- // gfStatus.IsGeofenceStatus = gfs.IsGeofenceStatus;
- // gfStatus.CreateTime = DateTime.Now;
- // client.Update(gfStatus);
-
- // //只保留一条记录,多余的删除(历史遗留数据)
- // List<GeofenceStatus> list = client.GetList(param).ToList();
- // if (list.Count > 1)
- // {
- // foreach (var item in list)
- // {
- // if (gfStatus.Id != item.Id)
- // {
- // client.Delete(item);
- // }
- // }
- // }
-
- // logger.LogInformation($"修改围栏状态:<{JsonConvert.SerializeObject(gfStatus)}>");
- //}
- //else
- //{
- // gfs.CreateTime = DateTime.Now;
- // client.Add(gfs);
- // logger.LogInformation($"增加围栏状态:<{JsonConvert.SerializeObject(gfs)}>");
- //}
- #endregion
- message = $"保存围栏状态发生成功!";
- result = true;
- }
- catch (Exception ex)
- {
- message = $"保存围栏状态发生异常:{ex.Message},{ex.StackTrace}!";
- }
- return (result, message);
- }
- }
- }
- }
|