1051 lines
40 KiB
C#
1051 lines
40 KiB
C#
using DotNetty.Common.Utilities;
|
||
using Jint;
|
||
using Jint.Native.Object;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Extensions.Options;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using Newtonsoft.Json.Serialization;
|
||
using Nito.AsyncEx;
|
||
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics;
|
||
using System.Dynamic;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Net.Http.Json;
|
||
using System.Numerics;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
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;
|
||
using YunDa.SOMS.Entities.DataMonitoring.SecondaryCircuitInspection;
|
||
using YunDa.Server.ISMSTcp.Helpers;
|
||
using Abp.AutoMapper;
|
||
|
||
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 SecondaryCircuitInspectionItemOutputEx
|
||
{
|
||
public int TriggerType { get; set; }
|
||
|
||
public SecondaryCircuitInspectionItemOutput Item { get; set; }
|
||
|
||
public SecondaryCircuitInspectionItemOutputEx(int triggerType, SecondaryCircuitInspectionItemOutput item)
|
||
{
|
||
TriggerType = triggerType;
|
||
Item = item;
|
||
}
|
||
}
|
||
|
||
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 SecondaryCircuitInspectionStoreDataModel
|
||
{
|
||
//遥测数据
|
||
public List<ZzDataResultModel> TelemetryData { get; set; }
|
||
|
||
//遥信数据
|
||
public List<ZzDataResultModel> TeleSignalData { get; set; }
|
||
|
||
//定值
|
||
public List<DeviceDzData> SettingData { get; set; }
|
||
|
||
//预置位
|
||
public List<InspectionResultData> PresetData { get; set; }
|
||
|
||
//虚点数据
|
||
public List<ZzDataResultModel> VariantData { get; set; }
|
||
|
||
//网关数据
|
||
public List<ZzDataResultModel> GatewayData { get; set; }
|
||
}
|
||
//巡检结果
|
||
public class SecondaryCircuitInspectionJsDataModel
|
||
{
|
||
public int TimeWindowSeconds { get; set; }
|
||
public int TimeWindowCount { get; set; }
|
||
public TimeWindowTypeEnum TimeWindowType { get; set; }
|
||
|
||
public SecondaryCircuitInspectionStoreDataModel StoreData { get; set; } = new SecondaryCircuitInspectionStoreDataModel();
|
||
public List<SecondaryCircuitInspectionTelemetryConfigOutput> TelemetryConfigs { get; set; }
|
||
public List<SecondaryCircuitInspectionTelesignalConfigOutput> TelesignalConfigs { get; set; }
|
||
public List<SecondaryCircuitInspectionCameraPresetOutput> CameraPresets { get; set; }
|
||
public List<SecondaryCircuitInspectionDeviceConfigOutput> DeviceConfigs { get; set; }
|
||
public List<SecondaryCircuitInspectionVariantOutput> VariantConfigs { get; set; }
|
||
public List<SecondaryCircuitInspectionGatewayOutput> GatewayConfigs { get; set; }
|
||
}
|
||
|
||
|
||
//巡检结果上传Model
|
||
public class SecondaryCircuitInspectionResultSaveModel
|
||
{
|
||
public string SecondaryCircuitInspectionItemId { get; set; } = string.Empty;
|
||
public string InspectionPlanId { get; set; } = string.Empty;
|
||
|
||
public string ExecutionTime { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||
|
||
public long ExecutionDurationMs { get; set; }
|
||
|
||
public int TriggerType { get; set; } //1计划,2事件
|
||
|
||
public string TriggerEventId { get; set; } = string.Empty;
|
||
public string Status { get; set; } = string.Empty; //"异常|正常|故障",
|
||
public string InspectionResult { get; set; } = string.Empty; //巡检结果 inspectionResult不能超过15个字符
|
||
public string CalculationProcess { get; set; } = string.Empty; //计算过程
|
||
public string VerificationResult { get; set; } = string.Empty; //校验结果
|
||
public SecondaryCircuitInspectionJsDataModel Data { get; set; }
|
||
public string Remark { get; set; } = string.Empty; //无用
|
||
|
||
|
||
}
|
||
|
||
|
||
//
|
||
public class SecondaryCircuitInspectionAiParamModel
|
||
{
|
||
public string InspectionResultId { get; set; } = string.Empty;
|
||
public string InspectionName { get; set; } = string.Empty;
|
||
public string InspectionDescription { get; set; } = string.Empty;
|
||
public string PlanName => string.Concat(InspectionName, " ", InspectionDescription);
|
||
public string Status { get; set; } = string.Empty; //"异常|正常|故障",
|
||
public string InspectionResult { get; set; } = string.Empty; //巡检结果 inspectionResult不能超过15个字符
|
||
public string CalculationProcess { get; set; } = string.Empty; //计算过程
|
||
public string VerificationResult { get; set; } = string.Empty; //校验结果
|
||
|
||
}
|
||
|
||
public class SecondaryCircuitInspectionAiSaveModel
|
||
{
|
||
public string InspectionResultId { get; set; } = string.Empty;
|
||
public string AiAnalysisResult { get; set; } = string.Empty; // "AI结果状态:失败|成功",
|
||
public string ResultHandleDescription { get; set; } = string.Empty; //"AI返回的处理结果"
|
||
}
|
||
|
||
//二次回路巡检计划
|
||
public class SecondaryCircuitInspectionPlanService
|
||
{
|
||
private readonly bool _isDebug = false;
|
||
|
||
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 AsyncLock _planLock = new AsyncLock();
|
||
private int _planCheckDay = 0;
|
||
|
||
private readonly Channel<SecondaryCircuitInspectionItemOutputEx> _singlePlanChannel; //巡检计划
|
||
private readonly ChannelEx<SecondaryCircuitInspectionAiParamModel, string> _aiChannel; //Ai调用
|
||
|
||
//巡检事件
|
||
private Queue<SecondaryCircuitEventDrivenConfigOutput> _eventPlanList;
|
||
private readonly AsyncLock _eventPlanLock = new AsyncLock();
|
||
private bool _getEventPlanListOk = false;
|
||
|
||
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<SecondaryCircuitInspectionItemOutputEx>();
|
||
|
||
_aiChannel = new ChannelEx<SecondaryCircuitInspectionAiParamModel, string>( item => item.InspectionResultId);
|
||
|
||
_eventPlanList = new Queue<SecondaryCircuitEventDrivenConfigOutput>();
|
||
|
||
//var kk = webApiRequest.GetTeleSignalData("08de219e-3524-41db-8d7d-86b547bac16a");
|
||
|
||
|
||
StartAsync();
|
||
|
||
}
|
||
|
||
private async Task StartAsync()
|
||
{
|
||
//更新计划
|
||
_ = Task.Run(async () =>
|
||
{
|
||
while (true)
|
||
{//每10分钟更新一下配置
|
||
|
||
await UpdatePlans();
|
||
|
||
await UpdateEventPlans();
|
||
|
||
//await CheckAiChannel();
|
||
|
||
await Task.Delay(TimeSpan.FromMinutes(10));
|
||
}
|
||
});
|
||
|
||
|
||
//检测计划
|
||
_ = Task.Run(async () =>
|
||
{
|
||
while (true)
|
||
{//每10秒检测一下计划
|
||
|
||
if (_updatedPlanOk)
|
||
{
|
||
await CheckPlan();
|
||
|
||
await Task.Delay(TimeSpan.FromSeconds(60));
|
||
}
|
||
else
|
||
{
|
||
await Task.Delay(TimeSpan.FromSeconds(60));
|
||
}
|
||
|
||
}
|
||
});
|
||
|
||
|
||
//事件计划
|
||
_ = Task.Run(async () =>
|
||
{
|
||
while (true)
|
||
{
|
||
if (_getEventPlanListOk)
|
||
{
|
||
await CheckEventPlan();
|
||
}
|
||
else
|
||
{
|
||
await Task.Delay(TimeSpan.FromSeconds(60));
|
||
}
|
||
|
||
}
|
||
});
|
||
|
||
//执行计划(两个线程同时执行)
|
||
int threadNumber = _isDebug ? 1 : 1;
|
||
|
||
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);
|
||
}
|
||
});
|
||
}
|
||
|
||
//调用Ai获取诊断结果
|
||
for (int i = 0; i < threadNumber; ++i)
|
||
{
|
||
_ = Task.Run(async () => {
|
||
|
||
var rand = new Random(Guid.NewGuid().GetHashCode());
|
||
|
||
await foreach (var item in _aiChannel.ReadAllAsync())
|
||
{
|
||
// 让每个线程在执行之间有不同的节奏
|
||
await Task.Delay(rand.Next(0, 300));
|
||
|
||
await CallAiAndSave(item);
|
||
|
||
await Task.Delay(5000);
|
||
}
|
||
});
|
||
}
|
||
|
||
await Task.CompletedTask;
|
||
}
|
||
|
||
//更新计划
|
||
private async Task UpdatePlans()
|
||
{
|
||
try
|
||
{
|
||
using (await _planLock.LockAsync())
|
||
{
|
||
var oldPlan = _planList.ToArray();
|
||
|
||
_planList.Clear();
|
||
|
||
int start = 0;
|
||
while (start < 1000)
|
||
{
|
||
var list = await _webApiRequest.GetSecondaryCircuitInspectionPlanListAsync(start);
|
||
if (list != null)
|
||
{
|
||
|
||
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);
|
||
if (plan != null)
|
||
{
|
||
item.ExecuteTime = plan.ExecuteTime;
|
||
}
|
||
|
||
_planList.Enqueue(item);
|
||
}
|
||
|
||
if (list.Count < 10)
|
||
{
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
start += 10;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
//获取到绑定信息,可以更新全部状态了
|
||
if (_planList.Count > 0)
|
||
_updatedPlanOk = true;
|
||
}
|
||
|
||
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_updatedPlanOk = false;
|
||
}
|
||
|
||
await Task.CompletedTask;
|
||
}
|
||
|
||
//更新事件计划
|
||
private async Task UpdateEventPlans()
|
||
{
|
||
try
|
||
{
|
||
|
||
using (await _eventPlanLock.LockAsync())
|
||
{
|
||
_eventPlanList.Clear();
|
||
|
||
int start = 0;
|
||
while (start < 1000)
|
||
{
|
||
var list = await _webApiRequest.GetCircuitEventDrivenConfigAsync(start);
|
||
if (list != null)
|
||
{
|
||
foreach (var item in list)
|
||
_eventPlanList.Enqueue(item);
|
||
|
||
if (list.Count < 10)
|
||
{
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
start += 10;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
//获取到绑定信息,可以更新全部状态了
|
||
if (_eventPlanList.Count > 0)
|
||
_getEventPlanListOk = true;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_getEventPlanListOk = false;
|
||
}
|
||
|
||
await Task.CompletedTask;
|
||
}
|
||
|
||
//检测计划,判断计划是否该执行
|
||
private async Task<bool> CheckPlan()
|
||
{
|
||
bool ret = false;
|
||
|
||
if (_planList == null)
|
||
return ret;
|
||
|
||
try
|
||
{
|
||
SecondaryCircuitInspectionPlanStateModel[] planList;
|
||
|
||
using (await _planLock.LockAsync())
|
||
{
|
||
planList = _planList.ToArray();
|
||
}
|
||
|
||
DateTime now = DateTime.Now;
|
||
int week = DateTime.Now.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)DateTime.Now.DayOfWeek;
|
||
|
||
if (now.Day != _planCheckDay)
|
||
{//翻天,清空已执行标记
|
||
using (await _planLock.LockAsync())
|
||
{
|
||
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(1, item.Plan);
|
||
|
||
using (await _planLock.LockAsync())
|
||
{
|
||
item.ExecuteTime = DateTime.Now;
|
||
}
|
||
|
||
ret = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckPlan:发生错误");
|
||
}
|
||
|
||
return ret;
|
||
|
||
}
|
||
|
||
//将需要执行的计划写到队列里
|
||
private async Task<bool> PushPlanToChannel(int triggerType, 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(new SecondaryCircuitInspectionItemOutputEx(triggerType, item));
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - ExecutePlan:发生错误");
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//执行计划
|
||
private async Task ExecutePlan(SecondaryCircuitInspectionItemOutputEx itemEx)
|
||
{
|
||
|
||
//string func2 = "";
|
||
//string data2 = "";
|
||
try
|
||
{
|
||
var item = itemEx.Item;
|
||
|
||
string id = item.Id.ToString();
|
||
|
||
|
||
if (!string.IsNullOrWhiteSpace(id))
|
||
{
|
||
|
||
SecondaryCircuitInspectionJsDataModel jsData = new SecondaryCircuitInspectionJsDataModel();
|
||
|
||
jsData.TimeWindowSeconds = item.TimeWindowSeconds;
|
||
jsData.TimeWindowCount = item.TimeWindowCount;
|
||
jsData.TimeWindowType = item.TimeWindowType;
|
||
|
||
jsData.TelemetryConfigs = item.TelemetryConfigs;
|
||
jsData.TelesignalConfigs = item.TelesignalConfigs;
|
||
jsData.CameraPresets = item.CameraPresets;
|
||
jsData.DeviceConfigs = item.DeviceConfigs;
|
||
jsData.VariantConfigs = item.VariantConfigs;
|
||
jsData.GatewayConfigs = item.GatewayConfigs;
|
||
|
||
|
||
var engine = new Jint.Engine();
|
||
engine.Execute(item.CalculationExpression);
|
||
|
||
|
||
ObjectInstance? saveObj = null;
|
||
|
||
var sw = Stopwatch.StartNew();
|
||
|
||
for (int i = 0; i < item.TimeWindowCount; i++)
|
||
{
|
||
List<ZzDataResultModel>? telemetryData = null;
|
||
List<ZzDataResultModel>? teleSignalData = null;
|
||
List<DeviceDzData>? settingData = null;
|
||
List<InspectionResultData>? presetData = null;
|
||
List<ZzDataResultModel>? gatewayData = null;
|
||
List<ZzDataResultModel>? variantData = null;
|
||
|
||
if (item.TelemetryConfigCount > 0)
|
||
{
|
||
telemetryData = await GetSourceDataAsync<List<ZzDataResultModel>>(id, 1);
|
||
}
|
||
if (item.TelesignalConfigCount > 0)
|
||
{
|
||
teleSignalData = await GetSourceDataAsync<List<ZzDataResultModel>>(id, 2);
|
||
}
|
||
if (item.DeviceConfigCount > 0)
|
||
{
|
||
settingData = await GetSourceDataAsync<List<DeviceDzData>>(id, 3);
|
||
}
|
||
if (item.CameraPresetCount > 0)
|
||
{
|
||
presetData = await GetSourceDataAsync<List<InspectionResultData>>(id, 4);
|
||
}
|
||
if (item.GatewayConfigCount > 0)
|
||
{
|
||
gatewayData = await GetSourceDataAsync<List<ZzDataResultModel>>(id, 5);
|
||
}
|
||
if (item.VariantConfigCount > 0)
|
||
{
|
||
variantData = await GetSourceDataAsync<List<ZzDataResultModel>>(id, 6);
|
||
}
|
||
|
||
jsData.StoreData.TelemetryData = telemetryData;
|
||
jsData.StoreData.TeleSignalData = teleSignalData;
|
||
jsData.StoreData.SettingData = settingData;
|
||
jsData.StoreData.PresetData = presetData;
|
||
jsData.StoreData.GatewayData = gatewayData;
|
||
jsData.StoreData.VariantData = variantData;
|
||
|
||
if (jsData.StoreData.TelemetryData == null && jsData.StoreData.TeleSignalData == null && jsData.StoreData.SettingData == null &&
|
||
jsData.StoreData.PresetData == null && jsData.StoreData.GatewayData == null && jsData.StoreData.VariantData == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var json = JsonConvert.SerializeObject(jsData,
|
||
new JsonSerializerSettings
|
||
{
|
||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||
});
|
||
var js = $"JSON.parse('{json.Replace("'", "\\'")}')";
|
||
var jsObj = engine.Evaluate(js).ToObject();
|
||
var jsResult = engine.Invoke("calculate", jsObj).AsObject();
|
||
if (jsResult != null && jsResult.HasProperty("status"))
|
||
{
|
||
saveObj = jsResult;
|
||
var status = jsResult.Get("status").ToString();
|
||
if (status == "正常" || string.IsNullOrWhiteSpace(status))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
saveObj = null;
|
||
break;
|
||
}
|
||
}
|
||
|
||
sw.Stop();
|
||
|
||
//保存结果
|
||
if (saveObj != null)
|
||
{
|
||
SecondaryCircuitInspectionResultSaveModel saveData = new SecondaryCircuitInspectionResultSaveModel();
|
||
|
||
if(saveObj.HasProperty("status") && saveObj.HasProperty("inspectionResult") && saveObj.HasProperty("calculationProcess") && saveObj.HasProperty("verificationResult"))
|
||
{
|
||
saveData.SecondaryCircuitInspectionItemId = item.Id.ToString();
|
||
saveData.ExecutionDurationMs = sw.ElapsedMilliseconds;
|
||
saveData.TriggerType = itemEx.TriggerType;
|
||
if (saveData.TriggerType == 1)
|
||
saveData.InspectionPlanId = id;
|
||
else
|
||
saveData.TriggerEventId = id;
|
||
|
||
saveData.Status = saveObj.Get("status").ToString();
|
||
saveData.InspectionResult = saveObj.Get("inspectionResult").ToString();
|
||
saveData.CalculationProcess = saveObj.Get("calculationProcess").ToString();
|
||
saveData.VerificationResult = saveObj.Get("verificationResult").ToString();
|
||
|
||
saveData.Data = jsData;
|
||
|
||
var inspectionResultId = await _webApiRequest.SaveSecondaryCircuitInspectionPlanResultAsync(saveData);
|
||
|
||
//获取Ai诊断结果并保存,不正常时才诊断
|
||
if (!string.IsNullOrWhiteSpace(inspectionResultId) && saveData.Status != "正常")
|
||
{
|
||
SecondaryCircuitInspectionAiParamModel aiParamModel = new SecondaryCircuitInspectionAiParamModel();
|
||
|
||
aiParamModel.InspectionResultId = inspectionResultId;
|
||
|
||
aiParamModel.InspectionName = item.Name;
|
||
aiParamModel.InspectionDescription = item.Description;
|
||
|
||
aiParamModel.Status = saveData.Status;
|
||
aiParamModel.InspectionResult = saveData.InspectionResult;
|
||
aiParamModel.CalculationProcess = saveData.CalculationProcess;
|
||
aiParamModel.VerificationResult = saveData.VerificationResult;
|
||
|
||
_aiChannel.WriteAsync(aiParamModel);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
catch(Exception ex)
|
||
{
|
||
_logger.LogError(ex, $"SecondaryCircuitInspectionPlanService 执行巡检项失败:{itemEx.Item.Id.ToString()}");
|
||
}
|
||
}
|
||
|
||
//获取没有调用Ai接口或调用失败的巡检记录结果
|
||
private async Task CheckAiChannel()
|
||
{
|
||
if (!_aiChannel.HasItems)
|
||
{
|
||
var list = await _webApiRequest.GetFailedAiInspectionListAsync();
|
||
if (list != null)
|
||
{
|
||
foreach (var item in list)
|
||
await _aiChannel.WriteAsync(item);
|
||
}
|
||
}
|
||
}
|
||
|
||
private async Task CallAiAndSave(SecondaryCircuitInspectionAiParamModel aiParamModel)
|
||
{
|
||
try
|
||
{
|
||
var aiParam = new
|
||
{
|
||
user_message = GetAiParamString(aiParamModel),
|
||
history = new List<string[]>()
|
||
};
|
||
|
||
var aiResultRaw = await _webApiRequest.GetAIAnalysis(aiParam);
|
||
|
||
bool isSuccess = false;
|
||
|
||
if (!string.IsNullOrWhiteSpace(aiResultRaw))
|
||
{
|
||
string aiResult = ParseAiResult(aiResultRaw);
|
||
|
||
if (!string.IsNullOrWhiteSpace(aiResult))
|
||
{
|
||
SecondaryCircuitInspectionAiSaveModel data = new SecondaryCircuitInspectionAiSaveModel();
|
||
data.InspectionResultId = aiParamModel.InspectionResultId;
|
||
data.AiAnalysisResult = "成功";
|
||
data.ResultHandleDescription = aiResult;
|
||
|
||
await _webApiRequest.UpdateReportWithAIAnalysisAsync(data);
|
||
|
||
isSuccess = true;
|
||
}
|
||
}
|
||
|
||
if(!isSuccess)
|
||
{
|
||
SecondaryCircuitInspectionAiSaveModel data = new SecondaryCircuitInspectionAiSaveModel();
|
||
data.InspectionResultId = aiParamModel.InspectionResultId;
|
||
data.AiAnalysisResult = "失败";
|
||
data.ResultHandleDescription = "";
|
||
|
||
await _webApiRequest.UpdateReportWithAIAnalysisAsync(data);
|
||
}
|
||
}
|
||
catch(Exception ex)
|
||
{
|
||
_logger.LogError(ex, $"SecondaryCircuitInspectionPlanService CallAiAndSave出错");
|
||
}
|
||
}
|
||
|
||
private static string GetAiParamString(SecondaryCircuitInspectionAiParamModel aiParamModel)
|
||
{
|
||
var sb = new StringBuilder();
|
||
|
||
sb.AppendLine("你是一个电力系统专家,请根据以下变电站二次巡检结果数据生成故障排除建议:");
|
||
sb.AppendLine();
|
||
|
||
sb.AppendLine("检修的建议要避免停止变电所的正常运行,要以宽松的方式,检修工作必须遵循“安全第一、预防为主”的核心原则,实施全过程精益化管理");
|
||
sb.AppendLine("检修前,需制定详尽的计划与方案,确保人员资质合格、物资工具齐备,并严格执行工作票制度,实现安全隔离。检修中,必须强化现场安全管控,设专人监护,并严格按标准工艺施工,做好关键数据记录与过程质检。检修后,要严格执行试验验收程序,确保设备性能达标,彻底清理现场,并完善检修报告与设备台账,形成闭环管理");
|
||
sb.AppendLine("整个流程旨在通过规范化、标准化的作业,确保人身、设备与电网安全,提升设备可靠性,保障电力稳定供应");
|
||
sb.AppendLine(",减少思考过程");
|
||
sb.AppendLine();
|
||
|
||
// 巡检结果
|
||
sb.AppendLine($"巡检内容为:");
|
||
sb.AppendLine(aiParamModel.PlanName);
|
||
sb.AppendLine();
|
||
sb.AppendLine("巡检结果为:");
|
||
sb.AppendLine($"巡检状态: {aiParamModel.Status}");
|
||
sb.AppendLine($"计算过程: {aiParamModel.CalculationProcess}");
|
||
sb.AppendLine($"巡检结果: {aiParamModel.InspectionResult}");
|
||
sb.AppendLine($"校验结果: {aiParamModel.VerificationResult}");
|
||
sb.AppendLine();
|
||
|
||
sb.AppendLine();
|
||
sb.AppendLine("请按以下格式生成排除建议,直接建议内容,每条建议前加序号,如1、,每条建议间加换行符");
|
||
|
||
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
private static string GetAiParamString2(SecondaryCircuitInspectionAiParamModel aiParamModel)
|
||
{
|
||
var sb = new StringBuilder();
|
||
|
||
sb.AppendLine("你是一个电力系统专家,请根据以下变电站二次巡检结果数据生成检修策略:");
|
||
sb.AppendLine();
|
||
|
||
sb.Append("检修的开始时间不得晚于当前时间 ")
|
||
.AppendLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
|
||
|
||
sb.AppendLine("检修的建议要避免停止变电所的正常运行,要以宽松的方式,检修工作必须遵循“安全第一、预防为主”的核心原则,实施全过程精益化管理");
|
||
sb.AppendLine("检修前,需制定详尽的计划与方案,确保人员资质合格、物资工具齐备,并严格执行工作票制度,实现安全隔离。检修中,必须强化现场安全管控,设专人监护,并严格按标准工艺施工,做好关键数据记录与过程质检。检修后,要严格执行试验验收程序,确保设备性能达标,彻底清理现场,并完善检修报告与设备台账,形成闭环管理");
|
||
sb.AppendLine("整个流程旨在通过规范化、标准化的作业,确保人身、设备与电网安全,提升设备可靠性,保障电力稳定供应");
|
||
sb.AppendLine(",减少思考过程");
|
||
sb.AppendLine();
|
||
|
||
// 巡检结果
|
||
sb.AppendLine("巡检结果为:");
|
||
sb.AppendLine($"巡检状态: {aiParamModel.Status}");
|
||
sb.AppendLine($"计算过程: {aiParamModel.CalculationProcess}");
|
||
sb.AppendLine($"巡检结果: {aiParamModel.InspectionResult}");
|
||
sb.AppendLine($"校验结果: {aiParamModel.VerificationResult}");
|
||
sb.AppendLine();
|
||
|
||
sb.AppendLine();
|
||
sb.AppendLine("请按以下JSON格式生成策略:");
|
||
|
||
sb.AppendLine(@"{
|
||
""level"": ""紧急程度"",
|
||
""focusAreas"": [""重点检修区域1"", ""重点检修区域2""],
|
||
""steps"": [
|
||
{""duration"": ""时间"", ""content"": ""步骤描述""}
|
||
],
|
||
""resources"": [""资源需求1"", ""资源需求2""],
|
||
""schedule"": {
|
||
""startDate"": ""开始时间"",
|
||
""optimalWindow"": ""最佳窗口"",
|
||
""endDate"": ""结束时间""
|
||
},
|
||
""risks"": [
|
||
{""title"": ""风险标题"", ""severity"": ""风险等级"", ""mitigation"": ""缓解措施""}
|
||
]
|
||
}");
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
private static string ParseAiResult(string raw)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(raw))
|
||
return string.Empty;
|
||
|
||
var result = new StringBuilder();
|
||
|
||
foreach (string line in raw.Split('\n'))
|
||
{
|
||
var trimmed = line.Trim();
|
||
if (string.IsNullOrWhiteSpace(trimmed))
|
||
continue;
|
||
|
||
try
|
||
{
|
||
using var doc = JsonDocument.Parse(trimmed);
|
||
var root = doc.RootElement;
|
||
|
||
if (root.TryGetProperty("type", out var typeProp) &&
|
||
typeProp.GetString() == "content" &&
|
||
root.TryGetProperty("content", out var contentProp))
|
||
{
|
||
result.Append(contentProp.GetString());
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 跳过非 JSON 行
|
||
continue;
|
||
}
|
||
}
|
||
|
||
return result.ToString();
|
||
}
|
||
|
||
//获取计划数据
|
||
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;
|
||
|
||
case 5:
|
||
result = await _webApiRequest.GetGatewayInfoData(id);
|
||
break;
|
||
|
||
case 6:
|
||
result = await _webApiRequest.GetVirtualPointInfoData(id);
|
||
break;
|
||
|
||
default:
|
||
throw new ArgumentException("Invalid type");
|
||
}
|
||
|
||
if(result != null)
|
||
{
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
await Task.Delay(200);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
return (T)result;
|
||
}
|
||
|
||
|
||
//执行事件计划
|
||
private async Task<bool> CheckEventPlan()
|
||
{
|
||
bool ret = false;
|
||
|
||
if (_eventPlanList == null)
|
||
return ret;
|
||
|
||
try
|
||
{
|
||
SecondaryCircuitEventDrivenConfigOutput[] planList;
|
||
using (await _eventPlanLock.LockAsync())
|
||
{
|
||
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();
|
||
|
||
SecondaryCircuitInspectionJsDataModel jsData = new SecondaryCircuitInspectionJsDataModel();
|
||
|
||
var tasks = new List<Task>();
|
||
|
||
Task<List<ZzDataResultModel>>? t1 = null;
|
||
Task<List<ZzDataResultModel>>? t2 = null;
|
||
|
||
if (item.TelemetryConfigs.Count > 0)
|
||
{
|
||
t1 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 1);
|
||
tasks.Add(t1);
|
||
}
|
||
|
||
if (item.TelesignalConfigs.Count > 0)
|
||
{
|
||
t2 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 2);
|
||
tasks.Add(t2);
|
||
}
|
||
|
||
await Task.WhenAll(tasks);
|
||
|
||
if (t1 != null) jsData.StoreData.TelemetryData = await t1;
|
||
if (t2 != null) jsData.StoreData.TeleSignalData = await t2;
|
||
|
||
engine.Execute(item.TriggerExpression);
|
||
|
||
bool canCheck = true;
|
||
|
||
if(!_isDebug)
|
||
{
|
||
canCheck = engine.Invoke("calculate", jsData).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(new SecondaryCircuitInspectionItemOutputEx(2, planItem));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckEventPlan:发生错误");
|
||
}
|
||
|
||
return ret;
|
||
|
||
}
|
||
|
||
|
||
|
||
////获取遥测数据
|
||
//public async Task<List<ZzDataResultModel>> CallYCByDataIdAsync(ZzDataRequestModel 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<ZzDataResultModel> list = jArray.ToObject<List<ZzDataResultModel>>();
|
||
|
||
// return list;
|
||
// }
|
||
// }
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// _logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CallYCByDataIdAsync:发生错误");
|
||
// }
|
||
|
||
|
||
|
||
// return null;
|
||
//}
|
||
}
|
||
}
|