# Dornier DO228(-212 NG) Electrical System
# Bea Wolf (D-ECHO) 2020
# Reference(s): see do228-212.xml

# based on turboprop engine electrical system by Syd Adams    ####

# Power Consumption References:
#		No.		Instrument		URL
#		E1		ACU-6100		https://www.becker-avionics.com/wp-content/uploads/00_Literature/ACU6100_IO.pdf

#	Ref. [1] p.7-95ff. :
# 	DC System
#		normal voltage: 28V
#		power supply:
#			* engine-driven starter generators ( left and right ) ( each 28V, 300A )
#			* two batteries ( each 24V, 43Ah )
#			* external power ( max. 28V, 1000A )
#		Buses:
#			* Hot Bus 8PP ( connected to battery 1 )
#			* Hot Bus 9PP ( connected to battery 2 )
#			* Battery Bus
#			* Bus 1 - 1PP
#			* Bus 2 - 2PP
#			* 3PP
#			* 4PP
#			* 5PP
#			* 6PP
#			* Ess Bus 7PP
#			* 10PP
#			* Avionics Bus 7PP
#			* Avionics Bus 4PP
#			* Avionics Bus 3PP
#			* Avionics Bus 5PP
#
#	AC System: typically not installed ( ref. [1] p.7-96 )

# Basic props.nas objects
var electrical = props.globals.getNode("systems/electrical");
var electrical_sw = electrical.initNode("internal-switches");
var output = electrical.getNode("outputs");
var breakers = props.globals.initNode("/controls/circuit-breakers");
var controls = props.globals.getNode("/controls/electric");
var light_ctrl = props.globals.getNode("/controls/lighting");
var deice = props.globals.initNode("/controls/de-ice");
var fuel = props.globals.initNode("/controls/fuel");
var engine0 = props.globals.getNode("/controls/engines/engine[0]");
var engine1 = props.globals.getNode("/controls/engines/engine[1]");

# Helper functions
var check_or_create = func ( prop, value, type ) {
	var obj = props.globals.getNode(prop, 1);
	if( obj.getValue() == nil ){
		return props.globals.initNode(prop, value, type);
	} else {
		return obj;
	}
}

#	Switches
var switches = {
	master:		controls.initNode("master",		0,	"BOOL"),
	#	Battery switch mapping: -1 = EXT; 0 = OFF; 1 = ON
	batt1:		controls.initNode("batt1",		0,	"INT"),
	batt2:		controls.initNode("batt2",		0,	"INT"),
	#	Generator switch mapping: -1 = RESET; 0 = OFF; 1 = ON
	generator0:	controls.initNode("generator[0]",	0,	"INT"),
	generator1:	controls.initNode("generator[1]",	0,	"INT"),
	#	Tie switch mapping:	-1 = BATT Bus 1; 0 = TIE; 1 = BATT Bus 2
	tie:		controls.initNode("tie",		0,	"INT"),
	nonessbus:	controls.initNode("non-ess-bus",	1,	"BOOL"),	# guarded in the ON position
	#	Voltmeter selection switch mapping:	-1 = Gen LH; 0 = ESS Bus; 1 = Gen RH
	voltmeter_sel:	controls.getNode("voltmeter-sel"),	# already initialized in -main.xml because it is used in a JSBSim system file
	avionics:	controls.initNode("avionics",		0,	"BOOL"),
	vhf1_hot:	controls.initNode("vhf1-hot",		0,	"BOOL"),
	fds_ap:		controls.initNode("fds-ap",		0,	"BOOL"),
	#	Standby battery switch mapping: -1 = Test; 0 = OFF; 1 = ON
	stby_batt:	controls.initNode("stby-batt",		0,	"INT"),
	#	Landing light switch mapping: -1 = Taxi; 0 = OFF; 1 = Land
	landing_light:	light_ctrl.initNode("landing-lights-sw",0,	"INT"),		# /controls/lighting/landing-lights is forced to be a boolean
	nav_light:	light_ctrl.initNode("navigation-lights",0,	"BOOL"),
	strobe_light:	light_ctrl.initNode("strobe-lights",	0,	"BOOL"),
	anticoll_light:	light_ctrl.initNode("anti-coll-lights",	0,	"BOOL"),
	#	Surface light switch mapping: -1 = Logo; 0 = OFF; 1 = Inspect
	surface_light:	light_ctrl.initNode("surface-lights",	0,	"INT"),
	cockpit_light:	light_ctrl.initNode("cockpit-lights",	0,	"BOOL"),
	#	Cabin light switch mapping: -1 = "HALF" (= only left row); 0 = OFF; 1 = "FULL" (= left and right row)
	cabin_light:	light_ctrl.initNode("cabin-lights",	0,	"INT"),
	seat_belt:	light_ctrl.initNode("seat-belt",	0,	"BOOL"),
	no_smoke:	light_ctrl.initNode("no-smoke",		0,	"BOOL"),
	panel_light:	light_ctrl.initNode("panel-lights[1]",	0.0,	"DOUBLE"),
	instr_light:	light_ctrl.initNode("instr-lights-sum",	0.0,	"DOUBLE"),	# see Systems/do228-electrical.xml
	#			De-Ice
	pitot0:		deice.initNode("pitot[0]",		0,	"BOOL"),
	pitot1:		deice.initNode("pitot[1]",		0,	"BOOL"),
	prop0:		deice.initNode("prop[0]",		0,	"BOOL"),
	prop1:		deice.initNode("prop[1]",		0,	"BOOL"),
	inlet0:		deice.initNode("inlet[0]",		0,	"BOOL"),
	inlet1:		deice.initNode("inlet[1]",		0,	"BOOL"),
	#	Windshield and Surface De-Ice switch mapping: 0 = OFF; 1 = LO; 2 = HIGH
	windshield:	deice.initNode("windshield",		0,	"INT"),
	surface:	deice.initNode("surface",		0,	"INT"),
	#			Fuel
	fp_lh0:		fuel.initNode("fuel-pump-lh[0]",	0,	"BOOL"),
	fp_lh1:		fuel.initNode("fuel-pump-lh[1]",	0,	"BOOL"),
	fp_rh0:		fuel.initNode("fuel-pump-rh[0]",	0,	"BOOL"),
	fp_rh1:		fuel.initNode("fuel-pump-rh[1]",	0,	"BOOL"),
	#			Engine Controls
	starter0s:	engine0.initNode("starter-switch",	0,	"INT"),
	starter1s:	engine1.initNode("starter-switch",	0,	"INT"),
	starter0i:	engine0.initNode("starter-int",		0,	"BOOL"),
	starter1i:	engine1.initNode("starter-int",		0,	"BOOL"),
	starter0c:	engine0.getNode("starter"),
	starter1c:	engine1.getNode("starter"),
	#	Start selector mapping: -1 = AIR; 0 = VENT; 1 = GROUND
	start_selector:	props.globals.initNode("/controls/engines/start-selector",	0,	"INT"),
};

