Added Ckpool stats

This commit is contained in:
Michele Marcucci 2024-02-05 14:38:31 +01:00
parent e1b6389a9c
commit 664d4a8ed4
3 changed files with 206 additions and 61 deletions

View File

@ -2,7 +2,6 @@ const path = require('path')
const express = require('express')
const cors = require('cors');
const graphqlApp = require('./graphqlApp')
const buildPath = path.join(__dirname, '../../apolloui/build');
const app = express()
@ -10,11 +9,4 @@ app.use(cors());
app.use('/api/graphql', graphqlApp)
if (process.env.NODE_ENV === 'production') app.use(express.static(buildPath));
app.get('*', function (req, res) {
if (process.env.NODE_ENV === 'production') res.sendFile(buildPath + '/index.html');
else res.json({ message: 'API DEV server running' })
});
module.exports = app

View File

@ -23,6 +23,7 @@ module.exports.typeDefs = `
temperature: MinerStatsTemperature
slots: MinerStatsSlots
slaves: [MinerStatsSlave]
ckpool: MinerStatsCkpool
}
type MinerStatsVersion {
@ -160,7 +161,7 @@ module.exports.typeDefs = `
errorRate: Float
chipRestarts: Int
wattPerGHs: Float
tmpAlert: [MinerStatsSlotAlert],
tmpAlert: [MinerStatsSlotAlert]
osc: Int
oscStopChip: String
}
@ -181,12 +182,68 @@ module.exports.typeDefs = `
ping: Int
}
`
type MinerStatsCkpool {
pool: MinerStatsCkpoolPool
users: MinerStatsCkpoolUsers
}
type MinerStatsCkpoolPool {
runtime: Int
lastupdate: Int
Users: Int
Workers: Int
Idle: Int
Disconnected: Int
hashrate1m: String
hashrate5m: String
hashrate15m: String
hashrate1hr: String
hashrate6hr: String
hashrate1d: String
hashrate7d: String
diff: Int
accepted: Int
rejected: Int
bestshare: Int
SPS1m: Float
SPS5m: Float
SPS15m: Float
SPS1h: Float
}
type MinerStatsCkpoolUsers {
hashrate1m: String
hashrate5m: String
hashrate1hr: String
hashrate1d: String
hashrate7d: String
lastshare: Int
workers: Int
shares: Int
bestshare: Float
bestever: Int
authorised: Int
worker: [MinerStatsCkpoolWorker]
}
type MinerStatsCkpoolWorker {
workername: String,
hashrate1m: String,
hashrate5m: String,
hashrate1hr: String,
hashrate1d: String,
hashrate7d: String,
lastshare: Int,
shares: Int,
bestshare: Float,
bestever: Int
}
`;
module.exports.resolvers = {
MinerActions: {
stats (root, args, { dispatch }) {
return dispatch('api/miner/stats')
stats(root, args, { dispatch }) {
return dispatch('api/miner/stats');
}
}
}
};

View File

@ -1,73 +1,169 @@
const { join } = require('path')
const { exec } = require('child_process')
const fs = require('fs').promises
const path = require('path')
const _ = require('lodash')
const moment = require('moment')
const { join } = require('path');
const { exec } = require('child_process');
const fs = require('fs').promises;
const path = require('path');
const _ = require('lodash');
const moment = require('moment');
const { existsSync } = require('fs');
const c = require('config');
module.exports = ({ define }) => {
define('stats', async (payload, { knex, errors, utils }) => {
const stats = await getMinerStats(errors)
return { stats }
}, {
auth: true
})
define(
'stats',
async (payload, { knex, errors, dispatch }) => {
const settings = await dispatch('api/settings/collection/read');
const { items: pools } = await dispatch('api/pools/collection/read', {});
const stats = await getMinerStats(errors, settings, pools);
return { stats };
},
{
auth: true,
}
);
};
const parseFileToJsonArray = async (filePath) => {
try {
// Read the file content
const fileContent = await fs.readFile(filePath, 'utf8');
// Divide the file content into lines
const lines = fileContent.split('\n');
const allKeys = {};
// Analyze each line
lines.forEach(line => {
if (line.trim() !== '') {
try {
const jsonObject = JSON.parse(line);
// Add the keys to the allKeys object
Object.entries(jsonObject).forEach(([key, value]) => {
if (!allKeys[key]) {
allKeys[key] = null;
}
allKeys[key] = value;
});
} catch (error) {
console.error(`Error during the parsing of the line: ${error.message}`);
}
}
});
return allKeys;
} catch (error) {
console.error(`Error during the reading of the file: ${error.message}`);
return {};
}
}
async function getMinerStats (errors) {
const getMinerStats = async (errors, settings, pools) => {
return new Promise((resolve, reject) => {
(async () => {
(async () => {
try {
const statsDir = path.resolve(__dirname, '../../../../backend/apollo-miner/');
const statsDir = path.resolve(
__dirname,
'../../../../backend/apollo-miner/'
);
const statsFilePattern = 'apollo-miner.*';
let statsFiles = await fs.readdir(statsDir);
statsFiles = _.filter(statsFiles, (f) => { return f.match(statsFilePattern) })
statsFiles = _.filter(statsFiles, (f) => {
return f.match(statsFilePattern);
});
// Get ckpool data
let ckpoolData = null;
if (settings?.nodeEnableSoloMining) {
const poolUsername = pools[0] && pools[0].username;
const ckpoolPoolStatsFile = path.resolve(
__dirname,
'../../../../backend/ckpool/logs/pool/pool.status'
);
const ckpoolUsersStatsFile = path.resolve(
__dirname,
`../../../../backend/ckpool/logs/users/${poolUsername}`
);
if (
existsSync(ckpoolPoolStatsFile) &&
existsSync(ckpoolUsersStatsFile)
) {
await Promise.all([
(async () => {
let ckpoolPoolData = await parseFileToJsonArray(ckpoolPoolStatsFile);
let ckpoolUsersData = await fs.readFile(ckpoolUsersStatsFile, 'utf8');
ckpoolUsersData = JSON.parse(ckpoolUsersData);
ckpoolData = {
pool: ckpoolPoolData,
users: ckpoolUsersData,
};
})(),
]);
}
}
let stats = [];
await Promise.all(statsFiles.map(async (file) => {
const data = await fs.readFile(`${statsDir}/${file}`);
let received = data.toString('utf8').trim();
// JSON from miner is dirty, clean it
received = received
.replace(/\-nan/g, '0')
.replace(/[^\x00-\x7F]/g, '')
.replace('}{', '},{')
.replace(String.fromCharCode(0), '')
.replace(/[^\}]+$/, '')
await Promise.all(
statsFiles.map(async (file) => {
const data = await fs.readFile(`${statsDir}/${file}`);
let received = data.toString('utf8').trim();
// JSON from miner is dirty, clean it
received = received
.replace(/\-nan/g, '0')
.replace(/[^\x00-\x7F]/g, '')
.replace('}{', '},{')
.replace(String.fromCharCode(0), '')
.replace(/[^\}]+$/, '');
received = JSON.parse(received);
received = JSON.parse(received);
received.uuid = file.replace('apollo-miner.', '');
received.uuid = file.replace('apollo-miner.', '');
received.master.intervals = _.mapKeys(received.master.intervals, (value, name) => {
return `int_${name}`
});
received.master.intervals = _.mapKeys(
received.master.intervals,
(value, name) => {
return `int_${name}`;
}
);
received.pool.intervals = _.mapKeys(received.pool.intervals, (value, name) => {
return `int_${name}`
});
received.pool.intervals = _.mapKeys(
received.pool.intervals,
(value, name) => {
return `int_${name}`;
}
);
received.fans = _.mapKeys(received.fans, (value, name) => {
return `int_${name}`
});
received.fans = _.mapKeys(received.fans, (value, name) => {
return `int_${name}`;
});
received.slots = _.mapKeys(received.slots, (value, name) => {
return `int_${name}`
});
received.slots = _.mapKeys(received.slots, (value, name) => {
return `int_${name}`;
});
// Hack to add timezone to miner date
let offset = new Date().getTimezoneOffset();
offset *= -1
received.date = moment(`${received.date}`, 'YYYY-MM-DD HH:mm:ss').utcOffset(offset).format();
// Hack to add timezone to miner date
let offset = new Date().getTimezoneOffset();
offset *= -1;
received.date = moment(`${received.date}`, 'YYYY-MM-DD HH:mm:ss')
.utcOffset(offset)
.format();
stats.push(received);
}));
if (ckpoolData) received.ckpool = ckpoolData;
resolve(stats)
stats.push(received);
})
);
resolve(stats);
} catch (err) {
reject(new errors.InternalError(err.toString()));
}
})()
})();
});
}