Compare commits

...

4 Commits

Author SHA1 Message Date
qsp89
326d50630a 邱蜀鹏合并程序:
1、每10分钟,主动判断一下遥信全体
2、程序启动时,提取遥测遥信全体命令,每个命令闹间隔1秒
3、提取遥测遥信全体命令完成后(10分钟超时),再执行巡检和定位发送提取遥测信息
4、每30秒请求一次虚点
5、其他一系列修改
2026-01-08 15:51:20 +08:00
qsp89
1fc9614257 Merge branch 'master' of http://175.154.160.23:3237/guorui/SOMS 2026-01-08 15:29:51 +08:00
qsp89
1d0d933aa5 合并 2025-12-03 13:03:37 +08:00
qsp89
52290ac5f7 修改性能状态推送不全问题
遥测数据返回为0,添加调试记录
2025-11-26 15:02:58 +08:00
22 changed files with 1810 additions and 619 deletions

View File

@ -378,6 +378,28 @@
<param name="videos">Video devices list</param>
<param name="presets">Preset points list</param>
</member>
<member name="T:YunDa.SOMS.ExternalInteraction.Application.MainStation.HandleUploadInspectionItems.Services.IBatchOperationService">
<summary>
Batch operation service interface for optimizing database operations
</summary>
</member>
<member name="M:YunDa.SOMS.ExternalInteraction.Application.MainStation.HandleUploadInspectionItems.Services.IBatchOperationService.BatchInsertInspectionResultsAsync(System.Collections.Generic.List{YunDa.SOMS.DataTransferObject.MainstationData.OriginalInspectionStoreResult},System.Threading.CancellationToken)">
<summary>
Batch insert inspection results into MongoDB with automatic partitioning
</summary>
<param name="items">Collection of inspection results to insert</param>
<param name="cancellationToken">Cancellation token for async operation</param>
<returns>True if all batches inserted successfully, false otherwise</returns>
</member>
<member name="M:YunDa.SOMS.ExternalInteraction.Application.MainStation.HandleUploadInspectionItems.Services.IBatchOperationService.BatchInsertInspectionResultsAsync(System.Collections.Generic.List{YunDa.SOMS.DataTransferObject.MainstationData.OriginalInspectionStoreResult},System.Int32,System.Threading.CancellationToken)">
<summary>
Batch insert inspection results with custom batch size
</summary>
<param name="items">Collection of inspection results to insert</param>
<param name="batchSize">Custom batch size for partitioning</param>
<param name="cancellationToken">Cancellation token for async operation</param>
<returns>True if all batches inserted successfully, false otherwise</returns>
</member>
<member name="T:YunDa.SOMS.ExternalInteraction.Application.PatternRecognition.MainstationGetTempService">
<summary>
对外通信-主站额外需求

View File

@ -1417,6 +1417,12 @@
</summary>
<returns></returns>
</member>
<member name="M:YunDa.SOMS.MongoDB.Application.Inspection.InspectionItemResultAppService.TestGetAlarmMessage(System.String,System.String,System.String)">
<summary>
测试报警api
</summary>
<returns></returns>
</member>
<member name="M:YunDa.SOMS.MongoDB.Application.Inspection.InspectionItemResultAppService.GetAlarmMessage(System.Nullable{System.Guid},System.Int32,System.String)">
<summary>
获取报警信息

View File

@ -150,6 +150,10 @@ namespace YunDa.Server.ISMSTcp.Configuration
/// </summary>
public string GetOpticalCableConfigUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/OpticalCable/GetList";
//获取架式设备配置
public string GetRackEquipmentConfig => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/RackEquipment/GetList";
/// <summary>
/// 保存巡检计划执行结果
/// </summary>

View File