#	TODO calculate prop heating amps correctly
var prop_ht_amps0 = props.globals.initNode("systems/electrical/eicas/prop-heating-amps[0]", 25.0, "DOUBLE");
var prop_ht_amps1 = props.globals.initNode("systems/electrical/eicas/prop-heating-amps[1]", 25.0, "DOUBLE");

var strobeLight = aircraft.light.new("/sim/model/lights/strobe", [0.08, 2.0], switches.strobe_light);
var antiCollLight = aircraft.light.new("/sim/model/lights/anti-coll", [0.06, 2.0], switches.anticoll_light);

var int_switches = {
	strobe_light:	props.globals.getNode("/sim/model/lights/strobe/state", 1),
	anticoll_light:	props.globals.getNode("/sim/model/lights/anti-coll/state", 1),
	cabin_signs:	electrical_sw.initNode("cabin-signs",		0,	"BOOL"),
};

var bus_breakers = {
	bw:	[
		breakers.initNode("bw[0]",	1,	"BOOL"),	# Non Ess Bus - Source Bus 1 ( Ref. [1], p. 7-100 )
		breakers.initNode("bw[1]",	1,	"BOOL"),	# 28V DC Bus 2 - Source Bus 2 ( Ref. [1], p. 7-102 )
	],
	ew:	[
		breakers.initNode("ew[0]",	1,	"BOOL"),	# Ess Bus - Source Batt Bus ( Ref. [1], p.7-103 )
		breakers.initNode("ew[1]",	1,	"BOOL"),	# Ess Bus - Source Batt Bus ( Ref. [1], p.7-103 )
		breakers.initNode("ew[2]",	1,	"BOOL"),	# Ess Bus - Source Batt Bus ( Ref. [1], p.7-103 )
	],
	non_ess_bus:	breakers.initNode("non-ess-bus",	1,	"BOOL"),
	avionics_mstr:	breakers.initNode("avionics-master",	1,	"BOOL"),
};

var dc_ext_flag	=	props.globals.initNode("/systems/electrical/DC/external-power",0,"BOOL");
var delta_sec	=	props.globals.getNode("sim/time/delta-sec");

