Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

PorcinePanel.py

00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2004
00004 #                                     by
00005 #                        The Board of Trustees of the
00006 #                     Leland Stanford Junior University.
00007 #                            All rights reserved.
00008 #
00009 
00010 __facility__ = "Online"
00011 __abstract__ = "GUI to control Power and Configuration of Event and Command Fabrics "
00012 __author__  = "Gregg Thayer <jgt@slac.stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__     = ("$Date: 2005/10/21 00:04:04 $").split(' ')[1]
00014 __version__ = "$Revision: 1.8 $"
00015 __release__  = "$Name: R04-12-00 $"
00016 __credits__ = "SLAC"
00017 
00018 import LATTE.copyright_SLAC
00019 import qt
00020 import logging
00021 import atexit
00022 import threading
00023 from LATTE.database.gFabrics import Fabrics
00024 from LATTE.tools.taskEngine import TaskEngine
00025 
00026 # This class exists to create a PIG gui as a MainWindow
00027 class PorcineWindow(qt.QMainWindow):
00028     def __init__(self,common,rc,*args):
00029         qt.QMainWindow.__init__(self,*args)
00030         self.__rc = rc
00031         self.__common = common
00032         self.__panel=PorcinePanel(self)
00033         self.setCentralWidget(self.__panel)
00034         self.__inShutdown = False
00035         self.__refreshComplete = threading.Event()
00036         self.__refreshComplete.set()
00037         atexit.register(self.shutdown)
00038 
00039     def shutdown(self):
00040         self.__inShutdown = True
00041         self.windowActivationChange = None
00042         self.__panel.shutdown()
00043         self.__refreshComplete.wait()
00044         self.close()
00045 
00046     def windowActivationChange(self, oldActive):
00047       if self.__inShutdown: return
00048       self.__refreshComplete.clear()
00049       if self.isActiveWindow():
00050         app = self.__rc.getApp()
00051         powerOn = self.__panel.getPowerTab().insertButton
00052         powerOff = self.__panel.getPowerTab().extractButton
00053         currentState = 'RESET'
00054         if app is not None:
00055           if app.__class__.__name__ == 'userSuite':
00056             currentState = app.getScriptState()
00057           elif self.__rc.getFSM() is not None:
00058             currentState = self.__rc.getFSM().current_state
00059         if currentState == 'RESET':
00060           powerOn.setEnabled(1)
00061           powerOff.setEnabled(1)
00062         else:
00063           powerOn.setEnabled(0)
00064           powerOff.setEnabled(0)
00065         self.__panel.refreshSchema(self.__common.getLAT(), self.__common.getXBRD())
00066         self.__panel.refreshStatus()
00067       self.__refreshComplete.set()
00068 
00069 
00070 
00071 # This class is the PIG gui Panel, which can be placed in any gui
00072 class PorcinePanel(qt.QVBox):
00073     __CR_TEM_OFFSET=0
00074     __CR_GEM_OFFSET=16
00075     __CR_AEM_OFFSET=17
00076     __CR_EBM_OFFSET=18
00077     __CR_PDU_OFFSET=19
00078     __CR_EPU_OFFSET=27
00079 
00080     __EV_GEM_OFFSET=0
00081     __EV_TEM_OFFSET=1
00082     __EV_AEM_OFFSET=17
00083     __EV_EPU_OFFSET=21
00084     def __init__(self,*args):
00085         qt.QVBox.__init__(self,*args)
00086 
00087         # This is a TaskEngine to execute our commands in
00088         # a non-gui thread
00089         self.__taskengine=TaskEngine()
00090 
00091         # These are our 2 tabs Fabric and Front End Power
00092         self.__tabs=qt.QTabWidget(self)
00093         self.__fabtab=FabricPanel(self.__tabs)
00094         self.__tabs.addTab(self.__fabtab,'Fabric Control')
00095         self.__powertab=FrontEndPowerPanel(self.__tabs)
00096         self.__tabs.addTab(self.__powertab,'Front End Power')
00097         # Displat the Power controls by default
00098         self.__tabs.setCurrentPage(1)
00099 
00100         # This variable is used to decide whether or not
00101         # to change the status of the event fabric based
00102         # on whether the EBM is in the C/R fabric
00103         self.__use_event_fabric=True
00104 
00105         # Connect to control buttons
00106         self.connect(self.__fabtab.insertButton,
00107                      qt.SIGNAL('clicked()'),
00108                      self.fabricInsert)
00109         self.connect(self.__fabtab.extractButton,
00110                      qt.SIGNAL('clicked()'),
00111                      self.fabricExtract)
00112         self.connect(self.__powertab.insertButton,
00113                      qt.SIGNAL('clicked()'),
00114                      self.powerOn)
00115         self.connect(self.__powertab.extractButton,
00116                      qt.SIGNAL('clicked()'),
00117                      self.powerOff)
00118 
00119         # Update our GUI to reflect the schema
00120         self.__taskengine.start()
00121         #self.refreshSchema()
00122         #self.refreshStatus()
00123 
00124     def getPowerTab(self):
00125       return self.__powertab
00126 
00127     def shutdown(self):
00128         self.__taskengine.shutdown()
00129 
00130 
00131     # These are the commands to turn things on and off
00132     def fabricInsert(self):
00133         self.__taskengine.spawn(self.__insert_fabrics,
00134                                 self.__getOCSMasks(self.__getSelect()))
00135     def fabricExtract(self):
00136         self.__taskengine.spawn(self.__extract_fabrics,
00137                                 self.__getOCSMasks(self.__getSelect()))
00138     def powerOn(self):
00139         self.__taskengine.spawn(self.__insert_power,
00140                                 self.__getOCSMasks(self.__getSelect()))
00141     def powerOff(self):
00142         self.__taskengine.spawn(self.__extract_power,
00143                                 self.__getOCSMasks(self.__getSelect()))
00144 
00145     # This function parses the Schema and applies it
00146     def refreshSchema(self, lat, xbrd):
00147         # These are our pointers to the latte infrastructure
00148         self.__lat=lat
00149         self.__xbrd=xbrd
00150         if self.__lat:
00151             self.__fabrics=Fabrics(self.__lat.LCB)
00152         else:
00153             self.__fabrics=None
00154 
00155         if not self.__lat: return {}
00156         cru=1&self.__lat.existsCRU()
00157         ebm=1&self.__lat.existsEBM()
00158         pdu0=1&self.__lat.existsPDU(0)
00159         pdu1=1&self.__lat.existsPDU(1)
00160         basemap={'cmd':cru,
00161                  'evt':ebm,
00162                  'pdu':(pdu1<<1)|pdu0,
00163                  'sec':(pdu1|pdu0)<<1|(pdu1|pdu0)}
00164         gasu={}
00165         arc={}
00166         if self.__lat.existsGEM():
00167             gasu['GEM']=basemap
00168         if self.__lat.existsEBM():
00169             gasu['EBM']=basemap
00170         if self.__lat.existsAEM():
00171             gasu['AEM']=basemap
00172             for free in self.__lat.AEM.ARC.values():
00173                 arc[free.id()]={'arc':1}
00174         crates={}
00175         for epu in self.__lat.EPU.values():
00176             crates[epu.id()]=basemap
00177         towers={}
00178         basemap.update({'cal':1,'tkr':1})
00179         for tem in self.__lat.TEM.values():
00180             towers[tem.id()]=basemap
00181         self.__fabtab.gasupanel.setEnables(gasu)
00182         self.__fabtab.cratepanel.setEnables(crates)
00183         self.__fabtab.towerpanel.setEnables(towers)
00184         self.__powertab.towerpanel.setEnables(towers)
00185         self.__powertab.acdpanel.setEnables(arc)
00186 
00187     # This function will go off and get the status
00188     # from the lat hierarchy.
00189     # One day, it might just suck off the LHK stream
00190     def refreshStatus(self):
00191         self.__taskengine.spawn(self.__getStatus)
00192 
00193     # Here are utility routines to retrieve the Fabric and Power Panels
00194     def getFabricControl(self):
00195         return self.__fabtab
00196     def getPowerControl(self):
00197         return self.__powertab
00198 
00199     # This function is called when commands queued to the
00200     # task engine begin and finish
00201     def __setCommanding(self,bool,message):
00202         logging.info(message)
00203         if bool:
00204             self.setCursor(qt.QCursor(qt.QWidget.WaitCursor))
00205         else:
00206             self.setCursor(qt.QCursor(qt.QWidget.ArrowCursor))
00207         self.__fabtab.powerBox.setEnabled(not bool)
00208         self.__powertab.powerBox.setEnabled(not bool)
00209 
00210     # These are the functions that are queued to the
00211     # task engine and which actually call the PIG
00212     # commands
00213     def __insert_fabrics(self,masks):
00214         if not self.__fabrics: return
00215         (cmd,pdu,sec,arc,ctr,evt)=masks
00216         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(True,'Adding to Fabrics--Begun')))
00217         try:
00218             self.__fabrics.insert_cmdrsp(cmd,pdu,sec,0,0)
00219             if self.__updateEventUsage():
00220                 self.__fabrics.insert_event(evt)
00221             self.__getStatus()
00222         except Exception, e:
00223             logging.exception(e)
00224         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(False,'Adding to Fabrics--Complete')))
00225     def __extract_fabrics(self,masks):
00226         if not self.__fabrics: return
00227         (cmd,pdu,sec,arc,ctr,evt)=masks
00228         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(True,'Removing from Fabrics--Begun')))
00229         try:
00230             if self.__updateEventUsage():
00231                 self.__fabrics.extract_event(evt)
00232             self.__fabrics.extract_cmdrsp(cmd,pdu,sec,0,0)
00233             self.__getStatus()
00234         except Exception, e:
00235             logging.exception(e)
00236         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(False,'Removing from Fabrics--Complete')))
00237     def __insert_power(self,masks):
00238         if not self.__fabrics: return
00239         (cmd,pdu,sec,arc,ctr,evt)=masks
00240         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(True,'Powering Up Front Ends--Begun')))
00241         try:
00242             self.__fabrics.insert_cmdrsp(0,0,0,arc,ctr)
00243             self.__getStatus()
00244         except Exception, e:
00245             logging.exception(e)
00246         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(False,'Powering Up Front Ends--Complete')))
00247     def __extract_power(self,masks):
00248         if not self.__fabrics: return
00249         (cmd,pdu,sec,arc,ctr,evt)=masks
00250         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(True,'Powering Down Front Ends--Begun')))
00251         try:
00252             self.__fabrics.extract_cmdrsp(0,0,0,arc,ctr)
00253             self.__getStatus()
00254         except Exception, e:
00255             logging.exception(e)
00256         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.CommandingChangeType,(False,'Powering Down Front Ends--Complete')))
00257 
00258     # This function will poll the registers and return
00259     # status masks the masks have the canonical format
00260     # It is  "hidden" becuase it should be queued to the
00261     # task engine. Users should use refreshStatus
00262     def __getStatus(self):
00263         (cru,ebm,pdu0,pdu1,sec,arc,cal,tkr)=(0,0,0,0,0,0,0,0)
00264         if not self.__lat: return (0,0,0,0,0,0,0,0)
00265         if self.__lat.existsCRU():
00266             cru=self.__lat.CRU.command_enable
00267         if self.__lat.existsPDU(0):
00268             tems=self.__lat.PDU[0].tems
00269             crates=self.__lat.PDU[0].crates
00270             acd=self.__lat.PDU[0].acd
00271             pdu0=((crates&7)<<self.__CR_EPU_OFFSET)|((acd&1)<<self.__CR_AEM_OFFSET)|tems
00272             sec|=((acd&2)>>1)<<self.__CR_AEM_OFFSET
00273         if self.__lat.existsPDU(1):
00274             tems=self.__lat.PDU[1].tems
00275             crates=self.__lat.PDU[1].crates
00276             acd=self.__lat.PDU[1].acd
00277             pdu1=((crates&7)<<self.__CR_EPU_OFFSET)|((acd&1)<<self.__CR_AEM_OFFSET)|tems
00278             sec|=((acd&2)>>1)<<self.__CR_AEM_OFFSET
00279 
00280         if self.__lat.existsEBM() and self.__updateEventUsage():
00281             ebm=self.__lat.EBM.downEBMC().input_enables
00282         for tem in self.__lat.TEM.values():
00283             if not cru&(1<<tem.id()): continue
00284             power=tem.TIC.power_supply
00285             t=power&1
00286             c=(power&2)>>1
00287             cal|=c<<tem.id()
00288             tkr|=t<<tem.id()
00289         if self.__lat.existsAEM() and cru&(1<<self.__CR_AEM_OFFSET):
00290             arc=self.__lat.AEM.power_status
00291         status=(cru,ebm,pdu0,pdu1,sec,arc,cal,tkr)
00292         qt.QApplication.postEvent(self,PorcineEvent(PorcineEvent.StatusChangeType,status))
00293 
00294     # This function will check the CRU register to see if
00295     # the EBM is enabled. If not, we should not modify the
00296     # event fabric
00297     def __updateEventUsage(self):
00298         if not self.__lat: return True
00299         if self.__lat.existsEBM() and \
00300                self.__lat.CRU.command_enable&(1<<self.__CR_EBM_OFFSET):
00301             use_event=True
00302         else:
00303             use_event=False
00304         self.__use_event_fabric=use_event
00305         return use_event
00306 
00307     # This function will set the status of all of the panels,
00308     # it accepts input in the form of the status event
00309     # posted by __getStatus()
00310     def __setStatus(self,cru,ebm,pdu0,pdu1,sec,arc,cal,tkr):
00311         # First I'll build the TEM status
00312         statmap={}
00313         for i in range(16):
00314             croff=self.__CR_TEM_OFFSET+i
00315             evoff=self.__EV_TEM_OFFSET+i
00316             statmap[i]={'cmd':(cru>>croff)&1,
00317                         'evt':(ebm>>evoff)&1,
00318                         'pdu':(((pdu1>>croff)&1)<<1)|((pdu0>>croff)&1),
00319                         'cal':cal>>croff&1,
00320                         'tkr':tkr>>croff&1}
00321             if not self.__use_event_fabric:
00322                 del statmap[i]['evt']
00323         self.__fabtab.towerpanel.setStatus(statmap)
00324         self.__powertab.towerpanel.setStatus(statmap)
00325         # Now the Gasu
00326         statmap.clear()
00327         croff=self.__CR_AEM_OFFSET
00328         evoff=self.__EV_AEM_OFFSET
00329         aempdu=(((pdu1>>croff)&1)<<1)|((pdu0>>croff)&1)
00330         if (sec>>croff)&1:
00331             aemsec=aempdu and 2
00332         else:
00333             aemsec=aempdu and 1
00334         statmap['AEM']={'cmd':(cru>>croff)&1,
00335                         'evt':(ebm>>evoff)&1,
00336                         'pdu':aempdu,
00337                         'sec':aemsec}
00338         if not self.__use_event_fabric:
00339             del statmap['AEM']['evt']
00340         croff=self.__CR_GEM_OFFSET
00341         evoff=self.__EV_GEM_OFFSET
00342         statmap['GEM']={'cmd':cru>>croff&1,
00343                         'evt':ebm>>evoff&1}
00344         if not self.__use_event_fabric:
00345             del statmap['GEM']['evt']
00346         croff=self.__CR_EBM_OFFSET
00347         statmap['EBM']={'cmd':cru>>croff&1}
00348         self.__fabtab.gasupanel.setStatus(statmap)
00349         # Now the Crates
00350         statmap.clear()
00351         for i in range(3):
00352             croff=self.__CR_EPU_OFFSET+i
00353             evoff=self.__EV_EPU_OFFSET+i
00354             statmap[i]={'cmd':cru>>croff&1,
00355                         'evt':ebm>>evoff&1,
00356                         'pdu':(((pdu1>>croff)&1)<<1)|((pdu0>>croff)&1)}
00357             if not self.__use_event_fabric:
00358                 del statmap[i]['evt']
00359         self.__fabtab.cratepanel.setStatus(statmap)
00360         # lastly the FREE boards
00361         statmap.clear()
00362         for i in range(12):
00363             statmap[i]={'arc':(arc>>i)&1}
00364         self.__powertab.acdpanel.setStatus(statmap)
00365 
00366     # Creates OCS masks from getSelect output
00367     def __getOCSMasks(self,selects):
00368         (fabtower,fabcrate,fabgasu,powertower,poweracd)=selects
00369         (cmd,pdu,sec,arc,ctr,evt)=(0L,0L,0L,0L,0L,0L)
00370         # Towers
00371         for i in range(16):
00372             cmd|=fabtower[i]['cmd']<<(self.__CR_TEM_OFFSET+i)
00373             pdu|=(fabtower[i]['pdu']>>1)<<(self.__CR_TEM_OFFSET+i)
00374             evt|=fabtower[i]['evt']<<(self.__EV_TEM_OFFSET+i)
00375             ctr|=powertower[i]['tkr']<<(self.__CR_TEM_OFFSET+i+16)
00376             ctr|=powertower[i]['cal']<<(self.__CR_TEM_OFFSET+i)
00377         # Gasu
00378         cmd|=fabgasu['GEM']['cmd']<<self.__CR_GEM_OFFSET
00379         evt|=fabgasu['GEM']['evt']<<self.__EV_GEM_OFFSET
00380         cmd|=fabgasu['EBM']['cmd']<<self.__CR_EBM_OFFSET
00381         cmd|=fabgasu['AEM']['cmd']<<self.__CR_AEM_OFFSET
00382         evt|=fabgasu['AEM']['evt']<<self.__EV_AEM_OFFSET
00383         pdu|=(fabgasu['AEM']['pdu']>>1)<<self.__CR_AEM_OFFSET
00384         sec|=(fabgasu['AEM']['sec']>>1)<<self.__CR_AEM_OFFSET
00385         # Crates
00386         for i in range(3):
00387             cmd|=fabcrate[i]['cmd']<<(self.__CR_EPU_OFFSET+i)
00388             evt|=fabcrate[i]['evt']<<(self.__EV_EPU_OFFSET+i)
00389             pdu|=(fabcrate[i]['pdu']>>1)<<(self.__CR_EPU_OFFSET+i)
00390         # Frees
00391         for i in range(12):
00392             arc|=poweracd[i]['arc']<<i
00393 
00394         return (cmd,pdu,sec,arc,ctr,evt)
00395 
00396     # This function returns the dicts from the panels.getSelect()
00397     def __getSelect(self):
00398         fabtower=self.__fabtab.towerpanel.getSelect()
00399         fabcrate=self.__fabtab.cratepanel.getSelect()
00400         fabgasu=self.__fabtab.gasupanel.getSelect()
00401         powertower=self.__powertab.towerpanel.getSelect()
00402         poweracd=self.__powertab.acdpanel.getSelect()
00403         return (fabtower,fabcrate,fabgasu,powertower,poweracd)
00404 
00405     # This function recieves the events from the thread
00406     # which executes all of the PIG calls and updates
00407     # the status of widgets in the GUI thread.
00408     def customEvent(self,e):
00409         if e.type()==PorcineEvent.StatusChangeType:
00410             apply(self.__setStatus,e.data())
00411         if e.type()==PorcineEvent.CommandingChangeType:
00412             apply(self.__setCommanding,e.data())
00413 
00414 # This class contains the information which gets passed between
00415 # the thread executing the commands to OCS and the GUI thread
00416 class PorcineEvent(qt.QCustomEvent):
00417     StatusChangeType=qt.QEvent.Type(qt.QEvent.User+10)
00418     CommandingChangeType=qt.QEvent.Type(qt.QEvent.User+11)
00419     def __init__(self,type,data):
00420         qt.QCustomEvent.__init__(self,type,data)
00421 
00422 # This Panel controls adding and removing Electronic Modules
00423 # from the command and event fabrics
00424 class FabricPanel(qt.QVBox):
00425     def __init__(self,*args):
00426         qt.QVBox.__init__(self,*args)
00427         self.setSpacing(5)
00428 
00429         # This Panel contains all of the convienience buttons
00430         self.__setupControlBox()
00431         # The Main selection Panel has 2 columns lbox and rbox
00432         hbox=qt.QHBox(self)
00433         hbox.setSpacing(5)
00434         hbox.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Minimum,
00435                                           qt.QSizePolicy.Fixed))
00436         self.towerpanel=TowerPanel(hbox)
00437 
00438         rbox=qt.QVBox(hbox)
00439         rbox.setSpacing(5)
00440         self.gasupanel=GasuPanel(rbox)
00441         self.cratepanel=CratePanel(rbox)
00442 
00443         # These are the insert and extract Buttons
00444         self.powerBox=qt.QHGroupBox(self)
00445         self.powerBox.setFrameStyle(qt.QFrame.NoFrame)
00446         self.powerBox.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Minimum,
00447                                                    qt.QSizePolicy.Fixed))
00448         commandFont=qt.QFont()
00449         commandFont.setPointSize(18)
00450         self.insertButton=qt.QPushButton('Add to Fabrics',self.powerBox)
00451         self.insertButton.setMinimumHeight(45)
00452         self.insertButton.setFont(commandFont)
00453         self.extractButton=qt.QPushButton('Remove from Fabrics',self.powerBox)
00454         self.extractButton.setMinimumHeight(45)
00455         self.extractButton.setFont(commandFont)
00456 
00457     def __setupControlBox(self):
00458         control=qt.QGroupBox(2,qt.Qt.Vertical,'Selection Controls',self)
00459         control.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Minimum,
00460                                              qt.QSizePolicy.Fixed))
00461         control.setLineWidth(2)
00462         self.allselb=qt.QPushButton('Select All',control)
00463         self.connect(self.allselb,qt.SIGNAL('clicked()'),self.allsel)
00464         self.allclearb=qt.QPushButton('Clear All',control)
00465         self.connect(self.allclearb,qt.SIGNAL('clicked()'),self.allclear)
00466 
00467         self.allselcrb=qt.QPushButton('Select All CMD Fabric',control)
00468         self.connect(self.allselcrb,qt.SIGNAL('clicked()'),self.allselcr)
00469         self.allclearcrb=qt.QPushButton('Clear All CMD Fabric',control)
00470         self.connect(self.allclearcrb,qt.SIGNAL('clicked()'),self.allclearcr)
00471 
00472         self.allselevtb=qt.QPushButton('Select All Event Fabric',control)
00473         self.connect(self.allselevtb,qt.SIGNAL('clicked()'),self.allselevt)
00474         self.allclearevtb=qt.QPushButton('Clear All Event Fabric',control)
00475         self.connect(self.allclearevtb,qt.SIGNAL('clicked()'),self.allclearevt)
00476 
00477         self.allselpdu0b=qt.QPushButton('Select All PDU 0',control)
00478         self.connect(self.allselpdu0b,qt.SIGNAL('clicked()'),self.allselpdu0)
00479         self.allselpdu1b=qt.QPushButton('Select All PDU 1',control)
00480         self.connect(self.allselpdu1b,qt.SIGNAL('clicked()'),self.allselpdu1)
00481 
00482     def selectAll(self,select):
00483         self.towerpanel.setAllSelect(select)
00484         self.gasupanel.setAllSelect(select)
00485         self.cratepanel.setAllSelect(select)
00486     def allsel(self):
00487         self.selectAll({'cmd':1,'evt':1})
00488     def allclear(self):
00489         self.selectAll({'cmd':0,'evt':0})
00490     def allselcr(self):
00491         self.selectAll({'cmd':1})
00492     def allclearcr(self):
00493         self.selectAll({'cmd':0})
00494     def allselevt(self):
00495         self.selectAll({'evt':1})
00496     def allclearevt(self):
00497         self.selectAll({'evt':0})
00498     def allselpdu0(self):
00499         self.selectAll({'pdu':1})
00500     def allselpdu1(self):
00501         self.selectAll({'pdu':2})
00502 
00503 # This Panel controls powering up and down the front ends
00504 class FrontEndPowerPanel(qt.QHBox):
00505     def __init__(self,*args):
00506         qt.QVBox.__init__(self,*args)
00507 
00508         qt.QHBox(self)
00509         vbox=qt.QVBox(self)
00510         hbox=qt.QHBox(vbox)
00511         hbox.setSpacing(5)
00512         self.towerpanel=TowerPowerPanel(hbox)
00513         self.acdpanel=ACDPowerPanel(hbox)
00514         qt.QHBox(self)
00515 
00516         self.powerBox=qt.QVGroupBox(vbox)
00517         self.powerBox.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Minimum,
00518                                                    qt.QSizePolicy.Fixed))
00519         self.powerBox.setFrameStyle(qt.QFrame.NoFrame)
00520 
00521         commandFont=qt.QFont()
00522         commandFont.setPointSize(18)
00523         self.insertButton=qt.QPushButton('Power Up',self.powerBox)
00524         self.insertButton.setMinimumHeight(45)
00525         self.insertButton.setFont(commandFont)
00526 
00527         self.extractButton=qt.QPushButton('Power Down',self.powerBox)
00528         self.extractButton.setMinimumHeight(45)
00529         self.extractButton.setFont(commandFont)
00530 
00531 # This is the base class for groups of related EMPanels (Towers,Gasu,...)
00532 class GroupPanel(qt.QVGroupBox):
00533     def __init__(self,*args):
00534         qt.QVGroupBox.__init__(self,*args)
00535         self.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Minimum,
00536                                           qt.QSizePolicy.Minimum))
00537         self.setLineWidth(2)
00538         self.__modules={}
00539         self.constructControls()
00540         self.constructGrid()
00541         self.__lockbox.setChecked(1)
00542     # This function is overloaded by child classes
00543     # to consrtuct the grid of EMPanels
00544     def constructGrid(self):
00545         pass
00546     # This is where we set up the controls for this Panel
00547     def constructControls(self):
00548         self.__controls=qt.QHBox(self)
00549         self.__lockbox=qt.QCheckBox('Lock Like Selections',
00550                                     self.__controls)
00551         self.connect(self.__lockbox,
00552                      qt.SIGNAL('toggled(bool)'),
00553                      self.__lockSelections)
00554     # This function is used to add modules to this panel
00555     def setModule(self,tag,module):
00556         self.__modules[tag]=module
00557     # This function sets the enables for all of the
00558     # EMPanels
00559     def setEnables(self,enables):
00560         for k,m in self.__modules.items():
00561             if enables.has_key(k):
00562                 m.setEnables(enables[k])
00563             else:
00564                 m.setEnables({})
00565     # This function returns a map of maps representing
00566     # the selected state of all the EMPanels
00567     def getSelect(self):
00568         select={}
00569         for k,s in self.__modules.items():
00570             select[k]=s.getSelect()
00571         return select
00572     # This function accepts a map of status maps for each
00573     # of the EMPanel status infos
00574     def setStatus(self,statusmap):
00575         for k,s in statusmap.items():
00576             if self.__modules.has_key(k):
00577                 self.__modules[k].setStatus(s)
00578     # This connects all of the EMPanels selectChanged signals
00579     # to our function to set all selections to the one which
00580     # changed
00581     def __lockSelections(self,bool):
00582         if bool:
00583             for m in self.__modules.values():
00584                 self.connect(m,qt.PYSIGNAL('selectChanged'),
00585                              self.setAllSelect)
00586         else:
00587             for m in self.__modules.values():
00588                 self.disconnect(m,qt.PYSIGNAL('selectChanged'),
00589                                 self.setAllSelect)
00590     # This function sets all EMPanel selections to be select
00591     # select has the same form as returned by EMPanel.getSelect()
00592     def setAllSelect(self,select,name=''):
00593         # We want to disable the locking for the duration
00594         # of this call
00595         lock=self.__lockbox.isChecked()
00596         self.__lockbox.setChecked(0)
00597         if name and select.has_key(name):
00598             select={name:select[name]}
00599         elif name:
00600             select={}
00601         # Now we can loop over all of the EMPanels
00602         for m in self.__modules.values():
00603             m.setSelect(select)
00604         # set the lock back to the previous setting
00605         self.__lockbox.setChecked(lock)
00606 
00607 # This class defines the power controls for the Tower FEs
00608 class TowerPowerPanel(GroupPanel):
00609     def __init__(self,*args):
00610         GroupPanel.__init__(self,*args)
00611         self.setTitle('Towers')
00612     def constructGrid(self):
00613         grid=qt.QGrid(4,qt.Qt.Horizontal,self)
00614         grid.setSpacing(10)
00615         # I reverse the array so that
00616         # the orientation of the Towers is the same as it is
00617         # on the LAT (looking from the -z side.
00618         for t in range(15,-1,-1):
00619             tower=EMPanel('TEM'+str(t),grid)
00620             tower.setOrientation(qt.Qt.Vertical)
00621             tower.addSelectable('tkr',CheckLed('tkr','TKR',tower))
00622             tower.addSelectable('cal',CheckLed('cal','CAL',tower))
00623             self.setModule(t,tower)
00624 
00625 # This class defines the power controls for the ACD FEs
00626 class ACDPowerPanel(GroupPanel):
00627     # This is just a map of Free board names
00628     FreeMap=['1LA','1RB','2LA','2LB','2RA','2RB',
00629              '3LA','3RB','4LA','4LB','4RA','4RB']
00630     def __init__(self,*args):
00631         GroupPanel.__init__(self,*args)
00632         self.setTitle('ACD')
00633     def constructGrid(self):
00634         grid=qt.QGrid(2,qt.Qt.Horizontal,self)
00635         for f in range(12):
00636             name=ACDPowerPanel.FreeMap[f]
00637             free=EMPanel(name,grid)
00638             free.addSelectable('arc',CheckLed('arc',name,free))
00639             free.setFrameStyle(qt.QFrame.NoFrame)
00640             free.setTitle('')
00641             self.setModule(f,free)
00642 
00643 # This class defines the fabric controls for the Towers
00644 class TowerPanel(GroupPanel):
00645     def __init__(self,*args):
00646         GroupPanel.__init__(self,*args)
00647         self.setTitle('Towers')
00648     def constructGrid(self):
00649         grid=qt.QGrid(4,qt.Qt.Horizontal,self)
00650         # I reverse the array so that
00651         # the orientation of the Towers is the same as it is
00652         # on the LAT (looking from the -z side.
00653         for t in range(15,-1,-1):
00654             tower=EMPanel('TEM'+str(t),grid)
00655             tower.addSelectable('cmd',CheckLed('cmd','CMD',tower))
00656             tower.addSelectable('evt',CheckLed('evt','EVT',tower))
00657             states={0:('','OFF'),
00658                     1:('0','PDU0'),
00659                     2:('1','PDU1'),
00660                     3:('','ERR')}
00661             tower.addSelectable('pdu',ExclusiveButtonGroup('pdu',
00662                                                            states,
00663                                                            StatusLabel(tower),
00664                                                            tower))
00665             self.setModule(t,tower)
00666 
00667 # This class defines the fabric controls for the Crates
00668 class CratePanel(GroupPanel):
00669     def __init__(self,*args):
00670         GroupPanel.__init__(self,*args)
00671         self.setTitle('Crates')
00672     def constructGrid(self):
00673         # I reverse the array so that
00674         # the orientation of the EPUs is the same as it is
00675         # on the LAT (looking from the -z side.
00676         grid=qt.QGrid(2,qt.Qt.Horizontal,self)
00677         qt.QHBox(grid)
00678         for e in range(2,-1,-1):
00679             epu=EMPanel('EPU'+`e`,grid)
00680             epu.addSelectable('cmd',CheckLed('cmd','CMD',epu))
00681             epu.addSelectable('evt',CheckLed('evt','EVT',epu))
00682             states={0:('','OFF'),
00683                     1:('0','PDU0'),
00684                     2:('1','PDU1'),
00685                     3:('','ERR')}
00686             epu.addSelectable('pdu',ExclusiveButtonGroup('pdu',
00687                                                          states,
00688                                                          StatusLabel(epu),
00689                                                          epu))
00690             self.setModule(e,epu)
00691 
00692 # This class defines the fabric controls for the GASU
00693 class GasuPanel(GroupPanel):
00694     def __init__(self,*args):
00695         GroupPanel.__init__(self,*args)
00696         self.setTitle('GASU')
00697     def constructGrid(self):
00698         hbox=qt.QHBox(self)
00699         # Build the AEM
00700         aem=EMPanel('AEM',hbox)
00701         aem.addSelectable('cmd',CheckLed('cmd','CMD',aem))
00702         aem.addSelectable('evt',CheckLed('evt','EVT',aem))
00703         states={0:('','OFF'),
00704                 1:('0','PDU0'),
00705                 2:('1','PDU1'),
00706                 3:('','ERR')}
00707         aem.addSelectable('pdu',ExclusiveButtonGroup('pdu',
00708                                                      states,
00709                                                      StatusLabel(aem),
00710                                                      aem))
00711         states={0:('','OFF'),
00712                 1:('P','P Supply'),
00713                 2:('R','R Supply'),
00714                 3:('','ERR')}
00715         aem.addSelectable('sec',ExclusiveButtonGroup('sec',
00716                                                      states,
00717                                                      StatusLabel(aem),
00718                                                      aem))
00719         vbox=qt.QVBox(hbox)
00720         self.setModule('AEM',aem)
00721 
00722         # Build the GEM
00723         gem=EMPanel('GEM',vbox)
00724         gem.addSelectable('cmd',CheckLed('cmd','CMD',gem))
00725         gem.addSelectable('evt',CheckLed('evt','EVT',gem))
00726         self.setModule('GEM',gem)
00727 
00728         # Build the EBM
00729         ebm=EMPanel('EBM',vbox)
00730         ebm.addSelectable('cmd',CheckLed('cmd','CMD',ebm))
00731         self.setModule('EBM',ebm)
00732 
00733 # This class defines a panel which represents an electronics
00734 # module from the standpoint of PIG, which is to say, it
00735 # has a number devices for selecting and displaying power
00736 # and fabric configurations
00737 class EMPanel(qt.QGroupBox):
00738     def __init__(self,name,*args):
00739         qt.QGroupBox.__init__(self,2,qt.Qt.Horizontal,name,*args)
00740         self.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Minimum,
00741                                           qt.QSizePolicy.Fixed))
00742         self.setInsideMargin(5)
00743         self.setInsideSpacing(3)
00744         self.__selectables={}
00745     # This function will add a selectable to this panel
00746     def addSelectable(self,key,sel):
00747         self.__selectables[key]=sel
00748         self.connect(sel,qt.PYSIGNAL('selectChanged'),self.selectChanged)
00749     # This function returns the selections for each of
00750     # its checkleds and button groups.
00751     # (checkboxes return 1 or 0, button groups return the bitfield
00752     # representing the button selected.)
00753     def getSelect(self):
00754         select={}
00755         for k,b in self.__selectables.items():
00756             select[k]=b.getSelect()
00757         return select
00758     # This function sets the status of the selectables
00759     # it takes an argument of the same form as the output
00760     # of getSelect
00761     def setStatus(self,status):
00762         for k,s in status.items():
00763             if self.__selectables.has_key(k):
00764                 self.__selectables[k].setStatus(s)
00765     # This function sets the selection of the selectables,
00766     # and also has the same form as above
00767     def setSelect(self,select):
00768         for k,s in select.items():
00769             if self.__selectables.has_key(k):
00770                 self.__selectables[k].setSelect(s)
00771     # This PYSIGNAL is emitted if any of our selectables
00772     # are clicked, its parameter is the return value of getSelect
00773     def selectChanged(self,name):
00774         self.emit(qt.PYSIGNAL('selectChanged'),(self.getSelect(),name))
00775     # This function sets the enables for this panel
00776     def setEnables(self,enablemap):
00777         enable=0
00778         for k,s in self.__selectables.items():
00779             if enablemap.has_key(k):
00780                 s.setEnables(enablemap[k])
00781                 enable|=enablemap[k]
00782             else:
00783                 s.setEnables(0)
00784         self.setEnabled(enable)
00785 
00786 # This class defines a check box with a label and an 'LED' which
00787 # displays a color based on the status
00788 class CheckLed(qt.QHBox):
00789     statusColors={0:'gray',
00790                   1:'green'}
00791     def __init__(self,name,label,*args):
00792         qt.QHBox.__init__(self,*args)
00793         self.__box=qt.QCheckBox(label,self)
00794         self.setFrameStyle(qt.QFrame.Panel|qt.QFrame.Sunken)
00795         self.setMargin(2)
00796         self.setStatus(0)
00797         qt.QObject.connect(self.__box,
00798                            qt.SIGNAL('toggled(bool)'),
00799                            self.emitChanged)
00800         self.__name=name
00801     def setSelect(self,select):
00802         if self.isEnabled():
00803             self.__box.setChecked(select)
00804     def getSelect(self):
00805         return 1L&self.__box.isChecked()
00806     def setStatus(self,status):
00807         self.setPaletteBackgroundColor(\
00808             qt.QColor(CheckLed.statusColors[status]))
00809     def emitChanged(self,bool):
00810         self.emit(qt.PYSIGNAL('selectChanged'),(self.__name,))
00811     def setEnables(self,enable):
00812         self.setEnabled(1&enable)
00813     def setEnabled(self,bool):
00814         self.__box.setChecked(0)
00815         qt.QWidget.setEnabled(self,bool)
00816 
00817 # This class is a simple status label, which clears when disabled
00818 class StatusLabel(qt.QLabel):
00819     def __init__(self,*args):
00820         qt.QLabel.__init__(self,*args)
00821         self.setAlignment(qt.Qt.AlignCenter)
00822         self.setFrameStyle(qt.QFrame.Panel|qt.QFrame.Sunken)
00823         self.setMargin(2)
00824     def setEnabled(self,bool):
00825         if not bool:
00826             self.setText('')
00827         qt.QWidget.setEnabled(self,bool)
00828 
00829 # This class will create a group of exclusive radio buttons
00830 # with an abbreviated label for the button, and an associated
00831 # status for when that button is selected to be applied to
00832 # a status label. The selected buttons will be retrieved as
00833 # a bitmask, and the status will be set similarly.
00834 # The statemap has the following form
00835 #  -> {buttonbit:(buttonlabel,status),buttonbit:(buttonlabel,status),...}
00836 # A status for a null button bit can also be defined.
00837 class ExclusiveButtonGroup(qt.QHButtonGroup):
00838     def __init__(self,name,statemap,statuslabel,*args):
00839         qt.QHButtonGroup.__init__(self,*args)
00840         self.setFrameStyle(qt.QFrame.NoFrame)
00841         self.setInsideMargin(0)
00842         self.setInsideSpacing(0)
00843         self.__statemap=statemap
00844         self.__status=statuslabel
00845         self.__buttons={}
00846         self.__name=name
00847         # Here we create the buttons for states with only one bit set
00848         buttonbits=statemap.keys()
00849         buttonbits.sort()
00850         for state in buttonbits:
00851             if self.__xor(state):
00852                 self.__buttons[state]=qt.QRadioButton(statemap[state][0],self)
00853         # The first button is checked by default.
00854         self.__buttons[1].setChecked(1)
00855         # This signal is emitted whenever a button is clicked
00856         # we will then emit a PYSIGNAL which contains the
00857         # bitfield of the selected button.
00858         qt.QObject.connect(self,qt.SIGNAL('clicked(int)'),self.__emitChanged)
00859     # This is my stupid function which provides an xor
00860     # of the bits in a integer. I can't remember the
00861     # trick to do this smartly.
00862     def __xor(self,int):
00863         ret=0
00864         while int:
00865             ret+=int&1
00866             if ret>1: return 0
00867             int>>=1
00868         return ret
00869     # This function will get called whenever the selection
00870     # changes, and emits the selectChanged signal
00871     def __emitChanged(self,int):
00872         select=self.getSelect()
00873         self.emit(qt.PYSIGNAL('selectChanged'),(self.__name,))
00874     # These functions get/set the bitfield which specifies
00875     # which buttons are selected
00876     def getSelect(self):
00877         select=0
00878         for k,b in self.__buttons.items():
00879             if b.isChecked(): select|=long(k)
00880         return select
00881     def setSelect(self,select):
00882         for k,b in self.__buttons.items():
00883             if k&select:
00884                 b.setChecked(b.isEnabled())
00885     # This function sets which buttons are available
00886     def setEnables(self,enable):
00887         # Since we want the lowest enabled box
00888         # to be selected by default, we'll go through the
00889         # sorted keys
00890         keys=self.__buttons.keys()
00891         keys.sort()
00892         keys.reverse()
00893         for k in keys:
00894             self.__buttons[k].setEnabled(k&enable)
00895             self.__buttons[k].setChecked(k&enable)
00896         # If there are no buttons enabled, disable the
00897         # status label, and the button group
00898         self.__status.setEnabled(enable)
00899         qt.QWidget.setEnabled(self,enable)
00900     # Should we be disabled, turn everything off
00901     # otherwise set the default button on
00902     def setEnabled(self,bool):
00903         if not bool:
00904             for b in self.__buttons.values():
00905                 b.setChecked(0)
00906         else:
00907             self.__buttons[1].setChecked(1)
00908         qt.QWidget.setEnabled(self,bool)
00909     # This function recieves a bitfield and sets the
00910     # status label appropriately
00911     def setStatus(self,status):
00912         if self.__statemap.has_key(status):
00913             self.__status.setText(self.__statemap[status][1])
00914 
00915 def simple_test(args):
00916     app=qt.QApplication(args)
00917     gui=PorcinePanel(None, None)
00918     app.connect(app,qt.SIGNAL("lastWindowClosed()"),
00919                 app,qt.SLOT("quit()"))
00920     app.setMainWidget(gui)
00921     gui.show()
00922     app.exec_loop()
00923 
00924 if __name__=='__main__':
00925     import sys
00926     simple_test(sys.argv)

Generated on Fri Jul 21 13:26:31 2006 for LATTE R04-12-00 by doxygen 1.4.3