<# .SYNOPSIS Translates a v1 OtOpcUa.Host appsettings.json into a v2 DriverInstance.DriverConfig JSON blob suitable for upserting into the central Configuration DB. .DESCRIPTION Phase 2 Stream D.3 — moves the legacy MxAccess + GalaxyRepository + Historian sections out of node-local appsettings.json and into the central DB so each node only needs Cluster.NodeId + ClusterId + DB conn (per decision #18). Idempotent + dry-run-able. Output shape matches the Galaxy DriverType schema in `docs/v2/plan.md` §"Galaxy DriverConfig": { "MxAccess": { "ClientName": "...", "RequestTimeoutSeconds": 30 }, "Database": { "ConnectionString": "...", "PollIntervalSeconds": 60 }, "Historian": { "Enabled": false } } .PARAMETER AppSettingsPath Path to the v1 appsettings.json. Defaults to ../../src/ZB.MOM.WW.OtOpcUa.Host/appsettings.json relative to the script. .PARAMETER OutputPath Where to write the generated DriverConfig JSON. Defaults to stdout. .PARAMETER DryRun Print what would be written without writing. .EXAMPLE pwsh ./Migrate-AppSettings-To-DriverConfig.ps1 -AppSettingsPath C:\OtOpcUa\appsettings.json -OutputPath C:\tmp\galaxy-driverconfig.json #> [CmdletBinding()] param( [string]$AppSettingsPath, [string]$OutputPath, [switch]$DryRun ) $ErrorActionPreference = 'Stop' if (-not $AppSettingsPath) { $AppSettingsPath = Join-Path (Split-Path -Parent $PSScriptRoot) '..\src\ZB.MOM.WW.OtOpcUa.Host\appsettings.json' } if (-not (Test-Path $AppSettingsPath)) { Write-Error "AppSettings file not found: $AppSettingsPath" exit 1 } $src = Get-Content -Raw $AppSettingsPath | ConvertFrom-Json $mx = $src.MxAccess $gr = $src.GalaxyRepository $hi = $src.Historian $driverConfig = [ordered]@{ MxAccess = [ordered]@{ ClientName = $mx.ClientName NodeName = $mx.NodeName GalaxyName = $mx.GalaxyName RequestTimeoutSeconds = $mx.ReadTimeoutSeconds WriteTimeoutSeconds = $mx.WriteTimeoutSeconds MaxConcurrentOps = $mx.MaxConcurrentOperations MonitorIntervalSec = $mx.MonitorIntervalSeconds AutoReconnect = $mx.AutoReconnect ProbeTag = $mx.ProbeTag } Database = [ordered]@{ ConnectionString = $gr.ConnectionString ChangeDetectionIntervalSec = $gr.ChangeDetectionIntervalSeconds CommandTimeoutSeconds = $gr.CommandTimeoutSeconds ExtendedAttributes = $gr.ExtendedAttributes Scope = $gr.Scope PlatformName = $gr.PlatformName } Historian = [ordered]@{ Enabled = if ($null -ne $hi -and $null -ne $hi.Enabled) { $hi.Enabled } else { $false } } } # Strip null-valued leaves so the resulting JSON is compact and round-trippable. function Remove-Nulls($obj) { $keys = @($obj.Keys) foreach ($k in $keys) { if ($null -eq $obj[$k]) { $obj.Remove($k) | Out-Null } elseif ($obj[$k] -is [System.Collections.Specialized.OrderedDictionary]) { Remove-Nulls $obj[$k] } } } Remove-Nulls $driverConfig $json = $driverConfig | ConvertTo-Json -Depth 8 if ($DryRun) { Write-Host "=== DriverConfig (dry-run, would write to $OutputPath) ===" Write-Host $json return } if ($OutputPath) { $dir = Split-Path -Parent $OutputPath if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null } Set-Content -Path $OutputPath -Value $json -Encoding UTF8 Write-Host "Wrote DriverConfig to $OutputPath" } else { $json }