From 4bc4b5ba14484108ea23711844f82573413ae78f Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Wed, 2 Sep 2015 16:58:09 +0200
Subject: [PATCH] refactoring driver

---
 .../Exceptions/VectoSimulationException.cs    |   4 +
 .../Models/Simulation/DataBus/IDataBus.cs     |   2 +-
 .../Simulation/Impl/VehicleContainer.cs       |  10 +-
 .../{IBreaks.cs => IBrakes.cs}                |   2 +-
 .../Impl/{Breaks.cs => Brakes.cs}             |   8 +-
 .../Models/SimulationComponent/Impl/Clutch.cs |   2 +-
 .../Impl/DefaultDriverStrategy.cs             |  10 +-
 .../Models/SimulationComponent/Impl/Driver.cs | 498 +++++++++++-------
 .../SimulationComponent/Impl/Gearbox.cs       |   7 +-
 9 files changed, 345 insertions(+), 198 deletions(-)
 rename VectoCore/Models/SimulationComponent/{IBreaks.cs => IBrakes.cs} (83%)
 rename VectoCore/Models/SimulationComponent/Impl/{Breaks.cs => Brakes.cs} (81%)

diff --git a/VectoCore/Exceptions/VectoSimulationException.cs b/VectoCore/Exceptions/VectoSimulationException.cs
index 0880f14e0f..72c4d12c44 100644
--- a/VectoCore/Exceptions/VectoSimulationException.cs
+++ b/VectoCore/Exceptions/VectoSimulationException.cs
@@ -1,4 +1,5 @@
 using System;
+using JetBrains.Annotations;
 
 namespace TUGraz.VectoCore.Exceptions
 {
@@ -6,5 +7,8 @@ namespace TUGraz.VectoCore.Exceptions
 	{
 		public VectoSimulationException(string msg) : base(msg) {}
 		public VectoSimulationException(string msg, Exception inner) : base(msg, inner) {}
+
+		[StringFormatMethod("message")]
+		public VectoSimulationException(string message, params object[] args) : base(message, args) {}
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/Models/Simulation/DataBus/IDataBus.cs b/VectoCore/Models/Simulation/DataBus/IDataBus.cs
index a3ad564caf..669d39c602 100644
--- a/VectoCore/Models/Simulation/DataBus/IDataBus.cs
+++ b/VectoCore/Models/Simulation/DataBus/IDataBus.cs
@@ -5,6 +5,6 @@ namespace TUGraz.VectoCore.Models.Simulation.DataBus
 	/// <summary>
 	/// Defines interfaces for all different cockpits to access shared data of the powertrain.
 	/// </summary>
-	public interface IDataBus : IGearboxInfo, IEngineInfo, IVehicleInfo, IMileageCounter, IClutchInfo, IBreaks,
+	public interface IDataBus : IGearboxInfo, IEngineInfo, IVehicleInfo, IMileageCounter, IClutchInfo, IBrakes,
 		IRoadLookAhead {}
 }
\ No newline at end of file
diff --git a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs
index 296584f360..70921712cc 100644
--- a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs
+++ b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs
@@ -16,7 +16,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 		internal IEngineInfo Engine;
 		internal IGearboxInfo Gearbox;
 		internal IVehicleInfo Vehicle;
-		internal IBreaks Breaks;
+		internal IBrakes Brakes;
 
 		internal IMileageCounter MilageCounter;
 
@@ -129,9 +129,9 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 				MilageCounter = milage;
 			}
 
-			var breaks = component as IBreaks;
+			var breaks = component as IBrakes;
 			if (breaks != null) {
-				Breaks = breaks;
+				Brakes = breaks;
 			}
 
 			var road = component as IRoadLookAhead;
@@ -196,8 +196,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 
 		public Watt BreakPower
 		{
-			get { return Breaks.BreakPower; }
-			set { Breaks.BreakPower = value; }
+			get { return Brakes.BreakPower; }
+			set { Brakes.BreakPower = value; }
 		}
 
 		public ClutchState ClutchState()
