Add pie chart and rewrite the pool ranking component
This commit is contained in:
		
							parent
							
								
									18a63933fa
								
							
						
					
					
						commit
						0a267affaf
					
				@ -43,8 +43,8 @@ class Mining {
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    poolsStatistics["blockCount"] = blockCount;
 | 
			
		||||
    poolsStatistics["poolsStats"] = poolsStats;
 | 
			
		||||
    poolsStatistics["lastEstimatedHashrate"] = lastBlockHashrate;
 | 
			
		||||
    poolsStatistics["pools"] = poolsStats;
 | 
			
		||||
 | 
			
		||||
    return poolsStatistics;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ import { FeesBoxComponent } from './components/fees-box/fees-box.component';
 | 
			
		||||
import { DashboardComponent } from './dashboard/dashboard.component';
 | 
			
		||||
import { DifficultyComponent } from './components/difficulty/difficulty.component';
 | 
			
		||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
 | 
			
		||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
 | 
			
		||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
 | 
			
		||||
  faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons';
 | 
			
		||||
import { ApiDocsComponent } from './components/docs/api-docs.component';
 | 
			
		||||
import { DocsComponent } from './components/docs/docs.component';
 | 
			
		||||
@ -145,6 +145,7 @@ export class AppModule {
 | 
			
		||||
    library.addIcons(faTv);
 | 
			
		||||
    library.addIcons(faTachometerAlt);
 | 
			
		||||
    library.addIcons(faCubes);
 | 
			
		||||
    library.addIcons(faHammer);
 | 
			
		||||
    library.addIcons(faCogs);
 | 
			
		||||
    library.addIcons(faThList);
 | 
			
		||||
    library.addIcons(faList);
 | 
			
		||||
 | 
			
		||||
@ -31,8 +31,8 @@
 | 
			
		||||
      <li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" id="btn-home">
 | 
			
		||||
        <a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li class="nav-item" routerLinkActive="active" id="btn-blocks">
 | 
			
		||||
        <a class="nav-link" [routerLink]="['/blocks' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" i18n-title="master-page.blocks" title="Blocks"></fa-icon></a>
 | 
			
		||||
      <li class="nav-item" routerLinkActive="active" id="btn-pools">
 | 
			
		||||
        <a class="nav-link" [routerLink]="['/mining/pools' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'hammer']" [fixedWidth]="true" i18n-title="master-page.pools" title="Pools"></fa-icon></a>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li class="nav-item" routerLinkActive="active" id="btn-graphs">
 | 
			
		||||
        <a class="nav-link" [routerLink]="['/graphs' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'chart-area']" [fixedWidth]="true" i18n-title="master-page.graphs" title="Graphs"></fa-icon></a>
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
<div class="container-xl">
 | 
			
		||||
  <div class="card-header">
 | 
			
		||||
    <i class="fa fa-area-chart"></i> <span i18n="mining.pools-by-vBytes">Pools</span>
 | 
			
		||||
    <form [formGroup]="radioGroupForm" class="formRadioGroup" (click)="savePoolsPreference()">
 | 
			
		||||
      <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
 | 
			
		||||
    <!-- <i class="fa fa-area-chart"></i> <span i18n="mining.pools-by-vBytes">Pools</span> -->
 | 
			
		||||
    <form [formGroup]="radioGroupForm" class="formRadioGroup">
 | 
			
		||||
      <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"
 | 
			
		||||
        (change)="onChangeWindowPreference($event)">
 | 
			
		||||
        <label ngbButtonLabel class="btn-primary btn-sm">
 | 
			
		||||
          <input ngbButton type="radio" [value]="'1d'" [routerLink]="['/pools' | relativeUrl]" fragment="1d"> 1D
 | 
			
		||||
        </label>
 | 
			
		||||
@ -37,6 +38,11 @@
 | 
			
		||||
    </form>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div style="height: 500px;" echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div>
 | 
			
		||||
  <div class="text-center loadingGraphs" *ngIf="isLoading">
 | 
			
		||||
    <div class="spinner-border text-light"></div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50">
 | 
			
		||||
    <thead>
 | 
			
		||||
      <th i18n="latest-blocks.height">Rank</th>
 | 
			
		||||
@ -45,23 +51,21 @@
 | 
			
		||||
      <th class="d-none d-md-block" i18n="latest-blocks.mined">Block Count (share)</th>
 | 
			
		||||
      <th i18n="latest-blocks.transactions">Empty Blocks (ratio)</th>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody *ngIf="pools$ | async as pools">
 | 
			
		||||
    <tbody *ngIf="(miningStatsEmitter$ | async) as miningStats">
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td>-</td>
 | 
			
		||||
        <td>All miners</td>
 | 
			
		||||
        <td>{{ pools["lastEstimatedHashrate"]}} PH/s</td>
 | 
			
		||||
        <td>{{ pools["blockCount"] }}</td>
 | 
			
		||||
        <td>{{ pools["totalEmptyBlock"] }} ({{ pools["totalEmptyBlockRatio"] }}%)</td>
 | 
			
		||||
        <td>{{ miningStats.lastEstimatedHashrate}} PH/s</td>
 | 
			
		||||
        <td>{{ miningStats.blockCount }}</td>
 | 
			
		||||
        <td>{{ miningStats.totalEmptyBlock }} ({{ miningStats.totalEmptyBlockRatio }}%)</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
      <tr *ngFor="let pool of miningStats.pools">
 | 
			
		||||
        <td>{{ pool.rank }}</td>
 | 
			
		||||
        <td><a href="{{ pool.link }}">{{ pool.name }}</a></td>
 | 
			
		||||
        <td>{{ pool.lastEstimatedHashrate }} PH/s</td>
 | 
			
		||||
        <td>{{ pool.blockCount }} ({{ pool.share }}%)</td>
 | 
			
		||||
        <td>{{ pool.emptyBlocks }} ({{ pool.emptyBlockRatio }}%)</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
      <ng-template ngFor let-pool [ngForOf]="pools['poolsStats']">
 | 
			
		||||
        <tr>
 | 
			
		||||
          <td>{{ pool.rank }}</td>
 | 
			
		||||
          <td><a href="{{ pool.link }}">{{ pool.name }}</a></td>
 | 
			
		||||
          <td>{{ pool.lastEstimatedHashrate }} PH/s</td>
 | 
			
		||||
          <td>{{ pool.blockCount }} ({{ pool.share }}%)</td>
 | 
			
		||||
          <td>{{ pool.emptyBlocks }} ({{ pool.emptyBlockRatio }}%)</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,103 +1,169 @@
 | 
			
		||||
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
 | 
			
		||||
import { FormBuilder, FormGroup } from '@angular/forms';
 | 
			
		||||
import { ActivatedRoute } from '@angular/router';
 | 
			
		||||
import { merge, Observable, ObservableInput, of } from 'rxjs';
 | 
			
		||||
import { map, share, switchMap, tap } from 'rxjs/operators';
 | 
			
		||||
import { PoolsStats } from 'src/app/interfaces/node-api.interface';
 | 
			
		||||
import { Component, OnInit, ChangeDetectionStrategy, OnChanges } from '@angular/core';
 | 
			
		||||
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
 | 
			
		||||
import { EChartsOption } from 'echarts';
 | 
			
		||||
import { BehaviorSubject, merge, of } from 'rxjs';
 | 
			
		||||
import { skip } from 'rxjs/operators';
 | 
			
		||||
import { MiningStats } from 'src/app/interfaces/node-api.interface';
 | 
			
		||||
import { StorageService } from 'src/app/services/storage.service';
 | 
			
		||||
import { ApiService } from '../../services/api.service';
 | 
			
		||||
import { MiningService } from '../../services/mining.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-pool-ranking',
 | 
			
		||||
  templateUrl: './pool-ranking.component.html',
 | 
			
		||||
  styles: [`
 | 
			
		||||
    .loadingGraphs {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 52%;
 | 
			
		||||
      left: calc(50% - 16px);
 | 
			
		||||
      z-index: 100;
 | 
			
		||||
    }
 | 
			
		||||
  `],
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
})
 | 
			
		||||
