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 $plotProgressObject = [PSCustomObject]@{ Progress = 0 Phase = "Phase 1" ElaspedTime = $ElaspedTime EST_TimeRemaining = New-TimeSpan PlotId = $plotId Phase1Progess = 0 Phase2Progess = 0 Phase3Progess = 0 Phase4Progess = 0 CopyProgess = 0 } if ($line_count -ge $phase1_line_end){ $plotProgressObject.Phase1Progess = 100 $plotProgressObject.Progress += $phase1_weight } else{ $Phase1Progess = ($line_count / $phase1_line_end) $plotProgressObject.Phase1Progess = [math]::Round(($Phase1Progess * 100),2) $plotProgressObject.Progress += $phase1_weight * $Phase1Progess $plotProgressObject.Phase = "Phase 1" $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $plotProgressObject.Progress $plotProgressObject.EST_TimeRemaining = New-TimeSpan -Seconds ($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return $plotProgressObject } if ($line_count -ge $phase2_line_end){ $plotProgressObject.Phase2Progess = 100 $plotProgressObject.Progress += $phase2_weight } else{ $phase2Progress = ($line_count - $phase1_line_end) / ($phase2_line_end - $phase1_line_end) $plotProgressObject.Phase2Progess = [math]::Round(($phase2Progress * 100),2) $plotProgressObject.Progress += $phase2_weight * $phase2Progress $plotProgressObject.Phase = "Phase 2" $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $plotProgressObject.Progress $plotProgressObject.EST_TimeRemaining = New-TimeSpan -Seconds ($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return $plotProgressObject } if ($line_count -ge $phase3_line_end){ $plotProgressObject.Phase3Progess = 100 $plotProgressObject.Progress += $phase3_weight } else{ $phase3Progess = ($line_count - $phase2_line_end) / ($phase3_line_end - $phase2_line_end) $plotProgressObject.Phase3Progess = [math]::Round(($phase3Progess * 100),2) $plotProgressObject.Progress += $phase3_weight * $phase3Progess $plotProgressObject.Phase = "Phase 3" $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $plotProgressObject.Progress $plotProgressObject.EST_TimeRemaining = New-TimeSpan -Seconds ($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return $plotProgressObject } if ($line_count -ge $phase4_line_end){ $plotProgressObject.Phase4Progess = 100 $plotProgressObject.Progress += $phase4_weight } else{ $phase4Progess = ($line_count - $phase3_line_end) / ($phase4_line_end - $phase3_line_end) $plotProgressObject.Phase4Progess = [math]::Round(($phase4Progess * 100),2) $plotProgressObject.Progress += $phase4_weight * $phase4Progess $plotProgressObject.Phase = "Phase 4" $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $plotProgressObject.Progress $plotProgressObject.EST_TimeRemaining = New-TimeSpan -Seconds ($Est_TimeRemaining - $ElaspedTime.TotalSeconds) return $plotProgressObject } if ($line_count -lt $copyfile_line_end){ $Est_TimeRemaining = ($ElaspedTime.TotalSeconds * 100) / $plotProgressObject.Progress $plotProgressObject.EST_TimeRemaining = New-TimeSpan -Seconds ($Est_TimeRemaining - $ElaspedTime.TotalSeconds) $plotProgressObject.Phase = "Copying" return $plotProgressObject } $plotProgressObject.Progress += $copyphase_weight $plotProgressObject.CopyProgess = 100 $plotProgressObject.Phase = "Complete" $plotProgressObject.ElaspedTime = New-TimeSpan -Start $StartTime -End $LogItem.LastWriteTime $plotProgressObject.EST_TimeRemaining = 0 return $plotProgressObject } 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_TimeRemaining.TotalSeconds if ($progress.EST_TimeRemaining.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 Test-ChiaPlot { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)] [Alias("FilePath","FullName","Filter")] [ValidateNotNullOrEmpty()] [string[]]$Path, [ALias("n")] [int]$Challenges = 30 ) Begin{ $ChiaPath = (Get-Item -Path "$ENV:LOCALAPPDATA\chia-blockchain\app-*\resources\app.asar.unpacked\daemon").FullName if ($ENV:Path.Split(";") -notcontains $ChiaPath){ $ENV:Path += ";$ChiaPath" } $Proofs = "Proofs ([0-9]*) / ([0-9]*), ([0-9.]*)" $Testing = "Testing plot" $ErrorString = "ERROR" } Process{ foreach ($plotpath in $Path){ chia.exe plots check -n $Challenges -g $plotpath 2>&1 | Select-String -SimpleMatch "Proofs","Error","Testing" | foreach { switch -Regex ($_){ $Proofs { $PlotObject.ProofsFound = $Matches[1] -as [int] $PlotObject.Ratio = $Matches[3] -as [double] $PlotObject break } $Testing { $PlotPath = ($_ -split "Testing plot ")[1].split(' ')[0] $Leaf = Split-Path -Path $PlotPath -Leaf $KSize = $Leaf -split "plot-k" -split "-" | Select-Object -First 1 -Skip 1 $PlotId = ($Leaf -split "-" | select -Last 1).Split(".")[0] $PlotObject = [PSCustomObject]@{ PSTypeName = "PSChiaPlotter.PlotTest" Path = $PlotPath ProofsFound = 0 Challenges = $Challenges Ratio = 0.0 KSize = $KSize PlotId = $PlotId Errors = New-Object System.Collections.Generic.List[string] } break } $ErrorString { $PlotObject.Errors.Add($_.ToString()) break } } #switch } #foreach line } #foreach } #process } function Test-ChiaPlotParallel { [CmdletBinding()] param( [Parameter()] [Alias("FilePath","FullName")] [ValidateNotNullOrEmpty()] [string[]]$Path, [ValidateRange(1,256)] [int]$Threads = 1, [ALias("n")] [int]$Challenges = 30 ) Begin{ $ChiaPath = (Get-Item -Path "$ENV:LOCALAPPDATA\chia-blockchain\app-*\resources\app.asar.unpacked\daemon").FullName if ($ENV:Path.Split(";") -notcontains $ChiaPath){ $ENV:Path += ";$ChiaPath" } $SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() $RunspacePool = [runspacefactory]::CreateRunspacePool(1,$Threads,$SessionState,$Host) $RunspacePool.ApartmentState = "STA" $RunspacePool.ThreadOptions = "ReuseThread" $RunspacePool.Open() $Jobs = New-Object System.Collections.Generic.List[Object] $TotalPlots = $Path.Count } #Begin Process{ foreach ($plotpath in $Path){ Write-Information "Adding runspace for $plot" $Parameters = @{ PlotPath = $plotpath Challenges = $Challenges ChiaPath = $ChiaPath } $CheckPlotScript = [powershell]::Create().AddScript({ Param ( $PlotPath, $Challenges, $ChiaPath ) $Leaf = Split-Path -Path $PlotPath -Leaf $KSize = $Leaf -split "plot-k" -split "-" | Select-Object -First 1 -Skip 1 $PlotId = ($Leaf -split "-" | select -Last 1).Split(".")[0] $PlotObject = [PSCustomObject]@{ PSTypeName = "PSChiaPlotter.PlotTest" Path = $PlotPath PlotsFound = 0 ProofsFound = 0 Challenges = $Challenges Ratio = 0.0 PlotSize = 0.0 LoadTime = 0.0 KSize = $KSize PlotId = $PlotId Errors = New-Object System.Collections.Generic.List[string] } $Proofs = "Proofs ([0-9]*) / ([0-9]*), ([0-9.]*)" $ValidPlot = "Found 1 valid plots" $InvalidPlot = "1 invalid plots found" $LoadedPlot = "Loaded a total of ([0-9]) plots of size ([0-9.]*) TiB, in ([0-9.]*) seconds" $ErrorString = "ERROR" if (Test-Path -Path $PlotPath -PathType Leaf){ $Results = chia.exe plots check -n $Challenges -g $PlotPath 2>&1 | Select-String -SimpleMatch "Proofs","Error","Found","Loaded" foreach ($line in $Results){ switch -Regex ($line){ $Proofs { $PlotObject.ProofsFound = $Matches[1] -as [int] $PlotObject.Ratio = $Matches[3] -as [double] break; } $ValidPlot { $PlotObject.Valid = $true } $InvalidPlot { $PlotObject.Valid = $false } $LoadedPlot { $PlotObject.PlotsFound = $Matches[1] -as [int] $PlotObject.PlotSize = $Matches[2] -as [double] $PlotObject.LoadTime = $Matches[3] -as [double] } $ErrorString { $PlotObject.Errors.Add($line.ToString()) } } } $PlotObject } }).AddParameters($Parameters) $CheckPlotScript.RunspacePool = $RunspacePool $Handle = $CheckPlotScript.BeginInvoke() $temp = [PSCustomObject]@{ PowerShell = $CheckPlotScript Handle = $Handle } [void]$jobs.Add($temp) } #foreach while ($jobs.handle.IsCompleted -contains $false){ Write-Information "Returning objects and closing runspaces" $RemoveJobs = New-Object System.Collections.Generic.List[Object] $jobs | where {$_.handle.IsCompleted -eq $true} | foreach { $_.powershell.EndInvoke($_.handle) $_.PowerShell.Dispose() [void]$RemoveJobs.Add($_) } if ($RemoveJobs){ foreach ($job in $RemoveJobs){ [void]$Jobs.Remove($job) } } $PercentComplete = [math]::Round(($TotalPlots - ($jobs | Measure-Object).Count) / $TotalPlots * 100,2) $ProgessParameters = @{ Activity = "Running chia check on $TotalPlots plots" Status = "$($TotalPlots - ($jobs | Measure-Object).Count) / $TotalPlots Completed | $PercentComplete%" PercentComplete = $PercentComplete } Write-Progress @ProgessParameters } #while } #process end{ Write-Information "Closing Runspace Pool" $RunspacePool.close() $RunspacePool.Dispose() } #end } function Get-BestChiaFinalDrive { [CmdletBinding()] param( $ChiaVolumes, $ChiaJob, $ChiaQueue ) $finalplotsize = $ChiaQueue.PlottingParameters.KSize.FinalSize $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, $ChiaQueue ) $requiredTempSize = $ChiaQueue.PlottingParameters.KSize.TempSize $finalplotsize = $ChiaQueue.PlottingParameters.KSize.FinalSize $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){ 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 -ErrorObject $_ } } #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 -ErrorObject $_ } } } 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 -Recurse | 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{ Write-PSChiaPlotterLog -LogType "ERROR" -ErrorObject $_ 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.Queues[$queue].IsBlocked = $false $Job.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 + 5) } } } catch{ Write-PSChiaPlotterLog -LogType "ERROR" -ErrorObject $_ 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 -Recurse | 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 while ($Queue.IsBlocked -or $Queue.Pause){ if ($Queue.Quit){ break } if (($Job.CompletedRunCount + $Job.RunsInProgress.Count) -ge $Job.TotalPlotCount){ break } if ($Queue.Pause){ $Queue.Status = "Paused" } else{ $Queue.Status = "Waiting" } Start-Sleep -Seconds 10 } $Job.QueueLooping = $true; $Queue.IsBlocked = $false if ($Job.BasicPlotting){ $TempVolume = [PSChiaPlotter.ChiaVolume]::new($Queue.PlottingParameters.BasicTempDirectory) $FinalVolume = [PSChiaPlotter.ChiaVolume]::new($Queue.PlottingParameters.BasicFinalDirectory) if ($Queue.PlottingParameters.EnableBasicSecondTempDirectory){ $SecondTempVolume = [PSChiaPlotter.ChiaVolume]::new($Queue.PlottingParameters.BasicSecondTempDirectory) } $PhaseOneIsOpen = Test-PhaseOneIsOpen -ChiaJob $Job while ($PhaseOneIsOpen -eq $false){ $Queue.Status = "Waiting - Phase 1 Limit" if (($Job.CompletedRunCount + $Job.RunsInProgress.Count) -ge $Job.TotalPlotCount){ break } if ($Queue.Quit){ break } Start-Sleep -Seconds 15 $PhaseOneIsOpen = Test-PhaseOneIsOpen -ChiaJob $Job } } else{ #grab a volume that has enough space Do { Try{ if ($Queue.Quit){ break } if (($Job.CompletedRunCount + $Job.RunsInProgress.Count) -ge $Job.TotalPlotCount){ break } Start-Sleep -Seconds 6 $PhaseOneIsOpen = Test-PhaseOneIsOpen -ChiaJob $Job if (-not$PhaseOneIsOpen){ $Queue.Status = "Waiting - Phase 1 Limit" Start-Sleep -Seconds 17 #do not need to check the drives if phase 1 is not open for another plot continue } $TempVolume = Get-BestChiaTempDrive -ChiaVolumes $Job.TempVolumes -ChiaJob $Job -ChiaQueue $Queue $FinalVolume = Get-BestChiaFinalDrive $Job.FinalVolumes -ChiaJob $Job -ChiaQueue $Queue if ($TempVolume -eq $Null){ $Queue.Status = "Waiting on Temp Space" Start-Sleep -Seconds 60 } if ($FinalVolume -eq $Null){ $Queue.Status = "Waiting on Final Dir Space" Start-Sleep -Seconds 60 } } catch{ $Queue.Status = "Failed To Grab Volume Info" Write-Error -LogType "Error" -ErrorObject $_ Start-Sleep -Seconds 30 } } while ($TempVolume -eq $null -or $FinalVolume -eq $null -or $PhaseOneIsOpen -eq $false) } #else $Job.QueueLooping = $false $BlockedQueue = $Job.Queues | where {$_.IsBlocked -and !$_.Pause} | sort QueueNumber | select -First 1 if ($BlockedQueue -ne $Null){ $BlockedQueue.IsBlocked = $false $BlockedQueue = $null } if (($Job.CompletedRunCount + $Job.RunsInProgress.Count) -ge $Job.TotalPlotCount){ break } if ($Queue.Quit){ break } $Queue.Status = "Running" $plottingParameters = [PSChiaPlotter.ChiaParameters]::New($Queue.PlottingParameters) $plottingParameters.TempVolume = $TempVolume $plottingParameters.FinalVolume = $FinalVolume if ($Job.BasicPlotting){ $plottingParameters.SecondTempVolume = $SecondTempVolume } $newRun = [PSChiaPlotter.ChiaRun]::new($Queue,$runNumber,$plottingParameters) if ($DataHash.Debug){ Start-GUIDebugRun -ChiaRun $newRun -ChiaQueue $Queue -ChiaJob $Job } else{ Start-GUIChiaPlotting -ChiaRun $newRun -ChiaQueue $Queue -ChiaJob $Job } $QueuesBlocked = ($Job.Queues | where {$_.IsBlocked -and !$_.pause} | Measure-Object).Count if ($QueuesBlocked -eq 0 -and $Job.QueueLooping -eq $false -and !$Queue.Pause){ $Queue.IsBlocked = $false } else{ $Queue.IsBlocked = $true } #sleep to give some time for updating sleep 2 } #for $Queue.IsBlocked = $false $Queue.Status = "Finished" } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ 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 } $Queue.Status = "Failed" } }.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 -Recurse | 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 #DataGrid $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") #Buttons $UIHash.NewJob_Button = $MainWindow.FindName("AddJob_Button") $UIHash.QuitJob_Button = $MainWindow.FindName("QuitJob_Button") $UIHash.PauseAllQueues_Button = $MainWindow.FindName("PauseAllQueues_Button") $UIHash.OpenLog_Button = $MainWindow.FindName("OpenLogButton") $UIHash.Refreshdrives_Button = $MainWindow.FindName("RefreshdrivesButton") $UIHash.CheckForUpdate_Button = $MainWindow.FindName("CheckForUpateButton") $UIHash.PauseQueue_Button = $MainWindow.FindName("PauseQueue_Button") $UIHash.QuitQueue_Button = $MainWindow.FindName("QuitQueue_Button") $DataHash.RefreshingDrives = $false $DataHash.MainViewModel = [PSChiaPlotter.MainViewModel]::new() $DataHash.MainViewModel.Version = (Get-Module -Name PSChiaPlotter).Version.ToString() $DataHash.MainViewModel.LogPath = $DataHash.LogPath $DataHash.MainViewModel.LogLevel = "Error" $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{ Invoke-NewJobButtonClick } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $UIHash.QuitJob_Button.Add_Click({ try{ Invoke-QuitJobButtonClick } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $UIHash.QuitQueue_Button.Add_Click({ try{ Invoke-QuitQueueButtonClick } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $UIHash.PauseQueue_Button.Add_Click({ try{ Invoke-PauseQueueButtonClick } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $UIHash.PauseAllQueues_Button.Add_Click({ try{ Invoke-PauseAllQueuesButtonClick } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $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 -ErrorObject $_ 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 -ErrorObject $_ Show-Messagebox "Unable to open log file, check the path '$($DataHash.MainViewModel.LogPath)'" | Out-Null } }) #Datagrid Selection Change $UIHash.Queues_DataGrid.Add_SelectionChanged({ try{ Invoke-QueueSelectionChange } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $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( $ChiaRun, $ChiaQueue, $ChiaJob ) try{ #not really needed, but just wanted to make each parameter its own variable $PlottingParameters = $ChiaRun.PlottingParameters $KSize = $PlottingParameters.KSize.KSizeValue $Buffer = $PlottingParameters.RAM $Threads = $PlottingParameters.Threads $DisableBitfield = $PlottingParameters.DisableBitField $ExcludeFinalDirectory = $PlottingParameters.ExcludeFinalDirectory $TempDirectoryPath = $PlottingParameters.TempVolume.DirectoryPath $FinalDirectoryPath = $PlottingParameters.FinalVolume.DirectoryPath $LogDirectoryPath = $PlottingParameters.LogDirectory $SecondTempDirectoryPath = $PlottingParameters.SecondTempVolume.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 ($ChiaJob.BasicPlotting){ if ($ChiaRun.PlottingParameters.EnableBasicSecondTempDirectory){ if (-not[string]::IsNullOrWhiteSpace($SecondTempDirectoryPath)){ $SecondTempDirectoryPath = $SecondTempDirectoryPath.TrimEnd('\') $ChiaArguments += " -2 `"$SecondTempDirectoryPath`"" } } } else{ if (-not[string]::IsNullOrWhiteSpace($SecondTempDirectoryPath)){ $SecondTempDirectoryPath = $SecondTempDirectoryPath.TrimEnd('\') $ChiaArguments += " -2 `"$SecondTempDirectoryPath`"" } } if ($KSize -eq 25){ $ChiaArguments += " --override-k" } 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) if (-not$ChiaJob.BasicPlotting){ $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 $ChiaRun.PlotId = $plotid $ChiaQueue.CurrentTime = [DateTime]::Now $ChiaRun.CurrentTime = [DateTime]::Now $ChiaRun.Phase = $progress.Phase if ($progress.EST_TimeRemaining.TotalSeconds -le 0){ $ChiaRun.EstTimeRemaining = New-TimeSpan -Seconds 0 } else{ $ChiaRun.EstTimeRemaining = $progress.EST_TimeRemaining } switch ($progress.Phase) { "Phase 1" {$ChiaRun.CurrentPhaseProgress = $progress.Phase1Progess} "Phase 2" {$ChiaRun.CurrentPhaseProgress = $progress.Phase2Progess} "Phase 3" {$ChiaRun.CurrentPhaseProgress = $progress.Phase3Progess} "Phase 4" {$ChiaRun.CurrentPhaseProgress = $progress.Phase4Progess} "Copying" {$ChiaRun.CurrentPhaseProgress = $progress.CopyProgess} } $ChiaRun.TempSize = Get-ChiaTempSize -DirectoryPath $TempDirectoryPath -PlotId $plotid Start-Sleep (5 + $ChiaQueue.QueueNumber) } catch{ Start-Sleep 30 } } #while $ChiaJob.RunsInProgress.Remove($ChiaRun) $ChiaJob.CompletedRunCount++ if (-not$ChiaJob.BasicPlotting){ $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) sleep -Seconds 1 Get-ChildItem -Path $TempDirectoryPath -Filter "*$plotid*.tmp" | foreach { try{ Remove-Item -Path $_.FullName -Force -ErrorAction Stop } catch{ Show-Messagebox -Text $_.Exception.Message | Out-Null } } if (-not[string]::IsNullOrWhiteSpace($SecondTempDirectoryPath)){ Get-ChildItem -Path $SecondTempDirectoryPath -Filter "*$plotid*.tmp" | foreach { try{ Remove-Item -Path $_.FullName -Force -ErrorAction Stop } catch{ Show-Messagebox -Text $_.Exception.Message | Out-Null } } #foreach file } } else{ $ChiaRun.Status = "Completed" $ChiaJob.CompletedPlotCount++ $ChiaQueue.CompletedPlotCount++ $DataHash.MainViewModel.CompletedRuns.Add($ChiaRun) $ChiaRun.CheckPlotPowershellCommand = "&'$ChiaPath' plots check -g $plotid" Update-ChiaGUISummary -Success } $ChiaQueue.CurrentRun = $null $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 (-not$ChiaJob.BasicPlotting){ 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") } } catch{ Write-PSChiaPlotterLog -LogType ERROR -ErrorObject $_ $PSCmdlet.WriteError($_) } } function Start-GUIDebugRun{ [CmdletBinding()] param( $ChiaRun, $ChiaQueue, $ChiaJob ) try{ $PlottingParameters = $ChiaRun.PlottingParameters $KSize = $PlottingParameters.KSize.KSizeValue $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.SecondTempVolume.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{ Write-PSChiaPlotterLog -LogType ERROR -ErrorObject $_ } } $RunningRunspaces = $DataHash.Runspaces foreach ($runspace in $RunningRunspaces){ try{ $runspace.Stop() } catch{ Write-PSChiaPlotterLog -LogType ERROR -ErrorObject $_ } } } 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 100){ return "RAM needs to be greater than 100" } 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.EnablePhaseOneLimitor){ if ($NewJob.PhaseOneLimit -lt 1){ return "Phase one limit cannot be less than 1... that doesn't make sense." } } if ($NewJob.BasicPlotting){ if (-not[System.IO.Directory]::Exists($ChiaParameters.BasicTempDirectory)){ return "Temp Directory `"$($ChiaParameters.BasicTempDirectory)`" does not exists" } if (-not[System.IO.Directory]::Exists($ChiaParameters.BasicFinalDirectory)){ return "Final Directory `"$($ChiaParameters.BasicFinalDirectory)`" does not exists" } if ($ChiaParameters.EnableBasicSecondTempDirectory){ if (-not[System.IO.Directory]::Exists($ChiaParameters.BasicSecondTempDirectory)){ return "2nd Temp Directory `"$($ChiaParameters.BasicSecondTempDirectory)`" does not exists" } } } else{ 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"})" } } } #else 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 Test-PhaseOneIsOpen { [CmdletBinding()] param( $ChiaJob ) try{ if ($ChiaJob.EnablePhaseOneLimitor){ $phaseOneCount = ($ChiaJob.RunsInProgress | where Phase -eq "Phase 1" | Measure-Object).Count if ($phaseOneCount -ge $ChiaJob.PhaseOneLimit){ return $false } else{ return $true } } else{ return $true } } catch{ Write-PSChiaPlotterLog -LogLevel "Warning" -Message "Unable to check if phase one is open!" } } function Update-ChiaGUISummary{ [CmdletBinding()] param( [switch]$Success, [switch]$Failed ) if ($Success){ $OneDayAgo = (Get-Date).AddDays(-1) $PlotsIn24Hrs = $DataHash.MainViewModel.CompletedRuns | where ExitTime -GT $OneDayAgo $DataHash.MainViewModel.PlotPlottedPerDay = ($PlotsIn24Hrs | Measure-Object).Count $totalTBPlotted = 0 foreach ($plot in $PlotsIn24Hrs){ $totalTBPlotted += ($plot.PlottingParameters.KSize.FinalSize / 1gb) } $DataHash.MainViewModel.TBPlottedPerDay = [math]::Round($totalTBPlotted / 1000,4) $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 -ErrorObject $_ 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 -ErrorObject $_ } } } $UpdateScript.RunspacePool = $ScriptsHash.Runspacepool $UpdateScript.BeginInvoke() } function Write-PSChiaPlotterLog { [CmdletBinding()] param( [ValidateSet("INFO","WARNING","ERROR")] [string]$LogType, [string]$Message, [int]$LineNumber, [string]$Line, $ErrorObject ) try{ $Date = Get-Date -Format "[yyyy-MM-dd.HH:mm:ss]" switch ($LogType){ "ERROR" { $Message = $ErrorObject.Exception.Message $LineNumber = $ErrorObject.InvocationInfo.ScriptLineNumber $Line = $ErrorObject.InvocationInfo.Line Write-Host "[$LogType]$Date-$LineNumber-$Message`n $Line" 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 } function Invoke-LoadJobButtonClick { [CmdletBinding()] param() try{ $JobFilePath = $SavedJobs_ComboBox.SelectedValue if (($JobFilePath -ne $null) -and (Test-Path $JobFilePath)){ #Have to transfer the properties over since the imported job is Desesersilzed Object $ImportedJob = Import-Clixml -Path $JobFilePath Write-PSChiaPlotterLog -LogType "INFO" -Message "Imported Job" $newSavedJob = [PSChiaPlotter.ChiaJob]::new() $newSavedJob.JobNumber = $jobNumber $newSavedJob.Status = "Waiting" $NewSavedJobViewModel = [PSChiaPlotter.NewJobViewModel]::new($newSavedJob) $SkipJobProperties = @("InitialChiaParameters","Queues","RunsInProgress","TempVolumes","FinalVolumes") $JobProperties = ($ImportedJob.NewChiaJob | Get-Member -MemberType Property).Name $JobProperties = $JobProperties | where {$_ -notin $SkipJobProperties} foreach ($property in $JobProperties){ try{ $NewSavedJobViewModel.NewChiaJob.$property = $ImportedJob.NewChiaJob.$property } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } } $SkipParameterProperties = @("SecondTempVolume","KSize") $ParameterProperties = ($ImportedJob.NewChiaJob.InitialChiaParameters | Get-Member -MemberType Property).Name $ParameterProperties = $ParameterProperties | where {$_ -notin $SkipParameterProperties} foreach ($property in $ParameterProperties){ try{ $NewSavedJobViewModel.NewChiaJob.InitialChiaParameters.$property = $ImportedJob.NewChiaJob.InitialChiaParameters.$property } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } } $NewSavedJobViewModel.NewChiaJob.JobNumber = $jobNumber $NewSavedJobViewModel.NewChiaJob.Status = "Waiting" $NewSavedJobViewModel.NewChiaJob.StartTime = Get-Date Get-ChiaVolume | foreach { $NewSavedJobViewModel.FinalAvailableVolumes.Add($_) } $NewSavedJobViewModel.FinalAvailableVolumes | foreach { $NewSavedJobViewModel.SecondTempVolumes.Add([PSChiaPlotter.ChiaVolume]::new($_)) $NewSavedJobViewModel.TempAvailableVolumes.Add([PSChiaPlotter.ChiaVolume]::new($_)) } #need to update directory paths for each volume... $NewSavedJobViewModel.TempAvailableVolumes | foreach { $FoundVolume = $ImportedJob.TempAvailableVolumes | where UniqueId -eq $_.UniqueId if ($FoundVolume -ne $Null){ $_.DirectoryPath = $FoundVolume.DirectoryPath } else{ $FoundVolume = $ImportedJob.NewChiaJob.TempVolumes | where UniqueId -eq $_.UniqueId if ($FoundVolume -ne $Null){ $_.DirectoryPath = $FoundVolume.DirectoryPath } } $FoundVolume = $Null } $NewSavedJobViewModel.FinalAvailableVolumes | foreach { $FoundVolume = $ImportedJob.FinalAvailableVolumes | where UniqueId -eq $_.UniqueId if ($FoundVolume -ne $Null){ $_.DirectoryPath = $FoundVolume.DirectoryPath } else{ $FoundVolume = $ImportedJob.NewChiaJob.FinalVolumes | where UniqueId -eq $_.UniqueId if ($FoundVolume -ne $Null){ $_.DirectoryPath = $FoundVolume.DirectoryPath } } $FoundVolume = $Null } $NewSavedJobViewModel.SecondTempVolumes | foreach { $FoundVolume = $ImportedJob.SecondTempVolumes | where UniqueId -eq $_.UniqueId if ($FoundVolume -ne $Null){ $_.DirectoryPath = $FoundVolume.DirectoryPath } $FoundVolume = $Null } $NewSavedJobViewModel.AvailableKSizes = $NewJobViewModel.AvailableKSizes $SecondTempVolume = $NewSavedJobViewModel.SecondTempVolumes | where UniqueId -eq $ImportedJob.NewChiaJob.InitialChiaParameters.SecondTempVolume.UniqueId $NewSavedJobViewModel.NewChiaJob.InitialChiaParameters.SecondTempVolume = $SecondTempVolume foreach ($Volume in $ImportedJob.NewChiaJob.TempVolumes){ $FoundTempVolume = $NewSavedJobViewModel.TempAvailableVolumes | where UniqueId -eq $Volume.UniqueId if ($FoundTempVolume){ $NewSavedJobViewModel.AddTempVolume($FoundTempVolume) } $FoundTempVolume = $null } $Volume = $null foreach ($Volume in $ImportedJob.NewChiaJob.FinalVolumes){ $FoundTempVolume = $NewSavedJobViewModel.FinalAvailableVolumes | where UniqueId -eq $Volume.UniqueId if ($FoundTempVolume){ $NewSavedJobViewModel.AddFinalVolume($FoundTempVolume) } $FoundTempVolume = $null } $DataHash.NewJobViewModel = $NewSavedJobViewModel $UIHash.NewJob_Window.DataContext = $NewSavedJobViewModel #Combobox wouldn't automatically update for some reason switch ($ImportedJob.NewChiaJob.InitialChiaParameters.KSize.KSizeValue){ 25 {$Index = 0;break} 32 {$Index = 1;break} 33 {$Index = 2;break} 34 {$Index = 3;break} 35 {$Index = 4;break} default {$Index = 1} } $KSize_ComboBox.SelectedIndex = $Index $DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.RAM = $ImportedJob.NewChiaJob.InitialChiaParameters.RAM if ($DataHash.NewJobViewModel.NewChiaJob.BasicPlotting){ $AdvancedBasic_Button.Content = "Switch To Advance" $AdvancedPlotting_TabControl.Visibility = [System.Windows.Visibility]::Collapsed $BasicPlotting_Grid.Visibility = [System.Windows.Visibility]::Visible } } } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ Show-MessageBox -Message "Unable to laod previous job :( Check logs for more info" } } function Invoke-NewJobButtonClick { [CmdletBinding()] param() try{ $SavedJobsPath = Join-Path -Path $ENV:LOCALAPPDATA -ChildPath 'PSChiaPlotter\SavedJobs' $SavedJobsList = New-Object -TypeName System.Collections.Generic.List[Object] if ([System.IO.Directory]::Exists($SavedJobsPath)){ Get-ChildItem -Path $SavedJobsPath | foreach { try{ $savedjob = [pscustomobject]@{ Name = $_.BaseName FullName = $_.FullName } $SavedJobsList.Add($savedjob) } catch{ Write-Error -LogType "Error" -ErrorObject $_ } } } $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" $newJob.Status = "Waiting" $NewJobViewModel = [PSChiaPlotter.NewJobViewModel]::new($newJob) $DataHash.NewJobViewModel = $NewJobViewModel $UIHash.NewJob_Window.DataContext = $NewJobViewModel #Combobox $KSize_ComboBox = $UIHash.NewJob_Window.FindName("KSize_ComboBox") $KSize_ComboBox.SelectedIndex = 1 $SavedJobs_ComboBox = $UIHash.NewJob_Window.FindName("SavedJobs_Combobox") $SavedJobs_ComboBox.ItemsSource = $SavedJobsList #Buttons $AdvancedBasic_Button = $UIHash.NewJob_Window.FindName("AdvancedBasic_Button") $LoadJob_Button = $UIHash.NewJob_Window.FindName("LoadJob_Button") $CreateJob_Button = $UIHash.NewJob_Window.FindName("CreateJob_Button") $CancelJobCreation_Button = $UIHash.NewJob_Window.FindName("CancelJobCreation_Button") $SaveJob_Button = $UIHash.NewJob_Window.FindName("SaveJob_Button") $ClearTempVolume_Button = $UIHash.NewJob_Window.FindName("RemoveTempVolumeButton") #TabControl $AdvancedPlotting_TabControl = $UIHash.NewJob_Window.FindName("AdvancedPlotting_TabControl") #Grid $BasicPlotting_Grid = $UIHash.NewJob_Window.FindName("BasicPlotting_Grid") #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.FinalAvailableVolumes.Add($_) } $NewJobViewModel.FinalAvailableVolumes | foreach { $NewJobViewModel.SecondTempVolumes.Add([PSChiaPlotter.ChiaVolume]::new($_)) $NewJobViewModel.TempAvailableVolumes.Add([PSChiaPlotter.ChiaVolume]::new($_)) } $AdvancedBasic_Button.Add_Click({ try{ if ($AdvancedBasic_Button.Content -eq "Switch To Basic"){ $AdvancedBasic_Button.Content = "Switch To Advance" $DataHash.NewJobViewModel.NewChiaJob.BasicPlotting = $true $AdvancedPlotting_TabControl.Visibility = [System.Windows.Visibility]::Collapsed $BasicPlotting_Grid.Visibility = [System.Windows.Visibility]::Visible } else{ $AdvancedBasic_Button.Content = "Switch To Basic" $DataHash.NewJobViewModel.NewChiaJob.BasicPlotting = $false $BasicPlotting_Grid.Visibility = [System.Windows.Visibility]::Collapsed $AdvancedPlotting_TabControl.Visibility = [System.Windows.Visibility]::Visible } } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $LoadJob_Button.Add_Click({ try{ Invoke-LoadJobButtonClick } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $KSize_ComboBox.Add_SelectionChanged({ try{ $KSizeTempSize = $DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.KSize.TempSize $KSizeFinalSize = $DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.KSize.FinalSize $DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.RAM = $DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.KSize.MinRAM foreach ($volume in $DataHash.NewJobViewModel.TempAvailableVolumes){ $max = [math]::Floor([decimal]($volume.size / $KSizeTempSize)) $volume.MaxConCurrentTempChiaRuns = $max } foreach ($volume in $DataHash.NewJobViewModel.NewChiaJob.TempVolumes){ $max = [math]::Floor([decimal]($volume.size / $KSizeTempSize)) $volume.MaxConCurrentTempChiaRuns = $max } foreach ($volume in $DataHash.NewJobViewModel.FinalAvailableVolumes){ $max = [math]::Floor([decimal]($volume.FreeSpace / $KSizeFinalSize)) $volume.PotentialFinalPlotsRemaining = $max } foreach ($volume in $DataHash.NewJobViewModel.NewChiaJob.FinalVolumes){ $max = [math]::Floor([decimal]($volume.FreeSpace / $KSizeFinalSize)) $volume.PotentialFinalPlotsRemaining = $max } foreach ($volume in $DataHash.NewJobViewModel.SecondTempVolumes){ $max = [math]::Floor([decimal]($volume.size / $KSizeTempSize)) $volume.MaxConCurrentTempChiaRuns = $max } } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $CreateJob_Button.add_Click({ try{ if ($DataHash.NewJobViewModel.NewChiaJob.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 ($DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.KSize.KSizeValue -eq 25){ $response = Show-Messagebox -Text "KSize 25 is only for testing and cannot farm XCH, continue?" -Button YesNo -Icon Warning if ($response -eq [System.Windows.MessageBoxResult]::No){ return } } $Results = Test-ChiaParameters $DataHash.NewJobViewModel.NewChiaJob if ($Results -ne $true){ Show-Messagebox -Text $Results -Title "Invalid Parameters" -Icon Warning return } $DataHash.MainViewModel.AllJobs.Add($DataHash.NewJobViewModel.NewChiaJob) $newJobRunSpace = New-ChiaJobRunspace -Job $DataHash.NewJobViewModel.NewChiaJob $newJobRunSpace.Runspacepool = $ScriptsHash.RunspacePool [void]$newJobRunSpace.BeginInvoke() $DataHash.Runspaces.Add($newJobRunSpace) $UIHash.NewJob_Window.Close() } catch{ Write-PSChiaPlotterLog -LogType "ERROR" -ErrorObject $_ Show-Messagebox -Text $_.Exception.Message -Title "Create New Job Error" -Icon Error } }) $CancelJobCreation_Button.Add_Click({ try{ $UIHash.NewJob_Window.Close() } catch{ Show-Messagebox -Text $_.Exception.Message -Title "Exit New Job Window Error" -Icon Error } }) $SaveJob_Button.Add_Click({ try{ $PSChiaPlotterFolderPath = Join-Path -Path $ENV:LOCALAPPDATA -ChildPath 'PSChiaPlotter\SavedJobs' if (-not[System.IO.Directory]::Exists($PSChiaPlotterFolderPath)){ New-Item -Path $PSChiaPlotterFolderPath -ItemType Directory | Out-Null } if ([string]::IsNullOrWhiteSpace($DataHash.NewJobViewModel.NewChiaJob.JobName)){ Show-Messagebox -Text "Please give your a job a name first" | Out-Null return } $SaveJobPath = Join-Path -Path $PSChiaPlotterFolderPath -ChildPath "$($DataHash.NewJobViewModel.NewChiaJob.JobName).xml" if (Test-Path $SaveJobPath){ $Response = Show-MessageBox -Text "Job $($DataHash.NewJobViewModel.NewChiaJob.JobName) already exists, would you like to overwrite it?" -Buttons YesNo if ($Response -eq [System.Windows.MessageBoxResult]::Yes){ $DataHash.NewJobViewModel | Export-Clixml -Path $SaveJobPath -Depth 10 -Force Show-MessageBox "$($DataHash.NewJobViewModel.NewChiaJob.JobName) job saved to $PSChiaPlotterFolderPath" return } } $DataHash.NewJobViewModel | Export-Clixml -Path $SaveJobPath -Depth 10 Show-MessageBox "$($DataHash.NewJobViewModel.NewChiaJob.JobName) job saved to $PSChiaPlotterFolderPath" } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } }) $ClearTempVolume_Button.Add_Click({ try{ $tempVolume = $DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.SecondTempVolume if ($tempVolume -ne $Null){ Write-PSChiaPlotterLog -LogType "INFO" -Message "Clearing Temp Volume Selection" $DataHash.NewJobViewModel.NewChiaJob.InitialChiaParameters.SecondTempVolume = $null } } catch{ Write-PSChiaPlotterLog -LogType ERROR -ErrorObject $_ } }) $UIHash.NewJob_Window.ShowDialog() } catch{ Write-PSChiaPlotterLog -LogType ERROR -ErrorObject $_ Show-Messagebox -Text $_.Exception.Message -Title "Create New Job Error" -Icon Error } } function Invoke-PauseAllQueuesButtonClick { [CmdletBinding()] param() try{ $Message = "Pausing Queues does not end the current running chia process." $message += " It will only prevent new processes from starting." $message += "`n`nAre you sure you want to pause all queues?" $Response = Show-MessageBox -Text $message -Buttons YesNo -Icon Information if ($Response -eq [System.Windows.MessageBoxResult]::Yes){ $AllCurrentQueues = $DataHash.MainViewModel.AllQueues | where {$_.Status -eq "Running" -or $_.Status -eq "Waiting"} foreach ($queue in $AllCurrentQueues){ $Queue.PauseResumeQueue() } } } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } } function Invoke-PauseQueueButtonClick{ [CmdletBinding()] param() try{ $SelectedQueue = $UIHash.Queues_DataGrid.SelectedItem $Pauseable = $SelectedQueue.Status -eq "Running" -or $SelectedQueue.Status -eq "Waiting" -or $SelectedQueue.Pause if ($SelectedQueue -ne $Null){ if ($Pauseable){ $SelectedQueue.PauseResumeQueue() $UIHash.PauseQueue_Button.Content = $SelectedQueue.ButtonContent } else{ Show-MessageBox -Text "Only Queues that are currenting running or waiting can be paused" -Icon Warnin } } else{ Show-MessageBox -Text "No Queue was selected!" -Icon Warning } } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ Show-MessageBox -Text "Unable To Pause Queue! Check logs for more info" -Icon Error } } function Invoke-QuitJobButtonClick{ [CmdletBinding()] param() try{ $SelectedJob = $Null $SelectedJob = $UIHash.Jobs_DataGrid.SelectedItem if ($SelectedJob){ if ($SelectedJob.Status -eq "Completed" -or $SelectedJob.Status -eq "Quitting"){ Show-MessageBox -Text "This Job is either completed or in the process of quitting" -Icon Information return } $Message = "Are you sure you want to quit job - $($SelectedJob.JobName)?" $Message += "`nAll running chia processes under this job will be cancelled!" $Response = Show-MessageBox -Text $Message -Buttons YesNo if ($Response -eq [System.Windows.MessageBoxResult]::Yes){ $runningQueues = $SelectedJob.Queues | where Status -ne "Finished" | where Status -ne "Failed" foreach ($queue in $runningQueues){ $queue.Quit = $true $queue.Status = "Quitting" } foreach ($run in $SelectedJob.RunsInProgress){ $run.ChiaProcess.Kill() } $SelectedJob.Status = "Completed" } } else{ Show-MessageBox -Text "No Job Selected!" -Icon Warning } } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } } function Invoke-QuitQueueButtonClick{ [CmdletBinding()] param() try{ $SelectedQueue = $UIHash.Queues_DataGrid.SelectedItem if ($SelectedQueue){ if ($SelectedQueue.Status -ne "Finished"){ $SelectedQueue.QuitQueue() $UIHash.QuitQueue_Button.IsEnabled = $false $UIHash.PauseQueue_Button.IsEnabled = $false } else{ Show-MessageBox -Text "Queue has already Finished!" -Icon Information } } else{ Show-MessageBox -Text "No Queue was selected!" -Icon Warning } } catch{ Show-MessageBox -Text "Unable To Quit Queue. Check logs for more info!" -Icon Error Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } } function Invoke-QueueSelectionChange{ [CmdletBinding()] param() try{ $SelectedQueue = $UIHash.Queues_DataGrid.SelectedItem if ($SelectedQueue){ $UIHash.PauseQueue_Button.Content = $SelectedQueue.ButtonContent $UIHash.PauseQueue_Button.IsEnabled = $SelectedQueue.ButtonEnabled $UIHash.QuitQueue_Button.IsEnabled = $SelectedQueue.QuitButtonEnabled } } catch{ Write-PSChiaPlotterLog -LogType "Error" -ErrorObject $_ } } 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, Test-ChiaPlot, Test-ChiaPlotParallel |