Skip to content

Commit 15d73b7

Browse files
feat: add ECharts horizontal bar charts to quadra view
Add Apache ECharts 6.0.0 as a vendored asset. Render per-card horizontal bar charts showing Load and Memory Usage for each VPU resource type (Decoder, Encoder, Uploader, Scaler, AI), matching the NetInt dashboard reference design. Add a VPU Devices summary table listing all detected cards. Use CSP nonce for inline script. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5730c16 commit 15d73b7

File tree

3 files changed

+200
-16
lines changed

3 files changed

+200
-16
lines changed

web/skins/classic/assets/echarts-6.0.0/echarts.min.js

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/skins/classic/views/js/quadra.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,96 @@
1+
function initCharts() {
2+
if (typeof echarts === 'undefined' || typeof quadraChartData === 'undefined') return;
3+
4+
quadraChartData.forEach(function(cardData) {
5+
var el = document.getElementById(cardData.id);
6+
if (!el) return;
7+
8+
var chart = echarts.init(el);
9+
10+
// Build category labels and data arrays (reversed so first item appears at top)
11+
var categories = [];
12+
var loadData = [];
13+
var memData = [];
14+
for (var i = cardData.resources.length - 1; i >= 0; i--) {
15+
var r = cardData.resources[i];
16+
categories.push(r.label);
17+
loadData.push(r.load);
18+
memData.push(r.mem);
19+
}
20+
21+
chart.setOption({
22+
tooltip: {
23+
trigger: 'axis',
24+
axisPointer: {type: 'shadow'},
25+
formatter: function(params) {
26+
var label = params[0].name;
27+
var lines = [label];
28+
params.forEach(function(p) {
29+
lines.push(p.marker + ' ' + p.seriesName + ': ' + p.value + '%');
30+
});
31+
return lines.join('<br/>');
32+
}
33+
},
34+
legend: {
35+
data: ['Load', 'Memory Usage'],
36+
top: 0,
37+
textStyle: {fontSize: 11}
38+
},
39+
grid: {
40+
left: 70,
41+
right: 30,
42+
top: 30,
43+
bottom: 25
44+
},
45+
xAxis: {
46+
type: 'value',
47+
min: 0,
48+
max: 100,
49+
axisLabel: {formatter: '{value}%', fontSize: 10},
50+
splitLine: {lineStyle: {type: 'dashed', color: '#e0e0e0'}}
51+
},
52+
yAxis: {
53+
type: 'category',
54+
data: categories,
55+
axisLabel: {fontSize: 11},
56+
axisTick: {show: false}
57+
},
58+
series: [
59+
{
60+
name: 'Load',
61+
type: 'bar',
62+
data: loadData,
63+
barWidth: 8,
64+
itemStyle: {color: '#8e8e8e', borderRadius: [0, 3, 3, 0]},
65+
label: {
66+
show: true,
67+
position: 'right',
68+
fontSize: 10,
69+
formatter: '{c}%'
70+
}
71+
},
72+
{
73+
name: 'Memory Usage',
74+
type: 'bar',
75+
data: memData,
76+
barWidth: 8,
77+
itemStyle: {color: '#00bcd4', borderRadius: [0, 3, 3, 0]},
78+
label: {
79+
show: true,
80+
position: 'right',
81+
fontSize: 10,
82+
formatter: '{c}%'
83+
}
84+
}
85+
]
86+
});
87+
88+
window.addEventListener('resize', function() {
89+
chart.resize();
90+
});
91+
});
92+
}
93+
194
function initPage() {
295
// Manage the BACK button
396
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
@@ -14,6 +107,9 @@ function initPage() {
14107
window.location.reload(true);
15108
});
16109

