diff --git a/GrasshopperAsyncComponent/Base/GH_AsyncComponent.cs b/GrasshopperAsyncComponent/Base/GH_AsyncComponent.cs index d3bec08..569a7a6 100644 --- a/GrasshopperAsyncComponent/Base/GH_AsyncComponent.cs +++ b/GrasshopperAsyncComponent/Base/GH_AsyncComponent.cs @@ -65,7 +65,6 @@ namespace GrasshopperAsyncComponent { Rhino.RhinoApp.InvokeOnUiThread((Action)delegate { - //State = 1; ExpireSolution(true); }); } @@ -116,6 +115,7 @@ namespace GrasshopperAsyncComponent // Create the task var tokenSource = new CancellationTokenSource(); CurrentWorker.CancellationToken = tokenSource.Token; + CurrentWorker.Id = DA.Iteration.ToString(); var CurrentRun = new Task(() => CurrentWorker.DoWork(ReportProgress, ReportError, Done), tokenSource.Token, TaskCreationOptions.LongRunning); // Add cancelation source to our bag @@ -131,6 +131,7 @@ namespace GrasshopperAsyncComponent Workers[DA.Iteration].SetData(DA); + // Note we're decrementing the state here. if (--State == 0) { foreach (var (message, type) in Errors) diff --git a/GrasshopperAsyncComponent/Base/IAsyncComponentWorker.cs b/GrasshopperAsyncComponent/Base/IAsyncComponentWorker.cs index ed7f5a4..a470595 100644 --- a/GrasshopperAsyncComponent/Base/IAsyncComponentWorker.cs +++ b/GrasshopperAsyncComponent/Base/IAsyncComponentWorker.cs @@ -8,51 +8,48 @@ using System.Threading.Tasks; namespace GrasshopperAsyncComponent { - // TODO: Would an an abstract class be better here? - public interface IAsyncComponentWorker - { - - /// - /// This function should return a duplicate instance of your class. Make sure any state is duplicated (or not) properly. - /// - /// - IAsyncComponentWorker GetNewInstance(); - - /// - /// Here you can safely set the data of your component, just like you would normally. Important: do not call this method directly! When you are ready, call the provided "Done" action from the DoWork function. - /// - /// - void SetData(IGH_DataAccess DA); - - /// - /// Here you can safely collect the data from your component, just like you would normally. Important: do not call this method directly. It will be invoked by the parent component. - /// - /// The magic Data Access class. - /// The parameters list, in case you need it. - void CollectData(IGH_DataAccess DA, GH_ComponentParamServer Params); - - /// - /// This where the computation happens. Make sure to check and return if the token is cancelled! - /// - /// The cancellation token. - /// Call this action to report progress. It will be displayed in the component's message bar. - /// Call this to report a warning or an error. - /// When you are done computing, call this function to have the parent component invoke the SetData function. - void DoWork(CancellationToken token, Action ReportProgress, Action ReportError, Action Done); - } - + + /// + /// A class that holds the actual compute logic and encapsulates the state it needs. Every needs to have one. + /// public abstract class WorkerInstance { + /// + /// This token is set by the parent . + /// public CancellationToken CancellationToken { get; set; } + /// + /// This is set by the parent . You can set it yourself, but it's not really worth it. + /// public string Id { get; set; } + /// + /// This is a "factory" method. It should return a fresh instance of this class, but with all the necessary state that you might have passed on directly from your component. + /// + /// public abstract WorkerInstance Duplicate(); - + + /// + /// This method is where the actual calculation/computation/heavy lifting should be done. + /// Make sure you always check as frequently as you can if is cancelled. For an example, see the . + /// + /// Call this to report progress up to the parent component. + /// Call this to report errors up to the parent component. + /// Call this when everything is done. It will tell the parent component that you're ready to . public abstract void DoWork(Action ReportProgress, Action ReportError, Action Done); + /// + /// Write your data setting logic here. Do not call this function directly from this class. It will be invoked by the parent after you've called `Done` in the function. + /// + /// public abstract void SetData(IGH_DataAccess DA); + /// + /// Write your data collection logic here. Do not call this method directly. It will be invoked by the parent . + /// + /// + /// public abstract void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params); } diff --git a/GrasshopperAsyncComponent/SampleImplementations/SampleAsyncComponent.cs b/GrasshopperAsyncComponent/SampleImplementations/SampleAsyncComponent.cs index 0e24ccb..05e7703 100644 --- a/GrasshopperAsyncComponent/SampleImplementations/SampleAsyncComponent.cs +++ b/GrasshopperAsyncComponent/SampleImplementations/SampleAsyncComponent.cs @@ -87,6 +87,7 @@ namespace GrasshopperAsyncComponent.SampleImplementations int count = 0; long a = 2; + // Thanks Steak Overflow (TM) https://stackoverflow.com/a/13001749/ while (count < TehNthPrime) { if (CancellationToken.IsCancellationRequested) return;