@ -37,6 +37,8 @@ namespace YunDa.Server.ISMSTcp.Controllers
private readonly TelesignalisationHandle _telesignalisationHandle;
private readonly GwErrorRatioService _gwErrorRatioService;
private readonly VirtualTerminalHandler _virtualTerminalHandler;
private readonly SecondaryCircuitInspectionPlanService _secondaryCircuitInspectionPlanService;
private readonly ThingService _thingService;
/// <summary>
/// 构造函数
@ -58,7 +60,9 @@ namespace YunDa.Server.ISMSTcp.Controllers
TelemeteringHandle telemeteringHandle,
TelesignalisationHandle telesignalisationHandle,
GwErrorRatioService gwErrorRatioService,
VirtualTerminalHandler virtualTerminalHandler)
VirtualTerminalHandler virtualTerminalHandler,
SecondaryCircuitInspectionPlanService secondaryCircuitInspectionPlanService,
ThingService thingService)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_queryService = queryService ?? throw new ArgumentNullException(nameof(queryService));
@ -70,6 +74,8 @@ namespace YunDa.Server.ISMSTcp.Controllers
_telesignalisationHandle = telesignalisationHandle ?? throw new ArgumentNullException(nameof(telesignalisationHandle));
_gwErrorRatioService = gwErrorRatioService ?? throw new ArgumentNullException(nameof(gwErrorRatioService));
_virtualTerminalHandler = virtualTerminalHandler ?? throw new ArgumentNullException(nameof(virtualTerminalHandler));
_secondaryCircuitInspectionPlanService = secondaryCircuitInspectionPlanService ?? throw new ArgumentNullException(nameof(secondaryCircuitInspectionPlanService));
_thingService = thingService ?? throw new ArgumentNullException( nameof(thingService));
}
/// <summary>
@ -255,49 +261,6 @@ namespace YunDa.Server.ISMSTcp.Controllers
return StatusCode(500, $"服务器内部错误: {ex.Message}");
}
private List<string> GetValidZzCmd(string cmdName, List<string> ids)
{
const int maxLength = 250;
var cmds = new List<string>();
// 开始构建
string prefix = cmdName + "|"; // 固定前缀
int prefixLength = prefix.Length;
var currentIds = new List<string>();
int currentLength = prefixLength; // 当前命令的长度(含前缀)
foreach (var id in ids)
{
// 如果添加这个ID会超长则先生成一个命令
int idLength = (currentIds.Count == 0 ? id.Length : (1 + id.Length)); // 第一个ID不需要 '#'
if (currentLength + idLength > maxLength)
{
// 将当前批次加入 cmds
cmds.Add(prefix + string.Join("#", currentIds));
// 清空并重建
currentIds.Clear();
currentLength = prefixLength;
}
// 添加新的 ID
currentIds.Add(id);
currentLength += idLength;
}
// 收尾:如果还有剩余 IDs生成最后一个命令
if (currentIds.Count > 0)
{
cmds.Add(prefix + string.Join("#", currentIds));
}
return cmds;
}
/// <summary>
/// 根据数据ID召唤遥测数据
/// </summary>
@ -320,21 +283,18 @@ namespace YunDa.Server.ISMSTcp.Controllers
[FromBody] ZzDataRequestModel request,
CancellationToken cancellationToken = default)
{
var id = request.Id;
var ids = request.Id;
var times = request.Times;
id.RemoveAll(string.IsNullOrWhiteSpace);
ids.RemoveAll(string.IsNullOrWhiteSpace);
//string cmd = string.Format("CallYCByDataID|{0}", string.Join("#", id));
_logger.LogWarning("收到 CallYCByDataId");
//System.Console.WriteLine($"发送遥测命令:{cmd}");
var cmds = GetValidZzCmd("CallYCByDataID", id);
try
{
// 参数验证
if (id.Count == 0)
if (ids.Count == 0)
{
return BadRequest(new
{
@ -344,98 +304,46 @@ namespace YunDa.Server.ISMSTcp.Controllers
});
}
_logger.LogInformation("收到遥测数据召唤请求 - cmd: {cmd}", string.Join("#", id));
// 发送TCP消息
List<ZzDataResultModel>? ycDataList = null;
DateTime cmdTime = DateTime.Now;
int sucessCount = 0;
if(request.TimeWindowType == 1 || request.TimeWindowType == 2)
for (int i = 0; i < 3; ++i)
{
for (int i = 0; i < times; i++)
ycDataList = await _telemeteringHandle.GetYCDataByDataIds(ids, times, request.TimeWindowType, cancellationToken);
if (ycDataList.Count == ids.Count)
{
bool isSuccess = true;
break;
}
foreach (var cmd in cmds)
{
var sendResult = await SendTcpMessageAsync(cmd, cancellationToken);
isSuccess &= sendResult.Success;
if (!isSuccess)
{
break;
}
else
{
await Task.Delay(5);
}
}
if (isSuccess)
{
sucessCount++;
}
if(request.TimeWindowType == 0)
await Task.Delay(1000);
}
}
else
{
sucessCount = 10;
}
if (times - sucessCount < 3)
if (ycDataList?.Count == ids.Count)
{
List<ZzDataResultModel>? ycDataList = null;
var sw = Stopwatch.StartNew();
while (sw.ElapsedMilliseconds < 10 * 1000)
return Ok(new
{
ycDataList = await _telemeteringHandle.GetYCDataByDataIds(id, times, request.TimeWindowType, cancellationToken, cmdTime);
if (ycDataList.Count == id.Count)
break;
await Task.Delay(100);
}
if (ycDataList?.Count == id.Count)
{
return Ok(new
{
success = true,
message = "获取遥测数据成功",
data = ycDataList,
count = ycDataList.Count,
timestamp = DateTime.Now
});
}
else
{
return StatusCode(500, new
{
success = false,
message = "发送命令成功,获取遥测数据超时",
timestamp = DateTime.Now
});
}
success = true,
message = "获取遥测数据成功",
data = ycDataList,
count = ycDataList.Count,
timestamp = DateTime.Now
});
}
else
{
return StatusCode(500, new
{
success = false,
message = $"发送命令失败",
message = "发送命令成功,获取遥测数据超时",
timestamp = DateTime.Now
});
}
}
catch (OperationCanceledException)
{
_logger.LogWarning("遥测数据召唤请求被取消 - cmd: {cmd}", string.Join("#", id));
_logger.LogWarning("遥测数据召唤请求被取消 - cmd: {cmd}", string.Join("#", ids));
return StatusCode(504, new
{
success = false,
@ -445,7 +353,7 @@ namespace YunDa.Server.ISMSTcp.Controllers
}
catch (Exception ex)
{
_logger.LogError(ex, "处理遥测数据召唤请求时发生异常 - cmd: {cmd}", string.Join("#", id));
_logger.LogError(ex, "处理遥测数据召唤请求时发生异常 - cmd: {cmd}", string.Join("#", ids));
return StatusCode(500, new
{
success = false,
@ -1163,6 +1071,103 @@ namespace YunDa.Server.ISMSTcp.Controllers
// });
// }
//}
[HttpGet("UpdateConfig")]
public async Task<IActionResult> UpdateConfig([FromQuery] int configType)
{
try
{
switch (configType)
{
//更新计划巡检、事件巡检
case 1:
await _secondaryCircuitInspectionPlanService.UpdatePlanConfig();
break;
//更新网线配置
case 2:
await _thingService.UpdateNetworkCableConfig();
break;
//更新光纤配置
case 3:
await _thingService.UpdateOpticalFiberConfig();
break;
//更新光缆配置
case 4:
await _thingService.UpdateOpticalCableConfig();
break;
//更新架式设备配置、孪生体与遥测数据绑定关系、设备和孪生体的关联
case 5:
await _thingService.UpdateRackEquipmentConfig();
await _thingService.UpdateDeviceBindingConfig();
await _thingService.UpdateSimDatasConfig();
break;
default:
return BadRequest(new
{
success = false,
message = "错误configType",
timestamp = DateTime.Now
});
}
_logger.LogInformation($"收到配置更新命令configType = {configType}");
return Ok(new
{
success = true,
message = "更新成功",
timestamp = DateTime.Now
});
}
catch (Exception ex)
{
return StatusCode(500, $"更新失败: {ex.Message}");
}
}
[HttpGet("ClearAllThingAlarm")]
public async Task<IActionResult> ClearAllThingAlarm()
{
try
{
bool success = await _thingService.ClearAllAlarm();
if (success)
{
return Ok(new
{
success = true,
message = "清除成功",
timestamp = DateTime.Now
});
}
else
{
return BadRequest(new
{
success = false,
message = "清除失败",
timestamp = DateTime.Now
});
}
}
catch (Exception ex)
{
return StatusCode(500, $"清除失败: {ex.Message}");
}
}
}

View File

@ -107,6 +107,9 @@ namespace YunDa.Server.ISMSTcp.Domain
/// </summary>
string GetOpticalCableConfigUri { get; }
//获取架式设备配置
string GetRackEquipmentConfig { get; }
/// <summary>
/// 保存巡检计划执行结果
/// </summary>
@ -251,6 +254,9 @@ namespace YunDa.Server.ISMSTcp.Domain
/// </summary>
public string GetOpticalCableConfigUri => _config.GetOpticalCableConfigUri;
//获取架式设备配置
public string GetRackEquipmentConfig => _config.GetRackEquipmentConfig;
/// <summary>
/// 保存巡检计划执行结果
/// </summary>

View File

@ -700,12 +700,12 @@ namespace YunDa.Server.ISMSTcp.Domain
/// <summary>
/// 获取孪生体与遥测数据绑定关系
/// </summary>
public async Task<List<ThingDeviceBindingModel>> GetThingDeviceBindingConfigAsync()
public async Task<List<ThingDeviceBindingModel>> GetThingDeviceBindingConfigAsync(int pageIndex, int pageSize)
{
try
{
var response = await HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.ThingDeviceBindingConfigUri, new object());
var response = await HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.ThingDeviceBindingConfigUri, new { pageIndex = pageIndex, pageSize = pageSize });
if (response != null)
{
@ -908,12 +908,12 @@ namespace YunDa.Server.ISMSTcp.Domain
/// <summary>
/// 获取网线配置
/// </summary>
public async Task<List<OpticalFiberConfigModel>> GetNetworkCableConfigAsync()
public async Task<List<OpticalFiberConfigModel>> GetNetworkCableConfigAsync(int pageIndex, int pageSize)
{
try
{
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetNetworkCableConfigUri, new object()));
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetNetworkCableConfigUri, new { pageIndex = pageIndex, pageSize = pageSize }));
if (response != null)
{
@ -947,12 +947,12 @@ namespace YunDa.Server.ISMSTcp.Domain
/// <summary>
/// 获取光纤配置
/// </summary>
public async Task<List<OpticalFiberConfigModel>> GetOpticalFiberConfigAsync()
public async Task<List<OpticalFiberConfigModel>> GetOpticalFiberConfigAsync(int pageIndex, int pageSize)
{
try
{
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetOpticalFiberConfigUri, new object()));
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetOpticalFiberConfigUri, new { pageIndex = pageIndex, pageSize = pageSize }));
if (response != null)
{
@ -985,11 +985,11 @@ namespace YunDa.Server.ISMSTcp.Domain
/// <summary>
/// 获取光缆配置
/// </summary>
public async Task<List<OpticalFiberConfigModel>> GetOpticalCableConfigAsync()
public async Task<List<OpticalFiberConfigModel>> GetOpticalCableConfigAsync(int pageIndex, int pageSize)
{
try
{
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetOpticalCableConfigUri, new { includeInspectionItems = true }));
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetOpticalCableConfigUri, new { pageIndex = pageIndex, pageSize = pageSize }));
if (response != null)
{
@ -1007,6 +1007,31 @@ namespace YunDa.Server.ISMSTcp.Domain
return null;
}
///获取架式设备配置
public async Task<List<OpticalFiberConfigModel>> GetRackEquipmentConfigAsync(int pageIndex, int pageSize)
{
try
{
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetRackEquipmentConfig, new { pageIndex = pageIndex, pageSize = pageSize }));
if (response != null)
{
var result = ExtractDataFromAbpResponse<List<OpticalFiberConfigModel>>(response);
return result;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error Call GetRackEquipmentConfigAsync Api");
}
return null;
}
/// <summary>
/// 保存巡检计划执行结果
/// </summary>

