PageRenderTime 54ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/v2-71/mh/lib/Timer.pm

#
Perl | 431 lines | 285 code | 43 blank | 103 comment | 60 complexity | 8dbcf1b737d5b6fd8928d73d15150d8b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, GPL-3.0
  1. use strict;
  2. package Timer;
  3. my ($class, $self, $id, $state, $action, $repeat, @timers_with_actions, $resort_timers_with_actions, @sets_from_previous_pass);
  4. # This is called from mh each pass
  5. sub check_for_timer_actions {
  6. my $ref;
  7. while ($ref = shift @sets_from_previous_pass) {
  8. &set_from_last_pass($ref);
  9. }
  10. for $ref (&expired_timers_with_actions) {
  11. &run_action($ref);
  12. }
  13. }
  14. sub expired_timers_with_actions {
  15. my @expired_timers = ();
  16. # Keep the timers in order for effecient checking
  17. if ($resort_timers_with_actions) {
  18. @timers_with_actions = sort { $a->{expire_time} <=> $b->{expire_time} } @timers_with_actions;
  19. $resort_timers_with_actions = 0;
  20. }
  21. # print "db twa=@timers_with_actions\n";
  22. while (@timers_with_actions) {
  23. $self = $timers_with_actions[0];
  24. # print "db3 s=$self ex=$self->{expire_time}\n";
  25. if (!$self->{expire_time}) {
  26. shift @timers_with_actions; # These timers were 'unset' ... delete them
  27. }
  28. # Use this method avoids problems with Timer is called from X10_Items
  29. # elsif (expired $self) {
  30. elsif (&Timer::expired($self)) {
  31. push(@expired_timers, $self);
  32. shift @timers_with_actions;
  33. if (--$self->{repeat} > 0) {
  34. set $self $self->{period}, $self->{action}, $self->{repeat};
  35. }
  36. }
  37. else {
  38. last; # The first timer has not expired yet, so don't check the others
  39. }
  40. }
  41. return @expired_timers;
  42. }
  43. sub delete_timer_with_action {
  44. my ($timer) = @_;
  45. my $i = 0;
  46. while ($i <= $#timers_with_actions) {
  47. print "testing i=$i timer=$timer\n" if $main::config_parms{debug} eq 'timer';
  48. if ($timers_with_actions[$i] eq $timer) {
  49. # print "db deleting timer $timer\n";
  50. splice(@timers_with_actions, $i, 1);
  51. last;
  52. }
  53. $i++;
  54. }
  55. }
  56. sub new {
  57. my ($class, $id, $state) = @_;
  58. my $self = {};
  59. # Not sure why this gives an error without || Timer
  60. bless $self, $class || 'Timer';
  61. return $self;
  62. }
  63. sub restore_string {
  64. my ($self) = @_;
  65. my $expire_time = $self->{expire_time};
  66. return unless $self->{time} or ($expire_time and $expire_time > main::get_tickcount);
  67. my $restore_string = "set $self->{object_name} $self->{period} " if $self->{period};
  68. $restore_string .= ", q|$self->{action}|" if $self->{action};
  69. $restore_string .= ", $self->{repeat}" if $self->{repeat};
  70. $restore_string .= "; ";
  71. $restore_string .= $self->{object_name} . "->{expire_time} = $expire_time;" if $expire_time;
  72. $restore_string .= $self->{object_name} . "->{time} = $self->{time};" if $self->{time};
  73. $restore_string .= $self->{object_name} . "->{time_pause} = $self->{time_pause};" if $self->{time_pause};
  74. $restore_string .= $self->{object_name} . "->{time_adjust} = $self->{time_adjust};" if $self->{time_adjust};
  75. return $restore_string;
  76. }
  77. # Use this to re-start dynamic timers after reload
  78. sub restore_self_set {
  79. my ($self) = @_;
  80. my $expire_time = $self->{expire_time};
  81. # Announced expired timers on restart/reload
  82. # return if !$expire_time or $expire_time < main::get_tickcount;
  83. return if !$expire_time;
  84. # Need to set NOW, not on next pass, so expire_time can be set
  85. # set $self $self->{period}, $self->{action}, $self->{repeat};
  86. @{$self->{set_next_pass}} = ($self->{period}, $self->{action}, $self->{repeat});
  87. &set_from_last_pass($self);
  88. $self->{expire_time} = $expire_time;
  89. }
  90. sub state {
  91. ($self) = @_;
  92. return $self->{state};
  93. }
  94. sub state_log {
  95. my ($self) = @_;
  96. return @{$$self{state_log}} if $$self{state_log};
  97. }
  98. sub set {
  99. ($self, $state, $action, $repeat) = @_;
  100. my @c = caller;
  101. # print "db1 $main::Time_Date running set s=$self s=$state a=$action t=$self->{text} c=@c\n";
  102. return if &main::check_for_tied_filters($self, $state);
  103. # Set states for NEXT pass, so expired, active, etc,
  104. # checks are consistent for one pass.
  105. push @sets_from_previous_pass, $self;
  106. @{$self->{set_next_pass}} = ($state, $action, $repeat);
  107. }
  108. # This is called from mh
  109. sub set_from_last_pass {
  110. my ($self) = @_;
  111. ($state, $action, $repeat) = @{$self->{set_next_pass}} ;
  112. # Turn a timer off
  113. if ($state == 0) {
  114. $self->{expire_time} = undef;
  115. &delete_timer_with_action($self);
  116. $resort_timers_with_actions = 1;
  117. }
  118. # Turn a timer on
  119. else {
  120. $self->{expire_time} = ($state * 1000) + main::get_tickcount;
  121. $self->{period} = $state;
  122. $self->{repeat} = $repeat;
  123. if ($action) {
  124. $self->{action} = $action;
  125. print "action timer s=$self a=$action s=$state\n" if $main::config_parms{debug} eq 'timer';
  126. &delete_timer_with_action($self); # delete possible previous
  127. push(@timers_with_actions, $self);
  128. $resort_timers_with_actions = 1;
  129. }
  130. }
  131. $self->{pass_triggered} = 0;
  132. unshift(@{$$self{state_log}}, "$main::Time_Date $state");
  133. pop @{$$self{state_log}} if @{$$self{state_log}} > $main::config_parms{max_state_log_entries};
  134. }
  135. sub resort_timers_with_actions {
  136. $resort_timers_with_actions = 1;
  137. }
  138. sub unset {
  139. ($self) = @_;
  140. undef $self->{expire_time};
  141. undef $self->{action};
  142. &delete_timer_with_action($self);
  143. }
  144. sub delete_old_timers {
  145. undef @timers_with_actions;
  146. }
  147. sub run_action {
  148. ($self) = @_;
  149. if (my $action = $self->{action}) {
  150. my $action_type = ref $action;
  151. print "Executing timer subroutine ref=$action_type action=$action\n" if $main::config_parms{debug} eq 'timer';
  152. # Note: passing in a sub ref will cause problems on code reloads.
  153. # So the 2nd of these 2 would be the better choice:
  154. # set $kids_bedtime_timer 10, \&kids_bedtime2;
  155. # set $kids_bedtime_timer 10, '&kids_bedtime2';
  156. if ($action_type eq 'CODE') {
  157. &{$action};
  158. }
  159. else {
  160. package main; # Had to do this to get the 'speak' function recognized without having to &main::speak() it
  161. my $timer_name = $self->{object_name}; # So we can use this in the timer action eval
  162. $state = $self->{object_name}; # So we can use this in the timer action eval
  163. eval $action;
  164. package Timer;
  165. print "\nError in running timer action: action=$action\n error: $@\n" if $@;
  166. }
  167. }
  168. }
  169. sub expired {
  170. ($self) = @_;
  171. # print "db $self->{expire_time} $self->{pass_triggered}\n";
  172. if ($self->{expire_time} and
  173. $self->{expire_time} < main::get_tickcount) {
  174. # print "db expired1 loop=$self->{pass_triggered} lc= $main::Loop_Count\n";
  175. # Reset if we finished the trigger pass
  176. if ($self->{pass_triggered} and
  177. $self->{pass_triggered} < $main::Loop_Count) {
  178. # print "db expired2 loop=$self->{pass_triggered}\n";
  179. $self->{expire_time} = 0;
  180. $self->{pass_triggered} = 0;
  181. return 0;
  182. }
  183. else {
  184. $self->{pass_triggered} = $main::Loop_Count;
  185. return 1;
  186. }
  187. }
  188. else {
  189. return 0;
  190. }
  191. }
  192. sub hours_remaining {
  193. ($self) = @_;
  194. return if inactive $self;
  195. my $diff = $self->{expire_time} - main::get_tickcount;
  196. # print "d=$diff s=$self st=", $self->{expire_time}, "\n";
  197. return sprintf("%3.1f", $diff/(60*60000));
  198. }
  199. sub hours_remaining_now {
  200. ($self) = @_;
  201. return if inactive $self;
  202. my $hours_left = int(.5 + ($self->{expire_time} - main::get_tickcount) / (60*60000));
  203. if ($hours_left and
  204. $self->{hours_remaining} != $hours_left) {
  205. $self->{hours_remaining} = $hours_left;
  206. return $hours_left;
  207. }
  208. else {
  209. return undef;
  210. }
  211. }
  212. sub minutes_remaining {
  213. ($self) = @_;
  214. return if inactive $self;
  215. my $diff = $self->{expire_time} - main::get_tickcount;
  216. # print "d=$diff s=$self st=", $self->{expire_time}, "\n";
  217. return sprintf("%3.1f", $diff/60000);
  218. }
  219. sub minutes_remaining_now {
  220. ($self) = @_;
  221. return if inactive $self;
  222. my $minutes_left = int(.5 + ($self->{expire_time} - main::get_tickcount) / 60000);
  223. if ($minutes_left and
  224. $self->{minutes_remaining} != $minutes_left) {
  225. $self->{minutes_remaining} = $minutes_left;
  226. return $minutes_left;
  227. }
  228. else {
  229. return undef;
  230. }
  231. }
  232. sub seconds_remaining {
  233. ($self) = @_;
  234. return if inactive $self;
  235. my $diff = $self->{expire_time} - main::get_tickcount;
  236. return sprintf("%3.1f", $diff/1000);
  237. }
  238. sub seconds_remaining_now {
  239. ($self) = @_;
  240. return if inactive $self;
  241. my $seconds_left = int(.5 + ($self->{expire_time} - main::get_tickcount) / 1000);
  242. if ($seconds_left and
  243. $self->{seconds_remaining} != $seconds_left) {
  244. $self->{seconds_remaining} = $seconds_left;
  245. return $seconds_left;
  246. }
  247. else {
  248. return undef;
  249. }
  250. }
  251. sub active {
  252. ($self) = @_;
  253. if ($self->{expire_time} and
  254. $self->{expire_time} >= main::get_tickcount) {
  255. return 1;
  256. }
  257. else {
  258. return 0;
  259. }
  260. }
  261. sub inactive {
  262. ($self) = @_;
  263. if ($self->{expire_time}) {
  264. if ($self->{expire_time} < main::get_tickcount) {
  265. # $self->{expire_time} = 0; ... this could disable a expire timer test??
  266. return 1;
  267. }
  268. else {
  269. return 0;
  270. }
  271. }
  272. else {
  273. return 1;
  274. }
  275. }
  276. # The reset of these methods apply to a countup/stopwatch type timer
  277. sub start {
  278. ($self) = @_;
  279. if ($self->{time}) {
  280. &main::print_log("Timer is already running");
  281. return;
  282. }
  283. $self->{time} = time;
  284. $self->{time_adjust} = 0;
  285. }
  286. sub restart {
  287. ($self) = @_;
  288. $self->{time} = time;
  289. $self->{time_adjust} = 0;
  290. $self->{time_pause} = 0;
  291. }
  292. sub stop {
  293. ($self) = @_;
  294. $self->{time} = undef;
  295. }
  296. sub pause {
  297. ($self) = @_;
  298. return if $self->{time_pause}; # Already paused
  299. $self->{time_pause} = time;
  300. }
  301. sub resume {
  302. ($self) = @_;
  303. return unless $self->{time_pause}; # Not paused
  304. $self->{time_adjust} += (time - $self->{time_pause});
  305. $self->{time_pause} = 0;
  306. }
  307. sub query {
  308. ($self) = @_;
  309. my $time = $self->{time};
  310. return undef unless $time;
  311. my $time_ref = ($self->{time_pause}) ? $self->{time_pause} : time;
  312. $time = $time_ref - $time;
  313. $time -= $self->{time_adjust} if $self->{time_adjust};
  314. return $time;
  315. }
  316. 1;
  317. #
  318. # $Log$
  319. # Revision 1.25 2002/08/22 13:45:50 winter
  320. # - 2.70 release
  321. #
  322. # Revision 1.24 2002/05/28 13:07:51 winter
  323. # - 2.68 release
  324. #
  325. # Revision 1.23 2001/12/16 21:48:41 winter
  326. # - 2.62 release
  327. #
  328. # Revision 1.22 2001/02/24 23:26:40 winter
  329. # - 2.45 release
  330. #
  331. # Revision 1.21 2001/02/04 20:31:31 winter
  332. # - 2.43 release
  333. #
  334. # Revision 1.20 2001/01/20 17:47:50 winter
  335. # - 2.41 release
  336. #
  337. # Revision 1.19 2000/12/21 18:54:15 winter
  338. # - 2.38 release
  339. #
  340. # Revision 1.18 2000/11/12 21:02:38 winter
  341. # - 2.34 release
  342. #
  343. # Revision 1.17 2000/10/22 16:48:29 winter
  344. # - 2.32 release
  345. #
  346. # Revision 1.16 2000/09/09 21:19:11 winter
  347. # - 2.28 release
  348. #
  349. # Revision 1.15 2000/08/19 01:22:36 winter
  350. # - 2.27 release
  351. #
  352. # Revision 1.14 2000/02/12 06:11:37 winter
  353. # - commit lots of changes, in preperation for mh release 2.0
  354. #
  355. # Revision 1.13 2000/01/27 13:43:19 winter
  356. # - update version number
  357. #
  358. # Revision 1.12 1999/12/12 23:59:55 winter
  359. # - change elseif (expired) check
  360. #
  361. # Revision 1.11 1999/11/08 02:20:41 winter
  362. # - fix xxx_left roundoff bug.
  363. #
  364. # Revision 1.10 1999/09/27 03:17:41 winter
  365. # - make debug conditional on debug parm
  366. #
  367. # Revision 1.9 1999/07/05 22:34:36 winter
  368. # *** empty log message ***
  369. #
  370. # Revision 1.8 1999/06/27 20:12:36 winter
  371. # - add delete_timer_with_action
  372. #
  373. # Revision 1.7 1999/02/16 02:05:59 winter
  374. # - print 'timer eval' errata only if debug is on
  375. #
  376. # Revision 1.6 1999/02/08 00:31:36 winter
  377. # - add delete_old_timers
  378. #
  379. # Revision 1.5 1999/01/23 16:31:47 winter
  380. # *** empty log message ***
  381. #
  382. # Revision 1.4 1999/01/23 16:25:30 winter
  383. # - Call get_tickcount, so we are platform independent
  384. #
  385. # Revision 1.3 1998/12/08 02:26:48 winter
  386. # - add log
  387. #
  388. #