Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion src/app/agents/agent.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';

import { CoreComponentsModule } from '@components/core-components.module';
import { AgentStatGraphComponent } from '@src/app/shared/graphs/echarts/agent-stat-graph/agent-stat-graph.component';

import { AgentStatusModalComponent } from '@src/app/agents/agent-status/agent-status-modal/agent-status-modal.component';
import { AgentStatusComponent } from '@src/app/agents/agent-status/agent-status.component';
Expand Down Expand Up @@ -42,7 +43,8 @@ import { PipesModule } from '@src/app/shared/pipes.module';
RouterModule,
FormsModule,
PipesModule,
NgbModule
NgbModule,
AgentStatGraphComponent
]
})
export class AgentsModule {}
14 changes: 9 additions & 5 deletions src/app/agents/edit-agent/edit-agent.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,31 @@
</grid-buttons>
</form>
</grid-main>
<!-- Graphs -->
<app-table>
<app-page-subtitle [subtitle]="' GPU Device(s) Temperature'"></app-page-subtitle>
<app-page-subtitle [subtitle]="'GPU Device(s) Temperature'"></app-page-subtitle>
<ng-container *ngIf="showagent">
<div class="row">
<div class="col col-12">
<div id="tempgraph" style="height: 250px"></div>
<app-agent-stat-graph [statType]="ASC.GPU_TEMP" [agentStats]="showagent.agentStats"></app-agent-stat-graph>
</div>
</div>

<app-page-subtitle [subtitle]="'GPU Device(s) Utilisation'"></app-page-subtitle>
<div class="row">
<div class="col col-12">
<div id="devicegraph" style="height: 250px"></div>
<app-agent-stat-graph [statType]="ASC.GPU_UTIL" [agentStats]="showagent.agentStats"></app-agent-stat-graph>
</div>
</div>

<app-page-subtitle [subtitle]="'CPU Device(s) Utilisation'"></app-page-subtitle>
<div class="row">
<div class="col col-12">
<div id="cpugraph" style="height: 250px"></div>
<app-agent-stat-graph [statType]="ASC.CPU_UTIL" [agentStats]="showagent.agentStats"></app-agent-stat-graph>
</div>
</div>
</ng-container>
</app-table>

<!-- ToDO API Errors table -->
<app-table>
<app-page-subtitle subtitle="Error messages"></app-page-subtitle>
Expand Down
216 changes: 1 addition & 215 deletions src/app/agents/edit-agent/edit-agent.component.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,10 @@
import { LineChart } from 'echarts/charts';
import {
GridComponent,
GridComponentOption,
LegendComponent,
MarkLineComponent,
MarkLineComponentOption,
MarkPointComponent,
TitleComponent,
TitleComponentOption,
ToolboxComponent,
ToolboxComponentOption,
TooltipComponent,
TooltipComponentOption
} from 'echarts/components';
import * as echarts from 'echarts/core';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { firstValueFrom } from 'rxjs';

import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router';

import { JAgentAssignment } from '@models/agent-assignment.model';
import { JAgentStat } from '@models/agent-stats.model';
import { JAgent } from '@models/agent.model';
import { JChunk } from '@models/chunk.model';
import { FilterType } from '@models/request-params.model';
Expand Down Expand Up @@ -89,6 +70,7 @@ export class EditAgentComponent implements OnInit, OnDestroy {
getchunks: JChunk[];

currentAssignment: JAgentAssignment;
public ASC = ASC;

constructor(
private unsubscribeService: UnsubscribeService,
Expand Down Expand Up @@ -125,9 +107,7 @@ export class EditAgentComponent implements OnInit, OnDestroy {
this.loadSelectUsers();
this.assignChunksInit(this.editedAgentIndex);
this.initForm();
this.drawGraphs(this.showagent.agentStats);
}

/**
* Lifecycle hook called before the component is destroyed.
* Unsubscribes from all subscriptions to prevent memory leaks.
Expand Down Expand Up @@ -190,7 +170,6 @@ export class EditAgentComponent implements OnInit, OnDestroy {
this.serializer.deserialize<JUser[]>(responseBody),
DEFAULT_FIELD_MAPPING
);
console.log(this.selectUsers);
});
this.unsubscribeService.add(loadUsersSubscription$);
}
Expand Down Expand Up @@ -351,197 +330,4 @@ export class EditAgentComponent implements OnInit, OnDestroy {
.map((device) => `${deviceCountMap[device]} x ${device}`)
.join('<br>');
}

// //
// GRAPHS SECTION
// //
/**
* Draw all graphs for GPU temperature and utilisation and CPU utilisation
* @param agentStatList List of agentStats objects
*/
drawGraphs(agentStatList: JAgentStat[]) {
this.drawGraph(
agentStatList.filter((agentStat) => agentStat.statType == ASC.GPU_TEMP),
ASC.GPU_TEMP,
'tempgraph'
); // filter Device Temperature
this.drawGraph(
agentStatList.filter((agentStat) => agentStat.statType == ASC.GPU_UTIL),
ASC.GPU_UTIL,
'devicegraph'
); // filter Device Utilisation
this.drawGraph(
agentStatList.filter((agentStat) => agentStat.statType == ASC.CPU_UTIL),
ASC.CPU_UTIL,
'cpugraph'
); // filter CPU Utilisation
}

/**
* Draw single Graph from AgentStats
* @param agentStatList List of AgentStats objects
* @param status Number to determine device and displayed stats (GPU_TEMP: 1, GPU_UTIL: 2, CPU_UTIL: 3)
* @param name Name of Graph
*/
drawGraph(agentStatList: JAgentStat[], status: number, name: string) {
echarts.use([
TitleComponent,
ToolboxComponent,
TooltipComponent,
GridComponent,
LegendComponent,
MarkLineComponent,
MarkPointComponent,
LineChart,
CanvasRenderer,
UniversalTransition
]);

type EChartsOption = echarts.ComposeOption<
| TitleComponentOption
| ToolboxComponentOption
| TooltipComponentOption
| GridComponentOption
| MarkLineComponentOption
>;

let templabel = '';

if (ASC.GPU_TEMP === status) {
if (this.getTemp2() > 100) {
templabel = '°F';
} else {
templabel = '°C';
}
}
if (ASC.GPU_UTIL === status) {
templabel = '%';
}
if (ASC.CPU_UTIL === status) {
templabel = '%';
}

const arr = [];
const max = [];
const devlabels = [];
const result = agentStatList;

for (let i = 0; i < result.length; i++) {
const val = result[i].value;
for (let i = 0; i < val.length; i++) {
const iso = this.transDate(result[i].time);
arr.push({ time: iso, value: val[i], device: i });
max.push(result[i].time);
devlabels.push('Device ' + i + '');
}
}

const grouped = [];
arr.forEach(function (a) {
grouped[a.device] = grouped[a.device] || [];
grouped[a.device].push({ time: a.time, value: a.value });
});

const labels = [...new Set(devlabels)];

const startdate = Math.max(...max);
const xAxis = this.generateIntervalsOf(1, +startdate - 500, +startdate);

const chartDom = document.getElementById(name);
const myChart = echarts.init(chartDom);
let option: EChartsOption;

const seriesData = [];
for (let i = 0; i < grouped.length; i++) {
seriesData.push({
name: 'Device ' + i + '',
type: 'line',
data: grouped[i],
markLine: {
data: [{ type: 'average', name: 'Avg' }],
symbol: ['none', 'none']
}
});
}

const self = this;
option = {
tooltip: {
position: 'top'
},
legend: {
data: labels
},
toolbox: {
show: true,
feature: {
dataZoom: {
yAxisIndex: 'none'
},
dataView: { readOnly: false },
restore: {},
saveAsImage: {
name: 'Device Temperature'
}
}
},
useUTC: true,
xAxis: {
data: xAxis.map(function (item: any[] | any) {
return self.transDate(item);
})
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value} ' + templabel + ''
}
},
series: seriesData
};
option && myChart.setOption(option);
}

