0

The goal is to have a progress bar monitoring an external process that writes the step it is on into a watch file.

If I do not have a Start-Sleep in the loop, it will produce messages about not being able to convert infinity. Why is this? I am willing to have some sleep time, but why is it needed and what is the minimum time needed to sleep?

PS C:\src\t\pb> .\Monitor-Progress2.ps1 -TotalCount 5 -WatchFile wf.txt -Verbose
New-TimeSpan : Cannot bind parameter 'Seconds'. Cannot convert value "∞" to type "System.Int32". Error: "Value was either too large or too small for an Int32."
At C:\src\t\pb\Monitor-Progress2.ps1:46 char:37
+ ... an -Seconds (($ts.TotalSeconds / $currentCount) * ($TotalCount - $cur ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [New-TimeSpan], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.NewTimeSpanCommand

Here is the code. The same problem occurs on PowerShell 5.1 and 6.0. All I do to run it is to ECHO>wf.txt 1, then ECHO>wf.txt 2, etc. Sometimes the error occurs on step two, but sometimes on step 3.

[CmdletBinding()]
Param (
    [Parameter(Mandatory=$true)]
    [int]$TotalCount

    ,[Parameter(Mandatory=$true)]
    [ValidateScript({Test-Path $_ -PathType 'Leaf'})]
    [string]$WatchFile
)

$currentcount = 0
$previouscount = $currentcount

$starttimestamp = Get-Date

while ($currentcount -lt $TotalCount) {
    $currentcount = [int32](Get-Content $WatchFile)

    ### Write-Verbose $currentcount

    if (($currentcount -lt $TotalCount) -and ($currentcount -ne $previouscount)) {
        $ts = $(Get-Date) - $starttimestamp

        ### Write-Verbose $ts.TotalSeconds
        ### Write-Verbose $currentcount
        ### Write-Verbose ($ts.TotalSeconds / $currentcount)
        ### Write-Verbose ($TotalCount - $currentcount)
        ### Write-Verbose (($ts.TotalSeconds / $currentcount) * ($TotalCount - $currentcount))

        $et = New-TimeSpan -Seconds (($ts.TotalSeconds / $currentCount) * ($TotalCount - $currentcount))
        $runningstatus = "Long process running for {0:%d} days {0:%h} hours {0:%m} minutes {0:%s} seconds" -f $ts
        $completionstatus = "Estimated completion in {0:%d} days {0:%h} hours {0:%m} minutes {0:%s} seconds" -f $et
        Write-Progress -Activity $runningstatus `
            -Status $completionstatus `
            -percentComplete ($currentcount / $TotalCount*100)
        $previouscount = $currentcount
    }

    #Start-Sleep -Seconds 1
}
lit
  • 14,456
  • 10
  • 65
  • 119
  • What is this meant to do? `$currentcount = [int32](Get-Content $WatchFile)` What are the values of `Write-Verbose` on a failed run? – G42 Dec 04 '17 at 12:35
  • Even though the watch file contains two (2) or three (3), $currentcount is reported as zero (0). Why would that be? – lit Dec 04 '17 at 13:59
  • So far, race condition best explains it. Especially the part with `Start-Sleep` helping. What is this external process? Have you found a resolution/workaround? – G42 Dec 06 '17 at 11:37
  • @gms0ulman - The current resolution is to check $currentcount from Get-Content to see if it is $null or an empty string. If it is, just let it go around the loop again. I would note that I do not think Start-Sleep is a solution. It all depends on the timing. – lit Dec 06 '17 at 13:17

1 Answers1

0

While your code didn't process a single entity, your $currentcount is zero, therefore your naive ETA calculation returns infinity. You should check if you can calculate ETA first by comparing current count with zero.

EDIT: You have done an implicit conversion of Get-Content which returns an array of strings, so if your file has a newline in it, you get a conversion error and your $currentcount remains zero, thus you divide by zero to get that infinity. You should either use [IO.File]::ReadAllText() method, or use only the first line (with index 0) for parsing. Or you should write to the file with -NoNewLine flag to Out-File cmdlet.

Vesper
  • 18,599
  • 6
  • 39
  • 61
  • I am getting `$currentcount` from the watch file. I always start it a one (1), then increment by one (1). Yes, I know this is something to check for later. That does not explain why this is not working. – lit Dec 04 '17 at 13:58
  • @lit Well, it's possible for this typecast to return an error, and the value in `$currentcount` will be unchanged, and it's initialized with zero. Here, have a facepalm. – Vesper Dec 04 '17 at 14:40
  • Why is it that the cast would fail if there is no Start-Sleep? It is always possible for something to fail. This seems irregular. – lit Dec 04 '17 at 15:49
  • 1
    @lit You probably hit a race condition, one thread writes to file, the other thread reads from file. – Vesper Dec 04 '17 at 17:11
  • Yes, that is the only thing I can reasonably expect. Interestingly, using an [int] cast will return 0 for strings of '' and $null. I would have expected Get-Content to indicate an error rather than just returning '' or $null. – lit Dec 04 '17 at 17:49