Autostart-Funktionalität implementiert: Crontab-Syntax, Scheduler und UI-Integration
This commit is contained in:
174
internal/scheduler/scheduler.go
Normal file
174
internal/scheduler/scheduler.go
Normal file
@@ -0,0 +1,174 @@
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user