using GpsCardGatewayPosition.Common.Helper; using GpsCardGatewayPosition.Model.Cache; using GpsCardGatewayPosition.Model.Config; using GpsCardGatewayPosition.Model.Enum; using GpsCardGatewayPosition.Service.Biz.Iot; using GpsCardGatewayPosition.Service.Biz.Location.Dto.Gaode; using GpsCardGatewayPosition.Service.Cache; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace GpsCardGatewayPosition.Service.Biz.Location { public class GaodeService { private const string CACHE_KEY_GAODE = "Gaode_"; private readonly DeviceCacheManager _deviceCacheMgr; private readonly AppsettingsConfig _configAppSettings; private readonly GaodeServicesConfig _configGaodeServices; private readonly HttpHelper _httpHelper; private readonly DeviceIotOpenService _serviceDeviceIot; private readonly ILogger _logger; private static object _syncLocker = new object(); private MD5 _md5; private int _idx = 0; //轮询索引 private int _count = 1; //高德服务接口key组数量 private int GaodeDurationSeconds { get; } public GaodeService(DeviceCacheManager deviceCacheMgr, IOptions optConfigAppSettings, IOptions optConfigGaodeServices, HttpHelper httpHelper, DeviceIotOpenService serviceDeviceIot, ILogger logger) { _deviceCacheMgr = deviceCacheMgr; _configAppSettings = optConfigAppSettings.Value; _httpHelper = httpHelper; _serviceDeviceIot = serviceDeviceIot; _logger = logger; _md5 = MD5.Create(); //高德接口 _configGaodeServices = optConfigGaodeServices.Value; _configGaodeServices.Items = _configGaodeServices.Items.Where(e => e.EnableConfig == true).ToList(); _count = _configGaodeServices.Items.Count; GaodeDurationSeconds = _configAppSettings.GaodeDurationSeconds; } /// /// wifi解析 /// /// /// /// public async Task GetGaodeWifiAddressAsync(string serialno, GaodeWifiRequest model) { var hash = ComputeHash(model); var serviceResult = new GetGaodePositionServiceResult { HashParam = hash }; var cacheKey = CACHE_KEY_GAODE + $"{hash}"; var info = await RedisHelper.GetAsync(cacheKey).ConfigureAwait(false); string result; try { //优先判断查询参数是否有效 if (model.Mmac.Equals("00:00:00:00:00:00,0,")) { _logger.LogWarning($"GetGaodeWifiAddress wifi解析失败, 定位参数无效: {JsonConvert.SerializeObject(model)}"); serviceResult.CanRetry = false; return serviceResult; } _idx = (_idx + 1) % _count; string wifiLbsKey = _configGaodeServices.Items[_idx].GaodeWifiKey; //智能硬件解析接口key:wifi解析,lbs解析 string wifiLbsBaseUrl = _configGaodeServices.Items[_idx].GaodeWifiUrl; //高德wifi,lbs解析地址 if (info == null) { //http://apilocate.amap.com/position?accesstype={0}&cdma=0&network=gsm&macs={1}&key={2} var url = string.Format(wifiLbsBaseUrl, 1, model.Macs, wifiLbsKey); url += "&mmac=" + model.Mmac; result = await _httpHelper.HttpToGetAsync(url).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result)) { info = JsonConvert.DeserializeObject(result); if (info != null && info.Status + "" == "1") RedisHelper.SetAsync(cacheKey, info, GaodeDurationSeconds); } } else _logger.LogInformation($"{nameof(GetGaodeWifiAddressAsync)}击中缓存 {cacheKey}"); if (info == null || info.Result == null || info.Info + "" != "OK" || info.Status + "" != "1") { serviceResult.Flag = false; } else { if (info.Result.Type + "" == "0") { serviceResult.Flag = false; } else { int radius; var accuracy = info.Result.Radius + ""; radius = int.TryParse(accuracy, out radius) ? radius : 35; var loc = info.Result.Location + ""; var poi = info.Result.Poi + ""; var adCode = info.Result.Adcode + ""; var ll = loc.Split(','); if (ll.Length == 2) { var lat = Convert.ToDecimal(ll[1]); var lng = Convert.ToDecimal(ll[0]); double gLat, gLng; GeoConvert.G2Gps((double)lat, (double)lng, out gLat, out gLng); serviceResult.Accuracy = radius; serviceResult.AdCode = adCode; serviceResult.Flag = true; serviceResult.Lat = (decimal)gLat; serviceResult.Lon = (decimal)gLng; serviceResult.Poi = poi; //地址解析 string province = info.Result.Province; string city = info.Result.City; string addr = info.Result.Desc; string county = null; var sectors = addr?.Split(" ".ToCharArray()) ?? new string[] { }; if (sectors.Length > 4 && sectors[0] == province && sectors[1] == city) { county = sectors[2]; addr = sectors[3] + sectors[4]; } serviceResult.Province = province; serviceResult.City = city; serviceResult.District = county; serviceResult.Address = addr; return serviceResult; } else { serviceResult.Flag = false; } } } if (!serviceResult.Flag) { _logger.LogWarning($"GetGaodeWifiAddress wifi解析失败, 参数: {JsonConvert.SerializeObject(model)}"); } } catch (Exception ex) { serviceResult.Flag = false; _logger.LogError($"GetGaodeWifiAddress wifi解析异常, 参数: {JsonConvert.SerializeObject(model)}\n {ex.Message}, {ex.StackTrace}"); } return serviceResult; } /// /// lbs解析 /// /// /// /// public async Task GetGaodeLbsAddressAsync(string serialno, GaodeLbsRequest model) { var hash = ComputeHash(model); var serviceResult = new GetGaodePositionServiceResult { HashParam = hash }; var cacheKey = CACHE_KEY_GAODE + $"{hash}"; var info = await RedisHelper.GetAsync(cacheKey).ConfigureAwait(false); string result; try { _idx = (_idx + 1) % _count; string wifiLbsKey = _configGaodeServices.Items[_idx].GaodeWifiKey; //智能硬件解析接口key:wifi解析,lbs解析 string wifiLbsBaseUrl = _configGaodeServices.Items[_idx].GaodeWifiUrl; //高德wifi,lbs解析地址 if (info == null) { //http://apilocate.amap.com/position?accesstype={0}&cdma=0&network=gsm&macs={1}&key={2} var url = string.Format(wifiLbsBaseUrl, 0, "", wifiLbsKey); url += "&smac=" + model.Smac; url += "&imsi=" + model.Imsi; url += "&bts=" + model.Bts; if (!string.IsNullOrWhiteSpace(model.NearBts)) url += "&nearbts=" + model.NearBts; result = await _httpHelper.HttpToGetAsync(url).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result)) { info = JsonConvert.DeserializeObject(result); if (info != null && info.Status + "" == "1") RedisHelper.SetAsync(cacheKey, info, GaodeDurationSeconds); } } else _logger.LogInformation($"{nameof(GetGaodeLbsAddressAsync)}击中缓存 {cacheKey}"); if (info == null || info.Result == null || info.Info + "" != "OK" || info.Status + "" != "1") { serviceResult.Flag = false; } else { if (info.Result.Type + "" == "0") { serviceResult.Flag = false; } else { int radius; var accuracy = info.Result.Radius + ""; radius = int.TryParse(accuracy, out radius) ? radius : 35; var loc = info.Result.Location + ""; var poi = info.Result.Poi + ""; var adCode = info.Result.Adcode + ""; var ll = loc.Split(','); if (ll.Length == 2) { var lat = Convert.ToDecimal(ll[1]); var lng = Convert.ToDecimal(ll[0]); double gLat, gLng; GeoConvert.G2Gps((double)lat, (double)lng, out gLat, out gLng); serviceResult.Accuracy = radius; serviceResult.AdCode = adCode; serviceResult.Flag = true; serviceResult.Lat = (decimal)gLat; serviceResult.Lon = (decimal)gLng; serviceResult.Poi = poi; //地址解析 string province = info.Result.Province; string city = info.Result.City; string addr = info.Result.Desc; string county = null; var sectors = addr?.Split(" ".ToCharArray()) ?? new string[] { }; if (sectors.Length > 4 && sectors[0] == province && sectors[1] == city) { county = sectors[2]; addr = sectors[3] + sectors[4]; } serviceResult.Province = province; serviceResult.City = city; serviceResult.District = county; serviceResult.Address = addr; return serviceResult; } else { serviceResult.Flag = false; } } } if (!serviceResult.Flag) { _logger.LogWarning($"GetGaodeLbsAddress lbs解析失败, 参数: {JsonConvert.SerializeObject(model)}"); } } catch (Exception ex) { serviceResult.Flag = false; _logger.LogError($"GetGaodeLbsAddress lbs解析异常, 参数: {JsonConvert.SerializeObject(model)}\n {ex.Message}, {ex.StackTrace}"); } return serviceResult; } /// /// gps解析 /// /// 设备imei /// 高德经纬度 /// public async Task GetGaodeReGeoAddressAsync(string serialno, GaodeGpsRequest model) { var hash = ComputeHash(model); var serviceResult = new GetAddressServiceResult { HashParam = hash }; var cacheKey = CACHE_KEY_GAODE + $"{hash}"; var info = await RedisHelper.GetAsync(cacheKey).ConfigureAwait(false); string result; try { _idx = (_idx + 1) % _count; string geoCodeKey = _configGaodeServices.Items[_idx].GaodeRegeoKey; //基础功能解析接口key:gps解析 string geoCodeBaseUrl = _configGaodeServices.Items[_idx].GaodeRegeoUrl; //高德gps解析地址 if (info == null) { var url = string.Format(geoCodeBaseUrl, geoCodeKey, model.Longitude, model.Latitude); result = await _httpHelper.HttpToGetAsync(url).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(result)) { info = JsonConvert.DeserializeObject(result); if (info != null && info.Status + "" == "1") RedisHelper.SetAsync(cacheKey, info, GaodeDurationSeconds); } } else _logger.LogInformation($"{nameof(GetGaodeReGeoAddressAsync)}击中缓存 {cacheKey}"); if (info == null || info.Regeocode == null || info.Info + "" != "OK" || info.Status + "" != "1") { serviceResult.Flag = false; } else { //广东省广州市海珠区龙凤街道怡雅苑(马涌直街) var tempAddress = info.Regeocode.FormattedAddress + ""; //龙凤街道 var townShip = info.Regeocode.AddressComponent.Township + ""; //城市编码 var cityCode = info.Regeocode.AddressComponent.Adcode + ""; var city = info.Regeocode.AddressComponent.City + ""; var province = info.Regeocode.AddressComponent.Province + ""; var district = info.Regeocode.AddressComponent.District + ""; if (!string.IsNullOrWhiteSpace(townShip)) //先通过townShip截取地址内容 { var startIndex = tempAddress.IndexOf(townShip); tempAddress = tempAddress.Substring(startIndex); } var roads = info.Regeocode.Roads; if (roads != null && roads.Count() > 0) { var addresses = roads.Select(e => { var road_name = e.Name + ""; int idx = tempAddress.IndexOf(road_name); idx = idx >= 0 ? idx + road_name.Length : 0; return e.Name + tempAddress.Substring(idx); }).ToList(); foreach (var addr in addresses) { if (addr.Length < tempAddress.Length) tempAddress = addr; } if (!string.IsNullOrWhiteSpace(townShip)) tempAddress = townShip + tempAddress; serviceResult.Address = tempAddress; //如果截取后剩余的字符都是符号(非中文、英文或数字),也当作没数据处理 Regex reg = new Regex(@"^[\u4e00-\u9fa5a-zA-Z0-9]+$"); if (!reg.Match(serviceResult.Address).Success) { serviceResult.Address = tempAddress; } } if (string.IsNullOrWhiteSpace(serviceResult.Address)) serviceResult.Address = info.Regeocode.FormattedAddress + ""; serviceResult.Province = province; serviceResult.City = city; serviceResult.CityCode = cityCode; serviceResult.District = district; serviceResult.Flag = true; if (string.IsNullOrWhiteSpace(serviceResult.Address)) throw new Exception("无效地址"); return serviceResult; } if (!serviceResult.Flag) { _logger.LogWarning($"GetGaodeReGeoAddress 逆地理解析失败, 参数: {JsonConvert.SerializeObject(model)}"); } } catch (Exception ex) { serviceResult.Flag = false; _logger.LogError($"GetGaodeReGeoAddress 逆地理解析异常, ({model.Longitude},{model.Latitude}) \n{ex.Message}, {ex.StackTrace}"); } return serviceResult; } public async Task SendRealtimeLocationAsync(string imei, DateTime time, RealtimeLocationTypeFlag type = RealtimeLocationTypeFlag.None) { var now = DateTime.Now; //定位信息的时间早于平台当前时间4分钟的,则不进行下发实时定位指令的处理 if (now.Subtract(time).TotalMinutes >= 4) return; //在30分钟内,只发起一次实时定位给设备 var status = await _deviceCacheMgr.GetPositionStatusCacheAsync(imei); if (status?.SendGetLocationTime != null && now.Subtract(status.SendGetLocationTime.Value).TotalMinutes < 30) return; var flags = new List(); if ((type & RealtimeLocationTypeFlag.Gps) == RealtimeLocationTypeFlag.Gps) flags.Add("GPS"); if ((type & RealtimeLocationTypeFlag.Lbs) == RealtimeLocationTypeFlag.Lbs) flags.Add("LBS"); if ((type & RealtimeLocationTypeFlag.Wifi) == RealtimeLocationTypeFlag.Wifi) flags.Add("WIFI"); if (flags.Count == 0) throw new ArgumentException($"请提供有效的实时定位类型{nameof(type)}"); //DeviceOpenApi api = new DeviceOpenApi(_configIot,_serviceGuardMq, _logger); string args = "{\"command\":\"getLocation\",\"parameter\":\"" + string.Join("|", flags.ToArray()) + "\"}"; var bResult = await _serviceDeviceIot.InvokeThingServiceAsync(imei, "getGeoLocation", args).ConfigureAwait(false); _logger.LogInformation($"定位解析失败[{imei}],下发立即定位指令,结果: {bResult}"); if (status == null) status = new DevicePositionStatus(); status.SendGetLocationTime = DateTime.Now; _deviceCacheMgr.SetPositionStatusCache(imei, status); } private string ComputeHash(object data) { lock (_syncLocker) { var value = JsonConvert.SerializeObject(data); value = Convert.ToBase64String(_md5.ComputeHash(Encoding.UTF8.GetBytes(value))); return value; } } } }