package inspur import ( "encoding/json" "fmt" "strings" "git.mchus.pro/mchus/logpile/internal/models" ) // PCIeRESTInfo represents the RESTful PCIE Device info structure type PCIeRESTInfo []struct { ID int `json:"id"` Present int `json:"present"` Enable int `json:"enable"` Status int `json:"status"` VendorID int `json:"vendor_id"` VendorName string `json:"vendor_name"` DeviceID int `json:"device_id"` DeviceName string `json:"device_name"` BusNum int `json:"bus_num"` DevNum int `json:"dev_num"` FuncNum int `json:"func_num"` MaxLinkWidth int `json:"max_link_width"` MaxLinkSpeed int `json:"max_link_speed"` CurrentLinkWidth int `json:"current_link_width"` CurrentLinkSpeed int `json:"current_link_speed"` Slot int `json:"slot"` Location string `json:"location"` DeviceLocator string `json:"DeviceLocator"` DevType int `json:"dev_type"` DevSubtype int `json:"dev_subtype"` PartNum string `json:"part_num"` SerialNum string `json:"serial_num"` FwVer string `json:"fw_ver"` } // ParsePCIeDevices parses RESTful PCIE Device info from devicefrusdr.log func ParsePCIeDevices(content []byte) []models.PCIeDevice { text := string(content) // Find RESTful PCIE Device info section startMarker := "RESTful PCIE Device info:" endMarker := "BMC sdr Info:" startIdx := strings.Index(text, startMarker) if startIdx == -1 { return nil } endIdx := strings.Index(text[startIdx:], endMarker) if endIdx == -1 { endIdx = len(text) - startIdx } jsonText := text[startIdx+len(startMarker) : startIdx+endIdx] jsonText = strings.TrimSpace(jsonText) var pcieInfo PCIeRESTInfo if err := json.Unmarshal([]byte(jsonText), &pcieInfo); err != nil { return nil } var devices []models.PCIeDevice for _, pcie := range pcieInfo { if pcie.Present != 1 { continue } // Convert PCIe speed to GEN notation maxSpeed := fmt.Sprintf("GEN%d", pcie.MaxLinkSpeed) currentSpeed := fmt.Sprintf("GEN%d", pcie.CurrentLinkSpeed) // Determine device class based on dev_type deviceClass := determineDeviceClass(pcie.DevType, pcie.DevSubtype, pcie.DeviceName) // Build BDF string bdf := fmt.Sprintf("%04x/%02x/%02x/%02x", 0, pcie.BusNum, pcie.DevNum, pcie.FuncNum) device := models.PCIeDevice{ Slot: pcie.Location, VendorID: pcie.VendorID, DeviceID: pcie.DeviceID, BDF: bdf, DeviceClass: deviceClass, Manufacturer: pcie.VendorName, LinkWidth: pcie.CurrentLinkWidth, LinkSpeed: currentSpeed, MaxLinkWidth: pcie.MaxLinkWidth, MaxLinkSpeed: maxSpeed, PartNumber: strings.TrimSpace(pcie.PartNum), SerialNumber: strings.TrimSpace(pcie.SerialNum), } devices = append(devices, device) } return devices } // determineDeviceClass maps device type to human-readable class func determineDeviceClass(devType, devSubtype int, deviceName string) string { // dev_type mapping: // 1 = Mass Storage Controller // 2 = Network Controller // 3 = Display Controller (GPU) // 4 = Multimedia Controller switch devType { case 1: if devSubtype == 4 { return "RAID Controller" } return "Storage Controller" case 2: return "Network Controller" case 3: // GPU if strings.Contains(strings.ToUpper(deviceName), "H100") { return "GPU (H100)" } if strings.Contains(strings.ToUpper(deviceName), "A100") { return "GPU (A100)" } if strings.Contains(strings.ToUpper(deviceName), "NVIDIA") { return "GPU" } return "Display Controller" case 4: return "Multimedia Controller" default: return "Unknown" } } // ParseGPUs extracts GPU data from PCIe devices and sensors func ParseGPUs(pcieDevices []models.PCIeDevice, sensors []models.SensorReading) []models.GPU { var gpus []models.GPU // Find GPU devices for _, pcie := range pcieDevices { if !strings.Contains(strings.ToLower(pcie.DeviceClass), "gpu") && !strings.Contains(strings.ToLower(pcie.DeviceClass), "display") { continue } // Skip integrated graphics (ASPEED, etc.) if strings.Contains(pcie.Manufacturer, "ASPEED") { continue } gpu := models.GPU{ Slot: pcie.Slot, Location: pcie.Slot, Model: pcie.DeviceClass, Manufacturer: pcie.Manufacturer, SerialNumber: pcie.SerialNumber, MaxLinkWidth: pcie.MaxLinkWidth, MaxLinkSpeed: pcie.MaxLinkSpeed, CurrentLinkWidth: pcie.LinkWidth, CurrentLinkSpeed: pcie.LinkSpeed, Status: "OK", } // Extract GPU number from slot name (e.g., "PCIE7" -> 7) slotNum := extractSlotNumber(pcie.Slot) // Find temperature sensors for this GPU for _, sensor := range sensors { sensorName := strings.ToUpper(sensor.Name) // Match GPU temperature sensor (e.g., "GPU7_Temp") if strings.Contains(sensorName, fmt.Sprintf("GPU%d_TEMP", slotNum)) { if sensor.RawValue != "" { fmt.Sscanf(sensor.RawValue, "%d", &gpu.Temperature) } } // Match GPU memory temperature (e.g., "GPU7_Mem_Temp") if strings.Contains(sensorName, fmt.Sprintf("GPU%d_MEM_TEMP", slotNum)) { if sensor.RawValue != "" { fmt.Sscanf(sensor.RawValue, "%d", &gpu.MemTemperature) } } // Match PCIe slot temperature (e.g., "PCIE7_GPU_TLM_T") if strings.Contains(sensorName, fmt.Sprintf("PCIE%d_GPU_TLM_T", slotNum)) { if sensor.RawValue != "" && gpu.Temperature == 0 { fmt.Sscanf(sensor.RawValue, "%d", &gpu.Temperature) } } } gpus = append(gpus, gpu) } return gpus } // extractSlotNumber extracts slot number from location string // e.g., "CPU0_PE3_AC_PCIE7" -> 7 func extractSlotNumber(location string) int { parts := strings.Split(location, "_") for _, part := range parts { if strings.HasPrefix(part, "PCIE") || strings.HasPrefix(part, "#CPU") { var num int fmt.Sscanf(part, "PCIE%d", &num) if num > 0 { return num } } } return 0 }