2015-01-20 19:29:31 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/* Controllers */
|
|
|
|
|
2015-02-17 11:14:48 +01:00
|
|
|
function StatsCtrl($scope, $filter, socket, _, toastr) {
|
2015-01-20 19:29:31 +01:00
|
|
|
|
2015-02-05 16:54:04 +01:00
|
|
|
// Main Stats init
|
|
|
|
// ---------------
|
|
|
|
|
|
|
|
$scope.nodesTotal = 0;
|
|
|
|
$scope.nodesActive = 0;
|
|
|
|
$scope.bestBlock = 0;
|
|
|
|
$scope.lastBlock = 0;
|
2015-02-18 08:54:04 +01:00
|
|
|
$scope.lastDifficulty = 0;
|
2015-02-05 16:54:04 +01:00
|
|
|
$scope.upTimeTotal = 0;
|
2015-02-18 08:54:04 +01:00
|
|
|
$scope.avgBlockTime = 0;
|
2015-04-06 01:28:42 +02:00
|
|
|
$scope.bestStats = {};
|
2015-02-05 16:54:04 +01:00
|
|
|
|
2015-03-27 17:52:42 +01:00
|
|
|
$scope.lastBlocksTime = [];
|
2015-03-28 00:27:40 +01:00
|
|
|
$scope.difficultyChange = [];
|
|
|
|
$scope.transactionDensity = [];
|
2015-03-28 19:07:15 +01:00
|
|
|
$scope.gasSpending = [];
|
2015-04-06 01:28:42 +02:00
|
|
|
$scope.miners = [];
|
2015-03-27 17:52:42 +01:00
|
|
|
|
2015-02-17 11:14:48 +01:00
|
|
|
$scope.nodes = [];
|
2015-02-08 16:28:19 +01:00
|
|
|
$scope.map = [];
|
2015-04-23 15:17:31 +02:00
|
|
|
$scope.blockPropagationChart = [];
|
2015-02-08 16:03:05 +01:00
|
|
|
|
2015-04-06 07:33:01 +02:00
|
|
|
$scope.latency = 0;
|
|
|
|
|
2015-04-09 23:26:52 +02:00
|
|
|
$scope.predicate = ['-stats.active', '-stats.block.number', 'stats.block.propagation'];
|
2015-04-06 04:40:29 +02:00
|
|
|
$scope.reverse = false;
|
|
|
|
|
2015-04-06 06:05:49 +02:00
|
|
|
$scope.orderTable = function(predicate, reverse)
|
|
|
|
{
|
|
|
|
if(predicate != $scope.predicate)
|
|
|
|
{
|
|
|
|
$scope.reverse = reverse;
|
|
|
|
$scope.predicate = predicate;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$scope.reverse = !$scope.reverse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-23 15:48:00 +01:00
|
|
|
$scope.timeout = setInterval(function(){
|
|
|
|
$scope.$apply();
|
|
|
|
}, 1000);
|
|
|
|
|
2015-04-06 06:30:32 +02:00
|
|
|
$scope.getNumber = function(num) {
|
|
|
|
return new Array(num);
|
|
|
|
}
|
|
|
|
|
2015-01-20 19:29:31 +01:00
|
|
|
// Socket listeners
|
|
|
|
// ----------------
|
|
|
|
|
2015-02-17 06:12:44 +01:00
|
|
|
socket = new Primus();
|
|
|
|
|
|
|
|
socket.on('open', function open() {
|
|
|
|
socket.emit('ready');
|
|
|
|
console.log('The connection has been opened.');
|
2015-02-17 10:30:41 +01:00
|
|
|
})
|
|
|
|
.on('end', function end() {
|
2015-02-17 11:14:48 +01:00
|
|
|
console.log('Socket connection ended.')
|
2015-02-17 10:30:41 +01:00
|
|
|
})
|
|
|
|
.on('error', function error(err) {
|
2015-02-17 06:12:44 +01:00
|
|
|
console.log(err);
|
2015-02-17 10:30:41 +01:00
|
|
|
})
|
|
|
|
.on('reconnecting', function reconnecting(opts) {
|
2015-02-17 06:12:44 +01:00
|
|
|
console.log('We are scheduling a reconnect operation', opts);
|
2015-02-17 10:30:41 +01:00
|
|
|
})
|
|
|
|
.on('data', function incoming(data) {
|
|
|
|
socketAction(data.action, data.data);
|
2015-02-17 06:12:44 +01:00
|
|
|
});
|
|
|
|
|
2015-02-08 16:03:05 +01:00
|
|
|
socket.on('init', function(data)
|
|
|
|
{
|
2015-02-17 11:14:48 +01:00
|
|
|
socketAction("init", data.nodes);
|
2015-01-29 17:50:16 +01:00
|
|
|
});
|
|
|
|
|
2015-04-06 07:33:01 +02:00
|
|
|
socket.on('client-latency', function(data)
|
|
|
|
{
|
|
|
|
$scope.latency = data.latency;
|
|
|
|
})
|
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
function socketAction(action, data)
|
2015-02-08 16:03:05 +01:00
|
|
|
{
|
2015-02-17 11:14:48 +01:00
|
|
|
console.log('Action: ', action);
|
|
|
|
console.log('Data: ', data);
|
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
switch(action) {
|
2015-02-17 11:14:48 +01:00
|
|
|
case "init":
|
|
|
|
$scope.nodes = data;
|
2015-04-17 11:10:20 +02:00
|
|
|
$scope.$apply();
|
|
|
|
|
|
|
|
_.forEach($scope.nodes, function(node, index) {
|
|
|
|
makePeerPropagationChart($scope.nodes[index]);
|
|
|
|
});
|
|
|
|
|
|
|
|
if($scope.nodes.length > 0)
|
|
|
|
toastr['success']("Got nodes list", "Got nodes!");
|
2015-02-17 19:14:38 +01:00
|
|
|
|
2015-02-17 11:14:48 +01:00
|
|
|
break;
|
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
case "add":
|
2015-02-17 19:14:38 +01:00
|
|
|
if(addNewNode(data))
|
2015-02-19 21:49:44 +01:00
|
|
|
toastr['success']("New node "+ $scope.nodes[findIndex({id: data.id})].info.name +" connected!", "New node!");
|
2015-02-17 21:45:30 +01:00
|
|
|
else
|
2015-02-19 21:49:44 +01:00
|
|
|
toastr['info']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" reconnected!", "Node is back!");
|
2015-04-17 11:10:20 +02:00
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case "update":
|
2015-04-06 16:08:36 +02:00
|
|
|
var index = findIndex({id: data.id});
|
|
|
|
$scope.nodes[index].stats = data.stats;
|
|
|
|
$scope.nodes[index].history = data.history;
|
2015-04-17 11:10:20 +02:00
|
|
|
makePeerPropagationChart($scope.nodes[index]);
|
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case "info":
|
|
|
|
$scope.nodes[findIndex({id: data.id})].info = data.info;
|
2015-04-17 11:10:20 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "blockPropagationChart":
|
|
|
|
$scope.blockPropagationChart = data;
|
|
|
|
|
2015-02-17 11:14:48 +01:00
|
|
|
break;
|
2015-02-18 07:06:41 +01:00
|
|
|
|
|
|
|
case "inactive":
|
|
|
|
$scope.nodes[findIndex({id: data.id})].stats = data.stats;
|
2015-02-19 21:49:44 +01:00
|
|
|
toastr['error']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" went away!", "Node connection was lost!");
|
2015-04-17 11:10:20 +02:00
|
|
|
|
2015-02-18 07:06:41 +01:00
|
|
|
break;
|
2015-04-03 06:00:06 +02:00
|
|
|
|
|
|
|
case "latency":
|
|
|
|
$scope.nodes[findIndex({id: data.id})].stats.latency = data.latency;
|
2015-04-17 11:10:20 +02:00
|
|
|
|
2015-04-03 06:00:06 +02:00
|
|
|
break;
|
2015-04-06 07:33:01 +02:00
|
|
|
|
|
|
|
case "client-ping":
|
|
|
|
socket.emit('client-pong');
|
2015-04-17 11:10:20 +02:00
|
|
|
|
2015-04-06 07:33:01 +02:00
|
|
|
break;
|
2015-02-17 10:30:41 +01:00
|
|
|
}
|
2015-01-29 17:50:16 +01:00
|
|
|
|
|
|
|
updateStats();
|
2015-02-17 10:30:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function findIndex(search)
|
|
|
|
{
|
|
|
|
return _.findIndex($scope.nodes, search);
|
|
|
|
}
|
2015-01-29 17:50:16 +01:00
|
|
|
|
2015-04-17 11:10:20 +02:00
|
|
|
function makePeerPropagationChart(node)
|
2015-04-06 16:56:48 +02:00
|
|
|
{
|
2015-04-17 11:10:20 +02:00
|
|
|
jQuery('.' + node.id).sparkline(node.history, {
|
|
|
|
type: 'bar',
|
|
|
|
negBarColor: '#7f7f7f',
|
|
|
|
zeroAxis: false,
|
|
|
|
height: 18,
|
|
|
|
barWidth : 2,
|
|
|
|
barSpacing : 1,
|
|
|
|
tooltipSuffix: ' ms',
|
2015-04-17 12:12:25 +02:00
|
|
|
chartRangeMax: 8000,
|
2015-04-17 11:10:20 +02:00
|
|
|
colorMap: jQuery.range_map({
|
|
|
|
'0:1': '#10a0de',
|
|
|
|
'1:1000': '#7bcc3a',
|
|
|
|
'1001:3000': '#FFD162',
|
|
|
|
'3001:7000': '#ff8a00',
|
|
|
|
'7001:': '#F74B4B'
|
|
|
|
})
|
2015-04-06 16:56:48 +02:00
|
|
|
});
|
2015-04-17 11:10:20 +02:00
|
|
|
}
|
2015-04-06 16:56:48 +02:00
|
|
|
|
2015-02-17 19:14:38 +01:00
|
|
|
function addNewNode(data)
|
|
|
|
{
|
|
|
|
var index = findIndex({id: data.id});
|
|
|
|
if(index < 0)
|
|
|
|
{
|
|
|
|
$scope.nodes.push(data);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.nodes[index] = data;
|
2015-04-17 11:10:20 +02:00
|
|
|
makePeerPropagationChart($scope.nodes[index]);
|
2015-02-17 19:14:38 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-01-29 17:50:16 +01:00
|
|
|
function updateStats()
|
|
|
|
{
|
2015-02-17 10:30:41 +01:00
|
|
|
if($scope.nodes.length)
|
|
|
|
{
|
|
|
|
$scope.nodesTotal = $scope.nodes.length;
|
|
|
|
|
|
|
|
$scope.nodesActive = _.filter($scope.nodes, function(node) {
|
|
|
|
return node.stats.active == true;
|
|
|
|
}).length;
|
|
|
|
|
2015-04-04 21:35:45 +02:00
|
|
|
var bestBlock = _.max($scope.nodes, function(node) {
|
2015-02-17 10:30:41 +01:00
|
|
|
return parseInt(node.stats.block.number);
|
|
|
|
}).stats.block.number;
|
|
|
|
|
2015-04-04 21:35:45 +02:00
|
|
|
if(bestBlock > $scope.bestBlock)
|
|
|
|
{
|
|
|
|
$scope.bestBlock = bestBlock;
|
2015-04-06 01:28:42 +02:00
|
|
|
$scope.bestStats = _.max($scope.nodes, function(node) {
|
2015-04-04 21:35:45 +02:00
|
|
|
return parseInt(node.stats.block.number);
|
2015-04-06 01:28:42 +02:00
|
|
|
}).stats;
|
2015-02-17 10:30:41 +01:00
|
|
|
|
2015-04-06 01:28:42 +02:00
|
|
|
$scope.lastBlock = $scope.bestStats.block.received;
|
|
|
|
$scope.lastBlocksTime = $scope.bestStats.blockTimes;
|
|
|
|
$scope.difficultyChange = $scope.bestStats.difficulty;
|
|
|
|
$scope.transactionDensity = $scope.bestStats.txDensity;
|
|
|
|
$scope.gasSpending = $scope.bestStats.gasSpending;
|
2015-03-27 17:52:42 +01:00
|
|
|
|
2015-04-06 06:30:32 +02:00
|
|
|
if(typeof $scope.bestStats.miners !== 'undefined') {
|
|
|
|
$scope.miners = $scope.bestStats.miners;
|
2015-04-06 01:28:42 +02:00
|
|
|
}
|
2015-04-05 18:22:49 +02:00
|
|
|
|
2015-04-17 02:12:15 +02:00
|
|
|
jQuery('.spark-blocktimes').sparkline($scope.lastBlocksTime.reverse(), {type: 'bar', tooltipSuffix: ' s'});
|
2015-04-04 21:35:45 +02:00
|
|
|
jQuery('.spark-difficulty').sparkline($scope.difficultyChange.reverse(), {type: 'bar'});
|
|
|
|
jQuery('.spark-transactions').sparkline($scope.transactionDensity.reverse(), {type: 'bar'});
|
|
|
|
jQuery('.spark-gasspending').sparkline($scope.gasSpending.reverse(), {type: 'bar'});
|
|
|
|
}
|
2015-03-28 00:27:40 +01:00
|
|
|
|
2015-04-04 21:35:45 +02:00
|
|
|
$scope.lastDifficulty = _.max($scope.nodes, function(node) {
|
2015-03-28 19:07:15 +01:00
|
|
|
return parseInt(node.stats.block.number);
|
2015-04-04 21:35:45 +02:00
|
|
|
}).stats.block.difficulty;
|
2015-03-28 19:07:15 +01:00
|
|
|
|
2015-04-04 21:35:45 +02:00
|
|
|
$scope.avgBlockTime = _.max($scope.nodes, function(node) {
|
|
|
|
return parseInt(node.stats.block.number);
|
|
|
|
}).stats.blocktimeAvg;
|
|
|
|
|
|
|
|
$scope.upTimeTotal = _.reduce($scope.nodes, function(total, node) {
|
|
|
|
return total + node.stats.uptime;
|
|
|
|
}, 0) / $scope.nodes.length;
|
2015-03-28 19:07:15 +01:00
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
$scope.map = _.map($scope.nodes, function(node) {
|
2015-04-17 02:12:15 +02:00
|
|
|
var fill = $filter('bubbleClass')(node.stats, $scope.bestBlock);
|
|
|
|
|
2015-02-17 10:30:41 +01:00
|
|
|
if(node.geo != null)
|
|
|
|
return {
|
2015-04-17 02:12:15 +02:00
|
|
|
radius: 3,
|
2015-02-17 10:30:41 +01:00
|
|
|
latitude: node.geo.ll[0],
|
|
|
|
longitude: node.geo.ll[1],
|
2015-04-17 02:12:15 +02:00
|
|
|
nodeName: node.info.name,
|
|
|
|
fillClass: "text-" + fill,
|
|
|
|
fillKey: fill,
|
2015-02-17 10:30:41 +01:00
|
|
|
};
|
|
|
|
else
|
|
|
|
return {
|
|
|
|
radius: 0,
|
|
|
|
latitude: 0,
|
|
|
|
longitude: 0
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
2015-02-17 06:12:44 +01:00
|
|
|
|
|
|
|
$scope.$apply();
|
2015-01-29 17:50:16 +01:00
|
|
|
}
|
2015-01-20 19:29:31 +01:00
|
|
|
}
|