using FrontendWorkerService.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Quartz; using Repository.FrontendRepository.Interface; using Repository.Models; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Xml; namespace FrontendWorkerService.Quartz.Jobs { /// /// 透過obix API向Niagara取得所有設備的最新的即時資料 /// [DisallowConcurrentExecution] class OnTimeDeviceRawDataJob : IJob { private readonly ILogger logger; private readonly IFrontendRepository frontendRepository; public OnTimeDeviceRawDataJob( ILogger logger, IFrontendRepository frontendRepository) { this.logger = logger; this.frontendRepository = frontendRepository; } public async Task Execute(IJobExecutionContext context) { try { logger.LogInformation("【OnTimeDeviceRawDataJob】【任務開始】"); EDFunction ed = new EDFunction(); var obixApiConfig = new ObixApiConfig(); var sqlObix = $@"SELECT system_value as Value, system_key as Name FROM variable WHERE deleted = 0 AND system_type = 'obixConfig'"; var variable = await frontendRepository.GetAllAsync(sqlObix); obixApiConfig.ApiBase = variable.Where(x => x.Name == "ApiBase").Select(x => x.Value).FirstOrDefault(); obixApiConfig.UserName = ed.AESDecrypt(variable.Where(x => x.Name == "UserName").Select(x => x.Value).FirstOrDefault()); obixApiConfig.Password = ed.AESDecrypt(variable.Where(x => x.Name == "Password").Select(x => x.Value).FirstOrDefault()); String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(obixApiConfig.UserName + ":" + obixApiConfig.Password)); var sWhere = @$"deleted = 0 AND system_type = @system_type AND system_key = @system_key"; var watch = await frontendRepository.GetOneAsync("variable", sWhere, new { system_type = "obixConfig", system_key = "watch_id" }); HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/watch{watch.system_value}/pollChanges/"); request.Method = "POST"; request.Headers.Add("Authorization", "Basic " + encoded); request.PreAuthenticate = true; //Stopwatch stopWatch = new Stopwatch(); //stopWatch.Start(); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd(); //stopWatch.Stop(); //logger.LogInformation("【OnTimeDeviceRawDataJob】【效能檢驗】[取得資料花費時間]{0} 毫秒", stopWatch.ElapsedMilliseconds); //stopWatch.Reset(); //stopWatch.Start(); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.LoadXml(responseContent); string json = JsonConvert.SerializeXmlNode(xmlDocument); JObject jsonResult = (JObject)JsonConvert.DeserializeObject(json); if (jsonResult.ContainsKey("err")) //抓取錯誤 { logger.LogError("【OnTimeDeviceRawDataJob】【API回傳錯誤資訊】"); logger.LogError("【OnTimeDeviceRawDataJob】【API回傳錯誤資訊】[錯誤內容]:{0}", json); #region 刪除舊有watch service logger.LogInformation("【OnTimeDeviceRawDataJob】【開始刪除該watch service】[編號:{0}]", watch.system_value); HttpWebRequest deleteWatchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/watch{watch.system_value}/delete/"); deleteWatchServiceRequest.Method = "POST"; deleteWatchServiceRequest.Headers.Add("Authorization", "Basic " + encoded); deleteWatchServiceRequest.PreAuthenticate = true; HttpWebResponse deleteWatchServiceResponse = (HttpWebResponse)deleteWatchServiceRequest.GetResponse(); var deleteWatchServiceResponseContent = new StreamReader(deleteWatchServiceResponse.GetResponseStream()).ReadToEnd(); xmlDocument.LoadXml(deleteWatchServiceResponseContent); string deleteWatchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument); JObject deleteWatchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(deleteWatchServiceJson); if (deleteWatchServiceJsonResult.ContainsKey("err")) //抓取錯誤 { logger.LogError("【OnTimeDeviceRawDataJob】【刪除舊有watch service失敗】[編號:{0}]"); logger.LogError("【OnTimeDeviceRawDataJob】【刪除舊有watch service失敗】[錯誤內容]:{0}", deleteWatchServiceJson); } else if (deleteWatchServiceJsonResult.ContainsKey("obj")) { var deleteResult = deleteWatchServiceJsonResult["obj"]["@null"].ToString(); if (deleteResult == "true") { logger.LogInformation("【OnTimeDeviceRawDataJob】【刪除舊有watch service成功】[編號:{0}]", watch.system_value); } else { logger.LogError("【OnTimeDeviceRawDataJob】【刪除舊有watch service失敗】[編號:{0}]", watch.system_value); logger.LogError("【OnTimeDeviceRawDataJob】【刪除舊有watch service失敗】[錯誤內容]:{0}", deleteWatchServiceJson); } } else //未知錯誤 { logger.LogError("【OnTimeDeviceRawDataJob】【刪除舊有watch service未知錯誤】[編號:{0}]", watch.system_value); logger.LogError("【OnTimeDeviceRawDataJob】【刪除舊有watch service未知錯誤】[錯誤內容]:{0}", deleteWatchServiceJson); } #endregion #region 重新建立watch service logger.LogInformation("【OnTimeDeviceRawDataJob】【開始重新建立watch service】"); string watch_id = string.Empty; //紀錄watch id的編號 //重新建立watch service HttpWebRequest makeWatchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/make/"); makeWatchServiceRequest.Method = "POST"; makeWatchServiceRequest.Headers.Add("Authorization", "Basic " + encoded); makeWatchServiceRequest.PreAuthenticate = true; HttpWebResponse makeWatchServiceResponse = (HttpWebResponse)makeWatchServiceRequest.GetResponse(); var makeWatchServiceResponseContent = new StreamReader(makeWatchServiceResponse.GetResponseStream()).ReadToEnd(); xmlDocument.LoadXml(makeWatchServiceResponseContent); string makeWatchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument); JObject makeWatchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(makeWatchServiceJson); if (makeWatchServiceJsonResult.ContainsKey("err")) //抓取錯誤 { logger.LogError("【OnTimeDeviceRawDataJob】【重新建立watch service失敗】"); logger.LogError("【OnTimeDeviceRawDataJob】【重新建立watch service失敗】[錯誤內容]:{0}", makeWatchServiceJson); return; } if (makeWatchServiceJsonResult.ContainsKey("obj")) //表示可以讀取到內容 { var makeSplit = makeWatchServiceJsonResult["obj"]["@href"].ToString().Split("/"); watch_id = makeSplit[makeSplit.Length - 2].Replace("watch", ""); } if (!string.IsNullOrEmpty(watch_id)) { logger.LogInformation("【OnTimeDeviceRawDataJob】【重新建立watch service的編號】[編號:{0}]", watch_id); //儲存至variable Dictionary updateWatchId = new Dictionary() { { "@system_value", watch_id} }; await frontendRepository.UpdateOneByCustomTable(updateWatchId, "variable", "system_type = 'obixConfig' AND system_key = 'watch_id'"); //修改watch service 存活時間 HttpWebRequest changeWatchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/watch{watch_id}/lease/"); changeWatchServiceRequest.Method = "PUT"; changeWatchServiceRequest.Headers.Add("Authorization", "Basic " + encoded); changeWatchServiceRequest.PreAuthenticate = true; var realTime = $@""; byte[] realTimeByteArray = Encoding.UTF8.GetBytes(realTime); using (Stream reqStream = changeWatchServiceRequest.GetRequestStream()) { reqStream.Write(realTimeByteArray, 0, realTimeByteArray.Length); } HttpWebResponse changeWatchServiceResponse = (HttpWebResponse)changeWatchServiceRequest.GetResponse(); var changeWatchServiceResponseContent = new StreamReader(changeWatchServiceResponse.GetResponseStream()).ReadToEnd(); xmlDocument.LoadXml(changeWatchServiceResponseContent); string changeWatchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument); JObject changeWatchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(changeWatchServiceJson); if (changeWatchServiceJsonResult.ContainsKey("err")) //抓取錯誤 { logger.LogError("【OnTimeDeviceRawDataJob】【修改watch service存活時間失敗】"); logger.LogError("【OnTimeDeviceRawDataJob】【修改watch service存活時間失敗】[錯誤內容]:{0}", changeWatchServiceJsonResult); return; } } #endregion #region 設定要訂閱的設備點位 var deviceSubscrips = await frontendRepository.GetAllAsync("ontime_device_subscription", string.Empty); var subscripUriFormat = @" "; List subscripStrings = new List(); foreach (var deviceSubscrip in deviceSubscrips) { List devicePaths = new List(); //拆解設備編號來組出路徑 var deviceSubscripSplit = deviceSubscrip.device_number.Split("_"); foreach (var temp in deviceSubscripSplit) { if (temp[0].ToString().All(char.IsDigit)) { devicePaths.Add($"$3{temp}"); } else { devicePaths.Add(temp); } } //剔除最後一個,因為是設備名稱 devicePaths.RemoveAt(devicePaths.Count() - 1); //查詢是否有做為狀態設定(device_kind) var sql_device_kind = $@"SELECT dk.* FROM device d JOIN device_kind dk ON d.device_building_tag = dk.device_building_tag AND d.device_system_tag = dk.device_system_tag AND d.device_name_tag = dk.device_name_tag WHERE d.deleted = 0 AND d.device_number = @device_number"; var deviceKind = await frontendRepository.GetOneAsync(sql_device_kind, new { device_number = deviceSubscrip.device_number }); if (deviceKind != null) { //表示該設備有特別設定點位 if ((deviceSubscrip.name == deviceKind.Device_normal_point_name || deviceSubscrip.name == deviceKind.Device_close_point_name || deviceSubscrip.name == deviceKind.Device_error_point_name) && deviceSubscrip.name != "ER") { subscripStrings.Add(string.Format(subscripUriFormat, string.Join('/', devicePaths), deviceSubscrip.device_number, deviceSubscrip.name, "facets")); subscripStrings.Add(string.Format(subscripUriFormat, string.Join('/', devicePaths), deviceSubscrip.device_number, deviceSubscrip.name, "out")); } else { //並未符合指定的點位 subscripStrings.Add(string.Format(subscripUriFormat, string.Join('/', devicePaths), deviceSubscrip.device_number, deviceSubscrip.name, "out")); } } else { subscripStrings.Add(string.Format(subscripUriFormat, string.Join('/', devicePaths), deviceSubscrip.device_number, deviceSubscrip.name, "out")); } } #endregion 設定要訂閱的設備點位 #region 訂閱設備點位 HttpWebRequest addWatchServiceRequest = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/watchService/watch{watch_id}/add/"); addWatchServiceRequest.Method = "POST"; addWatchServiceRequest.Headers.Add("Authorization", "Basic " + encoded); addWatchServiceRequest.PreAuthenticate = true; var subscripFullString = $@" {string.Join(null, subscripStrings)} "; byte[] byteArray = Encoding.UTF8.GetBytes(subscripFullString); using (Stream reqStream = addWatchServiceRequest.GetRequestStream()) { reqStream.Write(byteArray, 0, byteArray.Length); } HttpWebResponse addWatchServiceResponse = (HttpWebResponse)addWatchServiceRequest.GetResponse(); var addWatchServiceResponseContent = new StreamReader(addWatchServiceResponse.GetResponseStream()).ReadToEnd(); xmlDocument.LoadXml(addWatchServiceResponseContent); string addWatchServiceJson = JsonConvert.SerializeXmlNode(xmlDocument); JObject addWatchServiceJsonResult = (JObject)JsonConvert.DeserializeObject(addWatchServiceJson); if (addWatchServiceJsonResult.ContainsKey("err")) //抓取錯誤 { logger.LogError("【OnTimeDeviceRawDataJob】【設備點位訂閱失敗】"); logger.LogError("【OnTimeDeviceRawDataJob】【設備點位訂閱失敗】[錯誤內容]:{0}", addWatchServiceJsonResult); return; } else { logger.LogInformation("【OnTimeDeviceRawDataJob】【設備點位訂閱成功】"); } #endregion 訂閱設備點位 } else { if (jsonResult.ContainsKey("obj")) //表示可以讀取到內容 { List> ontimeRawDatas = new List>(); var ontimeList = jsonResult["obj"]["list"]; if (ontimeList["real"] != null && ontimeList["real"].HasValues) { //讀取數值型 //判斷是否為多筆數值資料 var temp_real_count = 0; //用來計算總共有幾個name foreach (var realVal in ontimeList["real"].Children()) { var tempKeyValue = realVal.ToString(); if (tempKeyValue.Contains("@displayName")) { temp_real_count++; } } if(temp_real_count > 1) { //多筆情況 foreach (var real in ontimeList["real"]) { var valueSplit = real["@display"].ToString().Split(); var value = valueSplit[0]; var href = real["@href"].ToString(); var hrefSplit = href.Split("/"); var findOutIndex = 0; var outIndex = 0; foreach (var tempHref in hrefSplit) //找出out的Index { if (tempHref == "out") { outIndex = findOutIndex; } findOutIndex++; } var device_number = hrefSplit[outIndex - 2]; //透過outIndex找出設備編號 var name = hrefSplit[outIndex - 1];//透過outIndex找出點位 Dictionary ontimeRawData = new Dictionary() { { "device_number", device_number}, { "name", name}, { "@value", value}, { "@is_bool", 0}, }; ontimeRawDatas.Add(ontimeRawData); } } else { //單筆情況 var valueSplit = ontimeList["real"]["@display"].ToString().Split(); var value = valueSplit[0]; var href = ontimeList["real"]["@href"].ToString(); var hrefSplit = href.Split("/"); var findOutIndex = 0; var outIndex = 0; foreach (var tempHref in hrefSplit) //找出out的Index { if (tempHref == "out") { outIndex = findOutIndex; } findOutIndex++; } var device_number = hrefSplit[outIndex - 2]; //透過outIndex找出設備編號 var name = hrefSplit[outIndex - 1];//透過outIndex找出點位 Dictionary ontimeRawData = new Dictionary() { { "device_number", device_number}, { "name", name}, { "@value", value}, { "@is_bool", 0}, }; ontimeRawDatas.Add(ontimeRawData); } } if (ontimeList["bool"] != null && ontimeList["bool"].HasValues) { //讀取布林型 //判斷是否為多筆bool資料 var temp_bool_count = 0; //用來計算總共有幾個name foreach (var boolVal in ontimeList["bool"].Children()) { var tempKeyValue = boolVal.ToString(); if (tempKeyValue.Contains("@displayName")) { temp_bool_count++; } } if (temp_bool_count > 1) { //多筆情況 foreach (var real in ontimeList["bool"]) { var valueSplit = real["@display"].ToString().Split(); var value = valueSplit[0]; var href = real["@href"].ToString(); var hrefSplit = href.Split("/"); var findOutIndex = 0; var outIndex = 0; foreach (var tempHref in hrefSplit) //找出out的Index { if (tempHref == "out") { outIndex = findOutIndex; } findOutIndex++; } var device_number = hrefSplit[outIndex - 2]; //透過outIndex找出設備編號 var name = hrefSplit[outIndex - 1];//透過outIndex找出點位 string finalFacetsValue = null; //判斷是否有facets if (ontimeList["str"] != null && ontimeList["str"].HasValues) { var has_find = false; foreach (var facet in ontimeList["str"]) { var facet_href = facet["@href"].ToString(); var facet_href_split = facet_href.Split("/"); if (facet_href.Contains(device_number) && facet_href_split[facet_href_split.Length - 3] == name) { //表示有找到 finalFacetsValue = facet["@display"].ToString(); var facetValueSplit = facet["@display"].ToString().Split(","); foreach (var facetValue in facetValueSplit) { if (facetValue.Contains(value)) { var tempTextValue = facetValue.Split("="); value = tempTextValue[0]; has_find = true; break; } } if (has_find) { break; } } } } else { //找出是否有存在facets,如果有則須多加判斷 var sql = $@"SELECT facets FROM ontime_device_rawdata WHERE device_number = @device_number AND name = @name"; var facets = await frontendRepository.GetOneAsync(sql, new { device_number = device_number, name = name }); finalFacetsValue = facets; if (!string.IsNullOrEmpty(facets)) { var facetValueSplit = facets.Split(","); foreach (var facetValue in facetValueSplit) { if (facetValue.Contains(value)) { var tempTextValue = facetValue.Split("="); value = tempTextValue[0]; break; } } } } Dictionary ontimeRawData = new Dictionary() { { "device_number", device_number}, { "name", name}, { "@value", value}, { "@is_bool", 1}, { "@facets", finalFacetsValue}, }; ontimeRawDatas.Add(ontimeRawData); } } else { //單筆情況 var valueSplit = ontimeList["bool"]["@display"].ToString().Split(); var value = valueSplit[0]; var href = ontimeList["bool"]["@href"].ToString(); var hrefSplit = href.Split("/"); var findOutIndex = 0; var outIndex = 0; foreach (var tempHref in hrefSplit) //找出out的Index { if (tempHref == "out") { outIndex = findOutIndex; } findOutIndex++; } var device_number = hrefSplit[outIndex - 2]; //透過outIndex找出設備編號 var name = hrefSplit[outIndex - 1];//透過outIndex找出點位 string finalFacetsValue = null; //判斷是否有facets if (ontimeList["str"] != null && ontimeList["str"].HasValues) { var has_find = false; foreach (var facet in ontimeList["str"]) { var facet_href = facet["@href"].ToString(); var facet_href_split = facet_href.Split("/"); if (facet_href.Contains(device_number) && facet_href_split[facet_href_split.Length - 3] == name) { //表示有找到 finalFacetsValue = facet["@display"].ToString(); var facetValueSplit = facet["@display"].ToString().Split(","); foreach (var facetValue in facetValueSplit) { if (facetValue.Contains(value)) { var tempTextValue = facetValue.Split("="); value = tempTextValue[0]; has_find = true; break; } } if (has_find) { break; } } } } else { //找出是否有存在facets,如果有則須多加判斷 var sql = $@"SELECT facets FROM ontime_device_rawdata WHERE device_number = @device_number AND name = @name"; var facets = await frontendRepository.GetOneAsync(sql, new { device_number = device_number, name = name }); finalFacetsValue = facets; if (!string.IsNullOrEmpty(facets)) { var facetValueSplit = facets.Split(","); foreach (var facetValue in facetValueSplit) { if (facetValue.Contains(value)) { var tempTextValue = facetValue.Split("="); value = tempTextValue[0]; break; } } } } Dictionary ontimeRawData = new Dictionary() { { "device_number", device_number}, { "name", name}, { "@value", value}, { "@is_bool", 1}, { "@facets", finalFacetsValue}, }; ontimeRawDatas.Add(ontimeRawData); } } //stopWatch.Stop(); //logger.LogInformation("【OnTimeDeviceRawDataJob】【效能檢驗】[解析資料花費時間]{0} 毫秒", stopWatch.ElapsedMilliseconds); if (ontimeRawDatas.Count() > 0) { //stopWatch.Reset(); //stopWatch.Start(); await frontendRepository.UpdateListByCustomTable(ontimeRawDatas, "ontime_device_rawdata", "device_number = @device_number AND name = @name"); //stopWatch.Stop(); //logger.LogInformation("【OnTimeDeviceRawDataJob】【效能檢驗】[資料存入資料庫花費時間]{0} 毫秒", stopWatch.ElapsedMilliseconds); } } } logger.LogInformation("【OnTimeDeviceRawDataJob】【任務完成】"); } catch (Exception exception) { logger.LogError("【OnTimeDeviceRawDataJob】【任務失敗】"); logger.LogError("【OnTimeDeviceRawDataJob】【任務失敗】[Exception]:{0}", exception.ToString()); } } } }