sensor.nas 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. # Sensor class to avoid repeated readout of properties.
  2. #
  3. # Each sensor object holds a list callbacks to other objects (usually filters or canvas elements) that
  4. # it calls to update in case of sensor value change
  5. # (original concept seen in Thorstens Zivko Edge)
  6. # set a default value for timer loop updates,
  7. # if none is defined, set to max.
  8. if (!defined("FAST"))
  9. var FAST = 0;
  10. if (!defined("USE_CENTRAL_UPDATE_LOOP"))
  11. var USE_CENTRAL_UPDATE_LOOP = 0;
  12. var TIMERDELAY = 0;
  13. var Sensor = {
  14. new: func(param) {
  15. var m = { parents: [Sensor] };
  16. # just to see from outside if this object is a sensor
  17. m.is_a_sensor = nil;
  18. m.val = 0;
  19. m.callbacks = [];
  20. m.mainfunction = func (x) { };
  21. m.mainfunctioncode = { };
  22. m.timer = nil;
  23. m.li = nil;
  24. m.th = nil;
  25. m.calls = 0; #actual executions of callback func
  26. m.freq = 0; #frequency of property checks
  27. foreach (var a; keys(param)) {
  28. if (-1 == isin(a, ["prop", "function", "thres", "type", "timer", "name" ])) {
  29. #debug.dump(caller(0));
  30. die("unknown parameter '"~a~"' in "~caller(0)[2]);
  31. }
  32. }
  33. m.function = contains(param, "function") ? param.function : nil;
  34. m.thres = contains(param, "thres") ? param.thres : 0;
  35. # set listener, if timer parameter is not given
  36. m.disable = contains(param, "timer") ? param.timer : -1;
  37. m.istext = contains(param, "type") ? (param.type == "STRING") : 0;
  38. m.isbool = contains(param, "type") ? (param.type == "BOOL") : 0;
  39. m.isnum = contains(param, "type") ? (param.type == "FLOAT") : 1;
  40. m.type = (m.isnum) ? "DOUBLE" : ( (m.isbool) ? "BOOL" : "STRING");
  41. if (contains(param, "prop")) {
  42. m.prop = (param.prop) ? props.globals.initNode(param.prop, 0, m.type) : nil;
  43. m.path = param.prop;
  44. } else {
  45. m.prop = nil;
  46. m.path = "";
  47. }
  48. # names are currently not in use, may be handy for later developments
  49. m.name = contains(param, "name") ? param.name : nil;
  50. if (m.name == nil) {
  51. if (contains(param, "prop"))
  52. if (param.prop)
  53. m.name = string.join("/", split("/", param.prop)[-2:-1] );
  54. else
  55. m.name = "unnamed func";
  56. else
  57. m.name = "unnamed func";
  58. }
  59. if (m.function==nil and m.prop==nil)
  60. die("Sensor.new(): must give either 'function' or 'prop' argument!");
  61. if (m.disable != -1 or m.function != nil) {
  62. if (!USE_CENTRAL_UPDATE_LOOP) {
  63. m.disable = (m.disable == -1) ? 0 : m.disable;
  64. if (m.function == nil) {
  65. if (m.isnum)
  66. m.timer = maketimer(m.disable, func m._check_num( m.prop.getValue() ));
  67. else
  68. m.timer = maketimer(m.disable, func m._check_text( m.prop.getValue() ));
  69. } else {
  70. if (m.isnum)
  71. m.timer = maketimer(m.disable, func m._check_num( m.function() ));
  72. else
  73. m.timer = maketimer(m.disable, func m._check_text( m.function() ));
  74. }
  75. # delay the start of the timer so that timers are more evenly distributed over time
  76. settimer(func m.timer.start();, TIMERDELAY);
  77. TIMERDELAY += 0.02;
  78. }
  79. } else if (m.prop != nil) {
  80. # use of listener needs no further check on property change
  81. if (m.isnum)
  82. m.li = setlistener(m.prop, func (u) m._check_num( u.getValue() ), startup=1, runtime=0);
  83. else
  84. m.li = setlistener(m.prop, func (u) m._check_text( u.getValue() ), startup=1, runtime=0);
  85. }
  86. m.refresh();
  87. return m;
  88. },
  89. # check callback methods are kept as simple as possible to improve performance
  90. _check_num: func (x) {
  91. me.freq += 1;
  92. if (math.abs(me.val-x) > me.thres) {
  93. me.val = x;
  94. me.calls +=1;
  95. me.mainfunction(x);
  96. foreach (var c; me.callbacks) {
  97. if (c.a.active) {
  98. c.f(c.o, c.c);
  99. }
  100. }
  101. }
  102. },
  103. _check_text: func (x) {
  104. # this method checks boolean and string data for changes
  105. me.freq += 1;
  106. if (me.val != x) {
  107. me.val = x;
  108. me.calls +=1;
  109. me.mainfunction(x);
  110. foreach (var c; me.callbacks) {
  111. if (c.a.active) {
  112. c.f(c.o, c.c);
  113. }
  114. }
  115. }
  116. },
  117. _check: func (x) {
  118. # force a refresh of all callback items,
  119. # should not be called too often
  120. me.val = x;
  121. me.freq += 1;
  122. me.calls +=1;
  123. me.mainfunction(x);
  124. foreach (var c; me.callbacks) {
  125. c.f(c.o, c.c);
  126. }
  127. },
  128. refresh: func () {
  129. if (me.function == nil) {
  130. me.val = me.prop.getValue();
  131. } else {
  132. me.val = me.function();
  133. }
  134. if (me.val == nil) {
  135. me.val = (me.istext) ? "" : 0;
  136. }
  137. me._check(me.val);
  138. },
  139. compile: func () {
  140. var lines=keys(me.mainfunctioncode);
  141. if (size(lines)>0) {
  142. var tempcode = "";
  143. #compose the code lines
  144. foreach (var condi; lines)
  145. tempcode ~= condi ~ " {" ~ me.mainfunctioncode[condi] ~ " }";
  146. #print(me.name,": ",tempcode);
  147. #compile the mainfunction
  148. me.mainfunction = compile(tempcode);
  149. }
  150. },
  151. update: func () {
  152. var x = (me.function==nil) ? me.prop.getValue() : me.function();
  153. if (x == nil)
  154. return;
  155. if (me.isnum)
  156. me._check_num(x);
  157. else
  158. me._check_text(x);
  159. },
  160. _register_: func (p, o=nil) {
  161. p.a = o;
  162. append(me.callbacks, p);
  163. #call once to init object
  164. p.f(p.o,p.c);
  165. },
  166. _register_code: func (condi, c) {
  167. if (DEBUG) print(c);
  168. #append the new code parts to mainfunction
  169. if (contains(me.mainfunctioncode, condi) )
  170. me.mainfunctioncode[condi] ~= c;
  171. else
  172. me.mainfunctioncode[condi] = c;
  173. #call once to init object
  174. compile(c)(me.val);
  175. },
  176. get: func () {
  177. return me.val;
  178. },
  179. _del_: func () {
  180. if (me.timer != nil)
  181. me.timer.stop();
  182. else if (me.li != nil)
  183. removelistener(me.li);
  184. }
  185. };