From 1cf75e777ba1ff9fb0ba9a13828e68a8d35f7f32 Mon Sep 17 00:00:00 2001
From: Michael Krisper <michael.krisper@tugraz.at>
Date: Wed, 8 Apr 2015 12:17:58 +0200
Subject: [PATCH] started with classes for reading job file and writing sum
 file

---
 .../Models/Simulation/Impl/JobContainer.cs    |  38 +-
 .../Simulation/Impl/SimulatorFactory.cs       |  60 +--
 .../Models/Simulation/Impl/VectoSimulator.cs  | 393 +++++++++++++++---
 VectoCore/Utils/VectoCSVFile.cs               | 248 +++++------
 4 files changed, 520 insertions(+), 219 deletions(-)

diff --git a/VectoCore/Models/Simulation/Impl/JobContainer.cs b/VectoCore/Models/Simulation/Impl/JobContainer.cs
index 74d9191336..fe54618460 100644
--- a/VectoCore/Models/Simulation/Impl/JobContainer.cs
+++ b/VectoCore/Models/Simulation/Impl/JobContainer.cs
@@ -5,21 +5,29 @@ using Common.Logging;
 
 namespace TUGraz.VectoCore.Models.Simulation.Impl
 {
-	//todo: add job tracking (state of jobs, iteration, ...)
-	//todo: add job control (pause, stop)
-	public class JobContainer
-	{
-		private readonly List<IVectoSimulator> _simulators = new List<IVectoSimulator>();
+    //todo: add job tracking (state of jobs, iteration, ...)
+    //todo: add job control (pause, stop)
 
-		public void AddJob(IVectoSimulator sim)
-		{
-			_simulators.Add(sim);
-		}
 
-		public void RunSimulation()
-		{
-			LogManager.GetLogger(GetType()).Info("VectoSimulator started running. Starting Jobs.");
-			Task.WaitAll(_simulators.Select(job => Task.Factory.StartNew(job.Run)).ToArray());
-		}
-	}
+    /// <summary>
+    ///     Container for simulation jobs.
+    /// </summary>
+    public class JobContainer
+    {
+        private readonly List<IVectoSimulator> _simulators = new List<IVectoSimulator>();
+
+        public void AddJob(IVectoSimulator sim)
+        {
+            _simulators.Add(sim);
+        }
+
+        /// <summary>
+        ///     Runs all jobs, waits until finished.
+        /// </summary>
+        public void RunSimulation()
+        {
+            LogManager.GetLogger(GetType()).Info("VectoSimulator started running. Starting Jobs.");
+            Task.WaitAll(_simulators.Select(job => Task.Factory.StartNew(job.Run)).ToArray());
+        }
+    }
 }
