using Jint; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Numerics; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using YunDa.Server.ISMSTcp.Domain; using YunDa.Server.ISMSTcp.Interfaces; using YunDa.Server.ISMSTcp.Models; using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection; using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection.Configurations; using YunDa.SOMS.DataTransferObject.MaintenanceAndOperations.SecondaryEquipment; namespace YunDa.Server.ISMSTcp.Services { //Web服务器设置 public class WebApiSettings { public int Port { get; set; } } //带执行状态的巡检计划结构体 public class SecondaryCircuitInspectionPlanStateModel { //执行时间 public DateTime ExecuteTime { get; set; } = DateTime.MinValue; public SecondaryCircuitInspectionPlanOutput Plan { get; set; } public SecondaryCircuitInspectionPlanStateModel() { Plan = new SecondaryCircuitInspectionPlanOutput(); } } public class DeviceDzData { public List UserDz { get; set; } public List SysDz { get; set; } public string DeviceName { get; set; } = string.Empty; } public class InspectionResultData { public string PresetName { get; set; } = string.Empty; public int PresetCode { get; set; } public string TimeStamp { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; } //巡检结果 public class SecondaryCircuitInspectionResultModel { //遥测数据 public List TelemetryInfoData { get; set; } //遥信数据 public List TeleSignalData { get; set; } //定值 public List DeviceDzData { get; set; } //预置位的巡检结果 public List InspectionResultData { get; set; } } //二次回路巡检计划 public class SecondaryCircuitInspectionPlanService { private readonly ILogger _logger; private readonly IApiEndpoints _apiEndpoints; private readonly WebApiRequest _webApiRequest; private readonly WebApiSettings _webApiSettings; private bool _updatedPlanOk = false; private Queue _planList; private readonly object _planLock = new object(); private int _planCheckDay = 0; private readonly Channel _singlePlanChannel; //巡检事件 private Queue _eventPlanList; private readonly object _eventPlanLock = new object(); private bool _getEventPlanListOk = false; public SecondaryCircuitInspectionPlanService( ILogger logger, IApiEndpoints apiEndpoints, WebApiRequest webApiRequest, IOptions webApiOptions ) { _apiEndpoints = apiEndpoints ?? throw new ArgumentNullException(nameof(apiEndpoints)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest)); _webApiSettings = webApiOptions.Value; _planList = new Queue(); _singlePlanChannel = Channel.CreateUnbounded(); _eventPlanList = new Queue(); StartAsync(); } private async Task StartAsync() { //更新计划 _ = Task.Run(async () => { while (true) {//每30秒更新一下配置 await UpdatePlans(); await UpdateEventPlans(); await Task.Delay(30000); } }); //检测计划 _ = Task.Run(async () => { while (true) {//每10秒检测一下计划 if (_updatedPlanOk) { await CheckPlan(); await Task.Delay(10000); } else { await Task.Delay(10000); } } }); //事件计划 _ = Task.Run(async () => { while (true) { if (_getEventPlanListOk) { await CheckEventPlan(); } else { await Task.Delay(10000); } } }); //执行计划(两个线程同时执行) int threadNumber = 3; for (int i = 0; i < threadNumber; ++i) { _ = Task.Run(async () => { var rand = new Random(Guid.NewGuid().GetHashCode()); await foreach (var item in _singlePlanChannel.Reader.ReadAllAsync()) { // 让每个线程在执行之间有不同的节奏 await Task.Delay(rand.Next(0, 300)); await ExecutePlan(item); await Task.Delay(500); } }); } await Task.CompletedTask; } //更新计划 private async Task UpdatePlans() { try { var list = await _webApiRequest.GetSecondaryCircuitInspectionPlanListAsync(); if (list != null) { lock (_planLock) { var oldPlan = _planList.ToArray(); _planList.Clear(); List planlist = new List(); foreach (var item in list) { planlist.Add(new SecondaryCircuitInspectionPlanStateModel() { Plan = item }); } foreach (var item in planlist) { var plan = oldPlan.FirstOrDefault(x => x.Plan.Id == item.Plan.Id && x.Plan.ScheduledTimeString == item.Plan.ScheduledTimeString); if (plan != null) { item.ExecuteTime = plan.ExecuteTime; } _planList.Enqueue(item); } //获取到绑定信息,可以更新全部状态了 if (planlist.Count > 0) _updatedPlanOk = true; } } } catch (Exception ex) { _updatedPlanOk = false; } await Task.CompletedTask; } //更新事件计划 private async Task UpdateEventPlans() { try { var list = await _webApiRequest.GetCircuitEventDrivenConfigAsync(); if (list != null) { lock (_eventPlanLock) { _eventPlanList.Clear(); foreach (var item in list) _eventPlanList.Enqueue(item); //获取到绑定信息,可以更新全部状态了 if (_eventPlanList.Count > 0) _getEventPlanListOk = true; } } } catch (Exception ex) { _getEventPlanListOk = false; } await Task.CompletedTask; } //检测计划,判断计划是否该执行 private async Task CheckPlan() { bool ret = false; if (_planList == null) return ret; try { SecondaryCircuitInspectionPlanStateModel[] planList; lock (_planLock) { planList = _planList.ToArray(); } DateTime now = DateTime.Now; int week = DateTime.Now.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)DateTime.Now.DayOfWeek; if (now.Day != _planCheckDay) {//翻天,清空已执行标记 lock (_planLock) { foreach (var item in _planList) item.ExecuteTime = DateTime.MinValue; } } _planCheckDay = now.Day; foreach (var item in planList) { if (item.ExecuteTime == DateTime.MinValue && item.Plan.IsActive) {//当天未执行 if (now.Hour == item.Plan.ScheduledHour && now.Minute == item.Plan.ScheduledMinute) { if (item.Plan.ScheduledWeekDaysList.IndexOf(week) != -1) { await PushPlanToChannel(item.Plan); lock (_planLock) { item.ExecuteTime = DateTime.Now; } ret = true; } } } } } catch (Exception ex) { _logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckPlan:发生错误"); } return ret; } //将需要执行的计划写到队列里 private async Task PushPlanToChannel(SecondaryCircuitInspectionPlanOutput plan) { try { bool ret = true; foreach (var item in plan.InspectionItems) { string id = item.Id.ToString(); if(item.IsActive && !string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(id)) { //写到队列里,由队列控制执行频率 await _singlePlanChannel.Writer.WriteAsync(item); } } return ret; } catch (Exception ex) { _logger.LogError(ex, "SecondaryCircuitInspectionPlanService - ExecutePlan:发生错误"); } return false; } //执行计划 private async Task ExecutePlan(SecondaryCircuitInspectionItemOutput item) { try { string id = item.Id.ToString(); if (!string.IsNullOrWhiteSpace(id)) { var engine = new Jint.Engine(); SecondaryCircuitInspectionResultModel resultModel = new SecondaryCircuitInspectionResultModel(); var task1 = GetSourceDataAsync>(id, 1); var task2 = GetSourceDataAsync>(id, 2); var task3 = GetSourceDataAsync>(id, 3); var task4 = GetSourceDataAsync>(id, 4); // 等待全部完成 await Task.WhenAll(task1, task2, task3, task4); resultModel.TelemetryInfoData = await task1; resultModel.TeleSignalData = await task2; resultModel.DeviceDzData = await task3; resultModel.InspectionResultData = await task4; engine.Execute(item.CalculationExpression); var result = engine.Invoke("calculate", resultModel).AsObject(); } } catch(Exception ex) { _logger.LogError(ex, $"SecondaryCircuitInspectionPlanService 执行巡检项失败:{item.Id.ToString()}"); } } //获取计划数据 private async Task GetSourceDataAsync(string id, int type, int retryCount = 3) { object result = null; for(int i = 0; i < retryCount; ++i) { switch (type) { case 1: result = await _webApiRequest.GetTelemetryInfoData(id); break; case 2: result = await _webApiRequest.GetTeleSignalData(id); break; case 3: result = await _webApiRequest.GetDeviceDzData(id); break; case 4: result = await _webApiRequest.GetInspectionResultData(id); break; default: throw new ArgumentException("Invalid type"); } if(result != null) { break; } else { await Task.Delay(200); } } return (T)result; } //执行事件计划 private async Task CheckEventPlan() { bool ret = false; if (_eventPlanList == null) return ret; try { SecondaryCircuitEventDrivenConfigOutput[] planList; lock (_planLock) { planList = _eventPlanList.ToArray(); } foreach (var item in planList) { await Task.Delay(item.DelayTriggerSeconds * 1000); if (item.IsActive && item.SecondaryCircuitInspectionEventItems?.Count > 0 && !string.IsNullOrWhiteSpace(item.TriggerExpression)) { string id = item.Id.ToString() ?? ""; if (!string.IsNullOrWhiteSpace(id)) { var engine = new Jint.Engine(); SecondaryCircuitInspectionResultModel resultModel = new SecondaryCircuitInspectionResultModel(); var task1 = GetSourceDataAsync>(id, 1); var task2 = GetSourceDataAsync>(id, 2); // 等待全部完成 await Task.WhenAll(task1, task2); resultModel.TelemetryInfoData = await task1; resultModel.TeleSignalData = await task2; engine.Execute(item.TriggerExpression); var canCheck = engine.Invoke("calculate", resultModel).AsBoolean(); if(canCheck) { await Task.Delay(item.MandatoryWaitSeconds * 1000); //CheckPlanFormIds(item.SecondaryCircuitInspectionEventItems); //执行巡检 foreach(var planItem in item.SecondaryCircuitInspectionEventItems) { if (!string.IsNullOrWhiteSpace(planItem.CalculationExpression) && !string.IsNullOrWhiteSpace(planItem.Id.ToString())) { //写到队列里,由队列控制执行频率 await _singlePlanChannel.Writer.WriteAsync(planItem); } } } } } } } catch (Exception ex) { _logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckEventPlan:发生错误"); } return ret; } ////获取遥测数据 //public async Task> CallYCByDataIdAsync(CallYCByDataIdRequest request, CancellationToken cancellationToken = default) //{ // try // { // using var httpClient = new HttpClient(); // string url = $"http://127.0.0.1:{_webApiSettings.Port}/api/CallYCByDataId"; // var response = await httpClient.PostAsJsonAsync(url, request); // response.EnsureSuccessStatusCode(); // var result = await response.Content.ReadAsStringAsync(); // JObject obj = JObject.Parse(result); // if (obj.ContainsKey("data")) // { // JArray jArray = obj.Value("data"); // if (jArray != null) // { // List list = jArray.ToObject>(); // return list; // } // } // } // catch(Exception ex) // { // _logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CallYCByDataIdAsync:发生错误"); // } // return null; //} } }