## Lights
#				EXTERIOR
#	Landing Lights
#		LH	systems/electrical/outputs/landing-light[0]
#		RH	systems/electrical/outputs/landing-light[1]
#	Taxi Lights
#		LH	systems/electrical/outputs/taxi-light[0]
#		RH	systems/electrical/outputs/taxi-light[1]
#	Navigation Lights
#			systems/electrical/outputs/navigation-lights
#	Strobe Lights (ref. [1] p.7-112: interval 2 secs)
#			systems/electrical/outputs/strobe-lights
#	Anti-Collision Lights
#			systems/electrical/outputs/anti-collision-lights
#		https://www.youtube.com/watch?v=ItQ47jHFkNE: interval is approx. 2 secs (00:08.208 to 00:10.261)
#							duration of flash is max. 0.242-0.175 = 0.067 secs, probably shorter
#	Surface Inspection Lights
#			systems/electrical/outputs/surface-light
#	Logo Lights
#			TODO
#	
#				INTERIOR
#	Cockpit Dome Lights
#			systems/electrical/outputs/dome-lights
#	Cabin Lights
#		LH	systems/electrical/outputs/cabin-lights[0]
#		RH	systems/electrical/outputs/cabin-lights[1]
#	Cabin Signs ( No-Smoking and Seatbelts)
#			systems/electrical/outputs/cabin-signs
#	Instrument Lights
#			systems/electrical/outputs/instrument-lights


var check_cabin_signs = func() {
	if( switches.seat_belt.getBoolValue() or switches.no_smoke.getBoolValue() ){
		int_switches.cabin_signs.setBoolValue( 1 );
	} else {
		int_switches.cabin_signs.setBoolValue( 0 );
	}
}

setlistener( switches.seat_belt, func() {
	check_cabin_signs();
});

setlistener( switches.no_smoke, func() {
	check_cabin_signs();
});



#	TODO calculate battery temperature correctly
Battery = {
	new : func( switch, breaker, volt, amps, amp_hours, charge_percent, charge_amps, n){
		m = { parents : [Battery] };
		m.switch = props.globals.initNode(switch, 1, "BOOL");
		m.temp = electrical.initNode("battery-temperature["~n~"]", 15.0, "DOUBLE");
		m.breaker = breaker;
		m.ideal_volts = volt;
		m.ideal_amps = amps;
		m.volt_p = electrical.initNode("battery-volts["~n~"]", 0.0, "DOUBLE");
		m.amp_hours = amp_hours;
		m.charge_percent = charge_percent; 
		m.charge_amps = charge_amps;
		return m;
	},
	apply_load : func(load,dt) {
		if( me.switch.getBoolValue() and me.breaker.getBoolValue() ){
			var amphrs_used = load * dt / 3600.0;
			var percent_used = amphrs_used / me.amp_hours;
			me.charge_percent -= percent_used;
			if ( me.charge_percent < 0.0 ) {
				me.charge_percent = 0.0;
			} elsif ( me.charge_percent > 1.0 ) {
				me.charge_percent = 1.0;
			}
			var output =me.amp_hours * me.charge_percent;
			return output;
		}else return 0;
	},
	
	get_output_volts : func {
		if( me.switch.getBoolValue() and me.breaker.getBoolValue() ){
			var x = 1.0 - me.charge_percent;
			var tmp = -(3.0 * x - 1.0);
			var factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
			var output =me.ideal_volts * factor;
			me.volt_p.setDoubleValue( output );
			return output;
		}else return 0;
	},
	
	get_output_amps : func {
		if( me.switch.getBoolValue() and me.breaker.getBoolValue() ){
			var x = 1.0 - me.charge_percent;
			var tmp = -(3.0 * x - 1.0);
			var factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
			var output =me.ideal_amps * factor;
			return output;
		}else return 0;
	}
};

# var alternator = Alternator.new(num,switch,rpm_source,rpm_threshold,volts,amps);
Alternator = {
	new : func (num,switch,src,thr,vlt,amp){
		m = { parents : [Alternator] };
		m.switch =  switch;
		m.meter =  props.globals.getNode("systems/electrical/gen-load["~num~"]",1);
		m.meter.setDoubleValue(0);
		m.gen_output =  props.globals.getNode("engines/engine["~num~"]/amp-v",1);
		m.gen_output.setDoubleValue(0);
		m.meter.setDoubleValue(0);
		m.rpm_source =  props.globals.getNode(src,1);
		m.rpm_threshold = thr;
		m.ideal_volts = vlt;
		m.ideal_amps = amp;
		return m;
	},
	
	apply_load : func(load) {
		var cur_volt=me.gen_output.getValue();
		var cur_amp=me.meter.getValue();
		if(cur_volt >1){
			var factor=1/cur_volt;
			gout = (load * factor);
			if(gout>1)gout=1;
		}else{
			gout=0;
		}
		if(cur_amp > gout)me.meter.setValue(cur_amp - 0.01);
		if(cur_amp < gout)me.meter.setValue(cur_amp + 0.01);
	},
	
	get_output_volts : func {
		var out = 0;
		if(me.switch.getIntValue() == 1){
			var factor = me.rpm_source.getDoubleValue() / me.rpm_threshold;
			if ( factor > 1.0 )factor = 1.0;
			var out = (me.ideal_volts * factor);
		}
		me.gen_output.setValue(out);
		if (out > 1) return out;
		return 0;
	},
	
	get_output_amps : func {
		var ampout =0;
		if(me.switch.getBoolValue()){
			var factor = me.rpm_source.getValue() / me.rpm_threshold;
			if ( factor > 1.0 ) {
				factor = 1.0;
			}
			ampout = me.ideal_amps * factor;
		}
		return ampout;
	}
};

