From f92a2217de1b9f8e0b7cb2417bd11e93e1ffefc1 Mon Sep 17 00:00:00 2001
From: Anton Gomez Alvedro <galvedro@gmail.com>
Date: Sat, 29 Nov 2014 11:53:26 +0100
Subject: [PATCH] Factoring out the soaring-sdk's update loop into a generic
 update-loop.nas that can be reused by other modules.

---
 .../Generic/soaring-instrumentation-sdk.nas   | 107 ++++---------
 Aircraft/Generic/update_loop.nas              | 147 ++++++++++++++++++
 .../glider/vario/ilec-sc7/ilec-sc7.nas        |   4 +-
 3 files changed, 176 insertions(+), 82 deletions(-)
 create mode 100644 Aircraft/Generic/update_loop.nas

diff --git a/Aircraft/Generic/soaring-instrumentation-sdk.nas b/Aircraft/Generic/soaring-instrumentation-sdk.nas
index de085bc6a..95b8cd77e 100644
--- a/Aircraft/Generic/soaring-instrumentation-sdk.nas
+++ b/Aircraft/Generic/soaring-instrumentation-sdk.nas
@@ -1,6 +1,20 @@
 # Glider Instrumentation Toolkit
-# Author: Anton Gomez Alvedro (galvedro)
-# Licensed under GNU GPL
+#
+# Copyright (C) 2013-2014 Anton Gomez Alvedro
+#
+# 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 the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 #
 # Features:
 #   + Total energy compensated variometer
@@ -14,16 +28,22 @@
 # - add wind correction to speed-to-fly
 # - final glide computer
 
+io.include("update_loop.nas");
+
 var MPS2KPH = 3.6;
 var sqr = func(x) {x * x}
 
 
 var InstrumentComponent = {
+	parents: [Component],
 	output: 0,
-	init: func { me.output = 0 },
-	update: func(dt) { },
+	reset: func { me.output = 0 },
 };
 
+# This alias is for keeping backwards compatibility.
+# TODO: Refactor aircrafts that use it and remove this.
+var Instrument = UpdateLoop;
+
 # update_prop(property)
 # Helper generator for updating the given property on every element update
 #
@@ -141,7 +161,7 @@ var TotalEnergyProbe = {
 		};
 	},
 
