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