var battery1 = Battery.new("/systems/electrical/battery/serviceable[0]", breakers.initNode("batt1", 1, "BOOL"), 24, 30, 43, 1.0, 7.0, 0);
var battery2 = Battery.new("/systems/electrical/battery/serviceable[1]", breakers.initNode("batt2", 1, "BOOL"), 24, 30, 43, 1.0, 7.0, 1);

var generator0 = Alternator.new(0, switches.generator0, "/engines/engine[0]/n1", 55.0, 28.0, 60.0);
var generator1 = Alternator.new(1, switches.generator1, "/engines/engine[1]/n1", 55.0, 28.0, 60.0);

var consumer = {
	new: func( name, switch, load ){
		m = { parents : [consumer] };
		m.cb = breakers.initNode(name, 1, "BOOL");
		m.switch_type = "none";
		if( switch != nil ){
			m.switch = switch;
			if ( switch.getType() == "DOUBLE" ) {
				m.switch_type = "double";
			} else if ( switch.getType() == "BOOL" ) {
				m.switch_type = "bool";
			} else {
				die("Consumer (non-int) switch of unsupported type: "~ switch.getType() ~ "!");
			}
		} else {
			m.switch = nil;
		}
		m.output = output.initNode(name, 0.0, "DOUBLE");
		m.load = load;
		return m;
	},
	power: func( bus_volts ){
		if( me.cb.getBoolValue() and bus_volts != 0.0 ){
			if ( me.switch_type == "none" or ( me.switch_type == "bool" and me.switch.getBoolValue() ) ) {
				me.output.setDoubleValue( bus_volts );
				return me.load;
			} else if ( me.switch_type == "double" ) {
				me.output.setDoubleValue( bus_volts * me.switch.getDoubleValue() );
				return me.load * me.switch.getDoubleValue();
			} else {
				me.output.setDoubleValue( 0.0 );
				return 0.0;
			}
		} else {
			me.output.setDoubleValue( 0.0 );
			return 0.0;
		}
	},
};
# Consumer with support for integer switches
var consumer_int = {
	new: func( name, switch, load, int, mode ){
		m = { parents : [consumer_int] };
		m.cb = breakers.initNode(name, 1, "BOOL");
		if ( switch.getType() == "INT" ) {
			m.switch = switch;
			m.int = int;
			# Mode: 0 means "=="; 1 means "!="
			if( mode != nil ){
				m.mode = mode;
			} else {
				m.mode = 0;
			}
		} else {
			die("Consumer (int) switch of unsupported type: "~ switch.getType() ~ "!");
		}
		m.output = output.initNode(name, 0.0, "DOUBLE");
		m.load = load;
		return m;
	},
	power: func( bus_volts ){
		if( me.cb.getBoolValue() and bus_volts != 0.0 ){
			if ( ( ( me.mode == 0 and me.switch.getIntValue() == me.int ) or ( me.mode == 1 and me.switch.getIntValue() != me.int ) ) ) {
				me.output.setDoubleValue( bus_volts );
				return me.load;
			} else {
				me.output.setDoubleValue( 0.0 );
				return 0.0;
			}
		} else {
			me.output.setDoubleValue( 0.0 );
			return 0.0;
		}
	},
};

var bus = {
	new: func( name, on_update, consumers ) {
		m = { parents : [bus] };
		m.name = name;
		m.volts = check_or_create("systems/electrical/bus/" ~ name ~ "-volts", 0.0, "DOUBLE");
		m.serviceable = check_or_create("systems/electrical/bus/" ~ name ~ "-serviceable", 1, "BOOL");
		m.on_update = on_update;
		m.bus_volts = 0.0;
		m.consumers = consumers;
		return m;
	},
	update_consumers: func () {
		#print("Update consumers of bus "~ me.name);
		load = 0.0;
		foreach( var c; me.consumers ) {
			load += c.power( me.bus_volts );
		}
		return load;
	},
};

var batt_bus_from_hot_8pp = 0.0;
var batt_bus_from_hot_9pp = 0.0;