-	init: func {
+	reset: func {
 		me.airspeed = getprop("/velocities/airspeed-kt") * KT2MPS;
 		me.altitude = getprop("/position/altitude-ft") * FT2M;
 		me.output = 0;
@@ -215,11 +235,11 @@ var Averager = {
 		m.sum = m.wp = 0;
 
 		m.buffer = setsize([], buffer_size);
-		m.init();
+		m.reset();
 		return m;
 	},
 
-	init: func {
+	reset: func {
 		me.sum = me.wp = me.output = 0;
 		forindex (var i; me.buffer)
 			me.buffer[i] = 0;
@@ -384,76 +404,3 @@ var SpeedCmdVario = {
 		if (me.on_update != nil) me.on_update(me.output);
 	}
 };
-
-# Instrument
-# Wraps a set of components and updates them periodically.
-# Takes care of critical sim signals (reinit, fdm-initialized, speed-up).
-#
-# var instrument = Instrument.new(
-#	components: List of components to update in the fast loop.
-#	update_period: (optional) Time in seconds between updates (fast components).
-#	enable: (optional) Enable instrument after creation.
-
-var Instrument = {
-
-	new: func(components, update_period = 0, enable = 1) {
-
-		var m = { parents: [me] };
-		m.initialized = 0;
-		m.enabled = enable;
-		m.update_period = update_period;
-		m.time_last = 0;
-		m.sim_speed = 1;
-		m.components = (components != nil)? components : [];
-
-		m.timer = maketimer(update_period,
-			func { call(me.update, [], m) });
-
-		setlistener("/sim/speed-up",
-			func(n) { m.sim_speed = n.getValue() }, 1, 0);
-
-		setlistener("sim/signals/reinit", func {
-			m.timer.stop();
-			m.initialized = 0;
-		});
-
-		setlistener("sim/signals/fdm-initialized", func {
-			if (m.timer.isRunning) m.timer.stop();
-			call(me.init, [], m);
-			if (m.enabled) m.timer.start();
-		});
-
-		return m;
-	},
-
-	init: func {
-		me.time_last = getprop("/sim/time/elapsed-sec");
-
-		foreach (var component; me.components)
-			component.init();
-
-		me.initialized = 1;
-	},
-
-	update: func {
-		var time_now = getprop("/sim/time/elapsed-sec");
-		var dt = (time_now - me.time_last) * me.sim_speed;
-		if (dt == 0) return;
-
-		me.time_last = time_now;
-
-		foreach (var component; me.components)
-			component.update(dt);
-	},
-
-	enable: func {
-		if (me.initialized) me.timer.start();
-		me.enabled = 1;
-	},
-
-	disable: func {
-		me.timer.stop();
-		me.enabled = 0;
-	}
-};
-
diff --git a/Aircraft/Generic/update_loop.nas b/Aircraft/Generic/update_loop.nas
new file mode 100644
index 000000000..b552f0df9
--- /dev/null
+++ b/Aircraft/Generic/update_loop.nas
@@ -0,0 +1,147 @@
+# update_loop.nas - Generic Nasal update loop for implementing sytems,
+# instruments or physics simulation.
+#
+# Copyright (C) 2014 Anton Gomez Alvedro
+#
+# 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 the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Component
+#
+# The update loop accepts a vector of Components that it will update at regular
+# intervals. In order to make custom components, you should add Component to
+# the parents vector of your object:
+#
+# var CustomComponent = {
+#	parents: [Component],
+#   ...
+# };
+#
+# Your component shall then implement an "update" and a "reset" methods that
+# the UpdateLoop will call when needed.
+
+var Component = {
+	reset: func { },
+	update: func(dt) { },
+};
+
+##
+# Helper generator for updating a property on every element update
+#
+# Example:
+#
+# var needle = Dampener.new(
+#	input: probe,
+#	dampening: 2.8,
+#	on_update: update_prop("/instrumentation/variometer/te-reading-mps"));
+#
+# See Aircraft/Instruments-3d/glider/vario/ilec-sc7.nas and
+# Aircraft/Generic/soaring-instrumentation-sdk.nas for usage examples.
+# You can also refer to the soaring sdk wiki page.
+
+var update_prop = func(property) {
+	func(value) { setprop(property, value) }
+};
+
+# UpdateLoop
+# Wraps a set of components and updates them periodically.
+# Takes care of critical sim signals (reinit, fdm-initialized, speed-up).
+
+var UpdateLoop = {
+
+	# UpdateLoop.new(components, [update_period], [enable])
+	#
+	# components:    Vector of components to update on every iteration.
+	# update_period: Time in seconds between updates (optional).
+	# enable:        Enable the loop immediately after creation (optional).
+
+	new: func(components, update_period = 0, enable = 1) {
+
+		var m = { parents: [UpdateLoop] };
+		m.initialized = 0;
+		m.enabled = enable;
+		m.update_period = update_period;
+		m.time_last = 0;
+		m.sim_speed = 1;
+		m.components = (components != nil)? components : [];
+
+		m.timer = maketimer(update_period,
+			func { call(me._update, [], m) });
+
+		m.lst = [];
+
+		append(m.lst, setlistener("/sim/speed-up",
+			func(n) { m.sim_speed = n.getValue() }, 1, 0));
+
+		append(m.lst, setlistener("sim/signals/reinit", func {
+			m.timer.stop();
+			m.initialized = 0;
+		}));
+
+		append(m.lst, setlistener("sim/signals/fdm-initialized", func {
+			if (m.timer.isRunning) m.timer.stop();
+			call(me.reset, [], m);
+			if (m.enabled) m.timer.start();
+		}));
+
+		return m;
+	},
+
+	del: func {
+		foreach (var l; me.lst) removelistener(l);
+	},
+
+	##
+	# Resets internal state for all components controlled by the loop.
+	# It is of course responsibility of every component to clean their internal
+	# state appropriately.
+
+	reset: func {
+		me.time_last = getprop("/sim/time/elapsed-sec");
+
+		foreach (var component; me.components)
+			component.reset();
+
+		me.initialized = 1;
+	},
+
+	##
+	# Enables the loop if it was disabled, i.e. the loop will start updating
+	# the components under control
+
+	enable: func {
+		if (me.initialized) me.timer.start();
+		me.enabled = 1;
+	},
+
+	##
+	# Disables the loop if it was enabled, i.e. the loop will freeze and its
+	# components will not get updates until enabled again
+
+	disable: func {
+		me.timer.stop();
+		me.enabled = 0;
+	},
+
+	_update: func {
+		var time_now = getprop("/sim/time/elapsed-sec");
+		var dt = (time_now - me.time_last) * me.sim_speed;
+		if (dt == 0) return;
+
+		me.time_last = time_now;
+
+		foreach (var component; me.components)
+			component.update(dt);
+	},
+};
diff --git a/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas b/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas
index 1c71b14f3..b9474f51b 100644
--- a/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas
+++ b/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas
@@ -62,12 +62,12 @@ setlistener("instrumentation/ilec-sc7/sensitivity",
 	func(n) { sc7_needle.dampening = n.getValue() }, 0, 0);
 
 # Wrap everything together into an instrument
-var fast_instruments = Instrument.new(
+var fast_instruments = UpdateLoop.new(
 	update_period: 0,
 	components: [probe, sc7_needle, extra_needle],
 	enable: 1);
 
-var slow_instruments = Instrument.new(
+var slow_instruments = UpdateLoop.new(
 	update_period: 1,
 	components: [averager, temperature, lcd_controller],
 	enable: 1);