document.addEventListener('DOMContentLoaded', function() {
// Initialize Chart
const ctx = document.getElementById('hashrateChart').getContext('2d');
const hashrateChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Hashrate (MH/s)',
data: [],
borderColor: '#0d6efd',
backgroundColor: 'rgba(13, 110, 253, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
x: {
grid: { color: '#333' },
ticks: { color: '#aaa' }
},
y: {
grid: { color: '#333' },
ticks: { color: '#aaa' },
beginAtZero: true
}
}
}
});
// Helper to format large numbers
function formatNumber(num) {
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
}
function updateDashboard() {
fetch('/api/data')
.then(response => response.json())
.then(data => {
const summary = data.summary;
const devs = data.devs;
const pools = data.pools;
// Update Top Stats
if (summary) {
const mhs = summary['MHS 5s'] || summary['MHS av'] || 0;
document.getElementById('stat-mhs').innerText = parseFloat(mhs).toFixed(2);
document.getElementById('stat-accepted').innerText = formatNumber(summary['Accepted'] || 0);
document.getElementById('stat-hw').innerText = formatNumber(summary['Hardware Errors'] || 0);
}
// Update Device Count
if (devs) {
document.getElementById('stat-devices').innerText = devs.length;
}
// Helper to find temp in stats if missing in devs
// Cgminer 'stats' command returns specific driver info.
// Structure: stats ->STATS -> list of dicts. usually one dict per device with ID keys like 'MM ID 0', etc.
// Or simplified list.
function getTempFallback(devId, statsData) {
// Try to find matching ID in stats
// This is heuristic as structure varies wildy between forks
return 0;
}
// Update Devs Table & Max Temp
let maxTemp = 0;
const tbody = document.getElementById('devs-table-body');
tbody.innerHTML = '';
if (devs && devs.length > 0) {
devs.forEach(dev => {
let temp = dev['Temperature'] || 0;
// Fallback logic could go here if we understood the STATS structure for this specific fork
// For now we rely on standard API.
if (temp > maxTemp) maxTemp = temp;
const tr = document.createElement('tr');
tr.innerHTML = `
${dev['ID']} |
${dev['Name'] || 'Gridseed'} |
${dev['Enabled']} |
${dev['Status']} |
${temp > 0 ? temp.toFixed(1) : '-'} |
${parseFloat(dev['MHS 5s'] || 0).toFixed(2)} |
${parseFloat(dev['MHS av'] || 0).toFixed(2)} |
${dev['Accepted']} |
${dev['Rejected']} |
${dev['Hardware Errors']} |
`;
tbody.appendChild(tr);
});
} else {
tbody.innerHTML = '| Keine Geräte gefunden... |
';
}
document.getElementById('stat-temp').innerText = maxTemp.toFixed(1);
// Update Chart
const now = new Date();
const timeLabel = now.getHours() + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0');
if (summary) {
const currentMhs = summary['MHS 5s'] || summary['MHS av'] || 0;
if (hashrateChart.data.labels.length > 60) { // Keep last 60 points
hashrateChart.data.labels.shift();
hashrateChart.data.datasets[0].data.shift();
}
hashrateChart.data.labels.push(timeLabel);
hashrateChart.data.datasets[0].data.push(currentMhs);
hashrateChart.update();
}
// Update Devs Table
const tbody = document.getElementById('devs-table-body');
tbody.innerHTML = '';
if (devs && devs.length > 0) {
devs.forEach(dev => {
const tr = document.createElement('tr');
tr.innerHTML = `
${dev['ID']} |
${dev['Name'] || 'Gridseed'} |
${dev['Enabled']} |
${dev['Status']} |
${dev['Temperature']} |
${parseFloat(dev['MHS 5s'] || 0).toFixed(2)} |
${parseFloat(dev['MHS av'] || 0).toFixed(2)} |
${dev['Accepted']} |
${dev['Rejected']} |
${dev['Hardware Errors']} |
`;
tbody.appendChild(tr);
});
} else {
tbody.innerHTML = '| Keine Geräte gefunden... |
';
}
// Update Pools
const poolList = document.getElementById('pool-list');
poolList.innerHTML = '';
if (pools && pools.length > 0) {
pools.forEach(pool => {
const div = document.createElement('div');
div.className = 'list-group-item bg-dark text-white border-secondary d-flex justify-content-between align-items-center';
div.innerHTML = `
Pool ${pool['POOL']}
${pool['URL']}
${pool['Status']}
`;
poolList.appendChild(div);
});
} else {
poolList.innerHTML = 'Keine Pools konfiguriert...
';
}
})
.catch(err => console.error('Error fetching data:', err));
}
// Update Log
function updateLog() {
fetch('/api/log')
.then(response => response.json())
.then(data => {
const logDiv = document.getElementById('miner-log');
if (data.log) {
const isScrolledToBottom = logDiv.scrollHeight - logDiv.clientHeight <= logDiv.scrollTop + 1;
logDiv.innerText = data.log;
if (isScrolledToBottom) {
logDiv.scrollTop = logDiv.scrollHeight;
}
}
})
.catch(err => console.error('Error fetching log:', err));
}
// Update every 3 seconds
setInterval(updateDashboard, 3000);
setInterval(updateLog, 5000);
updateDashboard(); // Initial call
updateLog();
});