110+
// Initialize ECharts
111+
initCharts();
112+
17113
// Auto-refresh page
18114
if (quadraRefreshInterval > 0) {
19115
setInterval(function() {

web/skins/classic/views/quadra.php

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -254,23 +254,36 @@ function formatBitrate($bps) {
254254
</div>
255255
<?php else: ?>
256256

257-
<!-- System-wide info -->
257+
<!-- VPU Devices -->
258258
<div class="card mb-3">
259259
<div class="card-header">
260-
<i class="material-icons md-18">info</i> System Information
260+
<i class="material-icons md-18">memory</i> VPU Devices
261+
<span class="text-muted small ml-2">
262+
<?php echo htmlspecialchars($quadraSummary['timestamp'] ?: '') ?>
263+
<?php if ($quadraSummary['uptime']) echo '&mdash; up '.htmlspecialchars($quadraSummary['uptime']) ?>
264+
</span>
261265
</div>
262-
<div class="card-body">
263-
<div class="row">
264-
<div class="col-md-3">
265-
<strong>Timestamp:</strong> <?php echo htmlspecialchars($quadraSummary['timestamp'] ?: 'N/A') ?>
266-
</div>
267-
<div class="col-md-3">
268-
<strong>Uptime:</strong> <?php echo htmlspecialchars($quadraSummary['uptime'] ?: 'N/A') ?>
269-
</div>
270-
<div class="col-md-3">
271-
<strong>Cards detected:</strong> <?php echo count($cards) ?>
272-
</div>
273-
</div>
266+
<div class="card-body table-responsive">
267+
<table class="table table-sm table-hover">
268+
<thead class="text-left">
269+
<tr>
270+
<th>Device</th>
271+
<th>Firmware</th>
272+
<th>PCIe Address</th>
273+
<th>NUMA</th>
274+
</tr>
275+
</thead>
276+
<tbody>
277+
<?php foreach ($cards as $cardIdx => $card): ?>
278+
<tr>
279+
<td><?php echo htmlspecialchars($card['device']) ?></td>
280+
<td><?php echo htmlspecialchars($card['firmware']) ?></td>
281+
<td><?php echo htmlspecialchars($card['pcie_addr']) ?></td>
282+
<td><?php echo htmlspecialchars($card['numa_node']) ?></td>
283+
</tr>
284+
<?php endforeach; ?>
285+
</tbody>
286+
</table>
274287
</div>
275288
</div>
276289

@@ -287,15 +300,16 @@ function formatBitrate($bps) {
287300
</div>
288301
<div class="card-body">
289302

290-
<!-- Resource Utilization -->
303+
<!-- VPU Usage Chart -->
291304
<?php
292305
$hasResources = false;
293306
foreach ($resourceSections as $key => $meta) {
294307
if (!empty($card['summary'][$key])) { $hasResources = true; break; }
295308
}
296309
if ($hasResources):
297310
?>
298-
<h6><i class="material-icons md-18">speed</i> Resource Utilization</h6>
311+
<h6><i class="material-icons md-18">speed</i> VPU Usage</h6>
312+
<div id="chart-card-<?php echo $cardIdx ?>" style="width:100%;height:<?php echo 60 + 40 * count(array_filter($resourceSections, function($k) use ($card) { return !empty($card['summary'][$k]); }, ARRAY_FILTER_USE_KEY)) ?>px;" class="mb-3"></div>
299313
<div class="table-responsive mb-3">
300314
<table class="table table-sm table-striped table-hover">
301315
<thead class="thead-highlight text-left">
@@ -494,4 +508,33 @@ function formatBitrate($bps) {
494508
<?php endif; ?>
495509
</div><!-- content -->
496510
</div>
511+
<?php
512+
// Pass chart data to JS
513+
if ($hasData):
514+
$chartData = [];
515+
foreach ($cards as $cardIdx => $card) {
516+
$resources = [];
517+
foreach ($resourceSections as $key => $meta) {
518+
if (empty($card['summary'][$key])) continue;
519+
$entry = $card['summary'][$key][0];
520+
$totalMem = intval($entry['MEM'] ?? 0) + intval($entry['CRITICAL_MEM'] ?? 0) + intval($entry['SHARE_MEM'] ?? 0);
521+
$resources[] = [
522+
'label' => $meta['label'],
523+
'load' => intval($entry['LOAD'] ?? 0),
524+
'mem' => $totalMem > 0 ? intval($entry['MEM'] ?? 0) : 0,
525+
];
526+
}
527+
if (!empty($resources)) {
528+
$chartData[] = [
529+
'id' => "chart-card-{$cardIdx}",
530+
'resources' => $resources,
531+
];
532+
}
533+
}
534+
?>
535+
<?php echo output_script_if_exists(array('assets/echarts-6.0.0/echarts.min.js'), false) ?>
536+
<script nonce="<?php echo $cspNonce ?>">
537+
var quadraChartData = <?php echo json_encode($chartData) ?>;
538+
</script>
539+
<?php endif; ?>
497540
<?php xhtmlFooter() ?>

0 commit comments

Comments
 (0)