Files
pxe-server/playbook/utilities/waxtrace-recovery/debug-waxtrace-cal.ps1
cproudlock 4015adeb33 utilities/waxtrace-recovery: ship cal diagnostic + repair scripts
Pair of operational tools used when a wax/trace bay's cal apply fails
(218-378-13 series cal Setup.exe crash, or any future variant). Were
living in /home/camp/pxe-images/ on the workstation; promoting to the
repo so they ship with the codebase, get version-controlled, and can
be pushed onto each PXE server's enrollment share via the standard
sync flow.

debug-waxtrace-cal.ps1 (+ .bat launcher):
- 9-section forensic walkthrough that runs on the bay as admin.
- Autodetects FormTracePak install location, dumps data/ dir contents,
  finds + mounts the per-asset cal ISO, lists its contents, checks the
  on-disk 09-Setup-WaxAndTrace.ps1 for the direct-copy bypass marker,
  greps the imaging-time log for cal lines, pulls the last 24h of
  .NET Runtime / Application Error / WER events related to Setup.exe.
- Output: C:\Logs\WaxTrace\debug-waxtrace-cal.log

fix-waxtrace-cal.ps1 (+ .bat launcher):
- Idempotent recovery: mounts the bay's cal ISO, unconditionally copies
  data\* into FormTracePak's data dir, renames any filename containing
  ' _' (space-underscore) to drop the embedded space, clears read-only,
  dismounts. Works on both 218-378-13 (broken filenames) and 218-458A
  (clean filenames) since the rename is a no-op when no space is
  present. Bypasses the buggy vendor cal Setup.exe entirely.
- Output: C:\Logs\WaxTrace\fix-waxtrace-cal.log

Both already pushed to \\172.16.9.1\enrollment\tools\ on both PXE
servers earlier today; this commit lands them in the repo as the
source of truth so future PXE server builds + ad-hoc rsyncs pick
them up automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:10:23 -04:00

273 lines
11 KiB
PowerShell