\ No newline at end of file
diff --git a/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs b/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
index 17e5fff093..196366ee96 100644
--- a/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
+++ b/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
@@ -6,44 +6,46 @@ using TUGraz.VectoCore.Models.SimulationComponent.Impl;
 
 namespace TUGraz.VectoCore.Models.Simulation.Impl
 {
-	public class SimulatorFactory
-	{
-		public static IVectoSimulator CreateTimeBasedEngineOnlyJob(string engineFile, string cycleFile, string resultFile)
-		{
-			Action<string> debug = LogManager.GetLogger<SimulatorFactory>().Debug;
+    public class SimulatorFactory
+    {
+        public static IVectoSimulator CreateTimeBasedEngineOnlyJob(string engineFile, string cycleFile,
+            string resultFile)
+        {
+            Action<string> debug = LogManager.GetLogger<SimulatorFactory>().Debug;
 
-			debug("Creating VehicleContainer.");
-			var container = new VehicleContainer();
+            debug("Creating VehicleContainer.");
+            var container = new VehicleContainer();
 
-			debug("SimulationFactory creating cycle.");
-			var cycleData = DrivingCycleData.ReadFromFileEngineOnly(cycleFile);
-			var cycle = new EngineOnlyDrivingCycle(container, cycleData);
+            debug("Creating cycle.");
+            var cycleData = DrivingCycleData.ReadFromFileEngineOnly(cycleFile);
+            var cycle = new EngineOnlyDrivingCycle(container, cycleData);
 
-			debug("SimulationFactory creating engine.");
-			var engineData = CombustionEngineData.ReadFromFile(engineFile);
-			var engine = new CombustionEngine(container, engineData);
+            debug("Creating engine.");
+            var engineData = CombustionEngineData.ReadFromFile(engineFile);
+            var engine = new CombustionEngine(container, engineData);
 
-			debug("Creating gearbox.");
-			var gearBox = new EngineOnlyGearbox(container);
+            debug("Creating gearbox.");
+            var gearBox = new EngineOnlyGearbox(container);
 
-			debug("SimulationFactory creating auxiliary");
-			var aux = new EngineOnlyAuxiliary(container, new AuxiliariesDemandAdapter(cycleData));
+            debug("Creating auxiliary");
+            var aux = new EngineOnlyAuxiliary(container, new AuxiliariesDemandAdapter(cycleData));
 
-			debug("SimulationFactory connecting auxiliary with engine.");
-			aux.Connect(engine.OutShaft());
+            debug("Connecting auxiliary with engine.");
+            aux.Connect(engine.OutShaft());
 
-			debug("SimulationFactory connecting gearbox with auxiliary.");
-			gearBox.InShaft().Connect(aux.OutShaft());
+            debug("Connecting gearbox with auxiliary.");
+            gearBox.InShaft().Connect(aux.OutShaft());
 
-			debug("SimulationFactory connecting cycle with gearbox.");
-			cycle.InShaft().Connect(gearBox.OutShaft());
+            debug("Connecting cycle with gearbox.");
+            cycle.InShaft().Connect(gearBox.OutShaft());
 
-			var dataWriter = new ModalDataWriter(resultFile);
+            var dataWriter = new ModalDataWriter(resultFile);
 
-			debug("Creating Simulator.");
-			var simulator = new VectoSimulator(container, cycle, dataWriter);
+            debug("Creating Simulator.");
+            //todo: load job file?
+            var simulator = new VectoSimulator(container, cycle, dataWriter);
 
-			return simulator;
-		}
-	}
+            return simulator;
+        }
+    }
 }
\ No newline at end of file
diff --git a/VectoCore/Models/Simulation/Impl/VectoSimulator.cs b/VectoCore/Models/Simulation/Impl/VectoSimulator.cs
index 3c551a88ae..2eb099ffe1 100644
--- a/VectoCore/Models/Simulation/Impl/VectoSimulator.cs
+++ b/VectoCore/Models/Simulation/Impl/VectoSimulator.cs
@@ -1,64 +1,347 @@
-using System;
+using System;
+using System.Collections.Generic;
+using System.Data;
 using Common.Logging;
+using Newtonsoft.Json;
 using TUGraz.VectoCore.Models.Connector.Ports;
 using TUGraz.VectoCore.Models.Connector.Ports.Impl;
 using TUGraz.VectoCore.Models.Simulation.Data;
 using TUGraz.VectoCore.Models.SimulationComponent;