diff --git a/VectoCore/Models/SimulationComponent/IBreaks.cs b/VectoCore/Models/SimulationComponent/IBrakes.cs
similarity index 83%
rename from VectoCore/Models/SimulationComponent/IBreaks.cs
rename to VectoCore/Models/SimulationComponent/IBrakes.cs
index d32488fe2e..f00f90dc8f 100644
--- a/VectoCore/Models/SimulationComponent/IBreaks.cs
+++ b/VectoCore/Models/SimulationComponent/IBrakes.cs
@@ -2,7 +2,7 @@
 
 namespace TUGraz.VectoCore.Models.SimulationComponent
 {
-	public interface IBreaks
+	public interface IBrakes
 	{
 		Watt BreakPower { get; set; }
 	}
diff --git a/VectoCore/Models/SimulationComponent/Impl/Breaks.cs b/VectoCore/Models/SimulationComponent/Impl/Brakes.cs
similarity index 81%
rename from VectoCore/Models/SimulationComponent/Impl/Breaks.cs
rename to VectoCore/Models/SimulationComponent/Impl/Brakes.cs
index 226fe0daae..0408d2fe86 100644
--- a/VectoCore/Models/SimulationComponent/Impl/Breaks.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/Brakes.cs
@@ -1,4 +1,5 @@
 using System;
+using TUGraz.VectoCore.Exceptions;
 using TUGraz.VectoCore.Models.Connector.Ports;
 using TUGraz.VectoCore.Models.Simulation;
 using TUGraz.VectoCore.Models.Simulation.Data;
@@ -6,13 +7,13 @@ using TUGraz.VectoCore.Utils;
 
 namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 {
-	public class Breaks : VectoSimulationComponent, IPowerTrainComponent, ITnOutPort, ITnInPort, IBreaks
+	public class Brakes : VectoSimulationComponent, IPowerTrainComponent, ITnOutPort, ITnInPort, IBrakes
 	{
 		protected ITnOutPort Next;
 
 		protected NewtonMeter BreakTorque;
 
-		public Breaks(IVehicleContainer dataBus) : base(dataBus) {}
+		public Brakes(IVehicleContainer dataBus) : base(dataBus) {}
 
 
 		public ITnInPort InPort()
@@ -36,6 +37,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 					BreakTorque = Formulas.PowerToTorque(BreakPower, angularVelocity);
 				}
 			}
+			if (!dryRun && BreakPower < 0) {
+				throw new VectoSimulationException("Negative Braking Power is not allowed!");
+			}
 			return Next.Request(absTime, dt, torque - BreakTorque, angularVelocity, dryRun);
 		}
 
diff --git a/VectoCore/Models/SimulationComponent/Impl/Clutch.cs b/VectoCore/Models/SimulationComponent/Impl/Clutch.cs
index 26db64051c..c7e53997cd 100644
--- a/VectoCore/Models/SimulationComponent/Impl/Clutch.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/Clutch.cs
@@ -115,7 +115,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 		public ClutchState ClutchState()
 		{
-			return _clutchState;
+			return DataBus.Gear == 0 ? SimulationComponent.ClutchState.ClutchOpened : _clutchState;
 		}
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs b/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs
index 9d3de10691..22ed6603f6 100644
--- a/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs
@@ -32,8 +32,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 		public DefaultDriverStrategy()
 		{
-			DrivingModes.Add(DrivingMode.DrivingModeDrive, new DriverModeDrive());
-			DrivingModes.Add(DrivingMode.DrivingModeBrake, new DriverModeBrake());
+			DrivingModes.Add(DrivingMode.DrivingModeDrive, new DriverModeDrive() { DriverStrategy = this });
+			DrivingModes.Add(DrivingMode.DrivingModeBrake, new DriverModeBrake() { DriverStrategy = this });
 			CurrentDrivingMode = DrivingMode.DrivingModeDrive;
 		}
 
@@ -106,7 +106,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 	public interface IDriverMode
 	{
-		IDriverActions Driver { get; set; }
+		DefaultDriverStrategy DriverStrategy { get; set; }
 
 		IResponse Request(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient);
 	}
