mirror of
https://github.com/skot/ESP-Miner.git
synced 2025-03-17 21:32:52 +01:00
swarm init
This commit is contained in:
parent
7abda9914c
commit
dd3a1dec87
.vscode
main
http_server
axe-os/src/app
app-routing.module.tsapp.component.htmlapp.component.scssapp.component.tsapp.module.ts
http_server.ccomponents
edit
home
swarm
services
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"idf.flashType": "UART",
|
||||
"idf.portWin": "COM30",
|
||||
"idf.portWin": "COM36",
|
||||
"idf.adapterTargetName": "esp32s3",
|
||||
"idf.openOcdConfigs": [
|
||||
"interface/ftdi/esp32_devkitj_v1.cfg",
|
||||
|
@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { EditComponent } from './components/edit/edit.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { SwarmComponent } from './components/swarm/swarm.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -12,6 +13,10 @@ const routes: Routes = [
|
||||
{
|
||||
path: 'edit',
|
||||
component: EditComponent
|
||||
},
|
||||
{
|
||||
path: 'swarm',
|
||||
component: SwarmComponent
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1,5 +1,23 @@
|
||||
<app-loading></app-loading>
|
||||
<app-header></app-header>
|
||||
<div class="content">
|
||||
<router-outlet></router-outlet>
|
||||
<div class="body">
|
||||
<!-- <button [routerLink]="['edit']" class="btn btn-primary edit">Settings</button> -->
|
||||
|
||||
<button (click)="restart()" class="btn btn-danger restart">Restart</button>
|
||||
|
||||
|
||||
<div class="tab-container">
|
||||
<div class="tab" [routerLink]="['/']" routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}">Home
|
||||
</div>
|
||||
<div class="tab" [routerLink]="['edit']" routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}">
|
||||
Settings
|
||||
</div>
|
||||
<div class="tab" [routerLink]="['swarm']" routerLinkActive="active" [routerLinkActiveOptions]="{exact:true}">
|
||||
Swarm
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
@ -1,14 +1,69 @@
|
||||
.content {
|
||||
margin-top: 150px;
|
||||
.body {
|
||||
margin-top: 100px;
|
||||
|
||||
padding-left: 10vw;
|
||||
padding-right: 10vw;
|
||||
padding-left: 5vw;
|
||||
padding-right: 5vw;
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width:900px) {
|
||||
.content {
|
||||
.body {
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
display: flex;
|
||||
margin-top: 32px;
|
||||
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
border-top: 1px solid #304562;
|
||||
border-left: 1px solid #304562;
|
||||
border-right: 1px solid #304562;
|
||||
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
font-size: 25px;
|
||||
|
||||
&:not(.active) {
|
||||
border-bottom: 1px solid #304562;
|
||||
background-color: #1f2d40;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-top: 26px;
|
||||
padding-left: 5vw;
|
||||
padding-right: 5vw;
|
||||
border-left: 1px solid #304562;
|
||||
border-bottom: 1px solid #304562;
|
||||
border-right: 1px solid #304562;
|
||||
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
|
||||
padding-bottom: 26px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width:900px) {
|
||||
.content {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
import { SystemService } from './services/system.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -6,10 +9,20 @@ import { Component } from '@angular/core';
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor() {
|
||||
constructor(
|
||||
private systemService: SystemService,
|
||||
private toastr: ToastrService,
|
||||
) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public restart() {
|
||||
this.systemService.restart().subscribe(res => {
|
||||
|
||||
});
|
||||
this.toastr.success('Success!', 'Bitaxe restarted');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import { HomeComponent } from './components/home/home.component';
|
||||
import { LoadingComponent } from './components/loading/loading.component';
|
||||
import { ANSIPipe } from './pipes/ansi.pipe';
|
||||
import { DateAgoPipe } from './pipes/date-ago.pipe';
|
||||
import { SwarmComponent } from './components/swarm/swarm.component';
|
||||
|
||||
const components = [
|
||||
AppComponent,
|
||||
@ -27,7 +28,8 @@ const components = [
|
||||
HomeComponent,
|
||||
LoadingComponent,
|
||||
ANSIPipe,
|
||||
DateAgoPipe
|
||||
DateAgoPipe,
|
||||
SwarmComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -1,6 +1,3 @@
|
||||
<div class="button-row">
|
||||
<button (click)="restart()" class="btn btn-danger restart">Restart</button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Settings</h2>
|
||||
<ng-container *ngIf="form != null">
|
||||
|
@ -2,10 +2,7 @@
|
||||
<h4>Loading...</h4>
|
||||
</ng-template>
|
||||
<ng-container>
|
||||
<div class="button-row">
|
||||
<button [routerLink]="['edit']" class="btn btn-primary edit">Settings</button>
|
||||
<button (click)="restart()" class="btn btn-danger restart">Restart</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<div>
|
||||
@ -128,8 +125,11 @@
|
||||
</div>
|
||||
|
||||
<div class="card mt-2">
|
||||
<h2>Realtime Logs</h2>
|
||||
<div id="logs" #scrollContainer>
|
||||
<h2>Realtime Logs <button (click)="showLogs = !showLogs" style="margin-left: 15px;"
|
||||
class="btn btn-secondary">{{showLogs ? 'Hide': 'Show'}}
|
||||
Logs</button></h2>
|
||||
|
||||
<div *ngIf="showLogs" id="logs" #scrollContainer>
|
||||
<div *ngFor="let log of logs">₿ {{log | ANSI}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { AfterViewChecked, Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { interval, map, Observable, shareReplay, startWith, Subscription, switchMap } from 'rxjs';
|
||||
import { LoadingService } from 'src/app/services/loading.service';
|
||||
import { SystemService } from 'src/app/services/system.service';
|
||||
import { WebsocketService } from 'src/app/services/web-socket.service';
|
||||
import { ISystemInfo } from 'src/models/ISystemInfo';
|
||||
@ -22,11 +20,11 @@ export class HomeComponent implements AfterViewChecked, OnDestroy {
|
||||
|
||||
private websocketSubscription: Subscription;
|
||||
|
||||
public showLogs = false;
|
||||
|
||||
|
||||
constructor(
|
||||
private systemService: SystemService,
|
||||
private toastr: ToastrService,
|
||||
private loadingService: LoadingService,
|
||||
private websocketService: WebsocketService
|
||||
) {
|
||||
|
||||
@ -69,11 +67,6 @@ export class HomeComponent implements AfterViewChecked, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
public restart() {
|
||||
this.systemService.restart().subscribe(res => {
|
||||
|
||||
});
|
||||
this.toastr.success('Success!', 'Bitaxe restarted');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,14 @@
|
||||
<form [formGroup]="form">
|
||||
<label>AxeOS Device IP: </label>
|
||||
<input formControlName="ip" type="text">
|
||||
<button style="margin-left: 15px;" class="btn btn-primary" (click)="add()">Add</button>
|
||||
</form>
|
||||
<div class="card">
|
||||
<ng-container *ngIf="swarm$ | async as swarm">
|
||||
<div *ngFor="let axeOs$ of swarm">
|
||||
<div class="row" *ngIf="axeOs$ | async as axe">
|
||||
{{axe.ip}}: {{axe.hashRate | number: '1.2-2'}}
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
@ -0,0 +1,9 @@
|
||||
form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.row {
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
border: 1px solid white
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SwarmComponent } from './swarm.component';
|
||||
|
||||
describe('SwarmComponent', () => {
|
||||
let component: SwarmComponent;
|
||||
let fixture: ComponentFixture<SwarmComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SwarmComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(SwarmComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,72 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { catchError, map, Observable, of, startWith, switchMap } from 'rxjs';
|
||||
import { SystemService } from 'src/app/services/system.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-swarm',
|
||||
templateUrl: './swarm.component.html',
|
||||
styleUrls: ['./swarm.component.scss']
|
||||
})
|
||||
export class SwarmComponent {
|
||||
|
||||
public form: FormGroup;
|
||||
|
||||
public swarm$: Observable<Observable<any>[]>;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private systemService: SystemService,
|
||||
private toastr: ToastrService
|
||||
) {
|
||||
this.form = this.fb.group({
|
||||
ip: []
|
||||
});
|
||||
|
||||
this.swarm$ = this.systemService.getSwarmInfo().pipe(
|
||||
map(swarmInfo => {
|
||||
return swarmInfo.map(({ ip }) => {
|
||||
// Make individual API calls for each IP
|
||||
return this.systemService.getInfo(`http://${ip}`).pipe(
|
||||
startWith({ ip }),
|
||||
map(info => {
|
||||
return {
|
||||
ip,
|
||||
...info
|
||||
};
|
||||
}),
|
||||
catchError(error => {
|
||||
return of(null);
|
||||
})
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public add() {
|
||||
const ip = this.form.value.ip;
|
||||
|
||||
this.systemService.getSwarmInfo().pipe(
|
||||
switchMap((swarm: any) => {
|
||||
return this.systemService.updateSwarm([{ ip }, ...swarm])
|
||||
})
|
||||
).subscribe({
|
||||
next: () => {
|
||||
this.toastr.success('Success!', 'Saved.');
|
||||
},
|
||||
error: (err) => {
|
||||
this.toastr.error('Error.', `Could not save. ${err.message}`);
|
||||
},
|
||||
complete: () => {
|
||||
this.form.reset();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -15,9 +15,9 @@ export class SystemService {
|
||||
private httpClient: HttpClient
|
||||
) { }
|
||||
|
||||
public getInfo(): Observable<ISystemInfo> {
|
||||
public getInfo(ip: string = ''): Observable<ISystemInfo> {
|
||||
if (environment.production) {
|
||||
return this.httpClient.get(`/api/system/info`) as Observable<ISystemInfo>;
|
||||
return this.httpClient.get(`${ip}/api/system/info`) as Observable<ISystemInfo>;
|
||||
} else {
|
||||
return of(
|
||||
{
|
||||
@ -100,4 +100,11 @@ export class SystemService {
|
||||
}
|
||||
|
||||
|
||||
public getSwarmInfo(): Observable<{ ip: string }[]> {
|
||||
return this.httpClient.get(`/api/swarm/info`) as Observable<{ ip: string }[]>;
|
||||
}
|
||||
|
||||
public updateSwarm(swarmConfig: any) {
|
||||
return this.httpClient.patch(`/api/swarm`, swarmConfig);
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +172,33 @@ static esp_err_t rest_common_get_handler(httpd_req_t * req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t PATCH_update_swarm(httpd_req_t * req)
|
||||
{
|
||||
int total_len = req->content_len;
|
||||
int cur_len = 0;
|
||||
char * buf = ((rest_server_context_t *) (req->user_ctx))->scratch;
|
||||
int received = 0;
|
||||
if (total_len >= SCRATCH_BUFSIZE) {
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "content too long");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
while (cur_len < total_len) {
|
||||
received = httpd_req_recv(req, buf + cur_len, total_len);
|
||||
if (received <= 0) {
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to post control value");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
cur_len += received;
|
||||
}
|
||||
buf[total_len] = '\0';
|
||||
|
||||
nvs_config_set_string(NVS_CONFIG_SWARM, buf);
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t PATCH_update_settings(httpd_req_t * req)
|
||||
{
|
||||
int total_len = req->content_len;
|
||||
@ -235,6 +262,21 @@ static esp_err_t POST_restart(httpd_req_t * req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t GET_swarm(httpd_req_t * req)
|
||||
{
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
|
||||
// Add CORS headers
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Headers", "Content-Type");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Credentials", "true");
|
||||
|
||||
char * swarm_config = nvs_config_get_string(NVS_CONFIG_SWARM, "[]");
|
||||
httpd_resp_sendstr(req, swarm_config);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Simple handler for getting system handler */
|
||||
static esp_err_t GET_system_info(httpd_req_t * req)
|
||||
{
|
||||
@ -446,6 +488,7 @@ esp_err_t start_rest_server(void * pvParameters)
|
||||
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
config.max_uri_handlers = 20;
|
||||
|
||||
ESP_LOGI(TAG, "Starting HTTP Server");
|
||||
REST_CHECK(httpd_start(&server, &config) == ESP_OK, "Start server failed", err_start);
|
||||
@ -455,6 +498,13 @@ esp_err_t start_rest_server(void * pvParameters)
|
||||
.uri = "/api/system/info", .method = HTTP_GET, .handler = GET_system_info, .user_ctx = rest_context};
|
||||
httpd_register_uri_handler(server, &system_info_get_uri);
|
||||
|
||||
httpd_uri_t swarm_get_uri = {.uri = "/api/swarm/info", .method = HTTP_GET, .handler = GET_swarm, .user_ctx = rest_context};
|
||||
httpd_register_uri_handler(server, &swarm_get_uri);
|
||||
|
||||
httpd_uri_t update_swarm_uri = {
|
||||
.uri = "/api/swarm", .method = HTTP_PATCH, .handler = PATCH_update_swarm, .user_ctx = rest_context};
|
||||
httpd_register_uri_handler(server, &update_swarm_uri);
|
||||
|
||||
httpd_uri_t system_restart_uri = {
|
||||
.uri = "/api/system/restart", .method = HTTP_POST, .handler = POST_restart, .user_ctx = rest_context};
|
||||
httpd_register_uri_handler(server, &system_restart_uri);
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Max length 15
|
||||
|
||||
#define NVS_CONFIG_WIFI_SSID "wifissid"
|
||||
#define NVS_CONFIG_WIFI_PASS "wifipass"
|
||||
#define NVS_CONFIG_STRATUM_URL "stratumurl"
|
||||
@ -21,6 +23,8 @@
|
||||
#define NVS_CONFIG_FAN_SPEED "fanspeed"
|
||||
#define NVS_CONFIG_BEST_DIFF "bestdiff"
|
||||
|
||||
#define NVS_CONFIG_SWARM "swarmconfig"
|
||||
|
||||
char * nvs_config_get_string(const char * key, const char * default_value);
|
||||
void nvs_config_set_string(const char * key, const char * default_value);
|
||||
uint16_t nvs_config_get_u16(const char * key, const uint16_t default_value);
|
||||
|
Loading…
x
Reference in New Issue
Block a user