]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - rbot/timer.rb
initial import of rbot
[user/henk/code/ruby/rbot.git] / rbot / timer.rb
1 module Timer
2
3   # timer event, something to do and when/how often to do it
4   class Action
5     
6     # when this action is due next (updated by tick())
7     attr_accessor :in
8     
9     # is this action blocked? if so it won't be run
10     attr_accessor :blocked
11     
12     # period:: how often (seconds) to run the action
13     # data::   optional data to pass to the proc
14     # once::   optional, if true, this action will be run once then removed
15     # func::   associate a block to be called to perform the action
16     # 
17     # create a new action
18     def initialize(period, data=nil, once=false, &func)
19       @blocked = false
20       @period = period
21       @in = period
22       @func = func
23       @data = data
24       @once = once
25     end
26
27     # run the action by calling its proc
28     def run
29       @in += @period
30       if(@data)
31         @func.call(@data)
32       else
33         @func.call
34       end
35       return @once
36     end
37   end
38   
39   # timer handler, manage multiple Action objects, calling them when required.
40   # The timer must be ticked by whatever controls it, i.e. regular calls to
41   # tick() at whatever granularity suits your application's needs.
42   # Alternatively you can call run(), and the timer will tick itself, but this
43   # blocks so you gotta do it in a thread (remember ruby's threads block on
44   # syscalls so that can suck).
45   class Timer
46     def initialize
47       @timers = Array.new
48       @handle = 0
49       @lasttime = 0
50     end
51     
52     # period:: how often (seconds) to run the action
53     # data::   optional data to pass to the action's proc
54     # func::   associate a block with add() to perform the action
55     # 
56     # add an action to the timer
57     def add(period, data=nil, &func)
58       @handle += 1
59       @timers[@handle] = Action.new(period, data, &func)
60       return @handle
61     end
62
63     # period:: how often (seconds) to run the action
64     # data::   optional data to pass to the action's proc
65     # func::   associate a block with add() to perform the action
66     # 
67     # add an action to the timer which will be run just once, after +period+
68     def add_once(period, data=nil, &func)
69       @handle += 1
70       @timers[@handle] = Action.new(period, data, true, &func)
71       return @handle
72     end
73
74     # remove action with handle +handle+ from the timer
75     def remove(handle)
76       @timers.delete_at(handle)
77     end
78     
79     # block action with handle +handle+
80     def block(handle)
81       @timers[handle].blocked = true
82     end
83
84     # unblock action with handle +handle+
85     def unblock(handle)
86       @timers[handle].blocked = false
87     end
88     
89     # you can call this when you know you're idle, or you can split off a
90     # thread and call the run() method to do it for you.
91     def tick 
92       if(@lasttime != 0)
93         diff = (Time.now - @lasttime).to_f
94         @lasttime = Time.now
95         @timers.compact.each { |timer|
96           timer.in = timer.in - diff
97         }
98         @timers.compact.each { |timer|
99           if (!timer.blocked)
100             if(timer.in <= 0)
101               if(timer.run)
102                 # run once
103                 @timers.delete(timer)
104               end
105             end
106           end
107         }
108       else
109         # don't do anything on the first tick
110         @lasttime = Time.now
111       end
112     end
113
114     # the timer will tick() itself. this blocks, so run it in a thread, and
115     # watch out for blocking syscalls
116     def run(granularity=0.1)
117       while(true)
118         sleep(granularity)
119         tick
120       end
121     end
122   end
123 end