View File

@ -132,6 +132,7 @@ namespace YunDa.Server.ISMSTcp.Extensions
services.AddSingleton<GwErrorRatioService>();
services.AddSingleton<ZzTcpService>();
services.AddSingleton<ZzDataCacheContainerInit>();
services.AddSingleton<ZzDataCmdService>();
// 日志过滤器配置
services.Configure<YunDa.Server.ISMSTcp.Filters.Configuration.LogFilterConfiguration>(

View File

@ -0,0 +1,19 @@
using System;
namespace YunDa.Server.ISMSTcp.Helpers
{
public static class StringHelper
{
public static string GetAfterLastOfAny(string input, params char[] separators)
{
if (string.IsNullOrEmpty(input) || separators == null || separators.Length == 0)
return input;
int index = input.AsSpan().LastIndexOfAny(separators);
return index >= 0
? input.Substring(index + 1)
: input;
}
}
}

View File

@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using YunDa.Server.ISMSTcp.Services;
using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection;
using YunDa.SOMS.DataTransferObject.MaintenanceAndOperations.SecondaryEquipment;
using YunDa.SOMS.Entities.DataMonitoring.SecondaryCircuitInspection;
namespace YunDa.Server.ISMSTcp.Models
{
//带执行状态的巡检计划结构体
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 string PlanId { get; set; }
public SecondaryCircuitInspectionItemOutputEx(int triggerType, SecondaryCircuitInspectionItemOutput item, string planId)
{
TriggerType = triggerType;
Item = item;
PlanId = planId;
}
}
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 bool IsAllDataNull()
{
return (TelemetryData == null && TeleSignalData == null && SettingData == null &&
PresetData == null && GatewayData == null && VariantData == null);
}
}
//巡检结果
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返回的处理结果"
}
}

View File

@ -0,0 +1,174 @@
using Esprima.Ast;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using YunDa.Server.ISMSTcp.Helpers;
using YunDa.SOMS.DataTransferObject.MaintenanceAndOperations.SecondaryEquipment;
using YunDa.SOMS.Entities.ExternalEntities.BeijingYounuo;
namespace YunDa.Server.ISMSTcp.Models
{
//ThingApiService配置
public class ThingApiServiceSettings
{
public string ApiServerUrl { get; set; } = string.Empty;
public string LoginCode { get; set; } = string.Empty;
public string LoginKey { get; set; } = string.Empty;
public string EsAuthorization { get; set; } = string.Empty;
}
public class ThingDeviceStatusModel
{
public string TwinID { get; set; } = string.Empty;
public string Metric { get; set; } = string.Empty;
public string Val { get; set; } = string.Empty;
}
public class ThingDeviceBindingModel
{
public string Description { get; set; } = string.Empty;
public string TwinID { get; set; } = string.Empty;
public string Metric { get; set; } = string.Empty; //格式1:绿灯1|0:红灯1
public string Val { get; set; } = string.Empty;
public int TwinDataBindingType { get; set; }
public string Remark { get; set; } = string.Empty;
public bool IsActive { get; set; }
public string Id { get; set; } = string.Empty;
public TelesignalisationConfigurationModel TelesignalisationConfiguration { get; set; }
public List<ThingDevicePushDataModel> MetricList { get; set; }
public List<ThingDevicePushDataModel> ValList { get; set; }
}
public class TelesignalisationConfigurationModel
{
public string Name { get; set; } = string.Empty;
public int DispatcherAddress { get; set; }
public string IsmsbaseYXId { get; set; } = string.Empty;
public int RemoteType { get; set; }
public string Id { get; set; } = string.Empty;
}
public class ThingDevicePushDataModel
{
public string Val { get; set; } = string.Empty;
public string Cmd { get; set; } = string.Empty;
public string CloseCmd { get; set; } = string.Empty;
}
//性能数据
public class TingSimDataModel
{
public string TwinId { get; set; } = string.Empty;
public string EquipmentInfoId { get; set; } = string.Empty;
public string EquipmentInfoName { get; set; } = string.Empty;
public bool IsActive { get; set; }
}
//告警推送数据
public class TingAlarmPushDataModel
{
public string TwinId { get; set; } = string.Empty; //孪生体Id ******
public int Severity { get; set; } //报警级别
public int Status { get; set; } //状态1=告警2=恢复
public string SourceAlertKey { get; set; } = string.Empty; //指标名称 ******
public string Summary { get; set; } = string.Empty; //告警内容
public string SourceIdentifier { get; set; } = string.Empty;//事件源的唯一标识 ******
public string LastOccurrence { get; set; } = string.Empty; //告警最后一次发生时间
static public string[] AlarmKeyword = { "分", "未同步", "告警", "异常", "断", "未就绪", "无效", "未复归", "动作", "检修", "未储能", "跳闸", "失步", "分位", "故障", "产生", "不定", "中断", "不良", "失灵" };
//static public string[] RecoveryKeyword = { "正常", "消失", "通", "同步", "解除", "复归", "已复归", "返回", "已储能", "恢复" };
public bool Parse(string twinId, AlertData alertData)
{
List<string> alarmLevel = new List<string> { "", "事故", "异常", "变位", "越限", "告知" };
Severity = alarmLevel.IndexOf(alertData.AlertLevel);
if (Severity < 1)
return false;
else if (Severity == 5)
Severity = 4;
bool isAlarm = AlarmKeyword.Any(e => alertData.Content.Contains(e));
TwinId = twinId;
Status = isAlarm ? 1 : 2;
SourceAlertKey = alertData.Alerter; //Alerter是唯一的相同的告警TwinId、SourceAlertKey和SourceIdentifier要相同
Summary = $"{ StringHelper.GetAfterLastOfAny(alertData.Alerter, '|')} : {StringHelper.GetAfterLastOfAny(alertData.Content, '')}";
SourceIdentifier = alertData.Alerter; //Alerter是唯一的
LastOccurrence = alertData.AlertTime.ToString("yyyy-MM-dd HH:mm:ss");
if (Summary.Contains("保护CPU连接状态"))
{
int kk = 0;
Debug.WriteLine($"{Summary}\ttwinId ={twinId}");
}
return true;
}
public void Parse(string twinId, int severity, bool isAlarm, string alarmUuid, string alarmContent, string alarmTime)
{
TwinId = twinId;
Severity = severity;
Status = isAlarm ? 1 : 2;
SourceAlertKey = alarmUuid;
Summary = $"{alarmUuid}{alarmContent}";
SourceIdentifier = alarmUuid;
LastOccurrence = alarmTime;
}
}
//光纤
public class OpticalFiberConfigModel : NetworkCable
{
public string VirtualPointCode { get; set; } = string.Empty;
public string[] VirtualPointCodes => string.IsNullOrWhiteSpace(VirtualPointCode) ? Array.Empty<string>() : VirtualPointCode.Split('', ',');
public string LinkageData { get; set; } = string.Empty;
public string[] LinkageDatas => string.IsNullOrWhiteSpace(LinkageData) ? Array.Empty<string>() : LinkageData.Split('', ',');
public string[] LogicalExpressions => string.IsNullOrWhiteSpace(LogicalExpression) ? Array.Empty<string>() : LogicalExpression.Split('', ',');
}
}

