Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
81f6a0b
Add scoreboard API
mutatrum Sep 16, 2025
6306d8c
Add scoreboard page
mutatrum Sep 16, 2025
2e56135
Fix difficultyPipe for M and greater
mutatrum Sep 16, 2025
fa4eaef
Print NVS stats at startup
mutatrum Sep 16, 2025
7ad71e1
Add persistent scoreboard
mutatrum Sep 16, 2025
8f8763b
Remove superfluous mutex create
mutatrum Sep 16, 2025
600f056
Remove newline
mutatrum Sep 16, 2025
b7f7ada
Hook scoreboard on destroy
mutatrum Sep 16, 2025
ef1a842
Remove EOF newlines
mutatrum Sep 16, 2025
587c15f
Remove changes from #1237
mutatrum Sep 17, 2025
9090b12
Unified suffix pipes
mutatrum Sep 17, 2025
7fd1159
Add loader
Sep 17, 2025
290f5ce
Add documentation
mutatrum Sep 17, 2025
19f8604
Wrong type
mutatrum Sep 17, 2025
ac00030
Table styling
Sep 17, 2025
b8b0b85
Merge pull request #4 from duckaxe/cr/mutatrum/1236-1
mutatrum Sep 17, 2025
ea90af1
Add sorting and tooltips
mutatrum Sep 18, 2025
80b7c91
Add EOF newline
mutatrum Sep 18, 2025
121e974
Clean up scoreboard styling
mutatrum Sep 19, 2025
df0311e
Limit dateAgo on scoreboard / Make dateAgo less jumpy
mutatrum Sep 19, 2025
011f3fc
Fix table column width
Sep 19, 2025
f1f3fd6
text-center fix
mutatrum Sep 19, 2025
de917fc
Revert text-center
mutatrum Sep 19, 2025
0c42c54
Merge branch 'master' of https://github.com/bitaxeorg/ESP-Miner into …
mutatrum Sep 19, 2025
5297728
Replace ByteSuffixPipe with SuffixPipe
mutatrum Sep 19, 2025
2749eba
Merge pull request #5 from duckaxe/cr/mutatrum/1236-5
mutatrum Sep 20, 2025
0f4a7ab
Fix table column min-width
Sep 21, 2025
c86b77a
Merge branch 'master' of https://github.com/bitaxeorg/ESP-Miner into …
mutatrum Sep 21, 2025
0a29a5f
Catch xTaskCreate errors
mutatrum Sep 22, 2025
5bbe117
Merge branch 'master' of https://github.com/bitaxeorg/ESP-Miner into …
mutatrum Sep 22, 2025
7cc5757
Fix tooltip
mutatrum Sep 23, 2025
a170897
Merge branch 'master' into scoreboard
mutatrum Sep 25, 2025
cfb84c8
Merge branch 'master' into scoreboard
mutatrum Oct 2, 2025
ef90eb0
Merge branch 'master' of https://github.com/bitaxeorg/ESP-Miner into …
mutatrum Oct 29, 2025
49ae8ab
Revert unified pipes
mutatrum Oct 29, 2025
4d99382
Add support for indexed NVS keys
mutatrum Oct 29, 2025
5c3eae1
Merge branch 'master' of https://github.com/bitaxeorg/ESP-Miner into …
mutatrum Oct 29, 2025
0d756b5
Merge branch 'scoreboard' of https://github.com/mutatrum/ESP-Miner in…
mutatrum Oct 29, 2025
d7457c5
Remove superfluous changes
mutatrum Oct 29, 2025
0354659
More superfluous changes removed
mutatrum Oct 29, 2025
9ed92ef
Delete .clangd
mutatrum Oct 29, 2025
d6eec66
Remove whitespace
mutatrum Oct 29, 2025
e2653d0
Merge branch 'master' into scoreboard
mutatrum Nov 3, 2025
578bf12
Merge branch 'master' into scoreboard
mutatrum Nov 7, 2025
9e80dcc
Merge branch 'master' into scoreboard
mutatrum Nov 8, 2025
d446af5
Fix merge
mutatrum Nov 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ SRCS
"./tasks/asic_result_task.c"
"./tasks/power_management_task.c"
"./tasks/statistics_task.c"
"./tasks/scoreboard.c"
"./tasks/hashrate_monitor_task.c"
"./thermal/EMC2101.c"
"./thermal/EMC2103.c"
Expand Down
2 changes: 2 additions & 0 deletions main/global_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "work_queue.h"
#include "device_config.h"
#include "display.h"
#include "scoreboard.h"

#define STRATUM_USER CONFIG_STRATUM_USER
#define FALLBACK_STRATUM_USER CONFIG_FALLBACK_STRATUM_USER
Expand Down Expand Up @@ -70,6 +71,7 @@ typedef struct
char firmware_update_filename[20];
char firmware_update_status[20];
char * asic_status;
Scoreboard scoreboard;
} SystemModule;

