feat(nulls): prevents the flash of null data between component iterations

overrides the ExpireDownStreamObjects() (c) @alanrynne 🙌

fixes #2 and fixes #3 too i think
This commit is contained in:
Dimitrie Stefanescu
2020-10-09 15:28:24 +01:00
parent 4d9d25cb21
commit 53157bc048
5 changed files with 48 additions and 15 deletions
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grasshopper.Kernel;
@@ -19,12 +20,14 @@ namespace GrasshopperAsyncComponent
public override GH_Exposure Exposure => GH_Exposure.hidden;
Action<string> ReportProgress;
Action<string, GH_RuntimeMessageLevel> ReportError;
List<(string, GH_RuntimeMessageLevel)> Errors;
Action<string, double> ReportProgress;
ConcurrentDictionary<string, double> ProgressReports;
Action Done;
Timer DisplayProgressTimer;
@@ -55,15 +58,31 @@ namespace GrasshopperAsyncComponent
DisplayProgressTimer = new Timer(333) { AutoReset = false };
DisplayProgressTimer.Elapsed += (s, e) =>
{
if (Workers.Count == 0) return;
if (Workers.Count == 1)
{
Message = ProgressReports.Values.Last().ToString("0.00%");
}
else
{
double total = 0;
foreach (var kvp in ProgressReports)
{
total += kvp.Value;
}
Message = (total / Workers.Count).ToString("0.00%");
}
Rhino.RhinoApp.InvokeOnUiThread((Action)delegate
{
OnDisplayExpired(true);
});
};
ReportProgress = (progress) =>
ReportProgress = (id, value) =>
{
Message = progress;
ProgressReports[id] = value;
if (!DisplayProgressTimer.Enabled) DisplayProgressTimer.Start();
};
@@ -87,6 +106,7 @@ namespace GrasshopperAsyncComponent
};
Errors = new List<(string, GH_RuntimeMessageLevel)>();
ProgressReports = new ConcurrentDictionary<string, double>();
Workers = new List<WorkerInstance>();
CancelationSources = new List<CancellationTokenSource>();
@@ -102,9 +122,12 @@ namespace GrasshopperAsyncComponent
CancelationSources.Clear();
Workers.Clear();
Errors.Clear();
ProgressReports.Clear();
Tasks.Clear();
State = 0;
var test = Params.Output[0].VolatileData;
}
protected override void AfterSolveInstance()
@@ -113,9 +136,17 @@ namespace GrasshopperAsyncComponent
if (State == 0 && Tasks.Count > 0)
{
foreach (var task in Tasks) task.Start();
var test = Params.Output[0].VolatileData;
}
}
protected override void ExpireDownStreamObjects()
{
// Prevents the flash of null data until the new solution is ready
if (SetData)
base.ExpireDownStreamObjects();
}
protected override void SolveInstance(IGH_DataAccess DA)
{
if (State == 0)
@@ -139,7 +170,7 @@ namespace GrasshopperAsyncComponent
// Create the task
var tokenSource = new CancellationTokenSource();
CurrentWorker.CancellationToken = tokenSource.Token;
CurrentWorker.Id = DA.Iteration.ToString();
CurrentWorker.Id = $"Worker-{DA.Iteration}";
Task CurrentRun;
if (TaskCreationOptions != null)
@@ -176,6 +207,7 @@ namespace GrasshopperAsyncComponent
CancelationSources.Clear();
Workers.Clear();
Errors.Clear();
ProgressReports.Clear();
Tasks.Clear();
SetData = false;
@@ -37,7 +37,7 @@ namespace GrasshopperAsyncComponent
/// <param name="ReportProgress">Call this to report progress up to the parent component.</param>
/// <param name="ReportError">Call this to report errors up to the parent component.</param>
/// <param name="Done">Call this when everything is <b>done</b>. It will tell the parent component that you're ready to <see cref="SetData(IGH_DataAccess)"/>.</param>
public abstract void DoWork(Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done);
public abstract void DoWork(Action<string, double> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done);
/// <summary>
/// Write your data setting logic here. <b>Do not call this function directly from this class. It will be invoked by the parent <see cref="GH_AsyncComponent"/> after you've called `Done` in the <see cref="DoWork(Action{string}, Action{string, GH_RuntimeMessageLevel}, Action)"/> function.</b>
@@ -61,7 +61,7 @@
<ItemGroup>
<Compile Include="Base\GH_AsyncComponent.cs" />
<Compile Include="Info\GrasshopperAsyncComponentInfo.cs" />
<Compile Include="Base\IAsyncComponentWorker.cs" />
<Compile Include="Base\WorkerInstance.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SampleImplementations\Sample_PrimeCalculatorAsyncComponent.cs" />
<Compile Include="SampleImplementations\Sample_UslessCyclesComponent.cs" />
@@ -16,7 +16,7 @@ namespace GrasshopperAsyncComponent.SampleImplementations
public override GH_Exposure Exposure => GH_Exposure.primary;
public Sample_PrimeCalculatorAsyncComponent() : base("Sample Async Component", "PRIME", "Calculates the nth prime number.", "Samples", "Async")
public Sample_PrimeCalculatorAsyncComponent() : base("The N-th Prime Calculator", "PRIME", "Calculates the nth prime number.", "Samples", "Async")
{
BaseWorker = new PrimeCalculatorWorker();
}
@@ -28,7 +28,8 @@ namespace GrasshopperAsyncComponent.SampleImplementations
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddTextParameter("Output", "O", "The n-th prime number.", GH_ParamAccess.item);
pManager.AddNumberParameter("Output", "O", "The n-th prime number.", GH_ParamAccess.item);
}
}
@@ -37,7 +38,7 @@ namespace GrasshopperAsyncComponent.SampleImplementations
int TheNthPrime { get; set; } = 100;
long ThePrime { get; set; } = -1;
public override void DoWork(Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done)
public override void DoWork(Action<string, double> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done)
{
// 👉 Checking for cancellation!
if (CancellationToken.IsCancellationRequested) return;
@@ -66,7 +67,7 @@ namespace GrasshopperAsyncComponent.SampleImplementations
b++;
}
ReportProgress(((double)(count) / TheNthPrime).ToString("0.00%"));
ReportProgress(Id, ((double)count) / TheNthPrime);
if (prime > 0)
{
@@ -96,7 +97,7 @@ namespace GrasshopperAsyncComponent.SampleImplementations
// 👉 Checking for cancellation!
if (CancellationToken.IsCancellationRequested) return;
DA.SetData(0, $"Worker {Id}: {TheNthPrime}th prime is: {ThePrime}");
DA.SetData(0, ThePrime);
}
}
@@ -36,7 +36,7 @@ namespace GrasshopperAsyncComponent.SampleImplementations
{
int MaxIterations { get; set; } = 100;
public override void DoWork(Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done)
public override void DoWork(Action<string, double> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done)
{
// Checking for cancellation
if (CancellationToken.IsCancellationRequested) return;
@@ -47,8 +47,8 @@ namespace GrasshopperAsyncComponent.SampleImplementations
for (int j = 0; j <= 100; j++)
sw.SpinOnce();
ReportProgress(((double)(i + 1) / (double)MaxIterations).ToString("0.00%"));
ReportProgress(Id, ((double)(i + 1) / (double)MaxIterations));
// Checking for cancellation
if (CancellationToken.IsCancellationRequested) return;
}