shopfloor: CMM PC-DMIS version gate, ShopDB reporter fixes, staging self-heal
- lib Install-FromManifest 2.5->2.6: add _CmmVersion per-entry filter (reads C:\Enrollment\cmm\version.txt). Lifted the version gate out of 09-Setup-CMM into the shared lib so imaging and GE-Enforce apply it identically and cannot drift (root cause of PC-DMIS 2016 installing on every CMM). - Install-goCMMSettings: canonicalize the part-group share host to the FQDN in both the registry and ApplicationSettings.xml. Handles bare \\tsgwp00525\ and the legacy rd.ds.ge.com domain; idempotent. VM-tested. - Report-AssetToShopDB: resolve the machine number eDNC registry first, then fall back to C:\Enrollment\machine-number.txt (matches the lib resolution order) so a freshly imaged PC still reports its number for the PC-machine relationship. - Add Update-CMMEnforcer.ps1/.bat: update one CMM's local lib to the gated version and self-heal its PC-DMIS version. - Add Debug-ShopDBReporting.ps1/.bat: one-shot reporter triage (preconditions, client log, live test POST, verdict). - Add Verify-And-Heal-Staging.ps1/.bat: post-boot check that every imaging payload arrived and re-pull anything missing from the share, including the CMM bundle and the selected bay's backup (the payload that times out in WinPE). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,25 @@ param(
|
||||
$DefaultRewrites = @(
|
||||
@{ From = 'rd.ds.ge.com'; To = 'wjs.geaerospace.net' } # WJ legacy domain -> new domain
|
||||
)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Part-group share host canonicalization. Bays were captured with three
|
||||
# inconsistent forms of the share host in the goCMM 'Selected Part Group' /
|
||||
# ApplicationSettings.xml <PartGroup FullName>:
|
||||
# \\tsgwp00525\... (bare hostname - DNS-suffix dependent)
|
||||
# \\tsgwp00525.rd.ds.ge.com\... (legacy GE corp domain - dead on the
|
||||
# air-gapped shopfloor net)
|
||||
# \\tsgwp00525.wjs.geaerospace.net\... (correct)
|
||||
# The DefaultRewrites above only fix the middle form. goCMM DISPLAYS the part
|
||||
# group from ApplicationSettings.xml, so the bare form survived into the UI.
|
||||
# Pin every form to the FQDN in BOTH the registry and the XML. The regex
|
||||
# matches the UNC host with an optional domain suffix and is idempotent (an
|
||||
# already-correct FQDN maps to itself). Disabled by -NoDefaultRewrite.
|
||||
$PartGroupHostShort = 'tsgwp00525'
|
||||
$PartGroupHostFqdn = 'tsgwp00525.wjs.geaerospace.net'
|
||||
# \\HOST or \\HOST.any.domain (followed by the next path backslash) -> \\FQDN
|
||||
$PartGroupHostRx = '(?i)\\\\' + [regex]::Escape($PartGroupHostShort) + '(?:\.[A-Za-z0-9.\-]+)?(?=\\)'
|
||||
$PartGroupHostTo = '\\' + $PartGroupHostFqdn
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$logDir = 'C:\Logs\CMM'
|
||||
@@ -148,6 +167,45 @@ if ($rewrites.Count -gt 0) {
|
||||
}
|
||||
}
|
||||
|
||||
# --- Canonicalize the part-group share host to the FQDN in reg + XML ---
|
||||
# Runs AFTER the geaofi robocopy (so ApplicationSettings.xml is in place)
|
||||
# and AFTER the literal rewrites above. Idempotent. This is what makes
|
||||
# goCMM show the FQDN regardless of which form the bay was captured with.
|
||||
if (-not $NoDefaultRewrite) {
|
||||
# registry: every string value under the goCMM key
|
||||
if (Test-Path $goCmmKey) {
|
||||
try {
|
||||
$props = Get-ItemProperty -Path $goCmmKey
|
||||
foreach ($p in $props.PSObject.Properties) {
|
||||
if ($p.Name -like 'PS*') { continue }
|
||||
if (($p.Value -is [string]) -and ([regex]::IsMatch($p.Value, $PartGroupHostRx))) {
|
||||
$new = [regex]::Replace($p.Value, $PartGroupHostRx, $PartGroupHostTo)
|
||||
if ($new -ne $p.Value) {
|
||||
Set-ItemProperty -Path $goCmmKey -Name $p.Name -Value $new
|
||||
Log " host-canon reg [$($p.Name)] -> $new"
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { Log " WARN: host canonicalize (reg) failed: $($_.Exception.Message)" }
|
||||
}
|
||||
# files: every text file under the Shared Data Directory (incl ApplicationSettings.xml)
|
||||
if (Test-Path $sharedDir) {
|
||||
$utf8NoBomHost = New-Object System.Text.UTF8Encoding($false)
|
||||
Get-ChildItem -Path $sharedDir -Recurse -File -Include *.xml,*.txt,*.csv,*.config,*.ini,*.bas -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
try {
|
||||
$c = [System.IO.File]::ReadAllText($_.FullName)
|
||||
if ([regex]::IsMatch($c, $PartGroupHostRx)) {
|
||||
$nc = [regex]::Replace($c, $PartGroupHostRx, $PartGroupHostTo)
|
||||
if ($nc -ne $c) {
|
||||
[System.IO.File]::WriteAllText($_.FullName, $nc, $utf8NoBomHost)
|
||||
Log " host-canon file [$($_.Name)] rewritten"
|
||||
}
|
||||
}
|
||||
} catch { Log " WARN: host canonicalize $($_.FullName): $($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Grant BUILTIN\Users ReadKey+WriteKey on the reg key (goCMM opens it writable:true to read) ---
|
||||
if (Test-Path $goCmmKey) {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
@echo off
|
||||
REM ==========================================================================
|
||||
REM Update-CMMEnforcer.bat - update ONE CMM's local GE-Enforce lib to the
|
||||
REM version that honors the PC-DMIS _CmmVersion gate, then self-heal.
|
||||
REM
|
||||
REM Usage (run on the CMM PC):
|
||||
REM Update-CMMEnforcer.bat update lib + report wrong versions + self-heal
|
||||
REM Update-CMMEnforcer.bat /remove also uninstall the wrong PC-DMIS version(s)
|
||||
REM
|
||||
REM Pulls the lib from the tsgwp00525 share by default. To use a USB copy:
|
||||
REM Update-CMMEnforcer.bat /src "D:\Install-FromManifest.ps1"
|
||||
REM Update-CMMEnforcer.bat /remove /src "D:\Install-FromManifest.ps1"
|
||||
REM ==========================================================================
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
REM --- self-elevate to administrator ---
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo Requesting administrator elevation...
|
||||
powershell -NoProfile -Command "Start-Process -Verb RunAs -FilePath '%~f0' -ArgumentList '%*'"
|
||||
exit /b
|
||||
)
|
||||
|
||||
set "PS=%~dp0Update-CMMEnforcer.ps1"
|
||||
set "ARGS="
|
||||
|
||||
:parse
|
||||
if "%~1"=="" goto run
|
||||
if /I "%~1"=="/remove" set "ARGS=!ARGS! -RemoveWrongVersions" & shift & goto parse
|
||||
if /I "%~1"=="/src" set "ARGS=!ARGS! -SourceLib '%~2'" & shift & shift & goto parse
|
||||
shift
|
||||
goto parse
|
||||
|
||||
:run
|
||||
echo Running: powershell -File "%PS%" !ARGS!
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS%" !ARGS!
|
||||
set "RC=%errorlevel%"
|
||||
echo.
|
||||
echo Exit code: %RC%
|
||||
pause
|
||||
endlocal
|
||||
@@ -0,0 +1,131 @@
|
||||
<#
|
||||
Update-CMMEnforcer.ps1
|
||||
|
||||
Bring ONE CMM shopfloor PC's local GE-Enforce engine up to the lib version
|
||||
that understands the PC-DMIS _CmmVersion gate (lib MINOR >= 6), then run a
|
||||
single enforce cycle so it self-heals to this bay's correct PC-DMIS version.
|
||||
|
||||
Why this exists: GE-Enforce runs its lib from LOCAL
|
||||
C:\Program Files\GE\Shopfloor\lib\ and does NOT auto-update from the share.
|
||||
A bay running an older lib ignores the _CmmVersion tags in the synced CMM
|
||||
manifest and would install every version it does not detect. This script
|
||||
pushes the new lib locally so the gate takes effect.
|
||||
|
||||
Run as administrator on the CMM PC.
|
||||
|
||||
Params:
|
||||
-SourceLib Explicit path to the 2.6+ lib (e.g. a USB copy). If omitted,
|
||||
pulls from the tsgwp00525 share (-ShareRoot).
|
||||
-ShareRoot Share root holding common\lib\Install-FromManifest.ps1.
|
||||
-RemoveWrongVersions Uninstall any installed PC-DMIS version that does not
|
||||
match this bay's C:\Enrollment\cmm\version.txt.
|
||||
-NoEnforceRun Skip the post-update enforce cycle (just update + report).
|
||||
#>
|
||||
param(
|
||||
[string]$SourceLib,
|
||||
[string]$ShareRoot = '\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor',
|
||||
[string]$InstallRoot = 'C:\Program Files\GE\Shopfloor',
|
||||
[int]$MinLibMinor = 6,
|
||||
[switch]$RemoveWrongVersions,
|
||||
[switch]$NoEnforceRun
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
function Log($m){ Write-Host ("[Update-CMMEnforcer] {0}" -f $m) }
|
||||
|
||||
function Get-LibMinor($path){
|
||||
if (-not (Test-Path -LiteralPath $path)) { return -1 }
|
||||
$m = Select-String -Path $path -Pattern 'LIB_MANIFEST_MINOR\s*=\s*(\d+)' -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($m) { return [int]$m.Matches[0].Groups[1].Value } else { return -1 }
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Resolve the source lib (USB override or share) and validate its version
|
||||
# ---------------------------------------------------------------------------
|
||||
if (-not $SourceLib) { $SourceLib = Join-Path $ShareRoot 'common\lib\Install-FromManifest.ps1' }
|
||||
if (-not (Test-Path -LiteralPath $SourceLib)) {
|
||||
throw "Source lib not found: $SourceLib (if using the share, confirm you are authenticated to it; or pass -SourceLib <path to a USB copy>)"
|
||||
}
|
||||
$srcMinor = Get-LibMinor $SourceLib
|
||||
if ($srcMinor -lt $MinLibMinor) {
|
||||
throw "Source lib is MINOR=$srcMinor but >= $MinLibMinor is required. Point -SourceLib at the updated lib (or sync the share first)."
|
||||
}
|
||||
|
||||
$dstLib = Join-Path $InstallRoot 'lib\Install-FromManifest.ps1'
|
||||
$oldMinor = Get-LibMinor $dstLib
|
||||
Log "Source lib MINOR=$srcMinor ($SourceLib)"
|
||||
Log "Local lib MINOR=$oldMinor ($dstLib)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Back up + copy the lib into place, verify
|
||||
# ---------------------------------------------------------------------------
|
||||
$libDir = Split-Path -Parent $dstLib
|
||||
New-Item -ItemType Directory -Path $libDir -Force | Out-Null
|
||||
if (Test-Path -LiteralPath $dstLib) {
|
||||
$bak = "$dstLib.bak-{0}" -f (Get-Date -Format 'yyyyMMdd-HHmmss')
|
||||
Copy-Item -LiteralPath $dstLib -Destination $bak -Force
|
||||
Log "Backed up local lib -> $bak"
|
||||
}
|
||||
Copy-Item -LiteralPath $SourceLib -Destination $dstLib -Force
|
||||
$newMinor = Get-LibMinor $dstLib
|
||||
if ($newMinor -lt $MinLibMinor) { throw "Copy did not take - local lib still MINOR=$newMinor" }
|
||||
Log "Local lib updated: MINOR $oldMinor -> $newMinor (OK - gate is now active)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Report bay version + which PC-DMIS versions are installed
|
||||
# ---------------------------------------------------------------------------
|
||||
$verFile = 'C:\Enrollment\cmm\version.txt'
|
||||
$bayVer = if (Test-Path -LiteralPath $verFile) { (Get-Content -LiteralPath $verFile -First 1).Trim() } else { '' }
|
||||
Log "Bay PC-DMIS version (version.txt): $(if($bayVer){$bayVer}else{'(none - bay not resolved; gate will install-all)'})"
|
||||
|
||||
$pcd = @(
|
||||
[pscustomobject]@{ Ver='2016'; Guid='{5389B196-81F0-44A9-A073-4C1D72041F09}'; Name='PC-DMIS 2016.0' },
|
||||
[pscustomobject]@{ Ver='2019'; Guid='{49DBE7F9-228A-4E66-8BB5-DB5A446DCAE7}'; Name='PC-DMIS 2019 R2' },
|
||||
[pscustomobject]@{ Ver='2026'; Guid='{81BACE1B-FB08-4DCF-8100-79911AD3EC1E}'; Name='PC-DMIS 2026.1' }
|
||||
)
|
||||
function Test-MsiInstalled($guid){
|
||||
foreach($r in @(
|
||||
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$guid",
|
||||
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$guid")){
|
||||
if (Test-Path -LiteralPath $r) { return $true }
|
||||
}
|
||||
return $false
|
||||
}
|
||||
$installed = @($pcd | Where-Object { Test-MsiInstalled $_.Guid } | ForEach-Object { $_.Ver })
|
||||
Log "PC-DMIS installed: $(if($installed){$installed -join ', '}else{'none'})"
|
||||
$wrong = @($installed | Where-Object { $bayVer -and $_ -ne $bayVer })
|
||||
if ($wrong.Count) { Log "WRONG for this bay (expected $bayVer): $($wrong -join ', ')" }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Optionally uninstall wrong versions
|
||||
# ---------------------------------------------------------------------------
|
||||
if ($RemoveWrongVersions -and $bayVer -and $wrong.Count) {
|
||||
foreach($p in ($pcd | Where-Object { $wrong -contains $_.Ver })){
|
||||
Log "Uninstalling $($p.Name) $($p.Guid) ..."
|
||||
$proc = Start-Process msiexec.exe -ArgumentList "/x $($p.Guid) /qn /norestart REBOOT=ReallySuppress" -Wait -PassThru -WindowStyle Hidden
|
||||
Log " msiexec exit $($proc.ExitCode) $(if($proc.ExitCode -in 0,1605,3010){'(OK)'}else{'(check)'})"
|
||||
}
|
||||
} elseif ($wrong.Count) {
|
||||
Log "Re-run with -RemoveWrongVersions to uninstall the wrong version(s)."
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Kick one enforce cycle so it self-heals against the gated manifest
|
||||
# ---------------------------------------------------------------------------
|
||||
if (-not $NoEnforceRun) {
|
||||
$task = Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.TaskName -match 'Enforce' } | Select-Object -First 1
|
||||
$enf = Join-Path $InstallRoot 'GE-Enforce.ps1'
|
||||
if ($task) {
|
||||
Log "Triggering scheduled task '$($task.TaskName)' ..."
|
||||
Start-ScheduledTask -TaskName $task.TaskName -TaskPath $task.TaskPath
|
||||
Log "Triggered. Watch C:\Logs\Shopfloor\enforce-*.log"
|
||||
} elseif (Test-Path -LiteralPath $enf) {
|
||||
Log "No enforce task found - running GE-Enforce.ps1 directly ..."
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $enf | Out-Null
|
||||
Log "Enforce cycle done. See C:\Logs\Shopfloor\enforce-*.log"
|
||||
} else {
|
||||
Log "Could not find an enforce task or $enf - trigger enforcement manually."
|
||||
}
|
||||
}
|
||||
|
||||
Log "DONE. lib MINOR=$newMinor | bay=$(if($bayVer){$bayVer}else{'?'}) | installed=$($installed -join ',')"
|
||||
Log "Verify next enforce log shows the 2016/2026 entries skipped with '_CmmVersion filter'."
|
||||
Reference in New Issue
Block a user