aboutsummaryrefslogtreecommitdiff
path: root/timer.py
blob: 6c174c867e5f49226119d844fd06826aecb59971 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import bisect
import time
from enigma import *

class TimerEntry:
	EventPrepare = 0
	EventStart   = 1
	EventEnd     = 2
	EventAbort   = 3
	
	StateWait    = 0
	StatePrepare = 1
	StateRunning = 2
	StateEnded   = 3
	
	def __init__(self, begin, end):
		self.begin = begin
		self.prepare_time = 10
		self.end = end
		self.state = 0
		self.resetRepeated()
		
	def resetRepeated(self):
		self.repeated = int(0)
		
	def setRepeated(self, day):
		self.repeated |= (2 ** day)
		print "Repeated: " + str(self.repeated)
		
	def getTime(self):
		if self.state == self.StateWait:
			return self.begin - self.prepare_time
		elif self.state == self.StatePrepare:
			return self.begin
		else:
			return self.end 
	
	def __lt__(self, o):
		return self.getTime() < o.getTime()
	
	def activate(self, event):
		print "[timer.py] timer %s got activated (%d)!" % (self.description, event)

class Timer:

	# the time between "polls". We do this because
	# we want to account for time jumps etc.
	# of course if they occur <100s before starting,
	# it's not good. thus, you have to repoll when
	# you change the time.
	#
	# this is just in case. We don't want the timer 
	# hanging. we use this "edge-triggered-polling-scheme"
	# anyway, so why don't make it a bit more fool-proof?
	MaxWaitTime = 100

	def __init__(self):
		self.timer_list = [ ]
		self.processed_timers = [ ]
		
		self.timer = eTimer()
		self.timer.timeout.get().append(self.calcNextActivation)
		self.lastActivation = time.time()
		
		self.calcNextActivation()
	
	def addTimerEntry(self, entry, noRecalc=0):
		# we either go trough Prepare/Start/End-state if the timer is still running,
		# or skip it when it's alrady past the end.
		if entry.end > time.time():
			bisect.insort(self.timer_list, entry)
			if not noRecalc:
				self.calcNextActivation()
		else:
			bisect.insort(self.processed_timers, entry)
	
	def setNextActivation(self, when):
		delay = int((when - time.time()) * 1000)
		print "[timer.py] next activation: %d (in %d ms)" % (when, delay)
		
		self.timer.start(delay, 1)
		self.next = when

	def calcNextActivation(self):
		if self.lastActivation > time.time():
			print "[timer.py] timewarp - re-evaluating all processed timers."
			tl = self.processed_timers
			self.processed_timers = [ ]
			for x in tl:
				self.addTimerEntry(x, noRecalc=1)
		
		self.processActivation()
		self.lastActivation = time.time()
	
		min = int(time.time()) + self.MaxWaitTime
		
		# calculate next activation point
		if len(self.timer_list):
			w = self.timer_list[0].getTime()
			if w < min:
				min = w
		
		self.setNextActivation(min)
	
	def timeChanged(self, timer):
		self.timer_list.remove(timer)
		self.addTimerEntry(timer)
	
	def doActivate(self, w):
		w.activate(w.state)
		self.timer_list.remove(w)
		w.state += 1
		if w.state < TimerEntry.StateEnded:
			bisect.insort(self.timer_list, w)
		else:
			bisect.insort(self.processed_timers, w)
	
	def processActivation(self):
		t = int(time.time()) + 1
		
		# we keep on processing the first entry until it goes into the future.
		while len(self.timer_list) and self.timer_list[0].getTime() < t:
			self.doActivate(self.timer_list[0])