From d9cd1beca2009b9b27ff8f63b99ea368854f2397 Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Mon, 2 Nov 2015 17:40:15 +0100
Subject: [PATCH] refactoring: VectoRuns now use background worker, driving
 cycle has progress indicator, vectorun updates progress

---
 .../Models/Connector/Ports/ISimulationPort.cs |   1 +
 VectoCore/Models/Simulation/IVectoRun.cs      |   4 +-
 .../Models/Simulation/Impl/JobContainer.cs    | 115 +++++++++++++++++-
 VectoCore/Models/Simulation/Impl/VectoRun.cs  |  11 +-
 .../Impl/DistanceBasedDrivingCycle.cs         |  11 ++
 .../Impl/EngineOnlyDrivingCycle.cs            |   5 +
 .../Impl/TimeBasedDrivingCycle.cs             |   5 +
 .../Integration/FullCycleDeclarationTest.cs   |   4 +-
 8 files changed, 146 insertions(+), 10 deletions(-)

diff --git a/VectoCore/Models/Connector/Ports/ISimulationPort.cs b/VectoCore/Models/Connector/Ports/ISimulationPort.cs
index 77026657a8..a30199ffef 100644
--- a/VectoCore/Models/Connector/Ports/ISimulationPort.cs
+++ b/VectoCore/Models/Connector/Ports/ISimulationPort.cs
@@ -33,5 +33,6 @@ namespace TUGraz.VectoCore.Models.Connector.Ports
 		IResponse Request(Second absTime, Second dt);
 
 		IResponse Initialize();
+		double Progress { get; }
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/Models/Simulation/IVectoRun.cs b/VectoCore/Models/Simulation/IVectoRun.cs
index eb359549ab..63a7b080ad 100644
--- a/VectoCore/Models/Simulation/IVectoRun.cs
+++ b/VectoCore/Models/Simulation/IVectoRun.cs
@@ -1,3 +1,5 @@
+using System.ComponentModel;
+
 namespace TUGraz.VectoCore.Models.Simulation
 {
 	/// <summary>
@@ -8,7 +10,7 @@ namespace TUGraz.VectoCore.Models.Simulation
 		/// <summary>
 		/// Run the simulation.
 		/// </summary>
-		void Run();
+		void Run(BackgroundWorker worker = null);
 
 		string Name { get; }
 
diff --git a/VectoCore/Models/Simulation/Impl/JobContainer.cs b/VectoCore/Models/Simulation/Impl/JobContainer.cs
index d399e270a4..6edded3df1 100644
--- a/VectoCore/Models/Simulation/Impl/JobContainer.cs
+++ b/VectoCore/Models/Simulation/Impl/JobContainer.cs
@@ -1,5 +1,8 @@
+using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
+using System.Threading;
 using System.Threading.Tasks;
 using TUGraz.VectoCore.Models.Simulation.Data;
 
@@ -14,7 +17,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 	/// </summary>
 	public class JobContainer : LoggingObject
 	{
-		private readonly List<IVectoRun> _runs = new List<IVectoRun>();
+		internal readonly List<JobEntry> Runs = new List<JobEntry>();
 		private readonly SummaryFileWriter _sumWriter;
 
 		private static int _jobNumber;
@@ -31,32 +34,132 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 		public void AddRun(IVectoRun run)
 		{
 			_jobNumber++;
-			_runs.Add(run);
+			Runs.Add(new JobEntry() {
+				Run = run,
+				Container = this,
+			});
 		}
 
 		public void AddRuns(IEnumerable<IVectoRun> runs)
 		{
 			_jobNumber++;
-			_runs.AddRange(runs);
+			//Runs.AddRange(runs);
+			foreach (var run in runs) {
+				Runs.Add(new JobEntry() {
+					Run = run,
+					Container = this,
+				});
+			}
 		}
 
 		public void AddRuns(SimulatorFactory factory)
 		{
 			factory.SumWriter = _sumWriter;
 			factory.JobNumber = _jobNumber++;
-			_runs.AddRange(factory.SimulationRuns());
+			AddRuns(factory.SimulationRuns());
 		}
 
 		/// <summary>
 		/// Execute all runs, waits until finished.
 		/// </summary>
-		public void Execute()
+		public void Execute(bool multithreaded = true)
 		{
 			Log.Info("VectoRun started running. Executing Runs.");
 
-			Task.WaitAll(_runs.Select(r => Task.Factory.StartNew(r.Run)).ToArray());
+			foreach (var job in Runs) {
+				job.Worker = new BackgroundWorker() {
+					WorkerSupportsCancellation = true,
+					WorkerReportsProgress = true,
+				};
+				job.Worker.DoWork += job.DoWork;
+				job.Worker.ProgressChanged += job.ProgressChanged;
+				job.Worker.RunWorkerCompleted += job.RunWorkerCompleted;
+				if (multithreaded) {
+					job.Started = true;
+					job.Worker.RunWorkerAsync();
+				}
+			}
+			if (!multithreaded) {
+				var entry = Runs.First();
+				entry.Started = true;
+				entry.Worker.RunWorkerAsync();
+			}
+			//Task.WaitAll(_runs.Select(r => Task.Factory.StartNew(r.Run)).ToArray());
 
 			_sumWriter.Finish();
 		}
+
+		public void Cancel()
+		{
+			foreach (var job in Runs) {
+				if (job.Worker != null && job.Worker.WorkerSupportsCancellation) {
+					job.Worker.CancelAsync();
+				}
+			}
+		}
+
+		private static AutoResetEvent resetEvent = new AutoResetEvent(false);
+
+		public void WaitFinished()
+		{
+			resetEvent.WaitOne();
+		}
+
+
+		private void JobCompleted(JobEntry jobEntry)
+		{
+			var next = Runs.FirstOrDefault(x => x.Started == false);
+			if (next != null) {
+				next.Started = true;
+				next.Worker.RunWorkerAsync();
+			}
+			if (Runs.Count(x => x.Done == true) == Runs.Count()) {
+				_sumWriter.Finish();
+				resetEvent.Set();
+			}
+		}
+
+		public Dictionary<string, double> GetProgress()
+		{
+			return Runs.ToDictionary(jobEntry => jobEntry.Run.Name, jobEntry => jobEntry.Progress);
+		}
+
+		public bool AllCompleted
+		{
+			get { return (Runs.Count(x => x.Done == true) == Runs.Count()); }
+		}
+
+		internal class JobEntry
+		{
+			public IVectoRun Run;
+			public JobContainer Container;
+			public double Progress;
+			public bool Done;
+			public bool Started;
+			public bool Success;
+			public bool Canceled;
+
+			public BackgroundWorker Worker;
+
+			public void DoWork(object sender, DoWorkEventArgs e)
+			{
+				var worker = sender as BackgroundWorker;
+				Run.Run(worker);
+				if (worker != null && worker.CancellationPending) {
+					e.Cancel = true;
+					Canceled = true;
+				}
+				Success = Run.FinishedWithoutErrors;
+				Done = true;
+				Container.JobCompleted(this);
+			}
+
+			public void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {}
+
+			public void ProgressChanged(object sender, ProgressChangedEventArgs e)
+			{
+				Progress = e.ProgressPercentage / 1000.0;
+			}
+		}
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/Models/Simulation/Impl/VectoRun.cs b/VectoCore/Models/Simulation/Impl/VectoRun.cs
index d5e36a81ae..6280199125 100644
--- a/VectoCore/Models/Simulation/Impl/VectoRun.cs
+++ b/VectoCore/Models/Simulation/Impl/VectoRun.cs
@@ -1,4 +1,5 @@
 using System;
+using System.ComponentModel;
 using TUGraz.VectoCore.Exceptions;
 using TUGraz.VectoCore.Models.Connector.Ports;
 using TUGraz.VectoCore.Models.Connector.Ports.Impl;
@@ -37,7 +38,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 		}
 
 
-		public void Run()
+		public void Run(BackgroundWorker worker = null)
 		{
 			Log.Info("VectoJob started running.");
 
@@ -50,6 +51,14 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 						Container.CommitSimulationStep(AbsTime, dt);
 						AbsTime += dt;
 					}
+					if (worker != null) {
+						worker.ReportProgress((int)(CyclePort.Progress * 1000));
+						if (worker.CancellationPending) {
+							Log.Info("Background Task canceled!");
+							Container.FinishSimulation();
+							return;
+						}
+					}
 				} while (response is ResponseSuccess);
 			} catch (VectoSimulationException vse) {
 				Log.Error("SIMULATION RUN ABORTED! ========================");
diff --git a/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs b/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs
index f3c72f5885..fa9cdab03e 100644
--- a/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs
@@ -224,6 +224,17 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 				CycleIntervalIterator.LeftSample.RoadGradient);
 		}
 
