feat(multiple runs): adds iteration handling to async component base
nifty data matching is now in place fixes #1
This commit is contained in:
@@ -16,14 +16,6 @@ namespace GrasshopperAsyncComponent
|
||||
|
||||
public override GH_Exposure Exposure => GH_Exposure.hidden;
|
||||
|
||||
public IAsyncComponentWorker Worker;
|
||||
|
||||
IAsyncComponentWorker CurrentWorker;
|
||||
|
||||
Task CurrentRun;
|
||||
|
||||
ConcurrentBag<CancellationTokenSource> TokenSources = new ConcurrentBag<CancellationTokenSource>();
|
||||
|
||||
Action<string> ReportProgress;
|
||||
|
||||
Action<string, GH_RuntimeMessageLevel> ReportError;
|
||||
@@ -32,9 +24,18 @@ namespace GrasshopperAsyncComponent
|
||||
|
||||
Action Done;
|
||||
|
||||
Timer DisplayProgressTimer;
|
||||
|
||||
int State = 0;
|
||||
|
||||
Timer DisplayProgressTimer;
|
||||
int Iterations = 0;
|
||||
|
||||
public WorkerInstance BaseWorker { get; set; }
|
||||
|
||||
List<WorkerInstance> Workers;
|
||||
|
||||
List<CancellationTokenSource> CancelationSources;
|
||||
|
||||
|
||||
protected GH_AsyncComponent(string name, string nickname, string description, string category, string subCategory) : base(name, nickname, description, category, subCategory)
|
||||
{
|
||||
@@ -50,78 +51,97 @@ namespace GrasshopperAsyncComponent
|
||||
|
||||
ReportProgress = (progress) =>
|
||||
{
|
||||
Rhino.RhinoApp.InvokeOnUiThread((Action)delegate
|
||||
{
|
||||
Message = progress;
|
||||
if (!DisplayProgressTimer.Enabled) DisplayProgressTimer.Start();
|
||||
});
|
||||
Message = progress;
|
||||
if (!DisplayProgressTimer.Enabled) DisplayProgressTimer.Start();
|
||||
};
|
||||
|
||||
ReportError = (error, type) => Errors?.Add((error, type));
|
||||
|
||||
Done = () =>
|
||||
{
|
||||
Rhino.RhinoApp.InvokeOnUiThread((Action)delegate
|
||||
State++;
|
||||
|
||||
if (State == Iterations)
|
||||
{
|
||||
State = 1;
|
||||
ExpireSolution(true);
|
||||
});
|
||||
Rhino.RhinoApp.InvokeOnUiThread((Action)delegate
|
||||
{
|
||||
//State = 1;
|
||||
ExpireSolution(true);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Errors = new List<(string, GH_RuntimeMessageLevel)>();
|
||||
|
||||
Workers = new List<WorkerInstance>();
|
||||
CancelationSources = new List<CancellationTokenSource>();
|
||||
}
|
||||
|
||||
protected override void BeforeSolveInstance()
|
||||
{
|
||||
if (State != 0) return;
|
||||
|
||||
foreach (var source in CancelationSources) source.Cancel();
|
||||
|
||||
CancelationSources.Clear();
|
||||
Workers.Clear();
|
||||
Errors.Clear();
|
||||
|
||||
State = 0;
|
||||
Iterations = 0;
|
||||
base.BeforeSolveInstance();
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess DA)
|
||||
{
|
||||
if (State == 0)
|
||||
{
|
||||
if (Worker == null)
|
||||
Iterations++;
|
||||
if (BaseWorker == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Worker class not provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentWorker = Worker.GetNewInstance();
|
||||
var CurrentWorker = BaseWorker.Duplicate();
|
||||
if (CurrentWorker == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Could not get a worker instance.");
|
||||
return;
|
||||
}
|
||||
|
||||
Errors = new List<(string, GH_RuntimeMessageLevel)>();
|
||||
|
||||
// Request the cancellation of any old tasks.
|
||||
CancellationTokenSource oldTokenSource;
|
||||
while (TokenSources.TryTake(out oldTokenSource))
|
||||
{
|
||||
oldTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
// Let the worker collect data.
|
||||
CurrentWorker.CollectData(DA, Params);
|
||||
CurrentWorker.GetData(DA, Params);
|
||||
|
||||
// Create the task
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
CurrentRun = new Task(() => CurrentWorker.DoWork(tokenSource.Token, ReportProgress, ReportError, Done), tokenSource.Token);
|
||||
CurrentWorker.CancellationToken = tokenSource.Token;
|
||||
var CurrentRun = new Task(() => CurrentWorker.DoWork(ReportProgress, ReportError, Done), tokenSource.Token, TaskCreationOptions.LongRunning);
|
||||
|
||||
// Add cancelation source to our bag
|
||||
TokenSources.Add(tokenSource);
|
||||
CancelationSources.Add(tokenSource);
|
||||
// Add the worker to our list
|
||||
Workers.Add(CurrentWorker);
|
||||
|
||||
CurrentRun.Start();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (message, type) in Errors)
|
||||
var test = DA.Iteration;
|
||||
|
||||
Workers[DA.Iteration].SetData(DA);
|
||||
|
||||
if (--State == 0)
|
||||
{
|
||||
AddRuntimeMessage(type, message);
|
||||
foreach (var (message, type) in Errors)
|
||||
{
|
||||
AddRuntimeMessage(type, message);
|
||||
}
|
||||
|
||||
Message = "Done";
|
||||
OnDisplayExpired(true);
|
||||
Errors.Clear();
|
||||
}
|
||||
|
||||
OnDisplayExpired(true);
|
||||
CurrentWorker.SetData(DA);
|
||||
|
||||
Message = "Done";
|
||||
|
||||
Errors.Clear();
|
||||
State = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,21 @@ namespace GrasshopperAsyncComponent
|
||||
/// <param name="ReportError">Call this to report a warning or an error.</param>
|
||||
/// <param name="Done">When you are done computing, call this function to have the parent component invoke the SetData function.</param>
|
||||
void DoWork(CancellationToken token, Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done);
|
||||
|
||||
}
|
||||
|
||||
public abstract class WorkerInstance
|
||||
{
|
||||
public CancellationToken CancellationToken { get; set; }
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public abstract WorkerInstance Duplicate();
|
||||
|
||||
public abstract void DoWork(Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done);
|
||||
|
||||
public abstract void SetData(IGH_DataAccess DA);
|
||||
|
||||
public abstract void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace GrasshopperAsyncComponent.SampleImplementations
|
||||
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
public SampleAsyncComponent() : base("Sample Async Component", "ASYNC", "Meaningless labour.", "Samples", "Async")
|
||||
public SampleAsyncComponent() : base("Sample Async Component", "CYCLOMAXOTRON", "Meaningless labour.", "Samples", "Async")
|
||||
{
|
||||
Worker = new SampleAsyncComponentWorker();
|
||||
BaseWorker = new PrimeCalculator();
|
||||
}
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
@@ -32,46 +32,112 @@ namespace GrasshopperAsyncComponent.SampleImplementations
|
||||
}
|
||||
}
|
||||
|
||||
public class SampleAsyncComponentWorker : IAsyncComponentWorker
|
||||
public class SampleAsyncWorker : WorkerInstance
|
||||
{
|
||||
int MaxIterations { get; set; } = 100;
|
||||
|
||||
public void CollectData(IGH_DataAccess DA, GH_ComponentParamServer Params)
|
||||
|
||||
public override void DoWork(Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done)
|
||||
{
|
||||
int _maxIterations = 100;
|
||||
DA.GetData(0, ref _maxIterations);
|
||||
if (_maxIterations > 1000) MaxIterations = 1000;
|
||||
if (_maxIterations < 10) MaxIterations = 10;
|
||||
|
||||
MaxIterations = _maxIterations;
|
||||
}
|
||||
|
||||
public void DoWork(CancellationToken token, Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done)
|
||||
{
|
||||
if (token.IsCancellationRequested) return;
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
|
||||
for (int i = 0; i <= MaxIterations; i++)
|
||||
{
|
||||
var sw = new SpinWait();
|
||||
for (int j = 0; j <= 100; j++)
|
||||
sw.SpinOnce();
|
||||
|
||||
|
||||
ReportProgress(((double)(i + 1) / (double)MaxIterations).ToString("0.00%"));
|
||||
|
||||
if (token.IsCancellationRequested) return;
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
}
|
||||
|
||||
Done();
|
||||
}
|
||||
|
||||
public IAsyncComponentWorker GetNewInstance()
|
||||
public override WorkerInstance Duplicate() => new SampleAsyncWorker();
|
||||
|
||||
public override void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params)
|
||||
{
|
||||
return new SampleAsyncComponentWorker();
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
|
||||
int _maxIterations = 100;
|
||||
DA.GetData(0, ref _maxIterations);
|
||||
if (_maxIterations > 1000) _maxIterations = 1000;
|
||||
if (_maxIterations < 10) _maxIterations = 10;
|
||||
|
||||
MaxIterations = _maxIterations;
|
||||
}
|
||||
|
||||
public void SetData(IGH_DataAccess DA)
|
||||
public override void SetData(IGH_DataAccess DA)
|
||||
{
|
||||
DA.SetData(0, "Hello world. I'm done spinning.");
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
DA.SetData(0, $"Hello world. Worker {Id} has spun for {MaxIterations} iterations.");
|
||||
}
|
||||
}
|
||||
|
||||
public class PrimeCalculator : WorkerInstance
|
||||
{
|
||||
int TehNthPrime { get; set; } = 100;
|
||||
long ThePrime { get; set; } = -1;
|
||||
|
||||
public override void DoWork(Action<string> ReportProgress, Action<string, GH_RuntimeMessageLevel> ReportError, Action Done)
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
|
||||
int count = 0;
|
||||
long a = 2;
|
||||
|
||||
while (count < TehNthPrime)
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
|
||||
long b = 2;
|
||||
int prime = 1;// to check if found a prime
|
||||
while (b * b <= a)
|
||||
{
|
||||
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
|
||||
if (a % b == 0)
|
||||
{
|
||||
prime = 0;
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
}
|
||||
|
||||
ReportProgress(((double)(count) / (double)TehNthPrime).ToString("0.00%"));
|
||||
|
||||
if (prime > 0)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
|
||||
ThePrime = --a;
|
||||
Done();
|
||||
}
|
||||
|
||||
public override WorkerInstance Duplicate() => new PrimeCalculator();
|
||||
|
||||
public override void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params)
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
|
||||
int _maxIterations = 100;
|
||||
DA.GetData(0, ref _maxIterations);
|
||||
if (_maxIterations > 1000000) _maxIterations = 1000000;
|
||||
if (_maxIterations < 10) _maxIterations = 10;
|
||||
|
||||
TehNthPrime = _maxIterations;
|
||||
}
|
||||
|
||||
public override void SetData(IGH_DataAccess DA)
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
DA.SetData(0, $"Hello world. Worker {Id} has found for that the {TehNthPrime}th prime is: {ThePrime}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user