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:
@@ -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 |
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user