# Verify-Rs485.ps1 — 验证 rs485_verification_plan.md §2 离线项 S1~S4(-Hardware 时附 COM 口探测) param( [switch]$Hardware ) $ErrorActionPreference = 'Stop' $script:pass = 0; $script:fail = 0 function Check($name, $cond, $detail) { if ($cond) { $script:pass++; Write-Host (" [PASS] {0} {1}" -f $name, $detail) } else { $script:fail++; Write-Host (" [FAIL] {0} {1}" -f $name, $detail) } } $dir = Split-Path -Parent $MyInvocation.MyCommand.Path Write-Host "== S1 帧编码 ==" $expected = [byte[]](0x42, 0x52, 0x54, 0x3D, 0x38, 0x30, 0x0A) $payload = "BRT=80\n" -replace '\\n', "`n" -replace '\\r', "`r" $bytes = [System.Text.Encoding]::ASCII.GetBytes($payload) $same = ($bytes.Count -eq $expected.Count) -and (-not (Compare-Object $bytes $expected -SyncWindow 0)) Check "S1a BRT=80\n字节序列" $same ("| " + (($bytes | ForEach-Object { $_.ToString('X2') }) -join ' ')) $p1 = "A\r\nB" -replace '\\r', "`r" -replace '\\n', "`n" $CR = $p1.Contains([char]0x0D); $LF = $p1.Contains([char]0x0A) Check "S1b \r\n 转义还原" ($CR -and $LF) ("| CR={0} LF={1}" -f $CR, $LF) Write-Host "== S2 行帧解析 ==" $buf = "BRT=0`nBRT=25`r`nBRT=50`nBRT=" $parts = $buf -split "`n" $complete = $parts[0..($parts.Count - 2)] $rest = $parts[-1] $clean = @($complete | ForEach-Object { $_.TrimEnd("`r") }) Check "S2a 完整行数" ($clean.Count -eq 3) ("| " + ($clean -join ' / ')) Check "S2b 半包留缓冲" ($rest -eq 'BRT=') ("| tail='$rest'") Check "S2c 行内容无损" (($clean -join ',') -eq 'BRT=0,BRT=25,BRT=50') "| 无CR残留且顺序正确" Write-Host "== S3 Lua 脚本结构 ==" $luaPath = Join-Path $dir 'lpc_serial_brt.lua' Check "S3a 脚本文件存在" (Test-Path $luaPath) "| $luaPath" if (Test-Path $luaPath) { $lua = Get-Content $luaPath -Raw Check "S3b 读取通配符变量" ($lua -match 'get_trigger_variable\(1\)') "| get_trigger_variable(1).integer(Designer 2 语法)" Check "S3c 边界钳位" ($lua -match 'if v < 0' -and $lua -match 'if v > 100') "| 0..100" Check "S3d 强度换算" ($lua -match 'v / 100') "| 百分比→0..1" Check "S3e 组主亮度调光" ($lua -match 'get_group' -and $lua -match 'set_master_intensity') "| get_group(n):set_master_intensity" $balance = ([regex]::Matches($lua, '\(')).Count -eq ([regex]::Matches($lua, '\)')).Count Check "S3f 括号配平" $balance "| 粗略语法完整性" } Write-Host "== S4 工具模式 ==" $tool = Join-Path $dir 'Test-Rs485.ps1' $errs = $null $null = [System.Management.Automation.Language.Parser]::ParseFile($tool, [ref]$null, [ref]$errs) Check "S4a Test-Rs485.ps1 语法" ($errs.Count -eq 0) ("| 语法错误 {0} 个" -f $errs.Count) $raw = Get-Content $tool -Raw foreach ($mode in 'ListPorts', 'Send', 'Listen', 'Interactive') { Check "S4b 模式 -$mode" ($raw -match "\`$$mode|\[$mode\]|-$mode") "| 参数存在" } if ($Hardware) { Write-Host "== 硬件探测(只开/关端口,不写数据)==" foreach ($p in ([System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object)) { $sp = $null try { $sp = New-Object System.IO.Ports.SerialPort($p, 9600, 'None', 8, 'One') $sp.ReadTimeout = 200 $sp.Open(); $sp.Close() Write-Host (" [OPEN] {0} 可用(候选 USB-485,拔插对比确认)" -f $p) } catch { Write-Host (" [BUSY] {0} 被占用或不可开:{1}" -f $p, $_.Exception.Message.Trim()) } finally { if ($sp -and $sp.IsOpen) { $sp.Close() } } } } Write-Host "" Write-Host "========================================" Write-Host ("RS485 链路离线验证结果: PASS={0} FAIL={1}" -f $script:pass, $script:fail) if ($script:fail -eq 0) { Write-Host ">>> 离线验证全部通过,可进入阶段1(H1 单机自发自收)<<<" }