You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

446 lines
25KB

  1. using GpsCardGatewayPosition.Common;
  2. using GpsCardGatewayPosition.Model.Cache;
  3. using GpsCardGatewayPosition.Model.Config;
  4. using GpsCardGatewayPosition.Model.Enum;
  5. using GpsCardGatewayPosition.Service.Biz.Location.Dto.Wayz;
  6. using GpsCardGatewayPosition.Service.Cache;
  7. using GpsCardGatewayPosition.Service.MqProducer;
  8. using Microsoft.Extensions.Logging;
  9. using Microsoft.Extensions.Options;
  10. using Newtonsoft.Json;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Linq;
  14. using System.Text;
  15. using System.Threading.Tasks;
  16. using TelpoDataService.Util.Clients;
  17. using TelpoDataService.Util.Entities.GpsCard;
  18. using TelpoDataService.Util.Entities.GpsLocationHistory;
  19. using TelpoDataService.Util;
  20. using GpsCardGatewayPosition.Service.Dto;
  21. using GpsCardGatewayPosition.Service.MqProducer.Model;
  22. using GpsCardGatewayPosition.Service.Biz.Location.Dto;
  23. namespace GpsCardGatewayPosition.Service.Biz.Location
  24. {
  25. public class LocationLogic
  26. {
  27. private readonly ServiceConfig _configService;
  28. private readonly MqProcessLogic _serviceMqProcess;
  29. private readonly DeviceCacheManager _deviceCacheMgr;
  30. private readonly GpsCardAccessorClient<GpsDeviceStatus> _deviceStatusApiClient;
  31. private readonly GpsLocationHistoryAccessorClient<HisGpsDeviceData> _hisDeviceDataApiClient;
  32. private readonly GpsLocationHistoryAccessorClient<History> _hisApiClient;
  33. private readonly ILogger<LocationLogic> _logger;
  34. private readonly int DEFAULT_LBS_ACCEPT_DISTANCE = 2000; // 默认IOT LBS经纬度与缓存经纬度
  35. public LocationLogic(IOptions<ServiceConfig> optConfigService, MqProcessLogic serviceMqProcess, DeviceCacheManager deviceCacheMgr,
  36. GpsCardAccessorClient<GpsDeviceStatus> deviceStatusApiClient, GpsLocationHistoryAccessorClient<HisGpsDeviceData> hisDeviceDataApiClient, GpsLocationHistoryAccessorClient<History> hisApiClient,
  37. ILogger<LocationLogic> logger)
  38. {
  39. _configService = optConfigService.Value;
  40. _serviceMqProcess = serviceMqProcess;
  41. _deviceCacheMgr = deviceCacheMgr;
  42. _deviceStatusApiClient = deviceStatusApiClient;
  43. _hisDeviceDataApiClient = hisDeviceDataApiClient;
  44. _hisApiClient = hisApiClient;
  45. _logger = logger;
  46. }
  47. #region 更新设备最后位置信息状态
  48. public async Task<GeneralResult> UpdateDeviceStatusAsync(string messageId, GpsDeviceStatus deviceStatus)
  49. {
  50. var result = new GeneralResult();
  51. try
  52. {
  53. var sn = deviceStatus.Serialno;
  54. var url = _configService.TelpoDataUrl;
  55. if (await _deviceCacheMgr.GetDeviceStatusBySerialNoAsync(messageId, sn) != null)
  56. {
  57. await _deviceStatusApiClient.UpdateAsync(deviceStatus, new RequestHeader { RequestId = messageId }).ConfigureAwait(false);
  58. }
  59. else
  60. {
  61. await _deviceStatusApiClient.AddAsync(deviceStatus, new RequestHeader { RequestId = messageId }).ConfigureAwait(false);
  62. }
  63. //更新DeviceStatus缓存
  64. _deviceCacheMgr.SetDeviceStatus(deviceStatus);
  65. result.IsSuccess = true;
  66. }
  67. catch (Exception ex)
  68. {
  69. result.Message = $"更新设备最后位置信息状态发生异常:{ex.Message}, {ex.StackTrace}";
  70. }
  71. return result;
  72. }
  73. #endregion
  74. #region 电子围栏处理
  75. public async void ProcessGeofence(LocationInfo loc, string messageId)
  76. {
  77. var fence = new FenceLocationPlus()
  78. {
  79. DeviceId = loc.DeviceId,
  80. imei = loc.SerialNo,
  81. address = loc.Address,
  82. baiduLatitude = loc.BaiduLat,
  83. baiduLongitude = loc.BaiduLng,
  84. gaodeLatitude = loc.GLat,
  85. gaodeLongitude = loc.GLng,
  86. originalLatitude = loc.OLat,
  87. originalLongitude = loc.OLng,
  88. LastUpdate = loc.LastUpdate!.Value.ToString("yyyy-MM-dd HH:mm:ss"), //LastUpdate = datetime,
  89. UtcDate = loc.UtcDate!.Value.ToString("yyyy-MM-dd HH:mm:ss"), //UtcDate =
  90. Radius = SafeType.SafeInt(loc.DeviceStatus),
  91. };
  92. await _serviceMqProcess.ProcessFencePlusAsync(fence, loc.LastUpdate.Value.ToString("yyyy-MM-dd HH:mm:ss"), messageId);
  93. }
  94. public async Task ProcessGeofenceAsync(LocationInfo loc, string messageId)
  95. {
  96. var fence = new FenceLocationPlus()
  97. {
  98. DeviceId = loc.DeviceId,
  99. imei = loc.SerialNo,
  100. address = loc.Address,
  101. baiduLatitude = loc.BaiduLat,
  102. baiduLongitude = loc.BaiduLng,
  103. gaodeLatitude = loc.GLat,
  104. gaodeLongitude = loc.GLng,
  105. originalLatitude = loc.OLat,
  106. originalLongitude = loc.OLng,
  107. LastUpdate = loc.LastUpdate!.Value.ToString("yyyy-MM-dd HH:mm:ss"), //LastUpdate = datetime,
  108. UtcDate = loc.UtcDate!.Value.ToString("yyyy-MM-dd HH:mm:ss"), //UtcDate =
  109. Radius = SafeType.SafeInt(loc.DeviceStatus),
  110. };
  111. await _serviceMqProcess.ProcessFencePlusAsync(fence, loc.LastUpdate.Value.ToString("yyyy-MM-dd HH:mm:ss"), messageId);
  112. }
  113. #endregion
  114. #region 保存原始数据包
  115. public async Task<GeneralResult> SavePackageAsync(string messageId, HisGpsDeviceData data)
  116. {
  117. var result = new GeneralResult();
  118. try
  119. {
  120. await _hisDeviceDataApiClient.AddAsync(data, header: new RequestHeader { RequestId = messageId }).ConfigureAwait(false);
  121. result.Message = $"保存原始数据包成功!";
  122. result.IsSuccess = true;
  123. }
  124. catch (Exception ex)
  125. {
  126. result.Message = $"保存原始数据包发生异常:{ex.Message}, {ex.StackTrace}!";
  127. }
  128. return result;
  129. }
  130. #endregion
  131. #region 保存历史位置
  132. /// <summary>
  133. ///
  134. /// </summary>
  135. /// <param name="loc"></param>
  136. /// <param name="positionStatus">定位状态缓存</param>
  137. /// <param name="sentTicks">设备上报消息到IOT的时间戳</param>
  138. /// <param name="cityCode">城市行政编码</param>
  139. /// <param name="steps">IOT上报步数(选填,暂时只有wifi2有)</param> // 2022/11/23 定位缓存需要增加步数字段实现前端“静止/行走”状态展示
  140. /// <returns></returns>
  141. public async Task<LocationServiceResult> AddLocationAsync(string messageId, LocationInfo loc, DevicePositionStatus positionStatus, long sentTicks, string cityCode = "", int? steps = default, object requestPostionData = default)
  142. {
  143. var result = new LocationServiceResult();
  144. var requestPostionVar = string.Empty;
  145. var requestPostionVarObj = new object(); ;
  146. if (requestPostionData is RequestLocationInfo<WayzWifiRequest> requestPostion && loc.LocationType == 3)
  147. {
  148. requestPostionVarObj = new
  149. {
  150. Location = new
  151. {
  152. Wifis = requestPostion.RequestData.Location.Wifis.Select(i => new { i.MacAddress, i.SignalStrength }).ToList()
  153. }
  154. };
  155. requestPostionVar = JsonConvert.SerializeObject(requestPostionVarObj);
  156. }
  157. else
  158. {
  159. requestPostionVar = null;
  160. }
  161. try
  162. {
  163. var url = _configService.TelpoDataUrl;
  164. var model = new History
  165. {
  166. LocationId = Guid.NewGuid().ToString("D"),
  167. DeviceId = loc.DeviceId,
  168. Serialno = loc.SerialNo,
  169. Utctime = loc.UtcDate,
  170. LastUpdate = loc.LastUpdate,
  171. IsStop = loc.IsStop ?? false,
  172. IsRedressed = loc.IsRedressed,
  173. Speed = loc.Speed,
  174. Course = loc.Course,
  175. LocationType = loc.LocationType,
  176. Olat = loc.OLat,
  177. Olng = loc.OLng,
  178. BaiduLat = loc.BaiduLat,
  179. BaiduLng = loc.BaiduLng,
  180. Glat = loc.GLat,
  181. Glng = loc.GLng,
  182. DeviceStatus = loc.DeviceStatus,
  183. Address = loc.Address,
  184. Remarks = $"{loc.Remarks}#{messageId}#{DateTime.Now}", //记录messageId方便查询异常的定位,系统保存时间
  185. RequestPosition = requestPostionVar,
  186. //RequestPosition =
  187. //loc.LocationType == 3 && requestPostionData. ? JsonConvert.SerializeObject(requestPostionData)
  188. };
  189. await _hisApiClient.AddAsync(model, model.Serialno, header: new RequestHeader { RequestId = messageId }).ConfigureAwait(false);
  190. result.Message = "保存历史位置成功!";
  191. result.IsSuccess = true;
  192. }
  193. catch (Exception ex)
  194. {
  195. result.Message = $"保存历史位置发生异常: message:{ex.Message}, {ex.StackTrace}";
  196. }
  197. if (positionStatus == null) positionStatus = new DevicePositionStatus();
  198. var cache = positionStatus.LastPosition;
  199. if (cache == null || cache.SentTicks < sentTicks)
  200. {
  201. //更新设备的坐标缓存
  202. // 2022/11/15 关于定位信息缓存更新机制的优化说明(实时定位场景):
  203. /**
  204. * // 2022/11/15 关于定位信息缓存更新机制的优化说明(实时定位场景):
  205. * 具体需求
  206. * 关于定位信息缓存更新机制的优化说明(实时定位场景):
  207. 1、缓存需要增加一个字段“原始有效定位时间”,该字段用于填写覆盖LBS定位数据的有效定位数据的定位时间。
  208. 2、缓存更新需要基于缓存的数据定位时间(last_update)与“原始有效定位时间”进行比较判定更新机制。
  209. 3、有效定位数据定位时间(非LBS IOT上报时间)>缓存的定位时间,直接用有效定位数据更新缓存,同时,有效定位数据的“原始有效定位时间”是当前数据的定位时间。
  210. 4、LBS定位数据定位时间(LBS IOT上报时间)>缓存的定位时间,则更新缓存的定位时间,定数数据还是原有缓存的定位数据,“原始有效定位时间”不变还是原有的缓存数据的时间。LBS定位数据定位时间<缓存的定位时间(不可能有这个情况),直接不处理。
  211. 5、有效定位数据时间(非LBS IOT上报时间)>缓存的“原始有效定位时间”,同时,有效定位数据时间<缓存的的定位时间的情况(就是之前有LBS定位更新了缓存定位时间),则更新缓存的定位数据内容及“原始有效定位时间”,“原始有效定位时间”为当前有效定位的定位时间,定位时间还是不变。
  212. ===================
  213. 注:原有定位数据不变都是直接入库。此缓存主要应用于实时定位数据获取。
  214. y: 原始有效定位;
  215. y_time:原始有效定位的时间;
  216. l_time: LBS有效定位的时间
  217. h: 缓存的定位
  218. h_time:缓存的定位时间(last_update)
  219. 1、定位数据
  220. y: 定位数据:区分有效定位数据(y_data)与LBS定位数据(w_data)。
  221. l_time: 定位的时间
  222. 2、缓存数据
  223. h: 缓存的定位
  224. h_time:缓存的定位时间(字段last_update)
  225. y_time:有效定位的时间(新增字段,取y_data的l_time);
  226. h_time>=y_time
  227. 3、y_data:l_time > h_time,则用y更新h、用l_time更新h_time、y_time。
  228. 4、w_data:l_time > h_time,则用y不更新h、用l_time更新h_time,y_time不变。
  229. 5、y_data:l_time < h_time & l_time > y_time, 则用y更新h、用l_time更新y_time
  230. */
  231. // 2022/12/2 所有定位类型都要生成缓存。
  232. var now = DateTime.Now;
  233. var positionCache = new DevicePositionStatus.PositionCache
  234. {
  235. Address = loc.Address,
  236. BaiduLat = loc.BaiduLat,
  237. BaiduLon = loc.BaiduLng,
  238. CityCode = cityCode,
  239. GaodeLat = loc.GLat,
  240. GaodeLon = loc.GLng,
  241. OriginalLat = loc.OLat,
  242. OriginalLon = loc.OLng,
  243. LocationType = loc.LocationType,
  244. //UpdateTime = loc.LastUpdate ?? DateTime.Now, //DateTime.Now,
  245. Steps = steps,
  246. // 2022/11/15 关于定位信息缓存更新机制的优化说明(实时定位场景):
  247. // 3、有效定位数据定位时间>缓存的定位时间,直接用有效定位数据更新缓存,同时,有效定位数据的“原始有效定位时间”是当前数据的定位时间。
  248. OriginalTime = loc.LastUpdate ?? now,
  249. UpdateTime = loc.LastUpdate ?? now, //DateTime.Now,
  250. ExpiredTime = DateTime.Now.AddHours(1),
  251. Radius = Convert.ToInt32(loc.DeviceStatus),
  252. SentTicks = sentTicks,
  253. Province = loc.Province,
  254. City = loc.City,
  255. District = loc.District
  256. };
  257. // 更新设备的坐标缓存(非LBS IOT)
  258. if (loc.LocationType != (int)LocationType.LBS) //此缓存值用于通过gps或者wifi类型来校对lbs的偏移
  259. {
  260. // 2022/11/15 关于定位信息缓存更新机制的优化说明(实时定位场景):
  261. /** 非LBS 定位缓存
  262. *
  263. //var now = DateTime.Now;
  264. //var positionCache = new DevicePositionStatus.PositionCache
  265. //{
  266. // Address = loc.Address,
  267. // BaiduLat = loc.BaiduLat,
  268. // BaiduLon = loc.BaiduLng,
  269. // CityCode = cityCode,
  270. // GaodeLat = loc.GLat,
  271. // GaodeLon = loc.GLng,
  272. // OriginalLat = loc.OLat,
  273. // OriginalLon = loc.OLng,
  274. // LocationType = loc.LocationType,
  275. // //UpdateTime = loc.LastUpdate ?? DateTime.Now, //DateTime.Now,
  276. // Steps = steps,
  277. // // 2022/11/15 关于定位信息缓存更新机制的优化说明(实时定位场景):
  278. // // 3、有效定位数据定位时间>缓存的定位时间,直接用有效定位数据更新缓存,同时,有效定位数据的“原始有效定位时间”是当前数据的定位时间。
  279. // OriginalTime = loc.LastUpdate ?? now,
  280. // UpdateTime = loc.LastUpdate ?? now, //DateTime.Now,
  281. // ExpiredTime = DateTime.Now.AddHours(1),
  282. // Radius = Convert.ToInt32(loc.DeviceStatus),
  283. // SentTicks = sentTicks,
  284. // Province = loc.Province,
  285. // City = loc.City,
  286. // District = loc.District
  287. //};
  288. */
  289. if (cache != null)
  290. {
  291. positionCache.ExpiredTime = cache.ExpiredTime;
  292. // 5、有效定位数据时间>缓存的“原始有效定位时间”,同时,有效定位数据时间<缓存的的定位时间的情况(就是之前有LBS定位更新了缓存定位时间),
  293. // 则更新缓存的定位数据内容及“原始有效定位时间”,“原始有效定位时间”为当前有效定位的定位时间,定位时间还是不变。
  294. if (
  295. Utils.ConvertToLocalDateTime(sentTicks) > cache.OriginalTime
  296. && Utils.ConvertToLocalDateTime(sentTicks) < cache.UpdateTime
  297. )
  298. {
  299. // “原始有效定位时间”为当前有效定位的定位时间
  300. positionCache.OriginalTime = Utils.ConvertToLocalDateTime(sentTicks);
  301. // 2022/11/23 定位缓存需要增加步数字段实现前端“静止/行走”状态展示
  302. positionCache.Steps = steps;
  303. }
  304. }
  305. // 更新缓存的定位数据内容
  306. positionStatus.LastPosition = positionCache;
  307. positionStatus.RequestPosition = loc.LocationType == 3 ? requestPostionVarObj : null;
  308. _deviceCacheMgr.SetPositionStatusCache(loc.SerialNo, positionStatus);
  309. _logger.LogInformation($"设备{loc.SerialNo},设备上报消息到IOT设备时间:{Utils.ConvertToLocalDateTime(sentTicks)},更新缓存时间:{DateTime.Now}|{JsonConvert.SerializeObject(positionStatus)}|实际定位类型:{loc.LocationType}");
  310. }
  311. //更新设备的坐标缓存(LBS IOT)
  312. else
  313. {
  314. //LBS 定位数据不写入缓存
  315. // 存在非LBS 缓存
  316. if (cache != null)
  317. {
  318. /**
  319. // 2023/04/12 当LBS定位处理优化:
  320. // 就是基于LBS与上次有效定位的距离进行分析,
  321. // 在一个合理的距离则判定LBS有效,
  322. // 也是需更新缓存并显示。
  323. // 这个距离可设置,默认大于2000米
  324. **/
  325. if (false) // 临时开发LBS
  326. {
  327. var distance = GeoUtils.GetDistance2((double)cache.GaodeLon, (double)cache.GaodeLat, (double)positionCache.GaodeLon, (double)positionCache.GaodeLat);
  328. if (distance >= DEFAULT_LBS_ACCEPT_DISTANCE) // 临时开放LBS(false) 说明可能关机下走了很远,此时IOT的定位是LBS,生成的定位缓存也是LBS
  329. {
  330. // 使用最新的IOT LBS数据更新定位缓存
  331. positionStatus.LastPosition = positionCache;
  332. positionStatus.RequestPosition = loc.LocationType == 3 ? requestPostionVarObj : null;
  333. _deviceCacheMgr.SetPositionStatusCache(loc.SerialNo, positionStatus);
  334. _logger.LogInformation($"设备{loc.SerialNo},设备上报消息到IOT设备时间:{Utils.ConvertToLocalDateTime(sentTicks)},更新缓存时间:{DateTime.Now}|缓存内容:{JsonConvert.SerializeObject(positionStatus)}|基于LBS与上次有效定位的距离超过{DEFAULT_LBS_ACCEPT_DISTANCE}米,生成新的LBS定位缓存");
  335. }
  336. else if (cache.LocationType == (int)LocationType.LBS) // 否则在2000米内,缓存中有LBS的,使用新的IOT LBS数据
  337. {
  338. ////LBS定位缓存中原始有效定位与IOT的LBS定位时间差大于1小时更新LBS定位缓存所有数据
  339. //if (Utils.ConvertToLocalDateTime(sentTicks).Subtract((DateTime)cache.OriginalTime).TotalHours > 1)
  340. //{
  341. // positionStatus.LastPosition = positionCache;
  342. // _deviceCacheMgr.SetPositionStatusCache(loc.SerialNo, positionStatus);
  343. // _logger.LogInformation($"设备{loc.SerialNo},设备上报消息到IOT设备时间:{Utils.ConvertToLocalDateTime(sentTicks)},更新缓存时间:{DateTime.Now}|缓存内容:{JsonConvert.SerializeObject(positionStatus)}|LBS定位缓存中原始有效定位与IOT的LBS定位时间差小于1小时更新LBS定位缓存所有数据");
  344. //}
  345. positionStatus.LastPosition = positionCache;
  346. positionStatus.RequestPosition = loc.LocationType == 3 ? requestPostionVarObj : null;
  347. _deviceCacheMgr.SetPositionStatusCache(loc.SerialNo, positionStatus);
  348. _logger.LogInformation($"设备{loc.SerialNo},设备上报消息到IOT设备时间:{Utils.ConvertToLocalDateTime(sentTicks)},更新缓存时间:{DateTime.Now}|缓存内容:{JsonConvert.SerializeObject(positionStatus)}定位缓存类型是LBS且定位缓存经纬度与 IOT LBS 经纬度直线少于2000米,生成新的LBS定位缓存");
  349. }
  350. // 4、LBS定位数据定位时间>缓存的定位时间,则更新缓存的定位时间,定数数据还是原有缓存的定位数据,
  351. //“原始有效定位时间”不变还是原有的缓存数据的时间。
  352. //// LBS定位数据定位时间<缓存的定位时间,直接不处理。
  353. //if (Utils.ConvertToLocalDateTime(sentTicks) > cache.OriginalTime)
  354. // LBS无效定位只更新时间和步数
  355. else
  356. {
  357. // 更新缓存的定位时间
  358. cache.UpdateTime = Utils.ConvertToLocalDateTime(sentTicks);
  359. // 定数数据还是原有缓存的定位数据,“原始有效定位时间”不变还是原有的缓存数据的时间。
  360. positionStatus.LastPosition = cache;
  361. // 2022/11/23 定位缓存需要增加步数字段实现前端“静止/行走”状态展示
  362. positionStatus.LastPosition.Steps = steps;
  363. positionStatus.RequestPosition = loc.LocationType == 3 ? requestPostionVarObj : null;
  364. _deviceCacheMgr.SetPositionStatusCache(loc.SerialNo, positionStatus);
  365. _logger.LogInformation($"设备{loc.SerialNo},设备上报消息到IOT设备时间:{Utils.ConvertToLocalDateTime(sentTicks)},更新缓存时间:{DateTime.Now}|缓存内容:{JsonConvert.SerializeObject(positionStatus)}|无效 IOT LBS经纬度|实际IOT定位类型:{loc.LocationType}");
  366. }
  367. }
  368. else
  369. {
  370. positionStatus.LastPosition = positionCache;
  371. positionStatus.LastPosition.Steps = steps;
  372. positionStatus.RequestPosition = loc.LocationType == 3 ? requestPostionVarObj : null;
  373. _deviceCacheMgr.SetPositionStatusCache(loc.SerialNo, positionStatus);
  374. _logger.LogInformation($"设备{loc.SerialNo},设备上报消息到IOT设备时间:{Utils.ConvertToLocalDateTime(sentTicks)},更新缓存时间:{DateTime.Now}|缓存内容:{JsonConvert.SerializeObject(positionStatus)} 临时开放LBS (覆盖原来有缓存)");
  375. }
  376. }
  377. // 2022/12/2 若是没有缓存,且一开始定位是LBS,还是要生成缓存(LBS数据)
  378. else
  379. {
  380. // 生成缓存(LBS数据)
  381. positionStatus.LastPosition = positionCache;
  382. positionStatus.RequestPosition = loc.LocationType == 3 ? requestPostionVarObj : null;
  383. _deviceCacheMgr.SetPositionStatusCache(loc.SerialNo, positionStatus);
  384. _logger.LogInformation($"设备{loc.SerialNo},设备上报消息到IOT设备时间:{Utils.ConvertToLocalDateTime(sentTicks)},更新缓存时间:{DateTime.Now}|缓存内容:{JsonConvert.SerializeObject(positionStatus)}|设备首次定位且是IOT LBS,生成首次LBS数据的定位缓存");
  385. }
  386. }
  387. }
  388. else
  389. {
  390. result.IsHistoryLocation = true;
  391. }
  392. return result;
  393. }
  394. #endregion
  395. }
  396. }