package inspur import ( "encoding/csv" "strings" "time" "git.mchus.pro/mchus/logpile/internal/models" ) // ParseSELList parses selelist.csv file with SEL events // Format: ID, Date (MM/DD/YYYY), Time (HH:MM:SS), Sensor, Event, Status // Example: 1,04/18/2025,09:31:18,Event Logging Disabled SEL_Status,Log area reset/cleared,Asserted func ParseSELList(content []byte) []models.Event { var events []models.Event text := string(content) lines := strings.Split(text, "\n") // Skip header line(s) if present startIdx := 0 for i, line := range lines { if strings.Contains(strings.ToLower(line), "sel elist") { startIdx = i + 1 break } } // Parse CSV data for i := startIdx; i < len(lines); i++ { line := strings.TrimSpace(lines[i]) if line == "" { continue } // Parse CSV line r := csv.NewReader(strings.NewReader(line)) records, err := r.Read() if err != nil || len(records) < 6 { continue } eventID := strings.TrimSpace(records[0]) dateStr := strings.TrimSpace(records[1]) timeStr := strings.TrimSpace(records[2]) sensorStr := strings.TrimSpace(records[3]) eventDesc := strings.TrimSpace(records[4]) status := strings.TrimSpace(records[5]) // Parse timestamp: MM/DD/YYYY HH:MM:SS timestamp := parseSELTimestamp(dateStr, timeStr) // Extract sensor type and name sensorType, sensorName := parseSensorInfo(sensorStr) // Determine severity severity := determineSELSeverity(sensorStr, eventDesc, status) // Build full description description := buildSELDescription(eventDesc, status) events = append(events, models.Event{ ID: eventID, Timestamp: timestamp, Source: "SEL", SensorType: sensorType, SensorName: sensorName, EventType: eventDesc, Severity: severity, Description: description, RawData: line, }) } return events } // parseSELTimestamp parses MM/DD/YYYY and HH:MM:SS into time.Time func parseSELTimestamp(dateStr, timeStr string) time.Time { // Combine date and time: MM/DD/YYYY HH:MM:SS timestampStr := dateStr + " " + timeStr // Try parsing with MM/DD/YYYY format t, err := time.Parse("01/02/2006 15:04:05", timestampStr) if err != nil { // Fallback to current time return time.Now() } return t } // parseSensorInfo extracts sensor type and name from sensor string // Example: "Event Logging Disabled SEL_Status" -> ("sel", "SEL_Status") // Example: "Power Supply PSU0_Status" -> ("power_supply", "PSU0_Status") func parseSensorInfo(sensorStr string) (sensorType, sensorName string) { parts := strings.Fields(sensorStr) if len(parts) == 0 { return "unknown", sensorStr } // Last part is usually the sensor name sensorName = parts[len(parts)-1] // First parts form the sensor type if len(parts) > 1 { sensorType = strings.ToLower(strings.Join(parts[:len(parts)-1], "_")) } else { sensorType = "system" } return } // determineSELSeverity determines event severity based on sensor and event description func determineSELSeverity(sensorStr, eventDesc, status string) models.Severity { lowerSensor := strings.ToLower(sensorStr) lowerEvent := strings.ToLower(eventDesc) lowerStatus := strings.ToLower(status) // Critical indicators criticalKeywords := []string{ "critical", "failure", "fault", "error", "ac lost", "predictive failure", "redundancy lost", "going high", "going low", "transition to critical", } for _, keyword := range criticalKeywords { if strings.Contains(lowerSensor, keyword) || strings.Contains(lowerEvent, keyword) || strings.Contains(lowerStatus, keyword) { return models.SeverityCritical } } // Warning indicators warningKeywords := []string{ "warning", "disabled", "non-recoverable", "device removed", "device absent", } for _, keyword := range warningKeywords { if strings.Contains(lowerSensor, keyword) || strings.Contains(lowerEvent, keyword) || strings.Contains(lowerStatus, keyword) { return models.SeverityWarning } } // Info indicators (normal operations) infoKeywords := []string{ "presence detected", "device present", "asserted", "initiated by", "state asserted", "s0/g0: working", "power button pressed", } for _, keyword := range infoKeywords { if strings.Contains(lowerEvent, keyword) || strings.Contains(lowerStatus, keyword) { return models.SeverityInfo } } // Default to info return models.SeverityInfo } // buildSELDescription builds human-readable description func buildSELDescription(eventDesc, status string) string { if status == "Asserted" || status == "Deasserted" { return eventDesc } return eventDesc + " (" + status + ")" }