Add DSLogic capture, signal conversion and DSView decoders
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$DsViewPath = (Join-Path $env:ProgramFiles 'DSView'),
|
||||
[switch]$CheckOnly
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version 2
|
||||
# Resolve source paths relative to templates, never the caller working directory.
|
||||
# Standalone DSView and SETGUI use the same adapters and parsing cores.
|
||||
$templateRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$targetRoot = [IO.Path]::GetFullPath($DsViewPath)
|
||||
$decoderRoot = Join-Path $targetRoot 'decoders'
|
||||
|
||||
# Use .NET for compatibility with PowerShell lacking Get-FileHash.
|
||||
# Release both stream and hashing provider even if reading fails.
|
||||
function Get-ContentHash([string]$Path) {
|
||||
$algorithm = [Security.Cryptography.SHA256]::Create()
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
try {
|
||||
return [BitConverter]::ToString($algorithm.ComputeHash($stream))
|
||||
} finally {
|
||||
$stream.Dispose()
|
||||
$algorithm.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
# Check all existing ancestors: a junction under decoders/common could
|
||||
# redirect a local-looking update outside the chosen installation.
|
||||
function Assert-PlainPath([string]$Path) {
|
||||
$cursor = [IO.Path]::GetFullPath($Path)
|
||||
$first = $true
|
||||
while ($cursor) {
|
||||
if (Test-Path -LiteralPath $cursor) {
|
||||
$item = Get-Item -LiteralPath $cursor -Force
|
||||
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
||||
throw "Symbolic links and junctions are not supported: $cursor"
|
||||
}
|
||||
if (-not $first -and -not $item.PSIsContainer) {
|
||||
throw "Expected a parent directory: $cursor"
|
||||
}
|
||||
}
|
||||
$parent = [IO.Path]::GetDirectoryName($cursor)
|
||||
if ($parent -eq $cursor) { break }
|
||||
$cursor = $parent
|
||||
$first = $false
|
||||
}
|
||||
}
|
||||
|
||||
Assert-PlainPath $targetRoot
|
||||
$exe = Join-Path $targetRoot 'DSView.exe'
|
||||
if (-not (Test-Path -LiteralPath $exe -PathType Leaf)) {
|
||||
throw "DSView.exe not found in '$targetRoot'. Pass -DsViewPath with the installed DSView directory."
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $decoderRoot 'common') -PathType Container)) {
|
||||
throw "DSView decoders/common is missing. Select a complete DSView installation."
|
||||
}
|
||||
|
||||
# An explicit allowlist excludes tests, bytecode and unrelated modules.
|
||||
# The private common/setgui_decoders layout matches the embedded build.
|
||||
$files = @()
|
||||
foreach ($name in @('gate_driver_timing', 'transistor_pair', 'set_uart', 'set_can', 'pm35_uart')) {
|
||||
foreach ($leaf in @('__init__.py', 'pd.py')) {
|
||||
$files += [pscustomobject]@{
|
||||
Source = Join-Path $PSScriptRoot "decoders/$name/$leaf"
|
||||
Relative = "$name/$leaf"
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($name in @('__init__', 'gate_timing', 'transistor_pair', 'set_uart', 'set_can', 'pm35_uart')) {
|
||||
$files += [pscustomobject]@{
|
||||
Source = Join-Path $templateRoot "python/logic_analyzer/decoders/$name.py"
|
||||
Relative = "common/setgui_decoders/$name.py"
|
||||
}
|
||||
}
|
||||
# Validate the entire payload and destination before changing any decoder.
|
||||
foreach ($file in $files) {
|
||||
if (-not (Test-Path -LiteralPath $file.Source -PathType Leaf)) {
|
||||
throw "Missing templates source: $($file.Source). Initialize the templates submodule."
|
||||
}
|
||||
$destination = Join-Path $decoderRoot $file.Relative
|
||||
Assert-PlainPath $destination
|
||||
if (Test-Path -LiteralPath $destination -PathType Container) {
|
||||
throw "Expected a file, found a directory: $destination"
|
||||
}
|
||||
if ([IO.Path]::GetFullPath($file.Source) -eq [IO.Path]::GetFullPath($destination)) {
|
||||
throw 'The installation destination must differ from the templates source.'
|
||||
}
|
||||
}
|
||||
# Read-only validation does not create backups or check elevated write access.
|
||||
# Real installation performs its own write-access check before copying.
|
||||
if ($CheckOnly) {
|
||||
Write-Output "Validated $($files.Count) files for $targetRoot. No files changed."
|
||||
return
|
||||
}
|
||||
# A mapped/running Windows EXE cannot be opened for writing. Never terminate DSView.
|
||||
try {
|
||||
$handle = [IO.File]::Open($exe, [IO.FileMode]::Open, [IO.FileAccess]::ReadWrite, [IO.FileShare]::Read)
|
||||
$handle.Dispose()
|
||||
} catch {
|
||||
throw "Close DSView and check write permissions for '$targetRoot'. For Program Files, run PowerShell as administrator. $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
# Keep each previous version outside decoder discovery directories.
|
||||
# Retain backups after success for manual recovery, not only after errors.
|
||||
$backup = Join-Path $targetRoot ('set-decoder-backups/' + (Get-Date -Format 'yyyyMMdd-HHmmss') + '-' + [guid]::NewGuid().ToString('N'))
|
||||
Assert-PlainPath $backup
|
||||
# Back up all existing files before writing the first replacement.
|
||||
foreach ($file in $files) {
|
||||
$destination = Join-Path $decoderRoot $file.Relative
|
||||
if (Test-Path -LiteralPath $destination -PathType Leaf) {
|
||||
$saved = Join-Path $backup $file.Relative
|
||||
[IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($saved)) | Out-Null
|
||||
Copy-Item -LiteralPath $destination -Destination $saved
|
||||
}
|
||||
}
|
||||
# Track files before copying: a failed copy may leave a partial destination.
|
||||
# Rollback must include that file as well as completed replacements.
|
||||
$attempted = @()
|
||||
try {
|
||||
foreach ($file in $files) {
|
||||
$destination = Join-Path $decoderRoot $file.Relative
|
||||
[IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($destination)) | Out-Null
|
||||
$attempted += $file
|
||||
Copy-Item -LiteralPath $file.Source -Destination $destination -Force
|
||||
if ((Get-ContentHash $file.Source) -ne (Get-ContentHash $destination)) {
|
||||
throw "Copy verification failed: $destination"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
$failure = $_
|
||||
# Restore overwritten files and remove only new files from our allowlist.
|
||||
# Never recursively delete decoder folders or remove unrelated analyzers.
|
||||
foreach ($file in $attempted) {
|
||||
$destination = Join-Path $decoderRoot $file.Relative
|
||||
$saved = Join-Path $backup $file.Relative
|
||||
try {
|
||||
if (Test-Path -LiteralPath $saved -PathType Leaf) {
|
||||
Copy-Item -LiteralPath $saved -Destination $destination -Force
|
||||
} elseif (Test-Path -LiteralPath $destination -PathType Leaf) {
|
||||
Remove-Item -LiteralPath $destination -Force
|
||||
}
|
||||
} catch {
|
||||
Write-Warning "Could not restore $destination. Retained backup: $backup"
|
||||
}
|
||||
}
|
||||
throw $failure
|
||||
}
|
||||
Write-Output "Installed: IGBT 1SP/1SD, SET UART, SET CAN, PM35 UART in $decoderRoot"
|
||||
if (Test-Path -LiteralPath $backup) { Write-Output "Previous files retained in: $backup" }
|
||||
Write-Output 'Restart DSView. Add UART/CAN first, then stack the corresponding SET decoder. No external Python is required.'
|
||||
Reference in New Issue
Block a user