#region License Information
/* HeuristicLab
* Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Threading;
namespace HeuristicLab.Core {
///
/// Base class to represent an engine, which is an interpreter, holding the code, the data and
/// the actual state, which is the runtime stack and a pointer onto the next operation. It represents
/// one execution and can handle parallel executions.
///
public abstract class EngineBase : ItemBase, IEngine {
///
/// Field of the current instance that represent the operator graph.
///
protected IOperatorGraph myOperatorGraph;
///
/// Gets the current operator graph.
///
public IOperatorGraph OperatorGraph {
get { return myOperatorGraph; }
}
///
/// Field of the current instance that represent the global scope.
///
protected IScope myGlobalScope;
///
/// Gets the current global scope.
///
public IScope GlobalScope {
get { return myGlobalScope; }
}
private TimeSpan myExecutionTime;
///
/// Gets or sets the execution time.
///
/// Calls in the setter.
public TimeSpan ExecutionTime {
get { return myExecutionTime; }
protected set {
myExecutionTime = value;
OnExecutionTimeChanged();
}
}
///
/// Field of the current instance that represent the execution stack.
///
protected Stack myExecutionStack;
///
/// Gets the current execution stack.
///
public Stack ExecutionStack {
get { return myExecutionStack; }
}
///
/// Flag of the current instance whether it is currently running.
///
protected bool myRunning;
///
/// Gets information whether the instance is currently running.
///
public bool Running {
get { return myRunning; }
}
///
/// Flag of the current instance whether it is canceled.
///
protected bool myCanceled;
///
/// Gets information whether the instance is currently canceled.
///
public bool Canceled {
get { return myCanceled; }
}
///
/// Gets information whether the instance has already terminated.
///
public virtual bool Terminated {
get { return ExecutionStack.Count == 0; }
}
///
/// Initializes a new instance of with a new global scope.
///
/// Calls .
protected EngineBase() {
myOperatorGraph = new OperatorGraph();
myGlobalScope = new Scope("Global");
myExecutionStack = new Stack();
Reset();
}
///
/// Clones the current instance (deep clone).
///
/// Deep clone through method of helper class
/// .
/// Dictionary of all already clone objects. (Needed to avoid cycles.)
/// The cloned object as .
public override object Clone(IDictionary clonedObjects) {
EngineBase clone = (EngineBase)base.Clone(clonedObjects);
clone.myOperatorGraph = (IOperatorGraph)Auxiliary.Clone(OperatorGraph, clonedObjects);
clone.myGlobalScope = (IScope)Auxiliary.Clone(GlobalScope, clonedObjects);
clone.myExecutionTime = ExecutionTime;
IOperation[] operations = new IOperation[ExecutionStack.Count];
ExecutionStack.CopyTo(operations, 0);
for (int i = operations.Length - 1; i >= 0; i--)
clone.myExecutionStack.Push((IOperation)Auxiliary.Clone(operations[i], clonedObjects));
clone.myRunning = Running;
clone.myCanceled = Canceled;
return clone;
}
///
/// Calls
/// of class .
public virtual void Execute() {
myRunning = true;
myCanceled = false;
ThreadPool.QueueUserWorkItem(new WaitCallback(Run), null);
}
///
/// Calls
/// of class .
public virtual void ExecuteSteps(int steps) {
myRunning = true;
myCanceled = false;
ThreadPool.QueueUserWorkItem(new WaitCallback(Run), steps);
}
///
/// Calls
/// of class .
public void ExecuteStep() {
ExecuteSteps(1);
}
///
/// Sets the protected flag myCanceled to true.
public virtual void Abort() {
myCanceled = true;
}
///
/// Sets myCanceled and myRunning to false. The global scope is cleared,
/// the execution time is reseted, the execution stack is cleared and a new
/// with the initial operator is added.
/// Calls .
public virtual void Reset() {
myCanceled = false;
myRunning = false;
GlobalScope.Clear();
ExecutionTime = new TimeSpan();
myExecutionStack.Clear();
if (OperatorGraph.InitialOperator != null)
myExecutionStack.Push(new AtomicOperation(OperatorGraph.InitialOperator, GlobalScope));
OnInitialized();
}
private void Run(object state) {
if (state == null) Run();
else RunSteps((int)state);
myRunning = false;
OnFinished();
}
private void Run() {
DateTime start = DateTime.Now;
DateTime end;
while ((!Canceled) && (!Terminated)) {
ProcessNextOperation();
end = DateTime.Now;
ExecutionTime += end - start;
start = end;
}
ExecutionTime += DateTime.Now - start;
}
private void RunSteps(int steps) {
DateTime start = DateTime.Now;
DateTime end;
int step = 0;
while ((!Canceled) && (!Terminated) && (step < steps)) {
ProcessNextOperation();
step++;
end = DateTime.Now;
ExecutionTime += end - start;
start = end;
}
ExecutionTime += DateTime.Now - start;
}
///
/// Performs the next operation.
///
protected abstract void ProcessNextOperation();
///
/// Occurs when the current instance is initialized.
///
public event EventHandler Initialized;
///
/// Fires a new Initialized event.
///
protected virtual void OnInitialized() {
if (Initialized != null)
Initialized(this, new EventArgs());
}
///
/// Occurs when an operation is executed.
///
public event EventHandler OperationExecuted;
///
/// Fires a new OperationExecuted event.
///
/// The operation that has been executed.
protected virtual void OnOperationExecuted(IOperation operation) {
if (OperationExecuted != null)
OperationExecuted(this, new OperationEventArgs(operation));
}
///
/// Occurs when an exception occured during the execution.
///
public event EventHandler ExceptionOccurred;
///
/// Aborts the execution and fires a new ExceptionOccurred event.
///
/// The exception that was thrown.
protected virtual void OnExceptionOccurred(Exception exception) {
Abort();
if (ExceptionOccurred != null)
ExceptionOccurred(this, new ExceptionEventArgs(exception));
}
///
/// Occurs when the execution time changed.
///
public event EventHandler ExecutionTimeChanged;
///
/// Fires a new ExecutionTimeChanged event.
///
protected virtual void OnExecutionTimeChanged() {
if (ExecutionTimeChanged != null)
ExecutionTimeChanged(this, new EventArgs());
}
///
/// Occurs when the execution is finished.
///
public event EventHandler Finished;
///
/// Fires a new Finished event.
///
protected virtual void OnFinished() {
if (Finished != null)
Finished(this, new EventArgs());
}
#region Persistence Methods
///
/// Saves the current instance as in the specified .
///
/// Calls of base class .
/// A quick overview how the single elements of the current instance are saved:
///
/// -
/// Operator graph:
/// Saved as a child node with the tag name OperatorGraph.
///
/// -
/// Global scope:
/// Saved as a child node with the tag name GlobalScope.
///
/// -
/// Execution stack:
/// A child node is created with the tag name ExecutionStack. Beyond this child node
/// all operations of the execution stack are saved as child nodes.
///
/// -
/// Execution time:
/// Saved as a child node with the tag name ExecutionTime, where the execution
/// time is saved as string in the node's inner text.
///
///
/// The (tag)name of the .
/// The where to save the data.
/// The dictionary of all already persisted objects. (Needed to avoid cycles.)
/// The saved .
public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary persistedObjects) {
XmlNode node = base.GetXmlNode(name, document, persistedObjects);
node.AppendChild(PersistenceManager.Persist("OperatorGraph", OperatorGraph, document, persistedObjects));
node.AppendChild(PersistenceManager.Persist("GlobalScope", GlobalScope, document, persistedObjects));
XmlNode stackNode = document.CreateNode(XmlNodeType.Element, "ExecutionStack", null);
IOperation[] operations = new IOperation[ExecutionStack.Count];
ExecutionStack.CopyTo(operations, 0);
for (int i = 0; i < operations.Length; i++)
stackNode.AppendChild(PersistenceManager.Persist(operations[i], document, persistedObjects));
node.AppendChild(stackNode);
XmlNode timeNode = document.CreateNode(XmlNodeType.Element, "ExecutionTime", null);
timeNode.InnerText = ExecutionTime.ToString();
node.AppendChild(timeNode);
return node;
}
///
/// Loads the persisted instance from the specified .
///
/// See to get information on how the instance must be saved.
/// Calls of base class .
/// The where the engine is saved.
/// The dictionary of all already restored objects.
/// (Needed to avoid cycles.)
public override void Populate(XmlNode node, IDictionary restoredObjects) {
base.Populate(node, restoredObjects);
myOperatorGraph = (IOperatorGraph)PersistenceManager.Restore(node.SelectSingleNode("OperatorGraph"), restoredObjects);
myGlobalScope = (IScope)PersistenceManager.Restore(node.SelectSingleNode("GlobalScope"), restoredObjects);
XmlNode stackNode = node.SelectSingleNode("ExecutionStack");
for (int i = stackNode.ChildNodes.Count - 1; i >= 0; i--)
myExecutionStack.Push((IOperation)PersistenceManager.Restore(stackNode.ChildNodes[i], restoredObjects));
XmlNode timeNode = node.SelectSingleNode("ExecutionTime");
myExecutionTime = TimeSpan.Parse(timeNode.InnerText);
}
#endregion
}
}