Added miner & Setting list API

This commit is contained in:
Michele Marcucci 2018-11-02 11:11:08 +01:00
parent e5b871cbd2
commit bfa366a2ec
18 changed files with 643 additions and 3 deletions

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
9.8.0

217
miner-test-server.js Normal file
View File

@ -0,0 +1,217 @@
'use strict';
const sampleData = {
"summary": [
{
"STATUS": [
{
"STATUS": "S",
"When": 1541144892,
"Code": 11,
"Msg": "Summary",
"Description": "bfgminer 5.4.2"
}
],
"SUMMARY": [
{
"Elapsed": 847,
"MHS av": 3.364,
"MHS 20s": 3.324,
"Found Blocks": 0,
"Getworks": 30,
"Accepted": 168,
"Rejected": 0,
"Hardware Errors": 2,
"Utility": 11.9,
"Discarded": 78,
"Stale": 0,
"Get Failures": 0,
"Local Work": 138,
"Remote Failures": 0,
"Network Blocks": 5,
"Total MH": 2849.3488,
"Diff1 Work": 0.67822266,
"Work Utility": 0.048,
"Difficulty Accepted": 0.65625,
"Difficulty Rejected": 0,
"Difficulty Stale": 0,
"Best Share": 1.03726997,
"Device Hardware%": 0.1438,
"Device Rejected%": 0,
"Pool Rejected%": 0,
"Pool Stale%": 0,
"Last getwork": 1541144888
}
],
"id": 1
}
],
"devs": [
{
"STATUS": [
{
"STATUS": "S",
"When": 1541144892,
"Code": 9,
"Msg": "1 PGA(s)",
"Description": "bfgminer 5.4.2"
}
],
"DEVS": [
{
"PGA": 0,
"Name": "MLD",
"ID": 0,
"Enabled": "Y",
"Status": "Alive",
"Device Elapsed": 847,
"MHS av": 3.363,
"MHS 20s": 3.373,
"MHS rolling": 3.373,
"Accepted": 168,
"Rejected": 0,
"Hardware Errors": 2,
"Utility": 11.897,
"Stale": 0,
"Last Share Pool": 1,
"Last Share Time": 1541144887,
"Total MH": 2849.3488,
"Diff1 Work": 0.67822266,
"Work Utility": 0.048,
"Difficulty Accepted": 0.65625,
"Difficulty Rejected": 0,
"Difficulty Stale": 0,
"Last Share Difficulty": 0.00390625,
"Last Valid Work": 1541144891,
"Device Hardware%": 0.1438,
"Device Rejected%": 0
}
],
"id": 1
}
],
"pools": [
{
"STATUS": [
{
"STATUS": "S",
"When": 1541144892,
"Code": 7,
"Msg": "2 Pool(s)",
"Description": "bfgminer 5.4.2"
}
],
"POOLS": [
{
"POOL": 0,
"URL": "stratum+tcp://us.litecoinpool.org:3333",
"Status": "Alive",
"Priority": 1,
"Quota": 1,
"Mining Goal": "default",
"Long Poll": "N",
"Getworks": 3,
"Accepted": 0,
"Rejected": 0,
"Works": 0,
"Discarded": 0,
"Stale": 0,
"Get Failures": 0,
"Remote Failures": 0,
"User": "jstefanop.1",
"Last Share Time": 0,
"Diff1 Shares": 0,
"Proxy": "",
"Difficulty Accepted": 0,
"Difficulty Rejected": 0,
"Difficulty Stale": 0,
"Last Share Difficulty": 0,
"Has Stratum": true,
"Stratum Active": false,
"Stratum URL": "",
"Best Share": 0,
"Pool Rejected%": 0,
"Pool Stale%": 0
},
{
"POOL": 1,
"URL": "stratum+tcp://us.litecoinpool.org:3333",
"Status": "Alive",
"Priority": 0,
"Quota": 1,
"Mining Goal": "default",
"Long Poll": "N",
"Getworks": 27,
"Accepted": 168,
"Rejected": 0,
"Works": 30,
"Discarded": 78,
"Stale": 0,
"Get Failures": 0,
"Remote Failures": 0,
"User": "jstefanop.1",
"Last Share Time": 1541144887,
"Diff1 Shares": 0.67822266,
"Proxy": "",
"Difficulty Accepted": 0.65625,
"Difficulty Rejected": 0,
"Difficulty Stale": 0,
"Last Share Difficulty": 0.00390625,
"Has Stratum": true,
"Stratum Active": true,
"Stratum URL": "us.litecoinpool.org",
"Best Share": 1.03726997,
"Pool Rejected%": 0,
"Pool Stale%": 0
}
],
"id": 1
}
],
"id": 1
}
const net = require('net');
const PORT = 4028;
const HOST = 'localhost';
class Server {
constructor(port, address) {
this.port = port || PORT;
this.address = address || HOST;
this.init();
}
init() {
let server = this;
let onClientConnected = (sock) => {
let clientName = `${sock.remoteAddress}:${sock.remotePort}`;
console.log(`new client connected: ${clientName}`);
sock.on('data', (data) => {
console.log(`${clientName} Says: ${data}`);
sock.write(JSON.stringify(sampleData));
// sock.write('exit');
});
sock.on('close', () => {
console.log(`connection from ${clientName} closed`);
});
sock.on('error', (err) => {
console.log(`Connection ${clientName} error: ${err.message}`);
});
}
server.connection = net.createServer(onClientConnected);
server.connection.listen(PORT, HOST, function() {
console.log(`Server started at: ${HOST}:${PORT}`);
});
}
}
new Server();

