123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- # Sensor class to avoid repeated readout of properties.
- #
- # Each sensor object holds a list callbacks to other objects (usually filters or canvas elements) that
- # it calls to update in case of sensor value change
- # (original concept seen in Thorstens Zivko Edge)
- # set a default value for timer loop updates,
- # if none is defined, set to max.
- if (!defined("FAST"))
- var FAST = 0;
- if (!defined("USE_CENTRAL_UPDATE_LOOP"))
- var USE_CENTRAL_UPDATE_LOOP = 0;
-
- var TIMERDELAY = 0;
- var Sensor = {
- new: func(param) {
- var m = { parents: [Sensor] };
-
- # just to see from outside if this object is a sensor
- m.is_a_sensor = nil;
- m.val = 0;
- m.callbacks = [];
- m.mainfunction = func (x) { };
- m.mainfunctioncode = { };
-
- m.timer = nil;
- m.li = nil;
- m.th = nil;
- m.calls = 0; #actual executions of callback func
- m.freq = 0; #frequency of property checks
-
- foreach (var a; keys(param)) {
- if (-1 == isin(a, ["prop", "function", "thres", "type", "timer", "name" ])) {
- #debug.dump(caller(0));
- die("unknown parameter '"~a~"' in "~caller(0)[2]);
- }
- }
-
- m.function = contains(param, "function") ? param.function : nil;
- m.thres = contains(param, "thres") ? param.thres : 0;
- # set listener, if timer parameter is not given
- m.disable = contains(param, "timer") ? param.timer : -1;
- m.istext = contains(param, "type") ? (param.type == "STRING") : 0;
- m.isbool = contains(param, "type") ? (param.type == "BOOL") : 0;
- m.isnum = contains(param, "type") ? (param.type == "FLOAT") : 1;
- m.type = (m.isnum) ? "DOUBLE" : ( (m.isbool) ? "BOOL" : "STRING");
-
- if (contains(param, "prop")) {
- m.prop = (param.prop) ? props.globals.initNode(param.prop, 0, m.type) : nil;
- m.path = param.prop;
- } else {
- m.prop = nil;
- m.path = "";
- }
-
- # names are currently not in use, may be handy for later developments
- m.name = contains(param, "name") ? param.name : nil;
-
- if (m.name == nil) {
- if (contains(param, "prop"))
- if (param.prop)
- m.name = string.join("/", split("/", param.prop)[-2:-1] );
- else
- m.name = "unnamed func";
- else
- m.name = "unnamed func";
- }
-
- if (m.function==nil and m.prop==nil)
- die("Sensor.new(): must give either 'function' or 'prop' argument!");
-
- if (m.disable != -1 or m.function != nil) {
- if (!USE_CENTRAL_UPDATE_LOOP) {
- m.disable = (m.disable == -1) ? 0 : m.disable;
-
- if (m.function == nil) {
- if (m.isnum)
- m.timer = maketimer(m.disable, func m._check_num( m.prop.getValue() ));
- else
- m.timer = maketimer(m.disable, func m._check_text( m.prop.getValue() ));
- } else {
- if (m.isnum)
- m.timer = maketimer(m.disable, func m._check_num( m.function() ));
- else
- m.timer = maketimer(m.disable, func m._check_text( m.function() ));
- }
- # delay the start of the timer so that timers are more evenly distributed over time
- settimer(func m.timer.start();, TIMERDELAY);
- TIMERDELAY += 0.02;
- }
- } else if (m.prop != nil) {
- # use of listener needs no further check on property change
- if (m.isnum)
- m.li = setlistener(m.prop, func (u) m._check_num( u.getValue() ), startup=1, runtime=0);
- else
- m.li = setlistener(m.prop, func (u) m._check_text( u.getValue() ), startup=1, runtime=0);
- }
-
- m.refresh();
- return m;
- },
-
- # check callback methods are kept as simple as possible to improve performance
- _check_num: func (x) {
- me.freq += 1;
- if (math.abs(me.val-x) > me.thres) {
- me.val = x;
- me.calls +=1;
- me.mainfunction(x);
-
- foreach (var c; me.callbacks) {
- if (c.a.active) {
- c.f(c.o, c.c);
- }
- }
- }
- },
-
- _check_text: func (x) {
- # this method checks boolean and string data for changes
- me.freq += 1;
- if (me.val != x) {
- me.val = x;
- me.calls +=1;
- me.mainfunction(x);
-
- foreach (var c; me.callbacks) {
- if (c.a.active) {
- c.f(c.o, c.c);
- }
- }
- }
- },
-
- _check: func (x) {
- # force a refresh of all callback items,
- # should not be called too often
-
- me.val = x;
- me.freq += 1;
- me.calls +=1;
- me.mainfunction(x);
-
- foreach (var c; me.callbacks) {
- c.f(c.o, c.c);
- }
- },
-
- refresh: func () {
- if (me.function == nil) {
- me.val = me.prop.getValue();
- } else {
- me.val = me.function();
- }
- if (me.val == nil) {
- me.val = (me.istext) ? "" : 0;
- }
- me._check(me.val);
-
- },
-
- compile: func () {
-
- var lines=keys(me.mainfunctioncode);
-
- if (size(lines)>0) {
- var tempcode = "";
-
- #compose the code lines
- foreach (var condi; lines)
- tempcode ~= condi ~ " {" ~ me.mainfunctioncode[condi] ~ " }";
-
- #print(me.name,": ",tempcode);
- #compile the mainfunction
- me.mainfunction = compile(tempcode);
- }
- },
-
- update: func () {
- var x = (me.function==nil) ? me.prop.getValue() : me.function();
- if (x == nil)
- return;
- if (me.isnum)
- me._check_num(x);
- else
- me._check_text(x);
- },
-
- _register_: func (p, o=nil) {
- p.a = o;
- append(me.callbacks, p);
-
- #call once to init object
- p.f(p.o,p.c);
- },
-
- _register_code: func (condi, c) {
- if (DEBUG) print(c);
- #append the new code parts to mainfunction
-
- if (contains(me.mainfunctioncode, condi) )
- me.mainfunctioncode[condi] ~= c;
- else
- me.mainfunctioncode[condi] = c;
-
- #call once to init object
- compile(c)(me.val);
- },
-
- get: func () {
- return me.val;
- },
-
- _del_: func () {
- if (me.timer != nil)
- me.timer.stop();
- else if (me.li != nil)
- removelistener(me.li);
- }
- };
|