@@ -115,7 +115,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 	public class DriverModeDrive : IDriverMode
 	{
-		public IDriverActions Driver { get; set; }
+		public DefaultDriverStrategy DriverStrategy { get; set; }
 
 		public IResponse Request(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient)
 		{
@@ -127,7 +127,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 	public class DriverModeBrake : IDriverMode
 	{
-		public IDriverActions Driver { get; set; }
+		public DefaultDriverStrategy DriverStrategy { get; set; }
 
 		public IResponse Request(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient)
 		{
diff --git a/VectoCore/Models/SimulationComponent/Impl/Driver.cs b/VectoCore/Models/SimulationComponent/Impl/Driver.cs
index d0a3169ce8..42a530ba6f 100644
--- a/VectoCore/Models/SimulationComponent/Impl/Driver.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/Driver.cs
@@ -1,4 +1,5 @@
 using System;
+using System.CodeDom;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
@@ -138,198 +139,312 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 		//	throw new VectoSimulationException("unhandled driving action " + CurrentState.DrivingAction);
 		//}
 
-		public IResponse DrivingActionRoll(Second absTime, Meter ds, Radian gradient)
-		{
-			throw new NotImplementedException();
-		}
-
 		IDataBus IDriverActions.DataBus
 		{
 			get { return DataBus; }
 		}
 
-
-		public IResponse DrivingActionBrake(Second absTime, Meter ds, Radian gradient, MeterPerSecond nextTargetSpeed)
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="absTime"></param>
+		/// <param name="ds"></param>
+		/// <param name="targetVelocity"></param>
+		/// <param name="gradient"></param>
+		/// <returns></returns>
+		public IResponse DrivingActionAccelerate(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient)
 		{
-			ComputeAcceleration(ref ds, nextTargetSpeed);
+			var operatingPoint = ComputeAcceleration(ds, targetVelocity);
 
-			// todo: still required?
-			if (CurrentState.dt <= 0) {
+			if (operatingPoint.SimulationInterval <= 0) {
 				return new ResponseFailTimeInterval();
 			}
 
-			var response = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, true);
+			IResponse retVal = null;
+			var response = Next.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient);
+			response.Switch().
+				Case<ResponseSuccess>(r => {
+					r.SimulationInterval = operatingPoint.SimulationInterval;
+					retVal = r; // => return
+				}).
+				Case<ResponseEngineOverload>(r => {
+					if (r.Delta < 0) {
+						// if Delta is negative we are already below the Drag-load curve. activate breaks
+						retVal = r; // => return, strategy should brake
+					}
+				}).
+				Case<ResponseGearShift>(r => { retVal = r; }).
+				Default(r => { throw new VectoException(string.Format("Unknown Response: {0}", r)); });
 
-			var dryRun = response as ResponseDryRun;
-			if (dryRun == null) {
-				throw new VectoSimulationException("Expected DryRunResponse after Dry-Run Request!");
+			if (retVal == null) {
+				// unhandled response (overload, delta > 0) - we need to search for a valid operating point..				
+				operatingPoint = SearchOperatingPoint(absTime, ds, gradient, operatingPoint.Acceleration, response);
+
+				operatingPoint = LimitAccelerationByDriverModel(operatingPoint, false);
+				Log.Debug("Found operating point for Drive/Accelerate. dt: {0}, acceleration: {1}",
+					CurrentState.dt, CurrentState.Acceleration);
+
+				retVal = Next.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient);
+				retVal.SimulationInterval = operatingPoint.SimulationInterval;
+			}
+			CurrentState.Acceleration = operatingPoint.Acceleration;
+			CurrentState.dt = operatingPoint.SimulationInterval;
+			CurrentState.Response = retVal;
+
+			return retVal;
+		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="absTime"></param>
+		/// <param name="ds"></param>
+		/// <param name="gradient"></param>
+		/// <returns></returns>
+		public IResponse DrivingActionCoast(Second absTime, Meter ds, Radian gradient)
+		{
+			var operatingPoint = ComputeAcceleration(ds, 0.SI<MeterPerSecond>());
+
+			if (operatingPoint.SimulationInterval <= 0) {
+				return new ResponseFailTimeInterval();
 			}
 
-			var newDs = ds;
-			SearchBreakingPower(absTime, ref newDs, gradient, dryRun);
+			CurrentState.Acceleration = operatingPoint.Acceleration;
 
-			if (!ds.IsEqual(newDs)) {
-				Log.Debug(
-					"SearchOperatingPoint Breaking reduced the max. distance: {0} -> {1}. Issue new request from driving cycle!", newDs,
-					ds);
-				return new ResponseDrivingCycleDistanceExceeded { MaxDistance = newDs, SimulationInterval = CurrentState.dt };
+			var response = Next.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient,
+				dryRun: true);
+
+			operatingPoint = SearchOperatingPoint(absTime, operatingPoint.SimulationDistance, gradient,
+				operatingPoint.Acceleration, response, coasting: true);
+
+			if (!ds.IsEqual(operatingPoint.SimulationDistance)) {
+				// vehicle is at low speed, coasting would lead to stop before ds is reached.
+				Log.Debug("SearchOperatingPoint reduced the max. distance: {0} -> {1}. Issue new request from driving cycle!",
+					operatingPoint.SimulationDistance, ds);
+				return new ResponseDrivingCycleDistanceExceeded {
+					MaxDistance = operatingPoint.SimulationDistance,
+					SimulationInterval = CurrentState.dt
+				};
 			}
 
-			Log.Debug("Found operating point for breaking. dt: {0}, acceleration: {1}", CurrentState.dt,
+			Log.Debug("Found operating point for coasting. dt: {0}, acceleration: {1}", CurrentState.dt,
 				CurrentState.Acceleration);
+
+			operatingPoint = LimitAccelerationByDriverModel(operatingPoint, true);
+
+			CurrentState.dt = operatingPoint.SimulationInterval;
+			CurrentState.Acceleration = operatingPoint.Acceleration;
+
 			var retVal = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient);
 			CurrentState.Response = retVal;
 
 			retVal.Switch().
 				Case<ResponseSuccess>(r => r.SimulationInterval = CurrentState.dt).
-				Default(() => Log.Debug("unhandled response from powertrain: {0}", retVal));
+				Case<ResponseEngineOverload>(() => {
+					/* an overload may occur due to limiting the acceleration. strategy has to handle this */
+				}).
+				Case<ResponseGearShift>(r => retVal = r).
+				Default(() => { throw new VectoSimulationException("unhandled response from powertrain: {0}", retVal); });
 
 			return retVal; //new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt };
 		}
 
-		private void SearchBreakingPower(Second absTime, ref Meter ds, Radian gradient, ResponseDryRun response)
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="absTime"></param>
+		/// <param name="ds"></param>
+		/// <param name="gradient"></param>
+		/// <returns></returns>
+		public IResponse DrivingActionRoll(Second absTime, Meter ds, Radian gradient)
 		{
-			var debug = new List<dynamic>(); // only used while testing
-			var breakingPower = (DataBus.ClutchState() != ClutchState.ClutchClosed)
-				? response.AxlegearPowerRequest.Abs()
-				: response.DeltaDragLoad.Abs();
+			var operatingPoint = ComputeAcceleration(ds, 0.SI<MeterPerSecond>());
 
-			var searchInterval = Constants.SimulationSettings.BreakingPowerInitialSearchInterval;
+			if (operatingPoint.SimulationInterval <= 0) {
+				return new ResponseFailTimeInterval();
+			}
 
-			var originalDs = ds;
-			Watt origDelta = null;
+			CurrentState.Acceleration = operatingPoint.Acceleration;
 
-			// double the searchInterval until a good interval was found
-			var intervalFactor = 2.0;
+			var response = Next.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient,
+				dryRun: true);
 
-			do {
-				ds = originalDs;
-				var delta = DataBus.ClutchState() == ClutchState.ClutchClosed
-					? -response.DeltaDragLoad
-					: -response.AxlegearPowerRequest;
+			operatingPoint = SearchOperatingPoint(absTime, operatingPoint.SimulationDistance, gradient,
+				operatingPoint.Acceleration, response, coasting: true);
 
-				debug.Add(new { breakingPower, searchInterval, delta });
+			if (!ds.IsEqual(operatingPoint.SimulationDistance)) {
+				// vehicle is at low speed, coasting would lead to stop before ds is reached.
+				Log.Debug("SearchOperatingPoint reduced the max. distance: {0} -> {1}. Issue new request from driving cycle!",
+					operatingPoint.SimulationDistance, ds);
+				return new ResponseDrivingCycleDistanceExceeded {
+					MaxDistance = operatingPoint.SimulationDistance,
+					SimulationInterval = CurrentState.dt
+				};
+			}
 
-				if (origDelta == null) {
-					origDelta = delta;
-				} else {
-					// check if a correct searchInterval was found (when the delta changed signs, we stepped through the 0-point)
-					// from then on the searchInterval can be bisected.
-					if (origDelta.Sign() != delta.Sign()) {
-						intervalFactor = 0.5;
-					}
-				}
+			Log.Debug("Found operating point for coasting. dt: {0}, acceleration: {1}", CurrentState.dt,
+				CurrentState.Acceleration);
 
-				if (delta.IsEqual(0, Constants.SimulationSettings.EngineFLDPowerTolerance)) {
-					Log.Debug("found operating point in {0} iterations, delta: {1}", debug.Count, delta);
-					return;
-				}
+			operatingPoint = LimitAccelerationByDriverModel(operatingPoint, true);
 
+			CurrentState.dt = operatingPoint.SimulationInterval;
+			CurrentState.Acceleration = operatingPoint.Acceleration;
 
-				breakingPower += searchInterval * -delta.Sign();
-				searchInterval *= intervalFactor;
+			var retVal = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient);
+			CurrentState.Response = retVal;
 
-				CurrentState.dt = ComputeTimeInterval(CurrentState.Acceleration, ref ds);
-				DataBus.BreakPower = breakingPower;
-				response = (ResponseDryRun)Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, true);
-			} while (CurrentState.RetryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold);
+			retVal.Switch().
+				Case<ResponseSuccess>(r => r.SimulationInterval = CurrentState.dt).
+				Case<ResponseEngineOverload>(() => {
+					/* an overload may occur due to limiting the acceleration. strategy has to handle this */
+				}).
+				Default(() => { throw new VectoSimulationException("unhandled response from powertrain: {0}", retVal); });
 
-			Log.Debug("Exceeded max iterations when searching for operating point!");
-			Log.Debug("exceeded: {0} ... {1}", ", ".Join(debug.Take(5)), ", ".Join(debug.Slice(-6)));
-			Log.Error("Failed to find operating point for breaking!");
-			throw new VectoSimulationException("Failed to find operating point for breaking!");
+			return retVal; //new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt };
 		}
 
-		public IResponse DrivingActionAccelerate(Second absTime, Meter ds, MeterPerSecond targetVelocity, Radian gradient)
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="absTime"></param>
+		/// <param name="ds"></param>
+		/// <param name="gradient"></param>
+		/// <param name="nextTargetSpeed"></param>
+		/// <returns></returns>
+		public IResponse DrivingActionBrake(Second absTime, Meter ds, Radian gradient, MeterPerSecond nextTargetSpeed)
 		{
-			ComputeAcceleration(ref ds, targetVelocity);
-			if (CurrentState.dt <= 0) {
+			var operatingPoint = ComputeAcceleration(ds, nextTargetSpeed);
+
+			// todo: still required?
+			if (operatingPoint.SimulationInterval <= 0) {
 				return new ResponseFailTimeInterval();
 			}
 
-			IResponse retVal = null;
-			do {
-				var response = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient);
-				response.Switch().
-					Case<ResponseSuccess>(r => {
-						r.SimulationInterval = CurrentState.dt;
-						retVal = r;
-					}).
-					Case<ResponseEngineOverload>(r => {
-						if (r != null && r.Delta < 0) {
-							// if Delta is negative we are already below the Drag-load curve. activate breaks
-							retVal = DrivingActionBrake(absTime, ds, gradient, targetVelocity);
-						} else {
-							var doAccelerate = (DataBus.VehicleSpeed() - targetVelocity).Abs() > 0.1 * targetVelocity;
-
-							SearchOperatingPoint(absTime, ref ds, gradient, r, accelerating: doAccelerate);
-							Log.Debug("Found operating point for Drive/Accelerate. dt: {0}, acceleration: {1}, doAccelerate: {2}",
-								CurrentState.dt, CurrentState.Acceleration, doAccelerate);
-						}
-					}).
-					Case<ResponseGearShift>(() => { }).
-					Case<ResponseGearboxOverload>(r => {
-						if (r != null && r.Delta < 0) {
-							// if Delta is negative we are below the Drag-load curve: activate breaks
-							retVal = DrivingActionBrake(absTime, ds, gradient, targetVelocity);
-						} else {
-							var doAccelerate = (DataBus.VehicleSpeed() - targetVelocity).Abs() > 0.1 * targetVelocity;
-
-							SearchOperatingPoint(absTime, ref ds, gradient, r, accelerating: doAccelerate);
-							Log.Debug("Found operating point for Drive/Accelerate. dt: {0}, acceleration: {1}, doAccelerate: {2}",
-								CurrentState.dt, CurrentState.Acceleration, doAccelerate);
-						}
-					}).
-					Default(r => { throw new VectoException(string.Format("Unknown Response: {0}", r)); });
-				if (retVal != null) {
-					return retVal;
-				}
-			} while (CurrentState.RetryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold);
-
-			return new ResponseDrivingCycleDistanceExceeded { SimulationInterval = CurrentState.dt };
-		}
-
-		public IResponse DrivingActionCoast(Second absTime, Meter ds, Radian gradient)
-		{
-			ComputeAcceleration(ref ds, 0.SI<MeterPerSecond>());
+			var response = Next.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient, true);
 
-			if (CurrentState.dt <= 0) {
-				return new ResponseFailTimeInterval();
+			var dryRun = response as ResponseDryRun;
+			if (dryRun == null) {
+				throw new VectoSimulationException("Expected DryRunResponse after Dry-Run Request!");
 			}
 
-			var response = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, dryRun: true);
+			if (dryRun.DeltaDragLoad > 0) {
+				throw new VectoSimulationException("Braking not required, above drag load! Use engine brake!");
+			}
 
-			var newDs = ds;
-			SearchOperatingPoint(absTime, ref newDs, gradient, response, coasting: true);
+			operatingPoint = SearchBreakingPower(absTime, operatingPoint.SimulationDistance, gradient,
+				operatingPoint.Acceleration, dryRun);
 
-			if (!ds.IsEqual(newDs)) {
+			if (!ds.IsEqual(operatingPoint.SimulationDistance)) {
 				Log.Debug(
-					"SearchOperatingPoint reduced the max. distance: {0} -> {1}. Issue new request from driving cycle!", newDs, ds);
-				return new ResponseDrivingCycleDistanceExceeded { MaxDistance = newDs, SimulationInterval = CurrentState.dt };
+					"SearchOperatingPoint Breaking reduced the max. distance: {0} -> {1}. Issue new request from driving cycle!",
+					operatingPoint.SimulationDistance, ds);
+				return new ResponseDrivingCycleDistanceExceeded {
+					MaxDistance = operatingPoint.SimulationDistance,
+					SimulationInterval = operatingPoint.SimulationInterval
+				};
 			}
 
-			Log.Debug("Found operating point for coasting. dt: {0}, acceleration: {1}", CurrentState.dt,
-				CurrentState.Acceleration);
+			Log.Debug("Found operating point for breaking. dt: {0}, acceleration: {1}", operatingPoint.SimulationInterval,
+				operatingPoint.Acceleration);
+
+			var retVal = Next.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient);
+
+			CurrentState.Acceleration = operatingPoint.Acceleration;
+			CurrentState.dt = operatingPoint.SimulationInterval;
+			CurrentState.Response = retVal;
 
