Add detailed hardware configuration view with sub-tabs

- Redesign config page with tabs: Spec, CPU, Memory, Power, Storage, GPU, Network, PCIe
- Parse detailed memory info from component.log with all fields:
  Location, Present, Size, Type, Max/Current Speed, Manufacturer, Part Number, Status
- Add GPU model extraction from PCIe devices
- Add NetworkAdapter model with detailed fields from RESTful API
- Update PSU model with power metrics (input/output power, voltage, temperature)
- Memory modules with 0GB size (failed) highlighted in warning color
- Add memory overview stats (total GB, installed count, active count)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 09:11:44 +03:00
parent 24b722df6c
commit c243b4e141
6 changed files with 689 additions and 95 deletions

View File

@@ -125,18 +125,24 @@ func ParseAssetJSON(content []byte) (*models.HardwareConfig, error) {
})
}
// Parse memory info
// Memory info is parsed from component.log (RESTful Memory info) which has more details
// Only use asset.json memory data as fallback if component.log is not available
if len(asset.MemInfo.MemCommonInfo) > 0 {
common := asset.MemInfo.MemCommonInfo[0]
for i, dimm := range asset.MemInfo.DimmInfo {
slot := fmt.Sprintf("DIMM%d", i)
config.Memory = append(config.Memory, models.MemoryDIMM{
Slot: i,
SizeMB: common.PhysicalSize * 1024,
Type: memoryTypeToString(common.MemoryType),
SpeedMHz: common.CurrentSpeed,
Manufacturer: common.Manufacturer,
SerialNumber: dimm.SerialNumber,
PartNumber: strings.TrimSpace(dimm.PartNumber),
Slot: slot,
Location: slot,
Present: true,
SizeMB: common.PhysicalSize * 1024,
Type: memoryTypeToString(common.MemoryType),
MaxSpeedMHz: common.MaxSpeed,
CurrentSpeedMHz: common.CurrentSpeed,
Manufacturer: common.Manufacturer,
SerialNumber: dimm.SerialNumber,
PartNumber: strings.TrimSpace(dimm.PartNumber),
Ranks: common.Rank,
})
}
}
@@ -195,6 +201,31 @@ func ParseAssetJSON(content []byte) (*models.HardwareConfig, error) {
device.DeviceClass = deviceName
}
config.PCIeDevices = append(config.PCIeDevices, device)
// Extract GPUs (class 3 = display controller)
if pcie.ClassCode == 3 {
gpuModel := deviceName
if gpuModel == "" {
gpuModel = pcieClassToString(pcie.ClassCode, pcie.SubClassCode)
}
gpu := models.GPU{
Slot: pcie.LocString,
Model: gpuModel,
Manufacturer: vendor,
VendorID: pcie.VendorId,
DeviceID: pcie.DeviceId,
BDF: formatBDF(pcie.BusNumber, pcie.DeviceNumber, pcie.FunctionNumber),
LinkWidth: pcie.NegotiatedLinkWidth,
LinkSpeed: pcieLinkSpeedToString(pcie.CurrentLinkSpeed),
}
if pcie.PartNumber != nil {
gpu.PartNumber = strings.TrimSpace(*pcie.PartNumber)
}
if pcie.SerialNumber != nil {
gpu.SerialNumber = strings.TrimSpace(*pcie.SerialNumber)
}
config.GPUs = append(config.GPUs, gpu)
}
}
return config, nil