+using TUGraz.VectoCore.Utils;
 
 namespace TUGraz.VectoCore.Models.Simulation.Impl
 {
-	public class VectoSimulator : IVectoSimulator
-	{
-		private TimeSpan _absTime = new TimeSpan(seconds: 0, minutes: 0, hours: 0);
-		private TimeSpan _dt = new TimeSpan(seconds: 1, minutes: 0, hours: 0);
-
-		public VectoSimulator(IVehicleContainer container, IDrivingCycle cycle, IModalDataWriter dataWriter)
-		{
-			Container = container;
-			Cycle = cycle;
-			DataWriter = dataWriter;
-		}
-
-		protected IDrivingCycle Cycle { get; set; }
-		protected IModalDataWriter DataWriter { get; set; }
-		protected IVehicleContainer Container { get; set; }
-
-		public IVehicleContainer GetContainer()
-		{
-			return Container;
-		}
-
-		public void Run()
-		{
-			LogManager.GetLogger(GetType()).Info("VectoJob started running.");
-			IResponse response;
-			do {
-				response = Cycle.Request(_absTime, _dt);
-				while (response is ResponseFailTimeInterval) {
-					_dt = (response as ResponseFailTimeInterval).DeltaT;
-					response = Cycle.Request(_absTime, _dt);
-				}
-
-				if (response is ResponseCycleFinished) {
-					break;
-				}
-
-
-				DataWriter[ModalResultField.time] = (_absTime + TimeSpan.FromTicks(_dt.Ticks / 2)).TotalSeconds;
-				DataWriter[ModalResultField.simulationInterval] = _dt.TotalSeconds;
-
-				Container.CommitSimulationStep(DataWriter);
-
-				// set _dt to difference to next full second.
-				_absTime += _dt;
-				_dt = TimeSpan.FromSeconds(1) - TimeSpan.FromMilliseconds(_dt.Milliseconds);
-			} while (response is ResponseSuccess);
-
-			Container.FinishSimulation(DataWriter);
-
-			//todo: write vsum file
-
-			LogManager.GetLogger(GetType()).Info("VectoJob finished.");
-		}
-	}
+    public class VectoJob
+    {
+        /// <summary>
+        ///     A class which represents the json data format for serializing and deserializing the VectoJob files.
+        /// </summary>
+        /// <remarks>
+        ///{
+        ///   "Header": {
+        ///     "CreatedBy": " ()",
+        ///     "Date": "3/4/2015 2:09:13 PM",
+        ///     "AppVersion": "2.0.4-beta3",
+        ///     "FileVersion": 2
+        ///   },
+        ///   "Body": {
+        ///     "SavedInDeclMode": false,
+        ///     "VehicleFile": "24t Coach.vveh",
+        ///     "EngineFile": "24t Coach.veng",
+        ///     "GearboxFile": "24t Coach.vgbx",
+        ///     "Cycles": [
+        ///       "W:\\VECTO\\CITnet\\VECTO\\bin\\Debug\\Declaration\\MissionCycles\\LOT2_rural Engine Only.vdri"
+        ///     ],
+        ///     "Aux": [
+        ///       {
+        ///         "ID": "ALT1",
+        ///         "Type": "Alternator",
+        ///         "Path": "24t_Coach_ALT.vaux",
+        ///         "Technology": ""
+        ///       },
+        ///       {
+        ///         "ID": "ALT2",
+        ///         "Type": "Alternator",
+        ///         "Path": "24t_Coach_ALT.vaux",
+        ///         "Technology": ""
+        ///       },
+        ///       {
+        ///         "ID": "ALT3",
+        ///         "Type": "Alternator",
+        ///         "Path": "24t_Coach_ALT.vaux",
+        ///         "Technology": ""
+        ///       }
+        ///     ],
+        ///     "VACC": "Coach.vacc",
+        ///     "EngineOnlyMode": true,
+        ///     "StartStop": {
+        ///       "Enabled": false,
+        ///       "MaxSpeed": 5.0,
+        ///       "MinTime": 0.0,
+        ///       "Delay": 0
+        ///     },
+        ///     "LAC": {
+        ///       "Enabled": true,
+        ///       "Dec": -0.5,
+        ///       "MinSpeed": 50.0
+        ///     },
+        ///     "OverSpeedEcoRoll": {
+        ///       "Mode": "OverSpeed",
+        ///       "MinSpeed": 70.0,
+        ///       "OverSpeed": 5.0,
+        ///       "UnderSpeed": 5.0
+        ///     }
+        ///   }
+        /// }
+        /// </remarks>
+        public class Data
+        {
+            [JsonProperty(Required = Required.Always)] public DataHeader Header;
+            [JsonProperty(Required = Required.Always)] public DataBody Body;
+
+            public class DataHeader
+            {
+                [JsonProperty(Required = Required.Always)] public string CreatedBy;
+                [JsonProperty(Required = Required.Always)] public DateTime Date;
+                [JsonProperty(Required = Required.Always)] public string AppVersion;
+                [JsonProperty(Required = Required.Always)] public double FileVersion;
+            }
+
+            public class DataBody
+            {
+                [JsonProperty("SavedInDeclMode")] public bool SavedInDeclMode;
+
+                [JsonProperty(Required = Required.Always)] public string VehicleFile;
+                [JsonProperty(Required = Required.Always)] public string EngineFile;
+                [JsonProperty(Required = Required.Always)] public string GearboxFile;
+                [JsonProperty(Required = Required.Always)] public IList<string> Cycles;
+                [JsonProperty(Required = Required.Always)] public IList<AuxData> Aux;
+                [JsonProperty(Required = Required.Always)] public string VACC;
+                [JsonProperty(Required = Required.Always)] public bool EngineOnlyMode;
+                [JsonProperty(Required = Required.Always)] public StartStopData StartStop;
+                [JsonProperty(Required = Required.Always)] public LACData LAC;
+                [JsonProperty(Required = Required.Always)] public OverSpeedEcoRollData OverSpeedEcoRoll;
+
+                public class AuxData
+                {
+                    [JsonProperty(Required = Required.Always)] public string ID;
+                    [JsonProperty(Required = Required.Always)] public string Type;
+                    [JsonProperty(Required = Required.Always)] public string Path;
+                    [JsonProperty(Required = Required.Always)] public string Technology;
+                }
+
+                public class StartStopData
+                {
+                    [JsonProperty(Required = Required.Always)] public bool Enabled;
+                    [JsonProperty(Required = Required.Always)] public double MaxSpeed;
+                    [JsonProperty(Required = Required.Always)] public double MinTime;
+                    [JsonProperty(Required = Required.Always)] public double Delay;
+                }
+
+                public class LACData
+                {
+                    [JsonProperty(Required = Required.Always)] public bool Enabled;
+                    [JsonProperty(Required = Required.Always)] public double Dec;
+                    [JsonProperty(Required = Required.Always)] public double MinSpeed;
+                }
+
+                public class OverSpeedEcoRollData
+                {
+                    [JsonProperty(Required = Required.Always)] public string Mode;
+                    [JsonProperty(Required = Required.Always)] public double MinSpeed;
+                    [JsonProperty(Required = Required.Always)] public double OverSpeed;
+                    [JsonProperty(Required = Required.Always)] public double UnderSpeed;
+                }
+            }
+        }
+    }
+
+
+    /// <summary>
+    /// Simulator for one vecto simulation job.
+    /// </summary>
+    public class VectoSimulator : IVectoSimulator
+    {
+        private TimeSpan _absTime = new TimeSpan(seconds: 0, minutes: 0, hours: 0);
+        private TimeSpan _dt = new TimeSpan(seconds: 1, minutes: 0, hours: 0);
+
+        public VectoSimulator(string name, string fileName, IVehicleContainer container, IDrivingCycle cycle,
+            IModalDataWriter dataWriter)
+        {
+            Name = name;
+            FileName = fileName;
+            Container = container;
+            Cycle = cycle;
+            DataWriter = dataWriter;
+        }
+
+        public string FileName { get; set; }
+
+        protected string Name { get; set; }
+
+        protected IDrivingCycle Cycle { get; set; }
+        protected IModalDataWriter DataWriter { get; set; }
+        protected IVehicleContainer Container { get; set; }
+
+        public IVehicleContainer GetContainer()
+        {
+            return Container;
+        }
+
+        public void Run()
+        {
+            LogManager.GetLogger(GetType()).Info("VectoJob started running.");
+            IResponse response;
+            do {
+                response = Cycle.Request(_absTime, _dt);
+                while (response is ResponseFailTimeInterval) {
+                    _dt = (response as ResponseFailTimeInterval).DeltaT;
+                    response = Cycle.Request(_absTime, _dt);
+                }
+
+                if (response is ResponseCycleFinished) {
+                    break;
+                }
+
+                DataWriter[ModalResultField.time] = (_absTime + TimeSpan.FromTicks(_dt.Ticks / 2)).TotalSeconds;
+                DataWriter[ModalResultField.simulationInterval] = _dt.TotalSeconds;
+
+                Container.CommitSimulationStep(DataWriter);
+
+                // set _dt to difference to next full second.
+                _absTime += _dt;
+                _dt = TimeSpan.FromSeconds(1) - TimeSpan.FromMilliseconds(_dt.Milliseconds);
+            } while (response is ResponseSuccess);
+
+            Container.FinishSimulation(DataWriter);
+
+
+            WriteSummary();
+
+            LogManager.GetLogger(GetType()).Info("VectoJob finished.");
+        }
+
+
+        /*
+        Name	Unit	Description
+        Job	[-]	Job number. Format is "x-y" with x = file number and y = cycle number
+        Input File	[-]	Name of the input file
+        Cycle	[-]	Name of the cycle file
+        time	[s]	Total simulation time
+        distance	[km]	Total travelled distance
+        speed	[km/h]	Average vehicle speed
+        ∆altitude	[m]	Altitude difference between start and end of cycle
+        Ppos	[kW]	Average positive engine power
+        Pneg	[kW]	Average negative engine power
+        FC	[g/km]	Average fuel consumption
+        FC-AUXc	[g/km]	Fuel consumption after Auxiliary-Start/Stop Correction. (Based on FC.)
+        FC-WHTCc	[g/km]	Fuel consumption after WHTC Correction. (Based on FC-AUXc.)
+        Pbrake	[kW]	Average brake power (not including engine drag)
+        EposICE	[kWh]	Total positive engine work
+        EnegICE	[kWh]	Total negative engine work (engine brake)
+        Eair	[kWh]	Total work of air resistance
+        Eroll	[kWh]	Total work of rolling resistance
+        Egrad	[kWh]	Total work of gradient resistance
+        Eacc	[kWh]	Total work from accelerations (<0) / decelerations (>0) 
+        Eaux	[kWh]	Total energy demand of auxiliaries
+        Eaux_xxx	[kWh]	Energy demand of auxiliary with ID xxx. See also Aux Dialog and Driving Cycle.
+        Ebrake	[kWh]	Total work dissipated in mechanical braking (sum of service brakes, retader and additional engine exhaust brakes)
+        Etransm	[kWh]	Total work of transmission losses
+        Eretarder	[kWh]	Total retarder losses
+        Mass	[kg]	Vehicle mass (equals Curb Weight Vehicle plus Curb Weight Extra Trailer/Body, see Vehicle Editor)
+        Loading	[kg]	Vehicle loading (see Vehicle Editor)
+        a	[m/s2]	Average acceleration
+        a_pos	[m/s2]	Average acceleration in acceleration phases*
+        a_neg	[m/s2]	Average deceleration in deceleration phases*
+        Acc.Noise	[m/s2]	Acceleration noise
+        pAcc	[%]	Time share of acceleration phases*
+        pDec	[%]	Time share of deceleration phases*
+        pCruise	[%]	Time share of cruise phases*
+        pStop	[%]	Time share of stop phases*
+    */
+
+        private static class SummaryFile
+        {
+            private static DataTable table;
+
+            static SummaryFile()
+            {
+                table = new DataTable();
+                table.Columns.Add("Job [-]", typeof(string));
+                table.Columns.Add("Input File [-]", typeof(string));
+                table.Columns.Add("Cycle [-]", typeof(string));
+                table.Columns.Add("Time [s]", typeof(double));
+                table.Columns.Add("distance [km]", typeof(double));
+                table.Columns.Add("speed [km/h]", typeof(double));
+                table.Columns.Add("∆altitude [m]", typeof(double));
+                table.Columns.Add("Ppos [kw]", typeof(double));
+                table.Columns.Add("Pneg [kw]", typeof(double));
+                table.Columns.Add("FC [g/km]", typeof(double));
+                table.Columns.Add("FC-AUXc [g/km]", typeof(double));
+                table.Columns.Add("FC-WHTCc [g/km]", typeof(double));
+                table.Columns.Add("Pbrake [kw]", typeof(double));
+                table.Columns.Add("EposICE [kwh]", typeof(double));
+                table.Columns.Add("EnegICE [kwh]", typeof(double));
+                table.Columns.Add("Eair [kwh]", typeof(double));
+                table.Columns.Add("Eroll [kwh]", typeof(double));
+                table.Columns.Add("Egrad [kwh]", typeof(double));
+                table.Columns.Add("Eacc [kwh]", typeof(double));
+                table.Columns.Add("Eaux [kwh]", typeof(double));
+                table.Columns.Add("Eaux_xxx [kwh]", typeof(double));
+                table.Columns.Add("Ebrake [kwh]", typeof(double));
+                table.Columns.Add("Etransm [kwh]", typeof(double));
+                table.Columns.Add("Eretarder [kwh]", typeof(double));
+                table.Columns.Add("Mass [kg]", typeof(double));
+                table.Columns.Add("Loading [kg]", typeof(double));
+                table.Columns.Add("a [m/s2]", typeof(double));
+                table.Columns.Add("a_pos [m/s2]", typeof(double));
+                table.Columns.Add("a_neg [m/s2]", typeof(double));
+                table.Columns.Add("Acc.Noise [m/s2]", typeof(double));
+                table.Columns.Add("pAcc [%]", typeof(double));
+                table.Columns.Add("pDec [%]", typeof(double));
+                table.Columns.Add("pCruise [%]", typeof(double));
+                table.Columns.Add("pStop [%]", typeof(double));
+            }
+        }
+
+
+        private void WriteSummary(ModalResults modData)
+        {
+            //var data = new ModalResults();
+
+            var row = table.NewRow();
+            //row["Job [-]"] = Name;
+            //row["Input File [-]"] = FileName;
+            //row["Cycle [-]"] = Container.CycleFileName();
+            //row["time [s]"] = data.Compute("Max(time)", "");
+            //row["distance [km]"] = data.Compute("Max(distance)", "");
+            //row["speed [km/h]"] = data.Compute("Avg(speed)", "");
+            //row["∆altitude [m]"] = data.Rows[data.Rows.Count-1].Field<double>("altitude") - data.Rows[0].Field<double>("altitude");
+            //row["Ppos [kw]"] = data.Compute("Avg(Peng)", "Peng > 0");
+            //row["Pneg [kw]"] = data.Compute("Avg(Peng)", "Peng < 0");
+            //row["FC [g/km]"] = data.Compute("Avg(FC)", "");
+            //row["FC-AUXc [g/km]"] = data.Compute("Avg(FC-AUXc)", "");
+            //row["FC-WHTCc [g/km]"] = data.Compute("Avg(FC-WHTCc)", "");
+            //row["Pbrake [kw]"] = data.Compute("Avg(Pbrake)", "");
+            //row["EposICE [kwh]"] = data.Compute("Avg(pos)", "pos > 0");
+            //row["EnegICE [kwh]"] = data.Compute("Avg(pos)", "pos < 0");
+            //row["Eair [kwh]"] = data.Compute("Sum(air)", "");
+            //row["Eroll [kwh]"] = data.Compute("Sum(roll)", "");
+            //row["Egrad [kwh]"] = data.Compute("Sum(grad)", "");
+            //row["Eacc [kwh]"] = data.Compute("Sum(acc)", "");
+            //row["Eaux [kwh]"] = data.Compute("Sum(aux)", "");
+
+            //todo auxiliaries
+            //foreach (var auxCol in data.Auxiliaries) {
+            //    row["Eaux_" + auxCol.Name + " [kwh]"] = data.Compute("Sum(aux_" + auxCol.Name + ")", "");
+            //}
+
+
+            //row["Ebrake [kwh]"] = data.Compute("Sum(brake)", "");
+            //row["Etransm [kwh]"] = data.Compute("Sum(transm)", "");
+            //row["Eretarder [kwh]"] = data.Compute("Sum(retarder)", "");
+            //row["Mass [kg]"] = Container.VehicleMass();
+            //row["Loading [kg]"] = Container.LoadingMass();
+            //row["a [m/s2]"] = data.Compute("Avg(a)", "");
+
+            ////todo: a3s = average over 3 seconds!!!
+            //row["a_pos [m/s2]"] = data.Compute("Avg(a)", "a > 0.125");
+            //row["a_neg [m/s2]"] = data.Compute("Avg(a)", "a < -0.125");
+
+            //// todo: is this really acc.noise?
+            //row["Acc.Noise [m/s2]"] = data.Compute("Sum(a)", "a < 0.125 and a > -0.125");
+
+            //row["pAcc [%]"] = (double) data.Compute("Sum(time_interval)", "a > 0.125") /
+            //                  (double) data.Compute("Sum(time_interval)", "");
+            //row["pDec [%]"] = (double) data.Compute("Sum(time_interval)", "a > 0.125") /
+            //                  (double) data.Compute("Sum(time_interval)", "");
+            //row["pCruise [%]"] = (double) data.Compute("Sum(time_interval)", "a > 0.125") /
+            //                     (double) data.Compute("Sum(time_interval)", "");
+            //row["pStop [%]"] = (double) data.Compute("Sum(time_interval)", "a < 0.125 and a > -0.125") /
+            //                   (double) data.Compute("Sum(time_interval)", "");
+
+            table.ImportRow(row);
+            VectoCSVFile.Write(FileName, table);
+        }
+    }
 }
