81_M232Counter.pm 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. #
  2. #
  3. # 81_M232Counter.pm
  4. # written by Dr. Boris Neubert 2007-11-26
  5. # e-mail: omega at online dot de
  6. #
  7. ##############################################
  8. # $Id: 81_M232Counter.pm 16375 2018-03-10 15:40:02Z neubert $
  9. package main;
  10. use strict;
  11. use warnings;
  12. use Time::HiRes qw(gettimeofday);
  13. sub M232Counter_Get($@);
  14. sub M232Counter_Set($@);
  15. sub M232Counter_SetBasis($@);
  16. sub M232Counter_Define($$);
  17. sub M232Counter_GetStatus($);
  18. ###################################
  19. sub
  20. M232Counter_Initialize($)
  21. {
  22. my ($hash) = @_;
  23. $hash->{GetFn} = "M232Counter_Get";
  24. $hash->{SetFn} = "M232Counter_Set";
  25. $hash->{DefFn} = "M232Counter_Define";
  26. $hash->{AttrList} = "dummy:1,0 model:M232Counter";
  27. }
  28. ###################################
  29. sub
  30. M232Counter_GetStatus($)
  31. {
  32. my ($hash) = @_;
  33. if(!$hash->{LOCAL}) {
  34. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "M232Counter_GetStatus", $hash, 1);
  35. }
  36. my $name = $hash->{NAME};
  37. my $r= $hash->{READINGS};
  38. my $d = IOWrite($hash, "z");
  39. if(!defined($d)) {
  40. my $msg = "M232Counter $name tick count read error";
  41. Log3 $name, 2, $msg;
  42. return $msg;
  43. }
  44. # time
  45. my $tn = TimeNow();
  46. #tsecs
  47. my $tsecs= time(); # number of non-leap seconds since January 1, 1970, UTC
  48. # previous tsecs
  49. my $tsecs_prev;
  50. if(defined($r->{tsecs})) {
  51. $tsecs_prev= $r->{tsecs}{VAL};
  52. } else{
  53. $tsecs_prev= $tsecs; # 1970-01-01
  54. }
  55. # basis
  56. my $basis;
  57. if(defined($r->{basis})) {
  58. $basis= $r->{basis}{VAL};
  59. } else {
  60. $basis= 0;
  61. }
  62. my $basis_prev= $basis;
  63. # previous count (this variable is currently unused)
  64. my $count_prev;
  65. if(defined($r->{count})) {
  66. $count_prev= $r->{count}{VAL};
  67. } else {
  68. $count_prev= 0;
  69. }
  70. # current count
  71. my $count= hex $d;
  72. # If the counter reaches 65536, the counter does not wrap around but
  73. # stops at 0. We therefore purposefully reset the counter to 0 before
  74. # it reaches its final tick count.
  75. if($count > 64000) {
  76. $basis+= $count;
  77. $count= 0;
  78. $r->{basis}{VAL} = $basis;
  79. $r->{basis}{TIME}= $tn;
  80. my $ret = IOWrite($hash, "Z1");
  81. if(!defined($ret)) {
  82. my $msg = "M232Counter $name reset error";
  83. Log3 $name, 2, $msg;
  84. return $msg;
  85. }
  86. }
  87. # previous value
  88. my $value_prev;
  89. if(defined($r->{value})) {
  90. $value_prev= $r->{value}{VAL};
  91. } else {
  92. $value_prev= 0;
  93. }
  94. # current value
  95. my $value= ($basis+$count) * $hash->{FACTOR};
  96. # round to 3 digits
  97. $value= int($value*1000.0+0.5)/1000.0;
  98. # set new values
  99. $r->{count}{TIME} = $tn;
  100. $r->{count}{VAL} = $count;
  101. $r->{value}{TIME} = $tn;
  102. $r->{value}{VAL} = $value;
  103. $r->{tsecs}{TIME} = $tn;
  104. $r->{tsecs}{VAL} = $tsecs;
  105. $hash->{CHANGED}[0]= "count: $count";
  106. $hash->{CHANGED}[1]= "value: $value";
  107. # delta
  108. my $tsecs_delta= $tsecs-$tsecs_prev;
  109. my $count_delta= ($count+$basis)-($count_prev+$basis_prev);
  110. if($tsecs_delta>0) {
  111. my $delta= ($count_delta/$tsecs_delta)*$hash->{DELTAFACTOR};
  112. # round to 3 digits
  113. $delta= int($delta*1000.0+0.5)/1000.0;
  114. $r->{delta}{TIME} = $tn;
  115. $r->{delta}{VAL} = $delta;
  116. $hash->{CHANGED}[2]= "delta: $delta";
  117. }
  118. if(!$hash->{LOCAL}) {
  119. DoTrigger($name, undef) if($init_done);
  120. }
  121. $hash->{STATE} = $value;
  122. Log3 $name, 4, "M232Counter $name: $value $hash->{UNIT}";
  123. return $hash->{STATE};
  124. }
  125. ###################################
  126. sub
  127. M232Counter_Get($@)
  128. {
  129. my ($hash, @a) = @_;
  130. return "argument is missing" if(int(@a) != 2);
  131. my $msg;
  132. if($a[1] ne "status") {
  133. return "unknown get value, valid is status";
  134. }
  135. $hash->{LOCAL} = 1;
  136. my $v = M232Counter_GetStatus($hash);
  137. delete $hash->{LOCAL};
  138. return "$a[0] $a[1] => $v";
  139. }
  140. #############################
  141. sub
  142. M232Counter_Calibrate($@)
  143. {
  144. my ($hash, $value) = @_;
  145. my $rm= undef;
  146. my $name = $hash->{NAME};
  147. # adjust basis
  148. my $tn = TimeNow();
  149. $hash->{READINGS}{basis}{VAL}= $value / $hash->{FACTOR};
  150. $hash->{READINGS}{basis}{TIME}= $tn;
  151. $hash->{READINGS}{count}{VAL}= 0;
  152. $hash->{READINGS}{count}{TIME}= $tn;
  153. # recalculate value
  154. $hash->{READINGS}{value}{VAL} = $value;
  155. $hash->{READINGS}{value}{TIME} = $tn;
  156. # reset counter
  157. my $ret = IOWrite($hash, "Z1");
  158. if(!defined($ret)) {
  159. my $rm = "M232Counter $name read error";
  160. Log3 $name, 2, $rm;
  161. }
  162. return $rm;
  163. }
  164. #############################
  165. sub
  166. M232Counter_Set($@)
  167. {
  168. my ($hash, @a) = @_;
  169. my $u = "Usage: set <name> value <value>\n" .
  170. "set <name> interval <seconds>\n" ;
  171. return $u if(int(@a) != 3);
  172. my $reading= $a[1];
  173. if($a[1] eq "value") {
  174. my $value= $a[2];
  175. my $rm= M232Counter_Calibrate($hash, $value);
  176. } elsif($a[1] eq "interval") {
  177. my $interval= $a[2];
  178. $hash->{INTERVAL}= $interval;
  179. } else {
  180. return $u;
  181. }
  182. return undef;
  183. }
  184. #############################
  185. sub
  186. M232Counter_Define($$)
  187. {
  188. my ($hash, $def) = @_;
  189. my @a = split("[ \t][ \t]*", $def);
  190. return "syntax: define <name> M232Counter [unit] [factor] [deltaunit] [deltafactor]"
  191. if(int(@a) < 2 && int(@a) > 6);
  192. my $unit= ((int(@a) > 2) ? $a[2] : "ticks");
  193. my $factor= ((int(@a) > 3) ? $a[3] : 1.0);
  194. my $deltaunit= ((int(@a) > 4) ? $a[4] : "ticks per second");
  195. my $deltafactor= ((int(@a) > 5) ? $a[5] : 1.0);
  196. $hash->{UNIT}= $unit;
  197. $hash->{FACTOR}= $factor;
  198. $hash->{DELTAUNIT}= $deltaunit;
  199. $hash->{DELTAFACTOR}= $deltafactor;
  200. $hash->{INTERVAL}= 60; # poll every minute per default
  201. AssignIoPort($hash);
  202. if(!$hash->{LOCAL}) {
  203. InternalTimer(gettimeofday()+60, "M232Counter_GetStatus", $hash, 0);
  204. }
  205. return undef;
  206. }
  207. 1;
  208. =pod
  209. =item summary digital input of a ELV M232 module
  210. =item summary_DE digitaler Eingang eines ELV-M232-Moduls
  211. =begin html
  212. <a name="M232Counter"></a>
  213. <h3>M232Counter</h3>
  214. <ul>
  215. <a name="M232Counterdefine"></a>
  216. <b>Define</b>
  217. <ul>
  218. <code>define &lt;name&gt; M232Counter [unit [factor [deltaunit [deltafactor]]]]</code>
  219. <br><br>
  220. Define at most one M232Counter for a M232 device. Defining a M232Counter
  221. will schedule an internal task, which periodically reads the status of the
  222. counter, and triggers notify/filelog commands. <code>unit</code> is the unit
  223. name, <code>factor</code> is used to calculate the reading of the counter
  224. from the number of ticks. <code>deltaunit</code> is the unit name of the counter
  225. differential per second, <code>deltafactor</code> is used to calculate the
  226. counter differential per second from the number of ticks per second.<br><br>
  227. Default values:
  228. <ul>
  229. <li>unit: ticks</li>
  230. <li>factor: 1.0</li>
  231. <li>deltaunit: ticks per second</li>
  232. <li>deltafactor: 1.0</li>
  233. </ul>
  234. <br>Note: the parameters in square brackets are optional. If you wish to
  235. specify an optional parameter, all preceding parameters must be specified
  236. as well.
  237. <br><br>Examples:
  238. <ul>
  239. <code>define counter M232Counter turns</code><br>
  240. <code>define counter M232Counter kWh 0.0008 kW 2.88</code>
  241. (one tick equals 1/1250th kWh)<br>
  242. </ul>
  243. <br>
  244. Do not forget to start the counter (with <code>set .. start</code> for
  245. M232) or to start the counter and set the reading to a specified value
  246. (with <code>set ... value</code> for M232Counter).<br><br>
  247. To avoid issues with the tick count reaching the end point, the device's
  248. internal counter is automatically reset to 0 when the tick count is 64,000
  249. or above and the reading <i>basis</i> is adjusted accordingly.
  250. <br><br>
  251. </ul>
  252. <a name="M232Counterset"></a>
  253. <b>Set </b>
  254. <ul>
  255. <code>set &lt;name&gt; value &lt;value&gt;</code>
  256. <br><br>
  257. Sets the reading of the counter to the given value. The counter is reset
  258. and started and the offset is adjusted to value/unit.
  259. <br><br>
  260. <code>set &lt;name&gt; interval &lt;interval&gt;</code>
  261. <br><br>
  262. Sets the status polling interval in seconds to the given value. The default
  263. is 60 seconds.
  264. <br><br>
  265. </ul>
  266. <a name="M232Counterget"></a>
  267. <b>Get</b>
  268. <ul>
  269. <code>get &lt;name&gt; status</code>
  270. <br><br>
  271. Gets the reading of the counter multiplied by the factor from the
  272. <code>define</code> statement. Wraparounds of the counter are accounted for
  273. by an offset (see reading <code>basis</code> in the output of the
  274. <code>list</code> statement for the device).
  275. <br><br>
  276. </ul>
  277. <a name="M232Counterattr"></a>
  278. <b>Attributes</b>
  279. <ul>
  280. <li><a href="#attrdummy">dummy</a></li><br>
  281. <li><a href="#model">model</a> (M232Counter)</li>
  282. </ul>
  283. <br>
  284. </ul>
  285. =end html
  286. =cut