From 79aec147e3f960d4ce13c4c39a887d378d9049cb Mon Sep 17 00:00:00 2001 From: HardiReady Date: Sun, 10 Feb 2019 00:54:25 +0100 Subject: [PATCH] Add line chart visualizations for server performance (CC-80) --- server/models/player.js | 2 +- server/routes/wars.js | 48 +++++++---- .../server-stats/server-stats.component.html | 36 ++++---- .../server-stats/server-stats.component.ts | 86 ++++++++++++++++--- .../war/war-header/war-header.component.html | 5 +- 5 files changed, 127 insertions(+), 50 deletions(-) diff --git a/server/models/player.js b/server/models/player.js index 85bd6f3..da3f07d 100644 --- a/server/models/player.js +++ b/server/models/player.js @@ -85,7 +85,7 @@ const PlayerSchema = new Schema({ performance: { type: mongoose.Schema.Types.ObjectId, ref: 'LogServerFpsSchema', - required: true, + required: false, }, }, { collection: 'player', diff --git a/server/routes/wars.js b/server/routes/wars.js index 62984b4..0ec1ef9 100644 --- a/server/routes/wars.js +++ b/server/routes/wars.js @@ -74,25 +74,37 @@ wars.route('/') if (err) { return next(err); } - LogKillModel.create(statsResult.kills, () => { - LogVehicleKillModel.create(statsResult.vehicles, () => { - LogRespawnModel.create(statsResult.respawn, () => { - LogReviveModel.create(statsResult.revive, () => { - LogFlagModel.create(statsResult.flag, () => { - LogBudgetModel.create(statsResult.budget, () => { - LogTransportModel.create(statsResult.transport, () => { - LogPlayerCountModel.create(statsResult.playerCount, () => { - LogPointsModel.create(statsResult.points, () => { + LogKillModel.create(statsResult.kills, (err) => { + if (err) console.log(err); + LogVehicleKillModel.create(statsResult.vehicles, (err) => { + if (err) console.log(err); + LogRespawnModel.create(statsResult.respawn, (err) => { + if (err) console.log(err); + LogReviveModel.create(statsResult.revive, (err) => { + if (err) console.log(err); + LogFlagModel.create(statsResult.flag, (err) => { + if (err) console.log(err); + LogBudgetModel.create(statsResult.budget, (err) => { + if (err) console.log(err); + LogTransportModel.create(statsResult.transport, (err) => { + if (err) console.log(err); + LogPlayerCountModel.create(statsResult.playerCount, (err) => { + if (err) console.log(err); + LogPointsModel.create(statsResult.points, (err) => { + if (err) console.log(err); LogServerFpsModel.create(statsResult.serverFps, (err, serverPerformanceEntries) => { - serverPerformanceEntries.forEach((entry) => { - const idx = statsResult.players - .findIndex((player) => player.name === entry.entityName); - if (idx !== -1) { - const player = statsResult.players[idx]; - player['performance'] = entry._id; - statsResult.players[idx] = player; - } - }); + if (err) console.log(err); + if (serverPerformanceEntries) { + serverPerformanceEntries.forEach((entry) => { + const idx = statsResult.players + .findIndex((player) => player.name === entry.entityName); + if (idx !== -1) { + const player = statsResult.players[idx]; + player['performance'] = entry._id; + statsResult.players[idx] = player; + } + }); + } PlayerModel.create(statsResult.players, (err) => { if (err) { return next(err); diff --git a/static/src/app/statistic/war/server-stats/server-stats.component.html b/static/src/app/statistic/war/server-stats/server-stats.component.html index 09192f3..bb1f534 100644 --- a/static/src/app/statistic/war/server-stats/server-stats.component.html +++ b/static/src/app/statistic/war/server-stats/server-stats.component.html @@ -9,9 +9,10 @@ -
+
- - - - - - - - - - - - - - - - +
+ + +
diff --git a/static/src/app/statistic/war/server-stats/server-stats.component.ts b/static/src/app/statistic/war/server-stats/server-stats.component.ts index 68724e4..ea224c1 100644 --- a/static/src/app/statistic/war/server-stats/server-stats.component.ts +++ b/static/src/app/statistic/war/server-stats/server-stats.component.ts @@ -1,6 +1,7 @@ import {Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild} from '@angular/core'; import {War} from '../../../models/model-interfaces'; import {TranslateService} from '@ngx-translate/core'; +import {ChartUtils} from '../../../utils/chart-utils'; @Component({ @@ -20,11 +21,9 @@ export class ServerStatsComponent implements OnInit, OnChanges { public activeChartSelect: string; - barChartData: any[] = []; - - lineChartData: any[] = []; - showBarChart = true; + barChartData: any[] = []; + lineChartData: any[] = []; tmpSingleAvg; tmpSingleMin; @@ -32,6 +31,8 @@ export class ServerStatsComponent implements OnInit, OnChanges { tmpMinTimeline; tmpServerTimeline; + barChartLabel: string; + lineChartLabel: string; readonly labels = { singleAvg: 'stats.performance.select.single.avg', singleMin: 'stats.performance.select.single.min', @@ -42,9 +43,6 @@ export class ServerStatsComponent implements OnInit, OnChanges { readonly labelsAsString = Object.keys(this.labels) .map((key) => this.labels[key]); - barChartLabel: string; - lineChartLabel: string; - gradient = false; yAxis = true; xAxis = true; @@ -55,6 +53,15 @@ export class ServerStatsComponent implements OnInit, OnChanges { autoscale = true; timeline = false; roundDomains = true; + colorScheme = { + name: 'nightLights', + selectable: false, + group: 'Ordinal', + domain: [ + '#4e31a5', '#9c25a7', '#3065ab', '#57468b', '#904497', '#46648b', + '#32118d', '#a00fb3', '#1052a2', '#6e51bd', '#b63cc3', '#6c97cb', '#8671c1', '#b455be', '#7496c3' + ] + }; constructor(private translate: TranslateService) { } @@ -64,19 +71,16 @@ export class ServerStatsComponent implements OnInit, OnChanges { } ngOnChanges(changes: SimpleChanges) { - if (changes.war || changes.performanceData) { + if ((changes.war || changes.performanceData) && this.performanceData) { this.initializeChartData(); Object.assign(this, [this.barChartData]); this.activeChartSelect = this.labels.singleAvg; - this.startDateObj = new Date(this.war.date); - this.startDateObj.setHours(0); - this.startDateObj.setMinutes(1); } } selectChart(newSelection) { this.activeChartSelect = newSelection; - if (this.activeChartSelect !== this.labels.serverFps) { + if (this.activeChartSelect === this.labels.singleAvg || this.activeChartSelect === this.labels.singleMin) { this.showBarChart = true; this.setBarChartLabel(this.activeChartSelect); switch (this.activeChartSelect) { @@ -104,9 +108,52 @@ export class ServerStatsComponent implements OnInit, OnChanges { } initializeChartData() { + this.tmpAvgTimeline = ChartUtils.getMultiDataArray("min", "avg", "max"); + this.tmpMinTimeline = ChartUtils.getMultiDataArray("min", "avg", "max"); + this.tmpServerTimeline = ChartUtils.getMultiDataArray("min", "avg"); + + const diffMs = (new Date(this.war.endDate).getTime() - new Date(this.war.date).getTime()); + const warDurationMinutes = Math.round(diffMs / 60000); + + let dateObj = new Date(this.war.date); + dateObj.setHours(0); + dateObj.setMinutes(1); + this.tmpSingleAvg = []; this.tmpSingleMin = []; + const maxAvgIdx = Math.min(this.performanceData.map(p => p.avgFps.length) + .sort((a, b) => b - a)[0], warDurationMinutes); + const maxMinIdx = Math.min(this.performanceData.map(p => p.avgFps.length) + .sort((a, b) => b - a)[0], warDurationMinutes); + let tmpAvgArray = new Array(maxAvgIdx).fill(0); + let tmpAvgMin = new Array(maxAvgIdx).fill(1000); + let tmpAvgMax = new Array(maxAvgIdx).fill(0); + let tmpMinArray = new Array(maxMinIdx).fill(0); + let tmpMinMin = new Array(maxMinIdx).fill(1000); + let tmpMinMax = new Array(maxMinIdx).fill(0); + this.performanceData.forEach((entry) => { + if (entry.entityName !== 'SERVER') { + // PLAYER AVERAGE TIMELINE DATA + for (let i = 0; i < entry.avgFps.length && i < tmpAvgArray.length; i++) { + tmpAvgArray[i] = tmpAvgArray[i] + entry.avgFps[i]; + tmpAvgMin[i] = Math.round(Math.min(tmpAvgMin[i], entry.avgFps[i])); + tmpAvgMax[i] = Math.round(Math.max(tmpAvgMax[i], entry.avgFps[i])); + } + // PLAYER MINIMUM TIMELINE DATA + for (let i = 0; i < entry.minFps.length && i < tmpMinArray.length; i++) { + tmpMinArray[i] = tmpMinArray[i] + entry.minFps[i]; + tmpMinMin[i] = Math.round(Math.min(tmpMinMin[i], entry.minFps[i])); + tmpMinMax[i] = Math.round(Math.max(tmpMinMax[i], entry.minFps[i])); + } + } else { + // SERVER TIMELINE DATA + for (let i = 0; i < entry.avgFps.length && i < warDurationMinutes; i++) { + const currDate = new Date(dateObj.getTime() + i * 60000); + this.tmpServerTimeline[0].series.push(ChartUtils.getSeriesEntry(currDate, entry.minFps[i])); + this.tmpServerTimeline[1].series.push(ChartUtils.getSeriesEntry(currDate, entry.avgFps[i])); + } + } this.tmpSingleAvg.push({ name: entry.entityName, value: entry.singleAvgFps @@ -116,6 +163,21 @@ export class ServerStatsComponent implements OnInit, OnChanges { value: entry.singleMinFps }); }); + + tmpAvgArray = tmpAvgArray.map(x => Math.round(x / this.performanceData.length)); + for (let i = 0; i < tmpAvgArray.length; i++) { + const currDate = new Date(dateObj.getTime() + i * 60000); + this.tmpAvgTimeline[0].series.push(ChartUtils.getSeriesEntry(currDate, tmpAvgMin[i])); + this.tmpAvgTimeline[1].series.push(ChartUtils.getSeriesEntry(currDate, tmpAvgArray[i])); + this.tmpAvgTimeline[2].series.push(ChartUtils.getSeriesEntry(currDate, tmpAvgMax[i])); + } + tmpMinArray = tmpMinArray.map(x => Math.round(x / this.performanceData.length)); + for (let i = 0; i < tmpMinArray.length; i++) { + const currDate = new Date(dateObj.getTime() + i * 60000); + this.tmpMinTimeline[0].series.push(ChartUtils.getSeriesEntry(currDate, tmpMinMin[i])); + this.tmpMinTimeline[1].series.push(ChartUtils.getSeriesEntry(currDate, tmpMinArray[i])); + this.tmpMinTimeline[2].series.push(ChartUtils.getSeriesEntry(currDate, tmpMinMax[i])); + } this.tmpSingleAvg.sort((a, b) => a.value - b.value); this.tmpSingleMin.sort((a, b) => a.value - b.value); this.barChartData = this.tmpSingleAvg; diff --git a/static/src/app/statistic/war/war-header/war-header.component.html b/static/src/app/statistic/war/war-header/war-header.component.html index e922d3d..673a9a6 100644 --- a/static/src/app/statistic/war/war-header/war-header.component.html +++ b/static/src/app/statistic/war/war-header/war-header.component.html @@ -48,8 +48,9 @@ {{'stats.scoreboard.tab.player' | translate}} -