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)