feat(migration): upgrade legacy ps1 scripts to single-binary Golang platform
This commit is contained in:
124
pkg/monitor/statemachine.go
Normal file
124
pkg/monitor/statemachine.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"smart-shutdown/pkg/config"
|
||||
"smart-shutdown/pkg/logger"
|
||||
"smart-shutdown/pkg/pinger"
|
||||
"smart-shutdown/pkg/system"
|
||||
)
|
||||
|
||||
// Run 开启核心状态机循环监控,接收 ctx 以便主程序或者服务管理器控制安全退出
|
||||
func Run(ctx context.Context, cfg *config.Config) {
|
||||
logger.Info("========== 智能关机监控已启动 ==========")
|
||||
logger.Info("监控目标: %s", cfg.TargetIP)
|
||||
logger.Info("监控窗口: %d秒", cfg.MonitorWindowSeconds)
|
||||
logger.Info("关机倒计时: %d秒", cfg.ShutdownCountdown)
|
||||
|
||||
var failureStartTime time.Time
|
||||
var inFailureWindow bool = false
|
||||
|
||||
normalStatusCounter := 0
|
||||
// 每 24 次正常检测输出一次日志(约 6 分钟)
|
||||
const normalStatusLogInterval = 24
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
logger.Info("========== 收到停止信号,退出监控 ==========")
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
isOnline := pinger.Ping(cfg.TargetIP, 3) // 3 秒超时探测一次
|
||||
|
||||
if isOnline {
|
||||
if inFailureWindow {
|
||||
logger.Succ("网络连接已恢复!重置断网计时器。")
|
||||
inFailureWindow = false
|
||||
normalStatusCounter = 0
|
||||
} else {
|
||||
normalStatusCounter++
|
||||
if normalStatusCounter >= normalStatusLogInterval {
|
||||
logger.Info("网络连接持续正常(已检测 %d 次)", normalStatusCounter)
|
||||
normalStatusCounter = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 正常状态下休眠等待下一次探测
|
||||
time.Sleep(time.Duration(cfg.NormalPingInterval) * time.Second)
|
||||
|
||||
} else {
|
||||
logger.Fail("Ping 测试失败 - 目标: %s", cfg.TargetIP)
|
||||
|
||||
if !inFailureWindow {
|
||||
// 首次断网
|
||||
failureStartTime = time.Now()
|
||||
inFailureWindow = true
|
||||
logger.Warn("网络首次中断,开始进入监控窗口计时。")
|
||||
}
|
||||
|
||||
// 计算当前断网累计时间
|
||||
failureDuration := time.Since(failureStartTime).Seconds()
|
||||
|
||||
if int(failureDuration) >= cfg.MonitorWindowSeconds {
|
||||
logger.Crit("网络持续中断已超过 %d 秒,触发关机流程!", cfg.MonitorWindowSeconds)
|
||||
|
||||
// 进入倒计时阶段,倒计时期间高频检测(阻塞调用)
|
||||
shutdownCancelled := startCountdown(ctx, cfg.TargetIP, cfg.ShutdownCountdown)
|
||||
|
||||
if !shutdownCancelled {
|
||||
logger.Crit("倒计时结束,执行最终关机!")
|
||||
system.ExecuteShutdown()
|
||||
// 关机指令发出,理论上系统即将终止进程,为了优雅,此处也退出
|
||||
return
|
||||
} else {
|
||||
// 倒计时中途网络恢复被取消了,重置状态重新进入监控环节
|
||||
inFailureWindow = false
|
||||
}
|
||||
} else {
|
||||
// 还在容忍窗口期内,仅仅打印日志并休眠
|
||||
logger.Info("网络已持续中断 %.0f / %d 秒,继续监控...", failureDuration, cfg.MonitorWindowSeconds)
|
||||
time.Sleep(time.Duration(cfg.NormalPingInterval) * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// startCountdown 执行关机前倒计时的轮询,如果中途恢复网络返回 true (代表取消关机)。
|
||||
// 返回 false 代表断网到底,坚决关机。
|
||||
func startCountdown(ctx context.Context, targetIP string, countdownSec int) bool {
|
||||
logger.Warn("============================================")
|
||||
logger.Warn("准备关机!倒计时 %d 秒期间将快速探测网络...", countdownSec)
|
||||
logger.Warn("============================================")
|
||||
|
||||
endTime := time.Now().Add(time.Duration(countdownSec) * time.Second)
|
||||
// 倒计时期间提高探测频率:每 3 秒一次
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
remaining := int(time.Until(endTime).Seconds())
|
||||
if remaining <= 0 {
|
||||
return false // 时间到,确认无法恢复
|
||||
}
|
||||
|
||||
logger.Warn("距离关机还有 %d 秒... 正在高频检测网络", remaining)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
logger.Info("收到退出系统服务信号,中止倒计时关机!")
|
||||
return true
|
||||
case <-ticker.C:
|
||||
// 高频重试检测网络
|
||||
if pinger.Ping(targetIP, 3) {
|
||||
logger.Succ("============================================")
|
||||
logger.Succ("网络在倒计时期间奇迹般地恢复了!立刻取消关机!")
|
||||
logger.Succ("============================================")
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user