diff options
author | Udo Walter | 2019-01-22 22:16:32 +0100 |
---|---|---|
committer | Udo Walter | 2019-01-22 22:16:32 +0100 |
commit | 724efa2e57565a78a76a7a341927af54e2bd237c (patch) | |
tree | ce6133df9829ddf08c7496da4d01ea0bdf7a5529 | |
parent | rework new_status-dnbd3.html with vuejs instead of jquery (diff) | |
download | dnbd3-status-724efa2e57565a78a76a7a341927af54e2bd237c.tar.gz dnbd3-status-724efa2e57565a78a76a7a341927af54e2bd237c.tar.xz dnbd3-status-724efa2e57565a78a76a7a341927af54e2bd237c.zip |
Add server speeds graph, option to show peak speeds and some design changes
Options/query strings: 'peak' to turn on peak speed instead of average speed
'timespan' the timespan in which to calculate the average and peak speeds (default: 120) (seconds)
-rw-r--r-- | static/new_status-dnbd3.html | 170 |
1 files changed, 131 insertions, 39 deletions
diff --git a/static/new_status-dnbd3.html b/static/new_status-dnbd3.html index b4049bb..cb26cba 100644 --- a/static/new_status-dnbd3.html +++ b/static/new_status-dnbd3.html @@ -4,16 +4,32 @@ <head> <style type="text/css"> - body { margin: 0; } + html, body { + height: 100%; + margin: 0; + } + + #app { + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; + } - .container { + .server-container { display: flex; + justify-content: center; + overflow-y: auto; } .server { border-spacing: 5px; background-color: #eee; border: 1px solid #666; + flex: 1; + white-space: nowrap; + max-width: 360px; + margin: 5px; } .first-row { @@ -28,9 +44,9 @@ border: 1px solid black; text-align: center; } - - .client-speed { - width: 240px; + + .expand { + width: 99%; } </style> @@ -38,67 +54,112 @@ <body> <div id="app"> - <div class="container"> - <table v-for="server in servers" class="server"> + <div class="server-container"> + <table v-for="(server, index) in servers" class="server" :style="{ border: '2px solid ' + GRAPH_COLORS[index] }"> <tbody> - <tr class="first-row"><td>Server IP:</td><td class="server-ip">{{ server.address }}</td></tr> - <tr><td>Uptime:</td><td>{{ formatSeconds(server.uptime) }}</td></tr> - <tr><td>Upload speed:</td><td>{{ formatBytes(server.uploadSpeed) +'/s' }}</td></tr> - <tr><td>Download speed:</td><td>{{ formatBytes(server.downloadSpeed) +'/s' }}</td></tr> + <tr class="first-row"><td colspan="2" class="server-ip">{{ server.address }}</td></tr> + <tr><td>Uptime:</td><td class="expand">{{ formatSeconds(server.uptime) }}</td></tr> + <tr><td>Upload speed:</td><td>{{ formatBytes(peak ? server.peakUploadSpeed : server.avgUploadSpeed) +'/s' }}</td></tr> + <tr><td>Download speed:</td><td>{{ formatBytes(peak ? server.peakDownloadSpeed : server.avgDownloadSpeed) +'/s' }}</td></tr> <tr><td>Total sent:</td><td>{{ formatBytes(server.bytesSent) }}</td></tr> <tr><td>Total received:</td><td>{{ formatBytes(server.bytesReceived) }}</td></tr> + <tr><td>Client count:</td><td>{{ server.clientCount }}</td></tr> <tr v-for="client in server.clients"> <td class="client-label">{{ client.address.split( ":" )[0] }}</td> - <td class="client-speed" :style="calcBackgroundStyle(client.uploadSpeed)"><span>{{ formatBytes(client.uploadSpeed) + '/s' }}</span></td> + <td class="client-speed" :style="calcBackgroundStyle(peak ? client.peakUploadSpeed : client.avgUploadSpeed)"> + <span>{{ formatBytes(peak ? client.peakUploadSpeed : client.avgUploadSpeed) + '/s' }}</span> + </td> </tr> </tbody> </table> </div> + <canvas ref="chart" :width="this.canvasWidth"></canvas> </div> <script src="vue.min.js"></script> +<script src="smoothie.js"></script> + <script> var app = new Vue({ el: '#app', data: { - data: {}, - log: { server: {}, client: {} }, - timespan: 120 + rawData: {}, + servers: [], + logs: {}, + timespan: 120, + peak: false, + canvasWidth: 0, + smoothie: null, + graphLines: {}, + GRAPH_COLORS: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"] }, - computed: { - servers () { - if (!this.data.servers) return [] - const servers = this.data.servers.slice(0) - servers.sort((a, b) => b.clients.length - a.clients.length) - servers.forEach(server => { - this.calcSpeed('server', server) - server.clients.forEach(client => { - this.calcSpeed('client', client, server.timestamp) + watch: { + rawData () { + if (this.rawData.servers) { + const servers = this.rawData.servers.slice(0) + servers.sort(this.compareObjectIps) + const currentTime = new Date().getTime() + servers.forEach((server, index) => { + this.calcSpeed(server) + + // Update the graph + if (!this.graphLines[server.address]) { + Vue.set(this.graphLines, server.address, new TimeSeries({ logarithmicScale: true })) + this.smoothie.addTimeSeries(this.graphLines[server.address], { lineWidth: 2, strokeStyle: this.GRAPH_COLORS[index] }) + } + this.graphLines[server.address].append(currentTime, server.uploadSpeed) + + // Update clients + server.clients.forEach(client => { + client.timestamp = server.timestamp + this.calcSpeed(client) + }) + if (this.peak) { + server.clients = server.clients.filter(client => client.peakUploadSpeed > 0) + server.clients.sort((a, b) => b.peakUploadSpeed - a.peakUploadSpeed) + } else { + server.clients = server.clients.filter(client => client.avgUploadSpeed > 0) + server.clients.sort((a, b) => b.avgUploadSpeed - a.avgUploadSpeed) + } }) - server.clients = server.clients.filter(client => client.uploadSpeed > 0) - server.clients.sort((a, b) => b.uploadSpeed - a.uploadSpeed) - }) - return servers + this.servers = servers + } } }, methods: { async updateData () { const response = await fetch('/data2.json') - this.data = await response.json() + this.rawData = await response.json() setTimeout(this.updateData, 2000) }, - calcSpeed (type, obj, timestamp) { - var log = this.log[type][obj.address] - log = this.log[type][obj.address] = log ? log.filter(x => x.timestamp > this.data.timestamp - this.timespan * 1000) : [] - if (timestamp) obj.timestamp = timestamp - log.push(obj) - if (log.length > 0) { - const a = log[0] + calcSpeed (obj) { + // Create a log for this ip if it doesn't exist already + if(!this.logs[obj.address]) Vue.set(this.logs, obj.address, []) + // Remove outdated values + this.logs[obj.address] = this.logs[obj.address].filter(x => x.timestamp > this.rawData.timestamp - this.timespan * 1000) + // Get the all the old values accumulated over the timespan + const log = this.logs[obj.address] + // Add new values to the log + if (log.length === 0 || obj.timestamp > log[log.length - 1].timestamp) log.push(obj) + + if (log.length > 1) { + // Calculate current speeds + var a = log[log.length - 2] const b = log[log.length - 1] - const time = (b.timestamp - a.timestamp) / 1000 + var time = (b.timestamp - a.timestamp) / 1000 obj.uploadSpeed = (b.bytesSent - a.bytesSent) / time - if (type === 'server') obj.downloadSpeed = (b.bytesReceived - a.bytesReceived) / time + obj.downloadSpeed = (b.bytesReceived - a.bytesReceived) / time + + // Calculate peak speeds + obj.peakUploadSpeed = Math.max(...log.map(x => x.uploadSpeed || 0)) + obj.peakDownloadSpeed = Math.max(...log.map(x => x.downloadSpeed || 0)) + + // Calculate average speeds + a = log[0] + time = (b.timestamp - a.timestamp) / 1000 + obj.avgUploadSpeed = (b.bytesSent - a.bytesSent) / time + obj.avgDownloadSpeed = (b.bytesReceived - a.bytesReceived) / time } }, formatBytes (bytes) { @@ -114,18 +175,49 @@ var app = new Vue({ + Math.floor(seconds / 60 ) % 60 + 'min' ) }, calcBackgroundStyle (speed) { - const percent = Math.round(Math.max(0, Math.min(100, Math.log(1 + speed * (100000 / 1073741824)) / Math.log(100000) * 100))) + const percent = Math.round(Math.max(0, Math.min(100, this.logScale(speed) / this.logScale(262144000) * 100))) var color = '#056d00' if (speed < 1048576) color = '#8adb8b' else if (speed < 10485760) color = '#5cdb55' else if (speed < 104857600) color = '#26a81f' return { background: `linear-gradient(90deg, ${color} ${percent}%, #e0e0e0 ${percent}%)` } + }, + compareObjectIps (obj1, obj2) { + const parts1 = obj1.address.split('.').map(str => parseInt(str)) + const parts2 = obj2.address.split('.').map(str => parseInt(str)) + var result = parts1[0] - parts2[0] + if (result === 0) result = parts1[1] - parts2[1] + if (result === 0) result = parts1[2] - parts2[2] + if (result === 0) result = parts1[3] - parts2[3] + return result + }, + logScale (value) { + return Math.log(value/100000 + 1) } }, created () { const urlParams = new URLSearchParams(window.location.search) this.timespan = parseInt(urlParams.get('timespan')) || 120 + this.peak = urlParams.get('peak') === 'true' || urlParams.get('peak') === '' this.updateData() + }, + mounted () { + this.canvasWidth = window.innerWidth + window.addEventListener('resize', () => { + this.canvasWidth = window.innerWidth + }) + this.smoothie = new SmoothieChart({ + 'millisPerPixel': 300, + 'grid': { 'millisPerLine': 30000 }, + timestampFormatter: SmoothieChart.timeFormatter, + yMaxFormatter: this.formatBytes, + yMinFormatter: this.formatBytes, + valueTransformFunction: this.logScale, + minValue: 0, + maxValue: 262144000, + labels: { fontSize: 13 } + }) + this.smoothie.streamTo(this.$refs.chart, 2000) } }) |