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.

450 lines
20KB

  1. using GpsCardGatewayPosition.Common.Helper;
  2. using GpsCardGatewayPosition.Model.Cache;
  3. using GpsCardGatewayPosition.Model.Config;
  4. using GpsCardGatewayPosition.Model.Enum;
  5. using GpsCardGatewayPosition.Service.Biz.Iot;
  6. using GpsCardGatewayPosition.Service.Biz.Location.Dto.Gaode;
  7. using GpsCardGatewayPosition.Service.Cache;
  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.Security.Cryptography;
  15. using System.Text;
  16. using System.Text.RegularExpressions;
  17. using System.Threading.Tasks;
  18. namespace GpsCardGatewayPosition.Service.Biz.Location
  19. {
  20. public class GaodeService
  21. {
  22. private const string CACHE_KEY_GAODE = "Gaode_";
  23. private readonly DeviceCacheManager _deviceCacheMgr;
  24. private readonly AppsettingsConfig _configAppSettings;
  25. private readonly GaodeServicesConfig _configGaodeServices;
  26. private readonly HttpHelper _httpHelper;
  27. private readonly DeviceIotOpenService _serviceDeviceIot;
  28. private readonly ILogger<GaodeService> _logger;
  29. private static object _syncLocker = new object();
  30. private MD5 _md5;
  31. private int _idx = 0; //轮询索引
  32. private int _count = 1; //高德服务接口key组数量
  33. private int GaodeDurationSeconds { get; }
  34. public GaodeService(DeviceCacheManager deviceCacheMgr,
  35. IOptions<AppsettingsConfig> optConfigAppSettings, IOptions<GaodeServicesConfig> optConfigGaodeServices,
  36. HttpHelper httpHelper, DeviceIotOpenService serviceDeviceIot, ILogger<GaodeService> logger)
  37. {
  38. _deviceCacheMgr = deviceCacheMgr;
  39. _configAppSettings = optConfigAppSettings.Value;
  40. _httpHelper = httpHelper;
  41. _serviceDeviceIot = serviceDeviceIot;
  42. _logger = logger;
  43. _md5 = MD5.Create();
  44. //高德接口
  45. _configGaodeServices = optConfigGaodeServices.Value;
  46. _configGaodeServices.Items = _configGaodeServices.Items.Where(e => e.EnableConfig == true).ToList();
  47. _count = _configGaodeServices.Items.Count;
  48. GaodeDurationSeconds = _configAppSettings.GaodeDurationSeconds;
  49. }
  50. /// <summary>
  51. /// wifi解析
  52. /// </summary>
  53. /// <param name="serialno"></param>
  54. /// <param name="model"></param>
  55. /// <returns></returns>
  56. public async Task<GetGaodePositionServiceResult> GetGaodeWifiAddressAsync(string serialno, GaodeWifiRequest model)
  57. {
  58. var hash = ComputeHash(model);
  59. var serviceResult = new GetGaodePositionServiceResult
  60. {
  61. HashParam = hash
  62. };
  63. var cacheKey = CACHE_KEY_GAODE + $"{hash}";
  64. var info = await RedisHelper.GetAsync<GaodeWifiResponseInfo>(cacheKey).ConfigureAwait(false);
  65. string result;
  66. try
  67. {
  68. //优先判断查询参数是否有效
  69. if (model.Mmac.Equals("00:00:00:00:00:00,0,"))
  70. {
  71. _logger.LogWarning($"GetGaodeWifiAddress wifi解析失败, 定位参数无效: {JsonConvert.SerializeObject(model)}");
  72. serviceResult.CanRetry = false;
  73. return serviceResult;
  74. }
  75. _idx = (_idx + 1) % _count;
  76. string wifiLbsKey = _configGaodeServices.Items[_idx].GaodeWifiKey; //智能硬件解析接口key:wifi解析,lbs解析
  77. string wifiLbsBaseUrl = _configGaodeServices.Items[_idx].GaodeWifiUrl; //高德wifi,lbs解析地址
  78. if (info == null)
  79. {
  80. //http://apilocate.amap.com/position?accesstype={0}&cdma=0&network=gsm&macs={1}&key={2}
  81. var url = string.Format(wifiLbsBaseUrl, 1, model.Macs, wifiLbsKey);
  82. url += "&mmac=" + model.Mmac;
  83. result = await _httpHelper.HttpToGetAsync(url).ConfigureAwait(false);
  84. if (!string.IsNullOrWhiteSpace(result))
  85. {
  86. info = JsonConvert.DeserializeObject<GaodeWifiResponseInfo>(result);
  87. if (info != null && info.Status + "" == "1") RedisHelper.SetAsync(cacheKey, info, GaodeDurationSeconds);
  88. }
  89. }
  90. else _logger.LogInformation($"{nameof(GetGaodeWifiAddressAsync)}击中缓存 {cacheKey}");
  91. if (info == null || info.Result == null || info.Info + "" != "OK" || info.Status + "" != "1")
  92. {
  93. serviceResult.Flag = false;
  94. }
  95. else
  96. {
  97. if (info.Result.Type + "" == "0")
  98. {
  99. serviceResult.Flag = false;
  100. }
  101. else
  102. {
  103. int radius;
  104. var accuracy = info.Result.Radius + "";
  105. radius = int.TryParse(accuracy, out radius) ? radius : 35;
  106. var loc = info.Result.Location + "";
  107. var poi = info.Result.Poi + "";
  108. var adCode = info.Result.Adcode + "";
  109. var ll = loc.Split(',');
  110. if (ll.Length == 2)
  111. {
  112. var lat = Convert.ToDecimal(ll[1]);
  113. var lng = Convert.ToDecimal(ll[0]);
  114. double gLat, gLng;
  115. GeoConvert.G2Gps((double)lat, (double)lng, out gLat, out gLng);
  116. serviceResult.Accuracy = radius;
  117. serviceResult.AdCode = adCode;
  118. serviceResult.Flag = true;
  119. serviceResult.Lat = (decimal)gLat;
  120. serviceResult.Lon = (decimal)gLng;
  121. serviceResult.Poi = poi;
  122. //地址解析
  123. string province = info.Result.Province;
  124. string city = info.Result.City;
  125. string addr = info.Result.Desc;
  126. string county = null;
  127. var sectors = addr?.Split(" ".ToCharArray()) ?? new string[] { };
  128. if (sectors.Length > 4 && sectors[0] == province && sectors[1] == city)
  129. {
  130. county = sectors[2];
  131. addr = sectors[3] + sectors[4];
  132. }
  133. serviceResult.Province = province;
  134. serviceResult.City = city;
  135. serviceResult.District = county;
  136. serviceResult.Address = addr;
  137. return serviceResult;
  138. }
  139. else
  140. {
  141. serviceResult.Flag = false;
  142. }
  143. }
  144. }
  145. if (!serviceResult.Flag)
  146. {
  147. _logger.LogWarning($"GetGaodeWifiAddress wifi解析失败, 参数: {JsonConvert.SerializeObject(model)}");
  148. }
  149. }
  150. catch (Exception ex)
  151. {
  152. serviceResult.Flag = false;
  153. _logger.LogError($"GetGaodeWifiAddress wifi解析异常, 参数: {JsonConvert.SerializeObject(model)}\n {ex.Message}, {ex.StackTrace}");
  154. }
  155. return serviceResult;
  156. }
  157. /// <summary>
  158. /// lbs解析
  159. /// </summary>
  160. /// <param name="serialno"></param>
  161. /// <param name="model"></param>
  162. /// <returns></returns>
  163. public async Task<GetGaodePositionServiceResult> GetGaodeLbsAddressAsync(string serialno, GaodeLbsRequest model)
  164. {
  165. var hash = ComputeHash(model);
  166. var serviceResult = new GetGaodePositionServiceResult
  167. {
  168. HashParam = hash
  169. };
  170. var cacheKey = CACHE_KEY_GAODE + $"{hash}";
  171. var info = await RedisHelper.GetAsync<GaodeWifiResponseInfo>(cacheKey).ConfigureAwait(false);
  172. string result;
  173. try
  174. {
  175. _idx = (_idx + 1) % _count;
  176. string wifiLbsKey = _configGaodeServices.Items[_idx].GaodeWifiKey; //智能硬件解析接口key:wifi解析,lbs解析
  177. string wifiLbsBaseUrl = _configGaodeServices.Items[_idx].GaodeWifiUrl; //高德wifi,lbs解析地址
  178. if (info == null)
  179. {
  180. //http://apilocate.amap.com/position?accesstype={0}&cdma=0&network=gsm&macs={1}&key={2}
  181. var url = string.Format(wifiLbsBaseUrl, 0, "", wifiLbsKey);
  182. url += "&smac=" + model.Smac;
  183. url += "&imsi=" + model.Imsi;
  184. url += "&bts=" + model.Bts;
  185. if (!string.IsNullOrWhiteSpace(model.NearBts)) url += "&nearbts=" + model.NearBts;
  186. result = await _httpHelper.HttpToGetAsync(url).ConfigureAwait(false);
  187. if (!string.IsNullOrWhiteSpace(result))
  188. {
  189. info = JsonConvert.DeserializeObject<GaodeWifiResponseInfo>(result);
  190. if (info != null && info.Status + "" == "1") RedisHelper.SetAsync(cacheKey, info, GaodeDurationSeconds);
  191. }
  192. }
  193. else _logger.LogInformation($"{nameof(GetGaodeLbsAddressAsync)}击中缓存 {cacheKey}");
  194. if (info == null || info.Result == null || info.Info + "" != "OK" || info.Status + "" != "1")
  195. {
  196. serviceResult.Flag = false;
  197. }
  198. else
  199. {
  200. if (info.Result.Type + "" == "0")
  201. {
  202. serviceResult.Flag = false;
  203. }
  204. else
  205. {
  206. int radius;
  207. var accuracy = info.Result.Radius + "";
  208. radius = int.TryParse(accuracy, out radius) ? radius : 35;
  209. var loc = info.Result.Location + "";
  210. var poi = info.Result.Poi + "";
  211. var adCode = info.Result.Adcode + "";
  212. var ll = loc.Split(',');
  213. if (ll.Length == 2)
  214. {
  215. var lat = Convert.ToDecimal(ll[1]);
  216. var lng = Convert.ToDecimal(ll[0]);
  217. double gLat, gLng;
  218. GeoConvert.G2Gps((double)lat, (double)lng, out gLat, out gLng);
  219. serviceResult.Accuracy = radius;
  220. serviceResult.AdCode = adCode;
  221. serviceResult.Flag = true;
  222. serviceResult.Lat = (decimal)gLat;
  223. serviceResult.Lon = (decimal)gLng;
  224. serviceResult.Poi = poi;
  225. //地址解析
  226. string province = info.Result.Province;
  227. string city = info.Result.City;
  228. string addr = info.Result.Desc;
  229. string county = null;
  230. var sectors = addr?.Split(" ".ToCharArray()) ?? new string[] { };
  231. if (sectors.Length > 4 && sectors[0] == province && sectors[1] == city)
  232. {
  233. county = sectors[2];
  234. addr = sectors[3] + sectors[4];
  235. }
  236. serviceResult.Province = province;
  237. serviceResult.City = city;
  238. serviceResult.District = county;
  239. serviceResult.Address = addr;
  240. return serviceResult;
  241. }
  242. else
  243. {
  244. serviceResult.Flag = false;
  245. }
  246. }
  247. }
  248. if (!serviceResult.Flag)
  249. {
  250. _logger.LogWarning($"GetGaodeLbsAddress lbs解析失败, 参数: {JsonConvert.SerializeObject(model)}");
  251. }
  252. }
  253. catch (Exception ex)
  254. {
  255. serviceResult.Flag = false;
  256. _logger.LogError($"GetGaodeLbsAddress lbs解析异常, 参数: {JsonConvert.SerializeObject(model)}\n {ex.Message}, {ex.StackTrace}");
  257. }
  258. return serviceResult;
  259. }
  260. /// <summary>
  261. /// gps解析
  262. /// </summary>
  263. /// <param name="serialno">设备imei</param>
  264. /// <param name="model">高德经纬度</param>
  265. /// <returns></returns>
  266. public async Task<GetAddressServiceResult> GetGaodeReGeoAddressAsync(string serialno, GaodeGpsRequest model)
  267. {
  268. var hash = ComputeHash(model);
  269. var serviceResult = new GetAddressServiceResult
  270. {
  271. HashParam = hash
  272. };
  273. var cacheKey = CACHE_KEY_GAODE + $"{hash}";
  274. var info = await RedisHelper.GetAsync<GaodeGpsResponseInfo>(cacheKey).ConfigureAwait(false);
  275. string result;
  276. try
  277. {
  278. _idx = (_idx + 1) % _count;
  279. string geoCodeKey = _configGaodeServices.Items[_idx].GaodeRegeoKey; //基础功能解析接口key:gps解析
  280. string geoCodeBaseUrl = _configGaodeServices.Items[_idx].GaodeRegeoUrl; //高德gps解析地址
  281. if (info == null)
  282. {
  283. var url = string.Format(geoCodeBaseUrl, geoCodeKey, model.Longitude, model.Latitude);
  284. result = await _httpHelper.HttpToGetAsync(url).ConfigureAwait(false);
  285. if (!string.IsNullOrWhiteSpace(result))
  286. {
  287. info = JsonConvert.DeserializeObject<GaodeGpsResponseInfo>(result);
  288. if (info != null && info.Status + "" == "1") RedisHelper.SetAsync(cacheKey, info, GaodeDurationSeconds);
  289. }
  290. }
  291. else _logger.LogInformation($"{nameof(GetGaodeReGeoAddressAsync)}击中缓存 {cacheKey}");
  292. if (info == null || info.Regeocode == null || info.Info + "" != "OK" || info.Status + "" != "1")
  293. {
  294. serviceResult.Flag = false;
  295. }
  296. else
  297. {
  298. //广东省广州市海珠区龙凤街道怡雅苑(马涌直街)
  299. var tempAddress = info.Regeocode.FormattedAddress + "";
  300. //龙凤街道
  301. var townShip = info.Regeocode.AddressComponent.Township + "";
  302. //城市编码
  303. var cityCode = info.Regeocode.AddressComponent.Adcode + "";
  304. var city = info.Regeocode.AddressComponent.City + "";
  305. var province = info.Regeocode.AddressComponent.Province + "";
  306. var district = info.Regeocode.AddressComponent.District + "";
  307. if (!string.IsNullOrWhiteSpace(townShip)) //先通过townShip截取地址内容
  308. {
  309. var startIndex = tempAddress.IndexOf(townShip);
  310. tempAddress = tempAddress.Substring(startIndex);
  311. }
  312. var roads = info.Regeocode.Roads;
  313. if (roads != null && roads.Count() > 0)
  314. {
  315. var addresses = roads.Select(e =>
  316. {
  317. var road_name = e.Name + "";
  318. int idx = tempAddress.IndexOf(road_name);
  319. idx = idx >= 0 ? idx + road_name.Length : 0;
  320. return e.Name + tempAddress.Substring(idx);
  321. }).ToList();
  322. foreach (var addr in addresses)
  323. {
  324. if (addr.Length < tempAddress.Length) tempAddress = addr;
  325. }
  326. if (!string.IsNullOrWhiteSpace(townShip)) tempAddress = townShip + tempAddress;
  327. serviceResult.Address = tempAddress;
  328. //如果截取后剩余的字符都是符号(非中文、英文或数字),也当作没数据处理
  329. Regex reg = new Regex(@"^[\u4e00-\u9fa5a-zA-Z0-9]+$");
  330. if (!reg.Match(serviceResult.Address).Success)
  331. {
  332. serviceResult.Address = tempAddress;
  333. }
  334. }
  335. if (string.IsNullOrWhiteSpace(serviceResult.Address)) serviceResult.Address = info.Regeocode.FormattedAddress + "";
  336. serviceResult.Province = province;
  337. serviceResult.City = city;
  338. serviceResult.CityCode = cityCode;
  339. serviceResult.District = district;
  340. serviceResult.Flag = true;
  341. if (string.IsNullOrWhiteSpace(serviceResult.Address)) throw new Exception("无效地址");
  342. return serviceResult;
  343. }
  344. if (!serviceResult.Flag)
  345. {
  346. _logger.LogWarning($"GetGaodeReGeoAddress 逆地理解析失败, 参数: {JsonConvert.SerializeObject(model)}");
  347. }
  348. }
  349. catch (Exception ex)
  350. {
  351. serviceResult.Flag = false;
  352. _logger.LogError($"GetGaodeReGeoAddress 逆地理解析异常, ({model.Longitude},{model.Latitude}) \n{ex.Message}, {ex.StackTrace}");
  353. }
  354. return serviceResult;
  355. }
  356. public async Task SendRealtimeLocationAsync(string imei, DateTime time, RealtimeLocationTypeFlag type = RealtimeLocationTypeFlag.None)
  357. {
  358. var now = DateTime.Now;
  359. //定位信息的时间早于平台当前时间4分钟的,则不进行下发实时定位指令的处理
  360. if (now.Subtract(time).TotalMinutes >= 4) return;
  361. //在30分钟内,只发起一次实时定位给设备
  362. var status = await _deviceCacheMgr.GetPositionStatusCacheAsync(imei);
  363. if (status?.SendGetLocationTime != null && now.Subtract(status.SendGetLocationTime.Value).TotalMinutes < 30) return;
  364. var flags = new List<string>();
  365. if ((type & RealtimeLocationTypeFlag.Gps) == RealtimeLocationTypeFlag.Gps) flags.Add("GPS");
  366. if ((type & RealtimeLocationTypeFlag.Lbs) == RealtimeLocationTypeFlag.Lbs) flags.Add("LBS");
  367. if ((type & RealtimeLocationTypeFlag.Wifi) == RealtimeLocationTypeFlag.Wifi) flags.Add("WIFI");
  368. if (flags.Count == 0) throw new ArgumentException($"请提供有效的实时定位类型{nameof(type)}");
  369. //DeviceOpenApi api = new DeviceOpenApi(_configIot,_serviceGuardMq, _logger);
  370. string args = "{\"command\":\"getLocation\",\"parameter\":\"" + string.Join("|", flags.ToArray()) + "\"}";
  371. var bResult = await _serviceDeviceIot.InvokeThingServiceAsync(imei, "getGeoLocation", args).ConfigureAwait(false);
  372. _logger.LogInformation($"定位解析失败[{imei}],下发立即定位指令,结果: {bResult}");
  373. if (status == null) status = new DevicePositionStatus();
  374. status.SendGetLocationTime = DateTime.Now;
  375. _deviceCacheMgr.SetPositionStatusCache(imei, status);
  376. }
  377. private string ComputeHash(object data)
  378. {
  379. lock (_syncLocker)
  380. {
  381. var value = JsonConvert.SerializeObject(data);
  382. value = Convert.ToBase64String(_md5.ComputeHash(Encoding.UTF8.GetBytes(value)));
  383. return value;
  384. }
  385. }
  386. }
  387. }