From 8994a4df3f3288cd173e696d96b0af1022315eaa Mon Sep 17 00:00:00 2001
From: mfranz <mfranz>
Date: Sun, 27 Aug 2006 12:25:43 +0000
Subject: [PATCH] - merge YASim fuel management parts in (should still work
 with JSBSim as   far as this supports air refueling at all) - make listeners
 keep variables up-to-date (for better performance) - remove the ugly
 initialization stuff in the main loop, but wait in   an extra loop for the
 FDM to come up

This script works now with the a4f, too. The vulcan2 is *supposed* to
work, but there's apparently a bug in it that prevents it.
/systems/refuel/contact is correcly set, and that should be enough for it.
---
 Aircraft/Generic/aar.nas | 306 ++++++++++++++++++++++++++-------------
 1 file changed, 209 insertions(+), 97 deletions(-)

diff --git a/Aircraft/Generic/aar.nas b/Aircraft/Generic/aar.nas
index 6539b032e..59a04df2c 100644
--- a/Aircraft/Generic/aar.nas
+++ b/Aircraft/Generic/aar.nas
@@ -3,118 +3,230 @@
 # + level-lbs       - OUTPUT ONLY property, do not try to set
 # + selected        - boolean indicating tank selection.
 # + density-ppg     - Fuel density, in lbs/gallon.
-# + capacity-gal_us - Tank capacity 
+# + capacity-gal_us - Tank capacity
 #
 # Properties under /engines/engine[n]:
 # + fuel-consumed-lbs - Output from the FDM, zeroed by this script
 # + out-of-fuel       - boolean, set by this code.
 
-# ==================================== timer stuff ===========================================
 
-# set the update period
+var UPDATE_PERIOD = 0.3;
 
-UPDATE_PERIOD = 0.3;
-
-# set the timer for the selected function
-
-registerTimer = func {
-	
-    settimer(arg[0], UPDATE_PERIOD);
-
-} # end function 
-
-# =============================== end timer stuff ===========================================
+var enabled = nil;
+var fuel_freeze = nil;
+var ai_enabled = nil;
+var engines = nil;
+var tanks = nil;
+var refuelingN = nil;
+var aimodelsN = nil;
 
 
 