-			if (CurrentState.Acceleration < DeclarationData.Driver.LookAhead.Deceleration) {
+			retVal.SimulationInterval = CurrentState.dt;
+			return retVal;
+		}
+
+
+		// ================================================
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="operatingPoint"></param>
+		/// <param name="limitByLookahead"></param>
+		/// <returns></returns>
+		private OperatingPoint LimitAccelerationByDriverModel(OperatingPoint operatingPoint, bool limitByLookahead)
+		{
+			// todo: limit by driver model!
+
+			if (limitByLookahead && operatingPoint.Acceleration < DeclarationData.Driver.LookAhead.Deceleration) {
 				Log.Debug("Limiting coasting deceleration from {0} to {1}", CurrentState.Acceleration,
 					DeclarationData.Driver.LookAhead.Deceleration);
-				CurrentState.Acceleration = DeclarationData.Driver.LookAhead.Deceleration;
-				//CurrentState.dt = ComputeTimeInterval(CurrentState.Acceleration, ref ds);
+				operatingPoint.Acceleration = DeclarationData.Driver.LookAhead.Deceleration;
+				operatingPoint.SimulationInterval =
+					ComputeTimeInterval(CurrentState.Acceleration, operatingPoint.SimulationDistance).SimulationInterval;
 				Log.Debug("Changed dt due to limited coasting deceleration. dt: {0}", CurrentState.dt);
 			}
+			return operatingPoint;
+		}
 
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="absTime"></param>
+		/// <param name="ds"></param>
+		/// <param name="gradient"></param>
+		/// <param name="acceleration"></param>
+		/// <param name="response"></param>
+		/// <returns></returns>
+		private OperatingPoint SearchBreakingPower(Second absTime, Meter ds, Radian gradient,
+			MeterPerSquareSecond acceleration, IResponse response)
+		{
+			var debug = new List<dynamic>(); // only used while testing
 
-			var retVal = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient);
-			CurrentState.Response = retVal;
+			var searchInterval = Constants.SimulationSettings.BreakingPowerInitialSearchInterval;
 
