|
|
@ -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,7 +93,9 @@ |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="card-body"> |
|
|
|
<div class="card-body p-0"> |
|
|
|
<div class="config-container"> |
|
|
|
<div class="stats-content"> |
|
|
|
<!-- 统计摘要 --> |
|
|
|
<div class="row mb-3"> |
|
|
|
<div class="col-md-3"> |
|
|
@ -79,6 +154,82 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 图表区域 --> |
|
|
|
<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 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 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 class="table-responsive"> |
|
|
|
<table class="table table-bordered table-striped" id="statsTable"> |
|
|
@ -137,21 +288,252 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</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) { |
|
|
|