custom-ocean.xyz-dashboard/static/js/console.js
DJObleezy 695e4cfb95 Change unpaid earnings display to BTC format
Updated the `logCurrentStats` function to convert unpaid earnings from SATS to BTC. The new implementation divides the unpaid earnings by 100,000,000 and formats the result to 8 decimal places, improving clarity by using a more recognized cryptocurrency unit.
2025-04-12 17:37:20 -07:00

384 lines
14 KiB
JavaScript

/**
* Bitcoin Mining Console Log - Real-time Mining Data Display
* Displays actual mining metrics rather than simulated logs
*/
// Message type constants
const MSG_TYPE = {
SYSTEM: 'system',
INFO: 'info',
WARNING: 'warning',
ERROR: 'error',
SUCCESS: 'success',
HASH: 'hash',
SHARE: 'share',
BLOCK: 'block',
NETWORK: 'network'
};
// Global settings and state
const consoleSettings = {
maxLines: 500, // Maximum number of lines to keep in the console
autoScroll: true, // Auto-scroll to bottom on new messages
refreshInterval: 15000, // Refresh metrics every 15 seconds
glitchProbability: 0.05 // 5% chance of text glitch effect for retro feel
};
// Cache for metrics data and tracking changes
let cachedMetrics = null;
let previousMetrics = null;
let lastBlockHeight = null;
let lastUpdateTime = null;
let logUpdateQueue = []; // Queue to store log updates
let logInterval = 2000; // Interval to process log updates (2 seconds)
// Initialize console
document.addEventListener('DOMContentLoaded', function () {
console.log('Bitcoin Mining Console initialized');
// Update clock
updateClock();
setInterval(updateClock, 1000);
// Fetch initial metrics
fetchMetrics();
// Setup event source for real-time updates
setupEventSource();
// Periodic full refresh as backup
setInterval(fetchMetrics, consoleSettings.refreshInterval);
// Process queued metric updates regularly
setInterval(processLogQueue, logInterval);
// Add layout adjustment
adjustConsoleLayout();
window.addEventListener('resize', adjustConsoleLayout);
// Add initial system message
addConsoleMessage("BITCOIN MINING CONSOLE INITIALIZED - CONNECTING TO DATA SOURCES...", MSG_TYPE.SYSTEM);
});
/**
* Format date for console display
*/
function formatDate(date) {
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
/**
* Update the clock in the console header
*/
function updateClock() {
const now = new Date();
document.getElementById('current-time').textContent = formatDate(now);
}
/**
* Set up Server-Sent Events for real-time updates
*/
function setupEventSource() {
const eventSource = new EventSource('/stream');
eventSource.onmessage = function (event) {
try {
const data = JSON.parse(event.data);
if (data.type === "ping" || data.type === "timeout_warning" || data.type === "timeout") {
return; // Ignore ping and timeout messages
}
// Store previous metrics for comparison
previousMetrics = cachedMetrics ? { ...cachedMetrics } : null;
// Update cached metrics
cachedMetrics = data;
// Check for significant changes and log them
processMetricChanges(previousMetrics, cachedMetrics);
// Update dashboard stats
updateDashboardStats(data);
} catch (error) {
console.error('Error processing SSE data:', error);
addConsoleMessage(`DATA STREAM ERROR: ${error.message}`, MSG_TYPE.ERROR);
}
};
eventSource.onerror = function () {
console.error('SSE connection error');
addConsoleMessage("CONNECTION ERROR: METRICS STREAM INTERRUPTED - ATTEMPTING RECONNECTION...", MSG_TYPE.ERROR);
// Reconnect after 5 seconds
setTimeout(setupEventSource, 5000);
};
// Log successful connection
addConsoleMessage("REAL-TIME DATA STREAM ESTABLISHED", MSG_TYPE.SUCCESS);
}
/**
* Fetch metrics via API
*/
function fetchMetrics() {
fetch('/api/metrics')
.then(response => response.json())
.then(data => {
// Store previous metrics for comparison
previousMetrics = cachedMetrics ? { ...cachedMetrics } : null;
// Update cached metrics
cachedMetrics = data;
lastUpdateTime = new Date();
// Check for significant changes and log them
processMetricChanges(previousMetrics, cachedMetrics);
// Update dashboard stats
updateDashboardStats(data);
// Log first connection
if (!previousMetrics) {
addConsoleMessage("CONNECTED TO MINING DATA SOURCE - MONITORING METRICS", MSG_TYPE.SUCCESS);
}
})
.catch(error => {
console.error('Error fetching metrics:', error);
addConsoleMessage(`METRICS FETCH ERROR: ${error.message} - RETRYING...`, MSG_TYPE.ERROR);
});
}
/**
* Process changes between old and new metrics
*/
function processMetricChanges(oldMetrics, newMetrics) {
if (!oldMetrics || !newMetrics) return;
// Check for block height change (new block found)
if (oldMetrics.block_number !== newMetrics.block_number) {
const message = `BLOCKCHAIN UPDATE: NEW BLOCK #${numberWithCommas(newMetrics.block_number)} DETECTED`;
queueLogUpdate(message, MSG_TYPE.BLOCK);
lastBlockHeight = newMetrics.block_number;
}
// Check for worker count changes
if (oldMetrics.workers_hashing !== newMetrics.workers_hashing) {
let message;
if (newMetrics.workers_hashing > oldMetrics.workers_hashing) {
const diff = newMetrics.workers_hashing - oldMetrics.workers_hashing;
message = `WORKER STATUS: ${diff} ADDITIONAL WORKER${diff > 1 ? 'S' : ''} CAME ONLINE - NOW ${newMetrics.workers_hashing} ACTIVE`;
queueLogUpdate(message, MSG_TYPE.SUCCESS);
} else {
const diff = oldMetrics.workers_hashing - newMetrics.workers_hashing;
message = `WORKER STATUS: ${diff} WORKER${diff > 1 ? 'S' : ''} WENT OFFLINE - NOW ${newMetrics.workers_hashing} ACTIVE`;
queueLogUpdate(message, MSG_TYPE.WARNING);
}
}
// Check for significant hashrate changes (>5%)
const oldHashrate = oldMetrics.hashrate_10min || oldMetrics.hashrate_3hr || 0;
const newHashrate = newMetrics.hashrate_10min || newMetrics.hashrate_3hr || 0;
const hashrateUnit = newMetrics.hashrate_10min_unit || newMetrics.hashrate_3hr_unit || 'TH/s';
if (oldHashrate > 0 && Math.abs((newHashrate - oldHashrate) / oldHashrate) > 0.05) {
const pctChange = ((newHashrate - oldHashrate) / oldHashrate * 100).toFixed(1);
const direction = newHashrate > oldHashrate ? 'INCREASE' : 'DECREASE';
const message = `HASHRATE ${direction}: ${newHashrate.toFixed(2)} ${hashrateUnit} - ${Math.abs(pctChange)}% CHANGE`;
queueLogUpdate(message, newHashrate > oldHashrate ? MSG_TYPE.SUCCESS : MSG_TYPE.INFO);
}
// Check for BTC price changes
if (Math.abs((newMetrics.btc_price - oldMetrics.btc_price) / oldMetrics.btc_price) > 0.005) {
const direction = newMetrics.btc_price > oldMetrics.btc_price ? 'UP' : 'DOWN';
const pctChange = ((newMetrics.btc_price - oldMetrics.btc_price) / oldMetrics.btc_price * 100).toFixed(2);
const message = `MARKET UPDATE: BTC ${direction} ${Math.abs(pctChange)}% - NOW $${numberWithCommas(newMetrics.btc_price.toFixed(2))}`;
queueLogUpdate(message, newMetrics.btc_price > oldMetrics.btc_price ? MSG_TYPE.SUCCESS : MSG_TYPE.INFO);
}
// Check mining profitability changes
if (newMetrics.daily_profit_usd !== oldMetrics.daily_profit_usd) {
if ((oldMetrics.daily_profit_usd < 0 && newMetrics.daily_profit_usd >= 0) ||
(oldMetrics.daily_profit_usd >= 0 && newMetrics.daily_profit_usd < 0)) {
const message = newMetrics.daily_profit_usd >= 0
? `PROFITABILITY ALERT: MINING NOW PROFITABLE AT $${newMetrics.daily_profit_usd.toFixed(2)}/DAY`
: `PROFITABILITY ALERT: MINING NOW UNPROFITABLE - LOSING $${Math.abs(newMetrics.daily_profit_usd).toFixed(2)}/DAY`;
queueLogUpdate(message, newMetrics.daily_profit_usd >= 0 ? MSG_TYPE.SUCCESS : MSG_TYPE.WARNING);
}
}
// Log difficulty changes
if (oldMetrics.difficulty && newMetrics.difficulty &&
Math.abs((newMetrics.difficulty - oldMetrics.difficulty) / oldMetrics.difficulty) > 0.001) {
const direction = newMetrics.difficulty > oldMetrics.difficulty ? 'INCREASED' : 'DECREASED';
const pctChange = ((newMetrics.difficulty - oldMetrics.difficulty) / oldMetrics.difficulty * 100).toFixed(2);
const message = `NETWORK DIFFICULTY ${direction} BY ${Math.abs(pctChange)}% - NOW ${numberWithCommas(Math.round(newMetrics.difficulty))}`;
queueLogUpdate(message, MSG_TYPE.NETWORK);
}
// Add periodic stats summary regardless of changes
if (!lastUpdateTime || (new Date() - lastUpdateTime) > 60000) { // Every minute
logCurrentStats(newMetrics);
}
}
/**
* Queue a log update to be shown (prevents flooding)
*/
function queueLogUpdate(message, type = MSG_TYPE.INFO) {
logUpdateQueue.push({ message, type });
}
/**
* Process queued log updates
*/
function processLogQueue() {
if (logUpdateQueue.length > 0) {
const update = logUpdateQueue.shift(); // Get the next update
addConsoleMessage(update.message, update.type); // Display it
} else {
// If the queue is empty, log periodic stats
logCurrentStats(cachedMetrics);
}
}
/**
* Log current mining stats periodically
*/
function logCurrentStats(metrics) {
if (!metrics) return;
// Define an array of possible log messages with corrected formatting
const logMessages = [
`HASHRATE: ${metrics.hashrate_60sec || metrics.hashrate_10min || metrics.hashrate_3hr || 0} ${metrics.hashrate_60sec_unit || metrics.hashrate_10min_unit || metrics.hashrate_3hr_unit || 'TH/s'}`,
`BLOCK HEIGHT: ${numberWithCommas(metrics.block_number || 0)}`,
`WORKERS ONLINE: ${metrics.workers_hashing || 0}`,
`BTC PRICE: $${numberWithCommas(parseFloat(metrics.btc_price || 0).toFixed(2))}`,
`DAILY PROFIT: $${metrics.daily_profit_usd ? metrics.daily_profit_usd.toFixed(2) : '0.00'}`,
// Convert SATS to BTC (1 BTC = 100,000,000 SATS) and format with 8 decimal places
`UNPAID EARNINGS: ${(parseFloat(metrics.unpaid_earnings || 0) / 100000000).toFixed(8)} BTC`,
`NETWORK DIFFICULTY: ${numberWithCommas(Math.round(metrics.difficulty || 0))}`,
// Fix power consumption to show 0W instead of N/AW when not available
`POWER CONSUMPTION: ${metrics.power_usage || '0'} WATTS`,
];
// Randomize the order of log messages
shuffleArray(logMessages);
// Queue the first few messages for display
logMessages.slice(0, 3).forEach(message => queueLogUpdate(message, MSG_TYPE.INFO));
// Update the last update time
lastUpdateTime = new Date();
}
/**
* Shuffle an array in place
*/
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
/**
* Update the dashboard stats display in the footer
*/
function updateDashboardStats(data) {
if (!data) return;
// Update hashrate
const hashrate = data.hashrate_60sec || data.hashrate_10min || data.hashrate_3hr || 0;
const hashrateUnit = (data.hashrate_60sec_unit || data.hashrate_10min_unit || data.hashrate_3hr_unit || 'TH/s').toUpperCase();
document.getElementById('current-hashrate').textContent = `${hashrate} ${hashrateUnit}`;
// Update block height
document.getElementById('block-height').textContent = numberWithCommas(data.block_number || 0);
// Update workers online
document.getElementById('workers-online').textContent = data.workers_hashing || 0;
// Update BTC price
document.getElementById('btc-price').textContent = `$${numberWithCommas(parseFloat(data.btc_price || 0).toFixed(2))}`;
}
/**
* Format number with commas
*/
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
/**
* Add a message to the console output
*/
function addConsoleMessage(message, type = MSG_TYPE.SYSTEM) {
const consoleOutput = document.getElementById('console-output');
const now = new Date();
const timestamp = formatDate(now);
// Create the message element
const lineElement = document.createElement('div');
lineElement.className = 'console-line';
// Add timestamp
const timestampSpan = document.createElement('span');
timestampSpan.className = 'timestamp';
timestampSpan.textContent = `[${timestamp}] `;
lineElement.appendChild(timestampSpan);
// Add message with appropriate class based on type
const messageSpan = document.createElement('span');
messageSpan.className = type;
messageSpan.textContent = message;
// Apply glitch effect occasionally for retro aesthetic
if (Math.random() < consoleSettings.glitchProbability) {
messageSpan.classList.add('glitch');
messageSpan.setAttribute('data-text', message);
}
lineElement.appendChild(messageSpan);
// Add to console
consoleOutput.appendChild(lineElement);
// Limit the number of lines in the console
while (consoleOutput.children.length > consoleSettings.maxLines) {
consoleOutput.removeChild(consoleOutput.firstChild);
}
// Auto-scroll to bottom
if (consoleSettings.autoScroll) {
const consoleWrapper = document.querySelector('.console-wrapper');
consoleWrapper.scrollTop = consoleWrapper.scrollHeight;
}
}
/**
* Adjust console layout to ensure proper proportions
*/
function adjustConsoleLayout() {
const container = document.querySelector('.console-container');
const header = document.querySelector('.console-header');
const stats = document.querySelector('.console-stats');
const wrapper = document.querySelector('.console-wrapper');
if (container && header && stats && wrapper) {
// Calculate the proper height for wrapper
const containerHeight = container.clientHeight;
const headerHeight = header.clientHeight;
const statsHeight = stats.clientHeight;
// Set the wrapper height to fill the space between header and stats
const wrapperHeight = containerHeight - headerHeight - statsHeight;
wrapper.style.height = `${Math.max(wrapperHeight, 150)}px`; // Min height of 150px
}
}