Better network charts

This commit is contained in:
everoddandeven 2024-10-26 14:46:46 +02:00
parent 388021b25b
commit 80e0a5091b
6 changed files with 103 additions and 60 deletions

View file

@ -400,6 +400,14 @@ export class DaemonService {
console.log("Starting daemon"); console.log("Starting daemon");
if (!this.restarting && !this.enablingSync) {
window.electronAPI.showNotification({
title: 'Daemon starting',
body: 'Monero daemon is starting',
closeButtonText: 'Dismiss'
});
}
this.settings = customSettings ? customSettings : await this.getSettings(); this.settings = customSettings ? customSettings : await this.getSettings();
if (!this.settings.noSync && !this.settings.syncOnWifi && await this.isWifiConnected()) { if (!this.settings.noSync && !this.settings.syncOnWifi && await this.isWifiConnected()) {

View file

@ -47,7 +47,23 @@ export abstract class BasePageComponent implements AfterContentInit, OnDestroy {
this.setTableInitialized(id, $table); this.setTableInitialized(id, $table);
} }
protected loadTable(id: string, rows: any[]): void { protected setTableLoading(id: string, loading: boolean = true): void {
if (!this.isTableInitialized(id)) {
this.initTable(id, loading);
return;
}
const $table = this.initializedTables[id];
if (!$table) {
console.warn(`Could not set table loading to ${id}`);
return;
}
$table.bootstrapTable('showLoading');
}
protected loadTable(id: string, rows: any[], loading: boolean = false): void {
if (!this.isTableInitialized(id)) { if (!this.isTableInitialized(id)) {
this.initTable(id); this.initTable(id);
} }
@ -60,10 +76,11 @@ export abstract class BasePageComponent implements AfterContentInit, OnDestroy {
const $table = this.initializedTables[id] as JQuery<HTMLElement>; const $table = this.initializedTables[id] as JQuery<HTMLElement>;
$table.bootstrapTable('load', rows); $table.bootstrapTable('load', rows);
$table.bootstrapTable('hideLoading'); if (loading) $table.bootstrapTable('showLoading');
else $table.bootstrapTable('hideLoading');
} }
private destroyTable(id: string): void { protected destroyTable(id: string): void {
const $table = this.initializedTables[id]; const $table = this.initializedTables[id];
if (!$table) { if (!$table) {
@ -76,7 +93,7 @@ export abstract class BasePageComponent implements AfterContentInit, OnDestroy {
this.initializedTables[id] = undefined; this.initializedTables[id] = undefined;
} }
private destroyTables(): void { protected destroyTables(): void {
for(const key in this.initializedTables) { for(const key in this.initializedTables) {
this.destroyTable(key); this.destroyTable(key);
} }

View file

@ -150,13 +150,13 @@ export class DetailComponent extends BasePageComponent implements AfterViewInit
this.ngZone.run(() => { this.ngZone.run(() => {
this.cards = this.createCards(); this.cards = this.createCards();
this.loadTables(true);
}); });
}); });
const syncInfoRefreshEndSub: Subscription = this.daemonData.syncInfoRefreshEnd.subscribe(() => { const syncInfoRefreshEndSub: Subscription = this.daemonData.syncInfoRefreshEnd.subscribe(() => {
this.loadTables();
this.cards = this.createCards(); this.cards = this.createCards();
this.loadTables();
}); });
this.subscriptions.push(syncStartSub, syncInfoRefreshEndSub); this.subscriptions.push(syncStartSub, syncInfoRefreshEndSub);
@ -169,17 +169,17 @@ export class DetailComponent extends BasePageComponent implements AfterViewInit
}); });
} }
private loadPeersTable(): void { private loadPeersTable(loading: boolean = false): void {
this.loadTable('peersTable', this.getPeers()); this.loadTable('peersTable', this.getPeers(), loading);
} }
private loadSpansTable(): void { private loadSpansTable(loading: boolean = false): void {
this.loadTable('spansTable', this.getSpans()); this.loadTable('spansTable', this.getSpans(), loading);
} }
private loadTables(): void { private loadTables(loading: boolean = false): void {
this.loadPeersTable(); this.loadPeersTable(loading);
this.loadSpansTable(); this.loadSpansTable(loading);
} }
private createCards(): SimpleBootstrapCard[] { private createCards(): SimpleBootstrapCard[] {

View file

@ -14,10 +14,10 @@
<div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent"> <div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="pills-net-stats" role="tabpanel" aria-labelledby="pills-net-stats-tab" tabindex="0"> <div class="tab-pane fade show active" id="pills-net-stats" role="tabpanel" aria-labelledby="pills-net-stats-tab" tabindex="0">
<h2>Bytes In</h2> <h2><i class="bi bi-cloud-arrow-down m-4"></i> Download ({{ currentNetStats.totalGigaBytesIn.toFixed(2) }} GB)</h2>
<canvas class="my-4 w-100" id="netStatsBytesInChart" width="900" height="380"></canvas> <canvas class="my-4 w-100" id="netStatsBytesInChart" width="900" height="380"></canvas>
<h2>Bytes Out</h2> <h2><i class="bi bi-cloud-arrow-up m-4"></i> Upload ({{ currentNetStats.totalGigaBytesOut.toFixed(2) }} GB)</h2>
<canvas class="my-4 w-100" id="netStatsBytesOutChart" width="900" height="380"></canvas> <canvas class="my-4 w-100" id="netStatsBytesOutChart" width="900" height="380"></canvas>
</div> </div>
@ -29,7 +29,6 @@
id="connectionsTable" id="connectionsTable"
data-toggle="connectionsTable" data-toggle="connectionsTable"
data-toolbar="#toolbar" data-toolbar="#toolbar"
data-pagination="true" data-pagination="true"
> >
<thead> <thead>

View file

@ -3,7 +3,7 @@ import { NavbarService } from '../../shared/components/navbar/navbar.service';
import { DaemonDataService, DaemonService } from '../../core/services'; import { DaemonDataService, DaemonService } from '../../core/services';
import { NavbarLink } from '../../shared/components/navbar/navbar.model'; import { NavbarLink } from '../../shared/components/navbar/navbar.model';
import { Chart, ChartData } from 'chart.js/auto' import { Chart, ChartData } from 'chart.js/auto'
import { NetStatsHistoryEntry } from '../../../common'; import { NetStats, NetStatsHistoryEntry } from '../../../common';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { BasePageComponent } from '../base-page/base-page.component'; import { BasePageComponent } from '../base-page/base-page.component';
@ -32,6 +32,8 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
public setLimitSuccess: boolean = false; public setLimitSuccess: boolean = false;
public setLimitError: string = ''; public setLimitError: string = '';
public currentNetStats: NetStats;
constructor(navbarService: NavbarService, private daemonService: DaemonService, private daemonData: DaemonDataService) { constructor(navbarService: NavbarService, private daemonService: DaemonService, private daemonData: DaemonDataService) {
super(navbarService); super(navbarService);
this.setLinks([ this.setLinks([
@ -40,6 +42,9 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
new NavbarLink('pills-limits-tab', '#pills-limit', 'pills-limit', false, 'Limit') new NavbarLink('pills-limits-tab', '#pills-limit', 'pills-limit', false, 'Limit')
]); ]);
const lastNetStats = this.daemonData.netStatsHistory.last;
this.currentNetStats = lastNetStats ? lastNetStats.netStats : new NetStats(0, 0, 0, 0);
const netStatsRefreshStartSub: Subscription = this.daemonData.netStatsRefreshEnd.subscribe(() => { const netStatsRefreshStartSub: Subscription = this.daemonData.netStatsRefreshEnd.subscribe(() => {
this.refreshNetStatsHistory(); this.refreshNetStatsHistory();
}); });
@ -54,6 +59,10 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
this.netStatsBytesInChart.destroy(); this.netStatsBytesInChart.destroy();
this.netStatsBytesInChart = undefined; this.netStatsBytesInChart = undefined;
} }
if (this.netStatsBytesOutChart) {
this.netStatsBytesOutChart.destroy();
this.netStatsBytesOutChart = undefined;
}
} }
else { else {
this.initNetStatsHistoryChart(); this.initNetStatsHistoryChart();
@ -78,7 +87,7 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
const data: number[] = []; const data: number[] = [];
this.daemonData.netStatsHistory.history.forEach((entry: NetStatsHistoryEntry) => { this.daemonData.netStatsHistory.history.forEach((entry: NetStatsHistoryEntry) => {
labels.push(`${entry.date.toLocaleTimeString()} ${entry.date.toLocaleDateString()}`); labels.push(`${entry.date.toLocaleTimeString()} ${entry.date.toLocaleDateString()}`);
data.push(entry.netStats.totalBytesIn); data.push(entry.netStats.totalGigaBytesIn);
}); });
return { return {
@ -86,9 +95,10 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
datasets: [{ datasets: [{
data: data, data: data,
backgroundColor: 'transparent', backgroundColor: 'transparent',
borderColor: '#007bff', borderColor: '#ff5733',
borderWidth: 4, borderWidth: 4,
pointBackgroundColor: '#007bff' pointBackgroundColor: '#ff5733',
radius: 0
}] }]
}; };
} }
@ -98,7 +108,7 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
const data: number[] = []; const data: number[] = [];
this.daemonData.netStatsHistory.history.forEach((entry: NetStatsHistoryEntry) => { this.daemonData.netStatsHistory.history.forEach((entry: NetStatsHistoryEntry) => {
labels.push(`${entry.date.toLocaleTimeString()} ${entry.date.toLocaleDateString()}`); labels.push(`${entry.date.toLocaleTimeString()} ${entry.date.toLocaleDateString()}`);
data.push(entry.netStats.totalBytesOut); data.push(entry.netStats.totalGigaBytesOut);
}); });
return { return {
@ -106,9 +116,10 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
datasets: [{ datasets: [{
data: data, data: data,
backgroundColor: 'transparent', backgroundColor: 'transparent',
borderColor: '#007bff', borderColor: '#ff5733',
borderWidth: 4, borderWidth: 4,
pointBackgroundColor: '#007bff' pointBackgroundColor: '#ff5733',
radius: 0
}] }]
}; };
} }
@ -166,32 +177,28 @@ export class NetworkComponent extends BasePageComponent implements AfterViewInit
} }
private refreshNetStatsHistory(): void { private refreshNetStatsHistory(): void {
if (!this.netStatsBytesInChart) {
return;
}
const last = this.daemonData.netStatsHistory.last; const last = this.daemonData.netStatsHistory.last;
if (!this.netStatsBytesInChart || !this.netStatsBytesOutChart) { if (!this.netStatsBytesInChart || !this.netStatsBytesOutChart) {
this.initNetStatsHistoryChart(); this.initNetStatsHistoryChart();
} }
else if (last) { else if (last) {
const label = `${last.date.toLocaleTimeString()} ${last.date.toLocaleDateString()}`; const label = `${last.date.toLocaleDateString()} ${last.date.toLocaleTimeString()}`;
this.netStatsBytesInChart.data.labels?.push(label); this.netStatsBytesInChart.data.labels?.push(label);
this.netStatsBytesInChart.data.datasets.forEach((dataset) => { this.netStatsBytesInChart.data.datasets.forEach((dataset) => {
dataset.data.push(last.netStats.totalBytesIn); dataset.data.push(last.netStats.totalGigaBytesIn);
}); });
this.netStatsBytesOutChart.data.labels?.push(label); this.netStatsBytesOutChart.data.labels?.push(label);
this.netStatsBytesOutChart.data.datasets.forEach((dataset) => { this.netStatsBytesOutChart.data.datasets.forEach((dataset) => {
dataset.data.push(last.netStats.totalBytesOut); dataset.data.push(last.netStats.totalGigaBytesOut);
}); });
this.netStatsBytesInChart.update(); this.netStatsBytesInChart.update();
this.netStatsBytesOutChart.update(); this.netStatsBytesOutChart.update();
this.currentNetStats = last.netStats;
} }
} }
public async setLimit(): Promise<void> { public async setLimit(): Promise<void> {

View file

@ -1,34 +1,46 @@
export class NetStats { export class NetStats {
public readonly startTime: number; public readonly startTime: number;
public readonly totalPacketsIn: number; public readonly totalPacketsIn: number;
public readonly totalBytesIn: number; public readonly totalBytesIn: number;
public readonly totalBytesOut: number; public readonly totalBytesOut: number;
constructor(startTime: number, totalPacketsIn: number, totalBytesIn: number, totalBytesOut: number) { public get totalKiloBytesIn(): number {
this.startTime = startTime; return this.totalBytesIn / 1000;
this.totalPacketsIn = totalPacketsIn; }
this.totalBytesIn = totalBytesIn;
this.totalBytesOut = totalBytesOut;
}
public static parse(netStats: any): NetStats { public get totalKiloBytesOut(): number {
const startTime: number = netStats.start_time; return this.totalBytesOut / 1000;
const totalPacketsIn: number = netStats.total_packets_in; }
const totalBytesIn: number = netStats.total_bytes_in;
const totalBytesOut: number = netStats.total_bytes_out;
return new NetStats(startTime, totalPacketsIn, totalBytesIn, totalBytesOut); public get totalMegaBytesIn(): number {
} return this.totalKiloBytesIn / 1000;
}
public get totalMegaBytesOut(): number {
return this.totalKiloBytesOut / 1000;
}
public get totalGigaBytesIn(): number {
return this.totalMegaBytesIn / 1000;
}
public get totalGigaBytesOut(): number {
return this.totalMegaBytesOut / 1000;
}
constructor(startTime: number, totalPacketsIn: number, totalBytesIn: number, totalBytesOut: number) {
this.startTime = startTime;
this.totalPacketsIn = totalPacketsIn;
this.totalBytesIn = totalBytesIn;
this.totalBytesOut = totalBytesOut;
}
public static parse(netStats: any): NetStats {
const startTime: number = netStats.start_time;
const totalPacketsIn: number = netStats.total_packets_in;
const totalBytesIn: number = netStats.total_bytes_in;
const totalBytesOut: number = netStats.total_bytes_out;
return new NetStats(startTime, totalPacketsIn, totalBytesIn, totalBytesOut);
}
} }
/**
* start_time - unsigned int; Unix start time.
total_packets_in - unsigned int;
total_bytes_in - unsigned int;
total_packets_out - unsigned int;
total_bytes_out - unsigned int;
status - string; General RPC error code. "OK" means everything looks good.
untrusted - boolean; States if the result is obtained using the bootstrap mode, and is therefore not trusted (true), or when the daemon is fully synced and thus handles the RPC locally (false).
*/