View File

@ -24,6 +24,7 @@
"express": "^4.16.4",
"knex": "^0.15.2",
"luxon": "^1.4.4",
"normalize-object": "^1.1.5",
"sqlite3": "^4.0.3"
},
"devDependencies": {

27
src/app/minerClient.js Normal file
View File

@ -0,0 +1,27 @@
'use strict';
const net = require('net');
const PORT = 4028;
const HOST = 'localhost';
class Client {
constructor(port, address) {
this.socket = new net.Socket();
this.address = address || HOST;
this.port = port || PORT;
this.init();
}
init() {
var client = this;
client.socket.connect(client.port, client.address, () => {
console.log(`Client connected to: ${client.address} : ${client.port}`);
});
client.socket.on('close', () => {
console.log('Client closed');
});
}
}
module.exports = Client;

View File

@ -0,0 +1,13 @@
module.exports.typeDefs = `
type Query {
Miner: MinerActions
}
`
module.exports.resolvers = {
Query: {
Miner () {
return {}
}
}
}

View File

@ -0,0 +1,13 @@
module.exports.typeDefs = `
type MinerActions {
restart: EmptyOutput!
}
`
module.exports.resolvers = {
MinerActions: {
restart (root, args, { dispatch }) {
return dispatch('api/miner/restart')
}
}
}

View File

@ -0,0 +1,13 @@
module.exports.typeDefs = `
type MinerActions {
start: EmptyOutput!
}
`
module.exports.resolvers = {
MinerActions: {
start (root, args, { dispatch }) {
return dispatch('api/miner/start')
}
}
}

View File

@ -0,0 +1,142 @@
module.exports.typeDefs = `
type MinerActions {
stats: MinerStatsOutput!
}
type MinerStatsOutput {
result: MinerStatsResult
error: Error
}
type MinerStatsResult {
stats: MinerStats!
}
type MinerStats {
summary: MinerStatsSummary
devs: MinerStatsDevs
pools: MinerStatsPools
}
type MinerStatsSummary {
status: MinerStatsStatus
data: MinerStatsSummaryData
}
type MinerStatsDevs {
status: MinerStatsStatus
data: MinerStatsDevsData
}
type MinerStatsPools {
status: MinerStatsStatus
data: MinerStatsPoolsData
}
type MinerStatsStatus {
status: String
when: Int
code: Int
msg: String
description: String
}
type MinerStatsSummaryData {
elapsed: Int
mHSAv: Float
mHS20s: Float
foundBlocks: Int
getworks: Int
accepted: Int
rejected: Int
hardwareErrors: Int
utility: Float
discarded: Int
stale: Int
getFailures: Int
localWork: Int
remoteFailures: Int
networkBlocks: Int
totalMH: Float
diff1Work: Float
workUtility: Float
difficultyAccepted: Float
difficultyRejected: Int
difficultyStale: Int
bestShare: Float
deviceHardware: Float
deviceRejected: Int
poolRejected: Int
poolStale: Int
lastGetwork: Int
}
type MinerStatsDevsData {
pga: Int
name: String
id: Int
enabled: String
status: String
deviceElapsed: Int
mHSAv: Float
mHS20s: Float
mHSRolling: Float
accepted: Int
rejected: Int
hardwareErrors: Int
utility: Float
stale: Int
lastSharePool: Int
lastShareTime: Int
totalMH: Float
diff1Work: Float
workUtility: Float
difficultyAccepted: Float
difficultyRejected: Int
difficultyStale: Int
lastShareDifficulty: Float
lastValidWork: Int
deviceHardware: Float
deviceRejected: Int
}
type MinerStatsPoolsData {
pool: Int
url: String
status: String
priority: Int
quota: Int
miningGoal: String
longPoll: String
getworks: Int
accepted: Int
rejected: Int
works: Int
discarded: Int
stale: Int
getFailures: Int
remoteFailures: Int
user: String
lastShareTime: Int
diff1Shares: Int
proxy: String
difficultyAccepted: Int
difficultyRejected: Int
difficultyStale: Int
lastShareDifficulty: Int
hasStratum: Boolean
stratumActive: Boolean
stratumURL: String
bestShare: Int
poolRejected: Int
poolStale: Int
}
`
module.exports.resolvers = {
MinerActions: {
stats (root, args, { dispatch }) {
return dispatch('api/miner/stats')
}
}
}

View File

@ -0,0 +1,13 @@
module.exports.typeDefs = `
type MinerActions {
stop: EmptyOutput!
}
`
module.exports.resolvers = {
MinerActions: {
stop (root, args, { dispatch }) {
return dispatch('api/miner/stop')
}
}
}

View File

@ -7,9 +7,11 @@ module.exports.typeDefs = `
enum TemperatureUnit { f, c }
type Settings {
id: Int!
createdAt: String!
minerMode: MinerMode!
voltage: Float!,
frequency: Int!,
voltage: Float!
frequency: Int!
fan: Int!
connectedWifi: String
leftSidebarVisibility: Boolean!

View File

@ -0,0 +1,22 @@
module.exports.typeDefs = `
type SettingsActions {
list: SettingListOutput!
}
type SettingListOutput {
result: SettingListResult
error: Error
}
type SettingListResult {
settings: [Settings!]!
}
`
module.exports.resolvers = {
SettingsActions: {
list (root, args, { dispatch }) {
return dispatch('api/settings/list', args.input)
}
}
}

View File

@ -0,0 +1,9 @@
const { exec } = require('child_process')
module.exports = ({ define }) => {
define('restart', async (payload, { knex, errors, utils }) => {
exec('sudo systemctl restart bfgminer')
}, {
auth: true
})
}

View File

@ -0,0 +1,9 @@
const { exec } = require('child_process')
module.exports = ({ define }) => {
define('start', async (payload, { knex, errors, utils }) => {
exec('sudo systemctl start bfgminer')
}, {
auth: true
})
}

View File

@ -0,0 +1,59 @@
const { join } = require('path')
const { exec } = require('child_process')
const normalize = require('normalize-object')
const Client =require('../../../app/minerClient');
module.exports = ({ define }) => {
define('stats', async (payload, { knex, errors, utils }) => {
const stats = await getMinerStats()
return { stats }
}, {
auth: true
})
}
function getMinerStats () {
const client = new Client();
return new Promise((resolve, reject) => {
client.socket.write('{"command":"summary+devs+pools"}');
client.socket.on('data', (data) => {
let received = data.toString('utf8').trim();
try {
received = JSON.parse(received);
const summary = (received.summary && received.summary[0]) ? received.summary[0] : null;
const devs = (received.devs && received.devs[0]) ? received.devs[0] : null;
const pools = (received.pools && received.pools[0]) ? received.pools[0] : null;
let results = {
summary: {
status: (summary.STATUS && summary.STATUS[0]) ? summary.STATUS[0] : null,
data: (summary.SUMMARY && summary.SUMMARY[0]) ? summary.SUMMARY[0] : null
},
devs: {
status: (devs.STATUS && devs.STATUS[0]) ? devs.STATUS[0] : null,
data: (devs.DEVS && devs.DEVS[0]) ? devs.DEVS[0] : null
},
pools: {
status: (pools.STATUS && pools.STATUS[0]) ? pools.STATUS[0] : null,
data: (pools.POOLS && pools.POOLS[0]) ? pools.POOLS[0] : null
}
}
results = normalize(results, 'camel');
resolve(results)
} catch (err) {
reject(err);
}
client.socket.destroy();
});
client.socket.on('error', (err) => {
reject(err);
});
});
}

View File

@ -0,0 +1,9 @@
const { exec } = require('child_process')
module.exports = ({ define }) => {
define('stop', async (payload, { knex, errors, utils }) => {
exec('sudo systemctl stop bfgminer')
}, {
auth: true
})
}

View File

@ -0,0 +1,48 @@
module.exports = ({ define }) => {
define('list', async ({
where = {},
one,
forUpdate
}, {
context: { trx } = {},
knex
}) => {
const readQ = (trx || knex)('settings')
if (where.id) {
readQ.where('id', where.id)
}
readQ.select(
'id',
'created_at as createdAt',
'miner_mode as minerMode',
'voltage',
'frequency',
'fan',
'connected_wifi as connectedWifi',
'left_sidebar_visibility as leftSidebarVisibility',
'left_sidebar_extended as leftSidebarExtended',
'right_sidebar_visibility as rightSidebarVisibility',
'temperature_unit as temperatureUnit'
)
readQ.orderBy('created_at', 'desc')
readQ.limit(10)
if (forUpdate) {
readQ.forUpdate()
}
const items = await readQ
if (one) {
return items[0] || null
}
return {
items
}
})
}

View File

@ -0,0 +1,10 @@
module.exports = ({ define }) => {
define('list', async (payload, { dispatch, errors, utils }) => {
const { items: settings } = await dispatch('api/settings/collection/list', {})
return {
settings
}
}, {
auth: true
})
}

View File

@ -367,6 +367,11 @@ call-me-maybe@^1.0.1:
resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
case@^1.4.1:
version "1.5.5"
resolved "https://registry.yarnpkg.com/case/-/case-1.5.5.tgz#8e832ef0329609a4f3198dee37c029645dc27c72"
integrity sha512-tQm8bxc8L9Dk/6FGhtBtV89rrRzqytUbdLqGZxmGwYKqeAD0VmLc8kYSqm0GXOTsf9r1tc0bzq+CDLqtrjiuHw==
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@ -1945,6 +1950,15 @@ nopt@^4.0.1:
abbrev "1"
osenv "^0.1.4"
normalize-object@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/normalize-object/-/normalize-object-1.1.5.tgz#c8f767851d04d6f28ef863af61ff083ba24affee"
integrity sha1-yPdnhR0E1vKO+GOvYf8IO6JK/+4=
dependencies:
case "^1.4.1"
underscore "^1.7.0"
underscore.string "^3.3.4"
npm-bundled@^1.0.1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979"
@ -2602,6 +2616,11 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
sprintf-js@^1.0.3:
version "1.1.1"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c"
integrity sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=
sqlite3@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-4.0.3.tgz#da8c167a87941657fd22e27b248aa371e192b715"
@ -2818,6 +2837,19 @@ unc-path-regex@^0.1.2:
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo=
underscore.string@^3.3.4:
version "3.3.5"
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023"
integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==
dependencies:
sprintf-js "^1.0.3"
util-deprecate "^1.0.2"
underscore@^1.7.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
union-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
@ -2865,7 +2897,7 @@ use@^3.1.0:
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
util-deprecate@~1.0.1:
util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=