CMM/DODA: fix DODA-bay profile resolution + goCMM 2.12 DataFolder + settings converter
- Get-PCProfile: subtype-strip fallback. DODA bays set pc-subtype.txt=doda, so the profile key became "gea-shopfloor-cmm-doda" which matched NO profile/alias -> Get-PCProfile returned null -> callers fell to hardcoded defaults (no PC-DMIS desktop icons; Defect Tracker / WJ Shopfloor / Plant Apps force-started). Now an unmatched compound key falls back to the bare pc-type (-> CMM). VM-tested: gea-shopfloor-cmm/doda + CMM/doda resolve to CMM (7 apps, PC-DMIS present); non-CMM unaffected. - 09-Setup-CMM Step 2.5c: Active Setup seed for goCMM 2.12 DataFolder. goCMM 2.12 stores its shared-data-dir in HKCU\Software\General Electric\goCMM\DataFolder (decompiled: RegistrySettings uses Registry.CurrentUser - per-user). Imaging as SupportUser wouldn't reach the ShopFloor operator's HKCU. Active Setup runs the StubPath once per user at first logon -> every user gets DataFolder=C:\geaofi\. VM-tested: StubPath writes the value with the trailing backslash intact. - Convert-goCMMSettings.ps1: converts legacy goCMM 1.1 ApplicationSettings.xml -> goCMM 2.12 goCMMSettings.xml schema. VM-tested: output byte-identical to a real goCMM-2.12-produced CMM10 goCMMSettings.xml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,28 @@ if ($siteConfig -and $siteConfig.pcProfiles -and $profileKey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# Subtype-strip fallback: a compound key like "gea-shopfloor-cmm-doda" (DODA
|
||||||
|
# CMM bays set pc-subtype.txt=doda) has no profile of its own. Fall back to
|
||||||
|
# the BARE pc-type ("gea-shopfloor-cmm" -> "CMM"). Without this the profile
|
||||||
|
# resolves to $null and callers silently use hardcoded defaults (wrong
|
||||||
|
# desktop icons + unwanted startup items).
|
||||||
|
if (-not $pcProfile -and $pcSubtype) {
|
||||||
|
$bare = $pcType
|
||||||
|
$pcProfile = $siteConfig.pcProfiles.$bare
|
||||||
|
if (-not $pcProfile) {
|
||||||
|
foreach ($g in $pcProfileAliasGroups) {
|
||||||
|
if ($g -icontains $bare) {
|
||||||
|
foreach ($alias in $g) {
|
||||||
|
if ($alias -ieq $bare) { continue }
|
||||||
|
$candidate = $siteConfig.pcProfiles.$alias
|
||||||
|
if ($candidate) { $pcProfile = $candidate; break }
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($pcProfile) { $profileKey = $bare }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($pcProfile) {
|
if ($pcProfile) {
|
||||||
|
|||||||
@@ -193,6 +193,27 @@ try {
|
|||||||
Write-CMMLog "Granted Users + Authenticated Users Modify on $frontEnd"
|
Write-CMMLog "Granted Users + Authenticated Users Modify on $frontEnd"
|
||||||
} catch { Write-CMMLog "Failed to ensure ${frontEnd}: $_" "WARN" }
|
} catch { Write-CMMLog "Failed to ensure ${frontEnd}: $_" "WARN" }
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 2.5c: Active Setup - seed goCMM 2.12 DataFolder per-user for ALL users
|
||||||
|
# ============================================================================
|
||||||
|
# goCMM 2.12 stores its shared-data-dir in HKCU\Software\General Electric\goCMM
|
||||||
|
# \DataFolder (decompiled: RegistrySettings uses Registry.CurrentUser - PER USER,
|
||||||
|
# not HKLM). Setting it at imaging only touches the imaging user (SupportUser);
|
||||||
|
# the ShopFloor operator's HKCU would be empty -> goCMM prompts them to pick the
|
||||||
|
# folder on first launch. Active Setup runs StubPath once in EACH user's context
|
||||||
|
# at first logon, so every user (SupportUser, ShopFloor, future) gets
|
||||||
|
# DataFolder=C:\geaofi\. Bump Version to re-push. (Uses Set-ItemProperty in the
|
||||||
|
# stub, not reg.exe, to avoid the trailing-backslash quoting trap.)
|
||||||
|
$asKey = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{8F3B2A10-7C4D-4E9A-B1F6-0A9C5D2E7B43}'
|
||||||
|
try {
|
||||||
|
New-Item -Path $asKey -Force | Out-Null
|
||||||
|
Set-ItemProperty -Path $asKey -Name '(default)' -Value 'goCMM DataFolder (per-user)'
|
||||||
|
Set-ItemProperty -Path $asKey -Name 'Version' -Value '1'
|
||||||
|
$stub = 'powershell.exe -NoProfile -WindowStyle Hidden -Command "New-Item -Path ''HKCU:\SOFTWARE\General Electric\goCMM'' -Force | Out-Null; Set-ItemProperty -Path ''HKCU:\SOFTWARE\General Electric\goCMM'' -Name DataFolder -Value ''C:\geaofi\''"'
|
||||||
|
Set-ItemProperty -Path $asKey -Name 'StubPath' -Value $stub
|
||||||
|
Write-CMMLog "Seeded Active Setup: goCMM DataFolder=C:\geaofi\ (per-user, all users)"
|
||||||
|
} catch { Write-CMMLog "Failed to seed Active Setup goCMM DataFolder: $_" 'WARN' }
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Step 2.6: First-run-as-admin for each installed PC-DMIS version
|
# Step 2.6: First-run-as-admin for each installed PC-DMIS version
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
<#
|
||||||
|
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 }
|
||||||
|
" <OperatorInput ClearInput=`"true`">`n <Key>$k</Key>`n <DisplayName>$d</DisplayName>`n <ValidationRules />`n </OperatorInput>"
|
||||||
|
}) -join "`n"
|
||||||
|
|
||||||
|
function X([string]$s){ [System.Security.SecurityElement]::Escape($s) }
|
||||||
|
|
||||||
|
$xml = @"
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<SettingsModel xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" LockInterface="false" AdminGroupName="" PowerUsersGroupName="" PasswordHash="" PasswordSalt="" LockMachineReset="false">
|
||||||
|
<MaintainFolderStructureWhenCopying>true</MaintainFolderStructureWhenCopying>
|
||||||
|
<DefaultSoftware>PCDMIS</DefaultSoftware>
|
||||||
|
<PartGroups>
|
||||||
|
<PartGroup>
|
||||||
|
<FullName>$(X $pg)</FullName>
|
||||||
|
<Children />
|
||||||
|
</PartGroup>
|
||||||
|
</PartGroups>
|
||||||
|
<OperatorInputs>
|
||||||
|
$opXml
|
||||||
|
</OperatorInputs>
|
||||||
|
<UseBladeGrid>false</UseBladeGrid>
|
||||||
|
<DefaultWindowState>$(X $winState)</DefaultWindowState>
|
||||||
|
<AllowOperationProgramSelection>$allowSel</AllowOperationProgramSelection>
|
||||||
|
<BladesDatFile />
|
||||||
|
<PcDmisSettings>
|
||||||
|
<IsEnabled>$pcdmisEnabled</IsEnabled>
|
||||||
|
<VisibleDuringExecution>$visExec</VisibleDuringExecution>
|
||||||
|
<ResetProgram>$(X $ResetProgram)</ResetProgram>
|
||||||
|
<RotaryTableCalibrationProgram />
|
||||||
|
<TrackProbeCalibration>false</TrackProbeCalibration>
|
||||||
|
<TrackRotaryTableCalbiration>false</TrackRotaryTableCalbiration>
|
||||||
|
<ProbeCalibrationProgram>$(X $ProbeCalibrationProgram)</ProbeCalibrationProgram>
|
||||||
|
<RotaryTableCalibration />
|
||||||
|
<IPPServerExe />
|
||||||
|
<IPPArguments />
|
||||||
|
<SaveAndClose>false</SaveAndClose>
|
||||||
|
<DelayMeasurementRoutines>false</DelayMeasurementRoutines>
|
||||||
|
<MeasurementRoutineDelay>1000</MeasurementRoutineDelay>
|
||||||
|
<Restart>true</Restart>
|
||||||
|
<RestartMode>ProgramCount</RestartMode>
|
||||||
|
<UseIPPServer>false</UseIPPServer>
|
||||||
|
<SelectedVersionString>$(X $selVer)</SelectedVersionString>
|
||||||
|
<NumberOfProgramsBeforeRestart>5</NumberOfProgramsBeforeRestart>
|
||||||
|
<RestartDelay>5</RestartDelay>
|
||||||
|
<StartupDelay>0</StartupDelay>
|
||||||
|
<UseOtherSoftwareForRotaryCal>false</UseOtherSoftwareForRotaryCal>
|
||||||
|
<UseOtherSoftwareForProbeCalibration>false</UseOtherSoftwareForProbeCalibration>
|
||||||
|
<RotaryCalSoftware />
|
||||||
|
<ProbeCalSoftware />
|
||||||
|
</PcDmisSettings>
|
||||||
|
<ModusSettings>
|
||||||
|
<IsEnabled>$modusEnabled</IsEnabled>
|
||||||
|
<VisibleDuringExecution>true</VisibleDuringExecution>
|
||||||
|
<ResetProgram />
|
||||||
|
<RotaryTableCalibrationProgram />
|
||||||
|
<TrackProbeCalibration>false</TrackProbeCalibration>
|
||||||
|
<TrackRotaryTableCalbiration>false</TrackRotaryTableCalbiration>
|
||||||
|
<ProbeCalibrationProgram />
|
||||||
|
<RotaryTableCalibration />
|
||||||
|
<IPPServerExe />
|
||||||
|
<IPPArguments />
|
||||||
|
<ModusArguments>-Organiser</ModusArguments>
|
||||||
|
</ModusSettings>
|
||||||
|
<MachineDefinition>
|
||||||
|
<CMMID>$(X $machineCmmId)</CMMID>
|
||||||
|
<MachineType />
|
||||||
|
<HeadType />
|
||||||
|
<ForceProbeCalibration>false</ForceProbeCalibration>
|
||||||
|
<ProbeCalSizeTol>$sizeTol</ProbeCalSizeTol>
|
||||||
|
<ProbeCalFormTolerance>$formTol</ProbeCalFormTolerance>
|
||||||
|
<ProbeCalibrationInterval>
|
||||||
|
<Hours>72</Hours>
|
||||||
|
<Minutes>0</Minutes>
|
||||||
|
</ProbeCalibrationInterval>
|
||||||
|
<RotaryTableCalibrationInterval>
|
||||||
|
<Hours>24</Hours>
|
||||||
|
<Minutes>0</Minutes>
|
||||||
|
</RotaryTableCalibrationInterval>
|
||||||
|
<MachineHasRotary>$hasRotary</MachineHasRotary>
|
||||||
|
<ForceRotaryCalibration>false</ForceRotaryCalibration>
|
||||||
|
<CMMMode>$(X $cmmMode)</CMMMode>
|
||||||
|
</MachineDefinition>
|
||||||
|
<Notifications UseAndon="false">
|
||||||
|
<Notifications />
|
||||||
|
</Notifications>
|
||||||
|
<ProgramConstants />
|
||||||
|
<CommonFiles />
|
||||||
|
</SettingsModel>
|
||||||
|
"@
|
||||||
|
|
||||||
|
$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)."
|
||||||
Reference in New Issue
Block a user