diff --git a/README.md b/README.md index 41265e2..8e0f7d2 100644 --- a/README.md +++ b/README.md @@ -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 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) @@ -129,6 +131,7 @@ The script uses exponential backoff (500ms → 1s → 2s → 4s → up to 16s) f | 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.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 | diff --git a/Run-eDNCFix.bat b/Run-eDNCFix.bat index 593473a..8094328 100644 --- a/Run-eDNCFix.bat +++ b/Run-eDNCFix.bat @@ -1,13 +1,28 @@ @echo off :: eDNC Special Character Fix Launcher -:: Runs the PowerShell script with appropriate execution policy +:: Auto-elevates to Administrator 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 Starting eDNC Special Character Fix... +echo Running with Administrator privileges... echo. +cd /d "%~dp0" powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0eDNC-SpecialCharFix.ps1" %* echo. diff --git a/eDNC-SpecialCharFix.ps1 b/eDNC-SpecialCharFix.ps1 index 35ce0f7..b290a62 100644 --- a/eDNC-SpecialCharFix.ps1 +++ b/eDNC-SpecialCharFix.ps1 @@ -36,9 +36,15 @@ .NOTES Author: GE Aerospace - Rutland - Version: 1.2.1 + Version: 1.3.0 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: - Process file immediately when eDNC releases it (50ms initial delay) - Aggressive retry: 100ms -> 200ms -> 400ms -> 800ms (15 attempts) @@ -68,38 +74,82 @@ param( ) # Script info -$ScriptVersion = "1.2.1" +$ScriptVersion = "1.3.0" $ScriptName = "eDNC Special Character Fix" -# Display banner -Write-Host "" -Write-Host " ____ ____ " -ForegroundColor Cyan -Write-Host " / ___|| ___| / \ ___ _ __ ___ ___ _ __ __ _ ___ ___ " -ForegroundColor Cyan -Write-Host "| | _ | _| / _ \ / _ \ '__/ _ \/ __| '_ \ / _`` |/ __/ _ \" -ForegroundColor Cyan -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 +# Header display function +function Show-Header { + param([int]$Cleaned = 0, [int]$Failed = 0, [string]$Status = "Watching...") + + $headerLines = @( + "" + " ____ ____ " + " / ___|| ___| / \ ___ _ __ ___ ___ _ __ __ _ ___ ___ " + "| | _ | _| / _ \ / _ \ '__/ _ \/ __| '_ \ / _`` |/ __/ _ \" + "| |_| || |___ / ___ \ __/ | | (_) \__ \ |_) | (_| | (_| __/" + " \____||_____| /_/ \_\___|_| \___/|___/ .__/ \__,_|\___\___|" + " |_| " + "" + " $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 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 exit 1 } -# Display configuration -Write-Host "Configuration:" -ForegroundColor Yellow -Write-Host " Watch Folder: $WatchFolder" -Write-Host " File Filter: $FileFilter" -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 +# Update header with ready status +Show-Header -Status "Watching for files... (Ctrl+C to stop)" | Out-Null +[Console]::SetCursorPosition(0, $script:HeaderHeight + 1) +Write-Host "Removing bytes: $($CharactersToRemove -join ', ') (0x$($CharactersToRemove | ForEach-Object { '{0:X2}' -f $_ } | Join-String -Separator ', 0x'))" -ForegroundColor DarkGray Write-Host "" # Statistics @@ -127,6 +177,24 @@ $action = { # Get parameters from message data $charsToRemove = $Event.MessageData.CharsToRemove $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 $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 # Brief initial delay to let eDNC finish (50ms) @@ -186,8 +255,11 @@ $action = { Write-Host " [CLEANED] Removed $removed byte(s)" -ForegroundColor Green $script:FilesProcessed++ $script:BytesRemoved += $removed + & $updateStatus "CLEANED: $fileName ($removed bytes)" "Green" + & $updateStats } else { Write-Host " [OK] No special characters found" -ForegroundColor Gray + & $updateStatus "OK: $fileName (no changes)" "Gray" } # Mark as recently processed @@ -197,18 +269,23 @@ $action = { catch [System.IO.IOException] { $retryCount++ 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) 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 } else { Write-Host " [FAILED] Could not access file after $maxRetries attempts" -ForegroundColor Red $script:FailedFiles++ + & $updateStatus "FAILED: $fileName" "Red" + & $updateStats } } catch { Write-Host " [ERROR] $($_.Exception.Message)" -ForegroundColor Red $script:FailedFiles++ + & $updateStatus "ERROR: $fileName" "Red" + & $updateStats break } finally { @@ -224,6 +301,8 @@ $action = { $messageData = @{ CharsToRemove = $CharactersToRemove DebounceSeconds = $script:DebounceSeconds + StatusLine = 14 # Line number for status updates + FileFilter = $FileFilter } # Register event handlers