mirror of
https://github.com/skot/ESP-Miner.git
synced 2025-03-17 21:32:52 +01:00
Merge branch 'master' into hex_v302
This commit is contained in:
commit
e561f60220
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -24,4 +24,4 @@
|
||||
],
|
||||
"idf.port": "/dev/cu.usbmodem1434301",
|
||||
"C_Cpp.intelliSenseEngine": "Tag Parser"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"bm1368.c"
|
||||
"bm1366.c"
|
||||
"bm1397.c"
|
||||
"serial.c"
|
||||
|
@ -179,7 +179,7 @@ void BM1366_send_hash_frequency(float target_freq)
|
||||
}
|
||||
}
|
||||
|
||||
_send_BM1366((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbuf, 6, true);
|
||||
_send_BM1366((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbuf, 6, false);
|
||||
|
||||
ESP_LOGI(TAG, "Setting Frequency to %.2fMHz (%.2f)", target_freq, newf);
|
||||
}
|
||||
@ -665,7 +665,7 @@ asic_result * BM1366_receive_work(void)
|
||||
return SERIAL_rx_aa55(asic_response_buffer, 11);
|
||||
}
|
||||
|
||||
uint16_t reverse_uint16(uint16_t num)
|
||||
static uint16_t reverse_uint16(uint16_t num)
|
||||
{
|
||||
return (num >> 8) | (num << 8);
|
||||
}
|
||||
|
537
components/bm1397/bm1368.c
Normal file
537
components/bm1397/bm1368.c
Normal file
@ -0,0 +1,537 @@
|
||||
#include "bm1368.h"
|
||||
|
||||
#include "crc.h"
|
||||
#include "global_state.h"
|
||||
#include "serial.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BM1368_RST_PIN GPIO_NUM_1
|
||||
|
||||
#define TYPE_JOB 0x20
|
||||
#define TYPE_CMD 0x40
|
||||
|
||||
#define GROUP_SINGLE 0x00
|
||||
#define GROUP_ALL 0x10
|
||||
|
||||
#define CMD_JOB 0x01
|
||||
|
||||
#define CMD_SETADDRESS 0x00
|
||||
#define CMD_WRITE 0x01
|
||||
#define CMD_READ 0x02
|
||||
#define CMD_INACTIVE 0x03
|
||||
|
||||
#define RESPONSE_CMD 0x00
|
||||
#define RESPONSE_JOB 0x80
|
||||
|
||||
#define SLEEP_TIME 20
|
||||
#define FREQ_MULT 25.0
|
||||
|
||||
#define CLOCK_ORDER_CONTROL_0 0x80
|
||||
#define CLOCK_ORDER_CONTROL_1 0x84
|
||||
#define ORDERED_CLOCK_ENABLE 0x20
|
||||
#define CORE_REGISTER_CONTROL 0x3C
|
||||
#define PLL3_PARAMETER 0x68
|
||||
#define FAST_UART_CONFIGURATION 0x28
|
||||
#define TICKET_MASK 0x14
|
||||
#define MISC_CONTROL 0x18
|
||||
|
||||
typedef struct __attribute__((__packed__))
|
||||
{
|
||||
uint8_t preamble[2];
|
||||
uint32_t nonce;
|
||||
uint8_t midstate_num;
|
||||
uint8_t job_id;
|
||||
uint16_t version;
|
||||
uint8_t crc;
|
||||
} asic_result;
|
||||
|
||||
static const char * TAG = "bm1368Module";
|
||||
|
||||
static uint8_t asic_response_buffer[CHUNK_SIZE];
|
||||
static task_result result;
|
||||
|
||||
/// @brief
|
||||
/// @param ftdi
|
||||
/// @param header
|
||||
/// @param data
|
||||
/// @param len
|
||||
static void _send_BM1368(uint8_t header, uint8_t * data, uint8_t data_len, bool debug)
|
||||
{
|
||||
packet_type_t packet_type = (header & TYPE_JOB) ? JOB_PACKET : CMD_PACKET;
|
||||
uint8_t total_length = (packet_type == JOB_PACKET) ? (data_len + 6) : (data_len + 5);
|
||||
|
||||
// allocate memory for buffer
|
||||
unsigned char * buf = malloc(total_length);
|
||||
|
||||
// add the preamble
|
||||
buf[0] = 0x55;
|
||||
buf[1] = 0xAA;
|
||||
|
||||
// add the header field
|
||||
buf[2] = header;
|
||||
|
||||
// add the length field
|
||||
buf[3] = (packet_type == JOB_PACKET) ? (data_len + 4) : (data_len + 3);
|
||||
|
||||
// add the data
|
||||
memcpy(buf + 4, data, data_len);
|
||||
|
||||
// add the correct crc type
|
||||
if (packet_type == JOB_PACKET) {
|
||||
uint16_t crc16_total = crc16_false(buf + 2, data_len + 2);
|
||||
buf[4 + data_len] = (crc16_total >> 8) & 0xFF;
|
||||
buf[5 + data_len] = crc16_total & 0xFF;
|
||||
} else {
|
||||
buf[4 + data_len] = crc5(buf + 2, data_len + 2);
|
||||
}
|
||||
|
||||
// send serial data
|
||||
SERIAL_send(buf, total_length, debug);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void _send_simple(uint8_t * data, uint8_t total_length)
|
||||
{
|
||||
unsigned char * buf = malloc(total_length);
|
||||
memcpy(buf, data, total_length);
|
||||
SERIAL_send(buf, total_length, false);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void _send_chain_inactive(void)
|
||||
{
|
||||
|
||||
unsigned char read_address[2] = {0x00, 0x00};
|
||||
// send serial data
|
||||
_send_BM1368((TYPE_CMD | GROUP_ALL | CMD_INACTIVE), read_address, 2, false);
|
||||
}
|
||||
|
||||
static void _set_chip_address(uint8_t chipAddr)
|
||||
{
|
||||
|
||||
unsigned char read_address[2] = {chipAddr, 0x00};
|
||||
// send serial data
|
||||
_send_BM1368((TYPE_CMD | GROUP_SINGLE | CMD_SETADDRESS), read_address, 2, false);
|
||||
}
|
||||
|
||||
void BM1368_send_hash_frequency(float target_freq)
|
||||
{
|
||||
// default 200Mhz if it fails
|
||||
unsigned char freqbuf[9] = {0x00, 0x08, 0x40, 0xA0, 0x02, 0x41}; // freqbuf - pll0_parameter
|
||||
float newf = 200.0;
|
||||
|
||||
uint8_t fb_divider = 0;
|
||||
uint8_t post_divider1 = 0, post_divider2 = 0;
|
||||
uint8_t ref_divider = 0;
|
||||
float min_difference = 10;
|
||||
|
||||
// refdiver is 2 or 1
|
||||
// postdivider 2 is 1 to 7
|
||||
// postdivider 1 is 1 to 7 and less than postdivider 2
|
||||
// fbdiv is 144 to 235
|
||||
for (uint8_t refdiv_loop = 2; refdiv_loop > 0 && fb_divider == 0; refdiv_loop--) {
|
||||
for (uint8_t postdiv1_loop = 7; postdiv1_loop > 0 && fb_divider == 0; postdiv1_loop--) {
|
||||
for (uint8_t postdiv2_loop = 1; postdiv2_loop < postdiv1_loop && fb_divider == 0; postdiv2_loop++) {
|
||||
int temp_fb_divider = round(((float) (postdiv1_loop * postdiv2_loop * target_freq * refdiv_loop) / 25.0));
|
||||
|
||||
if (temp_fb_divider >= 144 && temp_fb_divider <= 235) {
|
||||
float temp_freq = 25.0 * (float) temp_fb_divider / (float) (refdiv_loop * postdiv2_loop * postdiv1_loop);
|
||||
float freq_diff = fabs(target_freq - temp_freq);
|
||||
|
||||
if (freq_diff < min_difference) {
|
||||
fb_divider = temp_fb_divider;
|
||||
post_divider1 = postdiv1_loop;
|
||||
post_divider2 = postdiv2_loop;
|
||||
ref_divider = refdiv_loop;
|
||||
min_difference = freq_diff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fb_divider == 0) {
|
||||
puts("Finding dividers failed, using default value (200Mhz)");
|
||||
} else {
|
||||
newf = 25.0 / (float) (ref_divider * fb_divider) / (float) (post_divider1 * post_divider2);
|
||||
printf("final refdiv: %d, fbdiv: %d, postdiv1: %d, postdiv2: %d, min diff value: %f\n", ref_divider, fb_divider,
|
||||
post_divider1, post_divider2, min_difference);
|
||||
|
||||
freqbuf[3] = fb_divider;
|
||||
freqbuf[4] = ref_divider;
|
||||
freqbuf[5] = (((post_divider1 - 1) & 0xf) << 4) + ((post_divider2 - 1) & 0xf);
|
||||
|
||||
if (fb_divider * 25 / (float) ref_divider >= 2400) {
|
||||
freqbuf[2] = 0x50;
|
||||
}
|
||||
}
|
||||
|
||||
_send_BM1368((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbuf, 6, false);
|
||||
|
||||
ESP_LOGI(TAG, "Setting Frequency to %.2fMHz (%.2f)", target_freq, newf);
|
||||
}
|
||||
|
||||
static void do_frequency_ramp_up() {
|
||||
|
||||
//PLLO settings taken from a S21 dump.
|
||||
//todo: do this right.
|
||||
uint8_t freq_list[65][4] = {{0x40, 0xA2, 0x02, 0x55},
|
||||
{0x40, 0xAF, 0x02, 0x64},
|
||||
{0x40, 0xA5, 0x02, 0x54},
|
||||
{0x40, 0xA8, 0x02, 0x63},
|
||||
{0x40, 0xB6, 0x02, 0x63},
|
||||
{0x40, 0xA8, 0x02, 0x53},
|
||||
{0x40, 0xB4, 0x02, 0x53},
|
||||
{0x40, 0xA8, 0x02, 0x62},
|
||||
{0x40, 0xAA, 0x02, 0x43},
|
||||
{0x40, 0xA2, 0x02, 0x52},
|
||||
{0x40, 0xAB, 0x02, 0x52},
|
||||
{0x40, 0xB4, 0x02, 0x52},
|
||||
{0x40, 0xBD, 0x02, 0x52},
|
||||
{0x40, 0xA5, 0x02, 0x42},
|
||||
{0x40, 0xA1, 0x02, 0x61},
|
||||
{0x40, 0xA8, 0x02, 0x61},
|
||||
{0x40, 0xAF, 0x02, 0x61},
|
||||
{0x40, 0xB6, 0x02, 0x61},
|
||||
{0x40, 0xA2, 0x02, 0x51},
|
||||
{0x40, 0xA8, 0x02, 0x51},
|
||||
{0x40, 0xAE, 0x02, 0x51},
|
||||
{0x40, 0xB4, 0x02, 0x51},
|
||||
{0x40, 0xBA, 0x02, 0x51},
|
||||
{0x40, 0xA0, 0x02, 0x41},
|
||||
{0x40, 0xA5, 0x02, 0x41},
|
||||
{0x40, 0xAA, 0x02, 0x41},
|
||||
{0x40, 0xAF, 0x02, 0x41},
|
||||
{0x40, 0xB4, 0x02, 0x41},
|
||||
{0x40, 0xB9, 0x02, 0x41},
|
||||
{0x40, 0xBE, 0x02, 0x41},
|
||||
{0x40, 0xA0, 0x02, 0x31},
|
||||
{0x40, 0xA4, 0x02, 0x31},
|
||||
{0x40, 0xA8, 0x02, 0x31},
|
||||
{0x40, 0xAC, 0x02, 0x31},
|
||||
{0x40, 0xB0, 0x02, 0x31},
|
||||
{0x40, 0xB4, 0x02, 0x31},
|
||||
{0x40, 0xA1, 0x02, 0x60},
|
||||
{0x40, 0xBC, 0x02, 0x31},
|
||||
{0x40, 0xA8, 0x02, 0x60},
|
||||
{0x40, 0xAF, 0x02, 0x60},
|
||||
{0x50, 0xCC, 0x02, 0x31},
|
||||
{0x40, 0xB6, 0x02, 0x60},
|
||||
{0x50, 0xD4, 0x02, 0x31},
|
||||
{0x40, 0xA2, 0x02, 0x50},
|
||||
{0x40, 0xA5, 0x02, 0x50},
|
||||
{0x40, 0xA8, 0x02, 0x50},
|
||||
{0x40, 0xAB, 0x02, 0x50},
|
||||
{0x40, 0xAE, 0x02, 0x50},
|
||||
{0x40, 0xB1, 0x02, 0x50},
|
||||
{0x40, 0xB4, 0x02, 0x50},
|
||||
{0x40, 0xB7, 0x02, 0x50},
|
||||
{0x40, 0xBA, 0x02, 0x50},
|
||||
{0x40, 0xBD, 0x02, 0x50},
|
||||
{0x40, 0xA0, 0x02, 0x40},
|
||||
{0x50, 0xC3, 0x02, 0x50},
|
||||
{0x40, 0xA5, 0x02, 0x40},
|
||||
{0x50, 0xC9, 0x02, 0x50},
|
||||
{0x40, 0xAA, 0x02, 0x40},
|
||||
{0x50, 0xCF, 0x02, 0x50},
|
||||
{0x40, 0xAF, 0x02, 0x40},
|
||||
{0x50, 0xD5, 0x02, 0x50},
|
||||
{0x40, 0xB4, 0x02, 0x40},
|
||||
{0x50, 0xDB, 0x02, 0x50},
|
||||
{0x40, 0xB9, 0x02, 0x40},
|
||||
{0x50, 0xE0, 0x02, 0x50}};
|
||||
|
||||
uint8_t freq_cmd[6] = {0x00, 0x08, 0x40, 0xB4, 0x02, 0x40};
|
||||
|
||||
for (int i = 0; i < 65; i++) {
|
||||
freq_cmd[2] = freq_list[i][0];
|
||||
freq_cmd[3] = freq_list[i][1];
|
||||
freq_cmd[4] = freq_list[i][2];
|
||||
freq_cmd[5] = freq_list[i][3];
|
||||
_send_BM1368((TYPE_CMD | GROUP_ALL | CMD_WRITE), freq_cmd, 6, false);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
static void _send_init(uint64_t frequency) {
|
||||
|
||||
//enable and set version rolling mask to 0xFFFF
|
||||
unsigned char init0[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0xA4, 0x90, 0x00, 0xFF, 0xFF, 0x1C};
|
||||
_send_simple(init0, 11);
|
||||
|
||||
//enable and set version rolling mask to 0xFFFF (again)
|
||||
unsigned char init1[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0xA4, 0x90, 0x00, 0xFF, 0xFF, 0x1C};
|
||||
_send_simple(init1, 11);
|
||||
|
||||
//enable and set version rolling mask to 0xFFFF (again)
|
||||
unsigned char init2[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0xA4, 0x90, 0x00, 0xFF, 0xFF, 0x1C};
|
||||
_send_simple(init2, 11);
|
||||
|
||||
//read register 00 on all chips (should respond AA 55 13 68 00 00 00 00 00 00 0F)
|
||||
unsigned char init3[7] = {0x55, 0xAA, 0x52, 0x05, 0x00, 0x00, 0x0A};
|
||||
_send_simple(init3, 7);
|
||||
|
||||
//enable and set version rolling mask to 0xFFFF (again)
|
||||
unsigned char init4[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0xA4, 0x90, 0x00, 0xFF, 0xFF, 0x1C};
|
||||
_send_simple(init4, 11);
|
||||
|
||||
//Reg_A8
|
||||
unsigned char init5[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0xA8, 0x00, 0x07, 0x00, 0x00, 0x03};
|
||||
_send_simple(init5, 11);
|
||||
|
||||
//Misc Control
|
||||
unsigned char init6[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0x18, 0xFF, 0x0F, 0xC1, 0x00, 0x00};
|
||||
_send_simple(init6, 11);
|
||||
|
||||
//chain inactive
|
||||
unsigned char init7[7] = {0x55, 0xAA, 0x53, 0x05, 0x00, 0x00, 0x03};
|
||||
_send_simple(init7, 7);
|
||||
|
||||
//assign address 0x00 to the first chip
|
||||
unsigned char init8[7] = {0x55, 0xAA, 0x40, 0x05, 0x00, 0x00, 0x1C};
|
||||
_send_simple(init8, 7);
|
||||
|
||||
//Core Register Control
|
||||
unsigned char init9[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0x3C, 0x80, 0x00, 0x8B, 0x00, 0x12};
|
||||
_send_simple(init9, 11);
|
||||
|
||||
//Core Register Control
|
||||
unsigned char init10[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0x3C, 0x80, 0x00, 0x80, 0x18, 0x1F};
|
||||
_send_simple(init10, 11);
|
||||
|
||||
//set ticket mask
|
||||
unsigned char init11[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0xFF, 0x08};
|
||||
_send_simple(init11, 11);
|
||||
|
||||
//Analog Mux Control
|
||||
unsigned char init12[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0x54, 0x00, 0x00, 0x00, 0x03, 0x1D};
|
||||
_send_simple(init12, 11);
|
||||
|
||||
//Set the IO Driver Strength on chip 00
|
||||
unsigned char init13[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0x58, 0x02, 0x11, 0x11, 0x11, 0x06};
|
||||
_send_simple(init13, 11);
|
||||
|
||||
//Reg_A8
|
||||
unsigned char init14[11] = {0x55, 0xAA, 0x41, 0x09, 0x00, 0xA8, 0x00, 0x07, 0x01, 0xF0, 0x15};
|
||||
_send_simple(init14, 11);
|
||||
|
||||
//Misc Control
|
||||
unsigned char init15[11] = {0x55, 0xAA, 0x41, 0x09, 0x00, 0x18, 0xF0, 0x00, 0xC1, 0x00, 0x0C};
|
||||
_send_simple(init15, 11);
|
||||
|
||||
//Core Register Control
|
||||
unsigned char init16[11] = {0x55, 0xAA, 0x41, 0x09, 0x00, 0x3C, 0x80, 0x00, 0x8B, 0x00, 0x1A};
|
||||
_send_simple(init16, 11);
|
||||
|
||||
//Core Register Control
|
||||
unsigned char init17[11] = {0x55, 0xAA, 0x41, 0x09, 0x00, 0x3C, 0x80, 0x00, 0x80, 0x18, 0x17};
|
||||
_send_simple(init17, 11);
|
||||
|
||||
//Core Register Control
|
||||
unsigned char init18[11] = {0x55, 0xAA, 0x41, 0x09, 0x00, 0x3C, 0x80, 0x00, 0x82, 0xAA, 0x05};
|
||||
_send_simple(init18, 11);
|
||||
|
||||
do_frequency_ramp_up();
|
||||
|
||||
BM1368_send_hash_frequency(frequency);
|
||||
|
||||
}
|
||||
|
||||
// reset the BM1368 via the RTS line
|
||||
static void _reset(void)
|
||||
{
|
||||
gpio_set_level(BM1368_RST_PIN, 0);
|
||||
|
||||
// delay for 100ms
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
// set the gpio pin high
|
||||
gpio_set_level(BM1368_RST_PIN, 1);
|
||||
|
||||
// delay for 100ms
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static void _send_read_address(void)
|
||||
{
|
||||
|
||||
unsigned char read_address[2] = {0x00, 0x00};
|
||||
// send serial data
|
||||
_send_BM1368((TYPE_CMD | GROUP_ALL | CMD_READ), read_address, 2, false);
|
||||
}
|
||||
|
||||
void BM1368_init(uint64_t frequency)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing BM1368");
|
||||
|
||||
memset(asic_response_buffer, 0, 1024);
|
||||
|
||||
esp_rom_gpio_pad_select_gpio(BM1368_RST_PIN);
|
||||
gpio_set_direction(BM1368_RST_PIN, GPIO_MODE_OUTPUT);
|
||||
|
||||
// reset the bm1368
|
||||
_reset();
|
||||
|
||||
// send the init command
|
||||
//_send_read_address();
|
||||
|
||||
_send_init(frequency);
|
||||
}
|
||||
|
||||
// Baud formula = 25M/((denominator+1)*8)
|
||||
// The denominator is 5 bits found in the misc_control (bits 9-13)
|
||||
int BM1368_set_default_baud(void)
|
||||
{
|
||||
// default divider of 26 (11010) for 115,749
|
||||
unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01111010, 0b00110001}; // baudrate - misc_control
|
||||
_send_BM1368((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, false);
|
||||
return 115749;
|
||||
}
|
||||
|
||||
int BM1368_set_max_baud(void)
|
||||
{
|
||||
|
||||
/// return 115749;
|
||||
|
||||
// divider of 0 for 3,125,000
|
||||
ESP_LOGI(TAG, "Setting max baud of 1000000 ");
|
||||
|
||||
unsigned char init8[11] = {0x55, 0xAA, 0x51, 0x09, 0x00, 0x28, 0x11, 0x30, 0x02, 0x00, 0x03};
|
||||
_send_simple(init8, 11);
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
void BM1368_set_job_difficulty_mask(int difficulty)
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
// Default mask of 256 diff
|
||||
unsigned char job_difficulty_mask[9] = {0x00, TICKET_MASK, 0b00000000, 0b00000000, 0b00000000, 0b11111111};
|
||||
|
||||
// The mask must be a power of 2 so there are no holes
|
||||
// Correct: {0b00000000, 0b00000000, 0b11111111, 0b11111111}
|
||||
// Incorrect: {0b00000000, 0b00000000, 0b11100111, 0b11111111}
|
||||
// (difficulty - 1) if it is a pow 2 then step down to second largest for more hashrate sampling
|
||||
difficulty = _largest_power_of_two(difficulty) - 1;
|
||||
|
||||
// convert difficulty into char array
|
||||
// Ex: 256 = {0b00000000, 0b00000000, 0b00000000, 0b11111111}, {0x00, 0x00, 0x00, 0xff}
|
||||
// Ex: 512 = {0b00000000, 0b00000000, 0b00000001, 0b11111111}, {0x00, 0x00, 0x01, 0xff}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char value = (difficulty >> (8 * i)) & 0xFF;
|
||||
// The char is read in backwards to the register so we need to reverse them
|
||||
// So a mask of 512 looks like 0b00000000 00000000 00000001 1111111
|
||||
// and not 0b00000000 00000000 10000000 1111111
|
||||
|
||||
job_difficulty_mask[5 - i] = _reverse_bits(value);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Setting job ASIC mask to %d", difficulty);
|
||||
|
||||
_send_BM1368((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, false);
|
||||
}
|
||||
|
||||
static uint8_t id = 0;
|
||||
|
||||
void BM1368_send_work(void * pvParameters, bm_job * next_bm_job)
|
||||
{
|
||||
|
||||
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
|
||||
|
||||
BM1368_job job;
|
||||
id = (id + 24) % 128;
|
||||
job.job_id = id;
|
||||
job.num_midstates = 0x01;
|
||||
memcpy(&job.starting_nonce, &next_bm_job->starting_nonce, 4);
|
||||
memcpy(&job.nbits, &next_bm_job->target, 4);
|
||||
memcpy(&job.ntime, &next_bm_job->ntime, 4);
|
||||
memcpy(job.merkle_root, next_bm_job->merkle_root_be, 32);
|
||||
memcpy(job.prev_block_hash, next_bm_job->prev_block_hash_be, 32);
|
||||
memcpy(&job.version, &next_bm_job->version, 4);
|
||||
|
||||
if (GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] != NULL) {
|
||||
free_bm_job(GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id]);
|
||||
}
|
||||
|
||||
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] = next_bm_job;
|
||||
|
||||
pthread_mutex_lock(&GLOBAL_STATE->valid_jobs_lock);
|
||||
GLOBAL_STATE->valid_jobs[job.job_id] = 1;
|
||||
// ESP_LOGI(TAG, "Added Job: %i", job.job_id);
|
||||
pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock);
|
||||
|
||||
_send_BM1368((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), &job, sizeof(BM1368_job), false);
|
||||
}
|
||||
|
||||
asic_result * BM1368_receive_work(void)
|
||||
{
|
||||
// wait for a response, wait time is pretty arbitrary
|
||||
int received = SERIAL_rx(asic_response_buffer, 11, 60000);
|
||||
|
||||
if (received < 0) {
|
||||
ESP_LOGI(TAG, "Error in serial RX");
|
||||
return NULL;
|
||||
} else if (received == 0) {
|
||||
// Didn't find a solution, restart and try again
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (received != 11 || asic_response_buffer[0] != 0xAA || asic_response_buffer[1] != 0x55) {
|
||||
ESP_LOGI(TAG, "Serial RX invalid %i", received);
|
||||
ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (asic_result *) asic_response_buffer;
|
||||
}
|
||||
|
||||
static uint16_t reverse_uint16(uint16_t num)
|
||||
{
|
||||
return (num >> 8) | (num << 8);
|
||||
}
|
||||
|
||||
task_result * BM1368_proccess_work(void * pvParameters)
|
||||
{
|
||||
|
||||
asic_result * asic_result = BM1368_receive_work();
|
||||
|
||||
if (asic_result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t job_id = asic_result->job_id;
|
||||
uint8_t rx_job_id = ((int8_t)job_id & 0xf0) >> 1;
|
||||
ESP_LOGI(TAG, "RX Job ID: %02X", rx_job_id);
|
||||
|
||||
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
|
||||
|
||||
if (GLOBAL_STATE->valid_jobs[rx_job_id] == 0) {
|
||||
ESP_LOGE(TAG, "Invalid job nonce found, 0x%02X", rx_job_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t rolled_version = GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[rx_job_id]->version;
|
||||
|
||||
// // // shift the 16 bit value left 13
|
||||
rolled_version = (reverse_uint16(asic_result->version) << 13) | rolled_version;
|
||||
|
||||
result.job_id = rx_job_id;
|
||||
result.nonce = asic_result->nonce;
|
||||
result.rolled_version = rolled_version;
|
||||
|
||||
return &result;
|
||||
}
|
44
components/bm1397/include/bm1368.h
Normal file
44
components/bm1397/include/bm1368.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef BM1368_H_
|
||||
#define BM1368_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mining.h"
|
||||
|
||||
#define CRC5_MASK 0x1F
|
||||
|
||||
// static const u_int64_t BM1368_FREQUENCY = CONFIG_ASIC_FREQUENCY;
|
||||
static const u_int64_t BM1368_CORE_COUNT = 672;
|
||||
// static const u_int64_t BM1368_HASHRATE_S = BM1368_FREQUENCY * BM1368_CORE_COUNT * 1000000;
|
||||
// 2^32
|
||||
// static const u_int64_t NONCE_SPACE = 4294967296;
|
||||
static const double BM1368_FULLSCAN_MS = 2140;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float frequency;
|
||||
} bm1368Module;
|
||||
|
||||
typedef struct __attribute__((__packed__))
|
||||
{
|
||||
uint8_t job_id;
|
||||
uint8_t num_midstates;
|
||||
uint8_t starting_nonce[4];
|
||||
uint8_t nbits[4];
|
||||
uint8_t ntime[4];
|
||||
uint8_t merkle_root[32];
|
||||
uint8_t prev_block_hash[32];
|
||||
uint8_t version[4];
|
||||
} BM1368_job;
|
||||
|
||||
void BM1368_init(u_int64_t frequency);
|
||||
|
||||
void BM1368_send_init(void);
|
||||
void BM1368_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
|
||||
void BM1368_set_job_difficulty_mask(int);
|
||||
int BM1368_set_max_baud(void);
|
||||
int BM1368_set_default_baud(void);
|
||||
void BM1368_send_hash_frequency(float frequency);
|
||||
task_result * BM1368_proccess_work(void * GLOBAL_STATE);
|
||||
|
||||
#endif /* BM1368_H_ */
|
@ -122,16 +122,16 @@ void STRATUM_V1_parse(StratumApiV1Message * message, const char * stratum_json)
|
||||
} else {
|
||||
// parse results
|
||||
cJSON * result_json = cJSON_GetObjectItem(json, "result");
|
||||
if (result_json != NULL && cJSON_IsBool(result_json)) {
|
||||
|
||||
if (result_json == NULL){
|
||||
message->response_success = false;
|
||||
}
|
||||
else if (cJSON_IsBool(result_json)) {
|
||||
result = STRATUM_RESULT;
|
||||
|
||||
bool response_success = false;
|
||||
if (result_json != NULL && cJSON_IsTrue(result_json)) {
|
||||
response_success = true;
|
||||
if (cJSON_IsTrue(result_json)) {
|
||||
message->response_success = true;
|
||||
}else{
|
||||
message->response_success = false;
|
||||
}
|
||||
|
||||
message->response_success = response_success;
|
||||
} else {
|
||||
cJSON * mask = cJSON_GetObjectItem(result_json, "version-rolling.mask");
|
||||
if (mask != NULL) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define GLOBAL_STATE_H_
|
||||
|
||||
#include "asic_task.h"
|
||||
#include "bm1368.h"
|
||||
#include "bm1366.h"
|
||||
#include "bm1397.h"
|
||||
#include "common.h"
|
||||
|
@ -46,8 +46,8 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "750kb",
|
||||
"maximumError": "1mb"
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "3mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
|
16
main/http_server/axe-os/only-gzip.js
Normal file
16
main/http_server/axe-os/only-gzip.js
Normal file
@ -0,0 +1,16 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const directory = './dist/axe-os';
|
||||
|
||||
fs.readdir(directory, (err, files) => {
|
||||
if (err) throw err;
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.gz')) {
|
||||
fs.unlink(path.join(directory, file), err => {
|
||||
if (err) throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
5552
main/http_server/axe-os/package-lock.json
generated
5552
main/http_server/axe-os/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,31 +4,37 @@
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --configuration=production && gzipper compress --verbose --gzip --gzip-level 9 ./dist/axe-os",
|
||||
"build": "ng build --configuration=production && gzipper compress --verbose --gzip --gzip-level 9 ./dist/axe-os && node only-gzip.js",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test",
|
||||
"bundle-report": "ng build --configuration=production --stats-json && webpack-bundle-analyzer dist/axe-os/stats.json"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^16.1.0",
|
||||
"@angular/common": "^16.1.0",
|
||||
"@angular/compiler": "^16.1.0",
|
||||
"@angular/core": "^16.1.0",
|
||||
"@angular/forms": "^16.1.0",
|
||||
"@angular/platform-browser": "^16.1.0",
|
||||
"@angular/platform-browser-dynamic": "^16.1.0",
|
||||
"@angular/router": "^16.1.0",
|
||||
"@angular/animations": "17.2.2",
|
||||
"@angular/common": "17.2.2",
|
||||
"@angular/compiler": "17.2.2",
|
||||
"@angular/core": "17.2.2",
|
||||
"@angular/forms": "17.2.2",
|
||||
"@angular/platform-browser": "17.2.2",
|
||||
"@angular/platform-browser-dynamic": "17.2.2",
|
||||
"@angular/router": "17.2.2",
|
||||
"chart.js": "^4.4.1",
|
||||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"moment": "^2.30.1",
|
||||
"ngx-toastr": "^17.0.2",
|
||||
"primeflex": "^3.3.1",
|
||||
"primeicons": "^6.0.1",
|
||||
"primeng": "^17.8.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"xterm": "^5.2.1",
|
||||
"zone.js": "~0.13.0"
|
||||
"zone.js": "~0.14.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^16.1.3",
|
||||
"@angular/cli": "~16.1.3",
|
||||
"@angular/compiler-cli": "^16.1.0",
|
||||
"@angular-devkit/build-angular": "17.2.1",
|
||||
"@angular/cli": "17.2.1",
|
||||
"@angular/compiler-cli": "17.2.2",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"gzipper": "^7.2.0",
|
||||
"jasmine-core": "~4.6.0",
|
||||
@ -37,7 +43,7 @@
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.1.3",
|
||||
"typescript": "~5.3.3",
|
||||
"webpack-bundle-analyzer": "^4.9.0"
|
||||
}
|
||||
}
|
@ -2,22 +2,35 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { LogsComponent } from './components/logs/logs.component';
|
||||
import { SettingsComponent } from './components/settings/settings.component';
|
||||
import { SwarmComponent } from './components/swarm/swarm.component';
|
||||
import { AppLayoutComponent } from './layout/app.layout.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: HomeComponent
|
||||
component: AppLayoutComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: HomeComponent
|
||||
},
|
||||
{
|
||||
path: 'logs',
|
||||
component: LogsComponent
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent
|
||||
},
|
||||
{
|
||||
path: 'swarm',
|
||||
component: SwarmComponent
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent
|
||||
},
|
||||
{
|
||||
path: 'swarm',
|
||||
component: SwarmComponent
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -1,22 +1,9 @@
|
||||
<app-loading></app-loading>
|
||||
<app-header></app-header>
|
||||
<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]="['settings']" 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>
|
||||
|
@ -1,69 +0,0 @@
|
||||
.body {
|
||||
margin-top: 100px;
|
||||
|
||||
padding-left: 5vw;
|
||||
padding-right: 5vw;
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width:900px) {
|
||||
.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: 50px;
|
||||
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,7 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
import { SystemService } from './services/system.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -10,19 +7,11 @@ import { SystemService } from './services/system.service';
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(
|
||||
private systemService: SystemService,
|
||||
private toastr: ToastrService,
|
||||
|
||||
) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public restart() {
|
||||
this.systemService.restart().subscribe(res => {
|
||||
|
||||
});
|
||||
this.toastr.success('Success!', 'Bitaxe restarted');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'chartjs-adapter-moment';
|
||||
|
||||
import { CommonModule, HashLocationStrategy, LocationStrategy } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
@ -9,21 +11,26 @@ import { ToastrModule } from 'ngx-toastr';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { EditComponent } from './components/edit/edit.component';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { LoadingComponent } from './components/loading/loading.component';
|
||||
import { LogsComponent } from './components/logs/logs.component';
|
||||
import { SettingsComponent } from './components/settings/settings.component';
|
||||
import { SwarmComponent } from './components/swarm/swarm.component';
|
||||
import { AppLayoutModule } from './layout/app.layout.module';
|
||||
import { ANSIPipe } from './pipes/ansi.pipe';
|
||||
import { DateAgoPipe } from './pipes/date-ago.pipe';
|
||||
import { HashSuffixPipe } from './pipes/hash-suffix.pipe';
|
||||
import { PrimeNGModule } from './prime-ng.module';
|
||||
|
||||
|
||||
|
||||
const components = [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
EditComponent,
|
||||
HomeComponent,
|
||||
LoadingComponent,
|
||||
SettingsComponent
|
||||
SettingsComponent,
|
||||
LogsComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
@ -33,7 +40,8 @@ const components = [
|
||||
ANSIPipe,
|
||||
DateAgoPipe,
|
||||
SwarmComponent,
|
||||
SettingsComponent
|
||||
SettingsComponent,
|
||||
HashSuffixPipe
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -45,7 +53,9 @@ const components = [
|
||||
positionClass: 'toast-bottom-right'
|
||||
}),
|
||||
BrowserAnimationsModule,
|
||||
CommonModule
|
||||
CommonModule,
|
||||
PrimeNGModule,
|
||||
AppLayoutModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||
|
@ -1,129 +1,160 @@
|
||||
<ng-container *ngIf="form != null">
|
||||
<form [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<label>Flip Screen</label>
|
||||
<input formControlName="flipscreen" type="checkbox">
|
||||
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="ssid" class="col-12 mb-2 md:col-2 md:mb-0">WiFi SSID:</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="ssid" type="text" formControlName="ssid" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label>Invert Screen</label>
|
||||
<input formControlName="invertscreen" type="checkbox">
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label>WiFi SSID: </label>
|
||||
<input formControlName="ssid" type="text">
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="wifiPass" class="col-12 mb-2 md:col-2 md:mb-0">WiFi Password:</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="wifiPass" formControlName="wifiPass" type="password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>WiFi Password: </label>
|
||||
<input formControlName="wifiPass" type="password">
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="stratumURL" class="col-12 mb-2 md:col-2 md:mb-0">Stratum URL:</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="stratumURL" type="text" formControlName="stratumURL"
|
||||
formControlName="stratumURL" />
|
||||
<div>
|
||||
<small>Do not include 'stratum+tcp://' or port.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stratum URL: (Do not include 'stratum+tcp://' or port</label>
|
||||
<input formControlName="stratumURL" type="text">
|
||||
|
||||
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="stratumPort" class="col-12 mb-2 md:col-2 md:mb-0">Stratum Port:</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="stratumPort" formControlName="stratumPort" type="number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stratum Port:</label>
|
||||
<input formControlName="stratumPort" type="number">
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="stratumUser" class="col-12 mb-2 md:col-2 md:mb-0">Stratum User:</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="stratumUser" formControlName="stratumUser" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stratum User: </label>
|
||||
<input formControlName="stratumUser" type="text">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stratum Password: </label>
|
||||
<input formControlName="stratumPassword" type="password">
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="stratumPassword" class="col-12 mb-2 md:col-2 md:mb-0">Stratum Password:</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="stratumPassword" formControlName="stratumPassword" type="password" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<ng-container *ngIf="!devToolsOpen && ASICModel == eASICModel.BM1366">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Frequency </label>
|
||||
<select formControlName="frequency">
|
||||
<option value="400">400</option>
|
||||
<option value="425">425</option>
|
||||
<option value="450">450</option>
|
||||
<option value="475">475</option>
|
||||
<option value="485">485 (default)</option>
|
||||
<option value="500">500</option>
|
||||
<option value="525">525</option>
|
||||
<option value="550">550</option>
|
||||
<option value="575">575</option>
|
||||
</select>
|
||||
<div class="field grid p-fluid">
|
||||
<label class="col-12 mb-2 md:col-2 md:mb-0" htmlFor="frequency">Frequency</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<p-dropdown [options]="BM1366DropdownFrequency" optionLabel="name" optionValue="value"
|
||||
formControlName="frequency"></p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Core Voltage </label>
|
||||
<select formControlName="coreVoltage">
|
||||
<option value="1100">1100</option>
|
||||
<option value="1150">1150</option>
|
||||
<option value="1200">1200 (default)</option>
|
||||
<option value="1250">1250</option>
|
||||
<option value="1300">1300</option>
|
||||
</select>
|
||||
|
||||
<div class="field grid p-fluid">
|
||||
<label class="col-12 mb-2 md:col-2 md:mb-0" htmlFor="coreVoltage">Core Voltage</label>
|
||||
<p-dropdown class="col-12 md:col-10" [options]="BM1366CoreVoltage" optionLabel="name"
|
||||
optionValue="value" formControlName="coreVoltage"></p-dropdown>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!devToolsOpen && ASICModel == eASICModel.BM1368">
|
||||
|
||||
<div class="field grid p-fluid">
|
||||
<label class="col-12 mb-2 md:col-2 md:mb-0" htmlFor="frequency">Frequency</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<p-dropdown [options]="BM1368DropdownFrequency" optionLabel="name" optionValue="value"
|
||||
formControlName="frequency"></p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field grid p-fluid">
|
||||
<label class="col-12 mb-2 md:col-2 md:mb-0" htmlFor="coreVoltage">Core Voltage</label>
|
||||
<p-dropdown class="col-12 md:col-10" [options]="BM1368CoreVoltage" optionLabel="name"
|
||||
optionValue="value" formControlName="coreVoltage"></p-dropdown>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!devToolsOpen && ASICModel == eASICModel.BM1397">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Frequency </label>
|
||||
<select formControlName="frequency">
|
||||
<option value="400">400</option>
|
||||
<option value="425">425 (default)</option>
|
||||
<option value="450">450</option>
|
||||
<option value="475">475</option>
|
||||
<option value="485">485</option>
|
||||
<option value="500">500</option>
|
||||
<option value="525">525</option>
|
||||
<option value="550">550</option>
|
||||
<option value="575">575</option>
|
||||
</select>
|
||||
<div class="field grid p-fluid">
|
||||
<label class="col-12 mb-2 md:col-2 md:mb-0" htmlFor="frequency">Frequency</label>
|
||||
|
||||
<p-dropdown class="col-12 md:col-10" [options]="BM1397DropdownFrequency" optionLabel="name"
|
||||
optionValue="value" formControlName="frequency"></p-dropdown>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Core Voltage </label>
|
||||
<select formControlName="coreVoltage">
|
||||
<option value="1300">1300</option>
|
||||
<option value="1350">1350</option>
|
||||
<option value="1400">1400 (default)</option>
|
||||
<option value="1450">1450</option>
|
||||
<option value="1500">1500</option>
|
||||
</select>
|
||||
<div class="field grid p-fluid">
|
||||
<label class="col-12 mb-2 md:col-2 md:mb-0" htmlFor="coreVoltage">Core Voltage</label>
|
||||
<p-dropdown class="col-12 md:col-10" [options]="BM1397CoreVoltage" optionLabel="name"
|
||||
optionValue="value" formControlName="coreVoltage"></p-dropdown>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="devToolsOpen === true">
|
||||
<div class="form-group">
|
||||
<label>Frequency </label>
|
||||
<input formControlName="frequency" type="number">
|
||||
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="frequency" class="col-12 mb-2 md:col-2 md:mb-0">Frequency</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="frequency" formControlName="frequency" type="number" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Core Voltage</label>
|
||||
<input formControlName="coreVoltage" type="number">
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="coreVoltage" class="col-12 mb-2 md:col-2 md:mb-0">Core Voltage</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<input pInputText id="coreVoltage" formControlName="coreVoltage" type="number" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Invert Fan Polarity</label>
|
||||
<input formControlName="invertfanpolarity" type="checkbox">
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="field-checkbox">
|
||||
<p-checkbox name="flipscreen" formControlName="flipscreen" inputId="flipscreen"
|
||||
[binary]="true"></p-checkbox>
|
||||
<label for="flipscreen">Flip Screen</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Automatic Fan Control</label>
|
||||
<input formControlName="autofanspeed" type="checkbox">
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="field-checkbox">
|
||||
<p-checkbox name="invertfanpolarity" formControlName="invertfanpolarity" inputId="invertfanpolarity"
|
||||
[binary]="true"></p-checkbox>
|
||||
<label for="invertfanpolarity">Invert Fan Polarity</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="field-checkbox">
|
||||
<p-checkbox name="autofanspeed" formControlName="autofanspeed" inputId="autofanspeed"
|
||||
[binary]="true"></p-checkbox>
|
||||
<label for="autofanspeed">Automatic Fan Control</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group" *ngIf="form.controls['autofanspeed'].value != true">
|
||||
<label>Fan Speed {{form.controls['fanspeed'].value}}%
|
||||
<b *ngIf="form.controls['fanspeed'].value < 33" style="color:red">Danger: Could Cause
|
||||
Overheating</b> <b *ngIf="form.controls['fanspeed'].value == 100" style="color: #F2A900">S19
|
||||
Simulator</b></label>
|
||||
<input formControlName="fanspeed" type="range" [min]="0" [max]="100">
|
||||
<div *ngIf="form.controls['autofanspeed'].value != true">
|
||||
<div class="col-12" *ngIf="form.controls['autofanspeed'].value != true">
|
||||
<label>Fan Speed {{form.controls['fanspeed'].value}}%
|
||||
<b *ngIf="form.controls['fanspeed'].value < 33" style="color:red">Danger: Could Cause
|
||||
Overheating</b> <b *ngIf="form.controls['fanspeed'].value == 100" style="color: #F2A900">S19
|
||||
Simulator</b></label>
|
||||
|
||||
<p-slider formControlName="fanspeed"></p-slider>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button [disabled]="form.invalid" (click)="updateSystem()" class="btn btn-primary mr-2">Save</button>
|
||||
<button pButton [disabled]="form.invalid" (click)="updateSystem()"
|
||||
class="btn btn-primary mr-2">Save</button>
|
||||
<b style="line-height: 34px;">You must restart this device after saving for changes to take effect.</b>
|
||||
</div>
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
input[type="password"],
|
||||
input[type="range"] {
|
||||
min-width: 250px;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
select {
|
||||
min-width: 268px;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.restart {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (min-width:900px) {
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
input[type="range"] {
|
||||
min-width: 500px
|
||||
}
|
||||
|
||||
select {
|
||||
min-width: 518px
|
||||
}
|
||||
}
|
@ -26,6 +26,64 @@ export class EditComponent implements OnInit {
|
||||
|
||||
@Input() uri = '';
|
||||
|
||||
public BM1397DropdownFrequency = [
|
||||
{ name: '400', value: 400 },
|
||||
{ name: '425 (default)', value: 425 },
|
||||
{ name: '450', value: 450 },
|
||||
{ name: '475', value: 475 },
|
||||
{ name: '485', value: 485 },
|
||||
{ name: '500', value: 500 },
|
||||
{ name: '525', value: 525 },
|
||||
{ name: '550', value: 550 },
|
||||
{ name: '575', value: 575 },
|
||||
];
|
||||
|
||||
public BM1366DropdownFrequency = [
|
||||
{ name: '400', value: 400 },
|
||||
{ name: '425', value: 425 },
|
||||
{ name: '450', value: 450 },
|
||||
{ name: '475', value: 475 },
|
||||
{ name: '485 (default)', value: 485 },
|
||||
{ name: '500', value: 500 },
|
||||
{ name: '525', value: 525 },
|
||||
{ name: '550', value: 550 },
|
||||
{ name: '575', value: 575 },
|
||||
];
|
||||
|
||||
public BM1368DropdownFrequency = [
|
||||
{ name: '400', value: 400 },
|
||||
{ name: '425', value: 425 },
|
||||
{ name: '450', value: 450 },
|
||||
{ name: '475', value: 475 },
|
||||
{ name: '490 (default)', value: 490 },
|
||||
{ name: '500', value: 500 },
|
||||
{ name: '525', value: 525 },
|
||||
{ name: '550', value: 550 },
|
||||
{ name: '575', value: 575 },
|
||||
];
|
||||
|
||||
public BM1397CoreVoltage = [
|
||||
{ name: '1100', value: 1100 },
|
||||
{ name: '1150', value: 1150 },
|
||||
{ name: '1200', value: 1200 },
|
||||
{ name: '1250', value: 1250 },
|
||||
{ name: '1300', value: 1300 },
|
||||
];
|
||||
public BM1366CoreVoltage = [
|
||||
{ name: '1100', value: 1100 },
|
||||
{ name: '1150', value: 1150 },
|
||||
{ name: '1200 (default)', value: 1200 },
|
||||
{ name: '1250', value: 1250 },
|
||||
{ name: '1300', value: 1300 },
|
||||
];
|
||||
public BM1368CoreVoltage = [
|
||||
{ name: '1100', value: 1100 },
|
||||
{ name: '1150', value: 1150 },
|
||||
{ name: '1200', value: 1200 },
|
||||
{ name: '1250', value: 1250 },
|
||||
{ name: '1300', value: 1300 },
|
||||
];
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private systemService: SystemService,
|
||||
@ -96,15 +154,6 @@ export class EditComponent implements OnInit {
|
||||
|
||||
const form = this.form.getRawValue();
|
||||
|
||||
form.frequency = parseInt(form.frequency);
|
||||
form.coreVoltage = parseInt(form.coreVoltage);
|
||||
|
||||
// bools to ints
|
||||
form.flipscreen = form.flipscreen == true ? 1 : 0;
|
||||
form.invertscreen = form.invertscreen == true ? 1 : 0;
|
||||
form.invertfanpolarity = form.invertfanpolarity == true ? 1 : 0;
|
||||
form.autofanspeed = form.autofanspeed == true ? 1 : 0;
|
||||
|
||||
if (form.wifiPass === 'password') {
|
||||
delete form.wifiPass;
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
<div [routerLink]="['/']" class="header-text">Axe<span class="os">OS</span></div>
|
@ -1,22 +0,0 @@
|
||||
:host {
|
||||
height: 70px;
|
||||
background-color: #1f2d40;
|
||||
border-bottom: 1px solid #304562;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
font-size: 30pt;
|
||||
padding: 6px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.os {
|
||||
color: #64B5F6;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeaderComponent } from './header.component';
|
||||
|
||||
describe('HeaderComponent', () => {
|
||||
let component: HeaderComponent;
|
||||
let fixture: ComponentFixture<HeaderComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [HeaderComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(HeaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss']
|
||||
})
|
||||
export class HeaderComponent {
|
||||
|
||||
}
|
@ -3,44 +3,182 @@
|
||||
</ng-template>
|
||||
<ng-container>
|
||||
|
||||
<div class="grid" *ngIf="info$ | async as info; else loading">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="grid mb-4">
|
||||
<div class="col-12 lg:col-6 xl:col-3">
|
||||
<div class="card mb-0">
|
||||
<div class="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span class="block text-500 font-medium mb-3">Hash Rate</span>
|
||||
<div class="text-900 font-medium text-xl">{{info.hashRate | number: '1.2-2'}}
|
||||
<small>Gh/s</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-orange-100 border-round"
|
||||
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||
<i class="pi pi-bolt text-orange-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="expectedHashRate$ | async as expectedHashRate">
|
||||
<span class="text-green-500 font-medium">{{expectedHashRate}}
|
||||
<small>Gh/s </small></span>
|
||||
<span class="text-500">expected</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 lg:col-6 xl:col-3">
|
||||
<div class="card mb-0">
|
||||
<div class="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span class="block text-500 font-medium mb-3">Shares</span>
|
||||
<div class="text-900 font-medium text-xl">{{info.sharesAccepted}}</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-blue-100 border-round"
|
||||
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||
<i class="pi pi-send text-blue-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-red-500 font-medium">{{info.sharesRejected}} </span>
|
||||
<span class="text-500">rejected</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 lg:col-6 xl:col-3">
|
||||
<div class="card mb-0">
|
||||
<div class="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span class="block text-500 font-medium mb-3">Efficiency</span>
|
||||
<div class="text-900 font-medium text-xl">
|
||||
<td>{{info.power / (info.hashRate/1000) | number: '1.2-2'}} <small>W/Th</small>
|
||||
</td>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-orange-100 border-round"
|
||||
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||
<i class="pi pi-thumbs-up text-orange-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <span class="text-green-500 font-medium">%52+ </span>
|
||||
<span class="text-500">since last week</span> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 lg:col-6 xl:col-3">
|
||||
<div class="card mb-0">
|
||||
<div class="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span class="block text-500 font-medium mb-3">Best Difficulty</span>
|
||||
<div class="text-900 font-medium text-xl">{{info.bestDiff}}</div>
|
||||
</div>
|
||||
<div class="flex align-items-center justify-content-center bg-yellow-100 border-round"
|
||||
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||
<i class="pi pi-star-fill text-yellow-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <span class="text-green-500 font-medium">520 </span>
|
||||
<span class="text-500">newly registered</span> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div>
|
||||
<div class="card">
|
||||
<h2>Overview</h2>
|
||||
<table *ngIf="info$ | async as info; else loading">
|
||||
<tr>
|
||||
<td>Model:</td>
|
||||
<td>{{info.ASICModel}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uptime:</td>
|
||||
<td>{{info.uptimeSeconds | dateAgo}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WiFi Status:</td>
|
||||
<td>{{info.wifiStatus}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Free Heap Memory:</td>
|
||||
<td>{{info.freeHeap}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version:</td>
|
||||
<td>{{info.version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Board Version:</td>
|
||||
<td>{{info.boardVersion}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<p-chart [data]="chartData" [options]="chartOptions"></p-chart>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 lg:col-12 xl:col-6">
|
||||
<div class="card">
|
||||
<h2>Pool Information</h2>
|
||||
<h5>Power</h5>
|
||||
<div class="grid text-center">
|
||||
<div class="col-4">
|
||||
<p-knob [min]="3" [max]="20" [readonly]="true" [(ngModel)]="info.power"
|
||||
valueTemplate="{value}W"></p-knob>
|
||||
Power
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<p-knob [min]="4.5" [max]="5.5" [readonly]="true" [(ngModel)]="info.voltage"
|
||||
valueTemplate="{value}V"></p-knob>
|
||||
Input Voltage
|
||||
<span class="danger" *ngIf="info.voltage < 4.8"> Danger: Low voltage</span>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<p-knob [min]="0.9" [max]="1.8" [readonly]="true" [(ngModel)]="info.coreVoltage"
|
||||
valueTemplate="{value}A"></p-knob>
|
||||
ASIC Voltage Requested
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 lg:col-6 xl:col-3">
|
||||
<div class="card">
|
||||
<h5>Heat</h5>
|
||||
<div class="grid text-center">
|
||||
<div class="col-6">
|
||||
<p-knob [min]="20" [max]="75" [readonly]="true" [(ngModel)]="info.temp"
|
||||
valueTemplate="{value}C"></p-knob>
|
||||
ASIC Temperature
|
||||
|
||||
<span class="danger" *ngIf="info.temp >= 70">
|
||||
Danger:
|
||||
High Temperature</span>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p-knob [min]="0" [max]="100" [readonly]="true" [(ngModel)]="info.fanspeed"
|
||||
valueTemplate="{value}%"></p-knob>
|
||||
Fan
|
||||
</div>
|
||||
<div class="col-6" *ngIf="info.boardtemp1">
|
||||
<p-knob [min]="20" [max]="75" [readonly]="true" [(ngModel)]="info.boardtemp1"
|
||||
valueTemplate="{value}C"></p-knob>
|
||||
Board Temp 1
|
||||
</div>
|
||||
<div class="col-6" *ngIf="info.boardtemp2">
|
||||
<p-knob [min]="20" [max]="75" [readonly]="true" [(ngModel)]="info.boardtemp2"
|
||||
valueTemplate="{value}C"></p-knob>
|
||||
Board Temp 2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="col-12 lg:col-6 xl:col-3">
|
||||
<div class="card">
|
||||
<h5>Performance</h5>
|
||||
<div class="grid text-center">
|
||||
<div class="col-6">
|
||||
<p-knob [min]="100" [max]="800" [readonly]="true" [(ngModel)]="info.frequency"
|
||||
valueTemplate="{value}Mhz"></p-knob>
|
||||
ASIC Frequency
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p-knob [min]="0.9" [max]="1.8" [readonly]="true" [(ngModel)]="info.coreVoltageActual"
|
||||
valueTemplate="{value}V"></p-knob>
|
||||
ASIC Voltage Measured
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class=" col-12 lg:col-6">
|
||||
<div class="card">
|
||||
<h5>Pool Information</h5>
|
||||
<table *ngIf="info$ | async as info; else loading">
|
||||
<tr *ngIf="quickLink$ | async as quickLink">
|
||||
<td>Quick Link:</td>
|
||||
<td><a style="color: white;" [href]="quickLink" target="_blank">{{quickLink}}</a></td>
|
||||
<td><a style="color: white; text-decoration: underline;" [href]="quickLink"
|
||||
target="_blank">{{quickLink}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL:</td>
|
||||
@ -54,97 +192,13 @@
|
||||
<td>User:</td>
|
||||
<td>{{info.stratumUser}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shares Accepted:</td>
|
||||
<td>{{info.sharesAccepted}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shares Rejected:</td>
|
||||
<td>{{info.sharesRejected}}</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="card">
|
||||
<h2>Power</h2>
|
||||
|
||||
<table *ngIf="info$ | async as info; else loading">
|
||||
<tr>
|
||||
<td>Power Consumption:</td>
|
||||
<td>{{info.power | number: '1.2-2'}} <small>W</small> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Input Voltage:</td>
|
||||
<td>{{info.voltage | number: '0.0-0'}} <small>mV</small> <span class="danger"
|
||||
*ngIf="info.voltage < 4800"> Danger: Low voltage</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Regulator Current:</td>
|
||||
<td>{{info.current | number: '0.0-0'}} <small>mA</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Frequency:</td>
|
||||
<td>{{info.frequency}} <small>Mhz</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Core Voltage:</td>
|
||||
<td>{{info.coreVoltage}} <small>mV</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Measured Core Voltage:</td>
|
||||
<td>{{info.coreVoltageActual}} <small>mV</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fan Speed:</td>
|
||||
<td>{{info.fanSpeed}} <small>RPM</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Chip Temperature:</td>
|
||||
<td>{{info.temp}} <small>C</small> <span class="danger" *ngIf="info.temp >= 100"> Danger:
|
||||
High Temperature</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Board Temperature 1:</td>
|
||||
<td>{{info.boardtemp1}} <small>C</small> <span class="danger" *ngIf="info.boardtemp1 >= 60"> Danger:
|
||||
High Temperature</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Board Temperature 2:</td>
|
||||
<td>{{info.boardtemp2}} <small>C</small> <span class="danger" *ngIf="info.boardtemp1 >= 60"> Danger:
|
||||
High Temperature</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Results</h2>
|
||||
<table *ngIf="info$ | async as info; else loading">
|
||||
<tr>
|
||||
<td> Hash Rate:</td>
|
||||
<td>{{info.hashRate | number: '1.2-2'}} <small>Gh/s</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Efficiency:</td>
|
||||
<td>{{info.power / (info.hashRate/1000) | number: '1.2-2'}} <small>W/Th</small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Best Difficulty:</td>
|
||||
<td>{{info.bestDiff}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-2">
|
||||
<h2>Realtime Logs <button (click)="toggleLogs()" 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>
|
||||
</ng-container>
|
||||
|
@ -1,56 +1,8 @@
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 2fr;
|
||||
grid-gap: 1.5rem;
|
||||
}
|
||||
|
||||
table>tr>td {
|
||||
|
||||
&:first-child {
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width:900px) {
|
||||
.container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.edit {
|
||||
margin-bottom: 1.5rem;
|
||||
margin-right: 1.5rem;
|
||||
|
||||
}
|
||||
|
||||
.restart {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#logs {
|
||||
height: 500px;
|
||||
font-family: monospace;
|
||||
border: 1px solid #304562;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
>div {
|
||||
max-width: 100%;
|
||||
line-break: anywhere;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card {
|
||||
min-height: 100%;
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import { AfterViewChecked, Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { interval, map, Observable, shareReplay, startWith, Subscription, switchMap } from 'rxjs';
|
||||
import { Component } from '@angular/core';
|
||||
import { interval, map, Observable, shareReplay, startWith, switchMap, tap } from 'rxjs';
|
||||
import { HashSuffixPipe } from 'src/app/pipes/hash-suffix.pipe';
|
||||
import { SystemService } from 'src/app/services/system.service';
|
||||
import { WebsocketService } from 'src/app/services/web-socket.service';
|
||||
import { eASICModel } from 'src/models/enum/eASICModel';
|
||||
import { ISystemInfo } from 'src/models/ISystemInfo';
|
||||
|
||||
@Component({
|
||||
@ -9,33 +10,143 @@ import { ISystemInfo } from 'src/models/ISystemInfo';
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss']
|
||||
})
|
||||
export class HomeComponent implements AfterViewChecked, OnDestroy {
|
||||
@ViewChild('scrollContainer') private scrollContainer!: ElementRef;
|
||||
export class HomeComponent {
|
||||
|
||||
|
||||
public info$: Observable<ISystemInfo>;
|
||||
|
||||
public quickLink$: Observable<string | undefined>;
|
||||
public expectedHashRate$: Observable<number | undefined>;
|
||||
|
||||
public logs: string[] = [];
|
||||
|
||||
private websocketSubscription?: Subscription;
|
||||
|
||||
public showLogs = false;
|
||||
|
||||
public chartOptions: any;
|
||||
public dataLabel: number[] = [];
|
||||
public dataData: number[] = [];
|
||||
public chartData?: any;
|
||||
|
||||
constructor(
|
||||
private systemService: SystemService,
|
||||
private websocketService: WebsocketService
|
||||
private systemService: SystemService
|
||||
) {
|
||||
|
||||
|
||||
const documentStyle = getComputedStyle(document.documentElement);
|
||||
const textColor = documentStyle.getPropertyValue('--text-color');
|
||||
const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary');
|
||||
const surfaceBorder = documentStyle.getPropertyValue('--surface-border');
|
||||
const primaryColor = documentStyle.getPropertyValue('--primary-color');
|
||||
|
||||
this.chartData = {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
type: 'line',
|
||||
label: '10 Minute',
|
||||
data: [],
|
||||
fill: false,
|
||||
backgroundColor: primaryColor,
|
||||
borderColor: primaryColor,
|
||||
tension: .4,
|
||||
pointRadius: 1,
|
||||
borderWidth: 1
|
||||
},
|
||||
|
||||
]
|
||||
};
|
||||
|
||||
this.chartOptions = {
|
||||
animation: false,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: {
|
||||
color: textColor
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'hour', // Set the unit to 'minute'
|
||||
},
|
||||
ticks: {
|
||||
color: textColorSecondary
|
||||
},
|
||||
grid: {
|
||||
color: surfaceBorder,
|
||||
drawBorder: false,
|
||||
display: true
|
||||
}
|
||||
},
|
||||
y: {
|
||||
ticks: {
|
||||
color: textColorSecondary,
|
||||
callback: (value: number) => HashSuffixPipe.transform(value)
|
||||
},
|
||||
grid: {
|
||||
color: surfaceBorder,
|
||||
drawBorder: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.info$ = interval(5000).pipe(
|
||||
startWith(() => this.systemService.getInfo()),
|
||||
switchMap(() => {
|
||||
return this.systemService.getInfo()
|
||||
}),
|
||||
tap(info => {
|
||||
|
||||
this.dataData.push(info.hashRate * 1000000000);
|
||||
this.dataLabel.push(new Date().getTime());
|
||||
|
||||
if (this.dataData.length >= 1000) {
|
||||
this.dataData.shift();
|
||||
this.dataLabel.shift();
|
||||
}
|
||||
|
||||
this.chartData.labels = this.dataLabel;
|
||||
this.chartData.datasets[0].data = this.dataData;
|
||||
|
||||
|
||||
|
||||
this.chartData = {
|
||||
...this.chartData
|
||||
};
|
||||
|
||||
}),
|
||||
map(info => {
|
||||
info.power = parseFloat(info.power.toFixed(1))
|
||||
info.voltage = parseFloat((info.voltage / 1000).toFixed(1));
|
||||
info.current = parseFloat((info.current / 1000).toFixed(1));
|
||||
info.coreVoltageActual = parseFloat((info.coreVoltageActual / 1000).toFixed(2));
|
||||
info.coreVoltage = parseFloat((info.coreVoltage / 1000).toFixed(2));
|
||||
|
||||
|
||||
|
||||
return info;
|
||||
}),
|
||||
shareReplay({ refCount: true, bufferSize: 1 })
|
||||
);
|
||||
|
||||
this.expectedHashRate$ = this.info$.pipe(map(info => {
|
||||
if (info.ASICModel === eASICModel.BM1366) {
|
||||
const version = parseInt(info.boardVersion);
|
||||
if (version >= 400 && version < 500) {
|
||||
return (info.frequency * ((894 * 6) / 1000))
|
||||
} else {
|
||||
return (info.frequency * (894 / 1000))
|
||||
}
|
||||
} else if (info.ASICModel === eASICModel.BM1397) {
|
||||
return (info.frequency * (672 / 1000))
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
}))
|
||||
|
||||
this.quickLink$ = this.info$.pipe(
|
||||
map(info => {
|
||||
if (info.stratumURL.includes('public-pool.io')) {
|
||||
@ -49,32 +160,9 @@ export class HomeComponent implements AfterViewChecked, OnDestroy {
|
||||
|
||||
|
||||
|
||||
}
|
||||
ngOnDestroy(): void {
|
||||
this.websocketSubscription?.unsubscribe();
|
||||
}
|
||||
ngAfterViewChecked(): void {
|
||||
if (this.scrollContainer?.nativeElement != null) {
|
||||
this.scrollContainer.nativeElement.scrollTo({ left: 0, top: this.scrollContainer.nativeElement.scrollHeight, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
public toggleLogs() {
|
||||
this.showLogs = !this.showLogs;
|
||||
|
||||
if (this.showLogs) {
|
||||
this.websocketSubscription = this.websocketService.ws$.subscribe({
|
||||
next: (val) => {
|
||||
this.logs.push(val);
|
||||
if (this.logs.length > 100) {
|
||||
this.logs.shift();
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.websocketSubscription?.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
<div class="grid">
|
||||
<div class="col-12 lg:col-6">
|
||||
<div class="card">
|
||||
<h5>Overview</h5>
|
||||
<table *ngIf="info$ | async as info">
|
||||
<tr>
|
||||
<td>Model:</td>
|
||||
<td>{{info.ASICModel}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uptime:</td>
|
||||
<td>{{info.uptimeSeconds | dateAgo}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WiFi Status:</td>
|
||||
<td>{{info.wifiStatus}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Free Heap Memory:</td>
|
||||
<td>{{info.freeHeap}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version:</td>
|
||||
<td>{{info.version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Board Version:</td>
|
||||
<td>{{info.boardVersion}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<h2>Realtime Logs <button pButton (click)="toggleLogs()" 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>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,13 @@
|
||||
#logs {
|
||||
height: 500px;
|
||||
font-family: monospace;
|
||||
border: 1px solid #304562;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
>div {
|
||||
max-width: 100%;
|
||||
line-break: anywhere;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LogsComponent } from './logs.component';
|
||||
|
||||
describe('LogsComponent', () => {
|
||||
let component: LogsComponent;
|
||||
let fixture: ComponentFixture<LogsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LogsComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LogsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,73 @@
|
||||
import { AfterViewChecked, Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { interval, map, Observable, shareReplay, startWith, Subscription, switchMap } from 'rxjs';
|
||||
import { SystemService } from 'src/app/services/system.service';
|
||||
import { WebsocketService } from 'src/app/services/web-socket.service';
|
||||
import { ISystemInfo } from 'src/models/ISystemInfo';
|
||||
|
||||
@Component({
|
||||
selector: 'app-logs',
|
||||
templateUrl: './logs.component.html',
|
||||
styleUrl: './logs.component.scss'
|
||||
})
|
||||
export class LogsComponent implements OnDestroy, AfterViewChecked {
|
||||
|
||||
@ViewChild('scrollContainer') private scrollContainer!: ElementRef;
|
||||
public info$: Observable<ISystemInfo>;
|
||||
|
||||
public logs: string[] = [];
|
||||
|
||||
private websocketSubscription?: Subscription;
|
||||
|
||||
public showLogs = false;
|
||||
|
||||
constructor(
|
||||
private websocketService: WebsocketService,
|
||||
private systemService: SystemService
|
||||
) {
|
||||
|
||||
|
||||
this.info$ = interval(5000).pipe(
|
||||
startWith(() => this.systemService.getInfo()),
|
||||
switchMap(() => {
|
||||
return this.systemService.getInfo()
|
||||
}),
|
||||
map(info => {
|
||||
info.power = parseFloat(info.power.toFixed(1))
|
||||
info.voltage = parseFloat((info.voltage / 1000).toFixed(1));
|
||||
info.current = parseFloat((info.current / 1000).toFixed(1));
|
||||
info.coreVoltageActual = parseFloat((info.coreVoltageActual / 1000).toFixed(2));
|
||||
info.coreVoltage = parseFloat((info.coreVoltage / 1000).toFixed(2));
|
||||
return info;
|
||||
}),
|
||||
shareReplay({ refCount: true, bufferSize: 1 })
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
ngOnDestroy(): void {
|
||||
this.websocketSubscription?.unsubscribe();
|
||||
}
|
||||
public toggleLogs() {
|
||||
this.showLogs = !this.showLogs;
|
||||
|
||||
if (this.showLogs) {
|
||||
this.websocketSubscription = this.websocketService.ws$.subscribe({
|
||||
next: (val) => {
|
||||
this.logs.push(val);
|
||||
if (this.logs.length > 100) {
|
||||
this.logs.shift();
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.websocketSubscription?.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewChecked(): void {
|
||||
if (this.scrollContainer?.nativeElement != null) {
|
||||
this.scrollContainer.nativeElement.scrollTo({ left: 0, top: this.scrollContainer.nativeElement.scrollHeight, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -6,33 +6,42 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card" *ngIf="latestRelease$ | async as latestRelease">
|
||||
<h2>Latest Release: {{latestRelease.name}}</h2>
|
||||
<div class="grid">
|
||||
<div class="col-12 lg:col-6 xl:col-4">
|
||||
<div class="card" *ngIf="latestRelease$ | async as latestRelease">
|
||||
<h2>Latest Release: {{latestRelease.name}}</h2>
|
||||
|
||||
|
||||
|
||||
<div *ngFor="let asset of latestRelease.assets">
|
||||
<div *ngIf="asset.name == 'esp-miner.bin'">
|
||||
<a target="_blank" [href]="asset.browser_download_url">esp-miner.bin</a>
|
||||
</div>
|
||||
<div *ngIf="asset.name == 'www.bin'">
|
||||
<a target="_blank" [href]="asset.browser_download_url">www.bin</a>
|
||||
<div *ngFor="let asset of latestRelease.assets">
|
||||
<div *ngIf="asset.name == 'esp-miner.bin'">
|
||||
<a style="text-decoration: underline;" target="_blank"
|
||||
[href]="asset.browser_download_url">esp-miner.bin</a>
|
||||
</div>
|
||||
<div *ngIf="asset.name == 'www.bin'">
|
||||
<a style="text-decoration: underline;" target="_blank"
|
||||
[href]="asset.browser_download_url">www.bin</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 lg:col-6 xl:col-4">
|
||||
<div class="card">
|
||||
<h2>Update Firmware <span *ngIf="firmwareUpdateProgress != null">{{firmwareUpdateProgress}}%</span></h2>
|
||||
<!-- <input type="file" id="file" (change)="otaUpdate($event)" accept=".bin"> -->
|
||||
<p-fileUpload [customUpload]="true" mode="basic" accept=".bin" (uploadHandler)="otaUpdate($event)"
|
||||
[auto]="true" chooseLabel="Browse"></p-fileUpload>
|
||||
|
||||
<small>(esp-miner.bin)</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 lg:col-12 xl:col-4">
|
||||
<div class="card">
|
||||
<h2>Update Website <span *ngIf="websiteUpdateProgress != null">{{websiteUpdateProgress}}%</span></h2>
|
||||
|
||||
<div class="card">
|
||||
<h2>Update Firmware <span *ngIf="firmwareUpdateProgress != null">{{firmwareUpdateProgress}}%</span></h2>
|
||||
<input type="file" id="file" (change)="otaUpdate($event)" accept=".bin">
|
||||
<br>
|
||||
<small>(esp-miner.bin)</small>
|
||||
</div>
|
||||
<p-fileUpload [customUpload]="true" mode="basic" accept=".bin" (uploadHandler)="otaWWWUpdate($event)"
|
||||
[auto]="true" chooseLabel="Browse"></p-fileUpload>
|
||||
|
||||
<div class="card">
|
||||
<h2>Update Website <span *ngIf="websiteUpdateProgress != null">{{websiteUpdateProgress}}%</span></h2>
|
||||
<input type="file" id="file" (change)="otaWWWUpdate($event)" accept=".bin">
|
||||
<br>
|
||||
<small>(www.bin)</small>
|
||||
<small>(www.bin)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -2,6 +2,7 @@ import { HttpErrorResponse, HttpEventType } from '@angular/common/http';
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { FileUploadHandlerEvent } from 'primeng/fileupload';
|
||||
import { map, Observable, startWith } from 'rxjs';
|
||||
import { GithubUpdateService } from 'src/app/services/github-update.service';
|
||||
import { LoadingService } from 'src/app/services/loading.service';
|
||||
@ -129,8 +130,8 @@ export class SettingsComponent {
|
||||
});
|
||||
}
|
||||
|
||||
otaUpdate(event: any) {
|
||||
const file = event.target?.files.item(0) as File;
|
||||
otaUpdate(event: FileUploadHandlerEvent) {
|
||||
const file = event.files[0];
|
||||
|
||||
if (file.name != 'esp-miner.bin') {
|
||||
this.toastrService.error('Incorrect file, looking for esp-miner.bin.', 'Error');
|
||||
@ -153,15 +154,16 @@ export class SettingsComponent {
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.toastrService.error(event.statusText, 'Error');
|
||||
this.toastrService.error('Uploaded Error', 'Error');
|
||||
},
|
||||
complete: () => {
|
||||
this.firmwareUpdateProgress = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
otaWWWUpdate(event: any) {
|
||||
const file = event.target?.files.item(0) as File;
|
||||
|
||||
otaWWWUpdate(event: FileUploadHandlerEvent) {
|
||||
const file = event.files[0];
|
||||
if (file.name != 'www.bin') {
|
||||
this.toastrService.error('Incorrect file, looking for www.bin.', 'Error');
|
||||
return;
|
||||
@ -187,7 +189,7 @@ export class SettingsComponent {
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.toastrService.error(event.statusText, 'Error');
|
||||
this.toastrService.error('Upload Error', 'Error');
|
||||
},
|
||||
complete: () => {
|
||||
this.websiteUpdateProgress = null;
|
||||
|
@ -1,11 +1,20 @@
|
||||
<form [formGroup]="form">
|
||||
<label>AxeOS Device IP: </label>
|
||||
<input formControlName="ip" type="text">
|
||||
<button style="margin-left: 15px;" class="btn btn-primary" (click)="add()" [disabled]="form.invalid">Add</button>
|
||||
<!-- <input type="checkbox" formControlName="sync"> Sync All -->
|
||||
</form>
|
||||
<div class="card">
|
||||
<form [formGroup]="form">
|
||||
<div class="field grid p-fluid">
|
||||
<label htmlFor="ip" class="col-12 mb-2 md:col-2 md:mb-0">AxeOS Device IP</label>
|
||||
<div class="col-12 md:col-10">
|
||||
<p-inputGroup>
|
||||
<input pInputText id="ip" formControlName="ip" type="text" />
|
||||
<button pButton (click)="add()" [disabled]="form.invalid">Add</button>
|
||||
</p-inputGroup>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary" (click)="refresh()">Refresh</button>
|
||||
<button pButton (click)="refresh()">Refresh</button>
|
||||
</div>
|
||||
<div>
|
||||
<table cellspacing="0" cellpadding="0" *ngIf="swarm$ | async as swarm">
|
||||
@ -32,9 +41,9 @@
|
||||
<td>{{axe.temp}} <small>C</small></td>
|
||||
<td>{{axe.bestDiff}}</td>
|
||||
<td>{{axe.version}}</td>
|
||||
<td><button class="btn btn-primary" (click)="edit(axe)">✎</button></td>
|
||||
<td><button class="btn btn-danger" (click)="restart(axe)">♺</button></td>
|
||||
<td><button class="btn btn-secondary" (click)="remove(axe)">✖</button></td>
|
||||
<td><p-button icon="pi pi-pencil" pp-button (click)="edit(axe)"></p-button></td>
|
||||
<td><p-button icon="pi pi-sync" pp-button severity="danger" (click)="restart(axe)"></p-button></td>
|
||||
<td><p-button icon="pi pi-trash" pp-button severity="secondary" (click)="remove(axe)"></p-button></td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</table>
|
||||
|
@ -61,13 +61,18 @@ export class SwarmComponent {
|
||||
const newIp = this.form.value.ip;
|
||||
|
||||
combineLatest([this.systemService.getSwarmInfo('http://' + newIp), this.systemService.getSwarmInfo()]).pipe(
|
||||
switchMap(([newSwarmInfo, swarmInfo]) => {
|
||||
switchMap(([newSwarmInfo, existingSwarmInfo]) => {
|
||||
|
||||
const swarmUpdate = swarmInfo.map(({ ip }) => {
|
||||
return this.systemService.updateSwarm('http://' + ip, [{ ip: newIp }, ...newSwarmInfo, ...swarmInfo])
|
||||
if (existingSwarmInfo.length < 1) {
|
||||
existingSwarmInfo.push({ ip: window.location.host });
|
||||
}
|
||||
|
||||
|
||||
const swarmUpdate = existingSwarmInfo.map(({ ip }) => {
|
||||
return this.systemService.updateSwarm('http://' + ip, [{ ip: newIp }, ...newSwarmInfo, ...existingSwarmInfo])
|
||||
});
|
||||
|
||||
const newAxeOs = this.systemService.updateSwarm('http://' + newIp, [{ ip: newIp }, ...swarmInfo])
|
||||
const newAxeOs = this.systemService.updateSwarm('http://' + newIp, [{ ip: newIp }, ...existingSwarmInfo])
|
||||
|
||||
return forkJoin([newAxeOs, ...swarmUpdate]);
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
export interface MenuChangeEvent {
|
||||
key: string;
|
||||
routeEvent?: boolean;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
<div class="layout-footer">
|
||||
|
||||
</div>
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { LayoutService } from "./service/app.layout.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './app.footer.component.html'
|
||||
})
|
||||
export class AppFooterComponent {
|
||||
constructor(public layoutService: LayoutService) { }
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<div class="layout-wrapper" [ngClass]="containerClass">
|
||||
<app-topbar></app-topbar>
|
||||
<div class="layout-sidebar">
|
||||
<app-sidebar></app-sidebar>
|
||||
</div>
|
||||
<div class="layout-main-container">
|
||||
<div class="layout-main">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<app-footer></app-footer>
|
||||
</div>
|
||||
|
||||
<div class="layout-mask"></div>
|
||||
</div>
|
121
main/http_server/axe-os/src/app/layout/app.layout.component.ts
Normal file
121
main/http_server/axe-os/src/app/layout/app.layout.component.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { Component, OnDestroy, Renderer2, ViewChild } from '@angular/core';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { filter, Subscription } from 'rxjs';
|
||||
import { LayoutService } from "./service/app.layout.service";
|
||||
import { AppSidebarComponent } from "./app.sidebar.component";
|
||||
import { AppTopBarComponent } from './app.topbar.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout',
|
||||
templateUrl: './app.layout.component.html'
|
||||
})
|
||||
export class AppLayoutComponent implements OnDestroy {
|
||||
|
||||
overlayMenuOpenSubscription: Subscription;
|
||||
|
||||
menuOutsideClickListener: any;
|
||||
|
||||
profileMenuOutsideClickListener: any;
|
||||
|
||||
@ViewChild(AppSidebarComponent) appSidebar!: AppSidebarComponent;
|
||||
|
||||
@ViewChild(AppTopBarComponent) appTopbar!: AppTopBarComponent;
|
||||
|
||||
constructor(public layoutService: LayoutService, public renderer: Renderer2, public router: Router) {
|
||||
this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => {
|
||||
if (!this.menuOutsideClickListener) {
|
||||
this.menuOutsideClickListener = this.renderer.listen('document', 'click', event => {
|
||||
const isOutsideClicked = !(this.appSidebar.el.nativeElement.isSameNode(event.target) || this.appSidebar.el.nativeElement.contains(event.target)
|
||||
|| this.appTopbar.menuButton.nativeElement.isSameNode(event.target) || this.appTopbar.menuButton.nativeElement.contains(event.target));
|
||||
|
||||
if (isOutsideClicked) {
|
||||
this.hideMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.profileMenuOutsideClickListener) {
|
||||
this.profileMenuOutsideClickListener = this.renderer.listen('document', 'click', event => {
|
||||
const isOutsideClicked = !(this.appTopbar.menu.nativeElement.isSameNode(event.target) || this.appTopbar.menu.nativeElement.contains(event.target)
|
||||
|| this.appTopbar.topbarMenuButton.nativeElement.isSameNode(event.target) || this.appTopbar.topbarMenuButton.nativeElement.contains(event.target));
|
||||
|
||||
if (isOutsideClicked) {
|
||||
this.hideProfileMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.layoutService.state.staticMenuMobileActive) {
|
||||
this.blockBodyScroll();
|
||||
}
|
||||
});
|
||||
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(() => {
|
||||
this.hideMenu();
|
||||
this.hideProfileMenu();
|
||||
});
|
||||
}
|
||||
|
||||
hideMenu() {
|
||||
this.layoutService.state.overlayMenuActive = false;
|
||||
this.layoutService.state.staticMenuMobileActive = false;
|
||||
this.layoutService.state.menuHoverActive = false;
|
||||
if (this.menuOutsideClickListener) {
|
||||
this.menuOutsideClickListener();
|
||||
this.menuOutsideClickListener = null;
|
||||
}
|
||||
this.unblockBodyScroll();
|
||||
}
|
||||
|
||||
hideProfileMenu() {
|
||||
this.layoutService.state.profileSidebarVisible = false;
|
||||
if (this.profileMenuOutsideClickListener) {
|
||||
this.profileMenuOutsideClickListener();
|
||||
this.profileMenuOutsideClickListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
blockBodyScroll(): void {
|
||||
if (document.body.classList) {
|
||||
document.body.classList.add('blocked-scroll');
|
||||
}
|
||||
else {
|
||||
document.body.className += ' blocked-scroll';
|
||||
}
|
||||
}
|
||||
|
||||
unblockBodyScroll(): void {
|
||||
if (document.body.classList) {
|
||||
document.body.classList.remove('blocked-scroll');
|
||||
}
|
||||
else {
|
||||
document.body.className = document.body.className.replace(new RegExp('(^|\\b)' +
|
||||
'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
|
||||
}
|
||||
}
|
||||
|
||||
get containerClass() {
|
||||
return {
|
||||
'layout-theme-light': this.layoutService.config().colorScheme === 'light',
|
||||
'layout-theme-dark': this.layoutService.config().colorScheme === 'dark',
|
||||
'layout-overlay': this.layoutService.config().menuMode === 'overlay',
|
||||
'layout-static': this.layoutService.config().menuMode === 'static',
|
||||
'layout-static-inactive': this.layoutService.state.staticMenuDesktopInactive && this.layoutService.config().menuMode === 'static',
|
||||
'layout-overlay-active': this.layoutService.state.overlayMenuActive,
|
||||
'layout-mobile-active': this.layoutService.state.staticMenuMobileActive,
|
||||
'p-input-filled': this.layoutService.config().inputStyle === 'filled',
|
||||
'p-ripple-disabled': !this.layoutService.config().ripple
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.overlayMenuOpenSubscription) {
|
||||
this.overlayMenuOpenSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.menuOutsideClickListener) {
|
||||
this.menuOutsideClickListener();
|
||||
}
|
||||
}
|
||||
}
|
47
main/http_server/axe-os/src/app/layout/app.layout.module.ts
Normal file
47
main/http_server/axe-os/src/app/layout/app.layout.module.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { BadgeModule } from 'primeng/badge';
|
||||
import { InputSwitchModule } from 'primeng/inputswitch';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { RadioButtonModule } from 'primeng/radiobutton';
|
||||
import { RippleModule } from 'primeng/ripple';
|
||||
import { SidebarModule } from 'primeng/sidebar';
|
||||
|
||||
import { PrimeNGModule } from '../prime-ng.module';
|
||||
import { AppFooterComponent } from './app.footer.component';
|
||||
import { AppLayoutComponent } from './app.layout.component';
|
||||
import { AppMenuComponent } from './app.menu.component';
|
||||
import { AppMenuitemComponent } from './app.menuitem.component';
|
||||
import { AppSidebarComponent } from './app.sidebar.component';
|
||||
import { AppTopBarComponent } from './app.topbar.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppMenuitemComponent,
|
||||
AppTopBarComponent,
|
||||
AppFooterComponent,
|
||||
AppMenuComponent,
|
||||
AppSidebarComponent,
|
||||
AppLayoutComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
InputTextModule,
|
||||
SidebarModule,
|
||||
BadgeModule,
|
||||
RadioButtonModule,
|
||||
InputSwitchModule,
|
||||
RippleModule,
|
||||
RouterModule,
|
||||
PrimeNGModule
|
||||
],
|
||||
exports: [AppLayoutComponent]
|
||||
})
|
||||
export class AppLayoutModule { }
|
@ -0,0 +1,7 @@
|
||||
<ul class="layout-menu">
|
||||
<ng-container *ngFor="let item of model; let i = index;">
|
||||
<li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
|
||||
<li *ngIf="item.separator" class="menu-separator"></li>
|
||||
</ng-container>
|
||||
|
||||
</ul>
|
30
main/http_server/axe-os/src/app/layout/app.menu.component.ts
Normal file
30
main/http_server/axe-os/src/app/layout/app.menu.component.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { LayoutService } from './service/app.layout.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-menu',
|
||||
templateUrl: './app.menu.component.html'
|
||||
})
|
||||
export class AppMenuComponent implements OnInit {
|
||||
|
||||
model: any[] = [];
|
||||
|
||||
constructor(public layoutService: LayoutService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.model = [
|
||||
{
|
||||
label: 'Menu',
|
||||
items: [
|
||||
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: ['/'] },
|
||||
{ label: 'Swarm', icon: 'pi pi-fw pi-share-alt', routerLink: ['swarm'] },
|
||||
{ label: 'Settings', icon: 'pi pi-fw pi-cog', routerLink: ['settings'] },
|
||||
{ label: 'Logs', icon: 'pi pi-fw pi-list', routerLink: ['logs'] },
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
];
|
||||
}
|
||||
}
|
23
main/http_server/axe-os/src/app/layout/app.menu.service.ts
Normal file
23
main/http_server/axe-os/src/app/layout/app.menu.service.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { MenuChangeEvent } from './api/menuchangeevent';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MenuService {
|
||||
|
||||
private menuSource = new Subject<MenuChangeEvent>();
|
||||
private resetSource = new Subject();
|
||||
|
||||
menuSource$ = this.menuSource.asObservable();
|
||||
resetSource$ = this.resetSource.asObservable();
|
||||
|
||||
onMenuStateChange(event: MenuChangeEvent) {
|
||||
this.menuSource.next(event);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.resetSource.next(true);
|
||||
}
|
||||
}
|
148
main/http_server/axe-os/src/app/layout/app.menuitem.component.ts
Normal file
148
main/http_server/axe-os/src/app/layout/app.menuitem.component.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import { ChangeDetectorRef, Component, Host, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { MenuService } from './app.menu.service';
|
||||
import { LayoutService } from './service/app.layout.service';
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: '[app-menuitem]',
|
||||
template: `
|
||||
<ng-container>
|
||||
<div *ngIf="root && item.visible !== false" class="layout-menuitem-root-text">{{item.label}}</div>
|
||||
<a *ngIf="(!item.routerLink || item.items) && item.visible !== false" [attr.href]="item.url" (click)="itemClick($event)"
|
||||
[ngClass]="item.class" [attr.target]="item.target" tabindex="0" pRipple>
|
||||
<i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
|
||||
<span class="layout-menuitem-text">{{item.label}}</span>
|
||||
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
|
||||
</a>
|
||||
<a *ngIf="(item.routerLink && !item.items) && item.visible !== false" (click)="itemClick($event)" [ngClass]="item.class"
|
||||
[routerLink]="item.routerLink" routerLinkActive="active-route" [routerLinkActiveOptions]="item.routerLinkActiveOptions||{ paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }"
|
||||
[fragment]="item.fragment" [queryParamsHandling]="item.queryParamsHandling" [preserveFragment]="item.preserveFragment"
|
||||
[skipLocationChange]="item.skipLocationChange" [replaceUrl]="item.replaceUrl" [state]="item.state" [queryParams]="item.queryParams"
|
||||
[attr.target]="item.target" tabindex="0" pRipple>
|
||||
<i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
|
||||
<span class="layout-menuitem-text">{{item.label}}</span>
|
||||
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
|
||||
</a>
|
||||
|
||||
<ul *ngIf="item.items && item.visible !== false" [@children]="submenuAnimation">
|
||||
<ng-template ngFor let-child let-i="index" [ngForOf]="item.items">
|
||||
<li app-menuitem [item]="child" [index]="i" [parentKey]="key" [class]="child.badgeClass"></li>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</ng-container>
|
||||
`,
|
||||
animations: [
|
||||
trigger('children', [
|
||||
state('collapsed', style({
|
||||
height: '0'
|
||||
})),
|
||||
state('expanded', style({
|
||||
height: '*'
|
||||
})),
|
||||
transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
|
||||
])
|
||||
]
|
||||
})
|
||||
export class AppMenuitemComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() item: any;
|
||||
|
||||
@Input() index!: number;
|
||||
|
||||
@Input() @HostBinding('class.layout-root-menuitem') root!: boolean;
|
||||
|
||||
@Input() parentKey!: string;
|
||||
|
||||
active = false;
|
||||
|
||||
menuSourceSubscription: Subscription;
|
||||
|
||||
menuResetSubscription: Subscription;
|
||||
|
||||
key: string = "";
|
||||
|
||||
constructor(public layoutService: LayoutService, private cd: ChangeDetectorRef, public router: Router, private menuService: MenuService) {
|
||||
this.menuSourceSubscription = this.menuService.menuSource$.subscribe(value => {
|
||||
Promise.resolve(null).then(() => {
|
||||
if (value.routeEvent) {
|
||||
this.active = (value.key === this.key || value.key.startsWith(this.key + '-')) ? true : false;
|
||||
}
|
||||
else {
|
||||
if (value.key !== this.key && !value.key.startsWith(this.key + '-')) {
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.menuResetSubscription = this.menuService.resetSource$.subscribe(() => {
|
||||
this.active = false;
|
||||
});
|
||||
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(params => {
|
||||
if (this.item.routerLink) {
|
||||
this.updateActiveStateFromRoute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index);
|
||||
|
||||
if (this.item.routerLink) {
|
||||
this.updateActiveStateFromRoute();
|
||||
}
|
||||
}
|
||||
|
||||
updateActiveStateFromRoute() {
|
||||
let activeRoute = this.router.isActive(this.item.routerLink[0], { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' });
|
||||
|
||||
if (activeRoute) {
|
||||
this.menuService.onMenuStateChange({ key: this.key, routeEvent: true });
|
||||
}
|
||||
}
|
||||
|
||||
itemClick(event: Event) {
|
||||
// avoid processing disabled items
|
||||
if (this.item.disabled) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// execute command
|
||||
if (this.item.command) {
|
||||
this.item.command({ originalEvent: event, item: this.item });
|
||||
}
|
||||
|
||||
// toggle active state
|
||||
if (this.item.items) {
|
||||
this.active = !this.active;
|
||||
}
|
||||
|
||||
this.menuService.onMenuStateChange({ key: this.key });
|
||||
}
|
||||
|
||||
get submenuAnimation() {
|
||||
return this.root ? 'expanded' : (this.active ? 'expanded' : 'collapsed');
|
||||
}
|
||||
|
||||
@HostBinding('class.active-menuitem')
|
||||
get activeClass() {
|
||||
return this.active && !this.root;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.menuSourceSubscription) {
|
||||
this.menuSourceSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.menuResetSubscription) {
|
||||
this.menuResetSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<app-menu></app-menu>
|
@ -0,0 +1,11 @@
|
||||
import { Component, ElementRef } from '@angular/core';
|
||||
import { LayoutService } from "./service/app.layout.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar',
|
||||
templateUrl: './app.sidebar.component.html'
|
||||
})
|
||||
export class AppSidebarComponent {
|
||||
constructor(public layoutService: LayoutService, public el: ElementRef) { }
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
<div class="layout-topbar">
|
||||
<a class="layout-topbar-logo" routerLink="">
|
||||
<div [routerLink]="['/']" class="header-text">Axe<span class="os">OS</span></div>
|
||||
</a>
|
||||
|
||||
<button #menubutton class="p-link layout-menu-button layout-topbar-button" (click)="layoutService.onMenuToggle()">
|
||||
<i class="pi pi-bars"></i>
|
||||
</button>
|
||||
|
||||
|
||||
<div class="layout-topbar-menu">
|
||||
<p-button (click)="restart()" label="Restart" severity="danger"></p-button>
|
||||
</div>
|
||||
|
||||
</div>
|
@ -0,0 +1,36 @@
|
||||
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { MenuItem } from 'primeng/api';
|
||||
|
||||
import { SystemService } from '../services/system.service';
|
||||
import { LayoutService } from './service/app.layout.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-topbar',
|
||||
templateUrl: './app.topbar.component.html'
|
||||
})
|
||||
export class AppTopBarComponent {
|
||||
|
||||
items!: MenuItem[];
|
||||
|
||||
@ViewChild('menubutton') menuButton!: ElementRef;
|
||||
|
||||
@ViewChild('topbarmenubutton') topbarMenuButton!: ElementRef;
|
||||
|
||||
@ViewChild('topbarmenu') menu!: ElementRef;
|
||||
|
||||
constructor(public layoutService: LayoutService,
|
||||
private systemService: SystemService,
|
||||
private toastr: ToastrService
|
||||
) { }
|
||||
|
||||
|
||||
|
||||
public restart() {
|
||||
this.systemService.restart().subscribe(res => {
|
||||
|
||||
});
|
||||
this.toastr.success('Success!', 'Bitaxe restarted');
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
import { Injectable, effect, signal } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export interface AppConfig {
|
||||
inputStyle: string;
|
||||
colorScheme: string;
|
||||
theme: string;
|
||||
ripple: boolean;
|
||||
menuMode: string;
|
||||
scale: number;
|
||||
}
|
||||
|
||||
interface LayoutState {
|
||||
staticMenuDesktopInactive: boolean;
|
||||
overlayMenuActive: boolean;
|
||||
profileSidebarVisible: boolean;
|
||||
configSidebarVisible: boolean;
|
||||
staticMenuMobileActive: boolean;
|
||||
menuHoverActive: boolean;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LayoutService {
|
||||
_config: AppConfig = {
|
||||
ripple: false,
|
||||
inputStyle: 'outlined',
|
||||
menuMode: 'static',
|
||||
colorScheme: 'light',
|
||||
theme: 'lara-light-indigo',
|
||||
scale: 14,
|
||||
};
|
||||
|
||||
config = signal<AppConfig>(this._config);
|
||||
|
||||
state: LayoutState = {
|
||||
staticMenuDesktopInactive: false,
|
||||
overlayMenuActive: false,
|
||||
profileSidebarVisible: false,
|
||||
configSidebarVisible: false,
|
||||
staticMenuMobileActive: false,
|
||||
menuHoverActive: false,
|
||||
};
|
||||
|
||||
private configUpdate = new Subject<AppConfig>();
|
||||
|
||||
private overlayOpen = new Subject<any>();
|
||||
|
||||
configUpdate$ = this.configUpdate.asObservable();
|
||||
|
||||
overlayOpen$ = this.overlayOpen.asObservable();
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const config = this.config();
|
||||
if (this.updateStyle(config)) {
|
||||
this.changeTheme();
|
||||
}
|
||||
this.changeScale(config.scale);
|
||||
this.onConfigUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
updateStyle(config: AppConfig) {
|
||||
return (
|
||||
config.theme !== this._config.theme ||
|
||||
config.colorScheme !== this._config.colorScheme
|
||||
);
|
||||
}
|
||||
|
||||
onMenuToggle() {
|
||||
if (this.isOverlay()) {
|
||||
this.state.overlayMenuActive = !this.state.overlayMenuActive;
|
||||
if (this.state.overlayMenuActive) {
|
||||
this.overlayOpen.next(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isDesktop()) {
|
||||
this.state.staticMenuDesktopInactive =
|
||||
!this.state.staticMenuDesktopInactive;
|
||||
} else {
|
||||
this.state.staticMenuMobileActive =
|
||||
!this.state.staticMenuMobileActive;
|
||||
|
||||
if (this.state.staticMenuMobileActive) {
|
||||
this.overlayOpen.next(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showProfileSidebar() {
|
||||
this.state.profileSidebarVisible = !this.state.profileSidebarVisible;
|
||||
if (this.state.profileSidebarVisible) {
|
||||
this.overlayOpen.next(null);
|
||||
}
|
||||
}
|
||||
|
||||
showConfigSidebar() {
|
||||
this.state.configSidebarVisible = true;
|
||||
}
|
||||
|
||||
isOverlay() {
|
||||
return this.config().menuMode === 'overlay';
|
||||
}
|
||||
|
||||
isDesktop() {
|
||||
return window.innerWidth > 991;
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return !this.isDesktop();
|
||||
}
|
||||
|
||||
onConfigUpdate() {
|
||||
this._config = { ...this.config() };
|
||||
this.configUpdate.next(this.config());
|
||||
}
|
||||
|
||||
changeTheme() {
|
||||
const config = this.config();
|
||||
const themeLink = <HTMLLinkElement>document.getElementById('theme-css');
|
||||
const themeLinkHref = themeLink.getAttribute('href')!;
|
||||
const newHref = themeLinkHref
|
||||
.split('/')
|
||||
.map((el) =>
|
||||
el == this._config.theme
|
||||
? (el = config.theme)
|
||||
: el == `theme-${this._config.colorScheme}`
|
||||
? (el = `theme-${config.colorScheme}`)
|
||||
: el
|
||||
)
|
||||
.join('/');
|
||||
|
||||
this.replaceThemeLink(newHref);
|
||||
}
|
||||
replaceThemeLink(href: string) {
|
||||
const id = 'theme-css';
|
||||
let themeLink = <HTMLLinkElement>document.getElementById(id);
|
||||
const cloneLinkElement = <HTMLLinkElement>themeLink.cloneNode(true);
|
||||
|
||||
cloneLinkElement.setAttribute('href', href);
|
||||
cloneLinkElement.setAttribute('id', id + '-clone');
|
||||
|
||||
themeLink.parentNode!.insertBefore(
|
||||
cloneLinkElement,
|
||||
themeLink.nextSibling
|
||||
);
|
||||
cloneLinkElement.addEventListener('load', () => {
|
||||
themeLink.remove();
|
||||
cloneLinkElement.setAttribute('id', id);
|
||||
});
|
||||
}
|
||||
|
||||
changeScale(value: number) {
|
||||
document.documentElement.style.fontSize = `${value}px`;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
.layout-config-button {
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
background: var(--primary-color);
|
||||
color: var(--primary-color-text);
|
||||
text-align: center;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
margin-top: -1.5rem;
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
transition: background-color var(--transition-duration);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
box-shadow: -.25rem 0 1rem rgba(0,0,0,.15);
|
||||
|
||||
i {
|
||||
font-size: 2rem;
|
||||
line-height: inherit;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 1s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-400);
|
||||
}
|
||||
}
|
||||
|
||||
.layout-config-sidebar {
|
||||
&.p-sidebar {
|
||||
.p-sidebar-content {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
.layout-main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
justify-content: space-between;
|
||||
padding: 7rem 2rem 2rem 4rem;
|
||||
transition: margin-left $transitionDuration;
|
||||
}
|
||||
|
||||
.layout-main {
|
||||
flex: 1 1 auto;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
.layout-footer {
|
||||
transition: margin-left $transitionDuration;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--surface-border);
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
font-size: $scale;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
color: var(--text-color);
|
||||
background-color: var(--surface-ground);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.layout-wrapper {
|
||||
min-height: 100vh;
|
||||
}
|
135
main/http_server/axe-os/src/app/layout/styles/layout/_menu.scss
Normal file
135
main/http_server/axe-os/src/app/layout/styles/layout/_menu.scss
Normal file
@ -0,0 +1,135 @@
|
||||
.layout-sidebar {
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
height: calc(100vh - 9rem);
|
||||
z-index: 999;
|
||||
overflow-y: auto;
|
||||
user-select: none;
|
||||
top: 7rem;
|
||||
left: 2rem;
|
||||
transition: transform $transitionDuration, left $transitionDuration;
|
||||
background-color: var(--surface-overlay);
|
||||
border-radius: $borderRadius;
|
||||
padding: 0.5rem 1.5rem;
|
||||
box-shadow: 0px 3px 5px rgba(0, 0, 0, .02), 0px 0px 2px rgba(0, 0, 0, .05), 0px 1px 4px rgba(0, 0, 0, .08);
|
||||
}
|
||||
|
||||
.layout-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
|
||||
.layout-root-menuitem {
|
||||
>.layout-menuitem-root-text {
|
||||
font-size: .857rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
color: var(--surface-900);
|
||||
margin: .75rem 0;
|
||||
}
|
||||
|
||||
>a {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
user-select: none;
|
||||
|
||||
&.active-menuitem {
|
||||
>.layout-submenu-toggler {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li.active-menuitem {
|
||||
>a {
|
||||
.layout-submenu-toggler {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
outline: 0 none;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
padding: .75rem 1rem;
|
||||
border-radius: $borderRadius;
|
||||
transition: background-color $transitionDuration, box-shadow $transitionDuration;
|
||||
|
||||
.layout-menuitem-icon {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.layout-submenu-toggler {
|
||||
font-size: 75%;
|
||||
margin-left: auto;
|
||||
transition: transform $transitionDuration;
|
||||
}
|
||||
|
||||
&.active-route {
|
||||
font-weight: 700;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--surface-hover);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include focused-inset();
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
overflow: hidden;
|
||||
border-radius: $borderRadius;
|
||||
|
||||
li {
|
||||
a {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
margin-left: 2.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
margin-left: 3.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
margin-left: 4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
@mixin focused() {
|
||||
outline: 0 none;
|
||||
outline-offset: 0;
|
||||
transition: box-shadow .2s;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
@mixin focused-inset() {
|
||||
outline: 0 none;
|
||||
outline-offset: 0;
|
||||
transition: box-shadow .2s;
|
||||
box-shadow: inset var(--focus-ring);
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
.preloader {
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
background: #edf1f5;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.preloader-content {
|
||||
border: 0 solid transparent;
|
||||
border-radius: 50%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
position: absolute;
|
||||
top: calc(50vh - 75px);
|
||||
left: calc(50vw - 75px);
|
||||
}
|
||||
|
||||
.preloader-content:before,
|
||||
.preloader-content:after {
|
||||
content: '';
|
||||
border: 1em solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
animation: loader 2s linear infinite;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.preloader-content:before {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
@media screen and (min-width: 1960px) {
|
||||
.layout-main, .landing-wrapper {
|
||||
width: 1504px;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.layout-wrapper {
|
||||
&.layout-overlay {
|
||||
.layout-main-container {
|
||||
margin-left: 0;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.layout-sidebar {
|
||||
transform: translateX(-100%);
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
&.layout-overlay-active {
|
||||
.layout-sidebar {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.layout-static {
|
||||
.layout-main-container {
|
||||
margin-left: 300px;
|
||||
}
|
||||
|
||||
&.layout-static-inactive {
|
||||
.layout-sidebar {
|
||||
transform: translateX(-100%);
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.layout-main-container {
|
||||
margin-left: 0;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-mask {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.blocked-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.layout-wrapper {
|
||||
.layout-main-container {
|
||||
margin-left: 0;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.layout-sidebar {
|
||||
transform: translateX(-100%);
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.layout-mask {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 998;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--maskbg);
|
||||
}
|
||||
|
||||
&.layout-mobile-active {
|
||||
.layout-sidebar {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.layout-mask {
|
||||
display: block;
|
||||
animation: fadein $transitionDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
.layout-topbar {
|
||||
position: fixed;
|
||||
height: 5rem;
|
||||
z-index: 997;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: 0 2rem;
|
||||
background-color: var(--surface-card);
|
||||
transition: left $transitionDuration;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0px 3px 5px rgba(0,0,0,.02), 0px 0px 2px rgba(0,0,0,.05), 0px 1px 4px rgba(0,0,0,.08);
|
||||
|
||||
.layout-topbar-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--surface-900);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
width: 300px;
|
||||
border-radius: 12px;
|
||||
|
||||
img {
|
||||
height: 2.5rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include focused();
|
||||
}
|
||||
}
|
||||
|
||||
.layout-topbar-button {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
color: var(--text-color-secondary);
|
||||
border-radius: 50%;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
cursor: pointer;
|
||||
transition: background-color $transitionDuration;
|
||||
|
||||
&:hover {
|
||||
color: var(--text-color);
|
||||
background-color: var(--surface-hover);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include focused();
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 1rem;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-menu-button {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.layout-topbar-menu-button {
|
||||
display: none;
|
||||
|
||||
i {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-topbar-menu {
|
||||
margin: 0 0 0 auto;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
|
||||
.layout-topbar-button {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.layout-topbar {
|
||||
justify-content: space-between;
|
||||
|
||||
.layout-topbar-logo {
|
||||
width: auto;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.layout-menu-button {
|
||||
margin-left: 0;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.layout-topbar-menu-button {
|
||||
display: inline-flex;
|
||||
margin-left: 0;
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.layout-topbar-menu {
|
||||
margin-left: 0;
|
||||
position: absolute;
|
||||
flex-direction: column;
|
||||
background-color: var(--surface-overlay);
|
||||
box-shadow: 0px 3px 5px rgba(0,0,0,.02), 0px 0px 2px rgba(0,0,0,.05), 0px 1px 4px rgba(0,0,0,.08);
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
right: 2rem;
|
||||
top: 5rem;
|
||||
min-width: 15rem;
|
||||
display: none;
|
||||
-webkit-animation: scalein 0.15s linear;
|
||||
animation: scalein 0.15s linear;
|
||||
|
||||
&.layout-topbar-menu-mobile-active {
|
||||
display: block
|
||||
}
|
||||
|
||||
.layout-topbar-button {
|
||||
margin-left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
justify-content: flex-start;
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
|
||||
i {
|
||||
font-size: 1rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: medium;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 1.5rem 0 1rem 0;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--surface-900);
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
mark {
|
||||
background: #FFF8E1;
|
||||
padding: .25rem .4rem;
|
||||
border-radius: $borderRadius;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 1rem 0;
|
||||
padding: 0 2rem;
|
||||
border-left: 4px solid #90A4AE;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-top: solid var(--surface-border);
|
||||
border-width: 1px 0 0 0;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1rem 0;
|
||||
line-height: 1.5;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
.card {
|
||||
background: var(--surface-card);
|
||||
border: 1px solid var(--surface-border);
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: var(--card-shadow);
|
||||
border-radius: $borderRadius;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.p-toast {
|
||||
&.p-toast-top-right,
|
||||
&.p-toast-top-left,
|
||||
&.p-toast-top-center {
|
||||
top: 100px;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/* General */
|
||||
$scale:14px; /* main font size */
|
||||
$borderRadius:12px; /* border radius of layout element e.g. card, sidebar */
|
||||
$transitionDuration:.2s; /* transition duration of layout elements e.g. sidebar, overlay menus */
|
@ -0,0 +1,12 @@
|
||||
@import './_variables';
|
||||
@import "./_mixins";
|
||||
@import "./_preloading";
|
||||
@import "./_main";
|
||||
@import "./_topbar";
|
||||
@import "./_menu";
|
||||
@import "./_config";
|
||||
@import "./_content";
|
||||
@import "./_footer";
|
||||
@import "./_responsive";
|
||||
@import "./_utils";
|
||||
@import "./_typography";
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
import { HashSuffixPipe } from './hash-suffix.pipe';
|
||||
|
||||
describe('HashSuffixPipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new HashSuffixPipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
33
main/http_server/axe-os/src/app/pipes/hash-suffix.pipe.ts
Normal file
33
main/http_server/axe-os/src/app/pipes/hash-suffix.pipe.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'hashSuffix'
|
||||
})
|
||||
export class HashSuffixPipe implements PipeTransform {
|
||||
|
||||
private static _this = new HashSuffixPipe();
|
||||
|
||||
public static transform(value: number): string {
|
||||
return this._this.transform(value);
|
||||
}
|
||||
|
||||
public transform(value: number): string {
|
||||
|
||||
if (value == null || value < 0) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
const suffixes = [' H/s', ' KH/s', ' MH/s', ' GH/s', ' TH/s', ' PH/s', ' EH/s'];
|
||||
|
||||
let power = Math.floor(Math.log10(value) / 3);
|
||||
if (power < 0) {
|
||||
power = 0;
|
||||
}
|
||||
const scaledValue = value / Math.pow(1000, power);
|
||||
const suffix = suffixes[power];
|
||||
|
||||
return scaledValue.toFixed(1) + suffix;
|
||||
}
|
||||
|
||||
|
||||
}
|
36
main/http_server/axe-os/src/app/prime-ng.module.ts
Normal file
36
main/http_server/axe-os/src/app/prime-ng.module.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { ChartModule } from 'primeng/chart';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FileUploadModule } from 'primeng/fileupload';
|
||||
import { InputGroupModule } from 'primeng/inputgroup';
|
||||
import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { KnobModule } from 'primeng/knob';
|
||||
import { SidebarModule } from 'primeng/sidebar';
|
||||
import { SliderModule } from 'primeng/slider';
|
||||
|
||||
const primeNgModules = [
|
||||
SidebarModule,
|
||||
InputTextModule,
|
||||
CheckboxModule,
|
||||
DropdownModule,
|
||||
SliderModule,
|
||||
ButtonModule,
|
||||
FileUploadModule,
|
||||
KnobModule,
|
||||
ChartModule,
|
||||
InputGroupModule,
|
||||
InputGroupAddonModule
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
...primeNgModules
|
||||
],
|
||||
exports: [
|
||||
...primeNgModules
|
||||
],
|
||||
})
|
||||
export class PrimeNGModule { }
|
@ -26,9 +26,7 @@ export class SystemService {
|
||||
current: 2237.5,
|
||||
fanSpeed: 82,
|
||||
temp: 60,
|
||||
boardtemp1: 60,
|
||||
boardtemp2: 60,
|
||||
hashRate: 0,
|
||||
hashRate: 475,
|
||||
bestDiff: "0",
|
||||
freeHeap: 200504,
|
||||
coreVoltage: 1200,
|
||||
@ -50,7 +48,10 @@ export class SystemService {
|
||||
invertscreen: 0,
|
||||
invertfanpolarity: 1,
|
||||
autofanspeed: 1,
|
||||
fanspeed: 100
|
||||
fanspeed: 100,
|
||||
|
||||
boardtemp1: 30,
|
||||
boardtemp2: 40
|
||||
}
|
||||
).pipe(delay(1000));
|
||||
}
|
||||
|
@ -30,5 +30,5 @@ export interface ISystemInfo {
|
||||
invertfanpolarity: number,
|
||||
autofanspeed: number,
|
||||
fanspeed: number,
|
||||
coreVoltageActual: number
|
||||
coreVoltageActual: number,
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum eASICModel {
|
||||
BM1366 = 'BM1366',
|
||||
BM1368 = 'BM1368',
|
||||
BM1397 = 'BM1397'
|
||||
}
|
||||
}
|
||||
|
@ -1,176 +1,20 @@
|
||||
@import "app/layout/styles/layout/layout.scss";
|
||||
@import "app/layout/styles/theme/vela-blue/theme.css";
|
||||
|
||||
@import "../node_modules/primeicons/primeicons.css";
|
||||
@import "../node_modules/primeng/resources/primeng.min.css";
|
||||
|
||||
@import "../node_modules/primeflex/primeflex.min.css";
|
||||
|
||||
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
body {
|
||||
background-color: #17212f;
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
|
||||
p-chart>div {
|
||||
position: relative;
|
||||
min-height: 40vh;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1f2d40;
|
||||
border: 1px solid #304562;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
&.chart {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
line-break: anywhere;
|
||||
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
border-radius: 3px;
|
||||
|
||||
&.btn-primary {
|
||||
color: #212529;
|
||||
background: #64B5F6;
|
||||
border: 1px solid #64B5F6;
|
||||
|
||||
&:hover {
|
||||
background: #43a5f4;
|
||||
color: #212529;
|
||||
border-color: #43a5f4;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-danger {
|
||||
color: #121212;
|
||||
background: #F48FB1;
|
||||
border: 1px solid #F48FB1;
|
||||
|
||||
&:hover {
|
||||
background: #f16c98;
|
||||
color: #121212;
|
||||
border-color: #f16c98;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-secondary {
|
||||
color: #ffffff;
|
||||
background: #78909C;
|
||||
border: 1px solid #78909C;
|
||||
|
||||
&:hover {
|
||||
background: #69838f;
|
||||
color: #ffffff;
|
||||
border-color: #69838f;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-warning {
|
||||
color: #121212;
|
||||
background: #FFE082;
|
||||
border: 1px solid #FFE082;
|
||||
|
||||
&:hover {
|
||||
background: #ffd65c;
|
||||
color: #121212;
|
||||
border-color: #ffd65c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
button[disabled] {
|
||||
background: #45657f;
|
||||
border: 1px solid #1c4567;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: #45657f;
|
||||
border: 1px solid #1c4567;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
select {
|
||||
|
||||
font-size: 1rem;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background: #17212f;
|
||||
padding: 0.5rem 0.5rem;
|
||||
border: 1px solid #304562;
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
appearance: none;
|
||||
border-radius: 3px;
|
||||
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
label {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
label,
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.p-fileupload-choose {
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
border-radius: 3px;
|
||||
color: #212529;
|
||||
background: #64B5F6;
|
||||
border: 1px solid #64B5F6;
|
||||
}
|
12
main/main.c
12
main/main.c
@ -45,6 +45,18 @@ void app_main(void)
|
||||
.send_work_fn = BM1366_send_work};
|
||||
GLOBAL_STATE.asic_job_frequency_ms = BM1366_FULLSCAN_MS;
|
||||
|
||||
GLOBAL_STATE.ASIC_functions = ASIC_functions;
|
||||
} else if (strcmp(GLOBAL_STATE.asic_model, "BM1368") == 0) {
|
||||
ESP_LOGI(TAG, "ASIC: BM1368");
|
||||
AsicFunctions ASIC_functions = {.init_fn = BM1368_init,
|
||||
.receive_result_fn = BM1368_proccess_work,
|
||||
.set_max_baud_fn = BM1368_set_max_baud,
|
||||
.set_difficulty_mask_fn = BM1368_set_job_difficulty_mask,
|
||||
.send_work_fn = BM1368_send_work};
|
||||
|
||||
uint64_t bm1368_hashrate = GLOBAL_STATE.POWER_MANAGEMENT_MODULE.frequency_value * BM1368_CORE_COUNT * 1000000;
|
||||
GLOBAL_STATE.asic_job_frequency_ms = ((double) NONCE_SPACE / (double) bm1368_hashrate) * 1000;
|
||||
|
||||
GLOBAL_STATE.ASIC_functions = ASIC_functions;
|
||||
} else if (strcmp(GLOBAL_STATE.asic_model, "BM1397") == 0) {
|
||||
ESP_LOGI(TAG, "ASIC: BM1397");
|
||||
|
@ -301,18 +301,16 @@ static double _calculate_network_difficulty(uint32_t nBits)
|
||||
|
||||
static void _check_for_best_diff(SystemModule * module, double diff, uint32_t nbits)
|
||||
{
|
||||
if (diff < module->best_nonce_diff) {
|
||||
if (diff <= module->best_nonce_diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t old = module->best_nonce_diff;
|
||||
module->best_nonce_diff = diff;
|
||||
// only write to flash if new > old
|
||||
if (module->best_nonce_diff > old) {
|
||||
nvs_config_set_u64(NVS_CONFIG_BEST_DIFF, module->best_nonce_diff);
|
||||
}
|
||||
|
||||
nvs_config_set_u64(NVS_CONFIG_BEST_DIFF, module->best_nonce_diff);
|
||||
|
||||
// make the best_nonce_diff into a string
|
||||
_suffix_string((uint64_t) diff, module->best_diff_string, DIFF_STRING_SIZE, 0);
|
||||
|
||||
double network_diff = _calculate_network_difficulty(nbits);
|
||||
if (diff > network_diff) {
|
||||
module->FOUND_BLOCK = true;
|
||||
|
@ -43,13 +43,6 @@ void ASIC_result_task(void *pvParameters)
|
||||
|
||||
if (nonce_diff > GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->pool_diff)
|
||||
{
|
||||
SYSTEM_notify_found_nonce(
|
||||
&GLOBAL_STATE->SYSTEM_MODULE,
|
||||
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->pool_diff,
|
||||
nonce_diff,
|
||||
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->target,
|
||||
GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power
|
||||
);
|
||||
|
||||
STRATUM_V1_submit_share(
|
||||
GLOBAL_STATE->sock,
|
||||
@ -59,6 +52,10 @@ void ASIC_result_task(void *pvParameters)
|
||||
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->ntime,
|
||||
asic_result->nonce,
|
||||
asic_result->rolled_version ^ GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->version);
|
||||
|
||||
SYSTEM_notify_found_nonce(&GLOBAL_STATE->SYSTEM_MODULE, GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->pool_diff,
|
||||
nonce_diff, GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->target,
|
||||
GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power);
|
||||
}
|
||||
}
|
||||
}
|
@ -177,7 +177,7 @@ void POWER_MANAGEMENT_task(void * pvParameters)
|
||||
last_frequency_increase++;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(GLOBAL_STATE->asic_model, "BM1366") == 0) {
|
||||
} else if (strcmp(GLOBAL_STATE->asic_model, "BM1366") == 0 || strcmp(GLOBAL_STATE->asic_model, "BM1368") == 0) {
|
||||
power_management->chip_temp = EMC2101_get_internal_temp() + 5;
|
||||
|
||||
if (power_management->chip_temp > THROTTLE_TEMP &&
|
||||
|
0
merge_bin_with_config.sh
Normal file → Executable file
0
merge_bin_with_config.sh
Normal file → Executable file
14
readme.md
14
readme.md
@ -30,13 +30,25 @@ Starting with v2.0.0, the ESP-Miner firmware requires some basic manufacturing d
|
||||
|
||||
The following are recommendations but it is necessary that you do have all values in your `config.cvs`file to flash properly.
|
||||
|
||||
- recommended values for the Bitaxe 1368 (supra)
|
||||
|
||||
```
|
||||
key,type,encoding,value
|
||||
main,namespace,,
|
||||
asicfrequency,data,u16,490
|
||||
asicvoltage,data,u16,1200
|
||||
asicmodel,data,string,BM1368
|
||||
devicemodel,data,string,supra
|
||||
boardversion,data,string,400
|
||||
```
|
||||
|
||||
- recommended values for the Bitaxe 1366 (ultra)
|
||||
|
||||
```
|
||||
key,type,encoding,value
|
||||
main,namespace,,
|
||||
asicfrequency,data,u16,485
|
||||
asicvoltage,data,u16,1320
|
||||
asicvoltage,data,u16,1200
|
||||
asicmodel,data,string,BM1366
|
||||
devicemodel,data,string,ultra
|
||||
boardversion,data,string,0.11
|
||||
|
Loading…
x
Reference in New Issue
Block a user