Fix host sender for PowerShell 7 via pyserial fallback.
System.IO.Ports is often missing on PS7; keep WMI collection in PowerShell and write USB serial through Python when needed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
# .\send_sysinfo.ps1
|
||||
# .\send_sysinfo.ps1 -Port COM5
|
||||
# .\send_sysinfo.ps1 -Port COM5 -IntervalMs 1000
|
||||
#
|
||||
# Serial I/O: Windows PowerShell 5 (System.IO.Ports) or Python + pyserial.
|
||||
|
||||
param(
|
||||
[string]$Port = "",
|
||||
@@ -15,12 +17,22 @@ $ErrorActionPreference = "Stop"
|
||||
function Find-CydPort {
|
||||
$ports = @()
|
||||
try {
|
||||
Add-Type -AssemblyName System.IO.Ports -ErrorAction SilentlyContinue | Out-Null
|
||||
$ports = [System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object
|
||||
} catch {}
|
||||
if (-not $ports -or $ports.Count -eq 0) {
|
||||
$ports = Get-CimInstance Win32_SerialPort -ErrorAction SilentlyContinue |
|
||||
Select-Object -ExpandProperty DeviceID | Sort-Object
|
||||
try {
|
||||
$ports = Get-CimInstance Win32_SerialPort -ErrorAction SilentlyContinue |
|
||||
Select-Object -ExpandProperty DeviceID | Sort-Object
|
||||
} catch {}
|
||||
}
|
||||
if (-not $ports -or $ports.Count -eq 0) {
|
||||
$key = 'HKLM:\HARDWARE\DEVICEMAP\SERIALCOMM'
|
||||
if (Test-Path $key) {
|
||||
$ports = (Get-ItemProperty $key).PSObject.Properties |
|
||||
Where-Object { $_.Name -notmatch '^PS' } |
|
||||
ForEach-Object { [string]$_.Value } |
|
||||
Sort-Object
|
||||
}
|
||||
}
|
||||
if (-not $ports -or $ports.Count -eq 0) {
|
||||
throw "No serial ports found. Plug in the CYD USB cable."
|
||||
@@ -43,7 +55,6 @@ function Get-CpuName {
|
||||
$p = Get-CimInstance Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if (-not $p) { return "CPU" }
|
||||
$n = [string]$p.Name
|
||||
# Shorten common Intel/AMD noise
|
||||
$n = $n -replace '\(R\)|\(TM\)|CPU @.*|with Radeon.*', ''
|
||||
$n = ($n -replace '\s+', ' ').Trim()
|
||||
return Sanitize $n 44
|
||||
@@ -64,18 +75,16 @@ function Get-GpuName {
|
||||
$gpus = Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -and $_.Name -notmatch 'Microsoft Basic|Remote Desktop' }
|
||||
if (-not $gpus) { return "GPU" }
|
||||
# Prefer NVIDIA / AMD discrete if present
|
||||
$pick = $gpus | Where-Object { $_.Name -match 'NVIDIA|GeForce|RTX|GTX|Radeon|AMD' } |
|
||||
Select-Object -First 1
|
||||
if (-not $pick) { $pick = $gpus | Select-Object -First 1 }
|
||||
$n = [string]$pick.Name
|
||||
$n = $n -replace 'NVIDIA GeForce ', 'RTX/GTX '
|
||||
$n = $n -replace 'NVIDIA GeForce ', ''
|
||||
$n = $n -replace 'AMD ', ''
|
||||
return Sanitize $n 44
|
||||
}
|
||||
|
||||
function Get-GpuPct {
|
||||
# 1) nvidia-smi
|
||||
$smi = Get-Command nvidia-smi -ErrorAction SilentlyContinue
|
||||
if ($smi) {
|
||||
try {
|
||||
@@ -86,7 +95,6 @@ function Get-GpuPct {
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
# 2) Windows GPU Engine counter (may need a few samples)
|
||||
try {
|
||||
$counters = Get-Counter '\GPU Engine(*)\Utilization Percentage' -ErrorAction Stop
|
||||
$samples = $counters.CounterSamples |
|
||||
@@ -136,27 +144,127 @@ function Build-Line {
|
||||
return "SI|host=$hostName|cpu=$cpu|cpu%=$cpuPct|gpu=$gpu|$gpuField|ram=$($ram.Used)/$($ram.Total)|disk=$($disk.Used)/$($disk.Total)"
|
||||
}
|
||||
|
||||
function Find-Python {
|
||||
foreach ($c in @(
|
||||
"python",
|
||||
(Join-Path $env:USERPROFILE "AppData\Local\Programs\Python\Python312\python.exe"),
|
||||
(Join-Path $env:USERPROFILE "AppData\Local\Programs\Python\Python311\python.exe")
|
||||
)) {
|
||||
if (-not $c) { continue }
|
||||
$cmd = Get-Command $c -ErrorAction SilentlyContinue
|
||||
if ($cmd) { return $cmd.Source }
|
||||
if (Test-Path $c) { return $c }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Test-DotNetSerial {
|
||||
try {
|
||||
[void][System.IO.Ports.SerialPort]
|
||||
return $true
|
||||
} catch {
|
||||
try {
|
||||
Add-Type -AssemblyName System.IO.Ports -ErrorAction Stop | Out-Null
|
||||
return $true
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $Port) { $Port = Find-CydPort }
|
||||
|
||||
Add-Type -AssemblyName System.IO.Ports
|
||||
$sp = [System.IO.Ports.SerialPort]::new($Port, $Baud)
|
||||
$sp.ReadTimeout = 200
|
||||
$sp.WriteTimeout = 2000
|
||||
$sp.NewLine = "`n"
|
||||
$sp.DtrEnable = $true
|
||||
$sp.RtsEnable = $true
|
||||
Write-Host "Sending host info -> $Port @ $Baud every ${IntervalMs}ms (Ctrl+C to stop)"
|
||||
|
||||
if (Test-DotNetSerial) {
|
||||
$sp = [System.IO.Ports.SerialPort]::new($Port, $Baud)
|
||||
$sp.ReadTimeout = 200
|
||||
$sp.WriteTimeout = 2000
|
||||
$sp.NewLine = "`n"
|
||||
$sp.DtrEnable = $true
|
||||
$sp.RtsEnable = $true
|
||||
try {
|
||||
$sp.Open()
|
||||
Start-Sleep -Milliseconds 400
|
||||
while ($true) {
|
||||
$line = Build-Line
|
||||
$sp.WriteLine($line)
|
||||
Write-Host $line
|
||||
Start-Sleep -Milliseconds $IntervalMs
|
||||
}
|
||||
} finally {
|
||||
if ($sp.IsOpen) { $sp.Close() }
|
||||
$sp.Dispose()
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
$py = Find-Python
|
||||
if (-not $py) {
|
||||
throw "No System.IO.Ports (PS7) and no python. Use Windows PowerShell 5, or: pip install pyserial"
|
||||
}
|
||||
|
||||
$pyCheck = & $py -c "import serial; print('ok')" 2>$null
|
||||
if ($LASTEXITCODE -ne 0 -or "$pyCheck" -notmatch 'ok') {
|
||||
throw "Python found but pyserial missing. Run: `"$py`" -m pip install pyserial"
|
||||
}
|
||||
|
||||
$writer = Join-Path $PSScriptRoot "_serial_writer.py"
|
||||
@'
|
||||
import sys, serial, time
|
||||
port, baud = sys.argv[1], int(sys.argv[2])
|
||||
# Keep DTR/RTS stable so ESP32 is not reset each write
|
||||
ser = serial.Serial()
|
||||
ser.port = port
|
||||
ser.baudrate = baud
|
||||
ser.timeout = 0.2
|
||||
ser.dtr = False
|
||||
ser.rts = False
|
||||
ser.open()
|
||||
time.sleep(0.3)
|
||||
try:
|
||||
for line in sys.stdin:
|
||||
line = line.rstrip("\r\n")
|
||||
if not line:
|
||||
continue
|
||||
ser.write((line + "\n").encode("utf-8", errors="ignore"))
|
||||
ser.flush()
|
||||
print(line, flush=True)
|
||||
finally:
|
||||
ser.close()
|
||||
'@ | Set-Content -Encoding ascii $writer
|
||||
|
||||
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$psi.FileName = $py
|
||||
$psi.Arguments = "`"$writer`" $Port $Baud"
|
||||
$psi.UseShellExecute = $false
|
||||
$psi.RedirectStandardInput = $true
|
||||
$psi.RedirectStandardOutput = $true
|
||||
$psi.RedirectStandardError = $true
|
||||
$psi.CreateNoWindow = $true
|
||||
$proc = [System.Diagnostics.Process]::Start($psi)
|
||||
try {
|
||||
$sp.Open()
|
||||
Write-Host "Sending host info -> $Port @ $Baud every ${IntervalMs}ms (Ctrl+C to stop)"
|
||||
Start-Sleep -Milliseconds 400
|
||||
while ($true) {
|
||||
while (-not $proc.HasExited) {
|
||||
$line = Build-Line
|
||||
$sp.WriteLine($line)
|
||||
Write-Host $line
|
||||
$proc.StandardInput.WriteLine($line)
|
||||
$proc.StandardInput.Flush()
|
||||
# Non-blocking-ish read of echo
|
||||
if (-not $proc.StandardOutput.EndOfStream) {
|
||||
$echo = $proc.StandardOutput.ReadLine()
|
||||
if ($echo) { Write-Host $echo }
|
||||
} else {
|
||||
Write-Host $line
|
||||
}
|
||||
Start-Sleep -Milliseconds $IntervalMs
|
||||
}
|
||||
$err = $proc.StandardError.ReadToEnd()
|
||||
if ($err) { Write-Host $err }
|
||||
} finally {
|
||||
if ($sp.IsOpen) { $sp.Close() }
|
||||
$sp.Dispose()
|
||||
try { $proc.StandardInput.Close() } catch {}
|
||||
if (-not $proc.HasExited) {
|
||||
Start-Sleep -Milliseconds 200
|
||||
if (-not $proc.HasExited) { $proc.Kill() }
|
||||
}
|
||||
$proc.Dispose()
|
||||
Remove-Item $writer -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user