first commit

This commit is contained in:
2025-06-06 17:14:08 +08:00
commit 7ed04262c3
6 changed files with 1079 additions and 0 deletions

234
smart_shutdown.ps1 Normal file
View File

@@ -0,0 +1,234 @@
#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
一个智能网络监控脚本当检测到与目标IP的网络持续中断后会自动关闭计算机。
.DESCRIPTION
该脚本会定期测试与指定目标IP的网络连通性。
如果在设定的“监控窗口”时间内连接持续失败,脚本将启动一个可取消的关机倒计时。
如果在倒计时期间网络恢复,关机将被中止。
所有操作都会被记录在日志文件中。
#>
# ==================== 配置参数 ====================
# 监控的目标IP地址
[string]$TargetIP = "192.168.3.3"
# 正常监控时的ping间隔
[int]$NormalPingInterval = 15
# 监控窗口时长(秒) - 在此期间持续失败则触发关机
[int]$MonitorWindowSeconds = 60
# 关机倒计时时长(秒)
[int]$ShutdownCountdown = 60
# 关机倒计时阶段的ping间隔
[int]$CountdownPingInterval = 3
# ping超时时间
[int]$PingTimeout = 3
# 日志配置
[string]$LogDirectory = Join-Path -Path $PSScriptRoot -ChildPath "logs"
[string]$LogFile = Join-Path -Path $LogDirectory -ChildPath "network_monitor_$(Get-Date -Format 'yyyyMMdd').log"
[int]$MaxLogDays = 30
# =================================================
# --- 函数定义 ---
# 日志写入函数
function Write-Log {
param(
[Parameter(Mandatory=$true)]
[ValidateSet("INFO", "SUCCESS", "FAIL", "WARN", "CRITICAL", "DEBUG")]
[string]$Level,
[Parameter(Mandatory=$true)]
[string]$Message
)
$logTimestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$logTimestamp] [$Level] $Message"
# 尝试写入日志文件
try {
if (-not (Test-Path -Path $LogDirectory)) {
New-Item -Path $LogDirectory -ItemType Directory -ErrorAction Stop | Out-Null
}
Add-Content -Path $LogFile -Value $logEntry -ErrorAction Stop
}
catch {
Write-Host "[$logTimestamp] [CRITICAL] 日志文件写入失败!请检查权限或磁盘空间。" -ForegroundColor Red
}
# 根据日志级别在控制台输出不同颜色的信息
switch ($Level) {
"SUCCESS" { Write-Host $logEntry -ForegroundColor Green }
"FAIL" { Write-Host $logEntry -ForegroundColor Red }
"WARN" { Write-Host $logEntry -ForegroundColor Yellow }
"CRITICAL" { Write-Host $logEntry -ForegroundColor DarkRed }
default { Write-Host $logEntry }
}
}
# 获取本机的最佳IP地址
function Get-LocalIP {
param($TargetIP)
try {
# 尝试寻找与目标IP在同一个子网的本机IP地址
$targetSubnet = ($TargetIP.Split('.')[0..2]) -join '.'
$ip = Get-NetIPAddress -AddressFamily IPv4 -AddressState Preferred | Where-Object { $_.IPAddress -like "$targetSubnet.*" } | Select-Object -First 1 -ExpandProperty IPAddress
if ($ip) { return $ip }
# 如果找不到则返回任意一个非环回的私有IP地址
$ip = Get-NetIPAddress -AddressFamily IPv4 -AddressState Preferred | Where-Object { $_.IPAddress -notlike "127.0.0.1" -and $_.InterfaceAlias -notlike "Loopback*" } | Select-Object -First 1 -ExpandProperty IPAddress
return $ip
}
catch {
return "未知"
}
}
# --- 脚本主逻辑 ---
# 清理旧日志
if (Test-Path -Path $LogDirectory) {
Get-ChildItem -Path $LogDirectory -Filter "*.log" | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$MaxLogDays) } | Remove-Item
}
# 初始化
Clear-Host
$localIP = Get-LocalIP -TargetIP $TargetIP
Write-Log -Level "INFO" -Message "========== 脚本启动 =========="
Write-Log -Level "INFO" -Message "本机IP: $localIP"
Write-Log -Level "INFO" -Message "监控目标: $TargetIP"
Write-Log -Level "INFO" -Message "监控窗口: ${MonitorWindowSeconds}"
Write-Log -Level "INFO" -Message "关机倒计时: ${ShutdownCountdown}"
Write-Log -Level "INFO" -Message "日志文件: $LogFile"
Write-Log -Level "INFO" -Message "脚本已启动,按 Ctrl+C 退出..."
Write-Host "========================================"
# 主循环
$failureStartTime = $null
$normalStatusCounter = 0 # 正常状态计数器,用于定期记录正常运行日志
$normalStatusLogInterval = 24 # 每24次正常检测记录一次日志约6分钟
while ($true) {# 测试网络连接
try {
# 使用兼容性更好的ping方法
$pingResult = Test-Connection -ComputerName $TargetIP -Count 1 -Quiet -ErrorAction SilentlyContinue
$isOnline = $pingResult
} catch {
Write-Log -Level "FAIL" -Message "网络测试出现异常: $($_.Exception.Message)"
$isOnline = $false
} if ($isOnline) {
if ($failureStartTime) {
Write-Log -Level "SUCCESS" -Message "网络连接已恢复。"
Write-Host "[OK] 网络连接已恢复,重置监控窗口" -ForegroundColor Green
$failureStartTime = $null # 重置失败计时器
$normalStatusCounter = 0 # 重置正常状态计数器
} else {
Write-Host "[OK] 网络连接正常" -ForegroundColor Green
# 正常连接时减少日志频率,只定期记录
$normalStatusCounter++
if ($normalStatusCounter -ge $normalStatusLogInterval) {
Write-Log -Level "INFO" -Message "网络连接持续正常(已检测 $normalStatusCounter 次)"
$normalStatusCounter = 0 # 重置计数器
}
}
# 只在控制台显示等待信息,不写入日志文件
Write-Host "等待 ${NormalPingInterval}秒后继续监控..." -ForegroundColor Cyan
Start-Sleep -Seconds $NormalPingInterval
} else {
# 如果网络不通
Write-Log -Level "FAIL" -Message "Ping失败 - 目标: $TargetIP"
if (-not $failureStartTime) {
# 记录首次失败的时间
$failureStartTime = Get-Date
Write-Log -Level "WARN" -Message "网络首次中断,开始进入监控窗口计时。"
} # 计算网络已中断的时间
$failureDuration = (New-TimeSpan -Start $failureStartTime -End (Get-Date)).TotalSeconds
Write-Log -Level "INFO" -Message "网络已持续中断 $([math]::Round($failureDuration)) / ${MonitorWindowSeconds} 秒。"
# 显示详细状态
$remainingTime = $MonitorWindowSeconds - [math]::Round($failureDuration)
Write-Host "监控状态 - 已中断: $([math]::Round($failureDuration))/${MonitorWindowSeconds}秒,剩余: ${remainingTime}" -ForegroundColor Yellow
if ($failureDuration -ge $MonitorWindowSeconds) {
# 持续失败时间超过监控窗口,开始关机倒计时
Write-Log -Level "CRITICAL" -Message "网络持续中断已超过 ${MonitorWindowSeconds} 秒,开始关机流程。"
Write-Host "========================================" -ForegroundColor Yellow
$shutdownCancelled = $false
$countdownEndTime = (Get-Date).AddSeconds($ShutdownCountdown)
while ((Get-Date) -lt $countdownEndTime -and -not $shutdownCancelled) {
$remainingSeconds = [math]::Ceiling(($countdownEndTime - (Get-Date)).TotalSeconds)
if ($remainingSeconds -le 0) { break }
Write-Log -Level "WARN" -Message "距离关机还有 $remainingSeconds 秒... 正在快速检测网络。"
Write-Host "----------------------------------------" -ForegroundColor Yellow
Write-Host "距离关机还有 $remainingSeconds 秒..." -ForegroundColor Yellow
Write-Host "正在快速检测网络连接... 按任意键可手动取消关机" -ForegroundColor Yellow # 在倒计时中再次检测网络
try {
if (Test-Connection -ComputerName $TargetIP -Count 1 -Quiet -ErrorAction SilentlyContinue) {
Write-Log -Level "SUCCESS" -Message "网络在倒计时期间恢复!取消关机。"
Write-Host "========================================" -ForegroundColor Green
Write-Host "[OK] 网络连接已恢复!取消关机操作" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
$failureStartTime = $null
$shutdownCancelled = $true
break
} else {
Write-Host "[X] 网络仍然中断" -ForegroundColor Red
}
}
catch {
Write-Log -Level "WARN" -Message "倒计时期间网络测试出现异常: $($_.Exception.Message)"
Write-Host "[X] 网络测试异常" -ForegroundColor Red
}
# 检查是否有按键输入(手动取消)
$timeout = $CountdownPingInterval
$startTime = Get-Date
while (((Get-Date) - $startTime).TotalSeconds -lt $timeout -and -not $shutdownCancelled) {
if ([Console]::KeyAvailable) {
$null = [Console]::ReadKey($true) # 读取按键但不使用
Write-Log -Level "WARN" -Message "用户手动取消了关机操作" Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "[INFO] 用户已手动取消关机,返回监控模式" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
$failureStartTime = $null
$shutdownCancelled = $true
break }
Start-Sleep -Milliseconds 100
}
}
if (-not $shutdownCancelled) {
Write-Log -Level "CRITICAL" -Message "关机倒计时完成,执行系统关机命令。"
try {
# 取消注释下一行以启用实际关机
# Stop-Computer -Force
Write-Host "模拟关机Stop-Computer -Force (为防止意外,此行已注释)" -ForegroundColor Red
Write-Log -Level "CRITICAL" -Message "关机命令已执行(模拟模式)。"
}
catch {
Write-Log -Level "CRITICAL" -Message "关机命令执行失败: $($_.Exception.Message)"
}
# 脚本执行完关机命令后可以退出
exit
}
} else {
# 在监控窗口期内,继续等待
Write-Log -Level "INFO" -Message "等待 ${NormalPingInterval}秒后继续..."
Start-Sleep -Seconds $NormalPingInterval
}
}
}