Merge pull request #22 from alexfriesen/improve-serverinfo
Serverinfo performance monitoring
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
<nav class="navbar navbar-default" ng-controller="ServerInfoController" ng-show="serverinfo">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-header" aria-expanded="false">
|
||||||
|
<span class="sr-only">Toggle navigation</span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a class="navbar-brand" href="#">{{serverinfo.Hostname}}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="navbar-collapse-header">
|
||||||
|
<p class="navbar-text" title="Uptime">
|
||||||
|
<span class="glyphicon glyphicon-time"></span>
|
||||||
|
{{serverinfo.Uptime|SecondsToDuration}}
|
||||||
|
</p>
|
||||||
|
<p class="navbar-text" title="Map Name">
|
||||||
|
<span class="glyphicon glyphicon-globe"></span>
|
||||||
|
{{serverinfo.Map}}
|
||||||
|
</p>
|
||||||
|
<p class="navbar-text" title="Current Players/Maximum Players">
|
||||||
|
<span class="glyphicon glyphicon-user"></span>
|
||||||
|
{{serverinfo.Players|number}}/{{serverinfo.MaxPlayers|number}}
|
||||||
|
</p>
|
||||||
|
<p class="navbar-text" title="Framerate">
|
||||||
|
<span class="glyphicon glyphicon-hourglass"></span>
|
||||||
|
{{serverinfo.Framerate|number}}fps
|
||||||
|
</p>
|
||||||
|
<p class="navbar-text" title="Entity Count">
|
||||||
|
<span class="glyphicon glyphicon-oil"></span>
|
||||||
|
{{serverinfo.EntityCount|number}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button class="btn btn-danger navbar-btn navbar-right" type="button" ng-click="Disconnect()">Disconnect</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
+73
-38
@@ -1,57 +1,92 @@
|
|||||||
|
<div ng-controller="ServerInfoController" id="ServerInfoController" data-ng-init="useCharts=true;">
|
||||||
<div ng-controller="ServerInfoController" flex layout="column" id="ServerInfoController">
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h2>
|
<h2>{{serverinfo.Hostname}}</h2>
|
||||||
{{info.Hostname}}
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-4" ng-repeat="(key, value) in info">
|
<div class="col-sm-4" ng-repeat="(key, value) in serverinfo">
|
||||||
<p><span class="text-muted">{{key}}:</span> <span>{{value}}</span></p>
|
<p>
|
||||||
|
<span class="text-muted">{{key}}:</span>
|
||||||
|
<span>{{value}}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="row">
|
||||||
<p>Map: {{info.Map}}</p>
|
|
||||||
|
<div class="col-sm-12">
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading" id="headingPerformance">
|
||||||
|
<button
|
||||||
|
class="btn btn-xs btn-primary pull-right"
|
||||||
|
role="button"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#performanceCollapse"
|
||||||
|
aria-expanded="true"
|
||||||
|
aria-controls="performanceCollapse">
|
||||||
|
toggle
|
||||||
|
</button>
|
||||||
|
<h4 class="panel-title">
|
||||||
|
Server Performance
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="performanceCollapse" class="panel-collapse collapse in" aria-labelledby="headingPerformance">
|
||||||
|
<div class="panel-body">
|
||||||
|
<nvd3 options="performanceChart.options" data="performanceChart.data"></nvd3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-4">
|
<div class="panel panel-default">
|
||||||
<p>Max Players: {{info.MaxPlayers}}</p>
|
<div class="panel-heading" id="headingNetwork">
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-xs btn-primary pull-right"
|
||||||
|
role="button"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#networkCollapse"
|
||||||
|
aria-expanded="true"
|
||||||
|
aria-controls="networkCollapse">
|
||||||
|
toggle
|
||||||
|
</button>
|
||||||
|
<h4 class="panel-title">
|
||||||
|
Server Networking
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="networkCollapse" class="panel-collapse collapse in" aria-labelledby="headingNetwork">
|
||||||
|
<div class="panel-body">
|
||||||
|
<nvd3 options="netChart.options" data="netChart.data"></nvd3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-4">
|
<div class="panel panel-default">
|
||||||
<p>GameTime: {{info.GameTime}}</p>
|
<div class="panel-heading" id="headingPlayers">
|
||||||
|
<button
|
||||||
|
class="btn btn-xs btn-primary pull-right"
|
||||||
|
role="button"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#playersCollapse"
|
||||||
|
aria-expanded="true"
|
||||||
|
aria-controls="playersCollapse">
|
||||||
|
toggle
|
||||||
|
</button>
|
||||||
|
<h4 class="panel-title">
|
||||||
|
Players
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="playersCollapse" class="panel-collapse collapse in" aria-labelledby="headingPlayers">
|
||||||
|
<div class="panel-body">
|
||||||
|
<nvd3 options="playersChart.options" data="playersChart.data"></nvd3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<p>Framerate: <span class="badge">{{info.Framerate}}</span></p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<p>EntityCount: <span class="badge">{{info.EntityCount}}</span></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<p>Uptime: <span class="badge">{{info.Uptime}}</span></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<p>Players: <span class="badge">{{info.Players}}</span></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<p>Joining: <span class="badge">{{info.Joining}}</span></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<p>Queued: <span class="badge">{{info.Queued}}</span></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+3
-58
@@ -12,62 +12,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body ng-controller="RconController">
|
<body ng-controller="RconController">
|
||||||
|
|
||||||
<nav class="navbar navbar-default" ng-controller="ServerInfoController" ng-show="Connected">
|
<div ng-include="'html/header.html'" ng-show="Connected"></div>
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-header" aria-expanded="false">
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<a class="navbar-brand" href="#">{{info.Hostname}}</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbar-collapse-header">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<li>
|
|
||||||
<p class="navbar-text" title="Uptime">
|
|
||||||
<span class="glyphicon glyphicon-time"></span>
|
|
||||||
{{info.Uptime|SecondsToDuration}}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p class="navbar-text" title="Map Name">
|
|
||||||
<span class="glyphicon glyphicon-globe"></span>
|
|
||||||
{{info.Map}}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p class="navbar-text" title="Current Players/Maximum Players">
|
|
||||||
<span class="glyphicon glyphicon-user"></span>
|
|
||||||
{{info.Players|number}}/{{info.MaxPlayers|number}}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p class="navbar-text" title="Framerate">
|
|
||||||
<span class="glyphicon glyphicon-hourglass"></span>
|
|
||||||
{{info.Framerate|number}}fps
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p class="navbar-text" title="Entity Count">
|
|
||||||
<span class="glyphicon glyphicon-oil"></span>
|
|
||||||
{{info.EntityCount|number}}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
|
||||||
<li>
|
|
||||||
<button class="btn btn-danger navbar-btn" type="button" ng-click="Disconnect()">Disconnect</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
@@ -106,8 +51,8 @@
|
|||||||
|
|
||||||
<!-- For charts -->
|
<!-- For charts -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-nvd3/1.0.5/angular-nvd3.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-nvd3/1.0.9/angular-nvd3.min.js"></script>
|
||||||
|
|
||||||
<!-- Data manipulation -->
|
<!-- Data manipulation -->
|
||||||
<script src="js/linq.min.js"></script>
|
<script src="js/linq.min.js"></script>
|
||||||
|
|||||||
+221
-9
@@ -1,19 +1,231 @@
|
|||||||
|
|
||||||
app.controller('ServerInfoController', ServerInfoController);
|
app.controller('ServerInfoController', ServerInfoController);
|
||||||
|
|
||||||
function ServerInfoController( $scope, rconService, $routeParams, $interval )
|
var DATA_LIMIT = 100;
|
||||||
{
|
var recordedData = [];
|
||||||
$scope.info = {};
|
|
||||||
|
|
||||||
$scope.refresh = function ()
|
function ServerInfoController($scope, rconService, $routeParams, $interval) {
|
||||||
|
$scope.useCharts = false;
|
||||||
|
|
||||||
|
// TODO: move serverinfo to service
|
||||||
|
$scope.serverinfo = {};
|
||||||
|
|
||||||
|
$scope.playersChart = {
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
type: 'pieChart',
|
||||||
|
height: 250,
|
||||||
|
margin: {
|
||||||
|
top: 0,
|
||||||
|
right: 75,
|
||||||
|
bottom: 0,
|
||||||
|
left: 75
|
||||||
|
},
|
||||||
|
donut: true,
|
||||||
|
x: function(d) {
|
||||||
|
return d.key;
|
||||||
|
},
|
||||||
|
y: function(d) {
|
||||||
|
return d.y;
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
axisLabel: 'Slots',
|
||||||
|
tickFormat: function(d) {
|
||||||
|
return d3.format('.d')(d);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showLabels: true,
|
||||||
|
pie: {
|
||||||
|
startAngle: function(d) {
|
||||||
|
return d.startAngle / 2 - Math.PI / 2
|
||||||
|
},
|
||||||
|
endAngle: function(d) {
|
||||||
|
return d.endAngle / 2 - Math.PI / 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
duration: 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: []
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.performanceChart = {
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
type: 'multiChart',
|
||||||
|
height: 200,
|
||||||
|
margin: {
|
||||||
|
top: 30,
|
||||||
|
right: 75,
|
||||||
|
bottom: 40,
|
||||||
|
left: 75
|
||||||
|
},
|
||||||
|
color: [
|
||||||
|
'darkred', 'yellowgreen'
|
||||||
|
],
|
||||||
|
duration: 0,
|
||||||
|
lines1: {
|
||||||
|
duration: 0
|
||||||
|
},
|
||||||
|
lines2: {
|
||||||
|
duration: 0
|
||||||
|
},
|
||||||
|
useInteractiveGuideline: true,
|
||||||
|
yAxis1: {
|
||||||
|
axisLabel: 'Framerate',
|
||||||
|
tickFormat: function(d) {
|
||||||
|
return d3.format(',d')(d);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis2: {
|
||||||
|
axisLabel: 'Entities',
|
||||||
|
tickFormat: function(d) {
|
||||||
|
return d3.format(',d')(d);
|
||||||
|
},
|
||||||
|
// axisLabelDistance: 12
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
axisLabel: "Time",
|
||||||
|
tickFormat: function(d) {
|
||||||
|
return d3.time.format('%H:%M:%S')(new Date(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [
|
||||||
{
|
{
|
||||||
|
key: 'Framerate',
|
||||||
|
type: "line",
|
||||||
|
duration: 0,
|
||||||
|
yAxis: 1,
|
||||||
|
values: []
|
||||||
|
}, {
|
||||||
|
key: 'Entities',
|
||||||
|
type: 'line',
|
||||||
|
duration: 0,
|
||||||
|
yAxis: 2,
|
||||||
|
values: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.netChart = {
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
type: 'stackedAreaChart',
|
||||||
|
height: 250,
|
||||||
|
margin: {
|
||||||
|
top: 0,
|
||||||
|
right: 75,
|
||||||
|
bottom: 40,
|
||||||
|
left: 75
|
||||||
|
},
|
||||||
|
useVoronoi: false,
|
||||||
|
duration: 0,
|
||||||
|
useInteractiveGuideline: true,
|
||||||
|
yAxis: {
|
||||||
|
axisLabel: 'Network',
|
||||||
|
tickFormat: function(bytes) {
|
||||||
|
var fmt = d3.format('.0f');
|
||||||
|
if (bytes < 1024) {
|
||||||
|
return fmt(bytes) + 'B';
|
||||||
|
} else if (bytes < 1024 * 1024) {
|
||||||
|
return fmt(bytes / 1024) + 'kB';
|
||||||
|
} else if (bytes < 1024 * 1024 * 1024) {
|
||||||
|
return fmt(bytes / 1024 / 1024) + 'MB';
|
||||||
|
} else {
|
||||||
|
return fmt(bytes / 1024 / 1024 / 1024) + 'GB';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
axisLabel: "Time",
|
||||||
|
tickFormat: function(d) {
|
||||||
|
return d3.time.format('%H:%M:%S')(new Date(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
key: 'IN',
|
||||||
|
values: []
|
||||||
|
}, {
|
||||||
|
key: 'OUT',
|
||||||
|
values: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
rconService.InstallService($scope, _refresh);
|
||||||
|
|
||||||
|
// TODO: move updateinterval to service
|
||||||
|
var timer = $interval(_refresh, 1000);
|
||||||
|
$scope.$on("$destroy", function() {
|
||||||
|
$interval.cancel(timer);
|
||||||
|
$scope.serverinfo = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
function _refresh() {
|
||||||
rconService.Request('serverinfo', $scope, function(msg) {
|
rconService.Request('serverinfo', $scope, function(msg) {
|
||||||
$scope.info = JSON.parse( msg.Message );
|
_updateData(JSON.parse(msg.Message));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
rconService.InstallService( $scope, $scope.refresh );
|
function _updateData(data) {
|
||||||
|
$scope.serverinfo = data;
|
||||||
|
|
||||||
|
if ($scope.useCharts) {
|
||||||
|
_collectChartData(data);
|
||||||
|
_generateChartData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _collectChartData(data) {
|
||||||
|
// player chart
|
||||||
|
$scope.playersChart.data = [
|
||||||
|
{
|
||||||
|
key: 'Queued',
|
||||||
|
y: data.Queued
|
||||||
|
}, {
|
||||||
|
key: 'Joining',
|
||||||
|
y: data.Joining
|
||||||
|
}, {
|
||||||
|
key: 'Players',
|
||||||
|
y: data.Players
|
||||||
|
}, {
|
||||||
|
key: 'Free',
|
||||||
|
y: (data.MaxPlayers - (data.Joining + data.Players))
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
recordedData.push({ts: Date.now(), data: data});
|
||||||
|
if (recordedData.length > DATA_LIMIT) {
|
||||||
|
recordedData = recordedData.slice(Math.max(recordedData.length - DATA_LIMIT, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _generateChartData() {
|
||||||
|
|
||||||
|
var fpsChartValues = [];
|
||||||
|
var entChartValues = [];
|
||||||
|
|
||||||
|
var netInChartValues = [];
|
||||||
|
var netOutChartValues = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < recordedData.length; i++) {
|
||||||
|
var record = recordedData[i];
|
||||||
|
fpsChartValues.push({x: record.ts, y: record.data.Framerate});
|
||||||
|
entChartValues.push({x: record.ts, y: record.data.EntityCount});
|
||||||
|
|
||||||
|
netInChartValues.push({x: record.ts, y: record.data.NetworkIn});
|
||||||
|
netOutChartValues.push({x: record.ts, y: record.data.NetworkOut});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.performanceChart.data[0].values = fpsChartValues;
|
||||||
|
$scope.performanceChart.data[1].values = entChartValues;
|
||||||
|
|
||||||
|
$scope.netChart.data[0].values = netInChartValues;
|
||||||
|
$scope.netChart.data[1].values = netOutChartValues;
|
||||||
|
}
|
||||||
|
|
||||||
var timer = $interval( $scope.refresh, 500 );
|
|
||||||
$scope.$on( "$destroy", function () { $interval.cancel( timer ); } );
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ a:hover
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-default .navbar-nav > li > .navbar-text,
|
.navbar-default .navbar-text,
|
||||||
.navbar-default .navbar-nav > li > a, .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus
|
.navbar-default .navbar-nav > li > a, .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus
|
||||||
{
|
{
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|||||||
Reference in New Issue
Block a user