From c4e53244afba221b6f8e1ddda4ff4a5e9c54e959 Mon Sep 17 00:00:00 2001 From: Markus Quaritsch <markus.quaritsch@tugraz.at> Date: Tue, 27 Jun 2017 17:32:05 +0200 Subject: [PATCH] refactoring to reduce complexity metric: 48 down to 16 (max complexity), --- .../DeclarationDataAdapter.cs | 898 +++++++------- .../EngineeringDataAdapter.cs | 678 +++++------ .../Models/Simulation/Data/VectoRunData.cs | 368 +++--- .../Simulation/Impl/SimulatorFactory.cs | 361 +++--- .../SimulationComponent/Impl/ATGearbox.cs | 773 ++++++------ .../Impl/ATShiftStrategy.cs | 664 ++++++----- .../SimulationComponent/Impl/CycleGearbox.cs | 1002 ++++++++-------- .../Impl/DefaultDriverStrategy.cs | 705 ++++++----- .../Impl/DistanceBasedDrivingCycle.cs | 1056 +++++++++-------- .../Impl/MeasuredSpeedDrivingCycle.cs | 687 +++++------ .../OutputData/SummaryDataContainer.cs | 805 +++++++------ VectoCore/VectoCore/Utils/VectoCSVFile.cs | 8 +- 12 files changed, 4114 insertions(+), 3891 deletions(-) diff --git a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs index df3cc851b0..4e58f0cc81 100644 --- a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs +++ b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs @@ -29,450 +29,456 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Collections.Generic; -using System.Linq; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.InputData; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.InputData.Reader.ComponentData; -using TUGraz.VectoCore.Models.Declaration; -using TUGraz.VectoCore.Models.Simulation.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine; -using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; -using TUGraz.VectoCore.OutputData; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter -{ - public class DeclarationDataAdapter : AbstractSimulationDataAdapter - { - public DriverData CreateDriverData(IDriverDeclarationInputData data) - { - if (!data.SavedInDeclarationMode) { - WarnDeclarationMode("DriverData"); - } - var lookAheadData = new DriverData.LACData { - Enabled = DeclarationData.Driver.LookAhead.Enabled, - //Deceleration = DeclarationData.Driver.LookAhead.Deceleration, - MinSpeed = DeclarationData.Driver.LookAhead.MinimumSpeed, - LookAheadDecisionFactor = new LACDecisionFactor(), - LookAheadDistanceFactor = DeclarationData.Driver.LookAhead.LookAheadDistanceFactor, - }; - var overspeedData = new DriverData.OverSpeedEcoRollData { - Mode = data.OverSpeedEcoRoll.Mode, - MinSpeed = DeclarationData.Driver.OverSpeedEcoRoll.MinSpeed, - OverSpeed = DeclarationData.Driver.OverSpeedEcoRoll.OverSpeed, - UnderSpeed = DeclarationData.Driver.OverSpeedEcoRoll.UnderSpeed - }; - if (!DeclarationData.Driver.OverSpeedEcoRoll.AllowedModes.Contains(overspeedData.Mode)) { - throw new VectoSimulationException( - "Specified Overspeed/EcoRoll Mode not allowed in declaration mode! {0}", - overspeedData.Mode); - } - var retVal = new DriverData { - LookAheadCoasting = lookAheadData, - OverSpeedEcoRoll = overspeedData, - }; - return retVal; - } - - internal VehicleData CreateVehicleData(IVehicleDeclarationInputData data, Mission mission, Kilogram loading, - Kilogram municipalBodyWeight) - { - if (!data.SavedInDeclarationMode) { - WarnDeclarationMode("VehicleData"); - } - - var retVal = SetCommonVehicleData(data); - retVal.VIN = data.VIN; - retVal.ManufacturerAddress = data.ManufacturerAddress; - retVal.LegislativeClass = data.LegislativeClass; - retVal.TrailerGrossVehicleWeight = mission.Trailer.Sum(t => t.TrailerGrossVehicleWeight).DefaultIfNull(0); - - retVal.BodyAndTrailerWeight = (mission.MissionType == MissionType.MunicipalUtility - ? municipalBodyWeight - : mission.BodyCurbWeight) + mission.Trailer.Sum(t => t.TrailerCurbWeight).DefaultIfNull(0); - //retVal.CurbWeight += retVal.BodyAndTrailerWeight; - - retVal.Loading = loading; - var drivenIndex = DrivenAxleIndex(data.Axles); - retVal.DynamicTyreRadius = - DeclarationData.Wheels.Lookup(data.Axles[drivenIndex].Wheels).DynamicTyreRadius; - retVal.CargoVolume = mission.MissionType != MissionType.Construction ? mission.TotalCargoVolume : 0.SI<CubicMeter>(); - - - var axles = data.Axles; - if (axles.Count < mission.AxleWeightDistribution.Length) { - throw new VectoException("Vehicle does not contain sufficient axles. {0} axles defined, {1} axles required", - data.Axles.Count, mission.AxleWeightDistribution.Length); - } - var axleData = new List<Axle>(); - for (var i = 0; i < mission.AxleWeightDistribution.Length; i++) { - var axleInput = axles[i]; - var axle = new Axle { - WheelsDimension = axleInput.Wheels, - AxleType = axleInput.AxleType, - AxleWeightShare = mission.AxleWeightDistribution[i], - TwinTyres = axleInput.TwinTyres, - RollResistanceCoefficient = axleInput.RollResistanceCoefficient, - TyreTestLoad = axleInput.TyreTestLoad, - Inertia = DeclarationData.Wheels.Lookup(axleInput.Wheels.RemoveWhitespace()).Inertia, - }; - axleData.Add(axle); - } - - foreach (var trailer in mission.Trailer) { - axleData.AddRange(trailer.TrailerWheels.Select(trailerWheel => new Axle { - AxleType = AxleType.Trailer, - AxleWeightShare = trailer.TrailerAxleWeightShare / trailer.TrailerWheels.Count, - TwinTyres = DeclarationData.Trailer.TwinTyres, - RollResistanceCoefficient = DeclarationData.Trailer.RollResistanceCoefficient, - TyreTestLoad = DeclarationData.Trailer.TyreTestLoad.SI<Newton>(), - Inertia = trailerWheel.Inertia - })); - } - retVal.AxleData = axleData; - return retVal; - } - - private static int DrivenAxleIndex(IList<IAxleDeclarationInputData> axles) - { - for (var i = 0; i < axles.Count; i++) { - if (axles[i].AxleType != AxleType.VehicleDriven) { - continue; - } - return i; - } - return DeclarationData.PoweredAxle(); - } - - internal CombustionEngineData CreateEngineData(IEngineDeclarationInputData engine, PerSecond vehicleEngineIdleSpeed, - IGearboxDeclarationInputData gearbox, IEnumerable<ITorqueLimitInputData> torqueLimits) - { - if (!engine.SavedInDeclarationMode) { - WarnDeclarationMode("EngineData"); - } - - var retVal = SetCommonCombustionEngineData(engine); - retVal.IdleSpeed = VectoMath.Max(engine.IdleSpeed, vehicleEngineIdleSpeed); - retVal.WHTCUrban = engine.WHTCUrban; - retVal.WHTCMotorway = engine.WHTCMotorway; - retVal.WHTCRural = engine.WHTCRural; - retVal.ColdHotCorrectionFactor = engine.ColdHotBalancingFactor; - retVal.Inertia = DeclarationData.Engine.EngineInertia(retVal.Displacement, gearbox.Type); - var limits = torqueLimits.ToDictionary(e => e.Gear); - var numGears = gearbox.Gears.Count; - var fullLoadCurves = new Dictionary<uint, EngineFullLoadCurve>(numGears + 1); - fullLoadCurves[0] = FullLoadCurveReader.Create(engine.FullLoadCurve, true); - fullLoadCurves[0].EngineData = retVal; - foreach (var gear in gearbox.Gears) { - var maxTorque = VectoMath.Min( - GbxMaxTorque(gear, numGears, fullLoadCurves[0].MaxTorque), - VehMaxTorque(gear, numGears, limits, fullLoadCurves[0].MaxTorque)); - fullLoadCurves[(uint)gear.Gear] = IntersectFullLoadCurves(fullLoadCurves[0], maxTorque); - } - retVal.FullLoadCurves = fullLoadCurves; - return retVal; - } - - private static NewtonMeter VehMaxTorque(ITransmissionInputData gear, int numGears, - Dictionary<int, ITorqueLimitInputData> limits, - NewtonMeter maxEngineTorque) - { - if (gear.Gear - 1 >= numGears / 2) { - // only upper half of gears can limit if max-torque <= 0.95 of engine max torque - if (limits.ContainsKey(gear.Gear) && - limits[gear.Gear].MaxTorque <= DeclarationData.Engine.TorqueLimitVehicleFactor * maxEngineTorque) { - return limits[gear.Gear].MaxTorque; - } - } - return null; - } - - private static NewtonMeter GbxMaxTorque(ITransmissionInputData gear, int numGears, NewtonMeter maxEngineTorque - ) - { - if (gear.Gear - 1 < numGears / 2) { - // gears count from 1 to n, -> n entries, lower half can always limit - if (gear.MaxTorque != null) { - return gear.MaxTorque; - } - } else { - // upper half can only limit if max-torque <= 90% of engine max torque - if (gear.MaxTorque != null && gear.MaxTorque <= DeclarationData.Engine.TorqueLimitGearboxFactor * maxEngineTorque) { - return gear.MaxTorque; - } - } - return null; - } - - internal GearboxData CreateGearboxData(IGearboxDeclarationInputData gearbox, CombustionEngineData engine, - double axlegearRatio, Meter dynamicTyreRadius, bool useEfficiencyFallback) - { - if (!gearbox.SavedInDeclarationMode) { - WarnDeclarationMode("GearboxData"); - } - var retVal = SetCommonGearboxData(gearbox); - switch (gearbox.Type) { - case GearboxType.DrivingCycle: - case GearboxType.ATPowerSplit: - throw new VectoSimulationException( - "Unsupported gearbox type: {0}!", retVal.Type); - //case GearboxType.Custom: - // throw new VectoSimulationException("Custom Transmission not supported in DeclarationMode!"); - } - var gearsInput = gearbox.Gears; - if (gearsInput.Count < 1) { - throw new VectoSimulationException( - "At least one Gear-Entry must be defined in Gearbox!"); - } - - SetDeclarationData(retVal); - - var gearDifferenceRatio = gearbox.Type.AutomaticTransmission() && gearbox.Gears.Count > 2 - ? gearbox.Gears[0].Ratio / gearbox.Gears[1].Ratio - : 1.0; - - var gears = new Dictionary<uint, GearData>(); - var tcShiftPolygon = DeclarationData.TorqueConverter.ComputeShiftPolygon(engine.FullLoadCurves[0]); - for (uint i = 0; i < gearsInput.Count; i++) { - var gear = gearsInput[(int)i]; - var lossMap = CreateGearLossMap(gear, i, useEfficiencyFallback, true); - - var shiftPolygon = DeclarationData.Gearbox.ComputeShiftPolygon(gearbox.Type, (int)i, engine.FullLoadCurves[i + 1], - gearsInput, engine, - axlegearRatio, dynamicTyreRadius); - - var gearData = new GearData { - ShiftPolygon = shiftPolygon, - MaxSpeed = gear.MaxInputSpeed, - Ratio = gear.Ratio, - LossMap = lossMap, - }; - - if (gearbox.Type == GearboxType.ATPowerSplit && i == 0) { - // powersplit transmission: torque converter already contains ratio and losses - CretateTCFirstGearATPowerSplit(gearData, i, tcShiftPolygon); - } - if (gearbox.Type == GearboxType.ATSerial) { - if (i == 0) { - // torqueconverter is active in first gear - duplicate ratio and lossmap for torque converter mode - CreateTCFirstGearATSerial(gearData, tcShiftPolygon); - } - if (i == 1 && gearDifferenceRatio >= DeclarationData.Gearbox.TorqueConverterSecondGearThreshold) { - // ratio between first and second gear is above threshold, torqueconverter is active in second gear as well - // -> duplicate ratio and lossmap for torque converter mode, remove locked transmission for previous gear - CreateTCSecondGearATSerial(gearData, tcShiftPolygon); - // NOTE: the lower gear in 'gears' dictionary has index i !! - gears[i].Ratio = double.NaN; - gears[i].LossMap = null; - } - } - gears.Add(i + 1, gearData); - } - retVal.Gears = gears; - if (retVal.Type.AutomaticTransmission()) { - var ratio = double.IsNaN(retVal.Gears[1].Ratio) ? 1 : retVal.Gears[1].TorqueConverterRatio / retVal.Gears[1].Ratio; - retVal.PowershiftShiftTime = DeclarationData.Gearbox.PowershiftShiftTime; - retVal.TorqueConverterData = TorqueConverterDataReader.Create(gearbox.TorqueConverter.TCData, - DeclarationData.TorqueConverter.ReferenceRPM, DeclarationData.TorqueConverter.MaxInputSpeed, - ExecutionMode.Declaration, ratio, - DeclarationData.TorqueConverter.CLUpshiftMinAcceleration, DeclarationData.TorqueConverter.CCUpshiftMinAcceleration); - } - - return retVal; - } - - private static void SetDeclarationData(GearboxData retVal) - { - retVal.Inertia = DeclarationData.Gearbox.Inertia; - retVal.TractionInterruption = retVal.Type.TractionInterruption(); - retVal.TorqueReserve = DeclarationData.Gearbox.TorqueReserve; - retVal.StartTorqueReserve = DeclarationData.Gearbox.TorqueReserveStart; - retVal.ShiftTime = DeclarationData.Gearbox.MinTimeBetweenGearshifts; - retVal.StartSpeed = DeclarationData.Gearbox.StartSpeed; - retVal.StartAcceleration = DeclarationData.Gearbox.StartAcceleration; - retVal.DownshiftAfterUpshiftDelay = DeclarationData.Gearbox.DownshiftAfterUpshiftDelay; - retVal.UpshiftAfterDownshiftDelay = DeclarationData.Gearbox.UpshiftAfterDownshiftDelay; - retVal.UpshiftMinAcceleration = DeclarationData.Gearbox.UpshiftMinAcceleration; - } - - public IList<VectoRunData.AuxData> CreateAuxiliaryData(IAuxiliariesDeclarationInputData auxInputData, - MissionType mission, VehicleClass hvdClass) - { - if (!auxInputData.SavedInDeclarationMode) { - WarnDeclarationMode("AuxiliariesData"); - } - var retVal = new List<VectoRunData.AuxData>(); - - if (auxInputData.Auxiliaries.Count != 5) { - Log.Error( - "In Declaration Mode exactly 5 Auxiliaries must be defined: Fan, Steering pump, HVAC, Electric System, Pneumatic System."); - throw new VectoException( - "In Declaration Mode exactly 5 Auxiliaries must be defined: Fan, Steering pump, HVAC, Electric System, Pneumatic System."); - } - - foreach (var auxType in EnumHelper.GetValues<AuxiliaryType>()) { - var auxData = auxInputData.Auxiliaries.FirstOrDefault(a => a.Type == auxType); - if (auxData == null) { - throw new VectoException("Auxiliary {0} not found.", auxType); - } - var aux = new VectoRunData.AuxData { - DemandType = AuxiliaryDemandType.Constant, - Technology = auxData.Technology - }; - - mission = mission.GetNonEMSMissionType(); - switch (auxType) { - case AuxiliaryType.Fan: - aux.PowerDemand = DeclarationData.Fan.Lookup(mission, auxData.Technology.FirstOrDefault()); - aux.ID = Constants.Auxiliaries.IDs.Fan; - break; - case AuxiliaryType.SteeringPump: - aux.PowerDemand = DeclarationData.SteeringPump.Lookup(mission, hvdClass, auxData.Technology); - aux.ID = Constants.Auxiliaries.IDs.SteeringPump; - break; - case AuxiliaryType.HVAC: - aux.PowerDemand = DeclarationData.HeatingVentilationAirConditioning.Lookup(mission, - auxData.Technology.FirstOrDefault(), hvdClass); - aux.ID = Constants.Auxiliaries.IDs.HeatingVentilationAirCondition; - break; - case AuxiliaryType.PneumaticSystem: - aux.PowerDemand = DeclarationData.PneumaticSystem.Lookup(mission, auxData.Technology.FirstOrDefault()); - aux.ID = Constants.Auxiliaries.IDs.PneumaticSystem; - break; - case AuxiliaryType.ElectricSystem: - aux.PowerDemand = DeclarationData.ElectricSystem.Lookup(mission, auxData.Technology.FirstOrDefault()); - aux.ID = Constants.Auxiliaries.IDs.ElectricSystem; - break; - default: - continue; - } - retVal.Add(aux); - } - return retVal; - } - - private void WarnDeclarationMode(string inputData) - { - Log.Warn("{0} not in Declaration Mode!", inputData); - } - - public RetarderData CreateRetarderData(IRetarderInputData retarder) - { - return SetCommonRetarderData(retarder); - } - - public static List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> GetDeclarationAirResistanceCurve( - string crosswindCorrectionParameters, SquareMeter aerodynamicDragAera, Meter vehicleHeight) - { - const int startSpeed = 60; - const int maxSpeed = 130; - const int speedStep = 5; - - const int maxAlpha = 180; - const int alphaStep = 10; - - const int startHeightPercent = 5; - const int maxHeightPercent = 100; - const int heightPercentStep = 10; - const double heightShare = (double)heightPercentStep / maxHeightPercent; - - var values = DeclarationData.AirDrag.Lookup(crosswindCorrectionParameters); - - // first entry (0m/s) will get CdxA of second entry. - var points = new List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> { - new CrossWindCorrectionCurveReader.CrossWindCorrectionEntry { - Velocity = 0.SI<MeterPerSecond>(), - EffectiveCrossSectionArea = 0.SI<SquareMeter>() - } - }; - - for (var speed = startSpeed; speed <= maxSpeed; speed += speedStep) { - var vVeh = speed.KMPHtoMeterPerSecond(); - - var cdASum = 0.SI<SquareMeter>(); - - for (var heightPercent = startHeightPercent; heightPercent < maxHeightPercent; heightPercent += heightPercentStep) { - var height = heightPercent / 100.0 * vehicleHeight; - var vWind = Physics.BaseWindSpeed * Math.Pow(height / Physics.BaseWindHeight, Physics.HellmannExponent); - - for (var alpha = 0; alpha <= maxAlpha; alpha += alphaStep) { - var vAirX = vVeh + vWind * Math.Cos(alpha.ToRadian()); - var vAirY = vWind * Math.Sin(alpha.ToRadian()); - - var beta = Math.Atan(vAirY / vAirX).ToDegree(); - - // ΔCdxA = A1β + A2β² + A3β³ - var deltaCdA = values.A1 * beta + values.A2 * beta * beta + values.A3 * beta * beta * beta; - - // CdxA(β) = CdxA(0) + ΔCdxA(β) - var cdA = aerodynamicDragAera + deltaCdA; - - var share = (alpha == 0 || alpha == maxAlpha ? alphaStep / 2.0 : alphaStep) / maxAlpha; - - // v_air = sqrt(v_airX²+vAirY²) - // cdASum = CdxA(β) * v_air²/v_veh² - cdASum += heightShare * share * cdA * (vAirX * vAirX + vAirY * vAirY) / (vVeh * vVeh); - } - } - points.Add(new CrossWindCorrectionCurveReader.CrossWindCorrectionEntry { - Velocity = vVeh, - EffectiveCrossSectionArea = cdASum - }); - } - - points[0].EffectiveCrossSectionArea = points[1].EffectiveCrossSectionArea; - return points; - } - - public PTOData CreatePTOTransmissionData(IPTOTransmissionInputData pto) - { - if (pto.PTOTransmissionType != "None") { - return new PTOData { - TransmissionType = pto.PTOTransmissionType, - LossMap = PTOIdleLossMapReader.GetZeroLossMap(), - }; - } - - return null; - } - - public AirdragData CreateAirdragData(IAirdragDeclarationInputData airdragInputData, Mission mission, - Segment segment) - { - if (airdragInputData == null || airdragInputData.AirDragArea == null) { - return DefaultAirdragData(mission, segment); - } - - var retVal = SetCommonAirdragData(airdragInputData); - var aerodynamicDragArea = airdragInputData.AirDragArea + mission.Trailer.Sum(t => t.DeltaCdA).DefaultIfNull(0); - - retVal.DeclaredAirdragArea = airdragInputData.AirDragArea; - retVal.CrossWindCorrectionCurve = - new CrosswindCorrectionCdxALookup(aerodynamicDragArea, - GetDeclarationAirResistanceCurve(mission.CrossWindCorrectionParameters, aerodynamicDragArea, segment.VehicleHeight), - CrossWindCorrectionMode.DeclarationModeCorrection); - return retVal; - } - - private AirdragData DefaultAirdragData(Mission mission, Segment segment) - { - var aerodynamicDragArea = mission.MissionType == MissionType.Construction - ? segment.CdAConstruction - : segment.CdADefault + mission.Trailer.Sum(t => t.DeltaCdA).DefaultIfNull(0); - - return new AirdragData() { - CertificationMethod = CertificationMethod.StandardValues, - DeclaredAirdragArea = mission.MissionType == MissionType.Construction ? segment.CdAConstruction : segment.CdADefault, - CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup(aerodynamicDragArea, - GetDeclarationAirResistanceCurve(mission.CrossWindCorrectionParameters, aerodynamicDragArea, segment.VehicleHeight), - CrossWindCorrectionMode.DeclarationModeCorrection) - }; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.InputData; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.InputData.Reader.ComponentData; +using TUGraz.VectoCore.Models.Declaration; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; +using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter +{ + public class DeclarationDataAdapter : AbstractSimulationDataAdapter + { + public DriverData CreateDriverData(IDriverDeclarationInputData data) + { + if (!data.SavedInDeclarationMode) { + WarnDeclarationMode("DriverData"); + } + var lookAheadData = new DriverData.LACData { + Enabled = DeclarationData.Driver.LookAhead.Enabled, + //Deceleration = DeclarationData.Driver.LookAhead.Deceleration, + MinSpeed = DeclarationData.Driver.LookAhead.MinimumSpeed, + LookAheadDecisionFactor = new LACDecisionFactor(), + LookAheadDistanceFactor = DeclarationData.Driver.LookAhead.LookAheadDistanceFactor, + }; + var overspeedData = new DriverData.OverSpeedEcoRollData { + Mode = data.OverSpeedEcoRoll.Mode, + MinSpeed = DeclarationData.Driver.OverSpeedEcoRoll.MinSpeed, + OverSpeed = DeclarationData.Driver.OverSpeedEcoRoll.OverSpeed, + UnderSpeed = DeclarationData.Driver.OverSpeedEcoRoll.UnderSpeed + }; + if (!DeclarationData.Driver.OverSpeedEcoRoll.AllowedModes.Contains(overspeedData.Mode)) { + throw new VectoSimulationException( + "Specified Overspeed/EcoRoll Mode not allowed in declaration mode! {0}", + overspeedData.Mode); + } + var retVal = new DriverData { + LookAheadCoasting = lookAheadData, + OverSpeedEcoRoll = overspeedData, + }; + return retVal; + } + + internal VehicleData CreateVehicleData(IVehicleDeclarationInputData data, Mission mission, Kilogram loading, + Kilogram municipalBodyWeight) + { + if (!data.SavedInDeclarationMode) { + WarnDeclarationMode("VehicleData"); + } + + var retVal = SetCommonVehicleData(data); + retVal.VIN = data.VIN; + retVal.ManufacturerAddress = data.ManufacturerAddress; + retVal.LegislativeClass = data.LegislativeClass; + retVal.TrailerGrossVehicleWeight = mission.Trailer.Sum(t => t.TrailerGrossVehicleWeight).DefaultIfNull(0); + + retVal.BodyAndTrailerWeight = (mission.MissionType == MissionType.MunicipalUtility + ? municipalBodyWeight + : mission.BodyCurbWeight) + mission.Trailer.Sum(t => t.TrailerCurbWeight).DefaultIfNull(0); + //retVal.CurbWeight += retVal.BodyAndTrailerWeight; + + retVal.Loading = loading; + var drivenIndex = DrivenAxleIndex(data.Axles); + retVal.DynamicTyreRadius = + DeclarationData.Wheels.Lookup(data.Axles[drivenIndex].Wheels).DynamicTyreRadius; + retVal.CargoVolume = mission.MissionType != MissionType.Construction ? mission.TotalCargoVolume : 0.SI<CubicMeter>(); + + + var axles = data.Axles; + if (axles.Count < mission.AxleWeightDistribution.Length) { + throw new VectoException("Vehicle does not contain sufficient axles. {0} axles defined, {1} axles required", + data.Axles.Count, mission.AxleWeightDistribution.Length); + } + var axleData = new List<Axle>(); + for (var i = 0; i < mission.AxleWeightDistribution.Length; i++) { + var axleInput = axles[i]; + var axle = new Axle { + WheelsDimension = axleInput.Wheels, + AxleType = axleInput.AxleType, + AxleWeightShare = mission.AxleWeightDistribution[i], + TwinTyres = axleInput.TwinTyres, + RollResistanceCoefficient = axleInput.RollResistanceCoefficient, + TyreTestLoad = axleInput.TyreTestLoad, + Inertia = DeclarationData.Wheels.Lookup(axleInput.Wheels.RemoveWhitespace()).Inertia, + }; + axleData.Add(axle); + } + + foreach (var trailer in mission.Trailer) { + axleData.AddRange(trailer.TrailerWheels.Select(trailerWheel => new Axle { + AxleType = AxleType.Trailer, + AxleWeightShare = trailer.TrailerAxleWeightShare / trailer.TrailerWheels.Count, + TwinTyres = DeclarationData.Trailer.TwinTyres, + RollResistanceCoefficient = DeclarationData.Trailer.RollResistanceCoefficient, + TyreTestLoad = DeclarationData.Trailer.TyreTestLoad.SI<Newton>(), + Inertia = trailerWheel.Inertia + })); + } + retVal.AxleData = axleData; + return retVal; + } + + private static int DrivenAxleIndex(IList<IAxleDeclarationInputData> axles) + { + for (var i = 0; i < axles.Count; i++) { + if (axles[i].AxleType != AxleType.VehicleDriven) { + continue; + } + return i; + } + return DeclarationData.PoweredAxle(); + } + + internal CombustionEngineData CreateEngineData(IEngineDeclarationInputData engine, PerSecond vehicleEngineIdleSpeed, + IGearboxDeclarationInputData gearbox, IEnumerable<ITorqueLimitInputData> torqueLimits) + { + if (!engine.SavedInDeclarationMode) { + WarnDeclarationMode("EngineData"); + } + + var retVal = SetCommonCombustionEngineData(engine); + retVal.IdleSpeed = VectoMath.Max(engine.IdleSpeed, vehicleEngineIdleSpeed); + retVal.WHTCUrban = engine.WHTCUrban; + retVal.WHTCMotorway = engine.WHTCMotorway; + retVal.WHTCRural = engine.WHTCRural; + retVal.ColdHotCorrectionFactor = engine.ColdHotBalancingFactor; + retVal.Inertia = DeclarationData.Engine.EngineInertia(retVal.Displacement, gearbox.Type); + var limits = torqueLimits.ToDictionary(e => e.Gear); + var numGears = gearbox.Gears.Count; + var fullLoadCurves = new Dictionary<uint, EngineFullLoadCurve>(numGears + 1); + fullLoadCurves[0] = FullLoadCurveReader.Create(engine.FullLoadCurve, true); + fullLoadCurves[0].EngineData = retVal; + foreach (var gear in gearbox.Gears) { + var maxTorque = VectoMath.Min( + GbxMaxTorque(gear, numGears, fullLoadCurves[0].MaxTorque), + VehMaxTorque(gear, numGears, limits, fullLoadCurves[0].MaxTorque)); + fullLoadCurves[(uint)gear.Gear] = IntersectFullLoadCurves(fullLoadCurves[0], maxTorque); + } + retVal.FullLoadCurves = fullLoadCurves; + return retVal; + } + + private static NewtonMeter VehMaxTorque(ITransmissionInputData gear, int numGears, + Dictionary<int, ITorqueLimitInputData> limits, + NewtonMeter maxEngineTorque) + { + if (gear.Gear - 1 >= numGears / 2) { + // only upper half of gears can limit if max-torque <= 0.95 of engine max torque + if (limits.ContainsKey(gear.Gear) && + limits[gear.Gear].MaxTorque <= DeclarationData.Engine.TorqueLimitVehicleFactor * maxEngineTorque) { + return limits[gear.Gear].MaxTorque; + } + } + return null; + } + + private static NewtonMeter GbxMaxTorque(ITransmissionInputData gear, int numGears, NewtonMeter maxEngineTorque + ) + { + if (gear.Gear - 1 < numGears / 2) { + // gears count from 1 to n, -> n entries, lower half can always limit + if (gear.MaxTorque != null) { + return gear.MaxTorque; + } + } else { + // upper half can only limit if max-torque <= 90% of engine max torque + if (gear.MaxTorque != null && gear.MaxTorque <= DeclarationData.Engine.TorqueLimitGearboxFactor * maxEngineTorque) { + return gear.MaxTorque; + } + } + return null; + } + + internal GearboxData CreateGearboxData(IGearboxDeclarationInputData gearbox, CombustionEngineData engine, + double axlegearRatio, Meter dynamicTyreRadius, bool useEfficiencyFallback) + { + if (!gearbox.SavedInDeclarationMode) { + WarnDeclarationMode("GearboxData"); + } + var retVal = SetCommonGearboxData(gearbox); + switch (gearbox.Type) { + case GearboxType.DrivingCycle: + case GearboxType.ATPowerSplit: + throw new VectoSimulationException( + "Unsupported gearbox type: {0}!", retVal.Type); + //case GearboxType.Custom: + // throw new VectoSimulationException("Custom Transmission not supported in DeclarationMode!"); + } + var gearsInput = gearbox.Gears; + if (gearsInput.Count < 1) { + throw new VectoSimulationException( + "At least one Gear-Entry must be defined in Gearbox!"); + } + + SetDeclarationData(retVal); + + var gearDifferenceRatio = gearbox.Type.AutomaticTransmission() && gearbox.Gears.Count > 2 + ? gearbox.Gears[0].Ratio / gearbox.Gears[1].Ratio + : 1.0; + + var gears = new Dictionary<uint, GearData>(); + var tcShiftPolygon = DeclarationData.TorqueConverter.ComputeShiftPolygon(engine.FullLoadCurves[0]); + for (uint i = 0; i < gearsInput.Count; i++) { + var gear = gearsInput[(int)i]; + var lossMap = CreateGearLossMap(gear, i, useEfficiencyFallback, true); + + var shiftPolygon = DeclarationData.Gearbox.ComputeShiftPolygon(gearbox.Type, (int)i, engine.FullLoadCurves[i + 1], + gearsInput, engine, + axlegearRatio, dynamicTyreRadius); + + var gearData = new GearData { + ShiftPolygon = shiftPolygon, + MaxSpeed = gear.MaxInputSpeed, + Ratio = gear.Ratio, + LossMap = lossMap, + }; + + CreateATGearData(gearbox, i, gearData, tcShiftPolygon, gearDifferenceRatio, gears); + gears.Add(i + 1, gearData); + } + retVal.Gears = gears; + if (retVal.Type.AutomaticTransmission()) { + var ratio = double.IsNaN(retVal.Gears[1].Ratio) ? 1 : retVal.Gears[1].TorqueConverterRatio / retVal.Gears[1].Ratio; + retVal.PowershiftShiftTime = DeclarationData.Gearbox.PowershiftShiftTime; + retVal.TorqueConverterData = TorqueConverterDataReader.Create(gearbox.TorqueConverter.TCData, + DeclarationData.TorqueConverter.ReferenceRPM, DeclarationData.TorqueConverter.MaxInputSpeed, + ExecutionMode.Declaration, ratio, + DeclarationData.TorqueConverter.CLUpshiftMinAcceleration, DeclarationData.TorqueConverter.CCUpshiftMinAcceleration); + } + + return retVal; + } + + private static void CreateATGearData(IGearboxDeclarationInputData gearbox, uint i, GearData gearData, + ShiftPolygon tcShiftPolygon, double gearDifferenceRatio, Dictionary<uint, GearData> gears) + { + if (gearbox.Type == GearboxType.ATPowerSplit && i == 0) { + // powersplit transmission: torque converter already contains ratio and losses + CretateTCFirstGearATPowerSplit(gearData, i, tcShiftPolygon); + } + if (gearbox.Type == GearboxType.ATSerial) { + if (i == 0) { + // torqueconverter is active in first gear - duplicate ratio and lossmap for torque converter mode + CreateTCFirstGearATSerial(gearData, tcShiftPolygon); + } + if (i == 1 && gearDifferenceRatio >= DeclarationData.Gearbox.TorqueConverterSecondGearThreshold) { + // ratio between first and second gear is above threshold, torqueconverter is active in second gear as well + // -> duplicate ratio and lossmap for torque converter mode, remove locked transmission for previous gear + CreateTCSecondGearATSerial(gearData, tcShiftPolygon); + // NOTE: the lower gear in 'gears' dictionary has index i !! + gears[i].Ratio = double.NaN; + gears[i].LossMap = null; + } + } + } + + private static void SetDeclarationData(GearboxData retVal) + { + retVal.Inertia = DeclarationData.Gearbox.Inertia; + retVal.TractionInterruption = retVal.Type.TractionInterruption(); + retVal.TorqueReserve = DeclarationData.Gearbox.TorqueReserve; + retVal.StartTorqueReserve = DeclarationData.Gearbox.TorqueReserveStart; + retVal.ShiftTime = DeclarationData.Gearbox.MinTimeBetweenGearshifts; + retVal.StartSpeed = DeclarationData.Gearbox.StartSpeed; + retVal.StartAcceleration = DeclarationData.Gearbox.StartAcceleration; + retVal.DownshiftAfterUpshiftDelay = DeclarationData.Gearbox.DownshiftAfterUpshiftDelay; + retVal.UpshiftAfterDownshiftDelay = DeclarationData.Gearbox.UpshiftAfterDownshiftDelay; + retVal.UpshiftMinAcceleration = DeclarationData.Gearbox.UpshiftMinAcceleration; + } + + public IList<VectoRunData.AuxData> CreateAuxiliaryData(IAuxiliariesDeclarationInputData auxInputData, + MissionType mission, VehicleClass hvdClass) + { + if (!auxInputData.SavedInDeclarationMode) { + WarnDeclarationMode("AuxiliariesData"); + } + var retVal = new List<VectoRunData.AuxData>(); + + if (auxInputData.Auxiliaries.Count != 5) { + Log.Error( + "In Declaration Mode exactly 5 Auxiliaries must be defined: Fan, Steering pump, HVAC, Electric System, Pneumatic System."); + throw new VectoException( + "In Declaration Mode exactly 5 Auxiliaries must be defined: Fan, Steering pump, HVAC, Electric System, Pneumatic System."); + } + + foreach (var auxType in EnumHelper.GetValues<AuxiliaryType>()) { + var auxData = auxInputData.Auxiliaries.FirstOrDefault(a => a.Type == auxType); + if (auxData == null) { + throw new VectoException("Auxiliary {0} not found.", auxType); + } + var aux = new VectoRunData.AuxData { + DemandType = AuxiliaryDemandType.Constant, + Technology = auxData.Technology + }; + + mission = mission.GetNonEMSMissionType(); + switch (auxType) { + case AuxiliaryType.Fan: + aux.PowerDemand = DeclarationData.Fan.Lookup(mission, auxData.Technology.FirstOrDefault()); + aux.ID = Constants.Auxiliaries.IDs.Fan; + break; + case AuxiliaryType.SteeringPump: + aux.PowerDemand = DeclarationData.SteeringPump.Lookup(mission, hvdClass, auxData.Technology); + aux.ID = Constants.Auxiliaries.IDs.SteeringPump; + break; + case AuxiliaryType.HVAC: + aux.PowerDemand = DeclarationData.HeatingVentilationAirConditioning.Lookup(mission, + auxData.Technology.FirstOrDefault(), hvdClass); + aux.ID = Constants.Auxiliaries.IDs.HeatingVentilationAirCondition; + break; + case AuxiliaryType.PneumaticSystem: + aux.PowerDemand = DeclarationData.PneumaticSystem.Lookup(mission, auxData.Technology.FirstOrDefault()); + aux.ID = Constants.Auxiliaries.IDs.PneumaticSystem; + break; + case AuxiliaryType.ElectricSystem: + aux.PowerDemand = DeclarationData.ElectricSystem.Lookup(mission, auxData.Technology.FirstOrDefault()); + aux.ID = Constants.Auxiliaries.IDs.ElectricSystem; + break; + default: + continue; + } + retVal.Add(aux); + } + return retVal; + } + + private void WarnDeclarationMode(string inputData) + { + Log.Warn("{0} not in Declaration Mode!", inputData); + } + + public RetarderData CreateRetarderData(IRetarderInputData retarder) + { + return SetCommonRetarderData(retarder); + } + + public static List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> GetDeclarationAirResistanceCurve( + string crosswindCorrectionParameters, SquareMeter aerodynamicDragAera, Meter vehicleHeight) + { + const int startSpeed = 60; + const int maxSpeed = 130; + const int speedStep = 5; + + const int maxAlpha = 180; + const int alphaStep = 10; + + const int startHeightPercent = 5; + const int maxHeightPercent = 100; + const int heightPercentStep = 10; + const double heightShare = (double)heightPercentStep / maxHeightPercent; + + var values = DeclarationData.AirDrag.Lookup(crosswindCorrectionParameters); + + // first entry (0m/s) will get CdxA of second entry. + var points = new List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> { + new CrossWindCorrectionCurveReader.CrossWindCorrectionEntry { + Velocity = 0.SI<MeterPerSecond>(), + EffectiveCrossSectionArea = 0.SI<SquareMeter>() + } + }; + + for (var speed = startSpeed; speed <= maxSpeed; speed += speedStep) { + var vVeh = speed.KMPHtoMeterPerSecond(); + + var cdASum = 0.SI<SquareMeter>(); + + for (var heightPercent = startHeightPercent; heightPercent < maxHeightPercent; heightPercent += heightPercentStep) { + var height = heightPercent / 100.0 * vehicleHeight; + var vWind = Physics.BaseWindSpeed * Math.Pow(height / Physics.BaseWindHeight, Physics.HellmannExponent); + + for (var alpha = 0; alpha <= maxAlpha; alpha += alphaStep) { + var vAirX = vVeh + vWind * Math.Cos(alpha.ToRadian()); + var vAirY = vWind * Math.Sin(alpha.ToRadian()); + + var beta = Math.Atan(vAirY / vAirX).ToDegree(); + + // ΔCdxA = A1β + A2β² + A3β³ + var deltaCdA = values.A1 * beta + values.A2 * beta * beta + values.A3 * beta * beta * beta; + + // CdxA(β) = CdxA(0) + ΔCdxA(β) + var cdA = aerodynamicDragAera + deltaCdA; + + var share = (alpha == 0 || alpha == maxAlpha ? alphaStep / 2.0 : alphaStep) / maxAlpha; + + // v_air = sqrt(v_airX²+vAirY²) + // cdASum = CdxA(β) * v_air²/v_veh² + cdASum += heightShare * share * cdA * (vAirX * vAirX + vAirY * vAirY) / (vVeh * vVeh); + } + } + points.Add(new CrossWindCorrectionCurveReader.CrossWindCorrectionEntry { + Velocity = vVeh, + EffectiveCrossSectionArea = cdASum + }); + } + + points[0].EffectiveCrossSectionArea = points[1].EffectiveCrossSectionArea; + return points; + } + + public PTOData CreatePTOTransmissionData(IPTOTransmissionInputData pto) + { + if (pto.PTOTransmissionType != "None") { + return new PTOData { + TransmissionType = pto.PTOTransmissionType, + LossMap = PTOIdleLossMapReader.GetZeroLossMap(), + }; + } + + return null; + } + + public AirdragData CreateAirdragData(IAirdragDeclarationInputData airdragInputData, Mission mission, + Segment segment) + { + if (airdragInputData == null || airdragInputData.AirDragArea == null) { + return DefaultAirdragData(mission, segment); + } + + var retVal = SetCommonAirdragData(airdragInputData); + var aerodynamicDragArea = airdragInputData.AirDragArea + mission.Trailer.Sum(t => t.DeltaCdA).DefaultIfNull(0); + + retVal.DeclaredAirdragArea = airdragInputData.AirDragArea; + retVal.CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(aerodynamicDragArea, + GetDeclarationAirResistanceCurve(mission.CrossWindCorrectionParameters, aerodynamicDragArea, segment.VehicleHeight), + CrossWindCorrectionMode.DeclarationModeCorrection); + return retVal; + } + + private AirdragData DefaultAirdragData(Mission mission, Segment segment) + { + var aerodynamicDragArea = mission.MissionType == MissionType.Construction + ? segment.CdAConstruction + : segment.CdADefault + mission.Trailer.Sum(t => t.DeltaCdA).DefaultIfNull(0); + + return new AirdragData() { + CertificationMethod = CertificationMethod.StandardValues, + DeclaredAirdragArea = mission.MissionType == MissionType.Construction ? segment.CdAConstruction : segment.CdADefault, + CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup(aerodynamicDragArea, + GetDeclarationAirResistanceCurve(mission.CrossWindCorrectionParameters, aerodynamicDragArea, segment.VehicleHeight), + CrossWindCorrectionMode.DeclarationModeCorrection) + }; + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs index 50c771198b..f5af5e2dcd 100644 --- a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs +++ b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/EngineeringDataAdapter.cs @@ -29,340 +29,346 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Collections.Generic; -using System.Linq; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.InputData; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.InputData.Reader.ComponentData; -using TUGraz.VectoCore.Models.Declaration; -using TUGraz.VectoCore.Models.Simulation.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine; -using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; - -namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter -{ - public class EngineeringDataAdapter : AbstractSimulationDataAdapter - { - internal VehicleData CreateVehicleData(IVehicleEngineeringInputData data) - { - if (data.SavedInDeclarationMode) { - WarnEngineeringMode("VehicleData"); - } - - var retVal = SetCommonVehicleData(data); - retVal.BodyAndTrailerWeight = data.CurbMassExtra; - //retVal.CurbWeight += data.CurbMassExtra; - retVal.TrailerGrossVehicleWeight = 0.SI<Kilogram>(); - retVal.Loading = data.Loading; - retVal.DynamicTyreRadius = data.DynamicTyreRadius; - var axles = data.Axles; - - - retVal.AxleData = axles.Select(axle => new Axle { - WheelsDimension = axle.Wheels, - Inertia = axle.Inertia, - TwinTyres = axle.TwinTyres, - RollResistanceCoefficient = axle.RollResistanceCoefficient, - AxleWeightShare = axle.AxleWeightShare, - TyreTestLoad = axle.TyreTestLoad, - //Wheels = axle.WheelsStr - }).ToList(); - return retVal; - } - - public AirdragData CreateAirdragData(IAirdragEngineeringInputData airdragData, IVehicleEngineeringInputData data) - { - var retVal = SetCommonAirdragData(airdragData); - retVal.CrossWindCorrectionMode = airdragData.CrossWindCorrectionMode; - - switch (airdragData.CrossWindCorrectionMode) { - case CrossWindCorrectionMode.NoCorrection: - retVal.CrossWindCorrectionCurve = - new CrosswindCorrectionCdxALookup(airdragData.AirDragArea, - CrossWindCorrectionCurveReader.GetNoCorrectionCurve(airdragData.AirDragArea), - CrossWindCorrectionMode.NoCorrection); - break; - case CrossWindCorrectionMode.SpeedDependentCorrectionFactor: - retVal.CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup(airdragData.AirDragArea, - CrossWindCorrectionCurveReader.ReadSpeedDependentCorrectionCurve(airdragData.CrosswindCorrectionMap, - airdragData.AirDragArea), CrossWindCorrectionMode.SpeedDependentCorrectionFactor); - break; - case CrossWindCorrectionMode.VAirBetaLookupTable: - retVal.CrossWindCorrectionCurve = new CrosswindCorrectionVAirBeta(airdragData.AirDragArea, - CrossWindCorrectionCurveReader.ReadCdxABetaTable(airdragData.CrosswindCorrectionMap)); - break; - case CrossWindCorrectionMode.DeclarationModeCorrection: - var height = DeclarationData.Segments.LookupHeight(data.VehicleCategory, data.AxleConfiguration, - data.GrossVehicleMassRating); - retVal.CrossWindCorrectionCurve = - new CrosswindCorrectionCdxALookup(airdragData.AirDragArea, - DeclarationDataAdapter.GetDeclarationAirResistanceCurve( - GetAirdragParameterSet(data.VehicleCategory, data.AxleConfiguration, data.Axles.Count), - airdragData.AirDragArea, height), CrossWindCorrectionMode.DeclarationModeCorrection); - break; - default: - throw new ArgumentOutOfRangeException("CrosswindCorrection", airdragData.CrossWindCorrectionMode.ToString()); - } - return retVal; - } - - private string GetAirdragParameterSet(VehicleCategory vehicleCategory, AxleConfiguration axles, int numAxles) - { - switch (vehicleCategory) { - case VehicleCategory.RigidTruck: - return numAxles > axles.NumAxles() ? "RigidTrailer" : "RigidSolo"; - case VehicleCategory.Tractor: - return "TractorSemitrailer"; - case VehicleCategory.CityBus: - case VehicleCategory.InterurbanBus: - case VehicleCategory.Coach: - return "CoachBus"; - default: - throw new ArgumentOutOfRangeException("vehicleCategory", vehicleCategory, null); - } - } - - private void WarnEngineeringMode(string msg) - { - Log.Error("{0} is in Declaration Mode but is used for Engineering Mode!", msg); - } - - internal CombustionEngineData CreateEngineData(IEngineEngineeringInputData engine, IGearboxEngineeringInputData gbx, - IEnumerable<ITorqueLimitInputData> torqueLimits) - { - if (engine.SavedInDeclarationMode) { - WarnEngineeringMode("EngineData"); - } - - var retVal = SetCommonCombustionEngineData(engine); - retVal.Inertia = engine.Inertia + - (gbx != null && gbx.Type.AutomaticTransmission() ? gbx.TorqueConverter.Inertia : 0.SI<KilogramSquareMeter>()); - var limits = torqueLimits.ToDictionary(e => e.Gear); - var numGears = gbx == null ? 0 : gbx.Gears.Count; - var fullLoadCurves = new Dictionary<uint, EngineFullLoadCurve>(numGears + 1); - fullLoadCurves[0] = FullLoadCurveReader.Create(engine.FullLoadCurve); - fullLoadCurves[0].EngineData = retVal; - if (gbx != null) { - foreach (var gear in gbx.Gears) { - var maxTorque = VectoMath.Min(gear.MaxTorque, limits.ContainsKey(gear.Gear) ? limits[gear.Gear].MaxTorque : null); - fullLoadCurves[(uint)gear.Gear] = IntersectFullLoadCurves(fullLoadCurves[0], maxTorque); - } - } - retVal.FullLoadCurves = fullLoadCurves; - retVal.FuelConsumptionCorrectionFactor = engine.WHTCEngineering; - return retVal; - } - - internal GearboxData CreateGearboxData(IGearboxEngineeringInputData gearbox, CombustionEngineData engineData, - double axlegearRatio, Meter dynamicTyreRadius, bool useEfficiencyFallback) - { - if (gearbox.SavedInDeclarationMode) { - WarnEngineeringMode("GearboxData"); - } - - var retVal = SetCommonGearboxData(gearbox); - - //var gears = gearbox.Gears; - if (gearbox.Gears.Count < 2) { - throw new VectoSimulationException("At least two Gear-Entries must be defined in Gearbox!"); - } - - SetEngineeringData(gearbox, retVal); - - var gearDifferenceRatio = gearbox.Type.AutomaticTransmission() && gearbox.Gears.Count > 2 - ? gearbox.Gears[0].Ratio / gearbox.Gears[1].Ratio - : 1.0; - - var gears = new Dictionary<uint, GearData>(); - ShiftPolygon tcShiftPolygon = null; - if (gearbox.Type.AutomaticTransmission()) { - tcShiftPolygon = gearbox.TorqueConverter.ShiftPolygon != null - ? ShiftPolygonReader.Create(gearbox.TorqueConverter.ShiftPolygon) - : DeclarationData.TorqueConverter.ComputeShiftPolygon(engineData.FullLoadCurves[0]); - } - for (uint i = 0; i < gearbox.Gears.Count; i++) { - var gear = gearbox.Gears[(int)i]; - var lossMap = CreateGearLossMap(gear, i, useEfficiencyFallback, false); - - var shiftPolygon = gear.ShiftPolygon != null && gear.ShiftPolygon.SourceType != DataSourceType.Missing - ? ShiftPolygonReader.Create(gear.ShiftPolygon) - : DeclarationData.Gearbox.ComputeShiftPolygon(gearbox.Type, (int)i, engineData.FullLoadCurves[i + 1], gearbox.Gears, - engineData, - axlegearRatio, dynamicTyreRadius); - var gearData = new GearData { - ShiftPolygon = shiftPolygon, - MaxSpeed = gear.MaxInputSpeed, - Ratio = gear.Ratio, - LossMap = lossMap, - }; - - if (gearbox.Type == GearboxType.ATPowerSplit && i == 0) { - // powersplit transmission: torque converter already contains ratio and losses - CretateTCFirstGearATPowerSplit(gearData, i, tcShiftPolygon); - } - if (gearbox.Type == GearboxType.ATSerial) { - if (i == 0) { - // torqueconverter is active in first gear - duplicate ratio and lossmap for torque converter mode - CreateTCFirstGearATSerial(gearData, tcShiftPolygon); - } - if (i == 1 && gearDifferenceRatio >= DeclarationData.Gearbox.TorqueConverterSecondGearThreshold) { - // ratio between first and second gear is above threshold, torqueconverter is active in second gear as well - // -> duplicate ratio and lossmap for torque converter mode, remove locked transmission for previous gear - CreateTCSecondGearATSerial(gearData, tcShiftPolygon); - // NOTE: the lower gear in 'gears' dictionary has index i !! - gears[i].Ratio = double.NaN; - gears[i].LossMap = null; - } - } - gears.Add(i + 1, gearData); - } - retVal.Gears = gears; - - if (retVal.Type.AutomaticTransmission()) { - var ratio = double.IsNaN(retVal.Gears[1].Ratio) ? 1 : retVal.Gears[1].TorqueConverterRatio / retVal.Gears[1].Ratio; - retVal.PowershiftShiftTime = gearbox.PowershiftShiftTime; - retVal.TorqueConverterData = TorqueConverterDataReader.Create(gearbox.TorqueConverter.TCData, - gearbox.TorqueConverter.ReferenceRPM, gearbox.TorqueConverter.MaxInputSpeed, ExecutionMode.Engineering, ratio, - gearbox.TorqueConverter.CLUpshiftMinAcceleration, gearbox.TorqueConverter.CCUpshiftMinAcceleration); - } - - - return retVal; - } - - private static void SetEngineeringData(IGearboxEngineeringInputData gearbox, GearboxData retVal) - { - retVal.Inertia = gearbox.Type.ManualTransmission() ? gearbox.Inertia : 0.SI<KilogramSquareMeter>(); - retVal.TractionInterruption = gearbox.TractionInterruption; - retVal.TorqueReserve = gearbox.TorqueReserve; - retVal.StartTorqueReserve = gearbox.StartTorqueReserve; - retVal.ShiftTime = gearbox.MinTimeBetweenGearshift; - retVal.StartSpeed = gearbox.StartSpeed; - retVal.StartAcceleration = gearbox.StartAcceleration; - retVal.DownshiftAfterUpshiftDelay = gearbox.DownshiftAfterUpshiftDelay; - retVal.UpshiftAfterDownshiftDelay = gearbox.UpshiftAfterDownshiftDelay; - retVal.UpshiftMinAcceleration = gearbox.UpshiftMinAcceleration; - } - - - public IList<VectoRunData.AuxData> CreateAuxiliaryData(IAuxiliariesEngineeringInputData auxInputData) - { - var auxList = new List<VectoRunData.AuxData>(auxInputData.Auxiliaries.Count + 1) { - new VectoRunData.AuxData { ID = Constants.Auxiliaries.Cycle, DemandType = AuxiliaryDemandType.Direct } - }; - - foreach (var a in auxInputData.Auxiliaries) { - switch (a.AuxiliaryType) { - case AuxiliaryDemandType.Mapping: - auxList.Add(CreateMappingAuxiliary(a)); - break; - case AuxiliaryDemandType.Constant: - auxList.Add(CreateConstantAuxiliary(a)); - break; - default: - throw new VectoException("Auxiliary type {0} not supported!", a.AuxiliaryType); - } - } - return auxList; - } - - private static VectoRunData.AuxData CreateMappingAuxiliary(IAuxiliaryEngineeringInputData a) - { - if (a.DemandMap == null) { - throw new VectoSimulationException("Demand Map for auxiliary {0} required", a.ID); - } - if (a.DemandMap.Columns.Count != 3 || a.DemandMap.Rows.Count < 4) { - throw new VectoSimulationException( - "Demand Map for auxiliary {0} has to contain exactly 3 columns and at least 4 rows", a.ID); - } - return new VectoRunData.AuxData { - ID = a.ID, - DemandType = AuxiliaryDemandType.Mapping, - Data = AuxiliaryDataReader.Create(a) - }; - } - - private static VectoRunData.AuxData CreateConstantAuxiliary(IAuxiliaryEngineeringInputData a) - { - return new VectoRunData.AuxData { - ID = a.ID, - DemandType = AuxiliaryDemandType.Constant, - PowerDemand = a.ConstantPowerDemand - }; - } - - internal DriverData CreateDriverData(IDriverEngineeringInputData driver) - { - if (driver.SavedInDeclarationMode) { - WarnEngineeringMode("DriverData"); - } - - AccelerationCurveData accelerationData = null; - if (driver.AccelerationCurve != null) { - accelerationData = AccelerationCurveReader.Create(driver.AccelerationCurve); - } - - if (driver.Lookahead == null) { - throw new VectoSimulationException("Error: Lookahead Data is missing."); - } - var lookAheadData = new DriverData.LACData { - Enabled = driver.Lookahead.Enabled, - //Deceleration = driver.Lookahead.Deceleration, - MinSpeed = driver.Lookahead.MinSpeed, - LookAheadDecisionFactor = - new LACDecisionFactor(driver.Lookahead.CoastingDecisionFactorOffset, driver.Lookahead.CoastingDecisionFactorScaling, - driver.Lookahead.CoastingDecisionFactorTargetSpeedLookup, - driver.Lookahead.CoastingDecisionFactorVelocityDropLookup), - LookAheadDistanceFactor = driver.Lookahead.LookaheadDistanceFactor - }; - var overspeedData = new DriverData.OverSpeedEcoRollData { - Mode = driver.OverSpeedEcoRoll.Mode, - MinSpeed = driver.OverSpeedEcoRoll.MinSpeed, - OverSpeed = driver.OverSpeedEcoRoll.OverSpeed, - UnderSpeed = driver.OverSpeedEcoRoll.UnderSpeed, - }; - var retVal = new DriverData { - AccelerationCurve = accelerationData, - LookAheadCoasting = lookAheadData, - OverSpeedEcoRoll = overspeedData, - }; - return retVal; - } - - //================================= - public RetarderData CreateRetarderData(IRetarderInputData retarder) - { - return SetCommonRetarderData(retarder); - } - - public PTOData CreatePTOTransmissionData(IPTOTransmissionInputData pto) - { - if (pto.PTOTransmissionType != "None") { - var ptoData = new PTOData { - TransmissionType = pto.PTOTransmissionType, - LossMap = PTOIdleLossMapReader.Create(pto.PTOLossMap), - }; - if (pto.PTOCycle != null) { - ptoData.PTOCycle = DrivingCycleDataReader.ReadFromDataTable(pto.PTOCycle, CycleType.PTO, "PTO", false); - } - return ptoData; - } - - return null; - } - - public AdvancedAuxData CreateAdvancedAuxData(IAuxiliariesEngineeringInputData auxInputData) - { - return new AdvancedAuxData() { - AdvancedAuxiliaryFilePath = auxInputData.AdvancedAuxiliaryFilePath, - AuxiliaryAssembly = auxInputData.AuxiliaryAssembly, - AuxiliaryVersion = auxInputData.AuxiliaryVersion - }; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.InputData; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.InputData.Reader.ComponentData; +using TUGraz.VectoCore.Models.Declaration; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; + +namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter +{ + public class EngineeringDataAdapter : AbstractSimulationDataAdapter + { + internal VehicleData CreateVehicleData(IVehicleEngineeringInputData data) + { + if (data.SavedInDeclarationMode) { + WarnEngineeringMode("VehicleData"); + } + + var retVal = SetCommonVehicleData(data); + retVal.BodyAndTrailerWeight = data.CurbMassExtra; + //retVal.CurbWeight += data.CurbMassExtra; + retVal.TrailerGrossVehicleWeight = 0.SI<Kilogram>(); + retVal.Loading = data.Loading; + retVal.DynamicTyreRadius = data.DynamicTyreRadius; + var axles = data.Axles; + + + retVal.AxleData = axles.Select(axle => new Axle { + WheelsDimension = axle.Wheels, + Inertia = axle.Inertia, + TwinTyres = axle.TwinTyres, + RollResistanceCoefficient = axle.RollResistanceCoefficient, + AxleWeightShare = axle.AxleWeightShare, + TyreTestLoad = axle.TyreTestLoad, + //Wheels = axle.WheelsStr + }).ToList(); + return retVal; + } + + public AirdragData CreateAirdragData(IAirdragEngineeringInputData airdragData, IVehicleEngineeringInputData data) + { + var retVal = SetCommonAirdragData(airdragData); + retVal.CrossWindCorrectionMode = airdragData.CrossWindCorrectionMode; + + switch (airdragData.CrossWindCorrectionMode) { + case CrossWindCorrectionMode.NoCorrection: + retVal.CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(airdragData.AirDragArea, + CrossWindCorrectionCurveReader.GetNoCorrectionCurve(airdragData.AirDragArea), + CrossWindCorrectionMode.NoCorrection); + break; + case CrossWindCorrectionMode.SpeedDependentCorrectionFactor: + retVal.CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup(airdragData.AirDragArea, + CrossWindCorrectionCurveReader.ReadSpeedDependentCorrectionCurve(airdragData.CrosswindCorrectionMap, + airdragData.AirDragArea), CrossWindCorrectionMode.SpeedDependentCorrectionFactor); + break; + case CrossWindCorrectionMode.VAirBetaLookupTable: + retVal.CrossWindCorrectionCurve = new CrosswindCorrectionVAirBeta(airdragData.AirDragArea, + CrossWindCorrectionCurveReader.ReadCdxABetaTable(airdragData.CrosswindCorrectionMap)); + break; + case CrossWindCorrectionMode.DeclarationModeCorrection: + var height = DeclarationData.Segments.LookupHeight(data.VehicleCategory, data.AxleConfiguration, + data.GrossVehicleMassRating); + retVal.CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(airdragData.AirDragArea, + DeclarationDataAdapter.GetDeclarationAirResistanceCurve( + GetAirdragParameterSet(data.VehicleCategory, data.AxleConfiguration, data.Axles.Count), + airdragData.AirDragArea, height), CrossWindCorrectionMode.DeclarationModeCorrection); + break; + default: + throw new ArgumentOutOfRangeException("CrosswindCorrection", airdragData.CrossWindCorrectionMode.ToString()); + } + return retVal; + } + + private string GetAirdragParameterSet(VehicleCategory vehicleCategory, AxleConfiguration axles, int numAxles) + { + switch (vehicleCategory) { + case VehicleCategory.RigidTruck: + return numAxles > axles.NumAxles() ? "RigidTrailer" : "RigidSolo"; + case VehicleCategory.Tractor: + return "TractorSemitrailer"; + case VehicleCategory.CityBus: + case VehicleCategory.InterurbanBus: + case VehicleCategory.Coach: + return "CoachBus"; + default: + throw new ArgumentOutOfRangeException("vehicleCategory", vehicleCategory, null); + } + } + + private void WarnEngineeringMode(string msg) + { + Log.Error("{0} is in Declaration Mode but is used for Engineering Mode!", msg); + } + + internal CombustionEngineData CreateEngineData(IEngineEngineeringInputData engine, IGearboxEngineeringInputData gbx, + IEnumerable<ITorqueLimitInputData> torqueLimits) + { + if (engine.SavedInDeclarationMode) { + WarnEngineeringMode("EngineData"); + } + + var retVal = SetCommonCombustionEngineData(engine); + retVal.Inertia = engine.Inertia + + (gbx != null && gbx.Type.AutomaticTransmission() ? gbx.TorqueConverter.Inertia : 0.SI<KilogramSquareMeter>()); + var limits = torqueLimits.ToDictionary(e => e.Gear); + var numGears = gbx == null ? 0 : gbx.Gears.Count; + var fullLoadCurves = new Dictionary<uint, EngineFullLoadCurve>(numGears + 1); + fullLoadCurves[0] = FullLoadCurveReader.Create(engine.FullLoadCurve); + fullLoadCurves[0].EngineData = retVal; + if (gbx != null) { + foreach (var gear in gbx.Gears) { + var maxTorque = VectoMath.Min(gear.MaxTorque, limits.ContainsKey(gear.Gear) ? limits[gear.Gear].MaxTorque : null); + fullLoadCurves[(uint)gear.Gear] = IntersectFullLoadCurves(fullLoadCurves[0], maxTorque); + } + } + retVal.FullLoadCurves = fullLoadCurves; + retVal.FuelConsumptionCorrectionFactor = engine.WHTCEngineering; + return retVal; + } + + internal GearboxData CreateGearboxData(IGearboxEngineeringInputData gearbox, CombustionEngineData engineData, + double axlegearRatio, Meter dynamicTyreRadius, bool useEfficiencyFallback) + { + if (gearbox.SavedInDeclarationMode) { + WarnEngineeringMode("GearboxData"); + } + + var retVal = SetCommonGearboxData(gearbox); + + //var gears = gearbox.Gears; + if (gearbox.Gears.Count < 2) { + throw new VectoSimulationException("At least two Gear-Entries must be defined in Gearbox!"); + } + + SetEngineeringData(gearbox, retVal); + + var gearDifferenceRatio = gearbox.Type.AutomaticTransmission() && gearbox.Gears.Count > 2 + ? gearbox.Gears[0].Ratio / gearbox.Gears[1].Ratio + : 1.0; + + var gears = new Dictionary<uint, GearData>(); + ShiftPolygon tcShiftPolygon = null; + if (gearbox.Type.AutomaticTransmission()) { + tcShiftPolygon = gearbox.TorqueConverter.ShiftPolygon != null + ? ShiftPolygonReader.Create(gearbox.TorqueConverter.ShiftPolygon) + : DeclarationData.TorqueConverter.ComputeShiftPolygon(engineData.FullLoadCurves[0]); + } + for (uint i = 0; i < gearbox.Gears.Count; i++) { + var gear = gearbox.Gears[(int)i]; + var lossMap = CreateGearLossMap(gear, i, useEfficiencyFallback, false); + + var shiftPolygon = gear.ShiftPolygon != null && gear.ShiftPolygon.SourceType != DataSourceType.Missing + ? ShiftPolygonReader.Create(gear.ShiftPolygon) + : DeclarationData.Gearbox.ComputeShiftPolygon(gearbox.Type, (int)i, engineData.FullLoadCurves[i + 1], gearbox.Gears, + engineData, + axlegearRatio, dynamicTyreRadius); + var gearData = new GearData { + ShiftPolygon = shiftPolygon, + MaxSpeed = gear.MaxInputSpeed, + Ratio = gear.Ratio, + LossMap = lossMap, + }; + + CreateATGearData(gearbox, i, gearData, tcShiftPolygon, gearDifferenceRatio, gears); + gears.Add(i + 1, gearData); + } + retVal.Gears = gears; + + if (retVal.Type.AutomaticTransmission()) { + var ratio = double.IsNaN(retVal.Gears[1].Ratio) ? 1 : retVal.Gears[1].TorqueConverterRatio / retVal.Gears[1].Ratio; + retVal.PowershiftShiftTime = gearbox.PowershiftShiftTime; + retVal.TorqueConverterData = TorqueConverterDataReader.Create(gearbox.TorqueConverter.TCData, + gearbox.TorqueConverter.ReferenceRPM, gearbox.TorqueConverter.MaxInputSpeed, ExecutionMode.Engineering, ratio, + gearbox.TorqueConverter.CLUpshiftMinAcceleration, gearbox.TorqueConverter.CCUpshiftMinAcceleration); + } + + + return retVal; + } + + private static void CreateATGearData(IGearboxEngineeringInputData gearbox, uint i, GearData gearData, + ShiftPolygon tcShiftPolygon, double gearDifferenceRatio, Dictionary<uint, GearData> gears) + { + if (gearbox.Type == GearboxType.ATPowerSplit && i == 0) { + // powersplit transmission: torque converter already contains ratio and losses + CretateTCFirstGearATPowerSplit(gearData, i, tcShiftPolygon); + } + if (gearbox.Type == GearboxType.ATSerial) { + if (i == 0) { + // torqueconverter is active in first gear - duplicate ratio and lossmap for torque converter mode + CreateTCFirstGearATSerial(gearData, tcShiftPolygon); + } + if (i == 1 && gearDifferenceRatio >= DeclarationData.Gearbox.TorqueConverterSecondGearThreshold) { + // ratio between first and second gear is above threshold, torqueconverter is active in second gear as well + // -> duplicate ratio and lossmap for torque converter mode, remove locked transmission for previous gear + CreateTCSecondGearATSerial(gearData, tcShiftPolygon); + // NOTE: the lower gear in 'gears' dictionary has index i !! + gears[i].Ratio = double.NaN; + gears[i].LossMap = null; + } + } + } + + private static void SetEngineeringData(IGearboxEngineeringInputData gearbox, GearboxData retVal) + { + retVal.Inertia = gearbox.Type.ManualTransmission() ? gearbox.Inertia : 0.SI<KilogramSquareMeter>(); + retVal.TractionInterruption = gearbox.TractionInterruption; + retVal.TorqueReserve = gearbox.TorqueReserve; + retVal.StartTorqueReserve = gearbox.StartTorqueReserve; + retVal.ShiftTime = gearbox.MinTimeBetweenGearshift; + retVal.StartSpeed = gearbox.StartSpeed; + retVal.StartAcceleration = gearbox.StartAcceleration; + retVal.DownshiftAfterUpshiftDelay = gearbox.DownshiftAfterUpshiftDelay; + retVal.UpshiftAfterDownshiftDelay = gearbox.UpshiftAfterDownshiftDelay; + retVal.UpshiftMinAcceleration = gearbox.UpshiftMinAcceleration; + } + + + public IList<VectoRunData.AuxData> CreateAuxiliaryData(IAuxiliariesEngineeringInputData auxInputData) + { + var auxList = new List<VectoRunData.AuxData>(auxInputData.Auxiliaries.Count + 1) { + new VectoRunData.AuxData { ID = Constants.Auxiliaries.Cycle, DemandType = AuxiliaryDemandType.Direct } + }; + + foreach (var a in auxInputData.Auxiliaries) { + switch (a.AuxiliaryType) { + case AuxiliaryDemandType.Mapping: + auxList.Add(CreateMappingAuxiliary(a)); + break; + case AuxiliaryDemandType.Constant: + auxList.Add(CreateConstantAuxiliary(a)); + break; + default: + throw new VectoException("Auxiliary type {0} not supported!", a.AuxiliaryType); + } + } + return auxList; + } + + private static VectoRunData.AuxData CreateMappingAuxiliary(IAuxiliaryEngineeringInputData a) + { + if (a.DemandMap == null) { + throw new VectoSimulationException("Demand Map for auxiliary {0} required", a.ID); + } + if (a.DemandMap.Columns.Count != 3 || a.DemandMap.Rows.Count < 4) { + throw new VectoSimulationException( + "Demand Map for auxiliary {0} has to contain exactly 3 columns and at least 4 rows", a.ID); + } + return new VectoRunData.AuxData { + ID = a.ID, + DemandType = AuxiliaryDemandType.Mapping, + Data = AuxiliaryDataReader.Create(a) + }; + } + + private static VectoRunData.AuxData CreateConstantAuxiliary(IAuxiliaryEngineeringInputData a) + { + return new VectoRunData.AuxData { + ID = a.ID, + DemandType = AuxiliaryDemandType.Constant, + PowerDemand = a.ConstantPowerDemand + }; + } + + internal DriverData CreateDriverData(IDriverEngineeringInputData driver) + { + if (driver.SavedInDeclarationMode) { + WarnEngineeringMode("DriverData"); + } + + AccelerationCurveData accelerationData = null; + if (driver.AccelerationCurve != null) { + accelerationData = AccelerationCurveReader.Create(driver.AccelerationCurve); + } + + if (driver.Lookahead == null) { + throw new VectoSimulationException("Error: Lookahead Data is missing."); + } + var lookAheadData = new DriverData.LACData { + Enabled = driver.Lookahead.Enabled, + //Deceleration = driver.Lookahead.Deceleration, + MinSpeed = driver.Lookahead.MinSpeed, + LookAheadDecisionFactor = + new LACDecisionFactor(driver.Lookahead.CoastingDecisionFactorOffset, driver.Lookahead.CoastingDecisionFactorScaling, + driver.Lookahead.CoastingDecisionFactorTargetSpeedLookup, + driver.Lookahead.CoastingDecisionFactorVelocityDropLookup), + LookAheadDistanceFactor = driver.Lookahead.LookaheadDistanceFactor + }; + var overspeedData = new DriverData.OverSpeedEcoRollData { + Mode = driver.OverSpeedEcoRoll.Mode, + MinSpeed = driver.OverSpeedEcoRoll.MinSpeed, + OverSpeed = driver.OverSpeedEcoRoll.OverSpeed, + UnderSpeed = driver.OverSpeedEcoRoll.UnderSpeed, + }; + var retVal = new DriverData { + AccelerationCurve = accelerationData, + LookAheadCoasting = lookAheadData, + OverSpeedEcoRoll = overspeedData, + }; + return retVal; + } + + //================================= + public RetarderData CreateRetarderData(IRetarderInputData retarder) + { + return SetCommonRetarderData(retarder); + } + + public PTOData CreatePTOTransmissionData(IPTOTransmissionInputData pto) + { + if (pto.PTOTransmissionType != "None") { + var ptoData = new PTOData { + TransmissionType = pto.PTOTransmissionType, + LossMap = PTOIdleLossMapReader.Create(pto.PTOLossMap), + }; + if (pto.PTOCycle != null) { + ptoData.PTOCycle = DrivingCycleDataReader.ReadFromDataTable(pto.PTOCycle, CycleType.PTO, "PTO", false); + } + return ptoData; + } + + return null; + } + + public AdvancedAuxData CreateAdvancedAuxData(IAuxiliariesEngineeringInputData auxInputData) + { + return new AdvancedAuxData() { + AdvancedAuxiliaryFilePath = auxInputData.AdvancedAuxiliaryFilePath, + AuxiliaryAssembly = auxInputData.AuxiliaryAssembly, + AuxiliaryVersion = auxInputData.AuxiliaryVersion + }; + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs b/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs index 37846d8653..83f67a1267 100644 --- a/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs +++ b/VectoCore/VectoCore/Models/Simulation/Data/VectoRunData.cs @@ -29,176 +29,200 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Xml.Linq; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Declaration; -using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.OutputData; -using DriverData = TUGraz.VectoCore.Models.SimulationComponent.Data.DriverData; - -namespace TUGraz.VectoCore.Models.Simulation.Data -{ - [CustomValidation(typeof(VectoRunData), "ValidateRunData")] - public class VectoRunData : SimulationComponentData - { - [ValidateObject] - public VehicleData VehicleData { get; internal set; } - - [ValidateObject] - public AirdragData AirdragData { get; internal set; } - - [ValidateObject] - public CombustionEngineData EngineData { get; internal set; } - - [ValidateObject] - public GearboxData GearboxData { get; internal set; } - - [ValidateObject] - public AxleGearData AxleGearData { get; internal set; } - - [ValidateObject] - public AngledriveData AngledriveData { get; internal set; } - - [Required, ValidateObject] - public IDrivingCycleData Cycle { get; internal set; } - - [ValidateObject] - public IEnumerable<AuxData> Aux { get; internal set; } - - public AdvancedAuxData AdvancedAux { get; internal set; } - - [ValidateObject] - public RetarderData Retarder { get; internal set; } - - [ValidateObject] - public PTOData PTO { get; internal set; } - - [ValidateObject] - public DriverData DriverData { get; internal set; } - - public ExecutionMode ExecutionMode { get; internal set; } - - [Required, MinLength(1)] - public string JobName { get; internal set; } - - public string ModFileSuffix { get; internal set; } - - [ValidateObject] - public IDeclarationReport Report { get; internal set; } - - [Required, ValidateObject] - public LoadingType Loading { get; internal set; } - - [ValidateObject] - public Mission Mission { get; internal set; } - - public XElement InputDataHash { get; internal set; } - - public class AuxData - { - // ReSharper disable once InconsistentNaming - public string ID; - - public IList<string> Technology; - - [SIRange(0, 100 * Constants.Kilo)] public Watt PowerDemand; - - [Required] public AuxiliaryDemandType DemandType; - - [ValidateObject] public AuxiliaryData Data; - } - - public static ValidationResult ValidateRunData(VectoRunData runData, ValidationContext validationContext) - { - var gearboxData = runData.GearboxData; - var engineData = runData.EngineData; - - var maxSpeed = 95.KMPHtoMeterPerSecond(); - - if (gearboxData != null) { - var axleGearData = runData.AxleGearData; - var angledriveData = runData.AngledriveData; - var hasAngleDrive = angledriveData != null && angledriveData.Angledrive != null; - var angledriveRatio = hasAngleDrive && angledriveData.Type == AngledriveType.SeparateAngledrive - ? angledriveData.Angledrive.Ratio - : 1.0; - var axlegearRatio = axleGearData != null ? axleGearData.AxleGear.Ratio : 1.0; - var dynamicTyreRadius = runData.VehicleData != null ? runData.VehicleData.DynamicTyreRadius : 0.0.SI<Meter>(); - - if (gearboxData.Gears.Count + 1 != engineData.FullLoadCurves.Count) { - return - new ValidationResult( - string.Format("number of full-load curves in engine does not match gear count. engine fld: {0}, gears: {1}", - engineData.FullLoadCurves.Count, gearboxData.Gears.Count)); - } - - foreach (var gear in gearboxData.Gears) { - for (var angularVelocity = engineData.IdleSpeed; - angularVelocity < engineData.FullLoadCurves[gear.Key].RatedSpeed; - angularVelocity += 2.0 / 3.0 * (engineData.FullLoadCurves[gear.Key].RatedSpeed - engineData.IdleSpeed) / 10.0) { - if (!gear.Value.HasLockedGear) { - continue; - } - - var velocity = angularVelocity / gear.Value.Ratio / angledriveRatio / axlegearRatio * dynamicTyreRadius; - - if (velocity > maxSpeed) { - continue; - } - - for (var inTorque = engineData.FullLoadCurves[gear.Key].FullLoadStationaryTorque(angularVelocity) / 3; - inTorque < engineData.FullLoadCurves[gear.Key].FullLoadStationaryTorque(angularVelocity); - inTorque += 2.0 / 3.0 * engineData.FullLoadCurves[gear.Key].FullLoadStationaryTorque(angularVelocity) / 10.0) { - NewtonMeter angledriveTorque; - try { - angledriveTorque = gear.Value.LossMap.GetOutTorque(angularVelocity, inTorque); - } catch (VectoException) { - return new ValidationResult( - string.Format("Interpolation of Gear-{0}-LossMap failed with torque={1} and angularSpeed={2}", gear.Key, - inTorque, angularVelocity.ConvertTo().Rounds.Per.Minute)); - } - var axlegearTorque = angledriveTorque; - try { - if (hasAngleDrive) { - axlegearTorque = angledriveData.Angledrive.LossMap.GetOutTorque(angularVelocity / gear.Value.Ratio, - angledriveTorque); - } - } catch (VectoException) { - return new ValidationResult( - string.Format("Interpolation of Angledrive-LossMap failed with torque={0} and angularSpeed={1}", - angledriveTorque, (angularVelocity / gear.Value.Ratio).ConvertTo().Rounds.Per.Minute)); - } - - if (axleGearData != null) { - var axleAngularVelocity = angularVelocity / gear.Value.Ratio / angledriveRatio; - try { - axleGearData.AxleGear.LossMap.GetOutTorque(axleAngularVelocity, axlegearTorque); - } catch (VectoException) { - return - new ValidationResult( - string.Format( - "Interpolation of AxleGear-LossMap failed with torque={0} and angularSpeed={1} (gear={2}, velocity={3})", - axlegearTorque, axleAngularVelocity.ConvertTo().Rounds.Per.Minute, gear.Key, velocity)); - } - } - } - } - } - } - - if (runData.Cycle != null && runData.Cycle.Entries.Any(e => e.PTOActive)) { - if (runData.PTO == null || runData.PTO.PTOCycle == null) { - return new ValidationResult("PTOCycle is used in DrivingCycle, but is not defined in Vehicle-Data."); - } - } - - return ValidationResult.Success; - } - } +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Xml.Linq; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Declaration; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; +using TUGraz.VectoCore.OutputData; +using DriverData = TUGraz.VectoCore.Models.SimulationComponent.Data.DriverData; + +namespace TUGraz.VectoCore.Models.Simulation.Data +{ + [CustomValidation(typeof(VectoRunData), "ValidateRunData")] + public class VectoRunData : SimulationComponentData + { + [ValidateObject] + public VehicleData VehicleData { get; internal set; } + + [ValidateObject] + public AirdragData AirdragData { get; internal set; } + + [ValidateObject] + public CombustionEngineData EngineData { get; internal set; } + + [ValidateObject] + public GearboxData GearboxData { get; internal set; } + + [ValidateObject] + public AxleGearData AxleGearData { get; internal set; } + + [ValidateObject] + public AngledriveData AngledriveData { get; internal set; } + + [Required, ValidateObject] + public IDrivingCycleData Cycle { get; internal set; } + + [ValidateObject] + public IEnumerable<AuxData> Aux { get; internal set; } + + public AdvancedAuxData AdvancedAux { get; internal set; } + + [ValidateObject] + public RetarderData Retarder { get; internal set; } + + [ValidateObject] + public PTOData PTO { get; internal set; } + + [ValidateObject] + public DriverData DriverData { get; internal set; } + + public ExecutionMode ExecutionMode { get; internal set; } + + [Required, MinLength(1)] + public string JobName { get; internal set; } + + public string ModFileSuffix { get; internal set; } + + [ValidateObject] + public IDeclarationReport Report { get; internal set; } + + [Required, ValidateObject] + public LoadingType Loading { get; internal set; } + + [ValidateObject] + public Mission Mission { get; internal set; } + + public XElement InputDataHash { get; internal set; } + + public class AuxData + { + // ReSharper disable once InconsistentNaming + public string ID; + + public IList<string> Technology; + + [SIRange(0, 100 * Constants.Kilo)] public Watt PowerDemand; + + [Required] public AuxiliaryDemandType DemandType; + + [ValidateObject] public AuxiliaryData Data; + } + + public static ValidationResult ValidateRunData(VectoRunData runData, ValidationContext validationContext) + { + var gearboxData = runData.GearboxData; + var engineData = runData.EngineData; + + if (gearboxData != null) { + var validationResult = CheckPowertrainLossMapsSize(runData, gearboxData, engineData); + if (validationResult != null) { + return validationResult; + } + } + + if (runData.Cycle != null && runData.Cycle.Entries.Any(e => e.PTOActive)) { + if (runData.PTO == null || runData.PTO.PTOCycle == null) { + return new ValidationResult("PTOCycle is used in DrivingCycle, but is not defined in Vehicle-Data."); + } + } + + return ValidationResult.Success; + } + + private static ValidationResult CheckPowertrainLossMapsSize(VectoRunData runData, GearboxData gearboxData, + CombustionEngineData engineData) + { + var maxSpeed = 95.KMPHtoMeterPerSecond(); + var axleGearData = runData.AxleGearData; + var angledriveData = runData.AngledriveData; + var hasAngleDrive = angledriveData != null && angledriveData.Angledrive != null; + var angledriveRatio = hasAngleDrive && angledriveData.Type == AngledriveType.SeparateAngledrive + ? angledriveData.Angledrive.Ratio + : 1.0; + var axlegearRatio = axleGearData != null ? axleGearData.AxleGear.Ratio : 1.0; + var dynamicTyreRadius = runData.VehicleData != null ? runData.VehicleData.DynamicTyreRadius : 0.0.SI<Meter>(); + + if (gearboxData.Gears.Count + 1 != engineData.FullLoadCurves.Count) { + return + new ValidationResult( + string.Format("number of full-load curves in engine does not match gear count. engine fld: {0}, gears: {1}", + engineData.FullLoadCurves.Count, gearboxData.Gears.Count)); + } + + foreach (var gear in gearboxData.Gears) { + for (var angularVelocity = engineData.IdleSpeed; + angularVelocity < engineData.FullLoadCurves[gear.Key].RatedSpeed; + angularVelocity += 2.0 / 3.0 * (engineData.FullLoadCurves[gear.Key].RatedSpeed - engineData.IdleSpeed) / 10.0) { + if (!gear.Value.HasLockedGear) { + continue; + } + + var velocity = angularVelocity / gear.Value.Ratio / angledriveRatio / axlegearRatio * dynamicTyreRadius; + + if (velocity > maxSpeed) { + continue; + } + + for (var inTorque = engineData.FullLoadCurves[gear.Key].FullLoadStationaryTorque(angularVelocity) / 3; + inTorque < engineData.FullLoadCurves[gear.Key].FullLoadStationaryTorque(angularVelocity); + inTorque += 2.0 / 3.0 * engineData.FullLoadCurves[gear.Key].FullLoadStationaryTorque(angularVelocity) / 10.0) { + var validateRunData = CheckLossMapsEntries(gear, angularVelocity, inTorque, angledriveData, axleGearData, velocity); + if (validateRunData != null) { + return validateRunData; + } + } + } + } + return null; + } + + private static ValidationResult CheckLossMapsEntries(KeyValuePair<uint, GearData> gear, PerSecond angularVelocity, + NewtonMeter inTorque, AngledriveData angledriveData, AxleGearData axleGearData, SI velocity) + { + var hasAngleDrive = angledriveData != null && angledriveData.Angledrive != null; + var angledriveRatio = hasAngleDrive && angledriveData.Type == AngledriveType.SeparateAngledrive + ? angledriveData.Angledrive.Ratio + : 1.0; + NewtonMeter angledriveTorque; + try { + angledriveTorque = gear.Value.LossMap.GetOutTorque(angularVelocity, inTorque); + } catch (VectoException) { + return new ValidationResult( + string.Format("Interpolation of Gear-{0}-LossMap failed with torque={1} and angularSpeed={2}", gear.Key, + inTorque, angularVelocity.ConvertTo().Rounds.Per.Minute)); + } + var axlegearTorque = angledriveTorque; + try { + if (hasAngleDrive) { + axlegearTorque = angledriveData.Angledrive.LossMap.GetOutTorque(angularVelocity / gear.Value.Ratio, + angledriveTorque); + } + } catch (VectoException) { + return new ValidationResult( + string.Format("Interpolation of Angledrive-LossMap failed with torque={0} and angularSpeed={1}", + angledriveTorque, (angularVelocity / gear.Value.Ratio).ConvertTo().Rounds.Per.Minute)); + } + + if (axleGearData != null) { + var axleAngularVelocity = angularVelocity / gear.Value.Ratio / angledriveRatio; + try { + axleGearData.AxleGear.LossMap.GetOutTorque(axleAngularVelocity, axlegearTorque); + } catch (VectoException) { + return + new ValidationResult( + string.Format( + "Interpolation of AxleGear-LossMap failed with torque={0} and angularSpeed={1} (gear={2}, velocity={3})", + axlegearTorque, axleAngularVelocity.ConvertTo().Rounds.Per.Minute, gear.Key, velocity)); + } + } + return null; + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs index 8d72fe2608..2c395c3bde 100644 --- a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs +++ b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs @@ -29,166 +29,203 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.InputData; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.InputData; -using TUGraz.VectoCore.InputData.Reader.Impl; -using TUGraz.VectoCore.Models.Declaration; -using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.OutputData; -using TUGraz.VectoCore.OutputData.ModFilter; -using TUGraz.VectoCore.OutputData.XML; - -namespace TUGraz.VectoCore.Models.Simulation.Impl -{ - public class SimulatorFactory : LoggingObject - { - private static int _jobNumberCounter; - - private readonly ExecutionMode _mode; - private readonly bool _engineOnlyMode; - - public SimulatorFactory(ExecutionMode mode, IInputDataProvider dataProvider, IOutputDataWriter writer, - IDeclarationReport declarationReport = null, bool validate = true) - { - Log.Info("########## VectoCore Version {0} ##########", Assembly.GetExecutingAssembly().GetName().Version); - JobNumber = Interlocked.Increment(ref _jobNumberCounter); - _mode = mode; - ModWriter = writer; - Validate = validate; - - int workerThreads; - int completionThreads; - ThreadPool.GetMinThreads(out workerThreads, out completionThreads); - if (workerThreads < 12) { - workerThreads = 12; - } - ThreadPool.SetMinThreads(workerThreads, completionThreads); - - switch (mode) { - case ExecutionMode.Declaration: - var declDataProvider = dataProvider as IDeclarationInputDataProvider; - if (declDataProvider == null) { - throw new VectoException("InputDataProvider does not implement DeclarationData interface"); - } - var report = declarationReport ?? new XMLDeclarationReport(writer); - DataReader = new DeclarationModeVectoRunDataFactory(declDataProvider, report); - break; - case ExecutionMode.Engineering: - var engDataProvider = dataProvider as IEngineeringInputDataProvider; - if (engDataProvider == null) { - throw new VectoException("InputDataProvider does not implement Engineering interface"); - } - if (engDataProvider.JobInputData().EngineOnlyMode) { - DataReader = new EngineOnlyVectoRunDataFactory(engDataProvider); - _engineOnlyMode = true; - } else { - DataReader = new EngineeringModeVectoRunDataFactory(engDataProvider); - } - break; - default: - throw new VectoException("Unkown factory mode in SimulatorFactory: {0}", mode); - } - } - - public bool Validate { get; set; } - - public IVectoRunDataFactory DataReader { get; private set; } - - public SummaryDataContainer SumData { get; set; } - - public IOutputDataWriter ModWriter { get; private set; } - - public int JobNumber { get; set; } - - public bool WriteModalResults { get; set; } - public bool ModalResults1Hz { get; set; } - public bool ActualModalData { get; set; } - - /// <summary> - /// Creates powertrain and initializes it with the component's data. - /// </summary> - /// <returns>new VectoRun Instance</returns> - public IEnumerable<IVectoRun> SimulationRuns() - { - var i = 0; - var modDataFilter = ModalResults1Hz - ? new IModalDataFilter[] { new ModalData1HzFilter() } - : null; - - if (ActualModalData) { - modDataFilter = new IModalDataFilter[] { new ActualModalDataFilter(), }; - } - - - var warning1Hz = false; - - foreach (var data in DataReader.NextRun()) { - var d = data; - if (d.Report != null) { - d.Report.PrepareResult(d.Loading, d.Mission, d); - } - Action<ModalDataContainer> addReportResult = writer => { - if (d.Report != null) { - d.Report.AddResult(d.Loading, d.Mission, d, writer); - } - }; - if (!data.Cycle.CycleType.IsDistanceBased() && ModalResults1Hz && !warning1Hz) { - Log.Error("Output filter for 1Hz results is only available for distance-based cycles!"); - warning1Hz = true; - } - IModalDataContainer modContainer = - new ModalDataContainer(data, ModWriter, - addReportResult: _mode == ExecutionMode.Declaration ? addReportResult : null, - writeEngineOnly: _engineOnlyMode, - filter: data.Cycle.CycleType.IsDistanceBased() && ModalResults1Hz || ActualModalData ? modDataFilter : null) { - WriteAdvancedAux = data.AdvancedAux != null && data.AdvancedAux.AuxiliaryAssembly == AuxiliaryModel.Advanced, - WriteModalResults = _mode != ExecutionMode.Declaration || WriteModalResults - }; - var current = i++; - var builder = new PowertrainBuilder(modContainer, modData => { - if (SumData != null) { - SumData.Write(modData, JobNumber, current, d); - //SumData.Write(modContainer, d.JobName, string.Format("{0}-{1}", JobNumber, current), - // d.Cycle.Name + Constants.FileExtensions.CycleFile, mass, loading, volume ?? 0.SI<CubicMeter>(), gearCount); - } - }); - - VectoRun run; - - switch (data.Cycle.CycleType) { - case CycleType.DistanceBased: - run = new DistanceRun(builder.Build(data)); - break; - case CycleType.EngineOnly: - case CycleType.PWheel: - case CycleType.MeasuredSpeed: - case CycleType.MeasuredSpeedGear: - run = new TimeRun(builder.Build(data)); - break; - case CycleType.PTO: - throw new VectoException("PTO Cycle can not be used as main cycle!"); - default: - throw new ArgumentOutOfRangeException("CycleType unknown:" + data.Cycle.CycleType); - } - - if (Validate) { - var validationErrors = run.Validate(_mode, data.GearboxData == null ? (GearboxType?)null : data.GearboxData.Type, - data.Mission != null && data.Mission.MissionType.IsEMS()); - if (validationErrors.Any()) { - throw new VectoException("Validation of Run-Data Failed: " + - string.Join("\n", validationErrors.Select(r => r.ErrorMessage + string.Join("; ", r.MemberNames)))); - } - } - yield return run; - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.InputData; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.InputData; +using TUGraz.VectoCore.InputData.Reader.Impl; +using TUGraz.VectoCore.Models.Declaration; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCore.OutputData.ModFilter; +using TUGraz.VectoCore.OutputData.XML; + +namespace TUGraz.VectoCore.Models.Simulation.Impl +{ + public class SimulatorFactory : LoggingObject + { + private static int _jobNumberCounter; + + private readonly ExecutionMode _mode; + private bool _engineOnlyMode; + + public SimulatorFactory(ExecutionMode mode, IInputDataProvider dataProvider, IOutputDataWriter writer, + IDeclarationReport declarationReport = null, bool validate = true) + { + Log.Info("########## VectoCore Version {0} ##########", Assembly.GetExecutingAssembly().GetName().Version); + JobNumber = Interlocked.Increment(ref _jobNumberCounter); + _mode = mode; + ModWriter = writer; + Validate = validate; + + int workerThreads; + int completionThreads; + ThreadPool.GetMinThreads(out workerThreads, out completionThreads); + if (workerThreads < 12) { + workerThreads = 12; + } + ThreadPool.SetMinThreads(workerThreads, completionThreads); + + switch (mode) { + case ExecutionMode.Declaration: + var declDataProvider = ToDeclarationInputDataProvider(dataProvider); + var report = declarationReport ?? new XMLDeclarationReport(writer); + DataReader = new DeclarationModeVectoRunDataFactory(declDataProvider, report); + break; + case ExecutionMode.Engineering: + CreateEngineeringDataReader(dataProvider); + break; + default: + throw new VectoException("Unkown factory mode in SimulatorFactory: {0}", mode); + } + } + + private void CreateEngineeringDataReader(IInputDataProvider dataProvider) + { + var engDataProvider = ToEngineeringInputDataProvider(dataProvider); + if (engDataProvider.JobInputData().EngineOnlyMode) { + DataReader = new EngineOnlyVectoRunDataFactory(engDataProvider); + _engineOnlyMode = true; + } else { + DataReader = new EngineeringModeVectoRunDataFactory(engDataProvider); + } + } + + private static IDeclarationInputDataProvider ToDeclarationInputDataProvider(IInputDataProvider dataProvider) + { + var declDataProvider = dataProvider as IDeclarationInputDataProvider; + if (declDataProvider == null) { + throw new VectoException("InputDataProvider does not implement DeclarationData interface"); + } + return declDataProvider; + } + + private static IEngineeringInputDataProvider ToEngineeringInputDataProvider(IInputDataProvider dataProvider) + { + var engDataProvider = dataProvider as IEngineeringInputDataProvider; + if (engDataProvider == null) { + throw new VectoException("InputDataProvider does not implement Engineering interface"); + } + return engDataProvider; + } + + public bool Validate { get; set; } + + public IVectoRunDataFactory DataReader { get; private set; } + + public SummaryDataContainer SumData { get; set; } + + public IOutputDataWriter ModWriter { get; private set; } + + public int JobNumber { get; set; } + + public bool WriteModalResults { get; set; } + public bool ModalResults1Hz { get; set; } + public bool ActualModalData { get; set; } + + /// <summary> + /// Creates powertrain and initializes it with the component's data. + /// </summary> + /// <returns>new VectoRun Instance</returns> + public IEnumerable<IVectoRun> SimulationRuns() + { + var i = 0; + + + var warning1Hz = false; + + foreach (var data in DataReader.NextRun()) { + var d = data; + var addReportResult = PrepareReport(data); + if (!data.Cycle.CycleType.IsDistanceBased() && ModalResults1Hz && !warning1Hz) { + Log.Error("Output filter for 1Hz results is only available for distance-based cycles!"); + warning1Hz = true; + } + IModalDataContainer modContainer = + new ModalDataContainer(data, ModWriter, + addReportResult: _mode == ExecutionMode.Declaration ? addReportResult : null, + writeEngineOnly: _engineOnlyMode, + filter: GetModDataFilter(data)) { + WriteAdvancedAux = data.AdvancedAux != null && data.AdvancedAux.AuxiliaryAssembly == AuxiliaryModel.Advanced, + WriteModalResults = _mode != ExecutionMode.Declaration || WriteModalResults + }; + var current = i++; + var builder = new PowertrainBuilder(modContainer, modData => { + if (SumData != null) { + SumData.Write(modData, JobNumber, current, d); + } + }); + + var run = GetVectoRun(data, builder); + + if (Validate) { + ValidateVectoRunData(run, data.GearboxData == null ? (GearboxType?)null : data.GearboxData.Type, + data.Mission != null && data.Mission.MissionType.IsEMS()); + } + yield return run; + } + } + + private IModalDataFilter[] GetModDataFilter(VectoRunData data) + { + var modDataFilter = ModalResults1Hz + ? new IModalDataFilter[] { new ModalData1HzFilter() } + : null; + + if (ActualModalData) { + modDataFilter = new IModalDataFilter[] { new ActualModalDataFilter(), }; + } + return data.Cycle.CycleType.IsDistanceBased() && ModalResults1Hz || ActualModalData ? modDataFilter : null; + } + + private void ValidateVectoRunData(VectoRun run, GearboxType? gearboxtype, bool isEms) + { + var validationErrors = run.Validate(_mode, gearboxtype, isEms); + if (validationErrors.Any()) { + throw new VectoException("Validation of Run-Data Failed: " + + string.Join("\n", validationErrors.Select(r => r.ErrorMessage + string.Join("; ", r.MemberNames)))); + } + } + + private static VectoRun GetVectoRun(VectoRunData data, PowertrainBuilder builder) + { + VectoRun run; + switch (data.Cycle.CycleType) { + case CycleType.DistanceBased: + run = new DistanceRun(builder.Build(data)); + break; + case CycleType.EngineOnly: + case CycleType.PWheel: + case CycleType.MeasuredSpeed: + case CycleType.MeasuredSpeedGear: + run = new TimeRun(builder.Build(data)); + break; + case CycleType.PTO: + throw new VectoException("PTO Cycle can not be used as main cycle!"); + default: + throw new ArgumentOutOfRangeException("CycleType unknown:" + data.Cycle.CycleType); + } + return run; + } + + private static Action<ModalDataContainer> PrepareReport(VectoRunData data) + { + if (data.Report != null) { + data.Report.PrepareResult(data.Loading, data.Mission, data); + } + Action<ModalDataContainer> addReportResult = writer => { + if (data.Report != null) { + data.Report.AddResult(data.Loading, data.Mission, data, writer); + } + }; + return addReportResult; + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs index d9f0b03413..d9fb06e6d8 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs @@ -29,386 +29,395 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Connector.Ports; -using TUGraz.VectoCore.Models.Connector.Ports.Impl; -using TUGraz.VectoCore.Models.Simulation; -using TUGraz.VectoCore.Models.Simulation.Data; -using TUGraz.VectoCore.Models.Simulation.DataBus; -using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; -using TUGraz.VectoCore.OutputData; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Impl -{ - public class ATGearbox : AbstractGearbox<ATGearbox.ATGearboxState> - { - private readonly IShiftStrategy _strategy; - protected internal readonly TorqueConverter TorqueConverter; - private IIdleController _idleController; - protected bool RequestAfterGearshift; - - public bool TorqueConverterLocked - { - get { return CurrentState.TorqueConverterLocked; } - set { CurrentState.TorqueConverterLocked = value; } - } - - public ATGearbox(IVehicleContainer container, IShiftStrategy strategy, VectoRunData runData) - : base(container, runData) - { - _strategy = strategy; - _strategy.Gearbox = this; - LastShift = -double.MaxValue.SI<Second>(); - TorqueConverter = new TorqueConverter(this, _strategy, container, ModelData.TorqueConverterData, - runData); - } - - public IIdleController IdleController - { - get { return _idleController; } - set { - _idleController = value; - _idleController.RequestPort = NextComponent; - } - } - - public bool Disengaged - { - get { return CurrentState.Disengaged; } - set { CurrentState.Disengaged = value; } - } - - public override void Connect(ITnOutPort other) - { - base.Connect(other); - TorqueConverter.NextComponent = other; - } - - public override GearInfo NextGear - { - get { return _strategy.NextGear; } - } - - public override bool ClutchClosed(Second absTime) - { - return absTime.IsGreater(DataBus.AbsTime) || - !(CurrentState.Disengaged || (DataBus.DriverBehavior == DrivingBehavior.Halted)); - } - - public override IResponse Initialize(NewtonMeter outTorque, PerSecond outAngularVelocity) - { - if (CurrentState.Disengaged) { - Gear = _strategy.InitGear(0.SI<Second>(), Constants.SimulationSettings.TargetTimeInterval, outTorque, - outAngularVelocity); - } - var inAngularVelocity = 0.SI<PerSecond>(); - var inTorque = 0.SI<NewtonMeter>(); - var effectiveRatio = ModelData.Gears[Gear].Ratio; - var effectiveLossMap = ModelData.Gears[Gear].LossMap; - if (!CurrentState.TorqueConverterLocked) { - effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; - effectiveLossMap = ModelData.Gears[Gear].TorqueConverterGearLossMap; - } - if (!DataBus.VehicleStopped) { - inAngularVelocity = outAngularVelocity * effectiveRatio; - var torqueLossResult = effectiveLossMap.GetTorqueLoss(outAngularVelocity, outTorque); - CurrentState.TorqueLossResult = torqueLossResult; - - inTorque = outTorque / effectiveRatio + torqueLossResult.Value; - } - if (CurrentState.Disengaged) { - return NextComponent.Initialize(0.SI<NewtonMeter>(), null); - } - - if (!CurrentState.TorqueConverterLocked && !ModelData.Gears[Gear].HasTorqueConverter) { - throw new VectoSimulationException( - "Torque converter requested by strategy for gear without torque converter!"); - } - var response = CurrentState.TorqueConverterLocked - ? NextComponent.Initialize(inTorque, inAngularVelocity) - : TorqueConverter.Initialize(inTorque, inAngularVelocity); - - CurrentState.TorqueLossResult = new TransmissionLossMap.LossMapResult() { - Value = 0.SI<NewtonMeter>(), - Extrapolated = false - }; - - PreviousState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); - PreviousState.Gear = Gear; - PreviousState.TorqueConverterLocked = CurrentState.TorqueConverterLocked; - return response; - } - - internal ResponseDryRun Initialize(uint gear, bool torqueConverterLocked, NewtonMeter outTorque, - PerSecond outAngularVelocity) - { - var effectiveRatio = torqueConverterLocked - ? ModelData.Gears[gear].Ratio - : ModelData.Gears[gear].TorqueConverterRatio; - - var inAngularVelocity = outAngularVelocity * effectiveRatio; - var torqueLossResult = torqueConverterLocked - ? ModelData.Gears[gear].LossMap.GetTorqueLoss(outAngularVelocity, outTorque) - : ModelData.Gears[gear].TorqueConverterGearLossMap.GetTorqueLoss(outAngularVelocity, outTorque); - - var inTorque = outTorque / effectiveRatio + torqueLossResult.Value; - - IResponse response; - if (torqueConverterLocked) { - response = NextComponent.Initialize(inTorque, inAngularVelocity); - } else { - if (!ModelData.Gears[gear].HasTorqueConverter) { - throw new VectoSimulationException( - "Torque converter requested by strategy for gear without torque converter!"); - } - response = TorqueConverter.Initialize(inTorque, inAngularVelocity); - } - - response.Switch(). - Case<ResponseSuccess>(). // accept - Case<ResponseUnderload>(). // accept - Case<ResponseOverload>(). // accept - Default(r => { throw new UnexpectedResponseException("AT-Gearbox.Initialize", r); }); - - return new ResponseDryRun { - Source = this, - EngineSpeed = response.EngineSpeed, - EnginePowerRequest = response.EnginePowerRequest, - GearboxPowerRequest = outTorque * outAngularVelocity, - }; - } - - public override IResponse Request(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - bool dryRun = false) - { - IterationStatistics.Increment(this, "Requests"); - - Log.Debug("AT-Gearbox Power Request: torque: {0}, angularVelocity: {1}", outTorque, outAngularVelocity); - - if (!dryRun && - ((DataBus.VehicleStopped && outAngularVelocity > 0) || - (CurrentState.Disengaged && outTorque.IsGreater(0, 1e-3)))) { - Gear = 1; - CurrentState.TorqueConverterLocked = false; - LastShift = absTime; - CurrentState.Disengaged = false; - } - - IResponse retVal; - var count = 0; - var loop = false; - if (RequestAfterGearshift) { - LastShift = absTime; - Gear = _strategy.Engage(absTime, dt, outTorque, outAngularVelocity); - CurrentState.PowershiftLossEnergy = ComputeShiftLosses(outTorque, outAngularVelocity); - } else { - if (PreviousState.PowershiftLossEnergy != null && PreviousState.PowershiftLossEnergy.IsGreater(0)) { - CurrentState.PowershiftLossEnergy = PreviousState.PowershiftLossEnergy; - } - } - do { - if (CurrentState.Disengaged || (DataBus.DriverBehavior == DrivingBehavior.Halted)) { - // only when vehicle is halted or close before halting - retVal = RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun); - } else { - CurrentState.Disengaged = false; - retVal = RequestEngaged(absTime, dt, outTorque, outAngularVelocity, dryRun); - IdleController.Reset(); - } - if (retVal is ResponseGearShift) { - if (ConsiderShiftLosses(_strategy.NextGear, outTorque)) { - retVal = new ResponseFailTimeInterval { - Source = this, - DeltaT = ModelData.PowershiftShiftTime, - GearboxPowerRequest = - outTorque * (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0 - }; - RequestAfterGearshift = true; - LastShift = absTime; - } else { - loop = true; - Gear = _strategy.Engage(absTime, dt, outTorque, outAngularVelocity); - LastShift = absTime; - } - } - } while (loop && ++count < 2); - - retVal.GearboxPowerRequest = outTorque * (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; - return retVal; - } - - private IResponse RequestEngaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - bool dryRun) - { - if (!CurrentState.TorqueConverterLocked && !ModelData.Gears[Gear].HasTorqueConverter) { - throw new VectoSimulationException( - "Torque converter requested by strategy for gear without torque converter!"); - } - - var effectiveRatio = ModelData.Gears[Gear].Ratio; - var effectiveLossMap = ModelData.Gears[Gear].LossMap; - if (!CurrentState.TorqueConverterLocked) { - effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; - effectiveLossMap = ModelData.Gears[Gear].TorqueConverterGearLossMap; - } - - var inAngularVelocity = outAngularVelocity * effectiveRatio; - var avgOutAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; - var avgInAngularVelocity = (PreviousState.InAngularVelocity + inAngularVelocity) / 2.0; - var inTorqueLossResult = effectiveLossMap.GetTorqueLoss(avgOutAngularVelocity, outTorque); - var inTorque = outTorque * (avgOutAngularVelocity / avgInAngularVelocity) + inTorqueLossResult.Value; - - var inertiaTorqueLossOut = !inAngularVelocity.IsEqual(0) - ? Formulas.InertiaPower(outAngularVelocity, PreviousState.OutAngularVelocity, ModelData.Inertia, dt) / - avgOutAngularVelocity - : 0.SI<NewtonMeter>(); - inTorque += inertiaTorqueLossOut / effectiveRatio; - - if (CurrentState.PowershiftLossEnergy != null) { - var remainingShiftLossLime = ModelData.PowershiftShiftTime - (absTime - LastShift); - if (remainingShiftLossLime.IsGreater(0)) { - var aliquotEnergyLoss = CurrentState.PowershiftLossEnergy * VectoMath.Min(1.0, dt / remainingShiftLossLime); - var avgEngineSpeed = (DataBus.EngineSpeed + outAngularVelocity * ModelData.Gears[Gear].Ratio) / 2; - CurrentState.PowershiftLoss = aliquotEnergyLoss / dt / avgEngineSpeed; - inTorque += CurrentState.PowershiftLoss; - CurrentState.PowershiftLossEnergy -= aliquotEnergyLoss; - //inTorque += CurrentState.PowershiftLossEnergy; - } - } - - if (!dryRun) { - CurrentState.InertiaTorqueLossOut = inertiaTorqueLossOut; - CurrentState.TorqueLossResult = inTorqueLossResult; - CurrentState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); - CurrentState.Gear = Gear; - CurrentState.TransmissionTorqueLoss = inTorque * effectiveRatio - outTorque; - TorqueConverter.Locked(CurrentState.InTorque, CurrentState.InAngularVelocity, CurrentState.InTorque, - CurrentState.InAngularVelocity); - } - - if (!CurrentState.TorqueConverterLocked) { - return TorqueConverter.Request(absTime, dt, inTorque, inAngularVelocity, dryRun); - } - var retVal = NextComponent.Request(absTime, dt, inTorque, inAngularVelocity, dryRun); - if (!dryRun && retVal is ResponseSuccess && - _strategy.ShiftRequired(absTime, dt, outTorque, outAngularVelocity, inTorque, inAngularVelocity, Gear, - LastShift)) { - return new ResponseGearShift { Source = this }; - } - - return retVal; - } - - private IResponse RequestDisengaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - bool dryRun) - { - var avgAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; - if (dryRun) { - // if gearbox is disengaged the 0[W]-line is the limit for drag and full load. - return new ResponseDryRun { - Source = this, - GearboxPowerRequest = outTorque * avgAngularVelocity, - DeltaDragLoad = outTorque * avgAngularVelocity, - DeltaFullLoad = outTorque * avgAngularVelocity, - }; - } - if ((outTorque * avgAngularVelocity).IsGreater(0.SI<Watt>(), - Constants.SimulationSettings.LineSearchTolerance)) { - return new ResponseOverload { - Source = this, - Delta = outTorque * avgAngularVelocity, - GearboxPowerRequest = outTorque * avgAngularVelocity - }; - } - - if ((outTorque * avgAngularVelocity).IsSmaller(0.SI<Watt>(), - Constants.SimulationSettings.LineSearchTolerance)) { - return new ResponseUnderload { - Source = this, - Delta = outTorque * avgAngularVelocity, - GearboxPowerRequest = outTorque * avgAngularVelocity - }; - } - - Log.Debug("Invoking IdleController..."); - - var retval = IdleController.Request(absTime, dt, 0.SI<NewtonMeter>(), null); - retval.ClutchPowerRequest = 0.SI<Watt>(); - - // no dry-run - update state - var effectiveRatio = ModelData.Gears[Gear].Ratio; - if (!CurrentState.TorqueConverterLocked) { - effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; - } - CurrentState.SetState(0.SI<NewtonMeter>(), outAngularVelocity * effectiveRatio, outTorque, - outAngularVelocity); - CurrentState.Gear = 1; - CurrentState.TorqueConverterLocked = !ModelData.Gears[Gear].HasTorqueConverter; - CurrentState.TorqueLossResult = new TransmissionLossMap.LossMapResult() { - Extrapolated = false, - Value = 0.SI<NewtonMeter>() - }; - TorqueConverter.Locked(DataBus.VehicleStopped ? 0.SI<NewtonMeter>() : CurrentState.InTorque, retval.EngineSpeed, - CurrentState.InTorque, - outAngularVelocity * effectiveRatio); - - - return retval; - } - - protected override void DoWriteModalResults(IModalDataContainer container) - { - var avgInAngularSpeed = (PreviousState.InAngularVelocity + CurrentState.InAngularVelocity) / 2.0; - var avgOutAngularSpeed = (PreviousState.OutAngularVelocity + CurrentState.OutAngularVelocity) / 2.0; - - container[ModalResultField.Gear] = CurrentState.Disengaged || DataBus.VehicleStopped ? 0 : Gear; - container[ModalResultField.TC_Locked] = CurrentState.TorqueConverterLocked; - container[ModalResultField.P_gbx_loss] = CurrentState.InTorque * avgInAngularSpeed - - CurrentState.OutTorque * avgOutAngularSpeed; - container[ModalResultField.P_gbx_inertia] = CurrentState.InertiaTorqueLossOut * avgOutAngularSpeed; - container[ModalResultField.P_gbx_in] = CurrentState.InTorque * avgInAngularSpeed; - container[ModalResultField.P_gbx_shift_loss] = CurrentState.PowershiftLoss.DefaultIfNull(0) * avgInAngularSpeed; - container[ModalResultField.n_gbx_out_avg] = avgOutAngularSpeed; - container[ModalResultField.T_gbx_out] = CurrentState.OutTorque; - } - - protected override void DoCommitSimulationStep() - { - if (!CurrentState.Disengaged && CurrentState.TorqueLossResult != null && - CurrentState.TorqueLossResult.Extrapolated) { - Log.Warn( - "Gear {0} LossMap data was extrapolated: range for loss map is not sufficient: n:{1}, torque:{2}, ratio:{3}", - Gear, CurrentState.OutAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.OutTorque, - ModelData.Gears[Gear].Ratio); - if (DataBus.ExecutionMode == ExecutionMode.Declaration) { - throw new VectoException( - "Gear {0} LossMap data was extrapolated in Declaration Mode: range for loss map is not sufficient: n:{1}, torque:{2}, ratio:{3}", - Gear, CurrentState.InAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.InTorque, - ModelData.Gears[Gear].Ratio); - } - } - RequestAfterGearshift = false; - - if (DataBus.VehicleStopped) { - CurrentState.Disengaged = true; - } - - AdvanceState(); - - CurrentState.TorqueConverterLocked = PreviousState.TorqueConverterLocked; - CurrentState.Disengaged = PreviousState.Disengaged; - } - - public class ATGearboxState : GearboxState - { - public bool TorqueConverterLocked; - public bool Disengaged = true; - public WattSecond PowershiftLossEnergy; - public NewtonMeter PowershiftLoss; - } - } +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Connector.Ports; +using TUGraz.VectoCore.Models.Connector.Ports.Impl; +using TUGraz.VectoCore.Models.Simulation; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.Simulation.DataBus; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; +using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + public class ATGearbox : AbstractGearbox<ATGearbox.ATGearboxState> + { + private readonly IShiftStrategy _strategy; + protected internal readonly TorqueConverter TorqueConverter; + private IIdleController _idleController; + protected bool RequestAfterGearshift; + + public bool TorqueConverterLocked + { + get { return CurrentState.TorqueConverterLocked; } + set { CurrentState.TorqueConverterLocked = value; } + } + + public ATGearbox(IVehicleContainer container, IShiftStrategy strategy, VectoRunData runData) + : base(container, runData) + { + _strategy = strategy; + _strategy.Gearbox = this; + LastShift = -double.MaxValue.SI<Second>(); + TorqueConverter = new TorqueConverter(this, _strategy, container, ModelData.TorqueConverterData, + runData); + } + + public IIdleController IdleController + { + get { return _idleController; } + set + { + _idleController = value; + _idleController.RequestPort = NextComponent; + } + } + + public bool Disengaged + { + get { return CurrentState.Disengaged; } + set { CurrentState.Disengaged = value; } + } + + public override void Connect(ITnOutPort other) + { + base.Connect(other); + TorqueConverter.NextComponent = other; + } + + public override GearInfo NextGear + { + get { return _strategy.NextGear; } + } + + public override bool ClutchClosed(Second absTime) + { + return absTime.IsGreater(DataBus.AbsTime) || + !(CurrentState.Disengaged || (DataBus.DriverBehavior == DrivingBehavior.Halted)); + } + + public override IResponse Initialize(NewtonMeter outTorque, PerSecond outAngularVelocity) + { + if (CurrentState.Disengaged) { + Gear = _strategy.InitGear(0.SI<Second>(), Constants.SimulationSettings.TargetTimeInterval, outTorque, + outAngularVelocity); + } + var inAngularVelocity = 0.SI<PerSecond>(); + var inTorque = 0.SI<NewtonMeter>(); + var effectiveRatio = ModelData.Gears[Gear].Ratio; + var effectiveLossMap = ModelData.Gears[Gear].LossMap; + if (!CurrentState.TorqueConverterLocked) { + effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; + effectiveLossMap = ModelData.Gears[Gear].TorqueConverterGearLossMap; + } + if (!DataBus.VehicleStopped) { + inAngularVelocity = outAngularVelocity * effectiveRatio; + var torqueLossResult = effectiveLossMap.GetTorqueLoss(outAngularVelocity, outTorque); + CurrentState.TorqueLossResult = torqueLossResult; + + inTorque = outTorque / effectiveRatio + torqueLossResult.Value; + } + if (CurrentState.Disengaged) { + return NextComponent.Initialize(0.SI<NewtonMeter>(), null); + } + + if (!CurrentState.TorqueConverterLocked && !ModelData.Gears[Gear].HasTorqueConverter) { + throw new VectoSimulationException( + "Torque converter requested by strategy for gear without torque converter!"); + } + var response = CurrentState.TorqueConverterLocked + ? NextComponent.Initialize(inTorque, inAngularVelocity) + : TorqueConverter.Initialize(inTorque, inAngularVelocity); + + CurrentState.TorqueLossResult = new TransmissionLossMap.LossMapResult() { + Value = 0.SI<NewtonMeter>(), + Extrapolated = false + }; + + PreviousState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); + PreviousState.Gear = Gear; + PreviousState.TorqueConverterLocked = CurrentState.TorqueConverterLocked; + return response; + } + + internal ResponseDryRun Initialize(uint gear, bool torqueConverterLocked, NewtonMeter outTorque, + PerSecond outAngularVelocity) + { + var effectiveRatio = torqueConverterLocked + ? ModelData.Gears[gear].Ratio + : ModelData.Gears[gear].TorqueConverterRatio; + + var inAngularVelocity = outAngularVelocity * effectiveRatio; + var torqueLossResult = torqueConverterLocked + ? ModelData.Gears[gear].LossMap.GetTorqueLoss(outAngularVelocity, outTorque) + : ModelData.Gears[gear].TorqueConverterGearLossMap.GetTorqueLoss(outAngularVelocity, outTorque); + + var inTorque = outTorque / effectiveRatio + torqueLossResult.Value; + + IResponse response; + if (torqueConverterLocked) { + response = NextComponent.Initialize(inTorque, inAngularVelocity); + } else { + if (!ModelData.Gears[gear].HasTorqueConverter) { + throw new VectoSimulationException( + "Torque converter requested by strategy for gear without torque converter!"); + } + response = TorqueConverter.Initialize(inTorque, inAngularVelocity); + } + + response.Switch(). + Case<ResponseSuccess>(). // accept + Case<ResponseUnderload>(). // accept + Case<ResponseOverload>(). // accept + Default(r => { + throw new UnexpectedResponseException("AT-Gearbox.Initialize", r); + }); + + return new ResponseDryRun { + Source = this, + EngineSpeed = response.EngineSpeed, + EnginePowerRequest = response.EnginePowerRequest, + GearboxPowerRequest = outTorque * outAngularVelocity, + }; + } + + public override IResponse Request(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + bool dryRun = false) + { + IterationStatistics.Increment(this, "Requests"); + + Log.Debug("AT-Gearbox Power Request: torque: {0}, angularVelocity: {1}", outTorque, outAngularVelocity); + + var driveOffSpeed = DataBus.VehicleStopped && outAngularVelocity > 0; + var driveOffTorque = CurrentState.Disengaged && outTorque.IsGreater(0, 1e-3); + if (!dryRun && (driveOffSpeed || driveOffTorque)) { + Gear = 1; + CurrentState.TorqueConverterLocked = false; + LastShift = absTime; + CurrentState.Disengaged = false; + } + + IResponse retVal; + var count = 0; + var loop = false; + SetPowershiftLossEnergy(absTime, dt, outTorque, outAngularVelocity); + do { + if (CurrentState.Disengaged || (DataBus.DriverBehavior == DrivingBehavior.Halted)) { + // only when vehicle is halted or close before halting + retVal = RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun); + } else { + CurrentState.Disengaged = false; + retVal = RequestEngaged(absTime, dt, outTorque, outAngularVelocity, dryRun); + IdleController.Reset(); + } + if (!(retVal is ResponseGearShift)) { + continue; + } + if (ConsiderShiftLosses(_strategy.NextGear, outTorque)) { + retVal = new ResponseFailTimeInterval { + Source = this, + DeltaT = ModelData.PowershiftShiftTime, + GearboxPowerRequest = + outTorque * (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0 + }; + RequestAfterGearshift = true; + LastShift = absTime; + } else { + loop = true; + Gear = _strategy.Engage(absTime, dt, outTorque, outAngularVelocity); + LastShift = absTime; + } + } while (loop && ++count < 2); + + retVal.GearboxPowerRequest = outTorque * (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; + return retVal; + } + + private void SetPowershiftLossEnergy(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity) + { + if (RequestAfterGearshift) { + LastShift = absTime; + Gear = _strategy.Engage(absTime, dt, outTorque, outAngularVelocity); + CurrentState.PowershiftLossEnergy = ComputeShiftLosses(outTorque, outAngularVelocity); + } else { + if (PreviousState.PowershiftLossEnergy != null && PreviousState.PowershiftLossEnergy.IsGreater(0)) { + CurrentState.PowershiftLossEnergy = PreviousState.PowershiftLossEnergy; + } + } + } + + private IResponse RequestEngaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + bool dryRun) + { + if (!CurrentState.TorqueConverterLocked && !ModelData.Gears[Gear].HasTorqueConverter) { + throw new VectoSimulationException( + "Torque converter requested by strategy for gear without torque converter!"); + } + + var effectiveRatio = ModelData.Gears[Gear].Ratio; + var effectiveLossMap = ModelData.Gears[Gear].LossMap; + if (!CurrentState.TorqueConverterLocked) { + effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; + effectiveLossMap = ModelData.Gears[Gear].TorqueConverterGearLossMap; + } + + var inAngularVelocity = outAngularVelocity * effectiveRatio; + var avgOutAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; + var avgInAngularVelocity = (PreviousState.InAngularVelocity + inAngularVelocity) / 2.0; + var inTorqueLossResult = effectiveLossMap.GetTorqueLoss(avgOutAngularVelocity, outTorque); + var inTorque = outTorque * (avgOutAngularVelocity / avgInAngularVelocity) + inTorqueLossResult.Value; + + var inertiaTorqueLossOut = !inAngularVelocity.IsEqual(0) + ? Formulas.InertiaPower(outAngularVelocity, PreviousState.OutAngularVelocity, ModelData.Inertia, dt) / + avgOutAngularVelocity + : 0.SI<NewtonMeter>(); + inTorque += inertiaTorqueLossOut / effectiveRatio; + + if (CurrentState.PowershiftLossEnergy != null) { + var remainingShiftLossLime = ModelData.PowershiftShiftTime - (absTime - LastShift); + if (remainingShiftLossLime.IsGreater(0)) { + var aliquotEnergyLoss = CurrentState.PowershiftLossEnergy * VectoMath.Min(1.0, dt / remainingShiftLossLime); + var avgEngineSpeed = (DataBus.EngineSpeed + outAngularVelocity * ModelData.Gears[Gear].Ratio) / 2; + CurrentState.PowershiftLoss = aliquotEnergyLoss / dt / avgEngineSpeed; + inTorque += CurrentState.PowershiftLoss; + CurrentState.PowershiftLossEnergy -= aliquotEnergyLoss; + //inTorque += CurrentState.PowershiftLossEnergy; + } + } + + if (!dryRun) { + CurrentState.InertiaTorqueLossOut = inertiaTorqueLossOut; + CurrentState.TorqueLossResult = inTorqueLossResult; + CurrentState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); + CurrentState.Gear = Gear; + CurrentState.TransmissionTorqueLoss = inTorque * effectiveRatio - outTorque; + TorqueConverter.Locked(CurrentState.InTorque, CurrentState.InAngularVelocity, CurrentState.InTorque, + CurrentState.InAngularVelocity); + } + + if (!CurrentState.TorqueConverterLocked) { + return TorqueConverter.Request(absTime, dt, inTorque, inAngularVelocity, dryRun); + } + var retVal = NextComponent.Request(absTime, dt, inTorque, inAngularVelocity, dryRun); + if (!dryRun && retVal is ResponseSuccess && + _strategy.ShiftRequired(absTime, dt, outTorque, outAngularVelocity, inTorque, inAngularVelocity, Gear, + LastShift)) { + return new ResponseGearShift { Source = this }; + } + + return retVal; + } + + private IResponse RequestDisengaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + bool dryRun) + { + var avgAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; + if (dryRun) { + // if gearbox is disengaged the 0[W]-line is the limit for drag and full load. + return new ResponseDryRun { + Source = this, + GearboxPowerRequest = outTorque * avgAngularVelocity, + DeltaDragLoad = outTorque * avgAngularVelocity, + DeltaFullLoad = outTorque * avgAngularVelocity, + }; + } + if ((outTorque * avgAngularVelocity).IsGreater(0.SI<Watt>(), + Constants.SimulationSettings.LineSearchTolerance)) { + return new ResponseOverload { + Source = this, + Delta = outTorque * avgAngularVelocity, + GearboxPowerRequest = outTorque * avgAngularVelocity + }; + } + + if ((outTorque * avgAngularVelocity).IsSmaller(0.SI<Watt>(), + Constants.SimulationSettings.LineSearchTolerance)) { + return new ResponseUnderload { + Source = this, + Delta = outTorque * avgAngularVelocity, + GearboxPowerRequest = outTorque * avgAngularVelocity + }; + } + + Log.Debug("Invoking IdleController..."); + + var retval = IdleController.Request(absTime, dt, 0.SI<NewtonMeter>(), null); + retval.ClutchPowerRequest = 0.SI<Watt>(); + + // no dry-run - update state + var effectiveRatio = ModelData.Gears[Gear].Ratio; + if (!CurrentState.TorqueConverterLocked) { + effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; + } + CurrentState.SetState(0.SI<NewtonMeter>(), outAngularVelocity * effectiveRatio, outTorque, + outAngularVelocity); + CurrentState.Gear = 1; + CurrentState.TorqueConverterLocked = !ModelData.Gears[Gear].HasTorqueConverter; + CurrentState.TorqueLossResult = new TransmissionLossMap.LossMapResult() { + Extrapolated = false, + Value = 0.SI<NewtonMeter>() + }; + TorqueConverter.Locked(DataBus.VehicleStopped ? 0.SI<NewtonMeter>() : CurrentState.InTorque, retval.EngineSpeed, + CurrentState.InTorque, + outAngularVelocity * effectiveRatio); + + + return retval; + } + + protected override void DoWriteModalResults(IModalDataContainer container) + { + var avgInAngularSpeed = (PreviousState.InAngularVelocity + CurrentState.InAngularVelocity) / 2.0; + var avgOutAngularSpeed = (PreviousState.OutAngularVelocity + CurrentState.OutAngularVelocity) / 2.0; + + container[ModalResultField.Gear] = CurrentState.Disengaged || DataBus.VehicleStopped ? 0 : Gear; + container[ModalResultField.TC_Locked] = CurrentState.TorqueConverterLocked; + container[ModalResultField.P_gbx_loss] = CurrentState.InTorque * avgInAngularSpeed - + CurrentState.OutTorque * avgOutAngularSpeed; + container[ModalResultField.P_gbx_inertia] = CurrentState.InertiaTorqueLossOut * avgOutAngularSpeed; + container[ModalResultField.P_gbx_in] = CurrentState.InTorque * avgInAngularSpeed; + container[ModalResultField.P_gbx_shift_loss] = CurrentState.PowershiftLoss.DefaultIfNull(0) * avgInAngularSpeed; + container[ModalResultField.n_gbx_out_avg] = avgOutAngularSpeed; + container[ModalResultField.T_gbx_out] = CurrentState.OutTorque; + } + + protected override void DoCommitSimulationStep() + { + if (!CurrentState.Disengaged && CurrentState.TorqueLossResult != null && + CurrentState.TorqueLossResult.Extrapolated) { + Log.Warn( + "Gear {0} LossMap data was extrapolated: range for loss map is not sufficient: n:{1}, torque:{2}, ratio:{3}", + Gear, CurrentState.OutAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.OutTorque, + ModelData.Gears[Gear].Ratio); + if (DataBus.ExecutionMode == ExecutionMode.Declaration) { + throw new VectoException( + "Gear {0} LossMap data was extrapolated in Declaration Mode: range for loss map is not sufficient: n:{1}, torque:{2}, ratio:{3}", + Gear, CurrentState.InAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.InTorque, + ModelData.Gears[Gear].Ratio); + } + } + RequestAfterGearshift = false; + + if (DataBus.VehicleStopped) { + CurrentState.Disengaged = true; + } + + AdvanceState(); + + CurrentState.TorqueConverterLocked = PreviousState.TorqueConverterLocked; + CurrentState.Disengaged = PreviousState.Disengaged; + } + + public class ATGearboxState : GearboxState + { + public bool TorqueConverterLocked; + public bool Disengaged = true; + public WattSecond PowershiftLossEnergy; + public NewtonMeter PowershiftLoss; + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs index d70578e50f..3031474f54 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs @@ -29,326 +29,346 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Simulation.DataBus; -using TUGraz.VectoCore.Models.SimulationComponent.Data; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Impl -{ - public class ATShiftStrategy : BaseShiftStrategy - { - private ATGearbox _gearbox; - private readonly NextGearState _nextGear = new NextGearState(); - - public override IGearbox Gearbox - { - get { return _gearbox; } - set { - _gearbox = value as ATGearbox; - if (_gearbox == null) { - throw new VectoException("AT Shift strategy can only handle AT gearboxes, given: {0}", value.GetType()); - } - } - } - - public override GearInfo NextGear - { - get { return new GearInfo(_nextGear.Gear, _nextGear.TorqueConverterLocked); } - } - - public ATShiftStrategy(GearboxData data, IDataBus dataBus) : base(data, dataBus) {} - - public override uint InitGear(Second absTime, Second dt, NewtonMeter torque, PerSecond outAngularVelocity) - { - if (DataBus.VehicleSpeed.IsEqual(0)) { - // AT always starts in first gear and TC active! - _gearbox.TorqueConverterLocked = false; - _gearbox.Disengaged = true; - return 1; - } - var torqueConverterLocked = true; - for (var gear = ModelData.Gears.Keys.Max(); gear > 1; gear--) { - if (_gearbox.ModelData.Gears[gear].HasTorqueConverter) { - torqueConverterLocked = false; - } - var response = _gearbox.Initialize(gear, torqueConverterLocked, torque, outAngularVelocity); - - if (response.EngineSpeed > DataBus.EngineRatedSpeed || response.EngineSpeed < DataBus.EngineIdleSpeed) { - continue; - } - - if (!IsBelowDownShiftCurve(gear, response.EnginePowerRequest / response.EngineSpeed, response.EngineSpeed)) { - _gearbox.TorqueConverterLocked = torqueConverterLocked; - _gearbox.Disengaged = false; - return gear; - } - } - // fallback: start with first gear; - _gearbox.TorqueConverterLocked = false; - _gearbox.Disengaged = false; - return 1; - } - - public override uint Engage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity) - { - if (_nextGear.AbsTime != null && _nextGear.AbsTime.IsEqual(absTime)) { - _gearbox.TorqueConverterLocked = _nextGear.TorqueConverterLocked; - _gearbox.Disengaged = _nextGear.Disengaged; - _nextGear.AbsTime = null; - return _nextGear.Gear; - } - _nextGear.AbsTime = null; - return _gearbox.Gear; - } - - public override void Disengage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outEngineSpeed) - { - throw new System.NotImplementedException("AT Shift Strategy does not support disengaging."); - } - - public override bool ShiftRequired(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - NewtonMeter inTorque, PerSecond inAngularVelocity, uint gear, Second lastShiftTime) - { - // ENGAGE --------------------------------------------------------- - // 0 -> 1C: drive off after disengaged - engage first gear - if (_gearbox.Disengaged && outAngularVelocity.IsGreater(0.SI<PerSecond>())) { - Log.Debug("shift required: drive off after vehicle stopped"); - _nextGear.SetState(absTime, disengaged: false, gear: 1, tcLocked: false); - return true; - } - - // DISENGAGE ------------------------------------------------------ - // 1) _ -> 0: disengage before halting - var braking = DataBus.DriverBehavior == DrivingBehavior.Braking; - var torqueNegative = outTorque.IsSmaller(0); - var slowerThanDisengageSpeed = - DataBus.VehicleSpeed.IsSmaller(Constants.SimulationSettings.ATGearboxDisengageWhenHaltingSpeed); - var disengageBeforeHalting = braking && torqueNegative && slowerThanDisengageSpeed; - - // 2) L -> 0: disengage if inAngularVelocity == 0 - var disengageAngularVelocityZero = _gearbox.TorqueConverterLocked && inAngularVelocity.IsEqual(0.SI<PerSecond>()); - - // 3) 1C -> 0: disengange when negative T_out and positive T_in - var gear1C = gear == 1 && !_gearbox.TorqueConverterLocked; - var disengageTOutNegativeAndTInPositive = DataBus.DriverAcceleration <= 0 && gear1C && outTorque.IsSmaller(0) && - inTorque.IsGreater(0); - - var disengageTCEngineSpeedLowerIdle = braking && torqueNegative && gear1C && - inAngularVelocity.IsSmallerOrEqual(DataBus.EngineIdleSpeed); - - if (disengageBeforeHalting || disengageTCEngineSpeedLowerIdle || disengageAngularVelocityZero || - disengageTOutNegativeAndTInPositive) { - _nextGear.SetState(absTime, disengaged: true, gear: 1, tcLocked: false); - return true; - } - - // EMERGENCY SHIFTS --------------------------------------- - // Emergency Downshift: if lower than engine idle speed - if (inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed)) { - Log.Debug("engine speed would fall below idle speed - shift down"); - Downshift(absTime, gear); - return true; - } - // Emergency Upshift: if higher than engine rated speed - if (inAngularVelocity.IsGreaterOrEqual(ModelData.Gears[gear].MaxSpeed ?? DataBus.EngineRatedSpeed)) { - // check if upshift is possible - if (!ModelData.Gears.ContainsKey(gear + 1)) { - return false; - } - Log.Debug("engine speed would be above max speed / rated speed - shift up"); - Upshift(absTime, gear); - return true; - } - - // UPSHIFT -------------------------------------------------------- - if (CheckUpshift(absTime, dt, outTorque, outAngularVelocity, inTorque, inAngularVelocity, gear, - lastShiftTime)) { - return true; - } - - // DOWNSHIFT ------------------------------------------------------ - if (CheckDownshift(absTime, dt, outTorque, outAngularVelocity, inTorque, inAngularVelocity, gear, - lastShiftTime)) { - return true; - } - - return false; - } - - [SuppressMessage("ReSharper", "UnusedParameter.Local")] - private bool CheckUpshift(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - NewtonMeter inTorque, PerSecond inAngularVelocity, uint gear, Second lastShiftTime) - { - var shiftTimeReached = (absTime - lastShiftTime).IsGreaterOrEqual(ModelData.ShiftTime); - if (!shiftTimeReached) { - return false; - } - - var currentGear = ModelData.Gears[gear]; - - if (_gearbox.TorqueConverterLocked || currentGear.HasLockedGear) { - // UPSHIFT - General Rule - // L -> L+1 - // C -> L - var nextGear = _gearbox.TorqueConverterLocked ? gear + 1 : gear; - if (!ModelData.Gears.ContainsKey(nextGear)) { - return false; - } - - var nextEngineSpeed = outAngularVelocity * ModelData.Gears[nextGear].Ratio; - if (nextEngineSpeed.IsEqual(0)) { - return false; - } - - var currentEnginePower = inTorque * inAngularVelocity; - var nextEngineTorque = currentEnginePower / nextEngineSpeed; - var isAboveUpShift = IsAboveUpShiftCurve(gear, nextEngineTorque, nextEngineSpeed, _gearbox.TorqueConverterLocked); - - var minAccelerationReachable = true; - if (!DataBus.VehicleSpeed.IsEqual(0)) { - var reachableAcceleration = EstimateAccelerationForGear(nextGear, outAngularVelocity); - var minAcceleration = _gearbox.TorqueConverterLocked - ? ModelData.UpshiftMinAcceleration - : ModelData.TorqueConverterData.CLUpshiftMinAcceleration; - minAcceleration = VectoMath.Min(minAcceleration, DataBus.DriverAcceleration); - minAccelerationReachable = reachableAcceleration.IsGreaterOrEqual(minAcceleration); - } - - if (isAboveUpShift && minAccelerationReachable) { - Upshift(absTime, gear); - return true; - } - } - - // UPSHIFT - Special rule for 1C -> 2C - if (!_gearbox.TorqueConverterLocked && ModelData.Gears.ContainsKey(gear + 1) && - ModelData.Gears[gear + 1].HasTorqueConverter && outAngularVelocity.IsGreater(0)) { - // C -> C+1 - var nextGear = ModelData.Gears[gear + 1]; - var gearRatio = nextGear.TorqueConverterRatio / currentGear.TorqueConverterRatio; - var minEngineSpeed = VectoMath.Min(700.RPMtoRad(), gearRatio * (DataBus.EngineN80hSpeed - 150.RPMtoRad())); - - var nextGearboxInSpeed = outAngularVelocity * nextGear.TorqueConverterRatio; - var nextGearboxInTorque = outTorque / nextGear.TorqueConverterRatio; - var tcOperatingPoint = _gearbox.TorqueConverter.FindOperatingPoint(nextGearboxInTorque, nextGearboxInSpeed); - - var engineSpeedOverMin = tcOperatingPoint.InAngularVelocity.IsGreater(minEngineSpeed); - - var reachableAcceleration = EstimateAccelerationForGear(gear + 1, outAngularVelocity); - var minAcceleration = VectoMath.Min(ModelData.TorqueConverterData.CCUpshiftMinAcceleration, - DataBus.DriverAcceleration); - var minAccelerationReachable = reachableAcceleration.IsGreaterOrEqual(minAcceleration); - - if (engineSpeedOverMin && minAccelerationReachable) { - Upshift(absTime, gear); - return true; - } - } - return false; - } - - /// <summary> - /// Tests if the operating point is above (right of) the up-shift curve. - /// </summary> - /// <param name="gear">The gear.</param> - /// <param name="inTorque">The in torque.</param> - /// <param name="inEngineSpeed">The in engine speed.</param> - /// <param name="torqueConverterLocked">if true, the regular shift polygon is used, otherwise the shift polygon for the torque converter is used</param> - /// <returns><c>true</c> if the operating point is above the up-shift curve; otherwise, <c>false</c>.</returns> - private bool IsAboveUpShiftCurve(uint gear, NewtonMeter inTorque, PerSecond inEngineSpeed, - bool torqueConverterLocked) - { - var shiftPolygon = torqueConverterLocked - ? ModelData.Gears[gear].ShiftPolygon - : ModelData.Gears[gear].TorqueConverterShiftPolygon; - - return gear < ModelData.Gears.Keys.Max() && shiftPolygon.IsAboveUpshiftCurve(inTorque, inEngineSpeed); - } - - private void Upshift(Second absTime, uint gear) - { - // C -> L: switch from torque converter to locked gear - if (!_gearbox.TorqueConverterLocked && ModelData.Gears[gear].HasLockedGear) { - _nextGear.SetState(absTime, disengaged: false, gear: gear, tcLocked: true); - return; - } - - // L -> L+1 - // C -> C+1 - if (ModelData.Gears.ContainsKey(gear + 1)) { - _nextGear.SetState(absTime, disengaged: false, gear: gear + 1, tcLocked: _gearbox.TorqueConverterLocked); - return; - } - - // C -> L+1 -- not allowed!! - throw new VectoSimulationException( - "ShiftStrategy wanted to shift up, but current gear has active torque converter (C) but no locked gear (no L) and shifting directly to (L) is not allowed."); - } - - [SuppressMessage("ReSharper", "UnusedParameter.Local")] - private bool CheckDownshift(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - NewtonMeter inTorque, PerSecond inAngularVelocity, uint gear, Second lastShiftTime) - { - var shiftTimeReached = (absTime - lastShiftTime).IsGreaterOrEqual(ModelData.ShiftTime); - - if (shiftTimeReached && IsBelowDownShiftCurve(gear, inTorque, inAngularVelocity)) { - Downshift(absTime, gear); - return true; - } - - return false; - } - - /// <summary> - /// Tests if the operating point is below (left of) the down-shift curve. - /// </summary> - /// <param name="gear">The gear.</param> - /// <param name="inTorque">The in torque.</param> - /// <param name="inEngineSpeed">The in engine speed.</param> - /// <returns><c>true</c> if the operating point is below the down-shift curv; otherwise, <c>false</c>.</returns> - private bool IsBelowDownShiftCurve(uint gear, NewtonMeter inTorque, PerSecond inEngineSpeed) - { - return gear > 1 && ModelData.Gears[gear].ShiftPolygon.IsBelowDownshiftCurve(inTorque, inEngineSpeed); - } - - private void Downshift(Second absTime, uint gear) - { - // L -> C - if (_gearbox.TorqueConverterLocked && ModelData.Gears[gear].HasTorqueConverter) { - _nextGear.SetState(absTime, disengaged: false, gear: gear, tcLocked: false); - return; - } - - // L -> L-1 - // C -> C-1 - if (ModelData.Gears.ContainsKey(gear - 1)) { - _nextGear.SetState(absTime, disengaged: false, gear: gear - 1, tcLocked: _gearbox.TorqueConverterLocked); - return; - } - - // L -> 0 -- not allowed!! - throw new VectoSimulationException( - "ShiftStrategy wanted to shift down but current gear is locked (L) and has no torque converter (C) and disenganging directly from (L) is not allowed."); - } - - private class NextGearState - { - public Second AbsTime; - public bool Disengaged; - public uint Gear; - public bool TorqueConverterLocked; - - public void SetState(Second absTime, bool disengaged, uint gear, bool tcLocked) - { - AbsTime = absTime; - Disengaged = disengaged; - Gear = gear; - TorqueConverterLocked = tcLocked; - } - } - } +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Simulation.DataBus; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + public class ATShiftStrategy : BaseShiftStrategy + { + private ATGearbox _gearbox; + private readonly NextGearState _nextGear = new NextGearState(); + + public override IGearbox Gearbox + { + get { return _gearbox; } + set + { + _gearbox = value as ATGearbox; + if (_gearbox == null) { + throw new VectoException("AT Shift strategy can only handle AT gearboxes, given: {0}", value.GetType()); + } + } + } + + public override GearInfo NextGear + { + get { return new GearInfo(_nextGear.Gear, _nextGear.TorqueConverterLocked); } + } + + public ATShiftStrategy(GearboxData data, IDataBus dataBus) : base(data, dataBus) {} + + public override uint InitGear(Second absTime, Second dt, NewtonMeter torque, PerSecond outAngularVelocity) + { + if (DataBus.VehicleSpeed.IsEqual(0)) { + // AT always starts in first gear and TC active! + _gearbox.TorqueConverterLocked = false; + _gearbox.Disengaged = true; + return 1; + } + var torqueConverterLocked = true; + for (var gear = ModelData.Gears.Keys.Max(); gear > 1; gear--) { + if (_gearbox.ModelData.Gears[gear].HasTorqueConverter) { + torqueConverterLocked = false; + } + var response = _gearbox.Initialize(gear, torqueConverterLocked, torque, outAngularVelocity); + + if (response.EngineSpeed > DataBus.EngineRatedSpeed || response.EngineSpeed < DataBus.EngineIdleSpeed) { + continue; + } + + if (!IsBelowDownShiftCurve(gear, response.EnginePowerRequest / response.EngineSpeed, response.EngineSpeed)) { + _gearbox.TorqueConverterLocked = torqueConverterLocked; + _gearbox.Disengaged = false; + return gear; + } + } + // fallback: start with first gear; + _gearbox.TorqueConverterLocked = false; + _gearbox.Disengaged = false; + return 1; + } + + public override uint Engage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity) + { + if (_nextGear.AbsTime != null && _nextGear.AbsTime.IsEqual(absTime)) { + _gearbox.TorqueConverterLocked = _nextGear.TorqueConverterLocked; + _gearbox.Disengaged = _nextGear.Disengaged; + _nextGear.AbsTime = null; + return _nextGear.Gear; + } + _nextGear.AbsTime = null; + return _gearbox.Gear; + } + + public override void Disengage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outEngineSpeed) + { + throw new System.NotImplementedException("AT Shift Strategy does not support disengaging."); + } + + public override bool ShiftRequired(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + NewtonMeter inTorque, PerSecond inAngularVelocity, uint gear, Second lastShiftTime) + { + // ENGAGE --------------------------------------------------------- + // 0 -> 1C: drive off after disengaged - engage first gear + if (_gearbox.Disengaged && outAngularVelocity.IsGreater(0.SI<PerSecond>())) { + Log.Debug("shift required: drive off after vehicle stopped"); + _nextGear.SetState(absTime, disengaged: false, gear: 1, tcLocked: false); + return true; + } + + // DISENGAGE ------------------------------------------------------ + // 1) _ -> 0: disengage before halting + var braking = DataBus.DriverBehavior == DrivingBehavior.Braking; + var torqueNegative = outTorque.IsSmaller(0); + var slowerThanDisengageSpeed = + DataBus.VehicleSpeed.IsSmaller(Constants.SimulationSettings.ATGearboxDisengageWhenHaltingSpeed); + var disengageBeforeHalting = braking && torqueNegative && slowerThanDisengageSpeed; + + // 2) L -> 0: disengage if inAngularVelocity == 0 + var disengageAngularVelocityZero = _gearbox.TorqueConverterLocked && inAngularVelocity.IsEqual(0.SI<PerSecond>()); + + // 3) 1C -> 0: disengange when negative T_out and positive T_in + var gear1C = gear == 1 && !_gearbox.TorqueConverterLocked; + var disengageTOutNegativeAndTInPositive = DataBus.DriverAcceleration <= 0 && gear1C && outTorque.IsSmaller(0) && + inTorque.IsGreater(0); + + var disengageTCEngineSpeedLowerIdle = braking && torqueNegative && gear1C && + inAngularVelocity.IsSmallerOrEqual(DataBus.EngineIdleSpeed); + + if (disengageBeforeHalting || disengageTCEngineSpeedLowerIdle || disengageAngularVelocityZero || + disengageTOutNegativeAndTInPositive) { + _nextGear.SetState(absTime, disengaged: true, gear: 1, tcLocked: false); + return true; + } + + // EMERGENCY SHIFTS --------------------------------------- + // Emergency Downshift: if lower than engine idle speed + if (inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed)) { + Log.Debug("engine speed would fall below idle speed - shift down"); + Downshift(absTime, gear); + return true; + } + // Emergency Upshift: if higher than engine rated speed + if (inAngularVelocity.IsGreaterOrEqual(ModelData.Gears[gear].MaxSpeed ?? DataBus.EngineRatedSpeed)) { + // check if upshift is possible + if (!ModelData.Gears.ContainsKey(gear + 1)) { + return false; + } + Log.Debug("engine speed would be above max speed / rated speed - shift up"); + Upshift(absTime, gear); + return true; + } + + // UPSHIFT -------------------------------------------------------- + if (CheckUpshift(absTime, dt, outTorque, outAngularVelocity, inTorque, inAngularVelocity, gear, + lastShiftTime)) { + return true; + } + + // DOWNSHIFT ------------------------------------------------------ + if (CheckDownshift(absTime, dt, outTorque, outAngularVelocity, inTorque, inAngularVelocity, gear, + lastShiftTime)) { + return true; + } + + return false; + } + + [SuppressMessage("ReSharper", "UnusedParameter.Local")] + private bool CheckUpshift(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + NewtonMeter inTorque, PerSecond inAngularVelocity, uint gear, Second lastShiftTime) + { + var shiftTimeReached = (absTime - lastShiftTime).IsGreaterOrEqual(ModelData.ShiftTime); + if (!shiftTimeReached) { + return false; + } + + var currentGear = ModelData.Gears[gear]; + + if (_gearbox.TorqueConverterLocked || currentGear.HasLockedGear) { + if (CheckUpshiftToLocked(absTime, outAngularVelocity, inTorque, inAngularVelocity, gear)) { + return true; + } + } + + // UPSHIFT - Special rule for 1C -> 2C + if (!_gearbox.TorqueConverterLocked && ModelData.Gears.ContainsKey(gear + 1) && + ModelData.Gears[gear + 1].HasTorqueConverter && outAngularVelocity.IsGreater(0)) { + if (CheckUpshiftTcTc(absTime, outTorque, outAngularVelocity, gear, currentGear)) { + return true; + } + } + return false; + } + + private bool CheckUpshiftTcTc(Second absTime, NewtonMeter outTorque, PerSecond outAngularVelocity, uint gear, + GearData currentGear) + { +// C -> C+1 + var nextGear = ModelData.Gears[gear + 1]; + var gearRatio = nextGear.TorqueConverterRatio / currentGear.TorqueConverterRatio; + var minEngineSpeed = VectoMath.Min(700.RPMtoRad(), gearRatio * (DataBus.EngineN80hSpeed - 150.RPMtoRad())); + + var nextGearboxInSpeed = outAngularVelocity * nextGear.TorqueConverterRatio; + var nextGearboxInTorque = outTorque / nextGear.TorqueConverterRatio; + var tcOperatingPoint = _gearbox.TorqueConverter.FindOperatingPoint(nextGearboxInTorque, nextGearboxInSpeed); + + var engineSpeedOverMin = tcOperatingPoint.InAngularVelocity.IsGreater(minEngineSpeed); + + var reachableAcceleration = EstimateAccelerationForGear(gear + 1, outAngularVelocity); + var minAcceleration = VectoMath.Min(ModelData.TorqueConverterData.CCUpshiftMinAcceleration, + DataBus.DriverAcceleration); + var minAccelerationReachable = reachableAcceleration.IsGreaterOrEqual(minAcceleration); + + if (engineSpeedOverMin && minAccelerationReachable) { + Upshift(absTime, gear); + return true; + } + return false; + } + + private bool CheckUpshiftToLocked(Second absTime, PerSecond outAngularVelocity, NewtonMeter inTorque, + PerSecond inAngularVelocity, uint gear) + { +// UPSHIFT - General Rule + // L -> L+1 + // C -> L + var nextGear = _gearbox.TorqueConverterLocked ? gear + 1 : gear; + if (!ModelData.Gears.ContainsKey(nextGear)) { + return true; + } + + var nextEngineSpeed = outAngularVelocity * ModelData.Gears[nextGear].Ratio; + if (nextEngineSpeed.IsEqual(0)) { + return true; + } + + var currentEnginePower = inTorque * inAngularVelocity; + var nextEngineTorque = currentEnginePower / nextEngineSpeed; + var isAboveUpShift = IsAboveUpShiftCurve(gear, nextEngineTorque, nextEngineSpeed, _gearbox.TorqueConverterLocked); + + var minAccelerationReachable = true; + if (!DataBus.VehicleSpeed.IsEqual(0)) { + var reachableAcceleration = EstimateAccelerationForGear(nextGear, outAngularVelocity); + var minAcceleration = _gearbox.TorqueConverterLocked + ? ModelData.UpshiftMinAcceleration + : ModelData.TorqueConverterData.CLUpshiftMinAcceleration; + minAcceleration = VectoMath.Min(minAcceleration, DataBus.DriverAcceleration); + minAccelerationReachable = reachableAcceleration.IsGreaterOrEqual(minAcceleration); + } + + if (isAboveUpShift && minAccelerationReachable) { + Upshift(absTime, gear); + return true; + } + return false; + } + + /// <summary> + /// Tests if the operating point is above (right of) the up-shift curve. + /// </summary> + /// <param name="gear">The gear.</param> + /// <param name="inTorque">The in torque.</param> + /// <param name="inEngineSpeed">The in engine speed.</param> + /// <param name="torqueConverterLocked">if true, the regular shift polygon is used, otherwise the shift polygon for the torque converter is used</param> + /// <returns><c>true</c> if the operating point is above the up-shift curve; otherwise, <c>false</c>.</returns> + private bool IsAboveUpShiftCurve(uint gear, NewtonMeter inTorque, PerSecond inEngineSpeed, + bool torqueConverterLocked) + { + var shiftPolygon = torqueConverterLocked + ? ModelData.Gears[gear].ShiftPolygon + : ModelData.Gears[gear].TorqueConverterShiftPolygon; + + return gear < ModelData.Gears.Keys.Max() && shiftPolygon.IsAboveUpshiftCurve(inTorque, inEngineSpeed); + } + + private void Upshift(Second absTime, uint gear) + { + // C -> L: switch from torque converter to locked gear + if (!_gearbox.TorqueConverterLocked && ModelData.Gears[gear].HasLockedGear) { + _nextGear.SetState(absTime, disengaged: false, gear: gear, tcLocked: true); + return; + } + + // L -> L+1 + // C -> C+1 + if (ModelData.Gears.ContainsKey(gear + 1)) { + _nextGear.SetState(absTime, disengaged: false, gear: gear + 1, tcLocked: _gearbox.TorqueConverterLocked); + return; + } + + // C -> L+1 -- not allowed!! + throw new VectoSimulationException( + "ShiftStrategy wanted to shift up, but current gear has active torque converter (C) but no locked gear (no L) and shifting directly to (L) is not allowed."); + } + + [SuppressMessage("ReSharper", "UnusedParameter.Local")] + private bool CheckDownshift(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + NewtonMeter inTorque, PerSecond inAngularVelocity, uint gear, Second lastShiftTime) + { + var shiftTimeReached = (absTime - lastShiftTime).IsGreaterOrEqual(ModelData.ShiftTime); + + if (shiftTimeReached && IsBelowDownShiftCurve(gear, inTorque, inAngularVelocity)) { + Downshift(absTime, gear); + return true; + } + + return false; + } + + /// <summary> + /// Tests if the operating point is below (left of) the down-shift curve. + /// </summary> + /// <param name="gear">The gear.</param> + /// <param name="inTorque">The in torque.</param> + /// <param name="inEngineSpeed">The in engine speed.</param> + /// <returns><c>true</c> if the operating point is below the down-shift curv; otherwise, <c>false</c>.</returns> + private bool IsBelowDownShiftCurve(uint gear, NewtonMeter inTorque, PerSecond inEngineSpeed) + { + return gear > 1 && ModelData.Gears[gear].ShiftPolygon.IsBelowDownshiftCurve(inTorque, inEngineSpeed); + } + + private void Downshift(Second absTime, uint gear) + { + // L -> C + if (_gearbox.TorqueConverterLocked && ModelData.Gears[gear].HasTorqueConverter) { + _nextGear.SetState(absTime, disengaged: false, gear: gear, tcLocked: false); + return; + } + + // L -> L-1 + // C -> C-1 + if (ModelData.Gears.ContainsKey(gear - 1)) { + _nextGear.SetState(absTime, disengaged: false, gear: gear - 1, tcLocked: _gearbox.TorqueConverterLocked); + return; + } + + // L -> 0 -- not allowed!! + throw new VectoSimulationException( + "ShiftStrategy wanted to shift down but current gear is locked (L) and has no torque converter (C) and disenganging directly from (L) is not allowed."); + } + + private class NextGearState + { + public Second AbsTime; + public bool Disengaged; + public uint Gear; + public bool TorqueConverterLocked; + + public void SetState(Second absTime, bool disengaged, uint gear, bool tcLocked) + { + AbsTime = absTime; + Disengaged = disengaged; + Gear = gear; + TorqueConverterLocked = tcLocked; + } + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/CycleGearbox.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/CycleGearbox.cs index 8609584f95..1a001d4de8 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/CycleGearbox.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/CycleGearbox.cs @@ -29,506 +29,504 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Connector.Ports; -using TUGraz.VectoCore.Models.Connector.Ports.Impl; -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.OutputData; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Impl -{ - public class CycleGearbox : AbstractGearbox<CycleGearbox.CycleGearboxState> - { - /// <summary> - /// True if gearbox is disengaged (no gear is set). - /// </summary> - protected internal Second Disengaged; - - protected bool? TorqueConverterActive; - - protected internal readonly TorqueConverter TorqueConverter; - - public CycleGearbox(IVehicleContainer container, VectoRunData runData) - : base(container, runData) - { - if (!ModelData.Type.AutomaticTransmission()) { - return; - } - var strategy = new CycleShiftStrategy(ModelData, null); - TorqueConverter = new TorqueConverter(this, strategy, container, ModelData.TorqueConverterData, runData); - if (TorqueConverter == null) { - throw new VectoException("Torque Converter required for AT transmission!"); - } - } - - public override void Connect(ITnOutPort other) - { - base.Connect(other); - if (TorqueConverter != null) { - TorqueConverter.NextComponent = other; - } - } - - public override IResponse Initialize(NewtonMeter outTorque, PerSecond outAngularVelocity) - { - var dt = Constants.SimulationSettings.TargetTimeInterval; - - Gear = DataBus.CycleData.LeftSample.Gear; - TorqueConverterActive = DataBus.CycleData.LeftSample.TorqueConverterActive; - - if (TorqueConverter != null && TorqueConverterActive == null) { - throw new VectoSimulationException("Driving cycle does not contain information about TorqueConverter!"); - } - - var inAngularVelocity = DataBus.EngineIdleSpeed; - var inTorque = 0.SI<NewtonMeter>(); - IResponse response; - - if (Gear != 0) { - inAngularVelocity = outAngularVelocity * ModelData.Gears[Gear].Ratio; - var inTorqueLossResult = ModelData.Gears[Gear].LossMap.GetTorqueLoss(outAngularVelocity, outTorque); - CurrentState.TorqueLossResult = inTorqueLossResult; - inTorque = outTorque / ModelData.Gears[Gear].Ratio + inTorqueLossResult.Value; - - var torqueLossInertia = outAngularVelocity.IsEqual(0) - ? 0.SI<NewtonMeter>() - : Formulas.InertiaPower(inAngularVelocity, PreviousState.InAngularVelocity, ModelData.Inertia, dt) / - inAngularVelocity; - - inTorque += torqueLossInertia; - - response = TorqueConverterActive != null && TorqueConverterActive.Value && TorqueConverter != null - ? TorqueConverter.Initialize(inTorque, inAngularVelocity) - : NextComponent.Initialize(inTorque, inAngularVelocity); - } else { - response = NextComponent.Initialize(inTorque, inAngularVelocity); - } - CurrentState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); - PreviousState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); - PreviousState.InertiaTorqueLossOut = 0.SI<NewtonMeter>(); - PreviousState.Gear = Gear; - - response.GearboxPowerRequest = inTorque * inAngularVelocity; - return response; - } - - /// <summary> - /// Requests the Gearbox to deliver torque and angularVelocity - /// </summary> - /// <returns> - /// <list type="bullet"> - /// <item><description>ResponseDryRun</description></item> - /// <item><description>ResponseOverload</description></item> - /// <item><description>ResponseGearshift</description></item> - /// </list> - /// </returns> - public override IResponse Request(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - bool dryRun = false) - { - Log.Debug("Gearbox Power Request: torque: {0}, angularVelocity: {1}", outTorque, outAngularVelocity); - var gear = GetGearFromCycle(); - - TorqueConverterActive = DataBus.DriverBehavior == DrivingBehavior.Braking - ? DataBus.CycleData.LeftSample.TorqueConverterActive - : DataBus.CycleData.RightSample.TorqueConverterActive; - - if (TorqueConverter != null && TorqueConverterActive == null) { - throw new VectoSimulationException("Driving cycle does not contain information about TorqueConverter!"); - } - if (gear != 0 && !ModelData.Gears.ContainsKey(gear)) { - throw new VectoSimulationException("Requested Gear {0} from driving cycle is not available", gear); - } - - // mk 2016-11-30: added additional check for outAngularVelocity due to failing test: MeasuredSpeed_Gear_AT_PS_Run - // mq 2016-12-16: changed check to vehicle halted due to failing test: MeasuredSpeed_Gear_AT_* - var retVal = gear == 0 || DataBus.DriverBehavior == DrivingBehavior.Halted - //|| (outAngularVelocity.IsSmallerOrEqual(0, 1) && outTorque.IsSmallerOrEqual(0, 1)) - ? RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun) - : RequestEngaged(absTime, dt, outTorque, outAngularVelocity, dryRun); - - retVal.GearboxPowerRequest = outTorque * (PreviousState.OutAngularVelocity + outAngularVelocity) / 2; - return retVal; - } - - private uint GetGearFromCycle() - { - return DataBus.DriverBehavior == DrivingBehavior.Braking - ? DataBus.CycleData.LeftSample.Gear - : DataBus.CycleData.RightSample.Gear; - } - - /// <summary> - /// Handles requests when a gear is engaged - /// </summary> - /// <param name="absTime"></param> - /// <param name="dt"></param> - /// <param name="outTorque"></param> - /// <param name="outAngularVelocity"></param> - /// <param name="dryRun"></param> - /// <returns></returns> - private IResponse RequestEngaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - bool dryRun) - { - Disengaged = null; - - Gear = GetGearFromCycle(); - - var torqueConverterLocked = TorqueConverterActive == null || !TorqueConverterActive.Value; - - var effectiveRatio = ModelData.Gears[Gear].Ratio; - var effectiveLossMap = ModelData.Gears[Gear].LossMap; - if (!torqueConverterLocked) { - effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; - effectiveLossMap = ModelData.Gears[Gear].TorqueConverterGearLossMap; - } - - if (effectiveLossMap == null || double.IsNaN(effectiveRatio)) { - throw new VectoSimulationException("Ratio or loss-map for gear {0}{1} invalid. Please check input data", Gear, - torqueConverterLocked ? "L" : "C"); - } - - var avgOutAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; - var inTorqueLossResult = effectiveLossMap.GetTorqueLoss(avgOutAngularVelocity, outTorque); - CurrentState.TorqueLossResult = inTorqueLossResult; - var inTorque = outTorque / effectiveRatio + inTorqueLossResult.Value; - CurrentState.TorqueLossResult = inTorqueLossResult; - - if (!torqueConverterLocked && !ModelData.Gears[Gear].HasTorqueConverter) { - throw new VectoSimulationException("Torque converter requested by cycle for gear without torque converter!"); - } - - var inAngularVelocity = outAngularVelocity * effectiveRatio; - - // TODO: MQ 20170111 - disabled this check, caused more problems than it actually solved... -- re-think - //if (!dryRun && ModelData.Type.AutomaticTransmission() && torqueConverterLocked && - // inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed) && !dryRun) { - // Log.Error( - // "ERROR: EngineSpeed is lower than Idlespeed in Measuredspeed-Cycle with given Gear (Automatic Transmission). AbsTime: {0}, Gear: {1} TC-Active: {2}, EngineSpeed: {3}", - // absTime, Gear, !torqueConverterLocked, inAngularVelocity.AsRPM); - // return new ResponseEngineSpeedTooLow { Source = this, EngineSpeed = inAngularVelocity }; - //} - - if (!inAngularVelocity.IsEqual(0)) { - // MQ 19.2.2016: check! inertia is related to output side, torque loss accounts to input side - CurrentState.InertiaTorqueLossOut = - Formulas.InertiaPower(outAngularVelocity, PreviousState.OutAngularVelocity, ModelData.Inertia, dt) / - avgOutAngularVelocity; - inTorque += CurrentState.InertiaTorqueLossOut / effectiveRatio; - } else { - CurrentState.InertiaTorqueLossOut = 0.SI<NewtonMeter>(); - } - if (Gear != PreviousState.Gear && - ConsiderShiftLosses(new GearInfo(Gear, torqueConverterLocked), outTorque)) { - CurrentState.PowershiftLosses = ComputeShiftLosses(outTorque, outAngularVelocity); - } - if (CurrentState.PowershiftLosses != null) { - var averageEngineSpeed = (DataBus.EngineSpeed + outAngularVelocity * ModelData.Gears[Gear].Ratio) / 2; - inTorque += CurrentState.PowershiftLosses / dt / averageEngineSpeed; - } - if (dryRun) { - if (TorqueConverter != null && !torqueConverterLocked) { - return TorqueConverter.Request(absTime, dt, inTorque, inAngularVelocity, true); - } - // mk 2016-12-13 - //if (outTorque.IsSmaller(0) && inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed)) { - // //Log.Warn("engine speed would fall below idle speed - disengage! gear from cycle: {0}, vehicle speed: {1}", Gear, - // // DataBus.VehicleSpeed); - // Gear = 0; - // return RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun); - //} - var dryRunResponse = NextComponent.Request(absTime, dt, inTorque, inAngularVelocity, true); - dryRunResponse.GearboxPowerRequest = outTorque * avgOutAngularVelocity; - return dryRunResponse; - } - - CurrentState.TransmissionTorqueLoss = inTorque * effectiveRatio - outTorque; - - - CurrentState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); - CurrentState.Gear = Gear; - // end critical section - - if (TorqueConverter != null && !torqueConverterLocked) { - CurrentState.TorqueConverterActive = true; - return TorqueConverter.Request(absTime, dt, inTorque, inAngularVelocity); - } - // mk 2016-12-13 - //if (outTorque.IsSmaller(0) && inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed)) { - // Log.Warn("engine speed would fall below idle speed - disengage! gear from cycle: {0}, vehicle speed: {1}", Gear, - // DataBus.VehicleSpeed); - // Gear = 0; - // return RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun); - //} - if (TorqueConverter != null) { - TorqueConverter.Locked(CurrentState.InTorque, CurrentState.InAngularVelocity, CurrentState.InTorque, - CurrentState.InAngularVelocity); - } - var response = NextComponent.Request(absTime, dt, inTorque, inAngularVelocity); - response.GearboxPowerRequest = outTorque * avgOutAngularVelocity; - return response; - } - - /// <summary> - /// Handles Requests when no gear is disengaged - /// </summary> - /// <param name="absTime"></param> - /// <param name="dt"></param> - /// <param name="outTorque"></param> - /// <param name="outAngularVelocity"></param> - /// <param name="dryRun"></param> - /// <returns></returns> - private IResponse RequestDisengaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - bool dryRun) - { - if (Disengaged == null) { - Disengaged = absTime; - } - - var avgOutAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; - if (dryRun) { - // if gearbox is disengaged the 0-line is the limit for drag and full load - return new ResponseDryRun { - Source = this, - GearboxPowerRequest = outTorque * avgOutAngularVelocity, - DeltaDragLoad = outTorque * avgOutAngularVelocity, - DeltaFullLoad = outTorque * avgOutAngularVelocity, - }; - } - - if ((outTorque * avgOutAngularVelocity).IsGreater(0.SI<Watt>(), Constants.SimulationSettings.LineSearchTolerance) && - !outAngularVelocity.IsEqual(0)) { - return new ResponseOverload { - Source = this, - Delta = outTorque * avgOutAngularVelocity, - GearboxPowerRequest = outTorque * avgOutAngularVelocity - }; - } - - if ((outTorque * avgOutAngularVelocity).IsSmaller(0.SI<Watt>(), Constants.SimulationSettings.LineSearchTolerance)) { - return new ResponseUnderload { - Source = this, - Delta = outTorque * avgOutAngularVelocity, - GearboxPowerRequest = outTorque * avgOutAngularVelocity - }; - } - - - //var motoringSpeed = DataBus.EngineIdleSpeed; - //var disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), DataBus.EngineIdleSpeed); - //if (!(disengagedResponse is ResponseSuccess)) { - // motoringSpeed = DataBus.EngineSpeed; - // if (motoringSpeed.IsGreater(DataBus.EngineIdleSpeed)) { - // var first = (ResponseDryRun)NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed, true); - // try { - // motoringSpeed = SearchAlgorithm.Search(motoringSpeed, first.DeltaDragLoad, - // Constants.SimulationSettings.EngineIdlingSearchInterval, - // getYValue: result => ((ResponseDryRun)result).DeltaDragLoad, - // evaluateFunction: n => NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), n, true), - // criterion: result => ((ResponseDryRun)result).DeltaDragLoad.Value()); - // } catch (VectoException) { - // Log.Warn("CycleGearbox could not find motoring speed for disengaged state."); - // } - // motoringSpeed = motoringSpeed.LimitTo(DataBus.EngineIdleSpeed, DataBus.EngineSpeed); - // } - // disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed); - //} - IResponse disengagedResponse; - if (GearboxType.AutomaticTransmission()) { - disengagedResponse = EngineIdleRequest(absTime, dt); - } else { - disengagedResponse = NextGear.Gear > 0 - ? NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), - outAngularVelocity * ModelData.Gears[NextGear.Gear].Ratio) - : EngineIdleRequest(absTime, dt); - } - if (TorqueConverter != null) { - if (DataBus.VehicleStopped) { - TorqueConverter.Locked(0.SI<NewtonMeter>(), disengagedResponse.EngineSpeed, CurrentState.InTorque, - outAngularVelocity); - } else { - TorqueConverter.Locked(CurrentState.InTorque, disengagedResponse.EngineSpeed, CurrentState.InTorque, - disengagedResponse.EngineSpeed); - } - } - disengagedResponse.GearboxPowerRequest = outTorque * avgOutAngularVelocity; - CurrentState.SetState(0.SI<NewtonMeter>(), disengagedResponse.EngineSpeed, 0.SI<NewtonMeter>(), outAngularVelocity); - CurrentState.Gear = Gear; - - return disengagedResponse; - } - - private IResponse EngineIdleRequest(Second absTime, Second dt) - { - var disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), DataBus.EngineIdleSpeed); - if (disengagedResponse is ResponseSuccess) { - return disengagedResponse; - } - var motoringSpeed = DataBus.EngineSpeed; - if (motoringSpeed.IsGreater(DataBus.EngineIdleSpeed)) { - var first = (ResponseDryRun)NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed, true); - try { - motoringSpeed = SearchAlgorithm.Search(motoringSpeed, first.DeltaDragLoad, - Constants.SimulationSettings.EngineIdlingSearchInterval, - getYValue: result => ((ResponseDryRun)result).DeltaDragLoad, - evaluateFunction: n => NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), n, true), - criterion: result => ((ResponseDryRun)result).DeltaDragLoad.Value()); - } catch (VectoException) { - Log.Warn("CycleGearbox could not find motoring speed for disengaged state."); - } - motoringSpeed = motoringSpeed.LimitTo(DataBus.EngineIdleSpeed, DataBus.EngineSpeed); - } - disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed); - return disengagedResponse; - } - - protected override void DoWriteModalResults(IModalDataContainer container) - { - var avgInAngularSpeed = (PreviousState.InAngularVelocity + CurrentState.InAngularVelocity) / 2.0; - var avgOutAngularSpeed = (PreviousState.OutAngularVelocity + CurrentState.OutAngularVelocity) / 2.0; - container[ModalResultField.Gear] = Disengaged != null ? 0 : Gear; - container[ModalResultField.P_gbx_loss] = CurrentState.TransmissionTorqueLoss * avgOutAngularSpeed; - container[ModalResultField.P_gbx_inertia] = CurrentState.InertiaTorqueLossOut * avgOutAngularSpeed; - container[ModalResultField.P_gbx_in] = CurrentState.InTorque * avgInAngularSpeed; - container[ModalResultField.n_gbx_out_avg] = (PreviousState.OutAngularVelocity + - CurrentState.OutAngularVelocity) / 2.0; - container[ModalResultField.T_gbx_out] = CurrentState.OutTorque; - - if (ModelData.Type.AutomaticTransmission()) { - container[ModalResultField.TC_Locked] = !CurrentState.TorqueConverterActive; - container[ModalResultField.P_gbx_shift_loss] = CurrentState.PowershiftLosses == null - ? 0.SI<Watt>() - : CurrentState.PowershiftLosses * avgInAngularSpeed; - } - // torque converter fields are written by TorqueConverter (if present), called from Vehicle container - } - - protected override void DoCommitSimulationStep() - { - if (Gear != 0) { - if (CurrentState.TorqueLossResult != null && CurrentState.TorqueLossResult.Extrapolated) { - Log.Warn( - "Gear {0} LossMap data was extrapolated: range for loss map is not sufficient: n:{1}, torque:{2}", - Gear, CurrentState.OutAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.OutTorque); - if (DataBus.ExecutionMode == ExecutionMode.Declaration) { - throw new VectoException( - "Gear {0} LossMap data was extrapolated in Declaration Mode: range for loss map is not sufficient: n:{1}, torque:{2}", - Gear, CurrentState.OutAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.OutTorque); - } - } - } - base.DoCommitSimulationStep(); - } - - #region ICluchInfo - - public override GearInfo NextGear - { - get { - if (Disengaged == null) { - return new GearInfo(Gear, !TorqueConverterActive ?? true); - } - var future = DataBus.LookAhead(ModelData.TractionInterruption * 5); - var nextGear = 0u; - var torqueConverterLocked = false; - foreach (var entry in future) { - if (entry.VehicleTargetSpeed != null && entry.VehicleTargetSpeed.IsEqual(0)) { - // vehicle is stopped, no next gear, engine should go to idle - break; - } - if (entry.WheelAngularVelocity != null && entry.WheelAngularVelocity.IsEqual(0)) { - // vehicle is stopped, no next gear, engine should go to idle - break; - } - if (entry.Gear == 0) { - continue; - } - nextGear = entry.Gear; - torqueConverterLocked = !entry.TorqueConverterActive ?? false; - break; - } - return new GearInfo(nextGear, torqueConverterLocked); - } - } - - public override Second TractionInterruption - { - get { - if (Disengaged == null) { - return ModelData.TractionInterruption; - } - var future = DataBus.LookAhead(ModelData.TractionInterruption * 5); - foreach (var entry in future) { - if (entry.VehicleTargetSpeed != null && entry.VehicleTargetSpeed.IsEqual(0)) { - // vehicle is stopped, no next gear, engine should go to idle - break; - } - if (entry.WheelAngularVelocity != null && entry.WheelAngularVelocity.IsEqual(0)) { - // vehicle is stopped, no next gear, engine should go to idle - break; - } - if (entry.Gear == 0) { - continue; - } - return entry.Time - Disengaged; - } - return ModelData.TractionInterruption; - } - } - - public override bool ClutchClosed(Second absTime) - { - return (DataBus.DriverBehavior == DrivingBehavior.Braking - ? DataBus.CycleData.LeftSample.Gear - : DataBus.CycleData.RightSample.Gear) != 0; - } - - #endregion - - public class CycleGearboxState : GearboxState - { - public bool TorqueConverterActive; - public WattSecond PowershiftLosses { get; set; } - } - - public class CycleShiftStrategy : BaseShiftStrategy - { - public CycleShiftStrategy(GearboxData data, IDataBus dataBus) : base(data, dataBus) {} - - public override IGearbox Gearbox { get; set; } - - public override bool ShiftRequired(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, - NewtonMeter inTorque, - PerSecond inAngularVelocity, uint gear, Second lastShiftTime) - { - return false; - } - - public override uint InitGear(Second absTime, Second dt, NewtonMeter torque, PerSecond outAngularVelocity) - { - throw new System.NotImplementedException(); - } - - public override uint Engage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity) - { - throw new System.NotImplementedException(); - } - - public override void Disengage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outEngineSpeed) - { - throw new System.NotImplementedException(); - } - - public override GearInfo NextGear - { - get { throw new System.NotImplementedException(); } - } - } - } +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Connector.Ports; +using TUGraz.VectoCore.Models.Connector.Ports.Impl; +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.Models.SimulationComponent.Data.Gearbox; +using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + public class CycleGearbox : AbstractGearbox<CycleGearbox.CycleGearboxState> + { + /// <summary> + /// True if gearbox is disengaged (no gear is set). + /// </summary> + protected internal Second Disengaged; + + protected bool? TorqueConverterActive; + + protected internal readonly TorqueConverter TorqueConverter; + + public CycleGearbox(IVehicleContainer container, VectoRunData runData) + : base(container, runData) + { + if (!ModelData.Type.AutomaticTransmission()) { + return; + } + var strategy = new CycleShiftStrategy(ModelData, null); + TorqueConverter = new TorqueConverter(this, strategy, container, ModelData.TorqueConverterData, runData); + if (TorqueConverter == null) { + throw new VectoException("Torque Converter required for AT transmission!"); + } + } + + public override void Connect(ITnOutPort other) + { + base.Connect(other); + if (TorqueConverter != null) { + TorqueConverter.NextComponent = other; + } + } + + public override IResponse Initialize(NewtonMeter outTorque, PerSecond outAngularVelocity) + { + var dt = Constants.SimulationSettings.TargetTimeInterval; + + Gear = DataBus.CycleData.LeftSample.Gear; + TorqueConverterActive = DataBus.CycleData.LeftSample.TorqueConverterActive; + + if (TorqueConverter != null && TorqueConverterActive == null) { + throw new VectoSimulationException("Driving cycle does not contain information about TorqueConverter!"); + } + + var inAngularVelocity = DataBus.EngineIdleSpeed; + var inTorque = 0.SI<NewtonMeter>(); + IResponse response; + + if (Gear != 0) { + inAngularVelocity = outAngularVelocity * ModelData.Gears[Gear].Ratio; + var inTorqueLossResult = ModelData.Gears[Gear].LossMap.GetTorqueLoss(outAngularVelocity, outTorque); + CurrentState.TorqueLossResult = inTorqueLossResult; + inTorque = outTorque / ModelData.Gears[Gear].Ratio + inTorqueLossResult.Value; + + var torqueLossInertia = outAngularVelocity.IsEqual(0) + ? 0.SI<NewtonMeter>() + : Formulas.InertiaPower(inAngularVelocity, PreviousState.InAngularVelocity, ModelData.Inertia, dt) / + inAngularVelocity; + + inTorque += torqueLossInertia; + + response = TorqueConverterActive != null && TorqueConverterActive.Value && TorqueConverter != null + ? TorqueConverter.Initialize(inTorque, inAngularVelocity) + : NextComponent.Initialize(inTorque, inAngularVelocity); + } else { + response = NextComponent.Initialize(inTorque, inAngularVelocity); + } + CurrentState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); + PreviousState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); + PreviousState.InertiaTorqueLossOut = 0.SI<NewtonMeter>(); + PreviousState.Gear = Gear; + + response.GearboxPowerRequest = inTorque * inAngularVelocity; + return response; + } + + /// <summary> + /// Requests the Gearbox to deliver torque and angularVelocity + /// </summary> + /// <returns> + /// <list type="bullet"> + /// <item><description>ResponseDryRun</description></item> + /// <item><description>ResponseOverload</description></item> + /// <item><description>ResponseGearshift</description></item> + /// </list> + /// </returns> + public override IResponse Request(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + bool dryRun = false) + { + Log.Debug("Gearbox Power Request: torque: {0}, angularVelocity: {1}", outTorque, outAngularVelocity); + var gear = GetGearFromCycle(); + + TorqueConverterActive = DataBus.DriverBehavior == DrivingBehavior.Braking + ? DataBus.CycleData.LeftSample.TorqueConverterActive + : DataBus.CycleData.RightSample.TorqueConverterActive; + + if (TorqueConverter != null && TorqueConverterActive == null) { + throw new VectoSimulationException("Driving cycle does not contain information about TorqueConverter!"); + } + if (gear != 0 && !ModelData.Gears.ContainsKey(gear)) { + throw new VectoSimulationException("Requested Gear {0} from driving cycle is not available", gear); + } + + // mk 2016-11-30: added additional check for outAngularVelocity due to failing test: MeasuredSpeed_Gear_AT_PS_Run + // mq 2016-12-16: changed check to vehicle halted due to failing test: MeasuredSpeed_Gear_AT_* + var retVal = gear == 0 || DataBus.DriverBehavior == DrivingBehavior.Halted + //|| (outAngularVelocity.IsSmallerOrEqual(0, 1) && outTorque.IsSmallerOrEqual(0, 1)) + ? RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun) + : RequestEngaged(absTime, dt, outTorque, outAngularVelocity, dryRun); + + retVal.GearboxPowerRequest = outTorque * (PreviousState.OutAngularVelocity + outAngularVelocity) / 2; + return retVal; + } + + private uint GetGearFromCycle() + { + return DataBus.DriverBehavior == DrivingBehavior.Braking + ? DataBus.CycleData.LeftSample.Gear + : DataBus.CycleData.RightSample.Gear; + } + + /// <summary> + /// Handles requests when a gear is engaged + /// </summary> + /// <param name="absTime"></param> + /// <param name="dt"></param> + /// <param name="outTorque"></param> + /// <param name="outAngularVelocity"></param> + /// <param name="dryRun"></param> + /// <returns></returns> + private IResponse RequestEngaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + bool dryRun) + { + Disengaged = null; + + Gear = GetGearFromCycle(); + + var torqueConverterLocked = TorqueConverterActive == null || !TorqueConverterActive.Value; + + var effectiveRatio = ModelData.Gears[Gear].Ratio; + var effectiveLossMap = ModelData.Gears[Gear].LossMap; + if (!torqueConverterLocked) { + effectiveRatio = ModelData.Gears[Gear].TorqueConverterRatio; + effectiveLossMap = ModelData.Gears[Gear].TorqueConverterGearLossMap; + } + + CheckModelData(effectiveLossMap, effectiveRatio, torqueConverterLocked); + + var avgOutAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; + var inTorqueLossResult = effectiveLossMap.GetTorqueLoss(avgOutAngularVelocity, outTorque); + CurrentState.TorqueLossResult = inTorqueLossResult; + var inTorque = outTorque / effectiveRatio + inTorqueLossResult.Value; + CurrentState.TorqueLossResult = inTorqueLossResult; + var inAngularVelocity = outAngularVelocity * effectiveRatio; + + CurrentState.InertiaTorqueLossOut = !inAngularVelocity.IsEqual(0) + ? Formulas.InertiaPower(outAngularVelocity, PreviousState.OutAngularVelocity, ModelData.Inertia, dt) / + avgOutAngularVelocity + : 0.SI<NewtonMeter>(); + inTorque += CurrentState.InertiaTorqueLossOut / effectiveRatio; + if (Gear != PreviousState.Gear && + ConsiderShiftLosses(new GearInfo(Gear, torqueConverterLocked), outTorque)) { + CurrentState.PowershiftLosses = ComputeShiftLosses(outTorque, outAngularVelocity); + } + if (CurrentState.PowershiftLosses != null) { + var averageEngineSpeed = (DataBus.EngineSpeed + outAngularVelocity * ModelData.Gears[Gear].Ratio) / 2; + inTorque += CurrentState.PowershiftLosses / dt / averageEngineSpeed; + } + if (dryRun) { + var dryRunResponse = HandleDryRunRequest(absTime, dt, torqueConverterLocked, inTorque, inAngularVelocity); + dryRunResponse.GearboxPowerRequest = outTorque * avgOutAngularVelocity; + return dryRunResponse; + } + + CurrentState.TransmissionTorqueLoss = inTorque * effectiveRatio - outTorque; + + CurrentState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); + CurrentState.Gear = Gear; + // end critical section + + if (TorqueConverter != null && !torqueConverterLocked) { + CurrentState.TorqueConverterActive = true; + return TorqueConverter.Request(absTime, dt, inTorque, inAngularVelocity); + } + // mk 2016-12-13 + //if (outTorque.IsSmaller(0) && inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed)) { + // Log.Warn("engine speed would fall below idle speed - disengage! gear from cycle: {0}, vehicle speed: {1}", Gear, + // DataBus.VehicleSpeed); + // Gear = 0; + // return RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun); + //} + if (TorqueConverter != null) { + TorqueConverter.Locked(CurrentState.InTorque, CurrentState.InAngularVelocity, CurrentState.InTorque, + CurrentState.InAngularVelocity); + } + var response = NextComponent.Request(absTime, dt, inTorque, inAngularVelocity); + response.GearboxPowerRequest = outTorque * avgOutAngularVelocity; + return response; + } + + private void CheckModelData(TransmissionLossMap effectiveLossMap, double effectiveRatio, bool torqueConverterLocked) + { + if (effectiveLossMap == null || double.IsNaN(effectiveRatio)) { + throw new VectoSimulationException("Ratio or loss-map for gear {0}{1} invalid. Please check input data", Gear, + torqueConverterLocked ? "L" : "C"); + } + if (!torqueConverterLocked && !ModelData.Gears[Gear].HasTorqueConverter) { + throw new VectoSimulationException("Torque converter requested by cycle for gear without torque converter!"); + } + } + + private IResponse HandleDryRunRequest(Second absTime, Second dt, bool torqueConverterLocked, NewtonMeter inTorque, + PerSecond inAngularVelocity) + { + if (TorqueConverter != null && !torqueConverterLocked) { + return TorqueConverter.Request(absTime, dt, inTorque, inAngularVelocity, true); + } + // mk 2016-12-13 + //if (outTorque.IsSmaller(0) && inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed)) { + // //Log.Warn("engine speed would fall below idle speed - disengage! gear from cycle: {0}, vehicle speed: {1}", Gear, + // // DataBus.VehicleSpeed); + // Gear = 0; + // return RequestDisengaged(absTime, dt, outTorque, outAngularVelocity, dryRun); + //} + return NextComponent.Request(absTime, dt, inTorque, inAngularVelocity, true); + } + + /// <summary> + /// Handles Requests when no gear is disengaged + /// </summary> + /// <param name="absTime"></param> + /// <param name="dt"></param> + /// <param name="outTorque"></param> + /// <param name="outAngularVelocity"></param> + /// <param name="dryRun"></param> + /// <returns></returns> + private IResponse RequestDisengaged(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + bool dryRun) + { + if (Disengaged == null) { + Disengaged = absTime; + } + + var avgOutAngularVelocity = (PreviousState.OutAngularVelocity + outAngularVelocity) / 2.0; + if (dryRun) { + // if gearbox is disengaged the 0-line is the limit for drag and full load + return new ResponseDryRun { + Source = this, + GearboxPowerRequest = outTorque * avgOutAngularVelocity, + DeltaDragLoad = outTorque * avgOutAngularVelocity, + DeltaFullLoad = outTorque * avgOutAngularVelocity, + }; + } + + if ((outTorque * avgOutAngularVelocity).IsGreater(0.SI<Watt>(), Constants.SimulationSettings.LineSearchTolerance) && + !outAngularVelocity.IsEqual(0)) { + return new ResponseOverload { + Source = this, + Delta = outTorque * avgOutAngularVelocity, + GearboxPowerRequest = outTorque * avgOutAngularVelocity + }; + } + + if ((outTorque * avgOutAngularVelocity).IsSmaller(0.SI<Watt>(), Constants.SimulationSettings.LineSearchTolerance)) { + return new ResponseUnderload { + Source = this, + Delta = outTorque * avgOutAngularVelocity, + GearboxPowerRequest = outTorque * avgOutAngularVelocity + }; + } + + + //var motoringSpeed = DataBus.EngineIdleSpeed; + //var disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), DataBus.EngineIdleSpeed); + //if (!(disengagedResponse is ResponseSuccess)) { + // motoringSpeed = DataBus.EngineSpeed; + // if (motoringSpeed.IsGreater(DataBus.EngineIdleSpeed)) { + // var first = (ResponseDryRun)NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed, true); + // try { + // motoringSpeed = SearchAlgorithm.Search(motoringSpeed, first.DeltaDragLoad, + // Constants.SimulationSettings.EngineIdlingSearchInterval, + // getYValue: result => ((ResponseDryRun)result).DeltaDragLoad, + // evaluateFunction: n => NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), n, true), + // criterion: result => ((ResponseDryRun)result).DeltaDragLoad.Value()); + // } catch (VectoException) { + // Log.Warn("CycleGearbox could not find motoring speed for disengaged state."); + // } + // motoringSpeed = motoringSpeed.LimitTo(DataBus.EngineIdleSpeed, DataBus.EngineSpeed); + // } + // disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed); + //} + IResponse disengagedResponse; + if (GearboxType.AutomaticTransmission()) { + disengagedResponse = EngineIdleRequest(absTime, dt); + } else { + disengagedResponse = NextGear.Gear > 0 + ? NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), + outAngularVelocity * ModelData.Gears[NextGear.Gear].Ratio) + : EngineIdleRequest(absTime, dt); + } + if (TorqueConverter != null) { + if (DataBus.VehicleStopped) { + TorqueConverter.Locked(0.SI<NewtonMeter>(), disengagedResponse.EngineSpeed, CurrentState.InTorque, + outAngularVelocity); + } else { + TorqueConverter.Locked(CurrentState.InTorque, disengagedResponse.EngineSpeed, CurrentState.InTorque, + disengagedResponse.EngineSpeed); + } + } + disengagedResponse.GearboxPowerRequest = outTorque * avgOutAngularVelocity; + CurrentState.SetState(0.SI<NewtonMeter>(), disengagedResponse.EngineSpeed, 0.SI<NewtonMeter>(), outAngularVelocity); + CurrentState.Gear = Gear; + + return disengagedResponse; + } + + private IResponse EngineIdleRequest(Second absTime, Second dt) + { + var disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), DataBus.EngineIdleSpeed); + if (disengagedResponse is ResponseSuccess) { + return disengagedResponse; + } + var motoringSpeed = DataBus.EngineSpeed; + if (motoringSpeed.IsGreater(DataBus.EngineIdleSpeed)) { + var first = (ResponseDryRun)NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed, true); + try { + motoringSpeed = SearchAlgorithm.Search(motoringSpeed, first.DeltaDragLoad, + Constants.SimulationSettings.EngineIdlingSearchInterval, + getYValue: result => ((ResponseDryRun)result).DeltaDragLoad, + evaluateFunction: n => NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), n, true), + criterion: result => ((ResponseDryRun)result).DeltaDragLoad.Value()); + } catch (VectoException) { + Log.Warn("CycleGearbox could not find motoring speed for disengaged state."); + } + motoringSpeed = motoringSpeed.LimitTo(DataBus.EngineIdleSpeed, DataBus.EngineSpeed); + } + disengagedResponse = NextComponent.Request(absTime, dt, 0.SI<NewtonMeter>(), motoringSpeed); + return disengagedResponse; + } + + protected override void DoWriteModalResults(IModalDataContainer container) + { + var avgInAngularSpeed = (PreviousState.InAngularVelocity + CurrentState.InAngularVelocity) / 2.0; + var avgOutAngularSpeed = (PreviousState.OutAngularVelocity + CurrentState.OutAngularVelocity) / 2.0; + container[ModalResultField.Gear] = Disengaged != null ? 0 : Gear; + container[ModalResultField.P_gbx_loss] = CurrentState.TransmissionTorqueLoss * avgOutAngularSpeed; + container[ModalResultField.P_gbx_inertia] = CurrentState.InertiaTorqueLossOut * avgOutAngularSpeed; + container[ModalResultField.P_gbx_in] = CurrentState.InTorque * avgInAngularSpeed; + container[ModalResultField.n_gbx_out_avg] = (PreviousState.OutAngularVelocity + + CurrentState.OutAngularVelocity) / 2.0; + container[ModalResultField.T_gbx_out] = CurrentState.OutTorque; + + if (ModelData.Type.AutomaticTransmission()) { + container[ModalResultField.TC_Locked] = !CurrentState.TorqueConverterActive; + container[ModalResultField.P_gbx_shift_loss] = CurrentState.PowershiftLosses == null + ? 0.SI<Watt>() + : CurrentState.PowershiftLosses * avgInAngularSpeed; + } + // torque converter fields are written by TorqueConverter (if present), called from Vehicle container + } + + protected override void DoCommitSimulationStep() + { + if (Gear != 0) { + if (CurrentState.TorqueLossResult != null && CurrentState.TorqueLossResult.Extrapolated) { + Log.Warn( + "Gear {0} LossMap data was extrapolated: range for loss map is not sufficient: n:{1}, torque:{2}", + Gear, CurrentState.OutAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.OutTorque); + if (DataBus.ExecutionMode == ExecutionMode.Declaration) { + throw new VectoException( + "Gear {0} LossMap data was extrapolated in Declaration Mode: range for loss map is not sufficient: n:{1}, torque:{2}", + Gear, CurrentState.OutAngularVelocity.ConvertTo().Rounds.Per.Minute, CurrentState.OutTorque); + } + } + } + base.DoCommitSimulationStep(); + } + + #region ICluchInfo + + public override GearInfo NextGear + { + get + { + if (Disengaged == null) { + return new GearInfo(Gear, !TorqueConverterActive ?? true); + } + var future = DataBus.LookAhead(ModelData.TractionInterruption * 5); + var nextGear = 0u; + var torqueConverterLocked = false; + foreach (var entry in future) { + if (entry.VehicleTargetSpeed != null && entry.VehicleTargetSpeed.IsEqual(0)) { + // vehicle is stopped, no next gear, engine should go to idle + break; + } + if (entry.WheelAngularVelocity != null && entry.WheelAngularVelocity.IsEqual(0)) { + // vehicle is stopped, no next gear, engine should go to idle + break; + } + if (entry.Gear == 0) { + continue; + } + nextGear = entry.Gear; + torqueConverterLocked = !entry.TorqueConverterActive ?? false; + break; + } + return new GearInfo(nextGear, torqueConverterLocked); + } + } + + public override Second TractionInterruption + { + get + { + if (Disengaged == null) { + return ModelData.TractionInterruption; + } + var future = DataBus.LookAhead(ModelData.TractionInterruption * 5); + foreach (var entry in future) { + if (entry.VehicleTargetSpeed != null && entry.VehicleTargetSpeed.IsEqual(0)) { + // vehicle is stopped, no next gear, engine should go to idle + break; + } + if (entry.WheelAngularVelocity != null && entry.WheelAngularVelocity.IsEqual(0)) { + // vehicle is stopped, no next gear, engine should go to idle + break; + } + if (entry.Gear == 0) { + continue; + } + return entry.Time - Disengaged; + } + return ModelData.TractionInterruption; + } + } + + public override bool ClutchClosed(Second absTime) + { + return (DataBus.DriverBehavior == DrivingBehavior.Braking + ? DataBus.CycleData.LeftSample.Gear + : DataBus.CycleData.RightSample.Gear) != 0; + } + + #endregion + + public class CycleGearboxState : GearboxState + { + public bool TorqueConverterActive; + public WattSecond PowershiftLosses { get; set; } + } + + public class CycleShiftStrategy : BaseShiftStrategy + { + public CycleShiftStrategy(GearboxData data, IDataBus dataBus) : base(data, dataBus) {} + + public override IGearbox Gearbox { get; set; } + + public override bool ShiftRequired(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity, + NewtonMeter inTorque, + PerSecond inAngularVelocity, uint gear, Second lastShiftTime) + { + return false; + } + + public override uint InitGear(Second absTime, Second dt, NewtonMeter torque, PerSecond outAngularVelocity) + { + throw new System.NotImplementedException(); + } + + public override uint Engage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity) + { + throw new System.NotImplementedException(); + } + + public override void Disengage(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outEngineSpeed) + { + throw new System.NotImplementedException(); + } + + public override GearInfo NextGear + { + get { throw new System.NotImplementedException(); } + } + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs index 485e4be47e..ef9a330dd3 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using TUGraz.VectoCommon.Exceptions; using TUGraz.VectoCommon.Models; using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.Configuration; @@ -130,42 +131,52 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } } else { // update action distance for current 'next action' - if (Driver.DataBus.VehicleSpeed > NextDrivingAction.NextTargetSpeed) { - var brakingDistance = Driver.ComputeDecelerationDistance(NextDrivingAction.NextTargetSpeed) + BrakingSafetyMargin; - switch (NextDrivingAction.Action) { - case DrivingBehavior.Coasting: - //var coastingDistance = ComputeCoastingDistance(Driver.DataBus.VehicleSpeed, NextDrivingAction.NextTargetSpeed); - var coastingDistance = ComputeCoastingDistance(Driver.DataBus.VehicleSpeed, NextDrivingAction.CycleEntry); - NextDrivingAction.CoastingStartDistance = NextDrivingAction.TriggerDistance - coastingDistance; - NextDrivingAction.BrakingStartDistance = NextDrivingAction.TriggerDistance - brakingDistance; - break; - case DrivingBehavior.Braking: - NextDrivingAction.BrakingStartDistance = NextDrivingAction.TriggerDistance - brakingDistance; - NextDrivingAction.CoastingStartDistance = double.MaxValue.SI<Meter>(); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } + UpdateDistancesForCurrentNextAction(); - if (nextAction != null) { - if (nextAction.HasEqualTrigger(NextDrivingAction)) { - // if the action changes and the vehicle has not yet exceeded the action distance => update the action - // otherwise do nothing, NextDrivingAction's action distance has already been updated - if (nextAction.Action != NextDrivingAction.Action && nextAction.ActionDistance > currentDistance) { - NextDrivingAction = nextAction; - } - } else { - // hmm, we've got a new action that is closer to what we got before? - if (nextAction.ActionDistance < NextDrivingAction.ActionDistance) { - NextDrivingAction = nextAction; - } + SetNextDrivingAction(currentDistance, nextAction); + } + Log.Debug("Next Driving Action: {0}", NextDrivingAction); + } + + private void SetNextDrivingAction(Meter currentDistance, DrivingBehaviorEntry nextAction) + { + if (nextAction != null) { + if (nextAction.HasEqualTrigger(NextDrivingAction)) { + // if the action changes and the vehicle has not yet exceeded the action distance => update the action + // otherwise do nothing, NextDrivingAction's action distance has already been updated + if (nextAction.Action != NextDrivingAction.Action && nextAction.ActionDistance > currentDistance) { + NextDrivingAction = nextAction; } } else { - NextDrivingAction = null; + // hmm, we've got a new action that is closer to what we got before? + if (nextAction.ActionDistance < NextDrivingAction.ActionDistance) { + NextDrivingAction = nextAction; + } + } + } else { + NextDrivingAction = null; + } + } + + private void UpdateDistancesForCurrentNextAction() + { + if (Driver.DataBus.VehicleSpeed > NextDrivingAction.NextTargetSpeed) { + var brakingDistance = Driver.ComputeDecelerationDistance(NextDrivingAction.NextTargetSpeed) + BrakingSafetyMargin; + switch (NextDrivingAction.Action) { + case DrivingBehavior.Coasting: + //var coastingDistance = ComputeCoastingDistance(Driver.DataBus.VehicleSpeed, NextDrivingAction.NextTargetSpeed); + var coastingDistance = ComputeCoastingDistance(Driver.DataBus.VehicleSpeed, NextDrivingAction.CycleEntry); + NextDrivingAction.CoastingStartDistance = NextDrivingAction.TriggerDistance - coastingDistance; + NextDrivingAction.BrakingStartDistance = NextDrivingAction.TriggerDistance - brakingDistance; + break; + case DrivingBehavior.Braking: + NextDrivingAction.BrakingStartDistance = NextDrivingAction.TriggerDistance - brakingDistance; + NextDrivingAction.CoastingStartDistance = double.MaxValue.SI<Meter>(); + break; + default: + throw new ArgumentOutOfRangeException(); } } - Log.Debug("Next Driving Action: {0}", NextDrivingAction); } protected internal DrivingBehaviorEntry GetNextDrivingAction(Meter ds) @@ -184,51 +195,58 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var nextTargetSpeed = OverspeedAllowed(entry.VehicleTargetSpeed) ? entry.VehicleTargetSpeed + Driver.DriverData.OverSpeedEcoRoll.OverSpeed : entry.VehicleTargetSpeed; - if (nextTargetSpeed < currentSpeed) { - var action = DrivingBehavior.Braking; - var coastingDistance = ComputeCoastingDistance(currentSpeed, entry); - var brakingDistance = Driver.ComputeDecelerationDistance(nextTargetSpeed) + BrakingSafetyMargin; - - if (!Driver.DriverData.LookAheadCoasting.Enabled || coastingDistance < 0) { - Log.Debug( - "adding 'Braking' starting at distance {0}. brakingDistance: {1}, triggerDistance: {2}, nextTargetSpeed: {3}", - entry.Distance - brakingDistance, brakingDistance, entry.Distance, nextTargetSpeed); - coastingDistance = brakingDistance; - } else { - //var coastingDistance = ComputeCoastingDistance(currentSpeed, nextTargetSpeed); - if (currentSpeed > Driver.DriverData.LookAheadCoasting.MinSpeed) { - action = DrivingBehavior.Coasting; - - Log.Debug( - "adding 'Coasting' starting at distance {0}. coastingDistance: {1}, triggerDistance: {2}, nextTargetSpeed: {3}", - entry.Distance - coastingDistance, coastingDistance, entry.Distance, nextTargetSpeed); - } else { - coastingDistance = -1.SI<Meter>(); - } - } - nextActions.Add( - new DrivingBehaviorEntry { - Action = action, - CoastingStartDistance = entry.Distance - coastingDistance, - BrakingStartDistance = entry.Distance - brakingDistance, - TriggerDistance = entry.Distance, - NextTargetSpeed = nextTargetSpeed, - CycleEntry = entry, - }); + if (nextTargetSpeed >= currentSpeed) { + // acceleration is not relevant + continue; } + nextActions.Add(GetDrivingBehaviorEntry(nextTargetSpeed, currentSpeed, entry)); } if (!nextActions.Any()) { return null; } var nextBrakingAction = nextActions.OrderBy(x => x.BrakingStartDistance).First(); var nextCoastingAction = nextActions.OrderBy(x => x.CoastingStartDistance).First(); - if (nextBrakingAction.TriggerDistance.IsEqual(nextCoastingAction.TriggerDistance)) { - // its the same trigger, use it - return nextCoastingAction; - } + + return nextBrakingAction.TriggerDistance.IsEqual(nextCoastingAction.TriggerDistance) + ? nextCoastingAction + : nextBrakingAction; // MQ: 27.5.2016 remark: one could set the coasting distance to the closest coasting distance as found above to start coasting a little bit earlier. - return nextBrakingAction; + } + + private DrivingBehaviorEntry GetDrivingBehaviorEntry(MeterPerSecond nextTargetSpeed, MeterPerSecond currentSpeed, + DrivingCycleData.DrivingCycleEntry entry) + { + var action = DrivingBehavior.Braking; + + var brakingDistance = Driver.ComputeDecelerationDistance(nextTargetSpeed) + BrakingSafetyMargin; + var coastingDistance = ComputeCoastingDistance(currentSpeed, entry); + if (!Driver.DriverData.LookAheadCoasting.Enabled || coastingDistance < 0) { + Log.Debug( + "adding 'Braking' starting at distance {0}. brakingDistance: {1}, triggerDistance: {2}, nextTargetSpeed: {3}", + entry.Distance - brakingDistance, brakingDistance, entry.Distance, nextTargetSpeed); + coastingDistance = brakingDistance; + } else { + //var coastingDistance = ComputeCoastingDistance(currentSpeed, nextTargetSpeed); + if (currentSpeed > Driver.DriverData.LookAheadCoasting.MinSpeed) { + action = DrivingBehavior.Coasting; + + Log.Debug( + "adding 'Coasting' starting at distance {0}. coastingDistance: {1}, triggerDistance: {2}, nextTargetSpeed: {3}", + entry.Distance - coastingDistance, coastingDistance, entry.Distance, nextTargetSpeed); + } else { + coastingDistance = -1.SI<Meter>(); + } + } + var nextEntry = new DrivingBehaviorEntry { + Action = action, + CoastingStartDistance = entry.Distance - coastingDistance, + BrakingStartDistance = entry.Distance - brakingDistance, + TriggerDistance = entry.Distance, + NextTargetSpeed = nextTargetSpeed, + CycleEntry = entry, + }; + return nextEntry; } protected internal virtual Meter ComputeCoastingDistance(MeterPerSecond vehicleSpeed, @@ -383,96 +401,122 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl velocity += DriverData.OverSpeedEcoRoll.OverSpeed; } if (DataBus.GearboxType.AutomaticTransmission() || DataBus.ClutchClosed(absTime)) { - // drive along - IResponse first; - if (DriverStrategy.OverspeedAllowed(targetVelocity, prohibitOverspeed) && - DataBus.VehicleSpeed.IsEqual(targetVelocity)) { - first = Driver.DrivingActionCoast(absTime, ds, velocity, gradient); - debug.Add(new { action = "Coast", first }); - if (first is ResponseSuccess && first.Acceleration < 0) { - first = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); - debug.Add(new { action = "Coast:(Success & Acc<0) -> Accelerate", first }); - } - } else { - first = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); - debug.Add(new { action = "Accelerate", first }); - } + return HandleRequestEngaged(absTime, ds, targetVelocity, gradient, prohibitOverspeed, velocity, debug); + } else { + return HandleRequestDisengaged(absTime, ds, gradient, velocity, debug); + } + } - var second = first; - first.Switch(). - Case<ResponseUnderload>(r => { - if (DataBus.VehicleSpeed.IsGreater(0) && DriverStrategy.OverspeedAllowed(targetVelocity, prohibitOverspeed)) { - second = Driver.DrivingActionCoast(absTime, ds, velocity, gradient); - debug.Add(new { action = "first:(Underload & Overspeed)-> Coast", second }); - if (second is ResponseUnderload || second is ResponseSpeedLimitExceeded) { - second = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); - debug.Add(new { - action = "second:(Underload|SpeedLimitExceeded) -> Brake", - second - }); - } - if (second is ResponseEngineSpeedTooHigh) { - second = Driver.DrivingActionBrake(absTime, ds, velocity, gradient, second); - debug.Add(new { - action = "second:(EngineSpeedTooHigh|SpeedLimitExceeded) -> Brake with reduced acceleration", - second - }); - } - } else { - second = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); - debug.Add(new { action = "first:(Underload & !Overspeed) -> Brake", second }); - } - }); - - var third = second; - - second.Switch(). - Case<ResponseGearShift>(r => { - third = Driver.DrivingActionRoll(absTime, ds, velocity, gradient); - debug.Add(new { action = "second: GearShift -> Roll", third }); - third.Switch(). - Case<ResponseUnderload>(() => { - // overload may happen if driver limits acceleration when rolling downhill - third = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); - debug.Add(new { action = "third:Underload -> Brake", third }); - }). - Case<ResponseSpeedLimitExceeded>(() => { - third = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); - debug.Add(new { action = "third:SpeedLimitExceeded -> Brake", third }); - }); - }). - Case<ResponseOverload>(r => { - third = Driver.DrivingActionCoast(absTime, ds, velocity, gradient); - debug.Add(new { action = "second:Overload -> Coast", third }); - }); + private IResponse HandleRequestDisengaged(Second absTime, Meter ds, Radian gradient, MeterPerSecond velocity, + DebugData debug) + { + if (DataBus.VehicleSpeed.IsSmallerOrEqual(0.SI<MeterPerSecond>())) { + // the clutch is disengaged, and the vehicle stopped - we can't perform a roll action. wait for the clutch to be engaged + // todo mk 2016-08-23: is this still needed? + var remainingShiftTime = Constants.SimulationSettings.TargetTimeInterval; + while (!DataBus.ClutchClosed(absTime + remainingShiftTime)) { + remainingShiftTime += Constants.SimulationSettings.TargetTimeInterval; + } + return new ResponseFailTimeInterval { + Source = this, + DeltaT = remainingShiftTime, + }; + } + var response = Driver.DrivingActionRoll(absTime, ds, velocity, gradient); + debug.Add(new { action = "ClutchOpen -> Roll", response }); + response.Switch(). + Case<ResponseUnderload>(r => { + response = Driver.DrivingActionBrake(absTime, ds, velocity, gradient, r); + debug.Add(new { action = "Roll:Underload -> Brake", response }); + }) + .Case<ResponseSpeedLimitExceeded>(() => { + response = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); + debug.Add(new { action = "Roll:SpeedLimitExceeded -> Brake", response }); + }); + return response; + } - return third; - } else { - if (DataBus.VehicleSpeed.IsSmallerOrEqual(0.SI<MeterPerSecond>())) { - // the clutch is disengaged, and the vehicle stopped - we can't perform a roll action. wait for the clutch to be engaged - // todo mk 2016-08-23: is this still needed? - var remainingShiftTime = Constants.SimulationSettings.TargetTimeInterval; - while (!DataBus.ClutchClosed(absTime + remainingShiftTime)) { - remainingShiftTime += Constants.SimulationSettings.TargetTimeInterval; + private IResponse HandleRequestEngaged(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient, + bool prohibitOverspeed, MeterPerSecond velocity, DebugData debug) + { + // drive along + var first = FirstAccelerateOrCoast(absTime, ds, targetVelocity, gradient, prohibitOverspeed, velocity, debug); + + var second = first; + first.Switch(). + Case<ResponseUnderload>(r => { + if (DataBus.VehicleSpeed.IsGreater(0) && DriverStrategy.OverspeedAllowed(targetVelocity, prohibitOverspeed)) { + second = Driver.DrivingActionCoast(absTime, ds, velocity, gradient); + debug.Add(new { action = "first:(Underload & Overspeed)-> Coast", second }); + second = HandleCoastAfterUnderloadWithOverspeed(absTime, ds, gradient, velocity, debug, second); + } else { + second = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); + debug.Add(new { action = "first:(Underload & !Overspeed) -> Brake", second }); } - return new ResponseFailTimeInterval { - Source = this, - DeltaT = remainingShiftTime, - }; + }); + + var third = second; + + second.Switch(). + Case<ResponseGearShift>(r => { + third = Driver.DrivingActionRoll(absTime, ds, velocity, gradient); + debug.Add(new { action = "second: GearShift -> Roll", third }); + third.Switch(). + Case<ResponseUnderload>(() => { + // overload may happen if driver limits acceleration when rolling downhill + third = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); + debug.Add(new { action = "third:Underload -> Brake", third }); + }). + Case<ResponseSpeedLimitExceeded>(() => { + third = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); + debug.Add(new { action = "third:SpeedLimitExceeded -> Brake", third }); + }); + }). + Case<ResponseOverload>(r => { + third = Driver.DrivingActionCoast(absTime, ds, velocity, gradient); + debug.Add(new { action = "second:Overload -> Coast", third }); + }); + + return third; + } + + private IResponse HandleCoastAfterUnderloadWithOverspeed(Second absTime, Meter ds, Radian gradient, + MeterPerSecond velocity, DebugData debug, IResponse second) + { + if (second is ResponseUnderload || second is ResponseSpeedLimitExceeded) { + second = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); + debug.Add(new { + action = "second:(Underload|SpeedLimitExceeded) -> Brake", + second + }); + } + if (second is ResponseEngineSpeedTooHigh) { + second = Driver.DrivingActionBrake(absTime, ds, velocity, gradient, second); + debug.Add(new { + action = "second:(EngineSpeedTooHigh|SpeedLimitExceeded) -> Brake with reduced acceleration", + second + }); + } + return second; + } + + private IResponse FirstAccelerateOrCoast(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient, + bool prohibitOverspeed, MeterPerSecond velocity, DebugData debug) + { + IResponse first; + if (DriverStrategy.OverspeedAllowed(targetVelocity, prohibitOverspeed) && + DataBus.VehicleSpeed.IsEqual(targetVelocity)) { + first = Driver.DrivingActionCoast(absTime, ds, velocity, gradient); + debug.Add(new { action = "Coast", first }); + if (first is ResponseSuccess && first.Acceleration < 0) { + first = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); + debug.Add(new { action = "Coast:(Success & Acc<0) -> Accelerate", first }); } - var response = Driver.DrivingActionRoll(absTime, ds, velocity, gradient); - debug.Add(new { action = "ClutchOpen -> Roll", response }); - response.Switch(). - Case<ResponseUnderload>(r => { - response = Driver.DrivingActionBrake(absTime, ds, velocity, gradient, r); - debug.Add(new { action = "Roll:Underload -> Brake", response }); - }) - .Case<ResponseSpeedLimitExceeded>(() => { - response = Driver.DrivingActionBrake(absTime, ds, velocity, gradient); - debug.Add(new { action = "Roll:SpeedLimitExceeded -> Brake", response }); - }); - return response; + } else { + first = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); + debug.Add(new { action = "Accelerate", first }); } + return first; } protected override IResponse CheckRequestDoesNotExceedNextAction(Second absTime, Meter ds, @@ -536,7 +580,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected override IResponse DoHandleRequest(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient, bool prohibitOverspeed = false) { - IResponse response = null; if (DataBus.VehicleSpeed <= DriverStrategy.BrakeTrigger.NextTargetSpeed) { return HandleTargetspeedReached(absTime, ds, targetVelocity, gradient); } @@ -546,137 +589,218 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl DefaultDriverStrategy.BrakingSafetyMargin; DriverStrategy.BrakeTrigger.BrakingStartDistance = DriverStrategy.BrakeTrigger.TriggerDistance - brakingDistance; if (Phase == BrakingPhase.Coast) { - var nextBrakeAction = DriverStrategy.GetNextDrivingAction(ds); - if (nextBrakeAction != null && !DriverStrategy.BrakeTrigger.TriggerDistance.IsEqual(nextBrakeAction.TriggerDistance) && - nextBrakeAction.BrakingStartDistance.IsSmaller(DriverStrategy.BrakeTrigger.BrakingStartDistance)) { - DriverStrategy.BrakeTrigger = nextBrakeAction; - Log.Debug("setting brake trigger to new trigger: trigger distance: {0}, start braking @ {1}", - nextBrakeAction.TriggerDistance, nextBrakeAction.BrakingStartDistance); - } - - Log.Debug("start braking @ {0}", DriverStrategy.BrakeTrigger.BrakingStartDistance); - var remainingDistanceToBrake = DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance; - var estimatedTimeInterval = remainingDistanceToBrake / DataBus.VehicleSpeed; - if (estimatedTimeInterval.IsSmaller(Constants.SimulationSettings.LowerBoundTimeInterval) || - currentDistance + Constants.SimulationSettings.DriverActionDistanceTolerance > - DriverStrategy.BrakeTrigger.BrakingStartDistance) { - Phase = BrakingPhase.Brake; - Log.Debug("Switching to BRAKE Phase. currentDistance: {0}", currentDistance); - } else { - if ((currentDistance + ds).IsGreater(DriverStrategy.BrakeTrigger.BrakingStartDistance)) { - return new ResponseDrivingCycleDistanceExceeded() { - //Source = this, - MaxDistance = DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance - }; - } - } - if (DataBus.VehicleSpeed < Constants.SimulationSettings.MinVelocityForCoast) { - Phase = BrakingPhase.Brake; - Log.Debug("Switching to BRAKE Phase. currentDistance: {0} v: {1}", currentDistance, - DataBus.VehicleSpeed); + var resp = CheckSwitchingToBraking(ds, currentDistance); + if (resp != null) { + return resp; } } switch (Phase) { case BrakingPhase.Coast: - Driver.DriverBehavior = DrivingBehavior.Coasting; + return DoCoast(absTime, ds, targetVelocity, gradient, currentDistance); + case BrakingPhase.Brake: + return DoBrake(absTime, ds, targetVelocity, gradient, brakingDistance, currentDistance); + default: + throw new VectoException("Invalid Phase in DriverModeBrake"); + } + } + + private IResponse DoBrake(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient, + Meter brakingDistance, Meter currentDistance) + { + IResponse response; + Log.Debug("Phase: BRAKE. breaking distance: {0} start braking @ {1}", brakingDistance, + DriverStrategy.BrakeTrigger.BrakingStartDistance); + if (DriverStrategy.BrakeTrigger.BrakingStartDistance.IsSmaller(currentDistance, + Constants.SimulationSettings.DriverActionDistanceTolerance / 2)) { + Log.Info("Expected Braking Deceleration could not be reached! {0}", + DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance); + } + var targetDistance = DataBus.VehicleSpeed < Constants.SimulationSettings.MinVelocityForCoast + ? DriverStrategy.BrakeTrigger.TriggerDistance + : null; + if (targetDistance == null && DriverStrategy.BrakeTrigger.NextTargetSpeed.IsEqual(0.SI<MeterPerSecond>())) { + targetDistance = DriverStrategy.BrakeTrigger.TriggerDistance - DefaultDriverStrategy.BrakingSafetyMargin; + } + Driver.DriverBehavior = DrivingBehavior.Braking; + response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient, targetDistance: targetDistance); + response.Switch(). + Case<ResponseOverload>(r => { + Log.Info( + "Brake -> Got OverloadResponse during brake action - desired deceleration could not be reached! response: {0}", + r); + if (!DataBus.ClutchClosed(absTime)) { + Log.Info("Brake -> Overload -> Clutch is open - Trying roll action"); + response = Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); + } else { + Log.Info("Brake -> Overload -> Clutch is closed - Trying brake action again"); + DataBus.BrakePower = 0.SI<Watt>(); + response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient, + targetDistance: targetDistance); + response.Switch(). + Case<ResponseOverload>(r1 => { + Log.Info("Brake -> Overload -> 2nd Brake -> Overload -> Trying accelerate action"); + response = Driver.DrivingActionAccelerate(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); + response.Switch().Case<ResponseGearShift>( + rs => { + Log.Info("Brake -> Overload -> 2nd Brake -> Accelerate -> Got GearShift response, performing roll action"); + response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); + }); + }); + } + }). + Case<ResponseGearShift>(r => { + Log.Info("Brake -> Got GearShift response, performing roll action + brakes"); + //response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); + DataBus.BrakePower = 0.SI<Watt>(); + response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient, targetDistance: targetDistance); + }); + return response; + } + + private IResponse DoCoast(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient, + Meter currentDistance) + { + IResponse response; + Driver.DriverBehavior = DrivingBehavior.Coasting; + response = DataBus.ClutchClosed(absTime) + ? Driver.DrivingActionCoast(absTime, ds, VectoMath.Max(targetVelocity, DataBus.VehicleSpeed), gradient) + : Driver.DrivingActionRoll(absTime, ds, VectoMath.Max(targetVelocity, DataBus.VehicleSpeed), gradient); + response.Switch(). + Case<ResponseUnderload>(r => { + // coast would decelerate more than driver's max deceleration => issue brakes to decelerate with driver's max deceleration + response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient, r); + if ((DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance).IsSmallerOrEqual( + Constants.SimulationSettings.DriverActionDistanceTolerance)) { + Phase = BrakingPhase.Brake; + } + }). + Case<ResponseOverload>(r => { + // limiting deceleration while coast may result in an overload => issue brakes to decelerate with driver's max deceleration response = DataBus.ClutchClosed(absTime) - ? Driver.DrivingActionCoast(absTime, ds, VectoMath.Max(targetVelocity, DataBus.VehicleSpeed), gradient) - : Driver.DrivingActionRoll(absTime, ds, VectoMath.Max(targetVelocity, DataBus.VehicleSpeed), gradient); + ? Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient) + : Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); + //Phase = BrakingPhase.Brake; + }). + Case<ResponseDrivingCycleDistanceExceeded>(r => { + if (!ds.IsEqual(r.MaxDistance)) { + // distance has been reduced due to vehicle stop in coast/roll action => use brake action to get exactly to the stop-distance + // TODO what if no gear is enaged (and we need driveline power to get to the stop-distance? + response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); + } + }). + Case<ResponseGearShift>(r => { + response = Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); + }). + Case<ResponseEngineSpeedTooHigh>(r => { + response = Driver.DrivingActionBrake(absTime, ds, targetVelocity, gradient, r); + }); + // handle the SpeedLimitExceeded Response separately in case it occurs in one of the requests in the second try + response.Switch(). + Case<ResponseSpeedLimitExceeded>(() => { + response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed, + gradient); + if (response is ResponseOverload && !DataBus.ClutchClosed(absTime)) { + response = Driver.DrivingActionRoll(absTime, ds, DataBus.VehicleSpeed, gradient); + } + }); + return response; + } + + private IResponse CheckSwitchingToBraking(Meter ds, Meter currentDistance) + { + var nextBrakeAction = DriverStrategy.GetNextDrivingAction(ds); + if (nextBrakeAction != null && !DriverStrategy.BrakeTrigger.TriggerDistance.IsEqual(nextBrakeAction.TriggerDistance) && + nextBrakeAction.BrakingStartDistance.IsSmaller(DriverStrategy.BrakeTrigger.BrakingStartDistance)) { + DriverStrategy.BrakeTrigger = nextBrakeAction; + Log.Debug("setting brake trigger to new trigger: trigger distance: {0}, start braking @ {1}", + nextBrakeAction.TriggerDistance, nextBrakeAction.BrakingStartDistance); + } + + Log.Debug("start braking @ {0}", DriverStrategy.BrakeTrigger.BrakingStartDistance); + var remainingDistanceToBrake = DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance; + var estimatedTimeInterval = remainingDistanceToBrake / DataBus.VehicleSpeed; + if (estimatedTimeInterval.IsSmaller(Constants.SimulationSettings.LowerBoundTimeInterval) || + currentDistance + Constants.SimulationSettings.DriverActionDistanceTolerance > + DriverStrategy.BrakeTrigger.BrakingStartDistance) { + Phase = BrakingPhase.Brake; + Log.Debug("Switching to BRAKE Phase. currentDistance: {0}", currentDistance); + } else { + if ((currentDistance + ds).IsGreater(DriverStrategy.BrakeTrigger.BrakingStartDistance)) { + return new ResponseDrivingCycleDistanceExceeded() { + //Source = this, + MaxDistance = DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance + }; + } + } + if (DataBus.VehicleSpeed < Constants.SimulationSettings.MinVelocityForCoast) { + Phase = BrakingPhase.Brake; + Log.Debug("Switching to BRAKE Phase. currentDistance: {0} v: {1}", currentDistance, + DataBus.VehicleSpeed); + } + return null; + } + + private IResponse HandleTargetspeedReached(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient) + { + var response = TargetSpeedReachedDriveAlong(absTime, ds, targetVelocity, gradient); + //var i = 0; + //do { + response.Switch(). + Case<ResponseGearShift>(() => { + response = Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); response.Switch(). Case<ResponseUnderload>(r => { - // coast would decelerate more than driver's max deceleration => issue brakes to decelerate with driver's max deceleration + // under-load may happen if driver limits acceleration when rolling downhill response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient, r); - if ((DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance).IsSmallerOrEqual( - Constants.SimulationSettings.DriverActionDistanceTolerance)) { - Phase = BrakingPhase.Brake; - } - }). - Case<ResponseOverload>(r => { - // limiting deceleration while coast may result in an overload => issue brakes to decelerate with driver's max deceleration - response = DataBus.ClutchClosed(absTime) - ? Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient) - : Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); - //Phase = BrakingPhase.Brake; }). - Case<ResponseDrivingCycleDistanceExceeded>(r => { - if (!ds.IsEqual(r.MaxDistance)) { - // distance has been reduced due to vehicle stop in coast/roll action => use brake action to get exactly to the stop-distance - // TODO what if no gear is enaged (and we need driveline power to get to the stop-distance? - response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); - } - }). - Case<ResponseGearShift>(r => { response = Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); }). - Case<ResponseEngineSpeedTooHigh>(r => { - response = Driver.DrivingActionBrake(absTime, ds, targetVelocity, gradient, r); - }); - // handle the SpeedLimitExceeded Response separately in case it occurs in one of the requests in the second try - response.Switch(). Case<ResponseSpeedLimitExceeded>(() => { response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed, gradient); - if (response is ResponseOverload && !DataBus.ClutchClosed(absTime)) { - response = Driver.DrivingActionRoll(absTime, ds, DataBus.VehicleSpeed, gradient); - } }); - break; - case BrakingPhase.Brake: - - Log.Debug("Phase: BRAKE. breaking distance: {0} start braking @ {1}", brakingDistance, - DriverStrategy.BrakeTrigger.BrakingStartDistance); - if (DriverStrategy.BrakeTrigger.BrakingStartDistance.IsSmaller(currentDistance, - Constants.SimulationSettings.DriverActionDistanceTolerance / 2)) { - Log.Info("Expected Braking Deceleration could not be reached! {0}", - DriverStrategy.BrakeTrigger.BrakingStartDistance - currentDistance); - } - var targetDistance = DataBus.VehicleSpeed < Constants.SimulationSettings.MinVelocityForCoast - ? DriverStrategy.BrakeTrigger.TriggerDistance - : null; - if (targetDistance == null && DriverStrategy.BrakeTrigger.NextTargetSpeed.IsEqual(0.SI<MeterPerSecond>())) { - targetDistance = DriverStrategy.BrakeTrigger.TriggerDistance - DefaultDriverStrategy.BrakingSafetyMargin; - } - Driver.DriverBehavior = DrivingBehavior.Braking; - response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, - gradient, targetDistance: targetDistance); + }). + Case<ResponseSpeedLimitExceeded>(() => { + response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed, + gradient); + }). + Case<ResponseUnderload>(r => { + //response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + // gradient, r); + response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed + r.Acceleration * r.SimulationInterval, + gradient, r); response.Switch(). - Case<ResponseOverload>(r => { - Log.Info( - "Brake -> Got OverloadResponse during brake action - desired deceleration could not be reached! response: {0}", - r); - if (!DataBus.ClutchClosed(absTime)) { - Log.Info("Brake -> Overload -> Clutch is open - Trying roll action"); - response = Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); - } else { - Log.Info("Brake -> Overload -> Clutch is closed - Trying brake action again"); - DataBus.BrakePower = 0.SI<Watt>(); - response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient, - targetDistance: targetDistance); - response.Switch(). - Case<ResponseOverload>(r1 => { - Log.Info("Brake -> Overload -> 2nd Brake -> Overload -> Trying accelerate action"); - response = Driver.DrivingActionAccelerate(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); - response.Switch().Case<ResponseGearShift>( - rs => { - Log.Info("Brake -> Overload -> 2nd Brake -> Accelerate -> Got GearShift response, performing roll action"); - response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); - }); - }); - } - }). - Case<ResponseGearShift>(r => { - Log.Info("Brake -> Got GearShift response, performing roll action + brakes"); - //response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); + Case<ResponseGearShift>(() => { DataBus.BrakePower = 0.SI<Watt>(); response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, - gradient, targetDistance: targetDistance); + gradient, r); + }). + Case<ResponseOverload>(() => { + DataBus.BrakePower = 0.SI<Watt>(); + if (DataBus.GearboxType.AutomaticTransmission() || DataBus.ClutchClosed(absTime)) { + if (DataBus.VehicleSpeed.IsGreater(0)) { + response = Driver.DrivingActionAccelerate(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); + } else { + if (RetryDistanceExceeded) { + response = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); + } else { + RetryDistanceExceeded = true; + response = new ResponseDrivingCycleDistanceExceeded() { MaxDistance = ds / 2 }; + } + } + } else { + response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); + } }); - break; - } - + }); + //} while (!(response is ResponseSuccess) && i++ < 3); return response; } - private IResponse HandleTargetspeedReached(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient) + private IResponse TargetSpeedReachedDriveAlong(Second absTime, Meter ds, MeterPerSecond targetVelocity, + Radian gradient) { IResponse response; if (DataBus.GearboxType.AutomaticTransmission() || DataBus.ClutchClosed(absTime)) { @@ -687,62 +811,12 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl response = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); } else { RetryDistanceExceeded = true; - return new ResponseDrivingCycleDistanceExceeded() { MaxDistance = ds / 2 }; + response = new ResponseDrivingCycleDistanceExceeded() { MaxDistance = ds / 2 }; } } } else { response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); } - var i = 0; - do { - response.Switch(). - Case<ResponseGearShift>(() => { - response = Driver.DrivingActionRoll(absTime, ds, targetVelocity, gradient); - response.Switch(). - Case<ResponseUnderload>(r => { - // under-load may happen if driver limits acceleration when rolling downhill - response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, - gradient, r); - }). - Case<ResponseSpeedLimitExceeded>(() => { - response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed, - gradient); - }); - }). - Case<ResponseSpeedLimitExceeded>(() => { - response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed, - gradient); - }). - Case<ResponseUnderload>(r => { - //response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, - // gradient, r); - response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed + r.Acceleration * r.SimulationInterval, - gradient, r); - response.Switch(). - Case<ResponseGearShift>(() => { - DataBus.BrakePower = 0.SI<Watt>(); - response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, - gradient, r); - }). - Case<ResponseOverload>(() => { - DataBus.BrakePower = 0.SI<Watt>(); - if (DataBus.GearboxType.AutomaticTransmission() || DataBus.ClutchClosed(absTime)) { - if (DataBus.VehicleSpeed.IsGreater(0)) { - response = Driver.DrivingActionAccelerate(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); - } else { - if (RetryDistanceExceeded) { - response = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); - } else { - RetryDistanceExceeded = true; - response = new ResponseDrivingCycleDistanceExceeded() { MaxDistance = ds / 2 }; - } - } - } else { - response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); - } - }); - }); - } while (!(response is ResponseSuccess) && i++ < 3); return response; } @@ -790,7 +864,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public Meter ActionDistance { - get { + get + { return VectoMath.Min(CoastingStartDistance ?? double.MaxValue.SI<Meter>(), BrakingStartDistance ?? double.MaxValue.SI<Meter>()); } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs index 207f17c590..4abc435139 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DistanceBasedDrivingCycle.cs @@ -29,525 +29,539 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Collections.Generic; -using System.Linq; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Connector.Ports; -using TUGraz.VectoCore.Models.Connector.Ports.Impl; -using TUGraz.VectoCore.Models.Simulation; -using TUGraz.VectoCore.Models.Simulation.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Data; -using TUGraz.VectoCore.OutputData; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Impl -{ - /// <summary> - /// Class representing one Distance Based Driving Cycle - /// </summary> - public sealed class DistanceBasedDrivingCycle : StatefulProviderComponent - <DistanceBasedDrivingCycle.DrivingCycleState, ISimulationOutPort, IDrivingCycleInPort, IDrivingCycleOutPort>, - IDrivingCycle, ISimulationOutPort, IDrivingCycleInPort, IDisposable - { - private const double LookaheadTimeSafetyMargin = 1.5; - internal readonly IDrivingCycleData Data; - internal readonly DrivingCycleEnumerator CycleIntervalIterator; - private bool _intervalProlonged; - internal IdleControllerSwitcher IdleController; - - private DrivingCycleData.DrivingCycleEntry Left - { - get { return CycleIntervalIterator.LeftSample; } - } - - private DrivingCycleData.DrivingCycleEntry Right - { - get { return CycleIntervalIterator.RightSample; } - } - - public DistanceBasedDrivingCycle(IVehicleContainer container, IDrivingCycleData cycle) : base(container) - { - Data = cycle; - CycleIntervalIterator = new DrivingCycleEnumerator(Data); - CycleStartDistance = Data.Entries.Count > 0 ? Data.Entries.First().Distance : 0.SI<Meter>(); - - var first = Data.Entries.First(); - PreviousState = new DrivingCycleState { - AbsTime = 0.SI<Second>(), - WaitTime = 0.SI<Second>(), - Distance = first.Distance, - Altitude = first.Altitude, - }; - CurrentState = PreviousState.Clone(); - } - - public IResponse Initialize() - { - if (Left.VehicleTargetSpeed.IsEqual(0)) { - var retVal = NextComponent.Initialize(DataBus.StartSpeed, - Left.RoadGradient, DataBus.StartAcceleration); - if (!(retVal is ResponseSuccess)) { - throw new UnexpectedResponseException("DistanceBasedDrivingCycle.Initialize: Couldn't find start gear.", retVal); - } - } - - return NextComponent.Initialize(Left.VehicleTargetSpeed, - Left.RoadGradient); - } - - public IResponse Request(Second absTime, Second dt) - { - throw new NotImplementedException("Distance Based Driving Cycle does not support time requests."); - } - - public IResponse Request(Second absTime, Meter ds) - { - if (Left.Distance.IsEqual(PreviousState.Distance.Value())) { - // we are exactly on an entry in the cycle. - var stopTime = Left.PTOActive && IdleController != null - ? Left.StoppingTime + IdleController.Duration - : Left.StoppingTime; - - if (stopTime.IsGreater(0) && PreviousState.WaitTime.IsSmaller(stopTime)) { - // stop for certain time unless we've already waited long enough ... - - // we are stopping: ensure that velocity is 0. - if (!Left.VehicleTargetSpeed.IsEqual(0)) { - Log.Warn("Stopping Time requested in cycle but target-velocity not zero. distance: {0}, target speed: {1}", - Left.StoppingTime, Left.VehicleTargetSpeed); - throw new VectoSimulationException("Stopping Time only allowed when target speed is zero!"); - } - var dt = GetStopTimeInterval(); - if (dt == null) { - CurrentState.WaitPhase++; - dt = GetStopTimeInterval(); - } - CurrentState.Response = DriveTimeInterval(absTime, dt); - return CurrentState.Response; - } - } - if (CycleIntervalIterator.LastEntry && PreviousState.Distance.IsEqual(Right.Distance)) { - CurrentState.Response = new ResponseCycleFinished(); - return CurrentState.Response; - } - - var nextSpeedChange = GetSpeedChangeWithinSimulationInterval(ds); - if (nextSpeedChange == null || ds.IsSmallerOrEqual(nextSpeedChange - PreviousState.Distance)) { - if (nextSpeedChange == null || DataBus.VehicleSpeed.IsEqual(0.SI<MeterPerSecond>())) { - CurrentState.Response = DriveDistance(absTime, ds); - return CurrentState.Response; - } - var remainingDistance = nextSpeedChange - PreviousState.Distance - ds; - var estimatedRemainingTime = remainingDistance / DataBus.VehicleSpeed; - if (_intervalProlonged || remainingDistance.IsEqual(0.SI<Meter>()) || - estimatedRemainingTime.IsGreater(Constants.SimulationSettings.LowerBoundTimeInterval)) { - CurrentState.Response = DriveDistance(absTime, ds); - return CurrentState.Response; - } - Log.Debug("Extending distance by {0} to next sample point. ds: {1} new ds: {2}", remainingDistance, ds, - nextSpeedChange - PreviousState.Distance); - _intervalProlonged = true; - CurrentState.Response = new ResponseDrivingCycleDistanceExceeded { - Source = this, - MaxDistance = nextSpeedChange - PreviousState.Distance - }; - return CurrentState.Response; - } - // only drive until next sample point in cycle with speed change - Log.Debug("Limiting distance to next sample point {0}", - Right.Distance - PreviousState.Distance); - CurrentState.Response = new ResponseDrivingCycleDistanceExceeded { - Source = this, - MaxDistance = nextSpeedChange - PreviousState.Distance - }; - return CurrentState.Response; - } - - private Second GetStopTimeInterval() - { - if (!Left.PTOActive || IdleController == null) { - return Left.StoppingTime.IsGreater(3 * Constants.SimulationSettings.TargetTimeInterval) - ? GetStopTimeIntervalThreePhases() - : Left.StoppingTime; - } - if (Left.StoppingTime.IsGreater(6 * Constants.SimulationSettings.TargetTimeInterval)) { - // 7 phases - return GetStopTimeIntervalSevenPhasesPTO(); - } - if (Left.StoppingTime.IsGreater(0)) { - // 3 phases - return GetStopTimeIntervalThreePhasesPTO(); - } - // we have a pto cycle without stopping time. - IdleController.ActivatePTO(); - return IdleController.GetNextCycleTime(); - } - - private Second GetStopTimeIntervalThreePhases() - { - switch (CurrentState.WaitPhase) { - case 1: - case 3: - CurrentState.WaitPhase++; - return Constants.SimulationSettings.TargetTimeInterval; - case 2: - CurrentState.WaitPhase++; - return Left.StoppingTime - 2 * Constants.SimulationSettings.TargetTimeInterval; - } - return null; - } - - private Second GetStopTimeIntervalThreePhasesPTO() - { - switch (CurrentState.WaitPhase) { - case 1: - case 3: - CurrentState.WaitPhase++; - IdleController.ActivateIdle(); - PTOActive = false; - return Left.StoppingTime / 2; - case 2: - PTOActive = true; - IdleController.ActivatePTO(); - return IdleController.GetNextCycleTime(); - } - return null; - } - - private Second GetStopTimeIntervalSevenPhasesPTO() - { - switch (CurrentState.WaitPhase) { - case 1: - case 5: - CurrentState.WaitPhase++; - IdleController.ActivateIdle(); - PTOActive = false; - return Constants.SimulationSettings.TargetTimeInterval; - case 3: - case 7: - CurrentState.WaitPhase++; - return Constants.SimulationSettings.TargetTimeInterval; - case 2: - case 6: - CurrentState.WaitPhase++; - return Left.StoppingTime / 2 - 2 * Constants.SimulationSettings.TargetTimeInterval; - case 4: - PTOActive = true; - IdleController.ActivatePTO(); - return IdleController.GetNextCycleTime(); - } - return null; - } - - private IResponse DriveTimeInterval(Second absTime, Second dt) - { - CurrentState.AbsTime = absTime; - CurrentState.WaitTime = PreviousState.WaitTime + dt; - CurrentState.Gradient = ComputeGradient(0.SI<Meter>()); - CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed; - - return NextComponent.Request(absTime, dt, Left.VehicleTargetSpeed, CurrentState.Gradient); - } - - private IResponse DriveDistance(Second absTime, Meter ds) - { - var nextSpeedChanges = LookAhead(Constants.SimulationSettings.BrakeNextTargetDistance); - if (nextSpeedChanges.Count > 0 && !CurrentState.RequestToNextSamplePointDone) { - CurrentState.RequestToNextSamplePointDone = true; - Log.Debug("current distance is close to the next speed change: {0}", - nextSpeedChanges.First().Distance - PreviousState.Distance); - return new ResponseDrivingCycleDistanceExceeded { - Source = this, - MaxDistance = Constants.SimulationSettings.BrakeNextTargetDistance - }; - } - - CurrentState.WaitPhase = 0; - //CurrentState.Distance = PreviousState.Distance + ds; - CurrentState.SimulationDistance = ds; - CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed; - CurrentState.Gradient = ComputeGradient(ds); - - var retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient); - retVal.Switch() - .Case<ResponseFailTimeInterval>( - r => { - retVal = NextComponent.Request(absTime, r.DeltaT, 0.SI<MeterPerSecond>(), CurrentState.Gradient); - retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient); - }); - CurrentState.AbsTime = absTime; - if (retVal is ResponseSuccess) { - CurrentState.Distance = PreviousState.Distance + retVal.SimulationDistance; - } - return retVal; - } - - protected override void DoWriteModalResults(IModalDataContainer container) - { - container[ModalResultField.dist] = CurrentState.Distance; // (CurrentState.Distance + PreviousState.Distance) / 2.0; - container[ModalResultField.simulationDistance] = CurrentState.SimulationDistance; - container[ModalResultField.v_targ] = CurrentState.VehicleTargetSpeed; - container[ModalResultField.grad] = (Math.Tan(CurrentState.Gradient.Value()) * 100).SI<Scalar>(); - container[ModalResultField.altitude] = CurrentState.Altitude; - - if (IdleController != null) { - IdleController.CommitSimulationStep(container); - } - } - - protected override void DoCommitSimulationStep() - { - if (!(CurrentState.Response is ResponseSuccess)) { - throw new VectoSimulationException("Previous request did not succeed!"); - } - - PreviousState = CurrentState; - CurrentState = CurrentState.Clone(); - _intervalProlonged = false; - - var stopTime = Left.PTOActive && IdleController != null - ? Left.StoppingTime + IdleController.Duration - : Left.StoppingTime; - - if (!stopTime.IsEqual(0) && stopTime.IsEqual(PreviousState.WaitTime)) { - // we needed to stop at the current interval in the cycle and have already waited enough time, move on.. - if (IdleController != null) { - IdleController.ActivateIdle(); - } - CycleIntervalIterator.MoveNext(); - } - - stopTime = Left.PTOActive && IdleController != null ? Left.StoppingTime + IdleController.Duration : Left.StoppingTime; - - // separately test for equality and greater than to have tolerance for equality comparison - if (stopTime.IsEqual(0)) { - while (stopTime.IsEqual(0) && CurrentState.Distance.IsGreaterOrEqual(Right.Distance) && - !CycleIntervalIterator.LastEntry) { - // we have reached the end of the current interval in the cycle, move on... - CycleIntervalIterator.MoveNext(); - - stopTime = Left.PTOActive && IdleController != null - ? Left.StoppingTime + IdleController.Duration - : Left.StoppingTime; - } - } else { - if (stopTime.IsEqual(PreviousState.WaitTime)) { - // we needed to stop at the current interval in the cycle and have already waited enough time, move on.. - if (IdleController != null) { - IdleController.ActivateIdle(); - } - CycleIntervalIterator.MoveNext(); - } - } - } - - private Radian ComputeGradient(Meter ds) - { - var cycleIterator = CycleIntervalIterator.Clone(); - while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && !cycleIterator.LastEntry) { - cycleIterator.MoveNext(); - } - var leftSamplePoint = cycleIterator.LeftSample; - var rightSamplePoint = cycleIterator.RightSample; - - if (leftSamplePoint.Distance.IsEqual(rightSamplePoint.Distance)) { - return leftSamplePoint.RoadGradient; - } - if (ds.IsEqual(0.SI<Meter>())) { - return leftSamplePoint.RoadGradient; - } - CurrentState.Altitude = VectoMath.Interpolate(leftSamplePoint.Distance, rightSamplePoint.Distance, - leftSamplePoint.Altitude, rightSamplePoint.Altitude, PreviousState.Distance + ds); - - var gradient = VectoMath.InclinationToAngle(((CurrentState.Altitude - PreviousState.Altitude) / - ds).Value()); - //return 0.SI<Radian>(); - return gradient; - } - - private Meter GetSpeedChangeWithinSimulationInterval(Meter ds) - { - var leftSamplePoint = Left; - var cycleIterator = CycleIntervalIterator.Clone(); - - do { - if (!leftSamplePoint.VehicleTargetSpeed.IsEqual(cycleIterator.RightSample.VehicleTargetSpeed)) { - return cycleIterator.RightSample.Distance; - } - } while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && cycleIterator.MoveNext()); - if (cycleIterator.LastEntry) { - return cycleIterator.RightSample.Distance; - } - return null; - } - - /// <summary> - /// Progress of the distance in the driving cycle. - /// </summary> - public double Progress - { - get { - return Data.Entries.Count > 0 - ? (CurrentState.Distance.Value() - Data.Entries.First().Distance.Value()) / - (Data.Entries.Last().Distance.Value() - Data.Entries.First().Distance.Value()) - : 0; - } - } - - public Meter CycleStartDistance { get; internal set; } - - public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Meter lookaheadDistance) - { - var retVal = new List<DrivingCycleData.DrivingCycleEntry>(); - - var cycleIterator = CycleIntervalIterator.Clone(); - var velocity = cycleIterator.LeftSample.VehicleTargetSpeed; - - do { - if (cycleIterator.RightSample.VehicleTargetSpeed.IsEqual(velocity)) { - continue; - } - var lookaheadEntry = retVal.Find(x => x.Distance == cycleIterator.RightSample.Distance); - if (lookaheadEntry != null) { - // an entry may occur twice when vehicle stops (one entry with v=0 and the other with drive on after stop) - // only use the one with min. speed - if (cycleIterator.RightSample.VehicleTargetSpeed < lookaheadEntry.VehicleTargetSpeed) { - retVal.Remove(lookaheadEntry); - retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here? - } - } else { - retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here? - } - velocity = cycleIterator.RightSample.VehicleTargetSpeed; - if (velocity.IsEqual(0.KMPHtoMeterPerSecond())) { - // do not look beyond vehicle stop - break; - } - } while (cycleIterator.MoveNext() && cycleIterator.RightSample.Distance < PreviousState.Distance + lookaheadDistance); - if (retVal.Count > 0) { - retVal = retVal.Where(x => x.Distance <= PreviousState.Distance + lookaheadDistance).ToList(); - retVal.Sort((x, y) => x.Distance.CompareTo(y.Distance)); - } - return retVal; - } - - public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Second time) - { - return LookAhead(LookaheadTimeSafetyMargin * DataBus.VehicleSpeed * time); - } - - public void FinishSimulation() - { - Data.Finish(); - } - - public CycleData CycleData - { - get { - return new CycleData { - AbsTime = CurrentState.AbsTime, - AbsDistance = CurrentState.Distance, - LeftSample = Left, - RightSample = CycleIntervalIterator.RightSample - }; - } - } - - public bool PTOActive { get; private set; } - - public DrivingCycleData.DrivingCycleEntry CycleLookAhead(Meter distance) - { - var absDistance = CurrentState.Distance + distance; - var myIterator = CycleIntervalIterator.Clone(); - - if (absDistance > Data.Entries.Last().Distance) { - return ExtrapolateCycleEntry(absDistance, Data.Entries.Last()); - } - while (myIterator.RightSample.Distance < absDistance) { - myIterator.MoveNext(); - } - - return InterpolateCycleEntry(absDistance, myIterator.RightSample); - } - - private DrivingCycleData.DrivingCycleEntry InterpolateCycleEntry(Meter absDistance, - DrivingCycleData.DrivingCycleEntry lookahead) - { - var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) { - Distance = absDistance, - Altitude = VectoMath.Interpolate(CurrentState.Distance, lookahead.Distance, CurrentState.Altitude, - lookahead.Altitude, absDistance) - }; - - retVal.RoadGradient = - ((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>(); - - return retVal; - } - - private DrivingCycleData.DrivingCycleEntry ExtrapolateCycleEntry(Meter absDistance, - DrivingCycleData.DrivingCycleEntry lookahead) - { - var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) { - Distance = absDistance, - Altitude = lookahead.Altitude + lookahead.RoadGradient * (absDistance - lookahead.Distance), - }; - - retVal.RoadGradient = - ((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>(); - - return retVal; - } - - public Meter Altitude - { - get { return PreviousState.Altitude; } - } - - public sealed class DrivingCycleState - { - public DrivingCycleState Clone() - { - return new DrivingCycleState { - AbsTime = AbsTime, - Distance = Distance, - VehicleTargetSpeed = VehicleTargetSpeed, - Altitude = Altitude, - WaitPhase = WaitPhase, - // WaitTime is not cloned on purpose! - WaitTime = 0.SI<Second>(), - Response = null - }; - } - - public Second AbsTime; - - public Meter Distance; - - public Second WaitTime; - - public uint WaitPhase; - - public MeterPerSecond VehicleTargetSpeed; - - public Meter Altitude; - - public Radian Gradient; - - public IResponse Response; - - public bool RequestToNextSamplePointDone; - - public Meter SimulationDistance; - } - - public void Dispose() - { - CycleIntervalIterator.Dispose(); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Connector.Ports; +using TUGraz.VectoCore.Models.Connector.Ports.Impl; +using TUGraz.VectoCore.Models.Simulation; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data; +using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + /// <summary> + /// Class representing one Distance Based Driving Cycle + /// </summary> + public sealed class DistanceBasedDrivingCycle : StatefulProviderComponent + <DistanceBasedDrivingCycle.DrivingCycleState, ISimulationOutPort, IDrivingCycleInPort, IDrivingCycleOutPort>, + IDrivingCycle, ISimulationOutPort, IDrivingCycleInPort, IDisposable + { + private const double LookaheadTimeSafetyMargin = 1.5; + internal readonly IDrivingCycleData Data; + internal readonly DrivingCycleEnumerator CycleIntervalIterator; + private bool _intervalProlonged; + internal IdleControllerSwitcher IdleController; + + private DrivingCycleData.DrivingCycleEntry Left + { + get { return CycleIntervalIterator.LeftSample; } + } + + private DrivingCycleData.DrivingCycleEntry Right + { + get { return CycleIntervalIterator.RightSample; } + } + + public DistanceBasedDrivingCycle(IVehicleContainer container, IDrivingCycleData cycle) : base(container) + { + Data = cycle; + CycleIntervalIterator = new DrivingCycleEnumerator(Data); + CycleStartDistance = Data.Entries.Count > 0 ? Data.Entries.First().Distance : 0.SI<Meter>(); + + var first = Data.Entries.First(); + PreviousState = new DrivingCycleState { + AbsTime = 0.SI<Second>(), + WaitTime = 0.SI<Second>(), + Distance = first.Distance, + Altitude = first.Altitude, + }; + CurrentState = PreviousState.Clone(); + } + + public IResponse Initialize() + { + if (Left.VehicleTargetSpeed.IsEqual(0)) { + var retVal = NextComponent.Initialize(DataBus.StartSpeed, + Left.RoadGradient, DataBus.StartAcceleration); + if (!(retVal is ResponseSuccess)) { + throw new UnexpectedResponseException("DistanceBasedDrivingCycle.Initialize: Couldn't find start gear.", retVal); + } + } + + return NextComponent.Initialize(Left.VehicleTargetSpeed, + Left.RoadGradient); + } + + public IResponse Request(Second absTime, Second dt) + { + throw new NotImplementedException("Distance Based Driving Cycle does not support time requests."); + } + + public IResponse Request(Second absTime, Meter ds) + { + if (Left.Distance.IsEqual(PreviousState.Distance.Value())) { + var response = DoFirstSimulationInterval(absTime); + if (response != null) { + return response; + } + } + if (CycleIntervalIterator.LastEntry && PreviousState.Distance.IsEqual(Right.Distance)) { + CurrentState.Response = new ResponseCycleFinished(); + return CurrentState.Response; + } + + var nextSpeedChange = GetSpeedChangeWithinSimulationInterval(ds); + if (nextSpeedChange == null || ds.IsSmallerOrEqual(nextSpeedChange - PreviousState.Distance)) { + if (nextSpeedChange == null || DataBus.VehicleSpeed.IsEqual(0.SI<MeterPerSecond>())) { + CurrentState.Response = DriveDistance(absTime, ds); + return CurrentState.Response; + } + var remainingDistance = nextSpeedChange - PreviousState.Distance - ds; + var estimatedRemainingTime = remainingDistance / DataBus.VehicleSpeed; + if (_intervalProlonged || remainingDistance.IsEqual(0.SI<Meter>()) || + estimatedRemainingTime.IsGreater(Constants.SimulationSettings.LowerBoundTimeInterval)) { + CurrentState.Response = DriveDistance(absTime, ds); + return CurrentState.Response; + } + Log.Debug("Extending distance by {0} to next sample point. ds: {1} new ds: {2}", remainingDistance, ds, + nextSpeedChange - PreviousState.Distance); + _intervalProlonged = true; + CurrentState.Response = new ResponseDrivingCycleDistanceExceeded { + Source = this, + MaxDistance = nextSpeedChange - PreviousState.Distance + }; + return CurrentState.Response; + } + // only drive until next sample point in cycle with speed change + Log.Debug("Limiting distance to next sample point {0}", + Right.Distance - PreviousState.Distance); + CurrentState.Response = new ResponseDrivingCycleDistanceExceeded { + Source = this, + MaxDistance = nextSpeedChange - PreviousState.Distance + }; + return CurrentState.Response; + } + + private IResponse DoFirstSimulationInterval(Second absTime) + { +// we are exactly on an entry in the cycle. + var stopTime = Left.PTOActive && IdleController != null + ? Left.StoppingTime + IdleController.Duration + : Left.StoppingTime; + + if (stopTime.IsGreater(0) && PreviousState.WaitTime.IsSmaller(stopTime)) { + // stop for certain time unless we've already waited long enough ... + + // we are stopping: ensure that velocity is 0. + if (!Left.VehicleTargetSpeed.IsEqual(0)) { + Log.Warn("Stopping Time requested in cycle but target-velocity not zero. distance: {0}, target speed: {1}", + Left.StoppingTime, Left.VehicleTargetSpeed); + throw new VectoSimulationException("Stopping Time only allowed when target speed is zero!"); + } + var dt = GetStopTimeInterval(); + if (dt == null) { + CurrentState.WaitPhase++; + dt = GetStopTimeInterval(); + } + CurrentState.Response = DriveTimeInterval(absTime, dt); + return CurrentState.Response; + } + return null; + } + + private Second GetStopTimeInterval() + { + if (!Left.PTOActive || IdleController == null) { + return Left.StoppingTime.IsGreater(3 * Constants.SimulationSettings.TargetTimeInterval) + ? GetStopTimeIntervalThreePhases() + : Left.StoppingTime; + } + if (Left.StoppingTime.IsGreater(6 * Constants.SimulationSettings.TargetTimeInterval)) { + // 7 phases + return GetStopTimeIntervalSevenPhasesPTO(); + } + if (Left.StoppingTime.IsGreater(0)) { + // 3 phases + return GetStopTimeIntervalThreePhasesPTO(); + } + // we have a pto cycle without stopping time. + IdleController.ActivatePTO(); + return IdleController.GetNextCycleTime(); + } + + private Second GetStopTimeIntervalThreePhases() + { + switch (CurrentState.WaitPhase) { + case 1: + case 3: + CurrentState.WaitPhase++; + return Constants.SimulationSettings.TargetTimeInterval; + case 2: + CurrentState.WaitPhase++; + return Left.StoppingTime - 2 * Constants.SimulationSettings.TargetTimeInterval; + } + return null; + } + + private Second GetStopTimeIntervalThreePhasesPTO() + { + switch (CurrentState.WaitPhase) { + case 1: + case 3: + CurrentState.WaitPhase++; + IdleController.ActivateIdle(); + PTOActive = false; + return Left.StoppingTime / 2; + case 2: + PTOActive = true; + IdleController.ActivatePTO(); + return IdleController.GetNextCycleTime(); + } + return null; + } + + private Second GetStopTimeIntervalSevenPhasesPTO() + { + switch (CurrentState.WaitPhase) { + case 1: + case 5: + CurrentState.WaitPhase++; + IdleController.ActivateIdle(); + PTOActive = false; + return Constants.SimulationSettings.TargetTimeInterval; + case 3: + case 7: + CurrentState.WaitPhase++; + return Constants.SimulationSettings.TargetTimeInterval; + case 2: + case 6: + CurrentState.WaitPhase++; + return Left.StoppingTime / 2 - 2 * Constants.SimulationSettings.TargetTimeInterval; + case 4: + PTOActive = true; + IdleController.ActivatePTO(); + return IdleController.GetNextCycleTime(); + } + return null; + } + + private IResponse DriveTimeInterval(Second absTime, Second dt) + { + CurrentState.AbsTime = absTime; + CurrentState.WaitTime = PreviousState.WaitTime + dt; + CurrentState.Gradient = ComputeGradient(0.SI<Meter>()); + CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed; + + return NextComponent.Request(absTime, dt, Left.VehicleTargetSpeed, CurrentState.Gradient); + } + + private IResponse DriveDistance(Second absTime, Meter ds) + { + var nextSpeedChanges = LookAhead(Constants.SimulationSettings.BrakeNextTargetDistance); + if (nextSpeedChanges.Count > 0 && !CurrentState.RequestToNextSamplePointDone) { + CurrentState.RequestToNextSamplePointDone = true; + Log.Debug("current distance is close to the next speed change: {0}", + nextSpeedChanges.First().Distance - PreviousState.Distance); + return new ResponseDrivingCycleDistanceExceeded { + Source = this, + MaxDistance = Constants.SimulationSettings.BrakeNextTargetDistance + }; + } + + CurrentState.WaitPhase = 0; + //CurrentState.Distance = PreviousState.Distance + ds; + CurrentState.SimulationDistance = ds; + CurrentState.VehicleTargetSpeed = Left.VehicleTargetSpeed; + CurrentState.Gradient = ComputeGradient(ds); + + var retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient); + retVal.Switch() + .Case<ResponseFailTimeInterval>( + r => { + retVal = NextComponent.Request(absTime, r.DeltaT, 0.SI<MeterPerSecond>(), CurrentState.Gradient); + retVal = NextComponent.Request(absTime, ds, CurrentState.VehicleTargetSpeed, CurrentState.Gradient); + }); + CurrentState.AbsTime = absTime; + if (retVal is ResponseSuccess) { + CurrentState.Distance = PreviousState.Distance + retVal.SimulationDistance; + } + return retVal; + } + + protected override void DoWriteModalResults(IModalDataContainer container) + { + container[ModalResultField.dist] = CurrentState.Distance; // (CurrentState.Distance + PreviousState.Distance) / 2.0; + container[ModalResultField.simulationDistance] = CurrentState.SimulationDistance; + container[ModalResultField.v_targ] = CurrentState.VehicleTargetSpeed; + container[ModalResultField.grad] = (Math.Tan(CurrentState.Gradient.Value()) * 100).SI<Scalar>(); + container[ModalResultField.altitude] = CurrentState.Altitude; + + if (IdleController != null) { + IdleController.CommitSimulationStep(container); + } + } + + protected override void DoCommitSimulationStep() + { + if (!(CurrentState.Response is ResponseSuccess)) { + throw new VectoSimulationException("Previous request did not succeed!"); + } + + PreviousState = CurrentState; + CurrentState = CurrentState.Clone(); + _intervalProlonged = false; + + // @@@quam TODO: duplicate code - same as below! + //var stopTime = Left.PTOActive && IdleController != null + // ? Left.StoppingTime + IdleController.Duration + // : Left.StoppingTime; + + //if (!stopTime.IsEqual(0) && stopTime.IsEqual(PreviousState.WaitTime)) { + // // we needed to stop at the current interval in the cycle and have already waited enough time, move on.. + // if (IdleController != null) { + // IdleController.ActivateIdle(); + // } + // CycleIntervalIterator.MoveNext(); + //} + + var stopTime = Left.PTOActive && IdleController != null + ? Left.StoppingTime + IdleController.Duration + : Left.StoppingTime; + + // separately test for equality and greater than to have tolerance for equality comparison + if (stopTime.IsEqual(0)) { + while (stopTime.IsEqual(0) && CurrentState.Distance.IsGreaterOrEqual(Right.Distance) && + !CycleIntervalIterator.LastEntry) { + // we have reached the end of the current interval in the cycle, move on... + CycleIntervalIterator.MoveNext(); + + stopTime = Left.PTOActive && IdleController != null + ? Left.StoppingTime + IdleController.Duration + : Left.StoppingTime; + } + } else { + if (stopTime.IsEqual(PreviousState.WaitTime)) { + // we needed to stop at the current interval in the cycle and have already waited enough time, move on.. + if (IdleController != null) { + IdleController.ActivateIdle(); + } + CycleIntervalIterator.MoveNext(); + } + } + } + + private Radian ComputeGradient(Meter ds) + { + var cycleIterator = CycleIntervalIterator.Clone(); + while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && !cycleIterator.LastEntry) { + cycleIterator.MoveNext(); + } + var leftSamplePoint = cycleIterator.LeftSample; + var rightSamplePoint = cycleIterator.RightSample; + + if (leftSamplePoint.Distance.IsEqual(rightSamplePoint.Distance)) { + return leftSamplePoint.RoadGradient; + } + if (ds.IsEqual(0.SI<Meter>())) { + return leftSamplePoint.RoadGradient; + } + CurrentState.Altitude = VectoMath.Interpolate(leftSamplePoint.Distance, rightSamplePoint.Distance, + leftSamplePoint.Altitude, rightSamplePoint.Altitude, PreviousState.Distance + ds); + + var gradient = VectoMath.InclinationToAngle(((CurrentState.Altitude - PreviousState.Altitude) / + ds).Value()); + //return 0.SI<Radian>(); + return gradient; + } + + private Meter GetSpeedChangeWithinSimulationInterval(Meter ds) + { + var leftSamplePoint = Left; + var cycleIterator = CycleIntervalIterator.Clone(); + + do { + if (!leftSamplePoint.VehicleTargetSpeed.IsEqual(cycleIterator.RightSample.VehicleTargetSpeed)) { + return cycleIterator.RightSample.Distance; + } + } while (cycleIterator.RightSample.Distance < PreviousState.Distance + ds && cycleIterator.MoveNext()); + if (cycleIterator.LastEntry) { + return cycleIterator.RightSample.Distance; + } + return null; + } + + /// <summary> + /// Progress of the distance in the driving cycle. + /// </summary> + public double Progress + { + get + { + return Data.Entries.Count > 0 + ? (CurrentState.Distance.Value() - Data.Entries.First().Distance.Value()) / + (Data.Entries.Last().Distance.Value() - Data.Entries.First().Distance.Value()) + : 0; + } + } + + public Meter CycleStartDistance { get; internal set; } + + public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Meter lookaheadDistance) + { + var retVal = new List<DrivingCycleData.DrivingCycleEntry>(); + + var cycleIterator = CycleIntervalIterator.Clone(); + var velocity = cycleIterator.LeftSample.VehicleTargetSpeed; + + do { + if (cycleIterator.RightSample.VehicleTargetSpeed.IsEqual(velocity)) { + continue; + } + var lookaheadEntry = retVal.Find(x => x.Distance == cycleIterator.RightSample.Distance); + if (lookaheadEntry != null) { + // an entry may occur twice when vehicle stops (one entry with v=0 and the other with drive on after stop) + // only use the one with min. speed + if (cycleIterator.RightSample.VehicleTargetSpeed < lookaheadEntry.VehicleTargetSpeed) { + retVal.Remove(lookaheadEntry); + retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here? + } + } else { + retVal.Add(cycleIterator.RightSample); // TODO: MQ 2016-05-13: use clone of iterator here? + } + velocity = cycleIterator.RightSample.VehicleTargetSpeed; + if (velocity.IsEqual(0.KMPHtoMeterPerSecond())) { + // do not look beyond vehicle stop + break; + } + } while (cycleIterator.MoveNext() && cycleIterator.RightSample.Distance < PreviousState.Distance + lookaheadDistance); + if (retVal.Count > 0) { + retVal = retVal.Where(x => x.Distance <= PreviousState.Distance + lookaheadDistance).ToList(); + retVal.Sort((x, y) => x.Distance.CompareTo(y.Distance)); + } + return retVal; + } + + public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Second time) + { + return LookAhead(LookaheadTimeSafetyMargin * DataBus.VehicleSpeed * time); + } + + public void FinishSimulation() + { + Data.Finish(); + } + + public CycleData CycleData + { + get + { + return new CycleData { + AbsTime = CurrentState.AbsTime, + AbsDistance = CurrentState.Distance, + LeftSample = Left, + RightSample = CycleIntervalIterator.RightSample + }; + } + } + + public bool PTOActive { get; private set; } + + public DrivingCycleData.DrivingCycleEntry CycleLookAhead(Meter distance) + { + var absDistance = CurrentState.Distance + distance; + var myIterator = CycleIntervalIterator.Clone(); + + if (absDistance > Data.Entries.Last().Distance) { + return ExtrapolateCycleEntry(absDistance, Data.Entries.Last()); + } + while (myIterator.RightSample.Distance < absDistance) { + myIterator.MoveNext(); + } + + return InterpolateCycleEntry(absDistance, myIterator.RightSample); + } + + private DrivingCycleData.DrivingCycleEntry InterpolateCycleEntry(Meter absDistance, + DrivingCycleData.DrivingCycleEntry lookahead) + { + var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) { + Distance = absDistance, + Altitude = VectoMath.Interpolate(CurrentState.Distance, lookahead.Distance, CurrentState.Altitude, + lookahead.Altitude, absDistance) + }; + + retVal.RoadGradient = + ((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>(); + + return retVal; + } + + private DrivingCycleData.DrivingCycleEntry ExtrapolateCycleEntry(Meter absDistance, + DrivingCycleData.DrivingCycleEntry lookahead) + { + var retVal = new DrivingCycleData.DrivingCycleEntry(lookahead) { + Distance = absDistance, + Altitude = lookahead.Altitude + lookahead.RoadGradient * (absDistance - lookahead.Distance), + }; + + retVal.RoadGradient = + ((retVal.Altitude - CurrentState.Altitude) / (absDistance - CurrentState.Distance)).Value().SI<Radian>(); + + return retVal; + } + + public Meter Altitude + { + get { return PreviousState.Altitude; } + } + + public sealed class DrivingCycleState + { + public DrivingCycleState Clone() + { + return new DrivingCycleState { + AbsTime = AbsTime, + Distance = Distance, + VehicleTargetSpeed = VehicleTargetSpeed, + Altitude = Altitude, + WaitPhase = WaitPhase, + // WaitTime is not cloned on purpose! + WaitTime = 0.SI<Second>(), + Response = null + }; + } + + public Second AbsTime; + + public Meter Distance; + + public Second WaitTime; + + public uint WaitPhase; + + public MeterPerSecond VehicleTargetSpeed; + + public Meter Altitude; + + public Radian Gradient; + + public IResponse Response; + + public bool RequestToNextSamplePointDone; + + public Meter SimulationDistance; + } + + public void Dispose() + { + CycleIntervalIterator.Dispose(); + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/MeasuredSpeedDrivingCycle.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/MeasuredSpeedDrivingCycle.cs index cecf32836f..424e42675a 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/MeasuredSpeedDrivingCycle.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/MeasuredSpeedDrivingCycle.cs @@ -29,337 +29,358 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Collections.Generic; -using System.Linq; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Connector.Ports; -using TUGraz.VectoCore.Models.Connector.Ports.Impl; -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.OutputData; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Impl -{ - /// <summary> - /// Driving Cycle for the Measured Speed Gear driving cycle. - /// </summary> - public class MeasuredSpeedDrivingCycle : - StatefulProviderComponent - <MeasuredSpeedDrivingCycle.DrivingCycleState, ISimulationOutPort, IDriverDemandInPort, IDriverDemandOutPort>, - IDriverInfo, IDrivingCycleInfo, IMileageCounter, IDriverDemandInProvider, IDriverDemandInPort, ISimulationOutProvider, - ISimulationOutPort - { - public class DrivingCycleState - { - public DrivingCycleState Clone() - { - return new DrivingCycleState { - Distance = Distance, - }; - } - - public Meter Distance; - public Meter SimulationDistance; - public MeterPerSquareSecond Acceleration; - } - - protected readonly IDrivingCycleData Data; - - protected internal readonly DrivingCycleEnumerator CycleIterator; - - protected Second AbsTime { get; set; } - - /// <summary> - /// Initializes a new instance of the <see cref="PowertrainDrivingCycle"/> class. - /// </summary> - /// <param name="container">The container.</param> - /// <param name="cycle">The cycle.</param> - public MeasuredSpeedDrivingCycle(IVehicleContainer container, IDrivingCycleData cycle) - : base(container) - { - Data = cycle; - CycleIterator = new DrivingCycleEnumerator(cycle); - - PreviousState = new DrivingCycleState { - Distance = 0.SI<Meter>(), - }; - CurrentState = PreviousState.Clone(); - } - - public IResponse Initialize() - { - var first = Data.Entries.First(); - - AbsTime = first.Time; - - var response = NextComponent.Initialize(first.VehicleTargetSpeed, first.RoadGradient); - if (!(response is ResponseSuccess)) { - throw new UnexpectedResponseException("MeasuredSpeedDrivingCycle: Couldn't find start gear.", response); - } - - response.AbsTime = AbsTime; - return response; - } - - public IResponse Request(Second absTime, Meter ds) - { - Log.Fatal("MeasuredSpeed Cycle can not handle distance request."); - throw new VectoSimulationException("MeasuredSpeed Cycle can not handle distance request."); - } - - public virtual IResponse Request(Second absTime, Second dt) - { - var debug = new DebugData(); - - // cycle finished - if (CycleIterator.LastEntry && absTime >= CycleIterator.RightSample.Time) { - return new ResponseCycleFinished { AbsTime = absTime, Source = this }; - } - - if (CycleIterator.RightSample == null) { - throw new VectoException("Exceeding cycle!"); - } - // interval exceeded - if ((absTime + dt).IsGreater(CycleIterator.RightSample.Time)) { - return new ResponseFailTimeInterval { - AbsTime = absTime, - Source = this, - DeltaT = CycleIterator.RightSample.Time - absTime - }; - } - - // calc acceleration from speed diff vehicle to cycle - var targetSpeed = CycleIterator.RightSample.VehicleTargetSpeed; - if (targetSpeed.IsEqual(0.KMPHtoMeterPerSecond(), 0.5.KMPHtoMeterPerSecond())) { - targetSpeed = 0.KMPHtoMeterPerSecond(); - } - var deltaV = targetSpeed - DataBus.VehicleSpeed; - var deltaT = CycleIterator.RightSample.Time - CycleIterator.LeftSample.Time; - - if (DataBus.VehicleSpeed.IsSmaller(0)) { - throw new VectoSimulationException("vehicle velocity is smaller than zero"); - } - - if (deltaT.IsSmaller(0)) { - throw new VectoSimulationException("deltaT is smaller than zero"); - } - - var acceleration = deltaV / deltaT; - var gradient = CycleIterator.LeftSample.RoadGradient; - DriverAcceleration = acceleration; - DriverBehavior = acceleration < 0 - ? DriverBehavior = DrivingBehavior.Braking - : DriverBehavior = DrivingBehavior.Driving; - if (DataBus.VehicleStopped && acceleration.IsEqual(0)) { - DriverBehavior = DrivingBehavior.Halted; - } - - IResponse response; - var responseCount = 0; - do { - response = NextComponent.Request(absTime, dt, acceleration, gradient); - debug.Add(response); - response.Switch() - .Case<ResponseGearShift>(() => response = NextComponent.Request(absTime, dt, acceleration, gradient)) - .Case<ResponseUnderload>(r => { - var acceleration1 = acceleration; - DataBus.BrakePower = SearchAlgorithm.Search(DataBus.BrakePower, r.Delta, -r.Delta, - getYValue: result => DataBus.ClutchClosed(absTime) - ? ((ResponseDryRun)result).DeltaDragLoad - : ((ResponseDryRun)result).GearboxPowerRequest, - evaluateFunction: x => { - DataBus.BrakePower = x; - return NextComponent.Request(absTime, dt, acceleration1, gradient, true); - }, - criterion: y => DataBus.ClutchClosed(absTime) - ? ((ResponseDryRun)y).DeltaDragLoad.Value() - : ((ResponseDryRun)y).GearboxPowerRequest.Value()); - Log.Info( - "Found operating point for braking. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}, BrakePower: {4}", - absTime, dt, acceleration, gradient, DataBus.BrakePower); - - if (DataBus.BrakePower.IsSmaller(0)) { - Log.Info( - "BrakePower was negative: {4}. Setting to 0 and searching for acceleration operating point. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", - absTime, dt, acceleration, gradient, DataBus.BrakePower); - DataBus.BrakePower = 0.SI<Watt>(); - acceleration = SearchAlgorithm.Search(acceleration, r.Delta, - Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, - getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, - evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), - criterion: y => ((ResponseDryRun)y).DeltaFullLoad.Value()); - } - - response = NextComponent.Request(absTime, dt, acceleration, gradient); - }) - .Case<ResponseOverload>(r => { - if (DataBus.ClutchClosed(absTime)) { - acceleration = SearchAlgorithm.Search(acceleration, r.Delta, - Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, - getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, - evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), - criterion: - y => ((ResponseDryRun)y).DeltaFullLoad.Value()); - Log.Info( - "Found operating point for driver acceleration. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", - absTime, dt, acceleration, gradient); - } else { - DataBus.BrakePower = SearchAlgorithm.Search(DataBus.BrakePower, r.Delta, -r.Delta, - getYValue: result => DataBus.ClutchClosed(absTime) - ? ((ResponseDryRun)result).DeltaDragLoad - : ((ResponseDryRun)result).GearboxPowerRequest, - evaluateFunction: x => { - DataBus.BrakePower = x; - return NextComponent.Request(absTime, dt, acceleration, gradient, true); - }, - criterion: y => DataBus.ClutchClosed(absTime) - ? ((ResponseDryRun)y).DeltaDragLoad.Value() - : ((ResponseDryRun)y).GearboxPowerRequest.Value()); - Log.Info( - "Found operating point for braking. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}, BrakePower: {4}", - absTime, dt, acceleration, gradient, DataBus.BrakePower); - - if (DataBus.BrakePower.IsSmaller(0)) { - Log.Info( - "BrakePower was negative: {4}. Setting to 0 and searching for acceleration operating point. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", - absTime, dt, acceleration, gradient, DataBus.BrakePower); - DataBus.BrakePower = 0.SI<Watt>(); - acceleration = SearchAlgorithm.Search(acceleration, r.Delta, - Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, - getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, - evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), - criterion: y => ((ResponseDryRun)y).DeltaFullLoad.Value()); - } - } - response = NextComponent.Request(absTime, dt, acceleration, gradient); - }) - .Case<ResponseEngineSpeedTooHigh>(r => { - acceleration = SearchAlgorithm.Search(acceleration, r.DeltaEngineSpeed, - Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, - getYValue: result => ((ResponseDryRun)result).DeltaEngineSpeed, - evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), - criterion: - y => ((ResponseDryRun)y).DeltaEngineSpeed.Value()); - Log.Info( - "Found operating point for driver acceleration. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", - absTime, dt, acceleration, gradient); - }) - .Case<ResponseFailTimeInterval>(r => { dt = r.DeltaT; }) - .Case<ResponseSuccess>() - .Default( - r => { throw new UnexpectedResponseException("MeasuredSpeedDrivingCycle received an unexpected response.", r); }); - } while (!(response is ResponseSuccess || response is ResponseFailTimeInterval) && (++responseCount < 10)); - - AbsTime = absTime + dt; - - response.SimulationInterval = dt; - response.Acceleration = acceleration; - debug.Add(response); - - CurrentState.SimulationDistance = acceleration / 2 * dt * dt + DataBus.VehicleSpeed * dt; - if (CurrentState.SimulationDistance.IsSmaller(0)) { - throw new VectoSimulationException( - "MeasuredSpeed: Simulation Distance must not be negative. Driving Backward is not allowed."); - } - - CurrentState.Distance = CurrentState.SimulationDistance + PreviousState.Distance; - CurrentState.Acceleration = acceleration; - - return response; - } - - protected override void DoWriteModalResults(IModalDataContainer container) - { - container[ModalResultField.dist] = CurrentState.Distance; - container[ModalResultField.simulationDistance] = CurrentState.SimulationDistance; - container[ModalResultField.v_targ] = CycleIterator.LeftSample.VehicleTargetSpeed; - container[ModalResultField.grad] = CycleIterator.LeftSample.RoadGradientPercent; - container[ModalResultField.altitude] = CycleIterator.LeftSample.Altitude; - container[ModalResultField.acc] = CurrentState.Acceleration; - } - - protected override void DoCommitSimulationStep() - { - if ((CycleIterator.RightSample == null) || AbsTime.IsGreaterOrEqual(CycleIterator.RightSample.Time)) { - CycleIterator.MoveNext(); - } - AdvanceState(); - } - - public double Progress - { - get { return AbsTime == null ? 0 : AbsTime.Value() / Data.Entries.Last().Time.Value(); } - } - - public CycleData CycleData - { - get { - return new CycleData { - AbsTime = CycleIterator.LeftSample.Time, - AbsDistance = null, - LeftSample = CycleIterator.LeftSample, - RightSample = CycleIterator.RightSample, - }; - } - } - - public bool PTOActive - { - get { return false; } - } - - public DrivingCycleData.DrivingCycleEntry CycleLookAhead(Meter distance) - { - return new DrivingCycleData.DrivingCycleEntry(CycleIterator.RightSample); - //throw new System.NotImplementedException(); - } - - public Meter Altitude - { - get { return CycleIterator.LeftSample.Altitude; } - } - - public Meter CycleStartDistance - { - get { return 0.SI<Meter>(); } - } - - public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Meter lookaheadDistance) - { - throw new NotImplementedException(); - } - - public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Second time) - { - var retVal = new List<DrivingCycleData.DrivingCycleEntry>(); - - var iterator = CycleIterator.Clone(); - do { - retVal.Add(iterator.RightSample); - } while (iterator.MoveNext() && iterator.RightSample.Time < AbsTime + time); - - return retVal; - } - - public void FinishSimulation() - { - Data.Finish(); - } - - public DrivingBehavior DriverBehavior { get; internal set; } - - public MeterPerSquareSecond DriverAcceleration { get; protected set; } - - public Meter Distance - { - get { return CurrentState.Distance; } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Connector.Ports; +using TUGraz.VectoCore.Models.Connector.Ports.Impl; +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.OutputData; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Impl +{ + /// <summary> + /// Driving Cycle for the Measured Speed Gear driving cycle. + /// </summary> + public class MeasuredSpeedDrivingCycle : + StatefulProviderComponent + <MeasuredSpeedDrivingCycle.DrivingCycleState, ISimulationOutPort, IDriverDemandInPort, IDriverDemandOutPort>, + IDriverInfo, IDrivingCycleInfo, IMileageCounter, IDriverDemandInProvider, IDriverDemandInPort, ISimulationOutProvider, + ISimulationOutPort + { + public class DrivingCycleState + { + public DrivingCycleState Clone() + { + return new DrivingCycleState { + Distance = Distance, + }; + } + + public Meter Distance; + public Meter SimulationDistance; + public MeterPerSquareSecond Acceleration; + } + + protected readonly IDrivingCycleData Data; + + protected internal readonly DrivingCycleEnumerator CycleIterator; + + protected Second AbsTime { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="PowertrainDrivingCycle"/> class. + /// </summary> + /// <param name="container">The container.</param> + /// <param name="cycle">The cycle.</param> + public MeasuredSpeedDrivingCycle(IVehicleContainer container, IDrivingCycleData cycle) + : base(container) + { + Data = cycle; + CycleIterator = new DrivingCycleEnumerator(cycle); + + PreviousState = new DrivingCycleState { + Distance = 0.SI<Meter>(), + }; + CurrentState = PreviousState.Clone(); + } + + public IResponse Initialize() + { + var first = Data.Entries.First(); + + AbsTime = first.Time; + + var response = NextComponent.Initialize(first.VehicleTargetSpeed, first.RoadGradient); + if (!(response is ResponseSuccess)) { + throw new UnexpectedResponseException("MeasuredSpeedDrivingCycle: Couldn't find start gear.", response); + } + + response.AbsTime = AbsTime; + return response; + } + + public IResponse Request(Second absTime, Meter ds) + { + Log.Fatal("MeasuredSpeed Cycle can not handle distance request."); + throw new VectoSimulationException("MeasuredSpeed Cycle can not handle distance request."); + } + + public virtual IResponse Request(Second absTime, Second dt) + { + var debug = new DebugData(); + + // cycle finished + if (CycleIterator.LastEntry && absTime >= CycleIterator.RightSample.Time) { + return new ResponseCycleFinished { AbsTime = absTime, Source = this }; + } + + if (CycleIterator.RightSample == null) { + throw new VectoException("Exceeding cycle!"); + } + // interval exceeded + if ((absTime + dt).IsGreater(CycleIterator.RightSample.Time)) { + return new ResponseFailTimeInterval { + AbsTime = absTime, + Source = this, + DeltaT = CycleIterator.RightSample.Time - absTime + }; + } + + // calc acceleration from speed diff vehicle to cycle + var targetSpeed = CycleIterator.RightSample.VehicleTargetSpeed; + if (targetSpeed.IsEqual(0.KMPHtoMeterPerSecond(), 0.5.KMPHtoMeterPerSecond())) { + targetSpeed = 0.KMPHtoMeterPerSecond(); + } + var deltaV = targetSpeed - DataBus.VehicleSpeed; + var deltaT = CycleIterator.RightSample.Time - CycleIterator.LeftSample.Time; + + if (DataBus.VehicleSpeed.IsSmaller(0)) { + throw new VectoSimulationException("vehicle velocity is smaller than zero"); + } + + if (deltaT.IsSmaller(0)) { + throw new VectoSimulationException("deltaT is smaller than zero"); + } + + var acceleration = deltaV / deltaT; + var gradient = CycleIterator.LeftSample.RoadGradient; + DriverAcceleration = acceleration; + DriverBehavior = acceleration < 0 + ? DriverBehavior = DrivingBehavior.Braking + : DriverBehavior = DrivingBehavior.Driving; + if (DataBus.VehicleStopped && acceleration.IsEqual(0)) { + DriverBehavior = DrivingBehavior.Halted; + } + + IResponse response; + var responseCount = 0; + do { + response = NextComponent.Request(absTime, dt, acceleration, gradient); + debug.Add(response); + response.Switch() + .Case<ResponseGearShift>(() => response = NextComponent.Request(absTime, dt, acceleration, gradient)) + .Case<ResponseUnderload>(r => { + response = HandleUnderload(absTime, dt, r, gradient, ref acceleration); + }) + .Case<ResponseOverload>(r => { + response = HandleOverload(absTime, dt, r, gradient, ref acceleration); + }) + .Case<ResponseEngineSpeedTooHigh>(r => { + acceleration = SearchAlgorithm.Search(acceleration, r.DeltaEngineSpeed, + Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, + getYValue: result => ((ResponseDryRun)result).DeltaEngineSpeed, + evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), + criterion: + y => ((ResponseDryRun)y).DeltaEngineSpeed.Value()); + Log.Info( + "Found operating point for driver acceleration. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", + absTime, dt, acceleration, gradient); + }) + .Case<ResponseFailTimeInterval>(r => { + dt = r.DeltaT; + }) + .Case<ResponseSuccess>() + .Default( + r => { + throw new UnexpectedResponseException("MeasuredSpeedDrivingCycle received an unexpected response.", r); + }); + } while (!(response is ResponseSuccess || response is ResponseFailTimeInterval) && (++responseCount < 10)); + + AbsTime = absTime + dt; + + response.SimulationInterval = dt; + response.Acceleration = acceleration; + debug.Add(response); + + CurrentState.SimulationDistance = acceleration / 2 * dt * dt + DataBus.VehicleSpeed * dt; + if (CurrentState.SimulationDistance.IsSmaller(0)) { + throw new VectoSimulationException( + "MeasuredSpeed: Simulation Distance must not be negative. Driving Backward is not allowed."); + } + + CurrentState.Distance = CurrentState.SimulationDistance + PreviousState.Distance; + CurrentState.Acceleration = acceleration; + + return response; + } + + private IResponse HandleUnderload(Second absTime, Second dt, ResponseUnderload r, + Radian gradient, ref MeterPerSquareSecond acceleration) + { + MeterPerSquareSecond acc = acceleration; + DataBus.BrakePower = SearchAlgorithm.Search(DataBus.BrakePower, r.Delta, -r.Delta, + getYValue: result => DataBus.ClutchClosed(absTime) + ? ((ResponseDryRun)result).DeltaDragLoad + : ((ResponseDryRun)result).GearboxPowerRequest, + evaluateFunction: x => { + DataBus.BrakePower = x; + return NextComponent.Request(absTime, dt, acc, gradient, true); + }, + criterion: y => DataBus.ClutchClosed(absTime) + ? ((ResponseDryRun)y).DeltaDragLoad.Value() + : ((ResponseDryRun)y).GearboxPowerRequest.Value()); + Log.Info( + "Found operating point for braking. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}, BrakePower: {4}", + absTime, dt, acceleration, gradient, DataBus.BrakePower); + + if (DataBus.BrakePower.IsSmaller(0)) { + Log.Info( + "BrakePower was negative: {4}. Setting to 0 and searching for acceleration operating point. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", + absTime, dt, acceleration, gradient, DataBus.BrakePower); + DataBus.BrakePower = 0.SI<Watt>(); + acceleration = SearchAlgorithm.Search(acceleration, r.Delta, + Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, + getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, + evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), + criterion: y => ((ResponseDryRun)y).DeltaFullLoad.Value()); + } + + var response = NextComponent.Request(absTime, dt, acceleration, gradient); + return response; + } + + private IResponse HandleOverload(Second absTime, Second dt, ResponseOverload r, Radian gradient, + ref MeterPerSquareSecond acceleration) + { + IResponse response; + if (DataBus.ClutchClosed(absTime)) { + acceleration = SearchAlgorithm.Search(acceleration, r.Delta, + Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, + getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, + evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), + criterion: + y => ((ResponseDryRun)y).DeltaFullLoad.Value()); + Log.Info( + "Found operating point for driver acceleration. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", + absTime, dt, acceleration, gradient); + } else { + var acc = acceleration; + DataBus.BrakePower = SearchAlgorithm.Search(DataBus.BrakePower, r.Delta, -r.Delta, + getYValue: result => DataBus.ClutchClosed(absTime) + ? ((ResponseDryRun)result).DeltaDragLoad + : ((ResponseDryRun)result).GearboxPowerRequest, + evaluateFunction: x => { + DataBus.BrakePower = x; + return NextComponent.Request(absTime, dt, acc, gradient, true); + }, + criterion: y => DataBus.ClutchClosed(absTime) + ? ((ResponseDryRun)y).DeltaDragLoad.Value() + : ((ResponseDryRun)y).GearboxPowerRequest.Value()); + Log.Info( + "Found operating point for braking. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}, BrakePower: {4}", + absTime, dt, acceleration, gradient, DataBus.BrakePower); + + if (DataBus.BrakePower.IsSmaller(0)) { + Log.Info( + "BrakePower was negative: {4}. Setting to 0 and searching for acceleration operating point. absTime: {0}, dt: {1}, acceleration: {2}, gradient: {3}", + absTime, dt, acceleration, gradient, DataBus.BrakePower); + DataBus.BrakePower = 0.SI<Watt>(); + acceleration = SearchAlgorithm.Search(acceleration, r.Delta, + Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating, + getYValue: result => ((ResponseDryRun)result).DeltaFullLoad, + evaluateFunction: x => NextComponent.Request(absTime, dt, x, gradient, true), + criterion: y => ((ResponseDryRun)y).DeltaFullLoad.Value()); + } + } + response = NextComponent.Request(absTime, dt, acceleration, gradient); + return response; + } + + protected override void DoWriteModalResults(IModalDataContainer container) + { + container[ModalResultField.dist] = CurrentState.Distance; + container[ModalResultField.simulationDistance] = CurrentState.SimulationDistance; + container[ModalResultField.v_targ] = CycleIterator.LeftSample.VehicleTargetSpeed; + container[ModalResultField.grad] = CycleIterator.LeftSample.RoadGradientPercent; + container[ModalResultField.altitude] = CycleIterator.LeftSample.Altitude; + container[ModalResultField.acc] = CurrentState.Acceleration; + } + + protected override void DoCommitSimulationStep() + { + if ((CycleIterator.RightSample == null) || AbsTime.IsGreaterOrEqual(CycleIterator.RightSample.Time)) { + CycleIterator.MoveNext(); + } + AdvanceState(); + } + + public double Progress + { + get { return AbsTime == null ? 0 : AbsTime.Value() / Data.Entries.Last().Time.Value(); } + } + + public CycleData CycleData + { + get + { + return new CycleData { + AbsTime = CycleIterator.LeftSample.Time, + AbsDistance = null, + LeftSample = CycleIterator.LeftSample, + RightSample = CycleIterator.RightSample, + }; + } + } + + public bool PTOActive + { + get { return false; } + } + + public DrivingCycleData.DrivingCycleEntry CycleLookAhead(Meter distance) + { + return new DrivingCycleData.DrivingCycleEntry(CycleIterator.RightSample); + //throw new System.NotImplementedException(); + } + + public Meter Altitude + { + get { return CycleIterator.LeftSample.Altitude; } + } + + public Meter CycleStartDistance + { + get { return 0.SI<Meter>(); } + } + + public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Meter lookaheadDistance) + { + throw new NotImplementedException(); + } + + public IReadOnlyList<DrivingCycleData.DrivingCycleEntry> LookAhead(Second time) + { + var retVal = new List<DrivingCycleData.DrivingCycleEntry>(); + + var iterator = CycleIterator.Clone(); + do { + retVal.Add(iterator.RightSample); + } while (iterator.MoveNext() && iterator.RightSample.Time < AbsTime + time); + + return retVal; + } + + public void FinishSimulation() + { + Data.Finish(); + } + + public DrivingBehavior DriverBehavior { get; internal set; } + + public MeterPerSquareSecond DriverAcceleration { get; protected set; } + + public Meter Distance + { + get { return CurrentState.Distance; } + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs b/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs index 38be1b2c9d..f539a4a6bd 100644 --- a/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs +++ b/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs @@ -29,346 +29,342 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Data; -using System.Linq; -using System.Runtime.CompilerServices; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; -using TUGraz.VectoCore.Configuration; -using TUGraz.VectoCore.Models.Declaration; -using TUGraz.VectoCore.Models.Simulation.Data; -using TUGraz.VectoCore.Models.SimulationComponent.Data; - -// ReSharper disable MemberCanBePrivate.Global -- used by API! - -namespace TUGraz.VectoCore.OutputData -{ - public delegate void WriteSumData(IModalDataContainer data); - - /// <summary> - /// Class for the sum file in vecto. - /// </summary> - public class SummaryDataContainer : LoggingObject, IDisposable - { - // ReSharper disable InconsistentNaming - public const string INTERNAL_PREFIX = "INTERNAL"; - public const string SORT = INTERNAL_PREFIX + " Sorting"; - public const string JOB = "Job [-]"; - public const string INPUTFILE = "Input File [-]"; - public const string CYCLE = "Cycle [-]"; - public const string STATUS = "Status"; - public const string CURB_MASS = "Chassis curb mass [kg]"; - public const string LOADING = "Loading [kg]"; - - public const string VEHICLE_MANUFACTURER = "Vehicle manufacturer [-]"; - public const string VIN_NUMBER = "VIN number"; - public const string VEHICLE_MODEL = "Vehicle model [-]"; - - public const string ENGINE_MANUFACTURER = "Engine manufacturer [-]"; - public const string ENGINE_MODEL = "Engine model [-]"; - public const string ENGINE_FUEL_TYPE = "Engine fuel type [-]"; - public const string ENGINE_WHTC_URBAN = "Engine WHTCUrban"; - public const string ENGINE_WHTC_RURAL = "Engine WHTCRural"; - public const string ENGINE_WHTC_MOTORWAY = "Engine WHTCMotorway"; - public const string ENGINE_BF_COLD_HOT = "Engine BFColdHot"; - public const string ENGINE_CF_REG_PER = "Engine CFRegPer"; - public const string ENGINE_ACTUAL_CORRECTION_FACTOR = "Engine actual CF"; - public const string ENGINE_RATED_POWER = "Engine rated power [kW]"; - public const string ENGINE_IDLING_SPEED = "Engine idling speed [rpm]"; - public const string ENGINE_RATED_SPEED = "Engine rated speed [rpm]"; - public const string ENGINE_DISPLACEMENT = "Engine displacement [ccm]"; - - public const string ROLLING_RESISTANCE_COEFFICIENT_W_TRAILER = "total RRC [-]"; - public const string ROLLING_RESISTANCE_COEFFICIENT_WO_TRAILER = "weighted RRC w/o trailer [-]"; - - public const string GEARBOX_MANUFACTURER = "Gearbox manufacturer [-]"; - public const string GEARBOX_MODEL = "Gearbox model [-]"; - public const string GEARBOX_TYPE = "Gearbox type [-]"; - public const string GEAR_RATIO_FIRST_GEAR = "Gear ratio first gear [-]"; - public const string GEAR_RATIO_LAST_GEAR = "Gear ratio last gear [-]"; - - public const string TORQUECONVERTER_MANUFACTURER = "Torque converter manufacturer [-]"; - public const string TORQUECONVERTER_MODEL = "Torque converter model [-]"; - - public const string RETARDER_MANUFACTURER = "Retarder manufacturer [-]"; - public const string RETARDER_MODEL = "Retarder model [-]"; - public const string RETARDER_TYPE = "Retarder type [-]"; - - public const string ANGLEDRIVE_MANUFACTURER = "Angledrive manufacturer [-]"; - public const string ANGLEDRIVE_MODEL = "Angledrive model [-]"; - public const string ANGLEDRIVE_RATIO = "Angledrive ratio [-]"; - - public const string AXLE_MANUFACTURER = "Axle manufacturer [-]"; - public const string AXLE_MODEL = "Axle model [-]"; - public const string AXLE_RATIO = "Axle gear ratio [-]"; - - public const string AUX_TECH_FORMAT = "Auxiliary technology {0} [-]"; - - public const string HDV_CO2_VEHICLE_CLASS = "HDV CO2 vehicle class [-]"; - public const string TOTAL_VEHICLE_MASS = "Total vehicle mass [kg]"; - public const string CD_x_A = "CdxA [m²]"; - //public const string ROLLING_RESISTANCE_COEFFICIENT = "weighed RRC [-]"; - public const string R_DYN = "r_dyn [m]"; - - public const string CARGO_VOLUME = "Cargo Volume [m³]"; - public const string TIME = "time [s]"; - public const string DISTANCE = "distance [km]"; - public const string SPEED = "speed [km/h]"; - public const string ALTITUDE_DELTA = "altitudeDelta [m]"; - - public const string FCMAP_H = "FC-Map [g/h]"; - public const string FCMAP_KM = "FC-Map [g/km]"; - public const string FCAUXC_H = "FC-AUXc [g/h]"; - public const string FCAUXC_KM = "FC-AUXc [g/km]"; - public const string FCWHTCC_H = "FC-WHTCc [g/h]"; - public const string FCWHTCC_KM = "FC-WHTCc [g/km]"; - public const string FCAAUX_H = "FC-AAUX [g/h]"; - public const string FCAAUX_KM = "FC-AAUX [g/km]"; - - public const string FCFINAL_H = "FC-Final [g/h]"; - public const string FCFINAL_KM = "FC-Final [g/km]"; - public const string FCFINAL_LITERPER100KM = "FC-Final [l/100km]"; - public const string FCFINAL_LITERPER100TKM = "FC-Final [l/100tkm]"; - public const string FCFINAL_LiterPer100M3KM = "FC-Final [l/100m³km]"; - - public const string CO2_KM = "CO2 [g/km]"; - public const string CO2_TKM = "CO2 [g/tkm]"; - public const string CO2_M3KM = "CO2 [g/m³km]"; - - public const string P_WHEEL_POS = "P_wheel_in_pos [kW]"; - public const string P_FCMAP_POS = "P_fcmap_pos [kW]"; - - public const string E_FORMAT = "E_{0} [kWh]"; - public const string E_AUX_FORMAT = "E_aux_{0} [kWh]"; - public const string E_AUX = "E_aux_sum [kWh]"; - - public const string E_AIR = "E_air [kWh]"; - public const string E_ROLL = "E_roll [kWh]"; - public const string E_GRAD = "E_grad [kWh]"; - public const string E_VEHICLE_INERTIA = "E_vehi_inertia [kWh]"; - public const string E_POWERTRAIN_INERTIA = "E_powertrain_inertia [kWh]"; - public const string E_BRAKE = "E_brake [kWh]"; - public const string E_GBX_LOSS = "E_gbx_loss [kWh]"; - public const string E_SHIFT_LOSS = "E_shift_loss [kWh]"; - public const string E_AXL_LOSS = "E_axl_loss [kWh]"; - public const string E_RET_LOSS = "E_ret_loss [kWh]"; - public const string E_TC_LOSS = "E_tc_loss [kWh]"; - public const string E_ANGLE_LOSS = "E_angle_loss [kWh]"; - public const string E_CLUTCH_LOSS = "E_clutch_loss [kWh]"; - public const string E_FCMAP_POS = "E_fcmap_pos [kWh]"; - public const string E_FCMAP_NEG = "E_fcmap_neg [kWh]"; - - public const string ACC = "a [m/s^2]"; - public const string ACC_POS = "a_pos [m/s^2]"; - public const string ACC_NEG = "a_neg [m/s^2]"; - - public const string ACC_TIMESHARE = "AccelerationTimeShare [%]"; - public const string DEC_TIMESHARE = "DecelerationTimeShare [%]"; - public const string CRUISE_TIMESHARE = "CruiseTimeShare [%]"; - public const string STOP_TIMESHARE = "StopTimeShare [%]"; - - public const string MAX_SPEED = "max. speed [km/h"; - public const string MAX_ACCELERATION = "max. acc [m/s²]"; - public const string MAX_DECELERATION = "max. dec [m/s²]"; - public const string AVG_ENGINE_SPEED = "n_eng_avg [rpm]"; - public const string MAX_ENGINE_SPEED = "n_eng_max [rpm]"; - public const string NUM_GEARSHIFTS = "gear shifts [-]"; - public const string ENGINE_FULL_LOAD_TIME_SHARE = "Engine max. Load time share [%]"; - public const string COASTING_TIME_SHARE = "CoastingTimeShare [%]"; - public const string BRAKING_TIME_SHARE = "BrakingTImeShare [%]"; - - public const string TIME_SHARE_PER_GEAR_FORMAT = "Gear {0} TimeShare [%]"; - - // ReSharper restore InconsistentNaming - - internal readonly DataTable Table; - private readonly ISummaryWriter _sumWriter; - - - protected SummaryDataContainer() {} - - /// <summary> - /// Initializes a new instance of the <see cref="SummaryDataContainer"/> class. - /// </summary> - /// <param name="writer"></param> - public SummaryDataContainer(ISummaryWriter writer) - { - _sumWriter = writer; - - Table = new DataTable(); - - Table.Columns.AddRange(new[] { - Tuple.Create(SORT, typeof(int)), - Tuple.Create(JOB, typeof(string)), - Tuple.Create(INPUTFILE, typeof(string)), - Tuple.Create(CYCLE, typeof(string)), - Tuple.Create(STATUS, typeof(string)), - Tuple.Create(VEHICLE_MANUFACTURER, typeof(string)), - Tuple.Create(VIN_NUMBER, typeof(string)), - Tuple.Create(VEHICLE_MODEL, typeof(string)), - Tuple.Create(HDV_CO2_VEHICLE_CLASS, typeof(string)), - Tuple.Create(CURB_MASS, typeof(SI)), - Tuple.Create(LOADING, typeof(SI)), - Tuple.Create(TOTAL_VEHICLE_MASS, typeof(SI)), - Tuple.Create(ENGINE_MANUFACTURER, typeof(string)), - Tuple.Create(ENGINE_MODEL, typeof(string)), - Tuple.Create(ENGINE_FUEL_TYPE, typeof(string)), - Tuple.Create(ENGINE_RATED_POWER, typeof(SI)), - Tuple.Create(ENGINE_IDLING_SPEED, typeof(SI)), - Tuple.Create(ENGINE_RATED_SPEED, typeof(SI)), - Tuple.Create(ENGINE_DISPLACEMENT, typeof(SI)), - Tuple.Create(ENGINE_WHTC_URBAN, typeof(double)), - Tuple.Create(ENGINE_WHTC_RURAL, typeof(double)), - Tuple.Create(ENGINE_WHTC_MOTORWAY, typeof(double)), - Tuple.Create(ENGINE_BF_COLD_HOT, typeof(double)), - Tuple.Create(ENGINE_CF_REG_PER, typeof(double)), - Tuple.Create(ENGINE_ACTUAL_CORRECTION_FACTOR, typeof(double)), - Tuple.Create(CD_x_A, typeof(SI)), - Tuple.Create(ROLLING_RESISTANCE_COEFFICIENT_W_TRAILER, typeof(double)), - Tuple.Create(ROLLING_RESISTANCE_COEFFICIENT_WO_TRAILER, typeof(double)), - Tuple.Create(R_DYN, typeof(SI)), - Tuple.Create(GEARBOX_MANUFACTURER, typeof(string)), - Tuple.Create(GEARBOX_MODEL, typeof(string)), - Tuple.Create(GEARBOX_TYPE, typeof(string)), - Tuple.Create(GEAR_RATIO_FIRST_GEAR, typeof(SI)), - Tuple.Create(GEAR_RATIO_LAST_GEAR, typeof(SI)), - Tuple.Create(TORQUECONVERTER_MANUFACTURER, typeof(string)), - Tuple.Create(TORQUECONVERTER_MODEL, typeof(string)), - Tuple.Create(RETARDER_MANUFACTURER, typeof(string)), - Tuple.Create(RETARDER_MODEL, typeof(string)), - Tuple.Create(RETARDER_TYPE, typeof(string)), - Tuple.Create(ANGLEDRIVE_MANUFACTURER, typeof(string)), - Tuple.Create(ANGLEDRIVE_MODEL, typeof(string)), - Tuple.Create(ANGLEDRIVE_RATIO, typeof(string)), - Tuple.Create(AXLE_MANUFACTURER, typeof(string)), - Tuple.Create(AXLE_MODEL, typeof(string)), - Tuple.Create(AXLE_RATIO, typeof(SI)), - Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.SteeringPump), typeof(string)), - Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.Fan), typeof(string)), - Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.HeatingVentilationAirCondition), - typeof(string)), - Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.PneumaticSystem), typeof(string)), - Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.ElectricSystem), typeof(string)), - }.Select(x => new DataColumn(x.Item1, x.Item2)).ToArray()); - - Table.Columns.AddRange(new[] { - CARGO_VOLUME, - TIME, DISTANCE, - SPEED, ALTITUDE_DELTA, - FCMAP_H, FCMAP_KM, - FCAUXC_H, FCAUXC_KM, - FCWHTCC_H, FCWHTCC_KM, - FCAAUX_H, FCAAUX_KM, - FCFINAL_H, FCFINAL_KM, - FCFINAL_LITERPER100KM, FCFINAL_LITERPER100TKM, FCFINAL_LiterPer100M3KM, - CO2_KM, CO2_TKM, CO2_M3KM, - P_WHEEL_POS, P_FCMAP_POS, - E_FCMAP_POS, E_FCMAP_NEG, E_POWERTRAIN_INERTIA, - E_AUX, E_CLUTCH_LOSS, E_TC_LOSS, E_SHIFT_LOSS, E_GBX_LOSS, - E_RET_LOSS, E_ANGLE_LOSS, E_AXL_LOSS, E_BRAKE, E_VEHICLE_INERTIA, E_AIR, E_ROLL, E_GRAD, - ACC, ACC_POS, ACC_NEG, ACC_TIMESHARE, DEC_TIMESHARE, CRUISE_TIMESHARE, STOP_TIMESHARE, - MAX_SPEED, MAX_ACCELERATION, MAX_DECELERATION, AVG_ENGINE_SPEED, MAX_ENGINE_SPEED, NUM_GEARSHIFTS, - ENGINE_FULL_LOAD_TIME_SHARE, COASTING_TIME_SHARE, BRAKING_TIME_SHARE - }.Select(x => new DataColumn(x, typeof(SI))).ToArray()); - } - - /// <summary> - /// Finishes the summary data container (writes the data to the sumWriter). - /// </summary> - public virtual void Finish() - { - if (_sumWriter != null) { - var view = new DataView(Table, "", SORT, DataViewRowState.CurrentRows).ToTable(); - var toRemove = - view.Columns.Cast<DataColumn>().Where(column => column.ColumnName.StartsWith(INTERNAL_PREFIX)).ToList(); - foreach (var dataColumn in toRemove) { - view.Columns.Remove(dataColumn); - } - _sumWriter.WriteSumData(view); - } - } - - /// <summary> - /// Writes the result of one run into the summary data container. - /// </summary> - [MethodImpl(MethodImplOptions.Synchronized)] - //public virtual void Write(IModalDataContainer modData, string jobFileName, string jobName, string cycleFileName, - // Kilogram vehicleMass, Kilogram vehicleLoading, CubicMeter cargoVolume, uint gearCount) - public virtual void Write(IModalDataContainer modData, int jobNr, int runNr, VectoRunData runData) - { - var row = Table.NewRow(); - Table.Rows.Add(row); - - row[SORT] = jobNr * 1000 + runNr; - row[JOB] = string.Format("{0}-{1}", jobNr, runNr); //ReplaceNotAllowedCharacters(current); - row[INPUTFILE] = ReplaceNotAllowedCharacters(runData.JobName); - row[CYCLE] = ReplaceNotAllowedCharacters(runData.Cycle.Name + Constants.FileExtensions.CycleFile); - - row[STATUS] = modData.RunStatus; - - var vehicleLoading = 0.SI<Kilogram>(); - var cargoVolume = 0.SI<CubicMeter>(); - var gearCount = 0u; - if (runData.Cycle.CycleType != CycleType.EngineOnly) { +using System; +using System.Data; +using System.Linq; +using System.Runtime.CompilerServices; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; +using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Declaration; +using TUGraz.VectoCore.Models.Simulation.Data; +using TUGraz.VectoCore.Models.SimulationComponent.Data; + +// ReSharper disable MemberCanBePrivate.Global -- used by API! + +namespace TUGraz.VectoCore.OutputData +{ + public delegate void WriteSumData(IModalDataContainer data); + + /// <summary> + /// Class for the sum file in vecto. + /// </summary> + public class SummaryDataContainer : LoggingObject, IDisposable + { + // ReSharper disable InconsistentNaming + public const string INTERNAL_PREFIX = "INTERNAL"; + public const string SORT = INTERNAL_PREFIX + " Sorting"; + public const string JOB = "Job [-]"; + public const string INPUTFILE = "Input File [-]"; + public const string CYCLE = "Cycle [-]"; + public const string STATUS = "Status"; + public const string CURB_MASS = "Chassis curb mass [kg]"; + public const string LOADING = "Loading [kg]"; + + public const string VEHICLE_MANUFACTURER = "Vehicle manufacturer [-]"; + public const string VIN_NUMBER = "VIN number"; + public const string VEHICLE_MODEL = "Vehicle model [-]"; + + public const string ENGINE_MANUFACTURER = "Engine manufacturer [-]"; + public const string ENGINE_MODEL = "Engine model [-]"; + public const string ENGINE_FUEL_TYPE = "Engine fuel type [-]"; + public const string ENGINE_WHTC_URBAN = "Engine WHTCUrban"; + public const string ENGINE_WHTC_RURAL = "Engine WHTCRural"; + public const string ENGINE_WHTC_MOTORWAY = "Engine WHTCMotorway"; + public const string ENGINE_BF_COLD_HOT = "Engine BFColdHot"; + public const string ENGINE_CF_REG_PER = "Engine CFRegPer"; + public const string ENGINE_ACTUAL_CORRECTION_FACTOR = "Engine actual CF"; + public const string ENGINE_RATED_POWER = "Engine rated power [kW]"; + public const string ENGINE_IDLING_SPEED = "Engine idling speed [rpm]"; + public const string ENGINE_RATED_SPEED = "Engine rated speed [rpm]"; + public const string ENGINE_DISPLACEMENT = "Engine displacement [ccm]"; + + public const string ROLLING_RESISTANCE_COEFFICIENT_W_TRAILER = "total RRC [-]"; + public const string ROLLING_RESISTANCE_COEFFICIENT_WO_TRAILER = "weighted RRC w/o trailer [-]"; + + public const string GEARBOX_MANUFACTURER = "Gearbox manufacturer [-]"; + public const string GEARBOX_MODEL = "Gearbox model [-]"; + public const string GEARBOX_TYPE = "Gearbox type [-]"; + public const string GEAR_RATIO_FIRST_GEAR = "Gear ratio first gear [-]"; + public const string GEAR_RATIO_LAST_GEAR = "Gear ratio last gear [-]"; + + public const string TORQUECONVERTER_MANUFACTURER = "Torque converter manufacturer [-]"; + public const string TORQUECONVERTER_MODEL = "Torque converter model [-]"; + + public const string RETARDER_MANUFACTURER = "Retarder manufacturer [-]"; + public const string RETARDER_MODEL = "Retarder model [-]"; + public const string RETARDER_TYPE = "Retarder type [-]"; + + public const string ANGLEDRIVE_MANUFACTURER = "Angledrive manufacturer [-]"; + public const string ANGLEDRIVE_MODEL = "Angledrive model [-]"; + public const string ANGLEDRIVE_RATIO = "Angledrive ratio [-]"; + + public const string AXLE_MANUFACTURER = "Axle manufacturer [-]"; + public const string AXLE_MODEL = "Axle model [-]"; + public const string AXLE_RATIO = "Axle gear ratio [-]"; + + public const string AUX_TECH_FORMAT = "Auxiliary technology {0} [-]"; + + public const string HDV_CO2_VEHICLE_CLASS = "HDV CO2 vehicle class [-]"; + public const string TOTAL_VEHICLE_MASS = "Total vehicle mass [kg]"; + public const string CD_x_A = "CdxA [m²]"; + //public const string ROLLING_RESISTANCE_COEFFICIENT = "weighed RRC [-]"; + public const string R_DYN = "r_dyn [m]"; + + public const string CARGO_VOLUME = "Cargo Volume [m³]"; + public const string TIME = "time [s]"; + public const string DISTANCE = "distance [km]"; + public const string SPEED = "speed [km/h]"; + public const string ALTITUDE_DELTA = "altitudeDelta [m]"; + + public const string FCMAP_H = "FC-Map [g/h]"; + public const string FCMAP_KM = "FC-Map [g/km]"; + public const string FCAUXC_H = "FC-AUXc [g/h]"; + public const string FCAUXC_KM = "FC-AUXc [g/km]"; + public const string FCWHTCC_H = "FC-WHTCc [g/h]"; + public const string FCWHTCC_KM = "FC-WHTCc [g/km]"; + public const string FCAAUX_H = "FC-AAUX [g/h]"; + public const string FCAAUX_KM = "FC-AAUX [g/km]"; + + public const string FCFINAL_H = "FC-Final [g/h]"; + public const string FCFINAL_KM = "FC-Final [g/km]"; + public const string FCFINAL_LITERPER100KM = "FC-Final [l/100km]"; + public const string FCFINAL_LITERPER100TKM = "FC-Final [l/100tkm]"; + public const string FCFINAL_LiterPer100M3KM = "FC-Final [l/100m³km]"; + + public const string CO2_KM = "CO2 [g/km]"; + public const string CO2_TKM = "CO2 [g/tkm]"; + public const string CO2_M3KM = "CO2 [g/m³km]"; + + public const string P_WHEEL_POS = "P_wheel_in_pos [kW]"; + public const string P_FCMAP_POS = "P_fcmap_pos [kW]"; + + public const string E_FORMAT = "E_{0} [kWh]"; + public const string E_AUX_FORMAT = "E_aux_{0} [kWh]"; + public const string E_AUX = "E_aux_sum [kWh]"; + + public const string E_AIR = "E_air [kWh]"; + public const string E_ROLL = "E_roll [kWh]"; + public const string E_GRAD = "E_grad [kWh]"; + public const string E_VEHICLE_INERTIA = "E_vehi_inertia [kWh]"; + public const string E_POWERTRAIN_INERTIA = "E_powertrain_inertia [kWh]"; + public const string E_BRAKE = "E_brake [kWh]"; + public const string E_GBX_LOSS = "E_gbx_loss [kWh]"; + public const string E_SHIFT_LOSS = "E_shift_loss [kWh]"; + public const string E_AXL_LOSS = "E_axl_loss [kWh]"; + public const string E_RET_LOSS = "E_ret_loss [kWh]"; + public const string E_TC_LOSS = "E_tc_loss [kWh]"; + public const string E_ANGLE_LOSS = "E_angle_loss [kWh]"; + public const string E_CLUTCH_LOSS = "E_clutch_loss [kWh]"; + public const string E_FCMAP_POS = "E_fcmap_pos [kWh]"; + public const string E_FCMAP_NEG = "E_fcmap_neg [kWh]"; + + public const string ACC = "a [m/s^2]"; + public const string ACC_POS = "a_pos [m/s^2]"; + public const string ACC_NEG = "a_neg [m/s^2]"; + + public const string ACC_TIMESHARE = "AccelerationTimeShare [%]"; + public const string DEC_TIMESHARE = "DecelerationTimeShare [%]"; + public const string CRUISE_TIMESHARE = "CruiseTimeShare [%]"; + public const string STOP_TIMESHARE = "StopTimeShare [%]"; + + public const string MAX_SPEED = "max. speed [km/h"; + public const string MAX_ACCELERATION = "max. acc [m/s²]"; + public const string MAX_DECELERATION = "max. dec [m/s²]"; + public const string AVG_ENGINE_SPEED = "n_eng_avg [rpm]"; + public const string MAX_ENGINE_SPEED = "n_eng_max [rpm]"; + public const string NUM_GEARSHIFTS = "gear shifts [-]"; + public const string ENGINE_FULL_LOAD_TIME_SHARE = "Engine max. Load time share [%]"; + public const string COASTING_TIME_SHARE = "CoastingTimeShare [%]"; + public const string BRAKING_TIME_SHARE = "BrakingTImeShare [%]"; + + public const string TIME_SHARE_PER_GEAR_FORMAT = "Gear {0} TimeShare [%]"; + + // ReSharper restore InconsistentNaming + + internal readonly DataTable Table; + private readonly ISummaryWriter _sumWriter; + + + protected SummaryDataContainer() {} + + /// <summary> + /// Initializes a new instance of the <see cref="SummaryDataContainer"/> class. + /// </summary> + /// <param name="writer"></param> + public SummaryDataContainer(ISummaryWriter writer) + { + _sumWriter = writer; + + Table = new DataTable(); + + Table.Columns.AddRange(new[] { + Tuple.Create(SORT, typeof(int)), + Tuple.Create(JOB, typeof(string)), + Tuple.Create(INPUTFILE, typeof(string)), + Tuple.Create(CYCLE, typeof(string)), + Tuple.Create(STATUS, typeof(string)), + Tuple.Create(VEHICLE_MANUFACTURER, typeof(string)), + Tuple.Create(VIN_NUMBER, typeof(string)), + Tuple.Create(VEHICLE_MODEL, typeof(string)), + Tuple.Create(HDV_CO2_VEHICLE_CLASS, typeof(string)), + Tuple.Create(CURB_MASS, typeof(SI)), + Tuple.Create(LOADING, typeof(SI)), + Tuple.Create(TOTAL_VEHICLE_MASS, typeof(SI)), + Tuple.Create(ENGINE_MANUFACTURER, typeof(string)), + Tuple.Create(ENGINE_MODEL, typeof(string)), + Tuple.Create(ENGINE_FUEL_TYPE, typeof(string)), + Tuple.Create(ENGINE_RATED_POWER, typeof(SI)), + Tuple.Create(ENGINE_IDLING_SPEED, typeof(SI)), + Tuple.Create(ENGINE_RATED_SPEED, typeof(SI)), + Tuple.Create(ENGINE_DISPLACEMENT, typeof(SI)), + Tuple.Create(ENGINE_WHTC_URBAN, typeof(double)), + Tuple.Create(ENGINE_WHTC_RURAL, typeof(double)), + Tuple.Create(ENGINE_WHTC_MOTORWAY, typeof(double)), + Tuple.Create(ENGINE_BF_COLD_HOT, typeof(double)), + Tuple.Create(ENGINE_CF_REG_PER, typeof(double)), + Tuple.Create(ENGINE_ACTUAL_CORRECTION_FACTOR, typeof(double)), + Tuple.Create(CD_x_A, typeof(SI)), + Tuple.Create(ROLLING_RESISTANCE_COEFFICIENT_W_TRAILER, typeof(double)), + Tuple.Create(ROLLING_RESISTANCE_COEFFICIENT_WO_TRAILER, typeof(double)), + Tuple.Create(R_DYN, typeof(SI)), + Tuple.Create(GEARBOX_MANUFACTURER, typeof(string)), + Tuple.Create(GEARBOX_MODEL, typeof(string)), + Tuple.Create(GEARBOX_TYPE, typeof(string)), + Tuple.Create(GEAR_RATIO_FIRST_GEAR, typeof(SI)), + Tuple.Create(GEAR_RATIO_LAST_GEAR, typeof(SI)), + Tuple.Create(TORQUECONVERTER_MANUFACTURER, typeof(string)), + Tuple.Create(TORQUECONVERTER_MODEL, typeof(string)), + Tuple.Create(RETARDER_MANUFACTURER, typeof(string)), + Tuple.Create(RETARDER_MODEL, typeof(string)), + Tuple.Create(RETARDER_TYPE, typeof(string)), + Tuple.Create(ANGLEDRIVE_MANUFACTURER, typeof(string)), + Tuple.Create(ANGLEDRIVE_MODEL, typeof(string)), + Tuple.Create(ANGLEDRIVE_RATIO, typeof(string)), + Tuple.Create(AXLE_MANUFACTURER, typeof(string)), + Tuple.Create(AXLE_MODEL, typeof(string)), + Tuple.Create(AXLE_RATIO, typeof(SI)), + Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.SteeringPump), typeof(string)), + Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.Fan), typeof(string)), + Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.HeatingVentilationAirCondition), + typeof(string)), + Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.PneumaticSystem), typeof(string)), + Tuple.Create(string.Format(AUX_TECH_FORMAT, Constants.Auxiliaries.IDs.ElectricSystem), typeof(string)), + }.Select(x => new DataColumn(x.Item1, x.Item2)).ToArray()); + + Table.Columns.AddRange(new[] { + CARGO_VOLUME, + TIME, DISTANCE, + SPEED, ALTITUDE_DELTA, + FCMAP_H, FCMAP_KM, + FCAUXC_H, FCAUXC_KM, + FCWHTCC_H, FCWHTCC_KM, + FCAAUX_H, FCAAUX_KM, + FCFINAL_H, FCFINAL_KM, + FCFINAL_LITERPER100KM, FCFINAL_LITERPER100TKM, FCFINAL_LiterPer100M3KM, + CO2_KM, CO2_TKM, CO2_M3KM, + P_WHEEL_POS, P_FCMAP_POS, + E_FCMAP_POS, E_FCMAP_NEG, E_POWERTRAIN_INERTIA, + E_AUX, E_CLUTCH_LOSS, E_TC_LOSS, E_SHIFT_LOSS, E_GBX_LOSS, + E_RET_LOSS, E_ANGLE_LOSS, E_AXL_LOSS, E_BRAKE, E_VEHICLE_INERTIA, E_AIR, E_ROLL, E_GRAD, + ACC, ACC_POS, ACC_NEG, ACC_TIMESHARE, DEC_TIMESHARE, CRUISE_TIMESHARE, STOP_TIMESHARE, + MAX_SPEED, MAX_ACCELERATION, MAX_DECELERATION, AVG_ENGINE_SPEED, MAX_ENGINE_SPEED, NUM_GEARSHIFTS, + ENGINE_FULL_LOAD_TIME_SHARE, COASTING_TIME_SHARE, BRAKING_TIME_SHARE + }.Select(x => new DataColumn(x, typeof(SI))).ToArray()); + } + + /// <summary> + /// Finishes the summary data container (writes the data to the sumWriter). + /// </summary> + public virtual void Finish() + { + if (_sumWriter != null) { + var view = new DataView(Table, "", SORT, DataViewRowState.CurrentRows).ToTable(); + var toRemove = + view.Columns.Cast<DataColumn>().Where(column => column.ColumnName.StartsWith(INTERNAL_PREFIX)).ToList(); + foreach (var dataColumn in toRemove) { + view.Columns.Remove(dataColumn); + } + _sumWriter.WriteSumData(view); + } + } + + /// <summary> + /// Writes the result of one run into the summary data container. + /// </summary> + [MethodImpl(MethodImplOptions.Synchronized)] + //public virtual void Write(IModalDataContainer modData, string jobFileName, string jobName, string cycleFileName, + // Kilogram vehicleMass, Kilogram vehicleLoading, CubicMeter cargoVolume, uint gearCount) + public virtual void Write(IModalDataContainer modData, int jobNr, int runNr, VectoRunData runData) + { + var row = Table.NewRow(); + Table.Rows.Add(row); + + row[SORT] = jobNr * 1000 + runNr; + row[JOB] = string.Format("{0}-{1}", jobNr, runNr); //ReplaceNotAllowedCharacters(current); + row[INPUTFILE] = ReplaceNotAllowedCharacters(runData.JobName); + row[CYCLE] = ReplaceNotAllowedCharacters(runData.Cycle.Name + Constants.FileExtensions.CycleFile); + + row[STATUS] = modData.RunStatus; + + var vehicleLoading = 0.SI<Kilogram>(); + var cargoVolume = 0.SI<CubicMeter>(); + var gearCount = 0u; + if (runData.Cycle.CycleType != CycleType.EngineOnly) { WriteFullPowertrain(runData, row); - cargoVolume = runData.VehicleData.CargoVolume; - vehicleLoading = runData.VehicleData.Loading; - gearCount = (uint)runData.GearboxData.Gears.Count; - } - - - var totalTime = modData.Duration(); - row[TIME] = totalTime; - - var distance = modData.Distance(); - if (distance != null) { - row[DISTANCE] = distance.ConvertTo().Kilo.Meter; - } - - var speed = modData.Speed(); - if (speed != null) { - row[SPEED] = speed.ConvertTo().Kilo.Meter.Per.Hour; - } - + cargoVolume = runData.VehicleData.CargoVolume; + vehicleLoading = runData.VehicleData.Loading; + gearCount = (uint)runData.GearboxData.Gears.Count; + } + + + var totalTime = modData.Duration(); + row[TIME] = totalTime; + + var distance = modData.Distance(); + if (distance != null) { + row[DISTANCE] = distance.ConvertTo().Kilo.Meter; + } + + var speed = modData.Speed(); + if (speed != null) { + row[SPEED] = speed.ConvertTo().Kilo.Meter.Per.Hour; + } + row[ALTITUDE_DELTA] = modData.AltitudeDelta(); WriteFuelconsumptionEntries(modData, row, vehicleLoading, cargoVolume); - var kilogramPerMeter = modData.CO2PerMeter(); - if (kilogramPerMeter != null) { - row[CO2_KM] = kilogramPerMeter.ConvertTo().Gramm.Per.Kilo.Meter; - if (vehicleLoading != null && !vehicleLoading.IsEqual(0)) { - row[CO2_TKM] = kilogramPerMeter.ConvertTo().Gramm.Per.Kilo.Meter / vehicleLoading.ConvertTo().Ton; - } - if (cargoVolume > 0) { - row[CO2_M3KM] = kilogramPerMeter.ConvertTo().Gramm.Per.Kilo.Meter / cargoVolume; - } - } - - row[P_WHEEL_POS] = modData.PowerWheelPositive().ConvertTo().Kilo.Watt; - - row[P_FCMAP_POS] = modData.TotalPowerEnginePositiveAverage().ConvertTo().Kilo.Watt; - - WriteAuxiliaries(modData, row); - + var kilogramPerMeter = modData.CO2PerMeter(); + if (kilogramPerMeter != null) { + row[CO2_KM] = kilogramPerMeter.ConvertTo().Gramm.Per.Kilo.Meter; + if (vehicleLoading != null && !vehicleLoading.IsEqual(0)) { + row[CO2_TKM] = kilogramPerMeter.ConvertTo().Gramm.Per.Kilo.Meter / vehicleLoading.ConvertTo().Ton; + } + if (cargoVolume > 0) { + row[CO2_M3KM] = kilogramPerMeter.ConvertTo().Gramm.Per.Kilo.Meter / cargoVolume; + } + } + + row[P_WHEEL_POS] = modData.PowerWheelPositive().ConvertTo().Kilo.Watt; + + row[P_FCMAP_POS] = modData.TotalPowerEnginePositiveAverage().ConvertTo().Kilo.Watt; + + WriteAuxiliaries(modData, row); + WriteWorkEntries(modData, row); - //var acc = modData.AccelerationPer3Seconds(); - - WritePerformanceEntries(modData, row); - row[ENGINE_FULL_LOAD_TIME_SHARE] = modData.EngineMaxLoadTimeShare(); - row[COASTING_TIME_SHARE] = modData.CoastingTimeShare(); - row[BRAKING_TIME_SHARE] = modData.BrakingTimeShare(); - - - if (gearCount <= 0) { - return; - } - + row[ENGINE_FULL_LOAD_TIME_SHARE] = modData.EngineMaxLoadTimeShare(); + row[COASTING_TIME_SHARE] = modData.CoastingTimeShare(); + row[BRAKING_TIME_SHARE] = modData.BrakingTimeShare(); + + if (gearCount <= 0) { + return; + } + WriteGearshiftStats(modData, row, gearCount); } @@ -383,27 +379,19 @@ namespace TUGraz.VectoCore.OutputData row[FCAUXC_H] = modData.FuelConsumptionAuxStartStopPerSecond().ConvertTo().Gramm.Per.Hour; var fuelConsumptionAuxStartStopCorrected = modData.FuelConsumptionAuxStartStop(); - if (fuelConsumptionAuxStartStopCorrected != null) { - row[FCAUXC_KM] = fuelConsumptionAuxStartStopCorrected.ConvertTo().Gramm.Per.Kilo.Meter; - } + row[FCAUXC_KM] = FuelConsumptionAsGrammPerKiloMeter(fuelConsumptionAuxStartStopCorrected); row[FCWHTCC_H] = modData.FuelConsumptionWHTCPerSecond().ConvertTo().Gramm.Per.Hour; var fuelConsumptionWHTCCorrected = modData.FuelConsumptionWHTC(); - if (fuelConsumptionWHTCCorrected != null) { - row[FCWHTCC_KM] = fuelConsumptionWHTCCorrected.ConvertTo().Gramm.Per.Kilo.Meter; - } + row[FCWHTCC_KM] = FuelConsumptionAsGrammPerKiloMeter(fuelConsumptionWHTCCorrected); row[FCAAUX_H] = modData.FuelConsumptionAAUXPerSecond().ConvertTo().Gramm.Per.Hour; var fuelConsumptionAaux = modData.FuelConsumptionAAUX(); - if (fuelConsumptionAaux != null) { - row[FCAAUX_KM] = fuelConsumptionAaux.ConvertTo().Gramm.Per.Kilo.Meter; - } + row[FCAAUX_KM] = FuelConsumptionAsGrammPerKiloMeter(fuelConsumptionAaux); row[FCFINAL_H] = modData.FuelConsumptionFinalPerSecond().ConvertTo().Gramm.Per.Hour; var fcfinal = modData.FuelConsumptionFinal(); - if (fcfinal != null) { - row[FCFINAL_KM] = fcfinal.ConvertTo().Gramm.Per.Kilo.Meter; - } + row[FCFINAL_KM] = FuelConsumptionAsGrammPerKiloMeter(fcfinal); var fcPer100lkm = modData.FuelConsumptionFinalLiterPer100Kilometer(); row[FCFINAL_LITERPER100KM] = fcPer100lkm; @@ -416,6 +404,14 @@ namespace TUGraz.VectoCore.OutputData } } + private static SI FuelConsumptionAsGrammPerKiloMeter(SI fc) + { + if (fc == null) { + return null; + } + return fc.ConvertTo().Gramm.Per.Kilo.Meter; + } + private void WriteAuxiliaries(IModalDataContainer modData, DataRow row) { foreach (var aux in modData.Auxiliaries) { @@ -545,6 +541,64 @@ namespace TUGraz.VectoCore.OutputData row[GEARBOX_MANUFACTURER] = runData.GearboxData.Manufacturer; row[GEARBOX_MODEL] = runData.GearboxData.ModelName; row[GEARBOX_TYPE] = runData.GearboxData.Type; + WriteGearboxData(runData, row); + + row[RETARDER_TYPE] = runData.Retarder.Type.GetLabel(); + WriteRetarderData(runData, row); + + WriteAngledriveData(runData, row); + + row[AXLE_MANUFACTURER] = runData.AxleGearData.Manufacturer; + row[AXLE_MODEL] = runData.AxleGearData.ModelName; + row[AXLE_RATIO] = runData.AxleGearData.AxleGear.Ratio.SI<Scalar>(); + + WriteAuxTechnologies(runData, row); + } + + private void WriteAuxTechnologies(VectoRunData runData, DataRow row) + { + foreach (var aux in runData.Aux) { + if (aux.ID == Constants.Auxiliaries.IDs.PTOConsumer || aux.ID == Constants.Auxiliaries.IDs.PTOTransmission) { + continue; + } + var colName = string.Format(AUX_TECH_FORMAT, aux.ID); + + if (!Table.Columns.Contains(colName)) { + var col = Table.Columns.Add(colName, typeof(string)); + // move the new column to correct position + col.SetOrdinal(Table.Columns[CARGO_VOLUME].Ordinal); + } + + row[colName] = aux.Technology == null ? "" : string.Join("; ", aux.Technology); + } + } + + private static void WriteAngledriveData(VectoRunData runData, DataRow row) + { + if (runData.AngledriveData != null) { + row[ANGLEDRIVE_MANUFACTURER] = runData.AngledriveData.Manufacturer; + row[ANGLEDRIVE_MODEL] = runData.AngledriveData.ModelName; + row[ANGLEDRIVE_RATIO] = runData.AngledriveData.Angledrive.Ratio; + } else { + row[ANGLEDRIVE_MANUFACTURER] = "n.a."; + row[ANGLEDRIVE_MODEL] = "n.a."; + row[ANGLEDRIVE_RATIO] = "n.a."; + } + } + + private static void WriteRetarderData(VectoRunData runData, DataRow row) + { + if (runData.Retarder.Type.IsDedicatedComponent()) { + row[RETARDER_MANUFACTURER] = runData.Retarder.Manufacturer; + row[RETARDER_MODEL] = runData.Retarder.ModelName; + } else { + row[RETARDER_MANUFACTURER] = "n.a."; + row[RETARDER_MODEL] = "n.a."; + } + } + + private static void WriteGearboxData(VectoRunData runData, DataRow row) + { if (runData.GearboxData.Type.AutomaticTransmission()) { row[GEAR_RATIO_FIRST_GEAR] = runData.GearboxData.Gears.Count > 0 ? (double.IsNaN(runData.GearboxData.Gears.First().Value.Ratio) @@ -566,61 +620,24 @@ namespace TUGraz.VectoCore.OutputData row[TORQUECONVERTER_MANUFACTURER] = "n.a."; row[TORQUECONVERTER_MODEL] = "n.a."; } - row[RETARDER_TYPE] = runData.Retarder.Type.GetLabel(); - if (runData.Retarder.Type.IsDedicatedComponent()) { - row[RETARDER_MANUFACTURER] = runData.Retarder.Manufacturer; - row[RETARDER_MODEL] = runData.Retarder.ModelName; - } else { - row[RETARDER_MANUFACTURER] = "n.a."; - row[RETARDER_MODEL] = "n.a."; - } - - if (runData.AngledriveData != null) { - row[ANGLEDRIVE_MANUFACTURER] = runData.AngledriveData.Manufacturer; - row[ANGLEDRIVE_MODEL] = runData.AngledriveData.ModelName; - row[ANGLEDRIVE_RATIO] = runData.AngledriveData.Angledrive.Ratio; - } else { - row[ANGLEDRIVE_MANUFACTURER] = "n.a."; - row[ANGLEDRIVE_MODEL] = "n.a."; - row[ANGLEDRIVE_RATIO] = "n.a."; - } - - row[AXLE_MANUFACTURER] = runData.AxleGearData.Manufacturer; - row[AXLE_MODEL] = runData.AxleGearData.ModelName; - row[AXLE_RATIO] = runData.AxleGearData.AxleGear.Ratio.SI<Scalar>(); + } - foreach (var aux in runData.Aux) { - if (aux.ID == Constants.Auxiliaries.IDs.PTOConsumer || aux.ID == Constants.Auxiliaries.IDs.PTOTransmission) { - continue; - } - var colName = string.Format(AUX_TECH_FORMAT, aux.ID); + private static string ReplaceNotAllowedCharacters(string text) + { + return text.Replace('#', '_').Replace(',', '_').Replace('\n', '_').Replace('\r', '_'); + } - if (!Table.Columns.Contains(colName)) { - var col = Table.Columns.Add(colName, typeof(string)); - // move the new column to correct position - col.SetOrdinal(Table.Columns[CARGO_VOLUME].Ordinal); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - row[colName] = aux.Technology == null ? "" : string.Join("; ", aux.Technology); + protected void Dispose(bool disposing) + { + if (disposing) { + Table.Dispose(); } } - - private static string ReplaceNotAllowedCharacters(string text) - { - return text.Replace('#', '_').Replace(',', '_').Replace('\n', '_').Replace('\r', '_'); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected void Dispose(bool disposing) - { - if (disposing) { - Table.Dispose(); - } - } - } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Utils/VectoCSVFile.cs b/VectoCore/VectoCore/Utils/VectoCSVFile.cs index e904ab0ade..59a24a038a 100644 --- a/VectoCore/VectoCore/Utils/VectoCSVFile.cs +++ b/VectoCore/VectoCore/Utils/VectoCSVFile.cs @@ -147,12 +147,8 @@ namespace TUGraz.VectoCore.Utils table.Columns.Add(col); } - if (p.EndOfData) { - return; - } - var lineNumber = 1; - do { + while (!p.EndOfData) { string[] cells = { }; if (firstLineIsData) { cells = colsWithoutComment; @@ -179,7 +175,7 @@ namespace TUGraz.VectoCore.Utils string.Format("Line {0}: The data format of a value is not correct. {1}", lineNumber, e.Message), e); } lineNumber++; - } while (!p.EndOfData); + } } /// <summary> -- GitLab