Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <#
- ProgressManager class
- .SYNOPSIS
- Implements the ProgressManager class.
- .DESCRIPTION
- The ProgressManager class handles the work of creating and managing a progress bar.
- .PROPERTY Activity
- Specifies the first line of text in the heading above the status bar. This text describes the activity whose progress is being reported.
- .PROPERTY Id
- Specifies an ID that distinguishes each progress bar from the others. Use this parameter when you are creating more than one progress bar in a single command. If the progress bars do not have different IDs, they are superimposed instead of being displayed in a series.
- .PROPERTY ParentId
- Specifies the parent activity of the current activity. The default is -1, meaning the current activity has no parent activity.
- .PROPERTY Format
- Specifies a scriptblock that is used to turn the InputObject into a string for display in the progress bar. The default is { $_ | Out-String }.
- .PROPERTY Total
- Specifies the total number of items. This is used to calculate almost every value displayed.
- .PROPERTY Completed
- Specifies the number of items that have been processed thus far.
- .PROPERTY StartTime
- Specifies the starting time of the operation. The default is the time the ProgressManager object is instantiated. Normally this value does not need to be set manually.
- .METHOD PercentComplete
- Returns the percent complete, based on the Completed and Total properties.
- .METHOD PerSec
- Returns the number of items processed per second.
- .METHOD Pending
- Returns the number of items yet to be processed, based on the Completed and Total properties.
- .METHOD Remaining
- Returns an estimate of the amount of time needed to process the remaining items.
- .METHOD Status
- Returns the text that would be displayed in the progress bar.
- .METHOD WriteProgress
- Displays the progress bar based on current values.
- .METHOD Update
- Increments the number of items completed by one.
- .METHOD Complete
- Hides the progress bar. Normally used after all objects have been processed.
- .NOTES
- Uses [TimeSpan].Approximate script method from https://pastebin.com/Y55bA77H
- Based on Staffan Gustafsson's presentation from PS Conference EU 2017.
- #>
- class ProgressManagerTime {
- [datetime]$Date
- [long]$Completed
- ProgressManagerTime([long]$Completed) {
- $this.Date = Get-Date
- $this.Completed = $Completed
- }
- }
- class ProgressManager {
- [string]$Activity
- [int]$Id
- [int]$ParentId
- [scriptblock]$Format
- [long]$Total
- [long]$Completed
- [datetime]$StartTime
- hidden [datetime]$LastUpdate
- hidden [long]$ListSize
- hidden [System.Collections.Generic.List`1[ProgressManagerTime]]$Times
- hidden static [string]$DefaultActivity = 'Watching Progress'
- hidden static [scriptblock]$DefaultFormat = { $_.ToString() }
- ProgressManager() {
- $this.Init(0, (Get-Random -Maximum 1000), -1, [ProgressManager]::DefaultActivity, [ProgressManager]::DefaultFormat)
- }
- ProgressManager([long]$Total) {
- $this.Init($Total, (Get-Random -Maximum 1000), -1, [ProgressManager]::DefaultActivity, [ProgressManager]::DefaultFormat)
- }
- ProgressManager([long]$Total, [int]$Id, [int]$ParentId) {
- $this.Init($Total, $Id, $ParentId, [ProgressManager]::DefaultActivity, [ProgressManager]::DefaultFormat)
- }
- ProgressManager([long]$Total, [int]$Id, [int]$ParentId, [string]$Activity) {
- $this.Init($Total, $Id, $ParentId, $Activity, [ProgressManager]::DefaultFormat)
- }
- ProgressManager([long]$Total, [int]$Id, [int]$ParentId, [string]$Activity, [scriptblock]$Format) {
- $this.Init($Total, $Id, $ParentId, $Activity, $Format)
- }
- hidden Init([long]$Total, [int]$Id, [int]$ParentId, [string]$Activity, [scriptblock]$Format) {
- $this.Id = $Id
- $this.ParentId = $ParentId
- $this.Activity = $Activity
- $this.Total = $Total
- $this.Format = $Format
- $this.Completed = 0
- $this.StartTime = Get-Date
- $this.LastUpdate = (Get-Date).AddMilliSeconds(-501)
- $this.ListSize = [Math]::Max(10, [long]($Total / 10))
- $this.Times = @()
- [Void]$this.Times.Add([ProgressManagerTime]::new($this.Completed))
- $this.Times[0].Date = $this.LastUpdate
- }
- [int]PercentComplete() {
- if ($this.Total -lt 1) { return 0 }
- return ([Math]::Min(($this.Completed / $this.Total * 100), 100))
- }
- [double]PerSec() {
- return (1 / [Math]::Max((($this.Times[-1].Date - $this.Times[0].Date).TotalMilliseconds / [Math]::Max(($this.Times[-1].Completed - $this.Times[0].Completed), 1) / 1000), 0.000001))
- }
- [long]Pending() { return ($this.Total - $this.Completed) }
- [string]Remaining() {
- if ($this.Completed -eq 0) {
- return 'Unknown'
- } else {
- return ('~' + (New-TimeSpan -Seconds ([Math]::Min(($this.Pending() / $this.PerSec()), [int32]::MaxValue))).Approximate())
- }
- }
- [string]Status($Item) {
- [timespan]$Duration = (New-TimeSpan $this.StartTime) + [timespan]1000
- $RunTime = (New-TimeSpan -Days $Duration.Days -Hours $Duration.Hours -Minutes $Duration.Minutes -Seconds $Duration.Seconds).ToString()
- $PerSecond = $this.PerSec()
- [double]$Per, [string]$Unit = switch ($PerSecond) { { $_ -gt 10 } { $PerSecond, 'Sec'; break }
- { $_ -gt 0.166667 } { ($PerSecond * 60), 'Min'; break }
- default { ($PerSecond * 3600), 'Hr' }
- }
- [string]$ItemString = try { $Item | % $this.Format | Out-String -Stream } catch { $Item | Out-String -Stream }
- return (('StartTime: {0} | RunTime: {3} | Remaining: {6} | {8:N0} = {1:N0} << {2:N0} | {4:N0} / {5} | Processing: {7}...' -f $this.StartTime, $this.Completed, $this.Pending(), $RunTime, $Per, $Unit, $this.Remaining(), ($ItemString -replace '(.{1,60})(.*)', '$1'), $this.Total))
- }
- [void]WriteProgress($Item) {
- [boolean]$ShouldOutput = $true
- if ($this.Times[-1].Completed -lt $this.Completed) {
- [Void]$this.Times.Add([ProgressManagerTime]::new($this.Completed))
- $ShouldOutput = (($this.Times[-1].Date - $this.LastUpdate).TotalMilliseconds -gt 500)
- }
- if ($ShouldOutput) {
- if ($this.Times.Count -gt $this.ListSize) { $this.Times.RemoveRange(0, ($this.Times.Count - $this.ListSize)) }
- Write-Progress -Activity $this.Activity -Id $this.Id -ParentId $this.ParentId -Status $this.Status($Item) -PercentComplete $this.PercentComplete()
- $this.LastUpdate = Get-Date
- }
- }
- [void]Update() { $this.Completed += 1 }
- [void]Complete() {
- Write-Progress -Activity $this.Activity -Id $this.Id -Completed
- }
- }
- Update-TypeData -Force -TypeName TimeSpan -MemberType ScriptMethod -MemberName Approximate -Value {
- if ($args.Count -gt 1) {
- throw ('Cannot find an overload for "{0}" and the argument count: "{1}".' -f 'Approximate', $Args.Count)
- }
- function Round([double]$Number, [string]$Unit) {
- $Number, [string]$Half = if ((($Number % 1) -gt 0.25) -and (($Number % 1) -lt 0.75)) { ($Number - 0.5), [char][byte]189 } else { $Number, '' }
- '{0}{1} {2}' -f [System.Math]::Round($Number, 0), $Half, $Unit
- }
- $Units = @('seconds', 'a minute', 'minutes', 'an hour', 'hours', 'days')
- if ($args.Count -gt 0 -and $args[0] -is [array]) {
- for ($i = 0; ($i -lt $Units.Count) -and ($i -lt $args[0].Count); $i++) {
- $Units[$i] = $args[0][$i]
- }
- }
- $result = switch ($this.TotalSeconds) {
- { $_ -le 3 } { "<5 $($Units[0])"; break }
- { $_ -le 7.5 } { "5 $($Units[0])"; break }
- { $_ -le 47.5 } {
- "$([System.Math]::Round((($this.TotalSeconds - 2.5) - (($this.TotalSeconds - 2.5) % 5) + 5), 0)) $($Units[0])"
- break
- }
- { $_ -le 75 } { $Units[1]; break }
- { $_ -le 2700 } {
- Round -Number $this.TotalMinutes -Unit $Units[2]
- break
- } # 45 minutes or less
- { $_ -le 4500 } { $Units[3]; break } # 45 minutes to 1.25 hours
- { $_ -le 173700 } {
- Round -Number $this.TotalHours -Unit $Units[4]
- break
- } # less than 2 days 15 minutes
- default {
- Round -Number $this.TotalDays -Unit $Units[5]
- }
- }
- return $result
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement