// Hardware Renderer - Common rendering functions for hardware sections // Shared between devices.js and device_detail.js to avoid duplication (function() { 'use strict'; // Get utilities from global scope const utils = window.BenchUtils; // Helper: Clean empty/whitespace values const cleanValue = (val) => { if (!val || (typeof val === 'string' && val.trim() === '')) return 'N/A'; return val; }; // Helper: Render no data message const noData = (message = 'Aucune information disponible') => { return `

${message}

`; }; // ======================= // MOTHERBOARD SECTION // ======================= function renderMotherboardDetails(snapshot) { if (!snapshot) return noData(); const items = [ { label: 'Fabricant', value: cleanValue(snapshot.motherboard_vendor || snapshot.system_vendor) }, { label: 'Modèle', value: cleanValue(snapshot.motherboard_model || snapshot.system_model) }, { label: 'BIOS fabricant', value: cleanValue(snapshot.bios_vendor) }, { label: 'Version BIOS', value: cleanValue(snapshot.bios_version) }, { label: 'Date BIOS', value: cleanValue(snapshot.bios_date) }, { label: 'Hostname', value: cleanValue(snapshot.hostname) }, { label: 'Slots RAM', value: (snapshot.ram_slots_used != null || snapshot.ram_slots_total != null) ? `${snapshot.ram_slots_used ?? '?'} / ${snapshot.ram_slots_total ?? '?'}` : 'N/A' }, { label: 'Famille', value: cleanValue(snapshot.system_family) }, { label: 'Châssis', value: cleanValue(snapshot.chassis_type) } ]; return `
${items.map(item => `
${item.label}
${utils.escapeHtml(String(item.value))}
`).join('')}
`; } // ======================= // CPU SECTION // ======================= function renderCPUDetails(snapshot) { if (!snapshot) return noData(); // Parse multi-CPU from raw_info if available const rawInfo = snapshot.raw_info_json ? (typeof snapshot.raw_info_json === 'string' ? JSON.parse(snapshot.raw_info_json) : snapshot.raw_info_json) : null; const dmidecode = rawInfo?.dmidecode || ''; // Check for multi-socket CPUs (Proc 1, Proc 2, etc.) const cpuSockets = []; const socketRegex = /Handle 0x[0-9A-F]+, DMI type 4[\s\S]*?Socket Designation: (.*?)[\s\S]*?Version: (.*?)[\s\S]*?Core Count: (\d+)[\s\S]*?Thread Count: (\d+)[\s\S]*?(?:Max Speed: (\d+) MHz)?[\s\S]*?(?:Current Speed: (\d+) MHz)?[\s\S]*?(?:Voltage: ([\d.]+ V))?/g; let match; while ((match = socketRegex.exec(dmidecode)) !== null) { cpuSockets.push({ socket: match[1].trim(), model: match[2].trim(), cores: match[3], threads: match[4], maxSpeed: match[5] ? `${match[5]} MHz` : 'N/A', currentSpeed: match[6] ? `${match[6]} MHz` : 'N/A', voltage: match[7] || 'N/A' }); } // Parse CPU signature (Family, Model, Stepping) const signatureRegex = /Signature: Type \d+, Family (\d+), Model (\d+), Stepping (\d+)/; const sigMatch = dmidecode.match(signatureRegex); const cpuSignature = sigMatch ? `Family ${sigMatch[1]}, Model ${sigMatch[2]}, Stepping ${sigMatch[3]}` : 'N/A'; const items = [ { label: 'Fabricant', value: snapshot.cpu_vendor || 'N/A', tooltip: snapshot.cpu_model }, { label: 'Modèle', value: snapshot.cpu_model || 'N/A', tooltip: snapshot.cpu_microarchitecture ? `Architecture: ${snapshot.cpu_microarchitecture}` : null }, { label: 'Signature CPU', value: cpuSignature }, { label: 'Socket', value: cpuSockets.length > 0 ? cpuSockets[0].socket : 'N/A' }, { label: 'Famille', value: snapshot.cpu_vendor || 'N/A' }, { label: 'Microarchitecture', value: snapshot.cpu_microarchitecture || 'N/A' }, { label: 'Cores', value: snapshot.cpu_cores != null ? snapshot.cpu_cores : 'N/A', tooltip: snapshot.cpu_threads ? `${snapshot.cpu_threads} threads disponibles` : null }, { label: 'Threads', value: snapshot.cpu_threads != null ? snapshot.cpu_threads : 'N/A', tooltip: snapshot.cpu_cores ? `${snapshot.cpu_cores} cores physiques` : null }, { label: 'Fréquence de base', value: snapshot.cpu_base_freq_ghz ? `${snapshot.cpu_base_freq_ghz} GHz` : 'N/A', tooltip: snapshot.cpu_max_freq_ghz ? `Max: ${snapshot.cpu_max_freq_ghz} GHz` : null }, { label: 'Fréquence maximale', value: snapshot.cpu_max_freq_ghz ? `${snapshot.cpu_max_freq_ghz} GHz` : (cpuSockets.length > 0 && cpuSockets[0].maxSpeed !== 'N/A' ? cpuSockets[0].maxSpeed : 'N/A') }, { label: 'Fréquence actuelle', value: cpuSockets.length > 0 && cpuSockets[0].currentSpeed !== 'N/A' ? cpuSockets[0].currentSpeed : 'N/A' }, { label: 'Tension', value: cpuSockets.length > 0 ? cpuSockets[0].voltage : 'N/A' }, { label: 'TDP', value: snapshot.cpu_tdp_w ? `${snapshot.cpu_tdp_w} W` : 'N/A', tooltip: 'Thermal Design Power - Consommation thermique typique' }, { label: 'Cache L1', value: snapshot.cpu_cache_l1_kb ? utils.formatCache(snapshot.cpu_cache_l1_kb) : 'N/A', tooltip: 'Cache de niveau 1 - Le plus rapide' }, { label: 'Cache L2', value: snapshot.cpu_cache_l2_kb ? utils.formatCache(snapshot.cpu_cache_l2_kb) : 'N/A', tooltip: 'Cache de niveau 2 - Intermédiaire' }, { label: 'Cache L3', value: snapshot.cpu_cache_l3_kb ? utils.formatCache(snapshot.cpu_cache_l3_kb) : 'N/A', tooltip: 'Cache de niveau 3 - Partagé entre les cores' } ]; let html = `
${items.map(item => `
${item.label}
${utils.escapeHtml(String(item.value))}
`).join('')}
`; // Multi-CPU grid if (cpuSockets.length > 1) { html += `
Configuration multi-CPU (${cpuSockets.length} sockets)
${cpuSockets.map((cpu, idx) => ` `).join('')}
Socket Modèle Cores Threads Fréq. Max Fréq. Actuelle Tension
${utils.escapeHtml(cpu.socket)} ${utils.escapeHtml(cpu.model)} ${cpu.cores} ${cpu.threads} ${cpu.maxSpeed} ${cpu.currentSpeed} ${cpu.voltage}
`; } // CPU flags if (snapshot.cpu_flags) { let flags = snapshot.cpu_flags; if (typeof flags === 'string') { try { flags = JSON.parse(flags); } catch (error) { flags = flags.split(',').map(flag => flag.trim()).filter(Boolean); } } if (!Array.isArray(flags)) { flags = []; } const limitedFlags = flags.slice(0, 20); // Limit to 20 html += `
Extensions CPU (${flags.length} total)
${limitedFlags.map(flag => ` ${utils.escapeHtml(flag)} `).join('')} ${flags.length > 20 ? `+${flags.length - 20} autres...` : ''}
`; } return html; } // ======================= // MEMORY SECTION // ======================= function renderMemoryDetails(snapshot, deviceData) { if (!snapshot) return noData(); // Parse RAM layout const ramLayout = snapshot.ram_layout_json ? (typeof snapshot.ram_layout_json === 'string' ? JSON.parse(snapshot.ram_layout_json) : snapshot.ram_layout_json) : []; const slotsUsed = ramLayout.filter(slot => slot.size_mb > 0).length; const slotsTotal = snapshot.ram_slots_total || ramLayout.length || 0; // ECC detection const hasECC = ramLayout.some(slot => slot.type_detail && slot.type_detail.toLowerCase().includes('ecc')); // RAM bars data const ramTotal = snapshot.ram_total_mb || 0; const ramFree = snapshot.ram_free_mb || 0; const ramUsed = ramTotal - ramFree; const ramShared = snapshot.ram_shared_mb || 0; const ramUsedPercent = ramTotal > 0 ? Math.round((ramUsed / ramTotal) * 100) : 0; const swapTotal = snapshot.swap_total_mb || 0; const swapUsed = snapshot.swap_used_mb || 0; const swapPercent = swapTotal > 0 ? Math.round((swapUsed / swapTotal) * 100) : 0; const cards = [ { label: 'Capacité max carte mère', value: snapshot.ram_max_capacity_mb ? `${Math.round(snapshot.ram_max_capacity_mb / 1024)} GB` : 'N/A' }, { label: 'RAM Totale', value: utils.formatStorage(ramTotal, 'MB') }, { label: 'RAM Libre', value: utils.formatStorage(ramFree, 'MB') }, { label: 'RAM Utilisée', value: utils.formatStorage(ramUsed, 'MB') }, { label: 'RAM Partagée', value: utils.formatStorage(ramShared, 'MB') }, { label: 'Slots utilisés / total', value: `${slotsUsed} / ${slotsTotal}` }, { label: 'ECC', value: hasECC ? 'Oui' : 'Non' } ]; let html = `
${cards.map(card => `
${card.label}
${card.value}
`).join('')}
`; // RAM bar html += `
RAM (${utils.formatStorage(ramTotal, 'MB')}) ${ramUsedPercent}% utilisée
▮ Utilisée: ${utils.formatStorage(ramUsed, 'MB')} ▯ Disponible: ${utils.formatStorage(ramFree, 'MB')}
`; // SWAP bar if (swapTotal > 0) { html += `
SWAP (${utils.formatStorage(swapTotal, 'MB')}) ${swapPercent}% utilisé
▮ Utilisé: ${utils.formatStorage(swapUsed, 'MB')} | ▯ Libre: ${utils.formatStorage(swapTotal - swapUsed, 'MB')}
`; } // Memory slots if (ramLayout && ramLayout.length > 0) { html += `
`; ramLayout.forEach((slot, idx) => { const slotName = slot.slot || slot.locator || `DIMM${idx}`; const sizeMB = slot.size_mb || 0; const sizeGB = sizeMB > 0 ? Math.round(sizeMB / 1024) : 0; const status = sizeMB > 0 ? 'occupé' : 'libre'; const type = slot.type || 'N/A'; const speed = slot.speed_mhz ? `${slot.speed_mhz} MT/s` : 'N/A'; const typeDetail = slot.type_detail || 'N/A'; const formFactor = slot.form_factor || 'N/A'; const voltage = slot.voltage_v ? `${slot.voltage_v} V` : 'N/A'; const manufacturer = slot.manufacturer || 'N/A'; const serialNumber = slot.serial_number || 'N/A'; const partNumber = slot.part_number || 'N/A'; html += `
${utils.escapeHtml(slotName)} ${sizeGB > 0 ? sizeGB + 'GB' : ''} ${status}
${sizeMB > 0 ? `
${utils.escapeHtml(type)} ${utils.escapeHtml(speed)} | ${utils.escapeHtml(typeDetail)}
${utils.escapeHtml(formFactor)} | ${utils.escapeHtml(voltage)} | ${utils.escapeHtml(manufacturer)}
SN: ${utils.escapeHtml(serialNumber)}
PN: ${utils.escapeHtml(partNumber)}
` : ''}
`; }); html += `
`; } return html; } // ======================= // STORAGE SECTION // ======================= function renderStorageDetails(snapshot) { if (!snapshot) return noData(); const storageDevices = snapshot.storage_devices_json ? (typeof snapshot.storage_devices_json === 'string' ? JSON.parse(snapshot.storage_devices_json) : snapshot.storage_devices_json) : []; if (!storageDevices || storageDevices.length === 0) { return noData('Aucun périphérique de stockage détecté'); } return storageDevices.map(device => { const name = device.name || 'N/A'; const model = device.model || 'N/A'; const size = device.size_gb ? `${device.size_gb} GB` : 'N/A'; const type = device.type || 'N/A'; const smart = device.smart_status || 'N/A'; const temp = device.temperature_c != null ? `${device.temperature_c}°C` : 'N/A'; const smartColor = smart.toLowerCase().includes('passed') || smart.toLowerCase().includes('ok') ? 'var(--color-success)' : smart.toLowerCase().includes('fail') ? 'var(--color-danger)' : 'var(--text-secondary)'; return `
${type.toLowerCase().includes('ssd') ? '💾' : '🗄️'}
${utils.escapeHtml(name)}
${utils.escapeHtml(model)}
Capacité
${size}
Type
${utils.escapeHtml(type)}
SMART
${utils.escapeHtml(smart)}
Température
${temp}
`; }).join(''); } // ======================= // GPU SECTION // ======================= function renderGPUDetails(snapshot) { if (!snapshot) return noData(); const items = [ { label: 'Fabricant', value: snapshot.gpu_vendor || 'N/A' }, { label: 'Modèle', value: snapshot.gpu_model || 'N/A' }, { label: 'VRAM', value: snapshot.gpu_vram_mb ? `${snapshot.gpu_vram_mb} MB` : 'N/A' }, { label: 'Driver', value: snapshot.gpu_driver || 'N/A' } ]; return `
${items.map(item => `
${item.label}
${utils.escapeHtml(String(item.value))}
`).join('')}
`; } // ======================= // NETWORK SECTION // ======================= function renderNetworkDetails(snapshot) { if (!snapshot) return noData(); const networkInterfaces = snapshot.network_interfaces_json ? (typeof snapshot.network_interfaces_json === 'string' ? JSON.parse(snapshot.network_interfaces_json) : snapshot.network_interfaces_json) : []; if (!networkInterfaces || networkInterfaces.length === 0) { return noData('Aucune interface réseau détectée'); } return networkInterfaces.map(iface => { const name = iface.name || 'N/A'; const ipv4 = iface.ipv4 || 'N/A'; const ipv6 = iface.ipv6 || 'N/A'; const mac = iface.mac || 'N/A'; const speed = iface.speed_mbps ? `${iface.speed_mbps} Mbps` : 'N/A'; const status = iface.status || 'N/A'; const statusColor = status.toLowerCase().includes('up') ? 'var(--color-success)' : status.toLowerCase().includes('down') ? 'var(--color-danger)' : 'var(--text-secondary)'; return `
🌐
${utils.escapeHtml(name)}
${utils.escapeHtml(mac)}
${utils.escapeHtml(status)}
IPv4
${utils.escapeHtml(ipv4)}
IPv6
${utils.escapeHtml(ipv6)}
Vitesse
${speed}
`; }).join(''); } // ======================= // OS SECTION // ======================= function renderOSDetails(snapshot) { if (!snapshot) return noData(); const items = [ { label: 'OS', value: snapshot.os_name || 'N/A' }, { label: 'Version', value: snapshot.os_version || 'N/A' }, { label: 'Kernel', value: snapshot.kernel_version || 'N/A' }, { label: 'Architecture', value: snapshot.architecture || 'N/A' }, { label: 'Hostname', value: snapshot.hostname || 'N/A' }, { label: 'Uptime', value: snapshot.uptime_seconds ? utils.formatUptime(snapshot.uptime_seconds) : 'N/A' }, { label: 'Batterie', value: snapshot.battery_percent != null ? `${snapshot.battery_percent}%` : 'N/A' } ]; return `
${items.map(item => `
${item.label}
${utils.escapeHtml(String(item.value))}
`).join('')}
`; } // ======================= // PROXMOX SECTION // ======================= function renderProxmoxDetails(snapshot) { if (!snapshot) return noData(); const isProxmoxHost = snapshot.is_proxmox_host; const isProxmoxGuest = snapshot.is_proxmox_guest; const proxmoxVersion = snapshot.proxmox_version; if (!isProxmoxHost && !isProxmoxGuest) { return noData('Non détecté comme hôte ou invité Proxmox'); } const items = [ { label: 'Type', value: isProxmoxHost ? 'Hôte Proxmox' : 'Invité Proxmox' }, { label: 'Version', value: proxmoxVersion || 'N/A' } ]; return `
🔧
Proxmox VE détecté
${items.map(item => `
${item.label}
${utils.escapeHtml(String(item.value))}
`).join('')}
`; } // ======================= // AUDIO SECTION // ======================= function renderAudioDetails(snapshot) { if (!snapshot) return noData(); const audioHardware = snapshot.audio_hardware_json ? (typeof snapshot.audio_hardware_json === 'string' ? JSON.parse(snapshot.audio_hardware_json) : snapshot.audio_hardware_json) : null; const audioSoftware = snapshot.audio_software_json ? (typeof snapshot.audio_software_json === 'string' ? JSON.parse(snapshot.audio_software_json) : snapshot.audio_software_json) : null; if (!audioHardware && !audioSoftware) { return noData('Aucune information audio disponible'); } let html = ''; // Hardware section if (audioHardware && Array.isArray(audioHardware) && audioHardware.length > 0) { html += `
🔊 Matériel Audio
${audioHardware.map(device => `
${utils.escapeHtml(device.name || 'N/A')}
${utils.escapeHtml(device.driver || 'N/A')}
`).join('')}
`; } // Software section if (audioSoftware) { html += `
🎵 Logiciels Audio
${Object.entries(audioSoftware).map(([key, value]) => `
${utils.escapeHtml(key)}
${utils.escapeHtml(String(value))}
`).join('')}
`; } return html; } // ======================= // EXPORT PUBLIC API // ======================= window.HardwareRenderer = { renderMotherboardDetails, renderCPUDetails, renderMemoryDetails, renderStorageDetails, renderGPUDetails, renderNetworkDetails, renderOSDetails, renderProxmoxDetails, renderAudioDetails }; })();