From 884d90c739b9aa66053d7dc08a76a848811d587c Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Fri, 30 Sep 2016 10:15:58 +0200
Subject: [PATCH] vecto gui: open engine-only jobs, validate engine-only jobs
 on save; validate cycle data

---
 VECTO/GUI/VectoJobForm.vb                     | 22 +++++++++-
 VECTO/Input Files/Gearbox.vb                  |  6 +--
 VECTO/Input Files/VectoJob.vb                 | 24 +++++++++++
 VectoCommon/VectoCommon/Utils/Validation.cs   | 10 +++--
 .../Simulation/Impl/SimulatorFactory.cs       |  2 +-
 .../Data/DrivingCycleData.cs                  | 41 +++++++++++++++++++
 6 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/VECTO/GUI/VectoJobForm.vb b/VECTO/GUI/VectoJobForm.vb
index e1adc35097..fa1cf36958 100644
--- a/VECTO/GUI/VectoJobForm.vb
+++ b/VECTO/GUI/VectoJobForm.vb
@@ -398,6 +398,23 @@ Public Class VectoJobForm
 		_basePath = Path.GetDirectoryName(file)
 		'Update Form
 
+		If inputData.JobInputData().EngineOnlyMode Then
+			TbENG.Text = GetRelativePath(inputData.EngineInputData.Source, _basePath)
+			CbEngOnly.Checked = True
+			Try
+				Dim sb As ICycleData
+				For Each sb In vectoJob.Cycles
+					Dim lv0 As ListViewItem = New ListViewItem
+					lv0.Text = GetRelativePath(sb.CycleData.Source, Path.GetDirectoryName(Path.GetFullPath(file))) 'sb.Name
+					LvCycles.Items.Add(lv0)
+				Next
+			Catch ex As Exception
+			End Try
+			CheckEngOnly()
+			Exit Sub
+		End If
+		CbEngOnly.Checked = False
+		CheckEngOnly()
 		'Files -----------------------------
 		TbVEH.Text = GetRelativePath(inputData.VehicleInputData.Source, _basePath)
 		TbENG.Text = GetRelativePath(inputData.EngineInputData.Source, _basePath)
@@ -480,8 +497,7 @@ Public Class VectoJobForm
 			Next
 		Catch ex As Exception
 		End Try
-		CbEngOnly.Checked = vectoJob.EngineOnlyMode
-
+		
 		If driver.OverSpeedEcoRoll.Mode = DriverMode.EcoRoll Then
 			RdEcoRoll.Checked = True
 		ElseIf driver.OverSpeedEcoRoll.Mode = DriverMode.Overspeed Then
@@ -528,6 +544,8 @@ Public Class VectoJobForm
 		'-------------------------------------------------------------
 	End Sub
 
+
+
 	'Save file
 	Private Function VECTOsave(file As String) As Boolean
 		Dim message As String = String.Empty
diff --git a/VECTO/Input Files/Gearbox.vb b/VECTO/Input Files/Gearbox.vb
index 44c79d2cb5..34825a8c40 100644
--- a/VECTO/Input Files/Gearbox.vb	
+++ b/VECTO/Input Files/Gearbox.vb	
@@ -393,7 +393,7 @@ Public Class Gearbox
 				Dim gearDict As New TransmissionInputData With {
 						.Ratio = GearRatios(i)
 						}
-				If File.Exists(GearshiftFiles(i).OriginalPath) Then
+				If File.Exists(GearshiftFiles(i).FullPath) Then
 					gearDict.ShiftPolygon = VectoCSVFile.Read(GearshiftFiles(i).FullPath)
 				End If
 				If Not String.IsNullOrWhiteSpace(MaxTorque(i)) AndAlso IsNumeric(MaxTorque(i)) Then
@@ -432,7 +432,7 @@ Public Class Gearbox
 
 	Public ReadOnly Property ShiftPolygon As TableData Implements ITorqueConverterEngineeringInputData.ShiftPolygon
 		Get
-			Return VectoCSVFile.Read(TorqueConverterShiftPolygonFile)
+			Return VectoCSVFile.Read(Path.Combine(_myPath, TorqueConverterShiftPolygonFile))
 		End Get
 	End Property
 
@@ -507,7 +507,7 @@ Public Class Gearbox
 
 	Public ReadOnly Property TCData As TableData Implements ITorqueConverterDeclarationInputData.TCData
 		Get
-			Return VectoCSVFile.Read(_torqueConverterFile.OriginalPath)
+			Return VectoCSVFile.Read(_torqueConverterFile.FullPath)
 		End Get
 	End Property
 
diff --git a/VECTO/Input Files/VectoJob.vb b/VECTO/Input Files/VectoJob.vb
index deaeed50a9..7d0837fde7 100644
--- a/VECTO/Input Files/VectoJob.vb	
+++ b/VECTO/Input Files/VectoJob.vb	
@@ -614,6 +614,30 @@ Public Class VectoJob
 																	ExecutionModeServiceContainer)
 		Dim mode As ExecutionMode = If(modeService Is Nothing, ExecutionMode.Declaration, modeService.Mode)
 
+		If mode = ExecutionMode.Engineering AndAlso vectoJob.EngineOnly Then
+			Return ValidateEngineOnlyJob(vectoJob, mode)
+		End If
+
+		Return ValidateVehicleJob(vectoJob, mode)
+	End Function
+
+	Private Shared Function ValidateEngineOnlyJob(vectoJob As VectoJob, executionMode As ExecutionMode) As ValidationResult
+		Dim result As IList(Of ValidationResult) = New List(Of ValidationResult)
+
+		vectoJob._engineInputData = New JSONComponentInputData(vectoJob._engineFile.FullPath)
+
+		If vectoJob._engineInputData.EngineInputData Is Nothing Then _
+			result.Add(New ValidationResult("Engine File is missing or invalid"))
+		If result.Any() Then
+			Return _
+				New ValidationResult("Vecto Job Configuration is invalid. ", result.Select(Function(r) r.ErrorMessage).ToList())
+		End If
+
+		Return ValidationResult.Success
+	End Function
+
+	Private Shared Function ValidateVehicleJob(vectoJob As VectoJob, mode As ExecutionMode) As ValidationResult
+
 		Dim jobData As IEnumerable(Of VectoRunData)
 
 		vectoJob._vehicleInputData = New JSONComponentInputData(vectoJob._vehicleFile.FullPath)
