Howto:Basic OOP Programming

From FlightGear wiki
Jump to navigation Jump to search

First, we need to define some variables at the very beginning of the nasal file. This improves performance, since if you use "var" inside a loop, it degrades performance: the variable is recreated every time the loop is run!.

# Define variables here to prevent them being recreated at every run of the loop() function
var power_consumption = nil;
var screen_power_consumption = nil;
var screens = nil;

# set the dimming properties to reasonable values, in order to be able to test the code
setprop("/controls/lighting/DU/du1", 0.5);
setprop("/controls/lighting/DU/du2", 0.6);
setprop("/controls/lighting/DU/du3", 0.9);
setprop("/controls/lighting/DU/du4", 0.1);
setprop("/controls/lighting/DU/du5", 1.0);
setprop("/controls/lighting/DU/du6", 0.4);

Then, we need to create the class for the screen: we define some attributes, then add a function that calculates the power consumption. Finally, at the end we have the constructor which can be used to create the object over and over by simply running screen.new(); with the attributes defined inside the brackets.

var screen = {
        # first, create the attributes of the screen
	name: "", # a string. Note the comma at the end, it is important
	type: "", 
	max_watts: 0, # a number or "double"
	dim_watts: 0, 
	dim_prop: "", # a property: defined in the same format as the string. We could run getprop(me.dim_prop) to fetch the contents of the format.

        # next we have a function that measures the power consumption and returns it

	power_consumption: func() { 
		var dim_prop = me.dim_prop; # define the dim_prop attribute of this class as a variable, so we can just do getprop(dim_prop)

		if (getprop(me.dim_prop) != 0) { # if the screen is not off
			screen_power_consumption = (50 + (10 * getprop(dim_prop))); # calculate the power consumed by the string
		} else { # if the screen is off, it consumes no power.
			screen_power_consumption = 0; # so we set screen_power_consumption to 0
		} 

		return screen_power_consumption; # this means that when you run this function, you will get a value returned to you as a number.

	}, # again we have a comma closing the brackets

	new: func(name,type,max_watts,dim_watts,dim_prop) { # finally the creator function, that creates instances of the class
		var s = {parents:[screen]}; # the instances will each be a "copy" of the screen class: screen is the "parent"
		
		s.name = name; # associate the func(name...) with the name attribute of the screen class
		s.type = type;
		s.max_watts = max_watts;
		s.dim_watts = dim_watts;
		s.dim_prop = dim_prop;
		
		return s;
	}, # new() constructor 
};

Next, we create a new object called ELEC.

In the first part, we have a function called whenever the FDM initialization is complete: inside it, various instances of the screen class are created using the new() constructor. Then, in the second part, we have a loop function that actually does the hard work of calculating the power consumption per screen.

var ELEC = {
	init: func() { # initialization function
              # Create the instances of the screen class, inside a vector so we can use a foreach loop later
              # format: screen.new(name: "XX", type:"XX", max_watts:XX, dim_watts:XX, dim_prop:"foo/bar"),

              screens = [screen.new(name: "DU1", type:"LCD", max_watts:60, dim_watts:50, dim_prop:"controls/lighting/DU/du1"),
			screen.new(name: "DU2", type:"LCD", max_watts:60, dim_watts:50, dim_prop:"controls/lighting/DU/du2"),
			screen.new(name: "DU3", type:"LCD", max_watts:60, dim_watts:50, dim_prop:"controls/lighting/DU/du3"),
			screen.new(name: "DU4", type:"LCD", max_watts:60, dim_watts:50, dim_prop:"controls/lighting/DU/du4"),
			screen.new(name: "DU5", type:"LCD", max_watts:60, dim_watts:50, dim_prop:"controls/lighting/DU/du5"),
			screen.new(name: "DU6", type:"LCD", max_watts:60, dim_watts:50, dim_prop:"controls/lighting/DU/du6")]; 

        },
        loop: func() {
               foreach(var screena; screens) {  # note that we use a different variable, screena; essentially foreach(var a; b) associates var a with vector b 
			power_consumption = screena.power_consumption(); # call the power_consumption() for each instance of the screen class
			setprop("/systems/electrical/DU/" ~ screena.name ~ "/watts",power_consumption); # write the result to a property
	       }
        },
};

Finally, we add the updating function: the following code creates a maketimer and a setlistener that execute the loop(); and init(); functions respectively: we remove the listener to make sure it does not run repeatedly.

var Loop = maketimer(0.1, func {
ELEC.loop(); # runs the loop(); function every 0.1 seconds
});

var l = setlistener("/sim/signals/fdm-initialized", func { # listens to this property. When it changes, the following lines are run, then the listener is removed.
   ELEC.init(); # run the init loop to set things up
   timer.start(); # start the timer.
   removelistener(l); # ensure that the listener only runs once to improve performance
});


This is a basic example of OOP. This code should be reasonably standalone, in that it should work without depending on other code. For instance, to make a test of the code, you could setprop the DU dimming properties at the beginning of the file to reasonable values, as follows, then run it using the UFO. Or, in the Nasal console, you could just copy the code snippes to the console ( skipping the setlistener), then add this at the bottom and execute.

ELEC.init();
timer.start();

If you then hooked up the dimming properties to 3D knobs in any aircraft you could control the dimming and thus the power consumption by rotating the knobs.