158 lines
8.5 KiB
PowerShell
158 lines
8.5 KiB
PowerShell
[Console]::OutputEncoding = [Text.Encoding]::UTF8
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function H2B($hex) {
|
|
if (-not $hex) { return $null }
|
|
$n = $hex.Length / 2
|
|
$b = New-Object byte[] $n
|
|
for ($i = 0; $i -lt $n; $i++) { $b[$i] = [Convert]::ToByte($hex.Substring(2*$i, 2), 16) }
|
|
return $b
|
|
}
|
|
function UidStr($b, $o) { if ($b.Length -lt $o+6) { return "?" }; return ($b[$o..($o+5)] | ForEach-Object { $_.ToString("x2") }) -join "" }
|
|
|
|
function Analyze($name, $tsv, $devIp, $hostIp) {
|
|
$lines = [IO.File]::ReadAllLines($tsv)
|
|
$t0 = 1e9; $t1 = 0
|
|
foreach ($l in $lines) { $p = $l -split "`t"; if ($p.Count -lt 1 -or -not $p[0]) { continue }; $t = [double]$p[0]; if ($t -lt $t0) { $t0 = $t }; if ($t -gt $t1) { $t1 = $t } }
|
|
$dur = $t1 - $t0
|
|
|
|
$h2d = @{} # host->dev: 帧型
|
|
$d2h = @{} # dev->host: 帧型
|
|
$rdmCmd = @{} # host->dev RDM cmd 直方图 (flag=00)
|
|
$rdmPid = @{}
|
|
$queries = New-Object System.Collections.ArrayList # RDM 查询 (flag=00, cmd 18/19/1a/24)
|
|
$answers = New-Object System.Collections.ArrayList # dev->host RDM 短帧
|
|
$dmxA = 0; $evtB = 0; $ack42 = 0
|
|
$devUnsolicited = New-Object System.Collections.ArrayList # dev->host 42B 帧时间
|
|
|
|
foreach ($l in $lines) {
|
|
$p = $l -split "`t"; if ($p.Count -lt 5 -or -not $p[4]) { continue }
|
|
$b = H2B $p[4]; if ($b.Length -lt 0x1A) { continue }
|
|
$t = [double]$p[0]
|
|
$typ = [int]$b[4]
|
|
$isH2D = ($p[1] -eq $hostIp -and $p[2] -eq $devIp)
|
|
$isD2H = ($p[1] -eq $devIp -and $p[2] -eq $hostIp)
|
|
if (-not $isH2D -and -not $isD2H) { continue }
|
|
$len = $b.Length
|
|
if ($isH2D) {
|
|
$k = "t$typ|$len"
|
|
if (-not $h2d.ContainsKey($k)) { $h2d[$k] = 0 }; $h2d[$k]++
|
|
if ($typ -eq 4 -and $len -gt 1000) {
|
|
if ($b[0x20] -eq 1 -and $b[0x21] -eq 1) { $dmxA++ } else { $evtB++ }
|
|
}
|
|
if ($typ -eq 4 -and $len -lt 2000 -and $len -gt 0x44 -and $b[0x30] -eq 0) {
|
|
$cmd = $b[0x3D]
|
|
if ($cmd -eq 0x24 -or $cmd -eq 0x18 -or $cmd -eq 0x19 -or $cmd -eq 0x1a) {
|
|
$ck = "cmd={0:x2}" -f $cmd
|
|
if (-not $rdmCmd.ContainsKey($ck)) { $rdmCmd[$ck] = 0 }; $rdmCmd[$ck]++
|
|
$pidKey = ""
|
|
if ($cmd -ne 0x24 -and $b.Length -gt 0x51) { $pidKey = "{0:x2}{1:x2}" -f $b[0x50], $b[0x51]
|
|
if (-not $rdmPid.ContainsKey($pidKey)) { $rdmPid[$pidKey] = 0 }; $rdmPid[$pidKey]++ }
|
|
[void]$queries.Add(@{ t=$t; cmd=$cmd; pid=$pidKey; uid=(UidStr $b 0x3E); seq=([int]$b[0x22] + [int]$b[0x23]*256); tn=$b[0x4A]; len=$len })
|
|
}
|
|
}
|
|
} else {
|
|
$k = "t$typ|$len"
|
|
if (-not $d2h.ContainsKey($k)) { $d2h[$k] = 0 }; $d2h[$k]++
|
|
if ($typ -eq 4 -and $len -eq 42) { $ack42++; [void]$devUnsolicited.Add($t) }
|
|
if ($typ -eq 4 -and $len -lt 200 -and $len -gt 0x2C -and ($b[0x2A] -eq 0xCC -or $b[0x2A] -eq 0xAA)) {
|
|
[void]$answers.Add(@{ t=$t; b=$b; len=$len; seq=([int]$b[0x20] + [int]$b[0x21]*256); pulse=($b[0x2A] -eq 0xAA) })
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host "`n############ $name ($devIp) 时长 $([int]$dur)s ############"
|
|
Write-Host "--- host→dev 帧型 (Top12) ---"
|
|
$h2d.GetEnumerator() | Sort-Object { [int]$_.Value } -Descending | Select-Object -First 12 | ForEach-Object { Write-Host (" {0} : {1} ({2:F1}/s)" -f $_.Key, $_.Value, ($_.Value / $dur)) }
|
|
Write-Host "--- dev→host 帧型 (Top12) ---"
|
|
$d2h.GetEnumerator() | Sort-Object { [int]$_.Value } -Descending | Select-Object -First 12 | ForEach-Object { Write-Host (" {0} : {1} ({2:F1}/s)" -f $_.Key, $_.Value, ($_.Value / $dur)) }
|
|
Write-Host ("--- DMX-A={0} ({1:F1}/s) 事件B={2} ({3:F1}/s) 42B回帧={4} ({5:F1}/s) ACK/事件B比={6:F2}" -f $dmxA, ($dmxA/$dur), $evtB, ($evtB/$dur), $ack42, ($ack42/$dur), $(if ($evtB -gt 0) { $ack42/$evtB } else { 0 }))
|
|
Write-Host "--- RDM 查询 cmd (flag=00) ---"
|
|
$rdmCmd.GetEnumerator() | Sort-Object { [int]$_.Value } -Descending | ForEach-Object { Write-Host (" {0} : {1}" -f $_.Key, $_.Value) }
|
|
Write-Host "--- RDM 查询 PID (cmd!=24) ---"
|
|
$rdmPid.GetEnumerator() | Sort-Object { [int]$_.Value } -Descending | Select-Object -First 15 | ForEach-Object { Write-Host (" PID={0} : {1}" -f $_.Key, $_.Value) }
|
|
|
|
# 应答时延: cmd=18/19/1a 查询 → 200ms 内 seq 回显应答
|
|
$lat = New-Object System.Collections.ArrayList
|
|
$paired = 0; $unpaired = 0
|
|
foreach ($q in $queries) {
|
|
if ($q.cmd -eq 0x24) { continue }
|
|
$best = $null
|
|
foreach ($a in $answers) {
|
|
if ($a.pulse) { continue }
|
|
if ($a.t -lt $q.t -or $a.t -gt $q.t + 0.2) { continue }
|
|
if ($a.seq -eq $q.seq) { $best = $a; break }
|
|
}
|
|
if ($best) { $paired++; [void]$lat.Add([int](($best.t - $q.t) * 1000)) } else { $unpaired++ }
|
|
}
|
|
if ($lat.Count -gt 0) {
|
|
$ls = $lat | Sort-Object
|
|
Write-Host ("--- 参数应答时延: 配对={0} 未配对={1} min={2}ms 中位={3}ms max={4}ms" -f $paired, $unpaired, $ls[0], $ls[[int]($ls.Count/2)], $ls[-1])
|
|
} else {
|
|
Write-Host ("--- 参数应答时延: 配对={0} 未配对={1} (无样本)" -f $paired, $unpaired)
|
|
}
|
|
# 脉冲时延
|
|
$plat = New-Object System.Collections.ArrayList
|
|
$discQs = @($queries | Where-Object { $_.cmd -eq 0x24 })
|
|
$pulses = @($answers | Where-Object { $_.pulse })
|
|
foreach ($pu in $pulses) {
|
|
$best = $null
|
|
for ($i = $discQs.Count - 1; $i -ge 0; $i--) { if ($discQs[$i].t -le $pu.t) { $best = $discQs[$i]; break } }
|
|
if ($best -and ($pu.t - $best.t) -lt 0.5) { [void]$plat.Add([int](($pu.t - $best.t) * 1000)) }
|
|
}
|
|
if ($plat.Count -gt 0) {
|
|
$ps = $plat | Sort-Object
|
|
Write-Host ("--- 脉冲: {0} 个 (应答率 {1:F0}% of cmd24) 时延 min={2}ms 中位={3}ms max={4}ms" -f $pulses.Count, $(if ($discQs.Count -gt 0) { 100.0*$pulses.Count/$discQs.Count } else { 0 }), $ps[0], $ps[[int]($ps.Count/2)], $ps[-1])
|
|
}
|
|
return @{ queries=$queries; answers=$answers }
|
|
}
|
|
|
|
$r1 = Analyze "真机 .1" "$env:TEMP\opencode\ss123.tsv" "192.168.123.1" "192.168.123.113"
|
|
$r7 = Analyze "虚拟 .7" "$env:TEMP\opencode\ss58.tsv" "192.168.58.7" "192.168.58.107"
|
|
|
|
# ============ cmd=19/1a 深挖: 布局解析 (用 .1 的样本) ============
|
|
Write-Host "`n############ cmd=19/1a 布局深挖 (.1 样本) ############"
|
|
$cnt19 = 0; $cnt1a = 0
|
|
foreach ($q in $r1.queries) {
|
|
if ($q.cmd -eq 0x19 -and $cnt19 -lt 2) {
|
|
Write-Host (" cmd=19 t={0:F3} UID={1} PID={2} TN={3:x2} len={4}" -f $q.t, $q.uid, $q.pid, $q.tn, $q.len)
|
|
$cnt19++
|
|
}
|
|
if ($q.cmd -eq 0x1a -and $cnt1a -lt 2) {
|
|
Write-Host (" cmd=1a t={0:F3} UID={1} PID={2} TN={3:x2} len={4}" -f $q.t, $q.uid, $q.pid, $q.tn, $q.len)
|
|
$cnt1a++
|
|
}
|
|
}
|
|
# cmd=19/1a 的 PID 分布
|
|
$p19 = @{}; $p1a = @{}
|
|
foreach ($q in $r1.queries) {
|
|
if ($q.cmd -eq 0x19) { if (-not $p19.ContainsKey($q.pid)) { $p19[$q.pid] = 0 }; $p19[$q.pid]++ }
|
|
if ($q.cmd -eq 0x1a) { if (-not $p1a.ContainsKey($q.pid)) { $p1a[$q.pid] = 0 }; $p1a[$q.pid]++ }
|
|
}
|
|
Write-Host "--- cmd=19 PID 分布 (.1) ---"
|
|
$p19.GetEnumerator() | Sort-Object { [int]$_.Value } -Descending | Select-Object -First 10 | ForEach-Object { Write-Host (" PID={0} : {1}" -f $_.Key, $_.Value) }
|
|
Write-Host "--- cmd=1a PID 分布 (.1) ---"
|
|
$p1a.GetEnumerator() | Sort-Object { [int]$_.Value } -Descending | Select-Object -First 10 | ForEach-Object { Write-Host (" PID={0} : {1}" -f $_.Key, $_.Value) }
|
|
|
|
# cmd=19 查询的完整 hex 样本 (从原始 tsv 找一条)
|
|
Write-Host "--- cmd=19 查询完整 hex (1 条) ---"
|
|
$lines = [IO.File]::ReadAllLines("$env:TEMP\opencode\ss123.tsv")
|
|
$done = 0
|
|
foreach ($l in $lines) {
|
|
$p = $l -split "`t"; if ($p.Count -lt 5 -or -not $p[4]) { continue }
|
|
if ($p[1] -ne "192.168.123.113" -or $p[2] -ne "192.168.123.1") { continue }
|
|
$b = H2B $p[4]; if ($b.Length -lt 0x60 -or $b.Length -gt 2000) { continue }
|
|
if ($b[0x30] -ne 0 -or $b[0x3D] -ne 0x19) { continue }
|
|
Write-Host (" len={0} hex[0x20..0x60]={1}" -f $b.Length, (($b[0x20..([Math]::Min($b.Length-1, 0x60))] | ForEach-Object { $_.ToString("x2") }) -join ""))
|
|
# 找其应答
|
|
$seq = [int]$b[0x22] + [int]$b[0x23]*256
|
|
foreach ($a in $r1.answers) {
|
|
if (-not $a.pulse -and $a.seq -eq $seq -and $a.t -ge [double]$p[0] -and $a.t -le [double]$p[0] + 0.2) {
|
|
Write-Host (" 应答 len={0}: {1}" -f $a.len, (($a.b | ForEach-Object { $_.ToString("x2") }) -join ""))
|
|
break
|
|
}
|
|
}
|
|
$done++; if ($done -ge 2) { break }
|
|
}
|
|
Write-Host "`n分析完成。"
|