PSChiaPlotter.psm1
enum KSize{ K32 = 32 K33 = 33 K34 = 34 K35 = 35 } class MaximizedKSize { [KSize]$KSize [int]$MaxPlots [Decimal]$RemainingBytes [Decimal]$KSizeBytes [int64]$TotalBytes static [Decimal]$K35 = 884.1 * 1gb static [Decimal]$K34 = 429.8 * 1gb static [Decimal]$K33 = 208.8 * 1gb static [Decimal]$K32 = 101.4 * 1gb MaximizedKSize( [KSize]$KSize, [int64]$TotalBytes ){ $this.KSize = $Ksize $this.TotalBytes = $TotalBytes $this.KSizeBytes = switch ($this.KSize){ "K35" {[MaximizedKSize]::K35} "K34" {[MaximizedKSize]::K34} "K33" {[MaximizedKSize]::K33} "K32" {[MaximizedKSize]::K32} } $this.MaxPlots = [math]::Floor([decimal]($this.TotalBytes / $this.KSizeBytes)) $this.RemainingBytes = $Totalbytes - (([math]::Floor([decimal]($this.TotalBytes / $this.KSizeBytes))) * $this.KSizeBytes) } } class OptimizedKPlots { [int]$K35 [int]$K34 [int]$K33 [int]$K32 [decimal]$RemainingBytes [double]$RemainingGB OptimizedKPlots ( [int]$K35, [int]$K34, [int]$K33, [int64]$Totalbytes ){ $sizeremaining = $TotalBytes - (($K35 * [MaximizedKSize]::K35) + ($K34 * [MaximizedKSize]::K34) + ($K33 * [MaximizedKSize]::K33)) $k32max = Get-MaxKSize -Totalbytes $sizeremaining -KSize "K32" $this.K35 = $K35 $this.K34 = $K34 $this.K33 = $K33 $this.K32 = $k32max.MaxPlots $this.RemainingBytes = $k32max.RemainingBytes $this.RemainingGB = [math]::Round($k32max.RemainingBytes / 1gb,2) } } function ConvertTo-FriendlyTimeSpan { [CmdletBinding()] param( [int32]$Seconds ) $TimeSpan = New-TimeSpan -Seconds $Seconds switch ($TimeSpan){ {$_.Days -ge 1} {return "$([math]::Round($TimeSpan.TotalDays,2)) days";break} {$_.Hours -ge 1} {return "$([math]::Round($TimeSpan.TotalHours,2)) hrs";break} {$_.Minutes -ge 1} {return "$([math]::Round($TimeSpan.TotalMinutes,2)) mins";break} {$_.seconds -ge 1} {return "$([math]::Round($TimeSpan.TotalSeconds,2)) sec";break} } } function Get-ChiaHarvesterActivity { [CmdletBinding()] param( [string[]]$DebugLogFilePath = (Get-ChildItem -Path "$([System.Environment]::GetFolderPath("User"))\.chia\mainnet\log" -filter "debug.log*").FullName, [switch]$Summary ) $chiaharvesterlog = "([0-9:.\-T]*) harvester (?:src|chia).harvester.harvester(?:\s?): INFO\s*([0-9]*) plots were eligible for farming ([a-z0-9.]*) Found ([0-9]*) proofs. Time: ([0-9.]*) s. Total ([0-9]*) plots" foreach ($logfile in $DebugLogFilePath){ try{ $SummaryLog = New-Object 'System.Collections.Generic.List[System.Object]' Get-Content -Path $logfile | foreach-object { switch -Regex ($_){ $chiaharvesterlog { $harvesterActivity = [pscustomobject]@{ PSTypeName = "PSChiaPlotter.ChiaHarvesterActivity" Time = [datetime]::parse($Matches[1]) EligiblePlots = $Matches[2] LookUpTime = [double]$Matches[5] ProofsFound = $Matches[4] TotalPlots = $Matches[6] FilterRatio = 0 } #psobject try { #Prevent the divide by zero error message $harvesterActivity.FilterRatio = $Matches[2] / $Matches[6] } catch { } if (-not$Summary){ $harvesterActivity } else{ $SummaryLog.Add($harvesterActivity) } } } #switch } #foreach line if ($Summary.IsPresent -and $SummaryLog.Count -ne 0){ Write-Information "Computing Summary for $logfile" if ([System.Environment]::OSVersion.Platform -eq "Win32NT"){ $FirstandLast = $SummaryLog | Sort-Object Time -Descending | Select-Object -First 1 -Last 1 | Sort-Object -Descending $RunTime = $FirstandLast[1].Time - $FirstandLast[0].Time if ($RunTime.TotalMinutes -lt 0){$RunTime = $FirstandLast[0].Time - $FirstandLast[1].Time} if ($RunTime.TotalMinutes -ne 0){$ChallengesPerMinute = $SummaryLog.Count / $RunTime.TotalMinutes} } else{ Write-Warning "Unable to calculate average challenges per min on linux due the timestamps missing the date portion." } [PSCustomObject]@{ PSTypeName = "PSChiaPlotter.ChiaHarvesterSummary" RunTime = $RunTime TotalEligiblePlots = ($SummaryLog | Measure-Object EligiblePlots -Sum).Sum BestLookUpTime = ($SummaryLog | Measure-Object LookUpTime -Minimum).Minimum WorstLookUpTime = ($SummaryLog | Measure-Object LookUpTime -Maximum).Maximum AverageLookUpTime = ($SummaryLog | Measure-Object LookUpTime -Average).Average ProofsFound = ($SummaryLog | Measure-Object -Property ProofsFound -Sum).Sum FilterRatio = ($SummaryLog | Measure-Object -Property FilterRatio -Average).Average ChallengesPerMinute = $ChallengesPerMinute } } } catch{ $PSCmdlet.WriteError($_) } } #foreach } function Get-ChiaKPlotCombination{ [CmdletBinding(DefaultParameterSetName = "DriveLetter")] param( [Parameter(ParameterSetName="FreeSpace")] [int64[]]$FreeSpace, [Parameter(ParameterSetName="DriveLetter")] [string[]]$DriveLetter = (Get-Volume).DriveLetter ) if ($PSCmdlet.ParameterSetName -eq "FreeSpace"){ foreach ($space in $FreeSpace){ $Max = Get-MaxKSize -TotalBytes $space $AllCombos = Get-OptimizedKSizePlotNumbers $Max | sort RemainingBytes $AllCombos | Add-Member -MemberType NoteProperty -Name "StartingFreeSpace" -Value $space $AllCombos } } elseif ($PSCmdlet.ParameterSetName -eq "DriveLetter"){ foreach ($letter in $DriveLetter){ $Drive = Get-Volume -DriveLetter $letter $Max = Get-MaxKSize -TotalBytes $Drive.SizeRemaining $AllCombos = Get-OptimizedKSizePlotNumbers $Max | sort RemainingBytes $AllCombos | Add-Member -NotePropertyMembers @{ DriveLetter = $letter FriendlyName = $Drive.FileSystemLabel } $AllCombos | foreach {$_.psobject.TypeNames.Insert(0,"PSChiaPlotter.KSizeCombination")} $AllCombos } } } function Get-ChiaMaxParallelCount { [CmdletBinding()] param( [Parameter()] [ValidateRange(1,128)] [int]$ThreadCount = 2, [Parameter()] [ValidateRange(1, [int]::MaxValue)] [int]$BufferMiB = 3390 ) if (!$PSBoundParameters.ContainsKey("ThreadCount") -and !$PSBoundParameters.ContainsKey("BufferMiB")){ Write-Warning "All calculations based on plotting k32 plot size only. SSD TB suggestion rounded up to the nearest TB." } else{ Write-Warning "SSD TB suggestion rounded up to the nearest TB." } $Processor = Get-CimInstance -ClassName Win32_Processor $Threads = ($Processor | measure -Property ThreadCount -Sum).Sum $MaxParallelCountCPU = [math]::Floor($Threads / $ThreadCount) #1mb = 1048576 bytes $RAM = (Get-CimInstance -ClassName Win32_PhysicalMemory | measure -Property Capacity -Sum).Sum / 1mb $MaxParallelCountRAM = [Math]::Floor([decimal]($RAM / $BufferMiB)) $SystemDisk = Get-CimInstance -Namespace ROOT/Microsoft/Windows/Storage -ClassName MSFT_Disk -Filter "IsSystem=True" $SSDs = Get-CimInstance -Namespace root/microsoft/windows/storage -ClassName MSFT_PhysicalDisk -Filter "MediaType=4" #4 -eq SSD $SSDs = $SSDs | where UniqueId -ne $SystemDisk.UniqueId | select $One_TB = 1000000000000 $One_GB = 1000000000 $TotalSSDspace = ($SSDs | measure -Property Size -Sum).Sum $SSD_Count = ($SSDs | Measure-Object).Count if ($SSD_Count -eq 0){ Write-Warning "No non-system SSD found, therefore Current_MaxParallelPlots will be 0. (Ignore if using mutiple HDDs)" } if ($Threads -gt ($Processor.NumberOfCores * 2)){ Write-Warning "Threads may actually only be half what is reported and therefore all calculations are off." } $SSD_MAX = [math]::Floor([decimal]($TotalSSDspace / (256.6 * $One_GB))) if ($MaxParallelCountCPU -le $MaxParallelCountRAM){ $MAXCount = $MaxParallelCountCPU $BottleNeck = "CPU" } else{ $MAXCount = $MaxParallelCountRAM $BottleNeck = "RAM" } $Suggested_SSD_TB = [math]::Ceiling([decimal](256.6 * $MAXCount) / 1000) if ($SSD_MAX -le $MAXCount){ $CurrentMax = $SSD_MAX $BottleNeck = "SSD" } else{ $CurrentMax = $MAXCount } $Suggested_SSD_TB = [math]::Ceiling([decimal](256.6 * $MAXCount) / 1000) [PSCustomObject]@{ ThreadCount = $ThreadCount Buffer = $BufferMiB CPUTotalThreads = $Threads CPUCores = ($Processor | Measure -Property NumberOfCores -Sum).Sum NumberOfProcessors = ($Processor | measure).Count TotalRAM_MiB = $RAM BottleNeck = $BottleNeck Current_SSD_SPACE_TB = [math]::Round(($TotalSSDspace / $One_TB),2) Current_SSD_Count = $SSD_Count Suggested_SSD_SPACE_TB = $Suggested_SSD_TB Current_MaxParallelPlots = $CurrentMax Potential_MAXParallelPlots = $MAXCount } } function Get-ChiaPlotProgress { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateScript({Test-Path -Path $_})] [string]$LogPath ) if ([System.IO.Directory]::Exists($LogPath)){ Write-Error "You provided a directory path and not a file path to the log file" -ErrorAction Stop } #base code from https://github.com/swar/Swar-Chia-Plot-Manager/blob/7287eef4796dbfa4cc009086c6502d19f0706f3e/config.yaml.default $phase1_line_end = 801 $phase2_line_end = 834 $phase3_line_end = 2474 $phase4_line_end = 2620 $copyfile_line_end = 2627 $phase1_weight = 33 $phase2_weight = 20 $phase3_weight = 42 $phase4_weight = 3 $copyphase_weight = 2 $LogItem = Get-Item -Path $LogPath $StartTime = $LogItem.CreationTime $EndTime = Get-Date $ElaspedTime = New-TimeSpan -Start $StartTime -End $EndTime $LogFile = Get-Content -Path $LogPath $plotId = $LogFile | Select-String -SimpleMatch "ID: " | foreach {$_.ToString().Split(" ")[1]} $line_count = $LogFile.Count if ($line_count -ge $phase1_line_end){ $progress += $phase1_weight } else{ $progress += $phase1_weight * ($line_count / $phase1_line_end) $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $progress $secondsRemaining = [int]($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return [PSCustomObject]@{ Progress = [math]::Round($progress,2) Phase = "Phase 1" ElaspedTime = $ElaspedTime EST_TimeReamining = New-TimeSpan -Seconds $secondsRemaining PlotId = $plotId } } if ($line_count -ge $phase2_line_end){ $progress += $phase2_weight } else{ $progress += $phase2_weight * (($line_count - $phase1_line_end) / ($phase2_line_end - $phase1_line_end)) $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $progress $secondsRemaining = [int]($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return [PSCustomObject]@{ Progress = [math]::Round($progress,2) Phase = "Phase 2" ElaspedTime = $ElaspedTime EST_TimeReamining = New-TimeSpan -Seconds $secondsRemaining PlotId = $plotId } } if ($line_count -ge $phase3_line_end){ $progress += $phase3_weight } else{ $progress += $phase3_weight * (($line_count - $phase2_line_end) / ($phase3_line_end - $phase2_line_end)) $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $progress $secondsRemaining = [int]($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return [PSCustomObject]@{ Progress = [math]::Round($progress,2) Phase = "Phase 3" ElaspedTime = $ElaspedTime EST_TimeReamining = New-TimeSpan -Seconds $secondsRemaining PlotId = $plotId } } if ($line_count -ge $phase4_line_end){ $progress += $phase4_weight } else{ $progress += $phase4_weight * (($line_count - $phase3_line_end) / ($phase4_line_end - $phase3_line_end)) $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $progress $secondsRemaining = [int]($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return [PSCustomObject]@{ Progress = [math]::Round($progress,2) Phase = "Phase 4" ElaspedTime = $ElaspedTime EST_TimeReamining = New-TimeSpan -Seconds $secondsRemaining PlotId = $plotId } } if ($line_count -lt $copyfile_line_end){ $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $progress $secondsRemaining = [int]($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return [PSCustomObject]@{ Progress = [math]::Round($progress,2) Phase = "Copying" ElaspedTime = $ElaspedTime EST_TimeReamining = New-TimeSpan -Seconds $secondsRemaining PlotId = $plotId } } $progress += $copyphase_weight return [PSCustomObject]@{ Progress = [math]::Round($progress,2) Phase = "Completed" ElaspedTime = New-TimeSpan -Start $StartTime -End $LogItem.LastWriteTime EST_TimeReamining = 0 PlotId = $plotId } } function Get-ChiaPlottingStatistic { [CmdletBinding()] param( [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)] [string[]]$Path = (Get-ChildItem -Path $env:USERPROFILE\.chia\mainnet\plotter\ | sort CreationTime -Descending).FullName ) Process{ foreach ($log in $path){ if (Test-Path $log){ $Content = Get-Content -Path $log | Select-String "ID: ","Time for phase","Total time","Plot size","Buffer size","threads of stripe","Copy time","Copied final file from","Starting plotting progress into temporary dirs" | foreach {$_.ToString()} foreach ($line in $Content){ switch -Wildcard ($line){ "ID: *" {$PlotID = $line.Split(' ') | select -Skip 1 -First 1} "Plot size*" {$PlotSize = $line.split(' ') | select -Skip 3} #using select for these since indexing will error if empty "Buffer Size*" {$BufferSize = ($line.Split(' ') | select -Skip 3).split("M") | select -First 1} "*threads*" {$ThreadCount = $line.split(' ') | select -First 1 -Skip 1} "*phase 1*" {$Phase_1 = $line.Split(' ') | select -First 1 -Skip 5} "*phase 2*" {$Phase_2 = $line.Split(' ') | select -First 1 -Skip 5} "*phase 3*" {$Phase_3 = $line.Split(' ') | select -First 1 -Skip 5} "*phase 4*" {$phase_4 = $line.Split(' ') | select -First 1 -Skip 5} "Total time*" {$TotalTime = $line.Split(' ') | select -First 1 -Skip 3} "Copy time*" {$CopyTime = $line.Split(' ') | select -First 1 -Skip 3} "Starting plotting progress into temporary dirs*" {$TempDrive = ($line.Split(' ') | select -First 1 -Skip 6).Split('\') | select -First 1 } "Copied final file from*" {$FinalDrive = ($line.Split(' ') | select -First 1 -Skip 6).Split('\').Replace('"', '') | select -First 1} default {Write-Information "Could not match line: $line"} } } [PSCustomObject]@{ PSTypeName = "PSChiaPlotter.ChiaPlottingStatistic" PlotId = $PlotID KSize = $PlotSize "RAM(MiB)" = $BufferSize Threads = $ThreadCount "Phase_1_sec" = [int]$Phase_1 "Phase_2_sec" = [int]$Phase_2 "Phase_3_sec" = [int]$Phase_3 "Phase_4_sec" = [int]$phase_4 "TotalTime_sec" = [int]$TotalTime "CopyTime_sec" = [int]$CopyTime "PlotAndCopyTime_sec" = ([int]$CopyTime + [int]$TotalTime) "Time_Started" = (Get-Item -Path $log).CreationTime "Temp_drive" = $TempDrive "Final_drive" = $FinalDrive } Clear-Variable -Name "PlotID","PlotSize","BufferSize","ThreadCount","Phase_1","Phase_2","Phase_3","Phase_4","TotalTime","CopyTime","FinalDrive","TempDrive" -ErrorAction SilentlyContinue } } } } function Get-ChiaProcessCounter{ [CmdletBinding()] param( [Parameter(Mandatory)] [int[]]$ChiaPID ) foreach ($ID in $ChiaPID){ $QueryString += "OR IDProcess=$ID " } $Performance = Get-CimInstance -Query "Select workingSetPrivate,PercentProcessorTime,IDProcess FROM Win32_PerfFormattedData_PerfProc_Process WHERE NAME='_Total' $QueryString" $TotalCPU = $Performance | where {$_.Name -eq '_Total'} $ChiaProcesses = $Performance | where {$_.Name -ne '_Total'} foreach ($process in $ChiaProcesses){ if ($process.PercentProcessorTime -ne 0){ $CPUPer = ($process.PercentProcessorTime / $TotalCPU.PercentProcessorTime) * 100 $RoundedCPU = [math]::Round($CPUPer,2) } else{$CPUPer = 0} [PSCustomObject]@{ ChiaPID = $process.IDProcess CPUPercent = $RoundedCPU } } } function Get-ChiaRAMInfo { [CmdletBinding()] param( ) Begin{ } #Begin Process{ $Array = Get-CimInstance -Class Win32_PhysicalMemoryArray $CurrentRAM = Get-CimInstance -Class Win32_PhysicalMemory [PSCustomObject]@{ PSTypeName = "PSChiaPlotter.RAMInfo" ComputerName = $ENV:COMPUTERNAME SlotsInUse = ($CurrentRAM | Measure).Count SlotsFree = $Array.MemoryDevices - ($CurrentRAM | Measure).Count CurrentSize_GB = (($CurrentRAM).Capacity | Measure -Sum).Sum / 1gb MaxSize_GB = $Array.MaxCapacityEx / 1mb PartNumber = ($CurrentRAM.PartNumber | Select -Unique | foreach {$_.Trim()}) Manufacturer = ($CurrentRAM.Manufacturer | Select -Unique | foreach {$_.Trim()}) TotalSlots = $Array.MemoryDevices RAMDevices = $CurrentRAM } } #Process } function Show-ChiaPlottingStatistic{ [CmdletBinding()] param( [ValidateNotNullOrEmpty()] [string[]]$LogPath ) $LogStats = Get-ChiaPlottingStatistic -Path $Logpath $newlogstats = foreach ($stat in $LogStats){ try{ $phase1 = New-TimeSpan -Seconds $stat.Phase_1_sec $phase2 = New-TimeSpan -Seconds $stat.Phase_2_sec $phase3 = New-TimeSpan -Seconds $stat.Phase_3_sec $phase4 = New-TimeSpan -Seconds $stat.Phase_4_sec $totaltime = New-TimeSpan -Seconds $stat.TotalTime_sec $copyTime = New-TimeSpan -Seconds $stat.CopyTime_sec $copyandplot = New-TimeSpan -Seconds $stat.PlotAndCopyTime_sec if ($stat.PlotId){ $stat | Add-Member -NotePropertyMembers @{ Phase_1 = $phase1 Phase_2 = $phase2 Phase_3 = $phase3 Phase_4 = $phase4 PlotTime = $totaltime CopyPhase = $copyTime PlotAndCopy = $copyandplot } $stat } } catch{ Write-Information "Unable to add time span properties" } } if ($logPath.Count -eq 1){ $WPF = Join-Path -Path $MyInvocation.MyCommand.Module.ModuleBase -ChildPath "WPFWindows" $XAMLPath = Join-Path -Path $WPF -ChildPath ChiaLogStats.xaml $ChiaLogWindow = Import-Xaml -Path $XAMLPath $ChiaLogWindow.DataContext = $newlogstats $ChiaLogWindow.ShowDialog() | Out-Null } else{ $WPF = Join-Path -Path $MyInvocation.MyCommand.Module.ModuleBase -ChildPath "WPFWindows" $XAMLPath = Join-Path -Path $WPF -ChildPath ChiaLogStatsGrid.xaml $ChiaLogWindow = Import-Xaml -Path $XAMLPath $DataGrid = $ChiaLogWindow.FindName("DataGrid") $DataGrid.ItemsSource = $newlogstats $ChiaLogWindow.ShowDialog() | Out-Null } } function Show-PSChiaPlotter { [CmdletBinding()] param( [switch]$DebugWithNotepad, [Parameter(DontShow)] [switch]$NoNewWindow, [Parameter(DontShow)] [int]$Threads = [int]$ENV:NUMBER_OF_PROCESSORS ) Add-Type -AssemblyName PresentationFramework $PSChiaPlotterFolderPath = "$ENV:LOCALAPPDATA\PSChiaPlotter" if (-not(Test-Path -Path $PSChiaPlotterFolderPath)){ New-Item -Path $PSChiaPlotterFolderPath -ItemType Directory | Out-Null } if (-not$PSBoundParameters.ContainsKey("Threads")){ $Threads = [int]$ENV:NUMBER_OF_PROCESSORS if ($Threads -eq 0){ Write-Warning "Unable to grab the CPU thread count... please enter the thread count below" $Respnose = Read-Host -Prompt "How many CPU Threads does this system have?" foreach ($char in $Respnose.ToCharArray()){ if (-not[char]::IsNumber($char)){ Write-Warning "You didn't enter in a number..." return } } #foreach $Threads = [int]$Respnose if (([int]$Threads -le 0)){ Write-Warning "You didn't enter in a number above 0... exiting" return } } } $PSChiaPlotterFolderPath = Join-Path -Path $ENV:LOCALAPPDATA -ChildPath 'PSChiaPlotter\Logs' $LogName = (Get-Date -Format yyyyMMdd) + '_debug.log' $LogPath = Join-Path -Path $PSChiaPlotterFolderPath -ChildPath $LogName if (-not$NoNewWindow.IsPresent){ $parameters = @{ FilePath = "powershell.exe" ArgumentList = "-NoExit -NoProfile -STA -Command Show-PSChiaPlotter -NoNewWindow -Threads $Threads" WindowStyle = "Hidden" RedirectStandardOutput = $LogPath } Start-Process @parameters return } #Start-Transcript -Path $LogPath | Out-Null $Global:UIHash = [hashtable]::Synchronized(@{}) $Global:DataHash = [hashtable]::Synchronized(@{}) $Global:ScriptsHash = [hashtable]::Synchronized(@{}) $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() $UISync = [System.Management.Automation.Runspaces.SessionStateVariableEntry]::new("UIHash", $UIHash, $Null) $DataSync = [System.Management.Automation.Runspaces.SessionStateVariableEntry]::new("DataHash", $DataHash, $Null) $ScriptsSync = [System.Management.Automation.Runspaces.SessionStateVariableEntry]::new("ScriptsHash", $ScriptsHash, $Null) $InitialSessionState.Variables.Add($UISync) $InitialSessionState.Variables.Add($DataSync) $InitialSessionState.Variables.Add($ScriptsSync) $MaxThreads = ([int]$Threads + 5) $RunspacePool = [runspacefactory]::CreateRunspacePool(1,$MaxThreads,$InitialSessionState,$Host) $RunspacePool.ApartmentState = "STA" $RunspacePool.ThreadOptions = "ReuseThread" $RunspacePool.open() #DataHash Adding Properties $DataHash.ModuleRoot = $MyInvocation.MyCommand.Module.ModuleBase $DataHash.PrivateFunctions = Join-Path -Path $DataHash.ModuleRoot -ChildPath "Private" $DataHash.WPF = Join-Path -Path $DataHash.ModuleRoot -ChildPath "WPFWindows" $DataHash.Classes = Join-Path -Path $DataHash.ModuleRoot -ChildPath "Classes" $DataHash.Runspaces = New-Object System.Collections.Generic.List[System.Object] #DEBUG SWITCH $DataHash.Debug = $DebugWithNotepad.IsPresent $DataHash.LogPath = $LogPath $ScriptsHash.RunspacePool = $RunspacePool #Import required assemblies and private functions Get-childItem -Path $DataHash.PrivateFunctions -File | ForEach-Object {Import-Module $_.FullName} #Get-childItem -Path $DataHash.Assemblies -File | ForEach-Object {Add-Type -Path $_.FullName} #$QueueRunspace = New-ChiaQueueRunspace #$QueueRunspace.Runspacepool = $RunspacePool #$ScriptsHash.QueueRunspace = $QueueRunspace #Create UI Thread $UIRunspace = New-UIRunspace $UIRunspace.RunspacePool = $RunspacePool $DataHash.UIRunspace = $UIRunspace $DataHash.UIHandle = $UIRunspace.BeginInvoke() $Date = Get-Date -Format "[yyyy-MM-dd.HH:mm:ss]" Write-Host "[INFO]-$Date-Staring PSChiaPlotter GUI - PID = $($PID)" $UIHash.NewWindow = $true $UIHash.PowershellPID = $PID $RunspacePoolEvent = Register-ObjectEvent -InputObject $DataHash.UIRunspace -EventName InvocationStateChanged -Action { $NewState = $Event.Sender.InvocationStateInfo.State #Get-childItem -Path $DataHash.PrivateFunctions -File | ForEach-Object {Import-Module $_.FullName} #for some reason importing the private functions is not working... $Date = Get-Date -Format "[yyyy-MM-dd.HH:mm:ss]" if ($NewState -eq "Completed"){ try{ $ScriptsHash.RunspacePool.Close() $ScriptsHash.RunspacePool.Dispose() if ($UIHash.NewWindow){ $Message = "PSChiaPlotter has been closed... Stopping Powershell process with PID of $($UIHash.PowershellPID)" Write-Host "[INFO]-$Date-$Message" Stop-Process -Id $UIHash.PowershellPID -Force } } catch{ $Date = Get-Date -Format "[yyyy-MM-dd.HH:mm:ss]" $Message = "PSChiaPlotter has been closed... unable to stop powershell process with PID of $($UIHash.PowershellPID)" Write-Host "[WARNING]-$Date-$WARNING-$Message" Write-Host "[ERROR]-$Date-$($_.InvocationInfo.ScriptLineNumber)-$($_.Exception.Message)" } } else{ #do nothing } } } <# .SYNOPSIS This will display your harvesters lookup times in graphical heatmap. .DESCRIPTION This function will display harvesters lookup times in graphcial heatmap by scaling the seconds to an RGB color and creating a color block with that color. The number of eligible proofs are placed inside the color block and proof found block will be blue. A summary is also generated and placed in the title of powershell window. The function will continue to look at the log file and update the heatmap with new harvester activities as they come in. .EXAMPLE PS C:\> Start-ChiaHarvesterWatcher |1| | | | |2| | | |.... | | |2|1|3| | | |1|.... This will create the default heat map. With good time showing up as bright green and transitions to yellow then orange then red as the get closer to or above 5 seconds. .EXAMPLE PS C:\> Start-ChiaHarvesterWatcher -DarkMode |1| | | | |2| | | |.... | | |2|1|3| | | |1|.... This will create a dark mode version of the heatmap that goes from gray (closer to 0 seconds) to yellow/orange/red as it gets clsoer to 5 seconds. .EXAMPLE PS C:\>Start-ChiaHarvesterWatcher -MaxLookUpSeconds 2 -NoWalls 1 11 2 3 ... 21 1 3 1 ... This example will have lookup times close to 0 still be bright green, but the color transitions faster to red since the MaxLookUpSeconds is set to 2. Nowall switch takes away the | between color blocks. .INPUTS Inputs (if any) .OUTPUTS Output (if any) .NOTES Requires Chia log levels to be set to INFO. #> function Start-ChiaHarvesterWatcher { [CmdletBinding()] param( [string]$DebugLogFilePath = (Get-ChildItem -Path "$ENV:USERPROFILE\.chia\mainnet\log" -filter "debug.log").FullName, [ValidateRange(0,1000)] [double]$MaxLookUpSeconds = 5, [Parameter()] [ValidateScript({[System.IO.Directory]::Exists((Split-Path -Path $_ -Parent))})] [string]$ExportCSVPath, [switch]$DarkMode, [switch]$NoWalls ) if ($PSBoundParameters.ContainsKey("ExportCSVPath")){ if (-not($ExportCSVPath.EndsWith('.csv'))){ Write-Warning "Export CSV Path does not end with .csv, please provide a valid CSV path and run the command again... exiting." return } } $chiaharvesterlog = "([0-9:.\-T]*) harvester chia.harvester.harvester: INFO\s*([0-9]*) plots were eligible for farming ([a-z0-9.]*) Found ([0-9]*) proofs. Time: ([0-9.]*) s. Total ([0-9]*) plots" $BestSpeed = 1000 $WorstSpeed = 0 $Over1Seconds = 0 $Over5Seconds = 0 $Over30Seconds = 0 $TotalAttempts = 0 $TotalFilterRatio = 0 $TotalLookupTime = 0 $proofsFound = 0 Get-Content -Path $DebugLogFilePath -Wait | foreach-object { switch -Regex ($_){ $chiaharvesterlog { $harvesterActivity = [pscustomobject]@{ Time = [datetime]::parse($Matches[1]) EligiblePlots = [int]$Matches[2] LookUpTime = [double]$Matches[5] ProofsFound = [int]$Matches[4] TotalPlots = [int]$Matches[6] FilterRatio = 0 } #psobject try { #Prevent the divide by zero error message $harvesterActivity.FilterRatio = $Matches[2] / $Matches[6] } catch { } $TotalAttempts++ switch ($harvesterActivity.LookUpTime) { {$_ -lt $BestSpeed} {$BestSpeed = $_} {$_ -gt $WorstSpeed} {$WorstSpeed = $_} {$_ -ge 1} {$Over1Seconds++} {$_ -ge 5} {$Over5Seconds++} {$_ -ge 30} {$Over30Seconds++} } if ($PSBoundParameters.ContainsKey("ExportCSVPath")){ $harvesterActivity | Export-Csv -Path $ExportCSVPath -Append } $proofsFound += $harvesterActivity.ProofsFound $TotalLookupTime += $harvesterActivity.LookUpTime $AverageSpeed = [math]::Round(($TotalLookupTime / $TotalAttempts),5) $TotalFilterRatio += $harvesterActivity.FilterRatio $newRatio = [math]::Round(($TotalFilterRatio / $TotalAttempts),5) $eligibleplots = " " if ($harvesterActivity.EligiblePlots -gt 0){ $eligibleplots = $harvesterActivity.EligiblePlots } $host.UI.RawUI.WindowTitle = "Total Attempts: $TotalAttempts || LookUp Time - Best: $BestSpeed , Worst: $WorstSpeed , Avg: $AverageSpeed || Over 1 Sec:$Over1Seconds, Over 5 Sec: $Over5Seconds, Over 30 Sec: $Over30Seconds || FilterRatio: $newRatio || Proofs Found: $proofsFound || Last Look up Time: $($harvesterActivity.LookUpTime)" $RGB = [math]::Round((255 * $harvesterActivity.LookUpTime) / $MaxLookUpSeconds) $RGBText = @{ bRed = ([math]::Min($RGB,255)) bGreen = ([math]::max([math]::Min(255,(510 - $RGB)),0)) UnderLine = $true Text = "$eligibleplots|" NoNewLine = $true } if ($DarkMode){ if ($harvesterActivity.LookUpTime -le $MaxLookUpSeconds){ $RGBText["bred"] = [math]::Min(($RGB / 2)+20,255) $RGBText["bgreen"] = [math]::Min(($RGB / 2)+20,255) $RGBText["bblue"] = 20 } } #Darkmode if ($NoWalls){ $RGBText["UnderLine"] = $false $RGBText["Text"] = $eligibleplots } #NoWalls if ($harvesterActivity.ProofsFound){ $RGBText["bred"] = 0 $RGBText["bgreen"] = 0 $RGBText["bblue"] = 255 } #if proofs found Write-RGBText @RGBText } #chia activity } #switch } #foreach } function Start-ChiaParallelPlotting { param( [ValidateRange(1,128)] [int]$ParallelCount = 1, [ValidateRange(0,[int]::MaxValue)] [Alias("Delay")] [int]$DelayInSeconds = 3600, [int]$PlotsPerQueue = 1, [ValidateRange(3390,[int]::MaxValue)] [int]$Buffer = 3390, [ValidateRange(1,128)] [int]$Threads = 2, [Parameter(Mandatory)] [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$TempDirectoryPath, [Parameter(Mandatory)] [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$FinalDirectoryPath, [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$LogDirectoryPath = "$ENV:USERPROFILE\.chia\mainnet\plotter", [Parameter()] [string]$FarmerPublicKey, [Parameter()] [string]$PoolPublicKey, [Parameter()] [ValidateRange(1,[int]::MaxValue)] [int]$Buckets, [Parameter()] [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$SecondTempDirectoryPath, [switch]$DisableBitfield, [switch]$ExcludeFinalDirectory, [switch]$NoExit, [ValidateNotNullOrEmpty()] [string]$WindowTitle ) $AdditionalParameters = "" if ($PSBoundParameters.ContainsKey("WindowTitle")){ $AdditionalParameters += " -WindowTitle $WindowTitle" } if ($PSBoundParameters.ContainsKey("FarmerPublicKey")){ $AdditionalParameters += " -FarmerPublicKey $FarmerPublicKey" } if ($PSBoundParameters.ContainsKey("PoolPublicKey")){ $AdditionalParameters += " -PoolPublicKey $PoolPublicKey" } if ($PSBoundParameters.ContainsKey("Buckets")){ $AdditionalParameters += " -Buckets $Buckets" } if ($PSBoundParameters.ContainsKey("SecondTempDirectoryPath")){ $AdditionalParameters += " -SecondTempDirectoryPath '$SecondTempDirectoryPath'" } if ($DisableBitfield){ $AdditionalParameters += " -DisableBitfield" } if ($ExcludeFinalDirectory){ $AdditionalParameters += " -ExcludeFinalDirectory" } for ($Queue = 1; $Queue -le $ParallelCount;$Queue++){ if ($NoExit){ $NoExitFlag = "-NoExit" } $ChiaArguments = "-TotalPlots $plotsperQueue -Buffer $Buffer -Threads $Threads -TempDirectoryPath '$TempDirectoryPath' -FinalDirectoryPath '$FinalDirectoryPath' -LogDirectoryPath '$LogDirectoryPath' -QueueName Queue_$Queue $AdditionalParameters" $processParam = @{ FilePath = "powershell.exe" ArgumentList = "$NoExitFlag -Command Start-ChiaPlotting $ChiaArguments" } Start-Process @processParam if ($Queue -lt $ParallelCount){ Start-Sleep -Seconds $DelayInSeconds } } #for } function Start-ChiaPlotting { [CmdletBinding()] param( [ValidateRange(32,35)] [int]$KSize = 32, [ValidateRange(1,5000)] [int]$TotalPlots = 1, [int]$Buffer, [ValidateRange(1,256)] [int]$Threads = 2, [switch]$DisableBitfield, [switch]$ExcludeFinalDirectory, [Parameter(Mandatory)] [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$TempDirectoryPath, [Parameter()] [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$SecondTempDirectoryPath, [Parameter(Mandatory)] [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$FinalDirectoryPath, [Parameter()] [string]$FarmerPublicKey, [Parameter()] [string]$PoolPublicKey, [Parameter()] [ValidateRange(1,[int]::MaxValue)] [int]$Buckets, [ValidateScript({[System.IO.Directory]::Exists($_)})] [string]$LogDirectoryPath = "$ENV:USERPROFILE\.chia\mainnet\plotter", [switch]$NewWindow, [string]$QueueName = "Default_Queue", [string]$WindowTitle ) if (-not$PSBoundParameters.ContainsKey("Buffer")){ switch ($KSize){ 32 {$Buffer = 3390} 33 {$Buffer = 7400} 34 {$Buffer = 14800} 35 {$Buffer = 29600} } Write-Information "Buffer set to: $Buffer" } if ($PSBoundParameters.ContainsKey("WindowTitle")){ $WindowTitle = $WindowTitle + " |" } $E = if ($DisableBitfield){"-e"} $X = if ($ExcludeFinalDirectory){"-x"} #remove any trailing '\' since chia.exe hates them $TempDirectoryPath = $TempDirectoryPath.TrimEnd('\') $FinalDirectoryPath = $FinalDirectoryPath.TrimEnd('\') #path to chia.exe $ChiaPath = (Get-Item -Path "$ENV:LOCALAPPDATA\chia-blockchain\app-*\resources\app.asar.unpacked\daemon\chia.exe").FullName $ChiaArguments = "plots create -k $KSize -b $Buffer -r $Threads -t `"$TempDirectoryPath`" -d `"$FinalDirectoryPath`" $E $X" if ($PSBoundParameters.ContainsKey("SecondTempDirectoryPath")){ $SecondTempDirectoryPath = $SecondTempDirectoryPath.TrimEnd('\') $ChiaArguments += " -2 $SecondTempDirectoryPath" Write-Information "Added 2nd Temp Dir to Chia ArguementList" } if ($PSBoundParameters.ContainsKey("FarmerPublicKey")){ $ChiaArguments += " -f $FarmerPublicKey" } if ($PSBoundParameters.ContainsKey("PoolPublicKey")){ $ChiaArguments += " -p $PoolPublicKey" } if ($PSBoundParameters.ContainsKey("Buckets")){ $ChiaArguments += " -u $Buckets" } if ($ChiaPath){ Write-Information "Chia path exists, starting the plotting process" if (!$NewWindow){ for ($plotNumber = 1;$plotNumber -le $TotalPlots;$plotNumber++){ try{ $LogPath = Join-Path $LogDirectoryPath ((Get-Date -Format yyyy_MM_dd_hh-mm-ss-tt_) + "plotlog-" + $plotNumber + ".log") $PlottingParam = @{ FilePath = $ChiaPath ArgumentList = $ChiaArguments RedirectStandardOutput = $LogPath NoNewWindow = $true } $chiaProcess = Start-Process @PlottingParam -PassThru $host.ui.RawUI.WindowTitle = "$WindowTitle $QueueName - Plot $plotNumber out of $TotalPlots | Chia Process Id - $($chiaProcess.id)" #Have noticed that giving the process a second to start before checking the logs works better Start-Sleep 1 while (!$chiaProcess.HasExited){ try{ $progress = Get-ChiaPlotProgress -LogPath $LogPath -ErrorAction Stop $plotid = $progress.PlotId #write-progress will fail if secondsremaining is less than 0... $secondsRemaining = $progress.EST_TimeReamining.TotalSeconds if ($progress.EST_TimeReamining.TotalSeconds -le 0){ $secondsRemaining = 0 } Write-Progress -Activity "Queue $($QueueName): Plot $plotNumber out of $TotalPlots" -Status "$($progress.phase) - $($progress.Progress)%" -PercentComplete $progress.progress -SecondsRemaining $secondsRemaining Start-Sleep 5 } catch{ Write-Progress -Activity "Queue $($QueueName): Plot $plotNumber out of $TotalPlots" -Status "WARNING! PROGRESS UPDATES HAS FAILED! $($progress.phase) - $($progress.Progress)%" -PercentComplete $progress.progress -SecondsRemaining $secondsRemaining Start-Sleep 30 } } #while if ($chiaProcess.ExitCode -ne 0){ Get-ChildItem -Path $TempDirectoryPath -Filter "*$plotid*.tmp" | Remove-Item -Force } } catch{ $PSCmdlet.WriteError($_) } } #for } #if noNewWindow else{ $ChiaArguments += " -n $TotalPlots" $PlottingParam = @{ FilePath = $ChiaPath ArgumentList = $ChiaArguments RedirectStandardOutput = $LogPath } $PlottingProcess = Start-Process @PlottingParam -PassThru [PSCustomObject]@{ KSize = $KSize Buffer = $Buffer Threads = $Threads PID = $PlottingProcess.Id StartTime = $PlottingProcess.StartTime TempDir = $TempDirectoryPath FinalDir = $FinalDirectoryPath TempDir2 = $SecondTempDirectoryPath LogPath = $LogPath TotalPlotCount = $TotalPlots BitfieldEnabled = !$DisableBitfield.IsPresent ExcludeFinalDir = $ExcludeFinalDirectory.IsPresent } Write-Information "Plotting started, PID = $PID" } # else } #if chia path exits } function Get-BestChiaFinalDrive { [CmdletBinding()] param( $ChiaVolumes ) $finalplotsize = 101.4 * 1gb $AllVolumes = Get-ChiaVolume foreach ($finalvol in $ChiaVolumes){ $newVolumeInfo = $AllVolumes | where UniqueId -eq $finalvol.UniqueId $finalvol.FreeSpace = $newVolumeInfo.FreeSpace $MasterVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $finalvol.UniqueId $finalvol.PendingFinalRuns = $MasterVolume.PendingFinalRuns } $sortedVolumes = $ChiaVolumes | Sort-Object -Property @{Expression = {$_.PendingFinalRuns.Count}; Descending = $false},@{Expression = "FreeSpace"; Descending = $True} foreach ($volume in $sortedVolumes){ if (($volume.FreeSpace - ($Volume.PendingFinalRuns.Count * $finalplotsize)) -gt $finalplotsize){ return $volume } } } function Get-BestChiaTempDrive { [CmdletBinding()] param( $ChiaVolumes, $ChiaJob ) $requiredTempSize = 239 * 1gb $finalplotsize = 101.4 * 1gb $AllVolumes = Get-ChiaVolume foreach ($tempvol in $ChiaVolumes){ $newVolumeInfo = $AllVolumes | where UniqueId -eq $tempvol.UniqueId $tempvol.FreeSpace = $newVolumeInfo.FreeSpace $MasterVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $tempvol.UniqueId $tempvol.CurrentChiaRuns = $MasterVolume.CurrentChiaRuns $tempvol.PendingFinalRuns = $MasterVolume.PendingFinalRuns } $sortedVolumes = $ChiaVolumes | sort -Property @{Expression = {$_.CurrentChiaRuns.Count}; Descending = $false},@{Expression = "FreeSpace"; Descending = $True} foreach ($volume in $sortedVolumes){ #$MasterVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $volume.UniqueId if (($Volume.CurrentChiaRuns.Count -lt $volume.MaxConCurrentTempChiaRuns) -or ($ChiaJob.IgnoreMaxParallel)){ if (($volume.FreeSpace - ($Volume.PendingFinalRuns.Count * $finalplotsize)) -gt $requiredTempSize){ return $volume } } } #foreach } function Get-ChiaTempSize{ [CmdletBinding()] param( $DirectoryPath, $PlotId ) try{ if ($PlotId -ne $null){ try{ #this will actually get the size on disk $tepmSize = (Get-ChildItem -Path $DirectoryPath -Filter "*$plotid*.tmp" | foreach {[Disk.Size]::SizeOnDisk($_.FullName)} | measure -Sum).Sum return [math]::Round($tepmSize / 1gb) } catch{ $tepmSize = (Get-ChildItem -Path $DirectoryPath -Filter "*$plotid*.tmp" | Measure-Object -Property Length -Sum).Sum return [math]::Round($tepmSize / 1gb) } } else{ return 0 } } catch{ return 0 } } function Get-ChiaVolume { [CmdletBinding()] param() #grabbing all volumes, partitions, disks, and physicaldisks at once since it has proven to be faster $AllVolumes = Get-Volume #filter out all paritions not are not accessible to the file system $AllPartitions = Get-Partition | Where {$_.AccessPaths.Count -gt 1} $AllDisks = Get-Disk $AllphysicalDisk = Get-PhysicalDisk foreach ($volume in $AllVolumes){ try{ $partition = $AllPartitions | where AccessPaths -Contains "$($volume.UniqueId)" $disk = $AllDisks | where DiskNumber -eq $partition.DiskNumber $physicalDisk = $AllphysicalDisk | where DeviceId -eq $disk.DiskNumber if ($physicalDisk -ne $null){ $MediaType = $physicalDisk.MediaType } else{ $MediaType = "Unknown" } $Label = $volume.FileSystemLabel if ([string]::IsNullOrEmpty($volume.FileSystemLabel)){ $Label = "N/A" } $DriveLetter = $volume.DriveLetter if (-not[char]::IsLetter($DriveLetter)){ $DriveLetter = '?' } if ($partition){ $DirectoryPaths = $partition.AccessPaths | where {$_ -ne $volume.UniqueId} $ChiaVolume = [PSChiaPlotter.ChiaVolume]::new($volume.UniqueId,$Label,$volume.Size,$volume.SizeRemaining) $ChiaVolume.BusType = $physicalDisk.BusType $ChiaVolume.MediaType = $MediaType $MaxTempCount = [math]::Floor([decimal]($volume.size / (239 * 1gb))) $ChiaVolume.MaxConCurrentTempChiaRuns = $MaxTempCount $ChiaVolume.DriveLetter = $DriveLetter $ChiaVolume.DirectoryPath = $DirectoryPaths | select -First 1 $DirectoryPaths | foreach {$ChiaVolume.AccessPaths.Add($_)} $ChiaVolume Clear-Variable PhysicalDisk,Disk,Partition,MaxTempCount -ErrorAction SilentlyContinue $Log = @{ LogType = "INFO" Message = "Chia Volume Found: Letter $($ChiaVolume.DriveLetter), UniqueId: $($ChiaVolume.UniqueID)" } Write-PSChiaPlotterLog @Log } } catch{ Write-PSChiaPlotterLog -LogType "Warning" -Message "Unable to create a ChiaVolume from driveletter $($DriveLetter.DriveLetter)" Write-PSChiaPlotterLog -LogType "Error" -LineNumber $_.InvocationInfo.ScriptLineNumber -Message $_.Exception.Message } } #volume $mappedDrives = Get-CimInstance -ClassName Win32_MappedLogicalDisk $BusType = "Network" $MediaType = "Unknown" foreach ($drive in $mappedDrives){ try{ if ([string]::IsNullOrEmpty($drive.ProviderName)){ $Label = "N/A" } else{ $Label = $drive.ProviderName } if (-not[string]::IsNullOrEmpty($drive.DeviceID)){ $DriveLetter = $drive.DeviceID.TrimEnd(':') $ChiaVolume = [PSChiaPlotter.ChiaVolume]::new($drive.VolumeSerialNumber,$Label,$drive.Size,$drive.FreeSpace) $ChiaVolume.BusType = $BusType $ChiaVolume.MediaType = $MediaType $MaxTempCount = [math]::Floor([decimal]($drive.size / (239 * 1gb))) $ChiaVolume.MaxConCurrentTempChiaRuns = $MaxTempCount $ChiaVolume.DriveLetter = $DriveLetter $DirectoryPath = $DriveLetter + ':\' $ChiaVolume.DirectoryPath = $DirectoryPath $ChiaVolume.AccessPaths.Add($DirectoryPath) if (Test-Path $label){ $ChiaVolume.AccessPaths.Add($Label) } $ChiaVolume Clear-Variable DriveLetter -ErrorAction SilentlyContinue $Log = @{ LogType = "INFO" Message = "Chia Volume Found: Letter $($ChiaVolume.DriveLetter), UniquieId: $($ChiaVolume.UniqueID)" } Write-PSChiaPlotterLog @Log } } catch{ Write-PSChiaPlotterLog -LogType "Warning" -Message "Unable to create a ChiaVolume from driveletter $($DriveLetter.DriveLetter)" Write-PSChiaPlotterLog -LogType "Error" -LineNumber $_.InvocationInfo.ScriptLineNumber -Message $_.Exception.Message } } } function Get-MaxKSize { [CmdletBinding()] param( [ValidateSet("K32","K33","K34","K35")] [string[]]$KSize = ("K32","K33","K34","K35"), [Parameter(Mandatory)] [int64]$TotalBytes ) foreach ($size in $KSize){ [MaximizedKSize]::new($size,$TotalBytes) } #foreach } function Get-OptimizedKSizePlotNumbers { [CmdletBinding()] param( [MaximizedKSize[]]$MaximizedKSize ) foreach ($size in $MaximizedKSize){ switch ($size.KSize){ "K32" { [OptimizedKPlots]::new(0,0,0,$Size.TotalBytes) } "K33" { for ($K33Count = 1; $K33Count -le $size.MaxPlots; $K33Count++){ [OptimizedKPlots]::new(0,0,$K33Count,$Size.TotalBytes) } #for } "K34" { for ($K34Count = 1; $K34Count -le $size.maxplots; $K34Count++){ [OptimizedKPlots]::new(0,$K34Count,0,$Size.TotalBytes) $k34sizeremaining = $Size.TotalBytes - ($K34Count * $size.KSizeBytes) $K33Max = Get-MaxKSize -TotalBytes $k34sizeremaining -KSize "K33" for ($k33 = 1; $k33 -le $k33max.MaxPlots; $k33++){ [OptimizedKPlots]::new(0,$K34Count,$k33,$Size.TotalBytes) } #for 33 } #for 34 } #34 "K35" { for ($k35count = 1; $k35count -le $size.maxplots; $k35count++){ [OptimizedKPlots]::new($k35count,0,0,$Size.TotalBytes) $k35sizeremaining = $Size.TotalBytes - ($k35count * $size.KSizeBytes) $k33max = Get-MaxKSize -Totalbytes $k35sizeremaining -KSize "K33" for ($k33 = 1; $k33 -le $k33max.MaxPlots; $k33++){ [OptimizedKPlots]::new($k35count,0,$k33,$Size.TotalBytes) } #for 33 $k34max = Get-MaxKSize -Totalbytes $k35sizeremaining -KSize "K34" for ($k34 = 1; $k34 -le $k34max.maxplots; $k34++){ [OptimizedKPlots]::new($k35count,$k34,0,$Size.TotalBytes) $sizeremaining = $Size.TotalBytes - (($k35count * $size.KSizeBytes) + ($k34 * $k34max.KSizeBytes)) $K33max = Get-MaxKSize -TotalBytes $sizeremaining -KSize "K33" for ($k33 = 1;$k33 -le $k33max.maxplots; $k33++){ [OptimizedKPlots]::new($k35count,$k34,$k33,$Size.TotalBytes) } } } } } #switch } #foreach } function Import-Xaml { param( $PathToXAML ) Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName System.Windows.Forms [xml]$xaml = Get-Content -Path $PathToXAML $manager = [System.Xml.XmlNamespaceManager]::new($xaml.NameTable) $manager.AddNamespace("x","http://schemas.microsoft.com/winfx/2006/xaml") $xamlReader = [System.Xml.XmlNodeReader]::new($xaml) [Windows.Markup.XamlReader]::Load($xamlReader) } function New-ChiaJobRunspace{ param( [Parameter(Mandatory)] $Job ) [powershell]::Create().AddScript{ Param ( $Job ) $ErrorActionPreference = "Stop" Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName System.Windows.Forms #Import required assemblies and private functions Get-childItem -Path $DataHash.PrivateFunctions -File | ForEach-Object {Import-Module $_.FullName} Get-childItem -Path $DataHash.Classes -File | ForEach-Object {Import-Module $_.FullName} for ($queue = 1; $queue -le $Job.QueueCount; $queue++){ try{ $newQueue = [PSChiaPlotter.ChiaQueue]::new($queue,$job.InitialChiaParameters,$job) $newQueue.Status = "Waiting" $DataHash.MainViewModel.AllQueues.Add($newQueue) $Job.Queues.Add($newQueue) } catch{ Show-Messagebox -Text $_.Exception.Message -Title "Job $($Job.JobNumber) - Runspace" } } try{ for ($queue = 0;$queue -lt $Job.QueueCount;$queue++){ if ($queue -eq 0){ sleep -Seconds ($Job.FirstDelay * 60) $Job.Status = "Running" } $Job.Queues[$queue].Status = "Running" $QueueRunspace = New-ChiaQueueRunspace -Queue $Job.Queues[$queue] -Job $Job $QueueRunspace.Runspacepool = $ScriptsHash.Runspacepool [void]$QueueRunspace.BeginInvoke() $DataHash.Runspaces.Add($QueueRunspace) if (($queue + 1) -ne $Job.QueueCount){ #plus 10 seconds for a min delay for data syncing insurance Start-Sleep -Seconds ($Job.DelayInMinutes * 60 + 10) } } } catch{ Show-Messagebox -Text $_.Exception.Message -Title "Job $($Job.JobNumber) - Runspace" | Out-Null } }.AddParameters($PSBoundParameters) } function New-ChiaQueueRunspace { param( [Parameter(Mandatory)] $Queue, $Job ) [powershell]::Create().AddScript{ Param ( $Job, $Queue ) $ErrorActionPreference = "Stop" Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName System.Windows.Forms #Import required assemblies and private functions Get-childItem -Path $DataHash.PrivateFunctions -File | ForEach-Object {Import-Module $_.FullName} Get-childItem -Path $DataHash.Classes -File | ForEach-Object {Import-Module $_.FullName} try{ for ($runNumber = 1;($Job.CompletedRunCount + $Job.RunsInProgress.Count) -lt $Job.TotalPlotCount;$runNumber++){ $ChiaProcess = $Null if ($Queue.Pause){ $Queue.Status = "Paused" while ($Queue.Pause){ sleep 10 } if (($Job.CompletedRunCount + $Job.RunsInProgress.Count) -ge $Job.TotalPlotCount){ break } } #grab a volume that has enough space Do { Try{ $TempVolume = Get-BestChiaTempDrive -ChiaVolumes $Job.TempVolumes -ChiaJob $Job $FinalVolume = Get-BestChiaFinalDrive $Job.FinalVolumes if ($TempVolume -eq $Null){ $Queue.Status = "Waiting on Temp Space" Start-Sleep -Seconds 60 } elseif ($FinalVolume -eq $Null){ $Queue.Status = "Waiting on Final Dir Space" Start-Sleep -Seconds 60 } } catch{ $Queue.Status = "Failed To Grab Volume Info" Start-Sleep -Seconds 30 } } while ($TempVolume -eq $null -or $FinalVolume -eq $null) if (($Job.CompletedRunCount + $Job.RunsInProgress.Count) -ge $Job.TotalPlotCount){ break } $Queue.Status = "Running" $plottingParameters = [PSChiaPlotter.ChiaParameters]::New($Queue.PlottingParameters) $plottingParameters.TempVolume = $TempVolume $plottingParameters.FinalVolume = $FinalVolume $newRun = [PSChiaPlotter.ChiaRun]::new($Queue,$runNumber,$plottingParameters) if ($DataHash.Debug){ Start-GUIDebugRun -ChiaRun $newRun -ChiaQueue $Queue -ChiaJob $Job } else{ #Show-Object $newRun Start-GUIChiaPlotting -ChiaRun $newRun -ChiaQueue $Queue -ChiaJob $Job } #sleep to give some time for updating sleep 2 } $Queue.Status = "Finished" } catch{ Write-PSChiaPlotterLog -LogType "Error" -LineNumber $_.InvocationInfo.ScriptLineNumber -Message $_.Exception.Message Show-Messagebox -Text $_.Exception.Message -Title "Queue - $($Queue.QueueNumber)" | Out-Null if ($ChiaProcess){ Show-Messagebox -Text "The Following Chia Process may be running and might need to killed - PID $($ChiaProcess.Id)" -Title "Queue" | Out-Null } } }.AddParameters($PSBoundParameters) } function New-UIRunspace{ [powershell]::Create().AddScript{ $ErrorActionPreference = "Stop" Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName System.Windows.Forms #[System.Windows.Forms.MessageBox]::Show("Hello") #Import required assemblies and private functions Try{ Get-childItem -Path $DataHash.PrivateFunctions -File | ForEach-Object {Import-Module $_.FullName} Get-childItem -Path $DataHash.Classes -File | ForEach-Object {Import-Module $_.FullName} Import-Module -Name PSChiaPLotter $XAMLPath = Join-Path -Path $DataHash.WPF -ChildPath MainWindow.xaml $MainWindow = Import-Xaml -Path $XAMLPath #Assign GUI Controls To Variables $UIHash.MainWindow = $MainWindow $UIHash.Jobs_DataGrid = $MainWindow.FindName("Jobs_DataGrid") $UIHash.Queues_DataGrid = $MainWindow.FindName("Queues_DataGrid") $UIHash.Runs_DataGrid = $MainWindow.FindName("Runs_DataGrid") $UIHash.CompletedRuns_DataGrid = $MainWindow.FindName("CompletedRuns_DataGrid") $UIHash.Refreshdrives_Button = $MainWindow.FindName("RefreshdrivesButton") #$UIHash.LogLevel_Combobox = $MainWindow.FindName("LogLevelCombobox") $UIHash.CheckForUpdate_Button = $MainWindow.FindName("CheckForUpateButton") $UIHash.OpenLog_Button = $MainWindow.FindName("OpenLogButton") $DataHash.RefreshingDrives = $false $UIHash.NewJob_Button = $MainWindow.FindName("AddJob_Button") $DataHash.MainViewModel = [PSChiaPlotter.MainViewModel]::new() $DataHash.MainViewModel.Version = (Get-Module -Name PSChiaPlotter).Version.ToString() $DataHash.MainViewModel.LogPath = $DataHash.LogPath $DataHash.MainViewModel.LogLevel = "Info" #$UIHash.LogLevel_Combobox.SelectedIndex = 0 $UIHash.MainWindow.DataContext = $DataHash.MainViewModel #Add Master Copy of volumes to MainViewModel these are used to keep track of # all jobs that are running on the drives Get-ChiaVolume | foreach { $DataHash.MainViewModel.AllVolumes.Add($_) } #ButtonClick $UIHash.NewJob_Button.add_Click({ try{ #Get-childItem -Path $DataHash.Classes -File | ForEach-Object {Import-Module $_.FullName} $XAMLPath = Join-Path -Path $DataHash.WPF -ChildPath NewJobWindow.xaml $UIHash.NewJob_Window = Import-Xaml -Path $XAMLPath $jobNumber = $DataHash.MainViewModel.AllJobs.Count + 1 $newJob = [PSChiaPlotter.ChiaJob]::new() $newJob.JobNumber = $jobNumber $newJob.JobName = "Job $jobNumber" $NewJobViewModel = [PSChiaPlotter.NewJobViewModel]::new($newJob) #need to run get-chiavolume twice or the temp and final drives will be the same object in the application and will update each other... Get-ChiaVolume | foreach { $NewJobViewModel.TempAvailableVolumes.Add($_) } Get-ChiaVolume | foreach { $NewJobViewModel.FinalAvailableVolumes.Add($_) } $newJob.Status = "Waiting" $UIHash.NewJob_Window.DataContext = $NewJobViewModel $CreateJob_Button = $UIHash.NewJob_Window.FindName("CreateJob_Button") $CreateJob_Button.add_Click({ try{ $Results = Test-ChiaParameters $newJob if ($NewJob.DelayInMinutes -eq 60){ $response = Show-Messagebox -Text "You left the default delay time of 60 Minutes, continue?" -Button YesNo if ($response -eq [System.Windows.MessageBoxResult]::No){ return } } if ($Results -ne $true){ Show-Messagebox -Text $Results -Title "Invalid Parameters" -Icon Warning return } $DataHash.MainViewModel.AllJobs.Add($newJob) $newJobRunSpace = New-ChiaJobRunspace -Job $newJob $newJobRunSpace.Runspacepool = $ScriptsHash.RunspacePool [void]$newJobRunSpace.BeginInvoke() $DataHash.Runspaces.Add($newJobRunSpace) $UIHash.NewJob_Window.Close() } catch{ Show-Messagebox -Text $_.Exception.Message -Title "Create New Job Error" -Icon Error } }) $CancelJobCreation_Button = $UIHash.NewJob_Window.FindName("CancelJobCreation_Button") $CancelJobCreation_Button.Add_Click({ try{ $UIHash.NewJob_Window.Close() } catch{ Show-Messagebox -Text $_.Exception.Message -Title "Exit New Job Window Error" -Icon Error } }) $UIHash.NewJob_Window.ShowDialog() } catch{ Show-Messagebox -Text $_.Exception.Message -Title "Create New Job Error" -Icon Error } }) $UIHash.Refreshdrives_Button.Add_Click({ try{ if ($DataHash.RefreshingDrives){ Show-Messagebox -Text "A drive refresh is currently in progress" -Icon Information return } $DataHash.RefreshingDrives = $true Update-ChiaVolume -ErrorAction Stop $DataHash.RefreshingDrives = $false } catch{ $DataHash.RefreshingDrives = $false Show-Messagebox -Text $_.Exception.Message -Title "Refresh Drives" -Icon Error } }) $UIHash.CheckForUpdate_Button.Add_Click({ try{ Update-PSChiaPlotter } catch{ Write-PSChiaPlotterLog -LogType ERROR -LineNumber $_.InvocationInfo.ScriptLineNumber -Message $_.Exception.Message Show-Messagebox "Unable to check for updates... check logs for more info" | Out-Null } }) $UIHash.OpenLog_Button.Add_Click({ try{ Invoke-Item -Path $DataHash.MainViewModel.LogPath -ErrorAction Stop } catch{ Write-PSChiaPlotterLog -LogType ERROR -LineNumber $_.InvocationInfo.ScriptLineNumber -Message $_.Exception.Message Show-Messagebox "Unable to open log file, check the path '$($DataHash.MainViewModel.LogPath)'" | Out-Null } }) #$ScriptsHash.QueueHandle = $ScriptsHash.QueueRunspace.BeginInvoke() $UIHash.MainWindow.add_Closing({ Get-childItem -Path $DataHash.PrivateFunctions -File | ForEach-Object {Import-Module $_.FullName} # end session and close runspace on window exit $DialogResult = Show-Messagebox -Text "Closing this window will end all Chia processes" -Title "Warning!" -Icon Warning -Buttons OKCancel if ($DialogResult -eq [System.Windows.MessageBoxResult]::Cancel) { $PSItem.Cancel = $true } else{ #$ScriptsHash.QueueHandle.EndInvoke($QueueHandle) Stop-PSChiaPlotter } }) $MainWindow.ShowDialog() } catch{ $Message = "$($_.Exception.Message)" $Message += "`nLine # -$($_.InvocationInfo.ScriptLineNumber )" $Message += "`nLine - $($_.InvocationInfo.Line)" Show-Messagebox -Text $Message -Title "UI Runspace Error" -Icon Error } } } function Show-Messagebox { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Text, [string]$Title = "Message Box", [System.Windows.MessageBoxButton]$Buttons =[System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]$Icon = [System.Windows.MessageBoxImage]::None ) [System.Windows.MessageBox]::Show($Text,$Title,$Buttons,$Icon) } function Start-GUIChiaPlotting { [CmdletBinding()] param( #[string]$SecondTempDirecoryPath, #$FarmerPublicKey, #$PoolPublicKey, $ChiaRun, $ChiaQueue, $ChiaJob ) #not really needed, but just wanted to make each parameter its own variable $PlottingParameters = $ChiaRun.PlottingParameters $KSize = $PlottingParameters.KSize $Buffer = $PlottingParameters.RAM $Threads = $PlottingParameters.Threads $DisableBitfield = $PlottingParameters.DisableBitField $ExcludeFinalDirectory = $PlottingParameters.ExcludeFinalDirectory $TempDirectoryPath = $PlottingParameters.TempVolume.DirectoryPath $FinalDirectoryPath = $PlottingParameters.FinalVolume.DirectoryPath $LogDirectoryPath = $PlottingParameters.LogDirectory #$SecondTempDirecoryPath = $PlottingParameters.TempVolume.DirectoryPath $PoolPublicKey = $PlottingParameters.PoolPublicKey $FarmerPublicKey = $PlottingParameters.FarmerPublicKey $Buckets = $PlottingParameters.Buckets $E = if ($DisableBitfield){"-e"} $X = if ($ExcludeFinalDirectory){"-x"} #remove any trailing '\' since chia.exe hates them $TempDirectoryPath = $TempDirectoryPath.TrimEnd('\') $FinalDirectoryPath = $FinalDirectoryPath.TrimEnd('\') #path to chia.exe $ChiaPath = (Get-Item -Path "$ENV:LOCALAPPDATA\chia-blockchain\app-*\resources\app.asar.unpacked\daemon\chia.exe").FullName $ChiaArguments = "plots create -k $KSize -b $Buffer -u $Buckets -r $Threads -t `"$TempDirectoryPath`" -d `"$FinalDirectoryPath`" $E $X" if (-not[string]::IsNullOrWhiteSpace($PoolPublicKey)){ $ChiaArguments += " -p $PoolPublicKey" } if (-not[string]::IsNullOrWhiteSpace($FarmerPublicKey)){ $ChiaArguments += " -f $FarmerPublicKey" } if ($ChiaPath){ Write-Information "Chia path exists, starting the plotting process" try{ $LogPath = Join-Path $LogDirectoryPath ((Get-Date -Format yyyy_MM_dd_hh-mm-ss-tt_) + "plotlog-" + $ChiaQueue.QueueNumber + "-" + $ChiaRun.RunNumber + ".log") $ChiaRun.LogPath = $LogPath $PlottingParam = @{ FilePath = $ChiaPath ArgumentList = $ChiaArguments RedirectStandardOutput = $LogPath } $chiaProcess = Start-Process @PlottingParam -PassThru -WindowStyle Hidden #this is 100% require for th exit code to be seen by powershell when redirectingstandardoutput $handle = $chiaProcess.Handle $ChiaRun.ChiaProcess = $ChiaProcess $ChiaRun.ProcessId = $ChiaProcess.Id $ChiaJob.RunsInProgress.Add($ChiaRun) $TempMasterVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $ChiaRun.PlottingParameters.TempVolume.UniqueId $TempMasterVolume.CurrentChiaRuns.Add($ChiaRun) $FinalMasterVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $ChiaRun.PlottingParameters.FinalVolume.UniqueId $FinalMasterVolume.PendingFinalRuns.Add($ChiaRun) $ChiaQueue.CurrentRun = $ChiaRun $DataHash.MainViewModel.CurrentRuns.Add($ChiaRun) $DataHash.MainViewModel.AllRuns.Add($ChiaRun) #Have noticed that giving the process a second to start before checking the logs works better Start-Sleep 1 while (!$chiaProcess.HasExited){ try{ $progress = Get-ChiaPlotProgress -LogPath $LogPath -ErrorAction Stop $plotid = $progress.PlotId $ChiaRun.Progress = $progress.progress $ChiaQueue.CurrentTime = [DateTime]::Now $ChiaRun.CurrentTime = [DateTime]::Now $ChiaRun.Phase = $progress.Phase if ($progress.EST_TimeReamining.TotalSeconds -le 0){ $ChiaRun.EstTimeRemaining = New-TimeSpan -Seconds 0 } else{ $ChiaRun.EstTimeRemaining = $progress.EST_TimeReamining } $ChiaRun.EstTimeRemaining = $progress.EST_TimeReamining $ChiaRun.TempSize = Get-ChiaTempSize -DirectoryPath $TempDirectoryPath -PlotId $plotid Start-Sleep (5 + $ChiaQueue.QueueNumber) } catch{ Start-Sleep 30 } } #while $ChiaJob.RunsInProgress.Remove($ChiaRun) $ChiaJob.CompletedRunCount++ $FinalMasterVolume.PendingFinalRuns.Remove($ChiaRun) $TempMasterVolume.CurrentChiaRuns.Remove($ChiaRun) $ChiaRun.ExitCode = $ChiaRun.ChiaPRocess.ExitCode #if this is null then an error will occur if we try to set this property if ($ChiaRun.ExitTime){ $ChiaRun.ExitTime = $ChiaProcess.ExitTime } if ($ChiaRun.ChiaPRocess.ExitCode -ne 0){ $ChiaRun.Status = "Failed" $ChiaQueue.FailedPlotCount++ $ChiaJob.FailedPlotCount++ $DataHash.MainViewModel.FailedRuns.Add($ChiaRun) Get-ChildItem -Path $TempDirectoryPath -Filter "*$plotid*.tmp" | foreach { try{ Remove-Item -Path $_.FullName -Force -ErrorAction Stop } catch{ Show-Messagebox -Text $_.Exception.Message | Out-Null } } } else{ $ChiaRun.Status = "Completed" $ChiaJob.CompletedPlotCount++ $ChiaQueue.CompletedPlotCount++ $DataHash.MainViewModel.CompletedRuns.Add($ChiaRun) Update-ChiaGUISummary -Success } $DataHash.MainViewModel.CurrentRuns.Remove($ChiaRun) } catch{ if (-not$DataHash.MainViewModel.FailedRuns.Contains($ChiaRun)){ $DataHash.MainViewModel.FailedRuns.Add($ChiaRun) } if ($DataHash.MainViewModel.CurrentRuns.Contains($ChiaRun)){ $DataHash.MainViewModel.CurrentRuns.Remove($ChiaRun) } if ($ChiaJob.RunsInProgress.Contains($ChiaRun)){ $ChiaJob.RunsInProgress.Remove($ChiaRun) } if ($FinalMasterVolume){ if ($FinalMasterVolume.PendingFinalRuns.Contains($ChiaRun)){ $FinalMasterVolume.PendingFinalRuns.Remove($ChiaRun) } } $PSCmdlet.WriteError($_) } } #if chia path exits else{ $Message = "chia.exe was not found" $ErrorRecord = [System.Management.Automation.ErrorRecord]::new( [System.IO.FileNotFoundException]::new($Message,"$ENV:LOCALAPPDATA\chia-blockchain\app-*\resources\app.asar.unpacked\daemon\chia.exe"), 'ChiaPathInvalid', [System.Management.Automation.ErrorCategory]::ObjectNotFound, "$ENV:LOCALAPPDATA\chia-blockchain\app-*\resources\app.asar.unpacked\daemon\chia.exe" ) $PSCmdlet.ThrowTerminatingError($ErrorRecord) $PSCmdlet.ThrowTerminatingError("Invalid Log Path Directory: $LogDirectoryPath") } } function Start-GUIDebugRun{ [CmdletBinding()] param( $ChiaRun, $ChiaQueue, $ChiaJob ) try{ $PlottingParameters = $ChiaRun.PlottingParameters $KSize = $PlottingParameters.KSize $Buffer = $PlottingParameters.RAM $Threads = $PlottingParameters.Threads $DisableBitfield = $PlottingParameters.DisableBitField $ExcludeFinalDirectory = $PlottingParameters.ExcludeFinalDirectory $TempDirectoryPath = $PlottingParameters.TempVolume.DirectoryPath $FinalDirectoryPath = $PlottingParameters.FinalVolume.DirectoryPath $LogDirectoryPath = $PlottingParameters.LogDirectory $SecondTempDirecoryPath = $PlottingParameters.TempVolume.DirectoryPath $E = if ($DisableBitfield){"-e"} $X = if ($ExcludeFinalDirectory){"-x"} if (Test-Path $LogDirectoryPath){ $LogPath = Join-Path $LogDirectoryPath ((Get-Date -Format yyyy_MM_dd_hh-mm-ss-tt_) + "plotlog" + ".log") } $ChiaProcess = start-process -filepath notepad.exe -PassThru -RedirectStandardOutput $LogPath $handle = $ChiaProcess.Handle $ChiaRun.ChiaProcess = $ChiaProcess $ChiaRun.ProcessId = $ChiaProcess.Id $DataHash.MainViewModel.AllRuns.Add($ChiaRun) $ChiaJob.RunsInProgress.Add($ChiaRun) $TempMasterVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $ChiaRun.PlottingParameters.TempVolume.UniqueId $TempMasterVolume.CurrentChiaRuns.Add($ChiaRun) $FinalMasterVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $ChiaRun.PlottingParameters.FinalVolume.UniqueId $FinalMasterVolume.PendingFinalRuns.Add($ChiaRun) $ChiaQueue.CurrentRun = $ChiaRun $DataHash.MainViewModel.CurrentRuns.Add($ChiaRun) while (-not$ChiaProcess.HasExited){ $ChiaQueue.CurrentTime = [DateTime]::Now $ChiaRun.CurrentTime = [DateTime]::Now $ChiaRun.Progress += 5 sleep (10 + $ChiaQueue.QueueNumber) } $TempMasterVolume.CurrentChiaRuns.Remove($ChiaRun) $FinalMasterVolume.PendingFinalRuns.Remove($ChiaRun) $ChiaJob.RunsInProgress.Remove($ChiaRun) $ChiaJob.CompletedRunCount++ $ChiaRun.ExitCode = $ChiaProcess.ExitCode if ($ChiaProcess.ExitTime -ne $null){ $ChiaRun.ExitTime = $ChiaProcess.ExitTime } $ChiaRun.ExitTime = $ChiaProcess.ExitTime if ($ChiaProcess.ExitCode -eq 0){ $ChiaRun.Status = "Completed" $ChiaJob.CompletedPlotCount++ $ChiaQueue.CompletedPlotCount++ $DataHash.MainViewModel.CompletedRuns.Add($ChiaRun) Update-ChiaGUISummary -Success } else{ $ChiaRun.Status = "Failed" $ChiaJob.FailedPlotCount++ $ChiaQueue.FailedPlotCount++ $DataHash.MainViewModel.FailedRuns.Add($ChiaRun) } $DataHash.MainViewModel.CurrentRuns.Remove($ChiaRun) } catch{ if (-not$DataHash.MainViewModel.FailedRuns.Contains($ChiaRun)){ $DataHash.MainViewModel.FailedRuns.Add($ChiaRun) } if ($DataHash.MainViewModel.CurrentRuns.Contains($ChiaRun)){ $DataHash.MainViewModel.CurrentRuns.Remove($ChiaRun) } if ($ChiaJob.RunsInProgress.Contains($ChiaRun)){ $ChiaJob.RunsInProgress.Remove($ChiaRun) } if ($FinalMasterVolume){ if ($FinalMasterVolume.PendingFinalRuns.Contains($ChiaRun)){ $FinalMasterVolume.PendingFinalRuns.Remove($ChiaRun) } } $PSCmdlet.WriteError($_) } } function Stop-PSChiaPlotter{ [CmdletBinding()] param() $RunningQueues = $DataHash.MainViewModel.AllQueues | Where-Object Status -eq "Running" foreach ($Queue in $RunningQueues){ $queue.Status = "Paused" } $ALLChiaProcesses = $DataHash.MainViewModel.CurrentRuns foreach ($run in $ALLChiaProcesses){ try{ Stop-Process $run.ProcessID } catch{ $logParam = @{ LogType = "Error" Message = $_.Exception.Message LineNumber = $_.InvocationInfo.ScriptLineNumber DebugLogPath = $DataHash.LogPath } Write-PSChiaPlotterLog @logParam } } $RunningRunspaces = $DataHash.Runspaces foreach ($runspace in $RunningRunspaces){ try{ $runspace.Stop() } catch{ $logParam = @{ LogType = "Error" Message = $_.Exception.Message LineNumber = $_.InvocationInfo.ScriptLineNumber DebugLogPath = $DataHash.LogPath } Write-PSChiaPlotterLog @logParam } } } function Test-ChiaParameters { param( $NewJob ) $ChiaParameters = $NewJob.InitialChiaParameters if ([string]::IsNullOrEmpty($NewJob.JobName)){ return "Job Name cannot be null or empty" } if ($ChiaParameters.RAM -lt 1000){ return "RAM needs to be greater than 1000" } if ($ChiaParameters.Threads -le 0){ return "Threads needs to 1 or higher" } if ($ChiaParameters.Buckets -le 0){ return "Buckets cannot be less than 1" } if ($NewJob.TempVolumes.Count -lt 1){ return "No Temp drives have been added!" } foreach ($tempvol in $NewJob.TempVolumes){ if (-not[System.IO.Directory]::Exists($tempvol.DirectoryPath)){ return "Temp Directory `"$($tempvol.DirectoryPath)`" does not exists" } $ValidPath = $false foreach ($path in $tempvol.AccessPaths){ if ($tempvol.DirectoryPath.StartsWith($path)){ $ValidPath = $true } } #foreach if (-not$ValidPath){ return "Directory path '$($tempvol.DirectoryPath)' for Drive $($tempvol.DriveLetter) does not start with a valid access path, valid paths shown below.`n`n$($tempvol.AccessPaths | foreach {"$_`n"})" } } if ($NewJob.FinalVolumes.Count -lt 1){ return "No Final Drives have been added!" } foreach ($finalvol in $NewJob.FinalVolumes){ if (-not[System.IO.Directory]::Exists($finalvol.DirectoryPath)){ return "Final Directory `"$($finalvol.DirectoryPath)`" does not exists" } $ValidPath = $false foreach ($path in $finalvol.AccessPaths){ if ($finalvol.DirectoryPath.StartsWith($path)){ $ValidPath = $true } } #foreach if (-not$ValidPath){ return "Directory path '$($finalvol.DirectoryPath)' for Drive $($finalvol.DriveLetter) does not start with a valid access path, valid paths shown below.`n`n$($finalvol.AccessPaths | foreach {"$_`n"})" } } if (-not[System.IO.Directory]::Exists($ChiaParameters.LogDirectory)){ return "Log Directory does not exists" } if ($NewJob.DelayInMinutes -gt 35791){ return "Delay Time is greater than 35791 minutes, which is the max" } if ($NewJob.FirstDelay -gt 35791){ return "First delay time is greater than 35791 minutes, which is the max" } return $true } function Update-ChiaGUISummary{ [CmdletBinding()] param( [switch]$Success, [switch]$Failed ) if ($Success){ $OneDayAgo = (Get-Date).AddDays(-1) $PlotsIn24Hrs = ($DataHash.MainViewModel.CompletedRuns | where ExitTime -GT $OneDayAgo | Measure-Object).Count $DataHash.MainViewModel.PlotPlottedPerDay = $PlotsIn24Hrs $DataHash.MainViewModel.TBPlottedPerDay = [math]::Round(($PlotsIn24Hrs * 101.4) / 1000,2) $SortedRuns = $DataHash.MainViewModel.CompletedRuns | Sort-Object -Property Runtime $Fastest = $SortedRuns | Select-Object -First 1 $Slowest = $SortedRuns | Select-Object -Last 1 $Average = $SortedRuns.RunTime | Measure-Object -Property TotalSeconds -Average if ($Fastest){ $DataHash.MainViewModel.FastestRun = $Fastest.Runtime } if ($Slowest){ $DataHash.MainViewModel.SlowestRun = $Slowest.Runtime } if ($Average){ $AverageRun = New-TimeSpan -Seconds $Average.Average $DataHash.MainViewModel.AverageTime = $AverageRun } } } function Update-ChiaVolume { [CmdletBinding()] param() $Volumes = Get-ChiaVolume $CurrentVolumes = $Volumes | where UniqueId -in $DataHash.MainViewModel.AllVolumes.UniqueId foreach ($volume in $CurrentVolumes){ $matchedVolume = $DataHash.MainViewModel.AllVolumes | where UniqueId -eq $volume.UniqueId if ($matchedVolume){ $matchedVolume.FreeSpace = $volume.FreeSpace $matchedVolume = $null } } $newVolumes = $Volumes | where UniqueId -notin $DataHash.MainViewModel.AllVolumes.UniqueId foreach ($newvolume in $newVolumes){ $DataHash.MainViewModel.AllVolumes.Add($newvolume) } $removedVolumes = $DataHash.MainViewModel.AllVolumes | where UniqueId -NotIn $Volumes.UniqueId foreach ($removedvolume in $removedVolumes){ $DataHash.MainViewModel.AllVolumes.Remove($removedvolume) } } function Update-PSChiaPlotter { [CmdletBinding()] param() $UpdateScript = [powershell]::Create().AddScript{ Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName System.Windows.Forms Get-childItem -Path $DataHash.PrivateFunctions -File | ForEach-Object {Import-Module $_.FullName} Get-childItem -Path $DataHash.Classes -File | ForEach-Object {Import-Module $_.FullName} Import-Module -Name PSChiaPlotter $CurrentModule = Get-Module -Name PSChiaPlotter $NewestModule = Find-Module -Name PSChiaPLotter -Repository PSGallery if ($NewestModule.Version -gt $CurrentModule.Version){ $Response = Show-Messagebox -Text "New version found! Version - $($NewestModule.Version.ToString())`nWould you like to update now?" -Buttons YesNo if ($Response -eq [System.Windows.MessageBoxResult]::Yes){ try{ Update-Module -Name PSChiaPlotter -Force -ErrorAction Stop $message = "PSChiaPlotter module successfully updated from $($CurrentModule.Version.ToString()) to $($NewestModule.Version.ToString())" $message += "`nYou must restart the GUI before changes can take effect.`nOnly do this when your plots have finished!" Write-PSChiaPlotterLog -LogType INFO -Message $message Show-Messagebox -Text $message | Out-Null } catch{ Write-PSChiaPlotterLog -LogType ERROR -Message $_.Exception.Message -LineNumber $_.InvocationInfo.ScriptLineNumber Show-Messagebox -Text "Unable to update to the latest version. Check logs more info" | Out-Null } } } else{ Try{ Show-Messagebox -Text "Your PSChiaPlotter is up to date!" | Out-Null } catch{ Write-PSChiaPlotterLog -LogType ERROR -Message $_.Exception.Message -LineNumber $_.InvocationInfo.ScriptLineNumber } } } $UpdateScript.RunspacePool = $ScriptsHash.Runspacepool $UpdateScript.BeginInvoke() } function Write-PSChiaPlotterLog { [CmdletBinding()] param( [ValidateSet("INFO","WARNING","ERROR")] [string]$LogType, [string]$Message, [int]$LineNumber ) try{ $Date = Get-Date -Format "[yyyy-MM-dd.HH:mm:ss]" switch ($LogType){ "ERROR" { Write-Host "[$LogType]$Date-$LineNumber-$Message" break } "WARNING" { if ($DataHash.MainViewModel.LogLevel -eq "WARNING"){ Write-Host "[$LogType]-$Date-$Message" } break } "INFO" { if ($DataHash.MainViewModel.LogLevel -eq "INFO" -or $DataHash.MainViewModel.LogLevel -eq "WARNING"){ Write-Host "[$LogType]-$Date-$Message" } break } } } catch{ Write-Host $_.Exception.Message } } function Write-RGBText { [CmdletBinding()] param( [string]$Text, [Parameter(Position = 1)] [int]$fRed = 0, [int]$fGreen = 0, [int]$fBlue = 0, [int]$bRed = 0, [int]$bGreen = 0, [int]$bBlue = 0, # No newline after the text. [Parameter()] [switch] $NoNewLine, [switch]$UnderLine ) $escape = [char]27 + '[' $resetAttributes = "$($escape)0m" if ($UnderLine){ $UL = "$($escape)4m" } $foreground = "$($escape)38;2;$($fRed);$($fGreen);$($fBlue)m" $background = "$($escape)48;2;$($bRed);$($bGreen);$($bBlue)m" Write-Host ($foreground + $background + $UL + $Text + $resetAttributes) -NoNewline:$NoNewLine } Export-ModuleMember -function ConvertTo-FriendlyTimeSpan, Get-ChiaHarvesterActivity, Get-ChiaKPlotCombination, Get-ChiaMaxParallelCount, Get-ChiaPlotProgress, Get-ChiaPlottingStatistic, Get-ChiaProcessCounter, Get-ChiaRAMInfo, Show-ChiaPlottingStatistic, Show-PSChiaPlotter, Start-ChiaHarvesterWatcher, Start-ChiaParallelPlotting, Start-ChiaPlotting |