revert change from april '06
[enigma2.git] / timer.py
1 import bisect
2 import time
3 from enigma import *
4
5 class TimerEntry:
6         StateWaiting  = 0
7         StatePrepared = 1
8         StateRunning  = 2
9         StateEnded    = 3
10         
11         def __init__(self, begin, end):
12                 self.begin = begin
13                 self.prepare_time = 20
14                 self.end = end
15                 self.state = 0
16                 self.resetRepeated()
17                 self.backoff = 0
18                 
19                 self.disabled = False
20                 
21         def resetRepeated(self):
22                 self.repeated = int(0)
23
24         def setRepeated(self, day):
25                 self.repeated |= (2 ** day)
26                 print "Repeated: " + str(self.repeated)
27                 
28         def isRunning(self):
29                 return self.state == self.StateRunning
30                 
31         # update self.begin and self.end according to the self.repeated-flags
32         def processRepeated(self):
33                 print "ProcessRepeated"
34                 print time.strftime("%c", time.localtime(self.begin))
35                 print time.strftime("%c", time.localtime(self.end))
36                 if (self.repeated != 0):
37                         now = int(time.time()) + 1
38                         
39                         day = []
40                         flags = self.repeated
41                         for x in range(0, 7):
42                                 if (flags & 1 == 1):
43                                         day.append(0)
44                                         print "Day: " + str(x)
45                                 else:
46                                         day.append(1)
47                                 flags = flags >> 1
48
49                         print time.strftime("%c", time.localtime(now))
50                         print time.strftime("%c", time.localtime(self.begin))
51                         print time.strftime("%c", time.localtime(self.end))
52                         print str(time.localtime(self.begin).tm_wday)
53                         while ((day[time.localtime(self.begin).tm_wday] != 0) or ((day[time.localtime(self.begin).tm_wday] == 0) and self.end < now)):
54                                 print time.strftime("%c", time.localtime(self.begin))
55                                 print time.strftime("%c", time.localtime(self.end))
56                                 self.begin += 86400
57                                 self.end += 86400
58                         
59                         self.timeChanged()
60                         
61
62         def __lt__(self, o):
63                 return self.getNextActivation() < o.getNextActivation()
64         
65         # must be overridden
66         def activate(self):
67                 pass
68                 
69         # can be overridden
70         def timeChanged(self):
71                 pass
72
73         # check if a timer entry must be skipped
74         def shouldSkip(self):
75                 return self.end <= time.time() and self.state == TimerEntry.StateWaiting
76
77         def abort(self):
78                 self.end = time.time()
79                 
80                 # in case timer has not yet started, but gets aborted (so it's preparing),
81                 # set begin to now.
82                 if self.begin > self.end:
83                         self.begin = self.end
84
85                 self.cancelled = True
86         
87         # must be overridden!
88         def getNextActivation():
89                 pass
90
91         def disable(self):
92                 self.disabled = True
93         
94         def enable(self):
95                 self.disabled = False
96
97 class Timer:
98         # the time between "polls". We do this because
99         # we want to account for time jumps etc.
100         # of course if they occur <100s before starting,
101         # it's not good. thus, you have to repoll when
102         # you change the time.
103         #
104         # this is just in case. We don't want the timer 
105         # hanging. we use this "edge-triggered-polling-scheme"
106         # anyway, so why don't make it a bit more fool-proof?
107         MaxWaitTime = 100
108
109         def __init__(self):
110                 self.timer_list = [ ]
111                 self.processed_timers = [ ]
112                 
113                 self.timer = eTimer()
114                 self.timer.timeout.get().append(self.calcNextActivation)
115                 self.lastActivation = time.time()
116                 
117                 self.calcNextActivation()
118                 self.on_state_change = [ ]
119         
120         def stateChanged(self, entry):
121                 for f in self.on_state_change:
122                         f(entry)
123                         
124         def getNextRecordingTime(self):
125                 if len(self.timer_list) > 0:
126                         return self.timer_list[0].begin
127                 return -1
128                         
129         def cleanup(self):
130                 self.processed_timers = [entry for entry in self.processed_timers if entry.disabled]
131         
132         def addTimerEntry(self, entry, noRecalc=0):
133                 entry.processRepeated()
134
135                 # when the timer has not yet started, and is already passed,
136                 # don't go trough waiting/running/end-states, but sort it
137                 # right into the processedTimers.
138                 if entry.shouldSkip() or entry.state == TimerEntry.StateEnded or (entry.state == TimerEntry.StateWaiting and entry.disabled):
139                         print "already passed, skipping"
140                         print "shouldSkip:", entry.shouldSkip()
141                         print "state == ended", entry.state == TimerEntry.StateEnded
142                         print "waiting && disabled:", (entry.state == TimerEntry.StateWaiting and entry.disabled)
143                         bisect.insort(self.processed_timers, entry)
144                         entry.state = TimerEntry.StateEnded
145                 else:
146                         bisect.insort(self.timer_list, entry)
147                         if not noRecalc:
148                                 self.calcNextActivation()
149         
150         def setNextActivation(self, when):
151                 delay = int((when - time.time()) * 1000)
152                 print "[timer.py] next activation: %d (in %d ms)" % (when, delay)
153                 
154                 self.timer.start(delay, 1)
155                 self.next = when
156
157         def calcNextActivation(self):
158                 if self.lastActivation > time.time():
159                         print "[timer.py] timewarp - re-evaluating all processed timers."
160                         tl = self.processed_timers
161                         self.processed_timers = [ ]
162                         for x in tl:
163                                 # simulate a "waiting" state to give them a chance to re-occure
164                                 x.resetState()
165                                 self.addTimerEntry(x, noRecalc=1)
166                 
167                 self.processActivation()
168                 self.lastActivation = time.time()
169         
170                 min = int(time.time()) + self.MaxWaitTime
171                 
172                 # calculate next activation point
173                 if len(self.timer_list):
174                         w = self.timer_list[0].getNextActivation()
175                         if w < min:
176                                 min = w
177                 
178                 self.setNextActivation(min)
179         
180         def timeChanged(self, timer):
181                 print "time changed"
182                 timer.timeChanged()
183                 if timer.state == TimerEntry.StateEnded:
184                         self.processed_timers.remove(timer)
185                 else:
186                         self.timer_list.remove(timer)
187
188                 # give the timer a chance to re-enqueue
189                 if timer.state == TimerEntry.StateEnded:
190                         timer.state = TimerEntry.StateWaiting
191                 self.addTimerEntry(timer)
192         
193         def doActivate(self, w):
194                 self.timer_list.remove(w)
195                 
196                 # when activating a timer which has already passed,
197                 # simply abort the timer. don't run trough all the stages.
198                 if w.shouldSkip():
199                         w.state = TimerEntry.StateEnded
200                 else:
201                         # when active returns true, this means "accepted".
202                         # otherwise, the current state is kept.
203                         # the timer entry itself will fix up the delay then.
204                         if w.activate():
205                                 w.state += 1
206
207                 # did this timer reached the last state?
208                 if w.state < TimerEntry.StateEnded:
209                         # no, sort it into active list
210                         bisect.insort(self.timer_list, w)
211                 else:
212                         # yes. Process repeated, and re-add.
213                         if w.repeated:
214                                 w.processRepeated()
215                                 w.state = TimerEntry.StateWaiting
216                                 self.addTimerEntry(w)
217                         else:
218                                 bisect.insort(self.processed_timers, w)
219                 
220                 self.stateChanged(w)
221
222         def processActivation(self):
223                 print "It's now ", time.strftime("%c", time.localtime(time.time()))
224                 t = int(time.time()) + 1
225                 
226                 # we keep on processing the first entry until it goes into the future.
227                 while len(self.timer_list) and self.timer_list[0].getNextActivation() < t:
228                         self.doActivate(self.timer_list[0])