358 lines
12 KiB
C#
358 lines
12 KiB
C#
using Google.Protobuf.WellKnownTypes;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Identity.Client;
|
||
using Org.BouncyCastle.Utilities;
|
||
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel.DataAnnotations;
|
||
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; }
|
||
public uint ValCount { get; set; } = 0;
|
||
public DateTime StartTimeStamp { get; set; } = DateTime.MinValue;
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
private readonly ConcurrentQueue<(DateTime time, ZzDataPoint point)> _queue = new ConcurrentQueue<(DateTime time, ZzDataPoint point)>();
|
||
|
||
public ConcurrentQueue<(DateTime time, ZzDataPoint point)> Datas => _queue;
|
||
|
||
|
||
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;
|
||
|
||
//////////////////////////////
|
||
ValCount++;
|
||
if(StartTimeStamp == DateTime.MinValue)
|
||
StartTimeStamp = now;
|
||
|
||
|
||
|
||
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 string DeviceName { 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 enum ZzDataCacheContainerDataType
|
||
{
|
||
eYC = 0, //遥测
|
||
eYX, //遥信
|
||
eVA, //虚点
|
||
eGW //网关
|
||
}
|
||
public class ZzDataCacheContainer
|
||
{
|
||
private readonly ConcurrentDictionary<string, ZzData> _datas = new ConcurrentDictionary<string, ZzData>();
|
||
|
||
private readonly int _cleanupMinutes = 5;
|
||
|
||
private ZzDataCacheContainerDataType _dataType = ZzDataCacheContainerDataType.eYC;
|
||
|
||
private TelemeteringHandle? _telemeteringHandle = null;
|
||
|
||
public ZzDataCacheContainer(ZzDataCacheContainerDataType dataType, int cleanupMinutes)
|
||
{
|
||
_dataType = dataType;
|
||
_cleanupMinutes = cleanupMinutes;
|
||
}
|
||
|
||
public void SetTelemeteringHandle(TelemeteringHandle handle)
|
||
{
|
||
_telemeteringHandle = handle;
|
||
}
|
||
|
||
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 async Task<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);
|
||
|
||
bool isFind = data.Data.Count > 0;
|
||
|
||
if (data.Data.Count == 0 && data.TimeStamp != DateTime.MinValue)
|
||
{
|
||
ZzDataPoint zzDataPoint = new ZzDataPoint(DateTime.Now, data.Value, data.ValueStr.Replace(" ", ""));
|
||
|
||
data.Data.Add(zzDataPoint);
|
||
}
|
||
|
||
if (_telemeteringHandle != null &&_dataType == ZzDataCacheContainerDataType.eYC && id == "YCB001103003")
|
||
{
|
||
if (data.Data.Where(e => Math.Abs(e.Value) < 0.0001).Count() == data.Data.Count)
|
||
{
|
||
string time = DateTime.Now.ToString("yyy/MM/dd HH:mm:ss");
|
||
_telemeteringHandle.LogWarning($"【{time}】: {id}所有值为0[{data.Data.Count}]:状态:{isFind}, Cache长度:{channel.Datas.Count}, Cache时间:{channel.StartTimeStamp.ToString("yyy/MM/dd HH:mm:ss")} ~ {channel.TimeStamp.ToString("yyy/MM/dd HH:mm:ss")}, 最后一个值:{channel.ValueStr}");
|
||
|
||
var redisData = await _telemeteringHandle.GetDataFromRedis(id);
|
||
if (redisData != null)
|
||
{
|
||
_telemeteringHandle.LogWarning($"【{time}】: {id}在Redis中的值为:{redisData.ResultValue} - {redisData.ResultTime }");
|
||
}
|
||
else
|
||
{
|
||
_telemeteringHandle.LogWarning($"【{time}】: {id}在Redis中没有找到");
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
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 = await 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 = await 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 < 5; i++)
|
||
{
|
||
var sendResult = await _zTcpService.SendTcpMessageAsync(cmd, CancellationToken.None);
|
||
if (sendResult.Success)
|
||
{
|
||
break;
|
||
}
|
||
await Task.Delay(2000);
|
||
}
|
||
}
|
||
}
|
||
|
||
//初始化网关信息
|
||
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 %");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|