Browse Source

adsfafadsf

feature/MultiClientLog
root 1 month ago
parent
commit
cddacee5fa
  1. 204
      LTEMvcApp/Models/StatisticsData.cs
  2. 579
      LTEMvcApp/Views/Statistics/Index.cshtml

204
LTEMvcApp/Models/StatisticsData.cs

@ -25,6 +25,16 @@ namespace LTEMvcApp.Models
/// </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>
@ -81,12 +91,105 @@ namespace LTEMvcApp.Models
};
// 解析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)
@ -127,31 +230,20 @@ namespace LTEMvcApp.Models
UlGbrUseAvg = cellObj["ul_gbr_use_avg"]?.Value<double>() ?? 0
};
// 解析计数器
if (cellObj["counters"] is JObject countersObj)
{
if (countersObj["messages"] is JObject messagesObj)
{
foreach (var msg in messagesObj)
{
cellInfo.Counters.Messages[msg.Key] = msg.Value?.Value<int>() ?? 0;
}
}
if (countersObj["errors"] is JObject errorsObj)
{
foreach (var err in errorsObj)
{
cellInfo.Counters.Errors[err.Key] = err.Value?.Value<int>() ?? 0;
}
}
}
// 解析小区计数器
ParseCounters(cellObj, cellInfo.Counters);
stats.Cells[cell.Key] = cellInfo;
}
}
}
}
// 解析RF端口信息
/// <summary>
/// 解析RF端口信息
/// </summary>
private static void ParseRfPortsInfo(JObject data, StatisticsData stats)
{
if (data["rf_ports"] is JObject rfPortsObj)
{
foreach (var port in rfPortsObj)
@ -170,27 +262,38 @@ namespace LTEMvcApp.Models
}
}
}
}
// 解析全局计数器
/// <summary>
/// 解析全局计数器
/// </summary>
private static void ParseGlobalCounters(JObject data, StatisticsData stats)
{
if (data["counters"] is JObject globalCountersObj)
{
if (globalCountersObj["messages"] is JObject messagesObj)
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)
{
foreach (var msg in messagesObj)
{
stats.Counters.Messages[msg.Key] = msg.Value?.Value<int>() ?? 0;
}
counters.Messages[msg.Key] = msg.Value?.Value<int>() ?? 0;
}
if (globalCountersObj["errors"] is JObject errorsObj)
}
if (countersObj["errors"] is JObject errorsObj)
{
foreach (var err in errorsObj)
{
foreach (var err in errorsObj)
{
stats.Counters.Errors[err.Key] = err.Value?.Value<int>() ?? 0;
}
counters.Errors[err.Key] = err.Value?.Value<int>() ?? 0;
}
}
return stats;
}
}
@ -202,6 +305,41 @@ namespace LTEMvcApp.Models
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>

579
LTEMvcApp/Views/Statistics/Index.cshtml