+		public double Progress
+		{
+			get
+			{
+				return Data.Entries.Count > 0
+					? ((CurrentState.Distance - Data.Entries.First().Distance) /
+						(Data.Entries.Last().Distance - Data.Entries.First().Distance)).Value()
+					: 0;
+			}
+		}
+
 		public Meter CycleStartDistance
 		{
 			get { return Data.Entries.Count > 0 ? Data.Entries.First().Distance : 0.SI<Meter>(); }
diff --git a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyDrivingCycle.cs b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyDrivingCycle.cs
index 7f70161620..aa69f91b6b 100644
--- a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyDrivingCycle.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyDrivingCycle.cs
@@ -75,6 +75,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 			return NextComponent.Initialize(Data.Entries[index].EngineTorque, Data.Entries[index].EngineSpeed);
 		}
 
+		public double Progress
+		{
+			get { throw new NotImplementedException(); }
+		}
+
 		public Meter StartDistance
 		{
 			get { throw new NotImplementedException(); }
diff --git a/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs b/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs
index 49c2374a05..e22232b233 100644
--- a/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/TimeBasedDrivingCycle.cs
@@ -69,6 +69,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 			throw new NotImplementedException();
 		}
 
+		public double Progress
+		{
+			get { throw new NotImplementedException(); }
+		}
+
 		public Meter StartDistance
 		{
 			get { return 0.SI<Meter>(); }
diff --git a/VectoCoreTest/Integration/FullCycleDeclarationTest.cs b/VectoCoreTest/Integration/FullCycleDeclarationTest.cs
index 317cb31e09..c09ae48552 100644
--- a/VectoCoreTest/Integration/FullCycleDeclarationTest.cs
+++ b/VectoCoreTest/Integration/FullCycleDeclarationTest.cs
@@ -131,8 +131,8 @@ namespace TUGraz.VectoCore.Tests.Integration
 
 			jobContainer.Execute();
 
-			foreach (var run in jobContainer._runs) {
-				Assert.IsTrue(run.FinishedWithoutErrors);
+			foreach (var run in jobContainer.Runs) {
+				Assert.IsTrue(run.Run.FinishedWithoutErrors);
 			}
 		}
 	}
-- 
GitLab