# Properties under /consumables/fuel/tank[n]: # + level-gal_us - Current fuel load. Can be set by user code. # + 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 # # Properties under /engines/engine[n]: # + fuel-consumed-lbs - Output from the FDM, zeroed by this script # + out-of-fuel - boolean, set by this code. var UPDATE_PERIOD = 0.3; var enabled = nil; var serviceable = nil; var fuel_freeze = nil; var ai_enabled = nil; var engines = nil; var tanks = nil; var refuelingN = nil; var aimodelsN = nil; # initialize property if it doesn't exist, and set node type otherwise var init_prop = func(node, prop, val, type = "double") { var n = node.getNode(prop); if (n != nil) { var v = n.getValue(); if (v != nil) val = v; } node = node.getNode(prop, 1); if (type == "double") node.setDoubleValue(val); elsif (type == "bool") node.setBoolValue(val); elsif (type == "int") node.setIntValue(val); } var update_loop = func { # check for contact with tanker aircraft var tankers = []; if (ai_enabled) { var ac = aimodelsN.getChildren("tanker"); var mp = aimodelsN.getChildren("multiplayer"); foreach (var a; ac ~ mp) { var contact = a.getNode("refuel/contact", 1).getValue(); var tanker = a.getNode("tanker", 1).getValue(); if (tanker and contact) append(tankers, a); } } var refueling = serviceable and size(tankers) > 0; refuelingN.setBoolValue(refueling); if (fuel_freeze) return settimer(update_loop, UPDATE_PERIOD); # sum up consumed fuel var consumed = 0; foreach (var e; engines) { var fuel = e.getNode("fuel-consumed-lbs"); consumed += fuel.getValue(); fuel.setDoubleValue(0); } # calculate fuel received if (refueling) { # assume max flow rate is 6000 lbs/min (for KC135) var received = 100 * UPDATE_PERIOD; consumed -= received; } # 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; } elsif (consumed >= 0) { var fuel_per_tank = consumed / 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 -= fuel_per_tank; 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); } } else { #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 fuel_per_tank = -consumed / 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; lbs += fuel_per_tank; if (lbs > capacity) lbs = capacity; t.getNode("level-gal_us").setDoubleValue(lbs / ppg); t.getNode("level-lbs").setDoubleValue(lbs); } # print ("available ", available , " fuel_per_tank " , fuel_per_tank); } } 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); } setlistener("/sim/signals/fdm-initialized", func { if (contains(globals, "fuel") and typeof(fuel) == "hash") fuel.loop = func {} # kill $FG_ROOT/Nasal/fuel.nas' loop 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); setlistener("systems/refuel/serviceable", func { serviceable = cmdarg().getBoolValue() }, 1); update_loop(); });