@ -2,7 +2,80 @@
ViewData["Title"] = "统计数据";
}
<div class="container-fluid">
<style>
/* 页面整体布局 */
.config-container {
max-height: calc(100vh - 280px);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 内容区域 */
.stats-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 0.75rem;
}
/* 自定义滚动条 */
.stats-content::-webkit-scrollbar {
width: 6px;
}
.stats-content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.stats-content::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.stats-content::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 图表容器样式 */
.chart-container {
position: relative;
height: 300px;
margin-bottom: 1rem;
}
/* 响应式调整 */
@@media (max-width: 768px) {
.config-container {
max-height: calc(100vh - 240px);
}
.stats-content {
padding: 0.6rem;
}
.chart-container {
height: 250px;
}
}
@@media (max-width: 576px) {
.config-container {
max-height: calc(100vh - 220px);
}
.stats-content {
padding: 0.5rem;
}
.chart-container {
height: 200px;
}
}
</style>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
@ -20,114 +93,194 @@
</button>
</div>
</div>
<div class="card-body">
<!-- 统计摘要 -->
<div class="row mb-3">
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-info"><i class="fas fa-server"></i></span>
<div class="info-box-content">
<span class="info-box-text">客户端数量</span>
<span class="info-box-number" id="clientCount">0</span>
<div class="card-body p-0">
<div class="config-container">
<div class="stats-content">
<!-- 统计摘要 -->
<div class="row mb-3">
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-info"><i class="fas fa-server"></i></span>
<div class="info-box-content">
<span class="info-box-text">客户端数量</span>
<span class="info-box-number" id="clientCount">0</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-success"><i class="fas fa-database"></i></span>
<div class="info-box-content">
<span class="info-box-text">队列大小</span>
<span class="info-box-number" id="queueCount">0</span>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-success"><i class="fas fa-database"></i></span>
<div class="info-box-content">
<span class="info-box-text">队列大小</span>
<span class="info-box-number" id="queueCount">0</span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-warning"><i class="fas fa-clock"></i></span>
<div class="info-box-content">
<span class="info-box-text">最后更新</span>
<span class="info-box-number" id="lastUpdate">-</span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-primary"><i class="fas fa-signal"></i></span>
<div class="info-box-content">
<span class="info-box-text">SSE状态</span>
<span class="info-box-number" id="sseStatus">未连接</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-warning"><i class="fas fa-clock"></i></span>
<div class="info-box-content">
<span class="info-box-text">最后更新</span>
<span class="info-box-number" id="lastUpdate">-</span>
<!-- 客户端选择 -->
<div class="row mb-3">
<div class="col-md-6">
<div class="form-group">
<label for="clientSelect">选择客户端:</label>
<select class="form-control" id="clientSelect" onchange="loadClientStats()">
<option value="">所有客户端</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="refreshInterval">刷新间隔 (秒):</label>
<input type="number" class="form-control" id="refreshInterval" value="5" min="1" max="60" onchange="updateRefreshInterval()">
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-primary"><i class="fas fa-signal"></i></span>
<div class="info-box-content">
<span class="info-box-text">SSE状态</span>
<span class="info-box-number" id="sseStatus">未连接</span>
<!-- 图表区域 -->
<div class="row mb-4">
<!-- CPU使用率图表 -->
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">CPU使用率</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="cpuChart"></canvas>
</div>
</div>
</div>
</div>
<!-- RF采样率图表 -->
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">RF采样率</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="rfChart"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 客户端选择 -->
<div class="row mb-3">
<div class="col-md-6">
<div class="form-group">
<label for="clientSelect">选择客户端:</label>
<select class="form-control" id="clientSelect" onchange="loadClientStats()">
<option value="">所有客户端</option>
</select>
<!-- 小区性能图表 -->
<div class="row mb-4">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">小区性能指标</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="cellPerformanceChart"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="refreshInterval">刷新间隔 (秒):</label>
<input type="number" class="form-control" id="refreshInterval" value="5" min="1" max="60" onchange="updateRefreshInterval()">
<!-- 样本数据图表 -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">TX样本数据</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="txSamplesChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">RX样本数据</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="rxSamplesChart"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 统计数据表格 -->
<div class="table-responsive">
<table class="table table-bordered table-striped" id="statsTable">
<thead>
<tr>
<th>客户端</th>
<th>实例ID</th>
<th>CPU使用率</th>
<th>小区数量</th>
<th>RF端口</th>
<th>消息ID</th>
<th>持续时间</th>
<th>接收时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="statsTableBody">
<!-- 数据将通过JavaScript动态填充 -->
</tbody>
</table>
</div>
<!-- 统计数据表格 -->
<div class="table-responsive">
<table class="table table-bordered table-striped" id="statsTable">
<thead>
<tr>
<th>客户端</th>
<th>实例ID</th>
<th>CPU使用率</th>
<th>小区数量</th>
<th>RF端口</th>
<th>消息ID</th>
<th>持续时间</th>
<th>接收时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="statsTableBody">
<!-- 数据将通过JavaScript动态填充 -->
</tbody>
</table>
</div>
<!-- 小区详细信息 -->
<div class="row mt-4" id="cellDetails" style="display: none;">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4 class="card-title">小区详细信息 - <span id="selectedClientName"></span></h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered table-striped" id="cellTable">
<thead>
<tr>
<th>小区ID</th>
<th>下行比特率</th>
<th>上行比特率</th>
<th>下行使用率(平均)</th>
<th>上行使用率(平均)</th>
<th>UE数量(平均)</th>
<th>ERAB数量(平均)</th>
<th>调度用户(下行)</th>
<th>调度用户(上行)</th>
</tr>
</thead>
<tbody id="cellTableBody">
<!-- 小区数据将通过JavaScript动态填充 -->
</tbody>
</table>
<!-- 小区详细信息 -->
<div class="row mt-4" id="cellDetails" style="display: none;">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4 class="card-title">小区详细信息 - <span id="selectedClientName"></span></h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered table-striped" id="cellTable">
<thead>
<tr>
<th>小区ID</th>
<th>下行比特率</th>
<th>上行比特率</th>
<th>下行使用率(平均)</th>
<th>上行使用率(平均)</th>
<th>UE数量(平均)</th>
<th>ERAB数量(平均)</th>
<th>调度用户(下行)</th>
<th>调度用户(上行)</th>
</tr>
</thead>
<tbody id="cellTableBody">
<!-- 小区数据将通过JavaScript动态填充 -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@ -140,18 +293,247 @@
</div>
@section Scripts {
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
let sseConnection = null;
let refreshTimer = null;
let currentStats = {};
// 图表实例
let cpuChart = null;
let rfChart = null;
let cellPerformanceChart = null;
let txSamplesChart = null;
let rxSamplesChart = null;
// 页面加载完成后初始化
$(document).ready(function() {
initializeCharts();
loadSummary();
loadAllStats();
startRefreshTimer();
});
// 初始化图表
function initializeCharts() {
// CPU使用率图表
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'doughnut',
data: {
labels: ['CPU使用率', '空闲'],
datasets: [{
data: [0, 100],
backgroundColor: ['#ff6384', '#36a2eb'],
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
// RF采样率图表
const rfCtx = document.getElementById('rfChart').getContext('2d');
rfChart = new Chart(rfCtx, {
type: 'bar',
data: {
labels: ['RX采样率', 'TX采样率'],
datasets: [{
label: '采样率 (MHz)',
data: [0, 0],
backgroundColor: ['#4bc0c0', '#ff9f40'],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
}
}
});
// 小区性能图表
const cellCtx = document.getElementById('cellPerformanceChart').getContext('2d');
cellPerformanceChart = new Chart(cellCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: '下行使用率 (%)',
data: [],
borderColor: '#ff6384',
backgroundColor: 'rgba(255, 99, 132, 0.1)',
tension: 0.1
}, {
label: '上行使用率 (%)',
data: [],
borderColor: '#36a2eb',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100
}
}
}
});
// TX样本图表
const txCtx = document.getElementById('txSamplesChart').getContext('2d');
txSamplesChart = new Chart(txCtx, {
type: 'scatter',
data: {
datasets: [{
label: 'TX样本',
data: [],
backgroundColor: '#ff6384',
pointRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
title: {
display: true,
text: 'RMS (dB)'
}
},
y: {
title: {
display: true,
text: 'Max (dB)'
}
}
}
}
});
// RX样本图表
const rxCtx = document.getElementById('rxSamplesChart').getContext('2d');
rxSamplesChart = new Chart(rxCtx, {
type: 'scatter',
data: {
datasets: [{
label: 'RX样本',
data: [],
backgroundColor: '#36a2eb',
pointRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
title: {
display: true,
text: 'RMS (dB)'
}
},
y: {
title: {
display: true,
text: 'Max (dB)'
}
}
}
}
});
}
// 更新图表数据
function updateCharts() {
const clients = Object.keys(currentStats);
if (clients.length === 0) return;
// 获取第一个客户端的数据作为示例
const firstClient = currentStats[clients[0]];
// 更新CPU图表
if (firstClient.cpu) {
const cpuUsage = firstClient.cpu.global || 0;
cpuChart.data.datasets[0].data = [cpuUsage, 100 - cpuUsage];
cpuChart.update();
}
// 更新RF图表
if (firstClient.rf) {
rfChart.data.datasets[0].data = [
firstClient.rf.rxSampleRate || 0,
firstClient.rf.txSampleRate || 0
];
rfChart.update();
}
// 更新小区性能图表
if (firstClient.cells) {
const cellIds = Object.keys(firstClient.cells);
const dlUsage = [];
const ulUsage = [];
cellIds.forEach(cellId => {
const cell = firstClient.cells[cellId];
dlUsage.push(cell.dlUseAvg || 0);
ulUsage.push(cell.ulUseAvg || 0);
});
cellPerformanceChart.data.labels = cellIds;
cellPerformanceChart.data.datasets[0].data = dlUsage;
cellPerformanceChart.data.datasets[1].data = ulUsage;
cellPerformanceChart.update();
}
// 更新样本图表
if (firstClient.samples) {
// TX样本
const txData = [];
if (firstClient.samples.txSamples) {
firstClient.samples.txSamples.forEach(sample => {
txData.push({
x: sample.rms,
y: sample.max
});
});
}
txSamplesChart.data.datasets[0].data = txData;
txSamplesChart.update();
// RX样本
const rxData = [];
if (firstClient.samples.rxSamples) {
firstClient.samples.rxSamples.forEach(sample => {
rxData.push({
x: sample.rms,
y: sample.max
});
});
}
rxSamplesChart.data.datasets[0].data = rxData;
rxSamplesChart.update();
}
}
// 加载统计摘要
function loadSummary() {
$.get('/Statistics/GetSummary', function(response) {
@ -172,6 +554,7 @@
currentStats = response.data;
updateStatsTable();
updateClientSelect();
updateCharts();
}
});
}
@ -256,6 +639,7 @@
if (response.success) {
currentStats = { [clientName]: response.data };
updateStatsTable();
updateCharts();
}
});
}
@ -304,6 +688,7 @@
if (data.type === 'stats_update') {
currentStats = data.data;
updateStatsTable();
updateCharts();
loadSummary();
}
} catch (e) {

Loading…
Cancel
Save