ibms-dome/FrontendWorkerService/Quartz/Jobs/OnTimeDeviceRawDataJob.cs
2022-10-14 16:08:54 +08:00

595 lines
34 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 透過obix API向Niagara取得所有設備的最新的即時資料
/// </summary>
[DisallowConcurrentExecution]
class OnTimeDeviceRawDataJob : IJob
{
private readonly ILogger<OnTimeDeviceRawDataJob> logger;
private readonly IFrontendRepository frontendRepository;
public OnTimeDeviceRawDataJob(
ILogger<OnTimeDeviceRawDataJob> 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<KeyValue>(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>("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<string, object> updateWatchId = new Dictionary<string, object>()
{
{ "@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 = $@"<reltime val='PT24H0S' />";
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<OntimeSubscrip>("ontime_device_subscription", string.Empty);
var subscripUriFormat = @" <uri val='/obix/config/Arena/{0}/{1}/{2}/{3}/' />";
List<string> subscripStrings = new List<string>();
foreach (var deviceSubscrip in deviceSubscrips)
{
List<string> devicePaths = new List<string>();
//拆解設備編號來組出路徑
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<DeviceKind>(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 = $@"<obj is='obix: WatchIn'>
<list names = 'hrefs'>
{string.Join(null, subscripStrings)}
</list>
</obj>";
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<Dictionary<string, object>> ontimeRawDatas = new List<Dictionary<string, object>>();
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<string, object> ontimeRawData = new Dictionary<string, object>()
{
{ "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<string, object> ontimeRawData = new Dictionary<string, object>()
{
{ "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<string>(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<string, object> ontimeRawData = new Dictionary<string, object>()
{
{ "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<string>(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<string, object> ontimeRawData = new Dictionary<string, object>()
{
{ "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());
}
}
}
}