5779b3b0227b7dbd14407bc0087bab839f3bb3d7
[enigma2.git] / lib / python / Plugins / SystemPlugins / DiseqcTester / plugin.py
1 from Screens.Satconfig import NimSelection
2 from Screens.Screen import Screen
3 from Screens.TextBox import TextBox
4
5 from Plugins.Plugin import PluginDescriptor
6
7 from Components.ActionMap import ActionMap, NumberActionMap
8 from Components.NimManager import nimmanager
9 from Components.ResourceManager import resourcemanager
10 from Components.Sources.FrontendStatus import FrontendStatus
11 from Components.TuneTest import TuneTest
12 from Components.Sources.List import List
13 from Components.Sources.Progress import Progress
14 from Components.Sources.StaticText import StaticText
15 from Components.ConfigList import ConfigListScreen
16 from Components.config import getConfigListEntry, ConfigSelection
17
18 # always use:
19 # setResultType(type)
20 # setResultParameter(parameter)
21 # getTextualResult()
22 class ResultParser:
23         def __init__(self):
24                 pass
25         
26         TYPE_BYORBPOS = 0
27         TYPE_BYINDEX = 1
28         TYPE_ALL = 2
29         def setResultType(self, type):
30                 self.type = type
31                 
32         def setResultParameter(self, parameter):
33                 if self.type == self.TYPE_BYORBPOS:
34                         self.orbpos = parameter
35                 elif self.type == self.TYPE_BYINDEX:
36                         self.index = parameter
37                         
38         def getTextualResultForIndex(self, index):
39                 text = ""
40                 text += "%s:\n" % self.getTextualIndexRepresentation(index)
41                 
42                 failed, successful = self.results[index]["failed"], self.results[index]["successful"]
43                 countfailed = len(failed)
44                 countsuccessful = len(successful)
45                 countall = countfailed + countsuccessful
46                 percentfailed = round(countfailed / float(countall + 0.0001) * 100)
47                 percentsuccessful = round(countsuccessful / float(countall + 0.0001) * 100)
48                 text += "Tested %d transponders\n%d (%d %%) transponders succeeded\n%d (%d %%) transponders failed\n" % (countall, countsuccessful, percentsuccessful, countfailed, percentfailed)
49                 reasons = {}
50                 if countfailed > 0:
51                         for transponder in failed:
52                                 reasons[transponder[2]] = reasons.get(transponder[2], [])
53                                 reasons[transponder[2]].append(transponder)
54                                 if transponder[2] == "pids_failed":
55                                         print transponder[2], "-", transponder[3]
56                                 
57                         text += "The %d unsuccessful tuning attempts failed for the following reasons:\n" % countfailed
58                         
59                         for reason in reasons.keys():
60                                 text += "%s: %d transponders failed\n" % (reason, len(reasons[reason]))
61                         
62                         for reason in reasons.keys():
63                                 text += "\n"
64                                 text += "%s previous planes:\n" % reason
65                                 for transponder in reasons[reason]:
66                                         if transponder[1] is not None:
67                                                 text += self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[1]))
68                                         else:
69                                                 text += "No transponder tuned"
70                                         text += " ==> " + self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[0]))
71                                         text += "\n"
72                 if countsuccessful > 0:
73                         text += "\n"
74                         text += "Successfully tuned transponders' previous planes:\n" 
75                         for transponder in successful:
76                                 if transponder[1] is not None:
77                                         text += self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[1]))
78                                 else:
79                                         text += "No transponder tuned"
80                                 text += " ==> " + self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[0]))
81                                 text += "\n"
82                 return text
83
84         def getTextualResult(self):
85                 text = ""
86                 if self.type == self.TYPE_BYINDEX:
87                         text += self.getTextualResultForIndex(self.index)
88                 elif self.type == self.TYPE_BYORBPOS:
89                         for index in self.results.keys():
90                                 if index[2] == self.orbpos:
91                                         text += self.getTextualResultForIndex(index)
92                                         text += "\n-----------------------------------------------------\n"
93                                 
94                 return text
95
96 class DiseqcTester(Screen, TuneTest, ResultParser):
97         skin = """
98                 <screen position="90,100" size="520,400" title="DiSEqC Tester" >
99                 <!--ePixmap pixmap="skin_default/icons/dish_scan.png" position="5,25" zPosition="0" size="119,110" transparent="1" alphatest="on" />
100                 <widget source="Frontend" render="Label" position="190,10" zPosition="2" size="260,20" font="Regular;19" halign="center" valign="center" transparent="1">
101                         <convert type="FrontendInfo">SNRdB</convert>
102                 </widget>
103                 <eLabel name="snr" text="SNR:" position="120,35" size="60,22" font="Regular;21" halign="right" transparent="1" />
104                 <widget source="Frontend" render="Progress" position="190,35" size="260,20" pixmap="skin_default/bar_snr.png" borderWidth="2" borderColor="#cccccc">
105                         <convert type="FrontendInfo">SNR</convert>
106                 </widget>
107                 <widget source="Frontend" render="Label" position="460,35" size="60,22" font="Regular;21">
108                         <convert type="FrontendInfo">SNR</convert>
109                 </widget>
110                 <eLabel name="agc" text="AGC:" position="120,60" size="60,22" font="Regular;21" halign="right" transparent="1" />
111                 <widget source="Frontend" render="Progress" position="190,60" size="260,20" pixmap="skin_default/bar_snr.png" borderWidth="2" borderColor="#cccccc">
112                         <convert type="FrontendInfo">AGC</convert>
113                 </widget>
114                 <widget source="Frontend" render="Label" position="460,60" size="60,22" font="Regular;21">
115                         <convert type="FrontendInfo">AGC</convert>
116                 </widget>
117                 <eLabel name="ber" text="BER:" position="120,85" size="60,22" font="Regular;21" halign="right" transparent="1" />
118                 <widget source="Frontend" render="Progress" position="190,85" size="260,20" pixmap="skin_default/bar_ber.png" borderWidth="2" borderColor="#cccccc">
119                         <convert type="FrontendInfo">BER</convert>
120                 </widget>
121                 <widget source="Frontend" render="Label" position="460,85" size="60,22" font="Regular;21">
122                         <convert type="FrontendInfo">BER</convert>
123                 </widget>
124                 <eLabel name="lock" text="Lock:" position="120,115" size="60,22" font="Regular;21" halign="right" />
125                 <widget source="Frontend" render="Pixmap" pixmap="skin_default/icons/lock_on.png" position="190,110" zPosition="1" size="38,31" alphatest="on">
126                         <convert type="FrontendInfo">LOCK</convert>
127                         <convert type="ConditionalShowHide" />
128                 </widget>
129                 <widget source="Frontend" render="Pixmap" pixmap="skin_default/icons/lock_off.png" position="190,110" zPosition="1" size="38,31" alphatest="on">
130                         <convert type="FrontendInfo">LOCK</convert>
131                         <convert type="ConditionalShowHide">Invert</convert>
132                 </widget-->
133                 <widget source="progress_list" render="Listbox" position="0,0" size="510,150" scrollbarMode="showOnDemand">
134                         <convert type="TemplatedMultiContent">
135                                 {"template": [
136                                                 MultiContentEntryText(pos = (10, 0), size = (330, 25), flags = RT_HALIGN_LEFT, text = 1), # index 1 is the index name,
137                                                 MultiContentEntryText(pos = (330, 0), size = (150, 25), flags = RT_HALIGN_RIGHT, text = 2) # index 2 is the status,
138                                         ],
139                                  "fonts": [gFont("Regular", 20)],
140                                  "itemHeight": 25
141                                 }
142                         </convert>
143                 </widget>
144                 <eLabel name="overall_progress" text="Overall progress:" position="20,162" size="480,22" font="Regular;21" halign="center" transparent="1" />
145                 <widget source="overall_progress" render="Progress" position="20,192" size="480,20" borderWidth="2" backgroundColor="#254f7497" />
146                 <eLabel name="overall_progress" text="Progress:" position="20,222" size="480,22" font="Regular;21" halign="center" transparent="1" />
147                 <widget source="sub_progress" render="Progress" position="20,252" size="480,20" borderWidth="2" backgroundColor="#254f7497" />
148                 
149                 <eLabel name="" text="Failed:" position="20,282" size="140,22" font="Regular;21" halign="left" transparent="1" />
150                 <widget source="failed_counter" render="Label" position="160,282" size="100,20" font="Regular;21" />
151                 
152                 <eLabel name="" text="Succeeded:" position="20,312" size="140,22" font="Regular;21" halign="left" transparent="1" />
153                 <widget source="succeeded_counter" render="Label" position="160,312" size="100,20" font="Regular;21" />
154                 
155                 <eLabel name="" text="With errors:" position="20,342" size="140,22" font="Regular;21" halign="left" transparent="1" />
156                 <widget source="witherrors_counter" render="Label" position="160,342" size="100,20" font="Regular;21" />
157                 
158                 <eLabel name="" text="Not tested:" position="20,372" size="140,22" font="Regular;21" halign="left" transparent="1" />
159                 <widget source="untestable_counter" render="Label" position="160,372" size="100,20" font="Regular;21" />
160                 
161                 <widget source="CmdText" render="Label" position="300,282" size="180,200" font="Regular;21" />
162                 </screen>"""
163                 
164         TEST_TYPE_QUICK = 0
165         TEST_TYPE_RANDOM = 1
166         TEST_TYPE_COMPLETE = 2
167         def __init__(self, session, feid, test_type = TEST_TYPE_QUICK, loopsfailed = 3, loopssuccessful = 1):
168                 Screen.__init__(self, session)
169                 self.feid = feid
170                 self.test_type = test_type
171                 self.loopsfailed = loopsfailed
172                 self.loopssuccessful = loopssuccessful
173                 
174                 self["actions"] = NumberActionMap(["SetupActions"],
175                 {
176                         "ok": self.select,
177                         "cancel": self.keyCancel,
178                 }, -2)
179                 
180                 TuneTest.__init__(self, feid, stopOnSuccess = self.loopssuccessful, stopOnError = self.loopsfailed)
181                 #self["Frontend"] = FrontendStatus(frontend_source = lambda : self.frontend, update_interval = 100)
182                 self["overall_progress"] = Progress()
183                 self["sub_progress"] = Progress()
184                 
185                 self["failed_counter"] = StaticText("0")
186                 self["succeeded_counter"] = StaticText("0")
187                 self["witherrors_counter"] = StaticText("0")
188                 self["untestable_counter"] = StaticText("0")
189                 
190                 self.list = []
191                 self["progress_list"] = List(self.list)
192                 self["progress_list"].onSelectionChanged.append(self.selectionChanged)
193                 
194                 self["CmdText"] = StaticText(_("Please wait while scanning is in progress..."))
195                                 
196                 self.indexlist = {}
197                 self.readTransponderList()
198                 
199                 self.running = False
200                 
201                 self.results = {}
202                 self.resultsstatus = {}
203                 
204                 self.onLayoutFinish.append(self.go)
205                 
206         def getProgressListComponent(self, index, status):
207                 return (index, self.getTextualIndexRepresentation(index), status)
208         
209         def clearProgressList(self):
210                 self.list = []
211                 self["progress_list"].list = self.list
212         
213         def addProgressListItem(self, index):
214                 if index in self.indexlist:
215                         for entry in self.list:
216                                 if entry[0] == index:
217                                         self.changeProgressListStatus(index, "working")
218                                         return
219                         self.list.append(self.getProgressListComponent(index, _("working")))
220                         self["progress_list"].list = self.list
221                         self["progress_list"].setIndex(len(self.list) - 1)
222
223         def changeProgressListStatus(self, index, status):
224                 self.newlist = []
225                 count = 0
226                 indexpos = 0
227                 for entry in self.list:
228                         if entry[0] == index:
229                                 self.newlist.append(self.getProgressListComponent(index, status))
230                                 indexpos = count
231                         else:
232                                 self.newlist.append(entry)
233                         count += 1
234                 self.list = self.newlist
235                 self["progress_list"].list = self.list
236                 self["progress_list"].setIndex(indexpos)
237
238         def readTransponderList(self):
239                 for sat in nimmanager.getSatListForNim(self.feid):
240                         for transponder in nimmanager.getTransponders(sat[0]):
241                                 #print transponder
242                                 mytransponder = (transponder[1] / 1000, transponder[2] / 1000, transponder[3], transponder[4], transponder[5], sat[0], None, None, transponder[10], transponder[11])
243                                 self.analyseTransponder(mytransponder)
244
245         def getIndexForTransponder(self, transponder):
246                 
247                 if transponder[0] < 11700:
248                         band = 1 # low
249                 else:
250                         band = 0 # high
251                 
252                 polarisation = transponder[2]
253                 
254                 sat = transponder[5]
255                 
256                 index = (band, polarisation, sat)
257                 return index
258
259         # sort the transponder into self.transponderlist
260         def analyseTransponder(self, transponder):
261                 index = self.getIndexForTransponder(transponder)
262                 if index not in self.indexlist:
263                         self.indexlist[index] = []
264                 self.indexlist[index].append(transponder)
265                 #print "self.indexlist:", self.indexlist
266         
267         # returns a string for the user representing a human readable output for index 
268         def getTextualIndexRepresentation(self, index):
269                 print "getTextualIndexRepresentation:", index
270                 text = ""
271                 
272                 text += nimmanager.getSatDescription(index[2]) + ", "
273                 
274                 if index[0] == 1:
275                         text += "Low Band, "
276                 else:
277                         text += "High Band, "
278                         
279                 if index[1] == 0:
280                         text += "H"
281                 else:
282                         text += "V"
283                 return text
284         
285         def fillTransponderList(self):
286                 self.clearTransponder()
287                 print "----------- fillTransponderList"
288                 print "index:", self.currentlyTestedIndex
289                 keys = self.indexlist.keys()
290                 if self.getContinueScanning():
291                         print "index:", self.getTextualIndexRepresentation(self.currentlyTestedIndex)
292                         for transponder in self.indexlist[self.currentlyTestedIndex]:
293                                 self.addTransponder(transponder)
294                         print "transponderList:", self.transponderlist
295                         return True
296                 else:
297                         return False
298                 
299         def progressCallback(self, progress):
300                 if progress[0] != self["sub_progress"].getRange():
301                         self["sub_progress"].setRange(progress[0])
302                 self["sub_progress"].setValue(progress[1])
303
304         # logic for scanning order of transponders
305         # on go getFirstIndex is called
306         def getFirstIndex(self):
307                 # TODO use other function to scan more randomly
308                 if self.test_type == self.TEST_TYPE_QUICK:
309                         self.myindex = 0
310                         keys = self.indexlist.keys()
311                         keys.sort(key = lambda a: a[2]) # sort by orbpos
312                         self["overall_progress"].setRange(len(keys))
313                         self["overall_progress"].setValue(self.myindex)
314                         return keys[0]
315                 
316         # after each index is finished, getNextIndex is called to get the next index to scan 
317         def getNextIndex(self):
318                 # TODO use other function to scan more randomly
319                 if self.test_type == self.TEST_TYPE_QUICK:
320                         self.myindex += 1
321                         keys = self.indexlist.keys()
322                         keys.sort(key = lambda a: a[2]) # sort by orbpos
323                         
324                         self["overall_progress"].setValue(self.myindex)
325                         if self.myindex < len(keys):
326                                 return keys[self.myindex]
327                         else:
328                                 return None
329         
330         # after each index is finished and the next index is returned by getNextIndex
331         # the algorithm checks, if we should continue scanning
332         def getContinueScanning(self):
333                 if self.test_type == self.TEST_TYPE_QUICK:
334                         return (self.myindex < len(self.indexlist.keys()))
335                 
336         def addResult(self, index, status, failedTune, successfullyTune):
337                 self.results[index] = self.results.get(index, {"failed": [], "successful": [], "status": None})
338                 self.resultsstatus[status] = self.resultsstatus.get(status, [])
339                 
340                 self.results[index]["status"] = status
341                 self.results[index]["failed"] = failedTune
342                 self.results[index]["successful"] = successfullyTune
343                 
344                 self.resultsstatus[status].append(index)
345         
346         def finishedChecking(self):
347                 print "finishedChecking"
348                 TuneTest.finishedChecking(self)
349
350                 if not self.results.has_key(self.currentlyTestedIndex):
351                         self.results[self.currentlyTestedIndex] = {"failed": [], "successful": [], "status": None}
352                 
353                 if len(self.failedTune) > 0 and len(self.successfullyTune) > 0:
354                         self.changeProgressListStatus(self.currentlyTestedIndex, "with errors")
355                         self["witherrors_counter"].setText(str(int(self["witherrors_counter"].getText()) + 1))
356                         self.addResult(self.currentlyTestedIndex, "with_errors", self.failedTune, self.successfullyTune)
357                 elif len(self.failedTune) == 0 and len(self.successfullyTune) == 0:
358                         self.changeProgressListStatus(self.currentlyTestedIndex, "not tested")
359                         self["untestable_counter"].setText(str(int(self["untestable_counter"].getText()) + 1))
360                         self.addResult(self.currentlyTestedIndex, "untestable", self.failedTune, self.successfullyTune)
361                 elif len(self.failedTune) > 0:
362                         self.changeProgressListStatus(self.currentlyTestedIndex, "failed")
363                         self["failed_counter"].setText(str(int(self["failed_counter"].getText()) + len(self.failedTune)))
364                         self.addResult(self.currentlyTestedIndex, "failed", self.failedTune, self.successfullyTune)
365                 else:
366                         self.changeProgressListStatus(self.currentlyTestedIndex, "successful")
367                         self["succeeded_counter"].setText(str(int(self["succeeded_counter"].getText()) + len(self.successfullyTune)))
368                         self.addResult(self.currentlyTestedIndex, "successful", self.failedTune, self.successfullyTune)
369                         
370                         
371                 #self["failed_counter"].setText(str(int(self["failed_counter"].getText()) + len(self.failedTune)))
372                 #self["succeeded_counter"].setText(str(int(self["succeeded_counter"].getText()) + len(self.successfullyTune)))
373                 #if len(self.failedTune) == 0 and len(self.successfullyTune) == 0:
374                         #self["untestable_counter"].setText(str(int(self["untestable_counter"].getText()) + 1))
375                         
376                 self.currentlyTestedIndex = self.getNextIndex()
377                 self.addProgressListItem(self.currentlyTestedIndex)
378                 
379                 if self.fillTransponderList():
380                         self.run(checkPIDs = True)
381                 else:
382                         self.running = False
383                         self["progress_list"].setIndex(0)
384                         print "results:", self.results
385                         print "resultsstatus:", self.resultsstatus
386
387         def go(self):
388                 self.running = True
389                 self["failed_counter"].setText("0")
390                 self["succeeded_counter"].setText("0")
391                 self["untestable_counter"].setText("0")
392                 self.currentlyTestedIndex = self.getFirstIndex()
393                 
394                 self.clearProgressList()
395                 self.addProgressListItem(self.currentlyTestedIndex)
396                 
397                 if self.fillTransponderList():
398                         self.run(True)
399
400         def keyCancel(self):
401                 self.close()
402                 
403         def select(self):
404                 print "selectedIndex:", self["progress_list"].getCurrent()[0]
405                 if not self.running:
406                         index = self["progress_list"].getCurrent()[0]
407                         #self.setResultType(ResultParser.TYPE_BYORBPOS)
408                         #self.setResultParameter(index[2])
409                         self.setResultType(ResultParser.TYPE_BYINDEX)
410                         self.setResultParameter(index)
411                         self.session.open(TextBox, self.getTextualResult())
412         
413         def selectionChanged(self):
414                 print "selection changed"
415                 if len(self.list) > 0 and not self.running:
416                         self["CmdText"].setText(_("Press OK to get further details for %s") % str(self["progress_list"].getCurrent()[1]))
417
418 class DiseqcTesterTestTypeSelection(Screen, ConfigListScreen):
419         skin = """<screen position="80,95" size="560,412" title="DiSEqC Tester Test Settings">
420                 <widget name="config" position="10,10" size="540,402" scrollbarMode="showOnDemand" />
421         </screen>
422         """
423         def __init__(self, session, feid):
424                 Screen.__init__(self, session)
425                 self.feid = feid
426                 
427                 self.list = []
428                 ConfigListScreen.__init__(self, self.list)
429                 
430                 self["actions"] = ActionMap(["SetupActions"],
431                 {
432                         "cancel": self.keyCancel
433                 }, -2)
434                 
435                 self.createSetup()
436                 
437         def createSetup(self):
438                 self.testtype = ConfigSelection(choices={"quick": _("Quick")}, default = "quick")
439                 self.testtypeEntry = getConfigListEntry(_("Test Type"), self.testtype)
440                 self.list.append(self.testtypeEntry)
441                 
442                 self.loopsfailed = ConfigSelection(choices={"-1": "Every known", "1": "1", "2": "2", "3": "3", "4": "4", "5": "5", "6": "6", "7": "7", "8": "8"}, default = "3")
443                 self.loopsfailedEntry = getConfigListEntry(_("Stop testing plane after # failed transponders"), self.loopsfailed)
444                 self.list.append(self.loopsfailedEntry)
445                 
446                 self.loopssuccessful = ConfigSelection(choices={"-1": "Every known", "1": "1", "2": "2", "3": "3", "4": "4", "5": "5", "6": "6", "7": "7", "8": "8"}, default = "1")
447                 self.loopssuccessfulEntry = getConfigListEntry(_("Stop testing plane after # successful transponders"), self.loopssuccessful)
448                 self.list.append(self.loopssuccessfulEntry)
449                 
450                 self["config"].list = self.list
451                 self["config"].l.setList(self.list)
452                 
453         def keyOK(self):
454                 print self.testtype.getValue()
455                 testtype = DiseqcTester.TEST_TYPE_QUICK
456                 if self.testtype.getValue() == "quick":
457                         testtype = DiseqcTester.TEST_TYPE_QUICK
458                 elif self.testtype.getValue() == "random":
459                         testtype = DiseqcTester.TEST_TYPE_RANDOM
460                 elif self.testtype.getValue() == "complete":
461                         testtype = DiseqcTester.TEST_TYPE_COMPLETE
462                 self.session.open(DiseqcTester, feid = self.feid, test_type = testtype, loopsfailed = int(self.loopsfailed.value), loopssuccessful = int(self.loopssuccessful.value))
463         
464         def keyCancel(self):
465                 self.close()
466
467 class DiseqcTesterNimSelection(NimSelection):
468         skin = """
469                 <screen position="160,123" size="400,330" title="Choose Tuner">
470                 <widget source="nimlist" render="Listbox" position="0,0" size="380,300" scrollbarMode="showOnDemand">
471                         <convert type="TemplatedMultiContent">
472                                 {"template": [
473                                                 MultiContentEntryText(pos = (10, 5), size = (360, 30), flags = RT_HALIGN_LEFT, text = 1), # index 1 is the nim name,
474                                                 MultiContentEntryText(pos = (50, 30), size = (320, 30), font = 1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is a description of the nim settings,
475                                         ],
476                                  "fonts": [gFont("Regular", 20), gFont("Regular", 15)],
477                                  "itemHeight": 70
478                                 }
479                         </convert>
480                 </widget>
481         </screen>"""
482                 
483         def __init__(self, session, args = None):
484                 NimSelection.__init__(self, session)
485
486         def setResultClass(self):
487                 #self.resultclass = DiseqcTester
488                 self.resultclass = DiseqcTesterTestTypeSelection
489                 
490         def showNim(self, nim):
491                 nimConfig = nimmanager.getNimConfig(nim.slot)
492                 if nim.isCompatible("DVB-S"):
493                         if nimConfig.configMode.value in ["loopthrough", "equal", "satposdepends", "nothing"]:
494                                 return False
495                         if nimConfig.configMode.value == "simple":
496                                 if nimConfig.diseqcMode.value == "positioner":
497                                         return True
498                         return True
499                 return False
500
501 def DiseqcTesterMain(session, **kwargs):
502         session.open(DiseqcTesterNimSelection)
503         
504 def autostart(reason, **kwargs):
505         resourcemanager.addResource("DiseqcTester", DiseqcTesterMain)
506
507 def Plugins(**kwargs):
508         return [ PluginDescriptor(name="DiSEqC Tester", description=_("Test DiSEqC settings"), where = PluginDescriptor.WHERE_PLUGINMENU, fnc=DiseqcTesterMain),
509                         PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart)]