2025-07-31 18:51:24 +08:00
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using Newtonsoft.Json;
|
2025-10-21 09:59:22 +08:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using Org.BouncyCastle.Crypto;
|
2025-10-21 09:59:22 +08:00
|
|
|
|
using Org.BouncyCastle.Utilities;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2025-10-26 15:12:32 +08:00
|
|
|
|
using System.Diagnostics;
|
2025-10-21 09:59:22 +08:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using YunDa.Server.Communication.Framework;
|
|
|
|
|
|
using YunDa.Server.ISMSTcp.Domain;
|
2025-07-31 18:51:24 +08:00
|
|
|
|
using YunDa.Server.ISMSTcp.Interfaces;
|
|
|
|
|
|
using YunDa.Server.ISMSTcp.Models;
|
2025-10-21 09:59:22 +08:00
|
|
|
|
using YunDa.Server.ISMSTcp.Services;
|
|
|
|
|
|
using YunDa.SOMS.DataTransferObject.GeneralInformation.ProtectionDeviceInfoDto;
|
|
|
|
|
|
using YunDa.SOMS.Redis.Repositories;
|
2025-07-31 18:51:24 +08:00
|
|
|
|
|
|
|
|
|
|
namespace YunDa.Server.ISMSTcp.Controllers
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 查询API控制器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ApiController]
|
|
|
|
|
|
[Route("api")]
|
|
|
|
|
|
public class QueryController : ControllerBase
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly ILogger<QueryController> _logger;
|
|
|
|
|
|
private readonly IQueryService _queryService;
|
2025-10-21 09:59:22 +08:00
|
|
|
|
private readonly IWebSocketMessageBroadcaster _webSocketBroadcaster;
|
|
|
|
|
|
private readonly IRedisRepository<ProtectionDeviceCommInfoOutput, string> _protectionDeviceCommInfoOutputredisRepository;
|
|
|
|
|
|
private readonly WebApiRequest _webApiRequest;
|
|
|
|
|
|
private readonly ITcpClient _tcpClient;
|
|
|
|
|
|
private readonly TelemeteringHandle _telemeteringHandle;
|
2025-10-30 12:24:29 +08:00
|
|
|
|
private readonly TelesignalisationHandle _telesignalisationHandle;
|
2025-07-31 18:51:24 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构造函数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="logger">日志记录器</param>
|
|
|
|
|
|
/// <param name="queryService">查询服务</param>
|
2025-10-21 09:59:22 +08:00
|
|
|
|
/// <param name="webSocketBroadcaster">WebSocket消息广播器</param>
|
|
|
|
|
|
/// <param name="protectionDeviceCommInfoOutputredisRepository">保护装置通信信息Redis仓储</param>
|
|
|
|
|
|
/// <param name="webApiRequest">Web API请求服务</param>
|
|
|
|
|
|
/// <param name="tcpClient">TCP客户端</param>
|
|
|
|
|
|
/// <param name="telemeteringHandle">遥测数据处理服务</param>
|
|
|
|
|
|
public QueryController(
|
|
|
|
|
|
ILogger<QueryController> logger,
|
|
|
|
|
|
IQueryService queryService,
|
|
|
|
|
|
IRedisRepository<ProtectionDeviceCommInfoOutput, string> protectionDeviceCommInfoOutputredisRepository,
|
|
|
|
|
|
IWebSocketMessageBroadcaster webSocketBroadcaster,
|
|
|
|
|
|
WebApiRequest webApiRequest,
|
|
|
|
|
|
ITcpClient tcpClient,
|
2025-10-30 12:24:29 +08:00
|
|
|
|
TelemeteringHandle telemeteringHandle,
|
|
|
|
|
|
TelesignalisationHandle telesignalisationHandle)
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
|
|
|
|
_queryService = queryService ?? throw new ArgumentNullException(nameof(queryService));
|
|
|
|
|
|
_protectionDeviceCommInfoOutputredisRepository = protectionDeviceCommInfoOutputredisRepository ?? throw new ArgumentNullException(nameof(protectionDeviceCommInfoOutputredisRepository));
|
|
|
|
|
|
_webSocketBroadcaster = webSocketBroadcaster ?? throw new ArgumentNullException(nameof(webSocketBroadcaster));
|
|
|
|
|
|
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
|
|
|
|
|
|
_tcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
|
|
|
|
|
|
_telemeteringHandle = telemeteringHandle ?? throw new ArgumentNullException(nameof(telemeteringHandle));
|
2025-10-30 12:24:29 +08:00
|
|
|
|
_telesignalisationHandle = telesignalisationHandle ?? throw new ArgumentNullException(nameof(telesignalisationHandle));
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理查询请求
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dz">定值参数</param>
|
|
|
|
|
|
/// <param name="faultRpt">故障报告参数</param>
|
|
|
|
|
|
/// <param name="waveCfg">波形配置参数</param>
|
|
|
|
|
|
/// <param name="waveDat">波形文件参数</param>
|
|
|
|
|
|
/// <param name="version">装置版本参数</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>查询结果</returns>
|
|
|
|
|
|
[HttpGet]
|
|
|
|
|
|
public async Task<IActionResult> ProcessQuery(
|
|
|
|
|
|
[FromQuery] string? dz = null,
|
|
|
|
|
|
[FromQuery] string? faultRpt = null,
|
|
|
|
|
|
[FromQuery] string? waveCfg = null,
|
|
|
|
|
|
[FromQuery] string? waveDat = null,
|
|
|
|
|
|
[FromQuery] string? version = null,
|
|
|
|
|
|
CancellationToken cancellationToken = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
LogQueryRequest(dz, faultRpt, waveCfg, waveDat, version);
|
2025-07-31 18:51:24 +08:00
|
|
|
|
|
2025-10-21 09:59:22 +08:00
|
|
|
|
var result = await DetermineAndExecuteQueryAsync(dz, faultRpt, waveCfg, waveDat, version, cancellationToken);
|
2025-07-31 18:51:24 +08:00
|
|
|
|
|
2025-10-21 09:59:22 +08:00
|
|
|
|
return CreateActionResult(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (OperationCanceledException)
|
|
|
|
|
|
{
|
|
|
|
|
|
return HandleOperationCanceled();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return HandleUnexpectedException(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 记录查询请求日志
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dz">定值参数</param>
|
|
|
|
|
|
/// <param name="faultRpt">故障报告参数</param>
|
|
|
|
|
|
/// <param name="waveCfg">波形配置参数</param>
|
|
|
|
|
|
/// <param name="waveDat">波形文件参数</param>
|
|
|
|
|
|
/// <param name="version">装置版本参数</param>
|
|
|
|
|
|
private void LogQueryRequest(string? dz, string? faultRpt, string? waveCfg, string? waveDat, string? version)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("收到查询请求 - dz:{Dz}, faultRpt:{FaultRpt}, waveCfg:{WaveCfg}, waveDat:{WaveDat}, version:{Version}",
|
|
|
|
|
|
dz, faultRpt, waveCfg, waveDat, version);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 确定查询类型并执行相应的查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dz">定值参数</param>
|
|
|
|
|
|
/// <param name="faultRpt">故障报告参数</param>
|
|
|
|
|
|
/// <param name="waveCfg">波形配置参数</param>
|
|
|
|
|
|
/// <param name="waveDat">波形文件参数</param>
|
|
|
|
|
|
/// <param name="version">装置版本参数</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>查询结果</returns>
|
|
|
|
|
|
private async Task<WebResult<object>> DetermineAndExecuteQueryAsync(
|
|
|
|
|
|
string? dz,
|
|
|
|
|
|
string? faultRpt,
|
|
|
|
|
|
string? waveCfg,
|
|
|
|
|
|
string? waveDat,
|
|
|
|
|
|
string? version,
|
|
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
//_logger.LogWarning("收到查询命令: {Item}", yxItem?.ToString());
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(dz))
|
|
|
|
|
|
{
|
|
|
|
|
|
return await ExecuteSettingValueQueryAsync(dz, cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrEmpty(faultRpt))
|
|
|
|
|
|
{
|
|
|
|
|
|
return await ExecuteFaultReportQueryAsync(faultRpt, cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrEmpty(waveCfg))
|
|
|
|
|
|
{
|
|
|
|
|
|
return await ExecuteWaveConfigQueryAsync(waveCfg, cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrEmpty(waveDat))
|
|
|
|
|
|
{
|
|
|
|
|
|
return await ExecuteWaveDataQueryAsync(waveDat, cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrEmpty(version))
|
|
|
|
|
|
{
|
|
|
|
|
|
return await ExecuteVersionQueryAsync(version, cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return WebResult<object>.InvalidParameter();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 执行定值查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="parameter">查询参数</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>查询结果</returns>
|
|
|
|
|
|
private async Task<WebResult<object>> ExecuteSettingValueQueryAsync(string parameter, CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
return await _queryService.ProcessRequestAsync(parameter, "dz", "定值", cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 执行故障报告查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="parameter">查询参数(格式:GetFaultRptByTime|2025-08-20 00:00|2025-08-20 23:59)</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>查询结果</returns>
|
|
|
|
|
|
private async Task<WebResult<object>> ExecuteFaultReportQueryAsync(string parameter, CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
return await _queryService.ProcessRequestAsync(parameter, "faultRpt", "故障报告", cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 执行波形配置查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="parameter">查询参数</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>查询结果</returns>
|
|
|
|
|
|
private async Task<WebResult<object>> ExecuteWaveConfigQueryAsync(string parameter, CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
return await _queryService.ProcessRequestAsync(parameter, "waveCfg", "波形配置", cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 执行波形文件查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="parameter">查询参数</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>查询结果</returns>
|
|
|
|
|
|
private async Task<WebResult<object>> ExecuteWaveDataQueryAsync(string parameter, CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
return await _queryService.ProcessRequestAsync(parameter, "waveDat", "波形文件", cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 执行装置版本查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="parameter">查询参数</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>查询结果</returns>
|
|
|
|
|
|
private async Task<WebResult<object>> ExecuteVersionQueryAsync(string parameter, CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
return await _queryService.ProcessRequestAsync(parameter, "version", "装置版本", cancellationToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建ActionResult响应
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="result">查询结果</param>
|
|
|
|
|
|
/// <returns>ActionResult</returns>
|
|
|
|
|
|
private IActionResult CreateActionResult(WebResult<object> result)
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(result.StatusCode, result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理操作取消异常
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>ActionResult</returns>
|
|
|
|
|
|
private IActionResult HandleOperationCanceled()
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("查询请求被取消");
|
|
|
|
|
|
return StatusCode(504, "请求超时");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理未预期的异常
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="ex">异常信息</param>
|
|
|
|
|
|
/// <returns>ActionResult</returns>
|
|
|
|
|
|
private IActionResult HandleUnexpectedException(Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "处理查询请求时发生异常");
|
|
|
|
|
|
return StatusCode(500, $"服务器内部错误: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据数据ID召唤遥测数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>召唤结果</returns>
|
|
|
|
|
|
/// <remarks>
|
2025-10-30 12:24:29 +08:00
|
|
|
|
/// 示例请求:POST
|
|
|
|
|
|
/*
|
|
|
|
|
|
{
|
|
|
|
|
|
"id":
|
|
|
|
|
|
[
|
|
|
|
|
|
"YCB001211132",
|
|
|
|
|
|
"YCB001177030",
|
|
|
|
|
|
],
|
|
|
|
|
|
"times": 10
|
|
|
|
|
|
}
|
|
|
|
|
|
*/
|
2025-10-21 09:59:22 +08:00
|
|
|
|
/// </remarks>
|
2025-10-26 15:12:32 +08:00
|
|
|
|
[HttpPost("CallYCByDataId")]
|
2025-10-21 09:59:22 +08:00
|
|
|
|
public async Task<IActionResult> CallYCByDataId(
|
2025-10-26 15:12:32 +08:00
|
|
|
|
[FromBody] CallYCByDataIdRequest request,
|
2025-10-21 09:59:22 +08:00
|
|
|
|
CancellationToken cancellationToken = default)
|
|
|
|
|
|
{
|
2025-10-26 15:12:32 +08:00
|
|
|
|
var id = request.Id;
|
|
|
|
|
|
var times = request.Times;
|
|
|
|
|
|
|
|
|
|
|
|
id.RemoveAll(string.IsNullOrWhiteSpace);
|
|
|
|
|
|
|
|
|
|
|
|
string cmd = string.Format("CallYCByDataID|{0}", string.Join("#", id));
|
|
|
|
|
|
|
2025-10-21 09:59:22 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 参数验证
|
2025-10-26 15:12:32 +08:00
|
|
|
|
if (id.Count == 0)
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
return BadRequest(new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
2025-10-26 15:12:32 +08:00
|
|
|
|
message = "未提供数据ID",
|
2025-10-21 09:59:22 +08:00
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
2025-10-21 09:59:22 +08:00
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("收到遥测数据召唤请求 - cmd: {cmd}", cmd);
|
|
|
|
|
|
// 发送TCP消息
|
|
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
DateTime cmdTime = DateTime.Now;
|
|
|
|
|
|
|
2025-10-21 09:59:22 +08:00
|
|
|
|
int sucessCount = 0;
|
|
|
|
|
|
for (int i = 0; i < times; i++)
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
var sendResult = await SendTcpMessageAsync(cmd, cancellationToken);
|
|
|
|
|
|
if (sendResult.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
sucessCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
await Task.Delay(1000);
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
2025-10-21 09:59:22 +08:00
|
|
|
|
|
2025-10-26 15:12:32 +08:00
|
|
|
|
|
2025-10-21 09:59:22 +08:00
|
|
|
|
|
|
|
|
|
|
if (times-sucessCount<3)
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-26 15:12:32 +08:00
|
|
|
|
List<YCResultData> ycDataList = null;
|
|
|
|
|
|
|
|
|
|
|
|
var sw = Stopwatch.StartNew();
|
|
|
|
|
|
while(sw.ElapsedMilliseconds < 10*1000)
|
2025-10-21 09:59:22 +08:00
|
|
|
|
{
|
2025-10-26 15:12:32 +08:00
|
|
|
|
ycDataList = await _telemeteringHandle.GetYCDataByDataIds(id, cmdTime, sucessCount);
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
2025-10-21 09:59:22 +08:00
|
|
|
|
else
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
return StatusCode(500, new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
|
|
|
|
|
message = $"发送命令失败",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
2025-10-21 09:59:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (OperationCanceledException)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("遥测数据召唤请求被取消 - cmd: {cmd}", cmd);
|
|
|
|
|
|
return StatusCode(504, new
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
success = false,
|
|
|
|
|
|
message = "请求超时",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "处理遥测数据召唤请求时发生异常 - cmd: {cmd}", cmd);
|
|
|
|
|
|
return StatusCode(500, new
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
success = false,
|
|
|
|
|
|
message = $"服务器内部错误: {ex.Message}",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-31 18:51:24 +08:00
|
|
|
|
|
2025-10-30 12:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据数据ID召唤遥信数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>召唤结果</returns>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// 示例请求:POST
|
|
|
|
|
|
/*
|
|
|
|
|
|
{
|
|
|
|
|
|
"id":
|
|
|
|
|
|
[
|
|
|
|
|
|
"YCB001211132",
|
|
|
|
|
|
"YCB001177030",
|
|
|
|
|
|
],
|
|
|
|
|
|
"times": 10
|
|
|
|
|
|
}
|
|
|
|
|
|
*/
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
[HttpPost("CallYXByDataId")]
|
|
|
|
|
|
public async Task<IActionResult> CallYXByDataId(
|
|
|
|
|
|
[FromBody] CallYCByDataIdRequest request,
|
|
|
|
|
|
CancellationToken cancellationToken = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
var ids = request.Id;
|
|
|
|
|
|
var times = request.Times;
|
|
|
|
|
|
|
|
|
|
|
|
ids.RemoveAll(string.IsNullOrWhiteSpace);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 参数验证
|
|
|
|
|
|
if (ids.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return BadRequest(new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
|
|
|
|
|
message = "未提供数据ID",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var dataList = await _telesignalisationHandle.GetYXDataByDataIds(ids, times, cancellationToken);
|
|
|
|
|
|
|
|
|
|
|
|
return Ok(new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = true,
|
|
|
|
|
|
message = "获取遥信数据成功",
|
|
|
|
|
|
data = dataList,
|
|
|
|
|
|
count = dataList.Count,
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch(Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return StatusCode(500, new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
|
|
|
|
|
message = $"获取遥信数据失败: {ex.Message}",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-21 09:59:22 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 发送TCP消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="message">消息内容</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>发送结果</returns>
|
|
|
|
|
|
private async Task<(bool Success, string ErrorMessage)> SendTcpMessageAsync(string message, CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
2025-07-31 18:51:24 +08:00
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
// 检查TCP连接状态
|
|
|
|
|
|
if (_queryService == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (false, "查询服务未初始化");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用QueryService的TCP客户端发送消息
|
|
|
|
|
|
// 注意:这里直接通过ITcpClient发送,不需要等待响应
|
|
|
|
|
|
var tcpClient = GetTcpClientFromQueryService();
|
|
|
|
|
|
if (tcpClient == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (false, "TCP客户端未初始化");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!tcpClient.IsConnected)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (false, "TCP连接未建立");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var sendResult = await tcpClient.SendMessageAsync(message, cancellationToken);
|
|
|
|
|
|
|
|
|
|
|
|
if (!sendResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (false, "TCP消息发送失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (true, string.Empty);
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2025-10-21 09:59:22 +08:00
|
|
|
|
_logger.LogError(ex, "发送TCP消息时发生异常");
|
|
|
|
|
|
return (false, ex.Message);
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-21 09:59:22 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从QueryService获取TCP客户端
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>TCP客户端实例</returns>
|
|
|
|
|
|
private ITcpClient GetTcpClientFromQueryService()
|
|
|
|
|
|
{
|
|
|
|
|
|
return _tcpClient;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-31 18:51:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取服务状态
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>服务状态</returns>
|
|
|
|
|
|
[HttpGet("status")]
|
|
|
|
|
|
public IActionResult GetStatus()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var status = new
|
|
|
|
|
|
{
|
|
|
|
|
|
IsQueryInProgress = _queryService.IsQueryInProgress,
|
|
|
|
|
|
Timestamp = DateTime.Now,
|
|
|
|
|
|
Status = "Running"
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-03 10:16:04 +08:00
|
|
|
|
return Ok(status);
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "获取服务状态时发生异常");
|
|
|
|
|
|
return StatusCode(500, $"获取状态失败: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 健康检查端点
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>健康状态</returns>
|
|
|
|
|
|
[HttpGet("health")]
|
|
|
|
|
|
public IActionResult HealthCheck()
|
|
|
|
|
|
{
|
|
|
|
|
|
return Ok("Healthy");
|
|
|
|
|
|
}
|
2025-10-21 09:59:22 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// HTTP消息转发到WebSocket
|
|
|
|
|
|
/// 支持ThingJS语法的事件和数据转发
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="request">转发请求</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>转发结果</returns>
|
|
|
|
|
|
[HttpPost("forward-to-websocket")]
|
|
|
|
|
|
public async Task<IActionResult> ForwardToWebSocket([FromBody] object request, CancellationToken cancellationToken = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 参数验证
|
|
|
|
|
|
if (request == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return BadRequest(new { message = "请求体不能为空" });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var jsonData = JToken.FromObject(request);
|
|
|
|
|
|
|
|
|
|
|
|
// 🔧 修复:智能确定目标分组,而不是硬编码为"thing"
|
|
|
|
|
|
var targetGroups = DetermineTargetGroupsFromMessage(jsonData);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理检修状态消息
|
|
|
|
|
|
await ProcessMaintenanceStatusMessage(jsonData);
|
|
|
|
|
|
|
|
|
|
|
|
var forwardResults = new List<string>();
|
|
|
|
|
|
|
|
|
|
|
|
// 转发到所有确定的目标分组
|
|
|
|
|
|
foreach (var targetGroup in targetGroups)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await _webSocketBroadcaster.PushToGroupAsync(targetGroup, "ex", jsonData, useRawData: true);
|
|
|
|
|
|
forwardResults.Add($"{targetGroup}: 成功");
|
|
|
|
|
|
_logger.LogDebug("消息已成功转发到WebSocket分组: {Group}", targetGroup);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception groupEx)
|
|
|
|
|
|
{
|
|
|
|
|
|
forwardResults.Add($"{targetGroup}: 失败 - {groupEx.Message}");
|
|
|
|
|
|
_logger.LogError(groupEx, "转发消息到分组 {Group} 失败", targetGroup);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("消息转发完成 - 目标分组: {Groups}, 结果: {Results}",
|
|
|
|
|
|
string.Join(", ", targetGroups), string.Join("; ", forwardResults));
|
|
|
|
|
|
|
|
|
|
|
|
return Ok(new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = true,
|
|
|
|
|
|
message = "消息转发完成",
|
|
|
|
|
|
targetGroups = targetGroups,
|
|
|
|
|
|
results = forwardResults,
|
|
|
|
|
|
timestamp = DateTime.UtcNow
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (JsonException jsonEx)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(jsonEx, "解析JSON数据时发生错误");
|
|
|
|
|
|
return BadRequest(new { message = $"JSON格式错误: {jsonEx.Message}" });
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "转发消息到WebSocket时发生异常");
|
|
|
|
|
|
return StatusCode(500, new { message = $"服务器内部错误: {ex.Message}" });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据消息内容确定目标分组
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="jsonData">消息数据</param>
|
|
|
|
|
|
/// <returns>目标分组列表</returns>
|
|
|
|
|
|
private List<string> DetermineTargetGroupsFromMessage(JToken jsonData)
|
|
|
|
|
|
{
|
|
|
|
|
|
var targetGroups = new List<string>();
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查消息是否包含检修状态信息
|
|
|
|
|
|
if (IsMaintenanceStatusMessage(jsonData))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检修状态消息发送到thing分组进行处理
|
|
|
|
|
|
targetGroups.Add("thing");
|
|
|
|
|
|
return targetGroups;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查消息类型并确定分组
|
|
|
|
|
|
var messageType = DetermineMessageType(jsonData);
|
|
|
|
|
|
|
|
|
|
|
|
switch (messageType.ToUpper())
|
|
|
|
|
|
{
|
|
|
|
|
|
case "YC":
|
|
|
|
|
|
case "TELEMETERING":
|
|
|
|
|
|
targetGroups.Add("YC");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "YX":
|
|
|
|
|
|
case "TELESIGNALISATION":
|
|
|
|
|
|
targetGroups.Add("YX");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "ALARM":
|
|
|
|
|
|
case "ALERT":
|
|
|
|
|
|
targetGroups.Add("ALERT");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "COMMSTATE":
|
|
|
|
|
|
targetGroups.Add("COMMSTATE");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "THING":
|
|
|
|
|
|
default:
|
|
|
|
|
|
// 默认发送到thing分组
|
|
|
|
|
|
targetGroups.Add("thing");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 同时发送到ALL分组以保持兼容性
|
|
|
|
|
|
if (!targetGroups.Contains("ALL"))
|
|
|
|
|
|
{
|
|
|
|
|
|
targetGroups.Add("ALL");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning(ex, "确定目标分组时发生错误,使用默认分组");
|
|
|
|
|
|
targetGroups.Clear();
|
|
|
|
|
|
targetGroups.Add("thing");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return targetGroups;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 判断是否为检修状态消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="jsonData">消息数据</param>
|
|
|
|
|
|
/// <returns>是否为检修状态消息</returns>
|
|
|
|
|
|
private bool IsMaintenanceStatusMessage(JToken jsonData)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (jsonData is JObject obj)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否包含检修状态的键值对
|
|
|
|
|
|
foreach (var property in obj.Properties())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (property.Value?.ToString().Contains("检修") == true)
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogDebug(ex, "检查检修状态消息时发生错误");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 确定消息类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="jsonData">消息数据</param>
|
|
|
|
|
|
/// <returns>消息类型</returns>
|
|
|
|
|
|
private string DetermineMessageType(JToken jsonData)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查消息中是否包含类型字段
|
|
|
|
|
|
if (jsonData["type"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return jsonData["type"].ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (jsonData["messageType"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return jsonData["messageType"].ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据数据结构推断类型
|
|
|
|
|
|
if (jsonData["AlarmLevel"] != null || jsonData["AlarmType"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return "ALARM";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (jsonData["ResultValue"] != null && jsonData["ResultTime"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return "YC"; // 遥测数据
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (jsonData["Value"] != null && jsonData["Time"] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return "YX"; // 遥信数据
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogDebug(ex, "确定消息类型时发生错误");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return "THING"; // 默认类型
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理检修状态消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="jsonData">消息数据</param>
|
|
|
|
|
|
/// <returns>处理任务</returns>
|
|
|
|
|
|
private async Task ProcessMaintenanceStatusMessage(JToken jsonData)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (IsMaintenanceStatusMessage(jsonData) && jsonData is JObject obj)
|
|
|
|
|
|
{
|
|
|
|
|
|
var maintenanceData = new Dictionary<string, string>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var property in obj.Properties())
|
|
|
|
|
|
{
|
|
|
|
|
|
var value = property.Value?.ToString();
|
|
|
|
|
|
if (!string.IsNullOrEmpty(value))
|
|
|
|
|
|
{
|
|
|
|
|
|
maintenanceData[property.Name] = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (maintenanceData.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation("检测到检修状态消息: {MaintenanceData}",
|
|
|
|
|
|
string.Join(", ", maintenanceData.Select(kvp => $"{kvp.Key}={kvp.Value}")));
|
|
|
|
|
|
|
|
|
|
|
|
// 🔧 更新Redis中的设备检修状态
|
|
|
|
|
|
await UpdateDeviceMaintenanceStatusInRedis(maintenanceData);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "处理检修状态消息时发生错误");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 🔧 新增:更新Redis中的设备检修状态
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="maintenanceData">检修状态数据</param>
|
|
|
|
|
|
/// <returns>处理任务</returns>
|
|
|
|
|
|
private async Task UpdateDeviceMaintenanceStatusInRedis(Dictionary<string, string> maintenanceData)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
const string protectionDeviceCommInfoRedisKey = "protectionDeviceCommInfo";
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var kvp in maintenanceData)
|
|
|
|
|
|
{
|
|
|
|
|
|
var twinId = kvp.Key;
|
|
|
|
|
|
var maintenanceStatus = kvp.Value;
|
|
|
|
|
|
var isInMaintenance = maintenanceStatus.Contains("检修");
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 使用TwinIdApiService查询TwinId对应的EquipmentInfoId
|
|
|
|
|
|
var equipmentInfoId = await _webApiRequest.GetTwIdAsync(twinId);
|
|
|
|
|
|
|
|
|
|
|
|
// 从Redis获取所有保护装置通信信息
|
|
|
|
|
|
var allDevices = await _protectionDeviceCommInfoOutputredisRepository.HashSetGetAllAsync(protectionDeviceCommInfoRedisKey);
|
|
|
|
|
|
|
|
|
|
|
|
// 查找匹配的设备
|
|
|
|
|
|
var targetDevice = allDevices.FirstOrDefault(d => d.EquipmentInfoId == equipmentInfoId);
|
|
|
|
|
|
if (targetDevice != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 更新检修状态
|
|
|
|
|
|
targetDevice.IsMaintenance = isInMaintenance;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新Redis中的数据
|
|
|
|
|
|
await _protectionDeviceCommInfoOutputredisRepository.HashSetUpdateOneAsync(
|
|
|
|
|
|
protectionDeviceCommInfoRedisKey,
|
|
|
|
|
|
targetDevice.DeviceAddr.ToString(),
|
|
|
|
|
|
targetDevice);
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("已更新设备检修状态 - TwinId: {TwinId}, EquipmentInfoId: {EquipmentInfoId}, IsMaintenance: {IsMaintenance}",
|
|
|
|
|
|
twinId, equipmentInfoId, isInMaintenance);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("未找到对应的保护装置设备 - EquipmentInfoId: {EquipmentInfoId}", equipmentInfoId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "更新设备检修状态失败 - TwinId: {TwinId}", twinId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "更新Redis中的设备检修状态时发生错误");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 解析ThingJS语法的数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">原始数据字符串</param>
|
|
|
|
|
|
/// <returns>解析后的数据</returns>
|
|
|
|
|
|
private object ParseThingJSData(string data)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查是否是ThingJS语法(以#开头)
|
|
|
|
|
|
if (data.StartsWith("#"))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 解析ThingJS语法,例如 #UINO_T1
|
|
|
|
|
|
var thingId = data.Substring(1); // 移除#前缀
|
|
|
|
|
|
|
|
|
|
|
|
return new
|
|
|
|
|
|
{
|
|
|
|
|
|
type = "thing",
|
|
|
|
|
|
thingId = thingId,
|
|
|
|
|
|
selector = data,
|
|
|
|
|
|
originalData = data
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试解析为JSON
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return JsonConvert.DeserializeObject(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果不是JSON,返回原始字符串
|
|
|
|
|
|
return data;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning(ex, "解析ThingJS数据时发生警告,返回原始数据: {Data}", data);
|
|
|
|
|
|
return data;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据事件名称和数据确定目标分组
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="eventName">事件名称</param>
|
|
|
|
|
|
/// <param name="data">数据内容</param>
|
|
|
|
|
|
/// <returns>目标分组名称</returns>
|
|
|
|
|
|
private string DetermineTargetGroup(string eventName, string data)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 根据事件名称确定分组
|
|
|
|
|
|
var eventLower = eventName.ToLower();
|
|
|
|
|
|
|
|
|
|
|
|
if (eventLower.Contains("yc") || eventLower.Contains("telemetering"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return WebSocketGroups.YC;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (eventLower.Contains("yx") || eventLower.Contains("telesignalisation"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return WebSocketGroups.YX;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (eventLower.Contains("alarm") || eventLower.Contains("alert"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return WebSocketGroups.ALERT;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (eventLower.Contains("comm") || eventLower.Contains("communication"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return WebSocketGroups.COMMSTATE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是Thing相关的事件
|
|
|
|
|
|
if (data.StartsWith("#") || eventLower.Contains("thing"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return "thing"; // Thing专用分组
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 默认发送到ALL分组
|
|
|
|
|
|
return WebSocketGroups.ALL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取召唤的遥测数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dataIds">数据ID字符串,格式:DataID1#DataID2#DataID3#......</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <returns>遥测数据列表</returns>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// 示例请求:GET /api/GetCollectedYCData?dataIds=001#002#003
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
[HttpGet("GetCollectedYCData")]
|
|
|
|
|
|
public async Task<IActionResult> GetCollectedYCData(
|
|
|
|
|
|
[FromQuery] string dataIds,
|
|
|
|
|
|
CancellationToken cancellationToken = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 参数验证
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(dataIds))
|
|
|
|
|
|
{
|
|
|
|
|
|
return BadRequest(new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
|
|
|
|
|
message = "参数 dataIds 不能为空",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("收到获取遥测数据请求 - dataIds: {DataIds}", dataIds);
|
|
|
|
|
|
|
|
|
|
|
|
// 解析DataId列表
|
|
|
|
|
|
var dataIdList = dataIds.Split('#', StringSplitOptions.RemoveEmptyEntries)
|
|
|
|
|
|
.Select(id => id.Trim())
|
|
|
|
|
|
.Where(id => !string.IsNullOrWhiteSpace(id))
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
if (dataIdList.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return BadRequest(new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
|
|
|
|
|
message = "未提供有效的数据ID",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从TelemeteringHandle获取数据
|
|
|
|
|
|
var ycDataList = _telemeteringHandle.GetYCDataByDataIds(dataIdList);
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("获取遥测数据成功 - 请求DataId数: {RequestCount}, 返回数据数: {ResultCount}",
|
|
|
|
|
|
dataIdList.Count, ycDataList.Count);
|
|
|
|
|
|
|
|
|
|
|
|
return Ok(new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = true,
|
|
|
|
|
|
message = "获取遥测数据成功",
|
|
|
|
|
|
data = ycDataList,
|
|
|
|
|
|
count = ycDataList.Count,
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (OperationCanceledException)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning("获取遥测数据请求被取消 - dataIds: {DataIds}", dataIds);
|
|
|
|
|
|
return StatusCode(504, new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
|
|
|
|
|
message = "请求超时",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "获取遥测数据时发生异常 - dataIds: {DataIds}", dataIds);
|
|
|
|
|
|
return StatusCode(500, new
|
|
|
|
|
|
{
|
|
|
|
|
|
success = false,
|
|
|
|
|
|
message = $"服务器内部错误: {ex.Message}",
|
|
|
|
|
|
timestamp = DateTime.Now
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|
2025-10-21 09:59:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-07-31 18:51:24 +08:00
|
|
|
|
}
|