# debug-waxtrace-cal.ps1 - Forensic walkthrough of why the wax/trace
# calibration data did not land in FormTracePak's data dir on a bay.
#
# Captures everything: FTPak install location autodetected (we DO NOT
# trust the hardcoded path), data dir contents at every candidate,
# cal ISO presence + mount + listing, the current on-disk version of
# 09-Setup-WaxAndTrace.ps1 (does it have the direct-copy fix or the old
# vendor-Setup.exe path?), the imaging-time log, and Application event
# log entries for Setup.exe crashes in the last 24 hours.
#
# Run as administrator. Output to C:\Logs\WaxTrace\debug-waxtrace-cal.log
# + console. Paste the log back to support for diagnosis.
[CmdletBinding()]
param(
[string]$Asset
)
$logDir = 'C:\Logs\WaxTrace'
if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
$logFile = Join-Path $logDir 'debug-waxtrace-cal.log'
Remove-Item $logFile -Force -EA 0
function Log {
param([string]$Msg, [string]$Lvl = 'INFO')
$line = '[{0}] [{1}] {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Lvl, $Msg
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
Write-Host $line
}
Log '=== debug-waxtrace-cal.ps1 ==='
Log "User: $([Security.Principal.WindowsIdentity]::GetCurrent().Name)"
Log "Host: $env:COMPUTERNAME"
# ---------- Section 1: Asset tag ----------
Log ''
Log '--- 1. ASSET TAG ---'
if (-not $Asset) {
$mn = 'C:\Enrollment\machine-number.txt'
if (Test-Path $mn) {
$Asset = (Get-Content $mn -First 1 -EA 0).Trim()
Log "machine-number.txt: '$Asset'"
} else {
Log "machine-number.txt MISSING at $mn" 'WARN'
}
}
Log "Using asset: $Asset"
# ---------- Section 2: locate FormTracePak install ----------
Log ''
Log '--- 2. FORMTRACEPAK INSTALL LOCATION ---'
$candidatePaths = @(
'C:\Program Files (x86)\MitutoyoApp\Formtracepak',
'C:\Program Files\MitutoyoApp\Formtracepak',
'C:\Program Files (x86)\Mitutoyo\Formtracepak',
'C:\Program Files\Mitutoyo\Formtracepak',
'C:\MitutoyoApp\Formtracepak',
'C:\Mitutoyo\Formtracepak'
)
$ftpakRoot = $null
foreach ($p in $candidatePaths) {
$exe = Join-Path $p 'Formtracepak.exe'
if (Test-Path -LiteralPath $exe) {
$ftpakRoot = $p
Log "FOUND Formtracepak.exe at: $exe"
break
} else {
Log " miss: $p"
}
}
if (-not $ftpakRoot) {
Log 'No Formtracepak.exe at any expected path - searching C:\ Program Files trees...'
$hits = @()
foreach ($pf in @('C:\Program Files', 'C:\Program Files (x86)', 'C:\Mitutoyo', 'C:\MitutoyoApp')) {
if (-not (Test-Path $pf)) { continue }
$hits += Get-ChildItem -Path $pf -Filter 'Formtracepak.exe' -Recurse -File -ErrorAction SilentlyContinue
}
if ($hits) {
Log "Brute-force search hits:"
$hits | ForEach-Object { Log " $($_.FullName)" }
$ftpakRoot = Split-Path $hits[0].FullName -Parent
} else {
Log 'NO Formtracepak.exe ANYWHERE on C:\ - FormTracePak install likely never completed.' 'ERROR'
}
}
# ---------- Section 3: data dir + contents ----------
Log ''
Log '--- 3. FORMTRACEPAK DATA DIR ---'
if ($ftpakRoot) {
$dataDir = Join-Path $ftpakRoot 'data'
Log "Expected data dir: $dataDir"
if (Test-Path -LiteralPath $dataDir) {
$files = Get-ChildItem -LiteralPath $dataDir -File -EA 0 | Sort-Object Name
if ($files) {
Log "Data dir contents ($($files.Count) files):"
foreach ($f in $files) {
Log " $($f.Name) ($($f.Length) bytes, modified $($f.LastWriteTime))"
}
} else {
Log 'Data dir EXISTS but is EMPTY' 'WARN'
}
# Look for any cal files matching the asset's probe ID
$probeMatches = $files | Where-Object { $_.Name -match '218-\d{3}-\d+' }
if ($probeMatches) {
Log 'Files with probe-ID pattern (cal data candidates):'
foreach ($f in $probeMatches) { Log " hit: $($f.Name)" }
} else {
Log 'NO files matching the 218-XXX-XX probe-ID pattern in data dir.' 'WARN'
}
} else {
Log "Data dir MISSING at $dataDir" 'ERROR'
Log "Parent dir contents:"
Get-ChildItem -LiteralPath $ftpakRoot -EA 0 | ForEach-Object { Log " $($_.Name)" }
}
} else {
Log 'Skipping data-dir check (FTPak install not found)'
}
# ---------- Section 4: cal ISO present at C:\WaxTrace-Install\calibrations\ ----------
Log ''
Log '--- 4. CAL ISO ON LOCAL DISK ---'
$calDir = 'C:\WaxTrace-Install\calibrations'
if (Test-Path -LiteralPath $calDir) {
$allCals = Get-ChildItem -LiteralPath $calDir -Filter 'CAL-*.iso' -File -EA 0
Log "Cal staging dir: $calDir ($($allCals.Count) ISO files total)"
if ($Asset) {
$matched = $allCals | Where-Object { $_.Name -like "CAL-${Asset}_*" }
if ($matched) {
Log "ISO matching this asset:"
foreach ($f in $matched) { Log " $($f.Name) ($([math]::Round($f.Length/1KB,1)) KB)" }
} else {
Log "NO ISO matches CAL-${Asset}_*.iso in $calDir" 'ERROR'
Log "Available ISOs (first 8):"
$allCals | Select-Object -First 8 | ForEach-Object { Log " $($_.Name)" }
}
}
} else {
Log "Cal staging dir MISSING: $calDir - startnet.cmd did not xcopy installers-post\waxtrace at imaging time, OR 09-Setup-WaxAndTrace.ps1 already deleted it (Step 5 cleanup)." 'ERROR'
}
# ---------- Section 5: mount the matching cal ISO and list contents ----------
Log ''
Log '--- 5. MOUNT CAL ISO + LIST CONTENTS ---'
if ($Asset -and (Test-Path -LiteralPath $calDir)) {
$iso = Get-ChildItem -LiteralPath $calDir -Filter "CAL-${Asset}_*.iso" -EA 0 | Select-Object -First 1
if ($iso) {
try {
$img = Mount-DiskImage -ImagePath $iso.FullName -PassThru -ErrorAction Stop
Start-Sleep -Seconds 5
$vol = Get-DiskImage -ImagePath $iso.FullName | Get-Volume
$drive = $vol.DriveLetter
if ($drive) {
Log "Mounted at ${drive}:\ (label=$($vol.FileSystemLabel))"
$items = [System.IO.Directory]::GetFileSystemEntries("${drive}:\", '*', 'AllDirectories')
foreach ($i in $items) {
try {
$fi = [System.IO.FileInfo]::new($i)
if ($fi.Attributes -band [System.IO.FileAttributes]::Directory) {
Log " DIR $i"
} else {
Log " FILE $i ($($fi.Length) bytes)"
}
} catch { Log " $i" }
}
} else {
Log "Mount succeeded but no drive letter assigned" 'ERROR'
}
Dismount-DiskImage -ImagePath $iso.FullName -EA 0 | Out-Null
Log "Cal ISO dismounted"
} catch {
Log "Mount failed: $_" 'ERROR'
}
}
}
# ---------- Section 6: on-disk version of 09-Setup-WaxAndTrace.ps1 ----------
Log ''
Log '--- 6. 09-SETUP-WAXANDTRACE.PS1 ON DISK ---'
$setupPath = 'C:\Enrollment\shopfloor-setup\gea-shopfloor-waxtrace\09-Setup-WaxAndTrace.ps1'
if (Test-Path -LiteralPath $setupPath) {
$fi = Get-Item $setupPath
Log "Path: $setupPath"
Log " size: $($fi.Length) bytes, mtime: $($fi.LastWriteTime)"
$content = Get-Content -LiteralPath $setupPath -Raw
$hasDirectCopy = $content -match 'hasBrokenFilenames'
$hasV6213 = $content -match 'V6\.213'
$vendorOnly = $content -match 'calSetup.*Setup\.exe' -and -not $hasDirectCopy
Log " Direct-copy bypass present (hasBrokenFilenames): $hasDirectCopy"
Log " V6.213 baseline references: $hasV6213"
if (-not $hasDirectCopy) {
Log ' !!! This bay has the OLD 09-Setup-WaxAndTrace.ps1 without the direct-copy fix.' 'ERROR'
Log ' !!! 218-378-13 series cal discs WILL crash setup.exe with .NET unhandled exception.' 'ERROR'
Log ' !!! Pull the latest from the share: copy /Y \\172.16.9.1\enrollment\shopfloor-setup\gea-shopfloor-waxtrace\09-Setup-WaxAndTrace.ps1 to local.' 'ERROR'
}
} else {
Log "09-Setup-WaxAndTrace.ps1 NOT FOUND at expected path $setupPath" 'WARN'
}
# Also check C:\WaxTrace-Install copy (staged from share at WinPE time)
$setupStaging = 'C:\WaxTrace-Install\09-Setup-WaxAndTrace.ps1'
if (Test-Path -LiteralPath $setupStaging) {
$fi = Get-Item $setupStaging
$content = Get-Content -LiteralPath $setupStaging -Raw
$hasDirectCopy = $content -match 'hasBrokenFilenames'
Log "Staging copy at $setupStaging :"
Log " size: $($fi.Length), mtime: $($fi.LastWriteTime), has direct-copy: $hasDirectCopy"
}
# ---------- Section 7: imaging-time log content ----------
Log ''
Log '--- 7. 09-SETUP-WAXANDTRACE.LOG CONTENT (cal sections) ---'
$wtLog = 'C:\Logs\WaxTrace\09-Setup-WaxAndTrace.log'
if (Test-Path -LiteralPath $wtLog) {
Log "Tail of $wtLog (last 40 lines + cal-related lines):"
$tail = Get-Content -LiteralPath $wtLog -Tail 40 -EA 0
foreach ($l in $tail) { Log " $l" }
Log ''
Log 'cal/calibration/setup/copy/Mount-DiskImage matching lines:'
Select-String -LiteralPath $wtLog -Pattern 'cal|calibration|copied|direct copy|Mount-DiskImage|setup\.exe exit|hasBrokenFilenames' -SimpleMatch:$false -EA 0 |
Select-Object -Last 30 | ForEach-Object { Log " L$($_.LineNumber): $($_.Line)" }
} else {
Log "$wtLog MISSING - 09-Setup-WaxAndTrace.ps1 never ran (or never reached transcript start)." 'WARN'
}
# ---------- Section 8: Event log .NET Runtime crashes ----------
Log ''
Log '--- 8. EVENT LOG (.NET Runtime / Application Error / WER, last 24h) ---'
$cutoff = (Get-Date).AddHours(-24)
foreach ($lname in @('Application')) {
try {
$events = Get-WinEvent -FilterHashtable @{
LogName = $lname
StartTime = $cutoff
ProviderName = @('.NET Runtime', 'Application Error', 'Windows Error Reporting')
} -ErrorAction Stop | Where-Object { $_.Message -match 'setup\.exe|Setup\.exe|MitutoyoLauncher|Formtracepak|FileSystemInfo|set_Attributes' } |
Select-Object -First 5
if ($events) {
foreach ($e in $events) {
Log "Event $($e.Id) [$($e.LevelDisplayName)] from $($e.ProviderName) at $($e.TimeCreated)"
$e.Message -split "`n" | Select-Object -First 10 | ForEach-Object { Log " $_" }
}
} else {
Log "No matching .NET/AppError events in last 24h"
}
} catch {}
}
# ---------- Section 9: scoped search for cal data on known dirs ----------
Log ''
Log '--- 9. SCOPED CAL FILE SEARCH (Mitutoyo / Hexagon known dirs) ---'
$searchRoots = @(
'C:\Program Files (x86)\MitutoyoApp',
'C:\Program Files\MitutoyoApp',
'C:\Program Files (x86)\Mitutoyo',
'C:\Program Files\Mitutoyo',
'C:\MitutoyoApp',
'C:\Mitutoyo',
'C:\ProgramData\Mitutoyo'
)
foreach ($r in $searchRoots) {
if (-not (Test-Path -LiteralPath $r)) { continue }
$hits = @(Get-ChildItem -Path $r -Include 'Frc_*.txt','Linear_X_*.txt','Linear_Zl_*.txt','Str_XZ_*.txt','cvifdll.ini','CVIFDLL.ini' -Recurse -ErrorAction SilentlyContinue -Force)
if ($hits) {
Log "Under $r :"
$hits | Select-Object -First 10 | ForEach-Object { Log " $($_.FullName) ($($_.Length) bytes, $($_.LastWriteTime))" }
}
}
Log ''
Log "=== debug-waxtrace-cal.ps1 end. Output: $logFile ==="