cgminer-api
Version:
Complete cgminer API implementation for Node.js with multi-version support, response validation, and integration tests
162 lines (150 loc) • 4.33 kB
JavaScript
var _ = require('lodash');
var net = require('net');
var Promise = require('bluebird');
var templates = require('./templates');
_.mixin(require('congruence'));
var singular = [
'VERSION',
'SUMMARY',
'CHECK'
];
var replyMapping = {
check: 'CHECK'
};
function createDeviceMethod (device) {
var deviceUC = device.toUpperCase();
this['_' + device] = function (r) {
return r['' + deviceUC][0];
}.bind(this);
this['_' + device + 'count'] = function (r) {
return r['' + deviceUC + 'S'][0].Count;
}.bind(this);
}
function createCommandMethod (command) {
this[command.name] = function (args) {
return this.request(command, args)
.then(function (result) {
if (_.isFunction(this['_' + command.name])) {
return Promise.try(function () {
return this['_' + name](result);
}.bind(this));
}
else {
return result;
}
}.bind(this));
}.bind(this);
}
/**
* @constructor
*/
function CGMinerClient(options) {
if (!_.isObject(options)) {
options = {};
}
this.host = options.host || '127.0.0.1';
this.port = options.port || 4028;
createCommandMethod.bind(this)({ name: 'version', reply: 'VERSION' });
}
/**
* Load the commands for the API version of the miner we are connected to.
*/
CGMinerClient.prototype.connect = function () {
if (this._loaded) return Promise.resolve(this);
this._loaded = true;
return this.version()
.then(function (version) {
return require('./apis/parser').parse(version.CGMiner);
})
.then(function (commands) {
//console.log(commands);
this._commands = commands;
this._types = ['pga', 'gpu', 'asc'];
_.each(this._commands, createCommandMethod.bind(this));
_.each(this._types, createDeviceMethod.bind(this));
}.bind(this))
.return(this);
};
/**
* Send a requret to cgminer
* @param command.name
* @param command.reply
* @param args
*/
CGMinerClient.prototype.request = function (command, args) {
if (_.has(replyMapping, command.name)) {
command.reply = replyMapping[command.name];
}
var deferred = Promise.defer();
var socket = net.connect({
host: this.host,
port: this.port
});
socket.on('error', function (err) {
return deferred.reject(err);
});
socket.on('connect', function () {
var buffer = '';
socket.on('data', function (data) {
buffer += data.toString();
});
socket.on('end', function () {
var json;
// XXX workaround for https://bitcointalk.org/index.php?topic=28402.msg9170949#msg9170949
var str = buffer
.replace(/\-nan/g, '0')
.replace(/[^\x00-\x7F]/g, '');
try {
if (/RESTART/.test(str)) {
json = {
STATUS: [{
STATUS: 'S',
Code: -1,
Msg: 'Restart',
Description: 'Restarting now',
When: (new Date()).valueOf()
}]
};
}
else {
console.log(str);
json = JSON.parse(str.replace(/[^\}]+$/, ''));
}
//console.log(command);
//console.log(command.reply);
//console.log(json);
if (command.reply === null) {
if (_.similar(templates['null'], json.STATUS[0])) {
return deferred.resolve(json.STATUS[0]);
}
else {
return deferred.reject({ msg: 'response did not match template', response: json });
}
}
if (json.STATUS[0].STATUS === 'E') {
return deferred.reject(new Error(json.STATUS[0].Msg));
}
_.each(json[command.reply], function (reply) {
if (!_.similar(templates[command.name], json[command.reply])) {
return deferred.reject({ msg: 'response did not match template', response: json });
}
});
if (_.contains(singular, command.reply)) {
return deferred.resolve(json[command.reply][0]);
}
else {
return deferred.resolve(json[command.reply]);
}
}
catch (e) {
return deferred.reject(e);
}
});
return socket.write(JSON.stringify({
command: command.name,
parameter: _.isArray(args) ? args.join(',') : args
}));
});
return deferred.promise;
};
module.exports = CGMinerClient;