From 1bf55295fcc73dd428d043a760400d60ac6b163e Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Mon, 7 May 2018 11:08:32 +0200
Subject: [PATCH] add logging of warnings in vtp cycle to report, adapt
 calculation of FC-Limits

---
 VectoCommon/VectoCommon/Utils/SI.cs           |   2 +
 .../Utils/SIConvertExtensionMethods.cs        | 226 +++++++++---------
 .../Models/Declaration/DeclarationData.cs     |   4 +-
 .../SimulationComponent/Impl/VTPCycle.cs      |  40 ++--
 .../VectoCore/OutputData/XML/XMLVTPReport.cs  |  41 ++++
 5 files changed, 183 insertions(+), 130 deletions(-)

diff --git a/VectoCommon/VectoCommon/Utils/SI.cs b/VectoCommon/VectoCommon/Utils/SI.cs
index ed8559e923..d8158aaf5e 100644
--- a/VectoCommon/VectoCommon/Utils/SI.cs
+++ b/VectoCommon/VectoCommon/Utils/SI.cs
@@ -894,6 +894,8 @@ namespace TUGraz.VectoCommon.Utils
 		private static readonly int[] Units = { 0, -2,2, 0, 0, 0, 0 };
 
 		private SpecificFuelConsumption(double val) : base(val, Units) { }
+
+		public override string UnitString { get { return "kg/Ws"; } }
 	}
 
 	/// <summary>
