diff --git a/static/js/workers.js b/static/js/workers.js
index d193184..89b2ec7 100644
--- a/static/js/workers.js
+++ b/static/js/workers.js
@@ -3,9 +3,9 @@
// Global variables for workers dashboard
let workerData = null;
let refreshTimer;
-let pageLoadTime = Date.now();
+const pageLoadTime = Date.now();
let lastManualRefreshTime = 0;
-let filterState = {
+const filterState = {
currentFilter: 'all',
searchTerm: ''
};
@@ -21,70 +21,54 @@ const MIN_REFRESH_INTERVAL = 10000; // Minimum 10 seconds between refreshes
// Hashrate Normalization Utilities
// Helper function to normalize hashrate to TH/s for consistent graphing
-function normalizeHashrate(value, unit) {
+function normalizeHashrate(value, unit = 'th/s') {
if (!value || isNaN(value)) return 0;
- unit = (unit || 'th/s').toLowerCase();
- if (unit.includes('ph/s')) {
- return value * 1000; // Convert PH/s to TH/s
- } else if (unit.includes('eh/s')) {
- return value * 1000000; // Convert EH/s to TH/s
- } else if (unit.includes('gh/s')) {
- return value / 1000; // Convert GH/s to TH/s
- } else if (unit.includes('mh/s')) {
- return value / 1000000; // Convert MH/s to TH/s
- } else if (unit.includes('kh/s')) {
- return value / 1000000000; // Convert KH/s to TH/s
- } else if (unit.includes('h/s') && !unit.includes('th/s') && !unit.includes('ph/s') &&
- !unit.includes('eh/s') && !unit.includes('gh/s') && !unit.includes('mh/s') &&
- !unit.includes('kh/s')) {
- return value / 1000000000000; // Convert H/s to TH/s
- } else {
- // Assume TH/s if unit is not recognized
- return value;
- }
+ unit = unit.toLowerCase();
+ const unitConversion = {
+ 'ph/s': 1000,
+ 'eh/s': 1000000,
+ 'gh/s': 1 / 1000,
+ 'mh/s': 1 / 1000000,
+ 'kh/s': 1 / 1000000000,
+ 'h/s': 1 / 1000000000000
+ };
+
+ return unitConversion[unit] !== undefined ? value * unitConversion[unit] : value;
}
// Helper function to format hashrate values for display
function formatHashrateForDisplay(value, unit) {
if (isNaN(value) || value === null || value === undefined) return "N/A";
- // Always normalize to TH/s first if unit is provided
- let normalizedValue = unit ? normalizeHashrate(value, unit) : value;
+ const normalizedValue = unit ? normalizeHashrate(value, unit) : value;
+ const unitRanges = [
+ { threshold: 1000000, unit: 'EH/s', divisor: 1000000 },
+ { threshold: 1000, unit: 'PH/s', divisor: 1000 },
+ { threshold: 1, unit: 'TH/s', divisor: 1 },
+ { threshold: 0.001, unit: 'GH/s', divisor: 1 / 1000 },
+ { threshold: 0, unit: 'MH/s', divisor: 1 / 1000000 }
+ ];
- // Select appropriate unit based on magnitude
- if (normalizedValue >= 1000000) { // EH/s range
- return (normalizedValue / 1000000).toFixed(2) + ' EH/s';
- } else if (normalizedValue >= 1000) { // PH/s range
- return (normalizedValue / 1000).toFixed(2) + ' PH/s';
- } else if (normalizedValue >= 1) { // TH/s range
- return normalizedValue.toFixed(2) + ' TH/s';
- } else if (normalizedValue >= 0.001) { // GH/s range
- return (normalizedValue * 1000).toFixed(2) + ' GH/s';
- } else { // MH/s range or smaller
- return (normalizedValue * 1000000).toFixed(2) + ' MH/s';
+ for (const range of unitRanges) {
+ if (normalizedValue >= range.threshold) {
+ return (normalizedValue / range.divisor).toFixed(2) + ' ' + range.unit;
+ }
}
+ return (normalizedValue * 1000000).toFixed(2) + ' MH/s';
}
// Initialize the page
$(document).ready(function () {
console.log("Worker page initializing...");
- // Initialize notification badge
initNotificationBadge();
-
- // Set up initial UI
initializePage();
-
- // Get server time for uptime calculation
updateServerTime();
- // Define global refresh function for BitcoinMinuteRefresh
window.manualRefresh = fetchWorkerData;
- // Wait before initializing BitcoinMinuteRefresh to ensure DOM is ready
- setTimeout(function () {
- // Initialize BitcoinMinuteRefresh with our refresh function
+ setTimeout(() => {
if (typeof BitcoinMinuteRefresh !== 'undefined' && BitcoinMinuteRefresh.initialize) {
BitcoinMinuteRefresh.initialize(window.manualRefresh);
console.log("BitcoinMinuteRefresh initialized with refresh function");
@@ -93,10 +77,8 @@ $(document).ready(function () {
}
}, 500);
- // Fetch worker data immediately on page load
fetchWorkerData();
- // Set up filter button click handlers
$('.filter-button').click(function () {
$('.filter-button').removeClass('active');
$(this).addClass('active');
@@ -104,7 +86,6 @@ $(document).ready(function () {
filterWorkers();
});
- // Set up search input handler
$('#worker-search').on('input', function () {
filterState.searchTerm = $(this).val().toLowerCase();
filterWorkers();
@@ -115,15 +96,12 @@ $(document).ready(function () {
function initializePage() {
console.log("Initializing page elements...");
- // Initialize mini chart for total hashrate if the element exists
if (document.getElementById('total-hashrate-chart')) {
initializeMiniChart();
}
- // Show loading state
$('#worker-grid').html('
Loading worker data...
');
- // Add retry button (hidden by default)
if (!$('#retry-button').length) {
$('body').append('');
@@ -157,10 +135,7 @@ function updateNotificationBadge() {
// Initialize notification badge checking
function initNotificationBadge() {
- // Update immediately
updateNotificationBadge();
-
- // Update every 60 seconds
setInterval(updateNotificationBadge, 60000);
}
@@ -168,7 +143,6 @@ function initNotificationBadge() {
function updateServerTime() {
console.log("Updating server time...");
- // First try to get stored values
try {
const storedOffset = localStorage.getItem('serverTimeOffset');
const storedStartTime = localStorage.getItem('serverStartTime');
@@ -178,31 +152,26 @@ function updateServerTime() {
serverStartTime = parseFloat(storedStartTime);
console.log("Using stored server time offset:", serverTimeOffset, "ms");
- // Only update BitcoinMinuteRefresh if it's initialized
if (typeof BitcoinMinuteRefresh !== 'undefined' && BitcoinMinuteRefresh.updateServerTime) {
BitcoinMinuteRefresh.updateServerTime(serverTimeOffset, serverStartTime);
}
- return; // Don't fetch if we have valid values
+ return;
}
} catch (e) {
console.error("Error reading stored server time:", e);
}
- // Fetch from API if needed
$.ajax({
url: "/api/time",
method: "GET",
timeout: 5000,
success: function (data) {
- // Calculate the offset between server time and local time
serverTimeOffset = new Date(data.server_timestamp).getTime() - Date.now();
serverStartTime = new Date(data.server_start_time).getTime();
- // Store in localStorage for cross-page sharing
localStorage.setItem('serverTimeOffset', serverTimeOffset.toString());
localStorage.setItem('serverStartTime', serverStartTime.toString());
- // Only update BitcoinMinuteRefresh if it's initialized
if (typeof BitcoinMinuteRefresh !== 'undefined' && BitcoinMinuteRefresh.updateServerTime) {
BitcoinMinuteRefresh.updateServerTime(serverTimeOffset, serverStartTime);
}
@@ -219,19 +188,16 @@ function updateServerTime() {
function fetchWorkerData(forceRefresh = false) {
console.log("Fetching worker data...");
- // Track this as a manual refresh for throttling purposes
lastManualRefreshTime = Date.now();
-
$('#worker-grid').addClass('loading-fade');
- // Choose API URL based on whether we're forcing a refresh
const apiUrl = `/api/workers${forceRefresh ? '?force=true' : ''}`;
$.ajax({
url: apiUrl,
method: 'GET',
dataType: 'json',
- timeout: 15000, // 15 second timeout
+ timeout: 15000,
success: function (data) {
if (!data || !data.workers || data.workers.length === 0) {
console.warn("No workers found in data response");
@@ -245,21 +211,16 @@ function fetchWorkerData(forceRefresh = false) {
workerData = data;
- // Notify BitcoinMinuteRefresh that we've refreshed the data
if (typeof BitcoinMinuteRefresh !== 'undefined' && BitcoinMinuteRefresh.notifyRefresh) {
BitcoinMinuteRefresh.notifyRefresh();
}
- // Update UI with new data
updateWorkerGrid();
updateSummaryStats();
updateMiniChart();
updateLastUpdated();
- // Hide retry button
$('#retry-button').hide();
-
- // Reset connection retry count
connectionRetryCount = 0;
console.log("Worker data updated successfully");
@@ -267,7 +228,6 @@ function fetchWorkerData(forceRefresh = false) {
error: function (xhr, status, error) {
console.error("Error fetching worker data:", error);
- // Show error in worker grid
$('#worker-grid').html(`
@@ -275,16 +235,14 @@ function fetchWorkerData(forceRefresh = false) {
`);
- // Show retry button
$('#retry-button').show();
- // Implement exponential backoff for automatic retry
connectionRetryCount++;
const delay = Math.min(30000, 1000 * Math.pow(1.5, Math.min(5, connectionRetryCount)));
console.log(`Will retry in ${delay / 1000} seconds (attempt ${connectionRetryCount})`);
setTimeout(() => {
- fetchWorkerData(true); // Force refresh on retry
+ fetchWorkerData(true);
}, delay);
},
complete: function () {
@@ -305,7 +263,6 @@ function updateWorkerGrid() {
const workerGrid = $('#worker-grid');
workerGrid.empty();
- // Apply current filters before rendering
const filteredWorkers = filterWorkersData(workerData.workers);
if (filteredWorkers.length === 0) {
@@ -318,107 +275,75 @@ function updateWorkerGrid() {
return;
}
- // Generate worker cards
filteredWorkers.forEach(worker => {
- // Create worker card
- const card = $('');
-
- // Add class based on status
- if (worker.status === 'online') {
- card.addClass('worker-card-online');
- } else {
- card.addClass('worker-card-offline');
- }
-
- // Add worker type badge
- card.append(`${worker.type}
`);
-
- // Add worker name
- card.append(`${worker.name}
`);
-
- // Add status badge
- if (worker.status === 'online') {
- card.append('ONLINE
');
- } else {
- card.append('OFFLINE
');
- }
-
- // Add hashrate bar with normalized values for consistent display
- const maxHashrate = 200; // TH/s - adjust based on your fleet
- const normalizedHashrate = normalizeHashrate(
- worker.hashrate_3hr,
- worker.hashrate_3hr_unit || 'th/s'
- );
- const hashratePercent = Math.min(100, (normalizedHashrate / maxHashrate) * 100);
-
- // Format hashrate for display with appropriate unit
- const formattedHashrate = formatHashrateForDisplay(
- worker.hashrate_3hr,
- worker.hashrate_3hr_unit || 'th/s'
- );
-
- card.append(`
-
-
Hashrate (3hr):
-
${formattedHashrate}
-
-
- `);
-
- // Add additional stats
- card.append(`
-
-
-
Last Share:
-
${typeof worker.last_share === 'string' ? worker.last_share.split(' ')[1] || worker.last_share : 'N/A'}
-
-
-
Earnings:
-
${worker.earnings.toFixed(8)}
-
-
-
Accept Rate:
-
${worker.acceptance_rate}%
-
-
-
Temp:
-
${worker.temperature > 0 ? worker.temperature + '°C' : 'N/A'}
-
-
- `);
-
- // Add card to grid
+ const card = createWorkerCard(worker);
workerGrid.append(card);
});
}
+// Create worker card element
+function createWorkerCard(worker) {
+ const card = $('');
+
+ card.addClass(worker.status === 'online' ? 'worker-card-online' : 'worker-card-offline');
+ card.append(`${worker.type}
`);
+ card.append(`${worker.name}
`);
+ card.append(`${worker.status.toUpperCase()}
`);
+
+ const maxHashrate = 125; // TH/s - adjust based on your fleet
+ const normalizedHashrate = normalizeHashrate(worker.hashrate_3hr, worker.hashrate_3hr_unit || 'th/s');
+ const hashratePercent = Math.min(100, (normalizedHashrate / maxHashrate) * 100);
+ const formattedHashrate = formatHashrateForDisplay(worker.hashrate_3hr, worker.hashrate_3hr_unit || 'th/s');
+
+ card.append(`
+
+
Hashrate (3hr):
+
${formattedHashrate}
+
+
+ `);
+
+ card.append(`
+
+
+
Last Share:
+
${typeof worker.last_share === 'string' ? worker.last_share.split(' ')[1] || worker.last_share : 'N/A'}
+
+
+
Earnings:
+
${worker.earnings.toFixed(8)}
+
+
+
Accept Rate:
+
${worker.acceptance_rate}%
+
+
+
Temp:
+
${worker.temperature > 0 ? worker.temperature + '°C' : 'N/A'}
+
+
+ `);
+
+ return card;
+}
+
// Filter worker data based on current filter state
function filterWorkersData(workers) {
if (!workers) return [];
return workers.filter(worker => {
- // Default to empty string if name is undefined
const workerName = (worker.name || '').toLowerCase();
const isOnline = worker.status === 'online';
const workerType = (worker.type || '').toLowerCase();
- // Check if worker matches filter
- let matchesFilter = false;
- if (filterState.currentFilter === 'all') {
- matchesFilter = true;
- } else if (filterState.currentFilter === 'online' && isOnline) {
- matchesFilter = true;
- } else if (filterState.currentFilter === 'offline' && !isOnline) {
- matchesFilter = true;
- } else if (filterState.currentFilter === 'asic' && workerType === 'asic') {
- matchesFilter = true;
- } else if (filterState.currentFilter === 'fpga' && workerType === 'fpga') {
- matchesFilter = true;
- }
+ const matchesFilter = filterState.currentFilter === 'all' ||
+ (filterState.currentFilter === 'online' && isOnline) ||
+ (filterState.currentFilter === 'offline' && !isOnline) ||
+ (filterState.currentFilter === 'asic' && workerType === 'asic') ||
+ (filterState.currentFilter === 'fpga' && workerType === 'fpga');
- // Check if worker matches search term
const matchesSearch = filterState.searchTerm === '' || workerName.includes(filterState.searchTerm);
return matchesFilter && matchesSearch;
@@ -428,38 +353,25 @@ function filterWorkersData(workers) {
// Apply filter to rendered worker cards
function filterWorkers() {
if (!workerData || !workerData.workers) return;
-
- // Re-render the worker grid with current filters
updateWorkerGrid();
}
-// Modified updateSummaryStats function with normalized hashrate display
+// Update summary stats with normalized hashrate display
function updateSummaryStats() {
if (!workerData) return;
- // Update worker counts
$('#workers-count').text(workerData.workers_total || 0);
$('#workers-online').text(workerData.workers_online || 0);
$('#workers-offline').text(workerData.workers_offline || 0);
- // Update worker ring percentage
- const onlinePercent = workerData.workers_total > 0 ?
- workerData.workers_online / workerData.workers_total : 0;
+ const onlinePercent = workerData.workers_total > 0 ? workerData.workers_online / workerData.workers_total : 0;
$('.worker-ring').css('--online-percent', onlinePercent);
- // Display normalized hashrate with appropriate unit
- if (workerData.total_hashrate !== undefined) {
- // Format with proper unit conversion
- const formattedHashrate = formatHashrateForDisplay(
- workerData.total_hashrate,
- workerData.hashrate_unit || 'TH/s'
- );
- $('#total-hashrate').text(formattedHashrate);
- } else {
- $('#total-hashrate').text(`0.0 TH/s`);
- }
+ const formattedHashrate = workerData.total_hashrate !== undefined ?
+ formatHashrateForDisplay(workerData.total_hashrate, workerData.hashrate_unit || 'TH/s') :
+ '0.0 TH/s';
+ $('#total-hashrate').text(formattedHashrate);
- // Update other summary stats
$('#total-earnings').text(`${(workerData.total_earnings || 0).toFixed(8)} BTC`);
$('#daily-sats').text(`${numberWithCommas(workerData.daily_sats || 0)} sats`);
$('#avg-acceptance-rate').text(`${(workerData.avg_acceptance_rate || 0).toFixed(2)}%`);
@@ -475,7 +387,6 @@ function initializeMiniChart() {
return;
}
- // Generate some sample data to start
const labels = Array(24).fill('').map((_, i) => i);
const data = Array(24).fill(0).map(() => Math.random() * 100 + 700);
@@ -525,32 +436,23 @@ function updateMiniChart() {
return;
}
- // Extract hashrate data from history
const historyData = workerData.hashrate_history;
if (!historyData || historyData.length === 0) {
console.log("No hashrate history data available");
return;
}
- // Get the normalized values for the chart
- const values = historyData.map(item => {
- const val = parseFloat(item.value) || 0;
- const unit = item.unit || workerData.hashrate_unit || 'th/s';
- return normalizeHashrate(val, unit);
- });
+ const values = historyData.map(item => normalizeHashrate(parseFloat(item.value) || 0, item.unit || workerData.hashrate_unit || 'th/s'));
const labels = historyData.map(item => item.time);
- // Update chart data
miniChart.data.labels = labels;
miniChart.data.datasets[0].data = values;
- // Update y-axis range
const min = Math.min(...values.filter(v => v > 0)) || 0;
const max = Math.max(...values) || 1;
miniChart.options.scales.y.min = min * 0.9;
miniChart.options.scales.y.max = max * 1.1;
- // Update the chart
miniChart.update('none');
}