-initialized = 0;
-enabled = 0;
-
-#print ("running aar");
-#print (" enabled " , enabled,  " initialized ", initialized);  
-
-updateTanker = func {
-#print ("tanker update running ");
-				#if (!initialized ) {
-				#print("calling initialize");
-				#initialize();}
-    
-        Refueling = props.globals.getNode("/systems/refuel/contact");
-        AllAircraft = props.globals.getNode("ai/models").getChildren("aircraft");
-				AllMultiplayer = props.globals.getNode("ai/models").getChildren("multiplayer");
-        Aircraft = props.globals.getNode("ai/models/aircraft");
-        
-#   select all tankers which are in contact. For now we assume that it must be in 
-#		contact	with us.
-                
-        selectedTankers = [];
-				
-				if ( enabled ) { # check that AI Models are enabled, otherwise don't bother
-            foreach(a; AllAircraft) {
-                contact_node = a.getNode("refuel/contact");
-                id_node = a.getNode("id");
-                tanker_node = a.getNode("tanker");
-                
-                contact = contact_node.getValue();
-                id = id_node.getValue();
-                tanker = tanker_node.getValue();
-                
-				#print ("contact ", contact , " tanker " , tanker );
-                            
-                if (tanker and contact) {
-                    append(selectedTankers, a);
-                }
-            }
-						
-						foreach(m; AllMultiplayer) {
-                contact_node = m.getNode("refuel/contact");
-                id_node = m.getNode("id");
-                tanker_node = m.getNode("tanker");
-                
-                contact = contact_node.getValue();
-                id = id_node.getValue();
-                tanker = tanker_node.getValue();
-                
-#				print (" mp contact ", contact , " tanker " , tanker );
-                            
-                if (tanker and contact) {
-                    append(selectedTankers, m);
-                }
-            }
-        }
-         
-		#print ("tankers ", size(selectedTankers) );
-
-        if ( size(selectedTankers) >= 1 ){
-            Refueling.setBoolValue(1);
-        } else {
-            Refueling.setBoolValue(0);
-        }
-		registerTimer(updateTanker);
+# initialize property if it doesn't exist, and set node type otherwise
+init_prop = func(node, prop, val, type = "double") {
+	if (node.getNode(prop) != nil) {
+		val = node.getNode(prop).getValue();
+	}
+	node = node.getNode(prop, 1);
+	if (type == "double") {
+		node.setDoubleValue(val);
+	} elsif (type == "bool") {
+		node.setBoolValue(val);
+	} elsif (type == "int") {
+		node.setIntValue(val);
+	}
 }
 
-# Initalize: Make sure all needed properties are present and accounted
-# for, and that they have sane default values.
+
+
+update_loop = func {
+	# check for contact with tanker aircraft
+	var tankers = [];
+	if (ai_enabled) {
+		foreach (a; aimodelsN.getChildren("aircraft")) {
+			var contact = a.getNode("refuel/contact").getBoolValue();
+			var tanker = a.getNode("tanker").getBoolValue();
+			#var id = a.getNode("id").getValue();
+			#print("ai '", id, "'  contact=", contact, "  tanker=", tanker);
+
+			if (tanker and contact) {
+				append(tankers, a);
+			}
+		}
+
+		foreach (m; aimodelsN.getChildren("multiplayer")) {
+			var contact = m.getNode("refuel/contact").getBoolValue();
+			var tanker = m.getNode("tanker").getBoolValue();
+			#var id = m.getNode("id").getValue();
+			#print("mp '", id, "'  contact=", contact, "  tanker=", tanker);
+
+			if (tanker and contact) {
+				append(tankers, m);
+			}
+		}
+	}
+	var refueling = size(tankers) >= 1;
+	refuelingN.setBoolValue(refueling);
+
+
+	if (fuel_freeze) {
+		return settimer(update_loop, UPDATE_PERIOD);
+	}
+
+
+	# sum up consumed fuel
+	var total = 0;
+	foreach (var e; engines) {
+		total += e.getNode("fuel-consumed-lbs").getValue();
+	}
+
+
+	# calculate fuel received
+	if (refueling) {
+		# assume max flow rate is 6000 lbs/min (for KC135)
+		var received = 100 * UPDATE_PERIOD;
+		total -= received;
+	}
+
+	var tanks = props.globals.getNode("consumables/fuel", 1).getChildren("tank");
+
+	# make list of selected tanks
+	var selected_tanks = [];
+	foreach (var t; tanks) {
+		var cap = t.getNode("capacity-gal_us", 1).getValue();
+		if (cap != nil and cap > 0.01 and t.getNode("selected", 1).getBoolValue()) {
+			append(selected_tanks, t);
+		}
+	}
+
+
+	var out_of_fuel = 0;
+	if (size(selected_tanks) == 0) {
+		out_of_fuel = 1;
+
+	} else {
+		if (total >= 0) {
+			var fuelPerTank = total / size(selected_tanks);
+			foreach (var t; selected_tanks) {
+				var ppg = t.getNode("density-ppg").getValue();
+				var lbs = t.getNode("level-gal_us").getValue() * ppg;
+				lbs -= fuelPerTank;
+
+				if (lbs < 0) {
+					lbs = 0;
+					# Kill the engines if we're told to, otherwise simply
+					# deselect the tank.
+					if (t.getNode("kill-when-empty", 1).getBoolValue()) {
+						out_of_fuel = 1;
+					} else {
+						t.getNode("selected", 1).setBoolValue(0);
+					}
+				}
+
+				var gals = lbs / ppg;
+				t.getNode("level-gal_us").setDoubleValue(gals);
+				t.getNode("level-lbs").setDoubleValue(lbs);
+			}
+
+		} elsif (total < 0) {
+			#find the number of tanks which can accept fuel
+			var available = 0;
+
+			foreach (var t; selected_tanks) {
+				var ppg = t.getNode("density-ppg").getValue();
+				var capacity = t.getNode("capacity-gal_us").getValue() * ppg;
+				var lbs = t.getNode("level-gal_us").getValue() * ppg;
+
+				if (lbs < capacity) {
+					available += 1;
+				}
+			}
+
+			if (available > 0) {
+				var fuelPerTank = total / available;
+
+				# add fuel to each available tank
+				foreach (var t; selected_tanks) {
+					var ppg = t.getNode("density-ppg").getValue();
+					var capacity = t.getNode("capacity-gal_us").getValue() * ppg;
+					var lbs = t.getNode("level-gal_us").getValue() * ppg;
+
+					if (capacity - lbs >= fuelPerTank) {
+						lbs -= fuelPerTank;
+					} elsif (capacity - lbs < fuelPerTank) {
+						lbs = capacity;
+					}
+
+					t.getNode("level-gal_us").setDoubleValue(lbs / ppg);
+					t.getNode("level-lbs").setDoubleValue(lbs);
+				}
+
+				# print ("available ", available , " fuelPerTank " , fuelPerTank);
+			}
+		}
+	}
+
+
+	var gals = 0;
+	var lbs = 0;
+	var cap = 0;
+	foreach (var t; tanks) {
+		gals += t.getNode("level-gal_us", 1).getValue();
+		lbs += t.getNode("level-lbs", 1).getValue();
+		cap += t.getNode("capacity-gal_us", 1).getValue();
+	}
+
+	setprop("/consumables/fuel/total-fuel-gals", gals);
+	setprop("/consumables/fuel/total-fuel-lbs", lbs);
+	setprop("/consumables/fuel/total-fuel-norm", gals / cap);
+
+	foreach (var e; engines) {
+		e.getNode("out-of-fuel", 1).setBoolValue(out_of_fuel);
+	}
+	settimer(update_loop, UPDATE_PERIOD);
+}
+
+
 
 initialize = func {
-   
-    AI_Enabled = props.globals.getNode("sim/ai/enabled");
-    Refueling = props.globals.getNode("/systems/refuel/contact",1);
-            
-    Refueling.setBoolValue(0);
-    enabled = AI_Enabled.getValue();
-        
-    initialized = 1;
+	fuel.updateFuel = func {}
+
+	refuelingN = props.globals.getNode("/systems/refuel/contact", 1);
+	refuelingN.setBoolValue(0);
+
+	aimodelsN = props.globals.getNode("ai/models", 1);
+	engines = props.globals.getNode("engines", 1).getChildren("engine");
+	tanks = props.globals.getNode("consumables/fuel", 1).getChildren("tank");
+
+	foreach (var e; engines) {
+		e.getNode("fuel-consumed-lbs", 1).setDoubleValue(0);
+		e.getNode("out-of-fuel", 1).setBoolValue(0);
+	}
+
+	foreach (var t; tanks) {
+		init_prop(t, "level-gal_us", 0);
+		init_prop(t, "level-lbs", 0);
+		init_prop(t, "capacity-gal_us", 0.01); # Not zero (div/zero issue)
+		init_prop(t, "density-ppg", 6.0);      # gasoline
+		init_prop(t, "selected", 1, "bool");
+	}
+
+	setlistener("sim/freeze/fuel", func { fuel_freeze = cmdarg().getBoolValue() }, 1);
+	setlistener("sim/ai/enabled", func { ai_enabled = cmdarg().getBoolValue() }, 1);
+	update_loop();
 }
 
-initDoubleProp = func {
-    node = arg[0]; prop = arg[1]; val = arg[2];
-    if(node.getNode(prop) != nil) {
-        val = num(node.getNode(prop).getValue());
-    }
-    node.getNode(prop, 1).setDoubleValue(val);
+
+
+wait_for_fdm = func {
+	if (getprop("/position/altitude-agl-ft")) { # is there a better indicator?
+		initialize();
+	} else {
+		settimer(wait_for_fdm, 1);
+	}
 }
 
-# Fire it up
-if (!initialized) {initialize();}
-registerTimer(updateTanker);
+
+settimer(wait_for_fdm, 0);
+
+