feat: add embedded app PC debugging skill

This commit is contained in:
zoukai committed 2026-09-11 17:17:09 +08:00
commit 23e3a4910a
12 files changed
+2818

No files matched your search

+222
View File
@@ -0,0 +1,222 @@
<#
.SYNOPSIS
实时显示并保存设备串口数据。
.DESCRIPTION
使用 .NET SerialPort 直接独占打开串口。收到的数据会原样写入二进制文件,
同时解码后实时显示到当前 PowerShell,并按行写入带时间戳的文本日志。
默认只读取数据,不向设备发送指令。DtrEnable 和 RtsEnable 会改变硬件控制线,
只有用户明确要求时才能启用。
.EXAMPLE
.\serial_monitor.ps1 -Port COMx -LogPath F:\tmp\embedded-app-pc-debug\task\logs\serial.log
.EXAMPLE
.\serial_monitor.ps1 -Port COMx -LogPath .\serial.log -DurationSeconds 60
.PARAMETER DtrEnable
改变 DTR 硬件控制线;仅在用户明确要求时使用。
.PARAMETER RtsEnable
改变 RTS 硬件控制线;仅在用户明确要求时使用。
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Port,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$LogPath,
[ValidateRange(1, 4000000)]
[int]$BaudRate = 115200,
[ValidateRange(5, 8)]
[int]$DataBits = 8,
[ValidateSet('None', 'Odd', 'Even', 'Mark', 'Space')]
[string]$Parity = 'None',
[ValidateSet('One', 'OnePointFive', 'Two')]
[string]$StopBits = 'One',
[ValidateSet('None', 'XOnXOff', 'RequestToSend', 'RequestToSendXOnXOff')]
[string]$Handshake = 'None',
[string]$EncodingName = 'utf-8',
[ValidateRange(0, 2147483647)]
[int]$DurationSeconds = 0,
[ValidateRange(1, 5000)]
[int]$PollMilliseconds = 100,
[ValidateRange(64, 1048576)]
[int]$BufferSize = 4096,
[string]$RawPath,
[switch]$DtrEnable,
[switch]$RtsEnable
)
$ErrorActionPreference = 'Stop'
$serialPort = $null
$textWriter = $null
$rawStream = $null
$pendingLine = ''
function Write-TimestampedLines {
param(
[Parameter(Mandatory = $true)]
[string]$Text,
[Parameter(Mandatory = $true)]
[System.IO.StreamWriter]$Writer
)
$script:pendingLine += $Text
while ($true) {
$newlineIndex = $script:pendingLine.IndexOf("`n", [System.StringComparison]::Ordinal)
if ($newlineIndex -lt 0) {
break
}
$line = $script:pendingLine.Substring(0, $newlineIndex).TrimEnd("`r")
$script:pendingLine = $script:pendingLine.Substring($newlineIndex + 1)
$Writer.WriteLine(('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'), $line))
}
}
try {
$resolvedLogPath = [System.IO.Path]::GetFullPath($LogPath)
if ([string]::IsNullOrWhiteSpace($RawPath)) {
$resolvedRawPath = $resolvedLogPath + '.raw.bin'
}
else {
$resolvedRawPath = [System.IO.Path]::GetFullPath($RawPath)
}
$logDirectory = [System.IO.Path]::GetDirectoryName($resolvedLogPath)
$rawDirectory = [System.IO.Path]::GetDirectoryName($resolvedRawPath)
if (-not [string]::IsNullOrWhiteSpace($logDirectory)) {
[System.IO.Directory]::CreateDirectory($logDirectory) | Out-Null
}
if (-not [string]::IsNullOrWhiteSpace($rawDirectory)) {
[System.IO.Directory]::CreateDirectory($rawDirectory) | Out-Null
}
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
$textFileStream = New-Object System.IO.FileStream(
$resolvedLogPath,
[System.IO.FileMode]::Append,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::Read
)
$textWriter = New-Object System.IO.StreamWriter($textFileStream, $utf8NoBom)
$textWriter.AutoFlush = $true
$rawStream = New-Object System.IO.FileStream(
$resolvedRawPath,
[System.IO.FileMode]::Append,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::Read
)
try {
$encoding = [System.Text.Encoding]::GetEncoding($EncodingName)
}
catch {
throw "无法识别编码 '$EncodingName':$($_.Exception.Message)"
}
$decoder = $encoding.GetDecoder()
$serialPort = New-Object System.IO.Ports.SerialPort
$serialPort.PortName = $Port
$serialPort.BaudRate = $BaudRate
$serialPort.DataBits = $DataBits
$serialPort.Parity = [System.Enum]::Parse([System.IO.Ports.Parity], $Parity)
$serialPort.StopBits = [System.Enum]::Parse([System.IO.Ports.StopBits], $StopBits)
$serialPort.Handshake = [System.Enum]::Parse([System.IO.Ports.Handshake], $Handshake)
$serialPort.DtrEnable = $DtrEnable.IsPresent
$serialPort.RtsEnable = $RtsEnable.IsPresent
$serialPort.ReadTimeout = $PollMilliseconds
$serialPort.ReadBufferSize = [Math]::Max(4096, $BufferSize)
try {
$serialPort.Open()
}
catch {
throw "无法打开串口 $Port。串口可能不存在或正被其他程序占用:$($_.Exception.Message)"
}
$startTime = Get-Date
$startMessage = '[{0}] 已打开 {1},{2} {3}{4}{5},流控 {6};原始数据:{7}' -f `
(Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'),
$Port,
$BaudRate,
$DataBits,
$Parity.Substring(0, 1),
$(if ($StopBits -eq 'One') { '1' } elseif ($StopBits -eq 'Two') { '2' } else { '1.5' }),
$Handshake,
$resolvedRawPath
[Console]::WriteLine($startMessage)
$textWriter.WriteLine($startMessage)
$byteBuffer = New-Object byte[] $BufferSize
$charBuffer = New-Object char[] ($encoding.GetMaxCharCount($BufferSize))
while ($true) {
if ($DurationSeconds -gt 0 -and ((Get-Date) - $startTime).TotalSeconds -ge $DurationSeconds) {
break
}
try {
$count = $serialPort.Read($byteBuffer, 0, $byteBuffer.Length)
}
catch [System.TimeoutException] {
continue
}
if ($count -le 0) {
continue
}
$rawStream.Write($byteBuffer, 0, $count)
$rawStream.Flush()
$charCount = $decoder.GetChars($byteBuffer, 0, $count, $charBuffer, 0, $false)
if ($charCount -gt 0) {
$text = New-Object string($charBuffer, 0, $charCount)
[Console]::Write($text)
Write-TimestampedLines -Text $text -Writer $textWriter
}
}
}
catch {
# 使用字符串连接保留底层异常文本,避免异常内容再次参与格式化。
$errorMessage = '[' + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff') + '] 串口监测失败:' + [string]$_.Exception.Message
[Console]::Error.WriteLine($errorMessage)
if ($null -ne $textWriter) {
$textWriter.WriteLine($errorMessage)
}
exit 1
}
finally {
if ($null -ne $textWriter -and -not [string]::IsNullOrEmpty($script:pendingLine)) {
$textWriter.WriteLine(('[{0}] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'), $script:pendingLine.TrimEnd("`r")))
}
if ($null -ne $serialPort) {
if ($serialPort.IsOpen) {
$serialPort.Close()
}
$serialPort.Dispose()
}
if ($null -ne $rawStream) {
$rawStream.Dispose()
}
if ($null -ne $textWriter) {
$textWriter.WriteLine('[{0}] 串口监测结束' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'))
$textWriter.Dispose()
}
}