#
Convert-goCMMSettings.ps1
Convert a legacy goCMM 1.1 ApplicationSettings.xml (C:\geaofi\) to the goCMM
2.12.3 goCMMSettings.xml schema.
The two are different SettingsModel schemas (different filename too). This is a
TEMPLATE-based converter: new-only fields get defaults, the per-bay values are
carried over from the old XML (+ optional overrides). Decisions baked in
(2026-06-19): access-control left blank, operator key normalized to "Operator",
cal intervals/restart use template defaults (old values NOT carried), Slack/Andon
/Quindos/Emx dropped.
Program paths default to the goCMM 2.12 convention (C:\geaofi\goCMM PC-DMIS
Programs\) - override with -ResetProgram / -ProbeCalibrationProgram if the .PRG
files live elsewhere. Those .PRG files MUST exist at the path written here.
Usage:
.\Convert-goCMMSettings.ps1 -OldXml C:\geaofi\ApplicationSettings.xml `
-OutFile C:\geaofi\goCMMSettings.xml -PcdmisVersion 2016 -CmmId CMM10
#>
param(
[Parameter(Mandatory=$true)][string]$OldXml,
[Parameter(Mandatory=$true)][string]$OutFile,
[string]$PcdmisVersion, # 2016 | 2019 | 2026 (or full "PC-DMIS ... 64-bit")
[string]$CmmId, # e.g. CMM10 - used for the cal-program name default
[string]$PartGroup, # optional override (friendly S:\ or UNC); else from old XML
[string]$ResetProgram, # optional override
[string]$ProbeCalibrationProgram # optional override
)
$ErrorActionPreference = 'Stop'
if (-not (Test-Path -LiteralPath $OldXml)) { throw "Old XML not found: $OldXml" }
[xml]$old = Get-Content -LiteralPath $OldXml -Raw
# --- part-group share canonicalization (FQDN + \SHARED, matches goCMM 2.12) ---
function Canon([string]$pg) {
if (-not $pg) { return $pg }
$pg = $pg -replace '(?i)^S:\\', '\\tsgwp00525.wjs.geaerospace.net\SHARED\'
$pg = [regex]::Replace($pg, '(?i)\\\\tsgwp00525(?:\.[A-Za-z0-9.\-]+)?\\shared(?=\\|$)', '\\tsgwp00525.wjs.geaerospace.net\SHARED')
return $pg
}
# --- resolve per-bay values (override -> old XML -> default) ---
# NOTE: read attributes with GetAttribute() - .Name collides with XmlElement.Name.
$pg = $PartGroup
if (-not $pg) {
$pgNode = $old.SettingsModel.PartGroups.PartGroup
if ($pgNode) { $pg = $pgNode.GetAttribute('FullName'); if (-not $pg) { $pg = $pgNode.FullName } }
}
$pg = Canon $pg
# NB: PowerShell vars are case-insensitive - do NOT name this $cmmid (collides with the $CmmId param).
$machineCmmId = $old.SettingsModel.MachineDefinition.CMMID
if (-not $machineCmmId -and $CmmId) { $machineCmmId = "WJRP/$CmmId" }
# SelectedVersionString from -PcdmisVersion
$verMap = @{ '2016' = 'PC-DMIS 2016.0 64-bit'; '2019' = 'PC-DMIS 2019 R2 64-bit'; '2026' = 'PC-DMIS 2026.1 64-bit' }
$selVer = ''
if ($PcdmisVersion) { $selVer = if ($verMap.ContainsKey($PcdmisVersion)) { $verMap[$PcdmisVersion] } else { $PcdmisVersion } }
# bool carryovers (old flat flags -> new IsEnabled)
function OldBool($node, $default='false') { $v = $node; if ($null -eq $v -or $v -eq '') { $default } else { ([string]$v).ToLower() } }
$pcdmisEnabled = OldBool $old.SettingsModel.UsingPcDmis 'true'
$modusEnabled = OldBool $old.SettingsModel.UsingModus 'false'
$winState = if ($old.SettingsModel.DefaultWindowState) { $old.SettingsModel.DefaultWindowState } else { 'Maximized' }
$allowSel = OldBool $old.SettingsModel.AllowOperationProgramSelection 'true'
$visExec = OldBool $old.SettingsModel.PcDmisSettings.VisibleDuringExecution 'true'
$cmmMode = if ($old.SettingsModel.PcDmisSettings.CMMMode) { $old.SettingsModel.PcDmisSettings.CMMMode } else { 'Online' }
$sizeTol = if ($old.SettingsModel.MachineDefinition.ProbeCalSizeTol) { $old.SettingsModel.MachineDefinition.ProbeCalSizeTol } else { '0.0005' }
$formTol = if ($old.SettingsModel.MachineDefinition.ProbeCalFormTol) { $old.SettingsModel.MachineDefinition.ProbeCalFormTol } else { '0.0005' }
$hasRotary = OldBool $old.SettingsModel.MachineDefinition.MachineHasRotaryTable 'false'
# program paths (override -> goCMM 2.12 convention default)
$progDir = 'C:\geaofi\goCMM PC-DMIS Programs'
if (-not $ResetProgram) { $ResetProgram = "$progDir\MachineResetProgram.prg" }
if (-not $ProbeCalibrationProgram) {
$calName = if ($CmmId) { "${CmmId}_Cal_All_Probes_rev2.PRG" } else { 'Cal_All_Probes_rev2.PRG' }
$ProbeCalibrationProgram = "$progDir\$calName"
}
# --- operator inputs (old KeyLabel + NonTBIInputs -> new OperatorInput; OPER -> Operator) ---
$inputs = @()
$kl = $old.SettingsModel.OperatorInputs.KeyLabel
if ($kl) { $inputs += [pscustomobject]@{ Key = $kl.GetAttribute('Name'); Display = $kl.GetAttribute('Display') } }
foreach ($oi in $old.SettingsModel.OperatorInputs.NonTBIInputs.OperatorInput) {
if ($oi -is [System.Xml.XmlElement]) { $inputs += [pscustomobject]@{ Key = $oi.GetAttribute('Name'); Display = $oi.GetAttribute('Display') } }
}
if (-not $inputs) {
$inputs = @([pscustomobject]@{Key='SERNO';Display='Serial Number'}, [pscustomobject]@{Key='Operator';Display='Operator'})
}
$opXml = ($inputs | ForEach-Object {
$k = if ($_.Key -ieq 'OPER') { 'Operator' } else { $_.Key } # decision #4
$d = if ($_.Key -ieq 'OPER') { 'Operator' } else { $_.Display }
" `n $k`n $d`n `n "
}) -join "`n"
function X([string]$s){ [System.Security.SecurityElement]::Escape($s) }
$xml = @"
true
PCDMIS
$(X $pg)
$opXml
false
$(X $winState)
$allowSel
$pcdmisEnabled
$visExec
$(X $ResetProgram)
false
false
$(X $ProbeCalibrationProgram)
false
false
1000
true
ProgramCount
false
$(X $selVer)
5
5
0
false
false
$modusEnabled
true
false
false
-Organiser
$(X $machineCmmId)
false
$sizeTol
$formTol
72
0
24
0
$hasRotary
false
$(X $cmmMode)
"@
$dir = Split-Path -Parent $OutFile
if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
# validate well-formed before writing
[void][xml]$xml
Set-Content -LiteralPath $OutFile -Value $xml -Encoding UTF8
Write-Host "Wrote $OutFile"
Write-Host " PartGroup = $pg"
Write-Host " CMMID = $machineCmmId"
Write-Host " SelectedVersion = $selVer"
Write-Host " ResetProgram = $ResetProgram"
Write-Host " ProbeCalProgram = $ProbeCalibrationProgram"
Write-Host "NOTE: confirm those .PRG files exist at the paths above (-ResetProgram / -ProbeCalibrationProgram to override)."