\ No newline at end of file
diff --git a/VectoCore/Utils/VectoCSVFile.cs b/VectoCore/Utils/VectoCSVFile.cs
index b17f6b5b9a..44fe91be41 100644
--- a/VectoCore/Utils/VectoCSVFile.cs
+++ b/VectoCore/Utils/VectoCSVFile.cs
@@ -12,124 +12,132 @@ using TUGraz.VectoCore.Exceptions;
 
 namespace TUGraz.VectoCore.Utils
 {
-	/// <summary>
-	///     Class for Reading and Writing VECTO CSV Files.
-	/// </summary>
-	/// <remarks>
-	///     The following format applies to all CSV (Comma-separated values) Input Files used in VECTO:
-	///     List Separator: Comma ","
-	///     Decimal-Mark: Dot "."
-	///     Comments: "#" at the beginning of the comment line. Number and position of comment lines is not limited.
-	///     Header: One header line (not a comment line) at the beginning of the file.
-	///     All Combinations between max-format and min-format possible. Only "id"-field is used.
-	///     max: id (name) [unit], id (name) [unit], ...
-	///     min: id,id,...
-	/// </remarks>
-	public static class VectoCSVFile
-	{
-		private const char Separator = ',';
-		private const char Comment = '#';
-
-		/// <summary>
-		///     Reads a CSV file which is stored in Vecto-CSV-Format.
-		/// </summary>
-		/// <param name="fileName"></param>
-		/// <exception cref="FileIOException"></exception>
-		/// <returns>A DataTable which represents the CSV File.</returns>
-		public static DataTable Read(string fileName)
-		{
-			try {
-				var lines = File.ReadAllLines(fileName);
-				lines = RemoveComments(lines);
-
-				var validColumns = GetValidHeaderColumns(lines.First());
-
-				if (validColumns.Length > 0) {
-					// Valid Columns found => header was valid => skip header line
-					lines = lines.Skip(1).ToArray();
-				} else {
-					var log = LogManager.GetLogger(typeof (VectoCSVFile));
-					log.Warn("No valid Data Header found. Interpreting the first line as data line.");
-					// set the validColumns to: {"0", "1", "2", "3", ...} for all columns in first line.
-					validColumns = GetColumns(lines.First()).Select((_, index) => index.ToString()).ToArray();
-				}
-
-				var table = new DataTable();
-				foreach (var col in validColumns) {
-					table.Columns.Add(col);
-				}
-
-				for (var i = 0; i < lines.Length; i++) {
-					var line = lines[i];
-
-					var cells = line.Split(Separator);
-					if (cells.Length != table.Columns.Count) {
-						throw new CSVReadException(string.Format("Line {0}: The number of values is not correct.", i));
-					}
-
-					try {
-						table.Rows.Add(line.Split(Separator));
-					} catch (InvalidCastException e) {
-						throw new CSVReadException(
-							string.Format("Line {0}: The data format of a value is not correct. {1}", i, e.Message), e);
-					}
-				}
-
-				return table;
-			} catch (Exception e) {
-				throw new VectoException(string.Format("File {0}: {1}", fileName, e.Message));
-			}
-		}
-
-		private static string[] GetValidHeaderColumns(string line)
-		{
-			Contract.Requires(line != null);
-			double test;
-			var validColumns = GetColumns(line).
-				Where(col => !double.TryParse(col, NumberStyles.Any, CultureInfo.InvariantCulture, out test));
-			return validColumns.ToArray();
-		}
-
-		private static IEnumerable<string> GetColumns(string line)
-		{
-			Contract.Requires(line != null);
-
-			line = Regex.Replace(line, @"\[.*?\]", "");
-			line = Regex.Replace(line, @"\(.*?\)", "");
-			line = line.Replace("<", "");
-			line = line.Replace(">", "");
-			return line.Split(Separator).Select(col => col.Trim());
-		}
-
-		private static string[] RemoveComments(string[] lines)
-		{
-			Contract.Requires(lines != null);
-
-			lines = lines.
-				Select(line => line.Contains('#') ? line.Substring(0, line.IndexOf(Comment)) : line).
-				Where(line => !string.IsNullOrEmpty(line)).
-				ToArray();
-			return lines;
-		}
-
-		public static void Write(string fileName, DataTable table)
-		{
-			var sb = new StringBuilder();
-
-			var header = table.Columns.Cast<DataColumn>().Select(col => col.Caption ?? col.ColumnName);
-			sb.AppendLine(string.Join(", ", header));
-
-			foreach (DataRow row in table.Rows) {
-				var formattedList = new List<string>();
-				foreach (var item in row.ItemArray) {
-					var formattable = item as IFormattable;
-					var formattedValue = formattable != null ? formattable.ToString("", CultureInfo.InvariantCulture) : item.ToString();
-					formattedList.Add(formattedValue);
-				}
-				sb.AppendLine(string.Join(Separator.ToString(), formattedList));
-			}
-
-			File.WriteAllText(fileName, sb.ToString());
-		}
-	}
+    /// <summary>
+    ///     Class for Reading and Writing VECTO CSV Files.
+    /// </summary>
+    /// <remarks>
+    ///     The following format applies to all CSV (Comma-separated values) Input Files used in VECTO:
+    ///     List Separator: Comma ","
+    ///     Decimal-Mark: Dot "."
+    ///     Comments: "#" at the beginning of the comment line. Number and position of comment lines is not limited.
+    ///     Header: One header line (not a comment line) at the beginning of the file.
+    ///     All Combinations between max-format and min-format possible. Only "id"-field is used.
+    ///     max: id (name) [unit], id (name) [unit], ...
+    ///     min: id,id,...
+    /// </remarks>
+    public static class VectoCSVFile
+    {
+        private const char Separator = ',';
+        private const char Comment = '#';
+
+        /// <summary>
+        ///     Reads a CSV file which is stored in Vecto-CSV-Format.
+        /// </summary>
+        /// <param name="fileName"></param>
+        /// <exception cref="FileIOException"></exception>
+        /// <returns>A DataTable which represents the CSV File.</returns>
+        public static DataTable Read(string fileName)
+        {
+            try {
+                var lines = File.ReadAllLines(fileName);
+                lines = RemoveComments(lines);
+
+                var validColumns = GetValidHeaderColumns(lines.First());
+
+                if (validColumns.Length > 0) {
+                    // Valid Columns found => header was valid => skip header line
+                    lines = lines.Skip(1).ToArray();
+                } else {
+                    var log = LogManager.GetLogger(typeof(VectoCSVFile));
+                    log.Warn("No valid Data Header found. Interpreting the first line as data line.");
+                    // set the validColumns to: {"0", "1", "2", "3", ...} for all columns in first line.
+                    validColumns = GetColumns(lines.First()).Select((_, index) => index.ToString()).ToArray();
+                }
+
+                var table = new DataTable();
+                foreach (var col in validColumns) {
+                    table.Columns.Add(col);
+                }
+
+                for (var i = 0; i < lines.Length; i++) {
+                    var line = lines[i];
+
+                    var cells = line.Split(Separator);
+                    if (cells.Length != table.Columns.Count) {
+                        throw new CSVReadException(string.Format("Line {0}: The number of values is not correct.", i));
+                    }
+
+                    try {
+                        table.Rows.Add(line.Split(Separator));
+                    } catch (InvalidCastException e) {
+                        throw new CSVReadException(
+                            string.Format("Line {0}: The data format of a value is not correct. {1}", i, e.Message), e);
+                    }
+                }
+
+                return table;
+            } catch (Exception e) {
+                throw new VectoException(string.Format("File {0}: {1}", fileName, e.Message));
+            }
+        }
+
+        private static string[] GetValidHeaderColumns(string line)
+        {
+            Contract.Requires(line != null);
+            double test;
+            var validColumns = GetColumns(line).
+                Where(col => !double.TryParse(col, NumberStyles.Any, CultureInfo.InvariantCulture, out test));
+            return validColumns.ToArray();
+        }
+
+        private static IEnumerable<string> GetColumns(string line)
+        {
+            Contract.Requires(line != null);
+
+            line = Regex.Replace(line, @"\[.*?\]", "");
+            line = Regex.Replace(line, @"\(.*?\)", "");
+            line = line.Replace("<", "");
+            line = line.Replace(">", "");
+            return line.Split(Separator).Select(col => col.Trim());
+        }
+
+        private static string[] RemoveComments(string[] lines)
+        {
+            Contract.Requires(lines != null);
+
+            lines = lines.
+                Select(line => line.Contains('#') ? line.Substring(0, line.IndexOf(Comment)) : line).
+                Where(line => !string.IsNullOrEmpty(line)).
+                ToArray();
+            return lines;
+        }
+
+        /// <summary>
+        /// Writes the datatable to the csv file.
+        /// Uses the column caption as header (with fallback to column name) for the csv header.
+        /// </summary>
+        /// <param name="fileName">Path to the file.</param>
+        /// <param name="table">The Datatable.</param>
+        public static void Write(string fileName, DataTable table)
+        {
+            var sb = new StringBuilder();
+
+            var header = table.Columns.Cast<DataColumn>().Select(col => col.Caption ?? col.ColumnName);
+            sb.AppendLine(string.Join(", ", header));
+
+            foreach (DataRow row in table.Rows) {
+                var formattedList = new List<string>();
+                foreach (var item in row.ItemArray) {
+                    var formattable = item as IFormattable;
+                    var formattedValue = formattable != null
+                        ? formattable.ToString("", CultureInfo.InvariantCulture)
+                        : item.ToString();
+                    formattedList.Add(formattedValue);
+                }
+                sb.AppendLine(string.Join(Separator.ToString(), formattedList));
+            }
+
+            File.WriteAllText(fileName, sb.ToString());
+        }
+    }
 }
\ No newline at end of file
-- 
GitLab