diff --git a/VectoCommon/VectoCommon/Utils/Validation.cs b/VectoCommon/VectoCommon/Utils/Validation.cs
index 2ab037be88..7a81b6f9b5 100644
--- a/VectoCommon/VectoCommon/Utils/Validation.cs
+++ b/VectoCommon/VectoCommon/Utils/Validation.cs
@@ -54,8 +54,9 @@ namespace TUGraz.VectoCommon.Utils
 		/// <returns>Null, if the validation was successfull. Otherwise a list of ValidationResults with the ErrorMessages.</returns>
 		public static IList<ValidationResult> Validate<T>(this T entity, ExecutionMode mode)
 		{
-			if (entity == null)
-				return  new[] { new ValidationResult(string.Format("null value given for {0}", typeof(T)))};
+			if (entity == null) {
+				return new[] { new ValidationResult(string.Format("null value given for {0}", typeof(T))) };
+			}
 			var context = new ValidationContext(entity);
 			context.ServiceContainer.AddService(typeof(ExecutionMode), new ExecutionModeServiceContainer(mode));
 			var results = new List<ValidationResult>();
@@ -200,11 +201,12 @@ namespace TUGraz.VectoCommon.Utils
 				if (!results.Any()) {
 					return ValidationResult.Success;
 				}
+				var messages = results.Select(r => String.Join(", ", r.MemberNames.Distinct()));
 				if (validationContext.MemberName == "Container" || validationContext.MemberName == "RunData") {
-					return new ValidationResult(string.Join("\n", results));
+					return new ValidationResult(string.Join("\n", results), messages);
 				}
 				return new ValidationResult(
-					string.Format("{{{0}}} invalid: {1}", validationContext.DisplayName, string.Join("\n", results)));
+					string.Format("{{{0}}} invalid: {1}", validationContext.DisplayName, string.Join("\n", results)), messages);
 			}
 
 			return ValidationResult.Success;
diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
index 8a0c5358ff..a7bcc05b48 100644
--- a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
+++ b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
@@ -161,7 +161,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 				var validationErrors = run.Validate(_mode);
 				if (validationErrors.Any()) {
 					throw new VectoException("Validation of Run-Data Failed: " +
-											string.Join("\n", validationErrors.Select(r => r.ErrorMessage)));
+											string.Join("\n", validationErrors.Select(r => r.ErrorMessage + string.Join("; ", r.MemberNames))));
 				}
 
 				yield return run;
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs
index cfbca148fa..6224c73025 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs
@@ -31,8 +31,12 @@
 
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Diagnostics;
+using System.Linq;
+using TUGraz.VectoCommon.Models;
 using TUGraz.VectoCommon.Utils;
+using TUGraz.VectoCore.Models.Simulation.Data;
 
 namespace TUGraz.VectoCore.Models.SimulationComponent.Data
 {
@@ -54,6 +58,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
 		}
 	}
 
+	[CustomValidation(typeof(DrivingCycleData), "ValidateCycleData")]
 	public class DrivingCycleData : SimulationComponentData
 	{
 		internal DrivingCycleData() {}
@@ -64,6 +69,42 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
 
 		public CycleType CycleType { get; internal set; }
 
+		// ReSharper disable once UnusedMember.Global -- used by Validation
+		public static ValidationResult ValidateCycleData(DrivingCycleData cycleData, ValidationContext validationContext)
+		{
+			var mode = GetExecutionMode(validationContext);
+			if (mode == ExecutionMode.Declaration) {
+				return ValidationResult.Success;
+			}
+
+			var result = new List<string>();
+			if (cycleData.CycleType.IsDistanceBased()) {
+				var cur = cycleData.Entries[0].Distance;
+				for (var i = 1; i < cycleData.Entries.Count; i++) {
+					if (cycleData.Entries[i].Distance < cur) {
+						result.Add(
+							string.Format("distance-based cycle is not increasing strictly monotonous. entry: {0}, s_{1}: {2} s_{0}: {3}", i,
+								i - 1, cycleData.Entries[i - 1].Distance, cycleData.Entries[i].Distance));
+					}
+					cur = cycleData.Entries[i].Distance;
+				}
+			} else {
+				var cur = cycleData.Entries[0].Time;
+				for (var i = 1; i < cycleData.Entries.Count; i++) {
+					if (cycleData.Entries[i].Time < cur) {
+						result.Add(
+							string.Format("time-based cycle is not increasing strictly monotonous. entry: {0}, t_{1}: {2} t_{0}: {3}", i,
+								i - 1, cycleData.Entries[i - 1].Time, cycleData.Entries[i].Time));
+					}
+					cur = cycleData.Entries[i].Time;
+				}
+			}
+			if (result.Any()) {
+				return new ValidationResult(string.Format("Validation of Cycle {0} failed", cycleData.Name), result);
+			}
+			return ValidationResult.Success;
+		}
+
 		[DebuggerDisplay(
 			"s:{Distance}, t:{Time}, v:{VehicleTargetSpeed}, grad:{RoadGradient}, n:{AngularVelocity}, gear:{Gear}")]
 		public class DrivingCycleEntry
-- 
GitLab