var hot_bus_8pp = bus.new( 
	"hot-bus-8pp", 
	func() {
		if( me.serviceable.getBoolValue() and switches.batt1.getIntValue() == 1 ){
			me.bus_volts = battery1.get_output_volts();
		} else {
			me.bus_volts = 0.0;
		}
		var load = me.update_consumers();
		load += batt_bus_from_hot_8pp;
		
		battery1.apply_load( load, delta_sec.getDoubleValue() );
		
		me.volts.setDoubleValue( me.bus_volts );   
	},
	[	
		consumer.new( "reu", nil, 0.1 ),
		consumer.new( "rcu[0]", nil, 0.1 ),
		consumer.new( "fire-lh", nil, 0.1 ),
		consumer.new( "fire-rh", nil, 0.1 ),
		consumer.new( "cock-lh", nil, 0.1 ),
		consumer.new( "cock-rh", nil, 0.1 ),
		consumer.new( "acu[0]", nil, 0.13 ),	# ref. E1 p.19: <= 150mA incl. illumination
		consumer.new( "elt", nil, 0.1 ),
		consumer.new( "vhf[0]", nil, 0.1 ),
		consumer.new( "light-emerg", nil, 0.1 ),
		consumer.new( "light-hydr", nil, 0.1 ),
		consumer.new( "refuel", nil, 0.1 ),
	],
);

var hot_bus_9pp = bus.new( 
	"hot-bus-9pp", 
	func() {
		if( me.serviceable.getBoolValue() and switches.batt2.getIntValue() == 1 ){
			me.bus_volts = battery2.get_output_volts();
		} else {
			me.bus_volts = 0.0;
		}
		
		var load = 0.0;
		load += batt_bus_from_hot_9pp;
		
		battery2.apply_load( load, delta_sec.getDoubleValue() );
		
		me.volts.setDoubleValue( me.bus_volts );   
	},
	[],
);

var battery_bus = bus.new(
	"battery-bus",
	func() {
		var powered_by = "";
		if( me.serviceable.getBoolValue() ){
			if( switches.master.getBoolValue() and hot_bus_8pp.volts.getDoubleValue() > 15.0 and hot_bus_9pp.volts.getDoubleValue() > 15.0 ){
				me.bus_volts = ( hot_bus_8pp.volts.getDoubleValue() + hot_bus_9pp.volts.getDoubleValue() ) / 2;
				powered_by = "both_batteries";
			} else if ( switches.master.getBoolValue() and hot_bus_8pp.volts.getDoubleValue() > 15.0 ) {
				me.bus_volts = hot_bus_8pp.volts.getDoubleValue();
				powered_by = "battery1";
			} else if ( switches.master.getBoolValue() and hot_bus_9pp.volts.getDoubleValue() > 15.0 ) {
				me.bus_volts = hot_bus_9pp.volts.getDoubleValue();
				powered_by = "battery2";
			} else {
				me.bus_volts = 0.0;
				powered_by = "nothing";
			}
		} else {
			me.bus_volts = 0.0;
		}
		
		var load = 0.0;
		
		load += ess_bus_7pp.on_update( me.bus_volts );
		load += bus1_1pp.on_update( me.bus_volts );
		load += bus2_2pp.on_update( me.bus_volts );
		
		if( powered_by == "both_batteries" ){
			batt_bus_from_hot_8pp = load / 2;
			batt_bus_from_hot_9pp = load / 2;
		} else if ( powered_by == "battery1" ){
			batt_bus_from_hot_8pp = load;
		} else if ( powered_by == "battery2" ){
			batt_bus_from_hot_9pp = load;
		}
		
		me.volts.setDoubleValue( me.bus_volts );
	},
	[],
);