View File

@ -7,6 +7,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using YunDa.Server.ISMSTcp.Extensions;
using YunDa.Server.ISMSTcp.Models;
using YunDa.Server.ISMSTcp.Services;
namespace YunDa.Server.ISMSTcp

View File

@ -0,0 +1,62 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace YunDa.Server.ISMSTcp.Services
{
public static class AppInit
{
private static readonly TaskCompletionSource<bool> _tcs =
new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
private static int _state = 0; // 0 = 未初始化; 1 = 已初始化
private static readonly DateTime _startTime = DateTime.UtcNow;
public static readonly TimeSpan DefaultTimeOut = TimeSpan.FromMinutes(10);
/// <summary>立即返回初始化状态</summary>
public static bool IsInitialized() => Volatile.Read(ref _state) == 1;
/// <summary>从程序启动开始计算超时,如果初始化完成或超过超时返回 true</summary>
public static bool IsInitialized(TimeSpan timeout)
{
if (IsInitialized())
return true;
return (DateTime.UtcNow - _startTime) >= timeout;
}
/// <summary>等待初始化完成(无限等待)</summary>
public static Task WaitAsync() => _tcs.Task;
/// <summary>等待初始化完成(从调用时间开始计时超时)</summary>
public static async ValueTask<bool> WaitAsync(TimeSpan timeout)
{
if (IsInitialized())
return true;
using var cts = new CancellationTokenSource(timeout);
try
{
await _tcs.Task.WaitAsync(cts.Token);
return IsInitialized();
}
catch (OperationCanceledException)
{
return false;
}
}
/// <summary>标记初始化完成(线程安全,只会触发一次)</summary>
public static void MarkInitialized()
{
if (Interlocked.Exchange(ref _state, 1) != 0)
return;
_tcs.TrySetResult(true);
}
}
}

View File

