package scheduler import ( "log" "medi-wol/internal/database" "medi-wol/internal/models" "medi-wol/internal/wol" "strconv" "strings" "time" ) // Scheduler verwaltet geplante WOL-Ereignisse type Scheduler struct { db *database.DB wolService *wol.Service ticker *time.Ticker stopChan chan bool } // NewScheduler erstellt einen neuen Scheduler func NewScheduler(db *database.DB, wolService *wol.Service) *Scheduler { return &Scheduler{ db: db, wolService: wolService, stopChan: make(chan bool), } } // Start startet den Scheduler func (s *Scheduler) Start() { s.ticker = time.NewTicker(1 * time.Minute) // Jede Minute prüfen go s.run() log.Println("Autostart-Scheduler gestartet") } // Stop stoppt den Scheduler func (s *Scheduler) Stop() { if s.ticker != nil { s.ticker.Stop() } close(s.stopChan) log.Println("Autostart-Scheduler gestoppt") } // run ist die Hauptschleife des Schedulers func (s *Scheduler) run() { for { select { case <-s.ticker.C: s.checkAndExecuteScheduledTasks() case <-s.stopChan: return } } } // checkAndExecuteScheduledTasks prüft alle geplanten Aufgaben func (s *Scheduler) checkAndExecuteScheduledTasks() { // Prüfe zuerst, ob der Urlaubsmodus aktiviert ist vacationMode, err := s.db.IsVacationModeEnabled() if err != nil { log.Printf("Fehler beim Prüfen des Urlaubsmodus: %v", err) return } if vacationMode { log.Println("Urlaubsmodus aktiviert - Autostart deaktiviert") return } now := time.Now() // Alle PCs mit aktiviertem Autostart holen pcs, err := s.db.GetPCsWithAutostart() if err != nil { log.Printf("Fehler beim Laden der PCs mit Autostart: %v", err) return } for _, pc := range pcs { if s.shouldExecuteNow(pc.AutostartCron, now) { s.executeWOL(pc) } } } // shouldExecuteNow prüft, ob ein Crontab-Ausdruck jetzt ausgeführt werden soll func (s *Scheduler) shouldExecuteNow(cronExpr string, now time.Time) bool { parts := strings.Fields(cronExpr) if len(parts) != 5 { log.Printf("Ungültiger Crontab-Ausdruck: %s", cronExpr) return false } // Einfache Crontab-Parser-Implementierung // Format: Minute Stunde Tag Monat Wochentag minute := s.parseCronField(parts[0], 0, 59, now.Minute()) hour := s.parseCronField(parts[1], 0, 23, now.Hour()) day := s.parseCronField(parts[2], 1, 31, now.Day()) month := s.parseCronField(parts[3], 1, 12, int(now.Month())) weekday := s.parseCronField(parts[4], 0, 6, int(now.Weekday())) return minute && hour && day && month && weekday } // parseCronField parst ein einzelnes Crontab-Feld func (s *Scheduler) parseCronField(field string, min, max, current int) bool { // Einfache Implementierung für häufige Fälle if field == "*" { return true } // Einzelne Zahl if num, err := strconv.Atoi(field); err == nil { return num == current } // Bereich (z.B. "1-5") if strings.Contains(field, "-") { parts := strings.Split(field, "-") if len(parts) == 2 { start, err1 := strconv.Atoi(parts[0]) end, err2 := strconv.Atoi(parts[1]) if err1 == nil && err2 == nil { return current >= start && current <= end } } } // Wochentag-Namen (z.B. "Mon-Fri") if strings.Contains(field, "-") && (strings.Contains(field, "Mon") || strings.Contains(field, "Tue") || strings.Contains(field, "Wed") || strings.Contains(field, "Thu") || strings.Contains(field, "Fri") || strings.Contains(field, "Sat") || strings.Contains(field, "Sun")) { return s.parseWeekdayRange(field, current) } return false } // parseWeekdayRange parst Wochentag-Bereiche func (s *Scheduler) parseWeekdayRange(field string, current int) bool { // Wochentag-Mapping (0=Sonntag, 1=Montag, ..., 6=Samstag) weekdayMap := map[string]int{ "Sun": 0, "Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6, } if strings.Contains(field, "-") { parts := strings.Split(field, "-") if len(parts) == 2 { start, ok1 := weekdayMap[parts[0]] end, ok2 := weekdayMap[parts[1]] if ok1 && ok2 { // Spezielle Behandlung für Wochentag-Bereiche if start <= end { return current >= start && current <= end } else { // Bereich über Mitternacht (z.B. Fri-Mon) return current >= start || current <= end } } } } return false } // executeWOL führt einen geplanten WOL-Befehl aus func (s *Scheduler) executeWOL(pc models.PC) { log.Printf("Führe geplanten WOL für PC %s (%s) aus", pc.Name, pc.MAC) // WOL-Paket senden err := s.wolService.WakePC(pc.MAC) if err != nil { log.Printf("Fehler beim Senden des WOL-Pakets für PC %s: %v", pc.Name, err) return } // Log-Eintrag erstellen _, logErr := s.db.CreateLog(pc.ID, pc.Name, pc.MAC, "cron") if logErr != nil { log.Printf("Fehler beim Erstellen des Log-Eintrags: %v", logErr) } log.Printf("Geplanter WOL für PC %s erfolgreich ausgeführt", pc.Name) }