0

Afternoon all,

I’m using this function provided by Santiago Squarzon Add Write-Progress to Get-Job/Wait-Job

I’m looking to change the progress bar to SecondsRemaining based off the $Timeout value

Below is the Write-Progress snippet from the full function.

using namespace System.Collections.Generic
using namespace System.Diagnostics
using namespace System.Threading
using namespace System.Management.Automation

function Wait-JobWithProgress {
    [cmdletbinding()]
    param(
        [parameter(Mandatory, ValueFromPipeline)]
        [Job[]] $InputObject,

        [parameter()]
        [double] $TimeOut
    )

    begin {
        $timer = [Stopwatch]::StartNew()
        $jobs  = [List[Job]]::new()
    }

    process {
        foreach($job in $InputObject) {
            $jobs.Add($job)
        }
    }

    end {
        $total      = $jobs.Count
        $completed  = 0
        $expression = { $true }

        if($PSBoundParameters.ContainsKey('TimeOut')) {
            $expression = { $timer.Elapsed.TotalSeconds -le $TimeOut }
        }

        while((& $expression) -and $jobs) {
            $status = “running job. Timeout in $($timeout / 60) minutes”

            $progress = @{
                Activity        = 'Waiting for Jobs'
                SecondsRemaining = #this is where I’m stuck
                Status          = $status
            }
            Write-Progress @progress

I’m stuck on how to get the math to properly work for counting down based off the $timeout value.

An example command is

Start-Job {sleep -s 60} | Wait-JobWithProgress -Timeout 120
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
Fitzgery
  • 558
  • 5
  • 14
  • I think you're looking for `SecondsRemaining = $TimeOut - $timer.Elapsed.TotalSeconds` but that will just work as long as `-TimeOut` is being used, I coded it in such a way that this parameter is optional – Santiago Squarzon Oct 13 '22 at 21:52
  • Thanks! I’ll give it a try tonight. I was looking to edit it this way for how I’m implementing it as I want the job to stop if it’s been running for 30 minutes but I wanted to have it reflect that information – Fitzgery Oct 13 '22 at 22:18
  • @SantiagoSquarzon I've made some tweaks and got it to how I feel like I want it to look/work. however I am running into an issue I can't figure out. The ``percentcomplete`` bar will reset back to the beginning after every minute while running. any ideas? – Fitzgery Oct 14 '22 at 03:33
  • the calculation for `PercentComplete` should not be changed the formula should remain the same ( `completedJobs / totalJobs * 100`) – Santiago Squarzon Oct 14 '22 at 03:46

1 Answers1

0

PercentComplete is the actual progress bar itself and a needed parameter for that to work. If you're looking to add the remaining time, it would go in your Status.

using namespace System.Collections.Generic
using namespace System.Diagnostics
using namespace System.Threading
using namespace System.Management.Automation

function Wait-JobWithProgress {
    [cmdletbinding()]
    param(
        [parameter(Mandatory, ValueFromPipeline)]
        [Job[]] $InputObject,

        [parameter()]
        [double] $TimeOut
    )

    begin {
        $timer = [Stopwatch]::StartNew()
        $jobs  = [List[Job]]::new()
    }

    process {
        foreach($job in $InputObject) {
            $jobs.Add($job)
        }
    }

    end {
        $is_done    = $false
        $total      = $jobs.Count
        $completed  = 0
        $expression = { $true }

        if($PSBoundParameters.ContainsKey('TimeOut')) {
            $expression = { $timer.Elapsed.TotalSeconds -le $TimeOut }
        }

        while(& $expression -and $jobs) {
            $status = 'Remaining Jobs {0} of {1}. Seconds remaining: {2}.' -f @(
                ($jobs.State -eq 'Running').Count
                $total
                $timeout - $timer.Elapsed.TotalSeconds
            )

            $progress = @{
                Activity        = 'Waiting for Jobs'
                PercentComplete = $completed / $total * 100
                Status          = $status
                Completed       = $is_done
            }

            if ($completed -ne $total) { Write-Progress @progress }
            elseif ($completed -eq $total -and -not $is_done)
            {
                $is_done = $true
                Remove-Variable -Name 'jobs'
            }
            elseif ($is_done) { Break }

            foreach ($completed_job in $jobs.Where{$_.State -ne 'Running'})
            {
                $completed_job
                $index = [array]::IndexOf($jobs,$completed_job,0)
                $jobs.RemoveAt($index)
                $completed++
            }
        }

        # Stop the jobs not yet Completed and remove them
        if ($jobs)
        {
            $jobs | Stop-Job -PassThru | ForEach-Object {
                Remove-Job -Job $_
                "Job [#{0} - {1}] did not complete on time and was removed." -f $_.Id, $_.Name
            } | Write-Warning
            $is_completed = $true
            Write-Progress @progress 
        }
    }
}

I was having some trouble with the original code and modified it for my use, but the solution is in the $status variable.