From 6c81bcf26ae204010ffc232d195fcca5c8d6ac6d Mon Sep 17 00:00:00 2001 From: "DESKTOP-PUHFO60\\dsx2016" Date: Tue, 27 Oct 2026 20:14:30 +0800 Subject: [PATCH] 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 --- host/send_sysinfo.ps1 | 154 +++++++++++++++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 23 deletions(-) diff --git a/host/send_sysinfo.ps1 b/host/send_sysinfo.ps1 index 126085b..06e903f 100644 --- a/host/send_sysinfo.ps1 +++ b/host/send_sysinfo.ps1 @@ -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 }