using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using FMSAdmin.Data; using FMSAdmin.Helpers; using FMSAdmin.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using FMSAdmin.Models; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using System.Net.Http; using System.Xml.Linq; using System.Net; using System.Data; using System.IO; namespace FMSAdmin.Services { public class SiteWeatherInfoService { private readonly ILogger _logger; private readonly AppSettings _appSettings; private readonly FMSContext _context; public SiteWeatherInfoService( ILogger logger, IOptions appSettings, FMSContext context, StorageHelper storage ) { _logger = logger; _appSettings = appSettings.Value; _context = context; } public DataTable WeatherStat(int dayAgo = 0) { var query = $@" SELECT Sido AS [시도], Sigun AS [구군], [00],[01],[02],[03],[04],[05],[06],[07],[08],[09],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[21],[22],[23] FROM ( SELECT weather.Sido, weather.Sigun, weather.CurrentTemp, DATEPART(HOUR, BaseTime) as Hour FROM SiteWeatherInfo weather LEFT JOIN CmSite site ON site.apiSido = weather.Sido AND site.ApiSigun = weather.Sigun WHERE CAST(weather.BaseTime AS DATE) = CAST(DATEADD(DAY, {dayAgo}, GETDATE()) AS DATE) ) T PIVOT ( MAX(CurrentTemp) FOR Hour in ([00],[01],[02],[03],[04],[05],[06],[07],[08],[09],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[21],[22],[23]) ) AS PV "; var conn = _context.Database.GetDbConnection(); conn.Open(); var command = conn.CreateCommand(); command.CommandText = query; var reader = command.ExecuteReader(); DataTable dt = new DataTable(); dt.Load(reader); return dt; } public string ReplaceSido(string sido) { string result = sido.ToString(); result = result.Replace("특별시", "시"); result = result.Replace("광역시", "시"); result = result.Replace("특별자치도", "도"); result = result.Replace("특별자치시", "시"); return result; } public SiteWeatherInfo GetInfo(int siteId, string sido = null, string sigun = null, bool weather = true, bool dust = true) { SiteWeatherInfo weatherInfo = null; var site = _context.CmSite.Where(x => x.SiteId == siteId).FirstOrDefault(); if (site != null) { if (string.IsNullOrEmpty(sido)) { sido = site.ApiSido; } if (string.IsNullOrEmpty(sigun)) { sigun = site.ApiSigun; } } if (string.IsNullOrEmpty(sido)) { sido = "서울시"; } if (string.IsNullOrEmpty(sigun)) { sigun = "용산구"; } sido = this.ReplaceSido(sido); var basetime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, 0, 0); var query = from x in _context.SiteWeatherInfo where x.Sido == sido && x.Sigun == sigun && x.BaseTime == basetime orderby x.BaseTime descending select x; if (query.Count() > 0) { weatherInfo = query.FirstOrDefault(); if (weatherInfo.CurrentTemp != null && weatherInfo.Pm10 != null) { return weatherInfo; } } var isNew = false; if (weatherInfo == null) { weatherInfo = new SiteWeatherInfo { Sido = sido, Sigun = sigun, BaseTime = basetime, }; isNew = true; } try { if (weatherInfo.CurrentTemp == null && weather) { var shortTermNow = _apiShortTermNow(sido, sigun); weatherInfo.ShortTermNowTime = shortTermNow.BaseTime; if (shortTermNow.T1H != null) { weatherInfo.CurrentTemp = Util.ToDouble(shortTermNow.T1H); } if (shortTermNow.RN1 != null) { weatherInfo.Sky = Util.ToInt(shortTermNow.RN1); } if (shortTermNow.REH != null) { weatherInfo.Humidity = Util.ToDouble(shortTermNow.REH); } if (shortTermNow.PTY != null) { weatherInfo.RainType = Util.ToInt(shortTermNow.PTY); } if (shortTermNow.RN1 != null) { weatherInfo.Rain = Util.ToDouble(shortTermNow.RN1); } if (shortTermNow.LGT != null) { weatherInfo.Thunder = Util.ToInt(shortTermNow.LGT); } if (shortTermNow.VEC != null) { weatherInfo.WindAngle = Util.ToInt(shortTermNow.VEC); } if (shortTermNow.WSD != null) { weatherInfo.WindSpeed = Util.ToDouble(shortTermNow.WSD); } } if (weatherInfo.HighTemp == null && weather) { var forecast = _apiForecast(sido, sigun); weatherInfo.ForecastTime = forecast.BaseTime; if (forecast.TMX != null) { weatherInfo.HighTemp = Util.ToDouble(forecast.TMX); } if (forecast.TMN != null) { weatherInfo.LowTemp = Util.ToDouble(forecast.TMN); } } } catch (ApiException ex) { _logger.LogWarning(ex.Message); } catch (Exception ex) { _logger.LogError(ex, ""); } try { if (weatherInfo.Pm10 == null && dust) { var dustHourly = _apiDustHourly(sido, sigun); if (dustHourly != null) { if (dustHourly.DataTime != null) { weatherInfo.DustTime = _parseDate(dustHourly.DataTime); } if (dustHourly.Pm10Value != null) { weatherInfo.Pm10 = Util.ToDouble(dustHourly.Pm10Value); } if (dustHourly.Pm25Value != null) { weatherInfo.Pm25 = Util.ToDouble(dustHourly.Pm25Value); } } } if (weatherInfo.CurrentTemp < -200 || weatherInfo.CurrentTemp > 200) { weatherInfo.CurrentTemp = null; //throw new Exception("잘못된 데이터"); } weatherInfo.IsNew = true; if (isNew) { _context.SiteWeatherInfo.Add(weatherInfo); } _context.SaveChanges(); } catch (ApiException ex) { _logger.LogWarning(ex.Message); } catch (Exception ex) { _logger.LogError(ex, ""); } if (weatherInfo.CurrentTemp == null && weatherInfo.Pm10 == null) { var query2 = from x in _context.SiteWeatherInfo where x.Sido == sido && x.Sigun == sigun && x.BaseTime <= basetime orderby x.BaseTime descending select x; if (query2.Count() > 0) { weatherInfo = query.FirstOrDefault(); return weatherInfo; } } return weatherInfo; } private DateTime? _parseDate(string datestr) { var items = datestr.Split(" "); DateTime? date = null; try { if (items.Length >= 2 && items[1].StartsWith("24")) { date = DateTime.Parse(items[0]); date.Value.AddDays(1); } else { date = DateTime.Parse(datestr); } } catch (Exception ex) { _logger.LogError(ex, ""); } return date; } private DustApiItem _apiDustHourly(string sido, string sigun) { if (sido == null) sido = ""; if (sido == "경상북도") { sido = "경북"; } else if (sido == "경상남도") { sido = "경남"; } else if (sido == "전라북도") { sido = "전북"; } else if (sido == "전라남도") { sido = "전남"; } else if (sido == "충청북도") { sido = "충북"; } else if (sido == "충청남도") { sido = "충남"; } if (sido[sido.Length - 1] == '시') { sido = sido.Substring(0, sido.Length - 1); } if (sido[sido.Length - 1] == '도') { sido = sido.Substring(0, sido.Length - 1); } var sidoName = WebUtility.UrlDecode(sido); //var forecast = $"http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureSidoLIst?serviceKey={_appSettings.DustApiKey}&numOfRows=100&pageNo=1&sidoName={sidoName}&searchCondition=HOUR"; var forecast = $"http://apis.data.go.kr/B552584/ArpltnStatsSvc/getCtprvnMesureSidoLIst?serviceKey={_appSettings.DustApiKey}&numOfRows=100&pageNo=1&sidoName={sidoName}&searchCondition=HOUR"; string content = null; content = pullUrl(forecast); // _logger.LogInformation(content); XElement xml = XElement.Parse(content); var resultCode = xml.Descendants("resultCode").FirstOrDefault()?.Value; var resultMsg = xml.Descendants("resultMsg").FirstOrDefault()?.Value; if (resultCode != "00") { throw new ApiException($"apiDustHourly {resultCode}:{resultMsg}"); } var items = from item in xml.Descendants("item") select new DustApiItem { DataTime = item.Element("dataTime")?.Value, CityName = item.Element("cityName")?.Value, So2Value = item.Element("so2Value")?.Value, CoValue = item.Element("coValue")?.Value, No2Value = item.Element("no2Value")?.Value, Pm10Value = item.Element("pm10Value")?.Value, Pm25Value = item.Element("pm25Value")?.Value, }; var result = items.Where(x => x.CityName == sigun).FirstOrDefault(); if (result == null) { result = items.FirstOrDefault(); } return result; } private WeatherForecast _apiForecast(string sido, string sigun, string gu = null) { var pos = _GetRegionPosition(sido, sigun, gu); var nx = pos.Nx; var ny = pos.Ny; var forenow = DateTime.Now.AddHours(-4); var forehour = forenow.Hour; if (forehour >= 2 && forehour < 5) { forehour = 2; } if (forehour < 2) { forenow = forenow.AddDays(-1); forehour = 23; } else { // 2시부터 23시까지 3시간 간격으로 발표 forehour = (int)Math.Floor((float)(forehour - 2) / 3) * 3 + 2; } forenow = new DateTime(forenow.Year, forenow.Month, forenow.Day, forehour, 0, 0); var targetnow = forenow.AddHours(4); var foredate = forenow.ToString("yyyyMMdd"); var foretime = forenow.ToString("HH00"); var targettime = targetnow.ToString("HH00"); //_logger.LogInformation("foredate:" + foredate); //_logger.LogInformation("foretime:" + foretime); //_logger.LogInformation("targettime:" + targettime); var forecast = $"http://apis.data.go.kr/1360000/VilageFcstInfoService/getVilageFcst?serviceKey={_appSettings.WeatherApiKey}&pageNo=1&numOfRows=100&dataType=XML&base_date={foredate}&base_time={foretime}&nx={nx}&ny={ny}"; //_logger.LogInformation(forecast); string content = null; content = pullUrl(forecast); //_logger.LogInformation(content); XElement xml = XElement.Parse(content); var resultCode = xml.Descendants("resultCode").FirstOrDefault()?.Value; var resultMsg = xml.Descendants("resultMsg").FirstOrDefault()?.Value; if (resultCode != "00") { throw new ApiException($"apiForecast {resultCode}:{resultMsg}"); } var items = from item in xml.Descendants("item") select new WeatherApiItem { BaseDate = item.Element("baseDate")?.Value, BaseTime = item.Element("baseTime")?.Value, Category = item.Element("category")?.Value, FcstValue = item.Element("fcstValue")?.Value, FcstTime = item.Element("fcstTime")?.Value, }; var list = items.ToList(); //_logger.LogInformation(list.Dump()); var result = new WeatherForecast(); result.BaseTime = targetnow; result.ForecastTime = forenow; result.POP = list.Where(x => x.Category == "POP").Select(x => x.FcstValue).FirstOrDefault(); result.PTY = list.Where(x => x.Category == "PTY").Select(x => x.FcstValue).FirstOrDefault(); result.R06 = list.Where(x => x.Category == "R06").Select(x => x.FcstValue).FirstOrDefault(); result.REH = list.Where(x => x.Category == "REH").Select(x => x.FcstValue).FirstOrDefault(); result.S06 = list.Where(x => x.Category == "S06").Select(x => x.FcstValue).FirstOrDefault(); result.SKY = list.Where(x => x.Category == "SKY").Select(x => x.FcstValue).FirstOrDefault(); result.T3H = list.Where(x => x.Category == "T3H").Select(x => x.FcstValue).FirstOrDefault(); result.TMN = list.Where(x => x.Category == "TMN").Select(x => x.FcstValue).FirstOrDefault(); result.TMX = list.Where(x => x.Category == "TMX").Select(x => x.FcstValue).FirstOrDefault(); result.UUU = list.Where(x => x.Category == "UUU").Select(x => x.FcstValue).FirstOrDefault(); result.VVV = list.Where(x => x.Category == "VVV").Select(x => x.FcstValue).FirstOrDefault(); result.VEC = list.Where(x => x.Category == "VEC").Select(x => x.FcstValue).FirstOrDefault(); result.WSD = list.Where(x => x.Category == "WSD").Select(x => x.FcstValue).FirstOrDefault(); return result; } private WeatherShortTermNow _apiShortTermNow(string sido, string sigun, string gu = null) { var pos = _GetRegionPosition(sido, sigun, gu); var nx = pos.Nx; var ny = pos.Ny; var nowminute = DateTime.Now.Minute; var shortnow = DateTime.Now; if (nowminute <= 40) { shortnow = shortnow.AddHours(-1); } shortnow = new DateTime(shortnow.Year, shortnow.Month, shortnow.Day, shortnow.Hour, 0, 0); var nowdate = shortnow.ToString("yyyyMMdd"); var nowtime = shortnow.ToString("HH00"); var shortNow = $"http://apis.data.go.kr/1360000/VilageFcstInfoService/getUltraSrtNcst?serviceKey={_appSettings.WeatherApiKey}&pageNo=1&numOfRows=100&dataType=XML&base_date={nowdate}&base_time={nowtime}&nx={nx}&ny={ny}"; //_logger.LogInformation(shortNow); string content = null; content = pullUrl(shortNow); //_logger.LogInformation(content); XElement xml = XElement.Parse(content); var resultCode = xml.Descendants("resultCode").FirstOrDefault()?.Value; var resultMsg = xml.Descendants("resultMsg").FirstOrDefault()?.Value; if (resultCode != "00") { throw new ApiException($"apiShortTermNow {resultCode}:{resultMsg}"); } var items = from item in xml.Descendants("item") select new WeatherApiItem { BaseDate = item.Element("baseDate")?.Value, BaseTime = item.Element("baseTime")?.Value, Category = item.Element("category")?.Value, ObsrValue = item.Element("obsrValue")?.Value, }; var list = items.ToList(); //_logger.LogInformation(list.Dump()); var result = new WeatherShortTermNow(); result.BaseTime = shortnow; result.T1H = list.Where(x => x.Category == "T1H").Select(x => x.ObsrValue).FirstOrDefault(); result.RN1 = list.Where(x => x.Category == "RN1").Select(x => x.ObsrValue).FirstOrDefault(); result.SKY = list.Where(x => x.Category == "SKY").Select(x => x.ObsrValue).FirstOrDefault(); result.UUU = list.Where(x => x.Category == "UUU").Select(x => x.ObsrValue).FirstOrDefault(); result.VVV = list.Where(x => x.Category == "VVV").Select(x => x.ObsrValue).FirstOrDefault(); result.REH = list.Where(x => x.Category == "REH").Select(x => x.ObsrValue).FirstOrDefault(); result.PTY = list.Where(x => x.Category == "PTY").Select(x => x.ObsrValue).FirstOrDefault(); result.LGT = list.Where(x => x.Category == "LGT").Select(x => x.ObsrValue).FirstOrDefault(); result.VEC = list.Where(x => x.Category == "VEC").Select(x => x.ObsrValue).FirstOrDefault(); result.WSD = list.Where(x => x.Category == "WSD").Select(x => x.ObsrValue).FirstOrDefault(); return result; } private WeatherRegionNxNy _GetRegionPosition(string sido, string sigun, string gu = null) { sido = sido.Trim(); if (sido == "서울시") { sido = "서울특별시"; } else if (sido == "제주도") { sido = "제주특별자치도"; } else if (sido == "인천시") { sido = "인천광역시"; } else if (sido == "광주시") { sido = "광주광역시"; } else if (sido == "대구시") { sido = "대구광역시"; } else if (sido == "대전시") { sido = "대전광역시"; } else if (sido == "부산시") { sido = "부산광역시"; } else if (sido == "울산시") { sido = "울산광역시"; } else if (sido == "세종시") { sido = "세종특별자치시"; } var query = _context.WeatherRegionNxNy.Where(x => x.Sido == sido && x.Sigun == sigun); WeatherRegionNxNy find = null; if (!string.IsNullOrEmpty(gu)) { if (query.Where(x => x.Gu == gu).Count() > 0) { find = query.Where(x => x.Gu == gu).FirstOrDefault(); } else { find = query.FirstOrDefault(); } } else { find = query.FirstOrDefault(); } return find; } private string pullUrl(string url) { // Create a request for the URL. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Timeout = 5000; // If required by the server, set the credentials. request.Credentials = CredentialCache.DefaultCredentials; // Get the response. HttpWebResponse response = (HttpWebResponse)request.GetResponse(); // Display the status. // Get the stream containing content returned by the server. Stream dataStream = response.GetResponseStream(); // Open the stream using a StreamReader for easy access. StreamReader reader = new StreamReader(dataStream); // Read the content. string responseFromServer = reader.ReadToEnd(); // Display the content. reader.Close(); dataStream.Close(); response.Close(); return responseFromServer; } } public class DustApiItem { public string DataTime { get; set; } public string CityName { get; set; } public string So2Value { get; set; } public string CoValue { get; set; } public string O3Value { get; set; } public string No2Value { get; set; } public string Pm10Value { get; set; } public string Pm25Value { get; set; } } public class WeatherApiItem { public string BaseDate { get; set; } public string BaseTime { get; set; } public string Category { get; set; } public string ObsrValue { get; set; } public string FcstValue { get; set; } public string FcstTime { get; set; } } public class WeatherForecast { public WeatherForecast() { } public DateTime BaseTime; public DateTime ForecastTime; [Display(Name = "강수확률")] public string POP { get; set; } [Display(Name = "강수형태")] public string PTY { get; set; } [Display(Name = "6시간 강수량")] public string R06 { get; set; } [Display(Name = "습도")] public string REH { get; set; } [Display(Name = "6시간 신적설")] public string S06 { get; set; } [Display(Name = "하늘상태")] public string SKY { get; set; } [Display(Name = "3시간 기온")] public string T3H { get; set; } [Display(Name = "아침 최저기온")] public string TMN { get; set; } [Display(Name = "낮 최고기온")] public string TMX { get; set; } [Display(Name = "풍속(동서)")] public string UUU { get; set; } [Display(Name = "풍속(남북)")] public string VVV { get; set; } [Display(Name = "풍향")] public string VEC { get; set; } [Display(Name = "풍속")] public string WSD { get; set; } } public class WeatherShortTermNow { public WeatherShortTermNow() { } public DateTime BaseTime; [Display(Name = "기온")] public string T1H { get; set; } [Display(Name = "1시간 강수량")] public string RN1 { get; set; } [Display(Name = "하늘상태")] public string SKY { get; set; } [Display(Name = "동서바람성분")] public string UUU { get; set; } [Display(Name = "남북바람성분")] public string VVV { get; set; } [Display(Name = "습도")] public string REH { get; set; } [Display(Name = "강수형태")] public string PTY { get; set; } [Display(Name = "낙뢰")] public string LGT { get; set; } [Display(Name = "풍향")] public string VEC { get; set; } [Display(Name = "풍속")] public string WSD { get; set; } } }