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