custom-ocean.xyz-dashboard/static/js/console.js
DJObleezy a60d21521d Improve console layout and responsiveness
Updated `console.css` to enhance the layout and responsiveness of the console interface. Adjusted the console container to utilize the full viewport height while maintaining a controlled height for the console wrapper. Changed console output positioning to relative for better spacing and positioned the stats bar at the bottom for consistency. Added a new JavaScript function `adjustConsoleLayout` to dynamically calculate and set the height of the console wrapper based on viewport size, improving user experience across different screen sizes.
2025-04-12 13:53:27 -07:00

507 lines
21 KiB
JavaScript

/**
* Bitcoin Mining Console Log Simulation
* A retro-styled terminal/console log display showing real-time mining metrics
*/
// 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
logFrequency: { // Frequency of different message types (ms)
system: 5000, // System messages every 5 seconds
hash: 2000, // Hash updates every 2 seconds
share: 10000, // Share submissions every 10 seconds
network: 30000, // Network updates every 30 seconds
block: 600000 // Block updates roughly every 10 minutes (on average)
},
glitchProbability: 0.05 // 5% chance of text glitch effect on any message
};
// Cache for metrics data
let cachedMetrics = null;
const hashRateFluctuation = 0.01; // 1% fluctuation for realistic variance
// Initialize console
document.addEventListener('DOMContentLoaded', function () {
console.log('Console log initialized');
// Update clock
updateClock();
setInterval(updateClock, 1000);
// Set up metrics fetch
fetchMetrics();
setupEventSource();
// Start log generators with slight delays to avoid all happening at once
setTimeout(() => startLogGenerator(MSG_TYPE.HASH), 1000);
setTimeout(() => startLogGenerator(MSG_TYPE.SHARE), 3000);
setTimeout(() => startLogGenerator(MSG_TYPE.SYSTEM), 5000);
setTimeout(() => startLogGenerator(MSG_TYPE.NETWORK), 7000);
setTimeout(() => startLogGenerator(MSG_TYPE.BLOCK), 9000);
// Add random errors/warnings occasionally
setInterval(generateRandomEvent, 60000);
});
/**
* 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
}
// Update cached metrics
cachedMetrics = data;
// Update dashboard stats
updateDashboardStats(data);
} catch (error) {
console.error('Error processing SSE data:', error);
}
};
eventSource.onerror = function () {
console.error('SSE connection error');
// Reconnect after 5 seconds
setTimeout(setupEventSource, 5000);
};
}
/**
* Fetch metrics via API
*/
function fetchMetrics() {
fetch('/api/metrics')
.then(response => response.json())
.then(data => {
cachedMetrics = data;
updateDashboardStats(data);
})
.catch(error => {
console.error('Error fetching metrics:', error);
addConsoleMessage('ERROR FETCHING METRICS DATA. RETRYING...', MSG_TYPE.ERROR);
});
}
/**
* Update the dashboard stats display
*/
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, ",");
}
/**
* Start a specific log generator
*/
function startLogGenerator(type) {
const interval = consoleSettings.logFrequency[type];
if (!interval) return;
setInterval(() => {
generateLog(type);
}, interval);
}
/**
* Generate a log message based on type
*/
function generateLog(type) {
if (!cachedMetrics) return;
let message = '';
let messageType = type;
switch (type) {
case MSG_TYPE.SYSTEM:
message = generateSystemMessage();
break;
case MSG_TYPE.HASH:
message = generateHashrateMessage();
break;
case MSG_TYPE.SHARE:
message = generateShareMessage();
break;
case MSG_TYPE.NETWORK:
message = generateNetworkMessage();
break;
case MSG_TYPE.BLOCK:
// Only generate block messages occasionally (simulating real mining)
if (Math.random() < 0.1) { // 10% chance each time this is called
message = generateBlockMessage();
} else {
return; // Skip this cycle
}
break;
}
// If we have a message, add it to console
if (message) {
addConsoleMessage(message, messageType);
}
}
/**
* Generate a system status message based on actual metrics
*/
function generateSystemMessage() {
if (!cachedMetrics) return 'SYSTEM STATUS: AWAITING METRICS DATA...';
// Create an array of possible messages that use real metrics where available
const systemMessages = [
// Power consumption using actual configured power usage when available
`POWER CONSUMPTION: ${cachedMetrics.power_usage ? `${cachedMetrics.power_usage}W` : `${randomInt(800, 1500)}W`} - EFFICIENCY: ${cachedMetrics.power_cost ? `$${cachedMetrics.power_cost}/kWh` : 'CALCULATING...'}`,
// Use real worker count for system health reports
`SYSTEM HEALTH CHECK: ${cachedMetrics.workers_hashing || 0}/${cachedMetrics.workers_total || 0} WORKERS OPERATIONAL - ${Math.round(((cachedMetrics.workers_hashing || 0) / (cachedMetrics.workers_total || 1)) * 100)}% ONLINE`,
// Use actual hashrate
`PROCESSING CAPACITY: ${cachedMetrics.hashrate_60sec || cachedMetrics.hashrate_10min || cachedMetrics.hashrate_3hr || 0} ${cachedMetrics.hashrate_60sec_unit || cachedMetrics.hashrate_10min_unit || cachedMetrics.hashrate_3hr_unit || 'TH/s'} - ${cachedMetrics.workers_hashing ? 'OPTIMAL' : 'SUBOPTIMAL'} PERFORMANCE`,
// Network synchronization based on real block height
`BLOCKCHAIN SYNC STATUS: HEIGHT ${numberWithCommas(cachedMetrics.block_number || 0)} - 100% SYNCHRONIZED`,
// Earnings projection based on actual data
`REVENUE PROJECTION: ${cachedMetrics.daily_revenue ? `$${cachedMetrics.daily_revenue.toFixed(2)}/DAY` : 'CALCULATING...'} - ${cachedMetrics.daily_profit_usd ? `$${cachedMetrics.daily_profit_usd.toFixed(2)} PROFIT` : 'CALCULATING PROFIT...'}`,
// Time to payout/rewards
`PAYOUT ESTIMATION: ${cachedMetrics.est_time_to_payout || 'CALCULATING...'} - ${numberWithCommas(cachedMetrics.unpaid_earnings || 0)} SATS PENDING`,
// Real Bitcoin price
`MARKET CONDITIONS: BTC $${numberWithCommas(parseFloat(cachedMetrics.btc_price || 0).toFixed(2))} - MINING PROFITABILITY ${cachedMetrics.daily_profit_usd > 0 ? 'POSITIVE' : 'NEGATIVE'}`,
// Monthly projections
`MONTHLY PROJECTION: ${cachedMetrics.monthly_mined_sats ? `${numberWithCommas(cachedMetrics.monthly_mined_sats)} SATS` : 'CALCULATING...'} - ${cachedMetrics.monthly_profit_usd ? `$${numberWithCommas(cachedMetrics.monthly_profit_usd.toFixed(2))}` : 'CALCULATING...'}`,
// Network difficulty and mining pool statistics
`MINING DIFFICULTY: ${numberWithCommas(Math.round(cachedMetrics.difficulty || 0))} - POOL HASHRATE: ${cachedMetrics.pool_total_hashrate || 0} ${cachedMetrics.pool_total_hashrate_unit || 'TH/s'}`,
// System uptime (still random as we don't track this)
`SYSTEM UPTIME: ${randomInt(1, 24)}h ${randomInt(0, 59)}m ${randomInt(0, 59)}s - STABILITY: ${cachedMetrics.workers_hashing > 0 ? 'HIGH' : 'DEGRADED'}`
];
// Add some random but realistic messages to maintain variety
if (Math.random() < 0.3) {
systemMessages.push(`SYSTEM TEMPERATURE: ${randomInt(50, 75)}°C - ${randomInt(50, 70) > 65 ? 'WARNING: HIGH' : 'WITHIN NORMAL PARAMETERS'}`);
systemMessages.push(`MEMORY USAGE: ${randomInt(30, 70)}% - ${randomInt(2048, 8192)}MB ALLOCATED`);
systemMessages.push(`CPU UTILIZATION: ${randomInt(20, 95)}% - PROCESSING MINING ALGORITHMS`);
systemMessages.push(`NETWORK LATENCY: ${randomInt(5, 200)}ms TO MINING POOL - ${randomInt(5, 200) > 100 ? 'HIGH LATENCY' : 'OPTIMAL'}`);
}
// Special alerts based on metrics state
if (cachedMetrics.workers_total && cachedMetrics.workers_hashing < cachedMetrics.workers_total) {
systemMessages.push(`ALERT: ${cachedMetrics.workers_total - cachedMetrics.workers_hashing} WORKERS OFFLINE - MAINTENANCE REQUIRED`);
}
if (cachedMetrics.daily_profit_usd && cachedMetrics.daily_profit_usd < 0) {
systemMessages.push(`WARNING: NEGATIVE MINING PROFITABILITY - $${Math.abs(cachedMetrics.daily_profit_usd).toFixed(2)}/DAY LOSS`);
}
// Return a random message from the array
return systemMessages[Math.floor(Math.random() * systemMessages.length)];
}
/**
* Generate a hashrate update message
*/
function generateHashrateMessage() {
if (!cachedMetrics) return '';
// Get base hashrate from cached metrics
const baseHashrate = cachedMetrics.hashrate_60sec || cachedMetrics.hashrate_10min || cachedMetrics.hashrate_3hr || 1;
const hashrateUnit = (cachedMetrics.hashrate_60sec_unit || cachedMetrics.hashrate_10min_unit || cachedMetrics.hashrate_3hr_unit || 'TH/s').toUpperCase();
// Add some fluctuation for realism
const fluctuation = (Math.random() * 2 - 1) * hashRateFluctuation;
const currentHashrate = (baseHashrate * (1 + fluctuation)).toFixed(2);
const hashMessages = [
`HASHRATE UPDATE: ${currentHashrate} ${hashrateUnit} - ${fluctuation >= 0 ? 'INCREASE' : 'DECREASE'} OF ${Math.abs(fluctuation * 100).toFixed(2)}%`,
`MINING PERFORMANCE: ${currentHashrate} ${hashrateUnit} - ${randomInt(97, 100)}% EFFICIENCY`,
`HASH COMPUTATION RATE: ${currentHashrate} ${hashrateUnit} - ${fluctuation >= 0 ? 'OPTIMAL' : 'SUBOPTIMAL'} PERFORMANCE`,
`PROCESSING HASHES AT ${currentHashrate} ${hashrateUnit} - ${cachedMetrics.workers_hashing || 1} WORKERS ACTIVE`,
`CURRENT HASHING POWER: ${currentHashrate} ${hashrateUnit} - NETWORK CONTRIBUTION: ${(baseHashrate / (cachedMetrics.network_hashrate * 1000 || 1) * 100).toFixed(8)}%`
];
return hashMessages[Math.floor(Math.random() * hashMessages.length)];
}
/**
* Generate a share submission message
*/
function generateShareMessage() {
if (!cachedMetrics) return '';
const difficulty = randomInt(8000, 12000) / 100;
const timeToSolve = randomInt(10, 990) / 10;
const accepted = Math.random() < 0.98; // 98% acceptance rate
if (accepted) {
const shareMessages = [
`SHARE ACCEPTED [${generateRandomHex(8)}] - DIFFICULTY ${difficulty} - SOLVED IN ${timeToSolve}s`,
`VALID SHARE SUBMITTED [${generateRandomHex(8)}] - POOL ACCEPTED - DIFFICULTY ${difficulty}`,
`SHARE SOLUTION FOUND [${generateRandomHex(8)}] - VERIFICATION SUCCESSFUL - EFFORT: ${randomInt(1, 200)}%`,
`MINING SHARE ACCEPTED BY POOL [${generateRandomHex(8)}] - ${timeToSolve}s SOLUTION TIME`,
`SHARE SUBMISSION SUCCESSFUL [${generateRandomHex(8)}] - YAY!!! ⚡`
];
return shareMessages[Math.floor(Math.random() * shareMessages.length)];
} else {
const rejectReasons = ['LOW DIFFICULTY', 'STALE', 'DUPLICATE', 'INVALID NONCE', 'BAD TRANSACTION'];
const reason = rejectReasons[Math.floor(Math.random() * rejectReasons.length)];
return `SHARE REJECTED [${generateRandomHex(8)}] - REASON: ${reason} - DIFFICULTY ${difficulty}`;
}
}
/**
* Generate a network update message
*/
function generateNetworkMessage() {
if (!cachedMetrics) return '';
const networkHashrate = cachedMetrics.network_hashrate || 350;
const difficulty = cachedMetrics.difficulty || 60000000000000;
const btcPrice = cachedMetrics.btc_price || 75000;
const networkMessages = [
`NETWORK HASHRATE: ${networkHashrate} EH/s - GLOBAL MINING POWER`,
`NETWORK DIFFICULTY: ${numberWithCommas(Math.round(difficulty))} - NEXT ADJUSTMENT IN ${randomInt(1, 14)} DAYS`,
`BTC MARKET UPDATE: $${numberWithCommas(btcPrice)} - ${Math.random() < 0.7 ? 'UP' : 'DOWN'} ${randomInt(1, 500) / 100}% IN 24HR`,
`BLOCKCHAIN HEIGHT: ${numberWithCommas(cachedMetrics.block_number || 0)} - FULLY SYNCHRONIZED`,
`MEMPOOL STATUS: ${randomInt(5000, 50000)} TRANSACTIONS PENDING - ${randomInt(10, 100)} SAT/VBYTE`,
`ESTIMATED EARNINGS: ${cachedMetrics.daily_mined_sats || 0} SATS PER DAY AT CURRENT RATE`,
`TRANSACTION FEES: AVERAGE ${randomInt(1000, 10000)} SATS PER TRANSACTION`,
`POOL FEE: ${randomInt(0, 3)}% + ${randomInt(0, 2)}% MINING FEE = ${randomInt(1, 5)}% TOTAL DEDUCTION`
];
return networkMessages[Math.floor(Math.random() * networkMessages.length)];
}
/**
* Generate a block-related message
*/
function generateBlockMessage() {
if (!cachedMetrics) return '';
const blockHeight = cachedMetrics.block_number || 0;
const nextBlockHeight = blockHeight + 1;
// Generate random transaction count for the block
const txCount = randomInt(1000, 4000);
const blockSize = randomInt(1, 4) + "." + randomInt(0, 9) + " MB";
const blockReward = "3.125 BTC + " + randomInt(0, 3) + "." + randomInt(0, 999) + " BTC FEES";
// Determine if this is a block found by the pool (rare event)
const isPoolBlock = Math.random() < 0.01; // 1% chance
if (isPoolBlock) {
// This is a special case - our pool found a block!
const blockMessages = [
`🎉 BLOCK FOUND BY POOL! 🎉 HEIGHT: ${blockHeight} - REWARD: ${blockReward}`,
`⚡⚡⚡ POOL SUCCESSFULLY MINED BLOCK ${blockHeight}! REWARD: ${blockReward} ⚡⚡⚡`,
`!!!CONGRATULATIONS!!! BLOCK ${blockHeight} MINED BY OUR POOL! ${txCount} TRANSACTIONS CONFIRMED`,
`$$$ BLOCK REWARD INCOMING $$$ - POOL MINED BLOCK ${blockHeight} - ${blockReward}`,
`NEW BLOCK ${blockHeight} MINED BY OUR POOL! SIZE: ${blockSize}, TXs: ${txCount}, REWARD: ${blockReward}`
];
return blockMessages[Math.floor(Math.random() * blockMessages.length)];
} else {
// Regular block found by someone else
const blockMessages = [
`NEW BLOCK DETECTED: HEIGHT ${blockHeight} - ${txCount} TRANSACTIONS - SIZE: ${blockSize}`,
`BLOCKCHAIN UPDATE: BLOCK ${blockHeight} CONFIRMED - MINED BY EXTERNAL POOL`,
`BLOCK ${blockHeight} ADDED TO BLOCKCHAIN - DIFFICULTY TARGET: ${generateRandomHex(8)}...`,
`NETWORK: NEW BLOCK ${blockHeight} - REWARD: ${blockReward}`,
`BLOCK HEIGHT ${blockHeight} CONFIRMED - WORKING ON NEXT BLOCK: ${nextBlockHeight}`
];
return blockMessages[Math.floor(Math.random() * blockMessages.length)];
}
}
/**
* Generate random technical or error events
*/
function generateRandomEvent() {
// Only 20% chance to generate a random event
if (Math.random() > 0.2) return;
const events = [
{ message: "WARNING: EXCESSIVE TEMPERATURE DETECTED ON WORKER #" + randomInt(1, 5), type: MSG_TYPE.WARNING },
{ message: "ERROR: INVALID MINING PARAMETER DETECTED - RECONFIGURING ALGORITHM", type: MSG_TYPE.ERROR },
{ message: "NOTICE: OPTIMIZING MEMORY ALLOCATION FOR IMPROVED PERFORMANCE", type: MSG_TYPE.INFO },
{ message: "ALERT: NETWORK DIFFICULTY INCREASED BY " + randomInt(1, 10) + "% - ADJUSTING PARAMETERS", type: MSG_TYPE.WARNING },
{ message: "SYSTEM: DETECTED " + randomInt(2, 10) + " REJECTED SHARES IN LAST MINUTE - ANALYZING CAUSE", type: MSG_TYPE.WARNING },
{ message: "ERROR: CONNECTION TO MINING POOL TIMED OUT - ATTEMPTING RECONNECTION...", type: MSG_TYPE.ERROR },
{ message: "SUCCESS: CONNECTION RESTORED - RESUMING MINING OPERATIONS", type: MSG_TYPE.SUCCESS },
{ message: "NOTICE: FIRMWARE UPDATE AVAILABLE FOR MINING HARDWARE", type: MSG_TYPE.INFO },
{ message: "WARNING: POWER SUPPLY FLUCTUATION DETECTED - MONITORING VOLTAGE", type: MSG_TYPE.WARNING },
{ message: "SYSTEM: RECALIBRATING HASH VERIFICATION ALGORITHM", type: MSG_TYPE.SYSTEM }
];
const event = events[Math.floor(Math.random() * events.length)];
addConsoleMessage(event.message, event.type);
}
/**
* 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;
}
}
/**
* Generate a random integer between min and max (inclusive)
*/
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Generate a random hexadecimal string of specified length
*/
function generateRandomHex(length) {
const characters = '0123456789ABCDEF';
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * 16));
}
return result;
}
// Add this function to your console.js file - this will fix the layout issue
function adjustConsoleLayout() {
// Get the elements
const container = document.querySelector('.console-container');
const header = document.querySelector('.console-header');
const stats = document.querySelector('.console-stats');
const wrapper = document.querySelector('.console-wrapper');
const output = document.querySelector('.console-output');
if (container && header && stats && wrapper && output) {
// Calculate available height for the wrapper
const viewportHeight = window.innerHeight;
const headerHeight = header.offsetHeight;
const statsHeight = stats.offsetHeight;
const wrapperHeight = viewportHeight - headerHeight - statsHeight - 2; // 2px for borders
// Apply the calculated height to the wrapper
wrapper.style.height = `${wrapperHeight}px`;
wrapper.style.maxHeight = `${wrapperHeight}px`;
wrapper.style.position = 'relative';
// Make the output relative instead of absolute
output.style.position = 'relative';
output.style.bottom = 'auto';
}
}
// Call the function on load and whenever the window is resized
document.addEventListener('DOMContentLoaded', function () {
adjustConsoleLayout();
window.addEventListener('resize', adjustConsoleLayout);
});