-			retVal.Switch().
-				Case<ResponseSuccess>(r => r.SimulationInterval = CurrentState.dt).
-				Default(() => Log.Debug("unhandled response from powertrain: {0}", retVal));
+			var operatingPoint = new OperatingPoint() { SimulationDistance = ds, Acceleration = acceleration };
+			var origDelta = DataBus.ClutchState() == ClutchState.ClutchClosed
+				? response.DeltaDragLoad
+				: response.GearboxPowerRequest;
 
-			return retVal; //new ResponseDrivingCycleDistanceExceeded() { SimulationInterval = CurrentState.dt };
+			var breakingPower = origDelta.Abs();
+
+
+			// double the searchInterval until a good interval was found
+			var intervalFactor = 2.0;
+			var retryCount = 0;
+
+			do {
+				var delta = DataBus.ClutchState() == ClutchState.ClutchClosed
+					? response.DeltaDragLoad
+					: response.GearboxPowerRequest;
+
+				debug.Add(new { breakingPower, searchInterval, delta });
+
+				// check if a correct searchInterval was found (when the delta changed signs, we stepped through the 0-point)
+				// from then on the searchInterval can be bisected.
+				if (origDelta.Sign() != delta.Sign()) {
+					intervalFactor = 0.5;
+				}
+
+				if (delta.IsEqual(0, Constants.SimulationSettings.EngineFLDPowerTolerance)) {
+					Log.Debug("found operating point in {0} iterations, delta: {1}", debug.Count, delta);
+					return operatingPoint;
+				}
+
+				breakingPower += searchInterval * -delta.Sign();
+				searchInterval *= intervalFactor;
+
+				operatingPoint = ComputeTimeInterval(operatingPoint.Acceleration, ds);
+				DataBus.BreakPower = breakingPower;
+				response =
+					(ResponseDryRun)
+						Next.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient, true);
+			} while (retryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold);
+
+			Log.Warn("Exceeded max iterations when searching for operating point!");
+			Log.Warn("exceeded: {0} ... {1}", ", ".Join(debug.Take(5)), ", ".Join(debug.Slice(-6)));
+			Log.Error("Failed to find operating point for breaking!");
+			throw new VectoSimulationException("Failed to find operating point for breaking!");
 		}
 
