Compare commits
5 Commits
13d5f843ce
...
8a4fceebc0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a4fceebc0 | ||
|
|
0adda965c2 | ||
|
|
c30d957a0e | ||
|
|
1fad43581a | ||
| 8864302771 |
@ -95,9 +95,9 @@ namespace YunDa.Server.ISMSTcp.Configuration
|
|||||||
public string SecondaryCircuitInspectionPlanUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionPlan/GetList";
|
public string SecondaryCircuitInspectionPlanUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionPlan/GetList";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 二次回路获取事件的列表
|
/// 二次回路获取事件巡检的配置数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetCircuitEventDrivenConfigUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitEventDrivenConfig/FindDatasNoPageList";
|
public string GetCircuitEventDrivenConfigUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitEventDrivenConfig/GetList";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取孪生体与遥测数据绑定关系 URI
|
/// 获取孪生体与遥测数据绑定关系 URI
|
||||||
@ -129,6 +129,12 @@ namespace YunDa.Server.ISMSTcp.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetInspectionResultDataUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/OriginalInspectionStoreResult/GetInspectionResultData";
|
public string GetInspectionResultDataUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/OriginalInspectionStoreResult/GetInspectionResultData";
|
||||||
|
|
||||||
|
/// 获取网关数据URI
|
||||||
|
public string GetGatewayInfoDataUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionItem/GetGatewayInfoData";
|
||||||
|
|
||||||
|
/// 获取虚点信息URI
|
||||||
|
public string GetVirtualPointInfoDataUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionItem/GetVirtualPointInfoData";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询网线配置
|
/// 查询网线配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -147,7 +153,19 @@ namespace YunDa.Server.ISMSTcp.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保存巡检计划执行结果
|
/// 保存巡检计划执行结果
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SaveSecondaryCircuitInspectionPlanResultUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/OpticalCable/GetList";
|
public string SaveSecondaryCircuitInspectionPlanResultUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionResult/Create";
|
||||||
|
|
||||||
|
//调用Ai
|
||||||
|
public string GetAIAnalysisUri => "http://192.168.81.25:8002/chat/ai";
|
||||||
|
|
||||||
|
//巡检计划执行结果AI诊断更新
|
||||||
|
public string UpdateReportWithAIAnalysisUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionResult/UpdateReportWithAIAnalysis";
|
||||||
|
|
||||||
|
//获取虚点信息
|
||||||
|
public string GetVariantBaseinfoUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionItem/GetVariantBaseinfo";
|
||||||
|
|
||||||
|
//获取网关信息
|
||||||
|
public string GetGateWayBaseInfoUri => $"{BaseUrl.TrimEnd('/')}/api/services/SOMS/SecondaryCircuitInspectionItem/GetGateWayBaseInfo";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,8 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
private readonly ITcpClient _tcpClient;
|
private readonly ITcpClient _tcpClient;
|
||||||
private readonly TelemeteringHandle _telemeteringHandle;
|
private readonly TelemeteringHandle _telemeteringHandle;
|
||||||
private readonly TelesignalisationHandle _telesignalisationHandle;
|
private readonly TelesignalisationHandle _telesignalisationHandle;
|
||||||
|
private readonly GwErrorRatioService _gwErrorRatioService;
|
||||||
|
private readonly VirtualTerminalHandler _virtualTerminalHandler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
@ -54,7 +56,9 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
WebApiRequest webApiRequest,
|
WebApiRequest webApiRequest,
|
||||||
ITcpClient tcpClient,
|
ITcpClient tcpClient,
|
||||||
TelemeteringHandle telemeteringHandle,
|
TelemeteringHandle telemeteringHandle,
|
||||||
TelesignalisationHandle telesignalisationHandle)
|
TelesignalisationHandle telesignalisationHandle,
|
||||||
|
GwErrorRatioService gwErrorRatioService,
|
||||||
|
VirtualTerminalHandler virtualTerminalHandler)
|
||||||
{
|
{
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_queryService = queryService ?? throw new ArgumentNullException(nameof(queryService));
|
_queryService = queryService ?? throw new ArgumentNullException(nameof(queryService));
|
||||||
@ -64,6 +68,8 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
_tcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
|
_tcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
|
||||||
_telemeteringHandle = telemeteringHandle ?? throw new ArgumentNullException(nameof(telemeteringHandle));
|
_telemeteringHandle = telemeteringHandle ?? throw new ArgumentNullException(nameof(telemeteringHandle));
|
||||||
_telesignalisationHandle = telesignalisationHandle ?? throw new ArgumentNullException(nameof(telesignalisationHandle));
|
_telesignalisationHandle = telesignalisationHandle ?? throw new ArgumentNullException(nameof(telesignalisationHandle));
|
||||||
|
_gwErrorRatioService = gwErrorRatioService ?? throw new ArgumentNullException(nameof(gwErrorRatioService));
|
||||||
|
_virtualTerminalHandler = virtualTerminalHandler ?? throw new ArgumentNullException(nameof(virtualTerminalHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -268,7 +274,7 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
[HttpPost("CallYCByDataId")]
|
[HttpPost("CallYCByDataId")]
|
||||||
public async Task<IActionResult> CallYCByDataId(
|
public async Task<IActionResult> CallYCByDataId(
|
||||||
[FromBody] CallYCByDataIdRequest request,
|
[FromBody] ZzDataRequestModel request,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var id = request.Id;
|
var id = request.Id;
|
||||||
@ -278,6 +284,8 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
|
|
||||||
string cmd = string.Format("CallYCByDataID|{0}", string.Join("#", id));
|
string cmd = string.Format("CallYCByDataID|{0}", string.Join("#", id));
|
||||||
|
|
||||||
|
System.Console.WriteLine($"发送遥测命令:{cmd}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 参数验证
|
// 参数验证
|
||||||
@ -297,26 +305,35 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
DateTime cmdTime = DateTime.Now;
|
DateTime cmdTime = DateTime.Now;
|
||||||
|
|
||||||
int sucessCount = 0;
|
int sucessCount = 0;
|
||||||
for (int i = 0; i < times; i++)
|
|
||||||
|
if(request.TimeWindowType == 1 || request.TimeWindowType == 2)
|
||||||
{
|
{
|
||||||
var sendResult = await SendTcpMessageAsync(cmd, cancellationToken);
|
for (int i = 0; i < times; i++)
|
||||||
if (sendResult.Success)
|
|
||||||
{
|
{
|
||||||
sucessCount++;
|
var sendResult = await SendTcpMessageAsync(cmd, cancellationToken);
|
||||||
|
if (sendResult.Success)
|
||||||
|
{
|
||||||
|
sucessCount++;
|
||||||
|
}
|
||||||
|
await Task.Delay(1000);
|
||||||
}
|
}
|
||||||
await Task.Delay(1000);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sucessCount = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (times-sucessCount<3)
|
if (times - sucessCount < 3)
|
||||||
{
|
{
|
||||||
List<YCResultData> ycDataList = null;
|
List<ZzDataResultModel>? ycDataList = null;
|
||||||
|
|
||||||
var sw = Stopwatch.StartNew();
|
var sw = Stopwatch.StartNew();
|
||||||
while(sw.ElapsedMilliseconds < 10*1000)
|
|
||||||
|
while (sw.ElapsedMilliseconds < 10 * 1000)
|
||||||
{
|
{
|
||||||
ycDataList = await _telemeteringHandle.GetYCDataByDataIds(id, cmdTime, sucessCount);
|
ycDataList = await _telemeteringHandle.GetYCDataByDataIds(id, times, request.TimeWindowType, cancellationToken, cmdTime);
|
||||||
|
|
||||||
if (ycDataList.Count == id.Count)
|
if (ycDataList.Count == id.Count)
|
||||||
break;
|
break;
|
||||||
@ -398,7 +415,7 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
[HttpPost("CallYXByDataId")]
|
[HttpPost("CallYXByDataId")]
|
||||||
public async Task<IActionResult> CallYXByDataId(
|
public async Task<IActionResult> CallYXByDataId(
|
||||||
[FromBody] CallYCByDataIdRequest request,
|
[FromBody] ZzDataRequestModel request,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var ids = request.Id;
|
var ids = request.Id;
|
||||||
@ -419,7 +436,7 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var dataList = await _telesignalisationHandle.GetYXDataByDataIds(ids, times, cancellationToken);
|
var dataList = await _telesignalisationHandle.GetYXDataByDataIds(ids, times, request.TimeWindowType, cancellationToken);
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
@ -439,10 +456,98 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
timestamp = DateTime.Now
|
timestamp = DateTime.Now
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 根据网关Id获取网关通信状态
|
||||||
|
[HttpPost("CallGatewayByDataId")]
|
||||||
|
public async Task<IActionResult> CallGatewayByDataId(
|
||||||
|
[FromBody] ZzDataRequestModel 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 _gwErrorRatioService.GetDataByIds(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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 根据Id获取虚点数据
|
||||||
|
[HttpPost("CallVAByDataId")]
|
||||||
|
public async Task<IActionResult> CallVAByDataId(
|
||||||
|
[FromBody] ZzDataRequestModel 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 _virtualTerminalHandler.GetDataByIds(ids, times, request.TimeWindowType, 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
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -917,87 +1022,87 @@ namespace YunDa.Server.ISMSTcp.Controllers
|
|||||||
return WebSocketGroups.ALL;
|
return WebSocketGroups.ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 获取召唤的遥测数据
|
///// 获取召唤的遥测数据
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <param name="dataIds">数据ID字符串,格式:DataID1#DataID2#DataID3#......</param>
|
///// <param name="dataIds">数据ID字符串,格式:DataID1#DataID2#DataID3#......</param>
|
||||||
/// <param name="cancellationToken">取消令牌</param>
|
///// <param name="cancellationToken">取消令牌</param>
|
||||||
/// <returns>遥测数据列表</returns>
|
///// <returns>遥测数据列表</returns>
|
||||||
/// <remarks>
|
///// <remarks>
|
||||||
/// 示例请求:GET /api/GetCollectedYCData?dataIds=001#002#003
|
///// 示例请求:GET /api/GetCollectedYCData?dataIds=001#002#003
|
||||||
/// </remarks>
|
///// </remarks>
|
||||||
[HttpGet("GetCollectedYCData")]
|
//[HttpGet("GetCollectedYCData")]
|
||||||
public async Task<IActionResult> GetCollectedYCData(
|
//public async Task<IActionResult> GetCollectedYCData(
|
||||||
[FromQuery] string dataIds,
|
// [FromQuery] string dataIds,
|
||||||
CancellationToken cancellationToken = default)
|
// CancellationToken cancellationToken = default)
|
||||||
{
|
//{
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
// 参数验证
|
// // 参数验证
|
||||||
if (string.IsNullOrWhiteSpace(dataIds))
|
// if (string.IsNullOrWhiteSpace(dataIds))
|
||||||
{
|
// {
|
||||||
return BadRequest(new
|
// return BadRequest(new
|
||||||
{
|
// {
|
||||||
success = false,
|
// success = false,
|
||||||
message = "参数 dataIds 不能为空",
|
// message = "参数 dataIds 不能为空",
|
||||||
timestamp = DateTime.Now
|
// timestamp = DateTime.Now
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
_logger.LogInformation("收到获取遥测数据请求 - dataIds: {DataIds}", dataIds);
|
// _logger.LogInformation("收到获取遥测数据请求 - dataIds: {DataIds}", dataIds);
|
||||||
|
|
||||||
// 解析DataId列表
|
// // 解析DataId列表
|
||||||
var dataIdList = dataIds.Split('#', StringSplitOptions.RemoveEmptyEntries)
|
// var dataIdList = dataIds.Split('#', StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(id => id.Trim())
|
// .Select(id => id.Trim())
|
||||||
.Where(id => !string.IsNullOrWhiteSpace(id))
|
// .Where(id => !string.IsNullOrWhiteSpace(id))
|
||||||
.ToList();
|
// .ToList();
|
||||||
|
|
||||||
if (dataIdList.Count == 0)
|
// if (dataIdList.Count == 0)
|
||||||
{
|
// {
|
||||||
return BadRequest(new
|
// return BadRequest(new
|
||||||
{
|
// {
|
||||||
success = false,
|
// success = false,
|
||||||
message = "未提供有效的数据ID",
|
// message = "未提供有效的数据ID",
|
||||||
timestamp = DateTime.Now
|
// timestamp = DateTime.Now
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 从TelemeteringHandle获取数据
|
// // 从TelemeteringHandle获取数据
|
||||||
var ycDataList = _telemeteringHandle.GetYCDataByDataIds(dataIdList);
|
// var ycDataList = _telemeteringHandle.GetYCDataByDataIds(dataIdList);
|
||||||
|
|
||||||
_logger.LogInformation("获取遥测数据成功 - 请求DataId数: {RequestCount}, 返回数据数: {ResultCount}",
|
// _logger.LogInformation("获取遥测数据成功 - 请求DataId数: {RequestCount}, 返回数据数: {ResultCount}",
|
||||||
dataIdList.Count, ycDataList.Count);
|
// dataIdList.Count, ycDataList.Count);
|
||||||
|
|
||||||
return Ok(new
|
// return Ok(new
|
||||||
{
|
// {
|
||||||
success = true,
|
// success = true,
|
||||||
message = "获取遥测数据成功",
|
// message = "获取遥测数据成功",
|
||||||
data = ycDataList,
|
// data = ycDataList,
|
||||||
count = ycDataList.Count,
|
// count = ycDataList.Count,
|
||||||
timestamp = DateTime.Now
|
// timestamp = DateTime.Now
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
catch (OperationCanceledException)
|
// catch (OperationCanceledException)
|
||||||
{
|
// {
|
||||||
_logger.LogWarning("获取遥测数据请求被取消 - dataIds: {DataIds}", dataIds);
|
// _logger.LogWarning("获取遥测数据请求被取消 - dataIds: {DataIds}", dataIds);
|
||||||
return StatusCode(504, new
|
// return StatusCode(504, new
|
||||||
{
|
// {
|
||||||
success = false,
|
// success = false,
|
||||||
message = "请求超时",
|
// message = "请求超时",
|
||||||
timestamp = DateTime.Now
|
// timestamp = DateTime.Now
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
catch (Exception ex)
|
// catch (Exception ex)
|
||||||
{
|
// {
|
||||||
_logger.LogError(ex, "获取遥测数据时发生异常 - dataIds: {DataIds}", dataIds);
|
// _logger.LogError(ex, "获取遥测数据时发生异常 - dataIds: {DataIds}", dataIds);
|
||||||
return StatusCode(500, new
|
// return StatusCode(500, new
|
||||||
{
|
// {
|
||||||
success = false,
|
// success = false,
|
||||||
message = $"服务器内部错误: {ex.Message}",
|
// message = $"服务器内部错误: {ex.Message}",
|
||||||
timestamp = DateTime.Now
|
// timestamp = DateTime.Now
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,12 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string GetInspectionResultDataUri { get; }
|
string GetInspectionResultDataUri { get; }
|
||||||
|
|
||||||
|
//查询网关数据
|
||||||
|
string GetGatewayInfoDataUri { get; }
|
||||||
|
|
||||||
|
//查询虚点信息
|
||||||
|
string GetVirtualPointInfoDataUri { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询网线配置
|
/// 查询网线配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -106,6 +112,19 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string SaveSecondaryCircuitInspectionPlanResultUri { get; }
|
string SaveSecondaryCircuitInspectionPlanResultUri { get; }
|
||||||
|
|
||||||
|
|
||||||
|
//调用Ai
|
||||||
|
string GetAIAnalysisUri { get; }
|
||||||
|
|
||||||
|
//巡检计划执行结果AI诊断更新
|
||||||
|
string UpdateReportWithAIAnalysisUri { get; }
|
||||||
|
|
||||||
|
//获取虚点信息
|
||||||
|
string GetVariantBaseinfoUri { get; }
|
||||||
|
|
||||||
|
//获取网关信息
|
||||||
|
string GetGateWayBaseInfoUri { get; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -207,6 +226,13 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
public string GetInspectionResultDataUri => _config.GetInspectionResultDataUri;
|
public string GetInspectionResultDataUri => _config.GetInspectionResultDataUri;
|
||||||
|
|
||||||
|
|
||||||
|
//查询网关数据
|
||||||
|
public string GetGatewayInfoDataUri => _config.GetGatewayInfoDataUri;
|
||||||
|
|
||||||
|
//查询虚点信息
|
||||||
|
public string GetVirtualPointInfoDataUri => _config.GetVirtualPointInfoDataUri;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询网线配置
|
/// 查询网线配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -227,5 +253,17 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string SaveSecondaryCircuitInspectionPlanResultUri => _config.SaveSecondaryCircuitInspectionPlanResultUri;
|
public string SaveSecondaryCircuitInspectionPlanResultUri => _config.SaveSecondaryCircuitInspectionPlanResultUri;
|
||||||
|
|
||||||
|
//调用Ai
|
||||||
|
public string GetAIAnalysisUri => _config.GetAIAnalysisUri;
|
||||||
|
|
||||||
|
//巡检计划执行结果AI诊断更新
|
||||||
|
public string UpdateReportWithAIAnalysisUri => _config.UpdateReportWithAIAnalysisUri;
|
||||||
|
|
||||||
|
//获取虚点信息
|
||||||
|
public string GetVariantBaseinfoUri => _config.GetVariantBaseinfoUri;
|
||||||
|
|
||||||
|
//获取网关信息
|
||||||
|
public string GetGateWayBaseInfoUri => _config.GetGateWayBaseInfoUri;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -648,12 +648,12 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
/// 获取二次回路巡检计划
|
/// 获取二次回路巡检计划
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>二次回路巡检计划列表</returns>
|
/// <returns>二次回路巡检计划列表</returns>
|
||||||
public async Task<List<SecondaryCircuitInspectionPlanOutput>> GetSecondaryCircuitInspectionPlanListAsync()
|
public async Task<List<SecondaryCircuitInspectionPlanOutput>> GetSecondaryCircuitInspectionPlanListAsync(int start)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
var response = await HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.SecondaryCircuitInspectionPlanUri, new object());
|
var response = await HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.SecondaryCircuitInspectionPlanUri, new { maxResultCount = 10, skipCount = start, includeInspectionItems = true });
|
||||||
|
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
@ -672,14 +672,14 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取事件的列表
|
/// 获取时间巡检的配置数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<SecondaryCircuitEventDrivenConfigOutput>> GetCircuitEventDrivenConfigAsync()
|
public async Task<List<SecondaryCircuitEventDrivenConfigOutput>> GetCircuitEventDrivenConfigAsync(int start)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
var response = await HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetCircuitEventDrivenConfigUri, new object());
|
var response = await HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.GetCircuitEventDrivenConfigUri, new { maxResultCount = 10, skipCount = start, includeInspectionItems = true });
|
||||||
|
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
@ -751,7 +751,7 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询遥测
|
/// 查询遥测
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<YCResultData>?> GetTelemetryInfoData(string id)
|
public async Task<List<ZzDataResultModel>?> GetTelemetryInfoData(string id)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -761,7 +761,7 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
var result = ExtractDataFromAbpResponse<List<YCResultData>>(response);
|
var result = ExtractDataFromAbpResponse<List<ZzDataResultModel>>(response);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -777,7 +777,7 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询遥信
|
/// 查询遥信
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<YXResultData>?> GetTeleSignalData(string id)
|
public async Task<List<ZzDataResultModel>?> GetTeleSignalData(string id)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -787,7 +787,7 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
var result = ExtractDataFromAbpResponse<List<YXResultData>>(response);
|
var result = ExtractDataFromAbpResponse<List<ZzDataResultModel>>(response);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -827,7 +827,7 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询定值
|
/// 查询预置位的巡检结果
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<InspectionResultData>?> GetInspectionResultData(string id)
|
public async Task<List<InspectionResultData>?> GetInspectionResultData(string id)
|
||||||
{
|
{
|
||||||
@ -852,6 +852,58 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询网关数据
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<ZzDataResultModel>?> GetGatewayInfoData(string id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var response = await Task.Run(() => ToolLibrary.HttpHelper.HttpGetRequest<JObject>($"{_apiEndpoints.GetGatewayInfoDataUri}?id={id}"));
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var result = ExtractDataFromAbpResponse<List<ZzDataResultModel>>(response);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Call GetTeleSignalData Api");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询虚点信息
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<ZzDataResultModel>?> GetVirtualPointInfoData(string id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var response = await Task.Run(() => ToolLibrary.HttpHelper.HttpGetRequest<JObject>($"{_apiEndpoints.GetVirtualPointInfoDataUri}?id={id}"));
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var result = ExtractDataFromAbpResponse<List<ZzDataResultModel>>(response);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Call GetTeleSignalData Api");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取网线配置
|
/// 获取网线配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -911,8 +963,7 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
{
|
{
|
||||||
try
|
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 object()));
|
|
||||||
|
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
@ -933,12 +984,53 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保存巡检计划执行结果
|
/// 保存巡检计划执行结果
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<bool> SaveSecondaryCircuitInspectionPlanResultAsync()
|
public async Task<string> SaveSecondaryCircuitInspectionPlanResultAsync(SecondaryCircuitInspectionResultSaveModel data)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.SaveSecondaryCircuitInspectionPlanResultUri, new object()));
|
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.SaveSecondaryCircuitInspectionPlanResultUri, data));
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
var result = ExtractDataFromAbpResponse<string>(response);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Call GetOpticalCableConfigAsync Api");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
//调用Ai
|
||||||
|
public async Task<string> GetAIAnalysis(object data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
ThingWebClientHelper httpClient = new ThingWebClientHelper();
|
||||||
|
var result = await httpClient.PostJsonAsync(_apiEndpoints.GetAIAnalysisUri, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Call GetOpticalCableConfigAsync Api");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//巡检计划执行结果AI诊断更新
|
||||||
|
public async Task<bool> UpdateReportWithAIAnalysisAsync(SecondaryCircuitInspectionAiSaveModel data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await Task.Run(() => HttpHelper.HttpPostRequestAsync<JObject>(_apiEndpoints.UpdateReportWithAIAnalysisUri, data));
|
||||||
|
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
@ -952,5 +1044,53 @@ namespace YunDa.Server.ISMSTcp.Domain
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取虚点信息
|
||||||
|
public async Task<List<ZzDataHistoryModel>> GetVariantBaseinfoAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var response = await Task.Run(() => ToolLibrary.HttpHelper.HttpGetRequest<JObject>(_apiEndpoints.GetVariantBaseinfoUri));
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var result = ExtractDataFromAbpResponse<List<ZzDataHistoryModel>>(response);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Call GetOpticalCableConfigAsync Api");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取网关信息
|
||||||
|
public async Task<List<ZzDataHistoryModel>> GetGateWayBaseInfoAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await Task.Run(() => ToolLibrary.HttpHelper.HttpGetRequest<JObject>(_apiEndpoints.GetGateWayBaseInfoUri));
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var result = ExtractDataFromAbpResponse<List<ZzDataHistoryModel>>(response);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error Call GetOpticalCableConfigAsync Api");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,6 +129,9 @@ namespace YunDa.Server.ISMSTcp.Extensions
|
|||||||
services.AddSingleton<SecondaryCircuitInspectionPlanService>();
|
services.AddSingleton<SecondaryCircuitInspectionPlanService>();
|
||||||
services.AddSingleton<ThingService>();
|
services.AddSingleton<ThingService>();
|
||||||
services.AddSingleton<ThingApiService>();
|
services.AddSingleton<ThingApiService>();
|
||||||
|
services.AddSingleton<GwErrorRatioService>();
|
||||||
|
services.AddSingleton<ZzTcpService>();
|
||||||
|
services.AddSingleton<ZzDataCacheContainerInit>();
|
||||||
|
|
||||||
// 日志过滤器配置
|
// 日志过滤器配置
|
||||||
services.Configure<YunDa.Server.ISMSTcp.Filters.Configuration.LogFilterConfiguration>(
|
services.Configure<YunDa.Server.ISMSTcp.Filters.Configuration.LogFilterConfiguration>(
|
||||||
|
|||||||
@ -20,50 +20,4 @@ namespace YunDa.Server.ISMSTcp.Models
|
|||||||
public string T { get; set; } = string.Empty;
|
public string T { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class CallYCByDataIdRequest
|
|
||||||
{
|
|
||||||
public List<string> Id { get; set; }
|
|
||||||
public int Times { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class YCResultValue
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Value")]
|
|
||||||
public decimal Value { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("TimeStamp")]
|
|
||||||
public string TimeStamp { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class YCResultData
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Id")]
|
|
||||||
public string Id { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
|
|
||||||
[JsonPropertyName("Name")]
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[JsonPropertyName("DispatcherAddress")]
|
|
||||||
public int? DispatcherAddress { get; set; } = null;
|
|
||||||
|
|
||||||
|
|
||||||
[JsonPropertyName("Data")]
|
|
||||||
public List<YCResultValue> Data { get; set; } = new List<YCResultValue>();
|
|
||||||
|
|
||||||
|
|
||||||
public void ParseValue(List<YCData> datas)
|
|
||||||
{
|
|
||||||
Data.Clear();
|
|
||||||
|
|
||||||
foreach (var data in datas)
|
|
||||||
{
|
|
||||||
Data.Add(new YCResultValue() { Value = data.V, TimeStamp = data.T });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,72 +20,72 @@ namespace YunDa.Server.ISMSTcp.Models
|
|||||||
public string T { get; set; } = string.Empty;
|
public string T { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class YXCacheData
|
//public class YXCacheData
|
||||||
{
|
//{
|
||||||
[JsonPropertyName("Id")]
|
// [JsonPropertyName("Id")]
|
||||||
public string Id { get; set; } = string.Empty;
|
// public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("Value")]
|
// [JsonPropertyName("Value")]
|
||||||
public int Value { get; set; }
|
// public int Value { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("ValueStr")]
|
// [JsonPropertyName("ValueStr")]
|
||||||
public string ValueStr { get; set; } = string.Empty;
|
// public string ValueStr { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("TimeStamp")]
|
// [JsonPropertyName("TimeStamp")]
|
||||||
public string TimeStamp { get; set; } = string.Empty;
|
// public string TimeStamp { get; set; } = string.Empty;
|
||||||
}
|
//}
|
||||||
|
|
||||||
public class YXResultData
|
//public class YXResultData
|
||||||
{
|
//{
|
||||||
[JsonPropertyName("Id")]
|
// [JsonPropertyName("Id")]
|
||||||
public string Id { get; set; } = string.Empty;
|
// public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
|
||||||
[JsonPropertyName("Name")]
|
// [JsonPropertyName("Name")]
|
||||||
public string Name { get; set; } = string.Empty;
|
// public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("DispatcherAddress")]
|
// [JsonPropertyName("DispatcherAddress")]
|
||||||
public int? DispatcherAddress { get; set; } = null;
|
// public int? DispatcherAddress { get; set; } = null;
|
||||||
|
|
||||||
|
|
||||||
[JsonPropertyName("Data")]
|
// [JsonPropertyName("Data")]
|
||||||
public List<YXCacheData> Data { get; set; } = new List<YXCacheData>();
|
// public List<YXCacheData> Data { get; set; } = new List<YXCacheData>();
|
||||||
|
|
||||||
[JsonPropertyName("Value")]
|
// [JsonPropertyName("Value")]
|
||||||
public int Value { get; set; }
|
// public int Value { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("ValueStr")]
|
// [JsonPropertyName("ValueStr")]
|
||||||
public string ValueStr { get; set; } = string.Empty;
|
// public string ValueStr { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("TimeStamp")]
|
// [JsonPropertyName("TimeStamp")]
|
||||||
public string TimeStamp { get; set; } = string.Empty;
|
// public string TimeStamp { get; set; } = string.Empty;
|
||||||
|
|
||||||
public void ParseValue(List<YXCacheData> datas)
|
// public void ParseValue(List<YXCacheData> datas)
|
||||||
{
|
// {
|
||||||
Data.Clear();
|
// Data.Clear();
|
||||||
|
|
||||||
foreach (var data in datas)
|
// foreach (var data in datas)
|
||||||
{
|
// {
|
||||||
Data.Add(data);
|
// Data.Add(data);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (Data.Count > 0)
|
// if (Data.Count > 0)
|
||||||
{
|
// {
|
||||||
Value = Data.Last().Value;
|
// Value = Data.Last().Value;
|
||||||
ValueStr = Data.Last().ValueStr;
|
// ValueStr = Data.Last().ValueStr;
|
||||||
TimeStamp = Data.Last().TimeStamp;
|
// TimeStamp = Data.Last().TimeStamp;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
public class YXResultValue
|
//public class YXResultValue
|
||||||
{
|
//{
|
||||||
[JsonPropertyName("Value")]
|
// [JsonPropertyName("Value")]
|
||||||
public decimal Value { get; set; }
|
// public decimal Value { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("TimeStamp")]
|
// [JsonPropertyName("TimeStamp")]
|
||||||
public string TimeStamp { get; set; } = string.Empty;
|
// public string TimeStamp { get; set; } = string.Empty;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,15 +18,22 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
private readonly ITcpClient _tcpClient;
|
private readonly ITcpClient _tcpClient;
|
||||||
private readonly ISMSServerConfiguration _config;
|
private readonly ISMSServerConfiguration _config;
|
||||||
private Timer? _connectionCheckTimer;
|
private Timer? _connectionCheckTimer;
|
||||||
|
private readonly ZzDataCacheContainerInit _zzDataCacheContainerInit;
|
||||||
|
|
||||||
public AutoConnectionService(
|
public AutoConnectionService(
|
||||||
ILogger<AutoConnectionService> logger,
|
ILogger<AutoConnectionService> logger,
|
||||||
ITcpClient tcpClient,
|
ITcpClient tcpClient,
|
||||||
IOptions<ISMSServerConfiguration> config)
|
IOptions<ISMSServerConfiguration> config,
|
||||||
|
ZzDataCacheContainerInit zzDataCacheContainerInit)
|
||||||
{
|
{
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_tcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
|
_tcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
|
||||||
_config = config?.Value ?? throw new ArgumentNullException(nameof(config));
|
_config = config?.Value ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
|
||||||
|
|
||||||
|
_zzDataCacheContainerInit = zzDataCacheContainerInit ?? throw new ArgumentNullException(nameof(zzDataCacheContainerInit));
|
||||||
|
|
||||||
|
zzDataCacheContainerInit.InitTcpService(_tcpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -57,6 +57,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
private readonly IRedisRepository<ProtectionDeviceCommInfoOutput, string> _protectionDeviceCommInfoRedis;
|
private readonly IRedisRepository<ProtectionDeviceCommInfoOutput, string> _protectionDeviceCommInfoRedis;
|
||||||
private readonly IApiEndpoints _apiEndpoints;
|
private readonly IApiEndpoints _apiEndpoints;
|
||||||
private readonly WebApiRequest _webApiRequest;
|
private readonly WebApiRequest _webApiRequest;
|
||||||
|
private readonly GwErrorRatioService _gwErrorRatioService;
|
||||||
|
|
||||||
//孪生体服务
|
//孪生体服务
|
||||||
private readonly ThingService _thingService;
|
private readonly ThingService _thingService;
|
||||||
@ -92,7 +93,8 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
IApiEndpoints apiEndpoints,
|
IApiEndpoints apiEndpoints,
|
||||||
WebApiRequest webApiRequest,
|
WebApiRequest webApiRequest,
|
||||||
SecondaryCircuitInspectionPlanService secondaryCircuitInspectionPlanService,
|
SecondaryCircuitInspectionPlanService secondaryCircuitInspectionPlanService,
|
||||||
ThingService thingService)
|
ThingService thingService,
|
||||||
|
GwErrorRatioService gwErrorRatioService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_messageParser = messageParser;
|
_messageParser = messageParser;
|
||||||
@ -111,6 +113,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
|
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
|
||||||
_secondaryCircuitInspectionPlanService = secondaryCircuitInspectionPlanService ?? throw new ArgumentNullException(nameof(secondaryCircuitInspectionPlanService));
|
_secondaryCircuitInspectionPlanService = secondaryCircuitInspectionPlanService ?? throw new ArgumentNullException(nameof(secondaryCircuitInspectionPlanService));
|
||||||
_thingService = thingService ?? throw new ArgumentNullException(nameof(thingService));
|
_thingService = thingService ?? throw new ArgumentNullException(nameof(thingService));
|
||||||
|
_gwErrorRatioService = gwErrorRatioService ?? throw new ArgumentNullException(nameof(gwErrorRatioService));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -158,6 +161,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
"WAVEDATA" => await ProcessWaveDataMessageAsync(contentToken),
|
"WAVEDATA" => await ProcessWaveDataMessageAsync(contentToken),
|
||||||
"故障报告" => await ProcessFaultRptMessageAsync(contentToken),
|
"故障报告" => await ProcessFaultRptMessageAsync(contentToken),
|
||||||
"ALLDATA" => await ProcessAllDataMessageAsync(contentToken),
|
"ALLDATA" => await ProcessAllDataMessageAsync(contentToken),
|
||||||
|
"GWERRORRATIO" => await ProcessGwErrorRatioMessageAsync(contentToken),
|
||||||
|
|
||||||
_ => ProcessUnknownMessageType(messageType, originalJson)
|
_ => ProcessUnknownMessageType(messageType, originalJson)
|
||||||
};
|
};
|
||||||
@ -254,19 +258,23 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
if (contentToken is JArray contentArray && contentArray.Count > 0)
|
if (contentToken is JArray contentArray && contentArray.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
//2025-10-25 所有值加1
|
//2025-10-25 所有值加1 send CallYXByDevice|B001208
|
||||||
foreach (var item in contentArray)
|
//foreach (var item in contentArray)
|
||||||
{
|
//{
|
||||||
if (item["V"] != null && item["V"].Type == JTokenType.String)
|
// if (item["YX_ID"].Value<string>() == "YXB001103058")
|
||||||
{
|
// {
|
||||||
string val = item["V"].Value<string>();
|
// int kk = 0;
|
||||||
if(int.TryParse(val, out int value))
|
// }
|
||||||
{
|
// if (item["V"] != null && item["V"].Type == JTokenType.String)
|
||||||
item["V"] = (value + 1).ToString();
|
// {
|
||||||
}
|
// string val = item["V"].Value<string>();
|
||||||
|
// if(int.TryParse(val, out int value))
|
||||||
|
// {
|
||||||
|
// //item["V"] = (value + 1).ToString();
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
List<TelesignalisationModel> telesignalisationModels = new List<TelesignalisationModel>();
|
List<TelesignalisationModel> telesignalisationModels = new List<TelesignalisationModel>();
|
||||||
|
|
||||||
@ -299,7 +307,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
//将状态推送到孪生体
|
//将状态推送到孪生体
|
||||||
_thingService.UpdateThingYXStatus(contentArray.ToObject<List<YXData>>(), telesignalisationModels);
|
_thingService.UpdateThingYXStatus(telesignalisationModels);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -992,6 +1000,16 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理网关通信数据消息,数据每小时发一次
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="contentToken">消息内容</param>
|
||||||
|
/// <returns>处理结果</returns>
|
||||||
|
private async Task<Models.ProcessResult> ProcessGwErrorRatioMessageAsync(JToken contentToken)
|
||||||
|
{
|
||||||
|
return await _gwErrorRatioService.TranselateData(contentToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -0,0 +1,134 @@
|
|||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Org.BouncyCastle.Utilities;
|
||||||
|
using Quartz;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using YunDa.Server.ISMSTcp.Models;
|
||||||
|
using YunDa.SOMS.DataTransferObject.MaintenanceAndOperations.SecondaryEquipment;
|
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||||
|
|
||||||
|
namespace YunDa.Server.ISMSTcp.Services
|
||||||
|
{
|
||||||
|
public class GwErrorRatioService
|
||||||
|
{
|
||||||
|
private readonly ILogger<AlarmService> _logger;
|
||||||
|
private readonly ZzDataCacheContainerInit _zzDataCacheContainerInit;
|
||||||
|
|
||||||
|
//数据缓冲队列
|
||||||
|
|
||||||
|
private ZzDataCacheContainer _cacheContainer = new ZzDataCacheContainer(2 * 60 + 1);//保留2小时的数据,多1分钟,兼容发送送数据发送延迟情况(每1小时发送一次)
|
||||||
|
|
||||||
|
public GwErrorRatioService(ILogger<AlarmService> logger, ZzDataCacheContainerInit zzDataCacheContainerInit)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_zzDataCacheContainerInit = zzDataCacheContainerInit;
|
||||||
|
_zzDataCacheContainerInit.InitGateWayBaseInfo(_cacheContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//解析数据
|
||||||
|
public async Task<Models.ProcessResult> TranselateData(JToken contentToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 输入验证
|
||||||
|
if (contentToken == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("GwErrorRatio message content is null");
|
||||||
|
return Models.ProcessResult.Error("GwErrorRatio message content is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量处理网关通信数据
|
||||||
|
List<GwErrorRatioDataModel> gatewayDatas;
|
||||||
|
|
||||||
|
gatewayDatas = contentToken.ToObject<List<GwErrorRatioDataModel>>();
|
||||||
|
|
||||||
|
if (gatewayDatas == null || gatewayDatas.Count == 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("GwErrorRatio message contains no valid alert data");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProcessData(gatewayDatas);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Models.ProcessResult.Success(contentToken, "GWERRORRATIO");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error processing GwErrorRatio message");
|
||||||
|
return Models.ProcessResult.Error($"GwErrorRatio processing error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理数据
|
||||||
|
private void ProcessData(List<GwErrorRatioDataModel> gatewayDatas)
|
||||||
|
{
|
||||||
|
foreach (var data in gatewayDatas)
|
||||||
|
{
|
||||||
|
data.DataTimeStamp = DateTime.Now;
|
||||||
|
SaveDataToCache(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//缓存遥信数据到内存
|
||||||
|
private void SaveDataToCache(GwErrorRatioDataModel data)
|
||||||
|
{
|
||||||
|
_cacheContainer.Write(data.GatewayID, data.ErrorRatio, data.DataTimeStamp, "", data.ErrorRatio.ToString("P2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询数据
|
||||||
|
public async Task<List<ZzDataResultModel>> GetDataByIds(List<string> ids, int seconds, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (seconds < 60 * 60)
|
||||||
|
{
|
||||||
|
seconds = 60 * 60 + 60; //多60秒,兼容发送送数据发送延迟情况(每1小时发送一次)
|
||||||
|
}
|
||||||
|
else if (seconds < 2 * 60 * 60)
|
||||||
|
{
|
||||||
|
seconds = 2 * 60 * 60 + 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _cacheContainer.Read(ids, seconds, 0, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
_logger.LogError(ex, "GwErrorRatioService: GetDataByIds 出错");
|
||||||
|
return new List<ZzDataResultModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GwErrorRatioDataModel
|
||||||
|
{
|
||||||
|
public string GatewayID { get; set; } = string.Empty; //网关Id
|
||||||
|
|
||||||
|
public double ErrorRatio { get; set; } //每小时的错误率
|
||||||
|
|
||||||
|
public DateTime DataTimeStamp { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ErrorRatioDataByIdRequestModel
|
||||||
|
{
|
||||||
|
public List<string> Id { get; set; }
|
||||||
|
public int Times { get; set; }
|
||||||
|
public int TimeWindowType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,18 +1,26 @@
|
|||||||
using Jint;
|
using DotNetty.Common.Utilities;
|
||||||
|
using Jint;
|
||||||
|
using Jint.Native.Object;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using Nito.AsyncEx;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Dynamic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -22,6 +30,7 @@ using YunDa.Server.ISMSTcp.Models;
|
|||||||
using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection;
|
using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection;
|
||||||
using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection.Configurations;
|
using YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspection.Configurations;
|
||||||
using YunDa.SOMS.DataTransferObject.MaintenanceAndOperations.SecondaryEquipment;
|
using YunDa.SOMS.DataTransferObject.MaintenanceAndOperations.SecondaryEquipment;
|
||||||
|
using YunDa.SOMS.Entities.DataMonitoring.SecondaryCircuitInspection;
|
||||||
|
|
||||||
namespace YunDa.Server.ISMSTcp.Services
|
namespace YunDa.Server.ISMSTcp.Services
|
||||||
{
|
{
|
||||||
@ -46,6 +55,19 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 class DeviceDzData
|
||||||
{
|
{
|
||||||
public List<DzInfo> UserDz { get; set; }
|
public List<DzInfo> UserDz { get; set; }
|
||||||
@ -66,26 +88,89 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
public string Value { get; set; } = string.Empty;
|
public string Value { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
//巡检结果
|
|
||||||
public class SecondaryCircuitInspectionResultModel
|
|
||||||
|
public class SecondaryCircuitInspectionStoreDataModel
|
||||||
{
|
{
|
||||||
//遥测数据
|
//遥测数据
|
||||||
public List<YCResultData> TelemetryInfoData { get; set; }
|
public List<ZzDataResultModel> TelemetryData { get; set; }
|
||||||
|
|
||||||
//遥信数据
|
//遥信数据
|
||||||
public List<YXResultData> TeleSignalData { get; set; }
|
public List<ZzDataResultModel> TeleSignalData { get; set; }
|
||||||
|
|
||||||
//定值
|
//定值
|
||||||
public List<DeviceDzData> DeviceDzData { get; set; }
|
public List<DeviceDzData> SettingData { get; set; }
|
||||||
|
|
||||||
//预置位的巡检结果
|
//预置位
|
||||||
public List<InspectionResultData> InspectionResultData { 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 DateTime ExecutionTime { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
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 PlanName { get; set; } = string.Empty;
|
||||||
|
public SecondaryCircuitInspectionResultSaveModel SaveModel { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
public class SecondaryCircuitInspectionPlanService
|
||||||
{
|
{
|
||||||
|
private readonly bool _isDebug = false;
|
||||||
|
|
||||||
private readonly ILogger<SecondaryCircuitInspectionPlanService> _logger;
|
private readonly ILogger<SecondaryCircuitInspectionPlanService> _logger;
|
||||||
private readonly IApiEndpoints _apiEndpoints;
|
private readonly IApiEndpoints _apiEndpoints;
|
||||||
private readonly WebApiRequest _webApiRequest;
|
private readonly WebApiRequest _webApiRequest;
|
||||||
@ -95,13 +180,15 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
|
|
||||||
private Queue<SecondaryCircuitInspectionPlanStateModel> _planList;
|
private Queue<SecondaryCircuitInspectionPlanStateModel> _planList;
|
||||||
private readonly object _planLock = new object();
|
private readonly AsyncLock _planLock = new AsyncLock();
|
||||||
private int _planCheckDay = 0;
|
private int _planCheckDay = 0;
|
||||||
private readonly Channel<SecondaryCircuitInspectionItemOutput> _singlePlanChannel;
|
|
||||||
|
private readonly Channel<SecondaryCircuitInspectionItemOutputEx> _singlePlanChannel; //巡检计划
|
||||||
|
private readonly Channel<SecondaryCircuitInspectionAiParamModel> _aiChannel; //Ai调用
|
||||||
|
|
||||||
//巡检事件
|
//巡检事件
|
||||||
private Queue<SecondaryCircuitEventDrivenConfigOutput> _eventPlanList;
|
private Queue<SecondaryCircuitEventDrivenConfigOutput> _eventPlanList;
|
||||||
private readonly object _eventPlanLock = new object();
|
private readonly AsyncLock _eventPlanLock = new AsyncLock();
|
||||||
private bool _getEventPlanListOk = false;
|
private bool _getEventPlanListOk = false;
|
||||||
|
|
||||||
public SecondaryCircuitInspectionPlanService(
|
public SecondaryCircuitInspectionPlanService(
|
||||||
@ -119,17 +206,21 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
_planList = new Queue<SecondaryCircuitInspectionPlanStateModel>();
|
_planList = new Queue<SecondaryCircuitInspectionPlanStateModel>();
|
||||||
|
|
||||||
_singlePlanChannel = Channel.CreateUnbounded<SecondaryCircuitInspectionItemOutput>();
|
_singlePlanChannel = Channel.CreateUnbounded<SecondaryCircuitInspectionItemOutputEx>();
|
||||||
|
|
||||||
|
_aiChannel = Channel.CreateUnbounded<SecondaryCircuitInspectionAiParamModel>();
|
||||||
|
|
||||||
_eventPlanList = new Queue<SecondaryCircuitEventDrivenConfigOutput>();
|
_eventPlanList = new Queue<SecondaryCircuitEventDrivenConfigOutput>();
|
||||||
|
|
||||||
|
//var kk = webApiRequest.GetTeleSignalData("08de219e-3524-41db-8d7d-86b547bac16a");
|
||||||
|
|
||||||
|
|
||||||
StartAsync();
|
StartAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartAsync()
|
private async Task StartAsync()
|
||||||
{
|
{
|
||||||
|
|
||||||
//更新计划
|
//更新计划
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@ -184,7 +275,8 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
});
|
});
|
||||||
|
|
||||||
//执行计划(两个线程同时执行)
|
//执行计划(两个线程同时执行)
|
||||||
int threadNumber = 3;
|
int threadNumber = _isDebug ? 1 : 3;
|
||||||
|
|
||||||
for (int i = 0; i < threadNumber; ++i)
|
for (int i = 0; i < threadNumber; ++i)
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () => {
|
_ = Task.Run(async () => {
|
||||||
@ -203,6 +295,25 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//调用Ai获取诊断结果
|
||||||
|
for (int i = 0; i < threadNumber; ++i)
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () => {
|
||||||
|
|
||||||
|
var rand = new Random(Guid.NewGuid().GetHashCode());
|
||||||
|
|
||||||
|
await foreach (var item in _aiChannel.Reader.ReadAllAsync())
|
||||||
|
{
|
||||||
|
// 让每个线程在执行之间有不同的节奏
|
||||||
|
await Task.Delay(rand.Next(0, 300));
|
||||||
|
|
||||||
|
await CallAiAndSave(item);
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,40 +322,60 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var list = await _webApiRequest.GetSecondaryCircuitInspectionPlanListAsync();
|
using (await _planLock.LockAsync())
|
||||||
if (list != null)
|
|
||||||
{
|
{
|
||||||
lock (_planLock)
|
var oldPlan = _planList.ToArray();
|
||||||
|
|
||||||
|
_planList.Clear();
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
while (start < 1000)
|
||||||
{
|
{
|
||||||
var oldPlan = _planList.ToArray();
|
var list = await _webApiRequest.GetSecondaryCircuitInspectionPlanListAsync(start);
|
||||||
|
if (list != null)
|
||||||
_planList.Clear();
|
|
||||||
|
|
||||||
List<SecondaryCircuitInspectionPlanStateModel> planlist = new List<SecondaryCircuitInspectionPlanStateModel>();
|
|
||||||
|
|
||||||
foreach (var item in list)
|
|
||||||
{
|
{
|
||||||
planlist.Add(new SecondaryCircuitInspectionPlanStateModel() { Plan = item });
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in planlist)
|
List<SecondaryCircuitInspectionPlanStateModel> planlist = new List<SecondaryCircuitInspectionPlanStateModel>();
|
||||||
{
|
|
||||||
var plan = oldPlan.FirstOrDefault(x => x.Plan.Id == item.Plan.Id && x.Plan.ScheduledTimeString == item.Plan.ScheduledTimeString);
|
foreach (var item in list)
|
||||||
if (plan != null)
|
|
||||||
{
|
{
|
||||||
item.ExecuteTime = plan.ExecuteTime;
|
planlist.Add(new SecondaryCircuitInspectionPlanStateModel() { Plan = item });
|
||||||
}
|
}
|
||||||
|
|
||||||
_planList.Enqueue(item);
|
foreach (var item in planlist)
|
||||||
}
|
{
|
||||||
|
var plan = oldPlan.FirstOrDefault(x => x.Plan.Id == item.Plan.Id && x.Plan.ScheduledTimeString == item.Plan.ScheduledTimeString);
|
||||||
|
if (plan != null)
|
||||||
|
{
|
||||||
|
item.ExecuteTime = plan.ExecuteTime;
|
||||||
|
}
|
||||||
|
|
||||||
//获取到绑定信息,可以更新全部状态了
|
_planList.Enqueue(item);
|
||||||
if (planlist.Count > 0)
|
}
|
||||||
_updatedPlanOk = true;
|
|
||||||
|
if (list.Count < 10)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取到绑定信息,可以更新全部状态了
|
||||||
|
if (_planList.Count > 0)
|
||||||
|
_updatedPlanOk = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -259,21 +390,38 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var list = await _webApiRequest.GetCircuitEventDrivenConfigAsync();
|
|
||||||
if (list != null)
|
using (await _eventPlanLock.LockAsync())
|
||||||
{
|
{
|
||||||
lock (_eventPlanLock)
|
_eventPlanList.Clear();
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
while (start < 1000)
|
||||||
{
|
{
|
||||||
_eventPlanList.Clear();
|
var list = await _webApiRequest.GetCircuitEventDrivenConfigAsync(start);
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
foreach (var item in list)
|
||||||
|
_eventPlanList.Enqueue(item);
|
||||||
|
|
||||||
foreach (var item in list)
|
if (list.Count < 10)
|
||||||
_eventPlanList.Enqueue(item);
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
//获取到绑定信息,可以更新全部状态了
|
else
|
||||||
if (_eventPlanList.Count > 0)
|
{
|
||||||
_getEventPlanListOk = true;
|
start += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取到绑定信息,可以更新全部状态了
|
||||||
|
if (_eventPlanList.Count > 0)
|
||||||
|
_getEventPlanListOk = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -295,7 +443,8 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
SecondaryCircuitInspectionPlanStateModel[] planList;
|
SecondaryCircuitInspectionPlanStateModel[] planList;
|
||||||
lock (_planLock)
|
|
||||||
|
using (await _planLock.LockAsync())
|
||||||
{
|
{
|
||||||
planList = _planList.ToArray();
|
planList = _planList.ToArray();
|
||||||
}
|
}
|
||||||
@ -305,7 +454,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
if (now.Day != _planCheckDay)
|
if (now.Day != _planCheckDay)
|
||||||
{//翻天,清空已执行标记
|
{//翻天,清空已执行标记
|
||||||
lock (_planLock)
|
using (await _planLock.LockAsync())
|
||||||
{
|
{
|
||||||
foreach (var item in _planList)
|
foreach (var item in _planList)
|
||||||
item.ExecuteTime = DateTime.MinValue;
|
item.ExecuteTime = DateTime.MinValue;
|
||||||
@ -322,9 +471,9 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
if (item.Plan.ScheduledWeekDaysList.IndexOf(week) != -1)
|
if (item.Plan.ScheduledWeekDaysList.IndexOf(week) != -1)
|
||||||
{
|
{
|
||||||
await PushPlanToChannel(item.Plan);
|
await PushPlanToChannel(1, item.Plan);
|
||||||
|
|
||||||
lock (_planLock)
|
using (await _planLock.LockAsync())
|
||||||
{
|
{
|
||||||
item.ExecuteTime = DateTime.Now;
|
item.ExecuteTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
@ -345,7 +494,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
//将需要执行的计划写到队列里
|
//将需要执行的计划写到队列里
|
||||||
private async Task<bool> PushPlanToChannel(SecondaryCircuitInspectionPlanOutput plan)
|
private async Task<bool> PushPlanToChannel(int triggerType, SecondaryCircuitInspectionPlanOutput plan)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -358,7 +507,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
if(item.IsActive && !string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(id))
|
if(item.IsActive && !string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(id))
|
||||||
{
|
{
|
||||||
//写到队列里,由队列控制执行频率
|
//写到队列里,由队列控制执行频率
|
||||||
await _singlePlanChannel.Writer.WriteAsync(item);
|
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(triggerType, item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,43 +522,345 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
//执行计划
|
//执行计划
|
||||||
private async Task ExecutePlan(SecondaryCircuitInspectionItemOutput item)
|
private async Task ExecutePlan(SecondaryCircuitInspectionItemOutputEx itemEx)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
string func2 = "";
|
||||||
|
string data2 = "";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var item = itemEx.Item;
|
||||||
|
|
||||||
string id = item.Id.ToString();
|
string id = item.Id.ToString();
|
||||||
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(id))
|
if (!string.IsNullOrWhiteSpace(id))
|
||||||
{
|
{
|
||||||
|
|
||||||
|
SecondaryCircuitInspectionJsDataModel jsData = new SecondaryCircuitInspectionJsDataModel();
|
||||||
|
|
||||||
|
jsData.TimeWindowSeconds = item.TimeWindowSeconds;
|
||||||
|
jsData.TimeWindowCount = item.TimeWindowCount;
|
||||||
|
jsData.TimeWindowType = item.TimeWindowType;
|
||||||
|
|
||||||
|
jsData.TelemetryConfigs = item.TelemetryConfigs;
|
||||||
|
jsData.TelesignalConfigs = item.TelesignalConfigs;
|
||||||
|
jsData.CameraPresets = item.CameraPresets;
|
||||||
|
jsData.DeviceConfigs = item.DeviceConfigs;
|
||||||
|
jsData.VariantConfigs = item.VariantConfigs;
|
||||||
|
jsData.GatewayConfigs = item.GatewayConfigs;
|
||||||
|
|
||||||
|
|
||||||
var engine = new Jint.Engine();
|
var engine = new Jint.Engine();
|
||||||
|
|
||||||
SecondaryCircuitInspectionResultModel resultModel = new SecondaryCircuitInspectionResultModel();
|
|
||||||
|
|
||||||
var task1 = GetSourceDataAsync<List<YCResultData>>(id, 1);
|
|
||||||
var task2 = GetSourceDataAsync<List<YXResultData>>(id, 2);
|
|
||||||
var task3 = GetSourceDataAsync<List<DeviceDzData>>(id, 3);
|
|
||||||
var task4 = GetSourceDataAsync<List<InspectionResultData>>(id, 4);
|
|
||||||
|
|
||||||
// 等待全部完成
|
|
||||||
await Task.WhenAll(task1, task2, task3, task4);
|
|
||||||
|
|
||||||
resultModel.TelemetryInfoData = await task1;
|
|
||||||
resultModel.TeleSignalData = await task2;
|
|
||||||
resultModel.DeviceDzData = await task3;
|
|
||||||
resultModel.InspectionResultData = await task4;
|
|
||||||
|
|
||||||
engine.Execute(item.CalculationExpression);
|
engine.Execute(item.CalculationExpression);
|
||||||
|
|
||||||
var result = engine.Invoke("calculate", resultModel).AsObject();
|
func2 = item.CalculationExpression;
|
||||||
|
|
||||||
|
ObjectInstance? saveObj = null;
|
||||||
|
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
for (int i = 0; i < item.TimeWindowCount; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
|
||||||
|
Task<List<ZzDataResultModel>>? t1 = null;
|
||||||
|
Task<List<ZzDataResultModel>>? t2 = null;
|
||||||
|
Task<List<DeviceDzData>>? t3 = null;
|
||||||
|
Task<List<InspectionResultData>>? t4 = null;
|
||||||
|
Task<List<ZzDataResultModel>>? t5 = null;
|
||||||
|
Task<List<ZzDataResultModel>>? t6 = null;
|
||||||
|
|
||||||
|
if (item.TelemetryConfigCount > 0)
|
||||||
|
{
|
||||||
|
t1 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 1);
|
||||||
|
tasks.Add(t1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.TelesignalConfigCount > 0)
|
||||||
|
{
|
||||||
|
t2 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 2);
|
||||||
|
tasks.Add(t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.DeviceConfigCount > 0)
|
||||||
|
{
|
||||||
|
t3 = GetSourceDataAsync<List<DeviceDzData>>(id, 3);
|
||||||
|
tasks.Add(t3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.CameraPresetCount > 0)
|
||||||
|
{
|
||||||
|
t4 = GetSourceDataAsync<List<InspectionResultData>>(id, 4);
|
||||||
|
tasks.Add(t4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.GatewayConfigCount > 0)
|
||||||
|
{
|
||||||
|
t5 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 5);
|
||||||
|
tasks.Add(t5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.VariantConfigCount > 0)
|
||||||
|
{
|
||||||
|
t6 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 6);
|
||||||
|
tasks.Add(t6);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
if (t1 != null) jsData.StoreData.TelemetryData = await t1;
|
||||||
|
if (t2 != null) jsData.StoreData.TeleSignalData = await t2;
|
||||||
|
if (t3 != null) jsData.StoreData.SettingData = await t3;
|
||||||
|
if (t4 != null) jsData.StoreData.PresetData = await t4;
|
||||||
|
if (t5 != null) jsData.StoreData.GatewayData = await t5;
|
||||||
|
if (t6 != null) jsData.StoreData.VariantData = await t6;
|
||||||
|
|
||||||
|
if(jsData.StoreData.TelemetryData == null && jsData.StoreData.TeleSignalData == null && jsData.StoreData.SettingData == null &&
|
||||||
|
jsData.StoreData.PresetData == null && jsData.StoreData.GatewayData == null && jsData.StoreData.VariantData == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(jsData,
|
||||||
|
new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
|
});
|
||||||
|
|
||||||
|
var js = $"JSON.parse('{json.Replace("'", "\\'")}')";
|
||||||
|
|
||||||
|
data2 = json;
|
||||||
|
|
||||||
|
var jsObj = engine.Evaluate(js).ToObject();
|
||||||
|
|
||||||
|
var jsResult = engine.Invoke("calculate", jsObj).AsObject();
|
||||||
|
|
||||||
|
if (jsResult != null && jsResult.HasProperty("status"))
|
||||||
|
{
|
||||||
|
saveObj = jsResult;
|
||||||
|
|
||||||
|
var status = jsResult.Get("status").ToString();
|
||||||
|
if (status == "正常" || string.IsNullOrWhiteSpace(status))
|
||||||
|
{//只要正常,就不用再检测了
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saveObj = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
//保存结果
|
||||||
|
if (saveObj != null)
|
||||||
|
{
|
||||||
|
SecondaryCircuitInspectionResultSaveModel saveData = new SecondaryCircuitInspectionResultSaveModel();
|
||||||
|
|
||||||
|
if(saveObj.HasProperty("status") && saveObj.HasProperty("inspectionResult") && saveObj.HasProperty("calculationProcess") && saveObj.HasProperty("verificationResult"))
|
||||||
|
{
|
||||||
|
saveData.SecondaryCircuitInspectionItemId = item.Id.ToString();
|
||||||
|
saveData.ExecutionDurationMs = sw.ElapsedMilliseconds;
|
||||||
|
saveData.TriggerType = itemEx.TriggerType;
|
||||||
|
if (saveData.TriggerType == 1)
|
||||||
|
saveData.InspectionPlanId = id;
|
||||||
|
else
|
||||||
|
saveData.TriggerEventId = id;
|
||||||
|
|
||||||
|
saveData.Status = saveObj.Get("status").ToString();
|
||||||
|
saveData.InspectionResult = saveObj.Get("inspectionResult").ToString();
|
||||||
|
saveData.CalculationProcess = saveObj.Get("calculationProcess").ToString();
|
||||||
|
saveData.VerificationResult = saveObj.Get("verificationResult").ToString();
|
||||||
|
|
||||||
|
saveData.Data = jsData;
|
||||||
|
|
||||||
|
var inspectionResultId = await _webApiRequest.SaveSecondaryCircuitInspectionPlanResultAsync(saveData);
|
||||||
|
|
||||||
|
//获取Ai诊断结果并保存,不正常时才诊断
|
||||||
|
if (!string.IsNullOrWhiteSpace(inspectionResultId) && saveData.Status != "正常")
|
||||||
|
{
|
||||||
|
SecondaryCircuitInspectionAiParamModel aiParamModel = new SecondaryCircuitInspectionAiParamModel();
|
||||||
|
aiParamModel.InspectionResultId = inspectionResultId;
|
||||||
|
aiParamModel.PlanName = $"{itemEx.Item.Name} {itemEx.Item.Description}";
|
||||||
|
aiParamModel.SaveModel = saveData;
|
||||||
|
|
||||||
|
_aiChannel.Writer.WriteAsync(aiParamModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, $"SecondaryCircuitInspectionPlanService 执行巡检项失败:{item.Id.ToString()}");
|
_logger.LogError(ex, $"SecondaryCircuitInspectionPlanService 执行巡检项失败:{itemEx.Item.Id.ToString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CallAiAndSave(SecondaryCircuitInspectionAiParamModel aiParamModel)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var aiParam = new
|
||||||
|
{
|
||||||
|
user_message = GetAiParamString(aiParamModel),
|
||||||
|
history = new List<string[]>()
|
||||||
|
};
|
||||||
|
|
||||||
|
var aiResultRaw = await _webApiRequest.GetAIAnalysis(aiParam);
|
||||||
|
|
||||||
|
bool isSuccess = false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(aiResultRaw))
|
||||||
|
{
|
||||||
|
string aiResult = ParseAiResult(aiResultRaw);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(aiResult))
|
||||||
|
{
|
||||||
|
SecondaryCircuitInspectionAiSaveModel data = new SecondaryCircuitInspectionAiSaveModel();
|
||||||
|
data.InspectionResultId = aiParamModel.InspectionResultId;
|
||||||
|
data.AiAnalysisResult = "成功";
|
||||||
|
data.ResultHandleDescription = aiResult;
|
||||||
|
|
||||||
|
await _webApiRequest.UpdateReportWithAIAnalysisAsync(data);
|
||||||
|
|
||||||
|
isSuccess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isSuccess)
|
||||||
|
{
|
||||||
|
SecondaryCircuitInspectionAiSaveModel data = new SecondaryCircuitInspectionAiSaveModel();
|
||||||
|
data.InspectionResultId = aiParamModel.InspectionResultId;
|
||||||
|
data.AiAnalysisResult = "失败";
|
||||||
|
data.ResultHandleDescription = "";
|
||||||
|
|
||||||
|
await _webApiRequest.UpdateReportWithAIAnalysisAsync(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"SecondaryCircuitInspectionPlanService CallAiAndSave出错");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetAiParamString(SecondaryCircuitInspectionAiParamModel aiParamModel)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.AppendLine("你是一个电力系统专家,请根据以下变电站二次巡检结果数据生成故障排除建议:");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine("检修的建议要避免停止变电所的正常运行,要以宽松的方式,检修工作必须遵循“安全第一、预防为主”的核心原则,实施全过程精益化管理");
|
||||||
|
sb.AppendLine("检修前,需制定详尽的计划与方案,确保人员资质合格、物资工具齐备,并严格执行工作票制度,实现安全隔离。检修中,必须强化现场安全管控,设专人监护,并严格按标准工艺施工,做好关键数据记录与过程质检。检修后,要严格执行试验验收程序,确保设备性能达标,彻底清理现场,并完善检修报告与设备台账,形成闭环管理");
|
||||||
|
sb.AppendLine("整个流程旨在通过规范化、标准化的作业,确保人身、设备与电网安全,提升设备可靠性,保障电力稳定供应");
|
||||||
|
sb.AppendLine(",减少思考过程");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// 巡检结果
|
||||||
|
sb.AppendLine($"巡检内容为:");
|
||||||
|
sb.AppendLine(aiParamModel.PlanName);
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("巡检结果为:");
|
||||||
|
sb.AppendLine($"巡检状态: {aiParamModel.SaveModel.Status}");
|
||||||
|
sb.AppendLine($"计算过程: {aiParamModel.SaveModel.CalculationProcess}");
|
||||||
|
sb.AppendLine($"巡检结果: {aiParamModel.SaveModel.InspectionResult}");
|
||||||
|
sb.AppendLine($"校验结果: {aiParamModel.SaveModel.VerificationResult}");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("请按以下格式生成排除建议,直接建议内容,每条建议前加序号,如1、,每条建议间加换行符");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetAiParamString2(SecondaryCircuitInspectionAiParamModel aiParamModel)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.AppendLine("你是一个电力系统专家,请根据以下变电站二次巡检结果数据生成检修策略:");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.Append("检修的开始时间不得晚于当前时间 ")
|
||||||
|
.AppendLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
|
||||||
|
sb.AppendLine("检修的建议要避免停止变电所的正常运行,要以宽松的方式,检修工作必须遵循“安全第一、预防为主”的核心原则,实施全过程精益化管理");
|
||||||
|
sb.AppendLine("检修前,需制定详尽的计划与方案,确保人员资质合格、物资工具齐备,并严格执行工作票制度,实现安全隔离。检修中,必须强化现场安全管控,设专人监护,并严格按标准工艺施工,做好关键数据记录与过程质检。检修后,要严格执行试验验收程序,确保设备性能达标,彻底清理现场,并完善检修报告与设备台账,形成闭环管理");
|
||||||
|
sb.AppendLine("整个流程旨在通过规范化、标准化的作业,确保人身、设备与电网安全,提升设备可靠性,保障电力稳定供应");
|
||||||
|
sb.AppendLine(",减少思考过程");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// 巡检结果
|
||||||
|
sb.AppendLine("巡检结果为:");
|
||||||
|
sb.AppendLine($"巡检状态: {aiParamModel.SaveModel.Status}");
|
||||||
|
sb.AppendLine($"计算过程: {aiParamModel.SaveModel.CalculationProcess}");
|
||||||
|
sb.AppendLine($"巡检结果: {aiParamModel.SaveModel.InspectionResult}");
|
||||||
|
sb.AppendLine($"校验结果: {aiParamModel.SaveModel.VerificationResult}");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("请按以下JSON格式生成策略:");
|
||||||
|
|
||||||
|
sb.AppendLine(@"{
|
||||||
|
""level"": ""紧急程度"",
|
||||||
|
""focusAreas"": [""重点检修区域1"", ""重点检修区域2""],
|
||||||
|
""steps"": [
|
||||||
|
{""duration"": ""时间"", ""content"": ""步骤描述""}
|
||||||
|
],
|
||||||
|
""resources"": [""资源需求1"", ""资源需求2""],
|
||||||
|
""schedule"": {
|
||||||
|
""startDate"": ""开始时间"",
|
||||||
|
""optimalWindow"": ""最佳窗口"",
|
||||||
|
""endDate"": ""结束时间""
|
||||||
|
},
|
||||||
|
""risks"": [
|
||||||
|
{""title"": ""风险标题"", ""severity"": ""风险等级"", ""mitigation"": ""缓解措施""}
|
||||||
|
]
|
||||||
|
}");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ParseAiResult(string raw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(raw))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var result = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (string line in raw.Split('\n'))
|
||||||
|
{
|
||||||
|
var trimmed = line.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(trimmed))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(trimmed);
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
if (root.TryGetProperty("type", out var typeProp) &&
|
||||||
|
typeProp.GetString() == "content" &&
|
||||||
|
root.TryGetProperty("content", out var contentProp))
|
||||||
|
{
|
||||||
|
result.Append(contentProp.GetString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 跳过非 JSON 行
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取计划数据
|
//获取计划数据
|
||||||
@ -437,6 +888,14 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
result = await _webApiRequest.GetInspectionResultData(id);
|
result = await _webApiRequest.GetInspectionResultData(id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
result = await _webApiRequest.GetGatewayInfoData(id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
result = await _webApiRequest.GetVirtualPointInfoData(id);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException("Invalid type");
|
throw new ArgumentException("Invalid type");
|
||||||
}
|
}
|
||||||
@ -468,7 +927,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
SecondaryCircuitEventDrivenConfigOutput[] planList;
|
SecondaryCircuitEventDrivenConfigOutput[] planList;
|
||||||
lock (_planLock)
|
using (await _eventPlanLock.LockAsync())
|
||||||
{
|
{
|
||||||
planList = _eventPlanList.ToArray();
|
planList = _eventPlanList.ToArray();
|
||||||
}
|
}
|
||||||
@ -485,26 +944,54 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
var engine = new Jint.Engine();
|
var engine = new Jint.Engine();
|
||||||
|
|
||||||
SecondaryCircuitInspectionResultModel resultModel = new SecondaryCircuitInspectionResultModel();
|
SecondaryCircuitInspectionJsDataModel jsData = new SecondaryCircuitInspectionJsDataModel();
|
||||||
|
|
||||||
var task1 = GetSourceDataAsync<List<YCResultData>>(id, 1);
|
var tasks = new List<Task>();
|
||||||
var task2 = GetSourceDataAsync<List<YXResultData>>(id, 2);
|
|
||||||
|
|
||||||
// 等待全部完成
|
Task<List<ZzDataResultModel>>? t1 = null;
|
||||||
await Task.WhenAll(task1, task2);
|
Task<List<ZzDataResultModel>>? t2 = null;
|
||||||
|
|
||||||
resultModel.TelemetryInfoData = await task1;
|
if (item.TelemetryConfigs.Count > 0)
|
||||||
resultModel.TeleSignalData = await task2;
|
{
|
||||||
|
t1 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 1);
|
||||||
|
tasks.Add(t1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.TelesignalConfigs.Count > 0)
|
||||||
|
{
|
||||||
|
t2 = GetSourceDataAsync<List<ZzDataResultModel>>(id, 2);
|
||||||
|
tasks.Add(t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
if (t1 != null) jsData.StoreData.TelemetryData = await t1;
|
||||||
|
if (t2 != null) jsData.StoreData.TeleSignalData = await t2;
|
||||||
|
|
||||||
engine.Execute(item.TriggerExpression);
|
engine.Execute(item.TriggerExpression);
|
||||||
|
|
||||||
var canCheck = engine.Invoke("calculate", resultModel).AsBoolean();
|
bool canCheck = true;
|
||||||
|
|
||||||
|
if(!_isDebug)
|
||||||
|
{
|
||||||
|
canCheck = engine.Invoke("calculate", jsData).AsBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
if(canCheck)
|
if(canCheck)
|
||||||
{
|
{
|
||||||
await Task.Delay(item.MandatoryWaitSeconds * 1000);
|
await Task.Delay(item.MandatoryWaitSeconds * 1000);
|
||||||
|
|
||||||
CheckPlanFormIds(item.SecondaryCircuitInspectionEventItems);
|
//CheckPlanFormIds(item.SecondaryCircuitInspectionEventItems);
|
||||||
|
|
||||||
|
//执行巡检
|
||||||
|
foreach(var planItem in item.SecondaryCircuitInspectionEventItems)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(planItem.CalculationExpression) && !string.IsNullOrWhiteSpace(planItem.Id.ToString()))
|
||||||
|
{
|
||||||
|
//写到队列里,由队列控制执行频率
|
||||||
|
await _singlePlanChannel.Writer.WriteAsync(new SecondaryCircuitInspectionItemOutputEx(2, planItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -519,45 +1006,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//检测巡检计划中的计划
|
|
||||||
private async Task CheckPlanFormIds(List<SecondaryCircuitInspectionItemOutput> planIds)
|
|
||||||
{
|
|
||||||
if (_planList == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SecondaryCircuitInspectionPlanStateModel[] planList;
|
|
||||||
lock (_planLock)
|
|
||||||
{
|
|
||||||
planList = _planList.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var plan in planList)
|
|
||||||
{
|
|
||||||
if(plan.Plan.IsActive)
|
|
||||||
{
|
|
||||||
foreach (var item in plan.Plan.InspectionItems)
|
|
||||||
{
|
|
||||||
if(planIds.Find( e => e.Id == item.Id) != null && item.IsActive)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(item.CalculationExpression) && !string.IsNullOrWhiteSpace(item.Id.ToString()))
|
|
||||||
{
|
|
||||||
//写到队列里,由队列控制执行频率
|
|
||||||
await _singlePlanChannel.Writer.WriteAsync(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "SecondaryCircuitInspectionPlanService - CheckPlanFormIds :发生错误");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
////获取遥测数据
|
////获取遥测数据
|
||||||
//public async Task<List<YCResultData>> CallYCByDataIdAsync(CallYCByDataIdRequest request, CancellationToken cancellationToken = default)
|
//public async Task<List<YCResultData>> CallYCByDataIdAsync(CallYCByDataIdRequest request, CancellationToken cancellationToken = default)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using StackExchange.Redis;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -62,10 +63,11 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
// 🔧 新增:遥测数据召唤存储相关字段
|
// 🔧 新增:遥测数据召唤存储相关字段
|
||||||
// 存储召唤的遥测数据:DataId -> List<TimestampedYCData>
|
// 存储召唤的遥测数据:DataId -> List<TimestampedYCData>
|
||||||
private readonly ConcurrentDictionary<string, ConcurrentBag<TimestampedYCData>> _ycDataStorage = new ConcurrentDictionary<string, ConcurrentBag<TimestampedYCData>>();
|
//private readonly ConcurrentDictionary<string, ConcurrentBag<TimestampedYCData>> _ycDataStorage = new ConcurrentDictionary<string, ConcurrentBag<TimestampedYCData>>();
|
||||||
private readonly Timer _cleanupTimer;
|
//private readonly Timer _cleanupTimer;
|
||||||
private const int CLEANUP_INTERVAL_MS = 60000; // 1分钟清理一次
|
//private const int CLEANUP_INTERVAL_MS = 60000; // 1分钟清理一次
|
||||||
private const int DATA_EXPIRATION_MINUTES = 5; // 数据保留5分钟
|
//private const int DATA_EXPIRATION_MINUTES = 5; // 数据保留5分钟
|
||||||
|
private ZzDataCacheContainer _zzDataCacheContainer = new ZzDataCacheContainer(3); // 数据保留3分钟
|
||||||
|
|
||||||
// 初始化状态
|
// 初始化状态
|
||||||
public volatile bool _isInitialized = false;
|
public volatile bool _isInitialized = false;
|
||||||
@ -98,7 +100,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
_batchPushTimer = new Timer(ProcessBatchPush, null, BATCH_PUSH_INTERVAL_MS, BATCH_PUSH_INTERVAL_MS);
|
_batchPushTimer = new Timer(ProcessBatchPush, null, BATCH_PUSH_INTERVAL_MS, BATCH_PUSH_INTERVAL_MS);
|
||||||
|
|
||||||
// 🔧 新增:初始化数据清理定时器
|
// 🔧 新增:初始化数据清理定时器
|
||||||
_cleanupTimer = new Timer(CleanupExpiredData, null, CLEANUP_INTERVAL_MS, CLEANUP_INTERVAL_MS);
|
//_cleanupTimer = new Timer(CleanupExpiredData, null, CLEANUP_INTERVAL_MS, CLEANUP_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -178,6 +180,10 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
if (_ycIdToHashKeysMapping[model.ismsbaseYCId].Contains(haskey))
|
if (_ycIdToHashKeysMapping[model.ismsbaseYCId].Contains(haskey))
|
||||||
{
|
{
|
||||||
mappingCount++;
|
mappingCount++;
|
||||||
|
|
||||||
|
//初始化
|
||||||
|
_zzDataCacheContainer.Write(model.ismsbaseYCId, model.ResultValue, model.ResultTime, model.Name, $"{model.ResultValue} {model.Unit}", model.DispatcherAddress);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -370,9 +376,6 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 🔧 新增:存储遥测数据到内存,用于数据召唤功能
|
|
||||||
StoreYCDataForRetrieval(ycDataModel);
|
|
||||||
|
|
||||||
// 使用映射字典查找对应的haskey列表
|
// 使用映射字典查找对应的haskey列表
|
||||||
if (!_ycIdToHashKeysMapping.TryGetValue(ycDataModel.YC_ID, out List<string> haskeys))
|
if (!_ycIdToHashKeysMapping.TryGetValue(ycDataModel.YC_ID, out List<string> haskeys))
|
||||||
{
|
{
|
||||||
@ -417,6 +420,14 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
// 保存到Redis
|
// 保存到Redis
|
||||||
await _telemeteringModelListRedis.HashSetUpdateOneAsync(redisKey, haskey, telemeteringModel);
|
await _telemeteringModelListRedis.HashSetUpdateOneAsync(redisKey, haskey, telemeteringModel);
|
||||||
|
|
||||||
|
//if(ycDataModel.YC_ID == "YCB001101001")
|
||||||
|
//{
|
||||||
|
// Debug.WriteLine("YCB001101001 : ", ycDataModel.V.ToString(), telemeteringModel.ResultValue.ToString(), telemeteringModel.Coefficient.ToString());
|
||||||
|
//}
|
||||||
|
|
||||||
|
//保存到缓存
|
||||||
|
_zzDataCacheContainer.Write(ycDataModel.YC_ID, (float)ycDataModel.V, telemeteringModel.ResultTime, telemeteringModel.Name, $"{(float)ycDataModel.V} {telemeteringModel.Unit}", telemeteringModel.DispatcherAddress);
|
||||||
|
|
||||||
// 检查设备是否处于检修状态
|
// 检查设备是否处于检修状态
|
||||||
equipmentId = telemeteringModel.EquipmentInfoId.Value;
|
equipmentId = telemeteringModel.EquipmentInfoId.Value;
|
||||||
var isInMaintenance = await IsEquipmentInMaintenanceAsync(equipmentId);
|
var isInMaintenance = await IsEquipmentInMaintenanceAsync(equipmentId);
|
||||||
@ -641,241 +652,28 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 🔧 新增:存储遥测数据用于召唤检索
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ycData">遥测数据</param>
|
|
||||||
private void StoreYCDataForRetrieval(YCData ycData)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (ycData == null || string.IsNullOrEmpty(ycData.YC_ID))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var timestampedData = new TimestampedYCData
|
|
||||||
{
|
|
||||||
Data = ycData,
|
|
||||||
ReceivedTime = DateTime.Now
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取或创建该DataId的存储列表
|
|
||||||
var dataList = _ycDataStorage.GetOrAdd(ycData.YC_ID, _ => new ConcurrentBag<TimestampedYCData>());
|
|
||||||
dataList.Add(timestampedData);
|
|
||||||
|
|
||||||
_logger.LogDebug("存储遥测数据 - YC_ID: {YcId}, Value: {Value}, Time: {Time}",
|
|
||||||
ycData.YC_ID, ycData.V, ycData.T);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "存储遥测数据失败 - YC_ID: {YcId}", ycData?.YC_ID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 🔧 新增:根据DataId列表获取召唤的遥测数据
|
/// 🔧 新增:根据DataId列表获取召唤的遥测数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataIds">DataId列表</param>
|
/// <param name="dataIds">DataId列表</param>
|
||||||
/// <returns>遥测数据列表</returns>
|
/// <returns>遥测数据列表</returns>
|
||||||
public List<YCData> GetYCDataByDataIds(List<string> dataIds)
|
public async Task<List<ZzDataResultModel>> GetYCDataByDataIds(List<string> ids, int seconds, int timeWindowType, CancellationToken cancellationToken, DateTime now = default)
|
||||||
{
|
{
|
||||||
var result = new List<YCData>();
|
if (!_isInitialized)
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (dataIds == null || dataIds.Count == 0)
|
await InitAsync();
|
||||||
{
|
|
||||||
_logger.LogWarning("DataId列表为空,无法获取遥测数据");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var dataId in dataIds)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(dataId))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ycDataStorage.TryGetValue(dataId, out var dataList))
|
|
||||||
{
|
|
||||||
|
|
||||||
// 只返回未过期的数据(5分钟内)
|
|
||||||
var validData = dataList
|
|
||||||
.Where(d => (DateTime.Now - d.ReceivedTime).TotalMinutes <= DATA_EXPIRATION_MINUTES)
|
|
||||||
.Select(d => d.Data)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
result.AddRange(validData);
|
|
||||||
|
|
||||||
_logger.LogDebug("获取遥测数据 - DataId: {DataId}, Count: {Count}", dataId, validData.Count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogDebug("未找到遥测数据 - DataId: {DataId}", dataId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("获取遥测数据完成 - 请求DataId数: {RequestCount}, 返回数据数: {ResultCount}",
|
|
||||||
dataIds.Count, result.Count);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "获取遥测数据失败");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var result = await _zzDataCacheContainer.Read(ids, seconds, timeWindowType, cancellationToken, now);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 🔧 新增:根据DataId列表获取召唤的遥测数据
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataIds">DataId列表</param>
|
|
||||||
/// <returns>遥测数据列表</returns>
|
|
||||||
public async Task<List<YCResultData>> GetYCDataByDataIds(List<string> dataIds, DateTime cmdTime, int times)
|
|
||||||
{
|
|
||||||
var result = new List<YCResultData>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (dataIds == null || dataIds.Count == 0)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("DataId列表为空,无法获取遥测数据");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var dataId in dataIds)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(dataId))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ycDataStorage.TryGetValue(dataId, out var dataList))
|
|
||||||
{
|
|
||||||
|
|
||||||
var validData = dataList
|
|
||||||
.Where(d => d.ReceivedTime >= cmdTime)
|
|
||||||
.Select(d => d.Data)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (validData.Count >= times)
|
|
||||||
{
|
|
||||||
var item = new YCResultData();
|
|
||||||
|
|
||||||
item.Id = dataId;
|
|
||||||
item.ParseValue(validData[^times..]);
|
|
||||||
|
|
||||||
result.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogDebug("获取遥测数据 - DataId: {DataId}, Count: {Count}", dataId, validData.Count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogDebug("未找到遥测数据 - DataId: {DataId}", dataId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("获取遥测数据完成 - 请求DataId数: {RequestCount}, 返回数据数: {ResultCount}",
|
|
||||||
dataIds.Count, result.Count);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "获取遥测数据失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(result.Count == dataIds.Count)
|
|
||||||
{
|
|
||||||
string redisKey = $"{_telemeteringModelListRediskey}_Zongzi";
|
|
||||||
|
|
||||||
foreach (var item in result)
|
|
||||||
{
|
|
||||||
if (!_ycIdToHashKeysMapping.TryGetValue(item.Id, out List<string> haskeys))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(haskeys.Count >= 0)
|
|
||||||
{
|
|
||||||
string haskey = haskeys.First();
|
|
||||||
|
|
||||||
var telemeteringModel = await _telemeteringModelListRedis.HashSetGetOneAsync(redisKey, haskey);
|
|
||||||
if (telemeteringModel == null)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Redis中未找到遥测数据: RedisKey={RedisKey}, HasKey={HasKey}", redisKey, haskey);
|
|
||||||
continue; // 继续处理下一个haskey
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Name = telemeteringModel.Name;
|
|
||||||
item.DispatcherAddress = telemeteringModel.DispatcherAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 🔧 新增:清理过期的遥测数据(定时器回调)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="state">定时器状态</param>
|
|
||||||
private void CleanupExpiredData(object state)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var now = DateTime.Now;
|
|
||||||
var expiredKeys = new List<string>();
|
|
||||||
var totalCleaned = 0;
|
|
||||||
|
|
||||||
foreach (var kvp in _ycDataStorage)
|
|
||||||
{
|
|
||||||
var dataId = kvp.Key;
|
|
||||||
var dataList = kvp.Value;
|
|
||||||
|
|
||||||
// 过滤出未过期的数据
|
|
||||||
var validData = dataList
|
|
||||||
.Where(d => (now - d.ReceivedTime).TotalMinutes <= DATA_EXPIRATION_MINUTES)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var cleanedCount = dataList.Count - validData.Count;
|
|
||||||
totalCleaned += cleanedCount;
|
|
||||||
|
|
||||||
if (validData.Count == 0)
|
|
||||||
{
|
|
||||||
// 如果没有有效数据,标记该键为待删除
|
|
||||||
expiredKeys.Add(dataId);
|
|
||||||
}
|
|
||||||
else if (cleanedCount > 0)
|
|
||||||
{
|
|
||||||
// 如果有部分数据过期,重新创建数据列表
|
|
||||||
var newBag = new ConcurrentBag<TimestampedYCData>();
|
|
||||||
foreach (var data in validData)
|
|
||||||
{
|
|
||||||
newBag.Add(data);
|
|
||||||
}
|
|
||||||
_ycDataStorage.TryUpdate(dataId, newBag, dataList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除没有有效数据的键
|
|
||||||
foreach (var key in expiredKeys)
|
|
||||||
{
|
|
||||||
_ycDataStorage.TryRemove(key, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalCleaned > 0 || expiredKeys.Count > 0)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("清理过期遥测数据完成 - 清理数据数: {CleanedCount}, 删除键数: {RemovedKeys}",
|
|
||||||
totalCleaned, expiredKeys.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "清理过期遥测数据时发生错误");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 🔧 新增:释放资源
|
/// 🔧 新增:释放资源
|
||||||
@ -883,7 +681,6 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_batchPushTimer?.Dispose();
|
_batchPushTimer?.Dispose();
|
||||||
_cleanupTimer?.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,9 +53,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
private readonly object _expressionInitLock = new object();
|
private readonly object _expressionInitLock = new object();
|
||||||
|
|
||||||
//遥信数据缓冲队列
|
//遥信数据缓冲队列
|
||||||
private static readonly ConcurrentQueue<(DateTime time, YXCacheData yxData)> _yxDataBuffer = new ConcurrentQueue<(DateTime time, YXCacheData yxData)>();
|
private ZzDataCacheContainer _zzDataCacheContainer = new ZzDataCacheContainer(30); //只保留5分钟的数据
|
||||||
private const int YXDATA_BUFFER_SECONDS = 60; //只保留60秒的数据
|
|
||||||
private static int _yxDataRecvCount = 0; //每100次清除一下超时数据,用于计数
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -145,6 +143,9 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
if (_yxIdToHashKeyMapping.TryAdd(model.ismsbaseYXId, haskey))
|
if (_yxIdToHashKeyMapping.TryAdd(model.ismsbaseYXId, haskey))
|
||||||
{
|
{
|
||||||
mappingCount++;
|
mappingCount++;
|
||||||
|
|
||||||
|
//初始化
|
||||||
|
_zzDataCacheContainer.Write(model.ismsbaseYXId, model.ResultValue, model.ResultTime, model.Name, model.ResultValueStr, model.DispatcherAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -349,12 +350,25 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
// 解析YXData结构
|
// 解析YXData结构
|
||||||
var yxDataModel = yxItem.ToObject<YXData>();
|
var yxDataModel = yxItem.ToObject<YXData>();
|
||||||
|
|
||||||
if (yxDataModel == null)
|
if (yxDataModel == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("无法解析遥信数据项: {Item}", yxItem?.ToString());
|
_logger.LogWarning("无法解析遥信数据项: {Item}", yxItem?.ToString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//开关状态(合、分、不定),与具体的规约(101、104)无关 TSwitchstate = (swon = 0 swOff = 1,swUncertainty = 2);
|
||||||
|
switch (yxDataModel.V)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
yxDataModel.V = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
yxDataModel.V = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// 使用映射字典查找对应的haskey
|
// 使用映射字典查找对应的haskey
|
||||||
if (!_yxIdToHashKeyMapping.TryGetValue(yxDataModel.YX_ID, out string haskey))
|
if (!_yxIdToHashKeyMapping.TryGetValue(yxDataModel.YX_ID, out string haskey))
|
||||||
{
|
{
|
||||||
@ -401,16 +415,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
_telesignalisationModelListRedis.PublishAsync(channel, telesignalisationModel);
|
_telesignalisationModelListRedis.PublishAsync(channel, telesignalisationModel);
|
||||||
|
|
||||||
//缓存遥信数据到内存
|
//缓存遥信数据到内存
|
||||||
YXCacheData yXCacheData = new YXCacheData
|
_zzDataCacheContainer.Write(yxDataModel.YX_ID, yxDataModel.V, resultTime, telesignalisationModel.Name, telesignalisationModel.ResultValueStr, telesignalisationModel.DispatcherAddress);
|
||||||
{
|
|
||||||
Id = yxDataModel.YX_ID,
|
|
||||||
Value = yxDataModel.V,
|
|
||||||
ValueStr = telesignalisationModel.ResultValueStr,
|
|
||||||
TimeStamp = resultTime.ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
};
|
|
||||||
|
|
||||||
SaveYxDataToCache(resultTime, yXCacheData);
|
|
||||||
|
|
||||||
|
|
||||||
// 🔧 优化:如果跳过转发,仅更新Redis后直接返回
|
// 🔧 优化:如果跳过转发,仅更新Redis后直接返回
|
||||||
if (skipForwarding)
|
if (skipForwarding)
|
||||||
@ -458,115 +463,40 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task<List<ZzDataResultModel>> GetYXDataByDataIds(List<string> ids, int times, int timeWindowType, CancellationToken cancellationToken)
|
||||||
//缓存遥信数据到内存
|
|
||||||
private void SaveYxDataToCache(DateTime dateTime, YXCacheData yxData)
|
|
||||||
{
|
|
||||||
_yxDataBuffer.Enqueue((dateTime, yxData));
|
|
||||||
|
|
||||||
// 每100次接收清理一次过期数据
|
|
||||||
if (Interlocked.Increment(ref _yxDataRecvCount) % 100 == 0)
|
|
||||||
{
|
|
||||||
var now = DateTime.Now;
|
|
||||||
while (_yxDataBuffer.TryPeek(out var oldest) &&
|
|
||||||
(now - oldest.time).TotalSeconds > YXDATA_BUFFER_SECONDS)
|
|
||||||
{
|
|
||||||
_yxDataBuffer.TryDequeue(out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<YXResultData>> GetYXDataByDataIds(List<string> ids, int times, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
var startTime = DateTime.Now;
|
//检查数据是否为空,如果为空,从Redis里取最新值
|
||||||
var resultDict = new Dictionary<string, List<YXCacheData>>(); // key = YX_ID
|
|
||||||
|
|
||||||
// 初始化每个ID的结果列表
|
|
||||||
foreach (var id in ids)
|
|
||||||
resultDict[id] = new List<YXCacheData>();
|
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource();
|
|
||||||
|
|
||||||
// 异步监听任务
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
while ((DateTime.Now - startTime).TotalSeconds < times)
|
|
||||||
{
|
|
||||||
var matched = _yxDataBuffer
|
|
||||||
.Where(x => x.time >= startTime && ids.Contains(x.yxData.Id))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// 按 ID 分类
|
|
||||||
foreach (var id in ids)
|
|
||||||
{
|
|
||||||
var datas = matched.Where(x => x.yxData.Id == id)
|
|
||||||
.Select(x => x.yxData)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
lock (resultDict[id])
|
|
||||||
{
|
|
||||||
resultDict[id].Clear();
|
|
||||||
resultDict[id].AddRange(datas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(500, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
tcs.TrySetResult();
|
|
||||||
}, cancellationToken);
|
|
||||||
|
|
||||||
await Task.WhenAny(tcs.Task, Task.Delay(times * 1000, cancellationToken));
|
|
||||||
|
|
||||||
// 构建最终返回数据
|
|
||||||
var finalResult = new List<YXResultData>();
|
|
||||||
foreach (var id in ids)
|
|
||||||
{
|
|
||||||
var yxResult = new YXResultData
|
|
||||||
{
|
|
||||||
Id = id,
|
|
||||||
};
|
|
||||||
|
|
||||||
lock (resultDict[id])
|
|
||||||
{
|
|
||||||
yxResult.ParseValue(resultDict[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
finalResult.Add(yxResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
//设置Name和DispatcherAddress,并且检查数据是否为空,如果为空,从Redis里取最新值
|
|
||||||
|
|
||||||
if(!_isInitialized)
|
if(!_isInitialized)
|
||||||
{
|
{
|
||||||
await InitAsync();
|
await InitAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var finalResult = await _zzDataCacheContainer.Read(ids, times, timeWindowType, cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
string redisKey = $"{_telesignalisationModelListRediskey}_Zongzi";
|
string redisKey = $"{_telesignalisationModelListRediskey}_Zongzi";
|
||||||
foreach (var item in finalResult)
|
foreach (var item in finalResult)
|
||||||
{
|
{
|
||||||
if (!_yxIdToHashKeyMapping.TryGetValue(item.Id, out string haskey))
|
if(item.TimeStamp == DateTime.MinValue)
|
||||||
{
|
{
|
||||||
continue;
|
if (!_yxIdToHashKeyMapping.TryGetValue(item.Id, out string haskey))
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var telesignalisationModel = await _telesignalisationModelListRedis.HashSetGetOneAsync(redisKey, haskey);
|
var telesignalisationModel = await _telesignalisationModelListRedis.HashSetGetOneAsync(redisKey, haskey);
|
||||||
if (telesignalisationModel == null)
|
if (telesignalisationModel == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Redis中未找到遥信数据: RedisKey={RedisKey}, HasKey={HasKey}", redisKey, haskey);
|
_logger.LogWarning("Redis中未找到遥信数据: RedisKey={RedisKey}, HasKey={HasKey}", redisKey, haskey);
|
||||||
continue; // 继续处理下一个haskey
|
continue; // 继续处理下一个haskey
|
||||||
}
|
}
|
||||||
|
|
||||||
item.Name = telesignalisationModel.Name;
|
|
||||||
item.DispatcherAddress = telesignalisationModel.DispatcherAddress;
|
|
||||||
|
|
||||||
if (item.Data.Count == 0)
|
|
||||||
{
|
|
||||||
item.Value = telesignalisationModel.ResultValue;
|
item.Value = telesignalisationModel.ResultValue;
|
||||||
item.ValueStr = telesignalisationModel.ResultValueStr;
|
item.ValueStr = telesignalisationModel.ResultValueStr;
|
||||||
item.TimeStamp = telesignalisationModel.ResultTime.ToString("yyyy-MM-dd HH:mm:ss");
|
item.TimeStamp = telesignalisationModel.ResultTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalResult;
|
return finalResult;
|
||||||
|
|||||||
@ -287,11 +287,12 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
/******************配置信息*****************/
|
/******************配置信息*****************/
|
||||||
|
|
||||||
//更新孪生体与遥测数据绑定关系
|
//更新孪生体与遥信数据绑定关系
|
||||||
private async Task UpdateDeviceBindingConfig()
|
private async Task UpdateDeviceBindingConfig()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
//db ex_younuo_twin_data_binding
|
||||||
List<ThingDeviceBindingModel> list = await _webApiRequest.GetThingDeviceBindingConfigAsync();
|
List<ThingDeviceBindingModel> list = await _webApiRequest.GetThingDeviceBindingConfigAsync();
|
||||||
|
|
||||||
if (list != null)
|
if (list != null)
|
||||||
@ -432,7 +433,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
/******************设备状态(遥信)*****************/
|
/******************设备状态(遥信)*****************/
|
||||||
|
|
||||||
//遥信状态有变化时,更新设备状态
|
//遥信状态有变化时,更新设备状态
|
||||||
private async Task UpdateDeviceChangeStatus(List<YXData> yXDatas)
|
private async Task UpdateDeviceChangeStatus(List<TelesignalisationModel> yXDatas)
|
||||||
{
|
{
|
||||||
if (yXDatas == null)
|
if (yXDatas == null)
|
||||||
return;
|
return;
|
||||||
@ -447,14 +448,17 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
foreach (var item in yXDatas)
|
foreach (var item in yXDatas)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(item.YX_ID))
|
if (!string.IsNullOrWhiteSpace(item.ismsbaseYXId))
|
||||||
{
|
{
|
||||||
if (_deviceBindingConfigs.TryGetValue(item.YX_ID, out var bindingItem))
|
if (_deviceBindingConfigs.TryGetValue(item.ismsbaseYXId, out var bindingItem))
|
||||||
{
|
{
|
||||||
if(bindingItem.IsActive)
|
if(bindingItem.IsActive)
|
||||||
{
|
{
|
||||||
var metric = bindingItem.MetricList.Find(e => e.Val == item.T);
|
|
||||||
var val = bindingItem.ValList.Find(e => e.Val == item.T);
|
var yxValue = item.ResultValue.ToString();
|
||||||
|
|
||||||
|
var metric = bindingItem.MetricList.Find(e => e.Val == yxValue);
|
||||||
|
var val = bindingItem.ValList.Find(e => e.Val == yxValue);
|
||||||
|
|
||||||
if (metric != null && val != null)
|
if (metric != null && val != null)
|
||||||
{
|
{
|
||||||
@ -501,6 +505,8 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//实时更新遥信全体设备状态
|
//实时更新遥信全体设备状态
|
||||||
public async Task UpdateYXDeviceAllStatus()
|
public async Task UpdateYXDeviceAllStatus()
|
||||||
{
|
{
|
||||||
@ -526,10 +532,10 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
if(bindingItem.IsActive)
|
if(bindingItem.IsActive)
|
||||||
{
|
{
|
||||||
int yxValue = item.ResultValue;
|
var yxValue = item.ResultValue.ToString();
|
||||||
|
|
||||||
var metric = bindingItem.MetricList.Find(e => e.Val == yxValue.ToString());
|
var metric = bindingItem.MetricList.Find(e => e.Val == yxValue);
|
||||||
var val = bindingItem.ValList.Find(e => e.Val == yxValue.ToString());
|
var val = bindingItem.ValList.Find(e => e.Val == yxValue);
|
||||||
|
|
||||||
if (metric != null && val != null)
|
if (metric != null && val != null)
|
||||||
{
|
{
|
||||||
@ -539,7 +545,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int k = 0;
|
//int k = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -598,10 +604,10 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
//遥信数据有变化时,将状态和性能数据推送到孪生体
|
//遥信数据有变化时,将状态和性能数据推送到孪生体
|
||||||
public async Task UpdateThingYXStatus(List<YXData> yXDatas, List<TelesignalisationModel> telesignalisationModels)
|
public async Task UpdateThingYXStatus(List<TelesignalisationModel> telesignalisationModels)
|
||||||
{
|
{
|
||||||
//更新孪生体
|
//更新孪生体
|
||||||
await UpdateDeviceChangeStatus(yXDatas);
|
await UpdateDeviceChangeStatus(telesignalisationModels);
|
||||||
|
|
||||||
//推送性能数据
|
//推送性能数据
|
||||||
await UpdateYXSimDatasStatus(telesignalisationModels);
|
await UpdateYXSimDatasStatus(telesignalisationModels);
|
||||||
@ -929,20 +935,9 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据虚点值处理报警
|
// 根据虚点值处理报警
|
||||||
// Value = 0 表示正常状态,Value = 1 表示故障状态
|
//Boolean型变量的值(真,假,不定)2018-04-26从uPro设备
|
||||||
bool isAlarm = false;
|
//TVariantBoolValue = (vbTrue = 0,vbFalse = 1 vbUncertain = 2):
|
||||||
if (Math.Abs(value - 1) < 0.0001) // 故障状态
|
bool isAlarm = Math.Abs(value) < 0.0001;
|
||||||
{
|
|
||||||
isAlarm = true;
|
|
||||||
}
|
|
||||||
else if (Math.Abs(value) < 0.0001) // 正常状态
|
|
||||||
{
|
|
||||||
isAlarm = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
TingAlarmPushDataModel alertData = new TingAlarmPushDataModel();
|
TingAlarmPushDataModel alertData = new TingAlarmPushDataModel();
|
||||||
|
|
||||||
@ -952,7 +947,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
isAlarm,
|
isAlarm,
|
||||||
$"{configItem.P1DeviceName}{configItem.P2DeviceName}",
|
$"{configItem.P1DeviceName}{configItem.P2DeviceName}",
|
||||||
"光纤断线",
|
"光纤断线",
|
||||||
item.T
|
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
||||||
);
|
);
|
||||||
|
|
||||||
alertDatas.Add(alertData);
|
alertDatas.Add(alertData);
|
||||||
@ -1109,6 +1104,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
configs = _opticalCableConfig.ToArray();
|
configs = _opticalCableConfig.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//虚点
|
||||||
if (virtualTerminalDatas != null)
|
if (virtualTerminalDatas != null)
|
||||||
{
|
{
|
||||||
foreach (var item in virtualTerminalDatas)
|
foreach (var item in virtualTerminalDatas)
|
||||||
@ -1126,20 +1122,10 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据虚点值处理报警
|
// 根据虚点值处理报警
|
||||||
// Value = 0 表示正常状态,Value = 1 表示故障状态
|
// 根据虚点值处理报警
|
||||||
bool isAlarm = false;
|
//Boolean型变量的值(真,假,不定)2018-04-26从uPro设备
|
||||||
if (Math.Abs(value - 1) < 0.0001) // 故障状态
|
//TVariantBoolValue = (vbTrue = 0,vbFalse = 1 vbUncertain = 2):
|
||||||
{
|
bool isAlarm = Math.Abs(value) < 0.0001;
|
||||||
isAlarm = true;
|
|
||||||
}
|
|
||||||
else if (Math.Abs(value) < 0.0001) // 正常状态
|
|
||||||
{
|
|
||||||
isAlarm = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
TingAlarmPushDataModel alertData = new TingAlarmPushDataModel();
|
TingAlarmPushDataModel alertData = new TingAlarmPushDataModel();
|
||||||
|
|
||||||
@ -1149,7 +1135,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
isAlarm,
|
isAlarm,
|
||||||
$"{configItem.P1CabinetName}{configItem.P2CabinetName}",
|
$"{configItem.P1CabinetName}{configItem.P2CabinetName}",
|
||||||
"光缆断线",
|
"光缆断线",
|
||||||
item.T
|
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
||||||
);
|
);
|
||||||
|
|
||||||
alertDatas.Add(alertData);
|
alertDatas.Add(alertData);
|
||||||
@ -1160,7 +1146,6 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (telesignalisationModels != null)
|
if (telesignalisationModels != null)
|
||||||
{
|
{
|
||||||
foreach (var item in telesignalisationModels)
|
foreach (var item in telesignalisationModels)
|
||||||
@ -1226,6 +1211,8 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
|
|
||||||
private string _apiToken = string.Empty;
|
private string _apiToken = string.Empty;
|
||||||
private DateTime _apiTokenTime = DateTime.MinValue;
|
private DateTime _apiTokenTime = DateTime.MinValue;
|
||||||
|
private readonly SemaphoreSlim _apiTokenSemaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
|
||||||
public ThingApiService(
|
public ThingApiService(
|
||||||
ILogger<ThingApiService> logger)
|
ILogger<ThingApiService> logger)
|
||||||
@ -1242,30 +1229,57 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
//获取Token
|
//获取Token
|
||||||
private async Task<string> GetTokenAsync()
|
private async Task<string> GetTokenAsync()
|
||||||
{
|
{
|
||||||
|
await _apiTokenSemaphore.WaitAsync();
|
||||||
|
|
||||||
|
string src = string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_apiToken) || (DateTime.Now - _apiTokenTime).TotalHours > 6)
|
int retryCnt = 3;
|
||||||
{//每6小时更新一次token
|
while (retryCnt-- > 0)
|
||||||
var http = new ThingWebClientHelper();
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_apiToken) || (DateTime.Now - _apiTokenTime).TotalHours > 6)
|
||||||
|
{//每6小时更新一次token
|
||||||
|
var http = new ThingWebClientHelper();
|
||||||
|
|
||||||
var json = await http.PostJsonAsync($"{_apiServerUrl}:1662/thing/provider/rest/getToken", new { loginCode = _loginCode, loginKey = _loginKey });
|
var json = await http.PostJsonAsync($"{_apiServerUrl}:1662/thing/provider/rest/getToken", new { loginCode = _loginCode, loginKey = _loginKey });
|
||||||
|
|
||||||
JObject obj = JObject.Parse(json);
|
src = json ?? "";
|
||||||
|
|
||||||
_apiToken = obj["data"]?["token"]?.ToString();
|
JObject obj = JObject.Parse(json);
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(_apiToken))
|
_apiToken = obj["data"]?["token"]?.ToString();
|
||||||
_apiTokenTime = DateTime.Now;
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_apiToken))
|
||||||
|
{
|
||||||
|
_apiTokenTime = DateTime.Now;
|
||||||
|
System.Console.WriteLine("ThingApiService - 获取token成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_apiToken))
|
||||||
|
{
|
||||||
|
await Task.Delay(6000); //此接 口每5秒只可以调用1次
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_apiTokenSemaphore.Release();
|
||||||
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(_apiToken))
|
if (string.IsNullOrWhiteSpace(_apiToken))
|
||||||
_logger.LogError("ThingApiService - 获取token失败");
|
_logger.LogError("ThingApiService - 连续3次获取token失败,返回内容:" + src);
|
||||||
|
|
||||||
return _apiToken;
|
return _apiToken;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
using System;
|
using Microsoft.ClearScript.JavaScript;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using YunDa.Server.ISMSTcp.Models;
|
using Org.BouncyCastle.Utilities;
|
||||||
using YunDa.Server.ISMSTcp.Domain;
|
using System;
|
||||||
using YunDa.SOMS.DataTransferObject.ExternalEntities.BeijingYounuo;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
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
|
namespace YunDa.Server.ISMSTcp.Services
|
||||||
{
|
{
|
||||||
@ -19,6 +23,7 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
{
|
{
|
||||||
private readonly ILogger<VirtualTerminalHandler> _logger;
|
private readonly ILogger<VirtualTerminalHandler> _logger;
|
||||||
private readonly WebApiRequest _webApiRequest;
|
private readonly WebApiRequest _webApiRequest;
|
||||||
|
private readonly ZzDataCacheContainerInit _zzDataCacheContainerInit;
|
||||||
|
|
||||||
// 🔧 新增:光纤虚点处理相关字段
|
// 🔧 新增:光纤虚点处理相关字段
|
||||||
private readonly ConcurrentDictionary<string, OpticalFiberDto> _virtualPointToOpticalFiberMapping = new ConcurrentDictionary<string, OpticalFiberDto>();
|
private readonly ConcurrentDictionary<string, OpticalFiberDto> _virtualPointToOpticalFiberMapping = new ConcurrentDictionary<string, OpticalFiberDto>();
|
||||||
@ -27,10 +32,17 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
public volatile bool _isOpticalFiberMappingInitialized = false;
|
public volatile bool _isOpticalFiberMappingInitialized = false;
|
||||||
private readonly object _opticalFiberInitLock = new object();
|
private readonly object _opticalFiberInitLock = new object();
|
||||||
|
|
||||||
public VirtualTerminalHandler(ILogger<VirtualTerminalHandler> logger, WebApiRequest webApiRequest)
|
|
||||||
|
//数据缓冲队列
|
||||||
|
private ZzDataCacheContainer _zzDataCacheContainer = new ZzDataCacheContainer(5);
|
||||||
|
|
||||||
|
|
||||||
|
public VirtualTerminalHandler(ILogger<VirtualTerminalHandler> logger, WebApiRequest webApiRequest, ZzDataCacheContainerInit zzDataCacheContainerInit)
|
||||||
{
|
{
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
|
_webApiRequest = webApiRequest ?? throw new ArgumentNullException(nameof(webApiRequest));
|
||||||
|
_zzDataCacheContainerInit = zzDataCacheContainerInit;
|
||||||
|
_zzDataCacheContainerInit.InitVariantBaseinfo(_zzDataCacheContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -172,6 +184,9 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
// 根据UA_ID执行不同的处理逻辑
|
// 根据UA_ID执行不同的处理逻辑
|
||||||
await ProcessByUAId(virtualTerminalData);
|
await ProcessByUAId(virtualTerminalData);
|
||||||
|
|
||||||
|
//存入缓存
|
||||||
|
SaveDataToCache(virtualTerminalData.VA_ID, value, timestamp);
|
||||||
|
|
||||||
_logger.LogInformation("虚端子数据处理完成:{UA_ID}", virtualTerminalData.VA_ID);
|
_logger.LogInformation("虚端子数据处理完成:{UA_ID}", virtualTerminalData.VA_ID);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -229,12 +244,13 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
_logger.LogDebug("处理光纤虚点: {VA_ID}, 值: {Value}, 光纤: {TwinId}", data.VA_ID, value, opticalFiber.TwinId);
|
_logger.LogDebug("处理光纤虚点: {VA_ID}, 值: {Value}, 光纤: {TwinId}", data.VA_ID, value, opticalFiber.TwinId);
|
||||||
|
|
||||||
// 根据虚点值处理报警
|
// 根据虚点值处理报警
|
||||||
// Value = 0 表示正常状态,Value = 1 表示故障状态
|
//Boolean型变量的值(真,假,不定)2018-04-26从uPro设备
|
||||||
if (Math.Abs(value - 1) < 0.0001) // 故障状态
|
//TVariantBoolValue = (vbTrue = 0,vbFalse = 1 vbUncertain = 2):
|
||||||
|
if (Math.Abs(value) < 0.0001) // 故障状态
|
||||||
{
|
{
|
||||||
await PushOpticalFiberAlarmAsync(opticalFiber, data);
|
await PushOpticalFiberAlarmAsync(opticalFiber, data);
|
||||||
}
|
}
|
||||||
else if (Math.Abs(value) < 0.0001) // 正常状态
|
else if (Math.Abs(value - 1) < 0.0001) // 正常状态
|
||||||
{
|
{
|
||||||
await DeleteOpticalFiberAlarmAsync(opticalFiber, data);
|
await DeleteOpticalFiberAlarmAsync(opticalFiber, data);
|
||||||
}
|
}
|
||||||
@ -352,5 +368,38 @@ namespace YunDa.Server.ISMSTcp.Services
|
|||||||
opticalFiber.TwinId, opticalFiber.VirtualPointCode);
|
opticalFiber.TwinId, opticalFiber.VirtualPointCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//缓存遥信数据到内存
|
||||||
|
private void SaveDataToCache(string id, double value, DateTime timestamp)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_zzDataCacheContainer.Write(id, value, timestamp, "");
|
||||||
|
}
|
||||||
|
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>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,303 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Org.BouncyCastle.Utilities;
|
||||||
|
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;
|
||||||
|
|
||||||
|
namespace YunDa.Server.ISMSTcp.Services
|
||||||
|
{
|
||||||
|
public class ZzDataPoint
|
||||||
|
{
|
||||||
|
public DateTime TimeStamp { get; set; }
|
||||||
|
public double Value { get; }
|
||||||
|
public string ValueStr { get; }
|
||||||
|
|
||||||
|
public ZzDataPoint(DateTime time, double value, string valueStr)
|
||||||
|
{
|
||||||
|
TimeStamp = time;
|
||||||
|
Value = value;
|
||||||
|
ValueStr = valueStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ZzData
|
||||||
|
{
|
||||||
|
public TimeSpan Retention { get; set; }
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public int? DispatcherAddress { get; set; } = null;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
public double Value { get; set; }
|
||||||
|
public string ValueStr { get; set; } = string.Empty;
|
||||||
|
public DateTime TimeStamp { get; set; }
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private readonly ConcurrentQueue<(DateTime time, ZzDataPoint point)> _queue = new ConcurrentQueue<(DateTime time, ZzDataPoint point)>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ZzData(int minutes, string id, string name, int dispatcherAddress)
|
||||||
|
{
|
||||||
|
Retention = TimeSpan.FromMinutes(minutes);
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
DispatcherAddress = dispatcherAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void AddData(ZzDataPoint point)
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
|
||||||
|
point.TimeStamp = now; //2025-11-18 用当前时间(临时修改)
|
||||||
|
|
||||||
|
_queue.Enqueue((now, point));
|
||||||
|
|
||||||
|
TimeStamp = point.TimeStamp;
|
||||||
|
Value = point.Value;
|
||||||
|
ValueStr = point.ValueStr;
|
||||||
|
|
||||||
|
CleanupOldData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupOldData()
|
||||||
|
{
|
||||||
|
DateTime limit = DateTime.Now - Retention;
|
||||||
|
while (_queue.TryPeek(out var dp) && dp.time < limit)
|
||||||
|
{
|
||||||
|
_queue.TryDequeue(out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ZzDataPoint> GetData(DateTime start, DateTime end)
|
||||||
|
{
|
||||||
|
return _queue.Where(dp => dp.point.TimeStamp >= start && dp.point.TimeStamp <= end)
|
||||||
|
.Select(x => x.point)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ZzDataRequestModel
|
||||||
|
{
|
||||||
|
public List<string> Id { get; set; }
|
||||||
|
public int Times { get; set; }
|
||||||
|
public int TimeWindowType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ZzDataResultModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public int? DispatcherAddress { get; set; } = null;
|
||||||
|
public double Value { get; set; }
|
||||||
|
public string ValueStr { get; set; } = string.Empty;
|
||||||
|
public DateTime TimeStamp { get; set; } = DateTime.MinValue;
|
||||||
|
|
||||||
|
public List<ZzDataPoint> Data { get; set; } = new List<ZzDataPoint>();
|
||||||
|
|
||||||
|
public ZzDataResultModel()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public ZzDataResultModel(ZzData data)
|
||||||
|
{
|
||||||
|
Id = data.Id;
|
||||||
|
Name = data.Name;
|
||||||
|
Value = data.Value;
|
||||||
|
ValueStr = data.ValueStr;
|
||||||
|
TimeStamp = data.TimeStamp;
|
||||||
|
DispatcherAddress = data.DispatcherAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ZzDataHistoryModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
public class ZzDataCacheContainer
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, ZzData> _datas = new ConcurrentDictionary<string, ZzData>();
|
||||||
|
|
||||||
|
private readonly int _cleanupMinutes = 5;
|
||||||
|
|
||||||
|
public ZzDataCacheContainer(int cleanupMinutes)
|
||||||
|
{
|
||||||
|
_cleanupMinutes = cleanupMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string id, double val, DateTime time, string name)
|
||||||
|
{
|
||||||
|
var data = _datas.GetOrAdd(id, _ => new ZzData(_cleanupMinutes, id, name, 0));
|
||||||
|
|
||||||
|
data.AddData(new ZzDataPoint(time, val, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string id, double val, DateTime time, string name, string valStr)
|
||||||
|
{
|
||||||
|
var data = _datas.GetOrAdd(id, _ => new ZzData(_cleanupMinutes, id, name, 0));
|
||||||
|
|
||||||
|
data.AddData(new ZzDataPoint(time, val, valStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string id, double val, DateTime time, string name, string valStr, int dispatcherAddress)
|
||||||
|
{
|
||||||
|
//if (id == "YCB001101001")
|
||||||
|
//{
|
||||||
|
// int kk = 0;
|
||||||
|
//}
|
||||||
|
var data = _datas.GetOrAdd(id, _ => new ZzData(_cleanupMinutes, id, name, dispatcherAddress));
|
||||||
|
|
||||||
|
data.AddData(new ZzDataPoint(time, val, valStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, ZzDataResultModel> Read(List<string> ids, DateTime start, DateTime end)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, ZzDataResultModel>();
|
||||||
|
|
||||||
|
foreach (var id in ids)
|
||||||
|
{
|
||||||
|
|
||||||
|
//if (id == "YCB001101001")
|
||||||
|
//{
|
||||||
|
// int kk = 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (_datas.TryGetValue(id, out var channel))
|
||||||
|
{
|
||||||
|
ZzDataResultModel data = new ZzDataResultModel(channel);
|
||||||
|
data.Data = channel.GetData(start, end);
|
||||||
|
|
||||||
|
if(data.Data.Count == 0 && data.TimeStamp != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
ZzDataPoint zzDataPoint = new ZzDataPoint(DateTime.Now, data.Value, data.ValueStr);
|
||||||
|
|
||||||
|
data.Data.Add(zzDataPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
result[id] = data;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<ZzDataResultModel>> Read(List<string> ids, int seconds, int timeWindowType, CancellationToken cancellationToken, DateTime now = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(now == default)
|
||||||
|
now = DateTime.Now;
|
||||||
|
|
||||||
|
//寻找匹配的值
|
||||||
|
Dictionary<string, ZzDataResultModel> matched1 = new Dictionary<string, ZzDataResultModel>();
|
||||||
|
Dictionary<string, ZzDataResultModel> matched2 = new Dictionary<string, ZzDataResultModel>();
|
||||||
|
|
||||||
|
if (timeWindowType == 0 || timeWindowType == 2)
|
||||||
|
{
|
||||||
|
matched1 = Read(ids, now.AddSeconds(-seconds), now);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeWindowType == 1 || timeWindowType == 2)
|
||||||
|
{
|
||||||
|
int span = seconds*1000 - (int)(DateTime.Now - now).TotalMilliseconds;
|
||||||
|
|
||||||
|
if(span > 0)
|
||||||
|
await Task.Delay(span, cancellationToken);
|
||||||
|
matched2 = Read(ids, now, DateTime.Now);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var kv in matched2)
|
||||||
|
{
|
||||||
|
if (!matched1.TryAdd(kv.Key, kv.Value))
|
||||||
|
{
|
||||||
|
matched1[kv.Key].Data.AddRange(kv.Value.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched1.Values.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new List<ZzDataResultModel>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ZzDataCacheContainerInit
|
||||||
|
{
|
||||||
|
private readonly ILogger<SecondaryCircuitInspectionPlanService> _logger;
|
||||||
|
private readonly WebApiRequest _webApiRequest;
|
||||||
|
private readonly ZzTcpService _zTcpService = null;
|
||||||
|
|
||||||
|
public ZzDataCacheContainerInit(ILogger<SecondaryCircuitInspectionPlanService> logger, WebApiRequest webApiRequest, ZzTcpService zTcpService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_webApiRequest = webApiRequest;
|
||||||
|
_zTcpService = zTcpService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void InitTcpService(ITcpClient tcpClient)
|
||||||
|
{
|
||||||
|
_zTcpService.Init(tcpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化虚点信息
|
||||||
|
public async Task InitVariantBaseinfo(ZzDataCacheContainer cache)
|
||||||
|
{
|
||||||
|
//从数据库获取
|
||||||
|
var list = await _webApiRequest.GetVariantBaseinfoAsync();
|
||||||
|
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
cache.Write(item.Id, 2, DateTime.Now, item.Name, "不定");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//发送命令从综自获取
|
||||||
|
if(_zTcpService != null)
|
||||||
|
{
|
||||||
|
string cmd = "CallVAByStat|B001";
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
var sendResult = await _zTcpService.SendTcpMessageAsync(cmd, CancellationToken.None);
|
||||||
|
if (sendResult.Success)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化网关信息
|
||||||
|
public async Task InitGateWayBaseInfo(ZzDataCacheContainer cache)
|
||||||
|
{
|
||||||
|
var list = await _webApiRequest.GetGateWayBaseInfoAsync();
|
||||||
|
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
cache.Write(item.Id, 0, DateTime.Now, item.Name, "0.00 %");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using YunDa.Server.ISMSTcp.Controllers;
|
||||||
|
using YunDa.Server.ISMSTcp.Interfaces;
|
||||||
|
using YunDa.SOMS.ExternalInteraction.DataTransferObject.InspectionEquipment;
|
||||||
|
|
||||||
|
namespace YunDa.Server.ISMSTcp.Services
|
||||||
|
{
|
||||||
|
public class ZzTcpService
|
||||||
|
{
|
||||||
|
private readonly ILogger<QueryController> _logger;
|
||||||
|
private ITcpClient _tcpClient = null;
|
||||||
|
|
||||||
|
public ZzTcpService(ILogger<QueryController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Init(ITcpClient tcpClient)
|
||||||
|
{
|
||||||
|
_tcpClient = tcpClient;
|
||||||
|
}
|
||||||
|
public async Task<(bool Success, string ErrorMessage)> SendTcpMessageAsync(string message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
// 使用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);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "发送TCP消息时发生异常");
|
||||||
|
return (false, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从QueryService获取TCP客户端
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>TCP客户端实例</returns>
|
||||||
|
private ITcpClient GetTcpClientFromQueryService()
|
||||||
|
{
|
||||||
|
return _tcpClient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -39,7 +39,10 @@ namespace YunDa.Server.ISMSTcp
|
|||||||
|
|
||||||
// 添加控制器
|
// 添加控制器
|
||||||
services.AddControllers()
|
services.AddControllers()
|
||||||
.AddNewtonsoftJson(); // 使用Newtonsoft.Json
|
.AddNewtonsoftJson(options => // 使用Newtonsoft.Json
|
||||||
|
{
|
||||||
|
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
});
|
||||||
|
|
||||||
// 添加CORS支持
|
// 添加CORS支持
|
||||||
services.AddCors(options =>
|
services.AddCors(options =>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user