From f6d970c08d592d15f732c726988e8128a2bd8adc Mon Sep 17 00:00:00 2001 From: cproudlock Date: Mon, 1 Jun 2026 10:59:55 -0400 Subject: [PATCH] CMM: seed goCMM Shared Data Directory per bay + grant Users write goCMM (.NET x86) stores its program-source path in HKLM\SOFTWARE\ WOW6432Node\General Electric\goCMM value 'Shared Data Directory'. Being HKLM, a non-admin shopfloor user cannot set it via goCMM's UI (nor save a Selected Part Group switch). 09-Setup-CMM Step 2.7 now seeds the per-bay path (admin context at imaging) and grants BUILTIN\Users write on the key, mirroring the existing Step 2.5 install-dir ACL grant. - cmm-bay-config.csv: add shared_data_dir column (per-bay paths, CMM1-12). - resolve-cmm-bay-config.ps1: write C:\Enrollment\cmm\shareddatadir.txt (space-safe; e.g. CMM8 'Venture CMM8'). - 09-Setup-CMM.ps1: Step 2.7 reg seed + Users ACL on the goCMM key. Not yet deployed to the live server (held). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../gea-shopfloor-cmm/09-Setup-CMM.ps1 | 63 +++++++++++++++++++ .../gea-shopfloor-cmm/cmm-bay-config.csv | 26 ++++---- .../resolve-cmm-bay-config.ps1 | 16 ++++- 3 files changed, 89 insertions(+), 16 deletions(-) diff --git a/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 b/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 index 6e2f707..5648853 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-cmm/09-Setup-CMM.ps1 @@ -237,6 +237,69 @@ foreach ($exe in $pcdmisExes) { } } +# ============================================================================ +# Step 2.7: Seed goCMM Shared Data Directory + grant Users write on the key +# ============================================================================ +# goCMM (.NET x86 WPF app) stores its program-source path in the registry at +# HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM, value "Shared Data +# Directory" (the folder it browses for *.prg PC-DMIS measurement routines). +# It is a 32-bit MSI / 32-bit process, so both the install seed and runtime +# reads land in the WOW6432Node view. Because the value lives in HKLM, a +# non-admin shopfloor user cannot set it via goCMM's settings UI - and cannot +# save a "Selected Part Group" switch either (same key). So we do two things +# here in admin context: +# 1. Seed "Shared Data Directory" to the per-bay path resolved by +# resolve-cmm-bay-config.ps1 (C:\Enrollment\cmm\shareddatadir.txt). +# 2. Grant BUILTIN\Users write on the key so runtime writes (part-group +# switching, or a deliberate path change) succeed without elevation. +# This mirrors Step 2.5, which grants Users Modify on the install dirs. +$goCmmKey = 'HKLM:\SOFTWARE\WOW6432Node\General Electric\goCMM' + +# Path may contain internal spaces (e.g. CMM8 "Venture CMM8"). Get-Content +# + Trim keeps internal spaces; the value is passed as a single -Value arg, +# never through a command line, so the space cannot split the path. +$sharedDataDir = '' +$sddFile = 'C:\Enrollment\cmm\shareddatadir.txt' +if (Test-Path -LiteralPath $sddFile) { + $sharedDataDir = (Get-Content -LiteralPath $sddFile -First 1 -EA 0).Trim() +} + +if (-not (Test-Path $goCmmKey)) { + Write-CMMLog "goCMM key absent ($goCmmKey) - goCMM not installed or install failed; creating key so the seed/ACL still land" 'WARN' + try { New-Item -Path $goCmmKey -Force | Out-Null } catch { Write-CMMLog "Could not create $goCmmKey : $_" 'WARN' } +} + +if ($sharedDataDir) { + try { + New-ItemProperty -Path $goCmmKey -Name 'Shared Data Directory' -Value $sharedDataDir -PropertyType String -Force | Out-Null + Write-CMMLog "Set goCMM 'Shared Data Directory' = $sharedDataDir" + } catch { + Write-CMMLog "Failed to set goCMM 'Shared Data Directory': $_" 'WARN' + } +} else { + Write-CMMLog "No shareddatadir.txt (bay not in bay-config, or manual CMM ID) - leaving goCMM path unset" 'WARN' +} + +# Grant BUILTIN\Users ReadKey+WriteKey (WriteKey = SetValue + CreateSubKey). +# Registry ACEs use ContainerInherit only (no leaf objects in the registry). +if (Test-Path $goCmmKey) { + try { + $racl = Get-Acl -Path $goCmmKey + $rrule = New-Object System.Security.AccessControl.RegistryAccessRule( + 'BUILTIN\Users', + 'ReadKey,WriteKey', + 'ContainerInherit', + 'None', + 'Allow' + ) + $racl.AddAccessRule($rrule) + Set-Acl -Path $goCmmKey -AclObject $racl -ErrorAction Stop + Write-CMMLog "Granted BUILTIN\Users write on $goCmmKey" + } catch { + Write-CMMLog "Failed to set ACL on $goCmmKey : $_" 'WARN' + } +} + # ============================================================================ # Step 3: Conditional cleanup of the bootstrap staging dir # ============================================================================ diff --git a/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-bay-config.csv b/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-bay-config.csv index b583f46..f97dea7 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-bay-config.csv +++ b/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-bay-config.csv @@ -1,13 +1,13 @@ -cmm_id,pcdmis_version,doda -CMM1,2019,no -CMM2,2019,no -CMM3,2019,no -CMM4,2016,no -CMM5,2019,no -CMM6,2019,no -CMM7,2019,no -CMM8,2019,no -CMM9,2019,no -CMM10,2016,no -CMM11,2026,no -CMM12,2026,no +cmm_id,pcdmis_version,doda,shared_data_dir +CMM1,2019,no,S:\CMM\CMM1\HPTCMM1 +CMM2,2019,no,S:\CMM\CMM2\HPT +CMM3,2019,no,S:\CMM\CMM3\VENTURE_CMM3 +CMM4,2016,no,S:\CMM\CMM4\Spool +CMM5,2019,no,S:\CMM\CMM5\BLISKCMM5 +CMM6,2019,no,S:\CMM\CMM6\BLISKCMM6 +CMM7,2019,no,S:\CMM\CMM7\VENTURE_CMM7 +CMM8,2019,no,S:\CMM\CMM8\Venture CMM8 +CMM9,2019,no,S:\CMM\CMM9\BLISKCMM9 +CMM10,2016,no,S:\CMM\CMM10\Spool +CMM11,2026,no,S:\CMM\CMM11\Spool +CMM12,2026,no,S:\CMM\CMM12\Spool diff --git a/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 b/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 index 8d2304d..8b4234b 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 @@ -40,11 +40,21 @@ if (-not (Test-Path $OutDir)) { $version = $match.pcdmis_version.Trim() $doda = $match.doda.Trim().ToLower() +# shared_data_dir may legitimately contain spaces (e.g. CMM8 "Venture CMM8"). +# Trim() strips only leading/trailing whitespace, never internal spaces. +$sharedDataDir = '' +if ($match.PSObject.Properties['shared_data_dir'] -and $match.shared_data_dir) { + $sharedDataDir = $match.shared_data_dir.Trim() +} [System.IO.File]::WriteAllText((Join-Path $OutDir 'version.txt'), $version) [System.IO.File]::WriteAllText((Join-Path $OutDir 'doda.txt'), $doda) +if ($sharedDataDir) { + [System.IO.File]::WriteAllText((Join-Path $OutDir 'shareddatadir.txt'), $sharedDataDir) +} -Write-Host "Resolved $CmmId -> PC-DMIS $version, DODA=$doda" -Write-Host " version.txt -> $OutDir\version.txt" -Write-Host " doda.txt -> $OutDir\doda.txt" +Write-Host "Resolved $CmmId -> PC-DMIS $version, DODA=$doda, SharedDataDir=$(if ($sharedDataDir) { $sharedDataDir } else { '(none)' })" +Write-Host " version.txt -> $OutDir\version.txt" +Write-Host " doda.txt -> $OutDir\doda.txt" +if ($sharedDataDir) { Write-Host " shareddatadir.txt -> $OutDir\shareddatadir.txt" } exit 0