Skip to content

Commit

Permalink
#41: Add module to aggregate the hashrates of multiple miners
Browse files Browse the repository at this point in the history
  • Loading branch information
selaux committed May 3, 2014
1 parent 96c68d7 commit ce46849
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -56,6 +56,7 @@ Note: Each module can have an unique id assigned in the configuration object, so

- [BFGMiner](https://github.com/selaux/miner-dashboard/wiki/BFGMiner)
- [CGMiner](https://github.com/selaux/miner-dashboard/wiki/CGMiner)
- [Aggregated Hashrate](https://github.com/selaux/miner-dashboard/wiki/aggregatedMiners)

#### Markets

Expand Down
3 changes: 3 additions & 0 deletions frontend/stylesheets/style.styl
Expand Up @@ -67,3 +67,6 @@ dt
.revenue
.value
@extend .graph-element
.aggregatedMiners
.hashrate
@extend .graph-element
44 changes: 44 additions & 0 deletions lib/modules/miners/aggregated.js
@@ -0,0 +1,44 @@
'use strict';

var _ = require('lodash'),

setWithHistoricalData = require('../../utils/setWithHistoricalData'),
Module = require('../../Module');

module.exports = Module.extend({

viewId: 'aggregatedMiners',

defaults: {
chartTimespan: 24 * 60 * 60 * 1000,
chartPrecision: 5 * 60 * 1000,
miners: []
},

initialize: function () {
var self = this;

self.title = this.config.title || 'Total Hashrate';

self.config.miners.forEach(function (id) {
self.app.on('update:data:' + id, self.aggregateHashrates.bind(self));
});
},

aggregateHashrates: function () {
var self = this,
currentHashrate = 0;

_.each(this.app.modules, function (module) {
if (self.config.miners.indexOf(module.id) !== -1) {
currentHashrate += module.get('currentHashrate') || 0;
}
});

self.set({
currentHashrate: currentHashrate
});
},

set: setWithHistoricalData([ 'currentHashrate' ], Module.prototype.set)
});
81 changes: 81 additions & 0 deletions lib/views/aggregatedMiners.js
@@ -0,0 +1,81 @@
'use strict';

var Rickshaw = require('rickshaw'),
_ = require('lodash'),
timeHelper = require('../handlebars/helpers/time'),
hashrateHelper = require('../handlebars/helpers/hashrate'),

View = require('../View');

module.exports = View.extend({
template: 'aggregatedMiners',

graph: null,

getChartData: function () {
return this.module.get('historicalData').map(function (measurement) {
return {
x: (measurement.timestamp / 1000),
y: measurement.currentHashrate
};
});
},

initializeGraph: function () {
this.graph = new Rickshaw.Graph({
element: this.$('.graph')[0],
height: 120,
renderer: 'area',
interpolation: 'linear',
stroke: true,
series: [
{
color: '#cae2f7',
name: 'Total Hashrate',
data: this.getChartData()
}
]
});
this.detail = new Rickshaw.Graph.HoverDetail({
graph: this.graph,
yFormatter: function (value) {
return hashrateHelper(value);
},
xFormatter: function (value) {
return timeHelper(value * 1000);
}
});
this.xAxis = new Rickshaw.Graph.Axis.Time({
graph: this.graph
});
this.yAxis = new Rickshaw.Graph.Axis.Y({
graph: this.graph
});
},

updateGraph: function () {
var chartData = this.getChartData();

this.graph.min = _(chartData).pluck('y').min().value() * 0.99;
this.graph.max = _(chartData).pluck('y').max().value() * 1.01;
this.graph.series[0].data = this.getChartData();

this.xAxis.render();
this.yAxis.render();
this.graph.update();
},

postRender: function () {
var graphElement = this.$('.graph'),
graphShouldBeRendered = graphElement.length > 0 && this.module.get('historicalData');

if (graphShouldBeRendered) {
if (this.graph) {
graphElement.replaceWith(this.graph.element);
} else {
this.initializeGraph();
}
this.updateGraph();
}
}
});
14 changes: 14 additions & 0 deletions templates/aggregatedMiners.hbs
@@ -0,0 +1,14 @@
<header class="panel-heading">
<div class="row">
<h2 class="panel-title col-xs-6">{{ title }}</h2>
</div>
</header>
<div class="panel-body dl-horizontal">
<div class="row">
<div class="hashrate col-xs-12 col-md-3">
<div class="graph"></div>
<strong>Current Hashrate: {{hashrate currentHashrate}}</strong>
</div>
</div>
</div>
<footer class="text-muted">Updated: {{time lastUpdated}}</footer>
81 changes: 81 additions & 0 deletions test/specs/lib/modules/miners/aggregatedSpec.js
@@ -0,0 +1,81 @@
'use strict';

var EventEmitter = require('events').EventEmitter,

chai = require('chai'),
sinon = require('sinon'),
sinonChai = require('sinon-chai'),
expect = chai.expect,
_ = require('lodash'),

Aggregated = require('../../../../../lib/modules/miners/aggregated');

chai.use(sinonChai);

describe('modules/miners/aggregated', function () {
var app,
defaultConfig = {
miners: [ 'miner1', 'miner2' ]
};

beforeEach(function () {
app = new EventEmitter();
});

it('should have the default title set to "Total Hashrate"', function () {
var aggregated = new Aggregated(app, defaultConfig);
expect(aggregated.title).to.equal('Total Hashrate');
});

it('should set the title to config.title if provided', function () {
var aggregated = new Aggregated(app, _.defaults({
title: 'Test Title'
}, defaultConfig));
expect(aggregated.title).to.equal('Test Title');
});

it('should aggregate the hashrate whenever a miner hashrate changes', function (done) {
var aggregated = new Aggregated(app, defaultConfig);

app.modules = [
{ get: sinon.stub().returns(123), id: 'miner1' },
{ get: sinon.stub().returns(456), id: 'miner2' },
];

aggregated.on('change', function () {
expect(aggregated.get('currentHashrate')).to.equal(579);
done();
});
app.emit('update:data:miner2');
});

describe('aggregateHashrates', function () {
it('should aggregate the hashrates of the provided modules', function () {
var aggregated = new Aggregated(app, defaultConfig);

app.modules = [
{ get: sinon.stub().returns(123), id: 'miner1' },
{ get: sinon.stub().returns(456), id: 'miner2' },
];
aggregated.set = sinon.stub();
aggregated.aggregateHashrates();

expect(aggregated.set).to.have.been.calledOnce;
expect(aggregated.set).to.have.been.calledWith({
currentHashrate: 579
});
});
});

describe('set', function () {
it('should add the values to historicalData', function () {
var solo = new Aggregated(this.app, {}),
now = new Date().getTime();

solo.set({ currentHashrate: 123 });
expect(solo.get('historicalData')).to.have.length(1);
expect(solo.get('historicalData')[0].currentHashrate).to.equal(123);
expect(solo.get('historicalData')[0].timestamp).to.be.within(now-1, now+1);
});
});
});

0 comments on commit ce46849

Please sign in to comment.