gea-shopfloor-waxtrace: stage Mitutoyo Backup/Install/Export triad
User received three PowerShell scripts from a Mitutoyo source for
backing up + restoring FormTracePak settings per asset:
Export-FormtracepakInventory.ps1 - audit: enumerate files + reg keys
Backup-FormtracepakSettings.ps1 - capture: config + data + reg into
timestamped ZIP, manifest-driven
Install-FormtracepakSettings.ps1 - restore: replay ZIP to a new bay,
hash-skip identicals, backup
existing as .pre_restore_bak
Cleanup pass over the vendor-shipped versions:
- Strip Unicode box-drawing characters from banners (ASCII-only policy)
- Install: switch to [ordered]@{} for DefaultAppTargets/DefaultDataTargets
so fallback priority is deterministic
- Install: add -AssetNumber gate that defaults to per-asset SFLD path
\\tsgwp00525...\Shopfloor\backup\formtracepac\<AssetNumber>
- Install: timestamp the .pre_restore_bak filename so re-runs don't
clobber the previous backup
- Install: handle BackupPath being a directory containing
formtracepak_backup_*.zip files (picks newest)
- .bat launchers for each PS1 (bypass execution policy, double-click)
Not yet wired into 09-Setup-WaxAndTrace.ps1; pending reference-backup
capture from a known-good bay before promoting to imaging path. Today
the V6.213 vendor MSI install + per-asset cal ISO still handle the
imaging-time setup directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
REM Bypass launcher for Backup-FormtracepakSettings.ps1.
|
||||||
|
REM Forwards any args to the PS1 (e.g. -AssetNumber WJRP2335 -IncludeExecutables).
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Backup-FormtracepakSettings.ps1" %*
|
||||||
|
exit /b %ERRORLEVEL%
|
||||||
@@ -0,0 +1,395 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Backs up all Mitutoyo Formtracepak settings, data, and registry to a ZIP archive.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Collects application config files, user data (part programs, layouts, measurements,
|
||||||
|
design values, calibration records), and registry keys from a Formtracepak host and
|
||||||
|
packages them into a portable ZIP file suitable for network storage or USB transfer.
|
||||||
|
|
||||||
|
The backup includes a manifest.json and per-folder file_manifest.csv files that
|
||||||
|
Install-FormtracepakSettings.ps1 uses to restore files to their original locations.
|
||||||
|
|
||||||
|
Supports Formtracepak v6.1 through current, FORMPAK-1000, SURFPAK-SV, DUAL TRACEPAK.
|
||||||
|
|
||||||
|
.PARAMETER AssetNumber
|
||||||
|
Asset number (e.g. WJRP1234) used to namespace the backup on the shopfloor
|
||||||
|
share. If omitted, the script prompts interactively and defaults to the
|
||||||
|
current $env:COMPUTERNAME. The asset number is the leaf folder under
|
||||||
|
\\tsgwp00525.wjs.geaerospace.net\shared\DT\Shopfloor\backup\formtracepac\
|
||||||
|
Ignored if -Destination is supplied.
|
||||||
|
|
||||||
|
.PARAMETER Destination
|
||||||
|
Where to write the ZIP file. Can be a USB drive, network share, or local path.
|
||||||
|
If omitted, defaults to the per-asset path on the GE shopfloor share:
|
||||||
|
\\tsgwp00525.wjs.geaerospace.net\shared\DT\Shopfloor\backup\formtracepac\<AssetNumber>
|
||||||
|
The destination directory is created if it does not exist.
|
||||||
|
|
||||||
|
.PARAMETER NoZip
|
||||||
|
Keep the backup as an uncompressed folder instead of creating a ZIP archive.
|
||||||
|
|
||||||
|
.PARAMETER IncludeExecutables
|
||||||
|
Also back up EXE and DLL files from the application directories. Off by default
|
||||||
|
since the application should be reinstalled via installer on the new host.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Backup-FormtracepakSettings.ps1
|
||||||
|
.\Backup-FormtracepakSettings.ps1 -AssetNumber WJRP2335
|
||||||
|
.\Backup-FormtracepakSettings.ps1 -Destination "F:\mitutoyo_backups" -IncludeExecutables
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$AssetNumber,
|
||||||
|
[string]$Destination,
|
||||||
|
[switch]$NoZip,
|
||||||
|
[switch]$IncludeExecutables
|
||||||
|
)
|
||||||
|
|
||||||
|
$SharedRoot = '\\tsgwp00525.wjs.geaerospace.net\shared\DT\Shopfloor\backup\formtracepac'
|
||||||
|
|
||||||
|
if (-not $Destination) {
|
||||||
|
if (-not $AssetNumber) {
|
||||||
|
$defaultAsset = $env:COMPUTERNAME
|
||||||
|
if ([Environment]::UserInteractive -and -not [Console]::IsInputRedirected) {
|
||||||
|
$input = Read-Host "Asset number for this backup (e.g. WJRP1234) [$defaultAsset]"
|
||||||
|
if ([string]::IsNullOrWhiteSpace($input)) {
|
||||||
|
$AssetNumber = $defaultAsset
|
||||||
|
} else {
|
||||||
|
$AssetNumber = $input.Trim()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# Non-interactive (qga / SYSTEM / scheduled task): fall back to COMPUTERNAME silently.
|
||||||
|
$AssetNumber = $defaultAsset
|
||||||
|
Write-Host "Non-interactive session - defaulting AssetNumber to $AssetNumber. Pass -AssetNumber to override."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$Destination = Join-Path $SharedRoot $AssetNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $Destination)) {
|
||||||
|
try {
|
||||||
|
New-Item -Path $Destination -ItemType Directory -Force -ErrorAction Stop | Out-Null
|
||||||
|
Write-Host "Created destination: $Destination"
|
||||||
|
} catch {
|
||||||
|
Write-Error "Destination not reachable / not creatable: $Destination`n$_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# CONFIGURABLE: Directories to scan. Add or remove to match your environment.
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
$ApplicationPaths = @(
|
||||||
|
"${env:ProgramFiles}\Mitutoyo"
|
||||||
|
"${env:ProgramFiles(x86)}\Mitutoyo"
|
||||||
|
"${env:ProgramFiles}\FORMTRACEPAK"
|
||||||
|
"${env:ProgramFiles(x86)}\FORMTRACEPAK"
|
||||||
|
"${env:ProgramFiles}\Formtracepak"
|
||||||
|
"${env:ProgramFiles(x86)}\Formtracepak"
|
||||||
|
"C:\Mitutoyo"
|
||||||
|
"D:\Mitutoyo"
|
||||||
|
"C:\FORMTRACEPAK"
|
||||||
|
"D:\FORMTRACEPAK"
|
||||||
|
"C:\FORMPAK"
|
||||||
|
"C:\SURFPAK"
|
||||||
|
"C:\MtFormTracer"
|
||||||
|
"C:\MtSurfMeas"
|
||||||
|
)
|
||||||
|
|
||||||
|
$UserDataPaths = @(
|
||||||
|
"${env:APPDATA}\Mitutoyo"
|
||||||
|
"${env:LOCALAPPDATA}\Mitutoyo"
|
||||||
|
"${env:ProgramData}\Mitutoyo"
|
||||||
|
"${env:APPDATA}\FORMTRACEPAK"
|
||||||
|
"${env:LOCALAPPDATA}\FORMTRACEPAK"
|
||||||
|
"${env:ProgramData}\FORMTRACEPAK"
|
||||||
|
"${env:USERPROFILE}\Documents\Mitutoyo"
|
||||||
|
"${env:USERPROFILE}\Documents\FORMTRACEPAK"
|
||||||
|
"${env:USERPROFILE}\Documents\FORMPAK"
|
||||||
|
"${env:USERPROFILE}\Documents\SURFPAK"
|
||||||
|
"${env:PUBLIC}\Documents\Mitutoyo"
|
||||||
|
"${env:PUBLIC}\Documents\FORMTRACEPAK"
|
||||||
|
)
|
||||||
|
|
||||||
|
$RegistryRoots = @(
|
||||||
|
"HKLM:\SOFTWARE\Mitutoyo"
|
||||||
|
"HKLM:\SOFTWARE\WOW6432Node\Mitutoyo"
|
||||||
|
"HKCU:\SOFTWARE\Mitutoyo"
|
||||||
|
"HKLM:\SOFTWARE\FORMTRACEPAK"
|
||||||
|
"HKLM:\SOFTWARE\WOW6432Node\FORMTRACEPAK"
|
||||||
|
"HKCU:\SOFTWARE\FORMTRACEPAK"
|
||||||
|
"HKLM:\SOFTWARE\FORMPAK"
|
||||||
|
"HKLM:\SOFTWARE\WOW6432Node\FORMPAK"
|
||||||
|
"HKCU:\SOFTWARE\FORMPAK"
|
||||||
|
"HKLM:\SOFTWARE\SURFPAK"
|
||||||
|
"HKCU:\SOFTWARE\SURFPAK"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Config file patterns (always backed up)
|
||||||
|
$ConfigExtensions = @('*.ini', '*.cfg', '*.xml', '*.json', '*.config', '*.settings', '*.prefs')
|
||||||
|
|
||||||
|
# Data file patterns (part programs, measurements, layouts, design data)
|
||||||
|
$DataExtensions = @(
|
||||||
|
'*.smp', '*.prn', '*.dat', '*.csv', '*.txt',
|
||||||
|
'*.tpl', '*.ppg', '*.ppt', '*.prg',
|
||||||
|
'*.fta', '*.srf', '*.ctr', '*.ctt',
|
||||||
|
'*.dxf', '*.igs', '*.iges',
|
||||||
|
'*.rpt', '*.lay', '*.lyt',
|
||||||
|
'*.html', '*.mhtml',
|
||||||
|
'*.cal', '*.stl', '*.ref',
|
||||||
|
'*.bmp', '*.jpg', '*.png'
|
||||||
|
)
|
||||||
|
|
||||||
|
$BinaryExtensions = @('*.exe', '*.dll')
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
||||||
|
$backupName = "formtracepak_backup_${env:COMPUTERNAME}_${timestamp}"
|
||||||
|
$stagingDir = Join-Path $env:TEMP $backupName
|
||||||
|
$configStage = Join-Path $stagingDir 'config'
|
||||||
|
$dataStage = Join-Path $stagingDir 'data'
|
||||||
|
$regStage = Join-Path $stagingDir 'registry'
|
||||||
|
$binStage = Join-Path $stagingDir 'binaries'
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path $configStage -Force | Out-Null
|
||||||
|
New-Item -ItemType Directory -Path $dataStage -Force | Out-Null
|
||||||
|
New-Item -ItemType Directory -Path $regStage -Force | Out-Null
|
||||||
|
|
||||||
|
if ($IncludeExecutables) {
|
||||||
|
New-Item -ItemType Directory -Path $binStage -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$counters = @{ Config = 0; Data = 0; Binaries = 0; RegKeys = 0; Errors = 0 }
|
||||||
|
|
||||||
|
Write-Host "`n----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host " FORMTRACEPAK Settings Backup" -ForegroundColor Yellow
|
||||||
|
Write-Host " Host: $env:COMPUTERNAME" -ForegroundColor Yellow
|
||||||
|
Write-Host "----------------------------------------------------------------------`n" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Helper: collect files into staging ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Copy-ToStaging {
|
||||||
|
param(
|
||||||
|
[string]$SourceRoot,
|
||||||
|
[string]$StageRoot,
|
||||||
|
[string[]]$Patterns,
|
||||||
|
[string]$CounterKey
|
||||||
|
)
|
||||||
|
|
||||||
|
$manifestRows = [System.Collections.Generic.List[PSObject]]::new()
|
||||||
|
|
||||||
|
foreach ($pattern in $Patterns) {
|
||||||
|
$files = Get-ChildItem -Path $SourceRoot -Filter $pattern -Recurse -File -ErrorAction SilentlyContinue
|
||||||
|
foreach ($f in $files) {
|
||||||
|
$relativePath = $f.FullName.Substring($SourceRoot.Length).TrimStart('\', '/')
|
||||||
|
$safeRelBase = ($SourceRoot -replace '[:\\]', '_').Trim('_')
|
||||||
|
$destRelPath = Join-Path $safeRelBase $relativePath
|
||||||
|
$destFull = Join-Path $StageRoot $destRelPath
|
||||||
|
$destDir = Split-Path $destFull -Parent
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (-not (Test-Path $destDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
|
||||||
|
}
|
||||||
|
Copy-Item -Path $f.FullName -Destination $destFull -Force
|
||||||
|
$counters[$CounterKey]++
|
||||||
|
|
||||||
|
$manifestRows.Add([PSCustomObject]@{
|
||||||
|
RelativePath = $destRelPath
|
||||||
|
OriginalPath = $f.FullName
|
||||||
|
SizeBytes = $f.Length
|
||||||
|
LastModified = $f.LastWriteTime.ToString('o')
|
||||||
|
Hash = (Get-FileHash $f.FullName -Algorithm MD5 -ErrorAction SilentlyContinue).Hash
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
Write-Warning " Failed to copy $($f.FullName): $_"
|
||||||
|
$counters.Errors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($manifestRows.Count -gt 0) {
|
||||||
|
$manifestFile = Join-Path $StageRoot 'file_manifest.csv'
|
||||||
|
if (Test-Path $manifestFile) {
|
||||||
|
$manifestRows | Export-Csv -Path $manifestFile -NoTypeInformation -Encoding UTF8 -Append
|
||||||
|
} else {
|
||||||
|
$manifestRows | Export-Csv -Path $manifestFile -NoTypeInformation -Encoding UTF8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 1. Backup Config Files ----------------------------------------------------------------------
|
||||||
|
Write-Host "[1/4] Backing up configuration files..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
foreach ($path in ($ApplicationPaths + $UserDataPaths)) {
|
||||||
|
if (-not (Test-Path $path)) { continue }
|
||||||
|
Write-Host " Scanning: $path" -ForegroundColor Green
|
||||||
|
Copy-ToStaging -SourceRoot $path -StageRoot $configStage `
|
||||||
|
-Patterns $ConfigExtensions -CounterKey 'Config'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 2. Backup Data Files ----------------------------------------------------------------------
|
||||||
|
Write-Host "[2/4] Backing up data files (part programs, layouts, measurements)..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
foreach ($path in ($ApplicationPaths + $UserDataPaths)) {
|
||||||
|
if (-not (Test-Path $path)) { continue }
|
||||||
|
Write-Host " Scanning: $path" -ForegroundColor Green
|
||||||
|
Copy-ToStaging -SourceRoot $path -StageRoot $dataStage `
|
||||||
|
-Patterns $DataExtensions -CounterKey 'Data'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 3. Backup Executables (optional) ----------------------------------------------------------------------
|
||||||
|
if ($IncludeExecutables) {
|
||||||
|
Write-Host "[2b/4] Backing up binaries (EXE/DLL)..." -ForegroundColor Cyan
|
||||||
|
foreach ($path in $ApplicationPaths) {
|
||||||
|
if (-not (Test-Path $path)) { continue }
|
||||||
|
Write-Host " Scanning: $path" -ForegroundColor Green
|
||||||
|
Copy-ToStaging -SourceRoot $path -StageRoot $binStage `
|
||||||
|
-Patterns $BinaryExtensions -CounterKey 'Binaries'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 4. Backup Registry ----------------------------------------------------------------------
|
||||||
|
Write-Host "[3/4] Backing up registry keys..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$regCsvRows = [System.Collections.Generic.List[PSObject]]::new()
|
||||||
|
|
||||||
|
foreach ($root in $RegistryRoots) {
|
||||||
|
if (-not (Test-Path $root)) { continue }
|
||||||
|
Write-Host " Found: $root" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Export as .reg file for easy manual import
|
||||||
|
$safeName = ($root -replace '[:\\]', '_').Trim('_')
|
||||||
|
$regFilePath = Join-Path $regStage "${safeName}.reg"
|
||||||
|
|
||||||
|
$nativeRoot = $root -replace '^HKLM:', 'HKEY_LOCAL_MACHINE' `
|
||||||
|
-replace '^HKCU:', 'HKEY_CURRENT_USER'
|
||||||
|
try {
|
||||||
|
$proc = Start-Process -FilePath 'reg.exe' `
|
||||||
|
-ArgumentList "export `"$nativeRoot`" `"$regFilePath`" /y" `
|
||||||
|
-Wait -PassThru -NoNewWindow -ErrorAction SilentlyContinue
|
||||||
|
if ($proc.ExitCode -eq 0) {
|
||||||
|
Write-Host " Exported: $regFilePath" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Warning " reg.exe export failed for ${root}: $_"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Also capture as CSV for granular restore
|
||||||
|
try {
|
||||||
|
$allKeys = @(Get-Item -Path $root -ErrorAction SilentlyContinue)
|
||||||
|
$allKeys += Get-ChildItem -Path $root -Recurse -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
foreach ($key in $allKeys) {
|
||||||
|
foreach ($valName in $key.GetValueNames()) {
|
||||||
|
$val = $key.GetValue($valName)
|
||||||
|
$kind = $key.GetValueKind($valName)
|
||||||
|
$regCsvRows.Add([PSCustomObject]@{
|
||||||
|
Path = $key.PSPath
|
||||||
|
Name = $valName
|
||||||
|
Value = [string]$val
|
||||||
|
Type = [string]$kind
|
||||||
|
})
|
||||||
|
$counters.RegKeys++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Warning " Registry read error at ${root}: $_"
|
||||||
|
$counters.Errors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($regCsvRows.Count -gt 0) {
|
||||||
|
$regCsvRows | Export-Csv -Path (Join-Path $regStage 'registry_values.csv') `
|
||||||
|
-NoTypeInformation -Encoding UTF8
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 5. Detect installed version ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
$detectedVersion = 'Unknown'
|
||||||
|
$uninstallPaths = @(
|
||||||
|
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
|
||||||
|
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
|
||||||
|
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
|
||||||
|
)
|
||||||
|
foreach ($uPath in $uninstallPaths) {
|
||||||
|
if (-not (Test-Path $uPath)) { continue }
|
||||||
|
$entries = Get-ChildItem -Path $uPath -ErrorAction SilentlyContinue
|
||||||
|
foreach ($entry in $entries) {
|
||||||
|
try {
|
||||||
|
$props = Get-ItemProperty -Path $entry.PSPath -ErrorAction SilentlyContinue
|
||||||
|
if ($props.DisplayName -and $props.DisplayName -match 'FORMTRACEPAK|FormTrace') {
|
||||||
|
$detectedVersion = $props.DisplayVersion
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
if ($detectedVersion -ne 'Unknown') { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Write manifest ----------------------------------------------------------------------
|
||||||
|
Write-Host "[4/4] Writing manifest..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$manifest = [PSCustomObject]@{
|
||||||
|
SourceComputer = $env:COMPUTERNAME
|
||||||
|
BackupTimestamp = (Get-Date -Format 'o')
|
||||||
|
FormtracepakVersion = $detectedVersion
|
||||||
|
OSVersion = [System.Environment]::OSVersion.VersionString
|
||||||
|
PowerShellVersion = $PSVersionTable.PSVersion.ToString()
|
||||||
|
IncludesConfig = ($counters.Config -gt 0)
|
||||||
|
IncludesData = ($counters.Data -gt 0)
|
||||||
|
IncludesBinaries = ($counters.Binaries -gt 0)
|
||||||
|
IncludesRegistry = ($counters.RegKeys -gt 0)
|
||||||
|
ConfigFileCount = $counters.Config
|
||||||
|
DataFileCount = $counters.Data
|
||||||
|
BinaryFileCount = $counters.Binaries
|
||||||
|
RegistryValueCount = $counters.RegKeys
|
||||||
|
ErrorCount = $counters.Errors
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifest | ConvertTo-Json -Depth 3 |
|
||||||
|
Set-Content -Path (Join-Path $stagingDir 'manifest.json') -Encoding UTF8
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Create ZIP or copy folder ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if (-not (Test-Path $Destination)) {
|
||||||
|
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($NoZip) {
|
||||||
|
$finalPath = Join-Path $Destination $backupName
|
||||||
|
Copy-Item -Path $stagingDir -Destination $finalPath -Recurse -Force
|
||||||
|
Write-Host "`n Backup folder: $finalPath" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
$zipPath = Join-Path $Destination "${backupName}.zip"
|
||||||
|
Compress-Archive -Path "$stagingDir\*" -DestinationPath $zipPath -Force
|
||||||
|
Write-Host "`n Backup archive: $zipPath" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup staging
|
||||||
|
Remove-Item -Path $stagingDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Summary ----------------------------------------------------------------------
|
||||||
|
Write-Host "`n----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host " Backup Summary" -ForegroundColor Yellow
|
||||||
|
Write-Host "----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host (" Config Files : {0}" -f $counters.Config) -ForegroundColor White
|
||||||
|
Write-Host (" Data Files : {0}" -f $counters.Data) -ForegroundColor White
|
||||||
|
Write-Host (" Binaries : {0}" -f $counters.Binaries) -ForegroundColor White
|
||||||
|
Write-Host (" Registry Values : {0}" -f $counters.RegKeys) -ForegroundColor White
|
||||||
|
Write-Host (" Errors : {0}" -f $counters.Errors) -ForegroundColor $(if ($counters.Errors -gt 0) { 'Red' } else { 'White' })
|
||||||
|
Write-Host (" Detected Version : {0}" -f $detectedVersion) -ForegroundColor White
|
||||||
|
Write-Host "----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
if ($counters.Config -eq 0 -and $counters.Data -eq 0 -and $counters.RegKeys -eq 0) {
|
||||||
|
Write-Warning "`n No Formtracepak artifacts found. Verify the search paths in the script"
|
||||||
|
Write-Warning " match your installation, or add custom paths to the configuration section."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "`n REMINDER: Stylus calibration data lives on the probe hardware." -ForegroundColor Magenta
|
||||||
|
Write-Host " Re-calibrate after restoring to a new host.`n" -ForegroundColor Magenta
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
REM Bypass launcher for Export-FormtracepakInventory.ps1.
|
||||||
|
REM Forwards any args to the PS1 (e.g. -OutputPath E:\inventories).
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Export-FormtracepakInventory.ps1" %*
|
||||||
|
exit /b %ERRORLEVEL%
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Inventories Mitutoyo Formtracepak installation artifacts on a Windows host.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Scans known and configurable paths for Formtracepak v6.1+ application files,
|
||||||
|
registry keys, part programs, calibration data, layout templates, design values,
|
||||||
|
and measurement data. Produces a CSV inventory and console summary.
|
||||||
|
|
||||||
|
Supports FORMTRACEPAK, FORMPAK-1000, SURFPAK-SV, and DUAL TRACEPAK components.
|
||||||
|
|
||||||
|
.PARAMETER OutputPath
|
||||||
|
Directory where the inventory CSV will be written. Defaults to script directory.
|
||||||
|
|
||||||
|
.PARAMETER ComputerName
|
||||||
|
Hostname to stamp on the inventory rows. Defaults to $env:COMPUTERNAME.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Export-FormtracepakInventory.ps1
|
||||||
|
.\Export-FormtracepakInventory.ps1 -OutputPath "E:\inventories"
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$OutputPath = $PSScriptRoot,
|
||||||
|
[string]$ComputerName = $env:COMPUTERNAME
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# CONFIGURABLE: Add or remove paths to match your environment.
|
||||||
|
# The script scans every path that exists on the target machine.
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
$ApplicationSearchPaths = @(
|
||||||
|
"${env:ProgramFiles}\Mitutoyo"
|
||||||
|
"${env:ProgramFiles(x86)}\Mitutoyo"
|
||||||
|
"${env:ProgramFiles}\FORMTRACEPAK"
|
||||||
|
"${env:ProgramFiles(x86)}\FORMTRACEPAK"
|
||||||
|
"${env:ProgramFiles}\Formtracepak"
|
||||||
|
"${env:ProgramFiles(x86)}\Formtracepak"
|
||||||
|
"C:\Mitutoyo"
|
||||||
|
"D:\Mitutoyo"
|
||||||
|
"C:\FORMTRACEPAK"
|
||||||
|
"D:\FORMTRACEPAK"
|
||||||
|
"C:\FORMPAK"
|
||||||
|
"C:\SURFPAK"
|
||||||
|
"C:\MtFormTracer"
|
||||||
|
"C:\MtSurfMeas"
|
||||||
|
)
|
||||||
|
|
||||||
|
$UserDataSearchPaths = @(
|
||||||
|
"${env:APPDATA}\Mitutoyo"
|
||||||
|
"${env:LOCALAPPDATA}\Mitutoyo"
|
||||||
|
"${env:ProgramData}\Mitutoyo"
|
||||||
|
"${env:APPDATA}\FORMTRACEPAK"
|
||||||
|
"${env:LOCALAPPDATA}\FORMTRACEPAK"
|
||||||
|
"${env:ProgramData}\FORMTRACEPAK"
|
||||||
|
"${env:USERPROFILE}\Documents\Mitutoyo"
|
||||||
|
"${env:USERPROFILE}\Documents\FORMTRACEPAK"
|
||||||
|
"${env:USERPROFILE}\Documents\FORMPAK"
|
||||||
|
"${env:USERPROFILE}\Documents\SURFPAK"
|
||||||
|
"${env:PUBLIC}\Documents\Mitutoyo"
|
||||||
|
"${env:PUBLIC}\Documents\FORMTRACEPAK"
|
||||||
|
)
|
||||||
|
|
||||||
|
$RegistrySearchRoots = @(
|
||||||
|
"HKLM:\SOFTWARE\Mitutoyo"
|
||||||
|
"HKLM:\SOFTWARE\WOW6432Node\Mitutoyo"
|
||||||
|
"HKCU:\SOFTWARE\Mitutoyo"
|
||||||
|
"HKLM:\SOFTWARE\FORMTRACEPAK"
|
||||||
|
"HKLM:\SOFTWARE\WOW6432Node\FORMTRACEPAK"
|
||||||
|
"HKCU:\SOFTWARE\FORMTRACEPAK"
|
||||||
|
"HKLM:\SOFTWARE\FORMPAK"
|
||||||
|
"HKLM:\SOFTWARE\WOW6432Node\FORMPAK"
|
||||||
|
"HKCU:\SOFTWARE\FORMPAK"
|
||||||
|
"HKLM:\SOFTWARE\SURFPAK"
|
||||||
|
"HKCU:\SOFTWARE\SURFPAK"
|
||||||
|
)
|
||||||
|
|
||||||
|
# File extensions associated with Formtracepak / Mitutoyo form-measurement software
|
||||||
|
$MitutoyoFileExtensions = @(
|
||||||
|
'*.smp', '*.prn', '*.dat', '*.csv', '*.txt',
|
||||||
|
'*.tpl', '*.ppg', '*.ppt', '*.prg',
|
||||||
|
'*.fta', '*.srf', '*.ctr', '*.ctt',
|
||||||
|
'*.dxf', '*.igs', '*.iges',
|
||||||
|
'*.rpt', '*.lay', '*.lyt', '*.html', '*.mhtml',
|
||||||
|
'*.ini', '*.cfg', '*.xml', '*.json', '*.config',
|
||||||
|
'*.cal', '*.stl', '*.ref',
|
||||||
|
'*.bmp', '*.jpg', '*.png',
|
||||||
|
'*.exe', '*.dll'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
||||||
|
$csvFile = Join-Path $OutputPath "formtracepak_inventory_${ComputerName}_${timestamp}.csv"
|
||||||
|
$inventory = [System.Collections.Generic.List[PSObject]]::new()
|
||||||
|
|
||||||
|
function Add-Item {
|
||||||
|
param(
|
||||||
|
[string]$Category,
|
||||||
|
[string]$ItemType,
|
||||||
|
[string]$Path,
|
||||||
|
[string]$Name,
|
||||||
|
[string]$Value,
|
||||||
|
[long]$SizeBytes = 0
|
||||||
|
)
|
||||||
|
$inventory.Add([PSCustomObject]@{
|
||||||
|
ComputerName = $ComputerName
|
||||||
|
Category = $Category
|
||||||
|
ItemType = $ItemType
|
||||||
|
Path = $Path
|
||||||
|
Name = $Name
|
||||||
|
Value = $Value
|
||||||
|
SizeBytes = $SizeBytes
|
||||||
|
ScanTime = (Get-Date -Format 'o')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 1. Scan Application Directories ----------------------------------------------------------------------
|
||||||
|
Write-Host "`n[1/5] Scanning application directories..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
foreach ($searchPath in $ApplicationSearchPaths) {
|
||||||
|
if (-not (Test-Path $searchPath)) { continue }
|
||||||
|
Write-Host " Found: $searchPath" -ForegroundColor Green
|
||||||
|
|
||||||
|
Add-Item -Category 'AppDirectory' -ItemType 'Directory' `
|
||||||
|
-Path $searchPath -Name (Split-Path $searchPath -Leaf) -Value 'EXISTS'
|
||||||
|
|
||||||
|
foreach ($pattern in $MitutoyoFileExtensions) {
|
||||||
|
try {
|
||||||
|
$files = Get-ChildItem -Path $searchPath -Filter $pattern -Recurse -File -ErrorAction SilentlyContinue
|
||||||
|
foreach ($f in $files) {
|
||||||
|
Add-Item -Category 'AppFile' -ItemType $f.Extension `
|
||||||
|
-Path $f.FullName -Name $f.Name `
|
||||||
|
-Value $f.LastWriteTime.ToString('o') `
|
||||||
|
-SizeBytes $f.Length
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 2. Scan User / Shared Data Directories ----------------------------------------------------------------------
|
||||||
|
Write-Host "[2/5] Scanning user and shared data directories..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
foreach ($searchPath in $UserDataSearchPaths) {
|
||||||
|
if (-not (Test-Path $searchPath)) { continue }
|
||||||
|
Write-Host " Found: $searchPath" -ForegroundColor Green
|
||||||
|
|
||||||
|
Add-Item -Category 'DataDirectory' -ItemType 'Directory' `
|
||||||
|
-Path $searchPath -Name (Split-Path $searchPath -Leaf) -Value 'EXISTS'
|
||||||
|
|
||||||
|
foreach ($pattern in $MitutoyoFileExtensions) {
|
||||||
|
try {
|
||||||
|
$files = Get-ChildItem -Path $searchPath -Filter $pattern -Recurse -File -ErrorAction SilentlyContinue
|
||||||
|
foreach ($f in $files) {
|
||||||
|
Add-Item -Category 'DataFile' -ItemType $f.Extension `
|
||||||
|
-Path $f.FullName -Name $f.Name `
|
||||||
|
-Value $f.LastWriteTime.ToString('o') `
|
||||||
|
-SizeBytes $f.Length
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 3. Scan Registry ----------------------------------------------------------------------
|
||||||
|
Write-Host "[3/5] Scanning registry..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
function Export-RegistryTree {
|
||||||
|
param([string]$RegPath)
|
||||||
|
if (-not (Test-Path $RegPath)) { return }
|
||||||
|
Write-Host " Found: $RegPath" -ForegroundColor Green
|
||||||
|
|
||||||
|
try {
|
||||||
|
$key = Get-Item -Path $RegPath -ErrorAction SilentlyContinue
|
||||||
|
if ($key) {
|
||||||
|
foreach ($valName in $key.GetValueNames()) {
|
||||||
|
$val = $key.GetValue($valName)
|
||||||
|
Add-Item -Category 'Registry' -ItemType 'Value' `
|
||||||
|
-Path $RegPath -Name $valName `
|
||||||
|
-Value ([string]$val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$subkeys = Get-ChildItem -Path $RegPath -Recurse -ErrorAction SilentlyContinue
|
||||||
|
foreach ($sk in $subkeys) {
|
||||||
|
foreach ($valName in $sk.GetValueNames()) {
|
||||||
|
$val = $sk.GetValue($valName)
|
||||||
|
Add-Item -Category 'Registry' -ItemType 'Value' `
|
||||||
|
-Path $sk.PSPath -Name $valName `
|
||||||
|
-Value ([string]$val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Warning " Registry error at ${RegPath}: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($root in $RegistrySearchRoots) {
|
||||||
|
Export-RegistryTree -RegPath $root
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 4. Locate Executables via Uninstall Keys ----------------------------------------------------------------------
|
||||||
|
Write-Host "[4/5] Checking installed programs (Uninstall registry)..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$uninstallPaths = @(
|
||||||
|
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
|
||||||
|
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
|
||||||
|
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($uPath in $uninstallPaths) {
|
||||||
|
if (-not (Test-Path $uPath)) { continue }
|
||||||
|
$entries = Get-ChildItem -Path $uPath -ErrorAction SilentlyContinue
|
||||||
|
foreach ($entry in $entries) {
|
||||||
|
try {
|
||||||
|
$displayName = (Get-ItemProperty -Path $entry.PSPath -ErrorAction SilentlyContinue).DisplayName
|
||||||
|
if ($displayName -and ($displayName -match 'Mitutoyo|FORMTRACEPAK|FORMPAK|SURFPAK|FormTrace|SurfPak|DUAL\s*TRACEPAK')) {
|
||||||
|
$props = Get-ItemProperty -Path $entry.PSPath
|
||||||
|
Add-Item -Category 'InstalledProgram' -ItemType 'Uninstall' `
|
||||||
|
-Path $entry.PSPath -Name $displayName `
|
||||||
|
-Value ($props.InstallLocation, $props.DisplayVersion, $props.Publisher -join ' | ')
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- 5. Locate Running Processes / Services ----------------------------------------------------------------------
|
||||||
|
Write-Host "[5/5] Checking running processes and services..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$processPatterns = @('Formpak', 'Formtracepak', 'SurfPak', 'MtSurf', 'MtForm', 'Mitutoyo')
|
||||||
|
foreach ($pp in $processPatterns) {
|
||||||
|
$procs = Get-Process -Name "*${pp}*" -ErrorAction SilentlyContinue
|
||||||
|
foreach ($p in $procs) {
|
||||||
|
Add-Item -Category 'RunningProcess' -ItemType 'Process' `
|
||||||
|
-Path $p.Path -Name $p.ProcessName `
|
||||||
|
-Value "PID=$($p.Id)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$services = Get-Service -ErrorAction SilentlyContinue |
|
||||||
|
Where-Object { $_.DisplayName -match 'Mitutoyo|FORMTRACEPAK|FORMPAK|SURFPAK' }
|
||||||
|
foreach ($svc in $services) {
|
||||||
|
Add-Item -Category 'Service' -ItemType 'Service' `
|
||||||
|
-Path $svc.Name -Name $svc.DisplayName `
|
||||||
|
-Value $svc.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Output ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if (-not (Test-Path $OutputPath)) {
|
||||||
|
New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$inventory | Export-Csv -Path $csvFile -NoTypeInformation -Encoding UTF8
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
$summary = $inventory | Group-Object Category | Select-Object Name, Count
|
||||||
|
Write-Host "`n----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host " FORMTRACEPAK Inventory Summary" -ForegroundColor Yellow
|
||||||
|
Write-Host " Host: $ComputerName" -ForegroundColor Yellow
|
||||||
|
Write-Host "----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
foreach ($g in $summary) {
|
||||||
|
Write-Host (" {0,-22} {1,6} items" -f $g.Name, $g.Count) -ForegroundColor White
|
||||||
|
}
|
||||||
|
$totalSize = 0
|
||||||
|
$measure = $inventory | Measure-Object SizeBytes -Sum -ErrorAction SilentlyContinue
|
||||||
|
if ($measure -and $null -ne $measure.Sum) { $totalSize = $measure.Sum }
|
||||||
|
Write-Host (" {0,-22} {1,6:N2} MB" -f 'Total File Size', ($totalSize / 1MB)) -ForegroundColor White
|
||||||
|
Write-Host "----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host " Inventory saved to: $csvFile" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
REM Bypass launcher for Install-FormtracepakSettings.ps1.
|
||||||
|
REM Forwards any args to the PS1 (e.g. -BackupPath E:\... -RestoreAll -DryRun).
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Install-FormtracepakSettings.ps1" %*
|
||||||
|
exit /b %ERRORLEVEL%
|
||||||
@@ -0,0 +1,419 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Deploys backed-up Mitutoyo Formtracepak settings from USB/removable media to a new host.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Reads a backup package created by Backup-FormtracepakSettings.ps1 and restores
|
||||||
|
application files, user data, registry keys, and configuration to the target host.
|
||||||
|
Supports Formtracepak v6.1 through current versions, plus FORMPAK-1000, SURFPAK-SV,
|
||||||
|
and DUAL TRACEPAK components.
|
||||||
|
|
||||||
|
Designed to run directly from USB media. Does NOT install the Formtracepak application
|
||||||
|
itself - the application must already be installed on the target host.
|
||||||
|
|
||||||
|
.PARAMETER AssetNumber
|
||||||
|
Asset number (e.g. WJRP1234) used to locate a per-asset backup on the
|
||||||
|
shopfloor share. If neither -BackupPath nor -AssetNumber is supplied, the
|
||||||
|
script prompts interactively and defaults to the current $env:COMPUTERNAME.
|
||||||
|
Ignored if -BackupPath is supplied.
|
||||||
|
|
||||||
|
.PARAMETER BackupPath
|
||||||
|
Path to the backup ZIP, directory containing a backup ZIP, or already-extracted
|
||||||
|
backup folder created by Backup-FormtracepakSettings.ps1. If omitted, defaults
|
||||||
|
to the per-asset path on the GE shopfloor share:
|
||||||
|
\\tsgwp00525.wjs.geaerospace.net\shared\DT\Shopfloor\backup\formtracepac\<AssetNumber>
|
||||||
|
If the path is a directory, the newest formtracepak_backup_*.zip inside it is used.
|
||||||
|
|
||||||
|
.PARAMETER RestoreRegistry
|
||||||
|
If specified, restores registry keys from the backup. Requires elevation.
|
||||||
|
|
||||||
|
.PARAMETER RestoreData
|
||||||
|
If specified, restores user data files (part programs, layouts, measurements, etc.).
|
||||||
|
|
||||||
|
.PARAMETER RestoreConfig
|
||||||
|
If specified, restores application configuration files (INI, XML, CFG, etc.).
|
||||||
|
|
||||||
|
.PARAMETER RestoreAll
|
||||||
|
Shortcut to restore everything: registry, data, and config.
|
||||||
|
|
||||||
|
.PARAMETER DryRun
|
||||||
|
Simulates the restore without writing any files or registry keys.
|
||||||
|
|
||||||
|
.PARAMETER Force
|
||||||
|
Overwrites existing files without prompting.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Install-FormtracepakSettings.ps1 -RestoreAll
|
||||||
|
.\Install-FormtracepakSettings.ps1 -AssetNumber WJRP2335 -RestoreAll
|
||||||
|
.\Install-FormtracepakSettings.ps1 -BackupPath "E:\mitutoyo\backup" -RestoreData -RestoreConfig
|
||||||
|
.\Install-FormtracepakSettings.ps1 -RestoreAll -DryRun
|
||||||
|
#>
|
||||||
|
[CmdletBinding(SupportsShouldProcess)]
|
||||||
|
param(
|
||||||
|
[string]$AssetNumber,
|
||||||
|
[string]$BackupPath,
|
||||||
|
[switch]$RestoreRegistry,
|
||||||
|
[switch]$RestoreData,
|
||||||
|
[switch]$RestoreConfig,
|
||||||
|
[switch]$RestoreAll,
|
||||||
|
[switch]$DryRun,
|
||||||
|
[switch]$Force
|
||||||
|
)
|
||||||
|
|
||||||
|
$SharedRoot = '\\tsgwp00525.wjs.geaerospace.net\shared\DT\Shopfloor\backup\formtracepac'
|
||||||
|
|
||||||
|
if (-not $BackupPath) {
|
||||||
|
if (-not $AssetNumber) {
|
||||||
|
$defaultAsset = $env:COMPUTERNAME
|
||||||
|
if ([Environment]::UserInteractive -and -not [Console]::IsInputRedirected) {
|
||||||
|
$assetInput = Read-Host "Asset number to restore (e.g. WJRP1234) [$defaultAsset]"
|
||||||
|
if ([string]::IsNullOrWhiteSpace($assetInput)) {
|
||||||
|
$AssetNumber = $defaultAsset
|
||||||
|
} else {
|
||||||
|
$AssetNumber = $assetInput.Trim()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$AssetNumber = $defaultAsset
|
||||||
|
Write-Host "Non-interactive session - defaulting AssetNumber to $AssetNumber. Pass -AssetNumber or -BackupPath to override."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$BackupPath = Join-Path $SharedRoot $AssetNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# CONFIGURABLE: Default restore targets. Edit these to match your environment.
|
||||||
|
# The backup manifest records the original paths; these are the fallback
|
||||||
|
# destinations if the original path does not exist on the new host.
|
||||||
|
# Ordered so fallback priority is deterministic.
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
$DefaultAppTargets = [ordered]@{
|
||||||
|
'ProgramFiles' = "${env:ProgramFiles}\Mitutoyo"
|
||||||
|
'ProgramFilesX86' = "${env:ProgramFiles(x86)}\Mitutoyo"
|
||||||
|
'CMitutoyo' = 'C:\Mitutoyo'
|
||||||
|
'CFORMTRACEPAK' = 'C:\FORMTRACEPAK'
|
||||||
|
}
|
||||||
|
|
||||||
|
$DefaultDataTargets = [ordered]@{
|
||||||
|
'AppDataRoaming' = "${env:APPDATA}\Mitutoyo"
|
||||||
|
'AppDataLocal' = "${env:LOCALAPPDATA}\Mitutoyo"
|
||||||
|
'ProgramData' = "${env:ProgramData}\Mitutoyo"
|
||||||
|
'UserDocuments' = "${env:USERPROFILE}\Documents\Mitutoyo"
|
||||||
|
'PublicDocuments' = "${env:PUBLIC}\Documents\Mitutoyo"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if ($RestoreAll) {
|
||||||
|
$RestoreRegistry = $true
|
||||||
|
$RestoreData = $true
|
||||||
|
$RestoreConfig = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $RestoreRegistry -and -not $RestoreData -and -not $RestoreConfig) {
|
||||||
|
Write-Warning "No restore scope specified. Use -RestoreAll, -RestoreData, -RestoreConfig, and/or -RestoreRegistry."
|
||||||
|
Write-Host "Run with -RestoreAll to restore everything, or combine flags as needed."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Locate and unpack backup ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if (-not (Test-Path $BackupPath)) {
|
||||||
|
Write-Error "Backup path not found: $BackupPath"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$workingBackup = $BackupPath
|
||||||
|
$zipToExtract = $null
|
||||||
|
|
||||||
|
if ($BackupPath -match '\.zip$') {
|
||||||
|
$zipToExtract = $BackupPath
|
||||||
|
} elseif ((Get-Item $BackupPath).PSIsContainer) {
|
||||||
|
# Directory: pick newest formtracepak_backup_*.zip if any. Else assume it
|
||||||
|
# already contains an extracted backup (manifest.json at root).
|
||||||
|
$cand = Get-ChildItem -Path $BackupPath -Filter 'formtracepak_backup_*.zip' -File -ErrorAction SilentlyContinue |
|
||||||
|
Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||||
|
if ($cand) {
|
||||||
|
$zipToExtract = $cand.FullName
|
||||||
|
Write-Host "Using newest backup ZIP in $BackupPath -> $($cand.Name)" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($zipToExtract) {
|
||||||
|
$extractDir = Join-Path $env:TEMP "formtracepak_restore_$(Get-Date -Format 'yyyyMMddHHmmss')"
|
||||||
|
Write-Host "Extracting backup archive to $extractDir ..." -ForegroundColor Cyan
|
||||||
|
Expand-Archive -Path $zipToExtract -DestinationPath $extractDir -Force
|
||||||
|
$workingBackup = $extractDir
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Load manifest ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
$manifestFile = Join-Path $workingBackup 'manifest.json'
|
||||||
|
if (-not (Test-Path $manifestFile)) {
|
||||||
|
Write-Error "No manifest.json found in backup at $workingBackup. Is this a valid Formtracepak backup?"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifest = Get-Content $manifestFile -Raw | ConvertFrom-Json
|
||||||
|
Write-Host "`n----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host " FORMTRACEPAK Settings Restore" -ForegroundColor Yellow
|
||||||
|
Write-Host "----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host " Source Host : $($manifest.SourceComputer)" -ForegroundColor White
|
||||||
|
Write-Host " Backup Date : $($manifest.BackupTimestamp)" -ForegroundColor White
|
||||||
|
Write-Host " Version : $($manifest.FormtracepakVersion)" -ForegroundColor White
|
||||||
|
Write-Host " Target Host : $env:COMPUTERNAME" -ForegroundColor White
|
||||||
|
Write-Host " Dry Run : $DryRun" -ForegroundColor $(if ($DryRun) { 'Yellow' } else { 'White' })
|
||||||
|
Write-Host "----------------------------------------------------------------------`n" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
$counters = @{ Files = 0; Directories = 0; RegistryKeys = 0; Skipped = 0; Errors = 0 }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Helper: Copy with logging ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Restore-FileItem {
|
||||||
|
param(
|
||||||
|
[string]$SourceFile,
|
||||||
|
[string]$DestFile
|
||||||
|
)
|
||||||
|
|
||||||
|
$destDir = Split-Path $DestFile -Parent
|
||||||
|
if (-not (Test-Path $destDir)) {
|
||||||
|
if ($DryRun) {
|
||||||
|
Write-Host " [DRYRUN] MKDIR $destDir" -ForegroundColor DarkGray
|
||||||
|
} else {
|
||||||
|
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
|
||||||
|
}
|
||||||
|
$counters.Directories++
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Test-Path $DestFile) -and -not $Force) {
|
||||||
|
$srcHash = (Get-FileHash $SourceFile -Algorithm MD5).Hash
|
||||||
|
$destHash = (Get-FileHash $DestFile -Algorithm MD5).Hash
|
||||||
|
if ($srcHash -eq $destHash) {
|
||||||
|
Write-Host " [SKIP] Identical: $DestFile" -ForegroundColor DarkGray
|
||||||
|
$counters.Skipped++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (-not $DryRun) {
|
||||||
|
$stamp = Get-Date -Format 'yyyyMMddHHmmss'
|
||||||
|
$backupDest = "${DestFile}.pre_restore_bak_${stamp}"
|
||||||
|
Copy-Item -Path $DestFile -Destination $backupDest -Force
|
||||||
|
Write-Host " [BAK] Existing file backed up: $backupDest" -ForegroundColor DarkYellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($DryRun) {
|
||||||
|
Write-Host " [DRYRUN] COPY $SourceFile -> $DestFile" -ForegroundColor DarkGray
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Copy-Item -Path $SourceFile -Destination $DestFile -Force
|
||||||
|
Write-Host " [OK] $DestFile" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Warning " [ERR] Failed to copy to ${DestFile}: $_"
|
||||||
|
$counters.Errors++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$counters.Files++
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Restore Config Files ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if ($RestoreConfig) {
|
||||||
|
Write-Host "[CONFIG] Restoring configuration files..." -ForegroundColor Cyan
|
||||||
|
$configDir = Join-Path $workingBackup 'config'
|
||||||
|
|
||||||
|
if (Test-Path $configDir) {
|
||||||
|
$configManifest = Join-Path $configDir 'file_manifest.csv'
|
||||||
|
if (Test-Path $configManifest) {
|
||||||
|
$entries = Import-Csv $configManifest
|
||||||
|
foreach ($entry in $entries) {
|
||||||
|
$srcFile = Join-Path $configDir $entry.RelativePath
|
||||||
|
if (-not (Test-Path $srcFile)) {
|
||||||
|
Write-Warning " Source file missing from backup: $($entry.RelativePath)"
|
||||||
|
$counters.Errors++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$destFile = $entry.OriginalPath
|
||||||
|
$destParent = Split-Path $destFile -Parent
|
||||||
|
if (-not (Test-Path $destParent)) {
|
||||||
|
foreach ($key in $DefaultAppTargets.Keys) {
|
||||||
|
$candidate = Join-Path $DefaultAppTargets[$key] $entry.RelativePath
|
||||||
|
$candidateParent = Split-Path $candidate -Parent
|
||||||
|
if (Test-Path (Split-Path $candidateParent -Parent)) {
|
||||||
|
$destFile = $candidate
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Restore-FileItem -SourceFile $srcFile -DestFile $destFile
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$configFiles = Get-ChildItem -Path $configDir -File -Recurse
|
||||||
|
foreach ($cf in $configFiles) {
|
||||||
|
$relPath = $cf.FullName.Substring($configDir.Length + 1)
|
||||||
|
$destFile = Join-Path $DefaultAppTargets['ProgramFiles'] $relPath
|
||||||
|
Restore-FileItem -SourceFile $cf.FullName -DestFile $destFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host " No config directory in backup. Skipping." -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Restore Data Files ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if ($RestoreData) {
|
||||||
|
Write-Host "[DATA] Restoring data files (part programs, layouts, measurements, designs)..." -ForegroundColor Cyan
|
||||||
|
$dataDir = Join-Path $workingBackup 'data'
|
||||||
|
|
||||||
|
if (Test-Path $dataDir) {
|
||||||
|
$dataManifest = Join-Path $dataDir 'file_manifest.csv'
|
||||||
|
if (Test-Path $dataManifest) {
|
||||||
|
$entries = Import-Csv $dataManifest
|
||||||
|
foreach ($entry in $entries) {
|
||||||
|
$srcFile = Join-Path $dataDir $entry.RelativePath
|
||||||
|
if (-not (Test-Path $srcFile)) {
|
||||||
|
Write-Warning " Source file missing from backup: $($entry.RelativePath)"
|
||||||
|
$counters.Errors++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$destFile = $entry.OriginalPath
|
||||||
|
$destParent = Split-Path $destFile -Parent
|
||||||
|
if (-not (Test-Path $destParent)) {
|
||||||
|
foreach ($key in $DefaultDataTargets.Keys) {
|
||||||
|
$candidate = Join-Path $DefaultDataTargets[$key] $entry.RelativePath
|
||||||
|
$candidateParent = Split-Path $candidate -Parent
|
||||||
|
if (Test-Path (Split-Path $candidateParent -Parent)) {
|
||||||
|
$destFile = $candidate
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Restore-FileItem -SourceFile $srcFile -DestFile $destFile
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$dataFiles = Get-ChildItem -Path $dataDir -File -Recurse
|
||||||
|
foreach ($df in $dataFiles) {
|
||||||
|
$relPath = $df.FullName.Substring($dataDir.Length + 1)
|
||||||
|
$destFile = Join-Path $DefaultDataTargets['UserDocuments'] $relPath
|
||||||
|
Restore-FileItem -SourceFile $df.FullName -DestFile $destFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host " No data directory in backup. Skipping." -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Restore Registry ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
if ($RestoreRegistry) {
|
||||||
|
Write-Host "[REG] Restoring registry keys..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
|
if (-not $isAdmin -and -not $DryRun) {
|
||||||
|
Write-Warning " Registry restore of HKLM keys requires elevation. HKCU keys will still be restored."
|
||||||
|
}
|
||||||
|
|
||||||
|
$regDir = Join-Path $workingBackup 'registry'
|
||||||
|
|
||||||
|
# Method 1: .reg file import
|
||||||
|
$regFiles = Get-ChildItem -Path $regDir -Filter '*.reg' -ErrorAction SilentlyContinue
|
||||||
|
foreach ($rf in $regFiles) {
|
||||||
|
if ($DryRun) {
|
||||||
|
Write-Host " [DRYRUN] REG IMPORT $($rf.FullName)" -ForegroundColor DarkGray
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$proc = Start-Process -FilePath 'reg.exe' -ArgumentList "import `"$($rf.FullName)`"" `
|
||||||
|
-Wait -PassThru -NoNewWindow -ErrorAction Stop
|
||||||
|
if ($proc.ExitCode -eq 0) {
|
||||||
|
Write-Host " [OK] Imported $($rf.Name)" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Warning " [ERR] reg.exe returned exit code $($proc.ExitCode) for $($rf.Name)"
|
||||||
|
$counters.Errors++
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Warning " [ERR] Failed to import $($rf.Name): $_"
|
||||||
|
$counters.Errors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$counters.RegistryKeys++
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method 2: CSV-based key-by-key restore
|
||||||
|
$regCsv = Join-Path $regDir 'registry_values.csv'
|
||||||
|
if (Test-Path $regCsv) {
|
||||||
|
$regEntries = Import-Csv $regCsv
|
||||||
|
foreach ($re in $regEntries) {
|
||||||
|
$regPath = $re.Path
|
||||||
|
$regName = $re.Name
|
||||||
|
$regValue = $re.Value
|
||||||
|
$regType = $re.Type
|
||||||
|
|
||||||
|
if ($regPath -match '^HKLM' -and -not $isAdmin -and -not $DryRun) {
|
||||||
|
Write-Host " [SKIP] HKLM key requires elevation: $regPath\$regName" -ForegroundColor DarkYellow
|
||||||
|
$counters.Skipped++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($DryRun) {
|
||||||
|
Write-Host " [DRYRUN] SET $regPath\$regName = $regValue ($regType)" -ForegroundColor DarkGray
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
if (-not (Test-Path $regPath)) {
|
||||||
|
New-Item -Path $regPath -Force | Out-Null
|
||||||
|
}
|
||||||
|
$splat = @{
|
||||||
|
Path = $regPath
|
||||||
|
Name = $regName
|
||||||
|
Value = $regValue
|
||||||
|
Force = $true
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
if ($regType) { $splat['PropertyType'] = $regType }
|
||||||
|
New-ItemProperty @splat | Out-Null
|
||||||
|
Write-Host " [OK] $regPath\$regName" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Warning " [ERR] Failed to set $regPath\$regName : $_"
|
||||||
|
$counters.Errors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$counters.RegistryKeys++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- Summary ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Write-Host "`n----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host " Restore Summary" -ForegroundColor Yellow
|
||||||
|
Write-Host "----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
Write-Host (" Files Restored : {0}" -f $counters.Files) -ForegroundColor White
|
||||||
|
Write-Host (" Directories Made : {0}" -f $counters.Directories) -ForegroundColor White
|
||||||
|
Write-Host (" Registry Entries : {0}" -f $counters.RegistryKeys) -ForegroundColor White
|
||||||
|
Write-Host (" Skipped : {0}" -f $counters.Skipped) -ForegroundColor DarkGray
|
||||||
|
Write-Host (" Errors : {0}" -f $counters.Errors) -ForegroundColor $(if ($counters.Errors -gt 0) { 'Red' } else { 'White' })
|
||||||
|
Write-Host "----------------------------------------------------------------------" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
if ($counters.Errors -gt 0) {
|
||||||
|
Write-Warning "Some items failed to restore. Review warnings above."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($DryRun) {
|
||||||
|
Write-Host "`n This was a DRY RUN. No changes were made." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup temp extraction if we unzipped
|
||||||
|
if ($workingBackup -ne $BackupPath -and (Test-Path $workingBackup) -and -not $DryRun) {
|
||||||
|
Remove-Item -Path $workingBackup -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "`n NOTE: Stylus calibration data is stored on the probe hardware." -ForegroundColor Magenta
|
||||||
|
Write-Host " Re-run calibration on the new host after restoring settings.`n" -ForegroundColor Magenta
|
||||||
Reference in New Issue
Block a user