export class PoolRankingComponent implements OnInit {
 | 
			
		||||
  pools$: Observable<object>
 | 
			
		||||
 | 
			
		||||
  radioGroupForm: FormGroup;
 | 
			
		||||
export class PoolRankingComponent implements OnInit, OnChanges {
 | 
			
		||||
  poolsWindowPreference: string;
 | 
			
		||||
  radioGroupForm: FormGroup;
 | 
			
		||||
 | 
			
		||||
  miningStats!: MiningStats;
 | 
			
		||||
  miningStatsEmitter$ = new BehaviorSubject<MiningStats>(this.miningStats);
 | 
			
		||||
 | 
			
		||||
  isLoading = true;
 | 
			
		||||
  chartOptions: EChartsOption = {};
 | 
			
		||||
  chartInitOptions = {
 | 
			
		||||
    renderer: 'svg'
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private formBuilder: FormBuilder,
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
    private apiService: ApiService,
 | 
			
		||||
    private storageService: StorageService,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    private formBuilder: FormBuilder,
 | 
			
		||||
    private miningService: MiningService,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference').trim() : '2h';
 | 
			
		||||
    this.radioGroupForm = this.formBuilder.group({
 | 
			
		||||
      dateSpan: this.poolsWindowPreference
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Setup datespan triggers
 | 
			
		||||
    this.route.fragment.subscribe((fragment) => {
 | 
			
		||||
      if (['1d', '3d', '1w', '1m', '3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) {
 | 
			
		||||
        this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    merge(of(''), this.radioGroupForm.controls.dateSpan.valueChanges)
 | 
			
		||||
      .pipe(switchMap(() => this.onDateSpanChanged()))
 | 
			
		||||
      .subscribe((pools: any) => {
 | 
			
		||||
        console.log(pools);
 | 
			
		||||
    this.radioGroupForm = this.formBuilder.group({ dateSpan: this.poolsWindowPreference });
 | 
			
		||||
    this.radioGroupForm.controls.dateSpan.setValue(this.poolsWindowPreference);
 | 
			
		||||
 | 
			
		||||
    this.refreshMiningStats();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sleep = (ms: number) => new Promise(res => setTimeout(res, ms));
 | 
			
		||||
 | 
			
		||||
  refreshMiningStats() {
 | 
			
		||||
    return this.miningService.getMiningStats(this.getSQLInterval(this.poolsWindowPreference))
 | 
			
		||||
      .subscribe(async data => {
 | 
			
		||||
        this.miningStats = data;
 | 
			
		||||
        this.miningStatsEmitter$.next(this.miningStats);
 | 
			
		||||
        this.prepareChartOptions();
 | 
			
		||||
        this.isLoading = false;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    // Fetch initial mining pool data
 | 
			
		||||
    this.onDateSpanChanged();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnChanges() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  rendered() {
 | 
			
		||||
  onChangeWindowPreference(e) {
 | 
			
		||||
    this.poolsWindowPreference = e.target.value;
 | 
			
		||||
    this.isLoading = true;
 | 
			
		||||
    this.refreshMiningStats();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  savePoolsPreference() {
 | 
			
		||||
    this.storageService.setValue('poolsWindowPreference', this.radioGroupForm.controls.dateSpan.value);
 | 
			
		||||
    this.poolsWindowPreference = this.radioGroupForm.controls.dateSpan.value;
 | 
			
		||||
  }
 | 
			
		||||
  generatePoolsChartSerieData() {
 | 
			
		||||
    const poolShareThreshold = 0.5; // Do not draw pools which hashrate share is lower than that
 | 
			
		||||
    const data: object[] = [];
 | 
			
		||||
 | 
			
		||||
  onDateSpanChanged(): ObservableInput<any> {
 | 
			
		||||
    let interval: string;
 | 
			
		||||
    console.log(this.poolsWindowPreference);
 | 
			
		||||
    switch (this.poolsWindowPreference) {
 | 
			
		||||
      case '1d': interval = '1 DAY'; break;
 | 
			
		||||
      case '3d': interval = '3 DAY'; break;
 | 
			
		||||
      case '1w': interval = '1 WEEK'; break;
 | 
			
		||||
      case '1m': interval = '1 MONTH'; break;
 | 
			
		||||
      case '3m': interval = '3 MONTH'; break;
 | 
			
		||||
      case '6m': interval = '6 MONTH'; break;
 | 
			
		||||
      case '1y': interval = '1 YEAR'; break;
 | 
			
		||||
      case '2y': interval = '2 YEAR'; break;
 | 
			
		||||
      case '3y': interval = '3 YEAR'; break;
 | 
			
		||||
      case 'all': interval = '1000 YEAR'; break;
 | 
			
		||||
    }
 | 
			
		||||
    this.pools$ = this.apiService.listPools$(interval).pipe(map(res => this.computeMiningStats(res)));
 | 
			
		||||
    return this.pools$;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  computeMiningStats(stats: PoolsStats) {
 | 
			
		||||
    const totalEmptyBlock = Object.values(stats.poolsStats).reduce((prev, cur) => {
 | 
			
		||||
      return prev + cur.emptyBlocks;
 | 
			
		||||
    }, 0);
 | 
			
		||||
    const totalEmptyBlockRatio = (totalEmptyBlock / stats.blockCount * 100).toFixed(2);
 | 
			
		||||
    const poolsStats = stats.poolsStats.map((poolStat) => {
 | 
			
		||||
      return {
 | 
			
		||||
        share: (poolStat.blockCount / stats.blockCount * 100).toFixed(2),
 | 
			
		||||
        lastEstimatedHashrate: (poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate / Math.pow(10, 15)).toFixed(2),
 | 
			
		||||
        emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
 | 
			
		||||
        ...poolStat
 | 
			
		||||
    this.miningStats.pools.forEach((pool) => {
 | 
			
		||||
      if (parseFloat(pool.share) < poolShareThreshold) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      data.push({
 | 
			
		||||
        value: pool.lastEstimatedHashrate,
 | 
			
		||||
        name: pool.name,
 | 
			
		||||
        label: { color: '#FFFFFF' },
 | 
			
		||||
        tooltip: {
 | 
			
		||||
          formatter: () => {
 | 
			
		||||
            return `<u><b>${pool.name}</b></u><br>` +
 | 
			
		||||
              pool.lastEstimatedHashrate.toString() + ' PH/s (' + pool.share + `%)
 | 
			
		||||
              <br>(` + pool.blockCount.toString() + ` blocks)`;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      lastEstimatedHashrate: (stats.lastEstimatedHashrate / Math.pow(10, 15)).toFixed(2),
 | 
			
		||||
      blockCount: stats.blockCount,
 | 
			
		||||
      totalEmptyBlock: totalEmptyBlock,
 | 
			
		||||
      totalEmptyBlockRatio: totalEmptyBlockRatio,
 | 
			
		||||
      poolsStats: poolsStats,
 | 
			
		||||
  prepareChartOptions() {
 | 
			
		||||
    this.chartOptions = {
 | 
			
		||||
      title: {
 | 
			
		||||
        text: 'Hashrate distribution',
 | 
			
		||||
        subtext: 'Estimated from the # of blocks mined',
 | 
			
		||||
        left: 'center',
 | 
			
		||||
        textStyle: {
 | 
			
		||||
          color: '#FFFFFF',
 | 
			
		||||
        },
 | 
			
		||||
        subtextStyle: {
 | 
			
		||||
          color: '#CCCCCC',
 | 
			
		||||
          fontStyle: 'italic',
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      tooltip: {
 | 
			
		||||
        trigger: 'item'
 | 
			
		||||
      },
 | 
			
		||||
      series: [
 | 
			
		||||
        {
 | 
			
		||||
          name: 'Mining pool',
 | 
			
		||||
          type: 'pie',
 | 
			
		||||
          radius: ['30%', '70%'],
 | 
			
		||||
          data: this.generatePoolsChartSerieData(),
 | 
			
		||||
          labelLine: {
 | 
			
		||||
            lineStyle: {
 | 
			
		||||
              width: 2,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          label: {
 | 
			
		||||
            fontSize: 14,
 | 
			
		||||
          },
 | 
			
		||||
          itemStyle: {
 | 
			
		||||
            borderRadius: 5,
 | 
			
		||||
            borderWidth: 2,
 | 
			
		||||
            borderColor: '#000',
 | 
			
		||||
          },
 | 
			
		||||
          emphasis: {
 | 
			
		||||
            itemStyle: {
 | 
			
		||||
              borderWidth: 5,
 | 
			
		||||
              borderColor: '#000',
 | 
			
		||||
              borderRadius: 20,
 | 
			
		||||
              shadowBlur: 40,
 | 
			
		||||
              shadowOffsetX: 0,
 | 
			
		||||
              shadowColor: 'rgba(0, 0, 0, 0.75)'
 | 
			
		||||
            },
 | 
			
		||||
            labelLine: {
 | 
			
		||||
              lineStyle: {
 | 
			
		||||
                width: 3,
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getSQLInterval(uiInterval: string) {
 | 
			
		||||
    switch (uiInterval) {
 | 
			
		||||
      case '1d': return '1 DAY';
 | 
			
		||||
      case '3d': return '3 DAY';
 | 
			
		||||
      case '1w': return '1 WEEK';
 | 
			
		||||
      case '1m': return '1 MONTH';
 | 
			
		||||
      case '3m': return '3 MONTH';
 | 
			
		||||
      case '6m': return '6 MONTH';
 | 
			
		||||
      case '1y': return '1 YEAR';
 | 
			
		||||
      case '2y': return '2 YEAR';
 | 
			
		||||
      case '3y': return '3 YEAR';
 | 
			
		||||
      default: return '1000 YEAR';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -52,15 +52,29 @@ export interface LiquidPegs {
 | 
			
		||||
 | 
			
		||||
export interface ITranslators { [language: string]: string; }
 | 
			
		||||
 | 
			
		||||
export interface SinglePoolStats {
 | 
			
		||||
  pooldId: number,
 | 
			
		||||
  name: string,
 | 
			
		||||
  link: string,
 | 
			
		||||
  blockCount: number,
 | 
			
		||||
  emptyBlocks: number,
 | 
			
		||||
  rank: number,
 | 
			
		||||
  share: string,
 | 
			
		||||
  lastEstimatedHashrate: string,
 | 
			
		||||
  emptyBlockRatio: string,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface PoolsStats {
 | 
			
		||||
  poolsStats: {
 | 
			
		||||
    pooldId: number,
 | 
			
		||||
    name: string,
 | 
			
		||||
    link: string,
 | 
			
		||||
    blockCount: number,
 | 
			
		||||
    emptyBlocks: number,
 | 
			
		||||
    rank: number,
 | 
			
		||||
  }[],
 | 
			
		||||
  blockCount: number,
 | 
			
		||||
  lastEstimatedHashrate: number,
 | 
			
		||||
  pools: SinglePoolStats[],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MiningStats {
 | 
			
		||||
  lastEstimatedHashrate: string,
 | 
			
		||||
  blockCount: number,
 | 
			
		||||
  totalEmptyBlock: number,
 | 
			
		||||
  totalEmptyBlockRatio: string,
 | 
			
		||||
  pools: SinglePoolStats[],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								frontend/src/app/services/mining.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								frontend/src/app/services/mining.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
import { MiningStats, PoolsStats } from '../interfaces/node-api.interface';
 | 
			
		||||
import { ApiService } from '../services/api.service';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class MiningService {
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private apiService: ApiService,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  public getMiningStats(interval: string): Observable<MiningStats> {
 | 
			
		||||
    return this.apiService.listPools$(interval).pipe(
 | 
			
		||||
      map(pools => this.generateMiningStats(pools))
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private generateMiningStats(stats: PoolsStats) : MiningStats {
 | 
			
		||||
    const totalEmptyBlock = Object.values(stats.pools).reduce((prev, cur) => {
 | 
			
		||||
      return prev + cur.emptyBlocks;
 | 
			
		||||
    }, 0);
 | 
			
		||||
    const totalEmptyBlockRatio = (totalEmptyBlock / stats.blockCount * 100).toFixed(2);
 | 
			
		||||
    const poolsStats = stats.pools.map((poolStat) => {
 | 
			
		||||
      return {
 | 
			
		||||
        share: (poolStat.blockCount / stats.blockCount * 100).toFixed(2),
 | 
			
		||||
        lastEstimatedHashrate: (poolStat.blockCount / stats.blockCount * stats.lastEstimatedHashrate / Math.pow(10, 15)).toFixed(2),
 | 
			
		||||
        emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
 | 
			
		||||
        ...poolStat
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      lastEstimatedHashrate: (stats.lastEstimatedHashrate / Math.pow(10, 15)).toFixed(2),
 | 
			
		||||
      blockCount: stats.blockCount,
 | 
			
		||||
      totalEmptyBlock: totalEmptyBlock,
 | 
			
		||||
      totalEmptyBlockRatio: totalEmptyBlockRatio,
 | 
			
		||||
      pools: poolsStats,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user