Fixed log spam for stage priority flow mode
Fixed log spam for stage priority flow mode

// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
public class EngineSim public class EngineSim
{ {
private readonly ResourceContainer resourceConsumptions = new ResourceContainer(); private readonly ResourceContainer resourceConsumptions = new ResourceContainer();
   
public double actualThrust = 0; public double actualThrust = 0;
public bool isActive = false; public bool isActive = false;
public double isp = 0; public double isp = 0;
public PartSim partSim; public PartSim partSim;
public List<AppliedForce> appliedForces; public List<AppliedForce> appliedForces;
   
public double thrust = 0; public double thrust = 0;
   
// Add thrust vector to account for directional losses // Add thrust vector to account for directional losses
public Vector3 thrustVec; public Vector3 thrustVec;
   
public EngineSim(PartSim theEngine, public EngineSim(PartSim theEngine,
double atmosphere, double atmosphere,
double velocity, double velocity,
float maxThrust, float maxThrust,
float minThrust, float minThrust,
float thrustPercentage, float thrustPercentage,
float requestedThrust, float requestedThrust,
Vector3 vecThrust, Vector3 vecThrust,
float realIsp, float realIsp,
FloatCurve atmosphereCurve, FloatCurve atmosphereCurve,
FloatCurve velocityCurve, FloatCurve velocityCurve,
bool throttleLocked, bool throttleLocked,
List<Propellant> propellants, List<Propellant> propellants,
bool active, bool active,
bool correctThrust, bool correctThrust,
List<Transform> thrustTransforms) List<Transform> thrustTransforms)
{ {
StringBuilder buffer = null; StringBuilder buffer = null;
//MonoBehaviour.print("Create EngineSim for " + theEngine.name); //MonoBehaviour.print("Create EngineSim for " + theEngine.name);
//MonoBehaviour.print("maxThrust = " + maxThrust); //MonoBehaviour.print("maxThrust = " + maxThrust);
//MonoBehaviour.print("minThrust = " + minThrust); //MonoBehaviour.print("minThrust = " + minThrust);
//MonoBehaviour.print("thrustPercentage = " + thrustPercentage); //MonoBehaviour.print("thrustPercentage = " + thrustPercentage);
//MonoBehaviour.print("requestedThrust = " + requestedThrust); //MonoBehaviour.print("requestedThrust = " + requestedThrust);
//MonoBehaviour.print("velocity = " + velocity); //MonoBehaviour.print("velocity = " + velocity);
   
this.partSim = theEngine; this.partSim = theEngine;
   
this.isActive = active; this.isActive = active;
this.thrust = (maxThrust - minThrust) * (thrustPercentage / 100f) + minThrust; this.thrust = (maxThrust - minThrust) * (thrustPercentage / 100f) + minThrust;
//MonoBehaviour.print("thrust = " + thrust); //MonoBehaviour.print("thrust = " + thrust);
   
this.thrustVec = vecThrust; this.thrustVec = vecThrust;
   
double flowRate = 0d; double flowRate = 0d;
if (this.partSim.hasVessel) if (this.partSim.hasVessel)
{ {
//MonoBehaviour.print("hasVessel is true"); //MonoBehaviour.print("hasVessel is true");
this.actualThrust = isActive ? requestedThrust : 0.0; this.actualThrust = isActive ? requestedThrust : 0.0;
if (velocityCurve != null) if (velocityCurve != null)
{ {
this.actualThrust *= velocityCurve.Evaluate((float)velocity); this.actualThrust *= velocityCurve.Evaluate((float)velocity);
//MonoBehaviour.print("actualThrust at velocity = " + actualThrust); //MonoBehaviour.print("actualThrust at velocity = " + actualThrust);
} }
   
this.isp = atmosphereCurve.Evaluate((float)this.partSim.part.staticPressureAtm); this.isp = atmosphereCurve.Evaluate((float)this.partSim.part.staticPressureAtm);
if (this.isp == 0d) if (this.isp == 0d)
{ {
MonoBehaviour.print("Isp at " + this.partSim.part.staticPressureAtm + " is zero. Flow rate will be NaN"); MonoBehaviour.print("Isp at " + this.partSim.part.staticPressureAtm + " is zero. Flow rate will be NaN");
} }
   
if (correctThrust && realIsp == 0) if (correctThrust && realIsp == 0)
{ {
float ispsl = atmosphereCurve.Evaluate(0); float ispsl = atmosphereCurve.Evaluate(0);
if (ispsl != 0) if (ispsl != 0)
{ {
this.thrust = this.thrust * this.isp / ispsl; this.thrust = this.thrust * this.isp / ispsl;
} }
else else
{ {
MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust."); MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust.");
} }
//MonoBehaviour.print("corrected thrust = " + thrust); //MonoBehaviour.print("corrected thrust = " + thrust);
} }
   
if (velocityCurve != null) if (velocityCurve != null)
{ {
this.thrust *= velocityCurve.Evaluate((float)velocity); this.thrust *= velocityCurve.Evaluate((float)velocity);
//MonoBehaviour.print("thrust at velocity = " + thrust); //MonoBehaviour.print("thrust at velocity = " + thrust);
} }
   
if (throttleLocked) if (throttleLocked)
{ {
//MonoBehaviour.print("throttleLocked is true"); //MonoBehaviour.print("throttleLocked is true");
flowRate = this.thrust / (this.isp * 9.82); flowRate = this.thrust / (this.isp * 9.82);
} }
else else
{ {
if (this.partSim.isLanded) if (this.partSim.isLanded)
{ {
//MonoBehaviour.print("partSim.isLanded is true, mainThrottle = " + FlightInputHandler.state.mainThrottle); //MonoBehaviour.print("partSim.isLanded is true, mainThrottle = " + FlightInputHandler.state.mainThrottle);
flowRate = Math.Max(0.000001d, this.thrust * FlightInputHandler.state.mainThrottle) / (this.isp * 9.82); flowRate = Math.Max(0.000001d, this.thrust * FlightInputHandler.state.mainThrottle) / (this.isp * 9.82);
} }
else else
{ {
if (requestedThrust > 0) if (requestedThrust > 0)
{ {
if (velocityCurve != null) if (velocityCurve != null)
{ {
requestedThrust *= velocityCurve.Evaluate((float)velocity); requestedThrust *= velocityCurve.Evaluate((float)velocity);
//MonoBehaviour.print("requestedThrust at velocity = " + requestedThrust); //MonoBehaviour.print("requestedThrust at velocity = " + requestedThrust);
} }
   
//MonoBehaviour.print("requestedThrust > 0"); //MonoBehaviour.print("requestedThrust > 0");
flowRate = requestedThrust / (this.isp * 9.82); flowRate = requestedThrust / (this.isp * 9.82);
} }
else else
{ {
//MonoBehaviour.print("requestedThrust <= 0"); //MonoBehaviour.print("requestedThrust <= 0");
flowRate = this.thrust / (this.isp * 9.82); flowRate = this.thrust / (this.isp * 9.82);
} }
} }
} }
} }
else else
{ {
//MonoBehaviour.print("hasVessel is false"); //MonoBehaviour.print("hasVessel is false");
this.isp = atmosphereCurve.Evaluate((float)atmosphere); this.isp = atmosphereCurve.Evaluate((float)atmosphere);
if (this.isp == 0d) if (this.isp == 0d)
{ {
MonoBehaviour.print("Isp at " + atmosphere + " is zero. Flow rate will be NaN"); MonoBehaviour.print("Isp at " + atmosphere + " is zero. Flow rate will be NaN");
} }
if (correctThrust) if (correctThrust)
{ {
float ispsl = atmosphereCurve.Evaluate(0); float ispsl = atmosphereCurve.Evaluate(0);
if (ispsl != 0) if (ispsl != 0)
{ {
this.thrust = this.thrust * this.isp / ispsl; this.thrust = this.thrust * this.isp / ispsl;
} }
else else
{ {
MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust."); MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust.");
} }
//MonoBehaviour.print("corrected thrust = " + thrust); //MonoBehaviour.print("corrected thrust = " + thrust);
} }
   
if (velocityCurve != null) if (velocityCurve != null)
{ {
this.thrust *= velocityCurve.Evaluate((float)velocity); this.thrust *= velocityCurve.Evaluate((float)velocity);
//MonoBehaviour.print("thrust at velocity = " + thrust); //MonoBehaviour.print("thrust at velocity = " + thrust);
} }
   
flowRate = this.thrust / (this.isp * 9.82); flowRate = this.thrust / (this.isp * 9.82);
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer = new StringBuilder(1024); buffer = new StringBuilder(1024);
buffer.AppendFormat("flowRate = {0:g6}\n", flowRate); buffer.AppendFormat("flowRate = {0:g6}\n", flowRate);
} }
   
float flowMass = 0f; float flowMass = 0f;
foreach (Propellant propellant in propellants) foreach (Propellant propellant in propellants)
{ {
flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendFormat("flowMass = {0:g6}\n", flowMass); buffer.AppendFormat("flowMass = {0:g6}\n", flowMass);
} }
   
foreach (Propellant propellant in propellants) foreach (Propellant propellant in propellants)
{ {
if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
{ {
continue; continue;
} }
   
double consumptionRate = propellant.ratio * flowRate / flowMass; double consumptionRate = propellant.ratio * flowRate / flowMass;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendFormat("Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate); buffer.AppendFormat("Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate);
} }
this.resourceConsumptions.Add(propellant.id, consumptionRate); this.resourceConsumptions.Add(propellant.id, consumptionRate);
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
   
appliedForces = new List<AppliedForce>(); appliedForces = new List<AppliedForce>();
double thrustPerThrustTransform = thrust / thrustTransforms.Count; double thrustPerThrustTransform = thrust / thrustTransforms.Count;
foreach (Transform thrustTransform in thrustTransforms) { foreach (Transform thrustTransform in thrustTransforms) {
Vector3d direction = thrustTransform.forward.normalized; Vector3d direction = thrustTransform.forward.normalized;
Vector3d position = thrustTransform.position; Vector3d position = thrustTransform.position;
appliedForces.Add(new AppliedForce(direction * thrustPerThrustTransform, position)); appliedForces.Add(new AppliedForce(direction * thrustPerThrustTransform, position));
} }
} }
   
