package miner import ( "sync" "time" "crypto-miner-agent/config" "crypto-miner-agent/stats" ) type ScheduleGuard struct { cfg config.RuntimeConfig reporter *stats.Reporter mu sync.Mutex idleSince time.Time idleReady bool } func NewScheduleGuard(cfg config.RuntimeConfig, reporter *stats.Reporter) *ScheduleGuard { return &ScheduleGuard{ cfg: cfg, reporter: reporter, } } func (g *ScheduleGuard) UpdateConfig(cfg config.RuntimeConfig) { g.mu.Lock() g.cfg = cfg g.idleSince = time.Time{} g.idleReady = false g.mu.Unlock() } func (g *ScheduleGuard) Allowed() bool { return g.allowedAt(time.Now()) } func (g *ScheduleGuard) allowedAt(now time.Time) bool { switch g.cfg.MiningModeNormalized() { case "idle": return g.idleAllowed() case "scheduled", "schedule": return g.cfg.InScheduleWindow(now) default: return true } } func (g *ScheduleGuard) idleAllowed() bool { cpu := g.reporter.SystemCPUPercent() threshold := float64(g.cfg.IdleThresholdPct) if threshold <= 0 { threshold = 20 } duration := time.Duration(g.cfg.IdleDurationMinutes) * time.Minute if duration <= 0 { duration = 5 * time.Minute } g.mu.Lock() defer g.mu.Unlock() if cpu == 0 { // First sample has no delta yet; treat as not idle. g.idleSince = time.Time{} g.idleReady = false return false } if cpu <= threshold { if g.idleSince.IsZero() { g.idleSince = time.Now() } if time.Since(g.idleSince) >= duration { g.idleReady = true } } else { g.idleSince = time.Time{} g.idleReady = false } return g.idleReady }