diff --git a/models/history.js b/models/history.js index 1a20517..1f35315 100644 --- a/models/history.js +++ b/models/history.js @@ -1,12 +1,14 @@ var _ = require('lodash'); var MAX_HISTORY = 1008; -var MAX_PROPAGATION = 36; +var MAX_PEER_PROPAGATION = 36; var MAX_BLOCK_PROPAGATION = 96; +var MIN_PROPAGATION_RANGE = 1; +var MAX_PROPAGATION_RANGE = 20000; +var MAX_BINS = 40; var History = function History(data) { - // this._items = new Array(MAX_HISTORY); this._items = []; var item = { @@ -24,8 +26,6 @@ var History = function History(data) }, propagTimes: [] }; - - // _.fill(this._items, item); } History.prototype.add = function(block, id) @@ -99,18 +99,20 @@ History.prototype.bestBlock = function(obj) History.prototype.getNodePropagation = function(id) { - var propagation = new Array(MAX_PROPAGATION); + var propagation = new Array(MAX_PEER_PROPAGATION); var bestBlock = this.bestBlock().height; + _.fill(propagation, -1); var sorted = _(this._items) .sortByOrder('height', false) - .slice(0, MAX_PROPAGATION) + .slice(0, MAX_PEER_PROPAGATION) .reverse() .forEach(function(n, key) { - var index = MAX_PROPAGATION - 1 - bestBlock + n.height; + var index = MAX_PEER_PROPAGATION - 1 - bestBlock + n.height; + if(index > 0) { @@ -124,27 +126,21 @@ History.prototype.getNodePropagation = function(id) History.prototype.getBlockPropagation = function() { - var propagation = new Array(MAX_BLOCK_PROPAGATION); - var bestBlock = this.bestBlock().height; - var i = 0; - - _.fill(propagation, -1); + var propagation = []; var sorted = _(this._items) .sortByOrder('height', false) - .slice(0, MAX_PROPAGATION) + .slice(0, MAX_BLOCK_PROPAGATION) .reverse() .forEach(function(n, key) { - if(i < MAX_BLOCK_PROPAGATION) + _.forEach(n.propagTimes, function(p, i) { - _.forEach(n.propagTimes, function(p, i) - { - propagation.push({block: n.height, propagation: _.result(p, 'propagation', -1)}); - propagation.shift(); - i++; - }); - } + var prop = _.result(p, 'propagation', -1); + + if(prop >= 0) + propagation.push(prop); + }); }) .value(); diff --git a/models/node.js b/models/node.js index e972a69..e85419e 100644 --- a/models/node.js +++ b/models/node.js @@ -52,8 +52,6 @@ var Node = function Node(data) this.info = data.info; if(typeof data.ip !== 'undefined'){ - if(data.ip === '::ffff:127.0.0.1') - data.ip = '84.117.82.122'; this.info.ip = data.ip; this.setGeo(data.ip); } diff --git a/public/css/style.css b/public/css/style.css index 6bc760e..c3660e4 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -114,7 +114,7 @@ div.small-title-miner { color: #aaa; } -.big-info span.big-details { +.big-info .big-details { display: block; font-weight: 200; font-size: 50px; @@ -122,7 +122,7 @@ div.small-title-miner { letter-spacing: -4px; } -.big-info.chart span.big-details { +.big-info.chart .big-details { padding-top: 15px; } @@ -157,7 +157,7 @@ div.small-title-miner { } .blocks-holder .block-count { - font-family: 'Lucida Console', monaco, "Courier New", Courier, monospace; + font-family: 'Lucida Console', "Courier New", Courier, monospace; font-weight: bold; font-size: 10px; padding-top: 3px; @@ -230,6 +230,11 @@ table td i:before { } .th-nodename { + width: 300px; + text-overflow: ellipsis; +} + +.th-nodetype { width: 200px; } @@ -319,4 +324,49 @@ table td i:before { .hoverinfo .propagationBox { top: 3px; +} + +svg { + overflow: visible !important; +} + +svg .bar { + fill: #1f77b4; + shape-rendering: crispEdges; +} + +svg .bar:hover { + opacity: 0.8; +} + +svg .line { + fill: none; + stroke: #ff0000; + stroke-width: 2px; + shape-rendering: auto; +} + +svg .bar .a { + fill: #aec7e8; +} + +svg .bar text { + text-anchor: end; + font-size: 12px; +} + +svg .axis path, +svg .axis line { + fill: none; + stroke: rgba(255,255,255,0.15); + shape-rendering: crispEdges; +} +svg .axis text { + fill: #777; + font-size: 10px; + letter-spacing: 0px; + font-family: "Source Sans Pro"; + font-weight: 700; + -webkit-font-smoothing: subpixel-antialiased; + -moz-font-smoothing: subpixel-antialiased; } \ No newline at end of file diff --git a/public/fonts/Simple-Line-Icons.ttf b/public/fonts/Simple-Line-Icons.ttf new file mode 100755 index 0000000..2194f1f Binary files /dev/null and b/public/fonts/Simple-Line-Icons.ttf differ diff --git a/public/js/controllers.js b/public/js/controllers.js index 127cc8b..f434173 100644 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -24,6 +24,7 @@ function StatsCtrl($scope, $filter, socket, _, toastr) { $scope.nodes = []; $scope.map = []; + $scope.blockPropagationChart = []; $scope.latency = 0; @@ -125,7 +126,6 @@ function StatsCtrl($scope, $filter, socket, _, toastr) { case "blockPropagationChart": $scope.blockPropagationChart = data; - makeBlockPropagationChart(); break; @@ -175,31 +175,6 @@ 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', - chartRangeMax: 8000, - colorMap: jQuery.range_map({ - '0:1': '#10a0de', - '1:1000': '#7bcc3a', - '1001:3000': '#FFD162', - '3001:7000': '#ff8a00', - '7001:': '#F74B4B' - }) - }); - } - function addNewNode(data) { var index = findIndex({id: data.id}); diff --git a/public/js/directives.js b/public/js/directives.js index d7337b7..30bc00f 100644 --- a/public/js/directives.js +++ b/public/js/directives.js @@ -75,4 +75,96 @@ angular.module('netStatsApp.directives', []). }); } }; +}]). + directive('histogram', ['$compile', function($compile) { + return { + restrict: 'EA', + scope: { + data: '=' + }, + link: function(scope, element, attrs) { + var margin = {top: 0, right: 0, bottom: 0, left: 5}; + var width = 285 - margin.left - margin.right, + height = 173 - margin.top - margin.bottom; + + var TICKS = 40; + + var x = d3.scale.linear() + .domain([0, 20000]) + .rangeRound([0, width]) + .interpolate(d3.interpolateRound); + + var y = d3.scale.linear() + .range([height, 0]) + .interpolate(d3.interpolateRound); + + var xAxis = d3.svg.axis() + .scale(x) + .orient("bottom") + .ticks(4, ",.1s") + .tickFormat(function(t){ return t/1000 + "s"}); + + var yAxis = d3.svg.axis() + .scale(y) + .orient("left") + .ticks(4); + + var line = d3.svg.line() + .x(function(d) { return x(d.x + d.dx); }) + .y(function(d) { return y(d.y); }) + .interpolate('basis'); + + scope.init = function() { + + var data = d3.layout.histogram() + .frequency(true) + .bins(x.ticks(TICKS)) + (scope.data); + + y.domain([0, d3.max(data, function(d) { return d.y; })]); + + element.empty(); + + var svg = d3.select(".d3-blockpropagation").append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + // .attr("transform", "translate(0,0)") + .call(xAxis); + + svg.append("g") + .attr("class", "y axis") + .attr("transform", "translate(0, 0)") + // .attr("transform", "translate(" + width + ", 0)") + .call(yAxis); + + + svg.selectAll(".bar") + .data(data) + .enter().insert("rect", ".axis") + .attr("class", "bar") + .attr("x", function(d) { return x(d.x) + 1; }) + .attr("y", function(d) { return y(d.y); }) + .attr("rx", 1.5) + .attr("ry", 1.5) + .attr("width", x(data[0].dx + data[0].x) - x(data[0].x) - 1) + .attr("height", function(d) { return height - y(d.y); }); + + svg.append("path") + .attr("class", "line") + .attr("d", line(data)); + } + + scope.init(); + + scope.$watch('data', function() { + scope.init(); + }, true); + } + }; }]); \ No newline at end of file diff --git a/public/js/filters.js b/public/js/filters.js index a4fc293..d5f42e7 100644 --- a/public/js/filters.js +++ b/public/js/filters.js @@ -50,6 +50,11 @@ angular.module('netStatsApp.filters', []) tmp[0] = tmp[0].replace('Ethereum(++)', 'Eth'); + if(tmp[0].indexOf('pyethapp') === 0) + { + tmp[0] = 'pyeth'; + } + if(tmp[1][0] !== 'v' && tmp[1][2] !== '.') { tmp.splice(1,1); diff --git a/public/js/script.js b/public/js/script.js index 475bf2f..05d034e 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -20,8 +20,56 @@ }); moment.relativeTimeThreshold('s', 60); - moment.relativeTimeThreshold('m', 60); - moment.relativeTimeThreshold('h', 24); - moment.relativeTimeThreshold('d', 28); - moment.relativeTimeThreshold('M', 12); + moment.relativeTimeThreshold('m', 60); + moment.relativeTimeThreshold('h', 24); + moment.relativeTimeThreshold('d', 28); + moment.relativeTimeThreshold('M', 12); + + + json = [4292,12436,13141,14268,14650,0,55,774,891,1045,1184,1520,1786,1819,2103,2523,2846,2895,3010,3219,13046,0,284,507,525,1410,1531,1673,1803,1978,2652,2961,4224,4863,5056,12281,0,561,812,903,2161,2210,2312,2450,2559,2725,3273,4559,4727,5868,7777,0,1621,6982,9008,14035,0,1181,2753,2782,3580,3945,5779,7936,21489,0,196,1587,1611,1648,2603,2998,3802,4265,4464,5323,5606,6551,9982,10417,0,884,989,1202,1247,1282,2297,2577,2735,2935,3288,4261,4652,4706,4855,6509]; + + // var data = lineData = d3.layout.histogram() + // .frequency(true) + // .bins(x.ticks(40)) + // (json); + + // y.domain([0, d3.max(data, function(d) { return d.y; })]); + + /* Start drawing */ + + + + // d3.tsv("histogram.tsv", type, function(error, histogram) { + // var n = d3.sum(histogram, function(d) { return d.y = d.a; }); + + // y.domain([0, d3.max(histogram, function(d) { return d.y; })]); + + // var bar = svg.insert("g", ".axis") + // .attr("class", "bar") + // .selectAll("g") + // .data(histogram) + // .enter().append("g") + // .attr("transform", function(d) { return "translate(" + x(d.x) + ",0)"; }); + + // bar.append("rect") + // .attr("class", "b") + // .attr("x", 1) + // .attr("y", function(d) { return y(d.b); }) + // .attr("width", x(histogram[0].dx) - 1) + // .attr("height", function(d) { return height - y(d.b); }); + + // bar.append("rect") + // .attr("class", "a") + // .attr("x", 1) + // .attr("y", function(d) { return y(d.y); }) + // .attr("width", x(histogram[0].dx) - 1) + // .attr("height", function(d) { return height - y(d.a); }); + + // bar.filter(function(d) { return d.y / n >= .0095; }).append("text") + // .attr("dy", ".015em") + // .attr("transform", function(d) { return "translate(" + x(histogram[0].dx) / 2 + "," + (y(1000) + 6) + ")rotate(-90)"; }) + // // .attr("transform", function(d) { return "translate(" + x(histogram[0].dx) / 2 + "," + (y(d.y) + 6) + ")rotate(-90)"; }) + // .text(function(d) { return formatPercent(d.y / n); }); + // }); + })(); \ No newline at end of file diff --git a/views/index.jade b/views/index.jade index f6cbdf8..8fb01c5 100644 --- a/views/index.jade +++ b/views/index.jade @@ -65,16 +65,16 @@ block content span.small-title block time span.big-details.spark-blocktimes - 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 span.small-title difficulty span.big-details.spark-difficulty + div.col-xs-4.stat-holder.pull-right + div.big-info.chart.double-chart + span.small-title block propagation + histogram.big-details.d3-blockpropagation(data="blockPropagationChart") + div.col-xs-4.stat-holder div.big-info.chart span.small-title transactions @@ -106,9 +106,9 @@ block content table.table.table-striped thead tr.text-info - th - i.icon-node(data-toggle="tooltip", data-placement="top", title="Node name", ng-click="orderTable(['-stats.active', 'info.name'], false)") th.th-nodename + i.icon-node(data-toggle="tooltip", data-placement="top", title="Node name", ng-click="orderTable(['-stats.active', 'info.name'], false)") + th.th-nodetype i.icon-laptop(data-toggle="tooltip", data-placement="top", title="Node type", ng-click="orderTable(['-stats.active', 'info.node'], false)") th.th-latency i.icon-clock(data-toggle="tooltip", data-placement="top", title="Node latency", ng-click="orderTable(['-stats.active', 'stats.latency'], false)")