public ResourceContainer ResourceConsumptions public ResourceContainer ResourceConsumptions
{ {
get { return this.resourceConsumptions; } get { return this.resourceConsumptions; }
} }
   
public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts) public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts)
{ {
LogMsg log = null; LogMsg log = null;
   
// A dictionary to hold a set of parts for each resource // A dictionary to hold a set of parts for each resource
Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>(); Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>();
   
foreach (int type in this.resourceConsumptions.Types) foreach (int type in this.resourceConsumptions.Types)
{ {
HashSet<PartSim> sourcePartSet = null; HashSet<PartSim> sourcePartSet = null;
switch (ResourceContainer.GetResourceFlowMode(type)) switch (ResourceContainer.GetResourceFlowMode(type))
{ {
case ResourceFlowMode.NO_FLOW: case ResourceFlowMode.NO_FLOW:
if (this.partSim.resources[type] > SimManager.RESOURCE_MIN && this.partSim.resourceFlowStates[type] != 0) if (this.partSim.resources[type] > SimManager.RESOURCE_MIN && this.partSim.resourceFlowStates[type] != 0)
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
//MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this"); //MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this");
sourcePartSet.Add(this.partSim); sourcePartSet.Add(this.partSim);
} }
break; break;
   
case ResourceFlowMode.ALL_VESSEL: case ResourceFlowMode.ALL_VESSEL:
foreach (PartSim aPartSim in allParts) foreach (PartSim aPartSim in allParts)
{ {
if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0) if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0)
{ {
if (sourcePartSet == null) if (sourcePartSet == null)
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
} }
   
sourcePartSet.Add(aPartSim); sourcePartSet.Add(aPartSim);
} }
} }
break; break;
   
