mirror of
https://github.com/asticode/go-astiencoder.git
synced 2025-12-24 13:57:53 +08:00
Added stats section to web
This commit is contained in:
130
web/index.css
130
web/index.css
@@ -23,60 +23,58 @@ header {
|
||||
background-color: #333333;
|
||||
border-bottom: solid 1px #000000;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
height: auto;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header #left {
|
||||
width: 74%
|
||||
}
|
||||
|
||||
header #left #top {
|
||||
header #top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
header #left #search {
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
padding: 7px 10px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
header #left #top i {
|
||||
header #top i {
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
margin-left: 10px;
|
||||
display: none;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
header #left #tags>div {
|
||||
body.nodes header #top #show-stats,
|
||||
body.nodes header #top #zoom-out,
|
||||
body.nodes header #top #zoom-auto,
|
||||
body.nodes header #top #zoom-in,
|
||||
body.nodes header #top #toggle-settings {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body.stats header #top #show-nodes,
|
||||
body.stats header #top #toggle-settings {
|
||||
display: block;
|
||||
}
|
||||
|
||||
header #settings {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header #settings #tags>div {
|
||||
background-color: #555555;
|
||||
border: solid 1px #777777;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
float: left;
|
||||
padding: 5px 8px;
|
||||
margin: 10px 10px 0 0;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
header #left #tags>div:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
header #left #tags>div:first-child {
|
||||
header #settings #tags>div:first-child {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header #left #tags .action:hover span {
|
||||
header #settings #tags .action:hover span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
header #left #tags .fa {
|
||||
header #settings #tags .fa {
|
||||
cursor: pointer;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
@@ -84,29 +82,19 @@ header #left #tags .fa {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
header #left #tags .fa.active,
|
||||
header #tags .fa:hover {
|
||||
header #settings #tags .fa.active,
|
||||
header #settings #tags .fa:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
header #left #tags>div>.fa:first-child {
|
||||
header #settings #tags>div>.fa:first-child {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
header #left #tags>div>.fa:last-child {
|
||||
header #settings #tags>div>.fa:last-child {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
header #right {
|
||||
padding-left: 10px;
|
||||
width: 26%;
|
||||
}
|
||||
|
||||
header #right>div {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
section {
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
@@ -123,6 +111,18 @@ section::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section>div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.nodes section #nodes {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body.stats section #stats {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
section #nodes {
|
||||
margin: 1em 0;
|
||||
position: relative;
|
||||
@@ -141,7 +141,7 @@ section #nodes svg line {
|
||||
stroke: #000000;
|
||||
}
|
||||
|
||||
section .node {
|
||||
section #nodes .node {
|
||||
align-items: center;
|
||||
border: solid 0.1em transparent;
|
||||
border-radius: 0.3em;
|
||||
@@ -156,45 +156,74 @@ section #nodes>div:last-child>div {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
section .node.paused {
|
||||
section #nodes .node.paused {
|
||||
color: #856404;
|
||||
background-color: #fff3cd;
|
||||
border-color: #ffeeba;
|
||||
}
|
||||
|
||||
section .node.running {
|
||||
section #nodes .node.running {
|
||||
color: #155724;
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
}
|
||||
|
||||
section .node.stopped {
|
||||
section #nodes .node.stopped {
|
||||
color: #721c24;
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
}
|
||||
|
||||
section .node .label {
|
||||
section #nodes .node .label {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
section .node .name {
|
||||
section #nodes .node .name {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
section .node .stats {
|
||||
section #nodes .node .stats {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
section .node .stats td {
|
||||
section #nodes .node .stats td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
section .node .stats td:first-child {
|
||||
section #nodes .node .stats td:first-child {
|
||||
padding-right: 0.3em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
section #stats {
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
section #stats .table .title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
section #stats .table table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
section #stats .table table tr,
|
||||
section #stats .table table th,
|
||||
section #stats .table table td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
section #stats .table table th,
|
||||
section #stats .table table td {
|
||||
border: solid 1px #000;
|
||||
padding: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #333333;
|
||||
border-top: solid 1px #000000;
|
||||
@@ -254,5 +283,6 @@ footer.recording-loaded>#recording-time {
|
||||
}
|
||||
|
||||
footer>div:last-child input[type="range"] {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -4,49 +4,30 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Astiencoder</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div id="left">
|
||||
<div id="top">
|
||||
<input id="search" type="text" placeholder="Search" oninput="astiencoder.onSearch(event)"
|
||||
autocomplete="off">
|
||||
<i class="fa fa-search-minus" onclick="astiencoder.zoomOut()"></i>
|
||||
<i class="fa fa-expand-arrows-alt" onclick="astiencoder.zoomAuto()"></i>
|
||||
<i class="fa fa-search-plus" onclick="astiencoder.zoomIn()"></i>
|
||||
</div>
|
||||
<div id="top">
|
||||
<i id="show-stats" class="fa fa-table" onclick="astiencoder.setSection('stats')"></i>
|
||||
<i id="show-nodes" class="fa fa-share-nodes" onclick="astiencoder.setSection('nodes')"></i>
|
||||
<i id="zoom-out" class="fa fa-search-minus" onclick="astiencoder.zoomOut()"></i>
|
||||
<i id="zoom-auto" class="fa fa-expand-arrows-alt" onclick="astiencoder.zoomAuto()"></i>
|
||||
<i id="zoom-in" class="fa fa-search-plus" onclick="astiencoder.zoomIn()"></i>
|
||||
<i id="toggle-settings" class="fa fa-gear" onclick="astiencoder.toggleSettings()"></i>
|
||||
</div>
|
||||
<div id="settings">
|
||||
<div id="tags">
|
||||
<div class="action" onclick="astiencoder.onResetAllTags()">
|
||||
<span>Reset all</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="right">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Process memory</td>
|
||||
<td><span id="memory-resident"></span>/<span id="memory-virtual"></span>GB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total memory</td>
|
||||
<td><span id="memory-used"></span>/<span id="memory-total"></span>GB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Process CPU</td>
|
||||
<td><span id="cpu-process"></span>%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total CPU</td>
|
||||
<td><span id="cpu-total"></span>%</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="cpus"></div>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
<div id="stats"></div>
|
||||
<div id="nodes"></div>
|
||||
</section>
|
||||
<footer>
|
||||
@@ -62,7 +43,7 @@
|
||||
<div id="recording-time"></div>
|
||||
<div id="recording-progress">
|
||||
<input min="0" max="100" type="range" onchange="astiencoder.onRecordingSeek(event)"
|
||||
onkeydown="event.preventDefault()" />
|
||||
oninput="astiencoder.onRecordingPreviewTime(event)" onkeydown="event.preventDefault()" />
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
552
web/index.js
552
web/index.js
@@ -7,8 +7,8 @@ var astiencoder = {
|
||||
// Get params
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
|
||||
// Init nodes position refresher
|
||||
this.initNodesPositionRefresher()
|
||||
// Show default section
|
||||
this.setSection('nodes')
|
||||
|
||||
// Get urls
|
||||
this.websocketUrl = params.get("websocket_url")
|
||||
@@ -45,8 +45,8 @@ var astiencoder = {
|
||||
|
||||
// No workflow or no nodes
|
||||
if (!data.workflow || data.workflow.nodes.length === 0) {
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ var astiencoder = {
|
||||
this.apply(item.name, item)
|
||||
}.bind(this))
|
||||
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
}.bind(this)
|
||||
})
|
||||
},
|
||||
@@ -163,22 +163,22 @@ var astiencoder = {
|
||||
// Apply
|
||||
this.apply(payload.parent, { children: [payload.child] })
|
||||
|
||||
// Refresh nodes position
|
||||
if (!recording) this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
if (!recording) this.refreshSection()
|
||||
break
|
||||
case 'astiencoder.node.child.removed':
|
||||
// Apply
|
||||
this.apply(payload.parent, { childrenRemoved: [payload.child] })
|
||||
|
||||
// Refresh nodes position
|
||||
if (!recording) this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
if (!recording) this.refreshSection()
|
||||
break
|
||||
case 'astiencoder.node.closed':
|
||||
// Apply
|
||||
this.apply(payload, { closed: true })
|
||||
|
||||
// Refresh nodes position
|
||||
if (!recording) this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
if (!recording) this.refreshSection()
|
||||
break
|
||||
case 'astiencoder.node.continued':
|
||||
this.apply(payload, { status: 'running' })
|
||||
@@ -190,15 +190,15 @@ var astiencoder = {
|
||||
// Apply
|
||||
this.apply(payload.name, payload)
|
||||
|
||||
// Refresh nodes position
|
||||
if (!recording) this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
if (!recording) this.refreshSection()
|
||||
break
|
||||
case 'astiencoder.node.stopped':
|
||||
// Apply
|
||||
this.apply(payload, { status: 'stopped' })
|
||||
|
||||
// Refresh nodes position
|
||||
if (!recording) this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
if (!recording) this.refreshSection()
|
||||
break
|
||||
case 'astiencoder.stats':
|
||||
// Apply
|
||||
@@ -206,8 +206,8 @@ var astiencoder = {
|
||||
this.apply(stat.target, { stat: stat })
|
||||
})
|
||||
|
||||
// Refresh nodes size
|
||||
if (!recording) this.refreshNodesSize()
|
||||
// Refresh section
|
||||
if (!recording) this.refreshSection()
|
||||
break
|
||||
}
|
||||
return rollbacks
|
||||
@@ -271,6 +271,17 @@ var astiencoder = {
|
||||
return false
|
||||
},
|
||||
|
||||
/* section */
|
||||
setSection(name) {
|
||||
document.querySelector('body').className = name
|
||||
this.sectionName = name
|
||||
this.refreshSection()
|
||||
},
|
||||
refreshSection() {
|
||||
if (this.sectionName === 'nodes') this.refreshNodesPosition()
|
||||
else if (this.sectionName === 'stats') this.refreshStats()
|
||||
},
|
||||
|
||||
/* workflow */
|
||||
workflow: {
|
||||
name: '',
|
||||
@@ -282,13 +293,8 @@ var astiencoder = {
|
||||
// Switch on prop
|
||||
switch (prop) {
|
||||
case 'astiencoder.host.usage':
|
||||
document.getElementById("memory-virtual").innerText = ''
|
||||
document.getElementById("memory-resident").innerText = ''
|
||||
document.getElementById("memory-used").innerText = ''
|
||||
document.getElementById("memory-total").innerText = ''
|
||||
document.getElementById("cpu-process").innerText = ''
|
||||
document.getElementById("cpu-total").innerText = ''
|
||||
document.getElementById("cpus").innerText = ''
|
||||
// Refresh stats
|
||||
astiencoder.refreshStats()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -302,19 +308,8 @@ var astiencoder = {
|
||||
// Switch on prop
|
||||
switch (prop) {
|
||||
case 'astiencoder.host.usage':
|
||||
if (value.value.memory.resident) document.getElementById("memory-resident").innerText = (value.value.memory.resident / Math.pow(1024, 3)).toFixed(2)
|
||||
if (value.value.memory.virtual) document.getElementById("memory-virtual").innerText = (value.value.memory.virtual / Math.pow(1024, 3)).toFixed(2)
|
||||
if (value.value.memory.used) document.getElementById("memory-used").innerText = (value.value.memory.used / Math.pow(1024, 3)).toFixed(2)
|
||||
if (value.value.memory.total) document.getElementById("memory-total").innerText = (value.value.memory.total / Math.pow(1024, 3)).toFixed(2)
|
||||
if (value.value.cpu.process) document.getElementById("cpu-process").innerText = value.value.cpu.process.toFixed(2)
|
||||
if (value.value.cpu.total) document.getElementById("cpu-total").innerText = value.value.cpu.total.toFixed(2)
|
||||
if (value.value.cpu.individual) {
|
||||
var e = document.getElementById("cpus")
|
||||
e.innerHTML = ""
|
||||
for (var idx = 0; idx < value.value.cpu.individual.length; idx++) {
|
||||
e.innerHTML += "<div>#" + (idx + 1) + ": " + value.value.cpu.individual[idx].toFixed(2) + "%</div>"
|
||||
}
|
||||
}
|
||||
// Refresh stats
|
||||
astiencoder.refreshStats()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -404,7 +399,7 @@ var astiencoder = {
|
||||
updateTime(t) {
|
||||
this.currentTime = t
|
||||
document.querySelector('#recording-progress > input').value = ((t.getTime() - this.from.getTime()) / this.duration) * 100
|
||||
document.querySelector('#recording-time').innerText = t.getHours().toString().padStart(2, '0') + ':' + t.getMinutes().toString().padStart(2, '0') + ':' + t.getSeconds().toString().padStart(2, '0')
|
||||
astiencoder.updateRecordingTime(t)
|
||||
},
|
||||
next() {
|
||||
// No nexts
|
||||
@@ -462,6 +457,9 @@ var astiencoder = {
|
||||
return true
|
||||
}
|
||||
}),
|
||||
updateRecordingTime(t) {
|
||||
document.querySelector('#recording-time').innerText = t.getHours().toString().padStart(2, '0') + ':' + t.getMinutes().toString().padStart(2, '0') + ':' + t.getSeconds().toString().padStart(2, '0')
|
||||
},
|
||||
onRecordingLoadClick() {
|
||||
document.querySelector('#recording-load input').click()
|
||||
},
|
||||
@@ -584,8 +582,8 @@ var astiencoder = {
|
||||
// Reset
|
||||
this.reset()
|
||||
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
|
||||
// On open
|
||||
if (this.websocketUrl && this.welcomeUrl) this.onopen()
|
||||
@@ -597,8 +595,8 @@ var astiencoder = {
|
||||
// Apply next
|
||||
this.recording.applyNext()
|
||||
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
},
|
||||
onRecordingPreviousClick() {
|
||||
// No recording
|
||||
@@ -607,8 +605,8 @@ var astiencoder = {
|
||||
// Apply previous
|
||||
this.recording.applyPrevious()
|
||||
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
},
|
||||
onRecordingSeek(e) {
|
||||
// No recording
|
||||
@@ -644,8 +642,18 @@ var astiencoder = {
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
},
|
||||
onRecordingPreviewTime(e) {
|
||||
// No recording
|
||||
if (!this.recording.loaded) return
|
||||
|
||||
// Get seek time
|
||||
const t = new Date(e.target.value / 100 * this.recording.duration + this.recording.from.getTime())
|
||||
|
||||
// Update recording time
|
||||
this.updateRecordingTime(t)
|
||||
},
|
||||
|
||||
/* tags */
|
||||
@@ -741,8 +749,8 @@ var astiencoder = {
|
||||
this.refreshTagsForNode(name)
|
||||
}
|
||||
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
},
|
||||
refreshTagsForNode(name) {
|
||||
// Index tags
|
||||
@@ -783,7 +791,6 @@ var astiencoder = {
|
||||
var n = {
|
||||
_key: value.label,
|
||||
dom: {},
|
||||
notInSearch: false,
|
||||
notInTags: false
|
||||
}
|
||||
|
||||
@@ -889,10 +896,10 @@ var astiencoder = {
|
||||
case 'label':
|
||||
_c1.innerText = value + ':'
|
||||
break
|
||||
case 'unit':
|
||||
case 'funit':
|
||||
_u.innerText = value
|
||||
break
|
||||
case 'value':
|
||||
case 'fvalue':
|
||||
_v.innerText = value
|
||||
break
|
||||
}
|
||||
@@ -927,7 +934,7 @@ var astiencoder = {
|
||||
|
||||
// Methods
|
||||
n.displayed = function () {
|
||||
return !this.notInTags && !this.notInSearch
|
||||
return !this.notInTags
|
||||
}
|
||||
|
||||
// Store node
|
||||
@@ -1062,35 +1069,45 @@ var astiencoder = {
|
||||
if (payload.stat.unit) this.nodes[name].stats[payload.stat.label].unit = payload.stat.unit
|
||||
|
||||
// Value
|
||||
if (typeof payload.stat.value !== 'undefined') this.nodes[name].stats[payload.stat.label].value = payload.stat.value
|
||||
|
||||
// Formatted
|
||||
if (typeof payload.stat.value !== 'undefined') {
|
||||
// Update formatted unit and value
|
||||
this.nodes[name].stats[payload.stat.label].funit = payload.stat.unit
|
||||
this.nodes[name].stats[payload.stat.label].fvalue = payload.stat.value
|
||||
|
||||
// Get value
|
||||
var v = payload.stat.value
|
||||
|
||||
// Parse value
|
||||
var f = parseFloat(v)
|
||||
if (!isNaN(f)) {
|
||||
switch (this.nodes[name].stats[payload.stat.label].unit) {
|
||||
case 'Bps':
|
||||
f *= 8
|
||||
this.nodes[name].stats[payload.stat.label].unit = 'bps'
|
||||
this.nodes[name].stats[payload.stat.label].funit = 'bps'
|
||||
if (f > 1e9) {
|
||||
f /= 1e9
|
||||
this.nodes[name].stats[payload.stat.label].unit = 'Gbps'
|
||||
this.nodes[name].stats[payload.stat.label].funit = 'Gbps'
|
||||
} else if (f > 1e6) {
|
||||
f /= 1e6
|
||||
this.nodes[name].stats[payload.stat.label].unit = 'Mbps'
|
||||
this.nodes[name].stats[payload.stat.label].funit = 'Mbps'
|
||||
} else if (f > 1e3) {
|
||||
f /= 1e3
|
||||
this.nodes[name].stats[payload.stat.label].unit = 'kbps'
|
||||
this.nodes[name].stats[payload.stat.label].funit = 'kbps'
|
||||
}
|
||||
break
|
||||
case 'ns':
|
||||
if (f > 1e9 || f < -1e9) {
|
||||
f /= 1e9
|
||||
this.nodes[name].stats[payload.stat.label].unit = 's'
|
||||
this.nodes[name].stats[payload.stat.label].funit = 's'
|
||||
} else if (f > 1e6 || f < -1e6) {
|
||||
f /= 1e6
|
||||
this.nodes[name].stats[payload.stat.label].unit = 'ms'
|
||||
this.nodes[name].stats[payload.stat.label].funit = 'ms'
|
||||
} else if (f > 1e3 || f < -1e3) {
|
||||
f /= 1e3
|
||||
this.nodes[name].stats[payload.stat.label].unit = 'µs'
|
||||
this.nodes[name].stats[payload.stat.label].funit = 'µs'
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -1102,7 +1119,7 @@ var astiencoder = {
|
||||
else if (f <= -1000) f = '-∞'
|
||||
}
|
||||
}
|
||||
this.nodes[name].stats[payload.stat.label].value = f
|
||||
this.nodes[name].stats[payload.stat.label].fvalue = f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1119,105 +1136,103 @@ var astiencoder = {
|
||||
},
|
||||
|
||||
/* nodes position refresher */
|
||||
initNodesPositionRefresher() {
|
||||
this.nodesPositionRefresher = {
|
||||
a: new Uint8Array(1),
|
||||
f: function () {
|
||||
// Reset
|
||||
document.getElementById('nodes').innerHTML = ''
|
||||
nodesPositionRefresher: {
|
||||
a: new Uint8Array(1),
|
||||
f: function () {
|
||||
// Reset
|
||||
document.getElementById('nodes').innerHTML = ''
|
||||
|
||||
// Create svg
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||
document.getElementById('nodes').appendChild(svg)
|
||||
// Create svg
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||
document.getElementById('nodes').appendChild(svg)
|
||||
|
||||
// Get levels
|
||||
var processedNodes = {}, total = Object.keys(this.nodes).length, levels = []
|
||||
while (Object.keys(processedNodes).length < total) {
|
||||
// Loop through nodes
|
||||
var level = [], tempLevel = {}
|
||||
for (var name in this.nodes) {
|
||||
// Already processed
|
||||
if (processedNodes[name]) continue
|
||||
// Get levels
|
||||
var processedNodes = {}, total = Object.keys(astiencoder.nodes).length, levels = []
|
||||
while (Object.keys(processedNodes).length < total) {
|
||||
// Loop through nodes
|
||||
var level = [], tempLevel = {}
|
||||
for (var name in astiencoder.nodes) {
|
||||
// Already processed
|
||||
if (processedNodes[name]) continue
|
||||
|
||||
// There are no levels yet, we check nodes with no parents
|
||||
if (levels.length === 0) {
|
||||
if (Object.keys(this.nodes[name].parents).length === 0) level.push(this.nodes[name])
|
||||
continue
|
||||
}
|
||||
|
||||
// Loop through previous level nodes
|
||||
levels[levels.length - 1].forEach(item => {
|
||||
if (this.nodes[name].parents[item.name]) tempLevel[name] = this.nodes[name]
|
||||
})
|
||||
// There are no levels yet, we check nodes with no parents
|
||||
if (levels.length === 0) {
|
||||
if (Object.keys(astiencoder.nodes[name].parents).length === 0) level.push(astiencoder.nodes[name])
|
||||
continue
|
||||
}
|
||||
|
||||
// Move children in the same zone as their parent
|
||||
if (levels.length > 0 && Object.keys(tempLevel).length > 0) {
|
||||
// Loop through previous level nodes
|
||||
levels[levels.length - 1].forEach(item => {
|
||||
// Loop through children
|
||||
for (var k in item.children) {
|
||||
const c = tempLevel[k]
|
||||
if (c) {
|
||||
level.push(c)
|
||||
delete tempLevel[k]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// No nodes in level
|
||||
// This shouldn't happen but we want to avoid infinite loops
|
||||
if (level.length === 0) break
|
||||
|
||||
// Append level
|
||||
levels.push(level)
|
||||
level.forEach(node => processedNodes[node.name] = true)
|
||||
// Loop through previous level nodes
|
||||
levels[levels.length - 1].forEach(item => {
|
||||
if (astiencoder.nodes[name].parents[item.name]) tempLevel[name] = astiencoder.nodes[name]
|
||||
})
|
||||
}
|
||||
|
||||
// Loop through levels
|
||||
levels.forEach(level => {
|
||||
// Create wrapper
|
||||
const lw = document.createElement('div')
|
||||
|
||||
// Loop through level items
|
||||
level.forEach(item => {
|
||||
// Node is not displayed
|
||||
if (!item.displayed()) return
|
||||
|
||||
// Append wrapper
|
||||
lw.appendChild(item.dom.w)
|
||||
|
||||
// Move children in the same zone as their parent
|
||||
if (levels.length > 0 && Object.keys(tempLevel).length > 0) {
|
||||
// Loop through previous level nodes
|
||||
levels[levels.length - 1].forEach(item => {
|
||||
// Loop through children
|
||||
for (var c in item.children) {
|
||||
// Node is not displayed
|
||||
const n = this.nodes[c]
|
||||
if (!n || !n.displayed()) continue
|
||||
|
||||
// Append arrow
|
||||
svg.appendChild(item.children[c].arrow.line)
|
||||
svg.appendChild(item.children[c].arrow.head.line1)
|
||||
svg.appendChild(item.children[c].arrow.head.line2)
|
||||
for (var k in item.children) {
|
||||
const c = tempLevel[k]
|
||||
if (c) {
|
||||
level.push(c)
|
||||
delete tempLevel[k]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Append wrapper
|
||||
document.getElementById('nodes').appendChild(lw)
|
||||
})
|
||||
|
||||
// Refresh size
|
||||
this.refreshNodesSize()
|
||||
}.bind(this),
|
||||
i: setInterval(function () {
|
||||
// Nothing to do
|
||||
if (Atomics.compareExchange(this.nodesPositionRefresher.a, 0, 1, 0) === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh nodes position
|
||||
this.nodesPositionRefresher.f()
|
||||
}.bind(this), 50)
|
||||
}
|
||||
// No nodes in level
|
||||
// This shouldn't happen but we want to avoid infinite loops
|
||||
if (level.length === 0) break
|
||||
|
||||
// Append level
|
||||
levels.push(level)
|
||||
level.forEach(node => processedNodes[node.name] = true)
|
||||
}
|
||||
|
||||
// Loop through levels
|
||||
levels.forEach(level => {
|
||||
// Create wrapper
|
||||
const lw = document.createElement('div')
|
||||
|
||||
// Loop through level items
|
||||
level.forEach(item => {
|
||||
// Node is not displayed
|
||||
if (!item.displayed()) return
|
||||
|
||||
// Append wrapper
|
||||
lw.appendChild(item.dom.w)
|
||||
|
||||
// Loop through children
|
||||
for (var c in item.children) {
|
||||
// Node is not displayed
|
||||
const n = astiencoder.nodes[c]
|
||||
if (!n || !n.displayed()) continue
|
||||
|
||||
// Append arrow
|
||||
svg.appendChild(item.children[c].arrow.line)
|
||||
svg.appendChild(item.children[c].arrow.head.line1)
|
||||
svg.appendChild(item.children[c].arrow.head.line2)
|
||||
}
|
||||
})
|
||||
|
||||
// Append wrapper
|
||||
document.getElementById('nodes').appendChild(lw)
|
||||
})
|
||||
|
||||
// Refresh size
|
||||
astiencoder.refreshNodesSize()
|
||||
},
|
||||
i: setInterval(function () {
|
||||
// Nothing to do
|
||||
if (Atomics.compareExchange(astiencoder.nodesPositionRefresher.a, 0, 1, 0) === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh nodes position
|
||||
astiencoder.nodesPositionRefresher.f()
|
||||
}, 50)
|
||||
},
|
||||
refreshNodesPosition() {
|
||||
Atomics.store(this.nodesPositionRefresher.a, 0, 1)
|
||||
@@ -1426,19 +1441,6 @@ var astiencoder = {
|
||||
}
|
||||
},
|
||||
|
||||
/* search */
|
||||
onSearch(event) {
|
||||
// Loop through nodes
|
||||
for (var name in this.nodes) {
|
||||
this.nodes[name].notInSearch = event.target.value !== ''
|
||||
&& this.nodes[name].label.toLowerCase().search(event.target.value.toLowerCase()) === -1
|
||||
&& this.nodes[name].name.toLowerCase().search(event.target.value.toLowerCase()) === -1
|
||||
}
|
||||
|
||||
// Refresh nodes position
|
||||
this.refreshNodesPosition()
|
||||
},
|
||||
|
||||
/* zoom */
|
||||
zoom: {
|
||||
auto: true,
|
||||
@@ -1452,8 +1454,8 @@ var astiencoder = {
|
||||
this.zoom.auto = false
|
||||
this.zoom.value += 10
|
||||
|
||||
// Refresh nodes size
|
||||
this.refreshNodesSize()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
},
|
||||
zoomOut() {
|
||||
// No nodes
|
||||
@@ -1466,8 +1468,8 @@ var astiencoder = {
|
||||
this.zoom.auto = false
|
||||
this.zoom.value -= 10
|
||||
|
||||
// Refresh nodes size
|
||||
this.refreshNodesSize()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
},
|
||||
zoomAuto() {
|
||||
// No nodes
|
||||
@@ -1476,8 +1478,224 @@ var astiencoder = {
|
||||
// Update zoom
|
||||
this.zoom.auto = true
|
||||
|
||||
// Refresh nodes size
|
||||
this.refreshNodesSize()
|
||||
// Refresh section
|
||||
this.refreshSection()
|
||||
},
|
||||
|
||||
/* stats */
|
||||
refreshStats() {
|
||||
// Workflow stats
|
||||
let tables = []
|
||||
for (let name in this.workflow.stats) {
|
||||
const v = this.workflow.stats[name]
|
||||
switch (name) {
|
||||
case 'astiencoder.host.usage':
|
||||
// Create table
|
||||
let table = {
|
||||
label: 'Host',
|
||||
rows: [],
|
||||
}
|
||||
|
||||
// Add process memory
|
||||
if (v.value.memory.resident && v.value.memory.virtual) {
|
||||
table.rows.push([
|
||||
{ text: 'Process memory' },
|
||||
{ text: (v.value.memory.resident / Math.pow(1024, 3)).toFixed(2) + '/' + (v.value.memory.virtual / Math.pow(1024, 3)).toFixed(2) + 'GB' }
|
||||
])
|
||||
}
|
||||
|
||||
// Add total memory
|
||||
if (v.value.memory.used && v.value.memory.total) {
|
||||
table.rows.push([
|
||||
{ text: 'Total memory' },
|
||||
{ text: (v.value.memory.used / Math.pow(1024, 3)).toFixed(2) + '/' + (v.value.memory.total / Math.pow(1024, 3)).toFixed(2) + 'GB' }
|
||||
])
|
||||
}
|
||||
|
||||
// Process CPU
|
||||
if (v.value.cpu.process) {
|
||||
table.rows.push([
|
||||
{ text: 'Process CPU' },
|
||||
{ text: v.value.cpu.process.toFixed(2) + '%' }
|
||||
])
|
||||
}
|
||||
|
||||
// Total CPU
|
||||
if (v.value.cpu.total) {
|
||||
table.rows.push([
|
||||
{ text: 'Total CPU' },
|
||||
{ text: v.value.cpu.total.toFixed(2) + '%' }
|
||||
])
|
||||
}
|
||||
|
||||
// Individual CPUs
|
||||
if (v.value.cpu.individual) {
|
||||
for (var idx = 0; idx < v.value.cpu.individual.length; idx++) {
|
||||
table.rows.push([
|
||||
{ text: 'CPU #' + (idx + 1) },
|
||||
{ text: v.value.cpu.individual[idx].toFixed(2) + '%' }
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// Append table
|
||||
tables.push(table)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through nodes
|
||||
const nodeStats = []
|
||||
for (let nodeName in this.nodes) {
|
||||
// Get node
|
||||
const n = this.nodes[nodeName]
|
||||
|
||||
// Node not in tags
|
||||
if (n.notInTags) continue
|
||||
|
||||
// Loop through stats
|
||||
for (let statLabel in n.stats) {
|
||||
// Invalid status
|
||||
if (n.status !== 'running') continue
|
||||
|
||||
// Get stat
|
||||
const s = n.stats[statLabel]
|
||||
|
||||
// Get node stat
|
||||
let nodeStat = nodeStats.find(s => s.label === statLabel)
|
||||
if (!nodeStat) {
|
||||
nodeStat = {
|
||||
label: statLabel,
|
||||
nodes: [],
|
||||
}
|
||||
nodeStats.push(nodeStat)
|
||||
}
|
||||
|
||||
// Append node value
|
||||
nodeStat.nodes.push({
|
||||
funit: s.funit,
|
||||
fvalue: s.fvalue,
|
||||
label: n.label,
|
||||
name: n.name,
|
||||
value: s.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Order node stats
|
||||
for (let a = 0; a < nodeStats.length; a++) {
|
||||
nodeStats[a].nodes.sort((a, b) => {
|
||||
if (a.value < b.value) return 1
|
||||
if (a.value === b.value) return 0
|
||||
return -1
|
||||
})
|
||||
}
|
||||
|
||||
// Loop through node stats
|
||||
nodeStats.forEach(s => {
|
||||
// Create table
|
||||
let table = {
|
||||
label: s.label,
|
||||
rows: [],
|
||||
}
|
||||
|
||||
// Loop through nodes
|
||||
s.nodes.forEach(n => {
|
||||
// Append row
|
||||
table.rows.push([
|
||||
{
|
||||
text: n.name,
|
||||
tooltip: n.label,
|
||||
},
|
||||
{ text: n.fvalue + n.funit },
|
||||
])
|
||||
})
|
||||
|
||||
// Append table
|
||||
tables.push(table)
|
||||
})
|
||||
|
||||
// Order tables
|
||||
tables.sort((a, b) => {
|
||||
if (a.label < b.label) return -1
|
||||
if (a.label === b.label) return 0
|
||||
return 1
|
||||
})
|
||||
|
||||
// Get html node
|
||||
const n = document.querySelector('section #stats')
|
||||
|
||||
// Reset html
|
||||
n.innerHTML = ''
|
||||
|
||||
// Loop through tables
|
||||
tables.forEach(t => {
|
||||
// Create container
|
||||
const container = document.createElement('div')
|
||||
container.className = 'table'
|
||||
|
||||
// Create title
|
||||
const title = document.createElement('div')
|
||||
title.className = 'title'
|
||||
title.innerText = t.label
|
||||
container.appendChild(title)
|
||||
|
||||
// Create table
|
||||
const table = document.createElement('table')
|
||||
container.appendChild(table)
|
||||
|
||||
// Process headers
|
||||
if (t.headers) {
|
||||
// Create header
|
||||
const header = document.createElement('thead')
|
||||
table.appendChild(header)
|
||||
|
||||
// Create row
|
||||
const row = document.createElement('tr')
|
||||
header.appendChild(row)
|
||||
|
||||
// Loop throughheaders
|
||||
t.headers.forEach(h => {
|
||||
// Create column
|
||||
const column = document.createElement('th')
|
||||
row.appendChild(column)
|
||||
|
||||
// Update text
|
||||
column.innerText = h
|
||||
})
|
||||
}
|
||||
|
||||
// Create body
|
||||
const body = document.createElement('tbody')
|
||||
table.appendChild(body)
|
||||
|
||||
// Loop through rows
|
||||
t.rows.forEach(r => {
|
||||
// Create row
|
||||
const row = document.createElement('tr')
|
||||
body.appendChild(row)
|
||||
|
||||
// Loop through columns
|
||||
r.forEach(c => {
|
||||
// Create column
|
||||
const column = document.createElement('td')
|
||||
row.appendChild(column)
|
||||
|
||||
// Update column
|
||||
column.innerText = c.text
|
||||
if (c.tooltip) column.setAttribute('title', c.tooltip)
|
||||
})
|
||||
})
|
||||
|
||||
// Add container to node
|
||||
n.appendChild(container)
|
||||
})
|
||||
},
|
||||
|
||||
/* settings */
|
||||
toggleSettings() {
|
||||
document.querySelector('header #settings').style.display = this.settingsDisplayed ? 'none' : 'flex'
|
||||
this.settingsDisplayed = !this.settingsDisplayed
|
||||
},
|
||||
|
||||
/* helpers */
|
||||
|
||||
Reference in New Issue
Block a user