getTemp1() {
// Temperature Config Setting
return this.uiService.getUIsettings('agentTempThreshold1').value;
}

getTemp2() {
// Temperature 2 Config Setting
return this.uiService.getUIsettings('agentTempThreshold2').value;
}

transDate(dt: number) {
const date = new Date(dt * 1000);
return (
date.getUTCDate() +
'-' +
this.leading_zeros(date.getUTCMonth() + 1) +
'-' +
date.getUTCFullYear() +
',' +
this.leading_zeros(date.getUTCHours()) +
':' +
this.leading_zeros(date.getUTCMinutes()) +
':' +
this.leading_zeros(date.getUTCSeconds())
);
}

leading_zeros(dt) {
return (dt < 10 ? '0' : '') + dt;
}

generateIntervalsOf(interval: number, start: number, end: number) {
const result = [];
let current = start;

while (current < end) {
result.push(current);
current += interval;
}

return result;
}
}
35 changes: 35 additions & 0 deletions src/app/core/_pipes/hashrate-pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Pipe, PipeTransform } from '@angular/core';

/**
* Transform hash rate into human-readable format (e.g. H/s, kH/s, MH/s, GH/s)
* @param hashrate - The hash rate number (e.g. 1000000)
* @param decimals - Optional number of decimal places (default: 2)
* Usage:
* value | hashRate
* Example:
* {{ 1000000 | hashRate }}
* @returns 1 MH/s
*/
@Pipe({
name: 'hashRate',
standalone: false
})
export class HashRatePipe implements PipeTransform {
private readonly units = ['H/s', 'kH/s', 'MH/s', 'GH/s', 'TH/s', 'PH/s'];

transform(value: number, decimals: number = 2, asObject: boolean = false): string | { value: number; unit: string } {
if (!value || value <= 0) {
return asObject ? { value: 0, unit: 'H/s' } : '0 H/s';
}

let i = 0;
while (value >= 1000 && i < this.units.length - 1) {
value /= 1000;
i++;
}

const rounded = +value.toFixed(decimals);

return asObject ? { value: rounded, unit: this.units[i] } : `${rounded} ${this.units[i]}`;
}
}
14 changes: 7 additions & 7 deletions src/app/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,19 @@ <h3>Cracks</h3>
}"
>
<div class="co co-100">
<div
id="pcard"
class="app-echarts"
style="height: 250px; display: flex; align-items: center; justify-content: center;"
>
<div class="app-echarts" style="height: 250px;">
<ng-container *ngIf="canReadCracks; else noPermChart">
<!-- Chart will render here -->
<app-heatmap-chart
*ngIf="heatmapData?.length"
[data]="heatmapData"
[isDarkMode]="isDarkMode"
></app-heatmap-chart>

</ng-container>
<ng-template #noPermChart>
<span class="no-permission">No permission to view chart data</span>
</ng-template>
</div>

<div class="chart-controls">
<span *ngIf="lastUpdated">Last updated: {{ lastUpdated }}</span>
<ng-container *ngIf="!refreshPage">
Expand Down
Loading