case ResourceFlowMode.STAGE_PRIORITY_FLOW: case ResourceFlowMode.STAGE_PRIORITY_FLOW:
var stagePartSets = new Dictionary<int, HashSet<PartSim>>(); var stagePartSets = new Dictionary<int, HashSet<PartSim>>();
var maxStage = -1; var maxStage = -1;
   
Logger.Log(type); //Logger.Log(type);
foreach (var aPartSim in allParts) foreach (var aPartSim in allParts)
{ {
if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0) continue; if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0) continue;
   
var stage = aPartSim.DecouplerCount(); var stage = aPartSim.DecouplerCount();
if (stage > maxStage) if (stage > maxStage)
{ {
maxStage = stage; maxStage = stage;
} }
   
if (!stagePartSets.TryGetValue(stage, out sourcePartSet)) if (!stagePartSets.TryGetValue(stage, out sourcePartSet))
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
stagePartSets.Add(stage, sourcePartSet); stagePartSets.Add(stage, sourcePartSet);
} }
sourcePartSet.Add(aPartSim); sourcePartSet.Add(aPartSim);
} }
   
for (var i = 0; i <= maxStage; i++) for (var i = 0; i <= maxStage; i++)
{ {
HashSet<PartSim> stagePartSet; HashSet<PartSim> stagePartSet;
if (stagePartSets.TryGetValue(i, out stagePartSet) && stagePartSet.Count > 0) if (stagePartSets.TryGetValue(i, out stagePartSet) && stagePartSet.Count > 0)
{ {
sourcePartSet = stagePartSet; sourcePartSet = stagePartSet;
} }
} }
break; break;
   
