v1.3.0: Fixed header UI + auto-elevate to Administrator

- GE Aerospace ASCII banner stays fixed at top of console
- Live status line updates (Processing, Cleaned, Failed, etc.)
- Live stats counter in header (Cleaned/Failed counts)
- Batch file auto-elevates to Administrator via UAC

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-12-12 08:40:35 -05:00
parent ba2dbbefda
commit 9a8b16e0d4
3 changed files with 125 additions and 28 deletions

View File

@@ -21,7 +21,9 @@ This utility monitors a folder for DNC files and automatically strips specified
1. Copy the project folder to your DNC workstation 1. Copy the project folder to your DNC workstation
2. Edit `eDNC-SpecialCharFix.ps1` to set your watch folder (default: `C:\Dnc_Files\Q`) 2. Edit `eDNC-SpecialCharFix.ps1` to set your watch folder (default: `C:\Dnc_Files\Q`)
3. Run `Run-eDNCFix.bat` or create a shortcut 3. Run `Run-eDNCFix.bat` - it will auto-elevate to Administrator
**Note:** The batch file automatically requests Administrator privileges via UAC prompt.
### Run at Startup (Optional) ### Run at Startup (Optional)
@@ -129,6 +131,7 @@ The script uses exponential backoff (500ms → 1s → 2s → 4s → up to 16s) f
| Version | Date | Changes | | Version | Date | Changes |
|---------|------|---------| |---------|------|---------|
| 1.3.0 | 2025-12-12 | Fixed header UI, live status/stats, auto-elevate to Administrator |
| 1.2.1 | 2025-12-12 | Added GE Aerospace ASCII banner | | 1.2.1 | 2025-12-12 | Added GE Aerospace ASCII banner |
| 1.2.0 | 2025-12-12 | Immediate processing: 50ms delay, aggressive retry (100ms+), 15 attempts | | 1.2.0 | 2025-12-12 | Immediate processing: 50ms delay, aggressive retry (100ms+), 15 attempts |
| 1.1.0 | 2025-12-12 | Improved file locking: stability check, exponential backoff, debouncing | | 1.1.0 | 2025-12-12 | Improved file locking: stability check, exponential backoff, debouncing |

View File

@@ -1,13 +1,28 @@
@echo off @echo off
:: eDNC Special Character Fix Launcher :: eDNC Special Character Fix Launcher
:: Runs the PowerShell script with appropriate execution policy :: Auto-elevates to Administrator
title eDNC Special Character Fix title eDNC Special Character Fix
:: Check for admin rights
net session >nul 2>&1
if %errorLevel% == 0 (
goto :run
) else (
goto :elevate
)
:elevate
echo Requesting Administrator privileges...
powershell -Command "Start-Process -FilePath '%~f0' -Verb RunAs"
exit /b
:run
echo. echo.
echo Starting eDNC Special Character Fix... echo Running with Administrator privileges...
echo. echo.
cd /d "%~dp0"
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0eDNC-SpecialCharFix.ps1" %* powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0eDNC-SpecialCharFix.ps1" %*
echo. echo.

View File

