Merge commit 'origin/master' into tmbinc/FixTimingBugs
[enigma2.git] / lib / python / Components / TimerSanityCheck.py
1 import NavigationInstance
2 from time import localtime, mktime, gmtime
3 from ServiceReference import ServiceReference
4 from enigma import iServiceInformation, eServiceCenter, eServiceReference
5
6 class TimerSanityCheck:
7         def __init__(self, timerlist, newtimer=None):
8                 print "sanitycheck"
9                 self.localtimediff = 25*3600 - mktime(gmtime(25*3600))
10                 self.timerlist = timerlist
11                 self.newtimer = newtimer
12                 self.simultimer = []
13                 self.rep_eventlist = []
14                 self.nrep_eventlist = []
15                 self.bflag = 1
16                 self.eflag = -1
17
18         def check(self, ext_timer=1):
19                 print "check"
20                 if ext_timer != 1:
21                         self.newtimer = ext_timer
22                 if self.newtimer is None:
23                         self.simultimer = []
24                 else:
25                         self.simultimer = [ self.newtimer ]
26                 return self.checkTimerlist()
27
28         def getSimulTimerList(self):
29                 return self.simultimer
30
31         def doubleCheck(self):
32                 if self.newtimer is not None and self.newtimer.service_ref.ref.valid():
33                         self.simultimer = [ self.newtimer ]
34                         for timer in self.timerlist:
35                                 if (timer == self.newtimer):
36                                         return True
37                                 else:
38                                         if timer.begin == self.newtimer.begin:
39                                                 getUnsignedDataRef1 = timer.service_ref.ref.getUnsignedData
40                                                 getUnsignedDataRef2 = self.newtimer.service_ref.ref.getUnsignedData
41                                                 for x in (1, 2, 3, 4):
42                                                         if getUnsignedDataRef1(x) != getUnsignedDataRef2(x):
43                                                                 break;
44                                                 else:
45                                                         return True
46                 return False
47
48         def checkTimerlist(self, ext_timer=1):
49                 #with special service for external plugins
50                 # Entries in eventlist
51                 # timeindex
52                 # BeginEndFlag 1 for begin, -1 for end
53                 # index -1 for the new Timer, 0..n index of the existing timers
54                 # count of running timers
55
56                 serviceHandler = eServiceCenter.getInstance()
57                 print "checkTimerlist"
58 # create a list with all start and end times
59 # split it into recurring and singleshot timers
60
61 ##################################################################################
62 # process the new timer
63                 self.rep_eventlist = []
64                 self.nrep_eventlist = []
65                 if ext_timer != 1:
66                         self.newtimer = ext_timer
67                 if (self.newtimer is not None) and (not self.newtimer.disabled):
68                         if not self.newtimer.service_ref.ref.valid():
69                                 return False
70                         rflags = self.newtimer.repeated
71                         rflags = ((rflags & 0x7F)>> 3)|((rflags & 0x07)<<4)
72                         if rflags:
73                                 begin = self.newtimer.begin % 86400 # map to first day
74                                 if (self.localtimediff > 0) and ((begin + self.localtimediff) > 86400):
75                                         rflags = ((rflags >> 1)& 0x3F)|((rflags << 6)& 0x40)
76                                 elif (self.localtimediff < 0) and (begin < self.localtimediff):
77                                         rflags = ((rflags << 1)& 0x7E)|((rflags >> 6)& 0x01)
78                                 while rflags: # then arrange on the week
79                                         if rflags & 1:
80                                                 self.rep_eventlist.append((begin, -1))
81                                         begin += 86400
82                                         rflags >>= 1
83                         else:
84                                 self.nrep_eventlist.extend([(self.newtimer.begin,self.bflag,-1),(self.newtimer.end,self.eflag,-1)])
85
86 ##################################################################################
87 # now process existing timers
88                 idx = 0
89                 for timer in self.timerlist:
90                         if (timer != self.newtimer) and (not timer.disabled):
91                                 if timer.repeated:
92                                         rflags = timer.repeated
93                                         rflags = ((rflags & 0x7F)>> 3)|((rflags & 0x07)<<4)
94                                         begin = timer.begin % 86400 # map all to first day
95                                         if (self.localtimediff > 0) and ((begin + self.localtimediff) > 86400):
96                                                 rflags = ((rflags >> 1)& 0x3F)|((rflags << 6)& 0x40)
97                                         elif (self.localtimediff < 0) and (begin < self.localtimediff):
98                                                 rflags = ((rflags << 1)& 0x7E)|((rflags >> 6)& 0x01)
99                                         while rflags:
100                                                 if rflags & 1:
101                                                         self.rep_eventlist.append((begin, idx))
102                                                 begin += 86400
103                                                 rflags >>= 1
104                                 else:
105                                         self.nrep_eventlist.extend([(timer.begin,self.bflag,idx),(timer.end,self.eflag,idx)])
106                         idx += 1
107
108 ################################################################################
109 # journalize timer repeations
110                 if self.nrep_eventlist:
111                         interval_begin = min(self.nrep_eventlist)[0]
112                         interval_end = max(self.nrep_eventlist)[0]
113                         offset_0 = interval_begin - (interval_begin % 604800)
114                         weeks = (interval_end - offset_0) / 604800
115                         if ((interval_end - offset_0) % 604800):
116                                 weeks += 1
117                         for cnt in range(weeks):
118                                 for event in self.rep_eventlist:
119                                         if event[1] == -1: # -1 is the identifier of the changed timer
120                                                 event_begin = self.newtimer.begin
121                                                 event_end = self.newtimer.end
122                                         else:
123                                                 event_begin = self.timerlist[event[1]].begin
124                                                 event_end = self.timerlist[event[1]].end
125                                         new_event_begin = event[0] + offset_0 + (cnt * 604800)
126                                         # summertime correction
127                                         new_lth = localtime(new_event_begin).tm_hour
128                                         new_event_begin += 3600 * (localtime(event_begin).tm_hour - new_lth)
129                                         new_event_end = new_event_begin + (event_end - event_begin)
130                                         if event[1] == -1:
131                                                 if new_event_begin >= self.newtimer.begin: # is the soap already running?
132                                                         self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]),(new_event_end, self.eflag, event[1])])
133                                         else:
134                                                 if new_event_begin >= self.timerlist[event[1]].begin: # is the soap already running?
135                                                         self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]),(new_event_end, self.eflag, event[1])])
136                 else:
137                         offset_0 = 345600 # the Epoch begins on Thursday
138                         for cnt in (0, 1): # test two weeks to take care of Sunday-Monday transitions
139                                 for event in self.rep_eventlist:
140                                         if event[1] == -1: # -1 is the identifier of the changed timer
141                                                 event_begin = self.newtimer.begin
142                                                 event_end = self.newtimer.end
143                                         else:
144                                                 event_begin = self.timerlist[event[1]].begin
145                                                 event_end = self.timerlist[event[1]].end
146                                         new_event_begin = event[0] + offset_0 + (cnt * 604800)
147                                         new_event_end = new_event_begin + (event_end - event_begin)
148                                         self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]),(new_event_end, self.eflag, event[1])])
149
150 ################################################################################
151 # order list chronological
152                 self.nrep_eventlist.sort()
153
154 ##################################################################################
155 # detect overlapping timers and overlapping times
156                 fakeRecList = []
157                 ConflictTimer = None
158                 ConflictTunerType = None
159                 newTimerTunerType = None
160                 cnt = 0
161                 idx = 0
162                 overlaplist = []
163                 for event in self.nrep_eventlist:
164                         cnt += event[1]
165                         if event[2] == -1: # new timer
166                                 timer = self.newtimer
167                         else:
168                                 timer = self.timerlist[event[2]]
169                         if event[1] == self.bflag:
170                                 tunerType = [ ]
171                                 fakeRecService = NavigationInstance.instance.recordService(timer.service_ref, True)
172                                 if fakeRecService:
173                                         fakeRecResult = fakeRecService.start(True)
174                                 else:
175                                         fakeRecResult = -1
176                                 if not fakeRecResult: # tune okay
177                                         feinfo = fakeRecService.frontendInfo().getFrontendData()
178                                         tunerType.append(feinfo.get("tuner_type"))
179                                 else: # tune failed.. so we must go another way to get service type (DVB-S, DVB-T, DVB-C)
180
181                                         def getServiceType(ref): # helper function to get a service type of a service reference
182                                                 serviceInfo = serviceHandler.info(ref)
183                                                 serviceInfo = serviceInfo and serviceInfo.getInfoObject(ref, iServiceInformation.sTransponderData)
184                                                 return serviceInfo and serviceInfo["tuner_type"] or ""
185
186                                         ref = timer.service_ref.ref
187                                         if ref.flags & eServiceReference.isGroup: # service group ?
188                                                 serviceList = serviceHandler.list(ref) # get all alternative services
189                                                 if serviceList:
190                                                         for ref in serviceList.getContent("R"): # iterate over all group service references
191                                                                 type = getServiceType(ref)
192                                                                 if not type in tunerType: # just add single time
193                                                                         tunerType.append(type) 
194                                         else:
195                                                 tunerType.append(getServiceType(ref))
196
197                                 if event[2] == -1: # new timer
198                                         newTimerTunerType = tunerType
199                                 overlaplist.append((fakeRecResult, timer, tunerType))
200                                 fakeRecList.append((timer, fakeRecService))
201                                 if fakeRecResult:
202                                         if ConflictTimer is None: # just take care of the first conflict
203                                                 ConflictTimer = timer
204                                                 ConflictTunerType = tunerType
205                         elif event[1] == self.eflag:
206                                 for fakeRec in fakeRecList:
207                                         if timer == fakeRec[0] and fakeRec[1]:
208                                                 NavigationInstance.instance.stopRecordService(fakeRec[1])
209                                                 fakeRecList.remove(fakeRec)
210                                 fakeRec = None
211                                 for entry in overlaplist:
212                                         if entry[1] == timer:
213                                                 overlaplist.remove(entry)
214                         else:
215                                 print "Bug: unknown flag!"
216                         self.nrep_eventlist[idx] = (event[0],event[1],event[2],cnt,overlaplist[:]) # insert a duplicate into current overlaplist
217                         idx += 1
218
219                 if ConflictTimer is None: # no conflict found :)
220                         return True
221
222 ##################################################################################
223 # we have detected a conflict, now we must figure out the involved timers
224
225                 if self.newtimer is not None: # new timer?
226                         if self.newtimer is not ConflictTimer: # the new timer is not the conflicting timer?
227                                 for event in self.nrep_eventlist:
228                                         if len(event[4]) > 1: # entry in overlaplist of this event??
229                                                 kt = False
230                                                 nt = False
231                                                 for entry in event[4]:
232                                                         if entry[1] is ConflictTimer:
233                                                                 kt = True
234                                                         if entry[1] is self.newtimer:
235                                                                 nt = True
236                                                 if nt and kt:
237                                                         ConflictTimer = self.newtimer
238                                                         ConflictTunerType = newTimerTunerType
239                                                         break
240
241                 self.simultimer = [ ConflictTimer ]
242                 for event in self.nrep_eventlist:
243                         if len(event[4]) > 1: # entry in overlaplist of this event??
244                                 for entry in event[4]:
245                                         if entry[1] is ConflictTimer:
246                                                 break
247                                 else:
248                                         continue
249                                 for entry in event[4]:
250                                         if not entry[1] in self.simultimer:
251                                                 for x in entry[2]:
252                                                         if x in ConflictTunerType:
253                                                                 self.simultimer.append(entry[1])
254                                                                 break
255
256                 if len(self.simultimer) < 2:
257                         print "Bug: unknown Conflict!"
258
259                 return False # conflict detected!