var bus1_1pp = bus.new(
	"bus1-1pp",
	func ( bv ) {
		var load = 0.0;
		var powered_by = "";
		me.bus_volts = 0.0;
		if( me.serviceable.getBoolValue() ){
			if( switches.generator0.getBoolValue() and generator0.get_output_volts() != 0.0 ){
				me.bus_volts = generator0.get_output_volts();
				powered_by = "generator0";
			}
			if ( switches.tie.getIntValue() <= 0 and bv > me.bus_volts ){
				me.bus_volts = bv;
				powered_by = "battery_bus";
			}
		}
		
		load += me.update_consumers();
		
		if( switches.starter0i.getBoolValue() and me.bus_volts > 20 ){
			load += 150;
			switches.starter0c.setBoolValue( 1 );
		} else {
			switches.starter0c.setBoolValue( 0 );
		}
		
		if( powered_by == "generator0" ){
			if( switches.tie.getIntValue() <= 0 ){
				generator0.apply_load( load + 1.0 );
				return -1.0;
			} else {
				generator0.apply_load( load );
				return 0.0;
			}
		} else if ( powered_by == "battery_bus" ){
			return load;
		}
		
		me.volts.setDoubleValue( me.bus_volts );
	},
	[
		consumer.new( "reu-bak", nil, 0.1 ),
		consumer.new( "acu-bak[0]", nil, 0.1 ),
		consumer.new( "acu-bak[1]", nil, 0.1 ),
		#consumer.new( "dme[0], nil, 0.1 ); non-standard equipment
		#consumer.new( "radio-altimeter[0]", nil, 0.1 ); non-standard equipment
		consumer.new( "fms", nil, 0.1 ),
		consumer.new( "dtu", nil, 0.1 ),
		consumer.new( "lpv-mon", nil, 0.1 ),
		consumer.new( "elt-nav-if", nil, 0.1 ),
		consumer.new( "instrument-lights", switches.instr_light, 0.05 ),
		consumer_int.new( "cabin-lights", switches.cabin_light, 0.1, 0, 1 ),
		consumer_int.new( "landing-light[1]", switches.landing_light, 0.1, 1, 0 ),
		consumer.new( "navigation-lights", switches.nav_light, 0.1 ),
		consumer.new( "strobe-lights", int_switches.strobe_light, 0.1 ),
		consumer.new( "nws", nil, 0.1 ),
		consumer.new( "ram-air-bleed-rh", nil, 0.1 ),
		consumer_int.new( "windshield-heat-htr[0]", switches.windshield, 0.1, 0, 1 ),
		consumer_int.new( "windshield-heat-ctl[0]", switches.windshield, 0.1, 0, 1 ),
		consumer.new( "ap-servos", nil, 0.1 ),
		consumer.new( "egt-comp[0]", nil, 0.01 ),
		consumer.new( "trq[0]", nil, 0.01 ),
		consumer.new( "trq-limit[0]", nil, 0.01 ),
		consumer.new( "lp-pump-1-lh", switches.fp_lh0, 0.01 ),
		consumer.new( "lp-pump-2-rh", switches.fp_rh1, 0.01 ),
		consumer.new( "lp-pump-ctl-1-lh", switches.fp_lh0, 0.01 ),
		consumer.new( "lp-pump-ctl-2-rh", switches.fp_rh1, 0.01 ),
		consumer.new( "fuel-flow[0]", nil, 0.01 ),
		consumer.new( "fuel-quantity[0]", nil, 0.01 ),
		consumer.new( "fire-cock-open[0]", nil, 0.01 ),
		consumer.new( "pax-amp", nil, 0.01 ),
		consumer.new( "dig-play", nil, 0.01 ),
		consumer.new( "caws", nil, 0.01 ),
		consumer.new( "wipers", nil, 0.1 ),
		consumer.new( "prop-heat-htr[0]", switches.prop0, 0.1 ),
		consumer.new( "prop-heat-ctl[0]", switches.prop0, 0.1 ),
		consumer.new( "inlet-anti-ice[0]", switches.inlet0, 0.1 ),
		consumer.new( "pitot-heating[0]", switches.pitot0, 0.1 ),
		consumer.new( "rsd-htr", nil, 0.1 ),
		# Service Panel
		consumer.new( "ah-meter", nil, 0.01 ),
		consumer.new( "aircon-hour-counter", nil, 0.01 ),
		consumer.new( "hyd-hour-counter", nil, 0.01 ),
	],
);

var bus2_2pp = bus.new(
	"bus2-2pp",
	func ( bv ) {
		var load = 0.0;
		var powered_by = "";
		me.bus_volts = 0.0;
		if( me.serviceable.getBoolValue() ){
			if( switches.generator1.getBoolValue() and generator1.get_output_volts() != 0.0 ){
				me.bus_volts = generator1.get_output_volts();
				powered_by = "generator1";
			}
			if ( switches.tie.getIntValue() >= 0 and bv > me.bus_volts ){
				me.bus_volts = bv;
				powered_by = "battery_bus";
			}
		}
		
		load += me.update_consumers();
		
		if( switches.starter1i.getBoolValue() and me.bus_volts > 20 ){
			load += 150;
			switches.starter1c.setBoolValue( 1 );
		} else {
			switches.starter1c.setBoolValue( 0 );
		}
		
		if( powered_by == "generator1" ){
			if( switches.tie.getIntValue() >= 0 ){
				generator1.apply_load( load + 1.0 );
				return -1.0;
			} else {
				generator1.apply_load( load );
				return 0.0;
			}
		} else if ( powered_by == "battery_bus" ){
			return load;
		}
		
		me.volts.setDoubleValue( me.bus_volts );
	},
	[
		consumer.new( "pfd-disp[1]", nil, 0.01 ),
		consumer.new( "pfd-ctl[1]", nil, 0.01 ),
		consumer.new( "mfd-disp[1]", nil, 0.01 ),
		consumer.new( "mfd-ctl[1]", nil, 0.01 ),
		consumer.new( "egt-comp[1]", nil, 0.01),
		consumer.new( "trq[1]", nil, 0.01 ),
		consumer.new( "trq-limit[1]", nil, 0.01 ),
		consumer.new( "lp-pump-2-lh", switches.fp_lh1, 0.01 ),
		consumer.new( "lp-pump-1-rh", switches.fp_rh0, 0.01 ),
		consumer.new( "lp-pump-ctl-2-lh", switches.fp_lh1, 0.01 ),
		consumer.new( "lp-pump-ctl-1-rh", switches.fp_rh0, 0.01 ),
		consumer.new( "fuel-flow[1]", nil, 0.01 ),
		consumer.new( "fuel-quantity[1]", nil, 0.01 ),
		consumer.new( "fire-cock-open[1]", nil, 0.01 ),
		consumer.new( "cabin-signs", int_switches.cabin_signs, 0.01 ), # Seatbelt and No-Smoking
		consumer.new( "console-panel-lights", switches.panel_light, 0.01 ),
		consumer.new( "anti-coll-lights", int_switches.anticoll_light, 0.1 ),
		consumer_int.new( "surface-light", switches.surface_light, 0.1, 1, 0 ),
		consumer.new( "prop-heat-htr[1]", switches.prop1, 0.1 ),
		consumer.new( "prop-heat-ctl[1]", switches.prop1, 0.1 ),
		consumer.new( "inlet-anti-ice[1]", switches.inlet1, 0.1 ),
		consumer.new( "pitot-heating[1]", switches.pitot1, 0.1 ),
		consumer.new( "windshield-heat-bak-htr[0]", nil, 0.1 ),
		consumer.new( "windshield-heat-bak-ctl[0]", nil, 0.1 ),
		
	],
);