diff --git a/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs b/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs
index 29d55faca6..17a38dc03b 100644
--- a/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs
+++ b/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs
@@ -34,16 +34,16 @@ using System.Globalization;
 
 namespace TUGraz.VectoCommon.Utils
 {
-    public class ConvertedSI
-    {
-        private readonly double _value;
-        public string Units { get; }
+	public class ConvertedSI
+	{
+		private readonly double _value;
+		public string Units { get; }
 
-        public ConvertedSI(double value, string units)
-        {
-            _value = value;
-            Units = units;
-        }
+		public ConvertedSI(double value, string units)
+		{
+			_value = value;
+			Units = units;
+		}
 
 		public double Value { get { return _value; } }
 
@@ -71,25 +71,25 @@ namespace TUGraz.VectoCommon.Utils
 		}
 
 		public static implicit operator double(ConvertedSI self)
-        {
-            return self._value;
-        }
-
-        public static implicit operator ConvertedSI(SI self)
-        {
-            return self == null ? null : new ConvertedSI(self.Value(), self.UnitString);
-        }
-
-        public override string ToString()
-        {
-            // todo mk2017-10-13: decimal places?
-            return _value.ToString(CultureInfo.InvariantCulture); // + " [" + _units + "]";
-        }
-
-        public object ToString(CultureInfo invariantCulture)
-        {
-            throw new NotImplementedException();
-        }
+		{
+			return self._value;
+		}
+
+		public static implicit operator ConvertedSI(SI self)
+		{
+			return self == null ? null : new ConvertedSI(self.Value(), self.UnitString);
+		}
+
+		public override string ToString()
+		{
+			// todo mk2017-10-13: decimal places?
+			return _value.ToString(CultureInfo.InvariantCulture); // + " [" + _units + "]";
+		}
+
+		public object ToString(CultureInfo invariantCulture)
+		{
+			throw new NotImplementedException();
+		}
 
 		public string ToOutputFormat(uint? decimals = null, double? outputFactor = null, bool? showUnit = null)
 		{
@@ -106,27 +106,27 @@ namespace TUGraz.VectoCommon.Utils
 		}
 	}
 
-    public static class SIConvertExtensionMethods
-    {
-        private const int Kilo = 1000;
-        private const int SecondsPerHour = 60 * 60;
-        
-        public static ConvertedSI ConvertToGramm(this Kilogram value)
-        {
-            return new ConvertedSI(value.Value() * Kilo, "g");
-        }
-        public static ConvertedSI ConvertToTon(this Kilogram value)
-        {
-            return new ConvertedSI(value.Value() / Kilo, "t");
-        }
-        public static ConvertedSI ConvertToKiloMeterPerHour(this MeterPerSecond value)
-        {
-            return new ConvertedSI(value.Value() * SecondsPerHour / Kilo, "km/h");
-        }
-        public static ConvertedSI ConvertToGrammPerKiloMeter(this KilogramPerMeter value)
-        {
-            return value == null ? null : new ConvertedSI(value.Value() * Kilo * Kilo, "g/km");
-        }
+	public static class SIConvertExtensionMethods
+	{
+		private const int Kilo = 1000;
+		private const int SecondsPerHour = 60 * 60;
+		
+		public static ConvertedSI ConvertToGramm(this Kilogram value)
+		{
+			return new ConvertedSI(value.Value() * Kilo, "g");
+		}
+		public static ConvertedSI ConvertToTon(this Kilogram value)
+		{
+			return new ConvertedSI(value.Value() / Kilo, "t");
+		}
+		public static ConvertedSI ConvertToKiloMeterPerHour(this MeterPerSecond value)
+		{
+			return new ConvertedSI(value.Value() * SecondsPerHour / Kilo, "km/h");
+		}
+		public static ConvertedSI ConvertToGrammPerKiloMeter(this KilogramPerMeter value)
+		{
+			return value == null ? null : new ConvertedSI(value.Value() * Kilo * Kilo, "g/km");
+		}
 
 		public static ConvertedSI ConvertToGramPerKiloWattHour(this SpecificFuelConsumption value)
 		{
@@ -135,72 +135,72 @@ namespace TUGraz.VectoCommon.Utils
 
 
 		public static ConvertedSI ConvertToLiterPer100Kilometer(this VolumePerMeter value)
-        {
-            return value == null ? null : new ConvertedSI(value.Value() * (10*10*10) * (100*1000), "l/100km");
-        }
-        
-        public static ConvertedSI ConvertToLiterPer100TonKiloMeter(this VolumePerMeterMass value)
-        {
-            const int CubicMeterToLiter = 10 * 10 * 10;
-            const int MeterTo100KiloMeter = 100 * Kilo;
-            const int KilogrammToTon = Kilo;
-
-            return value == null ? null  : new ConvertedSI(value.Value() * CubicMeterToLiter * (MeterTo100KiloMeter * KilogrammToTon), "l/100tkm");
-        }
+		{
+			return value == null ? null : new ConvertedSI(value.Value() * (10*10*10) * (100*1000), "l/100km");
+		}
+		
+		public static ConvertedSI ConvertToLiterPer100TonKiloMeter(this VolumePerMeterMass value)
+		{
+			const int CubicMeterToLiter = 10 * 10 * 10;
+			const int MeterTo100KiloMeter = 100 * Kilo;
+			const int KilogrammToTon = Kilo;
+
+			return value == null ? null  : new ConvertedSI(value.Value() * CubicMeterToLiter * (MeterTo100KiloMeter * KilogrammToTon), "l/100tkm");
+		}
 
 		public static ConvertedSI ConvertToLiterPerCubicMeter100KiloMeter(this VolumePerMeterVolume value)
-        {
-            const int CubicMeterToLiter = 10 * 10 * 10;
-            const int MeterTo100KiloMeter = 100 * Kilo;
-            return new ConvertedSI(value.Value() * CubicMeterToLiter * MeterTo100KiloMeter, "l/100m^3km");
-        }
-
-        public static ConvertedSI ConvertToGrammPerHour(this KilogramPerSecond value)
-        {
-            return new ConvertedSI(value.Value() * Kilo * SecondsPerHour, "g/h");
-        }
-
-        public static ConvertedSI ConvertToKiloMeter(this Meter value)
-        {
-            return new ConvertedSI(value.Value() / Kilo, "km");	
-        }
-
-        public static ConvertedSI ConvertToCubicCentiMeter(this CubicMeter value)
-        {
-            return new ConvertedSI(value.Value() * 100 * 100 * 100, "cm^3");
-        }
-
-        public static ConvertedSI ConvertToGrammPerCubicMeterKiloMeter(this KilogramPerMeterCubicMeter value)
-        {
-            return new ConvertedSI(value.Value()  * Kilo * Kilo, "g/m^3km");
-        }
-
-        public static ConvertedSI ConvertToGrammPerTonKilometer(this KilogramPerMeterMass value)
-        {
-            return new ConvertedSI(value.Value() * Kilo * Kilo * Kilo, "g/tkm");
-        }
-
-        public static ConvertedSI ConvertToKiloWattHour(this WattSecond value)
-        {
-            return new ConvertedSI(value.Value() / Kilo / SecondsPerHour, "kWh");
-        }
-        public static ConvertedSI ConvertToKiloWatt(this Watt value)
-        {
-            return new ConvertedSI(value.Value() / Kilo, "kW");
-        }
-
-        public static ConvertedSI ConvertToRoundsPerMinute(this PerSecond value)
-        {
-            return new ConvertedSI(value.AsRPM, "rpm");
-        }
-        public static ConvertedSI ConvertToCubicDeziMeter(this CubicMeter value)
-        {
-            return new ConvertedSI(value.Value() * 10 * 10 * 10, "dm^3");
-        }
-        public static ConvertedSI ConvertToMilliMeter(this Meter value)
-        {
-            return new ConvertedSI(value.Value() * Kilo, "mm");
-        }
+		{
+			const int CubicMeterToLiter = 10 * 10 * 10;
+			const int MeterTo100KiloMeter = 100 * Kilo;
+			return new ConvertedSI(value.Value() * CubicMeterToLiter * MeterTo100KiloMeter, "l/100m^3km");
+		}
+
+		public static ConvertedSI ConvertToGrammPerHour(this KilogramPerSecond value)
+		{
+			return new ConvertedSI(value.Value() * Kilo * SecondsPerHour, "g/h");
+		}
+
+		public static ConvertedSI ConvertToKiloMeter(this Meter value)
+		{
+			return new ConvertedSI(value.Value() / Kilo, "km");	
+		}
+
+		public static ConvertedSI ConvertToCubicCentiMeter(this CubicMeter value)
+		{
+			return new ConvertedSI(value.Value() * 100 * 100 * 100, "cm^3");
+		}
+
+		public static ConvertedSI ConvertToGrammPerCubicMeterKiloMeter(this KilogramPerMeterCubicMeter value)
+		{
+			return new ConvertedSI(value.Value()  * Kilo * Kilo, "g/m^3km");
+		}
+
+		public static ConvertedSI ConvertToGrammPerTonKilometer(this KilogramPerMeterMass value)
+		{
+			return new ConvertedSI(value.Value() * Kilo * Kilo * Kilo, "g/tkm");
+		}
+
+		public static ConvertedSI ConvertToKiloWattHour(this WattSecond value)
+		{
+			return new ConvertedSI(value.Value() / Kilo / SecondsPerHour, "kWh");
+		}
+		public static ConvertedSI ConvertToKiloWatt(this Watt value)
+		{
+			return new ConvertedSI(value.Value() / Kilo, "kW");
+		}
+
+		public static ConvertedSI ConvertToRoundsPerMinute(this PerSecond value)
+		{
+			return new ConvertedSI(value.AsRPM, "rpm");
+		}
+		public static ConvertedSI ConvertToCubicDeziMeter(this CubicMeter value)
+		{
+			return new ConvertedSI(value.Value() * 10 * 10 * 10, "dm^3");
+		}
+		public static ConvertedSI ConvertToMilliMeter(this Meter value)
+		{
+			return new ConvertedSI(value.Value() * Kilo, "mm");
+		}
 
 		public static ConvertedSI ConvertToMegaJoulePerKilogram(this JoulePerKilogramm value)
 		{
diff --git a/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs b/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs
index 44cda17332..cb8017175c 100644
--- a/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs
+++ b/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs
@@ -524,8 +524,8 @@ namespace TUGraz.VectoCore.Models.Declaration
 
 			public static readonly Second SamplingInterval = 0.5.SI<Second>();
 
-			public static readonly KilogramPerSecond LowerFCThreshold = 180.SI(Unit.SI.Gramm.Per.Hour).Cast<KilogramPerSecond>();
-			public static readonly KilogramPerSecond UpperFCThreshold = 600.SI(Unit.SI.Gramm.Per.Hour).Cast<KilogramPerSecond>();
+			public static readonly SpecificFuelConsumption LowerFCThreshold = 180.SI(Unit.SI.Gramm.Per.Kilo.Watt.Hour).Cast<SpecificFuelConsumption>();
+			public static readonly SpecificFuelConsumption UpperFCThreshold = 600.SI(Unit.SI.Gramm.Per.Kilo.Watt.Hour).Cast<SpecificFuelConsumption>();
 			public static readonly Second FCAccumulationWindow = 10.SI(Unit.SI.Minute).Cast<Second>();
 		}
 	}
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/VTPCycle.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/VTPCycle.cs
index 7735c430b6..5f75ce6edf 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/VTPCycle.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/VTPCycle.cs
@@ -53,10 +53,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 		public override IResponse Initialize()
 		{
+			PrepareCycleData();
 			if (DataBus.ExecutionMode == ExecutionMode.Declaration) {
 				VerifyInputData();
 			}
-			PrepareCycleData();
 			SelectStartGear();
 			return base.Initialize();
 		}
@@ -100,24 +100,34 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 		private void VerifyFCInput()
 		{
 			var idx = 0L;
-			int count = Convert.ToInt32(DeclarationData.VTPMode.FCAccumulationWindow / DeclarationData.VTPMode.SamplingInterval);
-			var sum = 0.SI<Kilogram>();
-			var window = 0.SI<Kilogram>().Repeat(count).ToArray();
+			var count = Convert.ToInt32(DeclarationData.VTPMode.FCAccumulationWindow / DeclarationData.VTPMode.SamplingInterval);
+			var sumFC= 0.SI<Kilogram>();
+			var sumEWheel = 0.SI<WattSecond>();
+
+			var window = new {FC = 0.SI<Kilogram>(), EWheel = 0.SI<WattSecond>()}.Repeat(count).ToArray();
 			
 			foreach (var entry in Data.Entries.Pairwise()) {
-				var fc = entry.Item1.Fuelconsumption * (entry.Item2.Time - entry.Item1.Time);
-				window[idx % count] = fc;
-				sum += window[idx % count];
-				sum -= window[(idx + 1) % count];
-				if (idx >= count && sum < DeclarationData.VTPMode.LowerFCThreshold * DeclarationData.VTPMode.FCAccumulationWindow) {
-					Log.Error("Fuel consumption for the previous {0} below threshold of {1}. t: {2}, FC: {3}", DeclarationData.VTPMode.FCAccumulationWindow.ConvertToMinutes(),
-						DeclarationData.VTPMode.LowerFCThreshold.ConvertToGrammPerHour(), entry.Item1.Time, (sum / DeclarationData.VTPMode.FCAccumulationWindow).ConvertToGrammPerHour());
+				var dt = entry.Item2.Time - entry.Item1.Time;
+				var fc = entry.Item1.Fuelconsumption * dt;
+				var eWheel = entry.Item1.PWheel > 0 ? entry.Item1.PWheel * dt : 0.SI<WattSecond>();
+				window[idx % count] = new {FC= fc, EWheel = eWheel};
+				sumFC += window[idx % count].FC;
+				sumFC -= window[(idx + 1) % count].FC;
+				sumEWheel += window[idx % count].EWheel;
+				sumEWheel -= window[(idx + 1) % count].EWheel;
+				idx++;
+
+				if (sumEWheel.IsSmaller(1.SI(Unit.SI.Kilo.Watt.Hour))) {
+					continue;
 				}
-				if (idx >= count && sum > DeclarationData.VTPMode.UpperFCThreshold * DeclarationData.VTPMode.FCAccumulationWindow) {
-					Log.Error("Fuel consumption for the previous {0} above threshold of {1}. t: {2}, FC: {3}", DeclarationData.VTPMode.FCAccumulationWindow.ConvertToMinutes(),
-							DeclarationData.VTPMode.UpperFCThreshold.ConvertToGrammPerHour(), entry.Item1.Time, (sum / DeclarationData.VTPMode.FCAccumulationWindow).ConvertToGrammPerHour());
+				if (sumFC / sumEWheel < DeclarationData.VTPMode.LowerFCThreshold ) {
+					Log.Error("Fuel consumption for the previous {0} [min] below threshold of {1} [g/kWh]. t: {2} [s], FC: {3} [g/kWh]", DeclarationData.VTPMode.FCAccumulationWindow.ConvertToMinutes(),
+						DeclarationData.VTPMode.LowerFCThreshold.ConvertToGramPerKiloWattHour(), entry.Item1.Time, (sumFC / sumEWheel).ConvertToGramPerKiloWattHour());
+				}
+				if (sumFC / sumEWheel > DeclarationData.VTPMode.UpperFCThreshold) {
+					Log.Error("Fuel consumption for the previous {0} [min] above threshold of {1} [g/kWh]. t: {2} [s], FC: {3} [g/kWh]", DeclarationData.VTPMode.FCAccumulationWindow.ConvertToMinutes(),
+							DeclarationData.VTPMode.UpperFCThreshold.ConvertToGramPerKiloWattHour(), entry.Item1.Time, (sumFC / sumEWheel).ConvertToGramPerKiloWattHour());
 				}
-				idx++;
 			}
 		}
 
diff --git a/VectoCore/VectoCore/OutputData/XML/XMLVTPReport.cs b/VectoCore/VectoCore/OutputData/XML/XMLVTPReport.cs
index 70ed49896a..e059e1f316 100644
--- a/VectoCore/VectoCore/OutputData/XML/XMLVTPReport.cs
+++ b/VectoCore/VectoCore/OutputData/XML/XMLVTPReport.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Xml;
 using System.Xml.Linq;
+using NLog.Config;
+using NLog.Targets;
 using TUGraz.IVT.VectoXML.Writer;
 using TUGraz.VectoCommon.Hashing;
 using TUGraz.VectoCommon.InputData;
@@ -17,6 +19,9 @@ using TUGraz.VectoCore.Models.SimulationComponent.Data;
 using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox;
 using TUGraz.VectoCore.Utils;
 using TUGraz.VectoHashing;
+using NLog;
+using TUGraz.VectoCore.Models.SimulationComponent.Impl;
+using LogManager = NLog.LogManager;
 
 namespace TUGraz.VectoCore.OutputData.XML
 {
@@ -24,6 +29,8 @@ namespace TUGraz.VectoCore.OutputData.XML
 	{
 		public const string CURRENT_SCHEMA_VERSION = "0.1";
 
+		private const string VTPReportTartetName = "VTPReportTarget";
+
 		protected XElement VehiclePart;
 		protected XElement GeneralPart;
 		protected XElement DataIntegrityPart;
@@ -34,6 +41,8 @@ namespace TUGraz.VectoCore.OutputData.XML
 		protected XNamespace tns;
 
 		private IOutputDataWriter _writer;
+		private static List<string> LogList = new List<string>();
+		private LoggingRule cycleChecksRule;
 
 		//protected XNamespace di;
 		//private bool allSuccess = true;
@@ -87,8 +96,32 @@ namespace TUGraz.VectoCore.OutputData.XML
 			Results = new XElement(tns + "Results");
 
 			_writer = writer;
+
+
+			AddLogging();
 		}
 
+		private void AddLogging()
+		{
+			LogList.Clear();
+			var target = new MethodCallTarget {
+				ClassName = typeof(XMLVTPReport).AssemblyQualifiedName,
+				MethodName = "LogMethod"
+			};
+			target.Parameters.Add(new MethodCallParameter("${level}"));
+			target.Parameters.Add(new MethodCallParameter("${message}"));
+			var config = LogManager.Configuration;
+			cycleChecksRule = new LoggingRule(typeof(VTPCycle).FullName, LogLevel.Error, target);
+			config.AddTarget(VTPReportTartetName, target);
+			config.LoggingRules.Add(cycleChecksRule);
+			LogManager.Configuration.Reload();
+		}
+
+		// ReSharper disable once UnusedMember.Global -- see AddLogging Method
+		public static void LogMethod(string level, string message)
+		{
+			LogList.Add(message);
+		}
 
 		#region Overrides of DeclarationReport<ResultEntry>
 
@@ -100,6 +133,11 @@ namespace TUGraz.VectoCore.OutputData.XML
 
 		protected internal override void DoWriteReport()
 		{
+			var config = LogManager.Configuration;
+			config.LoggingRules.Remove(cycleChecksRule);
+			config.RemoveTarget(VTPReportTartetName);
+			LogManager.Configuration.Reload();
+
 			GenerateResults();
 
 			var report = GenerateReport();
@@ -170,6 +208,9 @@ namespace TUGraz.VectoCore.OutputData.XML
 					)
 				),
 				new XElement(tns + "VTRatio", cVtp.ToXMLFormat(4)));
+			if (LogList.Any()) {
+				Results.Add(new XElement(tns + "Warnings", LogList.Select(x => new XElement(tns + "Warning", x))));
+			}
 		}
 
 		private XDocument GenerateReport()
-- 
GitLab