tools: add offline rpa window watchdog
This commit is contained in:
@@ -9,6 +9,8 @@ param(
|
||||
[int]$Y = 120,
|
||||
[int]$Width = 270,
|
||||
[int]$Height = 570,
|
||||
[int]$WatchSeconds = 0,
|
||||
[int]$PollIntervalMs = 500,
|
||||
[ValidateSet("Visible", "Offscreen", "Minimized")]
|
||||
[string]$WindowMode = "Visible",
|
||||
[switch]$SkipExtract,
|
||||
@@ -107,6 +109,19 @@ public static class ISphereOfflineWindowApi {
|
||||
"@ -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
function Start-OfflineClient {
|
||||
if ($script:UserSilentMode -or $script:WindowModeValue -ne "Visible" -or $script:NoActivateMode) {
|
||||
return Start-Process -FilePath $script:ClientExePath -WorkingDirectory $script:ClientDirPath -WindowStyle Minimized -PassThru
|
||||
}
|
||||
return Start-Process -FilePath $script:ClientExePath -WorkingDirectory $script:ClientDirPath -PassThru
|
||||
}
|
||||
|
||||
function Relaunch-OfflineClient {
|
||||
Stop-ExistingOfflineCopies -RootPath $script:ExtractRootPath
|
||||
Start-Sleep -Milliseconds 500
|
||||
return Start-OfflineClient
|
||||
}
|
||||
|
||||
function Move-ClientWindows([object[]]$Windows, [int]$MainX, [int]$MainY, [int]$MainWidth, [int]$MainHeight, [bool]$MakeTopMost, [bool]$Activate, [string]$Mode) {
|
||||
$hwndTop = [IntPtr]::Zero
|
||||
$topMostHandle = [IntPtr]::new(-1)
|
||||
@@ -157,6 +172,32 @@ function Move-ClientWindows([object[]]$Windows, [int]$MainX, [int]$MainY, [int]$
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ClientWindowsForProcess([object]$Process) {
|
||||
$scanResult = Invoke-HelperJson -Op "scan_windows" -OpArgs @{ include_all_visible = $false } -RequestId "offline-real-client-watch-scan"
|
||||
if (-not $scanResult.ok -or -not $scanResult.data.windows) {
|
||||
return @()
|
||||
}
|
||||
return @($scanResult.data.windows | Where-Object {
|
||||
$_.process_name -eq "IMPlatformClient" -and
|
||||
((-not $Process) -or $_.pid -eq $Process.Id)
|
||||
})
|
||||
}
|
||||
|
||||
function Test-WindowNear([object]$Window, [int]$ExpectedX, [int]$ExpectedY, [int]$ExpectedWidth, [int]$ExpectedHeight) {
|
||||
if (-not $Window -or -not $Window.bounds) {
|
||||
return $false
|
||||
}
|
||||
$tolerance = 3
|
||||
return ([Math]::Abs([int]$Window.bounds.x - $ExpectedX) -le $tolerance) -and
|
||||
([Math]::Abs([int]$Window.bounds.y - $ExpectedY) -le $tolerance) -and
|
||||
([Math]::Abs([int]$Window.bounds.width - $ExpectedWidth) -le $tolerance) -and
|
||||
([Math]::Abs([int]$Window.bounds.height - $ExpectedHeight) -le $tolerance)
|
||||
}
|
||||
|
||||
function Repair-ClientWindowPlacement([object[]]$Windows) {
|
||||
Move-ClientWindows -Windows $Windows -MainX $script:EffectiveX -MainY $script:EffectiveY -MainWidth $script:DesiredWidth -MainHeight $script:DesiredHeight -MakeTopMost ([bool]$script:TopMostMode) -Activate (-not [bool]$script:NoActivateMode) -Mode $script:WindowModeValue | Out-Null
|
||||
}
|
||||
|
||||
function Save-WindowScreenshot([string]$OutputPath, [int]$ScreenX, [int]$ScreenY, [int]$CaptureWidth, [int]$CaptureHeight) {
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$bitmap = New-Object System.Drawing.Bitmap $CaptureWidth, $CaptureHeight
|
||||
@@ -174,6 +215,7 @@ function Save-WindowScreenshot([string]$OutputPath, [int]$ScreenX, [int]$ScreenY
|
||||
$archiveFull = Resolve-RepoPath $ArchivePath
|
||||
$extractFull = Resolve-RepoPath $ExtractDir
|
||||
$script:HelperPath = Resolve-RepoPath $HelperExe
|
||||
$script:ExtractRootPath = $extractFull
|
||||
$runOut = Join-Path $repo "runs\offline-real-client-window"
|
||||
New-Item -ItemType Directory -Force -Path $runOut | Out-Null
|
||||
|
||||
@@ -185,6 +227,16 @@ $clientDir = Join-Path $extractFull ("zyl\" + $ClientRoot)
|
||||
$clientExe = Join-Path $clientDir "IMPlatformClient.exe"
|
||||
$clientConfig = Join-Path $clientDir "IMPlatformClient.exe.config"
|
||||
$requiredDependency = Join-Path $clientDir "Utilities.Lib.Base.dll"
|
||||
$script:ClientDirPath = $clientDir
|
||||
$script:ClientExePath = $clientExe
|
||||
$script:UserSilentMode = [bool]$UserSilent
|
||||
$script:NoActivateMode = [bool]$NoActivate
|
||||
$script:WindowModeValue = $WindowMode
|
||||
$script:EffectiveX = $effectiveX
|
||||
$script:EffectiveY = $effectiveY
|
||||
$script:DesiredWidth = $Width
|
||||
$script:DesiredHeight = $Height
|
||||
$script:TopMostMode = [bool]$TopMost
|
||||
|
||||
if (-not $SkipExtract -and (-not (Test-Path -LiteralPath $clientExe) -or -not (Test-Path -LiteralPath $clientConfig) -or -not (Test-Path -LiteralPath $requiredDependency))) {
|
||||
if (-not (Test-Path -LiteralPath $archiveFull)) {
|
||||
@@ -211,12 +263,7 @@ if (-not $KeepExisting) {
|
||||
|
||||
$process = $null
|
||||
if (-not $NoLaunch) {
|
||||
if ($UserSilent -or $WindowMode -ne "Visible" -or $NoActivate) {
|
||||
$process = Start-Process -FilePath $clientExe -WorkingDirectory $clientDir -WindowStyle Minimized -PassThru
|
||||
}
|
||||
else {
|
||||
$process = Start-Process -FilePath $clientExe -WorkingDirectory $clientDir -PassThru
|
||||
}
|
||||
$process = Start-OfflineClient
|
||||
}
|
||||
|
||||
$deadline = (Get-Date).AddSeconds([Math]::Max(1, $WaitSeconds))
|
||||
@@ -276,6 +323,65 @@ if ($latePromptWindows.Count -gt 0) {
|
||||
})
|
||||
}
|
||||
|
||||
$watchIterationCount = 0
|
||||
$repairCount = 0
|
||||
$relaunchCount = 0
|
||||
$recoveryActions = @()
|
||||
|
||||
if ($WatchSeconds -gt 0) {
|
||||
$safePollIntervalMs = [Math]::Max(100, $PollIntervalMs)
|
||||
$watchDeadline = (Get-Date).AddSeconds($WatchSeconds)
|
||||
while ((Get-Date) -lt $watchDeadline) {
|
||||
Start-Sleep -Milliseconds $safePollIntervalMs
|
||||
$watchIterationCount++
|
||||
$watchedWindows = Get-ClientWindowsForProcess -Process $process
|
||||
$watchedMain = @($watchedWindows | Where-Object { [string]$_.title -ne $InfoPromptTitle }) | Select-Object -First 1
|
||||
$watchedPrompt = @($watchedWindows | Where-Object { [string]$_.title -eq $InfoPromptTitle }) | Select-Object -First 1
|
||||
|
||||
if (-not $watchedMain) {
|
||||
if (-not $NoLaunch) {
|
||||
$process = Relaunch-OfflineClient
|
||||
$relaunchCount++
|
||||
$recoveryActions += "relaunch_missing_main_window"
|
||||
|
||||
$relaunchDeadline = (Get-Date).AddSeconds([Math]::Max(1, $WaitSeconds))
|
||||
do {
|
||||
Start-Sleep -Milliseconds $safePollIntervalMs
|
||||
$watchedWindows = Get-ClientWindowsForProcess -Process $process
|
||||
$watchedMain = @($watchedWindows | Where-Object { [string]$_.title -ne $InfoPromptTitle }) | Select-Object -First 1
|
||||
} while (-not $watchedMain -and (Get-Date) -lt $relaunchDeadline)
|
||||
|
||||
if ($watchedWindows.Count -gt 0) {
|
||||
Repair-ClientWindowPlacement -Windows $watchedWindows
|
||||
$repairCount++
|
||||
$recoveryActions += "repair_after_relaunch"
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
$expectedPromptX = if ($WindowMode -eq "Offscreen") { $effectiveX } else { $effectiveX + $Width + 40 }
|
||||
$expectedPromptY = if ($WindowMode -eq "Offscreen") { $effectiveY + $Height + 40 } else { $effectiveY + 40 }
|
||||
|
||||
$needsRepair = -not (Test-WindowNear -Window $watchedMain -ExpectedX $effectiveX -ExpectedY $effectiveY -ExpectedWidth $Width -ExpectedHeight $Height)
|
||||
if ($watchedPrompt) {
|
||||
$needsRepair = $needsRepair -or -not (Test-WindowNear -Window $watchedPrompt -ExpectedX $expectedPromptX -ExpectedY $expectedPromptY -ExpectedWidth 300 -ExpectedHeight 190)
|
||||
}
|
||||
|
||||
if ($needsRepair) {
|
||||
Repair-ClientWindowPlacement -Windows $watchedWindows
|
||||
$repairCount++
|
||||
$recoveryActions += "repair_window_placement"
|
||||
}
|
||||
}
|
||||
|
||||
$scanAfterMove = Invoke-HelperJson -Op "scan_windows" -OpArgs @{ include_all_visible = $false } -RequestId "offline-real-client-final-scan-after-watch"
|
||||
$windowsAfterMove = @($scanAfterMove.data.windows | Where-Object {
|
||||
$_.process_name -eq "IMPlatformClient" -and
|
||||
((-not $process) -or $_.pid -eq $process.Id)
|
||||
})
|
||||
}
|
||||
|
||||
$mainWindowMatches = @($windowsAfterMove | Where-Object { [string]$_.title -ne $InfoPromptTitle })
|
||||
$promptWindowMatches = @($windowsAfterMove | Where-Object { [string]$_.title -eq $InfoPromptTitle })
|
||||
$mainWindow = if ($mainWindowMatches.Count -gt 0) { $mainWindowMatches[0] } else { $null }
|
||||
@@ -306,6 +412,12 @@ if (-not $NoScreenshot -and $WindowMode -eq "Visible") {
|
||||
user_silent = [bool]$UserSilent
|
||||
window_mode = $WindowMode
|
||||
no_activate = [bool]$NoActivate
|
||||
watch_seconds = $WatchSeconds
|
||||
poll_interval_ms = $PollIntervalMs
|
||||
watch_iteration_count = $watchIterationCount
|
||||
relaunch_count = $relaunchCount
|
||||
repair_count = $repairCount
|
||||
recovery_actions = $recoveryActions
|
||||
effective_bounds = @{
|
||||
x = $effectiveX
|
||||
y = $effectiveY
|
||||
|
||||
Reference in New Issue
Block a user