ESP32/tools/pc-metrics/serve-metrics-http.ps1
2026-06-23 20:38:23 +08:00

125 lines
4.1 KiB
PowerShell

param(
[int]$Port = 8789
)
$ErrorActionPreference = "Stop"
function Get-Percent {
param([double]$Value)
if ([double]::IsNaN($Value) -or [double]::IsInfinity($Value)) {
return 0
}
$rounded = [math]::Round($Value)
if ($rounded -lt 0) { return 0 }
if ($rounded -gt 100) { return 100 }
return [int]$rounded
}
function Get-SystemMetrics {
$cpu = Get-CimInstance Win32_PerfFormattedData_PerfOS_Processor |
Where-Object { $_.Name -eq "_Total" } |
Select-Object -First 1
$os = Get-CimInstance Win32_OperatingSystem
$totalMemory = [double]$os.TotalVisibleMemorySize
$freeMemory = [double]$os.FreePhysicalMemory
$memoryPercent = if ($totalMemory -gt 0) {
(($totalMemory - $freeMemory) / $totalMemory) * 100
} else {
0
}
$disk = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'"
$diskPercent = if ($disk -and [double]$disk.Size -gt 0) {
(([double]$disk.Size - [double]$disk.FreeSpace) / [double]$disk.Size) * 100
} else {
0
}
$network = Get-CimInstance Win32_PerfFormattedData_Tcpip_NetworkInterface |
Where-Object {
$_.Name -notmatch "Loopback|isatap|Teredo" -and
([double]$_.BytesReceivedPersec -gt 0 -or [double]$_.BytesSentPersec -gt 0)
}
$rxBytes = 0.0
$txBytes = 0.0
foreach ($adapter in $network) {
$rxBytes += [double]$adapter.BytesReceivedPersec
$txBytes += [double]$adapter.BytesSentPersec
}
return [ordered]@{
cpu = Get-Percent ([double]$cpu.PercentProcessorTime)
mem = Get-Percent $memoryPercent
disk = Get-Percent $diskPercent
rx = [int][math]::Max(0, [math]::Round($rxBytes / 1024))
tx = [int][math]::Max(0, [math]::Round($txBytes / 1024))
}
}
function Get-MetricsLine {
$m = Get-SystemMetrics
return "SYS,$($m.cpu),$($m.mem),$($m.disk),$($m.rx),$($m.tx)"
}
function Send-Response {
param(
[System.Net.Sockets.TcpClient]$Client,
[string]$Status,
[string]$ContentType,
[string]$Body
)
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($Body)
$header = "HTTP/1.1 $Status`r`nContent-Type: $ContentType; charset=utf-8`r`nContent-Length: $($bodyBytes.Length)`r`nConnection: close`r`nCache-Control: no-store`r`n`r`n"
$headerBytes = [System.Text.Encoding]::ASCII.GetBytes($header)
$stream = $Client.GetStream()
$stream.Write($headerBytes, 0, $headerBytes.Length)
$stream.Write($bodyBytes, 0, $bodyBytes.Length)
$stream.Flush()
}
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Any, $Port)
$listener.Start()
Write-Host "PC metrics HTTP server listening on 0.0.0.0:$Port"
Write-Host "Text endpoint: /metrics.txt"
Write-Host "JSON endpoint: /metrics.json"
try {
while ($true) {
$client = $listener.AcceptTcpClient()
try {
$stream = $client.GetStream()
$buffer = New-Object byte[] 2048
$read = $stream.Read($buffer, 0, $buffer.Length)
$request = [System.Text.Encoding]::ASCII.GetString($buffer, 0, $read)
$firstLine = ($request -split "`r?`n")[0]
$path = "/"
if ($firstLine -match "^[A-Z]+\\s+([^\\s]+)") {
$path = $Matches[1]
}
if ($path -eq "/health") {
Send-Response $client "200 OK" "text/plain" "ok`n"
} elseif ($path -eq "/metrics.json") {
$metrics = Get-SystemMetrics
Send-Response $client "200 OK" "application/json" (($metrics | ConvertTo-Json -Compress) + "`n")
} elseif ($path -eq "/" -or $path -eq "/metrics.txt") {
Send-Response $client "200 OK" "text/plain" ((Get-MetricsLine) + "`n")
} else {
Send-Response $client "404 Not Found" "text/plain" "not found`n"
}
} catch {
try {
Send-Response $client "500 Internal Server Error" "text/plain" "error`n"
} catch {
}
} finally {
$client.Close()
}
}
} finally {
$listener.Stop()
}