var non_ess_bus = bus.new(
	"non-ess-bus",
	func( bv ) {
		var load = 0.0;
		me.bus_volts = 0.0;
		if( me.serviceable.getBoolValue() and switches.nonessbus.getBoolValue() and bus_breakers.bw[0].getBoolValue() and bus_breakers.non_ess_bus.getBoolValue() ){
			me.bus_volts = bv;
		}
		
		load += me.update_consumers();
		
		return load;
	},
	[
		consumer.new( "dome-lights", switches.cockpit_light, 0.1 ),
		consumer_int.new( "cabin-lights[1]", switches.cabin_light, 0.1, 1, 0 ),
		consumer_int.new( "landing-light[0]",  switches.landing_light, 0.1, 1, 0 ),
		consumer_int.new( "taxi-light[0]", switches.landing_light, 0.1, -1, 0 ),
		consumer_int.new( "taxi-light[1]", switches.landing_light, 0.1, -1, 0 ),
		consumer.new( "air-cond-inv", nil, 0.1 ),
		consumer.new( "air-cond-bleed-lh", nil, 0.1 ),
		consumer.new( "ej-valve-heating-htr", nil, 0.1 ),
		consumer.new( "ej-valve-heating-ctl", nil, 0.1 ),
		consumer.new( "windshield-heat-rh-htr[1]", nil, 0.1 ),
		consumer.new( "windshield-heat-rh-ctl[1]", nil, 0.1 ),
		consumer.new( "sync-phaser", nil, 0.1 ),
		consumer_int.new( "logo-lights", switches.surface_light, 0.1, -1, 0 ),
	],
);

var ess_bus_7pp = bus.new(
	"ess-bus-7pp",
	func( bv ) {
		if( me.serviceable.getBoolValue() ){
			me.bus_volts = bv;
		} else {
			me.bus_volts = 0.0;
		}
		
		var load = 0.0;
		
		load += me.update_consumers();
		load += avionics_bus.on_update( me.bus_volts );
		load += non_ess_bus.on_update( me.bus_volts );
		
		me.volts.setDoubleValue( me.bus_volts );
		
		return load;
	},
	[	
		consumer.new( "pfd-disp[0]", nil, 0.01 ),
		consumer.new( "pfd-ctl[0]", nil, 0.01 ),
		consumer.new( "mfd-disp[0]", nil, 0.01 ),
		consumer.new( "mfd-ctl[0]", nil, 0.01 ),
		consumer.new( "dcu", nil, 0.01 ),
		consumer.new( "chp", nil, 0.01 ),
		consumer.new( "rsp", nil, 0.01 ),
		consumer.new( "reu", nil, 0.1 ),
		consumer.new( "ignition[0]", nil, 0.1 ),
		consumer.new( "ignition[1]", nil, 0.1 ),
		consumer.new( "fuel-shut-off[0]", nil, 0.1 ),
		consumer.new( "fuel-shut-off[1]", nil, 0.1 ),
		consumer.new( "cross-feed", nil, 0.1 ),
		consumer.new( "eiu[0]", nil, 0.1 ),
		consumer.new( "eiu[1]", nil, 0.1 ),
		consumer.new( "start-ctl[0]", nil, 0.1 ),
		consumer.new( "start-ctl[1]", nil, 0.1 ),
		consumer.new( "prop-system[0]", nil, 0.1 ),
		consumer.new( "prop-system[1]", nil, 0.1 ),
		consumer.new( "fire-warn[0]", nil, 0.1 ),
		consumer.new( "fire-warn[1]", nil, 0.1 ),
		consumer.new( "caws-ess", nil, 0.01 ), # Duplicate from Bus 1 - 1PP ?
		consumer.new( "adnt", nil, 0.01 ),
		consumer.new( "stall-warn", nil, 0.01 ),
		consumer.new( "flaps-ctl", nil, 0.01 ),
		consumer.new( "flaps-pos", nil, 0.01 ),
		consumer.new( "landing-gear-ctl", nil, 0.01 ),
		consumer.new( "landing-gear-valve", nil, 0.01 ),
		consumer.new( "landing-gear-warn", nil, 0.01 ),
		consumer.new( "hydraulic-norm", nil, 0.01 ),
		consumer.new( "hydraulic-man", nil, 0.01 ),
		consumer_int.new( "surf-de-ice", switches.surface, 0.01, 0, 1 ),
		consumer.new( "pitch-trim-down", nil, 0.01 ),
		consumer.new( "pitch-trim-up", nil, 0.01 ),
		consumer.new( "aileron-trim", nil, 0.01 ),
		consumer.new( "gen-reset[0]", nil, 0.01 ),
		consumer.new( "gen-reset[1]", nil, 0.01 ),
		consumer.new( "volt-meter", nil, 0.01 ),
		consumer.new( "stby-batt", nil, 0.01 ),
	],
);

