186 lines
4.7 KiB
Go
186 lines
4.7 KiB
Go
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)
|
|
}
|