feat(multiple runs): adds iteration handling to async component base

nifty data matching is now in place

fixes #1
This commit is contained in:
Dimitrie Stefanescu
2020-10-07 13:35:04 +01:00
parent cf889e7f2c
commit b88c61fe88
3 changed files with 166 additions and 65 deletions
@@ -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}");
}
}
}