Repository: grayfallstown/Chia-Plot-Status Branch: main Commit: 7a513e54842c Files: 86 Total size: 423.8 KB Directory structure: gitextract_7ffcgm84/ ├── .github/ │ └── workflows/ │ └── dotnet.yml ├── .gitignore ├── ChiaPlotStatus.csproj ├── ChiaPlotStatus.sln ├── ChiaPlotStatusCli/ │ ├── CLI/ │ │ ├── CLI.cs │ │ └── CliOptions.cs │ └── ChiaPlotStatusCli.csproj ├── ChiaPlotStatusGUI/ │ ├── ChiaPlotStatusGUI.csproj │ ├── ChiaPlotStatusGUI.csproj.user │ ├── Directory.Build.props │ ├── GUI/ │ │ ├── App.axaml │ │ ├── App.axaml.cs │ │ ├── Assets/ │ │ │ └── en.yaml │ │ ├── Models/ │ │ │ ├── HighlightedText.cs │ │ │ ├── PlotCounts.cs │ │ │ └── Translation.cs │ │ ├── Program.cs │ │ ├── Utils/ │ │ │ └── Utils.cs │ │ ├── ViewLocator.cs │ │ ├── ViewModels/ │ │ │ ├── MainWindowViewModel.cs │ │ │ └── ViewModelBase.cs │ │ ├── Views/ │ │ │ ├── ChiaPlotterDialog.axaml │ │ │ ├── ChiaPlotterDialog.axaml.cs │ │ │ ├── DonationDialog.axaml │ │ │ ├── DonationDialog.axaml.cs │ │ │ ├── HarvestDialog.axaml │ │ │ ├── HarvestDialog.axaml.cs │ │ │ ├── MainWindow.axaml │ │ │ ├── MainWindow.axaml.cs │ │ │ ├── MarkOfDeathDialog.axaml │ │ │ ├── MarkOfDeathDialog.axaml.cs │ │ │ ├── NoteDialog.axaml │ │ │ ├── NoteDialog.axaml.cs │ │ │ ├── StatisticsDialog.axaml │ │ │ ├── StatisticsDialog.axaml.cs │ │ │ ├── UpdateDialog.axaml │ │ │ └── UpdateDialog.axaml.cs │ │ └── nuget.config │ └── chia-plot-status.desktop ├── ChiaPlotStatusLib/ │ ├── ChiaPlotStatusLib.csproj │ └── Logic/ │ ├── ChiaPlotStatus.cs │ ├── Models/ │ │ ├── CPPlotLog.cs │ │ ├── CPPlotLogReadable.cs │ │ ├── Columns.cs │ │ ├── Filter.cs │ │ ├── Health.cs │ │ ├── MarkOfDeath.cs │ │ ├── Note.cs │ │ ├── PlotLog.cs │ │ ├── PlotLogReadable.cs │ │ └── Settings.cs │ ├── Parser/ │ │ ├── CPPlotLogFileParser.cs │ │ ├── PlotLogFileParser.cs │ │ ├── PlotParserCache.cs │ │ └── TailLineEmitter.cs │ ├── Statistics/ │ │ ├── CPPlottingStatistics.cs │ │ ├── Harvest/ │ │ │ ├── Harvest.cs │ │ │ ├── HarvestParser.cs │ │ │ ├── HarvestSummary.cs │ │ │ └── HarvestSummeryReadable.cs │ │ ├── PlottingStatistics.cs │ │ ├── PlottingStatisticsDay.cs │ │ ├── PlottingStatisticsDayReadable.cs │ │ ├── PlottingStatisticsFull.cs │ │ ├── PlottingStatisticsFullReadable.cs │ │ ├── PlottingStatisticsHolder.cs │ │ ├── PlottingStatisticsID.cs │ │ └── PlottingStatisticsIdRelevanceWeights.cs │ └── Utils/ │ ├── Exporter.cs │ ├── Formatter.cs │ ├── SearchFilter.cs │ └── Sorter.cs ├── Directory.Build.props ├── InstallerConfig.ifp ├── LICENSE ├── Logo/ │ └── Icon - Readme.txt ├── NuGet.Config ├── Properties/ │ ├── PublishProfiles/ │ │ ├── FolderProfile.pubxml │ │ └── FolderProfile.pubxml.user │ ├── Resources.Designer.cs │ └── Resources.resx ├── README.md ├── grayfallstown.pfx ├── next-minor.ps1 ├── next-patch.ps1 └── release.ps1 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/dotnet.yml ================================================ name: .NET on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: strategy: matrix: platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 - name: Setup .NET uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.x #- name: Show Working Directory # does not work on windows as we are not in a powershell # run: ls -lah - name: Restore dependencies run: dotnet restore --configfile NuGet.Config ChiaPlotStatus.sln - name: Build run: dotnet build --no-restore --configuration .\ChiaPlotStatus.sln /p:Configuration=Release /p:Platform="Any CPU" - name: Test run: dotnet test --no-build --verbosity normal --configuration .\ChiaPlotStatus.sln /p:Configuration=Release /p:Platform="Any CPU" #- name: tree # run: tree # - uses: actions/upload-artifact@v2 # with: # name: binaries-${{ matrix.platform }} # path: bin/Release/net5.0 ================================================ FILE: .gitignore ================================================ .vs ChiaPlotStatus.csproj.user obj ChiaPlotStatusLib/obj ChiaPlotStatusLib/bin ChiaPlotStatusLib/.vs ChiaPlotStatusGUI/obj ChiaPlotStatusGUI/bin ChiaPlotStatusGUI/.vs ChiaPlotStatusCli/obj ChiaPlotStatusCli/bin ChiaPlotStatusCli/.vs release Notes.txt website Packaging Properties/PlotProgress.cs ================================================ FILE: ChiaPlotStatus.csproj ================================================ Library net5.0 win7-x64;ubuntu.16.10-x64;linux-x64 enable false grayfallstown.pfx false false grayfallstown none 0.11.11 5 Component True True Resources.resx ResXFileCodeGenerator Resources.Designer.cs ================================================ FILE: ChiaPlotStatus.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31129.286 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChiaPlotStatusCli", "ChiaPlotStatusCli\ChiaPlotStatusCli.csproj", "{677D6D3F-3693-4EA7-9FE0-AE64167BE82E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChiaPlotStatusGUI", "ChiaPlotStatusGUI\ChiaPlotStatusGUI.csproj", "{38F6EA6C-7E72-439B-B548-AFB5591DCEE3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChiaPlotStatusLib", "ChiaPlotStatusLib\ChiaPlotStatusLib.csproj", "{1CD172CB-184B-4C41-BB7A-4F68B61E80DE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {677D6D3F-3693-4EA7-9FE0-AE64167BE82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {677D6D3F-3693-4EA7-9FE0-AE64167BE82E}.Debug|Any CPU.Build.0 = Debug|Any CPU {677D6D3F-3693-4EA7-9FE0-AE64167BE82E}.Release|Any CPU.ActiveCfg = Release|Any CPU {677D6D3F-3693-4EA7-9FE0-AE64167BE82E}.Release|Any CPU.Build.0 = Release|Any CPU {38F6EA6C-7E72-439B-B548-AFB5591DCEE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38F6EA6C-7E72-439B-B548-AFB5591DCEE3}.Debug|Any CPU.Build.0 = Debug|Any CPU {38F6EA6C-7E72-439B-B548-AFB5591DCEE3}.Release|Any CPU.ActiveCfg = Release|Any CPU {38F6EA6C-7E72-439B-B548-AFB5591DCEE3}.Release|Any CPU.Build.0 = Release|Any CPU {1CD172CB-184B-4C41-BB7A-4F68B61E80DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1CD172CB-184B-4C41-BB7A-4F68B61E80DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {1CD172CB-184B-4C41-BB7A-4F68B61E80DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {1CD172CB-184B-4C41-BB7A-4F68B61E80DE}.Release|Any CPU.Build.0 = Release|Any CPU {91BD97DE-BA91-4BED-97C6-1AC8B306FFA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BD97DE-BA91-4BED-97C6-1AC8B306FFA1}.Debug|Any CPU.Build.0 = Debug|Any CPU {91BD97DE-BA91-4BED-97C6-1AC8B306FFA1}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BD97DE-BA91-4BED-97C6-1AC8B306FFA1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {75F20C1E-B71F-4C68-9945-E3C53E1C74D1} EndGlobalSection EndGlobal ================================================ FILE: ChiaPlotStatusCli/CLI/CLI.cs ================================================ using ChiaPlotStatus.Logic.Models; using ChiaPlotStatus.Logic.Utils; using CommandLine; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ChiaPlotStatus.CLI { public class ChiaPlotStatusCLI { public static void Main(string[] args) { Parser parser = new((parserSettings) => { parserSettings.CaseSensitive = false; parserSettings.AutoVersion = false; parserSettings.AutoHelp = true; parserSettings.HelpWriter = Console.Out; }); parser.ParseArguments(args) .WithParsed(options => { do { GenerateReport(options); Console.Out.WriteLine("File '" + options.File + "' written"); if (options.KeepUpdating) { int seconds = 10; Console.Out.WriteLine("Updating file in " + seconds + " seconds..."); Thread.Sleep(seconds * 1000); } } while (options.KeepUpdating); }); } public static void GenerateReport(CliOptions options) { ChiaPlotStatus PlotManager = SetupChiaPlotStatus(options); Filter filter = SetupFilter(options); string sortProperty = options.SortProperty; if (string.IsNullOrEmpty(sortProperty)) sortProperty = PlotManager.Settings.SortProperty; Console.Out.WriteLine("Sorting by " + sortProperty); List<(PlotLog, PlotLogReadable)> plotLogs = PlotManager.PollPlotLogs(options.SortProperty, options.SortAsc, options.Search, filter); ExportToFile(options, plotLogs); } private static ChiaPlotStatus SetupChiaPlotStatus(CliOptions options) { var configFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + Path.DirectorySeparatorChar; Settings Settings = new Settings(configFolder + "ChiaPlotStatu.config.json"); Settings.Load(); ChiaPlotStatus PlotManager = new(Settings); List folders = options.LogFolders.ToList(); if (folders.Count() == 0) { if (PlotManager.Settings.LogDirectories.Count == 0) PlotManager.AddDefaultLogFolders(); } else { // override LogFolders from settings file PlotManager.Settings.LogDirectories.Clear(); foreach (var folder in folders) PlotManager.AddLogFolder(folder); } return PlotManager; } private static Filter SetupFilter(CliOptions options) { Filter filter = new(); filter.HideHealthy = options.HideHealthy; filter.HideFinished = options.HideFinished; filter.HidePossiblyDead = options.HidePossiblyDead; filter.HideConfirmedDead = options.HideConfirmedDead; return filter; } private static void ExportToFile(CliOptions options, List<(PlotLog, PlotLogReadable)> plotLogs) { Exporter exporter = new Exporter(plotLogs); try { switch (options.Format.ToLower()) { case "json": exporter.ToJson(options.File, options.Raw); break; case "yaml": exporter.ToYaml(options.File, options.Raw); break; case "csv": exporter.ToCsv(options.File, options.Raw); break; default: throw new NotImplementedException("File format '" + options.Format + "' not supported"); } } catch (Exception e) { Console.Error.WriteLine("Could not write file '" + options.File + "': " + e.Message); Console.Error.WriteLine(e.StackTrace); System.Environment.Exit(1); } } } } ================================================ FILE: ChiaPlotStatusCli/CLI/CliOptions.cs ================================================ using CommandLine; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ChiaPlotStatus.CLI { public class CliOptions { [Option('o', "outfile", Required = true, HelpText = "The file to write to")] public string File { get; set; } [Option('f', "format", Required = true, HelpText = "The format to use while writing the file. Valid values are json, yaml and csv")] public string Format { get; set; } [Option('r', "raw", Required = false, HelpText = "Write raw, unformatted values instead of human readable ones.")] public bool Raw { get; set; } [Option('l', "log-folders", Required = false, Separator = ',', HelpText = "The folders where logs can be found, comma separated. Uses default folder when empty")] public IEnumerable LogFolders { get; set; } [Option('s', "sort-property", Required = false, HelpText = "The property you want the plotlogs sorted by")] public string SortProperty { get; set; } [Option('a', "sort-asc", Required = false, HelpText = "Sort ascending")] public bool SortAsc { get; set; } [Option("search", Required = false, HelpText = "Filter plotlogs by this search terms. You filter for your temp1 folder for example.")] public string Search { get; set; } [Option("hide-finished", Required = false, HelpText = "Hide finished plots")] public bool HideFinished { get; set; } [Option("hide-possibly-dead", Required = false, HelpText = "Hide possibly dead plots")] public bool HidePossiblyDead { get; set; } [Option("hide-confirmed-dead", Required = false, HelpText = "Hide confirmed dead plots")] public bool HideConfirmedDead { get; set; } [Option("hide-healthy", Required = false, HelpText = "Hide healthy plots")] public bool HideHealthy { get; set; } [Option("keep-updating", Required = false, HelpText = "Keep updating the file every 10 seconds")] public bool KeepUpdating { get; set; } } } ================================================ FILE: ChiaPlotStatusCli/ChiaPlotStatusCli.csproj ================================================ Exe net5.0 win7-x64;ubuntu.16.10-x64;linux-x64 ChiaPlotStatus.CLI.ChiaPlotStatusCLI Icon - Color changed.ico grayfallstown grayfallstown 0.11.11 ================================================ FILE: ChiaPlotStatusGUI/ChiaPlotStatusGUI.csproj ================================================ WinExe net5.0 win7-x64;ubuntu.16.10-x64;linux-x64 enable ChiaPlotStatus.Program ChiaPlotStatus ChiaPlotStatusGUI grayfallstown grayfallstown Icon - Color changed.ico 0.11.11 /usr/share/applications/chia-plot-status.desktop /usr/share/pixmaps/chia-plot-status.png %(Filename) %(Filename) %(Filename) HarvestDialog.axaml NoteDialog.axaml ChiaPlotterDialog.axaml StatisticsDialog.axaml %(Filename) %(Filename) ================================================ FILE: ChiaPlotStatusGUI/ChiaPlotStatusGUI.csproj.user ================================================  ================================================ FILE: ChiaPlotStatusGUI/Directory.Build.props ================================================  0.1.189-* all ================================================ FILE: ChiaPlotStatusGUI/GUI/App.axaml ================================================ ================================================ FILE: ChiaPlotStatusGUI/GUI/App.axaml.cs ================================================ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using ChiaPlotStatus.ViewModels; using ChiaPlotStatus.Views; namespace ChiaPlotStatus { public class App : Application { public override void Initialize() { AvaloniaXamlLoader.Load(this); } public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow { DataContext = new MainWindowViewModel(), }; } base.OnFrameworkInitializationCompleted(); } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Assets/en.yaml ================================================ Name: English Fields: Search: "Search" Light: "Light" Dark: "Dark" LogFolder: "Log Folders" RawExport: "Raw Export" HideHealthy: "Hide healthy" HideFinished: "Hide finished" HidePossiblyDead: "Hide possibly Dead" HideConfirmedDead: "Hide confirmed Dead" Buttons: Add: "Add" Remove: "Remove" Json: "Json" Yaml: "Yaml" CSV: "CSV" MarkAsDead: "Mark as dead" SelectAllConcerningCommand: "Select concerning" SelectAllPossiblyDeadCommand: "Selected possibly dead" MarkSelectionAsDead: "Mark selected logs as dead" UnmarkAsDead: "Unmark as dead" Abort: "Cancel" Copy: "Copy" Save: "Save" Columns: RuntimeMinutes: "Time Frame Minutes" PoolPuzzleHash: "Pool Puzzle / Address" Tmp1Drive: "Tmp1" Tmp2Drive: "Tmp2" DestDrive: "Destination" Errors: "Errors" PID: "PID" Progress: "Progress" TimeRemaining: "Remaining 🕐" RunTimeSeconds: "Runtime 🕐" ETA: "ETA" CurrentTable: "Cur. Table" CurrentBucket: "Cur. Bucket" CurrentPhase: "Cur. Phase" Phase1Cpu: "P1 CPU" Phase2Cpu: "P2 CPU" Phase3Cpu: "P3 CPU" Phase4Cpu: "P4 CPU" Phase1Seconds: "Phase 1 🕐" Phase2Seconds: "Phase 2 🕐" Phase3Seconds: "Phase 3 🕐" Phase4Seconds: "Phase 4 🕐" CopyTimeSeconds: "Copy 🕐" TotalSeconds: "Total 🕐" PlotSize: "K-Size" Threads: "Threads" Buffer: "Buffer" Buckets: "Buckets" StartDate: "Started" FinishDate: "Finished" PlotName: "Plot Name" LogFolder: "Log Folder" LogFile: "Log File" ApproximateWorkingSpace: "Workingspace" FinalFileSize: "Final Size" Health: "Health" LastLogLine: "Last Log Line" PlaceInLogFile: "Nr" Phase1AvgTimeNeed: "Phase 1 avg 🕐" Phase2AvgTimeNeed: "Phase 2 avg 🕐" Phase3AvgTimeNeed: "Phase 3 avg 🕐" Phase4AvgTimeNeed: "Phase 4 avg 🕐" CopyTimeAvgTimeNeed: "Copy avg 🕐" TotalAvgTimeNeed: "Total avg 🕐" Phase1Completed: "Sample Size" Note: "Note/Tags" TotalEligiblePlots: "Eligible Plots" FoundProofs: "Found Proofs" BestLookupTime: "Best Lookup Time" WorstLookupTime: "Worst Lookup Time" AvgLookupTime: "Avg Lookup Time" FilterRatio: "Filter Ratio" TotalPlots: "Total Plots" ChallengesPerMinute: "Challenges per Minute" AvgHeat: "Avg Heat" MaxHeat: "Max Heat" MinHeat: "Min Heat" AvgEligiblePlots: "Avg eligible Plots" Tooltips: RuntimeMinutes: "This statistic uses the last n minutes from the log for its calculations" PoolPuzzleHash: "The Pool Puzzle Hash if you plot with madmax and the pool address if you still use chiapos." JsonExport: "Export shown data to Json. Check if you want plain unformatted numbers." YamlExport: "Export shown data to Yaml. Check if you want plain unformatted numbers." CsvExport: "Export shown data to Csv. Check if you want plain unformatted numbers." RawExport: "Export shown data as plain unformatted numbers." Tmp1Drive: null Tmp2Drive: null DestDrive: "Directory where the plot file was/will be written to." Errors: > Errors that occured in the plotting process while reading from or writing data to disk. The chia plotter will retry in case of such an error after a short Seconds. One is not that bad. If you use external harddrives or network attached Storage this usally means the connection dropped. Reattach the device and chia will continue. If the error count keeps rising you need to troubleshoot. PID: > Process ID if the running plotter process. You can use this nr to find and kill the process if it misbehaves. Progress: > Shows the progression defined by the number of steps done vs the number of steps left to completion. The number of steps tells you nothing about how long the process will take to completion. TimeRemaining: > An estimate on how long it will take for the plot to complete. It is based on the plots progress and all the data extracted from your finished plots. It will try to use the data of those plots that match the configuration of that plot most closely to make a better prediction if you mix hard disks, ssd, internal and external drives and even network attached storage. RunTimeSeconds: > "How long the process is running already" ETA: > An estimate on when the plotting process completes formatted MM/dd HH:mm. It is based on the plots progress and all the data extracted from your finished plots. It will try to use the data of those plots that match the configuration of that plot most closely to make a better prediction if you mix hard disks, ssd, internal and external drives and even network attached storage. CurrentTable: > The table number currently worked on. On phase 1 and 3 it will start with table 1 and work its way up to 7. In Phase 2 it will start with table 7 and work its way back to table 1. An arrow shows in which way it currently progresses. CurrentBucket: > The Chia plotter will work through each table in the phases 1 and 3 and will process each bucket in each table. This shows which bucket in the current table is currently worked on. CurrentPhase: > Shows in which of the four phases the plotting process is. It does not go back to a previous phase. Phase1Cpu: > "CPU Usage during phase 1." Phase2Cpu: > "CPU Usage during phase 2." Phase3Cpu: > "CPU Usage during phase 3." Phase4Cpu: > "CPU Usage during phase 4." Phase1Seconds: > The time this plot process took to complete phase 1. This is the only phase with multithreading enabled and the only one you can optimize with the threads option. Phase2Seconds: > The time this plot process took to complete phase 2 Phase3Seconds: > The time this plot process took to complete phase 3 Phase4Seconds: > The time this plot process took to complete phase 4 CopyTimeSeconds: > Unofficial phase 5. The time this plot process took to copy the final file to the destination. It gets copied from temp folder 2 to destination unless temp folder 2 and the destination are identical. If you are using a remote destination you can optimize this process by using a local ssd or even just a hdd as destination and then moving the plot to its remote destination by other means. This way your plotting process is completed before the plot slowly gets moved to its final destination and the plotter can already begin the next plot. Google "windows task scheduler" or "linux chron jobs" to see how to set up automatic jobs that move the files for you. TotalSeconds: > The time this plot process took to fully complete PlotSize: > The k-size of the plot. Usally 32, which produces a 100GB plot file. Apparently there is very little reason to use a larger k-size for now. Threads: > The number of threads used to complete phase 1. As of now the other phases are not multithreaded and will only use one cpu core, tweaking this only affects phase 1 Buffer: > This buffer size the plotter uses. It is used to sort the data within the buckets in memory. It will resort to a different sort algorithm which is slower on most tables. Too small buffer sizes will slow down the plotter, too large ones will bring no additional benefit, but lower the number on plotting processes you can run in parallel with the ram installed in your system. Buckets: > The number of parts a table will be split into. Each bucket is sorted individually. Each bucket gets written in its own file. StartDate: > When the plotting process was started. FinishDate: > When the plotting process finished. PlotName: > Name of the final plot file. LogFolder: > The folder in which this log file in stored. If you have multiple log folder for multiple rigs, this tells you which rig it is from. LogFile: > The name of the logfile. ApproximateWorkingSpace: > An estimate on how much temporary space was used during the creation of the plot. FinalFileSize: > The size of the finished plot file. Varies little over the same k-size. Health: > Read catefully! Don't be rushed into making a decision here! This feature requires existing logs of finished plots from the same log directory to run correctly or it uses default warning thresholds that are pretty much diced at the moment! If your system does not update "last modfied at" on files (some deactivate it on SSDs) ignore the warnings with time in it. Chia Plot Status doesn't know any better. An Estimate on the processes health. In some cases it is not clear if the process is still running, this can happen especially in phase 4. "⚠ Temp Errors": Usually not so serious, but you need to take action. It means the last line in the logfile is a read or write error (look at Errors column). You might be out of disk space for temp1, temp2 or the destination, so free up some space. Another reason could be your external hard disk / network attached storage is detached or the connection is flaky. The plotter retries every 5 minutes, so it continues once you fixed the error. "⚠ Slow 20m/15m": Usually not serious, just have an eye on it. It means last update to the logfile was 20 minutes ago, but Chia Plot Status expected a new log line within 15 minutes. Process is likely still running. Maybe you gave your temp drives more IO than they can handle this time? If you only have finished plots that ran on SSDs and this one is on a HDD now, it is expected to be slower and throw that warning. Chia Plot Status will learn that this drive is slower once this plot is done. "⚠ Dead? 40m/10m": This is scary. It means last update to the logFile was way too long ago. Process might be still running! If your system is not really slow/overtaxed at the moment the process could be dead (check to be sure). Chia Plot Status considers it possibly dead until it logs something again. "✗ Dead (m)": You manually marked this process as dead. "✗ Dead": Ouch. The chia plotter produced a fatal error. There will be something like "Caught plotting error" in your logfile or it means the processes is not done and a new process appeared in the same logfile. If your logging is not messed up and logging multiple processes in parallel to the same file this means the process is dead. LastLogLine: > Last line in this plots log file PlaceInLogFile: > When using --num 20 to plot 20 plots in a row this shows you the current plot nr on the left and the size of the queue on the right Note: > Here you can add notes and tags to group plot logs and compare performance. TotalEligiblePlots: > Plots that could have provided proofs for the challanges (for a reward). 1/512 chance per plot, per challenge. Maybe they did actually contain a proof, maybe not. Even if, this does not mean you earned Chia as the best proof provided by anyone gets rewarded, not everyone who delivers a proof. AvgEligiblePlots: > Average number of plots that can provide proofs for the challanges (for a reward) given in the timeframe from begin to end of your logfiles. 1/512 chance per plot, therefor should be somewhere around 0.001953125 times the nr of total plots. If the number is higher, lucky you. If it is a lot lower, well something is wrong unless you do not have many plots, like less than 50 or so or if you do not have a lot of data. if you have just set the log level to INFO, this means practically nothing. Wait a few hours to see any relevant value. The less plots you have, the longer it takes for this value to become relevant. Do you have a thousand plots? This value could become meaningfull after one to three hours. Do you have less than 100 plots? Wait at least a day, maybe a week. FoundProofs: > Number of Proofs found. A found proof does not necessarily mean you won chia, as the best submitted proof is picked. BestLookupTime: > Lookup time increases when a plot does actually contain proofs. Best lookup time does therefor not tell you much unless it is high. WorstLookupTime: > Lookup time increases when a plot does actually contain proofs. You have 30 seconds to submit a proof once a challenge is started. Keep in mind that the proof needs to be transfered to a lot of nodes to be successfull. Because of this the worst lookup time should not be larger that 5 seconds. AvgLookupTime: > Lookup time increases when a plot does actually contain proofs. You have 30 seconds to submit a proof once a challenge is started. Keep in mind that the proof needs to be transfered to a lot of nodes to be successfull. Because of this the avg lookup time should not be larger that ~2 seconds. FilterRatio: > Number of elgible plots divided by the total nr of plots. Shout be somewhere around 1/512 (0.001953125). Higher is better, way lower means something is seriously wrong. TotalPlots: > Total nr of plots that harvester uses to harvest. ChallengesPerMinute: > Number of challenges this harvester handled per minute AvgHeat: > The nearer this gets to 5 (or even above 5) the worse your harvester works. NAS does not work well with chia. MaxHeat: > The nearer this gets to 5 (or even above 5) the worse your harvester works, but Avg Heat is more important. NAS does not work well with chia. MinHeat: > Should be as low as possible. The nearer this gets to 5 (or even above 5) the worse your harvester works, but Avg and Max Heat are more important. NAS does not work well with chia. ================================================ FILE: ChiaPlotStatusGUI/GUI/Models/HighlightedText.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ChiaPlotStatus.GUI.Models { public class HighlightedText { public string Text { get; set; } public bool Level0 { get; set; } public bool Level1 { get; set; } public bool Level2 { get; set; } public bool Level3 { get; set; } public bool Level4 { get; set; } public bool Level5 { get; set; } public HighlightedText(string Text, int level) { this.Text = Text; this.Level0 = level == 0; this.Level1 = level == 1; this.Level2 = level == 2; this.Level3 = level == 3; this.Level4 = level == 4; this.Level5 = level == 5; } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Models/PlotCounts.cs ================================================ using ChiaPlotStatus; using ChiaPlotStatus.Logic.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ChiaPlotStatusGUI.GUI.Models { public class PlotCounts { public int PlotsInPhase1 { get; set; } = 0; public int PlotsInPhase2 { get; set; } = 0; public int PlotsInPhase3 { get; set; } = 0; public int PlotsInPhase4 { get; set; } = 0; public int PlotsInPhase5 { get; set; } = 0; public int Finished { get; set; } = 0; public int Running { get; set; } = 0; public int Concerning { get; set; } = 0; public int Failed { get; set; } = 0; public PlotCounts() { } public PlotCounts(List<(PlotLog, PlotLogReadable)> plotLogs) { foreach (var tuple in plotLogs) { var plotLog = tuple.Item1; var done = tuple.Item1.CurrentPhase == 6; switch (plotLog.Health) { case Healthy: if (done) Finished++; else handlePhase(plotLog); break; case TempError: case Concerning c: Concerning++; handlePhase(plotLog); break; case PossiblyDead p: Concerning++; Failed++; handlePhase(plotLog); break; case ConfirmedDead c: Failed++; break; } } } private void handlePhase(PlotLog plotLog) { Running++; switch (plotLog.CurrentPhase) { case 1: PlotsInPhase1++; break; case 2: PlotsInPhase2++; break; case 3: PlotsInPhase3++; break; case 4: PlotsInPhase4++; break; case 5: PlotsInPhase5++; break; } } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Models/Translation.cs ================================================ using Avalonia; using Avalonia.Platform; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using YamlDotNet.Serialization; namespace ChiaPlotStatus.GUI.Models { public static class Translation { public static Dictionary LoadLanguages() { var deserializer = new DeserializerBuilder().Build(); Dictionary langs = new(); var assets = AvaloniaLocator.Current.GetService(); foreach (var lang2 in new string[] { "en" }) { System.IO.Stream filestream = assets.Open(new Uri("avares://ChiaPlotStatus/GUI/Assets/" + lang2 + ".yaml")); StreamReader reader = new StreamReader(filestream); string yaml = reader.ReadToEnd(); var lang = deserializer.Deserialize(yaml); langs.Add(lang.Name, lang); } return langs; } } public class Language { public string Name { get; set; } public Tooltips Tooltips { get; set; } public Columns Columns { get; set; } public Fields Fields { get; set; } public Buttons Buttons { get; set; } } public class Fields { public string Search { get; set; } = ""; public string Light { get; set; } = ""; public string Dark { get; set; } = ""; public string LogFolder { get; set; } = ""; public string RawExport { get; set; } = ""; public string HideHealthy { get; set; } = ""; public string HideFinished { get; set; } = ""; public string HidePossiblyDead { get; set; } = ""; public string HideConfirmedDead { get; set; } = ""; } public class Buttons { public string Add { get; set; } = ""; public string Remove { get; set; } = ""; public string Json { get; set; } = ""; public string Yaml { get; set; } = ""; public string CSV { get; set; } = ""; public string MarkAsDead { get; set; } = ""; public string SelectAllConcerningCommand { get; set; } = ""; public string SelectAllPossiblyDeadCommand { get; set; } = ""; public string MarkSelectionAsDead { get; set; } = ""; public string UnmarkAsDead { get; set; } = ""; public string Abort { get; set; } = ""; public string Copy { get; set; } = ""; public string Save { get; set; } = ""; } public class Tooltips { public string JsonExport { get; set; } = ""; public string YamlExport { get; set; } = ""; public string CsvExport { get; set; } = ""; public string RawExport { get; set; } = ""; public string Tmp1Drive { get; set; } = ""; public string Tmp2Drive { get; set; } = ""; public string DestDrive { get; set; } = ""; public string Errors { get; set; } = ""; public string PID { get; set; } = ""; public string Progress { get; set; } = ""; public string TimeRemaining { get; set; } = ""; public string RunTimeSeconds { get; set; } = ""; public string ETA { get; set; } = ""; public string CurrentTable { get; set; } = ""; public string CurrentBucket { get; set; } = ""; public string CurrentPhase { get; set; } = ""; public string Phase1Cpu { get; set; } = ""; public string Phase2Cpu { get; set; } = ""; public string Phase3Cpu { get; set; } = ""; public string Phase4Cpu { get; set; } = ""; public string Phase1Seconds { get; set; } = ""; public string Phase2Seconds { get; set; } = ""; public string Phase3Seconds { get; set; } = ""; public string Phase4Seconds { get; set; } = ""; public string CopyTimeSeconds { get; set; } = ""; public string TotalSeconds { get; set; } = ""; public string PlotSize { get; set; } = ""; public string Threads { get; set; } = ""; public string Buffer { get; set; } = ""; public string Buckets { get; set; } = ""; public string StartDate { get; set; } = ""; public string FinishDate { get; set; } = ""; public string PlotName { get; set; } = ""; public string LogFolder { get; set; } = ""; public string LogFile { get; set; } = ""; public string ApproximateWorkingSpace { get; set; } = ""; public string FinalFileSize { get; set; } = ""; public string Health { get; set; } = ""; public string LastLogLine { get; set; } = ""; public string PlaceInLogFile { get; set; } = ""; public string Note { get; set; } = ""; public string TotalEligiblePlots { get; set; } = ""; public string AvgEligiblePlots { get; set; } = ""; public string FoundProofs { get; set; } = ""; public string BestLookupTime { get; set; } = ""; public string WorstLookupTime { get; set; } = ""; public string AvgLookupTime { get; set; } = ""; public string FilterRatio { get; set; } = ""; public string TotalPlots { get; set; } = ""; public string ChallengesPerMinute { get; set; } = ""; public string AvgHeat { get; set; } = ""; public string MaxHeat { get; set; } = ""; public string MinHeat { get; set; } = ""; public string PoolPuzzleHash { get; set; } = ""; public string RuntimeMinutes { get; set; } = ""; } public class Columns { public string Tmp1Drive { get; set; } = ""; public string Tmp2Drive { get; set; } = ""; public string DestDrive { get; set; } = ""; public string Errors { get; set; } = ""; public string PID { get; set; } = ""; public string Progress { get; set; } = ""; public string TimeRemaining { get; set; } = ""; public string RunTimeSeconds { get; set; } = ""; public string ETA { get; set; } = ""; public string CurrentTable { get; set; } = ""; public string CurrentBucket { get; set; } = ""; public string CurrentPhase { get; set; } = ""; public string Phase1Cpu { get; set; } = ""; public string Phase2Cpu { get; set; } = ""; public string Phase3Cpu { get; set; } = ""; public string Phase4Cpu { get; set; } = ""; public string Phase1Seconds { get; set; } = ""; public string Phase2Seconds { get; set; } = ""; public string Phase3Seconds { get; set; } = ""; public string Phase4Seconds { get; set; } = ""; public string CopyTimeSeconds { get; set; } = ""; public string TotalSeconds { get; set; } = ""; public string PlotSize { get; set; } = ""; public string Threads { get; set; } = ""; public string Buffer { get; set; } = ""; public string Buckets { get; set; } = ""; public string StartDate { get; set; } = ""; public string FinishDate { get; set; } = ""; public string PlotName { get; set; } = ""; public string LogFolder { get; set; } = ""; public string LogFile { get; set; } = ""; public string ApproximateWorkingSpace { get; set; } = ""; public string FinalFileSize { get; set; } = ""; public string Health { get; set; } = ""; public string LastLogLine { get; set; } = ""; public string PlaceInLogFile { get; set; } = ""; public string Phase1AvgTimeNeed { get; set; } = ""; public string Phase2AvgTimeNeed { get; set; } = ""; public string Phase3AvgTimeNeed { get; set; } = ""; public string Phase4AvgTimeNeed { get; set; } = ""; public string CopyTimeAvgTimeNeed { get; set; } = ""; public string TotalAvgTimeNeed { get; set; } = ""; public string Phase1Completed { get; set; } = ""; public string Note { get; set; } = ""; public string TotalEligiblePlots { get; set; } = ""; public string AvgEligiblePlots { get; set; } = ""; public string FoundProofs { get; set; } = ""; public string BestLookupTime { get; set; } = ""; public string WorstLookupTime { get; set; } = ""; public string AvgLookupTime { get; set; } = ""; public string FilterRatio { get; set; } = ""; public string TotalPlots { get; set; } = ""; public string ChallengesPerMinute { get; set; } = ""; public string AvgHeat { get; set; } = ""; public string MaxHeat { get; set; } = ""; public string MinHeat { get; set; } = ""; public string PoolPuzzleHash { get; set; } = ""; public string RuntimeMinutes { get; set; } = ""; } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Program.cs ================================================ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.ReactiveUI; using System; namespace ChiaPlotStatus { class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. public static void Main(string[] args) => BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() .LogToTrace() .UseReactiveUI(); } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Utils/Utils.cs ================================================ using Avalonia.Controls; using Avalonia.Markup.Xaml.Styling; using ChiaPlotStatus.Views; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace ChiaPlotStatusGUI.GUI.Utils { public class Utils { /** * Open URL in default browser */ public static void OpenUrl(string url) { try { Process.Start(url); } catch { // workaround because of this: https://github.com/dotnet/corefx/issues/10361 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { url = url.Replace("&", "^&"); Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true }); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Process.Start("xdg-open", url); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Process.Start("open", url); } else { throw; } } } public static void OpenLogFile(string url) { try { Process.Start(url); } catch { // hack because of this: https://github.com/dotnet/corefx/issues/10361 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { url = url.Replace("&", "^&"); // force notepad or it will not open log files of running plots Process.Start(new ProcessStartInfo("cmd", $"/c start notepad {url}") { CreateNoWindow = true }); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Process.Start("xdg-open", url); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Process.Start("open", url); } else { throw; } } } public static void SetTheme(Window window, string theme) { var light = new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default") }; var dark = new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default") }; switch (theme) { case "Dark": window.Styles[0] = dark; break; default: window.Styles[0] = light; break; } } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/ViewLocator.cs ================================================ using Avalonia.Controls; using Avalonia.Controls.Templates; using ChiaPlotStatus.ViewModels; using System; namespace ChiaPlotStatus { public class ViewLocator : IDataTemplate { public bool SupportsRecycling => false; public IControl Build(object data) { var name = data.GetType().FullName!.Replace("ViewModel", "View"); var type = Type.GetType(name); if (type != null) { return (Control)Activator.CreateInstance(type)!; } else { return new TextBlock { Text = "Not Found: " + name }; } } public bool Match(object data) { return data is ViewModelBase; } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/ViewModels/MainWindowViewModel.cs ================================================ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Markup.Xaml.Styling; using Avalonia.Threading; using ChiaPlotStatus; using ChiaPlotStatus.Logic.Utils; using ChiaPlotStatus.GUI.Models; using ChiaPlotStatus.Views; using ReactiveUI; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Reactive; using System.Text; using System.Threading.Tasks; using ChiaPlotStatus.Logic.Models; using Avalonia; using System.Runtime.InteropServices; using ChiaPlotStatusGUI.GUI.Models; using ChiaPlotStatusGUI.GUI.Utils; using System.ComponentModel; namespace ChiaPlotStatus.ViewModels { public class MainWindowViewModel : ViewModelBase { public ChiaPlotStatus PlotManager { get; internal set; } public ObservableCollection PlotLogs { get; set; } = new(); public List<(PlotLog, PlotLogReadable)> PlotLogTuples { get; set; } = new(); public PlotCounts PlotCounts { get; set; } public string? Search { get; set; } = null; public ObservableCollection SortProperties = new(); public Language Language { get; set; } public Dictionary Languages { get; set; } public ReactiveCommand ExportJsonCommand { get; set; } public bool RawExport { get; set; } = false; public ReactiveCommand ExportYamlCommand { get; set; } public ReactiveCommand ExportCsvCommand { get; set; } public ReactiveCommand AddFolderCommand { get; set; } public ReactiveCommand RemoveFolderCommand { get; set; } public ReactiveCommand IncreaseFontSizeCommand { get; set; } public ReactiveCommand DecreaseFontSizeCommand { get; set; } public ReactiveCommand MarkAsDeadCommand { get; set; } public ReactiveCommand NoteCommand { get; set; } public ReactiveCommand MarkSelectionAsDeadCommand { get; set; } public ReactiveCommand SelectAllPossiblyDeadCommand { get; set; } public ReactiveCommand SelectAllConcerningCommand { get; set; } public DispatcherTimer RefreshTimer { get; set; } public MainWindowViewModel() { if (MainWindow.Instance.Find ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/ChiaPlotterDialog.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using ChiaPlotStatus.GUI.Models; using ChiaPlotStatus.Logic.Models; using ChiaPlotStatus.Logic.Utils; using ChiaPlotStatusGUI.GUI.Utils; using ChiaPlotStatusGUI.GUI.ViewModels; using ChiaPlotStatusLib.Logic.Models; using ReactiveUI; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Reactive; using System.Text.RegularExpressions; namespace ChiaPlotStatus.Views { public class ChiaPlotterDialog : Window { public Language Language { get; set; } public List<(CPPlotLog, CPPlotLogReadable)> PlotLogTuples { get; set; } public ObservableCollection PlotLogs { get; set; } = new(); public ChiaPlotStatus PlotManager { get; set; } public string SortProperty { get; set; } = "Tmp1Drive"; public string Search { get; set; } = ""; public bool SortAsc { get; set; } = true; public ChiaPlotterDialog() { } public ChiaPlotterDialog(ChiaPlotStatus plotManager, Language language, string theme) { this.DataContext = this; this.Language = language; this.PlotManager = plotManager; InitializeComponent(); #if DEBUG this.AttachDevTools(); #endif KeepGridScrollbarOnScreen(); LoadData(); this.WindowState = WindowState.Maximized; Utils.SetTheme(this, theme); this.Focus(); } private void LoadData() { this.Find("LogDataGrid").BeginBatchUpdate(); PlotLogs.Clear(); PlotLogTuples = new(); foreach (var plotLog in PlotManager.PollCPPlotLogs(PlotManager.Settings.SortProperty, (bool)PlotManager.Settings.SortAsc, Search, PlotManager.Settings.Filter)) { PlotLogs.Add(plotLog.Item2); PlotLogTuples.Add(plotLog); } // PlotCounts = new(PlotLogTuples); // HandleFinishDateVisibility(); this.Find("LogDataGrid").EndBatchUpdate(); //this.RaisePropertyChanged("PlotCounts"); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } public void OnSelectionChanged(object sender, RoutedEventArgs e) { // TODO: SelectionChangedAction(); } public void OpenLogViewerWindow(object sender, RoutedEventArgs e) { var plotLogReadable = (PlotLogReadable)((Button)sender).Tag; var path = plotLogReadable.LogFolder + Path.DirectorySeparatorChar + plotLogReadable.LogFile; Utils.OpenLogFile(path); } public void LogDataGridHeaderClick(object sender, RoutedEventArgs e) { Button header = ((Button)sender); string headerText = (string)header.Content; var oldSortProperty = SortProperty; foreach (var property in typeof(GUI.Models.Columns).GetProperties()) { string translation = (string)property.GetValue(Language.Columns); if (string.Equals(headerText, translation)) { SortProperty = property.Name; break; } } if (string.Equals(SortProperty, oldSortProperty)) SortAsc = !SortAsc; Sorter.Sort(SortProperty, SortAsc, PlotLogTuples); PlotLogs.Clear(); foreach (var tuple in PlotLogTuples) PlotLogs.Add(tuple.Item2); // Debug.WriteLine("SearchProperty: " + SortProperty + ", ASC: " + SortAsc); } private void KeepGridScrollbarOnScreen() { this.WhenAnyValue(x => x.Height) .Subscribe(x => { var logDataGrid = this.Find("LogDataGrid"); logDataGrid.Height = x - 145; }); } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/DonationDialog.axaml ================================================ ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/DonationDialog.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using ChiaPlotStatus.GUI.Models; using ChiaPlotStatus.Logic.Models; using ChiaPlotStatusGUI.GUI.Utils; using ReactiveUI; using System; using System.Diagnostics; using System.Drawing; using System.Reactive; using System.Runtime.InteropServices; using System.Text.RegularExpressions; namespace ChiaPlotStatus.Views { public class DonationDialog : Window { public Language Language { get; set; } public string ChiaAddress { get; set; } = "xch15p8swrrdt5ujv0dxy4hwjrvpjseyvuquwtfwnrjhxqt6ws9uf90qzq4axl"; public string PaypalURL { get; set; } = "https://www.paypal.com/donate?hosted_button_id=PDLLVF5XVMJPC"; public string LiberapayURL { get; set; } = "https://liberapay.com/grayfallstown/donate"; public DonationDialog() { } public DonationDialog(Language language, string theme) { this.DataContext = this; this.Language = language; InitializeComponent(); #if DEBUG this.AttachDevTools(); #endif this.Find("Thx").IsVisible = false; Utils.SetTheme(this, theme); this.Focus(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } public void CopyToClipboard(object sender, RoutedEventArgs e) { string text = (string)(((Button)sender).Tag); Application.Current.Clipboard.SetTextAsync(text); this.Find("Thx").IsVisible = true; } public void OpenLink(object sender, RoutedEventArgs e) { string url = (string)(((Button)sender).Tag); Utils.OpenUrl(url); this.Find("Thx").IsVisible = true; } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/HarvestDialog.axaml ================================================ ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/HarvestDialog.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using Avalonia.Threading; using ChiaPlotStatus.GUI.Models; using ChiaPlotStatus.Logic.Models; using ChiaPlotStatusGUI.GUI.Utils; using ChiaPlotStatusLib.Logic.Models; using ChiaPlotStatusLib.Logic.Statistics.Harvest; using ReactiveUI; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Reactive; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace ChiaPlotStatus.Views { /** * TODO: * - FolderList * - AddFolder * - RemoveFolder * - Summary List * - Heatmaps in Details (either tabs or sub-dialogs) */ public class HarvestDialog : Window { public Settings Settings { get; set; } public Language Language { get; set; } public List>?> Results { get; set; } public ObservableCollection Summaries { get; set; } = new(); public ObservableCollection PathsWithoutResults { get; set; } = new(); public HarvestDialog() { } public HarvestDialog(Language language, Settings settings, string theme) { this.DataContext = this; this.Language = language; this.Settings = settings; foreach (var path in HarvestParser.DefaultPaths()) this.Settings.HarvesterLogDirectories.Add(path); InitializeComponent(); KeepGridScrollbarOnScreen(); #if DEBUG this.AttachDevTools(); #endif Utils.SetTheme(this, theme); //Task.Run(async () => //{ LoadData(); Dispatcher.UIThread.InvokeAsync(() => { this.Find("Loading").IsVisible = false; this.Find("Loaded").IsVisible = true; }); //}); this.WindowState = WindowState.Maximized; this.Focus(); } public void AddFolder(string folder) { if (!Settings.HarvesterLogDirectories.Contains(folder)) { Settings.HarvesterLogDirectories.Add(folder); Settings.Persist(); LoadData(); } } public void AddFolder(object sender, RoutedEventArgs e) { Task.Run(async () => { OpenFolderDialog picker = new OpenFolderDialog(); var result = await picker.ShowAsync(this); Dispatcher.UIThread.InvokeAsync(() => AddFolder(result)); }); } public void RemoveFolder (object sender, RoutedEventArgs e) { string folder = (string)((Button)sender).Tag; Settings.HarvesterLogDirectories.Remove(folder); Settings.Persist(); LoadData(); } private void LoadData() { Summaries.Clear(); PathsWithoutResults.Clear(); Results = new HarvestParser().ParseLogs(new List(this.Settings.HarvesterLogDirectories), (double)this.Settings.MaxHarvestLookupSeconds, 500); // assume all are missing until a result is found foreach (var path in this.Settings.HarvesterLogDirectories) PathsWithoutResults.Add(path); foreach (var triplet in Results) { if (triplet != null) { Summaries.Add(new HarvestSummeryReadable(triplet.Item2)); PathsWithoutResults.Remove(triplet.Item1); } } } private void KeepGridScrollbarOnScreen() { this.WhenAnyValue(x => x.Height) .Subscribe(x => { var summeriesDataGrid = this.Find("SummeriesDataGrid"); summeriesDataGrid.Height = x - 100; }); } public void DataGridHeaderClick(object sender, RoutedEventArgs e) { } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/MainWindow.axaml ================================================ Running: Finished: Concerning: Failed: Phase 1: Phase 2: Phase 3: Phase 4: Phase 5: Logo made by charlie on freeicons.io under License Creative Commons(Attribution 3.0 unported) ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/MainWindow.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using ChiaPlotStatusGUI.GUI.Utils; using ReactiveUI; using System; using System.Diagnostics; using System.Drawing; using System.IO; using System.Reactive; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace ChiaPlotStatus.Views { public class MainWindow : Window { public static MainWindow? Instance { get; private set; } public Func BtnClickWorkaround { get; set; } public Func TextChangeWorkaround { get; set; } public Func SortChangeWorkaround { get; set; } public Func ThemeSwitchWorkaround { get; set; } public Action SelectionChangedAction { get; set; } public MainWindow() { Instance = this; InitializeComponent(); #if DEBUG this.AttachDevTools(); #endif ThemeSwitcher(); this.WindowState = WindowState.Maximized; } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } // FIXME: RemoveFolderCommand does not trigger. Why is button.Command null? public void RemoveFolderWorkaround(object sender, RoutedEventArgs e) { Button button = (Button)sender; string folder = (string)button.CommandParameter; ReactiveCommand command = (ReactiveCommand)button.Command; if (command == null) { command = (ReactiveCommand)button.Tag; } // button.Command.Execute(folder); BtnClickWorkaround.Invoke(folder); } // FIXME: apparently avalonia cannot tell me when a textbox text changes in my MainWindowViewModel public void OnKeyPressUp(object sender, KeyEventArgs e) { TextChangeWorkaround(((TextBox)sender).Text); } public void LogDataGridHeaderClick(object sender, RoutedEventArgs e) { Button header = ((Button)sender); string headerText = (string) header.Content; SortChangeWorkaround(headerText); } public void ThemeSwitcher() { var themes = this.Find("Themes"); themes.SelectionChanged += (sender, e) => { switch (themes.SelectedIndex) { case 1: ThemeSwitchWorkaround("Dark"); break; default: ThemeSwitchWorkaround("Light"); break; } }; } public void OnSelectionChanged(object sender, RoutedEventArgs e) { SelectionChangedAction(); } public void OpenLogViewerWindow(object sender, RoutedEventArgs e) { var plotLogReadable = (PlotLogReadable)((Button)sender).Tag; var path = plotLogReadable.LogFolder + Path.DirectorySeparatorChar + plotLogReadable.LogFile; Utils.OpenLogFile(path); } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/MarkOfDeathDialog.axaml ================================================ ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/MarkOfDeathDialog.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using ChiaPlotStatus.GUI.Models; using ChiaPlotStatus.Logic.Models; using ChiaPlotStatusGUI.GUI.Utils; using ReactiveUI; using System; using System.Diagnostics; using System.Drawing; using System.Reactive; using System.Text.RegularExpressions; namespace ChiaPlotStatus.Views { public class MarkOfDeathDialog : Window { public Settings Settings { get; set; } public Language Language { get; set; } public Action OnUpdate { get; set; } public bool IsDead { get; set; } public bool IsAlive { get; set; } public PlotLogReadable plotLogReadable { get; } public MarkOfDeathDialog() { } public MarkOfDeathDialog(PlotLogReadable plotLogReadable, Language language, Settings settings, string theme, Action onUpdate) { this.DataContext = this; this.Language = language; this.Settings = settings; this.plotLogReadable = plotLogReadable; this.OnUpdate = onUpdate; InitializeComponent(); #if DEBUG this.AttachDevTools(); #endif foreach (var markOfDeath in Settings.MarksOfDeath) if (markOfDeath.IsMatch(plotLogReadable)) this.IsDead = true; this.IsAlive = !this.IsDead; Utils.SetTheme(this, theme); this.Focus(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } public void MarkAsDead(object sender, RoutedEventArgs e) { var mark = new MarkOfDeath(this.plotLogReadable); if (!this.Settings.MarksOfDeath.Contains(mark)) { this.Settings.MarksOfDeath.Add(mark); this.Settings.Persist(); this.OnUpdate(); } this.Close(); } public void UnmarkAsDead(object sender, RoutedEventArgs e) { var mark = new MarkOfDeath(this.plotLogReadable); if (this.Settings.MarksOfDeath.Contains(mark)) { this.Settings.MarksOfDeath.Remove(mark); this.Settings.Persist(); this.OnUpdate(); } this.Close(); } public void Abort(object sender, RoutedEventArgs e) { this.Close(); } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/NoteDialog.axaml ================================================ ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/NoteDialog.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using ChiaPlotStatus.GUI.Models; using ChiaPlotStatus.Logic.Models; using ChiaPlotStatusGUI.GUI.Utils; using ChiaPlotStatusLib.Logic.Models; using ReactiveUI; using System; using System.Diagnostics; using System.Drawing; using System.Reactive; using System.Text.RegularExpressions; namespace ChiaPlotStatus.Views { public class NoteDialog : Window { public Settings Settings { get; set; } public Language Language { get; set; } public Action OnUpdate { get; set; } public PlotLogReadable plotLogReadable { get; } public string Note { get; set; } public NoteDialog() { } public NoteDialog(PlotLogReadable plotLogReadable, Language language, Settings settings, string theme, Action onUpdate) { this.DataContext = this; this.Language = language; this.Settings = settings; this.plotLogReadable = plotLogReadable; this.OnUpdate = onUpdate; this.Note = plotLogReadable.Note; InitializeComponent(); #if DEBUG this.AttachDevTools(); #endif Utils.SetTheme(this, theme); this.Focus(); // does not work for some reason: this.Find("Input").SelectionStart = 0; this.Find("Input").SelectionEnd = plotLogReadable.Note.Length; this.Find("Input").Focus(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } public void Save(object sender, RoutedEventArgs e) { var note = new Note(this.plotLogReadable); note.text = this.Note; if (this.Settings.Notes.Contains(note)) this.Settings.Notes.Remove(note); this.Settings.Notes.Add(note); this.Settings.Persist(); this.OnUpdate(); this.Close(); } public void Abort(object sender, RoutedEventArgs e) { this.Close(); } } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/StatisticsDialog.axaml ================================================ ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/StatisticsDialog.axaml.cs ================================================ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using ChiaPlotStatus.GUI.Models; using ChiaPlotStatus.Logic.Models; using ChiaPlotStatus.Logic.Utils; using ChiaPlotStatusGUI.GUI.Utils; using ChiaPlotStatusGUI.GUI.ViewModels; using ReactiveUI; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; using System.Reactive; using System.Text.RegularExpressions; namespace ChiaPlotStatus.Views { public class StatisticsDialog : Window { public Language Language { get; set; } public List<(PlottingStatisticsFull, PlottingStatisticsFullReadable)> StatsTuples { get; set; } public ObservableCollection Stats { get; set; } = new(); public List DailyStats { get; set; } public ChiaPlotStatus PlotManager { get; set; } public string SortProperty { get; set; } = "Tmp1Drive"; public bool SortAsc { get; set; } = true; public StatisticsDialog() { } public StatisticsDialog(ChiaPlotStatus plotManager, Language language, string theme) { this.DataContext = this; this.Language = language; this.PlotManager = plotManager; LoadData(); InitDailyStatsTable(); InitializeComponent(); #if DEBUG this.AttachDevTools(); #endif KeepGridScrollbarOnScreen(); this.WindowState = WindowState.Maximized; Utils.SetTheme(this, theme); this.Focus(); } private void LoadData() { this.StatsTuples = PlotManager.Statistics.AllStatistics(); Sorter.Sort(SortProperty, SortAsc, StatsTuples); Stats.Clear(); foreach (var tuple in this.StatsTuples) Stats.Add(tuple.Item2); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } public void DataGridHeaderClick(object sender, RoutedEventArgs e) { Button header = ((Button)sender); string headerText = (string)header.Content; var oldSortProperty = SortProperty; foreach (var property in typeof(Columns).GetProperties()) { string translation = (string)property.GetValue(Language.Columns); if (string.Equals(headerText, translation)) { SortProperty = property.Name; break; } } if (string.Equals(SortProperty, oldSortProperty)) SortAsc = !SortAsc; Sorter.Sort(SortProperty, SortAsc, StatsTuples); Stats.Clear(); foreach (var tuple in StatsTuples) Stats.Add(tuple.Item2); // Debug.WriteLine("SearchProperty: " + SortProperty + ", ASC: " + SortAsc); } private void KeepGridScrollbarOnScreen() { this.WhenAnyValue(x => x.Height) .Subscribe(x => { var statsDataGrid = this.Find("StatsDataGrid"); var dailyStatsDataGrid = this.Find("DailyStatsDataGrid"); statsDataGrid.Height = x / 2; dailyStatsDataGrid.Height = x / 2; }); } public void InitDailyStatsTable() { Dictionary dailyStats = PlotManager.Statistics.GetDailyStats(); List days = new(dailyStats.Values); days.Sort((a, b) => -1 * a.Day.CompareTo(b.Day)); DailyStats = new(); foreach (var stat in days) DailyStats.Add(new(stat)); } /* private void BuildPlotModel() { Dictionary dailyStats = PlotManager.Statistics.GetDailyStats(); Dictionary lineSeries = new(); LineSeries getLineSeries(string name) { if (!lineSeries.ContainsKey(name)) lineSeries.Add(name, new LineSeries()); return lineSeries[name]; } var plotModel = new PlotModel { Title = "test1", TitleToolTip = "test2" }; DateTime twoWeeksAgo = DateTime.Now.AddDays(-14); plotModel.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom, Minimum = DateTimeAxis.ToDouble(twoWeeksAgo), Maximum = DateTimeAxis.ToDouble(DateTime.Now), StringFormat = "MMM dd" }); List stats = new(dailyStats.Values); stats.Sort((a, b)=> a.Day.CompareTo(b.Day)); foreach (var stat in stats) { getLineSeries("Phase1").Points.Add(new DataPoint(DateTimeAxis.ToDouble(stat.Day), stat.Phase1)); getLineSeries("Phase2").Points.Add(new DataPoint(DateTimeAxis.ToDouble(stat.Day), stat.Phase2)); getLineSeries("Phase3").Points.Add(new DataPoint(DateTimeAxis.ToDouble(stat.Day), stat.Phase3)); getLineSeries("Phase4").Points.Add(new DataPoint(DateTimeAxis.ToDouble(stat.Day), stat.Phase4)); getLineSeries("Phase5").Points.Add(new DataPoint(DateTimeAxis.ToDouble(stat.Day), stat.Phase5)); getLineSeries("Finished").Points.Add(new DataPoint(DateTimeAxis.ToDouble(stat.Day), stat.Finished)); getLineSeries("Died").Points.Add(new DataPoint(DateTimeAxis.ToDouble(stat.Day), stat.Died)); } foreach (var series in lineSeries.Values) plotModel.Series.Add(series); this.PlotModel = plotModel; } */ } } ================================================ FILE: ChiaPlotStatusGUI/GUI/Views/UpdateDialog.axaml ================================================