diff --git a/VectoConsole/Program.cs b/VectoConsole/Program.cs index ea60d0796cb4ccaded797db675ebac1ebce63264..a9f52003080dff0056a83759084e757799d5c0ff 100644 --- a/VectoConsole/Program.cs +++ b/VectoConsole/Program.cs @@ -28,7 +28,9 @@ Synopsis: Description: FILE1.vecto [FILE2.vecto ...]: A list of vecto-job files (with the extension: .vecto). At least one file must be given. Delimited by whitespace. - -v: Activates verbose mode (trace and exceptions will be displayed) + -v: Shows verbose information (errors and warnings will be displayed) + -vv: Shows more verbose information (infos will be displayed) + -vvv: Shows all verbose information (everything, slow!) -h: Displays this help. Examples: @@ -47,17 +49,31 @@ Examples: Console.Write(HELP); return 0; } - args = args.Where(a => a != "-h").ToArray(); // on -v: activate verbose console logger + var logLevel = LogLevel.Fatal; + + // Fatal > Error > Warn > Info > Debug > Trace + if (args.Contains("-v")) { - var config = LogManager.Configuration; - var target = config.FindTargetByName("ConsoleLogger"); - config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, target)); - LogManager.Configuration = config; - Trace.Listeners.Add(new ConsoleTraceListener(true)); + // display errors, warnings + logLevel = LogLevel.Warn; + } else if (args.Contains("-vv")) { + // also display info and debug + logLevel = LogLevel.Debug; + } else if (args.Contains("-vvv")) { + // display everything! + logLevel = LogLevel.Trace; } - args = args.Where(a => a != "-v").ToArray(); + + var config = LogManager.Configuration; + config.LoggingRules.Add(new LoggingRule("*", logLevel, config.FindTargetByName("ConsoleLogger"))); + config.LoggingRules.Add(new LoggingRule("*", logLevel, config.FindTargetByName("LogFile"))); + LogManager.Configuration = config; + Trace.Listeners.Add(new ConsoleTraceListener(true)); + + args = args.Except(new[] { "-v", "-vv", "-vvv" }).ToArray(); + // if no other arguments given: display usage and terminate if (!args.Any()) { @@ -66,7 +82,7 @@ Examples: } // process the file list and start simulation - var fileList = args.Where(a => a != "-v").ToList().ToList(); + var fileList = args; var sumFileName = Path.GetFileNameWithoutExtension(fileList.First()) + Constants.FileExtensions.SumFile; var sumWriter = new SummaryFileWriter(sumFileName); diff --git a/VectoCore/Configuration/Constants.cs b/VectoCore/Configuration/Constants.cs index d7c6e3c120730a266f4c2bf0f1f5d0090a24952e..a5bc06b9ce445fa7175d7bb0267fb70a7276b502 100644 --- a/VectoCore/Configuration/Constants.cs +++ b/VectoCore/Configuration/Constants.cs @@ -72,6 +72,8 @@ namespace TUGraz.VectoCore.Configuration public static Meter DriverActionDistanceTolerance = 0.25.SI<Meter>(); + public static MeterPerSecond VehicleSpeedHaltTolerance = 1e-3.SI<MeterPerSecond>(); + /// <summary> /// The initial search interval for the operating point search in the driver. /// </summary> diff --git a/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs b/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs index 10708fe00f3d40a1a041b84b9b106c00254af891..24d48ca996762709dd25da74241555489e699a9f 100644 --- a/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs +++ b/VectoCore/FileIO/Reader/Impl/DeclarationModeSimulationDataReader.cs @@ -37,9 +37,16 @@ namespace TUGraz.VectoCore.FileIO.Reader.Impl var resultCount = segment.Missions.Sum(m => m.Loadings.Count); var engineData = dao.CreateEngineData(Engine); + var engineTypeString = string.Format("{0} l, {1} kW", + engineData.Displacement.ConvertTo().Cubic.Dezi.Meter.ToOutputFormat(1), + engineData.FullLoadCurve.MaxPower.ConvertTo().Kilo.Watt.ToOutputFormat(0)); + var gearboxData = dao.CreateGearboxData(Gearbox, engineData); - var report = new DeclarationReport(engineData.FullLoadCurve, segment, "CREATOR", engineData.ModelName, "engineStr", - gearboxData.ModelName, "gearboxStr", Job.BasePath, Job.JobFile, resultCount); + var gearboxTypeString = string.Format("{0}-Speed {1}", gearboxData.Gears.Count, gearboxData.Type); + + // todo: set correct <USERNAME> in Report + var report = new DeclarationReport(engineData.FullLoadCurve, segment, "<USERNAME>", engineData.ModelName, + engineTypeString, gearboxData.ModelName, gearboxTypeString, Job.BasePath, Job.JobFile, resultCount); foreach (var mission in segment.Missions) { var cycle = DrivingCycleDataReader.ReadFromStream(mission.CycleFile, CycleType.DistanceBased); diff --git a/VectoCore/Models/Declaration/DeclarationReport.cs b/VectoCore/Models/Declaration/DeclarationReport.cs index bce841bab6952ea46766500d3b83fff4268515bd..139b444725fad3873267e7b2252044b527873a41 100644 --- a/VectoCore/Models/Declaration/DeclarationReport.cs +++ b/VectoCore/Models/Declaration/DeclarationReport.cs @@ -18,28 +18,90 @@ using Rectangle = System.Drawing.Rectangle; namespace TUGraz.VectoCore.Models.Declaration { + /// <summary> + /// Class for creating a declaration report. + /// </summary> public class DeclarationReport { + /// <summary> + /// Container class for one mission and the modData for the different loadings. + /// </summary> private class ResultContainer { public Mission Mission; public Dictionary<LoadingType, IModalDataWriter> ModData; } + /// <summary> + /// Data Dictionary for all missions. + /// </summary> private readonly Dictionary<MissionType, ResultContainer> _missions = new Dictionary<MissionType, ResultContainer>(); + /// <summary> + /// The full load curve. + /// </summary> private readonly FullLoadCurve _flc; + + /// <summary> + /// The declaration segment from the segment table + /// </summary> private readonly Segment _segment; + + /// <summary> + /// The creator name for the report. + /// </summary> private readonly string _creator; + + + /// <summary> + /// The engine model string from engine file. + /// </summary> private readonly string _engineModel; + + /// <summary> + /// The engine description (displacement and max power) + /// </summary> private readonly string _engineStr; + + /// <summary> + /// The gearbox model string from gearbox file. + /// </summary> private readonly string _gearboxModel; + + /// <summary> + /// The gearbox description (gear-count and gear type) + /// </summary> private readonly string _gearboxStr; + + /// <summary> + /// The name of the job file (report name will be the same) + /// </summary> private readonly string _jobFile; + + /// <summary> + /// The result count determines how many results must be given before the report gets written. + /// </summary> private readonly int _resultCount; + + /// <summary> + /// The base path of the application + /// </summary> private readonly string _basePath; + /// <summary> + /// Initializes a new instance of the <see cref="DeclarationReport"/> class. + /// </summary> + /// <param name="flc">The full load curve.</param> + /// <param name="segment">The segment of the current vehicle from the segment table.</param> + /// <param name="creator">The creator name.</param> + /// <param name="engineModel">The engine model.</param> + /// <param name="engineStr">The engine description string.</param> + /// <param name="gearboxModel">The gearbox model.</param> + /// <param name="gearboxStr">The gearbox description string.</param> + /// <param name="basePath">The base path.</param> + /// <param name="jobFile">The name of the job file.</param> + /// <param name="resultCount">The result count which defines after how many finished results the report gets written.</param> public DeclarationReport(FullLoadCurve flc, Segment segment, string creator, string engineModel, string engineStr, string gearboxModel, string gearboxStr, string basePath, string jobFile, int resultCount) { @@ -55,6 +117,13 @@ namespace TUGraz.VectoCore.Models.Declaration _basePath = basePath; } + + /// <summary> + /// Adds the result of one run for the specific mission and loading. If all runs finished (given by the resultCount) the report will be written. + /// </summary> + /// <param name="loadingType">Type of the loading.</param> + /// <param name="mission">The mission.</param> + /// <param name="modData">The mod data.</param> [MethodImpl(MethodImplOptions.Synchronized)] public void AddResult(LoadingType loadingType, Mission mission, IModalDataWriter modData) { @@ -72,22 +141,33 @@ namespace TUGraz.VectoCore.Models.Declaration } } + + /// <summary> + /// Creates the report and writes it to a pdf file. + /// </summary> private void WriteReport() { var titlePage = CreateTitlePage(_missions); - var cyclePages = _missions.Select((m, i) => CreateCyclePage(m.Value, i, _missions.Count)); + var cyclePages = _missions.OrderBy(m => m.Key).Select((m, i) => CreateCyclePage(m.Value, i + 2, _missions.Count + 1)); MergeDocuments(titlePage, cyclePages, Path.Combine(_basePath, _jobFile + ".pdf")); } + + /// <summary> + /// Creates the title page. + /// </summary> + /// <param name="missions">The missions.</param> + /// <returns>the out stream of the pdf stamper with the title page.</returns> private Stream CreateTitlePage(Dictionary<MissionType, ResultContainer> missions) { var stream = new MemoryStream(); - var reader = - new PdfReader( - RessourceHelper.ReadStream(RessourceHelper.Namespace + - string.Format("Report.title{0}CyclesTemplate.pdf", missions.Count))); + var resourceName = string.Format("{0}Report.title{1}CyclesTemplate.pdf", RessourceHelper.Namespace, missions.Count); + var inputStream = RessourceHelper.ReadStream(resourceName); + + var reader = new PdfReader(inputStream); + var stamper = new PdfStamper(reader, stream); var pdfFields = stamper.AcroFields; @@ -96,59 +176,66 @@ namespace TUGraz.VectoCore.Models.Declaration pdfFields.SetField("Date", DateTime.Now.ToString(CultureInfo.InvariantCulture)); pdfFields.SetField("Created", _creator); pdfFields.SetField("Config", - string.Format("{0:0.0}t {1} {2}", _segment.GrossVehicleMassRating / 1000, _segment.AxleConfiguration.GetName(), - _segment.VehicleCategory)); - pdfFields.SetField("HDVclass", "HDV Class " + _segment.VehicleClass); + string.Format(CultureInfo.InvariantCulture, "{0}t {1} {2}", + _segment.GrossVehicleMassRating.ConvertTo().Ton.ToOutputFormat(1), + _segment.AxleConfiguration.GetName(), _segment.VehicleCategory)); + pdfFields.SetField("HDVclass", "HDV Class " + _segment.VehicleClass.GetClassNumber()); pdfFields.SetField("Engine", _engineStr); pdfFields.SetField("EngM", _engineModel); pdfFields.SetField("Gearbox", _gearboxStr); pdfFields.SetField("GbxM", _gearboxModel); - pdfFields.SetField("PageNr", "Page 1 of " + missions.Count); + pdfFields.SetField("PageNr", string.Format("Page {0} of {1}", 1, missions.Count + 1)); + var i = 1; - foreach (var results in missions.Values) { + foreach (var results in missions.Values.OrderBy(m => m.Mission.MissionType)) { pdfFields.SetField("Mission" + i, results.Mission.MissionType.ToString()); var m = results.ModData[LoadingType.ReferenceLoad]; - var dt = m.GetValues<SI>(ModalResultField.simulationInterval); - var distance = m.GetValues<SI>(ModalResultField.dist).Max().Value(); + var dt = m.GetValues<Second>(ModalResultField.simulationInterval); + var maxDistance = m.GetValues<Meter>(ModalResultField.dist).Max(); + var maxTime = m.GetValues<Second>(ModalResultField.time).Max(); + + var avgSpeed = maxDistance / maxTime; - Func<ModalResultField, double> avgWeighted = - field => m.GetValues<SI>(field).Zip(dt, (v, t) => v / t).Average().Value(); + pdfFields.SetField("Loading" + i, results.Mission.RefLoad.ConvertTo().Ton.ToOutputFormat(1) + " t"); + pdfFields.SetField("Speed" + i, avgSpeed.ConvertTo().Kilo.Meter.Per.Hour.ToOutputFormat(1) + " km/h"); - pdfFields.SetField("Loading" + i, results.Mission.RefLoad.Value().ToString("0.0") + " t"); - pdfFields.SetField("Speed" + i, avgWeighted(ModalResultField.v_act).ToString("0.0") + " km/h"); + var fc = m.GetValues<KilogramPerSecond>(ModalResultField.FCMap); + var fcWeight = fc.Zip(dt, (fcVal, dtVal) => fcVal * dtVal).Sum(); + var fcVolume = (fcWeight / Physics.FuelDensity).Cast<CubicMeter>(); + var co2Weight = fcWeight * Physics.CO2PerFuelWeight; - var loading = results.Mission.RefLoad.Value(); + var fcAvgVolume = fcVolume / maxDistance; + var co2AvgWeight = co2Weight / maxDistance; - var fc = avgWeighted(ModalResultField.FCMap) / distance * 1000 * 100; + var loadingTon = results.Mission.RefLoad.ConvertTo().Ton; - // todo: calc co2 - var co2 = fc; + var fcLiterPer100Km = fcAvgVolume.ConvertTo().Cubic.Dezi.Meter * 100.SI().Kilo.Meter; + var fcLiterPer100Tonkm = fcLiterPer100Km / loadingTon; + var co2GrammPerKm = co2AvgWeight.ConvertTo().Gramm.Per.Kilo.Meter; + var co2GrammPerTonKm = co2GrammPerKm / loadingTon; - pdfFields.SetField("FC" + i, fc.ToString("0.0")); - pdfFields.SetField("FCt" + i, (fc / loading).ToString("0.0")); - pdfFields.SetField("CO2" + i, co2.ToString("0.0")); - pdfFields.SetField("CO2t" + i, (co2 / loading).ToString("0.0")); + pdfFields.SetField("FC" + i, fcLiterPer100Km.ToOutputFormat(1)); + pdfFields.SetField("FCt" + i, fcLiterPer100Tonkm.ToOutputFormat(1)); + pdfFields.SetField("CO2" + i, co2GrammPerKm.ToOutputFormat(1)); + pdfFields.SetField("CO2t" + i, co2GrammPerTonKm.ToOutputFormat(1)); i++; } // Add Images var content = stamper.GetOverContent(1); - var img = Image.GetInstance(DrawCO2MissionsChart(missions), BaseColor.WHITE); + var img = Image.GetInstance(DrawCo2MissionsChart(missions), BaseColor.WHITE); img.ScaleAbsolute(440, 195); img.SetAbsolutePosition(360, 270); content.AddImage(img); - img = Image.GetInstance(DrawCO2SpeedChart(missions), BaseColor.WHITE); + img = Image.GetInstance(DrawCo2SpeedChart(missions), BaseColor.WHITE); img.ScaleAbsolute(440, 195); img.SetAbsolutePosition(360, 75); content.AddImage(img); - img = - Image.GetInstance( - RessourceHelper.ReadStream(RessourceHelper.Namespace + "Report." + - GetHDVClassImageName(_segment, MissionType.LongHaul))); + img = GetVehicleImage(_segment, MissionType.LongHaul); img.ScaleAbsolute(180, 50); img.SetAbsolutePosition(30, 475); content.AddImage(img); @@ -160,26 +247,15 @@ namespace TUGraz.VectoCore.Models.Declaration return stream; } - private static string GetHDVClassImageName(Segment segment, MissionType missionType) - { - switch (segment.VehicleClass) { - case VehicleClass.Class1: - case VehicleClass.Class2: - case VehicleClass.Class3: - return "4x2r.png"; - case VehicleClass.Class4: - return missionType == MissionType.LongHaul ? "4x2rt.png" : "4x2r.png"; - case VehicleClass.Class5: - return "4x2tt.png"; - case VehicleClass.Class9: - return missionType == MissionType.LongHaul ? "6x2rt.png" : "6x2r.png"; - case VehicleClass.Class10: - return "6x2tt.png"; - } - return "Undef.png"; - } - private Stream CreateCyclePage(ResultContainer results, int i, int pgMax) + /// <summary> + /// Creates the cycle page. + /// </summary> + /// <param name="results">The results.</param> + /// <param name="currentPageNr">The current page nr.</param> + /// <param name="pageCount">The page count.</param> + /// <returns>the out stream of the pdfstamper for a single cycle page</returns> + private Stream CreateCyclePage(ResultContainer results, int currentPageNr, int pageCount) { var stream = new MemoryStream(); @@ -192,10 +268,10 @@ namespace TUGraz.VectoCore.Models.Declaration pdfFields.SetField("Date", DateTime.Now.ToString(CultureInfo.InvariantCulture)); pdfFields.SetField("Created", _creator); pdfFields.SetField("Config", - string.Format("{0:0.0}t {1} {2}", _segment.GrossVehicleMassRating / 1000, _segment.AxleConfiguration.GetName(), - _segment.VehicleCategory)); - pdfFields.SetField("HDVclass", "HDV Class " + _segment.VehicleClass); - pdfFields.SetField("PageNr", "Page " + (i + 1) + " of " + pgMax); + string.Format("{0}t {1} {2}", _segment.GrossVehicleMassRating.ConvertTo().Ton.ToOutputFormat(1), + _segment.AxleConfiguration.GetName(), _segment.VehicleCategory)); + pdfFields.SetField("HDVclass", "HDV Class " + _segment.VehicleClass.GetClassNumber()); + pdfFields.SetField("PageNr", string.Format("Page {0} of {1}", currentPageNr, pageCount)); pdfFields.SetField("Mission", results.Mission.MissionType.ToString()); @@ -203,42 +279,38 @@ namespace TUGraz.VectoCore.Models.Declaration var loadingType = pair.Key; var m = pair.Value; - var loadString = loadingType.GetShortName(); - pdfFields.SetField("Load" + loadString, results.Mission.Loadings[loadingType].Value().ToString("0.0") + " t"); - - var dt = m.GetValues<SI>(ModalResultField.simulationInterval); - var distance = m.GetValues<SI>(ModalResultField.dist).Max().Value(); - - Func<ModalResultField, double> avgWeighted = - field => m.GetValues<SI>(field).Zip(dt, (v, t) => v / t).Average().Value(); - - pdfFields.SetField("Speed" + loadString, avgWeighted(ModalResultField.v_act).ToString("0.0")); - - var fc = avgWeighted(ModalResultField.FCMap) / distance * 1000 * 100; + var dt = m.GetValues<Second>(ModalResultField.simulationInterval); + var maxDistance = m.GetValues<Meter>(ModalResultField.dist).Max(); + var maxTime = m.GetValues<Second>(ModalResultField.time).Max(); + var avgSpeed = maxDistance / maxTime; - // todo: calculate co2!! - var co2 = fc; + var fc = m.GetValues<KilogramPerSecond>(ModalResultField.FCMap); + var fcWeight = fc.Zip(dt, (fcVal, dtVal) => fcVal * dtVal).Sum(); + var fcVolume = (fcWeight / Physics.FuelDensity).Cast<CubicMeter>(); + var co2Weight = fcWeight * Physics.CO2PerFuelWeight; + var fcAvgVolume = fcVolume / maxDistance; + var co2AvgWeight = co2Weight / maxDistance; + var loadingTon = results.Mission.Loadings[loadingType].ConvertTo().Ton; - var loading = results.Mission.Loadings[loadingType].Value(); + var fcLiterPer100Km = fcAvgVolume.ConvertTo().Cubic.Dezi.Meter * 100.SI().Kilo.Meter; + var co2GrammPerKm = co2AvgWeight.ConvertTo().Gramm.Per.Kilo.Meter; - pdfFields.SetField("FCkm" + loadString, fc.ToString("0.0")); - pdfFields.SetField("CO2km" + loadString, co2.ToString("0.0")); + var loadString = loadingType.GetShortName(); - if (loading.IsEqual(0)) { - pdfFields.SetField("FCtkm" + loadString, "-"); - pdfFields.SetField("CO2tkm" + loadString, "-"); - } else { - pdfFields.SetField("FCtkm" + loadString, (fc / loading).ToString("0.0")); - pdfFields.SetField("CO2tkm" + loadString, (fc / loading).ToString("0.0")); - } + pdfFields.SetField("Load" + loadString, + results.Mission.Loadings[loadingType].ConvertTo().Ton.ToOutputFormat(1) + " t"); + pdfFields.SetField("Speed" + loadString, avgSpeed.ConvertTo().Kilo.Meter.Per.Hour.ToOutputFormat(1)); + pdfFields.SetField("FCkm" + loadString, fcLiterPer100Km.ToOutputFormat(1)); + pdfFields.SetField("FCtkm" + loadString, + loadingTon.IsEqual(0) ? "-" : (fcLiterPer100Km / loadingTon).ToOutputFormat(1)); + pdfFields.SetField("CO2km" + loadString, co2GrammPerKm.ToOutputFormat(1)); + pdfFields.SetField("CO2tkm" + loadString, + loadingTon.IsEqual(0) ? "-" : (co2GrammPerKm / loadingTon).ToOutputFormat(1)); } var content = stamper.GetOverContent(1); - var img = - Image.GetInstance( - RessourceHelper.ReadStream(RessourceHelper.Namespace + "Report." + - GetHDVClassImageName(_segment, results.Mission.MissionType))); + var img = GetVehicleImage(_segment, results.Mission.MissionType); img.ScaleAbsolute(180, 50); img.SetAbsolutePosition(600, 475); content.AddImage(img); @@ -260,6 +332,12 @@ namespace TUGraz.VectoCore.Models.Declaration return stream; } + /// <summary> + /// Merges the given stream to one document and writes it to a file on disk. + /// </summary> + /// <param name="titlePage">The title page.</param> + /// <param name="cyclePages">The cycle pages.</param> + /// <param name="outputFileName">Name of the output file.</param> private static void MergeDocuments(Stream titlePage, IEnumerable<Stream> cyclePages, string outputFileName) { var document = new Document(PageSize.A4.Rotate(), 12, 12, 12, 12); @@ -278,7 +356,12 @@ namespace TUGraz.VectoCore.Models.Declaration document.Close(); } - private static Bitmap DrawCO2MissionsChart(Dictionary<MissionType, ResultContainer> missions) + /// <summary> + /// Draws the co2 missions chart for the title page. + /// </summary> + /// <param name="missions">The missions.</param> + /// <returns></returns> + private static Bitmap DrawCo2MissionsChart(Dictionary<MissionType, ResultContainer> missions) { var co2Chart = new Chart { Width = 1500, Height = 700 }; co2Chart.Legends.Add(new Legend("main") { @@ -303,40 +386,53 @@ namespace TUGraz.VectoCore.Models.Declaration BorderWidth = 3 }); + foreach (var missionResult in missions.OrderBy(m => m.Key)) { + var m = missionResult.Value.ModData[LoadingType.ReferenceLoad]; + var fc = m.GetValues<KilogramPerSecond>(ModalResultField.FCMap); + var dt = m.GetValues<Second>(ModalResultField.simulationInterval); - foreach (var missionResult in missions) { - var series = new Series(missionResult.Key + " (Ref. load.)"); - var co2sum = missionResult.Value.ModData[LoadingType.ReferenceLoad].GetValues<SI>(ModalResultField.FCMap).Sum(); + var fcWeight = fc.Zip(dt, (fcValue, dtValue) => fcValue * dtValue).Sum(); - var maxDistance = missionResult.Value.ModData[LoadingType.ReferenceLoad].GetValues<SI>(ModalResultField.dist).Max(); + var maxDistance = m.GetValues<Meter>(ModalResultField.dist).Max(); var loading = missionResult.Value.Mission.Loadings[LoadingType.ReferenceLoad]; - var co2pertkm = co2sum / maxDistance / loading * 1000; - series.Points.AddXY(missionResult.Key.ToString(), co2pertkm.Value()); + var co2Weight = fcWeight * Physics.CO2PerFuelWeight; - series.Points[0].Label = series.Points[0].YValues[0].ToString("0.0") + " [g/tkm]"; - series.Points[0].Font = new Font("Helvetica", 20); - series.Points[0].LabelBackColor = Color.White; + var co2GPerTonKm = co2Weight.ConvertTo().Gramm / (maxDistance.ConvertTo().Kilo.Meter * loading.ConvertTo().Ton); + var series = new Series(missionResult.Key + " (Ref. load.)"); + var dataPoint = new DataPoint { + Name = missionResult.Key.ToString(), + YValues = new[] { co2GPerTonKm.Value() }, + Label = co2GPerTonKm.ToOutputFormat(1, showUnit: true), + Font = new Font("Helvetica", 20), + LabelBackColor = Color.White + }; + series.Points.Add(dataPoint); co2Chart.Series.Add(series); } co2Chart.Update(); - var chartCO2tkm = new Bitmap(co2Chart.Width, co2Chart.Height, PixelFormat.Format32bppArgb); - co2Chart.DrawToBitmap(chartCO2tkm, new Rectangle(0, 0, chartCO2tkm.Width, chartCO2tkm.Height)); - return chartCO2tkm; + var chartCo2Tkm = new Bitmap(co2Chart.Width, co2Chart.Height, PixelFormat.Format32bppArgb); + co2Chart.DrawToBitmap(chartCo2Tkm, new Rectangle(0, 0, chartCo2Tkm.Width, chartCo2Tkm.Height)); + return chartCo2Tkm; } - private static Bitmap DrawCO2SpeedChart(Dictionary<MissionType, ResultContainer> missions) + /// <summary> + /// Draws the co2 speed chart for the title page. + /// </summary> + /// <param name="missions">The missions.</param> + /// <returns></returns> + private static Bitmap DrawCo2SpeedChart(Dictionary<MissionType, ResultContainer> missions) { - var co2speedChart = new Chart { Width = 1500, Height = 700 }; - co2speedChart.Legends.Add(new Legend("main") { + var co2SpeedChart = new Chart { Width = 1500, Height = 700 }; + co2SpeedChart.Legends.Add(new Legend("main") { Font = new Font("Helvetica", 20), BorderColor = Color.Black, BorderWidth = 3, }); - co2speedChart.ChartAreas.Add(new ChartArea("main") { + co2SpeedChart.ChartAreas.Add(new ChartArea("main") { BorderDashStyle = ChartDashStyle.Solid, BorderWidth = 3, AxisX = { @@ -344,7 +440,8 @@ namespace TUGraz.VectoCore.Models.Declaration TitleFont = new Font("Helvetica", 20), LabelStyle = { Font = new Font("Helvetica", 20) }, LabelAutoFitStyle = LabelAutoFitStyles.None, - Minimum = 20.0, + Minimum = 0, + Maximum = 80 }, AxisY = { Title = "CO2 [g/km]", @@ -355,22 +452,29 @@ namespace TUGraz.VectoCore.Models.Declaration }); foreach (var missionResult in missions) { - var series = new Series { MarkerSize = 15, MarkerStyle = MarkerStyle.Circle, ChartType = SeriesChartType.Point }; + var series = new Series { + MarkerSize = 15, + MarkerStyle = MarkerStyle.Circle, + ChartType = SeriesChartType.Point + }; foreach (var pair in missionResult.Value.ModData) { - var dt = pair.Value.GetValues<SI>(ModalResultField.simulationInterval).ToDouble(); - var speed = pair.Value.GetValues<SI>(ModalResultField.v_act).ToDouble(); - var distance = pair.Value.GetValues<SI>(ModalResultField.dist).Max().Value(); + var modData = missionResult.Value.ModData[pair.Key]; + var fc = modData.GetValues<KilogramPerSecond>(ModalResultField.FCMap); + var dt = modData.GetValues<Second>(ModalResultField.simulationInterval).ToList(); + + var fcSum = fc.Zip(dt, (fcValue, dtValue) => fcValue * dtValue).Sum(); - //todo get co2 value - var co2 = pair.Value.GetValues<SI>(ModalResultField.FCMap).ToDouble(); - var avgSpeed = speed.Zip(dt, (v, t) => v / t).Average(); - var avgCO2km = co2.Zip(dt, (co, t) => co / t).Average() / distance * 1000; + var maxDistance = modData.GetValues<Meter>(ModalResultField.dist).Max(); + var maxTime = modData.GetValues<Second>(ModalResultField.time).Max(); - var loading = missionResult.Value.Mission.Loadings[pair.Key]; + var avgKmh = maxDistance.ConvertTo().Kilo.Meter / maxTime.ConvertTo().Hour; + var co2GPerKm = fcSum.ConvertTo().Gramm * Physics.CO2PerFuelWeight / maxDistance.ConvertTo().Kilo.Meter; - var point = new DataPoint(avgSpeed, avgCO2km) { - Label = loading.Value().ToString("0.0") + " t", + var loading = missionResult.Value.Mission.Loadings[pair.Key].ConvertTo().Ton; + + var point = new DataPoint(avgKmh.Value(), co2GPerKm.Value()) { + Label = string.Format(CultureInfo.InvariantCulture, "{0:0.0} t", loading.Value()), Font = new Font("Helvetica", 16), LabelBackColor = Color.White }; @@ -382,15 +486,20 @@ namespace TUGraz.VectoCore.Models.Declaration series.Points.Add(point); } series.Name = missionResult.Key.ToString(); - co2speedChart.Series.Add(series); + co2SpeedChart.Series.Add(series); } - co2speedChart.Update(); - var chartCO2speed = new Bitmap(co2speedChart.Width, co2speedChart.Height, PixelFormat.Format32bppArgb); - co2speedChart.DrawToBitmap(chartCO2speed, new Rectangle(0, 0, co2speedChart.Width, co2speedChart.Height)); - return chartCO2speed; + co2SpeedChart.Update(); + var chartCo2Speed = new Bitmap(co2SpeedChart.Width, co2SpeedChart.Height, PixelFormat.Format32bppArgb); + co2SpeedChart.DrawToBitmap(chartCo2Speed, new Rectangle(0, 0, co2SpeedChart.Width, co2SpeedChart.Height)); + return chartCo2Speed; } + /// <summary> + /// Draws the cycle chart for a cycle page. + /// </summary> + /// <param name="results">The results.</param> + /// <returns></returns> private static Bitmap DrawCycleChart(ResultContainer results) { var missionCycleChart = new Chart { Width = 2000, Height = 400 }; @@ -426,7 +535,7 @@ namespace TUGraz.VectoCore.Models.Declaration MinorGrid = { Enabled = false }, MajorGrid = { Enabled = false } }, - Position = { X = 0f, Y = 0f, Width = 90f, Height = 100f }, + Position = { X = 0f, Y = 0f, Width = 90, Height = 100 }, }); var altitude = new Series { @@ -436,24 +545,24 @@ namespace TUGraz.VectoCore.Models.Declaration YAxisType = AxisType.Secondary }; - var distance = results.ModData.First().Value.GetValues<SI>(ModalResultField.dist).ToDouble(); - var altitudeValues = results.ModData.First().Value.GetValues<SI>(ModalResultField.altitude).ToDouble(); + var m = results.ModData.First().Value; + var distanceKm = m.GetValues<Meter>(ModalResultField.dist).Select(v => v.ConvertTo().Kilo.Meter).ToDouble(); - altitude.Points.DataBindXY(distance, altitudeValues); + altitude.Points.DataBindXY(distanceKm, m.GetValues<Meter>(ModalResultField.altitude).ToDouble()); missionCycleChart.Series.Add(altitude); var targetSpeed = new Series { ChartType = SeriesChartType.FastLine, BorderWidth = 3, Name = "Target speed" }; - targetSpeed.Points.DataBindXY(distance, - results.ModData.First().Value.GetValues<SI>(ModalResultField.v_targ).ToDouble()); + targetSpeed.Points.DataBindXY(distanceKm, + m.GetValues<MeterPerSecond>(ModalResultField.v_targ).Select(v => v.ConvertTo().Kilo.Meter.Per.Hour).ToDouble()); missionCycleChart.Series.Add(targetSpeed); foreach (var result in results.ModData) { var name = result.Key.ToString(); var values = result.Value; - var series = new Series { ChartType = SeriesChartType.FastLine, Name = name }; - series.Points.DataBindXY(values.GetValues<SI>(ModalResultField.dist).ToDouble(), - values.GetValues<SI>(ModalResultField.v_act).ToDouble()); + series.Points.DataBindXY( + values.GetValues<Meter>(ModalResultField.dist).Select(v => v.ConvertTo().Kilo.Meter).ToDouble(), + values.GetValues<MeterPerSecond>(ModalResultField.v_act).Select(v => v.ConvertTo().Kilo.Meter.Per.Hour).ToDouble()); missionCycleChart.Series.Add(series); } missionCycleChart.Update(); @@ -463,6 +572,12 @@ namespace TUGraz.VectoCore.Models.Declaration return cycleChart; } + /// <summary> + /// Draws the operating points chart for a cycle page. + /// </summary> + /// <param name="modData">The mod data.</param> + /// <param name="flc">The FLC.</param> + /// <returns></returns> private static Bitmap DrawOperatingPointsChart(IModalDataWriter modData, FullLoadCurve flc) { var operatingPointsChart = new Chart { Width = 1000, Height = 427 }; @@ -487,8 +602,7 @@ namespace TUGraz.VectoCore.Models.Declaration TitleFont = new Font("Helvetica", 20), LabelStyle = { Font = new Font("Helvetica", 20) }, LabelAutoFitStyle = LabelAutoFitStyles.None - }, - Position = { X = 0, Y = 0, Width = 70, Height = 100 } + } }); var n = flc.FullLoadEntries.Select(x => x.EngineSpeed.ConvertTo().Rounds.Per.Minute).ToDouble(); @@ -523,5 +637,39 @@ namespace TUGraz.VectoCore.Models.Declaration operatingPointsChart.DrawToBitmap(tqnBitmap, new Rectangle(0, 0, tqnBitmap.Width, tqnBitmap.Height)); return tqnBitmap; } + + /// <summary> + /// Gets the appropriate vehicle image. + /// </summary> + /// <param name="segment">The segment.</param> + /// <param name="missionType">Type of the mission.</param> + /// <returns></returns> + private static Image GetVehicleImage(Segment segment, MissionType missionType) + { + var name = "Undef.png"; + switch (segment.VehicleClass) { + case VehicleClass.Class1: + case VehicleClass.Class2: + case VehicleClass.Class3: + name = "4x2r.png"; + break; + case VehicleClass.Class4: + name = missionType == MissionType.LongHaul ? "4x2rt.png" : "4x2r.png"; + break; + case VehicleClass.Class5: + name = "4x2tt.png"; + break; + case VehicleClass.Class9: + name = missionType == MissionType.LongHaul ? "6x2rt.png" : "6x2r.png"; + break; + case VehicleClass.Class10: + name = "6x2tt.png"; + break; + } + + var hdvClassImagePath = RessourceHelper.Namespace + "Report." + name; + var hdvClassImage = RessourceHelper.ReadStream(hdvClassImagePath); + return Image.GetInstance(hdvClassImage); + } } } \ No newline at end of file diff --git a/VectoCore/Models/Declaration/VehicleClass.cs b/VectoCore/Models/Declaration/VehicleClass.cs index fd37847b015aec856203f13ebd6fe785bd256602..d87c74588530a78d45cecea546915500c03d24a1 100644 --- a/VectoCore/Models/Declaration/VehicleClass.cs +++ b/VectoCore/Models/Declaration/VehicleClass.cs @@ -39,9 +39,9 @@ namespace TUGraz.VectoCore.Models.Declaration return text.Replace(Prefix, "").Parse<VehicleClass>(); } - public static string ToString(VehicleClass hdvClass) + public static string GetClassNumber(this VehicleClass hdvClass) { - return Prefix + hdvClass; + return hdvClass.ToString().Substring(Prefix.Length); } } } \ No newline at end of file diff --git a/VectoCore/Models/Simulation/DataBus/IGearboxInfo.cs b/VectoCore/Models/Simulation/DataBus/IGearboxInfo.cs index 2ee60dd78788294eb48618f875cf694e4d56d1d7..317b5b042a1e74e07bc60275e20529d3d36e02a4 100644 --- a/VectoCore/Models/Simulation/DataBus/IGearboxInfo.cs +++ b/VectoCore/Models/Simulation/DataBus/IGearboxInfo.cs @@ -1,4 +1,5 @@ -using TUGraz.VectoCore.Utils; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.Simulation.DataBus { @@ -17,5 +18,7 @@ namespace TUGraz.VectoCore.Models.Simulation.DataBus MeterPerSecond StartSpeed { get; } MeterPerSquareSecond StartAcceleration { get; } + + FullLoadCurve GearFullLoadCurve { get; } } } \ No newline at end of file diff --git a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs index 492cb064d90f2a0c5519a1c81c577fb1509dc2c7..38a81a5d0513deb3ec1dff56d0aa71acdb5b9056 100644 --- a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs +++ b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs @@ -69,6 +69,11 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl } } + public FullLoadCurve GearFullLoadCurve + { + get { return Gearbox != null ? Gearbox.GearFullLoadCurve : null; } + } + #endregion #region IEngineCockpit diff --git a/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs b/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs index 251312db42a91cce4bd4898ec8f773213b860688..7043b40b3c9403723bb4da6c5ef2addca9d0aacb 100644 --- a/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs +++ b/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs @@ -61,11 +61,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine /// <param name="engineSpeed">[rad/sec]</param> /// <param name="torque">[Nm]</param> /// <returns>[kg/s]</returns> - public SI GetFuelConsumption(NewtonMeter torque, PerSecond engineSpeed) + public KilogramPerSecond GetFuelConsumption(NewtonMeter torque, PerSecond engineSpeed) { // delauney map needs is initialised with rpm, therefore the engineSpeed has to be converted. return - _fuelMap.Interpolate(torque.Value(), engineSpeed.ConvertTo().Rounds.Per.Minute.Value()).SI().Kilo.Gramm.Per.Second; + _fuelMap.Interpolate(torque.Value(), engineSpeed.ConvertTo().Rounds.Per.Minute.Value()) + .SI() + .Kilo.Gramm.Per.Second.Cast<KilogramPerSecond>(); } private static class Fields diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index 21781aa91f2040e45b0a86f0c6eea195ff20a8dd..2f03a9ebc611fc6e70f8d2cb42aebe84745a0a7a 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Resources; using NLog; using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Exceptions; @@ -40,7 +38,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected readonly Watt StationaryIdleFullLoadPower; /// <summary> - /// Current state is computed in request method + /// Current state is computed in request method /// </summary> internal EngineState CurrentState = new EngineState(); @@ -126,7 +124,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl ValidatePowerDemand(requestedEnginePower); - CurrentState.EnginePower = LimitEnginePower(requestedEnginePower); if (dryRun) { return new ResponseDryRun { @@ -136,12 +133,17 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl }; } - if (!CurrentState.EnginePower.IsEqual(requestedEnginePower, Constants.SimulationSettings.EnginePowerSearchTolerance)) { - var delta = (requestedEnginePower - CurrentState.EnginePower); - Log.Debug("requested engine power exceeds FLD: delta: {0}", delta); - return delta > 0 - ? new ResponseOverload { Delta = delta, EnginePowerRequest = requestedEnginePower, Source = this } - : new ResponseUnderload { Delta = delta, EnginePowerRequest = requestedEnginePower, Source = this }; + CurrentState.EnginePower = LimitEnginePower(requestedEnginePower); + var delta = requestedEnginePower - CurrentState.EnginePower; + + if (delta.IsGreater(0.SI<Watt>(), Constants.SimulationSettings.EnginePowerSearchTolerance)) { + Log.Debug("requested engine power exceeds fullload power: delta: {0}", delta); + return new ResponseOverload { Delta = delta, EnginePowerRequest = requestedEnginePower, Source = this }; + } + + if (delta.IsSmaller(0.SI<Watt>(), Constants.SimulationSettings.EnginePowerSearchTolerance)) { + Log.Debug("requested engine power is below drag power: delta: {0}", delta); + return new ResponseUnderload { Delta = delta, EnginePowerRequest = requestedEnginePower, Source = this }; } UpdateEngineState(CurrentState.EnginePower); @@ -217,7 +219,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } catch (VectoException ex) { Log.Warn("t: {0} - {1} n: {2} Tq: {3}", CurrentState.AbsTime, ex.Message, CurrentState.EngineSpeed, CurrentState.EngineTorque); - writer[ModalResultField.FCMap] = double.NaN.SI(); + writer[ModalResultField.FCMap] = double.NaN.SI<KilogramPerSecond>(); } } @@ -230,9 +232,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl #endregion /// <summary> - /// Validates the requested power demand [W]. + /// Validates the requested power demand [W]. /// </summary> - /// <param name="requestedEnginePower">[W]</param> protected virtual void ValidatePowerDemand(Watt requestedEnginePower) { if (CurrentState.FullDragPower >= 0 && requestedEnginePower < 0) { @@ -246,17 +247,23 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } /// <summary> - /// Limits the engine power to either DynamicFullLoadPower (upper bound) or FullDragPower (lower bound) + /// Limits the engine power to: FullDragPower <= requestedEnginePower %lt;= DynamicFullLoadPower /// </summary> protected virtual Watt LimitEnginePower(Watt requestedEnginePower) { + var curve = DataBus.GearFullLoadCurve; + if (curve != null) { + var gearboxFullLoad = curve.FullLoadStationaryTorque(CurrentState.EngineSpeed) * CurrentState.EngineSpeed; + var gearboxDragLoad = curve.DragLoadStationaryTorque(CurrentState.EngineSpeed) * CurrentState.EngineSpeed; + requestedEnginePower = VectoMath.Limit(requestedEnginePower, gearboxDragLoad, gearboxFullLoad); + } + return VectoMath.Limit(requestedEnginePower, CurrentState.FullDragPower, CurrentState.DynamicFullLoadPower); } /// <summary> - /// Updates the engine state dependend on the requested power [W]. + /// Updates the engine state dependend on the requested power [W]. /// </summary> - /// <param name="requestedEnginePower">[W]</param> protected virtual void UpdateEngineState(Watt requestedEnginePower) { if (requestedEnginePower < -ZeroThreshold) { @@ -276,8 +283,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// <summary> /// computes full load power from gear [-], angularVelocity [rad/s] and dt [s]. /// </summary> - /// <param name="angularVelocity">[rad/s]</param> - /// <param name="dt">[s]</param> protected void ComputeFullLoadPower(PerSecond angularVelocity, Second dt) { if (dt <= 0) { @@ -289,7 +294,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Data.FullLoadCurve.FullLoadStationaryTorque(angularVelocity); CurrentState.StationaryFullLoadPower = CurrentState.StationaryFullLoadTorque * angularVelocity; - double pt1 = Data.FullLoadCurve.PT1(angularVelocity).Value(); + var pt1 = Data.FullLoadCurve.PT1(angularVelocity).Value(); // var dynFullPowerCalculated = (1 / (pt1 + 1)) * // (_currentState.StationaryFullLoadPower + pt1 * _previousState.EnginePower); @@ -322,61 +327,32 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl { public EngineOperationMode OperationMode { get; set; } - /// <summary> - /// [s] - /// </summary> public Second AbsTime { get; set; } - /// <summary> - /// [W] - /// </summary> public Watt EnginePower { get; set; } /// <summary> - /// [rad/s] + /// [rad/s] /// </summary> public PerSecond EngineSpeed { get; set; } - /// <summary> - /// [W] - /// </summary> public Watt EnginePowerLoss { get; set; } - /// <summary> - /// [W] - /// </summary> public Watt StationaryFullLoadPower { get; set; } - /// <summary> - /// [W] - /// </summary> public Watt DynamicFullLoadPower { get; set; } - /// <summary> - /// [Nm] - /// </summary> public NewtonMeter StationaryFullLoadTorque { get; set; } - /// <summary> - /// [Nm] - /// </summary> public NewtonMeter DynamicFullLoadTorque { get; set; } - /// <summary> - /// [W] - /// </summary> public Watt FullDragPower { get; set; } - /// <summary> - /// [Nm] - /// </summary> public NewtonMeter FullDragTorque { get; set; } - /// <summary> - /// [Nm] - /// </summary> public NewtonMeter EngineTorque { get; set; } + // ReSharper disable once InconsistentNaming public Second dt { get; set; } #region Equality members @@ -457,8 +433,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected CombustionEngine Engine; - protected Second IdleStart = null; - protected Watt LastEnginePower = null; + protected Second IdleStart; + protected Watt LastEnginePower; public CombustionEngineIdleController(CombustionEngine combustionEngine) { @@ -485,7 +461,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl IdleStart = absTime; LastEnginePower = Engine.PreviousState.EnginePower; } - IResponse retVal = null; + IResponse retVal; var idleTime = absTime - IdleStart + dt; var prevEngineSpeed = Engine.PreviousState.EngineSpeed; @@ -532,8 +508,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Log.Info("Disabling logging during search idling speed"); LogManager.DisableLogging(); - var prevEngineSpeed = Engine.PreviousState.EngineSpeed; - var searchInterval = Constants.SimulationSettings.EngineIdlingSearchInterval; var intervalFactor = 1.0; diff --git a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs index d99723a8677d0b7ebaca16879856e8c997c5f50d..dace35c05d91a638e53177e8e04412bd1f98862e 100644 --- a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyCombustionEngine.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Models.Connector.Ports; using TUGraz.VectoCore.Models.Connector.Ports.Impl; using TUGraz.VectoCore.Models.Simulation; diff --git a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs index 6b2d83c94eb2659953aa9c02d827cdead2be0e6f..5cd48bba7beb326f779c868b7f316d44c9937af7 100644 --- a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs +++ b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs @@ -4,6 +4,7 @@ using TUGraz.VectoCore.Models.Connector.Ports; using TUGraz.VectoCore.Models.Simulation; using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.Simulation.DataBus; +using TUGraz.VectoCore.Models.SimulationComponent.Data; using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.SimulationComponent.Impl @@ -36,7 +37,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl uint IGearboxInfo.Gear { get { return 0; } - //set { } } public MeterPerSecond StartSpeed @@ -49,6 +49,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl get { throw new VectoSimulationException("Not Implemented: EngineOnlyGearbox has no StartAcceleration value."); } } + public FullLoadCurve GearFullLoadCurve + { + get { return null; } + } + #endregion #region ITnInPort diff --git a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs index 6ef4e8ce0bad8da5ba99907e161595c67f37faef..ebb6565f850fe214f61e10198c47d79b0d090c8e 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs @@ -103,6 +103,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl get { return Data.StartAcceleration; } } + public FullLoadCurve GearFullLoadCurve + { + get { return Gear == 0 ? null : Data.Gears[Gear].FullLoadCurve; } + } + #endregion #region ITnOutPort @@ -309,7 +314,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl return dryRunResponse; } - var shiftAllowed = !inEngineSpeed.IsEqual(0) && !DataBus.VehicleSpeed.IsEqual(0) && absTime.IsGreater(0); + var shiftAllowed = !inEngineSpeed.IsEqual(0) && !DataBus.VehicleSpeed.IsEqual(0); if (shiftAllowed) { var shiftRequired = _strategy.ShiftRequired(absTime, dt, outTorque, outAngularVelocity, inTorque, inEngineSpeed, diff --git a/VectoCore/Models/SimulationComponent/Impl/ShiftStrategy.cs b/VectoCore/Models/SimulationComponent/Impl/ShiftStrategy.cs index 3c0cb58e8dc87154e5c444b0b6ef09f712811638..26bca7212073d5b07740e8e0cfaa89858a680555 100644 --- a/VectoCore/Models/SimulationComponent/Impl/ShiftStrategy.cs +++ b/VectoCore/Models/SimulationComponent/Impl/ShiftStrategy.cs @@ -240,7 +240,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl // normal shift when all requirements are fullfilled ------------------ - var minimumShiftTimePassed = (lastShiftTime + Data.ShiftTime).IsSmaller(absTime); + var minimumShiftTimePassed = (lastShiftTime + Data.ShiftTime).IsSmallerOrEqual(absTime); if (!minimumShiftTimePassed) { return false; } diff --git a/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs b/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs index a5ac4cdc5657739facb9dc8ed99b49f72b4fe710..9d097f103a6bba40a5818986fa281825ea50c260 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Vehicle.cs @@ -125,7 +125,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl _currentState.dt = dt; _currentState.Acceleration = acceleration; _currentState.Velocity = _previousState.Velocity + acceleration * dt; - if (_currentState.Velocity.IsEqual(0, 1e-4)) { + if (_currentState.Velocity.IsEqual(0.SI<MeterPerSecond>(), Constants.SimulationSettings.VehicleSpeedHaltTolerance)) { _currentState.Velocity = 0.SI<MeterPerSecond>(); } _currentState.Distance = _previousState.Distance + dt * (_previousState.Velocity + _currentState.Velocity) / 2; diff --git a/VectoCore/Properties/AssemblyInfo.cs b/VectoCore/Properties/AssemblyInfo.cs index df0c9973ba0b690d98d43b7ea5751b41a9141eb5..b8cf9458fc88b48c18c0fe9f6859c38a679a0867 100644 --- a/VectoCore/Properties/AssemblyInfo.cs +++ b/VectoCore/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ using System.Runtime.InteropServices; // associated with an assembly. [assembly: AssemblyTitle("VectoCore")] -[assembly: AssemblyDescription("")] +[assembly: AssemblyDescription("Core Library for VECTO Simulations")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("VectoCore")] @@ -35,6 +35,6 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("3.0.*")] +[assembly: AssemblyFileVersion("3.0.0.0")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("VectoCoreTest")] \ No newline at end of file diff --git a/VectoCore/Utils/IEnumberableExtensionMethods.cs b/VectoCore/Utils/IEnumberableExtensionMethods.cs index d4fdb9fd98923178d8fa0281c83dee6dcdeca63e..1ffc5b57fe5933d844a1524260ea7a7fb41eb727 100644 --- a/VectoCore/Utils/IEnumberableExtensionMethods.cs +++ b/VectoCore/Utils/IEnumberableExtensionMethods.cs @@ -43,6 +43,12 @@ namespace TUGraz.VectoCore.Utils } } + public static T Sum<T>(this IEnumerable<T> values) where T : SIBase<T> + { + var valueList = values.ToList(); + return valueList.Any() ? valueList.Aggregate((sum, current) => sum + current) : null; + } + public static SI Sum(this IEnumerable<SI> values) { var valueList = values.ToList(); diff --git a/VectoCore/Utils/IntExtensionMethods.cs b/VectoCore/Utils/IntExtensionMethods.cs index 902593ee8b40bd23b6e88f4d5ab8e31707ccb1ac..13dc8b28b644e38b772a08b081cc473f866521f8 100644 --- a/VectoCore/Utils/IntExtensionMethods.cs +++ b/VectoCore/Utils/IntExtensionMethods.cs @@ -10,12 +10,14 @@ namespace TUGraz.VectoCore.Utils /// </summary> /// <param name="d"></param> /// <returns></returns> + [DebuggerHidden] public static PerSecond RPMtoRad(this int d) { return (d * 2.0 * Math.PI / 60.0).SI<PerSecond>(); //d.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second.Cast<PerSecond>(); } + [DebuggerHidden] public static MeterPerSecond KMPHtoMeterPerSecond(this int d) { return (d / 3.6).SI<MeterPerSecond>(); @@ -24,8 +26,9 @@ namespace TUGraz.VectoCore.Utils /// <summary> - /// Gets the SI representation of the number (unit-less). + /// Gets the unit-less SI representation of the number. /// </summary> + [DebuggerHidden] public static SI SI(this int value) { return new SI(value); diff --git a/VectoCore/Utils/Physics.cs b/VectoCore/Utils/Physics.cs index 08cf111224e5271449d131696887c4c1574eb2e6..c814e3c0e53e058a8901faf55f7e27045914bcf1 100644 --- a/VectoCore/Utils/Physics.cs +++ b/VectoCore/Utils/Physics.cs @@ -4,10 +4,24 @@ { public static readonly MeterPerSquareSecond GravityAccelleration = 9.81.SI<MeterPerSquareSecond>(); + /// <summary> + /// Density of air. + /// </summary> public static readonly SI AirDensity = 1.188.SI().Kilo.Gramm.Per.Cubic.Meter; + /// <summary> + /// Density of fuel. + /// </summary> + public static readonly SI FuelDensity = 0.832.SI().Kilo.Gramm.Per.Cubic.Dezi.Meter; + public static readonly double RollResistanceExponent = 0.9; public static readonly MeterPerSecond BaseWindSpeed = 3.SI<MeterPerSecond>(); + + + /// <summary> + /// Factor to convert from fuel weight to co2 weight. + /// </summary> + public static readonly double CO2PerFuelWeight = 3.16; } } \ No newline at end of file diff --git a/VectoCore/Utils/SI.cs b/VectoCore/Utils/SI.cs index f4f772d28f6e0fd870f76572ba0507d6670ad3bc..40035c76940b770ed6542f39f848a66b019115e8 100644 --- a/VectoCore/Utils/SI.cs +++ b/VectoCore/Utils/SI.cs @@ -229,6 +229,22 @@ namespace TUGraz.VectoCore.Utils } } + /// <summary> + /// SI Class for Kilogram per Second [kg]. + /// </summary> + public class KilogramPerSecond : SIBase<KilogramPerSecond> + { + [JsonConstructor, DebuggerHidden] + protected KilogramPerSecond(double val) : base(new SI(val).Kilo.Gramm.Per.Second) {} + + [DebuggerHidden] + public static Kilogram operator *(KilogramPerSecond kilogramPerSecond, Second second) + { + return ((kilogramPerSecond as SI) * second).Cast<Kilogram>(); + } + } + + /// <summary> /// SI Class for Ton [t] (automatically converts to [kg]) /// </summary> @@ -737,6 +753,7 @@ namespace TUGraz.VectoCore.Utils Percent, min, c, + d, h, milli, t @@ -1074,7 +1091,6 @@ namespace TUGraz.VectoCore.Utils [DebuggerHidden] get { return new SI(new SI(this, toUnit: Unit.k), 1000, Unit.t, Unit.g); } } - /// <summary> /// [N] /// </summary> @@ -1166,6 +1182,15 @@ namespace TUGraz.VectoCore.Utils [DebuggerHidden] get { return new SI(this, 1000.0, Unit.k); } } + /// <summary> + /// Quantifier for Dezi (1/10) + /// </summary> + [DebuggerHidden] + public SI Dezi + { + [DebuggerHidden] get { return new SI(this, 0.1, Unit.d); } + } + /// <summary> /// Quantifier for Centi (1/100) /// </summary> diff --git a/VectoCoreTest/Integration/DeclarationReportTest.cs b/VectoCoreTest/Integration/DeclarationReportTest.cs index f720b4e69349761056c105979b7a8b33b441f156..a2b6d3d6b0e917d3d402ac3bddf65e4dd4255040 100644 --- a/VectoCoreTest/Integration/DeclarationReportTest.cs +++ b/VectoCoreTest/Integration/DeclarationReportTest.cs @@ -8,7 +8,7 @@ namespace TUGraz.VectoCore.Tests.Integration public class DeclarationReportTest { [TestMethod] - public void RunDeclarationMode() + public void DeclarationReport_Test() { var sumWriter = new SummaryFileWriter(@"job-report.vsum"); var jobContainer = new JobContainer(sumWriter); diff --git a/VectoCoreTest/Models/SimulationComponent/GearboxPowertrainTest.cs b/VectoCoreTest/Models/SimulationComponent/GearboxPowertrainTest.cs index 4044afa74958160c843faa54b6c84f39d17ca786..6ea7988b0ef6e101385e6d9fefe28a8cd6d6de05 100644 --- a/VectoCoreTest/Models/SimulationComponent/GearboxPowertrainTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/GearboxPowertrainTest.cs @@ -1,8 +1,5 @@ -using System.IO; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TUGraz.VectoCore.FileIO.Reader; +using Microsoft.VisualStudio.TestTools.UnitTesting; using TUGraz.VectoCore.Models.Connector.Ports.Impl; -using TUGraz.VectoCore.Models.SimulationComponent.Data; using TUGraz.VectoCore.Tests.Integration; using TUGraz.VectoCore.Tests.Utils; using TUGraz.VectoCore.Utils; @@ -23,7 +20,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var container = Truck40tPowerTrain.CreatePowerTrain(cycle, "Gearbox_Initialize.vmod", 7500.0.SI<Kilogram>(), 0.SI<Kilogram>()); var retVal = container.Cycle.Initialize(); - Assert.AreEqual(5u, container.Gear); + Assert.AreEqual(4u, container.Gear); Assert.IsInstanceOfType(retVal, typeof(ResponseSuccess)); AssertHelper.AreRelativeEqual(560.RPMtoRad(), container.EngineSpeed); @@ -97,6 +94,5 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent container.CommitSimulationStep(absTime, retVal.SimulationInterval); absTime += retVal.SimulationInterval; } - } } \ No newline at end of file diff --git a/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs b/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs index d3b6c7d158b0814f0e1fc6b58b5b6c9c77d46d34..bf03b90d46a275b6dfcd3972f429055e77523ff4 100644 --- a/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/GearboxTest.cs @@ -115,14 +115,14 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var gearboxData = DeclarationModeSimulationDataReader.CreateGearboxDataFromFile(GearboxDataFile, EngineDataFile); var container = new VehicleContainer(); var gearbox = new Gearbox(container, gearboxData, new AMTShiftStrategy(gearboxData, container)); - gearbox.Gear = 1; - var ratio = 6.38; - var driver = new MockDriver(container); var port = new MockTnOutPort(); gearbox.InPort().Connect(port); container.Engine = port; + gearbox.Initialize(0.SI<NewtonMeter>(), 0.RPMtoRad()); + + var ratio = 6.38; var absTime = 0.SI<Second>(); var dt = 2.SI<Second>(); var t = 2600.SI<NewtonMeter>(); @@ -148,13 +148,13 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var container = new VehicleContainer(); var gearboxData = DeclarationModeSimulationDataReader.CreateGearboxDataFromFile(GearboxDataFile, EngineDataFile); var gearbox = new Gearbox(container, gearboxData, new AMTShiftStrategy(gearboxData, container)); - var driver = new MockDriver(container); var port = new MockTnOutPort(); gearbox.InPort().Connect(port); container.Engine = port; - gearbox.Gear = 1; + + gearbox.Initialize(0.SI<NewtonMeter>(), 0.RPMtoRad()); var ratio = 6.38; @@ -167,8 +167,8 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent foreach (var exp in expected) { var torque = exp.t.SI<NewtonMeter>() * ratio; var angularVelocity = exp.n.RPMtoRad() / ratio; - var response = gearbox.OutPort().Request(0.SI<Second>(), 1.SI<Second>(), torque, angularVelocity); + Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); } @@ -262,6 +262,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var driver = new MockDriver(container); var port = new MockTnOutPort(); gearbox.InPort().Connect(port); + var vehicle = new MockVehicle(container) { MyVehicleSpeed = 10.SI<MeterPerSecond>() }; container.Engine = port; var ratios = new[] { 0.0, 6.38, 4.63, 3.44, 2.59, 1.86, 1.35, 1, 0.76 }; @@ -276,16 +277,13 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent new { gear = 3, newGear = 2, t = 1500, n = 750, responseType = typeof(ResponseGearShift) }, new { gear = 2, newGear = 1, t = 1500, n = 750, responseType = typeof(ResponseGearShift) }, new { gear = 1, newGear = 1, t = 1200, n = 700, responseType = typeof(ResponseSuccess) }, - new { gear = 8, newGear = 1, t = 10000, n = 120, responseType = typeof(ResponseGearShift) } + new { gear = 8, newGear = 1, t = 15000, n = 50, responseType = typeof(ResponseGearShift) } }; var absTime = 0.SI<Second>(); var dt = 2.SI<Second>(); - gearbox.OutPort().Initialize(0.SI<NewtonMeter>(), 0.SI<PerSecond>()); - - // just for test case mock driver - driver.VehicleStopped = false; + gearbox.OutPort().Initialize(1.SI<NewtonMeter>(), 1.SI<PerSecond>()); foreach (var exp in expected) { var expectedT = exp.t.SI<NewtonMeter>(); @@ -313,6 +311,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var gearboxData = DeclarationModeSimulationDataReader.CreateGearboxDataFromFile(GearboxDataFile, EngineDataFile); var gearbox = new Gearbox(container, gearboxData, new AMTShiftStrategy(gearboxData, container)); var driver = new MockDriver(container); + var vehicle = new MockVehicle(container) { MyVehicleSpeed = 10.SI<MeterPerSecond>() }; var port = new MockTnOutPort(); container.Engine = port; gearbox.InPort().Connect(port); @@ -335,8 +334,8 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var absTime = 0.SI<Second>(); var dt = 2.SI<Second>(); - gearbox.OutPort().Initialize(1000.SI<NewtonMeter>(), 100.SI<PerSecond>()); - var first = gearbox.OutPort().Request(absTime, dt, 1000.SI<NewtonMeter>(), 100.SI<PerSecond>()); + gearbox.OutPort().Initialize(1.SI<NewtonMeter>(), 1.SI<PerSecond>()); + absTime += dt; foreach (var exp in expected) { var expectedT = exp.t.SI<NewtonMeter>(); @@ -356,49 +355,5 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent absTime += dt; } } - - [TestMethod] - public void Gearbox_NoGear() - { - var container = new VehicleContainer(); - var gearboxData = CreateGearboxData(); - var gearbox = new Gearbox(container, gearboxData, new AMTShiftStrategy(gearboxData, container)); - - var driver = new MockDriver(container); - - var port = new MockTnOutPort(); - container.Engine = (IEngineInfo)port; - gearbox.InPort().Connect(port); - - gearbox.Initialize(0.SI<NewtonMeter>(), 0.SI<PerSecond>()); - - var absTime = 0.SI<Second>(); - var dt = 2.SI<Second>(); - - //just for test driver - driver.VehicleStopped = true; - - var response = gearbox.OutPort().Request(absTime, dt, 50.SI<NewtonMeter>(), 1000000.RPMtoRad()); - Assert.IsInstanceOfType(response, typeof(ResponseSuccess)); - AssertHelper.AreRelativeEqual(absTime, port.AbsTime); - AssertHelper.AreRelativeEqual(dt, port.Dt); - Assert.IsNotNull(port.AngularVelocity); - Assert.IsNotNull(port.Torque); - Assert.IsTrue(port.AngularVelocity.IsGreater(0)); - Assert.IsTrue(port.Torque.IsGreater(0)); - - port.DoCommitSimulationStep(); - - driver.VehicleStopped = false; - - absTime += dt; - - response = gearbox.OutPort().Request(absTime, dt, 5000.SI<NewtonMeter>(), 100.SI<PerSecond>()); - Assert.IsInstanceOfType(response, typeof(ResponseGearShift)); - Assert.IsNull(port.AbsTime); - Assert.IsNull(port.Dt); - Assert.IsNull(port.AngularVelocity); - Assert.IsNull(port.Torque); - } } } \ No newline at end of file diff --git a/VectoCoreTest/Utils/MockDriver.cs b/VectoCoreTest/Utils/MockDriver.cs index 971d6f394cb576e16bdd9c185c78e8308982e1be..10183c6a3048401df165403935c958208235a784 100644 --- a/VectoCoreTest/Utils/MockDriver.cs +++ b/VectoCoreTest/Utils/MockDriver.cs @@ -86,7 +86,7 @@ namespace TUGraz.VectoCore.Tests.Utils public DrivingBehavior DrivingBehavior { - get { throw new NotImplementedException(); } + get { return DrivingBehavior.Accelerating; } } } } \ No newline at end of file diff --git a/VectoCoreTest/Utils/MockGearbox.cs b/VectoCoreTest/Utils/MockGearbox.cs index 010629543985787f6f7ed85bde9aacf3427f9f3f..ed9e7f6f2c7152586a5ff83dc5de7720f4c15961 100644 --- a/VectoCoreTest/Utils/MockGearbox.cs +++ b/VectoCoreTest/Utils/MockGearbox.cs @@ -4,6 +4,7 @@ using TUGraz.VectoCore.Models.Simulation; using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.Simulation.DataBus; using TUGraz.VectoCore.Models.SimulationComponent; +using TUGraz.VectoCore.Models.SimulationComponent.Data; using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Tests.Utils @@ -36,6 +37,11 @@ namespace TUGraz.VectoCore.Tests.Utils get { return 0.6.SI<MeterPerSquareSecond>(); } } + public FullLoadCurve GearFullLoadCurve + { + get { return null; } + } + public void Connect(ITnOutPort other) {