Merge pull request #80 from cubedro/develop
Added block propagation history
This commit is contained in:
commit
07b2466d67
12
app.js
12
app.js
@ -63,6 +63,9 @@ api.on('connection', function(spark) {
|
|||||||
spark.emit('ready');
|
spark.emit('ready');
|
||||||
|
|
||||||
client.write({action: 'add', data: info});
|
client.write({action: 'add', data: info});
|
||||||
|
|
||||||
|
var blockPropagationChart = Nodes.blockPropagationChart();
|
||||||
|
client.write({action: 'blockPropagationChart', data: blockPropagationChart});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -70,14 +73,16 @@ api.on('connection', function(spark) {
|
|||||||
{
|
{
|
||||||
console.log('Latency: ', spark.latency);
|
console.log('Latency: ', spark.latency);
|
||||||
console.log('got update from ' + spark.id);
|
console.log('got update from ' + spark.id);
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
if(typeof data.id !== 'undefined' && typeof data.stats !== 'undefined')
|
if(typeof data.id !== 'undefined' && typeof data.stats !== 'undefined')
|
||||||
{
|
{
|
||||||
data.stats.latency = spark.latency;
|
data.stats.latency = spark.latency;
|
||||||
var stats = Nodes.update(data.id, data.stats);
|
|
||||||
|
|
||||||
|
var stats = Nodes.update(data.id, data.stats);
|
||||||
client.write({action: 'update', data: stats});
|
client.write({action: 'update', data: stats});
|
||||||
|
|
||||||
|
var blockPropagationChart = Nodes.blockPropagationChart();
|
||||||
|
client.write({action: 'blockPropagationChart', data: blockPropagationChart});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -116,6 +121,9 @@ client.on('connection', function(spark) {
|
|||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
spark.emit('init', {nodes: Nodes.all()});
|
spark.emit('init', {nodes: Nodes.all()});
|
||||||
|
|
||||||
|
var blockPropagationChart = Nodes.blockPropagationChart();
|
||||||
|
spark.write({action: 'blockPropagationChart', data: blockPropagationChart});
|
||||||
});
|
});
|
||||||
|
|
||||||
spark.on('client-pong', function(data) {
|
spark.on('client-pong', function(data) {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
var Blockchain = require('./history');
|
||||||
var Node = require('./node');
|
var Node = require('./node');
|
||||||
|
|
||||||
var Collection = function Collection()
|
var Collection = function Collection()
|
||||||
{
|
{
|
||||||
this._list = [];
|
this._list = [];
|
||||||
|
this._history = new Blockchain();
|
||||||
this._bestBlock = null;
|
this._bestBlock = null;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -24,36 +26,14 @@ Collection.prototype.update = function(id, stats)
|
|||||||
if(!node)
|
if(!node)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(this._bestBlock === null)
|
var block = this._history.add(stats.block, id);
|
||||||
{
|
var propagationHistory = this._history.getNodePropagation(id);
|
||||||
stats.block.received = (new Date()).getTime();
|
|
||||||
stats.block.propagation = 0;
|
|
||||||
this._bestBlock = stats.block;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var oldStats = node.getStats();
|
|
||||||
|
|
||||||
if(stats.block.number !== oldStats.stats.block.number)
|
stats.block.arrived = block.arrived;
|
||||||
{
|
stats.block.received = block.received;
|
||||||
stats.block.received = (new Date()).getTime();
|
stats.block.propagation = block.propagation;
|
||||||
|
|
||||||
if(this._bestBlock.number < stats.block.number)
|
return node.setStats(stats, propagationHistory);
|
||||||
{
|
|
||||||
stats.block.propagation = 0;
|
|
||||||
this._bestBlock = stats.block;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stats.block.propagation = stats.block.received - this._bestBlock.received;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stats.block.received = oldStats.stats.block.received;
|
|
||||||
stats.block.propagation = oldStats.stats.block.propagation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node.setStats(stats);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection.prototype.updateLatency = function(id, latency)
|
Collection.prototype.updateLatency = function(id, latency)
|
||||||
@ -118,4 +98,9 @@ Collection.prototype.all = function()
|
|||||||
return this._list;
|
return this._list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collection.prototype.blockPropagationChart = function()
|
||||||
|
{
|
||||||
|
return this._history.getBlockPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Collection;
|
module.exports = Collection;
|
159
models/history.js
Normal file
159
models/history.js
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var MAX_HISTORY = 1008;
|
||||||
|
var MAX_PROPAGATION = 36;
|
||||||
|
var MAX_BLOCK_PROPAGATION = 96;
|
||||||
|
|
||||||
|
var History = function History(data)
|
||||||
|
{
|
||||||
|
// this._items = new Array(MAX_HISTORY);
|
||||||
|
this._items = [];
|
||||||
|
|
||||||
|
var item = {
|
||||||
|
height: 0,
|
||||||
|
block: {
|
||||||
|
number: 0,
|
||||||
|
hash: '0x?',
|
||||||
|
arrived: 0,
|
||||||
|
received: 0,
|
||||||
|
propagation: 0,
|
||||||
|
difficulty: 0,
|
||||||
|
gasUsed: 0,
|
||||||
|
transactions: [],
|
||||||
|
uncles: []
|
||||||
|
},
|
||||||
|
propagTimes: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// _.fill(this._items, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.add = function(block, id)
|
||||||
|
{
|
||||||
|
var historyBlock = this.search(block.number);
|
||||||
|
|
||||||
|
var now = (new Date()).getTime();
|
||||||
|
block.arrived = now;
|
||||||
|
block.received = now;
|
||||||
|
block.propagation = 0;
|
||||||
|
|
||||||
|
if(historyBlock)
|
||||||
|
{
|
||||||
|
var propIndex = _.findIndex(historyBlock.propagTimes, {node: id});
|
||||||
|
|
||||||
|
if(propIndex === -1)
|
||||||
|
{
|
||||||
|
block.arrived = historyBlock.block.arrived;
|
||||||
|
block.received = now;
|
||||||
|
block.propagation = now - historyBlock.block.received;
|
||||||
|
|
||||||
|
historyBlock.propagTimes.push({node: id, received: now, propagation: block.propagation});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block.arrived = historyBlock.block.arrived;
|
||||||
|
block.received = historyBlock.propagTimes[propIndex].received;
|
||||||
|
block.propagation = historyBlock.propagTimes[propIndex].propagation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var item = {
|
||||||
|
height: block.number,
|
||||||
|
block: block,
|
||||||
|
propagTimes: []
|
||||||
|
}
|
||||||
|
|
||||||
|
item.propagTimes.push({node: id, received: now, propagation: block.propagation});
|
||||||
|
console.log('item: ', item);
|
||||||
|
this._save(item);
|
||||||
|
}
|
||||||
|
this.getNodePropagation(id);
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype._save = function(block)
|
||||||
|
{
|
||||||
|
this._items.push(block);
|
||||||
|
|
||||||
|
if(this._items.length > MAX_HISTORY){
|
||||||
|
this._items.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.search = function(number)
|
||||||
|
{
|
||||||
|
var index = _.findIndex(this._items, {height: number});
|
||||||
|
|
||||||
|
if(index < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return this._items[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.bestBlock = function(obj)
|
||||||
|
{
|
||||||
|
return _.max(this._items, 'height');
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.getNodePropagation = function(id)
|
||||||
|
{
|
||||||
|
var propagation = new Array(MAX_PROPAGATION);
|
||||||
|
var bestBlock = this.bestBlock().height;
|
||||||
|
|
||||||
|
_.fill(propagation, -1);
|
||||||
|
|
||||||
|
var sorted = _(this._items)
|
||||||
|
.sortByOrder('height', false)
|
||||||
|
.slice(0, MAX_PROPAGATION)
|
||||||
|
.reverse()
|
||||||
|
.forEach(function(n, key)
|
||||||
|
{
|
||||||
|
var index = MAX_PROPAGATION - 1 - bestBlock + n.height;
|
||||||
|
|
||||||
|
if(index > 0)
|
||||||
|
{
|
||||||
|
propagation[index] = _.result(_.find(n.propagTimes, 'node', id), 'propagation', -1);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return propagation;
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.getBlockPropagation = function()
|
||||||
|
{
|
||||||
|
var propagation = new Array(MAX_BLOCK_PROPAGATION);
|
||||||
|
var bestBlock = this.bestBlock().height;
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
_.fill(propagation, -1);
|
||||||
|
|
||||||
|
var sorted = _(this._items)
|
||||||
|
.sortByOrder('height', false)
|
||||||
|
.slice(0, MAX_PROPAGATION)
|
||||||
|
.reverse()
|
||||||
|
.forEach(function(n, key)
|
||||||
|
{
|
||||||
|
if(i < MAX_BLOCK_PROPAGATION)
|
||||||
|
{
|
||||||
|
_.forEach(n.propagTimes, function(p, i)
|
||||||
|
{
|
||||||
|
propagation.push({block: n.height, propagation: _.result(p, 'propagation', -1)});
|
||||||
|
propagation.shift();
|
||||||
|
i++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return propagation;
|
||||||
|
}
|
||||||
|
|
||||||
|
History.prototype.history = function()
|
||||||
|
{
|
||||||
|
return _.chain(this._items).sortBy('number').reverse().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = History;
|
@ -1,4 +1,5 @@
|
|||||||
var geoip = require('geoip-lite');
|
var geoip = require('geoip-lite');
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
var MAX_HISTORY = 36;
|
var MAX_HISTORY = 36;
|
||||||
|
|
||||||
@ -31,13 +32,13 @@ var Node = function Node(data)
|
|||||||
uptime: 0,
|
uptime: 0,
|
||||||
lastUpdate: 0
|
lastUpdate: 0
|
||||||
};
|
};
|
||||||
this.blockHistory = [];
|
this.history = new Array(MAX_HISTORY);
|
||||||
this.uptime = {
|
this.uptime = {
|
||||||
started: null,
|
started: null,
|
||||||
history: []
|
history: []
|
||||||
};
|
};
|
||||||
|
|
||||||
this.initBlockHistory();
|
_.fill(this.history, -1);
|
||||||
|
|
||||||
if(this.id === null) {
|
if(this.id === null) {
|
||||||
this.uptime.started = (new Date()).getTime();
|
this.uptime.started = (new Date()).getTime();
|
||||||
@ -90,41 +91,14 @@ Node.prototype.setInfo = function(data)
|
|||||||
|
|
||||||
Node.prototype.getInfo = function()
|
Node.prototype.getInfo = function()
|
||||||
{
|
{
|
||||||
return {id: this.id, info: this.info, geo: this.geo, stats: this.stats, history: this.blockHistory};
|
return {id: this.id, info: this.info, geo: this.geo, stats: this.stats, history: this.history};
|
||||||
}
|
}
|
||||||
|
|
||||||
Node.prototype.initBlockHistory = function()
|
Node.prototype.setStats = function(stats, history)
|
||||||
{
|
|
||||||
for(var i=0; i < MAX_HISTORY; i++)
|
|
||||||
{
|
|
||||||
this.blockHistory.push({
|
|
||||||
number: 0,
|
|
||||||
received: 0,
|
|
||||||
propagation: 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Node.prototype.setStats = function(stats)
|
|
||||||
{
|
{
|
||||||
if(typeof stats !== 'undefined' && typeof stats.block !== 'undefined' && typeof stats.block.number !== 'undefined')
|
if(typeof stats !== 'undefined' && typeof stats.block !== 'undefined' && typeof stats.block.number !== 'undefined')
|
||||||
{
|
{
|
||||||
stats.block.hash = stats.block.hash.replace('0x', '');
|
this.history = history;
|
||||||
|
|
||||||
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;
|
this.stats = stats;
|
||||||
|
|
||||||
return this.getStats();
|
return this.getStats();
|
||||||
@ -147,7 +121,7 @@ Node.prototype.setLatency = function(latency)
|
|||||||
|
|
||||||
Node.prototype.getStats = function()
|
Node.prototype.getStats = function()
|
||||||
{
|
{
|
||||||
return {id: this.id, stats: this.stats, history: this.blockHistory};
|
return {id: this.id, stats: this.stats, history: this.history};
|
||||||
}
|
}
|
||||||
|
|
||||||
Node.prototype.setState = function(active)
|
Node.prototype.setState = function(active)
|
||||||
|
@ -193,25 +193,88 @@ div.small-title-miner {
|
|||||||
opacity: .8;
|
opacity: .8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoverinfo {
|
table i {
|
||||||
|
-webkit-font-smoothing: subpixel-antialiased;
|
||||||
|
-moz-font-smoothing: subpixel-antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
border-color: #222 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th i {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
table td i {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: auto;
|
line-height: 16px;
|
||||||
left: -50%;
|
}
|
||||||
text-align: center;
|
table td i:before {
|
||||||
color: #333;
|
position: absolute;
|
||||||
border: none !important;
|
top: 10px;
|
||||||
box-shadow: none !important;
|
left: 5px;
|
||||||
border-radius: 3px !important;
|
|
||||||
padding: 5px !important;
|
|
||||||
line-height: 14px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoverinfo .propagationBox {
|
.table>tbody>tr>td,
|
||||||
top: 3px;
|
.table>thead>tr>th {
|
||||||
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jqstooltip {
|
.th-nodename {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-latency {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-blockhash {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-blocktime {
|
||||||
|
width: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-peerPropagationChart {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodeInfo .tooltip .tooltip-inner {
|
||||||
|
max-width: 400px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapHolder {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 56.25%;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin: 10px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapHolder > svg {
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jqsfield {
|
.jqsfield {
|
||||||
@ -241,127 +304,19 @@ div.small-title-miner {
|
|||||||
border-bottom-color: #fff;
|
border-bottom-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
table i {
|
.hoverinfo {
|
||||||
-webkit-font-smoothing: subpixel-antialiased;
|
|
||||||
-moz-font-smoothing: subpixel-antialiased;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th,
|
|
||||||
table td {
|
|
||||||
border-color: #222 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th i {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
table td i {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
line-height: 16px;
|
width: auto;
|
||||||
}
|
left: -50%;
|
||||||
table td i:before {
|
text-align: center;
|
||||||
position: absolute;
|
color: #333;
|
||||||
top: 10px;
|
border: none !important;
|
||||||
left: 5px;
|
box-shadow: none !important;
|
||||||
|
border-radius: 3px !important;
|
||||||
|
padding: 5px !important;
|
||||||
|
line-height: 14px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mapHolder {
|
.hoverinfo .propagationBox {
|
||||||
display: block;
|
top: 3px;
|
||||||
position: relative;
|
}
|
||||||
padding-bottom: 56.25%;
|
|
||||||
height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
margin: 10px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mapHolder > svg {
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nodeInfo .tooltip .tooltip-inner {
|
|
||||||
max-width: 400px;
|
|
||||||
text-align: left;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table>tbody>tr>td,
|
|
||||||
.table>thead>tr>th {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.th-nodename {
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.th-latency {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.th-blockhash {
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.th-blocktime {
|
|
||||||
width: 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.th-peerPropagationChart {
|
|
||||||
width: 140px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 639px) {
|
|
||||||
/*.big-info {
|
|
||||||
padding-bottom: 15px;
|
|
||||||
padding-top: 15px;
|
|
||||||
border: 1px solid rgba(255,255,255,0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-info .icon-full-width i {
|
|
||||||
width: 70px;
|
|
||||||
height: 60px;
|
|
||||||
font-size: 60px;
|
|
||||||
line-height: 60px;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-info span {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-info span.small-title {
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 14px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
padding-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-info span.big-details {
|
|
||||||
display: block;
|
|
||||||
font-weight: 200;
|
|
||||||
font-size: 46px;
|
|
||||||
line-height: 50px;
|
|
||||||
letter-spacing: -4px;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 479px) {
|
|
||||||
/*.stat-holder {
|
|
||||||
width: 100%;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
@ -91,8 +91,15 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
switch(action) {
|
switch(action) {
|
||||||
case "init":
|
case "init":
|
||||||
$scope.nodes = data;
|
$scope.nodes = data;
|
||||||
|
$scope.$apply();
|
||||||
|
|
||||||
|
_.forEach($scope.nodes, function(node, index) {
|
||||||
|
makePeerPropagationChart($scope.nodes[index]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if($scope.nodes.length > 0)
|
||||||
|
toastr['success']("Got nodes list", "Got nodes!");
|
||||||
|
|
||||||
if($scope.nodes.length > 0) toastr['success']("Got nodes list", "Got nodes!");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "add":
|
case "add":
|
||||||
@ -100,30 +107,42 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
toastr['success']("New node "+ $scope.nodes[findIndex({id: data.id})].info.name +" connected!", "New node!");
|
toastr['success']("New node "+ $scope.nodes[findIndex({id: data.id})].info.name +" connected!", "New node!");
|
||||||
else
|
else
|
||||||
toastr['info']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" reconnected!", "Node is back!");
|
toastr['info']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" reconnected!", "Node is back!");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "update":
|
case "update":
|
||||||
var index = findIndex({id: data.id});
|
var index = findIndex({id: data.id});
|
||||||
$scope.nodes[index].stats = data.stats;
|
$scope.nodes[index].stats = data.stats;
|
||||||
$scope.nodes[index].history = data.history;
|
$scope.nodes[index].history = data.history;
|
||||||
makePeerPropagationChart(index);
|
makePeerPropagationChart($scope.nodes[index]);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "info":
|
case "info":
|
||||||
$scope.nodes[findIndex({id: data.id})].info = data.info;
|
$scope.nodes[findIndex({id: data.id})].info = data.info;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "blockPropagationChart":
|
||||||
|
$scope.blockPropagationChart = data;
|
||||||
|
makeBlockPropagationChart();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "inactive":
|
case "inactive":
|
||||||
$scope.nodes[findIndex({id: data.id})].stats = data.stats;
|
$scope.nodes[findIndex({id: data.id})].stats = data.stats;
|
||||||
toastr['error']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" went away!", "Node connection was lost!");
|
toastr['error']("Node "+ $scope.nodes[findIndex({id: data.id})].info.name +" went away!", "Node connection was lost!");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "latency":
|
case "latency":
|
||||||
$scope.nodes[findIndex({id: data.id})].stats.latency = data.latency;
|
$scope.nodes[findIndex({id: data.id})].stats.latency = data.latency;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "client-ping":
|
case "client-ping":
|
||||||
socket.emit('client-pong');
|
socket.emit('client-pong');
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,14 +154,12 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
return _.findIndex($scope.nodes, search);
|
return _.findIndex($scope.nodes, search);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makePeerPropagationChart(index)
|
function makePeerPropagationChart(node)
|
||||||
{
|
{
|
||||||
$scope.nodes[index].propagation = _.map($scope.nodes[index].history, function(block) {
|
jQuery('.' + node.id).sparkline(node.history, {
|
||||||
return block.propagation;
|
|
||||||
});
|
|
||||||
|
|
||||||
jQuery('.' + $scope.nodes[index].id).sparkline($scope.nodes[index].propagation, {
|
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
|
negBarColor: '#7f7f7f',
|
||||||
|
zeroAxis: false,
|
||||||
height: 18,
|
height: 18,
|
||||||
barWidth : 2,
|
barWidth : 2,
|
||||||
barSpacing : 1,
|
barSpacing : 1,
|
||||||
@ -157,6 +174,30 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeBlockPropagationChart()
|
||||||
|
{
|
||||||
|
jQuery('.spark-blockpropagation').sparkline(_.map($scope.blockPropagationChart, function(history) {
|
||||||
|
if(typeof history.propagation === 'undefined')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return history.propagation;
|
||||||
|
}), {
|
||||||
|
type: 'bar',
|
||||||
|
negBarColor: '#7f7f7f',
|
||||||
|
zeroAxis: false,
|
||||||
|
barWidth : 2,
|
||||||
|
barSpacing : 1,
|
||||||
|
tooltipSuffix: ' ms',
|
||||||
|
colorMap: jQuery.range_map({
|
||||||
|
'0:1': '#10a0de',
|
||||||
|
'1:1000': '#7bcc3a',
|
||||||
|
'1001:3000': '#FFD162',
|
||||||
|
'3001:7000': '#ff8a00',
|
||||||
|
'7001:': '#F74B4B'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function addNewNode(data)
|
function addNewNode(data)
|
||||||
{
|
{
|
||||||
var index = findIndex({id: data.id});
|
var index = findIndex({id: data.id});
|
||||||
@ -168,8 +209,7 @@ function StatsCtrl($scope, $filter, socket, _, toastr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.nodes[index] = data;
|
$scope.nodes[index] = data;
|
||||||
$scope.nodes[index].history = data.history;
|
makePeerPropagationChart($scope.nodes[index]);
|
||||||
makePeerPropagationChart(index);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,9 @@ angular.module('netStatsApp.filters', [])
|
|||||||
})
|
})
|
||||||
.filter('hashFilter', function() {
|
.filter('hashFilter', function() {
|
||||||
return function(hash) {
|
return function(hash) {
|
||||||
|
if(hash.substr(0,2) === '0x')
|
||||||
|
hash = hash.substr(2,64);
|
||||||
|
|
||||||
return hash.substr(0, 8) + '...' + hash.substr(56, 8);
|
return hash.substr(0, 8) + '...' + hash.substr(56, 8);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -58,24 +58,29 @@ block content
|
|||||||
div.clearfix
|
div.clearfix
|
||||||
|
|
||||||
div.row
|
div.row
|
||||||
div.col-xs-4.stats-boxes(style="padding-top: 30px;")
|
div.col-xs-6.stats-boxes(style="padding-top: 30px;")
|
||||||
div.row
|
div.row
|
||||||
div.col-xs-6.stat-holder
|
div.col-xs-4.stat-holder
|
||||||
div.big-info.chart
|
div.big-info.chart
|
||||||
span.small-title block time
|
span.small-title block time
|
||||||
span.big-details.spark-blocktimes
|
span.big-details.spark-blocktimes
|
||||||
|
|
||||||
div.col-xs-6.stat-holder
|
div.col-xs-4.stat-holder
|
||||||
|
div.big-info.chart
|
||||||
|
span.small-title block propagation
|
||||||
|
span.big-details.spark-blockpropagation
|
||||||
|
|
||||||
|
div.col-xs-4.stat-holder
|
||||||
div.big-info.chart
|
div.big-info.chart
|
||||||
span.small-title difficulty
|
span.small-title difficulty
|
||||||
span.big-details.spark-difficulty
|
span.big-details.spark-difficulty
|
||||||
|
|
||||||
div.col-xs-6.stat-holder
|
div.col-xs-4.stat-holder
|
||||||
div.big-info.chart
|
div.big-info.chart
|
||||||
span.small-title transactions
|
span.small-title transactions
|
||||||
span.big-details.spark-transactions
|
span.big-details.spark-transactions
|
||||||
|
|
||||||
div.col-xs-6.stat-holder
|
div.col-xs-4.stat-holder
|
||||||
div.big-info.chart
|
div.big-info.chart
|
||||||
span.small-title gas spending
|
span.small-title gas spending
|
||||||
span.big-details.spark-gasspending
|
span.big-details.spark-gasspending
|
||||||
@ -91,13 +96,6 @@ block content
|
|||||||
div.block(ng-repeat="i in getNumber(miner.blocks) track by $index", class="{{miner.blocks | minerBlocksClass}}")
|
div.block(ng-repeat="i in getNumber(miner.blocks) track by $index", class="{{miner.blocks | minerBlocksClass}}")
|
||||||
div.clearfix
|
div.clearfix
|
||||||
|
|
||||||
div.col-xs-2.stats-boxes(style="padding-top: 30px;")
|
|
||||||
div.row
|
|
||||||
//- div.col-xs-12.stat-holder
|
|
||||||
//- div.big-info.chart
|
|
||||||
//- span.small-title miners
|
|
||||||
//- span.big-details test
|
|
||||||
|
|
||||||
div.col-xs-4
|
div.col-xs-4
|
||||||
div.col-xs-12
|
div.col-xs-12
|
||||||
nodemap#mapHolder(data="map")
|
nodemap#mapHolder(data="map")
|
||||||
@ -149,7 +147,7 @@ block content
|
|||||||
td(class="{{ node.stats | blockClass : bestBlock }}")
|
td(class="{{ node.stats | blockClass : bestBlock }}")
|
||||||
span.small {{node.stats.block.hash | hashFilter}}
|
span.small {{node.stats.block.hash | hashFilter}}
|
||||||
td(style="padding-left: 14px;") {{node.stats.block.transactions.length || 0}}
|
td(style="padding-left: 14px;") {{node.stats.block.transactions.length || 0}}
|
||||||
td(class="{{ node.stats.block.received | timeClass : node.stats.active }}") {{node.stats.block.received | blockTimeFilter }}
|
td(class="{{ node.stats.block.arrived | timeClass : node.stats.active }}") {{node.stats.block.arrived | blockTimeFilter }}
|
||||||
td(class="{{ node.stats | propagationTimeClass : bestBlock }}") {{node.stats.block.propagation | blockPropagationFilter}}
|
td(class="{{ node.stats | propagationTimeClass : bestBlock }}") {{node.stats.block.propagation | blockPropagationFilter}}
|
||||||
div.propagationBox
|
div.propagationBox
|
||||||
td.peerPropagationChart(class="{{node.id}}")
|
td.peerPropagationChart(class="{{node.id}}")
|
||||||
|
@ -3,6 +3,7 @@ html(ng-app="netStatsApp")
|
|||||||
head
|
head
|
||||||
meta(name="viewport", content="width=device-width, initial-scale=1.0, maximum-scale=1.0")
|
meta(name="viewport", content="width=device-width, initial-scale=1.0, maximum-scale=1.0")
|
||||||
title= title
|
title= title
|
||||||
|
style(type="text/css") [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
|
||||||
link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600,700')
|
link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600,700')
|
||||||
link(rel='stylesheet', href='/css/bootstrap.min.css')
|
link(rel='stylesheet', href='/css/bootstrap.min.css')
|
||||||
link(rel='stylesheet', href='/css/toastr.min.css')
|
link(rel='stylesheet', href='/css/toastr.min.css')
|
||||||
|
Loading…
Reference in New Issue
Block a user