diff --git a/models/node.js b/models/node.js index 1532ea9..b6fcfd1 100644 --- a/models/node.js +++ b/models/node.js @@ -1,5 +1,7 @@ var geoip = require('geoip-lite'); +var MAX_HISTORY = 36; + var Node = function Node(data) { this.id = null; @@ -29,6 +31,7 @@ var Node = function Node(data) uptime: 0, lastUpdate: 0 }; + this.blockHistory = []; this.uptime = { started: null, history: [] @@ -83,7 +86,7 @@ Node.prototype.setInfo = function(data) Node.prototype.getInfo = function() { - return {id: this.id, info: this.info, geo: this.geo, stats: this.stats}; + return {id: this.id, info: this.info, geo: this.geo, stats: this.stats, history: this.blockHistory}; } Node.prototype.setStats = function(stats) @@ -92,6 +95,20 @@ Node.prototype.setStats = function(stats) { stats.block.hash = stats.block.hash.replace('0x', ''); + if(stats.block.number > this.stats.block.number) + { + if(this.blockHistory.length === MAX_HISTORY ) + this.blockHistory.shift(); + + var history = { + number: stats.block.number, + received: stats.block.received, + propagation: stats.block.propagation + }; + + this.blockHistory.push(history); + } + this.stats = stats; return this.getStats(); @@ -114,7 +131,7 @@ Node.prototype.setLatency = function(latency) Node.prototype.getStats = function() { - return {id: this.id, stats: this.stats}; + return {id: this.id, stats: this.stats, history: this.blockHistory}; } Node.prototype.setState = function(active) diff --git a/public/css/style.css b/public/css/style.css index f22cc49..8ed2cb3 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -222,13 +222,17 @@ table td { } .th-blockhash { - width: 460px; + width: 140px; } .th-blocktime { width: 110px; } +.th-peerPropagationChart { + width: 140px; +} + @media only screen and (max-width: 639px) { /*.big-info { padding-bottom: 15px; diff --git a/public/js/controllers.js b/public/js/controllers.js index 0886664..893681d 100644 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -103,7 +103,18 @@ function StatsCtrl($scope, $filter, socket, _, toastr) { break; case "update": - $scope.nodes[findIndex({id: data.id})].stats = data.stats; + var index = findIndex({id: data.id}); + $scope.nodes[index].stats = data.stats; + $scope.nodes[index].history = data.history; + $scope.nodes[index].propagation = _.map(data.history, function(block) { + return block.propagation; + }); + jQuery('.' + data.id).sparkline($scope.nodes[index].propagation, { + type: 'bar', + height: 20, + barWidth : 2, + barSpacing : 1, + }); break; case "info": @@ -143,6 +154,10 @@ function StatsCtrl($scope, $filter, socket, _, toastr) { } $scope.nodes[index] = data; + $scope.nodes[index].history = data.history; + $scope.nodes[index].propagation = _.map(data.history, function(block) { + return block.propagation; + }); return false; } @@ -211,8 +226,6 @@ function StatsCtrl($scope, $filter, socket, _, toastr) { longitude: 0 }; }); - - // console.log($scope.map); } $scope.$apply(); diff --git a/public/js/filters.js b/public/js/filters.js index f361756..92788ae 100644 --- a/public/js/filters.js +++ b/public/js/filters.js @@ -66,12 +66,11 @@ angular.module('netStatsApp.filters', []) }) .filter('hashFilter', function() { return function(hash) { - return hash.substr(0, 6) + '...' + hash.substr(58, 6); + return hash.substr(0, 8) + '...' + hash.substr(56, 8); } }) .filter('timeClass', function() { return function(timestamp) { - console.log(timestamp); return timeClass(timestamp); }; }) diff --git a/views/index.jade b/views/index.jade index f07a753..8f3c493 100644 --- a/views/index.jade +++ b/views/index.jade @@ -129,6 +129,7 @@ block content i.icon-time(data-toggle="tooltip", data-placement="top", title="Last block time", ng-click="orderTable('-stats.block.received', false)") th i.icon-gas(data-toggle="tooltip", data-placement="top", title="Propagation time", ng-click="orderTable('stats.block.propagation', false)") + th.th-peerPropagationChart th i.icon-bulb(data-toggle="tooltip", data-placement="top", title="Up-time", ng-click="orderTable('-stats.uptime', false)") tbody @@ -146,8 +147,9 @@ block content td(style="padding-left: 15px;") {{node.stats.pending}} td(class="{{ node.stats.block.number | blockClass : bestBlock }}") {{'#' + node.stats.block.number}} td(class="{{ node.stats.block.number | blockClass : bestBlock }}") - span.small {{node.stats.block.hash}} + span.small {{node.stats.block.hash | hashFilter}} td(style="padding-left: 14px;") {{node.stats.block.transactions.length || 0}} td(class="{{ node.stats.block.received | timeClass }}") {{node.stats.block.received | blockTimeFilter }} td(class="{{ node.stats.block | propagationTimeClass : bestBlock }}") {{node.stats.block.propagation | blockPropagationFilter}} + td.peerPropagationChart(class="{{node.id}}") td(class="{{ node.stats.uptime | upTimeClass }}") {{ node.stats.uptime | upTimeFilter }}