@@ -36,9 +36,15 @@
.NOTES .NOTES
Author: GE Aerospace - Rutland Author: GE Aerospace - Rutland
Version: 1.2.1 Version: 1.3.0
Date: 2025-12-12 Date: 2025-12-12
v1.3.0 - Fixed header UI:
- GE Aerospace ASCII banner stays at top
- Live status updates in header (Processing, Cleaned, Failed)
- Live stats counter (Cleaned/Failed count)
- Auto-elevate to Administrator via batch file
v1.2.0 - Immediate processing: v1.2.0 - Immediate processing:
- Process file immediately when eDNC releases it (50ms initial delay) - Process file immediately when eDNC releases it (50ms initial delay)
- Aggressive retry: 100ms -> 200ms -> 400ms -> 800ms (15 attempts) - Aggressive retry: 100ms -> 200ms -> 400ms -> 800ms (15 attempts)
@@ -68,38 +74,82 @@ param(
) )
# Script info # Script info
$ScriptVersion = "1.2.1" $ScriptVersion = "1.3.0"
$ScriptName = "eDNC Special Character Fix" $ScriptName = "eDNC Special Character Fix"
# Display banner # Header display function
Write-Host "" function Show-Header {
Write-Host " ____ ____ " -ForegroundColor Cyan param([int]$Cleaned = 0, [int]$Failed = 0, [string]$Status = "Watching...")
Write-Host " / ___|| ___| / \ ___ _ __ ___ ___ _ __ __ _ ___ ___ " -ForegroundColor Cyan
Write-Host "| | _ | _| / _ \ / _ \ '__/ _ \/ __| '_ \ / _`` |/ __/ _ \" -ForegroundColor Cyan $headerLines = @(
Write-Host "| |_| || |___ / ___ \ __/ | | (_) \__ \ |_) | (_| | (_| __/" -ForegroundColor Cyan ""
Write-Host " \____||_____| /_/ \_\___|_| \___/|___/ .__/ \__,_|\___\___|" -ForegroundColor Cyan " ____ ____ "
Write-Host " |_| " -ForegroundColor Cyan " / ___|| ___| / \ ___ _ __ ___ ___ _ __ __ _ ___ ___ "
Write-Host "" "| | _ | _| / _ \ / _ \ '__/ _ \/ __| '_ \ / _`` |/ __/ _ \"
Write-Host " $ScriptName" -ForegroundColor White "| |_| || |___ / ___ \ __/ | | (_) \__ \ |_) | (_| | (_| __/"
Write-Host " by Cam P. | v$ScriptVersion" -ForegroundColor Gray " \____||_____| /_/ \_\___|_| \___/|___/ .__/ \__,_|\___\___|"
Write-Host "" " |_| "
Write-Host "================================================================" -ForegroundColor DarkGray ""
" $ScriptName"
" by Cam P. | v$ScriptVersion"
""
" Folder: $WatchFolder"
" Filter: $FileFilter | Cleaned: $Cleaned | Failed: $Failed"
""
" Status: $Status"
"================================================================"
)
# Save cursor, go to top, draw header
[Console]::SetCursorPosition(0, 0)
for ($i = 0; $i -lt $headerLines.Count; $i++) {
$line = $headerLines[$i]
# Clear line and write
Write-Host ("`r" + $line.PadRight([Console]::WindowWidth - 1)) -NoNewline
# Apply colors
[Console]::SetCursorPosition(0, $i)
if ($i -ge 1 -and $i -le 6) {
Write-Host $line -ForegroundColor Cyan
} elseif ($i -eq 8) {
Write-Host $line -ForegroundColor White
} elseif ($i -eq 9) {
Write-Host $line -ForegroundColor Gray
} elseif ($i -eq 14) {
if ($Status -like "*ERROR*" -or $Status -like "*FAILED*") {
Write-Host $line -ForegroundColor Red
} elseif ($Status -like "*CLEANED*") {
Write-Host $line -ForegroundColor Green
} else {
Write-Host $line -ForegroundColor Yellow
}
} elseif ($i -eq 15) {
Write-Host $line -ForegroundColor DarkGray
} else {
Write-Host $line
}
}
return $headerLines.Count
}
# Initialize console
Clear-Host
$script:HeaderHeight = Show-Header -Status "Initializing..."
# Validate watch folder exists # Validate watch folder exists
if (-not (Test-Path $WatchFolder)) { if (-not (Test-Path $WatchFolder)) {
Write-Host "[ERROR] Watch folder does not exist: $WatchFolder" -ForegroundColor Red Show-Header -Status "ERROR: Watch folder does not exist!"
[Console]::SetCursorPosition(0, $script:HeaderHeight + 1)
Write-Host "Please create the folder or specify a different path." -ForegroundColor Yellow Write-Host "Please create the folder or specify a different path." -ForegroundColor Yellow
exit 1 exit 1
} }
# Display configuration # Update header with ready status
Write-Host "Configuration:" -ForegroundColor Yellow Show-Header -Status "Watching for files... (Ctrl+C to stop)" | Out-Null
Write-Host " Watch Folder: $WatchFolder" [Console]::SetCursorPosition(0, $script:HeaderHeight + 1)
Write-Host " File Filter: $FileFilter" Write-Host "Removing bytes: $($CharactersToRemove -join ', ') (0x$($CharactersToRemove | ForEach-Object { '{0:X2}' -f $_ } | Join-String -Separator ', 0x'))" -ForegroundColor DarkGray
Write-Host " Subfolders: $IncludeSubfolders"
Write-Host " Removing bytes: $($CharactersToRemove -join ', ') (0x$($CharactersToRemove | ForEach-Object { '{0:X2}' -f $_ } | Join-String -Separator ', 0x'))"
Write-Host ""
Write-Host "Watching for files... Press Ctrl+C to stop" -ForegroundColor Green
Write-Host "" Write-Host ""
# Statistics # Statistics
@@ -127,6 +177,24 @@ $action = {
# Get parameters from message data # Get parameters from message data
$charsToRemove = $Event.MessageData.CharsToRemove $charsToRemove = $Event.MessageData.CharsToRemove
$debounceSeconds = $Event.MessageData.DebounceSeconds $debounceSeconds = $Event.MessageData.DebounceSeconds
$statusLine = $Event.MessageData.StatusLine
# Helper to update status line in header
$updateStatus = {
param($msg, $color = "Yellow")
$savedPos = [Console]::CursorTop
[Console]::SetCursorPosition(0, $statusLine)
Write-Host (" Status: " + $msg).PadRight([Console]::WindowWidth - 1) -ForegroundColor $color
[Console]::SetCursorPosition(0, $savedPos)
}
# Helper to update stats line in header
$updateStats = {
$savedPos = [Console]::CursorTop
[Console]::SetCursorPosition(0, 12)
Write-Host (" Filter: $($Event.MessageData.FileFilter) | Cleaned: $($script:FilesProcessed) | Failed: $($script:FailedFiles)").PadRight([Console]::WindowWidth - 1) -ForegroundColor White
[Console]::SetCursorPosition(0, $savedPos)
}
# Debounce check - skip if we processed this file recently # Debounce check - skip if we processed this file recently
$now = Get-Date $now = Get-Date
@@ -137,6 +205,7 @@ $action = {
} }
} }
& $updateStatus "Processing: $fileName" "Yellow"
Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $changeType | $fileName" -ForegroundColor White Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $changeType | $fileName" -ForegroundColor White
# Brief initial delay to let eDNC finish (50ms) # Brief initial delay to let eDNC finish (50ms)
@@ -186,8 +255,11 @@ $action = {
Write-Host " [CLEANED] Removed $removed byte(s)" -ForegroundColor Green Write-Host " [CLEANED] Removed $removed byte(s)" -ForegroundColor Green
$script:FilesProcessed++ $script:FilesProcessed++
$script:BytesRemoved += $removed $script:BytesRemoved += $removed
& $updateStatus "CLEANED: $fileName ($removed bytes)" "Green"
& $updateStats
} else { } else {
Write-Host " [OK] No special characters found" -ForegroundColor Gray Write-Host " [OK] No special characters found" -ForegroundColor Gray
& $updateStatus "OK: $fileName (no changes)" "Gray"
} }
# Mark as recently processed # Mark as recently processed
@@ -197,18 +269,23 @@ $action = {
catch [System.IO.IOException] { catch [System.IO.IOException] {
$retryCount++ $retryCount++
if ($retryCount -lt $maxRetries) { if ($retryCount -lt $maxRetries) {
# Exponential backoff: 500ms, 1s, 2s, 4s, 8s, 16s... # Exponential backoff: 100ms, 200ms, 400ms, 800ms...
$delay = [math]::Min($baseDelay * [math]::Pow(2, $retryCount - 1), 16000) $delay = [math]::Min($baseDelay * [math]::Pow(2, $retryCount - 1), 16000)
Write-Host " [RETRY] File locked, waiting $([math]::Round($delay/1000, 1))s... ($retryCount/$maxRetries)" -ForegroundColor Yellow Write-Host " [RETRY] File locked, waiting $([math]::Round($delay/1000, 1))s... ($retryCount/$maxRetries)" -ForegroundColor Yellow
& $updateStatus "RETRY: $fileName ($retryCount/$maxRetries)" "Yellow"
Start-Sleep -Milliseconds $delay Start-Sleep -Milliseconds $delay
} else { } else {
Write-Host " [FAILED] Could not access file after $maxRetries attempts" -ForegroundColor Red Write-Host " [FAILED] Could not access file after $maxRetries attempts" -ForegroundColor Red
$script:FailedFiles++ $script:FailedFiles++
& $updateStatus "FAILED: $fileName" "Red"
& $updateStats
} }
} }
catch { catch {
Write-Host " [ERROR] $($_.Exception.Message)" -ForegroundColor Red Write-Host " [ERROR] $($_.Exception.Message)" -ForegroundColor Red
$script:FailedFiles++ $script:FailedFiles++
& $updateStatus "ERROR: $fileName" "Red"
& $updateStats
break break
} }
finally { finally {
@@ -224,6 +301,8 @@ $action = {
$messageData = @{ $messageData = @{
CharsToRemove = $CharactersToRemove CharsToRemove = $CharactersToRemove
DebounceSeconds = $script:DebounceSeconds DebounceSeconds = $script:DebounceSeconds
StatusLine = 14 # Line number for status updates
FileFilter = $FileFilter
} }
# Register event handlers # Register event handlers