Merge hashrate and difficulty into one chart

This commit is contained in:
nymkappa 2022-02-22 15:50:14 +09:00
parent 98e0e1e9c1
commit 83a382a0cb
No known key found for this signature in database
GPG Key ID: E155910B16E8BD04
6 changed files with 148 additions and 74 deletions

View File

@ -42,9 +42,7 @@ class Mining {
}); });
poolsStatistics['pools'] = poolsStats; poolsStatistics['pools'] = poolsStats;
poolsStatistics['oldestIndexedBlockTimestamp'] = await BlocksRepository.$oldestBlockTimestamp();
const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp());
poolsStatistics['oldestIndexedBlockTimestamp'] = oldestBlock.getTime();
const blockCount: number = await BlocksRepository.$blockCount(null, interval); const blockCount: number = await BlocksRepository.$blockCount(null, interval);
poolsStatistics['blockCount'] = blockCount; poolsStatistics['blockCount'] = blockCount;
@ -79,26 +77,14 @@ class Mining {
* Return the historical difficulty adjustments and oldest indexed block timestamp * Return the historical difficulty adjustments and oldest indexed block timestamp
*/ */
public async $getHistoricalDifficulty(interval: string | null): Promise<object> { public async $getHistoricalDifficulty(interval: string | null): Promise<object> {
const difficultyAdjustments = await BlocksRepository.$getBlocksDifficulty(interval); return await BlocksRepository.$getBlocksDifficulty(interval);
const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp());
return {
adjustments: difficultyAdjustments,
oldestIndexedBlockTimestamp: oldestBlock.getTime(),
};
} }
/** /**
* Return the historical hashrates and oldest indexed block timestamp * Return the historical hashrates and oldest indexed block timestamp
*/ */
public async $getHistoricalHashrates(interval: string | null): Promise<object> { public async $getHistoricalHashrates(interval: string | null): Promise<object> {
const hashrates = await HashratesRepository.$get(interval); return await HashratesRepository.$get(interval);
const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp());
return {
hashrates: hashrates,
oldestIndexedBlockTimestamp: oldestBlock.getTime(),
};
} }
/** /**

View File

@ -187,12 +187,11 @@ class BlocksRepository {
* Get the oldest indexed block * Get the oldest indexed block
*/ */
public async $oldestBlockTimestamp(): Promise<number> { public async $oldestBlockTimestamp(): Promise<number> {
const query = `SELECT blockTimestamp const query = `SELECT UNIX_TIMESTAMP(blockTimestamp) as blockTimestamp
FROM blocks FROM blocks
ORDER BY height ORDER BY height
LIMIT 1;`; LIMIT 1;`;
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(query); const [rows]: any[] = await connection.query(query);

View File

@ -588,11 +588,18 @@ class Routes {
public async $getHistoricalHashrate(req: Request, res: Response) { public async $getHistoricalHashrate(req: Request, res: Response) {
try { try {
const stats = await mining.$getHistoricalHashrates(req.params.interval ?? null); const hashrates = await mining.$getHistoricalHashrates(req.params.interval ?? null);
const difficulty = await mining.$getHistoricalDifficulty(req.params.interval ?? null);
const oldestIndexedBlockTimestamp = await BlocksRepository.$oldestBlockTimestamp();
console.log(oldestIndexedBlockTimestamp);
res.header('Pragma', 'public'); res.header('Pragma', 'public');
res.header('Cache-control', 'public'); res.header('Cache-control', 'public');
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
res.json(stats); res.json({
oldestIndexedBlockTimestamp: oldestIndexedBlockTimestamp,
hashrates: hashrates,
difficulty: difficulty,
});
} catch (e) { } catch (e) {
res.status(500).send(e instanceof Error ? e.message : e); res.status(500).send(e instanceof Error ? e.message : e);
} }

View File

@ -59,7 +59,7 @@ export class DifficultyChartComponent implements OnInit {
}), }),
map(data => { map(data => {
const availableTimespanDay = ( const availableTimespanDay = (
(new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp / 1000) (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp)
) / 3600 / 24; ) / 3600 / 24;
const tableData = []; const tableData = [];

View File

@ -23,7 +23,7 @@ import { selectPowerOfTen } from 'src/app/bitcoin.utils';
}) })
export class HashrateChartComponent implements OnInit { export class HashrateChartComponent implements OnInit {
@Input() widget: boolean = false; @Input() widget: boolean = false;
@Input() right: number | string = 10; @Input() right: number | string = 45;
@Input() left: number | string = 75; @Input() left: number | string = 75;
radioGroupForm: FormGroup; radioGroupForm: FormGroup;
@ -45,7 +45,7 @@ export class HashrateChartComponent implements OnInit {
private apiService: ApiService, private apiService: ApiService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
) { ) {
this.seoService.setTitle($localize`:@@mining.hashrate:hashrate`); this.seoService.setTitle($localize`:@@mining.hashrate-difficulty:Hashrate and Difficulty`);
this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' });
this.radioGroupForm.controls.dateSpan.setValue('1y'); this.radioGroupForm.controls.dateSpan.setValue('1y');
} }
@ -57,17 +57,54 @@ export class HashrateChartComponent implements OnInit {
switchMap((timespan) => { switchMap((timespan) => {
return this.apiService.getHistoricalHashrate$(timespan) return this.apiService.getHistoricalHashrate$(timespan)
.pipe( .pipe(
tap(data => { map((data: any) => {
this.prepareChartOptions(data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate])); const diffFixed = [];
diffFixed.push({
timestamp: data.hashrates[0].timestamp,
difficulty: data.difficulty[0].difficulty
});
let diffIndex = 1;
let hashIndex = 0;
while (hashIndex < data.hashrates.length) {
if (diffIndex >= data.difficulty.length) {
while (hashIndex < data.hashrates.length) {
diffFixed.push({
timestamp: data.hashrates[hashIndex].timestamp,
difficulty: data.difficulty[data.difficulty.length - 1].difficulty
});
++hashIndex;
}
break;
}
while (data.hashrates[hashIndex].timestamp < data.difficulty[diffIndex].timestamp) {
diffFixed.push({
timestamp: data.hashrates[hashIndex].timestamp,
difficulty: data.difficulty[diffIndex - 1].difficulty
});
++hashIndex;
}
++diffIndex;
}
data.difficulty = diffFixed;
return data;
}),
tap((data: any) => {
this.prepareChartOptions({
hashrates: data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate]),
difficulty: data.difficulty.map(val => [val.timestamp * 1000, val.difficulty])
});
this.isLoading = false; this.isLoading = false;
}), }),
map(data => { map((data: any) => {
const availableTimespanDay = ( const availableTimespanDay = (
(new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp / 1000) (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp)
) / 3600 / 24; ) / 3600 / 24;
return { return {
availableTimespanDay: availableTimespanDay, availableTimespanDay: availableTimespanDay,
data: data.hashrates
}; };
}), }),
); );
@ -78,27 +115,25 @@ export class HashrateChartComponent implements OnInit {
prepareChartOptions(data) { prepareChartOptions(data) {
this.chartOptions = { this.chartOptions = {
color: new graphic.LinearGradient(0, 0, 0, 0.65, [ color: [
{ offset: 0, color: '#F4511E' }, new graphic.LinearGradient(0, 0, 0, 0.65, [
{ offset: 0.25, color: '#FB8C00' }, { offset: 0, color: '#F4511E' },
{ offset: 0.5, color: '#FFB300' }, { offset: 0.25, color: '#FB8C00' },
{ offset: 0.75, color: '#FDD835' }, { offset: 0.5, color: '#FFB300' },
{ offset: 1, color: '#7CB342' } { offset: 0.75, color: '#FDD835' },
]), { offset: 1, color: '#7CB342' }
]),
'#D81B60',
],
grid: { grid: {
right: this.right, right: this.right,
left: this.left, left: this.left,
}, },
title: {
text: this.widget ? '' : $localize`:@@mining.hashrate:Hashrate`,
left: 'center',
textStyle: {
color: '#FFF',
},
},
tooltip: { tooltip: {
show: true,
trigger: 'axis', trigger: 'axis',
axisPointer: {
type: 'line'
},
backgroundColor: 'rgba(17, 19, 31, 1)', backgroundColor: 'rgba(17, 19, 31, 1)',
borderRadius: 4, borderRadius: 4,
shadowColor: 'rgba(0, 0, 0, 0.5)', shadowColor: 'rgba(0, 0, 0, 0.5)',
@ -106,44 +141,91 @@ export class HashrateChartComponent implements OnInit {
color: '#b1b1b1', color: '#b1b1b1',
}, },
borderColor: '#000', borderColor: '#000',
formatter: params => {
return `<b style="color: white">${params[0].axisValueLabel}</b><br>
${params[0].marker} ${formatNumber(params[0].value[1], this.locale, '1.0-0')} H/s`
}
},
axisPointer: {
type: 'line',
}, },
xAxis: { xAxis: {
type: 'time', type: 'time',
splitNumber: this.isMobile() ? 5 : 10, splitNumber: this.isMobile() ? 5 : 10,
}, },
yAxis: { legend: {
type: 'value', data: [
axisLabel: { {
formatter: (val) => { name: 'Hashrate',
const selectedPowerOfTen: any = selectPowerOfTen(val); inactiveColor: 'rgb(110, 112, 121)',
const newVal = val / selectedPowerOfTen.divider; textStyle: {
return `${newVal} ${selectedPowerOfTen.unit}H/s` color: 'white',
},
icon: 'roundRect',
itemStyle: {
color: '#FFB300',
},
},
{
name: 'Difficulty',
inactiveColor: 'rgb(110, 112, 121)',
textStyle: {
color: 'white',
},
icon: 'roundRect',
itemStyle: {
color: '#D81B60',
}
},
],
},
yAxis: [
{
type: 'value',
name: 'Hashrate',
axisLabel: {
color: 'rgb(110, 112, 121)',
formatter: (val) => {
const selectedPowerOfTen: any = selectPowerOfTen(val);
const newVal = val / selectedPowerOfTen.divider;
return `${newVal} ${selectedPowerOfTen.unit}H/s`
}
},
splitLine: {
show: false,
} }
}, },
splitLine: { {
type: 'value',
name: 'Difficulty',
position: 'right',
axisLabel: {
color: 'rgb(110, 112, 121)',
formatter: (val) => {
const selectedPowerOfTen: any = selectPowerOfTen(val);
const newVal = val / selectedPowerOfTen.divider;
return `${newVal} ${selectedPowerOfTen.unit}`
}
},
splitLine: {
show: false,
}
}
],
series: [
{
name: 'Hashrate',
showSymbol: false,
data: data.hashrates,
type: 'line',
lineStyle: { lineStyle: {
type: 'dotted', width: 2,
color: '#ffffff66', },
opacity: 0.25, },
{
yAxisIndex: 1,
name: 'Difficulty',
showSymbol: false,
data: data.difficulty,
type: 'line',
lineStyle: {
width: 3,
} }
}, }
}, ],
series: {
showSymbol: false,
data: data,
type: 'line',
smooth: false,
lineStyle: {
width: 2,
},
},
dataZoom: this.widget ? null : [{ dataZoom: this.widget ? null : [{
type: 'inside', type: 'inside',
realtime: true, realtime: true,

View File

@ -82,7 +82,7 @@ export class MiningService {
}); });
const availableTimespanDay = ( const availableTimespanDay = (
(new Date().getTime() / 1000) - (stats.oldestIndexedBlockTimestamp / 1000) (new Date().getTime() / 1000) - (stats.oldestIndexedBlockTimestamp)
) / 3600 / 24; ) / 3600 / 24;
return { return {