typedef struct
Expand Down
6 changes: 6 additions & 0 deletions main/http_server/axe-os/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { UpdateComponent } from './components/update/update.component';
import { SettingsComponent } from './components/settings/settings.component';
import { NetworkComponent } from './components/network/network.component';
import { SwarmComponent } from './components/swarm/swarm.component';
import { ScoreboardComponent } from './components/scoreboard/scoreboard.component';
import { DesignComponent } from './components/design/design.component';
import { PoolComponent } from './components/pool/pool.component';
import { AppLayoutComponent } from './layout/app.layout.component';
Expand Down Expand Up @@ -67,6 +68,11 @@ const routes: Routes = [
component: SwarmComponent,
title: `${TITLE_PREFIX} Swarm`,
},
{
path: 'scoreboard',
component: ScoreboardComponent,
title: `${TITLE_PREFIX} Scoreboard`,
},
{
path: 'design',
component: DesignComponent,
Expand Down
2 changes: 2 additions & 0 deletions main/http_server/axe-os/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { UpdateComponent } from './components/update/update.component';
import { NetworkComponent } from './components/network/network.component';
import { SettingsComponent } from './components/settings/settings.component';
import { SwarmComponent } from './components/swarm/swarm.component';
import { ScoreboardComponent } from './components/scoreboard/scoreboard.component';
import { ThemeConfigComponent } from './components/design/theme-config.component';
import { DesignComponent } from './components/design/design.component';
import { AppLayoutModule } from './layout/app.layout.module';
Expand Down Expand Up @@ -62,6 +63,7 @@ const components = [
ANSIPipe,
DateAgoPipe,
SwarmComponent,
ScoreboardComponent,
SettingsComponent,
HashSuffixPipe,
DiffSuffixPipe,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@
}
}

.code {
line-break: anywhere;
font-family: 'Courier New', Courier, monospace;
}

