source: trunk/CrypPlugins/WorkspaceManager/Execution/ExecutionEngine.cs @ 1680

Last change on this file since 1680 was 1680, checked in by kopal, 11 years ago
  • Execution now is always triggered when one input changed and provides also old inputs
  • Added support for dynamic Connectors
  • some small bug fixing
File size: 13.5 KB
Line 
1/*                             
2   Copyright 2010 Nils Kopal, Viktor M.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.Collections.Generic;
19using System.Linq;
20using System.Text;
21
22using WorkspaceManager.Model;
23using System.Threading;
24using System.Collections;
25using Cryptool.PluginBase;
26using System.Reflection;
27using Gears4Net;
28using System.Windows.Threading;
29
30namespace WorkspaceManager.Execution
31{
32    /// <summary>
33    /// Engine to execute a model of the WorkspaceManager
34    /// This class needs a WorkspaceManager to be instantiated
35    /// To run an execution process it also needs a WorkspaceModel
36    ///
37    /// This class uses Gears4Net to execute the plugins
38    /// </summary>
39    public class ExecutionEngine
40    {
41        private WorkspaceManager WorkspaceManagerEditor;
42        private Scheduler[] schedulers;
43        private WorkspaceModel workspaceModel;
44
45        public long ExecutedPluginsCounter { get; set; }
46        public bool BenchmarkPlugins { get; set; }
47        public long GuiUpdateInterval { get; set; }
48
49        /// <summary>
50        /// Creates a new ExecutionEngine
51        /// </summary>
52        /// <param name="workspaceManagerEditor"></param>
53        public ExecutionEngine(WorkspaceManager workspaceManagerEditor)
54        {
55            WorkspaceManagerEditor = workspaceManagerEditor;
56        }
57
58        /// <summary>
59        /// Is this ExecutionEngine running?
60        /// </summary>
61        public bool IsRunning
62        {
63            get;
64            private set;
65        }
66
67        /// <summary>
68        /// Execute the given Model
69        /// </summary>
70        /// <param name="workspaceModel"></param>
71        public void Execute(WorkspaceModel workspaceModel)
72        {
73            this.workspaceModel = workspaceModel;
74
75            if (!IsRunning)
76            {
77                IsRunning = true;
78
79                //Here we create n = "ProcessorsCount * 2" Gears4Net schedulers
80                //We do this, because measurements showed that we get the best performance if we
81                //use this amount of schedulers
82                schedulers = new Scheduler[System.Environment.ProcessorCount*2];
83                for(int i=0;i<System.Environment.ProcessorCount*2;i++){
84                    schedulers[i] = new STAScheduler("Scheduler" + i);
85                }
86
87                //We have to reset all states of PluginModels, ConnectorModels and ConnectionModels:
88                workspaceModel.resetStates();
89
90                //The UpdateGuiProtocol is a kind of "daemon" which will update the view elements if necessary
91                UpdateGuiProtocol updateGuiProtocol = new UpdateGuiProtocol(schedulers[0], workspaceModel, this);
92                schedulers[0].AddProtocol(updateGuiProtocol);
93                updateGuiProtocol.Start();
94
95                //The BenchmarkProtocl counts the amount of executed plugins per seconds and writes this to debug
96                if (this.BenchmarkPlugins)
97                {
98                    BenchmarkProtocol benchmarkProtocol = new BenchmarkProtocol(schedulers[0], this.workspaceModel, this);
99                    schedulers[0].AddProtocol(benchmarkProtocol);
100                    benchmarkProtocol.Start();
101                }
102
103                //Here we create for each PluginModel an own PluginProtocol
104                //By using round-robin we give each protocol to another scheduler to gain
105                //a good average load balancing of the schedulers
106                int counter=0;
107                foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
108                {
109                    PluginProtocol pluginProtocol = new PluginProtocol(schedulers[counter], pluginModel,this);
110                    pluginModel.PluginProtocol = pluginProtocol;
111                    schedulers[counter].AddProtocol(pluginProtocol);
112                   
113                    pluginProtocol.Start();
114                    counter = (counter + 1) % (System.Environment.ProcessorCount*2);
115
116                    if (pluginModel.Startable)
117                    {
118                        MessageExecution msg = new MessageExecution();
119                        msg.PluginModel = pluginModel;
120                        pluginProtocol.BroadcastMessageReliably(msg);
121
122                    }
123                }
124            }
125        }     
126     
127        /// <summary>
128        /// Stop the execution process:
129        /// calls shutdown on all schedulers + calls stop() on each plugin
130        /// </summary>
131        public void Stop()
132        {
133            IsRunning = false;
134            //First stop all Gears4Net Schedulers
135            foreach (Scheduler scheduler in schedulers)
136            {
137                scheduler.Shutdown();
138            }
139            //Secondly stop alle plugins
140            foreach(PluginModel pluginModel in workspaceModel.AllPluginModels)
141            {
142                pluginModel.Plugin.Stop();
143            }           
144        }
145
146        /// <summary>
147        /// Pause the execution
148        /// </summary>
149        public void Pause()
150        {
151            //not implemented yet
152        }
153
154        /// <summary>
155        /// Use the logger of the WorkspaceManagerEditor
156        /// </summary>
157        /// <param name="message"></param>
158        /// <param name="level"></param>
159        public void GuiLogMessage(string message, NotificationLevel level)
160        {           
161            WorkspaceManagerEditor.GuiLogMessage(message, level);
162        }           
163    }
164 
165    /// <summary>
166    /// Message send to scheduler for a Plugin to trigger the Execution
167    /// </summary>
168    public class MessageExecution : MessageBase
169    {
170        public PluginModel PluginModel;
171    }
172
173    /// <summary>
174    /// A Protocol for updating the GUI in time intervals
175    /// </summary>
176    public class UpdateGuiProtocol : ProtocolBase
177    {
178        private WorkspaceModel workspaceModel;
179        private ExecutionEngine executionEngine;     
180
181        /// <summary>
182        /// Create a new protocol. Each protocol requires a scheduler which provides
183        /// a thread for execution.
184        /// </summary>
185        /// <param name="scheduler"></param>
186        public UpdateGuiProtocol(Scheduler scheduler, WorkspaceModel workspaceModel, ExecutionEngine executionEngine)
187            : base(scheduler)
188        {
189            this.workspaceModel = workspaceModel;
190            this.executionEngine = executionEngine;           
191        }
192
193        /// <summary>
194        /// The main function of the protocol
195        /// </summary>
196        /// <param name="stateMachine"></param>
197        /// <returns></returns>
198        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
199        {
200            while (this.executionEngine.IsRunning)
201            {
202                yield return Timeout(this.executionEngine.GuiUpdateInterval, HandleUpdateGui);
203            }
204        }
205
206        /// <summary>
207        /// Handler function for a message.
208        /// This handler must not block, because it executes inside the thread of the scheduler.
209        /// </summary>
210        /// <param name="msg"></param>
211        private void HandleUpdateGui()
212        {
213            //Get the gui Thread
214            this.workspaceModel.WorkspaceManagerEditor.Presentation.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
215            {
216                foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
217                {
218                    if (pluginModel.GuiNeedsUpdate)
219                    {
220                        //executionEngine.GuiLogMessage("UpdateGui for \"" + pluginModel.Name + "\"", NotificationLevel.Debug);
221                        pluginModel.GuiNeedsUpdate = false;
222                        pluginModel.paint();
223                        if (pluginModel.UpdateableView != null)
224                        {
225                            pluginModel.UpdateableView.update();
226                        }
227                    }
228                }
229                foreach (ConnectionModel connectionModel in workspaceModel.AllConnectionModels)
230                {
231                    if (connectionModel.GuiNeedsUpdate)
232                    {
233                        if (connectionModel.UpdateableView != null)
234                        {
235                            connectionModel.UpdateableView.update();
236                        }
237                    }
238                }
239            }
240            , null);
241        }
242    }
243
244   
245    /// <summary>
246    /// A Protocol for benchmarking
247    /// </summary>
248    public class BenchmarkProtocol : ProtocolBase
249    {
250        private WorkspaceModel workspaceModel;
251        private ExecutionEngine executionEngine;
252
253        /// <summary>
254        /// Create a new protocol. Each protocol requires a scheduler which provides
255        /// a thread for execution.
256        /// </summary>
257        /// <param name="scheduler"></param>
258        public BenchmarkProtocol(Scheduler scheduler, WorkspaceModel workspaceModel, ExecutionEngine executionEngine)
259            : base(scheduler)
260        {
261            this.workspaceModel = workspaceModel;
262            this.executionEngine = executionEngine;
263        }
264
265        /// <summary>
266        /// The main function of the protocol
267        /// </summary>
268        /// <param name="stateMachine"></param>
269        /// <returns></returns>
270        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
271        {
272            while (this.executionEngine.IsRunning)
273            {
274                yield return Timeout(1000, HandleBenchmark);
275            }
276        }
277
278        /// <summary>
279        /// Handler function for a message.
280        /// This handler must not block, because it executes inside the thread of the scheduler.
281        /// </summary>
282        /// <param name="msg"></param>
283        private void HandleBenchmark()
284        {
285            this.workspaceModel.WorkspaceManagerEditor.GuiLogMessage("Executing at about " + this.executionEngine.ExecutedPluginsCounter + " Plugins/s", NotificationLevel.Debug);
286            this.executionEngine.ExecutedPluginsCounter = 0;
287        }
288
289    }
290
291    /// <summary>
292    /// A Protocol for a PluginModel
293    /// </summary>
294    public class PluginProtocol : ProtocolBase
295    {
296        private PluginModel pluginModel;
297        private ExecutionEngine executionEngine;
298
299        /// <summary>
300        /// Create a new protocol. Each protocol requires a scheduler which provides
301        /// a thread for execution.
302        /// </summary>
303        /// <param name="scheduler"></param>
304        public PluginProtocol(Scheduler scheduler, PluginModel pluginModel,ExecutionEngine executionEngine)
305            : base(scheduler)
306        {
307            this.pluginModel = pluginModel;
308            this.executionEngine = executionEngine;
309        }
310
311        /// <summary>
312        /// The main function of the protocol     
313        /// </summary>
314        /// <param name="stateMachine"></param>
315        /// <returns></returns>
316        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
317        {
318            while (this.executionEngine.IsRunning)
319            {
320                yield return Receive<MessageExecution>(null, this.HandleExecute);             
321            }
322        }
323
324        /// <summary>
325        /// Call the execution function of the wrapped IPlugin
326        /// </summary>
327        /// <param name="msg"></param>
328        private void HandleExecute(MessageExecution msg)
329        {
330            msg.PluginModel.Plugin.PreExecution();
331
332            //executionEngine.GuiLogMessage("HandleExecute for \"" + msg.PluginModel.Name + "\"", NotificationLevel.Debug);
333            //Fill the plugins Inputs with data
334            foreach (ConnectorModel connectorModel in pluginModel.InputConnectors)
335            {
336                if (connectorModel.HasData)
337                {
338                    if (connectorModel.IsDynamic)
339                    {
340                        MethodInfo propertyInfo = pluginModel.Plugin.GetType().GetMethod(connectorModel.DynamicSetterName);
341                        propertyInfo.Invoke(pluginModel.Plugin, new object[]{connectorModel.PropertyName, connectorModel.Data});
342                    }
343                    else
344                    {
345                        PropertyInfo propertyInfo = pluginModel.Plugin.GetType().GetProperty(connectorModel.PropertyName);
346                        propertyInfo.SetValue(pluginModel.Plugin, connectorModel.Data, null);
347                    }
348                }
349            }
350           
351            msg.PluginModel.Plugin.Execute();
352
353            msg.PluginModel.Plugin.PostExecution();
354
355            if (this.executionEngine.BenchmarkPlugins)
356            {
357                this.executionEngine.ExecutedPluginsCounter++;                               
358            }
359        }
360     
361    }
362}
Note: See TracBrowser for help on using the repository browser.