You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
16 KiB
479 lines
16 KiB
|
|
using System.Collections.Concurrent;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace LTEMvcApp.Models
|
|
{
|
|
/// <summary>
|
|
/// 统计数据模型
|
|
/// </summary>
|
|
public class StatisticsData
|
|
{
|
|
/// <summary>
|
|
/// 消息类型
|
|
/// </summary>
|
|
public string Message { get; set; } = "";
|
|
|
|
/// <summary>
|
|
/// 实例ID
|
|
/// </summary>
|
|
public string InstanceId { get; set; } = "";
|
|
|
|
/// <summary>
|
|
/// CPU信息
|
|
/// </summary>
|
|
public CpuInfo Cpu { get; set; } = new CpuInfo();
|
|
|
|
/// <summary>
|
|
/// RF信息
|
|
/// </summary>
|
|
public RfInfo Rf { get; set; } = new RfInfo();
|
|
|
|
/// <summary>
|
|
/// 样本信息
|
|
/// </summary>
|
|
public SamplesInfo Samples { get; set; } = new SamplesInfo();
|
|
|
|
/// <summary>
|
|
/// 小区信息
|
|
/// </summary>
|
|
public Dictionary<string, CellInfo> Cells { get; set; } = new Dictionary<string, CellInfo>();
|
|
|
|
/// <summary>
|
|
/// RF端口信息
|
|
/// </summary>
|
|
public Dictionary<string, RfPortInfo> RfPorts { get; set; } = new Dictionary<string, RfPortInfo>();
|
|
|
|
/// <summary>
|
|
/// 计数器信息
|
|
/// </summary>
|
|
public CountersInfo Counters { get; set; } = new CountersInfo();
|
|
|
|
/// <summary>
|
|
/// 持续时间
|
|
/// </summary>
|
|
public double Duration { get; set; }
|
|
|
|
/// <summary>
|
|
/// 消息ID
|
|
/// </summary>
|
|
public int MessageId { get; set; }
|
|
|
|
/// <summary>
|
|
/// 时间戳
|
|
/// </summary>
|
|
public double Time { get; set; }
|
|
|
|
/// <summary>
|
|
/// 接收时间
|
|
/// </summary>
|
|
public DateTime ReceivedAt { get; set; } = DateTime.Now;
|
|
|
|
/// <summary>
|
|
/// 客户端名称
|
|
/// </summary>
|
|
public string ClientName { get; set; } = "";
|
|
|
|
/// <summary>
|
|
/// 从JObject创建StatisticsData
|
|
/// </summary>
|
|
public static StatisticsData FromJObject(JObject data, string clientName)
|
|
{
|
|
var stats = new StatisticsData
|
|
{
|
|
Message = data["message"]?.ToString() ?? "",
|
|
InstanceId = data["instance_id"]?.ToString() ?? "",
|
|
Duration = data["duration"]?.Value<double>() ?? 0,
|
|
MessageId = data["message_id"]?.Value<int>() ?? 0,
|
|
Time = data["time"]?.Value<double>() ?? 0,
|
|
ClientName = clientName
|
|
};
|
|
|
|
// 解析CPU信息
|
|
ParseCpuInfo(data, stats);
|
|
|
|
// 解析RF信息
|
|
ParseRfInfo(data, stats);
|
|
|
|
// 解析样本信息
|
|
ParseSamplesInfo(data, stats);
|
|
|
|
// 解析小区信息
|
|
ParseCellsInfo(data, stats);
|
|
|
|
// 解析RF端口信息
|
|
ParseRfPortsInfo(data, stats);
|
|
|
|
// 解析全局计数器
|
|
ParseGlobalCounters(data, stats);
|
|
|
|
return stats;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析CPU信息
|
|
/// </summary>
|
|
private static void ParseCpuInfo(JObject data, StatisticsData stats)
|
|
{
|
|
if (data["cpu"] is JObject cpuObj)
|
|
{
|
|
stats.Cpu.Global = cpuObj["global"]?.Value<double>() ?? 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析RF信息
|
|
/// </summary>
|
|
private static void ParseRfInfo(JObject data, StatisticsData stats)
|
|
{
|
|
if (data["rf"] is JObject rfObj)
|
|
{
|
|
stats.Rf.RxSampleRate = rfObj["rx_sample_rate"]?.Value<double>() ?? 0;
|
|
stats.Rf.TxSampleRate = rfObj["tx_sample_rate"]?.Value<double>() ?? 0;
|
|
stats.Rf.RxCpuTime = rfObj["rx_cpu_time"]?.Value<double>() ?? 0;
|
|
stats.Rf.TxCpuTime = rfObj["tx_cpu_time"]?.Value<double>() ?? 0;
|
|
stats.Rf.RxtxDelayMin = rfObj["rxtx_delay_min"]?.Value<double>() ?? 0;
|
|
stats.Rf.RxtxDelayAvg = rfObj["rxtx_delay_avg"]?.Value<double>() ?? 0;
|
|
stats.Rf.RxtxDelayMax = rfObj["rxtx_delay_max"]?.Value<double>() ?? 0;
|
|
stats.Rf.RxtxDelaySd = rfObj["rxtx_delay_sd"]?.Value<double>() ?? 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析样本信息
|
|
/// </summary>
|
|
private static void ParseSamplesInfo(JObject data, StatisticsData stats)
|
|
{
|
|
if (data["samples"] is JObject samplesObj)
|
|
{
|
|
// 解析TX样本
|
|
if (samplesObj["tx"] is JArray txArray)
|
|
{
|
|
foreach (var txSample in txArray)
|
|
{
|
|
if (txSample is JObject txObj)
|
|
{
|
|
stats.Samples.TxSamples.Add(new SampleInfo
|
|
{
|
|
Rms = txObj["rms"]?.Value<double>() ?? 0,
|
|
Max = txObj["max"]?.Value<double>() ?? 0,
|
|
Sat = txObj["sat"]?.Value<int>() ?? 0,
|
|
Count = txObj["count"]?.Value<long>() ?? 0
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// 解析RX样本
|
|
if (samplesObj["rx"] is JArray rxArray)
|
|
{
|
|
foreach (var rxSample in rxArray)
|
|
{
|
|
if (rxSample is JObject rxObj)
|
|
{
|
|
stats.Samples.RxSamples.Add(new SampleInfo
|
|
{
|
|
Rms = rxObj["rms"]?.Value<double>() ?? 0,
|
|
Max = rxObj["max"]?.Value<double>() ?? 0,
|
|
Sat = rxObj["sat"]?.Value<int>() ?? 0,
|
|
Count = rxObj["count"]?.Value<long>() ?? 0
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析小区信息
|
|
/// </summary>
|
|
private static void ParseCellsInfo(JObject data, StatisticsData stats)
|
|
{
|
|
if (data["cells"] is JObject cellsObj)
|
|
{
|
|
foreach (var cell in cellsObj)
|
|
{
|
|
if (cell.Value is JObject cellObj)
|
|
{
|
|
var cellInfo = new CellInfo
|
|
{
|
|
DlBitrate = cellObj["dl_bitrate"]?.Value<double>() ?? 0,
|
|
UlBitrate = cellObj["ul_bitrate"]?.Value<double>() ?? 0,
|
|
DlTx = cellObj["dl_tx"]?.Value<double>() ?? 0,
|
|
UlTx = cellObj["ul_tx"]?.Value<double>() ?? 0,
|
|
DlRetx = cellObj["dl_retx"]?.Value<double>() ?? 0,
|
|
UlRetx = cellObj["ul_retx"]?.Value<double>() ?? 0,
|
|
DlUseMin = cellObj["dl_use_min"]?.Value<double>() ?? 0,
|
|
DlUseMax = cellObj["dl_use_max"]?.Value<double>() ?? 0,
|
|
DlUseAvg = cellObj["dl_use_avg"]?.Value<double>() ?? 0,
|
|
UlUseMin = cellObj["ul_use_min"]?.Value<double>() ?? 0,
|
|
UlUseMax = cellObj["ul_use_max"]?.Value<double>() ?? 0,
|
|
UlUseAvg = cellObj["ul_use_avg"]?.Value<double>() ?? 0,
|
|
DlSchedUsersMin = cellObj["dl_sched_users_min"]?.Value<double>() ?? 0,
|
|
DlSchedUsersMax = cellObj["dl_sched_users_max"]?.Value<double>() ?? 0,
|
|
DlSchedUsersAvg = cellObj["dl_sched_users_avg"]?.Value<double>() ?? 0,
|
|
UlSchedUsersMin = cellObj["ul_sched_users_min"]?.Value<double>() ?? 0,
|
|
UlSchedUsersMax = cellObj["ul_sched_users_max"]?.Value<double>() ?? 0,
|
|
UlSchedUsersAvg = cellObj["ul_sched_users_avg"]?.Value<double>() ?? 0,
|
|
UeCountMin = cellObj["ue_count_min"]?.Value<double>() ?? 0,
|
|
UeCountMax = cellObj["ue_count_max"]?.Value<double>() ?? 0,
|
|
UeCountAvg = cellObj["ue_count_avg"]?.Value<double>() ?? 0,
|
|
ErabCountMin = cellObj["erab_count_min"]?.Value<double>() ?? 0,
|
|
ErabCountMax = cellObj["erab_count_max"]?.Value<double>() ?? 0,
|
|
ErabCountAvg = cellObj["erab_count_avg"]?.Value<double>() ?? 0,
|
|
DlGbrUseMin = cellObj["dl_gbr_use_min"]?.Value<double>() ?? 0,
|
|
DlGbrUseMax = cellObj["dl_gbr_use_max"]?.Value<double>() ?? 0,
|
|
DlGbrUseAvg = cellObj["dl_gbr_use_avg"]?.Value<double>() ?? 0,
|
|
UlGbrUseMin = cellObj["ul_gbr_use_min"]?.Value<double>() ?? 0,
|
|
UlGbrUseMax = cellObj["ul_gbr_use_max"]?.Value<double>() ?? 0,
|
|
UlGbrUseAvg = cellObj["ul_gbr_use_avg"]?.Value<double>() ?? 0
|
|
};
|
|
|
|
// 解析小区计数器
|
|
ParseCounters(cellObj, cellInfo.Counters);
|
|
|
|
stats.Cells[cell.Key] = cellInfo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析RF端口信息
|
|
/// </summary>
|
|
private static void ParseRfPortsInfo(JObject data, StatisticsData stats)
|
|
{
|
|
if (data["rf_ports"] is JObject rfPortsObj)
|
|
{
|
|
foreach (var port in rfPortsObj)
|
|
{
|
|
if (port.Value is JObject portObj)
|
|
{
|
|
var rfPortInfo = new RfPortInfo();
|
|
if (portObj["rxtx_delay"] is JObject delayObj)
|
|
{
|
|
foreach (var delay in delayObj)
|
|
{
|
|
rfPortInfo.RxtxDelay[delay.Key] = delay.Value?.Value<double>() ?? 0;
|
|
}
|
|
}
|
|
stats.RfPorts[port.Key] = rfPortInfo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析全局计数器
|
|
/// </summary>
|
|
private static void ParseGlobalCounters(JObject data, StatisticsData stats)
|
|
{
|
|
if (data["counters"] is JObject globalCountersObj)
|
|
{
|
|
ParseCounters(globalCountersObj, stats.Counters);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析计数器信息
|
|
/// </summary>
|
|
private static void ParseCounters(JObject countersObj, CountersInfo counters)
|
|
{
|
|
if (countersObj["messages"] is JObject messagesObj)
|
|
{
|
|
foreach (var msg in messagesObj)
|
|
{
|
|
counters.Messages[msg.Key] = msg.Value?.Value<int>() ?? 0;
|
|
}
|
|
}
|
|
if (countersObj["errors"] is JObject errorsObj)
|
|
{
|
|
foreach (var err in errorsObj)
|
|
{
|
|
counters.Errors[err.Key] = err.Value?.Value<int>() ?? 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// CPU信息
|
|
/// </summary>
|
|
public class CpuInfo
|
|
{
|
|
public double Global { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// RF信息
|
|
/// </summary>
|
|
public class RfInfo
|
|
{
|
|
public double RxSampleRate { get; set; }
|
|
public double TxSampleRate { get; set; }
|
|
public double RxCpuTime { get; set; }
|
|
public double TxCpuTime { get; set; }
|
|
public double RxtxDelayMin { get; set; }
|
|
public double RxtxDelayAvg { get; set; }
|
|
public double RxtxDelayMax { get; set; }
|
|
public double RxtxDelaySd { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 样本信息
|
|
/// </summary>
|
|
public class SamplesInfo
|
|
{
|
|
public List<SampleInfo> TxSamples { get; set; } = new List<SampleInfo>();
|
|
public List<SampleInfo> RxSamples { get; set; } = new List<SampleInfo>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 单个样本信息
|
|
/// </summary>
|
|
public class SampleInfo
|
|
{
|
|
public double Rms { get; set; }
|
|
public double Max { get; set; }
|
|
public int Sat { get; set; }
|
|
public long Count { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 小区信息
|
|
/// </summary>
|
|
public class CellInfo
|
|
{
|
|
public double DlBitrate { get; set; }
|
|
public double UlBitrate { get; set; }
|
|
public double DlTx { get; set; }
|
|
public double UlTx { get; set; }
|
|
public double DlRetx { get; set; }
|
|
public double UlRetx { get; set; }
|
|
public double DlUseMin { get; set; }
|
|
public double DlUseMax { get; set; }
|
|
public double DlUseAvg { get; set; }
|
|
public double UlUseMin { get; set; }
|
|
public double UlUseMax { get; set; }
|
|
public double UlUseAvg { get; set; }
|
|
public double DlSchedUsersMin { get; set; }
|
|
public double DlSchedUsersMax { get; set; }
|
|
public double DlSchedUsersAvg { get; set; }
|
|
public double UlSchedUsersMin { get; set; }
|
|
public double UlSchedUsersMax { get; set; }
|
|
public double UlSchedUsersAvg { get; set; }
|
|
public double UeCountMin { get; set; }
|
|
public double UeCountMax { get; set; }
|
|
public double UeCountAvg { get; set; }
|
|
public double ErabCountMin { get; set; }
|
|
public double ErabCountMax { get; set; }
|
|
public double ErabCountAvg { get; set; }
|
|
public double DlGbrUseMin { get; set; }
|
|
public double DlGbrUseMax { get; set; }
|
|
public double DlGbrUseAvg { get; set; }
|
|
public double UlGbrUseMin { get; set; }
|
|
public double UlGbrUseMax { get; set; }
|
|
public double UlGbrUseAvg { get; set; }
|
|
public CountersInfo Counters { get; set; } = new CountersInfo();
|
|
}
|
|
|
|
/// <summary>
|
|
/// RF端口信息
|
|
/// </summary>
|
|
public class RfPortInfo
|
|
{
|
|
public Dictionary<string, double> RxtxDelay { get; set; } = new Dictionary<string, double>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计数器信息
|
|
/// </summary>
|
|
public class CountersInfo
|
|
{
|
|
public Dictionary<string, int> Messages { get; set; } = new Dictionary<string, int>();
|
|
public Dictionary<string, int> Errors { get; set; } = new Dictionary<string, int>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 统计数据队列管理器
|
|
/// </summary>
|
|
public class StatisticsQueueManager
|
|
{
|
|
private readonly ConcurrentQueue<StatisticsData> _statsQueue = new ConcurrentQueue<StatisticsData>();
|
|
private readonly int _maxQueueSize = 1000; // 最大队列大小
|
|
private readonly object _lockObject = new object();
|
|
|
|
/// <summary>
|
|
/// 添加统计数据到队列
|
|
/// </summary>
|
|
public void EnqueueStats(StatisticsData stats)
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
_statsQueue.Enqueue(stats);
|
|
|
|
// 维持队列大小
|
|
while (_statsQueue.Count > _maxQueueSize)
|
|
{
|
|
_statsQueue.TryDequeue(out _);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取队列中的所有统计数据
|
|
/// </summary>
|
|
public List<StatisticsData> GetAllStats()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
var result = new List<StatisticsData>();
|
|
foreach (var item in _statsQueue)
|
|
{
|
|
if (item != null)
|
|
result.Add(item);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取最新的统计数据
|
|
/// </summary>
|
|
public StatisticsData? GetLatestStats()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
return _statsQueue.TryPeek(out var stats) ? stats : null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清空队列
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
while (_statsQueue.TryDequeue(out _))
|
|
{
|
|
// 清空所有数据
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取队列大小
|
|
/// </summary>
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
return _statsQueue.Count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|