+
 		/// <summary>
 		/// search for the operating point where the engine's requested power is either on the full-load curve or  on the drag curve (parameter 'coasting').
 		/// before the search can  be performed either a normal request or a dry-run request has to be made and the response is passed to this method.
@@ -341,37 +456,40 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 		/// <param name="absTime">absTime from the original request</param>
 		/// <param name="ds">ds from the original request</param>
 		/// <param name="gradient">gradient from the original request</param>
+		/// <param name="acceleration"></param>
 		/// <param name="response">response of the former request that resulted in an overload response (or a dry-run response)</param>
 		/// <param name="coasting">if true approach the drag-load curve, otherwise full-load curve</param>
-		/// <param name="accelerating"></param>
 		/// <returns></returns>
-		private void SearchOperatingPoint(Second absTime, ref Meter ds, Radian gradient,
-			IResponse response, bool coasting = false, bool accelerating = true)
+		protected OperatingPoint SearchOperatingPoint(Second absTime, Meter ds, Radian gradient,
+			MeterPerSquareSecond acceleration, IResponse response, bool coasting = false)
 		{
+			// remove accelerating param
 			var debug = new List<dynamic>();
 			var searchInterval = Constants.SimulationSettings.OperatingPointInitialSearchIntervalAccelerating;
 
-			var originalDs = ds;
-
-			if (coasting) {
-				accelerating = false;
-			}
-
 			// double the searchInterval until a good interval was found
 			var intervalFactor = 2.0;
 
+			var retVal = new OperatingPoint() { Acceleration = acceleration, SimulationDistance = ds };
+
+			var actionRoll = DataBus.ClutchState() == ClutchState.ClutchOpened;
+
 			var delta = 0.SI<Watt>();
 			Watt origDelta = null;
+			var retryCount = 0;
 
 			do {
-				ds = originalDs;
-				response.Switch().
-					Case<ResponseEngineOverload>(r => delta = r.Delta).
-					Case<ResponseGearboxOverload>(r => delta = r.Delta).
-					Case<ResponseDryRun>(r => delta = coasting ? r.DeltaDragLoad : r.DeltaFullLoad).
-					Default(r => { throw new VectoSimulationException(string.Format("Unknown response type. {0}", r)); });
-
-				debug.Add(new { delta, acceleration = CurrentState.Acceleration, searchInterval, intervalFactor });
+				if (actionRoll) {
+					response.Switch().
+						Case<ResponseEngineOverload>().
+						Case <
+				} else {
+					response.Switch().
+						Case<ResponseEngineOverload>(r => delta = r.Delta).
+						Case<ResponseDryRun>(r => delta = coasting ? r.DeltaDragLoad : r.DeltaFullLoad).
+						Default(r => { throw new VectoSimulationException(string.Format("Unknown response type. {0}", r)); });
+				}
+				debug.Add(new { delta, acceleration = retVal.Acceleration, searchInterval, intervalFactor });
 
 				if (origDelta == null) {
 					origDelta = delta;
@@ -387,34 +505,36 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 					Log.Debug(
 						"found operating point in {0} iterations. Engine Power req: {2}, Gearbox Power req: {3} delta: {1}",
 						debug.Count, delta, response.EnginePowerRequest, response.GearboxPowerRequest);
-					return;
+					return retVal;
 				}
 
 				searchInterval *= intervalFactor;
-				CurrentState.Acceleration += searchInterval * -delta.Sign();
+				retVal.Acceleration += searchInterval * -delta.Sign();
+
 
+				// TODO: move to driving mode
 				// check for minimum acceleration, add some safety margin due to search
-				if (!coasting && accelerating &&
-					CurrentState.Acceleration.Abs() < Constants.SimulationSettings.MinimumAcceleration.Value() / 5.0 &&
-					searchInterval.Abs() < Constants.SimulationSettings.MinimumAcceleration / 20.0) {
+				if (!coasting && retVal.Acceleration.Abs() < Constants.SimulationSettings.MinimumAcceleration.Value() / 5.0 /* &&
+				 searchInterval.Abs() < Constants.SimulationSettings.MinimumAcceleration / 20.0 */) {
 					throw new VectoSimulationException("Could not achieve minimum acceleration");
 				}
 
+				var tmp = ComputeTimeInterval(retVal.Acceleration, ds);
+				retVal.SimulationInterval = tmp.SimulationInterval;
 
-				CurrentState.dt = ComputeTimeInterval(CurrentState.Acceleration, ref ds);
+				response = Next.Request(absTime, retVal.SimulationInterval, retVal.Acceleration, gradient, true);
+			} while (retryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold);
 
-				response = Next.Request(absTime, CurrentState.dt, CurrentState.Acceleration, gradient, true);
-			} while (CurrentState.RetryCount++ < Constants.SimulationSettings.DriverSearchLoopThreshold);
-
-			Log.Debug("Exceeded max iterations when searching for operating point!");
-			Log.Debug("acceleration: {0} ... {1}", ", ".Join(debug.Take(5).Select(x => x.acceleration)),
+			Log.Warn("Exceeded max iterations when searching for operating point!");
+			Log.Warn("acceleration: {0} ... {1}", ", ".Join(debug.Take(5).Select(x => x.acceleration)),
 				", ".Join(debug.Slice(-6).Select(x => x.acceleration)));
-			Log.Debug("exceeded: {0} ... {1}", ", ".Join(debug.Take(5).Select(x => x.delta)),
+			Log.Warn("exceeded: {0} ... {1}", ", ".Join(debug.Take(5).Select(x => x.delta)),
 				", ".Join(debug.Slice(-6).Select(x => x.delta)));
 			Log.Error("Failed to find operating point! absTime: {0}", absTime);
 			throw new VectoSimulationException(string.Format("Failed to find operating point! absTime: {0}", absTime));
 		}
 
+
 		/// <summary>
 		/// compute the acceleration and time-interval such that the vehicle's velocity approaches the given target velocity
 		/// - first compute the acceleration to reach the targetVelocity within the given distance
@@ -424,9 +544,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 		/// </summary>
 		/// <param name="ds"></param>
 		/// <param name="targetVelocity"></param>
-		public void ComputeAcceleration(ref Meter ds, MeterPerSecond targetVelocity)
+		public OperatingPoint ComputeAcceleration(Meter ds, MeterPerSecond targetVelocity)
 		{
 			var currentSpeed = DataBus.VehicleSpeed();
+			var retVal = new OperatingPoint() { SimulationDistance = ds };
 
 			var requiredAverageSpeed = (targetVelocity + currentSpeed) / 2.0;
 			var requiredAcceleration =
@@ -440,15 +561,19 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 				requiredAcceleration = maxAcceleration.Deceleration;
 			}
 
-			CurrentState.Acceleration = requiredAcceleration;
-			var tmpDs = ds;
-			CurrentState.dt = ComputeTimeInterval(CurrentState.Acceleration, ref ds);
-			if (!ds.IsEqual(tmpDs)) {
-				Log.Error(
-					"Unexpected Condition: Distance has been adjusted from {0} to {1}, currentVelocity: {2} acceleration: {3}, targetVelocity: {4}",
-					tmpDs, ds, currentSpeed, CurrentState.Acceleration, targetVelocity);
-				throw new VectoSimulationException("Simulation distance unexpectedly adjusted!");
+			retVal.Acceleration = requiredAcceleration;
+			var operatingPoint = ComputeTimeInterval(retVal.Acceleration, ds);
+
+			if (ds.IsEqual(operatingPoint.SimulationDistance)) {
+				return retVal;
 			}
+
+			// this case should not happen, acceleration has been computed such that the target speed
+			// can be reached within ds.
+			Log.Error(
+				"Unexpected Condition: Distance has been adjusted from {0} to {1}, currentVelocity: {2} acceleration: {3}, targetVelocity: {4}",
+				operatingPoint.SimulationDistance, ds, currentSpeed, CurrentState.Acceleration, targetVelocity);
+			throw new VectoSimulationException("Simulation distance unexpectedly adjusted!");
 		}
 
 
@@ -471,19 +596,20 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 		/// </summary>
 		/// <param name="acceleration"></param>
 		/// <param name="ds"></param>
-		private Second ComputeTimeInterval(MeterPerSquareSecond acceleration, ref Meter ds)
+		private OperatingPoint ComputeTimeInterval(MeterPerSquareSecond acceleration, Meter ds)
 		{
 			if (!(ds > 0)) {
 				throw new VectoSimulationException("distance has to be greater than 0!");
 			}
 			var currentSpeed = DataBus.VehicleSpeed();
-
+			var retVal = new OperatingPoint() { Acceleration = acceleration, SimulationDistance = ds };
 			if (acceleration.IsEqual(0)) {
-				if (!(currentSpeed > 0)) {
-					Log.Error("vehicle speed is {0}, acceleration is {1}", currentSpeed, acceleration);
-					throw new VectoSimulationException("vehicle speed has to be > 0 if acceleration = 0");
+				if (currentSpeed > 0) {
+					retVal.SimulationInterval = ds / currentSpeed;
+					return retVal;
 				}
-				return ds / currentSpeed;
+				Log.Error("vehicle speed is {0}, acceleration is {1}", currentSpeed, acceleration);
+				throw new VectoSimulationException("vehicle speed has to be > 0 if acceleration = 0");
 			}
 
 			// we need to accelerate / decelerate. solve quadratic equation...
@@ -493,9 +619,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 			if (solutions.Count == 0) {
 				// no real-valued solutions, required distance can not be reached (vehicle stopped), adapt ds...
-				var dt = -currentSpeed / acceleration;
-				var stopDistance = currentSpeed * dt + acceleration / 2 * dt * dt;
+				retVal.SimulationInterval = -currentSpeed / acceleration;
+				var stopDistance = currentSpeed * retVal.SimulationInterval +
+									acceleration / 2 * retVal.SimulationInterval * retVal.SimulationInterval;
 				if (stopDistance > ds) {
+					// just to cover everything - does not happen...
 					Log.Warn(
 						"Could not find solution for computing required time interval to drive distance {0}. currentSpeed: {1}, acceleration: {2}",
 						ds, currentSpeed, acceleration);
@@ -503,14 +631,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 				}
 				Log.Info(
 					"Adjusted distance when computing time interval: currentSpeed: {0}, acceleration: {1}, distance: {2} -> {3}, timeInterval: {4}",
-					currentSpeed, acceleration, ds, stopDistance, dt);
-				ds = stopDistance;
-				return dt;
+					currentSpeed, acceleration, retVal.SimulationDistance, stopDistance, retVal.SimulationInterval);
+				retVal.SimulationDistance = stopDistance;
+				return retVal;
 			}
 			solutions = solutions.Where(x => x >= 0).ToList();
 			// if there are 2 positive solutions (i.e. when decelerating), take the smaller time interval
 			// (the second solution means that you reach negative speed 
-			return solutions.Min().SI<Second>();
+			retVal.SimulationInterval = solutions.Min().SI<Second>();
+			return retVal;
 		}
 
 		/// <summary>
@@ -551,7 +680,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 			if (!(CurrentState.Response is ResponseSuccess)) {
 				throw new VectoSimulationException("Previous request did not succeed!");
 			}
-			CurrentState.RetryCount = 0;
+			//CurrentState.RetryCount = 0;
 			CurrentState.Response = null;
 
 			//if (CurrentState.DrivingAction.NextTargetSpeed != null &&
@@ -572,7 +701,14 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 			public Second dt;
 			public MeterPerSquareSecond Acceleration;
 			public IResponse Response;
-			public int RetryCount;
+			//public int RetryCount;
+		}
+
+		public struct OperatingPoint
+		{
+			public MeterPerSquareSecond Acceleration;
+			public Meter SimulationDistance;
+			public Second SimulationInterval;
 		}
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs
index 1c6217e0e2..93670a6275 100644
--- a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs
+++ b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs
@@ -78,6 +78,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 			//Special Behaviour: When Gear is 0 (no gear set) OR the speed is 0 (not rotating) a zero-request is applied.
 			if (Gear == 0) {
+				if (dryRun) {
+					return new ResponseDryRun() { GearboxPowerRequest = outTorque * outEngineSpeed };
+				}
 				var resp = Next.Request(absTime, dt, 0.SI<NewtonMeter>(), 0.SI<PerSecond>(), dryRun);
 				resp.GearboxPowerRequest = outTorque * outEngineSpeed;
 				return resp;
@@ -110,9 +113,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 
 			_loss = inTorque * inEngineSpeed - outTorque * outEngineSpeed;
 
-			// DryRun Response
+			// DryRun Response, gear != 0
 			if (dryRun) {
-				//todo: Gearbox DryRun Request: Add the DeltaFull, DeltaDrag for Gearbox FullLoadCurve
 				var r = Next.Request(absTime, dt, inTorque, inEngineSpeed, true);
 				r.GearboxPowerRequest = outTorque * outEngineSpeed;
 				return r;
@@ -141,6 +143,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
 			// Normal Response
 			var response = Next.Request(absTime, dt, inTorque, inEngineSpeed);
 			response.GearboxPowerRequest = outTorque * outEngineSpeed;
+
 			return response;
 		}
 
-- 
GitLab