2025-11-20 19:06:02 +08:00
|
|
|
|
using DotNetty.Common.Utilities;
|
|
|
|
|
|
using Jint;
|
|
|
|
|
|
using Jint.Native.Object;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using Microsoft.Extensions.Options;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using Newtonsoft.Json;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using Newtonsoft.Json.Serialization;
|
|
|
|
|
|
using Nito.AsyncEx;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
|
using System.Collections.Generic;
|
2025-10-30 12:40:42 +08:00
|
|
|
|
using System.ComponentModel;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using System.Diagnostics;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using System.Dynamic;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
|
using System.Net.Http.Json;
|
|
|
|
|
|
using System.Numerics;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
using System.Security.Cryptography;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using System.Text;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using System.Text.Json;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using System.Threading;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
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;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection.Configurations;
|
|
|
|
|
|
using YunDa.SOMS.DataTransferObject.MaintenanceAndOperations.SecondaryEquipment;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using YunDa.SOMS.Entities.DataMonitoring.SecondaryCircuitInspection;
|
2025-11-26 14:08:37 +08:00
|
|
|
|
using YunDa.Server.ISMSTcp.Helpers;
|
|
|
|
|
|
using Abp.AutoMapper;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
|
|
|
|
|
namespace YunDa.Server.ISMSTcp.Services
|
|
|
|
|
|
{
|
2025-10-30 12:24:29 +08:00
|
|
|
|
//Web服务器设置
|
2025-10-26 15:12:32 +08:00
|
|
|
|
public class WebApiSettings
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Port { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
//带执行状态的巡检计划结构体
|
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();
|
|
|
|
|
|
}
|
2025-10-30 12:24:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
public class SecondaryCircuitInspectionItemOutputEx
|
|
|
|
|
|
{
|
|
|
|
|
|
public int TriggerType { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public SecondaryCircuitInspectionItemOutput Item { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public SecondaryCircuitInspectionItemOutputEx(int triggerType, SecondaryCircuitInspectionItemOutput item)
|
|
|
|
|
|
{
|
|
|
|
|
|
TriggerType = triggerType;
|
|
|
|
|
|
Item = item;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class SecondaryCircuitInspectionStoreDataModel
|
2025-10-30 12:24:29 +08:00
|
|
|
|
{
|
|
|
|
|
|
//遥测数据
|
2025-11-20 19:06:02 +08:00
|
|
|
|
public List<ZzDataResultModel> TelemetryData { get; set; }
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
//遥信数据
|
2025-11-20 19:06:02 +08:00
|
|
|
|
public List<ZzDataResultModel> TeleSignalData { get; set; }
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
//定值
|
2025-11-20 19:06:02 +08:00
|
|
|
|
public List<DeviceDzData> SettingData { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
//预置位
|
|
|
|
|
|
public List<InspectionResultData> PresetData { get; set; }
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
//虚点数据
|
|
|
|
|
|
public List<ZzDataResultModel> VariantData { get; set; }
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
//网关数据
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2025-11-21 18:07:07 +08:00
|
|
|
|
public string ExecutionTime { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
2025-11-26 14:08:37 +08:00
|
|
|
|
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; //校验结果
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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返回的处理结果"
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//二次回路巡检计划
|
|
|
|
|
|
public class SecondaryCircuitInspectionPlanService
|
|
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
private readonly bool _isDebug = false;
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
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;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
private readonly AsyncLock _planLock = new AsyncLock();
|
2025-10-26 15:12:32 +08:00
|
|
|
|
private int _planCheckDay = 0;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
private readonly Channel<SecondaryCircuitInspectionItemOutputEx> _singlePlanChannel; //巡检计划
|
2025-11-26 14:08:37 +08:00
|
|
|
|
private readonly ChannelEx<SecondaryCircuitInspectionAiParamModel, string> _aiChannel; //Ai调用
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
//巡检事件
|
|
|
|
|
|
private Queue<SecondaryCircuitEventDrivenConfigOutput> _eventPlanList;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
private readonly AsyncLock _eventPlanLock = new AsyncLock();
|
2025-10-30 12:24:29 +08:00
|
|
|
|
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>();
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
_singlePlanChannel = Channel.CreateUnbounded<SecondaryCircuitInspectionItemOutputEx>();
|
|
|
|
|
|
|
2025-11-26 14:08:37 +08:00
|
|
|
|
_aiChannel = new ChannelEx<SecondaryCircuitInspectionAiParamModel, string>( item => item.InspectionResultId);
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
_eventPlanList = new Queue<SecondaryCircuitEventDrivenConfigOutput>();
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
//var kk = webApiRequest.GetTeleSignalData("08de219e-3524-41db-8d7d-86b547bac16a");
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
StartAsync();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task StartAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
//更新计划
|
|
|
|
|
|
_ = Task.Run(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{//每30秒更新一下配置
|
2025-11-26 14:08:37 +08:00
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
await UpdatePlans();
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
await UpdateEventPlans();
|
|
|
|
|
|
|
2025-11-26 14:08:37 +08:00
|
|
|
|
//await CheckAiChannel();
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
await Task.Delay(30000);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
//检测计划
|
2025-10-26 15:12:32 +08:00
|
|
|
|
_ = Task.Run(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
while (true)
|
2025-10-30 12:24:29 +08:00
|
|
|
|
{//每10秒检测一下计划
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
|
|
|
|
|
if (_updatedPlanOk)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckPlan();
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
await Task.Delay(10000);
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(10000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
//事件计划
|
|
|
|
|
|
_ = Task.Run(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_getEventPlanListOk)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckEventPlan();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(10000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//执行计划(两个线程同时执行)
|
2025-11-20 19:06:02 +08:00
|
|
|
|
int threadNumber = _isDebug ? 1 : 3;
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
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-11-20 19:06:02 +08:00
|
|
|
|
//调用Ai获取诊断结果
|
|
|
|
|
|
for (int i = 0; i < threadNumber; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
_ = Task.Run(async () => {
|
|
|
|
|
|
|
|
|
|
|
|
var rand = new Random(Guid.NewGuid().GetHashCode());
|
|
|
|
|
|
|
2025-11-26 14:08:37 +08:00
|
|
|
|
await foreach (var item in _aiChannel.ReadAllAsync())
|
2025-11-20 19:06:02 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 让每个线程在执行之间有不同的节奏
|
|
|
|
|
|
await Task.Delay(rand.Next(0, 300));
|
|
|
|
|
|
|
|
|
|
|
|
await CallAiAndSave(item);
|
|
|
|
|
|
|
|
|
|
|
|
await Task.Delay(500);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//更新计划
|
|
|
|
|
|
private async Task UpdatePlans()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using (await _planLock.LockAsync())
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
var oldPlan = _planList.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
_planList.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
int start = 0;
|
|
|
|
|
|
while (start < 1000)
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
var list = await _webApiRequest.GetSecondaryCircuitInspectionPlanListAsync(start);
|
|
|
|
|
|
if (list != null)
|
|
|
|
|
|
{
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
List<SecondaryCircuitInspectionPlanStateModel> planlist = new List<SecondaryCircuitInspectionPlanStateModel>();
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
foreach (var item in list)
|
|
|
|
|
|
{
|
|
|
|
|
|
planlist.Add(new SecondaryCircuitInspectionPlanStateModel() { Plan = item });
|
|
|
|
|
|
}
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
_planList.Enqueue(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (list.Count < 10)
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
start += 10;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-11-20 19:06:02 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
//获取到绑定信息,可以更新全部状态了
|
|
|
|
|
|
if (_planList.Count > 0)
|
|
|
|
|
|
_updatedPlanOk = true;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_updatedPlanOk = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
//更新事件计划
|
|
|
|
|
|
private async Task UpdateEventPlans()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using (await _eventPlanLock.LockAsync())
|
|
|
|
|
|
{
|
|
|
|
|
|
_eventPlanList.Clear();
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
int start = 0;
|
|
|
|
|
|
while (start < 1000)
|
|
|
|
|
|
{
|
|
|
|
|
|
var list = await _webApiRequest.GetCircuitEventDrivenConfigAsync(start);
|
|
|
|
|
|
if (list != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var item in list)
|
|
|
|
|
|
_eventPlanList.Enqueue(item);
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
if (list.Count < 10)
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
start += 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-10-30 12:24:29 +08:00
|
|
|
|
}
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
//获取到绑定信息,可以更新全部状态了
|
|
|
|
|
|
if (_eventPlanList.Count > 0)
|
|
|
|
|
|
_getEventPlanListOk = true;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
using (await _planLock.LockAsync())
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
|
|
|
|
|
planList = _planList.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DateTime now = DateTime.Now;
|
|
|
|
|
|
int week = DateTime.Now.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)DateTime.Now.DayOfWeek;
|
|
|
|
|
|
|
|
|
|
|
|
if (now.Day != _planCheckDay)
|
|
|
|
|
|
{//翻天,清空已执行标记
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using (await _planLock.LockAsync())
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
await PushPlanToChannel(1, item.Plan);
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using (await _planLock.LockAsync())
|
2025-10-30 12:24:29 +08:00
|
|
|
|
{
|
|
|
|
|
|
item.ExecuteTime = DateTime.Now;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
ret = true;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckPlan:发生错误");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
//将需要执行的计划写到队列里
|
2025-11-20 19:06:02 +08:00
|
|
|
|
private async Task<bool> PushPlanToChannel(int triggerType, SecondaryCircuitInspectionPlanOutput plan)
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ret = true;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in plan.InspectionItems)
|
|
|
|
|
|
{
|
2025-10-30 12:24:29 +08:00
|
|
|
|
string id = item.Id.ToString();
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
if(item.IsActive && !string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(id))
|
|
|
|
|
|
{
|
|
|
|
|
|
//写到队列里,由队列控制执行频率
|
2025-11-20 19:06:02 +08:00
|
|
|
|
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(triggerType, item));
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - ExecutePlan:发生错误");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
//执行计划
|
2025-11-20 19:06:02 +08:00
|
|
|
|
private async Task ExecutePlan(SecondaryCircuitInspectionItemOutputEx itemEx)
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
2025-11-21 20:04:39 +08:00
|
|
|
|
//string func2 = "";
|
|
|
|
|
|
//string data2 = "";
|
2025-10-26 15:12:32 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
var item = itemEx.Item;
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
string id = item.Id.ToString();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(id))
|
2025-10-26 15:12:32 +08:00
|
|
|
|
{
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
SecondaryCircuitInspectionJsDataModel jsData = new SecondaryCircuitInspectionJsDataModel();
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
jsData.TimeWindowSeconds = item.TimeWindowSeconds;
|
|
|
|
|
|
jsData.TimeWindowCount = item.TimeWindowCount;
|
|
|
|
|
|
jsData.TimeWindowType = item.TimeWindowType;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
jsData.TelemetryConfigs = item.TelemetryConfigs;
|
|
|
|
|
|
jsData.TelesignalConfigs = item.TelesignalConfigs;
|
|
|
|
|
|
jsData.CameraPresets = item.CameraPresets;
|
|
|
|
|
|
jsData.DeviceConfigs = item.DeviceConfigs;
|
|
|
|
|
|
jsData.VariantConfigs = item.VariantConfigs;
|
|
|
|
|
|
jsData.GatewayConfigs = item.GatewayConfigs;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
var engine = new Jint.Engine();
|
2025-10-30 12:24:29 +08:00
|
|
|
|
engine.Execute(item.CalculationExpression);
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
ObjectInstance? saveObj = null;
|
|
|
|
|
|
|
|
|
|
|
|
var sw = Stopwatch.StartNew();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < item.TimeWindowCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
var tasks = new List<Task>();
|
|
|
|
|
|
|
|
|
|
|
|
Task<List<ZzDataResultModel>>? t1 = null;
|
|
|
|
|
|
Task<List<ZzDataResultModel>>? t2 = null;
|
|
|
|
|
|
Task<List<DeviceDzData>>? t3 = null;
|
|
|
|
|
|
Task<List<InspectionResultData>>? t4 = null;
|
|
|
|
|
|
Task<List<ZzDataResultModel>>? t5 = null;
|
|
|
|
|
|
Task<List<ZzDataResultModel>>? t6 = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (item.TelemetryConfigCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
t1 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 1);
|
|
|
|
|
|
tasks.Add(t1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.TelesignalConfigCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
t2 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 2);
|
|
|
|
|
|
tasks.Add(t2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.DeviceConfigCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
t3 = GetSourceDataAsync<List<DeviceDzData>>(id, 3);
|
|
|
|
|
|
tasks.Add(t3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.CameraPresetCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
t4 = GetSourceDataAsync<List<InspectionResultData>>(id, 4);
|
|
|
|
|
|
tasks.Add(t4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.GatewayConfigCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
t5 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 5);
|
|
|
|
|
|
tasks.Add(t5);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.VariantConfigCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
t6 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 6);
|
|
|
|
|
|
tasks.Add(t6);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await Task.WhenAll(tasks);
|
|
|
|
|
|
|
|
|
|
|
|
if (t1 != null) jsData.StoreData.TelemetryData = await t1;
|
|
|
|
|
|
if (t2 != null) jsData.StoreData.TeleSignalData = await t2;
|
|
|
|
|
|
if (t3 != null) jsData.StoreData.SettingData = await t3;
|
|
|
|
|
|
if (t4 != null) jsData.StoreData.PresetData = await t4;
|
|
|
|
|
|
if (t5 != null) jsData.StoreData.GatewayData = await t5;
|
|
|
|
|
|
if (t6 != null) jsData.StoreData.VariantData = await t6;
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
2025-11-26 14:08:37 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
aiParamModel.InspectionResultId = inspectionResultId;
|
|
|
|
|
|
|
2025-11-26 14:08:37 +08:00
|
|
|
|
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);
|
2025-11-20 19:06:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch(Exception ex)
|
|
|
|
|
|
{
|
2025-11-20 19:06:02 +08:00
|
|
|
|
_logger.LogError(ex, $"SecondaryCircuitInspectionPlanService 执行巡检项失败:{itemEx.Item.Id.ToString()}");
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
2025-11-20 19:06:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-26 14:08:37 +08:00
|
|
|
|
//获取没有调用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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
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("巡检结果为:");
|
2025-11-26 14:08:37 +08:00
|
|
|
|
sb.AppendLine($"巡检状态: {aiParamModel.Status}");
|
|
|
|
|
|
sb.AppendLine($"计算过程: {aiParamModel.CalculationProcess}");
|
|
|
|
|
|
sb.AppendLine($"巡检结果: {aiParamModel.InspectionResult}");
|
|
|
|
|
|
sb.AppendLine($"校验结果: {aiParamModel.VerificationResult}");
|
2025-11-20 19:06:02 +08:00
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
sb.AppendLine("请按以下格式生成排除建议,直接建议内容,每条建议前加序号,如1、,每条建议间加换行符");
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
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("巡检结果为:");
|
2025-11-26 14:08:37 +08:00
|
|
|
|
sb.AppendLine($"巡检状态: {aiParamModel.Status}");
|
|
|
|
|
|
sb.AppendLine($"计算过程: {aiParamModel.CalculationProcess}");
|
|
|
|
|
|
sb.AppendLine($"巡检结果: {aiParamModel.InspectionResult}");
|
|
|
|
|
|
sb.AppendLine($"校验结果: {aiParamModel.VerificationResult}");
|
2025-11-20 19:06:02 +08:00
|
|
|
|
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();
|
2025-10-30 12:24:29 +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;
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
case 5:
|
|
|
|
|
|
result = await _webApiRequest.GetGatewayInfoData(id);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
|
result = await _webApiRequest.GetVirtualPointInfoData(id);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
default:
|
|
|
|
|
|
throw new ArgumentException("Invalid type");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(result != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(200);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
return (T)result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//执行事件计划
|
|
|
|
|
|
private async Task<bool> CheckEventPlan()
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (_eventPlanList == null)
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
SecondaryCircuitEventDrivenConfigOutput[] planList;
|
2025-11-20 19:06:02 +08:00
|
|
|
|
using (await _eventPlanLock.LockAsync())
|
2025-10-30 12:24:29 +08:00
|
|
|
|
{
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
SecondaryCircuitInspectionJsDataModel jsData = new SecondaryCircuitInspectionJsDataModel();
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
var tasks = new List<Task>();
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
Task<List<ZzDataResultModel>>? t1 = null;
|
|
|
|
|
|
Task<List<ZzDataResultModel>>? t2 = null;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
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;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
engine.Execute(item.TriggerExpression);
|
|
|
|
|
|
|
2025-11-20 19:06:02 +08:00
|
|
|
|
bool canCheck = true;
|
|
|
|
|
|
|
|
|
|
|
|
if(!_isDebug)
|
|
|
|
|
|
{
|
|
|
|
|
|
canCheck = engine.Invoke("calculate", jsData).AsBoolean();
|
|
|
|
|
|
}
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
if(canCheck)
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(item.MandatoryWaitSeconds * 1000);
|
|
|
|
|
|
|
2025-10-30 12:40:42 +08:00
|
|
|
|
//CheckPlanFormIds(item.SecondaryCircuitInspectionEventItems);
|
|
|
|
|
|
|
|
|
|
|
|
//执行巡检
|
|
|
|
|
|
foreach(var planItem in item.SecondaryCircuitInspectionEventItems)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(planItem.CalculationExpression) && !string.IsNullOrWhiteSpace(planItem.Id.ToString()))
|
|
|
|
|
|
{
|
|
|
|
|
|
//写到队列里,由队列控制执行频率
|
2025-11-20 19:06:02 +08:00
|
|
|
|
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(2, planItem));
|
2025-10-30 12:40:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-30 12:24:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckEventPlan:发生错误");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
2025-11-26 14:08:37 +08:00
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
////获取遥测数据
|
2025-11-26 14:08:37 +08:00
|
|
|
|
//public async Task<List<ZzDataResultModel>> CallYCByDataIdAsync(ZzDataRequestModel request, CancellationToken cancellationToken = default)
|
2025-10-30 12:24:29 +08:00
|
|
|
|
//{
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
// {
|
2025-11-26 14:08:37 +08:00
|
|
|
|
// List<ZzDataResultModel> list = jArray.ToObject<List<ZzDataResultModel>>();
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
// return list;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
2025-11-26 14:08:37 +08:00
|
|
|
|
// catch (Exception ex)
|
2025-10-30 12:24:29 +08:00
|
|
|
|
// {
|
|
|
|
|
|
// _logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CallYCByDataIdAsync:发生错误");
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// return null;
|
|
|
|
|
|
//}
|
2025-10-26 15:12:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|