case ResourceFlowMode.STACK_PRIORITY_SEARCH: case ResourceFlowMode.STACK_PRIORITY_SEARCH:
HashSet<PartSim> visited = new HashSet<PartSim>(); HashSet<PartSim> visited = new HashSet<PartSim>();
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("Find " + ResourceContainer.GetResourceName(type) + " sources for " + this.partSim.name + ":" + this.partSim.partId); log.buf.AppendLine("Find " + ResourceContainer.GetResourceName(type) + " sources for " + this.partSim.name + ":" + this.partSim.partId);
} }
sourcePartSet = this.partSim.GetSourceSet(type, allParts, visited, log, ""); sourcePartSet = this.partSim.GetSourceSet(type, allParts, visited, log, "");
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print(log.buf); MonoBehaviour.print(log.buf);
} }
break; break;
   
default: default:
MonoBehaviour.print("SetResourceDrains(" + this.partSim.name + ":" + this.partSim.partId + ") Unexpected flow type for " + ResourceContainer.GetResourceName(type) + ")"); MonoBehaviour.print("SetResourceDrains(" + this.partSim.name + ":" + this.partSim.partId + ") Unexpected flow type for " + ResourceContainer.GetResourceName(type) + ")");
break; break;
} }
   
if (sourcePartSet != null && sourcePartSet.Count > 0) if (sourcePartSet != null && sourcePartSet.Count > 0)
{ {
sourcePartSets[type] = sourcePartSet; sourcePartSets[type] = sourcePartSet;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("Source parts for " + ResourceContainer.GetResourceName(type) + ":"); log.buf.AppendLine("Source parts for " + ResourceContainer.GetResourceName(type) + ":");
foreach (PartSim partSim in sourcePartSet) foreach (PartSim partSim in sourcePartSet)
{ {
log.buf.AppendLine(partSim.name + ":" + partSim.partId); log.buf.AppendLine(partSim.name + ":" + partSim.partId);
} }
MonoBehaviour.print(log.buf); MonoBehaviour.print(log.buf);
} }
} }
} }
   
// If we don't have sources for all the needed resources then return false without setting up any drains // If we don't have sources for all the needed resources then return false without setting up any drains
foreach (int type in this.resourceConsumptions.Types) foreach (int type in this.resourceConsumptions.Types)
{ {
if (!sourcePartSets.ContainsKey(type)) if (!sourcePartSets.ContainsKey(type))
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("No source of " + ResourceContainer.GetResourceName(type)); MonoBehaviour.print("No source of " + ResourceContainer.GetResourceName(type));
} }
   
this.isActive = false; this.isActive = false;
return false; return false;
} }
} }
   
// Now we set the drains on the members of the sets and update the draining parts set // Now we set the drains on the members of the sets and update the draining parts set
foreach (int type in this.resourceConsumptions.Types) foreach (int type in this.resourceConsumptions.Types)
{ {
HashSet<PartSim> sourcePartSet = sourcePartSets[type]; HashSet<PartSim> sourcePartSet = sourcePartSets[type];
// Loop through the members of the set // Loop through the members of the set
double amount = this.resourceConsumptions[type] / sourcePartSet.Count; double amount = this.resourceConsumptions[type] / sourcePartSet.Count;
foreach (PartSim partSim in sourcePartSet) foreach (PartSim partSim in sourcePartSet)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + partSim.partId); MonoBehaviour.print("Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + partSim.partId);
} }
   
partSim.resourceDrains.Add(type, amount); partSim.resourceDrains.Add(type, amount);
drainingParts.Add(partSim); drainingParts.Add(partSim);
} }
} }
   
return true; return true;
} }
   
public void DumpEngineToBuffer(StringBuilder buffer, String prefix) public void DumpEngineToBuffer(StringBuilder buffer, String prefix)
{ {
buffer.Append(prefix); buffer.Append(prefix);
buffer.AppendFormat("[thrust = {0:g6}, actual = {1:g6}, isp = {2:g6}\n", this.thrust, this.actualThrust, this.isp); buffer.AppendFormat("[thrust = {0:g6}, actual = {1:g6}, isp = {2:g6}\n", this.thrust, this.actualThrust, this.isp);
} }
} }
} }