Private/Progress.ps1

# Progress on the Technician's console.
#
# Several Checks take seconds to minutes - the CPU sampled at rest, pings to every Server,
# the disk write test, the stress test, the wait for the admin prompt - and a console that
# prints a heading and then says nothing for a minute looks hung. So a Run shows one bar
# for the Run and one beneath it for the Check that is waiting.
#
# Only Gatherers and the Run draw progress. A Judge touches nothing but its data, and the
# Elevated Part silences progress altogether (Invoke-GutcheckElevated.ps1), because its
# window is not the one the Technician watches.

$script:ProgressRunId   = 1
$script:ProgressCheckId = 2

# What the Check's bar last showed, so an unchanged bar is not drawn again: Write-Progress
# is slow on Windows PowerShell 5.1, and the disk test reports from a loop of thousands.
$script:ProgressLastShown = $null

function Get-ProgressPercent {
    <#
    .SYNOPSIS
        How far along step Step of Of is, as a whole percentage from 0 to 100. Pure.
    #>

    [CmdletBinding()]
    [OutputType([int])]
    param([double]$Step, [double]$Of)

    if ($Of -le 0 -or $Step -le 0) { return 0 }
    [int][math]::Min(100, [math]::Floor($Step / $Of * 100))
}

function Write-RunProgress {
    <#
    .SYNOPSIS
        The Run's own bar: which Check, and how many there are.
    #>

    [CmdletBinding()]
    [OutputType([void])]
    param(
        [Parameter(Mandatory)][int]$Index,
        [Parameter(Mandatory)][int]$Count,
        [Parameter(Mandatory)][string]$Name
    )

    Complete-CheckProgress
    Write-Progress -Id $script:ProgressRunId -Activity (Get-Text 'Console.Progress.Activity') `
        -Status ((Get-Text 'Console.Progress.Run') -f $Index, $Count, $Name) `
        -PercentComplete ([math]::Round(($Index - 1) / [math]::Max($Count, 1) * 100))
}

function Complete-RunProgress {
    [CmdletBinding()]
    [OutputType([void])]
    param()

    Complete-CheckProgress
    Write-Progress -Id $script:ProgressRunId -Activity (Get-Text 'Console.Progress.Activity') -Completed
}

function Write-CheckProgress {
    <#
    .SYNOPSIS
        The bar beneath the Run's, for a Check that is waiting on something. Emits nothing.
    .DESCRIPTION
        Emitting nothing is load-bearing for the same reason as Wait-Sample: this is called
        inside sampling loops whose output is the readings themselves.
    .PARAMETER Seconds
        For a Check that runs for a set time: the seconds so far, shown as "15 von 60 s".
    #>

    [CmdletBinding(DefaultParameterSetName = 'Step')]
    [OutputType([void])]
    param(
        [Parameter(Mandatory, ParameterSetName = 'Step')][string]$Status,
        [Parameter(Mandatory, ParameterSetName = 'Step')][double]$Step,
        [Parameter(Mandatory, ParameterSetName = 'Seconds')][double]$Seconds,
        [Parameter(Mandatory)][double]$Of
    )

    if ($PSCmdlet.ParameterSetName -eq 'Seconds') {
        $Status = (Get-Text 'Console.Progress.Seconds') -f [int]$Seconds, [int]$Of
        $Step   = $Seconds
    }

    $percent = Get-ProgressPercent -Step $Step -Of $Of
    $shown   = '{0}|{1}' -f $percent, $Status
    if ($shown -eq $script:ProgressLastShown) { return }
    $script:ProgressLastShown = $shown

    Write-Progress -Id $script:ProgressCheckId -ParentId $script:ProgressRunId `
        -Activity (Get-Text 'Console.Progress.CheckActivity') -Status $Status -PercentComplete $percent
}

function Complete-CheckProgress {
    [CmdletBinding()]
    [OutputType([void])]
    param()

    if ($null -eq $script:ProgressLastShown) { return }
    $script:ProgressLastShown = $null
    Write-Progress -Id $script:ProgressCheckId -ParentId $script:ProgressRunId `
        -Activity (Get-Text 'Console.Progress.CheckActivity') -Completed
}