Documentation: - Add ShopDB-API.md with full API reference (all GET/POST endpoints) - Add detailed docs for Update-ShopfloorPCs-Remote, Invoke-RemoteMaintenance, Update-PC-CompleteAsset - Add DATA_COLLECTION_PARITY.md comparing local vs remote data collection - Add HTML versions of all documentation with styled code blocks - Document software deployment mechanism and how to add new apps - Document deprecated scripts (Invoke-RemoteAssetCollection, Install-KioskApp) Script Updates: - Update deployment source paths to network share (tsgwp00525.wjs.geaerospace.net) - InstallDashboard: \\...\scripts\Dashboard\GEAerospaceDashboardSetup.exe - InstallLobbyDisplay: \\...\scripts\LobbyDisplay\GEAerospaceLobbyDisplaySetup.exe - UpdateEMxAuthToken: \\...\scripts\eMx\eMxInfo.txt - DeployUDCWebServerConfig: \\...\scripts\UDC\udc_webserver_settings.json - Update machine network detection to include 100.0.0.* for CMM cases - Rename PC Type #9 from "Part Marker" to "Inspection" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
491 lines
23 KiB
PowerShell
491 lines
23 KiB
PowerShell
# Functions to collect shopfloor PC network and communication configurations
|
|
|
|
# Function to get all network interfaces and their configurations
|
|
function Get-NetworkInterfaceConfig {
|
|
Write-Host " Collecting network interface information..." -ForegroundColor Yellow
|
|
|
|
$interfaces = @()
|
|
|
|
try {
|
|
# Get all network adapters with IP configurations
|
|
$adapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' }
|
|
|
|
foreach ($adapter in $adapters) {
|
|
$ipConfig = Get-NetIPConfiguration -InterfaceIndex $adapter.ifIndex -ErrorAction SilentlyContinue
|
|
|
|
if ($ipConfig -and $ipConfig.IPv4Address) {
|
|
foreach ($ip in $ipConfig.IPv4Address) {
|
|
$gateway = if ($ipConfig.IPv4DefaultGateway) { $ipConfig.IPv4DefaultGateway[0].NextHop } else { $null }
|
|
|
|
# Determine if this is a machine network (192.168.*.* or 100.0.0.* for CMM)
|
|
$isMachineNetwork = $ip.IPAddress -match '^192\.168\.' -or $ip.IPAddress -match '^100\.0\.0\.'
|
|
# Determine if this is the primary network (10.134.*.*)
|
|
$isPrimary = $ip.IPAddress -match '^10\.134\.'
|
|
|
|
$interface = @{
|
|
InterfaceName = $adapter.Name
|
|
IPAddress = $ip.IPAddress
|
|
SubnetMask = $ip.PrefixLength # Will need conversion
|
|
DefaultGateway = $gateway
|
|
MACAddress = $adapter.MacAddress
|
|
IsDHCP = if ($ipConfig.NetIPv4Interface.Dhcp -eq 'Enabled') { 1 } else { 0 }
|
|
IsActive = 1
|
|
IsMachineNetwork = if ($isMachineNetwork) { 1 } else { 0 }
|
|
IsPrimary = if ($isPrimary) { 1 } else { 0 }
|
|
}
|
|
|
|
$interfaces += $interface
|
|
|
|
if ($isPrimary) {
|
|
Write-Host " Found PRIMARY network: $($ip.IPAddress) on $($adapter.Name)" -ForegroundColor Green
|
|
}
|
|
elseif ($isMachineNetwork) {
|
|
Write-Host " Found machine network: $($ip.IPAddress) on $($adapter.Name)" -ForegroundColor Cyan
|
|
}
|
|
else {
|
|
Write-Host " Found network: $($ip.IPAddress) on $($adapter.Name)" -ForegroundColor Gray
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host " Error collecting network info: $_" -ForegroundColor Red
|
|
}
|
|
|
|
# Alternative method using WMI if NetAdapter cmdlets fail
|
|
if ($interfaces.Count -eq 0) {
|
|
try {
|
|
$wmiAdapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -eq $true }
|
|
|
|
foreach ($adapter in $wmiAdapters) {
|
|
if ($adapter.IPAddress) {
|
|
foreach ($i in 0..($adapter.IPAddress.Count - 1)) {
|
|
$ip = $adapter.IPAddress[$i]
|
|
|
|
# Skip IPv6 addresses
|
|
if ($ip -match ':') { continue }
|
|
|
|
$isMachineNetwork = $ip -match '^192\.168\.' -or $ip -match '^100\.0\.0\.'
|
|
$isPrimary = $ip -match '^10\.134\.'
|
|
|
|
$interface = @{
|
|
InterfaceName = $adapter.Description
|
|
IPAddress = $ip
|
|
SubnetMask = $adapter.IPSubnet[$i]
|
|
DefaultGateway = if ($adapter.DefaultIPGateway) { $adapter.DefaultIPGateway[0] } else { $null }
|
|
MACAddress = $adapter.MACAddress
|
|
IsDHCP = $adapter.DHCPEnabled
|
|
IsActive = 1
|
|
IsMachineNetwork = if ($isMachineNetwork) { 1 } else { 0 }
|
|
IsPrimary = if ($isPrimary) { 1 } else { 0 }
|
|
}
|
|
|
|
$interfaces += $interface
|
|
|
|
if ($isPrimary) {
|
|
Write-Host " Found PRIMARY network: $ip on $($adapter.Description)" -ForegroundColor Green
|
|
}
|
|
elseif ($isMachineNetwork) {
|
|
Write-Host " Found machine network: $ip on $($adapter.Description)" -ForegroundColor Cyan
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host " WMI method also failed: $_" -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
return $interfaces
|
|
}
|
|
|
|
# Function to get serial port configurations from registry
|
|
function Get-SerialPortConfig {
|
|
Write-Host " Collecting serial port configurations..." -ForegroundColor Yellow
|
|
|
|
$configs = @()
|
|
|
|
# Registry paths to check
|
|
$registryPaths = @{
|
|
'Serial' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\Serial', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\Serial')
|
|
'Mark' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\Mark', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\Mark')
|
|
'PPDCS' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\PPDCS', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\PPDCS')
|
|
'TQM9030' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\TQM9030', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\TQM9030')
|
|
'TQMCaron' = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\TQMCaron', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\TQMCaron')
|
|
}
|
|
|
|
foreach ($configType in $registryPaths.Keys) {
|
|
foreach ($path in $registryPaths[$configType]) {
|
|
if (Test-Path $path) {
|
|
try {
|
|
$regValues = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
|
|
|
|
if ($regValues) {
|
|
$config = @{
|
|
ConfigType = $configType
|
|
PortID = $regValues.'Port Id' -replace 'Port Id2', ''
|
|
Baud = $regValues.Baud
|
|
DataBits = $regValues.'Data Bits'
|
|
StopBits = $regValues.'Stop Bits'
|
|
Parity = $regValues.Parity
|
|
CRLF = $regValues.CRLF
|
|
IPAddress = $null
|
|
SocketNo = $null
|
|
AdditionalSettings = @{}
|
|
}
|
|
|
|
# Collect any additional settings
|
|
$standardKeys = @('Port Id', 'Baud', 'Data Bits', 'Stop Bits', 'Parity', 'CRLF', 'PSPath', 'PSParentPath', 'PSChildName', 'PSProvider')
|
|
foreach ($prop in $regValues.PSObject.Properties) {
|
|
if ($prop.Name -notin $standardKeys -and $prop.Value) {
|
|
$config.AdditionalSettings[$prop.Name] = $prop.Value
|
|
}
|
|
}
|
|
|
|
# Convert additional settings to JSON
|
|
if ($config.AdditionalSettings.Count -gt 0) {
|
|
$config.AdditionalSettings = $config.AdditionalSettings | ConvertTo-Json -Compress
|
|
}
|
|
else {
|
|
$config.AdditionalSettings = $null
|
|
}
|
|
|
|
if ($config.PortID) {
|
|
$configs += $config
|
|
Write-Host " Found $configType config: Port $($config.PortID), Baud $($config.Baud)" -ForegroundColor Cyan
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host " Error reading $configType registry: $_" -ForegroundColor Red
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Check for eFocas configuration (network-based)
|
|
$efocasPaths = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\eFocas', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\eFocas')
|
|
foreach ($path in $efocasPaths) {
|
|
if (Test-Path $path) {
|
|
try {
|
|
$regValues = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
|
|
|
|
if ($regValues -and $regValues.IpAddr) {
|
|
$config = @{
|
|
ConfigType = 'eFocas'
|
|
PortID = $null
|
|
Baud = $null
|
|
DataBits = $null
|
|
StopBits = $null
|
|
Parity = $null
|
|
CRLF = $null
|
|
IPAddress = $regValues.IpAddr
|
|
SocketNo = $regValues.SocketNo
|
|
AdditionalSettings = @{
|
|
DualPath = $regValues.DualPath
|
|
Path1Name = $regValues.Path1Name
|
|
Path2Name = $regValues.Path2Name
|
|
Danobat = $regValues.Danobat
|
|
DataServer = $regValues.DataServer
|
|
} | ConvertTo-Json -Compress
|
|
}
|
|
|
|
$configs += $config
|
|
Write-Host " Found eFocas config: IP $($config.IPAddress), Socket $($config.SocketNo)" -ForegroundColor Cyan
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host " Error reading eFocas registry: $_" -ForegroundColor Red
|
|
}
|
|
}
|
|
}
|
|
|
|
return $configs
|
|
}
|
|
|
|
# Function to get GE Aircraft Engines registry information and DualPath configuration
|
|
function Get-GERegistryInfo {
|
|
Write-Host " Collecting GE Aircraft Engines registry information..." -ForegroundColor Yellow
|
|
|
|
$geInfo = @{
|
|
Registry32Bit = $false
|
|
Registry64Bit = $false
|
|
DualPathEnabled = $null
|
|
Path1Name = $null
|
|
Path2Name = $null
|
|
RegistryNotes = @{}
|
|
}
|
|
|
|
# Check both 32-bit and 64-bit registry paths
|
|
$registryPaths = @{
|
|
'32bit' = 'HKLM:\SOFTWARE\GE Aircraft Engines'
|
|
'64bit' = 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines'
|
|
}
|
|
|
|
foreach ($pathType in $registryPaths.Keys) {
|
|
$basePath = $registryPaths[$pathType]
|
|
Write-Host " Checking $pathType registry: $basePath" -ForegroundColor Gray
|
|
|
|
if (Test-Path $basePath) {
|
|
Write-Host " [FOUND] GE Aircraft Engines in $pathType registry" -ForegroundColor Green
|
|
|
|
if ($pathType -eq '32bit') {
|
|
$geInfo.Registry32Bit = $true
|
|
} else {
|
|
$geInfo.Registry64Bit = $true
|
|
}
|
|
|
|
# Collect information about what's under GE Aircraft Engines
|
|
try {
|
|
$subKeys = Get-ChildItem -Path $basePath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name | ForEach-Object { Split-Path $_ -Leaf }
|
|
$geInfo.RegistryNotes[$pathType] = @{
|
|
BasePath = $basePath
|
|
SubKeys = $subKeys -join ', '
|
|
Found = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
}
|
|
Write-Host " Sub-keys found: $($subKeys -join ', ')" -ForegroundColor Cyan
|
|
}
|
|
catch {
|
|
Write-Host " Error reading sub-keys: $_" -ForegroundColor Red
|
|
$geInfo.RegistryNotes[$pathType] = @{
|
|
Error = $_.Exception.Message
|
|
}
|
|
}
|
|
|
|
# Check for DualPath configuration in eFocas
|
|
$efocasPath = "$basePath\DNC\eFocas"
|
|
if (Test-Path $efocasPath) {
|
|
Write-Host " Checking eFocas configuration for DualPath..." -ForegroundColor Yellow
|
|
try {
|
|
$efocasValues = Get-ItemProperty -Path $efocasPath -ErrorAction SilentlyContinue
|
|
|
|
if ($efocasValues.DualPath) {
|
|
Write-Host " [FOUND] DualPath = $($efocasValues.DualPath)" -ForegroundColor Green
|
|
|
|
# Handle case where both registry locations exist - prioritize settings from first found
|
|
if ($geInfo.DualPathEnabled -eq $null) {
|
|
$geInfo.DualPathEnabled = $efocasValues.DualPath -eq 'YES'
|
|
Write-Host " Setting DualPath from $pathType registry: $($geInfo.DualPathEnabled)" -ForegroundColor Cyan
|
|
} else {
|
|
Write-Host " DualPath already set from other registry location, keeping existing value" -ForegroundColor Yellow
|
|
}
|
|
|
|
# Handle Path1Name - use first non-empty value found
|
|
if (!$geInfo.Path1Name -and $efocasValues.Path1Name) {
|
|
$geInfo.Path1Name = $efocasValues.Path1Name
|
|
Write-Host " Path1Name = $($efocasValues.Path1Name)" -ForegroundColor Cyan
|
|
}
|
|
|
|
# Handle Path2Name - use first non-empty value found
|
|
if (!$geInfo.Path2Name -and $efocasValues.Path2Name) {
|
|
$geInfo.Path2Name = $efocasValues.Path2Name
|
|
Write-Host " Path2Name = $($efocasValues.Path2Name)" -ForegroundColor Cyan
|
|
}
|
|
|
|
# Store additional eFocas settings
|
|
$geInfo.RegistryNotes["$pathType-eFocas"] = @{
|
|
DualPath = $efocasValues.DualPath
|
|
Path1Name = $efocasValues.Path1Name
|
|
Path2Name = $efocasValues.Path2Name
|
|
IpAddr = $efocasValues.IpAddr
|
|
SocketNo = $efocasValues.SocketNo
|
|
Danobat = $efocasValues.Danobat
|
|
DataServer = $efocasValues.DataServer
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host " Error reading eFocas configuration: $_" -ForegroundColor Red
|
|
}
|
|
}
|
|
} else {
|
|
Write-Host " [NOT FOUND] No GE Aircraft Engines in $pathType registry" -ForegroundColor Gray
|
|
}
|
|
}
|
|
|
|
# Summary
|
|
Write-Host " GE Registry Summary:" -ForegroundColor Green
|
|
Write-Host " 32-bit registry: $(if ($geInfo.Registry32Bit) { 'YES' } else { 'NO' })" -ForegroundColor Cyan
|
|
Write-Host " 64-bit registry: $(if ($geInfo.Registry64Bit) { 'YES' } else { 'NO' })" -ForegroundColor Cyan
|
|
Write-Host " DualPath enabled: $(if ($geInfo.DualPathEnabled -eq $null) { 'NOT FOUND' } elseif ($geInfo.DualPathEnabled) { 'YES' } else { 'NO' })" -ForegroundColor Cyan
|
|
if ($geInfo.Path1Name) { Write-Host " Path1Name: $($geInfo.Path1Name)" -ForegroundColor Cyan }
|
|
if ($geInfo.Path2Name) { Write-Host " Path2Name: $($geInfo.Path2Name)" -ForegroundColor Cyan }
|
|
|
|
return $geInfo
|
|
}
|
|
|
|
# Function to get DNC configuration from registry
|
|
function Get-DNCConfig {
|
|
Write-Host " Collecting DNC configuration..." -ForegroundColor Yellow
|
|
|
|
$dncConfig = $null
|
|
$paths = @('HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General', 'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General')
|
|
|
|
Write-Host " Checking registry paths for DNC config..." -ForegroundColor Gray
|
|
foreach ($path in $paths) {
|
|
Write-Host " Checking path: $path" -ForegroundColor Gray
|
|
if (Test-Path $path) {
|
|
Write-Host " Path exists! Reading values..." -ForegroundColor Green
|
|
try {
|
|
$general = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
|
|
$mxPath = $path -replace 'General', 'MX'
|
|
$mx = Get-ItemProperty -Path $mxPath -ErrorAction SilentlyContinue
|
|
$fmsPath = $path -replace 'General', 'FMS'
|
|
$fms = Get-ItemProperty -Path $fmsPath -ErrorAction SilentlyContinue
|
|
|
|
if ($general) {
|
|
Write-Host " Found General config values!" -ForegroundColor Green
|
|
$dncConfig = @{
|
|
Site = $general.Site
|
|
CNC = $general.Cnc
|
|
NcIF = $general.NcIF
|
|
MachineNo = $general.MachineNo
|
|
HostType = $general.HostType
|
|
FtpHostPrimary = if ($mx) { $mx.FtpHostPrimary } else { $null }
|
|
FtpHostSecondary = if ($mx) { $mx.FtpHostSecondary } else { $null }
|
|
FtpAccount = if ($mx) { $mx.FtpAccount } else { $null }
|
|
Debug = $general.Debug
|
|
Uploads = $general.Uploads
|
|
Scanner = $general.Scanner
|
|
Dripfeed = $general.Dripfeed
|
|
AdditionalSettings = @{
|
|
Mode = $general.Mode
|
|
'Unit/Area' = $general.'Unit/Area'
|
|
DvUpldDir = $general.DvUpldDir
|
|
Ncedt = $general.Ncedt
|
|
Maint = $general.Maint
|
|
ChangeWorkstation = $general.ChangeWorkstation
|
|
FMSHostPrimary = if ($fms) { $fms.FMSHostPrimary } else { $null }
|
|
FMSHostSecondary = if ($fms) { $fms.FMSHostSecondary } else { $null }
|
|
} | ConvertTo-Json -Compress
|
|
}
|
|
|
|
Write-Host " Found DNC config: Site=$($dncConfig.Site), MachineNo=$($dncConfig.MachineNo), CNC=$($dncConfig.CNC)" -ForegroundColor Cyan
|
|
Write-Host " DNC Config JSON: $($dncConfig | ConvertTo-Json -Compress)" -ForegroundColor Gray
|
|
break
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host " Error reading DNC registry: $_" -ForegroundColor Red
|
|
}
|
|
} else {
|
|
Write-Host " Path does not exist" -ForegroundColor Yellow
|
|
}
|
|
}
|
|
|
|
if (-not $dncConfig) {
|
|
Write-Host " No DNC configuration found in registry" -ForegroundColor Yellow
|
|
}
|
|
|
|
return $dncConfig
|
|
}
|
|
|
|
# Main function to collect all shopfloor configurations
|
|
function Get-ShopfloorConfigurations {
|
|
Write-Host "`nCollecting shopfloor-specific configurations..." -ForegroundColor Yellow
|
|
|
|
$configurations = @{
|
|
NetworkInterfaces = Get-NetworkInterfaceConfig
|
|
CommConfigs = Get-SerialPortConfig
|
|
DNCConfig = Get-DNCConfig
|
|
GERegistryInfo = Get-GERegistryInfo
|
|
}
|
|
|
|
# Summary
|
|
Write-Host "`n Configuration Summary:" -ForegroundColor Green
|
|
Write-Host " Network Interfaces: $($configurations.NetworkInterfaces.Count)" -ForegroundColor Cyan
|
|
Write-Host " Comm Configs: $($configurations.CommConfigs.Count)" -ForegroundColor Cyan
|
|
Write-Host " DNC Config: $(if ($configurations.DNCConfig) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
|
|
Write-Host " GE Registry (32-bit): $(if ($configurations.GERegistryInfo.Registry32Bit) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
|
|
Write-Host " GE Registry (64-bit): $(if ($configurations.GERegistryInfo.Registry64Bit) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
|
|
Write-Host " DualPath Enabled: $(if ($configurations.GERegistryInfo.DualPathEnabled -eq $null) { 'Not Found' } elseif ($configurations.GERegistryInfo.DualPathEnabled) { 'Yes' } else { 'No' })" -ForegroundColor Cyan
|
|
|
|
return $configurations
|
|
}
|
|
|
|
function Get-InstalledApplications {
|
|
<#
|
|
.SYNOPSIS
|
|
Detects UDC and CLM applications and their active status on shopfloor PCs
|
|
.DESCRIPTION
|
|
Checks for UDC (UDC.exe) and CLM (ppdcs.exe) processes and returns data
|
|
compatible with your existing installedapps table structure.
|
|
#>
|
|
|
|
Write-Host " Scanning for UDC and CLM applications..." -ForegroundColor Yellow
|
|
|
|
$detectedApps = @()
|
|
|
|
try {
|
|
# Define the two applications we're looking for
|
|
$appsToCheck = @{
|
|
'UDC' = @{
|
|
AppID = 2 # Universal Data Collector
|
|
ProcessName = 'UDC' # UDC.exe shows as 'UDC' in Get-Process
|
|
Description = 'Universal Data Collector'
|
|
}
|
|
'CLM' = @{
|
|
AppID = 4 # Legacy UDC (CLM)
|
|
ProcessName = 'ppdcs' # ppdcs.exe shows as 'ppdcs' in Get-Process
|
|
Description = 'Legacy UDC (Cell Level Manager)'
|
|
}
|
|
}
|
|
|
|
foreach ($appName in $appsToCheck.Keys) {
|
|
$app = $appsToCheck[$appName]
|
|
|
|
Write-Host " Checking for $appName (AppID: $($app.AppID))..." -ForegroundColor Gray
|
|
|
|
# Check if the process is running
|
|
$isActive = $false
|
|
$processInfo = $null
|
|
|
|
try {
|
|
$processes = Get-Process -Name $app.ProcessName -ErrorAction SilentlyContinue
|
|
if ($processes) {
|
|
$isActive = $true
|
|
$processInfo = $processes[0] # Take first instance if multiple
|
|
Write-Host " [ACTIVE] Found running process: $($app.ProcessName).exe (PID: $($processInfo.Id))" -ForegroundColor Green
|
|
} else {
|
|
Write-Host " [NOT ACTIVE] Process $($app.ProcessName).exe not running" -ForegroundColor Gray
|
|
}
|
|
} catch {
|
|
Write-Host " [ERROR] Failed to check process $($app.ProcessName): $($_.Exception.Message)" -ForegroundColor Yellow
|
|
}
|
|
|
|
# Always return app info (both active and inactive)
|
|
$detectedApps += @{
|
|
AppID = $app.AppID
|
|
AppName = $appName
|
|
Description = $app.Description
|
|
ProcessName = $app.ProcessName
|
|
IsActive = $isActive
|
|
ProcessID = if ($processInfo) { $processInfo.Id } else { $null }
|
|
ProcessStartTime = if ($processInfo) { $processInfo.StartTime } else { $null }
|
|
}
|
|
}
|
|
|
|
# Business rule validation: Only one should be active
|
|
$activeApps = $detectedApps | Where-Object { $_.IsActive -eq $true }
|
|
|
|
if ($activeApps.Count -gt 1) {
|
|
Write-Host " [WARNING] Multiple applications active simultaneously:" -ForegroundColor Red
|
|
foreach ($activeApp in $activeApps) {
|
|
Write-Host " - $($activeApp.AppName) (PID: $($activeApp.ProcessID))" -ForegroundColor Red
|
|
}
|
|
} elseif ($activeApps.Count -eq 1) {
|
|
$activeApp = $activeApps[0]
|
|
Write-Host " [OK] Single active application: $($activeApp.AppName) (PID: $($activeApp.ProcessID))" -ForegroundColor Green
|
|
} else {
|
|
Write-Host " [INFO] No UDC or CLM applications currently running" -ForegroundColor Gray
|
|
}
|
|
|
|
return $detectedApps
|
|
|
|
} catch {
|
|
Write-Host " [ERROR] Failed to scan for applications: $($_.Exception.Message)" -ForegroundColor Red
|
|
return @()
|
|
}
|
|
} |