var avionics_bus = bus.new(
	"avionics-bus",
	func( bv ) {
		if( me.serviceable.getBoolValue() and switches.avionics.getBoolValue() and bus_breakers.avionics_mstr.getBoolValue() ) {
			me.bus_volts = bv;
			me.volts.setDoubleValue( me.bus_volts );
		
			return me.update_consumers();
		} else if( switches.vhf1_hot.getBoolValue() ){
			me.volts.setDoubleValue( 0.0 );
			
			var load = 0.0;
			#	VHF1 Hot switch: Only power RCU1, ACU1, REU, VHF1
			load += me.consumers[0].power( bv );
			load += me.consumers[2].power( bv );
			load += me.consumers[4].power( bv );
			load += me.consumers[9].power( bv );
			
			me.bus_volts = 0.0;
			me.update_consumers;
			
			return load;
		} else {
			me.volts.setDoubleValue( 0.0 );
			
			me.bus_volts = 0.0;
			me.update_consumers;
			
			return 0.0;
		}		
	},
	[	
		consumer.new( "rcu[0]",		nil, 0.1 ),
		consumer.new( "rcu[1]",		nil, 0.1 ),
		consumer.new( "acu[0]",		nil, 0.1 ),
		consumer.new( "acu[1]",		nil, 0.1 ),
		consumer.new( "reu",		nil, 0.1 ),
		consumer.new( "wx-rdr",		nil, 0.1 ),
		consumer.new( "tcas",		nil, 0.1 ),
		consumer.new( "radio-alt[0]",	nil, 0.1 ),
		consumer.new( "radio-alt[1]",	nil, 0.1 ),
		consumer.new( "comm[0]",	nil, 0.1 ),
		consumer.new( "comm[1]",	nil, 0.1 ),
		consumer.new( "adc[0]",		nil, 0.1 ),
		consumer.new( "adc[1]",		nil, 0.1 ),
		consumer.new( "ahrs[0]",	nil, 0.1 ),
		consumer.new( "ahrs[1]",	nil, 0.1 ),
		consumer.new( "efis",		nil, 0.1 ),
		consumer.new( "ctl",		nil, 0.1 ),
		consumer.new( "taws",		nil, 0.1 ),
		consumer.new( "nav[0]",		nil, 0.1 ),
		consumer.new( "nav[1]",		nil, 0.1 ),
		consumer.new( "adf[0]",		nil, 0.1 ),
		consumer.new( "adf[1]",		nil, 0.1 ),
		consumer.new( "dme[0]",		nil, 0.1 ),
		consumer.new( "transponder[0]",	nil, 0.1 ),
		consumer.new( "fdr",		nil, 0.1 ),
		consumer.new( "cvr",		nil, 0.1 ),
		consumer.new( "fds-ap",		switches.fds_ap, 0.1 ),
	],
);


update_electrical = func {
	var scnd = delta_sec.getDoubleValue();
	
	hot_bus_8pp.on_update();
	hot_bus_9pp.on_update();
	battery_bus.on_update();
	
	
	# Flaps only operate when electrically powered:
	var flaps_ctl_power = props.globals.getNode("systems/electrical/outputs/flaps-ctl");
	var cmd_flaps = props.globals.getNode("controls/flight/flaps");
	var int_flaps = props.globals.getNode("controls/flight/flaps-int");
	if( flaps_ctl_power.getDoubleValue() > 15 and cmd_flaps.getDoubleValue() != int_flaps.getDoubleValue() ){
		int_flaps.setDoubleValue( cmd_flaps.getDoubleValue() );
	}
}

var electrical_updater = maketimer( 0.0, update_electrical );
electrical_updater.simulatedTime = 1;
electrical_updater.start();
