SOMS/src/YunDa.Server/YunDa.Server.ISMSTcp/Services/SecondaryCircuitInspectionPlanService.cs

596 lines
19 KiB
C#
Raw Normal View History

2025-10-26 15:12:32 +08:00
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.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Numerics;
using System.Security.Cryptography;
2025-10-26 15:12:32 +08:00
using System.Text;
using System.Threading;
using System.Threading.Channels;
2025-10-26 15:12:32 +08:00
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;
2025-10-26 15:12:32 +08:00
namespace YunDa.Server.ISMSTcp.Services
{
//Web服务器设置
2025-10-26 15:12:32 +08:00
public class WebApiSettings
{
public int Port { get; set; }
}
//带执行状态的巡检计划结构体
2025-10-26 15:12:32 +08:00
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<DzInfo> UserDz { get; set; }
public List<DzInfo> 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<YCResultData> TelemetryInfoData { get; set; }
//遥信数据
public List<YXResultData> TeleSignalData { get; set; }
//定值
public List<DeviceDzData> DeviceDzData { get; set; }
//预置位的巡检结果
public List<InspectionResultData> InspectionResultData { get; set; }
2025-10-26 15:12:32 +08:00
}
//二次回路巡检计划
public class SecondaryCircuitInspectionPlanService
{
private readonly ILogger<SecondaryCircuitInspectionPlanService> _logger;
private readonly IApiEndpoints _apiEndpoints;
private readonly WebApiRequest _webApiRequest;
private readonly WebApiSettings _webApiSettings;
private bool _updatedPlanOk = false;
private Queue<SecondaryCircuitInspectionPlanStateModel> _planList;
private readonly object _planLock = new object();
private int _planCheckDay = 0;
private readonly Channel<SecondaryCircuitInspectionItemOutput> _singlePlanChannel;
//巡检事件
private Queue<SecondaryCircuitEventDrivenConfigOutput> _eventPlanList;
private readonly object _eventPlanLock = new object();
private bool _getEventPlanListOk = false;
2025-10-26 15:12:32 +08:00
public SecondaryCircuitInspectionPlanService(
ILogger<SecondaryCircuitInspectionPlanService> logger,
IApiEndpoints apiEndpoints,
WebApiRequest webApiRequest,
IOptions<WebApiSettings> 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<SecondaryCircuitInspectionPlanStateModel>();
_singlePlanChannel = Channel.CreateUnbounded<SecondaryCircuitInspectionItemOutput>();
_eventPlanList = new Queue<SecondaryCircuitEventDrivenConfigOutput>();
2025-10-26 15:12:32 +08:00
StartAsync();
}
private async Task StartAsync()
{
//更新计划
_ = Task.Run(async () =>
{
while (true)
{//每30秒更新一下配置
await UpdatePlans();
await UpdateEventPlans();
2025-10-26 15:12:32 +08:00
await Task.Delay(30000);
}
});
//检测计划
2025-10-26 15:12:32 +08:00
_ = Task.Run(async () =>
{
while (true)
{//每10秒检测一下计划
2025-10-26 15:12:32 +08:00
if (_updatedPlanOk)
{
await CheckPlan();
await Task.Delay(10000);
2025-10-26 15:12:32 +08:00
}
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);
}
});
}
2025-10-26 15:12:32 +08:00
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<SecondaryCircuitInspectionPlanStateModel> planlist = new List<SecondaryCircuitInspectionPlanStateModel>();
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);
2025-10-26 15:12:32 +08:00
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;
}
2025-10-26 15:12:32 +08:00
//检测计划,判断计划是否该执行
private async Task<bool> 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;
2025-10-26 15:12:32 +08:00
}
ret = true;
2025-10-26 15:12:32 +08:00
}
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckPlan发生错误");
}
return ret;
}
//将需要执行的计划写到队列里
private async Task<bool> PushPlanToChannel(SecondaryCircuitInspectionPlanOutput plan)
2025-10-26 15:12:32 +08:00
{
try
{
bool ret = true;
foreach (var item in plan.InspectionItems)
{
string id = item.Id.ToString();
2025-10-26 15:12:32 +08:00
if(item.IsActive && !string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(id))
{
//写到队列里,由队列控制执行频率
await _singlePlanChannel.Writer.WriteAsync(item);
2025-10-26 15:12:32 +08:00
}
}
return ret;
}
catch (Exception ex)
{
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - ExecutePlan发生错误");
}
return false;
}
//执行计划
private async Task ExecutePlan(SecondaryCircuitInspectionItemOutput item)
2025-10-26 15:12:32 +08:00
{
try
{
string id = item.Id.ToString();
if (!string.IsNullOrWhiteSpace(id))
2025-10-26 15:12:32 +08:00
{
var engine = new Jint.Engine();
2025-10-26 15:12:32 +08:00
SecondaryCircuitInspectionResultModel resultModel = new SecondaryCircuitInspectionResultModel();
var task1 = GetSourceDataAsync<List<YCResultData>>(id, 1);
var task2 = GetSourceDataAsync<List<YXResultData>>(id, 2);
var task3 = GetSourceDataAsync<List<DeviceDzData>>(id, 3);
var task4 = GetSourceDataAsync<List<InspectionResultData>>(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();
2025-10-26 15:12:32 +08:00
}
}
catch(Exception ex)
{
_logger.LogError(ex, $"SecondaryCircuitInspectionPlanService 执行巡检项失败:{item.Id.ToString()}");
2025-10-26 15:12:32 +08:00
}
}
//获取计划数据
private async Task<T> GetSourceDataAsync<T>(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);
}
}
2025-10-26 15:12:32 +08:00
return (T)result;
}
//执行事件计划
private async Task<bool> 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<List<YCResultData>>(id, 1);
var task2 = GetSourceDataAsync<List<YXResultData>>(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);
}
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckEventPlan发生错误");
}
return ret;
2025-10-26 15:12:32 +08:00
}
//检测巡检计划中的计划
private async Task CheckPlanFormIds(List<Guid> planIds)
{
if (_planList == null)
return;
try
{
SecondaryCircuitInspectionPlanStateModel[] planList;
lock (_planLock)
{
planList = _planList.ToArray();
}
foreach (var plan in planList)
{
if(plan.Plan.IsActive)
{
foreach (var item in plan.Plan.InspectionItems)
{
if (planIds.IndexOf(item.Id) > -1 && item.IsActive)
{
if (!string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(item.Id.ToString()))
{
//写到队列里,由队列控制执行频率
await _singlePlanChannel.Writer.WriteAsync(item);
}
}
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckPlanFormIds :发生错误");
}
return;
}
////获取遥测数据
//public async Task<List<YCResultData>> 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<JArray>("data");
// if (jArray != null)
// {
// List<YCResultData> list = jArray.ToObject<List<YCResultData>>();
// return list;
// }
// }
// }
// catch(Exception ex)
// {
// _logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CallYCByDataIdAsync发生错误");
// }
// return null;
//}
2025-10-26 15:12:32 +08:00
}
}