diff --git a/playbook/shopfloor-setup/Shopfloor/lib/Update-MachineNumber.ps1 b/playbook/shopfloor-setup/Shopfloor/lib/Update-MachineNumber.ps1 index d0bb905..c679848 100644 --- a/playbook/shopfloor-setup/Shopfloor/lib/Update-MachineNumber.ps1 +++ b/playbook/shopfloor-setup/Shopfloor/lib/Update-MachineNumber.ps1 @@ -41,6 +41,135 @@ function Get-CurrentMachineNumber { return $result } +function Update-MTConnectVariantName { + <# + .SYNOPSIS + Renames an MTConnect device across every file of a variant that carries it. + + .DESCRIPTION + Makino and Fanuc do not fit the one-file-one-service shape the other + variants do. + Per the vendor's "Installation Configuration and Trouble Shooting" guide, + the device name lives in several files that must be identical or the agent + may not start, and the Adapter Manager service rewrites the COMPLETE + configuration when it stops - so an edit made while it runs is thrown away. + + Makino, from a real bay capture: + Agent\Devices.xml + Agent\Devices.EDM.xml + Data\AdapterDataFile.xml MAKINO-1 + Agent\Agent.cfg Adapters { MAKINO-1 { Host=localhost ... } } + + Fanuc, likewise: + Agent\devices.xml + Adapter\devices.xml the adapter's own generated copy + Adapter\adapter.xml //3101 + + Editing only the agent's copy leaves the adapter streaming under the old + identity. Seven of thirty-four captured Fanuc bays are in that state - + agent on the right machine number, adapter still on the bay it was + imaged from (4007 on a 7801 Toshulin, 3031 on a 7804). + + The vendor's own path is ConfigurationManager.exe, a GUI tool, which a + remote renumber cannot drive. This does what it does, in the required + order: stop the manager and the agent, rewrite, start again. + + Anything that cannot be renamed is reported rather than half-applied - + a bay whose files disagree is worse than one still on its old name. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)][string]$NewNumber, + [Parameter(Mandatory)][hashtable[]]$Targets, + # Overridable so the rename can be exercised against a captured bay. + [Parameter(Mandatory)][string]$Root, + # Whatever owns the files goes down first - for Makino the Adapter + # Manager rewrites its configuration from memory as it stops, and for + # Fanuc the adapter holds its own copy. Overridable only so a test can + # drive the stop/start ordering with real services. + [string[]]$Services = @() + ) + + $out = @{ Updated = @(); Errors = @() } + + $present = @($Targets | + ForEach-Object { @{ Path = (Join-Path $Root $_.Path); Kind = $_.Kind } } | + Where-Object { Test-Path -LiteralPath $_.Path }) + if (-not $present) { return $out } + + # Collect EVERY name in play, not just one. A bay half-renamed by the old + # code carries two at once - Devices.xml already on the machine number while + # the adapter files still say MAKINO-n - and replacing only the name we + # happened to discover leaves the other behind, which is the very state this + # is meant to end. Gather them all, then converge every file on the target. + $names = New-Object System.Collections.Generic.HashSet[string] + foreach ($t in $present) { + $raw = Get-Content -LiteralPath $t.Path -Raw -ErrorAction SilentlyContinue + if (-not $raw) { continue } + switch ($t.Kind) { + 'attr' { if ($raw -match ']+name="([^"]+)"') { [void]$names.Add($matches[1]) } } + 'name' { if ($raw -match '\s*([^<]+?)\s*') { [void]$names.Add($matches[1]) } } + 'cfg' { + # The adapter block's key is the bare line inside Adapters { }. + if ($raw -match '(?ms)Adapters\s*\{\s*\r?\n\s*([^\s{]+)') { [void]$names.Add($matches[1]) } + } + } + } + [void]$names.Remove($NewNumber) + if ($names.Count -eq 0) { return $out } # every file already on the target + + # Agent is stopped too, so it re-reads Agent.cfg on the way back up. + $stopped = @() + foreach ($name in $Services) { + $svc = Get-Service -Name $name -ErrorAction SilentlyContinue + if (-not $svc) { continue } + if ($svc.Status -ne 'Stopped') { + try { + Stop-Service -Name $name -Force -ErrorAction Stop + $stopped += $name + } catch { + $out.Errors += "MTConnect: could not stop '$name' ($_); nothing was changed" + foreach ($back in $stopped) { + try { Start-Service -Name $back -ErrorAction Stop } catch {} + } + return $out + } + } + } + + foreach ($t in $present) { + try { + $content = Get-Content -LiteralPath $t.Path -Raw -ErrorAction Stop + $updated = $content + $from = @() + foreach ($old in $names) { + $escaped = [regex]::Escape($old) + switch ($t.Kind) { + 'attr' { $next = $updated -replace ('"' + $escaped + '"'), ('"' + $NewNumber + '"') } + 'name' { $next = $updated -replace ('\s*' + $escaped + '\s*'), ("$NewNumber") } + # The adapter block is keyed by a bare name on its own line. + 'cfg' { $next = $updated -replace ('(?m)^(\s*)' + $escaped + '(\s*)$'), ("`${1}$NewNumber`${2}") } + # // in the Fanuc adapter config + 'elem' { $next = $updated -replace ('()\s*' + $escaped + '\s*()'), ("`${1}$NewNumber`${2}") } + } + if ($next -ne $updated) { $from += $old; $updated = $next } + } + if ($updated -eq $content) { continue } # this file was already done + Set-Content -LiteralPath $t.Path -Value $updated -NoNewline -ErrorAction Stop + $out.Updated += "$($t.Path) ($($from -join ', ') -> $NewNumber)" + } catch { + $out.Errors += "MTConnect: rewrite failed for $($t.Path): $_" + } + } + + foreach ($name in $stopped) { + try { Start-Service -Name $name -ErrorAction Stop } + catch { $out.Errors += "MTConnect: restart of '$name' failed: $_" } + } + + return $out +} + function Update-MachineNumber { <# .SYNOPSIS @@ -307,11 +436,12 @@ function Update-MachineNumber { # devices.xml / Devices.xml entries collapse to the same file). Without # this filter, the Okuma branch on an Okuma PC sees the file already # rewritten by the (no-op) Fanuc branch and skips the service restart. + # Okuma and eDNC keep the name in one file only - verified across 7 Okuma + # and 15 eDNC bay captures - so the simple edit is right for them. Fanuc and + # Makino are handled below instead. $mtcVariants = @( - @{ Service='MTConnect Agent Fanuc'; Path='C:\MTConnect\Agent\devices.xml' }, - @{ Service='MTConnect Agent Okuma'; Path='C:\MTConnect\Agent\Devices.xml' }, - @{ Service='MTConnect eDNC Agent'; Path='C:\MTConnect_eDNC\Agent\Devices.xml' }, - @{ Service='Makino MTConnect Agent'; Path='C:\Makino-MTConnect\Agent\Devices.xml' } + @{ Service='MTConnect Agent Okuma'; Path='C:\MTConnect\Agent\Devices.xml' }, + @{ Service='MTConnect eDNC Agent'; Path='C:\MTConnect_eDNC\Agent\Devices.xml' } ) foreach ($v in $mtcVariants) { $svc = Get-Service -Name $v.Service -ErrorAction SilentlyContinue @@ -346,6 +476,43 @@ function Update-MachineNumber { } } + # --- Multi-file variants: rename every copy, services stopped first --- + # Fanuc and Makino each keep the device name in more than one file, and the + # agent's copy alone is not enough - the adapter goes on streaming under the + # old identity, which is how seven captured Fanuc bays ended up with the + # agent on the right machine number and the adapter on the bay it was + # imaged from. + $multiFileVariants = @( + @{ + Probe = 'MTConnect Agent Fanuc' + Root = 'C:\MTConnect' + Services = @('MTConnect Adapter Fanuc', 'MTConnect Agent Fanuc') + Targets = @( + @{ Path = 'Agent\devices.xml'; Kind = 'attr' }, + @{ Path = 'Adapter\devices.xml'; Kind = 'attr' }, + @{ Path = 'Adapter\adapter.xml'; Kind = 'elem' } + ) + }, + @{ + Probe = 'Makino MTConnect Agent' + Root = 'C:\Makino-MTConnect' + Services = @('Makino Adapter Manager', 'Makino MTConnect Agent') + Targets = @( + @{ Path = 'Agent\Devices.xml'; Kind = 'attr' }, + @{ Path = 'Agent\Devices.EDM.xml'; Kind = 'attr' }, + @{ Path = 'Data\AdapterDataFile.xml'; Kind = 'name' }, + @{ Path = 'Agent\Agent.cfg'; Kind = 'cfg' } + ) + } + ) + foreach ($variant in $multiFileVariants) { + if (-not (Get-Service -Name $variant.Probe -ErrorAction SilentlyContinue)) { continue } + $renamed = Update-MTConnectVariantName -NewNumber $NewNumber ` + -Root $variant.Root -Targets $variant.Targets -Services $variant.Services + $out.MTConnectUpdated += $renamed.Updated + $out.Errors += $renamed.Errors + } + # Keep C:\Enrollment\machine-number.txt in sync. Post-imaging GE-Enforce # prefers eDNC reg, but imaging-time scripts (Install-FromManifest # TargetMachineNumbers filter, 01-eDNC.ps1, 03-RestoreEDncConfig.ps1)