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

Last change on this file since 1684 was 1684, checked in by kopal, 11 years ago
  • error messages or warning messages now lead to coloring of the plugins view (currently color stays then) - plugins with error messages will not be executed again in the current run of the chain
  • a plugin now will be executed if all mandatory connectors are set (regardless if other connectors have data)
  • corrected calls of plugin methods (Execute, PreExecution...) according to state diagram of https://www.cryptool.org/trac/CrypTool2/wiki/IPluginHints
  • bug fix in view: PluginContainerView now does not flash between icon and Presentation if it is resized
File size: 13.6 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                //we also initalize each plugin
107                int counter=0;
108                foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
109                {
110                    pluginModel.Plugin.PreExecution();
111                    PluginProtocol pluginProtocol = new PluginProtocol(schedulers[counter], pluginModel,this);
112                    pluginModel.PluginProtocol = pluginProtocol;
113                    schedulers[counter].AddProtocol(pluginProtocol);
114                   
115                    pluginProtocol.Start();
116                    counter = (counter + 1) % (System.Environment.ProcessorCount*2);
117
118                    if (pluginModel.Startable)
119                    {
120                        MessageExecution msg = new MessageExecution();
121                        msg.PluginModel = pluginModel;
122                        pluginProtocol.BroadcastMessageReliably(msg);
123                    }
124                }
125            }
126        }     
127     
128        /// <summary>
129        /// Stop the execution process:
130        /// calls shutdown on all schedulers + calls stop() on each plugin
131        /// </summary>
132        public void Stop()
133        {
134            //First stop alle plugins
135            foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
136            {
137                pluginModel.Plugin.Stop();
138                pluginModel.Plugin.PostExecution();
139            }           
140
141            IsRunning = false;
142            //Secondly stop all Gears4Net Schedulers
143            foreach (Scheduler scheduler in schedulers)
144            {
145                scheduler.Shutdown();
146            }
147             
148        }
149
150        /// <summary>
151        /// Pause the execution
152        /// </summary>
153        public void Pause()
154        {
155            //not implemented yet
156        }
157
158        /// <summary>
159        /// Use the logger of the WorkspaceManagerEditor
160        /// </summary>
161        /// <param name="message"></param>
162        /// <param name="level"></param>
163        public void GuiLogMessage(string message, NotificationLevel level)
164        {           
165            WorkspaceManagerEditor.GuiLogMessage(message, level);
166        }           
167    }
168 
169    /// <summary>
170    /// Message send to scheduler for a Plugin to trigger the Execution
171    /// </summary>
172    public class MessageExecution : MessageBase
173    {
174        public PluginModel PluginModel;
175    }
176
177    /// <summary>
178    /// A Protocol for updating the GUI in time intervals
179    /// </summary>
180    public class UpdateGuiProtocol : ProtocolBase
181    {
182        private WorkspaceModel workspaceModel;
183        private ExecutionEngine executionEngine;     
184
185        /// <summary>
186        /// Create a new protocol. Each protocol requires a scheduler which provides
187        /// a thread for execution.
188        /// </summary>
189        /// <param name="scheduler"></param>
190        public UpdateGuiProtocol(Scheduler scheduler, WorkspaceModel workspaceModel, ExecutionEngine executionEngine)
191            : base(scheduler)
192        {
193            this.workspaceModel = workspaceModel;
194            this.executionEngine = executionEngine;           
195        }
196
197        /// <summary>
198        /// The main function of the protocol
199        /// </summary>
200        /// <param name="stateMachine"></param>
201        /// <returns></returns>
202        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
203        {
204            while (this.executionEngine.IsRunning)
205            {
206                yield return Timeout(this.executionEngine.GuiUpdateInterval, HandleUpdateGui);
207            }
208        }
209
210        /// <summary>
211        /// Handler function for a message.
212        /// This handler must not block, because it executes inside the thread of the scheduler.
213        /// </summary>
214        /// <param name="msg"></param>
215        private void HandleUpdateGui()
216        {
217            //Get the gui Thread
218            this.workspaceModel.WorkspaceManagerEditor.Presentation.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
219            {
220                foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
221                {
222                    if (pluginModel.GuiNeedsUpdate)
223                    {
224                        //executionEngine.GuiLogMessage("UpdateGui for \"" + pluginModel.Name + "\"", NotificationLevel.Debug);
225                        pluginModel.GuiNeedsUpdate = false;
226                        pluginModel.paint();
227                        if (pluginModel.UpdateableView != null)
228                        {
229                            pluginModel.UpdateableView.update();
230                        }
231                    }
232                }
233                foreach (ConnectionModel connectionModel in workspaceModel.AllConnectionModels)
234                {
235                    if (connectionModel.GuiNeedsUpdate)
236                    {
237                        if (connectionModel.UpdateableView != null)
238                        {
239                            connectionModel.UpdateableView.update();
240                        }
241                    }
242                }
243            }
244            , null);
245        }
246    }
247
248   
249    /// <summary>
250    /// A Protocol for benchmarking
251    /// </summary>
252    public class BenchmarkProtocol : ProtocolBase
253    {
254        private WorkspaceModel workspaceModel;
255        private ExecutionEngine executionEngine;
256
257        /// <summary>
258        /// Create a new protocol. Each protocol requires a scheduler which provides
259        /// a thread for execution.
260        /// </summary>
261        /// <param name="scheduler"></param>
262        public BenchmarkProtocol(Scheduler scheduler, WorkspaceModel workspaceModel, ExecutionEngine executionEngine)
263            : base(scheduler)
264        {
265            this.workspaceModel = workspaceModel;
266            this.executionEngine = executionEngine;
267        }
268
269        /// <summary>
270        /// The main function of the protocol
271        /// </summary>
272        /// <param name="stateMachine"></param>
273        /// <returns></returns>
274        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
275        {
276            while (this.executionEngine.IsRunning)
277            {
278                yield return Timeout(1000, HandleBenchmark);
279            }
280        }
281
282        /// <summary>
283        /// Handler function for a message.
284        /// This handler must not block, because it executes inside the thread of the scheduler.
285        /// </summary>
286        /// <param name="msg"></param>
287        private void HandleBenchmark()
288        {
289            this.workspaceModel.WorkspaceManagerEditor.GuiLogMessage("Executing at about " + this.executionEngine.ExecutedPluginsCounter + " Plugins/s", NotificationLevel.Debug);
290            this.executionEngine.ExecutedPluginsCounter = 0;
291        }
292
293    }
294
295    /// <summary>
296    /// A Protocol for a PluginModel
297    /// </summary>
298    public class PluginProtocol : ProtocolBase
299    {
300        private PluginModel pluginModel;
301        private ExecutionEngine executionEngine;
302
303        /// <summary>
304        /// Create a new protocol. Each protocol requires a scheduler which provides
305        /// a thread for execution.
306        /// </summary>
307        /// <param name="scheduler"></param>
308        public PluginProtocol(Scheduler scheduler, PluginModel pluginModel,ExecutionEngine executionEngine)
309            : base(scheduler)
310        {
311            this.pluginModel = pluginModel;
312            this.executionEngine = executionEngine;
313        }
314
315        /// <summary>
316        /// The main function of the protocol     
317        /// </summary>
318        /// <param name="stateMachine"></param>
319        /// <returns></returns>
320        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
321        {
322            while (this.executionEngine.IsRunning)
323            {
324                yield return Receive<MessageExecution>(null, this.HandleExecute);             
325            }
326        }
327
328        /// <summary>
329        /// Call the execution function of the wrapped IPlugin
330        /// </summary>
331        /// <param name="msg"></param>
332        private void HandleExecute(MessageExecution msg)
333        {
334           
335            //executionEngine.GuiLogMessage("HandleExecute for \"" + msg.PluginModel.Name + "\"", NotificationLevel.Debug);
336            //Fill the plugins Inputs with data
337            foreach (ConnectorModel connectorModel in pluginModel.InputConnectors)
338            {
339                if (connectorModel.HasData)
340                {
341                    if (connectorModel.IsDynamic)
342                    {
343                        MethodInfo propertyInfo = pluginModel.Plugin.GetType().GetMethod(connectorModel.DynamicSetterName);
344                        propertyInfo.Invoke(pluginModel.Plugin, new object[]{connectorModel.PropertyName, connectorModel.Data});
345                    }
346                    else
347                    {
348                        PropertyInfo propertyInfo = pluginModel.Plugin.GetType().GetProperty(connectorModel.PropertyName);
349                        propertyInfo.SetValue(pluginModel.Plugin, connectorModel.Data, null);
350                    }
351                }
352            }
353           
354            msg.PluginModel.Plugin.Execute();           
355
356            if (this.executionEngine.BenchmarkPlugins)
357            {
358                this.executionEngine.ExecutedPluginsCounter++;                               
359            }
360        }
361     
362    }
363}
Note: See TracBrowser for help on using the repository browser.