.heatmap {
width: 100%;
border-collapse: separate;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<div class="card">
<h2>Scoreboard</h2>

<ng-container *ngIf="scoreboard$ | async as scoreboard">
<div class="card mt-2 overflow-x-auto">
<table class="w-full">
<thead>
<tr>
<th *ngFor="let field of [
{ label: 'Rank', name: 'rank' },
{ label: 'Difficulty', name: 'difficulty' },
{ label: 'Job ID', name: 'job_id' },
{ label: 'ExtraNonce2', name: 'extranonce2' },
{ label: 'Time', name: 'ntime' },
{ label: 'Nonce', name: 'nonce' },
{ label: 'Version Bits', name: 'version_bits' }
]" class="text-500 font-medium">
<div class="flex align-items-center cursor-pointer select-none relative" (click)="sortBy(field.name)">
<span class="pr-3">{{field.label}}</span>
<i class="pi text-xs ml-2 absolute right-0" [ngClass]="{
'pi-sort-up-fill': sortField === field.name && sortDirection === 'asc',
'pi-sort-down-fill': sortField === field.name && sortDirection === 'desc'
}"></i>
</div>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let entry of scoreboard">
<td>
<i class="pi pi-trophy" *ngIf="entry.rank < 3" [ngStyle]="{
'color': entry.rank === 0 ? 'gold' : entry.rank === 1 ? 'silver' : 'peru'
}"></i>
<span *ngIf="entry.rank >= 3" class="text-500 text-xs">{{ entry.rank + 1 }}</span>
</td>
<td pTooltip="{{ entry.difficulty }}" tooltipPosition="top" class="text-900">{{ entry.difficulty | diffSuffix }}</td>
<td class="code">{{ entry.job_id }}</td>
<td class="code">{{ entry.extranonce2 }}</td>
<td pTooltip="{{ entry.since | dateAgo: {intervals: 2} }} ago" tooltipPosition="top">{{ entry.ntime * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}</td>
<td class="code">{{ entry.nonce }}</td>
<td class="code">{{ entry.version_bits }}</td>
</tr>
</tbody>
</table>
</div>
</ng-container>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
table {
border-collapse: collapse;
}
th,
td {
padding: 0 0.5rem 0.5rem;
white-space: nowrap;
text-align: left;

&:first-child {
padding-left: 0;
}
&:last-child {
padding-right: 0;
}
}
td {
padding-top: 0.5rem;
}
tbody tr {
border-top: 1px solid var(--surface-border);

&:last-child td {
padding-bottom: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ScoreboardComponent } from './scoreboard.component';

describe('SystemComponent', () => {
let component: ScoreboardComponent;
let fixture: ComponentFixture<ScoreboardComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ScoreboardComponent]
})
.compileComponents();

fixture = TestBed.createComponent(ScoreboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable, Subject, merge, switchMap, map, shareReplay, timer, takeUntil, finalize } from 'rxjs';
import { SystemService } from 'src/app/services/system.service';
import { LoadingService } from 'src/app/services/loading.service';
import { LocalStorageService } from 'src/app/local-storage.service';
import { ISystemScoreboardEntry } from 'src/models/ISystemScoreboard';

const SWARM_SORTING = 'SCOREBOARD_SORTING';

@Component({
selector: 'app-scoreboard',
templateUrl: './scoreboard.component.html',
styleUrls: ['./scoreboard.component.scss']
})
export class ScoreboardComponent implements OnInit, OnDestroy {
public scoreboard$: Observable<ISystemScoreboardEntry[]>;
public sortField: string = '';
public sortDirection: 'asc' | 'desc' = 'asc';

private refresh$ = new Subject<void>();
private destroy$ = new Subject<void>();

constructor(
private systemService: SystemService,
private loadingService: LoadingService,
private localStorageService: LocalStorageService,
) {
const storedSorting = this.localStorageService.getObject(SWARM_SORTING) ?? {
sortField: 'rank',
sortDirection: 'asc'
};
this.sortField = storedSorting.sortField;
this.sortDirection = storedSorting.sortDirection;

this.scoreboard$ = merge(timer(0, 5000), this.refresh$).pipe(
switchMap(() => this.systemService.getScoreboard().pipe(
map(data => data.map((entry, index) => ({
...entry,
rank: index,
since: (Date.now() / 1000) - entry.ntime
}))),
map(data => this.sortData(data, this.sortField, this.sortDirection)),
finalize(() => this.loadingService.loading$.next(false))
)),
shareReplay({refCount: true, bufferSize: 1}),
takeUntil(this.destroy$)
);
}

sortBy(field: string) {
this.sortDirection = this.sortField === field
? this.sortDirection === 'asc' ? 'desc' : 'asc'
: 'asc';
this.sortField = field;

this.localStorageService.setObject(SWARM_SORTING, {
sortField: this.sortField,
sortDirection: this.sortDirection
});
this.refresh$.next();
}

private sortData(data: ISystemScoreboardEntry[], field: string, direction: 'asc' | 'desc'): ISystemScoreboardEntry[] {
if (!data || !field) {
return data;
}

return data.sort((a, b) => {
let valueA = a[field as keyof ISystemScoreboardEntry];
let valueB = b[field as keyof ISystemScoreboardEntry];

if (valueA < valueB) {
return direction === 'asc' ? -1 : 1;
} else if (valueA > valueB) {
return direction === 'asc' ? 1 : -1;
}
return 0;
});
}

ngOnInit() {
this.loadingService.loading$.next(true);
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class AppMenuComponent implements OnInit {
label: 'Menu',
items: [
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: ['/'] },
{ label: 'Scoreboard', icon: 'pi pi-fw pi-trophy', routerLink: ['scoreboard'] },
{ label: 'Swarm', icon: 'pi pi-fw pi-sitemap', routerLink: ['swarm'] },
{ label: 'Logs', icon: 'pi pi-fw pi-list', routerLink: ['logs'] },
{ label: 'System', icon: 'pi pi-fw pi-wave-pulse', routerLink: ['system'] },
Expand Down
5 changes: 2 additions & 3 deletions main/http_server/axe-os/src/app/pipes/date-ago.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,11 @@ export class DateAgoPipe implements PipeTransform {
result += counter + ' ' + i + 's'; // plural (2 days ago)
seconds -= intervals[i] * counter
}
shownIntervals++;
}
if (result) shownIntervals++;
}
return result;
}
return value;
}

}
}
30 changes: 30 additions & 0 deletions main/http_server/axe-os/src/app/services/system.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { chartLabelValue } from 'src/models/enum/eChartLabel';
import { ISystemInfo } from 'src/models/ISystemInfo';
import { ISystemStatistics } from 'src/models/ISystemStatistics';
import { ISystemASIC } from 'src/models/ISystemASIC';
import { ISystemScoreboardEntry } from 'src/models/ISystemScoreboard';

import { environment } from '../../environments/environment';

Expand Down Expand Up @@ -179,6 +180,35 @@ export class SystemService {
});
}

public getScoreboard(uri: string = ''): Observable<ISystemScoreboardEntry[]> {
if (environment.production) {
return this.httpClient.get(`${uri}/api/system/scoreboard`) as Observable<ISystemScoreboardEntry[]>;
}

// Mock data for development
return of([
{
rank: 0,
since: 3606,
difficulty: 2000,
job_id: "123456",
extranonce2: "000000",
ntime: 61125,
nonce: "00000000",
version_bits: "20000000"
},
{
rank: 1,
since: 3605,
difficulty: 1000,
job_id: "123457",
extranonce2: "000001",
ntime: 61126,
nonce: "00000001",
version_bits: "20000000"
}]).pipe(delay(1000));
}

public restart(uri: string = '') {
return this.httpClient.post(`${uri}/api/system/restart`, {}, {responseType: 'text'});
}
Expand Down
10 changes: 10 additions & 0 deletions main/http_server/axe-os/src/models/ISystemScoreboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface ISystemScoreboardEntry {
rank: number;
since: number;
difficulty: number;
job_id: string;
extranonce2: string;
ntime: number;
nonce: string;
version_bits: string;
}
7 changes: 6 additions & 1 deletion main/http_server/axe-os/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ button.color-dot {
}
}

.code {
line-break: anywhere;
font-family: 'Courier New', Courier, monospace;
}

.line-break-anywhere {
line-break: anywhere;
}
}
Loading
Loading