@ -76,6 +76,9 @@ namespace YunDa.Server.ISMSTcp.Services
//二次回路巡检计划
private readonly SecondaryCircuitInspectionPlanService _secondaryCircuitInspectionPlanService;
private readonly ZzDataCmdService _zzDataCmdService;
public DataProcessor(
ILogger<DataProcessor> logger,
MessageParser messageParser,
@ -94,7 +97,8 @@ namespace YunDa.Server.ISMSTcp.Services
WebApiRequest webApiRequest,
SecondaryCircuitInspectionPlanService secondaryCircuitInspectionPlanService,
ThingService thingService,
GwErrorRatioService gwErrorRatioService)
GwErrorRatioService gwErrorRatioService,
ZzDataCmdService zzDataCmdService)
{
_logger = logger;
_messageParser = messageParser;
@ -114,6 +118,7 @@ namespace YunDa.Server.ISMSTcp.Services
_secondaryCircuitInspectionPlanService = secondaryCircuitInspectionPlanService ?? throw new ArgumentNullException(nameof(secondaryCircuitInspectionPlanService));
_thingService = thingService ?? throw new ArgumentNullException(nameof(thingService));
_gwErrorRatioService = gwErrorRatioService ?? throw new ArgumentNullException(nameof(gwErrorRatioService));
_zzDataCmdService = zzDataCmdService ?? throw new ArgumentNullException(nameof(zzDataCmdService));
}
/// <summary>
@ -180,6 +185,8 @@ namespace YunDa.Server.ISMSTcp.Services
{
HandleRecvMsg(contentToken, "FAULTRPT");
var alertDatas = contentToken.ToObject<List<AlertData>>();
if(alertDatas != null)
Log.Warning($"收到故障报告:数量{alertDatas.Count}");
foreach (var item in alertDatas)
{
var json = JsonConvert.SerializeObject(item);
@ -226,6 +233,7 @@ namespace YunDa.Server.ISMSTcp.Services
// 批量处理遥测数据
await _telemeteringHandle.ProcessYCDataAsync(contentArray);
//_logger.LogWarning($"收到遥测数据,数量:{contentArray.Count}");
}
else
{
@ -464,6 +472,10 @@ namespace YunDa.Server.ISMSTcp.Services
deviceUpdates.Add((deviceId, isOnline));
processedCount++;
//更新装置状态
_zzDataCmdService.UpdateDeviceCommState(deviceId, isOnline);
}
catch (Exception itemEx)
{
@ -610,10 +622,10 @@ namespace YunDa.Server.ISMSTcp.Services
//await ProcessFaultReportsAsync(alertDatas);
//报警上传
await _webApiRequest.UploadAlertMessageAsync(alertDatas);
_webApiRequest.UploadAlertMessageAsync(alertDatas);
//报警推送到孪生体
await _thingService.UpdateAlarmDatas(alertDatas);
_thingService.UpdateAlarmDatas(alertDatas);

View File

@ -31,6 +31,9 @@ namespace YunDa.Server.ISMSTcp.Services
_logger = logger;
_zzDataCacheContainerInit = zzDataCacheContainerInit;
_zzDataCacheContainerInit.InitGateWayBaseInfo(_cacheContainer);
ZzDataCache.SetDataCache(ZzDataCacheContainerDataType.eGW, _cacheContainer);
}
//解析数据
@ -79,7 +82,7 @@ namespace YunDa.Server.ISMSTcp.Services
}
}
//缓存遥信数据到内存
//缓存数据到内存
private void SaveDataToCache(GwErrorRatioDataModel data)
{
_cacheContainer.Write(data.GatewayID, data.ErrorRatio, data.DataTimeStamp, "", data.ErrorRatio.ToString("P2"));

View File

@ -1,5 +1,7 @@
using DotNetty.Common.Utilities;
using Abp.AutoMapper;
using DotNetty.Common.Utilities;
using Jint;
using Jint.Native;
using Jint.Native.Object;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@ -25,14 +27,13 @@ using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using YunDa.Server.ISMSTcp.Domain;
using YunDa.Server.ISMSTcp.Helpers;
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
{
@ -42,137 +43,7 @@ namespace YunDa.Server.ISMSTcp.Services
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
@ -184,12 +55,12 @@ namespace YunDa.Server.ISMSTcp.Services
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 bool _getPlanListOk = false;
private readonly Channel<SecondaryCircuitInspectionItemOutputEx> _singlePlanChannel; //巡检计划
private readonly ChannelEx<SecondaryCircuitInspectionAiParamModel, string> _aiChannel; //Ai调用
@ -199,11 +70,14 @@ namespace YunDa.Server.ISMSTcp.Services
private readonly AsyncLock _eventPlanLock = new AsyncLock();
private bool _getEventPlanListOk = false;
private readonly ZzDataCmdService _zzDataCmdService;
public SecondaryCircuitInspectionPlanService(
ILogger<SecondaryCircuitInspectionPlanService> logger,
IApiEndpoints apiEndpoints,
WebApiRequest webApiRequest,
IOptions<WebApiSettings> webApiOptions
IOptions<WebApiSettings> webApiOptions,
ZzDataCmdService zzDataCmdService
)
{
_apiEndpoints = apiEndpoints ?? throw new ArgumentNullException(nameof(apiEndpoints));
@ -212,6 +86,8 @@ namespace YunDa.Server.ISMSTcp.Services
_webApiSettings = webApiOptions.Value;
_zzDataCmdService = zzDataCmdService ?? throw new ArgumentNullException(nameof(zzDataCmdService));
_planList = new Queue<SecondaryCircuitInspectionPlanStateModel>();
_singlePlanChannel = Channel.CreateUnbounded<SecondaryCircuitInspectionItemOutputEx>();
@ -235,24 +111,32 @@ namespace YunDa.Server.ISMSTcp.Services
while (true)
{//每10分钟更新一下配置
await UpdatePlans();
await UpdateEventPlans();
await UpdatePlanConfig();
//await CheckAiChannel();
await Task.Delay(TimeSpan.FromMinutes(10));
//await Task.Delay(TimeSpan.FromMinutes(10));
if(_getPlanListOk && _getEventPlanListOk)
{
_logger.LogWarning("SecondaryCircuitInspectionPlanService 获取配置信息成功!");
break; //程序启动时,主动获取一次,之后有更新,会主动通知
}
else
{
await Task.Delay(TimeSpan.FromMinutes(3));
}
}
});
await AppInit.WaitAsync(AppInit.DefaultTimeOut);
//检测计划
_ = Task.Run(async () =>
{
while (true)
{//每10秒检测一下计划
{//每60秒检测一下计划
if (_updatedPlanOk)
if (_getPlanListOk)
{
await CheckPlan();
@ -300,7 +184,15 @@ namespace YunDa.Server.ISMSTcp.Services
await ExecutePlan(item);
await Task.Delay(500);
if(item.Item.DeviceConfigCount > 0)
{//需要判断定值的,间隔久一点
await Task.Delay(TimeSpan.FromSeconds(60));
}
else
{
await Task.Delay(TimeSpan.FromSeconds(30));
}
}
});
}
@ -327,11 +219,23 @@ namespace YunDa.Server.ISMSTcp.Services
await Task.CompletedTask;
}
public async Task UpdatePlanConfig()
{
_zzDataCmdService.ClearYcIds();
await UpdatePlans();
await UpdateEventPlans();
}
//更新计划
private async Task UpdatePlans()
{
try
{
SecondaryCircuitInspectionPlanStateModel[] planList;
using (await _planLock.LockAsync())
{
var oldPlan = _planList.ToArray();
@ -381,17 +285,21 @@ namespace YunDa.Server.ISMSTcp.Services
//获取到绑定信息,可以更新全部状态了
if (_planList.Count > 0)
_updatedPlanOk = true;
_getPlanListOk = true;
planList = _planList.ToArray();
}
_zzDataCmdService.AddCircuitPlanYcIds(planList);
}
catch (Exception ex)
{
_updatedPlanOk = false;
_getPlanListOk = false;
}
await Task.CompletedTask;
}
@ -400,6 +308,7 @@ namespace YunDa.Server.ISMSTcp.Services
{
try
{
SecondaryCircuitEventDrivenConfigOutput[] eventPlantList = null;
using (await _eventPlanLock.LockAsync())
{
@ -432,7 +341,12 @@ namespace YunDa.Server.ISMSTcp.Services
//获取到绑定信息,可以更新全部状态了
if (_eventPlanList.Count > 0)
_getEventPlanListOk = true;
eventPlantList = _eventPlanList.ToArray();
}
_zzDataCmdService.AddEventPlanYcIds(eventPlantList);
}
catch (Exception ex)
{
@ -475,11 +389,16 @@ namespace YunDa.Server.ISMSTcp.Services
foreach (var item in planList)
{
if (item.ExecuteTime == DateTime.MinValue && item.Plan.IsActive)
bool debugCheck = false;
//if (item.Plan.Name == "软压板巡检")
//{
// debugCheck = true;
//}
if (item.ExecuteTime == DateTime.MinValue && item.Plan.IsActive || debugCheck)
{//当天未执行
if (now.Hour == item.Plan.ScheduledHour && now.Minute == item.Plan.ScheduledMinute)
if (now.Hour == item.Plan.ScheduledHour && now.Minute == item.Plan.ScheduledMinute || debugCheck)
{
if (item.Plan.ScheduledWeekDaysList.IndexOf(week) != -1)
if (item.Plan.ScheduledWeekDaysList.IndexOf(week) != -1 || debugCheck)
{
await PushPlanToChannel(1, item.Plan);
@ -514,10 +433,21 @@ namespace YunDa.Server.ISMSTcp.Services
{
string id = item.Id.ToString();
if(item.IsActive && !string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(id))
bool debugCheck = true;
//if (item.Name == "212馈线软压板 + 控制字一致性校验")
//{
// debugCheck = true;
//}
//else
//{
// debugCheck = false;
//}
if (debugCheck && item.IsActive && !string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(id))
{
//写到队列里,由队列控制执行频率
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(triggerType, item));
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(triggerType, item, plan.Id.ToString()));
}
}
@ -589,6 +519,15 @@ namespace YunDa.Server.ISMSTcp.Services
if (item.DeviceConfigCount > 0)
{
settingData = await GetSourceDataAsync<List<DeviceDzData>>(id, 3);
if(settingData?.Count > 0)
{
_logger.LogWarning($"{DateTime.Now.ToString()}: 获取定值成功id = {id}");
}
else
{
_logger.LogError($"{DateTime.Now.ToString()}: 获取定值失败id = {id}");
}
}
if (item.CameraPresetCount > 0)
{
@ -610,8 +549,7 @@ namespace YunDa.Server.ISMSTcp.Services
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)
if (jsData.StoreData.IsAllDataNull())
{
continue;
}
@ -653,16 +591,36 @@ namespace YunDa.Server.ISMSTcp.Services
saveData.ExecutionDurationMs = sw.ElapsedMilliseconds;
saveData.TriggerType = itemEx.TriggerType;
if (saveData.TriggerType == 1)
saveData.InspectionPlanId = id;
saveData.InspectionPlanId = itemEx.PlanId;
else
saveData.TriggerEventId = id;
saveData.TriggerEventId = itemEx.PlanId;
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;
if (saveObj.HasProperty("data"))
{//使用过滤后的数据2025-12-09 王喜沟通
engine.SetValue("data", saveObj.Get("data"));
var dataJsonString = engine.Evaluate("JSON.stringify(data)").AsString();
var model = JsonConvert.DeserializeObject<SecondaryCircuitInspectionJsDataModel>(dataJsonString);
if(model != null && model.StoreData != null && !model.StoreData.IsAllDataNull())
{
saveData.Data = model;
}
else
{
saveData.Data = jsData;
}
}
else
{
saveData.Data = jsData;
}
var inspectionResultId = await _webApiRequest.SaveSecondaryCircuitInspectionPlanResultAsync(saveData);
@ -938,13 +896,13 @@ namespace YunDa.Server.ISMSTcp.Services
planList = _eventPlanList.ToArray();
}
foreach (var item in planList)
foreach (var plan in planList)
{
await Task.Delay(item.DelayTriggerSeconds * 1000);
await Task.Delay(plan.DelayTriggerSeconds * 1000);
if (item.IsActive && item.SecondaryCircuitInspectionEventItems?.Count > 0 && !string.IsNullOrWhiteSpace(item.TriggerExpression))
if (plan.IsActive && plan.SecondaryCircuitInspectionEventItems?.Count > 0 && !string.IsNullOrWhiteSpace(plan.TriggerExpression))
{
string id = item.Id.ToString() ?? "";
string id = plan.Id.ToString() ?? "";
if (!string.IsNullOrWhiteSpace(id))
{
@ -957,13 +915,13 @@ namespace YunDa.Server.ISMSTcp.Services
Task<List<ZzDataResultModel>>? t1 = null;
Task<List<ZzDataResultModel>>? t2 = null;
if (item.TelemetryConfigs.Count > 0)
if (plan.TelemetryConfigs.Count > 0)
{
t1 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 1);
tasks.Add(t1);
}
if (item.TelesignalConfigs.Count > 0)
if (plan.TelesignalConfigs.Count > 0)
{
t2 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 2);
tasks.Add(t2);
@ -974,7 +932,7 @@ namespace YunDa.Server.ISMSTcp.Services
if (t1 != null) jsData.StoreData.TelemetryData = await t1;
if (t2 != null) jsData.StoreData.TeleSignalData = await t2;
engine.Execute(item.TriggerExpression);
engine.Execute(plan.TriggerExpression);
bool canCheck = true;
@ -985,17 +943,17 @@ namespace YunDa.Server.ISMSTcp.Services
if(canCheck)
{
await Task.Delay(item.MandatoryWaitSeconds * 1000);
await Task.Delay(plan.MandatoryWaitSeconds * 1000);
//CheckPlanFormIds(item.SecondaryCircuitInspectionEventItems);
//执行巡检
foreach(var planItem in item.SecondaryCircuitInspectionEventItems)
foreach(var planItem in plan.SecondaryCircuitInspectionEventItems)
{
if (!string.IsNullOrWhiteSpace(planItem.CalculationExpression) && !string.IsNullOrWhiteSpace(planItem.Id.ToString()))
{
//写到队列里,由队列控制执行频率
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(2, planItem));
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(2, planItem, plan.Id.ToString()));
}
}
}

View File

@ -103,6 +103,9 @@ namespace YunDa.Server.ISMSTcp.Services
//_cleanupTimer = new Timer(CleanupExpiredData, null, CLEANUP_INTERVAL_MS, CLEANUP_INTERVAL_MS);
_zzDataCacheContainer.SetTelemeteringHandle(this);
ZzDataCache.SetDataCache(ZzDataCacheContainerDataType.eYC, _zzDataCacheContainer);
}
/// <summary>
@ -378,6 +381,12 @@ namespace YunDa.Server.ISMSTcp.Services
{
try
{
//if(ycDataModel.YC_ID == "YCB001162001")
//{
// _logger.LogWarning($"{DateTime.Now.ToString()}: 收到{ycDataModel.YC_ID}遥测数据");
//}
// 使用映射字典查找对应的haskey列表
if (!_ycIdToHashKeysMapping.TryGetValue(ycDataModel.YC_ID, out List<string> haskeys))
{
@ -393,6 +402,13 @@ namespace YunDa.Server.ISMSTcp.Services
{
if (parsedTime.Year>2000)
{
//if (ycDataModel.YC_ID == "YCB001209003")
//if(ycDataModels.Count == 188)
//if (ycDataModel.YC_ID == "YCB001112000")
//{
// _logger.LogWarning($"{resultTime.ToString()} : 收到遥测数据Id = {ycDataModel.YC_ID}, Time = {parsedTime.ToString()}, 相差{(resultTime - parsedTime).TotalSeconds}秒");
//}
resultTime = parsedTime;
}
}
@ -669,7 +685,7 @@ namespace YunDa.Server.ISMSTcp.Services
await InitAsync();
}
var result = await _zzDataCacheContainer.Read(ids, seconds, timeWindowType, cancellationToken, now);
var result = await _zzDataCacheContainer.Read(ids, seconds, timeWindowType, cancellationToken, now, true);
return result;
}
@ -695,6 +711,10 @@ namespace YunDa.Server.ISMSTcp.Services
return null;
}
public void LogWarning(string msg)
{
_logger.LogWarning(msg);
}
/// <summary>

View File

@ -127,7 +127,7 @@ namespace YunDa.Server.ISMSTcp.Services
// 发送遥测召唤命令到TCP客户端
bool ycResult = await _tcpClient.SendMessageAsync(ycCommand, cancellationToken);
// 遥测和遥信命令之间间隔100毫秒
await Task.Delay(100, cancellationToken);
await Task.Delay(1000, cancellationToken);
// 构造遥信召唤命令CallYXByDevice|{装置ID}
string yxCommand = $"CallYXByDevice|{device.Id}";
@ -135,7 +135,7 @@ namespace YunDa.Server.ISMSTcp.Services
// 发送遥信召唤命令到TCP客户端
bool yxResult = await _tcpClient.SendMessageAsync(yxCommand, cancellationToken);
// 每个装置处理完成后间隔100毫秒
await Task.Delay(100, cancellationToken);
await Task.Delay(1000, cancellationToken);
}
catch (Exception ex)
{
@ -179,6 +179,9 @@ namespace YunDa.Server.ISMSTcp.Services
// 执行遥测和遥信召唤
await ExecuteTelemetryCallAsync(cancellationToken);
//召唤完成
AppInit.MarkInitialized();
}
catch (OperationCanceledException)
{

View File

@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
@ -77,6 +78,8 @@ namespace YunDa.Server.ISMSTcp.Services
_webSocketPushService = webSocketPushService ?? throw new ArgumentNullException(nameof(webSocketPushService));
_protectionDeviceCommInfoRedis = protectionDeviceCommInfoRedis ?? throw new ArgumentNullException(nameof(protectionDeviceCommInfoRedis));
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
ZzDataCache.SetDataCache(ZzDataCacheContainerDataType.eYX, _zzDataCacheContainer);
}
/// <summary>
@ -144,7 +147,6 @@ namespace YunDa.Server.ISMSTcp.Services
if (_yxIdToHashKeyMapping.TryAdd(model.ismsbaseYXId, haskey))
{
mappingCount++;
//初始化
_zzDataCacheContainer.Write(model.ismsbaseYXId, model.ResultValue, model.ResultTime, model.Name, model.ResultValueStr, model.DispatcherAddress);
}
@ -370,6 +372,12 @@ namespace YunDa.Server.ISMSTcp.Services
break;
}
//if (yxDataModel.YX_ID == "YXB001121065")
//{
// _logger.LogWarning($"YXB001121065: {yxDataModel.V}, {yxDataModel.T}");
// Debug.WriteLine($"YXB001121065: {yxDataModel.V}, {yxDataModel.T}");
//}
// 使用映射字典查找对应的haskey
if (!_yxIdToHashKeyMapping.TryGetValue(yxDataModel.YX_ID, out string haskey))
{

View File

@ -43,6 +43,9 @@ namespace YunDa.Server.ISMSTcp.Services
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
_zzDataCacheContainerInit = zzDataCacheContainerInit;
_zzDataCacheContainerInit.InitVariantBaseinfo(_zzDataCacheContainer);
ZzDataCache.SetDataCache(ZzDataCacheContainerDataType.eVA, _zzDataCacheContainer);
}
/// <summary>

View File

@ -96,6 +96,11 @@ namespace YunDa.Server.ISMSTcp.Services
.Select(x => x.point)
.ToList();
}
public ZzDataPoint GetCurrentData()
{
return new ZzDataPoint(TimeStamp, Value, ValueStr);
}
}
@ -192,6 +197,20 @@ namespace YunDa.Server.ISMSTcp.Services
data.AddData(new ZzDataPoint(time, val, valStr));
}
public ZzDataPoint? ReadCurrentValue(string id, out ZzData? zzData)
{
if (_datas.TryGetValue(id, out var channel))
{
zzData = channel;
return channel.GetCurrentData();
}
else
{
zzData = null;
return null;
}
}
public async Task<Dictionary<string, ZzDataResultModel>> Read(List<string> ids, DateTime start, DateTime end)
{
var result = new Dictionary<string, ZzDataResultModel>();
@ -213,30 +232,30 @@ namespace YunDa.Server.ISMSTcp.Services
if (data.Data.Count == 0 && data.TimeStamp != DateTime.MinValue)
{
ZzDataPoint zzDataPoint = new ZzDataPoint(DateTime.Now, data.Value, data.ValueStr.Replace(" ", ""));
ZzDataPoint zzDataPoint = new ZzDataPoint(DateTime.Now, data.Value, data.ValueStr);
data.Data.Add(zzDataPoint);
}
if (_telemeteringHandle != null &&_dataType == ZzDataCacheContainerDataType.eYC && id == "YCB001103003")
{
if (data.Data.Where(e => Math.Abs(e.Value) < 0.0001).Count() == data.Data.Count)
{
string time = DateTime.Now.ToString("yyy/MM/dd HH:mm:ss");
System.Console.WriteLine($"【{time}】: {id}所有值为0[{data.Data.Count}]:状态:{isFind} Cache长度{channel.Datas.Count}, Cache时间{channel.StartTimeStamp.ToString("yyy/MM/dd HH:mm:ss")} ~ {channel.TimeStamp.ToString("yyy/MM/dd HH:mm:ss")}, 最后一个值:{channel.ValueStr}");
//if (_telemeteringHandle != null &&_dataType == ZzDataCacheContainerDataType.eYC && id == "YCB001103003")
//{
// if (data.Data.Where(e => Math.Abs(e.Value) < 0.0001).Count() == data.Data.Count)
// {
// string time = DateTime.Now.ToString("yyy/MM/dd HH:mm:ss");
// _telemeteringHandle.LogWarning($"【{time}】: {id}所有值为0[{data.Data.Count}]:状态:{isFind} Cache长度{channel.Datas.Count}, Cache时间{channel.StartTimeStamp.ToString("yyy/MM/dd HH:mm:ss")} ~ {channel.TimeStamp.ToString("yyy/MM/dd HH:mm:ss")}, 最后一个值:{channel.ValueStr}");
var redisData = await _telemeteringHandle.GetDataFromRedis(id);
if (redisData != null)
{
System.Console.WriteLine($"【{time}】: {id}在Redis中的值为{redisData.ResultValue} - {redisData.ResultTime }");
}
else
{
System.Console.WriteLine($"【{time}】: {id}在Redis中没有找到");
}
// var redisData = await _telemeteringHandle.GetDataFromRedis(id);
// if (redisData != null)
// {
// _telemeteringHandle.LogWarning($"【{time}】: {id}在Redis中的值为{redisData.ResultValue} - {redisData.ResultTime }");
// }
// else
// {
// _telemeteringHandle.LogWarning($"【{time}】: {id}在Redis中没有找到");
// }
}
}
// }
//}
result[id] = data;
@ -246,7 +265,7 @@ namespace YunDa.Server.ISMSTcp.Services
return result;
}
public async Task<List<ZzDataResultModel>> Read(List<string> ids, int seconds, int timeWindowType, CancellationToken cancellationToken, DateTime now = default)
public async Task<List<ZzDataResultModel>> Read(List<string> ids, int seconds, int timeWindowType, CancellationToken cancellationToken, DateTime now = default, bool forceCorrectCount = false)
{
try
{
@ -259,7 +278,40 @@ namespace YunDa.Server.ISMSTcp.Services
if (timeWindowType == 0 || timeWindowType == 2)
{
matched1 = await Read(ids, now.AddSeconds(-seconds), now);
DateTime startTime = now.AddSeconds(-seconds);
for(int i = 0; i < seconds; i++)
{
matched1 = await Read(ids, startTime, now);
//要保证有多少秒,就至少有多少个点
if (forceCorrectCount)
{
bool isCountCorrect = true;
foreach (var item in matched1)
{
if (item.Value.Data.Count < seconds)
{
isCountCorrect = false;
break;
}
}
if (isCountCorrect)
break;
else
{
startTime = startTime.AddSeconds(-3);
}
}
else
{
break;
}
}
}
if (timeWindowType == 1 || timeWindowType == 2)
@ -268,7 +320,41 @@ namespace YunDa.Server.ISMSTcp.Services
if(span > 0)
await Task.Delay(span, cancellationToken);
matched2 = await Read(ids, now, DateTime.Now);
for(int i = 0; i < seconds; i++)
{
matched2 = await Read(ids, now, DateTime.Now);
//要保证有多少秒,就至少有多少个点
if (forceCorrectCount)
{
bool isCountCorrect = true;
foreach (var item in matched2)
{
if (item.Value.Data.Count < seconds)
{
isCountCorrect = false;
break;
}
}
if (isCountCorrect)
break;
else
{
await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);
}
}
else
{
break;
}
}
}
foreach (var kv in matched2)
@ -288,6 +374,27 @@ namespace YunDa.Server.ISMSTcp.Services
}
}
public static class ZzDataCache
{
private static readonly ConcurrentDictionary<ZzDataCacheContainerDataType, ZzDataCacheContainer> _datas = new ConcurrentDictionary<ZzDataCacheContainerDataType, ZzDataCacheContainer>();
public static void SetDataCache(ZzDataCacheContainerDataType type, ZzDataCacheContainer cache)
{
_datas.TryAdd(type, cache);
}
public static ZzDataCacheContainer? GetDataCache(ZzDataCacheContainerDataType type)
{
if(_datas.TryGetValue(type, out ZzDataCacheContainer cache))
{
return cache;
}
else
{
return null;
}
}
}
public class ZzDataCacheContainerInit
{
private readonly ILogger<SecondaryCircuitInspectionPlanService> _logger;

View File

@ -0,0 +1,249 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
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.Configurations;
namespace YunDa.Server.ISMSTcp.Services
{
public class YcIdExInfo
{
public string Id { get; set; } = string.Empty;
public string DeviceId { get; set; } = string.Empty;
public bool DeviceCommState { get; set; } = true;
public YcIdExInfo(string id)
{
Id = id;
}
}
public class ZzDataIdList
{
private ConcurrentDictionary<string, YcIdExInfo> _ids = new ConcurrentDictionary<string, YcIdExInfo>();
public void Clear()
{
_ids.Clear();
}
public bool AddId(string id)
{
return _ids.TryAdd(id, new YcIdExInfo(id));
}
public void AddIds(string ids, string keyword, string separator)
{
if (string.IsNullOrWhiteSpace(ids))
return;
string[] list = ids.Trim().Split(separator);
foreach (string id in list)
{
if(string.IsNullOrWhiteSpace(id))
continue;
if(id.Contains(keyword))
_ids.TryAdd(id, new YcIdExInfo(id));
}
}
public void UpdateDeviceCommState(string deviceId, bool commState)
{
foreach (var yc in _ids.Values)
{
if (yc.Id.Contains(deviceId))
{
yc.DeviceCommState = commState;
}
}
}
public List<string> GetIds(bool? commState)
{
if(commState.HasValue)
return _ids.Where(kvp => kvp.Value.DeviceCommState == commState)
.Select(kvp => kvp.Key)
.ToList();
else
return _ids.Keys.ToList();
}
}
public class ZzDataCmdService
{
private readonly ILogger<ZzDataCmdService> _logger;
private readonly ZzTcpService _zTcpService = null;
private ZzDataIdList _ycIds = new ZzDataIdList();
public ZzDataCmdService(
ILogger<ZzDataCmdService> logger,
ZzTcpService zTcpService)
{
_logger = logger;
_zTcpService = zTcpService;
StartAsync();
}
private async Task StartAsync()
{
await AppInit.WaitAsync(AppInit.DefaultTimeOut);
_ = Task.Run(async () =>
{
while (true)
{//每秒发送一次命令
await SendYcCmd();
await Task.Delay(TimeSpan.FromSeconds(1));
}
});
_ = Task.Run(async () =>
{
while (true)
{//每30秒发送一次命令
await SendVaCmd();
await Task.Delay(TimeSpan.FromSeconds(30));
}
});
}
private List<string> GetValidZzCmd(string cmdName, List<string> ids)
{
const int maxLength = 25000;
var cmds = new List<string>();
// 开始构建
string prefix = cmdName + "|"; // 固定前缀
int prefixLength = prefix.Length;
var currentIds = new List<string>();
int currentLength = prefixLength; // 当前命令的长度(含前缀)
foreach (var id in ids)
{
// 如果添加这个ID会超长则先生成一个命令
int idLength = (currentIds.Count == 0 ? id.Length : (1 + id.Length)); // 第一个ID不需要 '#'
if (currentLength + idLength > maxLength)
{
// 将当前批次加入 cmds
cmds.Add(prefix + string.Join("#", currentIds));
// 清空并重建
currentIds.Clear();
currentLength = prefixLength;
}
// 添加新的 ID
currentIds.Add(id);
currentLength += idLength;
}
// 收尾:如果还有剩余 IDs生成最后一个命令
if (currentIds.Count > 0)
{
cmds.Add(prefix + string.Join("#", currentIds));
}
return cmds;
}
//更新装置状态
public void UpdateDeviceCommState(string deviceId, bool commState)
{
_ycIds.UpdateDeviceCommState(deviceId, commState);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////遥测
public void ClearYcIds()
{
_ycIds.Clear();
}
//计划巡检配置
public void AddCircuitPlanYcIds(SecondaryCircuitInspectionPlanStateModel[] planList)
{
foreach (var plan in planList)
{
foreach(var inspection in plan.Plan.InspectionItems)
{
foreach (var item in inspection.TelemetryConfigs)
{
if(!string.IsNullOrWhiteSpace(item.TelemetryConfigurationIsmsId) && item.TelemetryConfigurationIsmsId.StartsWith("YC"))
_ycIds.AddId(item.TelemetryConfigurationIsmsId);
}
}
}
}
//事件巡检配置
public void AddEventPlanYcIds(SecondaryCircuitEventDrivenConfigOutput[] planList)
{
foreach (var plan in planList)
{
foreach(var item in plan.TelemetryConfigs)
{
if (!string.IsNullOrWhiteSpace(item.TelemetryConfigurationIsmsId) && item.TelemetryConfigurationIsmsId.StartsWith("YC"))
_ycIds.AddId(item.TelemetryConfigurationIsmsId);
}
foreach (var inspection in plan.SecondaryCircuitInspectionEventItems)
{
foreach (var item in inspection.TelemetryConfigs)
{
if (!string.IsNullOrWhiteSpace(item.TelemetryConfigurationIsmsId) && item.TelemetryConfigurationIsmsId.StartsWith("YC"))
_ycIds.AddId(item.TelemetryConfigurationIsmsId);
}
}
}
}
private int _sendCnt = 0;
private async Task SendYcCmd()
{
var ids = _ycIds.GetIds(null);
var cmds = GetValidZzCmd("CallYCByDataID", ids);
foreach (var cmd in cmds)
{
await _zTcpService.SendTcpMessageAsync(cmd, CancellationToken.None);
if(_sendCnt % 60 == 0)
_logger.LogWarning($"发送命令:{ids.Count}个id");
_sendCnt++;
}
}
private async Task SendVaCmd()
{
string cmd = "CallVAByStat|B001";
await _zTcpService.SendTcpMessageAsync(cmd, CancellationToken.None);
}
}
}