SOMS/src/YunDa.Server/YunDa.Server.ISMSTcp/Services/VirtualTerminalHandler.cs
qsp89 487fa3def3 修改性能状态推送不全问题
遥测数据返回为0,添加调试记录
2025-11-26 14:08:37 +08:00

418 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.ClearScript.JavaScript;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using YunDa.Server.ISMSTcp.Domain;
using YunDa.Server.ISMSTcp.Models;
using YunDa.SOMS.DataTransferObject.ExternalEntities.BeijingYounuo;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace YunDa.Server.ISMSTcp.Services
{
/// <summary>
/// 虚端子处理服务
/// 负责处理虚端子相关的业务逻辑
/// </summary>
public class VirtualTerminalHandler
{
private readonly ILogger<VirtualTerminalHandler> _logger;
private readonly WebApiRequest _webApiRequest;
private readonly ZzDataCacheContainerInit _zzDataCacheContainerInit;
// 🔧 新增:光纤虚点处理相关字段
private readonly ConcurrentDictionary<string, OpticalFiberDto> _virtualPointToOpticalFiberMapping = new ConcurrentDictionary<string, OpticalFiberDto>();
// 初始化状态
public volatile bool _isOpticalFiberMappingInitialized = false;
private readonly object _opticalFiberInitLock = new object();
//数据缓冲队列
private ZzDataCacheContainer _zzDataCacheContainer = new ZzDataCacheContainer(ZzDataCacheContainerDataType.eVA, 5);
public VirtualTerminalHandler(ILogger<VirtualTerminalHandler> logger, WebApiRequest webApiRequest, ZzDataCacheContainerInit zzDataCacheContainerInit)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
_zzDataCacheContainerInit = zzDataCacheContainerInit;
_zzDataCacheContainerInit.InitVariantBaseinfo(_zzDataCacheContainer);
}
/// <summary>
/// 初始化光纤虚点映射
/// </summary>
/// <param name="maxRetries">最大重试次数</param>
/// <param name="retryDelayMs">重试间隔(毫秒)</param>
/// <returns>初始化任务</returns>
public async Task<bool> InitOpticalFiberMappingAsync(int maxRetries = 3, int retryDelayMs = 2000)
{
if (_isOpticalFiberMappingInitialized)
{
_logger.LogInformation("光纤虚点映射已经初始化完成");
return true;
}
lock (_opticalFiberInitLock)
{
if (_isOpticalFiberMappingInitialized)
{
return true;
}
_logger.LogInformation("开始初始化光纤虚点映射...");
}
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
_logger.LogInformation("第 {Attempt}/{MaxRetries} 次尝试获取光纤配置数据", attempt, maxRetries);
// 获取光纤配置数据
var opticalFibers = await _webApiRequest.GetOpticalFiberListAsync();
if (opticalFibers == null || opticalFibers.Count == 0)
{
_logger.LogWarning("第 {Attempt} 次查询光纤配置数据为空", attempt);
if (attempt < maxRetries)
{
_logger.LogInformation("等待 {DelayMs} 毫秒后重试...", retryDelayMs);
await Task.Delay(retryDelayMs);
continue;
}
else
{
_logger.LogError("所有重试都失败,无法获取光纤配置数据");
return false;
}
}
// 创建虚点编码到光纤配置的映射
int mappingCount = 0;
foreach (var fiber in opticalFibers.Where(f => f.IsActive && !string.IsNullOrWhiteSpace(f.VirtualPointCode)))
{
if (_virtualPointToOpticalFiberMapping.TryAdd(fiber.VirtualPointCode, fiber))
{
mappingCount++;
}
else
{
_logger.LogWarning("虚点编码重复: {VirtualPointCode}", fiber.VirtualPointCode);
}
}
lock (_opticalFiberInitLock)
{
_isOpticalFiberMappingInitialized = true;
}
_logger.LogInformation("光纤虚点映射初始化成功!光纤配置: {OpticalFiberCount} 个,虚点映射: {MappingCount} 个",
opticalFibers.Count, mappingCount);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "第 {Attempt} 次初始化光纤虚点映射时发生错误", attempt);
if (attempt < maxRetries)
{
_logger.LogInformation("等待 {DelayMs} 毫秒后重试...", retryDelayMs);
await Task.Delay(retryDelayMs);
}
else
{
_logger.LogError("所有重试都失败,初始化光纤虚点映射失败");
return false;
}
}
}
return false;
}
/// <summary>
/// 处理虚端子数据
/// </summary>
/// <param name="virtualTerminalData">虚端子数据对象</param>
/// <returns>处理任务</returns>
public async Task ProcessVirtualTerminalDataAsync(VirtualTerminalData virtualTerminalData)
{
try
{
if (virtualTerminalData == null)
{
_logger.LogWarning("虚端子数据为空,跳过处理");
return;
}
if (!virtualTerminalData.IsValid())
{
_logger.LogWarning("虚端子数据无效:{Data}", virtualTerminalData);
return;
}
_logger.LogInformation("开始处理虚端子数据:{Data}", virtualTerminalData);
// 解析时间戳
if (virtualTerminalData.TryParseDateTime(out DateTime timestamp))
{
_logger.LogInformation("虚端子时间戳解析成功:{Timestamp}", timestamp);
}
else
{
_logger.LogInformation("虚端子时间戳解析失败:{TimeString}", virtualTerminalData.T);
}
// 解析数值
if (virtualTerminalData.TryParseValue(out double value))
{
_logger.LogInformation("虚端子数值解析成功:{Value}", value);
}
else
{
_logger.LogInformation("虚端子数值为非数字类型:{ValueString}", virtualTerminalData.V);
}
// 根据UA_ID执行不同的处理逻辑
await ProcessByUAId(virtualTerminalData);
//存入缓存
SaveDataToCache(virtualTerminalData.VA_ID, value, timestamp);
_logger.LogInformation("虚端子数据处理完成:{UA_ID}", virtualTerminalData.VA_ID);
}
catch (Exception ex)
{
_logger.LogError(ex, "处理虚端子数据时发生异常:{Data}", virtualTerminalData);
}
}
/// <summary>
/// 根据UA_ID执行不同的处理逻辑
/// </summary>
/// <param name="data">虚端子数据</param>
/// <returns>处理任务</returns>
private async Task ProcessByUAId(VirtualTerminalData data)
{
try
{
await ProcessDefaultLogic(data);
}
catch (Exception ex)
{
_logger.LogError(ex, "根据UA_ID处理虚端子数据时发生异常{UA_ID}", data.VA_ID);
}
}
/// <summary>
/// 默认处理逻辑 - 处理光纤虚点
/// </summary>
/// <param name="data">虚端子数据</param>
/// <returns>处理任务</returns>
private async Task ProcessDefaultLogic(VirtualTerminalData data)
{
try
{
if (!_isOpticalFiberMappingInitialized)
{
_logger.LogDebug("光纤虚点映射尚未初始化,跳过处理");
return;
}
// 检查是否有对应的光纤配置
if (!_virtualPointToOpticalFiberMapping.TryGetValue(data.VA_ID, out var opticalFiber))
{
_logger.LogDebug("未找到虚点编码 {VA_ID} 对应的光纤配置", data.VA_ID);
return;
}
// 解析虚点值
if (!data.TryParseValue(out double value))
{
_logger.LogWarning("无法解析虚点值: {Value}, 虚点编码: {VA_ID}", data.V, data.VA_ID);
return;
}
_logger.LogDebug("处理光纤虚点: {VA_ID}, 值: {Value}, 光纤: {TwinId}", data.VA_ID, value, opticalFiber.TwinId);
// 根据虚点值处理报警
//Boolean型变量的值(真,假,不定)2018-04-26从uPro设备
//TVariantBoolValue = (vbTrue = 0vbFalse = 1 vbUncertain = 2):
if (Math.Abs(value) < 0.0001) // 故障状态
{
await PushOpticalFiberAlarmAsync(opticalFiber, data);
}
else if (Math.Abs(value - 1) < 0.0001) // 正常状态
{
await DeleteOpticalFiberAlarmAsync(opticalFiber, data);
}
else
{
_logger.LogWarning("光纤虚点值异常: {Value}, 预期值为0或1, 虚点编码: {VA_ID}", value, data.VA_ID);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "处理光纤虚点默认逻辑时发生错误: {VA_ID}", data.VA_ID);
}
}
/// <summary>
/// 尝试从JSON字符串解析虚端子数据
/// </summary>
/// <param name="json">JSON字符串</param>
/// <param name="virtualTerminalData">解析后的虚端子数据</param>
/// <returns>是否解析成功</returns>
public bool TryParseVirtualTerminalData(string json, out VirtualTerminalData virtualTerminalData)
{
virtualTerminalData = null;
try
{
if (string.IsNullOrEmpty(json))
{
return false;
}
virtualTerminalData = JsonConvert.DeserializeObject<VirtualTerminalData>(json);
return virtualTerminalData != null && virtualTerminalData.IsValid();
}
catch (Exception ex)
{
_logger.LogDebug(ex, "解析虚端子数据失败:{Json}", json);
return false;
}
}
/// <summary>
/// 推送光纤报警到3D系统
/// </summary>
/// <param name="opticalFiber">光纤配置</param>
/// <param name="virtualTerminalData">虚端子数据</param>
/// <returns></returns>
private async Task PushOpticalFiberAlarmAsync(OpticalFiberDto opticalFiber, VirtualTerminalData virtualTerminalData)
{
try
{
if (string.IsNullOrWhiteSpace(opticalFiber.TwinId))
{
_logger.LogWarning("光纤配置缺少TwinId: {VirtualPointCode}", opticalFiber.VirtualPointCode);
return;
}
// 解析时间戳
var timestamp = DateTime.Now;
if (virtualTerminalData.TryParseDateTime(out DateTime parsedTime))
{
timestamp = parsedTime;
}
// 构造报警数据
var alarmData = new YounuoAlert
{
TwinID = opticalFiber.TwinId,
Severity = 1, // 默认严重级别
Status = 1, // 活跃报警
SourceAlertKey = "光纤故障",
Summary = $"光纤 {opticalFiber.CiCode ?? opticalFiber.TwinId} 故障,虚点编码: {opticalFiber.VirtualPointCode}",
SourceIdentifier = $"OpticalFiber_{opticalFiber.TwinId}_{opticalFiber.VirtualPointCode}",
LastOccurrence = timestamp.ToString("yyyy-MM-dd HH:mm:ss"),
};
_logger.LogInformation("推送光纤报警: TwinId={TwinId}, 虚点编码={VirtualPointCode}, 值={Value}",
opticalFiber.TwinId, opticalFiber.VirtualPointCode, virtualTerminalData.V);
await _webApiRequest.CallPushAlarmsApiAsync(alarmData);
}
catch (Exception ex)
{
_logger.LogError(ex, "推送光纤报警时发生错误: TwinId={TwinId}, 虚点编码={VirtualPointCode}",
opticalFiber.TwinId, opticalFiber.VirtualPointCode);
}
}
/// <summary>
/// 删除光纤报警
/// </summary>
/// <param name="opticalFiber">光纤配置</param>
/// <param name="virtualTerminalData">虚端子数据</param>
/// <returns></returns>
private async Task DeleteOpticalFiberAlarmAsync(OpticalFiberDto opticalFiber, VirtualTerminalData virtualTerminalData)
{
try
{
if (string.IsNullOrWhiteSpace(opticalFiber.TwinId))
{
_logger.LogWarning("光纤配置缺少TwinId: {VirtualPointCode}", opticalFiber.VirtualPointCode);
return;
}
_logger.LogInformation("删除光纤报警: TwinId={TwinId}, 虚点编码={VirtualPointCode}, 值={Value}",
opticalFiber.TwinId, opticalFiber.VirtualPointCode, virtualTerminalData.V);
// TODO: 实现实际的API调用到BeijingYounuoApiAppService.DeleteAlarmsByTwinIdAsync
// 由于架构限制可能需要通过HTTP API或消息队列来实现
await _webApiRequest.CallDeleteAlarmsByTwinIdApiAsync(opticalFiber.TwinId);
}
catch (Exception ex)
{
_logger.LogError(ex, "删除光纤报警时发生错误: TwinId={TwinId}, 虚点编码={VirtualPointCode}",
opticalFiber.TwinId, opticalFiber.VirtualPointCode);
}
}
//缓存遥信数据到内存
private void SaveDataToCache(string id, double value, DateTime timestamp)
{
try
{
string valStr = "不定";
switch((int)value)
{
case 0:
valStr = "报警";
break;
case 1:
valStr = "正常";
break;
}
_zzDataCacheContainer.Write(id, value, timestamp, "", valStr);
}
catch (Exception ex)
{
_logger.LogError(ex, "VirtualTerminalHandler: SaveDataToCache 出错");
}
}
//查询数据
public async Task<List<ZzDataResultModel>> GetDataByIds(List<string> ids, int seconds, int timeWindowType, CancellationToken cancellationToken)
{
try
{
return await _zzDataCacheContainer.Read(ids, seconds, timeWindowType, cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "VirtualTerminalHandler: GetDataByIds 出错");
return new List<ZzDataResultModel>();
}
}
}
}